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
16 * The Original Code is tmreader.h/tmreader.c code, released
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.
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 ***** */
43 #include <errno.h> /* XXX push error reporting out to clients? */
51 /* make sure this happens before tmreader.h */
52 #define PL_ARENA_CONST_ALIGN_MASK 2
56 #include "nsTraceMalloc.h"
61 static int accum_byte(FILE *fp
, uint32
*uip
)
66 *uip
= (*uip
<< 8) | c
;
70 static int get_uint32(FILE *fp
, uint32
*uip
)
86 if (!accum_byte(fp
, &ui
))
91 if (!accum_byte(fp
, &ui
))
96 if (!accum_byte(fp
, &ui
))
101 if (!accum_byte(fp
, &ui
))
110 static char *get_string(FILE *fp
)
114 static char buf
[256];
115 static char *bp
= buf
, *ep
= buf
+ sizeof buf
;
116 static size_t bsize
= sizeof buf
;
125 bp
= malloc(2 * bsize
);
127 memcpy(bp
, buf
, bsize
);
129 bp
= realloc(bp
, 2 * bsize
);
142 static int get_tmevent(FILE *fp
, tmevent
*event
)
150 event
->type
= (char) c
;
151 if (!get_uint32(fp
, &event
->serial
))
154 case TM_EVENT_LIBRARY
:
158 event
->u
.libname
= s
;
159 #ifdef DEBUG_tmreader
160 fprintf(stderr
, "tmevent %c %u libname=\"%s\"\n", event
->type
, event
->serial
,
165 case TM_EVENT_FILENAME
:
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
);
176 case TM_EVENT_METHOD
:
177 if (!get_uint32(fp
, &event
->u
.method
.library
))
179 if (!get_uint32(fp
, &event
->u
.method
.filename
))
181 if (!get_uint32(fp
, &event
->u
.method
.linenumber
))
186 event
->u
.method
.name
= s
;
187 #ifdef DEBUG_tmreader
188 fprintf(stderr
, "tmevent %c %u library=%u filename=%u linenumber=%u "
190 event
->type
, event
->serial
,
191 event
->u
.method
.library
, event
->u
.method
.filename
,
192 event
->u
.method
.linenumber
, event
->u
.method
.name
);
196 case TM_EVENT_CALLSITE
:
197 if (!get_uint32(fp
, &event
->u
.site
.parent
))
199 if (!get_uint32(fp
, &event
->u
.site
.method
))
201 if (!get_uint32(fp
, &event
->u
.site
.offset
))
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
);
211 case TM_EVENT_MALLOC
:
212 case TM_EVENT_CALLOC
:
214 if (!get_uint32(fp
, &event
->u
.alloc
.interval
))
216 if (!get_uint32(fp
, &event
->u
.alloc
.cost
))
218 if (!get_uint32(fp
, &event
->u
.alloc
.ptr
))
220 if (!get_uint32(fp
, &event
->u
.alloc
.size
))
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
);
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
);
239 printf("%d free %d 0x%p\n", event
->u
.alloc
.cost
,
240 event
->u
.alloc
.size
, event
->u
.alloc
.ptr
);
244 case TM_EVENT_REALLOC
:
245 if (!get_uint32(fp
, &event
->u
.alloc
.interval
))
247 if (!get_uint32(fp
, &event
->u
.alloc
.cost
))
249 if (!get_uint32(fp
, &event
->u
.alloc
.ptr
))
251 if (!get_uint32(fp
, &event
->u
.alloc
.size
))
253 if (!get_uint32(fp
, &event
->u
.alloc
.oldserial
))
255 if (!get_uint32(fp
, &event
->u
.alloc
.oldptr
))
257 if (!get_uint32(fp
, &event
->u
.alloc
.oldsize
))
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
);
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
);
275 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.calltree_maxstack
))
277 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.calltree_maxdepth
))
279 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.calltree_parents
))
281 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.calltree_maxkids
))
283 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.calltree_kidhits
))
285 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.calltree_kidmisses
))
287 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.calltree_kidsteps
))
289 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.callsite_recurrences
))
291 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.backtrace_calls
))
293 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.backtrace_failures
))
295 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.btmalloc_failures
))
297 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.dladdr_failures
))
299 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.malloc_calls
))
301 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.malloc_failures
))
303 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.calloc_calls
))
305 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.calloc_failures
))
307 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.realloc_calls
))
309 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.realloc_failures
))
311 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.free_calls
))
313 if (!get_uint32(fp
, &event
->u
.stats
.tmstats
.null_free_calls
))
315 if (!get_uint32(fp
, &event
->u
.stats
.calltree_maxkids_parent
))
317 if (!get_uint32(fp
, &event
->u
.stats
.calltree_maxstack_top
))
319 #ifdef DEBUG_tmreader
320 fprintf(stderr
, "tmevent %c %u\n", event
->type
, event
->serial
);
324 fprintf(stderr
, "Unknown event type %c\n", event
->type
);
330 static void *arena_alloc(void* pool
, PRSize size
)
332 PLArenaPool
* arena
= (PLArenaPool
*)pool
;
334 PL_ARENA_ALLOCATE(result
, arena
, size
);
335 memset(result
, 0, size
);
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
;
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;
372 static PLHashEntry
*graphnode_allocentry(void *pool
, const void *key
)
374 tmgraphnode
* node
= (tmgraphnode
*)arena_alloc(pool
, sizeof(tmgraphnode
));
377 init_graphnode(node
);
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
)
399 (tmmethodnode
*) arena_alloc(pool
, sizeof(tmmethodnode
));
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. */
410 #if 0 /* using arenas now, no freeing! */
411 /* Free the whole thing if we're told to. */
412 if (flag
== HT_FREE_ENTRY
)
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! */
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
)
464 tmr
= calloc(1, sizeof *tmr
);
467 tmr
->program
= program
;
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
,
474 tmr
->filenames
= PL_NewHashTable(100, hash_serial
, PL_CompareValues
,
475 PL_CompareStrings
, &filename_hashallocops
,
477 tmr
->components
= PL_NewHashTable(10000, PL_HashString
, PL_CompareStrings
,
478 PL_CompareValues
, &component_hashallocops
,
480 tmr
->methods
= PL_NewHashTable(10000, hash_serial
, PL_CompareValues
,
481 PL_CompareStrings
, &method_hashallocops
,
483 tmr
->callsites
= PL_NewHashTable(200000, hash_serial
, PL_CompareValues
,
484 PL_CompareValues
, &callsite_hashallocops
,
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
||
491 tmreader_destroy(tmr
);
497 void tmreader_destroy(tmreader
*tmr
)
500 PL_HashTableDestroy(tmr
->libraries
);
502 PL_HashTableDestroy(tmr
->filenames
);
504 PL_HashTableDestroy(tmr
->components
);
506 PL_HashTableDestroy(tmr
->methods
);
508 PL_HashTableDestroy(tmr
->callsites
);
509 PL_FinishArenaPool(&tmr
->arena
);
513 int tmreader_eventloop(tmreader
*tmr
, const char *filename
,
514 tmeventhandler eventhandler
)
517 char buf
[NS_TRACE_MALLOC_MAGIC_SIZE
];
519 static const char magic
[] = NS_TRACE_MALLOC_MAGIC
;
521 if (strcmp(filename
, "-") == 0) {
524 #if defined(XP_WIN32)
525 fp
= fopen(filename
, "rb");
527 fp
= fopen(filename
, "r");
530 fprintf(stderr
, "%s: can't open %s: %s.\n",
531 tmr
->program
, filename
, strerror(errno
));
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");
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",
550 tmr
->ticksPerSec
= PR_ntohl(tmr
->ticksPerSec
);
552 printf("DEBUG: ticks per sec = %d\n", tmr
->ticksPerSec
);
554 while (get_tmevent(fp
, &event
)) {
555 switch (event
.type
) {
556 case TM_EVENT_LIBRARY
: {
559 PLHashEntry
**hep
, *he
;
561 key
= (const void*) event
.serial
;
562 hash
= hash_serial(key
);
563 hep
= PL_HashTableRawLookup(tmr
->libraries
, hash
, key
);
568 he
= PL_HashTableRawAdd(tmr
->libraries
, hep
, hash
, key
,
571 perror(tmr
->program
);
577 case TM_EVENT_FILENAME
: {
580 PLHashEntry
**hep
, *he
;
582 key
= (const void*) event
.serial
;
583 hash
= hash_serial(key
);
584 hep
= PL_HashTableRawLookup(tmr
->filenames
, hash
, key
);
589 he
= PL_HashTableRawAdd(tmr
->filenames
, hep
, hash
, key
,
592 perror(tmr
->program
);
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
;
606 key
= (const void*) event
.serial
;
607 hash
= hash_serial(key
);
608 hep
= PL_HashTableRawLookup(tmr
->methods
, hash
, key
);
613 name
= event
.u
.method
.name
;
614 he
= PL_HashTableRawAdd(tmr
->methods
, hep
, hash
, key
, name
);
616 perror(tmr
->program
);
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
);
629 mark
= strchr(name
, ':');
632 while (*mark
!= '\0' && *mark
== '_')
635 mark
= strchr(head
, '_');
637 mark
= strchr(head
, '+');
639 mark
= head
+ strlen(head
);
645 hash
= PL_HashString(head
);
646 hep
= PL_HashTableRawLookup(tmr
->components
, hash
, head
);
649 comp
= (tmgraphnode
*) he
;
653 he
= PL_HashTableRawAdd(tmr
->components
, hep
, hash
, head
,
657 perror(tmr
->program
);
660 comp
= (tmgraphnode
*) he
;
662 key
= (const void*) event
.u
.method
.library
;
663 hash
= hash_serial(key
);
665 *PL_HashTableRawLookup(tmr
->libraries
, hash
, key
);
668 comp
->next
= lib
->down
;
674 meth
->graphnode
.up
= comp
;
675 meth
->graphnode
.next
= comp
->down
;
676 comp
->down
= &(meth
->graphnode
);
680 case TM_EVENT_CALLSITE
: {
681 const void *key
, *mkey
;
682 PLHashNumber hash
, mhash
;
683 PLHashEntry
**hep
, *he
;
684 tmcallsite
*site
, *parent
;
687 key
= (const void*) event
.serial
;
688 hash
= hash_serial(key
);
689 hep
= PL_HashTableRawLookup(tmr
->callsites
, hash
, key
);
692 /* there should not be an entry here! */
696 if (event
.u
.site
.parent
== 0) {
697 parent
= &tmr
->calltree_root
;
699 parent
= tmreader_callsite(tmr
, event
.u
.site
.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
);
708 he
= PL_HashTableRawAdd(tmr
->callsites
, hep
, hash
, key
, NULL
);
710 perror(tmr
->program
);
714 site
= (tmcallsite
*) he
;
715 site
->parent
= parent
;
716 site
->siblings
= parent
->kids
;
720 mkey
= (const void*) event
.u
.site
.method
;
721 mhash
= hash_serial(mkey
);
722 meth
= (tmmethodnode
*)
723 *PL_HashTableRawLookup(tmr
->methods
, mhash
, mkey
);
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;
733 case TM_EVENT_MALLOC
:
734 case TM_EVENT_CALLOC
:
735 case TM_EVENT_REALLOC
: {
737 uint32 size
, oldsize
;
738 double delta
, sqdelta
, sqszdelta
= 0;
739 tmgraphnode
*comp
, *lib
;
742 site
= tmreader_callsite(tmr
, event
.serial
);
744 fprintf(stderr
, "%s: no callsite for '%c' (%lu)!\n",
745 tmr
->program
, event
.type
, (unsigned long) event
.serial
);
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
++;
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
;
764 meth
->graphnode
.sqsum
+= sqdelta
;
765 meth
->graphnode
.allocs
.calls
.direct
++;
767 comp
= meth
->graphnode
.up
;
769 comp
->allocs
.bytes
.direct
+= (unsigned long)delta
;
770 if (event
.type
== TM_EVENT_REALLOC
) {
771 comp
->sqsum
+= sqszdelta
;
773 comp
->sqsum
+= sqdelta
;
774 comp
->allocs
.calls
.direct
++;
778 lib
->allocs
.bytes
.direct
+= (unsigned long)delta
;
779 if (event
.type
== TM_EVENT_REALLOC
) {
780 lib
->sqsum
+= sqszdelta
;
782 lib
->sqsum
+= sqdelta
;
783 lib
->allocs
.calls
.direct
++;
791 case TM_EVENT_FREE
: {
794 tmgraphnode
*comp
, *lib
;
797 site
= tmreader_callsite(tmr
, event
.serial
);
799 fprintf(stderr
, "%s: no callsite for '%c' (%lu)!\n",
800 tmr
->program
, event
.type
, (unsigned long) event
.serial
);
803 size
= event
.u
.alloc
.size
;
804 site
->frees
.bytes
.direct
+= size
;
805 site
->frees
.calls
.direct
++;
808 meth
->graphnode
.frees
.bytes
.direct
+= size
;
809 meth
->graphnode
.frees
.calls
.direct
++;
810 comp
= meth
->graphnode
.up
;
812 comp
->frees
.bytes
.direct
+= size
;
813 comp
->frees
.calls
.direct
++;
816 lib
->frees
.bytes
.direct
+= size
;
817 lib
->frees
.calls
.direct
++;
828 eventhandler(tmr
, &event
);
834 tmgraphnode
*tmreader_library(tmreader
*tmr
, uint32 serial
)
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
)
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
)
858 hash
= PL_HashString(name
);
859 return (tmgraphnode
*) *PL_HashTableRawLookup(tmr
->components
, hash
, name
);
862 tmmethodnode
*tmreader_method(tmreader
*tmr
, uint32 serial
)
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
)
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
;
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
;
913 edge
= (tmgraphedge
*) malloc(sizeof(tmgraphedge
));
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
;