You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
530 lines
13 KiB
530 lines
13 KiB
/*++
|
|
|
|
Copyright (c) 1996-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
database.c
|
|
|
|
Abstract:
|
|
|
|
Quick and not-so-dirty user-mode dh for heap.
|
|
|
|
This module contains the functions and structures used to
|
|
read the whole stack trace database of a target process and
|
|
subsequently querying it.
|
|
|
|
Author(s):
|
|
|
|
Silviu Calinoiu (SilviuC) 07-Feb-00
|
|
|
|
Revision History:
|
|
|
|
SilviuC 06-Feb-00 Initial version
|
|
|
|
--*/
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <ntos.h>
|
|
|
|
#define NOWINBASEINTERLOCK
|
|
#include <windows.h>
|
|
|
|
#include <lmcons.h>
|
|
// #include <imagehlp.h>
|
|
#include <dbghelp.h>
|
|
|
|
#include <heap.h>
|
|
#include <heappagi.h>
|
|
#include <stktrace.h>
|
|
|
|
#include "types.h"
|
|
#include "symbols.h"
|
|
#include "miscellaneous.h"
|
|
#include "database.h"
|
|
|
|
// SilviuC: do we really need all these includes?
|
|
|
|
PVOID
|
|
GetTargetProcessDatabaseAddress (
|
|
HANDLE Process
|
|
)
|
|
{
|
|
PVOID Address;
|
|
BOOL Result;
|
|
PVOID DbAddress;
|
|
|
|
//
|
|
// SymbolAddress will return a NULL address on error.
|
|
//
|
|
|
|
Address = SymbolAddress (STACK_TRACE_DB_NAME);
|
|
|
|
if (Address == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Result = READVM (Address, &DbAddress, sizeof DbAddress);
|
|
|
|
if (Result == FALSE) {
|
|
|
|
Comment ( "ntdll.dll symbols are bad or we are not tracking "
|
|
"allocations in the target process.");
|
|
return NULL;
|
|
}
|
|
|
|
if (DbAddress == NULL) {
|
|
|
|
Comment ( "Stack trace collection is not enabled for this process. "
|
|
"Please use the gflags tool with the +ust option to enable it. \n");
|
|
|
|
Error (NULL, 0,
|
|
"Stack trace collection is not enabled for this process. "
|
|
"Please use the gflags tool with the +ust option to enable it. \n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
return DbAddress;
|
|
}
|
|
|
|
|
|
// returns TRUE if successful
|
|
|
|
BOOL
|
|
TraceDbInitialize (
|
|
HANDLE Process
|
|
)
|
|
{
|
|
SIZE_T Index;
|
|
BOOL Result;
|
|
SIZE_T BytesRead;
|
|
DWORD OldProtect;
|
|
PVOID TargetAddress;
|
|
PVOID SourceAddress;
|
|
SYSTEM_INFO SystemInfo;
|
|
SIZE_T PageSize;
|
|
ULONG PageCount = 0;
|
|
PVOID TargetDbAddress;
|
|
SIZE_T DatabaseSize;
|
|
SIZE_T TotalDbSize;
|
|
STACK_TRACE_DATABASE Db;
|
|
|
|
GetSystemInfo (&SystemInfo);
|
|
PageSize = (SIZE_T)(SystemInfo.dwPageSize);
|
|
|
|
TargetDbAddress = GetTargetProcessDatabaseAddress (Process);
|
|
|
|
if( TargetDbAddress == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Figure out the trace database size.
|
|
//
|
|
|
|
Result = ReadProcessMemory (Process,
|
|
TargetDbAddress,
|
|
&Db,
|
|
sizeof Db,
|
|
&BytesRead);
|
|
|
|
if (Result == FALSE) {
|
|
|
|
Error (NULL, 0,
|
|
"Failed to read trace database header (error %u)",
|
|
GetLastError());
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
TotalDbSize = (ULONG_PTR)(Db.EntryIndexArray) - (ULONG_PTR)(Db.CommitBase);
|
|
|
|
|
|
//
|
|
// Allocate memory for the database duplicate.
|
|
//
|
|
|
|
Globals.Database = VirtualAlloc (NULL,
|
|
TotalDbSize,
|
|
MEM_RESERVE | MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
|
|
if (Globals.Database == NULL) {
|
|
|
|
Error (NULL, 0,
|
|
"Failed to allocate memory for database (error %u)",
|
|
GetLastError());
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Read the whole thing
|
|
//
|
|
|
|
Comment ("Reading target process trace database ...");
|
|
|
|
DatabaseSize = PageSize;
|
|
|
|
for (Index = 0; Index < DatabaseSize; Index += PageSize) {
|
|
|
|
SourceAddress = (PVOID)((SIZE_T)(TargetDbAddress) + Index);
|
|
TargetAddress = (PVOID)((SIZE_T)(Globals.Database) + Index);
|
|
|
|
Result = ReadProcessMemory (Process,
|
|
SourceAddress,
|
|
TargetAddress,
|
|
PageSize,
|
|
&BytesRead);
|
|
|
|
if (Index == 0) {
|
|
|
|
//
|
|
// This is the first page of the database. We can now detect
|
|
// the real size of what we need to read.
|
|
//
|
|
|
|
if (Result == FALSE) {
|
|
|
|
Comment ("Failed to read trace database (error %u)", GetLastError());
|
|
return FALSE;
|
|
|
|
}
|
|
else {
|
|
|
|
PSTACK_TRACE_DATABASE pDb;
|
|
|
|
pDb= (PSTACK_TRACE_DATABASE)(Globals.Database);
|
|
|
|
DatabaseSize= (SIZE_T)(pDb->EntryIndexArray) - (SIZE_T)(pDb->CommitBase);
|
|
|
|
Comment ("Database size %p", DatabaseSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
Comment ("Trace database read.", PageCount);
|
|
|
|
if (Globals.DumpFileName) {
|
|
TraceDbBinaryDump ();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PVOID
|
|
RelocateDbAddress (
|
|
PVOID TargetAddress
|
|
)
|
|
{
|
|
ULONG_PTR TargetBase;
|
|
ULONG_PTR LocalBase;
|
|
PVOID LocalAddress;
|
|
|
|
LocalBase = (ULONG_PTR)(Globals.Database);
|
|
TargetBase = (ULONG_PTR)(((PSTACK_TRACE_DATABASE)LocalBase)->CommitBase);
|
|
LocalAddress = (PVOID)((ULONG_PTR)TargetAddress - TargetBase + LocalBase);
|
|
|
|
return LocalAddress;
|
|
}
|
|
|
|
|
|
VOID
|
|
TraceDbDump (
|
|
)
|
|
{
|
|
PSTACK_TRACE_DATABASE Db;
|
|
USHORT I;
|
|
PRTL_STACK_TRACE_ENTRY Entry;
|
|
PRTL_STACK_TRACE_ENTRY * IndexArray;
|
|
|
|
Comment ("Dumping raw data from the trace database ...");
|
|
Info ("");
|
|
|
|
Db = (PSTACK_TRACE_DATABASE)(Globals.Database);
|
|
|
|
Globals.ComplainAboutUnresolvedSymbols = TRUE;
|
|
|
|
for (I = 1; I <= Db->NumberOfEntriesAdded; I += 1) {
|
|
|
|
if (Globals.RawIndex > 0 && Globals.RawIndex != I) {
|
|
continue;
|
|
}
|
|
|
|
IndexArray = (PRTL_STACK_TRACE_ENTRY *) RelocateDbAddress (Db->EntryIndexArray);
|
|
|
|
if (IndexArray[-I] == NULL) {
|
|
|
|
Warning (NULL, 0, "Null/inaccessible trace pointer for trace index %u", I);
|
|
continue;
|
|
}
|
|
|
|
Entry = (PRTL_STACK_TRACE_ENTRY) RelocateDbAddress (IndexArray[-I]);
|
|
|
|
if (I != Entry->Index) {
|
|
|
|
Warning (NULL, 0, "Allocation trace index %u does not match trace entry index %u",
|
|
I, Entry->Index);
|
|
|
|
continue;
|
|
}
|
|
|
|
Info (" %u alloc(s) by: BackTrace%05u", Entry->TraceCount, I);
|
|
|
|
UmdhDumpStackByIndex (I);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
TraceDbBinaryDump (
|
|
)
|
|
{
|
|
PSTACK_TRACE_DATABASE Db;
|
|
SIZE_T DatabaseSize;
|
|
HANDLE DumpFile;
|
|
DWORD BytesWritten;
|
|
BOOL Result;
|
|
|
|
Db = (PSTACK_TRACE_DATABASE)(Globals.Database);
|
|
DatabaseSize = (SIZE_T)(Db->EntryIndexArray) - (SIZE_T)(Db->CommitBase);
|
|
|
|
Comment ("Creating the binary dump for the trace database in `%s'.",
|
|
Globals.DumpFileName);
|
|
|
|
DumpFile = CreateFile (Globals.DumpFileName,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
0,
|
|
NULL);
|
|
|
|
if (DumpFile == INVALID_HANDLE_VALUE) {
|
|
|
|
Comment ( "Failed to create the binary dump file (error %u)",
|
|
GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
Result = WriteFile (DumpFile,
|
|
Globals.Database,
|
|
(DWORD)DatabaseSize,
|
|
&BytesWritten,
|
|
NULL);
|
|
|
|
if (Result == FALSE || BytesWritten != DatabaseSize) {
|
|
|
|
Comment ("Failed to write the binary dump of trace database (error %u)",
|
|
GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
CloseHandle (DumpFile);
|
|
|
|
Comment ("Finished the binary dump.");
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
UmdhDumpStackByIndex(
|
|
IN USHORT TraceIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dumps a stack as it is stored in the stack trace database.
|
|
The trace index is used to find out the actual stack trace.
|
|
|
|
Arguments:
|
|
|
|
TraceIndex - index of the stack trace.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Side effects:
|
|
|
|
The trace is dumped to standard output.
|
|
|
|
--*/
|
|
{
|
|
PSTACK_TRACE_DATABASE StackTraceDb;
|
|
PRTL_STACK_TRACE_ENTRY Entry;
|
|
PRTL_STACK_TRACE_ENTRY * IndexArray;
|
|
PVOID Addr;
|
|
BOOL Result;
|
|
TRACE StackTrace;
|
|
|
|
if (TraceIndex == 0) {
|
|
|
|
//
|
|
// An index of 0 is returned by RtlLogStackBackTrace for an error
|
|
// condition, typically when the stack trace db has not been
|
|
// initialized.
|
|
//
|
|
|
|
Info ("No trace was saved for this allocation (Index == 0).");
|
|
|
|
return;
|
|
}
|
|
|
|
StackTraceDb = (PSTACK_TRACE_DATABASE)(Globals.Database);
|
|
|
|
//
|
|
// Read the pointer to the array of pointers to stack traces, then read
|
|
// the actual stack trace.
|
|
//
|
|
|
|
IndexArray = (PRTL_STACK_TRACE_ENTRY *) RelocateDbAddress (StackTraceDb->EntryIndexArray);
|
|
|
|
if (IndexArray[-TraceIndex] == NULL) {
|
|
|
|
Info ("Null/inaccessible trace pointer for trace index %u", TraceIndex);
|
|
return;
|
|
}
|
|
|
|
Entry = (PRTL_STACK_TRACE_ENTRY) RelocateDbAddress (IndexArray[-TraceIndex]);
|
|
|
|
if (TraceIndex != Entry->Index) {
|
|
|
|
Error (NULL, 0, "Allocation trace index %u does not match trace entry index %u",
|
|
TraceIndex, Entry->Index);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Read the stack trace pointers
|
|
//
|
|
|
|
ZeroMemory (&StackTrace, sizeof StackTrace);
|
|
|
|
StackTrace.te_EntryCount = min (Entry->Depth, MAX_STACK_DEPTH);
|
|
StackTrace.te_Address = (PULONG_PTR)(&(Entry->BackTrace));
|
|
|
|
UmdhDumpStack (&StackTrace);
|
|
|
|
//
|
|
// StackTrace is about to go out of scope, free any data we allocated
|
|
// for it. te_Address points to stack, but the te_Module, te_Name, and
|
|
// te_Offset fields were allocated by UmdhResolveName.
|
|
//
|
|
|
|
XFREE(StackTrace.te_Module);
|
|
XFREE(StackTrace.te_Name);
|
|
XFREE(StackTrace.te_Offset);
|
|
|
|
//
|
|
// SilviuC: We should probably read the whole trace database during
|
|
// process startup instead of poking the process space all the time.
|
|
//
|
|
}
|
|
|
|
|
|
/*
|
|
* UmdhDumpStack
|
|
*
|
|
* Send data in a LIST of TRACE_ENTRYs to the log function.
|
|
*
|
|
* t is the TRACE which we are to 'dump'.
|
|
*/
|
|
// silviuc: sanitize
|
|
VOID
|
|
UmdhDumpStack (
|
|
IN PTRACE Trace
|
|
)
|
|
{
|
|
ULONG i;
|
|
PCHAR FullName;
|
|
IMAGEHLP_LINE LineInfo;
|
|
DWORD Displacement;
|
|
BOOL LineInfoPresent;
|
|
|
|
if (Trace == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < Trace->te_EntryCount; i += 1) {
|
|
|
|
if (Trace->te_Address[i] != 0) {
|
|
|
|
FullName = GetSymbolicNameForAddress (Globals.Target,
|
|
Trace->te_Address[i]);
|
|
|
|
LineInfoPresent = FALSE;
|
|
|
|
if (Globals.LineInfo) {
|
|
|
|
ZeroMemory (&LineInfo, sizeof LineInfo);
|
|
LineInfo.SizeOfStruct = sizeof LineInfo;
|
|
|
|
LineInfoPresent = SymGetLineFromAddr (Globals.Target,
|
|
Trace->te_Address[i],
|
|
&Displacement,
|
|
&LineInfo);
|
|
|
|
}
|
|
|
|
if (FullName) {
|
|
|
|
if (Globals.Verbose) {
|
|
|
|
if (LineInfoPresent) {
|
|
|
|
Info (" %p : %s (%s, %u)",
|
|
Trace->te_Address[i],
|
|
FullName,
|
|
FullName,
|
|
LineInfo.FileName,
|
|
LineInfo.LineNumber);
|
|
}
|
|
else {
|
|
|
|
Info (" %p : %s",
|
|
Trace->te_Address[i],
|
|
FullName);
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (LineInfoPresent) {
|
|
|
|
Info (" %s (%s, %u)",
|
|
FullName,
|
|
LineInfo.FileName,
|
|
LineInfo.LineNumber);
|
|
}
|
|
else {
|
|
|
|
Info (" %s",
|
|
FullName);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
Info (" %p : <no module information>", Trace->te_Address[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
Info ("\n");
|
|
}
|
|
|
|
|
|
|
|
|