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.

3347 lines
81 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. oh.cxx
  5. Abstract:
  6. Prints out kernel handle information for a given process or systemwide
  7. and compares logs with handle information for possible leaks.
  8. Author:
  9. SteveWo (probably)
  10. ChrisW - tweaks, GC style leak checking
  11. SilviuC - support for stack traces
  12. SilviuC - added log compare functionality (a la ohcmp)
  13. Futures:
  14. get -h to work without -p (ie stack traces for all processes)
  15. for the -u feature, look for unaligned values
  16. --*/
  17. //
  18. // OH version.
  19. //
  20. // Please update this for important changes
  21. // so that people can understand what version of the tool
  22. // created a log.
  23. //
  24. #define LARGE_HITCOUNT 1234
  25. #include <assert.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <ctype.h>
  30. #include <memory.h>
  31. #include <tchar.h>
  32. #include <nt.h>
  33. #include <ntrtl.h>
  34. #include <nturtl.h>
  35. #include <windows.h>
  36. #include <common.ver>
  37. #include <dbghelp.h>
  38. #include "MAPSTRINGINT.h"
  39. LPTSTR OhHelpText =
  40. TEXT("oh - Object handles dump --") BUILD_MACHINE_TAG TEXT("\n")
  41. VER_LEGALCOPYRIGHT_STR TEXT("\n")
  42. TEXT(" \n")
  43. TEXT("OH [DUMP_OPTIONS ...] \n")
  44. TEXT("OH [FLAGS_OPTIONS ...] \n")
  45. TEXT("OH -c [COMPARE_OPTIONS ...] BEFORE_LOG AFTER_LOG \n")
  46. TEXT(" \n")
  47. TEXT("DUMP_OPTIONS are: \n")
  48. TEXT(" \n")
  49. TEXT(" -p N - displays only open handles for process with ID of n. If not \n")
  50. TEXT(" specified perform a system wide dump. \n")
  51. TEXT(" -t TYPENAME - displays only open object names of specified type. \n")
  52. TEXT(" -o FILENAME - specifies the name of the file to write the output to.\n")
  53. TEXT(" -a includes objects with no name. \n")
  54. TEXT(" -s display summary information \n")
  55. TEXT(" -h display stack traces for handles (a process ID must be specified)\n")
  56. TEXT(" -u display only handles with no references in process memory \n")
  57. TEXT(" -v verbose mode (used for debugging oh) \n")
  58. TEXT(" NAME - displays only handles that contain the specified name. \n")
  59. TEXT(" \n")
  60. TEXT("FLAGS_OPTIONS are: \n")
  61. TEXT(" \n")
  62. TEXT(" [+kst|-kst] - enable or disable kst flag (kernel mode stack traces).\n")
  63. TEXT(" [+otl|-otl] - enable or disable otl flag (object lists). \n")
  64. TEXT(" \n")
  65. TEXT("The `OH [+otl] [+kst]' command can be used to set the global flags \n")
  66. TEXT("needed by OH. `+otl' is needed for all OH options and `+kst' is needed \n")
  67. TEXT("by the `-h' option. The changes will take effect only after reboot. \n")
  68. TEXT("The flags can be disabled by using `-otl' or `-kst' respectively. \n")
  69. TEXT(" \n")
  70. TEXT("COMPARE_OPTIONS are: \n")
  71. TEXT(" \n")
  72. TEXT(" -l Print most interesting increases in a separate initial section. \n")
  73. TEXT(" -t Do not add TRACE id to the names if files contain traces. \n")
  74. TEXT(" -all Report decreases as well as increases. \n")
  75. TEXT(" \n")
  76. TEXT("If the OH files have been created with -h option (they contain traces) \n")
  77. TEXT("then handle names will be printed using this syntax: (TRACEID) NAME. \n")
  78. TEXT("In case of a potential leak just search for the TRACEID in the original\n")
  79. TEXT("OH file to find the stack trace. \n")
  80. TEXT(" \n");
  81. typedef DWORD PID;
  82. PID ProcessId;
  83. WCHAR TypeName[ 128 ];
  84. WCHAR SearchName[ 512 ];
  85. typedef struct _HANDLE_AUX_INFO {
  86. ULONG_PTR HandleValue; // value of handle for a process
  87. PID Pid; // Process ID for this handle
  88. DWORD HitCount; // number of references
  89. PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntry; // points to main table entry
  90. } HANDLE_AUX_INFO, *PHANDLE_AUX_INFO;
  91. //
  92. // Globals
  93. //
  94. struct {
  95. FILE* Output; // output file
  96. PRTL_PROCESS_BACKTRACES TraceDatabase; // stack traces
  97. HANDLE TargetProcess; // process handle to get symtab info from
  98. BOOL DumpTraces; // True if we are to dump stack traces
  99. HANDLE_AUX_INFO* AuxInfo; // additional data for every handle
  100. DWORD AuxSize; // number of entries in AuxInfo
  101. BOOL fOnlyShowNoRefs; // Only show handles with NO references
  102. BOOL fVerbose; // display debugging text
  103. } Globals;
  104. //
  105. // Log comparing main function (ohcmp).
  106. //
  107. VOID
  108. OhCmpCompareLogs (
  109. IN LONG argc,
  110. IN LPTSTR argv[]
  111. );
  112. //
  113. // Global flags handling
  114. //
  115. DWORD
  116. OhGetCurrentGlobalFlags (
  117. );
  118. VOID
  119. OhSetRequiredGlobalFlag (
  120. DWORD Flags
  121. );
  122. VOID
  123. OhResetRequiredGlobalFlag (
  124. DWORD Flags
  125. );
  126. //
  127. // Memory management
  128. //
  129. PVOID
  130. OhAlloc (
  131. SIZE_T Size
  132. );
  133. VOID
  134. OhFree (
  135. PVOID P
  136. );
  137. PVOID
  138. OhZoneAlloc(
  139. IN OUT SIZE_T *Length
  140. );
  141. VOID
  142. OhZoneFree(
  143. IN PVOID Buffer
  144. );
  145. PVOID
  146. OhZoneAllocEx(
  147. SIZE_T Size
  148. );
  149. //
  150. // Others
  151. //
  152. VOID
  153. OhError (
  154. PCHAR File,
  155. ULONG Line,
  156. PCHAR Format,
  157. ...
  158. );
  159. VOID
  160. OhDumpHandles (
  161. BOOLEAN DumpAnonymousHandles
  162. );
  163. VOID
  164. OhInitializeHandleStackTraces (
  165. PID Pid
  166. );
  167. VOID
  168. OhDumpStackTrace (
  169. PRTL_PROCESS_BACKTRACES TraceDatabase,
  170. USHORT Index
  171. );
  172. BOOL
  173. OhSetSymbolsPath (
  174. );
  175. VOID
  176. OhStampLog (
  177. VOID
  178. );
  179. VOID
  180. Info (
  181. PCHAR Format,
  182. ...
  183. );
  184. VOID
  185. OhComment (
  186. PCHAR Format,
  187. ...
  188. );
  189. VOID
  190. Warning (
  191. PCHAR File,
  192. ULONG Line,
  193. PCHAR Format,
  194. ...
  195. );
  196. PRTL_DEBUG_INFORMATION
  197. RtlQuerySystemDebugInformation(
  198. ULONG Flags
  199. );
  200. VOID
  201. DoSummary( VOID );
  202. BOOLEAN
  203. AnsiToUnicode(
  204. LPCSTR Source,
  205. PWSTR Destination,
  206. ULONG NumberOfChars
  207. )
  208. {
  209. if (NumberOfChars == 0) {
  210. NumberOfChars = strlen( Source );
  211. }
  212. if (MultiByteToWideChar( CP_ACP,
  213. MB_PRECOMPOSED,
  214. Source,
  215. NumberOfChars,
  216. Destination,
  217. NumberOfChars
  218. ) != (LONG)NumberOfChars
  219. ) {
  220. SetLastError( ERROR_NO_UNICODE_TRANSLATION );
  221. return FALSE;
  222. }
  223. else {
  224. Destination[ NumberOfChars ] = UNICODE_NULL;
  225. return TRUE;
  226. }
  227. }
  228. /////////////////////////////////////////////////////////////////////
  229. ///////////////////////////////////// main(), command line processing
  230. /////////////////////////////////////////////////////////////////////
  231. VOID
  232. Help (
  233. VOID
  234. )
  235. /*++
  236. Routine Description:
  237. This routine prints out a message describing the proper usage of OH.
  238. Arguments:
  239. None.
  240. Return value:
  241. None.
  242. --*/
  243. {
  244. fputs (OhHelpText, stderr);
  245. exit (1);
  246. }
  247. int __cdecl
  248. main (
  249. int argc,
  250. char *argv[],
  251. char *envp[]
  252. )
  253. {
  254. BOOLEAN fAnonymousToo;
  255. BOOLEAN fDoSummary;
  256. char *s;
  257. NTSTATUS Status;
  258. PRTL_DEBUG_INFORMATION p;
  259. CHAR OutputFileName [MAX_PATH];
  260. DWORD GlobalFlags;
  261. int Index;
  262. BOOL FlagsSetRequested = FALSE;
  263. BOOLEAN bJunk;
  264. _try {
  265. //
  266. // Print to stdout for now.
  267. //
  268. Globals.Output = stdout;
  269. //
  270. // Check for help first.
  271. //
  272. if (argc >= 2 && strstr (argv[1], "?") != NULL) {
  273. Help ();
  274. }
  275. //
  276. // Get current global flags.
  277. //
  278. GlobalFlags = OhGetCurrentGlobalFlags ();
  279. OutputFileName[0]='\0';
  280. ProcessId = 0;
  281. fAnonymousToo = FALSE;
  282. fDoSummary = FALSE ;
  283. //
  284. // Before any other command line parsing check for `+otl' or `+kst' options.
  285. //
  286. for (Index = 1; Index < argc; Index += 1) {
  287. if (_stricmp (argv[Index], "+otl") == 0) {
  288. OhSetRequiredGlobalFlag (FLG_MAINTAIN_OBJECT_TYPELIST);
  289. FlagsSetRequested = TRUE;
  290. }
  291. else if (_stricmp (argv[Index], "+kst") == 0) {
  292. OhSetRequiredGlobalFlag (FLG_KERNEL_STACK_TRACE_DB);
  293. FlagsSetRequested = TRUE;
  294. }
  295. else if (_stricmp (argv[Index], "-otl") == 0) {
  296. OhResetRequiredGlobalFlag (FLG_MAINTAIN_OBJECT_TYPELIST);
  297. FlagsSetRequested = TRUE;
  298. }
  299. else if (_stricmp (argv[Index], "-kst") == 0) {
  300. OhResetRequiredGlobalFlag (FLG_KERNEL_STACK_TRACE_DB);
  301. FlagsSetRequested = TRUE;
  302. }
  303. }
  304. if (FlagsSetRequested == TRUE) {
  305. exit (0);
  306. }
  307. //
  308. // Now check if we want log comparing functionality (a la ohcmp).
  309. //
  310. if (argc > 2 && _stricmp (argv[1], "-c") == 0) {
  311. OhCmpCompareLogs (argc - 1, &(argv[1]));
  312. exit (0);
  313. }
  314. //
  315. // Figure out if we have the +otl global flag. We need to do this
  316. // before getting into real oh functionality.
  317. //
  318. if ((GlobalFlags & FLG_MAINTAIN_OBJECT_TYPELIST) == 0) {
  319. Info ("The system global flag `maintain object type lists' is not enabled \n"
  320. "for this system. Please use `oh +otl' to enable it and then reboot. \n");
  321. exit (1);
  322. }
  323. //
  324. // Finally parse the `oh' command line.
  325. //
  326. while (--argc) {
  327. s = *++argv;
  328. if (*s == '/' || *s == '-') {
  329. while (*++s) {
  330. switch (tolower(*s)) {
  331. case 'a':
  332. case 'A':
  333. fAnonymousToo = TRUE;
  334. break;
  335. case 'p':
  336. case 'P':
  337. if (--argc) {
  338. ProcessId = (PID)atol( *++argv );
  339. }
  340. else {
  341. Help();
  342. }
  343. break;
  344. case 'h':
  345. case 'H':
  346. Globals.DumpTraces = TRUE;
  347. break;
  348. case 'o':
  349. case 'O':
  350. if (--argc) {
  351. strncpy( OutputFileName, *++argv, sizeof(OutputFileName)-1 );
  352. OutputFileName[sizeof(OutputFileName)-1]= 0;
  353. }
  354. else {
  355. Help();
  356. }
  357. break;
  358. case 't':
  359. case 'T':
  360. if (--argc) {
  361. AnsiToUnicode( *++argv, TypeName, 0 );
  362. }
  363. else {
  364. Help();
  365. }
  366. break;
  367. case 's':
  368. case 'S':
  369. fDoSummary = TRUE;
  370. break;
  371. case 'u':
  372. case 'U':
  373. Globals.fOnlyShowNoRefs= TRUE;
  374. break;
  375. case 'v':
  376. case 'V':
  377. Globals.fVerbose= TRUE;
  378. break;
  379. default:
  380. Help();
  381. }
  382. }
  383. }
  384. else
  385. if (*SearchName) {
  386. Help();
  387. }
  388. else {
  389. AnsiToUnicode( s, SearchName, 0 );
  390. }
  391. }
  392. if (OutputFileName[0] == '\0') {
  393. Globals.Output = stdout;
  394. }
  395. else {
  396. Globals.Output = fopen (OutputFileName, "w");
  397. }
  398. if (Globals.Output == NULL) {
  399. OhError (NULL, 0,
  400. "cannot open output file `%s' (error %u) \n",
  401. OutputFileName,
  402. GetLastError ());
  403. }
  404. //
  405. // Get debug privilege. This will be useful for opening processes.
  406. //
  407. Status= RtlAdjustPrivilege( SE_DEBUG_PRIVILEGE,
  408. TRUE,
  409. FALSE,
  410. &bJunk);
  411. if( !NT_SUCCESS(Status) ) {
  412. Info ( "RtlAdjustPrivilege(SE_DEBUG) failed with status = %X. -u may not work.",
  413. Status);
  414. }
  415. //
  416. // Stamp the log with OS version, time, machine name, etc.
  417. //
  418. OhStampLog ();
  419. if (Globals.DumpTraces) {
  420. if (ProcessId == 0) {
  421. OhError (NULL,
  422. 0,
  423. "`-h' option can be used only if a process ID is specified with `-p PID'");
  424. }
  425. if ((GlobalFlags & FLG_KERNEL_STACK_TRACE_DB) == 0) {
  426. Info ("The system global flag `get kernel mode stack traces' is not enabled \n"
  427. "for this system. Please use `oh +kst' to enable it and then reboot. \n");
  428. exit (1);
  429. }
  430. OhInitializeHandleStackTraces ( ProcessId );
  431. }
  432. p = RtlQuerySystemDebugInformation( 0 );
  433. if (p == NULL) {
  434. fprintf( stderr, "OH1: Unable to query kernel mode information.\n" );
  435. exit( 1 );
  436. }
  437. OhDumpHandles (fAnonymousToo);
  438. if ( fDoSummary ) {
  439. DoSummary();
  440. }
  441. if (Globals.Output != stdout) {
  442. fclose (Globals.Output);
  443. }
  444. RtlDestroyQueryDebugBuffer( p );
  445. return 0;
  446. }
  447. _except (EXCEPTION_EXECUTE_HANDLER) {
  448. OhComment ("Exception %X raised within OH process. Aborting ... \n",
  449. _exception_code());
  450. }
  451. return 0;
  452. }
  453. typedef struct _PROCESS_INFO {
  454. LIST_ENTRY Entry;
  455. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  456. PSYSTEM_THREAD_INFORMATION ThreadInfo[ 1 ];
  457. } PROCESS_INFO, *PPROCESS_INFO;
  458. LIST_ENTRY ProcessListHead;
  459. PSYSTEM_OBJECTTYPE_INFORMATION ObjectInformation;
  460. PSYSTEM_HANDLE_INFORMATION_EX HandleInformation;
  461. PSYSTEM_PROCESS_INFORMATION ProcessInformation;
  462. typedef struct _TYPE_COUNT {
  463. UNICODE_STRING TypeName ;
  464. ULONG HandleCount ;
  465. } TYPE_COUNT, * PTYPE_COUNT ;
  466. #define MAX_TYPE_NAMES 128
  467. TYPE_COUNT TypeCounts[ MAX_TYPE_NAMES + 1 ] ;
  468. UNICODE_STRING UnknownTypeIndex;
  469. #define RTL_NEW( p ) RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *p ) )
  470. BOOLEAN
  471. OhLoadSystemModules(
  472. PRTL_DEBUG_INFORMATION Buffer
  473. );
  474. BOOLEAN
  475. OhLoadSystemObjects(
  476. PRTL_DEBUG_INFORMATION Buffer
  477. );
  478. BOOLEAN
  479. OhLoadSystemHandles(
  480. PRTL_DEBUG_INFORMATION Buffer
  481. );
  482. BOOLEAN
  483. OhLoadSystemProcesses(
  484. PRTL_DEBUG_INFORMATION Buffer
  485. );
  486. PSYSTEM_PROCESS_INFORMATION
  487. OhFindProcessInfoForCid(
  488. IN PID UniqueProcessId
  489. );
  490. PRTL_DEBUG_INFORMATION
  491. RtlQuerySystemDebugInformation(
  492. ULONG Flags
  493. )
  494. /*++
  495. Routine description:
  496. Parameters:
  497. Return value:
  498. --*/
  499. {
  500. PRTL_DEBUG_INFORMATION Buffer;
  501. Buffer = (PRTL_DEBUG_INFORMATION)RTL_NEW( Buffer );
  502. if (Buffer == NULL) {
  503. return NULL;
  504. }
  505. if (!OhLoadSystemObjects( Buffer )) {
  506. fprintf( stderr, "OH2: Unable to query system object information.\n" );
  507. exit (1);
  508. }
  509. if (!OhLoadSystemHandles( Buffer )) {
  510. fprintf( stderr, "OH3: Unable to query system handle information.\n" );
  511. exit (1);
  512. }
  513. if (!OhLoadSystemProcesses( Buffer )) {
  514. fprintf( stderr, "OH4: Unable to query system process information.\n" );
  515. exit (1);
  516. }
  517. return Buffer;
  518. }
  519. BOOLEAN
  520. OhLoadSystemObjects(
  521. PRTL_DEBUG_INFORMATION Buffer
  522. )
  523. /*++
  524. Routine description:
  525. Parameters:
  526. Return value:
  527. --*/
  528. {
  529. NTSTATUS Status;
  530. SYSTEM_OBJECTTYPE_INFORMATION ObjectInfoBuffer;
  531. SIZE_T RequiredLength, NewLength=0;
  532. ULONG i;
  533. PSYSTEM_OBJECTTYPE_INFORMATION TypeInfo;
  534. ObjectInformation = &ObjectInfoBuffer;
  535. RequiredLength = sizeof( *ObjectInformation );
  536. while (TRUE) {
  537. Status = NtQuerySystemInformation( SystemObjectInformation,
  538. ObjectInformation,
  539. (ULONG)RequiredLength,
  540. (ULONG *)&NewLength
  541. );
  542. if (Status == STATUS_INFO_LENGTH_MISMATCH && NewLength > RequiredLength) {
  543. if (ObjectInformation != &ObjectInfoBuffer) {
  544. OhZoneFree (ObjectInformation);
  545. }
  546. RequiredLength = NewLength + 4096;
  547. ObjectInformation = (PSYSTEM_OBJECTTYPE_INFORMATION)OhZoneAlloc (&RequiredLength);
  548. if( ObjectInformation == NULL ) {
  549. return FALSE;
  550. }
  551. }
  552. else if (!NT_SUCCESS( Status )) {
  553. if( ObjectInformation != &ObjectInfoBuffer ) {
  554. OhZoneFree (ObjectInformation);
  555. }
  556. return FALSE;
  557. }
  558. else {
  559. break;
  560. }
  561. }
  562. TypeInfo = ObjectInformation;
  563. while (TRUE) {
  564. if (TypeInfo->TypeIndex < MAX_TYPE_NAMES) {
  565. TypeCounts[ TypeInfo->TypeIndex ].TypeName = TypeInfo->TypeName;
  566. }
  567. if (TypeInfo->NextEntryOffset == 0) {
  568. break;
  569. }
  570. TypeInfo = (PSYSTEM_OBJECTTYPE_INFORMATION)
  571. ((PCHAR)ObjectInformation + TypeInfo->NextEntryOffset);
  572. }
  573. RtlInitUnicodeString( &UnknownTypeIndex, L"UnknownTypeIdx" );
  574. for (i=0; i<=MAX_TYPE_NAMES; i++) {
  575. if (TypeCounts[ i ].TypeName.Length == 0 ) {
  576. TypeCounts[ i ].TypeName = UnknownTypeIndex;
  577. }
  578. }
  579. return TRUE;
  580. }
  581. BOOLEAN
  582. OhLoadSystemHandles(
  583. PRTL_DEBUG_INFORMATION Buffer
  584. )
  585. /*++
  586. Routine description:
  587. This routine ...
  588. Parameters:
  589. Information buffer to fill.
  590. Return value:
  591. True if all information was obtained from kernel side.
  592. --*/
  593. {
  594. NTSTATUS Status;
  595. SYSTEM_HANDLE_INFORMATION_EX HandleInfoBuffer;
  596. SIZE_T RequiredLength;
  597. SIZE_T NewLength = 0;
  598. PSYSTEM_OBJECTTYPE_INFORMATION TypeInfo;
  599. PSYSTEM_OBJECT_INFORMATION ObjectInfo;
  600. HandleInformation = &HandleInfoBuffer;
  601. RequiredLength = sizeof( *HandleInformation );
  602. while (TRUE) {
  603. Status = NtQuerySystemInformation( SystemExtendedHandleInformation,
  604. HandleInformation,
  605. (ULONG)RequiredLength,
  606. (ULONG *)&NewLength
  607. );
  608. if (Status == STATUS_INFO_LENGTH_MISMATCH && NewLength > RequiredLength) {
  609. if (HandleInformation != &HandleInfoBuffer) {
  610. OhZoneFree (HandleInformation);
  611. }
  612. RequiredLength = NewLength + 4096; // slop, since we may trigger more handle creations.
  613. HandleInformation = (PSYSTEM_HANDLE_INFORMATION_EX)OhZoneAlloc( &RequiredLength );
  614. if (HandleInformation == NULL) {
  615. return FALSE;
  616. }
  617. }
  618. else if (!NT_SUCCESS( Status )) {
  619. if (HandleInformation != &HandleInfoBuffer) {
  620. OhZoneFree (HandleInformation);
  621. }
  622. OhError (__FILE__, __LINE__,
  623. "query (SystemExtendedHandleInformation) failed with status %08X \n",
  624. Status);
  625. return FALSE;
  626. }
  627. else {
  628. break;
  629. }
  630. }
  631. TypeInfo = ObjectInformation;
  632. while (TRUE) {
  633. ObjectInfo = (PSYSTEM_OBJECT_INFORMATION)
  634. ((PCHAR)TypeInfo->TypeName.Buffer + TypeInfo->TypeName.MaximumLength);
  635. while (TRUE) {
  636. if (ObjectInfo->HandleCount != 0) {
  637. PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntry;
  638. ULONG HandleNumber;
  639. HandleEntry = &HandleInformation->Handles[ 0 ];
  640. HandleNumber = 0;
  641. while (HandleNumber++ < HandleInformation->NumberOfHandles) {
  642. if (!(HandleEntry->HandleAttributes & 0x80) &&
  643. HandleEntry->Object == ObjectInfo->Object
  644. ) {
  645. HandleEntry->Object = ObjectInfo;
  646. HandleEntry->HandleAttributes |= 0x80;
  647. }
  648. HandleEntry++;
  649. }
  650. }
  651. if (ObjectInfo->NextEntryOffset == 0) {
  652. break;
  653. }
  654. ObjectInfo = (PSYSTEM_OBJECT_INFORMATION)
  655. ((PCHAR)ObjectInformation + ObjectInfo->NextEntryOffset);
  656. }
  657. if (TypeInfo->NextEntryOffset == 0) {
  658. break;
  659. }
  660. TypeInfo = (PSYSTEM_OBJECTTYPE_INFORMATION)
  661. ((PCHAR)ObjectInformation + TypeInfo->NextEntryOffset);
  662. }
  663. return TRUE;
  664. }
  665. BOOLEAN
  666. OhLoadSystemProcesses(
  667. PRTL_DEBUG_INFORMATION Buffer
  668. )
  669. /*++
  670. Routine description:
  671. Parameters:
  672. Return value:
  673. --*/
  674. {
  675. NTSTATUS Status;
  676. SIZE_T RequiredLength;
  677. ULONG i, TotalOffset;
  678. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  679. PSYSTEM_THREAD_INFORMATION ThreadInfo;
  680. PPROCESS_INFO ProcessEntry;
  681. UCHAR NameBuffer[ MAX_PATH ];
  682. ANSI_STRING AnsiString;
  683. const SIZE_T SIZE_64_KB = 0x10000;
  684. //
  685. // Always initialize the list head, so that a failed
  686. // NtQuerySystemInformation call won't cause an AV later on.
  687. //
  688. InitializeListHead (&ProcessListHead);
  689. RequiredLength = SIZE_64_KB;
  690. ProcessInformation = (PSYSTEM_PROCESS_INFORMATION)OhZoneAllocEx (RequiredLength);
  691. while (TRUE) {
  692. Status = NtQuerySystemInformation (SystemProcessInformation,
  693. ProcessInformation,
  694. (ULONG)RequiredLength,
  695. NULL);
  696. if (Status == STATUS_INFO_LENGTH_MISMATCH) {
  697. OhZoneFree (ProcessInformation);
  698. //
  699. // Check for number overflow.
  700. //
  701. if (RequiredLength * 2 < RequiredLength) {
  702. return FALSE;
  703. }
  704. RequiredLength *= 2;
  705. ProcessInformation = (PSYSTEM_PROCESS_INFORMATION)OhZoneAllocEx (RequiredLength);
  706. }
  707. else if (! NT_SUCCESS(Status)) {
  708. OhError (__FILE__, __LINE__,
  709. "query (SystemProcessInformation) failed with status %08X \n",
  710. Status);
  711. }
  712. else {
  713. //
  714. // We managed to read the process information.
  715. //
  716. break;
  717. }
  718. }
  719. ProcessInfo = ProcessInformation;
  720. TotalOffset = 0;
  721. while (TRUE) {
  722. SIZE_T ProcessEntrySize;
  723. if (ProcessInfo->ImageName.Buffer == NULL) {
  724. sprintf ((PCHAR)NameBuffer,
  725. "System Process (%p)",
  726. ProcessInfo->UniqueProcessId );
  727. }
  728. else {
  729. sprintf ((PCHAR)NameBuffer,
  730. "%wZ",
  731. &ProcessInfo->ImageName );
  732. }
  733. RtlInitAnsiString( &AnsiString, (PCSZ)NameBuffer );
  734. RtlAnsiStringToUnicodeString( &ProcessInfo->ImageName, &AnsiString, TRUE );
  735. ProcessEntrySize = sizeof (*ProcessEntry) + sizeof (ThreadInfo) * ProcessInfo->NumberOfThreads;
  736. ProcessEntry = (PPROCESS_INFO)OhAlloc (ProcessEntrySize);
  737. InitializeListHead( &ProcessEntry->Entry );
  738. ProcessEntry->ProcessInfo = ProcessInfo;
  739. ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
  740. for (i = 0; i < ProcessInfo->NumberOfThreads; i += 1) {
  741. ProcessEntry->ThreadInfo[i] = ThreadInfo;
  742. ThreadInfo += 1;
  743. }
  744. InsertTailList( &ProcessListHead, &ProcessEntry->Entry );
  745. if (ProcessInfo->NextEntryOffset == 0) {
  746. break;
  747. }
  748. TotalOffset += ProcessInfo->NextEntryOffset;
  749. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) ((PCHAR)ProcessInformation + TotalOffset);
  750. }
  751. return TRUE;
  752. }
  753. PSYSTEM_PROCESS_INFORMATION
  754. OhFindProcessInfoForCid(
  755. IN PID UniqueProcessId
  756. )
  757. /*++
  758. Routine description:
  759. Parameters:
  760. Return value:
  761. --*/
  762. {
  763. PLIST_ENTRY Next, Head;
  764. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  765. PPROCESS_INFO ProcessEntry;
  766. UCHAR NameBuffer [64];
  767. ANSI_STRING AnsiString;
  768. Head = &ProcessListHead;
  769. Next = Head->Flink;
  770. while (Next != Head) {
  771. ProcessEntry = CONTAINING_RECORD (Next,
  772. PROCESS_INFO,
  773. Entry);
  774. ProcessInfo = ProcessEntry->ProcessInfo;
  775. if( ProcessInfo->UniqueProcessId == UlongToHandle(UniqueProcessId) ) {
  776. return ProcessInfo;
  777. }
  778. Next = Next->Flink;
  779. }
  780. ProcessEntry = (PPROCESS_INFO)RtlAllocateHeap (RtlProcessHeap(),
  781. HEAP_ZERO_MEMORY,
  782. sizeof( *ProcessEntry ) + sizeof( *ProcessInfo ));
  783. if (ProcessEntry == NULL) {
  784. printf ("Failed to allocate memory for process\n");
  785. ExitProcess (0);
  786. }
  787. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)(ProcessEntry+1);
  788. ProcessEntry->ProcessInfo = ProcessInfo;
  789. sprintf ((PCHAR)NameBuffer, "Unknown Process" );
  790. RtlInitAnsiString( &AnsiString, (PCSZ)NameBuffer );
  791. RtlAnsiStringToUnicodeString( (PUNICODE_STRING)&ProcessInfo->ImageName, &AnsiString, TRUE );
  792. ProcessInfo->UniqueProcessId = UlongToHandle(UniqueProcessId);
  793. InitializeListHead( &ProcessEntry->Entry );
  794. InsertTailList( &ProcessListHead, &ProcessEntry->Entry );
  795. return ProcessInfo;
  796. }
  797. //
  798. // comparison routine used by qsort and bsearch routines
  799. // key: (Pid, HandleValue)
  800. //
  801. int _cdecl AuxInfoCompare( const void* Arg1, const void* Arg2 )
  802. {
  803. HANDLE_AUX_INFO* Ele1= (HANDLE_AUX_INFO*) Arg1;
  804. HANDLE_AUX_INFO* Ele2= (HANDLE_AUX_INFO*) Arg2;
  805. if( Ele1->Pid < Ele2->Pid ) {
  806. return -1;
  807. }
  808. if( Ele1->Pid > Ele2->Pid ) {
  809. return 1;
  810. }
  811. if( Ele1->HandleValue < Ele2->HandleValue ) {
  812. return -1;
  813. }
  814. if( Ele1->HandleValue > Ele2->HandleValue ) {
  815. return 1;
  816. }
  817. return 0;
  818. }
  819. // OhGatherData
  820. //
  821. // Search through a region of process space for anything that looks
  822. // like it could the value of a handle that is opened by that process.
  823. // If we find a reference, increment the AuxInfo.Hitcount field for that handle.
  824. //
  825. // returns: FALSE if it couldn't scan the region
  826. BOOL
  827. OhGatherData(
  828. IN HANDLE ProcHan,
  829. IN PID PidToExamine,
  830. IN PVOID VAddr,
  831. IN SIZE_T RegionSize
  832. )
  833. {
  834. PDWORD Buf;
  835. SIZE_T BytesRead;
  836. BOOL bStatus;
  837. SIZE_T i;
  838. HANDLE_AUX_INFO AuxToCompare;
  839. HANDLE_AUX_INFO* AuxInfo;
  840. Buf= (PDWORD)LocalAlloc( LPTR, RegionSize );
  841. if( Buf == NULL ) {
  842. OhComment("Failed to alloc mem size= %d\n",RegionSize);
  843. SetLastError( ERROR_NOT_ENOUGH_MEMORY);
  844. return FALSE;
  845. }
  846. bStatus= ReadProcessMemory( ProcHan, VAddr, Buf, RegionSize, &BytesRead );
  847. if( !bStatus ) {
  848. OhComment ( "Failed to ReadProcessMemory\n");
  849. if( Buf ) {
  850. LocalFree( Buf );
  851. }
  852. return FALSE;
  853. }
  854. // feature: this only looks for aligned dword refs. may want unaligned too.
  855. for( i=0; i < BytesRead/sizeof(DWORD); i++ ) {
  856. AuxToCompare.HandleValue= Buf[ i ] & (~3); // kernel ignores 2 lsb
  857. AuxToCompare.Pid= PidToExamine;
  858. AuxInfo= (HANDLE_AUX_INFO*) bsearch( &AuxToCompare,
  859. Globals.AuxInfo,
  860. Globals.AuxSize,
  861. sizeof( HANDLE_AUX_INFO ),
  862. &AuxInfoCompare );
  863. if( AuxInfo ) {
  864. AuxInfo->HitCount++;
  865. }
  866. }
  867. LocalFree( Buf );
  868. return TRUE;
  869. }
  870. VOID OhSearchForReferencedHandles( PID PidToExamine )
  871. {
  872. HANDLE_AUX_INFO AuxToCompare;
  873. DWORD HandleNumber;
  874. HANDLE ProcHan= NULL; // process to examine
  875. PVOID VAddr; // pointer into process virtual memory
  876. MEMORY_BASIC_INFORMATION MemoryInfo;
  877. DWORD CurrentProtect;
  878. SIZE_T Size;
  879. // ignore process IDs= 0 or 4 because they are the system process
  880. if( ( PidToExamine == (PID)0 ) || ( PidToExamine == (PID)4 ) ) {
  881. return;
  882. }
  883. AuxToCompare.Pid= PidToExamine;
  884. ProcHan= OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
  885. FALSE,
  886. (PID) PidToExamine );
  887. if( NULL == ProcHan ) {
  888. OhComment ( "Could not open process %d\n",PidToExamine );
  889. goto errorexit;
  890. }
  891. // zero the hit counts for this process number
  892. // if we can't read the memory, we will re-fill the HitCount fields with non-zero
  893. for( HandleNumber=0; HandleNumber < Globals.AuxSize; HandleNumber++ ) {
  894. if( Globals.AuxInfo[ HandleNumber ].Pid == PidToExamine ) {
  895. Globals.AuxInfo[ HandleNumber ].HitCount= 0;
  896. }
  897. }
  898. // read thru all interesting process memory
  899. // if we get a value that the process could have written, then see if
  900. // it matches one of our handle values. Keep track of the number of matches.
  901. // if any HitCount field is zero when we are done, there is no way to reference it.
  902. // modulo (encrypted it in memory (xor), truncated into a short, or hidden it
  903. // in a file, memory section not mapped, registry.)
  904. for( VAddr= 0;
  905. VAddr < (PVOID) (0x80000000-0x10000);
  906. VAddr= (PVOID) ((PCHAR) VAddr+ MemoryInfo.RegionSize) )
  907. {
  908. MemoryInfo.RegionSize=0x1000;
  909. Size= VirtualQueryEx( ProcHan,
  910. VAddr,
  911. &MemoryInfo,
  912. sizeof( MemoryInfo ) );
  913. if( Size != sizeof(MemoryInfo) ) {
  914. fprintf(stderr,"VirtualQueryEx failed at %p LastError %d\n",VAddr,GetLastError() );
  915. }
  916. else {
  917. CurrentProtect= MemoryInfo.Protect;
  918. if( MemoryInfo.State == MEM_COMMIT ) {
  919. if( (CurrentProtect & (PAGE_READWRITE|PAGE_READWRITE) ) &&
  920. ( (CurrentProtect&PAGE_GUARD)==0 )
  921. ) {
  922. BOOL bSta;
  923. bSta= OhGatherData( ProcHan, PidToExamine, VAddr, MemoryInfo.RegionSize );
  924. if( !bSta ) {
  925. goto errorexit;
  926. }
  927. }
  928. }
  929. }
  930. }
  931. CloseHandle( ProcHan ); ProcHan= NULL;
  932. return;
  933. // If we have an error, just mark all the HitCount values so it looks like they
  934. // have been referenced.
  935. errorexit:
  936. for( HandleNumber=0; HandleNumber < Globals.AuxSize; HandleNumber++ ) {
  937. if( Globals.AuxInfo[ HandleNumber ].Pid == PidToExamine ) {
  938. Globals.AuxInfo[ HandleNumber].HitCount= LARGE_HITCOUNT;
  939. }
  940. }
  941. CloseHandle( ProcHan ); ProcHan= NULL;
  942. return;
  943. }
  944. VOID
  945. OhBuildAuxTables( PID PidToExamine )
  946. /*++
  947. Routine description:
  948. Creates auxillary table keyed with (HandleValue,Pid) containing HitCount
  949. Parameters:
  950. The Process ID to examine
  951. Return value:
  952. --*/
  953. {
  954. PID PreviousUniqueProcessId;
  955. PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntry;
  956. ULONG HandleNumber;
  957. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  958. DWORD TotalHandles;
  959. PID LastPid;
  960. HANDLE_AUX_INFO* AuxInfo;
  961. Globals.AuxInfo= NULL;
  962. Globals.AuxSize= 0;
  963. TotalHandles=(DWORD) HandleInformation->NumberOfHandles;
  964. // Allocate AuxInfo table
  965. AuxInfo= (HANDLE_AUX_INFO*) LocalAlloc( LPTR, TotalHandles*sizeof(HANDLE_AUX_INFO) );
  966. if( NULL == AuxInfo ) {
  967. return;
  968. }
  969. Globals.AuxInfo= AuxInfo;
  970. Globals.AuxSize= TotalHandles;
  971. // populate the table with key information
  972. HandleEntry = &HandleInformation->Handles[ 0 ];
  973. PreviousUniqueProcessId = (PID) -1;
  974. for (HandleNumber = 0; HandleNumber < TotalHandles; HandleNumber++ ) {
  975. if (PreviousUniqueProcessId != (PID)HandleEntry->UniqueProcessId) {
  976. PreviousUniqueProcessId = (PID)HandleEntry->UniqueProcessId;
  977. ProcessInfo= OhFindProcessInfoForCid( PreviousUniqueProcessId );
  978. }
  979. AuxInfo[ HandleNumber ].HandleValue= HandleEntry->HandleValue;
  980. AuxInfo[ HandleNumber ].Pid= HandleToUlong(ProcessInfo->UniqueProcessId);
  981. AuxInfo[ HandleNumber ].HitCount= LARGE_HITCOUNT;
  982. AuxInfo[ HandleNumber ].HandleEntry= HandleEntry;
  983. HandleEntry++;
  984. }
  985. // Sort the table so bsearch works later
  986. qsort( AuxInfo,
  987. TotalHandles,
  988. sizeof( HANDLE_AUX_INFO ),
  989. AuxInfoCompare );
  990. //
  991. // Search the process or processes for references and keep count
  992. //
  993. if( PidToExamine ) {
  994. OhSearchForReferencedHandles( PidToExamine );
  995. return;
  996. }
  997. //
  998. // No Pid then do all the Pids on the system
  999. // (actually only search Pids that have kernel handles)
  1000. //
  1001. LastPid= (PID) -1;
  1002. for( HandleNumber=0; HandleNumber < TotalHandles; HandleNumber++ ) {
  1003. PID ThisPid= Globals.AuxInfo[ HandleNumber ].Pid;
  1004. if( LastPid != ThisPid ) {
  1005. OhSearchForReferencedHandles( ThisPid );
  1006. LastPid= ThisPid;
  1007. }
  1008. }
  1009. }
  1010. VOID
  1011. OhDumpHandles (
  1012. BOOLEAN DumpAnonymousHandles
  1013. )
  1014. /*++
  1015. Routine description:
  1016. Parameters:
  1017. Return value:
  1018. --*/
  1019. {
  1020. PID PreviousUniqueProcessId;
  1021. PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleEntry;
  1022. ULONG HandleNumber;
  1023. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  1024. PSYSTEM_OBJECT_INFORMATION ObjectInfo;
  1025. PUNICODE_STRING ObjectTypeName;
  1026. WCHAR ObjectName[ 1024 ];
  1027. PVOID Object;
  1028. CHAR OutputLine[ 512 ];
  1029. PWSTR s;
  1030. ULONG n;
  1031. DWORD d = 0;
  1032. BOOL AnyRefs;
  1033. OhBuildAuxTables( ProcessId );
  1034. HandleEntry = &HandleInformation->Handles[ 0 ];
  1035. HandleNumber = 0;
  1036. PreviousUniqueProcessId = (PID) -1;
  1037. for (HandleNumber = 0;
  1038. HandleNumber < HandleInformation->NumberOfHandles;
  1039. HandleNumber++, HandleEntry++
  1040. )
  1041. {
  1042. if (PreviousUniqueProcessId != (PID)HandleEntry->UniqueProcessId) {
  1043. PreviousUniqueProcessId = (PID)HandleEntry->UniqueProcessId;
  1044. ProcessInfo = OhFindProcessInfoForCid( PreviousUniqueProcessId );
  1045. }
  1046. ObjectName[ 0 ] = UNICODE_NULL;
  1047. if (HandleEntry->HandleAttributes & 0x80) {
  1048. ObjectInfo = (PSYSTEM_OBJECT_INFORMATION)(HandleEntry->Object);
  1049. Object = ObjectInfo->Object;
  1050. _try {
  1051. if (ObjectInfo->NameInfo.Name.Length != 0 &&
  1052. *(ObjectInfo->NameInfo.Name.Buffer) == UNICODE_NULL
  1053. ) {
  1054. ObjectInfo->NameInfo.Name.Length = 0;
  1055. }
  1056. n = ObjectInfo->NameInfo.Name.Length / sizeof( WCHAR );
  1057. wcsncpy( ObjectName,
  1058. ObjectInfo->NameInfo.Name.Buffer,
  1059. n
  1060. );
  1061. ObjectName[ n ] = UNICODE_NULL;
  1062. }
  1063. _except( EXCEPTION_EXECUTE_HANDLER ) {
  1064. _snwprintf( ObjectName, 1024, L"[%04x, %04x, %08x]",
  1065. ObjectInfo->NameInfo.Name.MaximumLength,
  1066. ObjectInfo->NameInfo.Name.Length,
  1067. ObjectInfo->NameInfo.Name.Buffer
  1068. );
  1069. }
  1070. }
  1071. else {
  1072. ObjectInfo = NULL;
  1073. Object = HandleEntry->Object;
  1074. }
  1075. if( ProcessId != 0 && ProcessInfo->UniqueProcessId != UlongToHandle(ProcessId) ) {
  1076. continue;
  1077. }
  1078. ObjectTypeName = &TypeCounts[ HandleEntry->ObjectTypeIndex < MAX_TYPE_NAMES ?
  1079. HandleEntry->ObjectTypeIndex : MAX_TYPE_NAMES ].TypeName ;
  1080. TypeCounts[ HandleEntry->ObjectTypeIndex < MAX_TYPE_NAMES ?
  1081. HandleEntry->ObjectTypeIndex : MAX_TYPE_NAMES ].HandleCount++ ;
  1082. if (TypeName[0]) {
  1083. if (_wcsicmp( TypeName, ObjectTypeName->Buffer )) {
  1084. continue;
  1085. }
  1086. }
  1087. if (!*ObjectName) {
  1088. if (! DumpAnonymousHandles) {
  1089. continue;
  1090. }
  1091. }
  1092. else
  1093. if (SearchName[0]) {
  1094. if (!wcsstr( ObjectName, SearchName )) {
  1095. s = ObjectName;
  1096. n = wcslen( SearchName );
  1097. while (*s) {
  1098. if (!_wcsnicmp( s, SearchName, n )) {
  1099. break;
  1100. }
  1101. s += 1;
  1102. }
  1103. if (!*s) {
  1104. continue;
  1105. }
  1106. }
  1107. }
  1108. // See if there were any references to this handle in the process memory space
  1109. AnyRefs= TRUE;
  1110. {
  1111. HANDLE_AUX_INFO* AuxInfo;
  1112. HANDLE_AUX_INFO AuxToCompare;
  1113. AuxToCompare.Pid= HandleToUlong( ProcessInfo->UniqueProcessId );
  1114. AuxToCompare.HandleValue= HandleEntry->HandleValue;
  1115. AuxInfo= (HANDLE_AUX_INFO*) bsearch( &AuxToCompare,
  1116. Globals.AuxInfo,
  1117. Globals.AuxSize,
  1118. sizeof( HANDLE_AUX_INFO ),
  1119. &AuxInfoCompare );
  1120. if( AuxInfo ) {
  1121. if( AuxInfo->HitCount == 0 ) {
  1122. AnyRefs=FALSE;
  1123. }
  1124. }
  1125. }
  1126. if( (!Globals.fOnlyShowNoRefs) || (Globals.fOnlyShowNoRefs && (AnyRefs==FALSE) ) ) {
  1127. if( Globals.fOnlyShowNoRefs) {
  1128. Info ( "noref_" );
  1129. }
  1130. if (Globals.DumpTraces) {
  1131. Info ("%p %-14wZ %-14wZ %04x (%04x) %ws\n",
  1132. ProcessInfo->UniqueProcessId,
  1133. &ProcessInfo->ImageName,
  1134. ObjectTypeName,
  1135. HandleEntry->HandleValue,
  1136. HandleEntry->CreatorBackTraceIndex,
  1137. *ObjectName ? ObjectName : L"");
  1138. }
  1139. else {
  1140. Info ("%p %-14wZ %-14wZ %04x %ws\n",
  1141. ProcessInfo->UniqueProcessId,
  1142. &ProcessInfo->ImageName,
  1143. ObjectTypeName,
  1144. HandleEntry->HandleValue,
  1145. *ObjectName ? ObjectName : L"");
  1146. }
  1147. if (HandleEntry->CreatorBackTraceIndex && Globals.TraceDatabase) {
  1148. OhDumpStackTrace (Globals.TraceDatabase,
  1149. HandleEntry->CreatorBackTraceIndex);
  1150. }
  1151. }
  1152. }
  1153. return;
  1154. }
  1155. VOID
  1156. DoSummary( VOID )
  1157. /*++
  1158. Routine description:
  1159. Parameters:
  1160. Return value:
  1161. --*/
  1162. {
  1163. ULONG i ;
  1164. ULONG ignored ;
  1165. Info ("Summary: \n");
  1166. for ( i = 0 ; i < MAX_TYPE_NAMES ; i++ )
  1167. {
  1168. if ( TypeCounts[ i ].HandleCount )
  1169. {
  1170. Info (" %-20ws\t%d\n",
  1171. TypeCounts[ i ].TypeName.Buffer,
  1172. TypeCounts[ i ].HandleCount );
  1173. }
  1174. }
  1175. }
  1176. /////////////////////////////////////////////////////////////////////
  1177. ///////////////////////////////////////////////// Handle stack traces
  1178. /////////////////////////////////////////////////////////////////////
  1179. VOID
  1180. OhError (
  1181. PCHAR File,
  1182. ULONG Line,
  1183. PCHAR Format,
  1184. ...
  1185. );
  1186. PCHAR
  1187. OhNameForAddress(
  1188. IN HANDLE UniqueProcess,
  1189. IN PVOID Address
  1190. );
  1191. PRTL_PROCESS_BACKTRACES
  1192. OhLoadSystemTraceDatabase (
  1193. )
  1194. {
  1195. const SIZE_T OneMb = 0x100000;
  1196. NTSTATUS Status;
  1197. PRTL_PROCESS_BACKTRACES TraceDatabase;
  1198. SIZE_T RequiredLength;
  1199. SIZE_T CurrentLength;
  1200. CurrentLength = OneMb;
  1201. RequiredLength = 0;
  1202. TraceDatabase = (PRTL_PROCESS_BACKTRACES)VirtualAlloc (NULL,
  1203. CurrentLength,
  1204. MEM_COMMIT,
  1205. PAGE_READWRITE);
  1206. if (TraceDatabase == NULL) {
  1207. OhError (__FILE__, __LINE__,
  1208. "failed to allocate %p bytes", CurrentLength);
  1209. }
  1210. while (TRUE) {
  1211. Status = NtQuerySystemInformation (SystemStackTraceInformation,
  1212. TraceDatabase,
  1213. (ULONG)CurrentLength,
  1214. (ULONG *)&RequiredLength);
  1215. if (Status == STATUS_INFO_LENGTH_MISMATCH) {
  1216. CurrentLength = RequiredLength + OneMb;
  1217. VirtualFree (TraceDatabase,
  1218. 0,
  1219. MEM_RELEASE);
  1220. TraceDatabase = (PRTL_PROCESS_BACKTRACES)VirtualAlloc (NULL,
  1221. CurrentLength,
  1222. MEM_COMMIT,
  1223. PAGE_READWRITE);
  1224. if (TraceDatabase == NULL) {
  1225. OhError (__FILE__, __LINE__,
  1226. "failed to allocate %p bytes", CurrentLength);
  1227. }
  1228. }
  1229. else if (! NT_SUCCESS(Status)) {
  1230. OhError (__FILE__, __LINE__,
  1231. "QuerySystemInformation failed with status %08x",Status);
  1232. }
  1233. else {
  1234. //
  1235. // We managed to read the stack trace database.
  1236. //
  1237. break;
  1238. }
  1239. }
  1240. return TraceDatabase;
  1241. }
  1242. VOID
  1243. OhDumpStackTrace (
  1244. PRTL_PROCESS_BACKTRACES TraceDatabase,
  1245. USHORT Index
  1246. )
  1247. {
  1248. PRTL_PROCESS_BACKTRACE_INFORMATION Trace;
  1249. USHORT I;
  1250. PCHAR Name;
  1251. if (Index >= TraceDatabase->NumberOfBackTraces) {
  1252. return;
  1253. }
  1254. Trace = &(TraceDatabase->BackTraces[Index - 1]);
  1255. if (Trace->Depth > 0) {
  1256. Info ("\n");
  1257. }
  1258. for (I = 0; I < Trace->Depth; I += 1) {
  1259. if ((ULONG_PTR)(Trace->BackTrace[I]) < 0x80000000) {
  1260. if (Trace->BackTrace[I] == NULL) {
  1261. break;
  1262. }
  1263. Name = OhNameForAddress (Globals.TargetProcess,
  1264. Trace->BackTrace[I]);
  1265. Info ("\t%p %s\n",
  1266. Trace->BackTrace[I],
  1267. (Name ? Name : "<unknown>"));
  1268. }
  1269. else {
  1270. Info ("\t%p <kernel address>\n",
  1271. Trace->BackTrace[I]);
  1272. }
  1273. }
  1274. Info ("\n");
  1275. }
  1276. BOOL
  1277. OhEnumerateModules(
  1278. IN LPSTR ModuleName,
  1279. IN ULONG_PTR BaseOfDll,
  1280. IN PVOID UserContext
  1281. )
  1282. /*++
  1283. UmdhEnumerateModules
  1284. Module enumeration 'proc' for imagehlp. Call SymLoadModule on the
  1285. specified module and if that succeeds cache the module name.
  1286. ModuleName is an LPSTR indicating the name of the module imagehlp is
  1287. enumerating for us;
  1288. BaseOfDll is the load address of the DLL, which we don't care about, but
  1289. SymLoadModule does;
  1290. UserContext is a pointer to the relevant SYMINFO, which identifies
  1291. our connection.
  1292. --*/
  1293. {
  1294. DWORD64 Result;
  1295. Result = SymLoadModule(Globals.TargetProcess,
  1296. NULL, // hFile not used
  1297. NULL, // use symbol search path
  1298. ModuleName, // ModuleName from Enum
  1299. BaseOfDll, // LoadAddress from Enum
  1300. 0); // Let ImageHlp figure out DLL size
  1301. // SilviuC: need to understand exactly what does this function return
  1302. if (Result) {
  1303. Warning (NULL, 0,
  1304. "SymLoadModule (%s, %p) failed with error %X (%u)",
  1305. ModuleName, BaseOfDll,
  1306. GetLastError(), GetLastError());
  1307. return FALSE;
  1308. }
  1309. OhComment (" %s (%p) ...", ModuleName, BaseOfDll);
  1310. return TRUE;
  1311. }
  1312. VOID
  1313. Info (
  1314. PCHAR Format,
  1315. ...
  1316. )
  1317. {
  1318. va_list Params;
  1319. va_start (Params, Format);
  1320. vfprintf (Globals.Output, Format, Params);
  1321. }
  1322. VOID
  1323. OhComment (
  1324. PCHAR Format,
  1325. ...
  1326. )
  1327. {
  1328. va_list Params;
  1329. va_start (Params, Format);
  1330. fprintf (Globals.Output, "// ");
  1331. vfprintf (Globals.Output, Format, Params);
  1332. fprintf (Globals.Output, "\n");
  1333. }
  1334. VOID
  1335. Warning (
  1336. PCHAR File,
  1337. ULONG Line,
  1338. PCHAR Format,
  1339. ...
  1340. )
  1341. {
  1342. va_list Params;
  1343. va_start (Params, Format);
  1344. if (File) {
  1345. fprintf (stderr, "Warning: %s: %u: ", File, Line);
  1346. }
  1347. else {
  1348. fprintf (stderr, "Warning: ");
  1349. }
  1350. vfprintf (stderr, Format, Params);
  1351. fprintf (stderr, "\n");
  1352. }
  1353. VOID
  1354. OhError (
  1355. PCHAR File,
  1356. ULONG Line,
  1357. PCHAR Format,
  1358. ...
  1359. )
  1360. {
  1361. va_list Params;
  1362. va_start (Params, Format);
  1363. if (File) {
  1364. fprintf (stderr, "Error: %s: %u: ", File, Line);
  1365. }
  1366. else {
  1367. fprintf (stderr, "Error: ");
  1368. }
  1369. vfprintf (stderr, Format, Params);
  1370. fprintf (stderr, "\n");
  1371. exit (1);
  1372. }
  1373. VOID
  1374. OhStampLog (
  1375. VOID
  1376. )
  1377. /*++
  1378. Routine description:
  1379. This routines writes an initial stamp in the log.
  1380. Parameters:
  1381. None.
  1382. Return value:
  1383. None.
  1384. --*/
  1385. {
  1386. CHAR CompName[MAX_COMPUTERNAME_LENGTH + 1];
  1387. DWORD CompNameLength = MAX_COMPUTERNAME_LENGTH + 1;
  1388. SYSTEMTIME st;
  1389. OSVERSIONINFOEX OsInfo;
  1390. //
  1391. // Stamp the log
  1392. //
  1393. ZeroMemory (&OsInfo, sizeof OsInfo);
  1394. OsInfo.dwOSVersionInfoSize = sizeof OsInfo;
  1395. GetVersionEx ((POSVERSIONINFO)(&OsInfo));
  1396. GetLocalTime(&st);
  1397. GetComputerName(CompName, &CompNameLength);
  1398. OhComment ("");
  1399. OhComment ("TIME: %4u-%02u-%02u %02u:%02u", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute);
  1400. OhComment ("MACHINE: %s", CompName);
  1401. OhComment ("BUILD: %u", OsInfo.dwBuildNumber);
  1402. OhComment ("OH version: %s", BUILD_MACHINE_TAG);
  1403. OhComment ("");
  1404. OhComment ("");
  1405. }
  1406. VOID
  1407. OhInitializeHandleStackTraces (
  1408. PID Pid
  1409. )
  1410. /*++
  1411. Routine description:
  1412. This routine initializes all interal structures required to
  1413. read handle stack traces. It will adjust priviles (in order
  1414. for this to work on lsass, winlogon, etc.), open the process,
  1415. read from kernel the trace database.
  1416. Parameters:
  1417. Pid - process ID for the process for which we will get traces.
  1418. Return value:
  1419. None.
  1420. --*/
  1421. {
  1422. BOOL Result;
  1423. NTSTATUS Status;
  1424. //
  1425. // Check if we have a symbols path defined and define a default one
  1426. // if not.
  1427. //
  1428. OhSetSymbolsPath ();
  1429. //
  1430. // Imagehlp library needs the query privilege for the process
  1431. // handle and of course we need also read privilege because
  1432. // we will read all sorts of things from the process.
  1433. //
  1434. Globals.TargetProcess = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
  1435. FALSE,
  1436. Pid);
  1437. if (Globals.TargetProcess == NULL) {
  1438. OhError (__FILE__, __LINE__,
  1439. "OpenProcess(%u) failed with error %u", Pid, GetLastError());
  1440. }
  1441. OhComment ("Process %u opened.", Pid);
  1442. //
  1443. // Attach ImageHlp and enumerate the modules.
  1444. //
  1445. Result = SymInitialize(Globals.TargetProcess, // target process
  1446. NULL, // standard symbols search path
  1447. TRUE); // invade process space with symbols
  1448. if (Result == FALSE) {
  1449. ULONG ErrorCode = GetLastError();
  1450. if (ErrorCode >= 0x80000000) {
  1451. OhError (__FILE__, __LINE__,
  1452. "imagehlp.SymInitialize() failed with error %X", ErrorCode);
  1453. }
  1454. else {
  1455. OhError (__FILE__, __LINE__,
  1456. "imagehlp.SymInitialize() failed with error %u", ErrorCode);
  1457. }
  1458. }
  1459. OhComment ("Dbghelp initialized.");
  1460. SymSetOptions(SYMOPT_CASE_INSENSITIVE |
  1461. SYMOPT_DEFERRED_LOADS |
  1462. SYMOPT_LOAD_LINES |
  1463. SYMOPT_UNDNAME);
  1464. OhComment ("Enumerating modules ...");
  1465. OhComment ("");
  1466. Result = SymEnumerateModules (Globals.TargetProcess,
  1467. OhEnumerateModules,
  1468. Globals.TargetProcess);
  1469. if (Result == FALSE) {
  1470. OhError (__FILE__, __LINE__,
  1471. "SymEnumerateModules() failed with error %u", GetLastError());
  1472. }
  1473. OhComment ("");
  1474. OhComment ("Finished module enumeration.");
  1475. //
  1476. // Initialize local trace database. Note that order is important.
  1477. // Initialize() assumes the process handle to the target process
  1478. // already exists and the symbol management package was initialized.
  1479. //
  1480. OhComment ("Loading stack trace database ...");
  1481. Globals.TraceDatabase = OhLoadSystemTraceDatabase ();
  1482. OhComment ("Initialization finished.");
  1483. OhComment ("");
  1484. OhComment ("\n");
  1485. }
  1486. /////////////////////////////////////////////////////////////////////
  1487. /////////////////////////////////////////////////////// Symbol lookup
  1488. /////////////////////////////////////////////////////////////////////
  1489. #define HNDL_SYMBOL_MAP_BUCKETS 4096
  1490. typedef struct _HNDL_SYMBOL_ENTRY
  1491. {
  1492. PVOID Address;
  1493. PCHAR Symbol;
  1494. struct _HNDL_SYMBOL_ENTRY * Next;
  1495. } HNDL_SYMBOL_ENTRY, * PHNDL_SYMBOL_ENTRY;
  1496. PHNDL_SYMBOL_ENTRY OhSymbolsMap [HNDL_SYMBOL_MAP_BUCKETS];
  1497. PCHAR
  1498. OhFindSymbol (
  1499. PVOID Address
  1500. )
  1501. {
  1502. ULONG_PTR Bucket = ((ULONG_PTR)Address >> 2) % HNDL_SYMBOL_MAP_BUCKETS;
  1503. PHNDL_SYMBOL_ENTRY Node = OhSymbolsMap[Bucket];
  1504. while (Node != NULL ) {
  1505. if (Node->Address == Address) {
  1506. return Node->Symbol;
  1507. }
  1508. Node = Node->Next;
  1509. }
  1510. return NULL;
  1511. }
  1512. VOID
  1513. OhInsertSymbol (
  1514. PCHAR Symbol,
  1515. PVOID Address
  1516. )
  1517. {
  1518. ULONG_PTR Bucket = ((ULONG_PTR)Address >> 2) % HNDL_SYMBOL_MAP_BUCKETS;
  1519. PHNDL_SYMBOL_ENTRY New;
  1520. New = (PHNDL_SYMBOL_ENTRY) OhAlloc (sizeof (HNDL_SYMBOL_ENTRY));
  1521. New->Symbol = Symbol;
  1522. New->Address = Address;
  1523. New->Next = OhSymbolsMap[Bucket];
  1524. OhSymbolsMap[Bucket] = New;
  1525. }
  1526. PCHAR
  1527. OhNameForAddress(
  1528. IN HANDLE UniqueProcess,
  1529. IN PVOID Address
  1530. )
  1531. {
  1532. IMAGEHLP_MODULE ModuleInfo;
  1533. CHAR SymbolBuffer[512];
  1534. PIMAGEHLP_SYMBOL Symbol;
  1535. ULONG_PTR Offset;
  1536. CHAR Name [512 + 100];
  1537. SIZE_T TotalSize;
  1538. BOOL Result;
  1539. PVOID Duplicate;
  1540. PCHAR SymbolName;
  1541. //
  1542. // Lookup in map first ..
  1543. //
  1544. SymbolName = OhFindSymbol (Address);
  1545. if (SymbolName != NULL) {
  1546. return SymbolName;
  1547. }
  1548. TotalSize = 0;
  1549. ModuleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
  1550. if (SymGetModuleInfo (UniqueProcess, (ULONG_PTR)Address, &ModuleInfo)) {
  1551. TotalSize += strlen( ModuleInfo.ModuleName );
  1552. }
  1553. else {
  1554. Warning (NULL, 0,
  1555. "Symbols: cannot identify module for address %p",
  1556. Address);
  1557. return NULL;
  1558. }
  1559. Symbol = (PIMAGEHLP_SYMBOL)SymbolBuffer;
  1560. Symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL) - 1;
  1561. if (SymGetSymFromAddr (UniqueProcess, (ULONG_PTR)Address, &Offset, Symbol)) {
  1562. TotalSize += strlen (Symbol->Name) + 16 + 3;
  1563. sprintf (Name, "%s!%s+%08X", ModuleInfo.ModuleName, Symbol->Name, Offset);
  1564. Duplicate = _strdup(Name);
  1565. OhInsertSymbol ((PCHAR)Duplicate, Address);
  1566. return (PCHAR)Duplicate;
  1567. }
  1568. else {
  1569. Warning (NULL, 0,
  1570. "Symbols: incorrect symbols for module %s (address %p)",
  1571. ModuleInfo.ModuleName,
  1572. Address);
  1573. TotalSize += strlen ("???") + 16 + 5;
  1574. sprintf (Name, "%s!%s @ %p", ModuleInfo.ModuleName, "???", Address);
  1575. Duplicate = _strdup(Name);
  1576. OhInsertSymbol ((PCHAR)Duplicate, Address);
  1577. return (PCHAR)Duplicate;
  1578. }
  1579. }
  1580. /////////////////////////////////////////////////////////////////////
  1581. /////////////////////////////////////////////////// Registry handling
  1582. /////////////////////////////////////////////////////////////////////
  1583. //
  1584. // Registry key name to read/write system global flags
  1585. //
  1586. #define KEYNAME_SESSION_MANAGER "SYSTEM\\CurrentControlSet\\Control\\Session Manager"
  1587. DWORD
  1588. OhGetCurrentGlobalFlags (
  1589. )
  1590. {
  1591. SYSTEM_FLAGS_INFORMATION Information;
  1592. NTSTATUS Status;
  1593. Status = NtQuerySystemInformation (SystemFlagsInformation,
  1594. &Information,
  1595. sizeof Information,
  1596. NULL);
  1597. if (! NT_SUCCESS(Status)) {
  1598. OhError (NULL, 0,
  1599. "cannot get current global flags settings (error %08X) \n",
  1600. Status);
  1601. }
  1602. return Information.Flags;
  1603. }
  1604. DWORD
  1605. OhGetSystemRegistryFlags (
  1606. )
  1607. {
  1608. DWORD cbKey;
  1609. DWORD GFlags;
  1610. DWORD type;
  1611. HKEY hKey;
  1612. LONG Result;
  1613. Result = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
  1614. KEYNAME_SESSION_MANAGER,
  1615. 0,
  1616. KEY_READ | KEY_WRITE,
  1617. &hKey);
  1618. if (Result != ERROR_SUCCESS) {
  1619. OhError (NULL, 0,
  1620. "cannot open registry key `%s' \n",
  1621. KEYNAME_SESSION_MANAGER);
  1622. }
  1623. cbKey = sizeof (GFlags);
  1624. Result = RegQueryValueEx (hKey,
  1625. "GlobalFlag",
  1626. 0,
  1627. &type,
  1628. (LPBYTE)&GFlags,
  1629. &cbKey);
  1630. if (Result != ERROR_SUCCESS || type != REG_DWORD) {
  1631. OhError (NULL, 0,
  1632. "cannot read registry value '%s'\n",
  1633. KEYNAME_SESSION_MANAGER "\\GlobalFlag");
  1634. }
  1635. RegCloseKey (hKey);
  1636. hKey = NULL;
  1637. return GFlags;
  1638. }
  1639. VOID
  1640. OhSetSystemRegistryFlags(
  1641. DWORD GFlags
  1642. )
  1643. {
  1644. HKEY hKey;
  1645. LONG Result;
  1646. Result = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
  1647. KEYNAME_SESSION_MANAGER,
  1648. 0,
  1649. KEY_READ | KEY_WRITE,
  1650. &hKey);
  1651. if (Result != ERROR_SUCCESS) {
  1652. OhError (NULL, 0,
  1653. "cannot open registry key '%s'\n",
  1654. KEYNAME_SESSION_MANAGER );
  1655. }
  1656. Result = RegSetValueEx (hKey,
  1657. "GlobalFlag",
  1658. 0,
  1659. REG_DWORD,
  1660. (LPBYTE)&GFlags,
  1661. sizeof( GFlags ));
  1662. if (Result != ERROR_SUCCESS) {
  1663. OhError (NULL, 0,
  1664. "cannot write registry value '%s'\n",
  1665. KEYNAME_SESSION_MANAGER "\\GlobalFlag" );
  1666. }
  1667. RegCloseKey (hKey);
  1668. hKey = NULL;
  1669. }
  1670. VOID
  1671. OhSetRequiredGlobalFlag (
  1672. DWORD Flags
  1673. )
  1674. {
  1675. DWORD RegistryFlags;
  1676. if ((Flags & FLG_KERNEL_STACK_TRACE_DB)) {
  1677. RegistryFlags = OhGetSystemRegistryFlags ();
  1678. OhSetSystemRegistryFlags (RegistryFlags | FLG_KERNEL_STACK_TRACE_DB);
  1679. Info ("Enabled `kernel mode stack traces' flag needed for handle traces. \n"
  1680. "Will take effect next time you boot. \n"
  1681. " \n");
  1682. }
  1683. else if ((Flags & FLG_MAINTAIN_OBJECT_TYPELIST)) {
  1684. RegistryFlags = OhGetSystemRegistryFlags ();
  1685. OhSetSystemRegistryFlags (RegistryFlags | FLG_MAINTAIN_OBJECT_TYPELIST);
  1686. Info ("Enabled `object type list' flag needed by the OH utility. \n"
  1687. "Will take effect next time you boot. \n"
  1688. " \n");
  1689. }
  1690. }
  1691. VOID
  1692. OhResetRequiredGlobalFlag (
  1693. DWORD Flags
  1694. )
  1695. {
  1696. DWORD RegistryFlags;
  1697. if ((Flags & FLG_KERNEL_STACK_TRACE_DB)) {
  1698. RegistryFlags = OhGetSystemRegistryFlags ();
  1699. RegistryFlags &= ~FLG_KERNEL_STACK_TRACE_DB;
  1700. OhSetSystemRegistryFlags (RegistryFlags);
  1701. Info ("Disabled `kernel mode stack traces' flag needed for handle traces. \n"
  1702. "Will take effect next time you boot. \n"
  1703. " \n");
  1704. }
  1705. else if ((Flags & FLG_MAINTAIN_OBJECT_TYPELIST)) {
  1706. RegistryFlags = OhGetSystemRegistryFlags ();
  1707. RegistryFlags &= ~FLG_MAINTAIN_OBJECT_TYPELIST;
  1708. OhSetSystemRegistryFlags (RegistryFlags);
  1709. Info ("Disabled `object type list' flag needed by the OH utility. \n"
  1710. "Will take effect next time you boot. \n"
  1711. " \n");
  1712. }
  1713. }
  1714. /////////////////////////////////////////////////////////////////////
  1715. ////////////////////////////////////////// Memory management routines
  1716. /////////////////////////////////////////////////////////////////////
  1717. PVOID
  1718. OhAlloc (
  1719. SIZE_T Size
  1720. )
  1721. {
  1722. PVOID P;
  1723. P = RtlAllocateHeap (RtlProcessHeap(), HEAP_ZERO_MEMORY, Size);
  1724. if (P == NULL) {
  1725. OhError (__FILE__, __LINE__,
  1726. "failed to allocate %u bytes",
  1727. Size);
  1728. }
  1729. return P;
  1730. }
  1731. VOID
  1732. OhFree (
  1733. PVOID P
  1734. )
  1735. {
  1736. RtlFreeHeap (RtlProcessHeap(), 0, P);
  1737. }
  1738. PVOID
  1739. OhZoneAlloc(
  1740. IN OUT SIZE_T *Length
  1741. )
  1742. /*++
  1743. Routine description:
  1744. Parameters:
  1745. Return value:
  1746. --*/
  1747. {
  1748. PVOID Buffer;
  1749. MEMORY_BASIC_INFORMATION MemoryInformation;
  1750. Buffer = VirtualAlloc (NULL,
  1751. *Length,
  1752. MEM_COMMIT | MEM_RESERVE,
  1753. PAGE_READWRITE);
  1754. if (Buffer == NULL) {
  1755. OhError (__FILE__, __LINE__,
  1756. "failed to allocate %u bytes",
  1757. *Length);
  1758. }
  1759. else if (Buffer != NULL &&
  1760. VirtualQuery (Buffer, &MemoryInformation, sizeof (MemoryInformation))) {
  1761. *Length = MemoryInformation.RegionSize;
  1762. }
  1763. return Buffer;
  1764. }
  1765. PVOID
  1766. OhZoneAllocEx(
  1767. SIZE_T Size
  1768. )
  1769. /*++
  1770. Routine description:
  1771. Parameters:
  1772. Return value:
  1773. --*/
  1774. {
  1775. PVOID Buffer;
  1776. Buffer = VirtualAlloc (NULL,
  1777. Size,
  1778. MEM_COMMIT | MEM_RESERVE,
  1779. PAGE_READWRITE);
  1780. if (Buffer == NULL) {
  1781. OhError (__FILE__, __LINE__,
  1782. "failed to allocate %u bytes",
  1783. Size);
  1784. }
  1785. return Buffer;
  1786. }
  1787. VOID
  1788. OhZoneFree(
  1789. IN PVOID Buffer
  1790. )
  1791. /*++
  1792. Routine description:
  1793. Parameters:
  1794. Return value:
  1795. --*/
  1796. {
  1797. if (!VirtualFree (Buffer,
  1798. 0,
  1799. MEM_DECOMMIT)) {
  1800. fprintf( stderr, "Unable to free buffer memory %p, error == %u\n", Buffer, GetLastError() );
  1801. exit( 1 );
  1802. }
  1803. }
  1804. BOOL
  1805. OhSetSymbolsPath (
  1806. )
  1807. /*++
  1808. Routine Description:
  1809. OhSetSymbolsPath tries to set automatically the symbol path if
  1810. _NT_SYMBOL_PATH environment variable is not already defined.
  1811. Arguments:
  1812. None.
  1813. Return Value:
  1814. Returns TRUE if the symbols path seems to be ok, that is
  1815. _NT_SYMBOL_PATH was defined or we managed to define it to
  1816. a meaningful value.
  1817. --*/
  1818. {
  1819. TCHAR Buffer [MAX_PATH];
  1820. DWORD Length;
  1821. BOOL Result;
  1822. Length = GetEnvironmentVariable (TEXT("_NT_SYMBOL_PATH"),
  1823. Buffer,
  1824. MAX_PATH);
  1825. if (Length == 0) {
  1826. Warning (NULL, 0,
  1827. "_NT_SYMBOL_PATH variable is not defined. Will be set to %%windir%%\\symbols.");
  1828. Length = GetEnvironmentVariable (TEXT("windir"),
  1829. Buffer,
  1830. MAX_PATH);
  1831. if (Length == 0) {
  1832. OhError (NULL, 0,
  1833. "Cannot get value of WINDIR environment variable.");
  1834. return FALSE;
  1835. }
  1836. strcat (Buffer, TEXT("\\symbols"));
  1837. Result = SetEnvironmentVariable (TEXT("_NT_SYMBOL_PATH"),
  1838. Buffer);
  1839. if (Result == FALSE) {
  1840. OhError (NULL, 0,
  1841. "Failed to set _NT_SYMBOL_PATH to `%s'", Buffer);
  1842. return FALSE;
  1843. }
  1844. OhComment ("_NT_SYMBOL_PATH set by default to %s", Buffer);
  1845. }
  1846. return TRUE;
  1847. }
  1848. /////////////////////////////////////////////////////////////////////
  1849. ///////////////////////////////////////////////////////// Log compare
  1850. /////////////////////////////////////////////////////////////////////
  1851. LPTSTR
  1852. OhCmpSearchStackTrace (
  1853. LPTSTR FileName,
  1854. LPTSTR TraceId
  1855. );
  1856. PSTRINGTOINTASSOCIATION
  1857. MAPSTRINGTOINT::GetStartPosition(
  1858. VOID
  1859. )
  1860. /*++
  1861. Routine Description:
  1862. This routine retrieves the first association in the list for iteration with the
  1863. MAPSTRINGTOINT::GetNextAssociation function.
  1864. Arguments:
  1865. None.
  1866. Return value:
  1867. The first association in the list, or NULL if the map is empty.
  1868. --*/
  1869. {
  1870. return Associations;
  1871. }
  1872. VOID
  1873. MAPSTRINGTOINT::GetNextAssociation(
  1874. IN OUT PSTRINGTOINTASSOCIATION & Position,
  1875. OUT LPTSTR & Key,
  1876. OUT LONG & Value)
  1877. /*++
  1878. Routine Description:
  1879. This routine retrieves the data for the current association and sets Position to
  1880. point to the next association (or NULL if this is the last association.)
  1881. Arguments:
  1882. Position - Supplies the current association and returns the next association.
  1883. Key - Returns the key for the current association.
  1884. Value - Returns the value for the current association.
  1885. Return value:
  1886. None.
  1887. --*/
  1888. {
  1889. Key = Position->Key;
  1890. Value = Position->Value;
  1891. Position = Position->Next;
  1892. }
  1893. MAPSTRINGTOINT::MAPSTRINGTOINT(
  1894. )
  1895. /*++
  1896. Routine Description:
  1897. This routine initializes a MAPSTRINGTOINT to be empty.
  1898. Arguments:
  1899. None.
  1900. Return value:
  1901. None.
  1902. --*/
  1903. {
  1904. Associations = NULL;
  1905. }
  1906. MAPSTRINGTOINT::~MAPSTRINGTOINT(
  1907. )
  1908. /*++
  1909. Routine Description:
  1910. This routine cleans up memory used by a MAPSTRINGTOINT.
  1911. Arguments:
  1912. None.
  1913. Return value:
  1914. None.
  1915. --*/
  1916. {
  1917. PSTRINGTOINTASSOCIATION Deleting;
  1918. // clean up associations
  1919. while (Associations != NULL) {
  1920. // save pointer to first association
  1921. Deleting = Associations;
  1922. // remove first association from list
  1923. Associations = Deleting->Next;
  1924. // free removed association
  1925. free (Deleting->Key);
  1926. delete Deleting;
  1927. }
  1928. }
  1929. LONG &
  1930. MAPSTRINGTOINT::operator [] (
  1931. IN LPTSTR Key
  1932. )
  1933. /*++
  1934. Routine Description:
  1935. This routine retrieves an l-value for the value associated with a given key.
  1936. Arguments:
  1937. Key - The key for which the value is to be retrieved.
  1938. Return value:
  1939. A reference to the value associated with the provided key.
  1940. --*/
  1941. {
  1942. PSTRINGTOINTASSOCIATION CurrentAssociation = Associations;
  1943. // search for key
  1944. while(CurrentAssociation != NULL) {
  1945. if(!_tcscmp(CurrentAssociation->Key, Key)) {
  1946. // found key, return value
  1947. return CurrentAssociation->Value;
  1948. }
  1949. CurrentAssociation = CurrentAssociation->Next;
  1950. }
  1951. // not found, create new association
  1952. CurrentAssociation = new STRINGTOINTASSOCIATION;
  1953. if (CurrentAssociation == NULL) {
  1954. _tprintf(_T("Memory allocation failure\n"));
  1955. exit (0);
  1956. }
  1957. if (Key == NULL) {
  1958. _tprintf(_T("Null object name\n"));
  1959. exit (0);
  1960. }
  1961. else if (_tcscmp (Key, "") == 0) {
  1962. _tprintf(_T("Invalid object name `%s'\n"), Key);
  1963. exit (0);
  1964. }
  1965. CurrentAssociation->Key = _tcsdup(Key);
  1966. if (CurrentAssociation->Key == NULL) {
  1967. _tprintf(_T("Memory string allocation failure\n"));
  1968. exit (0);
  1969. }
  1970. // add association to front of list
  1971. CurrentAssociation->Next = Associations;
  1972. Associations = CurrentAssociation;
  1973. // return value for new association
  1974. return CurrentAssociation->Value;
  1975. }
  1976. BOOLEAN
  1977. MAPSTRINGTOINT::Lookup(
  1978. IN LPTSTR Key,
  1979. OUT LONG & Value
  1980. )
  1981. /*++
  1982. Routine Description:
  1983. This routine retrieves an r-value for the value association with a given key.
  1984. Arguments:
  1985. Key - The key for which the associated value is to be retrieved.
  1986. Value - Returns the value associated with Key if Key is present in the map.
  1987. Return value:
  1988. TRUE if the key is present in the map, FALSE otherwise.
  1989. --*/
  1990. {
  1991. PSTRINGTOINTASSOCIATION CurrentAssociation = Associations;
  1992. // search for key
  1993. while (CurrentAssociation != NULL) {
  1994. if(!_tcscmp(CurrentAssociation->Key , Key)) {
  1995. // found key, return it
  1996. Value = CurrentAssociation->Value;
  1997. return TRUE;
  1998. }
  1999. CurrentAssociation = CurrentAssociation->Next;
  2000. }
  2001. // didn't find it
  2002. return FALSE;
  2003. }
  2004. BOOLEAN
  2005. OhCmpPopulateMapsFromFile(
  2006. IN LPTSTR FileName,
  2007. OUT MAPSTRINGTOINT & TypeMap,
  2008. OUT MAPSTRINGTOINT & NameMap,
  2009. BOOLEAN FileWithTraces
  2010. )
  2011. /*++
  2012. Routine Description:
  2013. This routine parses an OH output file and fills two maps with the number of handles of
  2014. each type and the number of handles to each named object.
  2015. Arguments:
  2016. FileName - OH output file to parse.
  2017. TypeMap - Map to fill with handle type information.
  2018. NameMap - Map to fill with named object information.
  2019. Return value:
  2020. TRUE if the file was successfully parsed, FALSE otherwise.
  2021. --*/
  2022. {
  2023. LONG HowMany;
  2024. LPTSTR Name, Type, Process, Pid;
  2025. LPTSTR NewLine;
  2026. TCHAR LineBuffer[512];
  2027. TCHAR ObjectName[512];
  2028. TCHAR TypeName[512];
  2029. FILE *InputFile;
  2030. ULONG LineNumber;
  2031. BOOLEAN rc;
  2032. LineNumber = 0;
  2033. // open file
  2034. InputFile = _tfopen(FileName, _T("rt"));
  2035. if (InputFile == NULL) {
  2036. _ftprintf(stderr, _T("Error opening oh file %s.\n"), FileName);
  2037. return FALSE;
  2038. }
  2039. rc = TRUE;
  2040. // loop through lines in oh output
  2041. while (_fgetts(LineBuffer, sizeof(LineBuffer), InputFile)
  2042. && !( feof(InputFile) || ferror(InputFile) ) ) {
  2043. LineNumber += 1;
  2044. // trim off newline
  2045. if((NewLine = _tcschr(LineBuffer, _T('\n'))) != NULL) {
  2046. *NewLine = _T('\0');
  2047. }
  2048. // ignore lines that start with white space or are empty.
  2049. if (LineBuffer[0] == _T('\0') ||
  2050. LineBuffer[0] == _T('\t') ||
  2051. LineBuffer[0] == _T(' ')) {
  2052. continue;
  2053. }
  2054. // ignore lines that start with a comment
  2055. if( LineBuffer[0] == _T('/') && LineBuffer[1] == _T('/') ) {
  2056. continue;
  2057. }
  2058. // skip pid
  2059. if((Pid = _tcstok(LineBuffer, _T(" \t"))) == NULL) {
  2060. rc = FALSE;
  2061. break;
  2062. }
  2063. // skip process name
  2064. if((Process = _tcstok(NULL, _T(" \t"))) == NULL) {
  2065. rc = FALSE;
  2066. break;
  2067. }
  2068. // Type points to the type of handle
  2069. if ((Type = _tcstok(NULL, _T(" \t"))) == NULL) {
  2070. rc = FALSE;
  2071. break;
  2072. }
  2073. // HowMany = number of previous handles with this type
  2074. _stprintf (TypeName,
  2075. TEXT("<%s/%s/%s>"),
  2076. Process,
  2077. Pid,
  2078. Type);
  2079. if (TypeMap.Lookup(TypeName, HowMany) == FALSE) {
  2080. HowMany = 0;
  2081. }
  2082. // add another handle of this type
  2083. TypeMap[TypeName] = (HowMany + 1);
  2084. //
  2085. // Name points to the name. These are magic numbers based on the way
  2086. // OH formats output. The output is a little bit different if the
  2087. // `-h' option of OH was used (this dumps stack traces too).
  2088. //
  2089. Name = LineBuffer + 39 + 5;
  2090. if (FileWithTraces) {
  2091. Name += 7;
  2092. }
  2093. if (_tcscmp (Name, "") == 0) {
  2094. _stprintf (ObjectName,
  2095. TEXT("<%s/%s/%s>::<<noname>>"),
  2096. Process,
  2097. Pid,
  2098. Type);
  2099. }
  2100. else {
  2101. _stprintf (ObjectName,
  2102. TEXT("<%s/%s/%s>::%s"),
  2103. Process,
  2104. Pid,
  2105. Type,
  2106. Name);
  2107. }
  2108. // HowMany = number of previous handles with this name
  2109. // printf("name --> `%s' \n", ObjectName);
  2110. if (NameMap.Lookup(ObjectName, HowMany) == FALSE) {
  2111. HowMany = 0;
  2112. }
  2113. // add another handle with this name and read the next line
  2114. // note -- NameMap[] is a class operator, not an array.
  2115. NameMap[ObjectName] = (HowMany + 1);
  2116. }
  2117. // done, close file
  2118. fclose(InputFile);
  2119. return rc;
  2120. }
  2121. int
  2122. __cdecl
  2123. OhCmpKeyCompareAssociation (
  2124. const void * Left,
  2125. const void * Right
  2126. )
  2127. {
  2128. PSTRINGTOINTASSOCIATION X;
  2129. PSTRINGTOINTASSOCIATION Y;
  2130. X = (PSTRINGTOINTASSOCIATION)Left;
  2131. Y = (PSTRINGTOINTASSOCIATION)Right;
  2132. return _tcscmp (X->Key, Y->Key);
  2133. }
  2134. int
  2135. __cdecl
  2136. OhCmpValueCompareAssociation (
  2137. const void * Left,
  2138. const void * Right
  2139. )
  2140. {
  2141. PSTRINGTOINTASSOCIATION X;
  2142. PSTRINGTOINTASSOCIATION Y;
  2143. X = (PSTRINGTOINTASSOCIATION)Left;
  2144. Y = (PSTRINGTOINTASSOCIATION)Right;
  2145. return Y->Value - X->Value;
  2146. }
  2147. VOID
  2148. OhCmpPrintIncreases(
  2149. IN MAPSTRINGTOINT & BeforeMap,
  2150. IN MAPSTRINGTOINT & AfterMap,
  2151. IN BOOLEAN ReportIncreasesOnly,
  2152. IN BOOLEAN PrintHighlights,
  2153. IN LPTSTR AfterLogName
  2154. )
  2155. /*++
  2156. Routine Description:
  2157. This routine compares two maps and prints out the differences between them.
  2158. Arguments:
  2159. BeforeMap - First map to compare.
  2160. AfterMap - Second map to compare.
  2161. ReportIncreasesOnly - TRUE for report only increases from BeforeMap to AfterMap,
  2162. FALSE for report all differences.
  2163. Return value:
  2164. None.
  2165. --*/
  2166. {
  2167. PSTRINGTOINTASSOCIATION Association = NULL;
  2168. LONG HowManyBefore = 0;
  2169. LONG HowManyAfter = 0;
  2170. LPTSTR Key = NULL;
  2171. PSTRINGTOINTASSOCIATION SortBuffer;
  2172. ULONG SortBufferSize;
  2173. ULONG SortBufferIndex;
  2174. //
  2175. // Loop through associations in map and figure out how many output lines
  2176. // we will have.
  2177. //
  2178. SortBufferSize = 0;
  2179. for (Association = AfterMap.GetStartPosition(),
  2180. AfterMap.GetNextAssociation(Association, Key, HowManyAfter);
  2181. Association != NULL;
  2182. AfterMap.GetNextAssociation(Association, Key, HowManyAfter)) {
  2183. // look up value for this key in BeforeMap
  2184. if(BeforeMap.Lookup(Key, HowManyBefore) == FALSE) {
  2185. HowManyBefore = 0;
  2186. }
  2187. // should we report this?
  2188. if((HowManyAfter > HowManyBefore) ||
  2189. ((!ReportIncreasesOnly) && (HowManyAfter != HowManyBefore))) {
  2190. SortBufferSize += 1;
  2191. }
  2192. }
  2193. //
  2194. // Loop through associations in map again this time filling the output buffer.
  2195. //
  2196. SortBufferIndex = 0;
  2197. SortBuffer = new STRINGTOINTASSOCIATION[SortBufferSize];
  2198. if (SortBuffer == NULL) {
  2199. _ftprintf(stderr, _T("Failed to allocate internal buffer of %u bytes.\n"),
  2200. SortBufferSize);
  2201. return;
  2202. }
  2203. for (Association = AfterMap.GetStartPosition(),
  2204. AfterMap.GetNextAssociation(Association, Key, HowManyAfter);
  2205. Association != NULL;
  2206. AfterMap.GetNextAssociation(Association, Key, HowManyAfter)) {
  2207. // look up value for this key in BeforeMap
  2208. if(BeforeMap.Lookup(Key, HowManyBefore) == FALSE) {
  2209. HowManyBefore = 0;
  2210. }
  2211. // should we report this?
  2212. if((HowManyAfter > HowManyBefore) ||
  2213. ((!ReportIncreasesOnly) && (HowManyAfter != HowManyBefore))) {
  2214. ZeroMemory (&(SortBuffer[SortBufferIndex]),
  2215. sizeof (STRINGTOINTASSOCIATION));
  2216. SortBuffer[SortBufferIndex].Key = Key;
  2217. SortBuffer[SortBufferIndex].Value = HowManyAfter - HowManyBefore;
  2218. SortBufferIndex += 1;
  2219. }
  2220. }
  2221. //
  2222. // Sort the output buffer using the Key.
  2223. //
  2224. if (PrintHighlights) {
  2225. qsort (SortBuffer,
  2226. SortBufferSize,
  2227. sizeof (STRINGTOINTASSOCIATION),
  2228. OhCmpValueCompareAssociation);
  2229. }
  2230. else {
  2231. qsort (SortBuffer,
  2232. SortBufferSize,
  2233. sizeof (STRINGTOINTASSOCIATION),
  2234. OhCmpKeyCompareAssociation);
  2235. }
  2236. //
  2237. // Dump the buffer.
  2238. //
  2239. for (SortBufferIndex = 0; SortBufferIndex < SortBufferSize; SortBufferIndex += 1) {
  2240. if (PrintHighlights) {
  2241. if (SortBuffer[SortBufferIndex].Value >= 1) {
  2242. TCHAR TraceId[7];
  2243. LPTSTR Start;
  2244. _tprintf(_T("%d\t%s\n"),
  2245. SortBuffer[SortBufferIndex].Value,
  2246. SortBuffer[SortBufferIndex].Key);
  2247. Start = _tcsstr (SortBuffer[SortBufferIndex].Key, "(");
  2248. if (Start == NULL) {
  2249. TraceId[0] = 0;
  2250. }
  2251. else {
  2252. _tcsncpy (TraceId,
  2253. Start,
  2254. 6);
  2255. TraceId[6] = 0;
  2256. }
  2257. _tprintf (_T("%s"), OhCmpSearchStackTrace (AfterLogName, TraceId));
  2258. }
  2259. }
  2260. else {
  2261. _tprintf(_T("%d\t%s\n"),
  2262. SortBuffer[SortBufferIndex].Value,
  2263. SortBuffer[SortBufferIndex].Key);
  2264. }
  2265. }
  2266. //
  2267. // Clean up memory.
  2268. //
  2269. if (SortBuffer) {
  2270. delete[] SortBuffer;
  2271. }
  2272. }
  2273. VOID
  2274. OhCmpCompareLogs (
  2275. IN LONG argc,
  2276. IN LPTSTR argv[]
  2277. )
  2278. /*++
  2279. Routine Description:
  2280. This routine parses program arguments, reads the two input files, and prints out the
  2281. differences.
  2282. Arguments:
  2283. argc - Number of command-line arguments.
  2284. argv - Command-line arguments.
  2285. Return value:
  2286. None.
  2287. --*/
  2288. {
  2289. MAPSTRINGTOINT TypeMapBefore, TypeMapAfter;
  2290. MAPSTRINGTOINT NameMapBefore, NameMapAfter;
  2291. LPTSTR BeforeFileName=NULL;
  2292. LPTSTR AfterFileName=NULL;
  2293. BOOLEAN ReportIncreasesOnly = TRUE;
  2294. BOOLEAN Interpreted = FALSE;
  2295. BOOLEAN Result;
  2296. BOOLEAN FileWithTraces;
  2297. BOOLEAN PrintHighlights;
  2298. // parse arguments
  2299. FileWithTraces = FALSE;
  2300. PrintHighlights = FALSE;
  2301. for (LONG n = 1; n < argc; n++) {
  2302. Interpreted = FALSE;
  2303. switch(argv[n][0]) {
  2304. case _T('-'):
  2305. case _T('/'):
  2306. // the argument is a switch
  2307. if(_tcsicmp(argv[n]+1, _T("all")) == 0) {
  2308. ReportIncreasesOnly = FALSE;
  2309. Interpreted = TRUE;
  2310. }
  2311. else if (_tcsicmp(argv[n]+1, _T("t")) == 0) {
  2312. FileWithTraces = TRUE;
  2313. Interpreted = TRUE;
  2314. }
  2315. else if (_tcsicmp(argv[n]+1, _T("l")) == 0) {
  2316. PrintHighlights = TRUE;
  2317. Interpreted = TRUE;
  2318. }
  2319. break;
  2320. default:
  2321. // the argument is a file name
  2322. if(BeforeFileName == NULL) {
  2323. BeforeFileName = argv[n];
  2324. Interpreted = TRUE;
  2325. } else {
  2326. if(AfterFileName == NULL) {
  2327. AfterFileName = argv[n];
  2328. Interpreted = TRUE;
  2329. } else {
  2330. Help();
  2331. }
  2332. }
  2333. break;
  2334. }
  2335. if(!Interpreted) {
  2336. Help();
  2337. }
  2338. }
  2339. // did user specify required arguments?
  2340. if((BeforeFileName == NULL) || (AfterFileName == NULL)) {
  2341. Help();
  2342. }
  2343. // read oh1 file
  2344. Result = OhCmpPopulateMapsFromFile (BeforeFileName,
  2345. TypeMapBefore,
  2346. NameMapBefore,
  2347. FileWithTraces);
  2348. if(Result == FALSE) {
  2349. _ftprintf(stderr, _T("Failed to read first OH output file.\n"));
  2350. return;
  2351. }
  2352. // read oh2 file
  2353. Result = OhCmpPopulateMapsFromFile (AfterFileName,
  2354. TypeMapAfter,
  2355. NameMapAfter,
  2356. FileWithTraces);
  2357. if(Result == FALSE) {
  2358. _ftprintf(stderr, _T("Failed to read second OH output file.\n"));
  2359. return;
  2360. }
  2361. // print out increases by handle name
  2362. if (PrintHighlights) {
  2363. _putts (TEXT ("\n")
  2364. TEXT("// \n")
  2365. TEXT("// Possible leaks (DELTA <PROCESS/PID/TYPE>::NAME): \n")
  2366. TEXT("// \n")
  2367. TEXT("// Note that the NAME can appear as `(TRACEID) NAME' if output \n")
  2368. TEXT("// is generated by comparing OH files containing traces. In this case \n")
  2369. TEXT("// just search in the `AFTER' OH log file for the trace id to \n")
  2370. TEXT("// find the stack trace creating the handle possibly leaked. \n")
  2371. TEXT("// \n\n"));
  2372. OhCmpPrintIncreases (NameMapBefore,
  2373. NameMapAfter,
  2374. ReportIncreasesOnly,
  2375. TRUE,
  2376. AfterFileName);
  2377. }
  2378. // print out increases by handle type
  2379. _putts (TEXT ("\n")
  2380. TEXT("// \n")
  2381. TEXT("// Handle types (DELTA <PROCESS/PID/TYPE>): \n")
  2382. TEXT("// \n")
  2383. TEXT("// DELTA is the additional number of handles found in the `AFTER' log. \n")
  2384. TEXT("// PROCESS is the process name having a handle increase. \n")
  2385. TEXT("// PID is the process PID having a handle increase. \n")
  2386. TEXT("// TYPE is the type of the handle \n")
  2387. TEXT("// \n\n"));
  2388. OhCmpPrintIncreases (TypeMapBefore,
  2389. TypeMapAfter,
  2390. ReportIncreasesOnly,
  2391. FALSE,
  2392. NULL);
  2393. // print out increases by handle name
  2394. _putts (TEXT ("\n")
  2395. TEXT("// \n")
  2396. TEXT("// Objects (named and anonymous) (DELTA <PROCESS/PID/TYPE>::NAME): \n")
  2397. TEXT("// \n")
  2398. TEXT("// DELTA is the additional number of handles found in the `AFTER' log. \n")
  2399. TEXT("// PROCESS is the process name having a handle increase. \n")
  2400. TEXT("// PID is the process PID having a handle increase. \n")
  2401. TEXT("// TYPE is the type of the handle \n")
  2402. TEXT("// NAME is the name of the handle. Anonymous handles appear with name <<noname>>.\n")
  2403. TEXT("// \n")
  2404. TEXT("// Note that the NAME can appear as `(TRACEID) NAME' if output \n")
  2405. TEXT("// is generated by comparing OH files containing traces. In this case \n")
  2406. TEXT("// just search in the `AFTER' OH log file for the trace id to \n")
  2407. TEXT("// find the stack trace creating the handle possibly leaked. \n")
  2408. TEXT("// \n\n"));
  2409. OhCmpPrintIncreases (NameMapBefore,
  2410. NameMapAfter,
  2411. ReportIncreasesOnly,
  2412. FALSE,
  2413. NULL);
  2414. }
  2415. /////////////////////////////////////////////////////////////////////
  2416. /////////////////////////////////////////////////////////////////////
  2417. /////////////////////////////////////////////////////////////////////
  2418. TCHAR OhCmpStackTraceBuffer [0x10000];
  2419. LPTSTR
  2420. OhCmpSearchStackTrace (
  2421. LPTSTR FileName,
  2422. LPTSTR TraceId
  2423. )
  2424. {
  2425. TCHAR LineBuffer[512];
  2426. FILE *InputFile;
  2427. OhCmpStackTraceBuffer[0] = 0;
  2428. //
  2429. // Open file.
  2430. //
  2431. InputFile = _tfopen(FileName, _T("rt"));
  2432. if (InputFile == NULL) {
  2433. _ftprintf(stderr, _T("Error opening oh file %s.\n"), FileName);
  2434. return NULL;
  2435. }
  2436. //
  2437. // Loop through lines in oh output.
  2438. //
  2439. while (_fgetts(LineBuffer, sizeof(LineBuffer), InputFile)
  2440. && !( feof(InputFile) || ferror(InputFile) ) ) {
  2441. //
  2442. // Skip line if it does not contain trace ID.
  2443. //
  2444. if (_tcsstr (LineBuffer, TraceId) == NULL) {
  2445. continue;
  2446. }
  2447. //
  2448. // We have got a trace ID. We need now to copy everything
  2449. // to a trace buffer until we get a line containing a character
  2450. // in column zero.
  2451. //
  2452. while (_fgetts(LineBuffer, sizeof(LineBuffer), InputFile)
  2453. && !( feof(InputFile) || ferror(InputFile) ) ) {
  2454. if (LineBuffer[0] == _T(' ') ||
  2455. LineBuffer[0] == _T('\0') ||
  2456. LineBuffer[0] == _T('\n') ||
  2457. LineBuffer[0] == _T('\t')) {
  2458. _tcscat (OhCmpStackTraceBuffer, LineBuffer);
  2459. }
  2460. else {
  2461. break;
  2462. }
  2463. }
  2464. break;
  2465. }
  2466. //
  2467. // Close file.
  2468. fclose(InputFile);
  2469. return OhCmpStackTraceBuffer;
  2470. }