tools/trace-malloc/spacetrace.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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 ** spacetrace.c
     9 **
    10 ** SpaceTrace is meant to take the output of trace-malloc and present
    11 **   a picture of allocations over the run of the application.
    12 */
    14 /*
    15 ** Required include files.
    16 */
    17 #include "spacetrace.h"
    19 #include <ctype.h>
    20 #include <math.h>
    21 #include <string.h>
    22 #include <time.h>
    23 #if defined(XP_WIN32)
    24 #include <malloc.h>             /* _heapMin */
    25 #endif
    27 #if defined(HAVE_BOUTELL_GD)
    28 /*
    29 ** See http://www.boutell.com/gd for the GD graphics library.
    30 ** Ports for many platorms exist.
    31 ** Your box may already have the lib (mine did, redhat 7.1 workstation).
    32 */
    33 #include <gd.h>
    34 #include <gdfontt.h>
    35 #include <gdfonts.h>
    36 #include <gdfontmb.h>
    37 #endif /* HAVE_BOUTELL_GD */
    39 #include "nsQuickSort.h"
    40 /*
    41 **  strcasecmp API please.
    42 */
    43 #if defined(_MSC_VER)
    44 #define strcasecmp _stricmp
    45 #define strncasecmp _strnicmp
    46 #endif
    48 /*
    49 ** the globals variables.  happy joy.
    50 */
    51 STGlobals globals;
    53 /*
    54 ** have the heap cleanup at opportune times, if possible.
    55 */
    56 void
    57 heapCompact(void)
    58 {
    59 #if defined(XP_WIN32)
    60     _heapmin();
    61 #endif
    62 }
    64 #define ST_CMD_OPTION_BOOL(option_name, option_genre, option_help) \
    65     PR_fprintf(PR_STDOUT, "--%s\nDisabled by default.\n%s\n", #option_name, option_help);
    66 #define ST_CMD_OPTION_STRING(option_name, option_genre, default_value, option_help) \
    67     PR_fprintf(PR_STDOUT, "--%s=<value>\nDefault value is \"%s\".\n%s\n", #option_name, default_value, option_help);
    68 #define ST_CMD_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
    69     PR_fprintf(PR_STDOUT, "--%s=<value>\nUp to %u occurrences allowed.\n%s\n", #option_name, array_size, option_help);
    70 #define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help) \
    71     PR_fprintf(PR_STDOUT, "--%s=<value>\nUnlimited occurrences allowed.\n%s\n", #option_name, option_help);
    72 #define ST_CMD_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
    73     PR_fprintf(PR_STDOUT, "--%s=<value>\nDefault value is %u.\n%s\n", #option_name, default_value, option_help);
    74 #define ST_CMD_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
    75     PR_fprintf(PR_STDOUT, "--%s=<value>\nDefault value is %llu.\n%s\n", #option_name, default_value, option_help);
    77 /*
    78 ** showHelp
    79 **
    80 ** Give simple command line help.
    81 ** Returns !0 if the help was showed.
    82 */
    83 int
    84 showHelp(void)
    85 {
    86     int retval = 0;
    88     if (PR_FALSE != globals.mCommandLineOptions.mHelp) {
    89         PR_fprintf(PR_STDOUT, "Usage:\t%s [OPTION]... [-|filename]\n\n",
    90                    globals.mProgramName);
    93 #include "stoptions.h"
    95         /*
    96          ** Showed something.
    97          */
    98         retval = __LINE__;
    99     }
   101     return retval;
   102 }
   104 /*
   105 ** ticks2xsec
   106 **
   107 ** Convert platform specific ticks to second units
   108 ** Returns 0 on success.
   109 */
   110 uint32_t
   111 ticks2xsec(tmreader * aReader, uint32_t aTicks, uint32_t aResolution)
   112 {
   113     return (uint32_t)((aResolution * aTicks)/aReader->ticksPerSec);
   114 }
   116 #define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000)
   117 #define ticks2usec(reader, ticks) ticks2xsec((reader), (ticks), 1000000)
   119 /*
   120 ** initOptions
   121 **
   122 ** Determine global settings for the application.
   123 ** Returns 0 on success.
   124 */
   125 int
   126 initOptions(int aArgCount, char **aArgArray)
   127 {
   128     int retval = 0;
   129     int traverse = 0;
   131     /*
   132      **  Set the initial global default options.
   133      */
   134 #define ST_CMD_OPTION_BOOL(option_name, option_genre, option_help) globals.mCommandLineOptions.m##option_name = PR_FALSE;
   135 #define ST_CMD_OPTION_STRING(option_name, option_genre, default_value, option_help) PR_snprintf(globals.mCommandLineOptions.m##option_name, sizeof(globals.mCommandLineOptions.m##option_name), "%s", default_value);
   136 #define ST_CMD_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) { int loop; for(loop = 0; loop < array_size; loop++) { globals.mCommandLineOptions.m##option_name[loop][0] = '\0'; } }
   137 #define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help) globals.mCommandLineOptions.m##option_name = NULL; globals.mCommandLineOptions.m##option_name##Count = 0;
   138 #define ST_CMD_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) globals.mCommandLineOptions.m##option_name = default_value * multiplier;
   139 #define ST_CMD_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) { uint64_t def64 = default_value; uint64_t mul64 = multiplier; globals.mCommandLineOptions.m##option_name##64 = def64 * mul64; }
   141 #include "stoptions.h"
   143     /*
   144      ** Go through all arguments.
   145      ** Two dashes lead off an option.
   146      ** Any single dash leads off help, unless it is a lone dash (stdin).
   147      ** Anything else will be attempted as a file to be processed.
   148      */
   149     for (traverse = 1; traverse < aArgCount; traverse++) {
   150         if ('-' == aArgArray[traverse][0] && '-' == aArgArray[traverse][1]) {
   151             const char *option = &aArgArray[traverse][2];
   153             /*
   154              **  Initial if(0) needed to make "else if"s valid.
   155              */
   156             if (0) {
   157             }
   159 #define ST_CMD_OPTION_BOOL(option_name, option_genre, option_help) \
   160     else if(0 == strcasecmp(option, #option_name)) \
   161     { \
   162         globals.mCommandLineOptions.m##option_name = PR_TRUE; \
   163     }
   164 #define ST_CMD_OPTION_STRING(option_name, option_genre, default_value, option_help) \
   165     else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   166     { \
   167         PR_snprintf(globals.mCommandLineOptions.m##option_name, sizeof(globals.mCommandLineOptions.m##option_name), "%s", option + strlen(#option_name "=")); \
   168     }
   169 #define ST_CMD_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
   170     else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   171     { \
   172         int arrLoop = 0; \
   173         \
   174         for(arrLoop = 0; arrLoop < array_size; arrLoop++) \
   175         { \
   176             if('\0' == globals.mCommandLineOptions.m##option_name[arrLoop][0]) \
   177             { \
   178                 break; \
   179             } \
   180         }\
   181         \
   182         if(arrLoop != array_size) \
   183         { \
   184             PR_snprintf(globals.mCommandLineOptions.m##option_name[arrLoop], sizeof(globals.mCommandLineOptions.m##option_name[arrLoop]), "%s", option + strlen(#option_name "=")); \
   185         } \
   186         else \
   187         { \
   188             REPORT_ERROR_MSG(__LINE__, option); \
   189             retval = __LINE__; \
   190             globals.mCommandLineOptions.mHelp = PR_TRUE; \
   191         } \
   192     }
   193 #define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help) \
   194     else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   195     { \
   196         const char** expand = NULL; \
   197         \
   198         expand = (const char**)realloc((void*)globals.mCommandLineOptions.m##option_name, sizeof(const char*) * (globals.mCommandLineOptions.m##option_name##Count + 1)); \
   199         if(NULL != expand) \
   200         { \
   201             globals.mCommandLineOptions.m##option_name = expand; \
   202             globals.mCommandLineOptions.m##option_name[globals.mCommandLineOptions.m##option_name##Count] = option + strlen(#option_name "="); \
   203             globals.mCommandLineOptions.m##option_name##Count++; \
   204         } \
   205         else \
   206         { \
   207             retval = __LINE__; \
   208             globals.mCommandLineOptions.mHelp = PR_TRUE; \
   209         } \
   210     }
   211 #define ST_CMD_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
   212     else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   213     { \
   214         int32_t scanRes = 0; \
   215         \
   216         scanRes = PR_sscanf(option + strlen(#option_name "="), "%u", &globals.mCommandLineOptions.m##option_name); \
   217         if(1 != scanRes) \
   218         { \
   219             REPORT_ERROR_MSG(__LINE__, option); \
   220             retval = __LINE__; \
   221             globals.mCommandLineOptions.mHelp = PR_TRUE; \
   222         } \
   223     }
   224 #define ST_CMD_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
   225     else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   226     { \
   227         int32_t scanRes = 0; \
   228         \
   229         scanRes = PR_sscanf(option + strlen(#option_name "="), "%llu", &globals.mCommandLineOptions.m##option_name##64); \
   230         if(1 != scanRes) \
   231         { \
   232             REPORT_ERROR_MSG(__LINE__, option); \
   233             retval = __LINE__; \
   234             globals.mCommandLineOptions.mHelp = PR_TRUE; \
   235         } \
   236     }
   238 #include "stoptions.h"
   240             /*
   241              **  If no match on options, this else will get hit.
   242              */
   243             else {
   244                 REPORT_ERROR_MSG(__LINE__, option);
   245                 retval = __LINE__;
   246                 globals.mCommandLineOptions.mHelp = PR_TRUE;
   247             }
   248         }
   249         else if ('-' == aArgArray[traverse][0]
   250                  && '\0' != aArgArray[traverse][1]) {
   251             /*
   252              **  Show help, bad/legacy option.
   253              */
   254             REPORT_ERROR_MSG(__LINE__, aArgArray[traverse]);
   255             retval = __LINE__;
   256             globals.mCommandLineOptions.mHelp = PR_TRUE;
   257         }
   258         else {
   259             /*
   260              ** Default is same as FileName option, the file to process.
   261              */
   262             PR_snprintf(globals.mCommandLineOptions.mFileName,
   263                         sizeof(globals.mCommandLineOptions.mFileName), "%s",
   264                         aArgArray[traverse]);
   265         }
   266     }
   268     /*
   269      ** initialize the categories
   270      */
   271     initCategories(&globals);
   273     return retval;
   274 }
   276 #if ST_WANT_GRAPHS
   277 /*
   278 ** createGraph
   279 **
   280 ** Create a GD image with the common properties of a graph.
   281 ** Upon return, you normally allocate legend colors,
   282 **  draw your graph inside the region
   283 **  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGH-STGD_MARGIN,
   284 **  and then call drawGraph to format the surrounding information.
   285 **
   286 ** You should use the normal GD image release function, gdImageDestroy
   287 **  when done with it.
   288 **
   289 ** Image attributes:
   290 **   STGD_WIDTHxSTGD_HEIGHT
   291 **   trasparent (white) background
   292 **   incremental display
   293 */
   294 gdImagePtr
   295 createGraph(int *aTransparencyColor)
   296 {
   297     gdImagePtr retval = NULL;
   299     if (NULL != aTransparencyColor) {
   300         *aTransparencyColor = -1;
   302         retval = gdImageCreate(STGD_WIDTH, STGD_HEIGHT);
   303         if (NULL != retval) {
   304             /*
   305              ** Background color (first one).
   306              */
   307             *aTransparencyColor = gdImageColorAllocate(retval, 255, 255, 255);
   308             if (-1 != *aTransparencyColor) {
   309                 /*
   310                  ** As transparency.
   311                  */
   312                 gdImageColorTransparent(retval, *aTransparencyColor);
   313             }
   315             /*
   316              ** And to set interlacing.
   317              */
   318             gdImageInterlace(retval, 1);
   319         }
   320         else {
   321             REPORT_ERROR(__LINE__, gdImageCreate);
   322         }
   323     }
   324     else {
   325         REPORT_ERROR(__LINE__, createGraph);
   326     }
   328     return retval;
   329 }
   330 #endif /* ST_WANT_GRAPHS */
   332 #if ST_WANT_GRAPHS
   333 /*
   334 ** drawGraph
   335 **
   336 ** This function mainly exists to simplify putitng all the pretty lace
   337 **  around a home made graph.
   338 */
   339 void
   340 drawGraph(gdImagePtr aImage, int aColor,
   341           const char *aGraphTitle,
   342           const char *aXAxisTitle,
   343           const char *aYAxisTitle,
   344           uint32_t aXMarkCount,
   345           uint32_t * aXMarkPercents,
   346           const char **aXMarkTexts,
   347           uint32_t aYMarkCount,
   348           uint32_t * aYMarkPercents,
   349           const char **aYMarkTexts,
   350           uint32_t aLegendCount,
   351           int *aLegendColors, const char **aLegendTexts)
   352 {
   353     if (NULL != aImage && NULL != aGraphTitle &&
   354         NULL != aXAxisTitle && NULL != aYAxisTitle &&
   355         (0 == aXMarkCount || (NULL != aXMarkPercents && NULL != aXMarkTexts))
   356         && (0 == aYMarkCount
   357             || (NULL != aYMarkPercents && NULL != aYMarkTexts))
   358         && (0 == aLegendCount
   359             || (NULL != aLegendColors && NULL != aLegendTexts))) {
   360         int margin = 1;
   361         uint32_t traverse = 0;
   362         uint32_t target = 0;
   363         const int markSize = 2;
   364         int x1 = 0;
   365         int y1 = 0;
   366         int x2 = 0;
   367         int y2 = 0;
   368         time_t theTimeT = time(NULL);
   369         char *theTime = ctime(&theTimeT);
   370         const char *logo = "SpaceTrace";
   371         gdFontPtr titleFont = gdFontMediumBold;
   372         gdFontPtr markFont = gdFontTiny;
   373         gdFontPtr dateFont = gdFontTiny;
   374         gdFontPtr axisFont = gdFontSmall;
   375         gdFontPtr legendFont = gdFontTiny;
   376         gdFontPtr logoFont = gdFontTiny;
   378         /*
   379          ** Fixup the color.
   380          ** Black by default.
   381          */
   382         if (-1 == aColor) {
   383             aColor = gdImageColorAllocate(aImage, 0, 0, 0);
   384         }
   385         if (-1 == aColor) {
   386             aColor = gdImageColorClosest(aImage, 0, 0, 0);
   387         }
   389         /*
   390          ** Output the box.
   391          */
   392         x1 = STGD_MARGIN - margin;
   393         y1 = STGD_MARGIN - margin;
   394         x2 = STGD_WIDTH - x1;
   395         y2 = STGD_HEIGHT - y1;
   396         gdImageRectangle(aImage, x1, y1, x2, y2, aColor);
   397         margin++;
   399         /*
   400          ** Need to make small markings on the graph to indicate where the
   401          **  labels line up exactly.
   402          ** While we're at it, draw the label text.
   403          */
   404         for (traverse = 0; traverse < aXMarkCount; traverse++) {
   405             target =
   406                 ((STGD_WIDTH -
   407                   (STGD_MARGIN * 2)) * aXMarkPercents[traverse]) / 100;
   409             x1 = STGD_MARGIN + target;
   410             y1 = STGD_MARGIN - margin;
   411             x2 = x1;
   412             y2 = y1 - markSize;
   413             gdImageLine(aImage, x1, y1, x2, y2, aColor);
   415             y1 = STGD_HEIGHT - y1;
   416             y2 = STGD_HEIGHT - y2;
   417             gdImageLine(aImage, x1, y1, x2, y2, aColor);
   419             if (NULL != aXMarkTexts[traverse]) {
   420                 x1 = STGD_MARGIN + target - (markFont->h / 2);
   421                 y1 = STGD_HEIGHT - STGD_MARGIN + margin + markSize +
   422                     (strlen(aXMarkTexts[traverse]) * markFont->w);
   423                 gdImageStringUp(aImage, markFont, x1, y1,
   424                                 (unsigned char *) aXMarkTexts[traverse],
   425                                 aColor);
   426             }
   427         }
   428         for (traverse = 0; traverse < aYMarkCount; traverse++) {
   429             target =
   430                 ((STGD_HEIGHT - (STGD_MARGIN * 2)) * (100 -
   431                                                       aYMarkPercents
   432                                                       [traverse])) / 100;
   434             x1 = STGD_MARGIN - margin;
   435             y1 = STGD_MARGIN + target;
   436             x2 = x1 - markSize;
   437             y2 = y1;
   438             gdImageLine(aImage, x1, y1, x2, y2, aColor);
   440             x1 = STGD_WIDTH - x1;
   441             x2 = STGD_WIDTH - x2;
   442             gdImageLine(aImage, x1, y1, x2, y2, aColor);
   444             if (NULL != aYMarkTexts[traverse]) {
   445                 x1 = STGD_MARGIN - margin - markSize -
   446                     (strlen(aYMarkTexts[traverse]) * markFont->w);
   447                 y1 = STGD_MARGIN + target - (markFont->h / 2);
   448                 gdImageString(aImage, markFont, x1, y1,
   449                               (unsigned char *) aYMarkTexts[traverse],
   450                               aColor);
   451             }
   452         }
   453         margin += markSize;
   455         /*
   456          ** Title will be centered above the image.
   457          */
   458         x1 = (STGD_WIDTH / 2) - ((strlen(aGraphTitle) * titleFont->w) / 2);
   459         y1 = ((STGD_MARGIN - margin) / 2) - (titleFont->h / 2);
   460         gdImageString(aImage, titleFont, x1, y1,
   461                       (unsigned char *) aGraphTitle, aColor);
   463         /*
   464          ** Upper left will be the date.
   465          */
   466         x1 = 0;
   467         y1 = 0;
   468         traverse = strlen(theTime) - 1;
   469         if (isspace(theTime[traverse])) {
   470             theTime[traverse] = '\0';
   471         }
   472         gdImageString(aImage, dateFont, x1, y1, (unsigned char *) theTime,
   473                       aColor);
   475         /*
   476          ** Lower right will be the logo.
   477          */
   478         x1 = STGD_WIDTH - (strlen(logo) * logoFont->w);
   479         y1 = STGD_HEIGHT - logoFont->h;
   480         gdImageString(aImage, logoFont, x1, y1, (unsigned char *) logo,
   481                       aColor);
   483         /*
   484          ** X and Y axis titles
   485          */
   486         x1 = (STGD_WIDTH / 2) - ((strlen(aXAxisTitle) * axisFont->w) / 2);
   487         y1 = STGD_HEIGHT - axisFont->h;
   488         gdImageString(aImage, axisFont, x1, y1, (unsigned char *) aXAxisTitle,
   489                       aColor);
   490         x1 = 0;
   491         y1 = (STGD_HEIGHT / 2) + ((strlen(aYAxisTitle) * axisFont->w) / 2);
   492         gdImageStringUp(aImage, axisFont, x1, y1,
   493                         (unsigned char *) aYAxisTitle, aColor);
   495         /*
   496          ** The legend.
   497          ** Centered on the right hand side, going up.
   498          */
   499         x1 = STGD_WIDTH - STGD_MARGIN + margin +
   500             (aLegendCount * legendFont->h) / 2;
   501         x2 = STGD_WIDTH - (aLegendCount * legendFont->h);
   502         if (x1 > x2) {
   503             x1 = x2;
   504         }
   506         y1 = 0;
   507         for (traverse = 0; traverse < aLegendCount; traverse++) {
   508             y2 = (STGD_HEIGHT / 2) +
   509                 ((strlen(aLegendTexts[traverse]) * legendFont->w) / 2);
   510             if (y2 > y1) {
   511                 y1 = y2;
   512             }
   513         }
   514         for (traverse = 0; traverse < aLegendCount; traverse++) {
   515             gdImageStringUp(aImage, legendFont, x1, y1,
   516                             (unsigned char *) aLegendTexts[traverse],
   517                             aLegendColors[traverse]);
   518             x1 += legendFont->h;
   519         }
   520     }
   521 }
   523 #endif /* ST_WANT_GRAPHS */
   525 #if defined(HAVE_BOUTELL_GD)
   526 /*
   527 ** pngSink
   528 **
   529 ** GD callback, used to write out the png.
   530 */
   531 int
   532 pngSink(void *aContext, const char *aBuffer, int aLen)
   533 {
   534     return PR_Write((PRFileDesc *) aContext, aBuffer, aLen);
   535 }
   536 #endif /* HAVE_BOUTELL_GD */
   538 /*
   539 ** FormatNumber
   540 **
   541 ** Formats a number with thousands separator. Don't free the result. Returns
   542 ** static data.
   543 */
   544 char *
   545 FormatNumber(int32_t num)
   546 {
   547     static char buf[64];
   548     char tmpbuf[64];
   549     int len = 0;
   550     int bufindex = sizeof(buf) - 1;
   551     int mod3;
   553     PR_snprintf(tmpbuf, sizeof(tmpbuf), "%d", num);
   555     /* now insert the thousands separator */
   556     mod3 = 0;
   557     len = strlen(tmpbuf);
   558     while (len >= 0) {
   559         if (tmpbuf[len] >= '0' && tmpbuf[len] <= '9') {
   560             if (mod3 == 3) {
   561                 buf[bufindex--] = ',';
   562                 mod3 = 0;
   563             }
   564             mod3++;
   565         }
   566         buf[bufindex--] = tmpbuf[len--];
   567     }
   568     return buf + bufindex + 1;
   569 }
   571 /*
   572 ** actualByteSize
   573 **
   574 ** Apply alignment and overhead to size to figure out actual byte size
   575 */
   576 uint32_t
   577 actualByteSize(STOptions * inOptions, uint32_t retval)
   578 {
   579     /*
   580      ** Need to bump the result by our alignment and overhead.
   581      ** The idea here is that an allocation actually costs you more than you
   582      **  thought.
   583      **
   584      ** The msvcrt malloc has an alignment of 16 with an overhead of 8.
   585      ** The win32 HeapAlloc has an alignment of 8 with an overhead of 8.
   586      */
   587     if (0 != retval) {
   588         uint32_t eval = 0;
   589         uint32_t over = 0;
   591         eval = retval - 1;
   592         if (0 != inOptions->mAlignBy) {
   593             over = eval % inOptions->mAlignBy;
   594         }
   595         retval = eval + inOptions->mOverhead + inOptions->mAlignBy - over;
   596     }
   598     return retval;
   599 }
   601 /*
   602 ** byteSize
   603 **
   604 ** Figuring the byte size of an allocation.
   605 ** Might expand in the future to report size at a given time.
   606 ** For now, just use last relevant event.
   607 */
   608 uint32_t
   609 byteSize(STOptions * inOptions, STAllocation * aAlloc)
   610 {
   611     uint32_t retval = 0;
   613     if (NULL != aAlloc && 0 != aAlloc->mEventCount) {
   614         uint32_t index = aAlloc->mEventCount;
   616         /*
   617          ** Generally, the size is the last event's size.
   618          */
   619         do {
   620             index--;
   621             retval = aAlloc->mEvents[index].mHeapSize;
   622         }
   623         while (0 == retval && 0 != index);
   624     }
   625     return actualByteSize(inOptions, retval);
   626 }
   629 /*
   630 ** recalculateAllocationCost
   631 **
   632 ** Given an allocation, does a recalculation of Cost - weight, heapcount etc.
   633 ** and does the right thing to propagate the cost upwards.
   634 */
   635 int
   636 recalculateAllocationCost(STOptions * inOptions, STContext * inContext,
   637                           STRun * aRun, STAllocation * aAllocation,
   638                           PRBool updateParent)
   639 {
   640     /*
   641      ** Now, see if they desire a callsite update.
   642      ** As mentioned previously, we decide if the run desires us to
   643      **  manipulate the callsite data only if its stamp is set.
   644      ** We change all callsites and parent callsites to have that
   645      **  stamp as well, so as to mark them as being relevant to
   646      **  the current run in question.
   647      */
   648     if (NULL != inContext && 0 != aRun->mStats[inContext->mIndex].mStamp) {
   649         uint32_t timeval =
   650             aAllocation->mMaxTimeval - aAllocation->mMinTimeval;
   651         uint32_t size = byteSize(inOptions, aAllocation);
   652         uint32_t heapCost = aAllocation->mHeapRuntimeCost;
   653         uint64_t timeval64 = timeval;
   654         uint64_t size64 = size;
   655         uint64_t weight64 = timeval64 * size64;
   657         /*
   658          ** First, update this run.
   659          */
   660         aRun->mStats[inContext->mIndex].mCompositeCount++;
   661         aRun->mStats[inContext->mIndex].mHeapRuntimeCost += heapCost;
   662         aRun->mStats[inContext->mIndex].mSize += size;
   663         aRun->mStats[inContext->mIndex].mTimeval64 += timeval64;
   664         aRun->mStats[inContext->mIndex].mWeight64 += weight64;
   666         /*
   667          ** Use the first event of the allocation to update the parent
   668          **  callsites.
   669          ** This has positive effect of not updating realloc callsites
   670          **  with the same data over and over again.
   671          */
   672         if (updateParent && 0 < aAllocation->mEventCount) {
   673             tmcallsite *callsite = aAllocation->mEvents[0].mCallsite;
   674             STRun *callsiteRun = NULL;
   676             /*
   677              ** Go up parents till we drop.
   678              */
   679             while (NULL != callsite && NULL != callsite->method) {
   680                 callsiteRun = CALLSITE_RUN(callsite);
   681                 if (NULL != callsiteRun) {
   682                     /*
   683                      ** Do we init it?
   684                      */
   685                     if (callsiteRun->mStats[inContext->mIndex].mStamp !=
   686                         aRun->mStats[inContext->mIndex].mStamp) {
   687                         memset(&callsiteRun->mStats[inContext->mIndex], 0,
   688                                sizeof(STCallsiteStats));
   689                         callsiteRun->mStats[inContext->mIndex].mStamp =
   690                             aRun->mStats[inContext->mIndex].mStamp;
   691                     }
   693                     /*
   694                      ** Add the values.
   695                      ** Note that if the allocation was ever realloced,
   696                      **  we are actually recording the final size.
   697                      ** Also, the composite count does not include
   698                      **  calls to realloc (or free for that matter),
   699                      **  but rather is simply a count of actual heap
   700                      **  allocation objects, from which someone will
   701                      **  draw conclusions regarding number of malloc
   702                      **  and free calls.
   703                      ** It is possible to generate the exact number
   704                      **  of calls to free/malloc/realloc should the
   705                      **  absolute need arise to count them individually,
   706                      **  but I fear it will take mucho memory and this
   707                      **  is perhaps good enough for now.
   708                      */
   709                     callsiteRun->mStats[inContext->mIndex].mCompositeCount++;
   710                     callsiteRun->mStats[inContext->mIndex].mHeapRuntimeCost +=
   711                         heapCost;
   712                     callsiteRun->mStats[inContext->mIndex].mSize += size;
   713                     callsiteRun->mStats[inContext->mIndex].mTimeval64 +=
   714                         timeval64;
   715                     callsiteRun->mStats[inContext->mIndex].mWeight64 +=
   716                         weight64;
   717                 }
   719                 callsite = callsite->parent;
   720             }
   721         }
   722     }
   724     return 0;
   725 }
   728 /*
   729 ** appendAllocation
   730 **
   731 ** Given a run, append the allocation to it.
   732 ** No DUP checks are done.
   733 ** Also, we might want to update the parent callsites with stats.
   734 ** We decide to do this heavy duty work only if the run we are appending
   735 **  to has a non ZERO mStats[].mStamp, meaning that it is asking to track
   736 **  such information when it was created.
   737 ** Returns !0 on success.
   738 */
   739 int
   740 appendAllocation(STOptions * inOptions, STContext * inContext,
   741                  STRun * aRun, STAllocation * aAllocation)
   742 {
   743     int retval = 0;
   745     if (NULL != aRun && NULL != aAllocation && NULL != inOptions) {
   746         STAllocation **expand = NULL;
   748         /*
   749          ** Expand the size of the array if needed.
   750          */
   751         expand = (STAllocation **) realloc(aRun->mAllocations,
   752                                            sizeof(STAllocation *) *
   753                                            (aRun->mAllocationCount + 1));
   754         if (NULL != expand) {
   755             /*
   756              ** Reassign in case of pointer move.
   757              */
   758             aRun->mAllocations = expand;
   760             /*
   761              ** Stick the allocation in.
   762              */
   763             aRun->mAllocations[aRun->mAllocationCount] = aAllocation;
   765             /*
   766              ** If this is the global run, we need to let the allocation
   767              **  track the index back to us.
   768              */
   769             if (&globals.mRun == aRun) {
   770                 aAllocation->mRunIndex = aRun->mAllocationCount;
   771             }
   773             /*
   774              ** Increase the count.
   775              */
   776             aRun->mAllocationCount++;
   778             /*
   779              ** We're good.
   780              */
   781             retval = __LINE__;
   783             /*
   784              ** update allocation cost
   785              */
   786             recalculateAllocationCost(inOptions, inContext, aRun, aAllocation,
   787                                       PR_TRUE);
   788         }
   789         else {
   790             REPORT_ERROR(__LINE__, appendAllocation);
   791         }
   792     }
   793     else {
   794         REPORT_ERROR(__LINE__, appendAllocation);
   795     }
   797     return retval;
   798 }
   800 /*
   801 ** hasCallsiteMatch
   802 **
   803 ** Determine if the callsite or the other callsites has the matching text.
   804 **
   805 ** Returns 0 if there is no match.
   806 */
   807 int
   808 hasCallsiteMatch(tmcallsite * aCallsite, const char *aMatch, int aDirection)
   809 {
   810     int retval = 0;
   812     if (NULL != aCallsite && NULL != aCallsite->method &&
   813         NULL != aMatch && '\0' != *aMatch) {
   814         const char *methodName = NULL;
   816         do {
   817             methodName = tmmethodnode_name(aCallsite->method);
   818             if (NULL != methodName && NULL != strstr(methodName, aMatch)) {
   819                 /*
   820                  ** Contains the text.
   821                  */
   822                 retval = __LINE__;
   823                 break;
   824             }
   825             else {
   826                 switch (aDirection) {
   827                 case ST_FOLLOW_SIBLINGS:
   828                     aCallsite = aCallsite->siblings;
   829                     break;
   830                 case ST_FOLLOW_PARENTS:
   831                     aCallsite = aCallsite->parent;
   832                     break;
   833                 default:
   834                     aCallsite = NULL;
   835                     REPORT_ERROR(__LINE__, hasCallsiteMatch);
   836                     break;
   837                 }
   838             }
   839         }
   840         while (NULL != aCallsite && NULL != aCallsite->method);
   841     }
   842     else {
   843         REPORT_ERROR(__LINE__, hasCallsiteMatch);
   844     }
   846     return retval;
   847 }
   849 /*
   850 ** harvestRun
   851 **
   852 ** Provide a simply way to go over a run, and yield the relevant allocations.
   853 ** The restrictions are easily set via the options page or the command
   854 **  line switches.
   855 **
   856 ** On any match, add the allocation to the provided run.
   857 **
   858 ** This makes it much easier for all the code to respect the options in
   859 **  force.
   860 **
   861 ** Returns !0 on error, though aOutRun may contain a partial data set.
   862 */
   863 int
   864 harvestRun(const STRun * aInRun, STRun * aOutRun,
   865            STOptions * aOptions, STContext * inContext)
   866 {
   867     int retval = 0;
   869 #if defined(DEBUG_dp)
   870     PRIntervalTime start = PR_IntervalNow();
   872     fprintf(stderr, "DEBUG: harvesting run...\n");
   873 #endif
   875     if (NULL != aInRun && NULL != aOutRun && aInRun != aOutRun
   876         && NULL != aOptions && NULL != inContext) {
   877         uint32_t traverse = 0;
   878         STAllocation *current = NULL;
   880         for (traverse = 0;
   881              0 == retval && traverse < aInRun->mAllocationCount; traverse++) {
   882             current = aInRun->mAllocations[traverse];
   883             if (NULL != current) {
   884                 uint32_t lifetime = 0;
   885                 uint32_t bytesize = 0;
   886                 uint64_t weight64 = 0;
   887                 uint64_t bytesize64 = 0;
   888                 uint64_t lifetime64 = 0;
   889                 int appendRes = 0;
   890                 int looper = 0;
   891                 PRBool matched = PR_FALSE;
   893                 /*
   894                  ** Use this as an opportune time to fixup a memory
   895                  **  leaked timeval, so as to not completely skew
   896                  **  the weights.
   897                  */
   898                 if (ST_TIMEVAL_MAX == current->mMaxTimeval) {
   899                     current->mMaxTimeval = globals.mMaxTimeval;
   900                 }
   902                 /*
   903                  ** Check allocation timeval restrictions.
   904                  ** We have to slide the recorded timevals to be zero
   905                  **  based, so that the comparisons make sense.
   906                  */
   907                 if ((aOptions->mAllocationTimevalMin >
   908                      (current->mMinTimeval - globals.mMinTimeval)) ||
   909                     (aOptions->mAllocationTimevalMax <
   910                      (current->mMinTimeval - globals.mMinTimeval))) {
   911                     continue;
   912                 }
   914                 /*
   915                  ** Check timeval restrictions.
   916                  ** We have to slide the recorded timevals to be zero
   917                  **  based, so that the comparisons make sense.
   918                  */
   919                 if ((aOptions->mTimevalMin >
   920                      (current->mMinTimeval - globals.mMinTimeval)) ||
   921                     (aOptions->mTimevalMax <
   922                      (current->mMinTimeval - globals.mMinTimeval))) {
   923                     continue;
   924                 }
   926                 /*
   927                  ** Check lifetime restrictions.
   928                  */
   929                 lifetime = current->mMaxTimeval - current->mMinTimeval;
   930                 if ((lifetime < aOptions->mLifetimeMin) ||
   931                     (lifetime > aOptions->mLifetimeMax)) {
   932                     continue;
   933                 }
   935                 /*
   936                  ** Check byte size restrictions.
   937                  */
   938                 bytesize = byteSize(aOptions, current);
   939                 if ((bytesize < aOptions->mSizeMin) ||
   940                     (bytesize > aOptions->mSizeMax)) {
   941                     continue;
   942                 }
   944                 /*
   945                  ** Check weight restrictions.
   946                  */
   947                 weight64 = (uint64_t)(bytesize * lifetime);
   948                 if (weight64 < aOptions->mWeightMin64 ||
   949                     weight64 > aOptions->mWeightMax64) {
   950                     continue;
   951                 }
   953                 /*
   954                  ** Possibly restrict the callsite by text.
   955                  ** Do this last, as it is a heavier check.
   956                  **
   957                  ** One day, we may need to expand the logic to check for
   958                  **  events beyond the initial allocation event.
   959                  */
   960                 for (looper = 0; ST_SUBSTRING_MATCH_MAX > looper; looper++) {
   961                     if ('\0' != aOptions->mRestrictText[looper][0]) {
   962                         if (0 ==
   963                             hasCallsiteMatch(current->mEvents[0].mCallsite,
   964                                              aOptions->mRestrictText[looper],
   965                                              ST_FOLLOW_PARENTS)) {
   966                             break;
   967                         }
   968                     }
   969                     else {
   970                         matched = PR_TRUE;
   971                         break;
   972                     }
   973                 }
   974                 if (ST_SUBSTRING_MATCH_MAX == looper) {
   975                     matched = PR_TRUE;
   976                 }
   977                 if (PR_FALSE == matched) {
   978                     continue;
   979                 }
   981                 /*
   982                  ** You get here, we add to the run.
   983                  */
   984                 appendRes =
   985                     appendAllocation(aOptions, inContext, aOutRun, current);
   986                 if (0 == appendRes) {
   987                     retval = __LINE__;
   988                     REPORT_ERROR(__LINE__, appendAllocation);
   989                 }
   990             }
   991         }
   992     }
   994 #if defined(DEBUG_dp)
   995     fprintf(stderr, "DEBUG: harvesting ends: %dms [%d allocations]\n",
   996             PR_IntervalToMilliseconds(PR_IntervalNow() - start),
   997             aInRun->mAllocationCount);
   998 #endif
   999     return retval;
  1002 /*
  1003 ** recalculateRunCost
  1004 **
  1005 ** Goes over all allocations of a run and recalculates and propagates
  1006 ** the allocation costs - weight, heapcount, size
  1007 */
  1008 int
  1009 recalculateRunCost(STOptions * inOptions, STContext * inContext, STRun * aRun)
  1011     uint32_t traverse = 0;
  1012     STAllocation *current = NULL;
  1014 #if defined(DEBUG_dp)
  1015     PRIntervalTime start = PR_IntervalNow();
  1017     fprintf(stderr, "DEBUG: recalculateRunCost...\n");
  1018 #endif
  1020     if (NULL == aRun)
  1021         return -1;
  1023     /* reset stats of this run to 0 to begin recalculation */
  1024     memset(&aRun->mStats[inContext->mIndex], 0, sizeof(STCallsiteStats));
  1026     /* reset timestamp to force propogation of cost */
  1027     aRun->mStats[inContext->mIndex].mStamp = PR_IntervalNow();
  1029     for (traverse = 0; traverse < aRun->mAllocationCount; traverse++) {
  1030         current = aRun->mAllocations[traverse];
  1031         if (NULL != current) {
  1032             recalculateAllocationCost(inOptions, inContext, aRun, current,
  1033                                       PR_TRUE);
  1037 #if defined(DEBUG_dp)
  1038     fprintf(stderr, "DEBUG: recalculateRunCost ends: %dms [%d allocations]\n",
  1039             PR_IntervalToMilliseconds(PR_IntervalNow() - start),
  1040             aRun->mAllocationCount);
  1041 #endif
  1043     return 0;
  1047 /*
  1048 ** compareAllocations
  1049 **
  1050 ** qsort callback.
  1051 ** Compare the allocations as specified by the options.
  1052 */
  1053 int
  1054 compareAllocations(const void *aAlloc1, const void *aAlloc2, void *aContext)
  1056     int retval = 0;
  1057     STOptions *inOptions = (STOptions *) aContext;
  1059     if (NULL != aAlloc1 && NULL != aAlloc2 && NULL != inOptions) {
  1060         STAllocation *alloc1 = *((STAllocation **) aAlloc1);
  1061         STAllocation *alloc2 = *((STAllocation **) aAlloc2);
  1063         if (NULL != alloc1 && NULL != alloc2) {
  1064             /*
  1065              ** Logic determined by pref/option.
  1066              */
  1067             switch (inOptions->mOrderBy) {
  1068             case ST_COUNT:
  1069                 /*
  1070                  ** "By count" on a single allocation means nothing,
  1071                  **  fall through to weight.
  1072                  */
  1073             case ST_WEIGHT:
  1075                     uint64_t weight164 = 0;
  1076                     uint64_t weight264 = 0;
  1077                     uint64_t bytesize164 = 0;
  1078                     uint64_t bytesize264 = 0;
  1079                     uint64_t timeval164 = 0;
  1080                     uint64_t timeval264 = 0;
  1082                     bytesize164 = byteSize(inOptions, alloc1);
  1083                     timeval164 = alloc1->mMaxTimeval - alloc1->mMinTimeval;
  1084                     weight164 = bytesize164 * timeval164;
  1085                     bytesize264 = byteSize(inOptions, alloc2);
  1086                     timeval264 = alloc2->mMaxTimeval - alloc2->mMinTimeval;
  1087                     weight264 = bytesize264 * timeval264;
  1089                     if (weight164 < weight264) {
  1090                         retval = __LINE__;
  1092                     else if (weight164 > weight264) {
  1093                         retval = -__LINE__;
  1096                 break;
  1098             case ST_SIZE:
  1100                     uint32_t size1 = byteSize(inOptions, alloc1);
  1101                     uint32_t size2 = byteSize(inOptions, alloc2);
  1103                     if (size1 < size2) {
  1104                         retval = __LINE__;
  1106                     else if (size1 > size2) {
  1107                         retval = -__LINE__;
  1110                 break;
  1112             case ST_TIMEVAL:
  1114                     uint32_t timeval1 =
  1115                         (alloc1->mMaxTimeval - alloc1->mMinTimeval);
  1116                     uint32_t timeval2 =
  1117                         (alloc2->mMaxTimeval - alloc2->mMinTimeval);
  1119                     if (timeval1 < timeval2) {
  1120                         retval = __LINE__;
  1122                     else if (timeval1 > timeval2) {
  1123                         retval = -__LINE__;
  1126                 break;
  1128             case ST_HEAPCOST:
  1130                     uint32_t cost1 = alloc1->mHeapRuntimeCost;
  1131                     uint32_t cost2 = alloc2->mHeapRuntimeCost;
  1133                     if (cost1 < cost2) {
  1134                         retval = __LINE__;
  1136                     else if (cost1 > cost2) {
  1137                         retval = -__LINE__;
  1140                 break;
  1142             default:
  1144                     REPORT_ERROR(__LINE__, compareAllocations);
  1146                 break;
  1151     return retval;
  1154 /*
  1155 ** sortRun
  1156 **
  1157 ** Given a run, sort it in the manner specified by the options.
  1158 ** Returns !0 on failure.
  1159 */
  1160 int
  1161 sortRun(STOptions * inOptions, STRun * aRun)
  1163     int retval = 0;
  1165     if (NULL != aRun && NULL != inOptions) {
  1166         if (NULL != aRun->mAllocations && 0 < aRun->mAllocationCount) {
  1167             NS_QuickSort(aRun->mAllocations, aRun->mAllocationCount,
  1168                          sizeof(STAllocation *), compareAllocations,
  1169                          inOptions);
  1172     else {
  1173         retval = __LINE__;
  1174         REPORT_ERROR(__LINE__, sortRun);
  1177     return retval;
  1180 /*
  1181 ** createRun
  1182 **
  1183 ** Returns a newly allocated run, properly initialized.
  1184 ** Must call freeRun() with the new STRun.
  1185 **
  1186 ** ONLY PASS IN A NON_ZERO STAMP IF YOU KNOW WHAT YOU ARE DOING!!!
  1187 ** A non zero stamp in a run has side effects all over the
  1188 **  callsites of the allocations added to the run and their
  1189 **  parents.
  1190 **
  1191 ** Returns NULL on failure.
  1192 */
  1193 STRun *
  1194 createRun(STContext * inContext, uint32_t aStamp)
  1196     STRun *retval = NULL;
  1198     retval = (STRun *) calloc(1, sizeof(STRun));
  1199     if (NULL != retval) {
  1200         retval->mStats =
  1201             (STCallsiteStats *) calloc(globals.mCommandLineOptions.mContexts,
  1202                                        sizeof(STCallsiteStats));
  1203         if (NULL != retval->mStats) {
  1204             if (NULL != inContext) {
  1205                 retval->mStats[inContext->mIndex].mStamp = aStamp;
  1208         else {
  1209             free(retval);
  1210             retval = NULL;
  1214     return retval;
  1217 /*
  1218 ** freeRun
  1219 **
  1220 ** Free off the run and the associated data.
  1221 */
  1222 void
  1223 freeRun(STRun * aRun)
  1225     if (NULL != aRun) {
  1226         if (NULL != aRun->mAllocations) {
  1227             /*
  1228              ** We do not free the allocations themselves.
  1229              ** They are likely pointed to by at least 2 other existing
  1230              **  runs.
  1231              */
  1232             free(aRun->mAllocations);
  1233             aRun->mAllocations = NULL;
  1236         if (NULL != aRun->mStats) {
  1237             free(aRun->mStats);
  1238             aRun->mStats = NULL;
  1241         free(aRun);
  1242         aRun = NULL;
  1246 /*
  1247 ** createRunFromGlobal
  1248 **
  1249 ** Harvest the global run, then sort it.
  1250 ** Returns NULL on failure.
  1251 ** Must call freeRun() with the new STRun.
  1252 */
  1253 STRun *
  1254 createRunFromGlobal(STOptions * inOptions, STContext * inContext)
  1256     STRun *retval = NULL;
  1258     if (NULL != inOptions && NULL != inContext) {
  1259         /*
  1260          ** We stamp the run.
  1261          ** As things are appended to it, it realizes that it should stamp the
  1262          **  callsite backtrace with the information as well.
  1263          ** In this manner, we can provide meaningful callsite data.
  1264          */
  1265         retval = createRun(inContext, PR_IntervalNow());
  1267         if (NULL != retval) {
  1268             STCategoryNode *node = NULL;
  1269             int failure = 0;
  1270             int harvestRes =
  1271                 harvestRun(&globals.mRun, retval, inOptions, inContext);
  1272             if (0 == harvestRes) {
  1273                 int sortRes = sortRun(inOptions, retval);
  1275                 if (0 != sortRes) {
  1276                     failure = __LINE__;
  1279             else {
  1280                 failure = __LINE__;
  1284             if (0 != failure) {
  1285                 freeRun(retval);
  1286                 retval = NULL;
  1288                 REPORT_ERROR(failure, createRunFromGlobal);
  1291             /*
  1292              ** Categorize the run.
  1293              */
  1294             failure = categorizeRun(inOptions, inContext, retval, &globals);
  1295             if (0 != failure) {
  1296                 REPORT_ERROR(__LINE__, categorizeRun);
  1299             /*
  1300              ** if we are focussing on a category, return that run instead of
  1301              ** the harvested run. Make sure to recalculate cost.
  1302              */
  1303             node = findCategoryNode(inOptions->mCategoryName, &globals);
  1304             if (node) {
  1305                 /* Recalculate cost of run */
  1306                 recalculateRunCost(inOptions, inContext,
  1307                                    node->runs[inContext->mIndex]);
  1309                 retval = node->runs[inContext->mIndex];
  1313     else {
  1314         REPORT_ERROR(__LINE__, createRunFromGlobal);
  1317     return retval;
  1320 /*
  1321 ** getLiveAllocationByHeapID
  1322 **
  1323 ** Go through a run and find the right heap ID.
  1324 ** At the time of the call to this function, the allocation must be LIVE,
  1325 **   meaning that it can not be freed.
  1326 ** Go through the run backwards, in hopes of finding it near the end.
  1327 **
  1328 ** Returns the allocation on success, otherwise NULL.
  1329 */
  1330 STAllocation *
  1331 getLiveAllocationByHeapID(STRun * aRun, uint32_t aHeapID)
  1333     STAllocation *retval = NULL;
  1335     if (NULL != aRun && 0 != aHeapID) {
  1336         uint32_t traverse = aRun->mAllocationCount;
  1337         STAllocation *eval = NULL;
  1339         /*
  1340          ** Go through in reverse order.
  1341          ** Stop when we have a return value.
  1342          */
  1343         while (0 < traverse && NULL == retval) {
  1344             /*
  1345              ** Back up one to align with zero based index.
  1346              */
  1347             traverse--;
  1349             /*
  1350              ** Take the pointer math out of further operations.
  1351              */
  1352             eval = aRun->mAllocations[traverse];
  1354             /*
  1355              ** Take a look at the events in reverse order.
  1356              ** Basically the last event must NOT be a free.
  1357              ** The last event must NOT be a realloc of size zero (free).
  1358              ** Otherwise, try to match up the heapID of the event.
  1359              */
  1360             if (0 != eval->mEventCount) {
  1361                 STAllocEvent *event = eval->mEvents + (eval->mEventCount - 1);
  1363                 switch (event->mEventType) {
  1364                 case TM_EVENT_FREE:
  1366                         /*
  1367                          ** No freed allocation can match.
  1368                          */
  1370                     break;
  1372                 case TM_EVENT_REALLOC:
  1373                 case TM_EVENT_CALLOC:
  1374                 case TM_EVENT_MALLOC:
  1376                         /*
  1377                          ** Heap IDs must match.
  1378                          */
  1379                         if (aHeapID == event->mHeapID) {
  1380                             retval = eval;
  1383                     break;
  1385                 default:
  1387                         REPORT_ERROR(__LINE__, getAllocationByHeapID);
  1389                     break;
  1394     else {
  1395         REPORT_ERROR(__LINE__, getAllocationByHeapID);
  1398     return retval;
  1401 /*
  1402 ** appendEvent
  1403 **
  1404 ** Given an allocation, append a new event to its lifetime.
  1405 ** Returns the new event on success, otherwise NULL.
  1406 */
  1407 STAllocEvent *
  1408 appendEvent(STAllocation * aAllocation, uint32_t aTimeval, char aEventType,
  1409             uint32_t aHeapID, uint32_t aHeapSize, tmcallsite * aCallsite)
  1411     STAllocEvent *retval = NULL;
  1413     if (NULL != aAllocation && NULL != aCallsite) {
  1414         STAllocEvent *expand = NULL;
  1416         /*
  1417          ** Expand the allocation's event array.
  1418          */
  1419         expand =
  1420             (STAllocEvent *) realloc(aAllocation->mEvents,
  1421                                      sizeof(STAllocEvent) *
  1422                                      (aAllocation->mEventCount + 1));
  1423         if (NULL != expand) {
  1424             /*
  1425              ** Reassign in case of pointer move.
  1426              */
  1427             aAllocation->mEvents = expand;
  1429             /*
  1430              ** Remove the pointer math from rest of code.
  1431              */
  1432             retval = aAllocation->mEvents + aAllocation->mEventCount;
  1434             /*
  1435              ** Increase event array count.
  1436              */
  1437             aAllocation->mEventCount++;
  1439             /*
  1440              ** Fill in the event.
  1441              */
  1442             retval->mTimeval = aTimeval;
  1443             retval->mEventType = aEventType;
  1444             retval->mHeapID = aHeapID;
  1445             retval->mHeapSize = aHeapSize;
  1446             retval->mCallsite = aCallsite;
  1448             /*
  1449              ** Allocation may need to update idea of lifetime.
  1450              ** See allocationTracker to see mMinTimeval inited to ST_TIMEVAL_MAX.
  1451              */
  1452             if (aAllocation->mMinTimeval > aTimeval) {
  1453                 aAllocation->mMinTimeval = aTimeval;
  1456             /*
  1457              ** This a free event?
  1458              ** Can only set max timeval on a free.
  1459              ** Otherwise, mMaxTimeval remains  ST_TIMEVAL_MAX.
  1460              ** Set in allocationTracker.
  1461              */
  1462             if (TM_EVENT_FREE == aEventType) {
  1463                 aAllocation->mMaxTimeval = aTimeval;
  1466         else {
  1467             REPORT_ERROR(__LINE__, appendEvent);
  1470     else {
  1471         REPORT_ERROR(__LINE__, appendEvent);
  1474     return retval;
  1477 /*
  1478 ** hasAllocation
  1479 **
  1480 ** Determine if a given run has an allocation.
  1481 ** This is really nothing more than a pointer comparison loop.
  1482 ** Returns !0 if the run has the allocation.
  1483 */
  1484 int
  1485 hasAllocation(STRun * aRun, STAllocation * aTestFor)
  1487     int retval = 0;
  1489     if (NULL != aRun && NULL != aTestFor) {
  1490         uint32_t traverse = aRun->mAllocationCount;
  1492         /*
  1493          ** Go through reverse, in the hopes it exists nearer the end.
  1494          */
  1495         while (0 < traverse) {
  1496             /*
  1497              ** Back up.
  1498              */
  1499             traverse--;
  1501             if (aTestFor == aRun->mAllocations[traverse]) {
  1502                 retval = __LINE__;
  1503                 break;
  1507     else {
  1508         REPORT_ERROR(__LINE__, hasAllocation);
  1511     return retval;
  1514 /*
  1515 ** allocationTracker
  1516 **
  1517 ** Important to keep track of all allocations unique so as to determine
  1518 **  their lifetimes.
  1519 **
  1520 ** Returns a pointer to the allocation on success.
  1521 ** Return NULL on failure.
  1522 */
  1523 STAllocation *
  1524 allocationTracker(uint32_t aTimeval, char aType, uint32_t aHeapRuntimeCost,
  1525                   tmcallsite * aCallsite, uint32_t aHeapID, uint32_t aSize,
  1526                   tmcallsite * aOldCallsite, uint32_t aOldHeapID,
  1527                   uint32_t aOldSize)
  1529     STAllocation *retval = NULL;
  1530     static int compactor = 1;
  1531     const int frequency = 10000;
  1532     uint32_t actualSize, actualOldSize = 0;
  1534     actualSize = actualByteSize(&globals.mCommandLineOptions, aSize);
  1535     if (aOldSize)
  1536         actualOldSize =
  1537             actualByteSize(&globals.mCommandLineOptions, aOldSize);
  1539     if (NULL != aCallsite) {
  1540         int newAllocation = 0;
  1541         tmcallsite *searchCallsite = NULL;
  1542         uint32_t searchHeapID = 0;
  1543         STAllocation *allocation = NULL;
  1545         /*
  1546          ** Global operation ID increases.
  1547          */
  1548         globals.mOperationCount++;
  1550         /*
  1551          ** Fix up the timevals if needed.
  1552          */
  1553         if (aTimeval < globals.mMinTimeval) {
  1554             globals.mMinTimeval = aTimeval;
  1556         if (aTimeval > globals.mMaxTimeval) {
  1557             globals.mMaxTimeval = aTimeval;
  1560         switch (aType) {
  1561         case TM_EVENT_FREE:
  1563                 /*
  1564                  ** Update the global counter.
  1565                  */
  1566                 globals.mFreeCount++;
  1568                 /*
  1569                  ** Update our peak memory used counter
  1570                  */
  1571                 globals.mMemoryUsed -= actualSize;
  1573                 /*
  1574                  ** Not a new allocation, will need to search passed in site
  1575                  **  for the original allocation.
  1576                  */
  1577                 searchCallsite = aCallsite;
  1578                 searchHeapID = aHeapID;
  1580             break;
  1582         case TM_EVENT_MALLOC:
  1584                 /*
  1585                  ** Update the global counter.
  1586                  */
  1587                 globals.mMallocCount++;
  1589                 /*
  1590                  ** Update our peak memory used counter
  1591                  */
  1592                 globals.mMemoryUsed += actualSize;
  1593                 if (globals.mMemoryUsed > globals.mPeakMemoryUsed) {
  1594                     globals.mPeakMemoryUsed = globals.mMemoryUsed;
  1597                 /*
  1598                  ** This will be a new allocation.
  1599                  */
  1600                 newAllocation = __LINE__;
  1602             break;
  1604         case TM_EVENT_CALLOC:
  1606                 /*
  1607                  ** Update the global counter.
  1608                  */
  1609                 globals.mCallocCount++;
  1611                 /*
  1612                  ** Update our peak memory used counter
  1613                  */
  1614                 globals.mMemoryUsed += actualSize;
  1615                 if (globals.mMemoryUsed > globals.mPeakMemoryUsed) {
  1616                     globals.mPeakMemoryUsed = globals.mMemoryUsed;
  1619                 /*
  1620                  ** This will be a new allocation.
  1621                  */
  1622                 newAllocation = __LINE__;
  1624             break;
  1626         case TM_EVENT_REALLOC:
  1628                 /*
  1629                  ** Update the global counter.
  1630                  */
  1631                 globals.mReallocCount++;
  1633                 /*
  1634                  ** Update our peak memory used counter
  1635                  */
  1636                 globals.mMemoryUsed += actualSize - actualOldSize;
  1637                 if (globals.mMemoryUsed > globals.mPeakMemoryUsed) {
  1638                     globals.mPeakMemoryUsed = globals.mMemoryUsed;
  1641                 /*
  1642                  ** This might be a new allocation.
  1643                  */
  1644                 if (NULL == aOldCallsite) {
  1645                     newAllocation = __LINE__;
  1647                 else {
  1648                     /*
  1649                      ** Need to search for the original callsite for the
  1650                      **  index to the allocation.
  1651                      */
  1652                     searchCallsite = aOldCallsite;
  1653                     searchHeapID = aOldHeapID;
  1656             break;
  1658         default:
  1660                 REPORT_ERROR(__LINE__, allocationTracker);
  1662             break;
  1665         /*
  1666          ** We are either modifying an existing allocation or we are creating
  1667          **  a new one.
  1668          */
  1669         if (0 != newAllocation) {
  1670             allocation = (STAllocation *) calloc(1, sizeof(STAllocation));
  1671             if (NULL != allocation) {
  1672                 /*
  1673                  ** Fixup the min timeval so if logic later will just work.
  1674                  */
  1675                 allocation->mMinTimeval = ST_TIMEVAL_MAX;
  1676                 allocation->mMaxTimeval = ST_TIMEVAL_MAX;
  1679         else if (NULL != searchCallsite
  1680                  && NULL != CALLSITE_RUN(searchCallsite)
  1681                  && 0 != searchHeapID) {
  1682             /*
  1683              ** We know what to search for, and we reduce what we search
  1684              **  by only looking for those allocations at a known callsite.
  1685              */
  1686             allocation =
  1687                 getLiveAllocationByHeapID(CALLSITE_RUN(searchCallsite),
  1688                                           searchHeapID);
  1690         else {
  1691             REPORT_ERROR(__LINE__, allocationTracker);
  1694         if (NULL != allocation) {
  1695             STAllocEvent *appendResult = NULL;
  1697             /*
  1698              ** Record the amount of time this allocation event took.
  1699              */
  1700             allocation->mHeapRuntimeCost += aHeapRuntimeCost;
  1702             /*
  1703              ** Now that we have an allocation, we need to make sure it has
  1704              **  the proper event.
  1705              */
  1706             appendResult =
  1707                 appendEvent(allocation, aTimeval, aType, aHeapID, aSize,
  1708                             aCallsite);
  1709             if (NULL != appendResult) {
  1710                 if (0 != newAllocation) {
  1711                     int runAppendResult = 0;
  1712                     int callsiteAppendResult = 0;
  1714                     /*
  1715                      ** A new allocation needs to be added to the global run.
  1716                      ** A new allocation needs to be added to the callsite.
  1717                      */
  1718                     runAppendResult =
  1719                         appendAllocation(&globals.mCommandLineOptions, NULL,
  1720                                          &globals.mRun, allocation);
  1721                     callsiteAppendResult =
  1722                         appendAllocation(&globals.mCommandLineOptions, NULL,
  1723                                          CALLSITE_RUN(aCallsite), allocation);
  1724                     if (0 != runAppendResult && 0 != callsiteAppendResult) {
  1725                         /*
  1726                          ** Success.
  1727                          */
  1728                         retval = allocation;
  1730                     else {
  1731                         REPORT_ERROR(__LINE__, appendAllocation);
  1734                 else {
  1735                     /*
  1736                      ** An existing allocation, if a realloc situation,
  1737                      **  may need to be added to the new callsite.
  1738                      ** This can only occur if the new and old callsites
  1739                      **  differ.
  1740                      ** Even then, a brute force check will need to be made
  1741                      **  to ensure the allocation was not added twice; 
  1742                      **  consider a realloc scenario where two different
  1743                      **  call stacks bump the allocation back and forth.
  1744                      */
  1745                     if (aCallsite != searchCallsite) {
  1746                         int found = 0;
  1748                         found =
  1749                             hasAllocation(CALLSITE_RUN(aCallsite),
  1750                                           allocation);
  1751                         if (0 == found) {
  1752                             int appendResult = 0;
  1754                             appendResult =
  1755                                 appendAllocation(&globals.mCommandLineOptions,
  1756                                                  NULL,
  1757                                                  CALLSITE_RUN(aCallsite),
  1758                                                  allocation);
  1759                             if (0 != appendResult) {
  1760                                 /*
  1761                                  ** Success.
  1762                                  */
  1763                                 retval = allocation;
  1765                             else {
  1766                                 REPORT_ERROR(__LINE__, appendAllocation);
  1769                         else {
  1770                             /*
  1771                              ** Already there.
  1772                              */
  1773                             retval = allocation;
  1776                     else {
  1777                         /*
  1778                          ** Success.
  1779                          */
  1780                         retval = allocation;
  1784             else {
  1785                 REPORT_ERROR(__LINE__, appendEvent);
  1788         else {
  1789             REPORT_ERROR(__LINE__, allocationTracker);
  1792     else {
  1793         REPORT_ERROR(__LINE__, allocationTracker);
  1796     /*
  1797      ** Compact the heap a bit if you can.
  1798      */
  1799     compactor++;
  1800     if (0 == (compactor % frequency)) {
  1801         heapCompact();
  1804     return retval;
  1807 /*
  1808 ** trackEvent
  1809 **
  1810 ** An allocation event has dropped in on us.
  1811 ** We need to do the right thing and track it.
  1812 */
  1813 void
  1814 trackEvent(uint32_t aTimeval, char aType, uint32_t aHeapRuntimeCost,
  1815            tmcallsite * aCallsite, uint32_t aHeapID, uint32_t aSize,
  1816            tmcallsite * aOldCallsite, uint32_t aOldHeapID, uint32_t aOldSize)
  1818     if (NULL != aCallsite) {
  1819         /*
  1820          ** Verify the old callsite just in case.
  1821          */
  1822         if (NULL != CALLSITE_RUN(aCallsite)
  1823             && (NULL == aOldCallsite || NULL != CALLSITE_RUN(aOldCallsite))) {
  1824             STAllocation *allocation = NULL;
  1826             /*
  1827              ** Add to the allocation tracking code.
  1828              */
  1829             allocation =
  1830                 allocationTracker(aTimeval, aType, aHeapRuntimeCost,
  1831                                   aCallsite, aHeapID, aSize, aOldCallsite,
  1832                                   aOldHeapID, aOldSize);
  1834             if (NULL == allocation) {
  1835                 REPORT_ERROR(__LINE__, allocationTracker);
  1838         else {
  1839             REPORT_ERROR(__LINE__, trackEvent);
  1842     else {
  1843         REPORT_ERROR(__LINE__, trackEvent);
  1847 /*
  1848 ** tmEventHandler
  1849 **
  1850 ** Callback from the tmreader_eventloop function.
  1851 ** Simply tries to sort out what we desire to know.
  1852 */
  1854 static const char spinner_chars[] = { '/', '-', '\\', '|' };
  1856 #define SPINNER_UPDATE_FREQUENCY 4096
  1857 #define SPINNER_CHAR_COUNT (sizeof(spinner_chars) / sizeof(spinner_chars[0]))
  1858 #define SPINNER_CHAR(_x) spinner_chars[(_x / SPINNER_UPDATE_FREQUENCY) % SPINNER_CHAR_COUNT]
  1860 void
  1861 tmEventHandler(tmreader * aReader, tmevent * aEvent)
  1863     static int event_count = 0;     /* for spinner */
  1864     if ((event_count++ % SPINNER_UPDATE_FREQUENCY) == 0)
  1865         printf("\rReading... %c", SPINNER_CHAR(event_count));
  1867     if (NULL != aReader && NULL != aEvent) {
  1868         switch (aEvent->type) {
  1869             /*
  1870              ** Events we ignore.
  1871              */
  1872         case TM_EVENT_LIBRARY:
  1873         case TM_EVENT_METHOD:
  1874         case TM_EVENT_STATS:
  1875         case TM_EVENT_TIMESTAMP:
  1876         case TM_EVENT_FILENAME:
  1877             break;
  1879             /*
  1880              ** Allocation events need to be tracked.
  1881              */
  1882         case TM_EVENT_MALLOC:
  1883         case TM_EVENT_CALLOC:
  1884         case TM_EVENT_REALLOC:
  1885         case TM_EVENT_FREE:
  1887                 uint32_t oldptr = 0;
  1888                 uint32_t oldsize = 0;
  1889                 tmcallsite *callsite = NULL;
  1890                 tmcallsite *oldcallsite = NULL;
  1892                 if (TM_EVENT_REALLOC == aEvent->type) {
  1893                     /*
  1894                      ** Only care about old arguments if there were any.
  1895                      */
  1896                     if (0 != aEvent->u.alloc.oldserial) {
  1897                         oldptr = aEvent->u.alloc.oldptr;
  1898                         oldsize = aEvent->u.alloc.oldsize;
  1899                         oldcallsite =
  1900                             tmreader_callsite(aReader,
  1901                                               aEvent->u.alloc.oldserial);
  1902                         if (NULL == oldcallsite) {
  1903                             REPORT_ERROR(__LINE__, tmreader_callsite);
  1908                 callsite = tmreader_callsite(aReader, aEvent->serial);
  1909                 if (NULL != callsite) {
  1910                     /*
  1911                      ** Verify a callsite run is there.
  1912                      ** If not, we are ignoring this callsite.
  1913                      */
  1914                     if (NULL != CALLSITE_RUN(callsite)) {
  1915                         char eventType = aEvent->type;
  1916                         uint32_t eventSize = aEvent->u.alloc.size;
  1918                         /*
  1919                          ** Play a nasty trick on reallocs of size zero.
  1920                          ** They are to become free events, adjust the size accordingly.
  1921                          ** This allows me to avoid all types of special case code.
  1922                          */
  1923                         if (0 == aEvent->u.alloc.size
  1924                             && TM_EVENT_REALLOC == aEvent->type) {
  1925                             eventType = TM_EVENT_FREE;
  1926                             if (0 != aEvent->u.alloc.oldserial) {
  1927                                 eventSize = aEvent->u.alloc.oldsize;
  1930                         trackEvent(ticks2msec
  1931                                    (aReader, aEvent->u.alloc.interval),
  1932                                    eventType, ticks2usec(aReader,
  1933                                                          aEvent->u.alloc.
  1934                                                          cost), callsite,
  1935                                    aEvent->u.alloc.ptr, eventSize,
  1936                                    oldcallsite, oldptr, oldsize);
  1939                 else {
  1940                     REPORT_ERROR(__LINE__, tmreader_callsite);
  1943             break;
  1945             /*
  1946              ** Callsite, set up the callsite run if it does not exist.
  1947              */
  1948         case TM_EVENT_CALLSITE:
  1950                 tmcallsite *callsite =
  1951                     tmreader_callsite(aReader, aEvent->serial);
  1953                 if (NULL != callsite) {
  1954                     if (NULL == CALLSITE_RUN(callsite)) {
  1955                         int createrun = __LINE__;
  1957 #if defined(MOZILLA_CLIENT)
  1958                         /*
  1959                          ** For a mozilla spacetrace, ignore this particular
  1960                          **  callsite as it is just noise, and causes us to
  1961                          **  use a lot of memory.
  1962                          **
  1963                          ** This callsite is present on the linux build,
  1964                          **  not sure if the other platforms have it.
  1965                          */
  1966                         if (0 !=
  1967                             hasCallsiteMatch(callsite, "g_main_is_running",
  1968                                              ST_FOLLOW_PARENTS)) {
  1969                             createrun = 0;
  1971 #endif /* MOZILLA_CLIENT */
  1973                         if (0 != createrun) {
  1974                             callsite->data = createRun(NULL, 0);
  1978                 else {
  1979                     REPORT_ERROR(__LINE__, tmreader_callsite);
  1982             break;
  1984             /*
  1985              ** Unhandled events should not be allowed.
  1986              */
  1987         default:
  1989                 REPORT_ERROR(__LINE__, tmEventHandler);
  1991             break;
  1996 /*
  1997 **  optionGetDataOut
  1998 **
  1999 **  Output option get data.
  2000 */
  2001 void
  2002 optionGetDataOut(PRFileDesc * inFD, STOptions * inOptions)
  2004     if (NULL != inFD && NULL != inOptions) {
  2005         int mark = 0;
  2007 #define ST_WEB_OPTION_BOOL(option_name, option_genre, option_help) \
  2008     PR_fprintf(inFD, "%s%s=%d", (0 == mark++) ? "?" : "&", #option_name, inOptions->m##option_name);
  2009 #define ST_WEB_OPTION_STRING(option_name, option_genre, default_value, option_help) \
  2010     PR_fprintf(inFD, "%s%s=%s", (0 == mark++) ? "?" : "&", #option_name, inOptions->m##option_name);
  2011 #define ST_WEB_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
  2012     { \
  2013         uint32_t loop = 0; \
  2015         for(loop = 0; loop < array_size; loop++) \
  2016         { \
  2017             PR_fprintf(inFD, "%s%s=%s", (0 == mark++) ? "?" : "&", #option_name, inOptions->m##option_name[loop]); \
  2018         } \
  2020 #define ST_WEB_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help)  /* no implementation */
  2021 #define ST_WEB_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
  2022     PR_fprintf(inFD, "%s%s=%u", (0 == mark++) ? "?" : "&", #option_name, inOptions->m##option_name / multiplier);
  2023 #define ST_WEB_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
  2024     { \
  2025         uint64_t def64 = default_value; \
  2026         uint64_t mul64 = multiplier; \
  2027         uint64_t div64; \
  2029         div64 = inOptions->m##option_name##64 / mul64; \
  2030         PR_fprintf(inFD, "%s%s=%llu", (0 == mark++) ? "?" : "&", #option_name, div64); \
  2033 #include "stoptions.h"
  2037 /*
  2038 ** htmlAnchor
  2039 **
  2040 ** Output an HTML anchor, or just the text depending on the mode.
  2041 */
  2042 void
  2043 htmlAnchor(STRequest * inRequest,
  2044            const char *aHref,
  2045            const char *aText,
  2046            const char *aTarget, const char *aClass, STOptions * inOptions)
  2048     if (NULL != aHref && '\0' != *aHref && NULL != aText && '\0' != *aText) {
  2049         int anchorLive = 1;
  2051         /*
  2052          ** In batch mode, we need to verify the anchor is live.
  2053          */
  2054         if (0 != inRequest->mOptions.mBatchRequestCount) {
  2055             uint32_t loop = 0;
  2056             int comparison = 1;
  2058             for (loop = 0; loop < inRequest->mOptions.mBatchRequestCount;
  2059                  loop++) {
  2060                 comparison =
  2061                     strcmp(aHref, inRequest->mOptions.mBatchRequest[loop]);
  2062                 if (0 == comparison) {
  2063                     break;
  2067             /*
  2068              ** Did we find it?
  2069              */
  2070             if (0 == comparison) {
  2071                 anchorLive = 0;
  2075         /*
  2076          ** In any mode, don't make an href to the current page.
  2077          */
  2078         if (0 != anchorLive && NULL != inRequest->mGetFileName) {
  2079             if (0 == strcmp(aHref, inRequest->mGetFileName)) {
  2080                 anchorLive = 0;
  2084         /*
  2085          **  Do the right thing.
  2086          */
  2087         if (0 != anchorLive) {
  2088             PR_fprintf(inRequest->mFD, "<a class=\"%s\" ", aClass);
  2089             if (NULL != aTarget && '\0' != *aTarget) {
  2090                 PR_fprintf(inRequest->mFD, "target=\"%s\" ", aTarget);
  2092             PR_fprintf(inRequest->mFD, "href=\"./%s", aHref);
  2094             /*
  2095              **  The options, if desired, get appended as form data.
  2096              */
  2097             optionGetDataOut(inRequest->mFD, inOptions);
  2099             PR_fprintf(inRequest->mFD, "\">%s</a>\n", aText);
  2101         else {
  2102             PR_fprintf(inRequest->mFD, "<span class=\"%s\">%s</span>\n",
  2103                        aClass, aText);
  2106     else {
  2107         REPORT_ERROR(__LINE__, htmlAnchor);
  2111 /*
  2112 ** htmlAllocationAnchor
  2113 **
  2114 ** Output an html achor that will resolve to the allocation in question.
  2115 */
  2116 void
  2117 htmlAllocationAnchor(STRequest * inRequest, STAllocation * aAllocation,
  2118                      const char *aText)
  2120     if (NULL != aAllocation && NULL != aText && '\0' != *aText) {
  2121         char buffer[128];
  2123         /*
  2124          ** This is a total hack.
  2125          ** The filename contains the index of the allocation in globals.mRun.
  2126          ** Safer than using the raw pointer value....
  2127          */
  2128         PR_snprintf(buffer, sizeof(buffer), "allocation_%u.html",
  2129                     aAllocation->mRunIndex);
  2131         htmlAnchor(inRequest, buffer, aText, NULL, "allocation",
  2132                    &inRequest->mOptions);
  2134     else {
  2135         REPORT_ERROR(__LINE__, htmlAllocationAnchor);
  2139 /*
  2140 ** resolveSourceFile
  2141 **
  2142 ** Easy way to get a readable/short name.
  2143 ** NULL if not present, not resolvable.
  2144 */
  2145 const char *
  2146 resolveSourceFile(tmmethodnode * aMethod)
  2148     const char *retval = NULL;
  2150     if (NULL != aMethod) {
  2151         const char *methodSays = NULL;
  2153         methodSays = aMethod->sourcefile;
  2155         if (NULL != methodSays && '\0' != methodSays[0]
  2156             && 0 != strcmp("noname", methodSays)) {
  2157             retval = strrchr(methodSays, '/');
  2158             if (NULL != retval) {
  2159                 retval++;
  2161             else {
  2162                 retval = methodSays;
  2167     return retval;
  2170 /*
  2171 ** htmlCallsiteAnchor
  2172 **
  2173 ** Output an html anchor that will resolve to the callsite in question.
  2174 ** If no text is provided, we provide our own.
  2175 **
  2176 ** RealName determines whether or not we crawl our parents until the point
  2177 **  we no longer match stats.
  2178 */
  2179 void
  2180 htmlCallsiteAnchor(STRequest * inRequest, tmcallsite * aCallsite,
  2181                    const char *aText, int aRealName)
  2183     if (NULL != aCallsite) {
  2184         char textBuf[512];
  2185         char hrefBuf[128];
  2186         tmcallsite *namesite = aCallsite;
  2188         /*
  2189          ** Should we use a different name?
  2190          */
  2191         if (0 == aRealName && NULL != namesite->parent
  2192             && NULL != namesite->parent->method) {
  2193             STRun *myRun = NULL;
  2194             STRun *upRun = NULL;
  2196             do {
  2197                 myRun = CALLSITE_RUN(namesite);
  2198                 upRun = CALLSITE_RUN(namesite->parent);
  2200                 if (0 !=
  2201                     memcmp(&myRun->mStats[inRequest->mContext->mIndex],
  2202                            &upRun->mStats[inRequest->mContext->mIndex],
  2203                            sizeof(STCallsiteStats))) {
  2204                     /*
  2205                      ** Doesn't match, stop.
  2206                      */
  2207                     break;
  2209                 else {
  2210                     /*
  2211                      ** Matches, keep going up.
  2212                      */
  2213                     namesite = namesite->parent;
  2216             while (NULL != namesite->parent
  2217                    && NULL != namesite->parent->method);
  2220         /*
  2221          ** If no text, provide our own.
  2222          */
  2223         if (NULL == aText || '\0' == *aText) {
  2224             const char *methodName = NULL;
  2225             const char *sourceFile = NULL;
  2227             if (NULL != namesite->method) {
  2228                 methodName = tmmethodnode_name(namesite->method);
  2230             else {
  2231                 methodName = "==NONAME==";
  2234             /*
  2235              ** Decide which format to use to identify the callsite.
  2236              ** If we can detect availability, hook up the filename with lxr information.
  2237              */
  2238             sourceFile = resolveSourceFile(namesite->method);
  2239             if (NULL != sourceFile
  2240                 && 0 == strncmp("mozilla/", namesite->method->sourcefile,
  2241                                 8)) {
  2242                 char lxrHREFBuf[512];
  2244                 PR_snprintf(lxrHREFBuf, sizeof(lxrHREFBuf),
  2245                             " [<a href=\"http://lxr.mozilla.org/mozilla/source/%s#%u\" class=\"lxr\" target=\"_st_lxr\">%s:%u</a>]",
  2246                             namesite->method->sourcefile + 8,
  2247                             namesite->method->linenumber, sourceFile,
  2248                             namesite->method->linenumber);
  2249                 PR_snprintf(textBuf, sizeof(textBuf),
  2250                             "<span class=\"source mozilla-source\">%s</span>%s",
  2251                             methodName, lxrHREFBuf);
  2253             else if (NULL != sourceFile) {
  2254                 PR_snprintf(textBuf, sizeof(textBuf),
  2255                             "<span class=\"source external-source\">%s [<span class=\"source-extra\">%s:%u</span>]</span>",
  2256                             methodName, sourceFile,
  2257                             namesite->method->linenumber);
  2259             else {
  2260                 PR_snprintf(textBuf, sizeof(textBuf),
  2261                             "<span class=\"source binary-source\">%s [<span class=\"source-extra\">+%u(%u)</span>]</span>",
  2262                             methodName, namesite->offset,
  2263                             (uint32_t) namesite->entry.key);
  2266             aText = textBuf;
  2269         PR_snprintf(hrefBuf, sizeof(hrefBuf), "callsite_%u.html",
  2270                     (uint32_t) aCallsite->entry.key);
  2272         htmlAnchor(inRequest, hrefBuf, aText, NULL, "callsite",
  2273                    &inRequest->mOptions);
  2275     else {
  2276         REPORT_ERROR(__LINE__, htmlCallsiteAnchor);
  2280 /*
  2281 ** htmlHeader
  2282 **
  2283 ** Output a standard header in the report files.
  2284 */
  2285 void
  2286 htmlHeader(STRequest * inRequest, const char *aTitle)
  2288     PR_fprintf(inRequest->mFD,
  2289                "<html>\n"
  2290                "<head>\n"
  2291                "<title>%s</title>\n"
  2292                "<link rel=\"stylesheet\" href=\"spacetrace.css\" type=\"text/css\""
  2293                "</head>\n"
  2294                "<body>\n"
  2295                "<div class=spacetrace-header>\n"
  2296                "<span class=spacetrace-title>Spacetrace</span>"
  2297                "<span class=navigate>\n"
  2298                "<span class=\"category-title header-text\">Category:</span>\n"
  2299                "<span class=\"current-category\">%s</span>\n",
  2300                aTitle, inRequest->mOptions.mCategoryName);
  2302     PR_fprintf(inRequest->mFD, "<span class=\"header-item\">");
  2303     htmlAnchor(inRequest, "index.html", "Index", NULL, "header-menuitem",
  2304                &inRequest->mOptions);
  2305     PR_fprintf(inRequest->mFD, "</span>\n");
  2307     PR_fprintf(inRequest->mFD, "<span class=\"header-item\">");
  2308     htmlAnchor(inRequest, "options.html", "Options", NULL, "header-menuitem",
  2309                &inRequest->mOptions);
  2310     PR_fprintf(inRequest->mFD, "</span>\n");
  2312     PR_fprintf(inRequest->mFD, "</span>\n");    /* class=navigate */
  2314     PR_fprintf(inRequest->mFD,
  2315                "</div>\n\n<div class=\"header-separator\"></div>\n\n");
  2318 /*
  2319 ** htmlFooter
  2320 **
  2321 ** Output a standard footer in the report file.
  2322 */
  2323 void
  2324 htmlFooter(STRequest * inRequest)
  2326     PR_fprintf(inRequest->mFD,
  2327                "<div class=\"footer-separator\"></div>\n\n"
  2328                "<div class=\"footer\">\n"
  2329                "<span class=\"footer-text\">SpaceTrace</span>\n"
  2330                "</div>\n\n" "</body>\n" "</html>\n");
  2333 /*
  2334 ** htmlNotFound
  2335 **
  2336 ** Not found message.
  2337 */
  2338 void
  2339 htmlNotFound(STRequest * inRequest)
  2341     htmlHeader(inRequest, "File Not Found");
  2342     PR_fprintf(inRequest->mFD, "File Not Found\n");
  2343     htmlFooter(inRequest);
  2346 void
  2347 htmlStartTable(STRequest* inRequest,
  2348                const char* table_class,
  2349                const char* id,
  2350                const char* caption,
  2351                const char * const headers[], uint32_t header_length)
  2353         uint32_t i;
  2355         PR_fprintf(inRequest->mFD,
  2356                    "<div id=\"%s\"><table class=\"data %s\">\n"
  2357                    "  <caption>%s</caption>"
  2358                    "  <thead>\n"
  2359                    "    <tr class=\"row-header\">\n", id,
  2360                    table_class ? table_class : "",
  2361                    caption);
  2363         for (i=0; i< header_length; i++)
  2364             PR_fprintf(inRequest->mFD,
  2365                        "      <th>%s</th>\n", headers[i]);
  2367         PR_fprintf(inRequest->mFD, "    </tr>  </thead>  <tbody>\n");
  2370 /*
  2371 ** callsiteArrayFromCallsite
  2372 **
  2373 ** Simply return an array of the callsites divulged from the site passed in,
  2374 **  including the site passed in.
  2375 ** Do not worry about dups, or the order of the items.
  2376 **
  2377 ** Returns the number of items in the array.
  2378 ** If the same as aExistingCount, then nothing happened.
  2379 */
  2380 uint32_t
  2381 callsiteArrayFromCallsite(tmcallsite *** aArray, uint32_t aExistingCount,
  2382                           tmcallsite * aSite, int aFollow)
  2384     uint32_t retval = 0;
  2386     if (NULL != aArray && NULL != aSite) {
  2387         tmcallsite **expand = NULL;
  2389         /*
  2390          ** If we have an existing count, we just keep expanding this.
  2391          */
  2392         retval = aExistingCount;
  2394         /*
  2395          ** Go through every allocation.
  2396          */
  2397         do {
  2398             /*
  2399              ** expand the array.
  2400              */
  2401             expand =
  2402                 (tmcallsite **) realloc(*aArray,
  2403                                         sizeof(tmcallsite *) * (retval + 1));
  2404             if (NULL != expand) {
  2405                 /*
  2406                  ** Set the callsite in case of pointer move.
  2407                  */
  2408                 *aArray = expand;
  2410                 /*
  2411                  ** Assign the value.
  2412                  */
  2413                 (*aArray)[retval] = aSite;
  2414                 retval++;
  2416             else {
  2417                 REPORT_ERROR(__LINE__, realloc);
  2418                 break;
  2422             /*
  2423              ** What do we follow?
  2424              */
  2425             switch (aFollow) {
  2426             case ST_FOLLOW_SIBLINGS:
  2427                 aSite = aSite->siblings;
  2428                 break;
  2429             case ST_FOLLOW_PARENTS:
  2430                 aSite = aSite->parent;
  2431                 break;
  2432             default:
  2433                 aSite = NULL;
  2434                 REPORT_ERROR(__LINE__, callsiteArrayFromCallsite);
  2435                 break;
  2438         while (NULL != aSite && NULL != aSite->method);
  2441     return retval;
  2444 /*
  2445 ** callsiteArrayFromRun
  2446 **
  2447 ** Simply return an array of the callsites from the run allocations.
  2448 ** We only pay attention to callsites that were not free callsites.
  2449 ** Do not worry about dups, or the order of the items.
  2450 **
  2451 ** Returns the number of items in the array.
  2452 ** If the same as aExistingCount, then nothing happened.
  2453 */
  2454 uint32_t
  2455 callsiteArrayFromRun(tmcallsite *** aArray, uint32_t aExistingCount,
  2456                      STRun * aRun)
  2458     uint32_t retval = 0;
  2460     if (NULL != aArray && NULL != aRun && 0 < aRun->mAllocationCount) {
  2461         uint32_t allocLoop = 0;
  2462         uint32_t eventLoop = 0;
  2463         int stopLoops = 0;
  2465         /*
  2466          ** If we have an existing count, we just keep expanding this.
  2467          */
  2468         retval = aExistingCount;
  2470         /*
  2471          ** Go through every allocation.
  2472          */
  2473         for (allocLoop = 0;
  2474              0 == stopLoops && allocLoop < aRun->mAllocationCount;
  2475              allocLoop++) {
  2476             /*
  2477              ** Go through every event.
  2478              */
  2479             for (eventLoop = 0;
  2480                  0 == stopLoops
  2481                  && eventLoop < aRun->mAllocations[allocLoop]->mEventCount;
  2482                  eventLoop++) {
  2483                 /*
  2484                  ** Skip the free events.
  2485                  */
  2486                 if (TM_EVENT_FREE !=
  2487                     aRun->mAllocations[allocLoop]->mEvents[eventLoop].
  2488                     mEventType) {
  2489                     tmcallsite **expand = NULL;
  2491                     /*
  2492                      ** expand the array.
  2493                      */
  2494                     expand =
  2495                         (tmcallsite **) realloc(*aArray,
  2496                                                 sizeof(tmcallsite *) *
  2497                                                 (retval + 1));
  2498                     if (NULL != expand) {
  2499                         /*
  2500                          ** Set the callsite in case of pointer move.
  2501                          */
  2502                         *aArray = expand;
  2504                         /*
  2505                          ** Assign the value.
  2506                          */
  2507                         (*aArray)[retval] =
  2508                             aRun->mAllocations[allocLoop]->mEvents[eventLoop].
  2509                             mCallsite;
  2510                         retval++;
  2512                     else {
  2513                         REPORT_ERROR(__LINE__, realloc);
  2514                         stopLoops = __LINE__;
  2521     return retval;
  2524 /*
  2525 ** getDataPRUint*
  2526 **
  2527 ** Helper to avoid cut and paste code.
  2528 ** Failure to find aCheckFor does not mean failure.
  2529 ** In case of dups, specify an index on non "1" to get others.
  2530 ** Do not touch storage space unless a find is made.
  2531 ** Returns !0 on failure.
  2532 */
  2533 int
  2534 getDataPRUint32Base(const FormData * aGetData, const char *aCheckFor,
  2535                     int inIndex, void *aStoreResult, uint32_t aBits)
  2537     int retval = 0;
  2539     if (NULL != aGetData && NULL != aCheckFor && 0 != inIndex
  2540         && NULL != aStoreResult) {
  2541         unsigned finder = 0;
  2543         /*
  2544          **  Loop over the names, looking for an exact string match.
  2545          **  Skip over initial finds, decrementing inIndex, until "1".
  2546          */
  2547         for (finder = 0; finder < aGetData->mNVCount; finder++) {
  2548             if (0 == strcmp(aCheckFor, aGetData->mNArray[finder])) {
  2549                 inIndex--;
  2551                 if (0 == inIndex) {
  2552                     int32_t scanRes = 0;
  2554                     if (64 == aBits) {
  2555                         scanRes =
  2556                             PR_sscanf(aGetData->mVArray[finder], "%llu",
  2557                                       aStoreResult);
  2559                     else {
  2560                         scanRes =
  2561                             PR_sscanf(aGetData->mVArray[finder], "%u",
  2562                                       aStoreResult);
  2564                     if (1 != scanRes) {
  2565                         retval = __LINE__;
  2566                         REPORT_ERROR(__LINE__, PR_sscanf);
  2568                     break;
  2573     else {
  2574         retval = __LINE__;
  2575         REPORT_ERROR(__LINE__, getDataPRUint32Base);
  2578     return retval;
  2581 int
  2582 getDataPRUint32(const FormData * aGetData, const char *aCheckFor, int inIndex,
  2583                 uint32_t * aStoreResult, uint32_t aConversion)
  2585     int retval = 0;
  2587     retval =
  2588         getDataPRUint32Base(aGetData, aCheckFor, inIndex, aStoreResult, 32);
  2589     *aStoreResult *= aConversion;
  2591     return retval;
  2594 int
  2595 getDataPRUint64(const FormData * aGetData, const char *aCheckFor, int inIndex,
  2596                 uint64_t * aStoreResult64, uint64_t aConversion64)
  2598     int retval = 0;
  2599     uint64_t value64 = 0;
  2601     retval = getDataPRUint32Base(aGetData, aCheckFor, inIndex, &value64, 64);
  2602     *aStoreResult64 = value64 * aConversion64;
  2604     return retval;
  2607 /*
  2608 ** getDataString
  2609 **
  2610 ** Pull out the string data, if specified.
  2611 ** In case of dups, specify an index on non "1" to get others.
  2612 ** Do not touch storage space unless a find is made.
  2613 ** Return !0 on failure.
  2614 */
  2615 int
  2616 getDataString(const FormData * aGetData, const char *aCheckFor, int inIndex,
  2617               char *aStoreResult, int inStoreResultLength)
  2619     int retval = 0;
  2621     if (NULL != aGetData && NULL != aCheckFor && 0 != inIndex
  2622         && NULL != aStoreResult && 0 != inStoreResultLength) {
  2623         unsigned finder = 0;
  2625         /*
  2626          **  Loop over the names, looking for an exact string match.
  2627          **  Skip over initial finds, decrementing inIndex, until "1".
  2628          */
  2629         for (finder = 0; finder < aGetData->mNVCount; finder++) {
  2630             if (0 == strcmp(aCheckFor, aGetData->mNArray[finder])) {
  2631                 inIndex--;
  2633                 if (0 == inIndex) {
  2634                     PR_snprintf(aStoreResult, inStoreResultLength, "%s",
  2635                                 aGetData->mVArray[finder]);
  2636                     break;
  2641     else {
  2642         retval = __LINE__;
  2643         REPORT_ERROR(__LINE__, getDataPRUint32);
  2646     return retval;
  2649 /*
  2650 ** displayTopAllocations
  2651 **
  2652 ** Present the top allocations.
  2653 ** The run must be passed in, and it must be pre-sorted.
  2654 **
  2655 ** Returns !0 on failure.
  2656 */
  2657 int
  2658 displayTopAllocations(STRequest * inRequest, STRun * aRun,
  2659                       const char* id,
  2660                       const char* caption,
  2661                       int aWantCallsite)
  2663     int retval = 0;
  2665     if (NULL != aRun) {
  2666         if (0 < aRun->mAllocationCount) {
  2667             uint32_t loop = 0;
  2668             STAllocation *current = NULL;
  2670             static const char* const headers[] = {
  2671                 "Rank", "Index", "Byte Size", "Lifespan (sec)",
  2672                 "Weight", "Heap Op (sec)"
  2673             };
  2675             static const char* const headers_callsite[] = {
  2676                 "Rank", "Index", "Byte Size", "Lifespan (sec)",
  2677                 "Weight", "Heap Op (sec)", "Origin Callsite"
  2678             };
  2680             if (aWantCallsite)
  2681                 htmlStartTable(inRequest, NULL, id,
  2682                                caption,
  2683                                headers_callsite,
  2684                                sizeof(headers_callsite) / sizeof(headers_callsite[0]));
  2685             else
  2686                 htmlStartTable(inRequest, NULL, id, caption,
  2687                                headers,
  2688                                sizeof(headers) / sizeof(headers[0]));                           
  2689             /*
  2690              ** Loop over the items, up to some limit or until the end.
  2691              */
  2692             for (loop = 0;
  2693                  loop < inRequest->mOptions.mListItemMax
  2694                  && loop < aRun->mAllocationCount; loop++) {
  2695                 current = aRun->mAllocations[loop];
  2696                 if (NULL != current) {
  2697                     uint32_t lifespan =
  2698                         current->mMaxTimeval - current->mMinTimeval;
  2699                     uint32_t size = byteSize(&inRequest->mOptions, current);
  2700                     uint32_t heapCost = current->mHeapRuntimeCost;
  2701                     uint64_t weight64 = 0;
  2702                     char buffer[32];
  2704                     weight64 =(uint64_t)(size * lifespan);
  2706                     PR_fprintf(inRequest->mFD, "<tr>\n");
  2708                     /*
  2709                      ** Rank.
  2710                      */
  2711                     PR_fprintf(inRequest->mFD, "<td align=right>%u</td>\n",
  2712                                loop + 1);
  2714                     /*
  2715                      ** Index.
  2716                      */
  2717                     PR_snprintf(buffer, sizeof(buffer), "%u",
  2718                                 current->mRunIndex);
  2719                     PR_fprintf(inRequest->mFD, "<td align=right>\n");
  2720                     htmlAllocationAnchor(inRequest, current, buffer);
  2721                     PR_fprintf(inRequest->mFD, "</td>\n");
  2723                     /*
  2724                      ** Byte Size.
  2725                      */
  2726                     PR_fprintf(inRequest->mFD, "<td align=right>%u</td>\n",
  2727                                size);
  2729                     /*
  2730                      ** Lifespan.
  2731                      */
  2732                     PR_fprintf(inRequest->mFD,
  2733                                "<td align=right>" ST_TIMEVAL_FORMAT "</td>\n",
  2734                                ST_TIMEVAL_PRINTABLE(lifespan));
  2736                     /*
  2737                      ** Weight.
  2738                      */
  2739                     PR_fprintf(inRequest->mFD, "<td align=right>%llu</td>\n",
  2740                                weight64);
  2742                     /*
  2743                      ** Heap operation cost.
  2744                      */
  2745                     PR_fprintf(inRequest->mFD,
  2746                                "<td align=right>" ST_MICROVAL_FORMAT
  2747                                "</td>\n", ST_MICROVAL_PRINTABLE(heapCost));
  2749                     if (0 != aWantCallsite) {
  2750                         /*
  2751                          ** Callsite.
  2752                          */
  2753                         PR_fprintf(inRequest->mFD, "<td>");
  2754                         htmlCallsiteAnchor(inRequest,
  2755                                            current->mEvents[0].mCallsite,
  2756                                            NULL, 0);
  2757                         PR_fprintf(inRequest->mFD, "</td>\n");
  2760                     PR_fprintf(inRequest->mFD, "</tr>\n");
  2764             PR_fprintf(inRequest->mFD, "</tbody>\n</table></div>\n\n");
  2767     else {
  2768         retval = __LINE__;
  2769         REPORT_ERROR(__LINE__, displayTopAllocations);
  2772     return retval;
  2775 /*
  2776 ** displayMemoryLeaks
  2777 **
  2778 ** Present the top memory leaks.
  2779 ** The run must be passed in, and it must be pre-sorted.
  2780 **
  2781 ** Returns !0 on failure.
  2782 */
  2783 int
  2784 displayMemoryLeaks(STRequest * inRequest, STRun * aRun)
  2786     int retval = 0;
  2788     if (NULL != aRun) {
  2789         uint32_t loop = 0;
  2790         uint32_t displayed = 0;
  2791         STAllocation *current = NULL;
  2793         static const char * headers[] = {
  2794             "Rank", "Index", "Byte Size", "Lifespan (sec)",
  2795             "Weight", "Heap Op (sec)", "Origin Callsite"
  2796         };
  2798         htmlStartTable(inRequest, NULL, "memory-leaks", "Memory Leaks", headers,
  2799                        sizeof(headers) / sizeof(headers[0]));
  2801         /*
  2802          ** Loop over all of the items, or until we've displayed enough.
  2803          */
  2804         for (loop = 0;
  2805              displayed < inRequest->mOptions.mListItemMax
  2806              && loop < aRun->mAllocationCount; loop++) {
  2807             current = aRun->mAllocations[loop];
  2808             if (NULL != current && 0 != current->mEventCount) {
  2809                 /*
  2810                  ** In order to be a leak, the last event of its life must
  2811                  **  NOT be a free operation.
  2812                  **
  2813                  ** A free operation is just that, a free.
  2814                  */
  2815                 if (TM_EVENT_FREE !=
  2816                     current->mEvents[current->mEventCount - 1].mEventType) {
  2817                     uint32_t lifespan =
  2818                         current->mMaxTimeval - current->mMinTimeval;
  2819                     uint32_t size = byteSize(&inRequest->mOptions, current);
  2820                     uint32_t heapCost = current->mHeapRuntimeCost;
  2821                     uint64_t weight64 = 0;
  2822                     char buffer[32];
  2824                     weight64 =(uint64_t)(size * lifespan);
  2826                     /*
  2827                      ** One more shown.
  2828                      */
  2829                     displayed++;
  2831                     PR_fprintf(inRequest->mFD, "<tr>\n");
  2833                     /*
  2834                      ** Rank.
  2835                      */
  2836                     PR_fprintf(inRequest->mFD, "<td align=right>%u</td>\n",
  2837                                displayed);
  2839                     /*
  2840                      ** Index.
  2841                      */
  2842                     PR_snprintf(buffer, sizeof(buffer), "%u",
  2843                                 current->mRunIndex);
  2844                     PR_fprintf(inRequest->mFD, "<td align=right>\n");
  2845                     htmlAllocationAnchor(inRequest, current, buffer);
  2846                     PR_fprintf(inRequest->mFD, "</td>\n");
  2848                     /*
  2849                      ** Byte Size.
  2850                      */
  2851                     PR_fprintf(inRequest->mFD, "<td align=right>%u</td>\n",
  2852                                size);
  2854                     /*
  2855                      ** Lifespan.
  2856                      */
  2857                     PR_fprintf(inRequest->mFD,
  2858                                "<td align=right>" ST_TIMEVAL_FORMAT "</td>\n",
  2859                                ST_TIMEVAL_PRINTABLE(lifespan));
  2861                     /*
  2862                      ** Weight.
  2863                      */
  2864                     PR_fprintf(inRequest->mFD, "<td align=right>%llu</td>\n",
  2865                                weight64);
  2867                     /*
  2868                      ** Heap Operation Seconds.
  2869                      */
  2870                     PR_fprintf(inRequest->mFD,
  2871                                "<td align=right>" ST_MICROVAL_FORMAT
  2872                                "</td>\n", ST_MICROVAL_PRINTABLE(heapCost));
  2874                     /*
  2875                      ** Callsite.
  2876                      */
  2877                     PR_fprintf(inRequest->mFD, "<td>");
  2878                     htmlCallsiteAnchor(inRequest,
  2879                                        current->mEvents[0].mCallsite, NULL,
  2880                                        0);
  2881                     PR_fprintf(inRequest->mFD, "</td>\n");
  2883                     PR_fprintf(inRequest->mFD, "</tr>\n");
  2888         PR_fprintf(inRequest->mFD, "</tbody></table></div>\n\n");
  2890     else {
  2891         retval = __LINE__;
  2892         REPORT_ERROR(__LINE__, displayMemoryLeaks);
  2895     return retval;
  2898 /*
  2899 ** displayCallsites
  2900 **
  2901 ** Display a table of callsites.
  2902 ** If the stamp is non zero, then must match that stamp.
  2903 ** If the stamp is zero, then must match the global sorted run stamp.
  2904 ** Return !0 on error.
  2905 */
  2906 int
  2907 displayCallsites(STRequest * inRequest, tmcallsite * aCallsite, int aFollow,
  2908                  uint32_t aStamp,
  2909                  const char* id,
  2910                  const char* caption,
  2911                  int aRealNames)
  2913     int retval = 0;
  2915     if (NULL != aCallsite && NULL != aCallsite->method) {
  2916         int headerDisplayed = 0;
  2917         STRun *run = NULL;
  2919         /*
  2920          ** Correct the stamp if need be.
  2921          */
  2922         if (0 == aStamp && NULL != inRequest->mContext->mSortedRun) {
  2923             aStamp =
  2924                 inRequest->mContext->mSortedRun->mStats[inRequest->mContext->
  2925                                                         mIndex].mStamp;
  2928         /*
  2929          ** Loop over the callsites looking for a stamp match.
  2930          ** A stamp guarantees there is something interesting to look at too.
  2931          ** If found, output it.
  2932          */
  2933         while (NULL != aCallsite && NULL != aCallsite->method) {
  2934             run = CALLSITE_RUN(aCallsite);
  2935             if (NULL != run) {
  2936                 if (aStamp == run->mStats[inRequest->mContext->mIndex].mStamp) {
  2937                     /*
  2938                      ** We got a header?
  2939                      */
  2940                     if (0 == headerDisplayed) {
  2942                         static const char* const headers[] = {
  2943                             "Callsite",
  2944                             "<abbr title=\"Composite Size\">C. Size</abbr>",
  2945                             "<abbr title=\"Composite Seconds\">C. Seconds</abbr>",
  2946                             "<abbr title=\"Composite Weight\">C. Weight</abbr>",
  2947                             "<abbr title=\"Heap Object Count\">H.O. Count</abbr>",
  2948                             "<abbr title=\"Composite Heap Operation Seconds\">C.H. Operation (sec)</abbr>"
  2949                         };
  2950                         headerDisplayed = __LINE__;
  2951                         htmlStartTable(inRequest, NULL, id, caption, headers,
  2952                                        sizeof(headers)/sizeof(headers[0]));
  2955                     /*
  2956                      ** Output the information.
  2957                      */
  2958                     PR_fprintf(inRequest->mFD, "<tr>\n");
  2960                     /*
  2961                      ** Method name.
  2962                      */
  2963                     PR_fprintf(inRequest->mFD, "<td>");
  2964                     htmlCallsiteAnchor(inRequest, aCallsite, NULL,
  2965                                        aRealNames);
  2966                     PR_fprintf(inRequest->mFD, "</td>");
  2968                     /*
  2969                      ** Byte Size.
  2970                      */
  2971                     PR_fprintf(inRequest->mFD,
  2972                                "<td valign=top align=right>%u</td>\n",
  2973                                run->mStats[inRequest->mContext->mIndex].
  2974                                mSize);
  2976                     /*
  2977                      ** Seconds.
  2978                      */
  2979                     PR_fprintf(inRequest->mFD,
  2980                                "<td valign=top align=right>" ST_TIMEVAL_FORMAT
  2981                                "</td>\n",
  2982                                ST_TIMEVAL_PRINTABLE64(run->
  2983                                                       mStats[inRequest->
  2984                                                              mContext->
  2985                                                              mIndex].
  2986                                                       mTimeval64));
  2988                     /*
  2989                      ** Weight.
  2990                      */
  2991                     PR_fprintf(inRequest->mFD,
  2992                                "<td valign=top align=right>%llu</td>\n",
  2993                                run->mStats[inRequest->mContext->mIndex].
  2994                                mWeight64);
  2996                     /*
  2997                      ** Allocation object count.
  2998                      */
  2999                     PR_fprintf(inRequest->mFD,
  3000                                "<td valign=top align=right>%u</td>\n",
  3001                                run->mStats[inRequest->mContext->mIndex].
  3002                                mCompositeCount);
  3004                     /*
  3005                      ** Heap Operation Seconds.
  3006                      */
  3007                     PR_fprintf(inRequest->mFD,
  3008                                "<td valign=top align=right>"
  3009                                ST_MICROVAL_FORMAT "</td>\n",
  3010                                ST_MICROVAL_PRINTABLE(run->
  3011                                                      mStats[inRequest->
  3012                                                             mContext->mIndex].
  3013                                                      mHeapRuntimeCost));
  3015                     PR_fprintf(inRequest->mFD, "</tr>\n");
  3018             else {
  3019                 retval = __LINE__;
  3020                 REPORT_ERROR(__LINE__, displayCallsites);
  3021                 break;
  3024             /*
  3025              ** What do we follow?
  3026              */
  3027             switch (aFollow) {
  3028             case ST_FOLLOW_SIBLINGS:
  3029                 aCallsite = aCallsite->siblings;
  3030                 break;
  3031             case ST_FOLLOW_PARENTS:
  3032                 aCallsite = aCallsite->parent;
  3033                 break;
  3034             default:
  3035                 aCallsite = NULL;
  3036                 retval = __LINE__;
  3037                 REPORT_ERROR(__LINE__, displayCallsites);
  3038                 break;
  3042         /*
  3043          ** Terminate the table if we should.
  3044          */
  3045         if (0 != headerDisplayed) {
  3046             PR_fprintf(inRequest->mFD, "</tbody></table></div>\n\n");
  3049     else {
  3050         retval = __LINE__;
  3051         REPORT_ERROR(__LINE__, displayCallsites);
  3054     return retval;
  3057 /*
  3058 ** displayAllocationDetails
  3059 **
  3060 ** Report what we know about the allocation.
  3061 **
  3062 ** Returns !0 on error.
  3063 */
  3064 int
  3065 displayAllocationDetails(STRequest * inRequest, STAllocation * aAllocation)
  3067     int retval = 0;
  3069     if (NULL != aAllocation) {
  3070         uint32_t traverse = 0;
  3071         uint32_t bytesize = byteSize(&inRequest->mOptions, aAllocation);
  3072         uint32_t timeval =
  3073             aAllocation->mMaxTimeval - aAllocation->mMinTimeval;
  3074         uint32_t heapCost = aAllocation->mHeapRuntimeCost;
  3075         uint64_t weight64 = 0;
  3076         uint32_t cacheval = 0;
  3077         int displayRes = 0;
  3079         weight64 = (uint64_t)(bytesize * timeval);
  3081         PR_fprintf(inRequest->mFD, "<p>Allocation %u Details:</p>\n",
  3082                    aAllocation->mRunIndex);
  3084         PR_fprintf(inRequest->mFD, "<div id=\"allocation-details\"><table class=\"data summary\">\n");
  3085         PR_fprintf(inRequest->mFD,
  3086                    "<tr><td align=left>Final Size:</td><td align=right>%u</td></tr>\n",
  3087                    bytesize);
  3088         PR_fprintf(inRequest->mFD,
  3089                    "<tr><td align=left>Lifespan Seconds:</td><td align=right>"
  3090                    ST_TIMEVAL_FORMAT "</td></tr>\n",
  3091                    ST_TIMEVAL_PRINTABLE(timeval));
  3092         PR_fprintf(inRequest->mFD,
  3093                    "<tr><td align=left>Weight:</td><td align=right>%llu</td></tr>\n",
  3094                    weight64);
  3095         PR_fprintf(inRequest->mFD,
  3096                    "<tr><td align=left>Heap Operation Seconds:</td><td align=right>"
  3097                    ST_MICROVAL_FORMAT "</td></tr>\n",
  3098                    ST_MICROVAL_PRINTABLE(heapCost));
  3099         PR_fprintf(inRequest->mFD, "</table></div>\n");
  3101         /*
  3102          ** The events.
  3103          */
  3106             static const char* const headers[] = {
  3107                 "Operation", "Size", "Seconds", ""
  3108             };
  3110             char caption[100];
  3111             PR_snprintf(caption, sizeof(caption), "%u Life Event(s)",
  3112                        aAllocation->mEventCount);
  3113             htmlStartTable(inRequest, NULL, "allocation-details", caption, headers,
  3114                            sizeof(headers) / sizeof(headers[0]));
  3117         for (traverse = 0;
  3118              traverse < aAllocation->mEventCount
  3119              && traverse < inRequest->mOptions.mListItemMax; traverse++) {
  3120             PR_fprintf(inRequest->mFD, "<tr>\n");
  3122             /*
  3123              ** count.
  3124              */
  3125             PR_fprintf(inRequest->mFD,
  3126                        "<td valign=top align=right>%u.</td>\n", traverse + 1);
  3128             /*
  3129              ** Operation.
  3130              */
  3131             PR_fprintf(inRequest->mFD, "<td valign=top>");
  3132             switch (aAllocation->mEvents[traverse].mEventType) {
  3133             case TM_EVENT_CALLOC:
  3134                 PR_fprintf(inRequest->mFD, "calloc");
  3135                 break;
  3136             case TM_EVENT_FREE:
  3137                 PR_fprintf(inRequest->mFD, "free");
  3138                 break;
  3139             case TM_EVENT_MALLOC:
  3140                 PR_fprintf(inRequest->mFD, "malloc");
  3141                 break;
  3142             case TM_EVENT_REALLOC:
  3143                 PR_fprintf(inRequest->mFD, "realloc");
  3144                 break;
  3145             default:
  3146                 retval = __LINE__;
  3147                 REPORT_ERROR(__LINE__, displayAllocationDetails);
  3148                 break;
  3150             PR_fprintf(inRequest->mFD, "</td>");
  3152             /*
  3153              ** Size.
  3154              */
  3155             PR_fprintf(inRequest->mFD, "<td valign=top align=right>%u</td>\n",
  3156                        aAllocation->mEvents[traverse].mHeapSize);
  3158             /*
  3159              ** Timeval.
  3160              */
  3161             cacheval =
  3162                 aAllocation->mEvents[traverse].mTimeval - globals.mMinTimeval;
  3163             PR_fprintf(inRequest->mFD,
  3164                        "<td valign=top align=right>" ST_TIMEVAL_FORMAT
  3165                        "</td>\n", ST_TIMEVAL_PRINTABLE(cacheval));
  3167             /*
  3168              ** Callsite backtrace.
  3169              ** Only relevant backtrace is for event 0 for now until
  3170              **  trace-malloc outputs proper callsites for all others.
  3171              */
  3172             PR_fprintf(inRequest->mFD, "<td valign=top>\n");
  3173             if (0 == traverse) {
  3174                 displayRes =
  3175                     displayCallsites(inRequest,
  3176                                      aAllocation->mEvents[traverse].mCallsite,
  3177                                      ST_FOLLOW_PARENTS, 0, "event-stack", "", __LINE__);
  3178                 if (0 != displayRes) {
  3179                     retval = __LINE__;
  3180                     REPORT_ERROR(__LINE__, displayCallsite);
  3183             PR_fprintf(inRequest->mFD, "</td>\n");
  3185             PR_fprintf(inRequest->mFD, "</tr>\n");
  3187         PR_fprintf(inRequest->mFD, "</table></div>\n");
  3189     else {
  3190         retval = __LINE__;
  3191         REPORT_ERROR(__LINE__, displayAllocationDetails);
  3194     return retval;
  3197 /*
  3198 ** compareCallsites
  3199 **
  3200 ** qsort callback.
  3201 ** Compare the callsites as specified by the options.
  3202 ** There must be NO equal callsites, unless they really are duplicates,
  3203 **  this is so that a duplicate detector loop can
  3204 **  simply skip sorted items until the callsite is different.
  3205 */
  3206 int
  3207 compareCallsites(const void *aSite1, const void *aSite2, void *aContext)
  3209     int retval = 0;
  3210     STRequest *inRequest = (STRequest *) aContext;
  3212     if (NULL != aSite1 && NULL != aSite2) {
  3213         tmcallsite *site1 = *((tmcallsite **) aSite1);
  3214         tmcallsite *site2 = *((tmcallsite **) aSite2);
  3216         if (NULL != site1 && NULL != site2) {
  3217             STRun *run1 = CALLSITE_RUN(site1);
  3218             STRun *run2 = CALLSITE_RUN(site2);
  3220             if (NULL != run1 && NULL != run2) {
  3221                 STCallsiteStats *stats1 =
  3222                     &(run1->mStats[inRequest->mContext->mIndex]);
  3223                 STCallsiteStats *stats2 =
  3224                     &(run2->mStats[inRequest->mContext->mIndex]);
  3226                 /*
  3227                  ** Logic determined by pref/option.
  3228                  */
  3229                 switch (inRequest->mOptions.mOrderBy) {
  3230                 case ST_WEIGHT:
  3232                         uint64_t weight164 = stats1->mWeight64;
  3233                         uint64_t weight264 = stats2->mWeight64;
  3235                         if (weight164 < weight264) {
  3236                             retval = __LINE__;
  3238                         else if (weight164 > weight264) {
  3239                             retval = -__LINE__;
  3242                     break;
  3244                 case ST_SIZE:
  3246                         uint32_t size1 = stats1->mSize;
  3247                         uint32_t size2 = stats2->mSize;
  3249                         if (size1 < size2) {
  3250                             retval = __LINE__;
  3252                         else if (size1 > size2) {
  3253                             retval = -__LINE__;
  3256                     break;
  3258                 case ST_TIMEVAL:
  3260                         uint64_t timeval164 = stats1->mTimeval64;
  3261                         uint64_t timeval264 = stats2->mTimeval64;
  3263                         if (timeval164 < timeval264) {
  3264                             retval = __LINE__;
  3266                         else if (timeval164 > timeval264) {
  3267                             retval = -__LINE__;
  3270                     break;
  3272                 case ST_COUNT:
  3274                         uint32_t count1 = stats1->mCompositeCount;
  3275                         uint32_t count2 = stats2->mCompositeCount;
  3277                         if (count1 < count2) {
  3278                             retval = __LINE__;
  3280                         else if (count1 > count2) {
  3281                             retval = -__LINE__;
  3284                     break;
  3286                 case ST_HEAPCOST:
  3288                         uint32_t cost1 = stats1->mHeapRuntimeCost;
  3289                         uint32_t cost2 = stats2->mHeapRuntimeCost;
  3291                         if (cost1 < cost2) {
  3292                             retval = __LINE__;
  3294                         else if (cost1 > cost2) {
  3295                             retval = -__LINE__;
  3298                     break;
  3300                 default:
  3302                         REPORT_ERROR(__LINE__, compareAllocations);
  3304                     break;
  3307                 /*
  3308                  ** If the return value is still zero, do a pointer compare.
  3309                  ** This makes sure we return zero, only iff the same object.
  3310                  */
  3311                 if (0 == retval) {
  3312                     if (stats1 < stats2) {
  3313                         retval = __LINE__;
  3315                     else if (stats1 > stats2) {
  3316                         retval = -__LINE__;
  3323     return retval;
  3326 /*
  3327 ** displayTopCallsites
  3328 **
  3329 ** Given a list of callsites, sort it, and output skipping dups.
  3330 ** The passed in callsite array is side effected, as in that it will come
  3331 **  back sorted.  This function will not release the array.
  3332 **
  3333 ** Note:  If the stamp passed in is non zero, then all callsites must match.
  3334 **  If the stamp is zero, all callsites must match global sorted run stamp.
  3335 **
  3336 ** Returns !0 on error.
  3337 */
  3338 int
  3339 displayTopCallsites(STRequest * inRequest, tmcallsite ** aCallsites,
  3340                     uint32_t aCallsiteCount, uint32_t aStamp,
  3341                     const char* id,
  3342                     const char* caption,
  3343                     int aRealName)
  3345     int retval = 0;
  3347     if (NULL != aCallsites && 0 < aCallsiteCount) {
  3348         uint32_t traverse = 0;
  3349         STRun *run = NULL;
  3350         tmcallsite *site = NULL;
  3351         int headerDisplayed = 0;
  3352         uint32_t displayed = 0;
  3354         /*
  3355          ** Fixup the stamp.
  3356          */
  3357         if (0 == aStamp && NULL != inRequest->mContext->mSortedRun) {
  3358             aStamp =
  3359                 inRequest->mContext->mSortedRun->mStats[inRequest->mContext->
  3360                                                         mIndex].mStamp;
  3363         /*
  3364          ** Sort the things.
  3365          */
  3366         NS_QuickSort(aCallsites, aCallsiteCount, sizeof(tmcallsite *),
  3367                      compareCallsites, inRequest);
  3369         /*
  3370          ** Time for output.
  3371          */
  3372         for (traverse = 0;
  3373              traverse < aCallsiteCount
  3374              && inRequest->mOptions.mListItemMax > displayed; traverse++) {
  3375             site = aCallsites[traverse];
  3376             run = CALLSITE_RUN(site);
  3378             /*
  3379              ** Only if the same stamp....
  3380              */
  3381             if (aStamp == run->mStats[inRequest->mContext->mIndex].mStamp) {
  3382                 /*
  3383                  ** We got a header yet?
  3384                  */
  3385                 if (0 == headerDisplayed) {
  3386                     static const char* const headers[] = {
  3387                         "Rank",
  3388                         "Callsite",
  3389                         "<abbr title=\"Composite Size\">Size</abbr>",
  3390                         "<abbr title=\"Composite Seconds\">Seconds</abbr>",
  3391                         "<abbr title=\"Composite Weight\">Weight</abbr>",
  3392                         "<abbr title=\"Heap Object Count\">Object Count</abbr>",
  3393                         "<abbr title=\"Composite Heap Operation Seconds\">C.H. Operation (sec)</abbr>"
  3394                     };
  3395                     headerDisplayed = __LINE__;
  3397                     htmlStartTable(inRequest, NULL, id, caption, headers,
  3398                                    sizeof(headers) / sizeof(headers[0]));
  3401                 displayed++;
  3403                 PR_fprintf(inRequest->mFD, "<tr>\n");
  3405                 /*
  3406                  ** Rank.
  3407                  */
  3408                 PR_fprintf(inRequest->mFD,
  3409                            "<td align=right valign=top>%u</td>\n", displayed);
  3411                 /*
  3412                  ** Method.
  3413                  */
  3414                 PR_fprintf(inRequest->mFD, "<td>");
  3415                 htmlCallsiteAnchor(inRequest, site, NULL, aRealName);
  3416                 PR_fprintf(inRequest->mFD, "</td>\n");
  3418                 /*
  3419                  ** Size.
  3420                  */
  3421                 PR_fprintf(inRequest->mFD,
  3422                            "<td align=right valign=top>%u</td>\n",
  3423                            run->mStats[inRequest->mContext->mIndex].mSize);
  3425                 /*
  3426                  ** Timeval.
  3427                  */
  3428                 PR_fprintf(inRequest->mFD,
  3429                            "<td align=right valign=top>" ST_TIMEVAL_FORMAT
  3430                            "</td>\n",
  3431                            ST_TIMEVAL_PRINTABLE64(run->
  3432                                                   mStats[inRequest->mContext->
  3433                                                          mIndex].mTimeval64));
  3435                 /*
  3436                  ** Weight.
  3437                  */
  3438                 PR_fprintf(inRequest->mFD,
  3439                            "<td align=right valign=top>%llu</td>\n",
  3440                            run->mStats[inRequest->mContext->mIndex].
  3441                            mWeight64);
  3443                 /*
  3444                  ** Allocation object count.
  3445                  */
  3446                 PR_fprintf(inRequest->mFD,
  3447                            "<td align=right valign=top>%u</td>\n",
  3448                            run->mStats[inRequest->mContext->mIndex].
  3449                            mCompositeCount);
  3451                 /*
  3452                  ** Heap operation seconds.
  3453                  */
  3454                 PR_fprintf(inRequest->mFD,
  3455                            "<td align=right valign=top>" ST_MICROVAL_FORMAT
  3456                            "</td>\n",
  3457                            ST_MICROVAL_PRINTABLE(run->
  3458                                                  mStats[inRequest->mContext->
  3459                                                         mIndex].
  3460                                                  mHeapRuntimeCost));
  3462                 PR_fprintf(inRequest->mFD, "</tr>\n");
  3465                 if (inRequest->mOptions.mListItemMax > displayed) {
  3466                     /*
  3467                      ** Skip any dups.
  3468                      */
  3469                     while (((traverse + 1) < aCallsiteCount)
  3470                            && (site == aCallsites[traverse + 1])) {
  3471                         traverse++;
  3477         /*
  3478          ** We need to terminate anything?
  3479          */
  3480         if (0 != headerDisplayed) {
  3481             PR_fprintf(inRequest->mFD, "</table></div>\n");
  3484     else {
  3485         retval = __LINE__;
  3486         REPORT_ERROR(__LINE__, displayTopCallsites);
  3489     return retval;
  3492 /*
  3493 ** displayCallsiteDetails
  3494 **
  3495 ** The callsite specific report.
  3496 ** Try to report what we know.
  3497 ** This one hits a little harder than the rest.
  3498 **
  3499 ** Returns !0 on error.
  3500 */
  3501 int
  3502 displayCallsiteDetails(STRequest * inRequest, tmcallsite * aCallsite)
  3504     int retval = 0;
  3506     if (NULL != aCallsite && NULL != aCallsite->method) {
  3507         STRun *sortedRun = NULL;
  3508         STRun *thisRun = CALLSITE_RUN(aCallsite);
  3509         const char *sourceFile = NULL;
  3511         sourceFile = resolveSourceFile(aCallsite->method);
  3513         PR_fprintf(inRequest->mFD, "<div class=\"callsite-header\">\n");
  3514         if (sourceFile) {
  3515             PR_fprintf(inRequest->mFD, "<b>%s</b>",
  3516                        tmmethodnode_name(aCallsite->method));
  3517             PR_fprintf(inRequest->mFD,
  3518                        " [<a href=\"http://lxr.mozilla.org/mozilla/source/%s#%u\" class=\"lxr\" target=\"_st_lxr\">%s:%u</a>]",
  3519                        aCallsite->method->sourcefile,
  3520                        aCallsite->method->linenumber, sourceFile,
  3521                        aCallsite->method->linenumber);
  3523         else {
  3524             PR_fprintf(inRequest->mFD,
  3525                        "<p><b>%s</b>+%u(%u) Callsite Details:</p>\n",
  3526                        tmmethodnode_name(aCallsite->method),
  3527                        aCallsite->offset, (uint32_t) aCallsite->entry.key);
  3530         PR_fprintf(inRequest->mFD, "</div>\n\n");
  3531         PR_fprintf(inRequest->mFD, "<div id=\"callsite-details\"><table class=\"data summary\">\n");
  3532         PR_fprintf(inRequest->mFD,
  3533                    "<tr><td>Composite Byte Size:</td><td align=right>%u</td></tr>\n",
  3534                    thisRun->mStats[inRequest->mContext->mIndex].mSize);
  3535         PR_fprintf(inRequest->mFD,
  3536                    "<tr><td>Composite Seconds:</td><td align=right>"
  3537                    ST_TIMEVAL_FORMAT "</td></tr>\n",
  3538                    ST_TIMEVAL_PRINTABLE64(thisRun->
  3539                                           mStats[inRequest->mContext->mIndex].
  3540                                           mTimeval64));
  3541         PR_fprintf(inRequest->mFD,
  3542                    "<tr><td>Composite Weight:</td><td align=right>%llu</td></tr>\n",
  3543                    thisRun->mStats[inRequest->mContext->mIndex].mWeight64);
  3544         PR_fprintf(inRequest->mFD,
  3545                    "<tr><td>Heap Object Count:</td><td align=right>%u</td></tr>\n",
  3546                    thisRun->mStats[inRequest->mContext->mIndex].
  3547                    mCompositeCount);
  3548         PR_fprintf(inRequest->mFD,
  3549                    "<tr><td>Heap Operation Seconds:</td><td align=right>"
  3550                    ST_MICROVAL_FORMAT "</td></tr>\n",
  3551                    ST_MICROVAL_PRINTABLE(thisRun->
  3552                                          mStats[inRequest->mContext->mIndex].
  3553                                          mHeapRuntimeCost));
  3554         PR_fprintf(inRequest->mFD, "</table></div>\n\n");
  3556         /*
  3557          ** Kids (callsites we call):
  3558          */
  3559         if (NULL != aCallsite->kids && NULL != aCallsite->kids->method) {
  3560             int displayRes = 0;
  3561             uint32_t siteCount = 0;
  3562             tmcallsite **sites = NULL;
  3564             /*
  3565              ** Collect the kid sibling callsites.
  3566              ** Doing it this way sorts them for relevance.
  3567              */
  3568             siteCount =
  3569                 callsiteArrayFromCallsite(&sites, 0, aCallsite->kids,
  3570                                           ST_FOLLOW_SIBLINGS);
  3571             if (0 != siteCount && NULL != sites) {
  3572                 /*
  3573                  ** Got something to show.
  3574                  */
  3575                 displayRes =
  3576                     displayTopCallsites(inRequest, sites, siteCount, 0,
  3577                                         "callsites",
  3578                                         "Children Callsites",
  3579                                         __LINE__);
  3580                 if (0 != displayRes) {
  3581                     retval = __LINE__;
  3582                     REPORT_ERROR(__LINE__, displayTopCallsites);
  3585                 /*
  3586                  ** Done with array.
  3587                  */
  3588                 free(sites);
  3589                 sites = NULL;
  3593         /*
  3594          ** Parents (those who call us):
  3595          */
  3596         if (NULL != aCallsite->parent && NULL != aCallsite->parent->method) {
  3597             int displayRes = 0;
  3599             displayRes =
  3600                 displayCallsites(inRequest, aCallsite->parent,
  3601                                  ST_FOLLOW_PARENTS, 0, "caller-stack", "Caller stack",
  3602                                  __LINE__);
  3603             if (0 != displayRes) {
  3604                 retval = __LINE__;
  3605                 REPORT_ERROR(__LINE__, displayCallsites);
  3609         /*
  3610          ** Allocations we did.
  3611          ** Simply harvest our own run.
  3612          */
  3613         sortedRun = createRun(inRequest->mContext, 0);
  3614         if (NULL != sortedRun) {
  3615             int harvestRes = 0;
  3617             harvestRes =
  3618                 harvestRun(CALLSITE_RUN(aCallsite), sortedRun,
  3619                            &inRequest->mOptions, inRequest->mContext);
  3620             if (0 == harvestRes) {
  3621                 if (0 != sortedRun->mAllocationCount) {
  3622                     int sortRes = 0;
  3624                     sortRes = sortRun(&inRequest->mOptions, sortedRun);
  3625                     if (0 == sortRes) {
  3626                         int displayRes = 0;
  3628                         displayRes =
  3629                             displayTopAllocations(inRequest, sortedRun,
  3630                                                   "allocations",
  3631                                                   "Allocations",
  3632                                                   0);
  3633                         if (0 != displayRes) {
  3634                             retval = __LINE__;
  3635                             REPORT_ERROR(__LINE__, displayTopAllocations);
  3638                     else {
  3639                         retval = __LINE__;
  3640                         REPORT_ERROR(__LINE__, sortRun);
  3644             else {
  3645                 retval = __LINE__;
  3646                 REPORT_ERROR(__LINE__, harvestRun);
  3649             /*
  3650              ** Done with the run.
  3651              */
  3652             freeRun(sortedRun);
  3653             sortedRun = NULL;
  3655         else {
  3656             retval = __LINE__;
  3657             REPORT_ERROR(__LINE__, createRun);
  3660     else {
  3661         retval = __LINE__;
  3662         REPORT_ERROR(__LINE__, displayCallsiteDetails);
  3665     return retval;
  3668 #if ST_WANT_GRAPHS
  3669 /*
  3670 ** graphFootprint
  3671 **
  3672 ** Output a PNG graph of the memory usage of the run.
  3673 **
  3674 ** Draw the graph within these boundaries.
  3675 **  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGHT-STGD_MARGIN
  3676 **
  3677 ** Returns !0 on failure.
  3678 */
  3679 int
  3680 graphFootprint(STRequest * inRequest, STRun * aRun)
  3682     int retval = 0;
  3684     if (NULL != aRun) {
  3685         uint32_t *YData = NULL;
  3686         uint32_t YDataArray[STGD_SPACE_X];
  3687         uint32_t traverse = 0;
  3688         uint32_t timeval = 0;
  3689         uint32_t loop = 0;
  3690         PRBool underLock = PR_FALSE;
  3692         /*
  3693          ** Decide if this is custom or we should use the cache.
  3694          */
  3695         if (aRun == inRequest->mContext->mSortedRun) {
  3696             YData = inRequest->mContext->mFootprintYData;
  3697             underLock = PR_TRUE;
  3699         else {
  3700             YData = YDataArray;
  3703         /*
  3704          **  Protect the shared data so that only one client has access to it
  3705          **      at any given time.
  3706          */
  3707         if (PR_FALSE != underLock) {
  3708             PR_Lock(inRequest->mContext->mImageLock);
  3711         /*
  3712          ** Only do the computations if we aren't cached already.
  3713          */
  3714         if (YData != inRequest->mContext->mFootprintYData
  3715             || PR_FALSE == inRequest->mContext->mFootprintCached) {
  3716             memset(YData, 0, sizeof(uint32_t) * STGD_SPACE_X);
  3718             /*
  3719              ** Initialize our Y data.
  3720              ** Pretty brutal loop here....
  3721              */
  3722             for (traverse = 0; 0 == retval && traverse < STGD_SPACE_X;
  3723                  traverse++) {
  3724                 /*
  3725                  ** Compute what timeval this Y data lands in.
  3726                  */
  3727                 timeval =
  3728                     ((traverse *
  3729                       (globals.mMaxTimeval -
  3730                        globals.mMinTimeval)) / STGD_SPACE_X) +
  3731                     globals.mMinTimeval;
  3733                 /*
  3734                  ** Loop over the run.
  3735                  ** Should an allocation contain said Timeval, we're good.
  3736                  */
  3737                 for (loop = 0; loop < aRun->mAllocationCount; loop++) {
  3738                     if (timeval >= aRun->mAllocations[loop]->mMinTimeval
  3739                         && timeval <= aRun->mAllocations[loop]->mMaxTimeval) {
  3740                         YData[traverse] +=
  3741                             byteSize(&inRequest->mOptions,
  3742                                      aRun->mAllocations[loop]);
  3747             /*
  3748              ** Did we cache this?
  3749              */
  3750             if (YData == inRequest->mContext->mFootprintYData) {
  3751                 inRequest->mContext->mFootprintCached = PR_TRUE;
  3755         /*
  3756          **  Done with the lock.
  3757          */
  3758         if (PR_FALSE != underLock) {
  3759             PR_Unlock(inRequest->mContext->mImageLock);
  3762         if (0 == retval) {
  3763             uint32_t minMemory = (uint32_t) - 1;
  3764             uint32_t maxMemory = 0;
  3765             int transparent = 0;
  3766             gdImagePtr graph = NULL;
  3768             /*
  3769              ** Go through and find the minimum and maximum sizes.
  3770              */
  3771             for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  3772                 if (YData[traverse] < minMemory) {
  3773                     minMemory = YData[traverse];
  3775                 if (YData[traverse] > maxMemory) {
  3776                     maxMemory = YData[traverse];
  3780             /*
  3781              ** We can now draw the graph.
  3782              */
  3783             graph = createGraph(&transparent);
  3784             if (NULL != graph) {
  3785                 gdSink theSink;
  3786                 int red = 0;
  3787                 int x1 = 0;
  3788                 int y1 = 0;
  3789                 int x2 = 0;
  3790                 int y2 = 0;
  3791                 uint32_t percents[11] =
  3792                     { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
  3793                 char *timevals[11];
  3794                 char *bytes[11];
  3795                 char timevalSpace[11][32];
  3796                 char byteSpace[11][32];
  3797                 int legendColors[1];
  3798                 const char *legends[1] = { "Memory in Use" };
  3799                 uint32_t cached = 0;
  3801                 /*
  3802                  ** Figure out what the labels will say.
  3803                  */
  3804                 for (traverse = 0; traverse < 11; traverse++) {
  3805                     timevals[traverse] = timevalSpace[traverse];
  3806                     bytes[traverse] = byteSpace[traverse];
  3808                     cached =
  3809                         ((globals.mMaxTimeval -
  3810                           globals.mMinTimeval) * percents[traverse]) / 100;
  3811                     PR_snprintf(timevals[traverse], 32, ST_TIMEVAL_FORMAT,
  3812                                 ST_TIMEVAL_PRINTABLE(cached));
  3813                     PR_snprintf(bytes[traverse], 32, "%u",
  3814                                 ((maxMemory -
  3815                                   minMemory) * percents[traverse]) / 100);
  3818                 red = gdImageColorAllocate(graph, 255, 0, 0);
  3819                 legendColors[0] = red;
  3821                 drawGraph(graph, -1, "Memory Footprint Over Time", "Seconds",
  3822                           "Bytes", 11, percents, (const char **) timevals, 11,
  3823                           percents, (const char **) bytes, 1, legendColors,
  3824                           legends);
  3826                 if (maxMemory != minMemory) {
  3827                     int64_t in64 = 0;
  3828                     int64_t ydata64 = 0;
  3829                     int64_t spacey64 = 0;
  3830                     int64_t mem64 = 0;
  3831                     int32_t in32 = 0;
  3833                     /*
  3834                      ** Go through our Y data and mark it up.
  3835                      */
  3836                     for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  3837                         x1 = traverse + STGD_MARGIN;
  3838                         y1 = STGD_HEIGHT - STGD_MARGIN;
  3840                         /*
  3841                          ** Need to do this math in 64 bits.
  3842                          */
  3843                         ydata64 = (int64_t)YData[traverse];
  3844                         spacey64 = (int64_t)STGD_SPACE_Y;
  3845                         mem64 = (int64_t)(maxMemory - minMemory);
  3847                         in64 = ydata64 * spacey64;
  3848                         in64 /= mem64;
  3849                         in32 = int32_t(in64);
  3851                         x2 = x1;
  3852                         y2 = y1 - in32;
  3854                         gdImageLine(graph, x1, y1, x2, y2, red);
  3859                 theSink.context = inRequest->mFD;
  3860                 theSink.sink = pngSink;
  3861                 gdImagePngToSink(graph, &theSink);
  3863                 gdImageDestroy(graph);
  3865             else {
  3866                 retval = __LINE__;
  3867                 REPORT_ERROR(__LINE__, createGraph);
  3871     else {
  3872         retval = __LINE__;
  3873         REPORT_ERROR(__LINE__, graphFootprint);
  3876     return retval;
  3878 #endif /* ST_WANT_GRAPHS */
  3880 #if ST_WANT_GRAPHS
  3881 /*
  3882 ** graphTimeval
  3883 **
  3884 ** Output a PNG graph of when the memory is allocated.
  3885 **
  3886 ** Draw the graph within these boundaries.
  3887 **  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGHT-STGD_MARGIN
  3888 **
  3889 ** Returns !0 on failure.
  3890 */
  3891 int
  3892 graphTimeval(STRequest * inRequest, STRun * aRun)
  3894     int retval = 0;
  3896     if (NULL != aRun) {
  3897         uint32_t *YData = NULL;
  3898         uint32_t YDataArray[STGD_SPACE_X];
  3899         uint32_t traverse = 0;
  3900         uint32_t timeval = globals.mMinTimeval;
  3901         uint32_t loop = 0;
  3902         PRBool underLock = PR_FALSE;
  3904         /*
  3905          ** Decide if this is custom or we should use the global cache.
  3906          */
  3907         if (aRun == inRequest->mContext->mSortedRun) {
  3908             YData = inRequest->mContext->mTimevalYData;
  3909             underLock = PR_TRUE;
  3911         else {
  3912             YData = YDataArray;
  3915         /*
  3916          **  Protect the shared data so that only one client has access to it
  3917          **      at any given time.
  3918          */
  3919         if (PR_FALSE != underLock) {
  3920             PR_Lock(inRequest->mContext->mImageLock);
  3923         /*
  3924          ** Only do the computations if we aren't cached already.
  3925          */
  3926         if (YData != inRequest->mContext->mTimevalYData
  3927             || PR_FALSE == inRequest->mContext->mTimevalCached) {
  3928             uint32_t prevTimeval = 0;
  3930             memset(YData, 0, sizeof(uint32_t) * STGD_SPACE_X);
  3932             /*
  3933              ** Initialize our Y data.
  3934              ** Pretty brutal loop here....
  3935              */
  3936             for (traverse = 0; 0 == retval && traverse < STGD_SPACE_X;
  3937                  traverse++) {
  3938                 /*
  3939                  ** Compute what timeval this Y data lands in.
  3940                  */
  3941                 prevTimeval = timeval;
  3942                 timeval =
  3943                     ((traverse *
  3944                       (globals.mMaxTimeval -
  3945                        globals.mMinTimeval)) / STGD_SPACE_X) +
  3946                     globals.mMinTimeval;
  3948                 /*
  3949                  ** Loop over the run.
  3950                  ** Should an allocation have been allocated between
  3951                  **  prevTimeval and timeval....
  3952                  */
  3953                 for (loop = 0; loop < aRun->mAllocationCount; loop++) {
  3954                     if (prevTimeval < aRun->mAllocations[loop]->mMinTimeval
  3955                         && timeval >= aRun->mAllocations[loop]->mMinTimeval) {
  3956                         YData[traverse] +=
  3957                             byteSize(&inRequest->mOptions,
  3958                                      aRun->mAllocations[loop]);
  3963             /*
  3964              ** Did we cache this?
  3965              */
  3966             if (YData == inRequest->mContext->mTimevalYData) {
  3967                 inRequest->mContext->mTimevalCached = PR_TRUE;
  3971         /*
  3972          **  Done with the lock.
  3973          */
  3974         if (PR_FALSE != underLock) {
  3975             PR_Unlock(inRequest->mContext->mImageLock);
  3978         if (0 == retval) {
  3979             uint32_t minMemory = (uint32_t) - 1;
  3980             uint32_t maxMemory = 0;
  3981             int transparent = 0;
  3982             gdImagePtr graph = NULL;
  3984             /*
  3985              ** Go through and find the minimum and maximum sizes.
  3986              */
  3987             for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  3988                 if (YData[traverse] < minMemory) {
  3989                     minMemory = YData[traverse];
  3991                 if (YData[traverse] > maxMemory) {
  3992                     maxMemory = YData[traverse];
  3996             /*
  3997              ** We can now draw the graph.
  3998              */
  3999             graph = createGraph(&transparent);
  4000             if (NULL != graph) {
  4001                 gdSink theSink;
  4002                 int red = 0;
  4003                 int x1 = 0;
  4004                 int y1 = 0;
  4005                 int x2 = 0;
  4006                 int y2 = 0;
  4007                 uint32_t percents[11] =
  4008                     { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
  4009                 char *timevals[11];
  4010                 char *bytes[11];
  4011                 char timevalSpace[11][32];
  4012                 char byteSpace[11][32];
  4013                 int legendColors[1];
  4014                 const char *legends[1] = { "Memory Allocated" };
  4015                 uint32_t cached = 0;
  4017                 /*
  4018                  ** Figure out what the labels will say.
  4019                  */
  4020                 for (traverse = 0; traverse < 11; traverse++) {
  4021                     timevals[traverse] = timevalSpace[traverse];
  4022                     bytes[traverse] = byteSpace[traverse];
  4024                     cached =
  4025                         ((globals.mMaxTimeval -
  4026                           globals.mMinTimeval) * percents[traverse]) / 100;
  4027                     PR_snprintf(timevals[traverse], 32, ST_TIMEVAL_FORMAT,
  4028                                 ST_TIMEVAL_PRINTABLE(cached));
  4029                     PR_snprintf(bytes[traverse], 32, "%u",
  4030                                 ((maxMemory -
  4031                                   minMemory) * percents[traverse]) / 100);
  4034                 red = gdImageColorAllocate(graph, 255, 0, 0);
  4035                 legendColors[0] = red;
  4037                 drawGraph(graph, -1, "Allocation Times", "Seconds", "Bytes",
  4038                           11, percents, (const char **) timevals, 11,
  4039                           percents, (const char **) bytes, 1, legendColors,
  4040                           legends);
  4042                 if (maxMemory != minMemory) {
  4043                     int64_t in64 = 0;
  4044                     int64_t ydata64 = 0;
  4045                     int64_t spacey64 = 0;
  4046                     int64_t mem64 = 0;
  4047                     int32_t in32 = 0;
  4049                     /*
  4050                      ** Go through our Y data and mark it up.
  4051                      */
  4052                     for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  4053                         x1 = traverse + STGD_MARGIN;
  4054                         y1 = STGD_HEIGHT - STGD_MARGIN;
  4056                         /*
  4057                          ** Need to do this math in 64 bits.
  4058                          */
  4059                         ydata64 = (int64_t)YData[traverse];
  4060                         spacey64 = (int64_t)STGD_SPACE_Y;
  4061                         mem64 = (int64_t)(maxMemory - minMemory);
  4063                         in64 = ydata64 * spacey64;
  4064                         in64 /= mem64;
  4065                         in32 = int32_t(in64);
  4067                         x2 = x1;
  4068                         y2 = y1 - in32;
  4070                         gdImageLine(graph, x1, y1, x2, y2, red);
  4075                 theSink.context = inRequest->mFD;
  4076                 theSink.sink = pngSink;
  4077                 gdImagePngToSink(graph, &theSink);
  4079                 gdImageDestroy(graph);
  4081             else {
  4082                 retval = __LINE__;
  4083                 REPORT_ERROR(__LINE__, createGraph);
  4087     else {
  4088         retval = __LINE__;
  4089         REPORT_ERROR(__LINE__, graphTimeval);
  4092     return retval;
  4094 #endif /* ST_WANT_GRAPHS */
  4096 #if ST_WANT_GRAPHS
  4097 /*
  4098 ** graphLifespan
  4099 **
  4100 ** Output a PNG graph of how long memory lived.
  4101 **
  4102 ** Draw the graph within these boundaries.
  4103 **  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGHT-STGD_MARGIN
  4104 **
  4105 ** Returns !0 on failure.
  4106 */
  4107 int
  4108 graphLifespan(STRequest * inRequest, STRun * aRun)
  4110     int retval = 0;
  4112     if (NULL != aRun) {
  4113         uint32_t *YData = NULL;
  4114         uint32_t YDataArray[STGD_SPACE_X];
  4115         uint32_t traverse = 0;
  4116         uint32_t timeval = 0;
  4117         uint32_t loop = 0;
  4118         PRBool underLock = PR_FALSE;
  4120         /*
  4121          ** Decide if this is custom or we should use the global cache.
  4122          */
  4123         if (aRun == inRequest->mContext->mSortedRun) {
  4124             YData = inRequest->mContext->mLifespanYData;
  4125             underLock = PR_TRUE;
  4127         else {
  4128             YData = YDataArray;
  4131         /*
  4132          **  Protect the shared data so that only one client has access to it
  4133          **      at any given time.
  4134          */
  4135         if (PR_FALSE != underLock) {
  4136             PR_Lock(inRequest->mContext->mImageLock);
  4139         /*
  4140          ** Only do the computations if we aren't cached already.
  4141          */
  4142         if (YData != inRequest->mContext->mLifespanYData
  4143             || PR_FALSE == inRequest->mContext->mLifespanCached) {
  4144             uint32_t prevTimeval = 0;
  4145             uint32_t lifespan = 0;
  4147             memset(YData, 0, sizeof(uint32_t) * STGD_SPACE_X);
  4149             /*
  4150              ** Initialize our Y data.
  4151              ** Pretty brutal loop here....
  4152              */
  4153             for (traverse = 0; 0 == retval && traverse < STGD_SPACE_X;
  4154                  traverse++) {
  4155                 /*
  4156                  ** Compute what timeval this Y data lands in.
  4157                  */
  4158                 prevTimeval = timeval;
  4159                 timeval =
  4160                     (traverse * (globals.mMaxTimeval - globals.mMinTimeval)) /
  4161                     STGD_SPACE_X;
  4163                 /*
  4164                  ** Loop over the run.
  4165                  ** Should an allocation have lived between
  4166                  **  prevTimeval and timeval....
  4167                  */
  4168                 for (loop = 0; loop < aRun->mAllocationCount; loop++) {
  4169                     lifespan =
  4170                         aRun->mAllocations[loop]->mMaxTimeval -
  4171                         aRun->mAllocations[loop]->mMinTimeval;
  4173                     if (prevTimeval < lifespan && timeval >= lifespan) {
  4174                         YData[traverse] +=
  4175                             byteSize(&inRequest->mOptions,
  4176                                      aRun->mAllocations[loop]);
  4181             /*
  4182              ** Did we cache this?
  4183              */
  4184             if (YData == inRequest->mContext->mLifespanYData) {
  4185                 inRequest->mContext->mLifespanCached = PR_TRUE;
  4189         /*
  4190          **  Done with the lock.
  4191          */
  4192         if (PR_FALSE != underLock) {
  4193             PR_Unlock(inRequest->mContext->mImageLock);
  4196         if (0 == retval) {
  4197             uint32_t minMemory = (uint32_t) - 1;
  4198             uint32_t maxMemory = 0;
  4199             int transparent = 0;
  4200             gdImagePtr graph = NULL;
  4202             /*
  4203              ** Go through and find the minimum and maximum sizes.
  4204              */
  4205             for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  4206                 if (YData[traverse] < minMemory) {
  4207                     minMemory = YData[traverse];
  4209                 if (YData[traverse] > maxMemory) {
  4210                     maxMemory = YData[traverse];
  4214             /*
  4215              ** We can now draw the graph.
  4216              */
  4217             graph = createGraph(&transparent);
  4218             if (NULL != graph) {
  4219                 gdSink theSink;
  4220                 int red = 0;
  4221                 int x1 = 0;
  4222                 int y1 = 0;
  4223                 int x2 = 0;
  4224                 int y2 = 0;
  4225                 uint32_t percents[11] =
  4226                     { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
  4227                 char *timevals[11];
  4228                 char *bytes[11];
  4229                 char timevalSpace[11][32];
  4230                 char byteSpace[11][32];
  4231                 int legendColors[1];
  4232                 const char *legends[1] = { "Live Memory" };
  4233                 uint32_t cached = 0;
  4235                 /*
  4236                  ** Figure out what the labels will say.
  4237                  */
  4238                 for (traverse = 0; traverse < 11; traverse++) {
  4239                     timevals[traverse] = timevalSpace[traverse];
  4240                     bytes[traverse] = byteSpace[traverse];
  4242                     cached =
  4243                         ((globals.mMaxTimeval -
  4244                           globals.mMinTimeval) * percents[traverse]) / 100;
  4245                     PR_snprintf(timevals[traverse], 32, ST_TIMEVAL_FORMAT,
  4246                                 ST_TIMEVAL_PRINTABLE(cached));
  4247                     PR_snprintf(bytes[traverse], 32, "%u",
  4248                                 ((maxMemory -
  4249                                   minMemory) * percents[traverse]) / 100);
  4252                 red = gdImageColorAllocate(graph, 255, 0, 0);
  4253                 legendColors[0] = red;
  4255                 drawGraph(graph, -1, "Allocation Lifespans", "Lifespan",
  4256                           "Bytes", 11, percents, (const char **) timevals, 11,
  4257                           percents, (const char **) bytes, 1, legendColors,
  4258                           legends);
  4260                 if (maxMemory != minMemory) {
  4261                     int64_t in64 = 0;
  4262                     int64_t ydata64 = 0;
  4263                     int64_t spacey64 = 0;
  4264                     int64_t mem64 = 0;
  4265                     int32_t in32 = 0;
  4267                     /*
  4268                      ** Go through our Y data and mark it up.
  4269                      */
  4270                     for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  4271                         x1 = traverse + STGD_MARGIN;
  4272                         y1 = STGD_HEIGHT - STGD_MARGIN;
  4274                         /*
  4275                          ** Need to do this math in 64 bits.
  4276                          */
  4277                         ydata64 = (int64_t)YData[traverse];
  4278                         spacey64 = (int64_t)STGD_SPACE_Y;
  4279                         mem64 = (int64_t)(maxMemory - minMemory);
  4281                         in64 = ydata64 * spacey64;
  4282                         in64 /= mem64;
  4283                         in32 = int32_t(in64);
  4285                         x2 = x1;
  4286                         y2 = y1 - in32;
  4288                         gdImageLine(graph, x1, y1, x2, y2, red);
  4293                 theSink.context = inRequest->mFD;
  4294                 theSink.sink = pngSink;
  4295                 gdImagePngToSink(graph, &theSink);
  4297                 gdImageDestroy(graph);
  4299             else {
  4300                 retval = __LINE__;
  4301                 REPORT_ERROR(__LINE__, createGraph);
  4305     else {
  4306         retval = __LINE__;
  4307         REPORT_ERROR(__LINE__, graphLifespan);
  4310     return retval;
  4312 #endif /* ST_WANT_GRAPHS */
  4314 #if ST_WANT_GRAPHS
  4315 /*
  4316 ** graphWeight
  4317 **
  4318 ** Output a PNG graph of Allocations by Weight
  4319 **
  4320 ** Draw the graph within these boundaries.
  4321 **  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGHT-STGD_MARGIN
  4322 **
  4323 ** Returns !0 on failure.
  4324 */
  4325 int
  4326 graphWeight(STRequest * inRequest, STRun * aRun)
  4328     int retval = 0;
  4330     if (NULL != aRun) {
  4331         uint64_t *YData64 = NULL;
  4332         uint64_t YDataArray64[STGD_SPACE_X];
  4333         uint32_t traverse = 0;
  4334         uint32_t timeval = globals.mMinTimeval;
  4335         uint32_t loop = 0;
  4336         PRBool underLock = PR_FALSE;
  4338         /*
  4339          ** Decide if this is custom or we should use the global cache.
  4340          */
  4341         if (aRun == inRequest->mContext->mSortedRun) {
  4342             YData64 = inRequest->mContext->mWeightYData64;
  4343             underLock = PR_TRUE;
  4345         else {
  4346             YData64 = YDataArray64;
  4349         /*
  4350          **  Protect the shared data so that only one client has access to it
  4351          **      at any given time.
  4352          */
  4353         if (PR_FALSE != underLock) {
  4354             PR_Lock(inRequest->mContext->mImageLock);
  4357         /*
  4358          ** Only do the computations if we aren't cached already.
  4359          */
  4360         if (YData64 != inRequest->mContext->mWeightYData64
  4361             || PR_FALSE == inRequest->mContext->mWeightCached) {
  4362             uint32_t prevTimeval = 0;
  4364             memset(YData64, 0, sizeof(uint64_t) * STGD_SPACE_X);
  4366             /*
  4367              ** Initialize our Y data.
  4368              ** Pretty brutal loop here....
  4369              */
  4370             for (traverse = 0; 0 == retval && traverse < STGD_SPACE_X;
  4371                  traverse++) {
  4372                 /*
  4373                  ** Compute what timeval this Y data lands in.
  4374                  */
  4375                 prevTimeval = timeval;
  4376                 timeval =
  4377                     ((traverse *
  4378                       (globals.mMaxTimeval -
  4379                        globals.mMinTimeval)) / STGD_SPACE_X) +
  4380                     globals.mMinTimeval;
  4382                 /*
  4383                  ** Loop over the run.
  4384                  ** Should an allocation have been allocated between
  4385                  **  prevTimeval and timeval....
  4386                  */
  4387                 for (loop = 0; loop < aRun->mAllocationCount; loop++) {
  4388                     if (prevTimeval < aRun->mAllocations[loop]->mMinTimeval
  4389                         && timeval >= aRun->mAllocations[loop]->mMinTimeval) {
  4390                         uint64_t size64 = 0;
  4391                         uint64_t lifespan64 = 0;
  4392                         uint64_t weight64 = 0;
  4394                         size64 = byteSize(&inRequest->mOptions,
  4395                                           aRun->mAllocations[loop]);
  4396                         lifespan64 = aRun->mAllocations[loop]->mMaxTimeval -
  4397                                      aRun->mAllocations[loop]->mMinTimeval;
  4398                         weight64 = size64 * lifespan64;
  4400                         YData64[traverse] += weight64;
  4405             /*
  4406              ** Did we cache this?
  4407              */
  4408             if (YData64 == inRequest->mContext->mWeightYData64) {
  4409                 inRequest->mContext->mWeightCached = PR_TRUE;
  4413         /*
  4414          **  Done with the lock.
  4415          */
  4416         if (PR_FALSE != underLock) {
  4417             PR_Unlock(inRequest->mContext->mImageLock);
  4420         if (0 == retval) {
  4421             uint64_t minWeight64 = (0xFFFFFFFFLL << 32) + 0xFFFFFFFFLL;
  4422             uint64_t maxWeight64 = 0;
  4423             int transparent = 0;
  4424             gdImagePtr graph = NULL;
  4426             /*
  4427              ** Go through and find the minimum and maximum weights.
  4428              */
  4429             for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  4430                 if (YData64[traverse] < minWeight64) {
  4431                     minWeight64 = YData64[traverse];
  4433                 if (YData64[traverse] > maxWeight64) {
  4434                     maxWeight64 = YData64[traverse];
  4438             /*
  4439              ** We can now draw the graph.
  4440              */
  4441             graph = createGraph(&transparent);
  4442             if (NULL != graph) {
  4443                 gdSink theSink;
  4444                 int red = 0;
  4445                 int x1 = 0;
  4446                 int y1 = 0;
  4447                 int x2 = 0;
  4448                 int y2 = 0;
  4449                 uint32_t percents[11] =
  4450                     { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
  4451                 char *timevals[11];
  4452                 char *bytes[11];
  4453                 char timevalSpace[11][32];
  4454                 char byteSpace[11][32];
  4455                 int legendColors[1];
  4456                 const char *legends[1] = { "Memory Weight" };
  4457                 uint64_t percent64 = 0;
  4458                 uint64_t result64 = 0;
  4460                 uint32_t cached = 0;
  4461                 uint64_t hundred64 = 100;
  4463                 /*
  4464                  ** Figure out what the labels will say.
  4465                  */
  4466                 for (traverse = 0; traverse < 11; traverse++) {
  4467                     timevals[traverse] = timevalSpace[traverse];
  4468                     bytes[traverse] = byteSpace[traverse];
  4470                     cached =
  4471                         ((globals.mMaxTimeval -
  4472                           globals.mMinTimeval) * percents[traverse]) / 100;
  4473                     PR_snprintf(timevals[traverse], 32, ST_TIMEVAL_FORMAT,
  4474                                 ST_TIMEVAL_PRINTABLE(cached));
  4476                     result64 = (maxWeight64 - minWeight64) * percents[traverse];
  4477                     result64 /= hundred64;
  4478                     PR_snprintf(bytes[traverse], 32, "%llu", result64);
  4481                 red = gdImageColorAllocate(graph, 255, 0, 0);
  4482                 legendColors[0] = red;
  4484                 drawGraph(graph, -1, "Allocation Weights", "Seconds",
  4485                           "Weight", 11, percents, (const char **) timevals,
  4486                           11, percents, (const char **) bytes, 1,
  4487                           legendColors, legends);
  4489                 if (maxWeight64 != minWeight64) {
  4490                     int64_t in64 = 0;
  4491                     int64_t spacey64 = 0;
  4492                     int64_t weight64 = 0;
  4493                     int32_t in32 = 0;
  4495                     /*
  4496                      ** Go through our Y data and mark it up.
  4497                      */
  4498                     for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  4499                         x1 = traverse + STGD_MARGIN;
  4500                         y1 = STGD_HEIGHT - STGD_MARGIN;
  4502                         /*
  4503                          ** Need to do this math in 64 bits.
  4504                          */
  4505                         spacey64 = (int64_t)STGD_SPACE_Y;
  4506                         weight64 = maxWeight64 - minWeight64;
  4508                         in64 = YData64[traverse] * spacey64;
  4509                         in64 /= weight64;
  4510                         in32 = int32_t(in64);
  4512                         x2 = x1;
  4513                         y2 = y1 - in32;
  4515                         gdImageLine(graph, x1, y1, x2, y2, red);
  4520                 theSink.context = inRequest->mFD;
  4521                 theSink.sink = pngSink;
  4522                 gdImagePngToSink(graph, &theSink);
  4524                 gdImageDestroy(graph);
  4526             else {
  4527                 retval = __LINE__;
  4528                 REPORT_ERROR(__LINE__, createGraph);
  4532     else {
  4533         retval = __LINE__;
  4534         REPORT_ERROR(__LINE__, graphWeight);
  4537     return retval;
  4539 #endif /* ST_WANT_GRAPHS */
  4541 #define ST_WEB_OPTION_BOOL(option_name, option_genre, option_help) \
  4542     { \
  4543         uint32_t convert = (uint32_t)outOptions->m##option_name; \
  4545         getDataPRUint32(inFormData, #option_name, 1, &convert, 1); \
  4546         outOptions->m##option_name = (PRBool)convert; \
  4548 #define ST_WEB_OPTION_STRING(option_name, option_genre, default_value, option_help) \
  4549     getDataString(inFormData, #option_name, 1, outOptions->m##option_name, sizeof(outOptions->m##option_name));
  4550 #define ST_WEB_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
  4551     { \
  4552         uint32_t loop = 0; \
  4553         uint32_t found = 0; \
  4554         char buffer[ST_OPTION_STRING_MAX]; \
  4556         for(loop = 0; loop < array_size; loop++) \
  4557         { \
  4558             buffer[0] = '\0'; \
  4559             getDataString(inFormData, #option_name, (loop + 1), buffer, sizeof(buffer)); \
  4561             if('\0' != buffer[0]) \
  4562             { \
  4563                 PR_snprintf(outOptions->m##option_name[found], sizeof(outOptions->m##option_name[found]), "%s", buffer); \
  4564                 found++; \
  4565             } \
  4566         } \
  4568         for(; found < array_size; found++) \
  4569         { \
  4570             outOptions->m##option_name[found][0] = '\0'; \
  4571         } \
  4573 #define ST_WEB_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help)  /* no implementation */
  4574 #define ST_WEB_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
  4575     getDataPRUint32(inFormData, #option_name, 1, &outOptions->m##option_name, multiplier);
  4576 #define ST_WEB_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
  4577     { \
  4578         uint64_t mul64 = multiplier; \
  4580         getDataPRUint64(inFormData, #option_name, 1, &outOptions->m##option_name##64, mul64); \
  4582 /*
  4583 **  fillOptions
  4584 **
  4585 **  Given an appropriate hexcaped string, distill the option values
  4586 **      and fill the given STOption struct.
  4587 **
  4588 **  Note that the options passed in are not touched UNLESS there is
  4589 **      a replacement found in the form data.
  4590 */
  4591 void
  4592 fillOptions(STOptions * outOptions, const FormData * inFormData)
  4594     if (NULL != outOptions && NULL != inFormData) {
  4596 #include "stoptions.h"
  4598         /*
  4599          **  Special sanity check here for some options that need data validation.
  4600          */
  4601         if (!outOptions->mCategoryName[0]
  4602             || !findCategoryNode(outOptions->mCategoryName, &globals)) {
  4603             PR_snprintf(outOptions->mCategoryName,
  4604                         sizeof(outOptions->mCategoryName), "%s",
  4605                         ST_ROOT_CATEGORY_NAME);
  4611 void
  4612 displayOptionString(STRequest * inRequest,
  4613                     const char *option_name,
  4614                     const char *option_genre,
  4615                     const char *default_value,
  4616                     const char *option_help, const char *value)
  4618 #if 0
  4619     PR_fprintf(inRequest->mFD, "<input type=submit value=%s>\n", option_name);
  4620 #endif
  4621     PR_fprintf(inRequest->mFD, "<div class=option-box>\n");
  4622     PR_fprintf(inRequest->mFD, "<p class=option-name>%s</p>\n", option_name);
  4623     PR_fprintf(inRequest->mFD,
  4624                "<input type=text name=\"%s\" value=\"%s\">\n",
  4625                option_name, value);
  4626     PR_fprintf(inRequest->mFD,
  4627                "<p class=option-default>Default value is \"%s\".</p>\n<p class=option-help>%s</p>\n",
  4628                default_value, option_help);
  4629     PR_fprintf(inRequest->mFD, "</div>\n");
  4632 static void
  4633 displayOptionStringArray(STRequest * inRequest,
  4634                          const char *option_name,
  4635                          const char *option_genre,
  4636                          uint32_t array_size,
  4637                          const char *option_help, const char values[5]
  4638                          [ST_OPTION_STRING_MAX])
  4640     /* values should not be a fixed length! */
  4641     PR_ASSERT(array_size == 5);
  4642 #if 0
  4643     PR_fprintf(inRequest->mFD, "<input type=submit value=%s>\n", option_name);
  4644 #endif
  4645     PR_fprintf(inRequest->mFD, "<div class=\"option-box\">\n");
  4646     PR_fprintf(inRequest->mFD, "<p class=option-name>%s</p>\n", option_name); {
  4647         uint32_t loop = 0;
  4649         for (loop = 0; loop < array_size; loop++) {
  4650             PR_fprintf(inRequest->mFD,
  4651                        "<input type=text name=\"%s\" value=\"%s\"><br>\n",
  4652                        option_name, values[loop]);
  4655     PR_fprintf(inRequest->mFD,
  4656                "<p class=option-default>Up to %u occurrences allowed.</p>\n<p class=option-help>%s</p>\n",
  4657                array_size, option_help);
  4658     PR_fprintf(inRequest->mFD, "</div>\n");
  4661 static void
  4662 displayOptionInt(STRequest * inRequest,
  4663                  const char *option_name,
  4664                  const char *option_genre,
  4665                  uint32_t default_value,
  4666                  uint32_t multiplier, const char *option_help, uint32_t value)
  4668 #if 0
  4669     PR_fprintf(inRequest->mFD, "<input type=submit value=%s>\n", option_name);
  4670 #endif
  4671     PR_fprintf(inRequest->mFD, "<div class=\"option-box\">\n");
  4672     PR_fprintf(inRequest->mFD, "<p class=option-name>%s</p>\n", option_name);
  4673     PR_fprintf(inRequest->mFD,
  4674                "<input type=text name=%s value=%u>\n", option_name,
  4675                value / multiplier);
  4676     PR_fprintf(inRequest->mFD,
  4677                "<p class=option-default>Default value is %u.</p>\n<p class=option-help>%s</p>\n",
  4678                default_value, option_help);
  4679     PR_fprintf(inRequest->mFD, "</div>\n");
  4682 static void
  4683 displayOptionInt64(STRequest * inRequest,
  4684                    const char *option_name,
  4685                    const char *option_genre,
  4686                    uint64_t default_value,
  4687                    uint64_t multiplier,
  4688                    const char *option_help, uint64_t value)
  4690 #if 0
  4691     PR_fprintf(inRequest->mFD, "<input type=submit value=%s>\n", option_name);
  4692 #endif
  4693     PR_fprintf(inRequest->mFD, "<div class=\"option-box\">\n");
  4694     PR_fprintf(inRequest->mFD, "<p class=option-name>%s</p>\n", option_name); {
  4695         uint64_t def64 = default_value;
  4696         uint64_t mul64 = multiplier;
  4697         uint64_t div64;
  4699         div64 = value / mul64;
  4700         PR_fprintf(inRequest->mFD,
  4701                    "<input type=text name=%s value=%llu>\n",
  4702                    option_name, div64);
  4703         PR_fprintf(inRequest->mFD,
  4704                    "<p class=option-default>Default value is %llu.</p>\n<p class=option-help>%s</p>\n",
  4705                    def64, option_help);
  4707     PR_fprintf(inRequest->mFD, "</div>\n");
  4710 /*
  4711 **  displaySettings
  4712 **
  4713 **  Present the settings for change during execution.
  4714 */
  4715 void
  4716 displaySettings(STRequest * inRequest)
  4718     int applyRes = 0;
  4720     /*
  4721      ** We've got a form to create.
  4722      */
  4723     PR_fprintf(inRequest->mFD, "<form method=get action=\"./index.html\">\n");
  4724     /*
  4725      **  Respect newlines in help text.
  4726      */
  4727 #if 0
  4728     PR_fprintf(inRequest->mFD, "<pre>\n");
  4729 #endif
  4730 #define ST_WEB_OPTION_BOOL(option_name, option_genre, option_help) \
  4731     displayOptionBool(option_name, option_genre, option_help)
  4732 #define ST_WEB_OPTION_STRING(option_name, option_genre, default_value, option_help) \
  4733     displayOptionString(inRequest, #option_name, #option_genre, default_value, option_help, inRequest->mOptions.m##option_name);
  4734 #define ST_WEB_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
  4735     displayOptionStringArray(inRequest, #option_name, #option_genre, array_size, option_help, inRequest->mOptions.m##option_name);
  4736 #define ST_WEB_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help)  /* no implementation */
  4737 #define ST_WEB_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
  4738     displayOptionInt(inRequest, #option_name, #option_genre, default_value, multiplier, option_help, inRequest->mOptions.m##option_name);
  4739 #define ST_WEB_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
  4740     displayOptionInt64(inRequest, #option_name, #option_genre, default_value, multiplier, option_help, inRequest->mOptions.m##option_name##64);
  4741 #include "stoptions.h"
  4742     /*
  4743      **  Give a submit/reset button, obligatory.
  4744      **  Done respecting newlines in help text.
  4745      */
  4746     PR_fprintf(inRequest->mFD,
  4747                "<input type=submit value=\"Save Options\"> <input type=reset>\n");
  4748 #if 0
  4749     PR_fprintf(inRequest->mFD, "</pre>\n");
  4750 #endif
  4751     /*
  4752      ** Done with form.
  4753      */
  4754     PR_fprintf(inRequest->mFD, "</form>\n");
  4757 int
  4758 handleLocalFile(STRequest * inRequest, const char *aFilename)
  4760     static const char *const local_files[] = {
  4761         "spacetrace.css",
  4762     };
  4763     static const size_t local_file_count =
  4764         sizeof(local_files) / sizeof(local_files[0]);
  4765     size_t i;
  4767     for (i = 0; i < local_file_count; i++) {
  4768         if (0 == strcmp(local_files[i], aFilename))
  4769             return 1;
  4771     return 0;
  4774 /*
  4775 ** displayFile
  4776 **
  4777 ** reads a file from disk, and streams it to the request
  4778 */
  4779 int
  4780 displayFile(STRequest * inRequest, const char *aFilename)
  4782     PRFileDesc *inFd;
  4783     const char *filepath =
  4784         PR_smprintf("res%c%s", PR_GetDirectorySeparator(), aFilename);
  4785     char buffer[2048];
  4786     int32_t readRes;
  4788     inFd = PR_Open(filepath, PR_RDONLY, PR_IRUSR);
  4789     if (!inFd)
  4790         return -1;
  4791     while ((readRes = PR_Read(inFd, buffer, sizeof(buffer))) > 0) {
  4792         PR_Write(inRequest->mFD, buffer, readRes);
  4794     if (readRes != 0)
  4795         return -1;
  4796     PR_Close(inFd);
  4797     return 0;
  4800 /*
  4801 ** displayIndex
  4802 **
  4803 ** Present a list of the reports you can drill down into.
  4804 ** Returns !0 on failure.
  4805 */
  4806 int
  4807 displayIndex(STRequest * inRequest)
  4809     int retval = 0;
  4810     STOptions *options = &inRequest->mOptions;
  4812     /*
  4813      ** Present reports in a list format.
  4814      */
  4815     PR_fprintf(inRequest->mFD, "<ul>");
  4816     PR_fprintf(inRequest->mFD, "\n<li>");
  4817     htmlAnchor(inRequest, "root_callsites.html", "Root Callsites",
  4818                NULL, "mainmenu", options);
  4819     PR_fprintf(inRequest->mFD, "\n<li>");
  4820     htmlAnchor(inRequest, "categories_summary.html",
  4821                "Categories Report", NULL, "mainmenu", options);
  4822     PR_fprintf(inRequest->mFD, "\n<li>");
  4823     htmlAnchor(inRequest, "top_callsites.html",
  4824                "Top Callsites Report", NULL, "mainmenu", options);
  4825     PR_fprintf(inRequest->mFD, "\n<li>");
  4826     htmlAnchor(inRequest, "top_allocations.html",
  4827                "Top Allocations Report", NULL, "mainmenu", options);
  4828     PR_fprintf(inRequest->mFD, "\n<li>");
  4829     htmlAnchor(inRequest, "memory_leaks.html",
  4830                "Memory Leak Report", NULL, "mainmenu", options);
  4831 #if ST_WANT_GRAPHS
  4832     PR_fprintf(inRequest->mFD, "\n<li>Graphs");
  4833     PR_fprintf(inRequest->mFD, "<ul>");
  4834     PR_fprintf(inRequest->mFD, "\n<li>");
  4835     htmlAnchor(inRequest, "footprint_graph.html", "Footprint",
  4836                NULL, "mainmenu graph", options);
  4837     PR_fprintf(inRequest->mFD, "\n<li>");
  4838     htmlAnchor(inRequest, "lifespan_graph.html",
  4839                "Allocation Lifespans", NULL, "mainmenu graph", options);
  4840     PR_fprintf(inRequest->mFD, "\n<li>");
  4841     htmlAnchor(inRequest, "times_graph.html", "Allocation Times",
  4842                NULL, "mainmenu graph", options);
  4843     PR_fprintf(inRequest->mFD, "\n<li>");
  4844     htmlAnchor(inRequest, "weight_graph.html",
  4845                "Allocation Weights", NULL, "mainmenu graph", options);
  4846     PR_fprintf(inRequest->mFD, "\n</ul>\n");
  4847 #endif /* ST_WANT_GRAPHS */
  4848     PR_fprintf(inRequest->mFD, "\n</ul>\n");
  4849     return retval;
  4852 /*
  4853 **  initRequestOptions
  4854 **
  4855 **  Given the request, set the options that are specific to the request.
  4856 **  These can generally be determined in the following manner:
  4857 **      Copy over global options.
  4858 **      If getData present, attempt to use options therein.
  4859 */
  4860 void
  4861 initRequestOptions(STRequest * inRequest)
  4863     if (NULL != inRequest) {
  4864         /*
  4865          **  Copy of global options.
  4866          */
  4867         memcpy(&inRequest->mOptions, &globals.mCommandLineOptions,
  4868                sizeof(globals.mCommandLineOptions));
  4869         /*
  4870          **  Decide what will override global options if anything.
  4871          */
  4872         if (NULL != inRequest->mGetData) {
  4873             fillOptions(&inRequest->mOptions, inRequest->mGetData);
  4878 STContext *
  4879 contextLookup(STOptions * inOptions)
  4880 /*
  4881 **  Lookup a context that matches the options.
  4882 **  The lookup may block, especially if the context needs to be created.
  4883 **  Callers of this API must eventually call contextRelease with the
  4884 **      return value;  failure to do so will cause this applications
  4885 **      to eventually not work as advertised.
  4886 **
  4887 **  inOptions   The options determine which context is relevant.
  4888 **  returns     The fully completed context on success.
  4889 **              The context is read only in practice, so please do not
  4890 **                  write to it or anything it points to.
  4891 **              NULL on failure.
  4892 */
  4894     STContext *retval = NULL;
  4895     STContextCache *inCache = &globals.mContextCache;
  4897     if (NULL != inOptions && NULL != inCache) {
  4898         uint32_t loop = 0;
  4899         STContext *categoryException = NULL;
  4900         PRBool newContext = PR_FALSE;
  4901         PRBool evictContext = PR_FALSE;
  4902         PRBool changeCategoryContext = PR_FALSE;
  4904         /*
  4905          **  Own the context cache while we are in here.
  4906          */
  4907         PR_Lock(inCache->mLock);
  4908         /*
  4909          **  Loop until successful.
  4910          **  Waiting on the condition variable makes sure we don't hog the
  4911          **      lock below.
  4912          */
  4913         while (1) {
  4914             /*
  4915              **  Go over the cache items.
  4916              **  At this point we are looking for a cache hit, with multiple
  4917              **      readers.
  4918              */
  4919             for (loop = 0; loop < inCache->mItemCount; loop++) {
  4920                 /*
  4921                  **  Must be in use.
  4922                  */
  4923                 if (PR_FALSE != inCache->mItems[loop].mInUse) {
  4924                     int delta[(STOptionGenre) MaxGenres];
  4926                     /*
  4927                      **  Compare the relevant options, figure out if different
  4928                      **      in any genre that we care about.
  4929                      */
  4930                     memset(&delta, 0, sizeof(delta));
  4931 #define ST_WEB_OPTION_BOOL(option_name, option_genre, option_help) \
  4932     if(inOptions->m##option_name != inCache->mItems[loop].mOptions.m##option_name) \
  4933     { \
  4934         delta[(STOptionGenre)option_genre]++; \
  4936 #define ST_WEB_OPTION_STRING(option_name, option_genre, default_value, option_help) \
  4937     if(0 != strcmp(inOptions->m##option_name, inCache->mItems[loop].mOptions.m##option_name)) \
  4938     { \
  4939         delta[(STOptionGenre)option_genre]++; \
  4941 #define ST_WEB_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
  4942     { \
  4943         uint32_t macro_loop = 0; \
  4945         for(macro_loop = 0; macro_loop < array_size; macro_loop++) \
  4946         { \
  4947             if(0 != strcmp(inOptions->m##option_name[macro_loop], inCache->mItems[loop].mOptions.m##option_name[macro_loop])) \
  4948             { \
  4949                 break; \
  4950             } \
  4951         } \
  4953         if(macro_loop != array_size) \
  4954         { \
  4955             delta[(STOptionGenre)option_genre]++; \
  4956         } \
  4958 #define ST_WEB_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help)  /* no implementation */
  4959 #define ST_WEB_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
  4960     if(inOptions->m##option_name != inCache->mItems[loop].mOptions.m##option_name) \
  4961     { \
  4962         delta[(STOptionGenre)option_genre]++; \
  4964 #define ST_WEB_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
  4965     if(inOptions->m##option_name##64 != inCache->mItems[loop].mOptions.m##option_name##64) \
  4966     { \
  4967         delta[(STOptionGenre)option_genre]++; \
  4969 #include "stoptions.h"
  4970                     /*
  4971                      **  If there is no genre out of alignment, we accept this as the context.
  4972                      */
  4973                     if (0 == delta[CategoryGenre] &&
  4974                         0 == delta[DataSortGenre] &&
  4975                         0 == delta[DataSetGenre] && 0 == delta[DataSizeGenre]
  4976                         ) {
  4977                         retval = &inCache->mItems[loop].mContext;
  4978                         break;
  4981                     /*
  4982                      **  A special exception to the rule here.
  4983                      **  If all that is different is the category genre, and there
  4984                      **      is no one looking at the context (zero ref count),
  4985                      **      then there is some magic we can perform.
  4986                      */
  4987                     if (NULL == retval &&
  4988                         0 == inCache->mItems[loop].mReferenceCount &&
  4989                         0 != delta[CategoryGenre] &&
  4990                         0 == delta[DataSortGenre] &&
  4991                         0 == delta[DataSetGenre] && 0 == delta[DataSizeGenre]
  4992                         ) {
  4993                         categoryException = &inCache->mItems[loop].mContext;
  4998             /*
  4999              **  Pick up our category exception if relevant.
  5000              */
  5001             if (NULL == retval && NULL != categoryException) {
  5002                 retval = categoryException;
  5003                 categoryException = NULL;
  5004                 changeCategoryContext = PR_TRUE;
  5007             /*
  5008              **  If we don't have a cache hit, then we need to check for an empty
  5009              **      spot to take over.
  5010              */
  5011             if (NULL == retval) {
  5012                 for (loop = 0; loop < inCache->mItemCount; loop++) {
  5013                     /*
  5014                      **  Must NOT be in use, then it will be the context.
  5015                      */
  5016                     if (PR_FALSE == inCache->mItems[loop].mInUse) {
  5017                         retval = &inCache->mItems[loop].mContext;
  5018                         newContext = PR_TRUE;
  5019                         break;
  5024             /*
  5025              **  If we still don't have a return value, then we need to see if
  5026              **      there are any old items with zero ref counts that we
  5027              **      can take over.
  5028              */
  5029             if (NULL == retval) {
  5030                 for (loop = 0; loop < inCache->mItemCount; loop++) {
  5031                     /*
  5032                      **  Must be in use.
  5033                      */
  5034                     if (PR_FALSE != inCache->mItems[loop].mInUse) {
  5035                         /*
  5036                          **  Must have a ref count of zero.
  5037                          */
  5038                         if (0 == inCache->mItems[loop].mReferenceCount) {
  5039                             /*
  5040                              **  Must be older than any other we know of.
  5041                              */
  5042                             if (NULL != retval) {
  5043                                 if (inCache->mItems[loop].mLastAccessed <
  5044                                     inCache->mItems[retval->mIndex].
  5045                                     mLastAccessed) {
  5046                                     retval = &inCache->mItems[loop].mContext;
  5049                             else {
  5050                                 retval = &inCache->mItems[loop].mContext;
  5056                 if (NULL != retval) {
  5057                     evictContext = PR_TRUE;
  5061             /*
  5062              **  If we still don't have a return value, then we can not avoid
  5063              **      waiting around until someone gives us the chance.
  5064              **  The chance, in specific, comes when a cache item reference
  5065              **      count returns to zero, upon which we can try to take
  5066              **      it over again.
  5067              */
  5068             if (NULL == retval) {
  5069                 /*
  5070                  **  This has the side effect of release the context lock.
  5071                  **  This is a good thing so that other clients can continue
  5072                  **      to connect and hopefully have cache hits.
  5073                  **  If they do not have cache hits, then we will end up
  5074                  **      with a bunch of waiters here....
  5075                  */
  5076                 PR_WaitCondVar(inCache->mCacheMiss, PR_INTERVAL_NO_TIMEOUT);
  5079             /*
  5080              **  If we have a return value, improve the reference count here.
  5081              */
  5082             if (NULL != retval) {
  5083                 /*
  5084                  **  Decide if there are any changes to be made.
  5085                  **  Do as little as possible, then fall through the context
  5086                  **      cache lock to finish up.
  5087                  **  This way, lengthy init operations will not block
  5088                  **      other clients, only matches to this context.
  5089                  */
  5090                 if (PR_FALSE != newContext ||
  5091                     PR_FALSE != evictContext ||
  5092                     PR_FALSE != changeCategoryContext) {
  5093                     /*
  5094                      **  Overwrite the prefs for this context.
  5095                      **  They are changing.
  5096                      */
  5097                     memcpy(&inCache->mItems[retval->mIndex].mOptions,
  5098                            inOptions,
  5099                            sizeof(inCache->mItems[retval->mIndex].mOptions));
  5100                     /*
  5101                      **  As we are going to be changing the context, we need to write lock it.
  5102                      **  This makes sure no readers are allowed while we are making our changes.
  5103                      */
  5104                     PR_RWLock_Wlock(retval->mRWLock);
  5107                 /*
  5108                  **  NOTE, ref count gets incremented here, inside content
  5109                  **      cache lock so it can not be flushed once lock
  5110                  **      released.
  5111                  */
  5112                 inCache->mItems[retval->mIndex].mInUse = PR_TRUE;
  5113                 inCache->mItems[retval->mIndex].mReferenceCount++;
  5114                 /*
  5115                  **  That's all folks.
  5116                  */
  5117                 break;
  5120         }                       /* while(1), try again */
  5122         /*
  5123          **  Done with context cache.
  5124          */
  5125         PR_Unlock(inCache->mLock);
  5126         /*
  5127          **  Now that the context cached is free to continue accepting other
  5128          **      requests, we have a little more work to do.
  5129          */
  5130         if (NULL != retval) {
  5131             PRBool unlock = PR_FALSE;
  5133             /*
  5134              **  If evicting, we need to free off the old stuff.
  5135              */
  5136             if (PR_FALSE != evictContext) {
  5137                 unlock = PR_TRUE;
  5138                 /*
  5139                  **  We do not free the sorted run.
  5140                  **  The category code takes care of this.
  5141                  */
  5142                 retval->mSortedRun = NULL;
  5143 #if ST_WANT_GRAPHS
  5144                 /*
  5145                  **  There is no need to
  5146                  **      PR_Lock(retval->mImageLock)
  5147                  **  We are already under write lock for the entire structure.
  5148                  */
  5149                 retval->mFootprintCached = PR_FALSE;
  5150                 retval->mTimevalCached = PR_FALSE;
  5151                 retval->mLifespanCached = PR_FALSE;
  5152                 retval->mWeightCached = PR_FALSE;
  5153 #endif
  5156             /*
  5157              **  If new or recently evicted, we need to fully init.
  5158              */
  5159             if (PR_FALSE != newContext || PR_FALSE != evictContext) {
  5160                 unlock = PR_TRUE;
  5161                 retval->mSortedRun =
  5162                     createRunFromGlobal(&inCache->mItems[retval->mIndex].
  5163                                         mOptions,
  5164                                         &inCache->mItems[retval->mIndex].
  5165                                         mContext);
  5168             /*
  5169              **  If changing category, we need to do some sneaky stuff.
  5170              */
  5171             if (PR_FALSE != changeCategoryContext) {
  5172                 STCategoryNode *node = NULL;
  5174                 unlock = PR_TRUE;
  5175                 /*
  5176                  ** Just a category change. We don't need to harvest. Just find the
  5177                  ** right node and set the cache.mSortedRun. We need to recompute
  5178                  ** cost though. But that is cheap.
  5179                  */
  5180                 node =
  5181                     findCategoryNode(inCache->mItems[retval->mIndex].mOptions.
  5182                                      mCategoryName, &globals);
  5183                 if (node) {
  5184                     /* Recalculate cost of run */
  5185                     recalculateRunCost(&inCache->mItems[retval->mIndex].
  5186                                        mOptions, retval,
  5187                                        node->runs[retval->mIndex]);
  5188                     retval->mSortedRun = node->runs[retval->mIndex];
  5191 #if ST_WANT_GRAPHS
  5192                 /*
  5193                  **  There is no need to
  5194                  **      PR_Lock(retval->mImageLock)
  5195                  **  We are already under write lock for the entire structure.
  5196                  */
  5197                 retval->mFootprintCached = PR_FALSE;
  5198                 retval->mTimevalCached = PR_FALSE;
  5199                 retval->mLifespanCached = PR_FALSE;
  5200                 retval->mWeightCached = PR_FALSE;
  5201 #endif
  5204             /*
  5205              **  Release the write lock if we took one to make changes.
  5206              */
  5207             if (PR_FALSE != unlock) {
  5208                 PR_RWLock_Unlock(retval->mRWLock);
  5211             /*
  5212              **  Last thing possible, take a read lock on our return value.
  5213              **  This will cause us to block if the context is not fully
  5214              **      initialized in another thread holding the write lock.
  5215              */
  5216             PR_RWLock_Rlock(retval->mRWLock);
  5220     return retval;
  5223 void
  5224 contextRelease(STContext * inContext)
  5225 /*
  5226 **  After a successful call to contextLookup, one should call this API when
  5227 **      done with the context.
  5228 **  This effectively removes the usage of the client on a cached item.
  5229 */
  5231     STContextCache *inCache = &globals.mContextCache;
  5233     if (NULL != inContext && NULL != inCache) {
  5234         /*
  5235          **  Own the context cache while in here.
  5236          */
  5237         PR_Lock(inCache->mLock);
  5238         /*
  5239          **  Give up the read lock on the context.
  5240          */
  5241         PR_RWLock_Unlock(inContext->mRWLock);
  5242         /*
  5243          **  Decrement the reference count on the context.
  5244          **  If it was the last reference, notify that a new item is
  5245          **      available for eviction.
  5246          **  A waiting thread will wake up and eat it.
  5247          **  Also set when it was last accessed so the oldest unused item
  5248          **      can be targeted for eviction.
  5249          */
  5250         inCache->mItems[inContext->mIndex].mReferenceCount--;
  5251         if (0 == inCache->mItems[inContext->mIndex].mReferenceCount) {
  5252             PR_NotifyCondVar(inCache->mCacheMiss);
  5253             inCache->mItems[inContext->mIndex].mLastAccessed =
  5254                 PR_IntervalNow();
  5257         /*
  5258          **  Done with context cache.
  5259          */
  5260         PR_Unlock(inCache->mLock);
  5265 /*
  5266 ** handleRequest
  5267 **
  5268 ** Based on what file they are asking for, perform some processing.
  5269 ** Output the results to aFD.
  5270 **
  5271 ** Returns !0 on error.
  5272 */
  5273 int
  5274 handleRequest(tmreader * aTMR, PRFileDesc * aFD,
  5275               const char *aFileName, const FormData * aGetData)
  5277     int retval = 0;
  5279     if (NULL != aTMR && NULL != aFD && NULL != aFileName
  5280         && '\0' != *aFileName) {
  5281         STRequest request;
  5283         /*
  5284          ** Init the request.
  5285          */
  5286         memset(&request, 0, sizeof(request));
  5287         request.mFD = aFD;
  5288         request.mGetFileName = aFileName;
  5289         request.mGetData = aGetData;
  5290         /*
  5291          **  Set local options for this request.
  5292          */
  5293         initRequestOptions(&request);
  5294         /*
  5295          **  Get our cached context for this client.
  5296          **  Simply based on the options.
  5297          */
  5298         request.mContext = contextLookup(&request.mOptions);
  5299         if (NULL != request.mContext) {
  5300             /*
  5301              ** Attempt to find the file of interest.
  5302              */
  5303             if (handleLocalFile(&request, aFileName)) {
  5304                 displayFile(&request, aFileName);
  5306             else if (0 == strcmp("index.html", aFileName)) {
  5307                 int displayRes = 0;
  5309                 htmlHeader(&request, "SpaceTrace Index");
  5310                 displayRes = displayIndex(&request);
  5311                 if (0 != displayRes) {
  5312                     retval = __LINE__;
  5313                     REPORT_ERROR(__LINE__, displayIndex);
  5316                 htmlFooter(&request);
  5318             else if (0 == strcmp("settings.html", aFileName) ||
  5319                      0 == strcmp("options.html", aFileName)) {
  5320                 htmlHeader(&request, "SpaceTrace Options");
  5321                 displaySettings(&request);
  5322                 htmlFooter(&request);
  5324             else if (0 == strcmp("top_allocations.html", aFileName)) {
  5325                 int displayRes = 0;
  5327                 htmlHeader(&request, "SpaceTrace Top Allocations Report");
  5328                 displayRes =
  5329                     displayTopAllocations(&request,
  5330                                           request.mContext->mSortedRun,
  5331                                           "top-allocations",
  5332                                           "SpaceTrace Top Allocations Report",
  5333                                           1);
  5334                 if (0 != displayRes) {
  5335                     retval = __LINE__;
  5336                     REPORT_ERROR(__LINE__, displayTopAllocations);
  5339                 htmlFooter(&request);
  5341             else if (0 == strcmp("top_callsites.html", aFileName)) {
  5342                 int displayRes = 0;
  5343                 tmcallsite **array = NULL;
  5344                 uint32_t arrayCount = 0;
  5346                 /*
  5347                  ** Display header after we figure out if we are going to focus
  5348                  ** on a category.
  5349                  */
  5350                 htmlHeader(&request, "SpaceTrace Top Callsites Report");
  5351                 if (NULL != request.mContext->mSortedRun
  5352                     && 0 < request.mContext->mSortedRun->mAllocationCount) {
  5353                     arrayCount =
  5354                         callsiteArrayFromRun(&array, 0,
  5355                                              request.mContext->mSortedRun);
  5356                     if (0 != arrayCount && NULL != array) {
  5357                         displayRes =
  5358                             displayTopCallsites(&request, array, arrayCount,
  5359                                                 0,
  5360                                                 "top-callsites",
  5361                                                 "Top Callsites Report",
  5362                                                 0);
  5363                         if (0 != displayRes) {
  5364                             retval = __LINE__;
  5365                             REPORT_ERROR(__LINE__, displayTopCallsites);
  5368                         /*
  5369                          ** Done with the array.
  5370                          */
  5371                         free(array);
  5372                         array = NULL;
  5375                 else {
  5376                     retval = __LINE__;
  5377                     REPORT_ERROR(__LINE__, handleRequest);
  5380                 htmlFooter(&request);
  5382             else if (0 == strcmp("memory_leaks.html", aFileName)) {
  5383                 int displayRes = 0;
  5385                 htmlHeader(&request, "SpaceTrace Memory Leaks Report");
  5386                 displayRes =
  5387                     displayMemoryLeaks(&request,
  5388                                        request.mContext->mSortedRun);
  5389                 if (0 != displayRes) {
  5390                     retval = __LINE__;
  5391                     REPORT_ERROR(__LINE__, displayMemoryLeaks);
  5394                 htmlFooter(&request);
  5396             else if (0 == strncmp("allocation_", aFileName, 11)) {
  5397                 int scanRes = 0;
  5398                 uint32_t allocationIndex = 0;
  5400                 /*
  5401                  ** Oh, what a hack....
  5402                  ** The index to the allocation structure in the global run
  5403                  **   is in the filename.  Better than the pointer value....
  5404                  */
  5405                 scanRes = PR_sscanf(aFileName + 11, "%u", &allocationIndex);
  5406                 if (1 == scanRes
  5407                     && globals.mRun.mAllocationCount > allocationIndex
  5408                     && NULL != globals.mRun.mAllocations[allocationIndex]) {
  5409                     STAllocation *allocation =
  5410                         globals.mRun.mAllocations[allocationIndex];
  5411                     char buffer[128];
  5412                     int displayRes = 0;
  5414                     PR_snprintf(buffer, sizeof(buffer),
  5415                                 "SpaceTrace Allocation %u Details Report",
  5416                                 allocationIndex);
  5417                     htmlHeader(&request, buffer);
  5418                     displayRes =
  5419                         displayAllocationDetails(&request, allocation);
  5420                     if (0 != displayRes) {
  5421                         retval = __LINE__;
  5422                         REPORT_ERROR(__LINE__, displayAllocationDetails);
  5425                     htmlFooter(&request);
  5427                 else {
  5428                     htmlNotFound(&request);
  5431             else if (0 == strncmp("callsite_", aFileName, 9)) {
  5432                 int scanRes = 0;
  5433                 uint32_t callsiteSerial = 0;
  5434                 tmcallsite *resolved = NULL;
  5436                 /*
  5437                  ** Oh, what a hack....
  5438                  ** The serial(key) to the callsite structure in the hash table
  5439                  **   is in the filename.  Better than the pointer value....
  5440                  */
  5441                 scanRes = PR_sscanf(aFileName + 9, "%u", &callsiteSerial);
  5442                 if (1 == scanRes && 0 != callsiteSerial
  5443                     && NULL != (resolved =
  5444                                 tmreader_callsite(aTMR, callsiteSerial))) {
  5445                     char buffer[128];
  5446                     int displayRes = 0;
  5448                     PR_snprintf(buffer, sizeof(buffer),
  5449                                 "SpaceTrace Callsite %u Details Report",
  5450                                 callsiteSerial);
  5451                     htmlHeader(&request, buffer);
  5452                     displayRes = displayCallsiteDetails(&request, resolved);
  5453                     if (0 != displayRes) {
  5454                         retval = __LINE__;
  5455                         REPORT_ERROR(__LINE__, displayAllocationDetails);
  5458                     htmlFooter(&request);
  5460                 else {
  5461                     htmlNotFound(&request);
  5464             else if (0 == strcmp("root_callsites.html", aFileName)) {
  5465                 int displayRes = 0;
  5467                 htmlHeader(&request, "SpaceTrace Root Callsites");
  5468                 displayRes =
  5469                     displayCallsites(&request, aTMR->calltree_root.kids,
  5470                                      ST_FOLLOW_SIBLINGS, 0,
  5471                                      "callsites-root",
  5472                                      "SpaceTrace Root Callsites",
  5473                                      __LINE__);
  5474                 if (0 != displayRes) {
  5475                     retval = __LINE__;
  5476                     REPORT_ERROR(__LINE__, displayCallsites);
  5479                 htmlFooter(&request);
  5481 #if ST_WANT_GRAPHS
  5482             else if (0 == strcmp("footprint_graph.html", aFileName)) {
  5483                 int displayRes = 0;
  5485                 htmlHeader(&request, "SpaceTrace Memory Footprint Report");
  5486                 PR_fprintf(request.mFD, "<div align=center>\n");
  5487                 PR_fprintf(request.mFD, "<img src=\"./footprint.png");
  5488                 optionGetDataOut(request.mFD, &request.mOptions);
  5489                 PR_fprintf(request.mFD, "\">\n");
  5490                 PR_fprintf(request.mFD, "</div>\n");
  5491                 htmlFooter(&request);
  5493 #endif /* ST_WANT_GRAPHS */
  5494 #if ST_WANT_GRAPHS
  5495             else if (0 == strcmp("times_graph.html", aFileName)) {
  5496                 int displayRes = 0;
  5498                 htmlHeader(&request, "SpaceTrace Allocation Times Report");
  5499                 PR_fprintf(request.mFD, "<div align=center>\n");
  5500                 PR_fprintf(request.mFD, "<img src=\"./times.png");
  5501                 optionGetDataOut(request.mFD, &request.mOptions);
  5502                 PR_fprintf(request.mFD, "\">\n");
  5503                 PR_fprintf(request.mFD, "</div>\n");
  5504                 htmlFooter(&request);
  5506 #endif /* ST_WANT_GRAPHS */
  5507 #if ST_WANT_GRAPHS
  5508             else if (0 == strcmp("lifespan_graph.html", aFileName)) {
  5509                 int displayRes = 0;
  5511                 htmlHeader(&request,
  5512                            "SpaceTrace Allocation Lifespans Report");
  5513                 PR_fprintf(request.mFD, "<div align=center>\n");
  5514                 PR_fprintf(request.mFD, "<img src=\"./lifespan.png");
  5515                 optionGetDataOut(request.mFD, &request.mOptions);
  5516                 PR_fprintf(request.mFD, "\">\n");
  5517                 PR_fprintf(request.mFD, "</div>\n");
  5518                 htmlFooter(&request);
  5520 #endif /* ST_WANT_GRAPHS */
  5521 #if ST_WANT_GRAPHS
  5522             else if (0 == strcmp("weight_graph.html", aFileName)) {
  5523                 int displayRes = 0;
  5525                 htmlHeader(&request, "SpaceTrace Allocation Weights Report");
  5526                 PR_fprintf(request.mFD, "<div align=center>\n");
  5527                 PR_fprintf(request.mFD, "<img src=\"./weight.png");
  5528                 optionGetDataOut(request.mFD, &request.mOptions);
  5529                 PR_fprintf(request.mFD, "\">\n");
  5530                 PR_fprintf(request.mFD, "</div>\n");
  5531                 htmlFooter(&request);
  5533 #endif /* ST_WANT_GRAPHS */
  5534 #if ST_WANT_GRAPHS
  5535             else if (0 == strcmp("footprint.png", aFileName)) {
  5536                 int graphRes = 0;
  5538                 graphRes =
  5539                     graphFootprint(&request, request.mContext->mSortedRun);
  5540                 if (0 != graphRes) {
  5541                     retval = __LINE__;
  5542                     REPORT_ERROR(__LINE__, graphFootprint);
  5545 #endif /* ST_WANT_GRAPHS */
  5546 #if ST_WANT_GRAPHS
  5547             else if (0 == strcmp("times.png", aFileName)) {
  5548                 int graphRes = 0;
  5550                 graphRes =
  5551                     graphTimeval(&request, request.mContext->mSortedRun);
  5552                 if (0 != graphRes) {
  5553                     retval = __LINE__;
  5554                     REPORT_ERROR(__LINE__, graphTimeval);
  5557 #endif /* ST_WANT_GRAPHS */
  5558 #if ST_WANT_GRAPHS
  5559             else if (0 == strcmp("lifespan.png", aFileName)) {
  5560                 int graphRes = 0;
  5562                 graphRes =
  5563                     graphLifespan(&request, request.mContext->mSortedRun);
  5564                 if (0 != graphRes) {
  5565                     retval = __LINE__;
  5566                     REPORT_ERROR(__LINE__, graphLifespan);
  5569 #endif /* ST_WANT_GRAPHS */
  5570 #if ST_WANT_GRAPHS
  5571             else if (0 == strcmp("weight.png", aFileName)) {
  5572                 int graphRes = 0;
  5574                 graphRes =
  5575                     graphWeight(&request, request.mContext->mSortedRun);
  5576                 if (0 != graphRes) {
  5577                     retval = __LINE__;
  5578                     REPORT_ERROR(__LINE__, graphWeight);
  5581 #endif /* ST_WANT_GRAPHS */
  5582             else if (0 == strcmp("categories_summary.html", aFileName)) {
  5583                 int displayRes = 0;
  5585                 htmlHeader(&request, "Category Report");
  5586                 displayRes =
  5587                     displayCategoryReport(&request, &globals.mCategoryRoot,
  5588                                           1);
  5589                 if (0 != displayRes) {
  5590                     retval = __LINE__;
  5591                     REPORT_ERROR(__LINE__, displayMemoryLeaks);
  5594                 htmlFooter(&request);
  5596             else {
  5597                 htmlNotFound(&request);
  5600             /*
  5601              **  Release the context we obtained earlier.
  5602              */
  5603             contextRelease(request.mContext);
  5604             request.mContext = NULL;
  5606         else {
  5607             retval = __LINE__;
  5608             REPORT_ERROR(__LINE__, contextObtain);
  5611     else {
  5612         retval = __LINE__;
  5613         REPORT_ERROR(__LINE__, handleRequest);
  5616     /*
  5617      ** Compact a little if you can after each request.
  5618      */
  5619     heapCompact();
  5620     return retval;
  5623 /*
  5624 ** handleClient
  5625 **
  5626 **  main() of the new client thread.
  5627 ** Read the fd for the request.
  5628 ** Output the results.
  5629 */
  5630 void
  5631 handleClient(void *inArg)
  5633     PRFileDesc *aFD = NULL;
  5635     aFD = (PRFileDesc *) inArg;
  5636     if (NULL != aFD) {
  5637         PRStatus closeRes = PR_SUCCESS;
  5638         char aBuffer[2048];
  5639         int32_t readRes = 0;
  5641         readRes = PR_Read(aFD, aBuffer, sizeof(aBuffer));
  5642         if (0 <= readRes) {
  5643             const char *sanityCheck = "GET /";
  5645             if (0 == strncmp(sanityCheck, aBuffer, 5)) {
  5646                 char *eourl = NULL;
  5647                 char *start = &aBuffer[5];
  5648                 char *getData = NULL;
  5649                 int realFun = 0;
  5650                 const char *crlf = "\015\012";
  5651                 char *eoline = NULL;
  5652                 FormData *fdGet = NULL;
  5654                 /*
  5655                  ** Truncate the line if possible.
  5656                  ** Only want first one.
  5657                  */
  5658                 eoline = strstr(aBuffer, crlf);
  5659                 if (NULL != eoline) {
  5660                     *eoline = '\0';
  5663                 /*
  5664                  ** Find the whitespace.
  5665                  ** That is either end of line or the " HTTP/1.x" suffix.
  5666                  ** We do not care.
  5667                  */
  5668                 for (eourl = start; 0 == isspace(*eourl) && '\0' != *eourl;
  5669                      eourl++) {
  5670                     /*
  5671                      ** No body.
  5672                      */
  5675                 /*
  5676                  ** Cap it off.
  5677                  ** Convert empty '/' to index.html.
  5678                  */
  5679                 *eourl = '\0';
  5680                 if ('\0' == *start) {
  5681                     strcpy(start, "index.html");
  5684                 /*
  5685                  ** Have we got any GET form data?
  5686                  */
  5687                 getData = strchr(start, '?');
  5688                 if (NULL != getData) {
  5689                     /*
  5690                      ** Whack it off.
  5691                      */
  5692                     *getData = '\0';
  5693                     getData++;
  5696                 /*
  5697                  **  Convert get data into a more useful format.
  5698                  */
  5699                 fdGet = FormData_Create(getData);
  5700                 /*
  5701                  ** This is totally a hack, but oh well....
  5702                  **
  5703                  ** Send that the request was OK, regardless.
  5704                  **
  5705                  ** If we have any get data, then it is a set of options
  5706                  **      we attempt to apply.
  5707                  **
  5708                  ** Other code will tell the user they were wrong or if
  5709                  **      there was an error.
  5710                  ** If the filename contains a ".png", then send the image
  5711                  **      mime type, otherwise, say it is text/html. 
  5712                  */
  5713                 PR_fprintf(aFD, "HTTP/1.1 200 OK%s", crlf);
  5714                 PR_fprintf(aFD, "Server: %s%s",
  5715                            "$Id: spacetrace.c,v 1.54 2006/11/01 23:02:17 timeless%mozdev.org Exp $",
  5716                            crlf);
  5717                 PR_fprintf(aFD, "Content-type: ");
  5718                 if (NULL != strstr(start, ".png")) {
  5719                     PR_fprintf(aFD, "image/png");
  5721                 else if (NULL != strstr(start, ".jpg")) {
  5722                     PR_fprintf(aFD, "image/jpeg");
  5724                 else if (NULL != strstr(start, ".txt")) {
  5725                     PR_fprintf(aFD, "text/plain");
  5727                 else if (NULL != strstr(start, ".css")) {
  5728                     PR_fprintf(aFD, "text/css");
  5730                 else {
  5731                     PR_fprintf(aFD, "text/html");
  5733                 PR_fprintf(aFD, crlf);
  5734                 /*
  5735                  ** One more to separate headers from content.
  5736                  */
  5737                 PR_fprintf(aFD, crlf);
  5738                 /*
  5739                  ** Ready for the real fun.
  5740                  */
  5741                 realFun = handleRequest(globals.mTMR, aFD, start, fdGet);
  5742                 if (0 != realFun) {
  5743                     REPORT_ERROR(__LINE__, handleRequest);
  5746                 /*
  5747                  **  Free off get data if around.
  5748                  */
  5749                 FormData_Destroy(fdGet);
  5750                 fdGet = NULL;
  5752             else {
  5753                 REPORT_ERROR(__LINE__, handleClient);
  5756         else {
  5757             REPORT_ERROR(__LINE__, lineReader);
  5760         /*
  5761          ** Done with the connection.
  5762          */
  5763         closeRes = PR_Close(aFD);
  5764         if (PR_SUCCESS != closeRes) {
  5765             REPORT_ERROR(__LINE__, PR_Close);
  5768     else {
  5769         REPORT_ERROR(__LINE__, handleClient);
  5773 /*
  5774 ** serverMode
  5775 **
  5776 ** List on a port as a httpd.
  5777 ** Output results interactively on demand.
  5778 **
  5779 ** Returns !0 on error.
  5780 */
  5781 int
  5782 serverMode(void)
  5784     int retval = 0;
  5785     PRFileDesc *socket = NULL;
  5787     /*
  5788      ** Create a socket.
  5789      */
  5790     socket = PR_NewTCPSocket();
  5791     if (NULL != socket) {
  5792         PRStatus closeRes = PR_SUCCESS;
  5793         PRNetAddr bindAddr;
  5794         PRStatus bindRes = PR_SUCCESS;
  5796         /*
  5797          ** Bind it to an interface/port.
  5798          ** Any interface.
  5799          */
  5800         bindAddr.inet.family = PR_AF_INET;
  5801         bindAddr.inet.port =
  5802             PR_htons((uint16_t) globals.mCommandLineOptions.mHttpdPort);
  5803         bindAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
  5804         bindRes = PR_Bind(socket, &bindAddr);
  5805         if (PR_SUCCESS == bindRes) {
  5806             PRStatus listenRes = PR_SUCCESS;
  5807             const int backlog = 0x20;
  5809             /*
  5810              ** Start listening for clients.
  5811              ** Give a decent backlog, some of our processing will take
  5812              **  a bit.
  5813              */
  5814             listenRes = PR_Listen(socket, backlog);
  5815             if (PR_SUCCESS == listenRes) {
  5816                 PRFileDesc *connection = NULL;
  5817                 int failureSum = 0;
  5818                 char message[80];
  5820                 /*
  5821                  ** Output a little message saying we are receiving.
  5822                  */
  5823                 PR_snprintf(message, sizeof(message),
  5824                             "server accepting connections at http://localhost:%u/",
  5825                             globals.mCommandLineOptions.mHttpdPort);
  5826                 REPORT_INFO(message);
  5827                 PR_fprintf(PR_STDOUT, "Peak memory used: %s bytes\n",
  5828                            FormatNumber(globals.mPeakMemoryUsed));
  5829                 PR_fprintf(PR_STDOUT, "Allocations     : %s total\n",
  5830                            FormatNumber(globals.mMallocCount +
  5831                                         globals.mCallocCount +
  5832                                         globals.mReallocCount),
  5833                            FormatNumber(globals.mFreeCount));
  5834                 PR_fprintf(PR_STDOUT, "Breakdown       : %s malloc\n",
  5835                            FormatNumber(globals.mMallocCount));
  5836                 PR_fprintf(PR_STDOUT, "                  %s calloc\n",
  5837                            FormatNumber(globals.mCallocCount));
  5838                 PR_fprintf(PR_STDOUT, "                  %s realloc\n",
  5839                            FormatNumber(globals.mReallocCount));
  5840                 PR_fprintf(PR_STDOUT, "                  %s free\n",
  5841                            FormatNumber(globals.mFreeCount));
  5842                 PR_fprintf(PR_STDOUT, "Leaks           : %s\n",
  5843                            FormatNumber((globals.mMallocCount +
  5844                                          globals.mCallocCount +
  5845                                          globals.mReallocCount) -
  5846                                         globals.mFreeCount));
  5847                 /*
  5848                  **  Keep accepting until we know otherwise.
  5849                  **
  5850                  **  We do a thread per connection.
  5851                  **  Up to the thread to close the connection when done.
  5852                  **
  5853                  **  This is known by me to be suboptimal, and I would rather
  5854                  **      do a thread pool if it ever becomes a resource issue.
  5855                  **  Any issues would simply point to a need to get
  5856                  **      more machines or a beefier machine to handle the
  5857                  **      requests, as well as a need to do thread pooling and
  5858                  **      avoid thread creation overhead.
  5859                  **  The threads are not tracked, except possibly by NSPR
  5860                  **      itself and PR_Cleanup will wait on them all to exit as
  5861                  **      user threads so our shared data is valid.
  5862                  */
  5863                 while (0 == retval) {
  5864                     connection =
  5865                         PR_Accept(socket, NULL, PR_INTERVAL_NO_TIMEOUT);
  5866                     if (NULL != connection) {
  5867                         PRThread *clientThread = NULL;
  5869                         /*
  5870                          **  Thread per connection.
  5871                          */
  5872                         clientThread = PR_CreateThread(PR_USER_THREAD,  /* PR_Cleanup sync */
  5873                                                        handleClient, (void *) connection, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, /* IO enabled */
  5874                                                        PR_UNJOINABLE_THREAD,
  5875                                                        0);
  5876                         if (NULL == clientThread) {
  5877                             PRStatus closeRes = PR_SUCCESS;
  5879                             failureSum += __LINE__;
  5880                             REPORT_ERROR(__LINE__, PR_Accept);
  5881                             /*
  5882                              **  Close the connection as well, no service
  5883                              */
  5884                             closeRes = PR_Close(connection);
  5885                             if (PR_FAILURE == closeRes) {
  5886                                 REPORT_ERROR(__LINE__, PR_Close);
  5890                     else {
  5891                         failureSum += __LINE__;
  5892                         REPORT_ERROR(__LINE__, PR_Accept);
  5896                 if (0 != failureSum) {
  5897                     retval = __LINE__;
  5900                 /*
  5901                  ** Output a little message saying it is all over.
  5902                  */
  5903                 REPORT_INFO("server no longer accepting connections....");
  5905             else {
  5906                 retval = __LINE__;
  5907                 REPORT_ERROR(__LINE__, PR_Listen);
  5910         else {
  5911             retval = __LINE__;
  5912             REPORT_ERROR(__LINE__, PR_Bind);
  5915         /*
  5916          ** Done with socket.
  5917          */
  5918         closeRes = PR_Close(socket);
  5919         if (PR_SUCCESS != closeRes) {
  5920             retval = __LINE__;
  5921             REPORT_ERROR(__LINE__, PR_Close);
  5923         socket = NULL;
  5925     else {
  5926         retval = __LINE__;
  5927         REPORT_ERROR(__LINE__, PR_NewTCPSocket);
  5930     return retval;
  5933 /*
  5934 ** batchMode
  5935 **
  5936 ** Perform whatever batch requests we were asked to do.
  5937 */
  5938 int
  5939 batchMode(void)
  5941     int retval = 0;
  5943     if (0 != globals.mCommandLineOptions.mBatchRequestCount) {
  5944         uint32_t loop = 0;
  5945         int failureSum = 0;
  5946         int handleRes = 0;
  5947         char aFileName[1024];
  5948         uint32_t sprintfRes = 0;
  5950         /*
  5951          ** Go through and process the various files requested.
  5952          ** We do not stop on failure, as it is too costly to rerun the
  5953          **  batch job.
  5954          */
  5955         for (loop = 0;
  5956              loop < globals.mCommandLineOptions.mBatchRequestCount; loop++) {
  5957             sprintfRes =
  5958                 PR_snprintf(aFileName, sizeof(aFileName), "%s%c%s",
  5959                             globals.mCommandLineOptions.mOutputDir,
  5960                             PR_GetDirectorySeparator(),
  5961                             globals.mCommandLineOptions.mBatchRequest[loop]);
  5962             if ((uint32_t) - 1 != sprintfRes) {
  5963                 PRFileDesc *outFile = NULL;
  5965                 outFile = PR_Open(aFileName, ST_FLAGS, ST_PERMS);
  5966                 if (NULL != outFile) {
  5967                     PRStatus closeRes = PR_SUCCESS;
  5969                     handleRes =
  5970                         handleRequest(globals.mTMR, outFile,
  5971                                       globals.mCommandLineOptions.
  5972                                       mBatchRequest[loop], NULL);
  5973                     if (0 != handleRes) {
  5974                         failureSum += __LINE__;
  5975                         REPORT_ERROR(__LINE__, handleRequest);
  5978                     closeRes = PR_Close(outFile);
  5979                     if (PR_SUCCESS != closeRes) {
  5980                         failureSum += __LINE__;
  5981                         REPORT_ERROR(__LINE__, PR_Close);
  5984                 else {
  5985                     failureSum += __LINE__;
  5986                     REPORT_ERROR(__LINE__, PR_Open);
  5989             else {
  5990                 failureSum += __LINE__;
  5991                 REPORT_ERROR(__LINE__, PR_snprintf);
  5995         if (0 != failureSum) {
  5996             retval = __LINE__;
  5999     else {
  6000         retval = __LINE__;
  6001         REPORT_ERROR(__LINE__, outputReports);
  6004     return retval;
  6007 /*
  6008 ** doRun
  6009 **
  6010 ** Perform the actual processing this program requires.
  6011 ** Returns !0 on failure.
  6012 */
  6013 int
  6014 doRun(void)
  6016     int retval = 0;
  6018     /*
  6019      ** Create the new trace-malloc reader.
  6020      */
  6021     globals.mTMR = tmreader_new(globals.mProgramName, NULL);
  6022     if (NULL != globals.mTMR) {
  6023         int tmResult = 0;
  6024         int outputResult = 0;
  6026 #if defined(DEBUG_dp)
  6027         PRIntervalTime start = PR_IntervalNow();
  6029         fprintf(stderr, "DEBUG: reading tracemalloc data...\n");
  6030 #endif
  6031         tmResult =
  6032             tmreader_eventloop(globals.mTMR,
  6033                                globals.mCommandLineOptions.mFileName,
  6034                                tmEventHandler);
  6035         printf("\rReading... Done.\n");
  6036 #if defined(DEBUG_dp)
  6037         fprintf(stderr,
  6038                 "DEBUG: reading tracemalloc data ends: %dms [%d allocations]\n",
  6039                 PR_IntervalToMilliseconds(PR_IntervalNow() - start),
  6040                 globals.mRun.mAllocationCount);
  6041 #endif
  6042         if (0 == tmResult) {
  6043             REPORT_ERROR(__LINE__, tmreader_eventloop);
  6044             retval = __LINE__;
  6047         if (0 == retval) {
  6048             /*
  6049              ** Decide if we're going into batch mode or server mode.
  6050              */
  6051             if (0 != globals.mCommandLineOptions.mBatchRequestCount) {
  6052                 /*
  6053                  ** Output in one big step while everything still exists.
  6054                  */
  6055                 outputResult = batchMode();
  6056                 if (0 != outputResult) {
  6057                     REPORT_ERROR(__LINE__, batchMode);
  6058                     retval = __LINE__;
  6061             else {
  6062                 int serverRes = 0;
  6064                 /*
  6065                  ** httpd time.
  6066                  */
  6067                 serverRes = serverMode();
  6068                 if (0 != serverRes) {
  6069                     REPORT_ERROR(__LINE__, serverMode);
  6070                     retval = __LINE__;
  6074             /*
  6075              ** Clear our categorization tree
  6076              */
  6077             freeCategories(&globals);
  6080     else {
  6081         REPORT_ERROR(__LINE__, tmreader_new);
  6082         retval = __LINE__;
  6085     return retval;
  6088 int
  6089 initCaches(void)
  6090 /*
  6091 **  Initialize the global caches.
  6092 **  More involved since we have to allocated/create some objects.
  6093 **
  6094 **  returns     Zero if all is well.
  6095 **              Non-zero on error.
  6096 */
  6098     int retval = 0;
  6099     STContextCache *inCache = &globals.mContextCache;
  6101     if (NULL != inCache && 0 != globals.mCommandLineOptions.mContexts) {
  6102         inCache->mLock = PR_NewLock();
  6103         if (NULL != inCache->mLock) {
  6104             inCache->mCacheMiss = PR_NewCondVar(inCache->mLock);
  6105             if (NULL != inCache->mCacheMiss) {
  6106                 inCache->mItems =
  6107                     (STContextCacheItem *) calloc(globals.mCommandLineOptions.
  6108                                                   mContexts,
  6109                                                   sizeof(STContextCacheItem));
  6110                 if (NULL != inCache->mItems) {
  6111                     uint32_t loop = 0;
  6112                     char buffer[64];
  6114                     inCache->mItemCount =
  6115                         globals.mCommandLineOptions.mContexts;
  6116                     /*
  6117                      **  Init each item as needed.
  6118                      */
  6119                     for (loop = 0; loop < inCache->mItemCount; loop++) {
  6120                         inCache->mItems[loop].mContext.mIndex = loop;
  6121                         PR_snprintf(buffer, sizeof(buffer),
  6122                                     "Context Item %d RW Lock", loop);
  6123                         inCache->mItems[loop].mContext.mRWLock =
  6124                             PR_NewRWLock(PR_RWLOCK_RANK_NONE, buffer);
  6125                         if (NULL == inCache->mItems[loop].mContext.mRWLock) {
  6126                             break;
  6128 #if ST_WANT_GRAPHS
  6129                         inCache->mItems[loop].mContext.mImageLock =
  6130                             PR_NewLock();
  6131                         if (NULL == inCache->mItems[loop].mContext.mImageLock) {
  6132                             break;
  6134 #endif
  6137                     if (loop != inCache->mItemCount) {
  6138                         retval = __LINE__;
  6139                         REPORT_ERROR(__LINE__, initCaches);
  6142                 else {
  6143                     retval = __LINE__;
  6144                     REPORT_ERROR(__LINE__, calloc);
  6147             else {
  6148                 retval = __LINE__;
  6149                 REPORT_ERROR(__LINE__, PR_NewCondVar);
  6152         else {
  6153             retval = __LINE__;
  6154             REPORT_ERROR(__LINE__, PR_NewLock);
  6157     else {
  6158         retval = __LINE__;
  6159         REPORT_ERROR(__LINE__, initCaches);
  6162     return retval;
  6165 int
  6166 destroyCaches(void)
  6167 /*
  6168 **  Clean up any global caches we have laying around.
  6169 **
  6170 **  returns     Zero if all is well.
  6171 **              Non-zero on error.
  6172 */
  6174     int retval = 0;
  6175     STContextCache *inCache = &globals.mContextCache;
  6177     if (NULL != inCache) {
  6178         uint32_t loop = 0;
  6180         /*
  6181          **  Uninit item data one by one.
  6182          */
  6183         for (loop = 0; loop < inCache->mItemCount; loop++) {
  6184             if (NULL != inCache->mItems[loop].mContext.mRWLock) {
  6185                 PR_DestroyRWLock(inCache->mItems[loop].mContext.mRWLock);
  6186                 inCache->mItems[loop].mContext.mRWLock = NULL;
  6188 #if ST_WANT_GRAPHS
  6189             if (NULL != inCache->mItems[loop].mContext.mImageLock) {
  6190                 PR_DestroyLock(inCache->mItems[loop].mContext.mImageLock);
  6191                 inCache->mItems[loop].mContext.mImageLock = NULL;
  6193 #endif
  6196         inCache->mItemCount = 0;
  6197         if (NULL != inCache->mItems) {
  6198             free(inCache->mItems);
  6199             inCache->mItems = NULL;
  6202         if (NULL != inCache->mCacheMiss) {
  6203             PR_DestroyCondVar(inCache->mCacheMiss);
  6204             inCache->mCacheMiss = NULL;
  6207         if (NULL != inCache->mLock) {
  6208             PR_DestroyLock(inCache->mLock);
  6209             inCache->mLock = NULL;
  6212     else {
  6213         retval = __LINE__;
  6214         REPORT_ERROR(__LINE__, destroyCaches);
  6217     return retval;
  6220 /*
  6221 ** main
  6222 **
  6223 ** Process entry and exit.
  6224 */
  6225 int
  6226 main(int aArgCount, char **aArgArray)
  6228     int retval = 0;
  6229     int optionsResult = 0;
  6230     PRStatus prResult = PR_SUCCESS;
  6231     int showedHelp = 0;
  6232     int looper = 0;
  6233     int cacheResult = 0;
  6235     /*
  6236      ** NSPR init.
  6237      */
  6238     PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
  6239     /*
  6240      ** Initialize globals
  6241      */
  6242     memset(&globals, 0, sizeof(globals));
  6243     /*
  6244      ** Set the program name.
  6245      */
  6246     globals.mProgramName = aArgArray[0];
  6247     /*
  6248      ** Set the minimum timeval really high so other code
  6249      **  that checks the timeval will get it right.
  6250      */
  6251     globals.mMinTimeval = ST_TIMEVAL_MAX;
  6252     /*
  6253      ** Handle initializing options.
  6254      */
  6255     optionsResult = initOptions(aArgCount, aArgArray);
  6256     if (0 != optionsResult) {
  6257         REPORT_ERROR(optionsResult, initOptions);
  6258         retval = __LINE__;
  6261     /*
  6262      **  Initialize our caches.
  6263      */
  6264     cacheResult = initCaches();
  6265     if (0 != cacheResult) {
  6266         retval = __LINE__;
  6267         REPORT_ERROR(__LINE__, initCaches);
  6270     /*
  6271      **  Small alloc code init.
  6272      */
  6273     globals.mCategoryRoot.runs =
  6274         (STRun **) calloc(globals.mCommandLineOptions.mContexts,
  6275                           sizeof(STRun *));
  6276     if (NULL == globals.mCategoryRoot.runs) {
  6277         retval = __LINE__;
  6278         REPORT_ERROR(__LINE__, calloc);
  6281     /*
  6282      ** Show help on usage if need be.
  6283      */
  6284     showedHelp = showHelp();
  6285     /*
  6286      ** Only perform the run if everything is checking out.
  6287      */
  6288     if (0 == showedHelp && 0 == retval) {
  6289         int runResult = 0;
  6291         runResult = doRun();
  6292         if (0 != runResult) {
  6293             REPORT_ERROR(runResult, doRun);
  6294             retval = __LINE__;
  6298     if (0 != retval) {
  6299         REPORT_ERROR(retval, main);
  6302     /*
  6303      **  Have NSPR join all client threads we started.
  6304      */
  6305     prResult = PR_Cleanup();
  6306     if (PR_SUCCESS != prResult) {
  6307         REPORT_ERROR(retval, PR_Cleanup);
  6308         retval = __LINE__;
  6310     /*
  6311      **  All threads are joined/done by this line.
  6312      */
  6314     /*
  6315      **  Options allocated a little.
  6316      */
  6317 #define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help) \
  6318     if(NULL != globals.mCommandLineOptions.m##option_name) \
  6319     { \
  6320         free((void*)globals.mCommandLineOptions.m##option_name); \
  6321         globals.mCommandLineOptions.m##option_name = NULL; \
  6322         globals.mCommandLineOptions.m##option_name##Count = 0; \
  6324 #include "stoptions.h"
  6326     /*
  6327      **  globals has a small modification to clear up.
  6328      */
  6329     if (NULL != globals.mCategoryRoot.runs) {
  6330         free(globals.mCategoryRoot.runs);
  6331         globals.mCategoryRoot.runs = NULL;
  6334     /*
  6335      **  Blow away our caches.
  6336      */
  6337     cacheResult = destroyCaches();
  6338     if (0 != cacheResult) {
  6339         retval = __LINE__;
  6340         REPORT_ERROR(__LINE__, initCaches);
  6343     /*
  6344      **  We are safe to kill our tmreader data.
  6345      */
  6346     if (NULL != globals.mTMR) {
  6347         tmreader_destroy(globals.mTMR);
  6348         globals.mTMR = NULL;
  6351     return retval;

mercurial