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.

662 lines
15 KiB

  1. // TITLE("Debug Support Functions")
  2. //++
  3. //
  4. // Copyright (c) 1990 Microsoft Corporation
  5. //
  6. // Module Name:
  7. //
  8. // debug.c
  9. //
  10. // Abstract:
  11. //
  12. // This module implements functions to support debugging NT. They call
  13. // architecture specific routines to do the actual work.
  14. //
  15. // Author:
  16. //
  17. // Steven R. Wood (stevewo) 8-Nov-1994
  18. //
  19. // Environment:
  20. //
  21. // Any mode.
  22. //
  23. // Revision History:
  24. //
  25. //--
  26. #include "stdarg.h"
  27. #include "stdio.h"
  28. #include "ntrtlp.h"
  29. #define NOEXTAPI
  30. #include "wdbgexts.h"
  31. #include <ntdbg.h>
  32. #if !defined(BLDR_KERNEL_RUNTIME) || (defined(BLDR_KERNEL_RUNTIME) && defined(ENABLE_LOADER_DEBUG))
  33. ULONG
  34. DbgPrint(
  35. IN PCHAR Format,
  36. ...
  37. )
  38. //++
  39. //
  40. // Routine Description:
  41. //
  42. // This routine provides a "printf" style capability for the kernel
  43. // debugger.
  44. //
  45. // Note: control-C is consumed by the debugger and returned to
  46. // this routine as status. If status indicates control-C was
  47. // pressed, this routine breakpoints.
  48. //
  49. // Arguments:
  50. //
  51. // Format - printf style format string
  52. // ... - additional arguments consumed according to the
  53. // format string.
  54. //
  55. // Return Value:
  56. //
  57. // Defined as returning a ULONG, actually returns status.
  58. //
  59. //--
  60. {
  61. va_list arglist;
  62. va_start(arglist, Format);
  63. return vDbgPrintExWithPrefix("", -1, 0, Format, arglist);
  64. }
  65. ULONG
  66. DbgPrintEx(
  67. IN ULONG ComponentId,
  68. IN ULONG Level,
  69. PCHAR Format,
  70. ...
  71. )
  72. //++
  73. //
  74. // Routine Description:
  75. //
  76. // This routine provides a "printf" style capability for the kernel
  77. // debugger.
  78. //
  79. // Note: control-C is consumed by the debugger and returned to
  80. // this routine as status. If status indicates control-C was
  81. // pressed, this routine breakpoints.
  82. //
  83. // Arguments:
  84. //
  85. // ComponentId - Supplies the Id of the calling component.
  86. // Level - Supplies the output filter level.
  87. // Format - printf style format string
  88. // ... - additional arguments consumed according to the
  89. // format string.
  90. //
  91. // Return Value:
  92. //
  93. // Defined as returning a ULONG, actually returns status.
  94. //
  95. //--
  96. {
  97. va_list arglist;
  98. va_start(arglist, Format);
  99. return vDbgPrintExWithPrefix("", ComponentId, Level, Format, arglist);
  100. }
  101. ULONG
  102. vDbgPrintEx(
  103. IN ULONG ComponentId,
  104. IN ULONG Level,
  105. IN PCHAR Format,
  106. va_list arglist
  107. )
  108. //++
  109. //
  110. // Routine Description:
  111. //
  112. // This routine provides a "printf" style capability for the kernel
  113. // debugger.
  114. //
  115. // Note: control-C is consumed by the debugger and returned to
  116. // this routine as status. If status indicates control-C was
  117. // pressed, this routine breakpoints.
  118. //
  119. // Arguments:
  120. //
  121. // ComponentId - Supplies the Id of the calling component.
  122. //
  123. // Level - Supplies the output filter level or mask.
  124. //
  125. // Arguments - Supplies a pointer to a variable argument list.
  126. //
  127. // Return Value:
  128. //
  129. // Defined as returning a ULONG, actually returns status.
  130. //
  131. //--
  132. {
  133. return vDbgPrintExWithPrefix("", ComponentId, Level, Format, arglist);
  134. }
  135. ULONG
  136. vDbgPrintExWithPrefix(
  137. IN PCH Prefix,
  138. IN ULONG ComponentId,
  139. IN ULONG Level,
  140. IN PCHAR Format,
  141. va_list arglist
  142. )
  143. //++
  144. //
  145. // Routine Description:
  146. //
  147. // This routine provides a "printf" style capability for the kernel
  148. // debugger.
  149. //
  150. // Note: control-C is consumed by the debugger and returned to
  151. // this routine as status. If status indicates control-C was
  152. // pressed, this routine breakpoints.
  153. //
  154. // Arguments:
  155. //
  156. // Prefix - Supplies a pointer to text that is to prefix the formatted
  157. // output.
  158. //
  159. // ComponentId - Supplies the Id of the calling component.
  160. //
  161. // Level - Supplies the output filter level or mask.
  162. //
  163. // Arguments - Supplies a pointer to a variable argument list.
  164. //
  165. // Return Value:
  166. //
  167. // Defined as returning a ULONG, actually returns status.
  168. //
  169. //--
  170. {
  171. UCHAR Buffer[512];
  172. int cb;
  173. STRING Output;
  174. NTSTATUS Status = STATUS_SUCCESS;
  175. //
  176. // If the debug output will be suppressed, then return success
  177. // immediately.
  178. //
  179. #if !defined(BLDR_KERNEL_RUNTIME)
  180. if ((ComponentId != -1) &&
  181. (NtQueryDebugFilterState(ComponentId, Level) == FALSE)) {
  182. return STATUS_SUCCESS;
  183. }
  184. #endif
  185. #if !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
  186. if (NtCurrentTeb()->InDbgPrint) {
  187. return STATUS_SUCCESS;
  188. }
  189. NtCurrentTeb()->InDbgPrint = TRUE;
  190. #endif
  191. //
  192. // Format the output into a buffer and then print it.
  193. //
  194. #if !defined(BLDR_KERNEL_RUNTIME)
  195. try {
  196. cb = strlen(Prefix);
  197. strcpy(Buffer, Prefix);
  198. cb = _vsnprintf(Buffer + cb , sizeof(Buffer) - cb, Format, arglist) + cb;
  199. } except (EXCEPTION_EXECUTE_HANDLER) {
  200. Status = GetExceptionCode();
  201. }
  202. #else
  203. cb = strlen(Prefix);
  204. strcpy(Buffer, Prefix);
  205. cb = _vsnprintf(Buffer + cb, sizeof(Buffer) - cb, Format, arglist) + cb;
  206. #endif
  207. if (!NT_SUCCESS(Status)) {
  208. #if !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
  209. NtCurrentTeb()->InDbgPrint = FALSE;
  210. #endif
  211. return Status;
  212. }
  213. if (cb == -1) { // detect buffer overflow
  214. cb = sizeof(Buffer);
  215. Buffer[sizeof(Buffer) - 1] = '\n';
  216. }
  217. Output.Buffer = Buffer;
  218. Output.Length = (USHORT) cb;
  219. //
  220. // If APP is being debugged, raise an exception and the debugger
  221. // will catch and handle this. Otherwise, kernel debugger service
  222. // is called.
  223. //
  224. #if !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
  225. #if !i386
  226. //
  227. // For non-Intel architectures, can't raise exceptions until the PebLock
  228. // is initialized, since the Function Table lookup code uses the PebLock
  229. // to serialize access to the loaded module database. What a crock
  230. //
  231. if (NtCurrentPeb()->FastPebLockRoutine != NULL)
  232. #endif //!i386
  233. if (NtCurrentPeb()->BeingDebugged) {
  234. EXCEPTION_RECORD ExceptionRecord;
  235. //
  236. // Construct an exception record.
  237. //
  238. ExceptionRecord.ExceptionCode = DBG_PRINTEXCEPTION_C;
  239. ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
  240. ExceptionRecord.NumberParameters = 2;
  241. ExceptionRecord.ExceptionFlags = 0;
  242. ExceptionRecord.ExceptionInformation[ 0 ] = Output.Length + 1;
  243. ExceptionRecord.ExceptionInformation[ 1 ] = (ULONG_PTR)(Output.Buffer);
  244. try {
  245. RtlRaiseException( &ExceptionRecord );
  246. } except (EXCEPTION_EXECUTE_HANDLER) {
  247. }
  248. #if !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
  249. NtCurrentTeb()->InDbgPrint = FALSE;
  250. #endif
  251. return STATUS_SUCCESS;
  252. }
  253. #endif
  254. Status = DebugPrint(&Output, ComponentId, Level);
  255. if (Status == STATUS_BREAKPOINT) {
  256. DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
  257. Status = STATUS_SUCCESS;
  258. }
  259. #if !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
  260. NtCurrentTeb()->InDbgPrint = FALSE;
  261. #endif
  262. return Status;
  263. }
  264. ULONG
  265. DbgPrintReturnControlC(
  266. PCHAR Format,
  267. ...
  268. )
  269. //++
  270. //
  271. // Routine Description:
  272. //
  273. // This routine provides a "printf" style capability for the kernel
  274. // debugger.
  275. //
  276. // This routine is exactly the same as DbgPrint except that control-C
  277. // is NOT handled here. Instead, status indicating control-C is
  278. // returned to the caller to do with as they will.
  279. //
  280. // Arguments:
  281. //
  282. // Format - printf style format string
  283. // ... - additional arguments consumed according to the
  284. // format string.
  285. //
  286. // Return Value:
  287. //
  288. // Defined as returning a ULONG, actually returns status.
  289. //
  290. //--
  291. {
  292. va_list arglist;
  293. UCHAR Buffer[512];
  294. int cb;
  295. STRING Output;
  296. #if !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
  297. CONST PPEB Peb = NtCurrentPeb();
  298. #endif
  299. //
  300. // Format the output into a buffer and then print it.
  301. //
  302. va_start(arglist, Format);
  303. cb = _vsnprintf(Buffer, sizeof(Buffer), Format, arglist);
  304. if (cb == -1) { // detect buffer overflow
  305. cb = sizeof(Buffer);
  306. Buffer[sizeof(Buffer) - 1] = '\n';
  307. }
  308. Output.Buffer = Buffer;
  309. Output.Length = (USHORT) cb;
  310. //
  311. // If APP is being debugged, raise an exception and the debugger
  312. // will catch and handle this. Otherwise, kernel debugger service
  313. // is called.
  314. //
  315. #if !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
  316. #if !i386
  317. //
  318. // For non-Intel architectures, can't raise exceptions until the PebLock
  319. // is initialized, since the Function Table lookup code uses the PebLock
  320. // to serialize access to the loaded module database. What a crock
  321. //
  322. if (Peb->FastPebLockRoutine != NULL)
  323. //
  324. // For IA64 and probably AMD64, can't raise exceptions until ntdll is in
  325. // Peb->Ldr, so that RtlPcToFileHeader can find ntdll in Peb->Ldr. The
  326. // dbgprints / exceptions are necessarily from ntdll at this point.
  327. // The first two things in Peb->Ldr are the .exe and ntdll.dll, so
  328. // check that there are two things in the list.
  329. //
  330. if ((Peb->Ldr != NULL) &&
  331. (Peb->Ldr->InLoadOrderModuleList.Flink != &Peb->Ldr->InLoadOrderModuleList) &&
  332. (Peb->Ldr->InLoadOrderModuleList.Blink != Peb->Ldr->InLoadOrderModuleList.Flink))
  333. #endif //!i386
  334. if (Peb->BeingDebugged) {
  335. EXCEPTION_RECORD ExceptionRecord;
  336. //
  337. // Construct an exception record.
  338. //
  339. ExceptionRecord.ExceptionCode = DBG_PRINTEXCEPTION_C;
  340. ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
  341. ExceptionRecord.NumberParameters = 2;
  342. ExceptionRecord.ExceptionFlags = 0;
  343. ExceptionRecord.ExceptionInformation[ 0 ] = Output.Length + 1;
  344. ExceptionRecord.ExceptionInformation[ 1 ] = (ULONG_PTR)(Output.Buffer);
  345. try {
  346. RtlRaiseException( &ExceptionRecord );
  347. } except (EXCEPTION_EXECUTE_HANDLER) {
  348. }
  349. return STATUS_SUCCESS;
  350. }
  351. #endif
  352. return DebugPrint(&Output, 0, 0);
  353. }
  354. ULONG
  355. DbgPrompt(
  356. IN PCHAR Prompt,
  357. OUT PCHAR Response,
  358. IN ULONG MaximumResponseLength
  359. )
  360. //++
  361. //
  362. // Routine Description:
  363. //
  364. // This function displays the prompt string on the debugging console and
  365. // then reads a line of text from the debugging console. The line read
  366. // is returned in the memory pointed to by the second parameter. The
  367. // third parameter specifies the maximum number of characters that can
  368. // be stored in the response area.
  369. //
  370. // Arguments:
  371. //
  372. // Prompt - specifies the text to display as the prompt.
  373. //
  374. // Response - specifies where to store the response read from the
  375. // debugging console.
  376. //
  377. // Prompt - specifies the maximum number of characters that can be
  378. // stored in the Response buffer.
  379. //
  380. // Return Value:
  381. //
  382. // Number of characters stored in the Response buffer. Includes the
  383. // terminating newline character, but not the null character after
  384. // that.
  385. //
  386. //--
  387. {
  388. STRING Input;
  389. STRING Output;
  390. //
  391. // Output the prompt string and read input.
  392. //
  393. Input.MaximumLength = (USHORT)MaximumResponseLength;
  394. Input.Buffer = Response;
  395. Output.Length = (USHORT)strlen( Prompt );
  396. Output.Buffer = Prompt;
  397. return DebugPrompt( &Output, &Input );
  398. }
  399. #if defined(NTOS_KERNEL_RUNTIME) || defined(BLDR_KERNEL_RUNTIME)
  400. VOID
  401. DbgLoadImageSymbols(
  402. IN PSTRING FileName,
  403. IN PVOID ImageBase,
  404. IN ULONG_PTR ProcessId
  405. )
  406. //++
  407. //
  408. // Routine Description:
  409. //
  410. // Tells the debugger about newly loaded symbols.
  411. //
  412. // Arguments:
  413. //
  414. // Return Value:
  415. //
  416. //--
  417. {
  418. PIMAGE_NT_HEADERS NtHeaders;
  419. KD_SYMBOLS_INFO SymbolInfo;
  420. SymbolInfo.BaseOfDll = ImageBase;
  421. SymbolInfo.ProcessId = ProcessId;
  422. NtHeaders = RtlImageNtHeader( ImageBase );
  423. if (NtHeaders != NULL) {
  424. SymbolInfo.CheckSum = (ULONG)NtHeaders->OptionalHeader.CheckSum;
  425. SymbolInfo.SizeOfImage = (ULONG)NtHeaders->OptionalHeader.SizeOfImage;
  426. } else {
  427. #if defined(BLDR_KERNEL_RUNTIME)
  428. //
  429. // There is only one image loaded in the loader environment that
  430. // does not have an NT image header. The image is the OS loader
  431. // and it is loaded by the firmware which strips the file header
  432. // and the optional ROM header. All the debugger requires is a
  433. // good guest at the size of the image.
  434. //
  435. SymbolInfo.SizeOfImage = 0x100000;
  436. #else
  437. SymbolInfo.SizeOfImage = 0;
  438. #endif
  439. SymbolInfo.CheckSum = 0;
  440. }
  441. DebugService2(FileName, &SymbolInfo, BREAKPOINT_LOAD_SYMBOLS);
  442. return;
  443. }
  444. VOID
  445. DbgUnLoadImageSymbols (
  446. IN PSTRING FileName,
  447. IN PVOID ImageBase,
  448. IN ULONG_PTR ProcessId
  449. )
  450. //++
  451. //
  452. // Routine Description:
  453. //
  454. // Tells the debugger about newly unloaded symbols.
  455. //
  456. // Arguments:
  457. //
  458. // Return Value:
  459. //
  460. //--
  461. {
  462. KD_SYMBOLS_INFO SymbolInfo;
  463. SymbolInfo.BaseOfDll = ImageBase;
  464. SymbolInfo.ProcessId = ProcessId;
  465. SymbolInfo.CheckSum = 0;
  466. SymbolInfo.SizeOfImage = 0;
  467. DebugService2(FileName, &SymbolInfo, BREAKPOINT_UNLOAD_SYMBOLS);
  468. return;
  469. }
  470. VOID
  471. DbgCommandString(
  472. IN PCH Name,
  473. IN PCH Command
  474. )
  475. //++
  476. //
  477. // Routine Description:
  478. //
  479. // Tells the debugger to execute a command string
  480. //
  481. // Arguments:
  482. //
  483. // Name - Identifies the originator of the command.
  484. //
  485. // Command - Command string.
  486. //
  487. // Return Value:
  488. //
  489. //--
  490. {
  491. STRING NameStr, CommandStr;
  492. NameStr.Buffer = Name;
  493. NameStr.Length = (USHORT)strlen(Name);
  494. CommandStr.Buffer = Command;
  495. CommandStr.Length = (USHORT)strlen(Command);
  496. DebugService2(&NameStr, &CommandStr, BREAKPOINT_COMMAND_STRING);
  497. }
  498. #endif // defined(NTOS_KERNEL_RUNTIME)
  499. #if !defined(BLDR_KERNEL_RUNTIME)
  500. NTSTATUS
  501. DbgQueryDebugFilterState(
  502. IN ULONG ComponentId,
  503. IN ULONG Level
  504. )
  505. //++
  506. //
  507. // Routine Description:
  508. //
  509. // This function queries the debug print enable for a specified component
  510. // level. If Level is > 31, it's assumed to be a mask otherwise, it indicates
  511. // a specific debug level to test for (ERROR/WARNING/TRACE/INFO, etc).
  512. //
  513. // Arguments:
  514. //
  515. // ComponentId - Supplies the component id.
  516. //
  517. // Level - Supplies the debug filter level number or mask.
  518. //
  519. // Return Value:
  520. //
  521. // STATUS_INVALID_PARAMETER_1 is returned if the component id is not
  522. // valid.
  523. //
  524. // TRUE is returned if output is enabled for the specified component
  525. // and level or is enabled for the system.
  526. //
  527. // FALSE is returned if output is not enabled for the specified component
  528. // and level and is not enabled for the system.
  529. //
  530. //--
  531. {
  532. return NtQueryDebugFilterState(ComponentId, Level);
  533. }
  534. NTSTATUS
  535. DbgSetDebugFilterState(
  536. IN ULONG ComponentId,
  537. IN ULONG Level,
  538. IN BOOLEAN State
  539. )
  540. //++
  541. //
  542. // Routine Description:
  543. //
  544. // This function sets the state of the debug print enable for a specified
  545. // component and level. The debug print enable state for the system is set
  546. // by specifying the distinguished value -1 for the component id.
  547. //
  548. // Arguments:
  549. //
  550. // ComponentId - Supplies the Id of the calling component.
  551. //
  552. // Level - Supplies the output filter level or mask.
  553. //
  554. // State - Supplies a boolean value that determines the new state.
  555. //
  556. // Return Value:
  557. //
  558. // STATUS_ACCESS_DENIED is returned if the required privilege is not held.
  559. //
  560. // STATUS_INVALID_PARAMETER_1 is returned if the component id is not
  561. // valid.
  562. //
  563. // STATUS_SUCCESS is returned if the debug print enable state is set for
  564. // the specified component.
  565. //
  566. //--
  567. {
  568. return NtSetDebugFilterState(ComponentId, Level, State);
  569. }
  570. #endif
  571. #endif // !defined(BLDR_KERNEL_RUNTIME)