/*++ Copyright (c) 1998 Microsoft Corporation Module Name: memfilt.cpp Abstract: This module filters out the useful information from a sorted memsnap 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 #include #include #include #include #define MF_NEW_PROCESS 0 #define MF_UPDATE 1 // globals LONG MinimumCommitChangeToReport = 1; LONG MinimumHandleChangeToReport = 1; BOOLEAN ReportIncreasesOnly = TRUE; VOID PrintUsage( ) /*++ Routine Description: This routine prints an informational message about the proper usage of MEMFILT. Arguments: None. Return value: None. --*/ { _ftprintf(stderr, _T("Summarizes possible leaks in a sorted MEMSNAP output file.\n\n")); _ftprintf(stderr, _T("MEMFILT file [/MINCOMMIT:n] [/MINHANDLES:n] [/ALL]\n\n")); _ftprintf(stderr, _T("file A sorted memsnap output file.\n")); _ftprintf(stderr, _T("/MINCOMMIT:n Reports only processes where commit charge increased by\n")); _ftprintf(stderr, _T(" at least n.\n")); _ftprintf(stderr, _T("/MINHANDLES:n Reports only processes where handle count increased by\n")); _ftprintf(stderr, _T(" at least n.\n")); _ftprintf(stderr, _T("/ALL Reports decreases as well as increases.\n")); } VOID PrintProcessInformation( IN BOOLEAN CommitAlwaysGrows, IN BOOLEAN HandlesAlwaysGrow, IN LPTSTR ProcessName, IN LONG InitialCommit, IN LONG FinalCommit, IN LONG InitialHandles, IN LONG FinalHandles ) /*++ Routine Description: This routine reports the memory usage of a single process. Arguments: CommitAlwaysGrows - TRUE if commit monotonically increases. HandlesAlwaysGrow - TRUE if handles monotonically increase. ProcessName - the name of the process being reported. InitialCommit - initial commit charge for this process. FinalCommit - final commit charge for this process. InitialHandles - initial handle count for this process. FinalHandles - final handle count for this process. Return value: None. --*/ { _TCHAR CommitString[64]; _TCHAR HandlesString[64]; if(((!ReportIncreasesOnly) && (abs(FinalCommit - InitialCommit) >= MinimumCommitChangeToReport)) || (FinalCommit - InitialCommit >= MinimumCommitChangeToReport)) { _stprintf(CommitString, _T("%10d->%10d"), InitialCommit, FinalCommit); } else { _tcscpy(CommitString, _T(" ")); } if(((!ReportIncreasesOnly) && (abs(FinalHandles - InitialHandles) >= MinimumHandleChangeToReport)) || (FinalHandles - InitialHandles >= MinimumHandleChangeToReport)) { _stprintf(HandlesString, _T("%10d->%10d"), InitialHandles, FinalHandles); } else { _tcscpy(HandlesString, _T(" ")); } _tprintf(_T("%c%c %s %s %s\n"), (CommitAlwaysGrows && (FinalCommit != InitialCommit) ? _T('!') : _T(' ')), (HandlesAlwaysGrow && (FinalHandles != InitialHandles) ? _T('!') : _T(' ')), ProcessName, CommitString, HandlesString); } 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 { FILE *InputFile = NULL; _TCHAR LineBuffer[256]; _TCHAR ProcessName[64]; LONG CurrentState = MF_NEW_PROCESS; LONG InitialCommit = 0; LONG FinalCommit = 0; LONG NewCommit = 0; LONG InitialHandles = 0; LONG FinalHandles = 0; LONG NewHandles = 0; LONG MonotonicallyIncreasing = 0; BOOLEAN CommitAlwaysGrows = TRUE; BOOLEAN HandlesAlwaysGrow = TRUE; BOOLEAN InterpretedArgument = FALSE; LONG Processes = 0; LPTSTR InputFileName = NULL; // make sure ProcessName is properly terminated ProcessName[30]=_T('\0'); // parse command line arguments if(argc < 2) { PrintUsage(); return 1; } for(LONG n=1; n= MinimumCommitChangeToReport) || ((FinalHandles - InitialHandles) >= MinimumHandleChangeToReport)) { PrintProcessInformation(CommitAlwaysGrows, HandlesAlwaysGrow, ProcessName, InitialCommit, FinalCommit, InitialHandles, FinalHandles); } } else { if((abs(FinalCommit - InitialCommit) >= MinimumCommitChangeToReport) || (abs(FinalHandles - InitialHandles) >= MinimumHandleChangeToReport)) { PrintProcessInformation(CommitAlwaysGrows, HandlesAlwaysGrow, ProcessName, InitialCommit, FinalCommit, InitialHandles, FinalHandles); } } } else { if(_tcslen(LineBuffer) <= 80) { _ftprintf(stderr, _T("Format violated.\n")); return 1; } switch(CurrentState) { case MF_NEW_PROCESS: _tcsncpy(ProcessName, LineBuffer, 30); if (_stscanf(LineBuffer+70, _T("%d"), &InitialCommit) != 1) break; if (_stscanf(LineBuffer+80, _T("%d"), &InitialHandles) != 1) break; FinalCommit = 0; FinalHandles = 0; CommitAlwaysGrows = TRUE; HandlesAlwaysGrow = TRUE; CurrentState = MF_UPDATE; break; case MF_UPDATE: if (_stscanf(LineBuffer+70, _T("%d"), &NewCommit) != 1) break; if (_stscanf(LineBuffer+80, _T("%d"), &NewHandles) != 1) break; if(NewCommit < FinalCommit) { CommitAlwaysGrows = FALSE; } if(NewHandles < FinalHandles) { HandlesAlwaysGrow = FALSE; } FinalCommit = NewCommit; FinalHandles = NewHandles; break; } } if (!_fgetts(LineBuffer, 256, InputFile)) { _ftprintf(stderr, _T("Cannot read input file.\n")); return 1; } } fclose(InputFile); return 0; } catch (...) { // this is mostly intended to catch out-of-memory errors _tprintf(_T("\nAn exception was detected. MEMFILT aborted.\n")); return 1; } }