profile.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. ///////////////////////////////////////////////////////////////////////////////
  19. //
  20. // Profile.h
  21. //
  22. // History:
  23. // 06/11/97 JRD Started.
  24. //
  25. // 06/12/97 JRD Revamped user interfaceChanged to appear as though
  26. // it's not really a class instance. Worked on timing refinement
  27. //
  28. // 06/13/97 JRD Finally got 2nd order self timing working - accurate to 60ns.
  29. // Added ability to have inside out (exclusions) loops.
  30. //
  31. // 06/14/97 JRD Added multiple report sessions, min/max reporting,
  32. // percentage of target (100%) reporting, error reporting,
  33. // and error recovery.
  34. //
  35. // 07/07/97 MJR Switched to S64 instead of __int64.
  36. // Changed printf() stuff to use %g instead of %lg.
  37. //
  38. // 07/08/97 JRD Removed destructor reporting when define is off.
  39. //
  40. //////////////////////////////////////////////////////////////////////////////
  41. // Here's RSPiX's answer to controlled profiling.
  42. // It currently uses Microseconds....
  43. //////////////////////////////////////////////////////////////////////////////
  44. #include <stdio.h>
  45. #include "profile.h"
  46. #include "ORANGE/str/str.h"
  47. #if WIN32
  48. S64 i64GetTimeSpeed=7; // how long does the time command take?
  49. #else
  50. S64 i64GetTimeSpeed=8; // Mike's guess on the mac
  51. #endif
  52. // The new profiler has one global established instance:
  53. RProfile rspProfileInstance;
  54. //////////////////////////////////////////////////////////////////////////////
  55. //
  56. // Start Profile => Begin a range!
  57. //
  58. //////////////////////////////////////////////////////////////////////////////
  59. void RProfile::StartProfile(char* pszFieldName)
  60. {
  61. short sKey;
  62. //*****************************************************************************
  63. //************************************ TIME NOT BILLED CORRECTLY : START ******
  64. //*****************************************************************************
  65. S64 i64EntryTime = rspGetAppMicroseconds() - i64GetTimeSpeed; // Track Overhead
  66. //*****************************************************************************
  67. //************************************ TIME NOT BILLED CORRECTLY : .END. ******
  68. //*****************************************************************************
  69. if (!m_sActive) return; // Do not activate if in error state!
  70. m_sLastUnaccounted = m_lFastTimeOut - m_lFastTimeIn; // Get last overhead time
  71. m_lFastTimeIn = i64EntryTime;
  72. //------------------------------------------------------
  73. // See if this name currently exists:
  74. //------------------------------------------------------
  75. short sMatch = FALSE;
  76. // Do the same length regardless (for consistency)
  77. short i;
  78. for (i=0; i < m_sNumTracked /*PF_MAX_FIELDS*/;i++)
  79. {
  80. if (!rspStricmp(pszFieldName,m_aList[i].m_szFieldName))
  81. {
  82. sMatch = TRUE;
  83. sKey = i;
  84. }
  85. }
  86. if (!sMatch) // first time!
  87. {
  88. //------------------------------------------------------
  89. // Create a new field!
  90. //------------------------------------------------------
  91. if (m_sNumTracked >= PF_MAX_FIELDS)
  92. {
  93. // All timing is void in an error scenario!
  94. //STRACE("RProfile::StartProfile: out of FIELDS, increase PF_MAX_FIELDS or like it!\n");
  95. m_sInternalError = TRUE;
  96. ProfilingOff(); // Turn off the whole thing on error, but report it!
  97. return;
  98. }
  99. // Note the time if it's the first one:
  100. if (!m_sNumTracked)
  101. {
  102. m_lFirstTime = i64EntryTime;
  103. m_sLastUnaccounted = 0; // Assume previous overhead was zero!
  104. }
  105. m_aList[m_sNumTracked].Init();
  106. strcpy(m_aList[m_sNumTracked].m_szFieldName,pszFieldName);
  107. sKey = m_sNumTracked++; // the new handle
  108. }
  109. if (m_aList[sKey].m_eState == Timing)
  110. {
  111. // All timing is void in an error scenario!
  112. //STRACE("RProfile::StartProfile: Hit multiple starts without an end [%s]!\n",m_aList[sKey].m_szFieldName);
  113. m_aList[sKey].m_sInError |= PF_START_ERR;
  114. m_sCommandError = TRUE;
  115. ProfilingOff(); // Turn off the whole thing on error, but report it!
  116. return;
  117. }
  118. //------------------------------------------------------
  119. // Begin the profile:
  120. // We shall use the time at the end of the function to maximize accuracy.
  121. // Because of this, we will not start timing until after the adjustment's made.
  122. m_sCurDepth++; // You have descended down one level!
  123. if (m_sCurDepth > m_sMaxDepth) m_sMaxDepth = m_sCurDepth;
  124. //*****************************************************************************
  125. //************************************ TIME NOT BILLED CORRECTLY : START ******
  126. //*****************************************************************************
  127. // Track Overhead
  128. m_lCount++;
  129. m_lTotTime += m_sLastUnaccounted; // Total system overhead, lag one
  130. // Now remove this overhead from all currently begin timed!
  131. // We do all entries (slow) just to keep it consistent
  132. for (i=0;i < m_sNumTracked /*PF_MAX_FIELDS*/; i++)
  133. {
  134. // Give the timing a boost by bumping up the start time!
  135. // NOTE: This part is slow so it can be consistant:
  136. // NOTE::: It assumes a boolean true has a value of +1!!!
  137. m_aList[i].m_lLastTime += m_sLastUnaccounted*(m_aList[i].m_eState == Timing);
  138. }
  139. m_aList[sKey].m_eState = Timing; // Now, about to begin timing:
  140. // Start the timing! (For Overhead and section.)
  141. //------------------------------------------------------
  142. m_lFastTimeOut = m_aList[sKey].m_lLastTime = rspGetAppMicroseconds();
  143. return;
  144. //*****************************************************************************
  145. //************************************ TIME NOT BILLED CORRECTLY : .END. ******
  146. //*****************************************************************************
  147. }
  148. //////////////////////////////////////////////////////////////////////////////
  149. //////////////////////////////////////////////////////////////////////////////
  150. // Very inefficient for now!
  151. //
  152. void RProfile::EndProfile(char* pszFieldName)
  153. {
  154. short sKey;
  155. //*****************************************************************************
  156. //************************************ TIME NOT BILLED CORRECTLY : START ******
  157. //*****************************************************************************
  158. S64 i64EntryTime = rspGetAppMicroseconds() - i64GetTimeSpeed; // Track Overhead
  159. //*****************************************************************************
  160. //************************************ TIME NOT BILLED CORRECTLY : .END. ******
  161. //*****************************************************************************
  162. if (!m_sActive) return; // Do not activate if in error state!
  163. m_sLastUnaccounted = m_lFastTimeOut - m_lFastTimeIn; // Get last overhead time
  164. m_lFastTimeIn = i64EntryTime;
  165. //------------------------------------------------------
  166. // See if this name currently exists:
  167. //------------------------------------------------------
  168. short sMatch = FALSE;
  169. // Do the same length regardless (for consistency)
  170. short i;
  171. for (i=0; i < m_sNumTracked /*PF_MAX_FIELDS*/;i++)
  172. {
  173. if (!rspStricmp(pszFieldName,m_aList[i].m_szFieldName))
  174. {
  175. sMatch = TRUE;
  176. sKey = i;
  177. }
  178. }
  179. if (!sMatch) // First Time!
  180. {
  181. //------------------------------------------------------
  182. // Create a new field!
  183. //------------------------------------------------------
  184. if (m_sNumTracked >= PF_MAX_FIELDS)
  185. {
  186. // All timing is void in an error scenario!
  187. //STRACE("RProfile::StartProfile: out of FIELDS, increase PF_MAX_FIELDS or like it!\n");
  188. m_sInternalError = TRUE;
  189. ProfilingOff(); // Turn off the whole thing on error, but report it!
  190. return;
  191. }
  192. m_aList[m_sNumTracked].Init();
  193. strcpy(m_aList[m_sNumTracked].m_szFieldName,pszFieldName);
  194. sKey = m_sNumTracked++; // the new handle
  195. }
  196. if (m_aList[sKey].m_eState == DoneTiming)
  197. {
  198. // All timing is void in an error scenario!
  199. //STRACE("RProfile::EndProfile: Hit multiple ends without a start [%s]!\n",
  200. // m_aList[sKey].m_szFieldName);
  201. m_aList[sKey].m_sInError |= PF_END_ERR;
  202. m_sCommandError = TRUE;
  203. ProfilingOff(); // Turn off the whole thing on error, but report it!
  204. return;
  205. }
  206. // In this case, use the timing at entry, since we will NOT be subtracting the overhead
  207. // from this run...
  208. // Allow poosibility of hitting this first:
  209. if (m_aList[sKey].m_eState == Timing) // you're in progress
  210. {
  211. S64 i64Diff = i64EntryTime - m_aList[sKey].m_lLastTime;
  212. m_aList[sKey].m_lTotTime += i64Diff;
  213. m_aList[sKey].m_lNumCalls++; // A successful time
  214. m_sCurDepth--; // You have risen up one level!
  215. // Give current time as now - profile session start:
  216. // Remove conditions for consitency!
  217. if (i64Diff < m_aList[sKey].m_lMinTime)
  218. {
  219. m_aList[sKey].m_lMinTime = i64Diff;
  220. m_aList[sKey].m_lWhenMin = i64EntryTime - m_lBeginTime;
  221. }
  222. if (i64Diff > m_aList[sKey].m_lMaxTime)
  223. {
  224. m_aList[sKey].m_lMaxTime = i64Diff;
  225. m_aList[sKey].m_lWhenMax = i64EntryTime - m_lBeginTime;
  226. }
  227. }
  228. m_aList[sKey].m_eState = DoneTiming;
  229. //*****************************************************************************
  230. //************************************ TIME NOT BILLED CORRECTLY : START ******
  231. //*****************************************************************************
  232. // Track Overhead
  233. m_lCount++;
  234. m_lTotTime += m_sLastUnaccounted; // Total system overhead, lag one
  235. // Now remove this overhead from all currently begin timed!
  236. // We do all entries (slow) just to keep it consistent
  237. for (i=0;i < m_sNumTracked /*PF_MAX_FIELDS*/; i++)
  238. {
  239. // Give the timing a boost by bumping up the start time!
  240. // NOTE: This part is slow so it can be consistant:
  241. // NOTE::: It assumes a boolean true has a value of +1!!!
  242. m_aList[i].m_lLastTime += m_sLastUnaccounted*(m_aList[i].m_eState == Timing);
  243. }
  244. // Store this overhead to be lagged one use:
  245. m_lFastTimeOut = rspGetAppMicroseconds();
  246. return;
  247. //*****************************************************************************
  248. //************************************ TIME NOT BILLED CORRECTLY : .END. ******
  249. //*****************************************************************************
  250. }
  251. short gsReportNumber = 0;
  252. //===============================================
  253. void RProfile::Report()
  254. {
  255. FILE* fp;
  256. if (gsReportNumber == 0)
  257. {
  258. fp = fopen(m_szOutputFile,"w");
  259. }
  260. else
  261. {
  262. fp = fopen(m_szOutputFile,"a"); // append!
  263. }
  264. if (!m_lDeceasedTime) // degenerate report at end:
  265. {
  266. //=============================================== THE REAL REPORT ====================
  267. if (!gsReportNumber)
  268. {
  269. fprintf(fp,"Welcome to RSPiX Profiling! Measurement error typically ranges from 60ns to 75us.\n\n");
  270. fprintf(fp,"SYMBOL KEY: '@' - refers to the time at which something occurred relative to the START of the REPORT.\n");
  271. fprintf(fp,"Ex: '@+(s)' means the time in seconds after the start of the current report.\n");
  272. }
  273. fprintf(fp,"\n=======================\nREPORT #%d\n=======================\n\n",
  274. gsReportNumber + 1);
  275. fprintf(fp,"Activation occurred %g seconds into execution.\n",
  276. double(m_lBeginTime - m_lInitTime)/1000000.0);
  277. fprintf(fp,"Total report time was %g seconds.\n\n",
  278. double(m_lFastTimeOut - m_lBeginTime)/1000000.0);
  279. if (m_lCount) // safety
  280. fprintf(fp,"Profiler overhead: Tot(ms) = %g, # of calls = %ld, Avg(ms) = %g\n\n",
  281. double(m_lTotTime)/1000.0,long(m_lCount),double(m_lTotTime)/double(m_lCount * S64(1000)));
  282. fprintf(fp,"Number of profile ranges was %hd.\n",m_sNumTracked);
  283. fprintf(fp,"Maximum nesting depth was %hd\n\n",m_sMaxDepth-1);
  284. //--------------------------------------------------------------------------------------------
  285. // See if there are any typos which make this run invalid:
  286. if (m_sInternalError || m_sCommandError)
  287. {
  288. fprintf(fp,"\n************ PROFILING ERROR - DATA INVALID *************\n\n");
  289. if (m_sInternalError)
  290. {
  291. fprintf(fp,"Internal memory exceeded - the profiler was designed to only\n"
  292. "handle %hd timing sets. Please increase the number or get rid of old ones.\n\n",
  293. short(PF_MAX_FIELDS));
  294. return;
  295. }
  296. // Must be a user error:
  297. fprintf(fp,"You have made a command typo. You cannot nest timing runs of the \n"
  298. "same name. Nor can you have 'unbalanced parenthesis'.\n\n"
  299. "Here was the first command in error:\n");
  300. for (short i=0; i < m_sNumTracked;i++)
  301. {
  302. if (m_aList[i].m_sInError)
  303. {
  304. fprintf(fp,"%s: \n ",m_aList[i].m_szFieldName);
  305. if (m_aList[i].m_sInError & PF_START_ERR)
  306. fprintf(fp,"missing rspEndProfile\n");
  307. if (m_aList[i].m_sInError & PF_END_ERR)
  308. fprintf(fp,"missing rspStartProfile\n");
  309. }
  310. }
  311. fclose(fp); // since we're ducking out early, better sure up the file!
  312. gsReportNumber++;
  313. return;
  314. }
  315. //--------------------------------------------------------------------------------------------
  316. // See if there is a relative request:
  317. double dRel = 0.0;
  318. short i;
  319. for (i=0; i < m_sNumTracked;i++)
  320. {
  321. if (!strncmp(m_aList[i].m_szFieldName,"100%",4))
  322. {
  323. if (m_aList[i].m_lNumCalls)
  324. dRel = double(m_aList[i].m_lTotTime)/100.0;
  325. break;
  326. }
  327. }
  328. for (i=0; i < m_sNumTracked;i++)
  329. {
  330. if (m_aList[i].m_lNumCalls) // safety
  331. {
  332. fprintf(fp,"-------------------------------------------------------\n%s:\n",
  333. m_aList[i].m_szFieldName);
  334. fprintf(fp," # of passes = %ld, Tot(ms) = %g, Avg(ms) = %g",
  335. long(m_aList[i].m_lNumCalls),
  336. double(m_aList[i].m_lTotTime)/1000.0,
  337. double(m_aList[i].m_lTotTime)/double(m_aList[i].m_lNumCalls * S64(1000)));
  338. if (dRel)
  339. {
  340. double dRat = double(m_aList[i].m_lTotTime)/dRel;
  341. fprintf(fp," [%+1.6g %%]\n",double( long(dRat*10000.0) )/10000.0);
  342. }
  343. else
  344. fprintf(fp,"\n");
  345. //=========================
  346. fprintf(fp," MIN(ms): %+8.6g @+(s): %+8.6g\n",
  347. double(m_aList[i].m_lMinTime)/1000.0,double(m_aList[i].m_lWhenMin)/1000000.0);
  348. fprintf(fp," MAX(ms): %+8.6g @+(s): %+8.6g\n",
  349. double(m_aList[i].m_lMaxTime)/1000.0,double(m_aList[i].m_lWhenMax)/1000000.0);
  350. }
  351. }
  352. fprintf(fp,"-------------------------------------------------------\n");
  353. //=======================================================================================
  354. }
  355. else // Only for the final time:
  356. {
  357. fprintf(fp,"\n==========================================\n");
  358. fprintf(fp,"\nTOTAL PROGRAM TIME = %g seconds\n",double(m_lDeceasedTime - m_lInitTime)/1000000.0);
  359. fprintf(fp,"Last timing run finished %g seconds before program exit.\n",
  360. double(m_lDeceasedTime - m_lFastTimeOut)/1000000.0);
  361. }
  362. fclose(fp);
  363. gsReportNumber++;
  364. }
  365. RProfile::RProfile()
  366. {
  367. m_sNumTracked = 0;
  368. strcpy(m_szOutputFile,"rspProfile.txt");
  369. m_sActive = m_sCommandError = m_sInternalError = FALSE;
  370. m_sMaxDepth = 0;
  371. m_lCount = 0;
  372. m_lTotTime = m_lFirstTime = m_lBeginTime = m_lDeceasedTime =
  373. m_lFastTimeIn = m_lFastTimeOut = 0;
  374. m_lInitTime = rspGetAppMicroseconds(); // the First element
  375. m_lInitTime = 0; // Blue will reset this to zero before I can reach it!
  376. m_sLastUnaccounted = 0; // perhaps this is for the best (sigh)
  377. }