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.

1910 lines
54 KiB

  1. /*++
  2. Copyright (c) 1990-2000 Microsoft Corporation
  3. Module Name:
  4. ntsdexts.c
  5. Abstract:
  6. This function contains the default ntsd debugger extensions
  7. Revision History:
  8. Daniel Mihai (DMihai) 18-Feb-2001
  9. Add !htrace - useful for dumping handle trace information.
  10. --*/
  11. #include "ntsdextp.h"
  12. DECLARE_API( version )
  13. {
  14. OSVERSIONINFOA VersionInformation;
  15. HKEY hkey;
  16. DWORD cb, dwType;
  17. CHAR szCurrentType[128];
  18. CHAR szCSDString[3+128];
  19. INIT_API();
  20. VersionInformation.dwOSVersionInfoSize = sizeof(VersionInformation);
  21. if (!GetVersionEx( &VersionInformation )) {
  22. dprintf("GetVersionEx failed - %u\n", GetLastError());
  23. goto Exit;
  24. }
  25. szCurrentType[0] = '\0';
  26. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  27. "Software\\Microsoft\\Windows NT\\CurrentVersion",
  28. 0,
  29. KEY_READ,
  30. &hkey
  31. ) == NO_ERROR
  32. ) {
  33. cb = sizeof(szCurrentType);
  34. if (hkey) {
  35. if (RegQueryValueEx(hkey, "CurrentType", NULL, &dwType, szCurrentType, &cb ) != 0) {
  36. szCurrentType[0] = '\0';
  37. }
  38. RegCloseKey(hkey);
  39. }
  40. }
  41. if (VersionInformation.szCSDVersion[0]) {
  42. _snprintf(szCSDString, sizeof(szCSDString) / sizeof(szCSDString[0]), ": %s", VersionInformation.szCSDVersion);
  43. szCSDString[(sizeof(szCSDString) / sizeof(szCSDString[0])) - 1] = 0;
  44. }
  45. else {
  46. szCSDString[0] = '\0';
  47. }
  48. dprintf("Version %d.%d (Build %d%s) %s\n",
  49. VersionInformation.dwMajorVersion,
  50. VersionInformation.dwMinorVersion,
  51. VersionInformation.dwBuildNumber,
  52. szCSDString,
  53. szCurrentType
  54. );
  55. Exit:
  56. EXIT_API();
  57. }
  58. DECLARE_API( help )
  59. {
  60. INIT_API();
  61. while (*args == ' ')
  62. args++;
  63. if (*args == '\0') {
  64. dprintf("ntsdexts help:\n\n");
  65. dprintf("!critSec csAddress - Dump a critical section\n");
  66. dprintf("!dp [v] [pid | pcsr_process] - Dump CSR process\n");
  67. dprintf("!dreg -[d|w] <keyPath>[![<valueName> | *]] - Dump registry information\n");
  68. dprintf("!dt [v] pcsr_thread - Dump CSR thread\n");
  69. dprintf("!error value - Decode error value\n");
  70. dprintf("!gatom - Dump the global atom table\n");
  71. dprintf("!handle [handle] - Dump handle information\n");
  72. dprintf("!help [cmd] - Displays this list or gives details on command\n");
  73. dprintf("!locks [-v][-o] - Dump all Critical Sections in process\n");
  74. dprintf("!version - Dump system version and build number\n");
  75. } else {
  76. if (*args == '!')
  77. args++;
  78. if (strcmp( args, "handle") == 0) {
  79. dprintf("!handle [handle [flags [type]]] - Dump handle information\n");
  80. dprintf(" If no handle specified, all handles are dumped.\n");
  81. dprintf(" Flags are bits indicating greater levels of detail.\n");
  82. dprintf("If the handle is 0 or -1, all handles are scanned. If the handle is not\n");
  83. dprintf("zero, that particular handle is examined. The flags are as follows:\n");
  84. dprintf(" 1 - Get type information (default)\n");
  85. dprintf(" 2 - Get basic information\n");
  86. dprintf(" 4 - Get name information\n");
  87. dprintf(" 8 - Get object specific info (where available)\n");
  88. dprintf("\n");
  89. dprintf("If Type is specified, only object of that type are scanned. Type is a\n");
  90. dprintf("standard NT type name, e.g. Event, Semaphore, etc. Case sensitive, of\n");
  91. dprintf("course.\n");
  92. dprintf("\n");
  93. dprintf("Examples:\n");
  94. dprintf("\n");
  95. dprintf(" !handle -- dumps the types of all the handles, and a summary table\n");
  96. dprintf(" !handle 0 0 -- dumps a summary table of all the open handles\n");
  97. dprintf(" !handle 0 f -- dumps everything we can find about a handle.\n");
  98. dprintf(" !handle 0 f Event\n");
  99. dprintf(" -- dumps everything we can find about open events\n");
  100. } else if (strcmp( args, "gflag") == 0) {
  101. dprintf("If a value is not given then displays the current bits set in\n");
  102. dprintf("NTDLL!NtGlobalFlag variable. Otherwise value can be one of the\n");
  103. dprintf("following:\n");
  104. dprintf("\n");
  105. dprintf(" -? - displays a list of valid flag abbreviations\n");
  106. dprintf(" number - 32-bit number that becomes the new value stored into\n");
  107. dprintf(" NtGlobalFlag\n");
  108. dprintf(" +number - specifies one or more bits to set in NtGlobalFlag\n");
  109. dprintf(" +abbrev - specifies a single bit to set in NtGlobalFlag\n");
  110. dprintf(" -number - specifies one or more bits to clear in NtGlobalFlag\n");
  111. dprintf(" -abbrev - specifies a single bit to clear in NtGlobalFlag\n");
  112. } else {
  113. dprintf("Invalid command. No help available\n");
  114. }
  115. }
  116. EXIT_API();
  117. }
  118. VOID
  119. DumpStackBackTraceIndex(
  120. IN USHORT BackTraceIndex
  121. )
  122. {
  123. #if i386
  124. BOOL b;
  125. PRTL_STACK_TRACE_ENTRY pBackTraceEntry;
  126. RTL_STACK_TRACE_ENTRY BackTraceEntry;
  127. ULONG i;
  128. CHAR Symbol[ 1024 ];
  129. ULONG_PTR Displacement;
  130. ULONG NumberOfEntriesAdded;
  131. PRTL_STACK_TRACE_ENTRY *EntryIndexArray; // Indexed by [-1 .. -NumberOfEntriesAdded]
  132. PSTACK_TRACE_DATABASE *pRtlpStackTraceDataBase;
  133. PSTACK_TRACE_DATABASE RtlpStackTraceDataBase;
  134. STACK_TRACE_DATABASE StackTraceDataBase;
  135. pRtlpStackTraceDataBase = (PSTACK_TRACE_DATABASE *)GetExpression( "NTDLL!RtlpStackTraceDataBase" );
  136. if (pRtlpStackTraceDataBase == NULL) {
  137. dprintf( "HEAPEXT: Unable to get address of NTDLL!RtlpStackTraceDataBase\n" );
  138. }
  139. if ((BackTraceIndex != 0) && (pRtlpStackTraceDataBase != NULL)) {
  140. b = ReadMemory( (ULONG_PTR)pRtlpStackTraceDataBase,
  141. &RtlpStackTraceDataBase,
  142. sizeof( RtlpStackTraceDataBase ),
  143. NULL
  144. );
  145. if (!b || RtlpStackTraceDataBase == NULL) {
  146. return;
  147. }
  148. b = ReadMemory( (ULONG_PTR)RtlpStackTraceDataBase,
  149. &StackTraceDataBase,
  150. sizeof( StackTraceDataBase ),
  151. NULL
  152. );
  153. if (!b) {
  154. return;
  155. }
  156. if (BackTraceIndex < StackTraceDataBase.NumberOfEntriesAdded) {
  157. b = ReadMemory( (ULONG_PTR)(StackTraceDataBase.EntryIndexArray - BackTraceIndex),
  158. &pBackTraceEntry,
  159. sizeof( pBackTraceEntry ),
  160. NULL
  161. );
  162. if (!b) {
  163. dprintf( " unable to read stack back trace index (%x) entry at %p\n",
  164. BackTraceIndex,
  165. (StackTraceDataBase.EntryIndexArray - BackTraceIndex)
  166. );
  167. return;
  168. }
  169. b = ReadMemory( (ULONG_PTR)pBackTraceEntry,
  170. &BackTraceEntry,
  171. sizeof( BackTraceEntry ),
  172. NULL
  173. );
  174. if (!b) {
  175. dprintf( " unable to read stack back trace entry at %p\n",
  176. pBackTraceEntry
  177. );
  178. return;
  179. }
  180. dprintf( "\n Stack trace (%u) at %x:\n", BackTraceIndex, pBackTraceEntry );
  181. for (i=0; i<BackTraceEntry.Depth; i++) {
  182. GetSymbol( (LPVOID)BackTraceEntry.BackTrace[ i ],
  183. Symbol,
  184. &Displacement
  185. );
  186. dprintf( " %08x: %s", BackTraceEntry.BackTrace[ i ], Symbol );
  187. if (Displacement != 0) {
  188. dprintf( "+0x%p", Displacement );
  189. }
  190. dprintf( "\n" );
  191. }
  192. }
  193. }
  194. #endif
  195. return;
  196. }
  197. PLIST_ENTRY
  198. DumpCritSec(
  199. DWORD_PTR dwAddrCritSec,
  200. DWORD_PTR dwAddrDebugInfo,
  201. BOOLEAN bDumpIfUnowned,
  202. BOOLEAN bOrphaned
  203. )
  204. /*++
  205. Routine Description:
  206. This function is called as an NTSD extension to format and dump
  207. the contents of the specified critical section.
  208. Arguments:
  209. dwAddrCritSec - Supplies the address of the critical section to
  210. be dumped or NULL if dumping via debug info
  211. dwAddrDebugInfo - Supllies the address of a critical section debug info
  212. struct to be dumped or NULL if the critical section address is passed in
  213. bDumpIfUnowned - TRUE means to dump the critical section even if
  214. it is currently unowned.
  215. bOrphaned - TRUE: means that the caller only wants to know if the debuginfo does
  216. not point to a valid critical section
  217. Return Value:
  218. Pointer to the next critical section in the list for the process or
  219. NULL if no more critical sections.
  220. --*/
  221. {
  222. USHORT i;
  223. CHAR Symbol[1024];
  224. DWORD_PTR Displacement;
  225. CRITICAL_SECTION CriticalSection;
  226. CRITICAL_SECTION_DEBUG DebugInfo;
  227. BOOL b;
  228. PLIST_ENTRY Next=NULL;
  229. if (dwAddrDebugInfo != (DWORD_PTR)NULL) {
  230. //
  231. // the address of the debug info was passes in, read it in from the debugged process
  232. //
  233. b = ReadMemory( dwAddrDebugInfo,
  234. &DebugInfo,
  235. sizeof(DebugInfo),
  236. NULL
  237. );
  238. if ( !b ) {
  239. dprintf(" NTSDEXTS: Unable to read RTL_CRITICAL_SECTION_DEBUG at %p\n", dwAddrDebugInfo );
  240. return NULL;
  241. }
  242. //
  243. // get the critical section from the debug info
  244. //
  245. dwAddrCritSec=(DWORD_PTR)DebugInfo.CriticalSection;
  246. //
  247. // set the next pointer now. It is only used when the debuginfo is passed in
  248. //
  249. Next=DebugInfo.ProcessLocksList.Flink;
  250. } else {
  251. //
  252. // the debug info address was zero, the critical section address better not be too
  253. //
  254. if (dwAddrCritSec == (DWORD_PTR)NULL) {
  255. //
  256. // If the debuginfo value was not valid, then the critical section value must be
  257. //
  258. return NULL;
  259. }
  260. }
  261. //
  262. // we should now have a pointer to the critical section, either passed in or read from
  263. // the debug info
  264. //
  265. //
  266. // Read the critical section from the debuggees address space into our
  267. // own.
  268. //
  269. b = ReadMemory( dwAddrCritSec,
  270. &CriticalSection,
  271. sizeof(CriticalSection),
  272. NULL
  273. );
  274. if ( !b ) {
  275. if (bDumpIfUnowned || bOrphaned) {
  276. dprintf("\nCritSec at %p could not be read\n",dwAddrCritSec);
  277. dprintf("Perhaps the critical section was a global variable in a dll that was unloaded?\n");
  278. if (dwAddrDebugInfo != (DWORD_PTR)NULL) {
  279. if (bOrphaned) {
  280. DumpStackBackTraceIndex(DebugInfo.CreatorBackTraceIndex);
  281. }
  282. }
  283. }
  284. return Next;
  285. }
  286. if (dwAddrDebugInfo != (DWORD_PTR)NULL) {
  287. //
  288. // the debug info address was passed in, make sure the critical section that
  289. // it pointed to points back to it.
  290. //
  291. if ((DWORD_PTR)CriticalSection.DebugInfo != dwAddrDebugInfo) {
  292. //
  293. // this critical section does not point back to debug info that we got it from
  294. //
  295. CRITICAL_SECTION_DEBUG OtherDebugInfo;
  296. //
  297. // now lets try to read in the debug info that this critical section points to,
  298. // to see if it does point back the critical section in question
  299. //
  300. ZeroMemory(&OtherDebugInfo,sizeof(OtherDebugInfo));
  301. b = ReadMemory( (ULONG_PTR)CriticalSection.DebugInfo,
  302. &OtherDebugInfo,
  303. sizeof(DebugInfo),
  304. NULL
  305. );
  306. if ( !b ) {
  307. //
  308. // we could not read the debug info pointed to by the critical section,
  309. // probably means the critical section has been trashed
  310. //
  311. if (bDumpIfUnowned || bOrphaned) {
  312. dprintf("\nCritSec at %p does not point back to the debug info at %p\n",dwAddrCritSec,dwAddrDebugInfo);
  313. dprintf("Perhaps the memory that held the critical section has been reused without calling DeleteCriticalSection() ?\n");
  314. if (bOrphaned) {
  315. DumpStackBackTraceIndex(DebugInfo.CreatorBackTraceIndex);
  316. }
  317. }
  318. } else {
  319. //
  320. // we were able to read in the debug info, see if it points to this new
  321. // critical section
  322. //
  323. if ((DWORD_PTR)OtherDebugInfo.CriticalSection == dwAddrCritSec) {
  324. //
  325. // the debug info points back to the critical section.
  326. // The definitely means that it was re-initialized.
  327. //
  328. if (bDumpIfUnowned || bOrphaned) {
  329. GetSymbol((LPVOID)dwAddrCritSec,Symbol,&Displacement);
  330. dprintf(
  331. "\nThe CritSec %s+%lx at %p has been RE-INITIALIZED.\n",
  332. Symbol,
  333. Displacement,
  334. dwAddrCritSec
  335. );
  336. dprintf("The critical section points to DebugInfo at %p instead of %p\n",(DWORD_PTR)CriticalSection.DebugInfo,dwAddrDebugInfo);
  337. if (bOrphaned) {
  338. DumpStackBackTraceIndex(DebugInfo.CreatorBackTraceIndex);
  339. }
  340. }
  341. } else {
  342. //
  343. // The debug info does not point back the critical section, probably means that
  344. // the critical section was trashed
  345. //
  346. if (bDumpIfUnowned || bOrphaned) {
  347. dprintf("\nCritSec at %p does not point back to the debug info at %p\n",dwAddrCritSec,dwAddrDebugInfo);
  348. dprintf("Perhaps the memory that held the critical section has been reused without calling DeleteCriticalSection() ?\n");
  349. if (bOrphaned) {
  350. DumpStackBackTraceIndex(DebugInfo.CreatorBackTraceIndex);
  351. }
  352. }
  353. }
  354. }
  355. }
  356. } else {
  357. //
  358. // we need to read in the debug info from the critical section since it was not passed in
  359. //
  360. ZeroMemory(&DebugInfo,sizeof(DebugInfo));
  361. b = ReadMemory( (ULONG_PTR)CriticalSection.DebugInfo,
  362. &DebugInfo,
  363. sizeof(DebugInfo),
  364. NULL
  365. );
  366. if ( !b ) {
  367. //
  368. // use this to signal that we could not read the debuginfo for the critical section
  369. //
  370. CriticalSection.DebugInfo=NULL;
  371. dprintf("\nDebugInfo for CritSec at %p could not be read\n",dwAddrCritSec);
  372. dprintf("Probably NOT an initialized critical section.\n");
  373. } else {
  374. //
  375. // we were able to read in the debug info, see if it valid
  376. //
  377. if ((DWORD_PTR)DebugInfo.CriticalSection != dwAddrCritSec) {
  378. //
  379. // The debug info does not point back to the critical section
  380. //
  381. dprintf("\nDebugInfo for CritSec at %p does not point back to the critical section\n",dwAddrCritSec);
  382. dprintf("NOT an initialized critical section.\n");
  383. }
  384. }
  385. }
  386. //
  387. // we should now have read in both the critical section and debug info for that critical section
  388. //
  389. if (bOrphaned) {
  390. //
  391. // the user only wanted to check for orphaned critical sections
  392. //
  393. return Next;
  394. }
  395. //
  396. // Dump the critical section
  397. //
  398. if ( CriticalSection.LockCount == -1 && !bDumpIfUnowned) {
  399. //
  400. // Lock is not held and the user does not want verbose output
  401. //
  402. return Next;
  403. }
  404. //
  405. // Get the symbolic name of the critical section
  406. //
  407. dprintf("\n");
  408. GetSymbol((LPVOID)dwAddrCritSec,Symbol,&Displacement);
  409. dprintf(
  410. "CritSec %s+%lx at %p\n",
  411. Symbol,
  412. Displacement,
  413. dwAddrCritSec
  414. );
  415. if ( CriticalSection.LockCount == -1) {
  416. dprintf("LockCount NOT LOCKED\n");
  417. } else {
  418. dprintf("LockCount %ld\n",CriticalSection.LockCount);
  419. }
  420. dprintf("RecursionCount %ld\n",CriticalSection.RecursionCount);
  421. dprintf("OwningThread %lx\n",CriticalSection.OwningThread);
  422. if (CriticalSection.DebugInfo != NULL) {
  423. //
  424. // we have the debug info
  425. //
  426. dprintf("EntryCount %lx\n",DebugInfo.EntryCount);
  427. dprintf("ContentionCount %lx\n",DebugInfo.ContentionCount);
  428. }
  429. if ( CriticalSection.LockCount != -1) {
  430. dprintf("*** Locked\n");
  431. }
  432. return Next;
  433. }
  434. DECLARE_API( critsec )
  435. {
  436. DWORD_PTR dwAddrCritSec;
  437. INIT_API();
  438. //
  439. // Evaluate the argument string to get the address of
  440. // the critical section to dump.
  441. //
  442. dwAddrCritSec = GetExpression(args);
  443. if ( !dwAddrCritSec ) {
  444. goto Exit;
  445. }
  446. DumpCritSec(dwAddrCritSec,0,TRUE,FALSE);
  447. Exit:
  448. EXIT_API();
  449. }
  450. DECLARE_API( locks )
  451. /*++
  452. Routine Description:
  453. This function is called as an NTSD extension to display all
  454. critical sections in the target process.
  455. Return Value:
  456. None.
  457. --*/
  458. {
  459. BOOL b;
  460. CRITICAL_SECTION_DEBUG DebugInfo;
  461. PVOID AddrListHead;
  462. LIST_ENTRY ListHead;
  463. PLIST_ENTRY Next;
  464. BOOLEAN Verbose;
  465. BOOLEAN Orphaned=FALSE;
  466. LPCSTR p;
  467. DWORD NumberOfCriticalSections;
  468. INIT_API();
  469. Verbose = FALSE;
  470. p = (LPSTR)args;
  471. while ( p != NULL && *p ) {
  472. if ( *p == '-' ) {
  473. p++;
  474. switch ( *p ) {
  475. case 'V':
  476. case 'v':
  477. Verbose = TRUE;
  478. p++;
  479. break;
  480. case 'o':
  481. case 'O':
  482. Orphaned=TRUE;
  483. p++;
  484. break;
  485. case ' ':
  486. goto gotBlank;
  487. default:
  488. dprintf( "NTSDEXTS: !locks invalid option flag '-%c'\n", *p );
  489. break;
  490. }
  491. }
  492. else {
  493. gotBlank:
  494. p++;
  495. }
  496. }
  497. if (Orphaned) {
  498. dprintf( "Looking for orphaned critical sections\n" );
  499. }
  500. //
  501. // Locate the address of the list head.
  502. //
  503. AddrListHead = (PVOID)GetExpression("ntdll!RtlCriticalSectionList");
  504. if ( !AddrListHead ) {
  505. dprintf( "NTSDEXTS: Unable to resolve ntdll!RtlCriticalSectionList\n" );
  506. dprintf( "NTSDEXTS: Please check your symbols\n" );
  507. goto Exit;
  508. }
  509. //
  510. // Read the list head
  511. //
  512. b = ReadMemory( (ULONG_PTR)AddrListHead,
  513. &ListHead,
  514. sizeof(ListHead),
  515. NULL
  516. );
  517. if ( !b ) {
  518. dprintf( "NTSDEXTS: Unable to read memory at ntdll!RtlCriticalSectionList\n" );
  519. goto Exit;
  520. }
  521. Next = ListHead.Flink;
  522. (CheckControlC)();
  523. NumberOfCriticalSections=0;
  524. //
  525. // Walk the list of critical sections
  526. //
  527. while ( Next != AddrListHead ) {
  528. Next=DumpCritSec(
  529. 0,
  530. (DWORD_PTR)CONTAINING_RECORD( Next, RTL_CRITICAL_SECTION_DEBUG, ProcessLocksList),
  531. Verbose,
  532. Orphaned
  533. );
  534. if (Next == NULL) {
  535. dprintf( "\nStopped scanning because of problem reading critical section debug info\n");
  536. break;
  537. }
  538. NumberOfCriticalSections++;
  539. if ((CheckControlC)()) {
  540. dprintf( "\nStopped scanning because of control-C\n");
  541. break;
  542. }
  543. }
  544. dprintf( "\nScanned %d critical sections\n",NumberOfCriticalSections);
  545. Exit:
  546. EXIT_API();
  547. }
  548. //
  549. // Simple routine to convert from hex into a string of characters.
  550. // Used by debugger extensions.
  551. //
  552. // by scottlu
  553. //
  554. char *
  555. HexToString(
  556. ULONG_PTR dw,
  557. CHAR *pch
  558. )
  559. {
  560. if (dw > 0xf) {
  561. pch = HexToString(dw >> 4, pch);
  562. dw &= 0xf;
  563. }
  564. *pch++ = ((dw >= 0xA) ? ('A' - 0xA) : '0') + (CHAR)dw;
  565. *pch = 0;
  566. return pch;
  567. }
  568. //
  569. // dt == dump thread
  570. //
  571. // dt [v] pcsr_thread
  572. // v == verbose (structure)
  573. //
  574. // by scottlu
  575. //
  576. DECLARE_API( dt )
  577. {
  578. char chVerbose;
  579. CSR_THREAD csrt;
  580. ULONG_PTR dw;
  581. BOOL b;
  582. INIT_API();
  583. while (*args == ' ')
  584. args++;
  585. chVerbose = ' ';
  586. if (*args == 'v')
  587. chVerbose = *args++;
  588. dw = GetExpression(args);
  589. b = ReadMemory( dw, &csrt, sizeof(csrt), NULL);
  590. if ( !b ) {
  591. dprintf( "NTSDEXTS: Unable to read memory\n" );
  592. goto Exit;
  593. }
  594. //
  595. // Print simple thread info if the user did not ask for verbose.
  596. //
  597. if (chVerbose == ' ') {
  598. dprintf("Thread %08lx, Process %08lx, ClientId %lx.%lx, Flags %lx, Ref Count %lx\n",
  599. dw,
  600. csrt.Process,
  601. csrt.ClientId.UniqueProcess,
  602. csrt.ClientId.UniqueThread,
  603. csrt.Flags,
  604. csrt.ReferenceCount);
  605. goto Exit;
  606. }
  607. dprintf("PCSR_THREAD @ %08lx:\n"
  608. "\t+%04lx Link.Flink %08lx\n"
  609. "\t+%04lx Link.Blink %08lx\n"
  610. "\t+%04lx Process %08lx\n",
  611. dw,
  612. FIELD_OFFSET(CSR_THREAD, Link.Flink), csrt.Link.Flink,
  613. FIELD_OFFSET(CSR_THREAD, Link.Blink), csrt.Link.Blink,
  614. FIELD_OFFSET(CSR_THREAD, Process), csrt.Process);
  615. dprintf(
  616. "\t+%04lx WaitBlock %08lx\n"
  617. "\t+%04lx ClientId.UniqueProcess %08lx\n"
  618. "\t+%04lx ClientId.UniqueThread %08lx\n"
  619. "\t+%04lx ThreadHandle %08lx\n",
  620. FIELD_OFFSET(CSR_THREAD, WaitBlock), csrt.WaitBlock,
  621. FIELD_OFFSET(CSR_THREAD, ClientId.UniqueProcess), csrt.ClientId.UniqueProcess,
  622. FIELD_OFFSET(CSR_THREAD, ClientId.UniqueThread), csrt.ClientId.UniqueThread,
  623. FIELD_OFFSET(CSR_THREAD, ThreadHandle), csrt.ThreadHandle);
  624. dprintf(
  625. "\t+%04lx Flags %08lx\n"
  626. "\t+%04lx ReferenceCount %08lx\n"
  627. "\t+%04lx HashLinks.Flink %08lx\n"
  628. "\t+%04lx HashLinks.Blink %08lx\n",
  629. FIELD_OFFSET(CSR_THREAD, Flags), csrt.Flags,
  630. FIELD_OFFSET(CSR_THREAD, ReferenceCount), csrt.ReferenceCount,
  631. FIELD_OFFSET(CSR_THREAD, HashLinks.Flink), csrt.HashLinks.Flink,
  632. FIELD_OFFSET(CSR_THREAD, HashLinks.Blink), csrt.HashLinks.Blink);
  633. Exit:
  634. EXIT_API();
  635. }
  636. //
  637. // dp == dump process
  638. //
  639. // dp [v] [pid | pcsr_process]
  640. // v == verbose (structure + thread list)
  641. // no process == dump process list
  642. //
  643. // by scottlu
  644. //
  645. DECLARE_API( dp )
  646. {
  647. PLIST_ENTRY ListHead, ListNext;
  648. char ach[80];
  649. char chVerbose;
  650. PCSR_PROCESS pcsrpT;
  651. CSR_PROCESS csrp;
  652. PCSR_PROCESS pcsrpRoot;
  653. PCSR_THREAD pcsrt;
  654. ULONG_PTR dwProcessId;
  655. ULONG_PTR dw;
  656. DWORD_PTR dwRootProcess;
  657. BOOL b;
  658. INIT_API();
  659. while (*args == ' ')
  660. args++;
  661. chVerbose = ' ';
  662. if (*args == 'v')
  663. chVerbose = *args++;
  664. dwRootProcess = GetExpression("csrsrv!CsrRootProcess");
  665. if ( !dwRootProcess ) {
  666. goto Exit;
  667. }
  668. b = ReadMemory( dwRootProcess, &pcsrpRoot, sizeof(pcsrpRoot), NULL);
  669. if ( !b ) {
  670. dprintf( "NTSDEXTS: Unable to read RootProcess\n" );
  671. goto Exit;
  672. }
  673. //
  674. // See if user wants all processes. If so loop through them.
  675. //
  676. if (*args == 0) {
  677. ListHead = &pcsrpRoot->ListLink;
  678. b = ReadMemory( (ULONG_PTR)(&ListHead->Flink), &ListNext, sizeof(ListNext), NULL);
  679. if ( !b ) {
  680. dprintf( "NTSDEXTS: Unable to read ListNext\n" );
  681. goto Exit;
  682. }
  683. while (ListNext != ListHead) {
  684. pcsrpT = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink);
  685. ach[0] = chVerbose;
  686. ach[1] = ' ';
  687. HexToString((ULONG_PTR)pcsrpT, &ach[2]);
  688. dp(Client, ach);
  689. b = ReadMemory( (ULONG_PTR)(&ListNext->Flink), &ListNext, sizeof(ListNext), NULL);
  690. if ( !b ) {
  691. dprintf( "NTSDEXTS: Unable to read ListNext\n" );
  692. goto Exit;
  693. }
  694. }
  695. dprintf("---\n");
  696. goto Exit;
  697. }
  698. //
  699. // User wants specific process structure. Evaluate to find id or process
  700. // pointer.
  701. //
  702. dw = (ULONG)GetExpression(args);
  703. ListHead = &pcsrpRoot->ListLink;
  704. b = ReadMemory( (ULONG_PTR)(&ListHead->Flink), &ListNext, sizeof(ListNext), NULL);
  705. if ( !b ) {
  706. dprintf( "NTSDEXTS: Unable to read ListNext\n" );
  707. goto Exit;
  708. }
  709. while (ListNext != ListHead) {
  710. pcsrpT = CONTAINING_RECORD(ListNext, CSR_PROCESS, ListLink);
  711. b = ReadMemory( (ULONG_PTR)(&ListNext->Flink), &ListNext, sizeof(ListNext), NULL);
  712. if ( !b ) {
  713. dprintf( "NTSDEXTS: Unable to read ListNext\n" );
  714. goto Exit;
  715. }
  716. b = ReadMemory( (ULONG_PTR)(&pcsrpT->ClientId.UniqueProcess), &dwProcessId, sizeof(dwProcessId), NULL);
  717. if ( !b ) {
  718. dprintf( "NTSDEXTS: Unable to read ListNext\n" );
  719. goto Exit;
  720. }
  721. if (dw == dwProcessId) {
  722. dw = (ULONG_PTR)pcsrpT;
  723. break;
  724. }
  725. }
  726. pcsrpT = (PCSR_PROCESS)dw;
  727. b = ReadMemory( (ULONG_PTR)pcsrpT, &csrp, sizeof(csrp), NULL);
  728. if ( !b ) {
  729. dprintf( "NTSDEXTS: Unable to read RootProcess\n" );
  730. goto Exit;
  731. }
  732. //
  733. // If not verbose, print simple process info.
  734. //
  735. if (chVerbose == ' ') {
  736. dprintf("Process %08lx, Id %p, Seq# %lx, Flags %lx, Ref Count %lx\n",
  737. pcsrpT,
  738. csrp.ClientId.UniqueProcess,
  739. csrp.SequenceNumber,
  740. csrp.Flags,
  741. csrp.ReferenceCount);
  742. goto Exit;
  743. }
  744. dprintf("PCSR_PROCESS @ %08lx:\n"
  745. "\t+%04lx ListLink.Flink %08lx\n"
  746. "\t+%04lx ListLink.Blink %08lx\n",
  747. pcsrpT,
  748. FIELD_OFFSET(CSR_PROCESS, ListLink.Flink), csrp.ListLink.Flink,
  749. FIELD_OFFSET(CSR_PROCESS, ListLink.Blink), csrp.ListLink.Blink);
  750. dprintf(
  751. "\t+%04lx ThreadList.Flink %08lx\n"
  752. "\t+%04lx ThreadList.Blink %08lx\n"
  753. "\t+%04lx NtSession %08lx\n"
  754. "\t+%04lx ExpectedVersion %08lx\n",
  755. FIELD_OFFSET(CSR_PROCESS, ThreadList.Flink), csrp.ThreadList.Flink,
  756. FIELD_OFFSET(CSR_PROCESS, ThreadList.Blink), csrp.ThreadList.Blink,
  757. FIELD_OFFSET(CSR_PROCESS, NtSession), csrp.NtSession,
  758. FIELD_OFFSET(CSR_PROCESS, ExpectedVersion), csrp.ExpectedVersion);
  759. dprintf(
  760. "\t+%04lx ClientPort %08lx\n"
  761. "\t+%04lx ClientViewBase %08lx\n"
  762. "\t+%04lx ClientViewBounds %08lx\n"
  763. "\t+%04lx ClientId.UniqueProcess %08lx\n",
  764. FIELD_OFFSET(CSR_PROCESS, ClientPort), csrp.ClientPort,
  765. FIELD_OFFSET(CSR_PROCESS, ClientViewBase), csrp.ClientViewBase,
  766. FIELD_OFFSET(CSR_PROCESS, ClientViewBounds), csrp.ClientViewBounds,
  767. FIELD_OFFSET(CSR_PROCESS, ClientId.UniqueProcess), csrp.ClientId.UniqueProcess);
  768. dprintf(
  769. "\t+%04lx ProcessHandle %08lx\n"
  770. "\t+%04lx SequenceNumber %08lx\n"
  771. "\t+%04lx Flags %08lx\n"
  772. "\t+%04lx DebugFlags %08lx\n",
  773. FIELD_OFFSET(CSR_PROCESS, ProcessHandle), csrp.ProcessHandle,
  774. FIELD_OFFSET(CSR_PROCESS, SequenceNumber), csrp.SequenceNumber,
  775. FIELD_OFFSET(CSR_PROCESS, Flags), csrp.Flags,
  776. FIELD_OFFSET(CSR_PROCESS, DebugFlags), csrp.DebugFlags);
  777. dprintf(
  778. "\t+%04lx DebugUserInterface %08lx\n"
  779. "\t+%04lx ReferenceCount %08lx\n"
  780. "\t+%04lx ProcessGroupId %08lx\n"
  781. "\t+%04lx ProcessGroupSequence %08lx\n",
  782. FIELD_OFFSET(CSR_PROCESS, DebugUserInterface.UniqueProcess), csrp.DebugUserInterface.UniqueProcess,
  783. FIELD_OFFSET(CSR_PROCESS, ReferenceCount), csrp.ReferenceCount,
  784. FIELD_OFFSET(CSR_PROCESS, ProcessGroupId), csrp.ProcessGroupId,
  785. FIELD_OFFSET(CSR_PROCESS, ProcessGroupSequence), csrp.ProcessGroupSequence);
  786. dprintf(
  787. "\t+%04lx fVDM %08lx\n"
  788. "\t+%04lx ThreadCount %08lx\n"
  789. "\t+%04lx PriorityClass %08lx\n"
  790. "\t+%04lx ShutdownLevel %08lx\n"
  791. "\t+%04lx ShutdownFlags %08lx\n",
  792. FIELD_OFFSET(CSR_PROCESS, fVDM), csrp.fVDM,
  793. FIELD_OFFSET(CSR_PROCESS, ThreadCount), csrp.ThreadCount,
  794. FIELD_OFFSET(CSR_PROCESS, PriorityClass), csrp.PriorityClass,
  795. FIELD_OFFSET(CSR_PROCESS, ShutdownLevel), csrp.ShutdownLevel,
  796. FIELD_OFFSET(CSR_PROCESS, ShutdownFlags), csrp.ShutdownFlags);
  797. //
  798. // Now dump simple thread info for this processes' threads.
  799. //
  800. ListHead = &pcsrpT->ThreadList;
  801. b = ReadMemory( (ULONG_PTR)(&ListHead->Flink), &ListNext, sizeof(ListNext), NULL);
  802. if ( !b ) {
  803. dprintf( "NTSDEXTS: Unable to read ListNext\n" );
  804. goto Exit;
  805. }
  806. dprintf("Threads:\n");
  807. while (ListNext != ListHead) {
  808. pcsrt = CONTAINING_RECORD(ListNext, CSR_THREAD, Link);
  809. //
  810. // Make sure this pcsrt is somewhat real so we don't loop forever.
  811. //
  812. b = ReadMemory( (ULONG_PTR)(&pcsrt->ClientId.UniqueProcess), &dwProcessId, sizeof(dwProcessId), NULL);
  813. if ( !b ) {
  814. dprintf( "NTSDEXTS: Unable to read ListNext\n" );
  815. goto Exit;
  816. }
  817. if (dwProcessId != (DWORD_PTR)csrp.ClientId.UniqueProcess) {
  818. dprintf("Invalid thread. Probably invalid argument to this extension.\n");
  819. goto Exit;
  820. }
  821. HexToString((ULONG_PTR)pcsrt, ach);
  822. dt(Client, ach);
  823. b = ReadMemory( (ULONG_PTR)(&ListNext->Flink), &ListNext, sizeof(ListNext), NULL);
  824. if ( !b ) {
  825. dprintf( "NTSDEXTS: Unable to read ListNext\n" );
  826. goto Exit;
  827. }
  828. }
  829. Exit:
  830. EXIT_API();
  831. }
  832. VOID
  833. DllsExtension(
  834. PCSTR args,
  835. PPEB ProcessPeb
  836. );
  837. DECLARE_API( gatom )
  838. /*++
  839. Routine Description:
  840. This function is called as an NTSD extension to dump the global atom table
  841. kept in kernel mode
  842. Called as:
  843. !gatom
  844. Return Value:
  845. None.
  846. --*/
  847. {
  848. NTSTATUS Status;
  849. ATOM_TABLE_INFORMATION TableInfo;
  850. PATOM_TABLE_INFORMATION pTableInfo = NULL;
  851. PATOM_BASIC_INFORMATION pBasicInfo = NULL;
  852. ULONG RequiredLength, MaxLength, i;
  853. INIT_API();
  854. dprintf("\nGlobal atom table ");
  855. Status = NtQueryInformationAtom( RTL_ATOM_INVALID_ATOM,
  856. AtomTableInformation,
  857. &TableInfo,
  858. sizeof( TableInfo ),
  859. &RequiredLength
  860. );
  861. if (Status != STATUS_INFO_LENGTH_MISMATCH) {
  862. dprintf( " - cant get information - %x\n", Status );
  863. goto Exit;
  864. }
  865. RequiredLength += 100 * sizeof( RTL_ATOM );
  866. pTableInfo = LocalAlloc( 0, RequiredLength );
  867. if (pTableInfo == NULL) {
  868. dprintf( " - cant allocate memory for %u atoms\n", RequiredLength / sizeof( RTL_ATOM ) );
  869. goto Exit;
  870. }
  871. Status = NtQueryInformationAtom( RTL_ATOM_INVALID_ATOM,
  872. AtomTableInformation,
  873. pTableInfo,
  874. RequiredLength,
  875. &RequiredLength
  876. );
  877. if (!NT_SUCCESS( Status )) {
  878. dprintf( " - cant get information about %x atoms - %x\n", RequiredLength / sizeof( RTL_ATOM ), Status );
  879. goto Exit;
  880. }
  881. MaxLength = sizeof( *pBasicInfo ) + RTL_ATOM_MAXIMUM_NAME_LENGTH;
  882. pBasicInfo = LocalAlloc( 0, MaxLength );
  883. if (!pBasicInfo) {
  884. dprintf("LocalAlloc failed.\n");
  885. goto Exit;
  886. }
  887. for (i=0; i<pTableInfo->NumberOfAtoms; i++) {
  888. Status = NtQueryInformationAtom( pTableInfo->Atoms[ i ],
  889. AtomBasicInformation,
  890. pBasicInfo,
  891. MaxLength,
  892. &RequiredLength
  893. );
  894. if (!NT_SUCCESS( Status )) {
  895. dprintf( "%hx *** query failed (%x)\n", pTableInfo->Atoms[ i ], Status );
  896. }
  897. else {
  898. dprintf( "%hx(%2d) = %ls (%d)%s\n",
  899. pTableInfo->Atoms[ i ],
  900. pBasicInfo->UsageCount,
  901. pBasicInfo->Name,
  902. pBasicInfo->NameLength,
  903. pBasicInfo->Flags & RTL_ATOM_PINNED ? " pinned" : ""
  904. );
  905. }
  906. }
  907. Exit:
  908. if (pTableInfo != NULL) {
  909. LocalFree (pTableInfo);
  910. }
  911. if (pBasicInfo != NULL) {
  912. LocalFree (pBasicInfo);
  913. }
  914. EXIT_API();
  915. }
  916. /*++
  917. Routine Description:
  918. This function is called as an NTSD extension to mimic the !handle
  919. kd command. This will walk through the debuggee's handle table
  920. and duplicate the handle into the ntsd process, then call NtQueryobjectInfo
  921. to find out what it is.
  922. Called as:
  923. !handle [handle [flags [Type]]]
  924. If the handle is 0 or -1, all handles are scanned. If the handle is not
  925. zero, that particular handle is examined. The flags are as follows
  926. (corresponding to secexts.c):
  927. 1 - Get type information (default)
  928. 2 - Get basic information
  929. 4 - Get name information
  930. 8 - Get object specific info (where available)
  931. If Type is specified, only object of that type are scanned. Type is a
  932. standard NT type name, e.g. Event, Semaphore, etc. Case sensitive, of
  933. course.
  934. Examples:
  935. !handle -- dumps the types of all the handles, and a summary table
  936. !handle 0 0 -- dumps a summary table of all the open handles
  937. !handle 0 f -- dumps everything we can find about a handle.
  938. !handle 0 f Event
  939. -- dumps everything we can find about open events
  940. --*/
  941. DECLARE_API( handle )
  942. {
  943. HANDLE hThere;
  944. DWORD Type;
  945. DWORD Mask;
  946. DWORD HandleCount;
  947. NTSTATUS Status;
  948. DWORD Total;
  949. DWORD TypeCounts[TYPE_MAX];
  950. DWORD Handle;
  951. DWORD Hits;
  952. DWORD Matches;
  953. DWORD ObjectType;
  954. BOOL GetDirect;
  955. ULONG SessionType;
  956. ULONG SessionQual;
  957. INIT_API();
  958. Mask = GHI_TYPE ;
  959. hThere = INVALID_HANDLE_VALUE;
  960. Type = 0;
  961. while (*args == ' ') {
  962. args++;
  963. }
  964. if ( strcmp( args, "-?" ) == 0 )
  965. {
  966. help(Client, "handle" );
  967. goto Exit;
  968. }
  969. hThere = (PVOID) GetExpression( args );
  970. while (*args && (*args != ' ') ) {
  971. args++;
  972. }
  973. while (*args == ' ') {
  974. args++;
  975. }
  976. if (*args) {
  977. Mask = (DWORD)GetExpression( args );
  978. }
  979. while (*args && (*args != ' ') ) {
  980. args++;
  981. }
  982. while (*args == ' ') {
  983. args++;
  984. }
  985. if (*args) {
  986. Type = GetObjectTypeIndex( (LPSTR)args );
  987. if (Type == (DWORD) -1 ) {
  988. dprintf("Unknown type '%s'\n", args );
  989. goto Exit;
  990. }
  991. }
  992. //
  993. // if they specified 0, they just want the summary. Make sure nothing
  994. // sneaks out.
  995. //
  996. if ( Mask == 0 ) {
  997. Mask = GHI_SILENT;
  998. }
  999. //
  1000. // If this is a dump debug session,
  1001. // check and see whether we can retrieve handle
  1002. // information through the engine interface.
  1003. //
  1004. if (g_ExtControl == NULL ||
  1005. g_ExtControl->lpVtbl->
  1006. GetDebuggeeType(g_ExtControl, &SessionType, &SessionQual) != S_OK) {
  1007. SessionType = DEBUG_CLASS_USER_WINDOWS;
  1008. SessionQual = DEBUG_USER_WINDOWS_PROCESS;
  1009. }
  1010. if (SessionType == DEBUG_CLASS_USER_WINDOWS &&
  1011. SessionQual != DEBUG_USER_WINDOWS_PROCESS) {
  1012. // This is a dump or remote session so we have to use
  1013. // the stored handle information accessible
  1014. // through the interface.
  1015. if (g_ExtData2 == NULL ||
  1016. g_ExtData2->lpVtbl->
  1017. ReadHandleData(g_ExtData2, 0, DEBUG_HANDLE_DATA_TYPE_HANDLE_COUNT,
  1018. &HandleCount, sizeof(HandleCount),
  1019. NULL) != S_OK) {
  1020. dprintf("Unable to read handle information\n");
  1021. goto Exit;
  1022. }
  1023. GetDirect = FALSE;
  1024. } else {
  1025. // This is a live session so we can make direct NT calls.
  1026. // More information is available this way so we use it
  1027. // whenever we can.
  1028. GetDirect = TRUE;
  1029. }
  1030. //
  1031. // hThere of 0 indicates all handles.
  1032. //
  1033. if ((hThere == 0) || (hThere == INVALID_HANDLE_VALUE)) {
  1034. if (GetDirect) {
  1035. Status = NtQueryInformationProcess( g_hCurrentProcess,
  1036. ProcessHandleCount,
  1037. &HandleCount,
  1038. sizeof( HandleCount ),
  1039. NULL );
  1040. if ( !NT_SUCCESS( Status ) ) {
  1041. goto Exit;
  1042. }
  1043. }
  1044. Hits = 0;
  1045. Handle = 0;
  1046. Matches = 0;
  1047. ZeroMemory( TypeCounts, sizeof(TypeCounts) );
  1048. while ( Hits < HandleCount ) {
  1049. if ( Type ) {
  1050. if (GetHandleInfo( GetDirect, g_hCurrentProcess,
  1051. (HANDLE) (DWORD_PTR) Handle,
  1052. GHI_TYPE | GHI_SILENT,
  1053. &ObjectType ) ) {
  1054. Hits++;
  1055. if ( ObjectType == Type ) {
  1056. GetHandleInfo( GetDirect, g_hCurrentProcess,
  1057. (HANDLE)(DWORD_PTR)Handle,
  1058. Mask,
  1059. &ObjectType );
  1060. Matches ++;
  1061. }
  1062. }
  1063. } else {
  1064. if (GetHandleInfo( GetDirect, g_hCurrentProcess,
  1065. (HANDLE)(DWORD_PTR)Handle,
  1066. GHI_TYPE | GHI_SILENT,
  1067. &ObjectType) ) {
  1068. Hits++;
  1069. TypeCounts[ ObjectType ] ++;
  1070. GetHandleInfo( GetDirect, g_hCurrentProcess,
  1071. (HANDLE)(DWORD_PTR)Handle,
  1072. Mask,
  1073. &ObjectType );
  1074. }
  1075. }
  1076. Handle += 4;
  1077. }
  1078. if ( Type == 0 ) {
  1079. dprintf( "%d Handles\n", Hits );
  1080. dprintf( "Type \tCount\n");
  1081. for (Type = 0; Type < TYPE_MAX ; Type++ ) {
  1082. if (TypeCounts[Type]) {
  1083. dprintf("%-15ws\t%d\n", pszTypeNames[Type], TypeCounts[Type]);
  1084. }
  1085. }
  1086. } else {
  1087. dprintf("%d handles of type %ws\n", Matches, pszTypeNames[Type] );
  1088. }
  1089. } else {
  1090. GetHandleInfo( GetDirect, g_hCurrentProcess, hThere, Mask, &Type );
  1091. }
  1092. Exit:
  1093. EXIT_API();
  1094. }
  1095. DECLARE_API( threadtoken )
  1096. {
  1097. HANDLE hToken ;
  1098. NTSTATUS Status ;
  1099. INIT_API();
  1100. dprintf("\n\n!threadtoken has been replaced by !token (in exts.dll).\n\n");
  1101. EXIT_API();
  1102. }
  1103. #define PAGE_ALL (PAGE_READONLY|\
  1104. PAGE_READWRITE|\
  1105. PAGE_WRITECOPY|\
  1106. PAGE_EXECUTE|\
  1107. PAGE_EXECUTE_READ|\
  1108. PAGE_EXECUTE_READWRITE|\
  1109. PAGE_EXECUTE_WRITECOPY|\
  1110. PAGE_NOACCESS)
  1111. VOID
  1112. printflags(
  1113. DWORD Flags
  1114. )
  1115. {
  1116. switch (Flags & PAGE_ALL) {
  1117. case PAGE_READONLY:
  1118. dprintf("PAGE_READONLY");
  1119. break;
  1120. case PAGE_READWRITE:
  1121. dprintf("PAGE_READWRITE");
  1122. break;
  1123. case PAGE_WRITECOPY:
  1124. dprintf("PAGE_WRITECOPY");
  1125. break;
  1126. case PAGE_EXECUTE:
  1127. dprintf("PAGE_EXECUTE");
  1128. break;
  1129. case PAGE_EXECUTE_READ:
  1130. dprintf("PAGE_EXECUTE_READ");
  1131. break;
  1132. case PAGE_EXECUTE_READWRITE:
  1133. dprintf("PAGE_EXECUTE_READWRITE");
  1134. break;
  1135. case PAGE_EXECUTE_WRITECOPY:
  1136. dprintf("PAGE_EXECUTE_WRITECOPY");
  1137. break;
  1138. case PAGE_NOACCESS:
  1139. if ((Flags & ~PAGE_NOACCESS) == 0) {
  1140. dprintf("PAGE_NOACCESS");
  1141. break;
  1142. } // else fall through
  1143. default:
  1144. dprintf("*** Invalid page protection ***\n");
  1145. return;
  1146. break;
  1147. }
  1148. if (Flags & PAGE_NOCACHE) {
  1149. dprintf(" + PAGE_NOCACHE");
  1150. }
  1151. if (Flags & PAGE_GUARD) {
  1152. dprintf(" + PAGE_GUARD");
  1153. }
  1154. dprintf("\n");
  1155. }
  1156. /*++
  1157. Routine Description:
  1158. This function is called as an NTSD extension to dump registry information
  1159. Called as:
  1160. !dreg -[d|w] <keyPath>[![<valueName> | *]]
  1161. Arguments:
  1162. hCurrentProcess - Supplies a handle to the current process (at the
  1163. time the extension was called).
  1164. hCurrentThread - Supplies a handle to the current thread (at the
  1165. time the extension was called).
  1166. CurrentPc - Supplies the current pc at the time the extension is
  1167. called.
  1168. lpExtensionApis - Supplies the address of the functions callable
  1169. by this extension.
  1170. args - Supplies the pattern and expression for this
  1171. command.
  1172. Return Value:
  1173. None.
  1174. --*/
  1175. DECLARE_API( dreg )
  1176. {
  1177. DWORD opts = 1;
  1178. INIT_API();
  1179. // Skip past leading spaces
  1180. while (*args == ' ')
  1181. {
  1182. args++;
  1183. }
  1184. if (*args == '-')
  1185. {
  1186. args++;
  1187. switch (*args)
  1188. {
  1189. case 'd':
  1190. opts = 4;
  1191. break;
  1192. case 'w':
  1193. opts = 2;
  1194. break;
  1195. default:
  1196. opts = 1;
  1197. break;
  1198. }
  1199. if (*args)
  1200. {
  1201. // expect a space between options
  1202. args++;
  1203. // Skip past leading spaces
  1204. while (*args == ' ')
  1205. {
  1206. args++;
  1207. }
  1208. }
  1209. }
  1210. Idreg(opts, (LPSTR)args);
  1211. EXIT_API();
  1212. }
  1213. NTSTATUS
  1214. GetHandleTraceInfo(
  1215. PPROCESS_HANDLE_TRACING_QUERY *Info,
  1216. PULONG BufferSize
  1217. )
  1218. {
  1219. NTSTATUS Status = NO_ERROR;
  1220. ULONG CurrentBufferSize = *BufferSize;
  1221. if (*BufferSize == 0)
  1222. {
  1223. CurrentBufferSize = sizeof (PROCESS_HANDLE_TRACING_QUERY);
  1224. }
  1225. while (TRUE) {
  1226. //
  1227. // Allocate a new buffer
  1228. //
  1229. *Info = (PPROCESS_HANDLE_TRACING_QUERY)malloc (CurrentBufferSize);
  1230. if (*Info == NULL) {
  1231. dprintf ("ERROR: Cannot allocate buffer with size 0x%p\n",
  1232. CurrentBufferSize);
  1233. Status = ERROR_OUTOFMEMORY;
  1234. goto DoneAll;
  1235. }
  1236. ZeroMemory( *Info,
  1237. CurrentBufferSize );
  1238. Status = NtQueryInformationProcess (g_hCurrentProcess,
  1239. ProcessHandleTracing,
  1240. *Info,
  1241. CurrentBufferSize,
  1242. NULL );
  1243. if( NT_SUCCESS (Status) ) {
  1244. //
  1245. // We have all the information ready
  1246. //
  1247. break;
  1248. }
  1249. CurrentBufferSize = sizeof (PROCESS_HANDLE_TRACING_QUERY) + (*Info)->TotalTraces * sizeof ((*Info)->HandleTrace[ 0 ]);
  1250. free (*Info);
  1251. *Info = NULL;
  1252. if( CheckControlC() ) {
  1253. goto DoneAll;
  1254. }
  1255. if (Status != STATUS_INFO_LENGTH_MISMATCH) {
  1256. //
  1257. // No reason to try querying again
  1258. //
  1259. if (Status == STATUS_INVALID_PARAMETER) {
  1260. dprintf ("App verifier handle tracing is not enabled for this process.\n");
  1261. }
  1262. else {
  1263. dprintf ("Query process information failed, status 0x%X\n",
  1264. Status);
  1265. }
  1266. goto DoneAll;
  1267. }
  1268. //
  1269. // Try allocating another buffer with the new size
  1270. //
  1271. }
  1272. *BufferSize = CurrentBufferSize;
  1273. return NO_ERROR;
  1274. DoneAll:
  1275. free (*Info);
  1276. *Info = NULL;
  1277. return Status;
  1278. }
  1279. HRESULT
  1280. _EFN_GetHandleTrace(
  1281. PDEBUG_CLIENT Client,
  1282. ULONG TraceType,
  1283. ULONG StartIndex,
  1284. PULONG64 HandleValue,
  1285. PULONG64 StackFunctions,
  1286. ULONG StackTraceSize
  1287. )
  1288. /*+++
  1289. This extension function returns handle trace info for a given type of trace,
  1290. or handle.
  1291. Arguments:
  1292. If TraceType is set it returns first trace found of that type.
  1293. If StartIndex is set it starts looking for traces from that index onwards
  1294. If HandleValue is set it returns first stack found of that handle
  1295. Return Value:
  1296. S_OK for success.
  1297. --*/
  1298. {
  1299. HRESULT Hr = E_FAIL;
  1300. PPROCESS_HANDLE_TRACING_QUERY Info = NULL;
  1301. ULONG InfoSize = 0;
  1302. ULONG CrtStackTrace;
  1303. INIT_API();
  1304. if (NT_SUCCESS(Hr = GetHandleTraceInfo(&Info, &InfoSize)) && (Info != NULL))
  1305. {
  1306. for (CrtStackTrace = StartIndex; CrtStackTrace < Info->TotalTraces; CrtStackTrace += 1) {
  1307. if( CheckControlC() ) {
  1308. CrtStackTrace += 1;
  1309. goto Done;
  1310. }
  1311. if (*HandleValue == 0 || *HandleValue == (ULONG64) Info->HandleTrace[ CrtStackTrace ].Handle) {
  1312. if (Info->HandleTrace[ CrtStackTrace ].Type == TraceType ||
  1313. TraceType == 0)
  1314. {
  1315. ULONG CapturedAddressIndex;
  1316. PVOID *CrtStack;
  1317. PVOID CapturedAddress;
  1318. *HandleValue = (ULONG64) Info->HandleTrace[ CrtStackTrace ].Handle;
  1319. Hr = S_OK;
  1320. for (CapturedAddressIndex = 0, CrtStack = &Info->HandleTrace[ CrtStackTrace ].Stacks[ 0 ];
  1321. CapturedAddressIndex < (sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks) /
  1322. sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks[0]));
  1323. CapturedAddressIndex += 1, CrtStack += 1) {
  1324. if( CheckControlC() ) {
  1325. CrtStackTrace += 1;
  1326. goto Done;
  1327. }
  1328. CapturedAddress = *CrtStack;
  1329. if (CapturedAddress == NULL) {
  1330. //
  1331. // Done with dumping this stack trace
  1332. //
  1333. goto Done;
  1334. }
  1335. if (StackTraceSize > CapturedAddressIndex)
  1336. {
  1337. if (sizeof(ULONG64) == sizeof(PVOID))
  1338. {
  1339. StackFunctions[CapturedAddressIndex] = (ULONG64) (LONG64) (ULONG_PTR) CapturedAddress;
  1340. } else
  1341. {
  1342. StackFunctions[CapturedAddressIndex] = (ULONG64) (LONG64) (LONG) (ULONG_PTR) CapturedAddress;
  1343. }
  1344. }
  1345. }
  1346. goto Done;
  1347. }
  1348. }
  1349. }
  1350. }
  1351. Done:
  1352. if (Info)
  1353. {
  1354. free (Info);
  1355. }
  1356. EXIT_API();
  1357. return Hr;
  1358. }
  1359. /*++
  1360. Routine Description:
  1361. This function is called as an NTSD extension to dump handle tracing information
  1362. Called as:
  1363. !htrace [handle]
  1364. Arguments:
  1365. args - Supplies the pattern and expression for this
  1366. command.
  1367. Return Value:
  1368. None.
  1369. --*/
  1370. DECLARE_API( htrace )
  1371. {
  1372. HANDLE Handle;
  1373. PPROCESS_HANDLE_TRACING_QUERY Info;
  1374. ULONG_PTR Displacement;
  1375. NTSTATUS Status;
  1376. ULONG CurrentBufferSize;
  1377. ULONG CrtStackTrace;
  1378. ULONG EntriesDisplayed;
  1379. ULONG CapturedAddressIndex;
  1380. PVOID *CrtStack;
  1381. PVOID CapturedAddress;
  1382. SYSTEM_BASIC_INFORMATION SysBasicInfo;
  1383. CHAR Symbol[ 1024 ];
  1384. INIT_API();
  1385. Info = NULL;
  1386. CrtStackTrace = 0;
  1387. EntriesDisplayed = 0;
  1388. //
  1389. // Did the user ask for some help?
  1390. //
  1391. if (strcmp (args, "-?") == 0 ||
  1392. strcmp (args, "?") == 0 ||
  1393. strcmp (args, "-h") == 0) {
  1394. dprintf ("!htrace [handle]\n");
  1395. goto DoneAll;
  1396. }
  1397. //
  1398. // Get the handle from the command line
  1399. //
  1400. Handle = (HANDLE)GetExpression (args);
  1401. //
  1402. // Get the stack traces using NtQueryInformationProcess
  1403. //
  1404. Info = NULL; CurrentBufferSize = 0;
  1405. Status = GetHandleTraceInfo(&Info, &CurrentBufferSize);
  1406. if (!NT_SUCCESS(Status))
  1407. {
  1408. goto DoneAll;
  1409. }
  1410. //
  1411. // If we have 0 stack traces there is nothing we can dump
  1412. //
  1413. if (Info->TotalTraces == 0) {
  1414. dprintf( "No stack traces available.\n" );
  1415. goto DoneAll;
  1416. }
  1417. //
  1418. // Find out the highest user address because
  1419. // we will skip kernel mode addresses from the stack traces.
  1420. //
  1421. Status = NtQuerySystemInformation (SystemBasicInformation,
  1422. &SysBasicInfo,
  1423. sizeof (SysBasicInfo),
  1424. NULL);
  1425. if (!NT_SUCCESS (Status)) {
  1426. dprintf ("Query system basic information failed, status 0x%X\n",
  1427. Status);
  1428. goto DoneAll;
  1429. }
  1430. //
  1431. // Dump all the stack traces.
  1432. //
  1433. for (CrtStackTrace = 0; CrtStackTrace < Info->TotalTraces; CrtStackTrace += 1) {
  1434. if( CheckControlC() ) {
  1435. CrtStackTrace += 1;
  1436. goto DoneDumping;
  1437. }
  1438. if (Handle == 0 || Handle == Info->HandleTrace[ CrtStackTrace ].Handle) {
  1439. EntriesDisplayed += 1;
  1440. dprintf ("--------------------------------------\n"
  1441. "Handle = 0x%p - ",
  1442. Info->HandleTrace[ CrtStackTrace ].Handle);
  1443. switch( Info->HandleTrace[ CrtStackTrace ].Type ) {
  1444. case HANDLE_TRACE_DB_OPEN:
  1445. dprintf( "OPEN:\n" );
  1446. break;
  1447. case HANDLE_TRACE_DB_CLOSE:
  1448. dprintf( "CLOSE:\n" );
  1449. break;
  1450. case HANDLE_TRACE_DB_BADREF:
  1451. dprintf( "*** BAD REFERENCE ***:\n" );
  1452. break;
  1453. default:
  1454. dprintf( "Invalid operation type: %u\n",
  1455. Info->HandleTrace[ CrtStackTrace ].Type );
  1456. goto DoneDumping;
  1457. }
  1458. for (CapturedAddressIndex = 0, CrtStack = &Info->HandleTrace[ CrtStackTrace ].Stacks[ 0 ];
  1459. CapturedAddressIndex < (sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks) /
  1460. sizeof(Info->HandleTrace[ CrtStackTrace ].Stacks[0]));
  1461. CapturedAddressIndex += 1, CrtStack += 1) {
  1462. if( CheckControlC() ) {
  1463. CrtStackTrace += 1;
  1464. goto DoneDumping;
  1465. }
  1466. CapturedAddress = *CrtStack;
  1467. if (CapturedAddress == NULL) {
  1468. //
  1469. // Done with dumping this stack trace
  1470. //
  1471. break;
  1472. }
  1473. if ((ULONG_PTR)CapturedAddress > SysBasicInfo.MaximumUserModeAddress) {
  1474. //
  1475. // Skip kernel-mode addresses
  1476. //
  1477. continue;
  1478. }
  1479. GetSymbol (CapturedAddress,
  1480. Symbol,
  1481. &Displacement);
  1482. dprintf ("0x%p: %s+0x%p\n",
  1483. CapturedAddress,
  1484. Symbol,
  1485. Displacement );
  1486. }
  1487. }
  1488. }
  1489. DoneDumping:
  1490. dprintf ("\n--------------------------------------\n"
  1491. "Parsed 0x%X stack traces.\n"
  1492. "Dumped 0x%X stack traces.\n",
  1493. CrtStackTrace,
  1494. EntriesDisplayed);
  1495. DoneAll:
  1496. if (Info != NULL) {
  1497. free (Info);
  1498. }
  1499. EXIT_API();
  1500. }