Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

968 lines
30 KiB

  1. #include <assert.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <search.h>
  5. #include <string.h>
  6. #include <memory.h>
  7. #include <nt.h>
  8. #include <ntrtl.h>
  9. #include <nturtl.h>
  10. #include <windows.h>
  11. #include <..\ztools\inc\tools.h>
  12. #define MAX_PROCESSOR 16
  13. SYSTEM_BASIC_INFORMATION BasicInfo;
  14. SYSTEM_PERFORMANCE_INFORMATION SystemInfoStart;
  15. SYSTEM_PERFORMANCE_INFORMATION SystemInfoDone;
  16. SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ProcessorInfoStart[MAX_PROCESSOR];
  17. BOOLEAN IgnoreNonZeroExitCodes;
  18. BOOLEAN ForceSort;
  19. BOOLEAN RemoveKeys;
  20. LPSTR KeyNameToRemove;
  21. LONG KeyEntriesToTrim;
  22. BOOL WINAPI
  23. CtrlcHandler(
  24. ULONG CtrlType
  25. )
  26. {
  27. //
  28. // Ignore control C interrupts. Let child process deal with them
  29. // if it wants. If it doesn't then it will terminate and we will
  30. // get control and terminate ourselves
  31. //
  32. return TRUE;
  33. }
  34. void
  35. Usage( void )
  36. {
  37. fprintf( stderr, "Usage: TIMEIT [-f filename] [-a] [-c] [-i] [-d] [-s] [-t] [-k keyname | -r keyname] [commandline...]\n" );
  38. fprintf( stderr, "where: -f specifies the name of the database file where TIMEIT\n" );
  39. fprintf( stderr, " keeps a history of previous timings. Default is .\\timeit.dat\n" );
  40. fprintf( stderr, " -k specifies the keyname to use for this timing run\n" );
  41. fprintf( stderr, " -r specifies the keyname to remove from the database. If\n" );
  42. fprintf( stderr, " keyname is followed by a comma and a number then it will\n" );
  43. fprintf( stderr, " remove the slowest (positive number) or fastest (negative)\n" );
  44. fprintf( stderr, " times for that keyname.\n" );
  45. fprintf( stderr, " -a specifies that timeit should display average of all timings\n" );
  46. fprintf( stderr, " for the specified key.\n" );
  47. fprintf( stderr, " -i specifies to ignore non-zero return codes from program\n" );
  48. fprintf( stderr, " -d specifies to show detail for average\n" );
  49. fprintf( stderr, " -s specifies to suppress system wide counters\n" );
  50. fprintf( stderr, " -t specifies to tabular output\n" );
  51. fprintf( stderr, " -c specifies to force a resort of the data base\n" );
  52. exit( 1 );
  53. }
  54. typedef struct _TIMEIT_RECORD {
  55. UCHAR KeyName[ 24 ];
  56. ULONG GetVersionNumber;
  57. LARGE_INTEGER ExitTime;
  58. ULONG ExitCode;
  59. USHORT Flags;
  60. USHORT NumberOfRecordsInAverage;
  61. LARGE_INTEGER ElapsedTime;
  62. LARGE_INTEGER ProcessTime;
  63. ULONG SystemCalls;
  64. ULONG ContextSwitches;
  65. ULONG InterruptCount;
  66. ULONG PageFaultCount;
  67. LARGE_INTEGER IoReadTransferCount;
  68. LARGE_INTEGER IoWriteTransferCount;
  69. LARGE_INTEGER IoOtherTransferCount;
  70. } TIMEIT_RECORD, *PTIMEIT_RECORD;
  71. #define TIMEIT_RECORD_SYSTEMINFO 0x0001
  72. #define TIMEIT_VERSION 1
  73. typedef struct _TIMEIT_FILE {
  74. ULONG VersionNumber;
  75. ULONG NumberOfRecords;
  76. TIMEIT_RECORD Records[ 1 ];
  77. } TIMEIT_FILE, *PTIMEIT_FILE;
  78. void
  79. DisplayRecord(
  80. PTIMEIT_RECORD p,
  81. PTIMEIT_RECORD pavg
  82. );
  83. int
  84. __cdecl
  85. TimeitSortRoutine(
  86. const void *arg1,
  87. const void *arg2
  88. )
  89. {
  90. PTIMEIT_RECORD p1 = (PTIMEIT_RECORD)arg1;
  91. PTIMEIT_RECORD p2 = (PTIMEIT_RECORD)arg2;
  92. int cmp;
  93. cmp = _stricmp( p1->KeyName, p2->KeyName );
  94. if (cmp == 0) {
  95. if (p1->ElapsedTime.QuadPart < p2->ElapsedTime.QuadPart) {
  96. cmp = -1;
  97. }
  98. else
  99. if (p1->ElapsedTime.QuadPart > p2->ElapsedTime.QuadPart) {
  100. cmp = 1;
  101. }
  102. }
  103. return cmp;
  104. }
  105. void
  106. UnmapDataBase(
  107. PTIMEIT_FILE pf
  108. )
  109. {
  110. FlushViewOfFile( pf, 0 );
  111. UnmapViewOfFile( pf );
  112. }
  113. PTIMEIT_FILE
  114. MapDataBase(
  115. LPSTR DataBaseFileName,
  116. BOOLEAN WriteAccess,
  117. PTIMEIT_RECORD p,
  118. PULONG ActualNumberOfRecords
  119. )
  120. {
  121. DWORD BytesWritten;
  122. HANDLE hf, hm;
  123. TIMEIT_FILE Temp;
  124. PTIMEIT_FILE pf;
  125. ULONG i;
  126. BOOLEAN RecordsDeleted;
  127. DWORD FileSize;
  128. BOOLEAN DeleteRecord, BackupFlag;
  129. LONG KeyEntriesLeftToTrim;
  130. hf = CreateFile( DataBaseFileName,
  131. WriteAccess ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ,
  132. FILE_SHARE_READ,
  133. NULL,
  134. p != NULL ? OPEN_ALWAYS : OPEN_EXISTING,
  135. FILE_ATTRIBUTE_NORMAL,
  136. NULL
  137. );
  138. if (hf == INVALID_HANDLE_VALUE) {
  139. return NULL;
  140. }
  141. if (p != NULL && GetLastError() != ERROR_ALREADY_EXISTS) {
  142. Temp.VersionNumber = TIMEIT_VERSION;
  143. Temp.NumberOfRecords = 0;
  144. WriteFile( hf, &Temp, FIELD_OFFSET( TIMEIT_FILE, Records ), &BytesWritten, NULL );
  145. }
  146. FileSize = SetFilePointer( hf, 0, NULL, FILE_END );
  147. if (p != NULL) {
  148. if (!WriteFile( hf, p, sizeof( *p ), &BytesWritten, NULL )) {
  149. CloseHandle( hf );
  150. return NULL;
  151. }
  152. SetEndOfFile( hf );
  153. FileSize += BytesWritten;
  154. }
  155. retrymap:
  156. hm = CreateFileMapping( hf,
  157. NULL,
  158. WriteAccess ? PAGE_READWRITE | SEC_COMMIT : PAGE_READONLY,
  159. 0,
  160. 0,
  161. NULL
  162. );
  163. if (hm == NULL) {
  164. CloseHandle( hf );
  165. return NULL;
  166. }
  167. pf = MapViewOfFile( hm, WriteAccess ? FILE_MAP_WRITE : FILE_MAP_READ, 0, 0, 0 );
  168. if (pf != NULL) {
  169. if (p != NULL) {
  170. pf->NumberOfRecords += 1;
  171. }
  172. if (FileSize > FIELD_OFFSET( TIMEIT_FILE, Records )) {
  173. *ActualNumberOfRecords = (FileSize - FIELD_OFFSET( TIMEIT_FILE, Records )) / sizeof( TIMEIT_RECORD );
  174. }
  175. else {
  176. *ActualNumberOfRecords = 0;
  177. }
  178. if (WriteAccess && RemoveKeys && KeyNameToRemove != NULL) {
  179. if (pf->NumberOfRecords != *ActualNumberOfRecords) {
  180. pf->NumberOfRecords = *ActualNumberOfRecords;
  181. }
  182. KeyEntriesLeftToTrim = KeyEntriesToTrim;
  183. RecordsDeleted = FALSE;
  184. for (i=0; i<pf->NumberOfRecords; i++) {
  185. if ((*KeyNameToRemove == '\0'&& pf->Records[i].KeyName[0] == '\0') ||
  186. !_strnicmp( KeyNameToRemove, pf->Records[i].KeyName, 24 )) {
  187. DeleteRecord = FALSE;
  188. BackupFlag = FALSE;
  189. if (KeyEntriesToTrim) {
  190. if (KeyEntriesLeftToTrim < 0) {
  191. DeleteRecord = TRUE;
  192. KeyEntriesLeftToTrim += 1;
  193. }
  194. else
  195. if (KeyEntriesLeftToTrim > 0) {
  196. if ((i+1) == pf->NumberOfRecords ||
  197. _strnicmp( KeyNameToRemove, pf->Records[i+1].KeyName, 24 )
  198. ) {
  199. DeleteRecord = TRUE;
  200. KeyEntriesLeftToTrim -= 1;
  201. BackupFlag = TRUE;
  202. }
  203. }
  204. }
  205. else {
  206. DeleteRecord = TRUE;
  207. }
  208. if (DeleteRecord) {
  209. RecordsDeleted = TRUE;
  210. FileSize -= sizeof( TIMEIT_RECORD );
  211. if (i < --(pf->NumberOfRecords)) {
  212. memmove( &pf->Records[i],
  213. &pf->Records[i+1],
  214. (pf->NumberOfRecords - i + 1) * sizeof( TIMEIT_RECORD )
  215. );
  216. }
  217. i -= 1;
  218. if (BackupFlag) {
  219. i -= 1;
  220. }
  221. }
  222. }
  223. }
  224. if (RecordsDeleted) {
  225. RemoveKeys = FALSE;
  226. CloseHandle( hm );
  227. UnmapDataBase( pf );
  228. SetFilePointer( hf, FileSize, NULL, FILE_BEGIN );
  229. SetEndOfFile( hf );
  230. goto retrymap;
  231. }
  232. }
  233. if (WriteAccess) {
  234. qsort( pf->Records,
  235. pf->NumberOfRecords,
  236. sizeof( TIMEIT_RECORD ),
  237. TimeitSortRoutine
  238. );
  239. }
  240. }
  241. CloseHandle( hm );
  242. CloseHandle( hf );
  243. return pf;
  244. }
  245. void
  246. AccumulateRecord(
  247. PTIMEIT_RECORD pavg,
  248. PTIMEIT_RECORD p
  249. )
  250. {
  251. ULONG n;
  252. if (p != NULL) {
  253. if (!IgnoreNonZeroExitCodes || p->ExitCode != 0) {
  254. pavg->ElapsedTime.QuadPart += p->ElapsedTime.QuadPart;
  255. pavg->ProcessTime.QuadPart += p->ProcessTime.QuadPart;
  256. if (p->Flags & TIMEIT_RECORD_SYSTEMINFO) {
  257. pavg->Flags |= TIMEIT_RECORD_SYSTEMINFO;
  258. pavg->SystemCalls += p->SystemCalls;
  259. pavg->ContextSwitches += p->ContextSwitches;
  260. pavg->InterruptCount += p->InterruptCount;
  261. pavg->PageFaultCount += p->PageFaultCount;
  262. pavg->IoReadTransferCount.QuadPart += p->IoReadTransferCount.QuadPart;
  263. pavg->IoWriteTransferCount.QuadPart += p->IoWriteTransferCount.QuadPart;
  264. pavg->IoOtherTransferCount.QuadPart += p->IoOtherTransferCount.QuadPart;
  265. }
  266. pavg->NumberOfRecordsInAverage += 1;
  267. }
  268. }
  269. else {
  270. n = pavg->NumberOfRecordsInAverage;
  271. if (n > 1 && n != 0xFFFF) {
  272. pavg->ElapsedTime.QuadPart /= n;
  273. pavg->ProcessTime.QuadPart /= n;
  274. if (pavg->Flags & TIMEIT_RECORD_SYSTEMINFO) {
  275. pavg->SystemCalls /= n;
  276. pavg->ContextSwitches /= n;
  277. pavg->InterruptCount /= n;
  278. pavg->PageFaultCount /= n;
  279. pavg->IoReadTransferCount.QuadPart /= n;
  280. pavg->IoWriteTransferCount.QuadPart /= n;
  281. pavg->IoOtherTransferCount.QuadPart /= n;
  282. }
  283. }
  284. }
  285. return;
  286. }
  287. void
  288. UpdateDataBase(
  289. LPSTR DataBaseFileName,
  290. PTIMEIT_RECORD p,
  291. PTIMEIT_RECORD pavg
  292. )
  293. {
  294. PTIMEIT_FILE pf;
  295. PTIMEIT_RECORD p1;
  296. ULONG i, NumberOfRecords;
  297. pf = MapDataBase( DataBaseFileName, TRUE, p, &NumberOfRecords );
  298. if (pf == NULL) {
  299. fprintf( stderr, "Unable to access data base file '%s' (%u)\n",
  300. DataBaseFileName, GetLastError()
  301. );
  302. return;
  303. }
  304. if (pf->VersionNumber != TIMEIT_VERSION) {
  305. fprintf( stderr, "Invalid version number (%u) in data base file '%s'\n",
  306. pf->VersionNumber, DataBaseFileName
  307. );
  308. exit( 1 );
  309. }
  310. if (pavg != NULL) {
  311. memset( pavg, 0, sizeof( *pavg ) );
  312. pavg->GetVersionNumber = GetVersion();
  313. for (i=0; i<pf->NumberOfRecords; i++) {
  314. p1 = &pf->Records[ i ];
  315. if (!_stricmp( p1->KeyName, p->KeyName )) {
  316. AccumulateRecord( pavg, p1 );
  317. }
  318. }
  319. AccumulateRecord( pavg, NULL );
  320. }
  321. UnmapDataBase( pf );
  322. DisplayRecord( p, pavg );
  323. return;
  324. }
  325. char *PlatformStrings[] = {
  326. "Windows NT",
  327. "Win32s",
  328. "Windows 95",
  329. "unknown"
  330. };
  331. char *Days[] = {
  332. "Sunday",
  333. "Monday",
  334. "Tuesday",
  335. "Wednesday",
  336. "Thursday",
  337. "Friday",
  338. "Saturday"
  339. };
  340. char *Months[] = {
  341. "January",
  342. "February",
  343. "March",
  344. "April",
  345. "May",
  346. "June",
  347. "July",
  348. "August",
  349. "September",
  350. "October",
  351. "November",
  352. "December"
  353. };
  354. void
  355. DisplayRecord(
  356. PTIMEIT_RECORD p,
  357. PTIMEIT_RECORD pavg
  358. )
  359. {
  360. LARGE_INTEGER LocalExitTime;
  361. TIME_FIELDS ExitTime, ElapsedTime, ProcessTime;
  362. fprintf( stderr, "\n" );
  363. fprintf( stderr,
  364. "Version Number: %s %u.%u (Build %u)\n",
  365. PlatformStrings[ (p->GetVersionNumber >> 30) & 0x3 ],
  366. p->GetVersionNumber & 0xFF,
  367. (p->GetVersionNumber >> 8) & 0xFF,
  368. (p->GetVersionNumber >> 16) & 0x3FFF
  369. );
  370. if (pavg != NULL) {
  371. fprintf( stderr, "Average is for %u runs\n", pavg->NumberOfRecordsInAverage );
  372. }
  373. FileTimeToLocalFileTime( (LPFILETIME)&p->ExitTime, (LPFILETIME)&LocalExitTime );
  374. RtlTimeToTimeFields( &LocalExitTime, &ExitTime );
  375. fprintf( stderr,
  376. "Exit Time: %u:%02u %s, %s, %s %u %u\n",
  377. ExitTime.Hour > 12 ? ExitTime.Hour - 12 : ExitTime.Hour,
  378. ExitTime.Minute,
  379. ExitTime.Hour > 12 ? "pm" : "am",
  380. Days[ ExitTime.Weekday ],
  381. Months[ ExitTime.Month-1 ],
  382. ExitTime.Day,
  383. ExitTime.Year
  384. );
  385. RtlTimeToTimeFields( &p->ElapsedTime, &ElapsedTime );
  386. fprintf( stderr,
  387. "Elapsed Time: %u:%02u:%02u.%03u",
  388. ElapsedTime.Hour,
  389. ElapsedTime.Minute,
  390. ElapsedTime.Second,
  391. ElapsedTime.Milliseconds
  392. );
  393. if (pavg != NULL) {
  394. RtlTimeToTimeFields( &pavg->ElapsedTime, &ElapsedTime );
  395. fprintf( stderr,
  396. " Average: %u:%02u:%02u.%03u",
  397. ElapsedTime.Hour,
  398. ElapsedTime.Minute,
  399. ElapsedTime.Second,
  400. ElapsedTime.Milliseconds
  401. );
  402. }
  403. fprintf( stderr, "\n" );
  404. RtlTimeToTimeFields( &p->ProcessTime, &ProcessTime );
  405. fprintf( stderr,
  406. "Process Time: %u:%02u:%02u.%03u",
  407. ProcessTime.Hour,
  408. ProcessTime.Minute,
  409. ProcessTime.Second,
  410. ProcessTime.Milliseconds
  411. );
  412. if (pavg != NULL) {
  413. RtlTimeToTimeFields( &pavg->ProcessTime, &ProcessTime );
  414. fprintf( stderr,
  415. " Average: %u:%02u:%02u.%03u",
  416. ProcessTime.Hour,
  417. ProcessTime.Minute,
  418. ProcessTime.Second,
  419. ProcessTime.Milliseconds
  420. );
  421. }
  422. fprintf( stderr, "\n" );
  423. if (p->Flags & TIMEIT_RECORD_SYSTEMINFO) {
  424. fprintf( stderr, "System Calls: %-11u", p->SystemCalls );
  425. if (pavg != NULL) {
  426. fprintf( stderr, " Average: %u", pavg->SystemCalls );
  427. }
  428. fprintf( stderr, "\n" );
  429. fprintf( stderr, "Context Switches: %-11u", p->ContextSwitches );
  430. if (pavg != NULL) {
  431. fprintf( stderr, " Average: %u", pavg->ContextSwitches );
  432. }
  433. fprintf( stderr, "\n" );
  434. fprintf( stderr, "Page Faults: %-11u", p->PageFaultCount );
  435. if (pavg != NULL) {
  436. fprintf( stderr, " Average: %u", pavg->PageFaultCount );
  437. }
  438. fprintf( stderr, "\n" );
  439. fprintf( stderr, "Bytes Read: %-11u", p->IoReadTransferCount.QuadPart );
  440. if (pavg != NULL) {
  441. fprintf( stderr, " Average: %u", pavg->IoReadTransferCount.QuadPart );
  442. }
  443. fprintf( stderr, "\n" );
  444. fprintf( stderr, "Bytes Written: %-11u", p->IoWriteTransferCount.QuadPart );
  445. if (pavg != NULL) {
  446. fprintf( stderr, " Average: %u", pavg->IoReadTransferCount.QuadPart );
  447. }
  448. fprintf( stderr, "\n" );
  449. fprintf( stderr, "Bytes Other: %-11u", p->IoOtherTransferCount.QuadPart );
  450. if (pavg != NULL) {
  451. fprintf( stderr, " Average: %u", pavg->IoOtherTransferCount.QuadPart );
  452. }
  453. fprintf( stderr, "\n" );
  454. }
  455. }
  456. BOOLEAN DisplayDataBaseFirstTime;
  457. void
  458. DisplayDataBaseAverage(
  459. PTIMEIT_RECORD p,
  460. BOOLEAN ShowDetailForAverage,
  461. BOOLEAN TabularOutput
  462. )
  463. {
  464. LARGE_INTEGER LocalExitTime;
  465. TIME_FIELDS ExitTime, ElapsedTime, ProcessTime;
  466. AccumulateRecord( p, NULL );
  467. if (DisplayDataBaseFirstTime) {
  468. if (TabularOutput) {
  469. fprintf( stderr, "Runs Name Elapsed Time Process Time System Context Page Total I/O\n" );
  470. fprintf( stderr, " Calls Switches Faults\n" );
  471. }
  472. DisplayDataBaseFirstTime = FALSE;
  473. }
  474. else
  475. if (!TabularOutput) {
  476. fprintf( stderr, "\n\n" );
  477. }
  478. if (TabularOutput) {
  479. if (p->KeyName[0] == '\0') {
  480. FileTimeToLocalFileTime( (LPFILETIME)&p->ExitTime, (LPFILETIME)&LocalExitTime );
  481. RtlTimeToTimeFields( &LocalExitTime, &ExitTime );
  482. sprintf( p->KeyName,
  483. "%02u/%02u/%4u %02u:%02u %s",
  484. ExitTime.Month,
  485. ExitTime.Day,
  486. ExitTime.Year,
  487. ExitTime.Hour > 12 ? ExitTime.Hour - 12 : ExitTime.Hour,
  488. ExitTime.Minute,
  489. ExitTime.Hour > 12 ? "pm" : "am"
  490. );
  491. }
  492. if (p->NumberOfRecordsInAverage == 0) {
  493. fprintf( stderr, " " );
  494. }
  495. else
  496. if (p->NumberOfRecordsInAverage == 0xFFFF) {
  497. fprintf( stderr, "EXCL" );
  498. }
  499. else {
  500. fprintf( stderr, "%-4u", p->NumberOfRecordsInAverage );
  501. }
  502. RtlTimeToTimeFields( &p->ElapsedTime, &ElapsedTime );
  503. fprintf( stderr, " %-24s %3u:%02u:%02u.%03u",
  504. p->KeyName,
  505. ElapsedTime.Hour,
  506. ElapsedTime.Minute,
  507. ElapsedTime.Second,
  508. ElapsedTime.Milliseconds
  509. );
  510. RtlTimeToTimeFields( &p->ProcessTime, &ProcessTime );
  511. fprintf( stderr, " %3u:%02u:%02u.%03u",
  512. ProcessTime.Hour,
  513. ProcessTime.Minute,
  514. ProcessTime.Second,
  515. ProcessTime.Milliseconds
  516. );
  517. if (p->Flags & TIMEIT_RECORD_SYSTEMINFO) {
  518. fprintf( stderr, " %9u %9u %8u%12u",
  519. p->SystemCalls,
  520. p->ContextSwitches,
  521. p->PageFaultCount,
  522. p->IoReadTransferCount.QuadPart +
  523. p->IoWriteTransferCount.QuadPart +
  524. p->IoOtherTransferCount.QuadPart
  525. );
  526. }
  527. fprintf( stderr, "\n" );
  528. if (ShowDetailForAverage &&
  529. p->NumberOfRecordsInAverage != 0 &&
  530. p->NumberOfRecordsInAverage != 0xFFFF
  531. ) {
  532. fprintf( stderr, "\n" );
  533. }
  534. }
  535. else {
  536. if (p->NumberOfRecordsInAverage == 0) {
  537. fprintf( stderr, "Detail Record included in average below\n" );
  538. }
  539. else
  540. if (p->NumberOfRecordsInAverage == 0xFFFF) {
  541. fprintf( stderr, "Detail Record excluded from average below\n" );
  542. }
  543. else {
  544. fprintf( stderr,
  545. "Average for %s key over %u runs\n",
  546. p->KeyName,
  547. p->NumberOfRecordsInAverage
  548. );
  549. }
  550. DisplayRecord( p, NULL );
  551. }
  552. }
  553. void
  554. DisplayDataBase(
  555. LPSTR DataBaseFileName,
  556. LPSTR KeyName,
  557. BOOLEAN ShowDetailForAverage,
  558. BOOLEAN TabularOutput
  559. )
  560. {
  561. TIMEIT_RECORD Averages, Detail;
  562. PTIMEIT_RECORD p, pPrev, pNext;
  563. PTIMEIT_FILE pf;
  564. ULONG i, j;
  565. ULONG NumberOfRecords, NumberOfRecordsInGroup, CurrentRecordInGroup, NumberOfRecordsToTrim;
  566. pf = MapDataBase( DataBaseFileName,
  567. (BOOLEAN)(ForceSort | RemoveKeys),
  568. NULL,
  569. &NumberOfRecords
  570. );
  571. if (pf == NULL) {
  572. fprintf( stderr, "Unable to access data base file '%s' (%u)\n",
  573. DataBaseFileName, GetLastError()
  574. );
  575. exit( 1 );
  576. }
  577. if (pf->VersionNumber != TIMEIT_VERSION) {
  578. fprintf( stderr, "Invalid version number (%u) in data base file '%s'\n",
  579. pf->VersionNumber, DataBaseFileName
  580. );
  581. exit( 1 );
  582. }
  583. pPrev = NULL;
  584. DisplayDataBaseFirstTime = TRUE;
  585. for (i=0; i<NumberOfRecords; i++) {
  586. p = &pf->Records[ i ];
  587. if (i == 0 || _stricmp( p->KeyName, Averages.KeyName )) {
  588. if (i != 0 && (KeyName == NULL || !_stricmp( KeyName, Averages.KeyName ))) {
  589. DisplayDataBaseAverage( &Averages, ShowDetailForAverage, TabularOutput );
  590. }
  591. pNext = p+1;
  592. NumberOfRecordsInGroup = 1;
  593. for (j=i+1; j<NumberOfRecords; j++) {
  594. if (!_stricmp( p->KeyName, pNext->KeyName )) {
  595. NumberOfRecordsInGroup += 1;
  596. }
  597. else {
  598. break;
  599. }
  600. pNext += 1;
  601. }
  602. CurrentRecordInGroup = 0;
  603. NumberOfRecordsToTrim = NumberOfRecordsInGroup / 10;
  604. memset( &Averages, 0, sizeof( Averages ) );
  605. strcpy( Averages.KeyName, p->KeyName );
  606. Averages.GetVersionNumber = p->GetVersionNumber;
  607. }
  608. Detail = *p;
  609. Detail.KeyName[0] = '\0';
  610. CurrentRecordInGroup += 1;
  611. if (CurrentRecordInGroup <= NumberOfRecordsToTrim ||
  612. CurrentRecordInGroup > (NumberOfRecordsInGroup-NumberOfRecordsToTrim)
  613. ) {
  614. //
  615. // Ignore fastest and slowest records
  616. //
  617. Detail.NumberOfRecordsInAverage = 0xFFFF;
  618. }
  619. else {
  620. Detail.NumberOfRecordsInAverage = 0;
  621. }
  622. if (ShowDetailForAverage && (KeyName == NULL || !_stricmp( KeyName, p->KeyName ))) {
  623. DisplayDataBaseAverage( &Detail, ShowDetailForAverage, TabularOutput );
  624. }
  625. if (Detail.NumberOfRecordsInAverage != 0xFFFF) {
  626. AccumulateRecord( &Averages, p );
  627. }
  628. }
  629. if (i != 0 && (KeyName == NULL || !_stricmp( KeyName, Averages.KeyName ))) {
  630. DisplayDataBaseAverage( &Averages, ShowDetailForAverage, TabularOutput );
  631. }
  632. }
  633. int
  634. __cdecl
  635. main(
  636. int argc,
  637. char *argv[],
  638. char *envp[]
  639. )
  640. {
  641. LPSTR s, s1, KeyName, CommandLine, DataBaseFileName;
  642. BOOL b;
  643. NTSTATUS Status;
  644. STARTUPINFO StartupInfo;
  645. PROCESS_INFORMATION ProcessInformation;
  646. KERNEL_USER_TIMES Times;
  647. TIMEIT_RECORD t, Averages;
  648. BOOLEAN DisplayAverage;
  649. BOOLEAN ShowDetailForAverage;
  650. BOOLEAN TabularOutput;
  651. BOOLEAN SuppressSystemInfo;
  652. //
  653. // Console API's are OEM, so make it so for us as well
  654. //
  655. ConvertAppToOem( argc, argv );
  656. // printf( "sizeof( TIMEIT_RECORD ) == 0x%x\n", sizeof( TIMEIT_RECORD ) );
  657. IgnoreNonZeroExitCodes = FALSE;
  658. ShowDetailForAverage = FALSE;
  659. SuppressSystemInfo = FALSE;
  660. TabularOutput = FALSE;
  661. DisplayAverage = FALSE;
  662. DataBaseFileName = "timeit.dat";
  663. CommandLine = NULL;
  664. KeyName = NULL;
  665. RemoveKeys = FALSE;
  666. while (--argc) {
  667. s = *++argv;
  668. if (*s == '-') {
  669. while (*++s) {
  670. switch(tolower( *s )) {
  671. case 'd':
  672. ShowDetailForAverage = TRUE;
  673. break;
  674. case 's':
  675. SuppressSystemInfo = TRUE;
  676. break;
  677. case 't':
  678. TabularOutput = TRUE;
  679. break;
  680. case 'a':
  681. DisplayAverage = TRUE;
  682. break;
  683. case 'f':
  684. if (--argc) {
  685. DataBaseFileName = *++argv;
  686. }
  687. else {
  688. fprintf( stderr, "Missing parameter to -f switch\n" );
  689. Usage();
  690. }
  691. break;
  692. case 'i':
  693. IgnoreNonZeroExitCodes = TRUE;
  694. break;
  695. case 'k':
  696. if (--argc) {
  697. KeyName = *++argv;
  698. }
  699. else {
  700. fprintf( stderr, "Missing parameter to -k switch\n" );
  701. Usage();
  702. }
  703. break;
  704. case 'c':
  705. ForceSort = TRUE;
  706. break;
  707. case 'r':
  708. RemoveKeys = TRUE;
  709. if (--argc) {
  710. KeyNameToRemove = *++argv;
  711. if (s1 = strchr(KeyNameToRemove, ',')) {
  712. *s1++ = '\0';
  713. KeyEntriesToTrim = atoi(s1);
  714. }
  715. }
  716. else {
  717. fprintf( stderr, "Missing parameter to -r switch\n" );
  718. Usage();
  719. }
  720. break;
  721. default:
  722. fprintf( stderr, "Invalid switch -%c\n", *s );
  723. Usage();
  724. break;
  725. }
  726. }
  727. }
  728. else {
  729. if (KeyName == NULL) {
  730. KeyName = s;
  731. }
  732. CommandLine = GetCommandLine();
  733. while (*CommandLine && *CommandLine <= ' ') {
  734. CommandLine += 1;
  735. }
  736. while (*CommandLine && *CommandLine > ' ') {
  737. CommandLine += 1;
  738. }
  739. CommandLine = strstr( CommandLine, s );
  740. break;
  741. }
  742. }
  743. if (CommandLine == NULL) {
  744. DisplayDataBase( DataBaseFileName, KeyName, ShowDetailForAverage, TabularOutput );
  745. exit( 0 );
  746. }
  747. memset( &StartupInfo,0,sizeof( StartupInfo ) );
  748. StartupInfo.cb = sizeof(StartupInfo);
  749. Status = NtQuerySystemInformation( SystemBasicInformation,
  750. &BasicInfo,
  751. sizeof( BasicInfo ),
  752. NULL
  753. );
  754. if (!NT_SUCCESS( Status )) {
  755. fprintf( stderr, "Unable to query basic system information (%x)\n", Status );
  756. exit( RtlNtStatusToDosError( Status ) );
  757. }
  758. if (!SuppressSystemInfo) {
  759. Status = NtQuerySystemInformation( SystemPerformanceInformation,
  760. (PVOID)&SystemInfoStart,
  761. sizeof( SystemInfoStart ),
  762. NULL
  763. );
  764. if (!NT_SUCCESS( Status )) {
  765. fprintf( stderr, "Unable to query system performance data (%x)\n", Status );
  766. exit( RtlNtStatusToDosError( Status ) );
  767. }
  768. }
  769. if (!CreateProcess( NULL,
  770. CommandLine,
  771. NULL,
  772. NULL,
  773. TRUE,
  774. 0,
  775. NULL,
  776. NULL,
  777. &StartupInfo,
  778. &ProcessInformation
  779. )
  780. ) {
  781. fprintf( stderr,
  782. "CreateProcess( '%s' ) failed (%u)\n",
  783. CommandLine,
  784. GetLastError()
  785. );
  786. exit( GetLastError() );
  787. }
  788. SetConsoleCtrlHandler( CtrlcHandler, TRUE );
  789. WaitForSingleObject( ProcessInformation.hProcess, INFINITE );
  790. Status = NtQueryInformationProcess( ProcessInformation.hProcess,
  791. ProcessTimes,
  792. &Times,
  793. sizeof( Times ),
  794. NULL
  795. );
  796. if (!NT_SUCCESS( Status )) {
  797. fprintf( stderr, "Unable to query process times (%x)\n", Status );
  798. exit( RtlNtStatusToDosError( Status ) );
  799. }
  800. if (!SuppressSystemInfo) {
  801. Status = NtQuerySystemInformation( SystemPerformanceInformation,
  802. (PVOID)&SystemInfoDone,
  803. sizeof( SystemInfoDone ),
  804. NULL
  805. );
  806. if (!NT_SUCCESS( Status )) {
  807. fprintf( stderr, "Unable to query system performance data (%x)\n", Status );
  808. exit( RtlNtStatusToDosError( Status ) );
  809. }
  810. }
  811. memset( &t, 0, sizeof( t ) );
  812. strcpy( t.KeyName, KeyName );
  813. t.GetVersionNumber = GetVersion();
  814. GetExitCodeProcess( ProcessInformation.hProcess, &t.ExitCode );
  815. t.ExitTime.QuadPart = Times.ExitTime.QuadPart;
  816. t.ElapsedTime.QuadPart = Times.ExitTime.QuadPart - Times.CreateTime.QuadPart;
  817. // total process time ...
  818. t.ProcessTime.QuadPart = Times.KernelTime.QuadPart + Times.UserTime.QuadPart;
  819. if (!SuppressSystemInfo) {
  820. t.SystemCalls = SystemInfoDone.SystemCalls - SystemInfoStart.SystemCalls;
  821. t.ContextSwitches = SystemInfoDone.ContextSwitches - SystemInfoStart.ContextSwitches;
  822. t.PageFaultCount = SystemInfoDone.PageFaultCount - SystemInfoStart.PageFaultCount;
  823. t.IoReadTransferCount.QuadPart = SystemInfoDone.IoReadTransferCount.QuadPart -
  824. SystemInfoStart.IoReadTransferCount.QuadPart;
  825. t.IoReadTransferCount.QuadPart += (SystemInfoDone.PageReadCount -
  826. SystemInfoStart.PageReadCount) * BasicInfo.PageSize;
  827. t.IoReadTransferCount.QuadPart += (SystemInfoDone.CacheReadCount -
  828. SystemInfoStart.CacheReadCount) * BasicInfo.PageSize;
  829. t.IoWriteTransferCount.QuadPart = SystemInfoDone.IoWriteTransferCount.QuadPart -
  830. SystemInfoStart.IoWriteTransferCount.QuadPart;
  831. t.IoWriteTransferCount.QuadPart += (SystemInfoDone.DirtyPagesWriteCount -
  832. SystemInfoStart.DirtyPagesWriteCount) * BasicInfo.PageSize;
  833. t.IoWriteTransferCount.QuadPart += (SystemInfoDone.MappedPagesWriteCount -
  834. SystemInfoStart.MappedPagesWriteCount) * BasicInfo.PageSize;
  835. t.IoOtherTransferCount.QuadPart = SystemInfoDone.IoOtherTransferCount.QuadPart -
  836. SystemInfoStart.IoOtherTransferCount.QuadPart;
  837. t.Flags |= TIMEIT_RECORD_SYSTEMINFO;
  838. }
  839. UpdateDataBase( DataBaseFileName, &t, DisplayAverage ? &Averages : NULL );
  840. exit( t.ExitCode );
  841. return 0;
  842. }