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.
447 lines
14 KiB
447 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1992-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
dpa.c
|
|
|
|
Abstract:
|
|
Module to dump pool allocations
|
|
|
|
Environment:
|
|
|
|
User Mode.
|
|
|
|
Revision History:
|
|
|
|
Code taken from userkdx.dll ( windows\core\ntuser\kdexts\userexts.c )
|
|
|
|
Kshitiz K. Sharma (kksharma) 1/11/2002
|
|
|
|
--*/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
#define RECORD_STACK_TRACE_SIZE 6
|
|
|
|
#define POOL_ALLOC_TRACE_SIZE 8
|
|
|
|
/*
|
|
* Pool allocation flags
|
|
*/
|
|
|
|
#define POOL_HEAVY_ALLOCS 0x00000001 // use HeavyAllocPool
|
|
#define POOL_CAPTURE_STACK 0x00000002 // stack traces are captured
|
|
#define POOL_FAIL_ALLOCS 0x00000004 // fail pool allocations
|
|
#define POOL_FAIL_BY_INDEX 0x00000008 // fail allocations by index
|
|
#define POOL_TAIL_CHECK 0x00000010 // append tail string
|
|
#define POOL_KEEP_FREE_RECORD 0x00000020 // keep a list with last x frees
|
|
#define POOL_KEEP_FAIL_RECORD 0x00000040 // keep a list with last x failed a
|
|
#define POOL_BREAK_FOR_LEAKS 0x00000080 // break on pool leaks (remote sess
|
|
|
|
enum DumpPolllAllocOptions {
|
|
DpaCurrentAllocStats = 1,
|
|
DpaAllCurrentAllocs = 2,
|
|
DpaIncludeStack = 4,
|
|
DpaFailedAllocs = 8,
|
|
DpaFreePool = 0x10,
|
|
DpaContainsPointer = 0x20,
|
|
};
|
|
|
|
|
|
VOID PrintStackTrace(
|
|
ULONG64 pStackTrace,
|
|
int tracesCount)
|
|
{
|
|
int traceInd;
|
|
ULONG64 dwOffset, pSymbol;
|
|
CHAR symbol[MAX_PATH];
|
|
DWORD dwPointerSize = DBG_PTR_SIZE;
|
|
|
|
for (traceInd = 0; traceInd < tracesCount; traceInd++) {
|
|
ReadPointer(pStackTrace, &pSymbol);
|
|
if (pSymbol == 0) {
|
|
break;
|
|
}
|
|
|
|
GetSymbol(pSymbol, symbol, &dwOffset);
|
|
if (*symbol) {
|
|
dprintf("\t%s", symbol);
|
|
if (dwOffset) {
|
|
dprintf("+%p\n", dwOffset);
|
|
} else {
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
|
|
pStackTrace += dwPointerSize;
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
|
|
void
|
|
DisplayDpaUsage(void)
|
|
{
|
|
dprintf("dpa -cvsfrp - Dump pool allocations\n"
|
|
" dpa -c - dump current pool allocations statistics\n"
|
|
" dpa -v - dump all the current pool allocations\n"
|
|
" dpa -vs - include stack traces\n"
|
|
" dpa -f - dump failed allocations\n"
|
|
" dpa -r - dump free pool\n"
|
|
" dpa -p <ptr> - dump the allocation containing pointer 'ptr'\n");
|
|
|
|
}
|
|
|
|
BOOL
|
|
GetDpaArgs(
|
|
PCHAR Args,
|
|
PULONG pOptions,
|
|
PULONG64 pAllocPtr
|
|
)
|
|
{
|
|
*pOptions = 0;
|
|
*pAllocPtr = 0;
|
|
if (!Args)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
while (*Args)
|
|
{
|
|
if (*Args == '-' || *Args == '/')
|
|
{
|
|
++Args;
|
|
switch (*Args)
|
|
{
|
|
case 'c':
|
|
*pOptions |= DpaCurrentAllocStats;
|
|
break;
|
|
case 'f':
|
|
*pOptions |= DpaFailedAllocs;
|
|
break;
|
|
case 'p':
|
|
*pOptions |= DpaContainsPointer;
|
|
++Args;
|
|
GetExpressionEx(Args, pAllocPtr, &Args);
|
|
break;
|
|
case 'r':
|
|
*pOptions |= DpaFreePool;
|
|
break;
|
|
case 'v':
|
|
if (*(Args+1) == 's')
|
|
{
|
|
*pOptions |= DpaIncludeStack;
|
|
} else
|
|
{
|
|
*pOptions |= DpaAllCurrentAllocs;
|
|
}
|
|
break;
|
|
default:
|
|
dprintf("Unknown Arg %c.\n", *Args ? *Args : ' ');
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (*Args)
|
|
{
|
|
++Args;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* dpa - dump pool allocations
|
|
*
|
|
* Dump pool allocations.
|
|
*
|
|
* 12-27-96 CLupu Created
|
|
\***************************************************************************/
|
|
BOOL
|
|
DumpPoolAllocs(
|
|
ULONG opts,
|
|
ULONG64 param1
|
|
)
|
|
{
|
|
HRESULT Hr = S_OK;
|
|
|
|
try {
|
|
ULONG64 pAllocList;
|
|
DWORD dwPoolFlags;
|
|
DWORD dwSize = GetTypeSize("win32k!tagWin32PoolHead");
|
|
BOOL bIncludeStackTrace = FALSE;
|
|
|
|
dwPoolFlags = GetUlongValue( "win32k!gdwPoolFlags" );
|
|
if (!(dwPoolFlags & POOL_HEAVY_ALLOCS)) {
|
|
dprintf("win32k.sys doesn't have pool instrumentation !\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (opts & DpaIncludeStack) {
|
|
if (dwPoolFlags & POOL_CAPTURE_STACK) {
|
|
bIncludeStackTrace = TRUE;
|
|
} else {
|
|
dprintf("win32k.sys doesn't have stack traces enabled for pool allocations\n");
|
|
}
|
|
}
|
|
|
|
pAllocList = GetExpression( "win32k!gAllocList" );
|
|
if (!pAllocList) {
|
|
dprintf("Could not get Win32AllocStats structure win32k!gAllocList\n");
|
|
return FALSE;
|
|
}
|
|
InitTypeRead(pAllocList, win32k!tagWin32AllocStats);
|
|
if (opts & DpaCurrentAllocStats) {
|
|
dprintf("- pool instrumentation enabled for win32k.sys\n");
|
|
if (dwPoolFlags & POOL_CAPTURE_STACK) {
|
|
dprintf("- stack traces enabled for pool allocations\n");
|
|
} else {
|
|
dprintf("- stack traces disabled for pool allocations\n");
|
|
}
|
|
|
|
|
|
if (dwPoolFlags & POOL_KEEP_FAIL_RECORD) {
|
|
dprintf("- records of failed allocations enabled\n");
|
|
} else {
|
|
dprintf("- records of failed allocations disabled\n");
|
|
}
|
|
|
|
if (dwPoolFlags & POOL_KEEP_FREE_RECORD) {
|
|
dprintf("- records of free pool enabled\n");
|
|
} else {
|
|
dprintf("- records of free pool disabled\n");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
dprintf(" CrtM CrtA MaxM MaxA Head\n");
|
|
dprintf("------------|------------|------------|------------|------------|\n");
|
|
InitTypeRead(pAllocList, win32k!tagWin32AllocStats);
|
|
dprintf(" 0x%08x 0x%08x 0x%08x 0x%08x 0x%I64x\n",
|
|
(ULONG)ReadField(dwCrtMem),
|
|
(ULONG)ReadField(dwCrtAlloc),
|
|
(ULONG)ReadField(dwMaxMem),
|
|
(ULONG)ReadField(dwMaxAlloc),
|
|
ReadField(pHead));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
InitTypeRead(pAllocList, win32k!tagWin32AllocStats);
|
|
if (opts & DpaFailedAllocs) {
|
|
|
|
DWORD dwFailRecordCrtIndex, dwFailRecordTotalFailures;
|
|
DWORD dwFailRecords, Ind, dwFailuresToDump;
|
|
ULONG64 pFailRecord, pFailRecordOrg;
|
|
|
|
if (!(dwPoolFlags & POOL_KEEP_FAIL_RECORD)) {
|
|
dprintf("win32k.sys doesn't have records of failed allocations!\n");
|
|
return TRUE;
|
|
}
|
|
|
|
dwFailRecordTotalFailures = GetUlongValue( "win32k!gdwFailRecordTotalFailures");
|
|
|
|
if (dwFailRecordTotalFailures == 0) {
|
|
dprintf("No allocation failure in win32k.sys!\n");
|
|
return TRUE;
|
|
}
|
|
|
|
dwFailRecordCrtIndex = GetUlongValue( "win32k!gdwFailRecordCrtIndex");
|
|
dwFailRecords = GetUlongValue( "win32k!gdwFailRecords");
|
|
if (dwFailRecordTotalFailures < dwFailRecords) {
|
|
dwFailuresToDump = dwFailRecordTotalFailures;
|
|
} else {
|
|
dwFailuresToDump = dwFailRecords;
|
|
}
|
|
|
|
pFailRecord = GetPointerValue( "win32k!gparrFailRecord");
|
|
if (!pFailRecord) {
|
|
dprintf("\nCouldn't get gparrFailRecord!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
pFailRecordOrg = pFailRecord;
|
|
|
|
dprintf("\nFailures to dump : %d\n\n", dwFailuresToDump);
|
|
|
|
for (Ind = 0; Ind < dwFailuresToDump; Ind++) {
|
|
DWORD tag[2] = {0, 0};
|
|
|
|
if (dwFailRecordCrtIndex == 0) {
|
|
dwFailRecordCrtIndex = dwFailRecords - 1;
|
|
} else {
|
|
dwFailRecordCrtIndex--;
|
|
}
|
|
|
|
pFailRecord = pFailRecordOrg + dwFailRecordCrtIndex;
|
|
|
|
InitTypeRead(pFailRecord, win32k!tagPOOLRECORD);
|
|
|
|
tag[0] = (DWORD)(DWORD_PTR)ReadField(ExtraData);
|
|
|
|
dprintf("Allocation for tag '%s' size 0x%x failed\n",
|
|
tag,
|
|
(ULONG)ReadField(size));
|
|
|
|
PrintStackTrace(ReadField(pTrace), RECORD_STACK_TRACE_SIZE);
|
|
if (CheckControlC())
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
InitTypeRead(pAllocList, win32k!tagWin32AllocStats);
|
|
if (opts & DpaFreePool) {
|
|
|
|
DWORD dwFreeRecordCrtIndex, dwFreeRecordTotalFrees;
|
|
DWORD dwFreeRecords, Ind, dwFreesToDump;
|
|
ULONG64 pFreeRecord, pFreeRecordOrg;
|
|
|
|
if (!(dwPoolFlags & POOL_KEEP_FREE_RECORD)) {
|
|
dprintf("win32k.sys doesn't have records of free pool !\n");
|
|
return FALSE;
|
|
}
|
|
|
|
dwFreeRecordTotalFrees = GetUlongValue( "win32k!gdwFreeRecordTotalFrees" );
|
|
if (dwFreeRecordTotalFrees == 0) {
|
|
dprintf("No free pool in win32k.sys !\n");
|
|
return FALSE;
|
|
}
|
|
|
|
dwFreeRecordCrtIndex = GetUlongValue( "win32k!gdwFreeRecordCrtIndex" );
|
|
dwFreeRecords = GetUlongValue( "win32k!gdwFreeRecords" );
|
|
if (dwFreeRecordTotalFrees < dwFreeRecords) {
|
|
dwFreesToDump = dwFreeRecordTotalFrees;
|
|
} else {
|
|
dwFreesToDump = dwFreeRecords;
|
|
}
|
|
|
|
pFreeRecord = GetPointerValue( "win32k!gparrFreeRecord" );
|
|
if (!pFreeRecord) {
|
|
dprintf("\nCouldn't get gparrFreeRecord!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
pFreeRecordOrg = pFreeRecord;
|
|
|
|
dprintf("\nFrees to dump : %d\n\n", dwFreesToDump);
|
|
|
|
for (Ind = 0; Ind < dwFreesToDump; Ind++) {
|
|
if (dwFreeRecordCrtIndex == 0) {
|
|
dwFreeRecordCrtIndex = dwFreeRecords - 1;
|
|
} else {
|
|
dwFreeRecordCrtIndex--;
|
|
}
|
|
|
|
pFreeRecord = pFreeRecordOrg + dwFreeRecordCrtIndex;
|
|
|
|
/*
|
|
* Dump
|
|
*/
|
|
InitTypeRead(pFreeRecord, win32k!tagPOOLRECORD);
|
|
dprintf("Free pool for p %#p size 0x%x\n",
|
|
ReadField(ExtraData),
|
|
(ULONG)ReadField(size));
|
|
|
|
PrintStackTrace(ReadField(pTrace), RECORD_STACK_TRACE_SIZE);
|
|
}
|
|
}
|
|
|
|
InitTypeRead(pAllocList, win32k!tagWin32AllocStats);
|
|
if (opts & DpaAllCurrentAllocs) {
|
|
ULONG64 ph = ReadField(pHead);
|
|
|
|
while (ph != 0) {
|
|
InitTypeRead(ph, win32k!tagWin32PoolHead);
|
|
dprintf("p %#p pHead %#p size %x\n",
|
|
ph + dwSize, ph, (ULONG)ReadField(size));
|
|
|
|
if (bIncludeStackTrace) {
|
|
ULONG64 dwOffset;
|
|
CHAR symbol[MAX_PATH];
|
|
int ind;
|
|
ULONG64 trace;
|
|
ULONG64 pTrace;
|
|
|
|
pTrace = ReadField(pTrace);
|
|
for (ind = 0; ind < POOL_ALLOC_TRACE_SIZE; ind++) {
|
|
ReadPointer(pTrace, &trace);
|
|
if (trace == 0) {
|
|
break;
|
|
}
|
|
|
|
GetSymbol(trace, symbol, &dwOffset);
|
|
if (*symbol) {
|
|
dprintf("\t%s", symbol);
|
|
if (dwOffset != 0) {
|
|
dprintf("+0x%I64x", dwOffset);
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
|
|
++pTrace;
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
|
|
ph = (ULONG_PTR)ReadField(pNext);
|
|
if (CheckControlC())
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
InitTypeRead(pAllocList, win32k!tagWin32AllocStats);
|
|
if (opts & DpaContainsPointer) {
|
|
ULONG64 ph;
|
|
dwSize = GetTypeSize("win32k!tagWin32PoolHead");
|
|
|
|
if (param1 == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
ph = ReadField(pHead);
|
|
while (ph != 0) {
|
|
if ((param1 - ph) >= ((ULONG)ReadField(size) + dwSize)) {
|
|
dprintf("p %#p pHead %#p size %x\n",
|
|
ph + 1, ph, (ULONG)ReadField(size));
|
|
|
|
PrintStackTrace(ReadField(pTrace), RECORD_STACK_TRACE_SIZE);
|
|
return TRUE;
|
|
}
|
|
|
|
ph = ReadField(pNext);
|
|
}
|
|
return TRUE;
|
|
}
|
|
} except (Hr) {
|
|
dprintf("Unhandled exception %lx in DumpPoolAllocs\n", Hr);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
DECLARE_API( dpa )
|
|
{
|
|
ULONG opts;
|
|
ULONG64 param1;
|
|
|
|
INIT_API();
|
|
|
|
if (!GetDpaArgs((PCHAR)args, &opts, ¶m1))
|
|
{
|
|
DisplayDpaUsage();
|
|
Status = E_FAIL;
|
|
} else
|
|
{
|
|
DumpPoolAllocs(opts, param1);
|
|
}
|
|
EXIT_API();
|
|
return Status;
|
|
}
|