Windows2000/private/windbg64/windbg/wcrash.c
2020-09-30 17:12:32 +02:00

708 lines
22 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
crash.c
Abstract:
This file contains the code necessary to generate a
a user mode dump of the current app. The code will essentially
take a non-fatal snapshot and allow the debugging session
to continue.
Author:
Carlos Klapp (Sept. 24, 98)
Environment:
Win32, User Mode
--*/
#include "precomp.h"
#pragma hdrstop
#include <crash.h>
#include "dbugexcp.h"
/************************** Data declaration *************************/
#define MEM_SIZE (64*1024)
/****** Publics ********/
extern LPPD LppdCommand;
extern LPTD LptdCommand;
extern LPSHF Lpshf; // vector table for symbol handler
/****** Externs from ??? *******/
// these are here only so that we can link
// with crashlib. they are only referenced
// when reading a kernel mode crash dump
extern "C" {
ULONG64 KiProcessors[1];
ULONG64 KiPcrBaseAddress[1];
};
/************************** Private Structures *************************/
typedef struct _tagMODULELIST {
LPMODULE_LIST pModList;
LPMODULE_ENTRY pModEntry;
DWORD dwModIdx;
BOOL bFinished;
} MODULELIST_WCRASH, * PMODULELIST_WCRASH;
// private data structure use for communcating
// crash dump data to the callback function
typedef struct _tagDEBUGPACKET {
//HWND hwnd;
//HANDLE hEvent;
//OPTIONS options;
//DWORD dwPidToDebug;
//HANDLE hEventToSignal;
//DWORD dwProcessId;
HPID hpid;
HTID htid;
TCHAR szProcessName[_MAX_PATH];
DWORD dwBaseOfImage;
DWORD dwImageSize;
LPTD lptdThreadListHead;
//LIST_ENTRY ThreadList;
//PTHREADCONTEXT tctx;
//DWORD stackBase;
//DWORD stackRA;
DEBUG_EVENT DebugEvent;
//DWORD ExitStatus;
} DEBUGPACKET, *PDEBUGPACKET;
typedef struct _CRASH_DUMP_INFO {
// TRUE - stop all processing
// FALSE - continue processing
BOOL bFatalError;
// Module specific data
MODULELIST_WCRASH modlst;
DEBUGPACKET dp;
EXCEPTION_DEBUG_INFO *pExceptionInfo;
DWORD MemoryCount;
DWORD_PTR Address;
PUCHAR pMemoryData;
MEMORY_BASIC_INFORMATION mbi;
SIZE_T MbiOffset;
SIZE_T MbiRemaining;
LPTD lptdCurThread; // LIST_ENTRY ThreadList;
PCRASH_MODULE CrashModule;
} CRASH_DUMP_INFO, *PCRASH_DUMP_INFO;
// Used by the sort algorithm to make sure that the
// main module is the first in the list
static TCHAR szMainModule[MAX_PATH];
/******************* Func Declarations *************************/
BOOL GetThreadContext_CrashDump(LPTD, LPCONTEXT, UINT);
void ConvertMemInfoFromOSDFmtToSysFmt(MEMORY_BASIC_INFORMATION *, MEMINFO *);
int __cdecl SkewedSortModEntryByAddress(const void *, const void *);
UINT WINAPI DHGetDebuggeeBytes(ADDR addr, UINT cb, void * lpb);
/************************** Code *************************/
BOOL
WindbgCrashDumpCallback(
DWORD dwDataType,
PVOID *ppvDumpData,
LPDWORD pdwDumpDataLength,
PVOID pvData
)
/*++
Routine Description:
This function is the callback used by crashlib.
Its purpose is to provide data to DmpCreateUserDump()
for writting to the crashdump file.
Arguments:
dwDataType - requested data type
ppvDumpData - pointer to a pointer to the data
pdwDumpDataLength - pointer to the data length
CrashdumpInfo - DrWatson private data
Return Value:
TRUE - continue calling back for the requested data type
FALSE - stop calling back and go on to the next data type
--*/
{
PCRASH_DUMP_INFO pCrashdumpInfo = (PCRASH_DUMP_INFO) pvData;
DWORD cb;
if (pCrashdumpInfo->bFatalError) {
*pdwDumpDataLength = 0;
return FALSE;
}
switch( dwDataType ) {
case DMP_DEBUG_EVENT:
Assert(sizeof(pCrashdumpInfo->dp.DebugEvent) == sizeof(DEBUG_EVENT));
*ppvDumpData = &pCrashdumpInfo->dp.DebugEvent;
*pdwDumpDataLength = sizeof(DEBUG_EVENT);
break;
case DMP_THREAD_STATE:
{
static CRASH_THREAD CrashThread;
TST tst = {0};
// BUGBUG no 64 bit support
*ppvDumpData = &CrashThread;
if (pCrashdumpInfo->lptdCurThread == NULL) {
pCrashdumpInfo->lptdCurThread = pCrashdumpInfo->dp.lptdThreadListHead;
} else {
pCrashdumpInfo->lptdCurThread = pCrashdumpInfo->lptdCurThread->lptdNext;
}
if (NULL == pCrashdumpInfo->lptdCurThread) {
pCrashdumpInfo->lptdCurThread = NULL;
return FALSE;
}
ZeroMemory(&CrashThread, sizeof(CrashThread));
CrashThread.ThreadId = HandleToUlong(pCrashdumpInfo->lptdCurThread->htid);
if (xosdNone != OSDGetThreadStatus(
pCrashdumpInfo->lptdCurThread->lppd->hpid,
pCrashdumpInfo->lptdCurThread->htid, &tst)) {
CmdLogFmt("Crash warning: No status for thread %d\r\n",
pCrashdumpInfo->lptdCurThread->itid);
CrashThread.SuspendCount = (DWORD) -1;
} else {
CrashThread.SuspendCount = tst.dwSuspendCount;
/*if (CrashThread.SuspendCount != (DWORD)-1) {
ResumeThread(ptctx->hThread);
}*/
// BUGBUG 64 bit
CrashThread.Teb = (DWORD)tst.dwTeb;
Assert(tst.dwPriorityMax <= 31);
if (tst.dwPriorityMax < 16) {
// NOT realtime
if (tst.dwPriority <= 6) {
CrashThread.PriorityClass = IDLE_PRIORITY_CLASS;
CrashThread.Priority = tst.dwPriority - 4;
} else if (tst.dwPriority <= 11) {
CrashThread.PriorityClass = NORMAL_PRIORITY_CLASS;
CrashThread.Priority = tst.dwPriority - 9;
} else if (tst.dwPriority <= 15) {
CrashThread.PriorityClass = HIGH_PRIORITY_CLASS;
if (15 == tst.dwPriority) {
CrashThread.Priority = THREAD_PRIORITY_TIME_CRITICAL;
} else {
CrashThread.Priority = tst.dwPriority - 13;
}
}
} else {
CrashThread.PriorityClass = REALTIME_PRIORITY_CLASS;
if (15 == tst.dwPriority) {
CrashThread.Priority = THREAD_PRIORITY_TIME_CRITICAL;
} else {
CrashThread.Priority = tst.dwPriority - 4;
}
}
}
*pdwDumpDataLength = sizeof(CRASH_THREAD);
}
break;
case DMP_MEMORY_BASIC_INFORMATION:
while( TRUE ) {
MEMINFO meminfo = {0};
pCrashdumpInfo->Address += pCrashdumpInfo->mbi.RegionSize;
meminfo.addr.addr.off = pCrashdumpInfo->Address;
if (xosdNone != OSDGetMemoryInformation(pCrashdumpInfo->dp.hpid,
NULL, &meminfo)) {
return FALSE;
}
ConvertMemInfoFromOSDFmtToSysFmt(&pCrashdumpInfo->mbi, &meminfo);
if ((pCrashdumpInfo->mbi.AllocationProtect & PAGE_GUARD) ||
(pCrashdumpInfo->mbi.AllocationProtect & PAGE_NOACCESS)) {
continue;
}
if ((pCrashdumpInfo->mbi.State & MEM_FREE) ||
(pCrashdumpInfo->mbi.State & MEM_RESERVE)) {
continue;
}
break;
}
*ppvDumpData = &pCrashdumpInfo->mbi;
*pdwDumpDataLength = sizeof(MEMORY_BASIC_INFORMATION);
break;
case DMP_THREAD_CONTEXT:
{
static CONTEXT context;
if (pCrashdumpInfo->lptdCurThread == NULL) {
pCrashdumpInfo->lptdCurThread = pCrashdumpInfo->dp.lptdThreadListHead;
} else {
pCrashdumpInfo->lptdCurThread = pCrashdumpInfo->lptdCurThread->lptdNext;
}
if (NULL == pCrashdumpInfo->lptdCurThread) {
pCrashdumpInfo->lptdCurThread = NULL;
return FALSE;
}
ZeroMemory(&context, sizeof(context));
if (!GetThreadContext_CrashDump(pCrashdumpInfo->lptdCurThread, &context,
sizeof(context))) {
CmdLogFmt("!!!Crash error: No context for thread %d\r\n",
pCrashdumpInfo->lptdCurThread->itid);
pCrashdumpInfo->bFatalError = TRUE;
}
*ppvDumpData = &context;
*pdwDumpDataLength = sizeof(CONTEXT);
}
break;
case DMP_MODULE:
// Initialize module list
if (!pCrashdumpInfo->modlst.pModList) {
if (xosdNone != OSDGetModuleList( LppdCur->hpid,
LptdCur->htid, NULL, &pCrashdumpInfo->modlst.pModList)) {
// Error obtaining mod list
CmdLogFmt("!!!Crash error: Unable to obtain module list\r\n");
pCrashdumpInfo->bFatalError = TRUE;
// Fall thru to cleanup
pCrashdumpInfo->modlst.bFinished = TRUE;
} else {
// Obtained mod list
if (ModuleListCount(pCrashdumpInfo->modlst.pModList) > 0) {
// Necessary for everything to function. This sort will
// always place the process before the modules. It then
// sorts the modules by mem address.
qsort(FirstModuleEntry(pCrashdumpInfo->modlst.pModList),
ModuleListCount(pCrashdumpInfo->modlst.pModList),
sizeof(MODULE_ENTRY), SkewedSortModEntryByAddress);
// Get the first module entry
pCrashdumpInfo->modlst.pModEntry =
FirstModuleEntry(pCrashdumpInfo->modlst.pModList);
pCrashdumpInfo->modlst.dwModIdx = 0;
} else {
CmdLogFmt("!!!Crash error: Module list is empty\r\n");
pCrashdumpInfo->bFatalError = TRUE;
// Fall thru to cleanup
pCrashdumpInfo->modlst.bFinished = TRUE;
}
}
}
// Cleanup ??
if (pCrashdumpInfo->modlst.bFinished) {
// Cleanup
if (pCrashdumpInfo->modlst.pModList) {
MHFree(pCrashdumpInfo->modlst.pModList);
}
ZeroMemory(&pCrashdumpInfo->modlst, sizeof(pCrashdumpInfo->modlst));
return FALSE;
}
// Process the current module
// BUGBUG 64 bit
pCrashdumpInfo->CrashModule->BaseOfImage = (DWORD)ModuleEntryBase(pCrashdumpInfo->modlst.pModEntry);
pCrashdumpInfo->CrashModule->SizeOfImage = (DWORD)ModuleEntryLimit(pCrashdumpInfo->modlst.pModEntry);
// Get module name
{
PSTR pszImgName = SHGetExeName( (HEXE)ModuleEntryEmi(pCrashdumpInfo->modlst.pModEntry) );
Assert(pszImgName);
pCrashdumpInfo->CrashModule->ImageNameLength = strlen(pszImgName) + 1;
strcpy(pCrashdumpInfo->CrashModule->ImageName, pszImgName);
}
*pdwDumpDataLength = sizeof(CRASH_MODULE) + pCrashdumpInfo->CrashModule->ImageNameLength;
*ppvDumpData = pCrashdumpInfo->CrashModule;
// Get the next module
pCrashdumpInfo->modlst.dwModIdx++;
if (pCrashdumpInfo->modlst.dwModIdx < ModuleListCount(pCrashdumpInfo->modlst.pModList)) {
pCrashdumpInfo->modlst.pModEntry = NextModuleEntry(pCrashdumpInfo->modlst.pModEntry);
} else {
pCrashdumpInfo->modlst.bFinished = TRUE;
}
break;
case DMP_MEMORY_DATA:
{
MEMINFO meminfo = {0};
if (!pCrashdumpInfo->MemoryCount) {
pCrashdumpInfo->Address = 0;
pCrashdumpInfo->MbiOffset = 0;
pCrashdumpInfo->MbiRemaining = 0;
ZeroMemory( &pCrashdumpInfo->mbi, sizeof(MEMORY_BASIC_INFORMATION) );
pCrashdumpInfo->pMemoryData = (PUCHAR) VirtualAlloc(
NULL,
MEM_SIZE,
MEM_COMMIT,
PAGE_READWRITE
);
}
if (!pCrashdumpInfo->MbiRemaining) {
while( TRUE ) {
pCrashdumpInfo->Address += pCrashdumpInfo->mbi.RegionSize;
meminfo.addr.addr.off = pCrashdumpInfo->Address;
if (xosdNone != OSDGetMemoryInformation(pCrashdumpInfo->dp.hpid,
NULL, &meminfo)) {
if (pCrashdumpInfo->pMemoryData) {
VirtualFree( pCrashdumpInfo->pMemoryData, MEM_SIZE, MEM_RELEASE );
}
return FALSE;
}
ConvertMemInfoFromOSDFmtToSysFmt(&pCrashdumpInfo->mbi, &meminfo);
if ((pCrashdumpInfo->mbi.Protect & PAGE_GUARD) ||
(pCrashdumpInfo->mbi.Protect & PAGE_NOACCESS)) {
continue;
}
if ((pCrashdumpInfo->mbi.State & MEM_FREE) ||
(pCrashdumpInfo->mbi.State & MEM_RESERVE)) {
continue;
}
pCrashdumpInfo->MbiOffset = 0;
pCrashdumpInfo->MbiRemaining = pCrashdumpInfo->mbi.RegionSize;
pCrashdumpInfo->MemoryCount += 1;
break;
}
}
*pdwDumpDataLength = (DWORD)__min( pCrashdumpInfo->MbiRemaining, MEM_SIZE );
pCrashdumpInfo->MbiRemaining -= *pdwDumpDataLength;
meminfo.addr.addr.off = ((UOFFSET)pCrashdumpInfo->mbi.BaseAddress + pCrashdumpInfo->MbiOffset);
if (xosdNone != OSDReadMemory(pCrashdumpInfo->dp.hpid,
pCrashdumpInfo->dp.htid, &meminfo.addr,
pCrashdumpInfo->pMemoryData, *pdwDumpDataLength, &cb)) {
CmdLogFmt( "Crash warning: could not read memory at 0x%I64X\n", meminfo.addr.addr.off);
}
*ppvDumpData = pCrashdumpInfo->pMemoryData;
pCrashdumpInfo->MbiOffset += *pdwDumpDataLength;
}
break;
}
return TRUE;
}
LOGERR
LogCrash(
LPSTR pszFileNameArg,
DWORD /*dwUnused*/
)
{
LOGERR logerr = LOGERROR_NOERROR;
PTSTR pszDumpName = NULL;
// This should have been handled by the "LogDotCommand" function
Assert(!g_contWorkspace_WkSp.m_bKernelDebugger);
// Error checking
CmdInsertInit();
// don't generate a dump of a dump
if (g_contWorkspace_WkSp.m_bUserCrashDump ||
(g_contWorkspace_WkSp.m_bKernelDebugger && g_contKernelDbgPreferences_WkSp.m_bUseCrashDump)) {
CmdLogFmt( "Crash: <.crash> is not allowed for crash dumps\n" );
return LOGERROR_QUIET;
}
// Process must be stopped
if (IsProcRunning( LppdCur )) {
CmdLogFmt( "Crash: process must first be stopped\n" );
return LOGERROR_QUIET;
}
PreRunInvalid();
PDWildInvalid();
TDWildInvalid();
// debuggee has to be active
if (!DebuggeeActive()) {
CmdLogFmt("Crash: There is not process to dump\r\n");
return LOGERROR_QUIET;
}
// Skip over any leading blanks
pszDumpName = CPSkipWhitespace(pszFileNameArg);
// Check for no argument
if (!*pszDumpName) {
CmdLogFmt("Crash: Must specify a file name\r\n");
return LOGERROR_QUIET;
}
// We finally get to do something
// Initialize structures
CRASH_DUMP_INFO CrashdumpInfo = {0};
// Init CrashdumpInfo
*szMainModule = NULL;
// Get the stopped thread
{
TCHAR szExt[_MAX_EXT] = {0};
LPTD lptd = LppdCur->lptdList;
for (; lptd; lptd = lptd->lptdNext) {
if (tsStopped == lptd->tstate || tsException2 == lptd->tstate) {
CrashdumpInfo.dp.hpid = lptd->lppd->hpid;;
CrashdumpInfo.dp.htid = lptd->htid;
CrashdumpInfo.dp.lptdThreadListHead = lptd->lppd->lptdList;
_tsplitpath(lptd->lppd->lpBaseExeName, NULL, NULL, szMainModule, szExt);
_tcscat(szMainModule, szExt);
break;
}
}
if (NULL == CrashdumpInfo.dp.hpid) {
CmdLogFmt("Crash error: There is no stopped thread on which to base the dump\r\n");
goto CLEANUP;
}
}
// Get the last debug event for the stopped thread
{
PIOCTLGENERIC pig;
DWORD dw;
pig = (PIOCTLGENERIC)calloc(sizeof(IOCTLGENERIC) + sizeof(DEBUG_EVENT), 1);
if (!pig) {
CmdLogFmt("!!!!!Crash error: Unable to allocate memory\r\n");
logerr = LOGERROR_QUIET;
goto CLEANUP;
}
pig->ioctlSubType = IG_DEBUG_EVENT;
pig->length = sizeof(DEBUG_EVENT);
if (xosdNone != OSDSystemService( CrashdumpInfo.dp.hpid,
CrashdumpInfo.dp.htid,
(SSVC) ssvcGeneric,
(LPV)pig,
sizeof(IOCTLGENERIC) + sizeof(DEBUG_EVENT),
&dw
)) {
CmdLogFmt("!!!!!Crash error: Unable to obtain debug event\r\n");
logerr = LOGERROR_QUIET;
goto CLEANUP;
}
Assert(sizeof(DEBUG_EVENT) == pig->length);
memcpy(&CrashdumpInfo.dp.DebugEvent, pig->data, sizeof(DEBUG_EVENT));
free( pig );
}
CrashdumpInfo.pExceptionInfo = &CrashdumpInfo.dp.DebugEvent.u.Exception;
CrashdumpInfo.CrashModule = (PCRASH_MODULE) calloc( 4096, 1);
// Write the dump file out
CmdLogFmt("Crash: generating dump file. This could take a while.\r\n");
if (DmpCreateUserDump(pszDumpName, WindbgCrashDumpCallback, &CrashdumpInfo)) {
CmdLogFmt("Crash: success\r\n");
} else {
CmdLogFmt("Crash: error generating dump file\r\n");
logerr = LOGERROR_QUIET;
}
CLEANUP:
if (CrashdumpInfo.CrashModule) {
free(CrashdumpInfo.CrashModule);
}
*szMainModule = NULL;
return logerr;
}
BOOL
GetThreadContext_CrashDump(
LPTD lptd,
LPCONTEXT lpContext,
UINT uSize
)
/*++
Routine Description:
This function gets thread context.
Arguments:
lpContext - Supplies pointer to CONTEXT strructure.
cbSizeOfContext - Supplies the size of CONTEXT structure.
Return Value:
TRUE for success; FALSE for failure.
--*/
{
DWORD dw = 0;
Assert(uSize < ULONG_MAX);
return OSDSystemService( lptd->lppd->hpid,
lptd->htid,
(SSVC) ssvcGetThreadContext,
lpContext,
(DWORD) uSize,
&dw
) == xosdNone;
Assert(dw == uSize);
}
void
ConvertMemInfoFromOSDFmtToSysFmt(
MEMORY_BASIC_INFORMATION * pmiSys,
MEMINFO * pmiOSD
)
{
Assert(pmiOSD);
Assert(pmiSys);
ZeroMemory(pmiSys, sizeof(*pmiSys));
// BUGBUG 64 bit
pmiSys->BaseAddress = (PVOID) pmiOSD->addr.addr.off;
pmiSys->AllocationBase = (PVOID) pmiOSD->addrAllocBase.addr.off;
pmiSys->RegionSize = (DWORD)pmiOSD->uRegionSize;
pmiSys->Protect = pmiOSD->dwProtect;
pmiSys->State = pmiOSD->dwState;
pmiSys->Type = pmiOSD->dwType;
pmiSys->AllocationProtect = pmiOSD->dwAllocationProtect;
}
int __cdecl
SkewedSortModEntryByAddress(
const void *pMod1,
const void *pMod2
)
/*++
The main module will always be first
--*/
{
TCHAR szFullName[_MAX_PATH] = {0};
TCHAR szExt[_MAX_EXT] = {0};
// Build the full name of the modules
_tsplitpath(SHGetExeName( (HEXE)ModuleEntryEmi((LPMODULE_ENTRY)pMod1) ),
NULL, NULL, szFullName, szExt);
_tcscat(szFullName, szExt);
// Found the main module?
if (!_tcsicmp(szMainModule, szFullName)) {
return -1;
}
_tsplitpath(SHGetExeName( (HEXE)ModuleEntryEmi((LPMODULE_ENTRY)pMod2) ),
NULL, NULL, szFullName, szExt);
_tcscat(szFullName, szExt);
// Found the main module?
if (!_tcsicmp(szMainModule, szFullName)) {
return 1;
}
return ModuleEntryBase((LPMODULE_ENTRY)pMod1) < ModuleEntryBase((LPMODULE_ENTRY)pMod2) ? -1 : 1;
}