//---------------------------------------------------------------------------- // // Debug engine glue. // // Copyright (C) Microsoft Corporation, 1999-2001. // //---------------------------------------------------------------------------- #include "pch.cpp" #pragma hdrstop #include "conio.hpp" #include "engine.hpp" #include "main.hpp" // Global execution control. BOOL g_Exit; ULONG g_PlatformId; // Debug engine interfaces. IDebugClient* g_DbgClient; IDebugClient2* g_DbgClient2; IDebugControl* g_DbgControl; IDebugSymbols* g_DbgSymbols; IDebugRegisters* g_DbgRegisters; ULONG g_ExecStatus; ULONG g_LastProcessExitCode; BOOL g_Restarting; #define NTDLL_CALL_NAMES \ (sizeof(g_NtDllCallNames) / sizeof(g_NtDllCallNames[0])) // These names must match the ordering in the NTDLL_CALLS structure. char* g_NtDllCallNames[] = { "DbgPrint", "DbgPrompt", }; #define NTDLL_CALL_PROCS (sizeof(g_NtDllCalls) / sizeof(FARPROC)) NTDLL_CALLS g_NtDllCalls; //---------------------------------------------------------------------------- // // Event callbacks. // //---------------------------------------------------------------------------- class EventCallbacks : public DebugBaseEventCallbacks { public: // IUnknown. STDMETHOD_(ULONG, AddRef)( THIS ); STDMETHOD_(ULONG, Release)( THIS ); // IDebugEventCallbacks. STDMETHOD(GetInterestMask)( THIS_ OUT PULONG Mask ); STDMETHOD(ExitProcess)( THIS_ IN ULONG ExitCode ); STDMETHOD(ChangeEngineState)( THIS_ IN ULONG Flags, IN ULONG64 Argument ); }; STDMETHODIMP_(ULONG) EventCallbacks::AddRef( THIS ) { // This class is designed to be static so // there's no true refcount. return 1; } STDMETHODIMP_(ULONG) EventCallbacks::Release( THIS ) { // This class is designed to be static so // there's no true refcount. return 0; } STDMETHODIMP EventCallbacks::GetInterestMask( THIS_ OUT PULONG Mask ) { *Mask = DEBUG_EVENT_EXIT_PROCESS | DEBUG_EVENT_CHANGE_ENGINE_STATE; return S_OK; } STDMETHODIMP EventCallbacks::ExitProcess( THIS_ IN ULONG ExitCode ) { g_LastProcessExitCode = ExitCode; return DEBUG_STATUS_NO_CHANGE; } STDMETHODIMP EventCallbacks::ChangeEngineState( THIS_ IN ULONG Flags, IN ULONG64 Argument ) { if (Flags & DEBUG_CES_EXECUTION_STATUS) { g_ExecStatus = (ULONG)Argument; // If this notification came from a wait completing // we want to wake up the input thread so that new // commands can be processed. If it came from inside // a wait we don't want to ask for input as the engine // may go back to running at any time. if ((Argument & DEBUG_STATUS_INSIDE_WAIT) == 0) { if (g_IoMode == IO_NONE) { // Wake up the main loop. g_DbgClient->ExitDispatch(g_DbgClient); } else if (g_ConClient != NULL) { g_ConClient->ExitDispatch(g_DbgClient); } } } return S_OK; } EventCallbacks g_EventCb; //---------------------------------------------------------------------------- // // Functions. // //---------------------------------------------------------------------------- ULONG NTAPI Win9xDbgPrompt(char *Prompt, char *Buffer, ULONG BufferLen) { ULONG Len; // XXX drewb - Is there a real equivalent of DbgPrompt? if (BufferLen == 0) { return 0; } Buffer[0] = 0; printf("%s", Prompt); if (fgets(Buffer, BufferLen, stdin)) { Len = strlen(Buffer); while (Len > 0 && isspace(Buffer[Len - 1])) { Len--; } if (Len > 0) { Buffer[Len] = 0; } } else { Len = 0; } return Len; } ULONG __cdecl Win9xDbgPrint( char *Text, ... ) { char Temp[1024]; va_list Args; va_start(Args, Text); _vsnprintf(Temp, sizeof(Temp), Text, Args); va_end(Args); OutputDebugString(Temp); return 0; } void InitDynamicCalls(void) { HINSTANCE NtDll; ULONG i; char** Name; FARPROC* Proc; if (g_PlatformId != VER_PLATFORM_WIN32_NT) { g_NtDllCalls.DbgPrint = Win9xDbgPrint; g_NtDllCalls.DbgPrompt = Win9xDbgPrompt; return; } // // Dynamically link NT calls. // if (NTDLL_CALL_NAMES != NTDLL_CALL_PROCS) { ErrorExit("NtDllCalls mismatch\n"); } NtDll = LoadLibrary("ntdll.dll"); if (NtDll == NULL) { ErrorExit("%s: Unable to load ntdll\n", g_DebuggerName); } Name = g_NtDllCallNames; Proc = (FARPROC*)&g_NtDllCalls; for (i = 0; i < NTDLL_CALL_PROCS; i++) { *Proc = GetProcAddress(NtDll, *Name); if (*Proc == NULL) { ErrorExit("%s: Unable to link ntdll!%s\n", g_DebuggerName, *Name); } Proc++; Name++; } // If DbgPrintReturnControlC exists use it instead of // normal DbgPrint. FARPROC DpRetCc; DpRetCc = GetProcAddress(NtDll, "DbgPrintReturnControlC"); if (DpRetCc != NULL) { Proc = (FARPROC*)&g_NtDllCalls.DbgPrint; *Proc = DpRetCc; } } void DefaultEngineInitialize(void) { HRESULT Hr; OSVERSIONINFO OsVersionInfo; OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo); GetVersionEx(&OsVersionInfo); g_PlatformId = OsVersionInfo.dwPlatformId; if ((Hr = g_DbgClient->QueryInterface(IID_IDebugControl, (void **)&g_DbgControl)) != S_OK || (Hr = g_DbgClient->QueryInterface(IID_IDebugSymbols, (void **)&g_DbgSymbols)) != S_OK || (Hr = g_DbgClient->QueryInterface(IID_IDebugRegisters, (void **)&g_DbgRegisters)) != S_OK) { ErrorExit("Debug engine base queries failed, %s\n \"%s\"\n", FormatStatusCode(Hr), FormatStatus(Hr)); } // Queries for higher-version interfaces. These can // fail if this executable is run against an older engine. // This is highly unlikely since everything is shipped // as a set, but handle it anyway. if ((Hr = g_DbgClient->QueryInterface(IID_IDebugClient2, (void **)&g_DbgClient2)) != S_OK && Hr != E_NOINTERFACE && Hr != RPC_E_VERSION_MISMATCH) { ErrorExit("Debug engine base queries failed, %s\n \"%s\"\n", FormatStatusCode(Hr), FormatStatus(Hr)); } g_DbgClient->SetInputCallbacks(&g_ConInputCb); g_DbgClient->SetOutputCallbacks(&g_ConOutputCb); g_DbgClient->SetEventCallbacks(&g_EventCb); if (!g_RemoteClient) { // // Check environment variables to determine if any logfile needs to be // opened. // PSTR LogFile; BOOL Append; LogFile = getenv("_NT_DEBUG_LOG_FILE_APPEND"); if (LogFile != NULL) { Append = TRUE; } else { Append = FALSE; LogFile = getenv("_NT_DEBUG_LOG_FILE_OPEN"); } if (LogFile != NULL) { g_DbgControl->OpenLogFile(LogFile, Append); } } InitDynamicCalls(); } void CreateEngine(PCSTR RemoteOptions) { HRESULT Hr; if ((Hr = DebugCreate(IID_IDebugClient, (void **)&g_DbgClient)) != S_OK) { ErrorExit("DebugCreate failed, %s\n \"%s\"\n", FormatStatusCode(Hr), FormatStatus(Hr)); } if (RemoteOptions != NULL) { if ((Hr = g_DbgClient->StartServer(RemoteOptions)) != S_OK) { ErrorExit("StartServer failed, %s\n \"%s\"\n", FormatStatusCode(Hr), FormatStatus(Hr)); } } DefaultEngineInitialize(); } void ConnectEngine(PCSTR RemoteOptions) { HRESULT Hr; if ((Hr = DebugConnect(RemoteOptions, IID_IDebugClient, (void **)&g_DbgClient)) != S_OK) { ErrorExit("DebugCreate failed, %s\n \"%s\"\n", FormatStatusCode(Hr), FormatStatus(Hr)); } DefaultEngineInitialize(); } void InitializeSession(void) { HRESULT Hr; if (g_DumpFile != NULL) { if (g_DumpPageFile != NULL) { if (g_DbgClient2 == NULL) { ErrorExit("Debugger does not support extra dump files\n"); } if ((Hr = g_DbgClient2->AddDumpInformationFile (g_DumpPageFile, DEBUG_DUMP_FILE_PAGE_FILE_DUMP)) != S_OK) { ErrorExit("Unable to use '%s', %s\n \"%s\"\n", g_DumpPageFile, FormatStatusCode(Hr), FormatStatus(Hr)); } } Hr = g_DbgClient->OpenDumpFile(g_DumpFile); } else if (g_CommandLine != NULL || g_PidToDebug != 0 || g_ProcNameToDebug != NULL) { ULONG64 Server = 0; if (g_ProcessServer != NULL) { Hr = g_DbgClient->ConnectProcessServer(g_ProcessServer, &Server); if (Hr != S_OK) { ErrorExit("Unable to connect to process server, %s\n" " \"%s\"\n", FormatStatusCode(Hr), FormatStatus(Hr)); } } ULONG Pid; if (g_ProcNameToDebug != NULL) { Hr = g_DbgClient->GetRunningProcessSystemIdByExecutableName (Server, g_ProcNameToDebug, DEBUG_GET_PROC_ONLY_MATCH, &Pid); if (Hr != S_OK) { ErrorExit("Unable to find process '%s', %s\n \"%s\"\n", g_ProcNameToDebug, FormatStatusCode(Hr), FormatStatus(Hr)); } } else { Pid = g_PidToDebug; } Hr = g_DbgClient->CreateProcessAndAttach(Server, g_CommandLine, g_CreateFlags, Pid, g_AttachProcessFlags); if (g_DetachOnExitRequired && g_DbgClient-> AddProcessOptions(DEBUG_PROCESS_DETACH_ON_EXIT) != S_OK) { ErrorExit("%s: The system does not support detach on exit\n", g_DebuggerName); } else if (g_DetachOnExitImplied) { // The detach-on-exit is not required so don't check for // failures. This is necessary for the -- case where // detach-on-exit is implied but must work on systems // with and without the detach-on-exit support. g_DbgClient->AddProcessOptions(DEBUG_PROCESS_DETACH_ON_EXIT); } if (Server != 0) { g_DbgClient->DisconnectProcessServer(Server); } } else { Hr = g_DbgClient->AttachKernel(g_AttachKernelFlags, g_ConnectOptions); } if (Hr != S_OK) { ErrorExit("Debuggee initialization failed, %s\n \"%s\"\n", FormatStatusCode(Hr), FormatStatus(Hr)); } } BOOL WINAPI InterruptHandler( IN ULONG CtrlType ) { if (CtrlType == CTRL_C_EVENT || CtrlType == CTRL_BREAK_EVENT) { PDEBUG_CONTROL Control = g_RemoteClient ? g_ConControl : g_DbgControl; if (Control != NULL) { Control->SetInterrupt(DEBUG_INTERRUPT_ACTIVE); } else { ConOut("Debugger not initialized, cannot interrupt\n"); } return TRUE; } else { return FALSE; } } BOOL APIENTRY MyCreatePipeEx( OUT LPHANDLE lpReadPipe, OUT LPHANDLE lpWritePipe, IN LPSECURITY_ATTRIBUTES lpPipeAttributes, IN DWORD nSize, DWORD dwReadMode, DWORD dwWriteMode ) /*++ Routine Description: The CreatePipeEx API is used to create an anonymous pipe I/O device. Unlike CreatePipe FILE_FLAG_OVERLAPPED may be specified for one or both handles. Two handles to the device are created. One handle is opened for reading and the other is opened for writing. These handles may be used in subsequent calls to ReadFile and WriteFile to transmit data through the pipe. Arguments: lpReadPipe - Returns a handle to the read side of the pipe. Data may be read from the pipe by specifying this handle value in a subsequent call to ReadFile. lpWritePipe - Returns a handle to the write side of the pipe. Data may be written to the pipe by specifying this handle value in a subsequent call to WriteFile. lpPipeAttributes - An optional parameter that may be used to specify the attributes of the new pipe. If the parameter is not specified, then the pipe is created without a security descriptor, and the resulting handles are not inherited on process creation. Otherwise, the optional security attributes are used on the pipe, and the inherit handles flag effects both pipe handles. nSize - Supplies the requested buffer size for the pipe. This is only a suggestion and is used by the operating system to calculate an appropriate buffering mechanism. A value of zero indicates that the system is to choose the default buffering scheme. Return Value: TRUE - The operation was successful. FALSE/NULL - The operation failed. Extended error status is available using GetLastError. --*/ { static ULONG PipeSerialNumber; HANDLE ReadPipeHandle, WritePipeHandle; DWORD dwError; UCHAR PipeNameBuffer[ MAX_PATH ]; // // Only one valid OpenMode flag - FILE_FLAG_OVERLAPPED // if ((dwReadMode | dwWriteMode) & (~FILE_FLAG_OVERLAPPED)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // // Set the default timeout to 120 seconds // if (nSize == 0) { nSize = 4096; } sprintf( (char *)PipeNameBuffer, "\\\\.\\Pipe\\Win32PipesEx.%08x.%08x", GetCurrentProcessId(), PipeSerialNumber++ ); ReadPipeHandle = CreateNamedPipeA( (char *)PipeNameBuffer, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_WAIT, 1, // Number of pipes nSize, // Out buffer size nSize, // In buffer size 120 * 1000, // Timeout in ms lpPipeAttributes ); if (ReadPipeHandle == INVALID_HANDLE_VALUE) { return FALSE; } WritePipeHandle = CreateFileA( (char *)PipeNameBuffer, GENERIC_WRITE, 0, // No sharing lpPipeAttributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | dwWriteMode, NULL // Template file ); if (INVALID_HANDLE_VALUE == WritePipeHandle) { dwError = GetLastError(); CloseHandle( ReadPipeHandle ); SetLastError(dwError); return FALSE; } *lpReadPipe = ReadPipeHandle; *lpWritePipe = WritePipeHandle; return( TRUE ); } void StartRemote( PCSTR Args ) /*++ Routine Description: "remotes" the current debugger by starting a copy of remote.exe in a special mode that causes it to attach to us, the debugger, as its "child" process. Arguments: Args - Name of the pipe to use for this remote session, e.g. "ntsd" means to connect one would use "remote /c machinename ntsd". Return Value: None. --*/ { static BOOL fRemoteIsRunning; HANDLE hRemoteChildProcess; HANDLE hOrgStdIn; HANDLE hOrgStdOut; HANDLE hOrgStdErr; HANDLE hNewStdIn; HANDLE hRemoteWriteChildStdIn; HANDLE hNewStdOut; HANDLE hRemoteReadChildStdOut; HANDLE hNewStdErr; SECURITY_ATTRIBUTES sa; STARTUPINFO si; PROCESS_INFORMATION pi; char szCmd[64]; if (Args == NULL) { goto DotRemoteUsage; } while (*Args == ' ' || *Args == '\t') { Args++; } if (!Args[0]) { goto DotRemoteUsage; } if (g_PipeWrite != NULL) { ConOut("An input thread has already been started so .remote\n"); ConOut("cannot be used. Either start the debugger with\n"); ConOut("remote.exe, such as remote /s \"kd\" pipe; or use\n"); ConOut("debugger remoting with -server/-client/.server.\n"); return; } if (fRemoteIsRunning) { ConOut(".remote: can't .remote twice.\n"); goto Cleanup; } if (g_IoMode != IO_CONSOLE) { ConOut(".remote: can't .remote when using -d. " "Remote the kernel debugger instead.\n"); goto Cleanup; } ConOut("Starting remote with pipename '%s'\n", Args); // // We'll pass remote.exe inheritable handles to this process, // our standard in/out handles (for it to use as stdin/stdout), // and pipe handles for it to write to our new stdin and read // from our new stdout. // // // Get an inheritable handle to our process. // if ( ! DuplicateHandle( GetCurrentProcess(), // src process GetCurrentProcess(), // src handle GetCurrentProcess(), // targ process &hRemoteChildProcess, // targ handle 0, // access TRUE, // inheritable DUPLICATE_SAME_ACCESS // options )) { ConOut(".remote: Unable to duplicate process handle.\n"); goto Cleanup; } // // Get inheritable copies of our current stdin, stdout, stderr which // we'll use for same for remote.exe when we spawn it. // hOrgStdIn = g_ConInput; hOrgStdOut = g_ConOutput; hOrgStdErr = GetStdHandle(STD_ERROR_HANDLE); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; // // Create remote->ntsd pipe, our end of which will be our // new stdin. The remote.exe end needs to be opened // for overlapped I/O, so yet another copy of MyCreatePipeEx // spreads through our source base. // if ( ! MyCreatePipeEx( &hNewStdIn, // read handle &hRemoteWriteChildStdIn, // write handle &sa, // security 0, // size 0, // read handle overlapped? FILE_FLAG_OVERLAPPED // write handle overlapped? )) { ConOut(".remote: Unable to create stdin pipe.\n"); CloseHandle(hRemoteChildProcess); goto Cleanup; } // // We don't want remote.exe to inherit our end of the pipe // so duplicate it to a non-inheritable one. // if ( ! DuplicateHandle( GetCurrentProcess(), // src process hNewStdIn, // src handle GetCurrentProcess(), // targ process &hNewStdIn, // targ handle 0, // access FALSE, // inheritable DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE // options )) { ConOut(".remote: Unable to duplicate stdout handle.\n"); CloseHandle(hRemoteChildProcess); CloseHandle(hRemoteWriteChildStdIn); goto Cleanup; } // // Create ntsd->remote pipe, our end of which will be our // new stdout and stderr. // if ( ! MyCreatePipeEx( &hRemoteReadChildStdOut, // read handle &hNewStdOut, // write handle &sa, // security 0, // size FILE_FLAG_OVERLAPPED, // read handle overlapped? 0 // write handle overlapped? )) { ConOut(".remote: Unable to create stdout pipe.\n"); CloseHandle(hRemoteChildProcess); CloseHandle(hRemoteWriteChildStdIn); CloseHandle(hNewStdIn); goto Cleanup; } // // We don't want remote.exe to inherit our end of the pipe // so duplicate it to a non-inheritable one. // if ( ! DuplicateHandle( GetCurrentProcess(), // src process hNewStdOut, // src handle GetCurrentProcess(), // targ process &hNewStdOut, // targ handle 0, // access FALSE, // inheritable DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE // options )) { ConOut(".remote: Unable to duplicate stdout handle.\n"); CloseHandle(hRemoteChildProcess); CloseHandle(hRemoteWriteChildStdIn); CloseHandle(hNewStdIn); CloseHandle(hRemoteReadChildStdOut); goto Cleanup; } // // Duplicate our new stdout to a new stderr. // if ( ! DuplicateHandle( GetCurrentProcess(), // src process hNewStdOut, // src handle GetCurrentProcess(), // targ process &hNewStdErr, // targ handle 0, // access FALSE, // inheritable DUPLICATE_SAME_ACCESS // options )) { ConOut(".remote: Unable to duplicate stdout handle.\n"); CloseHandle(hRemoteChildProcess); CloseHandle(hRemoteWriteChildStdIn); CloseHandle(hNewStdIn); CloseHandle(hRemoteReadChildStdOut); CloseHandle(hNewStdOut); goto Cleanup; } // // We now have all the handles we need. Let's launch remote. // sprintf( szCmd, "remote.exe /a %d %d %d %s %s", HandleToUlong(hRemoteChildProcess), HandleToUlong(hRemoteWriteChildStdIn), HandleToUlong(hRemoteReadChildStdOut), g_DebuggerName, Args ); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = hOrgStdIn; si.hStdOutput = hOrgStdOut; si.hStdError = hOrgStdErr; si.wShowWindow = SW_SHOW; // // Create Child Process // if ( ! CreateProcess( NULL, szCmd, NULL, NULL, TRUE, GetPriorityClass( GetCurrentProcess() ), NULL, NULL, &si, &pi)) { if (GetLastError() == ERROR_FILE_NOT_FOUND) { ConOut("remote.exe not found\n"); } else { ConOut("CreateProcess(%s) failed, error %d.\n", szCmd, GetLastError()); } CloseHandle(hRemoteChildProcess); CloseHandle(hRemoteWriteChildStdIn); CloseHandle(hNewStdIn); CloseHandle(hRemoteReadChildStdOut); CloseHandle(hNewStdOut); CloseHandle(hNewStdErr); goto Cleanup; } CloseHandle(hRemoteChildProcess); CloseHandle(hRemoteWriteChildStdIn); CloseHandle(hRemoteReadChildStdOut); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); // // Switch to using the new handles. Might be nice to // start a thread here to watch for remote.exe dying // and switch back to the old handles. // // CloseHandle(hOrgStdIn); if (g_PromptInput == g_ConInput) { g_PromptInput = hNewStdIn; } g_ConInput = hNewStdIn; SetStdHandle(STD_INPUT_HANDLE, hNewStdIn); // CloseHandle(hOrgStdOut); g_ConOutput = hNewStdOut; SetStdHandle(STD_OUTPUT_HANDLE, hNewStdOut); // CloseHandle(hOrgStdErr); SetStdHandle(STD_ERROR_HANDLE, hNewStdErr); fRemoteIsRunning = TRUE; ConOut("%s: now running under remote.exe pipename %s\n", g_DebuggerName, Args); Cleanup: return; DotRemoteUsage: ConOut("Usage: .remote pipename\n"); } BOOL UiCommand(PSTR Command) { char Term; PSTR Scan, Arg; // // Check and see if this is a UI command // vs. a command that should go to the engine. // while (isspace(*Command)) { Command++; } Scan = Command; while (*Scan && !isspace(*Scan)) { Scan++; } Term = *Scan; *Scan = 0; // Advance to next nonspace char for arguments. if (Term != 0) { Arg = Scan + 1; while (isspace(*Arg)) { Arg++; } if (*Arg == 0) { Arg = NULL; } } else { Arg = NULL; } if (!_strcmpi(Command, ".hh")) { if (Arg == NULL) { OpenHelpTopic(HELP_TOPIC_TABLE_OF_CONTENTS); } else { OpenHelpIndex(Arg); } } else if (!_strcmpi(Command, ".remote")) { StartRemote(Arg); } else if (!_strcmpi(Command, ".restart")) { if (g_RemoteClient) { ConOut("Only the primary debugger can restart\n"); } else if (g_PidToDebug != 0 || g_ProcNameToDebug != NULL) { ConOut("Process attaches cannot be restarted. If you want to\n" "restart the process, use !peb to get what command line\n" "to use and other initialization information.\n"); } else { g_DbgClient->EndSession(DEBUG_END_ACTIVE_TERMINATE); g_Restarting = TRUE; } } else if (!_strcmpi(Command, ".server")) { // We need to start a separate input thread when // using remoting but we do not actually handle // the command. CreateInputThread(); *Scan = Term; return FALSE; } else if (Command[0] == '$' && Command[1] == '<') { *Scan = Term; if (g_NextOldInputFile >= MAX_INPUT_NESTING) { ConOut("Scripts too deeply nested\n"); } else { FILE* Script = fopen(Command + 2, "r"); if (Script == NULL) { ConOut("Unable to open '%s'\n", Command + 2); } else { g_OldInputFiles[g_NextOldInputFile++] = g_InputFile; g_InputFile = Script; } } } else { *Scan = Term; return FALSE; } return TRUE; } BOOL CallBugCheckExtension( void ) { HRESULT Status = E_FAIL; ULONG Code; ULONG64 Args[4]; // Run the bugcheck analyzers if this dump has a bugcheck. if (g_DbgControl->ReadBugCheckData(&Code, &Args[0], &Args[1], &Args[2], &Args[3]) != S_OK || Code == 0) { return FALSE; } if (g_DbgClient != 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. Status = g_DbgControl->CallExtension(NULL, ExtName, ""); } if (Status != S_OK) { if (g_DbgClient == NULL) { ConOut("WARNING: Unable to locate a client for " "bugcheck analysis\n"); } ConOut("*******************************************************************************\n"); ConOut("* *\n"); ConOut("* Bugcheck Analysis *\n"); ConOut("* *\n"); ConOut("*******************************************************************************\n"); g_DbgControl->Execute(DEBUG_OUTCTL_AMBIENT, ".bugcheck", DEBUG_EXECUTE_DEFAULT); ConOut("\n"); g_DbgControl->Execute(DEBUG_OUTCTL_AMBIENT, "kb", DEBUG_EXECUTE_DEFAULT); ConOut("\n"); } else { return TRUE; } return FALSE; } BOOL MainLoop(void) { HRESULT Hr; BOOL SessionEnded = FALSE; ULONG64 InstructionOffset; DEBUG_STACK_FRAME StkFrame; ULONG Class, Qual; if (!SetConsoleCtrlHandler(InterruptHandler, TRUE)) { ConOut("Warning: unable to set Control-C handler.\n"); } // Get initial status. g_DbgControl->GetExecutionStatus(&g_ExecStatus); g_DbgControl->GetDebuggeeType(&Class, &Qual); while (!g_Exit) { if (!g_RemoteClient) { Hr = g_DbgControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE); if (FAILED(Hr)) { // The debug session may have ended. If so, just exit. if (g_DbgControl->GetExecutionStatus(&g_ExecStatus) == S_OK && g_ExecStatus == DEBUG_STATUS_NO_DEBUGGEE) { SessionEnded = TRUE; break; } // Inform the user of the failure and go to // command processing. ConOut("WaitForEvent failed, %s\n \"%s\"\n", FormatStatusCode(Hr), FormatStatus(Hr)); } // By far the most likely reason for WaitForEvent to // fail on a dump is bad symbols, which would produce // further errors when trying to use processor state. // Avoid doing so in the dump case. if (FAILED(Hr) && g_DumpFile != NULL) { ConOut("When WaitForEvent fails on dump files the " "current state is not displayed\n"); } else { BOOL DisplayRegs = TRUE; if (Class == DEBUG_CLASS_KERNEL && (Qual == DEBUG_DUMP_SMALL || Qual == DEBUG_DUMP_DEFAULT || Qual == DEBUG_DUMP_FULL)) { if (CallBugCheckExtension()) { DisplayRegs = FALSE; } } if (DisplayRegs) { // Dump registers and such. g_DbgControl->OutputCurrentState(DEBUG_OUTCTL_ALL_CLIENTS, DEBUG_CURRENT_DEFAULT); } } } while (!g_Exit && g_ExecStatus == DEBUG_STATUS_BREAK) { if (g_IoMode == IO_NONE) { // This is a pure remoting server with no // local user. Just wait for a remote client // to get things running again. Hr = g_DbgClient->DispatchCallbacks(INFINITE); if (Hr != S_OK) { OutputDebugString("Unable to dispatch callbacks\n"); ExitDebugger(Hr); } } else { char Command[MAX_COMMAND]; g_DbgControl->OutputPrompt(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED, " "); if (ConIn(Command, sizeof(Command), TRUE)) { if (g_RemoteClient) { // Identify self before command. g_DbgClient-> OutputIdentity(DEBUG_OUTCTL_ALL_OTHER_CLIENTS, DEBUG_OUTPUT_IDENTITY_DEFAULT, "[%s] "); } g_DbgControl->OutputPrompt(DEBUG_OUTCTL_ALL_OTHER_CLIENTS, " %s\n", Command); // Intercept and handle UI commands. if (!UiCommand(Command)) { // Must be an engine command. g_DbgControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS, Command, DEBUG_EXECUTE_NOT_LOGGED); } } } } if (g_Restarting) { InitializeSession(); g_Restarting = FALSE; continue; } if (Class != DEBUG_CLASS_USER_WINDOWS) { // The kernel debugger doesn't exit when the machine reboots. g_Exit = FALSE; } else { g_Exit = g_ExecStatus == DEBUG_STATUS_NO_DEBUGGEE; if (g_Exit) { SessionEnded = TRUE; break; } } if (g_RemoteClient) { Hr = g_DbgClient->DispatchCallbacks(INFINITE); if (Hr != S_OK) { OutputDebugString("Unable to dispatch callbacks\n"); ExitDebugger(Hr); } } } return SessionEnded; }