1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/trace-malloc/tmfrags.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,950 @@ 1.4 +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <stdio.h> 1.11 +#include <stdlib.h> 1.12 +#include <string.h> 1.13 +#include <time.h> 1.14 +#include <ctype.h> 1.15 +#include <errno.h> 1.16 +#include <math.h> 1.17 + 1.18 +#include "nspr.h" 1.19 +#include "tmreader.h" 1.20 + 1.21 + 1.22 +#define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); 1.23 +#define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) 1.24 + 1.25 + 1.26 +#define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000) 1.27 +#define ticks2usec(reader, ticks) ticks2xsec((reader), (ticks), 1000000) 1.28 +#define TICK_RESOLUTION 1000 1.29 +#define TICK_PRINTABLE(timeval) ((double)(timeval) / (double)ST_TIMEVAL_RESOLUTION) 1.30 + 1.31 + 1.32 +typedef struct __struct_Options 1.33 +/* 1.34 +** Options to control how we perform. 1.35 +** 1.36 +** mProgramName Used in help text. 1.37 +** mInputName Name of the file. 1.38 +** mOutput Output file, append. 1.39 +** Default is stdout. 1.40 +** mOutputName Name of the file. 1.41 +** mHelp Whether or not help should be shown. 1.42 +** mOverhead How much overhead an allocation will have. 1.43 +** mAlignment What boundry will the end of an allocation line up on. 1.44 +** mPageSize Controls the page size. A page containing only fragments 1.45 +** is not fragmented. A page containing any life memory 1.46 +** costs mPageSize in bytes. 1.47 +*/ 1.48 +{ 1.49 + const char* mProgramName; 1.50 + char* mInputName; 1.51 + FILE* mOutput; 1.52 + char* mOutputName; 1.53 + int mHelp; 1.54 + unsigned mOverhead; 1.55 + unsigned mAlignment; 1.56 + unsigned mPageSize; 1.57 +} 1.58 +Options; 1.59 + 1.60 + 1.61 +typedef struct __struct_Switch 1.62 +/* 1.63 +** Command line options. 1.64 +*/ 1.65 +{ 1.66 + const char* mLongName; 1.67 + const char* mShortName; 1.68 + int mHasValue; 1.69 + const char* mValue; 1.70 + const char* mDescription; 1.71 +} 1.72 +Switch; 1.73 + 1.74 +#define DESC_NEWLINE "\n\t\t" 1.75 + 1.76 +static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; 1.77 +static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; 1.78 +static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; 1.79 +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."}; 1.80 +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."}; 1.81 +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."}; 1.82 + 1.83 +static Switch* gSwitches[] = { 1.84 + &gInputSwitch, 1.85 + &gOutputSwitch, 1.86 + &gAlignmentSwitch, 1.87 + &gOverheadSwitch, 1.88 + &gPageSizeSwitch, 1.89 + &gHelpSwitch 1.90 +}; 1.91 + 1.92 + 1.93 +typedef struct __struct_AnyArray 1.94 +/* 1.95 +** Variable sized item array. 1.96 +** 1.97 +** mItems The void pointer items. 1.98 +** mItemSize Size of each different item. 1.99 +** mCount The number of items in the array. 1.100 +** mCapacity How many more items we can hold before reallocing. 1.101 +** mGrowBy How many items we allocate when we grow. 1.102 +*/ 1.103 +{ 1.104 + void* mItems; 1.105 + unsigned mItemSize; 1.106 + unsigned mCount; 1.107 + unsigned mCapacity; 1.108 + unsigned mGrowBy; 1.109 +} 1.110 +AnyArray; 1.111 + 1.112 + 1.113 +typedef int (*arrayMatchFunc)(void* inContext, AnyArray* inArray, void* inItem, unsigned inItemIndex) 1.114 +/* 1.115 +** Callback function for the arrayIndexFn function. 1.116 +** Used to determine an item match by customizable criteria. 1.117 +** 1.118 +** inContext The criteria and state of the search. 1.119 +** User specified/created. 1.120 +** inArray The array the item is in. 1.121 +** inItem The item to evaluate for match. 1.122 +** inItemIndex The index of this particular item in the array. 1.123 +** 1.124 +** return int 0 to specify a match. 1.125 +** !0 to continue the search performed by arrayIndexFn. 1.126 +*/ 1.127 +; 1.128 + 1.129 + 1.130 +typedef enum __enum_HeapEventType 1.131 +/* 1.132 +** Simple heap events are really one of two things. 1.133 +*/ 1.134 +{ 1.135 + FREE, 1.136 + ALLOC 1.137 +} 1.138 +HeapEventType; 1.139 + 1.140 + 1.141 +typedef enum __enum_HeapObjectType 1.142 +/* 1.143 +** The various types of heap objects we track. 1.144 +*/ 1.145 +{ 1.146 + ALLOCATION, 1.147 + FRAGMENT 1.148 +} 1.149 +HeapObjectType; 1.150 + 1.151 + 1.152 +typedef struct __struct_HeapObject HeapObject; 1.153 +typedef struct __struct_HeapHistory 1.154 +/* 1.155 +** A marker as to what has happened. 1.156 +** 1.157 +** mTimestamp When history occurred. 1.158 +** mTMRSerial The historical state as known to the tmreader. 1.159 +** mObjectIndex Index to the object that was before or after this event. 1.160 +** The index as in the index according to all heap objects 1.161 +** kept in the TMState structure. 1.162 +** We use an index instead of a pointer as the array of 1.163 +** objects can change location in the heap. 1.164 +*/ 1.165 +{ 1.166 + unsigned mTimestamp; 1.167 + unsigned mTMRSerial; 1.168 + unsigned mObjectIndex; 1.169 +} 1.170 +HeapHistory; 1.171 + 1.172 + 1.173 +struct __struct_HeapObject 1.174 +/* 1.175 +** An object in the heap. 1.176 +** 1.177 +** A special case should be noted here. If either the birth or death 1.178 +** history leads to an object of the same type, then this object 1.179 +** is the same as that object, but was modified somehow. 1.180 +** Also note that multiple objects may have the same birth object, 1.181 +** as well as the same death object. 1.182 +** 1.183 +** mUniqueID Each object is unique. 1.184 +** mType Either allocation or fragment. 1.185 +** mHeapOffset Where in the heap the object is. 1.186 +** mSize How much of the heap the object takes. 1.187 +** mBirth History about the birth event. 1.188 +** mDeath History about the death event. 1.189 +*/ 1.190 +{ 1.191 + unsigned mUniqueID; 1.192 + 1.193 + HeapObjectType mType; 1.194 + unsigned mHeapOffset; 1.195 + unsigned mSize; 1.196 + 1.197 + HeapHistory mBirth; 1.198 + HeapHistory mDeath; 1.199 +}; 1.200 + 1.201 + 1.202 +typedef struct __struct_TMState 1.203 +/* 1.204 +** State of our current operation. 1.205 +** Stats we are trying to calculate. 1.206 +** 1.207 +** mOptions Obilgatory options pointer. 1.208 +** mTMR The tmreader, used in tmreader API calls. 1.209 +** mLoopExitTMR Set to non zero in order to quickly exit from tmreader 1.210 +** input loop. This will also result in an error. 1.211 +** uMinTicks Start of run, milliseconds. 1.212 +** uMaxTicks End of run, milliseconds. 1.213 +*/ 1.214 +{ 1.215 + Options* mOptions; 1.216 + tmreader* mTMR; 1.217 + 1.218 + int mLoopExitTMR; 1.219 + 1.220 + unsigned uMinTicks; 1.221 + unsigned uMaxTicks; 1.222 +} 1.223 +TMState; 1.224 + 1.225 + 1.226 +int initOptions(Options* outOptions, int inArgc, char** inArgv) 1.227 +/* 1.228 +** returns int 0 if successful. 1.229 +*/ 1.230 +{ 1.231 + int retval = 0; 1.232 + int loop = 0; 1.233 + int switchLoop = 0; 1.234 + int match = 0; 1.235 + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); 1.236 + Switch* current = NULL; 1.237 + 1.238 + /* 1.239 + ** Set any defaults. 1.240 + */ 1.241 + memset(outOptions, 0, sizeof(Options)); 1.242 + outOptions->mProgramName = inArgv[0]; 1.243 + outOptions->mInputName = strdup("-"); 1.244 + outOptions->mOutput = stdout; 1.245 + outOptions->mOutputName = strdup("stdout"); 1.246 + outOptions->mAlignment = 16; 1.247 + outOptions->mOverhead = 8; 1.248 + 1.249 + if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) 1.250 + { 1.251 + retval = __LINE__; 1.252 + ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); 1.253 + } 1.254 + 1.255 + /* 1.256 + ** Go through and attempt to do the right thing. 1.257 + */ 1.258 + for(loop = 1; loop < inArgc && 0 == retval; loop++) 1.259 + { 1.260 + match = 0; 1.261 + current = NULL; 1.262 + 1.263 + for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) 1.264 + { 1.265 + if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) 1.266 + { 1.267 + match = __LINE__; 1.268 + } 1.269 + else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) 1.270 + { 1.271 + match = __LINE__; 1.272 + } 1.273 + 1.274 + if(match) 1.275 + { 1.276 + if(gSwitches[switchLoop]->mHasValue) 1.277 + { 1.278 + /* 1.279 + ** Attempt to absorb next option to fullfill value. 1.280 + */ 1.281 + if(loop + 1 < inArgc) 1.282 + { 1.283 + loop++; 1.284 + 1.285 + current = gSwitches[switchLoop]; 1.286 + current->mValue = inArgv[loop]; 1.287 + } 1.288 + } 1.289 + else 1.290 + { 1.291 + current = gSwitches[switchLoop]; 1.292 + } 1.293 + 1.294 + break; 1.295 + } 1.296 + } 1.297 + 1.298 + if(0 == match) 1.299 + { 1.300 + outOptions->mHelp = __LINE__; 1.301 + retval = __LINE__; 1.302 + ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); 1.303 + } 1.304 + else if(NULL == current) 1.305 + { 1.306 + outOptions->mHelp = __LINE__; 1.307 + retval = __LINE__; 1.308 + ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); 1.309 + } 1.310 + else 1.311 + { 1.312 + /* 1.313 + ** Do something based on address/swtich. 1.314 + */ 1.315 + if(current == &gInputSwitch) 1.316 + { 1.317 + CLEANUP(outOptions->mInputName); 1.318 + outOptions->mInputName = strdup(current->mValue); 1.319 + if(NULL == outOptions->mInputName) 1.320 + { 1.321 + retval = __LINE__; 1.322 + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); 1.323 + } 1.324 + } 1.325 + else if(current == &gOutputSwitch) 1.326 + { 1.327 + CLEANUP(outOptions->mOutputName); 1.328 + if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) 1.329 + { 1.330 + fclose(outOptions->mOutput); 1.331 + outOptions->mOutput = NULL; 1.332 + } 1.333 + 1.334 + outOptions->mOutput = fopen(current->mValue, "a"); 1.335 + if(NULL == outOptions->mOutput) 1.336 + { 1.337 + retval = __LINE__; 1.338 + ERROR_REPORT(retval, current->mValue, "Unable to open output file."); 1.339 + } 1.340 + else 1.341 + { 1.342 + outOptions->mOutputName = strdup(current->mValue); 1.343 + if(NULL == outOptions->mOutputName) 1.344 + { 1.345 + retval = __LINE__; 1.346 + ERROR_REPORT(retval, current->mValue, "Unable to strdup."); 1.347 + } 1.348 + } 1.349 + } 1.350 + else if(current == &gHelpSwitch) 1.351 + { 1.352 + outOptions->mHelp = __LINE__; 1.353 + } 1.354 + else if(current == &gAlignmentSwitch) 1.355 + { 1.356 + unsigned arg = 0; 1.357 + char* endScan = NULL; 1.358 + 1.359 + errno = 0; 1.360 + arg = strtoul(current->mValue, &endScan, 0); 1.361 + if(0 == errno && endScan != current->mValue) 1.362 + { 1.363 + outOptions->mAlignment = arg; 1.364 + } 1.365 + else 1.366 + { 1.367 + retval = __LINE__; 1.368 + ERROR_REPORT(retval, current->mValue, "Unable to convert to a number."); 1.369 + } 1.370 + } 1.371 + else if(current == &gOverheadSwitch) 1.372 + { 1.373 + unsigned arg = 0; 1.374 + char* endScan = NULL; 1.375 + 1.376 + errno = 0; 1.377 + arg = strtoul(current->mValue, &endScan, 0); 1.378 + if(0 == errno && endScan != current->mValue) 1.379 + { 1.380 + outOptions->mOverhead = arg; 1.381 + } 1.382 + else 1.383 + { 1.384 + retval = __LINE__; 1.385 + ERROR_REPORT(retval, current->mValue, "Unable to convert to a number."); 1.386 + } 1.387 + } 1.388 + else if(current == &gPageSizeSwitch) 1.389 + { 1.390 + unsigned arg = 0; 1.391 + char* endScan = NULL; 1.392 + 1.393 + errno = 0; 1.394 + arg = strtoul(current->mValue, &endScan, 0); 1.395 + if(0 == errno && endScan != current->mValue) 1.396 + { 1.397 + outOptions->mPageSize = arg; 1.398 + } 1.399 + else 1.400 + { 1.401 + retval = __LINE__; 1.402 + ERROR_REPORT(retval, current->mValue, "Unable to convert to a number."); 1.403 + } 1.404 + } 1.405 + else 1.406 + { 1.407 + retval = __LINE__; 1.408 + ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); 1.409 + } 1.410 + } 1.411 + } 1.412 + 1.413 + return retval; 1.414 +} 1.415 + 1.416 + 1.417 +uint32_t ticks2xsec(tmreader* aReader, uint32_t aTicks, uint32_t aResolution) 1.418 +/* 1.419 +** Convert platform specific ticks to second units 1.420 +*/ 1.421 +{ 1.422 + return (uint32)((aResolution * aTicks) / aReader->ticksPerSec); 1.423 +} 1.424 + 1.425 + 1.426 +void cleanOptions(Options* inOptions) 1.427 +/* 1.428 +** Clean up any open handles. 1.429 +*/ 1.430 +{ 1.431 + unsigned loop = 0; 1.432 + 1.433 + CLEANUP(inOptions->mInputName); 1.434 + CLEANUP(inOptions->mOutputName); 1.435 + if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) 1.436 + { 1.437 + fclose(inOptions->mOutput); 1.438 + } 1.439 + 1.440 + memset(inOptions, 0, sizeof(Options)); 1.441 +} 1.442 + 1.443 + 1.444 +void showHelp(Options* inOptions) 1.445 +/* 1.446 +** Show some simple help text on usage. 1.447 +*/ 1.448 +{ 1.449 + int loop = 0; 1.450 + const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); 1.451 + const char* valueText = NULL; 1.452 + 1.453 + printf("usage:\t%s [arguments]\n", inOptions->mProgramName); 1.454 + printf("\n"); 1.455 + printf("arguments:\n"); 1.456 + 1.457 + for(loop = 0; loop < switchCount; loop++) 1.458 + { 1.459 + if(gSwitches[loop]->mHasValue) 1.460 + { 1.461 + valueText = " <value>"; 1.462 + } 1.463 + else 1.464 + { 1.465 + valueText = ""; 1.466 + } 1.467 + 1.468 + printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); 1.469 + printf("\t %s%s", gSwitches[loop]->mShortName, valueText); 1.470 + printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); 1.471 + } 1.472 + 1.473 + printf("This tool reports heap fragmentation stats from a trace-malloc log.\n"); 1.474 +} 1.475 + 1.476 + 1.477 +AnyArray* arrayCreate(unsigned inItemSize, unsigned inGrowBy) 1.478 +/* 1.479 +** Create an array container object. 1.480 +*/ 1.481 +{ 1.482 + AnyArray* retval = NULL; 1.483 + 1.484 + if(0 != inGrowBy && 0 != inItemSize) 1.485 + { 1.486 + retval = (AnyArray*)calloc(1, sizeof(AnyArray)); 1.487 + retval->mItemSize = inItemSize; 1.488 + retval->mGrowBy = inGrowBy; 1.489 + } 1.490 + 1.491 + return retval; 1.492 +} 1.493 + 1.494 + 1.495 +void arrayDestroy(AnyArray* inArray) 1.496 +/* 1.497 +** Release the memory the array contains. 1.498 +** This will release the items as well. 1.499 +*/ 1.500 +{ 1.501 + if(NULL != inArray) 1.502 + { 1.503 + if(NULL != inArray->mItems) 1.504 + { 1.505 + free(inArray->mItems); 1.506 + } 1.507 + free(inArray); 1.508 + } 1.509 +} 1.510 + 1.511 + 1.512 +unsigned arrayAlloc(AnyArray* inArray, unsigned inItems) 1.513 +/* 1.514 +** Resize the item array capcity to a specific number of items. 1.515 +** This could possibly truncate the array, so handle that as well. 1.516 +** 1.517 +** returns unsigned <= inArray->mCapacity on success. 1.518 +*/ 1.519 +{ 1.520 + unsigned retval = (unsigned)-1; 1.521 + 1.522 + if(NULL != inArray) 1.523 + { 1.524 + void* moved = NULL; 1.525 + 1.526 + moved = realloc(inArray->mItems, inItems * inArray->mItemSize); 1.527 + if(NULL != moved) 1.528 + { 1.529 + inArray->mItems = moved; 1.530 + inArray->mCapacity = inItems; 1.531 + if(inArray->mCount > inItems) 1.532 + { 1.533 + inArray->mCount = inItems; 1.534 + } 1.535 + 1.536 + retval = inItems; 1.537 + } 1.538 + } 1.539 + 1.540 + return retval; 1.541 +} 1.542 + 1.543 + 1.544 +void* arrayItem(AnyArray* inArray, unsigned inIndex) 1.545 +/* 1.546 +** Return the array item at said index. 1.547 +** Zero based index. 1.548 +** 1.549 +** returns void* NULL on failure. 1.550 +*/ 1.551 +{ 1.552 + void* retval = NULL; 1.553 + 1.554 + if(NULL != inArray && inIndex < inArray->mCount) 1.555 + { 1.556 + retval = (void*)((char*)inArray->mItems + (inArray->mItemSize * inIndex)); 1.557 + } 1.558 + 1.559 + return retval; 1.560 +} 1.561 + 1.562 + 1.563 +unsigned arrayIndex(AnyArray* inArray, void* inItem, unsigned inStartIndex) 1.564 +/* 1.565 +** Go through the array from the index specified looking for an item 1.566 +** match based on byte for byte comparison. 1.567 +** We allow specifying the start index in order to handle arrays with 1.568 +** duplicate items. 1.569 +** 1.570 +** returns unsigned >= inArray->mCount on failure. 1.571 +*/ 1.572 +{ 1.573 + unsigned retval = (unsigned)-1; 1.574 + 1.575 + if(NULL != inArray && NULL != inItem && inStartIndex < inArray->mCount) 1.576 + { 1.577 + void* curItem = NULL; 1.578 + 1.579 + for(retval = inStartIndex; retval < inArray->mCount; retval++) 1.580 + { 1.581 + curItem = arrayItem(inArray, retval); 1.582 + if(0 == memcmp(inItem, curItem, inArray->mItemSize)) 1.583 + { 1.584 + break; 1.585 + } 1.586 + } 1.587 + } 1.588 + 1.589 + 1.590 + return retval; 1.591 +} 1.592 + 1.593 + 1.594 +unsigned arrayIndexFn(AnyArray* inArray, arrayMatchFunc inFunc, void* inFuncContext, unsigned inStartIndex) 1.595 +/* 1.596 +** Go through the array from the index specified looking for an item 1.597 +** match based upon the return value of inFunc (0, Zero, is a match). 1.598 +** We allow specifying the start index in order to facilitate looping over 1.599 +** the array which could have multiple matches. 1.600 +** 1.601 +** returns unsigned >= inArray->mCount on failure. 1.602 +*/ 1.603 +{ 1.604 + unsigned retval = (unsigned)-1; 1.605 + 1.606 + if(NULL != inArray && NULL != inFunc && inStartIndex < inArray->mCount) 1.607 + { 1.608 + void* curItem = NULL; 1.609 + 1.610 + for(retval = inStartIndex; retval < inArray->mCount; retval++) 1.611 + { 1.612 + curItem = arrayItem(inArray, retval); 1.613 + if(0 == inFunc(inFuncContext, inArray, curItem, retval)) 1.614 + { 1.615 + break; 1.616 + } 1.617 + } 1.618 + } 1.619 + 1.620 + return retval; 1.621 +} 1.622 + 1.623 + 1.624 +unsigned arrayAddItem(AnyArray* inArray, void* inItem) 1.625 +/* 1.626 +** Add a new item to the array. 1.627 +** This is done by copying the item. 1.628 +** 1.629 +** returns unsigned < inArray->mCount on success. 1.630 +*/ 1.631 +{ 1.632 + unsigned retval = (unsigned)-1; 1.633 + 1.634 + if(NULL != inArray && NULL != inItem) 1.635 + { 1.636 + int noCopy = 0; 1.637 + 1.638 + /* 1.639 + ** See if the array should grow. 1.640 + */ 1.641 + if(inArray->mCount == inArray->mCapacity) 1.642 + { 1.643 + unsigned allocRes = 0; 1.644 + 1.645 + allocRes = arrayAlloc(inArray, inArray->mCapacity + inArray->mGrowBy); 1.646 + if(allocRes > inArray->mCapacity) 1.647 + { 1.648 + noCopy = __LINE__; 1.649 + } 1.650 + } 1.651 + 1.652 + if(0 == noCopy) 1.653 + { 1.654 + retval = inArray->mCount; 1.655 + 1.656 + inArray->mCount++; 1.657 + memcpy(arrayItem(inArray, retval), inItem, inArray->mItemSize); 1.658 + } 1.659 + } 1.660 + 1.661 + return retval; 1.662 +} 1.663 + 1.664 + 1.665 +HeapObject* initHeapObject(HeapObject* inObject) 1.666 +/* 1.667 +** Function to init the heap object just right. 1.668 +** Sets the unique ID to something unique. 1.669 +*/ 1.670 +{ 1.671 + HeapObject* retval = inObject; 1.672 + 1.673 + if(NULL != inObject) 1.674 + { 1.675 + static unsigned uniqueGenerator = 0; 1.676 + 1.677 + memset(inObject, -1, sizeof(HeapObject)); 1.678 + 1.679 + inObject->mUniqueID = uniqueGenerator; 1.680 + uniqueGenerator++; 1.681 + } 1.682 + 1.683 + return retval; 1.684 +} 1.685 + 1.686 + 1.687 +int simpleHeapEvent(TMState* inStats, HeapEventType inType, unsigned mTimestamp, unsigned inSerial, unsigned inHeapID, unsigned inSize) 1.688 +/* 1.689 +** A new heap event will cause the creation of a new heap object. 1.690 +** The new heap object will displace, or replace, a heap object of a different type. 1.691 +*/ 1.692 +{ 1.693 + int retval = 0; 1.694 + HeapObject newObject; 1.695 + 1.696 + /* 1.697 + ** Set the most basic object details. 1.698 + */ 1.699 + initHeapObject(&newObject); 1.700 + newObject.mHeapOffset = inHeapID; 1.701 + newObject.mSize = inSize; 1.702 + if(FREE == inType) 1.703 + { 1.704 + newObject.mType = FRAGMENT; 1.705 + } 1.706 + else if(ALLOC == inType) 1.707 + { 1.708 + newObject.mType = ALLOCATION; 1.709 + } 1.710 + 1.711 + /* 1.712 + ** Add it to the heap object array. 1.713 + */ 1.714 + 1.715 + /* 1.716 + ** TODO GAB 1.717 + ** 1.718 + ** First thing to do is to add the new object to the heap in order to 1.719 + ** obtain a valid index. 1.720 + ** 1.721 + ** Next, find all matches to this range of heap memory that this event 1.722 + ** refers to, that are alive during this timestamp (no death yet). 1.723 + ** Fill in the death event of those objects. 1.724 + ** If the objects contain some portions outside of the range, then 1.725 + ** new objects for those ranges need to be created that carry on 1.726 + ** the same object type, have the index of the old object for birth, 1.727 + ** and the serial of the old object, new timestamp of course. 1.728 + ** The old object's death points to the new object, which tells why the 1.729 + ** fragmentation took place. 1.730 + ** The new object birth points to the old object only if a fragment. 1.731 + ** An allocation only has a birth object when it is a realloc (complex) 1.732 + ** heap event. 1.733 + ** 1.734 + ** I believe this give us enough information to look up particular 1.735 + ** details of the heap at any given time. 1.736 + */ 1.737 + 1.738 + return retval; 1.739 +} 1.740 + 1.741 + 1.742 +int complexHeapEvent(TMState* inStats, unsigned mTimestamp, unsigned inOldSerial, unsigned inOldHeapID, unsigned inOSize, unsigned inNewSerial, unsigned inNewHeapID, unsigned inNewSize) 1.743 +/* 1.744 +** Generally, this event intends to chain one old heap object to a newer heap object. 1.745 +** Otherwise, the functionality should recognizable ala simpleHeapEvent. 1.746 +*/ 1.747 +{ 1.748 + int retval = 0; 1.749 + 1.750 + /* 1.751 + ** TODO GAB 1.752 + */ 1.753 + 1.754 + return retval; 1.755 +} 1.756 + 1.757 + 1.758 +unsigned actualByteSize(Options* inOptions, unsigned retval) 1.759 +/* 1.760 +** Apply alignment and overhead to size to figure out actual byte size. 1.761 +** This by default mimics spacetrace with default options (msvc crt heap). 1.762 +*/ 1.763 +{ 1.764 + if(0 != retval) 1.765 + { 1.766 + unsigned eval = 0; 1.767 + unsigned over = 0; 1.768 + 1.769 + eval = retval - 1; 1.770 + if(0 != inOptions->mAlignment) 1.771 + { 1.772 + over = eval % inOptions->mAlignment; 1.773 + } 1.774 + retval = eval + inOptions->mOverhead + inOptions->mAlignment - over; 1.775 + } 1.776 + 1.777 + return retval; 1.778 +} 1.779 + 1.780 + 1.781 +void tmEventHandler(tmreader* inReader, tmevent* inEvent) 1.782 +/* 1.783 +** Callback from the tmreader_eventloop. 1.784 +** Build up our fragmentation information herein. 1.785 +*/ 1.786 +{ 1.787 + char type = inEvent->type; 1.788 + TMState* stats = (TMState*)inReader->data; 1.789 + 1.790 + /* 1.791 + ** Only intersted in handling events of a particular type. 1.792 + */ 1.793 + switch(type) 1.794 + { 1.795 + default: 1.796 + return; 1.797 + 1.798 + case TM_EVENT_MALLOC: 1.799 + case TM_EVENT_CALLOC: 1.800 + case TM_EVENT_REALLOC: 1.801 + case TM_EVENT_FREE: 1.802 + break; 1.803 + } 1.804 + 1.805 + /* 1.806 + ** Should we even try to look? 1.807 + ** Set mLoopExitTMR to non-zero to abort the read loop faster. 1.808 + */ 1.809 + if(0 == stats->mLoopExitTMR) 1.810 + { 1.811 + Options* options = (Options*)stats->mOptions; 1.812 + unsigned timestamp = ticks2msec(stats->mTMR, inEvent->u.alloc.interval); 1.813 + unsigned actualSize = actualByteSize(options, inEvent->u.alloc.size); 1.814 + unsigned heapID = inEvent->u.alloc.ptr; 1.815 + unsigned serial = inEvent->serial; 1.816 + 1.817 + /* 1.818 + ** Check the timestamp range of our overall state. 1.819 + */ 1.820 + if(stats->uMinTicks > timestamp) 1.821 + { 1.822 + stats->uMinTicks = timestamp; 1.823 + } 1.824 + if(stats->uMaxTicks < timestamp) 1.825 + { 1.826 + stats->uMaxTicks = timestamp; 1.827 + } 1.828 + 1.829 + /* 1.830 + ** Realloc in general deserves some special attention if dealing 1.831 + ** with an old allocation (not new memory). 1.832 + */ 1.833 + if(TM_EVENT_REALLOC == type && 0 != inEvent->u.alloc.oldserial) 1.834 + { 1.835 + unsigned oldActualSize = actualByteSize(options, inEvent->u.alloc.oldsize); 1.836 + unsigned oldHeapID = inEvent->u.alloc.oldptr; 1.837 + unsigned oldSerial = inEvent->u.alloc.oldserial; 1.838 + 1.839 + if(0 == actualSize) 1.840 + { 1.841 + /* 1.842 + ** Reallocs of size zero are to become free events. 1.843 + */ 1.844 + stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, oldHeapID, oldActualSize); 1.845 + } 1.846 + else if(heapID != oldHeapID || actualSize != oldActualSize) 1.847 + { 1.848 + /* 1.849 + ** Reallocs which moved generate two events. 1.850 + ** Reallocs which changed size generate two events. 1.851 + ** 1.852 + ** One event to free the old memory area. 1.853 + ** Another event to allocate the new memory area. 1.854 + ** They are to be linked to one another, so the history 1.855 + ** and true origin can be tracked. 1.856 + */ 1.857 + stats->mLoopExitTMR = complexHeapEvent(stats, timestamp, oldSerial, oldHeapID, oldActualSize, serial, heapID, actualSize); 1.858 + } 1.859 + else 1.860 + { 1.861 + /* 1.862 + ** The realloc is not considered an operation and is skipped. 1.863 + ** It is not an operation, because it did not move or change 1.864 + ** size; this can happen if a realloc falls within the 1.865 + ** alignment of an allocation. 1.866 + ** Say if you realloc a 1 byte allocation to 2 bytes, it will 1.867 + ** not really change heap impact unless you have 1 set as 1.868 + ** the alignment of your allocations. 1.869 + */ 1.870 + } 1.871 + } 1.872 + else if(TM_EVENT_FREE == type) 1.873 + { 1.874 + /* 1.875 + ** Generate a free event to create a fragment. 1.876 + */ 1.877 + stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, heapID, actualSize); 1.878 + } 1.879 + else 1.880 + { 1.881 + /* 1.882 + ** Generate an allocation event to clear fragments. 1.883 + */ 1.884 + stats->mLoopExitTMR = simpleHeapEvent(stats, ALLOC, timestamp, serial, heapID, actualSize); 1.885 + } 1.886 + } 1.887 +} 1.888 + 1.889 + 1.890 +int tmfrags(Options* inOptions) 1.891 +/* 1.892 +** Load the input file and report stats. 1.893 +*/ 1.894 +{ 1.895 + int retval = 0; 1.896 + TMState stats; 1.897 + 1.898 + memset(&stats, 0, sizeof(stats)); 1.899 + stats.mOptions = inOptions; 1.900 + stats.uMinTicks = 0xFFFFFFFFU; 1.901 + 1.902 + /* 1.903 + ** Need a tmreader. 1.904 + */ 1.905 + stats.mTMR = tmreader_new(inOptions->mProgramName, &stats); 1.906 + if(NULL != stats.mTMR) 1.907 + { 1.908 + int tmResult = 0; 1.909 + 1.910 + tmResult = tmreader_eventloop(stats.mTMR, inOptions->mInputName, tmEventHandler); 1.911 + if(0 == tmResult) 1.912 + { 1.913 + retval = __LINE__; 1.914 + ERROR_REPORT(retval, inOptions->mInputName, "Problem reading trace-malloc data."); 1.915 + } 1.916 + if(0 != stats.mLoopExitTMR) 1.917 + { 1.918 + retval = stats.mLoopExitTMR; 1.919 + ERROR_REPORT(retval, inOptions->mInputName, "Aborted trace-malloc input loop."); 1.920 + } 1.921 + 1.922 + tmreader_destroy(stats.mTMR); 1.923 + stats.mTMR = NULL; 1.924 + } 1.925 + else 1.926 + { 1.927 + retval = __LINE__; 1.928 + ERROR_REPORT(retval, inOptions->mProgramName, "Unable to obtain tmreader."); 1.929 + } 1.930 + 1.931 + return retval; 1.932 +} 1.933 + 1.934 + 1.935 +int main(int inArgc, char** inArgv) 1.936 +{ 1.937 + int retval = 0; 1.938 + Options options; 1.939 + 1.940 + retval = initOptions(&options, inArgc, inArgv); 1.941 + if(options.mHelp) 1.942 + { 1.943 + showHelp(&options); 1.944 + } 1.945 + else if(0 == retval) 1.946 + { 1.947 + retval = tmfrags(&options); 1.948 + } 1.949 + 1.950 + cleanOptions(&options); 1.951 + return retval; 1.952 +} 1.953 +