forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			880 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			880 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 | |
|  *
 | |
|  * ***** BEGIN LICENSE BLOCK *****
 | |
|  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 | |
|  *
 | |
|  * The contents of this file are subject to the Mozilla Public License Version
 | |
|  * 1.1 (the "License"); you may not use this file except in compliance with
 | |
|  * the License. You may obtain a copy of the License at
 | |
|  * http://www.mozilla.org/MPL/
 | |
|  *
 | |
|  * Software distributed under the License is distributed on an "AS IS" basis,
 | |
|  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 | |
|  * for the specific language governing rights and limitations under the
 | |
|  * License.
 | |
|  *
 | |
|  * The Original Code is tmstats.c code, released
 | |
|  * Oct 25, 2002.
 | |
|  *
 | |
|  * The Initial Developer of the Original Code is
 | |
|  * Netscape Communications Corporation.
 | |
|  * Portions created by the Initial Developer are Copyright (C) 2002
 | |
|  * the Initial Developer. All Rights Reserved.
 | |
|  *
 | |
|  * Contributor(s):
 | |
|  *   Garrett Arch Blythe, 25-October-2002
 | |
|  *
 | |
|  * Alternatively, the contents of this file may be used under the terms of
 | |
|  * either the GNU General Public License Version 2 or later (the "GPL"), or
 | |
|  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 | |
|  * in which case the provisions of the GPL or the LGPL are applicable instead
 | |
|  * of those above. If you wish to allow use of your version of this file only
 | |
|  * under the terms of either the GPL or the LGPL, and not to allow others to
 | |
|  * use your version of this file under the terms of the MPL, indicate your
 | |
|  * decision by deleting the provisions above and replace them with the notice
 | |
|  * and other provisions required by the GPL or the LGPL. If you do not delete
 | |
|  * the provisions above, a recipient may use your version of this file under
 | |
|  * the terms of any one of the MPL, the GPL or the LGPL.
 | |
|  *
 | |
|  * ***** END LICENSE BLOCK ***** */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <time.h>
 | |
| #include <ctype.h>
 | |
| #include <errno.h>
 | |
| #include <math.h>
 | |
| 
 | |
| #include "nspr.h"
 | |
| #include "tmreader.h"
 | |
| 
 | |
| #define ERROR_REPORT(num, val, msg)   fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
 | |
| #define CLEANUP(ptr)    do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
 | |
| 
 | |
| 
 | |
| #define COST_RESOLUTION 1000
 | |
| #define COST_PRINTABLE(cost) ((PRFloat64)(cost) / (PRFloat64)COST_RESOLUTION)
 | |
| 
 | |
| 
 | |
| typedef struct __struct_Options
 | |
| /*
 | |
| **  Options to control how we perform.
 | |
| **
 | |
| **  mProgramName    Used in help text.
 | |
| **  mInputName      Name of the file.
 | |
| **  mOutput         Output file, append.
 | |
| **                  Default is stdout.
 | |
| **  mOutputName     Name of the file.
 | |
| **  mHelp           Whether or not help should be shown.
 | |
| **  mOverhead       How much overhead an allocation will have.
 | |
| **  mAlignment      What boundry will the end of an allocation line up on.
 | |
| **  mAverages       Whether or not to display averages.
 | |
| **  mDeviances      Whether or not to display standard deviations.
 | |
| **  mRunLength      Whether or not to display run length.
 | |
| */
 | |
| {
 | |
|     const char* mProgramName;
 | |
|     char* mInputName;
 | |
|     FILE* mOutput;
 | |
|     char* mOutputName;
 | |
|     int mHelp;
 | |
|     unsigned mOverhead;
 | |
|     unsigned mAlignment;
 | |
|     int mAverages;
 | |
|     int mDeviances;
 | |
|     int mRunLength;
 | |
| }
 | |
| Options;
 | |
| 
 | |
| 
 | |
| typedef struct __struct_Switch
 | |
| /*
 | |
| **  Command line options.
 | |
| */
 | |
| {
 | |
|     const char* mLongName;
 | |
|     const char* mShortName;
 | |
|     int mHasValue;
 | |
|     const char* mValue;
 | |
|     const char* mDescription;
 | |
| }
 | |
| Switch;
 | |
| 
 | |
| #define DESC_NEWLINE "\n\t\t"
 | |
| 
 | |
| static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."};
 | |
| static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."};
 | |
| static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."};
 | |
| 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."};
 | |
| 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."};
 | |
| static Switch gAveragesSwitch = {"--averages", "-avg", 0, NULL, "Display averages."};
 | |
| static Switch gDeviationsSwitch = {"--deviations", "-dev", 0, NULL, "Display standard deviations from the average."  DESC_NEWLINE "Implies --averages."};
 | |
| static Switch gRunLengthSwitch = {"--run-length", "-rl", 0, NULL, "Display the run length in seconds."};
 | |
| 
 | |
| static Switch* gSwitches[] = {
 | |
|         &gInputSwitch,
 | |
|         &gOutputSwitch,
 | |
|         &gAlignmentSwitch,
 | |
|         &gOverheadSwitch,
 | |
|         &gAveragesSwitch,
 | |
|         &gDeviationsSwitch,
 | |
|         &gRunLengthSwitch,
 | |
|         &gHelpSwitch
 | |
| };
 | |
| 
 | |
| 
 | |
| typedef struct _struct_VarianceState
 | |
| /*
 | |
| **  State for a single pass variance calculation.
 | |
| */
 | |
| {
 | |
|     unsigned mCount;
 | |
|     PRUint64 mSum;
 | |
|     PRUint64 mSquaredSum;
 | |
| }
 | |
| VarianceState;
 | |
| 
 | |
| 
 | |
| typedef struct __struct_TMStats
 | |
| /*
 | |
| **  Stats we are trying to calculate.
 | |
| **
 | |
| **  mOptions        Obilgatory options pointer.
 | |
| **  uMemoryInUse    Current tally of memory in use.
 | |
| **  uPeakMemory     Heap topped out at this byte level.
 | |
| **  uObjectsInUse   Different allocations outstanding.
 | |
| **  uPeakObjects    Highest object count.
 | |
| **  uMallocs        Number of malloc calls.
 | |
| **  uCallocs        Number of calloc calls.
 | |
| **  uReallocs       Number of realloc calls.
 | |
| **  uFrees          Number of free calls.
 | |
| **  uMallocSize     Bytes from malloc.
 | |
| **  uCallocSize     Bytes from calloc.
 | |
| **  uReallocSize    Bytes from realloc.
 | |
| **  uFreeSize       Bytes from free.
 | |
| **  mMallocSizeVar  Variance of bytes.
 | |
| **  mCallocSizeVar  Variance of bytes.
 | |
| **  mReallocSizeVar Variance of bytes.
 | |
| **  mFreeSizeVar    Variance of bytes.
 | |
| **  uMallocCost     Time of mallocs.
 | |
| **  uCallocCost     Time of callocs.
 | |
| **  uReallocCost    Time of reallocs.
 | |
| **  uFreeCost       Time of frees.
 | |
| **  mMallocCostVar  Variance of cost.
 | |
| **  mCallocCostVar  Variance of cost.
 | |
| **  mReallocCostVar Variance of cost.
 | |
| **  mFreeCostVar    Variance of cost.
 | |
| **  uMinTicks       Start of run.
 | |
| **  uMaxTicks       End of run.
 | |
| */
 | |
| {
 | |
|     Options* mOptions;
 | |
|     unsigned uMemoryInUse;
 | |
|     unsigned uPeakMemory;
 | |
|     unsigned uObjectsInUse;
 | |
|     unsigned uPeakObjects;
 | |
|     unsigned uMallocs;
 | |
|     unsigned uCallocs;
 | |
|     unsigned uReallocs;
 | |
|     unsigned uFrees;
 | |
| 
 | |
|     unsigned uMallocSize;
 | |
|     unsigned uCallocSize;
 | |
|     unsigned uReallocSize;
 | |
|     unsigned uFreeSize;
 | |
|     VarianceState mMallocSizeVar;
 | |
|     VarianceState mCallocSizeVar;
 | |
|     VarianceState mReallocSizeVar;
 | |
|     VarianceState mFreeSizeVar;
 | |
| 
 | |
|     unsigned uMallocCost;
 | |
|     unsigned uCallocCost;
 | |
|     unsigned uReallocCost;
 | |
|     unsigned uFreeCost;
 | |
|     VarianceState mMallocCostVar;
 | |
|     VarianceState mCallocCostVar;
 | |
|     VarianceState mReallocCostVar;
 | |
|     VarianceState mFreeCostVar;
 | |
| 
 | |
|     unsigned uMinTicks;
 | |
|     unsigned uMaxTicks;
 | |
| }
 | |
| TMStats;
 | |
| 
 | |
| 
 | |
| int initOptions(Options* outOptions, int inArgc, char** inArgv)
 | |
| /*
 | |
| **  returns int     0 if successful.
 | |
| */
 | |
| {
 | |
|     int retval = 0;
 | |
|     int loop = 0;
 | |
|     int switchLoop = 0;
 | |
|     int match = 0;
 | |
|     const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
 | |
|     Switch* current = NULL;
 | |
| 
 | |
|     /*
 | |
|     **  Set any defaults.
 | |
|     */
 | |
|     memset(outOptions, 0, sizeof(Options));
 | |
|     outOptions->mProgramName = inArgv[0];
 | |
|     outOptions->mInputName = strdup("-");
 | |
|     outOptions->mOutput = stdout;
 | |
|     outOptions->mOutputName = strdup("stdout");
 | |
|     outOptions->mAlignment = 16;
 | |
|     outOptions->mOverhead = 8;
 | |
| 
 | |
|     if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName)
 | |
|     {
 | |
|         retval = __LINE__;
 | |
|         ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup.");
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|     **  Go through and attempt to do the right thing.
 | |
|     */
 | |
|     for(loop = 1; loop < inArgc && 0 == retval; loop++)
 | |
|     {
 | |
|         match = 0;
 | |
|         current = NULL;
 | |
| 
 | |
|         for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++)
 | |
|         {
 | |
|             if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop]))
 | |
|             {
 | |
|                 match = __LINE__;
 | |
|             }
 | |
|             else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop]))
 | |
|             {
 | |
|                 match = __LINE__;
 | |
|             }
 | |
| 
 | |
|             if(match)
 | |
|             {
 | |
|                 if(gSwitches[switchLoop]->mHasValue)
 | |
|                 {
 | |
|                     /*
 | |
|                     **  Attempt to absorb next option to fullfill value.
 | |
|                     */
 | |
|                     if(loop + 1 < inArgc)
 | |
|                     {
 | |
|                         loop++;
 | |
| 
 | |
|                         current = gSwitches[switchLoop];
 | |
|                         current->mValue = inArgv[loop];
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     current = gSwitches[switchLoop];
 | |
|                 }
 | |
| 
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if(0 == match)
 | |
|         {
 | |
|             outOptions->mHelp = __LINE__;
 | |
|             retval = __LINE__;
 | |
|             ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch.");
 | |
|         }
 | |
|         else if(NULL == current)
 | |
|         {
 | |
|             outOptions->mHelp = __LINE__;
 | |
|             retval = __LINE__;
 | |
|             ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value.");
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             /*
 | |
|             ** Do something based on address/swtich.
 | |
|             */
 | |
|             if(current == &gInputSwitch)
 | |
|             {
 | |
|                 CLEANUP(outOptions->mInputName);
 | |
|                 outOptions->mInputName = strdup(current->mValue);
 | |
|                 if(NULL == outOptions->mInputName)
 | |
|                 {
 | |
|                     retval = __LINE__;
 | |
|                     ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
 | |
|                 }
 | |
|             }
 | |
|             else if(current == &gOutputSwitch)
 | |
|             {
 | |
|                 CLEANUP(outOptions->mOutputName);
 | |
|                 if(NULL != outOptions->mOutput && stdout != outOptions->mOutput)
 | |
|                 {
 | |
|                     fclose(outOptions->mOutput);
 | |
|                     outOptions->mOutput = NULL;
 | |
|                 }
 | |
| 
 | |
|                 outOptions->mOutput = fopen(current->mValue, "a");
 | |
|                 if(NULL == outOptions->mOutput)
 | |
|                 {
 | |
|                     retval = __LINE__;
 | |
|                     ERROR_REPORT(retval, current->mValue, "Unable to open output file.");
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     outOptions->mOutputName = strdup(current->mValue);
 | |
|                     if(NULL == outOptions->mOutputName)
 | |
|                     {
 | |
|                         retval = __LINE__;
 | |
|                         ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else if(current == &gHelpSwitch)
 | |
|             {
 | |
|                 outOptions->mHelp = __LINE__;
 | |
|             }
 | |
|             else if(current == &gAlignmentSwitch)
 | |
|             {
 | |
|                 unsigned arg = 0;
 | |
|                 char* endScan = NULL;
 | |
| 
 | |
|                 errno = 0;
 | |
|                 arg = strtoul(current->mValue, &endScan, 0);
 | |
|                 if(0 == errno && endScan != current->mValue)
 | |
|                 {
 | |
|                     outOptions->mAlignment = arg;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     retval = __LINE__;
 | |
|                     ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
 | |
|                 }
 | |
|             }
 | |
|             else if(current == &gOverheadSwitch)
 | |
|             {
 | |
|                 unsigned arg = 0;
 | |
|                 char* endScan = NULL;
 | |
| 
 | |
|                 errno = 0;
 | |
|                 arg = strtoul(current->mValue, &endScan, 0);
 | |
|                 if(0 == errno && endScan != current->mValue)
 | |
|                 {
 | |
|                     outOptions->mOverhead = arg;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     retval = __LINE__;
 | |
|                     ERROR_REPORT(retval, current->mValue, "Unable to convert to a number.");
 | |
|                 }
 | |
|             }
 | |
|             else if(current == &gAveragesSwitch)
 | |
|             {
 | |
|                 outOptions->mAverages = __LINE__;
 | |
|             }
 | |
|             else if(current == &gDeviationsSwitch)
 | |
|             {
 | |
|                 outOptions->mAverages = __LINE__;
 | |
|                 outOptions->mDeviances = __LINE__;
 | |
|             }
 | |
|             else if(current == &gRunLengthSwitch)
 | |
|             {
 | |
|                 outOptions->mRunLength = __LINE__;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 retval = __LINE__;
 | |
|                 ERROR_REPORT(retval, current->mLongName, "No handler for command line switch.");
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| void cleanOptions(Options* inOptions)
 | |
| /*
 | |
| **  Clean up any open handles.
 | |
| */
 | |
| {
 | |
|     unsigned loop = 0;
 | |
| 
 | |
|     CLEANUP(inOptions->mInputName);
 | |
|     CLEANUP(inOptions->mOutputName);
 | |
|     if(NULL != inOptions->mOutput && stdout != inOptions->mOutput)
 | |
|     {
 | |
|         fclose(inOptions->mOutput);
 | |
|     }
 | |
| 
 | |
|     memset(inOptions, 0, sizeof(Options));
 | |
| }
 | |
| 
 | |
| 
 | |
| void showHelp(Options* inOptions)
 | |
| /*
 | |
| **  Show some simple help text on usage.
 | |
| */
 | |
| {
 | |
|     int loop = 0;
 | |
|     const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
 | |
|     const char* valueText = NULL;
 | |
| 
 | |
|     printf("usage:\t%s [arguments]\n", inOptions->mProgramName);
 | |
|     printf("\n");
 | |
|     printf("arguments:\n");
 | |
| 
 | |
|     for(loop = 0; loop < switchCount; loop++)
 | |
|     {
 | |
|         if(gSwitches[loop]->mHasValue)
 | |
|         {
 | |
|             valueText = " <value>";
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             valueText = "";
 | |
|         }
 | |
| 
 | |
|         printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText);
 | |
|         printf("\t %s%s", gSwitches[loop]->mShortName, valueText);
 | |
|         printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription);
 | |
|     }
 | |
| 
 | |
|     printf("This tool reports simple heap usage and allocation call counts.\n");
 | |
|     printf("Useful for eyeballing trace-malloc numbers quickly.\n");
 | |
| }
 | |
| 
 | |
| 
 | |
| void addVariance(VarianceState* inVariance, unsigned inValue)
 | |
| /*
 | |
| **  Add a value to a variance state.
 | |
| */
 | |
| {
 | |
|     PRUint64 squared;
 | |
|     PRUint64 bigValue;
 | |
|     
 | |
|     LL_UI2L(bigValue, inValue);
 | |
| 
 | |
|     LL_ADD(inVariance->mSum, inVariance->mSum, bigValue);
 | |
| 
 | |
|     LL_MUL(squared, bigValue, bigValue);
 | |
|     LL_ADD(inVariance->mSquaredSum, inVariance->mSquaredSum, squared);
 | |
| 
 | |
|     inVariance->mCount++;
 | |
| }
 | |
| 
 | |
| 
 | |
| PRFloat64 getAverage(VarianceState* inVariance)
 | |
| /*
 | |
| **  Determine the mean/average based on the given state.
 | |
| */
 | |
| {
 | |
|     PRFloat64 retval = 0.0;
 | |
| 
 | |
|     if(NULL != inVariance && 0 < inVariance->mCount)
 | |
|     {
 | |
|         PRFloat64 count;
 | |
|         PRFloat64 sum;
 | |
|         PRInt64 isum;
 | |
| 
 | |
|         /*
 | |
|         **  Avoids a compiler error (not impl) under MSVC.
 | |
|         */
 | |
|         isum = inVariance->mSum;
 | |
| 
 | |
|         count = (PRFloat64)inVariance->mCount;
 | |
|         LL_L2F(sum, isum);
 | |
| 
 | |
|         retval = sum / count;
 | |
|     }
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| PRFloat64 getVariance(VarianceState* inVariance)
 | |
| /*
 | |
| **  Determine the variance based on the given state.
 | |
| */
 | |
| {
 | |
|     PRFloat64 retval = 0.0;
 | |
| 
 | |
|     if(NULL != inVariance && 1 < inVariance->mCount)
 | |
|     {
 | |
|         PRFloat64 count;
 | |
|         PRFloat64 squaredSum;
 | |
|         PRFloat64 avg;
 | |
|         PRFloat64 squaredAvg;
 | |
|         PRInt64 isquaredSum;
 | |
| 
 | |
|         /*
 | |
|         **  Avoids a compiler error (not impl) under MSVC.
 | |
|         */
 | |
|         isquaredSum = inVariance->mSquaredSum;
 | |
| 
 | |
|         count = (PRFloat64)inVariance->mCount;
 | |
|         LL_L2F(squaredSum, isquaredSum);
 | |
| 
 | |
|         avg = getAverage(inVariance);
 | |
|         squaredAvg = avg * avg;
 | |
| 
 | |
|         retval = (squaredSum - (count * squaredAvg)) / (count - 1.0);
 | |
|     }
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| PRFloat64 getStdDev(VarianceState* inVariance)
 | |
| /*
 | |
| **  Determine the standard deviation based on the given state.
 | |
| */
 | |
| {
 | |
|     PRFloat64 retval = 0.0;
 | |
|     PRFloat64 variance;
 | |
| 
 | |
|     variance = getVariance(inVariance);
 | |
|     retval = sqrt(variance);
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| unsigned actualByteSize(Options* inOptions, unsigned retval)
 | |
| /*
 | |
| **  Apply alignment and overhead to size to figure out actual byte size.
 | |
| **  This by default mimics spacetrace with default options (msvc crt heap).
 | |
| */
 | |
| {
 | |
|     if(0 != retval)
 | |
|     {
 | |
|         unsigned eval = 0;
 | |
|         unsigned over = 0;
 | |
| 
 | |
|         eval = retval - 1;
 | |
|         if(0 != inOptions->mAlignment)
 | |
|         {
 | |
|             over = eval % inOptions->mAlignment;
 | |
|         }
 | |
|         retval = eval + inOptions->mOverhead + inOptions->mAlignment - over;
 | |
|     }
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| PRUint32 ticks2xsec(tmreader* aReader, PRUint32 aTicks, PRUint32 aResolution)
 | |
| /*
 | |
| ** Convert platform specific ticks to second units
 | |
| ** Returns 0 on success.
 | |
| */
 | |
| {
 | |
|     PRUint32 retval = 0;
 | |
|     PRUint64 bigone;
 | |
|     PRUint64 tmp64;
 | |
| 
 | |
|     LL_UI2L(bigone, aResolution);
 | |
|     LL_UI2L(tmp64, aTicks);
 | |
|     LL_MUL(bigone, bigone, tmp64);
 | |
|     LL_UI2L(tmp64, aReader->ticksPerSec);
 | |
|     LL_DIV(bigone, bigone, tmp64);
 | |
|     LL_L2UI(retval, bigone);
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| #define ticks2msec(reader, ticks) ticks2xsec((reader), (ticks), 1000)
 | |
| 
 | |
| 
 | |
| void tmEventHandler(tmreader* inReader, tmevent* inEvent)
 | |
| /*
 | |
| **  Callback from the tmreader_eventloop.
 | |
| **  Keep it simple in here, this is where we'll spend the most time.
 | |
| **  The goal is to be fast.
 | |
| */
 | |
| {
 | |
|     TMStats* stats = (TMStats*)inReader->data;
 | |
|     Options* options = (Options*)stats->mOptions;
 | |
|     char type = inEvent->type;
 | |
|     unsigned size = inEvent->u.alloc.size;
 | |
|     unsigned actualSize = 0;
 | |
|     unsigned actualOldSize = 0;
 | |
|     PRUint32 interval = 0;
 | |
| 
 | |
|     /*
 | |
|     **  To match spacetrace stats, reallocs of size zero are frees.
 | |
|     **  Adjust the size to match what free expects.
 | |
|     */
 | |
|     if(TM_EVENT_REALLOC == type && 0 == size)
 | |
|     {
 | |
|         type = TM_EVENT_FREE;
 | |
|         if(0 != inEvent->u.alloc.oldserial)
 | |
|         {
 | |
|             size = inEvent->u.alloc.oldsize;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|     **  Adjust the size due to the options.
 | |
|     */
 | |
|     actualSize = actualByteSize(options, size);
 | |
|     if(TM_EVENT_REALLOC == type && 0 != inEvent->u.alloc.oldserial)
 | |
|     {
 | |
|         actualOldSize = actualByteSize(options, inEvent->u.alloc.oldsize);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|     **  Modify event specific data.
 | |
|     */
 | |
|     switch(type)
 | |
|     {
 | |
|     case TM_EVENT_MALLOC:
 | |
|         stats->uMallocs++;
 | |
|         stats->uMallocSize += actualSize;
 | |
|         stats->uMallocCost += ticks2msec(inReader, inEvent->u.alloc.cost);
 | |
|         stats->uMemoryInUse += actualSize;
 | |
|         stats->uObjectsInUse++;
 | |
| 
 | |
|         addVariance(&stats->mMallocSizeVar, actualSize);
 | |
|         addVariance(&stats->mMallocCostVar,  inEvent->u.alloc.cost);
 | |
|         break;
 | |
| 
 | |
|     case TM_EVENT_CALLOC:
 | |
|         stats->uCallocs++;
 | |
|         stats->uCallocSize += actualSize;
 | |
|         stats->uCallocCost += ticks2msec(inReader, inEvent->u.alloc.cost);
 | |
|         stats->uMemoryInUse += actualSize;
 | |
|         stats->uObjectsInUse++;
 | |
| 
 | |
|         addVariance(&stats->mCallocSizeVar, actualSize);
 | |
|         addVariance(&stats->mCallocCostVar,  inEvent->u.alloc.cost);
 | |
|         break;
 | |
| 
 | |
|     case TM_EVENT_REALLOC:
 | |
|         stats->uReallocs++;
 | |
|         stats->uReallocSize -= actualOldSize;
 | |
|         stats->uReallocSize += actualSize;
 | |
|         stats->uReallocCost += ticks2msec(inReader, inEvent->u.alloc.cost);
 | |
|         stats->uMemoryInUse -= actualOldSize;
 | |
|         stats->uMemoryInUse += actualSize;
 | |
|         if(0 == inEvent->u.alloc.oldserial)
 | |
|         {
 | |
|             stats->uObjectsInUse++;
 | |
|         }
 | |
| 
 | |
|         if(actualSize > actualOldSize)
 | |
|         {
 | |
|             addVariance(&stats->mReallocSizeVar, actualSize - actualOldSize);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             addVariance(&stats->mReallocSizeVar, actualOldSize - actualSize);
 | |
|         }
 | |
|         addVariance(&stats->mReallocCostVar,  inEvent->u.alloc.cost);
 | |
|         break;
 | |
| 
 | |
|     case TM_EVENT_FREE:
 | |
|         stats->uFrees++;
 | |
|         stats->uFreeSize += actualSize;
 | |
|         stats->uFreeCost += ticks2msec(inReader, inEvent->u.alloc.cost);
 | |
|         stats->uMemoryInUse -= actualSize;
 | |
|         stats->uObjectsInUse--;
 | |
| 
 | |
|         addVariance(&stats->mFreeSizeVar, actualSize);
 | |
|         addVariance(&stats->mFreeCostVar,  inEvent->u.alloc.cost);
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         /*
 | |
|         **  Don't care.
 | |
|         */
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     switch(type)
 | |
|     {
 | |
|     case TM_EVENT_MALLOC:
 | |
|     case TM_EVENT_CALLOC:
 | |
|     case TM_EVENT_REALLOC:
 | |
|         /*
 | |
|         **  Check the peaks.
 | |
|         */
 | |
|         if(stats->uMemoryInUse > stats->uPeakMemory)
 | |
|         {
 | |
|             stats->uPeakMemory = stats->uMemoryInUse;
 | |
|         }
 | |
|         if(stats->uObjectsInUse > stats->uPeakObjects)
 | |
|         {
 | |
|             stats->uPeakObjects = stats->uObjectsInUse;
 | |
|         }
 | |
| 
 | |
|         /*
 | |
|         **  Falling through.
 | |
|         */
 | |
| 
 | |
|     case TM_EVENT_FREE:
 | |
|         /*
 | |
|         **  Check the overall time.
 | |
|         */
 | |
|         interval = ticks2msec(inReader, inEvent->u.alloc.interval);
 | |
|         if(stats->uMinTicks > interval)
 | |
|         {
 | |
|             stats->uMinTicks = interval;
 | |
|         }
 | |
|         if(stats->uMaxTicks < interval)
 | |
|         {
 | |
|             stats->uMaxTicks = interval;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         /*
 | |
|         **  Don't care.
 | |
|         */
 | |
|         break;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| int report_stats(Options* inOptions, TMStats* inStats)
 | |
| {
 | |
|     int retval = 0;
 | |
| 
 | |
|     fprintf(inOptions->mOutput, "Peak Memory Usage:                   %11d\n", inStats->uPeakMemory);
 | |
|     fprintf(inOptions->mOutput, "Memory Leaked:                       %11d\n", inStats->uMemoryInUse);
 | |
|     fprintf(inOptions->mOutput, "\n");
 | |
| 
 | |
|     fprintf(inOptions->mOutput, "Peak Object Count:                   %11d\n", inStats->uPeakObjects);
 | |
|     fprintf(inOptions->mOutput, "Objects Leaked:                      %11d\n", inStats->uObjectsInUse);
 | |
|     if(0 != inOptions->mAverages && 0 != inStats->uObjectsInUse)
 | |
|     {
 | |
|         fprintf(inOptions->mOutput, "Average Leaked Object Size:          %11.4f\n", (PRFloat64)inStats->uMemoryInUse / (PRFloat64)inStats->uObjectsInUse);
 | |
|     }
 | |
|     fprintf(inOptions->mOutput, "\n");
 | |
| 
 | |
|     fprintf(inOptions->mOutput, "Call Total:                          %11d\n", inStats->uMallocs + inStats->uCallocs + inStats->uReallocs + inStats->uFrees);
 | |
|     fprintf(inOptions->mOutput, "        malloc:                      %11d\n", inStats->uMallocs);
 | |
|     fprintf(inOptions->mOutput, "        calloc:                      %11d\n", inStats->uCallocs);
 | |
|     fprintf(inOptions->mOutput, "       realloc:                      %11d\n", inStats->uReallocs);
 | |
|     fprintf(inOptions->mOutput, "          free:                      %11d\n", inStats->uFrees);
 | |
|     fprintf(inOptions->mOutput, "\n");
 | |
| 
 | |
|     fprintf(inOptions->mOutput, "Byte Total (sans free):              %11d\n", inStats->uMallocSize + inStats->uCallocSize + inStats->uReallocSize);
 | |
|     fprintf(inOptions->mOutput, "        malloc:                      %11d\n", inStats->uMallocSize);
 | |
|     fprintf(inOptions->mOutput, "        calloc:                      %11d\n", inStats->uCallocSize);
 | |
|     fprintf(inOptions->mOutput, "       realloc:                      %11d\n", inStats->uReallocSize);
 | |
|     fprintf(inOptions->mOutput, "          free:                      %11d\n", inStats->uFreeSize);
 | |
|     if(0 != inOptions->mAverages)
 | |
|     {
 | |
|         fprintf(inOptions->mOutput, "Byte Averages:\n");
 | |
|         fprintf(inOptions->mOutput, "        malloc:                      %11.4f\n", getAverage(&inStats->mMallocSizeVar));
 | |
|         fprintf(inOptions->mOutput, "        calloc:                      %11.4f\n", getAverage(&inStats->mCallocSizeVar));
 | |
|         fprintf(inOptions->mOutput, "       realloc:                      %11.4f\n", getAverage(&inStats->mReallocSizeVar));
 | |
|         fprintf(inOptions->mOutput, "          free:                      %11.4f\n", getAverage(&inStats->mFreeSizeVar));
 | |
|     }
 | |
|     if(0 != inOptions->mDeviances)
 | |
|     {
 | |
|         fprintf(inOptions->mOutput, "Byte Standard Deviations:\n");
 | |
|         fprintf(inOptions->mOutput, "        malloc:                      %11.4f\n", getStdDev(&inStats->mMallocSizeVar));
 | |
|         fprintf(inOptions->mOutput, "        calloc:                      %11.4f\n", getStdDev(&inStats->mCallocSizeVar));
 | |
|         fprintf(inOptions->mOutput, "       realloc:                      %11.4f\n", getStdDev(&inStats->mReallocSizeVar));
 | |
|         fprintf(inOptions->mOutput, "          free:                      %11.4f\n", getStdDev(&inStats->mFreeSizeVar));
 | |
|     }
 | |
|     fprintf(inOptions->mOutput, "\n");
 | |
|     
 | |
|     fprintf(inOptions->mOutput, "Overhead Total:                      %11.4f\n", COST_PRINTABLE(inStats->uMallocCost) + COST_PRINTABLE(inStats->uCallocCost) + COST_PRINTABLE(inStats->uReallocCost) + COST_PRINTABLE(inStats->uFreeCost));
 | |
|     fprintf(inOptions->mOutput, "        malloc:                      %11.4f\n", COST_PRINTABLE(inStats->uMallocCost));
 | |
|     fprintf(inOptions->mOutput, "        calloc:                      %11.4f\n", COST_PRINTABLE(inStats->uCallocCost));
 | |
|     fprintf(inOptions->mOutput, "       realloc:                      %11.4f\n", COST_PRINTABLE(inStats->uReallocCost));
 | |
|     fprintf(inOptions->mOutput, "          free:                      %11.4f\n", COST_PRINTABLE(inStats->uFreeCost));
 | |
|     if(0 != inOptions->mAverages)
 | |
|     {
 | |
|         fprintf(inOptions->mOutput, "Overhead Averages:\n");
 | |
|         fprintf(inOptions->mOutput, "        malloc:                      %11.4f\n", COST_PRINTABLE(getAverage(&inStats->mMallocCostVar)));
 | |
|         fprintf(inOptions->mOutput, "        calloc:                      %11.4f\n", COST_PRINTABLE(getAverage(&inStats->mCallocCostVar)));
 | |
|         fprintf(inOptions->mOutput, "       realloc:                      %11.4f\n", COST_PRINTABLE(getAverage(&inStats->mReallocCostVar)));
 | |
|         fprintf(inOptions->mOutput, "          free:                      %11.4f\n", COST_PRINTABLE(getAverage(&inStats->mFreeCostVar)));
 | |
|     }
 | |
|     if(0 != inOptions->mDeviances)
 | |
|     {
 | |
|         fprintf(inOptions->mOutput, "Overhead Standard Deviations:\n");
 | |
|         fprintf(inOptions->mOutput, "        malloc:                      %11.4f\n", COST_PRINTABLE(getStdDev(&inStats->mMallocCostVar)));
 | |
|         fprintf(inOptions->mOutput, "        calloc:                      %11.4f\n", COST_PRINTABLE(getStdDev(&inStats->mCallocCostVar)));
 | |
|         fprintf(inOptions->mOutput, "       realloc:                      %11.4f\n", COST_PRINTABLE(getStdDev(&inStats->mReallocCostVar)));
 | |
|         fprintf(inOptions->mOutput, "          free:                      %11.4f\n", COST_PRINTABLE(getStdDev(&inStats->mFreeCostVar)));
 | |
|     }
 | |
|     fprintf(inOptions->mOutput, "\n");
 | |
|     
 | |
|     if(0 != inOptions->mRunLength)
 | |
|     {
 | |
|         unsigned length = inStats->uMaxTicks - inStats->uMinTicks;
 | |
| 
 | |
|         fprintf(inOptions->mOutput, "Run Length:                          %11.4f\n", COST_PRINTABLE(length));
 | |
|         fprintf(inOptions->mOutput, "\n");
 | |
|     }
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| int tmstats(Options* inOptions)
 | |
| /*
 | |
| **  As quick as possible, load the input file and report stats.
 | |
| */
 | |
| {
 | |
|     int retval = 0;
 | |
|     tmreader* tmr = NULL;
 | |
|     TMStats stats;
 | |
| 
 | |
|     memset(&stats, 0, sizeof(stats));
 | |
|     stats.mOptions = inOptions;
 | |
|     stats.uMinTicks = 0xFFFFFFFFU;
 | |
| 
 | |
|     /*
 | |
|     **  Need a tmreader.
 | |
|     */
 | |
|     tmr = tmreader_new(inOptions->mProgramName, &stats);
 | |
|     if(NULL != tmr)
 | |
|     {
 | |
|         int tmResult = 0;
 | |
| 
 | |
|         tmResult = tmreader_eventloop(tmr, inOptions->mInputName, tmEventHandler);
 | |
|         if(0 == tmResult)
 | |
|         {
 | |
|             retval = __LINE__;
 | |
|             ERROR_REPORT(retval, inOptions->mInputName, "Problem reading trace-malloc data.");
 | |
|         }
 | |
| 
 | |
|         tmreader_destroy(tmr);
 | |
|         tmr = NULL;
 | |
| 
 | |
|         if(0 == retval)
 | |
|         {
 | |
|             retval = report_stats(inOptions, &stats);
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         retval = __LINE__;
 | |
|         ERROR_REPORT(retval, inOptions->mProgramName, "Unable to obtain tmreader.");
 | |
|     }
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| 
 | |
| int main(int inArgc, char** inArgv)
 | |
| {
 | |
|     int retval = 0;
 | |
|     Options options;
 | |
| 
 | |
|     retval = initOptions(&options, inArgc, inArgv);
 | |
|     if(options.mHelp)
 | |
|     {
 | |
|         showHelp(&options);
 | |
|     }
 | |
|     else if(0 == retval)
 | |
|     {
 | |
|         retval = tmstats(&options);
 | |
|     }
 | |
| 
 | |
|     cleanOptions(&options);
 | |
|     return retval;
 | |
| }
 | |
| 
 | 
