mirror of
https://github.com/electronicarts/CnC_Generals_Zero_Hour
synced 2025-08-16 22:25:04 +02:00
Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.
This commit is contained in:
29
GeneralsMD/Code/Libraries/Source/profile/_pch.cpp
Normal file
29
GeneralsMD/Code/Libraries/Source/profile/_pch.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/_pch.cpp $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #1 $
|
||||
// $DateTime: 2003/07/03 11:55:26 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Precompiled header (module internal)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "_pch.h"
|
42
GeneralsMD/Code/Libraries/Source/profile/_pch.h
Normal file
42
GeneralsMD/Code/Libraries/Source/profile/_pch.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/_pch.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #1 $
|
||||
// $DateTime: 2003/07/03 11:55:26 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Precompiled header (module internal)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef _PCH_H // Include guard
|
||||
#define _PCH_H
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define STRICT
|
||||
#include <windows.h>
|
||||
|
||||
#include "profile.h"
|
||||
#include "internal.h"
|
||||
|
||||
#endif // _PCH_H
|
26
GeneralsMD/Code/Libraries/Source/profile/compile_doxygen.bat
Normal file
26
GeneralsMD/Code/Libraries/Source/profile/compile_doxygen.bat
Normal file
@@ -0,0 +1,26 @@
|
||||
@echo off
|
||||
|
||||
rem
|
||||
rem This batch file generates DoxyGen info and compiles the separate
|
||||
rem HTML files into CHMs.
|
||||
rem
|
||||
rem Assumptions:
|
||||
rem ------------
|
||||
rem - doxygen installed and in path
|
||||
rem - hhc (HTML Help Compiler) installed and in path
|
||||
rem - dot (Graphviz package) installed and in path
|
||||
rem
|
||||
rem Both generated CHM files are then copied over to the
|
||||
rem common DOC area.
|
||||
|
||||
doxygen profile.dox
|
||||
doxygen profile_priv.dox
|
||||
cd doc\html
|
||||
hhc index.hhp
|
||||
copy index.chm ..\..\profile.chm
|
||||
cd ..\html_priv
|
||||
hhc index.hhp
|
||||
copy index.chm ..\..\profile_priv.chm
|
||||
cd ..\..
|
||||
|
||||
copy profile.chm ..\..\docs
|
125
GeneralsMD/Code/Libraries/Source/profile/internal.h
Normal file
125
GeneralsMD/Code/Libraries/Source/profile/internal.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/internal.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #3 $
|
||||
// $DateTime: 2003/07/09 10:57:23 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Internal header
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef INTERNAL_H // Include guard
|
||||
#define INTERNAL_H
|
||||
|
||||
#include "../debug/debug.h"
|
||||
#include "internal_funclevel.h"
|
||||
#include "internal_highlevel.h"
|
||||
#include "internal_cmd.h"
|
||||
#include "internal_result.h"
|
||||
|
||||
class ProfileFastCS
|
||||
{
|
||||
ProfileFastCS(const ProfileFastCS&);
|
||||
ProfileFastCS& operator=(const ProfileFastCS&);
|
||||
|
||||
volatile unsigned m_Flag;
|
||||
static HANDLE testEvent;
|
||||
|
||||
void ThreadSafeSetFlag()
|
||||
{
|
||||
volatile unsigned& nFlag=m_Flag;
|
||||
|
||||
#define ts_lock _emit 0xF0
|
||||
DASSERT(((unsigned)&nFlag % 4) == 0);
|
||||
|
||||
__asm mov ebx, [nFlag]
|
||||
__asm ts_lock
|
||||
__asm bts dword ptr [ebx], 0
|
||||
__asm jc The_Bit_Was_Previously_Set_So_Try_Again
|
||||
return;
|
||||
|
||||
The_Bit_Was_Previously_Set_So_Try_Again:
|
||||
// can't use SwitchToThread() here because Win9X doesn't have it!
|
||||
if (testEvent)
|
||||
::WaitForSingleObject(testEvent,1);
|
||||
__asm mov ebx, [nFlag]
|
||||
__asm ts_lock
|
||||
__asm bts dword ptr [ebx], 0
|
||||
__asm jc The_Bit_Was_Previously_Set_So_Try_Again
|
||||
}
|
||||
|
||||
void ThreadSafeClearFlag()
|
||||
{
|
||||
m_Flag=0;
|
||||
}
|
||||
|
||||
public:
|
||||
ProfileFastCS(void):
|
||||
m_Flag(0)
|
||||
{
|
||||
}
|
||||
|
||||
class Lock
|
||||
{
|
||||
Lock(const Lock&);
|
||||
Lock& operator=(const Lock&);
|
||||
|
||||
ProfileFastCS& CriticalSection;
|
||||
|
||||
public:
|
||||
Lock(ProfileFastCS& cs):
|
||||
CriticalSection(cs)
|
||||
{
|
||||
CriticalSection.ThreadSafeSetFlag();
|
||||
}
|
||||
|
||||
~Lock()
|
||||
{
|
||||
CriticalSection.ThreadSafeClearFlag();
|
||||
}
|
||||
};
|
||||
|
||||
friend class Lock;
|
||||
};
|
||||
|
||||
void *ProfileAllocMemory(unsigned numBytes);
|
||||
void *ProfileReAllocMemory(void *oldPtr, unsigned newSize);
|
||||
void ProfileFreeMemory(void *ptr);
|
||||
|
||||
__forceinline void ProfileGetTime(__int64 &t)
|
||||
{
|
||||
_asm
|
||||
{
|
||||
mov ecx,[t]
|
||||
push eax
|
||||
push edx
|
||||
rdtsc
|
||||
mov [ecx],eax
|
||||
mov [ecx+4],edx
|
||||
pop edx
|
||||
pop eax
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INTERNAL_H
|
61
GeneralsMD/Code/Libraries/Source/profile/internal_cmd.h
Normal file
61
GeneralsMD/Code/Libraries/Source/profile/internal_cmd.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/internal_cmd.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #1 $
|
||||
// $DateTime: 2003/07/09 10:57:23 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Profile module command interface
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef INTERNAL_CMD_H // Include guard
|
||||
#define INTERNAL_CMD_H
|
||||
|
||||
class ProfileCmdInterface: public DebugCmdInterface
|
||||
{
|
||||
struct Factory
|
||||
{
|
||||
ProfileResultInterface* (*func)(int, const char * const *);
|
||||
const char *name,*arg;
|
||||
};
|
||||
|
||||
static unsigned numResIf;
|
||||
static Factory *resIf;
|
||||
|
||||
unsigned numResFunc; // optimizer bug: must be declared volatile!
|
||||
ProfileResultInterface **resFunc;
|
||||
|
||||
public:
|
||||
ProfileCmdInterface(void): numResFunc(0), resFunc(0) {}
|
||||
|
||||
static void AddResultFunction(ProfileResultInterface* (*func)(int, const char * const *),
|
||||
const char *name, const char *arg);
|
||||
void RunResultFunctions(void);
|
||||
|
||||
virtual bool Execute(class Debug& dbg, const char *cmd, CommandMode cmdmode,
|
||||
unsigned argn, const char * const * argv);
|
||||
virtual void Delete(void) {}
|
||||
};
|
||||
|
||||
#endif // INTERNAL_CMD_H
|
362
GeneralsMD/Code/Libraries/Source/profile/internal_funclevel.h
Normal file
362
GeneralsMD/Code/Libraries/Source/profile/internal_funclevel.h
Normal file
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/internal_funclevel.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #4 $
|
||||
// $DateTime: 2003/08/14 13:43:29 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Function level profiling (internal header)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef INTERNAL_FUNCLEVEL_H // Include guard
|
||||
#define INTERNAL_FUNCLEVEL_H
|
||||
|
||||
class ProfileFuncLevelTracer
|
||||
{
|
||||
friend class ProfileCmdInterface;
|
||||
|
||||
// can't copy this
|
||||
ProfileFuncLevelTracer(const ProfileFuncLevelTracer&);
|
||||
ProfileFuncLevelTracer& operator=(const ProfileFuncLevelTracer&);
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
// # of simultaneous frame recordings
|
||||
MAX_FRAME_RECORDS = 4
|
||||
};
|
||||
|
||||
/// simple unique unsigned/unsigned map
|
||||
class UnsignedMap
|
||||
{
|
||||
UnsignedMap(const UnsignedMap&);
|
||||
UnsignedMap& operator=(const UnsignedMap&);
|
||||
|
||||
struct Entry
|
||||
{
|
||||
Entry *next;
|
||||
unsigned val;
|
||||
unsigned count;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
// do not make this number too large
|
||||
// a single function uses approx HASH_SIZE*20 bytes!
|
||||
HASH_SIZE = 131
|
||||
};
|
||||
|
||||
Entry *e;
|
||||
unsigned alloc,used;
|
||||
Entry *hash[HASH_SIZE];
|
||||
bool writeLock;
|
||||
|
||||
void _Insert(unsigned at, unsigned val, int countAdd);
|
||||
|
||||
public:
|
||||
UnsignedMap(void);
|
||||
~UnsignedMap();
|
||||
|
||||
void Clear(void);
|
||||
void Insert(unsigned val, int countAdd);
|
||||
unsigned Enumerate(int index);
|
||||
unsigned GetCount(int index);
|
||||
void Copy(const UnsignedMap &src);
|
||||
void MixIn(const UnsignedMap &src);
|
||||
};
|
||||
|
||||
/// profile entry
|
||||
struct Profile
|
||||
{
|
||||
/// call count
|
||||
__int64 callCount;
|
||||
|
||||
/// pure time
|
||||
__int64 tickPure;
|
||||
|
||||
/// total time
|
||||
__int64 tickTotal;
|
||||
|
||||
/// caller list
|
||||
UnsignedMap caller;
|
||||
|
||||
/// tracer for this profile
|
||||
ProfileFuncLevelTracer *tracer;
|
||||
|
||||
void Copy(const Profile &src)
|
||||
{
|
||||
callCount=src.callCount;
|
||||
tickPure=src.tickPure;
|
||||
tickTotal=src.tickTotal;
|
||||
caller.Copy(src.caller);
|
||||
}
|
||||
|
||||
void MixIn(const Profile &src)
|
||||
{
|
||||
callCount+=src.callCount;
|
||||
tickPure+=src.tickPure;
|
||||
tickTotal+=src.tickTotal;
|
||||
caller.MixIn(src.caller);
|
||||
}
|
||||
};
|
||||
|
||||
/// map of profiles
|
||||
class ProfileMap
|
||||
{
|
||||
ProfileMap(const ProfileMap&);
|
||||
ProfileMap& operator=(const ProfileMap&);
|
||||
|
||||
struct List
|
||||
{
|
||||
List *next;
|
||||
int frame;
|
||||
Profile p;
|
||||
};
|
||||
|
||||
List *root,**tail;
|
||||
|
||||
public:
|
||||
ProfileMap(void);
|
||||
~ProfileMap();
|
||||
|
||||
Profile *Find(int frame);
|
||||
void Append(int frame, const Profile &p);
|
||||
void MixIn(int frame, const Profile &p);
|
||||
};
|
||||
|
||||
/// function entry (map address -> Function)
|
||||
struct Function
|
||||
{
|
||||
/// address of this function
|
||||
unsigned addr;
|
||||
|
||||
/// global profile
|
||||
Profile glob;
|
||||
|
||||
/// profile for current frame(s)
|
||||
Profile cur[MAX_FRAME_RECORDS];
|
||||
|
||||
/// frame based profiles
|
||||
ProfileMap frame;
|
||||
|
||||
/// current call depth (for recursion)
|
||||
int depth;
|
||||
|
||||
/// function source
|
||||
char *funcSource;
|
||||
|
||||
/// function source line
|
||||
unsigned funcLine;
|
||||
|
||||
/// function name
|
||||
char *funcName;
|
||||
|
||||
Function(ProfileFuncLevelTracer *tr)
|
||||
{
|
||||
glob.tracer=tr;
|
||||
for (int k=0;k<MAX_FRAME_RECORDS;k++)
|
||||
cur[k].tracer=tr;
|
||||
funcSource=funcName=NULL;
|
||||
funcLine=0;
|
||||
}
|
||||
};
|
||||
|
||||
ProfileFuncLevelTracer(void);
|
||||
~ProfileFuncLevelTracer();
|
||||
|
||||
/**
|
||||
Enters the function at the given address.
|
||||
|
||||
@param addr function address
|
||||
@param esp current ESP value
|
||||
@param ret return address for given function
|
||||
*/
|
||||
void Enter(unsigned addr, unsigned esp, unsigned ret);
|
||||
|
||||
/**
|
||||
Leaves the function at the ESP value.
|
||||
If this does not match with the last Enter call
|
||||
the internal stack is unwound until a match is found.
|
||||
|
||||
@param esp current ESP value
|
||||
@return return address
|
||||
*/
|
||||
unsigned Leave(unsigned esp);
|
||||
|
||||
/**
|
||||
Shutdown function.
|
||||
*/
|
||||
static void Shutdown(void);
|
||||
|
||||
/**
|
||||
Starts frame based profiling, starts a new frame.
|
||||
*/
|
||||
static int FrameStart(void);
|
||||
|
||||
/**
|
||||
Ends frame based profiling.
|
||||
*/
|
||||
static void FrameEnd(int which, int mixIndex);
|
||||
|
||||
/**
|
||||
Clears all total values.
|
||||
*/
|
||||
static void ClearTotals(void);
|
||||
|
||||
/**
|
||||
Retrieves the first function level tracer.
|
||||
|
||||
\return first function level tracer
|
||||
*/
|
||||
static ProfileFuncLevelTracer *GetFirst(void)
|
||||
{
|
||||
return head;
|
||||
}
|
||||
|
||||
/**
|
||||
Retrieves next function level tracer.
|
||||
|
||||
\return next function level tracer, NULL if none
|
||||
*/
|
||||
ProfileFuncLevelTracer *GetNext(void)
|
||||
{
|
||||
return next;
|
||||
}
|
||||
|
||||
Function *FindFunction(unsigned addr)
|
||||
{
|
||||
return func.Find(addr);
|
||||
}
|
||||
|
||||
Function *EnumFunction(unsigned index)
|
||||
{
|
||||
return func.Enumerate(index);
|
||||
}
|
||||
|
||||
private:
|
||||
/// single stack entry
|
||||
struct StackEntry
|
||||
{
|
||||
/// function
|
||||
Function *func;
|
||||
|
||||
/// ESP value
|
||||
unsigned esp;
|
||||
|
||||
/// return value
|
||||
unsigned retVal;
|
||||
|
||||
/// enter tick count
|
||||
__int64 tickEnter;
|
||||
|
||||
/// time spend in called functions
|
||||
__int64 tickSubTime;
|
||||
};
|
||||
|
||||
/// map of functions
|
||||
class FunctionMap
|
||||
{
|
||||
FunctionMap(const FunctionMap&);
|
||||
FunctionMap& operator=(const FunctionMap&);
|
||||
|
||||
struct Entry
|
||||
{
|
||||
Entry *next;
|
||||
Function *funcPtr;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
HASH_SIZE = 15551
|
||||
};
|
||||
|
||||
Entry *e;
|
||||
unsigned alloc,used;
|
||||
Entry *hash[HASH_SIZE];
|
||||
|
||||
public:
|
||||
FunctionMap(void);
|
||||
~FunctionMap();
|
||||
|
||||
Function *Find(unsigned addr);
|
||||
void Insert(Function *funcPtr);
|
||||
Function *Enumerate(int index);
|
||||
};
|
||||
|
||||
/// head of list
|
||||
static ProfileFuncLevelTracer *head;
|
||||
|
||||
/// next tracer object
|
||||
ProfileFuncLevelTracer *next;
|
||||
|
||||
/// are we in shutdown mode?
|
||||
static bool shuttingDown;
|
||||
|
||||
/// stack
|
||||
StackEntry *stack;
|
||||
|
||||
/// number of used entries
|
||||
int usedStack;
|
||||
|
||||
/// total number of entries
|
||||
int totalStack;
|
||||
|
||||
/// max call depth
|
||||
int maxDepth;
|
||||
|
||||
/// current frame
|
||||
static int curFrame;
|
||||
|
||||
/// function map
|
||||
FunctionMap func;
|
||||
|
||||
/// bit mask, currently recording which cur[] entries?
|
||||
static unsigned frameRecordMask;
|
||||
|
||||
/// record caller information?
|
||||
static bool recordCaller;
|
||||
};
|
||||
|
||||
inline void ProfileFuncLevelTracer::UnsignedMap::Insert(unsigned val, int countAdd)
|
||||
{
|
||||
// in hash?
|
||||
unsigned at=(val/16)%HASH_SIZE;
|
||||
for (Entry *e=hash[at];e;e=e->next)
|
||||
if (e->val==val)
|
||||
{
|
||||
e->count+=countAdd;
|
||||
return;
|
||||
}
|
||||
_Insert(at,val,countAdd);
|
||||
}
|
||||
|
||||
inline ProfileFuncLevelTracer::Function *ProfileFuncLevelTracer::FunctionMap::Find(unsigned addr)
|
||||
{
|
||||
for (Entry *e=hash[(addr/16)%HASH_SIZE];e;e=e->next)
|
||||
if (e->funcPtr->addr==addr)
|
||||
return e->funcPtr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // INTERNAL_FUNCLEVEL_H
|
247
GeneralsMD/Code/Libraries/Source/profile/internal_highlevel.h
Normal file
247
GeneralsMD/Code/Libraries/Source/profile/internal_highlevel.h
Normal file
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/internal_highlevel.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #2 $
|
||||
// $DateTime: 2003/08/14 13:43:29 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// High level profiling (internal header)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef INTERNAL_HIGHLEVEL_H // Include guard
|
||||
#define INTERNAL_HIGHLEVEL_H
|
||||
|
||||
/// an internal high level profile ID
|
||||
class ProfileId
|
||||
{
|
||||
ProfileId(const ProfileId&);
|
||||
ProfileId& operator=(const ProfileId&);
|
||||
|
||||
public:
|
||||
/**
|
||||
Creates a new high level profile ID.
|
||||
|
||||
\param name profile name
|
||||
\param descr descriptive name
|
||||
\param unit unit name
|
||||
\param precision number of decimal places to show
|
||||
\param exp10 10 base exponent (used for scaleing)
|
||||
*/
|
||||
ProfileId(const char *name, const char *descr, const char *unit, int precision, int exp10);
|
||||
|
||||
/**
|
||||
Retrieves the first profile ID.
|
||||
|
||||
\return first profile ID
|
||||
*/
|
||||
static ProfileId *GetFirst(void) { return first; }
|
||||
|
||||
/**
|
||||
Retrieves next profile ID.
|
||||
|
||||
\return next profile ID, NULL if none
|
||||
*/
|
||||
ProfileId *GetNext(void) const { return m_next; }
|
||||
|
||||
/**
|
||||
Retrieves name of the profile ID.
|
||||
|
||||
\return profile ID name
|
||||
*/
|
||||
const char *GetName(void) const { return m_name; }
|
||||
|
||||
/**
|
||||
Retrieves unit name of the profile ID.
|
||||
|
||||
\return profile ID unit name
|
||||
*/
|
||||
const char *GetUnit(void) const { return m_unit?m_unit:""; }
|
||||
|
||||
/**
|
||||
Retrieves description of the profile ID.
|
||||
|
||||
\return profile description
|
||||
*/
|
||||
const char *GetDescr(void) const { return m_descr?m_descr:""; }
|
||||
|
||||
/**
|
||||
Increments the profile value.
|
||||
|
||||
\param add add value
|
||||
*/
|
||||
void Increment(double add);
|
||||
|
||||
/**
|
||||
Sets a new maximum value.
|
||||
|
||||
\param max new maximum value
|
||||
*/
|
||||
void Maximum(double max);
|
||||
|
||||
/**
|
||||
Returns current value, internally resetting it.
|
||||
|
||||
\return current value
|
||||
*/
|
||||
double GetCurrentValue(void)
|
||||
{
|
||||
double help=m_curVal;
|
||||
m_curVal=0.;
|
||||
return help;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns total value
|
||||
|
||||
\return total value
|
||||
*/
|
||||
double GetTotalValue(void) const
|
||||
{
|
||||
return m_totalVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns value at the given frame.
|
||||
|
||||
\param frame frame number
|
||||
\param value value at frame
|
||||
\return true if frame found, false if not
|
||||
*/
|
||||
bool GetFrameValue(unsigned frame, double &value) const
|
||||
{
|
||||
if (frame<(unsigned)m_firstFrame||
|
||||
frame>=(unsigned)curFrame)
|
||||
return false;
|
||||
value=m_recFrameVal[frame-m_firstFrame];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Translate given numeric value into a string, using an internal temp buffer.
|
||||
|
||||
\param v value
|
||||
\return given numeric value as string
|
||||
*/
|
||||
const char *AsString(double v) const;
|
||||
|
||||
/**
|
||||
Shutdown function.
|
||||
*/
|
||||
static void Shutdown(void);
|
||||
|
||||
/**
|
||||
Starts frame based profiling, starts a new frame.
|
||||
*/
|
||||
static int FrameStart(void);
|
||||
|
||||
/**
|
||||
Ends frame based profiling.
|
||||
*/
|
||||
static void FrameEnd(int which, int mixIndex);
|
||||
|
||||
/**
|
||||
Clears all total values.
|
||||
*/
|
||||
static void ClearTotals(void)
|
||||
{
|
||||
for (ProfileId *cur=first;cur;cur=cur->m_next)
|
||||
cur->m_totalVal=0.;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
ProfileId's can't be destructed.
|
||||
*/
|
||||
~ProfileId() {}
|
||||
|
||||
enum
|
||||
{
|
||||
/// # of simultaneous frame recordings
|
||||
MAX_FRAME_RECORDS = 4,
|
||||
|
||||
/// size of internal string buffer
|
||||
STRING_BUFFER_SIZE = 1024
|
||||
};
|
||||
|
||||
/// possible value modes
|
||||
enum ValueMode
|
||||
{
|
||||
Unknown,
|
||||
ModeIncrement,
|
||||
ModeMaximum
|
||||
};
|
||||
|
||||
/// first profile ID
|
||||
static ProfileId *first;
|
||||
|
||||
/// next profile ID
|
||||
ProfileId *m_next;
|
||||
|
||||
/// profile name
|
||||
char *m_name;
|
||||
|
||||
/// profile description
|
||||
char *m_descr;
|
||||
|
||||
/// profile unit
|
||||
char *m_unit;
|
||||
|
||||
/// number of decimal places to show
|
||||
int m_precision;
|
||||
|
||||
/// 10 base exponent (used for scaleing)
|
||||
int m_exp10;
|
||||
|
||||
/// current value
|
||||
double m_curVal;
|
||||
|
||||
/// total value
|
||||
double m_totalVal;
|
||||
|
||||
/// frame values
|
||||
double m_frameVal[MAX_FRAME_RECORDS];
|
||||
|
||||
/// list of recorded frame values
|
||||
double *m_recFrameVal;
|
||||
|
||||
/// index of first recorded frame
|
||||
int m_firstFrame;
|
||||
|
||||
/// value mode
|
||||
ValueMode m_valueMode;
|
||||
|
||||
/// current frame
|
||||
static int curFrame;
|
||||
|
||||
/// bit mask, currently recording which cur[] entries?
|
||||
static unsigned frameRecordMask;
|
||||
|
||||
/// internal string buffer
|
||||
static char stringBuf[STRING_BUFFER_SIZE];
|
||||
|
||||
/// next unused char in string buffer
|
||||
static unsigned stringBufUnused;
|
||||
};
|
||||
|
||||
#endif // INTERNAL_HIGHLEVEL_H
|
102
GeneralsMD/Code/Libraries/Source/profile/internal_result.h
Normal file
102
GeneralsMD/Code/Libraries/Source/profile/internal_result.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/internal_result.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #1 $
|
||||
// $DateTime: 2003/07/09 10:57:23 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Internal result functions
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef INTERNAL_RESULT_H // Include guard
|
||||
#define INTERNAL_RESULT_H
|
||||
|
||||
/// \brief Simple CSV format flat file result function, for all threads.
|
||||
class ProfileResultFileCSV: public ProfileResultInterface
|
||||
{
|
||||
ProfileResultFileCSV(void) {}
|
||||
|
||||
void WriteThread(ProfileFuncLevel::Thread &thread);
|
||||
|
||||
public:
|
||||
static ProfileResultInterface *Create(int argn, const char * const *);
|
||||
virtual const char *GetName(void) const { return "file_csv"; }
|
||||
virtual void WriteResults(void);
|
||||
virtual void Delete(void);
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Write out DOT file for calling hierarchy.
|
||||
|
||||
The frame name and the file name must be specified when creating an
|
||||
instance of this result function. The result function will always pick
|
||||
the thread with the highest function count (which is usually the
|
||||
main thread).
|
||||
|
||||
\note A DOT file is used with the DOT tool from the GraphViz package
|
||||
for generating directed graphs, e.g. by issuing dot -Tgif -ograph.gif profile.dot
|
||||
*/
|
||||
class ProfileResultFileDOT: public ProfileResultInterface
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MAX_FUNCTIONS_PER_FILE = 200
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Creates a class instance.
|
||||
|
||||
\param fileName name of DOT file to generate (defaults to profile.dot)
|
||||
\param frameName name of frame to use (NULL for global)
|
||||
\param foldThreshold if the number of functions exceeds the given threshold
|
||||
then all functions belonging to the same source file
|
||||
will be folded into a single entry
|
||||
\return new instance
|
||||
*/
|
||||
static ProfileResultInterface *Create(int argn, const char * const *);
|
||||
|
||||
virtual const char *GetName(void) const { return "file_dot"; }
|
||||
virtual void WriteResults(void);
|
||||
virtual void Delete(void);
|
||||
|
||||
private:
|
||||
|
||||
ProfileResultFileDOT(const char *fileName, const char *frameName, int foldThreshold);
|
||||
|
||||
struct FoldHelper
|
||||
{
|
||||
FoldHelper *next;
|
||||
const char *source;
|
||||
ProfileFuncLevel::Id id[MAX_FUNCTIONS_PER_FILE];
|
||||
unsigned numId;
|
||||
bool mark;
|
||||
};
|
||||
|
||||
char *m_fileName;
|
||||
char *m_frameName;
|
||||
int m_foldThreshold;
|
||||
};
|
||||
|
||||
#endif // INTERNAL_RESULT_H
|
382
GeneralsMD/Code/Libraries/Source/profile/profile.cpp
Normal file
382
GeneralsMD/Code/Libraries/Source/profile/profile.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile.cpp $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #6 $
|
||||
// $DateTime: 2003/08/14 13:43:29 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Profile module main code
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "_pch.h"
|
||||
#include <new>
|
||||
#include "mmsystem.h"
|
||||
|
||||
#pragma comment (lib,"winmm")
|
||||
|
||||
// yuk, I'm doing this so weird because the destructor
|
||||
// of cmd must never be called...
|
||||
static ProfileCmdInterface &cmd=*(ProfileCmdInterface *)new
|
||||
(ProfileAllocMemory(sizeof(ProfileCmdInterface))) ProfileCmdInterface();
|
||||
|
||||
// we have this here so that our command interface will always
|
||||
// be linked in as well...
|
||||
static bool __RegisterDebugCmdGroup_Profile=Debug::AddCommands("profile",&cmd);
|
||||
|
||||
void *ProfileAllocMemory(unsigned numBytes)
|
||||
{
|
||||
HGLOBAL h=GlobalAlloc(GMEM_FIXED,numBytes);
|
||||
if (!h)
|
||||
DCRASH_RELEASE("Debug mem alloc failed");
|
||||
return (void *)h;
|
||||
}
|
||||
|
||||
void *ProfileReAllocMemory(void *oldPtr, unsigned newSize)
|
||||
{
|
||||
// Windows doesn't like ReAlloc with NULL handle/ptr...
|
||||
if (!oldPtr)
|
||||
return newSize?ProfileAllocMemory(newSize):0;
|
||||
|
||||
// Shrinking to 0 size is basically freeing memory
|
||||
if (!newSize)
|
||||
{
|
||||
GlobalFree((HGLOBAL)oldPtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// now try GlobalReAlloc first
|
||||
HGLOBAL h=GlobalReAlloc((HGLOBAL)oldPtr,newSize,0);
|
||||
if (!h)
|
||||
{
|
||||
// this failed (Windows doesn't like ReAlloc'ing larger
|
||||
// fixed memory blocks) - go with Alloc/Free instead
|
||||
h=GlobalAlloc(GMEM_FIXED,newSize);
|
||||
if (!h)
|
||||
DCRASH_RELEASE("Debug mem realloc failed");
|
||||
unsigned oldSize=GlobalSize((HGLOBAL)oldPtr);
|
||||
memcpy((void *)h,oldPtr,oldSize<newSize?oldSize:newSize);
|
||||
GlobalFree((HGLOBAL)oldPtr);
|
||||
}
|
||||
|
||||
return (void *)h;
|
||||
}
|
||||
|
||||
void ProfileFreeMemory(void *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
GlobalFree((HGLOBAL)ptr);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static _int64 GetClockCyclesFast(void)
|
||||
{
|
||||
// this is where we're adding our internal result functions
|
||||
Profile::AddResultFunction(ProfileResultFileCSV::Create,
|
||||
"file_csv",
|
||||
"");
|
||||
Profile::AddResultFunction(ProfileResultFileCSV::Create,
|
||||
"file_dot",
|
||||
"[ file [ frame_name [ fold_threshold ] ] ]");
|
||||
|
||||
// this must not take a very huge CPU hit...
|
||||
|
||||
// measure clock cycles 3 times for 20 msec each
|
||||
// then take the 2 counts that are closest, average
|
||||
_int64 n[3];
|
||||
for (int k=0;k<3;k++)
|
||||
{
|
||||
// wait for end of current tick
|
||||
unsigned timeEnd=timeGetTime()+2;
|
||||
while (timeGetTime()<timeEnd);
|
||||
|
||||
// get cycles
|
||||
_int64 start,startQPC,endQPC;
|
||||
QueryPerformanceCounter((LARGE_INTEGER *)&startQPC);
|
||||
ProfileGetTime(start);
|
||||
timeEnd+=20;
|
||||
while (timeGetTime()<timeEnd);
|
||||
ProfileGetTime(n[k]);
|
||||
n[k]-=start;
|
||||
|
||||
// convert to 1 second
|
||||
if (QueryPerformanceCounter((LARGE_INTEGER *)&endQPC))
|
||||
{
|
||||
_int64 freq;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
|
||||
n[k]=(n[k]*freq)/(endQPC-startQPC);
|
||||
}
|
||||
else
|
||||
{
|
||||
n[k]=(n[k]*1000)/20;
|
||||
}
|
||||
}
|
||||
|
||||
// find two closest values
|
||||
_int64 d01=n[1]-n[0],d02=n[2]-n[0],d12=n[2]-n[1];
|
||||
if (d01<0) d01=-d01;
|
||||
if (d02<0) d02=-d02;
|
||||
if (d12<0) d12=-d12;
|
||||
_int64 avg;
|
||||
if (d01<d02)
|
||||
{
|
||||
avg=d01<d12?n[0]+n[1]:n[1]+n[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
avg=d02<d12?n[0]+n[2]:n[1]+n[2];
|
||||
}
|
||||
|
||||
// return result
|
||||
// (rounded to the next MHz)
|
||||
return ((avg/2+500000)/1000000)*1000000;
|
||||
}
|
||||
|
||||
unsigned Profile::m_rec;
|
||||
char **Profile::m_recNames;
|
||||
unsigned Profile::m_names;
|
||||
Profile::FrameName *Profile::m_frameNames;
|
||||
_int64 Profile::m_clockCycles=GetClockCyclesFast();
|
||||
Profile::PatternListEntry *Profile::firstPatternEntry;
|
||||
Profile::PatternListEntry *Profile::lastPatternEntry;
|
||||
|
||||
void Profile::StartRange(const char *range)
|
||||
{
|
||||
// set default
|
||||
if (!range)
|
||||
range="frame";
|
||||
|
||||
// known name?
|
||||
for (unsigned k=0;k<m_names;++k)
|
||||
if (!strcmp(range,m_frameNames[k].name))
|
||||
break;
|
||||
if (k==m_names)
|
||||
{
|
||||
// no, must add to list
|
||||
m_frameNames=(FrameName *)ProfileReAllocMemory(m_frameNames,(++m_names)*sizeof(FrameName));
|
||||
m_frameNames[k].name=(char *)ProfileAllocMemory(strlen(range)+1);
|
||||
strcpy(m_frameNames[k].name,range);
|
||||
m_frameNames[k].frames=0;
|
||||
m_frameNames[k].isRecording=false;
|
||||
m_frameNames[k].doAppend=false;
|
||||
m_frameNames[k].lastGlobalIndex=-1;
|
||||
}
|
||||
|
||||
// stop old recording?
|
||||
if (m_frameNames[k].isRecording)
|
||||
StopRange(range);
|
||||
|
||||
// start new recording
|
||||
m_frameNames[k].isRecording=true;
|
||||
m_frameNames[k].doAppend=false;
|
||||
|
||||
// but check first: is recording enabled?
|
||||
bool active=false;
|
||||
for (PatternListEntry *cur=firstPatternEntry;cur;cur=cur->next)
|
||||
{
|
||||
if (SimpleMatch(range,cur->pattern))
|
||||
active=cur->isActive;
|
||||
}
|
||||
|
||||
if (active)
|
||||
{
|
||||
#ifdef _PROFILE
|
||||
m_frameNames[k].funcIndex=ProfileFuncLevelTracer::FrameStart();
|
||||
DASSERT(m_frameNames[k].funcIndex>=0);
|
||||
#endif
|
||||
m_frameNames[k].highIndex=ProfileId::FrameStart();
|
||||
DASSERT(m_frameNames[k].highIndex>=0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_frameNames[k].funcIndex=-1;
|
||||
m_frameNames[k].highIndex=-1;
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::AppendRange(const char *range)
|
||||
{
|
||||
// set default
|
||||
if (!range)
|
||||
range="frame";
|
||||
|
||||
// known name?
|
||||
for (unsigned k=0;k<m_names;++k)
|
||||
if (!strcmp(range,m_frameNames[k].name))
|
||||
break;
|
||||
if (k==m_names)
|
||||
{
|
||||
// no, so StartRange will do the job for us
|
||||
StartRange(range);
|
||||
return;
|
||||
}
|
||||
|
||||
// still recording?
|
||||
if (m_frameNames[k].isRecording)
|
||||
// don't do anything
|
||||
return;
|
||||
|
||||
// start new recording
|
||||
m_frameNames[k].isRecording=true;
|
||||
m_frameNames[k].doAppend=true;
|
||||
|
||||
// but check first: is recording enabled?
|
||||
bool active=false;
|
||||
for (PatternListEntry *cur=firstPatternEntry;cur;cur=cur->next)
|
||||
{
|
||||
if (SimpleMatch(range,cur->pattern))
|
||||
active=cur->isActive;
|
||||
}
|
||||
|
||||
if (active)
|
||||
{
|
||||
#ifdef _PROFILE
|
||||
m_frameNames[k].funcIndex=ProfileFuncLevelTracer::FrameStart();
|
||||
DASSERT(m_frameNames[k].funcIndex>=0);
|
||||
#endif
|
||||
m_frameNames[k].highIndex=ProfileId::FrameStart();
|
||||
DASSERT(m_frameNames[k].highIndex>=0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_frameNames[k].funcIndex=-1;
|
||||
m_frameNames[k].highIndex=-1;
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::StopRange(const char *range)
|
||||
{
|
||||
// set default
|
||||
if (!range)
|
||||
range="frame";
|
||||
|
||||
// known name?
|
||||
for (unsigned k=0;k<m_names;++k)
|
||||
if (!strcmp(range,m_frameNames[k].name))
|
||||
break;
|
||||
DFAIL_IF(k==m_names) return;
|
||||
DFAIL_IF(!m_frameNames[k].isRecording) return;
|
||||
|
||||
// stop recording
|
||||
m_frameNames[k].isRecording=false;
|
||||
if (
|
||||
#ifdef _PROFILE
|
||||
m_frameNames[k].funcIndex>=0 ||
|
||||
#endif
|
||||
m_frameNames[k].highIndex>=0
|
||||
)
|
||||
{
|
||||
// add to list of known frames?
|
||||
int atIndex;
|
||||
if (!m_frameNames[k].doAppend||
|
||||
m_frameNames[k].lastGlobalIndex<0)
|
||||
{
|
||||
atIndex=-1;
|
||||
m_frameNames[k].lastGlobalIndex=m_rec;
|
||||
m_recNames=(char **)ProfileReAllocMemory(m_recNames,(m_rec+1)*sizeof(char *));
|
||||
m_recNames[m_rec]=(char *)ProfileAllocMemory(strlen(range)+1+6);
|
||||
wsprintf(m_recNames[m_rec++],"%s:%i",range,++m_frameNames[k].frames);
|
||||
}
|
||||
else
|
||||
atIndex=m_frameNames[k].lastGlobalIndex;
|
||||
#ifdef _PROFILE
|
||||
if (m_frameNames[k].funcIndex>=0)
|
||||
ProfileFuncLevelTracer::FrameEnd(m_frameNames[k].funcIndex,atIndex);
|
||||
if (m_frameNames[k].highIndex>=0)
|
||||
#endif
|
||||
ProfileId::FrameEnd(m_frameNames[k].highIndex,atIndex);
|
||||
}
|
||||
}
|
||||
|
||||
bool Profile::IsEnabled(void)
|
||||
{
|
||||
for (unsigned k=0;k<m_names;++k)
|
||||
if (m_frameNames[k].isRecording)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned Profile::GetFrameCount(void)
|
||||
{
|
||||
return m_rec;
|
||||
}
|
||||
|
||||
const char *Profile::GetFrameName(unsigned frame)
|
||||
{
|
||||
return frame>=m_rec?NULL:m_recNames[frame];
|
||||
}
|
||||
|
||||
void Profile::ClearTotals(void)
|
||||
{
|
||||
#ifdef _PROFILE
|
||||
ProfileFuncLevelTracer::ClearTotals();
|
||||
#endif
|
||||
ProfileId::ClearTotals();
|
||||
}
|
||||
|
||||
_int64 Profile::GetClockCyclesPerSecond(void)
|
||||
{
|
||||
return m_clockCycles;
|
||||
}
|
||||
|
||||
void Profile::AddResultFunction(ProfileResultInterface* (*func)(int, const char * const *),
|
||||
const char *name, const char *arg)
|
||||
{
|
||||
ProfileCmdInterface::AddResultFunction(func,name,arg);
|
||||
}
|
||||
|
||||
bool Profile::SimpleMatch(const char *str, const char *pattern)
|
||||
{
|
||||
DASSERT(str);
|
||||
DASSERT(pattern);
|
||||
while (*str&&*pattern)
|
||||
{
|
||||
if (*pattern=='*')
|
||||
{
|
||||
pattern++;
|
||||
while (*str)
|
||||
if (SimpleMatch(str++,pattern))
|
||||
return true;
|
||||
return *str==*pattern;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*str++!=*pattern++)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return *str==*pattern;
|
||||
}
|
||||
|
||||
static void ProfileShutdown(void)
|
||||
{
|
||||
#ifdef _PROFILE
|
||||
ProfileFuncLevelTracer::Shutdown();
|
||||
#endif
|
||||
ProfileId::Shutdown();
|
||||
|
||||
DLOG("CPU speed is " << unsigned(Profile::GetClockCyclesPerSecond()) << " Hz.\n");
|
||||
|
||||
cmd.RunResultFunctions();
|
||||
}
|
||||
|
||||
int profileTracerInit=atexit(ProfileShutdown);
|
208
GeneralsMD/Code/Libraries/Source/profile/profile.dox
Normal file
208
GeneralsMD/Code/Libraries/Source/profile/profile.dox
Normal file
@@ -0,0 +1,208 @@
|
||||
# Doxyfile 1.3.1
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# General configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
PROJECT_NAME = "EA/Profile module"
|
||||
PROJECT_NUMBER =
|
||||
OUTPUT_DIRECTORY = doc/
|
||||
OUTPUT_LANGUAGE = English
|
||||
USE_WINDOWS_ENCODING = YES
|
||||
EXTRACT_ALL = NO
|
||||
EXTRACT_PRIVATE = NO
|
||||
EXTRACT_STATIC = YES
|
||||
EXTRACT_LOCAL_CLASSES = YES
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
HIDE_FRIEND_COMPOUNDS = YES
|
||||
HIDE_IN_BODY_DOCS = YES
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = YES
|
||||
FULL_PATH_NAMES = NO
|
||||
STRIP_FROM_PATH =
|
||||
INTERNAL_DOCS = NO
|
||||
CASE_SENSE_NAMES = YES
|
||||
SHORT_NAMES = NO
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
SHOW_INCLUDE_FILES = YES
|
||||
JAVADOC_AUTOBRIEF = NO
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
DETAILS_AT_TOP = NO
|
||||
INHERIT_DOCS = YES
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
TAB_SIZE = 8
|
||||
GENERATE_TODOLIST = NO
|
||||
GENERATE_TESTLIST = YES
|
||||
GENERATE_BUGLIST = YES
|
||||
GENERATE_DEPRECATEDLIST= YES
|
||||
ALIASES = "todo_opt=\todo"
|
||||
ALIASES += "todo_urgent=\todo"
|
||||
ALIASES += "todo_feature=\todo"
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
SHOW_USED_FILES = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to warning and progress messages
|
||||
#---------------------------------------------------------------------------
|
||||
QUIET = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
WARN_LOGFILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = ./
|
||||
FILE_PATTERNS = profile*.h
|
||||
RECURSIVE = NO
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS =
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS =
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
SOURCE_BROWSER = NO
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCES_RELATION = YES
|
||||
VERBATIM_HEADERS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
ALPHABETICAL_INDEX = YES
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
IGNORE_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the HTML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_HTML = YES
|
||||
HTML_OUTPUT = html
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER =
|
||||
HTML_FOOTER =
|
||||
HTML_STYLESHEET =
|
||||
HTML_ALIGN_MEMBERS = YES
|
||||
GENERATE_HTMLHELP = YES
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
GENERATE_CHI = NO
|
||||
BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
DISABLE_INDEX = NO
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
GENERATE_TREEVIEW = YES
|
||||
TREEVIEW_WIDTH = 250
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the LaTeX output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_LATEX = NO
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = a4wide
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
PDF_HYPERLINKS = NO
|
||||
USE_PDFLATEX = NO
|
||||
LATEX_BATCHMODE = NO
|
||||
LATEX_HIDE_INDICES = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_RTF = YES
|
||||
RTF_OUTPUT = rtf
|
||||
COMPACT_RTF = NO
|
||||
RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_MAN = NO
|
||||
MAN_OUTPUT = man
|
||||
MAN_EXTENSION = .3
|
||||
MAN_LINKS = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the XML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_XML = NO
|
||||
XML_OUTPUT = xml
|
||||
XML_SCHEMA =
|
||||
XML_DTD =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_PERLMOD = NO
|
||||
PERLMOD_LATEX = NO
|
||||
PERLMOD_PRETTY = YES
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = NO
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED = _DEBUG \
|
||||
DOXYGEN
|
||||
EXPAND_AS_DEFINED =
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::addtions related to external references
|
||||
#---------------------------------------------------------------------------
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE =
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
PERL_PATH = /usr/bin/perl
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
CLASS_DIAGRAMS = YES
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = YES
|
||||
CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = NO
|
||||
TEMPLATE_RELATIONS = NO
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DOT_IMAGE_FORMAT = png
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
MAX_DOT_GRAPH_WIDTH = 1024
|
||||
MAX_DOT_GRAPH_HEIGHT = 1024
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
GENERATE_LEGEND = YES
|
||||
DOT_CLEANUP = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::addtions related to the search engine
|
||||
#---------------------------------------------------------------------------
|
||||
SEARCHENGINE = NO
|
||||
CGI_NAME = search.cgi
|
||||
CGI_URL =
|
||||
DOC_URL =
|
||||
DOC_ABSPATH =
|
||||
BIN_ABSPATH = /usr/local/bin/
|
||||
EXT_DOC_PATHS =
|
289
GeneralsMD/Code/Libraries/Source/profile/profile.dsp
Normal file
289
GeneralsMD/Code/Libraries/Source/profile/profile.dsp
Normal file
@@ -0,0 +1,289 @@
|
||||
# Microsoft Developer Studio Project File - Name="profile" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
||||
|
||||
CFG=profile - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "profile.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "profile.mak" CFG="profile - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "profile - Win32 Release" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "profile - Win32 Debug" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "profile - Win32 Internal" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "profile - Win32 Profile" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "profile"
|
||||
# PROP Scc_LocalPath "."
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "profile - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
||||
# ADD CPP /nologo /G6 /MD /W3 /WX /Zi /Ot /Og /Oi /Oy /Ob2 /Gy /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Yu"_pch.h" /FD /GF /Gs /c
|
||||
# SUBTRACT CPP /Oa
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
# Begin Special Build Tool
|
||||
SOURCE="$(InputPath)"
|
||||
PostBuild_Desc=copying
|
||||
PostBuild_Cmds=copy release\profile.lib ..\..\lib
|
||||
# End Special Build Tool
|
||||
|
||||
!ELSEIF "$(CFG)" == "profile - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /G6 /MDd /W3 /WX /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Yu"_pch.h" /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo /out:"Debug\profiledebug.lib"
|
||||
# Begin Special Build Tool
|
||||
SOURCE="$(InputPath)"
|
||||
PostBuild_Desc=copying
|
||||
PostBuild_Cmds=copy debug\profiledebug.lib ..\..\lib
|
||||
# End Special Build Tool
|
||||
|
||||
!ELSEIF "$(CFG)" == "profile - Win32 Internal"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Internal"
|
||||
# PROP BASE Intermediate_Dir "Internal"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Internal"
|
||||
# PROP Intermediate_Dir "Internal"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /G6 /MD /W3 /WX /Zi /Ot /Oa /Og /Oi /Oy /Ob2 /Gy /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GF /Gs /c
|
||||
# ADD CPP /nologo /G6 /MD /W3 /WX /Zi /Ot /Og /Oi /Oy /Ob2 /Gy /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "_INTERNAL" /Yu"_pch.h" /FD /GF /Gs /c
|
||||
# SUBTRACT CPP /Oa
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo /out:"Internal\profileinternal.lib"
|
||||
# Begin Special Build Tool
|
||||
SOURCE="$(InputPath)"
|
||||
PostBuild_Desc=copying
|
||||
PostBuild_Cmds=copy internal\profileinternal.lib ..\..\lib
|
||||
# End Special Build Tool
|
||||
|
||||
!ELSEIF "$(CFG)" == "profile - Win32 Profile"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Profile"
|
||||
# PROP BASE Intermediate_Dir "Profile"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Profile"
|
||||
# PROP Intermediate_Dir "Profile"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /G6 /MD /W3 /WX /Zi /Ot /Oa /Og /Oi /Oy /Ob2 /Gy /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GF /Gs /c
|
||||
# ADD CPP /nologo /G6 /MD /W3 /WX /Zi /Ot /Og /Oi /Oy /Ob2 /Gy /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D "_PROFILE" /Yu"_pch.h" /FD /GF /Gs /c
|
||||
# SUBTRACT CPP /Oa /Ow
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo /out:"Profile\profileprofile.lib"
|
||||
# Begin Special Build Tool
|
||||
SOURCE="$(InputPath)"
|
||||
PostBuild_Desc=copying
|
||||
PostBuild_Cmds=copy profile\profileprofile.lib ..\..\lib
|
||||
# End Special Build Tool
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "profile - Win32 Release"
|
||||
# Name "profile - Win32 Debug"
|
||||
# Name "profile - Win32 Internal"
|
||||
# Name "profile - Win32 Profile"
|
||||
# Begin Group "Precompiled header"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\_pch.cpp
|
||||
# ADD CPP /Yc"_pch.h"
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\_pch.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Doxygen"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\compile_doxygen.bat
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile.dox
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile_priv.dox
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Function level"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\internal_funclevel.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile_funclevel.cpp
|
||||
|
||||
!IF "$(CFG)" == "profile - Win32 Release"
|
||||
|
||||
!ELSEIF "$(CFG)" == "profile - Win32 Debug"
|
||||
|
||||
!ELSEIF "$(CFG)" == "profile - Win32 Internal"
|
||||
|
||||
!ELSEIF "$(CFG)" == "profile - Win32 Profile"
|
||||
|
||||
# ADD CPP /FAcs
|
||||
|
||||
!ENDIF
|
||||
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile_funclevel.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "High level"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\internal_highlevel.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile_highlevel.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile_highlevel.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Command interface"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\internal_cmd.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile_cmd.cpp
|
||||
|
||||
!IF "$(CFG)" == "profile - Win32 Release"
|
||||
|
||||
!ELSEIF "$(CFG)" == "profile - Win32 Debug"
|
||||
|
||||
!ELSEIF "$(CFG)" == "profile - Win32 Internal"
|
||||
|
||||
!ELSEIF "$(CFG)" == "profile - Win32 Profile"
|
||||
|
||||
# ADD CPP /FAcs
|
||||
# SUBTRACT CPP /Oa /Ow
|
||||
|
||||
!ENDIF
|
||||
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Result functions"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\internal_result.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile_result.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile_result.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\internal.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\profile_doc.h
|
||||
# End Source File
|
||||
# End Target
|
||||
# End Project
|
75
GeneralsMD/Code/Libraries/Source/profile/profile.dsw
Normal file
75
GeneralsMD/Code/Libraries/Source/profile/profile.dsw
Normal file
@@ -0,0 +1,75 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "debug"=..\debug\debug.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
Perforce Project
|
||||
..\debug
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "profile"=.\profile.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
Perforce Project
|
||||
.
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "test1"=.\test1\test1.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
Perforce Project
|
||||
.\test1
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
Begin Project Dependency
|
||||
Project_Dep_Name profile
|
||||
End Project Dependency
|
||||
Begin Project Dependency
|
||||
Project_Dep_Name debug
|
||||
End Project Dependency
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
Perforce Project
|
||||
.
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
225
GeneralsMD/Code/Libraries/Source/profile/profile.h
Normal file
225
GeneralsMD/Code/Libraries/Source/profile/profile.h
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #4 $
|
||||
// $DateTime: 2003/08/14 13:43:29 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Profiling module
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef PROFILE_H // Include guard
|
||||
#define PROFILE_H
|
||||
|
||||
#if defined(_DEBUG) && defined(_INTERNAL)
|
||||
#error "Only either _DEBUG or _INTERNAL should ever be defined"
|
||||
#endif
|
||||
|
||||
// Define which libraries to use.
|
||||
#if defined(_INTERNAL)
|
||||
# pragma comment (lib,"profileinternal.lib")
|
||||
#elif defined(_DEBUG)
|
||||
# pragma comment (lib,"profiledebug.lib")
|
||||
#elif defined(_PROFILE)
|
||||
# pragma comment (lib,"profileprofile.lib")
|
||||
#else
|
||||
# pragma comment (lib,"profile.lib")
|
||||
#endif
|
||||
|
||||
// include all our public header files (use double quotes here)
|
||||
#include "profile_doc.h"
|
||||
#include "profile_highlevel.h"
|
||||
#include "profile_funclevel.h"
|
||||
#include "profile_result.h"
|
||||
|
||||
/**
|
||||
\brief Functions common to both profilers.
|
||||
*/
|
||||
class Profile
|
||||
{
|
||||
friend class ProfileCmdInterface;
|
||||
|
||||
// nobody can construct this class
|
||||
Profile();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
\brief Starts range recording.
|
||||
|
||||
\param range name of range to record, ==NULL for "frame"
|
||||
*/
|
||||
static void StartRange(const char *range=0);
|
||||
|
||||
/**
|
||||
\brief Appends profile data to the last recorded frame
|
||||
of the given range.
|
||||
|
||||
\param range name of range to record, ==NULL for "frame"
|
||||
*/
|
||||
static void AppendRange(const char *range=0);
|
||||
|
||||
/**
|
||||
\brief Stops range recording.
|
||||
|
||||
\note After this call the recorded range data will be available
|
||||
as a new range frame.
|
||||
|
||||
\param range name of range to record, ==NULL for "frame"
|
||||
*/
|
||||
static void StopRange(const char *range=0);
|
||||
|
||||
/**
|
||||
\brief Determines if any range recording is enabled or not.
|
||||
|
||||
\return true if range profiling is enabled, false if not
|
||||
*/
|
||||
static bool IsEnabled(void);
|
||||
|
||||
/**
|
||||
\brief Determines the number of known (recorded) range frames.
|
||||
|
||||
Note that if function level profiling is enabled then the number
|
||||
of recorded high level frames is the same as the number of recorded
|
||||
function level frames.
|
||||
|
||||
\return number of recorded range frames
|
||||
*/
|
||||
static unsigned GetFrameCount(void);
|
||||
|
||||
/**
|
||||
\brief Determines the range name of a recorded range frame.
|
||||
|
||||
\note A unique number will be added to the frame name, separated by
|
||||
a ':', e.g. 'frame:3'
|
||||
|
||||
\param frame number of recorded frame
|
||||
\return range name
|
||||
*/
|
||||
static const char *GetFrameName(unsigned frame);
|
||||
|
||||
/**
|
||||
\brief Resets all 'total' counter values to 0.
|
||||
|
||||
This function does not change any recorded frames.
|
||||
*/
|
||||
static void ClearTotals(void);
|
||||
|
||||
/**
|
||||
\brief Determines number of CPU clock cycles per second.
|
||||
|
||||
\note This value is cached internally so this function is
|
||||
quite fast.
|
||||
|
||||
\return number of CPU clock cycles per second
|
||||
*/
|
||||
static _int64 GetClockCyclesPerSecond(void);
|
||||
|
||||
/**
|
||||
\brief Add the given result function interface.
|
||||
|
||||
\param func factory function
|
||||
\param name factory name
|
||||
\param arg description of optional parameters the factory function recognizes
|
||||
*/
|
||||
static void AddResultFunction(ProfileResultInterface* (*func)(int, const char * const *),
|
||||
const char *name, const char *arg);
|
||||
|
||||
private:
|
||||
/** \internal
|
||||
|
||||
\brief Simple recursive pattern matcher.
|
||||
|
||||
\param str string to match
|
||||
\param pattern pattern, only wildcard valid is '*'
|
||||
\return true if string matches pattern, false if not
|
||||
*/
|
||||
static bool SimpleMatch(const char *str, const char *pattern);
|
||||
|
||||
/// known frame names
|
||||
struct FrameName
|
||||
{
|
||||
/// frame name
|
||||
char *name;
|
||||
|
||||
/// number of recorded frames for this name
|
||||
unsigned frames;
|
||||
|
||||
/// are we currently recording?
|
||||
bool isRecording;
|
||||
|
||||
/// should current frame be appended to last frame with same name
|
||||
bool doAppend;
|
||||
|
||||
/// internal index for function level profiler
|
||||
int funcIndex;
|
||||
|
||||
/// internal index for high level profiler
|
||||
int highIndex;
|
||||
|
||||
/// global frame number of last recorded frame for this range, -1 if none
|
||||
int lastGlobalIndex;
|
||||
};
|
||||
|
||||
/// \internal pattern list entry
|
||||
struct PatternListEntry
|
||||
{
|
||||
/// next entry
|
||||
PatternListEntry *next;
|
||||
|
||||
/// active (true) or inactive (false)?
|
||||
bool isActive;
|
||||
|
||||
/// pattern itself (dynamic allocated memory)
|
||||
char *pattern;
|
||||
};
|
||||
|
||||
/** \internal
|
||||
|
||||
First pattern list list entry. A singly linked list is
|
||||
okay for this because checking patterns is a costly
|
||||
operation anyway and is therefore cached.
|
||||
*/
|
||||
static PatternListEntry *firstPatternEntry;
|
||||
|
||||
/// \internal last pattern list entry for fast additions to list at end
|
||||
static PatternListEntry *lastPatternEntry;
|
||||
|
||||
/// number of recorded frames
|
||||
static unsigned m_rec;
|
||||
|
||||
/// names of recorded frames
|
||||
static char **m_recNames;
|
||||
|
||||
/// number of known frame names
|
||||
static unsigned m_names;
|
||||
|
||||
/// list of known frame names
|
||||
static FrameName *m_frameNames;
|
||||
|
||||
/// CPU clock cycles/second
|
||||
static _int64 m_clockCycles;
|
||||
};
|
||||
|
||||
#endif // PROFILE_H
|
253
GeneralsMD/Code/Libraries/Source/profile/profile_cmd.cpp
Normal file
253
GeneralsMD/Code/Libraries/Source/profile/profile_cmd.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile_cmd.cpp $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #2 $
|
||||
// $DateTime: 2003/08/12 15:05:00 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Profile module command interface
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "_pch.h"
|
||||
|
||||
unsigned ProfileCmdInterface::numResIf;
|
||||
ProfileCmdInterface::Factory *ProfileCmdInterface::resIf;
|
||||
|
||||
void ProfileCmdInterface::AddResultFunction(ProfileResultInterface* (*func)(int, const char * const *),
|
||||
const char *name, const char *arg)
|
||||
{
|
||||
DFAIL_IF(!func) return;
|
||||
DFAIL_IF(!name) return;
|
||||
for (unsigned k=0;k<numResIf;k++)
|
||||
if (!strcmp(resIf[k].name,name))
|
||||
return;
|
||||
++numResIf;
|
||||
resIf=(Factory *)ProfileReAllocMemory(resIf,numResIf*sizeof(Factory));
|
||||
resIf[numResIf-1].func=func;
|
||||
resIf[numResIf-1].name=name;
|
||||
resIf[numResIf-1].arg=arg;
|
||||
}
|
||||
|
||||
void ProfileCmdInterface::RunResultFunctions(void)
|
||||
{
|
||||
// no result functions registered?
|
||||
if (!numResFunc)
|
||||
Debug::Command("profile.result file_csv");
|
||||
|
||||
// process result interfaces
|
||||
for (unsigned k=0;k<numResFunc;k++)
|
||||
{
|
||||
resFunc[k]->WriteResults();
|
||||
resFunc[k]->Delete();
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfileCmdInterface::Execute(class Debug& dbg, const char *cmd, CommandMode cmdmode,
|
||||
unsigned argn, const char * const * argv)
|
||||
{
|
||||
// just for convenience...
|
||||
bool normalMode=cmdmode==CommandMode::Normal;
|
||||
|
||||
if (!strcmp(cmd,"help"))
|
||||
{
|
||||
if (!normalMode)
|
||||
return true;
|
||||
|
||||
if (!argn)
|
||||
{
|
||||
dbg << "profile group help:\n"
|
||||
" result, caller, clear, add, view\n";
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(argv[0],"result"))
|
||||
{
|
||||
dbg << "result\n\n"
|
||||
"Shows the list of available result functions and their\n"
|
||||
"optional parameters.\n"
|
||||
"\n"
|
||||
"result <res_func_name> [ <arg1> .. <argN> ]\n\n"
|
||||
"Adds the given result function to be executed on program\n"
|
||||
"exit.\n";
|
||||
}
|
||||
else if (!strcmp(argv[0],"caller"))
|
||||
{
|
||||
dbg << "caller [ (+|-) ]\n\n"
|
||||
"Enables/disables recording of caller information while\n"
|
||||
"performing function level profiling. Turned off by default\n"
|
||||
"since CPU hit is non-zero.\n";
|
||||
}
|
||||
else if (!strcmp(argv[0],"clear"))
|
||||
{
|
||||
dbg << "clear\n\n"
|
||||
"Clears the profile inclusion/exclusion list.\n";
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(argv[0],"add"))
|
||||
{
|
||||
dbg << "add (+|-) <pattern>\n"
|
||||
"\n"
|
||||
"Adds a pattern to the profile list. By default all\n"
|
||||
"profile ranges are disabled. Each new range is then checked\n"
|
||||
"against all pattern in this list. If a match is found the\n"
|
||||
"active/inactive state is modified accordingly (+ for active,\n"
|
||||
"- for inactive). The final state is always the last match.";
|
||||
return true;
|
||||
}
|
||||
else if (!strcmp(argv[0],"view"))
|
||||
{
|
||||
dbg << "view\n\n"
|
||||
"Shows the active pattern list.\n";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// command: result
|
||||
if (!strcmp(cmd,"result"))
|
||||
{
|
||||
if (!argn)
|
||||
{
|
||||
for (unsigned k=0;k<numResIf;k++)
|
||||
{
|
||||
dbg << resIf[k].name;
|
||||
if ((resIf[k].arg&&*resIf[k].arg)||!normalMode)
|
||||
dbg << "\n " << resIf[k].arg;
|
||||
dbg << "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned k=0;k<numResIf;k++)
|
||||
if (!strcmp(argv[0],resIf[k].name))
|
||||
break;
|
||||
if (k==numResIf)
|
||||
{
|
||||
dbg << "Unknown result function\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
ProfileResultInterface *newIf=resIf[k].func(argn-1,argv+1);
|
||||
if (!newIf)
|
||||
{
|
||||
dbg << "Could not add result function\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
++numResFunc;
|
||||
resFunc=(ProfileResultInterface **)ProfileReAllocMemory(resFunc,numResFunc*sizeof(ProfileResultInterface *));
|
||||
resFunc[numResFunc-1]=newIf;
|
||||
if (normalMode)
|
||||
dbg << "Result function " << argv[0] << " added\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// command: caller
|
||||
if (!strcmp(cmd,"caller"))
|
||||
{
|
||||
#ifdef HAS_PROFILE
|
||||
if (argn)
|
||||
{
|
||||
if (*argv[0]=='+')
|
||||
ProfileFuncLevelTracer::recordCaller=true;
|
||||
if (*argv[0]=='-')
|
||||
ProfileFuncLevelTracer::recordCaller=false;
|
||||
}
|
||||
if (normalMode)
|
||||
dbg << "Record caller: " << (ProfileFuncLevelTracer::recordCaller?"on":"off");
|
||||
else
|
||||
dbg << (ProfileFuncLevelTracer::recordCaller?"1":"0");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// command: clear
|
||||
if (!strcmp(cmd,"clear"))
|
||||
{
|
||||
// remove some (or all) pattern
|
||||
const char *pattern=argn<1?"*":argv[0];
|
||||
for (Profile::PatternListEntry **entryPtr=&Profile::firstPatternEntry;*entryPtr;)
|
||||
{
|
||||
if (Profile::SimpleMatch((*entryPtr)->pattern,pattern))
|
||||
{
|
||||
// remove this entry
|
||||
Profile::PatternListEntry *cur=*entryPtr;
|
||||
*entryPtr=cur->next;
|
||||
ProfileFreeMemory(cur->pattern);
|
||||
ProfileFreeMemory(cur);
|
||||
}
|
||||
else
|
||||
entryPtr=&((*entryPtr)->next);
|
||||
}
|
||||
|
||||
// must fixup lastPatternEntry now
|
||||
if (Profile::firstPatternEntry)
|
||||
{
|
||||
for (Profile::PatternListEntry *cur=Profile::firstPatternEntry;cur->next;cur=cur->next);
|
||||
Profile::lastPatternEntry=cur;
|
||||
}
|
||||
else
|
||||
Profile::lastPatternEntry=NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
// command: add
|
||||
if (!strcmp(cmd,"add"))
|
||||
{
|
||||
// add a pattern
|
||||
if (argn<2)
|
||||
dbg << "Please specify mode and pattern";
|
||||
else
|
||||
{
|
||||
// alloc new pattern entry
|
||||
Profile::PatternListEntry *cur=(Profile::PatternListEntry *)
|
||||
ProfileAllocMemory(sizeof(Profile::PatternListEntry));
|
||||
|
||||
// init
|
||||
cur->next=NULL;
|
||||
cur->isActive=*argv[0]=='+';
|
||||
cur->pattern=(char *)ProfileAllocMemory(strlen(argv[1])+1);
|
||||
strcpy(cur->pattern,argv[1]);
|
||||
|
||||
// add to list
|
||||
if (Profile::lastPatternEntry)
|
||||
Profile::lastPatternEntry->next=cur;
|
||||
else
|
||||
Profile::firstPatternEntry=cur;
|
||||
Profile::lastPatternEntry=cur;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// command: view
|
||||
if (!strcmp(cmd,"view"))
|
||||
{
|
||||
// show list of defined patterns
|
||||
for (Profile::PatternListEntry *cur=Profile::firstPatternEntry;cur;cur=cur->next)
|
||||
dbg << (cur->isActive?"+ ":"- ") << cur->pattern << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
// unknown command
|
||||
return false;
|
||||
}
|
196
GeneralsMD/Code/Libraries/Source/profile/profile_doc.h
Normal file
196
GeneralsMD/Code/Libraries/Source/profile/profile_doc.h
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile_doc.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #3 $
|
||||
// $DateTime: 2003/07/09 10:57:23 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// additional Doxygen module documentation
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef PROFILE_DOC_H // Include guard
|
||||
#define PROFILE_DOC_H
|
||||
|
||||
// This generates a small main page for Doxygen if a module only
|
||||
// documentation is built.
|
||||
#ifndef DOXYGEN_GLOBAL_DOC
|
||||
/**
|
||||
\mainpage %Profile module
|
||||
|
||||
The following pages contain the most useful information:
|
||||
- \ref module_profile
|
||||
- \ref profile_cmd
|
||||
|
||||
\internal This is the internal module documentation meant only for
|
||||
programmers who are working on the module internals.
|
||||
*/
|
||||
#endif
|
||||
|
||||
/**
|
||||
\page module_profile Profile module overview
|
||||
|
||||
\section overview Overview
|
||||
|
||||
The profile module contains the following logical groups:
|
||||
- high level hierarchical timer and logical profiling (frame based and global)
|
||||
- function level hierarchical timer based profiling (frame based and global)
|
||||
|
||||
Profiling data can be accessed in different ways:
|
||||
- using debug commands to query for profiling data
|
||||
- using debug commands to set up a write-to-file mode for profile data
|
||||
- using the C++ interface herein
|
||||
|
||||
\section highlevel High Level Profiling
|
||||
|
||||
High level profiling can be done both timer based and logical. An example
|
||||
for a logical profile would be the number of texture changes per frame
|
||||
or the number of triangles rendered.
|
||||
|
||||
The hierarchy is enforced by using a hierarchical naming scheme. For
|
||||
timer based profiles this hierachy is build automatically, for
|
||||
logical profiles each profile must be named accordingly when being created.
|
||||
|
||||
High level profiling is available in all build configurations. All
|
||||
high level profile functions are optimized for speed (at least while
|
||||
profiling is disabled) so that there is an almost zero cost for
|
||||
having profiling in all configurations.
|
||||
|
||||
\section funclevel Function Level Profiling
|
||||
|
||||
Function level profiling determines general call statistics, e.g. call
|
||||
count per frame/total, time spent in function, time per call, time
|
||||
spend in function and children, etc.
|
||||
|
||||
Function level profiling is available in the 'profile' build configuration
|
||||
only since it relies on the fact that the compiler generates '_penter'
|
||||
function calls at the beginning of each function.
|
||||
|
||||
\section frames Profile frames
|
||||
|
||||
Instead of just providing frame based profiles this module has the concept
|
||||
of profile ranges. A range is a period of time specified by a Begin and
|
||||
and End function call. All data within this range is recorded as a range.
|
||||
|
||||
It is possible to capture to more than one active range at the same time.
|
||||
|
||||
Ordinary frame capturing can be achieved by recording a range back-to-back.
|
||||
|
||||
Using ranges makes it very easy to profile logically connected events, e.g.
|
||||
profiling a level load time.
|
||||
|
||||
\section cmdif Command interface
|
||||
|
||||
This module provides a Debug Module command interface called 'profile'.
|
||||
The commands in this command interface are basically:
|
||||
- enabling/disabling profiles, either for frame counts or ranges
|
||||
- querying of current profile data
|
||||
- enabling write-to-file mode for profile data
|
||||
*/
|
||||
|
||||
/**
|
||||
\page profile_cmd Profile commands
|
||||
|
||||
Notation used:
|
||||
- [ abc ] means that 'abc' may or may not be specified
|
||||
- { abc } means that 'abc' may be specified any number of times (or not at all)
|
||||
- (a|b|c) means that either 'a', 'b' or 'c' must be specified once
|
||||
|
||||
The following commands exist (group: profile):
|
||||
|
||||
<table><tr>
|
||||
<td><b>Command</b></td>
|
||||
<td><b>Parameters</b></td>
|
||||
<td><b>Description</b></td>
|
||||
|
||||
</tr><tr>
|
||||
|
||||
<!---------------------------------->
|
||||
<td valign=top>result</td>
|
||||
<td valign=top>[ \<res_func_name\> [ \<arg1\> .. \<argN\> ] ]</td>
|
||||
<td>
|
||||
Without parameters this command shows the list of available
|
||||
result functions and their optional parameters.
|
||||
|
||||
If however a result function name is given then this result
|
||||
function (and the given parameters) are added to the list of
|
||||
active result functions. They are executed on program exit and
|
||||
used to generate e.g. a CSV file containing the profile results.
|
||||
</td>
|
||||
|
||||
</tr><tr>
|
||||
|
||||
<!---------------------------------->
|
||||
<td valign=top>caller</td>
|
||||
<td valign=top>[ (+|-) ]</td>
|
||||
<td>
|
||||
Enables/disables recording of caller information while
|
||||
performing function level profiling. Turned off by default
|
||||
since CPU hit is non-zero.
|
||||
</td>
|
||||
|
||||
</tr><tr>
|
||||
|
||||
<!---------------------------------->
|
||||
<td valign=top>clear</td>
|
||||
<td valign=top></td>
|
||||
<td>
|
||||
Clears the profile inclusion/exclusion list.
|
||||
</td>
|
||||
|
||||
</tr><tr>
|
||||
|
||||
<!---------------------------------->
|
||||
<td valign=top>add</td>
|
||||
<td valign=top>(+|-) \<pattern\></td>
|
||||
<td>
|
||||
Adds a pattern to the profile list. By default all
|
||||
profile ranges are disabled. Each new range is then checked
|
||||
against all pattern in this list. If a match is found the
|
||||
active/inactive state is modified accordingly (+ for active,
|
||||
- for inactive). The final state is always the last match.
|
||||
</td>
|
||||
|
||||
</tr><tr>
|
||||
|
||||
<!---------------------------------->
|
||||
<td valign=top>view</td>
|
||||
<td valign=top></td>
|
||||
<td>
|
||||
Shows the active pattern list.
|
||||
</td>
|
||||
|
||||
<!-- keep this as template for new commands -->
|
||||
</tr><tr>
|
||||
|
||||
<!---------------------------------->
|
||||
<td valign=top></td>
|
||||
<td valign=top></td>
|
||||
<td>
|
||||
</td>
|
||||
|
||||
</tr></table>
|
||||
|
||||
*/
|
||||
|
||||
#endif // PROFILE_DOC_H
|
787
GeneralsMD/Code/Libraries/Source/profile/profile_funclevel.cpp
Normal file
787
GeneralsMD/Code/Libraries/Source/profile/profile_funclevel.cpp
Normal file
@@ -0,0 +1,787 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile_funclevel.cpp $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #4 $
|
||||
// $DateTime: 2003/08/14 13:43:29 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Function level profiling
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "_pch.h"
|
||||
#include "../debug/debug.h"
|
||||
#include <new>
|
||||
|
||||
#ifdef HAS_PROFILE
|
||||
|
||||
// TLS index (-1 if not yet initialized)
|
||||
static int TLSIndex=-1;
|
||||
|
||||
// our own fast critical section
|
||||
static ProfileFastCS cs;
|
||||
|
||||
// Unfortunately VC 6 doesn't support _pleave (or _pexit) so
|
||||
// we have to come up with our own type of implementation here...
|
||||
static void __declspec(naked) _pleave(void)
|
||||
{
|
||||
ProfileFuncLevelTracer *p;
|
||||
unsigned curESP,leaveAddr;
|
||||
|
||||
_asm
|
||||
{
|
||||
push ebp // this will be overwritten down there...
|
||||
push ebp // setup standard stack frame
|
||||
push eax
|
||||
mov ebp,esp
|
||||
mov eax,esp
|
||||
sub esp,3*4 // 3 local DWord vars
|
||||
pushad
|
||||
mov [curESP],eax
|
||||
}
|
||||
|
||||
curESP+=8;
|
||||
p=(ProfileFuncLevelTracer *)TlsGetValue(TLSIndex);
|
||||
leaveAddr=p->Leave(curESP);
|
||||
*(unsigned *)(curESP)=leaveAddr;
|
||||
|
||||
_asm
|
||||
{
|
||||
popad
|
||||
add esp,3*4 // must match sub esp above
|
||||
pop eax
|
||||
pop ebp
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __declspec(naked) _cdecl _penter(void)
|
||||
{
|
||||
unsigned callerFunc,ESPonReturn,callerRet;
|
||||
ProfileFuncLevelTracer *p;
|
||||
|
||||
_asm
|
||||
{
|
||||
push ebp
|
||||
push eax
|
||||
mov ebp,esp
|
||||
mov eax,esp
|
||||
sub esp,4*4 // 4 local DWord vars
|
||||
pushad
|
||||
|
||||
// calc return address
|
||||
add eax,4+4 // account for push ebp and push eax
|
||||
mov ebx,[eax] // grab return address
|
||||
mov [callerFunc],ebx
|
||||
|
||||
// get some more stuff
|
||||
add eax,4
|
||||
mov [ESPonReturn],eax
|
||||
mov ebx,[eax]
|
||||
mov [callerRet],ebx
|
||||
|
||||
// jam in our exit code
|
||||
mov dword ptr [eax],offset _pleave
|
||||
}
|
||||
|
||||
// do we need a new stack tracer?
|
||||
if (TLSIndex==-1)
|
||||
TLSIndex=TlsAlloc();
|
||||
p=(ProfileFuncLevelTracer *)TlsGetValue(TLSIndex);
|
||||
if (!p)
|
||||
{
|
||||
p=(ProfileFuncLevelTracer *)ProfileAllocMemory(sizeof(ProfileFuncLevelTracer));
|
||||
new (p) ProfileFuncLevelTracer;
|
||||
TlsSetValue(TLSIndex,p);
|
||||
}
|
||||
|
||||
// enter function
|
||||
p->Enter(callerFunc-5,ESPonReturn,callerRet);
|
||||
|
||||
// cleanup
|
||||
_asm
|
||||
{
|
||||
popad
|
||||
add esp,4*4 // must match sub esp above
|
||||
pop eax
|
||||
pop ebp
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer *ProfileFuncLevelTracer::head=NULL;
|
||||
bool ProfileFuncLevelTracer::shuttingDown=false;
|
||||
int ProfileFuncLevelTracer::curFrame=0;
|
||||
unsigned ProfileFuncLevelTracer::frameRecordMask;
|
||||
bool ProfileFuncLevelTracer::recordCaller=false;
|
||||
|
||||
ProfileFuncLevelTracer::ProfileFuncLevelTracer(void):
|
||||
stack(NULL), usedStack(0), totalStack(0), maxDepth(0)
|
||||
{
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
|
||||
next=head;
|
||||
head=this;
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer::~ProfileFuncLevelTracer()
|
||||
{
|
||||
// yes, I know we leak...
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::Enter(unsigned addr, unsigned esp, unsigned ret)
|
||||
{
|
||||
// must stack grow?
|
||||
if (usedStack>=totalStack)
|
||||
stack=(StackEntry *)ProfileReAllocMemory(stack,(totalStack+=100)*sizeof(StackEntry));
|
||||
|
||||
// save info
|
||||
Function *f=func.Find(addr);
|
||||
if (!f)
|
||||
{
|
||||
// new function
|
||||
f=(Function *)ProfileAllocMemory(sizeof(Function));
|
||||
new (f) Function(this);
|
||||
f->addr=addr;
|
||||
f->glob.callCount=f->glob.tickPure=f->glob.tickTotal=0;
|
||||
for (int i=0;i<MAX_FRAME_RECORDS;i++)
|
||||
f->cur[i].callCount=f->cur[i].tickPure=f->cur[i].tickTotal=0;
|
||||
f->depth=0;
|
||||
func.Insert(f);
|
||||
}
|
||||
|
||||
StackEntry &s=stack[usedStack++];
|
||||
s.func=f;
|
||||
s.esp=esp;
|
||||
s.retVal=ret;
|
||||
ProfileGetTime(s.tickEnter);
|
||||
s.tickSubTime=0;
|
||||
f->depth++;
|
||||
|
||||
// new max depth?
|
||||
if (usedStack>=maxDepth)
|
||||
maxDepth=usedStack;
|
||||
|
||||
DLOG_GROUP(profile_stack,Debug::RepeatChar(' ',usedStack-1)
|
||||
<< Debug::Hex() << this
|
||||
<< " Enter " << Debug::Width(8) << addr
|
||||
<< " ESP " << Debug::Width(8) << esp
|
||||
<< " return " << Debug::Width(8) << ret
|
||||
<< " level " << Debug::Dec() << usedStack
|
||||
);
|
||||
}
|
||||
|
||||
unsigned ProfileFuncLevelTracer::Leave(unsigned esp)
|
||||
{
|
||||
// get current "time"
|
||||
__int64 cur;
|
||||
ProfileGetTime(cur);
|
||||
|
||||
while (usedStack>0)
|
||||
{
|
||||
// leave current function
|
||||
usedStack--;
|
||||
StackEntry &s=stack[usedStack],
|
||||
&sPrev=stack[usedStack-1];
|
||||
|
||||
Function *f=s.func;
|
||||
|
||||
// decrease call depth
|
||||
// note: add global time only if call depth is 0
|
||||
f->depth--;
|
||||
|
||||
// insert caller
|
||||
if (recordCaller&&usedStack)
|
||||
f->glob.caller.Insert(sPrev.func->addr,1);
|
||||
|
||||
// inc call counter
|
||||
f->glob.callCount++;
|
||||
|
||||
// add total time
|
||||
__int64 delta=cur-s.tickEnter;
|
||||
if (!f->depth)
|
||||
f->glob.tickTotal+=delta;
|
||||
|
||||
// add pure time
|
||||
f->glob.tickPure+=delta-s.tickSubTime;
|
||||
|
||||
// add sub time for higher function
|
||||
if (usedStack)
|
||||
sPrev.tickSubTime+=delta;
|
||||
|
||||
// frame based profiling?
|
||||
if (frameRecordMask)
|
||||
{
|
||||
unsigned mask=frameRecordMask;
|
||||
for (unsigned i=0;i<MAX_FRAME_RECORDS;i++)
|
||||
{
|
||||
if (mask&1)
|
||||
{
|
||||
if (recordCaller&&usedStack>0)
|
||||
f->cur[i].caller.Insert(sPrev.func->addr,1);
|
||||
f->cur[i].callCount++;
|
||||
if (!f->depth)
|
||||
f->cur[i].tickTotal+=delta;
|
||||
f->cur[i].tickPure+=delta-s.tickSubTime;
|
||||
}
|
||||
if (!(mask>>=1))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// exit if address match (somewhat...)
|
||||
if (s.esp==esp)
|
||||
break;
|
||||
|
||||
// catching those nasty ret<n>...
|
||||
if (s.esp<esp&&
|
||||
(esp-s.esp)%4==0&&
|
||||
(esp-s.esp)<256)
|
||||
break;
|
||||
|
||||
// emit warning
|
||||
DCRASH("ESP " << Debug::Hex() << esp << " does not match " << stack[usedStack].esp << Debug>>Dec());
|
||||
}
|
||||
|
||||
DLOG_GROUP(profile_stack,Debug::RepeatChar(' ',usedStack-1)
|
||||
<< Debug::Hex() << this
|
||||
<< " Leave " << Debug::Width(8) << ""
|
||||
<< " ESP " << Debug::Width(8) << stack[usedStack].esp
|
||||
<< " return " << Debug::Width(8) << stack[usedStack].retVal
|
||||
<< " level " << Debug::Dec() << usedStack
|
||||
);
|
||||
|
||||
return stack[usedStack].retVal;
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::Shutdown(void)
|
||||
{
|
||||
if (frameRecordMask)
|
||||
{
|
||||
for (unsigned i=0;i<MAX_FRAME_RECORDS;i++)
|
||||
if (frameRecordMask&(1<<i))
|
||||
for (ProfileFuncLevelTracer *p=head;p;p=p->next)
|
||||
p->FrameEnd(i,-1);
|
||||
}
|
||||
}
|
||||
|
||||
int ProfileFuncLevelTracer::FrameStart(void)
|
||||
{
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
|
||||
for (unsigned i=0;i<MAX_FRAME_RECORDS;i++)
|
||||
if (!(frameRecordMask&(1<<i)))
|
||||
break;
|
||||
if (i==MAX_FRAME_RECORDS)
|
||||
return -1;
|
||||
|
||||
for (ProfileFuncLevelTracer *p=head;p;p=p->next)
|
||||
{
|
||||
Function *f;
|
||||
for (int k=0;(f=p->func.Enumerate(k))!=NULL;k++)
|
||||
{
|
||||
Profile &p=f->cur[i];
|
||||
p.caller.Clear();
|
||||
p.callCount=p.tickPure=p.tickTotal=0;
|
||||
}
|
||||
}
|
||||
|
||||
frameRecordMask|=1<<i;
|
||||
return i;
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::FrameEnd(int which, int mixIndex)
|
||||
{
|
||||
DFAIL_IF(which<0||which>=MAX_FRAME_RECORDS)
|
||||
return;
|
||||
DFAIL_IF(!(frameRecordMask&(1<<which)))
|
||||
return;
|
||||
DFAIL_IF(mixIndex>=curFrame)
|
||||
return;
|
||||
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
|
||||
frameRecordMask^=1<<which;
|
||||
if (mixIndex<0)
|
||||
curFrame++;
|
||||
for (ProfileFuncLevelTracer *p=head;p;p=p->next)
|
||||
{
|
||||
Function *f;
|
||||
for (int k=0;(f=p->func.Enumerate(k))!=NULL;k++)
|
||||
{
|
||||
Profile &p=f->cur[which];
|
||||
if (p.callCount)
|
||||
{
|
||||
if (mixIndex<0)
|
||||
f->frame.Append(curFrame,p);
|
||||
else
|
||||
f->frame.MixIn(mixIndex,p);
|
||||
}
|
||||
p.caller.Clear();
|
||||
p.callCount=p.tickPure=p.tickTotal=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::ClearTotals(void)
|
||||
{
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
|
||||
for (ProfileFuncLevelTracer *p=head;p;p=p->next)
|
||||
{
|
||||
Function *f;
|
||||
for (int k=0;(f=p->func.Enumerate(k))!=NULL;k++)
|
||||
{
|
||||
f->glob.caller.Clear();
|
||||
f->glob.callCount=0;
|
||||
f->glob.tickPure=0;
|
||||
f->glob.tickTotal=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer::UnsignedMap::UnsignedMap(void):
|
||||
e(NULL), alloc(0), used(0), writeLock(false)
|
||||
{
|
||||
memset(hash,0,sizeof(hash));
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer::UnsignedMap::~UnsignedMap()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::UnsignedMap::Clear(void)
|
||||
{
|
||||
ProfileFreeMemory(e);
|
||||
e=NULL;
|
||||
alloc=used=0;
|
||||
memset(hash,0,sizeof(hash));
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::UnsignedMap::_Insert(unsigned at, unsigned val, int countAdd)
|
||||
{
|
||||
DFAIL_IF(writeLock) return;
|
||||
|
||||
// realloc list?
|
||||
if (used==alloc)
|
||||
{
|
||||
// must fixup pointers...
|
||||
unsigned delta=unsigned(e);
|
||||
e=(Entry *)ProfileReAllocMemory(e,((alloc+=64)*sizeof(Entry)));
|
||||
delta=unsigned(e)-delta;
|
||||
if (used&&delta)
|
||||
{
|
||||
for (unsigned k=0;k<HASH_SIZE;k++)
|
||||
if (hash[k])
|
||||
((unsigned &)hash[k])+=delta;
|
||||
for (k=0;k<used;k++)
|
||||
if (e[k].next)
|
||||
((unsigned &)e[k].next)+=delta;
|
||||
}
|
||||
}
|
||||
|
||||
// add new item
|
||||
e[used].val=val;
|
||||
e[used].count=countAdd;
|
||||
e[used].next=hash[at];
|
||||
hash[at]=e+used++;
|
||||
}
|
||||
|
||||
unsigned ProfileFuncLevelTracer::UnsignedMap::Enumerate(int index)
|
||||
{
|
||||
if (index<0||index>=(int)used)
|
||||
return 0;
|
||||
return e[index].val;
|
||||
}
|
||||
|
||||
unsigned ProfileFuncLevelTracer::UnsignedMap::GetCount(int index)
|
||||
{
|
||||
if (index<0||index>=(int)used)
|
||||
return 0;
|
||||
return e[index].count;
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::UnsignedMap::Copy(const UnsignedMap &src)
|
||||
{
|
||||
Clear();
|
||||
if (src.e)
|
||||
{
|
||||
alloc=used=src.used;
|
||||
e=(Entry *)ProfileAllocMemory(alloc*sizeof(Entry));
|
||||
memcpy(e,src.e,alloc*sizeof(Entry));
|
||||
writeLock=true;
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::UnsignedMap::MixIn(const UnsignedMap &src)
|
||||
{
|
||||
writeLock=false;
|
||||
for (unsigned k=0;k<src.used;k++)
|
||||
Insert(src.e[k].val,src.e[k].count);
|
||||
writeLock=true;
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer::ProfileMap::ProfileMap(void):
|
||||
root(NULL), tail(&root)
|
||||
{
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer::ProfileMap::~ProfileMap()
|
||||
{
|
||||
while (root)
|
||||
{
|
||||
List *next=root->next;
|
||||
root->~List();
|
||||
ProfileFreeMemory(root);
|
||||
root=next;
|
||||
}
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer::Profile *ProfileFuncLevelTracer::ProfileMap::Find(int frame)
|
||||
{
|
||||
for (List *p=root;p&&p->frame<frame;p=p->next);
|
||||
return p&&p->frame==frame?&p->p:NULL;
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::ProfileMap::Append(int frame, const Profile &p)
|
||||
{
|
||||
List *newEntry=(List *)ProfileAllocMemory(sizeof(List));
|
||||
new (newEntry) List;
|
||||
newEntry->frame=frame;
|
||||
newEntry->p.Copy(p);
|
||||
newEntry->next=NULL;
|
||||
*tail=newEntry;
|
||||
tail=&newEntry->next;
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::ProfileMap::MixIn(int frame, const Profile &p)
|
||||
{
|
||||
// search correct list entry
|
||||
for (List *oldEntry=root;oldEntry;oldEntry=oldEntry->next)
|
||||
if (oldEntry->frame==frame)
|
||||
break;
|
||||
if (!oldEntry)
|
||||
Append(frame,p);
|
||||
else
|
||||
oldEntry->p.MixIn(p);
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer::FunctionMap::FunctionMap(void):
|
||||
e(NULL), alloc(0), used(0)
|
||||
{
|
||||
memset(hash,0,sizeof(hash));
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer::FunctionMap::~FunctionMap()
|
||||
{
|
||||
if (e)
|
||||
{
|
||||
for (unsigned k=0;k<used;k++)
|
||||
{
|
||||
e[k].funcPtr->~Function();
|
||||
ProfileFreeMemory(e[k].funcPtr);
|
||||
}
|
||||
ProfileFreeMemory(e);
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileFuncLevelTracer::FunctionMap::Insert(Function *funcPtr)
|
||||
{
|
||||
// realloc list?
|
||||
if (used==alloc)
|
||||
{
|
||||
// must fixup pointers...
|
||||
unsigned delta=unsigned(e);
|
||||
e=(Entry *)ProfileReAllocMemory(e,(alloc+=1024)*sizeof(Entry));
|
||||
delta=unsigned(e)-delta;
|
||||
if (used&&delta)
|
||||
{
|
||||
for (unsigned k=0;k<HASH_SIZE;k++)
|
||||
if (hash[k])
|
||||
((unsigned &)hash[k])+=delta;
|
||||
for (k=0;k<used;k++)
|
||||
if (e[k].next)
|
||||
((unsigned &)e[k].next)+=delta;
|
||||
}
|
||||
}
|
||||
|
||||
// add to hash
|
||||
unsigned at=(funcPtr->addr/16)%HASH_SIZE;
|
||||
e[used].funcPtr=funcPtr;
|
||||
e[used].next=hash[at];
|
||||
hash[at]=e+used++;
|
||||
}
|
||||
|
||||
ProfileFuncLevelTracer::Function *ProfileFuncLevelTracer::FunctionMap::Enumerate(int index)
|
||||
{
|
||||
if (index<0||index>=(int)used)
|
||||
return NULL;
|
||||
return e[index].funcPtr;
|
||||
}
|
||||
|
||||
bool ProfileFuncLevel::IdList::Enum(unsigned index, Id &id, unsigned *countPtr) const
|
||||
{
|
||||
if (!m_ptr)
|
||||
return false;
|
||||
|
||||
ProfileFuncLevelTracer::Profile &prof=*(ProfileFuncLevelTracer::Profile *)m_ptr;
|
||||
|
||||
unsigned addr;
|
||||
if ((addr=prof.caller.Enumerate(index)))
|
||||
{
|
||||
id.m_funcPtr=prof.tracer->FindFunction(addr);
|
||||
if (countPtr)
|
||||
*countPtr=prof.caller.GetCount(index);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *ProfileFuncLevel::Id::GetSource(void) const
|
||||
{
|
||||
if (!m_funcPtr)
|
||||
return NULL;
|
||||
|
||||
ProfileFuncLevelTracer::Function *func=(ProfileFuncLevelTracer::Function *)m_funcPtr;
|
||||
if (!func->funcSource)
|
||||
{
|
||||
char helpFunc[256],helpFile[256];
|
||||
unsigned ofsFunc;
|
||||
DebugStackwalk::Signature::GetSymbol(func->addr,
|
||||
NULL,0,NULL,
|
||||
helpFunc,sizeof(helpFunc),&ofsFunc,
|
||||
helpFile,sizeof(helpFile),&func->funcLine,NULL);
|
||||
|
||||
char help[300];
|
||||
wsprintf(help,ofsFunc?"%s+0x%x":"%s",helpFunc,ofsFunc);
|
||||
func->funcSource=(char *)ProfileAllocMemory(strlen(helpFile)+1);
|
||||
strcpy(func->funcSource,helpFile);
|
||||
func->funcName=(char *)ProfileAllocMemory(strlen(help)+1);
|
||||
strcpy(func->funcName,help);
|
||||
}
|
||||
|
||||
return func->funcSource;
|
||||
}
|
||||
|
||||
const char *ProfileFuncLevel::Id::GetFunction(void) const
|
||||
{
|
||||
if (!m_funcPtr)
|
||||
return NULL;
|
||||
ProfileFuncLevelTracer::Function *func=(ProfileFuncLevelTracer::Function *)m_funcPtr;
|
||||
if (!func->funcSource)
|
||||
GetSource();
|
||||
return func->funcName;
|
||||
}
|
||||
|
||||
unsigned ProfileFuncLevel::Id::GetAddress(void) const
|
||||
{
|
||||
if (!m_funcPtr)
|
||||
return 0;
|
||||
ProfileFuncLevelTracer::Function *func=(ProfileFuncLevelTracer::Function *)m_funcPtr;
|
||||
return func->addr;
|
||||
}
|
||||
|
||||
unsigned ProfileFuncLevel::Id::GetLine(void) const
|
||||
{
|
||||
if (!m_funcPtr)
|
||||
return NULL;
|
||||
ProfileFuncLevelTracer::Function *func=(ProfileFuncLevelTracer::Function *)m_funcPtr;
|
||||
if (!func->funcSource)
|
||||
GetSource();
|
||||
return func->funcLine;
|
||||
}
|
||||
|
||||
unsigned _int64 ProfileFuncLevel::Id::GetCalls(unsigned frame) const
|
||||
{
|
||||
if (!m_funcPtr)
|
||||
return 0;
|
||||
|
||||
ProfileFuncLevelTracer::Function &func=*(ProfileFuncLevelTracer::Function *)m_funcPtr;
|
||||
|
||||
switch(frame)
|
||||
{
|
||||
case Total:
|
||||
return func.glob.callCount;
|
||||
default:
|
||||
ProfileFuncLevelTracer::Profile *prof=func.frame.Find(frame);
|
||||
return prof?prof->callCount:0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned _int64 ProfileFuncLevel::Id::GetTime(unsigned frame) const
|
||||
{
|
||||
if (!m_funcPtr)
|
||||
return 0;
|
||||
|
||||
ProfileFuncLevelTracer::Function &func=*(ProfileFuncLevelTracer::Function *)m_funcPtr;
|
||||
|
||||
switch(frame)
|
||||
{
|
||||
case Total:
|
||||
return func.glob.tickTotal;
|
||||
default:
|
||||
ProfileFuncLevelTracer::Profile *prof=func.frame.Find(frame);
|
||||
return prof?prof->tickTotal:0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned _int64 ProfileFuncLevel::Id::GetFunctionTime(unsigned frame) const
|
||||
{
|
||||
if (!m_funcPtr)
|
||||
return 0;
|
||||
|
||||
ProfileFuncLevelTracer::Function &func=*(ProfileFuncLevelTracer::Function *)m_funcPtr;
|
||||
|
||||
switch(frame)
|
||||
{
|
||||
case Total:
|
||||
return func.glob.tickPure;
|
||||
default:
|
||||
ProfileFuncLevelTracer::Profile *prof=func.frame.Find(frame);
|
||||
return prof?prof->tickPure:0;
|
||||
}
|
||||
}
|
||||
|
||||
ProfileFuncLevel::IdList ProfileFuncLevel::Id::GetCaller(unsigned frame) const
|
||||
{
|
||||
if (!m_funcPtr)
|
||||
return IdList();
|
||||
|
||||
ProfileFuncLevelTracer::Function &func=*(ProfileFuncLevelTracer::Function *)m_funcPtr;
|
||||
|
||||
IdList ret;
|
||||
switch(frame)
|
||||
{
|
||||
case Total:
|
||||
ret.m_ptr=&func.glob;
|
||||
break;
|
||||
default:
|
||||
ProfileFuncLevelTracer::Profile *prof=func.frame.Find(frame);
|
||||
if (prof)
|
||||
ret.m_ptr=prof;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ProfileFuncLevel::Thread::EnumProfile(unsigned index, Id &id) const
|
||||
{
|
||||
if (!m_threadID)
|
||||
return false;
|
||||
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
|
||||
ProfileFuncLevelTracer::Function *f=m_threadID->EnumFunction(index);
|
||||
if (f)
|
||||
{
|
||||
id.m_funcPtr=f;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProfileFuncLevel::EnumThreads(unsigned index, Thread &thread)
|
||||
{
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
|
||||
for (ProfileFuncLevelTracer *p=ProfileFuncLevelTracer::GetFirst();p;p=p->GetNext())
|
||||
if (!index--)
|
||||
break;
|
||||
if (p)
|
||||
{
|
||||
thread.m_threadID=p;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
ProfileFuncLevel::ProfileFuncLevel(void)
|
||||
{
|
||||
}
|
||||
|
||||
#else // !defined HAS_PROFILE
|
||||
|
||||
bool ProfileFuncLevel::IdList::Enum(unsigned index, Id &id, unsigned *) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *ProfileFuncLevel::Id::GetSource(void) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *ProfileFuncLevel::Id::GetFunction(void) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned ProfileFuncLevel::Id::GetAddress(void) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned ProfileFuncLevel::Id::GetLine(void) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned _int64 ProfileFuncLevel::Id::GetCalls(unsigned frame) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned _int64 ProfileFuncLevel::Id::GetTime(unsigned frame) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned _int64 ProfileFuncLevel::Id::GetFunctionTime(unsigned frame) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ProfileFuncLevel::IdList ProfileFuncLevel::Id::GetCaller(unsigned frame) const
|
||||
{
|
||||
return ProfileFuncLevel::IdList();
|
||||
}
|
||||
|
||||
bool ProfileFuncLevel::Thread::EnumProfile(unsigned index, Id &id) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProfileFuncLevel::EnumThreads(unsigned index, Thread &thread)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ProfileFuncLevel::ProfileFuncLevel(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif // !defined HAS_PROFILE
|
||||
|
||||
ProfileFuncLevel ProfileFuncLevel::Instance;
|
||||
HANDLE ProfileFastCS::testEvent=::CreateEvent(NULL,FALSE,FALSE,"");
|
223
GeneralsMD/Code/Libraries/Source/profile/profile_funclevel.h
Normal file
223
GeneralsMD/Code/Libraries/Source/profile/profile_funclevel.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile_funclevel.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #3 $
|
||||
// $DateTime: 2003/07/09 10:57:23 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Function level profiling
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef PROFILE_FUNCLEVEL_H // Include guard
|
||||
#define PROFILE_FUNCLEVEL_H
|
||||
|
||||
/**
|
||||
\brief The function level profiler.
|
||||
|
||||
Note that this class exists even if the current build configuration
|
||||
is not _PROFILE. In these cases all calls will simply return
|
||||
empty data.
|
||||
*/
|
||||
class ProfileFuncLevel
|
||||
{
|
||||
friend class Profile;
|
||||
|
||||
// no, no copying allowed!
|
||||
ProfileFuncLevel(const ProfileFuncLevel&);
|
||||
ProfileFuncLevel& operator=(const ProfileFuncLevel&);
|
||||
|
||||
public:
|
||||
class Id;
|
||||
class Thread;
|
||||
|
||||
/// \brief A list of function level profile IDs
|
||||
class IdList
|
||||
{
|
||||
friend Id;
|
||||
|
||||
public:
|
||||
IdList(void): m_ptr(0) {}
|
||||
|
||||
/**
|
||||
\brief Enumerates the list of IDs.
|
||||
|
||||
\note These values are not sorted in any way.
|
||||
|
||||
\param index index value, >=0
|
||||
\param id return buffer for ID value
|
||||
\param countPtr return buffer for count, if given
|
||||
\return true if ID found at given index, false if not
|
||||
*/
|
||||
bool Enum(unsigned index, Id &id, unsigned *countPtr=0) const;
|
||||
|
||||
private:
|
||||
|
||||
/// internal value
|
||||
void *m_ptr;
|
||||
};
|
||||
|
||||
/// \brief A function level profile ID.
|
||||
class Id
|
||||
{
|
||||
friend IdList;
|
||||
friend Thread;
|
||||
|
||||
public:
|
||||
Id(void): m_funcPtr(0) {}
|
||||
|
||||
/// special 'frame' numbers
|
||||
enum
|
||||
{
|
||||
/// return the total value/count
|
||||
Total = 0xffffffff
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Returns the source file this Id is in.
|
||||
|
||||
\return source file name, may be NULL
|
||||
*/
|
||||
const char *GetSource(void) const;
|
||||
|
||||
/**
|
||||
\brief Returns the function name for this Id.
|
||||
|
||||
\return function name, may be NULL
|
||||
*/
|
||||
const char *GetFunction(void) const;
|
||||
|
||||
/**
|
||||
\brief Returns function address.
|
||||
|
||||
\return function address
|
||||
*/
|
||||
unsigned GetAddress(void) const;
|
||||
|
||||
/**
|
||||
\brief Returns the line number for this Id.
|
||||
|
||||
\return line number, 0 if unknown
|
||||
*/
|
||||
unsigned GetLine(void) const;
|
||||
|
||||
/**
|
||||
\brief Determine call counts.
|
||||
|
||||
\param frame number of recorded frame, or Total
|
||||
\return number of calls
|
||||
*/
|
||||
unsigned _int64 GetCalls(unsigned frame) const;
|
||||
|
||||
/**
|
||||
\brief Determine time spend in this function and its children.
|
||||
|
||||
\param frame number of recorded frame, or Total
|
||||
\return time spend (in CPU ticks)
|
||||
*/
|
||||
unsigned _int64 GetTime(unsigned frame) const;
|
||||
|
||||
/**
|
||||
\brief Determine time spend in this function only (exclude
|
||||
any time spend in child functions).
|
||||
|
||||
\param frame number of recorded frame, or Total
|
||||
\return time spend in this function alone (in CPU ticks)
|
||||
*/
|
||||
unsigned _int64 GetFunctionTime(unsigned frame) const;
|
||||
|
||||
/**
|
||||
\brief Determine the list of caller Ids.
|
||||
|
||||
\param frame number of recorded frame, or Total
|
||||
\return Caller Id list (actually just a handle value)
|
||||
*/
|
||||
IdList GetCaller(unsigned frame) const;
|
||||
|
||||
private:
|
||||
/// internal function pointer
|
||||
void *m_funcPtr;
|
||||
};
|
||||
|
||||
/// \brief a profiled thread
|
||||
class Thread
|
||||
{
|
||||
friend ProfileFuncLevel;
|
||||
|
||||
public:
|
||||
Thread(void): m_threadID(0) {}
|
||||
|
||||
/**
|
||||
\brief Enumerates the list of known function level profile values.
|
||||
|
||||
\note These values are not sorted in any way.
|
||||
|
||||
\param index index value, >=0
|
||||
\param id return buffer for ID value
|
||||
\return true if ID found at given index, false if not
|
||||
*/
|
||||
bool EnumProfile(unsigned index, Id &id) const;
|
||||
|
||||
/**
|
||||
\brief Returns a unique thread ID (not related to Windows thread ID)
|
||||
|
||||
\return profile thread ID
|
||||
*/
|
||||
unsigned GetId(void) const
|
||||
{
|
||||
return unsigned(m_threadID);
|
||||
}
|
||||
|
||||
private:
|
||||
/// internal thread ID
|
||||
class ProfileFuncLevelTracer *m_threadID;
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Enumerates the list of known and profiled threads.
|
||||
|
||||
\note These values are not sorted in any way.
|
||||
|
||||
\param index index value, >=0
|
||||
\param thread return buffer for thread handle
|
||||
\return true if Thread found, false if not
|
||||
*/
|
||||
static bool EnumThreads(unsigned index, Thread &thread);
|
||||
|
||||
private:
|
||||
|
||||
/** \internal
|
||||
|
||||
Undocumented default constructor. Initializes function level profiler.
|
||||
We can make this private as well so nobody accidently tries to create
|
||||
another instance.
|
||||
*/
|
||||
ProfileFuncLevel(void);
|
||||
|
||||
/**
|
||||
\brief The only function level profiler instance.
|
||||
*/
|
||||
static ProfileFuncLevel Instance;
|
||||
};
|
||||
|
||||
#endif // PROFILE_FUNCLEVEL_H
|
339
GeneralsMD/Code/Libraries/Source/profile/profile_highlevel.cpp
Normal file
339
GeneralsMD/Code/Libraries/Source/profile/profile_highlevel.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile_highlevel.cpp $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #2 $
|
||||
// $DateTime: 2003/08/14 13:43:29 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// High level profiling
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "_pch.h"
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
|
||||
// our own fast critical section
|
||||
static ProfileFastCS cs;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ProfileHighLevel::Id
|
||||
|
||||
void ProfileHighLevel::Id::Increment(double add)
|
||||
{
|
||||
if (m_idPtr)
|
||||
m_idPtr->Increment(add);
|
||||
}
|
||||
|
||||
void ProfileHighLevel::Id::SetMax(double max)
|
||||
{
|
||||
if (m_idPtr)
|
||||
m_idPtr->Maximum(max);
|
||||
}
|
||||
|
||||
const char *ProfileHighLevel::Id::GetName(void) const
|
||||
{
|
||||
return m_idPtr?m_idPtr->GetName():NULL;
|
||||
}
|
||||
|
||||
const char *ProfileHighLevel::Id::GetDescr(void) const
|
||||
{
|
||||
return m_idPtr?m_idPtr->GetDescr():NULL;
|
||||
}
|
||||
|
||||
const char *ProfileHighLevel::Id::GetUnit(void) const
|
||||
{
|
||||
return m_idPtr?m_idPtr->GetUnit():NULL;
|
||||
}
|
||||
|
||||
const char *ProfileHighLevel::Id::GetCurrentValue(void) const
|
||||
{
|
||||
return m_idPtr?m_idPtr->AsString(m_idPtr->GetCurrentValue()):NULL;
|
||||
}
|
||||
|
||||
const char *ProfileHighLevel::Id::GetValue(unsigned frame) const
|
||||
{
|
||||
double v;
|
||||
if (!m_idPtr||!m_idPtr->GetFrameValue(frame,v))
|
||||
return NULL;
|
||||
return m_idPtr->AsString(v);
|
||||
}
|
||||
|
||||
const char *ProfileHighLevel::Id::GetTotalValue(void) const
|
||||
{
|
||||
return m_idPtr?m_idPtr->AsString(m_idPtr->GetTotalValue()):NULL;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ProfileHighLevel::Block
|
||||
|
||||
ProfileHighLevel::Block::Block(const char *name)
|
||||
{
|
||||
DFAIL_IF(!name) return;
|
||||
|
||||
m_idTime=AddProfile(name,NULL,"msec",6,-4);
|
||||
|
||||
char help[256];
|
||||
strncpy(help,name,sizeof(help));
|
||||
help[sizeof(help)-1-2]=0;
|
||||
strcat(help,".c");
|
||||
AddProfile(help,NULL,"calls",6,0).Increment();
|
||||
|
||||
ProfileGetTime(m_start);
|
||||
}
|
||||
|
||||
ProfileHighLevel::Block::~Block()
|
||||
{
|
||||
_int64 end;
|
||||
ProfileGetTime(end);
|
||||
end-=m_start;
|
||||
|
||||
m_idTime.Increment(double(end)/(double)Profile::GetClockCyclesPerSecond());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ProfileId
|
||||
|
||||
// profile ID stuff
|
||||
ProfileId *ProfileId::first;
|
||||
int ProfileId::curFrame;
|
||||
unsigned ProfileId::frameRecordMask;
|
||||
char ProfileId::stringBuf[ProfileId::STRING_BUFFER_SIZE];
|
||||
unsigned ProfileId::stringBufUnused;
|
||||
|
||||
ProfileId::ProfileId(const char *name, const char *descr, const char *unit, int precision, int exp10)
|
||||
{
|
||||
m_next=first; first=this;
|
||||
m_name=(char *)ProfileAllocMemory(strlen(name)+1);
|
||||
strcpy(m_name,name);
|
||||
if (descr)
|
||||
{
|
||||
m_descr=(char *)ProfileAllocMemory(strlen(descr)+1);
|
||||
strcpy(m_descr,descr);
|
||||
}
|
||||
else
|
||||
m_descr=NULL;
|
||||
if (unit)
|
||||
{
|
||||
m_unit=(char *)ProfileAllocMemory(strlen(unit)+1);
|
||||
strcpy(m_unit,unit);
|
||||
}
|
||||
else
|
||||
m_unit=NULL;
|
||||
m_precision=precision;
|
||||
m_exp10=exp10;
|
||||
m_curVal=m_totalVal=0.;
|
||||
m_recFrameVal=NULL;
|
||||
m_firstFrame=curFrame;
|
||||
m_valueMode=Unknown;
|
||||
}
|
||||
|
||||
void ProfileId::Increment(double add)
|
||||
{
|
||||
DFAIL_IF(m_valueMode!=Unknown&&m_valueMode!=ModeIncrement)
|
||||
return;
|
||||
|
||||
m_valueMode=ModeIncrement;
|
||||
m_curVal+=add;
|
||||
m_totalVal+=add;
|
||||
if (frameRecordMask)
|
||||
{
|
||||
unsigned mask=frameRecordMask;
|
||||
for (unsigned i=0;i<MAX_FRAME_RECORDS;i++)
|
||||
{
|
||||
if (mask&1)
|
||||
m_frameVal[i]+=add;
|
||||
if (!(mask>>=1))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileId::Maximum(double max)
|
||||
{
|
||||
DFAIL_IF(m_valueMode!=Unknown&&m_valueMode!=ModeMaximum)
|
||||
return;
|
||||
|
||||
m_valueMode=ModeMaximum;
|
||||
if (max>m_curVal)
|
||||
m_curVal=max;
|
||||
if (max>m_totalVal)
|
||||
m_totalVal=max;
|
||||
if (frameRecordMask)
|
||||
{
|
||||
unsigned mask=frameRecordMask;
|
||||
for (unsigned i=0;i<MAX_FRAME_RECORDS;i++)
|
||||
{
|
||||
if (mask&1)
|
||||
{
|
||||
if (max>m_frameVal[i])
|
||||
m_frameVal[i]=max;
|
||||
}
|
||||
if (!(mask>>=1))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *ProfileId::AsString(double v) const
|
||||
{
|
||||
char help1[10],help[40];
|
||||
wsprintf(help1,"%%%i.lf",m_precision);
|
||||
|
||||
double mul=1.0;
|
||||
int k;
|
||||
for (k=m_exp10;k<0;k++) mul*=10.0;
|
||||
for (;k>0;k--) mul/=10.0;
|
||||
|
||||
unsigned len=_snprintf(help,sizeof(help),help1,v*mul)+1;
|
||||
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
if (stringBufUnused+len>STRING_BUFFER_SIZE)
|
||||
stringBufUnused=0;
|
||||
char *ret=stringBuf+stringBufUnused;
|
||||
memcpy(ret,help,len);
|
||||
stringBufUnused+=len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ProfileId::FrameStart(void)
|
||||
{
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
|
||||
for (unsigned i=0;i<MAX_FRAME_RECORDS;i++)
|
||||
if (!(frameRecordMask&(1<<i)))
|
||||
break;
|
||||
if (i==MAX_FRAME_RECORDS)
|
||||
return -1;
|
||||
|
||||
for (ProfileId *p=first;p;p=p->m_next)
|
||||
p->m_frameVal[i]=0.;
|
||||
|
||||
frameRecordMask|=1<<i;
|
||||
return i;
|
||||
}
|
||||
|
||||
void ProfileId::FrameEnd(int which, int mixIndex)
|
||||
{
|
||||
DFAIL_IF(which<0||which>=MAX_FRAME_RECORDS)
|
||||
return;
|
||||
DFAIL_IF(!(frameRecordMask&(1<<which)))
|
||||
return;
|
||||
DFAIL_IF(mixIndex>=curFrame)
|
||||
return;
|
||||
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
|
||||
frameRecordMask^=1<<which;
|
||||
if (mixIndex<0)
|
||||
{
|
||||
// new frame
|
||||
curFrame++;
|
||||
for (ProfileId *p=first;p;p=p->m_next)
|
||||
{
|
||||
p->m_recFrameVal=(double *)ProfileReAllocMemory(p->m_recFrameVal,sizeof(double)*(curFrame-p->m_firstFrame));
|
||||
p->m_recFrameVal[curFrame-p->m_firstFrame-1]=p->m_frameVal[which];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// append data
|
||||
for (ProfileId *p=first;p;p=p->m_next)
|
||||
{
|
||||
if (p->m_firstFrame>mixIndex)
|
||||
continue;
|
||||
|
||||
double &val=p->m_recFrameVal[mixIndex-p->m_firstFrame];
|
||||
switch(p->m_valueMode)
|
||||
{
|
||||
case ProfileId::Unknown:
|
||||
break;
|
||||
case ProfileId::ModeIncrement:
|
||||
val+=p->m_frameVal[which];
|
||||
break;
|
||||
case ProfileId::ModeMaximum:
|
||||
if (p->m_frameVal[which]>val)
|
||||
val=p->m_frameVal[which];
|
||||
break;
|
||||
default:
|
||||
DFAIL();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileId::Shutdown(void)
|
||||
{
|
||||
if (frameRecordMask)
|
||||
{
|
||||
for (unsigned i=0;i<MAX_FRAME_RECORDS;i++)
|
||||
if (frameRecordMask&(1<<i))
|
||||
FrameEnd(i,-1);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ProfileHighLevel
|
||||
|
||||
ProfileHighLevel::Id ProfileHighLevel::AddProfile(const char *name, const char *descr, const char *unit, int precision, int exp10)
|
||||
{
|
||||
// check if there is already an ID with the given name...
|
||||
Id id;
|
||||
if (FindProfile(name,id))
|
||||
return id;
|
||||
|
||||
// checks...
|
||||
DFAIL_IF(!name) return id;
|
||||
|
||||
// no, allocate one
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
id.m_idPtr=new (ProfileAllocMemory(sizeof(ProfileId))) ProfileId(name,descr,unit,precision,exp10);
|
||||
return id;
|
||||
}
|
||||
|
||||
bool ProfileHighLevel::EnumProfile(unsigned index, Id &id)
|
||||
{
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
for (ProfileId *cur=ProfileId::GetFirst();cur&&index--;cur=cur->GetNext());
|
||||
id.m_idPtr=cur;
|
||||
return cur!=NULL;
|
||||
}
|
||||
|
||||
bool ProfileHighLevel::FindProfile(const char *name, Id &id)
|
||||
{
|
||||
DFAIL_IF(!name) return false;
|
||||
|
||||
ProfileFastCS::Lock lock(cs);
|
||||
for (ProfileId *cur=ProfileId::GetFirst();cur;cur=cur->GetNext())
|
||||
if (!strcmp(name,cur->GetName()))
|
||||
{
|
||||
id.m_idPtr=cur;
|
||||
return true;
|
||||
}
|
||||
|
||||
id.m_idPtr=NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
ProfileHighLevel::ProfileHighLevel(void)
|
||||
{
|
||||
}
|
||||
|
||||
ProfileHighLevel ProfileHighLevel::Instance;
|
241
GeneralsMD/Code/Libraries/Source/profile/profile_highlevel.h
Normal file
241
GeneralsMD/Code/Libraries/Source/profile/profile_highlevel.h
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile_highlevel.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #2 $
|
||||
// $DateTime: 2003/07/09 10:57:23 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// High level profiling
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef PROFILE_HIGHLEVEL_H // Include guard
|
||||
#define PROFILE_HIGHLEVEL_H
|
||||
|
||||
/// \internal internal Id representation
|
||||
class ProfileId;
|
||||
|
||||
/**
|
||||
\brief The high level profiler.
|
||||
*/
|
||||
class ProfileHighLevel
|
||||
{
|
||||
friend class Profile;
|
||||
|
||||
// no, no copying allowed!
|
||||
ProfileHighLevel(const ProfileHighLevel&);
|
||||
ProfileHighLevel& operator=(const ProfileHighLevel&);
|
||||
|
||||
public:
|
||||
|
||||
/// \brief A high level profile ID.
|
||||
class Id
|
||||
{
|
||||
friend ProfileHighLevel;
|
||||
|
||||
public:
|
||||
Id(void): m_idPtr(0) {}
|
||||
|
||||
/**
|
||||
\brief Increment the internal profile value.
|
||||
|
||||
\note Do not mix with SetMax.
|
||||
|
||||
\param add amount to add to internal profile value
|
||||
*/
|
||||
void Increment(double add=1.0);
|
||||
|
||||
/**
|
||||
\brief Set a new maximum value.
|
||||
|
||||
\note Do not mix with Increment.
|
||||
|
||||
This function sets a new maximum value (if the value
|
||||
passed in is actually larger than the current max value).
|
||||
|
||||
\param max new maximum value (if larger than current max value,
|
||||
otherwise current max value is left unchanged)
|
||||
*/
|
||||
void SetMax(double max);
|
||||
|
||||
/**
|
||||
\brief Returns the internal Id name.
|
||||
|
||||
\return internal Id name, e.g. 'render.texture.count.512x512'
|
||||
*/
|
||||
const char *GetName(void) const;
|
||||
|
||||
/**
|
||||
\brief Returns the descriptive name.
|
||||
|
||||
\return descriptive name, e.g. '# of 512x512 textures'
|
||||
*/
|
||||
const char *GetDescr(void) const;
|
||||
|
||||
/**
|
||||
\brief Returns the value's unit text.
|
||||
|
||||
\return unit text, e.g. 'bytes'
|
||||
*/
|
||||
const char *GetUnit(void) const;
|
||||
|
||||
/**
|
||||
\brief Returns the current value.
|
||||
|
||||
'Current' means the value since the last call to this function for
|
||||
the same Id.
|
||||
|
||||
This function is intended for displaying profile data while the
|
||||
application is running.
|
||||
|
||||
\note The contents of the buffer returned may be overwritten by
|
||||
any consecutive call to any profile module function.
|
||||
|
||||
\return current value
|
||||
*/
|
||||
const char *GetCurrentValue(void) const;
|
||||
|
||||
/**
|
||||
\brief Returns the value for the given recorded frame/range.
|
||||
|
||||
\note The contents of the buffer returned may be overwritten by
|
||||
any consecutive call to any profile module function.
|
||||
|
||||
\param frame number of recorded frame/range
|
||||
\return value at given frame, NULL if frame not found
|
||||
*/
|
||||
const char *GetValue(unsigned frame) const;
|
||||
|
||||
/**
|
||||
\brief Returns the total value for all frames.
|
||||
|
||||
This even includes data collected while no frames have been
|
||||
recorded.
|
||||
|
||||
\note A call to ProfileHighLevel::ClearTotals() resets this value.
|
||||
|
||||
\return total value
|
||||
*/
|
||||
const char *GetTotalValue(void) const;
|
||||
|
||||
private:
|
||||
|
||||
/// internal pointer
|
||||
ProfileId *m_idPtr;
|
||||
};
|
||||
|
||||
/// \brief Timer based function block profile
|
||||
class Block
|
||||
{
|
||||
friend ProfileHighLevel;
|
||||
|
||||
// no copying
|
||||
Block(const Block&);
|
||||
Block& operator=(const Block&);
|
||||
|
||||
public:
|
||||
/**
|
||||
\brief Instructs high level profiler to start a new timer
|
||||
based function block (or update if it already exists)
|
||||
|
||||
\note These function blocks are in the same name space as
|
||||
high level profile values registered with ProfileHighLevel::AddProfile()
|
||||
|
||||
\param name name of function block
|
||||
*/
|
||||
explicit Block(const char *name);
|
||||
|
||||
/// \brief Updates timer based function block
|
||||
~Block();
|
||||
|
||||
private:
|
||||
/// internal id (time)
|
||||
Id m_idTime;
|
||||
|
||||
/// start time
|
||||
_int64 m_start;
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Registers a new high level profile value.
|
||||
|
||||
If there is already a high level profile with the given name the
|
||||
Id of that profile is returned instead.
|
||||
|
||||
High level profiles can only be added, never removed.
|
||||
|
||||
\note Important: This function can (and should) be used in static
|
||||
initializers!
|
||||
|
||||
\note This function may be slow so don't use it too often.
|
||||
|
||||
\param name value name, e.g. "render.texture.count.512x512"
|
||||
\param descr descriptive name, e.g. "# of 512x512 textures"
|
||||
\param unit unit name, e.g. "byte" or "sec"
|
||||
\param precision number of decimal places to show
|
||||
\param exp10 10 base exponent (used for scaleing)
|
||||
\return internal profile ID value
|
||||
*/
|
||||
static Id AddProfile(const char *name, const char *descr, const char *unit, int precision, int exp10=0);
|
||||
|
||||
/**
|
||||
\brief Enumerates the list of known high level profile values.
|
||||
|
||||
\note Profiles are always sorted ascending by profile name.
|
||||
|
||||
\param index index value, >=0
|
||||
\param id return buffer for ID value
|
||||
\return true if ID found at given index, false if not
|
||||
*/
|
||||
static bool EnumProfile(unsigned index, Id &id);
|
||||
|
||||
/**
|
||||
\brief Searches for the given high level profile.
|
||||
|
||||
\note Actually the ID returned belongs to the first profile
|
||||
which has a name that is equal to or larger than the name
|
||||
searched for.
|
||||
|
||||
\param name profile name to search for
|
||||
\param id return buffer for ID value
|
||||
\return true if ID found, false if not
|
||||
*/
|
||||
static bool FindProfile(const char *name, Id &id);
|
||||
|
||||
private:
|
||||
|
||||
/** \internal
|
||||
|
||||
Undocumented default constructor. Initializes high level profiler.
|
||||
We can make this private as well so nobody accidently tries to create
|
||||
another instance.
|
||||
*/
|
||||
ProfileHighLevel(void);
|
||||
|
||||
/**
|
||||
\brief The only high level profiler instance.
|
||||
*/
|
||||
static ProfileHighLevel Instance;
|
||||
};
|
||||
|
||||
#endif // PROFILE_HIGHLEVEL_H
|
208
GeneralsMD/Code/Libraries/Source/profile/profile_priv.dox
Normal file
208
GeneralsMD/Code/Libraries/Source/profile/profile_priv.dox
Normal file
@@ -0,0 +1,208 @@
|
||||
# Doxyfile 1.3.1
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# General configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
PROJECT_NAME = "EA/Profile module"
|
||||
PROJECT_NUMBER =
|
||||
OUTPUT_DIRECTORY = doc/
|
||||
OUTPUT_LANGUAGE = English
|
||||
USE_WINDOWS_ENCODING = YES
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_PRIVATE = YES
|
||||
EXTRACT_STATIC = YES
|
||||
EXTRACT_LOCAL_CLASSES = YES
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
HIDE_FRIEND_COMPOUNDS = YES
|
||||
HIDE_IN_BODY_DOCS = YES
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = YES
|
||||
FULL_PATH_NAMES = NO
|
||||
STRIP_FROM_PATH =
|
||||
INTERNAL_DOCS = YES
|
||||
CASE_SENSE_NAMES = YES
|
||||
SHORT_NAMES = NO
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
SHOW_INCLUDE_FILES = YES
|
||||
JAVADOC_AUTOBRIEF = NO
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
DETAILS_AT_TOP = NO
|
||||
INHERIT_DOCS = YES
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
TAB_SIZE = 8
|
||||
GENERATE_TODOLIST = YES
|
||||
GENERATE_TESTLIST = YES
|
||||
GENERATE_BUGLIST = YES
|
||||
GENERATE_DEPRECATEDLIST= YES
|
||||
ALIASES = "todo_opt=\todo (Opt) " \
|
||||
"todo_urgent=\todo <b>(URGENT)</b> " \
|
||||
"todo_feature=\todo (Feature) "
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
SHOW_USED_FILES = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to warning and progress messages
|
||||
#---------------------------------------------------------------------------
|
||||
QUIET = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
WARN_LOGFILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = ./
|
||||
FILE_PATTERNS = profile*.h internal*.h *.cpp
|
||||
RECURSIVE = NO
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS =
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS =
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
SOURCE_BROWSER = NO
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCES_RELATION = YES
|
||||
VERBATIM_HEADERS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
ALPHABETICAL_INDEX = YES
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
IGNORE_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the HTML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_HTML = YES
|
||||
HTML_OUTPUT = html_priv
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER =
|
||||
HTML_FOOTER =
|
||||
HTML_STYLESHEET =
|
||||
HTML_ALIGN_MEMBERS = YES
|
||||
GENERATE_HTMLHELP = YES
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
GENERATE_CHI = NO
|
||||
BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
DISABLE_INDEX = NO
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
GENERATE_TREEVIEW = YES
|
||||
TREEVIEW_WIDTH = 250
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the LaTeX output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_LATEX = NO
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = a4wide
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
PDF_HYPERLINKS = NO
|
||||
USE_PDFLATEX = NO
|
||||
LATEX_BATCHMODE = NO
|
||||
LATEX_HIDE_INDICES = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_RTF = YES
|
||||
RTF_OUTPUT = rtf_priv
|
||||
COMPACT_RTF = NO
|
||||
RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_MAN = NO
|
||||
MAN_OUTPUT = man
|
||||
MAN_EXTENSION = .3
|
||||
MAN_LINKS = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the XML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_XML = NO
|
||||
XML_OUTPUT = xml
|
||||
XML_SCHEMA =
|
||||
XML_DTD =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_PERLMOD = NO
|
||||
PERLMOD_LATEX = NO
|
||||
PERLMOD_PRETTY = YES
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = NO
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED = _DEBUG \
|
||||
DOXYGEN
|
||||
EXPAND_AS_DEFINED =
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::addtions related to external references
|
||||
#---------------------------------------------------------------------------
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE =
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
PERL_PATH = /usr/bin/perl
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
CLASS_DIAGRAMS = YES
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = YES
|
||||
CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = YES
|
||||
TEMPLATE_RELATIONS = NO
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DOT_IMAGE_FORMAT = png
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
MAX_DOT_GRAPH_WIDTH = 1024
|
||||
MAX_DOT_GRAPH_HEIGHT = 1024
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
GENERATE_LEGEND = YES
|
||||
DOT_CLEANUP = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::addtions related to the search engine
|
||||
#---------------------------------------------------------------------------
|
||||
SEARCHENGINE = NO
|
||||
CGI_NAME = search.cgi
|
||||
CGI_URL =
|
||||
DOC_URL =
|
||||
DOC_ABSPATH =
|
||||
BIN_ABSPATH = /usr/local/bin/
|
||||
EXT_DOC_PATHS =
|
304
GeneralsMD/Code/Libraries/Source/profile/profile_result.cpp
Normal file
304
GeneralsMD/Code/Libraries/Source/profile/profile_result.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile_result.cpp $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #2 $
|
||||
// $DateTime: 2003/08/12 15:05:00 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Result function interface and result functions
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "_pch.h"
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ProfileResultFileCSV
|
||||
|
||||
ProfileResultInterface *ProfileResultFileCSV::Create(int, const char * const *)
|
||||
{
|
||||
return new (ProfileAllocMemory(sizeof(ProfileResultFileCSV))) ProfileResultFileCSV();
|
||||
}
|
||||
|
||||
void ProfileResultFileCSV::WriteThread(ProfileFuncLevel::Thread &thread)
|
||||
{
|
||||
char help[40];
|
||||
|
||||
sprintf(help,"prof%08x-all.csv",thread.GetId());
|
||||
FILE *f=fopen(help,"wt");
|
||||
|
||||
// CSV file header
|
||||
fprintf(f,"Function\tFile\tCall count\tPTT (all)\tGTT (all)\tPT/C (all)\tGT/C (all)\tCaller (all)");
|
||||
for (unsigned k=0;k<Profile::GetFrameCount();k++)
|
||||
{
|
||||
const char *s=Profile::GetFrameName(k);
|
||||
fprintf(f,"\tCall (%s)\tPTT (%s)\tGTT (%s)\tPT/C (%s)\tGT/C (%s)\tCaller (%s)",s,s,s,s,s,s);
|
||||
}
|
||||
fprintf(f,"\n");
|
||||
|
||||
// now show all profile IDs (functions)
|
||||
ProfileFuncLevel::Id id;
|
||||
for (k=0;thread.EnumProfile(k,id);k++)
|
||||
{
|
||||
fprintf(f,"%s[%08x]\t%s, %i",id.GetFunction(),id.GetAddress(),
|
||||
id.GetSource(),id.GetLine());
|
||||
|
||||
for (unsigned i=ProfileFuncLevel::Id::Total;i!=Profile::GetFrameCount();i++)
|
||||
{
|
||||
if (!id.GetCalls(i))
|
||||
{
|
||||
// early skip...
|
||||
fprintf(f,"\t\t\t\t\t\t");
|
||||
continue;
|
||||
}
|
||||
|
||||
// call count
|
||||
fprintf(f,"\t%I64i",id.GetCalls(i));
|
||||
|
||||
// pure total time
|
||||
fprintf(f,"\t%I64i",id.GetFunctionTime(i));
|
||||
|
||||
// global total time
|
||||
fprintf(f,"\t%I64i",id.GetTime(i));
|
||||
|
||||
// pure time per call
|
||||
fprintf(f,"\t%I64i",id.GetFunctionTime(i)/id.GetCalls(i));
|
||||
|
||||
// global time per call
|
||||
fprintf(f,"\t%I64i",id.GetTime(i)/id.GetCalls(i));
|
||||
|
||||
// list of callers
|
||||
ProfileFuncLevel::IdList idlist=id.GetCaller(i);
|
||||
fprintf(f,"\t");
|
||||
ProfileFuncLevel::Id callid;
|
||||
unsigned count;
|
||||
for (unsigned j=0;idlist.Enum(j,callid,&count);j++)
|
||||
fprintf(f," %s[%08x](%i)",callid.GetFunction(),callid.GetAddress(),count);
|
||||
}
|
||||
fprintf(f,"\n");
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void ProfileResultFileCSV::WriteResults(void)
|
||||
{
|
||||
ProfileFuncLevel::Thread t;
|
||||
for (unsigned k=0;ProfileFuncLevel::EnumThreads(k,t);k++)
|
||||
WriteThread(t);
|
||||
|
||||
FILE *f=fopen("profile-high.csv","wt");
|
||||
|
||||
// CSV file header
|
||||
fprintf(f,"Profile\tUnit\ttotal");
|
||||
for (k=0;k<Profile::GetFrameCount();k++)
|
||||
fprintf(f,"\t%s",Profile::GetFrameName(k));
|
||||
fprintf(f,"\n");
|
||||
|
||||
// now show all high level profile IDs
|
||||
ProfileHighLevel::Id id;
|
||||
for (k=0;ProfileHighLevel::EnumProfile(k,id);k++)
|
||||
{
|
||||
fprintf(f,"%s\t%s\t%s",id.GetName(),id.GetUnit(),id.GetTotalValue());
|
||||
for (unsigned i=0;i<Profile::GetFrameCount();i++)
|
||||
{
|
||||
const char *p=id.GetValue(i);
|
||||
fprintf(f,"\t%s",p?p:"");
|
||||
}
|
||||
fprintf(f,"\n");
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void ProfileResultFileCSV::Delete(void)
|
||||
{
|
||||
this->~ProfileResultFileCSV();
|
||||
ProfileFreeMemory(this);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// ProfileResultFileDOT
|
||||
|
||||
ProfileResultInterface *ProfileResultFileDOT::Create(int argn, const char * const *argv)
|
||||
{
|
||||
return new (ProfileAllocMemory(sizeof(ProfileResultFileDOT)))
|
||||
ProfileResultFileDOT(argn>0?argv[0]:NULL,
|
||||
argn>1?argv[1]:NULL,
|
||||
argn>2?atoi(argv[2]):NULL);
|
||||
}
|
||||
|
||||
ProfileResultFileDOT::ProfileResultFileDOT(const char *fileName, const char *frameName, int foldThreshold)
|
||||
{
|
||||
if (!fileName)
|
||||
fileName="profile.dot";
|
||||
m_fileName=(char *)ProfileAllocMemory(strlen(fileName)+1);
|
||||
strcpy(m_fileName,fileName);
|
||||
if (frameName)
|
||||
{
|
||||
m_frameName=(char *)ProfileAllocMemory(strlen(frameName)+1);
|
||||
strcpy(m_frameName,frameName);
|
||||
}
|
||||
else
|
||||
m_frameName=NULL;
|
||||
m_foldThreshold=foldThreshold;
|
||||
}
|
||||
|
||||
void ProfileResultFileDOT::WriteResults(void)
|
||||
{
|
||||
// search "main" thread
|
||||
ProfileFuncLevel::Thread t,tMax;
|
||||
if (!ProfileFuncLevel::EnumThreads(0,tMax))
|
||||
return;
|
||||
|
||||
unsigned curMax=0;
|
||||
for (unsigned k=1;ProfileFuncLevel::EnumThreads(k,t);k++)
|
||||
{
|
||||
for (;curMax++;)
|
||||
{
|
||||
ProfileFuncLevel::Id help;
|
||||
if (!tMax.EnumProfile(curMax,help))
|
||||
{
|
||||
tMax=t;
|
||||
break;
|
||||
}
|
||||
if (!t.EnumProfile(curMax,help))
|
||||
break;
|
||||
curMax++;
|
||||
}
|
||||
}
|
||||
|
||||
// search frame
|
||||
unsigned frame=ProfileFuncLevel::Id::Total;
|
||||
if (m_frameName)
|
||||
{
|
||||
for (unsigned k=0;k<Profile::GetFrameCount();k++)
|
||||
if (!strcmp(Profile::GetFrameName(k),m_frameName))
|
||||
{
|
||||
frame=k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// determine number of active functions
|
||||
unsigned active=0;
|
||||
ProfileFuncLevel::Id id;
|
||||
for (k=0;tMax.EnumProfile(k,id);k++)
|
||||
if (id.GetCalls(frame))
|
||||
active++;
|
||||
|
||||
FILE *f=fopen(m_fileName,"wt");
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
// DOT header
|
||||
fprintf(f,"digraph G { rankdir=\"LR\";\n");
|
||||
fprintf(f,"node [shape=box, fontname=Arial]\n");
|
||||
fprintf(f,"edge [arrowhead=%s, labelfontname=Arial, labelfontsize=10, labelangle=0, labelfontcolor=blue]\n",
|
||||
active>m_foldThreshold?"closed":"none");
|
||||
|
||||
// fold or not?
|
||||
if (active>m_foldThreshold)
|
||||
{
|
||||
// folding version
|
||||
|
||||
// build source code clusters first
|
||||
FoldHelper *fold=NULL;
|
||||
for (k=0;tMax.EnumProfile(k,id);k++)
|
||||
{
|
||||
const char *source=id.GetSource();
|
||||
for (FoldHelper *cur=fold;cur;cur=cur->next)
|
||||
if (!strcmp(source,cur->source))
|
||||
{
|
||||
if (cur->numId<MAX_FUNCTIONS_PER_FILE)
|
||||
cur->id[cur->numId++]=id;
|
||||
break;
|
||||
}
|
||||
if (!cur)
|
||||
{
|
||||
cur=(FoldHelper *)ProfileAllocMemory(sizeof(FoldHelper));
|
||||
cur->next=fold;
|
||||
fold=cur;
|
||||
cur->source=source;
|
||||
cur->numId=1;
|
||||
cur->id[0]=id;
|
||||
}
|
||||
}
|
||||
|
||||
// now write data
|
||||
for (FoldHelper *cur=fold;cur;cur=cur->next)
|
||||
{
|
||||
for (FoldHelper *cur2=fold;cur2;cur2=cur2->next)
|
||||
cur2->mark=false;
|
||||
|
||||
for (k=0;k<cur->numId;k++)
|
||||
{
|
||||
ProfileFuncLevel::IdList idlist=id.GetCaller(frame);
|
||||
ProfileFuncLevel::Id caller;
|
||||
for (unsigned i=0;idlist.Enum(i,caller);i++)
|
||||
{
|
||||
const char *s=caller.GetSource();
|
||||
for (FoldHelper *cur2=fold;cur2;cur2=cur2->next)
|
||||
if (!strcmp(cur2->source,s))
|
||||
break;
|
||||
if (!cur2||cur2->mark)
|
||||
continue;
|
||||
cur2->mark=true;
|
||||
|
||||
fprintf(f,"\"%s\" -> \"%s\"\n",s,cur->source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
while (fold)
|
||||
{
|
||||
FoldHelper *next=fold->next;
|
||||
ProfileFreeMemory(fold);
|
||||
fold=next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-folding version
|
||||
for (k=0;tMax.EnumProfile(k,id);k++)
|
||||
if (id.GetCalls(frame))
|
||||
fprintf(f,"f%08x [label=\"%s\"]\n",id.GetAddress(),id.GetFunction());
|
||||
for (k=0;tMax.EnumProfile(k,id);k++)
|
||||
{
|
||||
ProfileFuncLevel::IdList idlist=id.GetCaller(frame);
|
||||
ProfileFuncLevel::Id caller;
|
||||
unsigned count;
|
||||
for (unsigned i=0;idlist.Enum(i,caller,&count);i++)
|
||||
fprintf(f,"f%08x -> f%08x [headlabel=\"%i\"];\n",caller.GetAddress(),id.GetAddress(),count);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(f,"}\n");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void ProfileResultFileDOT::Delete(void)
|
||||
{
|
||||
this->~ProfileResultFileDOT();
|
||||
ProfileFreeMemory(this);
|
||||
}
|
66
GeneralsMD/Code/Libraries/Source/profile/profile_result.h
Normal file
66
GeneralsMD/Code/Libraries/Source/profile/profile_result.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/profile_result.h $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #1 $
|
||||
// $DateTime: 2003/07/09 10:57:23 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Result function interface and result functions
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef _MSC_VER
|
||||
# pragma once
|
||||
#endif
|
||||
#ifndef PROFILE_RESULT_H // Include guard
|
||||
#define PROFILE_RESULT_H
|
||||
|
||||
/**
|
||||
\brief Result function class.
|
||||
|
||||
Factories for instances of this class are registered using
|
||||
\ref Profile::AddResultFunction.
|
||||
*/
|
||||
class ProfileResultInterface
|
||||
{
|
||||
// no copying
|
||||
ProfileResultInterface(const ProfileResultInterface&);
|
||||
ProfileResultInterface& operator=(const ProfileResultInterface&);
|
||||
|
||||
public:
|
||||
/**
|
||||
\brief Write out results.
|
||||
|
||||
This function is called on program exit.
|
||||
*/
|
||||
virtual void WriteResults(void)=0;
|
||||
|
||||
/**
|
||||
\brief Destroys the current result function.
|
||||
|
||||
Use this function instead of just delete'ing the instance.
|
||||
*/
|
||||
virtual void Delete(void)=0;
|
||||
|
||||
protected:
|
||||
ProfileResultInterface(void) {}
|
||||
};
|
||||
|
||||
#endif // PROFILE_RESULT_H
|
94
GeneralsMD/Code/Libraries/Source/profile/test1/test1.cpp
Normal file
94
GeneralsMD/Code/Libraries/Source/profile/test1/test1.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
** Command & Conquer Generals Zero Hour(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////EA-V1
|
||||
// $File: //depot/GeneralsMD/Staging/code/Libraries/Source/profile/test1/test1.cpp $
|
||||
// $Author: mhoffe $
|
||||
// $Revision: #3 $
|
||||
// $DateTime: 2003/07/09 10:57:23 $
|
||||
//
|
||||
// <20>2003 Electronic Arts
|
||||
//
|
||||
// Profile module - Test 1 (basic testing)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#include "../profile.h"
|
||||
#include "../../debug/debug.h"
|
||||
#include <stdio.h>
|
||||
|
||||
const char *DebugGetDefaultCommands(void)
|
||||
{
|
||||
return "!debug.io con add\ndebug.add l + *\nprofile.result";
|
||||
}
|
||||
|
||||
extern int q;
|
||||
|
||||
void calcThis(void)
|
||||
{
|
||||
q++;
|
||||
}
|
||||
|
||||
void calcThat(void)
|
||||
{
|
||||
calcThis();
|
||||
q--;
|
||||
}
|
||||
|
||||
// it must be done this "complicated" because
|
||||
// otherwise VC does not generate a real recursive
|
||||
// function call for this simple function...
|
||||
void recursion2(int level);
|
||||
|
||||
void recursion(int level)
|
||||
{
|
||||
q+=level;
|
||||
if (level<5000)
|
||||
recursion2(level+1);
|
||||
}
|
||||
|
||||
void recursion2(int level)
|
||||
{
|
||||
recursion(level);
|
||||
}
|
||||
|
||||
void recursionShell(void)
|
||||
{
|
||||
ProfileHighLevel::Block b("Test block");
|
||||
recursion(0);
|
||||
}
|
||||
|
||||
void showResults(void)
|
||||
{
|
||||
ProfileHighLevel::Id id;
|
||||
for (unsigned index=0;ProfileHighLevel::EnumProfile(index,id);index++)
|
||||
printf("%-16s%-6s %s\n",id.GetName(),id.GetTotalValue(),id.GetUnit());
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
for (int k=0;k<100;k++)
|
||||
if (k%2&&k>80)
|
||||
calcThat();
|
||||
else
|
||||
calcThis();
|
||||
|
||||
recursionShell();
|
||||
|
||||
showResults();
|
||||
}
|
||||
|
||||
int q;
|
116
GeneralsMD/Code/Libraries/Source/profile/test1/test1.dsp
Normal file
116
GeneralsMD/Code/Libraries/Source/profile/test1/test1.dsp
Normal file
@@ -0,0 +1,116 @@
|
||||
# Microsoft Developer Studio Project File - Name="test1" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||
|
||||
CFG=test1 - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "test1.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "test1.mak" CFG="test1 - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "test1 - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "test1 - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE "test1 - Win32 Profile" (based on "Win32 (x86) Console Application")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName "test1"
|
||||
# PROP Scc_LocalPath "."
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "test1 - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /WX /GX /Zi /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /libpath:"..\..\..\lib"
|
||||
|
||||
!ELSEIF "$(CFG)" == "test1 - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MDd /W3 /WX /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /libpath:"..\..\..\lib"
|
||||
|
||||
!ELSEIF "$(CFG)" == "test1 - Win32 Profile"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Profile"
|
||||
# PROP BASE Intermediate_Dir "Profile"
|
||||
# PROP BASE Ignore_Export_Lib 0
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Profile"
|
||||
# PROP Intermediate_Dir "Profile"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MD /W3 /WX /GX /Zi /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /WX /GX /Zi /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "_PROFILE" /YX /FD /Gh /GH /c
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /libpath:"..\..\..\lib"
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "test1 - Win32 Release"
|
||||
# Name "test1 - Win32 Debug"
|
||||
# Name "test1 - Win32 Profile"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\test1.cpp
|
||||
# End Source File
|
||||
# End Target
|
||||
# End Project
|
Reference in New Issue
Block a user