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