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.

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