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.

1053 lines
31 KiB

  1. // Copyright (c) 1994 - 1996 Microsoft Corporation. All Rights Reserved.
  2. #pragma warning(disable: 4201 4514)
  3. /*
  4. It is very easy to fill large amounts of storage with data
  5. (e.g. 5 filters in a graph,
  6. 5 interesting start/stop times per sample each = 10 events
  7. 30 samples per second
  8. 16 bytes per sample (8 bytes time, 8 bytes to identify incident)
  9. = 24KB/sec)
  10. this means that even a quite large buffer (I have in mind 64KB) will
  11. overflow after a few seconds. Writing it out to a file is completely out
  12. (don't want to take page faults in the middle of things - even writing to
  13. memory carries some risk).
  14. I want to have two kinds of data available at the end of the run:
  15. 1. A record of what actually happened (i.e. what the sequence of events
  16. actually was for at least a few frames)
  17. 2. Statistical information (e.g. for inter-frame video times I would like
  18. to see number, average, standard deviation, greatest, smallest.
  19. The volume of information that the actual sequence of events generates means
  20. that it may only hold a second or two of information. The statistical
  21. information should take in the whole run. This means that information will
  22. be logged two ways.
  23. For the detailed record I log
  24. <incident, type, time>
  25. in a circular buffer and regularly over-write the oldest information.
  26. For the statistical information I record the informatin in an array, where
  27. the incident identifier is the index. the array elements hold
  28. <Number of readings, sum, sum of squares, largest, smallest, latest>
  29. The readings are differences (start..next or start..stop). This means that
  30. the actual number of Noted events will be one more than the number of
  31. "readings", whereas for Start-Stop events they will be equal. To fix this,
  32. the number of readings is articifially initialised to -1. If Start sees
  33. this number it resets it to 0.
  34. Times will be in tens of microseconds (this allows up to about 1 3/4 hrs)
  35. The statistics array will have room for up to 128 types of incident (this is
  36. 4K - i.e. one page. I hope this will ensure that it never gets
  37. paged out and so causes negligible overhead.
  38. */
  39. #include <Windows.h> // BOOL etc
  40. #include <limits.h> // for INTT_MAX
  41. #include <math.h> // for sqrt
  42. #include <stdio.h> // for sprintf
  43. #include "Measure.h"
  44. #include "Perf.h" // ultra fast QueryPerformanceCounter for pentium
  45. // forwards
  46. enum {START, STOP, NOTE, RESET, INTGR, PAUSE, RUN}; // values for Type field
  47. typedef struct {
  48. LONGLONG Time; // microsec since class construction
  49. int Id;
  50. int Type;
  51. int n; // the integer for Msr_Integer
  52. } LogDatum;
  53. typedef struct {
  54. LONGLONG Latest; // microsec since class construction
  55. LONGLONG SumSq; // sum of squares of entries for this incident
  56. int Largest; // tenmicrosec
  57. int Smallest; // tenmicrosec
  58. int Sum; // sum of entries for this incident
  59. int Number; // number of entries for this incident
  60. // for Start/Stop it counts the stops
  61. // for Note it counts the intervals (Number of Notes-1)
  62. int iType; // STOP, NOTE, INTGR
  63. } Stat;
  64. #define MAXLOG 4096
  65. static BOOL bInitOk; // Set to true once initialised
  66. static LogDatum Log[MAXLOG]; // 64K circular buffer
  67. static int NextLog; // Next slot to overwrite in the log buffer.
  68. static BOOL bFull; // TRUE => buffer has wrapped at least once.
  69. static BOOL bPaused; // TRUE => do not record. No log, no stats.
  70. #define MAXSTAT 128
  71. static Stat StatBuffer[MAXSTAT];
  72. static int NextStat; // next free slot in StatBuffer.
  73. static LPTSTR Incidents[MAXSTAT];// Names of incidents
  74. static LONGLONG QPFreq;
  75. static LONGLONG QPStart; // base time in perf counts
  76. #ifdef DEBUG
  77. static LONGLONG tLast; // last time - looks for going backwards
  78. #endif
  79. static CRITICAL_SECTION CSMeasure; // Controls access to list
  80. // set it to 100000 for 10 microsecs
  81. // if you fiddle with it then you have to rewrite Format.
  82. #define UNIT 100000
  83. // Times are printed as 9 digits - this means that we can go
  84. // up to 9,999.999,99 secs or about 2 and 3/4 hours.
  85. // ASSERT(condition, msg) e.g. ASSERT(x>1, "Too many xs");
  86. #define ASSERT(_cond_, _msg_) \
  87. if (!(_cond_)) Assert(_msg_, __FILE__, __LINE__)
  88. // print out debug message box
  89. void Assert( const CHAR *pText
  90. , const CHAR *pFile
  91. , INT iLine
  92. )
  93. {
  94. CHAR Buffer[200];
  95. sprintf(Buffer, "%s\nAt line %d file %s"
  96. , pText, iLine, pFile);
  97. INT MsgId = MessageBox( NULL, Buffer, TEXT("ASSERT Failed")
  98. , MB_SYSTEMMODAL |MB_ICONHAND |MB_ABORTRETRYIGNORE);
  99. switch (MsgId)
  100. {
  101. case IDABORT: /* Kill the application */
  102. FatalAppExit(FALSE, TEXT("Application terminated"));
  103. break;
  104. case IDRETRY: /* Break into the debugger */
  105. DebugBreak();
  106. break;
  107. case IDIGNORE: /* Ignore assertion continue executing */
  108. break;
  109. }
  110. } // Assert
  111. //=============================================================================
  112. //
  113. // Init
  114. //
  115. // Call this first.
  116. //=============================================================================
  117. void WINAPI Msr_Init()
  118. {
  119. // I would like this to be idempotent - that is, harmless if it
  120. // gets called more than once. However that's not 100% possible
  121. // At least we should be OK so long as it's not re-entered.
  122. if (!bInitOk) {
  123. bInitOk = TRUE;
  124. InitializeCriticalSection(&CSMeasure);
  125. NextLog = 0;
  126. bFull = FALSE;
  127. NextStat = 0;
  128. LARGE_INTEGER li;
  129. QUERY_PERFORMANCE_FREQUENCY(&li);
  130. QPFreq = li.QuadPart;
  131. QUERY_PERFORMANCE_COUNTER(&li);
  132. QPStart = li.QuadPart;
  133. #ifdef DEBUG
  134. tLast = 0L;
  135. #endif
  136. Msr_Register("Scratch pad");
  137. }
  138. } // Msr_Init "constructor"
  139. //=============================================================================
  140. //
  141. // ResetAll
  142. //
  143. // Do a Reset on every Incident that has been registered.
  144. //=============================================================================
  145. void WINAPI ResetAll()
  146. {
  147. EnterCriticalSection(&CSMeasure);
  148. int i;
  149. for (i = 0; i<NextStat; ++i) {
  150. Msr_Reset(i);
  151. }
  152. LeaveCriticalSection(&CSMeasure);
  153. } // ResetAll
  154. //=============================================================================
  155. //
  156. // Pause
  157. //
  158. // Pause it all
  159. //=============================================================================
  160. void Pause()
  161. {
  162. if (!bInitOk) Msr_Init();
  163. EnterCriticalSection(&CSMeasure);
  164. bPaused = TRUE;
  165. // log a PAUSE event for this id.
  166. LARGE_INTEGER Time;
  167. QUERY_PERFORMANCE_COUNTER(&Time);
  168. // Get time in 10muSec from start - this gets the number small
  169. // it's OK for nearly 6 hrs then an int overflows
  170. LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
  171. Log[NextLog].Time = Tim;
  172. Log[NextLog].Type = PAUSE;
  173. Log[NextLog].Id = -1;
  174. ++NextLog;
  175. if (NextLog==MAXLOG) {
  176. NextLog = 0;
  177. bFull = TRUE;
  178. }
  179. LeaveCriticalSection(&CSMeasure);
  180. } // Pause
  181. //=============================================================================
  182. //
  183. // Run
  184. //
  185. // Set it all running again
  186. //=============================================================================
  187. void Run()
  188. {
  189. if (!bInitOk) Msr_Init();
  190. EnterCriticalSection(&CSMeasure);
  191. bPaused = FALSE;
  192. // log a RUN event for this id.
  193. LARGE_INTEGER Time;
  194. QUERY_PERFORMANCE_COUNTER(&Time);
  195. // Get time in 10muSec from start - this gets the number small
  196. // it's OK for nearly 6 hrs then an int overflows
  197. LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
  198. Log[NextLog].Time = Tim;
  199. Log[NextLog].Type = RUN;
  200. Log[NextLog].Id = -1;
  201. ++NextLog;
  202. if (NextLog==MAXLOG) {
  203. NextLog = 0;
  204. bFull = TRUE;
  205. }
  206. LeaveCriticalSection(&CSMeasure);
  207. } // Run
  208. //=============================================================================
  209. //
  210. // Msr_Control
  211. //
  212. // Do ResetAll, set bPaused to FALSE or TRUE according to iAction
  213. //=============================================================================
  214. void WINAPI Msr_Control(int iAction)
  215. {
  216. switch (iAction) {
  217. case MSR_RESET_ALL:
  218. ResetAll();
  219. break;
  220. case MSR_RUN:
  221. Run();
  222. break;
  223. case MSR_PAUSE:
  224. Pause();
  225. break;
  226. }
  227. } // Msr_Control
  228. //=============================================================================
  229. //
  230. // Terminate
  231. //
  232. // Call this last. It frees storage for names of incidents.
  233. //=============================================================================
  234. void WINAPI Msr_Terminate()
  235. {
  236. int i;
  237. if (bInitOk) {
  238. EnterCriticalSection(&CSMeasure);
  239. for (i = 0; i<NextStat; ++i) {
  240. free(Incidents[i]);
  241. }
  242. bInitOk = FALSE;
  243. LeaveCriticalSection(&CSMeasure);
  244. DeleteCriticalSection(&CSMeasure);
  245. }
  246. } // Msr_Terminate "~Measure"
  247. //=============================================================================
  248. //
  249. // InitIncident
  250. //
  251. // Reset the statistical counters for this incident.
  252. //=============================================================================
  253. void InitIncident(int Id)
  254. {
  255. StatBuffer[Id].Latest = -1; // recogniseably odd (see STOP)
  256. StatBuffer[Id].Largest = 0;
  257. StatBuffer[Id].Smallest = INT_MAX;
  258. StatBuffer[Id].Sum = 0;
  259. StatBuffer[Id].SumSq = 0;
  260. StatBuffer[Id].Number = -1;
  261. StatBuffer[Id].iType = NOTE; // reset on first Start for Start/Stop
  262. // reset on first Integer for INTGR
  263. } // InitIncident
  264. //=============================================================================
  265. //
  266. // Register
  267. //
  268. // Register a new kind of incident. The id that is returned can then be used
  269. // on calls to Start, Stop and Note to record the occurrences of these incidents
  270. // so that statistical performance information can be dumped later.
  271. //=============================================================================
  272. int Msr_Register(LPTSTR Incident)
  273. {
  274. if (!bInitOk) {
  275. Msr_Init();
  276. }
  277. // it's now safe to enter the critical section as it will be there!
  278. EnterCriticalSection(&CSMeasure);
  279. int i;
  280. for (i = 0; i<NextStat; ++i) {
  281. if (0==strcmp(Incidents[i],Incident) ) {
  282. // Attempting to re-register the same name.
  283. // Possible actions
  284. // 1. ASSERT - that just causes trouble.
  285. // 2. Register it as a new incident. That produced quartz bug 1
  286. // 3. Hand the old number back and reset it.
  287. // Msr_Reset(i); - possible, but not today.
  288. // 4. Hand the old number back and just keep going.
  289. LeaveCriticalSection(&CSMeasure);
  290. return i;
  291. }
  292. }
  293. if (NextStat==MAXSTAT-1) {
  294. Assert("Too many types of incident\n(ignore is safe)", __FILE__, __LINE__);
  295. LeaveCriticalSection(&CSMeasure);
  296. return -1;
  297. }
  298. Incidents[NextStat] = (LPTSTR)malloc(strlen(Incident)+1);
  299. strcpy(Incidents[NextStat], Incident);
  300. InitIncident(NextStat);
  301. LeaveCriticalSection(&CSMeasure);
  302. return NextStat++;
  303. } // Msr_Register
  304. //=============================================================================
  305. //
  306. // Reset
  307. //
  308. // Reset the statistical counters for this incident.
  309. // Log that we did it.
  310. //=============================================================================
  311. void WINAPI Msr_Reset(int Id)
  312. {
  313. if (!bInitOk) {
  314. Msr_Init();
  315. }
  316. // it's now safe to enter the critical section as it will be there!
  317. EnterCriticalSection(&CSMeasure);
  318. // log a RESET event for this id.
  319. LARGE_INTEGER Time;
  320. QUERY_PERFORMANCE_COUNTER(&Time);
  321. // Get time in 10muSec from start - this gets the number small
  322. // it's OK for nearly 6 hrs then an int overflows
  323. LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
  324. Log[NextLog].Time = Tim;
  325. Log[NextLog].Type = RESET;
  326. Log[NextLog].Id = Id;
  327. ++NextLog;
  328. if (NextLog==MAXLOG) {
  329. NextLog = 0;
  330. bFull = TRUE;
  331. }
  332. InitIncident(Id);
  333. LeaveCriticalSection(&CSMeasure);
  334. } // Msr_Reset
  335. //=============================================================================
  336. //
  337. // Msr_Start
  338. //
  339. // Record the start time of the event with registered id Id.
  340. // Add it to the circular Log and record the time in StatBuffer.
  341. // Do not update the statistical information, that happens when Stop is called.
  342. //=============================================================================
  343. void WINAPI Msr_Start(int Id)
  344. {
  345. if (bPaused) return;
  346. // This is performance critical. Keep all array subscripting
  347. // together and with any luck the compiler will only calculate the
  348. // offset once. Avoid subroutine calls unless they are definitely inline.
  349. // An id of -1 is the standard rotten registration one.
  350. // We already did an assert for that - so let it go.
  351. if (Id<-1 || Id>=NextStat) {
  352. // ASSERT(!"Performance logging with bad Id");
  353. return;
  354. }
  355. EnterCriticalSection(&CSMeasure);
  356. LARGE_INTEGER Time;
  357. QUERY_PERFORMANCE_COUNTER(&Time);
  358. LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
  359. #ifdef DEBUG
  360. ASSERT(Tim>=tLast, "Time is going backwards!!"); tLast = Tim;
  361. #endif
  362. Log[NextLog].Time = Tim;
  363. Log[NextLog].Type = START;
  364. Log[NextLog].Id = Id;
  365. ++NextLog;
  366. if (NextLog==MAXLOG) {
  367. NextLog = 0;
  368. bFull = TRUE;
  369. }
  370. StatBuffer[Id].Latest = Tim;
  371. if (StatBuffer[Id].Number == -1) {
  372. StatBuffer[Id].Number = 0;
  373. StatBuffer[Id].iType = STOP;
  374. }
  375. LeaveCriticalSection(&CSMeasure);
  376. } // Msr_Start
  377. //=============================================================================
  378. //
  379. // Msr_Stop
  380. //
  381. // Record the stop time of the event with registered id Id.
  382. // Add it to the circular Log and
  383. // add (StopTime-StartTime) to the statistical record StatBuffer.
  384. //=============================================================================
  385. void WINAPI Msr_Stop(int Id)
  386. {
  387. if (bPaused) return;
  388. // This is performance critical. Keep all array subscripting
  389. // together and with any luck the compiler will only calculate the
  390. // offset once. Avoid subroutine calls unless they are definitely inline.
  391. EnterCriticalSection(&CSMeasure);
  392. LARGE_INTEGER Time;
  393. QUERY_PERFORMANCE_COUNTER(&Time);
  394. // An id of -1 is the standard rotten registration one.
  395. // We already did an assert for that - so let it go.
  396. if (Id<-1 || Id>=NextStat) {
  397. // ASSERT(!"Performance logging with bad Id");
  398. return;
  399. }
  400. // Get time in 10muSec from start - this gets the number small
  401. // it's OK for nearly 6 hrs, then an int overflows
  402. LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
  403. #ifdef DEBUG
  404. ASSERT(Tim>=tLast, "Time is going backwards!!"); tLast = Tim;
  405. #endif
  406. Log[NextLog].Time = Tim;
  407. Log[NextLog].Type = STOP;
  408. Log[NextLog].Id = Id;
  409. ++NextLog;
  410. if (NextLog==MAXLOG) {
  411. NextLog = 0;
  412. bFull = TRUE;
  413. }
  414. if (StatBuffer[Id].Latest!=-1) {
  415. int t = (int)(Tim - StatBuffer[Id].Latest); // convert to delta
  416. // this is now OK for almost 6hrs since the last Start of this quantity.
  417. if (t > StatBuffer[Id].Largest) StatBuffer[Id].Largest = t;
  418. if (t < StatBuffer[Id].Smallest) StatBuffer[Id].Smallest = t;
  419. StatBuffer[Id].Sum += t;
  420. LONGLONG lt = t;
  421. StatBuffer[Id].SumSq += lt*lt;
  422. ++StatBuffer[Id].Number;
  423. }
  424. LeaveCriticalSection(&CSMeasure);
  425. } // Msr_Stop
  426. //=============================================================================
  427. //
  428. // Msr_Note
  429. //
  430. // Record the event with registered id Id. Add it to the circular Log and
  431. // add (ThisTime-PreviousTime) to the statistical record StatBuffer
  432. //=============================================================================
  433. void WINAPI Msr_Note(int Id)
  434. {
  435. if (bPaused) return;
  436. // This is performance critical. Keep all array subscripting
  437. // together and with any luck the compiler will only calculate the
  438. // offset once. Avoid subroutine calls unless they are definitely inline.
  439. // An id of -1 is the standard rotten registration one.
  440. // We already did an assert for that - so let it go.
  441. if (Id<-1 || Id>=NextStat) {
  442. // ASSERT(!"Performance logging with bad Id");
  443. return;
  444. }
  445. EnterCriticalSection(&CSMeasure);
  446. LARGE_INTEGER Time;
  447. QUERY_PERFORMANCE_COUNTER(&Time);
  448. // Get time in 10muSec from start - this gets the number small
  449. // it's OK for nearly 6 hrs then an int overflows
  450. LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
  451. #ifdef DEBUG
  452. ASSERT(Tim>=tLast, "Time is going backwards!!"); tLast = Tim;
  453. #endif
  454. Log[NextLog].Time = Tim;
  455. Log[NextLog].Type = NOTE;
  456. Log[NextLog].Id = Id;
  457. ++NextLog;
  458. if (NextLog==MAXLOG) {
  459. NextLog = 0;
  460. bFull = TRUE;
  461. }
  462. int t = (int)(Tim - StatBuffer[Id].Latest); // convert to delta
  463. // this is now OK for nearly 6 hrs since the last Note of this quantity.
  464. StatBuffer[Id].Latest = Tim;
  465. ++StatBuffer[Id].Number;
  466. if (StatBuffer[Id].Number>0) {
  467. if (t > StatBuffer[Id].Largest) StatBuffer[Id].Largest = t;
  468. if (t < StatBuffer[Id].Smallest) StatBuffer[Id].Smallest = t;
  469. StatBuffer[Id].Sum += (int)t;
  470. LONGLONG lt = t;
  471. StatBuffer[Id].SumSq += lt*lt;
  472. }
  473. LeaveCriticalSection(&CSMeasure);
  474. } // Msr_Note
  475. //=============================================================================
  476. //
  477. // Msr_Integer
  478. //
  479. // Record the event with registered id Id. Add it to the circular Log and
  480. // add (ThisTime-PreviousTime) to the statistical record StatBuffer
  481. //=============================================================================
  482. void WINAPI Msr_Integer(int Id, int n)
  483. {
  484. if (bPaused) return;
  485. // This is performance critical. Keep all array subscripting
  486. // together and with any luck the compiler will only calculate the
  487. // offset once. Avoid subroutine calls unless they are definitely inline.
  488. // An id of -1 is the standard rotten registration one.
  489. // We already did an assert for that - so let it go.
  490. if (Id<-1 || Id>=NextStat) {
  491. // ASSERT(!"Performance logging with bad Id");
  492. return;
  493. }
  494. EnterCriticalSection(&CSMeasure);
  495. LARGE_INTEGER Time;
  496. QUERY_PERFORMANCE_COUNTER(&Time);
  497. // Get time in 10muSec from start - this gets the number small
  498. // it's OK for nearly 6 hrs then an int overflows
  499. LONGLONG Tim = (Time.QuadPart-QPStart) * UNIT / QPFreq;
  500. #ifdef DEBUG
  501. ASSERT(Tim>=tLast, "Time is going backwards!!"); tLast = Tim;
  502. #endif
  503. Log[NextLog].Time = Tim;
  504. Log[NextLog].Type = INTGR;
  505. Log[NextLog].Id = Id;
  506. Log[NextLog].n = n;
  507. ++NextLog;
  508. if (NextLog==MAXLOG) {
  509. NextLog = 0;
  510. bFull = TRUE;
  511. }
  512. // StatBuffer[Id].Latest = garbage for Intgr
  513. if (StatBuffer[Id].Number == -1) {
  514. StatBuffer[Id].Number = 0;
  515. StatBuffer[Id].iType = INTGR;
  516. }
  517. ++StatBuffer[Id].Number;
  518. if (n > StatBuffer[Id].Largest) StatBuffer[Id].Largest = n;
  519. if (n < StatBuffer[Id].Smallest) StatBuffer[Id].Smallest = n;
  520. StatBuffer[Id].Sum += (int)n;
  521. LONGLONG ln = n;
  522. StatBuffer[Id].SumSq += ln*ln;
  523. LeaveCriticalSection(&CSMeasure);
  524. } // Msr_Integer
  525. //=============================================================================
  526. //
  527. // TypeName
  528. //
  529. // Convert the type code into readable format
  530. //=============================================================================
  531. const LPTSTR TypeName(int Type)
  532. {
  533. switch(Type){
  534. case START: return "START";
  535. case STOP: return "STOP ";
  536. case NOTE: return "NOTE ";
  537. case RESET: return "RESET";
  538. case INTGR: return "INTGR";
  539. case PAUSE: return "PAUSE";
  540. case RUN: return "RUN ";
  541. default: return "DUNNO";
  542. }
  543. } // TypeName
  544. //==============================================================================
  545. //
  546. // Format
  547. //
  548. // I haven't found any way to get sprintf to format integers as
  549. // 1,234.567.89 - so this does it. (that's 12 spaces)
  550. // All times are in tens of microsecs - so they are formatted as
  551. // n,nnn.mmm,mm - this uses 12 spaces.
  552. // The result that it returns points to Buff - it doesn't allocate any storage
  553. // i must be positive. Negative numbers are not handled (the pain of the floating
  554. // minus sign is the reason - i.e. " -12,345" not "- 12,345"
  555. //==============================================================================
  556. LPTSTR Format( LPTSTR Buff, int i)
  557. {
  558. if (i<0) {
  559. sprintf(Buff, " -. ");
  560. return Buff;
  561. }
  562. BOOL bStarted; // TRUE means that some left part of the number has been
  563. // formatted and so we must continue with zeros not spaces
  564. if (i>999999999) {
  565. sprintf(Buff, " ***large***");
  566. return Buff;
  567. }
  568. if (i>99999999) {
  569. sprintf(Buff, "%1d,", i/100000000);
  570. i = i%100000000;
  571. bStarted = TRUE;
  572. } else {
  573. sprintf(Buff, " ");
  574. bStarted = FALSE;
  575. }
  576. if (bStarted) {
  577. sprintf(Buff, "%s%03d.", Buff, i/100000);
  578. i = i%100000;
  579. } else {
  580. sprintf(Buff, "%s%3d.", Buff,i/100000);
  581. i = i%100000;
  582. }
  583. sprintf(Buff, "%s%03d,%02d", Buff, i/100, i%100);
  584. return Buff;
  585. } // Format
  586. //=============================================================================
  587. //
  588. // WriteOut
  589. //
  590. // If hFile==NULL then write str to debug output, else write it to file hFile
  591. //
  592. //=============================================================================
  593. void WriteOut(HANDLE hFile, LPSTR str)
  594. {
  595. if (hFile==NULL) {
  596. OutputDebugString(str);
  597. } else {
  598. DWORD dw;
  599. WriteFile(hFile, str, lstrlen(str), &dw, NULL);
  600. }
  601. } // WriteOut
  602. typedef LONGLONG longlongarray[MAXSTAT];
  603. //=============================================================================
  604. //
  605. // WriteLogEntry
  606. //
  607. // If hFile==NULL then write to debug output, else write to file hFile
  608. // write the ith entry of Log in a readable format
  609. //
  610. //=============================================================================
  611. void WriteLogEntry(HANDLE hFile, int i, longlongarray &Prev)
  612. {
  613. // We have the problem of printing LONGLONGs and wsprintf (26/6/95)
  614. // doesn't like them - found out the hard way - Laurie.
  615. char Buffer[200];
  616. char s1[20];
  617. char s2[20];
  618. int Delta; // time since previous interesting incident
  619. switch(Log[i].Type) {
  620. case START:
  621. Prev[Log[i].Id] = Log[i].Time;
  622. Delta = -2;
  623. sprintf( Buffer, "%s %5s %s : %s\r\n"
  624. , Format(s1,(int)(Log[i].Time))
  625. , TypeName(Log[i].Type)
  626. , Format(s2, Delta)
  627. , Incidents[Log[i].Id]
  628. );
  629. break;
  630. case STOP:
  631. if (Prev[Log[i].Id]==-1) {
  632. Delta = -2;
  633. } else {
  634. Delta = (int)(Log[i].Time - Prev[Log[i].Id]);
  635. }
  636. Prev[Log[i].Id] = -1;
  637. sprintf( Buffer, "%s %5s %s : %s\r\n"
  638. , Format(s1,(int)(Log[i].Time))
  639. , TypeName(Log[i].Type)
  640. , Format(s2, Delta)
  641. , Incidents[Log[i].Id]
  642. );
  643. break;
  644. case NOTE:
  645. if (Prev[Log[i].Id]==-1) {
  646. Delta = -2;
  647. } else {
  648. Delta = (int)(Log[i].Time - Prev[Log[i].Id]);
  649. }
  650. Prev[Log[i].Id] = Log[i].Time;
  651. sprintf( Buffer, "%s %5s %s : %s\r\n"
  652. , Format(s1,(int)(Log[i].Time))
  653. , TypeName(Log[i].Type)
  654. , Format(s2, Delta)
  655. , Incidents[Log[i].Id]
  656. );
  657. break;
  658. case INTGR:
  659. sprintf( Buffer, "%s %5s %12d : %s\r\n"
  660. , Format(s1,(int)(Log[i].Time))
  661. , TypeName(Log[i].Type)
  662. , Log[i].n
  663. , Incidents[Log[i].Id]
  664. );
  665. break;
  666. case RESET: // the delta for a reset will be length of run
  667. case PAUSE:
  668. case RUN:
  669. if ((Log[i].Id==-1)||(Prev[Log[i].Id]==-1)) {
  670. Delta = (int)(Log[i].Time); // = time from start
  671. } else {
  672. Delta = (int)(Log[i].Time - Prev[Log[i].Id]);
  673. }
  674. if (Log[i].Id!=-1) Prev[Log[i].Id] = Log[i].Time;
  675. sprintf( Buffer, "%s %5s %s : %s\r\n"
  676. , Format(s1,(int)(Log[i].Time))
  677. , TypeName(Log[i].Type)
  678. , Format(s2, Delta)
  679. , Incidents[Log[i].Id]
  680. );
  681. break;
  682. }
  683. WriteOut(hFile, Buffer);
  684. } // WriteLogEntry
  685. //=============================================================================
  686. //
  687. // WriteLog
  688. //
  689. // Write the whole of Log out in readable format.
  690. // If hFile==NULL then write to debug output, else write to hFile.
  691. //=============================================================================
  692. void WriteLog(HANDLE hFile)
  693. {
  694. //LONGLONG Prev[MAXSTAT]; // previous values found in log
  695. longlongarray Prev;
  696. char Buffer[100];
  697. sprintf(Buffer, " Time (sec) Type Delta Incident_Name\r\n");
  698. WriteOut(hFile, Buffer);
  699. int i;
  700. // initialise Prev to recognisable odd values
  701. for (i = 0; i<MAXSTAT; ++i) {
  702. Prev[i] = -1;
  703. }
  704. if (bFull) {
  705. for(i = NextLog; i<MAXLOG; ++i) {
  706. WriteLogEntry(hFile, i, Prev);
  707. }
  708. }
  709. for(i = 0; i<NextLog; ++i) {
  710. WriteLogEntry(hFile, i, Prev);
  711. }
  712. } // WriteLog
  713. //=============================================================================
  714. //
  715. // WriteStats
  716. //
  717. // Write the whole of StatBuffer out in readable format.
  718. // If hFile==NULL then write to DbgLog, else write to hFile.
  719. //=============================================================================
  720. void WriteStats(HANDLE hFile)
  721. {
  722. char Buffer[200];
  723. char s1[20];
  724. char s2[20];
  725. char s3[20];
  726. char s4[20];
  727. sprintf( Buffer
  728. , "Number Average StdDev Smallest Largest Incident_Name\r\n"
  729. );
  730. WriteOut(hFile, Buffer);
  731. int i;
  732. for (i = 0; i<NextStat; ++i) {
  733. if (i==0 && StatBuffer[i].Number==0) {
  734. continue; // no temp scribbles to report
  735. }
  736. double SumSq = (double)StatBuffer[i].SumSq;
  737. double Sum = StatBuffer[i].Sum;
  738. if (StatBuffer[i].iType==INTGR) {
  739. double Average;
  740. if (StatBuffer[i].Number<=0) {
  741. Average = 0;
  742. } else {
  743. Average = (double)StatBuffer[i].Sum / (double)StatBuffer[i].Number;
  744. }
  745. double Std;
  746. if (StatBuffer[i].Number<=1) Std = 0.0;
  747. Std = sqrt( ( (double)SumSq
  748. - ( (double)(Sum * Sum)
  749. / (double)StatBuffer[i].Number
  750. )
  751. )
  752. / ((double)StatBuffer[i].Number-1.0)
  753. );
  754. sprintf( Buffer
  755. , "%6d %12.3f %12.3f %12d %12d : %s\r\n"
  756. , StatBuffer[i].Number + (StatBuffer[i].iType==NOTE ? 1 : 0)
  757. , Average
  758. , Std
  759. , StatBuffer[i].Smallest
  760. , StatBuffer[i].Largest
  761. , Incidents[i]
  762. );
  763. } else {
  764. double StDev;
  765. int Avg;
  766. int Smallest;
  767. int Largest;
  768. // Calculate Standard Deviation
  769. if (StatBuffer[i].Number<=1) StDev = -2;
  770. else {
  771. StDev = sqrt( ( SumSq
  772. - ( (Sum * Sum)
  773. / StatBuffer[i].Number
  774. )
  775. )
  776. / (StatBuffer[i].Number-1)
  777. );
  778. }
  779. // Calculate average
  780. if (StatBuffer[i].Number<=0) {
  781. Avg = -2;
  782. } else {
  783. Avg = StatBuffer[i].Sum / StatBuffer[i].Number;
  784. }
  785. // Calculate smallest and largest
  786. if (StatBuffer[i].Number<=0) {
  787. Smallest = -2;
  788. Largest = -2;
  789. } else {
  790. Smallest = StatBuffer[i].Smallest;
  791. Largest = StatBuffer[i].Largest;
  792. }
  793. sprintf( Buffer
  794. , "%6d %s %s %s %s : %s\r\n"
  795. , StatBuffer[i].Number + (StatBuffer[i].iType==NOTE ? 1 : 0)
  796. , Format(s1, Avg )
  797. , Format(s2, (int)StDev )
  798. , Format(s3, Smallest )
  799. , Format(s4, Largest )
  800. , Incidents[i]
  801. );
  802. }
  803. WriteOut(hFile, Buffer);
  804. }
  805. WriteOut(hFile, "Times such as 0.050,00 are in seconds (that was 1/20 sec) \r\n");
  806. } // WriteStats
  807. #if 0 // test format
  808. void TestFormat(int n)
  809. {
  810. char Buffer[50];
  811. char s1[20];
  812. sprintf(Buffer, ">%s<",Format(s1,n));
  813. DbgLog((LOG_TRACE, 0, Buffer));
  814. } // TestFormat
  815. #endif
  816. //=====================================================================
  817. //
  818. // Dump
  819. //
  820. // Dump out all the results from Log and StatBuffer in readable format.
  821. // If hFile is NULL then it uses DbgLog
  822. // otherwise it prints it to that file
  823. //=====================================================================
  824. void Msr_Dump(HANDLE hFile)
  825. {
  826. EnterCriticalSection(&CSMeasure);
  827. if (!bInitOk) {
  828. Msr_Init(); // of course the log will be empty - never mind!
  829. }
  830. WriteLog(hFile);
  831. WriteStats(hFile);
  832. #if 0 // test Format
  833. TestFormat(1);
  834. TestFormat(12);
  835. TestFormat(123);
  836. TestFormat(1234);
  837. TestFormat(12345);
  838. TestFormat(123456);
  839. TestFormat(1234567);
  840. TestFormat(12345678);
  841. TestFormat(123456789);
  842. TestFormat(1234567890);
  843. #endif
  844. LeaveCriticalSection(&CSMeasure);
  845. } // Msr_Dump
  846. //=====================================================================
  847. //
  848. // DumpStats
  849. //
  850. // Dump out all the results from Log and StatBuffer in readable format.
  851. // If hFile is NULL then it uses DbgLog
  852. // otherwise it prints it to that file
  853. //=====================================================================
  854. void WINAPI Msr_DumpStats(HANDLE hFile)
  855. {
  856. EnterCriticalSection(&CSMeasure);
  857. if (!bInitOk) {
  858. Msr_Init(); // of course the stats will be empty - never mind!
  859. }
  860. WriteStats(hFile);
  861. LeaveCriticalSection(&CSMeasure);
  862. } // Msr_DumpStats
  863. extern "C" BOOL WINAPI DllMain(HINSTANCE, ULONG, LPVOID);
  864. BOOL WINAPI
  865. DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pv)
  866. {
  867. UNREFERENCED_PARAMETER(pv);
  868. switch (ulReason)
  869. {
  870. case DLL_PROCESS_ATTACH:
  871. DisableThreadLibraryCalls(hInstance);
  872. InitPerfCounter();
  873. Msr_Init();
  874. break;
  875. case DLL_PROCESS_DETACH:
  876. Msr_Terminate();
  877. break;
  878. }
  879. return TRUE;
  880. }