tools/trace-malloc/spacetrace.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/trace-malloc/spacetrace.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,6352 @@
     1.4 +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 +** spacetrace.c
    1.12 +**
    1.13 +** SpaceTrace is meant to take the output of trace-malloc and present
    1.14 +**   a picture of allocations over the run of the application.
    1.15 +*/
    1.16 +
    1.17 +/*
    1.18 +** Required include files.
    1.19 +*/
    1.20 +#include "spacetrace.h"
    1.21 +
    1.22 +#include <ctype.h>
    1.23 +#include <math.h>
    1.24 +#include <string.h>
    1.25 +#include <time.h>
    1.26 +#if defined(XP_WIN32)
    1.27 +#include <malloc.h>             /* _heapMin */
    1.28 +#endif
    1.29 +
    1.30 +#if defined(HAVE_BOUTELL_GD)
    1.31 +/*
    1.32 +** See http://www.boutell.com/gd for the GD graphics library.
    1.33 +** Ports for many platorms exist.
    1.34 +** Your box may already have the lib (mine did, redhat 7.1 workstation).
    1.35 +*/
    1.36 +#include <gd.h>
    1.37 +#include <gdfontt.h>
    1.38 +#include <gdfonts.h>
    1.39 +#include <gdfontmb.h>
    1.40 +#endif /* HAVE_BOUTELL_GD */
    1.41 +
    1.42 +#include "nsQuickSort.h"
    1.43 +/*
    1.44 +**  strcasecmp API please.
    1.45 +*/
    1.46 +#if defined(_MSC_VER)
    1.47 +#define strcasecmp _stricmp
    1.48 +#define strncasecmp _strnicmp
    1.49 +#endif
    1.50 +
    1.51 +/*
    1.52 +** the globals variables.  happy joy.
    1.53 +*/
    1.54 +STGlobals globals;
    1.55 +
    1.56 +/*
    1.57 +** have the heap cleanup at opportune times, if possible.
    1.58 +*/
    1.59 +void
    1.60 +heapCompact(void)
    1.61 +{
    1.62 +#if defined(XP_WIN32)
    1.63 +    _heapmin();
    1.64 +#endif
    1.65 +}
    1.66 +
    1.67 +#define ST_CMD_OPTION_BOOL(option_name, option_genre, option_help) \
    1.68 +    PR_fprintf(PR_STDOUT, "--%s\nDisabled by default.\n%s\n", #option_name, option_help);
    1.69 +#define ST_CMD_OPTION_STRING(option_name, option_genre, default_value, option_help) \
    1.70 +    PR_fprintf(PR_STDOUT, "--%s=<value>\nDefault value is \"%s\".\n%s\n", #option_name, default_value, option_help);
    1.71 +#define ST_CMD_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
    1.72 +    PR_fprintf(PR_STDOUT, "--%s=<value>\nUp to %u occurrences allowed.\n%s\n", #option_name, array_size, option_help);
    1.73 +#define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help) \
    1.74 +    PR_fprintf(PR_STDOUT, "--%s=<value>\nUnlimited occurrences allowed.\n%s\n", #option_name, option_help);
    1.75 +#define ST_CMD_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
    1.76 +    PR_fprintf(PR_STDOUT, "--%s=<value>\nDefault value is %u.\n%s\n", #option_name, default_value, option_help);
    1.77 +#define ST_CMD_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
    1.78 +    PR_fprintf(PR_STDOUT, "--%s=<value>\nDefault value is %llu.\n%s\n", #option_name, default_value, option_help);
    1.79 +
    1.80 +/*
    1.81 +** showHelp
    1.82 +**
    1.83 +** Give simple command line help.
    1.84 +** Returns !0 if the help was showed.
    1.85 +*/
    1.86 +int
    1.87 +showHelp(void)
    1.88 +{
    1.89 +    int retval = 0;
    1.90 +
    1.91 +    if (PR_FALSE != globals.mCommandLineOptions.mHelp) {
    1.92 +        PR_fprintf(PR_STDOUT, "Usage:\t%s [OPTION]... [-|filename]\n\n",
    1.93 +                   globals.mProgramName);
    1.94 +
    1.95 +
    1.96 +#include "stoptions.h"
    1.97 +
    1.98 +        /*
    1.99 +         ** Showed something.
   1.100 +         */
   1.101 +        retval = __LINE__;
   1.102 +    }
   1.103 +
   1.104 +    return retval;
   1.105 +}
   1.106 +
   1.107 +/*
   1.108 +** ticks2xsec
   1.109 +**
   1.110 +** Convert platform specific ticks to second units
   1.111 +** Returns 0 on success.
   1.112 +*/
   1.113 +uint32_t
   1.114 +ticks2xsec(tmreader * aReader, uint32_t aTicks, uint32_t aResolution)
   1.115 +{
   1.116 +    return (uint32_t)((aResolution * aTicks)/aReader->ticksPerSec);
   1.117 +}
   1.118 +
   1.119 +#define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000)
   1.120 +#define ticks2usec(reader, ticks) ticks2xsec((reader), (ticks), 1000000)
   1.121 +
   1.122 +/*
   1.123 +** initOptions
   1.124 +**
   1.125 +** Determine global settings for the application.
   1.126 +** Returns 0 on success.
   1.127 +*/
   1.128 +int
   1.129 +initOptions(int aArgCount, char **aArgArray)
   1.130 +{
   1.131 +    int retval = 0;
   1.132 +    int traverse = 0;
   1.133 +
   1.134 +    /*
   1.135 +     **  Set the initial global default options.
   1.136 +     */
   1.137 +#define ST_CMD_OPTION_BOOL(option_name, option_genre, option_help) globals.mCommandLineOptions.m##option_name = PR_FALSE;
   1.138 +#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);
   1.139 +#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'; } }
   1.140 +#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;
   1.141 +#define ST_CMD_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) globals.mCommandLineOptions.m##option_name = default_value * multiplier;
   1.142 +#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; }
   1.143 +
   1.144 +#include "stoptions.h"
   1.145 +
   1.146 +    /*
   1.147 +     ** Go through all arguments.
   1.148 +     ** Two dashes lead off an option.
   1.149 +     ** Any single dash leads off help, unless it is a lone dash (stdin).
   1.150 +     ** Anything else will be attempted as a file to be processed.
   1.151 +     */
   1.152 +    for (traverse = 1; traverse < aArgCount; traverse++) {
   1.153 +        if ('-' == aArgArray[traverse][0] && '-' == aArgArray[traverse][1]) {
   1.154 +            const char *option = &aArgArray[traverse][2];
   1.155 +
   1.156 +            /*
   1.157 +             **  Initial if(0) needed to make "else if"s valid.
   1.158 +             */
   1.159 +            if (0) {
   1.160 +            }
   1.161 +
   1.162 +#define ST_CMD_OPTION_BOOL(option_name, option_genre, option_help) \
   1.163 +    else if(0 == strcasecmp(option, #option_name)) \
   1.164 +    { \
   1.165 +        globals.mCommandLineOptions.m##option_name = PR_TRUE; \
   1.166 +    }
   1.167 +#define ST_CMD_OPTION_STRING(option_name, option_genre, default_value, option_help) \
   1.168 +    else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   1.169 +    { \
   1.170 +        PR_snprintf(globals.mCommandLineOptions.m##option_name, sizeof(globals.mCommandLineOptions.m##option_name), "%s", option + strlen(#option_name "=")); \
   1.171 +    }
   1.172 +#define ST_CMD_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
   1.173 +    else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   1.174 +    { \
   1.175 +        int arrLoop = 0; \
   1.176 +        \
   1.177 +        for(arrLoop = 0; arrLoop < array_size; arrLoop++) \
   1.178 +        { \
   1.179 +            if('\0' == globals.mCommandLineOptions.m##option_name[arrLoop][0]) \
   1.180 +            { \
   1.181 +                break; \
   1.182 +            } \
   1.183 +        }\
   1.184 +        \
   1.185 +        if(arrLoop != array_size) \
   1.186 +        { \
   1.187 +            PR_snprintf(globals.mCommandLineOptions.m##option_name[arrLoop], sizeof(globals.mCommandLineOptions.m##option_name[arrLoop]), "%s", option + strlen(#option_name "=")); \
   1.188 +        } \
   1.189 +        else \
   1.190 +        { \
   1.191 +            REPORT_ERROR_MSG(__LINE__, option); \
   1.192 +            retval = __LINE__; \
   1.193 +            globals.mCommandLineOptions.mHelp = PR_TRUE; \
   1.194 +        } \
   1.195 +    }
   1.196 +#define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help) \
   1.197 +    else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   1.198 +    { \
   1.199 +        const char** expand = NULL; \
   1.200 +        \
   1.201 +        expand = (const char**)realloc((void*)globals.mCommandLineOptions.m##option_name, sizeof(const char*) * (globals.mCommandLineOptions.m##option_name##Count + 1)); \
   1.202 +        if(NULL != expand) \
   1.203 +        { \
   1.204 +            globals.mCommandLineOptions.m##option_name = expand; \
   1.205 +            globals.mCommandLineOptions.m##option_name[globals.mCommandLineOptions.m##option_name##Count] = option + strlen(#option_name "="); \
   1.206 +            globals.mCommandLineOptions.m##option_name##Count++; \
   1.207 +        } \
   1.208 +        else \
   1.209 +        { \
   1.210 +            retval = __LINE__; \
   1.211 +            globals.mCommandLineOptions.mHelp = PR_TRUE; \
   1.212 +        } \
   1.213 +    }
   1.214 +#define ST_CMD_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
   1.215 +    else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   1.216 +    { \
   1.217 +        int32_t scanRes = 0; \
   1.218 +        \
   1.219 +        scanRes = PR_sscanf(option + strlen(#option_name "="), "%u", &globals.mCommandLineOptions.m##option_name); \
   1.220 +        if(1 != scanRes) \
   1.221 +        { \
   1.222 +            REPORT_ERROR_MSG(__LINE__, option); \
   1.223 +            retval = __LINE__; \
   1.224 +            globals.mCommandLineOptions.mHelp = PR_TRUE; \
   1.225 +        } \
   1.226 +    }
   1.227 +#define ST_CMD_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
   1.228 +    else if(0 == strncasecmp(option, #option_name "=", strlen(#option_name "="))) \
   1.229 +    { \
   1.230 +        int32_t scanRes = 0; \
   1.231 +        \
   1.232 +        scanRes = PR_sscanf(option + strlen(#option_name "="), "%llu", &globals.mCommandLineOptions.m##option_name##64); \
   1.233 +        if(1 != scanRes) \
   1.234 +        { \
   1.235 +            REPORT_ERROR_MSG(__LINE__, option); \
   1.236 +            retval = __LINE__; \
   1.237 +            globals.mCommandLineOptions.mHelp = PR_TRUE; \
   1.238 +        } \
   1.239 +    }
   1.240 +
   1.241 +#include "stoptions.h"
   1.242 +
   1.243 +            /*
   1.244 +             **  If no match on options, this else will get hit.
   1.245 +             */
   1.246 +            else {
   1.247 +                REPORT_ERROR_MSG(__LINE__, option);
   1.248 +                retval = __LINE__;
   1.249 +                globals.mCommandLineOptions.mHelp = PR_TRUE;
   1.250 +            }
   1.251 +        }
   1.252 +        else if ('-' == aArgArray[traverse][0]
   1.253 +                 && '\0' != aArgArray[traverse][1]) {
   1.254 +            /*
   1.255 +             **  Show help, bad/legacy option.
   1.256 +             */
   1.257 +            REPORT_ERROR_MSG(__LINE__, aArgArray[traverse]);
   1.258 +            retval = __LINE__;
   1.259 +            globals.mCommandLineOptions.mHelp = PR_TRUE;
   1.260 +        }
   1.261 +        else {
   1.262 +            /*
   1.263 +             ** Default is same as FileName option, the file to process.
   1.264 +             */
   1.265 +            PR_snprintf(globals.mCommandLineOptions.mFileName,
   1.266 +                        sizeof(globals.mCommandLineOptions.mFileName), "%s",
   1.267 +                        aArgArray[traverse]);
   1.268 +        }
   1.269 +    }
   1.270 +
   1.271 +    /*
   1.272 +     ** initialize the categories
   1.273 +     */
   1.274 +    initCategories(&globals);
   1.275 +
   1.276 +    return retval;
   1.277 +}
   1.278 +
   1.279 +#if ST_WANT_GRAPHS
   1.280 +/*
   1.281 +** createGraph
   1.282 +**
   1.283 +** Create a GD image with the common properties of a graph.
   1.284 +** Upon return, you normally allocate legend colors,
   1.285 +**  draw your graph inside the region
   1.286 +**  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGH-STGD_MARGIN,
   1.287 +**  and then call drawGraph to format the surrounding information.
   1.288 +**
   1.289 +** You should use the normal GD image release function, gdImageDestroy
   1.290 +**  when done with it.
   1.291 +**
   1.292 +** Image attributes:
   1.293 +**   STGD_WIDTHxSTGD_HEIGHT
   1.294 +**   trasparent (white) background
   1.295 +**   incremental display
   1.296 +*/
   1.297 +gdImagePtr
   1.298 +createGraph(int *aTransparencyColor)
   1.299 +{
   1.300 +    gdImagePtr retval = NULL;
   1.301 +
   1.302 +    if (NULL != aTransparencyColor) {
   1.303 +        *aTransparencyColor = -1;
   1.304 +
   1.305 +        retval = gdImageCreate(STGD_WIDTH, STGD_HEIGHT);
   1.306 +        if (NULL != retval) {
   1.307 +            /*
   1.308 +             ** Background color (first one).
   1.309 +             */
   1.310 +            *aTransparencyColor = gdImageColorAllocate(retval, 255, 255, 255);
   1.311 +            if (-1 != *aTransparencyColor) {
   1.312 +                /*
   1.313 +                 ** As transparency.
   1.314 +                 */
   1.315 +                gdImageColorTransparent(retval, *aTransparencyColor);
   1.316 +            }
   1.317 +
   1.318 +            /*
   1.319 +             ** And to set interlacing.
   1.320 +             */
   1.321 +            gdImageInterlace(retval, 1);
   1.322 +        }
   1.323 +        else {
   1.324 +            REPORT_ERROR(__LINE__, gdImageCreate);
   1.325 +        }
   1.326 +    }
   1.327 +    else {
   1.328 +        REPORT_ERROR(__LINE__, createGraph);
   1.329 +    }
   1.330 +
   1.331 +    return retval;
   1.332 +}
   1.333 +#endif /* ST_WANT_GRAPHS */
   1.334 +
   1.335 +#if ST_WANT_GRAPHS
   1.336 +/*
   1.337 +** drawGraph
   1.338 +**
   1.339 +** This function mainly exists to simplify putitng all the pretty lace
   1.340 +**  around a home made graph.
   1.341 +*/
   1.342 +void
   1.343 +drawGraph(gdImagePtr aImage, int aColor,
   1.344 +          const char *aGraphTitle,
   1.345 +          const char *aXAxisTitle,
   1.346 +          const char *aYAxisTitle,
   1.347 +          uint32_t aXMarkCount,
   1.348 +          uint32_t * aXMarkPercents,
   1.349 +          const char **aXMarkTexts,
   1.350 +          uint32_t aYMarkCount,
   1.351 +          uint32_t * aYMarkPercents,
   1.352 +          const char **aYMarkTexts,
   1.353 +          uint32_t aLegendCount,
   1.354 +          int *aLegendColors, const char **aLegendTexts)
   1.355 +{
   1.356 +    if (NULL != aImage && NULL != aGraphTitle &&
   1.357 +        NULL != aXAxisTitle && NULL != aYAxisTitle &&
   1.358 +        (0 == aXMarkCount || (NULL != aXMarkPercents && NULL != aXMarkTexts))
   1.359 +        && (0 == aYMarkCount
   1.360 +            || (NULL != aYMarkPercents && NULL != aYMarkTexts))
   1.361 +        && (0 == aLegendCount
   1.362 +            || (NULL != aLegendColors && NULL != aLegendTexts))) {
   1.363 +        int margin = 1;
   1.364 +        uint32_t traverse = 0;
   1.365 +        uint32_t target = 0;
   1.366 +        const int markSize = 2;
   1.367 +        int x1 = 0;
   1.368 +        int y1 = 0;
   1.369 +        int x2 = 0;
   1.370 +        int y2 = 0;
   1.371 +        time_t theTimeT = time(NULL);
   1.372 +        char *theTime = ctime(&theTimeT);
   1.373 +        const char *logo = "SpaceTrace";
   1.374 +        gdFontPtr titleFont = gdFontMediumBold;
   1.375 +        gdFontPtr markFont = gdFontTiny;
   1.376 +        gdFontPtr dateFont = gdFontTiny;
   1.377 +        gdFontPtr axisFont = gdFontSmall;
   1.378 +        gdFontPtr legendFont = gdFontTiny;
   1.379 +        gdFontPtr logoFont = gdFontTiny;
   1.380 +
   1.381 +        /*
   1.382 +         ** Fixup the color.
   1.383 +         ** Black by default.
   1.384 +         */
   1.385 +        if (-1 == aColor) {
   1.386 +            aColor = gdImageColorAllocate(aImage, 0, 0, 0);
   1.387 +        }
   1.388 +        if (-1 == aColor) {
   1.389 +            aColor = gdImageColorClosest(aImage, 0, 0, 0);
   1.390 +        }
   1.391 +
   1.392 +        /*
   1.393 +         ** Output the box.
   1.394 +         */
   1.395 +        x1 = STGD_MARGIN - margin;
   1.396 +        y1 = STGD_MARGIN - margin;
   1.397 +        x2 = STGD_WIDTH - x1;
   1.398 +        y2 = STGD_HEIGHT - y1;
   1.399 +        gdImageRectangle(aImage, x1, y1, x2, y2, aColor);
   1.400 +        margin++;
   1.401 +
   1.402 +        /*
   1.403 +         ** Need to make small markings on the graph to indicate where the
   1.404 +         **  labels line up exactly.
   1.405 +         ** While we're at it, draw the label text.
   1.406 +         */
   1.407 +        for (traverse = 0; traverse < aXMarkCount; traverse++) {
   1.408 +            target =
   1.409 +                ((STGD_WIDTH -
   1.410 +                  (STGD_MARGIN * 2)) * aXMarkPercents[traverse]) / 100;
   1.411 +
   1.412 +            x1 = STGD_MARGIN + target;
   1.413 +            y1 = STGD_MARGIN - margin;
   1.414 +            x2 = x1;
   1.415 +            y2 = y1 - markSize;
   1.416 +            gdImageLine(aImage, x1, y1, x2, y2, aColor);
   1.417 +
   1.418 +            y1 = STGD_HEIGHT - y1;
   1.419 +            y2 = STGD_HEIGHT - y2;
   1.420 +            gdImageLine(aImage, x1, y1, x2, y2, aColor);
   1.421 +
   1.422 +            if (NULL != aXMarkTexts[traverse]) {
   1.423 +                x1 = STGD_MARGIN + target - (markFont->h / 2);
   1.424 +                y1 = STGD_HEIGHT - STGD_MARGIN + margin + markSize +
   1.425 +                    (strlen(aXMarkTexts[traverse]) * markFont->w);
   1.426 +                gdImageStringUp(aImage, markFont, x1, y1,
   1.427 +                                (unsigned char *) aXMarkTexts[traverse],
   1.428 +                                aColor);
   1.429 +            }
   1.430 +        }
   1.431 +        for (traverse = 0; traverse < aYMarkCount; traverse++) {
   1.432 +            target =
   1.433 +                ((STGD_HEIGHT - (STGD_MARGIN * 2)) * (100 -
   1.434 +                                                      aYMarkPercents
   1.435 +                                                      [traverse])) / 100;
   1.436 +
   1.437 +            x1 = STGD_MARGIN - margin;
   1.438 +            y1 = STGD_MARGIN + target;
   1.439 +            x2 = x1 - markSize;
   1.440 +            y2 = y1;
   1.441 +            gdImageLine(aImage, x1, y1, x2, y2, aColor);
   1.442 +
   1.443 +            x1 = STGD_WIDTH - x1;
   1.444 +            x2 = STGD_WIDTH - x2;
   1.445 +            gdImageLine(aImage, x1, y1, x2, y2, aColor);
   1.446 +
   1.447 +            if (NULL != aYMarkTexts[traverse]) {
   1.448 +                x1 = STGD_MARGIN - margin - markSize -
   1.449 +                    (strlen(aYMarkTexts[traverse]) * markFont->w);
   1.450 +                y1 = STGD_MARGIN + target - (markFont->h / 2);
   1.451 +                gdImageString(aImage, markFont, x1, y1,
   1.452 +                              (unsigned char *) aYMarkTexts[traverse],
   1.453 +                              aColor);
   1.454 +            }
   1.455 +        }
   1.456 +        margin += markSize;
   1.457 +
   1.458 +        /*
   1.459 +         ** Title will be centered above the image.
   1.460 +         */
   1.461 +        x1 = (STGD_WIDTH / 2) - ((strlen(aGraphTitle) * titleFont->w) / 2);
   1.462 +        y1 = ((STGD_MARGIN - margin) / 2) - (titleFont->h / 2);
   1.463 +        gdImageString(aImage, titleFont, x1, y1,
   1.464 +                      (unsigned char *) aGraphTitle, aColor);
   1.465 +
   1.466 +        /*
   1.467 +         ** Upper left will be the date.
   1.468 +         */
   1.469 +        x1 = 0;
   1.470 +        y1 = 0;
   1.471 +        traverse = strlen(theTime) - 1;
   1.472 +        if (isspace(theTime[traverse])) {
   1.473 +            theTime[traverse] = '\0';
   1.474 +        }
   1.475 +        gdImageString(aImage, dateFont, x1, y1, (unsigned char *) theTime,
   1.476 +                      aColor);
   1.477 +
   1.478 +        /*
   1.479 +         ** Lower right will be the logo.
   1.480 +         */
   1.481 +        x1 = STGD_WIDTH - (strlen(logo) * logoFont->w);
   1.482 +        y1 = STGD_HEIGHT - logoFont->h;
   1.483 +        gdImageString(aImage, logoFont, x1, y1, (unsigned char *) logo,
   1.484 +                      aColor);
   1.485 +
   1.486 +        /*
   1.487 +         ** X and Y axis titles
   1.488 +         */
   1.489 +        x1 = (STGD_WIDTH / 2) - ((strlen(aXAxisTitle) * axisFont->w) / 2);
   1.490 +        y1 = STGD_HEIGHT - axisFont->h;
   1.491 +        gdImageString(aImage, axisFont, x1, y1, (unsigned char *) aXAxisTitle,
   1.492 +                      aColor);
   1.493 +        x1 = 0;
   1.494 +        y1 = (STGD_HEIGHT / 2) + ((strlen(aYAxisTitle) * axisFont->w) / 2);
   1.495 +        gdImageStringUp(aImage, axisFont, x1, y1,
   1.496 +                        (unsigned char *) aYAxisTitle, aColor);
   1.497 +
   1.498 +        /*
   1.499 +         ** The legend.
   1.500 +         ** Centered on the right hand side, going up.
   1.501 +         */
   1.502 +        x1 = STGD_WIDTH - STGD_MARGIN + margin +
   1.503 +            (aLegendCount * legendFont->h) / 2;
   1.504 +        x2 = STGD_WIDTH - (aLegendCount * legendFont->h);
   1.505 +        if (x1 > x2) {
   1.506 +            x1 = x2;
   1.507 +        }
   1.508 +
   1.509 +        y1 = 0;
   1.510 +        for (traverse = 0; traverse < aLegendCount; traverse++) {
   1.511 +            y2 = (STGD_HEIGHT / 2) +
   1.512 +                ((strlen(aLegendTexts[traverse]) * legendFont->w) / 2);
   1.513 +            if (y2 > y1) {
   1.514 +                y1 = y2;
   1.515 +            }
   1.516 +        }
   1.517 +        for (traverse = 0; traverse < aLegendCount; traverse++) {
   1.518 +            gdImageStringUp(aImage, legendFont, x1, y1,
   1.519 +                            (unsigned char *) aLegendTexts[traverse],
   1.520 +                            aLegendColors[traverse]);
   1.521 +            x1 += legendFont->h;
   1.522 +        }
   1.523 +    }
   1.524 +}
   1.525 +
   1.526 +#endif /* ST_WANT_GRAPHS */
   1.527 +
   1.528 +#if defined(HAVE_BOUTELL_GD)
   1.529 +/*
   1.530 +** pngSink
   1.531 +**
   1.532 +** GD callback, used to write out the png.
   1.533 +*/
   1.534 +int
   1.535 +pngSink(void *aContext, const char *aBuffer, int aLen)
   1.536 +{
   1.537 +    return PR_Write((PRFileDesc *) aContext, aBuffer, aLen);
   1.538 +}
   1.539 +#endif /* HAVE_BOUTELL_GD */
   1.540 +
   1.541 +/*
   1.542 +** FormatNumber
   1.543 +**
   1.544 +** Formats a number with thousands separator. Don't free the result. Returns
   1.545 +** static data.
   1.546 +*/
   1.547 +char *
   1.548 +FormatNumber(int32_t num)
   1.549 +{
   1.550 +    static char buf[64];
   1.551 +    char tmpbuf[64];
   1.552 +    int len = 0;
   1.553 +    int bufindex = sizeof(buf) - 1;
   1.554 +    int mod3;
   1.555 +
   1.556 +    PR_snprintf(tmpbuf, sizeof(tmpbuf), "%d", num);
   1.557 +
   1.558 +    /* now insert the thousands separator */
   1.559 +    mod3 = 0;
   1.560 +    len = strlen(tmpbuf);
   1.561 +    while (len >= 0) {
   1.562 +        if (tmpbuf[len] >= '0' && tmpbuf[len] <= '9') {
   1.563 +            if (mod3 == 3) {
   1.564 +                buf[bufindex--] = ',';
   1.565 +                mod3 = 0;
   1.566 +            }
   1.567 +            mod3++;
   1.568 +        }
   1.569 +        buf[bufindex--] = tmpbuf[len--];
   1.570 +    }
   1.571 +    return buf + bufindex + 1;
   1.572 +}
   1.573 +
   1.574 +/*
   1.575 +** actualByteSize
   1.576 +**
   1.577 +** Apply alignment and overhead to size to figure out actual byte size
   1.578 +*/
   1.579 +uint32_t
   1.580 +actualByteSize(STOptions * inOptions, uint32_t retval)
   1.581 +{
   1.582 +    /*
   1.583 +     ** Need to bump the result by our alignment and overhead.
   1.584 +     ** The idea here is that an allocation actually costs you more than you
   1.585 +     **  thought.
   1.586 +     **
   1.587 +     ** The msvcrt malloc has an alignment of 16 with an overhead of 8.
   1.588 +     ** The win32 HeapAlloc has an alignment of 8 with an overhead of 8.
   1.589 +     */
   1.590 +    if (0 != retval) {
   1.591 +        uint32_t eval = 0;
   1.592 +        uint32_t over = 0;
   1.593 +
   1.594 +        eval = retval - 1;
   1.595 +        if (0 != inOptions->mAlignBy) {
   1.596 +            over = eval % inOptions->mAlignBy;
   1.597 +        }
   1.598 +        retval = eval + inOptions->mOverhead + inOptions->mAlignBy - over;
   1.599 +    }
   1.600 +
   1.601 +    return retval;
   1.602 +}
   1.603 +
   1.604 +/*
   1.605 +** byteSize
   1.606 +**
   1.607 +** Figuring the byte size of an allocation.
   1.608 +** Might expand in the future to report size at a given time.
   1.609 +** For now, just use last relevant event.
   1.610 +*/
   1.611 +uint32_t
   1.612 +byteSize(STOptions * inOptions, STAllocation * aAlloc)
   1.613 +{
   1.614 +    uint32_t retval = 0;
   1.615 +
   1.616 +    if (NULL != aAlloc && 0 != aAlloc->mEventCount) {
   1.617 +        uint32_t index = aAlloc->mEventCount;
   1.618 +
   1.619 +        /*
   1.620 +         ** Generally, the size is the last event's size.
   1.621 +         */
   1.622 +        do {
   1.623 +            index--;
   1.624 +            retval = aAlloc->mEvents[index].mHeapSize;
   1.625 +        }
   1.626 +        while (0 == retval && 0 != index);
   1.627 +    }
   1.628 +    return actualByteSize(inOptions, retval);
   1.629 +}
   1.630 +
   1.631 +
   1.632 +/*
   1.633 +** recalculateAllocationCost
   1.634 +**
   1.635 +** Given an allocation, does a recalculation of Cost - weight, heapcount etc.
   1.636 +** and does the right thing to propagate the cost upwards.
   1.637 +*/
   1.638 +int
   1.639 +recalculateAllocationCost(STOptions * inOptions, STContext * inContext,
   1.640 +                          STRun * aRun, STAllocation * aAllocation,
   1.641 +                          PRBool updateParent)
   1.642 +{
   1.643 +    /*
   1.644 +     ** Now, see if they desire a callsite update.
   1.645 +     ** As mentioned previously, we decide if the run desires us to
   1.646 +     **  manipulate the callsite data only if its stamp is set.
   1.647 +     ** We change all callsites and parent callsites to have that
   1.648 +     **  stamp as well, so as to mark them as being relevant to
   1.649 +     **  the current run in question.
   1.650 +     */
   1.651 +    if (NULL != inContext && 0 != aRun->mStats[inContext->mIndex].mStamp) {
   1.652 +        uint32_t timeval =
   1.653 +            aAllocation->mMaxTimeval - aAllocation->mMinTimeval;
   1.654 +        uint32_t size = byteSize(inOptions, aAllocation);
   1.655 +        uint32_t heapCost = aAllocation->mHeapRuntimeCost;
   1.656 +        uint64_t timeval64 = timeval;
   1.657 +        uint64_t size64 = size;
   1.658 +        uint64_t weight64 = timeval64 * size64;
   1.659 +
   1.660 +        /*
   1.661 +         ** First, update this run.
   1.662 +         */
   1.663 +        aRun->mStats[inContext->mIndex].mCompositeCount++;
   1.664 +        aRun->mStats[inContext->mIndex].mHeapRuntimeCost += heapCost;
   1.665 +        aRun->mStats[inContext->mIndex].mSize += size;
   1.666 +        aRun->mStats[inContext->mIndex].mTimeval64 += timeval64;
   1.667 +        aRun->mStats[inContext->mIndex].mWeight64 += weight64;
   1.668 +
   1.669 +        /*
   1.670 +         ** Use the first event of the allocation to update the parent
   1.671 +         **  callsites.
   1.672 +         ** This has positive effect of not updating realloc callsites
   1.673 +         **  with the same data over and over again.
   1.674 +         */
   1.675 +        if (updateParent && 0 < aAllocation->mEventCount) {
   1.676 +            tmcallsite *callsite = aAllocation->mEvents[0].mCallsite;
   1.677 +            STRun *callsiteRun = NULL;
   1.678 +
   1.679 +            /*
   1.680 +             ** Go up parents till we drop.
   1.681 +             */
   1.682 +            while (NULL != callsite && NULL != callsite->method) {
   1.683 +                callsiteRun = CALLSITE_RUN(callsite);
   1.684 +                if (NULL != callsiteRun) {
   1.685 +                    /*
   1.686 +                     ** Do we init it?
   1.687 +                     */
   1.688 +                    if (callsiteRun->mStats[inContext->mIndex].mStamp !=
   1.689 +                        aRun->mStats[inContext->mIndex].mStamp) {
   1.690 +                        memset(&callsiteRun->mStats[inContext->mIndex], 0,
   1.691 +                               sizeof(STCallsiteStats));
   1.692 +                        callsiteRun->mStats[inContext->mIndex].mStamp =
   1.693 +                            aRun->mStats[inContext->mIndex].mStamp;
   1.694 +                    }
   1.695 +
   1.696 +                    /*
   1.697 +                     ** Add the values.
   1.698 +                     ** Note that if the allocation was ever realloced,
   1.699 +                     **  we are actually recording the final size.
   1.700 +                     ** Also, the composite count does not include
   1.701 +                     **  calls to realloc (or free for that matter),
   1.702 +                     **  but rather is simply a count of actual heap
   1.703 +                     **  allocation objects, from which someone will
   1.704 +                     **  draw conclusions regarding number of malloc
   1.705 +                     **  and free calls.
   1.706 +                     ** It is possible to generate the exact number
   1.707 +                     **  of calls to free/malloc/realloc should the
   1.708 +                     **  absolute need arise to count them individually,
   1.709 +                     **  but I fear it will take mucho memory and this
   1.710 +                     **  is perhaps good enough for now.
   1.711 +                     */
   1.712 +                    callsiteRun->mStats[inContext->mIndex].mCompositeCount++;
   1.713 +                    callsiteRun->mStats[inContext->mIndex].mHeapRuntimeCost +=
   1.714 +                        heapCost;
   1.715 +                    callsiteRun->mStats[inContext->mIndex].mSize += size;
   1.716 +                    callsiteRun->mStats[inContext->mIndex].mTimeval64 +=
   1.717 +                        timeval64;
   1.718 +                    callsiteRun->mStats[inContext->mIndex].mWeight64 +=
   1.719 +                        weight64;
   1.720 +                }
   1.721 +
   1.722 +                callsite = callsite->parent;
   1.723 +            }
   1.724 +        }
   1.725 +    }
   1.726 +
   1.727 +    return 0;
   1.728 +}
   1.729 +
   1.730 +
   1.731 +/*
   1.732 +** appendAllocation
   1.733 +**
   1.734 +** Given a run, append the allocation to it.
   1.735 +** No DUP checks are done.
   1.736 +** Also, we might want to update the parent callsites with stats.
   1.737 +** We decide to do this heavy duty work only if the run we are appending
   1.738 +**  to has a non ZERO mStats[].mStamp, meaning that it is asking to track
   1.739 +**  such information when it was created.
   1.740 +** Returns !0 on success.
   1.741 +*/
   1.742 +int
   1.743 +appendAllocation(STOptions * inOptions, STContext * inContext,
   1.744 +                 STRun * aRun, STAllocation * aAllocation)
   1.745 +{
   1.746 +    int retval = 0;
   1.747 +
   1.748 +    if (NULL != aRun && NULL != aAllocation && NULL != inOptions) {
   1.749 +        STAllocation **expand = NULL;
   1.750 +
   1.751 +        /*
   1.752 +         ** Expand the size of the array if needed.
   1.753 +         */
   1.754 +        expand = (STAllocation **) realloc(aRun->mAllocations,
   1.755 +                                           sizeof(STAllocation *) *
   1.756 +                                           (aRun->mAllocationCount + 1));
   1.757 +        if (NULL != expand) {
   1.758 +            /*
   1.759 +             ** Reassign in case of pointer move.
   1.760 +             */
   1.761 +            aRun->mAllocations = expand;
   1.762 +
   1.763 +            /*
   1.764 +             ** Stick the allocation in.
   1.765 +             */
   1.766 +            aRun->mAllocations[aRun->mAllocationCount] = aAllocation;
   1.767 +
   1.768 +            /*
   1.769 +             ** If this is the global run, we need to let the allocation
   1.770 +             **  track the index back to us.
   1.771 +             */
   1.772 +            if (&globals.mRun == aRun) {
   1.773 +                aAllocation->mRunIndex = aRun->mAllocationCount;
   1.774 +            }
   1.775 +
   1.776 +            /*
   1.777 +             ** Increase the count.
   1.778 +             */
   1.779 +            aRun->mAllocationCount++;
   1.780 +
   1.781 +            /*
   1.782 +             ** We're good.
   1.783 +             */
   1.784 +            retval = __LINE__;
   1.785 +
   1.786 +            /*
   1.787 +             ** update allocation cost
   1.788 +             */
   1.789 +            recalculateAllocationCost(inOptions, inContext, aRun, aAllocation,
   1.790 +                                      PR_TRUE);
   1.791 +        }
   1.792 +        else {
   1.793 +            REPORT_ERROR(__LINE__, appendAllocation);
   1.794 +        }
   1.795 +    }
   1.796 +    else {
   1.797 +        REPORT_ERROR(__LINE__, appendAllocation);
   1.798 +    }
   1.799 +
   1.800 +    return retval;
   1.801 +}
   1.802 +
   1.803 +/*
   1.804 +** hasCallsiteMatch
   1.805 +**
   1.806 +** Determine if the callsite or the other callsites has the matching text.
   1.807 +**
   1.808 +** Returns 0 if there is no match.
   1.809 +*/
   1.810 +int
   1.811 +hasCallsiteMatch(tmcallsite * aCallsite, const char *aMatch, int aDirection)
   1.812 +{
   1.813 +    int retval = 0;
   1.814 +
   1.815 +    if (NULL != aCallsite && NULL != aCallsite->method &&
   1.816 +        NULL != aMatch && '\0' != *aMatch) {
   1.817 +        const char *methodName = NULL;
   1.818 +
   1.819 +        do {
   1.820 +            methodName = tmmethodnode_name(aCallsite->method);
   1.821 +            if (NULL != methodName && NULL != strstr(methodName, aMatch)) {
   1.822 +                /*
   1.823 +                 ** Contains the text.
   1.824 +                 */
   1.825 +                retval = __LINE__;
   1.826 +                break;
   1.827 +            }
   1.828 +            else {
   1.829 +                switch (aDirection) {
   1.830 +                case ST_FOLLOW_SIBLINGS:
   1.831 +                    aCallsite = aCallsite->siblings;
   1.832 +                    break;
   1.833 +                case ST_FOLLOW_PARENTS:
   1.834 +                    aCallsite = aCallsite->parent;
   1.835 +                    break;
   1.836 +                default:
   1.837 +                    aCallsite = NULL;
   1.838 +                    REPORT_ERROR(__LINE__, hasCallsiteMatch);
   1.839 +                    break;
   1.840 +                }
   1.841 +            }
   1.842 +        }
   1.843 +        while (NULL != aCallsite && NULL != aCallsite->method);
   1.844 +    }
   1.845 +    else {
   1.846 +        REPORT_ERROR(__LINE__, hasCallsiteMatch);
   1.847 +    }
   1.848 +
   1.849 +    return retval;
   1.850 +}
   1.851 +
   1.852 +/*
   1.853 +** harvestRun
   1.854 +**
   1.855 +** Provide a simply way to go over a run, and yield the relevant allocations.
   1.856 +** The restrictions are easily set via the options page or the command
   1.857 +**  line switches.
   1.858 +**
   1.859 +** On any match, add the allocation to the provided run.
   1.860 +**
   1.861 +** This makes it much easier for all the code to respect the options in
   1.862 +**  force.
   1.863 +**
   1.864 +** Returns !0 on error, though aOutRun may contain a partial data set.
   1.865 +*/
   1.866 +int
   1.867 +harvestRun(const STRun * aInRun, STRun * aOutRun,
   1.868 +           STOptions * aOptions, STContext * inContext)
   1.869 +{
   1.870 +    int retval = 0;
   1.871 +
   1.872 +#if defined(DEBUG_dp)
   1.873 +    PRIntervalTime start = PR_IntervalNow();
   1.874 +
   1.875 +    fprintf(stderr, "DEBUG: harvesting run...\n");
   1.876 +#endif
   1.877 +
   1.878 +    if (NULL != aInRun && NULL != aOutRun && aInRun != aOutRun
   1.879 +        && NULL != aOptions && NULL != inContext) {
   1.880 +        uint32_t traverse = 0;
   1.881 +        STAllocation *current = NULL;
   1.882 +
   1.883 +        for (traverse = 0;
   1.884 +             0 == retval && traverse < aInRun->mAllocationCount; traverse++) {
   1.885 +            current = aInRun->mAllocations[traverse];
   1.886 +            if (NULL != current) {
   1.887 +                uint32_t lifetime = 0;
   1.888 +                uint32_t bytesize = 0;
   1.889 +                uint64_t weight64 = 0;
   1.890 +                uint64_t bytesize64 = 0;
   1.891 +                uint64_t lifetime64 = 0;
   1.892 +                int appendRes = 0;
   1.893 +                int looper = 0;
   1.894 +                PRBool matched = PR_FALSE;
   1.895 +
   1.896 +                /*
   1.897 +                 ** Use this as an opportune time to fixup a memory
   1.898 +                 **  leaked timeval, so as to not completely skew
   1.899 +                 **  the weights.
   1.900 +                 */
   1.901 +                if (ST_TIMEVAL_MAX == current->mMaxTimeval) {
   1.902 +                    current->mMaxTimeval = globals.mMaxTimeval;
   1.903 +                }
   1.904 +
   1.905 +                /*
   1.906 +                 ** Check allocation timeval restrictions.
   1.907 +                 ** We have to slide the recorded timevals to be zero
   1.908 +                 **  based, so that the comparisons make sense.
   1.909 +                 */
   1.910 +                if ((aOptions->mAllocationTimevalMin >
   1.911 +                     (current->mMinTimeval - globals.mMinTimeval)) ||
   1.912 +                    (aOptions->mAllocationTimevalMax <
   1.913 +                     (current->mMinTimeval - globals.mMinTimeval))) {
   1.914 +                    continue;
   1.915 +                }
   1.916 +
   1.917 +                /*
   1.918 +                 ** Check timeval restrictions.
   1.919 +                 ** We have to slide the recorded timevals to be zero
   1.920 +                 **  based, so that the comparisons make sense.
   1.921 +                 */
   1.922 +                if ((aOptions->mTimevalMin >
   1.923 +                     (current->mMinTimeval - globals.mMinTimeval)) ||
   1.924 +                    (aOptions->mTimevalMax <
   1.925 +                     (current->mMinTimeval - globals.mMinTimeval))) {
   1.926 +                    continue;
   1.927 +                }
   1.928 +
   1.929 +                /*
   1.930 +                 ** Check lifetime restrictions.
   1.931 +                 */
   1.932 +                lifetime = current->mMaxTimeval - current->mMinTimeval;
   1.933 +                if ((lifetime < aOptions->mLifetimeMin) ||
   1.934 +                    (lifetime > aOptions->mLifetimeMax)) {
   1.935 +                    continue;
   1.936 +                }
   1.937 +
   1.938 +                /*
   1.939 +                 ** Check byte size restrictions.
   1.940 +                 */
   1.941 +                bytesize = byteSize(aOptions, current);
   1.942 +                if ((bytesize < aOptions->mSizeMin) ||
   1.943 +                    (bytesize > aOptions->mSizeMax)) {
   1.944 +                    continue;
   1.945 +                }
   1.946 +
   1.947 +                /*
   1.948 +                 ** Check weight restrictions.
   1.949 +                 */
   1.950 +                weight64 = (uint64_t)(bytesize * lifetime);
   1.951 +                if (weight64 < aOptions->mWeightMin64 ||
   1.952 +                    weight64 > aOptions->mWeightMax64) {
   1.953 +                    continue;
   1.954 +                }
   1.955 +
   1.956 +                /*
   1.957 +                 ** Possibly restrict the callsite by text.
   1.958 +                 ** Do this last, as it is a heavier check.
   1.959 +                 **
   1.960 +                 ** One day, we may need to expand the logic to check for
   1.961 +                 **  events beyond the initial allocation event.
   1.962 +                 */
   1.963 +                for (looper = 0; ST_SUBSTRING_MATCH_MAX > looper; looper++) {
   1.964 +                    if ('\0' != aOptions->mRestrictText[looper][0]) {
   1.965 +                        if (0 ==
   1.966 +                            hasCallsiteMatch(current->mEvents[0].mCallsite,
   1.967 +                                             aOptions->mRestrictText[looper],
   1.968 +                                             ST_FOLLOW_PARENTS)) {
   1.969 +                            break;
   1.970 +                        }
   1.971 +                    }
   1.972 +                    else {
   1.973 +                        matched = PR_TRUE;
   1.974 +                        break;
   1.975 +                    }
   1.976 +                }
   1.977 +                if (ST_SUBSTRING_MATCH_MAX == looper) {
   1.978 +                    matched = PR_TRUE;
   1.979 +                }
   1.980 +                if (PR_FALSE == matched) {
   1.981 +                    continue;
   1.982 +                }
   1.983 +
   1.984 +                /*
   1.985 +                 ** You get here, we add to the run.
   1.986 +                 */
   1.987 +                appendRes =
   1.988 +                    appendAllocation(aOptions, inContext, aOutRun, current);
   1.989 +                if (0 == appendRes) {
   1.990 +                    retval = __LINE__;
   1.991 +                    REPORT_ERROR(__LINE__, appendAllocation);
   1.992 +                }
   1.993 +            }
   1.994 +        }
   1.995 +    }
   1.996 +
   1.997 +#if defined(DEBUG_dp)
   1.998 +    fprintf(stderr, "DEBUG: harvesting ends: %dms [%d allocations]\n",
   1.999 +            PR_IntervalToMilliseconds(PR_IntervalNow() - start),
  1.1000 +            aInRun->mAllocationCount);
  1.1001 +#endif
  1.1002 +    return retval;
  1.1003 +}
  1.1004 +
  1.1005 +/*
  1.1006 +** recalculateRunCost
  1.1007 +**
  1.1008 +** Goes over all allocations of a run and recalculates and propagates
  1.1009 +** the allocation costs - weight, heapcount, size
  1.1010 +*/
  1.1011 +int
  1.1012 +recalculateRunCost(STOptions * inOptions, STContext * inContext, STRun * aRun)
  1.1013 +{
  1.1014 +    uint32_t traverse = 0;
  1.1015 +    STAllocation *current = NULL;
  1.1016 +
  1.1017 +#if defined(DEBUG_dp)
  1.1018 +    PRIntervalTime start = PR_IntervalNow();
  1.1019 +
  1.1020 +    fprintf(stderr, "DEBUG: recalculateRunCost...\n");
  1.1021 +#endif
  1.1022 +
  1.1023 +    if (NULL == aRun)
  1.1024 +        return -1;
  1.1025 +
  1.1026 +    /* reset stats of this run to 0 to begin recalculation */
  1.1027 +    memset(&aRun->mStats[inContext->mIndex], 0, sizeof(STCallsiteStats));
  1.1028 +
  1.1029 +    /* reset timestamp to force propogation of cost */
  1.1030 +    aRun->mStats[inContext->mIndex].mStamp = PR_IntervalNow();
  1.1031 +
  1.1032 +    for (traverse = 0; traverse < aRun->mAllocationCount; traverse++) {
  1.1033 +        current = aRun->mAllocations[traverse];
  1.1034 +        if (NULL != current) {
  1.1035 +            recalculateAllocationCost(inOptions, inContext, aRun, current,
  1.1036 +                                      PR_TRUE);
  1.1037 +        }
  1.1038 +    }
  1.1039 +
  1.1040 +#if defined(DEBUG_dp)
  1.1041 +    fprintf(stderr, "DEBUG: recalculateRunCost ends: %dms [%d allocations]\n",
  1.1042 +            PR_IntervalToMilliseconds(PR_IntervalNow() - start),
  1.1043 +            aRun->mAllocationCount);
  1.1044 +#endif
  1.1045 +
  1.1046 +    return 0;
  1.1047 +}
  1.1048 +
  1.1049 +
  1.1050 +/*
  1.1051 +** compareAllocations
  1.1052 +**
  1.1053 +** qsort callback.
  1.1054 +** Compare the allocations as specified by the options.
  1.1055 +*/
  1.1056 +int
  1.1057 +compareAllocations(const void *aAlloc1, const void *aAlloc2, void *aContext)
  1.1058 +{
  1.1059 +    int retval = 0;
  1.1060 +    STOptions *inOptions = (STOptions *) aContext;
  1.1061 +
  1.1062 +    if (NULL != aAlloc1 && NULL != aAlloc2 && NULL != inOptions) {
  1.1063 +        STAllocation *alloc1 = *((STAllocation **) aAlloc1);
  1.1064 +        STAllocation *alloc2 = *((STAllocation **) aAlloc2);
  1.1065 +
  1.1066 +        if (NULL != alloc1 && NULL != alloc2) {
  1.1067 +            /*
  1.1068 +             ** Logic determined by pref/option.
  1.1069 +             */
  1.1070 +            switch (inOptions->mOrderBy) {
  1.1071 +            case ST_COUNT:
  1.1072 +                /*
  1.1073 +                 ** "By count" on a single allocation means nothing,
  1.1074 +                 **  fall through to weight.
  1.1075 +                 */
  1.1076 +            case ST_WEIGHT:
  1.1077 +                {
  1.1078 +                    uint64_t weight164 = 0;
  1.1079 +                    uint64_t weight264 = 0;
  1.1080 +                    uint64_t bytesize164 = 0;
  1.1081 +                    uint64_t bytesize264 = 0;
  1.1082 +                    uint64_t timeval164 = 0;
  1.1083 +                    uint64_t timeval264 = 0;
  1.1084 +
  1.1085 +                    bytesize164 = byteSize(inOptions, alloc1);
  1.1086 +                    timeval164 = alloc1->mMaxTimeval - alloc1->mMinTimeval;
  1.1087 +                    weight164 = bytesize164 * timeval164;
  1.1088 +                    bytesize264 = byteSize(inOptions, alloc2);
  1.1089 +                    timeval264 = alloc2->mMaxTimeval - alloc2->mMinTimeval;
  1.1090 +                    weight264 = bytesize264 * timeval264;
  1.1091 +
  1.1092 +                    if (weight164 < weight264) {
  1.1093 +                        retval = __LINE__;
  1.1094 +                    }
  1.1095 +                    else if (weight164 > weight264) {
  1.1096 +                        retval = -__LINE__;
  1.1097 +                    }
  1.1098 +                }
  1.1099 +                break;
  1.1100 +
  1.1101 +            case ST_SIZE:
  1.1102 +                {
  1.1103 +                    uint32_t size1 = byteSize(inOptions, alloc1);
  1.1104 +                    uint32_t size2 = byteSize(inOptions, alloc2);
  1.1105 +
  1.1106 +                    if (size1 < size2) {
  1.1107 +                        retval = __LINE__;
  1.1108 +                    }
  1.1109 +                    else if (size1 > size2) {
  1.1110 +                        retval = -__LINE__;
  1.1111 +                    }
  1.1112 +                }
  1.1113 +                break;
  1.1114 +
  1.1115 +            case ST_TIMEVAL:
  1.1116 +                {
  1.1117 +                    uint32_t timeval1 =
  1.1118 +                        (alloc1->mMaxTimeval - alloc1->mMinTimeval);
  1.1119 +                    uint32_t timeval2 =
  1.1120 +                        (alloc2->mMaxTimeval - alloc2->mMinTimeval);
  1.1121 +
  1.1122 +                    if (timeval1 < timeval2) {
  1.1123 +                        retval = __LINE__;
  1.1124 +                    }
  1.1125 +                    else if (timeval1 > timeval2) {
  1.1126 +                        retval = -__LINE__;
  1.1127 +                    }
  1.1128 +                }
  1.1129 +                break;
  1.1130 +
  1.1131 +            case ST_HEAPCOST:
  1.1132 +                {
  1.1133 +                    uint32_t cost1 = alloc1->mHeapRuntimeCost;
  1.1134 +                    uint32_t cost2 = alloc2->mHeapRuntimeCost;
  1.1135 +
  1.1136 +                    if (cost1 < cost2) {
  1.1137 +                        retval = __LINE__;
  1.1138 +                    }
  1.1139 +                    else if (cost1 > cost2) {
  1.1140 +                        retval = -__LINE__;
  1.1141 +                    }
  1.1142 +                }
  1.1143 +                break;
  1.1144 +
  1.1145 +            default:
  1.1146 +                {
  1.1147 +                    REPORT_ERROR(__LINE__, compareAllocations);
  1.1148 +                }
  1.1149 +                break;
  1.1150 +            }
  1.1151 +        }
  1.1152 +    }
  1.1153 +
  1.1154 +    return retval;
  1.1155 +}
  1.1156 +
  1.1157 +/*
  1.1158 +** sortRun
  1.1159 +**
  1.1160 +** Given a run, sort it in the manner specified by the options.
  1.1161 +** Returns !0 on failure.
  1.1162 +*/
  1.1163 +int
  1.1164 +sortRun(STOptions * inOptions, STRun * aRun)
  1.1165 +{
  1.1166 +    int retval = 0;
  1.1167 +
  1.1168 +    if (NULL != aRun && NULL != inOptions) {
  1.1169 +        if (NULL != aRun->mAllocations && 0 < aRun->mAllocationCount) {
  1.1170 +            NS_QuickSort(aRun->mAllocations, aRun->mAllocationCount,
  1.1171 +                         sizeof(STAllocation *), compareAllocations,
  1.1172 +                         inOptions);
  1.1173 +        }
  1.1174 +    }
  1.1175 +    else {
  1.1176 +        retval = __LINE__;
  1.1177 +        REPORT_ERROR(__LINE__, sortRun);
  1.1178 +    }
  1.1179 +
  1.1180 +    return retval;
  1.1181 +}
  1.1182 +
  1.1183 +/*
  1.1184 +** createRun
  1.1185 +**
  1.1186 +** Returns a newly allocated run, properly initialized.
  1.1187 +** Must call freeRun() with the new STRun.
  1.1188 +**
  1.1189 +** ONLY PASS IN A NON_ZERO STAMP IF YOU KNOW WHAT YOU ARE DOING!!!
  1.1190 +** A non zero stamp in a run has side effects all over the
  1.1191 +**  callsites of the allocations added to the run and their
  1.1192 +**  parents.
  1.1193 +**
  1.1194 +** Returns NULL on failure.
  1.1195 +*/
  1.1196 +STRun *
  1.1197 +createRun(STContext * inContext, uint32_t aStamp)
  1.1198 +{
  1.1199 +    STRun *retval = NULL;
  1.1200 +
  1.1201 +    retval = (STRun *) calloc(1, sizeof(STRun));
  1.1202 +    if (NULL != retval) {
  1.1203 +        retval->mStats =
  1.1204 +            (STCallsiteStats *) calloc(globals.mCommandLineOptions.mContexts,
  1.1205 +                                       sizeof(STCallsiteStats));
  1.1206 +        if (NULL != retval->mStats) {
  1.1207 +            if (NULL != inContext) {
  1.1208 +                retval->mStats[inContext->mIndex].mStamp = aStamp;
  1.1209 +            }
  1.1210 +        }
  1.1211 +        else {
  1.1212 +            free(retval);
  1.1213 +            retval = NULL;
  1.1214 +        }
  1.1215 +    }
  1.1216 +
  1.1217 +    return retval;
  1.1218 +}
  1.1219 +
  1.1220 +/*
  1.1221 +** freeRun
  1.1222 +**
  1.1223 +** Free off the run and the associated data.
  1.1224 +*/
  1.1225 +void
  1.1226 +freeRun(STRun * aRun)
  1.1227 +{
  1.1228 +    if (NULL != aRun) {
  1.1229 +        if (NULL != aRun->mAllocations) {
  1.1230 +            /*
  1.1231 +             ** We do not free the allocations themselves.
  1.1232 +             ** They are likely pointed to by at least 2 other existing
  1.1233 +             **  runs.
  1.1234 +             */
  1.1235 +            free(aRun->mAllocations);
  1.1236 +            aRun->mAllocations = NULL;
  1.1237 +        }
  1.1238 +
  1.1239 +        if (NULL != aRun->mStats) {
  1.1240 +            free(aRun->mStats);
  1.1241 +            aRun->mStats = NULL;
  1.1242 +        }
  1.1243 +
  1.1244 +        free(aRun);
  1.1245 +        aRun = NULL;
  1.1246 +    }
  1.1247 +}
  1.1248 +
  1.1249 +/*
  1.1250 +** createRunFromGlobal
  1.1251 +**
  1.1252 +** Harvest the global run, then sort it.
  1.1253 +** Returns NULL on failure.
  1.1254 +** Must call freeRun() with the new STRun.
  1.1255 +*/
  1.1256 +STRun *
  1.1257 +createRunFromGlobal(STOptions * inOptions, STContext * inContext)
  1.1258 +{
  1.1259 +    STRun *retval = NULL;
  1.1260 +
  1.1261 +    if (NULL != inOptions && NULL != inContext) {
  1.1262 +        /*
  1.1263 +         ** We stamp the run.
  1.1264 +         ** As things are appended to it, it realizes that it should stamp the
  1.1265 +         **  callsite backtrace with the information as well.
  1.1266 +         ** In this manner, we can provide meaningful callsite data.
  1.1267 +         */
  1.1268 +        retval = createRun(inContext, PR_IntervalNow());
  1.1269 +
  1.1270 +        if (NULL != retval) {
  1.1271 +            STCategoryNode *node = NULL;
  1.1272 +            int failure = 0;
  1.1273 +            int harvestRes =
  1.1274 +                harvestRun(&globals.mRun, retval, inOptions, inContext);
  1.1275 +            if (0 == harvestRes) {
  1.1276 +                int sortRes = sortRun(inOptions, retval);
  1.1277 +
  1.1278 +                if (0 != sortRes) {
  1.1279 +                    failure = __LINE__;
  1.1280 +                }
  1.1281 +            }
  1.1282 +            else {
  1.1283 +                failure = __LINE__;
  1.1284 +            }
  1.1285 +
  1.1286 +
  1.1287 +            if (0 != failure) {
  1.1288 +                freeRun(retval);
  1.1289 +                retval = NULL;
  1.1290 +
  1.1291 +                REPORT_ERROR(failure, createRunFromGlobal);
  1.1292 +            }
  1.1293 +
  1.1294 +            /*
  1.1295 +             ** Categorize the run.
  1.1296 +             */
  1.1297 +            failure = categorizeRun(inOptions, inContext, retval, &globals);
  1.1298 +            if (0 != failure) {
  1.1299 +                REPORT_ERROR(__LINE__, categorizeRun);
  1.1300 +            }
  1.1301 +
  1.1302 +            /*
  1.1303 +             ** if we are focussing on a category, return that run instead of
  1.1304 +             ** the harvested run. Make sure to recalculate cost.
  1.1305 +             */
  1.1306 +            node = findCategoryNode(inOptions->mCategoryName, &globals);
  1.1307 +            if (node) {
  1.1308 +                /* Recalculate cost of run */
  1.1309 +                recalculateRunCost(inOptions, inContext,
  1.1310 +                                   node->runs[inContext->mIndex]);
  1.1311 +
  1.1312 +                retval = node->runs[inContext->mIndex];
  1.1313 +            }
  1.1314 +        }
  1.1315 +    }
  1.1316 +    else {
  1.1317 +        REPORT_ERROR(__LINE__, createRunFromGlobal);
  1.1318 +    }
  1.1319 +
  1.1320 +    return retval;
  1.1321 +}
  1.1322 +
  1.1323 +/*
  1.1324 +** getLiveAllocationByHeapID
  1.1325 +**
  1.1326 +** Go through a run and find the right heap ID.
  1.1327 +** At the time of the call to this function, the allocation must be LIVE,
  1.1328 +**   meaning that it can not be freed.
  1.1329 +** Go through the run backwards, in hopes of finding it near the end.
  1.1330 +**
  1.1331 +** Returns the allocation on success, otherwise NULL.
  1.1332 +*/
  1.1333 +STAllocation *
  1.1334 +getLiveAllocationByHeapID(STRun * aRun, uint32_t aHeapID)
  1.1335 +{
  1.1336 +    STAllocation *retval = NULL;
  1.1337 +
  1.1338 +    if (NULL != aRun && 0 != aHeapID) {
  1.1339 +        uint32_t traverse = aRun->mAllocationCount;
  1.1340 +        STAllocation *eval = NULL;
  1.1341 +
  1.1342 +        /*
  1.1343 +         ** Go through in reverse order.
  1.1344 +         ** Stop when we have a return value.
  1.1345 +         */
  1.1346 +        while (0 < traverse && NULL == retval) {
  1.1347 +            /*
  1.1348 +             ** Back up one to align with zero based index.
  1.1349 +             */
  1.1350 +            traverse--;
  1.1351 +
  1.1352 +            /*
  1.1353 +             ** Take the pointer math out of further operations.
  1.1354 +             */
  1.1355 +            eval = aRun->mAllocations[traverse];
  1.1356 +
  1.1357 +            /*
  1.1358 +             ** Take a look at the events in reverse order.
  1.1359 +             ** Basically the last event must NOT be a free.
  1.1360 +             ** The last event must NOT be a realloc of size zero (free).
  1.1361 +             ** Otherwise, try to match up the heapID of the event.
  1.1362 +             */
  1.1363 +            if (0 != eval->mEventCount) {
  1.1364 +                STAllocEvent *event = eval->mEvents + (eval->mEventCount - 1);
  1.1365 +
  1.1366 +                switch (event->mEventType) {
  1.1367 +                case TM_EVENT_FREE:
  1.1368 +                    {
  1.1369 +                        /*
  1.1370 +                         ** No freed allocation can match.
  1.1371 +                         */
  1.1372 +                    }
  1.1373 +                    break;
  1.1374 +
  1.1375 +                case TM_EVENT_REALLOC:
  1.1376 +                case TM_EVENT_CALLOC:
  1.1377 +                case TM_EVENT_MALLOC:
  1.1378 +                    {
  1.1379 +                        /*
  1.1380 +                         ** Heap IDs must match.
  1.1381 +                         */
  1.1382 +                        if (aHeapID == event->mHeapID) {
  1.1383 +                            retval = eval;
  1.1384 +                        }
  1.1385 +                    }
  1.1386 +                    break;
  1.1387 +
  1.1388 +                default:
  1.1389 +                    {
  1.1390 +                        REPORT_ERROR(__LINE__, getAllocationByHeapID);
  1.1391 +                    }
  1.1392 +                    break;
  1.1393 +                }
  1.1394 +            }
  1.1395 +        }
  1.1396 +    }
  1.1397 +    else {
  1.1398 +        REPORT_ERROR(__LINE__, getAllocationByHeapID);
  1.1399 +    }
  1.1400 +
  1.1401 +    return retval;
  1.1402 +}
  1.1403 +
  1.1404 +/*
  1.1405 +** appendEvent
  1.1406 +**
  1.1407 +** Given an allocation, append a new event to its lifetime.
  1.1408 +** Returns the new event on success, otherwise NULL.
  1.1409 +*/
  1.1410 +STAllocEvent *
  1.1411 +appendEvent(STAllocation * aAllocation, uint32_t aTimeval, char aEventType,
  1.1412 +            uint32_t aHeapID, uint32_t aHeapSize, tmcallsite * aCallsite)
  1.1413 +{
  1.1414 +    STAllocEvent *retval = NULL;
  1.1415 +
  1.1416 +    if (NULL != aAllocation && NULL != aCallsite) {
  1.1417 +        STAllocEvent *expand = NULL;
  1.1418 +
  1.1419 +        /*
  1.1420 +         ** Expand the allocation's event array.
  1.1421 +         */
  1.1422 +        expand =
  1.1423 +            (STAllocEvent *) realloc(aAllocation->mEvents,
  1.1424 +                                     sizeof(STAllocEvent) *
  1.1425 +                                     (aAllocation->mEventCount + 1));
  1.1426 +        if (NULL != expand) {
  1.1427 +            /*
  1.1428 +             ** Reassign in case of pointer move.
  1.1429 +             */
  1.1430 +            aAllocation->mEvents = expand;
  1.1431 +
  1.1432 +            /*
  1.1433 +             ** Remove the pointer math from rest of code.
  1.1434 +             */
  1.1435 +            retval = aAllocation->mEvents + aAllocation->mEventCount;
  1.1436 +
  1.1437 +            /*
  1.1438 +             ** Increase event array count.
  1.1439 +             */
  1.1440 +            aAllocation->mEventCount++;
  1.1441 +
  1.1442 +            /*
  1.1443 +             ** Fill in the event.
  1.1444 +             */
  1.1445 +            retval->mTimeval = aTimeval;
  1.1446 +            retval->mEventType = aEventType;
  1.1447 +            retval->mHeapID = aHeapID;
  1.1448 +            retval->mHeapSize = aHeapSize;
  1.1449 +            retval->mCallsite = aCallsite;
  1.1450 +
  1.1451 +            /*
  1.1452 +             ** Allocation may need to update idea of lifetime.
  1.1453 +             ** See allocationTracker to see mMinTimeval inited to ST_TIMEVAL_MAX.
  1.1454 +             */
  1.1455 +            if (aAllocation->mMinTimeval > aTimeval) {
  1.1456 +                aAllocation->mMinTimeval = aTimeval;
  1.1457 +            }
  1.1458 +
  1.1459 +            /*
  1.1460 +             ** This a free event?
  1.1461 +             ** Can only set max timeval on a free.
  1.1462 +             ** Otherwise, mMaxTimeval remains  ST_TIMEVAL_MAX.
  1.1463 +             ** Set in allocationTracker.
  1.1464 +             */
  1.1465 +            if (TM_EVENT_FREE == aEventType) {
  1.1466 +                aAllocation->mMaxTimeval = aTimeval;
  1.1467 +            }
  1.1468 +        }
  1.1469 +        else {
  1.1470 +            REPORT_ERROR(__LINE__, appendEvent);
  1.1471 +        }
  1.1472 +    }
  1.1473 +    else {
  1.1474 +        REPORT_ERROR(__LINE__, appendEvent);
  1.1475 +    }
  1.1476 +
  1.1477 +    return retval;
  1.1478 +}
  1.1479 +
  1.1480 +/*
  1.1481 +** hasAllocation
  1.1482 +**
  1.1483 +** Determine if a given run has an allocation.
  1.1484 +** This is really nothing more than a pointer comparison loop.
  1.1485 +** Returns !0 if the run has the allocation.
  1.1486 +*/
  1.1487 +int
  1.1488 +hasAllocation(STRun * aRun, STAllocation * aTestFor)
  1.1489 +{
  1.1490 +    int retval = 0;
  1.1491 +
  1.1492 +    if (NULL != aRun && NULL != aTestFor) {
  1.1493 +        uint32_t traverse = aRun->mAllocationCount;
  1.1494 +
  1.1495 +        /*
  1.1496 +         ** Go through reverse, in the hopes it exists nearer the end.
  1.1497 +         */
  1.1498 +        while (0 < traverse) {
  1.1499 +            /*
  1.1500 +             ** Back up.
  1.1501 +             */
  1.1502 +            traverse--;
  1.1503 +
  1.1504 +            if (aTestFor == aRun->mAllocations[traverse]) {
  1.1505 +                retval = __LINE__;
  1.1506 +                break;
  1.1507 +            }
  1.1508 +        }
  1.1509 +    }
  1.1510 +    else {
  1.1511 +        REPORT_ERROR(__LINE__, hasAllocation);
  1.1512 +    }
  1.1513 +
  1.1514 +    return retval;
  1.1515 +}
  1.1516 +
  1.1517 +/*
  1.1518 +** allocationTracker
  1.1519 +**
  1.1520 +** Important to keep track of all allocations unique so as to determine
  1.1521 +**  their lifetimes.
  1.1522 +**
  1.1523 +** Returns a pointer to the allocation on success.
  1.1524 +** Return NULL on failure.
  1.1525 +*/
  1.1526 +STAllocation *
  1.1527 +allocationTracker(uint32_t aTimeval, char aType, uint32_t aHeapRuntimeCost,
  1.1528 +                  tmcallsite * aCallsite, uint32_t aHeapID, uint32_t aSize,
  1.1529 +                  tmcallsite * aOldCallsite, uint32_t aOldHeapID,
  1.1530 +                  uint32_t aOldSize)
  1.1531 +{
  1.1532 +    STAllocation *retval = NULL;
  1.1533 +    static int compactor = 1;
  1.1534 +    const int frequency = 10000;
  1.1535 +    uint32_t actualSize, actualOldSize = 0;
  1.1536 +
  1.1537 +    actualSize = actualByteSize(&globals.mCommandLineOptions, aSize);
  1.1538 +    if (aOldSize)
  1.1539 +        actualOldSize =
  1.1540 +            actualByteSize(&globals.mCommandLineOptions, aOldSize);
  1.1541 +
  1.1542 +    if (NULL != aCallsite) {
  1.1543 +        int newAllocation = 0;
  1.1544 +        tmcallsite *searchCallsite = NULL;
  1.1545 +        uint32_t searchHeapID = 0;
  1.1546 +        STAllocation *allocation = NULL;
  1.1547 +
  1.1548 +        /*
  1.1549 +         ** Global operation ID increases.
  1.1550 +         */
  1.1551 +        globals.mOperationCount++;
  1.1552 +
  1.1553 +        /*
  1.1554 +         ** Fix up the timevals if needed.
  1.1555 +         */
  1.1556 +        if (aTimeval < globals.mMinTimeval) {
  1.1557 +            globals.mMinTimeval = aTimeval;
  1.1558 +        }
  1.1559 +        if (aTimeval > globals.mMaxTimeval) {
  1.1560 +            globals.mMaxTimeval = aTimeval;
  1.1561 +        }
  1.1562 +
  1.1563 +        switch (aType) {
  1.1564 +        case TM_EVENT_FREE:
  1.1565 +            {
  1.1566 +                /*
  1.1567 +                 ** Update the global counter.
  1.1568 +                 */
  1.1569 +                globals.mFreeCount++;
  1.1570 +
  1.1571 +                /*
  1.1572 +                 ** Update our peak memory used counter
  1.1573 +                 */
  1.1574 +                globals.mMemoryUsed -= actualSize;
  1.1575 +
  1.1576 +                /*
  1.1577 +                 ** Not a new allocation, will need to search passed in site
  1.1578 +                 **  for the original allocation.
  1.1579 +                 */
  1.1580 +                searchCallsite = aCallsite;
  1.1581 +                searchHeapID = aHeapID;
  1.1582 +            }
  1.1583 +            break;
  1.1584 +
  1.1585 +        case TM_EVENT_MALLOC:
  1.1586 +            {
  1.1587 +                /*
  1.1588 +                 ** Update the global counter.
  1.1589 +                 */
  1.1590 +                globals.mMallocCount++;
  1.1591 +
  1.1592 +                /*
  1.1593 +                 ** Update our peak memory used counter
  1.1594 +                 */
  1.1595 +                globals.mMemoryUsed += actualSize;
  1.1596 +                if (globals.mMemoryUsed > globals.mPeakMemoryUsed) {
  1.1597 +                    globals.mPeakMemoryUsed = globals.mMemoryUsed;
  1.1598 +                }
  1.1599 +
  1.1600 +                /*
  1.1601 +                 ** This will be a new allocation.
  1.1602 +                 */
  1.1603 +                newAllocation = __LINE__;
  1.1604 +            }
  1.1605 +            break;
  1.1606 +
  1.1607 +        case TM_EVENT_CALLOC:
  1.1608 +            {
  1.1609 +                /*
  1.1610 +                 ** Update the global counter.
  1.1611 +                 */
  1.1612 +                globals.mCallocCount++;
  1.1613 +
  1.1614 +                /*
  1.1615 +                 ** Update our peak memory used counter
  1.1616 +                 */
  1.1617 +                globals.mMemoryUsed += actualSize;
  1.1618 +                if (globals.mMemoryUsed > globals.mPeakMemoryUsed) {
  1.1619 +                    globals.mPeakMemoryUsed = globals.mMemoryUsed;
  1.1620 +                }
  1.1621 +
  1.1622 +                /*
  1.1623 +                 ** This will be a new allocation.
  1.1624 +                 */
  1.1625 +                newAllocation = __LINE__;
  1.1626 +            }
  1.1627 +            break;
  1.1628 +
  1.1629 +        case TM_EVENT_REALLOC:
  1.1630 +            {
  1.1631 +                /*
  1.1632 +                 ** Update the global counter.
  1.1633 +                 */
  1.1634 +                globals.mReallocCount++;
  1.1635 +
  1.1636 +                /*
  1.1637 +                 ** Update our peak memory used counter
  1.1638 +                 */
  1.1639 +                globals.mMemoryUsed += actualSize - actualOldSize;
  1.1640 +                if (globals.mMemoryUsed > globals.mPeakMemoryUsed) {
  1.1641 +                    globals.mPeakMemoryUsed = globals.mMemoryUsed;
  1.1642 +                }
  1.1643 +
  1.1644 +                /*
  1.1645 +                 ** This might be a new allocation.
  1.1646 +                 */
  1.1647 +                if (NULL == aOldCallsite) {
  1.1648 +                    newAllocation = __LINE__;
  1.1649 +                }
  1.1650 +                else {
  1.1651 +                    /*
  1.1652 +                     ** Need to search for the original callsite for the
  1.1653 +                     **  index to the allocation.
  1.1654 +                     */
  1.1655 +                    searchCallsite = aOldCallsite;
  1.1656 +                    searchHeapID = aOldHeapID;
  1.1657 +                }
  1.1658 +            }
  1.1659 +            break;
  1.1660 +
  1.1661 +        default:
  1.1662 +            {
  1.1663 +                REPORT_ERROR(__LINE__, allocationTracker);
  1.1664 +            }
  1.1665 +            break;
  1.1666 +        }
  1.1667 +
  1.1668 +        /*
  1.1669 +         ** We are either modifying an existing allocation or we are creating
  1.1670 +         **  a new one.
  1.1671 +         */
  1.1672 +        if (0 != newAllocation) {
  1.1673 +            allocation = (STAllocation *) calloc(1, sizeof(STAllocation));
  1.1674 +            if (NULL != allocation) {
  1.1675 +                /*
  1.1676 +                 ** Fixup the min timeval so if logic later will just work.
  1.1677 +                 */
  1.1678 +                allocation->mMinTimeval = ST_TIMEVAL_MAX;
  1.1679 +                allocation->mMaxTimeval = ST_TIMEVAL_MAX;
  1.1680 +            }
  1.1681 +        }
  1.1682 +        else if (NULL != searchCallsite
  1.1683 +                 && NULL != CALLSITE_RUN(searchCallsite)
  1.1684 +                 && 0 != searchHeapID) {
  1.1685 +            /*
  1.1686 +             ** We know what to search for, and we reduce what we search
  1.1687 +             **  by only looking for those allocations at a known callsite.
  1.1688 +             */
  1.1689 +            allocation =
  1.1690 +                getLiveAllocationByHeapID(CALLSITE_RUN(searchCallsite),
  1.1691 +                                          searchHeapID);
  1.1692 +        }
  1.1693 +        else {
  1.1694 +            REPORT_ERROR(__LINE__, allocationTracker);
  1.1695 +        }
  1.1696 +
  1.1697 +        if (NULL != allocation) {
  1.1698 +            STAllocEvent *appendResult = NULL;
  1.1699 +
  1.1700 +            /*
  1.1701 +             ** Record the amount of time this allocation event took.
  1.1702 +             */
  1.1703 +            allocation->mHeapRuntimeCost += aHeapRuntimeCost;
  1.1704 +
  1.1705 +            /*
  1.1706 +             ** Now that we have an allocation, we need to make sure it has
  1.1707 +             **  the proper event.
  1.1708 +             */
  1.1709 +            appendResult =
  1.1710 +                appendEvent(allocation, aTimeval, aType, aHeapID, aSize,
  1.1711 +                            aCallsite);
  1.1712 +            if (NULL != appendResult) {
  1.1713 +                if (0 != newAllocation) {
  1.1714 +                    int runAppendResult = 0;
  1.1715 +                    int callsiteAppendResult = 0;
  1.1716 +
  1.1717 +                    /*
  1.1718 +                     ** A new allocation needs to be added to the global run.
  1.1719 +                     ** A new allocation needs to be added to the callsite.
  1.1720 +                     */
  1.1721 +                    runAppendResult =
  1.1722 +                        appendAllocation(&globals.mCommandLineOptions, NULL,
  1.1723 +                                         &globals.mRun, allocation);
  1.1724 +                    callsiteAppendResult =
  1.1725 +                        appendAllocation(&globals.mCommandLineOptions, NULL,
  1.1726 +                                         CALLSITE_RUN(aCallsite), allocation);
  1.1727 +                    if (0 != runAppendResult && 0 != callsiteAppendResult) {
  1.1728 +                        /*
  1.1729 +                         ** Success.
  1.1730 +                         */
  1.1731 +                        retval = allocation;
  1.1732 +                    }
  1.1733 +                    else {
  1.1734 +                        REPORT_ERROR(__LINE__, appendAllocation);
  1.1735 +                    }
  1.1736 +                }
  1.1737 +                else {
  1.1738 +                    /*
  1.1739 +                     ** An existing allocation, if a realloc situation,
  1.1740 +                     **  may need to be added to the new callsite.
  1.1741 +                     ** This can only occur if the new and old callsites
  1.1742 +                     **  differ.
  1.1743 +                     ** Even then, a brute force check will need to be made
  1.1744 +                     **  to ensure the allocation was not added twice; 
  1.1745 +                     **  consider a realloc scenario where two different
  1.1746 +                     **  call stacks bump the allocation back and forth.
  1.1747 +                     */
  1.1748 +                    if (aCallsite != searchCallsite) {
  1.1749 +                        int found = 0;
  1.1750 +
  1.1751 +                        found =
  1.1752 +                            hasAllocation(CALLSITE_RUN(aCallsite),
  1.1753 +                                          allocation);
  1.1754 +                        if (0 == found) {
  1.1755 +                            int appendResult = 0;
  1.1756 +
  1.1757 +                            appendResult =
  1.1758 +                                appendAllocation(&globals.mCommandLineOptions,
  1.1759 +                                                 NULL,
  1.1760 +                                                 CALLSITE_RUN(aCallsite),
  1.1761 +                                                 allocation);
  1.1762 +                            if (0 != appendResult) {
  1.1763 +                                /*
  1.1764 +                                 ** Success.
  1.1765 +                                 */
  1.1766 +                                retval = allocation;
  1.1767 +                            }
  1.1768 +                            else {
  1.1769 +                                REPORT_ERROR(__LINE__, appendAllocation);
  1.1770 +                            }
  1.1771 +                        }
  1.1772 +                        else {
  1.1773 +                            /*
  1.1774 +                             ** Already there.
  1.1775 +                             */
  1.1776 +                            retval = allocation;
  1.1777 +                        }
  1.1778 +                    }
  1.1779 +                    else {
  1.1780 +                        /*
  1.1781 +                         ** Success.
  1.1782 +                         */
  1.1783 +                        retval = allocation;
  1.1784 +                    }
  1.1785 +                }
  1.1786 +            }
  1.1787 +            else {
  1.1788 +                REPORT_ERROR(__LINE__, appendEvent);
  1.1789 +            }
  1.1790 +        }
  1.1791 +        else {
  1.1792 +            REPORT_ERROR(__LINE__, allocationTracker);
  1.1793 +        }
  1.1794 +    }
  1.1795 +    else {
  1.1796 +        REPORT_ERROR(__LINE__, allocationTracker);
  1.1797 +    }
  1.1798 +
  1.1799 +    /*
  1.1800 +     ** Compact the heap a bit if you can.
  1.1801 +     */
  1.1802 +    compactor++;
  1.1803 +    if (0 == (compactor % frequency)) {
  1.1804 +        heapCompact();
  1.1805 +    }
  1.1806 +
  1.1807 +    return retval;
  1.1808 +}
  1.1809 +
  1.1810 +/*
  1.1811 +** trackEvent
  1.1812 +**
  1.1813 +** An allocation event has dropped in on us.
  1.1814 +** We need to do the right thing and track it.
  1.1815 +*/
  1.1816 +void
  1.1817 +trackEvent(uint32_t aTimeval, char aType, uint32_t aHeapRuntimeCost,
  1.1818 +           tmcallsite * aCallsite, uint32_t aHeapID, uint32_t aSize,
  1.1819 +           tmcallsite * aOldCallsite, uint32_t aOldHeapID, uint32_t aOldSize)
  1.1820 +{
  1.1821 +    if (NULL != aCallsite) {
  1.1822 +        /*
  1.1823 +         ** Verify the old callsite just in case.
  1.1824 +         */
  1.1825 +        if (NULL != CALLSITE_RUN(aCallsite)
  1.1826 +            && (NULL == aOldCallsite || NULL != CALLSITE_RUN(aOldCallsite))) {
  1.1827 +            STAllocation *allocation = NULL;
  1.1828 +
  1.1829 +            /*
  1.1830 +             ** Add to the allocation tracking code.
  1.1831 +             */
  1.1832 +            allocation =
  1.1833 +                allocationTracker(aTimeval, aType, aHeapRuntimeCost,
  1.1834 +                                  aCallsite, aHeapID, aSize, aOldCallsite,
  1.1835 +                                  aOldHeapID, aOldSize);
  1.1836 +
  1.1837 +            if (NULL == allocation) {
  1.1838 +                REPORT_ERROR(__LINE__, allocationTracker);
  1.1839 +            }
  1.1840 +        }
  1.1841 +        else {
  1.1842 +            REPORT_ERROR(__LINE__, trackEvent);
  1.1843 +        }
  1.1844 +    }
  1.1845 +    else {
  1.1846 +        REPORT_ERROR(__LINE__, trackEvent);
  1.1847 +    }
  1.1848 +}
  1.1849 +
  1.1850 +/*
  1.1851 +** tmEventHandler
  1.1852 +**
  1.1853 +** Callback from the tmreader_eventloop function.
  1.1854 +** Simply tries to sort out what we desire to know.
  1.1855 +*/
  1.1856 +
  1.1857 +static const char spinner_chars[] = { '/', '-', '\\', '|' };
  1.1858 +
  1.1859 +#define SPINNER_UPDATE_FREQUENCY 4096
  1.1860 +#define SPINNER_CHAR_COUNT (sizeof(spinner_chars) / sizeof(spinner_chars[0]))
  1.1861 +#define SPINNER_CHAR(_x) spinner_chars[(_x / SPINNER_UPDATE_FREQUENCY) % SPINNER_CHAR_COUNT]
  1.1862 +
  1.1863 +void
  1.1864 +tmEventHandler(tmreader * aReader, tmevent * aEvent)
  1.1865 +{
  1.1866 +    static int event_count = 0;     /* for spinner */
  1.1867 +    if ((event_count++ % SPINNER_UPDATE_FREQUENCY) == 0)
  1.1868 +        printf("\rReading... %c", SPINNER_CHAR(event_count));
  1.1869 +    
  1.1870 +    if (NULL != aReader && NULL != aEvent) {
  1.1871 +        switch (aEvent->type) {
  1.1872 +            /*
  1.1873 +             ** Events we ignore.
  1.1874 +             */
  1.1875 +        case TM_EVENT_LIBRARY:
  1.1876 +        case TM_EVENT_METHOD:
  1.1877 +        case TM_EVENT_STATS:
  1.1878 +        case TM_EVENT_TIMESTAMP:
  1.1879 +        case TM_EVENT_FILENAME:
  1.1880 +            break;
  1.1881 +
  1.1882 +            /*
  1.1883 +             ** Allocation events need to be tracked.
  1.1884 +             */
  1.1885 +        case TM_EVENT_MALLOC:
  1.1886 +        case TM_EVENT_CALLOC:
  1.1887 +        case TM_EVENT_REALLOC:
  1.1888 +        case TM_EVENT_FREE:
  1.1889 +            {
  1.1890 +                uint32_t oldptr = 0;
  1.1891 +                uint32_t oldsize = 0;
  1.1892 +                tmcallsite *callsite = NULL;
  1.1893 +                tmcallsite *oldcallsite = NULL;
  1.1894 +
  1.1895 +                if (TM_EVENT_REALLOC == aEvent->type) {
  1.1896 +                    /*
  1.1897 +                     ** Only care about old arguments if there were any.
  1.1898 +                     */
  1.1899 +                    if (0 != aEvent->u.alloc.oldserial) {
  1.1900 +                        oldptr = aEvent->u.alloc.oldptr;
  1.1901 +                        oldsize = aEvent->u.alloc.oldsize;
  1.1902 +                        oldcallsite =
  1.1903 +                            tmreader_callsite(aReader,
  1.1904 +                                              aEvent->u.alloc.oldserial);
  1.1905 +                        if (NULL == oldcallsite) {
  1.1906 +                            REPORT_ERROR(__LINE__, tmreader_callsite);
  1.1907 +                        }
  1.1908 +                    }
  1.1909 +                }
  1.1910 +
  1.1911 +                callsite = tmreader_callsite(aReader, aEvent->serial);
  1.1912 +                if (NULL != callsite) {
  1.1913 +                    /*
  1.1914 +                     ** Verify a callsite run is there.
  1.1915 +                     ** If not, we are ignoring this callsite.
  1.1916 +                     */
  1.1917 +                    if (NULL != CALLSITE_RUN(callsite)) {
  1.1918 +                        char eventType = aEvent->type;
  1.1919 +                        uint32_t eventSize = aEvent->u.alloc.size;
  1.1920 +
  1.1921 +                        /*
  1.1922 +                         ** Play a nasty trick on reallocs of size zero.
  1.1923 +                         ** They are to become free events, adjust the size accordingly.
  1.1924 +                         ** This allows me to avoid all types of special case code.
  1.1925 +                         */
  1.1926 +                        if (0 == aEvent->u.alloc.size
  1.1927 +                            && TM_EVENT_REALLOC == aEvent->type) {
  1.1928 +                            eventType = TM_EVENT_FREE;
  1.1929 +                            if (0 != aEvent->u.alloc.oldserial) {
  1.1930 +                                eventSize = aEvent->u.alloc.oldsize;
  1.1931 +                            }
  1.1932 +                        }
  1.1933 +                        trackEvent(ticks2msec
  1.1934 +                                   (aReader, aEvent->u.alloc.interval),
  1.1935 +                                   eventType, ticks2usec(aReader,
  1.1936 +                                                         aEvent->u.alloc.
  1.1937 +                                                         cost), callsite,
  1.1938 +                                   aEvent->u.alloc.ptr, eventSize,
  1.1939 +                                   oldcallsite, oldptr, oldsize);
  1.1940 +                    }
  1.1941 +                }
  1.1942 +                else {
  1.1943 +                    REPORT_ERROR(__LINE__, tmreader_callsite);
  1.1944 +                }
  1.1945 +            }
  1.1946 +            break;
  1.1947 +
  1.1948 +            /*
  1.1949 +             ** Callsite, set up the callsite run if it does not exist.
  1.1950 +             */
  1.1951 +        case TM_EVENT_CALLSITE:
  1.1952 +            {
  1.1953 +                tmcallsite *callsite =
  1.1954 +                    tmreader_callsite(aReader, aEvent->serial);
  1.1955 +
  1.1956 +                if (NULL != callsite) {
  1.1957 +                    if (NULL == CALLSITE_RUN(callsite)) {
  1.1958 +                        int createrun = __LINE__;
  1.1959 +
  1.1960 +#if defined(MOZILLA_CLIENT)
  1.1961 +                        /*
  1.1962 +                         ** For a mozilla spacetrace, ignore this particular
  1.1963 +                         **  callsite as it is just noise, and causes us to
  1.1964 +                         **  use a lot of memory.
  1.1965 +                         **
  1.1966 +                         ** This callsite is present on the linux build,
  1.1967 +                         **  not sure if the other platforms have it.
  1.1968 +                         */
  1.1969 +                        if (0 !=
  1.1970 +                            hasCallsiteMatch(callsite, "g_main_is_running",
  1.1971 +                                             ST_FOLLOW_PARENTS)) {
  1.1972 +                            createrun = 0;
  1.1973 +                        }
  1.1974 +#endif /* MOZILLA_CLIENT */
  1.1975 +
  1.1976 +                        if (0 != createrun) {
  1.1977 +                            callsite->data = createRun(NULL, 0);
  1.1978 +                        }
  1.1979 +                    }
  1.1980 +                }
  1.1981 +                else {
  1.1982 +                    REPORT_ERROR(__LINE__, tmreader_callsite);
  1.1983 +                }
  1.1984 +            }
  1.1985 +            break;
  1.1986 +
  1.1987 +            /*
  1.1988 +             ** Unhandled events should not be allowed.
  1.1989 +             */
  1.1990 +        default:
  1.1991 +            {
  1.1992 +                REPORT_ERROR(__LINE__, tmEventHandler);
  1.1993 +            }
  1.1994 +            break;
  1.1995 +        }
  1.1996 +    }
  1.1997 +}
  1.1998 +
  1.1999 +/*
  1.2000 +**  optionGetDataOut
  1.2001 +**
  1.2002 +**  Output option get data.
  1.2003 +*/
  1.2004 +void
  1.2005 +optionGetDataOut(PRFileDesc * inFD, STOptions * inOptions)
  1.2006 +{
  1.2007 +    if (NULL != inFD && NULL != inOptions) {
  1.2008 +        int mark = 0;
  1.2009 +
  1.2010 +#define ST_WEB_OPTION_BOOL(option_name, option_genre, option_help) \
  1.2011 +    PR_fprintf(inFD, "%s%s=%d", (0 == mark++) ? "?" : "&", #option_name, inOptions->m##option_name);
  1.2012 +#define ST_WEB_OPTION_STRING(option_name, option_genre, default_value, option_help) \
  1.2013 +    PR_fprintf(inFD, "%s%s=%s", (0 == mark++) ? "?" : "&", #option_name, inOptions->m##option_name);
  1.2014 +#define ST_WEB_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
  1.2015 +    { \
  1.2016 +        uint32_t loop = 0; \
  1.2017 +        \
  1.2018 +        for(loop = 0; loop < array_size; loop++) \
  1.2019 +        { \
  1.2020 +            PR_fprintf(inFD, "%s%s=%s", (0 == mark++) ? "?" : "&", #option_name, inOptions->m##option_name[loop]); \
  1.2021 +        } \
  1.2022 +    }
  1.2023 +#define ST_WEB_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help)  /* no implementation */
  1.2024 +#define ST_WEB_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
  1.2025 +    PR_fprintf(inFD, "%s%s=%u", (0 == mark++) ? "?" : "&", #option_name, inOptions->m##option_name / multiplier);
  1.2026 +#define ST_WEB_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
  1.2027 +    { \
  1.2028 +        uint64_t def64 = default_value; \
  1.2029 +        uint64_t mul64 = multiplier; \
  1.2030 +        uint64_t div64; \
  1.2031 +        \
  1.2032 +        div64 = inOptions->m##option_name##64 / mul64; \
  1.2033 +        PR_fprintf(inFD, "%s%s=%llu", (0 == mark++) ? "?" : "&", #option_name, div64); \
  1.2034 +    }
  1.2035 +
  1.2036 +#include "stoptions.h"
  1.2037 +    }
  1.2038 +}
  1.2039 +
  1.2040 +/*
  1.2041 +** htmlAnchor
  1.2042 +**
  1.2043 +** Output an HTML anchor, or just the text depending on the mode.
  1.2044 +*/
  1.2045 +void
  1.2046 +htmlAnchor(STRequest * inRequest,
  1.2047 +           const char *aHref,
  1.2048 +           const char *aText,
  1.2049 +           const char *aTarget, const char *aClass, STOptions * inOptions)
  1.2050 +{
  1.2051 +    if (NULL != aHref && '\0' != *aHref && NULL != aText && '\0' != *aText) {
  1.2052 +        int anchorLive = 1;
  1.2053 +
  1.2054 +        /*
  1.2055 +         ** In batch mode, we need to verify the anchor is live.
  1.2056 +         */
  1.2057 +        if (0 != inRequest->mOptions.mBatchRequestCount) {
  1.2058 +            uint32_t loop = 0;
  1.2059 +            int comparison = 1;
  1.2060 +
  1.2061 +            for (loop = 0; loop < inRequest->mOptions.mBatchRequestCount;
  1.2062 +                 loop++) {
  1.2063 +                comparison =
  1.2064 +                    strcmp(aHref, inRequest->mOptions.mBatchRequest[loop]);
  1.2065 +                if (0 == comparison) {
  1.2066 +                    break;
  1.2067 +                }
  1.2068 +            }
  1.2069 +
  1.2070 +            /*
  1.2071 +             ** Did we find it?
  1.2072 +             */
  1.2073 +            if (0 == comparison) {
  1.2074 +                anchorLive = 0;
  1.2075 +            }
  1.2076 +        }
  1.2077 +
  1.2078 +        /*
  1.2079 +         ** In any mode, don't make an href to the current page.
  1.2080 +         */
  1.2081 +        if (0 != anchorLive && NULL != inRequest->mGetFileName) {
  1.2082 +            if (0 == strcmp(aHref, inRequest->mGetFileName)) {
  1.2083 +                anchorLive = 0;
  1.2084 +            }
  1.2085 +        }
  1.2086 +
  1.2087 +        /*
  1.2088 +         **  Do the right thing.
  1.2089 +         */
  1.2090 +        if (0 != anchorLive) {
  1.2091 +            PR_fprintf(inRequest->mFD, "<a class=\"%s\" ", aClass);
  1.2092 +            if (NULL != aTarget && '\0' != *aTarget) {
  1.2093 +                PR_fprintf(inRequest->mFD, "target=\"%s\" ", aTarget);
  1.2094 +            }
  1.2095 +            PR_fprintf(inRequest->mFD, "href=\"./%s", aHref);
  1.2096 +
  1.2097 +            /*
  1.2098 +             **  The options, if desired, get appended as form data.
  1.2099 +             */
  1.2100 +            optionGetDataOut(inRequest->mFD, inOptions);
  1.2101 +
  1.2102 +            PR_fprintf(inRequest->mFD, "\">%s</a>\n", aText);
  1.2103 +        }
  1.2104 +        else {
  1.2105 +            PR_fprintf(inRequest->mFD, "<span class=\"%s\">%s</span>\n",
  1.2106 +                       aClass, aText);
  1.2107 +        }
  1.2108 +    }
  1.2109 +    else {
  1.2110 +        REPORT_ERROR(__LINE__, htmlAnchor);
  1.2111 +    }
  1.2112 +}
  1.2113 +
  1.2114 +/*
  1.2115 +** htmlAllocationAnchor
  1.2116 +**
  1.2117 +** Output an html achor that will resolve to the allocation in question.
  1.2118 +*/
  1.2119 +void
  1.2120 +htmlAllocationAnchor(STRequest * inRequest, STAllocation * aAllocation,
  1.2121 +                     const char *aText)
  1.2122 +{
  1.2123 +    if (NULL != aAllocation && NULL != aText && '\0' != *aText) {
  1.2124 +        char buffer[128];
  1.2125 +
  1.2126 +        /*
  1.2127 +         ** This is a total hack.
  1.2128 +         ** The filename contains the index of the allocation in globals.mRun.
  1.2129 +         ** Safer than using the raw pointer value....
  1.2130 +         */
  1.2131 +        PR_snprintf(buffer, sizeof(buffer), "allocation_%u.html",
  1.2132 +                    aAllocation->mRunIndex);
  1.2133 +
  1.2134 +        htmlAnchor(inRequest, buffer, aText, NULL, "allocation",
  1.2135 +                   &inRequest->mOptions);
  1.2136 +    }
  1.2137 +    else {
  1.2138 +        REPORT_ERROR(__LINE__, htmlAllocationAnchor);
  1.2139 +    }
  1.2140 +}
  1.2141 +
  1.2142 +/*
  1.2143 +** resolveSourceFile
  1.2144 +**
  1.2145 +** Easy way to get a readable/short name.
  1.2146 +** NULL if not present, not resolvable.
  1.2147 +*/
  1.2148 +const char *
  1.2149 +resolveSourceFile(tmmethodnode * aMethod)
  1.2150 +{
  1.2151 +    const char *retval = NULL;
  1.2152 +
  1.2153 +    if (NULL != aMethod) {
  1.2154 +        const char *methodSays = NULL;
  1.2155 +
  1.2156 +        methodSays = aMethod->sourcefile;
  1.2157 +
  1.2158 +        if (NULL != methodSays && '\0' != methodSays[0]
  1.2159 +            && 0 != strcmp("noname", methodSays)) {
  1.2160 +            retval = strrchr(methodSays, '/');
  1.2161 +            if (NULL != retval) {
  1.2162 +                retval++;
  1.2163 +            }
  1.2164 +            else {
  1.2165 +                retval = methodSays;
  1.2166 +            }
  1.2167 +        }
  1.2168 +    }
  1.2169 +
  1.2170 +    return retval;
  1.2171 +}
  1.2172 +
  1.2173 +/*
  1.2174 +** htmlCallsiteAnchor
  1.2175 +**
  1.2176 +** Output an html anchor that will resolve to the callsite in question.
  1.2177 +** If no text is provided, we provide our own.
  1.2178 +**
  1.2179 +** RealName determines whether or not we crawl our parents until the point
  1.2180 +**  we no longer match stats.
  1.2181 +*/
  1.2182 +void
  1.2183 +htmlCallsiteAnchor(STRequest * inRequest, tmcallsite * aCallsite,
  1.2184 +                   const char *aText, int aRealName)
  1.2185 +{
  1.2186 +    if (NULL != aCallsite) {
  1.2187 +        char textBuf[512];
  1.2188 +        char hrefBuf[128];
  1.2189 +        tmcallsite *namesite = aCallsite;
  1.2190 +
  1.2191 +        /*
  1.2192 +         ** Should we use a different name?
  1.2193 +         */
  1.2194 +        if (0 == aRealName && NULL != namesite->parent
  1.2195 +            && NULL != namesite->parent->method) {
  1.2196 +            STRun *myRun = NULL;
  1.2197 +            STRun *upRun = NULL;
  1.2198 +
  1.2199 +            do {
  1.2200 +                myRun = CALLSITE_RUN(namesite);
  1.2201 +                upRun = CALLSITE_RUN(namesite->parent);
  1.2202 +
  1.2203 +                if (0 !=
  1.2204 +                    memcmp(&myRun->mStats[inRequest->mContext->mIndex],
  1.2205 +                           &upRun->mStats[inRequest->mContext->mIndex],
  1.2206 +                           sizeof(STCallsiteStats))) {
  1.2207 +                    /*
  1.2208 +                     ** Doesn't match, stop.
  1.2209 +                     */
  1.2210 +                    break;
  1.2211 +                }
  1.2212 +                else {
  1.2213 +                    /*
  1.2214 +                     ** Matches, keep going up.
  1.2215 +                     */
  1.2216 +                    namesite = namesite->parent;
  1.2217 +                }
  1.2218 +            }
  1.2219 +            while (NULL != namesite->parent
  1.2220 +                   && NULL != namesite->parent->method);
  1.2221 +        }
  1.2222 +
  1.2223 +        /*
  1.2224 +         ** If no text, provide our own.
  1.2225 +         */
  1.2226 +        if (NULL == aText || '\0' == *aText) {
  1.2227 +            const char *methodName = NULL;
  1.2228 +            const char *sourceFile = NULL;
  1.2229 +
  1.2230 +            if (NULL != namesite->method) {
  1.2231 +                methodName = tmmethodnode_name(namesite->method);
  1.2232 +            }
  1.2233 +            else {
  1.2234 +                methodName = "==NONAME==";
  1.2235 +            }
  1.2236 +
  1.2237 +            /*
  1.2238 +             ** Decide which format to use to identify the callsite.
  1.2239 +             ** If we can detect availability, hook up the filename with lxr information.
  1.2240 +             */
  1.2241 +            sourceFile = resolveSourceFile(namesite->method);
  1.2242 +            if (NULL != sourceFile
  1.2243 +                && 0 == strncmp("mozilla/", namesite->method->sourcefile,
  1.2244 +                                8)) {
  1.2245 +                char lxrHREFBuf[512];
  1.2246 +
  1.2247 +                PR_snprintf(lxrHREFBuf, sizeof(lxrHREFBuf),
  1.2248 +                            " [<a href=\"http://lxr.mozilla.org/mozilla/source/%s#%u\" class=\"lxr\" target=\"_st_lxr\">%s:%u</a>]",
  1.2249 +                            namesite->method->sourcefile + 8,
  1.2250 +                            namesite->method->linenumber, sourceFile,
  1.2251 +                            namesite->method->linenumber);
  1.2252 +                PR_snprintf(textBuf, sizeof(textBuf),
  1.2253 +                            "<span class=\"source mozilla-source\">%s</span>%s",
  1.2254 +                            methodName, lxrHREFBuf);
  1.2255 +            }
  1.2256 +            else if (NULL != sourceFile) {
  1.2257 +                PR_snprintf(textBuf, sizeof(textBuf),
  1.2258 +                            "<span class=\"source external-source\">%s [<span class=\"source-extra\">%s:%u</span>]</span>",
  1.2259 +                            methodName, sourceFile,
  1.2260 +                            namesite->method->linenumber);
  1.2261 +            }
  1.2262 +            else {
  1.2263 +                PR_snprintf(textBuf, sizeof(textBuf),
  1.2264 +                            "<span class=\"source binary-source\">%s [<span class=\"source-extra\">+%u(%u)</span>]</span>",
  1.2265 +                            methodName, namesite->offset,
  1.2266 +                            (uint32_t) namesite->entry.key);
  1.2267 +            }
  1.2268 +
  1.2269 +            aText = textBuf;
  1.2270 +        }
  1.2271 +
  1.2272 +        PR_snprintf(hrefBuf, sizeof(hrefBuf), "callsite_%u.html",
  1.2273 +                    (uint32_t) aCallsite->entry.key);
  1.2274 +
  1.2275 +        htmlAnchor(inRequest, hrefBuf, aText, NULL, "callsite",
  1.2276 +                   &inRequest->mOptions);
  1.2277 +    }
  1.2278 +    else {
  1.2279 +        REPORT_ERROR(__LINE__, htmlCallsiteAnchor);
  1.2280 +    }
  1.2281 +}
  1.2282 +
  1.2283 +/*
  1.2284 +** htmlHeader
  1.2285 +**
  1.2286 +** Output a standard header in the report files.
  1.2287 +*/
  1.2288 +void
  1.2289 +htmlHeader(STRequest * inRequest, const char *aTitle)
  1.2290 +{
  1.2291 +    PR_fprintf(inRequest->mFD,
  1.2292 +               "<html>\n"
  1.2293 +               "<head>\n"
  1.2294 +               "<title>%s</title>\n"
  1.2295 +               "<link rel=\"stylesheet\" href=\"spacetrace.css\" type=\"text/css\""
  1.2296 +               "</head>\n"
  1.2297 +               "<body>\n"
  1.2298 +               "<div class=spacetrace-header>\n"
  1.2299 +               "<span class=spacetrace-title>Spacetrace</span>"
  1.2300 +               "<span class=navigate>\n"
  1.2301 +               "<span class=\"category-title header-text\">Category:</span>\n"
  1.2302 +               "<span class=\"current-category\">%s</span>\n",
  1.2303 +               aTitle, inRequest->mOptions.mCategoryName);
  1.2304 +
  1.2305 +    PR_fprintf(inRequest->mFD, "<span class=\"header-item\">");
  1.2306 +    htmlAnchor(inRequest, "index.html", "Index", NULL, "header-menuitem",
  1.2307 +               &inRequest->mOptions);
  1.2308 +    PR_fprintf(inRequest->mFD, "</span>\n");
  1.2309 +
  1.2310 +    PR_fprintf(inRequest->mFD, "<span class=\"header-item\">");
  1.2311 +    htmlAnchor(inRequest, "options.html", "Options", NULL, "header-menuitem",
  1.2312 +               &inRequest->mOptions);
  1.2313 +    PR_fprintf(inRequest->mFD, "</span>\n");
  1.2314 +
  1.2315 +    PR_fprintf(inRequest->mFD, "</span>\n");    /* class=navigate */
  1.2316 +
  1.2317 +    PR_fprintf(inRequest->mFD,
  1.2318 +               "</div>\n\n<div class=\"header-separator\"></div>\n\n");
  1.2319 +}
  1.2320 +
  1.2321 +/*
  1.2322 +** htmlFooter
  1.2323 +**
  1.2324 +** Output a standard footer in the report file.
  1.2325 +*/
  1.2326 +void
  1.2327 +htmlFooter(STRequest * inRequest)
  1.2328 +{
  1.2329 +    PR_fprintf(inRequest->mFD,
  1.2330 +               "<div class=\"footer-separator\"></div>\n\n"
  1.2331 +               "<div class=\"footer\">\n"
  1.2332 +               "<span class=\"footer-text\">SpaceTrace</span>\n"
  1.2333 +               "</div>\n\n" "</body>\n" "</html>\n");
  1.2334 +}
  1.2335 +
  1.2336 +/*
  1.2337 +** htmlNotFound
  1.2338 +**
  1.2339 +** Not found message.
  1.2340 +*/
  1.2341 +void
  1.2342 +htmlNotFound(STRequest * inRequest)
  1.2343 +{
  1.2344 +    htmlHeader(inRequest, "File Not Found");
  1.2345 +    PR_fprintf(inRequest->mFD, "File Not Found\n");
  1.2346 +    htmlFooter(inRequest);
  1.2347 +}
  1.2348 +
  1.2349 +void
  1.2350 +htmlStartTable(STRequest* inRequest,
  1.2351 +               const char* table_class,
  1.2352 +               const char* id,
  1.2353 +               const char* caption,
  1.2354 +               const char * const headers[], uint32_t header_length)
  1.2355 +{
  1.2356 +        uint32_t i;
  1.2357 +        
  1.2358 +        PR_fprintf(inRequest->mFD,
  1.2359 +                   "<div id=\"%s\"><table class=\"data %s\">\n"
  1.2360 +                   "  <caption>%s</caption>"
  1.2361 +                   "  <thead>\n"
  1.2362 +                   "    <tr class=\"row-header\">\n", id,
  1.2363 +                   table_class ? table_class : "",
  1.2364 +                   caption);
  1.2365 +        
  1.2366 +        for (i=0; i< header_length; i++)
  1.2367 +            PR_fprintf(inRequest->mFD,
  1.2368 +                       "      <th>%s</th>\n", headers[i]);
  1.2369 +        
  1.2370 +        PR_fprintf(inRequest->mFD, "    </tr>  </thead>  <tbody>\n");
  1.2371 +}
  1.2372 +
  1.2373 +/*
  1.2374 +** callsiteArrayFromCallsite
  1.2375 +**
  1.2376 +** Simply return an array of the callsites divulged from the site passed in,
  1.2377 +**  including the site passed in.
  1.2378 +** Do not worry about dups, or the order of the items.
  1.2379 +**
  1.2380 +** Returns the number of items in the array.
  1.2381 +** If the same as aExistingCount, then nothing happened.
  1.2382 +*/
  1.2383 +uint32_t
  1.2384 +callsiteArrayFromCallsite(tmcallsite *** aArray, uint32_t aExistingCount,
  1.2385 +                          tmcallsite * aSite, int aFollow)
  1.2386 +{
  1.2387 +    uint32_t retval = 0;
  1.2388 +
  1.2389 +    if (NULL != aArray && NULL != aSite) {
  1.2390 +        tmcallsite **expand = NULL;
  1.2391 +
  1.2392 +        /*
  1.2393 +         ** If we have an existing count, we just keep expanding this.
  1.2394 +         */
  1.2395 +        retval = aExistingCount;
  1.2396 +
  1.2397 +        /*
  1.2398 +         ** Go through every allocation.
  1.2399 +         */
  1.2400 +        do {
  1.2401 +            /*
  1.2402 +             ** expand the array.
  1.2403 +             */
  1.2404 +            expand =
  1.2405 +                (tmcallsite **) realloc(*aArray,
  1.2406 +                                        sizeof(tmcallsite *) * (retval + 1));
  1.2407 +            if (NULL != expand) {
  1.2408 +                /*
  1.2409 +                 ** Set the callsite in case of pointer move.
  1.2410 +                 */
  1.2411 +                *aArray = expand;
  1.2412 +
  1.2413 +                /*
  1.2414 +                 ** Assign the value.
  1.2415 +                 */
  1.2416 +                (*aArray)[retval] = aSite;
  1.2417 +                retval++;
  1.2418 +            }
  1.2419 +            else {
  1.2420 +                REPORT_ERROR(__LINE__, realloc);
  1.2421 +                break;
  1.2422 +            }
  1.2423 +
  1.2424 +
  1.2425 +            /*
  1.2426 +             ** What do we follow?
  1.2427 +             */
  1.2428 +            switch (aFollow) {
  1.2429 +            case ST_FOLLOW_SIBLINGS:
  1.2430 +                aSite = aSite->siblings;
  1.2431 +                break;
  1.2432 +            case ST_FOLLOW_PARENTS:
  1.2433 +                aSite = aSite->parent;
  1.2434 +                break;
  1.2435 +            default:
  1.2436 +                aSite = NULL;
  1.2437 +                REPORT_ERROR(__LINE__, callsiteArrayFromCallsite);
  1.2438 +                break;
  1.2439 +            }
  1.2440 +        }
  1.2441 +        while (NULL != aSite && NULL != aSite->method);
  1.2442 +    }
  1.2443 +
  1.2444 +    return retval;
  1.2445 +}
  1.2446 +
  1.2447 +/*
  1.2448 +** callsiteArrayFromRun
  1.2449 +**
  1.2450 +** Simply return an array of the callsites from the run allocations.
  1.2451 +** We only pay attention to callsites that were not free callsites.
  1.2452 +** Do not worry about dups, or the order of the items.
  1.2453 +**
  1.2454 +** Returns the number of items in the array.
  1.2455 +** If the same as aExistingCount, then nothing happened.
  1.2456 +*/
  1.2457 +uint32_t
  1.2458 +callsiteArrayFromRun(tmcallsite *** aArray, uint32_t aExistingCount,
  1.2459 +                     STRun * aRun)
  1.2460 +{
  1.2461 +    uint32_t retval = 0;
  1.2462 +
  1.2463 +    if (NULL != aArray && NULL != aRun && 0 < aRun->mAllocationCount) {
  1.2464 +        uint32_t allocLoop = 0;
  1.2465 +        uint32_t eventLoop = 0;
  1.2466 +        int stopLoops = 0;
  1.2467 +
  1.2468 +        /*
  1.2469 +         ** If we have an existing count, we just keep expanding this.
  1.2470 +         */
  1.2471 +        retval = aExistingCount;
  1.2472 +
  1.2473 +        /*
  1.2474 +         ** Go through every allocation.
  1.2475 +         */
  1.2476 +        for (allocLoop = 0;
  1.2477 +             0 == stopLoops && allocLoop < aRun->mAllocationCount;
  1.2478 +             allocLoop++) {
  1.2479 +            /*
  1.2480 +             ** Go through every event.
  1.2481 +             */
  1.2482 +            for (eventLoop = 0;
  1.2483 +                 0 == stopLoops
  1.2484 +                 && eventLoop < aRun->mAllocations[allocLoop]->mEventCount;
  1.2485 +                 eventLoop++) {
  1.2486 +                /*
  1.2487 +                 ** Skip the free events.
  1.2488 +                 */
  1.2489 +                if (TM_EVENT_FREE !=
  1.2490 +                    aRun->mAllocations[allocLoop]->mEvents[eventLoop].
  1.2491 +                    mEventType) {
  1.2492 +                    tmcallsite **expand = NULL;
  1.2493 +
  1.2494 +                    /*
  1.2495 +                     ** expand the array.
  1.2496 +                     */
  1.2497 +                    expand =
  1.2498 +                        (tmcallsite **) realloc(*aArray,
  1.2499 +                                                sizeof(tmcallsite *) *
  1.2500 +                                                (retval + 1));
  1.2501 +                    if (NULL != expand) {
  1.2502 +                        /*
  1.2503 +                         ** Set the callsite in case of pointer move.
  1.2504 +                         */
  1.2505 +                        *aArray = expand;
  1.2506 +
  1.2507 +                        /*
  1.2508 +                         ** Assign the value.
  1.2509 +                         */
  1.2510 +                        (*aArray)[retval] =
  1.2511 +                            aRun->mAllocations[allocLoop]->mEvents[eventLoop].
  1.2512 +                            mCallsite;
  1.2513 +                        retval++;
  1.2514 +                    }
  1.2515 +                    else {
  1.2516 +                        REPORT_ERROR(__LINE__, realloc);
  1.2517 +                        stopLoops = __LINE__;
  1.2518 +                    }
  1.2519 +                }
  1.2520 +            }
  1.2521 +        }
  1.2522 +    }
  1.2523 +
  1.2524 +    return retval;
  1.2525 +}
  1.2526 +
  1.2527 +/*
  1.2528 +** getDataPRUint*
  1.2529 +**
  1.2530 +** Helper to avoid cut and paste code.
  1.2531 +** Failure to find aCheckFor does not mean failure.
  1.2532 +** In case of dups, specify an index on non "1" to get others.
  1.2533 +** Do not touch storage space unless a find is made.
  1.2534 +** Returns !0 on failure.
  1.2535 +*/
  1.2536 +int
  1.2537 +getDataPRUint32Base(const FormData * aGetData, const char *aCheckFor,
  1.2538 +                    int inIndex, void *aStoreResult, uint32_t aBits)
  1.2539 +{
  1.2540 +    int retval = 0;
  1.2541 +
  1.2542 +    if (NULL != aGetData && NULL != aCheckFor && 0 != inIndex
  1.2543 +        && NULL != aStoreResult) {
  1.2544 +        unsigned finder = 0;
  1.2545 +
  1.2546 +        /*
  1.2547 +         **  Loop over the names, looking for an exact string match.
  1.2548 +         **  Skip over initial finds, decrementing inIndex, until "1".
  1.2549 +         */
  1.2550 +        for (finder = 0; finder < aGetData->mNVCount; finder++) {
  1.2551 +            if (0 == strcmp(aCheckFor, aGetData->mNArray[finder])) {
  1.2552 +                inIndex--;
  1.2553 +
  1.2554 +                if (0 == inIndex) {
  1.2555 +                    int32_t scanRes = 0;
  1.2556 +
  1.2557 +                    if (64 == aBits) {
  1.2558 +                        scanRes =
  1.2559 +                            PR_sscanf(aGetData->mVArray[finder], "%llu",
  1.2560 +                                      aStoreResult);
  1.2561 +                    }
  1.2562 +                    else {
  1.2563 +                        scanRes =
  1.2564 +                            PR_sscanf(aGetData->mVArray[finder], "%u",
  1.2565 +                                      aStoreResult);
  1.2566 +                    }
  1.2567 +                    if (1 != scanRes) {
  1.2568 +                        retval = __LINE__;
  1.2569 +                        REPORT_ERROR(__LINE__, PR_sscanf);
  1.2570 +                    }
  1.2571 +                    break;
  1.2572 +                }
  1.2573 +            }
  1.2574 +        }
  1.2575 +    }
  1.2576 +    else {
  1.2577 +        retval = __LINE__;
  1.2578 +        REPORT_ERROR(__LINE__, getDataPRUint32Base);
  1.2579 +    }
  1.2580 +
  1.2581 +    return retval;
  1.2582 +}
  1.2583 +
  1.2584 +int
  1.2585 +getDataPRUint32(const FormData * aGetData, const char *aCheckFor, int inIndex,
  1.2586 +                uint32_t * aStoreResult, uint32_t aConversion)
  1.2587 +{
  1.2588 +    int retval = 0;
  1.2589 +
  1.2590 +    retval =
  1.2591 +        getDataPRUint32Base(aGetData, aCheckFor, inIndex, aStoreResult, 32);
  1.2592 +    *aStoreResult *= aConversion;
  1.2593 +
  1.2594 +    return retval;
  1.2595 +}
  1.2596 +
  1.2597 +int
  1.2598 +getDataPRUint64(const FormData * aGetData, const char *aCheckFor, int inIndex,
  1.2599 +                uint64_t * aStoreResult64, uint64_t aConversion64)
  1.2600 +{
  1.2601 +    int retval = 0;
  1.2602 +    uint64_t value64 = 0;
  1.2603 +
  1.2604 +    retval = getDataPRUint32Base(aGetData, aCheckFor, inIndex, &value64, 64);
  1.2605 +    *aStoreResult64 = value64 * aConversion64;
  1.2606 +
  1.2607 +    return retval;
  1.2608 +}
  1.2609 +
  1.2610 +/*
  1.2611 +** getDataString
  1.2612 +**
  1.2613 +** Pull out the string data, if specified.
  1.2614 +** In case of dups, specify an index on non "1" to get others.
  1.2615 +** Do not touch storage space unless a find is made.
  1.2616 +** Return !0 on failure.
  1.2617 +*/
  1.2618 +int
  1.2619 +getDataString(const FormData * aGetData, const char *aCheckFor, int inIndex,
  1.2620 +              char *aStoreResult, int inStoreResultLength)
  1.2621 +{
  1.2622 +    int retval = 0;
  1.2623 +
  1.2624 +    if (NULL != aGetData && NULL != aCheckFor && 0 != inIndex
  1.2625 +        && NULL != aStoreResult && 0 != inStoreResultLength) {
  1.2626 +        unsigned finder = 0;
  1.2627 +
  1.2628 +        /*
  1.2629 +         **  Loop over the names, looking for an exact string match.
  1.2630 +         **  Skip over initial finds, decrementing inIndex, until "1".
  1.2631 +         */
  1.2632 +        for (finder = 0; finder < aGetData->mNVCount; finder++) {
  1.2633 +            if (0 == strcmp(aCheckFor, aGetData->mNArray[finder])) {
  1.2634 +                inIndex--;
  1.2635 +
  1.2636 +                if (0 == inIndex) {
  1.2637 +                    PR_snprintf(aStoreResult, inStoreResultLength, "%s",
  1.2638 +                                aGetData->mVArray[finder]);
  1.2639 +                    break;
  1.2640 +                }
  1.2641 +            }
  1.2642 +        }
  1.2643 +    }
  1.2644 +    else {
  1.2645 +        retval = __LINE__;
  1.2646 +        REPORT_ERROR(__LINE__, getDataPRUint32);
  1.2647 +    }
  1.2648 +
  1.2649 +    return retval;
  1.2650 +}
  1.2651 +
  1.2652 +/*
  1.2653 +** displayTopAllocations
  1.2654 +**
  1.2655 +** Present the top allocations.
  1.2656 +** The run must be passed in, and it must be pre-sorted.
  1.2657 +**
  1.2658 +** Returns !0 on failure.
  1.2659 +*/
  1.2660 +int
  1.2661 +displayTopAllocations(STRequest * inRequest, STRun * aRun,
  1.2662 +                      const char* id,
  1.2663 +                      const char* caption,
  1.2664 +                      int aWantCallsite)
  1.2665 +{
  1.2666 +    int retval = 0;
  1.2667 +
  1.2668 +    if (NULL != aRun) {
  1.2669 +        if (0 < aRun->mAllocationCount) {
  1.2670 +            uint32_t loop = 0;
  1.2671 +            STAllocation *current = NULL;
  1.2672 +
  1.2673 +            static const char* const headers[] = {
  1.2674 +                "Rank", "Index", "Byte Size", "Lifespan (sec)",
  1.2675 +                "Weight", "Heap Op (sec)"
  1.2676 +            };
  1.2677 +            
  1.2678 +            static const char* const headers_callsite[] = {
  1.2679 +                "Rank", "Index", "Byte Size", "Lifespan (sec)",
  1.2680 +                "Weight", "Heap Op (sec)", "Origin Callsite"
  1.2681 +            };
  1.2682 +
  1.2683 +            if (aWantCallsite)
  1.2684 +                htmlStartTable(inRequest, NULL, id,
  1.2685 +                               caption,
  1.2686 +                               headers_callsite,
  1.2687 +                               sizeof(headers_callsite) / sizeof(headers_callsite[0]));
  1.2688 +            else
  1.2689 +                htmlStartTable(inRequest, NULL, id, caption,
  1.2690 +                               headers,
  1.2691 +                               sizeof(headers) / sizeof(headers[0]));                           
  1.2692 +            /*
  1.2693 +             ** Loop over the items, up to some limit or until the end.
  1.2694 +             */
  1.2695 +            for (loop = 0;
  1.2696 +                 loop < inRequest->mOptions.mListItemMax
  1.2697 +                 && loop < aRun->mAllocationCount; loop++) {
  1.2698 +                current = aRun->mAllocations[loop];
  1.2699 +                if (NULL != current) {
  1.2700 +                    uint32_t lifespan =
  1.2701 +                        current->mMaxTimeval - current->mMinTimeval;
  1.2702 +                    uint32_t size = byteSize(&inRequest->mOptions, current);
  1.2703 +                    uint32_t heapCost = current->mHeapRuntimeCost;
  1.2704 +                    uint64_t weight64 = 0;
  1.2705 +                    char buffer[32];
  1.2706 +
  1.2707 +                    weight64 =(uint64_t)(size * lifespan);
  1.2708 +
  1.2709 +                    PR_fprintf(inRequest->mFD, "<tr>\n");
  1.2710 +
  1.2711 +                    /*
  1.2712 +                     ** Rank.
  1.2713 +                     */
  1.2714 +                    PR_fprintf(inRequest->mFD, "<td align=right>%u</td>\n",
  1.2715 +                               loop + 1);
  1.2716 +
  1.2717 +                    /*
  1.2718 +                     ** Index.
  1.2719 +                     */
  1.2720 +                    PR_snprintf(buffer, sizeof(buffer), "%u",
  1.2721 +                                current->mRunIndex);
  1.2722 +                    PR_fprintf(inRequest->mFD, "<td align=right>\n");
  1.2723 +                    htmlAllocationAnchor(inRequest, current, buffer);
  1.2724 +                    PR_fprintf(inRequest->mFD, "</td>\n");
  1.2725 +
  1.2726 +                    /*
  1.2727 +                     ** Byte Size.
  1.2728 +                     */
  1.2729 +                    PR_fprintf(inRequest->mFD, "<td align=right>%u</td>\n",
  1.2730 +                               size);
  1.2731 +
  1.2732 +                    /*
  1.2733 +                     ** Lifespan.
  1.2734 +                     */
  1.2735 +                    PR_fprintf(inRequest->mFD,
  1.2736 +                               "<td align=right>" ST_TIMEVAL_FORMAT "</td>\n",
  1.2737 +                               ST_TIMEVAL_PRINTABLE(lifespan));
  1.2738 +
  1.2739 +                    /*
  1.2740 +                     ** Weight.
  1.2741 +                     */
  1.2742 +                    PR_fprintf(inRequest->mFD, "<td align=right>%llu</td>\n",
  1.2743 +                               weight64);
  1.2744 +
  1.2745 +                    /*
  1.2746 +                     ** Heap operation cost.
  1.2747 +                     */
  1.2748 +                    PR_fprintf(inRequest->mFD,
  1.2749 +                               "<td align=right>" ST_MICROVAL_FORMAT
  1.2750 +                               "</td>\n", ST_MICROVAL_PRINTABLE(heapCost));
  1.2751 +
  1.2752 +                    if (0 != aWantCallsite) {
  1.2753 +                        /*
  1.2754 +                         ** Callsite.
  1.2755 +                         */
  1.2756 +                        PR_fprintf(inRequest->mFD, "<td>");
  1.2757 +                        htmlCallsiteAnchor(inRequest,
  1.2758 +                                           current->mEvents[0].mCallsite,
  1.2759 +                                           NULL, 0);
  1.2760 +                        PR_fprintf(inRequest->mFD, "</td>\n");
  1.2761 +                    }
  1.2762 +
  1.2763 +                    PR_fprintf(inRequest->mFD, "</tr>\n");
  1.2764 +                }
  1.2765 +            }
  1.2766 +
  1.2767 +            PR_fprintf(inRequest->mFD, "</tbody>\n</table></div>\n\n");
  1.2768 +        }
  1.2769 +    }
  1.2770 +    else {
  1.2771 +        retval = __LINE__;
  1.2772 +        REPORT_ERROR(__LINE__, displayTopAllocations);
  1.2773 +    }
  1.2774 +
  1.2775 +    return retval;
  1.2776 +}
  1.2777 +
  1.2778 +/*
  1.2779 +** displayMemoryLeaks
  1.2780 +**
  1.2781 +** Present the top memory leaks.
  1.2782 +** The run must be passed in, and it must be pre-sorted.
  1.2783 +**
  1.2784 +** Returns !0 on failure.
  1.2785 +*/
  1.2786 +int
  1.2787 +displayMemoryLeaks(STRequest * inRequest, STRun * aRun)
  1.2788 +{
  1.2789 +    int retval = 0;
  1.2790 +
  1.2791 +    if (NULL != aRun) {
  1.2792 +        uint32_t loop = 0;
  1.2793 +        uint32_t displayed = 0;
  1.2794 +        STAllocation *current = NULL;
  1.2795 +
  1.2796 +        static const char * headers[] = {
  1.2797 +            "Rank", "Index", "Byte Size", "Lifespan (sec)",
  1.2798 +            "Weight", "Heap Op (sec)", "Origin Callsite"
  1.2799 +        };
  1.2800 +        
  1.2801 +        htmlStartTable(inRequest, NULL, "memory-leaks", "Memory Leaks", headers,
  1.2802 +                       sizeof(headers) / sizeof(headers[0]));
  1.2803 +
  1.2804 +        /*
  1.2805 +         ** Loop over all of the items, or until we've displayed enough.
  1.2806 +         */
  1.2807 +        for (loop = 0;
  1.2808 +             displayed < inRequest->mOptions.mListItemMax
  1.2809 +             && loop < aRun->mAllocationCount; loop++) {
  1.2810 +            current = aRun->mAllocations[loop];
  1.2811 +            if (NULL != current && 0 != current->mEventCount) {
  1.2812 +                /*
  1.2813 +                 ** In order to be a leak, the last event of its life must
  1.2814 +                 **  NOT be a free operation.
  1.2815 +                 **
  1.2816 +                 ** A free operation is just that, a free.
  1.2817 +                 */
  1.2818 +                if (TM_EVENT_FREE !=
  1.2819 +                    current->mEvents[current->mEventCount - 1].mEventType) {
  1.2820 +                    uint32_t lifespan =
  1.2821 +                        current->mMaxTimeval - current->mMinTimeval;
  1.2822 +                    uint32_t size = byteSize(&inRequest->mOptions, current);
  1.2823 +                    uint32_t heapCost = current->mHeapRuntimeCost;
  1.2824 +                    uint64_t weight64 = 0;
  1.2825 +                    char buffer[32];
  1.2826 +
  1.2827 +                    weight64 =(uint64_t)(size * lifespan);
  1.2828 +
  1.2829 +                    /*
  1.2830 +                     ** One more shown.
  1.2831 +                     */
  1.2832 +                    displayed++;
  1.2833 +
  1.2834 +                    PR_fprintf(inRequest->mFD, "<tr>\n");
  1.2835 +
  1.2836 +                    /*
  1.2837 +                     ** Rank.
  1.2838 +                     */
  1.2839 +                    PR_fprintf(inRequest->mFD, "<td align=right>%u</td>\n",
  1.2840 +                               displayed);
  1.2841 +
  1.2842 +                    /*
  1.2843 +                     ** Index.
  1.2844 +                     */
  1.2845 +                    PR_snprintf(buffer, sizeof(buffer), "%u",
  1.2846 +                                current->mRunIndex);
  1.2847 +                    PR_fprintf(inRequest->mFD, "<td align=right>\n");
  1.2848 +                    htmlAllocationAnchor(inRequest, current, buffer);
  1.2849 +                    PR_fprintf(inRequest->mFD, "</td>\n");
  1.2850 +
  1.2851 +                    /*
  1.2852 +                     ** Byte Size.
  1.2853 +                     */
  1.2854 +                    PR_fprintf(inRequest->mFD, "<td align=right>%u</td>\n",
  1.2855 +                               size);
  1.2856 +
  1.2857 +                    /*
  1.2858 +                     ** Lifespan.
  1.2859 +                     */
  1.2860 +                    PR_fprintf(inRequest->mFD,
  1.2861 +                               "<td align=right>" ST_TIMEVAL_FORMAT "</td>\n",
  1.2862 +                               ST_TIMEVAL_PRINTABLE(lifespan));
  1.2863 +
  1.2864 +                    /*
  1.2865 +                     ** Weight.
  1.2866 +                     */
  1.2867 +                    PR_fprintf(inRequest->mFD, "<td align=right>%llu</td>\n",
  1.2868 +                               weight64);
  1.2869 +
  1.2870 +                    /*
  1.2871 +                     ** Heap Operation Seconds.
  1.2872 +                     */
  1.2873 +                    PR_fprintf(inRequest->mFD,
  1.2874 +                               "<td align=right>" ST_MICROVAL_FORMAT
  1.2875 +                               "</td>\n", ST_MICROVAL_PRINTABLE(heapCost));
  1.2876 +
  1.2877 +                    /*
  1.2878 +                     ** Callsite.
  1.2879 +                     */
  1.2880 +                    PR_fprintf(inRequest->mFD, "<td>");
  1.2881 +                    htmlCallsiteAnchor(inRequest,
  1.2882 +                                       current->mEvents[0].mCallsite, NULL,
  1.2883 +                                       0);
  1.2884 +                    PR_fprintf(inRequest->mFD, "</td>\n");
  1.2885 +
  1.2886 +                    PR_fprintf(inRequest->mFD, "</tr>\n");
  1.2887 +                }
  1.2888 +            }
  1.2889 +        }
  1.2890 +
  1.2891 +        PR_fprintf(inRequest->mFD, "</tbody></table></div>\n\n");
  1.2892 +    }
  1.2893 +    else {
  1.2894 +        retval = __LINE__;
  1.2895 +        REPORT_ERROR(__LINE__, displayMemoryLeaks);
  1.2896 +    }
  1.2897 +
  1.2898 +    return retval;
  1.2899 +}
  1.2900 +
  1.2901 +/*
  1.2902 +** displayCallsites
  1.2903 +**
  1.2904 +** Display a table of callsites.
  1.2905 +** If the stamp is non zero, then must match that stamp.
  1.2906 +** If the stamp is zero, then must match the global sorted run stamp.
  1.2907 +** Return !0 on error.
  1.2908 +*/
  1.2909 +int
  1.2910 +displayCallsites(STRequest * inRequest, tmcallsite * aCallsite, int aFollow,
  1.2911 +                 uint32_t aStamp,
  1.2912 +                 const char* id,
  1.2913 +                 const char* caption,
  1.2914 +                 int aRealNames)
  1.2915 +{
  1.2916 +    int retval = 0;
  1.2917 +
  1.2918 +    if (NULL != aCallsite && NULL != aCallsite->method) {
  1.2919 +        int headerDisplayed = 0;
  1.2920 +        STRun *run = NULL;
  1.2921 +
  1.2922 +        /*
  1.2923 +         ** Correct the stamp if need be.
  1.2924 +         */
  1.2925 +        if (0 == aStamp && NULL != inRequest->mContext->mSortedRun) {
  1.2926 +            aStamp =
  1.2927 +                inRequest->mContext->mSortedRun->mStats[inRequest->mContext->
  1.2928 +                                                        mIndex].mStamp;
  1.2929 +        }
  1.2930 +
  1.2931 +        /*
  1.2932 +         ** Loop over the callsites looking for a stamp match.
  1.2933 +         ** A stamp guarantees there is something interesting to look at too.
  1.2934 +         ** If found, output it.
  1.2935 +         */
  1.2936 +        while (NULL != aCallsite && NULL != aCallsite->method) {
  1.2937 +            run = CALLSITE_RUN(aCallsite);
  1.2938 +            if (NULL != run) {
  1.2939 +                if (aStamp == run->mStats[inRequest->mContext->mIndex].mStamp) {
  1.2940 +                    /*
  1.2941 +                     ** We got a header?
  1.2942 +                     */
  1.2943 +                    if (0 == headerDisplayed) {
  1.2944 +
  1.2945 +                        static const char* const headers[] = {
  1.2946 +                            "Callsite",
  1.2947 +                            "<abbr title=\"Composite Size\">C. Size</abbr>",
  1.2948 +                            "<abbr title=\"Composite Seconds\">C. Seconds</abbr>",
  1.2949 +                            "<abbr title=\"Composite Weight\">C. Weight</abbr>",
  1.2950 +                            "<abbr title=\"Heap Object Count\">H.O. Count</abbr>",
  1.2951 +                            "<abbr title=\"Composite Heap Operation Seconds\">C.H. Operation (sec)</abbr>"
  1.2952 +                        };
  1.2953 +                        headerDisplayed = __LINE__;
  1.2954 +                        htmlStartTable(inRequest, NULL, id, caption, headers,
  1.2955 +                                       sizeof(headers)/sizeof(headers[0]));
  1.2956 +                    }
  1.2957 +
  1.2958 +                    /*
  1.2959 +                     ** Output the information.
  1.2960 +                     */
  1.2961 +                    PR_fprintf(inRequest->mFD, "<tr>\n");
  1.2962 +
  1.2963 +                    /*
  1.2964 +                     ** Method name.
  1.2965 +                     */
  1.2966 +                    PR_fprintf(inRequest->mFD, "<td>");
  1.2967 +                    htmlCallsiteAnchor(inRequest, aCallsite, NULL,
  1.2968 +                                       aRealNames);
  1.2969 +                    PR_fprintf(inRequest->mFD, "</td>");
  1.2970 +
  1.2971 +                    /*
  1.2972 +                     ** Byte Size.
  1.2973 +                     */
  1.2974 +                    PR_fprintf(inRequest->mFD,
  1.2975 +                               "<td valign=top align=right>%u</td>\n",
  1.2976 +                               run->mStats[inRequest->mContext->mIndex].
  1.2977 +                               mSize);
  1.2978 +
  1.2979 +                    /*
  1.2980 +                     ** Seconds.
  1.2981 +                     */
  1.2982 +                    PR_fprintf(inRequest->mFD,
  1.2983 +                               "<td valign=top align=right>" ST_TIMEVAL_FORMAT
  1.2984 +                               "</td>\n",
  1.2985 +                               ST_TIMEVAL_PRINTABLE64(run->
  1.2986 +                                                      mStats[inRequest->
  1.2987 +                                                             mContext->
  1.2988 +                                                             mIndex].
  1.2989 +                                                      mTimeval64));
  1.2990 +
  1.2991 +                    /*
  1.2992 +                     ** Weight.
  1.2993 +                     */
  1.2994 +                    PR_fprintf(inRequest->mFD,
  1.2995 +                               "<td valign=top align=right>%llu</td>\n",
  1.2996 +                               run->mStats[inRequest->mContext->mIndex].
  1.2997 +                               mWeight64);
  1.2998 +
  1.2999 +                    /*
  1.3000 +                     ** Allocation object count.
  1.3001 +                     */
  1.3002 +                    PR_fprintf(inRequest->mFD,
  1.3003 +                               "<td valign=top align=right>%u</td>\n",
  1.3004 +                               run->mStats[inRequest->mContext->mIndex].
  1.3005 +                               mCompositeCount);
  1.3006 +
  1.3007 +                    /*
  1.3008 +                     ** Heap Operation Seconds.
  1.3009 +                     */
  1.3010 +                    PR_fprintf(inRequest->mFD,
  1.3011 +                               "<td valign=top align=right>"
  1.3012 +                               ST_MICROVAL_FORMAT "</td>\n",
  1.3013 +                               ST_MICROVAL_PRINTABLE(run->
  1.3014 +                                                     mStats[inRequest->
  1.3015 +                                                            mContext->mIndex].
  1.3016 +                                                     mHeapRuntimeCost));
  1.3017 +
  1.3018 +                    PR_fprintf(inRequest->mFD, "</tr>\n");
  1.3019 +                }
  1.3020 +            }
  1.3021 +            else {
  1.3022 +                retval = __LINE__;
  1.3023 +                REPORT_ERROR(__LINE__, displayCallsites);
  1.3024 +                break;
  1.3025 +            }
  1.3026 +
  1.3027 +            /*
  1.3028 +             ** What do we follow?
  1.3029 +             */
  1.3030 +            switch (aFollow) {
  1.3031 +            case ST_FOLLOW_SIBLINGS:
  1.3032 +                aCallsite = aCallsite->siblings;
  1.3033 +                break;
  1.3034 +            case ST_FOLLOW_PARENTS:
  1.3035 +                aCallsite = aCallsite->parent;
  1.3036 +                break;
  1.3037 +            default:
  1.3038 +                aCallsite = NULL;
  1.3039 +                retval = __LINE__;
  1.3040 +                REPORT_ERROR(__LINE__, displayCallsites);
  1.3041 +                break;
  1.3042 +            }
  1.3043 +        }
  1.3044 +
  1.3045 +        /*
  1.3046 +         ** Terminate the table if we should.
  1.3047 +         */
  1.3048 +        if (0 != headerDisplayed) {
  1.3049 +            PR_fprintf(inRequest->mFD, "</tbody></table></div>\n\n");
  1.3050 +        }
  1.3051 +    }
  1.3052 +    else {
  1.3053 +        retval = __LINE__;
  1.3054 +        REPORT_ERROR(__LINE__, displayCallsites);
  1.3055 +    }
  1.3056 +
  1.3057 +    return retval;
  1.3058 +}
  1.3059 +
  1.3060 +/*
  1.3061 +** displayAllocationDetails
  1.3062 +**
  1.3063 +** Report what we know about the allocation.
  1.3064 +**
  1.3065 +** Returns !0 on error.
  1.3066 +*/
  1.3067 +int
  1.3068 +displayAllocationDetails(STRequest * inRequest, STAllocation * aAllocation)
  1.3069 +{
  1.3070 +    int retval = 0;
  1.3071 +
  1.3072 +    if (NULL != aAllocation) {
  1.3073 +        uint32_t traverse = 0;
  1.3074 +        uint32_t bytesize = byteSize(&inRequest->mOptions, aAllocation);
  1.3075 +        uint32_t timeval =
  1.3076 +            aAllocation->mMaxTimeval - aAllocation->mMinTimeval;
  1.3077 +        uint32_t heapCost = aAllocation->mHeapRuntimeCost;
  1.3078 +        uint64_t weight64 = 0;
  1.3079 +        uint32_t cacheval = 0;
  1.3080 +        int displayRes = 0;
  1.3081 +
  1.3082 +        weight64 = (uint64_t)(bytesize * timeval);
  1.3083 +
  1.3084 +        PR_fprintf(inRequest->mFD, "<p>Allocation %u Details:</p>\n",
  1.3085 +                   aAllocation->mRunIndex);
  1.3086 +
  1.3087 +        PR_fprintf(inRequest->mFD, "<div id=\"allocation-details\"><table class=\"data summary\">\n");
  1.3088 +        PR_fprintf(inRequest->mFD,
  1.3089 +                   "<tr><td align=left>Final Size:</td><td align=right>%u</td></tr>\n",
  1.3090 +                   bytesize);
  1.3091 +        PR_fprintf(inRequest->mFD,
  1.3092 +                   "<tr><td align=left>Lifespan Seconds:</td><td align=right>"
  1.3093 +                   ST_TIMEVAL_FORMAT "</td></tr>\n",
  1.3094 +                   ST_TIMEVAL_PRINTABLE(timeval));
  1.3095 +        PR_fprintf(inRequest->mFD,
  1.3096 +                   "<tr><td align=left>Weight:</td><td align=right>%llu</td></tr>\n",
  1.3097 +                   weight64);
  1.3098 +        PR_fprintf(inRequest->mFD,
  1.3099 +                   "<tr><td align=left>Heap Operation Seconds:</td><td align=right>"
  1.3100 +                   ST_MICROVAL_FORMAT "</td></tr>\n",
  1.3101 +                   ST_MICROVAL_PRINTABLE(heapCost));
  1.3102 +        PR_fprintf(inRequest->mFD, "</table></div>\n");
  1.3103 +
  1.3104 +        /*
  1.3105 +         ** The events.
  1.3106 +         */
  1.3107 +        
  1.3108 +        {
  1.3109 +            static const char* const headers[] = {
  1.3110 +                "Operation", "Size", "Seconds", ""
  1.3111 +            };
  1.3112 +
  1.3113 +            char caption[100];
  1.3114 +            PR_snprintf(caption, sizeof(caption), "%u Life Event(s)",
  1.3115 +                       aAllocation->mEventCount);
  1.3116 +            htmlStartTable(inRequest, NULL, "allocation-details", caption, headers,
  1.3117 +                           sizeof(headers) / sizeof(headers[0]));
  1.3118 +        }
  1.3119 +        
  1.3120 +        for (traverse = 0;
  1.3121 +             traverse < aAllocation->mEventCount
  1.3122 +             && traverse < inRequest->mOptions.mListItemMax; traverse++) {
  1.3123 +            PR_fprintf(inRequest->mFD, "<tr>\n");
  1.3124 +
  1.3125 +            /*
  1.3126 +             ** count.
  1.3127 +             */
  1.3128 +            PR_fprintf(inRequest->mFD,
  1.3129 +                       "<td valign=top align=right>%u.</td>\n", traverse + 1);
  1.3130 +
  1.3131 +            /*
  1.3132 +             ** Operation.
  1.3133 +             */
  1.3134 +            PR_fprintf(inRequest->mFD, "<td valign=top>");
  1.3135 +            switch (aAllocation->mEvents[traverse].mEventType) {
  1.3136 +            case TM_EVENT_CALLOC:
  1.3137 +                PR_fprintf(inRequest->mFD, "calloc");
  1.3138 +                break;
  1.3139 +            case TM_EVENT_FREE:
  1.3140 +                PR_fprintf(inRequest->mFD, "free");
  1.3141 +                break;
  1.3142 +            case TM_EVENT_MALLOC:
  1.3143 +                PR_fprintf(inRequest->mFD, "malloc");
  1.3144 +                break;
  1.3145 +            case TM_EVENT_REALLOC:
  1.3146 +                PR_fprintf(inRequest->mFD, "realloc");
  1.3147 +                break;
  1.3148 +            default:
  1.3149 +                retval = __LINE__;
  1.3150 +                REPORT_ERROR(__LINE__, displayAllocationDetails);
  1.3151 +                break;
  1.3152 +            }
  1.3153 +            PR_fprintf(inRequest->mFD, "</td>");
  1.3154 +
  1.3155 +            /*
  1.3156 +             ** Size.
  1.3157 +             */
  1.3158 +            PR_fprintf(inRequest->mFD, "<td valign=top align=right>%u</td>\n",
  1.3159 +                       aAllocation->mEvents[traverse].mHeapSize);
  1.3160 +
  1.3161 +            /*
  1.3162 +             ** Timeval.
  1.3163 +             */
  1.3164 +            cacheval =
  1.3165 +                aAllocation->mEvents[traverse].mTimeval - globals.mMinTimeval;
  1.3166 +            PR_fprintf(inRequest->mFD,
  1.3167 +                       "<td valign=top align=right>" ST_TIMEVAL_FORMAT
  1.3168 +                       "</td>\n", ST_TIMEVAL_PRINTABLE(cacheval));
  1.3169 +
  1.3170 +            /*
  1.3171 +             ** Callsite backtrace.
  1.3172 +             ** Only relevant backtrace is for event 0 for now until
  1.3173 +             **  trace-malloc outputs proper callsites for all others.
  1.3174 +             */
  1.3175 +            PR_fprintf(inRequest->mFD, "<td valign=top>\n");
  1.3176 +            if (0 == traverse) {
  1.3177 +                displayRes =
  1.3178 +                    displayCallsites(inRequest,
  1.3179 +                                     aAllocation->mEvents[traverse].mCallsite,
  1.3180 +                                     ST_FOLLOW_PARENTS, 0, "event-stack", "", __LINE__);
  1.3181 +                if (0 != displayRes) {
  1.3182 +                    retval = __LINE__;
  1.3183 +                    REPORT_ERROR(__LINE__, displayCallsite);
  1.3184 +                }
  1.3185 +            }
  1.3186 +            PR_fprintf(inRequest->mFD, "</td>\n");
  1.3187 +
  1.3188 +            PR_fprintf(inRequest->mFD, "</tr>\n");
  1.3189 +        }
  1.3190 +        PR_fprintf(inRequest->mFD, "</table></div>\n");
  1.3191 +    }
  1.3192 +    else {
  1.3193 +        retval = __LINE__;
  1.3194 +        REPORT_ERROR(__LINE__, displayAllocationDetails);
  1.3195 +    }
  1.3196 +
  1.3197 +    return retval;
  1.3198 +}
  1.3199 +
  1.3200 +/*
  1.3201 +** compareCallsites
  1.3202 +**
  1.3203 +** qsort callback.
  1.3204 +** Compare the callsites as specified by the options.
  1.3205 +** There must be NO equal callsites, unless they really are duplicates,
  1.3206 +**  this is so that a duplicate detector loop can
  1.3207 +**  simply skip sorted items until the callsite is different.
  1.3208 +*/
  1.3209 +int
  1.3210 +compareCallsites(const void *aSite1, const void *aSite2, void *aContext)
  1.3211 +{
  1.3212 +    int retval = 0;
  1.3213 +    STRequest *inRequest = (STRequest *) aContext;
  1.3214 +
  1.3215 +    if (NULL != aSite1 && NULL != aSite2) {
  1.3216 +        tmcallsite *site1 = *((tmcallsite **) aSite1);
  1.3217 +        tmcallsite *site2 = *((tmcallsite **) aSite2);
  1.3218 +
  1.3219 +        if (NULL != site1 && NULL != site2) {
  1.3220 +            STRun *run1 = CALLSITE_RUN(site1);
  1.3221 +            STRun *run2 = CALLSITE_RUN(site2);
  1.3222 +
  1.3223 +            if (NULL != run1 && NULL != run2) {
  1.3224 +                STCallsiteStats *stats1 =
  1.3225 +                    &(run1->mStats[inRequest->mContext->mIndex]);
  1.3226 +                STCallsiteStats *stats2 =
  1.3227 +                    &(run2->mStats[inRequest->mContext->mIndex]);
  1.3228 +
  1.3229 +                /*
  1.3230 +                 ** Logic determined by pref/option.
  1.3231 +                 */
  1.3232 +                switch (inRequest->mOptions.mOrderBy) {
  1.3233 +                case ST_WEIGHT:
  1.3234 +                    {
  1.3235 +                        uint64_t weight164 = stats1->mWeight64;
  1.3236 +                        uint64_t weight264 = stats2->mWeight64;
  1.3237 +
  1.3238 +                        if (weight164 < weight264) {
  1.3239 +                            retval = __LINE__;
  1.3240 +                        }
  1.3241 +                        else if (weight164 > weight264) {
  1.3242 +                            retval = -__LINE__;
  1.3243 +                        }
  1.3244 +                    }
  1.3245 +                    break;
  1.3246 +
  1.3247 +                case ST_SIZE:
  1.3248 +                    {
  1.3249 +                        uint32_t size1 = stats1->mSize;
  1.3250 +                        uint32_t size2 = stats2->mSize;
  1.3251 +
  1.3252 +                        if (size1 < size2) {
  1.3253 +                            retval = __LINE__;
  1.3254 +                        }
  1.3255 +                        else if (size1 > size2) {
  1.3256 +                            retval = -__LINE__;
  1.3257 +                        }
  1.3258 +                    }
  1.3259 +                    break;
  1.3260 +
  1.3261 +                case ST_TIMEVAL:
  1.3262 +                    {
  1.3263 +                        uint64_t timeval164 = stats1->mTimeval64;
  1.3264 +                        uint64_t timeval264 = stats2->mTimeval64;
  1.3265 +
  1.3266 +                        if (timeval164 < timeval264) {
  1.3267 +                            retval = __LINE__;
  1.3268 +                        }
  1.3269 +                        else if (timeval164 > timeval264) {
  1.3270 +                            retval = -__LINE__;
  1.3271 +                        }
  1.3272 +                    }
  1.3273 +                    break;
  1.3274 +
  1.3275 +                case ST_COUNT:
  1.3276 +                    {
  1.3277 +                        uint32_t count1 = stats1->mCompositeCount;
  1.3278 +                        uint32_t count2 = stats2->mCompositeCount;
  1.3279 +
  1.3280 +                        if (count1 < count2) {
  1.3281 +                            retval = __LINE__;
  1.3282 +                        }
  1.3283 +                        else if (count1 > count2) {
  1.3284 +                            retval = -__LINE__;
  1.3285 +                        }
  1.3286 +                    }
  1.3287 +                    break;
  1.3288 +
  1.3289 +                case ST_HEAPCOST:
  1.3290 +                    {
  1.3291 +                        uint32_t cost1 = stats1->mHeapRuntimeCost;
  1.3292 +                        uint32_t cost2 = stats2->mHeapRuntimeCost;
  1.3293 +
  1.3294 +                        if (cost1 < cost2) {
  1.3295 +                            retval = __LINE__;
  1.3296 +                        }
  1.3297 +                        else if (cost1 > cost2) {
  1.3298 +                            retval = -__LINE__;
  1.3299 +                        }
  1.3300 +                    }
  1.3301 +                    break;
  1.3302 +
  1.3303 +                default:
  1.3304 +                    {
  1.3305 +                        REPORT_ERROR(__LINE__, compareAllocations);
  1.3306 +                    }
  1.3307 +                    break;
  1.3308 +                }
  1.3309 +
  1.3310 +                /*
  1.3311 +                 ** If the return value is still zero, do a pointer compare.
  1.3312 +                 ** This makes sure we return zero, only iff the same object.
  1.3313 +                 */
  1.3314 +                if (0 == retval) {
  1.3315 +                    if (stats1 < stats2) {
  1.3316 +                        retval = __LINE__;
  1.3317 +                    }
  1.3318 +                    else if (stats1 > stats2) {
  1.3319 +                        retval = -__LINE__;
  1.3320 +                    }
  1.3321 +                }
  1.3322 +            }
  1.3323 +        }
  1.3324 +    }
  1.3325 +
  1.3326 +    return retval;
  1.3327 +}
  1.3328 +
  1.3329 +/*
  1.3330 +** displayTopCallsites
  1.3331 +**
  1.3332 +** Given a list of callsites, sort it, and output skipping dups.
  1.3333 +** The passed in callsite array is side effected, as in that it will come
  1.3334 +**  back sorted.  This function will not release the array.
  1.3335 +**
  1.3336 +** Note:  If the stamp passed in is non zero, then all callsites must match.
  1.3337 +**  If the stamp is zero, all callsites must match global sorted run stamp.
  1.3338 +**
  1.3339 +** Returns !0 on error.
  1.3340 +*/
  1.3341 +int
  1.3342 +displayTopCallsites(STRequest * inRequest, tmcallsite ** aCallsites,
  1.3343 +                    uint32_t aCallsiteCount, uint32_t aStamp,
  1.3344 +                    const char* id,
  1.3345 +                    const char* caption,
  1.3346 +                    int aRealName)
  1.3347 +{
  1.3348 +    int retval = 0;
  1.3349 +
  1.3350 +    if (NULL != aCallsites && 0 < aCallsiteCount) {
  1.3351 +        uint32_t traverse = 0;
  1.3352 +        STRun *run = NULL;
  1.3353 +        tmcallsite *site = NULL;
  1.3354 +        int headerDisplayed = 0;
  1.3355 +        uint32_t displayed = 0;
  1.3356 +
  1.3357 +        /*
  1.3358 +         ** Fixup the stamp.
  1.3359 +         */
  1.3360 +        if (0 == aStamp && NULL != inRequest->mContext->mSortedRun) {
  1.3361 +            aStamp =
  1.3362 +                inRequest->mContext->mSortedRun->mStats[inRequest->mContext->
  1.3363 +                                                        mIndex].mStamp;
  1.3364 +        }
  1.3365 +
  1.3366 +        /*
  1.3367 +         ** Sort the things.
  1.3368 +         */
  1.3369 +        NS_QuickSort(aCallsites, aCallsiteCount, sizeof(tmcallsite *),
  1.3370 +                     compareCallsites, inRequest);
  1.3371 +
  1.3372 +        /*
  1.3373 +         ** Time for output.
  1.3374 +         */
  1.3375 +        for (traverse = 0;
  1.3376 +             traverse < aCallsiteCount
  1.3377 +             && inRequest->mOptions.mListItemMax > displayed; traverse++) {
  1.3378 +            site = aCallsites[traverse];
  1.3379 +            run = CALLSITE_RUN(site);
  1.3380 +
  1.3381 +            /*
  1.3382 +             ** Only if the same stamp....
  1.3383 +             */
  1.3384 +            if (aStamp == run->mStats[inRequest->mContext->mIndex].mStamp) {
  1.3385 +                /*
  1.3386 +                 ** We got a header yet?
  1.3387 +                 */
  1.3388 +                if (0 == headerDisplayed) {
  1.3389 +                    static const char* const headers[] = {
  1.3390 +                        "Rank",
  1.3391 +                        "Callsite",
  1.3392 +                        "<abbr title=\"Composite Size\">Size</abbr>",
  1.3393 +                        "<abbr title=\"Composite Seconds\">Seconds</abbr>",
  1.3394 +                        "<abbr title=\"Composite Weight\">Weight</abbr>",
  1.3395 +                        "<abbr title=\"Heap Object Count\">Object Count</abbr>",
  1.3396 +                        "<abbr title=\"Composite Heap Operation Seconds\">C.H. Operation (sec)</abbr>"
  1.3397 +                    };
  1.3398 +                    headerDisplayed = __LINE__;
  1.3399 +
  1.3400 +                    htmlStartTable(inRequest, NULL, id, caption, headers,
  1.3401 +                                   sizeof(headers) / sizeof(headers[0]));
  1.3402 +                }
  1.3403 +
  1.3404 +                displayed++;
  1.3405 +
  1.3406 +                PR_fprintf(inRequest->mFD, "<tr>\n");
  1.3407 +
  1.3408 +                /*
  1.3409 +                 ** Rank.
  1.3410 +                 */
  1.3411 +                PR_fprintf(inRequest->mFD,
  1.3412 +                           "<td align=right valign=top>%u</td>\n", displayed);
  1.3413 +
  1.3414 +                /*
  1.3415 +                 ** Method.
  1.3416 +                 */
  1.3417 +                PR_fprintf(inRequest->mFD, "<td>");
  1.3418 +                htmlCallsiteAnchor(inRequest, site, NULL, aRealName);
  1.3419 +                PR_fprintf(inRequest->mFD, "</td>\n");
  1.3420 +
  1.3421 +                /*
  1.3422 +                 ** Size.
  1.3423 +                 */
  1.3424 +                PR_fprintf(inRequest->mFD,
  1.3425 +                           "<td align=right valign=top>%u</td>\n",
  1.3426 +                           run->mStats[inRequest->mContext->mIndex].mSize);
  1.3427 +
  1.3428 +                /*
  1.3429 +                 ** Timeval.
  1.3430 +                 */
  1.3431 +                PR_fprintf(inRequest->mFD,
  1.3432 +                           "<td align=right valign=top>" ST_TIMEVAL_FORMAT
  1.3433 +                           "</td>\n",
  1.3434 +                           ST_TIMEVAL_PRINTABLE64(run->
  1.3435 +                                                  mStats[inRequest->mContext->
  1.3436 +                                                         mIndex].mTimeval64));
  1.3437 +
  1.3438 +                /*
  1.3439 +                 ** Weight.
  1.3440 +                 */
  1.3441 +                PR_fprintf(inRequest->mFD,
  1.3442 +                           "<td align=right valign=top>%llu</td>\n",
  1.3443 +                           run->mStats[inRequest->mContext->mIndex].
  1.3444 +                           mWeight64);
  1.3445 +
  1.3446 +                /*
  1.3447 +                 ** Allocation object count.
  1.3448 +                 */
  1.3449 +                PR_fprintf(inRequest->mFD,
  1.3450 +                           "<td align=right valign=top>%u</td>\n",
  1.3451 +                           run->mStats[inRequest->mContext->mIndex].
  1.3452 +                           mCompositeCount);
  1.3453 +
  1.3454 +                /*
  1.3455 +                 ** Heap operation seconds.
  1.3456 +                 */
  1.3457 +                PR_fprintf(inRequest->mFD,
  1.3458 +                           "<td align=right valign=top>" ST_MICROVAL_FORMAT
  1.3459 +                           "</td>\n",
  1.3460 +                           ST_MICROVAL_PRINTABLE(run->
  1.3461 +                                                 mStats[inRequest->mContext->
  1.3462 +                                                        mIndex].
  1.3463 +                                                 mHeapRuntimeCost));
  1.3464 +
  1.3465 +                PR_fprintf(inRequest->mFD, "</tr>\n");
  1.3466 +
  1.3467 +
  1.3468 +                if (inRequest->mOptions.mListItemMax > displayed) {
  1.3469 +                    /*
  1.3470 +                     ** Skip any dups.
  1.3471 +                     */
  1.3472 +                    while (((traverse + 1) < aCallsiteCount)
  1.3473 +                           && (site == aCallsites[traverse + 1])) {
  1.3474 +                        traverse++;
  1.3475 +                    }
  1.3476 +                }
  1.3477 +            }
  1.3478 +        }
  1.3479 +
  1.3480 +        /*
  1.3481 +         ** We need to terminate anything?
  1.3482 +         */
  1.3483 +        if (0 != headerDisplayed) {
  1.3484 +            PR_fprintf(inRequest->mFD, "</table></div>\n");
  1.3485 +        }
  1.3486 +    }
  1.3487 +    else {
  1.3488 +        retval = __LINE__;
  1.3489 +        REPORT_ERROR(__LINE__, displayTopCallsites);
  1.3490 +    }
  1.3491 +
  1.3492 +    return retval;
  1.3493 +}
  1.3494 +
  1.3495 +/*
  1.3496 +** displayCallsiteDetails
  1.3497 +**
  1.3498 +** The callsite specific report.
  1.3499 +** Try to report what we know.
  1.3500 +** This one hits a little harder than the rest.
  1.3501 +**
  1.3502 +** Returns !0 on error.
  1.3503 +*/
  1.3504 +int
  1.3505 +displayCallsiteDetails(STRequest * inRequest, tmcallsite * aCallsite)
  1.3506 +{
  1.3507 +    int retval = 0;
  1.3508 +
  1.3509 +    if (NULL != aCallsite && NULL != aCallsite->method) {
  1.3510 +        STRun *sortedRun = NULL;
  1.3511 +        STRun *thisRun = CALLSITE_RUN(aCallsite);
  1.3512 +        const char *sourceFile = NULL;
  1.3513 +
  1.3514 +        sourceFile = resolveSourceFile(aCallsite->method);
  1.3515 +
  1.3516 +        PR_fprintf(inRequest->mFD, "<div class=\"callsite-header\">\n");
  1.3517 +        if (sourceFile) {
  1.3518 +            PR_fprintf(inRequest->mFD, "<b>%s</b>",
  1.3519 +                       tmmethodnode_name(aCallsite->method));
  1.3520 +            PR_fprintf(inRequest->mFD,
  1.3521 +                       " [<a href=\"http://lxr.mozilla.org/mozilla/source/%s#%u\" class=\"lxr\" target=\"_st_lxr\">%s:%u</a>]",
  1.3522 +                       aCallsite->method->sourcefile,
  1.3523 +                       aCallsite->method->linenumber, sourceFile,
  1.3524 +                       aCallsite->method->linenumber);
  1.3525 +        }
  1.3526 +        else {
  1.3527 +            PR_fprintf(inRequest->mFD,
  1.3528 +                       "<p><b>%s</b>+%u(%u) Callsite Details:</p>\n",
  1.3529 +                       tmmethodnode_name(aCallsite->method),
  1.3530 +                       aCallsite->offset, (uint32_t) aCallsite->entry.key);
  1.3531 +        }
  1.3532 +
  1.3533 +        PR_fprintf(inRequest->mFD, "</div>\n\n");
  1.3534 +        PR_fprintf(inRequest->mFD, "<div id=\"callsite-details\"><table class=\"data summary\">\n");
  1.3535 +        PR_fprintf(inRequest->mFD,
  1.3536 +                   "<tr><td>Composite Byte Size:</td><td align=right>%u</td></tr>\n",
  1.3537 +                   thisRun->mStats[inRequest->mContext->mIndex].mSize);
  1.3538 +        PR_fprintf(inRequest->mFD,
  1.3539 +                   "<tr><td>Composite Seconds:</td><td align=right>"
  1.3540 +                   ST_TIMEVAL_FORMAT "</td></tr>\n",
  1.3541 +                   ST_TIMEVAL_PRINTABLE64(thisRun->
  1.3542 +                                          mStats[inRequest->mContext->mIndex].
  1.3543 +                                          mTimeval64));
  1.3544 +        PR_fprintf(inRequest->mFD,
  1.3545 +                   "<tr><td>Composite Weight:</td><td align=right>%llu</td></tr>\n",
  1.3546 +                   thisRun->mStats[inRequest->mContext->mIndex].mWeight64);
  1.3547 +        PR_fprintf(inRequest->mFD,
  1.3548 +                   "<tr><td>Heap Object Count:</td><td align=right>%u</td></tr>\n",
  1.3549 +                   thisRun->mStats[inRequest->mContext->mIndex].
  1.3550 +                   mCompositeCount);
  1.3551 +        PR_fprintf(inRequest->mFD,
  1.3552 +                   "<tr><td>Heap Operation Seconds:</td><td align=right>"
  1.3553 +                   ST_MICROVAL_FORMAT "</td></tr>\n",
  1.3554 +                   ST_MICROVAL_PRINTABLE(thisRun->
  1.3555 +                                         mStats[inRequest->mContext->mIndex].
  1.3556 +                                         mHeapRuntimeCost));
  1.3557 +        PR_fprintf(inRequest->mFD, "</table></div>\n\n");
  1.3558 +
  1.3559 +        /*
  1.3560 +         ** Kids (callsites we call):
  1.3561 +         */
  1.3562 +        if (NULL != aCallsite->kids && NULL != aCallsite->kids->method) {
  1.3563 +            int displayRes = 0;
  1.3564 +            uint32_t siteCount = 0;
  1.3565 +            tmcallsite **sites = NULL;
  1.3566 +
  1.3567 +            /*
  1.3568 +             ** Collect the kid sibling callsites.
  1.3569 +             ** Doing it this way sorts them for relevance.
  1.3570 +             */
  1.3571 +            siteCount =
  1.3572 +                callsiteArrayFromCallsite(&sites, 0, aCallsite->kids,
  1.3573 +                                          ST_FOLLOW_SIBLINGS);
  1.3574 +            if (0 != siteCount && NULL != sites) {
  1.3575 +                /*
  1.3576 +                 ** Got something to show.
  1.3577 +                 */
  1.3578 +                displayRes =
  1.3579 +                    displayTopCallsites(inRequest, sites, siteCount, 0,
  1.3580 +                                        "callsites",
  1.3581 +                                        "Children Callsites",
  1.3582 +                                        __LINE__);
  1.3583 +                if (0 != displayRes) {
  1.3584 +                    retval = __LINE__;
  1.3585 +                    REPORT_ERROR(__LINE__, displayTopCallsites);
  1.3586 +                }
  1.3587 +
  1.3588 +                /*
  1.3589 +                 ** Done with array.
  1.3590 +                 */
  1.3591 +                free(sites);
  1.3592 +                sites = NULL;
  1.3593 +            }
  1.3594 +        }
  1.3595 +
  1.3596 +        /*
  1.3597 +         ** Parents (those who call us):
  1.3598 +         */
  1.3599 +        if (NULL != aCallsite->parent && NULL != aCallsite->parent->method) {
  1.3600 +            int displayRes = 0;
  1.3601 +
  1.3602 +            displayRes =
  1.3603 +                displayCallsites(inRequest, aCallsite->parent,
  1.3604 +                                 ST_FOLLOW_PARENTS, 0, "caller-stack", "Caller stack",
  1.3605 +                                 __LINE__);
  1.3606 +            if (0 != displayRes) {
  1.3607 +                retval = __LINE__;
  1.3608 +                REPORT_ERROR(__LINE__, displayCallsites);
  1.3609 +            }
  1.3610 +        }
  1.3611 +
  1.3612 +        /*
  1.3613 +         ** Allocations we did.
  1.3614 +         ** Simply harvest our own run.
  1.3615 +         */
  1.3616 +        sortedRun = createRun(inRequest->mContext, 0);
  1.3617 +        if (NULL != sortedRun) {
  1.3618 +            int harvestRes = 0;
  1.3619 +
  1.3620 +            harvestRes =
  1.3621 +                harvestRun(CALLSITE_RUN(aCallsite), sortedRun,
  1.3622 +                           &inRequest->mOptions, inRequest->mContext);
  1.3623 +            if (0 == harvestRes) {
  1.3624 +                if (0 != sortedRun->mAllocationCount) {
  1.3625 +                    int sortRes = 0;
  1.3626 +
  1.3627 +                    sortRes = sortRun(&inRequest->mOptions, sortedRun);
  1.3628 +                    if (0 == sortRes) {
  1.3629 +                        int displayRes = 0;
  1.3630 +
  1.3631 +                        displayRes =
  1.3632 +                            displayTopAllocations(inRequest, sortedRun,
  1.3633 +                                                  "allocations",
  1.3634 +                                                  "Allocations",
  1.3635 +                                                  0);
  1.3636 +                        if (0 != displayRes) {
  1.3637 +                            retval = __LINE__;
  1.3638 +                            REPORT_ERROR(__LINE__, displayTopAllocations);
  1.3639 +                        }
  1.3640 +                    }
  1.3641 +                    else {
  1.3642 +                        retval = __LINE__;
  1.3643 +                        REPORT_ERROR(__LINE__, sortRun);
  1.3644 +                    }
  1.3645 +                }
  1.3646 +            }
  1.3647 +            else {
  1.3648 +                retval = __LINE__;
  1.3649 +                REPORT_ERROR(__LINE__, harvestRun);
  1.3650 +            }
  1.3651 +
  1.3652 +            /*
  1.3653 +             ** Done with the run.
  1.3654 +             */
  1.3655 +            freeRun(sortedRun);
  1.3656 +            sortedRun = NULL;
  1.3657 +        }
  1.3658 +        else {
  1.3659 +            retval = __LINE__;
  1.3660 +            REPORT_ERROR(__LINE__, createRun);
  1.3661 +        }
  1.3662 +    }
  1.3663 +    else {
  1.3664 +        retval = __LINE__;
  1.3665 +        REPORT_ERROR(__LINE__, displayCallsiteDetails);
  1.3666 +    }
  1.3667 +
  1.3668 +    return retval;
  1.3669 +}
  1.3670 +
  1.3671 +#if ST_WANT_GRAPHS
  1.3672 +/*
  1.3673 +** graphFootprint
  1.3674 +**
  1.3675 +** Output a PNG graph of the memory usage of the run.
  1.3676 +**
  1.3677 +** Draw the graph within these boundaries.
  1.3678 +**  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGHT-STGD_MARGIN
  1.3679 +**
  1.3680 +** Returns !0 on failure.
  1.3681 +*/
  1.3682 +int
  1.3683 +graphFootprint(STRequest * inRequest, STRun * aRun)
  1.3684 +{
  1.3685 +    int retval = 0;
  1.3686 +
  1.3687 +    if (NULL != aRun) {
  1.3688 +        uint32_t *YData = NULL;
  1.3689 +        uint32_t YDataArray[STGD_SPACE_X];
  1.3690 +        uint32_t traverse = 0;
  1.3691 +        uint32_t timeval = 0;
  1.3692 +        uint32_t loop = 0;
  1.3693 +        PRBool underLock = PR_FALSE;
  1.3694 +
  1.3695 +        /*
  1.3696 +         ** Decide if this is custom or we should use the cache.
  1.3697 +         */
  1.3698 +        if (aRun == inRequest->mContext->mSortedRun) {
  1.3699 +            YData = inRequest->mContext->mFootprintYData;
  1.3700 +            underLock = PR_TRUE;
  1.3701 +        }
  1.3702 +        else {
  1.3703 +            YData = YDataArray;
  1.3704 +        }
  1.3705 +
  1.3706 +        /*
  1.3707 +         **  Protect the shared data so that only one client has access to it
  1.3708 +         **      at any given time.
  1.3709 +         */
  1.3710 +        if (PR_FALSE != underLock) {
  1.3711 +            PR_Lock(inRequest->mContext->mImageLock);
  1.3712 +        }
  1.3713 +
  1.3714 +        /*
  1.3715 +         ** Only do the computations if we aren't cached already.
  1.3716 +         */
  1.3717 +        if (YData != inRequest->mContext->mFootprintYData
  1.3718 +            || PR_FALSE == inRequest->mContext->mFootprintCached) {
  1.3719 +            memset(YData, 0, sizeof(uint32_t) * STGD_SPACE_X);
  1.3720 +
  1.3721 +            /*
  1.3722 +             ** Initialize our Y data.
  1.3723 +             ** Pretty brutal loop here....
  1.3724 +             */
  1.3725 +            for (traverse = 0; 0 == retval && traverse < STGD_SPACE_X;
  1.3726 +                 traverse++) {
  1.3727 +                /*
  1.3728 +                 ** Compute what timeval this Y data lands in.
  1.3729 +                 */
  1.3730 +                timeval =
  1.3731 +                    ((traverse *
  1.3732 +                      (globals.mMaxTimeval -
  1.3733 +                       globals.mMinTimeval)) / STGD_SPACE_X) +
  1.3734 +                    globals.mMinTimeval;
  1.3735 +
  1.3736 +                /*
  1.3737 +                 ** Loop over the run.
  1.3738 +                 ** Should an allocation contain said Timeval, we're good.
  1.3739 +                 */
  1.3740 +                for (loop = 0; loop < aRun->mAllocationCount; loop++) {
  1.3741 +                    if (timeval >= aRun->mAllocations[loop]->mMinTimeval
  1.3742 +                        && timeval <= aRun->mAllocations[loop]->mMaxTimeval) {
  1.3743 +                        YData[traverse] +=
  1.3744 +                            byteSize(&inRequest->mOptions,
  1.3745 +                                     aRun->mAllocations[loop]);
  1.3746 +                    }
  1.3747 +                }
  1.3748 +            }
  1.3749 +
  1.3750 +            /*
  1.3751 +             ** Did we cache this?
  1.3752 +             */
  1.3753 +            if (YData == inRequest->mContext->mFootprintYData) {
  1.3754 +                inRequest->mContext->mFootprintCached = PR_TRUE;
  1.3755 +            }
  1.3756 +        }
  1.3757 +
  1.3758 +        /*
  1.3759 +         **  Done with the lock.
  1.3760 +         */
  1.3761 +        if (PR_FALSE != underLock) {
  1.3762 +            PR_Unlock(inRequest->mContext->mImageLock);
  1.3763 +        }
  1.3764 +
  1.3765 +        if (0 == retval) {
  1.3766 +            uint32_t minMemory = (uint32_t) - 1;
  1.3767 +            uint32_t maxMemory = 0;
  1.3768 +            int transparent = 0;
  1.3769 +            gdImagePtr graph = NULL;
  1.3770 +
  1.3771 +            /*
  1.3772 +             ** Go through and find the minimum and maximum sizes.
  1.3773 +             */
  1.3774 +            for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  1.3775 +                if (YData[traverse] < minMemory) {
  1.3776 +                    minMemory = YData[traverse];
  1.3777 +                }
  1.3778 +                if (YData[traverse] > maxMemory) {
  1.3779 +                    maxMemory = YData[traverse];
  1.3780 +                }
  1.3781 +            }
  1.3782 +
  1.3783 +            /*
  1.3784 +             ** We can now draw the graph.
  1.3785 +             */
  1.3786 +            graph = createGraph(&transparent);
  1.3787 +            if (NULL != graph) {
  1.3788 +                gdSink theSink;
  1.3789 +                int red = 0;
  1.3790 +                int x1 = 0;
  1.3791 +                int y1 = 0;
  1.3792 +                int x2 = 0;
  1.3793 +                int y2 = 0;
  1.3794 +                uint32_t percents[11] =
  1.3795 +                    { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
  1.3796 +                char *timevals[11];
  1.3797 +                char *bytes[11];
  1.3798 +                char timevalSpace[11][32];
  1.3799 +                char byteSpace[11][32];
  1.3800 +                int legendColors[1];
  1.3801 +                const char *legends[1] = { "Memory in Use" };
  1.3802 +                uint32_t cached = 0;
  1.3803 +
  1.3804 +                /*
  1.3805 +                 ** Figure out what the labels will say.
  1.3806 +                 */
  1.3807 +                for (traverse = 0; traverse < 11; traverse++) {
  1.3808 +                    timevals[traverse] = timevalSpace[traverse];
  1.3809 +                    bytes[traverse] = byteSpace[traverse];
  1.3810 +
  1.3811 +                    cached =
  1.3812 +                        ((globals.mMaxTimeval -
  1.3813 +                          globals.mMinTimeval) * percents[traverse]) / 100;
  1.3814 +                    PR_snprintf(timevals[traverse], 32, ST_TIMEVAL_FORMAT,
  1.3815 +                                ST_TIMEVAL_PRINTABLE(cached));
  1.3816 +                    PR_snprintf(bytes[traverse], 32, "%u",
  1.3817 +                                ((maxMemory -
  1.3818 +                                  minMemory) * percents[traverse]) / 100);
  1.3819 +                }
  1.3820 +
  1.3821 +                red = gdImageColorAllocate(graph, 255, 0, 0);
  1.3822 +                legendColors[0] = red;
  1.3823 +
  1.3824 +                drawGraph(graph, -1, "Memory Footprint Over Time", "Seconds",
  1.3825 +                          "Bytes", 11, percents, (const char **) timevals, 11,
  1.3826 +                          percents, (const char **) bytes, 1, legendColors,
  1.3827 +                          legends);
  1.3828 +
  1.3829 +                if (maxMemory != minMemory) {
  1.3830 +                    int64_t in64 = 0;
  1.3831 +                    int64_t ydata64 = 0;
  1.3832 +                    int64_t spacey64 = 0;
  1.3833 +                    int64_t mem64 = 0;
  1.3834 +                    int32_t in32 = 0;
  1.3835 +
  1.3836 +                    /*
  1.3837 +                     ** Go through our Y data and mark it up.
  1.3838 +                     */
  1.3839 +                    for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  1.3840 +                        x1 = traverse + STGD_MARGIN;
  1.3841 +                        y1 = STGD_HEIGHT - STGD_MARGIN;
  1.3842 +
  1.3843 +                        /*
  1.3844 +                         ** Need to do this math in 64 bits.
  1.3845 +                         */
  1.3846 +                        ydata64 = (int64_t)YData[traverse];
  1.3847 +                        spacey64 = (int64_t)STGD_SPACE_Y;
  1.3848 +                        mem64 = (int64_t)(maxMemory - minMemory);
  1.3849 +
  1.3850 +                        in64 = ydata64 * spacey64;
  1.3851 +                        in64 /= mem64;
  1.3852 +                        in32 = int32_t(in64);
  1.3853 +
  1.3854 +                        x2 = x1;
  1.3855 +                        y2 = y1 - in32;
  1.3856 +
  1.3857 +                        gdImageLine(graph, x1, y1, x2, y2, red);
  1.3858 +                    }
  1.3859 +                }
  1.3860 +
  1.3861 +
  1.3862 +                theSink.context = inRequest->mFD;
  1.3863 +                theSink.sink = pngSink;
  1.3864 +                gdImagePngToSink(graph, &theSink);
  1.3865 +
  1.3866 +                gdImageDestroy(graph);
  1.3867 +            }
  1.3868 +            else {
  1.3869 +                retval = __LINE__;
  1.3870 +                REPORT_ERROR(__LINE__, createGraph);
  1.3871 +            }
  1.3872 +        }
  1.3873 +    }
  1.3874 +    else {
  1.3875 +        retval = __LINE__;
  1.3876 +        REPORT_ERROR(__LINE__, graphFootprint);
  1.3877 +    }
  1.3878 +
  1.3879 +    return retval;
  1.3880 +}
  1.3881 +#endif /* ST_WANT_GRAPHS */
  1.3882 +
  1.3883 +#if ST_WANT_GRAPHS
  1.3884 +/*
  1.3885 +** graphTimeval
  1.3886 +**
  1.3887 +** Output a PNG graph of when the memory is allocated.
  1.3888 +**
  1.3889 +** Draw the graph within these boundaries.
  1.3890 +**  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGHT-STGD_MARGIN
  1.3891 +**
  1.3892 +** Returns !0 on failure.
  1.3893 +*/
  1.3894 +int
  1.3895 +graphTimeval(STRequest * inRequest, STRun * aRun)
  1.3896 +{
  1.3897 +    int retval = 0;
  1.3898 +
  1.3899 +    if (NULL != aRun) {
  1.3900 +        uint32_t *YData = NULL;
  1.3901 +        uint32_t YDataArray[STGD_SPACE_X];
  1.3902 +        uint32_t traverse = 0;
  1.3903 +        uint32_t timeval = globals.mMinTimeval;
  1.3904 +        uint32_t loop = 0;
  1.3905 +        PRBool underLock = PR_FALSE;
  1.3906 +
  1.3907 +        /*
  1.3908 +         ** Decide if this is custom or we should use the global cache.
  1.3909 +         */
  1.3910 +        if (aRun == inRequest->mContext->mSortedRun) {
  1.3911 +            YData = inRequest->mContext->mTimevalYData;
  1.3912 +            underLock = PR_TRUE;
  1.3913 +        }
  1.3914 +        else {
  1.3915 +            YData = YDataArray;
  1.3916 +        }
  1.3917 +
  1.3918 +        /*
  1.3919 +         **  Protect the shared data so that only one client has access to it
  1.3920 +         **      at any given time.
  1.3921 +         */
  1.3922 +        if (PR_FALSE != underLock) {
  1.3923 +            PR_Lock(inRequest->mContext->mImageLock);
  1.3924 +        }
  1.3925 +
  1.3926 +        /*
  1.3927 +         ** Only do the computations if we aren't cached already.
  1.3928 +         */
  1.3929 +        if (YData != inRequest->mContext->mTimevalYData
  1.3930 +            || PR_FALSE == inRequest->mContext->mTimevalCached) {
  1.3931 +            uint32_t prevTimeval = 0;
  1.3932 +
  1.3933 +            memset(YData, 0, sizeof(uint32_t) * STGD_SPACE_X);
  1.3934 +
  1.3935 +            /*
  1.3936 +             ** Initialize our Y data.
  1.3937 +             ** Pretty brutal loop here....
  1.3938 +             */
  1.3939 +            for (traverse = 0; 0 == retval && traverse < STGD_SPACE_X;
  1.3940 +                 traverse++) {
  1.3941 +                /*
  1.3942 +                 ** Compute what timeval this Y data lands in.
  1.3943 +                 */
  1.3944 +                prevTimeval = timeval;
  1.3945 +                timeval =
  1.3946 +                    ((traverse *
  1.3947 +                      (globals.mMaxTimeval -
  1.3948 +                       globals.mMinTimeval)) / STGD_SPACE_X) +
  1.3949 +                    globals.mMinTimeval;
  1.3950 +
  1.3951 +                /*
  1.3952 +                 ** Loop over the run.
  1.3953 +                 ** Should an allocation have been allocated between
  1.3954 +                 **  prevTimeval and timeval....
  1.3955 +                 */
  1.3956 +                for (loop = 0; loop < aRun->mAllocationCount; loop++) {
  1.3957 +                    if (prevTimeval < aRun->mAllocations[loop]->mMinTimeval
  1.3958 +                        && timeval >= aRun->mAllocations[loop]->mMinTimeval) {
  1.3959 +                        YData[traverse] +=
  1.3960 +                            byteSize(&inRequest->mOptions,
  1.3961 +                                     aRun->mAllocations[loop]);
  1.3962 +                    }
  1.3963 +                }
  1.3964 +            }
  1.3965 +
  1.3966 +            /*
  1.3967 +             ** Did we cache this?
  1.3968 +             */
  1.3969 +            if (YData == inRequest->mContext->mTimevalYData) {
  1.3970 +                inRequest->mContext->mTimevalCached = PR_TRUE;
  1.3971 +            }
  1.3972 +        }
  1.3973 +
  1.3974 +        /*
  1.3975 +         **  Done with the lock.
  1.3976 +         */
  1.3977 +        if (PR_FALSE != underLock) {
  1.3978 +            PR_Unlock(inRequest->mContext->mImageLock);
  1.3979 +        }
  1.3980 +
  1.3981 +        if (0 == retval) {
  1.3982 +            uint32_t minMemory = (uint32_t) - 1;
  1.3983 +            uint32_t maxMemory = 0;
  1.3984 +            int transparent = 0;
  1.3985 +            gdImagePtr graph = NULL;
  1.3986 +
  1.3987 +            /*
  1.3988 +             ** Go through and find the minimum and maximum sizes.
  1.3989 +             */
  1.3990 +            for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  1.3991 +                if (YData[traverse] < minMemory) {
  1.3992 +                    minMemory = YData[traverse];
  1.3993 +                }
  1.3994 +                if (YData[traverse] > maxMemory) {
  1.3995 +                    maxMemory = YData[traverse];
  1.3996 +                }
  1.3997 +            }
  1.3998 +
  1.3999 +            /*
  1.4000 +             ** We can now draw the graph.
  1.4001 +             */
  1.4002 +            graph = createGraph(&transparent);
  1.4003 +            if (NULL != graph) {
  1.4004 +                gdSink theSink;
  1.4005 +                int red = 0;
  1.4006 +                int x1 = 0;
  1.4007 +                int y1 = 0;
  1.4008 +                int x2 = 0;
  1.4009 +                int y2 = 0;
  1.4010 +                uint32_t percents[11] =
  1.4011 +                    { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
  1.4012 +                char *timevals[11];
  1.4013 +                char *bytes[11];
  1.4014 +                char timevalSpace[11][32];
  1.4015 +                char byteSpace[11][32];
  1.4016 +                int legendColors[1];
  1.4017 +                const char *legends[1] = { "Memory Allocated" };
  1.4018 +                uint32_t cached = 0;
  1.4019 +
  1.4020 +                /*
  1.4021 +                 ** Figure out what the labels will say.
  1.4022 +                 */
  1.4023 +                for (traverse = 0; traverse < 11; traverse++) {
  1.4024 +                    timevals[traverse] = timevalSpace[traverse];
  1.4025 +                    bytes[traverse] = byteSpace[traverse];
  1.4026 +
  1.4027 +                    cached =
  1.4028 +                        ((globals.mMaxTimeval -
  1.4029 +                          globals.mMinTimeval) * percents[traverse]) / 100;
  1.4030 +                    PR_snprintf(timevals[traverse], 32, ST_TIMEVAL_FORMAT,
  1.4031 +                                ST_TIMEVAL_PRINTABLE(cached));
  1.4032 +                    PR_snprintf(bytes[traverse], 32, "%u",
  1.4033 +                                ((maxMemory -
  1.4034 +                                  minMemory) * percents[traverse]) / 100);
  1.4035 +                }
  1.4036 +
  1.4037 +                red = gdImageColorAllocate(graph, 255, 0, 0);
  1.4038 +                legendColors[0] = red;
  1.4039 +
  1.4040 +                drawGraph(graph, -1, "Allocation Times", "Seconds", "Bytes",
  1.4041 +                          11, percents, (const char **) timevals, 11,
  1.4042 +                          percents, (const char **) bytes, 1, legendColors,
  1.4043 +                          legends);
  1.4044 +
  1.4045 +                if (maxMemory != minMemory) {
  1.4046 +                    int64_t in64 = 0;
  1.4047 +                    int64_t ydata64 = 0;
  1.4048 +                    int64_t spacey64 = 0;
  1.4049 +                    int64_t mem64 = 0;
  1.4050 +                    int32_t in32 = 0;
  1.4051 +
  1.4052 +                    /*
  1.4053 +                     ** Go through our Y data and mark it up.
  1.4054 +                     */
  1.4055 +                    for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  1.4056 +                        x1 = traverse + STGD_MARGIN;
  1.4057 +                        y1 = STGD_HEIGHT - STGD_MARGIN;
  1.4058 +
  1.4059 +                        /*
  1.4060 +                         ** Need to do this math in 64 bits.
  1.4061 +                         */
  1.4062 +                        ydata64 = (int64_t)YData[traverse];
  1.4063 +                        spacey64 = (int64_t)STGD_SPACE_Y;
  1.4064 +                        mem64 = (int64_t)(maxMemory - minMemory);
  1.4065 +
  1.4066 +                        in64 = ydata64 * spacey64;
  1.4067 +                        in64 /= mem64;
  1.4068 +                        in32 = int32_t(in64);
  1.4069 +
  1.4070 +                        x2 = x1;
  1.4071 +                        y2 = y1 - in32;
  1.4072 +
  1.4073 +                        gdImageLine(graph, x1, y1, x2, y2, red);
  1.4074 +                    }
  1.4075 +                }
  1.4076 +
  1.4077 +
  1.4078 +                theSink.context = inRequest->mFD;
  1.4079 +                theSink.sink = pngSink;
  1.4080 +                gdImagePngToSink(graph, &theSink);
  1.4081 +
  1.4082 +                gdImageDestroy(graph);
  1.4083 +            }
  1.4084 +            else {
  1.4085 +                retval = __LINE__;
  1.4086 +                REPORT_ERROR(__LINE__, createGraph);
  1.4087 +            }
  1.4088 +        }
  1.4089 +    }
  1.4090 +    else {
  1.4091 +        retval = __LINE__;
  1.4092 +        REPORT_ERROR(__LINE__, graphTimeval);
  1.4093 +    }
  1.4094 +
  1.4095 +    return retval;
  1.4096 +}
  1.4097 +#endif /* ST_WANT_GRAPHS */
  1.4098 +
  1.4099 +#if ST_WANT_GRAPHS
  1.4100 +/*
  1.4101 +** graphLifespan
  1.4102 +**
  1.4103 +** Output a PNG graph of how long memory lived.
  1.4104 +**
  1.4105 +** Draw the graph within these boundaries.
  1.4106 +**  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGHT-STGD_MARGIN
  1.4107 +**
  1.4108 +** Returns !0 on failure.
  1.4109 +*/
  1.4110 +int
  1.4111 +graphLifespan(STRequest * inRequest, STRun * aRun)
  1.4112 +{
  1.4113 +    int retval = 0;
  1.4114 +
  1.4115 +    if (NULL != aRun) {
  1.4116 +        uint32_t *YData = NULL;
  1.4117 +        uint32_t YDataArray[STGD_SPACE_X];
  1.4118 +        uint32_t traverse = 0;
  1.4119 +        uint32_t timeval = 0;
  1.4120 +        uint32_t loop = 0;
  1.4121 +        PRBool underLock = PR_FALSE;
  1.4122 +
  1.4123 +        /*
  1.4124 +         ** Decide if this is custom or we should use the global cache.
  1.4125 +         */
  1.4126 +        if (aRun == inRequest->mContext->mSortedRun) {
  1.4127 +            YData = inRequest->mContext->mLifespanYData;
  1.4128 +            underLock = PR_TRUE;
  1.4129 +        }
  1.4130 +        else {
  1.4131 +            YData = YDataArray;
  1.4132 +        }
  1.4133 +
  1.4134 +        /*
  1.4135 +         **  Protect the shared data so that only one client has access to it
  1.4136 +         **      at any given time.
  1.4137 +         */
  1.4138 +        if (PR_FALSE != underLock) {
  1.4139 +            PR_Lock(inRequest->mContext->mImageLock);
  1.4140 +        }
  1.4141 +
  1.4142 +        /*
  1.4143 +         ** Only do the computations if we aren't cached already.
  1.4144 +         */
  1.4145 +        if (YData != inRequest->mContext->mLifespanYData
  1.4146 +            || PR_FALSE == inRequest->mContext->mLifespanCached) {
  1.4147 +            uint32_t prevTimeval = 0;
  1.4148 +            uint32_t lifespan = 0;
  1.4149 +
  1.4150 +            memset(YData, 0, sizeof(uint32_t) * STGD_SPACE_X);
  1.4151 +
  1.4152 +            /*
  1.4153 +             ** Initialize our Y data.
  1.4154 +             ** Pretty brutal loop here....
  1.4155 +             */
  1.4156 +            for (traverse = 0; 0 == retval && traverse < STGD_SPACE_X;
  1.4157 +                 traverse++) {
  1.4158 +                /*
  1.4159 +                 ** Compute what timeval this Y data lands in.
  1.4160 +                 */
  1.4161 +                prevTimeval = timeval;
  1.4162 +                timeval =
  1.4163 +                    (traverse * (globals.mMaxTimeval - globals.mMinTimeval)) /
  1.4164 +                    STGD_SPACE_X;
  1.4165 +
  1.4166 +                /*
  1.4167 +                 ** Loop over the run.
  1.4168 +                 ** Should an allocation have lived between
  1.4169 +                 **  prevTimeval and timeval....
  1.4170 +                 */
  1.4171 +                for (loop = 0; loop < aRun->mAllocationCount; loop++) {
  1.4172 +                    lifespan =
  1.4173 +                        aRun->mAllocations[loop]->mMaxTimeval -
  1.4174 +                        aRun->mAllocations[loop]->mMinTimeval;
  1.4175 +
  1.4176 +                    if (prevTimeval < lifespan && timeval >= lifespan) {
  1.4177 +                        YData[traverse] +=
  1.4178 +                            byteSize(&inRequest->mOptions,
  1.4179 +                                     aRun->mAllocations[loop]);
  1.4180 +                    }
  1.4181 +                }
  1.4182 +            }
  1.4183 +
  1.4184 +            /*
  1.4185 +             ** Did we cache this?
  1.4186 +             */
  1.4187 +            if (YData == inRequest->mContext->mLifespanYData) {
  1.4188 +                inRequest->mContext->mLifespanCached = PR_TRUE;
  1.4189 +            }
  1.4190 +        }
  1.4191 +
  1.4192 +        /*
  1.4193 +         **  Done with the lock.
  1.4194 +         */
  1.4195 +        if (PR_FALSE != underLock) {
  1.4196 +            PR_Unlock(inRequest->mContext->mImageLock);
  1.4197 +        }
  1.4198 +
  1.4199 +        if (0 == retval) {
  1.4200 +            uint32_t minMemory = (uint32_t) - 1;
  1.4201 +            uint32_t maxMemory = 0;
  1.4202 +            int transparent = 0;
  1.4203 +            gdImagePtr graph = NULL;
  1.4204 +
  1.4205 +            /*
  1.4206 +             ** Go through and find the minimum and maximum sizes.
  1.4207 +             */
  1.4208 +            for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  1.4209 +                if (YData[traverse] < minMemory) {
  1.4210 +                    minMemory = YData[traverse];
  1.4211 +                }
  1.4212 +                if (YData[traverse] > maxMemory) {
  1.4213 +                    maxMemory = YData[traverse];
  1.4214 +                }
  1.4215 +            }
  1.4216 +
  1.4217 +            /*
  1.4218 +             ** We can now draw the graph.
  1.4219 +             */
  1.4220 +            graph = createGraph(&transparent);
  1.4221 +            if (NULL != graph) {
  1.4222 +                gdSink theSink;
  1.4223 +                int red = 0;
  1.4224 +                int x1 = 0;
  1.4225 +                int y1 = 0;
  1.4226 +                int x2 = 0;
  1.4227 +                int y2 = 0;
  1.4228 +                uint32_t percents[11] =
  1.4229 +                    { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
  1.4230 +                char *timevals[11];
  1.4231 +                char *bytes[11];
  1.4232 +                char timevalSpace[11][32];
  1.4233 +                char byteSpace[11][32];
  1.4234 +                int legendColors[1];
  1.4235 +                const char *legends[1] = { "Live Memory" };
  1.4236 +                uint32_t cached = 0;
  1.4237 +
  1.4238 +                /*
  1.4239 +                 ** Figure out what the labels will say.
  1.4240 +                 */
  1.4241 +                for (traverse = 0; traverse < 11; traverse++) {
  1.4242 +                    timevals[traverse] = timevalSpace[traverse];
  1.4243 +                    bytes[traverse] = byteSpace[traverse];
  1.4244 +
  1.4245 +                    cached =
  1.4246 +                        ((globals.mMaxTimeval -
  1.4247 +                          globals.mMinTimeval) * percents[traverse]) / 100;
  1.4248 +                    PR_snprintf(timevals[traverse], 32, ST_TIMEVAL_FORMAT,
  1.4249 +                                ST_TIMEVAL_PRINTABLE(cached));
  1.4250 +                    PR_snprintf(bytes[traverse], 32, "%u",
  1.4251 +                                ((maxMemory -
  1.4252 +                                  minMemory) * percents[traverse]) / 100);
  1.4253 +                }
  1.4254 +
  1.4255 +                red = gdImageColorAllocate(graph, 255, 0, 0);
  1.4256 +                legendColors[0] = red;
  1.4257 +
  1.4258 +                drawGraph(graph, -1, "Allocation Lifespans", "Lifespan",
  1.4259 +                          "Bytes", 11, percents, (const char **) timevals, 11,
  1.4260 +                          percents, (const char **) bytes, 1, legendColors,
  1.4261 +                          legends);
  1.4262 +
  1.4263 +                if (maxMemory != minMemory) {
  1.4264 +                    int64_t in64 = 0;
  1.4265 +                    int64_t ydata64 = 0;
  1.4266 +                    int64_t spacey64 = 0;
  1.4267 +                    int64_t mem64 = 0;
  1.4268 +                    int32_t in32 = 0;
  1.4269 +
  1.4270 +                    /*
  1.4271 +                     ** Go through our Y data and mark it up.
  1.4272 +                     */
  1.4273 +                    for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  1.4274 +                        x1 = traverse + STGD_MARGIN;
  1.4275 +                        y1 = STGD_HEIGHT - STGD_MARGIN;
  1.4276 +
  1.4277 +                        /*
  1.4278 +                         ** Need to do this math in 64 bits.
  1.4279 +                         */
  1.4280 +                        ydata64 = (int64_t)YData[traverse];
  1.4281 +                        spacey64 = (int64_t)STGD_SPACE_Y;
  1.4282 +                        mem64 = (int64_t)(maxMemory - minMemory);
  1.4283 +
  1.4284 +                        in64 = ydata64 * spacey64;
  1.4285 +                        in64 /= mem64;
  1.4286 +                        in32 = int32_t(in64);
  1.4287 +
  1.4288 +                        x2 = x1;
  1.4289 +                        y2 = y1 - in32;
  1.4290 +
  1.4291 +                        gdImageLine(graph, x1, y1, x2, y2, red);
  1.4292 +                    }
  1.4293 +                }
  1.4294 +
  1.4295 +
  1.4296 +                theSink.context = inRequest->mFD;
  1.4297 +                theSink.sink = pngSink;
  1.4298 +                gdImagePngToSink(graph, &theSink);
  1.4299 +
  1.4300 +                gdImageDestroy(graph);
  1.4301 +            }
  1.4302 +            else {
  1.4303 +                retval = __LINE__;
  1.4304 +                REPORT_ERROR(__LINE__, createGraph);
  1.4305 +            }
  1.4306 +        }
  1.4307 +    }
  1.4308 +    else {
  1.4309 +        retval = __LINE__;
  1.4310 +        REPORT_ERROR(__LINE__, graphLifespan);
  1.4311 +    }
  1.4312 +
  1.4313 +    return retval;
  1.4314 +}
  1.4315 +#endif /* ST_WANT_GRAPHS */
  1.4316 +
  1.4317 +#if ST_WANT_GRAPHS
  1.4318 +/*
  1.4319 +** graphWeight
  1.4320 +**
  1.4321 +** Output a PNG graph of Allocations by Weight
  1.4322 +**
  1.4323 +** Draw the graph within these boundaries.
  1.4324 +**  STGD_MARGIN,STGD_MARGIN,STGD_WIDTH-STGD_MARGIN,STGD_HEIGHT-STGD_MARGIN
  1.4325 +**
  1.4326 +** Returns !0 on failure.
  1.4327 +*/
  1.4328 +int
  1.4329 +graphWeight(STRequest * inRequest, STRun * aRun)
  1.4330 +{
  1.4331 +    int retval = 0;
  1.4332 +
  1.4333 +    if (NULL != aRun) {
  1.4334 +        uint64_t *YData64 = NULL;
  1.4335 +        uint64_t YDataArray64[STGD_SPACE_X];
  1.4336 +        uint32_t traverse = 0;
  1.4337 +        uint32_t timeval = globals.mMinTimeval;
  1.4338 +        uint32_t loop = 0;
  1.4339 +        PRBool underLock = PR_FALSE;
  1.4340 +
  1.4341 +        /*
  1.4342 +         ** Decide if this is custom or we should use the global cache.
  1.4343 +         */
  1.4344 +        if (aRun == inRequest->mContext->mSortedRun) {
  1.4345 +            YData64 = inRequest->mContext->mWeightYData64;
  1.4346 +            underLock = PR_TRUE;
  1.4347 +        }
  1.4348 +        else {
  1.4349 +            YData64 = YDataArray64;
  1.4350 +        }
  1.4351 +
  1.4352 +        /*
  1.4353 +         **  Protect the shared data so that only one client has access to it
  1.4354 +         **      at any given time.
  1.4355 +         */
  1.4356 +        if (PR_FALSE != underLock) {
  1.4357 +            PR_Lock(inRequest->mContext->mImageLock);
  1.4358 +        }
  1.4359 +
  1.4360 +        /*
  1.4361 +         ** Only do the computations if we aren't cached already.
  1.4362 +         */
  1.4363 +        if (YData64 != inRequest->mContext->mWeightYData64
  1.4364 +            || PR_FALSE == inRequest->mContext->mWeightCached) {
  1.4365 +            uint32_t prevTimeval = 0;
  1.4366 +
  1.4367 +            memset(YData64, 0, sizeof(uint64_t) * STGD_SPACE_X);
  1.4368 +
  1.4369 +            /*
  1.4370 +             ** Initialize our Y data.
  1.4371 +             ** Pretty brutal loop here....
  1.4372 +             */
  1.4373 +            for (traverse = 0; 0 == retval && traverse < STGD_SPACE_X;
  1.4374 +                 traverse++) {
  1.4375 +                /*
  1.4376 +                 ** Compute what timeval this Y data lands in.
  1.4377 +                 */
  1.4378 +                prevTimeval = timeval;
  1.4379 +                timeval =
  1.4380 +                    ((traverse *
  1.4381 +                      (globals.mMaxTimeval -
  1.4382 +                       globals.mMinTimeval)) / STGD_SPACE_X) +
  1.4383 +                    globals.mMinTimeval;
  1.4384 +
  1.4385 +                /*
  1.4386 +                 ** Loop over the run.
  1.4387 +                 ** Should an allocation have been allocated between
  1.4388 +                 **  prevTimeval and timeval....
  1.4389 +                 */
  1.4390 +                for (loop = 0; loop < aRun->mAllocationCount; loop++) {
  1.4391 +                    if (prevTimeval < aRun->mAllocations[loop]->mMinTimeval
  1.4392 +                        && timeval >= aRun->mAllocations[loop]->mMinTimeval) {
  1.4393 +                        uint64_t size64 = 0;
  1.4394 +                        uint64_t lifespan64 = 0;
  1.4395 +                        uint64_t weight64 = 0;
  1.4396 +
  1.4397 +                        size64 = byteSize(&inRequest->mOptions,
  1.4398 +                                          aRun->mAllocations[loop]);
  1.4399 +                        lifespan64 = aRun->mAllocations[loop]->mMaxTimeval -
  1.4400 +                                     aRun->mAllocations[loop]->mMinTimeval;
  1.4401 +                        weight64 = size64 * lifespan64;
  1.4402 +
  1.4403 +                        YData64[traverse] += weight64;
  1.4404 +                    }
  1.4405 +                }
  1.4406 +            }
  1.4407 +
  1.4408 +            /*
  1.4409 +             ** Did we cache this?
  1.4410 +             */
  1.4411 +            if (YData64 == inRequest->mContext->mWeightYData64) {
  1.4412 +                inRequest->mContext->mWeightCached = PR_TRUE;
  1.4413 +            }
  1.4414 +        }
  1.4415 +
  1.4416 +        /*
  1.4417 +         **  Done with the lock.
  1.4418 +         */
  1.4419 +        if (PR_FALSE != underLock) {
  1.4420 +            PR_Unlock(inRequest->mContext->mImageLock);
  1.4421 +        }
  1.4422 +
  1.4423 +        if (0 == retval) {
  1.4424 +            uint64_t minWeight64 = (0xFFFFFFFFLL << 32) + 0xFFFFFFFFLL;
  1.4425 +            uint64_t maxWeight64 = 0;
  1.4426 +            int transparent = 0;
  1.4427 +            gdImagePtr graph = NULL;
  1.4428 +
  1.4429 +            /*
  1.4430 +             ** Go through and find the minimum and maximum weights.
  1.4431 +             */
  1.4432 +            for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  1.4433 +                if (YData64[traverse] < minWeight64) {
  1.4434 +                    minWeight64 = YData64[traverse];
  1.4435 +                }
  1.4436 +                if (YData64[traverse] > maxWeight64) {
  1.4437 +                    maxWeight64 = YData64[traverse];
  1.4438 +                }
  1.4439 +            }
  1.4440 +
  1.4441 +            /*
  1.4442 +             ** We can now draw the graph.
  1.4443 +             */
  1.4444 +            graph = createGraph(&transparent);
  1.4445 +            if (NULL != graph) {
  1.4446 +                gdSink theSink;
  1.4447 +                int red = 0;
  1.4448 +                int x1 = 0;
  1.4449 +                int y1 = 0;
  1.4450 +                int x2 = 0;
  1.4451 +                int y2 = 0;
  1.4452 +                uint32_t percents[11] =
  1.4453 +                    { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
  1.4454 +                char *timevals[11];
  1.4455 +                char *bytes[11];
  1.4456 +                char timevalSpace[11][32];
  1.4457 +                char byteSpace[11][32];
  1.4458 +                int legendColors[1];
  1.4459 +                const char *legends[1] = { "Memory Weight" };
  1.4460 +                uint64_t percent64 = 0;
  1.4461 +                uint64_t result64 = 0;
  1.4462 +
  1.4463 +                uint32_t cached = 0;
  1.4464 +                uint64_t hundred64 = 100;
  1.4465 +
  1.4466 +                /*
  1.4467 +                 ** Figure out what the labels will say.
  1.4468 +                 */
  1.4469 +                for (traverse = 0; traverse < 11; traverse++) {
  1.4470 +                    timevals[traverse] = timevalSpace[traverse];
  1.4471 +                    bytes[traverse] = byteSpace[traverse];
  1.4472 +
  1.4473 +                    cached =
  1.4474 +                        ((globals.mMaxTimeval -
  1.4475 +                          globals.mMinTimeval) * percents[traverse]) / 100;
  1.4476 +                    PR_snprintf(timevals[traverse], 32, ST_TIMEVAL_FORMAT,
  1.4477 +                                ST_TIMEVAL_PRINTABLE(cached));
  1.4478 +
  1.4479 +                    result64 = (maxWeight64 - minWeight64) * percents[traverse];
  1.4480 +                    result64 /= hundred64;
  1.4481 +                    PR_snprintf(bytes[traverse], 32, "%llu", result64);
  1.4482 +                }
  1.4483 +
  1.4484 +                red = gdImageColorAllocate(graph, 255, 0, 0);
  1.4485 +                legendColors[0] = red;
  1.4486 +
  1.4487 +                drawGraph(graph, -1, "Allocation Weights", "Seconds",
  1.4488 +                          "Weight", 11, percents, (const char **) timevals,
  1.4489 +                          11, percents, (const char **) bytes, 1,
  1.4490 +                          legendColors, legends);
  1.4491 +
  1.4492 +                if (maxWeight64 != minWeight64) {
  1.4493 +                    int64_t in64 = 0;
  1.4494 +                    int64_t spacey64 = 0;
  1.4495 +                    int64_t weight64 = 0;
  1.4496 +                    int32_t in32 = 0;
  1.4497 +
  1.4498 +                    /*
  1.4499 +                     ** Go through our Y data and mark it up.
  1.4500 +                     */
  1.4501 +                    for (traverse = 0; traverse < STGD_SPACE_X; traverse++) {
  1.4502 +                        x1 = traverse + STGD_MARGIN;
  1.4503 +                        y1 = STGD_HEIGHT - STGD_MARGIN;
  1.4504 +
  1.4505 +                        /*
  1.4506 +                         ** Need to do this math in 64 bits.
  1.4507 +                         */
  1.4508 +                        spacey64 = (int64_t)STGD_SPACE_Y;
  1.4509 +                        weight64 = maxWeight64 - minWeight64;
  1.4510 +
  1.4511 +                        in64 = YData64[traverse] * spacey64;
  1.4512 +                        in64 /= weight64;
  1.4513 +                        in32 = int32_t(in64);
  1.4514 +
  1.4515 +                        x2 = x1;
  1.4516 +                        y2 = y1 - in32;
  1.4517 +
  1.4518 +                        gdImageLine(graph, x1, y1, x2, y2, red);
  1.4519 +                    }
  1.4520 +                }
  1.4521 +
  1.4522 +
  1.4523 +                theSink.context = inRequest->mFD;
  1.4524 +                theSink.sink = pngSink;
  1.4525 +                gdImagePngToSink(graph, &theSink);
  1.4526 +
  1.4527 +                gdImageDestroy(graph);
  1.4528 +            }
  1.4529 +            else {
  1.4530 +                retval = __LINE__;
  1.4531 +                REPORT_ERROR(__LINE__, createGraph);
  1.4532 +            }
  1.4533 +        }
  1.4534 +    }
  1.4535 +    else {
  1.4536 +        retval = __LINE__;
  1.4537 +        REPORT_ERROR(__LINE__, graphWeight);
  1.4538 +    }
  1.4539 +
  1.4540 +    return retval;
  1.4541 +}
  1.4542 +#endif /* ST_WANT_GRAPHS */
  1.4543 +
  1.4544 +#define ST_WEB_OPTION_BOOL(option_name, option_genre, option_help) \
  1.4545 +    { \
  1.4546 +        uint32_t convert = (uint32_t)outOptions->m##option_name; \
  1.4547 +        \
  1.4548 +        getDataPRUint32(inFormData, #option_name, 1, &convert, 1); \
  1.4549 +        outOptions->m##option_name = (PRBool)convert; \
  1.4550 +    }
  1.4551 +#define ST_WEB_OPTION_STRING(option_name, option_genre, default_value, option_help) \
  1.4552 +    getDataString(inFormData, #option_name, 1, outOptions->m##option_name, sizeof(outOptions->m##option_name));
  1.4553 +#define ST_WEB_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
  1.4554 +    { \
  1.4555 +        uint32_t loop = 0; \
  1.4556 +        uint32_t found = 0; \
  1.4557 +        char buffer[ST_OPTION_STRING_MAX]; \
  1.4558 +        \
  1.4559 +        for(loop = 0; loop < array_size; loop++) \
  1.4560 +        { \
  1.4561 +            buffer[0] = '\0'; \
  1.4562 +            getDataString(inFormData, #option_name, (loop + 1), buffer, sizeof(buffer)); \
  1.4563 +            \
  1.4564 +            if('\0' != buffer[0]) \
  1.4565 +            { \
  1.4566 +                PR_snprintf(outOptions->m##option_name[found], sizeof(outOptions->m##option_name[found]), "%s", buffer); \
  1.4567 +                found++; \
  1.4568 +            } \
  1.4569 +        } \
  1.4570 +        \
  1.4571 +        for(; found < array_size; found++) \
  1.4572 +        { \
  1.4573 +            outOptions->m##option_name[found][0] = '\0'; \
  1.4574 +        } \
  1.4575 +    }
  1.4576 +#define ST_WEB_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help)  /* no implementation */
  1.4577 +#define ST_WEB_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
  1.4578 +    getDataPRUint32(inFormData, #option_name, 1, &outOptions->m##option_name, multiplier);
  1.4579 +#define ST_WEB_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
  1.4580 +    { \
  1.4581 +        uint64_t mul64 = multiplier; \
  1.4582 +        \
  1.4583 +        getDataPRUint64(inFormData, #option_name, 1, &outOptions->m##option_name##64, mul64); \
  1.4584 +    }
  1.4585 +/*
  1.4586 +**  fillOptions
  1.4587 +**
  1.4588 +**  Given an appropriate hexcaped string, distill the option values
  1.4589 +**      and fill the given STOption struct.
  1.4590 +**
  1.4591 +**  Note that the options passed in are not touched UNLESS there is
  1.4592 +**      a replacement found in the form data.
  1.4593 +*/
  1.4594 +void
  1.4595 +fillOptions(STOptions * outOptions, const FormData * inFormData)
  1.4596 +{
  1.4597 +    if (NULL != outOptions && NULL != inFormData) {
  1.4598 +
  1.4599 +#include "stoptions.h"
  1.4600 +
  1.4601 +        /*
  1.4602 +         **  Special sanity check here for some options that need data validation.
  1.4603 +         */
  1.4604 +        if (!outOptions->mCategoryName[0]
  1.4605 +            || !findCategoryNode(outOptions->mCategoryName, &globals)) {
  1.4606 +            PR_snprintf(outOptions->mCategoryName,
  1.4607 +                        sizeof(outOptions->mCategoryName), "%s",
  1.4608 +                        ST_ROOT_CATEGORY_NAME);
  1.4609 +        }
  1.4610 +    }
  1.4611 +}
  1.4612 +
  1.4613 +
  1.4614 +void
  1.4615 +displayOptionString(STRequest * inRequest,
  1.4616 +                    const char *option_name,
  1.4617 +                    const char *option_genre,
  1.4618 +                    const char *default_value,
  1.4619 +                    const char *option_help, const char *value)
  1.4620 +{
  1.4621 +#if 0
  1.4622 +    PR_fprintf(inRequest->mFD, "<input type=submit value=%s>\n", option_name);
  1.4623 +#endif
  1.4624 +    PR_fprintf(inRequest->mFD, "<div class=option-box>\n");
  1.4625 +    PR_fprintf(inRequest->mFD, "<p class=option-name>%s</p>\n", option_name);
  1.4626 +    PR_fprintf(inRequest->mFD,
  1.4627 +               "<input type=text name=\"%s\" value=\"%s\">\n",
  1.4628 +               option_name, value);
  1.4629 +    PR_fprintf(inRequest->mFD,
  1.4630 +               "<p class=option-default>Default value is \"%s\".</p>\n<p class=option-help>%s</p>\n",
  1.4631 +               default_value, option_help);
  1.4632 +    PR_fprintf(inRequest->mFD, "</div>\n");
  1.4633 +}
  1.4634 +
  1.4635 +static void
  1.4636 +displayOptionStringArray(STRequest * inRequest,
  1.4637 +                         const char *option_name,
  1.4638 +                         const char *option_genre,
  1.4639 +                         uint32_t array_size,
  1.4640 +                         const char *option_help, const char values[5]
  1.4641 +                         [ST_OPTION_STRING_MAX])
  1.4642 +{
  1.4643 +    /* values should not be a fixed length! */
  1.4644 +    PR_ASSERT(array_size == 5);
  1.4645 +#if 0
  1.4646 +    PR_fprintf(inRequest->mFD, "<input type=submit value=%s>\n", option_name);
  1.4647 +#endif
  1.4648 +    PR_fprintf(inRequest->mFD, "<div class=\"option-box\">\n");
  1.4649 +    PR_fprintf(inRequest->mFD, "<p class=option-name>%s</p>\n", option_name); {
  1.4650 +        uint32_t loop = 0;
  1.4651 +
  1.4652 +        for (loop = 0; loop < array_size; loop++) {
  1.4653 +            PR_fprintf(inRequest->mFD,
  1.4654 +                       "<input type=text name=\"%s\" value=\"%s\"><br>\n",
  1.4655 +                       option_name, values[loop]);
  1.4656 +        }
  1.4657 +    }
  1.4658 +    PR_fprintf(inRequest->mFD,
  1.4659 +               "<p class=option-default>Up to %u occurrences allowed.</p>\n<p class=option-help>%s</p>\n",
  1.4660 +               array_size, option_help);
  1.4661 +    PR_fprintf(inRequest->mFD, "</div>\n");
  1.4662 +}
  1.4663 +
  1.4664 +static void
  1.4665 +displayOptionInt(STRequest * inRequest,
  1.4666 +                 const char *option_name,
  1.4667 +                 const char *option_genre,
  1.4668 +                 uint32_t default_value,
  1.4669 +                 uint32_t multiplier, const char *option_help, uint32_t value)
  1.4670 +{
  1.4671 +#if 0
  1.4672 +    PR_fprintf(inRequest->mFD, "<input type=submit value=%s>\n", option_name);
  1.4673 +#endif
  1.4674 +    PR_fprintf(inRequest->mFD, "<div class=\"option-box\">\n");
  1.4675 +    PR_fprintf(inRequest->mFD, "<p class=option-name>%s</p>\n", option_name);
  1.4676 +    PR_fprintf(inRequest->mFD,
  1.4677 +               "<input type=text name=%s value=%u>\n", option_name,
  1.4678 +               value / multiplier);
  1.4679 +    PR_fprintf(inRequest->mFD,
  1.4680 +               "<p class=option-default>Default value is %u.</p>\n<p class=option-help>%s</p>\n",
  1.4681 +               default_value, option_help);
  1.4682 +    PR_fprintf(inRequest->mFD, "</div>\n");
  1.4683 +}
  1.4684 +
  1.4685 +static void
  1.4686 +displayOptionInt64(STRequest * inRequest,
  1.4687 +                   const char *option_name,
  1.4688 +                   const char *option_genre,
  1.4689 +                   uint64_t default_value,
  1.4690 +                   uint64_t multiplier,
  1.4691 +                   const char *option_help, uint64_t value)
  1.4692 +{
  1.4693 +#if 0
  1.4694 +    PR_fprintf(inRequest->mFD, "<input type=submit value=%s>\n", option_name);
  1.4695 +#endif
  1.4696 +    PR_fprintf(inRequest->mFD, "<div class=\"option-box\">\n");
  1.4697 +    PR_fprintf(inRequest->mFD, "<p class=option-name>%s</p>\n", option_name); {
  1.4698 +        uint64_t def64 = default_value;
  1.4699 +        uint64_t mul64 = multiplier;
  1.4700 +        uint64_t div64;
  1.4701 +
  1.4702 +        div64 = value / mul64;
  1.4703 +        PR_fprintf(inRequest->mFD,
  1.4704 +                   "<input type=text name=%s value=%llu>\n",
  1.4705 +                   option_name, div64);
  1.4706 +        PR_fprintf(inRequest->mFD,
  1.4707 +                   "<p class=option-default>Default value is %llu.</p>\n<p class=option-help>%s</p>\n",
  1.4708 +                   def64, option_help);
  1.4709 +    }
  1.4710 +    PR_fprintf(inRequest->mFD, "</div>\n");
  1.4711 +}
  1.4712 +
  1.4713 +/*
  1.4714 +**  displaySettings
  1.4715 +**
  1.4716 +**  Present the settings for change during execution.
  1.4717 +*/
  1.4718 +void
  1.4719 +displaySettings(STRequest * inRequest)
  1.4720 +{
  1.4721 +    int applyRes = 0;
  1.4722 +
  1.4723 +    /*
  1.4724 +     ** We've got a form to create.
  1.4725 +     */
  1.4726 +    PR_fprintf(inRequest->mFD, "<form method=get action=\"./index.html\">\n");
  1.4727 +    /*
  1.4728 +     **  Respect newlines in help text.
  1.4729 +     */
  1.4730 +#if 0
  1.4731 +    PR_fprintf(inRequest->mFD, "<pre>\n");
  1.4732 +#endif
  1.4733 +#define ST_WEB_OPTION_BOOL(option_name, option_genre, option_help) \
  1.4734 +    displayOptionBool(option_name, option_genre, option_help)
  1.4735 +#define ST_WEB_OPTION_STRING(option_name, option_genre, default_value, option_help) \
  1.4736 +    displayOptionString(inRequest, #option_name, #option_genre, default_value, option_help, inRequest->mOptions.m##option_name);
  1.4737 +#define ST_WEB_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
  1.4738 +    displayOptionStringArray(inRequest, #option_name, #option_genre, array_size, option_help, inRequest->mOptions.m##option_name);
  1.4739 +#define ST_WEB_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help)  /* no implementation */
  1.4740 +#define ST_WEB_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
  1.4741 +    displayOptionInt(inRequest, #option_name, #option_genre, default_value, multiplier, option_help, inRequest->mOptions.m##option_name);
  1.4742 +#define ST_WEB_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
  1.4743 +    displayOptionInt64(inRequest, #option_name, #option_genre, default_value, multiplier, option_help, inRequest->mOptions.m##option_name##64);
  1.4744 +#include "stoptions.h"
  1.4745 +    /*
  1.4746 +     **  Give a submit/reset button, obligatory.
  1.4747 +     **  Done respecting newlines in help text.
  1.4748 +     */
  1.4749 +    PR_fprintf(inRequest->mFD,
  1.4750 +               "<input type=submit value=\"Save Options\"> <input type=reset>\n");
  1.4751 +#if 0
  1.4752 +    PR_fprintf(inRequest->mFD, "</pre>\n");
  1.4753 +#endif
  1.4754 +    /*
  1.4755 +     ** Done with form.
  1.4756 +     */
  1.4757 +    PR_fprintf(inRequest->mFD, "</form>\n");
  1.4758 +}
  1.4759 +
  1.4760 +int
  1.4761 +handleLocalFile(STRequest * inRequest, const char *aFilename)
  1.4762 +{
  1.4763 +    static const char *const local_files[] = {
  1.4764 +        "spacetrace.css",
  1.4765 +    };
  1.4766 +    static const size_t local_file_count =
  1.4767 +        sizeof(local_files) / sizeof(local_files[0]);
  1.4768 +    size_t i;
  1.4769 +
  1.4770 +    for (i = 0; i < local_file_count; i++) {
  1.4771 +        if (0 == strcmp(local_files[i], aFilename))
  1.4772 +            return 1;
  1.4773 +    }
  1.4774 +    return 0;
  1.4775 +}
  1.4776 +
  1.4777 +/*
  1.4778 +** displayFile
  1.4779 +**
  1.4780 +** reads a file from disk, and streams it to the request
  1.4781 +*/
  1.4782 +int
  1.4783 +displayFile(STRequest * inRequest, const char *aFilename)
  1.4784 +{
  1.4785 +    PRFileDesc *inFd;
  1.4786 +    const char *filepath =
  1.4787 +        PR_smprintf("res%c%s", PR_GetDirectorySeparator(), aFilename);
  1.4788 +    char buffer[2048];
  1.4789 +    int32_t readRes;
  1.4790 +
  1.4791 +    inFd = PR_Open(filepath, PR_RDONLY, PR_IRUSR);
  1.4792 +    if (!inFd)
  1.4793 +        return -1;
  1.4794 +    while ((readRes = PR_Read(inFd, buffer, sizeof(buffer))) > 0) {
  1.4795 +        PR_Write(inRequest->mFD, buffer, readRes);
  1.4796 +    }
  1.4797 +    if (readRes != 0)
  1.4798 +        return -1;
  1.4799 +    PR_Close(inFd);
  1.4800 +    return 0;
  1.4801 +}
  1.4802 +
  1.4803 +/*
  1.4804 +** displayIndex
  1.4805 +**
  1.4806 +** Present a list of the reports you can drill down into.
  1.4807 +** Returns !0 on failure.
  1.4808 +*/
  1.4809 +int
  1.4810 +displayIndex(STRequest * inRequest)
  1.4811 +{
  1.4812 +    int retval = 0;
  1.4813 +    STOptions *options = &inRequest->mOptions;
  1.4814 +
  1.4815 +    /*
  1.4816 +     ** Present reports in a list format.
  1.4817 +     */
  1.4818 +    PR_fprintf(inRequest->mFD, "<ul>");
  1.4819 +    PR_fprintf(inRequest->mFD, "\n<li>");
  1.4820 +    htmlAnchor(inRequest, "root_callsites.html", "Root Callsites",
  1.4821 +               NULL, "mainmenu", options);
  1.4822 +    PR_fprintf(inRequest->mFD, "\n<li>");
  1.4823 +    htmlAnchor(inRequest, "categories_summary.html",
  1.4824 +               "Categories Report", NULL, "mainmenu", options);
  1.4825 +    PR_fprintf(inRequest->mFD, "\n<li>");
  1.4826 +    htmlAnchor(inRequest, "top_callsites.html",
  1.4827 +               "Top Callsites Report", NULL, "mainmenu", options);
  1.4828 +    PR_fprintf(inRequest->mFD, "\n<li>");
  1.4829 +    htmlAnchor(inRequest, "top_allocations.html",
  1.4830 +               "Top Allocations Report", NULL, "mainmenu", options);
  1.4831 +    PR_fprintf(inRequest->mFD, "\n<li>");
  1.4832 +    htmlAnchor(inRequest, "memory_leaks.html",
  1.4833 +               "Memory Leak Report", NULL, "mainmenu", options);
  1.4834 +#if ST_WANT_GRAPHS
  1.4835 +    PR_fprintf(inRequest->mFD, "\n<li>Graphs");
  1.4836 +    PR_fprintf(inRequest->mFD, "<ul>");
  1.4837 +    PR_fprintf(inRequest->mFD, "\n<li>");
  1.4838 +    htmlAnchor(inRequest, "footprint_graph.html", "Footprint",
  1.4839 +               NULL, "mainmenu graph", options);
  1.4840 +    PR_fprintf(inRequest->mFD, "\n<li>");
  1.4841 +    htmlAnchor(inRequest, "lifespan_graph.html",
  1.4842 +               "Allocation Lifespans", NULL, "mainmenu graph", options);
  1.4843 +    PR_fprintf(inRequest->mFD, "\n<li>");
  1.4844 +    htmlAnchor(inRequest, "times_graph.html", "Allocation Times",
  1.4845 +               NULL, "mainmenu graph", options);
  1.4846 +    PR_fprintf(inRequest->mFD, "\n<li>");
  1.4847 +    htmlAnchor(inRequest, "weight_graph.html",
  1.4848 +               "Allocation Weights", NULL, "mainmenu graph", options);
  1.4849 +    PR_fprintf(inRequest->mFD, "\n</ul>\n");
  1.4850 +#endif /* ST_WANT_GRAPHS */
  1.4851 +    PR_fprintf(inRequest->mFD, "\n</ul>\n");
  1.4852 +    return retval;
  1.4853 +}
  1.4854 +
  1.4855 +/*
  1.4856 +**  initRequestOptions
  1.4857 +**
  1.4858 +**  Given the request, set the options that are specific to the request.
  1.4859 +**  These can generally be determined in the following manner:
  1.4860 +**      Copy over global options.
  1.4861 +**      If getData present, attempt to use options therein.
  1.4862 +*/
  1.4863 +void
  1.4864 +initRequestOptions(STRequest * inRequest)
  1.4865 +{
  1.4866 +    if (NULL != inRequest) {
  1.4867 +        /*
  1.4868 +         **  Copy of global options.
  1.4869 +         */
  1.4870 +        memcpy(&inRequest->mOptions, &globals.mCommandLineOptions,
  1.4871 +               sizeof(globals.mCommandLineOptions));
  1.4872 +        /*
  1.4873 +         **  Decide what will override global options if anything.
  1.4874 +         */
  1.4875 +        if (NULL != inRequest->mGetData) {
  1.4876 +            fillOptions(&inRequest->mOptions, inRequest->mGetData);
  1.4877 +        }
  1.4878 +    }
  1.4879 +}
  1.4880 +
  1.4881 +STContext *
  1.4882 +contextLookup(STOptions * inOptions)
  1.4883 +/*
  1.4884 +**  Lookup a context that matches the options.
  1.4885 +**  The lookup may block, especially if the context needs to be created.
  1.4886 +**  Callers of this API must eventually call contextRelease with the
  1.4887 +**      return value;  failure to do so will cause this applications
  1.4888 +**      to eventually not work as advertised.
  1.4889 +**
  1.4890 +**  inOptions   The options determine which context is relevant.
  1.4891 +**  returns     The fully completed context on success.
  1.4892 +**              The context is read only in practice, so please do not
  1.4893 +**                  write to it or anything it points to.
  1.4894 +**              NULL on failure.
  1.4895 +*/
  1.4896 +{
  1.4897 +    STContext *retval = NULL;
  1.4898 +    STContextCache *inCache = &globals.mContextCache;
  1.4899 +
  1.4900 +    if (NULL != inOptions && NULL != inCache) {
  1.4901 +        uint32_t loop = 0;
  1.4902 +        STContext *categoryException = NULL;
  1.4903 +        PRBool newContext = PR_FALSE;
  1.4904 +        PRBool evictContext = PR_FALSE;
  1.4905 +        PRBool changeCategoryContext = PR_FALSE;
  1.4906 +
  1.4907 +        /*
  1.4908 +         **  Own the context cache while we are in here.
  1.4909 +         */
  1.4910 +        PR_Lock(inCache->mLock);
  1.4911 +        /*
  1.4912 +         **  Loop until successful.
  1.4913 +         **  Waiting on the condition variable makes sure we don't hog the
  1.4914 +         **      lock below.
  1.4915 +         */
  1.4916 +        while (1) {
  1.4917 +            /*
  1.4918 +             **  Go over the cache items.
  1.4919 +             **  At this point we are looking for a cache hit, with multiple
  1.4920 +             **      readers.
  1.4921 +             */
  1.4922 +            for (loop = 0; loop < inCache->mItemCount; loop++) {
  1.4923 +                /*
  1.4924 +                 **  Must be in use.
  1.4925 +                 */
  1.4926 +                if (PR_FALSE != inCache->mItems[loop].mInUse) {
  1.4927 +                    int delta[(STOptionGenre) MaxGenres];
  1.4928 +
  1.4929 +                    /*
  1.4930 +                     **  Compare the relevant options, figure out if different
  1.4931 +                     **      in any genre that we care about.
  1.4932 +                     */
  1.4933 +                    memset(&delta, 0, sizeof(delta));
  1.4934 +#define ST_WEB_OPTION_BOOL(option_name, option_genre, option_help) \
  1.4935 +    if(inOptions->m##option_name != inCache->mItems[loop].mOptions.m##option_name) \
  1.4936 +    { \
  1.4937 +        delta[(STOptionGenre)option_genre]++; \
  1.4938 +    }
  1.4939 +#define ST_WEB_OPTION_STRING(option_name, option_genre, default_value, option_help) \
  1.4940 +    if(0 != strcmp(inOptions->m##option_name, inCache->mItems[loop].mOptions.m##option_name)) \
  1.4941 +    { \
  1.4942 +        delta[(STOptionGenre)option_genre]++; \
  1.4943 +    }
  1.4944 +#define ST_WEB_OPTION_STRING_ARRAY(option_name, option_genre, array_size, option_help) \
  1.4945 +    { \
  1.4946 +        uint32_t macro_loop = 0; \
  1.4947 +        \
  1.4948 +        for(macro_loop = 0; macro_loop < array_size; macro_loop++) \
  1.4949 +        { \
  1.4950 +            if(0 != strcmp(inOptions->m##option_name[macro_loop], inCache->mItems[loop].mOptions.m##option_name[macro_loop])) \
  1.4951 +            { \
  1.4952 +                break; \
  1.4953 +            } \
  1.4954 +        } \
  1.4955 +        \
  1.4956 +        if(macro_loop != array_size) \
  1.4957 +        { \
  1.4958 +            delta[(STOptionGenre)option_genre]++; \
  1.4959 +        } \
  1.4960 +    }
  1.4961 +#define ST_WEB_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help)  /* no implementation */
  1.4962 +#define ST_WEB_OPTION_UINT32(option_name, option_genre, default_value, multiplier, option_help) \
  1.4963 +    if(inOptions->m##option_name != inCache->mItems[loop].mOptions.m##option_name) \
  1.4964 +    { \
  1.4965 +        delta[(STOptionGenre)option_genre]++; \
  1.4966 +    }
  1.4967 +#define ST_WEB_OPTION_UINT64(option_name, option_genre, default_value, multiplier, option_help) \
  1.4968 +    if(inOptions->m##option_name##64 != inCache->mItems[loop].mOptions.m##option_name##64) \
  1.4969 +    { \
  1.4970 +        delta[(STOptionGenre)option_genre]++; \
  1.4971 +    }
  1.4972 +#include "stoptions.h"
  1.4973 +                    /*
  1.4974 +                     **  If there is no genre out of alignment, we accept this as the context.
  1.4975 +                     */
  1.4976 +                    if (0 == delta[CategoryGenre] &&
  1.4977 +                        0 == delta[DataSortGenre] &&
  1.4978 +                        0 == delta[DataSetGenre] && 0 == delta[DataSizeGenre]
  1.4979 +                        ) {
  1.4980 +                        retval = &inCache->mItems[loop].mContext;
  1.4981 +                        break;
  1.4982 +                    }
  1.4983 +
  1.4984 +                    /*
  1.4985 +                     **  A special exception to the rule here.
  1.4986 +                     **  If all that is different is the category genre, and there
  1.4987 +                     **      is no one looking at the context (zero ref count),
  1.4988 +                     **      then there is some magic we can perform.
  1.4989 +                     */
  1.4990 +                    if (NULL == retval &&
  1.4991 +                        0 == inCache->mItems[loop].mReferenceCount &&
  1.4992 +                        0 != delta[CategoryGenre] &&
  1.4993 +                        0 == delta[DataSortGenre] &&
  1.4994 +                        0 == delta[DataSetGenre] && 0 == delta[DataSizeGenre]
  1.4995 +                        ) {
  1.4996 +                        categoryException = &inCache->mItems[loop].mContext;
  1.4997 +                    }
  1.4998 +                }
  1.4999 +            }
  1.5000 +
  1.5001 +            /*
  1.5002 +             **  Pick up our category exception if relevant.
  1.5003 +             */
  1.5004 +            if (NULL == retval && NULL != categoryException) {
  1.5005 +                retval = categoryException;
  1.5006 +                categoryException = NULL;
  1.5007 +                changeCategoryContext = PR_TRUE;
  1.5008 +            }
  1.5009 +
  1.5010 +            /*
  1.5011 +             **  If we don't have a cache hit, then we need to check for an empty
  1.5012 +             **      spot to take over.
  1.5013 +             */
  1.5014 +            if (NULL == retval) {
  1.5015 +                for (loop = 0; loop < inCache->mItemCount; loop++) {
  1.5016 +                    /*
  1.5017 +                     **  Must NOT be in use, then it will be the context.
  1.5018 +                     */
  1.5019 +                    if (PR_FALSE == inCache->mItems[loop].mInUse) {
  1.5020 +                        retval = &inCache->mItems[loop].mContext;
  1.5021 +                        newContext = PR_TRUE;
  1.5022 +                        break;
  1.5023 +                    }
  1.5024 +                }
  1.5025 +            }
  1.5026 +
  1.5027 +            /*
  1.5028 +             **  If we still don't have a return value, then we need to see if
  1.5029 +             **      there are any old items with zero ref counts that we
  1.5030 +             **      can take over.
  1.5031 +             */
  1.5032 +            if (NULL == retval) {
  1.5033 +                for (loop = 0; loop < inCache->mItemCount; loop++) {
  1.5034 +                    /*
  1.5035 +                     **  Must be in use.
  1.5036 +                     */
  1.5037 +                    if (PR_FALSE != inCache->mItems[loop].mInUse) {
  1.5038 +                        /*
  1.5039 +                         **  Must have a ref count of zero.
  1.5040 +                         */
  1.5041 +                        if (0 == inCache->mItems[loop].mReferenceCount) {
  1.5042 +                            /*
  1.5043 +                             **  Must be older than any other we know of.
  1.5044 +                             */
  1.5045 +                            if (NULL != retval) {
  1.5046 +                                if (inCache->mItems[loop].mLastAccessed <
  1.5047 +                                    inCache->mItems[retval->mIndex].
  1.5048 +                                    mLastAccessed) {
  1.5049 +                                    retval = &inCache->mItems[loop].mContext;
  1.5050 +                                }
  1.5051 +                            }
  1.5052 +                            else {
  1.5053 +                                retval = &inCache->mItems[loop].mContext;
  1.5054 +                            }
  1.5055 +                        }
  1.5056 +                    }
  1.5057 +                }
  1.5058 +
  1.5059 +                if (NULL != retval) {
  1.5060 +                    evictContext = PR_TRUE;
  1.5061 +                }
  1.5062 +            }
  1.5063 +
  1.5064 +            /*
  1.5065 +             **  If we still don't have a return value, then we can not avoid
  1.5066 +             **      waiting around until someone gives us the chance.
  1.5067 +             **  The chance, in specific, comes when a cache item reference
  1.5068 +             **      count returns to zero, upon which we can try to take
  1.5069 +             **      it over again.
  1.5070 +             */
  1.5071 +            if (NULL == retval) {
  1.5072 +                /*
  1.5073 +                 **  This has the side effect of release the context lock.
  1.5074 +                 **  This is a good thing so that other clients can continue
  1.5075 +                 **      to connect and hopefully have cache hits.
  1.5076 +                 **  If they do not have cache hits, then we will end up
  1.5077 +                 **      with a bunch of waiters here....
  1.5078 +                 */
  1.5079 +                PR_WaitCondVar(inCache->mCacheMiss, PR_INTERVAL_NO_TIMEOUT);
  1.5080 +            }
  1.5081 +
  1.5082 +            /*
  1.5083 +             **  If we have a return value, improve the reference count here.
  1.5084 +             */
  1.5085 +            if (NULL != retval) {
  1.5086 +                /*
  1.5087 +                 **  Decide if there are any changes to be made.
  1.5088 +                 **  Do as little as possible, then fall through the context
  1.5089 +                 **      cache lock to finish up.
  1.5090 +                 **  This way, lengthy init operations will not block
  1.5091 +                 **      other clients, only matches to this context.
  1.5092 +                 */
  1.5093 +                if (PR_FALSE != newContext ||
  1.5094 +                    PR_FALSE != evictContext ||
  1.5095 +                    PR_FALSE != changeCategoryContext) {
  1.5096 +                    /*
  1.5097 +                     **  Overwrite the prefs for this context.
  1.5098 +                     **  They are changing.
  1.5099 +                     */
  1.5100 +                    memcpy(&inCache->mItems[retval->mIndex].mOptions,
  1.5101 +                           inOptions,
  1.5102 +                           sizeof(inCache->mItems[retval->mIndex].mOptions));
  1.5103 +                    /*
  1.5104 +                     **  As we are going to be changing the context, we need to write lock it.
  1.5105 +                     **  This makes sure no readers are allowed while we are making our changes.
  1.5106 +                     */
  1.5107 +                    PR_RWLock_Wlock(retval->mRWLock);
  1.5108 +                }
  1.5109 +
  1.5110 +                /*
  1.5111 +                 **  NOTE, ref count gets incremented here, inside content
  1.5112 +                 **      cache lock so it can not be flushed once lock
  1.5113 +                 **      released.
  1.5114 +                 */
  1.5115 +                inCache->mItems[retval->mIndex].mInUse = PR_TRUE;
  1.5116 +                inCache->mItems[retval->mIndex].mReferenceCount++;
  1.5117 +                /*
  1.5118 +                 **  That's all folks.
  1.5119 +                 */
  1.5120 +                break;
  1.5121 +            }
  1.5122 +
  1.5123 +        }                       /* while(1), try again */
  1.5124 +
  1.5125 +        /*
  1.5126 +         **  Done with context cache.
  1.5127 +         */
  1.5128 +        PR_Unlock(inCache->mLock);
  1.5129 +        /*
  1.5130 +         **  Now that the context cached is free to continue accepting other
  1.5131 +         **      requests, we have a little more work to do.
  1.5132 +         */
  1.5133 +        if (NULL != retval) {
  1.5134 +            PRBool unlock = PR_FALSE;
  1.5135 +
  1.5136 +            /*
  1.5137 +             **  If evicting, we need to free off the old stuff.
  1.5138 +             */
  1.5139 +            if (PR_FALSE != evictContext) {
  1.5140 +                unlock = PR_TRUE;
  1.5141 +                /*
  1.5142 +                 **  We do not free the sorted run.
  1.5143 +                 **  The category code takes care of this.
  1.5144 +                 */
  1.5145 +                retval->mSortedRun = NULL;
  1.5146 +#if ST_WANT_GRAPHS
  1.5147 +                /*
  1.5148 +                 **  There is no need to
  1.5149 +                 **      PR_Lock(retval->mImageLock)
  1.5150 +                 **  We are already under write lock for the entire structure.
  1.5151 +                 */
  1.5152 +                retval->mFootprintCached = PR_FALSE;
  1.5153 +                retval->mTimevalCached = PR_FALSE;
  1.5154 +                retval->mLifespanCached = PR_FALSE;
  1.5155 +                retval->mWeightCached = PR_FALSE;
  1.5156 +#endif
  1.5157 +            }
  1.5158 +
  1.5159 +            /*
  1.5160 +             **  If new or recently evicted, we need to fully init.
  1.5161 +             */
  1.5162 +            if (PR_FALSE != newContext || PR_FALSE != evictContext) {
  1.5163 +                unlock = PR_TRUE;
  1.5164 +                retval->mSortedRun =
  1.5165 +                    createRunFromGlobal(&inCache->mItems[retval->mIndex].
  1.5166 +                                        mOptions,
  1.5167 +                                        &inCache->mItems[retval->mIndex].
  1.5168 +                                        mContext);
  1.5169 +            }
  1.5170 +
  1.5171 +            /*
  1.5172 +             **  If changing category, we need to do some sneaky stuff.
  1.5173 +             */
  1.5174 +            if (PR_FALSE != changeCategoryContext) {
  1.5175 +                STCategoryNode *node = NULL;
  1.5176 +
  1.5177 +                unlock = PR_TRUE;
  1.5178 +                /*
  1.5179 +                 ** Just a category change. We don't need to harvest. Just find the
  1.5180 +                 ** right node and set the cache.mSortedRun. We need to recompute
  1.5181 +                 ** cost though. But that is cheap.
  1.5182 +                 */
  1.5183 +                node =
  1.5184 +                    findCategoryNode(inCache->mItems[retval->mIndex].mOptions.
  1.5185 +                                     mCategoryName, &globals);
  1.5186 +                if (node) {
  1.5187 +                    /* Recalculate cost of run */
  1.5188 +                    recalculateRunCost(&inCache->mItems[retval->mIndex].
  1.5189 +                                       mOptions, retval,
  1.5190 +                                       node->runs[retval->mIndex]);
  1.5191 +                    retval->mSortedRun = node->runs[retval->mIndex];
  1.5192 +                }
  1.5193 +
  1.5194 +#if ST_WANT_GRAPHS
  1.5195 +                /*
  1.5196 +                 **  There is no need to
  1.5197 +                 **      PR_Lock(retval->mImageLock)
  1.5198 +                 **  We are already under write lock for the entire structure.
  1.5199 +                 */
  1.5200 +                retval->mFootprintCached = PR_FALSE;
  1.5201 +                retval->mTimevalCached = PR_FALSE;
  1.5202 +                retval->mLifespanCached = PR_FALSE;
  1.5203 +                retval->mWeightCached = PR_FALSE;
  1.5204 +#endif
  1.5205 +            }
  1.5206 +
  1.5207 +            /*
  1.5208 +             **  Release the write lock if we took one to make changes.
  1.5209 +             */
  1.5210 +            if (PR_FALSE != unlock) {
  1.5211 +                PR_RWLock_Unlock(retval->mRWLock);
  1.5212 +            }
  1.5213 +
  1.5214 +            /*
  1.5215 +             **  Last thing possible, take a read lock on our return value.
  1.5216 +             **  This will cause us to block if the context is not fully
  1.5217 +             **      initialized in another thread holding the write lock.
  1.5218 +             */
  1.5219 +            PR_RWLock_Rlock(retval->mRWLock);
  1.5220 +        }
  1.5221 +    }
  1.5222 +
  1.5223 +    return retval;
  1.5224 +}
  1.5225 +
  1.5226 +void
  1.5227 +contextRelease(STContext * inContext)
  1.5228 +/*
  1.5229 +**  After a successful call to contextLookup, one should call this API when
  1.5230 +**      done with the context.
  1.5231 +**  This effectively removes the usage of the client on a cached item.
  1.5232 +*/
  1.5233 +{
  1.5234 +    STContextCache *inCache = &globals.mContextCache;
  1.5235 +
  1.5236 +    if (NULL != inContext && NULL != inCache) {
  1.5237 +        /*
  1.5238 +         **  Own the context cache while in here.
  1.5239 +         */
  1.5240 +        PR_Lock(inCache->mLock);
  1.5241 +        /*
  1.5242 +         **  Give up the read lock on the context.
  1.5243 +         */
  1.5244 +        PR_RWLock_Unlock(inContext->mRWLock);
  1.5245 +        /*
  1.5246 +         **  Decrement the reference count on the context.
  1.5247 +         **  If it was the last reference, notify that a new item is
  1.5248 +         **      available for eviction.
  1.5249 +         **  A waiting thread will wake up and eat it.
  1.5250 +         **  Also set when it was last accessed so the oldest unused item
  1.5251 +         **      can be targeted for eviction.
  1.5252 +         */
  1.5253 +        inCache->mItems[inContext->mIndex].mReferenceCount--;
  1.5254 +        if (0 == inCache->mItems[inContext->mIndex].mReferenceCount) {
  1.5255 +            PR_NotifyCondVar(inCache->mCacheMiss);
  1.5256 +            inCache->mItems[inContext->mIndex].mLastAccessed =
  1.5257 +                PR_IntervalNow();
  1.5258 +        }
  1.5259 +
  1.5260 +        /*
  1.5261 +         **  Done with context cache.
  1.5262 +         */
  1.5263 +        PR_Unlock(inCache->mLock);
  1.5264 +    }
  1.5265 +}
  1.5266 +
  1.5267 +
  1.5268 +/*
  1.5269 +** handleRequest
  1.5270 +**
  1.5271 +** Based on what file they are asking for, perform some processing.
  1.5272 +** Output the results to aFD.
  1.5273 +**
  1.5274 +** Returns !0 on error.
  1.5275 +*/
  1.5276 +int
  1.5277 +handleRequest(tmreader * aTMR, PRFileDesc * aFD,
  1.5278 +              const char *aFileName, const FormData * aGetData)
  1.5279 +{
  1.5280 +    int retval = 0;
  1.5281 +
  1.5282 +    if (NULL != aTMR && NULL != aFD && NULL != aFileName
  1.5283 +        && '\0' != *aFileName) {
  1.5284 +        STRequest request;
  1.5285 +
  1.5286 +        /*
  1.5287 +         ** Init the request.
  1.5288 +         */
  1.5289 +        memset(&request, 0, sizeof(request));
  1.5290 +        request.mFD = aFD;
  1.5291 +        request.mGetFileName = aFileName;
  1.5292 +        request.mGetData = aGetData;
  1.5293 +        /*
  1.5294 +         **  Set local options for this request.
  1.5295 +         */
  1.5296 +        initRequestOptions(&request);
  1.5297 +        /*
  1.5298 +         **  Get our cached context for this client.
  1.5299 +         **  Simply based on the options.
  1.5300 +         */
  1.5301 +        request.mContext = contextLookup(&request.mOptions);
  1.5302 +        if (NULL != request.mContext) {
  1.5303 +            /*
  1.5304 +             ** Attempt to find the file of interest.
  1.5305 +             */
  1.5306 +            if (handleLocalFile(&request, aFileName)) {
  1.5307 +                displayFile(&request, aFileName);
  1.5308 +            }
  1.5309 +            else if (0 == strcmp("index.html", aFileName)) {
  1.5310 +                int displayRes = 0;
  1.5311 +
  1.5312 +                htmlHeader(&request, "SpaceTrace Index");
  1.5313 +                displayRes = displayIndex(&request);
  1.5314 +                if (0 != displayRes) {
  1.5315 +                    retval = __LINE__;
  1.5316 +                    REPORT_ERROR(__LINE__, displayIndex);
  1.5317 +                }
  1.5318 +
  1.5319 +                htmlFooter(&request);
  1.5320 +            }
  1.5321 +            else if (0 == strcmp("settings.html", aFileName) ||
  1.5322 +                     0 == strcmp("options.html", aFileName)) {
  1.5323 +                htmlHeader(&request, "SpaceTrace Options");
  1.5324 +                displaySettings(&request);
  1.5325 +                htmlFooter(&request);
  1.5326 +            }
  1.5327 +            else if (0 == strcmp("top_allocations.html", aFileName)) {
  1.5328 +                int displayRes = 0;
  1.5329 +
  1.5330 +                htmlHeader(&request, "SpaceTrace Top Allocations Report");
  1.5331 +                displayRes =
  1.5332 +                    displayTopAllocations(&request,
  1.5333 +                                          request.mContext->mSortedRun,
  1.5334 +                                          "top-allocations",
  1.5335 +                                          "SpaceTrace Top Allocations Report",
  1.5336 +                                          1);
  1.5337 +                if (0 != displayRes) {
  1.5338 +                    retval = __LINE__;
  1.5339 +                    REPORT_ERROR(__LINE__, displayTopAllocations);
  1.5340 +                }
  1.5341 +
  1.5342 +                htmlFooter(&request);
  1.5343 +            }
  1.5344 +            else if (0 == strcmp("top_callsites.html", aFileName)) {
  1.5345 +                int displayRes = 0;
  1.5346 +                tmcallsite **array = NULL;
  1.5347 +                uint32_t arrayCount = 0;
  1.5348 +
  1.5349 +                /*
  1.5350 +                 ** Display header after we figure out if we are going to focus
  1.5351 +                 ** on a category.
  1.5352 +                 */
  1.5353 +                htmlHeader(&request, "SpaceTrace Top Callsites Report");
  1.5354 +                if (NULL != request.mContext->mSortedRun
  1.5355 +                    && 0 < request.mContext->mSortedRun->mAllocationCount) {
  1.5356 +                    arrayCount =
  1.5357 +                        callsiteArrayFromRun(&array, 0,
  1.5358 +                                             request.mContext->mSortedRun);
  1.5359 +                    if (0 != arrayCount && NULL != array) {
  1.5360 +                        displayRes =
  1.5361 +                            displayTopCallsites(&request, array, arrayCount,
  1.5362 +                                                0,
  1.5363 +                                                "top-callsites",
  1.5364 +                                                "Top Callsites Report",
  1.5365 +                                                0);
  1.5366 +                        if (0 != displayRes) {
  1.5367 +                            retval = __LINE__;
  1.5368 +                            REPORT_ERROR(__LINE__, displayTopCallsites);
  1.5369 +                        }
  1.5370 +
  1.5371 +                        /*
  1.5372 +                         ** Done with the array.
  1.5373 +                         */
  1.5374 +                        free(array);
  1.5375 +                        array = NULL;
  1.5376 +                    }
  1.5377 +                }
  1.5378 +                else {
  1.5379 +                    retval = __LINE__;
  1.5380 +                    REPORT_ERROR(__LINE__, handleRequest);
  1.5381 +                }
  1.5382 +
  1.5383 +                htmlFooter(&request);
  1.5384 +            }
  1.5385 +            else if (0 == strcmp("memory_leaks.html", aFileName)) {
  1.5386 +                int displayRes = 0;
  1.5387 +
  1.5388 +                htmlHeader(&request, "SpaceTrace Memory Leaks Report");
  1.5389 +                displayRes =
  1.5390 +                    displayMemoryLeaks(&request,
  1.5391 +                                       request.mContext->mSortedRun);
  1.5392 +                if (0 != displayRes) {
  1.5393 +                    retval = __LINE__;
  1.5394 +                    REPORT_ERROR(__LINE__, displayMemoryLeaks);
  1.5395 +                }
  1.5396 +
  1.5397 +                htmlFooter(&request);
  1.5398 +            }
  1.5399 +            else if (0 == strncmp("allocation_", aFileName, 11)) {
  1.5400 +                int scanRes = 0;
  1.5401 +                uint32_t allocationIndex = 0;
  1.5402 +
  1.5403 +                /*
  1.5404 +                 ** Oh, what a hack....
  1.5405 +                 ** The index to the allocation structure in the global run
  1.5406 +                 **   is in the filename.  Better than the pointer value....
  1.5407 +                 */
  1.5408 +                scanRes = PR_sscanf(aFileName + 11, "%u", &allocationIndex);
  1.5409 +                if (1 == scanRes
  1.5410 +                    && globals.mRun.mAllocationCount > allocationIndex
  1.5411 +                    && NULL != globals.mRun.mAllocations[allocationIndex]) {
  1.5412 +                    STAllocation *allocation =
  1.5413 +                        globals.mRun.mAllocations[allocationIndex];
  1.5414 +                    char buffer[128];
  1.5415 +                    int displayRes = 0;
  1.5416 +
  1.5417 +                    PR_snprintf(buffer, sizeof(buffer),
  1.5418 +                                "SpaceTrace Allocation %u Details Report",
  1.5419 +                                allocationIndex);
  1.5420 +                    htmlHeader(&request, buffer);
  1.5421 +                    displayRes =
  1.5422 +                        displayAllocationDetails(&request, allocation);
  1.5423 +                    if (0 != displayRes) {
  1.5424 +                        retval = __LINE__;
  1.5425 +                        REPORT_ERROR(__LINE__, displayAllocationDetails);
  1.5426 +                    }
  1.5427 +
  1.5428 +                    htmlFooter(&request);
  1.5429 +                }
  1.5430 +                else {
  1.5431 +                    htmlNotFound(&request);
  1.5432 +                }
  1.5433 +            }
  1.5434 +            else if (0 == strncmp("callsite_", aFileName, 9)) {
  1.5435 +                int scanRes = 0;
  1.5436 +                uint32_t callsiteSerial = 0;
  1.5437 +                tmcallsite *resolved = NULL;
  1.5438 +
  1.5439 +                /*
  1.5440 +                 ** Oh, what a hack....
  1.5441 +                 ** The serial(key) to the callsite structure in the hash table
  1.5442 +                 **   is in the filename.  Better than the pointer value....
  1.5443 +                 */
  1.5444 +                scanRes = PR_sscanf(aFileName + 9, "%u", &callsiteSerial);
  1.5445 +                if (1 == scanRes && 0 != callsiteSerial
  1.5446 +                    && NULL != (resolved =
  1.5447 +                                tmreader_callsite(aTMR, callsiteSerial))) {
  1.5448 +                    char buffer[128];
  1.5449 +                    int displayRes = 0;
  1.5450 +
  1.5451 +                    PR_snprintf(buffer, sizeof(buffer),
  1.5452 +                                "SpaceTrace Callsite %u Details Report",
  1.5453 +                                callsiteSerial);
  1.5454 +                    htmlHeader(&request, buffer);
  1.5455 +                    displayRes = displayCallsiteDetails(&request, resolved);
  1.5456 +                    if (0 != displayRes) {
  1.5457 +                        retval = __LINE__;
  1.5458 +                        REPORT_ERROR(__LINE__, displayAllocationDetails);
  1.5459 +                    }
  1.5460 +
  1.5461 +                    htmlFooter(&request);
  1.5462 +                }
  1.5463 +                else {
  1.5464 +                    htmlNotFound(&request);
  1.5465 +                }
  1.5466 +            }
  1.5467 +            else if (0 == strcmp("root_callsites.html", aFileName)) {
  1.5468 +                int displayRes = 0;
  1.5469 +
  1.5470 +                htmlHeader(&request, "SpaceTrace Root Callsites");
  1.5471 +                displayRes =
  1.5472 +                    displayCallsites(&request, aTMR->calltree_root.kids,
  1.5473 +                                     ST_FOLLOW_SIBLINGS, 0,
  1.5474 +                                     "callsites-root",
  1.5475 +                                     "SpaceTrace Root Callsites",
  1.5476 +                                     __LINE__);
  1.5477 +                if (0 != displayRes) {
  1.5478 +                    retval = __LINE__;
  1.5479 +                    REPORT_ERROR(__LINE__, displayCallsites);
  1.5480 +                }
  1.5481 +
  1.5482 +                htmlFooter(&request);
  1.5483 +            }
  1.5484 +#if ST_WANT_GRAPHS
  1.5485 +            else if (0 == strcmp("footprint_graph.html", aFileName)) {
  1.5486 +                int displayRes = 0;
  1.5487 +
  1.5488 +                htmlHeader(&request, "SpaceTrace Memory Footprint Report");
  1.5489 +                PR_fprintf(request.mFD, "<div align=center>\n");
  1.5490 +                PR_fprintf(request.mFD, "<img src=\"./footprint.png");
  1.5491 +                optionGetDataOut(request.mFD, &request.mOptions);
  1.5492 +                PR_fprintf(request.mFD, "\">\n");
  1.5493 +                PR_fprintf(request.mFD, "</div>\n");
  1.5494 +                htmlFooter(&request);
  1.5495 +            }
  1.5496 +#endif /* ST_WANT_GRAPHS */
  1.5497 +#if ST_WANT_GRAPHS
  1.5498 +            else if (0 == strcmp("times_graph.html", aFileName)) {
  1.5499 +                int displayRes = 0;
  1.5500 +
  1.5501 +                htmlHeader(&request, "SpaceTrace Allocation Times Report");
  1.5502 +                PR_fprintf(request.mFD, "<div align=center>\n");
  1.5503 +                PR_fprintf(request.mFD, "<img src=\"./times.png");
  1.5504 +                optionGetDataOut(request.mFD, &request.mOptions);
  1.5505 +                PR_fprintf(request.mFD, "\">\n");
  1.5506 +                PR_fprintf(request.mFD, "</div>\n");
  1.5507 +                htmlFooter(&request);
  1.5508 +            }
  1.5509 +#endif /* ST_WANT_GRAPHS */
  1.5510 +#if ST_WANT_GRAPHS
  1.5511 +            else if (0 == strcmp("lifespan_graph.html", aFileName)) {
  1.5512 +                int displayRes = 0;
  1.5513 +
  1.5514 +                htmlHeader(&request,
  1.5515 +                           "SpaceTrace Allocation Lifespans Report");
  1.5516 +                PR_fprintf(request.mFD, "<div align=center>\n");
  1.5517 +                PR_fprintf(request.mFD, "<img src=\"./lifespan.png");
  1.5518 +                optionGetDataOut(request.mFD, &request.mOptions);
  1.5519 +                PR_fprintf(request.mFD, "\">\n");
  1.5520 +                PR_fprintf(request.mFD, "</div>\n");
  1.5521 +                htmlFooter(&request);
  1.5522 +            }
  1.5523 +#endif /* ST_WANT_GRAPHS */
  1.5524 +#if ST_WANT_GRAPHS
  1.5525 +            else if (0 == strcmp("weight_graph.html", aFileName)) {
  1.5526 +                int displayRes = 0;
  1.5527 +
  1.5528 +                htmlHeader(&request, "SpaceTrace Allocation Weights Report");
  1.5529 +                PR_fprintf(request.mFD, "<div align=center>\n");
  1.5530 +                PR_fprintf(request.mFD, "<img src=\"./weight.png");
  1.5531 +                optionGetDataOut(request.mFD, &request.mOptions);
  1.5532 +                PR_fprintf(request.mFD, "\">\n");
  1.5533 +                PR_fprintf(request.mFD, "</div>\n");
  1.5534 +                htmlFooter(&request);
  1.5535 +            }
  1.5536 +#endif /* ST_WANT_GRAPHS */
  1.5537 +#if ST_WANT_GRAPHS
  1.5538 +            else if (0 == strcmp("footprint.png", aFileName)) {
  1.5539 +                int graphRes = 0;
  1.5540 +
  1.5541 +                graphRes =
  1.5542 +                    graphFootprint(&request, request.mContext->mSortedRun);
  1.5543 +                if (0 != graphRes) {
  1.5544 +                    retval = __LINE__;
  1.5545 +                    REPORT_ERROR(__LINE__, graphFootprint);
  1.5546 +                }
  1.5547 +            }
  1.5548 +#endif /* ST_WANT_GRAPHS */
  1.5549 +#if ST_WANT_GRAPHS
  1.5550 +            else if (0 == strcmp("times.png", aFileName)) {
  1.5551 +                int graphRes = 0;
  1.5552 +
  1.5553 +                graphRes =
  1.5554 +                    graphTimeval(&request, request.mContext->mSortedRun);
  1.5555 +                if (0 != graphRes) {
  1.5556 +                    retval = __LINE__;
  1.5557 +                    REPORT_ERROR(__LINE__, graphTimeval);
  1.5558 +                }
  1.5559 +            }
  1.5560 +#endif /* ST_WANT_GRAPHS */
  1.5561 +#if ST_WANT_GRAPHS
  1.5562 +            else if (0 == strcmp("lifespan.png", aFileName)) {
  1.5563 +                int graphRes = 0;
  1.5564 +
  1.5565 +                graphRes =
  1.5566 +                    graphLifespan(&request, request.mContext->mSortedRun);
  1.5567 +                if (0 != graphRes) {
  1.5568 +                    retval = __LINE__;
  1.5569 +                    REPORT_ERROR(__LINE__, graphLifespan);
  1.5570 +                }
  1.5571 +            }
  1.5572 +#endif /* ST_WANT_GRAPHS */
  1.5573 +#if ST_WANT_GRAPHS
  1.5574 +            else if (0 == strcmp("weight.png", aFileName)) {
  1.5575 +                int graphRes = 0;
  1.5576 +
  1.5577 +                graphRes =
  1.5578 +                    graphWeight(&request, request.mContext->mSortedRun);
  1.5579 +                if (0 != graphRes) {
  1.5580 +                    retval = __LINE__;
  1.5581 +                    REPORT_ERROR(__LINE__, graphWeight);
  1.5582 +                }
  1.5583 +            }
  1.5584 +#endif /* ST_WANT_GRAPHS */
  1.5585 +            else if (0 == strcmp("categories_summary.html", aFileName)) {
  1.5586 +                int displayRes = 0;
  1.5587 +
  1.5588 +                htmlHeader(&request, "Category Report");
  1.5589 +                displayRes =
  1.5590 +                    displayCategoryReport(&request, &globals.mCategoryRoot,
  1.5591 +                                          1);
  1.5592 +                if (0 != displayRes) {
  1.5593 +                    retval = __LINE__;
  1.5594 +                    REPORT_ERROR(__LINE__, displayMemoryLeaks);
  1.5595 +                }
  1.5596 +
  1.5597 +                htmlFooter(&request);
  1.5598 +            }
  1.5599 +            else {
  1.5600 +                htmlNotFound(&request);
  1.5601 +            }
  1.5602 +
  1.5603 +            /*
  1.5604 +             **  Release the context we obtained earlier.
  1.5605 +             */
  1.5606 +            contextRelease(request.mContext);
  1.5607 +            request.mContext = NULL;
  1.5608 +        }
  1.5609 +        else {
  1.5610 +            retval = __LINE__;
  1.5611 +            REPORT_ERROR(__LINE__, contextObtain);
  1.5612 +        }
  1.5613 +    }
  1.5614 +    else {
  1.5615 +        retval = __LINE__;
  1.5616 +        REPORT_ERROR(__LINE__, handleRequest);
  1.5617 +    }
  1.5618 +
  1.5619 +    /*
  1.5620 +     ** Compact a little if you can after each request.
  1.5621 +     */
  1.5622 +    heapCompact();
  1.5623 +    return retval;
  1.5624 +}
  1.5625 +
  1.5626 +/*
  1.5627 +** handleClient
  1.5628 +**
  1.5629 +**  main() of the new client thread.
  1.5630 +** Read the fd for the request.
  1.5631 +** Output the results.
  1.5632 +*/
  1.5633 +void
  1.5634 +handleClient(void *inArg)
  1.5635 +{
  1.5636 +    PRFileDesc *aFD = NULL;
  1.5637 +
  1.5638 +    aFD = (PRFileDesc *) inArg;
  1.5639 +    if (NULL != aFD) {
  1.5640 +        PRStatus closeRes = PR_SUCCESS;
  1.5641 +        char aBuffer[2048];
  1.5642 +        int32_t readRes = 0;
  1.5643 +
  1.5644 +        readRes = PR_Read(aFD, aBuffer, sizeof(aBuffer));
  1.5645 +        if (0 <= readRes) {
  1.5646 +            const char *sanityCheck = "GET /";
  1.5647 +
  1.5648 +            if (0 == strncmp(sanityCheck, aBuffer, 5)) {
  1.5649 +                char *eourl = NULL;
  1.5650 +                char *start = &aBuffer[5];
  1.5651 +                char *getData = NULL;
  1.5652 +                int realFun = 0;
  1.5653 +                const char *crlf = "\015\012";
  1.5654 +                char *eoline = NULL;
  1.5655 +                FormData *fdGet = NULL;
  1.5656 +
  1.5657 +                /*
  1.5658 +                 ** Truncate the line if possible.
  1.5659 +                 ** Only want first one.
  1.5660 +                 */
  1.5661 +                eoline = strstr(aBuffer, crlf);
  1.5662 +                if (NULL != eoline) {
  1.5663 +                    *eoline = '\0';
  1.5664 +                }
  1.5665 +
  1.5666 +                /*
  1.5667 +                 ** Find the whitespace.
  1.5668 +                 ** That is either end of line or the " HTTP/1.x" suffix.
  1.5669 +                 ** We do not care.
  1.5670 +                 */
  1.5671 +                for (eourl = start; 0 == isspace(*eourl) && '\0' != *eourl;
  1.5672 +                     eourl++) {
  1.5673 +                    /*
  1.5674 +                     ** No body.
  1.5675 +                     */
  1.5676 +                }
  1.5677 +
  1.5678 +                /*
  1.5679 +                 ** Cap it off.
  1.5680 +                 ** Convert empty '/' to index.html.
  1.5681 +                 */
  1.5682 +                *eourl = '\0';
  1.5683 +                if ('\0' == *start) {
  1.5684 +                    strcpy(start, "index.html");
  1.5685 +                }
  1.5686 +
  1.5687 +                /*
  1.5688 +                 ** Have we got any GET form data?
  1.5689 +                 */
  1.5690 +                getData = strchr(start, '?');
  1.5691 +                if (NULL != getData) {
  1.5692 +                    /*
  1.5693 +                     ** Whack it off.
  1.5694 +                     */
  1.5695 +                    *getData = '\0';
  1.5696 +                    getData++;
  1.5697 +                }
  1.5698 +
  1.5699 +                /*
  1.5700 +                 **  Convert get data into a more useful format.
  1.5701 +                 */
  1.5702 +                fdGet = FormData_Create(getData);
  1.5703 +                /*
  1.5704 +                 ** This is totally a hack, but oh well....
  1.5705 +                 **
  1.5706 +                 ** Send that the request was OK, regardless.
  1.5707 +                 **
  1.5708 +                 ** If we have any get data, then it is a set of options
  1.5709 +                 **      we attempt to apply.
  1.5710 +                 **
  1.5711 +                 ** Other code will tell the user they were wrong or if
  1.5712 +                 **      there was an error.
  1.5713 +                 ** If the filename contains a ".png", then send the image
  1.5714 +                 **      mime type, otherwise, say it is text/html. 
  1.5715 +                 */
  1.5716 +                PR_fprintf(aFD, "HTTP/1.1 200 OK%s", crlf);
  1.5717 +                PR_fprintf(aFD, "Server: %s%s",
  1.5718 +                           "$Id: spacetrace.c,v 1.54 2006/11/01 23:02:17 timeless%mozdev.org Exp $",
  1.5719 +                           crlf);
  1.5720 +                PR_fprintf(aFD, "Content-type: ");
  1.5721 +                if (NULL != strstr(start, ".png")) {
  1.5722 +                    PR_fprintf(aFD, "image/png");
  1.5723 +                }
  1.5724 +                else if (NULL != strstr(start, ".jpg")) {
  1.5725 +                    PR_fprintf(aFD, "image/jpeg");
  1.5726 +                }
  1.5727 +                else if (NULL != strstr(start, ".txt")) {
  1.5728 +                    PR_fprintf(aFD, "text/plain");
  1.5729 +                }
  1.5730 +                else if (NULL != strstr(start, ".css")) {
  1.5731 +                    PR_fprintf(aFD, "text/css");
  1.5732 +                }
  1.5733 +                else {
  1.5734 +                    PR_fprintf(aFD, "text/html");
  1.5735 +                }
  1.5736 +                PR_fprintf(aFD, crlf);
  1.5737 +                /*
  1.5738 +                 ** One more to separate headers from content.
  1.5739 +                 */
  1.5740 +                PR_fprintf(aFD, crlf);
  1.5741 +                /*
  1.5742 +                 ** Ready for the real fun.
  1.5743 +                 */
  1.5744 +                realFun = handleRequest(globals.mTMR, aFD, start, fdGet);
  1.5745 +                if (0 != realFun) {
  1.5746 +                    REPORT_ERROR(__LINE__, handleRequest);
  1.5747 +                }
  1.5748 +
  1.5749 +                /*
  1.5750 +                 **  Free off get data if around.
  1.5751 +                 */
  1.5752 +                FormData_Destroy(fdGet);
  1.5753 +                fdGet = NULL;
  1.5754 +            }
  1.5755 +            else {
  1.5756 +                REPORT_ERROR(__LINE__, handleClient);
  1.5757 +            }
  1.5758 +        }
  1.5759 +        else {
  1.5760 +            REPORT_ERROR(__LINE__, lineReader);
  1.5761 +        }
  1.5762 +
  1.5763 +        /*
  1.5764 +         ** Done with the connection.
  1.5765 +         */
  1.5766 +        closeRes = PR_Close(aFD);
  1.5767 +        if (PR_SUCCESS != closeRes) {
  1.5768 +            REPORT_ERROR(__LINE__, PR_Close);
  1.5769 +        }
  1.5770 +    }
  1.5771 +    else {
  1.5772 +        REPORT_ERROR(__LINE__, handleClient);
  1.5773 +    }
  1.5774 +}
  1.5775 +
  1.5776 +/*
  1.5777 +** serverMode
  1.5778 +**
  1.5779 +** List on a port as a httpd.
  1.5780 +** Output results interactively on demand.
  1.5781 +**
  1.5782 +** Returns !0 on error.
  1.5783 +*/
  1.5784 +int
  1.5785 +serverMode(void)
  1.5786 +{
  1.5787 +    int retval = 0;
  1.5788 +    PRFileDesc *socket = NULL;
  1.5789 +
  1.5790 +    /*
  1.5791 +     ** Create a socket.
  1.5792 +     */
  1.5793 +    socket = PR_NewTCPSocket();
  1.5794 +    if (NULL != socket) {
  1.5795 +        PRStatus closeRes = PR_SUCCESS;
  1.5796 +        PRNetAddr bindAddr;
  1.5797 +        PRStatus bindRes = PR_SUCCESS;
  1.5798 +
  1.5799 +        /*
  1.5800 +         ** Bind it to an interface/port.
  1.5801 +         ** Any interface.
  1.5802 +         */
  1.5803 +        bindAddr.inet.family = PR_AF_INET;
  1.5804 +        bindAddr.inet.port =
  1.5805 +            PR_htons((uint16_t) globals.mCommandLineOptions.mHttpdPort);
  1.5806 +        bindAddr.inet.ip = PR_htonl(PR_INADDR_ANY);
  1.5807 +        bindRes = PR_Bind(socket, &bindAddr);
  1.5808 +        if (PR_SUCCESS == bindRes) {
  1.5809 +            PRStatus listenRes = PR_SUCCESS;
  1.5810 +            const int backlog = 0x20;
  1.5811 +
  1.5812 +            /*
  1.5813 +             ** Start listening for clients.
  1.5814 +             ** Give a decent backlog, some of our processing will take
  1.5815 +             **  a bit.
  1.5816 +             */
  1.5817 +            listenRes = PR_Listen(socket, backlog);
  1.5818 +            if (PR_SUCCESS == listenRes) {
  1.5819 +                PRFileDesc *connection = NULL;
  1.5820 +                int failureSum = 0;
  1.5821 +                char message[80];
  1.5822 +
  1.5823 +                /*
  1.5824 +                 ** Output a little message saying we are receiving.
  1.5825 +                 */
  1.5826 +                PR_snprintf(message, sizeof(message),
  1.5827 +                            "server accepting connections at http://localhost:%u/",
  1.5828 +                            globals.mCommandLineOptions.mHttpdPort);
  1.5829 +                REPORT_INFO(message);
  1.5830 +                PR_fprintf(PR_STDOUT, "Peak memory used: %s bytes\n",
  1.5831 +                           FormatNumber(globals.mPeakMemoryUsed));
  1.5832 +                PR_fprintf(PR_STDOUT, "Allocations     : %s total\n",
  1.5833 +                           FormatNumber(globals.mMallocCount +
  1.5834 +                                        globals.mCallocCount +
  1.5835 +                                        globals.mReallocCount),
  1.5836 +                           FormatNumber(globals.mFreeCount));
  1.5837 +                PR_fprintf(PR_STDOUT, "Breakdown       : %s malloc\n",
  1.5838 +                           FormatNumber(globals.mMallocCount));
  1.5839 +                PR_fprintf(PR_STDOUT, "                  %s calloc\n",
  1.5840 +                           FormatNumber(globals.mCallocCount));
  1.5841 +                PR_fprintf(PR_STDOUT, "                  %s realloc\n",
  1.5842 +                           FormatNumber(globals.mReallocCount));
  1.5843 +                PR_fprintf(PR_STDOUT, "                  %s free\n",
  1.5844 +                           FormatNumber(globals.mFreeCount));
  1.5845 +                PR_fprintf(PR_STDOUT, "Leaks           : %s\n",
  1.5846 +                           FormatNumber((globals.mMallocCount +
  1.5847 +                                         globals.mCallocCount +
  1.5848 +                                         globals.mReallocCount) -
  1.5849 +                                        globals.mFreeCount));
  1.5850 +                /*
  1.5851 +                 **  Keep accepting until we know otherwise.
  1.5852 +                 **
  1.5853 +                 **  We do a thread per connection.
  1.5854 +                 **  Up to the thread to close the connection when done.
  1.5855 +                 **
  1.5856 +                 **  This is known by me to be suboptimal, and I would rather
  1.5857 +                 **      do a thread pool if it ever becomes a resource issue.
  1.5858 +                 **  Any issues would simply point to a need to get
  1.5859 +                 **      more machines or a beefier machine to handle the
  1.5860 +                 **      requests, as well as a need to do thread pooling and
  1.5861 +                 **      avoid thread creation overhead.
  1.5862 +                 **  The threads are not tracked, except possibly by NSPR
  1.5863 +                 **      itself and PR_Cleanup will wait on them all to exit as
  1.5864 +                 **      user threads so our shared data is valid.
  1.5865 +                 */
  1.5866 +                while (0 == retval) {
  1.5867 +                    connection =
  1.5868 +                        PR_Accept(socket, NULL, PR_INTERVAL_NO_TIMEOUT);
  1.5869 +                    if (NULL != connection) {
  1.5870 +                        PRThread *clientThread = NULL;
  1.5871 +
  1.5872 +                        /*
  1.5873 +                         **  Thread per connection.
  1.5874 +                         */
  1.5875 +                        clientThread = PR_CreateThread(PR_USER_THREAD,  /* PR_Cleanup sync */
  1.5876 +                                                       handleClient, (void *) connection, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, /* IO enabled */
  1.5877 +                                                       PR_UNJOINABLE_THREAD,
  1.5878 +                                                       0);
  1.5879 +                        if (NULL == clientThread) {
  1.5880 +                            PRStatus closeRes = PR_SUCCESS;
  1.5881 +
  1.5882 +                            failureSum += __LINE__;
  1.5883 +                            REPORT_ERROR(__LINE__, PR_Accept);
  1.5884 +                            /*
  1.5885 +                             **  Close the connection as well, no service
  1.5886 +                             */
  1.5887 +                            closeRes = PR_Close(connection);
  1.5888 +                            if (PR_FAILURE == closeRes) {
  1.5889 +                                REPORT_ERROR(__LINE__, PR_Close);
  1.5890 +                            }
  1.5891 +                        }
  1.5892 +                    }
  1.5893 +                    else {
  1.5894 +                        failureSum += __LINE__;
  1.5895 +                        REPORT_ERROR(__LINE__, PR_Accept);
  1.5896 +                    }
  1.5897 +                }
  1.5898 +
  1.5899 +                if (0 != failureSum) {
  1.5900 +                    retval = __LINE__;
  1.5901 +                }
  1.5902 +
  1.5903 +                /*
  1.5904 +                 ** Output a little message saying it is all over.
  1.5905 +                 */
  1.5906 +                REPORT_INFO("server no longer accepting connections....");
  1.5907 +            }
  1.5908 +            else {
  1.5909 +                retval = __LINE__;
  1.5910 +                REPORT_ERROR(__LINE__, PR_Listen);
  1.5911 +            }
  1.5912 +        }
  1.5913 +        else {
  1.5914 +            retval = __LINE__;
  1.5915 +            REPORT_ERROR(__LINE__, PR_Bind);
  1.5916 +        }
  1.5917 +
  1.5918 +        /*
  1.5919 +         ** Done with socket.
  1.5920 +         */
  1.5921 +        closeRes = PR_Close(socket);
  1.5922 +        if (PR_SUCCESS != closeRes) {
  1.5923 +            retval = __LINE__;
  1.5924 +            REPORT_ERROR(__LINE__, PR_Close);
  1.5925 +        }
  1.5926 +        socket = NULL;
  1.5927 +    }
  1.5928 +    else {
  1.5929 +        retval = __LINE__;
  1.5930 +        REPORT_ERROR(__LINE__, PR_NewTCPSocket);
  1.5931 +    }
  1.5932 +
  1.5933 +    return retval;
  1.5934 +}
  1.5935 +
  1.5936 +/*
  1.5937 +** batchMode
  1.5938 +**
  1.5939 +** Perform whatever batch requests we were asked to do.
  1.5940 +*/
  1.5941 +int
  1.5942 +batchMode(void)
  1.5943 +{
  1.5944 +    int retval = 0;
  1.5945 +
  1.5946 +    if (0 != globals.mCommandLineOptions.mBatchRequestCount) {
  1.5947 +        uint32_t loop = 0;
  1.5948 +        int failureSum = 0;
  1.5949 +        int handleRes = 0;
  1.5950 +        char aFileName[1024];
  1.5951 +        uint32_t sprintfRes = 0;
  1.5952 +
  1.5953 +        /*
  1.5954 +         ** Go through and process the various files requested.
  1.5955 +         ** We do not stop on failure, as it is too costly to rerun the
  1.5956 +         **  batch job.
  1.5957 +         */
  1.5958 +        for (loop = 0;
  1.5959 +             loop < globals.mCommandLineOptions.mBatchRequestCount; loop++) {
  1.5960 +            sprintfRes =
  1.5961 +                PR_snprintf(aFileName, sizeof(aFileName), "%s%c%s",
  1.5962 +                            globals.mCommandLineOptions.mOutputDir,
  1.5963 +                            PR_GetDirectorySeparator(),
  1.5964 +                            globals.mCommandLineOptions.mBatchRequest[loop]);
  1.5965 +            if ((uint32_t) - 1 != sprintfRes) {
  1.5966 +                PRFileDesc *outFile = NULL;
  1.5967 +
  1.5968 +                outFile = PR_Open(aFileName, ST_FLAGS, ST_PERMS);
  1.5969 +                if (NULL != outFile) {
  1.5970 +                    PRStatus closeRes = PR_SUCCESS;
  1.5971 +
  1.5972 +                    handleRes =
  1.5973 +                        handleRequest(globals.mTMR, outFile,
  1.5974 +                                      globals.mCommandLineOptions.
  1.5975 +                                      mBatchRequest[loop], NULL);
  1.5976 +                    if (0 != handleRes) {
  1.5977 +                        failureSum += __LINE__;
  1.5978 +                        REPORT_ERROR(__LINE__, handleRequest);
  1.5979 +                    }
  1.5980 +
  1.5981 +                    closeRes = PR_Close(outFile);
  1.5982 +                    if (PR_SUCCESS != closeRes) {
  1.5983 +                        failureSum += __LINE__;
  1.5984 +                        REPORT_ERROR(__LINE__, PR_Close);
  1.5985 +                    }
  1.5986 +                }
  1.5987 +                else {
  1.5988 +                    failureSum += __LINE__;
  1.5989 +                    REPORT_ERROR(__LINE__, PR_Open);
  1.5990 +                }
  1.5991 +            }
  1.5992 +            else {
  1.5993 +                failureSum += __LINE__;
  1.5994 +                REPORT_ERROR(__LINE__, PR_snprintf);
  1.5995 +            }
  1.5996 +        }
  1.5997 +
  1.5998 +        if (0 != failureSum) {
  1.5999 +            retval = __LINE__;
  1.6000 +        }
  1.6001 +    }
  1.6002 +    else {
  1.6003 +        retval = __LINE__;
  1.6004 +        REPORT_ERROR(__LINE__, outputReports);
  1.6005 +    }
  1.6006 +
  1.6007 +    return retval;
  1.6008 +}
  1.6009 +
  1.6010 +/*
  1.6011 +** doRun
  1.6012 +**
  1.6013 +** Perform the actual processing this program requires.
  1.6014 +** Returns !0 on failure.
  1.6015 +*/
  1.6016 +int
  1.6017 +doRun(void)
  1.6018 +{
  1.6019 +    int retval = 0;
  1.6020 +
  1.6021 +    /*
  1.6022 +     ** Create the new trace-malloc reader.
  1.6023 +     */
  1.6024 +    globals.mTMR = tmreader_new(globals.mProgramName, NULL);
  1.6025 +    if (NULL != globals.mTMR) {
  1.6026 +        int tmResult = 0;
  1.6027 +        int outputResult = 0;
  1.6028 +
  1.6029 +#if defined(DEBUG_dp)
  1.6030 +        PRIntervalTime start = PR_IntervalNow();
  1.6031 +
  1.6032 +        fprintf(stderr, "DEBUG: reading tracemalloc data...\n");
  1.6033 +#endif
  1.6034 +        tmResult =
  1.6035 +            tmreader_eventloop(globals.mTMR,
  1.6036 +                               globals.mCommandLineOptions.mFileName,
  1.6037 +                               tmEventHandler);
  1.6038 +        printf("\rReading... Done.\n");
  1.6039 +#if defined(DEBUG_dp)
  1.6040 +        fprintf(stderr,
  1.6041 +                "DEBUG: reading tracemalloc data ends: %dms [%d allocations]\n",
  1.6042 +                PR_IntervalToMilliseconds(PR_IntervalNow() - start),
  1.6043 +                globals.mRun.mAllocationCount);
  1.6044 +#endif
  1.6045 +        if (0 == tmResult) {
  1.6046 +            REPORT_ERROR(__LINE__, tmreader_eventloop);
  1.6047 +            retval = __LINE__;
  1.6048 +        }
  1.6049 +
  1.6050 +        if (0 == retval) {
  1.6051 +            /*
  1.6052 +             ** Decide if we're going into batch mode or server mode.
  1.6053 +             */
  1.6054 +            if (0 != globals.mCommandLineOptions.mBatchRequestCount) {
  1.6055 +                /*
  1.6056 +                 ** Output in one big step while everything still exists.
  1.6057 +                 */
  1.6058 +                outputResult = batchMode();
  1.6059 +                if (0 != outputResult) {
  1.6060 +                    REPORT_ERROR(__LINE__, batchMode);
  1.6061 +                    retval = __LINE__;
  1.6062 +                }
  1.6063 +            }
  1.6064 +            else {
  1.6065 +                int serverRes = 0;
  1.6066 +
  1.6067 +                /*
  1.6068 +                 ** httpd time.
  1.6069 +                 */
  1.6070 +                serverRes = serverMode();
  1.6071 +                if (0 != serverRes) {
  1.6072 +                    REPORT_ERROR(__LINE__, serverMode);
  1.6073 +                    retval = __LINE__;
  1.6074 +                }
  1.6075 +            }
  1.6076 +
  1.6077 +            /*
  1.6078 +             ** Clear our categorization tree
  1.6079 +             */
  1.6080 +            freeCategories(&globals);
  1.6081 +        }
  1.6082 +    }
  1.6083 +    else {
  1.6084 +        REPORT_ERROR(__LINE__, tmreader_new);
  1.6085 +        retval = __LINE__;
  1.6086 +    }
  1.6087 +
  1.6088 +    return retval;
  1.6089 +}
  1.6090 +
  1.6091 +int
  1.6092 +initCaches(void)
  1.6093 +/*
  1.6094 +**  Initialize the global caches.
  1.6095 +**  More involved since we have to allocated/create some objects.
  1.6096 +**
  1.6097 +**  returns     Zero if all is well.
  1.6098 +**              Non-zero on error.
  1.6099 +*/
  1.6100 +{
  1.6101 +    int retval = 0;
  1.6102 +    STContextCache *inCache = &globals.mContextCache;
  1.6103 +
  1.6104 +    if (NULL != inCache && 0 != globals.mCommandLineOptions.mContexts) {
  1.6105 +        inCache->mLock = PR_NewLock();
  1.6106 +        if (NULL != inCache->mLock) {
  1.6107 +            inCache->mCacheMiss = PR_NewCondVar(inCache->mLock);
  1.6108 +            if (NULL != inCache->mCacheMiss) {
  1.6109 +                inCache->mItems =
  1.6110 +                    (STContextCacheItem *) calloc(globals.mCommandLineOptions.
  1.6111 +                                                  mContexts,
  1.6112 +                                                  sizeof(STContextCacheItem));
  1.6113 +                if (NULL != inCache->mItems) {
  1.6114 +                    uint32_t loop = 0;
  1.6115 +                    char buffer[64];
  1.6116 +
  1.6117 +                    inCache->mItemCount =
  1.6118 +                        globals.mCommandLineOptions.mContexts;
  1.6119 +                    /*
  1.6120 +                     **  Init each item as needed.
  1.6121 +                     */
  1.6122 +                    for (loop = 0; loop < inCache->mItemCount; loop++) {
  1.6123 +                        inCache->mItems[loop].mContext.mIndex = loop;
  1.6124 +                        PR_snprintf(buffer, sizeof(buffer),
  1.6125 +                                    "Context Item %d RW Lock", loop);
  1.6126 +                        inCache->mItems[loop].mContext.mRWLock =
  1.6127 +                            PR_NewRWLock(PR_RWLOCK_RANK_NONE, buffer);
  1.6128 +                        if (NULL == inCache->mItems[loop].mContext.mRWLock) {
  1.6129 +                            break;
  1.6130 +                        }
  1.6131 +#if ST_WANT_GRAPHS
  1.6132 +                        inCache->mItems[loop].mContext.mImageLock =
  1.6133 +                            PR_NewLock();
  1.6134 +                        if (NULL == inCache->mItems[loop].mContext.mImageLock) {
  1.6135 +                            break;
  1.6136 +                        }
  1.6137 +#endif
  1.6138 +                    }
  1.6139 +
  1.6140 +                    if (loop != inCache->mItemCount) {
  1.6141 +                        retval = __LINE__;
  1.6142 +                        REPORT_ERROR(__LINE__, initCaches);
  1.6143 +                    }
  1.6144 +                }
  1.6145 +                else {
  1.6146 +                    retval = __LINE__;
  1.6147 +                    REPORT_ERROR(__LINE__, calloc);
  1.6148 +                }
  1.6149 +            }
  1.6150 +            else {
  1.6151 +                retval = __LINE__;
  1.6152 +                REPORT_ERROR(__LINE__, PR_NewCondVar);
  1.6153 +            }
  1.6154 +        }
  1.6155 +        else {
  1.6156 +            retval = __LINE__;
  1.6157 +            REPORT_ERROR(__LINE__, PR_NewLock);
  1.6158 +        }
  1.6159 +    }
  1.6160 +    else {
  1.6161 +        retval = __LINE__;
  1.6162 +        REPORT_ERROR(__LINE__, initCaches);
  1.6163 +    }
  1.6164 +
  1.6165 +    return retval;
  1.6166 +}
  1.6167 +
  1.6168 +int
  1.6169 +destroyCaches(void)
  1.6170 +/*
  1.6171 +**  Clean up any global caches we have laying around.
  1.6172 +**
  1.6173 +**  returns     Zero if all is well.
  1.6174 +**              Non-zero on error.
  1.6175 +*/
  1.6176 +{
  1.6177 +    int retval = 0;
  1.6178 +    STContextCache *inCache = &globals.mContextCache;
  1.6179 +
  1.6180 +    if (NULL != inCache) {
  1.6181 +        uint32_t loop = 0;
  1.6182 +
  1.6183 +        /*
  1.6184 +         **  Uninit item data one by one.
  1.6185 +         */
  1.6186 +        for (loop = 0; loop < inCache->mItemCount; loop++) {
  1.6187 +            if (NULL != inCache->mItems[loop].mContext.mRWLock) {
  1.6188 +                PR_DestroyRWLock(inCache->mItems[loop].mContext.mRWLock);
  1.6189 +                inCache->mItems[loop].mContext.mRWLock = NULL;
  1.6190 +            }
  1.6191 +#if ST_WANT_GRAPHS
  1.6192 +            if (NULL != inCache->mItems[loop].mContext.mImageLock) {
  1.6193 +                PR_DestroyLock(inCache->mItems[loop].mContext.mImageLock);
  1.6194 +                inCache->mItems[loop].mContext.mImageLock = NULL;
  1.6195 +            }
  1.6196 +#endif
  1.6197 +        }
  1.6198 +
  1.6199 +        inCache->mItemCount = 0;
  1.6200 +        if (NULL != inCache->mItems) {
  1.6201 +            free(inCache->mItems);
  1.6202 +            inCache->mItems = NULL;
  1.6203 +        }
  1.6204 +
  1.6205 +        if (NULL != inCache->mCacheMiss) {
  1.6206 +            PR_DestroyCondVar(inCache->mCacheMiss);
  1.6207 +            inCache->mCacheMiss = NULL;
  1.6208 +        }
  1.6209 +
  1.6210 +        if (NULL != inCache->mLock) {
  1.6211 +            PR_DestroyLock(inCache->mLock);
  1.6212 +            inCache->mLock = NULL;
  1.6213 +        }
  1.6214 +    }
  1.6215 +    else {
  1.6216 +        retval = __LINE__;
  1.6217 +        REPORT_ERROR(__LINE__, destroyCaches);
  1.6218 +    }
  1.6219 +
  1.6220 +    return retval;
  1.6221 +}
  1.6222 +
  1.6223 +/*
  1.6224 +** main
  1.6225 +**
  1.6226 +** Process entry and exit.
  1.6227 +*/
  1.6228 +int
  1.6229 +main(int aArgCount, char **aArgArray)
  1.6230 +{
  1.6231 +    int retval = 0;
  1.6232 +    int optionsResult = 0;
  1.6233 +    PRStatus prResult = PR_SUCCESS;
  1.6234 +    int showedHelp = 0;
  1.6235 +    int looper = 0;
  1.6236 +    int cacheResult = 0;
  1.6237 +
  1.6238 +    /*
  1.6239 +     ** NSPR init.
  1.6240 +     */
  1.6241 +    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
  1.6242 +    /*
  1.6243 +     ** Initialize globals
  1.6244 +     */
  1.6245 +    memset(&globals, 0, sizeof(globals));
  1.6246 +    /*
  1.6247 +     ** Set the program name.
  1.6248 +     */
  1.6249 +    globals.mProgramName = aArgArray[0];
  1.6250 +    /*
  1.6251 +     ** Set the minimum timeval really high so other code
  1.6252 +     **  that checks the timeval will get it right.
  1.6253 +     */
  1.6254 +    globals.mMinTimeval = ST_TIMEVAL_MAX;
  1.6255 +    /*
  1.6256 +     ** Handle initializing options.
  1.6257 +     */
  1.6258 +    optionsResult = initOptions(aArgCount, aArgArray);
  1.6259 +    if (0 != optionsResult) {
  1.6260 +        REPORT_ERROR(optionsResult, initOptions);
  1.6261 +        retval = __LINE__;
  1.6262 +    }
  1.6263 +
  1.6264 +    /*
  1.6265 +     **  Initialize our caches.
  1.6266 +     */
  1.6267 +    cacheResult = initCaches();
  1.6268 +    if (0 != cacheResult) {
  1.6269 +        retval = __LINE__;
  1.6270 +        REPORT_ERROR(__LINE__, initCaches);
  1.6271 +    }
  1.6272 +
  1.6273 +    /*
  1.6274 +     **  Small alloc code init.
  1.6275 +     */
  1.6276 +    globals.mCategoryRoot.runs =
  1.6277 +        (STRun **) calloc(globals.mCommandLineOptions.mContexts,
  1.6278 +                          sizeof(STRun *));
  1.6279 +    if (NULL == globals.mCategoryRoot.runs) {
  1.6280 +        retval = __LINE__;
  1.6281 +        REPORT_ERROR(__LINE__, calloc);
  1.6282 +    }
  1.6283 +
  1.6284 +    /*
  1.6285 +     ** Show help on usage if need be.
  1.6286 +     */
  1.6287 +    showedHelp = showHelp();
  1.6288 +    /*
  1.6289 +     ** Only perform the run if everything is checking out.
  1.6290 +     */
  1.6291 +    if (0 == showedHelp && 0 == retval) {
  1.6292 +        int runResult = 0;
  1.6293 +
  1.6294 +        runResult = doRun();
  1.6295 +        if (0 != runResult) {
  1.6296 +            REPORT_ERROR(runResult, doRun);
  1.6297 +            retval = __LINE__;
  1.6298 +        }
  1.6299 +    }
  1.6300 +
  1.6301 +    if (0 != retval) {
  1.6302 +        REPORT_ERROR(retval, main);
  1.6303 +    }
  1.6304 +
  1.6305 +    /*
  1.6306 +     **  Have NSPR join all client threads we started.
  1.6307 +     */
  1.6308 +    prResult = PR_Cleanup();
  1.6309 +    if (PR_SUCCESS != prResult) {
  1.6310 +        REPORT_ERROR(retval, PR_Cleanup);
  1.6311 +        retval = __LINE__;
  1.6312 +    }
  1.6313 +    /*
  1.6314 +     **  All threads are joined/done by this line.
  1.6315 +     */
  1.6316 +
  1.6317 +    /*
  1.6318 +     **  Options allocated a little.
  1.6319 +     */
  1.6320 +#define ST_CMD_OPTION_STRING_PTR_ARRAY(option_name, option_genre, option_help) \
  1.6321 +    if(NULL != globals.mCommandLineOptions.m##option_name) \
  1.6322 +    { \
  1.6323 +        free((void*)globals.mCommandLineOptions.m##option_name); \
  1.6324 +        globals.mCommandLineOptions.m##option_name = NULL; \
  1.6325 +        globals.mCommandLineOptions.m##option_name##Count = 0; \
  1.6326 +    }
  1.6327 +#include "stoptions.h"
  1.6328 +
  1.6329 +    /*
  1.6330 +     **  globals has a small modification to clear up.
  1.6331 +     */
  1.6332 +    if (NULL != globals.mCategoryRoot.runs) {
  1.6333 +        free(globals.mCategoryRoot.runs);
  1.6334 +        globals.mCategoryRoot.runs = NULL;
  1.6335 +    }
  1.6336 +
  1.6337 +    /*
  1.6338 +     **  Blow away our caches.
  1.6339 +     */
  1.6340 +    cacheResult = destroyCaches();
  1.6341 +    if (0 != cacheResult) {
  1.6342 +        retval = __LINE__;
  1.6343 +        REPORT_ERROR(__LINE__, initCaches);
  1.6344 +    }
  1.6345 +
  1.6346 +    /*
  1.6347 +     **  We are safe to kill our tmreader data.
  1.6348 +     */
  1.6349 +    if (NULL != globals.mTMR) {
  1.6350 +        tmreader_destroy(globals.mTMR);
  1.6351 +        globals.mTMR = NULL;
  1.6352 +    }
  1.6353 +
  1.6354 +    return retval;
  1.6355 +}

mercurial