tools/trace-malloc/spacecategory.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/trace-malloc/spacecategory.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,959 @@
     1.4 +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 +** spacecategory.c
    1.12 +**
    1.13 +** Cagtegorizes each allocation using a predefined set of rules
    1.14 +** and presents a tree of categories for browsing.
    1.15 +*/
    1.16 +
    1.17 +/*
    1.18 +** Required include files.
    1.19 +*/
    1.20 +#include "spacetrace.h"
    1.21 +#include <ctype.h>
    1.22 +#include <string.h>
    1.23 +
    1.24 +#include "nsQuickSort.h"
    1.25 +
    1.26 +#if defined(HAVE_BOUTELL_GD)
    1.27 +/*
    1.28 +** See http://www.boutell.com/gd for the GD graphics library.
    1.29 +** Ports for many platorms exist.
    1.30 +** Your box may already have the lib (mine did, redhat 7.1 workstation).
    1.31 +*/
    1.32 +#include <gd.h>
    1.33 +#include <gdfontt.h>
    1.34 +#include <gdfonts.h>
    1.35 +#include <gdfontmb.h>
    1.36 +#endif /* HAVE_BOUTELL_GD */
    1.37 +
    1.38 +/*
    1.39 +** AddRule
    1.40 +**
    1.41 +** Add a rule into the list of rules maintainted in global
    1.42 +*/
    1.43 +int
    1.44 +AddRule(STGlobals * g, STCategoryRule * rule)
    1.45 +{
    1.46 +    if (g->mNRules % ST_ALLOC_STEP == 0) {
    1.47 +        /* Need more space */
    1.48 +        STCategoryRule **newrules;
    1.49 +
    1.50 +        newrules = (STCategoryRule **) realloc(g->mCategoryRules,
    1.51 +                                               (g->mNRules +
    1.52 +                                                ST_ALLOC_STEP) *
    1.53 +                                               sizeof(STCategoryRule *));
    1.54 +        if (!newrules) {
    1.55 +            REPORT_ERROR(__LINE__, AddRule_No_Memory);
    1.56 +            return -1;
    1.57 +        }
    1.58 +        g->mCategoryRules = newrules;
    1.59 +    }
    1.60 +    g->mCategoryRules[g->mNRules++] = rule;
    1.61 +    return 0;
    1.62 +}
    1.63 +
    1.64 +/*
    1.65 +** AddChild
    1.66 +**
    1.67 +** Add the node as a child of the parent node
    1.68 +*/
    1.69 +int
    1.70 +AddChild(STCategoryNode * parent, STCategoryNode * child)
    1.71 +{
    1.72 +    if (parent->nchildren % ST_ALLOC_STEP == 0) {
    1.73 +        /* need more space */
    1.74 +        STCategoryNode **newnodes;
    1.75 +
    1.76 +        newnodes = (STCategoryNode **) realloc(parent->children,
    1.77 +                                               (parent->nchildren +
    1.78 +                                                ST_ALLOC_STEP) *
    1.79 +                                               sizeof(STCategoryNode *));
    1.80 +        if (!newnodes) {
    1.81 +            REPORT_ERROR(__LINE__, AddChild_No_Memory);
    1.82 +            return -1;
    1.83 +        }
    1.84 +        parent->children = newnodes;
    1.85 +    }
    1.86 +    parent->children[parent->nchildren++] = child;
    1.87 +    return 0;
    1.88 +}
    1.89 +
    1.90 +int
    1.91 +Reparent(STCategoryNode * parent, STCategoryNode * child)
    1.92 +{
    1.93 +    uint32_t i;
    1.94 +
    1.95 +    if (child->parent == parent)
    1.96 +        return 0;
    1.97 +
    1.98 +    /* Remove child from old parent */
    1.99 +    if (child->parent) {
   1.100 +        for (i = 0; i < child->parent->nchildren; i++) {
   1.101 +            if (child->parent->children[i] == child) {
   1.102 +                /* Remove child from list */
   1.103 +                if (i + 1 < child->parent->nchildren)
   1.104 +                    memmove(&child->parent->children[i],
   1.105 +                            &child->parent->children[i + 1],
   1.106 +                            (child->parent->nchildren - i -
   1.107 +                             1) * sizeof(STCategoryNode *));
   1.108 +                child->parent->nchildren--;
   1.109 +                break;
   1.110 +            }
   1.111 +        }
   1.112 +    }
   1.113 +
   1.114 +    /* Add child into new parent */
   1.115 +    AddChild(parent, child);
   1.116 +
   1.117 +    return 0;
   1.118 +}
   1.119 +
   1.120 +/*
   1.121 +** findCategoryNode
   1.122 +**
   1.123 +** Given a category name, finds the Node corresponding to the category
   1.124 +*/
   1.125 +STCategoryNode *
   1.126 +findCategoryNode(const char *catName, STGlobals * g)
   1.127 +{
   1.128 +    uint32_t i;
   1.129 +
   1.130 +    for (i = 0; i < g->mNCategoryMap; i++) {
   1.131 +        if (!strcmp(g->mCategoryMap[i]->categoryName, catName))
   1.132 +            return g->mCategoryMap[i]->node;
   1.133 +    }
   1.134 +
   1.135 +    /* Check if we are looking for the root node */
   1.136 +    if (!strcmp(catName, ST_ROOT_CATEGORY_NAME))
   1.137 +        return &g->mCategoryRoot;
   1.138 +
   1.139 +    return NULL;
   1.140 +}
   1.141 +
   1.142 +/*
   1.143 +** AddCategoryNode
   1.144 +**
   1.145 +** Adds a mapping between a category and its Node into the categoryMap
   1.146 +*/
   1.147 +int
   1.148 +AddCategoryNode(STCategoryNode * node, STGlobals * g)
   1.149 +{
   1.150 +    if (g->mNCategoryMap % ST_ALLOC_STEP == 0) {
   1.151 +        /* Need more space */
   1.152 +        STCategoryMapEntry **newmap =
   1.153 +            (STCategoryMapEntry **) realloc(g->mCategoryMap,
   1.154 +                                            (g->mNCategoryMap +
   1.155 +                                             ST_ALLOC_STEP) *
   1.156 +                                            sizeof(STCategoryMapEntry *));
   1.157 +        if (!newmap) {
   1.158 +            REPORT_ERROR(__LINE__, AddCategoryNode_No_Memory);
   1.159 +            return -1;
   1.160 +        }
   1.161 +        g->mCategoryMap = newmap;
   1.162 +
   1.163 +    }
   1.164 +    g->mCategoryMap[g->mNCategoryMap] =
   1.165 +        (STCategoryMapEntry *) calloc(1, sizeof(STCategoryMapEntry));
   1.166 +    if (!g->mCategoryMap[g->mNCategoryMap]) {
   1.167 +        REPORT_ERROR(__LINE__, AddCategoryNode_No_Memory);
   1.168 +        return -1;
   1.169 +    }
   1.170 +    g->mCategoryMap[g->mNCategoryMap]->categoryName = node->categoryName;
   1.171 +    g->mCategoryMap[g->mNCategoryMap]->node = node;
   1.172 +    g->mNCategoryMap++;
   1.173 +    return 0;
   1.174 +}
   1.175 +
   1.176 +/*
   1.177 +** NewCategoryNode
   1.178 +**
   1.179 +** Creates a new category node for category name 'catname' and makes
   1.180 +** 'parent' its parent.
   1.181 +*/
   1.182 +STCategoryNode *
   1.183 +NewCategoryNode(const char *catName, STCategoryNode * parent, STGlobals * g)
   1.184 +{
   1.185 +    STCategoryNode *node;
   1.186 +
   1.187 +    node = (STCategoryNode *) calloc(1, sizeof(STCategoryNode));
   1.188 +    if (!node)
   1.189 +        return NULL;
   1.190 +
   1.191 +    node->runs =
   1.192 +        (STRun **) calloc(g->mCommandLineOptions.mContexts, sizeof(STRun *));
   1.193 +    if (NULL == node->runs) {
   1.194 +        free(node);
   1.195 +        return NULL;
   1.196 +    }
   1.197 +
   1.198 +    node->categoryName = catName;
   1.199 +
   1.200 +    /* Set parent of child */
   1.201 +    node->parent = parent;
   1.202 +
   1.203 +    /* Set child in parent */
   1.204 +    AddChild(parent, node);
   1.205 +
   1.206 +    /* Add node into mapping table */
   1.207 +    AddCategoryNode(node, g);
   1.208 +
   1.209 +    return node;
   1.210 +}
   1.211 +
   1.212 +/*
   1.213 +** ProcessCategoryLeafRule
   1.214 +**
   1.215 +** Add this into the tree as a leaf node. It doesn't know who its parent is. For now we make
   1.216 +** root as its parent
   1.217 +*/
   1.218 +int
   1.219 +ProcessCategoryLeafRule(STCategoryRule * leafRule, STCategoryNode * root,
   1.220 +                        STGlobals * g)
   1.221 +{
   1.222 +    STCategoryRule *rule;
   1.223 +    STCategoryNode *node;
   1.224 +
   1.225 +    rule = (STCategoryRule *) calloc(1, sizeof(STCategoryRule));
   1.226 +    if (!rule)
   1.227 +        return -1;
   1.228 +
   1.229 +    /* Take ownership of all elements of rule */
   1.230 +    *rule = *leafRule;
   1.231 +
   1.232 +    /* Find/Make a STCategoryNode and add it into the tree */
   1.233 +    node = findCategoryNode(rule->categoryName, g);
   1.234 +    if (!node)
   1.235 +        node = NewCategoryNode(rule->categoryName, root, g);
   1.236 +
   1.237 +    /* Make sure rule knows which node to access */
   1.238 +    rule->node = node;
   1.239 +
   1.240 +    /* Add rule into rulelist */
   1.241 +    AddRule(g, rule);
   1.242 +
   1.243 +    return 0;
   1.244 +}
   1.245 +
   1.246 +/*
   1.247 +** ProcessCategoryParentRule
   1.248 +**
   1.249 +** Rule has all the children of category as patterns. Sets up the tree so that
   1.250 +** the parent child relationship is honored.
   1.251 +*/
   1.252 +int
   1.253 +ProcessCategoryParentRule(STCategoryRule * parentRule, STCategoryNode * root,
   1.254 +                          STGlobals * g)
   1.255 +{
   1.256 +    STCategoryNode *node;
   1.257 +    STCategoryNode *child;
   1.258 +    uint32_t i;
   1.259 +
   1.260 +    /* Find the parent node in the tree. If not make one and add it into the tree */
   1.261 +    node = findCategoryNode(parentRule->categoryName, g);
   1.262 +    if (!node) {
   1.263 +        node = NewCategoryNode(parentRule->categoryName, root, g);
   1.264 +        if (!node)
   1.265 +            return -1;
   1.266 +    }
   1.267 +
   1.268 +    /* For every child node, Find/Create it and make it the child of this node */
   1.269 +    for (i = 0; i < parentRule->npats; i++) {
   1.270 +        child = findCategoryNode(parentRule->pats[i], g);
   1.271 +        if (!child) {
   1.272 +            child = NewCategoryNode(parentRule->pats[i], node, g);
   1.273 +            if (!child)
   1.274 +                return -1;
   1.275 +        }
   1.276 +        else {
   1.277 +            /* Reparent child to node. This is because when we created the node
   1.278 +             ** we would have created it as the child of root. Now we need to
   1.279 +             ** remove it from root's child list and add it into this node
   1.280 +             */
   1.281 +            Reparent(node, child);
   1.282 +        }
   1.283 +    }
   1.284 +
   1.285 +    return 0;
   1.286 +}
   1.287 +
   1.288 +/*
   1.289 +** initCategories
   1.290 +**
   1.291 +** Initialize all categories. This reads in a file that says how to categorize
   1.292 +** each callsite, creates a tree of these categories and makes a list of these
   1.293 +** patterns in order for matching
   1.294 +*/
   1.295 +int
   1.296 +initCategories(STGlobals * g)
   1.297 +{
   1.298 +    FILE *fp;
   1.299 +    char buf[1024], *in;
   1.300 +    int n;
   1.301 +    PRBool inrule, leaf;
   1.302 +    STCategoryRule rule;
   1.303 +
   1.304 +    fp = fopen(g->mCommandLineOptions.mCategoryFile, "r");
   1.305 +    if (!fp) {
   1.306 +        /* It isn't an error to not have a categories file */
   1.307 +        REPORT_INFO("No categories file.");
   1.308 +        return -1;
   1.309 +    }
   1.310 +
   1.311 +    inrule = PR_FALSE;
   1.312 +    leaf = PR_FALSE;
   1.313 +
   1.314 +    memset(&rule, 0, sizeof(rule));
   1.315 +
   1.316 +    while (fgets(buf, sizeof(buf), fp) != NULL) {
   1.317 +        /* Lose the \n */
   1.318 +        n = strlen(buf);
   1.319 +        if (buf[n - 1] == '\n')
   1.320 +            buf[--n] = '\0';
   1.321 +        in = buf;
   1.322 +
   1.323 +        /* skip comments */
   1.324 +        if (*in == '#')
   1.325 +            continue;
   1.326 +
   1.327 +        /* skip empty lines. If we are in a rule, end the rule. */
   1.328 +        while (*in && isspace(*in))
   1.329 +            in++;
   1.330 +        if (*in == '\0') {
   1.331 +            if (inrule) {
   1.332 +                /* End the rule : leaf or non-leaf */
   1.333 +                if (leaf)
   1.334 +                    ProcessCategoryLeafRule(&rule, &g->mCategoryRoot, g);
   1.335 +                else
   1.336 +                    /* non-leaf */
   1.337 +                    ProcessCategoryParentRule(&rule, &g->mCategoryRoot, g);
   1.338 +                inrule = PR_FALSE;
   1.339 +                memset(&rule, 0, sizeof(rule));
   1.340 +            }
   1.341 +            continue;
   1.342 +        }
   1.343 +
   1.344 +        /* if we are in a rule acculumate */
   1.345 +        if (inrule) {
   1.346 +            rule.pats[rule.npats] = strdup(in);
   1.347 +            rule.patlen[rule.npats++] = strlen(in);
   1.348 +        }
   1.349 +        else if (*in == '<') {
   1.350 +            /* Start a category */
   1.351 +            inrule = PR_TRUE;
   1.352 +            leaf = PR_TRUE;
   1.353 +
   1.354 +            /* Get the category name */
   1.355 +            in++;
   1.356 +            n = strlen(in);
   1.357 +            if (in[n - 1] == '>')
   1.358 +                in[n - 1] = '\0';
   1.359 +            rule.categoryName = strdup(in);
   1.360 +        }
   1.361 +        else {
   1.362 +            /* this is a non-leaf category. Should be of the form CategoryName
   1.363 +             ** followed by list of child category names one per line
   1.364 +             */
   1.365 +            inrule = PR_TRUE;
   1.366 +            leaf = PR_FALSE;
   1.367 +            rule.categoryName = strdup(in);
   1.368 +        }
   1.369 +    }
   1.370 +
   1.371 +    /* If we were in a rule when processing the last line, end the rule */
   1.372 +    if (inrule) {
   1.373 +        /* End the rule : leaf or non-leaf */
   1.374 +        if (leaf)
   1.375 +            ProcessCategoryLeafRule(&rule, &g->mCategoryRoot, g);
   1.376 +        else
   1.377 +            /* non-leaf */
   1.378 +            ProcessCategoryParentRule(&rule, &g->mCategoryRoot, g);
   1.379 +    }
   1.380 +
   1.381 +    /* Add the final "uncategorized" category. We make new memory locations
   1.382 +     ** for all these to conform to the general principle of all strings are allocated
   1.383 +     ** so it makes release logic very simple.
   1.384 +     */
   1.385 +    memset(&rule, 0, sizeof(rule));
   1.386 +    rule.categoryName = strdup("uncategorized");
   1.387 +    rule.pats[0] = strdup("");
   1.388 +    rule.patlen[0] = 0;
   1.389 +    rule.npats = 1;
   1.390 +    ProcessCategoryLeafRule(&rule, &g->mCategoryRoot, g);
   1.391 +
   1.392 +    return 0;
   1.393 +}
   1.394 +
   1.395 +/*
   1.396 +** callsiteMatchesRule
   1.397 +**
   1.398 +** Returns the corresponding node if callsite matches the rule. Rule is a sequence
   1.399 +** of patterns that must match contiguously the callsite.
   1.400 +*/
   1.401 +int
   1.402 +callsiteMatchesRule(tmcallsite * aCallsite, STCategoryRule * aRule)
   1.403 +{
   1.404 +    uint32_t patnum = 0;
   1.405 +    const char *methodName = NULL;
   1.406 +
   1.407 +    while (patnum < aRule->npats && aCallsite && aCallsite->method) {
   1.408 +        methodName = tmmethodnode_name(aCallsite->method);
   1.409 +        if (!methodName)
   1.410 +            return 0;
   1.411 +        if (!*aRule->pats[patnum]
   1.412 +            || !strncmp(methodName, aRule->pats[patnum],
   1.413 +                        aRule->patlen[patnum])) {
   1.414 +            /* We have matched so far. Proceed up the stack and to the next pattern */
   1.415 +            patnum++;
   1.416 +            aCallsite = aCallsite->parent;
   1.417 +        }
   1.418 +        else {
   1.419 +            /* Deal with mismatch */
   1.420 +            if (patnum > 0) {
   1.421 +                /* contiguous mismatch. Stop */
   1.422 +                return 0;
   1.423 +            }
   1.424 +            /* We still haven't matched the first pattern. Proceed up the stack without
   1.425 +             ** moving to the next pattern.
   1.426 +             */
   1.427 +            aCallsite = aCallsite->parent;
   1.428 +        }
   1.429 +    }
   1.430 +
   1.431 +    if (patnum == aRule->npats) {
   1.432 +        /* all patterns matched. We have a winner. */
   1.433 +#if defined(DEBUG_dp) && 0
   1.434 +        fprintf(stderr, "[%s] match\n", aRule->categoryName);
   1.435 +#endif
   1.436 +        return 1;
   1.437 +    }
   1.438 +
   1.439 +    return 0;
   1.440 +}
   1.441 +
   1.442 +#ifdef DEBUG_dp
   1.443 +PRIntervalTime _gMatchTime = 0;
   1.444 +uint32_t _gMatchCount = 0;
   1.445 +uint32_t _gMatchRules = 0;
   1.446 +#endif
   1.447 +
   1.448 +/*
   1.449 +** matchAllocation
   1.450 +**
   1.451 +** Runs through all rules and returns the node corresponding to
   1.452 +** a match of the allocation.
   1.453 +*/
   1.454 +STCategoryNode *
   1.455 +matchAllocation(STGlobals * g, STAllocation * aAllocation)
   1.456 +{
   1.457 +#ifdef DEBUG_dp
   1.458 +    PRIntervalTime start = PR_IntervalNow();
   1.459 +#endif
   1.460 +    uint32_t rulenum;
   1.461 +    STCategoryNode *node = NULL;
   1.462 +    STCategoryRule *rule;
   1.463 +
   1.464 +    for (rulenum = 0; rulenum < g->mNRules; rulenum++) {
   1.465 +#ifdef DEBUG_dp
   1.466 +        _gMatchRules++;
   1.467 +#endif
   1.468 +        rule = g->mCategoryRules[rulenum];
   1.469 +        if (callsiteMatchesRule(aAllocation->mEvents[0].mCallsite, rule)) {
   1.470 +            node = rule->node;
   1.471 +            break;
   1.472 +        }
   1.473 +    }
   1.474 +#ifdef DEBUG_dp
   1.475 +    _gMatchCount++;
   1.476 +    _gMatchTime += PR_IntervalNow() - start;
   1.477 +#endif
   1.478 +    return node;
   1.479 +}
   1.480 +
   1.481 +/*
   1.482 +** categorizeAllocation
   1.483 +**
   1.484 +** Given an allocation, it adds it into the category tree at the right spot
   1.485 +** by comparing the allocation to the rules and adding into the right node.
   1.486 +** Also, does propogation of cost upwards in the tree.
   1.487 +** The root of the tree is in the globls as the tree is dependent on the
   1.488 +** category file (options) rather than the run.
   1.489 +*/
   1.490 +int
   1.491 +categorizeAllocation(STOptions * inOptions, STContext * inContext,
   1.492 +                     STAllocation * aAllocation, STGlobals * g)
   1.493 +{
   1.494 +    /* Run through the rules in order to see if this allcation matches
   1.495 +     ** any of them.
   1.496 +     */
   1.497 +    STCategoryNode *node;
   1.498 +
   1.499 +    node = matchAllocation(g, aAllocation);
   1.500 +    if (!node) {
   1.501 +        /* ugh! it should atleast go into the "uncategorized" node. wierd!
   1.502 +         */
   1.503 +        REPORT_ERROR(__LINE__, categorizeAllocation);
   1.504 +        return -1;
   1.505 +    }
   1.506 +
   1.507 +    /* Create run for node if not already */
   1.508 +    if (!node->runs[inContext->mIndex]) {
   1.509 +        /*
   1.510 +         ** Create run with positive timestamp as we can harvest it later
   1.511 +         ** for callsite details summarization
   1.512 +         */
   1.513 +        node->runs[inContext->mIndex] =
   1.514 +            createRun(inContext, PR_IntervalNow());
   1.515 +        if (!node->runs[inContext->mIndex]) {
   1.516 +            REPORT_ERROR(__LINE__, categorizeAllocation_No_Memory);
   1.517 +            return -1;
   1.518 +        }
   1.519 +    }
   1.520 +
   1.521 +    /* Add allocation into node. We expand the table of allocations in steps */
   1.522 +    if (node->runs[inContext->mIndex]->mAllocationCount % ST_ALLOC_STEP == 0) {
   1.523 +        /* Need more space */
   1.524 +        STAllocation **allocs;
   1.525 +
   1.526 +        allocs =
   1.527 +            (STAllocation **) realloc(node->runs[inContext->mIndex]->
   1.528 +                                      mAllocations,
   1.529 +                                      (node->runs[inContext->mIndex]->
   1.530 +                                       mAllocationCount +
   1.531 +                                       ST_ALLOC_STEP) *
   1.532 +                                      sizeof(STAllocation *));
   1.533 +        if (!allocs) {
   1.534 +            REPORT_ERROR(__LINE__, categorizeAllocation_No_Memory);
   1.535 +            return -1;
   1.536 +        }
   1.537 +        node->runs[inContext->mIndex]->mAllocations = allocs;
   1.538 +    }
   1.539 +    node->runs[inContext->mIndex]->mAllocations[node->
   1.540 +                                                runs[inContext->mIndex]->
   1.541 +                                                mAllocationCount++] =
   1.542 +        aAllocation;
   1.543 +
   1.544 +    /*
   1.545 +     ** Make sure run's stats are calculated. We don't go update the parents of allocation
   1.546 +     ** at this time. That will happen when we focus on this category. This updating of
   1.547 +     ** stats will provide us fast categoryreports.
   1.548 +     */
   1.549 +    recalculateAllocationCost(inOptions, inContext,
   1.550 +                              node->runs[inContext->mIndex], aAllocation,
   1.551 +                              PR_FALSE);
   1.552 +
   1.553 +    /* Propagate upwards the statistics */
   1.554 +    /* XXX */
   1.555 +#if defined(DEBUG_dp) && 0
   1.556 +    fprintf(stderr, "DEBUG: [%s] match\n", node->categoryName);
   1.557 +#endif
   1.558 +    return 0;
   1.559 +}
   1.560 +
   1.561 +typedef PRBool STCategoryNodeProcessor(STRequest * inRequest,
   1.562 +                                       STOptions * inOptions,
   1.563 +                                       STContext * inContext,
   1.564 +                                       void *clientData,
   1.565 +                                       STCategoryNode * node);
   1.566 +
   1.567 +PRBool
   1.568 +freeNodeRunProcessor(STRequest * inRequest, STOptions * inOptions,
   1.569 +                     STContext * inContext, void *clientData,
   1.570 +                     STCategoryNode * node)
   1.571 +{
   1.572 +    if (node->runs && node->runs[inContext->mIndex]) {
   1.573 +        freeRun(node->runs[inContext->mIndex]);
   1.574 +        node->runs[inContext->mIndex] = NULL;
   1.575 +    }
   1.576 +    return PR_TRUE;
   1.577 +}
   1.578 +
   1.579 +PRBool
   1.580 +freeNodeRunsProcessor(STRequest * inRequest, STOptions * inOptions,
   1.581 +                      STContext * inContext, void *clientData,
   1.582 +                      STCategoryNode * node)
   1.583 +{
   1.584 +    if (node->runs) {
   1.585 +        uint32_t loop = 0;
   1.586 +
   1.587 +        for (loop = 0; loop < globals.mCommandLineOptions.mContexts; loop++) {
   1.588 +            if (node->runs[loop]) {
   1.589 +                freeRun(node->runs[loop]);
   1.590 +                node->runs[loop] = NULL;
   1.591 +            }
   1.592 +        }
   1.593 +
   1.594 +        free(node->runs);
   1.595 +        node->runs = NULL;
   1.596 +    }
   1.597 +
   1.598 +    return PR_TRUE;
   1.599 +}
   1.600 +
   1.601 +#if defined(DEBUG_dp)
   1.602 +PRBool
   1.603 +printNodeProcessor(STRequest * inRequest, STOptions * inOptions,
   1.604 +                   STContext * inContext, void *clientData,
   1.605 +                   STCategoryNode * node)
   1.606 +{
   1.607 +    STCategoryNode *root = (STCategoryNode *) clientData;
   1.608 +
   1.609 +    fprintf(stderr, "%-25s [ %9s size", node->categoryName,
   1.610 +            FormatNumber(node->run ? node->run->mStats[inContext->mIndex].
   1.611 +                         mSize : 0));
   1.612 +    fprintf(stderr, ", %5.1f%%",
   1.613 +            node->run ? ((double) node->run->mStats[inContext->mIndex].mSize /
   1.614 +                         root->run->mStats[inContext->mIndex].mSize *
   1.615 +                         100) : 0);
   1.616 +    fprintf(stderr, ", %7s allocations ]\n",
   1.617 +            FormatNumber(node->run ? node->run->mStats[inContext->mIndex].
   1.618 +                         mCompositeCount : 0));
   1.619 +    return PR_TRUE;
   1.620 +}
   1.621 +
   1.622 +#endif
   1.623 +
   1.624 +typedef struct __struct_optcon
   1.625 +{
   1.626 +    STOptions *mOptions;
   1.627 +    STContext *mContext;
   1.628 +}
   1.629 +optcon;
   1.630 +
   1.631 +/*
   1.632 +** compareNode
   1.633 +**
   1.634 +** qsort callback.
   1.635 +** Compare the nodes as specified by the options.
   1.636 +*/
   1.637 +int
   1.638 +compareNode(const void *aNode1, const void *aNode2, void *aContext)
   1.639 +{
   1.640 +    int retval = 0;
   1.641 +    STCategoryNode *node1, *node2;
   1.642 +    uint32_t a, b;
   1.643 +    optcon *oc = (optcon *) aContext;
   1.644 +
   1.645 +    if (!aNode1 || !aNode2 || !oc->mOptions || !oc->mContext)
   1.646 +        return 0;
   1.647 +
   1.648 +    node1 = *((STCategoryNode **) aNode1);
   1.649 +    node2 = *((STCategoryNode **) aNode2);
   1.650 +
   1.651 +    if (node1 && node2) {
   1.652 +        if (oc->mOptions->mOrderBy == ST_COUNT) {
   1.653 +            a = (node1->runs[oc->mContext->mIndex]) ? node1->runs[oc->
   1.654 +                                                                  mContext->
   1.655 +                                                                  mIndex]->
   1.656 +                mStats[oc->mContext->mIndex].mCompositeCount : 0;
   1.657 +            b = (node2->runs[oc->mContext->mIndex]) ? node2->runs[oc->
   1.658 +                                                                  mContext->
   1.659 +                                                                  mIndex]->
   1.660 +                mStats[oc->mContext->mIndex].mCompositeCount : 0;
   1.661 +        }
   1.662 +        else {
   1.663 +            /* Default is by size */
   1.664 +            a = (node1->runs[oc->mContext->mIndex]) ? node1->runs[oc->
   1.665 +                                                                  mContext->
   1.666 +                                                                  mIndex]->
   1.667 +                mStats[oc->mContext->mIndex].mSize : 0;
   1.668 +            b = (node2->runs[oc->mContext->mIndex]) ? node2->runs[oc->
   1.669 +                                                                  mContext->
   1.670 +                                                                  mIndex]->
   1.671 +                mStats[oc->mContext->mIndex].mSize : 0;
   1.672 +        }
   1.673 +        if (a < b)
   1.674 +            retval = __LINE__;
   1.675 +        else
   1.676 +            retval = -__LINE__;
   1.677 +    }
   1.678 +    return retval;
   1.679 +}
   1.680 +
   1.681 +PRBool
   1.682 +sortNodeProcessor(STRequest * inRequest, STOptions * inOptions,
   1.683 +                  STContext * inContext, void *clientData,
   1.684 +                  STCategoryNode * node)
   1.685 +{
   1.686 +    if (node->nchildren) {
   1.687 +        optcon context;
   1.688 +
   1.689 +        context.mOptions = inOptions;
   1.690 +        context.mContext = inContext;
   1.691 +
   1.692 +        NS_QuickSort(node->children, node->nchildren,
   1.693 +                     sizeof(STCategoryNode *), compareNode, &context);
   1.694 +    }
   1.695 +
   1.696 +    return PR_TRUE;
   1.697 +}
   1.698 +
   1.699 +
   1.700 +/*
   1.701 +** walkTree
   1.702 +**
   1.703 +** General purpose tree walker. Issues callback for each node.
   1.704 +** If 'maxdepth' > 0, then stops after processing that depth. Root is
   1.705 +** depth 0, the nodes below it are depth 1 etc...
   1.706 +*/
   1.707 +#define MODINC(n, mod) ((n+1) % mod)
   1.708 +
   1.709 +void
   1.710 +walkTree(STCategoryNode * root, STCategoryNodeProcessor func,
   1.711 +         STRequest * inRequest, STOptions * inOptions, STContext * inContext,
   1.712 +         void *clientData, int maxdepth)
   1.713 +{
   1.714 +    STCategoryNode *nodes[1024], *node;
   1.715 +    uint32_t begin, end, i;
   1.716 +    int ret = 0;
   1.717 +    int curdepth = 0, ncurdepth = 0;
   1.718 +
   1.719 +    nodes[0] = root;
   1.720 +    begin = 0;
   1.721 +    end = 1;
   1.722 +    ncurdepth = 1;
   1.723 +    while (begin != end) {
   1.724 +        node = nodes[begin];
   1.725 +        ret = (*func) (inRequest, inOptions, inContext, clientData, node);
   1.726 +        if (!ret) {
   1.727 +            /* Abort */
   1.728 +            break;
   1.729 +        }
   1.730 +        begin = MODINC(begin, 1024);
   1.731 +        for (i = 0; i < node->nchildren; i++) {
   1.732 +            nodes[end] = node->children[i];
   1.733 +            end = MODINC(end, 1024);
   1.734 +        }
   1.735 +        /* Depth tracking. Do it only if walkTree is contrained by a maxdepth */
   1.736 +        if (maxdepth > 0 && --ncurdepth == 0) {
   1.737 +            /*
   1.738 +             ** No more children in current depth. The rest of the nodes
   1.739 +             ** we have in our list should be nodes in the depth below us.
   1.740 +             */
   1.741 +            ncurdepth = (begin < end) ? (end - begin) : (1024 - begin + end);
   1.742 +            if (++curdepth > maxdepth) {
   1.743 +                /*
   1.744 +                 ** Gone too deep. Stop.
   1.745 +                 */
   1.746 +                break;
   1.747 +            }
   1.748 +        }
   1.749 +    }
   1.750 +    return;
   1.751 +}
   1.752 +
   1.753 +int
   1.754 +freeRule(STCategoryRule * rule)
   1.755 +{
   1.756 +    uint32_t i;
   1.757 +    char *p = (char *) rule->categoryName;
   1.758 +
   1.759 +    PR_FREEIF(p);
   1.760 +
   1.761 +    for (i = 0; i < rule->npats; i++)
   1.762 +        free(rule->pats[i]);
   1.763 +
   1.764 +    free(rule);
   1.765 +    return 0;
   1.766 +}
   1.767 +
   1.768 +void
   1.769 +freeNodeRuns(STCategoryNode * root)
   1.770 +{
   1.771 +    walkTree(root, freeNodeRunsProcessor, NULL, NULL, NULL, NULL, 0);
   1.772 +}
   1.773 +
   1.774 +void
   1.775 +freeNodeMap(STGlobals * g)
   1.776 +{
   1.777 +    uint32_t i;
   1.778 +
   1.779 +    /* all nodes are in the map table. Just delete all of those. */
   1.780 +    for (i = 0; i < g->mNCategoryMap; i++) {
   1.781 +        free(g->mCategoryMap[i]->node);
   1.782 +        free(g->mCategoryMap[i]);
   1.783 +    }
   1.784 +    free(g->mCategoryMap);
   1.785 +}
   1.786 +
   1.787 +int
   1.788 +freeCategories(STGlobals * g)
   1.789 +{
   1.790 +    uint32_t i;
   1.791 +
   1.792 +    /*
   1.793 +     ** walk the tree and free runs held in nodes
   1.794 +     */
   1.795 +    freeNodeRuns(&g->mCategoryRoot);
   1.796 +
   1.797 +    /*
   1.798 +     ** delete nodemap. This is the where nodes get deleted.
   1.799 +     */
   1.800 +    freeNodeMap(g);
   1.801 +
   1.802 +    /*
   1.803 +     ** delete rule stuff
   1.804 +     */
   1.805 +    for (i = 0; i < g->mNRules; i++) {
   1.806 +        freeRule(g->mCategoryRules[i]);
   1.807 +    }
   1.808 +    free(g->mCategoryRules);
   1.809 +
   1.810 +    return 0;
   1.811 +}
   1.812 +
   1.813 +
   1.814 +/*
   1.815 +** categorizeRun
   1.816 +**
   1.817 +** categorize all the allocations of the run using the rules into
   1.818 +** a tree rooted at globls.mCategoryRoot
   1.819 +*/
   1.820 +int
   1.821 +categorizeRun(STOptions * inOptions, STContext * inContext,
   1.822 +              const STRun * aRun, STGlobals * g)
   1.823 +{
   1.824 +    uint32_t i;
   1.825 +
   1.826 +#if defined(DEBUG_dp)
   1.827 +    PRIntervalTime start = PR_IntervalNow();
   1.828 +
   1.829 +    fprintf(stderr, "DEBUG: categorizing run...\n");
   1.830 +#endif
   1.831 +
   1.832 +    /*
   1.833 +     ** First, cleanup our tree
   1.834 +     */
   1.835 +    walkTree(&g->mCategoryRoot, freeNodeRunProcessor, NULL, inOptions,
   1.836 +             inContext, NULL, 0);
   1.837 +
   1.838 +    if (g->mNCategoryMap > 0) {
   1.839 +        for (i = 0; i < aRun->mAllocationCount; i++) {
   1.840 +            categorizeAllocation(inOptions, inContext, aRun->mAllocations[i],
   1.841 +                                 g);
   1.842 +        }
   1.843 +    }
   1.844 +
   1.845 +    /*
   1.846 +     ** the run is always going to be the one corresponding to the root node
   1.847 +     */
   1.848 +    g->mCategoryRoot.runs[inContext->mIndex] = (STRun *) aRun;
   1.849 +    g->mCategoryRoot.categoryName = ST_ROOT_CATEGORY_NAME;
   1.850 +
   1.851 +#if defined(DEBUG_dp)
   1.852 +    fprintf(stderr,
   1.853 +            "DEBUG: categorizing ends: %dms [%d rules, %d allocations]\n",
   1.854 +            PR_IntervalToMilliseconds(PR_IntervalNow() - start), g->mNRules,
   1.855 +            aRun->mAllocationCount);
   1.856 +    fprintf(stderr, "DEBUG: match : %dms [%d calls, %d rule-compares]\n",
   1.857 +            PR_IntervalToMilliseconds(_gMatchTime), _gMatchCount,
   1.858 +            _gMatchRules);
   1.859 +#endif
   1.860 +
   1.861 +    /*
   1.862 +     ** sort the tree based on our sort criterion
   1.863 +     */
   1.864 +    walkTree(&g->mCategoryRoot, sortNodeProcessor, NULL, inOptions, inContext,
   1.865 +             NULL, 0);
   1.866 +
   1.867 +#if defined(DEBUG_dp)
   1.868 +    walkTree(&g->mCategoryRoot, printNodeProcessor, NULL, inOptions,
   1.869 +             inContext, &g->mCategoryRoot, 0);
   1.870 +#endif
   1.871 +
   1.872 +    return 0;
   1.873 +}
   1.874 +
   1.875 +
   1.876 +/*
   1.877 +** displayCategoryReport
   1.878 +**
   1.879 +** Generate the category report - a list of all categories and details about each
   1.880 +** depth parameter controls how deep we traverse the category tree.
   1.881 +*/
   1.882 +PRBool
   1.883 +displayCategoryNodeProcessor(STRequest * inRequest, STOptions * inOptions,
   1.884 +                             STContext * inContext, void *clientData,
   1.885 +                             STCategoryNode * node)
   1.886 +{
   1.887 +    STCategoryNode *root = (STCategoryNode *) clientData;
   1.888 +    uint32_t byteSize = 0, heapCost = 0, count = 0;
   1.889 +    double percent = 0;
   1.890 +    STOptions customOps;
   1.891 +
   1.892 +    if (node->runs[inContext->mIndex]) {
   1.893 +        /*
   1.894 +         ** Byte size
   1.895 +         */
   1.896 +        byteSize =
   1.897 +            node->runs[inContext->mIndex]->mStats[inContext->mIndex].mSize;
   1.898 +
   1.899 +        /*
   1.900 +         ** Composite count
   1.901 +         */
   1.902 +        count =
   1.903 +            node->runs[inContext->mIndex]->mStats[inContext->mIndex].
   1.904 +            mCompositeCount;
   1.905 +
   1.906 +        /*
   1.907 +         ** Heap operation cost
   1.908 +         **/
   1.909 +        heapCost =
   1.910 +            node->runs[inContext->mIndex]->mStats[inContext->mIndex].
   1.911 +            mHeapRuntimeCost;
   1.912 +
   1.913 +        /*
   1.914 +         ** % of total size
   1.915 +         */
   1.916 +        if (root->runs[inContext->mIndex]) {
   1.917 +            percent =
   1.918 +                ((double) byteSize) /
   1.919 +                root->runs[inContext->mIndex]->mStats[inContext->mIndex].
   1.920 +                mSize * 100;
   1.921 +        }
   1.922 +    }
   1.923 +
   1.924 +    PR_fprintf(inRequest->mFD, " <tr>\n" "  <td>");
   1.925 +
   1.926 +    /* a link to topcallsites report with focus on category */
   1.927 +    memcpy(&customOps, inOptions, sizeof(customOps));
   1.928 +    PR_snprintf(customOps.mCategoryName, sizeof(customOps.mCategoryName),
   1.929 +                "%s", node->categoryName);
   1.930 +
   1.931 +    htmlAnchor(inRequest, "top_callsites.html", node->categoryName, NULL,
   1.932 +               "category-callsites", &customOps);
   1.933 +    PR_fprintf(inRequest->mFD,
   1.934 +               "</td>\n" "  <td align=right>%u</td>\n"
   1.935 +               "  <td align=right>%4.1f%%</td>\n"
   1.936 +               "  <td align=right>%u</td>\n" "  <td align=right>"
   1.937 +               ST_MICROVAL_FORMAT "</td>\n" " </tr>\n", byteSize, percent,
   1.938 +               count, ST_MICROVAL_PRINTABLE(heapCost));
   1.939 +
   1.940 +    return PR_TRUE;
   1.941 +}
   1.942 +
   1.943 +
   1.944 +int
   1.945 +displayCategoryReport(STRequest * inRequest, STCategoryNode * root, int depth)
   1.946 +{
   1.947 +    PR_fprintf(inRequest->mFD,
   1.948 +               "<table class=\"category-list data\">\n"
   1.949 +               " <tr class=\"row-header\">\n"
   1.950 +               "  <th>Category</th>\n"
   1.951 +               "  <th>Composite Byte Size</th>\n"
   1.952 +               "  <th>%% of Total Size</th>\n"
   1.953 +               "  <th>Heap Object Count</th>\n"
   1.954 +               "  <th>Composite Heap Operations Seconds</th>\n" " </tr>\n");
   1.955 +
   1.956 +    walkTree(root, displayCategoryNodeProcessor, inRequest,
   1.957 +             &inRequest->mOptions, inRequest->mContext, root, depth);
   1.958 +
   1.959 +    PR_fprintf(inRequest->mFD, "</table>\n");
   1.960 +
   1.961 +    return 0;
   1.962 +}

mercurial