tools/trace-malloc/tmreader.c

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:5513bf2a5c5f
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <string.h>
10 #include <errno.h> /* XXX push error reporting out to clients? */
11 #ifndef XP_WIN
12 #include <unistd.h>
13 #else
14 #include <stddef.h>
15 #endif
16 #include "prlog.h"
17 #include "plhash.h"
18 /* make sure this happens before tmreader.h */
19 #define PL_ARENA_CONST_ALIGN_MASK 2
20 #include "plarena.h"
21
22 #include "prnetdb.h"
23 #include "nsTraceMalloc.h"
24 #include "tmreader.h"
25
26 #undef DEBUG_tmreader
27
28 static int accum_byte(FILE *fp, uint32_t *uip)
29 {
30 int c = getc(fp);
31 if (c == EOF)
32 return 0;
33 *uip = (*uip << 8) | c;
34 return 1;
35 }
36
37 static int get_uint32(FILE *fp, uint32_t *uip)
38 {
39 int c;
40 uint32_t ui;
41
42 c = getc(fp);
43 if (c == EOF)
44 return 0;
45 ui = 0;
46 if (c & 0x80) {
47 c &= 0x7f;
48 if (c & 0x40) {
49 c &= 0x3f;
50 if (c & 0x20) {
51 c &= 0x1f;
52 if (c & 0x10) {
53 if (!accum_byte(fp, &ui))
54 return 0;
55 } else {
56 ui = (uint32_t) c;
57 }
58 if (!accum_byte(fp, &ui))
59 return 0;
60 } else {
61 ui = (uint32_t) c;
62 }
63 if (!accum_byte(fp, &ui))
64 return 0;
65 } else {
66 ui = (uint32_t) c;
67 }
68 if (!accum_byte(fp, &ui))
69 return 0;
70 } else {
71 ui = (uint32_t) c;
72 }
73 *uip = ui;
74 return 1;
75 }
76
77 static char *get_string(FILE *fp)
78 {
79 char *cp;
80 int c;
81 static char buf[256];
82 static char *bp = buf, *ep = buf + sizeof buf;
83 static size_t bsize = sizeof buf;
84
85 cp = bp;
86 do {
87 c = getc(fp);
88 if (c == EOF)
89 return 0;
90 if (cp == ep) {
91 if (bp == buf) {
92 bp = malloc(2 * bsize);
93 if (bp)
94 memcpy(bp, buf, bsize);
95 } else {
96 bp = realloc(bp, 2 * bsize);
97 }
98 if (!bp)
99 return 0;
100 cp = bp + bsize;
101 bsize *= 2;
102 ep = bp + bsize;
103 }
104 *cp++ = c;
105 } while (c != '\0');
106 return strdup(bp);
107 }
108
109 static int get_tmevent(FILE *fp, tmevent *event)
110 {
111 int c;
112 char *s;
113
114 c = getc(fp);
115 if (c == EOF)
116 return 0;
117 event->type = (char) c;
118 if (!get_uint32(fp, &event->serial))
119 return 0;
120 switch (c) {
121 case TM_EVENT_LIBRARY:
122 s = get_string(fp);
123 if (!s)
124 return 0;
125 event->u.libname = s;
126 #ifdef DEBUG_tmreader
127 fprintf(stderr, "tmevent %c %u libname=\"%s\"\n", event->type, event->serial,
128 event->u.libname);
129 #endif
130 break;
131
132 case TM_EVENT_FILENAME:
133 s = get_string(fp);
134 if (!s)
135 return 0;
136 event->u.srcname = s;
137 #ifdef DEBUG_tmreader
138 fprintf(stderr, "tmevent %c %u srcname=\"%s\"\n",
139 event->type, event->serial, event->u.srcname);
140 #endif
141 break;
142
143 case TM_EVENT_METHOD:
144 if (!get_uint32(fp, &event->u.method.library))
145 return 0;
146 if (!get_uint32(fp, &event->u.method.filename))
147 return 0;
148 if (!get_uint32(fp, &event->u.method.linenumber))
149 return 0;
150 s = get_string(fp);
151 if (!s)
152 return 0;
153 event->u.method.name = s;
154 #ifdef DEBUG_tmreader
155 fprintf(stderr, "tmevent %c %u library=%u filename=%u linenumber=%u "
156 "name=\"%s\"\n",
157 event->type, event->serial,
158 event->u.method.library, event->u.method.filename,
159 event->u.method.linenumber, event->u.method.name);
160 #endif
161 break;
162
163 case TM_EVENT_CALLSITE:
164 if (!get_uint32(fp, &event->u.site.parent))
165 return 0;
166 if (!get_uint32(fp, &event->u.site.method))
167 return 0;
168 if (!get_uint32(fp, &event->u.site.offset))
169 return 0;
170 #ifdef DEBUG_tmreader
171 fprintf(stderr, "tmevent %c %u parent=%u method=%u offset=%u\n",
172 event->type, event->serial,
173 event->u.site.parent, event->u.site.method,
174 event->u.site.offset);
175 #endif
176 break;
177
178 case TM_EVENT_MALLOC:
179 case TM_EVENT_CALLOC:
180 case TM_EVENT_FREE:
181 if (!get_uint32(fp, &event->u.alloc.interval))
182 return 0;
183 if (!get_uint32(fp, &event->u.alloc.cost))
184 return 0;
185 if (!get_uint32(fp, &event->u.alloc.ptr))
186 return 0;
187 if (!get_uint32(fp, &event->u.alloc.size))
188 return 0;
189 event->u.alloc.oldserial = 0;
190 event->u.alloc.oldptr = 0;
191 event->u.alloc.oldsize = 0;
192 #ifdef DEBUG_tmreader
193 fprintf(stderr, "tmevent %c %u interval=%u cost=%u ptr=0x%x size=%u\n",
194 event->type, event->serial,
195 event->u.alloc.interval, event->u.alloc.cost,
196 event->u.alloc.ptr, event->u.alloc.size);
197 #endif
198 #if defined(DEBUG_dp)
199 if (c == TM_EVENT_MALLOC)
200 printf("%d malloc %d 0x%p\n", event->u.alloc.cost,
201 event->u.alloc.size, event->u.alloc.ptr);
202 else if (c == TM_EVENT_CALLOC)
203 printf("%d calloc %d 0x%p\n", event->u.alloc.cost,
204 event->u.alloc.size, event->u.alloc.ptr);
205 else
206 printf("%d free %d 0x%p\n", event->u.alloc.cost,
207 event->u.alloc.size, event->u.alloc.ptr);
208 #endif
209 break;
210
211 case TM_EVENT_REALLOC:
212 if (!get_uint32(fp, &event->u.alloc.interval))
213 return 0;
214 if (!get_uint32(fp, &event->u.alloc.cost))
215 return 0;
216 if (!get_uint32(fp, &event->u.alloc.ptr))
217 return 0;
218 if (!get_uint32(fp, &event->u.alloc.size))
219 return 0;
220 if (!get_uint32(fp, &event->u.alloc.oldserial))
221 return 0;
222 if (!get_uint32(fp, &event->u.alloc.oldptr))
223 return 0;
224 if (!get_uint32(fp, &event->u.alloc.oldsize))
225 return 0;
226 #ifdef DEBUG_tmreader
227 fprintf(stderr, "tmevent %c %u interval=%u cost=%u ptr=0x%x size=%u "
228 "oldserial=%u oldptr=0x%x oldsize=%u\n",
229 event->type, event->serial,
230 event->u.alloc.interval, event->u.alloc.cost,
231 event->u.alloc.ptr, event->u.alloc.size,
232 event->u.alloc.oldserial, event->u.alloc.oldptr,
233 event->u.alloc.oldsize);
234 #endif
235 #if defined(DEBUG_dp)
236 printf("%d realloc %d 0x%p %d\n", event->u.alloc.cost,
237 event->u.alloc.size, event->u.alloc.ptr, event->u.alloc.oldsize);
238 #endif
239 break;
240
241 case TM_EVENT_STATS:
242 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxstack))
243 return 0;
244 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxdepth))
245 return 0;
246 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_parents))
247 return 0;
248 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxkids))
249 return 0;
250 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidhits))
251 return 0;
252 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidmisses))
253 return 0;
254 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidsteps))
255 return 0;
256 if (!get_uint32(fp, &event->u.stats.tmstats.callsite_recurrences))
257 return 0;
258 if (!get_uint32(fp, &event->u.stats.tmstats.backtrace_calls))
259 return 0;
260 if (!get_uint32(fp, &event->u.stats.tmstats.backtrace_failures))
261 return 0;
262 if (!get_uint32(fp, &event->u.stats.tmstats.btmalloc_failures))
263 return 0;
264 if (!get_uint32(fp, &event->u.stats.tmstats.dladdr_failures))
265 return 0;
266 if (!get_uint32(fp, &event->u.stats.tmstats.malloc_calls))
267 return 0;
268 if (!get_uint32(fp, &event->u.stats.tmstats.malloc_failures))
269 return 0;
270 if (!get_uint32(fp, &event->u.stats.tmstats.calloc_calls))
271 return 0;
272 if (!get_uint32(fp, &event->u.stats.tmstats.calloc_failures))
273 return 0;
274 if (!get_uint32(fp, &event->u.stats.tmstats.realloc_calls))
275 return 0;
276 if (!get_uint32(fp, &event->u.stats.tmstats.realloc_failures))
277 return 0;
278 if (!get_uint32(fp, &event->u.stats.tmstats.free_calls))
279 return 0;
280 if (!get_uint32(fp, &event->u.stats.tmstats.null_free_calls))
281 return 0;
282 if (!get_uint32(fp, &event->u.stats.calltree_maxkids_parent))
283 return 0;
284 if (!get_uint32(fp, &event->u.stats.calltree_maxstack_top))
285 return 0;
286 #ifdef DEBUG_tmreader
287 fprintf(stderr, "tmevent %c %u\n", event->type, event->serial);
288 #endif
289 break;
290 default:
291 fprintf(stderr, "Unknown event type 0x%x\n", (unsigned int)event->type);
292 return 0;
293 }
294 return 1;
295 }
296
297 static void *arena_alloc(void* pool, size_t size)
298 {
299 PLArenaPool* arena = (PLArenaPool*)pool;
300 void* result;
301 PL_ARENA_ALLOCATE(result, arena, size);
302 memset(result, 0, size);
303 return result;
304 }
305
306 static void *generic_alloctable(void *pool, size_t size)
307 {
308 return arena_alloc(pool, size);
309 }
310
311 static void generic_freetable(void *pool, void *item)
312 {
313 /* do nothing - arena-allocated */
314 }
315
316 static PLHashEntry *filename_allocentry(void *pool, const void *key)
317 {
318 return (PLHashEntry*)arena_alloc(pool, sizeof(PLHashEntry));
319 }
320
321 static PLHashEntry *callsite_allocentry(void *pool, const void *key)
322 {
323 return (PLHashEntry*)arena_alloc(pool, sizeof(tmcallsite));
324 }
325
326 static void init_graphnode(tmgraphnode* node)
327 {
328 node->in = node->out = NULL;
329 node->up = node->down = node->next = NULL;
330 node->low = 0;
331 node->allocs.bytes.direct = node->allocs.bytes.total = 0;
332 node->allocs.calls.direct = node->allocs.calls.total = 0;
333 node->frees.bytes.direct = node->frees.bytes.total = 0;
334 node->frees.calls.direct = node->frees.calls.total = 0;
335 node->sqsum = 0;
336 node->sort = -1;
337 }
338
339 static PLHashEntry *graphnode_allocentry(void *pool, const void *key)
340 {
341 tmgraphnode* node = (tmgraphnode*)arena_alloc(pool, sizeof(tmgraphnode));
342 if (!node)
343 return NULL;
344 init_graphnode(node);
345 return &node->entry;
346 }
347
348 static void init_method(tmmethodnode *node)
349 {
350 node->graphnode.in = node->graphnode.out = NULL;
351 node->graphnode.up = node->graphnode.down = node->graphnode.next = NULL;
352 node->graphnode.low = 0;
353 node->graphnode.allocs.bytes.direct = node->graphnode.allocs.bytes.total = 0;
354 node->graphnode.allocs.calls.direct = node->graphnode.allocs.calls.total = 0;
355 node->graphnode.frees.bytes.direct = node->graphnode.frees.bytes.total = 0;
356 node->graphnode.frees.calls.direct = node->graphnode.frees.calls.total = 0;
357 node->graphnode.sqsum = 0;
358 node->graphnode.sort = -1;
359 node->sourcefile = NULL;
360 node->linenumber = 0;
361 }
362
363 static PLHashEntry *method_allocentry(void *pool, const void *key)
364 {
365 tmmethodnode *node =
366 (tmmethodnode*) arena_alloc(pool, sizeof(tmmethodnode));
367 if (!node)
368 return NULL;
369 init_method(node);
370 return &node->graphnode.entry;
371 }
372
373 static void graphnode_freeentry(void *pool, PLHashEntry *he, unsigned flag)
374 {
375 /* Always free the value, which points to a strdup'd string. */
376 free(he->value);
377 #if 0 /* using arenas now, no freeing! */
378 /* Free the whole thing if we're told to. */
379 if (flag == HT_FREE_ENTRY)
380 free((void*) he);
381 #endif
382 }
383
384 static void component_freeentry(void *pool, PLHashEntry *he, unsigned flag)
385 {
386 if (flag == HT_FREE_ENTRY) {
387 tmgraphnode *comp = (tmgraphnode*) he;
388
389 /* Free the key, which was strdup'd (N.B. value also points to it). */
390 free((void*) tmcomponent_name(comp));
391 #if 0 /* using arenas now, no freeing! */
392 free((void*) comp);
393 #endif
394 }
395 }
396
397 static PLHashAllocOps filename_hashallocops = {
398 generic_alloctable, generic_freetable,
399 filename_allocentry, graphnode_freeentry
400 };
401
402 static PLHashAllocOps callsite_hashallocops = {
403 generic_alloctable, generic_freetable,
404 callsite_allocentry, graphnode_freeentry
405 };
406
407 static PLHashAllocOps graphnode_hashallocops = {
408 generic_alloctable, generic_freetable,
409 graphnode_allocentry, graphnode_freeentry
410 };
411
412 static PLHashAllocOps method_hashallocops = {
413 generic_alloctable, generic_freetable,
414 method_allocentry, graphnode_freeentry
415 };
416
417 static PLHashAllocOps component_hashallocops = {
418 generic_alloctable, generic_freetable,
419 graphnode_allocentry, component_freeentry
420 };
421
422 static PLHashNumber hash_serial(const void *key)
423 {
424 return (PLHashNumber) key;
425 }
426
427 tmreader *tmreader_new(const char *program, void *data)
428 {
429 tmreader *tmr;
430
431 tmr = calloc(1, sizeof *tmr);
432 if (!tmr)
433 return NULL;
434 tmr->program = program;
435 tmr->data = data;
436 PL_INIT_ARENA_POOL(&tmr->arena, "TMReader", 256*1024);
437
438 tmr->libraries = PL_NewHashTable(100, hash_serial, PL_CompareValues,
439 PL_CompareStrings, &graphnode_hashallocops,
440 &tmr->arena);
441 tmr->filenames = PL_NewHashTable(100, hash_serial, PL_CompareValues,
442 PL_CompareStrings, &filename_hashallocops,
443 &tmr->arena);
444 tmr->components = PL_NewHashTable(10000, PL_HashString, PL_CompareStrings,
445 PL_CompareValues, &component_hashallocops,
446 &tmr->arena);
447 tmr->methods = PL_NewHashTable(10000, hash_serial, PL_CompareValues,
448 PL_CompareStrings, &method_hashallocops,
449 &tmr->arena);
450 tmr->callsites = PL_NewHashTable(200000, hash_serial, PL_CompareValues,
451 PL_CompareValues, &callsite_hashallocops,
452 &tmr->arena);
453 tmr->calltree_root.entry.value = (void*) strdup("root");
454
455 if (!tmr->libraries || !tmr->components || !tmr->methods ||
456 !tmr->callsites || !tmr->calltree_root.entry.value ||
457 !tmr->filenames) {
458 tmreader_destroy(tmr);
459 return NULL;
460 }
461 return tmr;
462 }
463
464 void tmreader_destroy(tmreader *tmr)
465 {
466 if (tmr->libraries)
467 PL_HashTableDestroy(tmr->libraries);
468 if (tmr->filenames)
469 PL_HashTableDestroy(tmr->filenames);
470 if (tmr->components)
471 PL_HashTableDestroy(tmr->components);
472 if (tmr->methods)
473 PL_HashTableDestroy(tmr->methods);
474 if (tmr->callsites)
475 PL_HashTableDestroy(tmr->callsites);
476 PL_FinishArenaPool(&tmr->arena);
477 free(tmr);
478 }
479
480 int tmreader_eventloop(tmreader *tmr, const char *filename,
481 tmeventhandler eventhandler)
482 {
483 FILE *fp;
484 char buf[NS_TRACE_MALLOC_MAGIC_SIZE];
485 tmevent event;
486 static const char magic[] = NS_TRACE_MALLOC_MAGIC;
487
488 if (strcmp(filename, "-") == 0) {
489 fp = stdin;
490 } else {
491 #if defined(XP_WIN32)
492 fp = fopen(filename, "rb");
493 #else
494 fp = fopen(filename, "r");
495 #endif
496 if (!fp) {
497 fprintf(stderr, "%s: can't open %s: %s.\n",
498 tmr->program, filename, strerror(errno));
499 return 0;
500 }
501 }
502
503 if (read(fileno(fp), buf, sizeof buf) != sizeof buf ||
504 strncmp(buf, magic, sizeof buf) != 0) {
505 fprintf(stderr, "%s: bad magic string %s at start of %s.\n",
506 tmr->program, buf, filename);
507 fprintf(stderr, "either the data file is out of date,\nor your tools are out of date.\n");
508 return 0;
509 }
510
511 /* Read in ticks per second. Used to convert platform specific intervals to time values */
512 if (read(fileno(fp), &tmr->ticksPerSec, sizeof tmr->ticksPerSec) != sizeof tmr->ticksPerSec) {
513 fprintf(stderr, "%s: Cannot read ticksPerSec. Log file read error.\n",
514 tmr->program);
515 return 0;
516 }
517 tmr->ticksPerSec = PR_ntohl(tmr->ticksPerSec);
518 #ifdef DEBUG_dp
519 printf("DEBUG: ticks per sec = %d\n", tmr->ticksPerSec);
520 #endif
521 while (get_tmevent(fp, &event)) {
522 switch (event.type) {
523 case TM_EVENT_LIBRARY: {
524 const void *key;
525 PLHashNumber hash;
526 PLHashEntry **hep, *he;
527
528 key = (const void*) (uintptr_t) event.serial;
529 hash = hash_serial(key);
530 hep = PL_HashTableRawLookup(tmr->libraries, hash, key);
531 he = *hep;
532 PR_ASSERT(!he);
533 if (he) exit(2);
534
535 he = PL_HashTableRawAdd(tmr->libraries, hep, hash, key,
536 event.u.libname);
537 if (!he) {
538 perror(tmr->program);
539 return -1;
540 }
541 break;
542 }
543
544 case TM_EVENT_FILENAME: {
545 const void *key;
546 PLHashNumber hash;
547 PLHashEntry **hep, *he;
548
549 key = (const void*) (uintptr_t) event.serial;
550 hash = hash_serial(key);
551 hep = PL_HashTableRawLookup(tmr->filenames, hash, key);
552 he = *hep;
553 PR_ASSERT(!he);
554 if (he) exit(2);
555
556 he = PL_HashTableRawAdd(tmr->filenames, hep, hash, key,
557 event.u.srcname);
558 if (!he) {
559 perror(tmr->program);
560 return -1;
561 }
562 break;
563 }
564
565 case TM_EVENT_METHOD: {
566 const void *key, *sourcekey;
567 PLHashNumber hash, sourcehash;
568 PLHashEntry **hep, *he, **sourcehep, *sourcehe;
569 char *name, *head, *mark, save;
570 tmgraphnode *comp, *lib;
571 tmmethodnode *meth;
572
573 key = (const void*) (uintptr_t) event.serial;
574 hash = hash_serial(key);
575 hep = PL_HashTableRawLookup(tmr->methods, hash, key);
576 he = *hep;
577 PR_ASSERT(!he);
578 if (he) exit(2);
579
580 name = event.u.method.name;
581 he = PL_HashTableRawAdd(tmr->methods, hep, hash, key, name);
582 if (!he) {
583 perror(tmr->program);
584 return -1;
585 }
586 meth = (tmmethodnode*) he;
587
588 meth->linenumber = event.u.method.linenumber;
589 sourcekey = (const void*) (uintptr_t) event.u.method.filename;
590 sourcehash = hash_serial(sourcekey);
591 sourcehep = PL_HashTableRawLookup(tmr->filenames, sourcehash, sourcekey);
592 sourcehe = *sourcehep;
593 meth->sourcefile = filename_name(sourcehe);
594
595 head = name;
596 mark = strchr(name, ':');
597 if (!mark) {
598 mark = name;
599 while (*mark != '\0' && *mark == '_')
600 mark++;
601 head = mark;
602 mark = strchr(head, '_');
603 if (!mark) {
604 mark = strchr(head, '+');
605 if (!mark)
606 mark = head + strlen(head);
607 }
608 }
609
610 save = *mark;
611 *mark = '\0';
612 hash = PL_HashString(head);
613 hep = PL_HashTableRawLookup(tmr->components, hash, head);
614 he = *hep;
615 if (he) {
616 comp = (tmgraphnode*) he;
617 } else {
618 head = strdup(head);
619 if (head) {
620 he = PL_HashTableRawAdd(tmr->components, hep, hash, head,
621 head);
622 }
623 if (!he) {
624 perror(tmr->program);
625 return -1;
626 }
627 comp = (tmgraphnode*) he;
628
629 key = (const void*) (uintptr_t) event.u.method.library;
630 hash = hash_serial(key);
631 lib = (tmgraphnode*)
632 *PL_HashTableRawLookup(tmr->libraries, hash, key);
633 if (lib) {
634 comp->up = lib;
635 comp->next = lib->down;
636 lib->down = comp;
637 }
638 }
639 *mark = save;
640
641 meth->graphnode.up = comp;
642 meth->graphnode.next = comp->down;
643 comp->down = &(meth->graphnode);
644 break;
645 }
646
647 case TM_EVENT_CALLSITE: {
648 const void *key, *mkey;
649 PLHashNumber hash, mhash;
650 PLHashEntry **hep, *he;
651 tmcallsite *site, *parent;
652 tmmethodnode *meth;
653
654 key = (const void*) (uintptr_t) event.serial;
655 hash = hash_serial(key);
656 hep = PL_HashTableRawLookup(tmr->callsites, hash, key);
657 he = *hep;
658
659 /* there should not be an entry here! */
660 PR_ASSERT(!he);
661 if (he) exit(2);
662
663 if (event.u.site.parent == 0) {
664 parent = &tmr->calltree_root;
665 } else {
666 parent = tmreader_callsite(tmr, event.u.site.parent);
667 if (!parent) {
668 fprintf(stderr, "%s: no parent for %lu (%lu)!\n",
669 tmr->program, (unsigned long) event.serial,
670 (unsigned long) event.u.site.parent);
671 continue;
672 }
673 }
674
675 he = PL_HashTableRawAdd(tmr->callsites, hep, hash, key, NULL);
676 if (!he) {
677 perror(tmr->program);
678 return -1;
679 }
680
681 site = (tmcallsite*) he;
682 site->parent = parent;
683 site->siblings = parent->kids;
684 parent->kids = site;
685 site->kids = NULL;
686
687 mkey = (const void*) (uintptr_t) event.u.site.method;
688 mhash = hash_serial(mkey);
689 meth = (tmmethodnode*)
690 *PL_HashTableRawLookup(tmr->methods, mhash, mkey);
691 site->method = meth;
692 site->offset = event.u.site.offset;
693 site->allocs.bytes.direct = site->allocs.bytes.total = 0;
694 site->allocs.calls.direct = site->allocs.calls.total = 0;
695 site->frees.bytes.direct = site->frees.bytes.total = 0;
696 site->frees.calls.direct = site->frees.calls.total = 0;
697 break;
698 }
699
700 case TM_EVENT_MALLOC:
701 case TM_EVENT_CALLOC:
702 case TM_EVENT_REALLOC: {
703 tmcallsite *site;
704 uint32_t size, oldsize;
705 double delta, sqdelta, sqszdelta = 0;
706 tmgraphnode *comp, *lib;
707 tmmethodnode *meth;
708
709 site = tmreader_callsite(tmr, event.serial);
710 if (!site) {
711 fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n",
712 tmr->program, event.type, (unsigned long) event.serial);
713 continue;
714 }
715
716 size = event.u.alloc.size;
717 oldsize = event.u.alloc.oldsize;
718 delta = (double)size - (double)oldsize;
719 site->allocs.bytes.direct += (unsigned long)delta;
720 if (event.type != TM_EVENT_REALLOC)
721 site->allocs.calls.direct++;
722 meth = site->method;
723 if (meth) {
724 meth->graphnode.allocs.bytes.direct += (unsigned long)delta;
725 sqdelta = delta * delta;
726 if (event.type == TM_EVENT_REALLOC) {
727 sqszdelta = ((double)size * size)
728 - ((double)oldsize * oldsize);
729 meth->graphnode.sqsum += sqszdelta;
730 } else {
731 meth->graphnode.sqsum += sqdelta;
732 meth->graphnode.allocs.calls.direct++;
733 }
734 comp = meth->graphnode.up;
735 if (comp) {
736 comp->allocs.bytes.direct += (unsigned long)delta;
737 if (event.type == TM_EVENT_REALLOC) {
738 comp->sqsum += sqszdelta;
739 } else {
740 comp->sqsum += sqdelta;
741 comp->allocs.calls.direct++;
742 }
743 lib = comp->up;
744 if (lib) {
745 lib->allocs.bytes.direct += (unsigned long)delta;
746 if (event.type == TM_EVENT_REALLOC) {
747 lib->sqsum += sqszdelta;
748 } else {
749 lib->sqsum += sqdelta;
750 lib->allocs.calls.direct++;
751 }
752 }
753 }
754 }
755 break;
756 }
757
758 case TM_EVENT_FREE: {
759 tmcallsite *site;
760 uint32_t size;
761 tmgraphnode *comp, *lib;
762 tmmethodnode *meth;
763
764 site = tmreader_callsite(tmr, event.serial);
765 if (!site) {
766 fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n",
767 tmr->program, event.type, (unsigned long) event.serial);
768 continue;
769 }
770 size = event.u.alloc.size;
771 site->frees.bytes.direct += size;
772 site->frees.calls.direct++;
773 meth = site->method;
774 if (meth) {
775 meth->graphnode.frees.bytes.direct += size;
776 meth->graphnode.frees.calls.direct++;
777 comp = meth->graphnode.up;
778 if (comp) {
779 comp->frees.bytes.direct += size;
780 comp->frees.calls.direct++;
781 lib = comp->up;
782 if (lib) {
783 lib->frees.bytes.direct += size;
784 lib->frees.calls.direct++;
785 }
786 }
787 }
788 break;
789 }
790
791 case TM_EVENT_STATS:
792 break;
793 }
794
795 eventhandler(tmr, &event);
796 }
797
798 return 1;
799 }
800
801 tmgraphnode *tmreader_library(tmreader *tmr, uint32_t serial)
802 {
803 const void *key;
804 PLHashNumber hash;
805
806 key = (const void*) (uintptr_t) serial;
807 hash = hash_serial(key);
808 return (tmgraphnode*) *PL_HashTableRawLookup(tmr->libraries, hash, key);
809 }
810
811 tmgraphnode *tmreader_filename(tmreader *tmr, uint32_t serial)
812 {
813 const void *key;
814 PLHashNumber hash;
815
816 key = (const void*) (uintptr_t) serial;
817 hash = hash_serial(key);
818 return (tmgraphnode*) *PL_HashTableRawLookup(tmr->filenames, hash, key);
819 }
820
821 tmgraphnode *tmreader_component(tmreader *tmr, const char *name)
822 {
823 PLHashNumber hash;
824
825 hash = PL_HashString(name);
826 return (tmgraphnode*) *PL_HashTableRawLookup(tmr->components, hash, name);
827 }
828
829 tmmethodnode *tmreader_method(tmreader *tmr, uint32_t serial)
830 {
831 const void *key;
832 PLHashNumber hash;
833
834 key = (const void*) (uintptr_t) serial;
835 hash = hash_serial(key);
836 return (tmmethodnode*) *PL_HashTableRawLookup(tmr->methods, hash, key);
837 }
838
839 tmcallsite *tmreader_callsite(tmreader *tmr, uint32_t serial)
840 {
841 const void *key;
842 PLHashNumber hash;
843
844 key = (const void*) (uintptr_t) serial;
845 hash = hash_serial(key);
846 return (tmcallsite*) *PL_HashTableRawLookup(tmr->callsites, hash, key);
847 }
848
849 int tmgraphnode_connect(tmgraphnode *from, tmgraphnode *to, tmcallsite *site)
850 {
851 tmgraphlink *outlink;
852 tmgraphedge *edge;
853
854 for (outlink = from->out; outlink; outlink = outlink->next) {
855 if (outlink->node == to) {
856 /*
857 * Say the stack looks like this: ... => JS => js => JS => js.
858 * We must avoid overcounting JS=>js because the first edge total
859 * includes the second JS=>js edge's total (which is because the
860 * lower site's total includes all its kids' totals).
861 */
862 edge = TM_LINK_TO_EDGE(outlink, TM_EDGE_OUT_LINK);
863 if (!to->low || to->low < from->low) {
864 /* Add the direct and total counts to edge->allocs. */
865 edge->allocs.bytes.direct += site->allocs.bytes.direct;
866 edge->allocs.bytes.total += site->allocs.bytes.total;
867 edge->allocs.calls.direct += site->allocs.calls.direct;
868 edge->allocs.calls.total += site->allocs.calls.total;
869
870 /* Now update the free counts. */
871 edge->frees.bytes.direct += site->frees.bytes.direct;
872 edge->frees.bytes.total += site->frees.bytes.total;
873 edge->frees.calls.direct += site->frees.calls.direct;
874 edge->frees.calls.total += site->frees.calls.total;
875 }
876 return 1;
877 }
878 }
879
880 edge = (tmgraphedge*) malloc(sizeof(tmgraphedge));
881 if (!edge)
882 return 0;
883 edge->links[TM_EDGE_OUT_LINK].node = to;
884 edge->links[TM_EDGE_OUT_LINK].next = from->out;
885 from->out = &edge->links[TM_EDGE_OUT_LINK];
886 edge->links[TM_EDGE_IN_LINK].node = from;
887 edge->links[TM_EDGE_IN_LINK].next = to->in;
888 to->in = &edge->links[TM_EDGE_IN_LINK];
889 edge->allocs = site->allocs;
890 edge->frees = site->frees;
891 return 1;
892 }

mercurial