1 /*---------------------------------------------------------------------------
2 * Gamedriver - Dump object statistics.
3 *
4 *---------------------------------------------------------------------------
5 * Function to compute the memory usage of objects and dump their
6 * statistics.
7 *---------------------------------------------------------------------------
8 */
9
10 #include "driver.h"
11 #include "typedefs.h"
12
13 #include <stdio.h>
14 #ifdef HAVE_SYS_TIME_H
15 #include <sys/time.h>
16 #endif
17 #include <time.h>
18
19 #include "dumpstat.h"
20 #include "array.h"
21 #include "closure.h"
22 #include "exec.h"
23 #include "filestat.h"
24 #include "instrs.h" /* F_RETURN, F_RETURN0 for overhead computation */
25 #include "mapping.h"
26 #include "mstrings.h"
27 #include "object.h"
28 #include "ptrtable.h"
29 #include "simulate.h"
30 #include "stdstrings.h"
31 #ifdef USE_STRUCTS
32 #include "structs.h"
33 #endif /* USE_STRUCTS */
34 #include "svalue.h"
35 #include "xalloc.h"
36
37 /*-------------------------------------------------------------------------*/
38
39 static size_t svalue_size(svalue_t *, mp_int *); /* forward */
40
41 /* Auxiliary structure for counting a mapping */
42
43 struct svalue_size_locals
44 {
45 mp_uint composite; /* Return: total composite memory usage of all elmts */
46 mp_uint total; /* Return: total memory usage of all elmts */
47 int num_values; /* Passed: width of the mapping */
48 };
49
50 /*-------------------------------------------------------------------------*/
51
52 static struct pointer_table *ptable;
53 /* The pointer_table to register all arrays and mappings.
54 */
55
56 /*-------------------------------------------------------------------------*/
57 static void
svalue_size_map_filter(svalue_t * key,svalue_t * values,void * extra)58 svalue_size_map_filter (svalue_t *key, svalue_t *values, void *extra)
59
60 /* Called for all keys in a mapping, it counts the size of the values
61 * and returns the total in extra->num_values.
62 */
63
64 {
65 struct svalue_size_locals *locals;
66 mp_int total;
67 int i;
68
69 locals = (struct svalue_size_locals*)extra;
70 locals->composite += svalue_size(key, &total);
71 locals->total += total;
72 for(i = locals->num_values; --i >= 0; )
73 {
74 locals->composite += svalue_size(values++, &total) + sizeof(svalue_t);
75 locals->total += total + sizeof(svalue_t);
76 }
77 }
78
79 /*-------------------------------------------------------------------------*/
80 static size_t
svalue_size(svalue_t * v,mp_int * pTotal)81 svalue_size (svalue_t *v, mp_int * pTotal)
82
83 /* Compute the memory usage of *<v> (modified to reflect data sharing),
84 * calling svalue_size() recursively if necessary, and return it.
85 * The size of *v itself is not included.
86 * *<pUnshared> and *<pShared> are set to the total unshared and shared
87 * datasize.
88 */
89
90 {
91 mp_int i, composite, total, overhead;
92
93 assert_stack_gap();
94
95 *pTotal = 0;
96
97 total = overhead = composite = 0;
98
99 switch(v->type)
100 {
101 case T_OBJECT:
102 case T_NUMBER:
103 case T_FLOAT:
104 return 0;
105
106 case T_STRING:
107 case T_SYMBOL:
108 // If ref==0 the string is probably shared a lot, but we can't estimate
109 // the correct number, so we return 0 as memory consumption for the
110 // string.
111 if (v->u.str->info.ref)
112 {
113 *pTotal = mstr_mem_size(v->u.str);
114 return *pTotal / v->u.str->info.ref;
115 }
116 else
117 return 0;
118
119 case T_MAPPING:
120 {
121 struct svalue_size_locals locals;
122
123 if (NULL == register_pointer(ptable, v->u.map) ) return 0;
124
125 if (v->u.map->ref)
126 {
127 overhead = (mp_uint)mapping_overhead(v->u.map);
128 locals.total = 0;
129 locals.composite = 0;
130 locals.num_values = v->u.map->num_values;
131 walk_mapping(v->u.map, svalue_size_map_filter, &locals);
132
133 *pTotal = locals.total + overhead;
134 return (overhead + locals.composite) / v->u.map->ref;
135 }
136 else
137 return 0;
138 }
139
140 case T_POINTER:
141 case T_QUOTED_ARRAY:
142 {
143 if (v->u.vec == &null_vector) return 0;
144 if (NULL == register_pointer(ptable, v->u.vec) ) return 0;
145 if (v->u.vec->ref)
146 {
147 overhead = sizeof *v->u.vec - sizeof v->u.vec->item +
148 sizeof(svalue_t) * v->u.vec->size + sizeof(char *);
149 for (i=0; i < (mp_int)VEC_SIZE(v->u.vec); i++) {
150 composite += svalue_size(&v->u.vec->item[i], &total);
151 *pTotal += total;
152 }
153 *pTotal += overhead;
154
155 return (overhead + composite) / v->u.vec->ref;
156 }
157 else
158 return 0;
159 }
160
161 #ifdef USE_STRUCTS
162 case T_STRUCT:
163 {
164 struct_t *st = v->u.strct;
165 if (NULL == register_pointer(ptable, st) ) return 0;
166 if (st->ref)
167 {
168 overhead = sizeof *st - sizeof st->member
169 + sizeof(svalue_t) * struct_size(st);
170 for (i=0; i < (mp_int)struct_size(st); i++) {
171 composite += svalue_size(&st->member[i], &total);
172 *pTotal += total;
173 }
174 *pTotal += overhead;
175
176 return (overhead + composite) / st->ref;
177 }
178 else
179 return 0;
180 }
181 #endif /* USE_STRUCTS */
182
183 case T_CLOSURE:
184 {
185 int num_values;
186 svalue_t *svp;
187 lambda_t *l;
188
189 if (!CLOSURE_MALLOCED(v->x.closure_type)) return 0;
190 if (!CLOSURE_REFERENCES_CODE(v->x.closure_type))
191 {
192 #ifdef USE_NEW_INLINES
193 if (v->x.closure_type == CLOSURE_LFUN)
194 composite = SIZEOF_LAMBDA(v->u.lambda->function.lfun.context_size);
195 else /* CLOSURE_IDENTIFIER || CLOSURE_PRELIMINARY */
196 composite = sizeof *v->u.lambda;
197
198 composite += sizeof(char *);
199 #else
200 /* CLOSURE_LFUN || CLOSURE_IDENTIFIER || CLOSURE_PRELIMINARY */
201 composite = sizeof *v->u.lambda + sizeof(char *);
202 #endif
203 *pTotal = composite;
204 return composite / v->u.lambda->ref;
205 }
206 /* CLOSURE_LAMBDA */
207 composite = overhead = 0;
208 l = v->u.lambda;
209 if (v->x.closure_type == CLOSURE_BOUND_LAMBDA)
210 {
211 total = sizeof *l - sizeof l->function + sizeof l->function.lambda;
212 *pTotal += total;
213 composite += total / l->ref;
214 l = l->function.lambda;
215 }
216 num_values = EXTRACT_UCHAR(&l->function.code[0]);
217 if (num_values == 0xff)
218 num_values = ((svalue_t *)l)[-0xff].u.number;
219 svp = (svalue_t *)l - num_values;
220 if (NULL == register_pointer(ptable, svp)) return 0;
221 overhead = sizeof(svalue_t) * num_values + sizeof (char *);
222 {
223 bytecode_p p = &l->function.code[2];
224 do {
225 ++p;
226 switch(GET_CODE(p)) {
227 case F_RETURN:
228 case F_RETURN0:
229 break;
230 default:
231 continue;
232 }
233 break;
234 } while (1);
235 overhead += (p - (bytecode_p)l + (sizeof(bytecode_p) - 1))
236 & ~(sizeof(bytecode_p) - 1);
237 }
238
239 while (--num_values >= 0)
240 {
241 composite += svalue_size(svp++, &total);
242 *pTotal += total;
243 }
244
245 *pTotal += overhead;
246 if (l->ref)
247 return (overhead + composite) / l->ref;
248 else
249 return 0;
250 }
251
252 default:
253 fatal("Illegal type: %d\n", v->type);
254 }
255
256 /*NOTREACHED*/
257 return 0;
258 }
259
260 /*-------------------------------------------------------------------------*/
261 mp_int
data_size(object_t * ob,mp_int * pTotal)262 data_size (object_t *ob, mp_int * pTotal)
263
264 /* Compute the memory usage (modified to reflect shared data use)
265 * of the data held by object <ob> and return it.
266 * If the object is swapped out or has no variables, return 0.
267 *
268 * If <pTotal> is given, *<pTotal> is set to the raw datasize.
269 */
270
271 {
272 mp_int total = sizeof(p_int); /* smalloc overhead */
273 int i;
274 svalue_t *svp;
275
276 if (pTotal != NULL)
277 *pTotal = 0;
278
279 if (ob->flags & O_SWAPPED || !(i = ob->prog->num_variables) )
280 return 0;
281 ptable = new_pointer_table();
282 if (!ptable)
283 errorf("(dumpstat) Out of memory for new pointer table.\n");
284 for (svp = ob->variables; --i >= 0; svp++)
285 {
286 mp_int tmp;
287 total += svalue_size(svp, &tmp) + sizeof (svalue_t);
288 if (pTotal != NULL)
289 *pTotal += tmp + sizeof(svalue_t);
290 }
291 free_pointer_table(ptable);
292 return total;
293 } /* data_size() */
294
295 /*-------------------------------------------------------------------------*/
296 mp_int
program_string_size(program_t * prog,mp_int * pOverhead,mp_int * pData)297 program_string_size (program_t *prog, mp_int * pOverhead, mp_int * pData)
298
299 /* Compute the composite data size of all strings in program <prog>
300 * which must be swapped in.
301 * Set *<pOverhead> to the size of the overhead in the program structure,
302 * and *<pData> to the raw data size of the strings.
303 */
304
305 {
306 int i;
307 mp_int rc, data;
308
309 rc = data = 0;
310
311 for (i = prog->num_strings; i--; )
312 {
313 string_t * str = prog->strings[i];
314 mp_int size;
315
316 size = mstr_mem_size(str);
317 data += size;
318 rc += size / str->info.ref;
319 }
320
321 *pOverhead = prog->num_strings * sizeof(char *);
322 *pData = data;
323 return rc;
324 } /* program_string_size() */
325
326 /*-------------------------------------------------------------------------*/
327 Bool
dumpstat(string_t * fname)328 dumpstat (string_t *fname)
329
330 /* This function dumps statistics about all listed objects into the file
331 * $MUDLIB/<fname>. It is called by the command parser or from debug_info.
332 * Return TRUE on success, FALSE if <fname> can't be written.
333 */
334
335 {
336 FILE *f;
337 object_t *ob;
338
339 static char *swapstrings[] =
340 {"", "PROG SWAPPED", "VAR SWAPPED", "SWAPPED", };
341
342 fname = check_valid_path(fname, current_object, STR_OBJDUMP, MY_TRUE);
343 if (!fname)
344 return MY_FALSE;
345 f = fopen(get_txt(fname), "w");
346 if (!f)
347 {
348 free_mstring(fname);
349 return MY_FALSE;
350 }
351 FCOUNT_WRITE(get_txt(fname));
352
353 for (ob = obj_list; ob; ob = ob->next_all)
354 {
355 mp_int compsize, totalsize, overhead;
356 char timest[21];
357 struct tm *tm;
358
359 #ifdef DEBUG
360 if (ob->flags & O_DESTRUCTED) /* TODO: Can't happen */
361 continue;
362 #endif
363 compsize = data_size(ob, &totalsize);
364
365 if (!O_PROG_SWAPPED(ob)
366 && (ob->prog->ref == 1 || !(ob->flags & (O_CLONE|O_REPLACED))))
367 {
368 overhead = ob->prog->total_size;
369 }
370 else
371 {
372 overhead = 0;
373 }
374
375 overhead += sizeof (object_t);
376
377 fprintf(f, "%-20s %5"PRIdMPINT" (%5"PRIdMPINT") ref %2"PRIdPINT" %s "
378 , get_txt(ob->name)
379 , compsize + overhead, totalsize + overhead
380 , ob->ref
381 , ob->flags & O_HEART_BEAT ? "HB" : " "
382 );
383 if (ob->super)
384 fprintf(f, "%s ", get_txt(ob->super->name));
385 else
386 fprintf(f, "-- ");
387
388 if (ob->gigaticks)
389 fprintf(f, " (%"PRIuMPINT"%09"PRIuMPINT")",
390 (mp_uint)ob->gigaticks, (mp_uint)ob->ticks);
391 else
392 fprintf(f, " (%"PRIuMPINT")", (mp_uint)ob->ticks);
393 fprintf(f, " %s",
394 swapstrings[(O_PROG_SWAPPED(ob)?1:0) | (O_VAR_SWAPPED(ob)?2:0)]
395 );
396 tm = localtime((time_t *)&ob->load_time);
397 strftime(timest, sizeof(timest), "%Y.%m.%d-%H:%M:%S", tm);
398 fprintf(f, " %s\n", timest);
399 }
400 fclose(f);
401 free_mstring(fname);
402
403 return MY_TRUE;
404 } /* dumpstat() */
405
406 /*-------------------------------------------------------------------------*/
407 Bool
dumpstat_dest(string_t * fname)408 dumpstat_dest(string_t *fname)
409
410 /* this function dumps statistics about all destructed objects into the file
411 * $MUDLIB/<fname>. It is called by the commandparser and by debug_info().
412 * Return TRUE on success, FALSE if <fname> can't be written.
413 */
414
415 {
416 FILE *f;
417 object_t *ob;
418
419 fname = check_valid_path(fname, current_object, STR_OBJDUMP, MY_TRUE);
420 if (!fname)
421 return MY_FALSE;
422 f = fopen(get_txt(fname), "w");
423 if (!f)
424 {
425 free_mstring(fname);
426 return MY_FALSE;
427 }
428 FCOUNT_WRITE(get_txt(fname));
429
430 for (ob = newly_destructed_objs; ob; ob = ob->next_all)
431 {
432 #ifdef DEBUG
433 if (!(ob->flags & O_DESTRUCTED)) /* TODO: Can't happen */
434 continue;
435 #endif
436 fprintf(f, "%-20s ref %2"PRIdPINT" NEW\n"
437 , get_txt(ob->name)
438 , ob->ref
439 );
440 }
441
442 for (ob = destructed_objs; ob; ob = ob->next_all)
443 {
444 #ifdef DEBUG
445 if (!(ob->flags & O_DESTRUCTED)) /* TODO: Can't happen */
446 continue;
447 #endif
448 fprintf(f, "%-20s ref %2"PRIdPINT"\n"
449 , get_txt(ob->name)
450 , ob->ref
451 );
452 }
453 fclose(f);
454 free_mstring(fname);
455 return MY_TRUE;
456 } /* dumpstat_dest() */
457
458 /***************************************************************************/
459
460