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.

691 lines
22 KiB

  1. /*
  2. * Title: analog.c - main file for log analyzer
  3. *
  4. * Description: This file is a tool to analyze sorted memsnap and poolsnap log
  5. * files. It reads in the log files and records each of the
  6. * fields for each process or tag. It then does a trend analysis
  7. * of each field. If any field increases every period, it reports
  8. * a definite leak. If the difference of increase count and
  9. * decrease count for any field is more than half the periods, it
  10. * reports a probable leak.
  11. *
  12. * Functions:
  13. *
  14. * Usage Prints usage message
  15. * DetermineFileType Determines type of log file (mem/pool) & longest entry
  16. * AnalyzeMemLog Reads and analyzes sorted memsnap log
  17. * AnalyzePoolLog Reads and analyzes sorted poolsnap log
  18. * AnalyzeFile Opens file, determines type and calls analysis function
  19. * main Loops on each command arg and calls AnalyzeFile
  20. *
  21. * Copyright (c) 1998-1999 Microsoft Corporation
  22. *
  23. * ToDo:
  24. * 1. Way to ignore some of the periods at the beginning.
  25. * 2. Exceptions file to ignore tags or processes.
  26. * 3. Pick up comments from file and print them as notes.
  27. * *4. switch to just show definites.
  28. * 5. Output computername, build number,checked/free, arch. etc
  29. * 6. option to ignore process that weren't around the whole time
  30. *
  31. * Revision history: LarsOp 12/8/1998 - Created
  32. * ChrisW 3/22/1999 - HTML, Calculate rates
  33. *
  34. */
  35. #include <windows.h>
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include "analog.h"
  40. #include "htmprint.c" // all the HTML procs and variables
  41. INT g_iMaxPeriods=0; // Global for max periods
  42. BOOL g_fVerbose=FALSE; // Global verbosity for deltas on memlogs
  43. BOOL g_fShowExtraInfo=FALSE; // If true, show computer names, and comments
  44. DWORD g_dwElapseTickCount=0; // Total elapse time for these logs
  45. CHAR* g_pszComputerName=NULL; // name of computer the log file came from
  46. CHAR* g_pszBuildNumber=NULL; // build number
  47. CHAR* g_pszBuildType=NULL; // build type (retail/debug)
  48. CHAR* g_pszSystemTime=NULL; // last time
  49. CHAR* g_pszComments=NULL;
  50. INT g_ReportLevel=9; // 0= only definite, 9=all inclusive
  51. #define TAGCHAR '!' /* character that starts tag line */
  52. /*
  53. * Usage prints the usage message.
  54. */
  55. void AnalogUsage()
  56. {
  57. printf("Usage: AnaLog [-v] [-h] [-t] [-d] <file1> [<file2>] [<file3>] [...]\n");
  58. printf(" **no wild card support yet**\n\n");
  59. printf("AnaLog will analyze SortLog output of MemSnap or PoolSnap files.\n\n");
  60. printf("-v Print deltas>%d%% for all processes to be written to stderr\n", PERCENT_TO_PRINT);
  61. printf("-h Produce HTML tables\n");
  62. printf("-t Show Extra info like computer name, and comments\n");
  63. printf("-d Show only definite leaks\n");
  64. printf("\n");
  65. printf("Definite leak means that the value increased every period.\n");
  66. printf("Probable leak means that it increased more than half the periods.\n" );
  67. }
  68. DWORD Trick( LONG amount, DWORD ticks )
  69. {
  70. _int64 temp;
  71. temp= amount;
  72. temp= temp * 3600;
  73. temp= temp * 1000;
  74. temp= temp/(ticks);
  75. return( (DWORD) temp );
  76. }
  77. // GetLocalString
  78. //
  79. // Allocate a heap block and copy string into it.
  80. //
  81. // return: pointer to heap block
  82. //
  83. CHAR* AllocateLocalString( CHAR* pszString )
  84. {
  85. INT len;
  86. CHAR* pszTemp;
  87. len= strlen( pszString ) + 1;
  88. pszTemp= (CHAR*) LocalAlloc( LPTR, len );
  89. if( !pszTemp ) return NULL;
  90. strcpy( pszTemp, pszString );
  91. return( pszTemp );
  92. }
  93. /*
  94. * ProcessTag
  95. *
  96. * Args: char* - pointer to something like 'tag=value'
  97. *
  98. * return: nothing (but may set global variables)
  99. *
  100. */
  101. #define BREAKSYM "<BR>"
  102. VOID AnalogProcessTag( CHAR* pBuffer )
  103. {
  104. CHAR* pszTagName;
  105. CHAR* pszEqual;
  106. CHAR* pszValue;
  107. INT len;
  108. // eliminate trailing newline
  109. len= strlen( pBuffer );
  110. if( len ) {
  111. if( pBuffer[len-1] == '\n' ) {
  112. pBuffer[len-1]= 0;
  113. }
  114. }
  115. pszTagName= pBuffer;
  116. pszEqual= pBuffer;
  117. while( *pszEqual && (*pszEqual != '=' ) ) {
  118. pszEqual++;
  119. }
  120. if( !*pszEqual ) {
  121. return;
  122. }
  123. *pszEqual= 0; // zero terminate the tag name
  124. pszValue= pszEqual+1;
  125. if( _stricmp( pszTagName, "elapsetickcount" ) == 0 ) {
  126. g_dwElapseTickCount= atol( pszValue );
  127. }
  128. else if( _stricmp( pszTagName, "computername" ) == 0 ) {
  129. g_pszComputerName= AllocateLocalString( pszValue );
  130. }
  131. else if( _stricmp( pszTagName, "buildnumber" ) == 0 ) {
  132. g_pszBuildNumber= AllocateLocalString( pszValue );
  133. }
  134. else if( _stricmp( pszTagName, "buildtype" ) == 0 ) {
  135. g_pszBuildType= AllocateLocalString( pszValue );
  136. }
  137. else if( _stricmp( pszTagName, "systemtime" ) == 0 ) {
  138. g_pszSystemTime= AllocateLocalString( pszValue );
  139. }
  140. else if( _stricmp( pszTagName, "logtype" ) == 0 ) {
  141. // just ignore
  142. }
  143. else {
  144. INT buflen;
  145. CHAR* pBuf;
  146. BOOL bIgnoreTag= FALSE;
  147. if( _stricmp(pszTagName,"comment")==0 ) {
  148. bIgnoreTag=TRUE;
  149. }
  150. if( g_pszComments == NULL ) {
  151. buflen= strlen(pszTagName) + 1 + strlen(pszValue) + 1 +1;
  152. pBuf= (CHAR*) LocalAlloc( LPTR, buflen );
  153. if( pBuf ) {
  154. if( bIgnoreTag ) {
  155. sprintf(pBuf,"%s\n",pszValue);
  156. }
  157. else {
  158. sprintf(pBuf,"%s %s\n",pszTagName,pszValue);
  159. }
  160. g_pszComments= pBuf;
  161. }
  162. }
  163. else {
  164. buflen= strlen(g_pszComments)+strlen(pszTagName)+1+strlen(pszValue)+sizeof(BREAKSYM)+1 +1;
  165. pBuf= (CHAR*) LocalAlloc( LPTR, buflen );
  166. if( pBuf ) {
  167. if( bIgnoreTag ) {
  168. sprintf(pBuf,"%s%s%s\n",g_pszComments,BREAKSYM,pszValue);
  169. }
  170. else {
  171. sprintf(pBuf,"%s%s%s=%s\n",g_pszComments,BREAKSYM,pszTagName,pszValue);
  172. }
  173. LocalFree( g_pszComments );
  174. g_pszComments= pBuf;
  175. }
  176. }
  177. }
  178. }
  179. /*
  180. * DetermineFileType
  181. *
  182. * Args: pFile - File pointer to check
  183. *
  184. * Returns: The type of log of given file. UNKNOWN_LOG_TYPE is the error return.
  185. *
  186. * This function scans the file to determine the log type (based on the first
  187. * word) and the maximum number of lines for any process or tag.
  188. *
  189. */
  190. LogType DetermineFileType(FILE *pFile)
  191. {
  192. char buffer[BUF_LEN]; // buffer for reading lines
  193. char idstring[BUF_LEN]; // ident string (1st word of 1st line)
  194. LogType retval=UNKNOWN_LOG_TYPE;// return value (default to error case)
  195. fpos_t savedFilePosition; // file pos to reset after computing max
  196. int iTemp; // temporary used for computing max entries
  197. int iStatus;
  198. //
  199. // Read the first string of the first line to identify the type
  200. //
  201. if (fgets(buffer, BUF_LEN, pFile)) {
  202. iStatus= sscanf(buffer, "%s", idstring);
  203. if( iStatus == 0 ) {
  204. return UNKNOWN_LOG_TYPE;
  205. }
  206. if (0==_strcmpi(idstring, "Tag")) {
  207. retval=POOL_LOG;
  208. } else if (0==_strcmpi(idstring, "Process")) {
  209. retval=MEM_LOG;
  210. } else {
  211. return UNKNOWN_LOG_TYPE;
  212. }
  213. } else {
  214. return UNKNOWN_LOG_TYPE;
  215. }
  216. //
  217. // Save the position to reset after counting the number of polling periods
  218. //
  219. fgetpos(pFile, &savedFilePosition);
  220. //
  221. // Loop until you get a blank line or end of file
  222. //
  223. g_iMaxPeriods=0;
  224. while (TRUE) {
  225. iTemp=0;
  226. while (TRUE) {
  227. //
  228. // Blank line actually has length 1 for LF character.
  229. //
  230. if( (NULL==fgets(buffer, BUF_LEN, pFile)) ||
  231. (*buffer == TAGCHAR ) ||
  232. (strlen(buffer)<2)) {
  233. break;
  234. }
  235. iTemp++;
  236. }
  237. g_iMaxPeriods=MAX(g_iMaxPeriods, iTemp);
  238. if( *buffer == TAGCHAR ) {
  239. AnalogProcessTag( buffer+1 );
  240. }
  241. if (feof(pFile)) {
  242. break;
  243. }
  244. }
  245. //
  246. // Reset position to first record for reading/analyzing data
  247. //
  248. (void) fsetpos(pFile, &savedFilePosition);
  249. return retval;
  250. }
  251. /*
  252. * AnalyzeMemLog
  253. *
  254. * Args: pointer to sorted memsnap log file
  255. *
  256. * Returns: nothing
  257. *
  258. * This function reads a sorted memsnap logfile. For each process in the file,
  259. * it records each column for every period and then analyzes the memory trends
  260. * for leaks.
  261. *
  262. * If any column increases for each period, that is flagged as a definite leak.
  263. * If any column increases significatnly more often than decrease, it is a
  264. * flagged as a probable leak.
  265. *
  266. */
  267. void AnalyzeMemLog(FILE *pFile)
  268. {
  269. int iPeriod; // index for which period being read
  270. MemLogRec Delta; // Record to track increase from first to last entry
  271. MemLogRec TrendInfo; // Record to track period increases
  272. MemLogRec* pLogArray; // Array of records for each process
  273. char buffer[BUF_LEN]; // Buffer for reading each line from pFile
  274. //
  275. // Allocate enough space for the largest set
  276. //
  277. pLogArray=(MemLogRec *) malloc(g_iMaxPeriods*sizeof(MemLogRec));
  278. if (NULL==pLogArray) {
  279. fprintf(stderr,"Out of memory, aborting file.\n");
  280. return;
  281. }
  282. PRINT_HEADER();
  283. //
  284. // Read the entire file
  285. //
  286. while( !feof(pFile) ) {
  287. //
  288. // Reset trend and period info for each new process
  289. //
  290. memset(&TrendInfo, 0, sizeof(TrendInfo));
  291. iPeriod=0;
  292. //
  293. // Loop until you've read all the entries for this process or tag.
  294. //
  295. // Note: Empty line includes LF character that fgets doesn't eat.
  296. //
  297. while (TRUE) {
  298. if( iPeriod >= g_iMaxPeriods ) break; // done
  299. if ((NULL==fgets(buffer, BUF_LEN, pFile)) ||
  300. (strlen(buffer)<2) ||
  301. (*buffer == TAGCHAR) ||
  302. (0==sscanf(buffer,
  303. "%lx %s %ld %ld %ld %ld %ld %ld %ld",
  304. &pLogArray[iPeriod].Pid,
  305. pLogArray[iPeriod].Name,
  306. &pLogArray[iPeriod].WorkingSet,
  307. &pLogArray[iPeriod].PagedPool,
  308. &pLogArray[iPeriod].NonPagedPool,
  309. &pLogArray[iPeriod].PageFile,
  310. &pLogArray[iPeriod].Commit,
  311. &pLogArray[iPeriod].Handles,
  312. &pLogArray[iPeriod].Threads))) {
  313. break;
  314. }
  315. //
  316. // Calculate TrendInfo:
  317. //
  318. // TrendInfo is a running tally of the periods a value went up vs.
  319. // the periods it went down. See macro in analog.h
  320. //
  321. // if (curval>oldval) {
  322. // trend++;
  323. // } else if (curval<oldval) {
  324. // trend--;
  325. // } else {
  326. // trend=trend; // stay same
  327. // }
  328. //
  329. if (iPeriod>0) {
  330. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, WorkingSet);
  331. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, PagedPool);
  332. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, NonPagedPool);
  333. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, PageFile);
  334. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, Commit);
  335. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, Handles);
  336. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, Threads);
  337. }
  338. iPeriod++;
  339. }
  340. if (iPeriod>1) {
  341. //
  342. // GET_DELTA simply records the difference (end-begin) for each field
  343. //
  344. // Macro in analog.h
  345. //
  346. GET_DELTA(Delta, pLogArray, iPeriod, WorkingSet);
  347. GET_DELTA(Delta, pLogArray, iPeriod, PagedPool);
  348. GET_DELTA(Delta, pLogArray, iPeriod, NonPagedPool);
  349. GET_DELTA(Delta, pLogArray, iPeriod, PageFile);
  350. GET_DELTA(Delta, pLogArray, iPeriod, Commit);
  351. GET_DELTA(Delta, pLogArray, iPeriod, Handles);
  352. GET_DELTA(Delta, pLogArray, iPeriod, Threads);
  353. //
  354. // PRINT_IF_TREND reports probable or definite leaks for any field.
  355. //
  356. // Definite leak is where the value goes up every period
  357. // Probable leak is where the value goes up most of the time
  358. //
  359. // Macro in analog.h
  360. //
  361. // if (trend==numperiods-1) {
  362. // definite_leak;
  363. // } else if (trend>=numperiods/2) {
  364. // probable_leak;
  365. // }
  366. //
  367. // PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, WorkingSet);
  368. PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, PagedPool);
  369. PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, NonPagedPool);
  370. // PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, PageFile);
  371. PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, Commit);
  372. PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, Handles);
  373. PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, Threads);
  374. if (g_fVerbose && ANY_PERCENT_GREATER(Delta, pLogArray)) {
  375. printf("%-12s:WS=%4ld%% PP=%4ld%% NP=%4ld%% "
  376. "PF=%4ld%% C=%4ld%% H=%4ld%% T=%4ld%%\n",
  377. pLogArray[0].Name,
  378. PERCENT(Delta.WorkingSet , pLogArray[0].WorkingSet ),
  379. PERCENT(Delta.PagedPool , pLogArray[0].PagedPool ),
  380. PERCENT(Delta.NonPagedPool, pLogArray[0].NonPagedPool),
  381. PERCENT(Delta.PageFile , pLogArray[0].PageFile ),
  382. PERCENT(Delta.Commit , pLogArray[0].Commit ),
  383. PERCENT(Delta.Handles , pLogArray[0].Handles ),
  384. PERCENT(Delta.Threads , pLogArray[0].Threads ));
  385. }
  386. }
  387. }
  388. PRINT_TRAILER();
  389. if (pLogArray) {
  390. free (pLogArray);
  391. }
  392. }
  393. /*
  394. * AnalyzePoolLog
  395. *
  396. * Args: pointer to sorted poolsnap log file
  397. *
  398. * Returns: nothing
  399. *
  400. * This function reads a sorted poolsnap logfile. For each pool tag in the file,
  401. * it records each column for every period and then analyzes the memory trends
  402. * for leaks.
  403. *
  404. * If any column increases for each period, that is flagged as a definite leak.
  405. * If any column increases significatnly more often than decrease, it is a
  406. * flagged as a probable leak.
  407. *
  408. */
  409. void AnalyzePoolLog(FILE *pFile)
  410. {
  411. int iPeriod; // index for which period being read
  412. PoolLogRec Delta, // Record to track increase from first to last entry
  413. TrendInfo, // Record to track period increases
  414. *pLogArray;// Array of records for each pool tag
  415. char buffer[BUF_LEN]; // Buffer for reading each line from pFile
  416. //
  417. // Allocate enough space for the largest set
  418. //
  419. pLogArray=(PoolLogRec *) malloc(g_iMaxPeriods*sizeof(PoolLogRec));
  420. if (NULL==pLogArray) {
  421. fprintf(stderr,"Out of memory, aborting file.\n");
  422. return;
  423. }
  424. PRINT_HEADER();
  425. //
  426. // Read the entire file
  427. //
  428. while( !feof(pFile) ) {
  429. //
  430. // Reset trend and period info for each new pool tag
  431. //
  432. memset(&TrendInfo, 0, sizeof(TrendInfo));
  433. iPeriod=0;
  434. //
  435. // Loop until you've read all the entries for this process or tag.
  436. //
  437. // Note: Empty line includes LF character that fgets doesn't eat.
  438. //
  439. while( TRUE ) {
  440. if( iPeriod >= g_iMaxPeriods ) break; // done
  441. if ((NULL==fgets(buffer, BUF_LEN, pFile)) ||
  442. (strlen(buffer)<2) ||
  443. (*buffer == TAGCHAR ) ||
  444. (0==sscanf(buffer,
  445. " %4c %s %ld %ld %ld %ld %ld",
  446. pLogArray[iPeriod].Name,
  447. pLogArray[iPeriod].Type,
  448. &pLogArray[iPeriod].Allocs,
  449. &pLogArray[iPeriod].Frees,
  450. &pLogArray[iPeriod].Diff,
  451. &pLogArray[iPeriod].Bytes,
  452. &pLogArray[iPeriod].PerAlloc))) {
  453. break;
  454. }
  455. pLogArray[iPeriod].Name[4]='\0'; // Terminate the tag
  456. //
  457. // Calculate TrendInfo:
  458. //
  459. // TrendInfo is a running tally of the periods a value went up vs.
  460. // the periods it went down. See macro in analog.h
  461. //
  462. // if (curval>oldval) {
  463. // trend++;
  464. // } else if (curval<oldval) {
  465. // trend--;
  466. // } else {
  467. // trend=trend; // stay same
  468. // }
  469. //
  470. if (iPeriod>0) {
  471. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, Allocs);
  472. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, Frees);
  473. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, Diff);
  474. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, Bytes);
  475. GREATER_LESS_OR_EQUAL(TrendInfo, pLogArray, iPeriod, PerAlloc);
  476. }
  477. iPeriod++;
  478. }
  479. //
  480. // skip rest of loop if a blank line or useless line
  481. //
  482. if( iPeriod == 0 ) continue;
  483. strcpy(TrendInfo.Name,pLogArray[0].Name);
  484. //
  485. // GET_DELTA simply records the difference (end-begin) for each field
  486. //
  487. // Macro in analog.h
  488. //
  489. GET_DELTA(Delta, pLogArray, iPeriod, Allocs);
  490. GET_DELTA(Delta, pLogArray, iPeriod, Frees);
  491. GET_DELTA(Delta, pLogArray, iPeriod, Diff);
  492. GET_DELTA(Delta, pLogArray, iPeriod, Bytes);
  493. GET_DELTA(Delta, pLogArray, iPeriod, PerAlloc);
  494. //
  495. // PRINT_IF_TREND reports probable or definite leaks for any field.
  496. //
  497. // Definite leak is where the value goes up every period
  498. // Probable leak is where the value goes up most of the time
  499. //
  500. // Macro in analog.h
  501. //
  502. // if (trend==numperiods-1) {
  503. // definite_leak;
  504. // } else if (trend>=numperiods/2) {
  505. // probable_leak;
  506. // }
  507. //
  508. // Note: Allocs, Frees and PerAlloc don't make sense to report trends.
  509. //
  510. // PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, Allocs);
  511. // PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, Frees);
  512. // PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, PerAlloc);
  513. // PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, Diff);
  514. PRINT_IF_TREND(pLogArray, TrendInfo, Delta, iPeriod, Bytes);
  515. }
  516. PRINT_TRAILER();
  517. if (pLogArray) {
  518. free (pLogArray);
  519. }
  520. }
  521. /*
  522. * AnalyzeFile
  523. *
  524. * Args: pFileName - filename to analyze
  525. *
  526. * Returns: nothing
  527. *
  528. * This function opens the specified file, determines the file type and calls
  529. * the appropriate analyze function.
  530. *
  531. */
  532. void AnalyzeFile(char *pFileName)
  533. {
  534. FILE *pFile; // using fopen for fgets functionality
  535. LogType WhichType=UNKNOWN_LOG_TYPE; // which type of log (pool/mem)
  536. pFile=fopen(pFileName, "r");
  537. if (NULL==pFile) {
  538. fprintf(stderr,"Unable to open %s, Error=%d\n", pFileName, GetLastError());
  539. return;
  540. }
  541. WhichType=DetermineFileType(pFile);
  542. switch (WhichType)
  543. {
  544. case MEM_LOG:
  545. AnalyzeMemLog(pFile);
  546. break;
  547. case POOL_LOG:
  548. AnalyzePoolLog(pFile);
  549. break;
  550. default:
  551. ;
  552. }
  553. fclose(pFile);
  554. }
  555. /*
  556. * main
  557. *
  558. * Args: argc - count of command line args
  559. * argv - array of command line args
  560. *
  561. * Returns: 0 if called correctly, 1 if not.
  562. *
  563. * This is the entry point for analog. It simply parses the command line args
  564. * and then calls AnalyzeFile on each file.
  565. *
  566. */
  567. #if defined(ANALOG_INCLUDED)
  568. int __cdecl AnalogMain (int argc, char* argv[])
  569. #else
  570. int __cdecl main (int argc, char* argv[])
  571. #endif
  572. {
  573. int ArgIndex;
  574. if (argc<2) {
  575. AnalogUsage();
  576. return 1;
  577. }
  578. for( ArgIndex=1; ArgIndex<argc; ArgIndex++) {
  579. if( (*argv[ArgIndex] == '/') || (*argv[ArgIndex]=='-') ) {
  580. CHAR chr;
  581. chr= argv[ArgIndex][1];
  582. switch( chr ) {
  583. case 'v': case 'V': // verbose
  584. g_fVerbose= TRUE;
  585. break;
  586. case 'h': case 'H': // output HTML
  587. bHtmlStyle= TRUE;
  588. break;
  589. case 't': case 'T': // show all the extra info
  590. g_fShowExtraInfo=TRUE;
  591. break;
  592. case 'd': case 'D': // print definite only
  593. g_ReportLevel= 0;
  594. break;
  595. default:
  596. AnalogUsage();
  597. break;
  598. }
  599. }
  600. else {
  601. AnalyzeFile(argv[ArgIndex]);
  602. }
  603. }
  604. return 0;
  605. }