462 lines
11 KiB
C++
462 lines
11 KiB
C++
/*++
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
poolfilt.cpp
|
||
|
||
Abstract:
|
||
|
||
This module filters out the useful information from a sorted poolsnap output file.
|
||
|
||
Author:
|
||
|
||
Matt Bandy (t-mattba) 27-Jul-1998
|
||
|
||
Revision History:
|
||
|
||
27-Jul-1998 t-mattba
|
||
|
||
Modified module to conform to coding standards.
|
||
|
||
--*/
|
||
|
||
#include <nt.h>
|
||
#include <tchar.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
|
||
|
||
#define PF_NEW_TAG 0
|
||
#define PF_UPDATE 1
|
||
|
||
// globals
|
||
|
||
LONG MinimumAllocationsChangeToReport=1;
|
||
LONG MinimumBytesChangeToReport=1;
|
||
BOOLEAN ReportIncreasesOnly = TRUE;
|
||
|
||
|
||
VOID
|
||
PrintUsage(
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine prints an informational message about the proper usage of POOLFILT.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
_ftprintf(stderr, _T("Summarizes possible leaks in a sorted poolsnap output file.\n\n"));
|
||
_ftprintf(stderr, _T("POOLFILT file [/MINALLOCS:n] [/MINBYTES:n] [/ALL]\n\n"));
|
||
_ftprintf(stderr, _T("file The sorted poolsnap output file to summarize.\n"));
|
||
_ftprintf(stderr, _T("/MINALLOCS:n Reports only tags where open allocations change by at least n.\n"));
|
||
_ftprintf(stderr, _T("/MINBYTES:n Reports only tags where bytes allocated change by at least n.\n"));
|
||
_ftprintf(stderr, _T("/ALL Reports decreases as well as increases.\n"));
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
PrintTagInformation(
|
||
IN BOOLEAN AllocationsAlwaysGrow,
|
||
IN BOOLEAN BytesAlwaysGrow,
|
||
IN LPTSTR TagName,
|
||
IN LONG InitialAllocations,
|
||
IN LONG FinalAllocations,
|
||
IN LONG InitialBytes,
|
||
IN LONG FinalBytes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reports the memory usage of a single process.
|
||
|
||
Arguments:
|
||
|
||
AllocationsAlwaysGrow - TRUE if number of open allocations monotonically increases.
|
||
|
||
BytesAlwaysGrow - TRUE if number of bytes allocated monotonically increases.
|
||
|
||
TagName - the name of the tag being reported.
|
||
|
||
InitialAllocations - initial number of open allocations for this tag.
|
||
|
||
FinalAllocations - final number fo open allocations for this tag.
|
||
|
||
InitialBytes - initial number of bytes allocated for this tag.
|
||
|
||
FinalBytes - final number of bytes allocated for this tag.
|
||
|
||
Return value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
_TCHAR AllocationsString[64];
|
||
_TCHAR BytesString[64];
|
||
|
||
if(((!ReportIncreasesOnly) &&
|
||
(abs(FinalAllocations - InitialAllocations) >=
|
||
MinimumAllocationsChangeToReport)) ||
|
||
(FinalAllocations - InitialAllocations >=
|
||
MinimumAllocationsChangeToReport)) {
|
||
|
||
_stprintf(AllocationsString, _T("%10d->%10d"), InitialAllocations, FinalAllocations);
|
||
|
||
} else {
|
||
|
||
_tcscpy(AllocationsString, _T(" "));
|
||
|
||
}
|
||
|
||
if(((!ReportIncreasesOnly) &&
|
||
(abs(FinalBytes - InitialBytes) >=
|
||
MinimumBytesChangeToReport)) ||
|
||
(FinalBytes - InitialBytes >=
|
||
MinimumBytesChangeToReport)) {
|
||
|
||
_stprintf(BytesString, _T("%10d->%10d"), InitialBytes, FinalBytes);
|
||
|
||
} else {
|
||
|
||
_tcscpy(BytesString, _T(" "));
|
||
|
||
}
|
||
|
||
_tprintf(_T("%c%c %s %s %s\n"),
|
||
(AllocationsAlwaysGrow && (FinalAllocations != InitialAllocations) ? _T('!') : _T(' ')),
|
||
(BytesAlwaysGrow && (FinalBytes != InitialBytes) ? _T('!') : _T(' ')),
|
||
TagName, AllocationsString, BytesString);
|
||
|
||
}
|
||
|
||
LONG _cdecl
|
||
_tmain(
|
||
IN LONG argc,
|
||
IN LPTSTR argv[]
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine parses program arguments, reads the input file, and outputs the result.
|
||
|
||
Arguments:
|
||
|
||
argc - Number of command line arguments.
|
||
|
||
argv - Command line arguments.
|
||
|
||
Return value:
|
||
|
||
0 if filtering is successful, 1 otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
try {
|
||
|
||
_TCHAR LineBuffer[256];
|
||
_TCHAR PoolTag[11];
|
||
LONG CurrentState = PF_NEW_TAG;
|
||
LONG InitialAllocations = 0;
|
||
LONG FinalAllocations = 0;
|
||
LONG NewAllocations = 0;
|
||
LONG InitialBytes = 0;
|
||
LONG FinalBytes = 0;
|
||
LONG NewBytes = 0;
|
||
BOOLEAN AllocationsAlwaysGrow = TRUE;
|
||
BOOLEAN BytesAlwaysGrow = TRUE;
|
||
LPTSTR InputFileName = NULL;
|
||
BOOLEAN InterpretedArgument = FALSE;
|
||
FILE *InputFile = NULL;
|
||
TCHAR * ReadResult;
|
||
int ScanResult;
|
||
|
||
// make sure PoolTag is properly terminated
|
||
|
||
PoolTag[10]=_T('\0');
|
||
|
||
// process arguments
|
||
|
||
for(LONG n = 1; n < argc; n++) {
|
||
|
||
InterpretedArgument = FALSE;
|
||
switch(argv[n][0]) {
|
||
|
||
case _T('-'):
|
||
|
||
case _T('/'):
|
||
|
||
// it's a switch
|
||
|
||
if(!_tcsnicmp(argv[n]+1, _T("minallocs:"), 10)) {
|
||
|
||
MinimumAllocationsChangeToReport = _ttoi(argv[n]+11);
|
||
InterpretedArgument = TRUE;
|
||
|
||
}
|
||
|
||
if(!_tcsnicmp(argv[n]+1, _T("minbytes:"), 9)) {
|
||
|
||
MinimumBytesChangeToReport = _ttoi(argv[n]+10);
|
||
InterpretedArgument = TRUE;
|
||
|
||
}
|
||
|
||
if(!_tcsicmp(argv[n]+1, _T("all"))) {
|
||
|
||
ReportIncreasesOnly = FALSE;
|
||
InterpretedArgument = TRUE;
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
// it's a filename
|
||
|
||
if(InputFileName != NULL) {
|
||
|
||
// already have the filename
|
||
|
||
PrintUsage();
|
||
return 1;
|
||
|
||
}
|
||
|
||
InputFileName = argv[n];
|
||
InterpretedArgument = TRUE;
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
if(!InterpretedArgument) {
|
||
|
||
PrintUsage();
|
||
return 1;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if(InputFileName == NULL) {
|
||
|
||
// user didn't specify filename
|
||
|
||
PrintUsage();
|
||
return 1;
|
||
|
||
}
|
||
|
||
InputFile = _tfopen(InputFileName, _T("rt"));
|
||
|
||
if(InputFile == NULL) {
|
||
|
||
_ftprintf(stderr, _T("Cannot open input file."));
|
||
return 1;
|
||
|
||
}
|
||
|
||
// get first line
|
||
|
||
ReadResult = _fgetts(LineBuffer, 256, InputFile);
|
||
|
||
if (ReadResult == NULL) {
|
||
|
||
_ftprintf(stderr, _T("Input is not a sorted poolsnap log."));
|
||
return 1;
|
||
}
|
||
|
||
// simple check for sorted poolsnap output
|
||
|
||
if(_tcsncmp(LineBuffer, _T(" Tag Type Allocs Frees Diff Bytes Per Alloc"), 60)) {
|
||
|
||
_ftprintf(stderr, _T("Input is not a sorted poolsnap log."));
|
||
return 1;
|
||
}
|
||
|
||
// get next line
|
||
|
||
ReadResult = _fgetts(LineBuffer, 256, InputFile);
|
||
|
||
if (ReadResult == NULL) {
|
||
|
||
_ftprintf(stderr, _T("Input is not a sorted poolsnap log."));
|
||
return 1;
|
||
}
|
||
|
||
while(!feof(InputFile)) {
|
||
|
||
if(!_tcscmp(LineBuffer,_T("\n"))) {
|
||
|
||
CurrentState = PF_NEW_TAG;
|
||
|
||
if(ReportIncreasesOnly) {
|
||
|
||
if(((FinalAllocations - InitialAllocations) >= MinimumAllocationsChangeToReport)
|
||
|| ((FinalBytes - InitialBytes) >= MinimumBytesChangeToReport)) {
|
||
|
||
PrintTagInformation(AllocationsAlwaysGrow, BytesAlwaysGrow,
|
||
PoolTag, InitialAllocations, FinalAllocations,
|
||
InitialBytes, FinalBytes);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
if((abs(FinalAllocations - InitialAllocations) >= MinimumAllocationsChangeToReport)
|
||
|| (abs(FinalBytes - InitialBytes) >= MinimumBytesChangeToReport)) {
|
||
|
||
PrintTagInformation(AllocationsAlwaysGrow, BytesAlwaysGrow,
|
||
PoolTag, InitialAllocations, FinalAllocations,
|
||
InitialBytes, FinalBytes);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
if(_tcslen(LineBuffer) <= 42) {
|
||
|
||
_ftprintf(stderr, _T("Format violated.\n"));
|
||
return 1;
|
||
|
||
}
|
||
|
||
switch(CurrentState) {
|
||
|
||
case PF_NEW_TAG:
|
||
|
||
// get tag and paged/non-paged
|
||
|
||
_tcsncpy(PoolTag, LineBuffer+1, 10);
|
||
|
||
// get allocs
|
||
|
||
ScanResult = _stscanf(LineBuffer+32, _T("%d"), &InitialAllocations);
|
||
|
||
if (ScanResult != 1) {
|
||
_ftprintf(stderr, _T("Format violated.\n"));
|
||
return 1;
|
||
}
|
||
|
||
// get bytes
|
||
|
||
ScanResult = _stscanf(LineBuffer+42, _T("%d"), &InitialBytes);
|
||
|
||
if (ScanResult != 1) {
|
||
_ftprintf(stderr, _T("Format violated.\n"));
|
||
return 1;
|
||
}
|
||
|
||
// assume this always grows until we find a counterexample
|
||
|
||
AllocationsAlwaysGrow = TRUE;
|
||
BytesAlwaysGrow = TRUE;
|
||
|
||
// this is initial and final until we find another
|
||
|
||
FinalAllocations = InitialAllocations;
|
||
FinalBytes = InitialBytes;
|
||
|
||
// keep updating this tag
|
||
|
||
CurrentState = PF_UPDATE;
|
||
break;
|
||
|
||
case PF_UPDATE:
|
||
|
||
// get allocs
|
||
|
||
ScanResult = _stscanf(LineBuffer+32, _T("%d"), &NewAllocations);
|
||
|
||
if (ScanResult != 1) {
|
||
_ftprintf(stderr, _T("Format violated.\n"));
|
||
return 1;
|
||
}
|
||
|
||
// get bytes
|
||
|
||
ScanResult = _stscanf(LineBuffer+42, _T("%d"), &NewBytes);
|
||
|
||
if (ScanResult != 1) {
|
||
_ftprintf(stderr, _T("Format violated.\n"));
|
||
return 1;
|
||
}
|
||
|
||
// did allocs decrease?
|
||
|
||
if(NewAllocations < FinalAllocations) {
|
||
|
||
AllocationsAlwaysGrow = FALSE;
|
||
|
||
}
|
||
|
||
// did bytes decrease?
|
||
|
||
if(NewBytes < FinalBytes) {
|
||
|
||
BytesAlwaysGrow = FALSE;
|
||
|
||
}
|
||
|
||
// copy new to final
|
||
|
||
FinalAllocations = NewAllocations;
|
||
FinalBytes = NewBytes;
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
// get next line
|
||
ReadResult = _fgetts(LineBuffer, 256, InputFile);
|
||
|
||
if (ReadResult == NULL) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
// done
|
||
fclose(InputFile);
|
||
return 0;
|
||
|
||
} catch (...) {
|
||
|
||
// this is mostly intended to catch out-of-memory conditions
|
||
|
||
_tprintf(_T("\nAn exception was detected. POOLFILT aborted.\n"));
|
||
return 1;
|
||
|
||
}
|
||
|
||
}
|