profile.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /////////////////////////////////////////////////////////////////////////EA-V1
  19. // $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile.cpp $
  20. // $Author: mhoffe $
  21. // $Revision: #6 $
  22. // $DateTime: 2003/08/14 13:43:29 $
  23. //
  24. // ©2003 Electronic Arts
  25. //
  26. // Profile module main code
  27. //////////////////////////////////////////////////////////////////////////////
  28. #include "_pch.h"
  29. #include <new>
  30. #include "mmsystem.h"
  31. #pragma comment (lib,"winmm")
  32. // yuk, I'm doing this so weird because the destructor
  33. // of cmd must never be called...
  34. static ProfileCmdInterface &cmd=*(ProfileCmdInterface *)new
  35. (ProfileAllocMemory(sizeof(ProfileCmdInterface))) ProfileCmdInterface();
  36. // we have this here so that our command interface will always
  37. // be linked in as well...
  38. static bool __RegisterDebugCmdGroup_Profile=Debug::AddCommands("profile",&cmd);
  39. void *ProfileAllocMemory(unsigned numBytes)
  40. {
  41. HGLOBAL h=GlobalAlloc(GMEM_FIXED,numBytes);
  42. if (!h)
  43. DCRASH_RELEASE("Debug mem alloc failed");
  44. return (void *)h;
  45. }
  46. void *ProfileReAllocMemory(void *oldPtr, unsigned newSize)
  47. {
  48. // Windows doesn't like ReAlloc with NULL handle/ptr...
  49. if (!oldPtr)
  50. return newSize?ProfileAllocMemory(newSize):0;
  51. // Shrinking to 0 size is basically freeing memory
  52. if (!newSize)
  53. {
  54. GlobalFree((HGLOBAL)oldPtr);
  55. return 0;
  56. }
  57. // now try GlobalReAlloc first
  58. HGLOBAL h=GlobalReAlloc((HGLOBAL)oldPtr,newSize,0);
  59. if (!h)
  60. {
  61. // this failed (Windows doesn't like ReAlloc'ing larger
  62. // fixed memory blocks) - go with Alloc/Free instead
  63. h=GlobalAlloc(GMEM_FIXED,newSize);
  64. if (!h)
  65. DCRASH_RELEASE("Debug mem realloc failed");
  66. unsigned oldSize=GlobalSize((HGLOBAL)oldPtr);
  67. memcpy((void *)h,oldPtr,oldSize<newSize?oldSize:newSize);
  68. GlobalFree((HGLOBAL)oldPtr);
  69. }
  70. return (void *)h;
  71. }
  72. void ProfileFreeMemory(void *ptr)
  73. {
  74. if (ptr)
  75. GlobalFree((HGLOBAL)ptr);
  76. }
  77. //////////////////////////////////////////////////////////////////////////////
  78. static _int64 GetClockCyclesFast(void)
  79. {
  80. // this is where we're adding our internal result functions
  81. Profile::AddResultFunction(ProfileResultFileCSV::Create,
  82. "file_csv",
  83. "");
  84. Profile::AddResultFunction(ProfileResultFileCSV::Create,
  85. "file_dot",
  86. "[ file [ frame_name [ fold_threshold ] ] ]");
  87. // this must not take a very huge CPU hit...
  88. // measure clock cycles 3 times for 20 msec each
  89. // then take the 2 counts that are closest, average
  90. _int64 n[3];
  91. for (int k=0;k<3;k++)
  92. {
  93. // wait for end of current tick
  94. unsigned timeEnd=timeGetTime()+2;
  95. while (timeGetTime()<timeEnd);
  96. // get cycles
  97. _int64 start,startQPC,endQPC;
  98. QueryPerformanceCounter((LARGE_INTEGER *)&startQPC);
  99. ProfileGetTime(start);
  100. timeEnd+=20;
  101. while (timeGetTime()<timeEnd);
  102. ProfileGetTime(n[k]);
  103. n[k]-=start;
  104. // convert to 1 second
  105. if (QueryPerformanceCounter((LARGE_INTEGER *)&endQPC))
  106. {
  107. _int64 freq;
  108. QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
  109. n[k]=(n[k]*freq)/(endQPC-startQPC);
  110. }
  111. else
  112. {
  113. n[k]=(n[k]*1000)/20;
  114. }
  115. }
  116. // find two closest values
  117. _int64 d01=n[1]-n[0],d02=n[2]-n[0],d12=n[2]-n[1];
  118. if (d01<0) d01=-d01;
  119. if (d02<0) d02=-d02;
  120. if (d12<0) d12=-d12;
  121. _int64 avg;
  122. if (d01<d02)
  123. {
  124. avg=d01<d12?n[0]+n[1]:n[1]+n[2];
  125. }
  126. else
  127. {
  128. avg=d02<d12?n[0]+n[2]:n[1]+n[2];
  129. }
  130. // return result
  131. // (rounded to the next MHz)
  132. return ((avg/2+500000)/1000000)*1000000;
  133. }
  134. unsigned Profile::m_rec;
  135. char **Profile::m_recNames;
  136. unsigned Profile::m_names;
  137. Profile::FrameName *Profile::m_frameNames;
  138. _int64 Profile::m_clockCycles=GetClockCyclesFast();
  139. Profile::PatternListEntry *Profile::firstPatternEntry;
  140. Profile::PatternListEntry *Profile::lastPatternEntry;
  141. void Profile::StartRange(const char *range)
  142. {
  143. // set default
  144. if (!range)
  145. range="frame";
  146. // known name?
  147. for (unsigned k=0;k<m_names;++k)
  148. if (!strcmp(range,m_frameNames[k].name))
  149. break;
  150. if (k==m_names)
  151. {
  152. // no, must add to list
  153. m_frameNames=(FrameName *)ProfileReAllocMemory(m_frameNames,(++m_names)*sizeof(FrameName));
  154. m_frameNames[k].name=(char *)ProfileAllocMemory(strlen(range)+1);
  155. strcpy(m_frameNames[k].name,range);
  156. m_frameNames[k].frames=0;
  157. m_frameNames[k].isRecording=false;
  158. m_frameNames[k].doAppend=false;
  159. m_frameNames[k].lastGlobalIndex=-1;
  160. }
  161. // stop old recording?
  162. if (m_frameNames[k].isRecording)
  163. StopRange(range);
  164. // start new recording
  165. m_frameNames[k].isRecording=true;
  166. m_frameNames[k].doAppend=false;
  167. // but check first: is recording enabled?
  168. bool active=false;
  169. for (PatternListEntry *cur=firstPatternEntry;cur;cur=cur->next)
  170. {
  171. if (SimpleMatch(range,cur->pattern))
  172. active=cur->isActive;
  173. }
  174. if (active)
  175. {
  176. #ifdef _PROFILE
  177. m_frameNames[k].funcIndex=ProfileFuncLevelTracer::FrameStart();
  178. DASSERT(m_frameNames[k].funcIndex>=0);
  179. #endif
  180. m_frameNames[k].highIndex=ProfileId::FrameStart();
  181. DASSERT(m_frameNames[k].highIndex>=0);
  182. }
  183. else
  184. {
  185. m_frameNames[k].funcIndex=-1;
  186. m_frameNames[k].highIndex=-1;
  187. }
  188. }
  189. void Profile::AppendRange(const char *range)
  190. {
  191. // set default
  192. if (!range)
  193. range="frame";
  194. // known name?
  195. for (unsigned k=0;k<m_names;++k)
  196. if (!strcmp(range,m_frameNames[k].name))
  197. break;
  198. if (k==m_names)
  199. {
  200. // no, so StartRange will do the job for us
  201. StartRange(range);
  202. return;
  203. }
  204. // still recording?
  205. if (m_frameNames[k].isRecording)
  206. // don't do anything
  207. return;
  208. // start new recording
  209. m_frameNames[k].isRecording=true;
  210. m_frameNames[k].doAppend=true;
  211. // but check first: is recording enabled?
  212. bool active=false;
  213. for (PatternListEntry *cur=firstPatternEntry;cur;cur=cur->next)
  214. {
  215. if (SimpleMatch(range,cur->pattern))
  216. active=cur->isActive;
  217. }
  218. if (active)
  219. {
  220. #ifdef _PROFILE
  221. m_frameNames[k].funcIndex=ProfileFuncLevelTracer::FrameStart();
  222. DASSERT(m_frameNames[k].funcIndex>=0);
  223. #endif
  224. m_frameNames[k].highIndex=ProfileId::FrameStart();
  225. DASSERT(m_frameNames[k].highIndex>=0);
  226. }
  227. else
  228. {
  229. m_frameNames[k].funcIndex=-1;
  230. m_frameNames[k].highIndex=-1;
  231. }
  232. }
  233. void Profile::StopRange(const char *range)
  234. {
  235. // set default
  236. if (!range)
  237. range="frame";
  238. // known name?
  239. for (unsigned k=0;k<m_names;++k)
  240. if (!strcmp(range,m_frameNames[k].name))
  241. break;
  242. DFAIL_IF(k==m_names) return;
  243. DFAIL_IF(!m_frameNames[k].isRecording) return;
  244. // stop recording
  245. m_frameNames[k].isRecording=false;
  246. if (
  247. #ifdef _PROFILE
  248. m_frameNames[k].funcIndex>=0 ||
  249. #endif
  250. m_frameNames[k].highIndex>=0
  251. )
  252. {
  253. // add to list of known frames?
  254. int atIndex;
  255. if (!m_frameNames[k].doAppend||
  256. m_frameNames[k].lastGlobalIndex<0)
  257. {
  258. atIndex=-1;
  259. m_frameNames[k].lastGlobalIndex=m_rec;
  260. m_recNames=(char **)ProfileReAllocMemory(m_recNames,(m_rec+1)*sizeof(char *));
  261. m_recNames[m_rec]=(char *)ProfileAllocMemory(strlen(range)+1+6);
  262. wsprintf(m_recNames[m_rec++],"%s:%i",range,++m_frameNames[k].frames);
  263. }
  264. else
  265. atIndex=m_frameNames[k].lastGlobalIndex;
  266. #ifdef _PROFILE
  267. if (m_frameNames[k].funcIndex>=0)
  268. ProfileFuncLevelTracer::FrameEnd(m_frameNames[k].funcIndex,atIndex);
  269. if (m_frameNames[k].highIndex>=0)
  270. #endif
  271. ProfileId::FrameEnd(m_frameNames[k].highIndex,atIndex);
  272. }
  273. }
  274. bool Profile::IsEnabled(void)
  275. {
  276. for (unsigned k=0;k<m_names;++k)
  277. if (m_frameNames[k].isRecording)
  278. return true;
  279. return false;
  280. }
  281. unsigned Profile::GetFrameCount(void)
  282. {
  283. return m_rec;
  284. }
  285. const char *Profile::GetFrameName(unsigned frame)
  286. {
  287. return frame>=m_rec?NULL:m_recNames[frame];
  288. }
  289. void Profile::ClearTotals(void)
  290. {
  291. #ifdef _PROFILE
  292. ProfileFuncLevelTracer::ClearTotals();
  293. #endif
  294. ProfileId::ClearTotals();
  295. }
  296. _int64 Profile::GetClockCyclesPerSecond(void)
  297. {
  298. return m_clockCycles;
  299. }
  300. void Profile::AddResultFunction(ProfileResultInterface* (*func)(int, const char * const *),
  301. const char *name, const char *arg)
  302. {
  303. ProfileCmdInterface::AddResultFunction(func,name,arg);
  304. }
  305. bool Profile::SimpleMatch(const char *str, const char *pattern)
  306. {
  307. DASSERT(str);
  308. DASSERT(pattern);
  309. while (*str&&*pattern)
  310. {
  311. if (*pattern=='*')
  312. {
  313. pattern++;
  314. while (*str)
  315. if (SimpleMatch(str++,pattern))
  316. return true;
  317. return *str==*pattern;
  318. }
  319. else
  320. {
  321. if (*str++!=*pattern++)
  322. return false;
  323. }
  324. }
  325. return *str==*pattern;
  326. }
  327. static void ProfileShutdown(void)
  328. {
  329. #ifdef _PROFILE
  330. ProfileFuncLevelTracer::Shutdown();
  331. #endif
  332. ProfileId::Shutdown();
  333. DLOG("CPU speed is " << unsigned(Profile::GetClockCyclesPerSecond()) << " Hz.\n");
  334. cmd.RunResultFunctions();
  335. }
  336. int profileTracerInit=atexit(ProfileShutdown);