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: #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 COST_RESOLUTION 1000 michael@0: #define COST_PRINTABLE(cost) ((double)(cost) / (double)COST_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: ** mAverages Whether or not to display averages. michael@0: ** mDeviances Whether or not to display standard deviations. michael@0: ** mRunLength Whether or not to display run length. 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: int mAverages; michael@0: int mDeviances; michael@0: int mRunLength; 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 gAveragesSwitch = {"--averages", "-avg", 0, NULL, "Display averages."}; michael@0: static Switch gDeviationsSwitch = {"--deviations", "-dev", 0, NULL, "Display standard deviations from the average." DESC_NEWLINE "Implies --averages."}; michael@0: static Switch gRunLengthSwitch = {"--run-length", "-rl", 0, NULL, "Display the run length in seconds."}; michael@0: michael@0: static Switch* gSwitches[] = { michael@0: &gInputSwitch, michael@0: &gOutputSwitch, michael@0: &gAlignmentSwitch, michael@0: &gOverheadSwitch, michael@0: &gAveragesSwitch, michael@0: &gDeviationsSwitch, michael@0: &gRunLengthSwitch, michael@0: &gHelpSwitch michael@0: }; michael@0: michael@0: michael@0: typedef struct _struct_VarianceState michael@0: /* michael@0: ** State for a single pass variance calculation. michael@0: */ michael@0: { michael@0: unsigned mCount; michael@0: uint64_t mSum; michael@0: uint64_t mSquaredSum; michael@0: } michael@0: VarianceState; michael@0: michael@0: michael@0: typedef struct __struct_TMStats michael@0: /* michael@0: ** Stats we are trying to calculate. michael@0: ** michael@0: ** mOptions Obilgatory options pointer. michael@0: ** uMemoryInUse Current tally of memory in use. michael@0: ** uPeakMemory Heap topped out at this byte level. michael@0: ** uObjectsInUse Different allocations outstanding. michael@0: ** uPeakObjects Highest object count. michael@0: ** uMallocs Number of malloc calls. michael@0: ** uCallocs Number of calloc calls. michael@0: ** uReallocs Number of realloc calls. michael@0: ** uFrees Number of free calls. michael@0: ** uMallocSize Bytes from malloc. michael@0: ** uCallocSize Bytes from calloc. michael@0: ** uReallocSize Bytes from realloc. michael@0: ** uFreeSize Bytes from free. michael@0: ** mMallocSizeVar Variance of bytes. michael@0: ** mCallocSizeVar Variance of bytes. michael@0: ** mReallocSizeVar Variance of bytes. michael@0: ** mFreeSizeVar Variance of bytes. michael@0: ** uMallocCost Time of mallocs. michael@0: ** uCallocCost Time of callocs. michael@0: ** uReallocCost Time of reallocs. michael@0: ** uFreeCost Time of frees. michael@0: ** mMallocCostVar Variance of cost. michael@0: ** mCallocCostVar Variance of cost. michael@0: ** mReallocCostVar Variance of cost. michael@0: ** mFreeCostVar Variance of cost. michael@0: ** uMinTicks Start of run. michael@0: ** uMaxTicks End of run. michael@0: */ michael@0: { michael@0: Options* mOptions; michael@0: unsigned uMemoryInUse; michael@0: unsigned uPeakMemory; michael@0: unsigned uObjectsInUse; michael@0: unsigned uPeakObjects; michael@0: unsigned uMallocs; michael@0: unsigned uCallocs; michael@0: unsigned uReallocs; michael@0: unsigned uFrees; michael@0: michael@0: unsigned uMallocSize; michael@0: unsigned uCallocSize; michael@0: unsigned uReallocSize; michael@0: unsigned uFreeSize; michael@0: VarianceState mMallocSizeVar; michael@0: VarianceState mCallocSizeVar; michael@0: VarianceState mReallocSizeVar; michael@0: VarianceState mFreeSizeVar; michael@0: michael@0: unsigned uMallocCost; michael@0: unsigned uCallocCost; michael@0: unsigned uReallocCost; michael@0: unsigned uFreeCost; michael@0: VarianceState mMallocCostVar; michael@0: VarianceState mCallocCostVar; michael@0: VarianceState mReallocCostVar; michael@0: VarianceState mFreeCostVar; michael@0: michael@0: unsigned uMinTicks; michael@0: unsigned uMaxTicks; michael@0: } michael@0: TMStats; 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 == &gAveragesSwitch) michael@0: { michael@0: outOptions->mAverages = __LINE__; michael@0: } michael@0: else if(current == &gDeviationsSwitch) michael@0: { michael@0: outOptions->mAverages = __LINE__; michael@0: outOptions->mDeviances = __LINE__; michael@0: } michael@0: else if(current == &gRunLengthSwitch) michael@0: { michael@0: outOptions->mRunLength = __LINE__; 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: 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 simple heap usage and allocation call counts.\n"); michael@0: printf("Useful for eyeballing trace-malloc numbers quickly.\n"); michael@0: } michael@0: michael@0: michael@0: void addVariance(VarianceState* inVariance, unsigned inValue) michael@0: /* michael@0: ** Add a value to a variance state. michael@0: */ michael@0: { michael@0: uint64_t squared; michael@0: uint64_t bigValue; michael@0: michael@0: bigValue = inValue; michael@0: inVariance->mSum += bigValue; michael@0: michael@0: squared = bigValue * bigValue; michael@0: inVariance->mSquaredSum += squared; michael@0: michael@0: inVariance->mCount++; michael@0: } michael@0: michael@0: michael@0: double getAverage(VarianceState* inVariance) michael@0: /* michael@0: ** Determine the mean/average based on the given state. michael@0: */ michael@0: { michael@0: double retval = 0.0; michael@0: michael@0: if(NULL != inVariance && 0 < inVariance->mCount) michael@0: { michael@0: double count; michael@0: int64_t isum; michael@0: michael@0: /* michael@0: ** Avoids a compiler error (not impl) under MSVC. michael@0: */ michael@0: isum = inVariance->mSum; michael@0: michael@0: count = (double)inVariance->mCount; michael@0: michael@0: retval = (double)isum / count; michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: double getVariance(VarianceState* inVariance) michael@0: /* michael@0: ** Determine the variance based on the given state. michael@0: */ michael@0: { michael@0: double retval = 0.0; michael@0: michael@0: if(NULL != inVariance && 1 < inVariance->mCount) michael@0: { michael@0: double count; michael@0: double avg; michael@0: double squaredAvg; michael@0: int64_t isquaredSum; michael@0: michael@0: /* michael@0: ** Avoids a compiler error (not impl) under MSVC. michael@0: */ michael@0: isquaredSum = inVariance->mSquaredSum; michael@0: michael@0: count = (double)inVariance->mCount; michael@0: michael@0: avg = getAverage(inVariance); michael@0: squaredAvg = avg * avg; michael@0: michael@0: retval = ((double)isquaredSum - (count * squaredAvg)) / (count - 1.0); michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: double getStdDev(VarianceState* inVariance) michael@0: /* michael@0: ** Determine the standard deviation based on the given state. michael@0: */ michael@0: { michael@0: double retval = 0.0; michael@0: double variance; michael@0: michael@0: variance = getVariance(inVariance); michael@0: retval = sqrt(variance); 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: uint32_t ticks2xsec(tmreader* aReader, uint32_t aTicks, uint32_t aResolution) michael@0: /* michael@0: ** Convert platform specific ticks to second units michael@0: ** Returns 0 on success. michael@0: */ michael@0: { michael@0: return (uint32_t)((aResolution * aTicks) / aReader->ticksPerSec); michael@0: } michael@0: #define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000) michael@0: michael@0: michael@0: void tmEventHandler(tmreader* inReader, tmevent* inEvent) michael@0: /* michael@0: ** Callback from the tmreader_eventloop. michael@0: ** Keep it simple in here, this is where we'll spend the most time. michael@0: ** The goal is to be fast. michael@0: */ michael@0: { michael@0: TMStats* stats = (TMStats*)inReader->data; michael@0: Options* options = (Options*)stats->mOptions; michael@0: char type = inEvent->type; michael@0: unsigned size = inEvent->u.alloc.size; michael@0: unsigned actualSize = 0; michael@0: unsigned actualOldSize = 0; michael@0: uint32_t interval = 0; michael@0: michael@0: /* michael@0: ** To match spacetrace stats, reallocs of size zero are frees. michael@0: ** Adjust the size to match what free expects. michael@0: */ michael@0: if(TM_EVENT_REALLOC == type && 0 == size) michael@0: { michael@0: type = TM_EVENT_FREE; michael@0: if(0 != inEvent->u.alloc.oldserial) michael@0: { michael@0: size = inEvent->u.alloc.oldsize; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: ** Adjust the size due to the options. michael@0: */ michael@0: actualSize = actualByteSize(options, size); michael@0: if(TM_EVENT_REALLOC == type && 0 != inEvent->u.alloc.oldserial) michael@0: { michael@0: actualOldSize = actualByteSize(options, inEvent->u.alloc.oldsize); michael@0: } michael@0: michael@0: /* michael@0: ** Modify event specific data. michael@0: */ michael@0: switch(type) michael@0: { michael@0: case TM_EVENT_MALLOC: michael@0: stats->uMallocs++; michael@0: stats->uMallocSize += actualSize; michael@0: stats->uMallocCost += ticks2msec(inReader, inEvent->u.alloc.cost); michael@0: stats->uMemoryInUse += actualSize; michael@0: stats->uObjectsInUse++; michael@0: michael@0: addVariance(&stats->mMallocSizeVar, actualSize); michael@0: addVariance(&stats->mMallocCostVar, inEvent->u.alloc.cost); michael@0: break; michael@0: michael@0: case TM_EVENT_CALLOC: michael@0: stats->uCallocs++; michael@0: stats->uCallocSize += actualSize; michael@0: stats->uCallocCost += ticks2msec(inReader, inEvent->u.alloc.cost); michael@0: stats->uMemoryInUse += actualSize; michael@0: stats->uObjectsInUse++; michael@0: michael@0: addVariance(&stats->mCallocSizeVar, actualSize); michael@0: addVariance(&stats->mCallocCostVar, inEvent->u.alloc.cost); michael@0: break; michael@0: michael@0: case TM_EVENT_REALLOC: michael@0: stats->uReallocs++; michael@0: stats->uReallocSize -= actualOldSize; michael@0: stats->uReallocSize += actualSize; michael@0: stats->uReallocCost += ticks2msec(inReader, inEvent->u.alloc.cost); michael@0: stats->uMemoryInUse -= actualOldSize; michael@0: stats->uMemoryInUse += actualSize; michael@0: if(0 == inEvent->u.alloc.oldserial) michael@0: { michael@0: stats->uObjectsInUse++; michael@0: } michael@0: michael@0: if(actualSize > actualOldSize) michael@0: { michael@0: addVariance(&stats->mReallocSizeVar, actualSize - actualOldSize); michael@0: } michael@0: else michael@0: { michael@0: addVariance(&stats->mReallocSizeVar, actualOldSize - actualSize); michael@0: } michael@0: addVariance(&stats->mReallocCostVar, inEvent->u.alloc.cost); michael@0: break; michael@0: michael@0: case TM_EVENT_FREE: michael@0: stats->uFrees++; michael@0: stats->uFreeSize += actualSize; michael@0: stats->uFreeCost += ticks2msec(inReader, inEvent->u.alloc.cost); michael@0: stats->uMemoryInUse -= actualSize; michael@0: stats->uObjectsInUse--; michael@0: michael@0: addVariance(&stats->mFreeSizeVar, actualSize); michael@0: addVariance(&stats->mFreeCostVar, inEvent->u.alloc.cost); michael@0: break; michael@0: michael@0: default: michael@0: /* michael@0: ** Don't care. michael@0: */ michael@0: break; michael@0: } michael@0: michael@0: switch(type) michael@0: { michael@0: case TM_EVENT_MALLOC: michael@0: case TM_EVENT_CALLOC: michael@0: case TM_EVENT_REALLOC: michael@0: /* michael@0: ** Check the peaks. michael@0: */ michael@0: if(stats->uMemoryInUse > stats->uPeakMemory) michael@0: { michael@0: stats->uPeakMemory = stats->uMemoryInUse; michael@0: } michael@0: if(stats->uObjectsInUse > stats->uPeakObjects) michael@0: { michael@0: stats->uPeakObjects = stats->uObjectsInUse; michael@0: } michael@0: michael@0: /* michael@0: ** Falling through. michael@0: */ michael@0: michael@0: case TM_EVENT_FREE: michael@0: /* michael@0: ** Check the overall time. michael@0: */ michael@0: interval = ticks2msec(inReader, inEvent->u.alloc.interval); michael@0: if(stats->uMinTicks > interval) michael@0: { michael@0: stats->uMinTicks = interval; michael@0: } michael@0: if(stats->uMaxTicks < interval) michael@0: { michael@0: stats->uMaxTicks = interval; michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: /* michael@0: ** Don't care. michael@0: */ michael@0: break; michael@0: } michael@0: michael@0: } michael@0: michael@0: int report_stats(Options* inOptions, TMStats* inStats) michael@0: { michael@0: int retval = 0; michael@0: michael@0: fprintf(inOptions->mOutput, "Peak Memory Usage: %11d\n", inStats->uPeakMemory); michael@0: fprintf(inOptions->mOutput, "Memory Leaked: %11d\n", inStats->uMemoryInUse); michael@0: fprintf(inOptions->mOutput, "\n"); michael@0: michael@0: fprintf(inOptions->mOutput, "Peak Object Count: %11d\n", inStats->uPeakObjects); michael@0: fprintf(inOptions->mOutput, "Objects Leaked: %11d\n", inStats->uObjectsInUse); michael@0: if(0 != inOptions->mAverages && 0 != inStats->uObjectsInUse) michael@0: { michael@0: fprintf(inOptions->mOutput, "Average Leaked Object Size: %11.4f\n", (double)inStats->uMemoryInUse / (double)inStats->uObjectsInUse); michael@0: } michael@0: fprintf(inOptions->mOutput, "\n"); michael@0: michael@0: fprintf(inOptions->mOutput, "Call Total: %11d\n", inStats->uMallocs + inStats->uCallocs + inStats->uReallocs + inStats->uFrees); michael@0: fprintf(inOptions->mOutput, " malloc: %11d\n", inStats->uMallocs); michael@0: fprintf(inOptions->mOutput, " calloc: %11d\n", inStats->uCallocs); michael@0: fprintf(inOptions->mOutput, " realloc: %11d\n", inStats->uReallocs); michael@0: fprintf(inOptions->mOutput, " free: %11d\n", inStats->uFrees); michael@0: fprintf(inOptions->mOutput, "\n"); michael@0: michael@0: fprintf(inOptions->mOutput, "Byte Total (sans free): %11d\n", inStats->uMallocSize + inStats->uCallocSize + inStats->uReallocSize); michael@0: fprintf(inOptions->mOutput, " malloc: %11d\n", inStats->uMallocSize); michael@0: fprintf(inOptions->mOutput, " calloc: %11d\n", inStats->uCallocSize); michael@0: fprintf(inOptions->mOutput, " realloc: %11d\n", inStats->uReallocSize); michael@0: fprintf(inOptions->mOutput, " free: %11d\n", inStats->uFreeSize); michael@0: if(0 != inOptions->mAverages) michael@0: { michael@0: fprintf(inOptions->mOutput, "Byte Averages:\n"); michael@0: fprintf(inOptions->mOutput, " malloc: %11.4f\n", getAverage(&inStats->mMallocSizeVar)); michael@0: fprintf(inOptions->mOutput, " calloc: %11.4f\n", getAverage(&inStats->mCallocSizeVar)); michael@0: fprintf(inOptions->mOutput, " realloc: %11.4f\n", getAverage(&inStats->mReallocSizeVar)); michael@0: fprintf(inOptions->mOutput, " free: %11.4f\n", getAverage(&inStats->mFreeSizeVar)); michael@0: } michael@0: if(0 != inOptions->mDeviances) michael@0: { michael@0: fprintf(inOptions->mOutput, "Byte Standard Deviations:\n"); michael@0: fprintf(inOptions->mOutput, " malloc: %11.4f\n", getStdDev(&inStats->mMallocSizeVar)); michael@0: fprintf(inOptions->mOutput, " calloc: %11.4f\n", getStdDev(&inStats->mCallocSizeVar)); michael@0: fprintf(inOptions->mOutput, " realloc: %11.4f\n", getStdDev(&inStats->mReallocSizeVar)); michael@0: fprintf(inOptions->mOutput, " free: %11.4f\n", getStdDev(&inStats->mFreeSizeVar)); michael@0: } michael@0: fprintf(inOptions->mOutput, "\n"); michael@0: michael@0: fprintf(inOptions->mOutput, "Overhead Total: %11.4f\n", COST_PRINTABLE(inStats->uMallocCost) + COST_PRINTABLE(inStats->uCallocCost) + COST_PRINTABLE(inStats->uReallocCost) + COST_PRINTABLE(inStats->uFreeCost)); michael@0: fprintf(inOptions->mOutput, " malloc: %11.4f\n", COST_PRINTABLE(inStats->uMallocCost)); michael@0: fprintf(inOptions->mOutput, " calloc: %11.4f\n", COST_PRINTABLE(inStats->uCallocCost)); michael@0: fprintf(inOptions->mOutput, " realloc: %11.4f\n", COST_PRINTABLE(inStats->uReallocCost)); michael@0: fprintf(inOptions->mOutput, " free: %11.4f\n", COST_PRINTABLE(inStats->uFreeCost)); michael@0: if(0 != inOptions->mAverages) michael@0: { michael@0: fprintf(inOptions->mOutput, "Overhead Averages:\n"); michael@0: fprintf(inOptions->mOutput, " malloc: %11.4f\n", COST_PRINTABLE(getAverage(&inStats->mMallocCostVar))); michael@0: fprintf(inOptions->mOutput, " calloc: %11.4f\n", COST_PRINTABLE(getAverage(&inStats->mCallocCostVar))); michael@0: fprintf(inOptions->mOutput, " realloc: %11.4f\n", COST_PRINTABLE(getAverage(&inStats->mReallocCostVar))); michael@0: fprintf(inOptions->mOutput, " free: %11.4f\n", COST_PRINTABLE(getAverage(&inStats->mFreeCostVar))); michael@0: } michael@0: if(0 != inOptions->mDeviances) michael@0: { michael@0: fprintf(inOptions->mOutput, "Overhead Standard Deviations:\n"); michael@0: fprintf(inOptions->mOutput, " malloc: %11.4f\n", COST_PRINTABLE(getStdDev(&inStats->mMallocCostVar))); michael@0: fprintf(inOptions->mOutput, " calloc: %11.4f\n", COST_PRINTABLE(getStdDev(&inStats->mCallocCostVar))); michael@0: fprintf(inOptions->mOutput, " realloc: %11.4f\n", COST_PRINTABLE(getStdDev(&inStats->mReallocCostVar))); michael@0: fprintf(inOptions->mOutput, " free: %11.4f\n", COST_PRINTABLE(getStdDev(&inStats->mFreeCostVar))); michael@0: } michael@0: fprintf(inOptions->mOutput, "\n"); michael@0: michael@0: if(0 != inOptions->mRunLength) michael@0: { michael@0: unsigned length = inStats->uMaxTicks - inStats->uMinTicks; michael@0: michael@0: fprintf(inOptions->mOutput, "Run Length: %11.4f\n", COST_PRINTABLE(length)); michael@0: fprintf(inOptions->mOutput, "\n"); michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: michael@0: int tmstats(Options* inOptions) michael@0: /* michael@0: ** As quick as possible, load the input file and report stats. michael@0: */ michael@0: { michael@0: int retval = 0; michael@0: tmreader* tmr = NULL; michael@0: TMStats 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: tmr = tmreader_new(inOptions->mProgramName, &stats); michael@0: if(NULL != tmr) michael@0: { michael@0: int tmResult = 0; michael@0: michael@0: tmResult = tmreader_eventloop(tmr, 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: michael@0: tmreader_destroy(tmr); michael@0: tmr = NULL; michael@0: michael@0: if(0 == retval) michael@0: { michael@0: retval = report_stats(inOptions, &stats); michael@0: } 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 = tmstats(&options); michael@0: } michael@0: michael@0: cleanOptions(&options); michael@0: return retval; michael@0: } michael@0: