Tue, 06 Jan 2015 21:39:09 +0100
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.
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 | } |