Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
profile.cpp
Go to the documentation of this file.
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
20// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile.cpp $
21// $Author: mhoffe $
22// $Revision: #6 $
23// $DateTime: 2003/08/14 13:43:29 $
24//
25// ©2003 Electronic Arts
26//
27// Profile module main code
29#include "_pch.h"
30#include <new>
31#include "mmsystem.h"
32
33#pragma comment (lib,"winmm")
34
35// yuk, I'm doing this so weird because the destructor
36// of cmd must never be called...
39
40// we have this here so that our command interface will always
41// be linked in as well...
42static bool __RegisterDebugCmdGroup_Profile=Debug::AddCommands("profile",&cmd);
43
44void *ProfileAllocMemory(unsigned numBytes)
45{
46 HGLOBAL h=GlobalAlloc(GMEM_FIXED,numBytes);
47 if (!h)
48 DCRASH_RELEASE("Debug mem alloc failed");
49 return (void *)h;
50}
51
52void *ProfileReAllocMemory(void *oldPtr, unsigned newSize)
53{
54 // Windows doesn't like ReAlloc with NULL handle/ptr...
55 if (!oldPtr)
56 return newSize?ProfileAllocMemory(newSize):0;
57
58 // Shrinking to 0 size is basically freeing memory
59 if (!newSize)
60 {
61 GlobalFree((HGLOBAL)oldPtr);
62 return 0;
63 }
64
65 // now try GlobalReAlloc first
66 HGLOBAL h=GlobalReAlloc((HGLOBAL)oldPtr,newSize,0);
67 if (!h)
68 {
69 // this failed (Windows doesn't like ReAlloc'ing larger
70 // fixed memory blocks) - go with Alloc/Free instead
71 h=GlobalAlloc(GMEM_FIXED,newSize);
72 if (!h)
73 DCRASH_RELEASE("Debug mem realloc failed");
74 unsigned oldSize=GlobalSize((HGLOBAL)oldPtr);
75 memcpy((void *)h,oldPtr,oldSize<newSize?oldSize:newSize);
76 GlobalFree((HGLOBAL)oldPtr);
77 }
78
79 return (void *)h;
80}
81
82void ProfileFreeMemory(void *ptr)
83{
84 if (ptr)
85 GlobalFree((HGLOBAL)ptr);
86}
87
89
90static _int64 GetClockCyclesFast(void)
91{
92 // this is where we're adding our internal result functions
94 "file_csv",
95 "");
97 "file_dot",
98 "[ file [ frame_name [ fold_threshold ] ] ]");
99
100 // this must not take a very huge CPU hit...
101
102 // measure clock cycles 3 times for 20 msec each
103 // then take the 2 counts that are closest, average
104 _int64 n[3];
105 for (int k=0;k<3;k++)
106 {
107 // wait for end of current tick
108 unsigned timeEnd=timeGetTime()+2;
109 while (timeGetTime()<timeEnd);
110
111 // get cycles
112 _int64 start,startQPC,endQPC;
113 QueryPerformanceCounter((LARGE_INTEGER *)&startQPC);
114 ProfileGetTime(start);
115 timeEnd+=20;
116 while (timeGetTime()<timeEnd);
117 ProfileGetTime(n[k]);
118 n[k]-=start;
119
120 // convert to 1 second
121 if (QueryPerformanceCounter((LARGE_INTEGER *)&endQPC))
122 {
123 _int64 freq;
124 QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
125 n[k]=(n[k]*freq)/(endQPC-startQPC);
126 }
127 else
128 {
129 n[k]=(n[k]*1000)/20;
130 }
131 }
132
133 // find two closest values
134 _int64 d01=n[1]-n[0],d02=n[2]-n[0],d12=n[2]-n[1];
135 if (d01<0) d01=-d01;
136 if (d02<0) d02=-d02;
137 if (d12<0) d12=-d12;
138 _int64 avg;
139 if (d01<d02)
140 {
141 avg=d01<d12?n[0]+n[1]:n[1]+n[2];
142 }
143 else
144 {
145 avg=d02<d12?n[0]+n[2]:n[1]+n[2];
146 }
147
148 // return result
149 // (rounded to the next MHz)
150 return ((avg/2+500000)/1000000)*1000000;
151}
152
153unsigned Profile::m_rec;
154char **Profile::m_recNames;
155unsigned Profile::m_names;
156Profile::FrameName *Profile::m_frameNames;
157_int64 Profile::m_clockCycles=GetClockCyclesFast();
158Profile::PatternListEntry *Profile::firstPatternEntry;
159Profile::PatternListEntry *Profile::lastPatternEntry;
160
161void Profile::StartRange(const char *range)
162{
163 // set default
164 if (!range)
165 range="frame";
166
167 // known name?
168 for (unsigned k=0;k<m_names;++k)
169 if (!strcmp(range,m_frameNames[k].name))
170 break;
171 if (k==m_names)
172 {
173 // no, must add to list
174 m_frameNames=(FrameName *)ProfileReAllocMemory(m_frameNames,(++m_names)*sizeof(FrameName));
175 m_frameNames[k].name=(char *)ProfileAllocMemory(strlen(range)+1);
176 strcpy(m_frameNames[k].name,range);
177 m_frameNames[k].frames=0;
178 m_frameNames[k].isRecording=false;
179 m_frameNames[k].doAppend=false;
180 m_frameNames[k].lastGlobalIndex=-1;
181 }
182
183 // stop old recording?
184 if (m_frameNames[k].isRecording)
185 StopRange(range);
186
187 // start new recording
188 m_frameNames[k].isRecording=true;
189 m_frameNames[k].doAppend=false;
190
191 // but check first: is recording enabled?
192 bool active=false;
193 for (PatternListEntry *cur=firstPatternEntry;cur;cur=cur->next)
194 {
195 if (SimpleMatch(range,cur->pattern))
196 active=cur->isActive;
197 }
198
199 if (active)
200 {
201#ifdef _PROFILE
202 m_frameNames[k].funcIndex=ProfileFuncLevelTracer::FrameStart();
203 DASSERT(m_frameNames[k].funcIndex>=0);
204#endif
205 m_frameNames[k].highIndex=ProfileId::FrameStart();
206 DASSERT(m_frameNames[k].highIndex>=0);
207 }
208 else
209 {
210 m_frameNames[k].funcIndex=-1;
211 m_frameNames[k].highIndex=-1;
212 }
213}
214
215void Profile::AppendRange(const char *range)
216{
217 // set default
218 if (!range)
219 range="frame";
220
221 // known name?
222 for (unsigned k=0;k<m_names;++k)
223 if (!strcmp(range,m_frameNames[k].name))
224 break;
225 if (k==m_names)
226 {
227 // no, so StartRange will do the job for us
228 StartRange(range);
229 return;
230 }
231
232 // still recording?
233 if (m_frameNames[k].isRecording)
234 // don't do anything
235 return;
236
237 // start new recording
238 m_frameNames[k].isRecording=true;
239 m_frameNames[k].doAppend=true;
240
241 // but check first: is recording enabled?
242 bool active=false;
243 for (PatternListEntry *cur=firstPatternEntry;cur;cur=cur->next)
244 {
245 if (SimpleMatch(range,cur->pattern))
246 active=cur->isActive;
247 }
248
249 if (active)
250 {
251#ifdef _PROFILE
252 m_frameNames[k].funcIndex=ProfileFuncLevelTracer::FrameStart();
253 DASSERT(m_frameNames[k].funcIndex>=0);
254#endif
255 m_frameNames[k].highIndex=ProfileId::FrameStart();
256 DASSERT(m_frameNames[k].highIndex>=0);
257 }
258 else
259 {
260 m_frameNames[k].funcIndex=-1;
261 m_frameNames[k].highIndex=-1;
262 }
263}
264
265void Profile::StopRange(const char *range)
266{
267 // set default
268 if (!range)
269 range="frame";
270
271 // known name?
272 for (unsigned k=0;k<m_names;++k)
273 if (!strcmp(range,m_frameNames[k].name))
274 break;
275 DFAIL_IF(k==m_names) return;
276 DFAIL_IF(!m_frameNames[k].isRecording) return;
277
278 // stop recording
279 m_frameNames[k].isRecording=false;
280 if (
281#ifdef _PROFILE
282 m_frameNames[k].funcIndex>=0 ||
283#endif
284 m_frameNames[k].highIndex>=0
285 )
286 {
287 // add to list of known frames?
288 int atIndex;
289 if (!m_frameNames[k].doAppend||
290 m_frameNames[k].lastGlobalIndex<0)
291 {
292 atIndex=-1;
293 m_frameNames[k].lastGlobalIndex=m_rec;
294 m_recNames=(char **)ProfileReAllocMemory(m_recNames,(m_rec+1)*sizeof(char *));
295 m_recNames[m_rec]=(char *)ProfileAllocMemory(strlen(range)+1+6);
296 wsprintf(m_recNames[m_rec++],"%s:%i",range,++m_frameNames[k].frames);
297 }
298 else
299 atIndex=m_frameNames[k].lastGlobalIndex;
300#ifdef _PROFILE
301 if (m_frameNames[k].funcIndex>=0)
302 ProfileFuncLevelTracer::FrameEnd(m_frameNames[k].funcIndex,atIndex);
303 if (m_frameNames[k].highIndex>=0)
304#endif
305 ProfileId::FrameEnd(m_frameNames[k].highIndex,atIndex);
306 }
307}
308
310{
311 for (unsigned k=0;k<m_names;++k)
312 if (m_frameNames[k].isRecording)
313 return true;
314 return false;
315}
316
318{
319 return m_rec;
320}
321
322const char *Profile::GetFrameName(unsigned frame)
323{
324 return frame>=m_rec?NULL:m_recNames[frame];
325}
326
328{
329#ifdef _PROFILE
331#endif
333}
334
336{
337 return m_clockCycles;
338}
339
340void Profile::AddResultFunction(ProfileResultInterface* (*func)(int, const char * const *),
341 const char *name, const char *arg)
342{
344}
345
346bool Profile::SimpleMatch(const char *str, const char *pattern)
347{
348 DASSERT(str);
349 DASSERT(pattern);
350 while (*str&&*pattern)
351 {
352 if (*pattern=='*')
353 {
354 pattern++;
355 while (*str)
356 if (SimpleMatch(str++,pattern))
357 return true;
358 return *str==*pattern;
359 }
360 else
361 {
362 if (*str++!=*pattern++)
363 return false;
364 }
365 }
366
367 return *str==*pattern;
368}
369
370static void ProfileShutdown(void)
371{
372#ifdef _PROFILE
374#endif
376
377 DLOG("CPU speed is " << unsigned(Profile::GetClockCyclesPerSecond()) << " Hz.\n");
378
379 cmd.RunResultFunctions();
380}
381
382int profileTracerInit=atexit(ProfileShutdown);
#define NULL
Definition BaseType.h:92
__forceinline void ProfileGetTime(__int64 &t)
Definition PerfTimer.cpp:38
static bool AddCommands(const char *cmdgroup, DebugCmdInterface *cmdif)
Adds a new command group.
static void AddResultFunction(ProfileResultInterface *(*func)(int, const char *const *), const char *name, const char *arg)
static int FrameStart(void)
static void FrameEnd(int which, int mixIndex)
static void ClearTotals(void)
static void Shutdown(void)
static unsigned GetFrameCount(void)
Determines the number of known (recorded) range frames.
Definition profile.cpp:317
static void AddResultFunction(ProfileResultInterface *(*func)(int, const char *const *), const char *name, const char *arg)
Add the given result function interface.
Definition profile.cpp:340
static void StartRange(const char *range=0)
Starts range recording.
Definition profile.cpp:161
static bool IsEnabled(void)
Determines if any range recording is enabled or not.
Definition profile.cpp:309
static _int64 GetClockCyclesPerSecond(void)
Determines number of CPU clock cycles per second.
Definition profile.cpp:335
static void ClearTotals(void)
Resets all 'total' counter values to 0.
Definition profile.cpp:327
static void AppendRange(const char *range=0)
Appends profile data to the last recorded frame of the given range.
Definition profile.cpp:215
static const char * GetFrameName(unsigned frame)
Determines the range name of a recorded range frame.
Definition profile.cpp:322
static void StopRange(const char *range=0)
Stops range recording.
Definition profile.cpp:265
static int FrameStart(void)
static void ClearTotals(void)
static void Shutdown(void)
static void FrameEnd(int which, int mixIndex)
static ProfileResultInterface * Create(int argn, const char *const *)
Result function class.
#define DLOG(what)
#define DASSERT(expr)
#define DCRASH_RELEASE(msg)
#define DFAIL_IF(cond)
void * ProfileReAllocMemory(void *oldPtr, unsigned newSize)
Definition profile.cpp:52
void * ProfileAllocMemory(unsigned numBytes)
Definition profile.cpp:44
int profileTracerInit
Definition profile.cpp:382
void ProfileFreeMemory(void *ptr)
Definition profile.cpp:82
void * ProfileReAllocMemory(void *oldPtr, unsigned newSize)
Definition profile.cpp:52
void * ProfileAllocMemory(unsigned numBytes)
Definition profile.cpp:44
unsigned char d12
Definition vchannel.cpp:286