tools/trace-malloc/tmfrags.c

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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 #include <stdio.h>
michael@0 8 #include <stdlib.h>
michael@0 9 #include <string.h>
michael@0 10 #include <time.h>
michael@0 11 #include <ctype.h>
michael@0 12 #include <errno.h>
michael@0 13 #include <math.h>
michael@0 14
michael@0 15 #include "nspr.h"
michael@0 16 #include "tmreader.h"
michael@0 17
michael@0 18
michael@0 19 #define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
michael@0 20 #define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
michael@0 21
michael@0 22
michael@0 23 #define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000)
michael@0 24 #define ticks2usec(reader, ticks) ticks2xsec((reader), (ticks), 1000000)
michael@0 25 #define TICK_RESOLUTION 1000
michael@0 26 #define TICK_PRINTABLE(timeval) ((double)(timeval) / (double)ST_TIMEVAL_RESOLUTION)
michael@0 27
michael@0 28
michael@0 29 typedef struct __struct_Options
michael@0 30 /*
michael@0 31 ** Options to control how we perform.
michael@0 32 **
michael@0 33 ** mProgramName Used in help text.
michael@0 34 ** mInputName Name of the file.
michael@0 35 ** mOutput Output file, append.
michael@0 36 ** Default is stdout.
michael@0 37 ** mOutputName Name of the file.
michael@0 38 ** mHelp Whether or not help should be shown.
michael@0 39 ** mOverhead How much overhead an allocation will have.
michael@0 40 ** mAlignment What boundry will the end of an allocation line up on.
michael@0 41 ** mPageSize Controls the page size. A page containing only fragments
michael@0 42 ** is not fragmented. A page containing any life memory
michael@0 43 ** costs mPageSize in bytes.
michael@0 44 */
michael@0 45 {
michael@0 46 const char* mProgramName;
michael@0 47 char* mInputName;
michael@0 48 FILE* mOutput;
michael@0 49 char* mOutputName;
michael@0 50 int mHelp;
michael@0 51 unsigned mOverhead;
michael@0 52 unsigned mAlignment;
michael@0 53 unsigned mPageSize;
michael@0 54 }
michael@0 55 Options;
michael@0 56
michael@0 57
michael@0 58 typedef struct __struct_Switch
michael@0 59 /*
michael@0 60 ** Command line options.
michael@0 61 */
michael@0 62 {
michael@0 63 const char* mLongName;
michael@0 64 const char* mShortName;
michael@0 65 int mHasValue;
michael@0 66 const char* mValue;
michael@0 67 const char* mDescription;
michael@0 68 }
michael@0 69 Switch;
michael@0 70
michael@0 71 #define DESC_NEWLINE "\n\t\t"
michael@0 72
michael@0 73 static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."};
michael@0 74 static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."};
michael@0 75 static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."};
michael@0 76 static Switch gAlignmentSwitch = {"--alignment", "-al", 1, NULL, "All allocation sizes are made to be a multiple of this number." DESC_NEWLINE "Closer to actual heap conditions; set to 1 for true sizes." DESC_NEWLINE "Default value is 16."};
michael@0 77 static Switch gOverheadSwitch = {"--overhead", "-ov", 1, NULL, "After alignment, all allocations are made to increase by this number." DESC_NEWLINE "Closer to actual heap conditions; set to 0 for true sizes." DESC_NEWLINE "Default value is 8."};
michael@0 78 static Switch gPageSizeSwitch = {"--page-size", "-ps", 1, NULL, "Sets the page size which aids the identification of fragmentation." DESC_NEWLINE "Closer to actual heap conditions; set to 4294967295 for true sizes." DESC_NEWLINE "Default value is 4096."};
michael@0 79
michael@0 80 static Switch* gSwitches[] = {
michael@0 81 &gInputSwitch,
michael@0 82 &gOutputSwitch,
michael@0 83 &gAlignmentSwitch,
michael@0 84 &gOverheadSwitch,
michael@0 85 &gPageSizeSwitch,
michael@0 86 &gHelpSwitch
michael@0 87 };
michael@0 88
michael@0 89
michael@0 90 typedef struct __struct_AnyArray
michael@0 91 /*
michael@0 92 ** Variable sized item array.
michael@0 93 **
michael@0 94 ** mItems The void pointer items.
michael@0 95 ** mItemSize Size of each different item.
michael@0 96 ** mCount The number of items in the array.
michael@0 97 ** mCapacity How many more items we can hold before reallocing.
michael@0 98 ** mGrowBy How many items we allocate when we grow.
michael@0 99 */
michael@0 100 {
michael@0 101 void* mItems;
michael@0 102 unsigned mItemSize;
michael@0 103 unsigned mCount;
michael@0 104 unsigned mCapacity;
michael@0 105 unsigned mGrowBy;
michael@0 106 }
michael@0 107 AnyArray;
michael@0 108
michael@0 109
michael@0 110 typedef int (*arrayMatchFunc)(void* inContext, AnyArray* inArray, void* inItem, unsigned inItemIndex)
michael@0 111 /*
michael@0 112 ** Callback function for the arrayIndexFn function.
michael@0 113 ** Used to determine an item match by customizable criteria.
michael@0 114 **
michael@0 115 ** inContext The criteria and state of the search.
michael@0 116 ** User specified/created.
michael@0 117 ** inArray The array the item is in.
michael@0 118 ** inItem The item to evaluate for match.
michael@0 119 ** inItemIndex The index of this particular item in the array.
michael@0 120 **
michael@0 121 ** return int 0 to specify a match.
michael@0 122 ** !0 to continue the search performed by arrayIndexFn.
michael@0 123 */
michael@0 124 ;
michael@0 125
michael@0 126
michael@0 127 typedef enum __enum_HeapEventType
michael@0 128 /*
michael@0 129 ** Simple heap events are really one of two things.
michael@0 130 */
michael@0 131 {
michael@0 132 FREE,
michael@0 133 ALLOC
michael@0 134 }
michael@0 135 HeapEventType;
michael@0 136
michael@0 137
michael@0 138 typedef enum __enum_HeapObjectType
michael@0 139 /*
michael@0 140 ** The various types of heap objects we track.
michael@0 141 */
michael@0 142 {
michael@0 143 ALLOCATION,
michael@0 144 FRAGMENT
michael@0 145 }
michael@0 146 HeapObjectType;
michael@0 147
michael@0 148
michael@0 149 typedef struct __struct_HeapObject HeapObject;
michael@0 150 typedef struct __struct_HeapHistory
michael@0 151 /*
michael@0 152 ** A marker as to what has happened.
michael@0 153 **
michael@0 154 ** mTimestamp When history occurred.
michael@0 155 ** mTMRSerial The historical state as known to the tmreader.
michael@0 156 ** mObjectIndex Index to the object that was before or after this event.
michael@0 157 ** The index as in the index according to all heap objects
michael@0 158 ** kept in the TMState structure.
michael@0 159 ** We use an index instead of a pointer as the array of
michael@0 160 ** objects can change location in the heap.
michael@0 161 */
michael@0 162 {
michael@0 163 unsigned mTimestamp;
michael@0 164 unsigned mTMRSerial;
michael@0 165 unsigned mObjectIndex;
michael@0 166 }
michael@0 167 HeapHistory;
michael@0 168
michael@0 169
michael@0 170 struct __struct_HeapObject
michael@0 171 /*
michael@0 172 ** An object in the heap.
michael@0 173 **
michael@0 174 ** A special case should be noted here. If either the birth or death
michael@0 175 ** history leads to an object of the same type, then this object
michael@0 176 ** is the same as that object, but was modified somehow.
michael@0 177 ** Also note that multiple objects may have the same birth object,
michael@0 178 ** as well as the same death object.
michael@0 179 **
michael@0 180 ** mUniqueID Each object is unique.
michael@0 181 ** mType Either allocation or fragment.
michael@0 182 ** mHeapOffset Where in the heap the object is.
michael@0 183 ** mSize How much of the heap the object takes.
michael@0 184 ** mBirth History about the birth event.
michael@0 185 ** mDeath History about the death event.
michael@0 186 */
michael@0 187 {
michael@0 188 unsigned mUniqueID;
michael@0 189
michael@0 190 HeapObjectType mType;
michael@0 191 unsigned mHeapOffset;
michael@0 192 unsigned mSize;
michael@0 193
michael@0 194 HeapHistory mBirth;
michael@0 195 HeapHistory mDeath;
michael@0 196 };
michael@0 197
michael@0 198
michael@0 199 typedef struct __struct_TMState
michael@0 200 /*
michael@0 201 ** State of our current operation.
michael@0 202 ** Stats we are trying to calculate.
michael@0 203 **
michael@0 204 ** mOptions Obilgatory options pointer.
michael@0 205 ** mTMR The tmreader, used in tmreader API calls.
michael@0 206 ** mLoopExitTMR Set to non zero in order to quickly exit from tmreader
michael@0 207 ** input loop. This will also result in an error.
michael@0 208 ** uMinTicks Start of run, milliseconds.
michael@0 209 ** uMaxTicks End of run, milliseconds.
michael@0 210 */
michael@0 211 {
michael@0 212 Options* mOptions;
michael@0 213 tmreader* mTMR;
michael@0 214
michael@0 215 int mLoopExitTMR;
michael@0 216
michael@0 217 unsigned uMinTicks;
michael@0 218 unsigned uMaxTicks;
michael@0 219 }
michael@0 220 TMState;
michael@0 221
michael@0 222
michael@0 223 int initOptions(Options* outOptions, int inArgc, char** inArgv)
michael@0 224 /*
michael@0 225 ** returns int 0 if successful.
michael@0 226 */
michael@0 227 {
michael@0 228 int retval = 0;
michael@0 229 int loop = 0;
michael@0 230 int switchLoop = 0;
michael@0 231 int match = 0;
michael@0 232 const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
michael@0 233 Switch* current = NULL;
michael@0 234
michael@0 235 /*
michael@0 236 ** Set any defaults.
michael@0 237 */
michael@0 238 memset(outOptions, 0, sizeof(Options));
michael@0 239 outOptions->mProgramName = inArgv[0];
michael@0 240 outOptions->mInputName = strdup("-");
michael@0 241 outOptions->mOutput = stdout;
michael@0 242 outOptions->mOutputName = strdup("stdout");
michael@0 243 outOptions->mAlignment = 16;
michael@0 244 outOptions->mOverhead = 8;
michael@0 245
michael@0 246 if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName)
michael@0 247 {
michael@0 248 retval = __LINE__;
michael@0 249 ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup.");
michael@0 250 }
michael@0 251
michael@0 252 /*
michael@0 253 ** Go through and attempt to do the right thing.
michael@0 254 */
michael@0 255 for(loop = 1; loop < inArgc && 0 == retval; loop++)
michael@0 256 {
michael@0 257 match = 0;
michael@0 258 current = NULL;
michael@0 259
michael@0 260 for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++)
michael@0 261 {
michael@0 262 if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop]))
michael@0 263 {
michael@0 264 match = __LINE__;
michael@0 265 }
michael@0 266 else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop]))
michael@0 267 {
michael@0 268 match = __LINE__;
michael@0 269 }
michael@0 270
michael@0 271 if(match)
michael@0 272 {
michael@0 273 if(gSwitches[switchLoop]->mHasValue)
michael@0 274 {
michael@0 275 /*
michael@0 276 ** Attempt to absorb next option to fullfill value.
michael@0 277 */
michael@0 278 if(loop + 1 < inArgc)
michael@0 279 {
michael@0 280 loop++;
michael@0 281
michael@0 282 current = gSwitches[switchLoop];
michael@0 283 current->mValue = inArgv[loop];
michael@0 284 }
michael@0 285 }
michael@0 286 else
michael@0 287 {
michael@0 288 current = gSwitches[switchLoop];
michael@0 289 }
michael@0 290
michael@0 291 break;
michael@0 292 }
michael@0 293 }
michael@0 294
michael@0 295 if(0 == match)
michael@0 296 {
michael@0 297 outOptions->mHelp = __LINE__;
michael@0 298 retval = __LINE__;
michael@0 299 ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch.");
michael@0 300 }
michael@0 301 else if(NULL == current)
michael@0 302 {
michael@0 303 outOptions->mHelp = __LINE__;
michael@0 304 retval = __LINE__;
michael@0 305 ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value.");
michael@0 306 }
michael@0 307 else
michael@0 308 {
michael@0 309 /*
michael@0 310 ** Do something based on address/swtich.
michael@0 311 */
michael@0 312 if(current == &gInputSwitch)
michael@0 313 {
michael@0 314 CLEANUP(outOptions->mInputName);
michael@0 315 outOptions->mInputName = strdup(current->mValue);
michael@0 316 if(NULL == outOptions->mInputName)
michael@0 317 {
michael@0 318 retval = __LINE__;
michael@0 319 ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
michael@0 320 }
michael@0 321 }
michael@0 322 else if(current == &gOutputSwitch)
michael@0 323 {
michael@0 324 CLEANUP(outOptions->mOutputName);
michael@0 325 if(NULL != outOptions->mOutput && stdout != outOptions->mOutput)
michael@0 326 {
michael@0 327 fclose(outOptions->mOutput);
michael@0 328 outOptions->mOutput = NULL;
michael@0 329 }
michael@0 330
michael@0 331 outOptions->mOutput = fopen(current->mValue, "a");
michael@0 332 if(NULL == outOptions->mOutput)
michael@0 333 {
michael@0 334 retval = __LINE__;
michael@0 335 ERROR_REPORT(retval, current->mValue, "Unable to open output file.");
michael@0 336 }
michael@0 337 else
michael@0 338 {
michael@0 339 outOptions->mOutputName = strdup(current->mValue);
michael@0 340 if(NULL == outOptions->mOutputName)
michael@0 341 {
michael@0 342 retval = __LINE__;
michael@0 343 ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
michael@0 344 }
michael@0 345 }
michael@0 346 }
michael@0 347 else if(current == &gHelpSwitch)
michael@0 348 {
michael@0 349 outOptions->mHelp = __LINE__;
michael@0 350 }
michael@0 351 else if(current == &gAlignmentSwitch)
michael@0 352 {
michael@0 353 unsigned arg = 0;
michael@0 354 char* endScan = NULL;
michael@0 355
michael@0 356 errno = 0;
michael@0 357 arg = strtoul(current->mValue, &endScan, 0);
michael@0 358 if(0 == errno && endScan != current->mValue)
michael@0 359 {
michael@0 360 outOptions->mAlignment = arg;
michael@0 361 }
michael@0 362 else
michael@0 363 {
michael@0 364 retval = __LINE__;
michael@0 365 ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
michael@0 366 }
michael@0 367 }
michael@0 368 else if(current == &gOverheadSwitch)
michael@0 369 {
michael@0 370 unsigned arg = 0;
michael@0 371 char* endScan = NULL;
michael@0 372
michael@0 373 errno = 0;
michael@0 374 arg = strtoul(current->mValue, &endScan, 0);
michael@0 375 if(0 == errno && endScan != current->mValue)
michael@0 376 {
michael@0 377 outOptions->mOverhead = arg;
michael@0 378 }
michael@0 379 else
michael@0 380 {
michael@0 381 retval = __LINE__;
michael@0 382 ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
michael@0 383 }
michael@0 384 }
michael@0 385 else if(current == &gPageSizeSwitch)
michael@0 386 {
michael@0 387 unsigned arg = 0;
michael@0 388 char* endScan = NULL;
michael@0 389
michael@0 390 errno = 0;
michael@0 391 arg = strtoul(current->mValue, &endScan, 0);
michael@0 392 if(0 == errno && endScan != current->mValue)
michael@0 393 {
michael@0 394 outOptions->mPageSize = arg;
michael@0 395 }
michael@0 396 else
michael@0 397 {
michael@0 398 retval = __LINE__;
michael@0 399 ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
michael@0 400 }
michael@0 401 }
michael@0 402 else
michael@0 403 {
michael@0 404 retval = __LINE__;
michael@0 405 ERROR_REPORT(retval, current->mLongName, "No handler for command line switch.");
michael@0 406 }
michael@0 407 }
michael@0 408 }
michael@0 409
michael@0 410 return retval;
michael@0 411 }
michael@0 412
michael@0 413
michael@0 414 uint32_t ticks2xsec(tmreader* aReader, uint32_t aTicks, uint32_t aResolution)
michael@0 415 /*
michael@0 416 ** Convert platform specific ticks to second units
michael@0 417 */
michael@0 418 {
michael@0 419 return (uint32)((aResolution * aTicks) / aReader->ticksPerSec);
michael@0 420 }
michael@0 421
michael@0 422
michael@0 423 void cleanOptions(Options* inOptions)
michael@0 424 /*
michael@0 425 ** Clean up any open handles.
michael@0 426 */
michael@0 427 {
michael@0 428 unsigned loop = 0;
michael@0 429
michael@0 430 CLEANUP(inOptions->mInputName);
michael@0 431 CLEANUP(inOptions->mOutputName);
michael@0 432 if(NULL != inOptions->mOutput && stdout != inOptions->mOutput)
michael@0 433 {
michael@0 434 fclose(inOptions->mOutput);
michael@0 435 }
michael@0 436
michael@0 437 memset(inOptions, 0, sizeof(Options));
michael@0 438 }
michael@0 439
michael@0 440
michael@0 441 void showHelp(Options* inOptions)
michael@0 442 /*
michael@0 443 ** Show some simple help text on usage.
michael@0 444 */
michael@0 445 {
michael@0 446 int loop = 0;
michael@0 447 const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
michael@0 448 const char* valueText = NULL;
michael@0 449
michael@0 450 printf("usage:\t%s [arguments]\n", inOptions->mProgramName);
michael@0 451 printf("\n");
michael@0 452 printf("arguments:\n");
michael@0 453
michael@0 454 for(loop = 0; loop < switchCount; loop++)
michael@0 455 {
michael@0 456 if(gSwitches[loop]->mHasValue)
michael@0 457 {
michael@0 458 valueText = " <value>";
michael@0 459 }
michael@0 460 else
michael@0 461 {
michael@0 462 valueText = "";
michael@0 463 }
michael@0 464
michael@0 465 printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText);
michael@0 466 printf("\t %s%s", gSwitches[loop]->mShortName, valueText);
michael@0 467 printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription);
michael@0 468 }
michael@0 469
michael@0 470 printf("This tool reports heap fragmentation stats from a trace-malloc log.\n");
michael@0 471 }
michael@0 472
michael@0 473
michael@0 474 AnyArray* arrayCreate(unsigned inItemSize, unsigned inGrowBy)
michael@0 475 /*
michael@0 476 ** Create an array container object.
michael@0 477 */
michael@0 478 {
michael@0 479 AnyArray* retval = NULL;
michael@0 480
michael@0 481 if(0 != inGrowBy && 0 != inItemSize)
michael@0 482 {
michael@0 483 retval = (AnyArray*)calloc(1, sizeof(AnyArray));
michael@0 484 retval->mItemSize = inItemSize;
michael@0 485 retval->mGrowBy = inGrowBy;
michael@0 486 }
michael@0 487
michael@0 488 return retval;
michael@0 489 }
michael@0 490
michael@0 491
michael@0 492 void arrayDestroy(AnyArray* inArray)
michael@0 493 /*
michael@0 494 ** Release the memory the array contains.
michael@0 495 ** This will release the items as well.
michael@0 496 */
michael@0 497 {
michael@0 498 if(NULL != inArray)
michael@0 499 {
michael@0 500 if(NULL != inArray->mItems)
michael@0 501 {
michael@0 502 free(inArray->mItems);
michael@0 503 }
michael@0 504 free(inArray);
michael@0 505 }
michael@0 506 }
michael@0 507
michael@0 508
michael@0 509 unsigned arrayAlloc(AnyArray* inArray, unsigned inItems)
michael@0 510 /*
michael@0 511 ** Resize the item array capcity to a specific number of items.
michael@0 512 ** This could possibly truncate the array, so handle that as well.
michael@0 513 **
michael@0 514 ** returns unsigned <= inArray->mCapacity on success.
michael@0 515 */
michael@0 516 {
michael@0 517 unsigned retval = (unsigned)-1;
michael@0 518
michael@0 519 if(NULL != inArray)
michael@0 520 {
michael@0 521 void* moved = NULL;
michael@0 522
michael@0 523 moved = realloc(inArray->mItems, inItems * inArray->mItemSize);
michael@0 524 if(NULL != moved)
michael@0 525 {
michael@0 526 inArray->mItems = moved;
michael@0 527 inArray->mCapacity = inItems;
michael@0 528 if(inArray->mCount > inItems)
michael@0 529 {
michael@0 530 inArray->mCount = inItems;
michael@0 531 }
michael@0 532
michael@0 533 retval = inItems;
michael@0 534 }
michael@0 535 }
michael@0 536
michael@0 537 return retval;
michael@0 538 }
michael@0 539
michael@0 540
michael@0 541 void* arrayItem(AnyArray* inArray, unsigned inIndex)
michael@0 542 /*
michael@0 543 ** Return the array item at said index.
michael@0 544 ** Zero based index.
michael@0 545 **
michael@0 546 ** returns void* NULL on failure.
michael@0 547 */
michael@0 548 {
michael@0 549 void* retval = NULL;
michael@0 550
michael@0 551 if(NULL != inArray && inIndex < inArray->mCount)
michael@0 552 {
michael@0 553 retval = (void*)((char*)inArray->mItems + (inArray->mItemSize * inIndex));
michael@0 554 }
michael@0 555
michael@0 556 return retval;
michael@0 557 }
michael@0 558
michael@0 559
michael@0 560 unsigned arrayIndex(AnyArray* inArray, void* inItem, unsigned inStartIndex)
michael@0 561 /*
michael@0 562 ** Go through the array from the index specified looking for an item
michael@0 563 ** match based on byte for byte comparison.
michael@0 564 ** We allow specifying the start index in order to handle arrays with
michael@0 565 ** duplicate items.
michael@0 566 **
michael@0 567 ** returns unsigned >= inArray->mCount on failure.
michael@0 568 */
michael@0 569 {
michael@0 570 unsigned retval = (unsigned)-1;
michael@0 571
michael@0 572 if(NULL != inArray && NULL != inItem && inStartIndex < inArray->mCount)
michael@0 573 {
michael@0 574 void* curItem = NULL;
michael@0 575
michael@0 576 for(retval = inStartIndex; retval < inArray->mCount; retval++)
michael@0 577 {
michael@0 578 curItem = arrayItem(inArray, retval);
michael@0 579 if(0 == memcmp(inItem, curItem, inArray->mItemSize))
michael@0 580 {
michael@0 581 break;
michael@0 582 }
michael@0 583 }
michael@0 584 }
michael@0 585
michael@0 586
michael@0 587 return retval;
michael@0 588 }
michael@0 589
michael@0 590
michael@0 591 unsigned arrayIndexFn(AnyArray* inArray, arrayMatchFunc inFunc, void* inFuncContext, unsigned inStartIndex)
michael@0 592 /*
michael@0 593 ** Go through the array from the index specified looking for an item
michael@0 594 ** match based upon the return value of inFunc (0, Zero, is a match).
michael@0 595 ** We allow specifying the start index in order to facilitate looping over
michael@0 596 ** the array which could have multiple matches.
michael@0 597 **
michael@0 598 ** returns unsigned >= inArray->mCount on failure.
michael@0 599 */
michael@0 600 {
michael@0 601 unsigned retval = (unsigned)-1;
michael@0 602
michael@0 603 if(NULL != inArray && NULL != inFunc && inStartIndex < inArray->mCount)
michael@0 604 {
michael@0 605 void* curItem = NULL;
michael@0 606
michael@0 607 for(retval = inStartIndex; retval < inArray->mCount; retval++)
michael@0 608 {
michael@0 609 curItem = arrayItem(inArray, retval);
michael@0 610 if(0 == inFunc(inFuncContext, inArray, curItem, retval))
michael@0 611 {
michael@0 612 break;
michael@0 613 }
michael@0 614 }
michael@0 615 }
michael@0 616
michael@0 617 return retval;
michael@0 618 }
michael@0 619
michael@0 620
michael@0 621 unsigned arrayAddItem(AnyArray* inArray, void* inItem)
michael@0 622 /*
michael@0 623 ** Add a new item to the array.
michael@0 624 ** This is done by copying the item.
michael@0 625 **
michael@0 626 ** returns unsigned < inArray->mCount on success.
michael@0 627 */
michael@0 628 {
michael@0 629 unsigned retval = (unsigned)-1;
michael@0 630
michael@0 631 if(NULL != inArray && NULL != inItem)
michael@0 632 {
michael@0 633 int noCopy = 0;
michael@0 634
michael@0 635 /*
michael@0 636 ** See if the array should grow.
michael@0 637 */
michael@0 638 if(inArray->mCount == inArray->mCapacity)
michael@0 639 {
michael@0 640 unsigned allocRes = 0;
michael@0 641
michael@0 642 allocRes = arrayAlloc(inArray, inArray->mCapacity + inArray->mGrowBy);
michael@0 643 if(allocRes > inArray->mCapacity)
michael@0 644 {
michael@0 645 noCopy = __LINE__;
michael@0 646 }
michael@0 647 }
michael@0 648
michael@0 649 if(0 == noCopy)
michael@0 650 {
michael@0 651 retval = inArray->mCount;
michael@0 652
michael@0 653 inArray->mCount++;
michael@0 654 memcpy(arrayItem(inArray, retval), inItem, inArray->mItemSize);
michael@0 655 }
michael@0 656 }
michael@0 657
michael@0 658 return retval;
michael@0 659 }
michael@0 660
michael@0 661
michael@0 662 HeapObject* initHeapObject(HeapObject* inObject)
michael@0 663 /*
michael@0 664 ** Function to init the heap object just right.
michael@0 665 ** Sets the unique ID to something unique.
michael@0 666 */
michael@0 667 {
michael@0 668 HeapObject* retval = inObject;
michael@0 669
michael@0 670 if(NULL != inObject)
michael@0 671 {
michael@0 672 static unsigned uniqueGenerator = 0;
michael@0 673
michael@0 674 memset(inObject, -1, sizeof(HeapObject));
michael@0 675
michael@0 676 inObject->mUniqueID = uniqueGenerator;
michael@0 677 uniqueGenerator++;
michael@0 678 }
michael@0 679
michael@0 680 return retval;
michael@0 681 }
michael@0 682
michael@0 683
michael@0 684 int simpleHeapEvent(TMState* inStats, HeapEventType inType, unsigned mTimestamp, unsigned inSerial, unsigned inHeapID, unsigned inSize)
michael@0 685 /*
michael@0 686 ** A new heap event will cause the creation of a new heap object.
michael@0 687 ** The new heap object will displace, or replace, a heap object of a different type.
michael@0 688 */
michael@0 689 {
michael@0 690 int retval = 0;
michael@0 691 HeapObject newObject;
michael@0 692
michael@0 693 /*
michael@0 694 ** Set the most basic object details.
michael@0 695 */
michael@0 696 initHeapObject(&newObject);
michael@0 697 newObject.mHeapOffset = inHeapID;
michael@0 698 newObject.mSize = inSize;
michael@0 699 if(FREE == inType)
michael@0 700 {
michael@0 701 newObject.mType = FRAGMENT;
michael@0 702 }
michael@0 703 else if(ALLOC == inType)
michael@0 704 {
michael@0 705 newObject.mType = ALLOCATION;
michael@0 706 }
michael@0 707
michael@0 708 /*
michael@0 709 ** Add it to the heap object array.
michael@0 710 */
michael@0 711
michael@0 712 /*
michael@0 713 ** TODO GAB
michael@0 714 **
michael@0 715 ** First thing to do is to add the new object to the heap in order to
michael@0 716 ** obtain a valid index.
michael@0 717 **
michael@0 718 ** Next, find all matches to this range of heap memory that this event
michael@0 719 ** refers to, that are alive during this timestamp (no death yet).
michael@0 720 ** Fill in the death event of those objects.
michael@0 721 ** If the objects contain some portions outside of the range, then
michael@0 722 ** new objects for those ranges need to be created that carry on
michael@0 723 ** the same object type, have the index of the old object for birth,
michael@0 724 ** and the serial of the old object, new timestamp of course.
michael@0 725 ** The old object's death points to the new object, which tells why the
michael@0 726 ** fragmentation took place.
michael@0 727 ** The new object birth points to the old object only if a fragment.
michael@0 728 ** An allocation only has a birth object when it is a realloc (complex)
michael@0 729 ** heap event.
michael@0 730 **
michael@0 731 ** I believe this give us enough information to look up particular
michael@0 732 ** details of the heap at any given time.
michael@0 733 */
michael@0 734
michael@0 735 return retval;
michael@0 736 }
michael@0 737
michael@0 738
michael@0 739 int complexHeapEvent(TMState* inStats, unsigned mTimestamp, unsigned inOldSerial, unsigned inOldHeapID, unsigned inOSize, unsigned inNewSerial, unsigned inNewHeapID, unsigned inNewSize)
michael@0 740 /*
michael@0 741 ** Generally, this event intends to chain one old heap object to a newer heap object.
michael@0 742 ** Otherwise, the functionality should recognizable ala simpleHeapEvent.
michael@0 743 */
michael@0 744 {
michael@0 745 int retval = 0;
michael@0 746
michael@0 747 /*
michael@0 748 ** TODO GAB
michael@0 749 */
michael@0 750
michael@0 751 return retval;
michael@0 752 }
michael@0 753
michael@0 754
michael@0 755 unsigned actualByteSize(Options* inOptions, unsigned retval)
michael@0 756 /*
michael@0 757 ** Apply alignment and overhead to size to figure out actual byte size.
michael@0 758 ** This by default mimics spacetrace with default options (msvc crt heap).
michael@0 759 */
michael@0 760 {
michael@0 761 if(0 != retval)
michael@0 762 {
michael@0 763 unsigned eval = 0;
michael@0 764 unsigned over = 0;
michael@0 765
michael@0 766 eval = retval - 1;
michael@0 767 if(0 != inOptions->mAlignment)
michael@0 768 {
michael@0 769 over = eval % inOptions->mAlignment;
michael@0 770 }
michael@0 771 retval = eval + inOptions->mOverhead + inOptions->mAlignment - over;
michael@0 772 }
michael@0 773
michael@0 774 return retval;
michael@0 775 }
michael@0 776
michael@0 777
michael@0 778 void tmEventHandler(tmreader* inReader, tmevent* inEvent)
michael@0 779 /*
michael@0 780 ** Callback from the tmreader_eventloop.
michael@0 781 ** Build up our fragmentation information herein.
michael@0 782 */
michael@0 783 {
michael@0 784 char type = inEvent->type;
michael@0 785 TMState* stats = (TMState*)inReader->data;
michael@0 786
michael@0 787 /*
michael@0 788 ** Only intersted in handling events of a particular type.
michael@0 789 */
michael@0 790 switch(type)
michael@0 791 {
michael@0 792 default:
michael@0 793 return;
michael@0 794
michael@0 795 case TM_EVENT_MALLOC:
michael@0 796 case TM_EVENT_CALLOC:
michael@0 797 case TM_EVENT_REALLOC:
michael@0 798 case TM_EVENT_FREE:
michael@0 799 break;
michael@0 800 }
michael@0 801
michael@0 802 /*
michael@0 803 ** Should we even try to look?
michael@0 804 ** Set mLoopExitTMR to non-zero to abort the read loop faster.
michael@0 805 */
michael@0 806 if(0 == stats->mLoopExitTMR)
michael@0 807 {
michael@0 808 Options* options = (Options*)stats->mOptions;
michael@0 809 unsigned timestamp = ticks2msec(stats->mTMR, inEvent->u.alloc.interval);
michael@0 810 unsigned actualSize = actualByteSize(options, inEvent->u.alloc.size);
michael@0 811 unsigned heapID = inEvent->u.alloc.ptr;
michael@0 812 unsigned serial = inEvent->serial;
michael@0 813
michael@0 814 /*
michael@0 815 ** Check the timestamp range of our overall state.
michael@0 816 */
michael@0 817 if(stats->uMinTicks > timestamp)
michael@0 818 {
michael@0 819 stats->uMinTicks = timestamp;
michael@0 820 }
michael@0 821 if(stats->uMaxTicks < timestamp)
michael@0 822 {
michael@0 823 stats->uMaxTicks = timestamp;
michael@0 824 }
michael@0 825
michael@0 826 /*
michael@0 827 ** Realloc in general deserves some special attention if dealing
michael@0 828 ** with an old allocation (not new memory).
michael@0 829 */
michael@0 830 if(TM_EVENT_REALLOC == type && 0 != inEvent->u.alloc.oldserial)
michael@0 831 {
michael@0 832 unsigned oldActualSize = actualByteSize(options, inEvent->u.alloc.oldsize);
michael@0 833 unsigned oldHeapID = inEvent->u.alloc.oldptr;
michael@0 834 unsigned oldSerial = inEvent->u.alloc.oldserial;
michael@0 835
michael@0 836 if(0 == actualSize)
michael@0 837 {
michael@0 838 /*
michael@0 839 ** Reallocs of size zero are to become free events.
michael@0 840 */
michael@0 841 stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, oldHeapID, oldActualSize);
michael@0 842 }
michael@0 843 else if(heapID != oldHeapID || actualSize != oldActualSize)
michael@0 844 {
michael@0 845 /*
michael@0 846 ** Reallocs which moved generate two events.
michael@0 847 ** Reallocs which changed size generate two events.
michael@0 848 **
michael@0 849 ** One event to free the old memory area.
michael@0 850 ** Another event to allocate the new memory area.
michael@0 851 ** They are to be linked to one another, so the history
michael@0 852 ** and true origin can be tracked.
michael@0 853 */
michael@0 854 stats->mLoopExitTMR = complexHeapEvent(stats, timestamp, oldSerial, oldHeapID, oldActualSize, serial, heapID, actualSize);
michael@0 855 }
michael@0 856 else
michael@0 857 {
michael@0 858 /*
michael@0 859 ** The realloc is not considered an operation and is skipped.
michael@0 860 ** It is not an operation, because it did not move or change
michael@0 861 ** size; this can happen if a realloc falls within the
michael@0 862 ** alignment of an allocation.
michael@0 863 ** Say if you realloc a 1 byte allocation to 2 bytes, it will
michael@0 864 ** not really change heap impact unless you have 1 set as
michael@0 865 ** the alignment of your allocations.
michael@0 866 */
michael@0 867 }
michael@0 868 }
michael@0 869 else if(TM_EVENT_FREE == type)
michael@0 870 {
michael@0 871 /*
michael@0 872 ** Generate a free event to create a fragment.
michael@0 873 */
michael@0 874 stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, heapID, actualSize);
michael@0 875 }
michael@0 876 else
michael@0 877 {
michael@0 878 /*
michael@0 879 ** Generate an allocation event to clear fragments.
michael@0 880 */
michael@0 881 stats->mLoopExitTMR = simpleHeapEvent(stats, ALLOC, timestamp, serial, heapID, actualSize);
michael@0 882 }
michael@0 883 }
michael@0 884 }
michael@0 885
michael@0 886
michael@0 887 int tmfrags(Options* inOptions)
michael@0 888 /*
michael@0 889 ** Load the input file and report stats.
michael@0 890 */
michael@0 891 {
michael@0 892 int retval = 0;
michael@0 893 TMState stats;
michael@0 894
michael@0 895 memset(&stats, 0, sizeof(stats));
michael@0 896 stats.mOptions = inOptions;
michael@0 897 stats.uMinTicks = 0xFFFFFFFFU;
michael@0 898
michael@0 899 /*
michael@0 900 ** Need a tmreader.
michael@0 901 */
michael@0 902 stats.mTMR = tmreader_new(inOptions->mProgramName, &stats);
michael@0 903 if(NULL != stats.mTMR)
michael@0 904 {
michael@0 905 int tmResult = 0;
michael@0 906
michael@0 907 tmResult = tmreader_eventloop(stats.mTMR, inOptions->mInputName, tmEventHandler);
michael@0 908 if(0 == tmResult)
michael@0 909 {
michael@0 910 retval = __LINE__;
michael@0 911 ERROR_REPORT(retval, inOptions->mInputName, "Problem reading trace-malloc data.");
michael@0 912 }
michael@0 913 if(0 != stats.mLoopExitTMR)
michael@0 914 {
michael@0 915 retval = stats.mLoopExitTMR;
michael@0 916 ERROR_REPORT(retval, inOptions->mInputName, "Aborted trace-malloc input loop.");
michael@0 917 }
michael@0 918
michael@0 919 tmreader_destroy(stats.mTMR);
michael@0 920 stats.mTMR = NULL;
michael@0 921 }
michael@0 922 else
michael@0 923 {
michael@0 924 retval = __LINE__;
michael@0 925 ERROR_REPORT(retval, inOptions->mProgramName, "Unable to obtain tmreader.");
michael@0 926 }
michael@0 927
michael@0 928 return retval;
michael@0 929 }
michael@0 930
michael@0 931
michael@0 932 int main(int inArgc, char** inArgv)
michael@0 933 {
michael@0 934 int retval = 0;
michael@0 935 Options options;
michael@0 936
michael@0 937 retval = initOptions(&options, inArgc, inArgv);
michael@0 938 if(options.mHelp)
michael@0 939 {
michael@0 940 showHelp(&options);
michael@0 941 }
michael@0 942 else if(0 == retval)
michael@0 943 {
michael@0 944 retval = tmfrags(&options);
michael@0 945 }
michael@0 946
michael@0 947 cleanOptions(&options);
michael@0 948 return retval;
michael@0 949 }
michael@0 950

mercurial