DEBUG.C

/*++ 

Copyright (c) 1993 Microsoft Corporation

Module Name:

debug.c

Abstract:

This file implements the debug module for drwatson. This module
processes all debug events and generates the postmortem dump.

Author:

Wesley Witt (wesw) 1-May-1993

Environment:

User Mode

--*/

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>

#include "drwatson.h"
#include "cv.h"
#include "cvfmt.h"
#include "proto.h"
#include "messages.h"
#include "resource.h"

typedef struct tagSYSINFO {
char szUserName[MAX_PATH];
char szMachineName[MAX_PATH];
} SYSINFO, *PSYSINFO;


#define DBG_EXCEPTION_HANDLED ((DWORD)0x00010001L)
#define STATUS_POSSIBLE_DEADLOCK ((DWORD)0xC0000194L)
#define STATUS_VDM_EVENT STATUS_SEGMENT_NOTIFICATION

PTHREADCONTEXT
AllocTctx(
PDEBUGPACKET dp
);

void
PostMortemDump(
PDEBUGPACKET dp,
LPEXCEPTION_DEBUG_INFO ed
);

void
AttachToActiveProcess (
PDEBUGPACKET dp
);

void
ProcessCreateProcess(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
);

void
ProcessCreateThread(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
);

void
ProcessExitThread(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
);

void
ProcessLoadDll(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
);

void
LogSystemInformation(
PDEBUGPACKET dp
);

DWORD
SysInfoThread(
PSYSINFO si
);

void
LogDisassembly(
PDEBUGPACKET dp,
PCRASHES pCrash
);

void
LogStackWalk(
PDEBUGPACKET dp
);

void
LogStackDump(
PDEBUGPACKET dp
);

char *
GetExceptionText(
DWORD dwExceptionCode
);

LPSTR
ExpandPath(
LPSTR lpPath
);

void
SetFaultingContext(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
);

void
SkipOverBreakpoint(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
);

DWORD
DispatchDebugEventThread(
PDEBUGPACKET dp
)

/*++

Routine Description:

This is the entry point for DRWTSN32

Arguments:

None.

Return Value:

None.

--*/

{
DEBUG_EVENT de;
DWORD rc = 0;
char szLogFileName[1024];
char buf[1024];
LPSTR p;
DWORD ContinueStatus;


if (dp->dwPidToDebug == 0) {
rc = 1;
goto exit;
}

SetErrorMode( SEM_FAILCRITICALERRORS |
SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX );

AttachToActiveProcess( dp );

p = ExpandPath( dp->options.szLogPath );
if (p) {
strcpy( szLogFileName, p );
free( p );
} else {
strcpy( szLogFileName, dp->options.szLogPath );
}
MakeLogFileName( szLogFileName );
OpenLogFile( szLogFileName,
dp->options.fAppendToLogFile,
dp->options.fVisual
);

while (TRUE) {
if (!WaitForDebugEvent( &de, 10000 )) {
rc = GetLastError();
goto exit;
}
ContinueStatus = DBG_CONTINUE;

switch (de.dwDebugEventCode) {
case EXCEPTION_DEBUG_EVENT:
if (de.u.Exception.ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT) {
if (de.u.Exception.dwFirstChance) {
SkipOverBreakpoint(dp, &de);
ContinueStatus = DBG_EXCEPTION_HANDLED;
//
// The aedebug event will be signalled AFTER this
// thread exits, so that it will disappear before
// the dump snapshot is taken.
//
break;
}
}
if (dp->options.fVisual) {
//
// this notification is necessary because the shell must know when
// the debugee has been attached. if it doesn't know and the user is
// allowed to terminate drwatson then the system may intervene with
// a popup.
//
SendMessage( dp->hwnd, WM_ATTACHCOMPLETE, 0, 0 );
wsprintf( buf,
LoadRcString( IDS_AE_TEXT ),
GetExceptionText(de.u.Exception.ExceptionRecord.ExceptionCode),
de.u.Exception.ExceptionRecord.ExceptionCode,
de.u.Exception.ExceptionRecord.ExceptionAddress );
SendMessage( dp->hwnd, WM_EXCEPTIONINFO, 0, (LPARAM) buf );
}
SetFaultingContext(dp, &de);
PostMortemDump( dp, &de.u.Exception );
if (dp->options.fCrash) {
CreateDumpFile( dp, &de.u.Exception );
}
ContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
break;

case CREATE_THREAD_DEBUG_EVENT:
ProcessCreateThread( dp, &de );
break;

case CREATE_PROCESS_DEBUG_EVENT:
ProcessModuleLoad( dp, &de );
de.u.CreateThread.hThread = de.u.CreateProcessInfo.hThread;
ProcessCreateThread( dp, &de );
break;

case EXIT_THREAD_DEBUG_EVENT:
ProcessExitThread( dp, &de );
if ( dp->hEventToSignal ) {
SetEvent(dp->hEventToSignal);
dp->hEventToSignal = 0L;
}
break;

case EXIT_PROCESS_DEBUG_EVENT:
goto exit;
break;

case LOAD_DLL_DEBUG_EVENT:
ProcessModuleLoad( dp, &de );
break;

case UNLOAD_DLL_DEBUG_EVENT:
break;

case OUTPUT_DEBUG_STRING_EVENT:
break;

case RIP_EVENT:
break;

default:
lprintf( MSG_INVALID_DEBUG_EVENT, de.dwDebugEventCode );
break;
}
ContinueDebugEvent( de.dwProcessId, de.dwThreadId, ContinueStatus );
}

exit:
CloseLogFile();

if (dp->options.fVisual) {
SendMessage( dp->hwnd, WM_DUMPCOMPLETE, 0, 0 );
}

return 0;
}

PTHREADCONTEXT
AllocTctx(
PDEBUGPACKET dp
)
{
PTHREADCONTEXT ptctx;

ptctx = (PTHREADCONTEXT) malloc( sizeof(THREADCONTEXT) );
if (ptctx == NULL) {
if (dp->options.fVisual) {
FatalError( LoadRcString(IDS_MEMORY) );
}
else {
ExitProcess( 1 );
}
}
memset( ptctx, 0, sizeof(THREADCONTEXT) );

return ptctx;
}

void
ProcessCreateThread(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
)
{
dp->tctx = AllocTctx( dp );
dp->tctx->hThread = de->u.CreateThread.hThread;
dp->tctx->dwThreadId = de->dwThreadId;

InsertTailList(&dp->ThreadList, &dp->tctx->ThreadList);

return;
}

void
ProcessExitThread(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
)
{
PTHREADCONTEXT ptctx;
PLIST_ENTRY List = dp->ThreadList.Flink;

while (List != &dp->ThreadList) {
ptctx = CONTAINING_RECORD(List, THREADCONTEXT, ThreadList);
if (ptctx->dwThreadId == de->dwThreadId) {
RemoveEntryList(List);
free(ptctx);
break;
}
List = List->Flink;
}
}

void
PostMortemDump(
PDEBUGPACKET dp,
LPEXCEPTION_DEBUG_INFO ed
)
{
IMAGEHLP_MODULE mi;
char dbuf[1024];
char szDate[20];
char szTime[20];
CRASHES crash;
DWORD dwThreadId;
HANDLE hThread;
PLIST_ENTRY List;


GetLocalTime( &crash.time );
crash.dwExceptionCode = ed->ExceptionRecord.ExceptionCode;
crash.dwAddress = (DWORD)ed->ExceptionRecord.ExceptionAddress;
strcpy( crash.szAppName, szApp );

lprintf( MSG_APP_EXCEPTION );
wsprintf( dbuf, "%d", dp->dwPidToDebug );
lprintf( MSG_APP_EXEP_NAME, crash.szAppName, dbuf );
wsprintf( szDate, "%d/%d/%d", crash.time.wMonth,
crash.time.wDay,
crash.time.wYear );
wsprintf( szTime, "%d:%d:%d.%d", crash.time.wHour,
crash.time.wMinute,
crash.time.wSecond,
crash.time.wMilliseconds );
lprintf( MSG_APP_EXEP_WHEN, szDate, szTime );
wsprintf( dbuf, "%08lx", ed->ExceptionRecord.ExceptionCode );
lprintf( MSG_EXCEPTION_NUMBER, dbuf );


lprintfs( "(%s)\r\n\r\n",
GetExceptionText(ed->ExceptionRecord.ExceptionCode) );

LogSystemInformation( dp );

LogTaskList();

if (SymGetModuleInfo( dp->hProcess, 0, &mi )) {
do {
lprintfs( "(%08x - %08x) %s\r\n",
(DWORD)mi.BaseOfImage,
(DWORD)mi.BaseOfImage + mi.ImageSize,
mi.LoadedImageName
);
} while( SymGetModuleInfo( dp->hProcess, (DWORD)-1, &mi ));
lprintfs( "\r\n" );
}

List = dp->ThreadList.Flink;
while (List != &dp->ThreadList) {

dp->tctx = CONTAINING_RECORD(List, THREADCONTEXT, ThreadList);
List = List->Flink;

GetContextForThread( dp );

if (dp->tctx->fFaultingContext || dp->options.fDumpAllThreads) {
wsprintf( dbuf, "%x", dp->tctx->dwThreadId );
lprintf( MSG_STATE_DUMP, dbuf );
OutputAllRegs( dp, TRUE );
LogDisassembly( dp, &crash );
LogStackWalk( dp );
LogStackDump( dp );
}
}

if (dp->options.fDumpSymbols) {
DumpSymbols( dp );
}

ElSaveCrash( &crash, dp->options.dwMaxCrashes );

hThread = CreateThread( NULL,
16000,
(LPTHREAD_START_ROUTINE)TerminationThread,
dp,
0,
(LPDWORD)&dwThreadId
);

WaitForSingleObject( hThread, 30000 );

CloseHandle( hThread );

return;
}

void
LogStackDump(
PDEBUGPACKET dp
)
{
DWORD i;
DWORD j;
BYTE stack[1024];



memset( stack, 0, sizeof(stack) );
if (!DoMemoryRead( dp,
(LPVOID)dp->tctx->stack,
(LPVOID)stack,
sizeof(stack),
(LPDWORD)&i )) {
return;
}

lprintf( MSG_STACK_DUMP_HEADER );

for( i = 0; i < 20; i++ ) {
j = i * 16;
lprintfs( "%08x %02x %02x %02x %02x %02x %02x %02x %02x - %02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\r\n",
j + dp->tctx->stack,
stack[ j + 0 ],
stack[ j + 1 ],
stack[ j + 2 ],
stack[ j + 3 ],
stack[ j + 4 ],
stack[ j + 5 ],
stack[ j + 6 ],
stack[ j + 7 ],
stack[ j + 8 ],
stack[ j + 9 ],
stack[ j + 10 ],
stack[ j + 11 ],
stack[ j + 12 ],
stack[ j + 13 ],
stack[ j + 14 ],
stack[ j + 15 ],
isprint( stack[ j + 0 ]) ? stack[ j + 0 ] : '.',
isprint( stack[ j + 1 ]) ? stack[ j + 1 ] : '.',
isprint( stack[ j + 2 ]) ? stack[ j + 2 ] : '.',
isprint( stack[ j + 3 ]) ? stack[ j + 3 ] : '.',
isprint( stack[ j + 4 ]) ? stack[ j + 4 ] : '.',
isprint( stack[ j + 5 ]) ? stack[ j + 5 ] : '.',
isprint( stack[ j + 6 ]) ? stack[ j + 6 ] : '.',
isprint( stack[ j + 7 ]) ? stack[ j + 7 ] : '.',
isprint( stack[ j + 8 ]) ? stack[ j + 8 ] : '.',
isprint( stack[ j + 9 ]) ? stack[ j + 9 ] : '.',
isprint( stack[ j + 10 ]) ? stack[ j + 10 ] : '.',
isprint( stack[ j + 11 ]) ? stack[ j + 11 ] : '.',
isprint( stack[ j + 12 ]) ? stack[ j + 12 ] : '.',
isprint( stack[ j + 13 ]) ? stack[ j + 13 ] : '.',
isprint( stack[ j + 14 ]) ? stack[ j + 14 ] : '.',
isprint( stack[ j + 15 ]) ? stack[ j + 15 ] : '.'
);
}

lprintfs( "\r\n" );
return;
}

void
LogStackWalk(
PDEBUGPACKET dp
)
{
#define SAVE_EBP(f) f.Reserved[0]
#define TRAP_TSS(f) f.Reserved[1]
#define TRAP_EDITED(f) f.Reserved[1]
#define SAVE_TRAP(f) f.Reserved[2]

DWORD dwDisplacement = 0;
DWORD frames = 0;
LPSTR szSymName;
IMAGEHLP_MODULE mi;
DWORD i;
DWORD machType;
CONTEXT Context;
STACKFRAME stk;


Context = dp->tctx->context;
ZeroMemory( &stk, sizeof(stk) );

stk.AddrPC.Offset = dp->tctx->pc;
stk.AddrPC.Mode = AddrModeFlat;

#if defined(_M_IX86)
machType = IMAGE_FILE_MACHINE_I386;
stk.AddrStack.Offset = dp->tctx->stack;
stk.AddrStack.Mode = AddrModeFlat;
stk.AddrFrame.Offset = dp->tctx->frame;
stk.AddrFrame.Mode = AddrModeFlat;
#elif defined(_M_MRX000)
machType = IMAGE_FILE_MACHINE_R4000;
#elif defined(_M_ALPHA)
machType = IMAGE_FILE_MACHINE_ALPHA;
#elif defined(_M_PPC)
machType = IMAGE_FILE_MACHINE_POWERPC;
#else
#error( "unknown target machine" );
#endif

lprintf( MSG_STACKTRACE );

for (i=0; i<100; i++) {
if (!StackWalk( machType,
(HANDLE)dp,
NULL,
&stk,
&Context,
SwReadProcessMemory,
SwFunctionTableAccess,
SwGetModuleBase,
SwTranslateAddress )) {
break;
}
if (SymGetSymFromAddr( dp->hProcess, stk.AddrPC.Offset, &dwDisplacement, sym )) {
szSymName = sym->Name;
}
else {
szSymName = "<nosymbols>";
}
lprintfs( "%08x %08x %08x %08x %08x %08x ",
stk.AddrFrame.Offset,
stk.AddrReturn.Offset,
stk.Params[0],
stk.Params[1],
stk.Params[2],
stk.Params[3]
);

if (SymGetModuleInfo( dp->hProcess, stk.AddrPC.Offset, &mi )) {
lprintfs( "%s!", mi.ModuleName );
}

lprintfs( "%s ", szSymName );

if (sym && (sym->Flags & SYMF_OMAP_GENERATED || sym->Flags & SYMF_OMAP_MODIFIED)) {
lprintfs( "[omap] " );
}

if (stk.FuncTableEntry && machType == IMAGE_FILE_MACHINE_I386) {
PFPO_DATA pFpoData = (PFPO_DATA)stk.FuncTableEntry;
switch (pFpoData->cbFrame) {
case FRAME_FPO:
if (pFpoData->fHasSEH) {
lprintfs( "(FPO: [SEH])" );
} else {
lprintfs( " (FPO:" );
if (pFpoData->fUseBP) {
lprintfs( " [EBP 0x%08x]", SAVE_EBP(stk) );
}
lprintfs(" [%d,%d,%d])", pFpoData->cdwParams,
pFpoData->cdwLocals,
pFpoData->cbRegs);
}
break;
case FRAME_NONFPO:
lprintfs( "(FPO: Non-FPO [%d,%d,%d])",
pFpoData->cdwParams,
pFpoData->cdwLocals,
pFpoData->cbRegs);
break;

case FRAME_TRAP:
case FRAME_TSS:
default:
lprintfs( "(UNKNOWN FPO TYPE)" );
break;
}
}

lprintfs( "\r\n" );

}

lprintfs( "\r\n" );
return;
}


void
LogDisassembly(
PDEBUGPACKET dp,
PCRASHES pCrash
)
{
DWORD dwFuncAddr;
DWORD dwFuncSize;
DWORD dwDisplacement = 0;
char *szSymName;
DWORD offset;
int i;
int j;
char dbuf[1024];
BOOL fFaultingInst;
int dwStartDis;
int dwEndDis;


if (SymGetSymFromAddr( dp->hProcess, dp->tctx->pc, &dwDisplacement, sym )) {
dwFuncAddr = sym->Address;
dwFuncSize = sym->Size;
if (dwFuncSize < 1) {
dwFuncSize = 100;
}
szSymName = sym->Name;
}
else {
dwFuncAddr = dp->tctx->pc - 50;
dwFuncSize = 100;
szSymName = "<nosymbols>";
}

if (dp->tctx->fFaultingContext) {
strcpy( pCrash->szFunction, szSymName );
}

lprintf( MSG_FUNCTION, szSymName );

tryagain:
//
// count the number of instructions in the function
// also, save the instruction number of context's pc
//
for (i=0,offset=dwFuncAddr,j=-1; offset<dwFuncAddr+dwFuncSize; i++) {
if (offset == dp->tctx->pc) {
j = i;
}
if (!disasm( dp, &offset, dbuf, TRUE )) {
break;
}
}

if (j == -1) {
//
// we didn't find a match for the current pc
// this because we don't have symbols for the current pc and
// therefore had to just backup and start disassembling. we try
// to recover by adding 1 to the func addr and do it again.
// eventually we will hit the pc and we will be a-ok.
//
dwFuncAddr++;
goto tryagain;
}

//
// print the disassemled instructions. only print the number
// of instructions before and after the current pc that the
// user specified in the registry options.
//
dwStartDis = max(0,j - (int)dp->options.dwInstructions);
dwEndDis = j+(int)dp->options.dwInstructions;
fFaultingInst = FALSE;
for (i=0,offset=dwFuncAddr; offset<dwFuncAddr+dwFuncSize; i++) {
if (offset == dp->tctx->pc) {
fFaultingInst = TRUE;
}
if (!disasm( dp, &offset, dbuf, TRUE )) {
break;
}
if (i >= dwStartDis) {
if (fFaultingInst && dp->tctx->fFaultingContext) {
fFaultingInst = FALSE;
lprintf( MSG_FAULT );
}
else {
lprintfs( " " );
}
lprintfs( "%s\r\n", dbuf );
}
if (i > dwEndDis) {
break;
}
}

lprintfs( "\r\n" );
return;
}

void
AttachToActiveProcess (
PDEBUGPACKET dp
)
{
HANDLE Token;
PTOKEN_PRIVILEGES NewPrivileges;
BYTE OldPriv[1024];
PBYTE pbOldPriv;
ULONG cbNeeded;
BOOLEAN fRc;
LUID LuidPrivilege;

//
// Make sure we have access to adjust and to get the old token privileges
//
if (!OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&Token)) {
if (dp->options.fVisual) {
FatalError( LoadRcString(IDS_DEBUGPRIV) );
}
else {
ExitProcess( 1 );
}
}

cbNeeded = 0;

//
// Initialize the privilege adjustment structure
//

LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &LuidPrivilege );

NewPrivileges =
(PTOKEN_PRIVILEGES)LocalAlloc(LMEM_ZEROINIT,
sizeof(TOKEN_PRIVILEGES) +
(1 - ANYSIZE_ARRAY) *
sizeof(LUID_AND_ATTRIBUTES));
if (NewPrivileges == NULL) {
if (dp->options.fVisual) {
FatalError( LoadRcString(IDS_DEBUGPRIV) );
}
else {
ExitProcess( 1 );
}
}

NewPrivileges->PrivilegeCount = 1;
NewPrivileges->Privileges[0].Luid = LuidPrivilege;
NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

//
// Enable the privilege
//

pbOldPriv = OldPriv;
fRc = AdjustTokenPrivileges( Token,
FALSE,
NewPrivileges,
1024,
(PTOKEN_PRIVILEGES)pbOldPriv,
&cbNeeded );

if (!fRc) {

//
// If the stack was too small to hold the privileges
// then allocate off the heap
//
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

pbOldPriv = LocalAlloc(LMEM_FIXED, cbNeeded);
if (pbOldPriv == NULL) {
if (dp->options.fVisual) {
FatalError( LoadRcString(IDS_DEBUGPRIV) );
}
else {
ExitProcess( 1 );
}
}

fRc = AdjustTokenPrivileges( Token,
FALSE,
NewPrivileges,
cbNeeded,
(PTOKEN_PRIVILEGES)pbOldPriv,
&cbNeeded );
}
}


if (!DebugActiveProcess( dp->dwPidToDebug )) {
if (dp->options.fVisual) {
FatalError( LoadRcString(IDS_ATTACHFAIL) );
}
else {
ExitProcess( 1 );
}
}

return;
}

void
LogSystemInformation(
PDEBUGPACKET dp
)
{
char buf[1024];
SYSTEM_INFO si;
DWORD ver;
SYSINFO mySi;
DWORD dwThreadId;
HANDLE hThread;

lprintf( MSG_SYSINFO_HEADER );
hThread = CreateThread( NULL,
16000,
(LPTHREAD_START_ROUTINE)SysInfoThread,
&mySi,
0,
(LPDWORD)&dwThreadId
);
Sleep( 0 );
if (WaitForSingleObject( hThread, 30000 ) == WAIT_TIMEOUT) {
Assert(TerminateThread( hThread, 0 ) == TRUE);
}
CloseHandle( hThread );
lprintf( MSG_SYSINFO_COMPUTER, mySi.szMachineName );
lprintf( MSG_SYSINFO_USER, mySi.szUserName );
GetSystemInfo( &si );
wsprintf( buf, "%d", si.dwNumberOfProcessors );
lprintf( MSG_SYSINFO_NUM_PROC, buf );
RegLogProcessorType();
ver = GetVersion();
wsprintf( buf, "%d.%d", LOBYTE(LOWORD(ver)), HIBYTE(LOWORD(ver)) );
lprintf( MSG_SYSINFO_WINVER, buf );
RegLogCurrentVersion();
lprintfs( "\r\n" );
}

DWORD
SysInfoThread(
PSYSINFO si
)
{
DWORD len;

strcpy( si->szMachineName, LoadRcString( IDS_UNKNOWN_MACHINE ) );
strcpy( si->szUserName, LoadRcString( IDS_UNKNOWN_USER ) );
len = sizeof(si->szMachineName);
GetComputerName( si->szMachineName, &len );
len = sizeof(si->szUserName);
GetUserName( si->szUserName, &len );

return 0;
}

DWORD
TerminationThread(
PDEBUGPACKET dp
)
{
HANDLE hProcess;

hProcess = OpenProcess( PROCESS_TERMINATE, FALSE, dp->dwPidToDebug );
if (hProcess != NULL) {
TerminateProcess( hProcess, 0 );
CloseHandle( hProcess );
}

return 0;
}

char *
GetExceptionText(
DWORD dwExceptionCode
)
{
static char buf[80];
DWORD dwFormatId = 0;

memset( buf, 0, sizeof(buf) );

switch (dwExceptionCode) {
case STATUS_SINGLE_STEP:
dwFormatId = MSG_SINGLE_STEP_EXCEPTION;
break;

case DBG_CONTROL_C:
dwFormatId = MSG_CONTROLC_EXCEPTION;
break;

case DBG_CONTROL_BREAK:
dwFormatId = MSG_CONTROL_BRK_EXCEPTION;
break;

case STATUS_ACCESS_VIOLATION:
dwFormatId = MSG_ACCESS_VIOLATION_EXCEPTION;
break;

case STATUS_STACK_OVERFLOW:
dwFormatId = MSG_STACK_OVERFLOW_EXCEPTION;
break;

case STATUS_INTEGER_DIVIDE_BY_ZERO:
dwFormatId = MSG_INTEGER_DIVIDE_BY_ZERO_EXCEPTION;
break;

case STATUS_PRIVILEGED_INSTRUCTION:
dwFormatId = MSG_PRIVILEGED_INSTRUCTION_EXCEPTION;
break;

case STATUS_ILLEGAL_INSTRUCTION:
dwFormatId = MSG_ILLEGAL_INSTRUCTION_EXCEPTION;
break;

case STATUS_IN_PAGE_ERROR:
dwFormatId = MSG_IN_PAGE_IO_EXCEPTION;
break;

case STATUS_DATATYPE_MISALIGNMENT:
dwFormatId = MSG_DATATYPE_EXCEPTION;
break;

case STATUS_POSSIBLE_DEADLOCK:
dwFormatId = MSG_DEADLOCK_EXCEPTION;
break;

case STATUS_VDM_EVENT:
dwFormatId = MSG_VDM_EXCEPTION;
break;

case STATUS_BREAKPOINT:
dwFormatId = MSG_BREAKPOINT_EXCEPTION;
break;

default:
lprintfs( "\r\n" );
break;
}

FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL,
dwFormatId,
0, // GetUserDefaultLangID(),
buf,
sizeof(buf),
NULL
);

return buf;
}

BOOL
DoMemoryRead(
PDEBUGPACKET dp,
LPVOID addr,
LPVOID buf,
DWORD size,
LPDWORD lpcb
)
{
return ReadProcessMemory( dp->hProcess, addr, buf, size, lpcb );
}

LPSTR
ExpandPath(
LPSTR lpPath
)
{
DWORD len;
LPSTR p;


len = ExpandEnvironmentStrings( lpPath, NULL, 0 );
if (!len) {
return NULL;
}

len += 1;
p = malloc( len );
if (!p) {
return NULL;
}

len = ExpandEnvironmentStrings( lpPath, p, len );
if (!len) {
free( p );
return NULL;
}

return p;
}

void
SetFaultingContext(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
)
{
PTHREADCONTEXT ptctx;
PLIST_ENTRY List = dp->ThreadList.Flink;

dp->DebugEvent = *de;

while (List != &dp->ThreadList) {
ptctx = CONTAINING_RECORD(List, THREADCONTEXT, ThreadList);
List = List->Flink;
if (ptctx->dwThreadId == de->dwThreadId) {
ptctx->fFaultingContext = TRUE;
break;
}
}
}

PTHREADCONTEXT
DebugEventToThreadContext(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
)
{
PTHREADCONTEXT ptctx;
PLIST_ENTRY List = dp->ThreadList.Flink;

while (List != &dp->ThreadList) {
ptctx = CONTAINING_RECORD(List, THREADCONTEXT, ThreadList);
List = List->Flink;
if (ptctx->dwThreadId == de->dwThreadId) {
return ptctx;
}
}
return NULL;
}

void
SkipOverBreakpoint(
PDEBUGPACKET dp,
LPDEBUG_EVENT de
)
{
PTHREADCONTEXT ptctx;
CONTEXT Context;

ptctx = DebugEventToThreadContext(dp, de);
Context.ContextFlags = CONTEXT_FULL;
GetThreadContext(ptctx->hThread, &Context);
PC(&Context) = (DWORD) de->u.Exception.ExceptionRecord.ExceptionAddress + BP_SIZE;
SetThreadContext(ptctx->hThread, &Context);
}