/*** ntcmd.cpp - command processor for NT debugger * * Copyright 1990-2001, Microsoft Corporation * * Purpose: * To determine if the command processor should be invoked, and * if so, to parse and execute those commands entered. * *************************************************************************/ #include "ntsdp.hpp" #include #include ULONG g_X86BiosBaseAddress; #define CRASH_BUGCHECK_CODE 0xDEADDEAD API_VERSION g_NtsdApiVersion = { BUILD_MAJOR_VERSION, BUILD_MINOR_VERSION, API_VERSION_NUMBER, 0 }; API_VERSION g_DbgHelpAV; // 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 input 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; BOOL DotCommand(DebugClient* Client); void EvaluateExp(BOOL Verbose); void SetSuffix(void); void StackTrace( PSTR arg ); VOID DumpExr( ULONG64 Exr ); void CallBugCheckExtension(DebugClient* Client) { HRESULT Status = E_FAIL; if (Client == NULL) { // Use the session client. for (Client = g_Clients; Client != NULL; Client = Client->m_Next) { if ((Client->m_Flags & (CLIENT_REMOTE | CLIENT_PRIMARY)) == CLIENT_PRIMARY) { break; } } if (Client == NULL) { Client = g_Clients; } } if (Client != NULL) { char ExtName[32]; // Extension name has to be in writable memory as it // gets lower-cased. strcpy(ExtName, "AnalyzeBugCheck"); // 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"); } } BOOL dotFrame( ) { PDEBUG_STACK_FRAME pStackFrame; ULONG Traced = 0, Frame = 0; if (PeekChar()) { Frame = (ULONG) GetExpression(); } else { return TRUE; } pStackFrame = (PDEBUG_STACK_FRAME) malloc(sizeof(DEBUG_STACK_FRAME) * (Frame + 1)); if (!pStackFrame) { return FALSE; } Traced = StackTrace(0, 0, 0, pStackFrame, Frame + 1, 0, 0, FALSE); if (Traced <= Frame) { return FALSE; } // Do not change the previous context SetCurrentScope(&pStackFrame[Frame], NULL, 0); return TRUE; } VOID HandleBPWithStatus( VOID ) { ULONG Status = (ULONG)g_Machine->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; } return; } /*** InitNtCmd - one-time debugger initialization * * Purpose: * To perform the one-time initialization of global * variables used in the debugger. * * Input: * None. * * Output: * None. * * Exceptions: * None. * *************************************************************************/ HRESULT InitNtCmd(DebugClient* Client) { HRESULT Status; if ((Status = InitReg()) != S_OK) { return Status; } g_DbgHelpAV = *ImagehlpApiVersionEx(&g_NtsdApiVersion); if (g_DbgHelpAV.Revision < g_NtsdApiVersion.Revision) { // // bad version match // if ((g_EngOptions & DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION) == 0) { ErrOut("dbghelp.dll has a version mismatch with the debugger\n\n"); OutputVersionInformation(Client); return E_FAIL; } else { WarnOut( "dbghelp.dll has a version mismatch " "with the debugger\n\n" ); } } g_LastCommand[0] = '\0'; return S_OK; } #define MAX_PROMPT 32 HRESULT GetPromptText(PSTR Buffer, ULONG BufferSize, PULONG TextSize) { char Prompt[MAX_PROMPT]; PSTR Text = Prompt; if (IS_LOCAL_KERNEL_TARGET()) { strcpy(Text, "lkd"); Text += 3; } else if (IS_KERNEL_TARGET()) { if (g_X86InVm86) { strcpy(Text, "vm"); Text += 2; } else if (g_X86InCode16) { strcpy(Text, "16"); Text += 2; } else if (g_TargetMachineType == IMAGE_FILE_MACHINE_AMD64 && !g_Amd64InCode64) { strcpy(Text, "32"); Text += 2; } if (g_CurrentProcess == NULL || g_CurrentProcess->CurrentThread == NULL || ((IS_KERNEL_FULL_DUMP() || IS_KERNEL_SUMMARY_DUMP()) && KdDebuggerData.KiProcessorBlock == 0)) { strcpy(Text, "?: kd"); Text += 5; } else if (g_TargetNumberProcessors > 1) { sprintf(Text, "%d: kd", CURRENT_PROC); Text += strlen(Text); } else { strcpy(Text, "kd"); Text += 2; } } else if (IS_USER_TARGET()) { if (g_CurrentProcess != NULL) { sprintf(Text, "%1ld", g_CurrentProcess->UserId); Text += strlen(Text); *Text++ = ':'; if (g_CurrentProcess->CurrentThread != NULL) { sprintf(Text, "%03ld", g_CurrentProcess->CurrentThread->UserId); Text += strlen(Text); } else { strcpy(Text, "???"); Text += 3; } } else { strcpy(Text, "?:???"); Text += 5; } } else { strcpy(Text, "NoTarget"); Text += 8; } if (g_Machine && g_EffMachine != g_TargetMachineType) { *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) { PDEBUG_IMAGE_INFO Image; PSTR Pattern; CHAR Save; BOOL AnyMatched = FALSE; Pattern = StringValue(STRV_SPACE_IS_SEPARATOR | STRV_TRIM_TRAILING_SPACE | STRV_ESCAPED_CHARACTERS, &Save); _strupr(Pattern); for (Image = g_ProcessHead->ImageHead; Image; Image = Image->Next) { if (MatchPattern(Image->ModuleName, Pattern)) { IMAGEHLP_MODULE64 ModInfo; AnyMatched = TRUE; ModInfo.SizeOfStruct = sizeof(ModInfo); if (!SymGetModuleInfo64(g_CurrentProcess->Handle, Image->BaseOfImage, &ModInfo) || ModInfo.SymType == SymDeferred) { if (!SymLoadModule64(g_CurrentProcess->Handle, NULL, NULL, NULL, Image->BaseOfImage, 0)) { ErrOut("Symbol load for %s failed\n", Image->ModuleName); } else { dprintf("Symbols loaded for %s\n", Image->ModuleName); } } else { dprintf("Symbols already loaded for %s\n", Image->ModuleName); } } if (CheckUserInterrupt()) { break; } } if (!AnyMatched) { WarnOut("No modules matched '%s'\n", Pattern); } *g_CurCmd = Save; } void ParseProcessorCommands(void) { ULONG Proc; if (IS_KERNEL_TRIAGE_DUMP()) { ErrOut("Can't switch processors on a Triage dump\n"); error(SYNTAX); } Proc = 0; while (*g_CurCmd >= '0' && *g_CurCmd <= '9') { Proc = Proc * 10 + (*g_CurCmd - '0'); g_CurCmd++; } *g_CurCmd = 0; if (Proc < g_TargetNumberProcessors) { if (Proc != g_RegContextProcessor) { if (IS_DUMP_TARGET()) { SetCurrentProcessorThread(Proc, FALSE); } else { g_SwitchProcessor = Proc + 1; g_CmdState = 's'; } ResetCurrentScope(); } } else { ErrOut("%d is not a valid processor number\n", Proc); } } /*** CmdHelp - displays the legal commands * * Purpose: * Show user the debuger commands. * * Input: * none * * Output: * None. * *************************************************************************/ 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 [
] - assemble\n"); dprintf("BC[] - clear breakpoint(s)\n"); dprintf("BD[] - disable breakpoint(s)\n"); dprintf("BE[] - enable breakpoint(s)\n"); dprintf("BL - list breakpoints\n"); dprintf("[thread]BP[#]
- set breakpoint\n"); dprintf("C
[passes] [command] - compare\n"); dprintf("D[type][] - dump memory\n"); dprintf("DL[B]
- 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]
[] - enter\n"); dprintf("F - fill\n"); dprintf("[thread]G [=
[
...]] - go\n"); dprintf("[thread]GH [=
[
...]] - " "go with exception handled\n"); dprintf("[thread]GN [=
[
...]] - " "go with exception not handled\n"); dprintf("J [']cmd1['];[']cmd2['] - conditional execution\n"); dprintf("[thread|processor]K[B] - stacktrace\n"); dprintf("KD [] - stack trace with raw data\n"); dprintf("[thread|processor] KV [ | = ] - " "stacktrace with FPO data\n"); dprintf("L{+|-}[l|o|s|t|*] - Control source options\n"); dprintf("LD [] - refresh module information\n"); dprintf("LM[k|l|u|v] - list modules\n"); dprintf("LN - list nearest symbols\n"); dprintf("LS[.] [][,] - List source file lines\n"); dprintf("LSA [,][,] - " "List source file lines at addr\n"); dprintf("LSC - Show current source file and line\n"); dprintf("LSF[-] - Load or unload a source file for browsing\n"); dprintf("M
- move\n"); dprintf("N [] - set / show radix\n"); dprintf("[thread]P[R] [=] [] - program step\n"); dprintf("Q - quit\n"); dprintf("\n"); GetInput("Hit Enter...", buf, sizeof(buf) - 1); dprintf("\n"); dprintf("[thread|processor]R[F][L][M ] [[ [= ]]] - " "reg/flag\n"); dprintf("Rm[?] [] - Control prompt register output mask\n"); dprintf("S - search\n"); dprintf("SQ[e|d] - set quiet mode\n"); dprintf("SS - set symbol suffix\n"); dprintf("SX [e|d|i|n [|*|]] - event filtering\n"); dprintf("[thread]T[R] [=
] [] - trace\n"); dprintf("U [] - unassemble\n"); dprintf("version - show debuggee and debugger version\n"); dprintf("vertarget - show debuggee version\n"); dprintf("X [<*|module>!]<*|symbol> - view symbols\n"); dprintf("; [processor] z() - 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("|# - default process override\n"); dprintf("? - display expression\n"); dprintf("? - command help\n"); dprintf("# [address] - search for a string in the dissasembly\n"); dprintf("$< - take input from a command file\n"); dprintf(" - repeat previous command\n"); dprintf("; - command separator\n"); dprintf("*|$ - comment mark\n"); dprintf("\n"); GetInput("Hit Enter...", buf, sizeof(buf) - 1); dprintf("\n"); dprintf(" 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, \n"); dprintf(" : 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(" : [(nt | )!] " "( can include ? and *)\n"); dprintf(" : ct, et, ld, av, cc (see documentation for full list)\n"); dprintf(" : 8, 10, 16\n"); dprintf(" : $u0-$u9, $ea, $exp, $ra, $p\n"); dprintf(" : %%<32-bit address>\n"); dprintf(" :
\n"); dprintf(" :
L \n"); dprintf(" : [ ...]\n"); if (IS_KERNEL_TARGET()) { dprintf("\n"); dprintf("Kernel-mode options:\n"); dprintf("~s - change current processor\n"); dprintf("I - read I/O port\n"); dprintf("O - write I/O\n"); dprintf("RDMSR - read MSR\n"); dprintf("SO [] - set kernel debugging options\n"); dprintf("UX [
] - disassemble X86 BIOS code\n"); dprintf("WRMSR - write MSR\n"); dprintf(".cache [size] - set vmem cache size\n"); dprintf(".reboot - reboot target machine\n"); } switch(g_EffMachine) { case IMAGE_FILE_MACHINE_I386: dprintf("\n"); dprintf("x86 options:\n"); dprintf("BA[#] <1|2|4> - addr bp\n"); dprintf("DG - dump selector\n"); dprintf("KB = - stacktrace from specific state\n"); dprintf(" : [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()) { 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(" : iopl, of, df, if, tf, sf, zf, af, pf, cf\n"); dprintf(" : #<16-bit protect-mode [seg:]address>,\n"); dprintf(" &\n"); break; case IMAGE_FILE_MACHINE_IA64: dprintf("\n"); dprintf("IA64 options:\n"); dprintf("BA[#] <1|2|4|8> - addr bp\n"); dprintf(" : 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_ALPHA: case IMAGE_FILE_MACHINE_AXP64: dprintf("\n"); dprintf("Alpha options:\n"); dprintf(" : zero, at, v0, a0-a5, t0-t12, s0-s5, fp, gp, sp, ra\n"); dprintf(" fpcr, fir, psr, int0-int5, f0-f31\n"); break; case IMAGE_FILE_MACHINE_AMD64: dprintf("\n"); dprintf("x86-64 options:\n"); dprintf("BA[#] <1|2|4> - addr bp\n"); dprintf("DG - dump selector\n"); dprintf("KB = - stacktrace from specific state\n"); dprintf(" : [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()) { 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(" : iopl, of, df, if, tf, sf, zf, af, pf, cf\n"); dprintf(" : #<16-bit protect-mode [seg:]address>,\n"); dprintf(" &\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) { UCHAR ch; ADDR addr1; ADDR addr2; ULONG64 value1; ULONG64 value2; ULONG count; STACK_TRACE_TYPE traceType; BOOL fLength; UCHAR list[STRLISTSIZE]; ULONG size; PSTR SavedCurCmd; ULONGLONG valueL; PPROCESS_INFO pProcessPrevious = NULL; BOOL parseProcess = FALSE; CHAR buf[MAX_COMMAND]; HRESULT Status = S_FALSE; CROSS_PLATFORM_CONTEXT Context; ULONG State; if (g_CurrentProcess == NULL || g_CurrentProcess->CurrentThread == 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()) { if (g_TargetNumberProcessors > MAXIMUM_PROCESSORS) { WarnOut("WARNING: Number of processors corrupted - using 1\n"); g_TargetNumberProcessors = 1; } if (ch >= '0' && ch <= '9') { if (IS_KERNEL_TRIAGE_DUMP()) { 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 = (UCHAR)tolower(ch); if (ch == 'r' || ch == 'k' || ch == 'z' || (ch == 'd' && tolower(*SavedCurCmd) == 't')) { if (value1 < g_TargetNumberProcessors) { if (value1 != g_RegContextProcessor) { SaveSetCurrentProcessorThread((ULONG)value1); g_SwitchedProcs = TRUE; } } else { error(BADRANGE); } } else { error(SYNTAX); } g_CurCmd = SavedCurCmd; } } g_PrefixSymbols = FALSE; switch (ch = (UCHAR)tolower(ch)) { case '?': if ((ch = PeekChar()) == '\0' || ch == ';') { CmdHelp(); } else { EvaluateExp(FALSE); } break; case '$': if ( *g_CurCmd++ == '<') { ExecuteCommandFile(Client, (PCSTR)g_CurCmd, DEBUG_EXECUTE_ECHO); } *g_CurCmd = 0; break; case '~': if (IS_USER_TARGET()) { if (Nested) { ErrOut("Ignoring recursive thread command\n"); break; } parseThreadCmds(Client); } else { ParseProcessorCommands(); } break; case '|': if (IS_KERNEL_TARGET()) { break; } if (!parseProcess) { parseProcess = TRUE; pProcessPrevious = g_CurrentProcess; } parseProcessCmds(); if (!*g_CurCmd) { parseProcess = FALSE; } else { ch = *g_CurCmd++; goto EVALUATE; } break; case '.': SavedCurCmd = g_CurCmd; strcpy(buf, g_CurCmd); if (!DotCommand(Client)) { g_CurCmd = SavedCurCmd; strcpy(g_CurCmd, buf); fnBangCmd(Client, g_CurCmd, &g_CurCmd, TRUE); } break; case '!': fnBangCmd(Client, g_CurCmd, &g_CurCmd, FALSE); break; case '*': while (*g_CurCmd != '\0') { g_CurCmd++; } break; case '#': g_PrefixSymbols = TRUE; igrep(); g_PrefixSymbols = FALSE; while (*g_CurCmd != '\0') { g_CurCmd++; } 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: if ((ch = PeekChar()) != '\0' && ch != ';') { GetAddrExpression(SEGREG_CODE, &g_AssemDefault); } fnAssemble(&g_AssemDefault); break; } break; case 'b': ch = *g_CurCmd++; ch = (UCHAR)tolower(ch); if (!IS_EXECUTION_POSSIBLE()) { error(SESSIONNOTSUP); } switch(ch) { case 'p': case 'u': case 'a': case 'i': case 'w': ParseBpCmd(Client, ch, NULL); break; case 'c': case 'd': case 'e': value1 = GetIdList(); ChangeBreakpointState(Client, g_CurrentProcess, (ULONG)value1, ch); break; case 'l': if (PeekChar() != ';' && *g_CurCmd) { value1 = GetIdList(); } else { value1 = ALL_ID_LIST; } ListBreakpoints(Client, g_CurrentProcess, (ULONG)value1); break; default: error(SYNTAX); break; } break; case 'c': GetRange(&addr1, &value2, 1, SEGREG_DATA); GetAddrExpression(SEGREG_DATA, &addr2); fnCompareMemory(&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 = (UCHAR)tolower(*g_CurCmd); g_CurCmd++; if (ch != 'b' && ch != 'w' && ch != 'd') { error(SYNTAX); } if (IS_USER_TARGET() || IS_DUMP_TARGET()) { error(SESSIONNOTSUP); } fnInputIo((ULONG)GetExpression(), ch); break; case 'j': PSTR pch, Start; if (GetExpression()) { pch = g_CurCmd; // Find a semicolon or a quote while (*pch && *pch != ';' && *pch != '\'') { pch++; } if (*pch == ';') { *pch = 0; } else if (*pch) { *pch = ' '; // Find the closing quote while (*pch && *pch != '\'') { pch++; } *pch = 0; } } else { Start = pch = g_CurCmd; // Find a semicolon or a quote while (*pch && *pch != ';' && *pch != '\'') { pch++; } if (*pch == ';') { Start = ++pch; } else if (*pch) { pch++; while (*pch && *pch++ != '\'') { // Empty. } while (*pch && (*pch == ';' || *pch == ' ')) { pch++; } Start = pch; } while (*pch && *pch != ';' && *pch != '\'') { pch++; } if (*pch == ';') { *pch = 0; } else if (*pch) { *pch = ' '; // Find the closing quote while (*pch && *pch != '\'') { pch++; } *pch = 0; } g_CurCmd = Start; } ch = *g_CurCmd++; goto EVALUATE; case 'k': { if (IS_LOCAL_KERNEL_TARGET()) { error(SESSIONNOTSUP); } if (!IS_CONTEXT_ACCESSIBLE()) { error(BADTHREAD); } DEBUG_SCOPE_STATE SaveCurrCtxtState = g_ScopeBuffer.State; value1 = 0; if (g_SwitchedProcs) { g_ScopeBuffer.State = ScopeDefault; } PCROSS_PLATFORM_CONTEXT ScopeContext; if (ScopeContext = GetCurrentScopeContext()) { g_Machine->PushContext(ScopeContext); } if (g_EffMachine == IMAGE_FILE_MACHINE_I386) { value2 = GetRegVal64(X86_EIP); } else { value2 = 0; } count = g_DefaultStackTraceDepth; ParseStackTrace(&traceType, &addr2, &value1, &value2, &count); if (ScopeContext) { g_Machine->PopContext(); } if (count >= 0x10000) { ErrOut("Requested number of stack frames (0x%x) is too large! " "The maximum number is 0xffff.\n", count); error(BADRANGE); } DoStackTrace( addr2.off, value1, value2, count, traceType ); if (g_SwitchedProcs) { RestoreCurrentProcessorThread(); g_SwitchedProcs = FALSE; g_ScopeBuffer.State = SaveCurrCtxtState; } break; } case 'l': ch = (UCHAR)tolower(*g_CurCmd); if (ch == 'n') { g_CurCmd++; if ((ch = PeekChar()) != '\0' && ch != ';') { GetAddrExpression(SEGREG_CODE, &addr1); } else { PCROSS_PLATFORM_CONTEXT ScopeContext = GetCurrentScopeContext(); if (ScopeContext) { g_Machine->PushContext(ScopeContext); } g_Machine->GetPC(&addr1); if (ScopeContext) { g_Machine->PopContext(); } } fnListNear(Flat(addr1)); } else if (ch == '+' || ch == '-') { g_CurCmd++; ParseSrcOptCmd(ch); } else if (ch == 's') { g_CurCmd++; ch = (UCHAR)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': { ADDR TempAddr; GetRange(&addr1, &value2, 1, SEGREG_DATA); GetAddrExpression(SEGREG_DATA, &TempAddr); fnMoveMemory(&addr1, (ULONG)value2, &TempAddr); } 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 = (UCHAR)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() || IS_DUMP_TARGET()) { error(SESSIONNOTSUP); } value1 = GetExpression(); value2 = HexValue((ULONG)value2); fnOutputIo((ULONG)value1, (ULONG)value2, ch); break; case 'w': case 'p': case 't': if (IS_KERNEL_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': UCHAR QuitArgument; QuitArgument = (UCHAR)tolower(PeekChar()); if ((IS_LIVE_USER_TARGET() && QuitArgument == 'd') || QuitArgument == 'k' || QuitArgument == 'q') { g_CurCmd++; } if (PeekChar() != 0) { error(SYNTAX); } if (QuitArgument != 'q' && Client != NULL && (Client->m_Flags & CLIENT_REMOTE)) { ErrOut("Exit a remote client with Ctrl-b.\nIf you " "really want the server to quit use 'qq'.\n"); break; } // Detach if requested. if (QuitArgument == 'd') { DBG_ASSERT(IS_LIVE_USER_TARGET()); // If detach isn't supported warn the user // and abort the quit. if (((UserTargetInfo*)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_AllProcessFlags & ENG_PROC_EXAMINED)) { HRESULT PrepareStatus; // 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 process for detach, " "0x%X\n", 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()) { 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, &valueL) == S_OK) { dprintf ("msr[%x] = %08x:%08x\n", (ULONG)value1, (ULONG) (valueL >> 32), (ULONG) valueL); } else { ErrOut ("no such msr\n"); } break; } } PCROSS_PLATFORM_CONTEXT ScopeContext; if (ScopeContext = GetCurrentScopeContext()) { dprintf("Last set context:\n"); g_Machine->PushContext(ScopeContext); } __try { ParseRegCmd(); } __finally { if (ScopeContext) { g_Machine->PopContext(); } if (g_SwitchedProcs) { RestoreCurrentProcessorThread(); g_SwitchedProcs = FALSE; } } break; case 's': ch = (UCHAR)tolower(*g_CurCmd); if (ch == 's') { g_CurCmd++; SetSuffix(); } else if (ch == 'q') { g_CurCmd++; ch = (UCHAR)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() && ch == 'o') { PSTR pch = ++g_CurCmd; while ((*pch != '\0') && (*pch != ';')) { pch++; } ch = *pch; *pch = '\0'; ReadDebugOptions(FALSE, (*g_CurCmd == '\0' ? NULL : g_CurCmd)); *pch = ch; g_CurCmd = pch; } else { // s, s-w, s-d, s-q. ParseSearchMemory(); } break; case 'u': g_PrefixSymbols = TRUE; ch = (UCHAR)tolower(*g_CurCmd); if (IS_KERNEL_TARGET() && ch == 'x') { g_CurCmd += 1; } value1 = Flat(g_UnasmDefault); value2 = (g_EffMachine == IMAGE_FILE_MACHINE_IA64) ? 9 : 8; fLength = GetRange(&g_UnasmDefault, &value2, 0, SEGREG_CODE); if (IS_KERNEL_TARGET() && ch == 'x') { ADDR addr; char text[MAX_DISASM_LEN]; if (g_X86BiosBaseAddress == 0) { g_X86BiosBaseAddress = (ULONG)ExtGetExpression("hal!HalpEisaMemoryBase"); ADDRFLAT(&addr, g_X86BiosBaseAddress); GetMemString(&addr, (PUCHAR)&g_X86BiosBaseAddress, 4); } addr = g_UnasmDefault; addr.flat += (g_X86BiosBaseAddress + (addr.seg<<4)); addr.off = addr.flat; addr.type = ADDR_V86 | INSTR_POINTER; for (value2 = 0; value2 < 8; value2++) { g_X86Machine.Disassemble( &addr, text, TRUE ); addr.flat = addr.off; dprintf("%s", text ); } g_UnasmDefault = addr; g_UnasmDefault.off -= (g_X86BiosBaseAddress + (addr.seg<<4)); g_UnasmDefault.flat = g_UnasmDefault.off; } else { fnUnassemble(&g_UnasmDefault, value2, fLength); } break; case 'v': if (_stricmp(g_CurCmd, "ertarget") == 0) { g_CurCmd += strlen(g_CurCmd); g_Target->OutputVersion(); } else if (_stricmp(g_CurCmd, "ersion") == 0) { g_CurCmd += strlen(g_CurCmd); // Print target version, then debugger version. g_Target->OutputVersion(); OutputVersionInformation(Client); } 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) { PSTR 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 = E_FAIL; g_EngStatus &= ~ENG_STATUS_USER_INTERRUPT; } return Status; } /*** fnEvaluateExp - evaluate expression * * Purpose: * Function for the "?" command. * * Evaluate the expression and output it in both * hexadecimal and decimal. * * Input: * g_CurCmd - pointer to operand in command line * * Output: * None. * *************************************************************************/ void EvaluateExp ( BOOL Verbose ) { LONG64 Value; LONG Val32; BOOL Use64; Value = GetExpression(); // Allow 64-bit expressions to be evaluated even on // 32-bit platforms since they can also use 64-bit numbers. Use64 = g_Machine->m_Ptr64 || NeedUpper(Value); Val32 = (LONG)Value; if (!Verbose) { if (Use64) { dprintf("Evaluate expression: %I64d = %08x`%08x\n", Value, (ULONG)(Value >> 32), Val32); } else { dprintf("Evaluate expression: %d = %08x\n", Val32, Val32); } } else { dprintf("Evaluate expression:\n"); if (Use64) { dprintf(" Hex: %08x`%08x\n", (ULONG)(Value >> 32), Val32); dprintf(" Decimal: %I64d\n", Value); } else { dprintf(" Hex: %08x\n", Val32); dprintf(" Decimal: %d\n", Val32); } ULONG Shift = Use64 ? 63 : 30; dprintf(" Octal: "); for (;;) { dprintf("%c", ((Value >> Shift) & 7) + '0'); if (Shift == 0) { break; } Shift -= 3; } dprintf("\n"); Shift = Use64 ? 63 : 31; dprintf(" Binary: "); for (;;) { if ((Shift & 7) == 7) { dprintf(" "); } dprintf("%c", ((Value >> Shift) & 1) + '0'); if (Shift == 0) { break; } Shift--; } dprintf("\n"); Shift = Use64 ? 56 : 24; dprintf(" Chars: "); for (;;) { Val32 = (LONG)((Value >> Shift) & 0xff); if (Val32 >= ' ' && Val32 <= 127) { dprintf("%c", Val32); } else { dprintf("."); } if (Shift == 0) { break; } Shift -= 8; } dprintf("\n"); } } void ScanForImages(void) { ULONG64 Handle = 0; MEMORY_BASIC_INFORMATION64 Info; BOOL Verbose = FALSE; // // Scan virtual memory looking for image headers. // while (g_Target->QueryMemoryRegion(&Handle, FALSE, &Info) == S_OK) { ULONG64 Addr; if (Verbose) { dprintf("Checking %s - %s\n", FormatAddr64(Info.BaseAddress), FormatAddr64(Info.BaseAddress + Info.RegionSize - 1)); FlushCallbacks(); } // // Check for MZ at the beginning of every page. // Addr = Info.BaseAddress; while (Addr < Info.BaseAddress + Info.RegionSize) { USHORT ShortSig; ULONG Done; if (g_Target->ReadVirtual(Addr, &ShortSig, sizeof(ShortSig), &Done) == S_OK && Done == sizeof(ShortSig) && ShortSig == IMAGE_DOS_SIGNATURE) { dprintf(" MZ at %s, prot %08X, type %08X\n", FormatAddr64(Addr), Info.Protect, Info.Type); FlushCallbacks(); } if (CheckUserInterrupt()) { WarnOut(" Aborted\n"); return; } Addr += g_Machine->m_PageSize; } } } void OutputMemRegion(ULONG64 Addr) { ULONG64 Handle = Addr; MEMORY_BASIC_INFORMATION64 Info; if (g_Target->QueryMemoryRegion(&Handle, TRUE, &Info) == S_OK) { dprintf("Region %s - %s, prot %08X, type %08X\n", FormatAddr64(Info.BaseAddress), FormatAddr64(Info.BaseAddress + Info.RegionSize - 1), Info.Protect, Info.Type); } else { dprintf("No region contains %s\n", FormatAddr64(Addr)); } } void SetEffMachineByName(void) { if (PeekChar() != ';' && *g_CurCmd) { PSTR Name = g_CurCmd; while (*g_CurCmd && *g_CurCmd != ';' && !isspace(*g_CurCmd)) { g_CurCmd++; } if (*g_CurCmd) { *g_CurCmd++ = 0; } ULONG Machine; if (Name[0] == '.' && Name[1] == 0) { // Reset to target machine. Machine = g_TargetMachineType; } else if (Name[0] == '#' && Name[1] == 0) { // Reset to executing machine. Machine = g_TargetExecMachine; } else { for (Machine = 0; Machine < MACHIDX_COUNT; Machine++) { if (!_strcmpi(Name, g_AllMachines[Machine]->m_AbbrevName)) { break; } } if (Machine >= MACHIDX_COUNT) { ErrOut("Unknown machine '%s'\n", Name); return; } Machine = g_AllMachines[Machine]->m_ExecTypes[0]; } SetEffMachine(Machine, TRUE); } if (g_Machine != NULL) { dprintf("Effective machine: %s (%s)\n", g_Machine->m_FullName, g_Machine->m_AbbrevName); } else { dprintf("No effective machine\n"); } } void ListProcesses(void) { BOOL ListCurrent = FALSE; BOOL Verbose = FALSE; if (!IS_LIVE_USER_TARGET()) { error(SESSIONNOTSUP); } while (PeekChar() == '-' || *g_CurCmd == '/') { switch(*++g_CurCmd) { case 'c': if (g_CurrentProcess == NULL) { error(BADPROCESS); } ListCurrent = TRUE; break; case 'v': Verbose = TRUE; break; default: ErrOut("Unknown option '%c'\n", *g_CurCmd); break; } g_CurCmd++; } PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services; #define MAX_IDS 1024 ULONG Ids[MAX_IDS]; HRESULT Status; ULONG IdCount; ULONG i; char DescBuf[2 * MAX_PATH]; PSTR Desc; ULONG DescLen; if (ListCurrent) { Ids[0] = g_CurrentProcess->SystemId; IdCount = 1; } else { if ((Status = Services->GetProcessIds(Ids, MAX_IDS, &IdCount)) != S_OK) { ErrOut("Unable to get process list, %s\n", FormatStatusCode(Status)); return; } if (IdCount > MAX_IDS) { WarnOut("Process list missing %d processes\n", IdCount - MAX_IDS); IdCount = MAX_IDS; } } if (Verbose) { Desc = DescBuf; DescLen = sizeof(DescBuf); } else { Desc = NULL; DescLen = 0; } for (i = 0; i < IdCount; i++) { char ExeName[MAX_PATH]; int Space; if (Ids[i] < 10) { Space = 3; } else if (Ids[i] < 100) { Space = 2; } else if (Ids[i] < 1000) { Space = 1; } else { Space = 0; } dprintf("%.*s", Space, " "); if (FAILED(Status = Services-> GetProcessDescription(Ids[i], DEBUG_PROC_DESC_DEFAULT, ExeName, sizeof(ExeName), NULL, Desc, DescLen, NULL))) { ErrOut("0n%d Error %s\n", Ids[i], FormatStatusCode(Status)); } else if (Ids[i] >= 0x80000000) { dprintf("0x%x %s\n", Ids[i], ExeName); } else { dprintf("0n%d %s\n", Ids[i], ExeName); } if (Desc && Desc[0]) { dprintf(" %s\n", Desc); } } } void EchoString(void) { CHAR Save; PSTR Str; Str = StringValue(STRV_TRIM_TRAILING_SPACE, &Save); dprintf("%s\n", Str); *g_CurCmd = Save; } void ParseAttachProcess(void) { ULONG Pid; ULONG Flags = DEBUG_ATTACH_DEFAULT; while (PeekChar() == '-' || *g_CurCmd == '/') { switch(*++g_CurCmd) { case 'e': Flags = DEBUG_ATTACH_EXISTING; break; case 'v': Flags = DEBUG_ATTACH_NONINVASIVE; break; default: ErrOut("Unknown option '%c'\n", *g_CurCmd); break; } g_CurCmd++; } Pid = (ULONG)GetExpression(); PPENDING_PROCESS Pending; HRESULT Status; Status = StartAttachProcess(Pid, Flags, &Pending); if (Status == S_OK) { dprintf("Attach will occur on next execution\n"); } } void ParseOutputFunctionEntry(void) { if (!IS_MACHINE_ACCESSIBLE()) { error(BADTHREAD); } BOOL SymDirect = FALSE; while (PeekChar() == '-' || *g_CurCmd == '/') { switch(*++g_CurCmd) { case 's': SymDirect = TRUE; break; default: ErrOut("Unknown option '%c'\n", *g_CurCmd); break; } g_CurCmd++; } ULONG64 Addr = GetExpression(); PVOID FnEnt; if (SymDirect) { FnEnt = SymFunctionTableAccess64(g_CurrentProcess->Handle, Addr); } else { FnEnt = SwFunctionTableAccess(g_CurrentProcess->Handle, Addr); } if (FnEnt == NULL) { ErrOut("No function entry for %s\n", FormatAddr64(Addr)); return; } dprintf("%s function entry %s for:\n", SymDirect ? "Symbol" : "Debugger", FormatAddr64((ULONG_PTR)FnEnt)); fnListNear(Addr); dprintf("\n"); g_Machine->OutputFunctionEntry(FnEnt); } void ParseOutputFilter(void) { if (PeekChar() != ';' && *g_CurCmd) { g_OutFilterResult = TRUE; while (PeekChar() == '-' || *g_CurCmd == '/') { switch(*++g_CurCmd) { case '!': g_OutFilterResult = FALSE; break; default: ErrOut("Unknown option '%c'\n", *g_CurCmd); break; } g_CurCmd++; } CHAR Save; PSTR Pat = StringValue(STRV_TRIM_TRAILING_SPACE | STRV_ESCAPED_CHARACTERS, &Save); if (strlen(Pat) + 1 > sizeof(g_OutFilterPattern)) { error(OVERFLOW); } strcpy(g_OutFilterPattern, Pat); *g_CurCmd = Save; _strupr(g_OutFilterPattern); } if (g_OutFilterPattern[0]) { dprintf("Only display debuggee output that %s '%s'\n", g_OutFilterResult ? "matches" : "doesn't match", g_OutFilterPattern); } else { dprintf("No debuggee output filter set\n"); } } void ParseSeparateCurrentProcess(PSTR Command) { HRESULT Status; ULONG Mode; char Desc[128]; if (!strcmp(Command, "abandon")) { Mode = SEP_ABANDON; } else if (!strcmp(Command, "detach")) { Mode = SEP_DETACH; } else if (!strcmp(Command, "kill")) { Mode = SEP_TERMINATE; } else { error(SYNTAX); } if ((Status = SeparateCurrentProcess(Mode, Desc)) == S_OK) { dprintf("%s\n", Desc); } else if (Status == E_NOTIMPL) { dprintf("The system doesn't support %s\n", Command); } else { dprintf("Unable to %s, %s\n", Command, FormatStatusCode(Status)); } } void ParseSetChildDebug(void) { if (!IS_LIVE_USER_TARGET()) { error(SESSIONNOTSUP); } if (g_CurrentProcess == NULL) { error(BADTHREAD); } HRESULT Status; PUSER_DEBUG_SERVICES Services = ((UserTargetInfo*)g_Target)->m_Services; ULONG Opts; Opts = g_CurrentProcess->Options; if (PeekChar() && *g_CurCmd != ';') { ULONG64 Val = GetExpression(); if (Val) { Opts &= ~DEBUG_PROCESS_ONLY_THIS_PROCESS; } else { Opts |= DEBUG_PROCESS_ONLY_THIS_PROCESS; } if ((Status = Services->SetProcessOptions(g_CurrentProcess->FullHandle, Opts)) != S_OK) { if (Status == E_NOTIMPL) { ErrOut("The system doesn't support changing the flag\n"); } else { ErrOut("Unable to set process options, %s\n", FormatStatusCode(Status)); } return; } g_CurrentProcess->Options = Opts; } dprintf("Processes created by the current process will%s be debugged\n", (Opts & DEBUG_PROCESS_ONLY_THIS_PROCESS) ? " not" : ""); } /*** fnDotCmdHelp - displays the legal dot commands * * Purpose: * Show user the dot commands. * * Input: * none * * Output: * None. * *************************************************************************/ VOID fnDotCmdHelp( VOID ) { dprintf(". commands:\n"); dprintf(" .abandon - abandon the current process\n"); dprintf(" .asm[-] [options...] - change disassembly options\n"); if (IS_LIVE_USER_TARGET()) { dprintf(" .attach - attach to at next execution\n"); dprintf(" .breakin - break into KD\n"); } if (IS_KERNEL_TARGET()) { dprintf(" .bugcheck - display the bugcheck code and parameters for a crashed\n"); dprintf(" system\n"); } dprintf(" .chain - list current extensions\n"); dprintf(" .childdbg <0|~0> - turn child process debugging on or off\n"); dprintf(" .clients - list currently active clients\n"); if (IS_KERNEL_TARGET()) { dprintf(" .context [
] - set page directory base\n"); dprintf(" .crash - cause target to bugcheck\n"); } if (IS_LIVE_USER_TARGET()) { dprintf(" .create - create a new process\n"); } dprintf(" .cxr
- dump context record at specified address\n" " k* after this gives cxr stack\n"); if (IS_LIVE_USER_TARGET()) { dprintf(" .detach - detach from the current process\n"); } dprintf(" .dump [options] filename - create a dump file on the host system\n"); dprintf(" .echo [\"string\"|string] - Echo string\n"); dprintf(" .ecxr - dump context record for current exception\n"); dprintf(" .enable_unicode [0/1] - dump USHORT array/pointers and unicode strings\n"); if (IS_REMOTE_USER_TARGET()) { dprintf(" .endpsrv - cause the current session's process server to exit\n"); } dprintf(" .exepath [dir[;...]] - set executable search path\n"); dprintf(" .exepath+ [dir[;...]] - append executable search path\n"); dprintf(" .exr
- dump exception record at specified address\n"); dprintf(" .fnent
- dump function entry for the given code address\n"); dprintf(" .formats - displays expression result in many formats\n"); dprintf(" .help - display this help\n"); dprintf(" .kframes - set default stack trace depth\n"); if (IS_LIVE_USER_TARGET()) { dprintf(" .kill - kill the current process\n"); } dprintf(" .lastevent - display the last event that occurred\n"); dprintf(" .lines - toggle line symbol loading\n"); dprintf(" .logfile - display log status\n"); dprintf(" .logopen [] - open new log file\n"); dprintf(" .logappend [] - append to log file\n"); dprintf(" .logclose - close log file\n"); dprintf(" .noshell - Disable shell commands\n"); dprintf(" .noversion - disable extension version checking\n"); dprintf(" .ofilter - filter debuggee output against the given pattern\n"); dprintf(" .process [
] - Sets implicit process\n" " Resets default if no address specified.\n"); if (IS_REMOTE_KERNEL_TARGET()) { dprintf(" .reboot - reboot target\n"); } dprintf(" .reload [filename[=
]] - reload symbols\n"); dprintf(" .remote - start remote.exe server\n"); dprintf(" .server - start engine server\n"); dprintf(" .sleep - debugger sleeps for given duration\n"); dprintf(" Useful for allowing access to a machine that's broken in on an ntsd -d.\n"); dprintf(" .srcpath [dir[;...]] - set source search path\n"); dprintf(" .srcpath+ [dir[;...]] - append source search path\n"); dprintf(" .sympath [dir[;...]] - set symbol search path\n"); dprintf(" .sympath+ [dir[;...]] - append symbol search path\n"); dprintf(" .shell [] - execute shell command\n"); if (IS_LIVE_USER_TARGET()) { dprintf(" .tlist - list running processes\n"); } if (IS_KERNEL_TARGET()) { dprintf(" .trap
- Dump a trap frame\n"); if (g_EffMachine == IMAGE_FILE_MACHINE_I386) { dprintf(" .tss - Dump a Task State Segment\n"); } } dprintf(" .thread [
] - Sets context of thread at address\n" " Resets default context if no address specified.\n"); dprintf("\n"); dprintf("The following can be used with any extension module:\n"); dprintf(" .load\n"); dprintf(" .setdll\n"); dprintf(" .unload\n"); dprintf(" .unloadall\n"); dprintf("\n"); } /*** fnDotCommand - parse and execute dot command * * Purpose: * Parse and execute all commands starting with a dot ('.'). * * Input: * g_CurCmd - pointer to character after dot in command line * * Output: * None. * *************************************************************************/ #define MAX_DOT_COMMAND 16 BOOL DotCommand( DebugClient* Client ) { ULONG index = 0; char chCmd[MAX_DOT_COMMAND]; UCHAR ch; ADDR tempAddr; HRESULT Status; ULONG64 Value; // read in up to the first few alpha characters into // chCmd, converting to lower case while (index < MAX_DOT_COMMAND) { ch = (UCHAR)tolower(*g_CurCmd); if ((ch >= 'a' && ch <= 'z') || ch == '-' || ch == '+' || ch == '_') { chCmd[index++] = ch; g_CurCmd++; } else { break; } } // if all characters read, then too big, else terminate if (index == MAX_DOT_COMMAND) { error(SYNTAX); } chCmd[index] = '\0'; // test for the commands if (!strcmp(chCmd, "asm") || !strcmp(chCmd, "asm-")) { ChangeAsmOptions(chCmd[3] != '-', g_CurCmd); // Command uses the whole string so we're done. *g_CurCmd = 0; } else if (!strcmp(chCmd, "x")) { dprintf("Pending %X, process %X\n", g_AllPendingFlags, g_AllProcessFlags); } else if (!strcmp(chCmd, "bpcmds")) { ULONG Flags = 0; PPROCESS_INFO Process = g_CurrentProcess; ULONG Id; while (PeekChar() == '-') { switch(*(++g_CurCmd)) { case '1': Flags |= BPCMDS_ONE_LINE; break; case 'd': Flags |= BPCMDS_FORCE_DISABLE; break; case 'e': Flags |= BPCMDS_EXPR_ONLY; break; case 'm': Flags |= BPCMDS_MODULE_HINT; break; case 'p': g_CurCmd++; Id = (ULONG)GetTermExprDesc("Process ID missing from"); Process = FindProcessByUserId(Id); if (!Process) { error(BADPROCESS); } g_CurCmd--; break; default: dprintf("Unknown option '%c'\n", *g_CurCmd); break; } g_CurCmd++; } // Internal command only. ListBreakpointsAsCommands(Client, Process, Flags); } else if (!strcmp(chCmd, "bpsync")) { if (PeekChar() && *g_CurCmd != ';') { if (GetExpression()) { g_EngOptions |= DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS; } else { g_EngOptions &= ~DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS; } } dprintf("Breakpoint synchronization %s\n", (g_EngOptions & DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS) ? "enabled" : "disabled"); } else if (!strcmp(chCmd, "childdbg")) { ParseSetChildDebug(); } else if (!strcmp(chCmd, "clients")) { DebugClient* Cur; for (Cur = g_Clients; Cur != NULL; Cur = Cur->m_Next) { if (Cur->m_Flags & CLIENT_PRIMARY) { dprintf("%s, last active %s", Cur->m_Identity, ctime(&Cur->m_LastActivity)); } } } else if (!strcmp(chCmd, "cxr")) { ULONG Flags = 0; ULONG64 ContextBase = 0; if (PeekChar()) { ContextBase = GetExpression(); } if (ContextBase) { OutputVirtualContext(ContextBase, REGALL_INT32 | REGALL_INT64 | (g_EffMachine == IMAGE_FILE_MACHINE_I386 ? REGALL_EXTRA0 : 0)); } else if (GetCurrentScopeContext()) { dprintf("Resetting default context\n"); ResetCurrentScope(); } } else if (!strcmp(chCmd, "dump")) { ParseDumpFileCommand(); } else if (!strcmp(chCmd, "dumpdebug")) { if (IS_DUMP_TARGET()) { // // Dump detailed statistics to debug dump files // ((DumpTargetInfo *)g_Target)->DumpDebug(); } else { error(SESSIONNOTSUP); } } else if (!strcmp(chCmd, "dumpoff")) { if (IS_DUMP_TARGET()) { // // Show the file offset for a VA. // ULONG64 Addr = GetExpression(); ULONG64 Offs; ULONG File; ULONG Avail; Offs = ((DumpTargetInfo*)g_Target)-> VirtualToOffset(Addr, &File, &Avail); dprintf("Virtual %s maps to file %d offset %I64x\n", FormatAddr64(Addr), File, Offs); } else { error(SESSIONNOTSUP); } } else if (!strcmp(chCmd, "dumppoff")) { if (IS_KERNEL_SUMMARY_DUMP() || IS_KERNEL_FULL_DUMP()) { // // Show the file offset for a physical address. // ULONG64 Addr = GetExpression(); ULONG Avail; dprintf("Physical %I64x maps to file offset %I64x\n", Addr, ((KernelFullSumDumpTargetInfo *)g_Target)-> PhysicalToOffset(Addr, &Avail)); } else { error(SESSIONNOTSUP); } } else if (!strcmp(chCmd, "echo")) { EchoString(); } else if (!strcmp(chCmd, "ecxr")) { CROSS_PLATFORM_CONTEXT Context; if ((Status = g_Target->GetExceptionContext(&Context)) != S_OK) { ErrOut("Unable to get exception context, 0x%X\n", Status); } else { OutputContext(&Context, REGALL_INT32 | REGALL_INT64 | (g_EffMachine == IMAGE_FILE_MACHINE_I386 ? REGALL_EXTRA0 : 0)); } } else if (!strcmp(chCmd, "effmach")) { SetEffMachineByName(); } else if (!strcmp(chCmd, "enable_unicode")) { g_EnableUnicode = (BOOL) GetExpression(); if (g_EnableUnicode) { g_TypeOptions |= DEBUG_TYPEOPTS_UNICODE_DISPLAY; } else { g_TypeOptions &= ~DEBUG_TYPEOPTS_UNICODE_DISPLAY; } // Callback to update locals and watch window NotifyChangeSymbolState(DEBUG_CSS_TYPE_OPTIONS, 0, NULL); } else if (IS_REMOTE_USER_TARGET() && !strcmp(chCmd, "endpsrv")) { ((UserTargetInfo*)g_Target)->m_Services->Uninitialize(TRUE); dprintf("Server told to exit\n"); } else if (!strcmp(chCmd, "exr")) { DumpExr(GetExpression()); } else if (!strcmp(chCmd, "esplog")) { extern ULONG g_EspLog[]; extern PULONG g_EspLogCur; ULONG i; PULONG Cur = g_EspLogCur; // XXX drewb - Temporary log to try and catch some // SET_OF_INVALID_CONTEXT bugchecks occurring randomly on x86. for (i = 0; i < 32; i++) { if (Cur <= g_EspLog) { Cur = g_EspLog + 64; } Cur -= 2; if ((*Cur & 0x80000000) == 0) { // Unused slot. break; } if (*Cur & 0x40000000) { dprintf("%2d: Set proc %2d: %08X\n", i, *Cur & 0xffff, Cur[1]); } else { dprintf("%2d: Get proc %2d: %08X\n", i, *Cur & 0xffff, Cur[1]); } } } else if (!strcmp(chCmd, "eventlog")) { OutputEventLog(); } else if (!strcmp(chCmd, "fnent")) { ParseOutputFunctionEntry(); } else if (!strcmp(chCmd, "formats")) { EvaluateExp(TRUE); } else if (!_stricmp( chCmd, "frame" )) { dotFrame(); PrintStackFrame(&GetCurrentScope()->Frame, DEBUG_STACK_FRAME_ADDRESSES | DEBUG_STACK_FRAME_NUMBERS | DEBUG_STACK_SOURCE_LINE); } else if (!strcmp(chCmd, "help")) { fnDotCmdHelp(); } else if (!strcmp(chCmd, "imgscan")) { ScanForImages(); } #if _ENABLE_DOT_K_COMMANDS else if (chCmd[0] == 'k') { fnStackTrace(&chCmd[1]); } #endif else if (IS_CONN_KERNEL_TARGET() && !strcmp(chCmd, "kdtrans")) { g_DbgKdTransport->OutputInfo(); } else if (IS_CONN_KERNEL_TARGET() && !strcmp(chCmd, "kdfiles")) { ParseKdFileAssoc(); } else if (!strcmp(chCmd, "kframes")) { g_DefaultStackTraceDepth = (ULONG)GetExpression(); dprintf("Default stack trace depth is 0n%d frames\n", g_DefaultStackTraceDepth); } else if (!strcmp(chCmd, "lastevent")) { dprintf("Last event: %s\n", g_LastEventDesc); } else if (!strcmp(chCmd, "logappend")) { fnLogOpen(TRUE); } else if (!strcmp(chCmd, "logclose")) { fnLogClose(); } else if (!strcmp(chCmd, "logfile")) { if (g_LogFile >= 0) { dprintf("Log '%s' open%s\n", g_OpenLogFileName, g_OpenLogFileAppended ? " for append" : ""); } else { dprintf("No log file open\n"); } } else if (!strcmp(chCmd, "logopen")) { fnLogOpen(FALSE); } else if (!strcmp(chCmd, "memregion")) { OutputMemRegion(GetExpression()); } else if (!strcmp(chCmd, "noengerr")) { // Internal command to clear out the error suppression // flags in case we want to rerun operations and check // for errors that may be in suppression mode. g_EngErr = 0; } else if (!strcmp(chCmd, "noshell")) { g_EngOptions |= DEBUG_ENGOPT_DISALLOW_SHELL_COMMANDS; dprintf("Shell commands disabled\n"); } else if (!strcmp(chCmd, "ofilter")) { ParseOutputFilter(); } else if (!strcmp(chCmd, "outmask") || !strcmp(chCmd, "outmask-")) { // Private internal command for debugging the debugger. ULONG Expr = (ULONG)GetExpression(); if (chCmd[7] == '-') { Client->m_OutMask &= ~Expr; } else { Client->m_OutMask |= Expr; } dprintf("Client %p mask is %X\n", Client, Client->m_OutMask); CollectOutMasks(); } else if (!strcmp(chCmd, "echotimestamps")) { g_EchoEventTimestamps = !g_EchoEventTimestamps; dprintf("Event timestamps are now %s\n", g_EchoEventTimestamps ? "enabled" : "disabled"); } else if (!strcmp(chCmd, "process")) { ParseSetImplicitProcess(); } else if (!strcmp(chCmd, "server")) { // Skip whitespace. if (PeekChar() == 0) { ErrOut("Usage: .server tcp:port= OR " ".server npipe:pipe=\n"); } else { Status = Client->StartServer(g_CurCmd); if (Status != S_OK) { ErrOut("Unable to start server, 0x%X\n", Status); } else { dprintf("Server started with '%s'\n", g_CurCmd); } *g_CurCmd = 0; } } else if (!strcmp(chCmd, "shell")) { fnShell(g_CurCmd); // Command uses the whole string so we're done. *g_CurCmd = 0; } else if (!strcmp(chCmd, "sleep")) { ULONG WaitStatus; ULONG Millis = (ULONG)GetExprDesc("Number of milliseconds missing from"); // This command is intended for use with ntsd/cdb -d // when being at the prompt locks up the target machine. // If you want to use the target machine for something, // such as copy symbols, there's no easy way to get it // running again without resuming the program. By // sleeping you can return control to the target machine // without changing the session state. // The sleep is done with a wait on a named event so // that it can be interrupted from a different process. ResetEvent(g_SleepPidEvent); WaitStatus = WaitForSingleObject(g_SleepPidEvent, Millis); if (WaitStatus == WAIT_OBJECT_0) { dprintf("Sleep interrupted\n"); } else if (WaitStatus != WAIT_TIMEOUT) { ErrOut("Sleep failed, %s\n", FormatStatusCode(WIN32_LAST_STATUS())); } } else if (!strcmp(chCmd, "sxcmds")) { ULONG Flags = 0; while (PeekChar() == '-') { switch(*(++g_CurCmd)) { case '1': Flags |= SXCMDS_ONE_LINE; break; default: dprintf("Unknown option '%c'\n", *g_CurCmd); break; } g_CurCmd++; } // Internal command only. ListFiltersAsCommands(Client, Flags); } else if (!strcmp(chCmd, "symopt+") || !strcmp(chCmd, "symopt-")) { ULONG Flags = (ULONG)GetExpression(); if (chCmd[6] == '+') { Flags |= g_SymOptions; } else { Flags = g_SymOptions & ~Flags; } dprintf("Symbol options are %X\n", Flags); SetSymOptions(Flags); } else if (!strcmp(chCmd, "thread")) { ParseSetImplicitThread(); } else if (!strcmp(chCmd, "time")) { g_Target->OutputTime(); } else if (!strcmp(chCmd, "wake")) { ULONG Pid = (ULONG)GetExpression(); if (!SetPidEvent(Pid, OPEN_EXISTING)) { ErrOut("Process %d is not a sleeping debugger\n", Pid); } } else if ((IS_REMOTE_KERNEL_TARGET() || IS_REMOTE_USER_TARGET()) && !strcmp(chCmd, "cache")) { g_VirtualCache.ParseCommands(); } else if (IS_REMOTE_KERNEL_TARGET() && !strcmp(chCmd, "pcache")) { g_PhysicalCache.ParseCommands(); } else if (IS_KERNEL_TARGET()) { if (!strcmp(chCmd, "trap")) { ULONG64 frame = 0 ; CROSS_PLATFORM_CONTEXT Context; if (PeekChar()) { frame = GetExpression(); } if (frame) { g_TargetMachine->DisplayTrapFrame(frame, &Context); SetCurrentScope(&g_LastRegFrame, &Context, sizeof(Context)); } else if (GetCurrentScopeContext()) { dprintf("Resetting default context\n"); ResetCurrentScope(); } } else if ((g_EffMachine == IMAGE_FILE_MACHINE_I386) && !strcmp(chCmd, "tss")) { g_X86Machine.DumpTSS(); } else if (!IS_LOCAL_KERNEL_TARGET()) { if (!strcmp(chCmd, "bugcheck")) { ULONG Code; ULONG64 Args[4]; g_Target->ReadBugCheckData(&Code, Args); dprintf("Bugcheck code %08X\n", Code); dprintf("Arguments %s %s %s %s\n", FormatAddr64(Args[0]), FormatAddr64(Args[1]), FormatAddr64(Args[2]), FormatAddr64(Args[3])); } else if (!IS_DUMP_TARGET()) { if (!strcmp(chCmd, "pagein")) { ParsePageIn(); } else if (!strcmp(chCmd, "reboot") && ch == '\0') { // null out .reboot command g_LastCommand[0] = '\0'; g_Target->Reboot(); } else if (IS_CONN_KERNEL_TARGET() && !strcmp(chCmd, "crash")) { g_LastCommand[0] = '\0'; DbgKdCrash( CRASH_BUGCHECK_CODE ); // Go back to waiting for a state change to // receive the bugcheck exception. g_CmdState = 's'; } else { return FALSE; } } else { return FALSE; } } else { return FALSE; } } else if (IS_LIVE_USER_TARGET()) { if (!strcmp(chCmd, "abandon") || !strcmp(chCmd, "detach") || !strcmp(chCmd, "kill")) { ParseSeparateCurrentProcess(chCmd); } else if (!strcmp(chCmd, "attach")) { ParseAttachProcess(); } else if (!strcmp(chCmd, "breakin")) { if (g_DebuggerPlatformId == VER_PLATFORM_WIN32_NT) { if (g_SystemVersion <= NT_SVER_NT4) { // SysDbgBreakPoint isn't supported, so // try to use user32's PrivateKDBreakPoint. if (InitDynamicCalls(&g_User32CallsDesc) == S_OK && g_User32Calls.PrivateKDBreakPoint != NULL) { g_User32Calls.PrivateKDBreakPoint(); } else { ErrOut(".breakin is not supported on this system\n"); } } else { NTSTATUS NtStatus; NtStatus = g_NtDllCalls.NtSystemDebugControl (SysDbgBreakPoint, NULL, 0, NULL, 0, NULL); if (NtStatus == STATUS_ACCESS_DENIED) { ErrOut(".breakin requires debug privilege\n"); } else if (NtStatus == STATUS_INVALID_INFO_CLASS) { ErrOut(".breakin is not supported on this system\n"); } else if (!NT_SUCCESS(NtStatus)) { ErrOut(".breakin failed, 0x%X\n", NtStatus); } } } else { ErrOut(".breakin not supported on this platform\n"); } } else if (!strcmp(chCmd, "create")) { PPENDING_PROCESS Pending; PSTR CmdLine; CHAR Save; CmdLine = StringValue(STRV_TRIM_TRAILING_SPACE, &Save); Status = StartCreateProcess(CmdLine, DEBUG_ONLY_THIS_PROCESS, &Pending); if (Status == S_OK) { dprintf("Create will proceed with next execution\n"); } *g_CurCmd = Save; } else if (!strcmp(chCmd, "tlist")) { ListProcesses(); } else { return FALSE; } } else { // Target not set yet. return FALSE; } return TRUE; } VOID ParseStackTrace ( PSTACK_TRACE_TYPE pTraceType, PADDR StartFP, PULONG64 Esp, PULONG64 Eip, PULONG Count ) { UCHAR ch; ch = PeekChar(); *pTraceType = STACK_TRACE_TYPE_DEFAULT; if (tolower(ch) == 'b') { g_CurCmd++; *pTraceType = STACK_TRACE_TYPE_KB; } else if (tolower(ch) == 'v') { g_CurCmd++; *pTraceType = STACK_TRACE_TYPE_KV; } else if (tolower(ch) == 'd') { g_CurCmd++; *pTraceType = STACK_TRACE_TYPE_KD; } else if (tolower(ch) == 'p') { g_CurCmd++; *pTraceType = STACK_TRACE_TYPE_KP; } ch = PeekChar(); if (tolower(ch) == 'n') { g_CurCmd++; *pTraceType = (STACK_TRACE_TYPE) (*pTraceType + STACK_TRACE_TYPE_KN); } if (!PeekChar() && GetCurrentScopeContext()) { dprintf(" *** Stack trace for last set context - .thread resets it\n"); } if (PeekChar() == '=') { g_CurCmd++; GetAddrExpression(SEGREG_STACK, StartFP); } else if (g_EffMachine == IMAGE_FILE_MACHINE_I386) { g_Machine->GetFP(StartFP); } else { ADDRFLAT(StartFP, 0); } *Count = g_DefaultStackTraceDepth; if (g_EffMachine == 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 then one value, set extra value for special // FPO backtrace // *Eip = GetExpression(); *Esp = *Count; *Count = g_DefaultStackTraceDepth; } } if ((ch = PeekChar()) != '\0' && ch != ';') { *Count = (ULONG)GetExpression(); if ((LONG)*Count < 1) { g_CurCmd++; error(SYNTAX); } } } void SetSuffix(void) { UCHAR 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) { char Buf[MAX_PATH]; DBH_DIAVERSION DiaVer; // // Print out the connection options if we are doing live debugging. // if (IS_KERNEL_TARGET()) { switch(g_TargetClassQualifier) { case DEBUG_KERNEL_CONNECTION: g_DbgKdTransport->GetParameters(Buf, sizeof(Buf)); break; case DEBUG_KERNEL_LOCAL: strcpy(Buf, "Local Debugging"); break; case DEBUG_KERNEL_EXDI_DRIVER: strcpy(Buf, "eXDI Debugging"); break; default: strcpy(Buf, "Dump File"); break; } dprintf("Connection options: %s\n", Buf); } dprintf("Command line: '%s'\n", GetCommandLine()); dprintf("dbgeng: "); OutputModuleIdInfo(NULL, "dbgeng.dll", NULL); 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); } VOID DumpExr( ULONG64 ExrAddress ) { ULONG i; CHAR Buffer[256]; ULONG64 displacement; EXCEPTION_RECORD64 Exr64; EXCEPTION_RECORD32 Exr32; EXCEPTION_RECORD64 *Exr = &Exr64; ULONG BytesRead; HRESULT hr = S_OK; if (ExrAddress == (ULONG64) -1) { if (g_LastEventType == DEBUG_EVENT_EXCEPTION) { Exr64 = g_LastEventInfo.Exception.ExceptionRecord; } else { ErrOut("Last event was not an exception\n"); return; } } else if (g_TargetMachine->m_Ptr64) { hr = g_Target->ReadVirtual(ExrAddress, &Exr64, sizeof(Exr64), &BytesRead); } else { hr = g_Target->ReadVirtual(ExrAddress, &Exr32, sizeof(Exr32), &BytesRead); ExceptionRecord32To64(&Exr32, &Exr64); } if (hr != S_OK) { dprintf64("Cannot read Exception record @ %p\n", ExrAddress); return; } GetSymbolStdCall(Exr->ExceptionAddress, &Buffer[0], sizeof(Buffer), &displacement, NULL); if (*Buffer) { dprintf64("ExceptionAddress: %p (%s", Exr->ExceptionAddress, Buffer); if (displacement) { dprintf64("+0x%p)\n", displacement); } else { dprintf64(")\n"); } } else { dprintf64("ExceptionAddress: %p\n", Exr->ExceptionAddress); } dprintf(" ExceptionCode: %08lx\n", Exr->ExceptionCode); dprintf(" ExceptionFlags: %08lx\n", Exr->ExceptionFlags); dprintf("NumberParameters: %d\n", Exr->NumberParameters); if (Exr->NumberParameters > EXCEPTION_MAXIMUM_PARAMETERS) { Exr->NumberParameters = EXCEPTION_MAXIMUM_PARAMETERS; } for (i = 0; i < Exr->NumberParameters; i++) { dprintf64(" Parameter[%d]: %p\n", i, Exr->ExceptionInformation[i]); } // // Known Exception processing: // switch ( Exr->ExceptionCode ) { case STATUS_ACCESS_VIOLATION: if ( Exr->NumberParameters == 2 ) { dprintf64("Attempt to %s address %p\n", (Exr->ExceptionInformation[0] ? "write to" : "read from"), Exr->ExceptionInformation[1] ); } break; case STATUS_IN_PAGE_ERROR: if ( Exr->NumberParameters == 3 ) { dprintf64("Inpage operation failed at %p, due to I/O error %p\n", Exr->ExceptionInformation[1], Exr->ExceptionInformation[2] ); } break; case STATUS_INVALID_HANDLE: case STATUS_HANDLE_NOT_CLOSABLE: dprintf64( "Thread tried to close a handle that was " "invalid or illegal to close\n"); break; case STATUS_POSSIBLE_DEADLOCK: if ( Exr->NumberParameters == 1 ) { RTL_CRITICAL_SECTION64 CritSec64; RTL_CRITICAL_SECTION32 CritSec32; ULONG Result ; GetSymbolStdCall( Exr->ExceptionInformation[0], &Buffer[0], sizeof(Buffer), &displacement, NULL ); if ( *Buffer ) { dprintf64("Critical section at %p (%s+%p)", Exr->ExceptionInformation[0], Buffer, displacement ); } else { dprintf64("Critical section at %p", Exr->ExceptionInformation[0] ); } if (g_TargetMachine->m_Ptr64) { hr = g_Target->ReadVirtual(Exr->ExceptionInformation[0], &CritSec64, sizeof(CritSec64), &BytesRead); } else { hr = g_Target->ReadVirtual(Exr->ExceptionInformation[0], &CritSec32, sizeof(CritSec32), &BytesRead); if (hr == S_OK) { CritSec64.OwningThread = CritSec32.OwningThread; } } if (hr == S_OK) { dprintf64("is owned by thread %p,\n" "causing this thread to raise an exception", CritSec64.OwningThread ); } dprintf("\n"); } break; default: break; } }