tools/trace-malloc/spacecategory.c

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

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

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

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

mercurial