Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1265 lines
34 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. // Debug engine glue.
  4. //
  5. // Copyright (C) Microsoft Corporation, 1999-2001.
  6. //
  7. //----------------------------------------------------------------------------
  8. #include "pch.cpp"
  9. #pragma hdrstop
  10. #include "conio.hpp"
  11. #include "engine.hpp"
  12. #include "main.hpp"
  13. // Global execution control.
  14. BOOL g_Exit;
  15. ULONG g_PlatformId;
  16. // Debug engine interfaces.
  17. IDebugClient* g_DbgClient;
  18. IDebugClient2* g_DbgClient2;
  19. IDebugControl* g_DbgControl;
  20. IDebugSymbols* g_DbgSymbols;
  21. IDebugRegisters* g_DbgRegisters;
  22. ULONG g_ExecStatus;
  23. ULONG g_LastProcessExitCode;
  24. BOOL g_Restarting;
  25. #define NTDLL_CALL_NAMES \
  26. (sizeof(g_NtDllCallNames) / sizeof(g_NtDllCallNames[0]))
  27. // These names must match the ordering in the NTDLL_CALLS structure.
  28. char* g_NtDllCallNames[] =
  29. {
  30. "DbgPrint",
  31. "DbgPrompt",
  32. };
  33. #define NTDLL_CALL_PROCS (sizeof(g_NtDllCalls) / sizeof(FARPROC))
  34. NTDLL_CALLS g_NtDllCalls;
  35. //----------------------------------------------------------------------------
  36. //
  37. // Event callbacks.
  38. //
  39. //----------------------------------------------------------------------------
  40. class EventCallbacks : public DebugBaseEventCallbacks
  41. {
  42. public:
  43. // IUnknown.
  44. STDMETHOD_(ULONG, AddRef)(
  45. THIS
  46. );
  47. STDMETHOD_(ULONG, Release)(
  48. THIS
  49. );
  50. // IDebugEventCallbacks.
  51. STDMETHOD(GetInterestMask)(
  52. THIS_
  53. OUT PULONG Mask
  54. );
  55. STDMETHOD(ExitProcess)(
  56. THIS_
  57. IN ULONG ExitCode
  58. );
  59. STDMETHOD(ChangeEngineState)(
  60. THIS_
  61. IN ULONG Flags,
  62. IN ULONG64 Argument
  63. );
  64. };
  65. STDMETHODIMP_(ULONG)
  66. EventCallbacks::AddRef(
  67. THIS
  68. )
  69. {
  70. // This class is designed to be static so
  71. // there's no true refcount.
  72. return 1;
  73. }
  74. STDMETHODIMP_(ULONG)
  75. EventCallbacks::Release(
  76. THIS
  77. )
  78. {
  79. // This class is designed to be static so
  80. // there's no true refcount.
  81. return 0;
  82. }
  83. STDMETHODIMP
  84. EventCallbacks::GetInterestMask(
  85. THIS_
  86. OUT PULONG Mask
  87. )
  88. {
  89. *Mask = DEBUG_EVENT_EXIT_PROCESS | DEBUG_EVENT_CHANGE_ENGINE_STATE;
  90. return S_OK;
  91. }
  92. STDMETHODIMP
  93. EventCallbacks::ExitProcess(
  94. THIS_
  95. IN ULONG ExitCode
  96. )
  97. {
  98. g_LastProcessExitCode = ExitCode;
  99. return DEBUG_STATUS_NO_CHANGE;
  100. }
  101. STDMETHODIMP
  102. EventCallbacks::ChangeEngineState(
  103. THIS_
  104. IN ULONG Flags,
  105. IN ULONG64 Argument
  106. )
  107. {
  108. if (Flags & DEBUG_CES_EXECUTION_STATUS)
  109. {
  110. g_ExecStatus = (ULONG)Argument;
  111. // If this notification came from a wait completing
  112. // we want to wake up the input thread so that new
  113. // commands can be processed. If it came from inside
  114. // a wait we don't want to ask for input as the engine
  115. // may go back to running at any time.
  116. if ((Argument & DEBUG_STATUS_INSIDE_WAIT) == 0)
  117. {
  118. if (g_IoMode == IO_NONE)
  119. {
  120. // Wake up the main loop.
  121. g_DbgClient->ExitDispatch(g_DbgClient);
  122. }
  123. else if (g_ConClient != NULL)
  124. {
  125. g_ConClient->ExitDispatch(g_DbgClient);
  126. }
  127. }
  128. }
  129. return S_OK;
  130. }
  131. EventCallbacks g_EventCb;
  132. //----------------------------------------------------------------------------
  133. //
  134. // Functions.
  135. //
  136. //----------------------------------------------------------------------------
  137. ULONG NTAPI
  138. Win9xDbgPrompt(char *Prompt, char *Buffer, ULONG BufferLen)
  139. {
  140. ULONG Len;
  141. // XXX drewb - Is there a real equivalent of DbgPrompt?
  142. if (BufferLen == 0)
  143. {
  144. return 0;
  145. }
  146. Buffer[0] = 0;
  147. printf("%s", Prompt);
  148. if (fgets(Buffer, BufferLen, stdin))
  149. {
  150. Len = strlen(Buffer);
  151. while (Len > 0 && isspace(Buffer[Len - 1]))
  152. {
  153. Len--;
  154. }
  155. if (Len > 0)
  156. {
  157. Buffer[Len] = 0;
  158. }
  159. }
  160. else
  161. {
  162. Len = 0;
  163. }
  164. return Len;
  165. }
  166. ULONG __cdecl
  167. Win9xDbgPrint( char *Text, ... )
  168. {
  169. char Temp[1024];
  170. va_list Args;
  171. va_start(Args, Text);
  172. _vsnprintf(Temp, sizeof(Temp), Text, Args);
  173. va_end(Args);
  174. OutputDebugString(Temp);
  175. return 0;
  176. }
  177. void
  178. InitDynamicCalls(void)
  179. {
  180. HINSTANCE NtDll;
  181. ULONG i;
  182. char** Name;
  183. FARPROC* Proc;
  184. if (g_PlatformId != VER_PLATFORM_WIN32_NT)
  185. {
  186. g_NtDllCalls.DbgPrint = Win9xDbgPrint;
  187. g_NtDllCalls.DbgPrompt = Win9xDbgPrompt;
  188. return;
  189. }
  190. //
  191. // Dynamically link NT calls.
  192. //
  193. if (NTDLL_CALL_NAMES != NTDLL_CALL_PROCS)
  194. {
  195. ErrorExit("NtDllCalls mismatch\n");
  196. }
  197. NtDll = LoadLibrary("ntdll.dll");
  198. if (NtDll == NULL)
  199. {
  200. ErrorExit("%s: Unable to load ntdll\n", g_DebuggerName);
  201. }
  202. Name = g_NtDllCallNames;
  203. Proc = (FARPROC*)&g_NtDllCalls;
  204. for (i = 0; i < NTDLL_CALL_PROCS; i++)
  205. {
  206. *Proc = GetProcAddress(NtDll, *Name);
  207. if (*Proc == NULL)
  208. {
  209. ErrorExit("%s: Unable to link ntdll!%s\n",
  210. g_DebuggerName, *Name);
  211. }
  212. Proc++;
  213. Name++;
  214. }
  215. // If DbgPrintReturnControlC exists use it instead of
  216. // normal DbgPrint.
  217. FARPROC DpRetCc;
  218. DpRetCc = GetProcAddress(NtDll, "DbgPrintReturnControlC");
  219. if (DpRetCc != NULL)
  220. {
  221. Proc = (FARPROC*)&g_NtDllCalls.DbgPrint;
  222. *Proc = DpRetCc;
  223. }
  224. }
  225. void
  226. DefaultEngineInitialize(void)
  227. {
  228. HRESULT Hr;
  229. OSVERSIONINFO OsVersionInfo;
  230. OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo);
  231. GetVersionEx(&OsVersionInfo);
  232. g_PlatformId = OsVersionInfo.dwPlatformId;
  233. if ((Hr = g_DbgClient->QueryInterface(IID_IDebugControl,
  234. (void **)&g_DbgControl)) != S_OK ||
  235. (Hr = g_DbgClient->QueryInterface(IID_IDebugSymbols,
  236. (void **)&g_DbgSymbols)) != S_OK ||
  237. (Hr = g_DbgClient->QueryInterface(IID_IDebugRegisters,
  238. (void **)&g_DbgRegisters)) != S_OK)
  239. {
  240. ErrorExit("Debug engine base queries failed, %s\n \"%s\"\n",
  241. FormatStatusCode(Hr), FormatStatus(Hr));
  242. }
  243. // Queries for higher-version interfaces. These can
  244. // fail if this executable is run against an older engine.
  245. // This is highly unlikely since everything is shipped
  246. // as a set, but handle it anyway.
  247. if ((Hr = g_DbgClient->QueryInterface(IID_IDebugClient2,
  248. (void **)&g_DbgClient2)) != S_OK &&
  249. Hr != E_NOINTERFACE &&
  250. Hr != RPC_E_VERSION_MISMATCH)
  251. {
  252. ErrorExit("Debug engine base queries failed, %s\n \"%s\"\n",
  253. FormatStatusCode(Hr), FormatStatus(Hr));
  254. }
  255. g_DbgClient->SetInputCallbacks(&g_ConInputCb);
  256. g_DbgClient->SetOutputCallbacks(&g_ConOutputCb);
  257. g_DbgClient->SetEventCallbacks(&g_EventCb);
  258. if (!g_RemoteClient)
  259. {
  260. //
  261. // Check environment variables to determine if any logfile needs to be
  262. // opened.
  263. //
  264. PSTR LogFile;
  265. BOOL Append;
  266. LogFile = getenv("_NT_DEBUG_LOG_FILE_APPEND");
  267. if (LogFile != NULL)
  268. {
  269. Append = TRUE;
  270. }
  271. else
  272. {
  273. Append = FALSE;
  274. LogFile = getenv("_NT_DEBUG_LOG_FILE_OPEN");
  275. }
  276. if (LogFile != NULL)
  277. {
  278. g_DbgControl->OpenLogFile(LogFile, Append);
  279. }
  280. }
  281. InitDynamicCalls();
  282. }
  283. void
  284. CreateEngine(PCSTR RemoteOptions)
  285. {
  286. HRESULT Hr;
  287. if ((Hr = DebugCreate(IID_IDebugClient,
  288. (void **)&g_DbgClient)) != S_OK)
  289. {
  290. ErrorExit("DebugCreate failed, %s\n \"%s\"\n",
  291. FormatStatusCode(Hr), FormatStatus(Hr));
  292. }
  293. if (RemoteOptions != NULL)
  294. {
  295. if ((Hr = g_DbgClient->StartServer(RemoteOptions)) != S_OK)
  296. {
  297. ErrorExit("StartServer failed, %s\n \"%s\"\n",
  298. FormatStatusCode(Hr), FormatStatus(Hr));
  299. }
  300. }
  301. DefaultEngineInitialize();
  302. }
  303. void
  304. ConnectEngine(PCSTR RemoteOptions)
  305. {
  306. HRESULT Hr;
  307. if ((Hr = DebugConnect(RemoteOptions, IID_IDebugClient,
  308. (void **)&g_DbgClient)) != S_OK)
  309. {
  310. ErrorExit("DebugCreate failed, %s\n \"%s\"\n",
  311. FormatStatusCode(Hr), FormatStatus(Hr));
  312. }
  313. DefaultEngineInitialize();
  314. }
  315. void
  316. InitializeSession(void)
  317. {
  318. HRESULT Hr;
  319. if (g_DumpFile != NULL)
  320. {
  321. if (g_DumpPageFile != NULL)
  322. {
  323. if (g_DbgClient2 == NULL)
  324. {
  325. ErrorExit("Debugger does not support extra dump files\n");
  326. }
  327. if ((Hr = g_DbgClient2->AddDumpInformationFile
  328. (g_DumpPageFile, DEBUG_DUMP_FILE_PAGE_FILE_DUMP)) != S_OK)
  329. {
  330. ErrorExit("Unable to use '%s', %s\n \"%s\"\n",
  331. g_DumpPageFile,
  332. FormatStatusCode(Hr), FormatStatus(Hr));
  333. }
  334. }
  335. Hr = g_DbgClient->OpenDumpFile(g_DumpFile);
  336. }
  337. else if (g_CommandLine != NULL ||
  338. g_PidToDebug != 0 ||
  339. g_ProcNameToDebug != NULL)
  340. {
  341. ULONG64 Server = 0;
  342. if (g_ProcessServer != NULL)
  343. {
  344. Hr = g_DbgClient->ConnectProcessServer(g_ProcessServer,
  345. &Server);
  346. if (Hr != S_OK)
  347. {
  348. ErrorExit("Unable to connect to process server, %s\n"
  349. " \"%s\"\n", FormatStatusCode(Hr),
  350. FormatStatus(Hr));
  351. }
  352. }
  353. ULONG Pid;
  354. if (g_ProcNameToDebug != NULL)
  355. {
  356. Hr = g_DbgClient->GetRunningProcessSystemIdByExecutableName
  357. (Server, g_ProcNameToDebug, DEBUG_GET_PROC_ONLY_MATCH, &Pid);
  358. if (Hr != S_OK)
  359. {
  360. ErrorExit("Unable to find process '%s', %s\n \"%s\"\n",
  361. g_ProcNameToDebug, FormatStatusCode(Hr),
  362. FormatStatus(Hr));
  363. }
  364. }
  365. else
  366. {
  367. Pid = g_PidToDebug;
  368. }
  369. Hr = g_DbgClient->CreateProcessAndAttach(Server,
  370. g_CommandLine, g_CreateFlags,
  371. Pid, g_AttachProcessFlags);
  372. if (g_DetachOnExitRequired &&
  373. g_DbgClient->
  374. AddProcessOptions(DEBUG_PROCESS_DETACH_ON_EXIT) != S_OK)
  375. {
  376. ErrorExit("%s: The system does not support detach on exit\n",
  377. g_DebuggerName);
  378. }
  379. else if (g_DetachOnExitImplied)
  380. {
  381. // The detach-on-exit is not required so don't check for
  382. // failures. This is necessary for the -- case where
  383. // detach-on-exit is implied but must work on systems
  384. // with and without the detach-on-exit support.
  385. g_DbgClient->AddProcessOptions(DEBUG_PROCESS_DETACH_ON_EXIT);
  386. }
  387. if (Server != 0)
  388. {
  389. g_DbgClient->DisconnectProcessServer(Server);
  390. }
  391. }
  392. else
  393. {
  394. Hr = g_DbgClient->AttachKernel(g_AttachKernelFlags, g_ConnectOptions);
  395. }
  396. if (Hr != S_OK)
  397. {
  398. ErrorExit("Debuggee initialization failed, %s\n \"%s\"\n",
  399. FormatStatusCode(Hr), FormatStatus(Hr));
  400. }
  401. }
  402. BOOL WINAPI
  403. InterruptHandler(
  404. IN ULONG CtrlType
  405. )
  406. {
  407. if (CtrlType == CTRL_C_EVENT || CtrlType == CTRL_BREAK_EVENT)
  408. {
  409. PDEBUG_CONTROL Control =
  410. g_RemoteClient ? g_ConControl : g_DbgControl;
  411. if (Control != NULL)
  412. {
  413. Control->SetInterrupt(DEBUG_INTERRUPT_ACTIVE);
  414. }
  415. else
  416. {
  417. ConOut("Debugger not initialized, cannot interrupt\n");
  418. }
  419. return TRUE;
  420. }
  421. else
  422. {
  423. return FALSE;
  424. }
  425. }
  426. BOOL
  427. APIENTRY
  428. MyCreatePipeEx(
  429. OUT LPHANDLE lpReadPipe,
  430. OUT LPHANDLE lpWritePipe,
  431. IN LPSECURITY_ATTRIBUTES lpPipeAttributes,
  432. IN DWORD nSize,
  433. DWORD dwReadMode,
  434. DWORD dwWriteMode
  435. )
  436. /*++
  437. Routine Description:
  438. The CreatePipeEx API is used to create an anonymous pipe I/O device.
  439. Unlike CreatePipe FILE_FLAG_OVERLAPPED may be specified for one or
  440. both handles.
  441. Two handles to the device are created. One handle is opened for
  442. reading and the other is opened for writing. These handles may be
  443. used in subsequent calls to ReadFile and WriteFile to transmit data
  444. through the pipe.
  445. Arguments:
  446. lpReadPipe - Returns a handle to the read side of the pipe. Data
  447. may be read from the pipe by specifying this handle value in a
  448. subsequent call to ReadFile.
  449. lpWritePipe - Returns a handle to the write side of the pipe. Data
  450. may be written to the pipe by specifying this handle value in a
  451. subsequent call to WriteFile.
  452. lpPipeAttributes - An optional parameter that may be used to specify
  453. the attributes of the new pipe. If the parameter is not
  454. specified, then the pipe is created without a security
  455. descriptor, and the resulting handles are not inherited on
  456. process creation. Otherwise, the optional security attributes
  457. are used on the pipe, and the inherit handles flag effects both
  458. pipe handles.
  459. nSize - Supplies the requested buffer size for the pipe. This is
  460. only a suggestion and is used by the operating system to
  461. calculate an appropriate buffering mechanism. A value of zero
  462. indicates that the system is to choose the default buffering
  463. scheme.
  464. Return Value:
  465. TRUE - The operation was successful.
  466. FALSE/NULL - The operation failed. Extended error status is available
  467. using GetLastError.
  468. --*/
  469. {
  470. static ULONG PipeSerialNumber;
  471. HANDLE ReadPipeHandle, WritePipeHandle;
  472. DWORD dwError;
  473. UCHAR PipeNameBuffer[ MAX_PATH ];
  474. //
  475. // Only one valid OpenMode flag - FILE_FLAG_OVERLAPPED
  476. //
  477. if ((dwReadMode | dwWriteMode) & (~FILE_FLAG_OVERLAPPED)) {
  478. SetLastError(ERROR_INVALID_PARAMETER);
  479. return FALSE;
  480. }
  481. //
  482. // Set the default timeout to 120 seconds
  483. //
  484. if (nSize == 0) {
  485. nSize = 4096;
  486. }
  487. sprintf( (char *)PipeNameBuffer,
  488. "\\\\.\\Pipe\\Win32PipesEx.%08x.%08x",
  489. GetCurrentProcessId(),
  490. PipeSerialNumber++
  491. );
  492. ReadPipeHandle = CreateNamedPipeA(
  493. (char *)PipeNameBuffer,
  494. PIPE_ACCESS_INBOUND | dwReadMode,
  495. PIPE_TYPE_BYTE | PIPE_WAIT,
  496. 1, // Number of pipes
  497. nSize, // Out buffer size
  498. nSize, // In buffer size
  499. 120 * 1000, // Timeout in ms
  500. lpPipeAttributes
  501. );
  502. if (ReadPipeHandle == INVALID_HANDLE_VALUE) {
  503. return FALSE;
  504. }
  505. WritePipeHandle = CreateFileA(
  506. (char *)PipeNameBuffer,
  507. GENERIC_WRITE,
  508. 0, // No sharing
  509. lpPipeAttributes,
  510. OPEN_EXISTING,
  511. FILE_ATTRIBUTE_NORMAL | dwWriteMode,
  512. NULL // Template file
  513. );
  514. if (INVALID_HANDLE_VALUE == WritePipeHandle) {
  515. dwError = GetLastError();
  516. CloseHandle( ReadPipeHandle );
  517. SetLastError(dwError);
  518. return FALSE;
  519. }
  520. *lpReadPipe = ReadPipeHandle;
  521. *lpWritePipe = WritePipeHandle;
  522. return( TRUE );
  523. }
  524. void
  525. StartRemote(
  526. PCSTR Args
  527. )
  528. /*++
  529. Routine Description:
  530. "remotes" the current debugger by starting a copy of remote.exe in a
  531. special mode that causes it to attach to us, the debugger, as its
  532. "child" process.
  533. Arguments:
  534. Args - Name of the pipe to use for this remote session, e.g. "ntsd" means
  535. to connect one would use "remote /c machinename ntsd".
  536. Return Value:
  537. None.
  538. --*/
  539. {
  540. static BOOL fRemoteIsRunning;
  541. HANDLE hRemoteChildProcess;
  542. HANDLE hOrgStdIn;
  543. HANDLE hOrgStdOut;
  544. HANDLE hOrgStdErr;
  545. HANDLE hNewStdIn;
  546. HANDLE hRemoteWriteChildStdIn;
  547. HANDLE hNewStdOut;
  548. HANDLE hRemoteReadChildStdOut;
  549. HANDLE hNewStdErr;
  550. SECURITY_ATTRIBUTES sa;
  551. STARTUPINFO si;
  552. PROCESS_INFORMATION pi;
  553. char szCmd[64];
  554. if (Args == NULL)
  555. {
  556. goto DotRemoteUsage;
  557. }
  558. while (*Args == ' ' || *Args == '\t')
  559. {
  560. Args++;
  561. }
  562. if (!Args[0])
  563. {
  564. goto DotRemoteUsage;
  565. }
  566. if (g_PipeWrite != NULL)
  567. {
  568. ConOut("An input thread has already been started so .remote\n");
  569. ConOut("cannot be used. Either start the debugger with\n");
  570. ConOut("remote.exe, such as remote /s \"kd\" pipe; or use\n");
  571. ConOut("debugger remoting with -server/-client/.server.\n");
  572. return;
  573. }
  574. if (fRemoteIsRunning)
  575. {
  576. ConOut(".remote: can't .remote twice.\n");
  577. goto Cleanup;
  578. }
  579. if (g_IoMode != IO_CONSOLE)
  580. {
  581. ConOut(".remote: can't .remote when using -d. "
  582. "Remote the kernel debugger instead.\n");
  583. goto Cleanup;
  584. }
  585. ConOut("Starting remote with pipename '%s'\n", Args);
  586. //
  587. // We'll pass remote.exe inheritable handles to this process,
  588. // our standard in/out handles (for it to use as stdin/stdout),
  589. // and pipe handles for it to write to our new stdin and read
  590. // from our new stdout.
  591. //
  592. //
  593. // Get an inheritable handle to our process.
  594. //
  595. if ( ! DuplicateHandle(
  596. GetCurrentProcess(), // src process
  597. GetCurrentProcess(), // src handle
  598. GetCurrentProcess(), // targ process
  599. &hRemoteChildProcess, // targ handle
  600. 0, // access
  601. TRUE, // inheritable
  602. DUPLICATE_SAME_ACCESS // options
  603. ))
  604. {
  605. ConOut(".remote: Unable to duplicate process handle.\n");
  606. goto Cleanup;
  607. }
  608. //
  609. // Get inheritable copies of our current stdin, stdout, stderr which
  610. // we'll use for same for remote.exe when we spawn it.
  611. //
  612. hOrgStdIn = g_ConInput;
  613. hOrgStdOut = g_ConOutput;
  614. hOrgStdErr = GetStdHandle(STD_ERROR_HANDLE);
  615. sa.nLength = sizeof(sa);
  616. sa.lpSecurityDescriptor = NULL;
  617. sa.bInheritHandle = TRUE;
  618. //
  619. // Create remote->ntsd pipe, our end of which will be our
  620. // new stdin. The remote.exe end needs to be opened
  621. // for overlapped I/O, so yet another copy of MyCreatePipeEx
  622. // spreads through our source base.
  623. //
  624. if ( ! MyCreatePipeEx(
  625. &hNewStdIn, // read handle
  626. &hRemoteWriteChildStdIn, // write handle
  627. &sa, // security
  628. 0, // size
  629. 0, // read handle overlapped?
  630. FILE_FLAG_OVERLAPPED // write handle overlapped?
  631. ))
  632. {
  633. ConOut(".remote: Unable to create stdin pipe.\n");
  634. CloseHandle(hRemoteChildProcess);
  635. goto Cleanup;
  636. }
  637. //
  638. // We don't want remote.exe to inherit our end of the pipe
  639. // so duplicate it to a non-inheritable one.
  640. //
  641. if ( ! DuplicateHandle(
  642. GetCurrentProcess(), // src process
  643. hNewStdIn, // src handle
  644. GetCurrentProcess(), // targ process
  645. &hNewStdIn, // targ handle
  646. 0, // access
  647. FALSE, // inheritable
  648. DUPLICATE_SAME_ACCESS |
  649. DUPLICATE_CLOSE_SOURCE // options
  650. ))
  651. {
  652. ConOut(".remote: Unable to duplicate stdout handle.\n");
  653. CloseHandle(hRemoteChildProcess);
  654. CloseHandle(hRemoteWriteChildStdIn);
  655. goto Cleanup;
  656. }
  657. //
  658. // Create ntsd->remote pipe, our end of which will be our
  659. // new stdout and stderr.
  660. //
  661. if ( ! MyCreatePipeEx(
  662. &hRemoteReadChildStdOut, // read handle
  663. &hNewStdOut, // write handle
  664. &sa, // security
  665. 0, // size
  666. FILE_FLAG_OVERLAPPED, // read handle overlapped?
  667. 0 // write handle overlapped?
  668. ))
  669. {
  670. ConOut(".remote: Unable to create stdout pipe.\n");
  671. CloseHandle(hRemoteChildProcess);
  672. CloseHandle(hRemoteWriteChildStdIn);
  673. CloseHandle(hNewStdIn);
  674. goto Cleanup;
  675. }
  676. //
  677. // We don't want remote.exe to inherit our end of the pipe
  678. // so duplicate it to a non-inheritable one.
  679. //
  680. if ( ! DuplicateHandle(
  681. GetCurrentProcess(), // src process
  682. hNewStdOut, // src handle
  683. GetCurrentProcess(), // targ process
  684. &hNewStdOut, // targ handle
  685. 0, // access
  686. FALSE, // inheritable
  687. DUPLICATE_SAME_ACCESS |
  688. DUPLICATE_CLOSE_SOURCE // options
  689. ))
  690. {
  691. ConOut(".remote: Unable to duplicate stdout handle.\n");
  692. CloseHandle(hRemoteChildProcess);
  693. CloseHandle(hRemoteWriteChildStdIn);
  694. CloseHandle(hNewStdIn);
  695. CloseHandle(hRemoteReadChildStdOut);
  696. goto Cleanup;
  697. }
  698. //
  699. // Duplicate our new stdout to a new stderr.
  700. //
  701. if ( ! DuplicateHandle(
  702. GetCurrentProcess(), // src process
  703. hNewStdOut, // src handle
  704. GetCurrentProcess(), // targ process
  705. &hNewStdErr, // targ handle
  706. 0, // access
  707. FALSE, // inheritable
  708. DUPLICATE_SAME_ACCESS // options
  709. ))
  710. {
  711. ConOut(".remote: Unable to duplicate stdout handle.\n");
  712. CloseHandle(hRemoteChildProcess);
  713. CloseHandle(hRemoteWriteChildStdIn);
  714. CloseHandle(hNewStdIn);
  715. CloseHandle(hRemoteReadChildStdOut);
  716. CloseHandle(hNewStdOut);
  717. goto Cleanup;
  718. }
  719. //
  720. // We now have all the handles we need. Let's launch remote.
  721. //
  722. sprintf(
  723. szCmd,
  724. "remote.exe /a %d %d %d %s %s",
  725. HandleToUlong(hRemoteChildProcess),
  726. HandleToUlong(hRemoteWriteChildStdIn),
  727. HandleToUlong(hRemoteReadChildStdOut),
  728. g_DebuggerName,
  729. Args
  730. );
  731. ZeroMemory(&si, sizeof(si));
  732. si.cb = sizeof(si);
  733. si.dwFlags = STARTF_USESTDHANDLES;
  734. si.hStdInput = hOrgStdIn;
  735. si.hStdOutput = hOrgStdOut;
  736. si.hStdError = hOrgStdErr;
  737. si.wShowWindow = SW_SHOW;
  738. //
  739. // Create Child Process
  740. //
  741. if ( ! CreateProcess(
  742. NULL,
  743. szCmd,
  744. NULL,
  745. NULL,
  746. TRUE,
  747. GetPriorityClass( GetCurrentProcess() ),
  748. NULL,
  749. NULL,
  750. &si,
  751. &pi))
  752. {
  753. if (GetLastError() == ERROR_FILE_NOT_FOUND)
  754. {
  755. ConOut("remote.exe not found\n");
  756. }
  757. else
  758. {
  759. ConOut("CreateProcess(%s) failed, error %d.\n",
  760. szCmd, GetLastError());
  761. }
  762. CloseHandle(hRemoteChildProcess);
  763. CloseHandle(hRemoteWriteChildStdIn);
  764. CloseHandle(hNewStdIn);
  765. CloseHandle(hRemoteReadChildStdOut);
  766. CloseHandle(hNewStdOut);
  767. CloseHandle(hNewStdErr);
  768. goto Cleanup;
  769. }
  770. CloseHandle(hRemoteChildProcess);
  771. CloseHandle(hRemoteWriteChildStdIn);
  772. CloseHandle(hRemoteReadChildStdOut);
  773. CloseHandle(pi.hThread);
  774. CloseHandle(pi.hProcess);
  775. //
  776. // Switch to using the new handles. Might be nice to
  777. // start a thread here to watch for remote.exe dying
  778. // and switch back to the old handles.
  779. //
  780. // CloseHandle(hOrgStdIn);
  781. if (g_PromptInput == g_ConInput)
  782. {
  783. g_PromptInput = hNewStdIn;
  784. }
  785. g_ConInput = hNewStdIn;
  786. SetStdHandle(STD_INPUT_HANDLE, hNewStdIn);
  787. // CloseHandle(hOrgStdOut);
  788. g_ConOutput = hNewStdOut;
  789. SetStdHandle(STD_OUTPUT_HANDLE, hNewStdOut);
  790. // CloseHandle(hOrgStdErr);
  791. SetStdHandle(STD_ERROR_HANDLE, hNewStdErr);
  792. fRemoteIsRunning = TRUE;
  793. ConOut("%s: now running under remote.exe pipename %s\n",
  794. g_DebuggerName, Args);
  795. Cleanup:
  796. return;
  797. DotRemoteUsage:
  798. ConOut("Usage: .remote pipename\n");
  799. }
  800. BOOL
  801. UiCommand(PSTR Command)
  802. {
  803. char Term;
  804. PSTR Scan, Arg;
  805. //
  806. // Check and see if this is a UI command
  807. // vs. a command that should go to the engine.
  808. //
  809. while (isspace(*Command))
  810. {
  811. Command++;
  812. }
  813. Scan = Command;
  814. while (*Scan && !isspace(*Scan))
  815. {
  816. Scan++;
  817. }
  818. Term = *Scan;
  819. *Scan = 0;
  820. // Advance to next nonspace char for arguments.
  821. if (Term != 0)
  822. {
  823. Arg = Scan + 1;
  824. while (isspace(*Arg))
  825. {
  826. Arg++;
  827. }
  828. if (*Arg == 0)
  829. {
  830. Arg = NULL;
  831. }
  832. }
  833. else
  834. {
  835. Arg = NULL;
  836. }
  837. if (!_strcmpi(Command, ".hh"))
  838. {
  839. if (Arg == NULL)
  840. {
  841. OpenHelpTopic(HELP_TOPIC_TABLE_OF_CONTENTS);
  842. }
  843. else
  844. {
  845. OpenHelpIndex(Arg);
  846. }
  847. }
  848. else if (!_strcmpi(Command, ".remote"))
  849. {
  850. StartRemote(Arg);
  851. }
  852. else if (!_strcmpi(Command, ".restart"))
  853. {
  854. if (g_RemoteClient)
  855. {
  856. ConOut("Only the primary debugger can restart\n");
  857. }
  858. else if (g_PidToDebug != 0 ||
  859. g_ProcNameToDebug != NULL)
  860. {
  861. ConOut("Process attaches cannot be restarted. If you want to\n"
  862. "restart the process, use !peb to get what command line\n"
  863. "to use and other initialization information.\n");
  864. }
  865. else
  866. {
  867. g_DbgClient->EndSession(DEBUG_END_ACTIVE_TERMINATE);
  868. g_Restarting = TRUE;
  869. }
  870. }
  871. else if (!_strcmpi(Command, ".server"))
  872. {
  873. // We need to start a separate input thread when
  874. // using remoting but we do not actually handle
  875. // the command.
  876. CreateInputThread();
  877. *Scan = Term;
  878. return FALSE;
  879. }
  880. else if (Command[0] == '$' && Command[1] == '<')
  881. {
  882. *Scan = Term;
  883. if (g_NextOldInputFile >= MAX_INPUT_NESTING)
  884. {
  885. ConOut("Scripts too deeply nested\n");
  886. }
  887. else
  888. {
  889. FILE* Script = fopen(Command + 2, "r");
  890. if (Script == NULL)
  891. {
  892. ConOut("Unable to open '%s'\n", Command + 2);
  893. }
  894. else
  895. {
  896. g_OldInputFiles[g_NextOldInputFile++] = g_InputFile;
  897. g_InputFile = Script;
  898. }
  899. }
  900. }
  901. else
  902. {
  903. *Scan = Term;
  904. return FALSE;
  905. }
  906. return TRUE;
  907. }
  908. BOOL
  909. CallBugCheckExtension(
  910. void
  911. )
  912. {
  913. HRESULT Status = E_FAIL;
  914. ULONG Code;
  915. ULONG64 Args[4];
  916. // Run the bugcheck analyzers if this dump has a bugcheck.
  917. if (g_DbgControl->ReadBugCheckData(&Code, &Args[0], &Args[1], &Args[2], &Args[3]) != S_OK ||
  918. Code == 0)
  919. {
  920. return FALSE;
  921. }
  922. if (g_DbgClient != NULL)
  923. {
  924. char ExtName[32];
  925. // Extension name has to be in writable memory as it
  926. // gets lower-cased.
  927. strcpy(ExtName, "AnalyzeBugCheck");
  928. // See if any existing extension DLLs are interested
  929. // in analyzing this bugcheck.
  930. Status = g_DbgControl->CallExtension(NULL, ExtName, "");
  931. }
  932. if (Status != S_OK)
  933. {
  934. if (g_DbgClient == NULL)
  935. {
  936. ConOut("WARNING: Unable to locate a client for "
  937. "bugcheck analysis\n");
  938. }
  939. ConOut("*******************************************************************************\n");
  940. ConOut("* *\n");
  941. ConOut("* Bugcheck Analysis *\n");
  942. ConOut("* *\n");
  943. ConOut("*******************************************************************************\n");
  944. g_DbgControl->Execute(DEBUG_OUTCTL_AMBIENT, ".bugcheck", DEBUG_EXECUTE_DEFAULT);
  945. ConOut("\n");
  946. g_DbgControl->Execute(DEBUG_OUTCTL_AMBIENT, "kb", DEBUG_EXECUTE_DEFAULT);
  947. ConOut("\n");
  948. } else
  949. {
  950. return TRUE;
  951. }
  952. return FALSE;
  953. }
  954. BOOL
  955. MainLoop(void)
  956. {
  957. HRESULT Hr;
  958. BOOL SessionEnded = FALSE;
  959. ULONG64 InstructionOffset;
  960. DEBUG_STACK_FRAME StkFrame;
  961. ULONG Class, Qual;
  962. if (!SetConsoleCtrlHandler(InterruptHandler, TRUE))
  963. {
  964. ConOut("Warning: unable to set Control-C handler.\n");
  965. }
  966. // Get initial status.
  967. g_DbgControl->GetExecutionStatus(&g_ExecStatus);
  968. g_DbgControl->GetDebuggeeType(&Class, &Qual);
  969. while (!g_Exit)
  970. {
  971. if (!g_RemoteClient)
  972. {
  973. Hr = g_DbgControl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE);
  974. if (FAILED(Hr))
  975. {
  976. // The debug session may have ended. If so, just exit.
  977. if (g_DbgControl->GetExecutionStatus(&g_ExecStatus) == S_OK &&
  978. g_ExecStatus == DEBUG_STATUS_NO_DEBUGGEE)
  979. {
  980. SessionEnded = TRUE;
  981. break;
  982. }
  983. // Inform the user of the failure and go to
  984. // command processing.
  985. ConOut("WaitForEvent failed, %s\n \"%s\"\n",
  986. FormatStatusCode(Hr), FormatStatus(Hr));
  987. }
  988. // By far the most likely reason for WaitForEvent to
  989. // fail on a dump is bad symbols, which would produce
  990. // further errors when trying to use processor state.
  991. // Avoid doing so in the dump case.
  992. if (FAILED(Hr) && g_DumpFile != NULL)
  993. {
  994. ConOut("When WaitForEvent fails on dump files the "
  995. "current state is not displayed\n");
  996. }
  997. else
  998. {
  999. BOOL DisplayRegs = TRUE;
  1000. if (Class == DEBUG_CLASS_KERNEL &&
  1001. (Qual == DEBUG_DUMP_SMALL || Qual == DEBUG_DUMP_DEFAULT ||
  1002. Qual == DEBUG_DUMP_FULL))
  1003. {
  1004. if (CallBugCheckExtension())
  1005. {
  1006. DisplayRegs = FALSE;
  1007. }
  1008. }
  1009. if (DisplayRegs)
  1010. {
  1011. // Dump registers and such.
  1012. g_DbgControl->OutputCurrentState(DEBUG_OUTCTL_ALL_CLIENTS,
  1013. DEBUG_CURRENT_DEFAULT);
  1014. }
  1015. }
  1016. }
  1017. while (!g_Exit && g_ExecStatus == DEBUG_STATUS_BREAK)
  1018. {
  1019. if (g_IoMode == IO_NONE)
  1020. {
  1021. // This is a pure remoting server with no
  1022. // local user. Just wait for a remote client
  1023. // to get things running again.
  1024. Hr = g_DbgClient->DispatchCallbacks(INFINITE);
  1025. if (Hr != S_OK)
  1026. {
  1027. OutputDebugString("Unable to dispatch callbacks\n");
  1028. ExitDebugger(Hr);
  1029. }
  1030. }
  1031. else
  1032. {
  1033. char Command[MAX_COMMAND];
  1034. g_DbgControl->OutputPrompt(DEBUG_OUTCTL_THIS_CLIENT |
  1035. DEBUG_OUTCTL_NOT_LOGGED, " ");
  1036. if (ConIn(Command, sizeof(Command), TRUE))
  1037. {
  1038. if (g_RemoteClient)
  1039. {
  1040. // Identify self before command.
  1041. g_DbgClient->
  1042. OutputIdentity(DEBUG_OUTCTL_ALL_OTHER_CLIENTS,
  1043. DEBUG_OUTPUT_IDENTITY_DEFAULT,
  1044. "[%s] ");
  1045. }
  1046. g_DbgControl->OutputPrompt(DEBUG_OUTCTL_ALL_OTHER_CLIENTS,
  1047. " %s\n", Command);
  1048. // Intercept and handle UI commands.
  1049. if (!UiCommand(Command))
  1050. {
  1051. // Must be an engine command.
  1052. g_DbgControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS,
  1053. Command,
  1054. DEBUG_EXECUTE_NOT_LOGGED);
  1055. }
  1056. }
  1057. }
  1058. }
  1059. if (g_Restarting)
  1060. {
  1061. InitializeSession();
  1062. g_Restarting = FALSE;
  1063. continue;
  1064. }
  1065. if (Class != DEBUG_CLASS_USER_WINDOWS)
  1066. {
  1067. // The kernel debugger doesn't exit when the machine reboots.
  1068. g_Exit = FALSE;
  1069. }
  1070. else
  1071. {
  1072. g_Exit = g_ExecStatus == DEBUG_STATUS_NO_DEBUGGEE;
  1073. if (g_Exit)
  1074. {
  1075. SessionEnded = TRUE;
  1076. break;
  1077. }
  1078. }
  1079. if (g_RemoteClient)
  1080. {
  1081. Hr = g_DbgClient->DispatchCallbacks(INFINITE);
  1082. if (Hr != S_OK)
  1083. {
  1084. OutputDebugString("Unable to dispatch callbacks\n");
  1085. ExitDebugger(Hr);
  1086. }
  1087. }
  1088. }
  1089. return SessionEnded;
  1090. }