michael@0: /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "nspr.h" michael@0: #include "tmreader.h" michael@0: michael@0: michael@0: #define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); michael@0: #define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) michael@0: michael@0: michael@0: #define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000) michael@0: #define ticks2usec(reader, ticks) ticks2xsec((reader), (ticks), 1000000) michael@0: #define TICK_RESOLUTION 1000 michael@0: #define TICK_PRINTABLE(timeval) ((double)(timeval) / (double)ST_TIMEVAL_RESOLUTION) michael@0: michael@0: michael@0: typedef struct __struct_Options michael@0: /* michael@0: ** Options to control how we perform. michael@0: ** michael@0: ** mProgramName Used in help text. michael@0: ** mInputName Name of the file. michael@0: ** mOutput Output file, append. michael@0: ** Default is stdout. michael@0: ** mOutputName Name of the file. michael@0: ** mHelp Whether or not help should be shown. michael@0: ** mOverhead How much overhead an allocation will have. michael@0: ** mAlignment What boundry will the end of an allocation line up on. michael@0: ** mPageSize Controls the page size. A page containing only fragments michael@0: ** is not fragmented. A page containing any life memory michael@0: ** costs mPageSize in bytes. michael@0: */ michael@0: { michael@0: const char* mProgramName; michael@0: char* mInputName; michael@0: FILE* mOutput; michael@0: char* mOutputName; michael@0: int mHelp; michael@0: unsigned mOverhead; michael@0: unsigned mAlignment; michael@0: unsigned mPageSize; michael@0: } michael@0: Options; michael@0: michael@0: michael@0: typedef struct __struct_Switch michael@0: /* michael@0: ** Command line options. michael@0: */ michael@0: { michael@0: const char* mLongName; michael@0: const char* mShortName; michael@0: int mHasValue; michael@0: const char* mValue; michael@0: const char* mDescription; michael@0: } michael@0: Switch; michael@0: michael@0: #define DESC_NEWLINE "\n\t\t" michael@0: michael@0: static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; michael@0: static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; michael@0: static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; michael@0: 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: 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: 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: michael@0: static Switch* gSwitches[] = { michael@0: &gInputSwitch, michael@0: &gOutputSwitch, michael@0: &gAlignmentSwitch, michael@0: &gOverheadSwitch, michael@0: &gPageSizeSwitch, michael@0: &gHelpSwitch michael@0: }; michael@0: michael@0: michael@0: typedef struct __struct_AnyArray michael@0: /* michael@0: ** Variable sized item array. michael@0: ** michael@0: ** mItems The void pointer items. michael@0: ** mItemSize Size of each different item. michael@0: ** mCount The number of items in the array. michael@0: ** mCapacity How many more items we can hold before reallocing. michael@0: ** mGrowBy How many items we allocate when we grow. michael@0: */ michael@0: { michael@0: void* mItems; michael@0: unsigned mItemSize; michael@0: unsigned mCount; michael@0: unsigned mCapacity; michael@0: unsigned mGrowBy; michael@0: } michael@0: AnyArray; michael@0: michael@0: michael@0: typedef int (*arrayMatchFunc)(void* inContext, AnyArray* inArray, void* inItem, unsigned inItemIndex) michael@0: /* michael@0: ** Callback function for the arrayIndexFn function. michael@0: ** Used to determine an item match by customizable criteria. michael@0: ** michael@0: ** inContext The criteria and state of the search. michael@0: ** User specified/created. michael@0: ** inArray The array the item is in. michael@0: ** inItem The item to evaluate for match. michael@0: ** inItemIndex The index of this particular item in the array. michael@0: ** michael@0: ** return int 0 to specify a match. michael@0: ** !0 to continue the search performed by arrayIndexFn. michael@0: */ michael@0: ; michael@0: michael@0: michael@0: typedef enum __enum_HeapEventType michael@0: /* michael@0: ** Simple heap events are really one of two things. michael@0: */ michael@0: { michael@0: FREE, michael@0: ALLOC michael@0: } michael@0: HeapEventType; michael@0: michael@0: michael@0: typedef enum __enum_HeapObjectType michael@0: /* michael@0: ** The various types of heap objects we track. michael@0: */ michael@0: { michael@0: ALLOCATION, michael@0: FRAGMENT michael@0: } michael@0: HeapObjectType; michael@0: michael@0: michael@0: typedef struct __struct_HeapObject HeapObject; michael@0: typedef struct __struct_HeapHistory michael@0: /* michael@0: ** A marker as to what has happened. michael@0: ** michael@0: ** mTimestamp When history occurred. michael@0: ** mTMRSerial The historical state as known to the tmreader. michael@0: ** mObjectIndex Index to the object that was before or after this event. michael@0: ** The index as in the index according to all heap objects michael@0: ** kept in the TMState structure. michael@0: ** We use an index instead of a pointer as the array of michael@0: ** objects can change location in the heap. michael@0: */ michael@0: { michael@0: unsigned mTimestamp; michael@0: unsigned mTMRSerial; michael@0: unsigned mObjectIndex; michael@0: } michael@0: HeapHistory; michael@0: michael@0: michael@0: struct __struct_HeapObject michael@0: /* michael@0: ** An object in the heap. michael@0: ** michael@0: ** A special case should be noted here. If either the birth or death michael@0: ** history leads to an object of the same type, then this object michael@0: ** is the same as that object, but was modified somehow. michael@0: ** Also note that multiple objects may have the same birth object, michael@0: ** as well as the same death object. michael@0: ** michael@0: ** mUniqueID Each object is unique. michael@0: ** mType Either allocation or fragment. michael@0: ** mHeapOffset Where in the heap the object is. michael@0: ** mSize How much of the heap the object takes. michael@0: ** mBirth History about the birth event. michael@0: ** mDeath History about the death event. michael@0: */ michael@0: { michael@0: unsigned mUniqueID; michael@0: michael@0: HeapObjectType mType; michael@0: unsigned mHeapOffset; michael@0: unsigned mSize; michael@0: michael@0: HeapHistory mBirth; michael@0: HeapHistory mDeath; michael@0: }; michael@0: michael@0: michael@0: typedef struct __struct_TMState michael@0: /* michael@0: ** State of our current operation. michael@0: ** Stats we are trying to calculate. michael@0: ** michael@0: ** mOptions Obilgatory options pointer. michael@0: ** mTMR The tmreader, used in tmreader API calls. michael@0: ** mLoopExitTMR Set to non zero in order to quickly exit from tmreader michael@0: ** input loop. This will also result in an error. michael@0: ** uMinTicks Start of run, milliseconds. michael@0: ** uMaxTicks End of run, milliseconds. michael@0: */ michael@0: { michael@0: Options* mOptions; michael@0: tmreader* mTMR; michael@0: michael@0: int mLoopExitTMR; michael@0: michael@0: unsigned uMinTicks; michael@0: unsigned uMaxTicks; michael@0: } michael@0: TMState; michael@0: michael@0: michael@0: int initOptions(Options* outOptions, int inArgc, char** inArgv) michael@0: /* michael@0: ** returns int 0 if successful. michael@0: */ michael@0: { michael@0: int retval = 0; michael@0: int loop = 0; michael@0: int switchLoop = 0; michael@0: int match = 0; michael@0: const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); michael@0: Switch* current = NULL; michael@0: michael@0: /* michael@0: ** Set any defaults. michael@0: */ michael@0: memset(outOptions, 0, sizeof(Options)); michael@0: outOptions->mProgramName = inArgv[0]; michael@0: outOptions->mInputName = strdup("-"); michael@0: outOptions->mOutput = stdout; michael@0: outOptions->mOutputName = strdup("stdout"); michael@0: outOptions->mAlignment = 16; michael@0: outOptions->mOverhead = 8; michael@0: michael@0: if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); michael@0: } michael@0: michael@0: /* michael@0: ** Go through and attempt to do the right thing. michael@0: */ michael@0: for(loop = 1; loop < inArgc && 0 == retval; loop++) michael@0: { michael@0: match = 0; michael@0: current = NULL; michael@0: michael@0: for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) michael@0: { michael@0: if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) michael@0: { michael@0: match = __LINE__; michael@0: } michael@0: else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) michael@0: { michael@0: match = __LINE__; michael@0: } michael@0: michael@0: if(match) michael@0: { michael@0: if(gSwitches[switchLoop]->mHasValue) michael@0: { michael@0: /* michael@0: ** Attempt to absorb next option to fullfill value. michael@0: */ michael@0: if(loop + 1 < inArgc) michael@0: { michael@0: loop++; michael@0: michael@0: current = gSwitches[switchLoop]; michael@0: current->mValue = inArgv[loop]; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: current = gSwitches[switchLoop]; michael@0: } michael@0: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if(0 == match) michael@0: { michael@0: outOptions->mHelp = __LINE__; michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); michael@0: } michael@0: else if(NULL == current) michael@0: { michael@0: outOptions->mHelp = __LINE__; michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); michael@0: } michael@0: else michael@0: { michael@0: /* michael@0: ** Do something based on address/swtich. michael@0: */ michael@0: if(current == &gInputSwitch) michael@0: { michael@0: CLEANUP(outOptions->mInputName); michael@0: outOptions->mInputName = strdup(current->mValue); michael@0: if(NULL == outOptions->mInputName) michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, current->mValue, "Unable to strdup."); michael@0: } michael@0: } michael@0: else if(current == &gOutputSwitch) michael@0: { michael@0: CLEANUP(outOptions->mOutputName); michael@0: if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) michael@0: { michael@0: fclose(outOptions->mOutput); michael@0: outOptions->mOutput = NULL; michael@0: } michael@0: michael@0: outOptions->mOutput = fopen(current->mValue, "a"); michael@0: if(NULL == outOptions->mOutput) michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, current->mValue, "Unable to open output file."); michael@0: } michael@0: else michael@0: { michael@0: outOptions->mOutputName = strdup(current->mValue); michael@0: if(NULL == outOptions->mOutputName) michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, current->mValue, "Unable to strdup."); michael@0: } michael@0: } michael@0: } michael@0: else if(current == &gHelpSwitch) michael@0: { michael@0: outOptions->mHelp = __LINE__; michael@0: } michael@0: else if(current == &gAlignmentSwitch) michael@0: { michael@0: unsigned arg = 0; michael@0: char* endScan = NULL; michael@0: michael@0: errno = 0; michael@0: arg = strtoul(current->mValue, &endScan, 0); michael@0: if(0 == errno && endScan != current->mValue) michael@0: { michael@0: outOptions->mAlignment = arg; michael@0: } michael@0: else michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, current->mValue, "Unable to convert to a number."); michael@0: } michael@0: } michael@0: else if(current == &gOverheadSwitch) michael@0: { michael@0: unsigned arg = 0; michael@0: char* endScan = NULL; michael@0: michael@0: errno = 0; michael@0: arg = strtoul(current->mValue, &endScan, 0); michael@0: if(0 == errno && endScan != current->mValue) michael@0: { michael@0: outOptions->mOverhead = arg; michael@0: } michael@0: else michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, current->mValue, "Unable to convert to a number."); michael@0: } michael@0: } michael@0: else if(current == &gPageSizeSwitch) michael@0: { michael@0: unsigned arg = 0; michael@0: char* endScan = NULL; michael@0: michael@0: errno = 0; michael@0: arg = strtoul(current->mValue, &endScan, 0); michael@0: if(0 == errno && endScan != current->mValue) michael@0: { michael@0: outOptions->mPageSize = arg; michael@0: } michael@0: else michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, current->mValue, "Unable to convert to a number."); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: uint32_t ticks2xsec(tmreader* aReader, uint32_t aTicks, uint32_t aResolution) michael@0: /* michael@0: ** Convert platform specific ticks to second units michael@0: */ michael@0: { michael@0: return (uint32)((aResolution * aTicks) / aReader->ticksPerSec); michael@0: } michael@0: michael@0: michael@0: void cleanOptions(Options* inOptions) michael@0: /* michael@0: ** Clean up any open handles. michael@0: */ michael@0: { michael@0: unsigned loop = 0; michael@0: michael@0: CLEANUP(inOptions->mInputName); michael@0: CLEANUP(inOptions->mOutputName); michael@0: if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) michael@0: { michael@0: fclose(inOptions->mOutput); michael@0: } michael@0: michael@0: memset(inOptions, 0, sizeof(Options)); michael@0: } michael@0: michael@0: michael@0: void showHelp(Options* inOptions) michael@0: /* michael@0: ** Show some simple help text on usage. michael@0: */ michael@0: { michael@0: int loop = 0; michael@0: const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); michael@0: const char* valueText = NULL; michael@0: michael@0: printf("usage:\t%s [arguments]\n", inOptions->mProgramName); michael@0: printf("\n"); michael@0: printf("arguments:\n"); michael@0: michael@0: for(loop = 0; loop < switchCount; loop++) michael@0: { michael@0: if(gSwitches[loop]->mHasValue) michael@0: { michael@0: valueText = " "; michael@0: } michael@0: else michael@0: { michael@0: valueText = ""; michael@0: } michael@0: michael@0: printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); michael@0: printf("\t %s%s", gSwitches[loop]->mShortName, valueText); michael@0: printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); michael@0: } michael@0: michael@0: printf("This tool reports heap fragmentation stats from a trace-malloc log.\n"); michael@0: } michael@0: michael@0: michael@0: AnyArray* arrayCreate(unsigned inItemSize, unsigned inGrowBy) michael@0: /* michael@0: ** Create an array container object. michael@0: */ michael@0: { michael@0: AnyArray* retval = NULL; michael@0: michael@0: if(0 != inGrowBy && 0 != inItemSize) michael@0: { michael@0: retval = (AnyArray*)calloc(1, sizeof(AnyArray)); michael@0: retval->mItemSize = inItemSize; michael@0: retval->mGrowBy = inGrowBy; michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: void arrayDestroy(AnyArray* inArray) michael@0: /* michael@0: ** Release the memory the array contains. michael@0: ** This will release the items as well. michael@0: */ michael@0: { michael@0: if(NULL != inArray) michael@0: { michael@0: if(NULL != inArray->mItems) michael@0: { michael@0: free(inArray->mItems); michael@0: } michael@0: free(inArray); michael@0: } michael@0: } michael@0: michael@0: michael@0: unsigned arrayAlloc(AnyArray* inArray, unsigned inItems) michael@0: /* michael@0: ** Resize the item array capcity to a specific number of items. michael@0: ** This could possibly truncate the array, so handle that as well. michael@0: ** michael@0: ** returns unsigned <= inArray->mCapacity on success. michael@0: */ michael@0: { michael@0: unsigned retval = (unsigned)-1; michael@0: michael@0: if(NULL != inArray) michael@0: { michael@0: void* moved = NULL; michael@0: michael@0: moved = realloc(inArray->mItems, inItems * inArray->mItemSize); michael@0: if(NULL != moved) michael@0: { michael@0: inArray->mItems = moved; michael@0: inArray->mCapacity = inItems; michael@0: if(inArray->mCount > inItems) michael@0: { michael@0: inArray->mCount = inItems; michael@0: } michael@0: michael@0: retval = inItems; michael@0: } michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: void* arrayItem(AnyArray* inArray, unsigned inIndex) michael@0: /* michael@0: ** Return the array item at said index. michael@0: ** Zero based index. michael@0: ** michael@0: ** returns void* NULL on failure. michael@0: */ michael@0: { michael@0: void* retval = NULL; michael@0: michael@0: if(NULL != inArray && inIndex < inArray->mCount) michael@0: { michael@0: retval = (void*)((char*)inArray->mItems + (inArray->mItemSize * inIndex)); michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: unsigned arrayIndex(AnyArray* inArray, void* inItem, unsigned inStartIndex) michael@0: /* michael@0: ** Go through the array from the index specified looking for an item michael@0: ** match based on byte for byte comparison. michael@0: ** We allow specifying the start index in order to handle arrays with michael@0: ** duplicate items. michael@0: ** michael@0: ** returns unsigned >= inArray->mCount on failure. michael@0: */ michael@0: { michael@0: unsigned retval = (unsigned)-1; michael@0: michael@0: if(NULL != inArray && NULL != inItem && inStartIndex < inArray->mCount) michael@0: { michael@0: void* curItem = NULL; michael@0: michael@0: for(retval = inStartIndex; retval < inArray->mCount; retval++) michael@0: { michael@0: curItem = arrayItem(inArray, retval); michael@0: if(0 == memcmp(inItem, curItem, inArray->mItemSize)) michael@0: { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: unsigned arrayIndexFn(AnyArray* inArray, arrayMatchFunc inFunc, void* inFuncContext, unsigned inStartIndex) michael@0: /* michael@0: ** Go through the array from the index specified looking for an item michael@0: ** match based upon the return value of inFunc (0, Zero, is a match). michael@0: ** We allow specifying the start index in order to facilitate looping over michael@0: ** the array which could have multiple matches. michael@0: ** michael@0: ** returns unsigned >= inArray->mCount on failure. michael@0: */ michael@0: { michael@0: unsigned retval = (unsigned)-1; michael@0: michael@0: if(NULL != inArray && NULL != inFunc && inStartIndex < inArray->mCount) michael@0: { michael@0: void* curItem = NULL; michael@0: michael@0: for(retval = inStartIndex; retval < inArray->mCount; retval++) michael@0: { michael@0: curItem = arrayItem(inArray, retval); michael@0: if(0 == inFunc(inFuncContext, inArray, curItem, retval)) michael@0: { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: unsigned arrayAddItem(AnyArray* inArray, void* inItem) michael@0: /* michael@0: ** Add a new item to the array. michael@0: ** This is done by copying the item. michael@0: ** michael@0: ** returns unsigned < inArray->mCount on success. michael@0: */ michael@0: { michael@0: unsigned retval = (unsigned)-1; michael@0: michael@0: if(NULL != inArray && NULL != inItem) michael@0: { michael@0: int noCopy = 0; michael@0: michael@0: /* michael@0: ** See if the array should grow. michael@0: */ michael@0: if(inArray->mCount == inArray->mCapacity) michael@0: { michael@0: unsigned allocRes = 0; michael@0: michael@0: allocRes = arrayAlloc(inArray, inArray->mCapacity + inArray->mGrowBy); michael@0: if(allocRes > inArray->mCapacity) michael@0: { michael@0: noCopy = __LINE__; michael@0: } michael@0: } michael@0: michael@0: if(0 == noCopy) michael@0: { michael@0: retval = inArray->mCount; michael@0: michael@0: inArray->mCount++; michael@0: memcpy(arrayItem(inArray, retval), inItem, inArray->mItemSize); michael@0: } michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: HeapObject* initHeapObject(HeapObject* inObject) michael@0: /* michael@0: ** Function to init the heap object just right. michael@0: ** Sets the unique ID to something unique. michael@0: */ michael@0: { michael@0: HeapObject* retval = inObject; michael@0: michael@0: if(NULL != inObject) michael@0: { michael@0: static unsigned uniqueGenerator = 0; michael@0: michael@0: memset(inObject, -1, sizeof(HeapObject)); michael@0: michael@0: inObject->mUniqueID = uniqueGenerator; michael@0: uniqueGenerator++; michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: int simpleHeapEvent(TMState* inStats, HeapEventType inType, unsigned mTimestamp, unsigned inSerial, unsigned inHeapID, unsigned inSize) michael@0: /* michael@0: ** A new heap event will cause the creation of a new heap object. michael@0: ** The new heap object will displace, or replace, a heap object of a different type. michael@0: */ michael@0: { michael@0: int retval = 0; michael@0: HeapObject newObject; michael@0: michael@0: /* michael@0: ** Set the most basic object details. michael@0: */ michael@0: initHeapObject(&newObject); michael@0: newObject.mHeapOffset = inHeapID; michael@0: newObject.mSize = inSize; michael@0: if(FREE == inType) michael@0: { michael@0: newObject.mType = FRAGMENT; michael@0: } michael@0: else if(ALLOC == inType) michael@0: { michael@0: newObject.mType = ALLOCATION; michael@0: } michael@0: michael@0: /* michael@0: ** Add it to the heap object array. michael@0: */ michael@0: michael@0: /* michael@0: ** TODO GAB michael@0: ** michael@0: ** First thing to do is to add the new object to the heap in order to michael@0: ** obtain a valid index. michael@0: ** michael@0: ** Next, find all matches to this range of heap memory that this event michael@0: ** refers to, that are alive during this timestamp (no death yet). michael@0: ** Fill in the death event of those objects. michael@0: ** If the objects contain some portions outside of the range, then michael@0: ** new objects for those ranges need to be created that carry on michael@0: ** the same object type, have the index of the old object for birth, michael@0: ** and the serial of the old object, new timestamp of course. michael@0: ** The old object's death points to the new object, which tells why the michael@0: ** fragmentation took place. michael@0: ** The new object birth points to the old object only if a fragment. michael@0: ** An allocation only has a birth object when it is a realloc (complex) michael@0: ** heap event. michael@0: ** michael@0: ** I believe this give us enough information to look up particular michael@0: ** details of the heap at any given time. michael@0: */ michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: int complexHeapEvent(TMState* inStats, unsigned mTimestamp, unsigned inOldSerial, unsigned inOldHeapID, unsigned inOSize, unsigned inNewSerial, unsigned inNewHeapID, unsigned inNewSize) michael@0: /* michael@0: ** Generally, this event intends to chain one old heap object to a newer heap object. michael@0: ** Otherwise, the functionality should recognizable ala simpleHeapEvent. michael@0: */ michael@0: { michael@0: int retval = 0; michael@0: michael@0: /* michael@0: ** TODO GAB michael@0: */ michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: unsigned actualByteSize(Options* inOptions, unsigned retval) michael@0: /* michael@0: ** Apply alignment and overhead to size to figure out actual byte size. michael@0: ** This by default mimics spacetrace with default options (msvc crt heap). michael@0: */ michael@0: { michael@0: if(0 != retval) michael@0: { michael@0: unsigned eval = 0; michael@0: unsigned over = 0; michael@0: michael@0: eval = retval - 1; michael@0: if(0 != inOptions->mAlignment) michael@0: { michael@0: over = eval % inOptions->mAlignment; michael@0: } michael@0: retval = eval + inOptions->mOverhead + inOptions->mAlignment - over; michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: void tmEventHandler(tmreader* inReader, tmevent* inEvent) michael@0: /* michael@0: ** Callback from the tmreader_eventloop. michael@0: ** Build up our fragmentation information herein. michael@0: */ michael@0: { michael@0: char type = inEvent->type; michael@0: TMState* stats = (TMState*)inReader->data; michael@0: michael@0: /* michael@0: ** Only intersted in handling events of a particular type. michael@0: */ michael@0: switch(type) michael@0: { michael@0: default: michael@0: return; michael@0: michael@0: case TM_EVENT_MALLOC: michael@0: case TM_EVENT_CALLOC: michael@0: case TM_EVENT_REALLOC: michael@0: case TM_EVENT_FREE: michael@0: break; michael@0: } michael@0: michael@0: /* michael@0: ** Should we even try to look? michael@0: ** Set mLoopExitTMR to non-zero to abort the read loop faster. michael@0: */ michael@0: if(0 == stats->mLoopExitTMR) michael@0: { michael@0: Options* options = (Options*)stats->mOptions; michael@0: unsigned timestamp = ticks2msec(stats->mTMR, inEvent->u.alloc.interval); michael@0: unsigned actualSize = actualByteSize(options, inEvent->u.alloc.size); michael@0: unsigned heapID = inEvent->u.alloc.ptr; michael@0: unsigned serial = inEvent->serial; michael@0: michael@0: /* michael@0: ** Check the timestamp range of our overall state. michael@0: */ michael@0: if(stats->uMinTicks > timestamp) michael@0: { michael@0: stats->uMinTicks = timestamp; michael@0: } michael@0: if(stats->uMaxTicks < timestamp) michael@0: { michael@0: stats->uMaxTicks = timestamp; michael@0: } michael@0: michael@0: /* michael@0: ** Realloc in general deserves some special attention if dealing michael@0: ** with an old allocation (not new memory). michael@0: */ michael@0: if(TM_EVENT_REALLOC == type && 0 != inEvent->u.alloc.oldserial) michael@0: { michael@0: unsigned oldActualSize = actualByteSize(options, inEvent->u.alloc.oldsize); michael@0: unsigned oldHeapID = inEvent->u.alloc.oldptr; michael@0: unsigned oldSerial = inEvent->u.alloc.oldserial; michael@0: michael@0: if(0 == actualSize) michael@0: { michael@0: /* michael@0: ** Reallocs of size zero are to become free events. michael@0: */ michael@0: stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, oldHeapID, oldActualSize); michael@0: } michael@0: else if(heapID != oldHeapID || actualSize != oldActualSize) michael@0: { michael@0: /* michael@0: ** Reallocs which moved generate two events. michael@0: ** Reallocs which changed size generate two events. michael@0: ** michael@0: ** One event to free the old memory area. michael@0: ** Another event to allocate the new memory area. michael@0: ** They are to be linked to one another, so the history michael@0: ** and true origin can be tracked. michael@0: */ michael@0: stats->mLoopExitTMR = complexHeapEvent(stats, timestamp, oldSerial, oldHeapID, oldActualSize, serial, heapID, actualSize); michael@0: } michael@0: else michael@0: { michael@0: /* michael@0: ** The realloc is not considered an operation and is skipped. michael@0: ** It is not an operation, because it did not move or change michael@0: ** size; this can happen if a realloc falls within the michael@0: ** alignment of an allocation. michael@0: ** Say if you realloc a 1 byte allocation to 2 bytes, it will michael@0: ** not really change heap impact unless you have 1 set as michael@0: ** the alignment of your allocations. michael@0: */ michael@0: } michael@0: } michael@0: else if(TM_EVENT_FREE == type) michael@0: { michael@0: /* michael@0: ** Generate a free event to create a fragment. michael@0: */ michael@0: stats->mLoopExitTMR = simpleHeapEvent(stats, FREE, timestamp, serial, heapID, actualSize); michael@0: } michael@0: else michael@0: { michael@0: /* michael@0: ** Generate an allocation event to clear fragments. michael@0: */ michael@0: stats->mLoopExitTMR = simpleHeapEvent(stats, ALLOC, timestamp, serial, heapID, actualSize); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: int tmfrags(Options* inOptions) michael@0: /* michael@0: ** Load the input file and report stats. michael@0: */ michael@0: { michael@0: int retval = 0; michael@0: TMState stats; michael@0: michael@0: memset(&stats, 0, sizeof(stats)); michael@0: stats.mOptions = inOptions; michael@0: stats.uMinTicks = 0xFFFFFFFFU; michael@0: michael@0: /* michael@0: ** Need a tmreader. michael@0: */ michael@0: stats.mTMR = tmreader_new(inOptions->mProgramName, &stats); michael@0: if(NULL != stats.mTMR) michael@0: { michael@0: int tmResult = 0; michael@0: michael@0: tmResult = tmreader_eventloop(stats.mTMR, inOptions->mInputName, tmEventHandler); michael@0: if(0 == tmResult) michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, inOptions->mInputName, "Problem reading trace-malloc data."); michael@0: } michael@0: if(0 != stats.mLoopExitTMR) michael@0: { michael@0: retval = stats.mLoopExitTMR; michael@0: ERROR_REPORT(retval, inOptions->mInputName, "Aborted trace-malloc input loop."); michael@0: } michael@0: michael@0: tmreader_destroy(stats.mTMR); michael@0: stats.mTMR = NULL; michael@0: } michael@0: else michael@0: { michael@0: retval = __LINE__; michael@0: ERROR_REPORT(retval, inOptions->mProgramName, "Unable to obtain tmreader."); michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: int main(int inArgc, char** inArgv) michael@0: { michael@0: int retval = 0; michael@0: Options options; michael@0: michael@0: retval = initOptions(&options, inArgc, inArgv); michael@0: if(options.mHelp) michael@0: { michael@0: showHelp(&options); michael@0: } michael@0: else if(0 == retval) michael@0: { michael@0: retval = tmfrags(&options); michael@0: } michael@0: michael@0: cleanOptions(&options); michael@0: return retval; michael@0: } michael@0: