1870 lines
56 KiB
C++
1870 lines
56 KiB
C++
//----------------------------------------------------------------------------
|
|
//
|
|
// Top-level command parsing.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1990-2002.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
|
|
// Set if registers should be displayed by default in OutCurInfo.
|
|
BOOL g_OciOutputRegs;
|
|
|
|
ULONG g_DefaultStackTraceDepth = 20;
|
|
|
|
BOOL g_EchoEventTimestamps;
|
|
|
|
BOOL g_SwitchedProcs;
|
|
|
|
// Last command executed.
|
|
CHAR g_LastCommand[MAX_COMMAND];
|
|
|
|
// State variables for top-level command processing.
|
|
PSTR g_CommandStart; // Start of command buffer.
|
|
PSTR g_CurCmd; // Current pointer in command buffer.
|
|
ULONG g_PromptLength = 8; // Size of prompt string.
|
|
|
|
ADDR g_UnasmDefault; // Default unassembly address.
|
|
ADDR g_AssemDefault; // Default assembly address.
|
|
|
|
ULONG g_DefaultRadix = 16; // Default number base.
|
|
|
|
CHAR g_SymbolSuffix = 'n'; // Suffix to add to symbol if search
|
|
// failure - 'n'-none 'a'-'w'-append.
|
|
|
|
CHAR g_CmdState = 'i'; // State of present command processing
|
|
// 'i'-init; 'g'-go; 't'-trace
|
|
// 'p'-step; 'c'-cmd; 'b'-branch trace.
|
|
|
|
int g_RedoCount = 0;
|
|
|
|
PWSTR g_StartProcessDir;
|
|
|
|
BOOL
|
|
ChangeSymPath(PCSTR Args,
|
|
BOOL Append,
|
|
OUT PSTR PathRet,
|
|
ULONG PathRetChars)
|
|
{
|
|
TargetInfo* Target;
|
|
ProcessInfo* Process;
|
|
|
|
if (Args != NULL)
|
|
{
|
|
while (*Args == ' ' || *Args == '\t')
|
|
{
|
|
Args++;
|
|
}
|
|
}
|
|
if (Args != NULL && *Args)
|
|
{
|
|
if (ChangePath(&g_SymbolSearchPath, Args, Append,
|
|
DEBUG_CSS_PATHS) != S_OK)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ForAllLayersToProcess()
|
|
{
|
|
SymSetSearchPath(Process->m_SymHandle, g_SymbolSearchPath);
|
|
}
|
|
}
|
|
|
|
if (PathRet)
|
|
{
|
|
CopyString(PathRet, g_SymbolSearchPath, PathRetChars);
|
|
}
|
|
else
|
|
{
|
|
dprintf("Symbol search path is: %s\n", g_SymbolSearchPath);
|
|
CheckPath(g_SymbolSearchPath);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
CallBugCheckExtension(DebugClient* Client)
|
|
{
|
|
HRESULT Status = E_FAIL;
|
|
|
|
if (Client == NULL)
|
|
{
|
|
Client = FindExtClient();
|
|
}
|
|
|
|
if (Client != NULL)
|
|
{
|
|
char ExtName[32];
|
|
|
|
// Extension name has to be in writable memory as it
|
|
// gets lower-cased.
|
|
strcpy(ExtName, "Analyze");
|
|
|
|
// See if any existing extension DLLs are interested
|
|
// in analyzing this bugcheck.
|
|
CallAnyExtension(Client, NULL, ExtName, "",
|
|
FALSE, FALSE, &Status);
|
|
}
|
|
|
|
if (Status != S_OK)
|
|
{
|
|
if (Client == NULL)
|
|
{
|
|
WarnOut("WARNING: Unable to locate a client for "
|
|
"bugcheck analysis\n");
|
|
}
|
|
|
|
dprintf("*******************************************************************************\n");
|
|
dprintf("* *\n");
|
|
dprintf("* Bugcheck Analysis *\n");
|
|
dprintf("* *\n");
|
|
dprintf("*******************************************************************************\n");
|
|
|
|
Execute(Client, ".bugcheck", DEBUG_EXECUTE_DEFAULT);
|
|
dprintf("\n");
|
|
Execute(Client, "kb", DEBUG_EXECUTE_DEFAULT);
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
HandleBPWithStatus(void)
|
|
{
|
|
ULONG Status = (ULONG)g_EventMachine->GetArgReg();
|
|
|
|
switch(Status)
|
|
{
|
|
case DBG_STATUS_CONTROL_C:
|
|
case DBG_STATUS_SYSRQ:
|
|
if ((g_EngOptions & DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION) == 0 &&
|
|
!g_QuietMode)
|
|
{
|
|
dprintf("*******************************************************************************\n");
|
|
dprintf("* *\n");
|
|
|
|
if (Status == DBG_STATUS_SYSRQ)
|
|
{
|
|
dprintf("* You are seeing this message because you pressed the SysRq/PrintScreen *\n");
|
|
dprintf("* key on your test machine's keyboard. *\n");
|
|
}
|
|
|
|
if (Status == DBG_STATUS_DEBUG_CONTROL)
|
|
{
|
|
dprintf("* You are seeing this message because you typed in .breakin from ntsd. *\n");
|
|
}
|
|
|
|
if (Status == DBG_STATUS_CONTROL_C)
|
|
{
|
|
dprintf("* You are seeing this message because you pressed either *\n");
|
|
dprintf("* CTRL+C (if you run kd.exe) or, *\n");
|
|
dprintf("* CTRL+BREAK (if you run WinDBG), *\n");
|
|
dprintf("* on your debugger machine's keyboard. *\n");
|
|
|
|
}
|
|
|
|
dprintf("* *\n");
|
|
dprintf("* THIS IS NOT A BUG OR A SYSTEM CRASH *\n");
|
|
dprintf("* *\n");
|
|
dprintf("* If you did not intend to break into the debugger, press the \"g\" key, then *\n");
|
|
dprintf("* press the \"Enter\" key now. This message might immediately reappear. If it *\n");
|
|
dprintf("* does, press \"g\" and \"Enter\" again. *\n");
|
|
dprintf("* *\n");
|
|
dprintf("*******************************************************************************\n");
|
|
}
|
|
break;
|
|
|
|
case DBG_STATUS_BUGCHECK_FIRST:
|
|
ErrOut("\nA fatal system error has occurred.\n");
|
|
ErrOut("Debugger entered on first try; "
|
|
"Bugcheck callbacks have not been invoked.\n");
|
|
// Fall through.
|
|
|
|
case DBG_STATUS_BUGCHECK_SECOND:
|
|
ErrOut("\nA fatal system error has occurred.\n\n");
|
|
CallBugCheckExtension(NULL);
|
|
break;
|
|
|
|
case DBG_STATUS_FATAL:
|
|
// hals call KeEnterDebugger when they panic.
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define MAX_PROMPT 32
|
|
|
|
HRESULT
|
|
GetPromptText(PSTR Buffer, ULONG BufferSize, PULONG TextSize)
|
|
{
|
|
char Prompt[MAX_PROMPT];
|
|
PSTR Text = Prompt;
|
|
|
|
if (g_NumberTargets > 1)
|
|
{
|
|
*Text++ = '|';
|
|
*Text++ = '|';
|
|
if (g_Target)
|
|
{
|
|
sprintf(Text, "%1ld:", g_Target->m_UserId);
|
|
Text += strlen(Text);
|
|
}
|
|
else
|
|
{
|
|
*Text++ = '?';
|
|
*Text++ = ':';
|
|
}
|
|
}
|
|
|
|
if (IS_LOCAL_KERNEL_TARGET(g_Target))
|
|
{
|
|
strcpy(Text, "lkd");
|
|
Text += 3;
|
|
}
|
|
else if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
if (g_X86InVm86)
|
|
{
|
|
strcpy(Text, "vm");
|
|
Text += 2;
|
|
}
|
|
else if (g_X86InCode16)
|
|
{
|
|
strcpy(Text, "16");
|
|
Text += 2;
|
|
}
|
|
else if (g_Target &&
|
|
g_Target->m_MachineType == IMAGE_FILE_MACHINE_AMD64 &&
|
|
!g_Amd64InCode64)
|
|
{
|
|
strcpy(Text, "32");
|
|
Text += 2;
|
|
}
|
|
|
|
if (g_Target == NULL ||
|
|
g_Process == NULL ||
|
|
g_Thread == NULL ||
|
|
((IS_KERNEL_FULL_DUMP(g_Target) ||
|
|
IS_KERNEL_SUMMARY_DUMP(g_Target)) &&
|
|
g_Target->m_KdDebuggerData.KiProcessorBlock == 0))
|
|
{
|
|
strcpy(Text, "?: kd");
|
|
Text += 5;
|
|
}
|
|
else if (g_Target->m_NumProcessors > 1)
|
|
{
|
|
sprintf(Text, "%d: kd", CURRENT_PROC);
|
|
Text += strlen(Text);
|
|
}
|
|
else
|
|
{
|
|
strcpy(Text, "kd");
|
|
Text += 2;
|
|
}
|
|
}
|
|
else if (IS_USER_TARGET(g_Target))
|
|
{
|
|
if (g_Process)
|
|
{
|
|
sprintf(Text, "%1ld:", g_Process->m_UserId);
|
|
Text += strlen(Text);
|
|
}
|
|
else
|
|
{
|
|
*Text++ = '?';
|
|
*Text++ = ':';
|
|
}
|
|
|
|
if (g_Thread)
|
|
{
|
|
sprintf(Text, "%03ld", g_Thread->m_UserId);
|
|
Text += strlen(Text);
|
|
}
|
|
else
|
|
{
|
|
strcpy(Text, "???");
|
|
Text += 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strcpy(Text, "NoTarget");
|
|
Text += 8;
|
|
}
|
|
|
|
if (g_Machine && g_Target &&
|
|
g_Machine->m_ExecTypes[0] != g_Target->m_MachineType)
|
|
{
|
|
*Text++ = ':';
|
|
strcpy(Text, g_Machine->m_AbbrevName);
|
|
Text += strlen(Text);
|
|
}
|
|
|
|
*Text++ = '>';
|
|
*Text = 0;
|
|
|
|
return FillStringBuffer(Prompt, (ULONG)(Text - Prompt) + 1,
|
|
Buffer, BufferSize, TextSize);
|
|
}
|
|
|
|
void
|
|
OutputPrompt(PCSTR Format, va_list Args)
|
|
{
|
|
char Prompt[MAX_PROMPT];
|
|
|
|
GetPromptText(Prompt, sizeof(Prompt), NULL);
|
|
g_PromptLength = strlen(Prompt);
|
|
|
|
MaskOut(DEBUG_OUTPUT_PROMPT, "%s", Prompt);
|
|
// Include space after >.
|
|
g_PromptLength++;
|
|
if (Format != NULL)
|
|
{
|
|
MaskOutVa(DEBUG_OUTPUT_PROMPT, Format, Args, TRUE);
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
CommandExceptionFilter(PEXCEPTION_POINTERS Info)
|
|
{
|
|
if (Info->ExceptionRecord->ExceptionCode >=
|
|
(COMMAND_EXCEPTION_BASE + OVERFLOW) &&
|
|
Info->ExceptionRecord->ExceptionCode
|
|
<= (COMMAND_EXCEPTION_BASE + UNIMPLEMENT))
|
|
{
|
|
// This is a legitimate command error code exception.
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
else
|
|
{
|
|
// This is some other exception that the command
|
|
// filter isn't supposed to handle.
|
|
ErrOut("Non-command exception %X at %s in command filter\n",
|
|
Info->ExceptionRecord->ExceptionCode,
|
|
FormatAddr64((ULONG64)Info->ExceptionRecord->ExceptionAddress));
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
}
|
|
|
|
void
|
|
ParseLoadModules(void)
|
|
{
|
|
ImageInfo* Image;
|
|
PSTR Pattern;
|
|
CHAR Save;
|
|
BOOL AnyMatched = FALSE;
|
|
|
|
if (!g_Process)
|
|
{
|
|
error(BADPROCESS);
|
|
}
|
|
|
|
Pattern =
|
|
StringValue(STRV_SPACE_IS_SEPARATOR | STRV_TRIM_TRAILING_SPACE |
|
|
STRV_ESCAPED_CHARACTERS, &Save);
|
|
_strupr(Pattern);
|
|
|
|
for (Image = g_Process->m_ImageHead; Image; Image = Image->m_Next)
|
|
{
|
|
if (MatchPattern(Image->m_ModuleName, Pattern))
|
|
{
|
|
IMAGEHLP_MODULE64 ModInfo;
|
|
|
|
AnyMatched = TRUE;
|
|
|
|
ModInfo.SizeOfStruct = sizeof(ModInfo);
|
|
if (!SymGetModuleInfo64(g_Process->m_SymHandle,
|
|
Image->m_BaseOfImage, &ModInfo) ||
|
|
ModInfo.SymType == SymDeferred)
|
|
{
|
|
if (!SymLoadModule64(g_Process->m_SymHandle,
|
|
NULL, NULL, NULL,
|
|
Image->m_BaseOfImage, 0))
|
|
{
|
|
ErrOut("Symbol load for %s failed\n", Image->m_ModuleName);
|
|
}
|
|
else
|
|
{
|
|
dprintf("Symbols loaded for %s\n", Image->m_ModuleName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("Symbols already loaded for %s\n",
|
|
Image->m_ModuleName);
|
|
}
|
|
}
|
|
|
|
if (CheckUserInterrupt())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!AnyMatched)
|
|
{
|
|
WarnOut("No modules matched '%s'\n", Pattern);
|
|
}
|
|
|
|
*g_CurCmd = Save;
|
|
}
|
|
|
|
void
|
|
ParseProcessorCommands(void)
|
|
{
|
|
ULONG Proc;
|
|
PSTR Start = g_CurCmd;
|
|
|
|
if (!g_Target)
|
|
{
|
|
error(BADSYSTEM);
|
|
}
|
|
|
|
Proc = 0;
|
|
while (*g_CurCmd >= '0' && *g_CurCmd <= '9')
|
|
{
|
|
Proc = Proc * 10 + (*g_CurCmd - '0');
|
|
g_CurCmd++;
|
|
}
|
|
if (Start == g_CurCmd)
|
|
{
|
|
// No digits.
|
|
error(SYNTAX);
|
|
}
|
|
if (*g_CurCmd == 's')
|
|
{
|
|
g_CurCmd++;
|
|
}
|
|
|
|
if (Proc < g_Target->m_NumProcessors)
|
|
{
|
|
if (Proc != g_Target->m_RegContextProcessor)
|
|
{
|
|
if (g_Target->SwitchProcessors(Proc) == S_OK)
|
|
{
|
|
ResetCurrentScope();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ErrOut("%d is not a valid processor number\n", Proc);
|
|
}
|
|
}
|
|
|
|
void
|
|
SetSuffix(void)
|
|
{
|
|
char Ch;
|
|
|
|
Ch = PeekChar();
|
|
Ch = (UCHAR)tolower(Ch);
|
|
|
|
if (Ch == ';' || Ch == '\0')
|
|
{
|
|
if (g_SymbolSuffix == 'n')
|
|
{
|
|
dprintf("n - no suffix\n");
|
|
}
|
|
else if (g_SymbolSuffix == 'a')
|
|
{
|
|
dprintf("a - ascii\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("w - wide\n");
|
|
}
|
|
}
|
|
else if (Ch == 'n' || Ch == 'a' || Ch == 'w')
|
|
{
|
|
g_SymbolSuffix = Ch;
|
|
g_CurCmd++;
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
}
|
|
|
|
void
|
|
OutputVersionInformation(DebugClient* Client)
|
|
{
|
|
DBH_DIAVERSION DiaVer;
|
|
|
|
//
|
|
// Print out the connection options if we are doing live debugging.
|
|
//
|
|
if (g_Target)
|
|
{
|
|
char Buf[2 * MAX_PATH];
|
|
|
|
g_Target->GetDescription(Buf, sizeof(Buf), NULL);
|
|
dprintf("%s\n", Buf);
|
|
}
|
|
|
|
dprintf("command line: '%s' Debugger Process 0x%X \n",
|
|
GetCommandLine(), GetCurrentProcessId());
|
|
|
|
dprintf(ENGINE_MOD_NAME ": ");
|
|
OutputModuleIdInfo(NULL, ENGINE_DLL_NAME, NULL);
|
|
|
|
CheckForStaleBinary(ENGINE_DLL_NAME, TRUE);
|
|
|
|
dprintf("dbghelp: ");
|
|
OutputModuleIdInfo(NULL, "dbghelp.dll", NULL);
|
|
|
|
DiaVer.function = dbhDiaVersion;
|
|
DiaVer.sizeofstruct = sizeof(DiaVer);
|
|
if (dbghelp(NULL, &DiaVer))
|
|
{
|
|
dprintf(" DIA version: %d\n", DiaVer.ver);
|
|
}
|
|
|
|
// Dump information about the IA64 support DLLs if they're
|
|
// loaded. Don't bother forcing them to load.
|
|
if (GetModuleHandle("decem.dll") != NULL)
|
|
{
|
|
dprintf("decem: ");
|
|
OutputModuleIdInfo(NULL, "decem.dll", NULL);
|
|
}
|
|
|
|
OutputExtensions(Client, TRUE);
|
|
|
|
if (g_Wow64exts != NULL)
|
|
{
|
|
dprintf("WOW64 extensions loaded\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
ParseStackTrace(PULONG TraceFlags,
|
|
PULONG64 Frame,
|
|
PULONG64 Stack,
|
|
PULONG64 Instr,
|
|
PULONG Count,
|
|
PULONG PtrDef)
|
|
{
|
|
char Ch;
|
|
ADDR Addr;
|
|
|
|
Ch = PeekChar();
|
|
*TraceFlags =
|
|
DEBUG_STACK_COLUMN_NAMES |
|
|
DEBUG_STACK_FRAME_ADDRESSES |
|
|
DEBUG_STACK_SOURCE_LINE;
|
|
|
|
if (tolower(Ch) == 'b')
|
|
{
|
|
g_CurCmd++;
|
|
*TraceFlags |= DEBUG_STACK_ARGUMENTS;
|
|
}
|
|
else if (tolower(Ch) == 'v')
|
|
{
|
|
g_CurCmd++;
|
|
*TraceFlags |=
|
|
DEBUG_STACK_ARGUMENTS |
|
|
DEBUG_STACK_FUNCTION_INFO |
|
|
DEBUG_STACK_NONVOLATILE_REGISTERS;
|
|
}
|
|
else if (tolower(Ch) == 'd')
|
|
{
|
|
g_CurCmd++;
|
|
*TraceFlags = RAW_STACK_DUMP;
|
|
}
|
|
else if (tolower(Ch) == 'p')
|
|
{
|
|
g_CurCmd++;
|
|
*TraceFlags |= DEBUG_STACK_PARAMETERS;
|
|
}
|
|
Ch = PeekChar();
|
|
|
|
if (tolower(Ch) == 'n')
|
|
{
|
|
if (*TraceFlags == RAW_STACK_DUMP)
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
g_CurCmd++;
|
|
Ch = PeekChar();
|
|
*TraceFlags |= DEBUG_STACK_FRAME_NUMBERS;
|
|
}
|
|
if (tolower(Ch) == 'f')
|
|
{
|
|
if (*TraceFlags == RAW_STACK_DUMP)
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
g_CurCmd++;
|
|
Ch = PeekChar();
|
|
*TraceFlags |= DEBUG_STACK_FRAME_MEMORY_USAGE;
|
|
}
|
|
if (Ch == 'L')
|
|
{
|
|
if (*TraceFlags == RAW_STACK_DUMP)
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
g_CurCmd++;
|
|
Ch = PeekChar();
|
|
*TraceFlags &= ~DEBUG_STACK_SOURCE_LINE;
|
|
}
|
|
|
|
if (!PeekChar() && GetCurrentScopeContext())
|
|
{
|
|
dprintf(" *** Stack trace for last set context - "
|
|
".thread/.cxr resets it\n");
|
|
}
|
|
|
|
*PtrDef = STACK_ALL_DEFAULT;
|
|
*Frame = 0;
|
|
*Stack = 0;
|
|
*Instr = 0;
|
|
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
*Instr = g_Machine->GetReg64(X86_EIP);
|
|
*PtrDef &= ~STACK_INSTR_DEFAULT;
|
|
}
|
|
|
|
if (PeekChar() == '=')
|
|
{
|
|
g_CurCmd++;
|
|
GetAddrExpression(SEGREG_STACK, &Addr);
|
|
*Frame = Flat(Addr);
|
|
*PtrDef &= ~STACK_FRAME_DEFAULT;
|
|
}
|
|
else if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
g_Machine->GetFP(&Addr);
|
|
*Frame = Flat(Addr);
|
|
*PtrDef &= ~STACK_FRAME_DEFAULT;
|
|
}
|
|
|
|
*Count = g_DefaultStackTraceDepth;
|
|
|
|
if (g_Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_I386 &&
|
|
(Ch = PeekChar()) != '\0' && Ch != ';')
|
|
{
|
|
//
|
|
// If only one more value it's the count
|
|
//
|
|
|
|
*Count = (ULONG)GetExpression();
|
|
|
|
if ((Ch = PeekChar()) != '\0' && Ch != ';')
|
|
{
|
|
//
|
|
// More than one value, set extra value for special
|
|
// FPO backtrace.
|
|
//
|
|
|
|
*Instr = GetExpression();
|
|
*Stack = EXTEND64(*Count);
|
|
*PtrDef &= ~(STACK_INSTR_DEFAULT | STACK_STACK_DEFAULT);
|
|
*Count = g_DefaultStackTraceDepth;
|
|
}
|
|
}
|
|
|
|
if ((Ch = PeekChar()) != '\0' && Ch != ';')
|
|
{
|
|
*Count = (ULONG)GetExpression();
|
|
if ((LONG)*Count < 1)
|
|
{
|
|
g_CurCmd++;
|
|
error(SYNTAX);
|
|
}
|
|
}
|
|
|
|
if (*Count >= 0x10000)
|
|
{
|
|
ErrOut("Requested number of stack frames (0x%x) is too large! "
|
|
"The maximum number is 0xffff.\n", *Count);
|
|
error(BADRANGE);
|
|
}
|
|
}
|
|
|
|
void
|
|
CmdHelp(void)
|
|
{
|
|
char Buf[16];
|
|
|
|
// Commands available on all platforms and in all modes.
|
|
|
|
dprintf("\nOpen debugger.chm for complete debugger documentation\n\n");
|
|
|
|
dprintf("A [<address>] - assemble\n");
|
|
dprintf("BC[<bp>] - clear breakpoint(s)\n");
|
|
dprintf("BD[<bp>] - disable breakpoint(s)\n");
|
|
dprintf("BE[<bp>] - enable breakpoint(s)\n");
|
|
dprintf("BL - list breakpoints\n");
|
|
dprintf("[thread]BP[#] <address> - set breakpoint\n");
|
|
dprintf("C <range> <address> [passes] [command] - compare\n");
|
|
dprintf("D[type][<range>] - dump memory\n");
|
|
dprintf("DL[B] <address> <maxcount> <size> - dump linked list\n");
|
|
dprintf("#[processor] DT [-n|y] [[mod!]name] [[-n|y]fields]\n");
|
|
dprintf(" [address] [-l list] [-a[]|c|i|o|r[#]|v] - \n");
|
|
dprintf(" dump using type information\n");
|
|
dprintf("E[type] <address> [<list>] - enter\n");
|
|
dprintf("F <range> <list> - fill\n");
|
|
dprintf("[thread]G [=<address> [<address>...]] - go\n");
|
|
dprintf("[thread]GH [=<address> [<address>...]] - "
|
|
"go with exception handled\n");
|
|
dprintf("[thread]GN [=<address> [<address>...]] - "
|
|
"go with exception not handled\n");
|
|
dprintf("J<expr> [']cmd1['];[']cmd2['] - conditional execution\n");
|
|
dprintf("[thread|processor]K[B] <count> - stacktrace\n");
|
|
dprintf("KD [<count>] - stack trace with raw data\n");
|
|
dprintf("[thread|processor] KV [ <count> | =<reg> ] - "
|
|
"stacktrace with FPO data\n");
|
|
dprintf("L{+|-}[l|o|s|t|*] - Control source options\n");
|
|
dprintf("LD [<module>] - refresh module information\n");
|
|
dprintf("LM[k|l|u|v] - list modules\n");
|
|
dprintf("LN <expr> - list nearest symbols\n");
|
|
dprintf("LS[.] [<first>][,<count>] - List source file lines\n");
|
|
dprintf("LSA <addr>[,<first>][,<count>] - "
|
|
"List source file lines at addr\n");
|
|
dprintf("LSC - Show current source file and line\n");
|
|
dprintf("LSF[-] <file> - Load or unload a source file for browsing\n");
|
|
dprintf("M <range> <address> - move\n");
|
|
dprintf("N [<radix>] - set / show radix\n");
|
|
dprintf("[thread]P[R] [=<addr>] [<value>] - program step\n");
|
|
dprintf("Q - quit\n");
|
|
|
|
dprintf("\n");
|
|
GetInput("Hit Enter...", Buf, DIMA(Buf) - 1, GETIN_DEFAULT);
|
|
dprintf("\n");
|
|
|
|
dprintf("[thread|processor]R[F][L][M <expr>] [[<reg> [= <expr>]]] - "
|
|
"reg/flag\n");
|
|
dprintf("Rm[?] [<expr>] - Control prompt register output mask\n");
|
|
dprintf("S <range> <list> - search\n");
|
|
dprintf("SQ[e|d] - set quiet mode\n");
|
|
dprintf("SS <n | a | w> - set symbol suffix\n");
|
|
dprintf("SX [{e|d|i|n} [-c \"Cmd1\"] [-c2 \"Cmd2\"] [-h] "
|
|
"{Exception|Event|*}] - event filter\n");
|
|
dprintf("[thread]T[R] [=<address>] [<expr>] - trace\n");
|
|
dprintf("U [<range>] - unassemble\n");
|
|
dprintf("vercommand - show the debuggee command line\n");
|
|
dprintf("version - show debuggee and debugger version\n");
|
|
dprintf("vertarget - show debuggee version\n");
|
|
dprintf("X [<*|module>!]<*|symbol> - view symbols\n");
|
|
dprintf("<commands>; [processor] z(<expression>) - do while true\n");
|
|
dprintf("~ - list threads status\n");
|
|
dprintf("~#s - set default thread\n");
|
|
dprintf("~[.|#|*|ddd]f - freeze thread\n");
|
|
dprintf("~[.|#|*|ddd]u - unfreeze thread\n");
|
|
dprintf("~[.|#|ddd]k[expr] - backtrace stack\n");
|
|
dprintf("| - list processes status\n");
|
|
dprintf("|#s - set default process\n");
|
|
dprintf("|#<command> - default process override\n");
|
|
dprintf("? <expr> - display expression\n");
|
|
dprintf("? - command help\n");
|
|
dprintf("#<string> [address] - search for a string in the dissasembly\n");
|
|
dprintf("$< <filename> - take input from a command file\n");
|
|
dprintf("<Enter> - repeat previous command\n");
|
|
dprintf("; - command separator\n");
|
|
dprintf("*|$ - comment mark\n");
|
|
|
|
dprintf("\n");
|
|
GetInput("Hit Enter...", Buf, DIMA(Buf) - 1, GETIN_DEFAULT);
|
|
dprintf("\n");
|
|
|
|
dprintf("<expr> unary ops: + - not by wo dwo qwo poi hi low\n");
|
|
dprintf(" binary ops: + - * / mod(%%) and(&) xor(^) or(|)\n");
|
|
dprintf(" comparisons: == (=) < > !=\n");
|
|
dprintf(" operands: number in current radix, "
|
|
"public symbol, <reg>\n");
|
|
dprintf("<type> : b (byte), w (word), d[s] "
|
|
"(doubleword [with symbols]),\n");
|
|
dprintf(" a (ascii), c (dword and Char), u (unicode), l (list)\n");
|
|
dprintf(" f (float), D (double), s|S (ascii/unicode string)\n");
|
|
dprintf(" q (quadword)\n");
|
|
dprintf("<pattern> : [(nt | <dll-name>)!]<var-name> "
|
|
"(<var-name> can include ? and *)\n");
|
|
dprintf("<event> : ct, et, ld, av, cc "
|
|
"(see documentation for full list)\n");
|
|
dprintf("<radix> : 8, 10, 16\n");
|
|
dprintf("<reg> : $u0-$u9, $ea, $exp, $ra, $p\n");
|
|
dprintf("<addr> : %%<32-bit address>\n");
|
|
dprintf("<range> : <address> <address>\n");
|
|
dprintf(" : <address> L <count>\n");
|
|
dprintf("<list> : <byte> [<byte> ...]\n");
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
dprintf("\n");
|
|
dprintf("Kernel-mode options:\n");
|
|
dprintf("~<processor>s - change current processor\n");
|
|
dprintf("I<b|w|d> <port> - read I/O port\n");
|
|
dprintf("O<b|w|d> <port> <expr> - write I/O\n");
|
|
dprintf("RDMSR <MSR> - read MSR\n");
|
|
dprintf("SO [<options>] - set kernel debugging options\n");
|
|
dprintf("UX [<address>] - disassemble X86 BIOS code\n");
|
|
dprintf("WRMSR <MSR> - write MSR\n");
|
|
dprintf(".cache [size] - set vmem cache size\n");
|
|
dprintf(".reboot - reboot target machine\n");
|
|
}
|
|
|
|
switch(g_Machine->m_ExecTypes[0])
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
dprintf("\n");
|
|
dprintf("x86 options:\n");
|
|
dprintf("BA[#] <e|r|w|i><1|2|4> <addr> - addr bp\n");
|
|
dprintf("DG <selector> - dump selector\n");
|
|
dprintf("KB = <base> <stack> <ip> - stacktrace from specific state\n");
|
|
dprintf("<reg> : [e]ax, [e]bx, [e]cx, [e]dx, [e]si, [e]di, "
|
|
"[e]bp, [e]sp, [e]ip, [e]fl,\n");
|
|
dprintf(" al, ah, bl, bh, cl, ch, dl, dh, "
|
|
"cs, ds, es, fs, gs, ss\n");
|
|
dprintf(" dr0, dr1, dr2, dr3, dr6, dr7\n");
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
dprintf(" cr0, cr2, cr3, cr4\n");
|
|
dprintf(" gdtr, gdtl, idtr, idtl, tr, ldtr\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf(" fpcw, fpsw, fptw, st0-st7, mm0-mm7\n");
|
|
}
|
|
dprintf(" xmm0-xmm7\n");
|
|
dprintf("<flag> : iopl, of, df, if, tf, sf, zf, af, pf, cf\n");
|
|
dprintf("<addr> : #<16-bit protect-mode [seg:]address>,\n");
|
|
dprintf(" &<V86-mode [seg:]address>\n");
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
dprintf("\n");
|
|
dprintf("IA64 options:\n");
|
|
dprintf("BA[#] <r|w><1|2|4|8> <addr> - addr bp\n");
|
|
dprintf("<reg> : r2-r31, f2-f127, gp, sp, intnats, preds, brrp, brs0-brs4, brt0, brt1,\n");
|
|
dprintf(" dbi0-dbi7, dbd0-dbd7, kpfc0-kpfc7, kpfd0-kpfd7, h16-h31, unat, lc, ec,\n");
|
|
dprintf(" ccv, dcr, pfs, bsp, bspstore, rsc, rnat, ipsr, iip, ifs, kr0-kr7, itc,\n");
|
|
dprintf(" itm, iva, pta, isr, ifa, itir, iipa, iim, iha, lid, ivr, tpr, eoi,\n");
|
|
dprintf(" irr0-irr3, itv, pmv, lrr0, lrr1, cmcv, rr0-rr7, pkr0-pkr15, tri0-tri7,\n");
|
|
dprintf(" trd0-trd7\n");
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
dprintf("\n");
|
|
dprintf("x86-64 options:\n");
|
|
dprintf("BA[#] <e|r|w|i><1|2|4> <addr> - addr bp\n");
|
|
dprintf("DG <selector> - dump selector\n");
|
|
dprintf("KB = <base> <stack> <ip> - stacktrace from specific state\n");
|
|
dprintf("<reg> : [r|e]ax, [r|e]bx, [r|e]cx, [r|e]dx, [r|e]si, [r|e]di, "
|
|
"[r|e]bp, [r|e]sp, [r|e]ip, [e]fl,\n");
|
|
dprintf(" r8-r15 with b/w/d subregisters\n");
|
|
dprintf(" al, ah, bl, bh, cl, ch, dl, dh, "
|
|
"cs, ds, es, fs, gs, ss\n");
|
|
dprintf(" sil, dil, bpl, spl\n");
|
|
dprintf(" dr0, dr1, dr2, dr3, dr6, dr7\n");
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
dprintf(" cr0, cr2, cr3, cr4\n");
|
|
dprintf(" gdtr, gdtl, idtr, idtl, tr, ldtr\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf(" fpcw, fpsw, fptw, st0-st7, mm0-mm7\n");
|
|
}
|
|
dprintf(" xmm0-xmm15\n");
|
|
dprintf("<flag> : iopl, of, df, if, tf, sf, zf, af, pf, cf\n");
|
|
dprintf("<addr> : #<16-bit protect-mode [seg:]address>,\n");
|
|
dprintf(" &<V86-mode [seg:]address>\n");
|
|
break;
|
|
|
|
}
|
|
|
|
dprintf("\nOpen debugger.chm for complete debugger documentation\n\n");
|
|
}
|
|
|
|
|
|
/*** ProcessCommands - high-level command processor
|
|
*
|
|
* Purpose:
|
|
* If no command string remains, the user is prompted to
|
|
* input one. Once input, this routine parses the string
|
|
* into commands and their operands. Error checking is done
|
|
* on both commands and operands. Multiple commands may be
|
|
* input by separating them with semicolons. Once a command
|
|
* is parsefd, the appropriate routine (type fnXXXXX) is called
|
|
* to execute the command.
|
|
*
|
|
* Input:
|
|
* g_CurCmd = pointer to the next command in the string
|
|
*
|
|
* Output:
|
|
* None.
|
|
*
|
|
* Exceptions:
|
|
* error exit: SYNTAX - command type or operand error
|
|
* normal exit: termination on 'q' command
|
|
*
|
|
*************************************************************************/
|
|
|
|
HRESULT
|
|
ProcessCommands(DebugClient* Client, BOOL Nested)
|
|
{
|
|
char Ch;
|
|
ADDR Addr1;
|
|
ADDR Addr2;
|
|
ULONG64 Value1;
|
|
ULONG64 Value2;
|
|
ULONG Count;
|
|
PSTR SavedCurCmd;
|
|
ProcessInfo* ProcessPrevious = NULL;
|
|
BOOL ParseProcess = FALSE;
|
|
HRESULT Status = S_FALSE;
|
|
PCROSS_PLATFORM_CONTEXT ScopeContext;
|
|
DEBUG_SCOPE_STATE SaveCurrCtxtState;
|
|
ContextSave* PushContext;
|
|
PSTR Scan, Start;
|
|
|
|
if (g_Process == NULL ||
|
|
g_Thread == NULL)
|
|
{
|
|
WarnOut("WARNING: The debugger does not have a current "
|
|
"process or thread\n");
|
|
WarnOut("WARNING: Many commands will not work\n");
|
|
}
|
|
|
|
if (!Nested)
|
|
{
|
|
g_SwitchedProcs = FALSE;
|
|
}
|
|
|
|
do
|
|
{
|
|
Ch = *g_CurCmd++;
|
|
if (Ch == '\0' ||
|
|
(Ch == ';' && (g_EngStatus & ENG_STATUS_USER_INTERRUPT)))
|
|
{
|
|
if (!Nested)
|
|
{
|
|
g_EngStatus &= ~ENG_STATUS_USER_INTERRUPT;
|
|
g_BreakpointsSuspended = FALSE;
|
|
}
|
|
|
|
Status = S_OK;
|
|
// Back up to terminating character in
|
|
// case command processing is reentered without
|
|
// resetting things.
|
|
g_CurCmd--;
|
|
break;
|
|
}
|
|
|
|
EVALUATE:
|
|
while (Ch == ' ' || Ch == '\t' || Ch == '\r' || Ch == '\n' ||
|
|
Ch == ';')
|
|
{
|
|
Ch = *g_CurCmd++;
|
|
}
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
if (g_Target &&
|
|
g_Target->m_NumProcessors > MAXIMUM_PROCS)
|
|
{
|
|
WarnOut("WARNING: Number of processors corrupted - using 1\n");
|
|
g_Target->m_NumProcessors = 1;
|
|
}
|
|
|
|
if (Ch >= '0' && Ch <= '9')
|
|
{
|
|
if (IS_KERNEL_TRIAGE_DUMP(g_Target))
|
|
{
|
|
ErrOut("Can't switch processors on a Triage dump\n");
|
|
error(SYNTAX);
|
|
}
|
|
|
|
Value1 = 0;
|
|
SavedCurCmd = g_CurCmd;
|
|
while (Ch >= '0' && Ch <= '9')
|
|
{
|
|
Value1 = Value1 * 10 + (Ch - '0');
|
|
Ch = *SavedCurCmd++;
|
|
}
|
|
Ch = (char)tolower(Ch);
|
|
if (Ch == 'r' || Ch == 'k' || Ch == 'z' ||
|
|
(Ch == 'd' && tolower(*SavedCurCmd) == 't'))
|
|
{
|
|
if (g_Target && Value1 < g_Target->m_NumProcessors)
|
|
{
|
|
if (Value1 != g_Target->m_RegContextProcessor)
|
|
{
|
|
SaveSetCurrentProcessorThread(g_Target,
|
|
(ULONG)Value1);
|
|
g_SwitchedProcs = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error(BADRANGE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
g_CurCmd = SavedCurCmd;
|
|
}
|
|
}
|
|
|
|
g_PrefixSymbols = FALSE;
|
|
switch (Ch = (char)tolower(Ch))
|
|
{
|
|
case '?':
|
|
if ((Ch = PeekChar()) == '\0' || Ch == ';')
|
|
{
|
|
CmdHelp();
|
|
}
|
|
else if (*g_CurCmd == '?')
|
|
{
|
|
TypedData Result;
|
|
EvalExpression* Eval;
|
|
|
|
g_CurCmd++;
|
|
Eval = GetEvaluator(DEBUG_EXPR_CPLUSPLUS, FALSE);
|
|
Eval->EvalCurrent(&Result);
|
|
ReleaseEvaluator(Eval);
|
|
Result.OutputTypeAndValue();
|
|
}
|
|
else
|
|
{
|
|
DotFormats(NULL, Client);
|
|
}
|
|
break;
|
|
|
|
case '$':
|
|
if ( *g_CurCmd++ == '<')
|
|
{
|
|
if ((Status =
|
|
ExecuteCommandFile(Client, (PCSTR)g_CurCmd,
|
|
DEBUG_EXECUTE_ECHO)) != S_OK)
|
|
{
|
|
ErrOut("Command file execution failed, %s\n "
|
|
"\"%s\"\n", FormatStatusCode(Status),
|
|
FormatStatus(Status));
|
|
}
|
|
}
|
|
*g_CurCmd = 0;
|
|
break;
|
|
|
|
case '~':
|
|
if (IS_USER_TARGET(g_Target))
|
|
{
|
|
if (Nested)
|
|
{
|
|
ErrOut("Ignoring recursive thread command\n");
|
|
break;
|
|
}
|
|
|
|
ParseThreadCmds(Client);
|
|
}
|
|
else
|
|
{
|
|
ParseProcessorCommands();
|
|
}
|
|
break;
|
|
|
|
case '|':
|
|
if (*g_CurCmd == '|')
|
|
{
|
|
g_CurCmd++;
|
|
ParseSystemCommands();
|
|
break;
|
|
}
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!ParseProcess)
|
|
{
|
|
ParseProcess = TRUE;
|
|
ProcessPrevious = g_Process;
|
|
}
|
|
ParseProcessCmds();
|
|
if (!*g_CurCmd)
|
|
{
|
|
ParseProcess = FALSE;
|
|
}
|
|
else
|
|
{
|
|
Ch = *g_CurCmd++;
|
|
goto EVALUATE;
|
|
}
|
|
break;
|
|
|
|
case '.':
|
|
SavedCurCmd = g_CurCmd;
|
|
if (!DotCommand(Client, FALSE))
|
|
{
|
|
g_CurCmd = SavedCurCmd;
|
|
ParseBangCmd(Client, TRUE);
|
|
}
|
|
break;
|
|
|
|
case '!':
|
|
SavedCurCmd = g_CurCmd;
|
|
if (!DotCommand(Client, TRUE))
|
|
{
|
|
g_CurCmd = SavedCurCmd;
|
|
ParseBangCmd(Client, FALSE);
|
|
}
|
|
break;
|
|
|
|
case '*':
|
|
while (*g_CurCmd != '\0')
|
|
{
|
|
g_CurCmd++;
|
|
}
|
|
break;
|
|
|
|
case '#':
|
|
ParseInstrGrep();
|
|
break;
|
|
|
|
case 'a':
|
|
//
|
|
// Alias command or just default to pre-existing
|
|
// assemble command.
|
|
//
|
|
Ch = *g_CurCmd++;
|
|
switch (tolower(Ch))
|
|
{
|
|
// Alias list
|
|
case 'l':
|
|
ListAliases();
|
|
break;
|
|
|
|
// Alias set
|
|
case 's':
|
|
ParseSetAlias();
|
|
break;
|
|
|
|
// Alias delete
|
|
case 'd':
|
|
ParseDeleteAlias();
|
|
break;
|
|
|
|
// Pre-existing assemble command
|
|
default:
|
|
g_CurCmd--;
|
|
ParseAssemble();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'b':
|
|
Ch = *g_CurCmd++;
|
|
Ch = (char)tolower(Ch);
|
|
|
|
if (!g_Target || !g_Target->m_DynamicEvents)
|
|
{
|
|
error(TARGETNOTSUP);
|
|
}
|
|
|
|
switch(Ch)
|
|
{
|
|
case 'p':
|
|
case 'u':
|
|
case 'a':
|
|
case 'i':
|
|
case 'w':
|
|
case 'm':
|
|
ParseBpCmd(Client, Ch, NULL);
|
|
break;
|
|
|
|
case 'c':
|
|
case 'd':
|
|
case 'e':
|
|
Value1 = GetIdList(TRUE);
|
|
ChangeBreakpointState(Client, g_Process,
|
|
(ULONG)Value1, Ch);
|
|
break;
|
|
|
|
case 'l':
|
|
if (PeekChar() != ';' && *g_CurCmd)
|
|
{
|
|
Value1 = GetIdList(TRUE);
|
|
}
|
|
else
|
|
{
|
|
Value1 = ALL_ID_LIST;
|
|
}
|
|
ListBreakpoints(Client, g_Process, (ULONG)Value1);
|
|
break;
|
|
|
|
default:
|
|
error(SYNTAX);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'c':
|
|
GetRange(&Addr1, &Value2, 1, SEGREG_DATA,
|
|
DEFAULT_RANGE_LIMIT);
|
|
GetAddrExpression(SEGREG_DATA, &Addr2);
|
|
CompareTargetMemory(&Addr1, (ULONG)Value2, &Addr2);
|
|
break;
|
|
|
|
case 'd':
|
|
ParseDumpCommand();
|
|
break;
|
|
case 'e':
|
|
ParseEnterCommand();
|
|
break;
|
|
|
|
case 'f':
|
|
ParseFillMemory();
|
|
break;
|
|
|
|
case 'g':
|
|
ParseGoCmd(NULL, FALSE);
|
|
break;
|
|
|
|
case 'i':
|
|
Ch = (char)tolower(*g_CurCmd);
|
|
g_CurCmd++;
|
|
if (Ch != 'b' && Ch != 'w' && Ch != 'd')
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
if (IS_USER_TARGET(g_Target) || IS_DUMP_TARGET(g_Target))
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
|
|
InputIo((ULONG)GetExpression(), Ch);
|
|
break;
|
|
|
|
case 'j':
|
|
if (GetExpression())
|
|
{
|
|
Scan = g_CurCmd;
|
|
|
|
// Find a semicolon or a quote
|
|
|
|
while (*Scan && *Scan != ';' && *Scan != '\'')
|
|
{
|
|
Scan++;
|
|
}
|
|
if (*Scan == ';')
|
|
{
|
|
*Scan = 0;
|
|
}
|
|
else if (*Scan)
|
|
{
|
|
*Scan = ' ';
|
|
// Find the closing quote
|
|
while (*Scan && *Scan != '\'')
|
|
{
|
|
Scan++;
|
|
}
|
|
*Scan = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Start = Scan = g_CurCmd;
|
|
|
|
// Find a semicolon or a quote
|
|
|
|
while (*Scan && *Scan != ';' && *Scan != '\'')
|
|
{
|
|
Scan++;
|
|
}
|
|
if (*Scan == ';')
|
|
{
|
|
Start = ++Scan;
|
|
}
|
|
else if (*Scan)
|
|
{
|
|
Scan++;
|
|
while (*Scan && *Scan++ != '\'')
|
|
{
|
|
// Empty.
|
|
}
|
|
while (*Scan && (*Scan == ';' || *Scan == ' '))
|
|
{
|
|
Scan++;
|
|
}
|
|
Start = Scan;
|
|
}
|
|
while (*Scan && *Scan != ';' && *Scan != '\'')
|
|
{
|
|
Scan++;
|
|
}
|
|
if (*Scan == ';')
|
|
{
|
|
*Scan = 0;
|
|
}
|
|
else if (*Scan)
|
|
{
|
|
*Scan = ' ';
|
|
// Find the closing quote
|
|
while (*Scan && *Scan != '\'')
|
|
{
|
|
Scan++;
|
|
}
|
|
*Scan = 0;
|
|
}
|
|
g_CurCmd = Start;
|
|
}
|
|
Ch = *g_CurCmd++;
|
|
goto EVALUATE;
|
|
|
|
case 'k':
|
|
if (IS_LOCAL_KERNEL_TARGET(g_Target))
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
if (!IS_CUR_CONTEXT_ACCESSIBLE())
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
|
|
SaveCurrCtxtState = g_ScopeBuffer.State;
|
|
if (g_SwitchedProcs)
|
|
{
|
|
g_ScopeBuffer.State = ScopeDefault;
|
|
}
|
|
|
|
if (ScopeContext = GetCurrentScopeContext())
|
|
{
|
|
PushContext = g_Machine->PushContext(ScopeContext);
|
|
}
|
|
|
|
__try
|
|
{
|
|
ULONG64 Frame, Stack, Instr;
|
|
ULONG PtrDef;
|
|
ULONG TraceFlags;
|
|
|
|
Count = g_DefaultStackTraceDepth;
|
|
ParseStackTrace(&TraceFlags, &Frame, &Stack, &Instr,
|
|
&Count, &PtrDef);
|
|
DoStackTrace(Client, Frame, Stack, Instr, PtrDef,
|
|
Count, TraceFlags);
|
|
}
|
|
__except(CommandExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
// Stop command processing.
|
|
*g_CurCmd = 0;
|
|
}
|
|
|
|
if (ScopeContext)
|
|
{
|
|
g_Machine->PopContext(PushContext);
|
|
}
|
|
|
|
if (g_SwitchedProcs)
|
|
{
|
|
RestoreCurrentProcessorThread(g_Target);
|
|
g_SwitchedProcs = FALSE;
|
|
g_ScopeBuffer.State = SaveCurrCtxtState;
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
Ch = (char)tolower(*g_CurCmd);
|
|
if (Ch == 'n')
|
|
{
|
|
g_CurCmd++;
|
|
|
|
if (!g_Machine || !g_Process)
|
|
{
|
|
error(BADTHREAD);
|
|
}
|
|
|
|
if ((Ch = PeekChar()) != '\0' && Ch != ';')
|
|
{
|
|
GetAddrExpression(SEGREG_CODE, &Addr1);
|
|
}
|
|
else
|
|
{
|
|
ScopeContext = GetCurrentScopeContext();
|
|
if (ScopeContext)
|
|
{
|
|
PushContext = g_Machine->PushContext(ScopeContext);
|
|
}
|
|
g_Machine->GetPC(&Addr1);
|
|
if (ScopeContext)
|
|
{
|
|
g_Machine->PopContext(PushContext);
|
|
}
|
|
}
|
|
|
|
ListNearSymbols(Flat(Addr1));
|
|
}
|
|
else if (Ch == '+' || Ch == '-')
|
|
{
|
|
g_CurCmd++;
|
|
ParseSrcOptCmd(Ch);
|
|
}
|
|
else if (Ch == 's')
|
|
{
|
|
g_CurCmd++;
|
|
Ch = (char)tolower(*g_CurCmd);
|
|
if (Ch == 'f')
|
|
{
|
|
g_CurCmd++;
|
|
ParseSrcLoadCmd();
|
|
}
|
|
else if (Ch == 'p')
|
|
{
|
|
g_CurCmd++;
|
|
ParseOciSrcCmd();
|
|
}
|
|
else
|
|
{
|
|
ParseSrcListCmd(Ch);
|
|
}
|
|
}
|
|
else if (Ch == 'm')
|
|
{
|
|
ParseDumpModuleTable();
|
|
}
|
|
else if (Ch == 'd')
|
|
{
|
|
g_CurCmd++;
|
|
ParseLoadModules();
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
GetRange(&Addr1, &Value2, 1, SEGREG_DATA,
|
|
DEFAULT_RANGE_LIMIT);
|
|
GetAddrExpression(SEGREG_DATA, &Addr2);
|
|
MoveTargetMemory(&Addr1, (ULONG)Value2, &Addr2);
|
|
break;
|
|
|
|
case 'n':
|
|
if ((Ch = PeekChar()) != '\0' && Ch != ';')
|
|
{
|
|
if (Ch == '8')
|
|
{
|
|
g_CurCmd++;
|
|
g_DefaultRadix = 8;
|
|
}
|
|
else if (Ch == '1')
|
|
{
|
|
Ch = *++g_CurCmd;
|
|
if (Ch == '0' || Ch == '6')
|
|
{
|
|
g_CurCmd++;
|
|
g_DefaultRadix = 10 + Ch - '0';
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
NotifyChangeEngineState(DEBUG_CES_RADIX, g_DefaultRadix,
|
|
TRUE);
|
|
}
|
|
dprintf("base is %ld\n", g_DefaultRadix);
|
|
break;
|
|
|
|
case 'o':
|
|
Ch = (char)tolower(*g_CurCmd);
|
|
g_CurCmd++;
|
|
if (Ch == 'b')
|
|
{
|
|
Value2 = 1;
|
|
}
|
|
else if (Ch == 'w')
|
|
{
|
|
Value2 = 2;
|
|
}
|
|
else if (Ch == 'd')
|
|
{
|
|
Value2 = 4;
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
if (IS_USER_TARGET(g_Target) || IS_DUMP_TARGET(g_Target))
|
|
{
|
|
error(SESSIONNOTSUP);
|
|
}
|
|
|
|
Value1 = GetExpression();
|
|
Value2 = HexValue((ULONG)Value2);
|
|
OutputIo((ULONG)Value1, (ULONG)Value2, Ch);
|
|
break;
|
|
|
|
case 'w':
|
|
case 'p':
|
|
case 't':
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
if (Ch == 'w' &&
|
|
tolower(g_CurCmd[0]) == 'r' &&
|
|
tolower(g_CurCmd[1]) == 'm' &&
|
|
tolower(g_CurCmd[2]) == 's' &&
|
|
tolower(g_CurCmd[3]) == 'r')
|
|
{
|
|
g_CurCmd +=4;
|
|
Value1 = GetExpression();
|
|
if (g_Target->WriteMsr((ULONG)Value1,
|
|
HexValue(8)) != S_OK)
|
|
{
|
|
ErrOut ("no such msr\n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
ParseStepTrace(NULL, FALSE, Ch);
|
|
break;
|
|
|
|
case 'q':
|
|
char QuitArgument;
|
|
BOOL ForceQuit;
|
|
|
|
ForceQuit = FALSE;
|
|
QuitArgument = (char)tolower(PeekChar());
|
|
if (QuitArgument == 'q')
|
|
{
|
|
ForceQuit = TRUE;
|
|
g_CurCmd++;
|
|
QuitArgument = (char)tolower(PeekChar());
|
|
}
|
|
if ((IS_LIVE_USER_TARGET(g_Target) && QuitArgument == 'd') ||
|
|
QuitArgument == 'k')
|
|
{
|
|
g_CurCmd++;
|
|
}
|
|
if (PeekChar() != 0)
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
if (!ForceQuit &&
|
|
Client != NULL &&
|
|
(Client->m_Flags & CLIENT_REMOTE))
|
|
{
|
|
ErrOut("Exit a remote ntsd/cdb client "
|
|
"with Ctrl-b<enter>.\n"
|
|
"Exit a remote windbg client "
|
|
"with File.Exit (Alt-F4).\n"
|
|
"If you really want the server "
|
|
"to quit use 'qq'.\n");
|
|
break;
|
|
}
|
|
|
|
// Detach if requested.
|
|
if (QuitArgument == 'd')
|
|
{
|
|
DBG_ASSERT(IS_LIVE_USER_TARGET(g_Target));
|
|
|
|
// If detach isn't supported warn the user
|
|
// and abort the quit.
|
|
if (((LiveUserTargetInfo*)g_Target)->m_Services->
|
|
DetachProcess(0) != S_OK)
|
|
{
|
|
ErrOut("The system doesn't support detach\n");
|
|
break;
|
|
}
|
|
|
|
if (g_SessionThread &&
|
|
GetCurrentThreadId() != g_SessionThread)
|
|
{
|
|
ErrOut("Detach can only be done by the server\n");
|
|
break;
|
|
}
|
|
|
|
HRESULT DetachStatus;
|
|
|
|
if (FAILED(DetachStatus = DetachProcesses()))
|
|
{
|
|
ErrOut("Detach failed, 0x%X\n", DetachStatus);
|
|
}
|
|
}
|
|
else if ((g_GlobalProcOptions &
|
|
DEBUG_PROCESS_DETACH_ON_EXIT) ||
|
|
(g_Target &&
|
|
(g_Target->m_AllProcessFlags & ENG_PROC_EXAMINED)))
|
|
{
|
|
HRESULT PrepareStatus;
|
|
|
|
if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
error(NOTSECURE);
|
|
}
|
|
|
|
// We need to restart the program before we
|
|
// quit so that it's put back in a running state.
|
|
PrepareStatus = PrepareForSeparation();
|
|
if (PrepareStatus != S_OK)
|
|
{
|
|
ErrOut("Unable to prepare for detach, %s\n \"%s\"",
|
|
FormatStatusCode(PrepareStatus),
|
|
FormatStatus(PrepareStatus));
|
|
break;
|
|
}
|
|
}
|
|
|
|
dprintf("quit:\n");
|
|
|
|
// Force engine into a no-debuggee state
|
|
// to indicate quit.
|
|
g_CmdState = 'q';
|
|
g_EngStatus |= ENG_STATUS_STOP_SESSION;
|
|
NotifyChangeEngineState(DEBUG_CES_EXECUTION_STATUS,
|
|
DEBUG_STATUS_NO_DEBUGGEE, TRUE);
|
|
break;
|
|
|
|
case 'r':
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
if (tolower(g_CurCmd[0]) == 'd' &&
|
|
tolower(g_CurCmd[1]) == 'm' &&
|
|
tolower(g_CurCmd[2]) == 's' &&
|
|
tolower(g_CurCmd[3]) == 'r')
|
|
{
|
|
g_CurCmd +=4;
|
|
Value1 = GetExpression();
|
|
if (g_Target->ReadMsr((ULONG)Value1, &Value2) == S_OK)
|
|
{
|
|
dprintf("msr[%x] = %08x:%08x\n",
|
|
(ULONG)Value1, (ULONG)(Value2 >> 32),
|
|
(ULONG)Value2);
|
|
}
|
|
else
|
|
{
|
|
ErrOut("no such msr\n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ScopeContext = GetCurrentScopeContext())
|
|
{
|
|
dprintf("Last set context:\n");
|
|
PushContext = g_Machine->PushContext(ScopeContext);
|
|
}
|
|
|
|
__try
|
|
{
|
|
ParseRegCmd();
|
|
}
|
|
__except(CommandExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
// Stop command processing.
|
|
*g_CurCmd = 0;
|
|
}
|
|
|
|
if (ScopeContext)
|
|
{
|
|
g_Machine->PopContext(PushContext);
|
|
}
|
|
if (g_SwitchedProcs)
|
|
{
|
|
RestoreCurrentProcessorThread(g_Target);
|
|
g_SwitchedProcs = FALSE;
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
Ch = (char)tolower(*g_CurCmd);
|
|
|
|
if (Ch == 's')
|
|
{
|
|
g_CurCmd++;
|
|
SetSuffix();
|
|
}
|
|
else if (Ch == 'q')
|
|
{
|
|
g_CurCmd++;
|
|
Ch = (char)tolower(*g_CurCmd);
|
|
if (Ch == 'e')
|
|
{
|
|
g_QuietMode = TRUE;
|
|
g_CurCmd++;
|
|
}
|
|
else if (Ch == 'd')
|
|
{
|
|
g_QuietMode = FALSE;
|
|
g_CurCmd++;
|
|
}
|
|
else
|
|
{
|
|
g_QuietMode = !g_QuietMode;
|
|
}
|
|
dprintf("Quiet mode is %s\n", g_QuietMode ? "ON" : "OFF");
|
|
}
|
|
else if (Ch == 'x')
|
|
{
|
|
g_CurCmd++;
|
|
ParseSetEventFilter(Client);
|
|
}
|
|
else if (IS_KERNEL_TARGET(g_Target) && Ch == 'o')
|
|
{
|
|
Scan = ++g_CurCmd;
|
|
while ((*Scan != '\0') && (*Scan != ';'))
|
|
{
|
|
Scan++;
|
|
}
|
|
Ch = *Scan;
|
|
*Scan = '\0';
|
|
ReadDebugOptions(FALSE, (*g_CurCmd == '\0' ?
|
|
NULL : g_CurCmd));
|
|
*Scan = Ch;
|
|
g_CurCmd = Scan;
|
|
}
|
|
else
|
|
{
|
|
// s, s-w, s-d, s-q.
|
|
ParseSearchMemory();
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
ParseUnassemble();
|
|
break;
|
|
|
|
case 'v':
|
|
Scan = g_CurCmd;
|
|
while (*Scan && !isspace(*Scan) && *Scan != ';')
|
|
{
|
|
Scan++;
|
|
}
|
|
Count = (ULONG)(Scan - g_CurCmd);
|
|
if (Count == 8 &&
|
|
_memicmp(g_CurCmd, "ertarget", Count) == 0)
|
|
{
|
|
g_CurCmd = Scan;
|
|
g_Target->OutputVersion();
|
|
}
|
|
else if (Count == 6 &&
|
|
_memicmp(g_CurCmd, "ersion", Count) == 0)
|
|
{
|
|
g_CurCmd = Scan;
|
|
// Print target version, then debugger version.
|
|
g_Target->OutputVersion();
|
|
OutputVersionInformation(Client);
|
|
}
|
|
else if (Count == 9 &&
|
|
_memicmp(g_CurCmd, "ercommand", Count) == 0)
|
|
{
|
|
g_CurCmd = Scan;
|
|
dprintf("command line: '%s'\n", GetCommandLine());
|
|
}
|
|
else
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
break;
|
|
|
|
case 'x':
|
|
ParseExamine();
|
|
break;
|
|
|
|
case ';':
|
|
case '\0':
|
|
g_CurCmd--;
|
|
break;
|
|
|
|
case 'z':
|
|
|
|
// Works like do{ cmds }while(Cond);
|
|
// Eg. p;z(eax<2);
|
|
|
|
if (CheckUserInterrupt())
|
|
{
|
|
// Eat the expression also to prevent
|
|
// spurious extra character errors.
|
|
GetExpression();
|
|
g_RedoCount = 0;
|
|
break;
|
|
}
|
|
if (GetExpression())
|
|
{
|
|
g_CurCmd = g_CommandStart;
|
|
++g_RedoCount;
|
|
dprintf("redo [%d] %s\n", g_RedoCount, g_CurCmd );
|
|
FlushCallbacks();
|
|
Ch = *g_CurCmd++;
|
|
goto EVALUATE;
|
|
}
|
|
else
|
|
{
|
|
g_RedoCount = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error(SYNTAX);
|
|
break;
|
|
}
|
|
do
|
|
{
|
|
Ch = *g_CurCmd++;
|
|
} while (Ch == ' ' || Ch == '\t' || Ch == '\n' || Ch == '\r');
|
|
if (Ch != ';' && Ch != '\0')
|
|
{
|
|
error(EXTRACHARS);
|
|
}
|
|
g_CurCmd--;
|
|
}
|
|
while (g_CmdState == 'c');
|
|
|
|
if (Status == S_FALSE)
|
|
{
|
|
Scan = g_CurCmd;
|
|
|
|
// We switched to a non-'c' cmdState so the
|
|
// loop was exited. Check and see if we're
|
|
// also at the end of the command as that will
|
|
// take precedence in what the return value is.
|
|
while (*Scan == ' ' || *Scan == '\t' || *Scan == ';')
|
|
{
|
|
Scan++;
|
|
}
|
|
|
|
Status = *Scan ? S_FALSE : S_OK;
|
|
}
|
|
|
|
g_EngStatus &= ~ENG_STATUS_USER_INTERRUPT;
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
ProcessCommandsAndCatch(DebugClient* Client)
|
|
{
|
|
HRESULT Status;
|
|
|
|
__try
|
|
{
|
|
Status = ProcessCommands(Client, FALSE);
|
|
}
|
|
__except(CommandExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
g_CurCmd = g_CommandStart;
|
|
*g_CurCmd = '\0';
|
|
g_LastCommand[0] = '\0';
|
|
Status = HR_PROCESS_EXCEPTION;
|
|
g_EngStatus &= ~ENG_STATUS_USER_INTERRUPT;
|
|
}
|
|
|
|
return Status;
|
|
}
|