mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
881 lines
27 KiB
C
881 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;
|
||
|
}
|
||
|
|