📄 stackwalker.cpp
字号:
// if open failed, redirect output to stdout
static void AllocCheckFileOpen(BOOL bAppend = true) {
// is the File already open? If not open it...
if (g_fFile == NULL)
if (g_pszAllocLogName != NULL)
{
if (bAppend == false)
g_fFile = _tfopen(g_pszAllocLogName, _T("w"));
else
g_fFile = _tfopen(g_pszAllocLogName, _T("a"));
}
if (g_fFile == NULL)
g_fFile = stdout;
}
// Write Date/Time to specified file (will also work after 2038)
static void WriteDateTime(FILE *fFile, BOOL asXMLAttrs = FALSE) {
TCHAR pszTemp[11], pszTemp2[11];
if (fFile != NULL) {
_tstrdate( pszTemp );
_tstrtime( pszTemp2 );
if (asXMLAttrs == FALSE)
_ftprintf(fFile, _T("%s %s"), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)
else
_ftprintf(fFile, _T("date=\"%s\" time=\"%s\" "), pszTemp, pszTemp2 ); // also ok after year 2038 (asctime is NOT ok)
}
} // WriteDateTime
/*******************************************************************************
* Hash-Tabelle
*******************************************************************************/
// Memory for the EIP-Address (is used by the ShowStack-method)
#define MAX_EIP_LEN_BUF 4
#define ALLOC_ENTRY_NOT_FOUND 0xFFFFFFFF
typedef struct AllocHashEntryType {
long lRequestID; // RequestID from CRT (if 0, then this entry is empty)
size_t nDataSize; // Size of the allocated memory
char cRemovedFlag; // 0 => memory was not yet released
struct AllocHashEntryType *Next;
// Callstack for EIP
DWORD dwEIPOffset;
DWORD dwEIPLen;
char pcEIPAddr[MAX_EIP_LEN_BUF];
// Callstack for ESP
DWORD dwESPOffset;
DWORD dwESPLen;
char pcESPAddr[MAX_ESP_LEN_BUF];
} AllocHashEntryType;
static AllocHashEntryType AllocHashTable[ALLOC_HASH_ENTRIES];
static ULONG AllocHashEntries = 0;
static ULONG AllocHashCollisions = 0;
static ULONG AllocHashFreed = 0;
static ULONG AllocHashMaxUsed = 0; // maximal number of concurrent entries
static ULONG AllocHashCurrentCount = 0;
static ULONG AllocHashMaxCollisions = 0;
static ULONG AllocHashCurrentCollisions = 0;
// ##########################################################################################
#ifdef WITH_IMALLOC_SPY
// eigene Tabelle f? die IMallocs:
typedef struct IMallocHashEntryType {
void *pData; // Key-Word
size_t nDataSize; // gr鲞e des Datenblocks (optional)
char cRemovedFlag; // 0 => nicht wurde noch nicht freigegeben
struct IMallocHashEntryType *Next;
// Callstack f? EIP
DWORD dwEIPOffset;
DWORD dwEIPLen;
char pcEIPAddr[MAX_EIP_LEN_BUF];
// Callstack f? ESP
DWORD dwESPOffset;
DWORD dwESPLen;
char pcESPAddr[MAX_ESP_LEN_BUF];
} IMallocHashEntryType;
static IMallocHashEntryType IMallocHashTable[ALLOC_HASH_ENTRIES];
static ULONG IMallocHashEntries = 0;
static ULONG IMallocHashCollisions = 0;
static ULONG IMallocHashFreed = 0;
static ULONG IMallocHashMaxUsed = 0; // maximal number of concurrent entries
static ULONG IMallocHashCurrentCount = 0;
static ULONG IMallocHashMaxCollisions = 0;
static ULONG IMallocHashCurrentCollisions = 0;
//static void AllocHashOut(FILE*);
static ULONG IMallocHashOutLeaks(FILE*);
// AllocHashFunction
// Die eigentliche Hash-Funktion (hier ganz simpel)
static ULONG IMallocHashFunction(void *pData) {
ULONG ulTemp;
DWORD dwPointer = (DWORD) pData;
// relativ simpler Mechanismus f? die Hash-Funktion,
// mir ist nur nix besseres eingefallen...
ulTemp = dwPointer % ALLOC_HASH_ENTRIES;
_ASSERTE( (ulTemp >= 0) && (ulTemp < ALLOC_HASH_ENTRIES) );
return ulTemp;
} // AllocHashFunction
// IMallocHashInsert
// pData: Key-Word (Pointer to address)
// pContext: Context-Record, for retrieving Callstack (EIP and EBP is only needed)
// nDataSize: How many bytes
void IMallocHashInsert(void *pData, CONTEXT &Context, size_t nDataSize) {
ULONG HashIdx;
IMallocHashEntryType *pHashEntry;
// ermittle Statistische Werte
IMallocHashEntries++;
IMallocHashCurrentCount++;
if (IMallocHashCurrentCount > IMallocHashMaxUsed)
IMallocHashMaxUsed = IMallocHashCurrentCount;
// ermittle den Hash-Wert
HashIdx = IMallocHashFunction(pData);
// Eintrag darf nicht gr鲞er als die Hash-Tabelle sein
_ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
pHashEntry = &IMallocHashTable[HashIdx];
if (pHashEntry->pData == 0) {
// es ist noch kein Eintrag da
}
else {
//Statistische Daten:
IMallocHashCollisions++;
IMallocHashCurrentCollisions++;
if (IMallocHashCurrentCollisions > IMallocHashMaxCollisions)
IMallocHashMaxCollisions = IMallocHashCurrentCollisions;
// Eintrag ist schon belegt, verkette die Eintr?e
// wenn dies oft vorkommt, sollte man entweder die Tabelle vergr鲞ern oder eine
// andere Hash-Funktion w?len
while(pHashEntry->Next != NULL) {
pHashEntry = pHashEntry->Next;
}
pHashEntry->Next = (IMallocHashEntryType*) _calloc_dbg(sizeof(IMallocHashEntryType), 1, _CRT_BLOCK, __FILE__, __LINE__);
pHashEntry = pHashEntry->Next;
}
pHashEntry->pData = pData; // Key-Word
pHashEntry->nDataSize = nDataSize;
pHashEntry->Next = NULL;
// Get EIP and save it in the record
pHashEntry->dwEIPOffset = Context.Eip;
if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Eip, &(pHashEntry->pcEIPAddr), MAX_EIP_LEN_BUF, &(pHashEntry->dwEIPLen)) == 0) {
// Could not read memory... remove everything...
memset(pHashEntry->pcEIPAddr, 0, MAX_EIP_LEN_BUF);
pHashEntry->dwEIPLen = 0;
pHashEntry->dwEIPOffset = 0;
}
// Get ESP and save it in the record
pHashEntry->dwESPOffset = Context.Ebp;
if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), MAX_ESP_LEN_BUF, &(pHashEntry->dwESPLen)) == 0) {
// Could not read memory... remove everything...
memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
pHashEntry->dwESPLen = 0;
pHashEntry->dwESPOffset = 0;
// Check if I tried to read too much...
if (GetLastError() == ERROR_PARTIAL_COPY)
{
// ask how many I can read:
MEMORY_BASIC_INFORMATION MemBuffer;
DWORD dwRet = VirtualQuery((LPCVOID) Context.Ebp, &MemBuffer, sizeof(MemBuffer));
if (dwRet > 0)
{
// calculate the length
DWORD len = ((DWORD) MemBuffer.BaseAddress + MemBuffer.RegionSize) - Context.Ebp;
if ( (len > 0) && (len < MAX_ESP_LEN_BUF) )
{
// try to read it again (with the shorter length)
if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) Context.Ebp, &(pHashEntry->pcESPAddr), len, &(pHashEntry->dwESPLen)) == 0)
{
// ok, now everything goes wrong... remove it...
memset(pHashEntry->pcESPAddr, 0, MAX_ESP_LEN_BUF);
pHashEntry->dwESPLen = 0;
pHashEntry->dwESPOffset = 0;
}
else
{
pHashEntry->dwESPOffset = Context.Ebp;
}
}
} // VirtualQuery was successfully
} // ERROR_PARTIAL_COPY
}
}
// IMallocHashFind
// Wird ALLOC_ENTRY_NOT_FOUND zur?kgegeben, so wurde der Key nicht
// gefunden, ansonsten wird ein Zeiger auf den Hash-Eintrag zur?kgegeben
// ACHTUNG: In einem preemptiven Tasking-System kann hier nicht
// garantiert werden, ob der Zeiger noch g?tig ist, wenn er
// zur?kgegeben wird, da er von einem anderen Thread schon
// freigegeben sein k?nte.
// Die synchronisation mu?eine Ebene h?er erfolgen
static IMallocHashEntryType *IMallocHashFind(void *pData) {
ULONG HashIdx;
IMallocHashEntryType *pHashEntry;
// ermittle den Hash-Wert
HashIdx = IMallocHashFunction(pData);
// Eintrag darf nicht gr鲞er als die Hash-Tabelle sein
_ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
pHashEntry = &IMallocHashTable[HashIdx];
while(pHashEntry != NULL) {
if (pHashEntry->pData == pData) {
return pHashEntry;
}
pHashEntry = pHashEntry->Next;
}
// wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
return (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND;
} // AllocHashFind
// IMallocHashRemove
// Return: FALSE (0) : Key wurde gefunden und entfernt/markiert
// TRUE (!=0): Key wurde nicht gefunden!
BOOL IMallocHashRemove(void *pData) {
ULONG HashIdx;
IMallocHashEntryType *pHashEntry, *pHashEntryLast;
// ermittle den Hash-Wert
HashIdx = IMallocHashFunction(pData);
// Eintrag darf nicht gr鲞er als die Hash-Tabelle sein
_ASSERTE(HashIdx < ALLOC_HASH_ENTRIES);
pHashEntryLast = NULL;
pHashEntry = &IMallocHashTable[HashIdx];
while(pHashEntry != NULL) {
if (pHashEntry->pData == pData) {
#ifdef HASH_ENTRY_REMOVE_AT_FREE
IMallocHashFreed++;
IMallocHashCurrentCount--;
// gebe den Speicher frei
if (pHashEntryLast == NULL) {
// Es ist ein Eintrag direkt in der Tabelle
if (pHashEntry->Next == NULL) {
// Es ist der letze Eintrag l?che also die Tabelle
memset(&IMallocHashTable[HashIdx], 0, sizeof(IMallocHashTable[HashIdx]));
}
else {
// Es sind noch Eintr?e verkettet, ?erschreibe einfach den nicht mehr gebrauchten...
IMallocHashEntryType *pTmp = pHashEntry->Next;
*pHashEntry = *(pHashEntry->Next);
_free_dbg(pTmp, _CRT_BLOCK);
}
return TRUE;
}
else {
// ich bin in einem dynamischen Bereich
// dies war eine kollisions, z?le also wieder zur?k:
IMallocHashCurrentCollisions--;
pHashEntryLast->Next = pHashEntry->Next;
_free_dbg(pHashEntry, _CRT_BLOCK);
return TRUE;
}
#else
// erh?e nur den Removed counter und behalte das Object im Speicher
pHashEntry->cRemovedFlag++;
return TRUE; // erfolgreich
#endif
}
pHashEntryLast = pHashEntry;
pHashEntry = pHashEntry->Next;
}
// wenn hier angelangt, dann wurde der Eintrag nicht gefunden!
return FALSE;
}
// Callback-Funtion for StackWalk f? meine CallStack-Ausgabe aus der Hash-Tabelle
static BOOL __stdcall ReadProcMemoryFromIMallocHash(HANDLE pData, DWORD64 lpBaseAddress, PVOID lpBuffer, DWORD nSize, PDWORD lpNumberOfBytesRead) {
// Versuche die hRequestID zu finden
IMallocHashEntryType *pHashEntry;
*lpNumberOfBytesRead = 0;
pHashEntry = IMallocHashFind((PVOID) pData);
if (pHashEntry == (IMallocHashEntryType*) ALLOC_ENTRY_NOT_FOUND) {
// nicht gefunden, somit kann ich den Speicher nicht lesen
*lpNumberOfBytesRead = 0;
return FALSE;
}
if ( ((DWORD) lpBaseAddress >= pHashEntry->dwESPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwESPOffset+pHashEntry->dwESPLen)) ) {
// Speicher liegt im ESP:
// Errechne den Offset
DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwESPOffset;
DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
memcpy(lpBuffer, &(pHashEntry->pcESPAddr[dwOffset]), dwSize);
*lpNumberOfBytesRead = dwSize;
if (dwSize != nSize)
return FALSE;
}
if ( ((DWORD) lpBaseAddress >= pHashEntry->dwEIPOffset) && ((DWORD) lpBaseAddress <= (pHashEntry->dwEIPOffset+pHashEntry->dwEIPLen)) ) {
// Speicher liegt im EIP:
// Errechne den Offset
DWORD dwOffset = (DWORD) lpBaseAddress - pHashEntry->dwEIPOffset;
DWORD dwSize = __min(nSize, MAX_ESP_LEN_BUF-dwOffset);
memcpy(lpBuffer, &(pHashEntry->pcEIPAddr[dwOffset]), dwSize);
*lpNumberOfBytesRead = dwSize;
if (dwSize != nSize)
return FALSE;
}
if (*lpNumberOfBytesRead == 0) // Der Speicher konnte nicht gefunden werden
return FALSE;
return TRUE;
}
// AllocHashOutLeaks
// Gibt allen Speicher aus, der noch nicht wieder freigegeben wurde
// Returns the number of bytes, that are not freed (leaks)
ULONG IMallocHashOutLeaks(FILE *fFile) {
ULONG ulTemp;
IMallocHashEntryType *pHashEntry;
ULONG ulCount = 0;
ULONG ulLeaksByte = 0;
// Gehe jeden Eintrag durch und gebe ihn aus
for(ulTemp = 0; ulTemp < ALLOC_HASH_ENTRIES; ulTemp++) {
pHashEntry = &IMallocHashTable[ulTemp];
if (pHashEntry->pData != 0) {
while(pHashEntry != NULL) {
// gebe die Zeile aus
if ( (pHashEntry->cRemovedFlag <= 0) || (pHashEntry->cRemovedFlag > 1) ) {
ulCount++;
if (g_CallstackOutputType == ACOutput_XML)
_ftprintf(fFile, _T("<LEAK requestID=\"%u\" size=\"%u\">\n"), pHashEntry->pData, pHashEntry->nDataSize);
else
_ftprintf(fFile, _T("Pointer (RequestID): %12i, Removed: %i, Size: %12i\n"), pHashEntry->pData, pHashEntry->cRemovedFlag, pHashEntry->nDataSize);
CONTEXT c;
memset( &c, '\0', sizeof c );
c.Eip = pHashEntry->dwEIPOffset;
c.Ebp = pHashEntry->dwESPOffset;
ShowStackRM( NULL, c, fFile, &ReadProcMemoryFromIMallocHash, (HANDLE) pHashEntry->pData);
// Z?le zusammen wieviel Byte noch nicht freigegeben wurden
if (pHashEntry->nDataSize > 0)
ulLeaksByte += pHashEntry->nDataSize;
else
ulLeaksByte++; // Wenn zwar Speicher allokiert wurde, dieser aber 0 Bytes lang war, so reserviere f? diesen zumindest 1 Byte
if (g_CallstackOutputType == ACOutput_XML)
_ftprintf(fFile, _T("</LEAK>\n")); // terminate the xml-node
}
pHashEntry = pHashEntry->Next;
}
}
}
if (g_CallstackOutputType != ACOutput_XML)
_ftprintf(fFile, _T("\n**** Number of leaks: %i\n"), ulCount);
return ulLeaksByte;
} // AllocHashOutLeaks
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -