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.

1022 lines
24 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. ohcmp.cpp
  5. Abstract:
  6. This module reports the differences between two oh output files.
  7. Author:
  8. Matt Bandy (t-mattba) 23-Jul-1998
  9. Revision History:
  10. 24-Jul-1998 t-mattba
  11. Modified module to conform to coding standards.
  12. 11-Jun-2001 silviuc
  13. Deal with handles that are recreated with a different value
  14. and other simple output improvements (sorted output etc.).
  15. --*/
  16. #include <windows.h>
  17. #include <common.ver>
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <stdlib.h>
  21. #include <tchar.h>
  22. #include "MAPSTRINGINT.h"
  23. LPTSTR HelpText =
  24. TEXT("ohcmp - Display difference between two OH output files --") BUILD_MACHINE_TAG TEXT("\n")
  25. VER_LEGALCOPYRIGHT_STR TEXT("\n")
  26. TEXT(" \n")
  27. TEXT("ohcmp [OPTION ...] BEFORE_OH_FILE AFTER_OH_FILE \n")
  28. TEXT(" \n")
  29. TEXT("/h Print most interesting increases in a separate initial section. \n")
  30. TEXT("/t Do not add TRACE id to the names if files contain traces. \n")
  31. TEXT("/all Report decreases as well as increases. \n")
  32. TEXT(" \n")
  33. TEXT("If the OH files have been created with -h option (they contain traces) \n")
  34. TEXT("then ohcmp will print Names having this syntax: (TRACEID) NAME. \n")
  35. TEXT("In case of a potential leak just search for the TRACEID in the original\n")
  36. TEXT("OH file to find the stack trace. \n")
  37. TEXT(" \n");
  38. LPTSTR
  39. SearchStackTrace (
  40. LPTSTR FileName,
  41. LPTSTR TraceId
  42. );
  43. PSTRINGTOINTASSOCIATION
  44. MAPSTRINGTOINT::GetStartPosition(
  45. VOID
  46. )
  47. /*++
  48. Routine Description:
  49. This routine retrieves the first association in the list for iteration with the
  50. MAPSTRINGTOINT::GetNextAssociation function.
  51. Arguments:
  52. None.
  53. Return value:
  54. The first association in the list, or NULL if the map is empty.
  55. --*/
  56. {
  57. return Associations;
  58. }
  59. VOID
  60. MAPSTRINGTOINT::GetNextAssociation(
  61. IN OUT PSTRINGTOINTASSOCIATION & Position,
  62. OUT LPTSTR & Key,
  63. OUT LONG & Value)
  64. /*++
  65. Routine Description:
  66. This routine retrieves the data for the current association and sets Position to
  67. point to the next association (or NULL if this is the last association.)
  68. Arguments:
  69. Position - Supplies the current association and returns the next association.
  70. Key - Returns the key for the current association.
  71. Value - Returns the value for the current association.
  72. Return value:
  73. None.
  74. --*/
  75. {
  76. Key = Position->Key;
  77. Value = Position->Value;
  78. Position = Position->Next;
  79. }
  80. MAPSTRINGTOINT::MAPSTRINGTOINT(
  81. )
  82. /*++
  83. Routine Description:
  84. This routine initializes a MAPSTRINGTOINT to be empty.
  85. Arguments:
  86. None.
  87. Return value:
  88. None.
  89. --*/
  90. {
  91. Associations = NULL;
  92. }
  93. MAPSTRINGTOINT::~MAPSTRINGTOINT(
  94. )
  95. /*++
  96. Routine Description:
  97. This routine cleans up memory used by a MAPSTRINGTOINT.
  98. Arguments:
  99. None.
  100. Return value:
  101. None.
  102. --*/
  103. {
  104. PSTRINGTOINTASSOCIATION Deleting;
  105. // clean up associations
  106. while (Associations != NULL) {
  107. // save pointer to first association
  108. Deleting = Associations;
  109. // remove first association from list
  110. Associations = Deleting->Next;
  111. // free removed association
  112. free (Deleting->Key);
  113. delete Deleting;
  114. }
  115. }
  116. LONG &
  117. MAPSTRINGTOINT::operator [] (
  118. IN LPTSTR Key
  119. )
  120. /*++
  121. Routine Description:
  122. This routine retrieves an l-value for the value associated with a given key.
  123. Arguments:
  124. Key - The key for which the value is to be retrieved.
  125. Return value:
  126. A reference to the value associated with the provided key.
  127. --*/
  128. {
  129. PSTRINGTOINTASSOCIATION CurrentAssociation = Associations;
  130. // search for key
  131. while (CurrentAssociation != NULL) {
  132. if (!_tcscmp(CurrentAssociation->Key, Key)) {
  133. // found key, return value
  134. return CurrentAssociation->Value;
  135. }
  136. CurrentAssociation = CurrentAssociation->Next;
  137. }
  138. // not found, create new association
  139. CurrentAssociation = new STRINGTOINTASSOCIATION;
  140. if (CurrentAssociation == NULL) {
  141. _tprintf(_T("Memory allocation failure\n"));
  142. exit (0);
  143. }
  144. if (Key == NULL) {
  145. _tprintf(_T("Null object name\n"));
  146. exit (0);
  147. } else if (_tcscmp (Key, "") == 0) {
  148. _tprintf(_T("Invalid object name `%s'\n"), Key);
  149. exit (0);
  150. }
  151. CurrentAssociation->Key = _tcsdup(Key);
  152. if (CurrentAssociation->Key == NULL) {
  153. _tprintf(_T("Memory string allocation failure\n"));
  154. exit (0);
  155. }
  156. // add association to front of list
  157. CurrentAssociation->Next = Associations;
  158. Associations = CurrentAssociation;
  159. // return value for new association
  160. return CurrentAssociation->Value;
  161. }
  162. BOOLEAN
  163. MAPSTRINGTOINT::Lookup(
  164. IN LPTSTR Key,
  165. OUT LONG & Value
  166. )
  167. /*++
  168. Routine Description:
  169. This routine retrieves an r-value for the value association with a given key.
  170. Arguments:
  171. Key - The key for which the associated value is to be retrieved.
  172. Value - Returns the value associated with Key if Key is present in the map.
  173. Return value:
  174. TRUE if the key is present in the map, FALSE otherwise.
  175. --*/
  176. {
  177. PSTRINGTOINTASSOCIATION CurrentAssociation = Associations;
  178. // search for key
  179. while (CurrentAssociation != NULL) {
  180. if (!_tcscmp(CurrentAssociation->Key , Key)) {
  181. // found key, return it
  182. Value = CurrentAssociation->Value;
  183. return TRUE;
  184. }
  185. CurrentAssociation = CurrentAssociation->Next;
  186. }
  187. // didn't find it
  188. return FALSE;
  189. }
  190. BOOLEAN
  191. PopulateMapsFromFile(
  192. IN LPTSTR FileName,
  193. OUT MAPSTRINGTOINT & TypeMap,
  194. OUT MAPSTRINGTOINT & NameMap,
  195. BOOLEAN FileWithTraces
  196. )
  197. /*++
  198. Routine Description:
  199. This routine parses an OH output file and fills two maps with the number of handles of
  200. each type and the number of handles to each named object.
  201. Arguments:
  202. FileName - OH output file to parse.
  203. TypeMap - Map to fill with handle type information.
  204. NameMap - Map to fill with named object information.
  205. Return value:
  206. TRUE if the file was successfully parsed, FALSE otherwise.
  207. --*/
  208. {
  209. LONG HowMany;
  210. LPTSTR Name, Type, Process, Pid;
  211. LPTSTR NewLine;
  212. TCHAR LineBuffer[512];
  213. TCHAR ObjectName[512];
  214. TCHAR TypeName[512];
  215. FILE *InputFile;
  216. ULONG LineNumber;
  217. BOOLEAN rc;
  218. LineNumber = 0;
  219. // open file
  220. InputFile = _tfopen(FileName, _T("rt"));
  221. if (InputFile == NULL) {
  222. _ftprintf(stderr, _T("Error opening oh file %s.\n"), FileName);
  223. return FALSE;
  224. }
  225. rc = TRUE;
  226. // loop through lines in oh output
  227. while (_fgetts(LineBuffer, sizeof(LineBuffer) / sizeof(TCHAR), InputFile)
  228. && !( feof(InputFile) || ferror(InputFile) ) ) {
  229. LineNumber += 1;
  230. // trim off newline
  231. if ((NewLine = _tcschr(LineBuffer, _T('\n'))) != NULL) {
  232. *NewLine = _T('\0');
  233. }
  234. // ignore lines that start with white space or are empty.
  235. if (LineBuffer[0] == _T('\0') ||
  236. LineBuffer[0] == _T('\t') ||
  237. LineBuffer[0] == _T(' ')) {
  238. continue;
  239. }
  240. // ignore lines that start with a comment
  241. if ( LineBuffer[0] == _T('/') && LineBuffer[1] == _T('/') ) {
  242. continue;
  243. }
  244. // skip pid
  245. if ((Pid = _tcstok(LineBuffer, _T(" \t"))) == NULL) {
  246. rc = FALSE;
  247. break;
  248. }
  249. // skip process name
  250. if ((Process = _tcstok(NULL, _T(" \t"))) == NULL) {
  251. rc = FALSE;
  252. break;
  253. }
  254. // Type points to the type of handle
  255. if ((Type = _tcstok(NULL, _T(" \t"))) == NULL) {
  256. rc = FALSE;
  257. break;
  258. }
  259. // HowMany = number of previous handles with this type
  260. TypeName[sizeof(TypeName) / sizeof(TCHAR) - 1] = 0;
  261. _sntprintf (TypeName,
  262. sizeof(TypeName) / sizeof(TCHAR) - 1,
  263. TEXT("<%s/%s/%s>"),
  264. Process,
  265. Pid,
  266. Type);
  267. if (TypeMap.Lookup(TypeName, HowMany) == FALSE) {
  268. HowMany = 0;
  269. }
  270. // add another handle of this type
  271. TypeMap[TypeName] = (HowMany + 1);
  272. //
  273. // Name points to the name. These are magic numbers based on the way
  274. // OH formats output. The output is a little bit different if the
  275. // `-h' option of OH was used (this dumps stack traces too).
  276. //
  277. Name = LineBuffer + 39 + 5;
  278. if (FileWithTraces) {
  279. Name += 7;
  280. }
  281. ObjectName[sizeof(ObjectName) / sizeof(TCHAR) - 1] = 0;
  282. if (_tcscmp (Name, "") == 0) {
  283. _sntprintf (ObjectName,
  284. sizeof(ObjectName) / sizeof(TCHAR) - 1,
  285. TEXT("<%s/%s/%s>::<<noname>>"),
  286. Process,
  287. Pid,
  288. Type);
  289. } else {
  290. _sntprintf (ObjectName,
  291. sizeof(ObjectName) / sizeof(TCHAR) - 1,
  292. TEXT("<%s/%s/%s>::%s"),
  293. Process,
  294. Pid,
  295. Type,
  296. Name);
  297. }
  298. // HowMany = number of previous handles with this name
  299. // printf("name --> `%s' \n", ObjectName);
  300. if (NameMap.Lookup(ObjectName, HowMany) == FALSE) {
  301. HowMany = 0;
  302. }
  303. // add another handle with this name and read the next line
  304. // note -- NameMap[] is a class operator, not an array.
  305. NameMap[ObjectName] = (HowMany + 1);
  306. }
  307. // done, close file
  308. fclose(InputFile);
  309. return rc;
  310. }
  311. int
  312. __cdecl
  313. KeyCompareAssociation (
  314. const void * Left,
  315. const void * Right
  316. )
  317. {
  318. PSTRINGTOINTASSOCIATION X;
  319. PSTRINGTOINTASSOCIATION Y;
  320. X = (PSTRINGTOINTASSOCIATION)Left;
  321. Y = (PSTRINGTOINTASSOCIATION)Right;
  322. return _tcscmp (X->Key, Y->Key);
  323. }
  324. int
  325. __cdecl
  326. ValueCompareAssociation (
  327. const void * Left,
  328. const void * Right
  329. )
  330. {
  331. PSTRINGTOINTASSOCIATION X;
  332. PSTRINGTOINTASSOCIATION Y;
  333. X = (PSTRINGTOINTASSOCIATION)Left;
  334. Y = (PSTRINGTOINTASSOCIATION)Right;
  335. return Y->Value - X->Value;
  336. }
  337. VOID
  338. PrintIncreases(
  339. IN MAPSTRINGTOINT & BeforeMap,
  340. IN MAPSTRINGTOINT & AfterMap,
  341. IN BOOLEAN ReportIncreasesOnly,
  342. IN BOOLEAN PrintHighlights,
  343. IN LPTSTR AfterLogName
  344. )
  345. /*++
  346. Routine Description:
  347. This routine compares two maps and prints out the differences between them.
  348. Arguments:
  349. BeforeMap - First map to compare.
  350. AfterMap - Second map to compare.
  351. ReportIncreasesOnly - TRUE for report only increases from BeforeMap to AfterMap,
  352. FALSE for report all differences.
  353. Return value:
  354. None.
  355. --*/
  356. {
  357. PSTRINGTOINTASSOCIATION Association = NULL;
  358. LONG HowManyBefore = 0;
  359. LONG HowManyAfter = 0;
  360. LPTSTR Key = NULL;
  361. PSTRINGTOINTASSOCIATION SortBuffer;
  362. ULONG SortBufferSize;
  363. ULONG SortBufferIndex;
  364. //
  365. // Loop through associations in map and figure out how many output lines
  366. // we will have.
  367. //
  368. SortBufferSize = 0;
  369. for (Association = AfterMap.GetStartPosition(),
  370. AfterMap.GetNextAssociation(Association, Key, HowManyAfter);
  371. Association != NULL;
  372. AfterMap.GetNextAssociation(Association, Key, HowManyAfter)) {
  373. // look up value for this key in BeforeMap
  374. if (BeforeMap.Lookup(Key, HowManyBefore) == FALSE) {
  375. HowManyBefore = 0;
  376. }
  377. // should we report this?
  378. if ((HowManyAfter > HowManyBefore) ||
  379. ((!ReportIncreasesOnly) && (HowManyAfter != HowManyBefore))) {
  380. SortBufferSize += 1;
  381. }
  382. }
  383. //
  384. // Loop through associations in map again this time filling the output buffer.
  385. //
  386. SortBufferIndex = 0;
  387. SortBuffer = new STRINGTOINTASSOCIATION[SortBufferSize];
  388. if (SortBuffer == NULL) {
  389. _ftprintf(stderr, _T("Failed to allocate internal buffer of %u bytes.\n"),
  390. SortBufferSize);
  391. return;
  392. }
  393. for (Association = AfterMap.GetStartPosition(),
  394. AfterMap.GetNextAssociation(Association, Key, HowManyAfter);
  395. Association != NULL;
  396. AfterMap.GetNextAssociation(Association, Key, HowManyAfter)) {
  397. // look up value for this key in BeforeMap
  398. if (BeforeMap.Lookup(Key, HowManyBefore) == FALSE) {
  399. HowManyBefore = 0;
  400. }
  401. // should we report this?
  402. if ((HowManyAfter > HowManyBefore) ||
  403. ((!ReportIncreasesOnly) && (HowManyAfter != HowManyBefore))) {
  404. ZeroMemory (&(SortBuffer[SortBufferIndex]),
  405. sizeof (STRINGTOINTASSOCIATION));
  406. SortBuffer[SortBufferIndex].Key = Key;
  407. SortBuffer[SortBufferIndex].Value = HowManyAfter - HowManyBefore;
  408. SortBufferIndex += 1;
  409. }
  410. }
  411. //
  412. // Sort the output buffer using the Key.
  413. //
  414. if (PrintHighlights) {
  415. qsort (SortBuffer,
  416. SortBufferSize,
  417. sizeof (STRINGTOINTASSOCIATION),
  418. ValueCompareAssociation);
  419. } else {
  420. qsort (SortBuffer,
  421. SortBufferSize,
  422. sizeof (STRINGTOINTASSOCIATION),
  423. KeyCompareAssociation);
  424. }
  425. //
  426. // Dump the buffer.
  427. //
  428. for (SortBufferIndex = 0; SortBufferIndex < SortBufferSize; SortBufferIndex += 1) {
  429. if (PrintHighlights) {
  430. if (SortBuffer[SortBufferIndex].Value >= 1) {
  431. TCHAR TraceId[7];
  432. LPTSTR Start;
  433. _tprintf(_T("%d\t%s\n"),
  434. SortBuffer[SortBufferIndex].Value,
  435. SortBuffer[SortBufferIndex].Key);
  436. Start = _tcsstr (SortBuffer[SortBufferIndex].Key, "(");
  437. if (Start == NULL) {
  438. TraceId[0] = 0;
  439. } else {
  440. _tcsncpy (TraceId,
  441. Start,
  442. 6);
  443. TraceId[6] = 0;
  444. }
  445. _tprintf (_T("%s"), SearchStackTrace (AfterLogName, TraceId));
  446. }
  447. } else {
  448. _tprintf(_T("%d\t%s\n"),
  449. SortBuffer[SortBufferIndex].Value,
  450. SortBuffer[SortBufferIndex].Key);
  451. }
  452. }
  453. //
  454. // Clean up memory.
  455. //
  456. if (SortBuffer) {
  457. delete[] SortBuffer;
  458. }
  459. }
  460. VOID
  461. PrintUsage(
  462. VOID
  463. )
  464. /*++
  465. Routine Description:
  466. This routine prints out a message describing the proper usage of OHCMP.
  467. Arguments:
  468. None.
  469. Return value:
  470. None.
  471. --*/
  472. {
  473. _fputts (HelpText, stderr);
  474. }
  475. LONG _cdecl
  476. _tmain(
  477. IN LONG argc,
  478. IN LPTSTR argv[]
  479. )
  480. /*++
  481. Routine Description:
  482. This routine parses program arguments, reads the two input files, and prints out the
  483. differences.
  484. Arguments:
  485. argc - Number of command-line arguments.
  486. argv - Command-line arguments.
  487. Return value:
  488. 0 if comparison is successful, 1 otherwise.
  489. --*/
  490. {
  491. try {
  492. MAPSTRINGTOINT TypeMapBefore, TypeMapAfter;
  493. MAPSTRINGTOINT NameMapBefore, NameMapAfter;
  494. LPTSTR BeforeFileName=NULL;
  495. LPTSTR AfterFileName=NULL;
  496. BOOLEAN ReportIncreasesOnly = TRUE;
  497. BOOLEAN Interpreted = FALSE;
  498. BOOLEAN Result;
  499. BOOLEAN FileWithTraces;
  500. BOOLEAN PrintHighlights;
  501. // parse arguments
  502. FileWithTraces = FALSE;
  503. PrintHighlights = FALSE;
  504. for (LONG n = 1; n < argc; n++) {
  505. Interpreted = FALSE;
  506. switch (argv[n][0]) {
  507. case _T('-'):
  508. case _T('/'):
  509. // the argument is a switch
  510. if (_tcsicmp(argv[n]+1, _T("all")) == 0) {
  511. ReportIncreasesOnly = FALSE;
  512. Interpreted = TRUE;
  513. } else if (_tcsicmp(argv[n]+1, _T("t")) == 0) {
  514. FileWithTraces = TRUE;
  515. Interpreted = TRUE;
  516. } else if (_tcsicmp(argv[n]+1, _T("h")) == 0) {
  517. PrintHighlights = TRUE;
  518. Interpreted = TRUE;
  519. }
  520. break;
  521. default:
  522. // the argument is a file name
  523. if (BeforeFileName == NULL) {
  524. BeforeFileName = argv[n];
  525. Interpreted = TRUE;
  526. } else {
  527. if (AfterFileName == NULL) {
  528. AfterFileName = argv[n];
  529. Interpreted = TRUE;
  530. } else {
  531. // too many file arguments
  532. PrintUsage();
  533. return 1;
  534. }
  535. }
  536. break;
  537. }
  538. if (!Interpreted) {
  539. // user specified a bad argument
  540. PrintUsage();
  541. return 1;
  542. }
  543. }
  544. // did user specify required arguments?
  545. if ((BeforeFileName == NULL) || (AfterFileName == NULL)) {
  546. PrintUsage();
  547. return 1;
  548. }
  549. // read oh1 file
  550. Result = PopulateMapsFromFile (BeforeFileName,
  551. TypeMapBefore,
  552. NameMapBefore,
  553. FileWithTraces);
  554. if (Result == FALSE) {
  555. _ftprintf(stderr, _T("Failed to read first OH output file.\n"));
  556. return 1;
  557. }
  558. // read oh2 file
  559. Result = PopulateMapsFromFile (AfterFileName,
  560. TypeMapAfter,
  561. NameMapAfter,
  562. FileWithTraces);
  563. if (Result == FALSE) {
  564. _ftprintf(stderr, _T("Failed to read second OH output file.\n"));
  565. return 1;
  566. }
  567. // print out increases by handle name
  568. if (PrintHighlights) {
  569. _putts (TEXT ("\n")
  570. TEXT("// \n")
  571. TEXT("// Possible leaks (DELTA <PROCESS/PID/TYPE>::NAME): \n")
  572. TEXT("// \n")
  573. TEXT("// Note that the NAME can appear as `(TRACEID) NAME' if output \n")
  574. TEXT("// is generated by comparing OH files containing traces. In this case \n")
  575. TEXT("// just search in the `AFTER' OH log file for the trace id to \n")
  576. TEXT("// find the stack trace creating the handle possibly leaked. \n")
  577. TEXT("// \n\n"));
  578. PrintIncreases (NameMapBefore,
  579. NameMapAfter,
  580. ReportIncreasesOnly,
  581. TRUE,
  582. AfterFileName);
  583. }
  584. // print out increases by handle type
  585. _putts (TEXT ("\n")
  586. TEXT("// \n")
  587. TEXT("// Handle types (DELTA <PROCESS/PID/TYPE>): \n")
  588. TEXT("// \n")
  589. TEXT("// DELTA is the additional number of handles found in the `AFTER' log. \n")
  590. TEXT("// PROCESS is the process name having a handle increase. \n")
  591. TEXT("// PID is the process PID having a handle increase. \n")
  592. TEXT("// TYPE is the type of the handle \n")
  593. TEXT("// \n\n"));
  594. PrintIncreases (TypeMapBefore,
  595. TypeMapAfter,
  596. ReportIncreasesOnly,
  597. FALSE,
  598. NULL);
  599. // print out increases by handle name
  600. _putts (TEXT ("\n")
  601. TEXT("// \n")
  602. TEXT("// Objects (named and anonymous) (DELTA <PROCESS/PID/TYPE>::NAME): \n")
  603. TEXT("// \n")
  604. TEXT("// DELTA is the additional number of handles found in the `AFTER' log. \n")
  605. TEXT("// PROCESS is the process name having a handle increase. \n")
  606. TEXT("// PID is the process PID having a handle increase. \n")
  607. TEXT("// TYPE is the type of the handle \n")
  608. TEXT("// NAME is the name of the handle. Anonymous handles appear with name <<noname>>.\n")
  609. TEXT("// \n")
  610. TEXT("// Note that the NAME can appear as `(TRACEID) NAME' if output \n")
  611. TEXT("// is generated by comparing OH files containing traces. In this case \n")
  612. TEXT("// just search in the `AFTER' OH log file for the trace id to \n")
  613. TEXT("// find the stack trace creating the handle possibly leaked. \n")
  614. TEXT("// \n\n"));
  615. PrintIncreases (NameMapBefore,
  616. NameMapAfter,
  617. ReportIncreasesOnly,
  618. FALSE,
  619. NULL);
  620. return 0;
  621. } catch (...) {
  622. // this is mostly intended to catch out of memory conditions
  623. _tprintf(_T("\nAn exception has been detected. OHCMP aborted.\n"));
  624. return 1;
  625. }
  626. }
  627. /////////////////////////////////////////////////////////////////////
  628. /////////////////////////////////////////////////////////////////////
  629. /////////////////////////////////////////////////////////////////////
  630. TCHAR StackTraceBuffer [0x10000];
  631. LPTSTR
  632. SearchStackTrace (
  633. LPTSTR FileName,
  634. LPTSTR TraceId
  635. )
  636. {
  637. TCHAR LineBuffer[512];
  638. FILE *InputFile;
  639. ULONG spaceLeft;
  640. StackTraceBuffer[0] = 0;
  641. //
  642. // Open file.
  643. //
  644. InputFile = _tfopen(FileName, _T("rt"));
  645. if (InputFile == NULL) {
  646. _ftprintf(stderr, _T("Error opening oh file %s.\n"), FileName);
  647. return NULL;
  648. }
  649. //
  650. // Make sure the buffer is terminated
  651. //
  652. spaceLeft = sizeof(StackTraceBuffer) / sizeof(StackTraceBuffer[0]) - 1;
  653. StackTraceBuffer[spaceLeft] = 0;
  654. //
  655. // Loop through lines in oh output.
  656. //
  657. while (_fgetts(LineBuffer, sizeof(LineBuffer) / sizeof(TCHAR), InputFile)
  658. && !( feof(InputFile) || ferror(InputFile) ) ) {
  659. //
  660. // Skip line if it does not contain trace ID.
  661. //
  662. if (_tcsstr (LineBuffer, TraceId) == NULL) {
  663. continue;
  664. }
  665. //
  666. // We have got a trace ID. We need now to copy everything
  667. // to a trace buffer until we get a line containing a character
  668. // in column zero.
  669. //
  670. while (_fgetts(LineBuffer, sizeof(LineBuffer) / sizeof(TCHAR), InputFile)
  671. && !( feof(InputFile) || ferror(InputFile) ) ) {
  672. if (LineBuffer[0] == _T(' ') ||
  673. LineBuffer[0] == _T('\0') ||
  674. LineBuffer[0] == _T('\n') ||
  675. LineBuffer[0] == _T('\t')) {
  676. //
  677. // Make sure we have enough space left
  678. //
  679. if (spaceLeft < _tcslen(LineBuffer)) {
  680. break;
  681. } else {
  682. spaceLeft -= _tcslen(LineBuffer);
  683. _tcscat (StackTraceBuffer, LineBuffer);
  684. }
  685. } else {
  686. break;
  687. }
  688. }
  689. break;
  690. }
  691. //
  692. // Close file.
  693. fclose(InputFile);
  694. return StackTraceBuffer;
  695. }