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 +}