Backed out changeset b0a4653325cc (bug 941565) for Windows bustage.
[gecko.git] / tools / trace-malloc / spacecategory.c
blobf3b518cec8cefaa6c3681d778301e8d071fedb80
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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.
15 ** Required include files.
17 #include "spacetrace.h"
18 #include <ctype.h>
19 #include <string.h>
21 #include "nsQuickSort.h"
23 #if defined(HAVE_BOUTELL_GD)
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).
29 #include <gd.h>
30 #include <gdfontt.h>
31 #include <gdfonts.h>
32 #include <gdfontmb.h>
33 #endif /* HAVE_BOUTELL_GD */
36 ** AddRule
38 ** Add a rule into the list of rules maintainted in global
40 int
41 AddRule(STGlobals * g, STCategoryRule * rule)
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;
55 g->mCategoryRules = newrules;
57 g->mCategoryRules[g->mNRules++] = rule;
58 return 0;
62 ** AddChild
64 ** Add the node as a child of the parent node
66 int
67 AddChild(STCategoryNode * parent, STCategoryNode * child)
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;
81 parent->children = newnodes;
83 parent->children[parent->nchildren++] = child;
84 return 0;
87 int
88 Reparent(STCategoryNode * parent, STCategoryNode * child)
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;
111 /* Add child into new parent */
112 AddChild(parent, child);
114 return 0;
118 ** findCategoryNode
120 ** Given a category name, finds the Node corresponding to the category
122 STCategoryNode *
123 findCategoryNode(const char *catName, STGlobals * g)
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;
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;
140 ** AddCategoryNode
142 ** Adds a mapping between a category and its Node into the categoryMap
145 AddCategoryNode(STCategoryNode * node, STGlobals * g)
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;
158 g->mCategoryMap = newmap;
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;
167 g->mCategoryMap[g->mNCategoryMap]->categoryName = node->categoryName;
168 g->mCategoryMap[g->mNCategoryMap]->node = node;
169 g->mNCategoryMap++;
170 return 0;
174 ** NewCategoryNode
176 ** Creates a new category node for category name 'catname' and makes
177 ** 'parent' its parent.
179 STCategoryNode *
180 NewCategoryNode(const char *catName, STCategoryNode * parent, STGlobals * g)
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;
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;
210 ** ProcessCategoryLeafRule
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
216 ProcessCategoryLeafRule(STCategoryRule * leafRule, STCategoryNode * root,
217 STGlobals * g)
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;
244 ** ProcessCategoryParentRule
246 ** Rule has all the children of category as patterns. Sets up the tree so that
247 ** the parent child relationship is honored.
250 ProcessCategoryParentRule(STCategoryRule * parentRule, STCategoryNode * root,
251 STGlobals * g)
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;
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;
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
278 Reparent(node, child);
282 return 0;
286 ** initCategories
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
293 initCategories(STGlobals * g)
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;
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));
338 continue;
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);
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);
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
362 inrule = PR_TRUE;
363 leaf = PR_FALSE;
364 rule.categoryName = strdup(in);
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);
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.
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;
393 ** callsiteMatchesRule
395 ** Returns the corresponding node if callsite matches the rule. Rule is a sequence
396 ** of patterns that must match contiguously the callsite.
399 callsiteMatchesRule(tmcallsite * aCallsite, STCategoryRule * aRule)
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;
415 else {
416 /* Deal with mismatch */
417 if (patnum > 0) {
418 /* contiguous mismatch. Stop */
419 return 0;
421 /* We still haven't matched the first pattern. Proceed up the stack without
422 ** moving to the next pattern.
424 aCallsite = aCallsite->parent;
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;
436 return 0;
439 #ifdef DEBUG_dp
440 PRIntervalTime _gMatchTime = 0;
441 uint32_t _gMatchCount = 0;
442 uint32_t _gMatchRules = 0;
443 #endif
446 ** matchAllocation
448 ** Runs through all rules and returns the node corresponding to
449 ** a match of the allocation.
451 STCategoryNode *
452 matchAllocation(STGlobals * g, STAllocation * aAllocation)
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;
471 #ifdef DEBUG_dp
472 _gMatchCount++;
473 _gMatchTime += PR_IntervalNow() - start;
474 #endif
475 return node;
479 ** categorizeAllocation
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.
488 categorizeAllocation(STOptions * inOptions, STContext * inContext,
489 STAllocation * aAllocation, STGlobals * g)
491 /* Run through the rules in order to see if this allcation matches
492 ** any of them.
494 STCategoryNode *node;
496 node = matchAllocation(g, aAllocation);
497 if (!node) {
498 /* ugh! it should atleast go into the "uncategorized" node. wierd!
500 REPORT_ERROR(__LINE__, categorizeAllocation);
501 return -1;
504 /* Create run for node if not already */
505 if (!node->runs[inContext->mIndex]) {
507 ** Create run with positive timestamp as we can harvest it later
508 ** for callsite details summarization
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;
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;
534 node->runs[inContext->mIndex]->mAllocations = allocs;
536 node->runs[inContext->mIndex]->mAllocations[node->
537 runs[inContext->mIndex]->
538 mAllocationCount++] =
539 aAllocation;
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.
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;
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)
569 if (node->runs && node->runs[inContext->mIndex]) {
570 freeRun(node->runs[inContext->mIndex]);
571 node->runs[inContext->mIndex] = NULL;
573 return PR_TRUE;
576 PRBool
577 freeNodeRunsProcessor(STRequest * inRequest, STOptions * inOptions,
578 STContext * inContext, void *clientData,
579 STCategoryNode * node)
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;
591 free(node->runs);
592 node->runs = NULL;
595 return PR_TRUE;
598 #if defined(DEBUG_dp)
599 PRBool
600 printNodeProcessor(STRequest * inRequest, STOptions * inOptions,
601 STContext * inContext, void *clientData,
602 STCategoryNode * node)
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;
619 #endif
621 typedef struct __struct_optcon
623 STOptions *mOptions;
624 STContext *mContext;
626 optcon;
629 ** compareNode
631 ** qsort callback.
632 ** Compare the nodes as specified by the options.
635 compareNode(const void *aNode1, const void *aNode2, void *aContext)
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;
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;
670 if (a < b)
671 retval = __LINE__;
672 else
673 retval = -__LINE__;
675 return retval;
678 PRBool
679 sortNodeProcessor(STRequest * inRequest, STOptions * inOptions,
680 STContext * inContext, void *clientData,
681 STCategoryNode * node)
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);
693 return PR_TRUE;
698 ** walkTree
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...
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)
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;
727 begin = MODINC(begin, 1024);
728 for (i = 0; i < node->nchildren; i++) {
729 nodes[end] = node->children[i];
730 end = MODINC(end, 1024);
732 /* Depth tracking. Do it only if walkTree is contrained by a maxdepth */
733 if (maxdepth > 0 && --ncurdepth == 0) {
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.
738 ncurdepth = (begin < end) ? (end - begin) : (1024 - begin + end);
739 if (++curdepth > maxdepth) {
741 ** Gone too deep. Stop.
743 break;
747 return;
751 freeRule(STCategoryRule * rule)
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;
765 void
766 freeNodeRuns(STCategoryNode * root)
768 walkTree(root, freeNodeRunsProcessor, NULL, NULL, NULL, NULL, 0);
771 void
772 freeNodeMap(STGlobals * g)
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]);
781 free(g->mCategoryMap);
785 freeCategories(STGlobals * g)
787 uint32_t i;
790 ** walk the tree and free runs held in nodes
792 freeNodeRuns(&g->mCategoryRoot);
795 ** delete nodemap. This is the where nodes get deleted.
797 freeNodeMap(g);
800 ** delete rule stuff
802 for (i = 0; i < g->mNRules; i++) {
803 freeRule(g->mCategoryRules[i]);
805 free(g->mCategoryRules);
807 return 0;
812 ** categorizeRun
814 ** categorize all the allocations of the run using the rules into
815 ** a tree rooted at globls.mCategoryRoot
818 categorizeRun(STOptions * inOptions, STContext * inContext,
819 const STRun * aRun, STGlobals * g)
821 uint32_t i;
823 #if defined(DEBUG_dp)
824 PRIntervalTime start = PR_IntervalNow();
826 fprintf(stderr, "DEBUG: categorizing run...\n");
827 #endif
830 ** First, cleanup our tree
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],
843 ** the run is always going to be the one corresponding to the root node
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
859 ** sort the tree based on our sort criterion
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;
874 ** displayCategoryReport
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.
879 PRBool
880 displayCategoryNodeProcessor(STRequest * inRequest, STOptions * inOptions,
881 STContext * inContext, void *clientData,
882 STCategoryNode * node)
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]) {
891 ** Byte size
893 byteSize =
894 node->runs[inContext->mIndex]->mStats[inContext->mIndex].mSize;
897 ** Composite count
899 count =
900 node->runs[inContext->mIndex]->mStats[inContext->mIndex].
901 mCompositeCount;
904 ** Heap operation cost
906 heapCost =
907 node->runs[inContext->mIndex]->mStats[inContext->mIndex].
908 mHeapRuntimeCost;
911 ** % of total size
913 if (root->runs[inContext->mIndex]) {
914 percent =
915 ((double) byteSize) /
916 root->runs[inContext->mIndex]->mStats[inContext->mIndex].
917 mSize * 100;
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;
942 displayCategoryReport(STRequest * inRequest, STCategoryNode * root, int depth)
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;