tools/trace-malloc/tmreader.c

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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"
    22 #include "prnetdb.h"
    23 #include "nsTraceMalloc.h"
    24 #include "tmreader.h"
    26 #undef  DEBUG_tmreader
    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 }
    37 static int get_uint32(FILE *fp, uint32_t *uip)
    38 {
    39     int c;
    40     uint32_t ui;
    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 }
    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;
    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 }
   109 static int get_tmevent(FILE *fp, tmevent *event)
   110 {
   111     int c;
   112     char *s;
   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;
   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;
   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;
   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;
   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;
   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;
   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 }
   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 }
   306 static void *generic_alloctable(void *pool, size_t size)
   307 {
   308     return arena_alloc(pool, size);
   309 }
   311 static void generic_freetable(void *pool, void *item)
   312 {
   313     /* do nothing - arena-allocated */
   314 }
   316 static PLHashEntry *filename_allocentry(void *pool, const void *key)
   317 {
   318     return (PLHashEntry*)arena_alloc(pool, sizeof(PLHashEntry));
   319 }
   321 static PLHashEntry *callsite_allocentry(void *pool, const void *key)
   322 {
   323     return (PLHashEntry*)arena_alloc(pool, sizeof(tmcallsite));
   324 }
   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 }
   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 }
   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 }
   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 }
   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 }
   384 static void component_freeentry(void *pool, PLHashEntry *he, unsigned flag)
   385 {
   386     if (flag == HT_FREE_ENTRY) {
   387         tmgraphnode *comp = (tmgraphnode*) he;
   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 }
   397 static PLHashAllocOps filename_hashallocops = {
   398     generic_alloctable,     generic_freetable,
   399     filename_allocentry,    graphnode_freeentry
   400 };
   402 static PLHashAllocOps callsite_hashallocops = {
   403     generic_alloctable,     generic_freetable,
   404     callsite_allocentry,    graphnode_freeentry
   405 };
   407 static PLHashAllocOps graphnode_hashallocops = {
   408     generic_alloctable,     generic_freetable,
   409     graphnode_allocentry,   graphnode_freeentry
   410 };
   412 static PLHashAllocOps method_hashallocops = {
   413     generic_alloctable,     generic_freetable,
   414     method_allocentry,      graphnode_freeentry
   415 };
   417 static PLHashAllocOps component_hashallocops = {
   418     generic_alloctable,     generic_freetable,
   419     graphnode_allocentry,   component_freeentry
   420 };
   422 static PLHashNumber hash_serial(const void *key)
   423 {
   424     return (PLHashNumber) key;
   425 }
   427 tmreader *tmreader_new(const char *program, void *data)
   428 {
   429     tmreader *tmr;
   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);
   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");
   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 }
   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 }
   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;
   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     }
   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     }
   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;
   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);
   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           }
   544           case TM_EVENT_FILENAME: {
   545             const void *key;
   546             PLHashNumber hash;
   547             PLHashEntry **hep, *he;
   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);
   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           }
   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;
   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);
   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;
   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);
   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             }
   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;
   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;
   641             meth->graphnode.up = comp;
   642             meth->graphnode.next = comp->down;
   643             comp->down = &(meth->graphnode);
   644             break;
   645           }
   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;
   654             key = (const void*) (uintptr_t) event.serial;
   655             hash = hash_serial(key);
   656             hep = PL_HashTableRawLookup(tmr->callsites, hash, key);
   657             he = *hep;
   659             /* there should not be an entry here! */
   660             PR_ASSERT(!he);
   661             if (he) exit(2);
   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             }
   675             he = PL_HashTableRawAdd(tmr->callsites, hep, hash, key, NULL);
   676             if (!he) {
   677                 perror(tmr->program);
   678                 return -1;
   679             }
   681             site = (tmcallsite*) he;
   682             site->parent = parent;
   683             site->siblings = parent->kids;
   684             parent->kids = site;
   685             site->kids = NULL;
   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           }
   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;
   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             }
   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           }
   758           case TM_EVENT_FREE: {
   759             tmcallsite *site;
   760             uint32_t size;
   761             tmgraphnode *comp, *lib;
   762             tmmethodnode *meth;
   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           }
   791           case TM_EVENT_STATS:
   792             break;
   793         }
   795         eventhandler(tmr, &event);
   796     }
   798     return 1;
   799 }
   801 tmgraphnode *tmreader_library(tmreader *tmr, uint32_t serial)
   802 {
   803     const void *key;
   804     PLHashNumber hash;
   806     key = (const void*) (uintptr_t) serial;
   807     hash = hash_serial(key);
   808     return (tmgraphnode*) *PL_HashTableRawLookup(tmr->libraries, hash, key);
   809 }
   811 tmgraphnode *tmreader_filename(tmreader *tmr, uint32_t serial)
   812 {
   813     const void *key;
   814     PLHashNumber hash;
   816     key = (const void*) (uintptr_t) serial;
   817     hash = hash_serial(key);
   818     return (tmgraphnode*) *PL_HashTableRawLookup(tmr->filenames, hash, key);
   819 }
   821 tmgraphnode *tmreader_component(tmreader *tmr, const char *name)
   822 {
   823     PLHashNumber hash;
   825     hash = PL_HashString(name);
   826     return (tmgraphnode*) *PL_HashTableRawLookup(tmr->components, hash, name);
   827 }
   829 tmmethodnode *tmreader_method(tmreader *tmr, uint32_t serial)
   830 {
   831     const void *key;
   832     PLHashNumber hash;
   834     key = (const void*) (uintptr_t) serial;
   835     hash = hash_serial(key);
   836     return (tmmethodnode*) *PL_HashTableRawLookup(tmr->methods, hash, key);
   837 }
   839 tmcallsite *tmreader_callsite(tmreader *tmr, uint32_t serial)
   840 {
   841     const void *key;
   842     PLHashNumber hash;
   844     key = (const void*) (uintptr_t) serial;
   845     hash = hash_serial(key);
   846     return (tmcallsite*) *PL_HashTableRawLookup(tmr->callsites, hash, key);
   847 }
   849 int tmgraphnode_connect(tmgraphnode *from, tmgraphnode *to, tmcallsite *site)
   850 {
   851     tmgraphlink *outlink;
   852     tmgraphedge *edge;
   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;
   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     }
   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