tools/trace-malloc/spacecategory.c

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

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

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

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
michael@0 7 /*
michael@0 8 ** spacecategory.c
michael@0 9 **
michael@0 10 ** Cagtegorizes each allocation using a predefined set of rules
michael@0 11 ** and presents a tree of categories for browsing.
michael@0 12 */
michael@0 13
michael@0 14 /*
michael@0 15 ** Required include files.
michael@0 16 */
michael@0 17 #include "spacetrace.h"
michael@0 18 #include <ctype.h>
michael@0 19 #include <string.h>
michael@0 20
michael@0 21 #include "nsQuickSort.h"
michael@0 22
michael@0 23 #if defined(HAVE_BOUTELL_GD)
michael@0 24 /*
michael@0 25 ** See http://www.boutell.com/gd for the GD graphics library.
michael@0 26 ** Ports for many platorms exist.
michael@0 27 ** Your box may already have the lib (mine did, redhat 7.1 workstation).
michael@0 28 */
michael@0 29 #include <gd.h>
michael@0 30 #include <gdfontt.h>
michael@0 31 #include <gdfonts.h>
michael@0 32 #include <gdfontmb.h>
michael@0 33 #endif /* HAVE_BOUTELL_GD */
michael@0 34
michael@0 35 /*
michael@0 36 ** AddRule
michael@0 37 **
michael@0 38 ** Add a rule into the list of rules maintainted in global
michael@0 39 */
michael@0 40 int
michael@0 41 AddRule(STGlobals * g, STCategoryRule * rule)
michael@0 42 {
michael@0 43 if (g->mNRules % ST_ALLOC_STEP == 0) {
michael@0 44 /* Need more space */
michael@0 45 STCategoryRule **newrules;
michael@0 46
michael@0 47 newrules = (STCategoryRule **) realloc(g->mCategoryRules,
michael@0 48 (g->mNRules +
michael@0 49 ST_ALLOC_STEP) *
michael@0 50 sizeof(STCategoryRule *));
michael@0 51 if (!newrules) {
michael@0 52 REPORT_ERROR(__LINE__, AddRule_No_Memory);
michael@0 53 return -1;
michael@0 54 }
michael@0 55 g->mCategoryRules = newrules;
michael@0 56 }
michael@0 57 g->mCategoryRules[g->mNRules++] = rule;
michael@0 58 return 0;
michael@0 59 }
michael@0 60
michael@0 61 /*
michael@0 62 ** AddChild
michael@0 63 **
michael@0 64 ** Add the node as a child of the parent node
michael@0 65 */
michael@0 66 int
michael@0 67 AddChild(STCategoryNode * parent, STCategoryNode * child)
michael@0 68 {
michael@0 69 if (parent->nchildren % ST_ALLOC_STEP == 0) {
michael@0 70 /* need more space */
michael@0 71 STCategoryNode **newnodes;
michael@0 72
michael@0 73 newnodes = (STCategoryNode **) realloc(parent->children,
michael@0 74 (parent->nchildren +
michael@0 75 ST_ALLOC_STEP) *
michael@0 76 sizeof(STCategoryNode *));
michael@0 77 if (!newnodes) {
michael@0 78 REPORT_ERROR(__LINE__, AddChild_No_Memory);
michael@0 79 return -1;
michael@0 80 }
michael@0 81 parent->children = newnodes;
michael@0 82 }
michael@0 83 parent->children[parent->nchildren++] = child;
michael@0 84 return 0;
michael@0 85 }
michael@0 86
michael@0 87 int
michael@0 88 Reparent(STCategoryNode * parent, STCategoryNode * child)
michael@0 89 {
michael@0 90 uint32_t i;
michael@0 91
michael@0 92 if (child->parent == parent)
michael@0 93 return 0;
michael@0 94
michael@0 95 /* Remove child from old parent */
michael@0 96 if (child->parent) {
michael@0 97 for (i = 0; i < child->parent->nchildren; i++) {
michael@0 98 if (child->parent->children[i] == child) {
michael@0 99 /* Remove child from list */
michael@0 100 if (i + 1 < child->parent->nchildren)
michael@0 101 memmove(&child->parent->children[i],
michael@0 102 &child->parent->children[i + 1],
michael@0 103 (child->parent->nchildren - i -
michael@0 104 1) * sizeof(STCategoryNode *));
michael@0 105 child->parent->nchildren--;
michael@0 106 break;
michael@0 107 }
michael@0 108 }
michael@0 109 }
michael@0 110
michael@0 111 /* Add child into new parent */
michael@0 112 AddChild(parent, child);
michael@0 113
michael@0 114 return 0;
michael@0 115 }
michael@0 116
michael@0 117 /*
michael@0 118 ** findCategoryNode
michael@0 119 **
michael@0 120 ** Given a category name, finds the Node corresponding to the category
michael@0 121 */
michael@0 122 STCategoryNode *
michael@0 123 findCategoryNode(const char *catName, STGlobals * g)
michael@0 124 {
michael@0 125 uint32_t i;
michael@0 126
michael@0 127 for (i = 0; i < g->mNCategoryMap; i++) {
michael@0 128 if (!strcmp(g->mCategoryMap[i]->categoryName, catName))
michael@0 129 return g->mCategoryMap[i]->node;
michael@0 130 }
michael@0 131
michael@0 132 /* Check if we are looking for the root node */
michael@0 133 if (!strcmp(catName, ST_ROOT_CATEGORY_NAME))
michael@0 134 return &g->mCategoryRoot;
michael@0 135
michael@0 136 return NULL;
michael@0 137 }
michael@0 138
michael@0 139 /*
michael@0 140 ** AddCategoryNode
michael@0 141 **
michael@0 142 ** Adds a mapping between a category and its Node into the categoryMap
michael@0 143 */
michael@0 144 int
michael@0 145 AddCategoryNode(STCategoryNode * node, STGlobals * g)
michael@0 146 {
michael@0 147 if (g->mNCategoryMap % ST_ALLOC_STEP == 0) {
michael@0 148 /* Need more space */
michael@0 149 STCategoryMapEntry **newmap =
michael@0 150 (STCategoryMapEntry **) realloc(g->mCategoryMap,
michael@0 151 (g->mNCategoryMap +
michael@0 152 ST_ALLOC_STEP) *
michael@0 153 sizeof(STCategoryMapEntry *));
michael@0 154 if (!newmap) {
michael@0 155 REPORT_ERROR(__LINE__, AddCategoryNode_No_Memory);
michael@0 156 return -1;
michael@0 157 }
michael@0 158 g->mCategoryMap = newmap;
michael@0 159
michael@0 160 }
michael@0 161 g->mCategoryMap[g->mNCategoryMap] =
michael@0 162 (STCategoryMapEntry *) calloc(1, sizeof(STCategoryMapEntry));
michael@0 163 if (!g->mCategoryMap[g->mNCategoryMap]) {
michael@0 164 REPORT_ERROR(__LINE__, AddCategoryNode_No_Memory);
michael@0 165 return -1;
michael@0 166 }
michael@0 167 g->mCategoryMap[g->mNCategoryMap]->categoryName = node->categoryName;
michael@0 168 g->mCategoryMap[g->mNCategoryMap]->node = node;
michael@0 169 g->mNCategoryMap++;
michael@0 170 return 0;
michael@0 171 }
michael@0 172
michael@0 173 /*
michael@0 174 ** NewCategoryNode
michael@0 175 **
michael@0 176 ** Creates a new category node for category name 'catname' and makes
michael@0 177 ** 'parent' its parent.
michael@0 178 */
michael@0 179 STCategoryNode *
michael@0 180 NewCategoryNode(const char *catName, STCategoryNode * parent, STGlobals * g)
michael@0 181 {
michael@0 182 STCategoryNode *node;
michael@0 183
michael@0 184 node = (STCategoryNode *) calloc(1, sizeof(STCategoryNode));
michael@0 185 if (!node)
michael@0 186 return NULL;
michael@0 187
michael@0 188 node->runs =
michael@0 189 (STRun **) calloc(g->mCommandLineOptions.mContexts, sizeof(STRun *));
michael@0 190 if (NULL == node->runs) {
michael@0 191 free(node);
michael@0 192 return NULL;
michael@0 193 }
michael@0 194
michael@0 195 node->categoryName = catName;
michael@0 196
michael@0 197 /* Set parent of child */
michael@0 198 node->parent = parent;
michael@0 199
michael@0 200 /* Set child in parent */
michael@0 201 AddChild(parent, node);
michael@0 202
michael@0 203 /* Add node into mapping table */
michael@0 204 AddCategoryNode(node, g);
michael@0 205
michael@0 206 return node;
michael@0 207 }
michael@0 208
michael@0 209 /*
michael@0 210 ** ProcessCategoryLeafRule
michael@0 211 **
michael@0 212 ** Add this into the tree as a leaf node. It doesn't know who its parent is. For now we make
michael@0 213 ** root as its parent
michael@0 214 */
michael@0 215 int
michael@0 216 ProcessCategoryLeafRule(STCategoryRule * leafRule, STCategoryNode * root,
michael@0 217 STGlobals * g)
michael@0 218 {
michael@0 219 STCategoryRule *rule;
michael@0 220 STCategoryNode *node;
michael@0 221
michael@0 222 rule = (STCategoryRule *) calloc(1, sizeof(STCategoryRule));
michael@0 223 if (!rule)
michael@0 224 return -1;
michael@0 225
michael@0 226 /* Take ownership of all elements of rule */
michael@0 227 *rule = *leafRule;
michael@0 228
michael@0 229 /* Find/Make a STCategoryNode and add it into the tree */
michael@0 230 node = findCategoryNode(rule->categoryName, g);
michael@0 231 if (!node)
michael@0 232 node = NewCategoryNode(rule->categoryName, root, g);
michael@0 233
michael@0 234 /* Make sure rule knows which node to access */
michael@0 235 rule->node = node;
michael@0 236
michael@0 237 /* Add rule into rulelist */
michael@0 238 AddRule(g, rule);
michael@0 239
michael@0 240 return 0;
michael@0 241 }
michael@0 242
michael@0 243 /*
michael@0 244 ** ProcessCategoryParentRule
michael@0 245 **
michael@0 246 ** Rule has all the children of category as patterns. Sets up the tree so that
michael@0 247 ** the parent child relationship is honored.
michael@0 248 */
michael@0 249 int
michael@0 250 ProcessCategoryParentRule(STCategoryRule * parentRule, STCategoryNode * root,
michael@0 251 STGlobals * g)
michael@0 252 {
michael@0 253 STCategoryNode *node;
michael@0 254 STCategoryNode *child;
michael@0 255 uint32_t i;
michael@0 256
michael@0 257 /* Find the parent node in the tree. If not make one and add it into the tree */
michael@0 258 node = findCategoryNode(parentRule->categoryName, g);
michael@0 259 if (!node) {
michael@0 260 node = NewCategoryNode(parentRule->categoryName, root, g);
michael@0 261 if (!node)
michael@0 262 return -1;
michael@0 263 }
michael@0 264
michael@0 265 /* For every child node, Find/Create it and make it the child of this node */
michael@0 266 for (i = 0; i < parentRule->npats; i++) {
michael@0 267 child = findCategoryNode(parentRule->pats[i], g);
michael@0 268 if (!child) {
michael@0 269 child = NewCategoryNode(parentRule->pats[i], node, g);
michael@0 270 if (!child)
michael@0 271 return -1;
michael@0 272 }
michael@0 273 else {
michael@0 274 /* Reparent child to node. This is because when we created the node
michael@0 275 ** we would have created it as the child of root. Now we need to
michael@0 276 ** remove it from root's child list and add it into this node
michael@0 277 */
michael@0 278 Reparent(node, child);
michael@0 279 }
michael@0 280 }
michael@0 281
michael@0 282 return 0;
michael@0 283 }
michael@0 284
michael@0 285 /*
michael@0 286 ** initCategories
michael@0 287 **
michael@0 288 ** Initialize all categories. This reads in a file that says how to categorize
michael@0 289 ** each callsite, creates a tree of these categories and makes a list of these
michael@0 290 ** patterns in order for matching
michael@0 291 */
michael@0 292 int
michael@0 293 initCategories(STGlobals * g)
michael@0 294 {
michael@0 295 FILE *fp;
michael@0 296 char buf[1024], *in;
michael@0 297 int n;
michael@0 298 PRBool inrule, leaf;
michael@0 299 STCategoryRule rule;
michael@0 300
michael@0 301 fp = fopen(g->mCommandLineOptions.mCategoryFile, "r");
michael@0 302 if (!fp) {
michael@0 303 /* It isn't an error to not have a categories file */
michael@0 304 REPORT_INFO("No categories file.");
michael@0 305 return -1;
michael@0 306 }
michael@0 307
michael@0 308 inrule = PR_FALSE;
michael@0 309 leaf = PR_FALSE;
michael@0 310
michael@0 311 memset(&rule, 0, sizeof(rule));
michael@0 312
michael@0 313 while (fgets(buf, sizeof(buf), fp) != NULL) {
michael@0 314 /* Lose the \n */
michael@0 315 n = strlen(buf);
michael@0 316 if (buf[n - 1] == '\n')
michael@0 317 buf[--n] = '\0';
michael@0 318 in = buf;
michael@0 319
michael@0 320 /* skip comments */
michael@0 321 if (*in == '#')
michael@0 322 continue;
michael@0 323
michael@0 324 /* skip empty lines. If we are in a rule, end the rule. */
michael@0 325 while (*in && isspace(*in))
michael@0 326 in++;
michael@0 327 if (*in == '\0') {
michael@0 328 if (inrule) {
michael@0 329 /* End the rule : leaf or non-leaf */
michael@0 330 if (leaf)
michael@0 331 ProcessCategoryLeafRule(&rule, &g->mCategoryRoot, g);
michael@0 332 else
michael@0 333 /* non-leaf */
michael@0 334 ProcessCategoryParentRule(&rule, &g->mCategoryRoot, g);
michael@0 335 inrule = PR_FALSE;
michael@0 336 memset(&rule, 0, sizeof(rule));
michael@0 337 }
michael@0 338 continue;
michael@0 339 }
michael@0 340
michael@0 341 /* if we are in a rule acculumate */
michael@0 342 if (inrule) {
michael@0 343 rule.pats[rule.npats] = strdup(in);
michael@0 344 rule.patlen[rule.npats++] = strlen(in);
michael@0 345 }
michael@0 346 else if (*in == '<') {
michael@0 347 /* Start a category */
michael@0 348 inrule = PR_TRUE;
michael@0 349 leaf = PR_TRUE;
michael@0 350
michael@0 351 /* Get the category name */
michael@0 352 in++;
michael@0 353 n = strlen(in);
michael@0 354 if (in[n - 1] == '>')
michael@0 355 in[n - 1] = '\0';
michael@0 356 rule.categoryName = strdup(in);
michael@0 357 }
michael@0 358 else {
michael@0 359 /* this is a non-leaf category. Should be of the form CategoryName
michael@0 360 ** followed by list of child category names one per line
michael@0 361 */
michael@0 362 inrule = PR_TRUE;
michael@0 363 leaf = PR_FALSE;
michael@0 364 rule.categoryName = strdup(in);
michael@0 365 }
michael@0 366 }
michael@0 367
michael@0 368 /* If we were in a rule when processing the last line, end the rule */
michael@0 369 if (inrule) {
michael@0 370 /* End the rule : leaf or non-leaf */
michael@0 371 if (leaf)
michael@0 372 ProcessCategoryLeafRule(&rule, &g->mCategoryRoot, g);
michael@0 373 else
michael@0 374 /* non-leaf */
michael@0 375 ProcessCategoryParentRule(&rule, &g->mCategoryRoot, g);
michael@0 376 }
michael@0 377
michael@0 378 /* Add the final "uncategorized" category. We make new memory locations
michael@0 379 ** for all these to conform to the general principle of all strings are allocated
michael@0 380 ** so it makes release logic very simple.
michael@0 381 */
michael@0 382 memset(&rule, 0, sizeof(rule));
michael@0 383 rule.categoryName = strdup("uncategorized");
michael@0 384 rule.pats[0] = strdup("");
michael@0 385 rule.patlen[0] = 0;
michael@0 386 rule.npats = 1;
michael@0 387 ProcessCategoryLeafRule(&rule, &g->mCategoryRoot, g);
michael@0 388
michael@0 389 return 0;
michael@0 390 }
michael@0 391
michael@0 392 /*
michael@0 393 ** callsiteMatchesRule
michael@0 394 **
michael@0 395 ** Returns the corresponding node if callsite matches the rule. Rule is a sequence
michael@0 396 ** of patterns that must match contiguously the callsite.
michael@0 397 */
michael@0 398 int
michael@0 399 callsiteMatchesRule(tmcallsite * aCallsite, STCategoryRule * aRule)
michael@0 400 {
michael@0 401 uint32_t patnum = 0;
michael@0 402 const char *methodName = NULL;
michael@0 403
michael@0 404 while (patnum < aRule->npats && aCallsite && aCallsite->method) {
michael@0 405 methodName = tmmethodnode_name(aCallsite->method);
michael@0 406 if (!methodName)
michael@0 407 return 0;
michael@0 408 if (!*aRule->pats[patnum]
michael@0 409 || !strncmp(methodName, aRule->pats[patnum],
michael@0 410 aRule->patlen[patnum])) {
michael@0 411 /* We have matched so far. Proceed up the stack and to the next pattern */
michael@0 412 patnum++;
michael@0 413 aCallsite = aCallsite->parent;
michael@0 414 }
michael@0 415 else {
michael@0 416 /* Deal with mismatch */
michael@0 417 if (patnum > 0) {
michael@0 418 /* contiguous mismatch. Stop */
michael@0 419 return 0;
michael@0 420 }
michael@0 421 /* We still haven't matched the first pattern. Proceed up the stack without
michael@0 422 ** moving to the next pattern.
michael@0 423 */
michael@0 424 aCallsite = aCallsite->parent;
michael@0 425 }
michael@0 426 }
michael@0 427
michael@0 428 if (patnum == aRule->npats) {
michael@0 429 /* all patterns matched. We have a winner. */
michael@0 430 #if defined(DEBUG_dp) && 0
michael@0 431 fprintf(stderr, "[%s] match\n", aRule->categoryName);
michael@0 432 #endif
michael@0 433 return 1;
michael@0 434 }
michael@0 435
michael@0 436 return 0;
michael@0 437 }
michael@0 438
michael@0 439 #ifdef DEBUG_dp
michael@0 440 PRIntervalTime _gMatchTime = 0;
michael@0 441 uint32_t _gMatchCount = 0;
michael@0 442 uint32_t _gMatchRules = 0;
michael@0 443 #endif
michael@0 444
michael@0 445 /*
michael@0 446 ** matchAllocation
michael@0 447 **
michael@0 448 ** Runs through all rules and returns the node corresponding to
michael@0 449 ** a match of the allocation.
michael@0 450 */
michael@0 451 STCategoryNode *
michael@0 452 matchAllocation(STGlobals * g, STAllocation * aAllocation)
michael@0 453 {
michael@0 454 #ifdef DEBUG_dp
michael@0 455 PRIntervalTime start = PR_IntervalNow();
michael@0 456 #endif
michael@0 457 uint32_t rulenum;
michael@0 458 STCategoryNode *node = NULL;
michael@0 459 STCategoryRule *rule;
michael@0 460
michael@0 461 for (rulenum = 0; rulenum < g->mNRules; rulenum++) {
michael@0 462 #ifdef DEBUG_dp
michael@0 463 _gMatchRules++;
michael@0 464 #endif
michael@0 465 rule = g->mCategoryRules[rulenum];
michael@0 466 if (callsiteMatchesRule(aAllocation->mEvents[0].mCallsite, rule)) {
michael@0 467 node = rule->node;
michael@0 468 break;
michael@0 469 }
michael@0 470 }
michael@0 471 #ifdef DEBUG_dp
michael@0 472 _gMatchCount++;
michael@0 473 _gMatchTime += PR_IntervalNow() - start;
michael@0 474 #endif
michael@0 475 return node;
michael@0 476 }
michael@0 477
michael@0 478 /*
michael@0 479 ** categorizeAllocation
michael@0 480 **
michael@0 481 ** Given an allocation, it adds it into the category tree at the right spot
michael@0 482 ** by comparing the allocation to the rules and adding into the right node.
michael@0 483 ** Also, does propogation of cost upwards in the tree.
michael@0 484 ** The root of the tree is in the globls as the tree is dependent on the
michael@0 485 ** category file (options) rather than the run.
michael@0 486 */
michael@0 487 int
michael@0 488 categorizeAllocation(STOptions * inOptions, STContext * inContext,
michael@0 489 STAllocation * aAllocation, STGlobals * g)
michael@0 490 {
michael@0 491 /* Run through the rules in order to see if this allcation matches
michael@0 492 ** any of them.
michael@0 493 */
michael@0 494 STCategoryNode *node;
michael@0 495
michael@0 496 node = matchAllocation(g, aAllocation);
michael@0 497 if (!node) {
michael@0 498 /* ugh! it should atleast go into the "uncategorized" node. wierd!
michael@0 499 */
michael@0 500 REPORT_ERROR(__LINE__, categorizeAllocation);
michael@0 501 return -1;
michael@0 502 }
michael@0 503
michael@0 504 /* Create run for node if not already */
michael@0 505 if (!node->runs[inContext->mIndex]) {
michael@0 506 /*
michael@0 507 ** Create run with positive timestamp as we can harvest it later
michael@0 508 ** for callsite details summarization
michael@0 509 */
michael@0 510 node->runs[inContext->mIndex] =
michael@0 511 createRun(inContext, PR_IntervalNow());
michael@0 512 if (!node->runs[inContext->mIndex]) {
michael@0 513 REPORT_ERROR(__LINE__, categorizeAllocation_No_Memory);
michael@0 514 return -1;
michael@0 515 }
michael@0 516 }
michael@0 517
michael@0 518 /* Add allocation into node. We expand the table of allocations in steps */
michael@0 519 if (node->runs[inContext->mIndex]->mAllocationCount % ST_ALLOC_STEP == 0) {
michael@0 520 /* Need more space */
michael@0 521 STAllocation **allocs;
michael@0 522
michael@0 523 allocs =
michael@0 524 (STAllocation **) realloc(node->runs[inContext->mIndex]->
michael@0 525 mAllocations,
michael@0 526 (node->runs[inContext->mIndex]->
michael@0 527 mAllocationCount +
michael@0 528 ST_ALLOC_STEP) *
michael@0 529 sizeof(STAllocation *));
michael@0 530 if (!allocs) {
michael@0 531 REPORT_ERROR(__LINE__, categorizeAllocation_No_Memory);
michael@0 532 return -1;
michael@0 533 }
michael@0 534 node->runs[inContext->mIndex]->mAllocations = allocs;
michael@0 535 }
michael@0 536 node->runs[inContext->mIndex]->mAllocations[node->
michael@0 537 runs[inContext->mIndex]->
michael@0 538 mAllocationCount++] =
michael@0 539 aAllocation;
michael@0 540
michael@0 541 /*
michael@0 542 ** Make sure run's stats are calculated. We don't go update the parents of allocation
michael@0 543 ** at this time. That will happen when we focus on this category. This updating of
michael@0 544 ** stats will provide us fast categoryreports.
michael@0 545 */
michael@0 546 recalculateAllocationCost(inOptions, inContext,
michael@0 547 node->runs[inContext->mIndex], aAllocation,
michael@0 548 PR_FALSE);
michael@0 549
michael@0 550 /* Propagate upwards the statistics */
michael@0 551 /* XXX */
michael@0 552 #if defined(DEBUG_dp) && 0
michael@0 553 fprintf(stderr, "DEBUG: [%s] match\n", node->categoryName);
michael@0 554 #endif
michael@0 555 return 0;
michael@0 556 }
michael@0 557
michael@0 558 typedef PRBool STCategoryNodeProcessor(STRequest * inRequest,
michael@0 559 STOptions * inOptions,
michael@0 560 STContext * inContext,
michael@0 561 void *clientData,
michael@0 562 STCategoryNode * node);
michael@0 563
michael@0 564 PRBool
michael@0 565 freeNodeRunProcessor(STRequest * inRequest, STOptions * inOptions,
michael@0 566 STContext * inContext, void *clientData,
michael@0 567 STCategoryNode * node)
michael@0 568 {
michael@0 569 if (node->runs && node->runs[inContext->mIndex]) {
michael@0 570 freeRun(node->runs[inContext->mIndex]);
michael@0 571 node->runs[inContext->mIndex] = NULL;
michael@0 572 }
michael@0 573 return PR_TRUE;
michael@0 574 }
michael@0 575
michael@0 576 PRBool
michael@0 577 freeNodeRunsProcessor(STRequest * inRequest, STOptions * inOptions,
michael@0 578 STContext * inContext, void *clientData,
michael@0 579 STCategoryNode * node)
michael@0 580 {
michael@0 581 if (node->runs) {
michael@0 582 uint32_t loop = 0;
michael@0 583
michael@0 584 for (loop = 0; loop < globals.mCommandLineOptions.mContexts; loop++) {
michael@0 585 if (node->runs[loop]) {
michael@0 586 freeRun(node->runs[loop]);
michael@0 587 node->runs[loop] = NULL;
michael@0 588 }
michael@0 589 }
michael@0 590
michael@0 591 free(node->runs);
michael@0 592 node->runs = NULL;
michael@0 593 }
michael@0 594
michael@0 595 return PR_TRUE;
michael@0 596 }
michael@0 597
michael@0 598 #if defined(DEBUG_dp)
michael@0 599 PRBool
michael@0 600 printNodeProcessor(STRequest * inRequest, STOptions * inOptions,
michael@0 601 STContext * inContext, void *clientData,
michael@0 602 STCategoryNode * node)
michael@0 603 {
michael@0 604 STCategoryNode *root = (STCategoryNode *) clientData;
michael@0 605
michael@0 606 fprintf(stderr, "%-25s [ %9s size", node->categoryName,
michael@0 607 FormatNumber(node->run ? node->run->mStats[inContext->mIndex].
michael@0 608 mSize : 0));
michael@0 609 fprintf(stderr, ", %5.1f%%",
michael@0 610 node->run ? ((double) node->run->mStats[inContext->mIndex].mSize /
michael@0 611 root->run->mStats[inContext->mIndex].mSize *
michael@0 612 100) : 0);
michael@0 613 fprintf(stderr, ", %7s allocations ]\n",
michael@0 614 FormatNumber(node->run ? node->run->mStats[inContext->mIndex].
michael@0 615 mCompositeCount : 0));
michael@0 616 return PR_TRUE;
michael@0 617 }
michael@0 618
michael@0 619 #endif
michael@0 620
michael@0 621 typedef struct __struct_optcon
michael@0 622 {
michael@0 623 STOptions *mOptions;
michael@0 624 STContext *mContext;
michael@0 625 }
michael@0 626 optcon;
michael@0 627
michael@0 628 /*
michael@0 629 ** compareNode
michael@0 630 **
michael@0 631 ** qsort callback.
michael@0 632 ** Compare the nodes as specified by the options.
michael@0 633 */
michael@0 634 int
michael@0 635 compareNode(const void *aNode1, const void *aNode2, void *aContext)
michael@0 636 {
michael@0 637 int retval = 0;
michael@0 638 STCategoryNode *node1, *node2;
michael@0 639 uint32_t a, b;
michael@0 640 optcon *oc = (optcon *) aContext;
michael@0 641
michael@0 642 if (!aNode1 || !aNode2 || !oc->mOptions || !oc->mContext)
michael@0 643 return 0;
michael@0 644
michael@0 645 node1 = *((STCategoryNode **) aNode1);
michael@0 646 node2 = *((STCategoryNode **) aNode2);
michael@0 647
michael@0 648 if (node1 && node2) {
michael@0 649 if (oc->mOptions->mOrderBy == ST_COUNT) {
michael@0 650 a = (node1->runs[oc->mContext->mIndex]) ? node1->runs[oc->
michael@0 651 mContext->
michael@0 652 mIndex]->
michael@0 653 mStats[oc->mContext->mIndex].mCompositeCount : 0;
michael@0 654 b = (node2->runs[oc->mContext->mIndex]) ? node2->runs[oc->
michael@0 655 mContext->
michael@0 656 mIndex]->
michael@0 657 mStats[oc->mContext->mIndex].mCompositeCount : 0;
michael@0 658 }
michael@0 659 else {
michael@0 660 /* Default is by size */
michael@0 661 a = (node1->runs[oc->mContext->mIndex]) ? node1->runs[oc->
michael@0 662 mContext->
michael@0 663 mIndex]->
michael@0 664 mStats[oc->mContext->mIndex].mSize : 0;
michael@0 665 b = (node2->runs[oc->mContext->mIndex]) ? node2->runs[oc->
michael@0 666 mContext->
michael@0 667 mIndex]->
michael@0 668 mStats[oc->mContext->mIndex].mSize : 0;
michael@0 669 }
michael@0 670 if (a < b)
michael@0 671 retval = __LINE__;
michael@0 672 else
michael@0 673 retval = -__LINE__;
michael@0 674 }
michael@0 675 return retval;
michael@0 676 }
michael@0 677
michael@0 678 PRBool
michael@0 679 sortNodeProcessor(STRequest * inRequest, STOptions * inOptions,
michael@0 680 STContext * inContext, void *clientData,
michael@0 681 STCategoryNode * node)
michael@0 682 {
michael@0 683 if (node->nchildren) {
michael@0 684 optcon context;
michael@0 685
michael@0 686 context.mOptions = inOptions;
michael@0 687 context.mContext = inContext;
michael@0 688
michael@0 689 NS_QuickSort(node->children, node->nchildren,
michael@0 690 sizeof(STCategoryNode *), compareNode, &context);
michael@0 691 }
michael@0 692
michael@0 693 return PR_TRUE;
michael@0 694 }
michael@0 695
michael@0 696
michael@0 697 /*
michael@0 698 ** walkTree
michael@0 699 **
michael@0 700 ** General purpose tree walker. Issues callback for each node.
michael@0 701 ** If 'maxdepth' > 0, then stops after processing that depth. Root is
michael@0 702 ** depth 0, the nodes below it are depth 1 etc...
michael@0 703 */
michael@0 704 #define MODINC(n, mod) ((n+1) % mod)
michael@0 705
michael@0 706 void
michael@0 707 walkTree(STCategoryNode * root, STCategoryNodeProcessor func,
michael@0 708 STRequest * inRequest, STOptions * inOptions, STContext * inContext,
michael@0 709 void *clientData, int maxdepth)
michael@0 710 {
michael@0 711 STCategoryNode *nodes[1024], *node;
michael@0 712 uint32_t begin, end, i;
michael@0 713 int ret = 0;
michael@0 714 int curdepth = 0, ncurdepth = 0;
michael@0 715
michael@0 716 nodes[0] = root;
michael@0 717 begin = 0;
michael@0 718 end = 1;
michael@0 719 ncurdepth = 1;
michael@0 720 while (begin != end) {
michael@0 721 node = nodes[begin];
michael@0 722 ret = (*func) (inRequest, inOptions, inContext, clientData, node);
michael@0 723 if (!ret) {
michael@0 724 /* Abort */
michael@0 725 break;
michael@0 726 }
michael@0 727 begin = MODINC(begin, 1024);
michael@0 728 for (i = 0; i < node->nchildren; i++) {
michael@0 729 nodes[end] = node->children[i];
michael@0 730 end = MODINC(end, 1024);
michael@0 731 }
michael@0 732 /* Depth tracking. Do it only if walkTree is contrained by a maxdepth */
michael@0 733 if (maxdepth > 0 && --ncurdepth == 0) {
michael@0 734 /*
michael@0 735 ** No more children in current depth. The rest of the nodes
michael@0 736 ** we have in our list should be nodes in the depth below us.
michael@0 737 */
michael@0 738 ncurdepth = (begin < end) ? (end - begin) : (1024 - begin + end);
michael@0 739 if (++curdepth > maxdepth) {
michael@0 740 /*
michael@0 741 ** Gone too deep. Stop.
michael@0 742 */
michael@0 743 break;
michael@0 744 }
michael@0 745 }
michael@0 746 }
michael@0 747 return;
michael@0 748 }
michael@0 749
michael@0 750 int
michael@0 751 freeRule(STCategoryRule * rule)
michael@0 752 {
michael@0 753 uint32_t i;
michael@0 754 char *p = (char *) rule->categoryName;
michael@0 755
michael@0 756 PR_FREEIF(p);
michael@0 757
michael@0 758 for (i = 0; i < rule->npats; i++)
michael@0 759 free(rule->pats[i]);
michael@0 760
michael@0 761 free(rule);
michael@0 762 return 0;
michael@0 763 }
michael@0 764
michael@0 765 void
michael@0 766 freeNodeRuns(STCategoryNode * root)
michael@0 767 {
michael@0 768 walkTree(root, freeNodeRunsProcessor, NULL, NULL, NULL, NULL, 0);
michael@0 769 }
michael@0 770
michael@0 771 void
michael@0 772 freeNodeMap(STGlobals * g)
michael@0 773 {
michael@0 774 uint32_t i;
michael@0 775
michael@0 776 /* all nodes are in the map table. Just delete all of those. */
michael@0 777 for (i = 0; i < g->mNCategoryMap; i++) {
michael@0 778 free(g->mCategoryMap[i]->node);
michael@0 779 free(g->mCategoryMap[i]);
michael@0 780 }
michael@0 781 free(g->mCategoryMap);
michael@0 782 }
michael@0 783
michael@0 784 int
michael@0 785 freeCategories(STGlobals * g)
michael@0 786 {
michael@0 787 uint32_t i;
michael@0 788
michael@0 789 /*
michael@0 790 ** walk the tree and free runs held in nodes
michael@0 791 */
michael@0 792 freeNodeRuns(&g->mCategoryRoot);
michael@0 793
michael@0 794 /*
michael@0 795 ** delete nodemap. This is the where nodes get deleted.
michael@0 796 */
michael@0 797 freeNodeMap(g);
michael@0 798
michael@0 799 /*
michael@0 800 ** delete rule stuff
michael@0 801 */
michael@0 802 for (i = 0; i < g->mNRules; i++) {
michael@0 803 freeRule(g->mCategoryRules[i]);
michael@0 804 }
michael@0 805 free(g->mCategoryRules);
michael@0 806
michael@0 807 return 0;
michael@0 808 }
michael@0 809
michael@0 810
michael@0 811 /*
michael@0 812 ** categorizeRun
michael@0 813 **
michael@0 814 ** categorize all the allocations of the run using the rules into
michael@0 815 ** a tree rooted at globls.mCategoryRoot
michael@0 816 */
michael@0 817 int
michael@0 818 categorizeRun(STOptions * inOptions, STContext * inContext,
michael@0 819 const STRun * aRun, STGlobals * g)
michael@0 820 {
michael@0 821 uint32_t i;
michael@0 822
michael@0 823 #if defined(DEBUG_dp)
michael@0 824 PRIntervalTime start = PR_IntervalNow();
michael@0 825
michael@0 826 fprintf(stderr, "DEBUG: categorizing run...\n");
michael@0 827 #endif
michael@0 828
michael@0 829 /*
michael@0 830 ** First, cleanup our tree
michael@0 831 */
michael@0 832 walkTree(&g->mCategoryRoot, freeNodeRunProcessor, NULL, inOptions,
michael@0 833 inContext, NULL, 0);
michael@0 834
michael@0 835 if (g->mNCategoryMap > 0) {
michael@0 836 for (i = 0; i < aRun->mAllocationCount; i++) {
michael@0 837 categorizeAllocation(inOptions, inContext, aRun->mAllocations[i],
michael@0 838 g);
michael@0 839 }
michael@0 840 }
michael@0 841
michael@0 842 /*
michael@0 843 ** the run is always going to be the one corresponding to the root node
michael@0 844 */
michael@0 845 g->mCategoryRoot.runs[inContext->mIndex] = (STRun *) aRun;
michael@0 846 g->mCategoryRoot.categoryName = ST_ROOT_CATEGORY_NAME;
michael@0 847
michael@0 848 #if defined(DEBUG_dp)
michael@0 849 fprintf(stderr,
michael@0 850 "DEBUG: categorizing ends: %dms [%d rules, %d allocations]\n",
michael@0 851 PR_IntervalToMilliseconds(PR_IntervalNow() - start), g->mNRules,
michael@0 852 aRun->mAllocationCount);
michael@0 853 fprintf(stderr, "DEBUG: match : %dms [%d calls, %d rule-compares]\n",
michael@0 854 PR_IntervalToMilliseconds(_gMatchTime), _gMatchCount,
michael@0 855 _gMatchRules);
michael@0 856 #endif
michael@0 857
michael@0 858 /*
michael@0 859 ** sort the tree based on our sort criterion
michael@0 860 */
michael@0 861 walkTree(&g->mCategoryRoot, sortNodeProcessor, NULL, inOptions, inContext,
michael@0 862 NULL, 0);
michael@0 863
michael@0 864 #if defined(DEBUG_dp)
michael@0 865 walkTree(&g->mCategoryRoot, printNodeProcessor, NULL, inOptions,
michael@0 866 inContext, &g->mCategoryRoot, 0);
michael@0 867 #endif
michael@0 868
michael@0 869 return 0;
michael@0 870 }
michael@0 871
michael@0 872
michael@0 873 /*
michael@0 874 ** displayCategoryReport
michael@0 875 **
michael@0 876 ** Generate the category report - a list of all categories and details about each
michael@0 877 ** depth parameter controls how deep we traverse the category tree.
michael@0 878 */
michael@0 879 PRBool
michael@0 880 displayCategoryNodeProcessor(STRequest * inRequest, STOptions * inOptions,
michael@0 881 STContext * inContext, void *clientData,
michael@0 882 STCategoryNode * node)
michael@0 883 {
michael@0 884 STCategoryNode *root = (STCategoryNode *) clientData;
michael@0 885 uint32_t byteSize = 0, heapCost = 0, count = 0;
michael@0 886 double percent = 0;
michael@0 887 STOptions customOps;
michael@0 888
michael@0 889 if (node->runs[inContext->mIndex]) {
michael@0 890 /*
michael@0 891 ** Byte size
michael@0 892 */
michael@0 893 byteSize =
michael@0 894 node->runs[inContext->mIndex]->mStats[inContext->mIndex].mSize;
michael@0 895
michael@0 896 /*
michael@0 897 ** Composite count
michael@0 898 */
michael@0 899 count =
michael@0 900 node->runs[inContext->mIndex]->mStats[inContext->mIndex].
michael@0 901 mCompositeCount;
michael@0 902
michael@0 903 /*
michael@0 904 ** Heap operation cost
michael@0 905 **/
michael@0 906 heapCost =
michael@0 907 node->runs[inContext->mIndex]->mStats[inContext->mIndex].
michael@0 908 mHeapRuntimeCost;
michael@0 909
michael@0 910 /*
michael@0 911 ** % of total size
michael@0 912 */
michael@0 913 if (root->runs[inContext->mIndex]) {
michael@0 914 percent =
michael@0 915 ((double) byteSize) /
michael@0 916 root->runs[inContext->mIndex]->mStats[inContext->mIndex].
michael@0 917 mSize * 100;
michael@0 918 }
michael@0 919 }
michael@0 920
michael@0 921 PR_fprintf(inRequest->mFD, " <tr>\n" " <td>");
michael@0 922
michael@0 923 /* a link to topcallsites report with focus on category */
michael@0 924 memcpy(&customOps, inOptions, sizeof(customOps));
michael@0 925 PR_snprintf(customOps.mCategoryName, sizeof(customOps.mCategoryName),
michael@0 926 "%s", node->categoryName);
michael@0 927
michael@0 928 htmlAnchor(inRequest, "top_callsites.html", node->categoryName, NULL,
michael@0 929 "category-callsites", &customOps);
michael@0 930 PR_fprintf(inRequest->mFD,
michael@0 931 "</td>\n" " <td align=right>%u</td>\n"
michael@0 932 " <td align=right>%4.1f%%</td>\n"
michael@0 933 " <td align=right>%u</td>\n" " <td align=right>"
michael@0 934 ST_MICROVAL_FORMAT "</td>\n" " </tr>\n", byteSize, percent,
michael@0 935 count, ST_MICROVAL_PRINTABLE(heapCost));
michael@0 936
michael@0 937 return PR_TRUE;
michael@0 938 }
michael@0 939
michael@0 940
michael@0 941 int
michael@0 942 displayCategoryReport(STRequest * inRequest, STCategoryNode * root, int depth)
michael@0 943 {
michael@0 944 PR_fprintf(inRequest->mFD,
michael@0 945 "<table class=\"category-list data\">\n"
michael@0 946 " <tr class=\"row-header\">\n"
michael@0 947 " <th>Category</th>\n"
michael@0 948 " <th>Composite Byte Size</th>\n"
michael@0 949 " <th>%% of Total Size</th>\n"
michael@0 950 " <th>Heap Object Count</th>\n"
michael@0 951 " <th>Composite Heap Operations Seconds</th>\n" " </tr>\n");
michael@0 952
michael@0 953 walkTree(root, displayCategoryNodeProcessor, inRequest,
michael@0 954 &inRequest->mOptions, inRequest->mContext, root, depth);
michael@0 955
michael@0 956 PR_fprintf(inRequest->mFD, "</table>\n");
michael@0 957
michael@0 958 return 0;
michael@0 959 }

mercurial