Bug 473045 - Update to nsIHandlerApp for win7 jump lists (plus tests). r=bz
[mozilla-central.git] / tools / trace-malloc / tmreader.c
blobf33e934345871ba75e579e6e743de39320b5720a
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is tmreader.h/tmreader.c code, released
17 * July 7, 2000.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2000
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Brendan Eich, 7-July-2000
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <errno.h> /* XXX push error reporting out to clients? */
44 #ifndef XP_WIN
45 #include <unistd.h>
46 #else
47 #include <stddef.h>
48 #endif
49 #include "prlog.h"
50 #include "plhash.h"
51 /* make sure this happens before tmreader.h */
52 #define PL_ARENA_CONST_ALIGN_MASK 2
53 #include "plarena.h"
55 #include "prnetdb.h"
56 #include "nsTraceMalloc.h"
57 #include "tmreader.h"
59 #undef DEBUG_tmreader
61 static int accum_byte(FILE *fp, uint32 *uip)
63 int c = getc(fp);
64 if (c == EOF)
65 return 0;
66 *uip = (*uip << 8) | c;
67 return 1;
70 static int get_uint32(FILE *fp, uint32 *uip)
72 int c;
73 uint32 ui;
75 c = getc(fp);
76 if (c == EOF)
77 return 0;
78 ui = 0;
79 if (c & 0x80) {
80 c &= 0x7f;
81 if (c & 0x40) {
82 c &= 0x3f;
83 if (c & 0x20) {
84 c &= 0x1f;
85 if (c & 0x10) {
86 if (!accum_byte(fp, &ui))
87 return 0;
88 } else {
89 ui = (uint32) c;
91 if (!accum_byte(fp, &ui))
92 return 0;
93 } else {
94 ui = (uint32) c;
96 if (!accum_byte(fp, &ui))
97 return 0;
98 } else {
99 ui = (uint32) c;
101 if (!accum_byte(fp, &ui))
102 return 0;
103 } else {
104 ui = (uint32) c;
106 *uip = ui;
107 return 1;
110 static char *get_string(FILE *fp)
112 char *cp;
113 int c;
114 static char buf[256];
115 static char *bp = buf, *ep = buf + sizeof buf;
116 static size_t bsize = sizeof buf;
118 cp = bp;
119 do {
120 c = getc(fp);
121 if (c == EOF)
122 return 0;
123 if (cp == ep) {
124 if (bp == buf) {
125 bp = malloc(2 * bsize);
126 if (bp)
127 memcpy(bp, buf, bsize);
128 } else {
129 bp = realloc(bp, 2 * bsize);
131 if (!bp)
132 return 0;
133 cp = bp + bsize;
134 bsize *= 2;
135 ep = bp + bsize;
137 *cp++ = c;
138 } while (c != '\0');
139 return strdup(bp);
142 static int get_tmevent(FILE *fp, tmevent *event)
144 int c;
145 char *s;
147 c = getc(fp);
148 if (c == EOF)
149 return 0;
150 event->type = (char) c;
151 if (!get_uint32(fp, &event->serial))
152 return 0;
153 switch (c) {
154 case TM_EVENT_LIBRARY:
155 s = get_string(fp);
156 if (!s)
157 return 0;
158 event->u.libname = s;
159 #ifdef DEBUG_tmreader
160 fprintf(stderr, "tmevent %c %u libname=\"%s\"\n", event->type, event->serial,
161 event->u.libname);
162 #endif
163 break;
165 case TM_EVENT_FILENAME:
166 s = get_string(fp);
167 if (!s)
168 return 0;
169 event->u.srcname = s;
170 #ifdef DEBUG_tmreader
171 fprintf(stderr, "tmevent %c %u srcname=\"%s\"\n",
172 event->type, event->serial, event->u.srcname);
173 #endif
174 break;
176 case TM_EVENT_METHOD:
177 if (!get_uint32(fp, &event->u.method.library))
178 return 0;
179 if (!get_uint32(fp, &event->u.method.filename))
180 return 0;
181 if (!get_uint32(fp, &event->u.method.linenumber))
182 return 0;
183 s = get_string(fp);
184 if (!s)
185 return 0;
186 event->u.method.name = s;
187 #ifdef DEBUG_tmreader
188 fprintf(stderr, "tmevent %c %u library=%u filename=%u linenumber=%u "
189 "name=\"%s\"\n",
190 event->type, event->serial,
191 event->u.method.library, event->u.method.filename,
192 event->u.method.linenumber, event->u.method.name);
193 #endif
194 break;
196 case TM_EVENT_CALLSITE:
197 if (!get_uint32(fp, &event->u.site.parent))
198 return 0;
199 if (!get_uint32(fp, &event->u.site.method))
200 return 0;
201 if (!get_uint32(fp, &event->u.site.offset))
202 return 0;
203 #ifdef DEBUG_tmreader
204 fprintf(stderr, "tmevent %c %u parent=%u method=%u offset=%u\n",
205 event->type, event->serial,
206 event->u.site.parent, event->u.site.method,
207 event->u.site.offset);
208 #endif
209 break;
211 case TM_EVENT_MALLOC:
212 case TM_EVENT_CALLOC:
213 case TM_EVENT_FREE:
214 if (!get_uint32(fp, &event->u.alloc.interval))
215 return 0;
216 if (!get_uint32(fp, &event->u.alloc.cost))
217 return 0;
218 if (!get_uint32(fp, &event->u.alloc.ptr))
219 return 0;
220 if (!get_uint32(fp, &event->u.alloc.size))
221 return 0;
222 event->u.alloc.oldserial = 0;
223 event->u.alloc.oldptr = 0;
224 event->u.alloc.oldsize = 0;
225 #ifdef DEBUG_tmreader
226 fprintf(stderr, "tmevent %c %u interval=%u cost=%u ptr=0x%x size=%u\n",
227 event->type, event->serial,
228 event->u.alloc.interval, event->u.alloc.cost,
229 event->u.alloc.ptr, event->u.alloc.size);
230 #endif
231 #if defined(DEBUG_dp)
232 if (c == TM_EVENT_MALLOC)
233 printf("%d malloc %d 0x%p\n", event->u.alloc.cost,
234 event->u.alloc.size, event->u.alloc.ptr);
235 else if (c == TM_EVENT_CALLOC)
236 printf("%d calloc %d 0x%p\n", event->u.alloc.cost,
237 event->u.alloc.size, event->u.alloc.ptr);
238 else
239 printf("%d free %d 0x%p\n", event->u.alloc.cost,
240 event->u.alloc.size, event->u.alloc.ptr);
241 #endif
242 break;
244 case TM_EVENT_REALLOC:
245 if (!get_uint32(fp, &event->u.alloc.interval))
246 return 0;
247 if (!get_uint32(fp, &event->u.alloc.cost))
248 return 0;
249 if (!get_uint32(fp, &event->u.alloc.ptr))
250 return 0;
251 if (!get_uint32(fp, &event->u.alloc.size))
252 return 0;
253 if (!get_uint32(fp, &event->u.alloc.oldserial))
254 return 0;
255 if (!get_uint32(fp, &event->u.alloc.oldptr))
256 return 0;
257 if (!get_uint32(fp, &event->u.alloc.oldsize))
258 return 0;
259 #ifdef DEBUG_tmreader
260 fprintf(stderr, "tmevent %c %u interval=%u cost=%u ptr=0x%x size=%u "
261 "oldserial=%u oldptr=0x%x oldsize=%u\n",
262 event->type, event->serial,
263 event->u.alloc.interval, event->u.alloc.cost,
264 event->u.alloc.ptr, event->u.alloc.size,
265 event->u.alloc.oldserial, event->u.alloc.oldptr,
266 event->u.alloc.oldsize);
267 #endif
268 #if defined(DEBUG_dp)
269 printf("%d realloc %d 0x%p %d\n", event->u.alloc.cost,
270 event->u.alloc.size, event->u.alloc.ptr, event->u.alloc.oldsize);
271 #endif
272 break;
274 case TM_EVENT_STATS:
275 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxstack))
276 return 0;
277 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxdepth))
278 return 0;
279 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_parents))
280 return 0;
281 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxkids))
282 return 0;
283 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidhits))
284 return 0;
285 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidmisses))
286 return 0;
287 if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidsteps))
288 return 0;
289 if (!get_uint32(fp, &event->u.stats.tmstats.callsite_recurrences))
290 return 0;
291 if (!get_uint32(fp, &event->u.stats.tmstats.backtrace_calls))
292 return 0;
293 if (!get_uint32(fp, &event->u.stats.tmstats.backtrace_failures))
294 return 0;
295 if (!get_uint32(fp, &event->u.stats.tmstats.btmalloc_failures))
296 return 0;
297 if (!get_uint32(fp, &event->u.stats.tmstats.dladdr_failures))
298 return 0;
299 if (!get_uint32(fp, &event->u.stats.tmstats.malloc_calls))
300 return 0;
301 if (!get_uint32(fp, &event->u.stats.tmstats.malloc_failures))
302 return 0;
303 if (!get_uint32(fp, &event->u.stats.tmstats.calloc_calls))
304 return 0;
305 if (!get_uint32(fp, &event->u.stats.tmstats.calloc_failures))
306 return 0;
307 if (!get_uint32(fp, &event->u.stats.tmstats.realloc_calls))
308 return 0;
309 if (!get_uint32(fp, &event->u.stats.tmstats.realloc_failures))
310 return 0;
311 if (!get_uint32(fp, &event->u.stats.tmstats.free_calls))
312 return 0;
313 if (!get_uint32(fp, &event->u.stats.tmstats.null_free_calls))
314 return 0;
315 if (!get_uint32(fp, &event->u.stats.calltree_maxkids_parent))
316 return 0;
317 if (!get_uint32(fp, &event->u.stats.calltree_maxstack_top))
318 return 0;
319 #ifdef DEBUG_tmreader
320 fprintf(stderr, "tmevent %c %u\n", event->type, event->serial);
321 #endif
322 break;
323 default:
324 fprintf(stderr, "Unknown event type %c\n", event->type);
325 return 0;
327 return 1;
330 static void *arena_alloc(void* pool, PRSize size)
332 PLArenaPool* arena = (PLArenaPool*)pool;
333 void* result;
334 PL_ARENA_ALLOCATE(result, arena, size);
335 memset(result, 0, size);
336 return result;
339 static void *generic_alloctable(void *pool, PRSize size)
341 return arena_alloc(pool, size);
344 static void generic_freetable(void *pool, void *item)
346 /* do nothing - arena-allocated */
349 static PLHashEntry *filename_allocentry(void *pool, const void *key)
351 return (PLHashEntry*)arena_alloc(pool, sizeof(PLHashEntry));
354 static PLHashEntry *callsite_allocentry(void *pool, const void *key)
356 return (PLHashEntry*)arena_alloc(pool, sizeof(tmcallsite));
359 static void init_graphnode(tmgraphnode* node)
361 node->in = node->out = NULL;
362 node->up = node->down = node->next = NULL;
363 node->low = 0;
364 node->allocs.bytes.direct = node->allocs.bytes.total = 0;
365 node->allocs.calls.direct = node->allocs.calls.total = 0;
366 node->frees.bytes.direct = node->frees.bytes.total = 0;
367 node->frees.calls.direct = node->frees.calls.total = 0;
368 node->sqsum = 0;
369 node->sort = -1;
372 static PLHashEntry *graphnode_allocentry(void *pool, const void *key)
374 tmgraphnode* node = (tmgraphnode*)arena_alloc(pool, sizeof(tmgraphnode));
375 if (!node)
376 return NULL;
377 init_graphnode(node);
378 return &node->entry;
381 static void init_method(tmmethodnode *node)
383 node->graphnode.in = node->graphnode.out = NULL;
384 node->graphnode.up = node->graphnode.down = node->graphnode.next = NULL;
385 node->graphnode.low = 0;
386 node->graphnode.allocs.bytes.direct = node->graphnode.allocs.bytes.total = 0;
387 node->graphnode.allocs.calls.direct = node->graphnode.allocs.calls.total = 0;
388 node->graphnode.frees.bytes.direct = node->graphnode.frees.bytes.total = 0;
389 node->graphnode.frees.calls.direct = node->graphnode.frees.calls.total = 0;
390 node->graphnode.sqsum = 0;
391 node->graphnode.sort = -1;
392 node->sourcefile = NULL;
393 node->linenumber = 0;
396 static PLHashEntry *method_allocentry(void *pool, const void *key)
398 tmmethodnode *node =
399 (tmmethodnode*) arena_alloc(pool, sizeof(tmmethodnode));
400 if (!node)
401 return NULL;
402 init_method(node);
403 return &node->graphnode.entry;
406 static void graphnode_freeentry(void *pool, PLHashEntry *he, PRUintn flag)
408 /* Always free the value, which points to a strdup'd string. */
409 free(he->value);
410 #if 0 /* using arenas now, no freeing! */
411 /* Free the whole thing if we're told to. */
412 if (flag == HT_FREE_ENTRY)
413 free((void*) he);
414 #endif
417 static void component_freeentry(void *pool, PLHashEntry *he, PRUintn flag)
419 if (flag == HT_FREE_ENTRY) {
420 tmgraphnode *comp = (tmgraphnode*) he;
422 /* Free the key, which was strdup'd (N.B. value also points to it). */
423 free((void*) tmcomponent_name(comp));
424 #if 0 /* using arenas now, no freeing! */
425 free((void*) comp);
426 #endif
430 static PLHashAllocOps filename_hashallocops = {
431 generic_alloctable, generic_freetable,
432 filename_allocentry, graphnode_freeentry
435 static PLHashAllocOps callsite_hashallocops = {
436 generic_alloctable, generic_freetable,
437 callsite_allocentry, graphnode_freeentry
440 static PLHashAllocOps graphnode_hashallocops = {
441 generic_alloctable, generic_freetable,
442 graphnode_allocentry, graphnode_freeentry
445 static PLHashAllocOps method_hashallocops = {
446 generic_alloctable, generic_freetable,
447 method_allocentry, graphnode_freeentry
450 static PLHashAllocOps component_hashallocops = {
451 generic_alloctable, generic_freetable,
452 graphnode_allocentry, component_freeentry
455 static PLHashNumber hash_serial(const void *key)
457 return (PLHashNumber) key;
460 tmreader *tmreader_new(const char *program, void *data)
462 tmreader *tmr;
464 tmr = calloc(1, sizeof *tmr);
465 if (!tmr)
466 return NULL;
467 tmr->program = program;
468 tmr->data = data;
469 PL_INIT_ARENA_POOL(&tmr->arena, "TMReader", 256*1024);
471 tmr->libraries = PL_NewHashTable(100, hash_serial, PL_CompareValues,
472 PL_CompareStrings, &graphnode_hashallocops,
473 &tmr->arena);
474 tmr->filenames = PL_NewHashTable(100, hash_serial, PL_CompareValues,
475 PL_CompareStrings, &filename_hashallocops,
476 &tmr->arena);
477 tmr->components = PL_NewHashTable(10000, PL_HashString, PL_CompareStrings,
478 PL_CompareValues, &component_hashallocops,
479 &tmr->arena);
480 tmr->methods = PL_NewHashTable(10000, hash_serial, PL_CompareValues,
481 PL_CompareStrings, &method_hashallocops,
482 &tmr->arena);
483 tmr->callsites = PL_NewHashTable(200000, hash_serial, PL_CompareValues,
484 PL_CompareValues, &callsite_hashallocops,
485 &tmr->arena);
486 tmr->calltree_root.entry.value = (void*) strdup("root");
488 if (!tmr->libraries || !tmr->components || !tmr->methods ||
489 !tmr->callsites || !tmr->calltree_root.entry.value ||
490 !tmr->filenames) {
491 tmreader_destroy(tmr);
492 return NULL;
494 return tmr;
497 void tmreader_destroy(tmreader *tmr)
499 if (tmr->libraries)
500 PL_HashTableDestroy(tmr->libraries);
501 if (tmr->filenames)
502 PL_HashTableDestroy(tmr->filenames);
503 if (tmr->components)
504 PL_HashTableDestroy(tmr->components);
505 if (tmr->methods)
506 PL_HashTableDestroy(tmr->methods);
507 if (tmr->callsites)
508 PL_HashTableDestroy(tmr->callsites);
509 PL_FinishArenaPool(&tmr->arena);
510 free(tmr);
513 int tmreader_eventloop(tmreader *tmr, const char *filename,
514 tmeventhandler eventhandler)
516 FILE *fp;
517 char buf[NS_TRACE_MALLOC_MAGIC_SIZE];
518 tmevent event;
519 static const char magic[] = NS_TRACE_MALLOC_MAGIC;
521 if (strcmp(filename, "-") == 0) {
522 fp = stdin;
523 } else {
524 #if defined(XP_WIN32)
525 fp = fopen(filename, "rb");
526 #else
527 fp = fopen(filename, "r");
528 #endif
529 if (!fp) {
530 fprintf(stderr, "%s: can't open %s: %s.\n",
531 tmr->program, filename, strerror(errno));
532 return 0;
536 if (read(fileno(fp), buf, sizeof buf) != sizeof buf ||
537 strncmp(buf, magic, sizeof buf) != 0) {
538 fprintf(stderr, "%s: bad magic string %s at start of %s.\n",
539 tmr->program, buf, filename);
540 fprintf(stderr, "either the data file is out of date,\nor your tools are out of date.\n");
541 return 0;
544 /* Read in ticks per second. Used to convert platform specific intervals to time values */
545 if (read(fileno(fp), &tmr->ticksPerSec, sizeof tmr->ticksPerSec) != sizeof tmr->ticksPerSec) {
546 fprintf(stderr, "%s: Cannot read ticksPerSec. Log file read error.\n",
547 tmr->program);
548 return 0;
550 tmr->ticksPerSec = PR_ntohl(tmr->ticksPerSec);
551 #ifdef DEBUG_dp
552 printf("DEBUG: ticks per sec = %d\n", tmr->ticksPerSec);
553 #endif
554 while (get_tmevent(fp, &event)) {
555 switch (event.type) {
556 case TM_EVENT_LIBRARY: {
557 const void *key;
558 PLHashNumber hash;
559 PLHashEntry **hep, *he;
561 key = (const void*) event.serial;
562 hash = hash_serial(key);
563 hep = PL_HashTableRawLookup(tmr->libraries, hash, key);
564 he = *hep;
565 PR_ASSERT(!he);
566 if (he) exit(2);
568 he = PL_HashTableRawAdd(tmr->libraries, hep, hash, key,
569 event.u.libname);
570 if (!he) {
571 perror(tmr->program);
572 return -1;
574 break;
577 case TM_EVENT_FILENAME: {
578 const void *key;
579 PLHashNumber hash;
580 PLHashEntry **hep, *he;
582 key = (const void*) event.serial;
583 hash = hash_serial(key);
584 hep = PL_HashTableRawLookup(tmr->filenames, hash, key);
585 he = *hep;
586 PR_ASSERT(!he);
587 if (he) exit(2);
589 he = PL_HashTableRawAdd(tmr->filenames, hep, hash, key,
590 event.u.srcname);
591 if (!he) {
592 perror(tmr->program);
593 return -1;
595 break;
598 case TM_EVENT_METHOD: {
599 const void *key, *sourcekey;
600 PLHashNumber hash, sourcehash;
601 PLHashEntry **hep, *he, **sourcehep, *sourcehe;
602 char *name, *head, *mark, save;
603 tmgraphnode *comp, *lib;
604 tmmethodnode *meth;
606 key = (const void*) event.serial;
607 hash = hash_serial(key);
608 hep = PL_HashTableRawLookup(tmr->methods, hash, key);
609 he = *hep;
610 PR_ASSERT(!he);
611 if (he) exit(2);
613 name = event.u.method.name;
614 he = PL_HashTableRawAdd(tmr->methods, hep, hash, key, name);
615 if (!he) {
616 perror(tmr->program);
617 return -1;
619 meth = (tmmethodnode*) he;
621 meth->linenumber = event.u.method.linenumber;
622 sourcekey = (const void*)event.u.method.filename;
623 sourcehash = hash_serial(sourcekey);
624 sourcehep = PL_HashTableRawLookup(tmr->filenames, sourcehash, sourcekey);
625 sourcehe = *sourcehep;
626 meth->sourcefile = filename_name(sourcehe);
628 head = name;
629 mark = strchr(name, ':');
630 if (!mark) {
631 mark = name;
632 while (*mark != '\0' && *mark == '_')
633 mark++;
634 head = mark;
635 mark = strchr(head, '_');
636 if (!mark) {
637 mark = strchr(head, '+');
638 if (!mark)
639 mark = head + strlen(head);
643 save = *mark;
644 *mark = '\0';
645 hash = PL_HashString(head);
646 hep = PL_HashTableRawLookup(tmr->components, hash, head);
647 he = *hep;
648 if (he) {
649 comp = (tmgraphnode*) he;
650 } else {
651 head = strdup(head);
652 if (head) {
653 he = PL_HashTableRawAdd(tmr->components, hep, hash, head,
654 head);
656 if (!he) {
657 perror(tmr->program);
658 return -1;
660 comp = (tmgraphnode*) he;
662 key = (const void*) event.u.method.library;
663 hash = hash_serial(key);
664 lib = (tmgraphnode*)
665 *PL_HashTableRawLookup(tmr->libraries, hash, key);
666 if (lib) {
667 comp->up = lib;
668 comp->next = lib->down;
669 lib->down = comp;
672 *mark = save;
674 meth->graphnode.up = comp;
675 meth->graphnode.next = comp->down;
676 comp->down = &(meth->graphnode);
677 break;
680 case TM_EVENT_CALLSITE: {
681 const void *key, *mkey;
682 PLHashNumber hash, mhash;
683 PLHashEntry **hep, *he;
684 tmcallsite *site, *parent;
685 tmmethodnode *meth;
687 key = (const void*) event.serial;
688 hash = hash_serial(key);
689 hep = PL_HashTableRawLookup(tmr->callsites, hash, key);
690 he = *hep;
692 /* there should not be an entry here! */
693 PR_ASSERT(!he);
694 if (he) exit(2);
696 if (event.u.site.parent == 0) {
697 parent = &tmr->calltree_root;
698 } else {
699 parent = tmreader_callsite(tmr, event.u.site.parent);
700 if (!parent) {
701 fprintf(stderr, "%s: no parent for %lu (%lu)!\n",
702 tmr->program, (unsigned long) event.serial,
703 (unsigned long) event.u.site.parent);
704 continue;
708 he = PL_HashTableRawAdd(tmr->callsites, hep, hash, key, NULL);
709 if (!he) {
710 perror(tmr->program);
711 return -1;
714 site = (tmcallsite*) he;
715 site->parent = parent;
716 site->siblings = parent->kids;
717 parent->kids = site;
718 site->kids = NULL;
720 mkey = (const void*) event.u.site.method;
721 mhash = hash_serial(mkey);
722 meth = (tmmethodnode*)
723 *PL_HashTableRawLookup(tmr->methods, mhash, mkey);
724 site->method = meth;
725 site->offset = event.u.site.offset;
726 site->allocs.bytes.direct = site->allocs.bytes.total = 0;
727 site->allocs.calls.direct = site->allocs.calls.total = 0;
728 site->frees.bytes.direct = site->frees.bytes.total = 0;
729 site->frees.calls.direct = site->frees.calls.total = 0;
730 break;
733 case TM_EVENT_MALLOC:
734 case TM_EVENT_CALLOC:
735 case TM_EVENT_REALLOC: {
736 tmcallsite *site;
737 uint32 size, oldsize;
738 double delta, sqdelta, sqszdelta = 0;
739 tmgraphnode *comp, *lib;
740 tmmethodnode *meth;
742 site = tmreader_callsite(tmr, event.serial);
743 if (!site) {
744 fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n",
745 tmr->program, event.type, (unsigned long) event.serial);
746 continue;
749 size = event.u.alloc.size;
750 oldsize = event.u.alloc.oldsize;
751 delta = (double)size - (double)oldsize;
752 site->allocs.bytes.direct += (unsigned long)delta;
753 if (event.type != TM_EVENT_REALLOC)
754 site->allocs.calls.direct++;
755 meth = site->method;
756 if (meth) {
757 meth->graphnode.allocs.bytes.direct += (unsigned long)delta;
758 sqdelta = delta * delta;
759 if (event.type == TM_EVENT_REALLOC) {
760 sqszdelta = ((double)size * size)
761 - ((double)oldsize * oldsize);
762 meth->graphnode.sqsum += sqszdelta;
763 } else {
764 meth->graphnode.sqsum += sqdelta;
765 meth->graphnode.allocs.calls.direct++;
767 comp = meth->graphnode.up;
768 if (comp) {
769 comp->allocs.bytes.direct += (unsigned long)delta;
770 if (event.type == TM_EVENT_REALLOC) {
771 comp->sqsum += sqszdelta;
772 } else {
773 comp->sqsum += sqdelta;
774 comp->allocs.calls.direct++;
776 lib = comp->up;
777 if (lib) {
778 lib->allocs.bytes.direct += (unsigned long)delta;
779 if (event.type == TM_EVENT_REALLOC) {
780 lib->sqsum += sqszdelta;
781 } else {
782 lib->sqsum += sqdelta;
783 lib->allocs.calls.direct++;
788 break;
791 case TM_EVENT_FREE: {
792 tmcallsite *site;
793 uint32 size;
794 tmgraphnode *comp, *lib;
795 tmmethodnode *meth;
797 site = tmreader_callsite(tmr, event.serial);
798 if (!site) {
799 fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n",
800 tmr->program, event.type, (unsigned long) event.serial);
801 continue;
803 size = event.u.alloc.size;
804 site->frees.bytes.direct += size;
805 site->frees.calls.direct++;
806 meth = site->method;
807 if (meth) {
808 meth->graphnode.frees.bytes.direct += size;
809 meth->graphnode.frees.calls.direct++;
810 comp = meth->graphnode.up;
811 if (comp) {
812 comp->frees.bytes.direct += size;
813 comp->frees.calls.direct++;
814 lib = comp->up;
815 if (lib) {
816 lib->frees.bytes.direct += size;
817 lib->frees.calls.direct++;
821 break;
824 case TM_EVENT_STATS:
825 break;
828 eventhandler(tmr, &event);
831 return 1;
834 tmgraphnode *tmreader_library(tmreader *tmr, uint32 serial)
836 const void *key;
837 PLHashNumber hash;
839 key = (const void*) serial;
840 hash = hash_serial(key);
841 return (tmgraphnode*) *PL_HashTableRawLookup(tmr->libraries, hash, key);
844 tmgraphnode *tmreader_filename(tmreader *tmr, uint32 serial)
846 const void *key;
847 PLHashNumber hash;
849 key = (const void*) serial;
850 hash = hash_serial(key);
851 return (tmgraphnode*) *PL_HashTableRawLookup(tmr->filenames, hash, key);
854 tmgraphnode *tmreader_component(tmreader *tmr, const char *name)
856 PLHashNumber hash;
858 hash = PL_HashString(name);
859 return (tmgraphnode*) *PL_HashTableRawLookup(tmr->components, hash, name);
862 tmmethodnode *tmreader_method(tmreader *tmr, uint32 serial)
864 const void *key;
865 PLHashNumber hash;
867 key = (const void*) serial;
868 hash = hash_serial(key);
869 return (tmmethodnode*) *PL_HashTableRawLookup(tmr->methods, hash, key);
872 tmcallsite *tmreader_callsite(tmreader *tmr, uint32 serial)
874 const void *key;
875 PLHashNumber hash;
877 key = (const void*) serial;
878 hash = hash_serial(key);
879 return (tmcallsite*) *PL_HashTableRawLookup(tmr->callsites, hash, key);
882 int tmgraphnode_connect(tmgraphnode *from, tmgraphnode *to, tmcallsite *site)
884 tmgraphlink *outlink;
885 tmgraphedge *edge;
887 for (outlink = from->out; outlink; outlink = outlink->next) {
888 if (outlink->node == to) {
890 * Say the stack looks like this: ... => JS => js => JS => js.
891 * We must avoid overcounting JS=>js because the first edge total
892 * includes the second JS=>js edge's total (which is because the
893 * lower site's total includes all its kids' totals).
895 edge = TM_LINK_TO_EDGE(outlink, TM_EDGE_OUT_LINK);
896 if (!to->low || to->low < from->low) {
897 /* Add the direct and total counts to edge->allocs. */
898 edge->allocs.bytes.direct += site->allocs.bytes.direct;
899 edge->allocs.bytes.total += site->allocs.bytes.total;
900 edge->allocs.calls.direct += site->allocs.calls.direct;
901 edge->allocs.calls.total += site->allocs.calls.total;
903 /* Now update the free counts. */
904 edge->frees.bytes.direct += site->frees.bytes.direct;
905 edge->frees.bytes.total += site->frees.bytes.total;
906 edge->frees.calls.direct += site->frees.calls.direct;
907 edge->frees.calls.total += site->frees.calls.total;
909 return 1;
913 edge = (tmgraphedge*) malloc(sizeof(tmgraphedge));
914 if (!edge)
915 return 0;
916 edge->links[TM_EDGE_OUT_LINK].node = to;
917 edge->links[TM_EDGE_OUT_LINK].next = from->out;
918 from->out = &edge->links[TM_EDGE_OUT_LINK];
919 edge->links[TM_EDGE_IN_LINK].node = from;
920 edge->links[TM_EDGE_IN_LINK].next = to->in;
921 to->in = &edge->links[TM_EDGE_IN_LINK];
922 edge->allocs = site->allocs;
923 edge->frees = site->frees;
924 return 1;