tools/trace-malloc/tmreader.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial