tools/trace-malloc/spacetrace.c

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

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

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

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

mercurial