1014 lines
24 KiB
C++
1014 lines
24 KiB
C++
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ohcmp.cpp
|
|
|
|
Abstract:
|
|
|
|
This module reports the differences between two oh output files.
|
|
|
|
Author:
|
|
|
|
Matt Bandy (t-mattba) 23-Jul-1998
|
|
|
|
Revision History:
|
|
|
|
24-Jul-1998 t-mattba
|
|
|
|
Modified module to conform to coding standards.
|
|
|
|
11-Jun-2001 silviuc
|
|
|
|
Deal with handles that are recreated with a different value
|
|
and other simple output improvements (sorted output etc.).
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <common.ver>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <tchar.h>
|
|
#include "MAPSTRINGINT.h"
|
|
|
|
LPTSTR HelpText =
|
|
TEXT("ohcmp - Display difference between two OH output files --") BUILD_MACHINE_TAG TEXT("\n")
|
|
VER_LEGALCOPYRIGHT_STR TEXT("\n")
|
|
TEXT(" \n")
|
|
TEXT("ohcmp [OPTION ...] BEFORE_OH_FILE AFTER_OH_FILE \n")
|
|
TEXT(" \n")
|
|
TEXT("/h Print most interesting increases in a separate initial section. \n")
|
|
TEXT("/t Do not add TRACE id to the names if files contain traces. \n")
|
|
TEXT("/all Report decreases as well as increases. \n")
|
|
TEXT(" \n")
|
|
TEXT("If the OH files have been created with -h option (they contain traces) \n")
|
|
TEXT("then ohcmp will print Names having this syntax: (TRACEID) NAME. \n")
|
|
TEXT("In case of a potential leak just search for the TRACEID in the original\n")
|
|
TEXT("OH file to find the stack trace. \n")
|
|
TEXT(" \n");
|
|
|
|
LPTSTR
|
|
SearchStackTrace (
|
|
LPTSTR FileName,
|
|
LPTSTR TraceId
|
|
);
|
|
|
|
PSTRINGTOINTASSOCIATION
|
|
MAPSTRINGTOINT::GetStartPosition(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the first association in the list for iteration with the
|
|
MAPSTRINGTOINT::GetNextAssociation function.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
The first association in the list, or NULL if the map is empty.
|
|
|
|
--*/
|
|
|
|
{
|
|
return Associations;
|
|
}
|
|
|
|
|
|
VOID
|
|
MAPSTRINGTOINT::GetNextAssociation(
|
|
IN OUT PSTRINGTOINTASSOCIATION & Position,
|
|
OUT LPTSTR & Key,
|
|
OUT LONG & Value)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the data for the current association and sets Position to
|
|
point to the next association (or NULL if this is the last association.)
|
|
|
|
Arguments:
|
|
|
|
Position - Supplies the current association and returns the next association.
|
|
|
|
Key - Returns the key for the current association.
|
|
|
|
Value - Returns the value for the current association.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
Key = Position->Key;
|
|
Value = Position->Value;
|
|
Position = Position->Next;
|
|
}
|
|
|
|
|
|
MAPSTRINGTOINT::MAPSTRINGTOINT(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes a MAPSTRINGTOINT to be empty.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
Associations = NULL;
|
|
}
|
|
|
|
|
|
MAPSTRINGTOINT::~MAPSTRINGTOINT(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cleans up memory used by a MAPSTRINGTOINT.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTRINGTOINTASSOCIATION Deleting;
|
|
|
|
// clean up associations
|
|
|
|
while (Associations != NULL) {
|
|
|
|
// save pointer to first association
|
|
|
|
Deleting = Associations;
|
|
|
|
// remove first association from list
|
|
|
|
Associations = Deleting->Next;
|
|
|
|
// free removed association
|
|
|
|
free (Deleting->Key);
|
|
delete Deleting;
|
|
}
|
|
}
|
|
|
|
|
|
LONG &
|
|
MAPSTRINGTOINT::operator [] (
|
|
IN LPTSTR Key
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves an l-value for the value associated with a given key.
|
|
|
|
Arguments:
|
|
|
|
Key - The key for which the value is to be retrieved.
|
|
|
|
Return value:
|
|
|
|
A reference to the value associated with the provided key.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTRINGTOINTASSOCIATION CurrentAssociation = Associations;
|
|
|
|
// search for key
|
|
while(CurrentAssociation != NULL) {
|
|
|
|
if(!_tcscmp(CurrentAssociation->Key, Key)) {
|
|
|
|
// found key, return value
|
|
|
|
return CurrentAssociation->Value;
|
|
|
|
}
|
|
|
|
CurrentAssociation = CurrentAssociation->Next;
|
|
|
|
}
|
|
|
|
// not found, create new association
|
|
|
|
CurrentAssociation = new STRINGTOINTASSOCIATION;
|
|
|
|
if (CurrentAssociation == NULL) {
|
|
|
|
_tprintf(_T("Memory allocation failure\n"));
|
|
exit (0);
|
|
}
|
|
|
|
if (Key == NULL) {
|
|
_tprintf(_T("Null object name\n"));
|
|
exit (0);
|
|
}
|
|
else if (_tcscmp (Key, "") == 0) {
|
|
_tprintf(_T("Invalid object name `%s'\n"), Key);
|
|
exit (0);
|
|
}
|
|
|
|
CurrentAssociation->Key = _tcsdup(Key);
|
|
|
|
if (CurrentAssociation->Key == NULL) {
|
|
|
|
_tprintf(_T("Memory string allocation failure\n"));
|
|
exit (0);
|
|
}
|
|
|
|
// add association to front of list
|
|
|
|
CurrentAssociation->Next = Associations;
|
|
Associations = CurrentAssociation;
|
|
|
|
// return value for new association
|
|
|
|
return CurrentAssociation->Value;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
MAPSTRINGTOINT::Lookup(
|
|
IN LPTSTR Key,
|
|
OUT LONG & Value
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves an r-value for the value association with a given key.
|
|
|
|
Arguments:
|
|
|
|
Key - The key for which the associated value is to be retrieved.
|
|
|
|
Value - Returns the value associated with Key if Key is present in the map.
|
|
|
|
Return value:
|
|
|
|
TRUE if the key is present in the map, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSTRINGTOINTASSOCIATION CurrentAssociation = Associations;
|
|
|
|
// search for key
|
|
|
|
while (CurrentAssociation != NULL) {
|
|
|
|
if(!_tcscmp(CurrentAssociation->Key , Key)) {
|
|
|
|
// found key, return it
|
|
|
|
Value = CurrentAssociation->Value;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
CurrentAssociation = CurrentAssociation->Next;
|
|
|
|
}
|
|
|
|
// didn't find it
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
PopulateMapsFromFile(
|
|
IN LPTSTR FileName,
|
|
OUT MAPSTRINGTOINT & TypeMap,
|
|
OUT MAPSTRINGTOINT & NameMap,
|
|
BOOLEAN FileWithTraces
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses an OH output file and fills two maps with the number of handles of
|
|
each type and the number of handles to each named object.
|
|
|
|
Arguments:
|
|
|
|
FileName - OH output file to parse.
|
|
|
|
TypeMap - Map to fill with handle type information.
|
|
|
|
NameMap - Map to fill with named object information.
|
|
|
|
Return value:
|
|
|
|
TRUE if the file was successfully parsed, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
LONG HowMany;
|
|
LPTSTR Name, Type, Process, Pid;
|
|
LPTSTR NewLine;
|
|
TCHAR LineBuffer[512];
|
|
TCHAR ObjectName[512];
|
|
TCHAR TypeName[512];
|
|
FILE *InputFile;
|
|
ULONG LineNumber;
|
|
|
|
BOOLEAN rc;
|
|
|
|
LineNumber = 0;
|
|
|
|
// open file
|
|
|
|
InputFile = _tfopen(FileName, _T("rt"));
|
|
|
|
if (InputFile == NULL) {
|
|
|
|
_ftprintf(stderr, _T("Error opening oh file %s.\n"), FileName);
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
// loop through lines in oh output
|
|
|
|
while (_fgetts(LineBuffer, sizeof(LineBuffer), InputFile)
|
|
&& !( feof(InputFile) || ferror(InputFile) ) ) {
|
|
|
|
LineNumber += 1;
|
|
|
|
// trim off newline
|
|
|
|
if((NewLine = _tcschr(LineBuffer, _T('\n'))) != NULL) {
|
|
*NewLine = _T('\0');
|
|
}
|
|
|
|
// ignore lines that start with white space or are empty.
|
|
if (LineBuffer[0] == _T('\0') ||
|
|
LineBuffer[0] == _T('\t') ||
|
|
LineBuffer[0] == _T(' ')) {
|
|
continue;
|
|
}
|
|
|
|
// ignore lines that start with a comment
|
|
if( LineBuffer[0] == _T('/') && LineBuffer[1] == _T('/') ) {
|
|
continue;
|
|
}
|
|
|
|
// skip pid
|
|
|
|
if((Pid = _tcstok(LineBuffer, _T(" \t"))) == NULL) {
|
|
rc = FALSE;
|
|
break;
|
|
}
|
|
|
|
// skip process name
|
|
|
|
if((Process = _tcstok(NULL, _T(" \t"))) == NULL) {
|
|
rc = FALSE;
|
|
break;
|
|
}
|
|
|
|
// Type points to the type of handle
|
|
|
|
if ((Type = _tcstok(NULL, _T(" \t"))) == NULL) {
|
|
rc = FALSE;
|
|
break;
|
|
}
|
|
|
|
// HowMany = number of previous handles with this type
|
|
|
|
_stprintf (TypeName,
|
|
TEXT("<%s/%s/%s>"),
|
|
Process,
|
|
Pid,
|
|
Type);
|
|
|
|
if (TypeMap.Lookup(TypeName, HowMany) == FALSE) {
|
|
HowMany = 0;
|
|
}
|
|
|
|
// add another handle of this type
|
|
TypeMap[TypeName] = (HowMany + 1);
|
|
|
|
//
|
|
// Name points to the name. These are magic numbers based on the way
|
|
// OH formats output. The output is a little bit different if the
|
|
// `-h' option of OH was used (this dumps stack traces too).
|
|
//
|
|
|
|
Name = LineBuffer + 39 + 5;
|
|
|
|
if (FileWithTraces) {
|
|
Name += 7;
|
|
}
|
|
|
|
if (_tcscmp (Name, "") == 0) {
|
|
|
|
_stprintf (ObjectName,
|
|
TEXT("<%s/%s/%s>::<<noname>>"),
|
|
Process,
|
|
Pid,
|
|
Type);
|
|
}
|
|
else {
|
|
|
|
_stprintf (ObjectName,
|
|
TEXT("<%s/%s/%s>::%s"),
|
|
Process,
|
|
Pid,
|
|
Type,
|
|
Name);
|
|
}
|
|
|
|
// HowMany = number of previous handles with this name
|
|
|
|
// printf("name --> `%s' \n", ObjectName);
|
|
|
|
if (NameMap.Lookup(ObjectName, HowMany) == FALSE) {
|
|
HowMany = 0;
|
|
}
|
|
|
|
// add another handle with this name and read the next line
|
|
// note -- NameMap[] is a class operator, not an array.
|
|
|
|
NameMap[ObjectName] = (HowMany + 1);
|
|
}
|
|
|
|
// done, close file
|
|
|
|
fclose(InputFile);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
int
|
|
__cdecl
|
|
KeyCompareAssociation (
|
|
const void * Left,
|
|
const void * Right
|
|
)
|
|
{
|
|
PSTRINGTOINTASSOCIATION X;
|
|
PSTRINGTOINTASSOCIATION Y;
|
|
|
|
X = (PSTRINGTOINTASSOCIATION)Left;
|
|
Y = (PSTRINGTOINTASSOCIATION)Right;
|
|
|
|
return _tcscmp (X->Key, Y->Key);
|
|
}
|
|
|
|
|
|
int
|
|
__cdecl
|
|
ValueCompareAssociation (
|
|
const void * Left,
|
|
const void * Right
|
|
)
|
|
{
|
|
PSTRINGTOINTASSOCIATION X;
|
|
PSTRINGTOINTASSOCIATION Y;
|
|
|
|
X = (PSTRINGTOINTASSOCIATION)Left;
|
|
Y = (PSTRINGTOINTASSOCIATION)Right;
|
|
|
|
return Y->Value - X->Value;
|
|
}
|
|
|
|
|
|
VOID
|
|
PrintIncreases(
|
|
IN MAPSTRINGTOINT & BeforeMap,
|
|
IN MAPSTRINGTOINT & AfterMap,
|
|
IN BOOLEAN ReportIncreasesOnly,
|
|
IN BOOLEAN PrintHighlights,
|
|
IN LPTSTR AfterLogName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine compares two maps and prints out the differences between them.
|
|
|
|
Arguments:
|
|
|
|
BeforeMap - First map to compare.
|
|
|
|
AfterMap - Second map to compare.
|
|
|
|
ReportIncreasesOnly - TRUE for report only increases from BeforeMap to AfterMap,
|
|
FALSE for report all differences.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTRINGTOINTASSOCIATION Association = NULL;
|
|
LONG HowManyBefore = 0;
|
|
LONG HowManyAfter = 0;
|
|
LPTSTR Key = NULL;
|
|
PSTRINGTOINTASSOCIATION SortBuffer;
|
|
ULONG SortBufferSize;
|
|
ULONG SortBufferIndex;
|
|
|
|
//
|
|
// Loop through associations in map and figure out how many output lines
|
|
// we will have.
|
|
//
|
|
|
|
SortBufferSize = 0;
|
|
|
|
for (Association = AfterMap.GetStartPosition(),
|
|
AfterMap.GetNextAssociation(Association, Key, HowManyAfter);
|
|
Association != NULL;
|
|
AfterMap.GetNextAssociation(Association, Key, HowManyAfter)) {
|
|
|
|
// look up value for this key in BeforeMap
|
|
if(BeforeMap.Lookup(Key, HowManyBefore) == FALSE) {
|
|
|
|
HowManyBefore = 0;
|
|
|
|
}
|
|
|
|
// should we report this?
|
|
if((HowManyAfter > HowManyBefore) ||
|
|
((!ReportIncreasesOnly) && (HowManyAfter != HowManyBefore))) {
|
|
|
|
SortBufferSize += 1;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop through associations in map again this time filling the output buffer.
|
|
//
|
|
|
|
SortBufferIndex = 0;
|
|
|
|
SortBuffer = new STRINGTOINTASSOCIATION[SortBufferSize];
|
|
|
|
if (SortBuffer == NULL) {
|
|
_ftprintf(stderr, _T("Failed to allocate internal buffer of %u bytes.\n"),
|
|
SortBufferSize);
|
|
return;
|
|
}
|
|
|
|
for (Association = AfterMap.GetStartPosition(),
|
|
AfterMap.GetNextAssociation(Association, Key, HowManyAfter);
|
|
Association != NULL;
|
|
AfterMap.GetNextAssociation(Association, Key, HowManyAfter)) {
|
|
|
|
// look up value for this key in BeforeMap
|
|
if(BeforeMap.Lookup(Key, HowManyBefore) == FALSE) {
|
|
|
|
HowManyBefore = 0;
|
|
}
|
|
|
|
// should we report this?
|
|
if((HowManyAfter > HowManyBefore) ||
|
|
((!ReportIncreasesOnly) && (HowManyAfter != HowManyBefore))) {
|
|
|
|
ZeroMemory (&(SortBuffer[SortBufferIndex]),
|
|
sizeof (STRINGTOINTASSOCIATION));
|
|
|
|
SortBuffer[SortBufferIndex].Key = Key;
|
|
SortBuffer[SortBufferIndex].Value = HowManyAfter - HowManyBefore;
|
|
SortBufferIndex += 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sort the output buffer using the Key.
|
|
//
|
|
|
|
if (PrintHighlights) {
|
|
|
|
qsort (SortBuffer,
|
|
SortBufferSize,
|
|
sizeof (STRINGTOINTASSOCIATION),
|
|
ValueCompareAssociation);
|
|
}
|
|
else {
|
|
|
|
qsort (SortBuffer,
|
|
SortBufferSize,
|
|
sizeof (STRINGTOINTASSOCIATION),
|
|
KeyCompareAssociation);
|
|
}
|
|
|
|
//
|
|
// Dump the buffer.
|
|
//
|
|
|
|
for (SortBufferIndex = 0; SortBufferIndex < SortBufferSize; SortBufferIndex += 1) {
|
|
|
|
if (PrintHighlights) {
|
|
|
|
if (SortBuffer[SortBufferIndex].Value >= 1) {
|
|
|
|
TCHAR TraceId[7];
|
|
LPTSTR Start;
|
|
|
|
_tprintf(_T("%d\t%s\n"),
|
|
SortBuffer[SortBufferIndex].Value,
|
|
SortBuffer[SortBufferIndex].Key);
|
|
|
|
Start = _tcsstr (SortBuffer[SortBufferIndex].Key, "(");
|
|
|
|
if (Start == NULL) {
|
|
|
|
TraceId[0] = 0;
|
|
}
|
|
else {
|
|
|
|
_tcsncpy (TraceId,
|
|
Start,
|
|
6);
|
|
|
|
TraceId[6] = 0;
|
|
}
|
|
|
|
_tprintf (_T("%s"), SearchStackTrace (AfterLogName, TraceId));
|
|
}
|
|
}
|
|
else {
|
|
|
|
_tprintf(_T("%d\t%s\n"),
|
|
SortBuffer[SortBufferIndex].Value,
|
|
SortBuffer[SortBufferIndex].Key);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean up memory.
|
|
//
|
|
|
|
if (SortBuffer) {
|
|
delete[] SortBuffer;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
PrintUsage(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prints out a message describing the proper usage of OHCMP.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
_ftprintf (stderr, HelpText);
|
|
}
|
|
|
|
|
|
LONG _cdecl
|
|
_tmain(
|
|
IN LONG argc,
|
|
IN LPTSTR argv[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses program arguments, reads the two input files, and prints out the
|
|
differences.
|
|
|
|
Arguments:
|
|
|
|
argc - Number of command-line arguments.
|
|
|
|
argv - Command-line arguments.
|
|
|
|
Return value:
|
|
|
|
0 if comparison is successful, 1 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
MAPSTRINGTOINT TypeMapBefore, TypeMapAfter;
|
|
MAPSTRINGTOINT NameMapBefore, NameMapAfter;
|
|
LPTSTR BeforeFileName=NULL;
|
|
LPTSTR AfterFileName=NULL;
|
|
BOOLEAN ReportIncreasesOnly = TRUE;
|
|
BOOLEAN Interpreted = FALSE;
|
|
BOOLEAN Result;
|
|
BOOLEAN FileWithTraces;
|
|
BOOLEAN PrintHighlights;
|
|
|
|
// parse arguments
|
|
|
|
FileWithTraces = FALSE;
|
|
PrintHighlights = FALSE;
|
|
|
|
for (LONG n = 1; n < argc; n++) {
|
|
|
|
Interpreted = FALSE;
|
|
|
|
switch(argv[n][0]) {
|
|
|
|
case _T('-'):
|
|
case _T('/'):
|
|
|
|
// the argument is a switch
|
|
|
|
if(_tcsicmp(argv[n]+1, _T("all")) == 0) {
|
|
|
|
ReportIncreasesOnly = FALSE;
|
|
Interpreted = TRUE;
|
|
|
|
}
|
|
else if (_tcsicmp(argv[n]+1, _T("t")) == 0) {
|
|
|
|
FileWithTraces = TRUE;
|
|
Interpreted = TRUE;
|
|
}
|
|
else if (_tcsicmp(argv[n]+1, _T("h")) == 0) {
|
|
|
|
PrintHighlights = TRUE;
|
|
Interpreted = TRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// the argument is a file name
|
|
|
|
if(BeforeFileName == NULL) {
|
|
|
|
BeforeFileName = argv[n];
|
|
Interpreted = TRUE;
|
|
|
|
} else {
|
|
|
|
if(AfterFileName == NULL) {
|
|
|
|
AfterFileName = argv[n];
|
|
Interpreted = TRUE;
|
|
|
|
} else {
|
|
|
|
// too many file arguments
|
|
PrintUsage();
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if(!Interpreted) {
|
|
|
|
// user specified a bad argument
|
|
PrintUsage();
|
|
return 1;
|
|
|
|
}
|
|
}
|
|
|
|
// did user specify required arguments?
|
|
|
|
if((BeforeFileName == NULL) || (AfterFileName == NULL))
|
|
{
|
|
|
|
PrintUsage();
|
|
return 1;
|
|
|
|
}
|
|
|
|
// read oh1 file
|
|
|
|
Result = PopulateMapsFromFile (BeforeFileName,
|
|
TypeMapBefore,
|
|
NameMapBefore,
|
|
FileWithTraces);
|
|
|
|
if(Result == FALSE) {
|
|
|
|
_ftprintf(stderr, _T("Failed to read first OH output file.\n"));
|
|
return 1;
|
|
}
|
|
|
|
// read oh2 file
|
|
|
|
Result = PopulateMapsFromFile (AfterFileName,
|
|
TypeMapAfter,
|
|
NameMapAfter,
|
|
FileWithTraces);
|
|
|
|
if(Result == FALSE) {
|
|
|
|
_ftprintf(stderr, _T("Failed to read second OH output file.\n"));
|
|
return 1;
|
|
|
|
}
|
|
|
|
// print out increases by handle name
|
|
|
|
if (PrintHighlights) {
|
|
|
|
_putts (TEXT ("\n")
|
|
TEXT("// \n")
|
|
TEXT("// Possible leaks (DELTA <PROCESS/PID/TYPE>::NAME): \n")
|
|
TEXT("// \n")
|
|
TEXT("// Note that the NAME can appear as `(TRACEID) NAME' if output \n")
|
|
TEXT("// is generated by comparing OH files containing traces. In this case \n")
|
|
TEXT("// just search in the `AFTER' OH log file for the trace id to \n")
|
|
TEXT("// find the stack trace creating the handle possibly leaked. \n")
|
|
TEXT("// \n\n"));
|
|
|
|
PrintIncreases (NameMapBefore,
|
|
NameMapAfter,
|
|
ReportIncreasesOnly,
|
|
TRUE,
|
|
AfterFileName);
|
|
}
|
|
|
|
// print out increases by handle type
|
|
|
|
_putts (TEXT ("\n")
|
|
TEXT("// \n")
|
|
TEXT("// Handle types (DELTA <PROCESS/PID/TYPE>): \n")
|
|
TEXT("// \n")
|
|
TEXT("// DELTA is the additional number of handles found in the `AFTER' log. \n")
|
|
TEXT("// PROCESS is the process name having a handle increase. \n")
|
|
TEXT("// PID is the process PID having a handle increase. \n")
|
|
TEXT("// TYPE is the type of the handle \n")
|
|
TEXT("// \n\n"));
|
|
|
|
PrintIncreases (TypeMapBefore,
|
|
TypeMapAfter,
|
|
ReportIncreasesOnly,
|
|
FALSE,
|
|
NULL);
|
|
|
|
// print out increases by handle name
|
|
|
|
_putts (TEXT ("\n")
|
|
TEXT("// \n")
|
|
TEXT("// Objects (named and anonymous) (DELTA <PROCESS/PID/TYPE>::NAME): \n")
|
|
TEXT("// \n")
|
|
TEXT("// DELTA is the additional number of handles found in the `AFTER' log. \n")
|
|
TEXT("// PROCESS is the process name having a handle increase. \n")
|
|
TEXT("// PID is the process PID having a handle increase. \n")
|
|
TEXT("// TYPE is the type of the handle \n")
|
|
TEXT("// NAME is the name of the handle. Anonymous handles appear with name <<noname>>.\n")
|
|
TEXT("// \n")
|
|
TEXT("// Note that the NAME can appear as `(TRACEID) NAME' if output \n")
|
|
TEXT("// is generated by comparing OH files containing traces. In this case \n")
|
|
TEXT("// just search in the `AFTER' OH log file for the trace id to \n")
|
|
TEXT("// find the stack trace creating the handle possibly leaked. \n")
|
|
TEXT("// \n\n"));
|
|
|
|
PrintIncreases (NameMapBefore,
|
|
NameMapAfter,
|
|
ReportIncreasesOnly,
|
|
FALSE,
|
|
NULL);
|
|
|
|
return 0;
|
|
|
|
} catch (...) {
|
|
|
|
// this is mostly intended to catch out of memory conditions
|
|
|
|
_tprintf(_T("\nAn exception has been detected. OHCMP aborted.\n"));
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
TCHAR StackTraceBuffer [0x10000];
|
|
|
|
LPTSTR
|
|
SearchStackTrace (
|
|
LPTSTR FileName,
|
|
LPTSTR TraceId
|
|
)
|
|
{
|
|
TCHAR LineBuffer[512];
|
|
FILE *InputFile;
|
|
|
|
StackTraceBuffer[0] = 0;
|
|
|
|
//
|
|
// Open file.
|
|
//
|
|
|
|
InputFile = _tfopen(FileName, _T("rt"));
|
|
|
|
if (InputFile == NULL) {
|
|
|
|
_ftprintf(stderr, _T("Error opening oh file %s.\n"), FileName);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Loop through lines in oh output.
|
|
//
|
|
|
|
while (_fgetts(LineBuffer, sizeof(LineBuffer), InputFile)
|
|
&& !( feof(InputFile) || ferror(InputFile) ) ) {
|
|
|
|
//
|
|
// Skip line if it does not contain trace ID.
|
|
//
|
|
|
|
if (_tcsstr (LineBuffer, TraceId) == NULL) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We have got a trace ID. We need now to copy everything
|
|
// to a trace buffer until we get a line containing a character
|
|
// in column zero.
|
|
//
|
|
|
|
while (_fgetts(LineBuffer, sizeof(LineBuffer), InputFile)
|
|
&& !( feof(InputFile) || ferror(InputFile) ) ) {
|
|
|
|
if (LineBuffer[0] == _T(' ') ||
|
|
LineBuffer[0] == _T('\0') ||
|
|
LineBuffer[0] == _T('\n') ||
|
|
LineBuffer[0] == _T('\t')) {
|
|
|
|
_tcscat (StackTraceBuffer, LineBuffer);
|
|
}
|
|
else {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Close file.
|
|
|
|
fclose(InputFile);
|
|
|
|
return StackTraceBuffer;
|
|
}
|