Leaked source code of windows server 2003
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.

1397 lines
40 KiB

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