initial
This commit is contained in:
68
tier0/DESKey/ALGO.H
Normal file
68
tier0/DESKey/ALGO.H
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifndef APIENTRY
|
||||
#define APIENTRY FAR PASCAL
|
||||
#endif
|
||||
|
||||
void APIENTRY DK2SetupAlgorithmString ( LPSTR String, WORD Cmd );
|
||||
|
||||
void APIENTRY DK2SetMaximumIterations( WORD MaxIter );
|
||||
|
||||
void APIENTRY DK2Sub_ReadRandomNumbers( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPSTR PKey,
|
||||
WORD Seed,
|
||||
LPSTR Buffer );
|
||||
|
||||
void APIENTRY DK2Sub_ReadMemory( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPSTR PKey,
|
||||
WORD Seed,
|
||||
WORD Address,
|
||||
LPSTR Buffer );
|
||||
|
||||
void APIENTRY DK2Sub_WriteMemory( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPSTR PKey,
|
||||
WORD Seed,
|
||||
WORD Address,
|
||||
WORD SecretCounter,
|
||||
LPSTR Password,
|
||||
LPSTR DUSN,
|
||||
LPSTR Buffer );
|
||||
|
||||
void APIENTRY DK2Sub_ReadDownCounter( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPSTR PKey,
|
||||
LPDWORD DownCounter );
|
||||
|
||||
void APIENTRY DK2Sub_SubtractDownCounter( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPSTR PKey,
|
||||
DWORD SubValue,
|
||||
LPDWORD DownCounter );
|
||||
|
||||
void APIENTRY DK2Sub_RestartDownCounter( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPSTR PKey,
|
||||
WORD SecretCounter,
|
||||
LPSTR Password,
|
||||
LPSTR DUSN,
|
||||
DWORD DownCounter );
|
||||
|
||||
void APIENTRY DK2Sub_AccessNormalCommands( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPSTR PKey,
|
||||
WORD Disable );
|
||||
|
||||
void APIENTRY DK2Algorithm( WORD Iterations,
|
||||
LPSTR AlgoStr,
|
||||
LPSTR PrivKey );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
BIN
tier0/DESKey/ALGO32.LIB
Normal file
BIN
tier0/DESKey/ALGO32.LIB
Normal file
Binary file not shown.
366
tier0/DESKey/DK2WIN32.H
Normal file
366
tier0/DESKey/DK2WIN32.H
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* $History: DK2WIN32.H $
|
||||
*
|
||||
* ***************** Version 1 *****************
|
||||
* User: Alun Date: 1/07/99 Time: 11:31
|
||||
* Created in $/DK2/Software/C Drivers/Windows/API/DLL/DK2Win32
|
||||
* Initial version added to Source Safe version control
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Error Codes
|
||||
//
|
||||
// All codes are returned from DK2GetLastError and can be formated
|
||||
// into a message string by calling DK2FormatError
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_SUCCESS
|
||||
//
|
||||
// The command was successfull
|
||||
|
||||
#define DK2ERR_SUCCESS 0x0000
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_TOOMANUUSERS
|
||||
//
|
||||
// One or mode DK2 network servers were found but they were all
|
||||
// full.
|
||||
|
||||
#define DK2ERR_TOOMANYUSERS 0x0001
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_ACCESS_DENIED
|
||||
//
|
||||
// A DK2 network servers were found but access was denied, either
|
||||
// due to user restrictions, or invalid login memory.
|
||||
// See FindDK2Ex
|
||||
|
||||
#define DK2ERR_ACCESS_DENIED 0x0002
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_DESKEY_NOTFOUND
|
||||
//
|
||||
// A DK2 command failed because a DK2 was not found, either
|
||||
// locally or accross the network
|
||||
|
||||
#define DK2ERR_DESKEY_NOTFOUND 0x0003
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_NORESPONSE
|
||||
//
|
||||
// A DK2 oommand to a server failed becase the server did not
|
||||
// respond.
|
||||
|
||||
#define DK2ERR_NORESPONSE 0x0004
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_NOSERVERS
|
||||
//
|
||||
// The drivers searched for a DK2 a network server, but none were
|
||||
// found.
|
||||
|
||||
#define DK2ERR_NOSERVERS 0x0005
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_DRIVERNOTINSTALLED
|
||||
//
|
||||
// The DK2 drivers are not installed
|
||||
|
||||
#define DK2ERR_DRIVERNOTINSTALLED 0x0006
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_COMMANDNOTSUPPORTED
|
||||
//
|
||||
// The DK2 does not support the requested command
|
||||
|
||||
#define DK2ERR_COMMANDNOTSUPPORTED 0x0007
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_ALREADYNETWORK
|
||||
//
|
||||
// A local DK2 command failed because there is a server runnning
|
||||
// the command must be carried out over the network.
|
||||
|
||||
#define DK2ERR_ALREADYNETWORK 0x1001
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_COMMANDNOTNETWORK
|
||||
//
|
||||
// A DK2 command failed because the command cannot operate over
|
||||
// the network
|
||||
|
||||
#define DK2ERR_COMMANDNOTNETWORK 0x1002
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_TOOMANYPROGS
|
||||
//
|
||||
// The maximum possible programs using the DK2 drivers has been
|
||||
// reached
|
||||
|
||||
#define DK2ERR_TOOMANYPROGS 0x1004
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_BADOS
|
||||
//
|
||||
// The DK2 command will not function on the current operating system
|
||||
|
||||
#define DK2ERR_BADOS 0x1005
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_NETWORKONLY
|
||||
//
|
||||
// The DK2 command failed because it can only be performed across
|
||||
// the network and a local connection was specified
|
||||
|
||||
#define DK2ERR_NETWORKONLY 0x1006
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_CANCELLED
|
||||
//
|
||||
// Returned the GDI/ECP Window is cancelled
|
||||
|
||||
#define DK2ERR_CANCELLED 0x1007
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_FAILURE
|
||||
//
|
||||
// The command failed due to an error comunicating with the protocol
|
||||
|
||||
#define DK2ERR_FAILURE 0x8000
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_PROTOCOLFAILURE
|
||||
//
|
||||
// The DK2 command faied due to a problem in the protocol
|
||||
|
||||
#define DK2ERR_PROTOCOLFAILURE 0x8001
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_BADPARAMETER
|
||||
//
|
||||
// The DK2 command failed due to an invalid parameter passed to the
|
||||
// function
|
||||
|
||||
#define DK2ERR_BADPARAMETER 0x8002
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_NOMEMORY
|
||||
//
|
||||
// The DK2 command failed because the function could not allocate
|
||||
// enough memory
|
||||
|
||||
#define DK2ERR_NOMEMORY 0x8003
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_STARTPROTOCOL
|
||||
//
|
||||
// The DK2 command failed because the current protocol did not
|
||||
// start
|
||||
|
||||
#define DK2ERR_STARTPROTOCOL 0x8004
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_NOPROTOCOL
|
||||
//
|
||||
// The DK2 command failed be cause the current protocol does not
|
||||
// exist or is not loaded
|
||||
|
||||
#define DK2ERR_NOPROTOCOL 0x8005
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_NOSERVERMEMORY
|
||||
//
|
||||
// The DK2 command failed because the server could not allocate
|
||||
// enough memory
|
||||
|
||||
#define DK2ERR_NOSERVERMEMORY 0x8006
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2ERR_INVALIDCONNECTION
|
||||
//
|
||||
// The DK2 command failed because the specified connection is
|
||||
// invalid
|
||||
|
||||
#define DK2ERR_INVALIDCONNECTION 0x8007
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Structures
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma pack( 1 )
|
||||
|
||||
#define DK2MEMORYMAP
|
||||
typedef struct _tDK2Memory
|
||||
{
|
||||
WORD wAddress;
|
||||
WORD wSeed;
|
||||
WORD wCount;
|
||||
LPSTR lpBytes;
|
||||
WORD wModule;
|
||||
} DK2MEMORY, FAR *LPDK2MEMORY;
|
||||
|
||||
typedef struct _tDateTime
|
||||
{
|
||||
WORD wDay;
|
||||
WORD wMonth;
|
||||
WORD wYear;
|
||||
WORD wHour;
|
||||
WORD wMinute;
|
||||
WORD wSecond;
|
||||
WORD wMilliseconds;
|
||||
} DATETIME, *NPDATETIME, FAR *LPDATETIME;
|
||||
|
||||
#pragma pack()
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2 Functions
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
BOOL APIENTRY DK2DriverInstalled( void );
|
||||
|
||||
WORD APIENTRY FindDK2( LPSTR Id, LPSTR PKey );
|
||||
|
||||
WORD APIENTRY FindDK2Ex( LPSTR Id, LPSTR PKey, LPDK2MEMORY lpDK2Memory );
|
||||
|
||||
WORD APIENTRY FindDK2ExP( LPSTR Id, LPSTR PKey, WORD Address, WORD Seed, WORD Count, LPSTR Bytes, WORD Module );
|
||||
|
||||
void APIENTRY DK2LogoutFromServer( WORD DataReg );
|
||||
|
||||
WORD APIENTRY DK2FindLPTPort( WORD Port );
|
||||
|
||||
WORD APIENTRY DK2GetDelayTime ( void );
|
||||
|
||||
void APIENTRY DK2SetDelayTime( WORD Delay );
|
||||
|
||||
void APIENTRY DK2ReadRandomNumbers( WORD DataReg,
|
||||
LPSTR Id,
|
||||
WORD Seed,
|
||||
LPSTR Buffer,
|
||||
WORD BytesToRead );
|
||||
|
||||
void APIENTRY DK2ThroughEncryption( WORD DataReg,
|
||||
LPSTR Id,
|
||||
WORD Seed,
|
||||
LPSTR Buffer,
|
||||
WORD BytesToEncrypt );
|
||||
|
||||
|
||||
void APIENTRY DK2ReadMemory( WORD DataReg,
|
||||
LPSTR Id,
|
||||
WORD Seed,
|
||||
WORD Address,
|
||||
LPSTR Buffer,
|
||||
WORD BytesToRead );
|
||||
|
||||
void APIENTRY DK2WriteMemory( WORD DataReg,
|
||||
LPSTR Id,
|
||||
WORD Seed,
|
||||
WORD Address,
|
||||
LPSTR Buffer,
|
||||
WORD BytesToWrite,
|
||||
LPSTR Password );
|
||||
|
||||
void APIENTRY DK2ReadDownCounter( WORD DataReg,
|
||||
LPSTR Id,
|
||||
DWORD *DownCounter );
|
||||
|
||||
|
||||
void APIENTRY DK2DecrementDownCounter( WORD DataReg,
|
||||
LPSTR Id );
|
||||
|
||||
BOOL APIENTRY DK2RegisterModule( WORD DataReg, WORD wModule );
|
||||
|
||||
BOOL APIENTRY DK2UnregisterModule( WORD DataReg, WORD wModule );
|
||||
|
||||
void APIENTRY DK2RestartDownCounter( WORD DataReg,
|
||||
LPSTR Id,
|
||||
DWORD NewCounter,
|
||||
LPSTR Password );
|
||||
|
||||
void APIENTRY DK2ReadDUSN( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPSTR Password,
|
||||
LPWORD SecCount,
|
||||
LPSTR DUSN );
|
||||
|
||||
void APIENTRY DK2SendAlgorithmString( WORD DataReg,
|
||||
LPSTR Id,
|
||||
WORD Iteration1,
|
||||
WORD Iteration2,
|
||||
LPSTR Buffer1,
|
||||
LPSTR Buffer2 );
|
||||
|
||||
void APIENTRY DK2SendAlgorithmBuffer( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPWORD Iteration,
|
||||
LPSTR Buffer,
|
||||
WORD BufferCount );
|
||||
|
||||
void APIENTRY DK2SendAndReceive( WORD DataReg, LPSTR Id, LPSTR lpFirst, WORD wFirst, LPSTR lpSend, WORD wSend, LPSTR lpReceive, WORD wReceive, WORD wCount );
|
||||
|
||||
BOOL APIENTRY DK2Success( void );
|
||||
|
||||
void APIENTRY DK2AllowChangeInterrupts( WORD Change );
|
||||
|
||||
WORD APIENTRY DK2DetectSpeed( WORD DataReg,
|
||||
LPSTR Id );
|
||||
|
||||
WORD APIENTRY DK2SubDetectSpeed( WORD DataReg,
|
||||
LPSTR Id,
|
||||
LPSTR PKey,
|
||||
LPSTR Bytes );
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// time function
|
||||
void APIENTRY DK2GetSystemTime( WORD DateReg, LPSTR Id, LPDATETIME lpDateTime );
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2 Flags
|
||||
|
||||
#define DK2_BITRONICS 0x00000001
|
||||
#define DK2_HASBITRONICS 0x00000002
|
||||
|
||||
DWORD APIENTRY DK2GetFlags ( WORD DataReg,
|
||||
LPSTR Id );
|
||||
|
||||
VOID APIENTRY DK2SetFlags ( WORD DataReg,
|
||||
LPSTR Id,
|
||||
DWORD Flags );
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
WORD APIENTRY DK2Encode( LPSTR lpszData,
|
||||
WORD cbData,
|
||||
LPSTR lpszEncode,
|
||||
WORD cbEncode );
|
||||
|
||||
WORD APIENTRY DK2Decode( LPSTR lpszData,
|
||||
LPSTR lpszDecode );
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// DK2 Access Flags - Override Searching Network or Local
|
||||
|
||||
#define DNET_NETWORK 0x0001
|
||||
#define DNET_LOCAL 0x0002
|
||||
|
||||
void APIENTRY DK2SetAccessFlags( WORD wFlags );
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
DWORD APIENTRY DK2GetLastError( void );
|
||||
|
||||
void APIENTRY DK2FormatError( DWORD Error, LPSTR ErrorString, int MaxLen );
|
||||
|
||||
WORD APIENTRY DK2GetServerName( WORD DataReg, LPSTR lpszServerName, LPSTR lpszComputerName );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
BIN
tier0/DESKey/DK2WIN32.LIB
Normal file
BIN
tier0/DESKey/DK2WIN32.LIB
Normal file
Binary file not shown.
665
tier0/PMELib.cpp
Normal file
665
tier0/PMELib.cpp
Normal file
@@ -0,0 +1,665 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#pragma warning( disable : 4530 ) // warning: exception handler -GX option
|
||||
|
||||
#include "tier0/valve_off.h"
|
||||
#include "tier0/pmelib.h"
|
||||
#if _MSC_VER >=1300
|
||||
#else
|
||||
#include "winioctl.h"
|
||||
#endif
|
||||
#include "tier0/valve_on.h"
|
||||
|
||||
#include "tier0/ioctlcodes.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
PME* PME::_singleton = 0;
|
||||
|
||||
// Single interface.
|
||||
PME* PME::Instance()
|
||||
{
|
||||
if (_singleton == 0)
|
||||
{
|
||||
_singleton = new PME;
|
||||
}
|
||||
return _singleton;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Open the device driver and detect the processor
|
||||
//---------------------------------------------------------------------------
|
||||
HRESULT PME::Init( void )
|
||||
{
|
||||
OSVERSIONINFO OS;
|
||||
|
||||
if ( bDriverOpen )
|
||||
return E_DRIVER_ALREADY_OPEN;
|
||||
|
||||
switch( vendor )
|
||||
{
|
||||
case INTEL:
|
||||
case AMD:
|
||||
break;
|
||||
default:
|
||||
bDriverOpen = FALSE; // not an Intel or Athlon processor so return false
|
||||
return E_UNKNOWN_CPU_VENDOR;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Get the operating system version
|
||||
//-----------------------------------------------------------------------
|
||||
OS.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
|
||||
GetVersionEx( &OS );
|
||||
|
||||
if ( OS.dwPlatformId == VER_PLATFORM_WIN32_NT )
|
||||
{
|
||||
hFile = CreateFile( // WINDOWS NT
|
||||
"\\\\.\\GDPERF",
|
||||
GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
hFile = CreateFile( // WINDOWS 95
|
||||
"\\\\.\\GDPERF.VXD",
|
||||
GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE )
|
||||
return E_CANT_OPEN_DRIVER;
|
||||
|
||||
|
||||
bDriverOpen = TRUE;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// We have successfully opened the device driver, get the family
|
||||
// of the processor.
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// We need to write to counter 0 on the pro family to enable both
|
||||
// of the performance counters. We write to both so they start in a
|
||||
// known state. For the pentium this is not necessary.
|
||||
//-------------------------------------------------------------------
|
||||
if (vendor == INTEL && version.Family == PENTIUMPRO_FAMILY)
|
||||
{
|
||||
SelectP5P6PerformanceEvent(P6_CLOCK, 0, TRUE, TRUE);
|
||||
SelectP5P6PerformanceEvent(P6_CLOCK, 1, TRUE, TRUE);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Close the device driver
|
||||
//---------------------------------------------------------------------------
|
||||
HRESULT PME::Close(void)
|
||||
{
|
||||
if (bDriverOpen == false) // driver is not going
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
bDriverOpen = false;
|
||||
|
||||
if (hFile) // if we have no driver handle, return FALSE
|
||||
{
|
||||
BOOL result = CloseHandle(hFile);
|
||||
|
||||
hFile = NULL;
|
||||
return result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
|
||||
}
|
||||
else
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Select the event to monitor with counter 0
|
||||
//
|
||||
HRESULT PME::SelectP5P6PerformanceEvent(uint32 dw_event, uint32 dw_counter,
|
||||
bool b_user, bool b_kernel)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (dw_counter>1) // is the counter valid
|
||||
return E_BAD_COUNTER;
|
||||
|
||||
if (bDriverOpen == false) // driver is not going
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
if ( ((dw_event>>28)&0xF) != (uint32)version.Family)
|
||||
{
|
||||
return E_ILLEGAL_OPERATION; // this operation is not for this processor
|
||||
}
|
||||
|
||||
if ( (((dw_event & 0x300)>>8) & (dw_counter+1)) == 0 )
|
||||
{
|
||||
return E_ILLEGAL_OPERATION; // this operation is not for this counter
|
||||
}
|
||||
|
||||
switch(version.Family)
|
||||
{
|
||||
case PENTIUM_FAMILY:
|
||||
{
|
||||
uint64 i64_cesr;
|
||||
int i_kernel_bit,i_user_bit;
|
||||
BYTE u1_event = (BYTE)((dw_event & (0x3F0000))>>16);
|
||||
|
||||
if (dw_counter==0) // the kernel and user mode bits depend on
|
||||
{ // counter being used.
|
||||
i_kernel_bit = 6;
|
||||
i_user_bit = 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
i_kernel_bit = 22;
|
||||
i_user_bit = 23;
|
||||
}
|
||||
|
||||
ReadMSR(0x11, &i64_cesr); // get current P5 event select (cesr)
|
||||
|
||||
// top 32bits of cesr are not valid so ignore them
|
||||
i64_cesr &= ((dw_counter == 0)?0xffff0000:0x0000ffff);
|
||||
WriteMSR(0x11,i64_cesr); // stop the counter
|
||||
WriteMSR((dw_counter==0)?0x12:0x13,0ui64); // clear the p.counter
|
||||
|
||||
// set the user and kernel mode bits
|
||||
i64_cesr |= ( b_user?(1<<7):0 ) | ( b_kernel?(1<<6):0 );
|
||||
|
||||
// is this the special P5 value that signals count clocks??
|
||||
if (u1_event == 0x3f)
|
||||
{
|
||||
WriteMSR(0x11, i64_cesr|0x100); // Count clocks
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMSR(0x11, i64_cesr|u1_event); // Count events
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case PENTIUMPRO_FAMILY:
|
||||
|
||||
{
|
||||
BYTE u1_event = (BYTE)((dw_event & (0xFF0000))>>16);
|
||||
BYTE u1_mask = (BYTE)((dw_event & 0xFF));
|
||||
|
||||
// Event select 0 and 1 are identical.
|
||||
hr = WriteMSR((dw_counter==0)?0x186:0x187,
|
||||
|
||||
|
||||
uint64((u1_event | (b_user?(1<<16):0) | (b_kernel?(1<<17):0) | (1<<22) | (1<<18) | (u1_mask<<8)) )
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case PENTIUM4_FAMILY:
|
||||
// use the p4 path
|
||||
break;
|
||||
|
||||
default:
|
||||
return E_UNKNOWN_CPU;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Read model specific register
|
||||
//---------------------------------------------------------------------------
|
||||
HRESULT PME::ReadMSR(uint32 dw_reg, int64 * pi64_value)
|
||||
{
|
||||
DWORD dw_ret_len;
|
||||
|
||||
if (bDriverOpen == false) // driver is not going
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
BOOL result = DeviceIoControl
|
||||
(
|
||||
hFile, // Handle to device
|
||||
(DWORD) IOCTL_READ_MSR, // IO Control code for Read
|
||||
&dw_reg, // Input Buffer to driver.
|
||||
sizeof(uint32), // Length of input buffer.
|
||||
pi64_value, // Output Buffer from driver.
|
||||
sizeof(int64), // Length of output buffer in bytes.
|
||||
&dw_ret_len, // Bytes placed in output buffer.
|
||||
NULL // NULL means wait till op. completes
|
||||
);
|
||||
|
||||
HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
|
||||
if (hr == S_OK && dw_ret_len != sizeof(int64))
|
||||
hr = E_BAD_DATA;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT PME::ReadMSR(uint32 dw_reg, uint64 * pi64_value)
|
||||
{
|
||||
DWORD dw_ret_len;
|
||||
|
||||
if (bDriverOpen == false) // driver is not going
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
BOOL result = DeviceIoControl
|
||||
(
|
||||
hFile, // Handle to device
|
||||
(DWORD) IOCTL_READ_MSR, // IO Control code for Read
|
||||
&dw_reg, // Input Buffer to driver.
|
||||
sizeof(uint32), // Length of input buffer.
|
||||
pi64_value, // Output Buffer from driver.
|
||||
sizeof(uint64), // Length of output buffer in bytes.
|
||||
&dw_ret_len, // Bytes placed in output buffer.
|
||||
NULL // NULL means wait till op. completes
|
||||
);
|
||||
|
||||
HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
|
||||
if (hr == S_OK && dw_ret_len != sizeof(uint64))
|
||||
hr = E_BAD_DATA;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Write model specific register
|
||||
//---------------------------------------------------------------------------
|
||||
HRESULT PME::WriteMSR(uint32 dw_reg, const int64 & i64_value)
|
||||
{
|
||||
DWORD dw_buffer[3];
|
||||
DWORD dw_ret_len;
|
||||
|
||||
if (bDriverOpen == false) // driver is not going
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
dw_buffer[0] = dw_reg; // setup the 12 byte input
|
||||
*((int64*)(&dw_buffer[1]))= i64_value;
|
||||
|
||||
BOOL result = DeviceIoControl
|
||||
(
|
||||
hFile, // Handle to device
|
||||
(DWORD) IOCTL_WRITE_MSR, // IO Control code for Read
|
||||
dw_buffer, // Input Buffer to driver.
|
||||
12, // Length of Input buffer
|
||||
NULL, // Buffer from driver, None for WRMSR
|
||||
0, // Length of output buffer in bytes.
|
||||
&dw_ret_len, // Bytes placed in DataBuffer.
|
||||
NULL // NULL means wait till op. completes.
|
||||
);
|
||||
|
||||
HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
|
||||
if (hr == S_OK && dw_ret_len != 0)
|
||||
hr = E_BAD_DATA;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
HRESULT PME::WriteMSR(uint32 dw_reg, const uint64 & i64_value)
|
||||
{
|
||||
DWORD dw_buffer[3];
|
||||
DWORD dw_ret_len;
|
||||
|
||||
if (bDriverOpen == false) // driver is not going
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
dw_buffer[0] = dw_reg; // setup the 12 byte input
|
||||
*((uint64*)(&dw_buffer[1]))= i64_value;
|
||||
|
||||
BOOL result = DeviceIoControl
|
||||
(
|
||||
hFile, // Handle to device
|
||||
(DWORD) IOCTL_WRITE_MSR, // IO Control code for Read
|
||||
dw_buffer, // Input Buffer to driver.
|
||||
12, // Length of Input buffer
|
||||
NULL, // Buffer from driver, None for WRMSR
|
||||
0, // Length of output buffer in bytes.
|
||||
&dw_ret_len, // Bytes placed in DataBuffer.
|
||||
NULL // NULL means wait till op. completes.
|
||||
);
|
||||
|
||||
//E_POINTER
|
||||
HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
|
||||
if (hr == S_OK && dw_ret_len != 0)
|
||||
hr = E_BAD_DATA;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma hdrstop
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Return the frequency of the processor in Hz.
|
||||
//
|
||||
|
||||
double PME::GetCPUClockSpeedFast(void)
|
||||
{
|
||||
int64 i64_perf_start, i64_perf_freq, i64_perf_end;
|
||||
int64 i64_clock_start,i64_clock_end;
|
||||
double d_loop_period, d_clock_freq;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Query the performance of the Windows high resolution timer.
|
||||
//-----------------------------------------------------------------------
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&i64_perf_freq);
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Query the current value of the Windows high resolution timer.
|
||||
//-----------------------------------------------------------------------
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&i64_perf_start);
|
||||
i64_perf_end = 0;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Time of loop of 250000 windows cycles with RDTSC
|
||||
//-----------------------------------------------------------------------
|
||||
RDTSC(i64_clock_start);
|
||||
while(i64_perf_end<i64_perf_start+250000)
|
||||
{
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&i64_perf_end);
|
||||
}
|
||||
RDTSC(i64_clock_end);
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Caclulate the frequency of the RDTSC timer and therefore calculate
|
||||
// the frequency of the processor.
|
||||
//-----------------------------------------------------------------------
|
||||
i64_clock_end -= i64_clock_start;
|
||||
|
||||
d_loop_period = ((double)(i64_perf_freq)) / 250000.0;
|
||||
d_clock_freq = ((double)(i64_clock_end & 0xffffffff))*d_loop_period;
|
||||
|
||||
return (float)d_clock_freq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// takes 1 second
|
||||
double PME::GetCPUClockSpeedSlow(void)
|
||||
{
|
||||
|
||||
if (m_CPUClockSpeed != 0)
|
||||
return m_CPUClockSpeed;
|
||||
|
||||
unsigned long start_ms, stop_ms;
|
||||
unsigned long start_tsc,stop_tsc;
|
||||
|
||||
// boosting priority helps with noise. its optional and i dont think
|
||||
// it helps all that much
|
||||
|
||||
PME * pme = PME::Instance();
|
||||
|
||||
pme->SetProcessPriority(ProcessPriorityHigh);
|
||||
|
||||
// wait for millisecond boundary
|
||||
start_ms = GetTickCount() + 5;
|
||||
while (start_ms <= GetTickCount());
|
||||
|
||||
// read timestamp (you could use QueryPerformanceCounter in hires mode if you want)
|
||||
#ifdef COMPILER_MSVC64
|
||||
RDTSC(start_tsc);
|
||||
#else
|
||||
__asm
|
||||
{
|
||||
rdtsc
|
||||
mov dword ptr [start_tsc+0],eax
|
||||
mov dword ptr [start_tsc+4],edx
|
||||
}
|
||||
#endif
|
||||
|
||||
// wait for end
|
||||
stop_ms = start_ms + 1000; // longer wait gives better resolution
|
||||
while (stop_ms > GetTickCount());
|
||||
|
||||
// read timestamp (you could use QueryPerformanceCounter in hires mode if you want)
|
||||
#ifdef COMPILER_MSVC64
|
||||
RDTSC(stop_tsc);
|
||||
#else
|
||||
__asm
|
||||
{
|
||||
rdtsc
|
||||
mov dword ptr [stop_tsc+0],eax
|
||||
mov dword ptr [stop_tsc+4],edx
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// normalize priority
|
||||
pme->SetProcessPriority(ProcessPriorityNormal);
|
||||
|
||||
// return clock speed
|
||||
// optionally here you could round to known clocks, like speeds that are multimples
|
||||
// of 100, 133, 166, etc.
|
||||
m_CPUClockSpeed = ((stop_tsc - start_tsc) * 1000.0) / (double)(stop_ms - start_ms);
|
||||
return m_CPUClockSpeed;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const unsigned short cccr_escr_map[NCOUNTERS][8] =
|
||||
{
|
||||
{
|
||||
0x3B2,
|
||||
0x3B4,
|
||||
0x3AA,
|
||||
0x3B6,
|
||||
0x3AC,
|
||||
0x3C8,
|
||||
0x3A2,
|
||||
0x3A0,
|
||||
},
|
||||
{
|
||||
0x3B2,
|
||||
0x3B4,
|
||||
0x3AA,
|
||||
0x3B6,
|
||||
0x3AC,
|
||||
0x3C8,
|
||||
0x3A2,
|
||||
0x3A0,
|
||||
},
|
||||
{
|
||||
0x3B3,
|
||||
0x3B5,
|
||||
0x3AB,
|
||||
0x3B7,
|
||||
0x3AD,
|
||||
0x3C9,
|
||||
0x3A3,
|
||||
0x3A1,
|
||||
},
|
||||
{
|
||||
0x3B3,
|
||||
0x3B5,
|
||||
0x3AB,
|
||||
0x3B7,
|
||||
0x3AD,
|
||||
0x3C9,
|
||||
0x3A3,
|
||||
0x3A1,
|
||||
},
|
||||
{
|
||||
|
||||
0x3C0,
|
||||
0x3C4,
|
||||
0x3C2,
|
||||
},
|
||||
{
|
||||
0x3C0,
|
||||
0x3C4,
|
||||
0x3C2,
|
||||
},
|
||||
{
|
||||
0x3C1,
|
||||
0x3C5,
|
||||
0x3C3,
|
||||
},
|
||||
{
|
||||
0x3C1,
|
||||
0x3C5,
|
||||
0x3C3,
|
||||
},
|
||||
{
|
||||
0x3A6,
|
||||
0x3A4,
|
||||
0x3AE,
|
||||
0x3B0,
|
||||
0,
|
||||
0x3A8,
|
||||
},
|
||||
{
|
||||
0x3A6,
|
||||
0x3A4,
|
||||
0x3AE,
|
||||
0x3B0,
|
||||
0,
|
||||
0x3A8,
|
||||
},
|
||||
{
|
||||
|
||||
0x3A7,
|
||||
0x3A5,
|
||||
0x3AF,
|
||||
0x3B1,
|
||||
0,
|
||||
0x3A9,
|
||||
},
|
||||
{
|
||||
|
||||
0x3A7,
|
||||
0x3A5,
|
||||
0x3AF,
|
||||
0x3B1,
|
||||
0,
|
||||
0x3A9,
|
||||
},
|
||||
{
|
||||
|
||||
0x3BA,
|
||||
0x3CA,
|
||||
0x3BC,
|
||||
0x3BE,
|
||||
0x3B8,
|
||||
0x3CC,
|
||||
0x3E0,
|
||||
},
|
||||
{
|
||||
|
||||
0x3BA,
|
||||
0x3CA,
|
||||
0x3BC,
|
||||
0x3BE,
|
||||
0x3B8,
|
||||
0x3CC,
|
||||
0x3E0,
|
||||
},
|
||||
{
|
||||
|
||||
0x3BB,
|
||||
0x3CB,
|
||||
0x3BD,
|
||||
0,
|
||||
0x3B9,
|
||||
0x3CD,
|
||||
0x3E1,
|
||||
},
|
||||
{
|
||||
|
||||
|
||||
0x3BB,
|
||||
0x3CB,
|
||||
0x3BD,
|
||||
0,
|
||||
0x3B9,
|
||||
0x3CD,
|
||||
0x3E1,
|
||||
},
|
||||
{
|
||||
0x3BA,
|
||||
0x3CA,
|
||||
0x3BC,
|
||||
0x3BE,
|
||||
0x3B8,
|
||||
0x3CC,
|
||||
0x3E0,
|
||||
},
|
||||
{
|
||||
|
||||
0x3BB,
|
||||
0x3CB,
|
||||
0x3BD,
|
||||
0,
|
||||
0x3B9,
|
||||
0x3CD,
|
||||
0x3E1,
|
||||
},
|
||||
};
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Ensure that all of our internal structures are consistent, and
|
||||
// account for all memory that we've allocated.
|
||||
// Input: validator - Our global validator object
|
||||
// pchName - Our name (typically a member var in our container)
|
||||
//-----------------------------------------------------------------------------
|
||||
void PME::Validate( CValidator &validator, tchar *pchName )
|
||||
{
|
||||
validator.Push( _T("PME"), this, pchName );
|
||||
|
||||
validator.ClaimMemory( this );
|
||||
|
||||
validator.ClaimMemory( cache );
|
||||
|
||||
validator.ClaimMemory( ( void * ) vendor_name.c_str( ) );
|
||||
validator.ClaimMemory( ( void * ) brand.c_str( ) );
|
||||
|
||||
validator.Pop( );
|
||||
}
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
#pragma warning( default : 4530 ) // warning: exception handler -GX option
|
||||
#endif
|
||||
|
||||
244
tier0/_vpc_/manifest_tier0/win32/manifest.txt
Normal file
244
tier0/_vpc_/manifest_tier0/win32/manifest.txt
Normal file
@@ -0,0 +1,244 @@
|
||||
// ----------------------------------------- //
|
||||
// File generated by VPC //
|
||||
// ----------------------------------------- //
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\assert_dialog.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\assert_dialog.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\assert_dialog.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\commandline.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\commandline.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\commandline.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\cpu.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\cpu.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\cpu.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\cpumonitoring.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\cpumonitoring.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\cpumonitoring.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\cputopology.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\cputopology.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\cputopology.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\dbg.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\dbg.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\dbg.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\debug_dll_check.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\dynfunction.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\dynfunction.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\dynfunction.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\etwprof.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\etwprof.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\etwprof.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\fasttimer.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\fasttimer.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\fasttimer.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\logging.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\logging.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\logging.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\mem.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\mem.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\mem.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\mem_helpers.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\mem_helpers.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\mem_helpers.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\memdbg.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\memdbg.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\memdbg.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\memprocessheap.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\memprocessheap.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\memprocessheap.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\memstd.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\memstd.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\memstd.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\memvalidate.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\memvalidate.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\memvalidate.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\memvirt.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\memvirt.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\memvirt.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\minidump.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\minidump.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\minidump.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\miniprofiler.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\miniprofiler.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\miniprofiler.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\perfstats.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\perfstats.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\perfstats.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\platform.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\platform.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\platform.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\platform_independent.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\platform_independent.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\platform_independent.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\platwindow.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\platwindow.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\platwindow.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\pme.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\pme.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\pme.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\PMELib.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\PMELib.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\PMELib.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\progressbar.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\progressbar.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\progressbar.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\security.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\security.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\security.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\stackstats.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\stackstats.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\stackstats.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\stacktools.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\stacktools.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\stacktools.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\systeminformation.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\systeminformation.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\systeminformation.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\threadtools.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\threadtools.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\threadtools.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\tier0_strtools.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\tier0_strtools.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\tier0_strtools.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\tslist.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\tslist.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\tslist.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\unitlib\unitlib.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\unitlib\unitlib.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\unitlib\unitlib.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\vatoms.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\vatoms.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\vatoms.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\vprof.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\vprof.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\vprof.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\vtuneinterface.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\vtuneinterface.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\vtuneinterface.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\win32consoleio.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\win32consoleio.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\win32consoleio.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\tier0\dlmalloc\malloc.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\tier0\dlmalloc\malloc.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\tier0\dlmalloc\malloc.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
701
tier0/assert_dialog.cpp
Normal file
701
tier0/assert_dialog.cpp
Normal file
@@ -0,0 +1,701 @@
|
||||
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
#include "tier0/valve_off.h"
|
||||
#ifdef _X360
|
||||
#include "xbox/xbox_console.h"
|
||||
#include "xbox/xbox_vxconsole.h"
|
||||
#elif defined( _PS3 )
|
||||
#include "ps3/ps3_console.h"
|
||||
#elif defined( _WIN32 )
|
||||
#include <windows.h>
|
||||
#elif POSIX
|
||||
char *GetCommandLine();
|
||||
#endif
|
||||
#include "resource.h"
|
||||
#include "tier0/valve_on.h"
|
||||
#include "tier0/threadtools.h"
|
||||
#include "tier0/icommandline.h"
|
||||
|
||||
#if defined( LINUX ) || defined( OSX )
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#if defined( LINUX ) || defined( USE_SDL )
|
||||
// We lazily load the SDL shared object, and only reference functions if it's
|
||||
// available, so this can be included on the dedicated server too.
|
||||
#include "SDL.h"
|
||||
|
||||
typedef int ( SDLCALL FUNC_SDL_ShowMessageBox )( const SDL_MessageBoxData *messageboxdata, int *buttonid );
|
||||
typedef SDL_Window* ( SDLCALL FUNC_SDL_GetKeyboardFocus )();
|
||||
#endif
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
class CDialogInitInfo
|
||||
{
|
||||
public:
|
||||
const tchar *m_pFilename;
|
||||
int m_iLine;
|
||||
const tchar *m_pExpression;
|
||||
};
|
||||
|
||||
|
||||
class CAssertDisable
|
||||
{
|
||||
public:
|
||||
tchar m_Filename[512];
|
||||
|
||||
// If these are not -1, then this CAssertDisable only disables asserts on lines between
|
||||
// these values (inclusive).
|
||||
int m_LineMin;
|
||||
int m_LineMax;
|
||||
|
||||
// Decremented each time we hit this assert and ignore it, until it's 0.
|
||||
// Then the CAssertDisable is removed.
|
||||
// If this is -1, then we always ignore this assert.
|
||||
int m_nIgnoreTimes;
|
||||
|
||||
CAssertDisable *m_pNext;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
static HINSTANCE g_hTier0Instance = 0;
|
||||
#endif
|
||||
|
||||
static bool g_bAssertsEnabled = true;
|
||||
static bool g_bAssertDialogEnabled = true;
|
||||
|
||||
static CAssertDisable *g_pAssertDisables = NULL;
|
||||
|
||||
#if ( defined( _WIN32 ) && !defined( _X360 ) )
|
||||
static int g_iLastLineRange = 5;
|
||||
static int g_nLastIgnoreNumTimes = 1;
|
||||
#endif
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
static int g_VXConsoleAssertReturnValue = -1;
|
||||
#endif
|
||||
|
||||
// Set to true if they want to break in the debugger.
|
||||
static bool g_bBreak = false;
|
||||
|
||||
static CDialogInitInfo g_Info;
|
||||
|
||||
static bool g_bDisableAsserts = false;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// Internal functions.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
#if defined(_WIN32) && !defined(STATIC_TIER0)
|
||||
BOOL WINAPI DllMain(
|
||||
HINSTANCE hinstDLL, // handle to the DLL module
|
||||
DWORD fdwReason, // reason for calling function
|
||||
LPVOID lpvReserved // reserved
|
||||
)
|
||||
{
|
||||
g_hTier0Instance = hinstDLL;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool IsDebugBreakEnabled()
|
||||
{
|
||||
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-debugbreak") ) != NULL );
|
||||
return bResult;
|
||||
}
|
||||
|
||||
static bool AssertStack()
|
||||
{
|
||||
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-assertstack") ) != NULL );
|
||||
return bResult;
|
||||
}
|
||||
|
||||
static bool AreAssertsDisabled()
|
||||
{
|
||||
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-noassert") ) != NULL );
|
||||
return bResult || g_bDisableAsserts;
|
||||
}
|
||||
|
||||
static bool AllAssertOnce()
|
||||
{
|
||||
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-assertonce") ) != NULL );
|
||||
return bResult;
|
||||
}
|
||||
|
||||
static bool AreAssertsEnabledInFileLine( const tchar *pFilename, int iLine )
|
||||
{
|
||||
CAssertDisable **pPrev = &g_pAssertDisables;
|
||||
CAssertDisable *pNext;
|
||||
for ( CAssertDisable *pCur=g_pAssertDisables; pCur; pCur=pNext )
|
||||
{
|
||||
pNext = pCur->m_pNext;
|
||||
|
||||
if ( _tcsicmp( pFilename, pCur->m_Filename ) == 0 )
|
||||
{
|
||||
// Are asserts disabled in the whole file?
|
||||
bool bAssertsEnabled = true;
|
||||
if ( pCur->m_LineMin == -1 && pCur->m_LineMax == -1 )
|
||||
bAssertsEnabled = false;
|
||||
|
||||
// Are asserts disabled on the specified line?
|
||||
if ( iLine >= pCur->m_LineMin && iLine <= pCur->m_LineMax )
|
||||
bAssertsEnabled = false;
|
||||
|
||||
if ( !bAssertsEnabled )
|
||||
{
|
||||
// If this assert is only disabled for the next N times, then countdown..
|
||||
if ( pCur->m_nIgnoreTimes > 0 )
|
||||
{
|
||||
--pCur->m_nIgnoreTimes;
|
||||
if ( pCur->m_nIgnoreTimes == 0 )
|
||||
{
|
||||
// Remove this one from the list.
|
||||
*pPrev = pNext;
|
||||
delete pCur;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pPrev = &pCur->m_pNext;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CAssertDisable* CreateNewAssertDisable( const tchar *pFilename )
|
||||
{
|
||||
CAssertDisable *pDisable = new CAssertDisable;
|
||||
pDisable->m_pNext = g_pAssertDisables;
|
||||
g_pAssertDisables = pDisable;
|
||||
|
||||
pDisable->m_LineMin = pDisable->m_LineMax = -1;
|
||||
pDisable->m_nIgnoreTimes = -1;
|
||||
|
||||
_tcsncpy( pDisable->m_Filename, g_Info.m_pFilename, sizeof( pDisable->m_Filename ) - 1 );
|
||||
pDisable->m_Filename[ sizeof( pDisable->m_Filename ) - 1 ] = 0;
|
||||
|
||||
return pDisable;
|
||||
}
|
||||
|
||||
|
||||
void IgnoreAssertsInCurrentFile()
|
||||
{
|
||||
CreateNewAssertDisable( g_Info.m_pFilename );
|
||||
}
|
||||
|
||||
|
||||
CAssertDisable* IgnoreAssertsNearby( int nRange )
|
||||
{
|
||||
CAssertDisable *pDisable = CreateNewAssertDisable( g_Info.m_pFilename );
|
||||
pDisable->m_LineMin = g_Info.m_iLine - nRange;
|
||||
pDisable->m_LineMax = g_Info.m_iLine - nRange;
|
||||
return pDisable;
|
||||
}
|
||||
|
||||
|
||||
#if ( defined( _WIN32 ) && !defined( _X360 ) )
|
||||
INT_PTR CALLBACK AssertDialogProc(
|
||||
HWND hDlg, // handle to dialog box
|
||||
UINT uMsg, // message
|
||||
WPARAM wParam, // first message parameter
|
||||
LPARAM lParam // second message parameter
|
||||
)
|
||||
{
|
||||
switch( uMsg )
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
#ifdef TCHAR_IS_WCHAR
|
||||
SetDlgItemTextW( hDlg, IDC_ASSERT_MSG_CTRL, g_Info.m_pExpression );
|
||||
SetDlgItemTextW( hDlg, IDC_FILENAME_CONTROL, g_Info.m_pFilename );
|
||||
#else
|
||||
SetDlgItemText( hDlg, IDC_ASSERT_MSG_CTRL, g_Info.m_pExpression );
|
||||
SetDlgItemText( hDlg, IDC_FILENAME_CONTROL, g_Info.m_pFilename );
|
||||
#endif
|
||||
SetDlgItemInt( hDlg, IDC_LINE_CONTROL, g_Info.m_iLine, false );
|
||||
SetDlgItemInt( hDlg, IDC_IGNORE_NUMLINES, g_iLastLineRange, false );
|
||||
SetDlgItemInt( hDlg, IDC_IGNORE_NUMTIMES, g_nLastIgnoreNumTimes, false );
|
||||
|
||||
// Center the dialog.
|
||||
RECT rcDlg, rcDesktop;
|
||||
GetWindowRect( hDlg, &rcDlg );
|
||||
GetWindowRect( GetDesktopWindow(), &rcDesktop );
|
||||
SetWindowPos(
|
||||
hDlg,
|
||||
HWND_TOP,
|
||||
((rcDesktop.right-rcDesktop.left) - (rcDlg.right-rcDlg.left)) / 2,
|
||||
((rcDesktop.bottom-rcDesktop.top) - (rcDlg.bottom-rcDlg.top)) / 2,
|
||||
0,
|
||||
0,
|
||||
SWP_NOSIZE );
|
||||
}
|
||||
return true;
|
||||
|
||||
case WM_COMMAND:
|
||||
{
|
||||
switch( LOWORD( wParam ) )
|
||||
{
|
||||
case IDC_IGNORE_FILE:
|
||||
{
|
||||
IgnoreAssertsInCurrentFile();
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ignore this assert N times.
|
||||
case IDC_IGNORE_THIS:
|
||||
{
|
||||
BOOL bTranslated = false;
|
||||
UINT value = GetDlgItemInt( hDlg, IDC_IGNORE_NUMTIMES, &bTranslated, false );
|
||||
if ( bTranslated && value > 1 )
|
||||
{
|
||||
CAssertDisable *pDisable = IgnoreAssertsNearby( 0 );
|
||||
pDisable->m_nIgnoreTimes = value - 1;
|
||||
g_nLastIgnoreNumTimes = value;
|
||||
}
|
||||
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Always ignore this assert.
|
||||
case IDC_IGNORE_ALWAYS:
|
||||
{
|
||||
IgnoreAssertsNearby( 0 );
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
case IDC_IGNORE_NEARBY:
|
||||
{
|
||||
BOOL bTranslated = false;
|
||||
UINT value = GetDlgItemInt( hDlg, IDC_IGNORE_NUMLINES, &bTranslated, false );
|
||||
if ( !bTranslated || value < 1 )
|
||||
return true;
|
||||
|
||||
IgnoreAssertsNearby( value );
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
case IDC_IGNORE_ALL:
|
||||
{
|
||||
g_bAssertsEnabled = false;
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
case IDC_BREAK:
|
||||
{
|
||||
g_bBreak = true;
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
case WM_KEYDOWN:
|
||||
{
|
||||
// Escape?
|
||||
if ( wParam == 2 )
|
||||
{
|
||||
// Ignore this assert.
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static HWND g_hBestParentWindow;
|
||||
|
||||
|
||||
static BOOL CALLBACK ParentWindowEnumProc(
|
||||
HWND hWnd, // handle to parent window
|
||||
LPARAM lParam // application-defined value
|
||||
)
|
||||
{
|
||||
if ( IsWindowVisible( hWnd ) )
|
||||
{
|
||||
DWORD procID;
|
||||
GetWindowThreadProcessId( hWnd, &procID );
|
||||
if ( procID == (DWORD)lParam )
|
||||
{
|
||||
g_hBestParentWindow = hWnd;
|
||||
return FALSE; // don't iterate any more.
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static HWND FindLikelyParentWindow()
|
||||
{
|
||||
// Enumerate top-level windows and take the first visible one with our processID.
|
||||
g_hBestParentWindow = NULL;
|
||||
EnumWindows( ParentWindowEnumProc, GetCurrentProcessId() );
|
||||
return g_hBestParentWindow;
|
||||
}
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// Interface functions.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
// provides access to the global that turns asserts on and off
|
||||
PLATFORM_INTERFACE bool AreAllAssertsDisabled()
|
||||
{
|
||||
return !g_bAssertsEnabled;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void SetAllAssertsDisabled( bool bAssertsDisabled )
|
||||
{
|
||||
g_bAssertsEnabled = !bAssertsDisabled;
|
||||
}
|
||||
|
||||
|
||||
// provides access to the global that turns asserts on and off
|
||||
PLATFORM_INTERFACE bool IsAssertDialogDisabled()
|
||||
{
|
||||
return !g_bAssertDialogEnabled;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void SetAssertDialogDisabled( bool bAssertDialogDisabled )
|
||||
{
|
||||
g_bAssertDialogEnabled = !bAssertDialogDisabled;
|
||||
}
|
||||
|
||||
#if defined( LINUX ) || ( defined( USE_SDL ) && defined( OSX ) )
|
||||
SDL_Window *g_SDLWindow = NULL;
|
||||
|
||||
PLATFORM_INTERFACE void SetAssertDialogParent( struct SDL_Window *window )
|
||||
{
|
||||
g_SDLWindow = window;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE struct SDL_Window * GetAssertDialogParent()
|
||||
{
|
||||
return g_SDLWindow;
|
||||
}
|
||||
#endif
|
||||
|
||||
PLATFORM_INTERFACE bool ShouldUseNewAssertDialog()
|
||||
{
|
||||
static bool bMPIWorker = ( _tcsstr( Plat_GetCommandLine(), _T("-mpi_worker") ) != NULL );
|
||||
if ( bMPIWorker )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DBGFLAG_ASSERTDLG
|
||||
return true; // always show an assert dialog
|
||||
#else
|
||||
return Plat_IsInDebugSession(); // only show an assert dialog if the process is being debugged
|
||||
#endif // DBGFLAG_ASSERTDLG
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFilename, int line, const tchar *pExpression )
|
||||
{
|
||||
LOCAL_THREAD_LOCK();
|
||||
|
||||
if ( AreAssertsDisabled() )
|
||||
return false;
|
||||
|
||||
// If they have the old mode enabled (always break immediately), then just break right into
|
||||
// the debugger like we used to do.
|
||||
if ( IsDebugBreakEnabled() )
|
||||
return true;
|
||||
|
||||
// Have ALL Asserts been disabled?
|
||||
if ( !g_bAssertsEnabled )
|
||||
return false;
|
||||
|
||||
// Has this specific Assert been disabled?
|
||||
if ( !AreAssertsEnabledInFileLine( pFilename, line ) )
|
||||
return false;
|
||||
|
||||
// Now create the dialog.
|
||||
g_Info.m_pFilename = pFilename;
|
||||
g_Info.m_iLine = line;
|
||||
g_Info.m_pExpression = pExpression;
|
||||
|
||||
if ( AssertStack() )
|
||||
{
|
||||
IgnoreAssertsNearby( 0 );
|
||||
// @TODO: add-back callstack spew support
|
||||
Warning( "%s (%d) : Assertion callstack...(NOT IMPLEMENTED IN NEW LOGGING SYSTEM.)\n", pFilename, line );
|
||||
// Warning_SpewCallStack( 10, "%s (%d) : Assertion callstack...\n", pFilename, line );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( AllAssertOnce() )
|
||||
{
|
||||
IgnoreAssertsNearby( 0 );
|
||||
}
|
||||
|
||||
g_bBreak = false;
|
||||
|
||||
#if defined( _X360 )
|
||||
|
||||
char cmdString[XBX_MAX_RCMDLENGTH];
|
||||
|
||||
// Before calling VXConsole, init the global variable that receives the result
|
||||
g_VXConsoleAssertReturnValue = -1;
|
||||
|
||||
// Message VXConsole to pop up a PC-side Assert dialog
|
||||
_snprintf( cmdString, sizeof(cmdString), "Assert() 0x%.8x File: %s\tLine: %d\t%s",
|
||||
&g_VXConsoleAssertReturnValue, pFilename, line, pExpression );
|
||||
XBX_SendRemoteCommand( cmdString, false );
|
||||
|
||||
// We sent a synchronous message, so g_xbx_dbgVXConsoleAssertReturnValue should have been overwritten by now
|
||||
if ( g_VXConsoleAssertReturnValue == -1 )
|
||||
{
|
||||
// VXConsole isn't connected/running - default to the old behaviour (break)
|
||||
g_bBreak = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Respond to what the user selected
|
||||
switch( g_VXConsoleAssertReturnValue )
|
||||
{
|
||||
case ASSERT_ACTION_IGNORE_FILE:
|
||||
IgnoreAssertsInCurrentFile();
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_THIS:
|
||||
// Ignore this Assert once
|
||||
break;
|
||||
case ASSERT_ACTION_BREAK:
|
||||
// Break on this Assert
|
||||
g_bBreak = true;
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_ALL:
|
||||
// Ignore all Asserts from now on
|
||||
g_bAssertsEnabled = false;
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_ALWAYS:
|
||||
// Ignore this Assert from now on
|
||||
IgnoreAssertsNearby( 0 );
|
||||
break;
|
||||
case ASSERT_ACTION_OTHER:
|
||||
default:
|
||||
// Error... just break
|
||||
XBX_Error( "DoNewAssertDialog: invalid Assert response returned from VXConsole - breaking to debugger" );
|
||||
g_bBreak = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#elif defined( _PS3 )
|
||||
// There are a few ways to handle this sort of assert behavior with the PS3 / Target Manager API.
|
||||
// One is to use a DebuggerBreak per usual, and then SNProcessContinue in the TMAPI to make
|
||||
// the game resume after a breakpoint. (You can use snIsDebuggerPresent() to determine if
|
||||
// the debugger is attached, although really it doesn't matter here.)
|
||||
// This doesn't work because the DebuggerBreak() is actually an interrupt op, and so Continue()
|
||||
// won't continue past it -- you need to do that from inside the ProDG debugger itself.
|
||||
// Another is to wait on a mutex here and then trip it from the TMAPI, but there isn't
|
||||
// a clean way to trip sync primitives from TMAPI.
|
||||
// Another way is to suspend the thread here and have TMAPI resume it.
|
||||
// The simplest way is to spin-wait on a shared variable that you expect the
|
||||
// TMAPI to poke into memory. I'm trying that.
|
||||
|
||||
char cmdString[XBX_MAX_RCMDLENGTH];
|
||||
|
||||
// Before calling VXConsole, init the global variable that receives the result
|
||||
g_VXConsoleAssertReturnValue = -1;
|
||||
|
||||
// Message VXConsole to pop up a PC-side Assert dialog
|
||||
_snprintf( cmdString, sizeof(cmdString), "Assert() 0x%.8x File: %s\tLine: %d\t%s",
|
||||
&g_VXConsoleAssertReturnValue, pFilename, line, pExpression );
|
||||
XBX_SendRemoteCommand( cmdString, false );
|
||||
|
||||
if ( g_pValvePS3Console->IsConsoleConnected() )
|
||||
{
|
||||
// DebuggerBreak();
|
||||
|
||||
while ( g_VXConsoleAssertReturnValue == -1 )
|
||||
{
|
||||
ThreadSleep( 1000 );
|
||||
}
|
||||
|
||||
// assume that the VX has poked the return value
|
||||
// Respond to what the user selected
|
||||
switch( g_VXConsoleAssertReturnValue )
|
||||
{
|
||||
case ASSERT_ACTION_IGNORE_FILE:
|
||||
IgnoreAssertsInCurrentFile();
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_THIS:
|
||||
// Ignore this Assert once
|
||||
break;
|
||||
case ASSERT_ACTION_BREAK:
|
||||
// Break on this Assert
|
||||
g_bBreak = true;
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_ALL:
|
||||
// Ignore all Asserts from now on
|
||||
g_bAssertsEnabled = false;
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_ALWAYS:
|
||||
// Ignore this Assert from now on
|
||||
IgnoreAssertsNearby( 0 );
|
||||
break;
|
||||
case ASSERT_ACTION_OTHER:
|
||||
default:
|
||||
// nothing.
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( g_pValvePS3Console->IsDebuggerPresent() )
|
||||
{
|
||||
g_bBreak = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ignore the assert
|
||||
}
|
||||
|
||||
#elif defined( _WIN32 )
|
||||
|
||||
if ( !g_hTier0Instance || !ThreadInMainThread() )
|
||||
{
|
||||
int result = MessageBox( NULL, pExpression, "Assertion Failed", MB_SYSTEMMODAL | MB_CANCELTRYCONTINUE );
|
||||
|
||||
if ( result == IDCANCEL )
|
||||
{
|
||||
IgnoreAssertsNearby( 0 );
|
||||
}
|
||||
else if ( result == IDCONTINUE )
|
||||
{
|
||||
g_bBreak = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HWND hParentWindow = FindLikelyParentWindow();
|
||||
|
||||
DialogBox( g_hTier0Instance, MAKEINTRESOURCE( IDD_ASSERT_DIALOG ), hParentWindow, AssertDialogProc );
|
||||
}
|
||||
|
||||
#elif defined( LINUX ) || defined( USE_SDL )
|
||||
|
||||
#define COLOR_YELLOW "\033[1;33m"
|
||||
#define COLOR_GREEN "\033[1;32m"
|
||||
#define COLOR_RED "\033[1;31m"
|
||||
#define COLOR_END "\033[0m"
|
||||
fprintf(stderr, COLOR_YELLOW "ASSERT: " COLOR_RED "%s" COLOR_GREEN ":%i:" COLOR_RED "%s" COLOR_END "\n", pFilename, line, pExpression);
|
||||
|
||||
|
||||
static FUNC_SDL_ShowMessageBox *pfnSDLShowMessageBox = NULL;
|
||||
static FUNC_SDL_GetKeyboardFocus *pfnSDLGetKeyboardFocus = NULL;
|
||||
if( getenv( "GAME_ASSERT_DIALOG" ) && !pfnSDLShowMessageBox )
|
||||
{
|
||||
#if defined( WIN32 )
|
||||
HMODULE ret = LoadLibrary( "SDL2.lib" );
|
||||
|
||||
pfnSDLShowMessageBox = ( FUNC_SDL_ShowMessageBox * )GetProcAddress( ret, "SDL_ShowMessageBox" );
|
||||
pfnSDLGetKeyboardFocus = ( FUNC_SDL_GetKeyboardFocus * )GetProcAddress( ret, "SDL_GetKeyboardFocus" );
|
||||
#else
|
||||
|
||||
#if defined( OSX )
|
||||
void *ret = dlopen( "libSDL2-2.0.0.dylib", RTLD_LAZY );
|
||||
#else
|
||||
void *ret = dlopen( "libSDL2-2.0.so.0", RTLD_LAZY );
|
||||
#endif
|
||||
|
||||
pfnSDLShowMessageBox = ( FUNC_SDL_ShowMessageBox * )dlsym( ret, "SDL_ShowMessageBox" );
|
||||
pfnSDLGetKeyboardFocus = ( FUNC_SDL_GetKeyboardFocus * )dlsym( ret, "SDL_GetKeyboardFocus" );
|
||||
#endif
|
||||
}
|
||||
|
||||
if( pfnSDLShowMessageBox )
|
||||
{
|
||||
int buttonid;
|
||||
char text[ 4096 ];
|
||||
SDL_MessageBoxData messageboxdata = { 0 };
|
||||
const char *DefaultAction = Plat_IsInDebugSession() ? "Break" : "Corefile";
|
||||
SDL_MessageBoxButtonData buttondata[] =
|
||||
{
|
||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, IDC_BREAK, DefaultAction },
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, IDC_IGNORE_THIS, "Ignore" },
|
||||
{ 0, IDC_IGNORE_FILE, "Ignore This File" },
|
||||
{ 0, IDC_IGNORE_ALWAYS, "Always Ignore" },
|
||||
{ 0, IDC_IGNORE_ALL, "Ignore All Asserts" },
|
||||
};
|
||||
|
||||
_snprintf( text, sizeof( text ), "File: %s\nLine: %i\nExpr: %s\n", pFilename, line, pExpression );
|
||||
text[ sizeof( text ) - 1 ] = 0;
|
||||
|
||||
messageboxdata.window = g_SDLWindow;
|
||||
messageboxdata.title = "Assertion Failed";
|
||||
messageboxdata.message = text;
|
||||
messageboxdata.numbuttons = ARRAYSIZE( buttondata );
|
||||
messageboxdata.buttons = buttondata;
|
||||
|
||||
int Ret = ( *pfnSDLShowMessageBox )( &messageboxdata, &buttonid );
|
||||
if( Ret == -1 )
|
||||
{
|
||||
buttonid = IDC_BREAK;
|
||||
}
|
||||
|
||||
switch( buttonid )
|
||||
{
|
||||
default:
|
||||
case IDC_BREAK:
|
||||
// Break on this Assert
|
||||
g_bBreak = true;
|
||||
break;
|
||||
case IDC_IGNORE_THIS:
|
||||
// Ignore this Assert once
|
||||
break;
|
||||
case IDC_IGNORE_FILE:
|
||||
IgnoreAssertsInCurrentFile();
|
||||
break;
|
||||
case IDC_IGNORE_ALWAYS:
|
||||
// Ignore this Assert from now on
|
||||
IgnoreAssertsNearby( 0 );
|
||||
break;
|
||||
case IDC_IGNORE_ALL:
|
||||
// Ignore all Asserts from now on
|
||||
g_bAssertsEnabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( getenv( "RAISE_ON_ASSERT" ) )
|
||||
{
|
||||
g_bBreak = true;
|
||||
}
|
||||
|
||||
#elif defined( POSIX )
|
||||
|
||||
fprintf(stderr, "%s %i %s\n", pFilename, line, pExpression);
|
||||
if ( getenv( "RAISE_ON_ASSERT" ) )
|
||||
{
|
||||
g_bBreak = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
return g_bBreak;
|
||||
}
|
||||
|
||||
124
tier0/assert_dialog.rc
Normal file
124
tier0/assert_dialog.rc
Normal file
@@ -0,0 +1,124 @@
|
||||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_ASSERT_DIALOG DIALOG DISCARDABLE 0, 0, 268, 158
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Assert"
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
LTEXT "File:",IDC_NOID,7,7,23,9
|
||||
LTEXT "c:/hl2/src/blah.cpp",IDC_FILENAME_CONTROL,36,7,217,8
|
||||
LTEXT "Line:",IDC_NOID,7,18,23,9
|
||||
LTEXT "45",IDC_LINE_CONTROL,36,18,217,8
|
||||
LTEXT "Assert:",IDC_NOID,7,29,23,9
|
||||
CONTROL "ASSERT MESSAGE",IDC_ASSERT_MSG_CTRL,"Static",
|
||||
SS_LEFTNOWORDWRAP | SS_NOPREFIX | WS_GROUP,36,29,217,9
|
||||
DEFPUSHBUTTON "&Break in Debugger",IDC_BREAK,7,49,98,14
|
||||
PUSHBUTTON "&Ignore This Assert",IDC_IGNORE_THIS,7,66,98,14
|
||||
EDITTEXT IDC_IGNORE_NUMTIMES,110,66,24,14,ES_AUTOHSCROLL |
|
||||
ES_NUMBER
|
||||
LTEXT "time(s).",IDC_NOID,138,68,23,8
|
||||
PUSHBUTTON "Always Ignore &This Assert",IDC_IGNORE_ALWAYS,7,84,98,
|
||||
14
|
||||
PUSHBUTTON "Ignore &Nearby Asserts",IDC_IGNORE_NEARBY,7,102,98,14
|
||||
LTEXT "within",IDC_NOID,109,105,19,8
|
||||
EDITTEXT IDC_IGNORE_NUMLINES,131,102,40,14,ES_AUTOHSCROLL |
|
||||
ES_NUMBER
|
||||
LTEXT "lines.",IDC_NOID,175,105,17,8
|
||||
PUSHBUTTON "Ignore Asserts in This &File",IDC_IGNORE_FILE,7,120,98,
|
||||
14
|
||||
PUSHBUTTON "Ignore &All Asserts",IDC_IGNORE_ALL,7,137,98,14
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO DISCARDABLE
|
||||
BEGIN
|
||||
IDD_ASSERT_DIALOG, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 261
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 151
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// ETW event manifest data from ValveETWProviders.man
|
||||
//
|
||||
|
||||
#include "ValveETWProviderEvents.rc"
|
||||
676
tier0/commandline.cpp
Normal file
676
tier0/commandline.cpp
Normal file
@@ -0,0 +1,676 @@
|
||||
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/icommandline.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0_strtools.h"
|
||||
#include "tier1/strtools.h" // this is included for the definition of V_isspace()
|
||||
|
||||
#ifdef PLATFORM_POSIX
|
||||
#include <limits.h>
|
||||
#define _MAX_PATH PATH_MAX
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
static const int MAX_PARAMETER_LEN = 128;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Implements ICommandLine
|
||||
//-----------------------------------------------------------------------------
|
||||
class CCommandLine : public ICommandLine
|
||||
{
|
||||
public:
|
||||
// Construction
|
||||
CCommandLine( void );
|
||||
virtual ~CCommandLine( void );
|
||||
|
||||
// Implements ICommandLine
|
||||
virtual void CreateCmdLine( const char *commandline );
|
||||
virtual void CreateCmdLine( int argc, char **argv );
|
||||
virtual const char *GetCmdLine( void ) const;
|
||||
virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const;
|
||||
// A bool return of whether param exists, useful for just checking if param that is just a flag is set
|
||||
virtual bool HasParm( const char *psz ) const;
|
||||
|
||||
virtual void RemoveParm( const char *parm );
|
||||
virtual void AppendParm( const char *pszParm, const char *pszValues );
|
||||
|
||||
virtual int ParmCount() const;
|
||||
virtual int FindParm( const char *psz ) const;
|
||||
virtual const char* GetParm( int nIndex ) const;
|
||||
|
||||
virtual const char *ParmValue( const char *psz, const char *pDefaultVal = NULL ) const;
|
||||
virtual int ParmValue( const char *psz, int nDefaultVal ) const;
|
||||
virtual float ParmValue( const char *psz, float flDefaultVal ) const;
|
||||
virtual void SetParm( int nIndex, char const *pParm );
|
||||
|
||||
virtual const char **GetParms() const { return (const char**)m_ppParms; }
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
MAX_PARAMETER_LEN = 128,
|
||||
MAX_PARAMETERS = 256,
|
||||
};
|
||||
|
||||
// When the commandline contains @name, it reads the parameters from that file
|
||||
void LoadParametersFromFile( const char *&pSrc, char *&pDst, intp maxDestLen, bool bInQuotes );
|
||||
|
||||
// Parse command line...
|
||||
void ParseCommandLine();
|
||||
|
||||
// Frees the command line arguments
|
||||
void CleanUpParms();
|
||||
|
||||
// Adds an argument..
|
||||
void AddArgument( const char *pFirst, const char *pLast );
|
||||
|
||||
// Copy of actual command line
|
||||
char *m_pszCmdLine;
|
||||
|
||||
// Pointers to each argument...
|
||||
int m_nParmCount;
|
||||
char *m_ppParms[MAX_PARAMETERS];
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Instance singleton and expose interface to rest of code
|
||||
//-----------------------------------------------------------------------------
|
||||
static CCommandLine g_CmdLine;
|
||||
ICommandLine *CommandLine()
|
||||
{
|
||||
return &g_CmdLine;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CCommandLine::CCommandLine( void )
|
||||
{
|
||||
m_pszCmdLine = NULL;
|
||||
m_nParmCount = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CCommandLine::~CCommandLine( void )
|
||||
{
|
||||
CleanUpParms();
|
||||
delete[] m_pszCmdLine;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Read commandline from file instead...
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::LoadParametersFromFile( const char *&pSrc, char *&pDst, intp maxDestLen, bool bInQuotes )
|
||||
{
|
||||
// Suck out the file name
|
||||
char szFileName[ MAX_PATH ];
|
||||
char *pOut;
|
||||
char *pDestStart = pDst;
|
||||
|
||||
if ( maxDestLen < 3 )
|
||||
return;
|
||||
|
||||
// Skip the @ sign
|
||||
pSrc++;
|
||||
|
||||
pOut = szFileName;
|
||||
|
||||
char terminatingChar = ' ';
|
||||
if ( bInQuotes )
|
||||
terminatingChar = '\"';
|
||||
|
||||
while ( *pSrc && *pSrc != terminatingChar )
|
||||
{
|
||||
*pOut++ = *pSrc++;
|
||||
if ( (pOut - szFileName) >= (MAX_PATH-1) )
|
||||
break;
|
||||
}
|
||||
|
||||
*pOut = '\0';
|
||||
|
||||
// Skip the space after the file name
|
||||
if ( *pSrc )
|
||||
pSrc++;
|
||||
|
||||
// Now read in parameters from file
|
||||
FILE *fp = fopen( szFileName, "r" );
|
||||
if ( fp )
|
||||
{
|
||||
char c;
|
||||
c = (char)fgetc( fp );
|
||||
while ( c != EOF )
|
||||
{
|
||||
// Turn return characters into spaces
|
||||
if ( c == '\n' )
|
||||
c = ' ';
|
||||
|
||||
*pDst++ = c;
|
||||
|
||||
// Don't go past the end, and allow for our terminating space character AND a terminating null character.
|
||||
if ( (pDst - pDestStart) >= (maxDestLen-2) )
|
||||
break;
|
||||
|
||||
// Get the next character, if there are more
|
||||
c = (char)fgetc( fp );
|
||||
}
|
||||
|
||||
// Add a terminating space character
|
||||
*pDst++ = ' ';
|
||||
|
||||
fclose( fp );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "Parameter file '%s' not found, skipping...", szFileName );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Creates a command line from the arguments passed in
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::CreateCmdLine( int argc, char **argv )
|
||||
{
|
||||
char cmdline[2048];
|
||||
cmdline[0] = 0;
|
||||
const int MAX_CHARS = sizeof(cmdline) - 1;
|
||||
cmdline[MAX_CHARS] = 0;
|
||||
for ( int i = 0; i < argc; ++i )
|
||||
{
|
||||
strncat( cmdline, "\"", MAX_CHARS );
|
||||
strncat( cmdline, argv[i], MAX_CHARS );
|
||||
strncat( cmdline, "\"", MAX_CHARS );
|
||||
strncat( cmdline, " ", MAX_CHARS );
|
||||
}
|
||||
|
||||
CreateCmdLine( cmdline );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Create a command line from the passed in string
|
||||
// Note that if you pass in a @filename, then the routine will read settings
|
||||
// from a file instead of the command line
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::CreateCmdLine( const char *commandline )
|
||||
{
|
||||
const bool bNoAutoArgs = (Plat_GetEnv("autoargs")) == nullptr;
|
||||
if ( m_pszCmdLine )
|
||||
{
|
||||
delete[] m_pszCmdLine;
|
||||
}
|
||||
|
||||
char szFull[ 4096 ];
|
||||
|
||||
char *pDst = szFull;
|
||||
const char *pSrc = commandline;
|
||||
|
||||
bool bInQuotes = false;
|
||||
const char *pInQuotesStart = 0;
|
||||
while ( *pSrc )
|
||||
{
|
||||
// Is this an unslashed quote?
|
||||
if ( *pSrc == '"' )
|
||||
{
|
||||
if ( pSrc == commandline || ( pSrc[-1] != '/' && pSrc[-1] != '\\' ) )
|
||||
{
|
||||
bInQuotes = !bInQuotes;
|
||||
pInQuotesStart = pSrc + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !bNoAutoArgs && *pSrc == '@' )
|
||||
{
|
||||
if ( pSrc == commandline || (!bInQuotes && V_isspace( pSrc[-1] )) || (bInQuotes && pSrc == pInQuotesStart) )
|
||||
{
|
||||
LoadParametersFromFile( pSrc, pDst, sizeof( szFull ) - (pDst - szFull), bInQuotes );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't go past the end.
|
||||
if ( (pDst - szFull) >= (sizeof( szFull ) - 1) )
|
||||
break;
|
||||
|
||||
*pDst++ = *pSrc++;
|
||||
}
|
||||
|
||||
*pDst = '\0';
|
||||
|
||||
size_t len = strlen( szFull ) + 1;
|
||||
m_pszCmdLine = new char[len];
|
||||
memcpy( m_pszCmdLine, szFull, len );
|
||||
|
||||
#if defined( POSIX )
|
||||
Plat_SetCommandLine( m_pszCmdLine );
|
||||
#endif
|
||||
|
||||
ParseCommandLine();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finds a string in another string with a case insensitive test
|
||||
//-----------------------------------------------------------------------------
|
||||
static char * _stristr( char * pStr, const char * pSearch )
|
||||
{
|
||||
AssertValidStringPtr(pStr);
|
||||
AssertValidStringPtr(pSearch);
|
||||
|
||||
if (!pStr || !pSearch)
|
||||
return 0;
|
||||
|
||||
char* pLetter = pStr;
|
||||
|
||||
// Check the entire string
|
||||
while (*pLetter != 0)
|
||||
{
|
||||
// Skip over non-matches
|
||||
if (tolower((unsigned char)*pLetter) == tolower((unsigned char)*pSearch))
|
||||
{
|
||||
// Check for match
|
||||
char const* pMatch = pLetter + 1;
|
||||
char const* pTest = pSearch + 1;
|
||||
while (*pTest != 0)
|
||||
{
|
||||
// We've run off the end; don't bother.
|
||||
if (*pMatch == 0)
|
||||
return 0;
|
||||
|
||||
if (tolower((unsigned char)*pMatch) != tolower((unsigned char)*pTest))
|
||||
break;
|
||||
|
||||
++pMatch;
|
||||
++pTest;
|
||||
}
|
||||
|
||||
// Found a match!
|
||||
if (*pTest == 0)
|
||||
return pLetter;
|
||||
}
|
||||
|
||||
++pLetter;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Remove specified string ( and any args attached to it ) from command line
|
||||
// Input : *pszParm -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::RemoveParm( const char *pszParm )
|
||||
{
|
||||
if ( !m_pszCmdLine )
|
||||
return;
|
||||
|
||||
// Search for first occurrence of pszParm
|
||||
char *p, *found;
|
||||
char *pnextparam;
|
||||
intp n;
|
||||
size_t curlen;
|
||||
|
||||
p = m_pszCmdLine;
|
||||
while ( *p )
|
||||
{
|
||||
curlen = strlen( p );
|
||||
|
||||
found = _stristr( p, pszParm );
|
||||
if ( !found )
|
||||
break;
|
||||
|
||||
pnextparam = found + 1;
|
||||
bool bHadQuote = false;
|
||||
if ( found > m_pszCmdLine && found[-1] == '\"' )
|
||||
bHadQuote = true;
|
||||
|
||||
while ( pnextparam && *pnextparam && (*pnextparam != ' ') && (*pnextparam != '\"') )
|
||||
pnextparam++;
|
||||
|
||||
if ( pnextparam && ( static_cast<size_t>( pnextparam - found ) > strlen( pszParm ) ) )
|
||||
{
|
||||
p = pnextparam;
|
||||
continue;
|
||||
}
|
||||
|
||||
while ( pnextparam && *pnextparam && (*pnextparam != '-') && (*pnextparam != '+') )
|
||||
pnextparam++;
|
||||
|
||||
if ( bHadQuote )
|
||||
{
|
||||
found--;
|
||||
}
|
||||
|
||||
if ( pnextparam && *pnextparam )
|
||||
{
|
||||
// We are either at the end of the string, or at the next param. Just chop out the current param.
|
||||
n = curlen - ( pnextparam - p ); // # of characters after this param.
|
||||
memmove( found, pnextparam, n );
|
||||
|
||||
found[n] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear out rest of string.
|
||||
n = pnextparam - found;
|
||||
memset( found, 0, n );
|
||||
}
|
||||
}
|
||||
|
||||
// Strip and trailing ' ' characters left over.
|
||||
while ( 1 )
|
||||
{
|
||||
intp len = strlen( m_pszCmdLine );
|
||||
if ( len == 0 || m_pszCmdLine[ len - 1 ] != ' ' )
|
||||
break;
|
||||
|
||||
m_pszCmdLine[len - 1] = '\0';
|
||||
}
|
||||
|
||||
ParseCommandLine();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Append parameter and argument values to command line
|
||||
// Input : *pszParm -
|
||||
// *pszValues -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::AppendParm( const char *pszParm, const char *pszValues )
|
||||
{
|
||||
intp nNewLength = 0;
|
||||
char *pCmdString;
|
||||
|
||||
nNewLength = strlen( pszParm ); // Parameter.
|
||||
if ( pszValues )
|
||||
nNewLength += strlen( pszValues ) + 1; // Values + leading space character.
|
||||
nNewLength++; // Terminal 0;
|
||||
|
||||
if ( !m_pszCmdLine )
|
||||
{
|
||||
m_pszCmdLine = new char[ nNewLength ];
|
||||
strcpy( m_pszCmdLine, pszParm );
|
||||
if ( pszValues )
|
||||
{
|
||||
strcat( m_pszCmdLine, " " );
|
||||
strcat( m_pszCmdLine, pszValues );
|
||||
}
|
||||
|
||||
ParseCommandLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any remnants from the current Cmd Line.
|
||||
RemoveParm( pszParm );
|
||||
|
||||
nNewLength += strlen( m_pszCmdLine ) + 1 + 1;
|
||||
|
||||
pCmdString = new char[ nNewLength ];
|
||||
memset( pCmdString, 0, nNewLength );
|
||||
|
||||
strcpy ( pCmdString, m_pszCmdLine ); // Copy old command line.
|
||||
strcat ( pCmdString, " " ); // Put in a space
|
||||
strcat ( pCmdString, pszParm );
|
||||
if ( pszValues )
|
||||
{
|
||||
strcat( pCmdString, " " );
|
||||
strcat( pCmdString, pszValues );
|
||||
}
|
||||
|
||||
// Kill off the old one
|
||||
delete[] m_pszCmdLine;
|
||||
|
||||
// Point at the new command line.
|
||||
m_pszCmdLine = pCmdString;
|
||||
|
||||
ParseCommandLine();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return current command line
|
||||
// Output : const char
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *CCommandLine::GetCmdLine( void ) const
|
||||
{
|
||||
return m_pszCmdLine;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Search for the parameter in the current commandline
|
||||
// Input : *psz -
|
||||
// **ppszValue -
|
||||
// Output : char
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *CCommandLine::CheckParm( const char *psz, const char **ppszValue ) const
|
||||
{
|
||||
if ( ppszValue )
|
||||
*ppszValue = NULL;
|
||||
|
||||
int i = FindParm( psz );
|
||||
if ( i == 0 )
|
||||
return NULL;
|
||||
|
||||
if ( ppszValue )
|
||||
{
|
||||
if ( (i+1) >= m_nParmCount )
|
||||
{
|
||||
*ppszValue = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppszValue = m_ppParms[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
return m_ppParms[i];
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Adds an argument..
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::AddArgument( const char *pFirst, const char *pLast )
|
||||
{
|
||||
if ( pLast == pFirst )
|
||||
return;
|
||||
|
||||
if ( m_nParmCount >= MAX_PARAMETERS )
|
||||
Error( "CCommandLine::AddArgument: exceeded %d parameters", MAX_PARAMETERS );
|
||||
|
||||
size_t nLen = ( pLast - pFirst ) + 1;
|
||||
m_ppParms[m_nParmCount] = new char[nLen];
|
||||
memcpy( m_ppParms[m_nParmCount], pFirst, nLen - 1 );
|
||||
m_ppParms[m_nParmCount][nLen - 1] = 0;
|
||||
|
||||
++m_nParmCount;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parse command line...
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::ParseCommandLine()
|
||||
{
|
||||
CleanUpParms();
|
||||
if (!m_pszCmdLine)
|
||||
return;
|
||||
|
||||
const char *pChar = m_pszCmdLine;
|
||||
while ( *pChar && V_isspace(*pChar) )
|
||||
{
|
||||
++pChar;
|
||||
}
|
||||
|
||||
bool bInQuotes = false;
|
||||
const char *pFirstLetter = NULL;
|
||||
for ( ; *pChar; ++pChar )
|
||||
{
|
||||
if ( bInQuotes )
|
||||
{
|
||||
if ( *pChar != '\"' )
|
||||
continue;
|
||||
|
||||
AddArgument( pFirstLetter, pChar );
|
||||
pFirstLetter = NULL;
|
||||
bInQuotes = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Haven't started a word yet...
|
||||
if ( !pFirstLetter )
|
||||
{
|
||||
if ( *pChar == '\"' )
|
||||
{
|
||||
bInQuotes = true;
|
||||
pFirstLetter = pChar + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( V_isspace( *pChar ) )
|
||||
continue;
|
||||
|
||||
pFirstLetter = pChar;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Here, we're in the middle of a word. Look for the end of it.
|
||||
if ( V_isspace( *pChar ) )
|
||||
{
|
||||
AddArgument( pFirstLetter, pChar );
|
||||
pFirstLetter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ( pFirstLetter )
|
||||
{
|
||||
AddArgument( pFirstLetter, pChar );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Individual command line arguments
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::CleanUpParms()
|
||||
{
|
||||
for ( int i = 0; i < m_nParmCount; ++i )
|
||||
{
|
||||
delete [] m_ppParms[i];
|
||||
m_ppParms[i] = NULL;
|
||||
}
|
||||
m_nParmCount = 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns individual command line arguments
|
||||
//-----------------------------------------------------------------------------
|
||||
int CCommandLine::ParmCount() const
|
||||
{
|
||||
return m_nParmCount;
|
||||
}
|
||||
|
||||
int CCommandLine::FindParm( const char *psz ) const
|
||||
{
|
||||
// Start at 1 so as to not search the exe name
|
||||
for ( int i = 1; i < m_nParmCount; ++i )
|
||||
{
|
||||
if ( !V_tier0_stricmp( psz, m_ppParms[i] ) )
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CCommandLine::HasParm( const char *psz ) const
|
||||
{
|
||||
return ( FindParm( psz ) != 0 );
|
||||
}
|
||||
|
||||
const char* CCommandLine::GetParm( int nIndex ) const
|
||||
{
|
||||
Assert( (nIndex >= 0) && (nIndex < m_nParmCount) );
|
||||
if ( (nIndex < 0) || (nIndex >= m_nParmCount) )
|
||||
return "";
|
||||
return m_ppParms[nIndex];
|
||||
}
|
||||
void CCommandLine::SetParm( int nIndex, char const *pParm )
|
||||
{
|
||||
if ( pParm )
|
||||
{
|
||||
Assert( (nIndex >= 0) && (nIndex < m_nParmCount) );
|
||||
if ( (nIndex >= 0) && (nIndex < m_nParmCount) )
|
||||
{
|
||||
if ( m_ppParms[nIndex] )
|
||||
delete[] m_ppParms[nIndex];
|
||||
m_ppParms[nIndex] = strdup( pParm );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns the argument after the one specified, or the default if not found
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *CCommandLine::ParmValue( const char *psz, const char *pDefaultVal ) const
|
||||
{
|
||||
int nIndex = FindParm( psz );
|
||||
if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1))
|
||||
return pDefaultVal;
|
||||
|
||||
// Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-'
|
||||
if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' )
|
||||
return pDefaultVal;
|
||||
|
||||
return m_ppParms[nIndex + 1];
|
||||
}
|
||||
|
||||
int CCommandLine::ParmValue( const char *psz, int nDefaultVal ) const
|
||||
{
|
||||
int nIndex = FindParm( psz );
|
||||
if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1))
|
||||
return nDefaultVal;
|
||||
|
||||
// Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-'
|
||||
if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' )
|
||||
return nDefaultVal;
|
||||
|
||||
return atoi( m_ppParms[nIndex + 1] );
|
||||
}
|
||||
|
||||
float CCommandLine::ParmValue( const char *psz, float flDefaultVal ) const
|
||||
{
|
||||
int nIndex = FindParm( psz );
|
||||
if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1))
|
||||
return flDefaultVal;
|
||||
|
||||
// Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-'
|
||||
if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' )
|
||||
return flDefaultVal;
|
||||
|
||||
return atof( m_ppParms[nIndex + 1] );
|
||||
}
|
||||
925
tier0/cpu.cpp
Normal file
925
tier0/cpu.cpp
Normal file
@@ -0,0 +1,925 @@
|
||||
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(_X360)
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include "cputopology.h"
|
||||
#elif defined( PLATFORM_OSX )
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#ifndef _PS3
|
||||
#include "tier0_strtools.h"
|
||||
#endif
|
||||
|
||||
//#include "tier1/strtools.h" // this is included for the definition of V_isspace()
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
const tchar* GetProcessorVendorId();
|
||||
const tchar* GetProcessorBrand();
|
||||
|
||||
struct CpuIdResult_t
|
||||
{
|
||||
unsigned long eax;
|
||||
unsigned long ebx;
|
||||
unsigned long ecx;
|
||||
unsigned long edx;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
eax = ebx = ecx = edx = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static bool cpuid( unsigned long function, CpuIdResult_t &out )
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#elif defined(GNUC)
|
||||
unsigned long out_eax,out_ebx,out_ecx,out_edx;
|
||||
#ifdef PLATFORM_64BITS
|
||||
asm("mov %%rbx, %%rsi\n\t"
|
||||
"cpuid\n\t"
|
||||
"xchg %%rsi, %%rbx"
|
||||
: "=a" (out_eax),
|
||||
"=S" (out_ebx),
|
||||
"=c" (out_ecx),
|
||||
"=d" (out_edx)
|
||||
: "a" (function)
|
||||
);
|
||||
#else
|
||||
asm("mov %%ebx, %%esi\n\t"
|
||||
"cpuid\n\t"
|
||||
"xchg %%esi, %%ebx"
|
||||
: "=a" (out_eax),
|
||||
"=S" (out_ebx),
|
||||
"=c" (out_ecx),
|
||||
"=d" (out_edx)
|
||||
: "a" (function)
|
||||
);
|
||||
#endif
|
||||
out.eax = out_eax;
|
||||
out.ebx = out_ebx;
|
||||
out.ecx = out_ecx;
|
||||
out.edx = out_edx;
|
||||
|
||||
return true;
|
||||
#elif defined(_WIN64)
|
||||
int pCPUInfo[4];
|
||||
__cpuid( pCPUInfo, (int)function );
|
||||
out.eax = pCPUInfo[0];
|
||||
out.ebx = pCPUInfo[1];
|
||||
out.ecx = pCPUInfo[2];
|
||||
out.edx = pCPUInfo[3];
|
||||
return true;
|
||||
#else
|
||||
bool retval = true;
|
||||
unsigned long out_eax = 0, out_ebx = 0, out_ecx = 0, out_edx = 0;
|
||||
_asm pushad;
|
||||
|
||||
__try
|
||||
{
|
||||
_asm
|
||||
{
|
||||
xor edx, edx // Clue the compiler that EDX & others is about to be used.
|
||||
xor ecx, ecx
|
||||
xor ebx, ebx // <Sergiy> Note: if I don't zero these out, cpuid sometimes won't work, I didn't find out why yet
|
||||
mov eax, function // set up CPUID to return processor version and features
|
||||
// 0 = vendor string, 1 = version info, 2 = cache info
|
||||
cpuid // code bytes = 0fh, 0a2h
|
||||
mov out_eax, eax // features returned in eax
|
||||
mov out_ebx, ebx // features returned in ebx
|
||||
mov out_ecx, ecx // features returned in ecx
|
||||
mov out_edx, edx // features returned in edx
|
||||
}
|
||||
}
|
||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
retval = false;
|
||||
}
|
||||
|
||||
out.eax = out_eax;
|
||||
out.ebx = out_ebx;
|
||||
out.ecx = out_ecx;
|
||||
out.edx = out_edx;
|
||||
|
||||
_asm popad
|
||||
|
||||
return retval;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static bool cpuidex( unsigned long function, unsigned long subfunction, CpuIdResult_t &out )
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#elif defined(GNUC)
|
||||
unsigned long out_eax, out_ebx, out_ecx, out_edx;
|
||||
|
||||
asm( "mov %%ebx, %%esi\n\t"
|
||||
"cpuid\n\t"
|
||||
"xchg %%esi, %%ebx"
|
||||
: "=a" ( out_eax ),
|
||||
"=S" ( out_ebx ),
|
||||
"=c" ( out_ecx ),
|
||||
"=d" ( out_edx )
|
||||
: "a" ( function ),
|
||||
"c" ( subfunction )
|
||||
);
|
||||
|
||||
out.eax = out_eax;
|
||||
out.ebx = out_ebx;
|
||||
out.ecx = out_ecx;
|
||||
out.edx = out_edx;
|
||||
|
||||
return true;
|
||||
#elif defined(_WIN64)
|
||||
int pCPUInfo[ 4 ];
|
||||
__cpuidex( pCPUInfo, ( int )function, ( int )subfunction );
|
||||
out.eax = pCPUInfo[ 0 ];
|
||||
out.ebx = pCPUInfo[ 1 ];
|
||||
out.ecx = pCPUInfo[ 2 ];
|
||||
out.edx = pCPUInfo[ 3 ];
|
||||
return false;
|
||||
#else
|
||||
bool retval = true;
|
||||
unsigned long out_eax = 0, out_ebx = 0, out_ecx = 0, out_edx = 0;
|
||||
_asm pushad;
|
||||
|
||||
__try
|
||||
{
|
||||
_asm
|
||||
{
|
||||
xor edx, edx // Clue the compiler that EDX & others is about to be used.
|
||||
mov ecx, subfunction
|
||||
xor ebx, ebx // <Sergiy> Note: if I don't zero these out, cpuid sometimes won't work, I didn't find out why yet
|
||||
mov eax, function // set up CPUID to return processor version and features
|
||||
// 0 = vendor string, 1 = version info, 2 = cache info
|
||||
cpuid // code bytes = 0fh, 0a2h
|
||||
mov out_eax, eax // features returned in eax
|
||||
mov out_ebx, ebx // features returned in ebx
|
||||
mov out_ecx, ecx // features returned in ecx
|
||||
mov out_edx, edx // features returned in edx
|
||||
}
|
||||
}
|
||||
__except ( EXCEPTION_EXECUTE_HANDLER )
|
||||
{
|
||||
retval = false;
|
||||
}
|
||||
|
||||
out.eax = out_eax;
|
||||
out.ebx = out_ebx;
|
||||
out.ecx = out_ecx;
|
||||
out.edx = out_edx;
|
||||
|
||||
_asm popad
|
||||
|
||||
return retval;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static CpuIdResult_t cpuid( unsigned long function )
|
||||
{
|
||||
CpuIdResult_t out;
|
||||
if ( !cpuid( function, out ) )
|
||||
{
|
||||
out.Reset();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static CpuIdResult_t cpuidex( unsigned long function, unsigned long subfunction )
|
||||
{
|
||||
CpuIdResult_t out;
|
||||
if ( !cpuidex( function, subfunction, out ) )
|
||||
{
|
||||
out.Reset();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: This is a bit of a hack because it appears
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool IsWin98OrOlder()
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 ) || defined( POSIX )
|
||||
return false;
|
||||
#else
|
||||
bool retval = false;
|
||||
|
||||
OSVERSIONINFOEX osvi;
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||
|
||||
BOOL bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi);
|
||||
if( !bOsVersionInfoEx )
|
||||
{
|
||||
// If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
|
||||
|
||||
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
|
||||
if ( !GetVersionEx ( (OSVERSIONINFO *) &osvi) )
|
||||
{
|
||||
Error( _T("IsWin98OrOlder: Unable to get OS version information") );
|
||||
}
|
||||
}
|
||||
|
||||
switch (osvi.dwPlatformId)
|
||||
{
|
||||
case VER_PLATFORM_WIN32_NT:
|
||||
// NT, XP, Win2K, etc. all OK for SSE
|
||||
break;
|
||||
case VER_PLATFORM_WIN32_WINDOWS:
|
||||
// Win95, 98, Me can't do SSE
|
||||
retval = true;
|
||||
break;
|
||||
case VER_PLATFORM_WIN32s:
|
||||
// Can't really run this way I don't think...
|
||||
retval = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static bool CheckSSETechnology(void)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return true;
|
||||
#else
|
||||
if ( IsWin98OrOlder() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ( cpuid( 1 ).edx & 0x2000000L ) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool CheckSSE2Technology(void)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
return ( cpuid( 1 ).edx & 0x04000000 ) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CheckSSE3Technology(void)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
return ( cpuid( 1 ).ecx & 0x00000001 ) != 0; // bit 1 of ECX
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CheckSSSE3Technology(void)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
// SSSE 3 is implemented by both Intel and AMD
|
||||
// detection is done the same way for both vendors
|
||||
return ( cpuid( 1 ).ecx & ( 1 << 9 ) ) != 0; // bit 9 of ECX
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CheckSSE41Technology(void)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
// SSE 4.1 is implemented by both Intel and AMD
|
||||
// detection is done the same way for both vendors
|
||||
|
||||
return ( cpuid( 1 ).ecx & ( 1 << 19 ) ) != 0; // bit 19 of ECX
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CheckSSE42Technology(void)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
// SSE4.2 is an Intel-only feature
|
||||
|
||||
const char *pchVendor = GetProcessorVendorId();
|
||||
if ( 0 != V_tier0_stricmp( pchVendor, "GenuineIntel" ) )
|
||||
return false;
|
||||
|
||||
return ( cpuid( 1 ).ecx & ( 1 << 20 ) ) != 0; // bit 20 of ECX
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool CheckSSE4aTechnology( void )
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
// SSE 4a is an AMD-only feature
|
||||
|
||||
const char *pchVendor = GetProcessorVendorId();
|
||||
if ( 0 != V_tier0_stricmp( pchVendor, "AuthenticAMD" ) )
|
||||
return false;
|
||||
|
||||
return ( cpuid( 1 ).ecx & ( 1 << 6 ) ) != 0; // bit 6 of ECX
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static bool Check3DNowTechnology(void)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
if ( cpuid( 0x80000000 ).eax > 0x80000000L )
|
||||
{
|
||||
return ( cpuid( 0x80000001 ).eax & ( 1 << 31 ) ) != 0;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool CheckCMOVTechnology()
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
return ( cpuid( 1 ).edx & ( 1 << 15 ) ) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool CheckFCMOVTechnology(void)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
return ( cpuid( 1 ).edx & ( 1 << 16 ) ) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool CheckRDTSCTechnology(void)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
return ( cpuid( 1 ).edx & 0x10 ) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static tchar s_CpuVendorID[ 13 ] = "unknown";
|
||||
|
||||
bool s_bCpuVendorIdInitialized = false;
|
||||
|
||||
union CpuBrand_t
|
||||
{
|
||||
CpuIdResult_t cpuid[ 3 ];
|
||||
char name[ 49 ];
|
||||
};
|
||||
CpuBrand_t s_CpuBrand;
|
||||
|
||||
bool s_bCpuBrandInitialized = false;
|
||||
|
||||
// Return the Processor's vendor identification string, or "Generic_x86" if it doesn't exist on this CPU
|
||||
const tchar* GetProcessorVendorId()
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return "PPC";
|
||||
#else
|
||||
if ( s_bCpuVendorIdInitialized )
|
||||
{
|
||||
return s_CpuVendorID;
|
||||
}
|
||||
|
||||
s_bCpuVendorIdInitialized = true;
|
||||
|
||||
CpuIdResult_t cpuid0 = cpuid( 0 );
|
||||
|
||||
memset( s_CpuVendorID, 0, sizeof(s_CpuVendorID) );
|
||||
|
||||
if ( !cpuid0.eax )
|
||||
{
|
||||
// weird...
|
||||
if ( IsPC() )
|
||||
{
|
||||
_tcscpy( s_CpuVendorID, _T( "Generic_x86" ) );
|
||||
}
|
||||
else if ( IsX360() )
|
||||
{
|
||||
_tcscpy( s_CpuVendorID, _T( "PowerPC" ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy( s_CpuVendorID + 0, &( cpuid0.ebx ), sizeof( cpuid0.ebx ) );
|
||||
memcpy( s_CpuVendorID + 4, &( cpuid0.edx ), sizeof( cpuid0.edx ) );
|
||||
memcpy( s_CpuVendorID + 8, &( cpuid0.ecx ), sizeof( cpuid0.ecx ) );
|
||||
}
|
||||
|
||||
return s_CpuVendorID;
|
||||
#endif
|
||||
}
|
||||
|
||||
const tchar* GetProcessorBrand()
|
||||
{
|
||||
#if defined( _X360 )
|
||||
return "Xenon";
|
||||
#elif defined( _PS3 )
|
||||
return "Cell Broadband Engine";
|
||||
#else
|
||||
if ( s_bCpuBrandInitialized )
|
||||
{
|
||||
return s_CpuBrand.name;
|
||||
}
|
||||
s_bCpuBrandInitialized = true;
|
||||
|
||||
memset( &s_CpuBrand, 0, sizeof( s_CpuBrand ) );
|
||||
|
||||
const char *pchVendor = GetProcessorVendorId();
|
||||
if ( 0 == V_tier0_stricmp( pchVendor, "GenuineIntel" ) )
|
||||
{
|
||||
// Intel brand string
|
||||
if ( cpuid( 0x80000000 ).eax >= 0x80000004 )
|
||||
{
|
||||
s_CpuBrand.cpuid[ 0 ] = cpuid( 0x80000002 );
|
||||
s_CpuBrand.cpuid[ 1 ] = cpuid( 0x80000003 );
|
||||
s_CpuBrand.cpuid[ 2 ] = cpuid( 0x80000004 );
|
||||
}
|
||||
}
|
||||
return s_CpuBrand.name;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns non-zero if Hyper-Threading Technology is supported on the processors and zero if not.
|
||||
// If it's supported, it does not mean that it's been enabled. So we test another flag to see if it's enabled
|
||||
// See Intel Processor Identification and the CPUID instruction Application Note 485
|
||||
// http://www.intel.com/Assets/PDF/appnote/241618.pdf
|
||||
static bool HTSupported(void)
|
||||
{
|
||||
#if ( defined( _X360 ) || defined( _PS3 ) )
|
||||
// not entirtely sure about the semantic of HT support, it being an intel name
|
||||
// are we asking about HW threads or HT?
|
||||
return true;
|
||||
#else
|
||||
enum {
|
||||
HT_BIT = 0x10000000, // EDX[28] - Bit 28 set indicates Hyper-Threading Technology is supported in hardware.
|
||||
FAMILY_ID = 0x0f00, // EAX[11:8] - Bit 11 thru 8 contains family processor id
|
||||
EXT_FAMILY_ID = 0x0f00000, // EAX[23:20] - Bit 23 thru 20 contains extended family processor id
|
||||
FAMILY_ID_386 = 0x0300,
|
||||
FAMILY_ID_486 = 0x0400, // EAX[8:12] - 486, 487 and overdrive
|
||||
FAMILY_ID_PENTIUM = 0x0500, // Pentium, Pentium OverDrive 60 - 200
|
||||
FAMILY_ID_PENTIUM_PRO = 0x0600,// P Pro, P II, P III, P M, Celeron M, Core Duo, Core Solo, Core2 Duo, Core2 Extreme, P D, Xeon model F,
|
||||
// also 45-nm : Intel Atom, Core i7, Xeon MP ; see Intel Processor Identification and the CPUID instruction pg 20,21
|
||||
|
||||
FAMILY_ID_EXTENDED = 0x0F00 // P IV, Xeon, Celeron D, P D,
|
||||
};
|
||||
|
||||
// this works on both newer AMD and Intel CPUs
|
||||
CpuIdResult_t cpuid1 = cpuid( 1 );
|
||||
|
||||
// <Sergiy> Previously, we detected P4 specifically; now, we detect GenuineIntel with HT enabled in general
|
||||
// if (((cpuid1.eax & FAMILY_ID) == FAMILY_ID_EXTENDED) || (cpuid1.eax & EXT_FAMILY_ID))
|
||||
|
||||
// Check to see if this is an Intel Processor with HT or CMT capability , and if HT/CMT is enabled
|
||||
// ddk: This codef is actually correct: see example code at software.intel.com/en-us/articles/multi-core-detect/
|
||||
return ( cpuid1.edx & HT_BIT ) != 0 && // Genuine Intel Processor with Hyper-Threading Technology implemented
|
||||
( ( cpuid1.ebx >> 16 ) & 0xFF ) > 1; // Hyper-Threading OR Core Multi-Processing has been enabled
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the number of logical processors per physical processors.
|
||||
static uint8 LogicalProcessorsPerPackage(void)
|
||||
{
|
||||
#if defined( _X360 )
|
||||
return 2;
|
||||
#else
|
||||
// EBX[23:16] indicate number of logical processors per package
|
||||
const unsigned NUM_LOGICAL_BITS = 0x00FF0000;
|
||||
|
||||
if ( !HTSupported() )
|
||||
return 1;
|
||||
|
||||
return ( uint8 )( ( cpuid( 1 ).ebx & NUM_LOGICAL_BITS ) >> 16 );
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(POSIX)
|
||||
// Move this declaration out of the CalculateClockSpeed() function because
|
||||
// otherwise clang warns that it is non-obvious whether it is a variable
|
||||
// or a function declaration: [-Wvexing-parse]
|
||||
uint64 CalculateCPUFreq(); // from cpu_linux.cpp
|
||||
#endif
|
||||
|
||||
// Measure the processor clock speed by sampling the cycle count, waiting
|
||||
// for some fraction of a second, then measuring the elapsed number of cycles.
|
||||
static int64 CalculateClockSpeed()
|
||||
{
|
||||
#if defined( _X360 ) || defined(_PS3)
|
||||
// Xbox360 and PS3 have the same clock speed and share a lot of characteristics on PPU
|
||||
return 3200000000LL;
|
||||
#else
|
||||
#if defined( _WIN32 )
|
||||
LARGE_INTEGER waitTime, startCount, curCount;
|
||||
CCycleCount start, end;
|
||||
|
||||
// Take 1/32 of a second for the measurement.
|
||||
QueryPerformanceFrequency( &waitTime );
|
||||
int scale = 5;
|
||||
waitTime.QuadPart >>= scale;
|
||||
|
||||
QueryPerformanceCounter( &startCount );
|
||||
start.Sample();
|
||||
do
|
||||
{
|
||||
QueryPerformanceCounter( &curCount );
|
||||
}
|
||||
while ( curCount.QuadPart - startCount.QuadPart < waitTime.QuadPart );
|
||||
end.Sample();
|
||||
|
||||
return (end.m_Int64 - start.m_Int64) << scale;
|
||||
#elif defined(POSIX)
|
||||
int64 freq =(int64)CalculateCPUFreq();
|
||||
if ( freq == 0 ) // couldn't calculate clock speed
|
||||
{
|
||||
Error( "Unable to determine CPU Frequency\n" );
|
||||
}
|
||||
return freq;
|
||||
#else
|
||||
#error "Please implement Clock Speed function for this platform"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static CPUInformation s_cpuInformation;
|
||||
|
||||
struct IntelCacheDesc_t
|
||||
{
|
||||
uint8 nDesc;
|
||||
uint16 nCacheSize;
|
||||
};
|
||||
|
||||
static IntelCacheDesc_t s_IntelL1DataCacheDesc[] = {
|
||||
{ 0xA, 8 },
|
||||
{ 0xC, 16 },
|
||||
{ 0xD, 16 },
|
||||
{ 0x2C, 32 },
|
||||
{ 0x30, 32 },
|
||||
{ 0x60, 16 },
|
||||
{ 0x66, 8 },
|
||||
{ 0x67, 16 },
|
||||
{ 0x68, 32 }
|
||||
};
|
||||
|
||||
|
||||
static IntelCacheDesc_t s_IntelL2DataCacheDesc[] =
|
||||
{
|
||||
{ 0x21, 256 },
|
||||
{ 0x39, 128 },
|
||||
{ 0x3a, 192 },
|
||||
{ 0x3b, 128 },
|
||||
{ 0x3c, 256 },
|
||||
{ 0x3D, 384 },
|
||||
{ 0x3E, 512 },
|
||||
{ 0x41, 128 },
|
||||
{ 0x42, 256 },
|
||||
{ 0x43, 512 },
|
||||
{ 0x44, 1024 },
|
||||
{ 0x45, 2048 },
|
||||
{ 0x48, 3 * 1024 },
|
||||
{ 0x4e, 6 * 1024 },
|
||||
{ 0x78, 1024 },
|
||||
{ 0x79, 128 },
|
||||
{ 0x7a, 256 },
|
||||
{ 0x7b, 512 },
|
||||
{ 0x7c, 1024 },
|
||||
{ 0x7d, 2048 },
|
||||
{ 0x7f, 512 },
|
||||
{ 0x82, 256 },
|
||||
{ 0x83, 512 },
|
||||
{ 0x84, 1024 },
|
||||
{ 0x85, 2048 },
|
||||
{ 0x86, 512 },
|
||||
{ 0x87, 1024 }
|
||||
};
|
||||
|
||||
|
||||
static IntelCacheDesc_t s_IntelL3DataCacheDesc[] = {
|
||||
{ 0x22, 512 },
|
||||
{ 0x23, 1024 },
|
||||
{ 0x25, 2 * 1024 },
|
||||
{ 0x29, 4 * 1024 },
|
||||
{ 0x46, 4 * 1024 },
|
||||
{ 0x47, 8 * 1024 },
|
||||
// { 49,
|
||||
{ 0x4a, 6 * 1024 },
|
||||
{ 0x4b, 8 * 1024 },
|
||||
{ 0x4c, 12 * 1024 },
|
||||
{ 0x4d, 16 * 1014 },
|
||||
{ 0xD0, 512 },
|
||||
{ 0xD1, 1024 },
|
||||
{ 0xD2, 2048 },
|
||||
{ 0xD6, 1024 },
|
||||
{ 0xD7, 2048 },
|
||||
{ 0xD8, 4096 },
|
||||
{ 0xDC, 1536 },
|
||||
{ 0xDD, 3 * 1024 },
|
||||
{ 0xDE, 6 * 1024 },
|
||||
{ 0xE2, 2048 },
|
||||
{ 0xE3, 4096 },
|
||||
{ 0xE4, 8 * 1024 },
|
||||
{ 0xEA, 12 * 1024 },
|
||||
{ 0xEB, 18 * 1024 },
|
||||
{ 0xEC, 24 * 1024 }
|
||||
};
|
||||
|
||||
static void FindIntelCacheDesc( uint8 nDesc, const IntelCacheDesc_t *pDesc, int nDescCount, uint32 &nCache, uint32 &nCacheDesc )
|
||||
{
|
||||
for ( int i = 0; i < nDescCount; ++i )
|
||||
{
|
||||
if ( pDesc->nDesc == nDesc )
|
||||
{
|
||||
nCache = pDesc->nCacheSize;
|
||||
nCacheDesc = nDesc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// see "Output of the CPUID instruction" from Intel, page 26
|
||||
static void InterpretIntelCacheDescriptors( uint32 nPackedDesc )
|
||||
{
|
||||
if ( nPackedDesc & 0x80000000 )
|
||||
{
|
||||
return; // this is a wrong descriptor
|
||||
}
|
||||
for ( int i = 0; i < 4; ++i )
|
||||
{
|
||||
FindIntelCacheDesc( nPackedDesc & 0xFF, s_IntelL1DataCacheDesc, ARRAYSIZE( s_IntelL1DataCacheDesc ), s_cpuInformation.m_nL1CacheSizeKb, s_cpuInformation.m_nL1CacheDesc );
|
||||
FindIntelCacheDesc( nPackedDesc & 0xFF, s_IntelL2DataCacheDesc, ARRAYSIZE( s_IntelL2DataCacheDesc ), s_cpuInformation.m_nL2CacheSizeKb, s_cpuInformation.m_nL2CacheDesc );
|
||||
FindIntelCacheDesc( nPackedDesc & 0xFF, s_IntelL3DataCacheDesc, ARRAYSIZE( s_IntelL3DataCacheDesc ), s_cpuInformation.m_nL3CacheSizeKb, s_cpuInformation.m_nL3CacheDesc );
|
||||
nPackedDesc >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const CPUInformation& GetCPUInformation()
|
||||
{
|
||||
CPUInformation &pi = s_cpuInformation;
|
||||
// Has the structure already been initialized and filled out?
|
||||
if ( pi.m_Size == sizeof(pi) )
|
||||
return pi;
|
||||
|
||||
// Redundant, but just in case the user somehow messes with the size.
|
||||
memset(&pi, 0x0, sizeof(pi));
|
||||
|
||||
// Fill out the structure, and return it:
|
||||
pi.m_Size = sizeof(pi);
|
||||
|
||||
// Grab the processor frequency:
|
||||
pi.m_Speed = CalculateClockSpeed();
|
||||
|
||||
// Get the logical and physical processor counts:
|
||||
pi.m_nLogicalProcessors = LogicalProcessorsPerPackage();
|
||||
|
||||
bool bAuthenticAMD = ( 0 == V_tier0_stricmp( GetProcessorVendorId(), "AuthenticAMD" ) );
|
||||
bool bGenuineIntel = !bAuthenticAMD && ( 0 == V_tier0_stricmp( GetProcessorVendorId(), "GenuineIntel" ) );
|
||||
|
||||
#if defined( _X360 )
|
||||
pi.m_nPhysicalProcessors = 3;
|
||||
pi.m_nLogicalProcessors = 6;
|
||||
#elif defined( _PS3 )
|
||||
pi.m_nPhysicalProcessors = 1;
|
||||
pi.m_nLogicalProcessors = 2;
|
||||
#elif defined(_WIN32) && !defined( _X360 )
|
||||
SYSTEM_INFO si;
|
||||
ZeroMemory( &si, sizeof(si) );
|
||||
|
||||
GetSystemInfo( &si );
|
||||
|
||||
// Sergiy: fixing: si.dwNumberOfProcessors is the number of logical processors according to experiments on i7, P4 and a DirectX sample (Aug'09)
|
||||
// this is contrary to MSDN documentation on GetSystemInfo()
|
||||
//
|
||||
pi.m_nLogicalProcessors = si.dwNumberOfProcessors;
|
||||
|
||||
if ( bAuthenticAMD )
|
||||
{
|
||||
// quick fix for AMD Phenom: it reports 3 logical cores and 4 physical cores;
|
||||
// no AMD CPUs by the end of 2009 have HT, so we'll override HT detection here
|
||||
pi.m_nPhysicalProcessors = pi.m_nLogicalProcessors;
|
||||
}
|
||||
else
|
||||
{
|
||||
CpuTopology topo;
|
||||
pi.m_nPhysicalProcessors = topo.NumberOfSystemCores();
|
||||
}
|
||||
|
||||
// Make sure I always report at least one, when running WinXP with the /ONECPU switch,
|
||||
// it likes to report 0 processors for some reason.
|
||||
if ( pi.m_nPhysicalProcessors == 0 && pi.m_nLogicalProcessors == 0 )
|
||||
{
|
||||
Assert( !"Sergiy: apparently I didn't fix some CPU detection code completely. Let me know and I'll do my best to fix it soon." );
|
||||
pi.m_nPhysicalProcessors = 1;
|
||||
pi.m_nLogicalProcessors = 1;
|
||||
}
|
||||
#elif defined(LINUX)
|
||||
pi.m_nLogicalProcessors = 0;
|
||||
pi.m_nPhysicalProcessors = 0;
|
||||
const int k_cMaxProcessors = 256;
|
||||
bool rgbProcessors[k_cMaxProcessors];
|
||||
memset( rgbProcessors, 0, sizeof( rgbProcessors ) );
|
||||
int cMaxCoreId = 0;
|
||||
|
||||
FILE *fpCpuInfo = fopen( "/proc/cpuinfo", "r" );
|
||||
if ( fpCpuInfo )
|
||||
{
|
||||
char rgchLine[256];
|
||||
while ( fgets( rgchLine, sizeof( rgchLine ), fpCpuInfo ) )
|
||||
{
|
||||
if ( !strncasecmp( rgchLine, "processor", strlen( "processor" ) ) )
|
||||
{
|
||||
pi.m_nLogicalProcessors++;
|
||||
}
|
||||
if ( !strncasecmp( rgchLine, "core id", strlen( "core id" ) ) )
|
||||
{
|
||||
char *pchValue = strchr( rgchLine, ':' );
|
||||
cMaxCoreId = MAX( cMaxCoreId, atoi( pchValue + 1 ) );
|
||||
}
|
||||
if ( !strncasecmp( rgchLine, "physical id", strlen( "physical id" ) ) )
|
||||
{
|
||||
// it seems (based on survey data) that we can see
|
||||
// processor N (N > 0) when it's the only processor in
|
||||
// the system. so keep track of each processor
|
||||
char *pchValue = strchr( rgchLine, ':' );
|
||||
int cPhysicalId = atoi( pchValue + 1 );
|
||||
if ( cPhysicalId < k_cMaxProcessors )
|
||||
rgbProcessors[cPhysicalId] = true;
|
||||
}
|
||||
/* this code will tell us how many physical chips are in the machine, but we want
|
||||
core count, so for the moment, each processor counts as both logical and physical.
|
||||
if ( !strncasecmp( rgchLine, "physical id ", strlen( "physical id " ) ) )
|
||||
{
|
||||
char *pchValue = strchr( rgchLine, ':' );
|
||||
pi.m_nPhysicalProcessors = MAX( pi.m_nPhysicalProcessors, atol( pchValue ) );
|
||||
}
|
||||
*/
|
||||
}
|
||||
fclose( fpCpuInfo );
|
||||
for ( int i = 0; i < k_cMaxProcessors; i++ )
|
||||
if ( rgbProcessors[i] )
|
||||
pi.m_nPhysicalProcessors++;
|
||||
pi.m_nPhysicalProcessors *= ( cMaxCoreId + 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
pi.m_nLogicalProcessors = 1;
|
||||
pi.m_nPhysicalProcessors = 1;
|
||||
Assert( !"couldn't read cpu information from /proc/cpuinfo" );
|
||||
}
|
||||
|
||||
#elif defined(OSX)
|
||||
|
||||
int num_phys_cpu = 1, num_log_cpu = 1;
|
||||
size_t len = sizeof(num_phys_cpu);
|
||||
sysctlbyname( "hw.physicalcpu", &num_phys_cpu, &len, NULL, 0 );
|
||||
sysctlbyname( "hw.logicalcpu", &num_log_cpu, &len, NULL, 0 );
|
||||
pi.m_nPhysicalProcessors = num_phys_cpu;
|
||||
pi.m_nLogicalProcessors = num_log_cpu;
|
||||
|
||||
#endif
|
||||
|
||||
CpuIdResult_t cpuid0 = cpuid( 0 );
|
||||
if ( cpuid0.eax >= 1 )
|
||||
{
|
||||
CpuIdResult_t cpuid1 = cpuid( 1 );
|
||||
uint bFPU = cpuid1.edx & 1; // this should always be on on anything we support
|
||||
// Determine Processor Features:
|
||||
pi.m_bRDTSC = ( cpuid1.edx >> 4 ) & 1;
|
||||
pi.m_bCMOV = ( cpuid1.edx >> 15 ) & 1;
|
||||
pi.m_bFCMOV = ( pi.m_bCMOV && bFPU ) ? 1 : 0;
|
||||
pi.m_bMMX = ( cpuid1.edx >> 23 ) & 1;
|
||||
pi.m_bSSE = ( cpuid1.edx >> 25 ) & 1;
|
||||
pi.m_bSSE2 = ( cpuid1.edx >> 26 ) & 1;
|
||||
pi.m_bSSE3 = cpuid1.ecx & 1;
|
||||
pi.m_bSSSE3 = ( cpuid1.ecx >> 9 ) & 1;;
|
||||
pi.m_bSSE4a = CheckSSE4aTechnology();
|
||||
pi.m_bSSE41 = ( cpuid1.ecx >> 19 ) & 1;
|
||||
pi.m_bSSE42 = ( cpuid1.ecx >> 20 ) & 1;
|
||||
pi.m_b3DNow = Check3DNowTechnology();
|
||||
pi.m_bAVX = ( cpuid1.ecx >> 28 ) & 1;
|
||||
pi.m_szProcessorID = ( tchar* )GetProcessorVendorId();
|
||||
pi.m_szProcessorBrand = ( tchar* )GetProcessorBrand();
|
||||
pi.m_bHT = ( pi.m_nPhysicalProcessors < pi.m_nLogicalProcessors ); //HTSupported();
|
||||
|
||||
pi.m_nModel = cpuid1.eax; // full CPU model info
|
||||
pi.m_nFeatures[ 0 ] = cpuid1.edx; // x87+ features
|
||||
pi.m_nFeatures[ 1 ] = cpuid1.ecx; // sse3+ features
|
||||
pi.m_nFeatures[ 2 ] = cpuid1.ebx; // some additional features
|
||||
|
||||
if ( bGenuineIntel )
|
||||
{
|
||||
if ( cpuid0.eax >= 4 )
|
||||
{
|
||||
// we have CPUID.4, use it to find all the cache parameters
|
||||
const uint nCachesToQuery = 4; // leve 0 is not used
|
||||
uint nCacheSizeKiB[ nCachesToQuery ];
|
||||
for ( uint i = 0; i < nCachesToQuery; ++i )
|
||||
{
|
||||
nCacheSizeKiB[ i ] = 0;
|
||||
}
|
||||
for ( unsigned long nSub = 0; nSub < 1024 ; ++nSub )
|
||||
{
|
||||
CpuIdResult_t cpuid4 = cpuidex( 4, nSub );
|
||||
uint nCacheType = cpuid4.eax & 0x1F;
|
||||
if ( nCacheType == 0 )
|
||||
{
|
||||
// no more caches
|
||||
break;
|
||||
}
|
||||
if ( nCacheType & 1 )
|
||||
{
|
||||
// this cache includes data cache: it's either data or unified. Instuction cache type is 2
|
||||
uint nCacheLevel = ( cpuid4.eax >> 5 ) & 7;
|
||||
if ( nCacheLevel < nCachesToQuery )
|
||||
{
|
||||
uint nCacheWays = 1 + ( ( cpuid4.ebx >> 22 ) & 0x3F );
|
||||
uint nCachePartitions = 1 + ( ( cpuid4.ebx >> 12 ) & 0x3F );
|
||||
uint nCacheLineSize = 1 + ( cpuid4.ebx & 0xFF );
|
||||
uint nCacheSets = 1 + cpuid4.ecx;
|
||||
uint nCacheSizeBytes = nCacheWays * nCachePartitions * nCacheLineSize * nCacheSets;
|
||||
nCacheSizeKiB[ nCacheLevel ] = nCacheSizeBytes >> 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pi.m_nL1CacheSizeKb = nCacheSizeKiB[ 1 ];
|
||||
pi.m_nL2CacheSizeKb = nCacheSizeKiB[ 2 ];
|
||||
pi.m_nL3CacheSizeKb = nCacheSizeKiB[ 3 ];
|
||||
}
|
||||
else if ( cpuid0.eax >= 2 )
|
||||
{
|
||||
// get the cache
|
||||
CpuIdResult_t cpuid2 = cpuid( 2 );
|
||||
for ( int i = ( cpuid2.eax & 0xFF ); i-- > 0; )
|
||||
{
|
||||
InterpretIntelCacheDescriptors( cpuid2.eax & ~0xFF );
|
||||
InterpretIntelCacheDescriptors( cpuid2.ebx );
|
||||
InterpretIntelCacheDescriptors( cpuid2.ecx );
|
||||
InterpretIntelCacheDescriptors( cpuid2.edx );
|
||||
cpuid2 = cpuid( 2 ); // read the next
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CpuIdResult_t cpuid0ex = cpuid( 0x80000000 );
|
||||
if ( bAuthenticAMD )
|
||||
{
|
||||
if ( cpuid0ex.eax >= 0x80000005 )
|
||||
{
|
||||
CpuIdResult_t cpuid5ex = cpuid( 0x80000005 );
|
||||
pi.m_nL1CacheSizeKb = cpuid5ex.ecx >> 24;
|
||||
pi.m_nL1CacheDesc = cpuid5ex.ecx & 0xFFFFFF;
|
||||
}
|
||||
if ( cpuid0ex.eax >= 0x80000006 )
|
||||
{
|
||||
CpuIdResult_t cpuid6ex = cpuid( 0x80000006 );
|
||||
pi.m_nL2CacheSizeKb = cpuid6ex.ecx >> 16;
|
||||
pi.m_nL2CacheDesc = cpuid6ex.ecx & 0xFFFF;
|
||||
pi.m_nL3CacheSizeKb = ( cpuid6ex.edx >> 18 ) * 512;
|
||||
pi.m_nL3CacheDesc = cpuid6ex.edx & 0xFFFF;
|
||||
}
|
||||
}
|
||||
else if ( bGenuineIntel )
|
||||
{
|
||||
if ( cpuid0ex.eax >= 0x80000006 )
|
||||
{
|
||||
// make sure we got the L2 cache info right
|
||||
pi.m_nL2CacheSizeKb = ( cpuid( 0x80000006 ).ecx >> 16 );
|
||||
}
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
158
tier0/cpu_posix.cpp
Normal file
158
tier0/cpu_posix.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: determine CPU speed under linux
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <tier0/platform.h>
|
||||
#include <errno.h>
|
||||
|
||||
class TimeVal
|
||||
{
|
||||
public:
|
||||
TimeVal() {}
|
||||
TimeVal& operator=(const TimeVal &val) { m_TimeVal = val.m_TimeVal; return *this; }
|
||||
inline double operator-(const TimeVal &left)
|
||||
{
|
||||
uint64 left_us = (uint64) left.m_TimeVal.tv_sec * 1000000 + left.m_TimeVal.tv_usec;
|
||||
uint64 right_us = (uint64) m_TimeVal.tv_sec * 1000000 + m_TimeVal.tv_usec;
|
||||
uint64 diff_us = right_us - left_us;
|
||||
return diff_us * ( 1.0 / 1000000.0 );
|
||||
}
|
||||
|
||||
timeval m_TimeVal;
|
||||
};
|
||||
|
||||
// Compute the positive difference between two 64 bit numbers.
|
||||
static inline uint64 diff(uint64 v1, uint64 v2)
|
||||
{
|
||||
// uint64 d = v1 - v2;
|
||||
// if (d >= 0) return d; else return -d;
|
||||
return (v1 >= v2 ? v1 - v2 : v2 - v1);
|
||||
}
|
||||
|
||||
#ifdef OSX
|
||||
uint64 GetCPUFreqFromPROC()
|
||||
{
|
||||
int mib[2] = {CTL_HW, HW_CPU_FREQ};
|
||||
uint64 frequency = 0;
|
||||
size_t len = sizeof(frequency);
|
||||
|
||||
if (sysctl(mib, 2, &frequency, &len, NULL, 0) == -1)
|
||||
return 0;
|
||||
return frequency;
|
||||
}
|
||||
#else
|
||||
uint64 GetCPUFreqFromPROC()
|
||||
{
|
||||
double mhz = 0;
|
||||
char line[1024], *s, search_str[] = "cpu MHz";
|
||||
FILE *fp;
|
||||
|
||||
/* open proc/cpuinfo */
|
||||
if ((fp = fopen("/proc/cpuinfo", "r")) == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ignore all lines until we reach MHz information */
|
||||
while (fgets(line, 1024, fp) != NULL)
|
||||
{
|
||||
if (strstr(line, search_str) != NULL)
|
||||
{
|
||||
/* ignore all characters in line up to : */
|
||||
for (s = line; *s && (*s != ':'); ++s)
|
||||
;
|
||||
|
||||
/* get MHz number */
|
||||
if (*s && (sscanf(s+1, "%lf", &mhz) == 1))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fp!=NULL) fclose(fp);
|
||||
|
||||
return (uint64)(mhz*1000000);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
uint64 CalculateCPUFreq()
|
||||
{
|
||||
#ifdef LINUX
|
||||
char const *pFreq = getenv("CPU_MHZ");
|
||||
if ( pFreq )
|
||||
{
|
||||
uint64 retVal = 1000000;
|
||||
return retVal * atoi( pFreq );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compute the period. Loop until we get 3 consecutive periods that
|
||||
// are the same to within a small error. The error is chosen
|
||||
// to be +/- 0.02% on a P-200.
|
||||
const uint64 error = 40000;
|
||||
const int max_iterations = 600;
|
||||
int count;
|
||||
uint64 period, period1 = error * 2, period2 = 0, period3 = 0;
|
||||
|
||||
for (count = 0; count < max_iterations; count++)
|
||||
{
|
||||
TimeVal start_time, end_time;
|
||||
uint64 start_tsc, end_tsc;
|
||||
|
||||
gettimeofday( &start_time.m_TimeVal, 0 );
|
||||
start_tsc = Plat_Rdtsc();
|
||||
usleep( 5000 ); // sleep for 5 msec
|
||||
gettimeofday( &end_time.m_TimeVal, 0 );
|
||||
end_tsc = Plat_Rdtsc();
|
||||
|
||||
period3 = ( end_tsc - start_tsc ) / ( end_time - start_time );
|
||||
|
||||
if (diff( period1, period2 ) <= error &&
|
||||
diff( period2, period3 ) <= error &&
|
||||
diff( period1, period3 ) <= error )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
period1 = period2;
|
||||
period2 = period3;
|
||||
}
|
||||
|
||||
if ( count == max_iterations )
|
||||
{
|
||||
return GetCPUFreqFromPROC(); // fall back to /proc
|
||||
}
|
||||
|
||||
// Set the period to the average period measured.
|
||||
period = ( period1 + period2 + period3 ) / 3;
|
||||
|
||||
// Some Pentiums have broken TSCs that increment very
|
||||
// slowly or unevenly.
|
||||
if ( period < 10000000 )
|
||||
{
|
||||
return GetCPUFreqFromPROC(); // fall back to /proc
|
||||
}
|
||||
|
||||
return period;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
396
tier0/cpumonitoring.cpp
Normal file
396
tier0/cpumonitoring.cpp
Normal file
@@ -0,0 +1,396 @@
|
||||
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
||||
//
|
||||
// A non-trivial number of Valve customers hit performance problems because their CPUs overheat
|
||||
// and are thermally throttled. While thermal throttling is better than melting it is still a
|
||||
// hardware flaw and it leads to a bad user experience. In some cases the CPU frequency drops
|
||||
// (constantly or occasionally) by 50-75%, leading to equal or greater framerate drops.
|
||||
//
|
||||
// This is equivalent to a car that goes into limp-home mode to let it continue running after the
|
||||
// radiator fails -- it's better than destroying the engine, but clearly it needs to be fixed.
|
||||
//
|
||||
// When CPU monitoring is enabled a bunch of background threads are created that wake up at
|
||||
// the set frequency, spin in a loop to measure the actual usable CPU frequency, then sleep again.
|
||||
// A delay loop is used to measure the frequency because this is portable (it works for Intel
|
||||
// and AMD and handles both frequency throttling and duty-cycle reductions) and it doesn't
|
||||
// require administrator privileges. This technique has been used in VTrace for a while.
|
||||
//
|
||||
// This code doesn't use normal worker threads because of the special purpose nature of this
|
||||
// work. The threads are started on demand and are never terminated, in order to simplify
|
||||
// the code.
|
||||
//
|
||||
//===============================================================================
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/cpumonitoring.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS_PC32
|
||||
#include "tier0/threadtools.h"
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#include "PowrProf.h"
|
||||
#include <algorithm>
|
||||
#pragma comment(lib, "PowrProf.lib")
|
||||
|
||||
// This lock protects s_results and s_nDelayMilliseconds.
|
||||
static CThreadMutex s_lock;
|
||||
static CPUFrequencyResults s_results;
|
||||
static unsigned s_nDelayMilliseconds;
|
||||
// Has monitoring been enabled? If not measurements may still continue
|
||||
// if kDelayMillisecondsWhenDisabled is non-zero.
|
||||
static bool s_fEnabled = false;
|
||||
|
||||
// This is the delay between measurements when measurements are 'disabled'. If it
|
||||
// is zero then the measurements are truly disabled.
|
||||
const unsigned kDelayMillisecondsWhenDisabled = 5000;
|
||||
// Delay before first measurement
|
||||
const unsigned kFirstInterval = 500;
|
||||
const unsigned kPostMeasureInterval = 5;
|
||||
const unsigned kMinimumDelay = 300;
|
||||
|
||||
const int nMaxCPUs = 32;
|
||||
|
||||
// This loop spins spinCount times and should take about 50 times spinCount
|
||||
// cycles to execute. This should be true on any reasonable modern processor
|
||||
// since the latency of integer add is almost always one cycle.
|
||||
// The Xbox 360 and PS3 CPUs are the one known exception but this code will
|
||||
// never run on them.
|
||||
static void SpinALot( int spinCount )
|
||||
{
|
||||
__asm
|
||||
{
|
||||
mov ecx, spinCount
|
||||
start:
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
add eax, eax
|
||||
|
||||
sub ecx,1
|
||||
jne start
|
||||
}
|
||||
}
|
||||
|
||||
static LARGE_INTEGER frequency;
|
||||
static LARGE_INTEGER base;
|
||||
|
||||
static void InitializeGetTime()
|
||||
{
|
||||
QueryPerformanceFrequency( &frequency );
|
||||
QueryPerformanceCounter( &base );
|
||||
}
|
||||
|
||||
static double GetTime()
|
||||
{
|
||||
LARGE_INTEGER value;
|
||||
QueryPerformanceCounter( &value );
|
||||
|
||||
// Subtracting off the base time gives us a zero point at application start up and
|
||||
// gives us more precision.
|
||||
return ( value.QuadPart - base.QuadPart ) / double( frequency.QuadPart );
|
||||
}
|
||||
|
||||
static float GetFrequency()
|
||||
{
|
||||
double start = GetTime();
|
||||
// This should cause a delay of 500,000 cycles (50 * spinCount) which should be a
|
||||
// fraction of a millisecond on any reasonable processor, thus ensuring that the
|
||||
// sampling interrupt will not be hit too frequently.
|
||||
SpinALot( 10000 );
|
||||
double elapsed = GetTime() - start;
|
||||
double frequency = ( 500000 / elapsed ) / 1e9;
|
||||
return (float)frequency;
|
||||
}
|
||||
|
||||
// This semaphore is used to release all of the measurement threads simultaneously.
|
||||
static HANDLE g_releaseSemaphore;
|
||||
// This semaphore is used to wait for all of the measurement threads to complete.
|
||||
static HANDLE g_workCompleteSemaphore;
|
||||
static DWORD g_numCPUs;
|
||||
|
||||
// This function measures the CPU frequency by doing repeated integer adds.
|
||||
// It measures it multiple times and records the highest frequency -- the
|
||||
// assumption is that any given test might be slowed by interrupts or
|
||||
// context switches so the fastest run should indicate the true performance.
|
||||
static float GetSampledFrequency( int iterations )
|
||||
{
|
||||
float maxFrequency = 0.0;
|
||||
for ( int i = 0; i < iterations; ++i )
|
||||
{
|
||||
float frequency = GetFrequency();
|
||||
if ( frequency > maxFrequency )
|
||||
maxFrequency = frequency;
|
||||
}
|
||||
|
||||
return maxFrequency;
|
||||
}
|
||||
|
||||
// The measured frequency of all of the threads
|
||||
static float s_frequency[ nMaxCPUs ];
|
||||
|
||||
// Measurement thread, designed to be one per core.
|
||||
static DWORD WINAPI MeasureThread( LPVOID vThreadNum )
|
||||
{
|
||||
ThreadSetDebugName( "CPUMonitoringMeasureThread" );
|
||||
int threadNum = (int)vThreadNum;
|
||||
for ( ; ; )
|
||||
{
|
||||
// Wait until the MCP says it's time to wake up and measure CPU speed
|
||||
WaitForSingleObject( g_releaseSemaphore, INFINITE );
|
||||
|
||||
// Seven seems like a good number of times to measure the frequency -- it makes
|
||||
// it likely that a couple of the tests will not hit any interrupts.
|
||||
float frequency = GetSampledFrequency( 7 );
|
||||
s_frequency[ threadNum ] = frequency;
|
||||
|
||||
// Tell the heartbeat thread that one thread has completed.
|
||||
ReleaseSemaphore( g_workCompleteSemaphore, 1, NULL );
|
||||
}
|
||||
|
||||
// This will never be hit.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Note that this structure definition was accidentally omitted from WinNT.h. This error will be corrected in the future. In the meantime, to compile your application, include the structure definition contained in this topic in your source code.
|
||||
*/
|
||||
typedef struct _PROCESSOR_POWER_INFORMATION {
|
||||
ULONG Number;
|
||||
ULONG MaxMhz;
|
||||
ULONG CurrentMhz;
|
||||
ULONG MhzLimit;
|
||||
ULONG MaxIdleState;
|
||||
ULONG CurrentIdleState;
|
||||
} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
|
||||
|
||||
// Master control thread to periodically wake the measurement threads.
|
||||
static DWORD WINAPI HeartbeatThread( LPVOID )
|
||||
{
|
||||
ThreadSetDebugName( "CPUMonitoringHeartbeatThread" );
|
||||
// Arbitrary/hacky time to wait for results to become available.
|
||||
Sleep( kFirstInterval );
|
||||
for ( ; ; )
|
||||
{
|
||||
unsigned delay;
|
||||
{
|
||||
// Read and write all the state that is shared with the main thread while holding the lock.
|
||||
AUTO_LOCK( s_lock );
|
||||
delay = s_nDelayMilliseconds;
|
||||
}
|
||||
|
||||
// If monitoring is currently enabled then do the work.
|
||||
if ( delay )
|
||||
{
|
||||
// First ask Windows what the processor speed is -- this *might* reflect
|
||||
// some types of thermal throttling, but doesn't seem to.
|
||||
PROCESSOR_POWER_INFORMATION processorInfo[ nMaxCPUs ] = {};
|
||||
CallNtPowerInformation( ProcessorInformation, NULL, 0, &processorInfo, sizeof(processorInfo[0]) * g_numCPUs );
|
||||
|
||||
ULONG MaxMHz = processorInfo[ 0 ].MaxMhz;
|
||||
ULONG LimitMHz = processorInfo[ 0 ].MhzLimit;
|
||||
ULONG MinCurrentMHz = processorInfo[ 0 ].CurrentMhz;
|
||||
ULONG MaxCurrentMHz = processorInfo[ 0 ].CurrentMhz;
|
||||
for ( DWORD i = 0; i < g_numCPUs; ++i )
|
||||
{
|
||||
MinCurrentMHz = std::min( MinCurrentMHz, processorInfo[ i ].CurrentMhz );
|
||||
MaxCurrentMHz = std::max( MaxCurrentMHz, processorInfo[ i ].CurrentMhz );
|
||||
MaxMHz = std::max( MaxMHz, processorInfo[ i ].MaxMhz );
|
||||
LimitMHz = std::max( LimitMHz, processorInfo[ i ].MhzLimit );
|
||||
}
|
||||
|
||||
// This will wake up all of the worker threads. It is possible that some of the
|
||||
// threads will take a long time to wake up in which case the same thread might
|
||||
// wake up multiple times but this should be harmless.
|
||||
ReleaseSemaphore( g_releaseSemaphore, g_numCPUs, NULL );
|
||||
|
||||
// Wait until all of the measurement threads should have run.
|
||||
// This is just to avoid having the heartbeat thread fighting for cycles
|
||||
// but isn't strictly necessary.
|
||||
Sleep( kPostMeasureInterval );
|
||||
|
||||
// Wait for all of the worker threads to finish.
|
||||
for ( DWORD i = 0; i < g_numCPUs; ++i )
|
||||
{
|
||||
WaitForSingleObject( g_workCompleteSemaphore, INFINITE );
|
||||
}
|
||||
|
||||
// Find the minimum and maximum measured frequencies.
|
||||
float minActualFreq = s_frequency[ 0 ];
|
||||
float maxActualFreq = s_frequency[ 0 ];
|
||||
for ( DWORD i = 1; i < g_numCPUs; ++i )
|
||||
{
|
||||
minActualFreq = std::min( minActualFreq, s_frequency[ i ] );
|
||||
maxActualFreq = std::max( maxActualFreq, s_frequency[ i ] );
|
||||
}
|
||||
|
||||
{
|
||||
// Read and write all the state that is shared with the main thread while holding the lock.
|
||||
AUTO_LOCK( s_lock );
|
||||
float freqPercentage = maxActualFreq / (MaxCurrentMHz * 1e-5f);
|
||||
const float kFudgeFactor = 1.03f; // Make results match reality better
|
||||
s_results.m_timeStamp = Plat_FloatTime();
|
||||
s_results.m_GHz = maxActualFreq * kFudgeFactor;
|
||||
s_results.m_percentage = freqPercentage * kFudgeFactor;
|
||||
|
||||
if ( s_results.m_lowestPercentage == 0 || s_results.m_percentage < s_results.m_lowestPercentage )
|
||||
s_results.m_lowestPercentage = s_results.m_percentage;
|
||||
// delay may get set to zero at this point
|
||||
delay = s_nDelayMilliseconds;
|
||||
}
|
||||
|
||||
Sleep( delay );
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is nothing to do then just sleep for a bit.
|
||||
Sleep( kMinimumDelay );
|
||||
}
|
||||
}
|
||||
|
||||
// This will never be hit.
|
||||
return 0;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE CPUFrequencyResults GetCPUFrequencyResults( bool fGetDisabledResults )
|
||||
{
|
||||
AUTO_LOCK( s_lock );
|
||||
if ( s_fEnabled || fGetDisabledResults )
|
||||
{
|
||||
// Return actual results.
|
||||
return s_results;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return zero initialized struct.
|
||||
return CPUFrequencyResults();
|
||||
}
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void SetCPUMonitoringInterval( unsigned nDelayMilliseconds )
|
||||
{
|
||||
static bool s_initialized = false;
|
||||
|
||||
// Clamp the delay to a minimum value to save users from running the
|
||||
// measurements too frequently.
|
||||
if ( nDelayMilliseconds && nDelayMilliseconds <= kMinimumDelay )
|
||||
nDelayMilliseconds = kMinimumDelay;
|
||||
|
||||
// If not yet initialized then do one-time thread initialization
|
||||
if ( !s_initialized )
|
||||
{
|
||||
s_initialized = true;
|
||||
|
||||
InitializeGetTime();
|
||||
|
||||
g_releaseSemaphore = CreateSemaphore( NULL, 0, 1000, NULL );
|
||||
if ( !g_releaseSemaphore )
|
||||
return;
|
||||
g_workCompleteSemaphore = CreateSemaphore( NULL, 0, 1000, NULL );
|
||||
if ( !g_workCompleteSemaphore )
|
||||
return;
|
||||
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetSystemInfo( &systemInfo );
|
||||
|
||||
g_numCPUs = systemInfo.dwNumberOfProcessors;
|
||||
if ( g_numCPUs > nMaxCPUs )
|
||||
g_numCPUs = nMaxCPUs;
|
||||
|
||||
// Create n threads, affinitize them, and set them to high priority. This will (mostly)
|
||||
// ensure that they will run promptly on a specific CPU.
|
||||
for ( DWORD i = 0; i < g_numCPUs; ++i )
|
||||
{
|
||||
HANDLE thread = CreateThread( NULL, 0x10000, MeasureThread, (void*)i, 0, NULL );
|
||||
SetThreadAffinityMask( thread, 1u << i );
|
||||
SetThreadPriority( thread, THREAD_PRIORITY_HIGHEST );
|
||||
}
|
||||
|
||||
// Create the thread which tells the measurement threads to wake up periodically
|
||||
CreateThread( NULL, 0x10000, HeartbeatThread, NULL, 0, NULL );
|
||||
}
|
||||
|
||||
AUTO_LOCK( s_lock );
|
||||
if ( nDelayMilliseconds && s_nDelayMilliseconds == 0 )
|
||||
{
|
||||
// If we are enabling/re-enabling then reset the stats.
|
||||
memset( &s_results, 0, sizeof(s_results) );
|
||||
}
|
||||
// Set the specified delay time or 5,000 if it is disabled.
|
||||
s_nDelayMilliseconds = nDelayMilliseconds ? nDelayMilliseconds : kDelayMillisecondsWhenDisabled;
|
||||
s_fEnabled = nDelayMilliseconds != 0;
|
||||
}
|
||||
|
||||
class CPUMonitoringStarter
|
||||
{
|
||||
public:
|
||||
CPUMonitoringStarter()
|
||||
{
|
||||
// Start up the disabled CPU monitoring at low frequency.
|
||||
if ( kDelayMillisecondsWhenDisabled )
|
||||
SetCPUMonitoringInterval( 0 );
|
||||
}
|
||||
} s_CPUMonitoringStarter;
|
||||
|
||||
#else
|
||||
PLATFORM_INTERFACE CPUFrequencyResults GetCPUFrequencyResults(bool)
|
||||
{
|
||||
// Return zero initialized results which means no data available.
|
||||
CPUFrequencyResults results = {};
|
||||
return results;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void SetCPUMonitoringInterval( unsigned nDelayMilliseconds )
|
||||
{
|
||||
NOTE_UNUSED( nDelayMilliseconds );
|
||||
}
|
||||
|
||||
#endif
|
||||
1005
tier0/cputopology.cpp
Normal file
1005
tier0/cputopology.cpp
Normal file
File diff suppressed because it is too large
Load Diff
40
tier0/cputopology.h
Normal file
40
tier0/cputopology.h
Normal file
@@ -0,0 +1,40 @@
|
||||
//-------------------------------------------------------------------------------------
|
||||
// CpuTopology.h
|
||||
//
|
||||
// CpuToplogy class declaration.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-------------------------------------------------------------------------------------
|
||||
#pragma once
|
||||
#ifndef CPU_TOPOLOGY_H
|
||||
#define CPU_TOPOLOGY_H
|
||||
|
||||
#include "winlite.h"
|
||||
|
||||
class ICpuTopology;
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
// Name: CpuToplogy
|
||||
// Desc: This class constructs a supported cpu topology implementation object on
|
||||
// initialization and forwards calls to it. This is the Abstraction class
|
||||
// in the traditional Bridge Pattern.
|
||||
//---------------------------------------------------------------------------------
|
||||
class CpuTopology
|
||||
{
|
||||
public:
|
||||
CpuTopology( BOOL bForceCpuid = FALSE );
|
||||
~CpuTopology();
|
||||
|
||||
BOOL IsDefaultImpl() const;
|
||||
DWORD NumberOfProcessCores() const;
|
||||
DWORD NumberOfSystemCores() const;
|
||||
DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const;
|
||||
|
||||
void ForceCpuid( BOOL bForce );
|
||||
private:
|
||||
void Destroy_();
|
||||
|
||||
ICpuTopology* m_pImpl;
|
||||
};
|
||||
|
||||
#endif // CPU_TOPOLOGY_H
|
||||
686
tier0/dbg.cpp
Normal file
686
tier0/dbg.cpp
Normal file
@@ -0,0 +1,686 @@
|
||||
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
#if defined( PLATFORM_WINDOWS_PC )
|
||||
#define WIN_32_LEAN_AND_MEAN
|
||||
#include <windows.h> // Currently needed for IsBadReadPtr and IsBadWritePtr
|
||||
#pragma comment(lib,"user32.lib") // For MessageBox
|
||||
#endif
|
||||
|
||||
#include "tier0/minidump.h"
|
||||
#include "tier0/stacktools.h"
|
||||
#include "tier0/etwprof.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include "color.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/threadtools.h"
|
||||
#include "tier0/icommandline.h"
|
||||
#include "tier0/vprof.h"
|
||||
#include <math.h>
|
||||
|
||||
#if defined( _X360 )
|
||||
#include "xbox/xbox_console.h"
|
||||
#endif
|
||||
|
||||
#ifndef STEAM
|
||||
#define PvRealloc realloc
|
||||
#define PvAlloc malloc
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
#pragma optimize( "g", off ) //variable argument functions seem to screw up stack walking unless this optimization is disabled
|
||||
// Disable this warning: dbg.cpp(479): warning C4748: /GS can not protect parameters and local variables from local buffer overrun because optimizations are disabled in function
|
||||
#pragma warning( disable : 4748 )
|
||||
#endif
|
||||
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_LOADING, "LOADING" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Stack attachment management
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
|
||||
static bool s_bCallStacksWithAllWarnings = false; //if true, attach a call stack to every SPEW_WARNING message. Warning()/DevWarning()/...
|
||||
static int s_iWarningMaxCallStackLength = 5;
|
||||
#define AutomaticWarningCallStackLength() (s_bCallStacksWithAllWarnings ? s_iWarningMaxCallStackLength : 0)
|
||||
|
||||
void _Warning_AlwaysSpewCallStack_Enable( bool bEnable )
|
||||
{
|
||||
s_bCallStacksWithAllWarnings = bEnable;
|
||||
}
|
||||
|
||||
void _Warning_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
||||
{
|
||||
s_iWarningMaxCallStackLength = iMaxCallStackLength;
|
||||
}
|
||||
|
||||
static bool s_bCallStacksWithAllErrors = false; //if true, attach a call stack to every SPEW_ERROR message. Mostly just Error()
|
||||
static int s_iErrorMaxCallStackLength = 20; //default to higher output with an error since we're quitting anyways
|
||||
#define AutomaticErrorCallStackLength() (s_bCallStacksWithAllErrors ? s_iErrorMaxCallStackLength : 0)
|
||||
|
||||
void _Error_AlwaysSpewCallStack_Enable( bool bEnable )
|
||||
{
|
||||
s_bCallStacksWithAllErrors = bEnable;
|
||||
}
|
||||
|
||||
void _Error_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
||||
{
|
||||
s_iErrorMaxCallStackLength = iMaxCallStackLength;
|
||||
}
|
||||
|
||||
#else //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
|
||||
#define AutomaticWarningCallStackLength() 0
|
||||
#define AutomaticErrorCallStackLength() 0
|
||||
|
||||
void _Warning_AlwaysSpewCallStack_Enable( bool bEnable )
|
||||
{
|
||||
}
|
||||
|
||||
void _Warning_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
||||
{
|
||||
}
|
||||
|
||||
void _Error_AlwaysSpewCallStack_Enable( bool bEnable )
|
||||
{
|
||||
}
|
||||
|
||||
void _Error_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
||||
{
|
||||
}
|
||||
|
||||
#endif //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
|
||||
// Skip forward past the directory
|
||||
static const char *SkipToFname( const tchar* pFile )
|
||||
{
|
||||
if ( pFile == NULL )
|
||||
return "unknown";
|
||||
const tchar* pSlash = _tcsrchr( pFile, '\\' );
|
||||
const tchar* pSlash2 = _tcsrchr( pFile, '/' );
|
||||
if (pSlash < pSlash2) pSlash = pSlash2;
|
||||
return pSlash ? pSlash + 1: pFile;
|
||||
}
|
||||
|
||||
void _ExitOnFatalAssert( const tchar* pFile, int line )
|
||||
{
|
||||
Log_Msg( LOG_ASSERT, _T("Fatal assert failed: %s, line %d. Application exiting.\n"), pFile, line );
|
||||
|
||||
// only write out minidumps if we're not in the debugger
|
||||
if ( !Plat_IsInDebugSession() )
|
||||
{
|
||||
WriteMiniDump();
|
||||
}
|
||||
|
||||
Log_Msg( LOG_DEVELOPER, _T("_ExitOnFatalAssert\n") );
|
||||
Plat_ExitProcess( EXIT_FAILURE );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Templates to assist in validating pointers:
|
||||
//-----------------------------------------------------------------------------
|
||||
PLATFORM_INTERFACE void _AssertValidReadPtr( void* ptr, int count/* = 1*/ )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
Assert( !IsBadReadPtr( ptr, count ) );
|
||||
#else
|
||||
Assert( !count || ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void _AssertValidWritePtr( void* ptr, int count/* = 1*/ )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
Assert( !IsBadWritePtr( ptr, count ) );
|
||||
#else
|
||||
Assert( !count || ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void _AssertValidReadWritePtr( void* ptr, int count/* = 1*/ )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
Assert(!( IsBadWritePtr(ptr, count) || IsBadReadPtr(ptr,count)));
|
||||
#else
|
||||
Assert( !count || ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void _AssertValidStringPtr( const tchar* ptr, int maxchar/* = 0xFFFFFF */ )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
#ifdef TCHAR_IS_CHAR
|
||||
Assert( !IsBadStringPtr( ptr, maxchar ) );
|
||||
#else
|
||||
Assert( !IsBadStringPtrW( ptr, maxchar ) );
|
||||
#endif
|
||||
#else
|
||||
Assert( ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void AssertValidWStringPtr( const wchar_t* ptr, int maxchar/* = 0xFFFFFF */ )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
Assert( !IsBadStringPtrW( ptr, maxchar ) );
|
||||
#else
|
||||
Assert( ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
void AppendCallStackToLogMessage( tchar *formattedMessage, int iMessageLength, int iAppendCallStackLength )
|
||||
{
|
||||
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
# if defined( TCHAR_IS_CHAR ) //I'm horrible with unicode and I don't plan on testing this with wide characters just yet
|
||||
if( iAppendCallStackLength > 0 )
|
||||
{
|
||||
int iExistingMessageLength = (int)strlen( formattedMessage ); //no V_strlen in tier 0, plus we're only compiling this for windows and 360. Seems safe
|
||||
formattedMessage += iExistingMessageLength;
|
||||
iMessageLength -= iExistingMessageLength;
|
||||
|
||||
if( iMessageLength <= 32 )
|
||||
return; //no room for anything useful
|
||||
|
||||
//append directly to the spew message
|
||||
if( (iExistingMessageLength > 0) && (formattedMessage[-1] == '\n') )
|
||||
{
|
||||
--formattedMessage;
|
||||
++iMessageLength;
|
||||
}
|
||||
|
||||
//append preface
|
||||
int iAppendedLength = _snprintf( formattedMessage, iMessageLength, _T("\nCall Stack:\n\t") );
|
||||
|
||||
void **CallStackBuffer = (void **)stackalloc( iAppendCallStackLength * sizeof( void * ) );
|
||||
int iCount = GetCallStack( CallStackBuffer, iAppendCallStackLength, 2 );
|
||||
if( TranslateStackInfo( CallStackBuffer, iCount, formattedMessage + iAppendedLength, iMessageLength - iAppendedLength, _T("\n\t") ) == 0 )
|
||||
{
|
||||
//failure
|
||||
formattedMessage[0] = '\0'; //this is pointing at where we wrote "\nCall Stack:\n\t"
|
||||
}
|
||||
else
|
||||
{
|
||||
iAppendedLength += (int)strlen( formattedMessage + iAppendedLength ); //no V_strlen in tier 0, plus we're only compiling this for windows and 360. Seems safe
|
||||
|
||||
if( iAppendedLength < iMessageLength )
|
||||
{
|
||||
formattedMessage[iAppendedLength] = '\n'; //Add another newline.
|
||||
++iAppendedLength;
|
||||
|
||||
formattedMessage[iAppendedLength] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
# else
|
||||
AssertMsg( false, "Fixme" );
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Forward declare for internal use only.
|
||||
CLoggingSystem *GetGlobalLoggingSystem();
|
||||
|
||||
#define Log_LegacyHelperColor_Stack( Channel, Severity, Color, MessageFormat, AppendCallStackLength ) \
|
||||
do \
|
||||
{ \
|
||||
CLoggingSystem *pLoggingSystem = GetGlobalLoggingSystem(); \
|
||||
if ( pLoggingSystem->IsChannelEnabled( Channel, Severity ) ) \
|
||||
{ \
|
||||
tchar formattedMessage[MAX_LOGGING_MESSAGE_LENGTH]; \
|
||||
va_list args; \
|
||||
va_start( args, MessageFormat ); \
|
||||
Tier0Internal_vsntprintf( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, MessageFormat, args ); \
|
||||
va_end( args ); \
|
||||
AppendCallStackToLogMessage( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, AppendCallStackLength ); \
|
||||
pLoggingSystem->LogDirect( Channel, Severity, Color, formattedMessage ); \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
#define Log_LegacyHelperColor( Channel, Severity, Color, MessageFormat ) Log_LegacyHelperColor_Stack( Channel, Severity, Color, MessageFormat, 0 )
|
||||
|
||||
#define Log_LegacyHelper_Stack( Channel, Severity, MessageFormat, AppendCallStackLength ) Log_LegacyHelperColor_Stack( Channel, Severity, pLoggingSystem->GetChannelColor( Channel ), MessageFormat, AppendCallStackLength )
|
||||
#define Log_LegacyHelper( Channel, Severity, MessageFormat ) Log_LegacyHelperColor( Channel, Severity, pLoggingSystem->GetChannelColor( Channel ), MessageFormat )
|
||||
|
||||
#if !defined( DBGFLAG_STRINGS_STRIP )
|
||||
|
||||
void Msg( const tchar* pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_GENERAL, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
void Warning( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper_Stack( LOG_GENERAL, LS_WARNING, pMsgFormat, AutomaticWarningCallStackLength() );
|
||||
}
|
||||
|
||||
void Warning_SpewCallStack( int iMaxCallStackLength, const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper_Stack( LOG_GENERAL, LS_WARNING, pMsgFormat, iMaxCallStackLength );
|
||||
}
|
||||
|
||||
#endif // !DBGFLAG_STRINGS_STRIP
|
||||
|
||||
void Error( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
#if !defined( DBGFLAG_STRINGS_STRIP )
|
||||
Log_LegacyHelper_Stack( LOG_GENERAL, LS_ERROR, pMsgFormat, AutomaticErrorCallStackLength() );
|
||||
// Many places that call Error assume that execution will not continue afterwards so it
|
||||
// is important to exit here. The function prototype promises that this will happen.
|
||||
Plat_ExitProcess( 100 );
|
||||
#endif
|
||||
}
|
||||
|
||||
void Error_SpewCallStack( int iMaxCallStackLength, const tchar *pMsgFormat, ... )
|
||||
{
|
||||
#if !defined( DBGFLAG_STRINGS_STRIP )
|
||||
Log_LegacyHelper_Stack( LOG_GENERAL, LS_ERROR, pMsgFormat, iMaxCallStackLength );
|
||||
// Many places that call Error_SpewCallStack assume that execution will not continue afterwards so it
|
||||
// is important to exit here. The function prototype promises that this will happen.
|
||||
Plat_ExitProcess( 100 );
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined( DBGFLAG_STRINGS_STRIP )
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A couple of super-common dynamic spew messages, here for convenience
|
||||
// These looked at the "developer" group, print if it's level 1 or higher
|
||||
//-----------------------------------------------------------------------------
|
||||
void DevMsg( int level, const tchar* pMsgFormat, ... )
|
||||
{
|
||||
LoggingChannelID_t channel = level >= 2 ? LOG_DEVELOPER_VERBOSE : LOG_DEVELOPER;
|
||||
Log_LegacyHelper( channel, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
|
||||
void DevWarning( int level, const tchar *pMsgFormat, ... )
|
||||
{
|
||||
LoggingChannelID_t channel = level >= 2 ? LOG_DEVELOPER_VERBOSE : LOG_DEVELOPER;
|
||||
Log_LegacyHelper( channel, LS_WARNING, pMsgFormat );
|
||||
}
|
||||
|
||||
void DevMsg( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_DEVELOPER, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
void DevWarning( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_DEVELOPER, LS_WARNING, pMsgFormat );
|
||||
}
|
||||
|
||||
void ConColorMsg( const Color& clr, const tchar* pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelperColor( LOG_CONSOLE, LS_MESSAGE, clr, pMsgFormat );
|
||||
}
|
||||
|
||||
void ConMsg( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_CONSOLE, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
void ConDMsg( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_DEVELOPER_CONSOLE, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
#endif // !DBGFLAG_STRINGS_STRIP
|
||||
|
||||
// If we don't have a function from math.h, then it doesn't link certain floating-point
|
||||
// functions in and printfs with %f cause runtime errors in the C libraries.
|
||||
PLATFORM_INTERFACE float CrackSmokingCompiler( float a )
|
||||
{
|
||||
return (float)fabs( a );
|
||||
}
|
||||
|
||||
void* Plat_SimpleLog( const tchar* file, int line )
|
||||
{
|
||||
FILE* f = _tfopen( _T("simple.log"), _T("at+") );
|
||||
_ftprintf( f, _T("%s:%i\n"), file, line );
|
||||
fclose( f );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if !defined( DBGFLAG_STRINGS_STRIP )
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: For debugging startup times, etc.
|
||||
// Input : *fmt -
|
||||
// ... -
|
||||
//-----------------------------------------------------------------------------
|
||||
void COM_TimestampedLog( char const *fmt, ... )
|
||||
{
|
||||
static float s_LastStamp = 0.0;
|
||||
static bool s_bShouldLog = false;
|
||||
static bool s_bShouldLogToConsole = false;
|
||||
static bool s_bShouldLogToETW = false;
|
||||
static bool s_bChecked = false;
|
||||
static bool s_bFirstWrite = false;
|
||||
|
||||
if ( !s_bChecked )
|
||||
{
|
||||
s_bShouldLog = ( CommandLine()->CheckParm( "-profile" ) ) ? true : false;
|
||||
s_bShouldLogToConsole = ( CommandLine()->ParmValue( "-profile", 0.0f ) != 0.0f ) ? true : false;
|
||||
s_bShouldLogToETW = ( CommandLine()->CheckParm( "-etwprofile" ) ) ? true : false;
|
||||
if ( s_bShouldLogToETW )
|
||||
{
|
||||
s_bShouldLog = true;
|
||||
}
|
||||
s_bChecked = true;
|
||||
}
|
||||
if ( !s_bShouldLog )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char string[1024];
|
||||
va_list argptr;
|
||||
va_start( argptr, fmt );
|
||||
Tier0Internal_vsnprintf( string, sizeof( string ), fmt, argptr );
|
||||
va_end( argptr );
|
||||
|
||||
float curStamp = Plat_FloatTime();
|
||||
|
||||
#if defined( _X360 )
|
||||
XBX_rTimeStampLog( curStamp, string );
|
||||
#elif defined( _PS3 )
|
||||
Log_Warning( LOG_LOADING, "%8.4f / %8.4f: %s\n", curStamp, curStamp - s_LastStamp, string );
|
||||
#endif
|
||||
|
||||
if ( IsPC() )
|
||||
{
|
||||
// If ETW profiling is enabled then do it only.
|
||||
if ( s_bShouldLogToETW )
|
||||
{
|
||||
ETWMark( string );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !s_bFirstWrite )
|
||||
{
|
||||
unlink( "timestamped.log" );
|
||||
s_bFirstWrite = true;
|
||||
}
|
||||
|
||||
FILE* fp = fopen( "timestamped.log", "at+" );
|
||||
fprintf( fp, "%8.4f / %8.4f: %s\n", curStamp, curStamp - s_LastStamp, string );
|
||||
fclose( fp );
|
||||
|
||||
if ( s_bShouldLogToConsole )
|
||||
{
|
||||
Msg( "%8.4f / %8.4f: %s\n", curStamp, curStamp - s_LastStamp, string );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_LastStamp = curStamp;
|
||||
}
|
||||
|
||||
#endif // !DBGFLAG_STRINGS_STRIP
|
||||
|
||||
static AssertFailedNotifyFunc_t s_AssertFailedNotifyFunc = NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sets an assert failed notify handler
|
||||
//-----------------------------------------------------------------------------
|
||||
void SetAssertFailedNotifyFunc( AssertFailedNotifyFunc_t func )
|
||||
{
|
||||
s_AssertFailedNotifyFunc = func;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calls the assert failed notify handler if one has been set
|
||||
//-----------------------------------------------------------------------------
|
||||
void CallAssertFailedNotifyFunc( const char *pchFile, int nLine, const char *pchMessage )
|
||||
{
|
||||
if ( s_AssertFailedNotifyFunc )
|
||||
s_AssertFailedNotifyFunc( pchFile, nLine, pchMessage );
|
||||
}
|
||||
|
||||
|
||||
#ifdef IS_WINDOWS_PC
|
||||
|
||||
class CHardwareBreakPoint
|
||||
{
|
||||
public:
|
||||
|
||||
enum EOpCode
|
||||
{
|
||||
BRK_SET = 0,
|
||||
BRK_UNSET,
|
||||
};
|
||||
|
||||
CHardwareBreakPoint()
|
||||
{
|
||||
m_eOperation = BRK_SET;
|
||||
m_pvAddress = 0;
|
||||
m_hThread = 0;
|
||||
m_hThreadEvent = 0;
|
||||
m_nRegister = 0;
|
||||
m_bSuccess = false;
|
||||
}
|
||||
|
||||
const void *m_pvAddress;
|
||||
HANDLE m_hThread;
|
||||
EHardwareBreakpointType m_eType;
|
||||
EHardwareBreakpointSize m_eSize;
|
||||
HANDLE m_hThreadEvent;
|
||||
int m_nRegister;
|
||||
EOpCode m_eOperation;
|
||||
bool m_bSuccess;
|
||||
|
||||
static void SetBits( DWORD_PTR& dw, int lowBit, int bits, int newValue );
|
||||
static DWORD WINAPI ThreadProc( LPVOID lpParameter );
|
||||
};
|
||||
|
||||
void CHardwareBreakPoint::SetBits( DWORD_PTR& dw, int lowBit, int bits, int newValue )
|
||||
{
|
||||
DWORD_PTR mask = (1 << bits) - 1;
|
||||
dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
|
||||
}
|
||||
|
||||
DWORD WINAPI CHardwareBreakPoint::ThreadProc( LPVOID lpParameter )
|
||||
{
|
||||
CHardwareBreakPoint *h = reinterpret_cast< CHardwareBreakPoint * >( lpParameter );
|
||||
SuspendThread( h->m_hThread );
|
||||
|
||||
// Get current context
|
||||
CONTEXT ct = {0};
|
||||
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
GetThreadContext(h->m_hThread,&ct);
|
||||
|
||||
int FlagBit = 0;
|
||||
|
||||
bool Dr0Busy = false;
|
||||
bool Dr1Busy = false;
|
||||
bool Dr2Busy = false;
|
||||
bool Dr3Busy = false;
|
||||
if (ct.Dr7 & 1)
|
||||
Dr0Busy = true;
|
||||
if (ct.Dr7 & 4)
|
||||
Dr1Busy = true;
|
||||
if (ct.Dr7 & 16)
|
||||
Dr2Busy = true;
|
||||
if (ct.Dr7 & 64)
|
||||
Dr3Busy = true;
|
||||
|
||||
if ( h->m_eOperation == CHardwareBreakPoint::BRK_UNSET )
|
||||
{
|
||||
// Remove
|
||||
if (h->m_nRegister == 0)
|
||||
{
|
||||
FlagBit = 0;
|
||||
ct.Dr0 = 0;
|
||||
Dr0Busy = false;
|
||||
}
|
||||
if (h->m_nRegister == 1)
|
||||
{
|
||||
FlagBit = 2;
|
||||
ct.Dr1 = 0;
|
||||
Dr1Busy = false;
|
||||
}
|
||||
if (h->m_nRegister == 2)
|
||||
{
|
||||
FlagBit = 4;
|
||||
ct.Dr2 = 0;
|
||||
Dr2Busy = false;
|
||||
}
|
||||
if (h->m_nRegister == 3)
|
||||
{
|
||||
FlagBit = 6;
|
||||
ct.Dr3 = 0;
|
||||
Dr3Busy = false;
|
||||
}
|
||||
ct.Dr7 &= ~(1 << FlagBit);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Dr0Busy)
|
||||
{
|
||||
h->m_nRegister = 0;
|
||||
ct.Dr0 = (DWORD_PTR)h->m_pvAddress;
|
||||
Dr0Busy = true;
|
||||
}
|
||||
else if (!Dr1Busy)
|
||||
{
|
||||
h->m_nRegister = 1;
|
||||
ct.Dr1 = (DWORD_PTR)h->m_pvAddress;
|
||||
Dr1Busy = true;
|
||||
}
|
||||
else if (!Dr2Busy)
|
||||
{
|
||||
h->m_nRegister = 2;
|
||||
ct.Dr2 = (DWORD_PTR)h->m_pvAddress;
|
||||
Dr2Busy = true;
|
||||
}
|
||||
else if (!Dr3Busy)
|
||||
{
|
||||
h->m_nRegister = 3;
|
||||
ct.Dr3 = (DWORD_PTR)h->m_pvAddress;
|
||||
Dr3Busy = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
h->m_bSuccess = false;
|
||||
ResumeThread(h->m_hThread);
|
||||
SetEvent(h->m_hThreadEvent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ct.Dr6 = 0;
|
||||
int st = 0;
|
||||
if (h->m_eType == BREAKPOINT_EXECUTE)
|
||||
st = 0;
|
||||
if (h->m_eType == BREAKPOINT_READWRITE)
|
||||
st = 3;
|
||||
if (h->m_eType == BREAKPOINT_WRITE)
|
||||
st = 1;
|
||||
|
||||
int le = 0;
|
||||
if (h->m_eSize == BREAKPOINT_SIZE_1)
|
||||
le = 0;
|
||||
if (h->m_eSize == BREAKPOINT_SIZE_2)
|
||||
le = 1;
|
||||
if (h->m_eSize == BREAKPOINT_SIZE_4)
|
||||
le = 3;
|
||||
if (h->m_eSize == BREAKPOINT_SIZE_8)
|
||||
le = 2;
|
||||
|
||||
SetBits( ct.Dr7, 16 + h->m_nRegister*4, 2, st );
|
||||
SetBits( ct.Dr7, 18 + h->m_nRegister*4, 2, le );
|
||||
SetBits( ct.Dr7, h->m_nRegister*2,1,1);
|
||||
}
|
||||
|
||||
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
SetThreadContext(h->m_hThread,&ct);
|
||||
|
||||
ResumeThread( h->m_hThread );
|
||||
h->m_bSuccess = true;
|
||||
SetEvent( h->m_hThreadEvent );
|
||||
return 0;
|
||||
}
|
||||
|
||||
HardwareBreakpointHandle_t SetHardwareBreakpoint( EHardwareBreakpointType eType, EHardwareBreakpointSize eSize, const void *pvLocation )
|
||||
{
|
||||
CHardwareBreakPoint *h = new CHardwareBreakPoint();
|
||||
h->m_pvAddress = pvLocation;
|
||||
h->m_eSize = eSize;
|
||||
h->m_eType = eType;
|
||||
HANDLE hThread = GetCurrentThread();
|
||||
h->m_hThread = hThread;
|
||||
|
||||
if ( hThread == GetCurrentThread() )
|
||||
{
|
||||
DWORD nThreadId = GetCurrentThreadId();
|
||||
h->m_hThread = OpenThread( THREAD_ALL_ACCESS, 0, nThreadId );
|
||||
}
|
||||
|
||||
h->m_hThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
h->m_eOperation = CHardwareBreakPoint::BRK_SET; // Set Break
|
||||
CreateThread( 0, 0, CHardwareBreakPoint::ThreadProc, (LPVOID)h, 0, 0 );
|
||||
WaitForSingleObject( h->m_hThreadEvent,INFINITE );
|
||||
CloseHandle( h->m_hThreadEvent );
|
||||
h->m_hThreadEvent = 0;
|
||||
if ( hThread == GetCurrentThread() )
|
||||
{
|
||||
CloseHandle( h->m_hThread );
|
||||
}
|
||||
h->m_hThread = hThread;
|
||||
if ( !h->m_bSuccess )
|
||||
{
|
||||
delete h;
|
||||
return (HardwareBreakpointHandle_t)0;
|
||||
}
|
||||
return (HardwareBreakpointHandle_t)h;
|
||||
}
|
||||
|
||||
bool ClearHardwareBreakpoint( HardwareBreakpointHandle_t handle )
|
||||
{
|
||||
CHardwareBreakPoint *h = reinterpret_cast< CHardwareBreakPoint* >( handle );
|
||||
if ( !h )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bOpened = false;
|
||||
if ( h->m_hThread == GetCurrentThread() )
|
||||
{
|
||||
DWORD nThreadId = GetCurrentThreadId();
|
||||
h->m_hThread = OpenThread( THREAD_ALL_ACCESS, 0, nThreadId );
|
||||
bOpened = true;
|
||||
}
|
||||
|
||||
h->m_hThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
h->m_eOperation = CHardwareBreakPoint::BRK_UNSET; // Remove Break
|
||||
CreateThread( 0,0,CHardwareBreakPoint::ThreadProc, (LPVOID)h, 0,0 );
|
||||
WaitForSingleObject( h->m_hThreadEvent, INFINITE );
|
||||
CloseHandle( h->m_hThreadEvent );
|
||||
h->m_hThreadEvent = 0;
|
||||
if ( bOpened )
|
||||
{
|
||||
CloseHandle( h->m_hThread );
|
||||
}
|
||||
delete h;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // IS_WINDOWS_PC
|
||||
|
||||
545
tier0/dlmalloc/malloc-2.8.3.h
Normal file
545
tier0/dlmalloc/malloc-2.8.3.h
Normal file
@@ -0,0 +1,545 @@
|
||||
/*
|
||||
Default header file for malloc-2.8.x, written by Doug Lea
|
||||
and released to the public domain, as explained at
|
||||
http://creativecommons.org/licenses/publicdomain.
|
||||
|
||||
last update: Mon Aug 15 08:55:52 2005 Doug Lea (dl at gee)
|
||||
|
||||
This header is for ANSI C/C++ only. You can set any of
|
||||
the following #defines before including:
|
||||
|
||||
* If USE_DL_PREFIX is defined, it is assumed that malloc.c
|
||||
was also compiled with this option, so all routines
|
||||
have names starting with "dl".
|
||||
|
||||
* If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this
|
||||
file will be #included after <malloc.h>. this is needed only if
|
||||
your system defines a struct mallinfo that is incompatible with the
|
||||
standard one declared here. Otherwise, you can include this file
|
||||
INSTEAD of your system system <malloc.h>. At least on ANSI, all
|
||||
declarations should be compatible with system versions
|
||||
|
||||
* If MSPACES is defined, declarations for mspace versions are included.
|
||||
*/
|
||||
|
||||
#ifndef MALLOC_280_H
|
||||
#define MALLOC_280_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
|
||||
#define USE_DL_PREFIX
|
||||
|
||||
#if !ONLY_MSPACES
|
||||
|
||||
#ifndef USE_DL_PREFIX
|
||||
#define dlcalloc calloc
|
||||
#define dlfree free
|
||||
#define dlmalloc malloc
|
||||
#define dlmemalign memalign
|
||||
#define dlrealloc realloc
|
||||
#define dlvalloc valloc
|
||||
#define dlpvalloc pvalloc
|
||||
#define dlmallinfo mallinfo
|
||||
#define dlmallopt mallopt
|
||||
#define dlmalloc_trim malloc_trim
|
||||
#define dlmalloc_stats malloc_stats
|
||||
#define dlmalloc_usable_size malloc_usable_size
|
||||
#define dlmalloc_footprint malloc_footprint
|
||||
#define dlindependent_calloc independent_calloc
|
||||
#define dlindependent_comalloc independent_comalloc
|
||||
#endif /* USE_DL_PREFIX */
|
||||
|
||||
|
||||
/*
|
||||
malloc(size_t n)
|
||||
Returns a pointer to a newly allocated chunk of at least n bytes, or
|
||||
null if no space is available, in which case errno is set to ENOMEM
|
||||
on ANSI C systems.
|
||||
|
||||
If n is zero, malloc returns a minimum-sized chunk. (The minimum
|
||||
size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
|
||||
systems.) Note that size_t is an unsigned type, so calls with
|
||||
arguments that would be negative if signed are interpreted as
|
||||
requests for huge amounts of space, which will often fail. The
|
||||
maximum supported value of n differs across systems, but is in all
|
||||
cases less than the maximum representable value of a size_t.
|
||||
*/
|
||||
void* dlmalloc(size_t);
|
||||
|
||||
/*
|
||||
free(void* p)
|
||||
Releases the chunk of memory pointed to by p, that had been previously
|
||||
allocated using malloc or a related routine such as realloc.
|
||||
It has no effect if p is null. If p was not malloced or already
|
||||
freed, free(p) will by default cuase the current program to abort.
|
||||
*/
|
||||
void dlfree(void*);
|
||||
|
||||
/*
|
||||
calloc(size_t n_elements, size_t element_size);
|
||||
Returns a pointer to n_elements * element_size bytes, with all locations
|
||||
set to zero.
|
||||
*/
|
||||
void* dlcalloc(size_t, size_t);
|
||||
|
||||
/*
|
||||
realloc(void* p, size_t n)
|
||||
Returns a pointer to a chunk of size n that contains the same data
|
||||
as does chunk p up to the minimum of (n, p's size) bytes, or null
|
||||
if no space is available.
|
||||
|
||||
The returned pointer may or may not be the same as p. The algorithm
|
||||
prefers extending p in most cases when possible, otherwise it
|
||||
employs the equivalent of a malloc-copy-free sequence.
|
||||
|
||||
If p is null, realloc is equivalent to malloc.
|
||||
|
||||
If space is not available, realloc returns null, errno is set (if on
|
||||
ANSI) and p is NOT freed.
|
||||
|
||||
if n is for fewer bytes than already held by p, the newly unused
|
||||
space is lopped off and freed if possible. realloc with a size
|
||||
argument of zero (re)allocates a minimum-sized chunk.
|
||||
|
||||
The old unix realloc convention of allowing the last-free'd chunk
|
||||
to be used as an argument to realloc is not supported.
|
||||
*/
|
||||
|
||||
void* dlrealloc(void*, size_t);
|
||||
|
||||
/*
|
||||
memalign(size_t alignment, size_t n);
|
||||
Returns a pointer to a newly allocated chunk of n bytes, aligned
|
||||
in accord with the alignment argument.
|
||||
|
||||
The alignment argument should be a power of two. If the argument is
|
||||
not a power of two, the nearest greater power is used.
|
||||
8-byte alignment is guaranteed by normal malloc calls, so don't
|
||||
bother calling memalign with an argument of 8 or less.
|
||||
|
||||
Overreliance on memalign is a sure way to fragment space.
|
||||
*/
|
||||
void* dlmemalign(size_t, size_t);
|
||||
|
||||
/*
|
||||
valloc(size_t n);
|
||||
Equivalent to memalign(pagesize, n), where pagesize is the page
|
||||
size of the system. If the pagesize is unknown, 4096 is used.
|
||||
*/
|
||||
void* dlvalloc(size_t);
|
||||
|
||||
/*
|
||||
mallopt(int parameter_number, int parameter_value)
|
||||
Sets tunable parameters The format is to provide a
|
||||
(parameter-number, parameter-value) pair. mallopt then sets the
|
||||
corresponding parameter to the argument value if it can (i.e., so
|
||||
long as the value is meaningful), and returns 1 if successful else
|
||||
0. SVID/XPG/ANSI defines four standard param numbers for mallopt,
|
||||
normally defined in malloc.h. None of these are use in this malloc,
|
||||
so setting them has no effect. But this malloc also supports other
|
||||
options in mallopt:
|
||||
|
||||
Symbol param # default allowed param values
|
||||
M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming)
|
||||
M_GRANULARITY -2 page size any power of 2 >= page size
|
||||
M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support)
|
||||
*/
|
||||
int dlmallopt(int, int);
|
||||
|
||||
#define M_TRIM_THRESHOLD (-1)
|
||||
#define M_GRANULARITY (-2)
|
||||
#define M_MMAP_THRESHOLD (-3)
|
||||
|
||||
|
||||
/*
|
||||
malloc_footprint();
|
||||
Returns the number of bytes obtained from the system. The total
|
||||
number of bytes allocated by malloc, realloc etc., is less than this
|
||||
value. Unlike mallinfo, this function returns only a precomputed
|
||||
result, so can be called frequently to monitor memory consumption.
|
||||
Even if locks are otherwise defined, this function does not use them,
|
||||
so results might not be up to date.
|
||||
*/
|
||||
size_t dlmalloc_footprint(void);
|
||||
|
||||
#if !NO_MALLINFO
|
||||
/*
|
||||
mallinfo()
|
||||
Returns (by copy) a struct containing various summary statistics:
|
||||
|
||||
arena: current total non-mmapped bytes allocated from system
|
||||
ordblks: the number of free chunks
|
||||
smblks: always zero.
|
||||
hblks: current number of mmapped regions
|
||||
hblkhd: total bytes held in mmapped regions
|
||||
usmblks: the maximum total allocated space. This will be greater
|
||||
than current total if trimming has occurred.
|
||||
fsmblks: always zero
|
||||
uordblks: current total allocated space (normal or mmapped)
|
||||
fordblks: total free space
|
||||
keepcost: the maximum number of bytes that could ideally be released
|
||||
back to system via malloc_trim. ("ideally" means that
|
||||
it ignores page restrictions etc.)
|
||||
|
||||
Because these fields are ints, but internal bookkeeping may
|
||||
be kept as longs, the reported values may wrap around zero and
|
||||
thus be inaccurate.
|
||||
*/
|
||||
#ifndef HAVE_USR_INCLUDE_MALLOC_H
|
||||
#ifndef _MALLOC_H
|
||||
#ifndef MALLINFO_FIELD_TYPE
|
||||
#define MALLINFO_FIELD_TYPE size_t
|
||||
#endif /* MALLINFO_FIELD_TYPE */
|
||||
struct mallinfo {
|
||||
MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */
|
||||
MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */
|
||||
MALLINFO_FIELD_TYPE smblks; /* always 0 */
|
||||
MALLINFO_FIELD_TYPE hblks; /* always 0 */
|
||||
MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */
|
||||
MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */
|
||||
MALLINFO_FIELD_TYPE fsmblks; /* always 0 */
|
||||
MALLINFO_FIELD_TYPE uordblks; /* total allocated space */
|
||||
MALLINFO_FIELD_TYPE fordblks; /* total free space */
|
||||
MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */
|
||||
};
|
||||
#endif /* _MALLOC_H */
|
||||
#endif /* HAVE_USR_INCLUDE_MALLOC_H */
|
||||
|
||||
struct mallinfo dlmallinfo(void);
|
||||
#endif /* NO_MALLINFO */
|
||||
|
||||
/*
|
||||
independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);
|
||||
|
||||
independent_calloc is similar to calloc, but instead of returning a
|
||||
single cleared space, it returns an array of pointers to n_elements
|
||||
independent elements that can hold contents of size elem_size, each
|
||||
of which starts out cleared, and can be independently freed,
|
||||
realloc'ed etc. The elements are guaranteed to be adjacently
|
||||
allocated (this is not guaranteed to occur with multiple callocs or
|
||||
mallocs), which may also improve cache locality in some
|
||||
applications.
|
||||
|
||||
The "chunks" argument is optional (i.e., may be null, which is
|
||||
probably the most typical usage). If it is null, the returned array
|
||||
is itself dynamically allocated and should also be freed when it is
|
||||
no longer needed. Otherwise, the chunks array must be of at least
|
||||
n_elements in length. It is filled in with the pointers to the
|
||||
chunks.
|
||||
|
||||
In either case, independent_calloc returns this pointer array, or
|
||||
null if the allocation failed. If n_elements is zero and "chunks"
|
||||
is null, it returns a chunk representing an array with zero elements
|
||||
(which should be freed if not wanted).
|
||||
|
||||
Each element must be individually freed when it is no longer
|
||||
needed. If you'd like to instead be able to free all at once, you
|
||||
should instead use regular calloc and assign pointers into this
|
||||
space to represent elements. (In this case though, you cannot
|
||||
independently free elements.)
|
||||
|
||||
independent_calloc simplifies and speeds up implementations of many
|
||||
kinds of pools. It may also be useful when constructing large data
|
||||
structures that initially have a fixed number of fixed-sized nodes,
|
||||
but the number is not known at compile time, and some of the nodes
|
||||
may later need to be freed. For example:
|
||||
|
||||
struct Node { int item; struct Node* next; };
|
||||
|
||||
struct Node* build_list() {
|
||||
struct Node** pool;
|
||||
int n = read_number_of_nodes_needed();
|
||||
if (n <= 0) return 0;
|
||||
pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
|
||||
if (pool == 0) die();
|
||||
// organize into a linked list...
|
||||
struct Node* first = pool[0];
|
||||
for (i = 0; i < n-1; ++i)
|
||||
pool[i]->next = pool[i+1];
|
||||
free(pool); // Can now free the array (or not, if it is needed later)
|
||||
return first;
|
||||
}
|
||||
*/
|
||||
void** dlindependent_calloc(size_t, size_t, void**);
|
||||
|
||||
/*
|
||||
independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
|
||||
|
||||
independent_comalloc allocates, all at once, a set of n_elements
|
||||
chunks with sizes indicated in the "sizes" array. It returns
|
||||
an array of pointers to these elements, each of which can be
|
||||
independently freed, realloc'ed etc. The elements are guaranteed to
|
||||
be adjacently allocated (this is not guaranteed to occur with
|
||||
multiple callocs or mallocs), which may also improve cache locality
|
||||
in some applications.
|
||||
|
||||
The "chunks" argument is optional (i.e., may be null). If it is null
|
||||
the returned array is itself dynamically allocated and should also
|
||||
be freed when it is no longer needed. Otherwise, the chunks array
|
||||
must be of at least n_elements in length. It is filled in with the
|
||||
pointers to the chunks.
|
||||
|
||||
In either case, independent_comalloc returns this pointer array, or
|
||||
null if the allocation failed. If n_elements is zero and chunks is
|
||||
null, it returns a chunk representing an array with zero elements
|
||||
(which should be freed if not wanted).
|
||||
|
||||
Each element must be individually freed when it is no longer
|
||||
needed. If you'd like to instead be able to free all at once, you
|
||||
should instead use a single regular malloc, and assign pointers at
|
||||
particular offsets in the aggregate space. (In this case though, you
|
||||
cannot independently free elements.)
|
||||
|
||||
independent_comallac differs from independent_calloc in that each
|
||||
element may have a different size, and also that it does not
|
||||
automatically clear elements.
|
||||
|
||||
independent_comalloc can be used to speed up allocation in cases
|
||||
where several structs or objects must always be allocated at the
|
||||
same time. For example:
|
||||
|
||||
struct Head { ... }
|
||||
struct Foot { ... }
|
||||
|
||||
void send_message(char* msg) {
|
||||
int msglen = strlen(msg);
|
||||
size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
|
||||
void* chunks[3];
|
||||
if (independent_comalloc(3, sizes, chunks) == 0)
|
||||
die();
|
||||
struct Head* head = (struct Head*)(chunks[0]);
|
||||
char* body = (char*)(chunks[1]);
|
||||
struct Foot* foot = (struct Foot*)(chunks[2]);
|
||||
// ...
|
||||
}
|
||||
|
||||
In general though, independent_comalloc is worth using only for
|
||||
larger values of n_elements. For small values, you probably won't
|
||||
detect enough difference from series of malloc calls to bother.
|
||||
|
||||
Overuse of independent_comalloc can increase overall memory usage,
|
||||
since it cannot reuse existing noncontiguous small chunks that
|
||||
might be available for some of the elements.
|
||||
*/
|
||||
void** dlindependent_comalloc(size_t, size_t*, void**);
|
||||
|
||||
|
||||
/*
|
||||
pvalloc(size_t n);
|
||||
Equivalent to valloc(minimum-page-that-holds(n)), that is,
|
||||
round up n to nearest pagesize.
|
||||
*/
|
||||
void* dlpvalloc(size_t);
|
||||
|
||||
/*
|
||||
malloc_trim(size_t pad);
|
||||
|
||||
If possible, gives memory back to the system (via negative arguments
|
||||
to sbrk) if there is unused memory at the `high' end of the malloc
|
||||
pool or in unused MMAP segments. You can call this after freeing
|
||||
large blocks of memory to potentially reduce the system-level memory
|
||||
requirements of a program. However, it cannot guarantee to reduce
|
||||
memory. Under some allocation patterns, some large free blocks of
|
||||
memory will be locked between two used chunks, so they cannot be
|
||||
given back to the system.
|
||||
|
||||
The `pad' argument to malloc_trim represents the amount of free
|
||||
trailing space to leave untrimmed. If this argument is zero, only
|
||||
the minimum amount of memory to maintain internal data structures
|
||||
will be left. Non-zero arguments can be supplied to maintain enough
|
||||
trailing space to service future expected allocations without having
|
||||
to re-obtain memory from the system.
|
||||
|
||||
Malloc_trim returns 1 if it actually released any memory, else 0.
|
||||
*/
|
||||
int dlmalloc_trim(size_t);
|
||||
|
||||
/*
|
||||
malloc_usable_size(void* p);
|
||||
|
||||
Returns the number of bytes you can actually use in
|
||||
an allocated chunk, which may be more than you requested (although
|
||||
often not) due to alignment and minimum size constraints.
|
||||
You can use this many bytes without worrying about
|
||||
overwriting other allocated objects. This is not a particularly great
|
||||
programming practice. malloc_usable_size can be more useful in
|
||||
debugging and assertions, for example:
|
||||
|
||||
p = malloc(n);
|
||||
assert(malloc_usable_size(p) >= 256);
|
||||
*/
|
||||
size_t dlmalloc_usable_size(void*);
|
||||
|
||||
/*
|
||||
malloc_stats();
|
||||
Prints on stderr the amount of space obtained from the system (both
|
||||
via sbrk and mmap), the maximum amount (which may be more than
|
||||
current if malloc_trim and/or munmap got called), and the current
|
||||
number of bytes allocated via malloc (or realloc, etc) but not yet
|
||||
freed. Note that this is the number of bytes allocated, not the
|
||||
number requested. It will be larger than the number requested
|
||||
because of alignment and bookkeeping overhead. Because it includes
|
||||
alignment wastage as being in use, this figure may be greater than
|
||||
zero even when no user-level chunks are allocated.
|
||||
|
||||
The reported current and maximum system memory can be inaccurate if
|
||||
a program makes other calls to system memory allocation functions
|
||||
(normally sbrk) outside of malloc.
|
||||
|
||||
malloc_stats prints only the most commonly interesting statistics.
|
||||
More information can be obtained by calling mallinfo.
|
||||
*/
|
||||
void dlmalloc_stats(void);
|
||||
|
||||
#endif /* !ONLY_MSPACES */
|
||||
|
||||
#if MSPACES
|
||||
|
||||
/*
|
||||
mspace is an opaque type representing an independent
|
||||
region of space that supports mspace_malloc, etc.
|
||||
*/
|
||||
typedef void* mspace;
|
||||
|
||||
void *global_mspace();
|
||||
void *determine_mspace(void *mem);
|
||||
|
||||
/*
|
||||
create_mspace creates and returns a new independent space with the
|
||||
given initial capacity, or, if 0, the default granularity size. It
|
||||
returns null if there is no system memory available to create the
|
||||
space. If argument locked is non-zero, the space uses a separate
|
||||
lock to control access. The capacity of the space will grow
|
||||
dynamically as needed to service mspace_malloc requests. You can
|
||||
control the sizes of incremental increases of this space by
|
||||
compiling with a different DEFAULT_GRANULARITY or dynamically
|
||||
setting with mallopt(M_GRANULARITY, value).
|
||||
*/
|
||||
mspace create_mspace(size_t capacity, int locked);
|
||||
|
||||
/*
|
||||
destroy_mspace destroys the given space, and attempts to return all
|
||||
of its memory back to the system, returning the total number of
|
||||
bytes freed. After destruction, the results of access to all memory
|
||||
used by the space become undefined.
|
||||
*/
|
||||
size_t destroy_mspace(mspace msp);
|
||||
|
||||
/*
|
||||
create_mspace_with_base uses the memory supplied as the initial base
|
||||
of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
|
||||
space is used for bookkeeping, so the capacity must be at least this
|
||||
large. (Otherwise 0 is returned.) When this initial space is
|
||||
exhausted, additional memory will be obtained from the system.
|
||||
Destroying this space will deallocate all additionally allocated
|
||||
space (if possible) but not the initial base.
|
||||
*/
|
||||
mspace create_mspace_with_base(void* base, size_t capacity, int locked);
|
||||
|
||||
/*
|
||||
mspace_malloc behaves as malloc, but operates within
|
||||
the given space.
|
||||
*/
|
||||
void* mspace_malloc(mspace msp, size_t bytes);
|
||||
|
||||
/*
|
||||
mspace_free behaves as free, but operates within
|
||||
the given space.
|
||||
|
||||
If compiled with FOOTERS==1, mspace_free is not actually needed.
|
||||
free may be called instead of mspace_free because freed chunks from
|
||||
any space are handled by their originating spaces.
|
||||
*/
|
||||
void mspace_free(mspace msp, void* mem);
|
||||
|
||||
/*
|
||||
mspace_realloc behaves as realloc, but operates within
|
||||
the given space.
|
||||
|
||||
If compiled with FOOTERS==1, mspace_realloc is not actually
|
||||
needed. realloc may be called instead of mspace_realloc because
|
||||
realloced chunks from any space are handled by their originating
|
||||
spaces.
|
||||
*/
|
||||
void* mspace_realloc(mspace msp, void* mem, size_t newsize);
|
||||
|
||||
/*
|
||||
mspace_calloc behaves as calloc, but operates within
|
||||
the given space.
|
||||
*/
|
||||
void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
|
||||
|
||||
/*
|
||||
mspace_memalign behaves as memalign, but operates within
|
||||
the given space.
|
||||
*/
|
||||
void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
|
||||
|
||||
/*
|
||||
mspace_independent_calloc behaves as independent_calloc, but
|
||||
operates within the given space.
|
||||
*/
|
||||
void** mspace_independent_calloc(mspace msp, size_t n_elements,
|
||||
size_t elem_size, void* chunks[]);
|
||||
|
||||
/*
|
||||
mspace_independent_comalloc behaves as independent_comalloc, but
|
||||
operates within the given space.
|
||||
*/
|
||||
void** mspace_independent_comalloc(mspace msp, size_t n_elements,
|
||||
size_t sizes[], void* chunks[]);
|
||||
|
||||
/*
|
||||
mspace_footprint() returns the number of bytes obtained from the
|
||||
system for this space.
|
||||
*/
|
||||
size_t mspace_footprint(mspace msp);
|
||||
|
||||
/*
|
||||
mspace_max_footprint() returns the peak number of bytes obtained from the
|
||||
system for this space.
|
||||
*/
|
||||
size_t mspace_max_footprint(mspace msp);
|
||||
|
||||
|
||||
#if !NO_MALLINFO
|
||||
/*
|
||||
mspace_mallinfo behaves as mallinfo, but reports properties of
|
||||
the given space.
|
||||
*/
|
||||
struct mallinfo mspace_mallinfo(mspace msp);
|
||||
#endif /* NO_MALLINFO */
|
||||
|
||||
/*
|
||||
mspace_malloc_stats behaves as malloc_stats, but reports
|
||||
properties of the given space.
|
||||
*/
|
||||
void mspace_malloc_stats(mspace msp);
|
||||
|
||||
/*
|
||||
mspace_trim behaves as malloc_trim, but
|
||||
operates within the given space.
|
||||
*/
|
||||
int mspace_trim(mspace msp, size_t pad);
|
||||
|
||||
/*
|
||||
An alias for malloc_usable_size.
|
||||
*/
|
||||
size_t mspace_usable_size(void *mem);
|
||||
|
||||
/*
|
||||
An alias for mallopt.
|
||||
*/
|
||||
int mspace_mallopt(int, int);
|
||||
|
||||
#endif /* MSPACES */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}; /* end of extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* MALLOC_280_H */
|
||||
5652
tier0/dlmalloc/malloc.cpp
Normal file
5652
tier0/dlmalloc/malloc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
151
tier0/dynfunction.cpp
Normal file
151
tier0/dynfunction.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Shared library loading and symbol lookup.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/dynfunction.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef HMODULE LibraryHandle;
|
||||
#define LoadLibraryHandle(libname) LoadLibrary(libname)
|
||||
#define CloseLibraryHandle(handle) FreeLibrary(handle)
|
||||
#define LookupInLibraryHandle(handle, fn) GetProcAddress(handle, fn)
|
||||
#elif defined(POSIX)
|
||||
#include <dlfcn.h>
|
||||
typedef void *LibraryHandle;
|
||||
#define LoadLibraryHandle(libname) dlopen(libname, RTLD_NOW)
|
||||
#define CloseLibraryHandle(handle) dlclose(handle)
|
||||
#define LookupInLibraryHandle(handle, fn) dlsym(handle, fn)
|
||||
#else
|
||||
#error Please define your platform.
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG
|
||||
static inline void dbgdynfn(const char *fmt, ...) {}
|
||||
#else
|
||||
#define dbgdynfn printf
|
||||
#endif
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
class CSharedLibraryCache
|
||||
{
|
||||
public:
|
||||
static CSharedLibraryCache &GetCache()
|
||||
{
|
||||
static CSharedLibraryCache Singleton;
|
||||
return Singleton;
|
||||
}
|
||||
|
||||
struct CSharedLibraryItem
|
||||
{
|
||||
CSharedLibraryItem(LibraryHandle handle, const char *name)
|
||||
{
|
||||
m_handle = handle;
|
||||
m_name = new char[strlen(name) + 1];
|
||||
m_next = NULL;
|
||||
strcpy(m_name, name);
|
||||
}
|
||||
|
||||
~CSharedLibraryItem()
|
||||
{
|
||||
dbgdynfn("CDynamicFunction: Closing library '%s' (%p)\n", m_name, (void *) m_handle);
|
||||
CloseLibraryHandle(m_handle);
|
||||
delete[] m_name;
|
||||
delete m_next;
|
||||
}
|
||||
|
||||
char *m_name;
|
||||
CSharedLibraryItem *m_next;
|
||||
LibraryHandle m_handle;
|
||||
};
|
||||
|
||||
CSharedLibraryCache() : m_pList(NULL) {}
|
||||
~CSharedLibraryCache() { CloseAllLibraries(); }
|
||||
|
||||
LibraryHandle GetHandle(const char *name)
|
||||
{
|
||||
CSharedLibraryItem *item = GetCacheItem(name);
|
||||
if (item == NULL)
|
||||
{
|
||||
LibraryHandle lib = LoadLibraryHandle(name);
|
||||
dbgdynfn("CDynamicFunction: Loading library '%s' (%p)\n", name, (void *) lib);
|
||||
if (lib == NULL)
|
||||
return NULL;
|
||||
|
||||
item = new CSharedLibraryItem(lib, name);
|
||||
item->m_next = m_pList;
|
||||
m_pList = item;
|
||||
}
|
||||
return item->m_handle;
|
||||
}
|
||||
|
||||
void CloseLibrary(const char *name)
|
||||
{
|
||||
CSharedLibraryItem *item = GetCacheItem(name);
|
||||
if (item)
|
||||
{
|
||||
assert(item == m_pList);
|
||||
m_pList = item->m_next;
|
||||
item->m_next = NULL;
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void CloseAllLibraries()
|
||||
{
|
||||
delete m_pList;
|
||||
}
|
||||
|
||||
private:
|
||||
CSharedLibraryItem *GetCacheItem(const char *name)
|
||||
{
|
||||
CSharedLibraryItem *prev = NULL;
|
||||
CSharedLibraryItem *item = m_pList;
|
||||
while (item)
|
||||
{
|
||||
if (strcmp(item->m_name, name) == 0)
|
||||
{
|
||||
// move this item to the front of the list, since there will
|
||||
// probably be a big pile of these lookups in a row
|
||||
// and then none ever again.
|
||||
if (prev != NULL)
|
||||
{
|
||||
prev->m_next = item->m_next;
|
||||
item->m_next = m_pList;
|
||||
m_pList = item;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
prev = item;
|
||||
item = item->m_next;
|
||||
}
|
||||
return NULL; // not found.
|
||||
}
|
||||
|
||||
CSharedLibraryItem *m_pList;
|
||||
};
|
||||
|
||||
void *VoidFnPtrLookup_Tier0(const char *libname, const char *fn, void *fallback)
|
||||
{
|
||||
LibraryHandle lib = CSharedLibraryCache::GetCache().GetHandle(libname);
|
||||
void *retval = NULL;
|
||||
if (lib != NULL)
|
||||
{
|
||||
retval = LookupInLibraryHandle(lib, fn);
|
||||
dbgdynfn("CDynamicFunction: Lookup of '%s' in '%s': %p\n", fn, libname, retval);
|
||||
}
|
||||
|
||||
if (retval == NULL)
|
||||
retval = fallback;
|
||||
return retval;
|
||||
}
|
||||
|
||||
413
tier0/etwprof.cpp
Normal file
413
tier0/etwprof.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
||||
//
|
||||
// ETW (Event Tracing for Windows) profiling helpers.
|
||||
// This allows easy insertion of Generic Event markers into ETW/xperf tracing
|
||||
// which then aids in analyzing the traces and finding performance problems.
|
||||
//
|
||||
//===============================================================================
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/etwprof.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef ETW_MARKS_ENABLED
|
||||
|
||||
// After building the DLL if it has never been registered on this machine or
|
||||
// if the providers have changed you need to go:
|
||||
// xcopy /y %vgame%\bin\tier0.dll %temp%
|
||||
// wevtutil um %vgame%\..\src\tier0\ValveETWProvider.man
|
||||
// wevtutil im %vgame%\..\src\tier0\ValveETWProvider.man
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
// These are defined in evntrace.h but you need a Vista+ Windows
|
||||
// SDK to have them available, so I define them here.
|
||||
#define EVENT_CONTROL_CODE_DISABLE_PROVIDER 0
|
||||
#define EVENT_CONTROL_CODE_ENABLE_PROVIDER 1
|
||||
#define EVENT_CONTROL_CODE_CAPTURE_STATE 2
|
||||
|
||||
// EVNTAPI is used in evntprov.h which is included by ValveETWProviderEvents.h
|
||||
// We define EVNTAPI without the DECLSPEC_IMPORT specifier so that
|
||||
// we can implement these functions locally instead of using the import library,
|
||||
// and can therefore still run on Windows XP.
|
||||
#define EVNTAPI __stdcall
|
||||
// Include the event register/write/unregister macros compiled from the manifest file.
|
||||
// Note that this includes evntprov.h which requires a Vista+ Windows SDK
|
||||
// which we don't currently have, so evntprov.h is checked in.
|
||||
#include "ValveETWProviderEvents.h"
|
||||
|
||||
// Typedefs for use with GetProcAddress
|
||||
typedef ULONG (__stdcall *tEventRegister)( LPCGUID ProviderId, PENABLECALLBACK EnableCallback, PVOID CallbackContext, PREGHANDLE RegHandle);
|
||||
typedef ULONG (__stdcall *tEventWrite)( REGHANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData);
|
||||
typedef ULONG (__stdcall *tEventUnregister)( REGHANDLE RegHandle );
|
||||
|
||||
// Helper class to dynamically load Advapi32.dll, find the ETW functions,
|
||||
// register the providers if possible, and get the performance counter frequency.
|
||||
class CETWRegister
|
||||
{
|
||||
public:
|
||||
CETWRegister()
|
||||
{
|
||||
QueryPerformanceFrequency( &m_frequency );
|
||||
|
||||
// Find Advapi32.dll. This should always succeed.
|
||||
HMODULE pAdvapiDLL = LoadLibraryW( L"Advapi32.dll" );
|
||||
if ( pAdvapiDLL )
|
||||
{
|
||||
// Try to find the ETW functions. This will fail on XP.
|
||||
m_pEventRegister = ( tEventRegister )GetProcAddress( pAdvapiDLL, "EventRegister" );
|
||||
m_pEventWrite = ( tEventWrite )GetProcAddress( pAdvapiDLL, "EventWrite" );
|
||||
m_pEventUnregister = ( tEventUnregister )GetProcAddress( pAdvapiDLL, "EventUnregister" );
|
||||
|
||||
// Register two ETW providers. If registration fails then the event logging calls will fail.
|
||||
// On XP these calls will do nothing.
|
||||
// On Vista and above, if these providers have been enabled by xperf or logman then
|
||||
// the VALVE_FRAMERATE_Context and VALVE_MAIN_Context globals will be modified
|
||||
// like this:
|
||||
// MatchAnyKeyword: 0xffffffffffffffff
|
||||
// IsEnabled: 1
|
||||
// Level: 255
|
||||
// In other words, fully enabled.
|
||||
|
||||
EventRegisterValve_FrameRate();
|
||||
EventRegisterValve_ServerFrameRate();
|
||||
EventRegisterValve_Main();
|
||||
EventRegisterValve_Input();
|
||||
EventRegisterValve_Network();
|
||||
|
||||
// Emit the thread ID for the main thread. This also indicates that
|
||||
// the main provider is initialized.
|
||||
EventWriteThread_ID( GetCurrentThreadId(), "Main thread" );
|
||||
// Emit an input system event so we know that it is active.
|
||||
EventWriteKey_down( "Valve input provider initialized.", 0, 0 );
|
||||
}
|
||||
}
|
||||
~CETWRegister()
|
||||
{
|
||||
// Unregister our providers.
|
||||
EventUnregisterValve_Network();
|
||||
EventUnregisterValve_Input();
|
||||
EventUnregisterValve_Main();
|
||||
EventUnregisterValve_ServerFrameRate();
|
||||
EventUnregisterValve_FrameRate();
|
||||
}
|
||||
|
||||
tEventRegister m_pEventRegister;
|
||||
tEventWrite m_pEventWrite;
|
||||
tEventUnregister m_pEventUnregister;
|
||||
|
||||
// QPC frequency
|
||||
LARGE_INTEGER m_frequency;
|
||||
|
||||
} g_ETWRegister;
|
||||
|
||||
// Redirector function for EventRegister. Called by macros in ValveETWProviderEvents.h
|
||||
ULONG EVNTAPI EventRegister( LPCGUID ProviderId, PENABLECALLBACK EnableCallback, PVOID CallbackContext, PREGHANDLE RegHandle )
|
||||
{
|
||||
if ( g_ETWRegister.m_pEventRegister )
|
||||
return g_ETWRegister.m_pEventRegister( ProviderId, EnableCallback, CallbackContext, RegHandle );
|
||||
|
||||
// We are contractually obliged to initialize this.
|
||||
*RegHandle = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Redirector function for EventWrite. Called by macros in ValveETWProviderEvents.h
|
||||
ULONG EVNTAPI EventWrite( REGHANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData )
|
||||
{
|
||||
if ( g_ETWRegister.m_pEventWrite )
|
||||
return g_ETWRegister.m_pEventWrite( RegHandle, EventDescriptor, UserDataCount, UserData );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Redirector function for EventUnregister. Called by macros in ValveETWProviderEvents.h
|
||||
ULONG EVNTAPI EventUnregister( REGHANDLE RegHandle )
|
||||
{
|
||||
if ( g_ETWRegister.m_pEventUnregister )
|
||||
return g_ETWRegister.m_pEventUnregister( RegHandle );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Call QueryPerformanceCounter
|
||||
static int64 GetQPCTime()
|
||||
{
|
||||
LARGE_INTEGER time;
|
||||
|
||||
QueryPerformanceCounter( &time );
|
||||
return time.QuadPart;
|
||||
}
|
||||
|
||||
// Convert a QueryPerformanceCounter delta into milliseconds
|
||||
static float QPCToMS( int64 nDelta )
|
||||
{
|
||||
// Convert from a QPC delta to seconds.
|
||||
float flSeconds = ( float )( nDelta / double( g_ETWRegister.m_frequency.QuadPart ) );
|
||||
|
||||
// Convert from seconds to milliseconds
|
||||
return flSeconds * 1000;
|
||||
}
|
||||
|
||||
// Public functions for emitting ETW events.
|
||||
|
||||
bool ETWIsTracingEnabled()
|
||||
{
|
||||
if ( VALVE_MAIN_Context.IsEnabled )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int64 ETWMark( const char *pMessage )
|
||||
{
|
||||
int64 nTime = GetQPCTime();
|
||||
EventWriteMark( pMessage );
|
||||
return nTime;
|
||||
}
|
||||
|
||||
void ETWMarkPrintf( const char *pMessage, ... )
|
||||
{
|
||||
// If we are running on Windows XP or if our providers have not been enabled
|
||||
// (by xperf or other) then this will be false and we can early out.
|
||||
// Be sure to check the appropriate context for the event. This is only
|
||||
// worth checking if there is some cost beyond the EventWrite that we can
|
||||
// avoid -- the redirectors in this file guarantee that EventWrite is always
|
||||
// safe to call.
|
||||
if ( !VALVE_MAIN_Context.IsEnabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[1000];
|
||||
va_list args;
|
||||
va_start( args, pMessage );
|
||||
vsprintf_s( buffer, pMessage, args );
|
||||
va_end( args );
|
||||
|
||||
EventWriteMark( buffer );
|
||||
}
|
||||
|
||||
void ETWMark1F( const char *pMessage, float data1 )
|
||||
{
|
||||
EventWriteMark1F( pMessage, data1 );
|
||||
}
|
||||
|
||||
void ETWMark2F( const char *pMessage, float data1, float data2 )
|
||||
{
|
||||
EventWriteMark2F( pMessage, data1, data2 );
|
||||
}
|
||||
|
||||
void ETWMark3F( const char *pMessage, float data1, float data2, float data3 )
|
||||
{
|
||||
EventWriteMark3F( pMessage, data1, data2, data3 );
|
||||
}
|
||||
|
||||
void ETWMark4F( const char *pMessage, float data1, float data2, float data3, float data4 )
|
||||
{
|
||||
EventWriteMark4F( pMessage, data1, data2, data3, data4 );
|
||||
}
|
||||
|
||||
void ETWMark1I( const char *pMessage, int data1 )
|
||||
{
|
||||
EventWriteMark1I( pMessage, data1 );
|
||||
}
|
||||
|
||||
void ETWMark2I( const char *pMessage, int data1, int data2 )
|
||||
{
|
||||
EventWriteMark2I( pMessage, data1, data2 );
|
||||
}
|
||||
|
||||
void ETWMark3I( const char *pMessage, int data1, int data2, int data3 )
|
||||
{
|
||||
EventWriteMark3I( pMessage, data1, data2, data3 );
|
||||
}
|
||||
|
||||
void ETWMark4I( const char *pMessage, int data1, int data2, int data3, int data4 )
|
||||
{
|
||||
EventWriteMark4I( pMessage, data1, data2, data3, data4 );
|
||||
}
|
||||
|
||||
void ETWMark1S( const char *pMessage, const char* data1 )
|
||||
{
|
||||
EventWriteMark1S( pMessage, data1 );
|
||||
}
|
||||
|
||||
void ETWMark2S( const char *pMessage, const char* data1, const char* data2 )
|
||||
{
|
||||
EventWriteMark2S( pMessage, data1, data2 );
|
||||
}
|
||||
|
||||
// Track the depth of ETW Begin/End pairs. This needs to be per-thread
|
||||
// if we start emitting marks on multiple threads. Using __declspec(thread)
|
||||
// has some problems on Windows XP, but since these ETW functions only work
|
||||
// on Vista+ that doesn't matter.
|
||||
static __declspec( thread ) int s_nDepth;
|
||||
|
||||
int64 ETWBegin( const char *pMessage )
|
||||
{
|
||||
// If we are running on Windows XP or if our providers have not been enabled
|
||||
// (by xperf or other) then this will be false and we can early out.
|
||||
// Be sure to check the appropriate context for the event. This is only
|
||||
// worth checking if there is some cost beyond the EventWrite that we can
|
||||
// avoid -- the redirectors in this file guarantee that EventWrite is always
|
||||
// safe to call.
|
||||
// In this case we also avoid the potentially unreliable TLS implementation
|
||||
// (for dynamically loaded DLLs) on Windows XP.
|
||||
if ( !VALVE_MAIN_Context.IsEnabled )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64 nTime = GetQPCTime();
|
||||
EventWriteStart( pMessage, s_nDepth++ );
|
||||
return nTime;
|
||||
}
|
||||
|
||||
int64 ETWEnd( const char *pMessage, int64 nStartTime )
|
||||
{
|
||||
// If we are running on Windows XP or if our providers have not been enabled
|
||||
// (by xperf or other) then this will be false and we can early out.
|
||||
// Be sure to check the appropriate context for the event. This is only
|
||||
// worth checking if there is some cost beyond the EventWrite that we can
|
||||
// avoid -- the redirectors in this file guarantee that EventWrite is always
|
||||
// safe to call.
|
||||
// In this case we also avoid the potentially unreliable TLS implementation
|
||||
// (for dynamically loaded DLLs) on Windows XP.
|
||||
if ( !VALVE_MAIN_Context.IsEnabled )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64 nTime = GetQPCTime();
|
||||
EventWriteStop( pMessage, --s_nDepth, QPCToMS( nTime - nStartTime ) );
|
||||
return nTime;
|
||||
}
|
||||
|
||||
// Record server and client frame counts separately, in case they are
|
||||
// in the same process.
|
||||
static int s_nRenderFrameCount[2];
|
||||
|
||||
int ETWGetRenderFrameNumber()
|
||||
{
|
||||
return s_nRenderFrameCount[0];
|
||||
}
|
||||
|
||||
// Insert a render frame marker using the Valve-FrameRate provider. Automatically
|
||||
// count the frame number and frame time.
|
||||
void ETWRenderFrameMark( bool bIsServerProcess )
|
||||
{
|
||||
Assert( bIsServerProcess == false || bIsServerProcess == true );
|
||||
// Record server and client frame counts separately, in case they are
|
||||
// in the same process.
|
||||
static int64 s_lastFrameTime[2];
|
||||
|
||||
int64 nCurrentFrameTime = GetQPCTime();
|
||||
float flElapsedFrameTime = 0.0f;
|
||||
if ( s_nRenderFrameCount[bIsServerProcess] )
|
||||
{
|
||||
flElapsedFrameTime = QPCToMS( nCurrentFrameTime - s_lastFrameTime[bIsServerProcess] );
|
||||
}
|
||||
|
||||
if ( bIsServerProcess )
|
||||
{
|
||||
EventWriteServerRenderFrameMark( s_nRenderFrameCount[bIsServerProcess], flElapsedFrameTime );
|
||||
}
|
||||
else
|
||||
{
|
||||
EventWriteRenderFrameMark( s_nRenderFrameCount[bIsServerProcess], flElapsedFrameTime );
|
||||
}
|
||||
|
||||
++s_nRenderFrameCount[bIsServerProcess];
|
||||
s_lastFrameTime[bIsServerProcess] = nCurrentFrameTime;
|
||||
}
|
||||
|
||||
// Insert a simulation frame marker using the Valve-FrameRate provider. Automatically
|
||||
// count the frame number and frame time.
|
||||
void ETWSimFrameMark( bool bIsServerProcess )
|
||||
{
|
||||
Assert( bIsServerProcess == false || bIsServerProcess == true );
|
||||
// Record server and client frame counts separately, in case they are
|
||||
// in the same process.
|
||||
static int s_nFrameCount[2];
|
||||
static int64 s_lastFrameTime[2];
|
||||
|
||||
int64 nCurrentFrameTime = GetQPCTime();
|
||||
float flElapsedFrameTime = 0.0f;
|
||||
if ( s_nFrameCount[bIsServerProcess] )
|
||||
{
|
||||
flElapsedFrameTime = QPCToMS( nCurrentFrameTime - s_lastFrameTime[bIsServerProcess] );
|
||||
}
|
||||
|
||||
if ( bIsServerProcess )
|
||||
{
|
||||
EventWriteServerSimFrameMark( s_nFrameCount[bIsServerProcess], flElapsedFrameTime );
|
||||
}
|
||||
else
|
||||
{
|
||||
EventWriteSimFrameMark( s_nFrameCount[bIsServerProcess], flElapsedFrameTime );
|
||||
}
|
||||
|
||||
++s_nFrameCount[bIsServerProcess];
|
||||
s_lastFrameTime[bIsServerProcess] = nCurrentFrameTime;
|
||||
}
|
||||
|
||||
void ETWMouseDown( int whichButton, int x, int y )
|
||||
{
|
||||
// Always have x/y first to make the summary tables easier to read.
|
||||
EventWriteMouse_down( x, y, whichButton );
|
||||
}
|
||||
|
||||
void ETWMouseUp( int whichButton, int x, int y )
|
||||
{
|
||||
// Always have x/y first to make the summary tables easier to read.
|
||||
EventWriteMouse_up( x, y, whichButton );
|
||||
}
|
||||
|
||||
void ETWMouseMove( int nX, int nY )
|
||||
{
|
||||
static int lastX, lastY;
|
||||
|
||||
// Only emit mouse-move events if the mouse position has changed, since
|
||||
// otherwise source2 emits a continous stream of events which makes it
|
||||
// harder to find 'real' mouse-move events.
|
||||
if ( lastX != nX || lastY != nY )
|
||||
{
|
||||
lastX = nX;
|
||||
lastY = nY;
|
||||
// Always have x/y first to make the summary tables easier to read.
|
||||
EventWriteMouse_Move( nX, nY );
|
||||
}
|
||||
}
|
||||
|
||||
void ETWMouseWheel( int nWheelDelta, int nX, int nY )
|
||||
{
|
||||
// Always have x/y first to make the summary tables easier to read.
|
||||
EventWriteMouse_Wheel( nX, nY, nWheelDelta );
|
||||
}
|
||||
|
||||
void ETWKeyDown( int nScanCode, int nVirtualCode, const char *pChar )
|
||||
{
|
||||
EventWriteKey_down( pChar, nScanCode, nVirtualCode );
|
||||
}
|
||||
|
||||
void ETWSendPacket( const char *pTo, int nWireSize, int nOutSequenceNR, int nOutSequenceNrAck )
|
||||
{
|
||||
static int s_nCumulativeWireSize;
|
||||
s_nCumulativeWireSize += nWireSize;
|
||||
|
||||
EventWriteSendPacket( pTo, nWireSize, nOutSequenceNR, nOutSequenceNrAck, s_nCumulativeWireSize );
|
||||
}
|
||||
|
||||
void ETWThrottled()
|
||||
{
|
||||
EventWriteThrottled();
|
||||
}
|
||||
|
||||
void ETWReadPacket( const char *pFrom, int nWireSize, int nInSequenceNR, int nOutSequenceNRAck )
|
||||
{
|
||||
static int s_nCumulativeWireSize;
|
||||
s_nCumulativeWireSize += nWireSize;
|
||||
|
||||
EventWriteReadPacket( pFrom, nWireSize, nInSequenceNR, nOutSequenceNRAck, s_nCumulativeWireSize );
|
||||
}
|
||||
|
||||
#endif // ETW_MARKS_ENABLED
|
||||
23
tier0/fasttimer.cpp
Normal file
23
tier0/fasttimer.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tier0/fasttimer.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
uint64 g_ClockSpeed; // Clocks/sec
|
||||
unsigned long g_dwClockSpeed;
|
||||
double g_ClockSpeedMicrosecondsMultiplier;
|
||||
double g_ClockSpeedMillisecondsMultiplier;
|
||||
double g_ClockSpeedSecondsMultiplier;
|
||||
|
||||
// Constructor init the clock speed.
|
||||
CClockSpeedInit g_ClockSpeedInit;
|
||||
695
tier0/logging.cpp
Normal file
695
tier0/logging.cpp
Normal file
@@ -0,0 +1,695 @@
|
||||
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
||||
//
|
||||
// Logging system definitions.
|
||||
//
|
||||
//===============================================================================
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "logging.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "dbg.h"
|
||||
#include "threadtools.h"
|
||||
#include "tier0_strtools.h" // this is from tier1, but only included for inline definition of V_isspace
|
||||
|
||||
#ifdef _PS3
|
||||
#include <sys/tty.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define DBG_SPEW_ALL_WARNINGS_AND_ERRORS_ASSERT false
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Define commonly used channels here
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_GENERAL, "General" );
|
||||
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_ASSERT, "Assert" );
|
||||
|
||||
// Corresponds to ConMsg/ConWarning/etc. with a level <= 1.
|
||||
// Only errors are spewed by default.
|
||||
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_CONSOLE, "Console", LCF_CONSOLE_ONLY, LS_ERROR );
|
||||
ADD_LOGGING_CHANNEL_TAG( "Console" );
|
||||
END_DEFINE_LOGGING_CHANNEL();
|
||||
|
||||
// Corresponds to DevMsg/DevWarning/etc. with a level <= 1.
|
||||
// Only errors are spewed by default.
|
||||
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_DEVELOPER, "Developer", LCF_CONSOLE_ONLY, LS_ERROR );
|
||||
ADD_LOGGING_CHANNEL_TAG( "Developer" );
|
||||
END_DEFINE_LOGGING_CHANNEL();
|
||||
|
||||
// Corresponds to ConMsg/ConWarning/etc. with a level >= 2.
|
||||
// Only errors are spewed by default.
|
||||
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_DEVELOPER_CONSOLE, "DeveloperConsole", LCF_CONSOLE_ONLY, LS_ERROR );
|
||||
ADD_LOGGING_CHANNEL_TAG( "DeveloperVerbose" );
|
||||
ADD_LOGGING_CHANNEL_TAG( "Console" );
|
||||
END_DEFINE_LOGGING_CHANNEL();
|
||||
|
||||
// Corresponds to DevMsg/DevWarning/etc, with a level >= 2.
|
||||
// Only errors are spewed by default.
|
||||
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_DEVELOPER_VERBOSE, "DeveloperVerbose", LCF_CONSOLE_ONLY, LS_ERROR, Color( 192, 128, 192, 255 ) );
|
||||
ADD_LOGGING_CHANNEL_TAG( "DeveloperVerbose" );
|
||||
END_DEFINE_LOGGING_CHANNEL();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Globals
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The index of the logging state used by the current thread. This defaults to 0 across all threads,
|
||||
// which indicates that the global listener set should be used (CLoggingSystem::m_nGlobalStateIndex).
|
||||
//
|
||||
// NOTE:
|
||||
// Because our linux TLS implementation does not support embedding a thread local
|
||||
// integer in a class, the logging system must use a global thread-local integer.
|
||||
// This means that we can only have one instance of CLoggingSystem, although
|
||||
// we could support additional instances if we are willing to lose support for
|
||||
// thread-local spew handling.
|
||||
// There is no other reason why this class must be a singleton, except
|
||||
// for the fact that there's no reason to have more than one in existence.
|
||||
bool g_bEnforceLoggingSystemSingleton = false;
|
||||
|
||||
#ifdef _PS3
|
||||
#include "tls_ps3.h"
|
||||
#else // _PS3
|
||||
CTHREADLOCALINT g_nThreadLocalStateIndex;
|
||||
#endif // _PS3
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Implementation
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CLoggingSystem *g_pGlobalLoggingSystem = NULL;
|
||||
|
||||
// This function does not get inlined due to the static variable :(
|
||||
CLoggingSystem *GetGlobalLoggingSystem_Internal()
|
||||
{
|
||||
static CLoggingSystem globalLoggingSystem;
|
||||
g_pGlobalLoggingSystem = &globalLoggingSystem;
|
||||
return &globalLoggingSystem;
|
||||
}
|
||||
|
||||
// This function can get inlined
|
||||
CLoggingSystem *GetGlobalLoggingSystem()
|
||||
{
|
||||
return ( g_pGlobalLoggingSystem == NULL ) ? GetGlobalLoggingSystem_Internal() : g_pGlobalLoggingSystem;
|
||||
}
|
||||
|
||||
CLoggingSystem::CLoggingSystem() :
|
||||
m_nChannelCount( 0 ),
|
||||
m_nChannelTagCount( 0 ),
|
||||
m_nTagNamePoolIndex( 0 ),
|
||||
m_nGlobalStateIndex( 0 )
|
||||
{
|
||||
Assert( !g_bEnforceLoggingSystemSingleton );
|
||||
g_bEnforceLoggingSystemSingleton = true;
|
||||
#if !defined( _PS3 ) && !defined(POSIX) && !defined(PLATFORM_WINDOWS)
|
||||
// Due to uncertain constructor ordering (g_nThreadLocalStateIndex
|
||||
// may not be constructed yet so TLS index may not be available yet)
|
||||
// we cannot initialize the state index here without risking
|
||||
// AppVerifier errors and undefined behavior. Luckily TlsAlloc values
|
||||
// are guaranteed to be zero-initialized so we don't need to zero-init,
|
||||
// this, and in fact we can't for all threads.
|
||||
// TLS on PS3 is zero-initialized in global ELF section
|
||||
// TLS is also not accessible at this point before PRX entry point runs
|
||||
g_nThreadLocalStateIndex = 0;
|
||||
#endif
|
||||
|
||||
m_LoggingStates[0].m_nPreviousStackEntry = -1;
|
||||
|
||||
m_LoggingStates[0].m_nListenerCount = 1;
|
||||
m_LoggingStates[0].m_RegisteredListeners[0] = &m_DefaultLoggingListener;
|
||||
m_LoggingStates[0].m_pLoggingResponse = &m_DefaultLoggingResponse;
|
||||
|
||||
// Mark all other logging state blocks as unused.
|
||||
for ( int i = 1; i < MAX_LOGGING_STATE_COUNT; ++ i )
|
||||
{
|
||||
m_LoggingStates[i].m_nListenerCount = -1;
|
||||
}
|
||||
|
||||
m_pStateMutex = NULL;
|
||||
}
|
||||
|
||||
CLoggingSystem::~CLoggingSystem()
|
||||
{
|
||||
g_bEnforceLoggingSystemSingleton = false;
|
||||
delete m_pStateMutex;
|
||||
}
|
||||
|
||||
LoggingChannelID_t CLoggingSystem::RegisterLoggingChannel( const char *pChannelName, RegisterTagsFunc registerTagsFunc, int flags, LoggingSeverity_t severity, Color spewColor )
|
||||
{
|
||||
if ( m_nChannelCount >= MAX_LOGGING_CHANNEL_COUNT )
|
||||
{
|
||||
// Out of logging channels... catastrophic fail!
|
||||
Log_Error( LOG_GENERAL, "Out of logging channels.\n" );
|
||||
Assert( 0 );
|
||||
return INVALID_LOGGING_CHANNEL_ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Channels can be multiply defined, in which case return the ID of the existing channel.
|
||||
for ( int i = 0; i < m_nChannelCount; ++ i )
|
||||
{
|
||||
if ( V_tier0_stricmp( m_RegisteredChannels[i].m_Name, pChannelName ) == 0 )
|
||||
{
|
||||
// OK to call the tag registration callback; duplicates will be culled away.
|
||||
// This allows multiple people to register a logging channel, and the union of all tags will be registered.
|
||||
if ( registerTagsFunc != NULL )
|
||||
{
|
||||
registerTagsFunc();
|
||||
}
|
||||
|
||||
// If a logging channel is registered multiple times, only one of the registrations should specify flags/severity/color.
|
||||
if ( m_RegisteredChannels[i].m_Flags == 0 && m_RegisteredChannels[i].m_MinimumSeverity == LS_MESSAGE && m_RegisteredChannels[i].m_SpewColor == UNSPECIFIED_LOGGING_COLOR )
|
||||
{
|
||||
m_RegisteredChannels[i].m_Flags = ( LoggingChannelFlags_t )flags;
|
||||
m_RegisteredChannels[i].m_MinimumSeverity = severity;
|
||||
m_RegisteredChannels[i].m_SpewColor = spewColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertMsg( flags == 0 || flags == m_RegisteredChannels[i].m_Flags, "Non-zero or mismatched flags specified in logging channel re-registration!" );
|
||||
AssertMsg( severity == LS_MESSAGE || severity == m_RegisteredChannels[i].m_MinimumSeverity, "Non-default or mismatched severity specified in logging channel re-registration!" );
|
||||
AssertMsg( spewColor == UNSPECIFIED_LOGGING_COLOR || spewColor == m_RegisteredChannels[i].m_SpewColor, "Non-default or mismatched color specified in logging channel re-registration!" );
|
||||
}
|
||||
|
||||
return m_RegisteredChannels[i].m_ID;
|
||||
}
|
||||
}
|
||||
|
||||
m_RegisteredChannels[m_nChannelCount].m_ID = m_nChannelCount;
|
||||
m_RegisteredChannels[m_nChannelCount].m_Flags = ( LoggingChannelFlags_t )flags;
|
||||
m_RegisteredChannels[m_nChannelCount].m_MinimumSeverity = severity;
|
||||
m_RegisteredChannels[m_nChannelCount].m_SpewColor = spewColor;
|
||||
strncpy( m_RegisteredChannels[m_nChannelCount].m_Name, pChannelName, MAX_LOGGING_IDENTIFIER_LENGTH );
|
||||
|
||||
if ( registerTagsFunc != NULL )
|
||||
{
|
||||
registerTagsFunc();
|
||||
}
|
||||
return m_nChannelCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
LoggingChannelID_t CLoggingSystem::FindChannel( const char *pChannelName ) const
|
||||
{
|
||||
for ( int i = 0; i < m_nChannelCount; ++ i )
|
||||
{
|
||||
if ( V_tier0_stricmp( m_RegisteredChannels[i].m_Name, pChannelName ) == 0 )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return INVALID_LOGGING_CHANNEL_ID;
|
||||
}
|
||||
|
||||
void CLoggingSystem::AddTagToCurrentChannel( const char *pTagName )
|
||||
{
|
||||
// Add tags at the head of the tag-list of the most recently added channel.
|
||||
LoggingChannel_t *pChannel = &m_RegisteredChannels[m_nChannelCount];
|
||||
|
||||
// First check for duplicates
|
||||
if ( pChannel->HasTag( pTagName ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LoggingTag_t *pTag = AllocTag( pTagName );
|
||||
|
||||
pTag->m_pNextTag = pChannel->m_pFirstTag;
|
||||
pChannel->m_pFirstTag = pTag;
|
||||
}
|
||||
|
||||
void CLoggingSystem::SetChannelSpewLevel( LoggingChannelID_t channelID, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
GetChannel( channelID )->SetSpewLevel( minimumSeverity );
|
||||
}
|
||||
|
||||
void CLoggingSystem::SetChannelSpewLevelByName( const char *pName, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
for ( int i = 0; i < m_nChannelCount; ++ i )
|
||||
{
|
||||
if ( V_tier0_stricmp( m_RegisteredChannels[i].m_Name, pName ) == 0 )
|
||||
{
|
||||
m_RegisteredChannels[i].SetSpewLevel( minimumSeverity );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLoggingSystem::SetChannelSpewLevelByTag( const char *pTag, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
for ( int i = 0; i < m_nChannelCount; ++ i )
|
||||
{
|
||||
if ( m_RegisteredChannels[i].HasTag( pTag ) )
|
||||
{
|
||||
m_RegisteredChannels[i].SetSpewLevel( minimumSeverity );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLoggingSystem::SetGlobalSpewLevel( LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
for ( int i = 0; i < m_nChannelCount; ++ i )
|
||||
{
|
||||
m_RegisteredChannels[i].SetSpewLevel( minimumSeverity );
|
||||
}
|
||||
}
|
||||
|
||||
void CLoggingSystem::PushLoggingState( bool bThreadLocal, bool bClearState )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
|
||||
int nNewState = FindUnusedStateIndex();
|
||||
// Ensure we're not out of state blocks.
|
||||
Assert( nNewState != -1 );
|
||||
|
||||
int nCurrentState = bThreadLocal ? (int)g_nThreadLocalStateIndex : m_nGlobalStateIndex;
|
||||
|
||||
if ( bClearState )
|
||||
{
|
||||
m_LoggingStates[nNewState].m_nListenerCount = 0;
|
||||
m_LoggingStates[nNewState].m_pLoggingResponse = &m_DefaultLoggingResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LoggingStates[nNewState] = m_LoggingStates[nCurrentState];
|
||||
}
|
||||
|
||||
m_LoggingStates[nNewState].m_nPreviousStackEntry = nCurrentState;
|
||||
|
||||
if ( bThreadLocal )
|
||||
{
|
||||
g_nThreadLocalStateIndex = nNewState;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nGlobalStateIndex = nNewState;
|
||||
}
|
||||
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
void CLoggingSystem::PopLoggingState( bool bThreadLocal )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
|
||||
int nCurrentState = bThreadLocal ? (int)g_nThreadLocalStateIndex : m_nGlobalStateIndex;
|
||||
|
||||
// Shouldn't be less than 0 (implies error during Push()) or 0 (implies that Push() was never called)
|
||||
Assert( nCurrentState > 0 );
|
||||
|
||||
// Mark the current state as unused.
|
||||
m_LoggingStates[nCurrentState].m_nListenerCount = -1;
|
||||
|
||||
if ( bThreadLocal )
|
||||
{
|
||||
g_nThreadLocalStateIndex = m_LoggingStates[nCurrentState].m_nPreviousStackEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nGlobalStateIndex = m_LoggingStates[nCurrentState].m_nPreviousStackEntry;
|
||||
}
|
||||
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
void CLoggingSystem::RegisterLoggingListener( ILoggingListener *pListener )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
LoggingState_t *pState = GetCurrentState();
|
||||
if ( pState->m_nListenerCount >= ARRAYSIZE(pState->m_RegisteredListeners) )
|
||||
{
|
||||
// Out of logging listener slots... catastrophic fail!
|
||||
Assert( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
pState->m_RegisteredListeners[pState->m_nListenerCount] = pListener;
|
||||
++ pState->m_nListenerCount;
|
||||
}
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
bool CLoggingSystem::IsListenerRegistered( ILoggingListener *pListener )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
const LoggingState_t *pState = GetCurrentState();
|
||||
bool bFound = false;
|
||||
for ( int i = 0; i < pState->m_nListenerCount; ++ i )
|
||||
{
|
||||
if ( pState->m_RegisteredListeners[i] == pListener )
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_pStateMutex->Unlock();
|
||||
return bFound;
|
||||
}
|
||||
|
||||
void CLoggingSystem::ResetCurrentLoggingState()
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
LoggingState_t *pState = GetCurrentState();
|
||||
pState->m_nListenerCount = 0;
|
||||
pState->m_pLoggingResponse = &m_DefaultLoggingResponse;
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
void CLoggingSystem::SetLoggingResponsePolicy( ILoggingResponsePolicy *pLoggingResponse )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
LoggingState_t *pState = GetCurrentState();
|
||||
if ( pLoggingResponse == NULL )
|
||||
{
|
||||
pState->m_pLoggingResponse = &m_DefaultLoggingResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
pState->m_pLoggingResponse = pLoggingResponse;
|
||||
}
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
LoggingResponse_t CLoggingSystem::LogDirect( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color color, const tchar *pMessage )
|
||||
{
|
||||
Assert( IsValidChannelID( channelID ) );
|
||||
if ( !IsValidChannelID( channelID ) )
|
||||
return LR_CONTINUE;
|
||||
|
||||
LoggingContext_t context;
|
||||
context.m_ChannelID = channelID;
|
||||
context.m_Flags = m_RegisteredChannels[channelID].m_Flags;
|
||||
context.m_Severity = severity;
|
||||
context.m_Color = ( color == UNSPECIFIED_LOGGING_COLOR ) ? m_RegisteredChannels[channelID].m_SpewColor : color;
|
||||
|
||||
// It is assumed that the mutex is reentrant safe on all platforms.
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
|
||||
LoggingState_t *pState = GetCurrentState();
|
||||
|
||||
for ( int i = 0; i < pState->m_nListenerCount; ++ i )
|
||||
{
|
||||
pState->m_RegisteredListeners[i]->Log( &context, pMessage );
|
||||
}
|
||||
|
||||
#if defined( _PS3 ) && !defined( _CERT )
|
||||
if ( !pState->m_nListenerCount )
|
||||
{
|
||||
unsigned int unBytesWritten;
|
||||
sys_tty_write( SYS_TTYP15, pMessage, strlen( pMessage ), &unBytesWritten );
|
||||
}
|
||||
#endif
|
||||
|
||||
LoggingResponse_t response = pState->m_pLoggingResponse->OnLog( &context );
|
||||
|
||||
if ( DBG_SPEW_ALL_WARNINGS_AND_ERRORS_ASSERT && severity != LS_MESSAGE )
|
||||
{
|
||||
response = LR_DEBUGGER;
|
||||
}
|
||||
|
||||
m_pStateMutex->Unlock();
|
||||
|
||||
switch( response )
|
||||
{
|
||||
case LR_DEBUGGER:
|
||||
// Asserts put the debug break in the macro itself so the code breaks at the failure point.
|
||||
if ( severity != LS_ASSERT )
|
||||
{
|
||||
DebuggerBreakIfDebugging();
|
||||
}
|
||||
break;
|
||||
|
||||
case LR_ABORT:
|
||||
Log_Msg( LOG_DEVELOPER_VERBOSE, "Exiting due to logging LR_ABORT request.\n" );
|
||||
Plat_ExitProcess( EXIT_FAILURE );
|
||||
break;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
CLoggingSystem::LoggingChannel_t *CLoggingSystem::GetChannel( LoggingChannelID_t channelID )
|
||||
{
|
||||
Assert( IsValidChannelID( channelID ) );
|
||||
return &m_RegisteredChannels[channelID];
|
||||
}
|
||||
|
||||
const CLoggingSystem::LoggingChannel_t *CLoggingSystem::GetChannel( LoggingChannelID_t channelID ) const
|
||||
{
|
||||
Assert( IsValidChannelID( channelID ) );
|
||||
return &m_RegisteredChannels[channelID];
|
||||
}
|
||||
|
||||
CLoggingSystem::LoggingState_t *CLoggingSystem::GetCurrentState()
|
||||
{
|
||||
// Assume the caller grabbed the mutex.
|
||||
int nState = g_nThreadLocalStateIndex;
|
||||
if ( nState != 0 )
|
||||
{
|
||||
Assert( nState > 0 && nState < MAX_LOGGING_STATE_COUNT );
|
||||
return &m_LoggingStates[nState];
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( m_nGlobalStateIndex >= 0 && m_nGlobalStateIndex < MAX_LOGGING_STATE_COUNT );
|
||||
return &m_LoggingStates[m_nGlobalStateIndex];
|
||||
}
|
||||
}
|
||||
|
||||
const CLoggingSystem::LoggingState_t *CLoggingSystem::GetCurrentState() const
|
||||
{
|
||||
// Assume the caller grabbed the mutex.
|
||||
int nState = g_nThreadLocalStateIndex;
|
||||
if ( nState != 0 )
|
||||
{
|
||||
Assert( nState > 0 && nState < MAX_LOGGING_STATE_COUNT );
|
||||
return &m_LoggingStates[nState];
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( m_nGlobalStateIndex >= 0 && m_nGlobalStateIndex < MAX_LOGGING_STATE_COUNT );
|
||||
return &m_LoggingStates[m_nGlobalStateIndex];
|
||||
}
|
||||
}
|
||||
|
||||
int CLoggingSystem::FindUnusedStateIndex()
|
||||
{
|
||||
for ( int i = 0; i < MAX_LOGGING_STATE_COUNT; ++ i )
|
||||
{
|
||||
if ( m_LoggingStates[i].m_nListenerCount < 0 )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
CLoggingSystem::LoggingTag_t *CLoggingSystem::AllocTag( const char *pTagName )
|
||||
{
|
||||
Assert( m_nChannelTagCount < MAX_LOGGING_TAG_COUNT );
|
||||
LoggingTag_t *pTag = &m_ChannelTags[m_nChannelTagCount ++];
|
||||
|
||||
pTag->m_pNextTag = NULL;
|
||||
pTag->m_pTagName = m_TagNamePool + m_nTagNamePoolIndex;
|
||||
|
||||
// Copy string into pool.
|
||||
size_t nTagLength = strlen( pTagName );
|
||||
Assert( m_nTagNamePoolIndex + nTagLength + 1 <= MAX_LOGGING_TAG_CHARACTER_COUNT );
|
||||
strcpy( m_TagNamePool + m_nTagNamePoolIndex, pTagName );
|
||||
m_nTagNamePoolIndex += ( int )nTagLength + 1;
|
||||
|
||||
return pTag;
|
||||
}
|
||||
|
||||
LoggingChannelID_t LoggingSystem_RegisterLoggingChannel( const char *pName, RegisterTagsFunc registerTagsFunc, int flags, LoggingSeverity_t severity, Color color )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->RegisterLoggingChannel( pName, registerTagsFunc, flags, severity, color );
|
||||
}
|
||||
|
||||
void LoggingSystem_ResetCurrentLoggingState()
|
||||
{
|
||||
GetGlobalLoggingSystem()->ResetCurrentLoggingState();
|
||||
}
|
||||
|
||||
void LoggingSystem_RegisterLoggingListener( ILoggingListener *pListener )
|
||||
{
|
||||
GetGlobalLoggingSystem()->RegisterLoggingListener( pListener );
|
||||
}
|
||||
|
||||
void LoggingSystem_UnregisterLoggingListener(ILoggingListener *pListener)
|
||||
{
|
||||
}
|
||||
|
||||
void LoggingSystem_SetLoggingResponsePolicy( ILoggingResponsePolicy *pResponsePolicy )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetLoggingResponsePolicy( pResponsePolicy );
|
||||
}
|
||||
|
||||
void LoggingSystem_PushLoggingState( bool bThreadLocal, bool bClearState )
|
||||
{
|
||||
GetGlobalLoggingSystem()->PushLoggingState( bThreadLocal, bClearState );
|
||||
}
|
||||
|
||||
void LoggingSystem_PopLoggingState( bool bThreadLocal )
|
||||
{
|
||||
GetGlobalLoggingSystem()->PopLoggingState( bThreadLocal );
|
||||
}
|
||||
|
||||
void LoggingSystem_AddTagToCurrentChannel( const char *pTagName )
|
||||
{
|
||||
GetGlobalLoggingSystem()->AddTagToCurrentChannel( pTagName );
|
||||
}
|
||||
|
||||
LoggingChannelID_t LoggingSystem_FindChannel( const char *pChannelName )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->FindChannel( pChannelName );
|
||||
}
|
||||
|
||||
int LoggingSystem_GetChannelCount()
|
||||
{
|
||||
return GetGlobalLoggingSystem()->GetChannelCount();
|
||||
}
|
||||
|
||||
LoggingChannelID_t LoggingSystem_GetFirstChannelID()
|
||||
{
|
||||
return ( GetGlobalLoggingSystem()->GetChannelCount() > 0 ) ? 0 : INVALID_LOGGING_CHANNEL_ID;
|
||||
}
|
||||
|
||||
LoggingChannelID_t LoggingSystem_GetNextChannelID( LoggingChannelID_t channelID )
|
||||
{
|
||||
int nChannelCount = GetGlobalLoggingSystem()->GetChannelCount();
|
||||
int nNextChannel = channelID + 1;
|
||||
return ( nNextChannel < nChannelCount ) ? nNextChannel : INVALID_LOGGING_CHANNEL_ID;
|
||||
}
|
||||
|
||||
const CLoggingSystem::LoggingChannel_t *LoggingSystem_GetChannel( LoggingChannelID_t channelIndex )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->GetChannel( channelIndex );
|
||||
}
|
||||
|
||||
bool LoggingSystem_HasTag( LoggingChannelID_t channelID, const char *pTag )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->HasTag( channelID, pTag );
|
||||
}
|
||||
|
||||
bool LoggingSystem_IsChannelEnabled( LoggingChannelID_t channelID, LoggingSeverity_t severity )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->IsChannelEnabled( channelID, severity );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelSpewLevel( LoggingChannelID_t channelID, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetChannelSpewLevel( channelID, minimumSeverity );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelSpewLevelByName( const char *pName, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetChannelSpewLevelByName( pName, minimumSeverity );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelSpewLevelByTag( const char *pTag, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetChannelSpewLevelByTag( pTag, minimumSeverity );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetGlobalSpewLevel( LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetGlobalSpewLevel( minimumSeverity );
|
||||
}
|
||||
|
||||
int32 LoggingSystem_GetChannelColor( LoggingChannelID_t channelID )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->GetChannelColor( channelID ).GetRawColor();
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelColor( LoggingChannelID_t channelID, int color )
|
||||
{
|
||||
Color c;
|
||||
c.SetRawColor( color );
|
||||
GetGlobalLoggingSystem()->SetChannelColor( channelID, c );
|
||||
}
|
||||
|
||||
LoggingChannelFlags_t LoggingSystem_GetChannelFlags( LoggingChannelID_t channelID )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->GetChannelFlags( channelID );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelFlags( LoggingChannelID_t channelID, LoggingChannelFlags_t flags )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetChannelFlags( channelID, flags );
|
||||
}
|
||||
|
||||
LoggingResponse_t LoggingSystem_Log( LoggingChannelID_t channelID, LoggingSeverity_t severity, const char *pMessageFormat, ... )
|
||||
{
|
||||
if ( !GetGlobalLoggingSystem()->IsChannelEnabled( channelID, severity ) )
|
||||
return LR_CONTINUE;
|
||||
|
||||
tchar formattedMessage[MAX_LOGGING_MESSAGE_LENGTH];
|
||||
|
||||
va_list args;
|
||||
va_start( args, pMessageFormat );
|
||||
Tier0Internal_vsntprintf( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, pMessageFormat, args );
|
||||
va_end( args );
|
||||
|
||||
return GetGlobalLoggingSystem()->LogDirect( channelID, severity, UNSPECIFIED_LOGGING_COLOR, formattedMessage );
|
||||
}
|
||||
|
||||
LoggingResponse_t LoggingSystem_Log( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color spewColor, const char *pMessageFormat, ... )
|
||||
{
|
||||
if ( !GetGlobalLoggingSystem()->IsChannelEnabled( channelID, severity ) )
|
||||
return LR_CONTINUE;
|
||||
|
||||
tchar formattedMessage[MAX_LOGGING_MESSAGE_LENGTH];
|
||||
|
||||
va_list args;
|
||||
va_start( args, pMessageFormat );
|
||||
Tier0Internal_vsntprintf( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, pMessageFormat, args );
|
||||
va_end( args );
|
||||
|
||||
return GetGlobalLoggingSystem()->LogDirect( channelID, severity, spewColor, formattedMessage );
|
||||
}
|
||||
|
||||
LoggingResponse_t LoggingSystem_LogDirect( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color spewColor, const char *pMessage )
|
||||
{
|
||||
if ( !GetGlobalLoggingSystem()->IsChannelEnabled( channelID, severity ) )
|
||||
return LR_CONTINUE;
|
||||
return GetGlobalLoggingSystem()->LogDirect( channelID, severity, spewColor, pMessage );
|
||||
}
|
||||
|
||||
LoggingResponse_t LoggingSystem_LogAssert( const char *pMessageFormat, ... )
|
||||
{
|
||||
if ( !GetGlobalLoggingSystem()->IsChannelEnabled( LOG_ASSERT, LS_ASSERT ) )
|
||||
return LR_CONTINUE;
|
||||
|
||||
tchar formattedMessage[MAX_LOGGING_MESSAGE_LENGTH];
|
||||
|
||||
va_list args;
|
||||
va_start( args, pMessageFormat );
|
||||
Tier0Internal_vsntprintf( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, pMessageFormat, args );
|
||||
va_end( args );
|
||||
|
||||
return GetGlobalLoggingSystem()->LogDirect( LOG_ASSERT, LS_ASSERT, UNSPECIFIED_LOGGING_COLOR, formattedMessage );
|
||||
}
|
||||
93
tier0/mem.cpp
Normal file
93
tier0/mem.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Memory allocation!
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/mem.h"
|
||||
//#include <malloc.h>
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
#include "tier0/minidump.h"
|
||||
|
||||
#ifndef STEAM
|
||||
#define PvRealloc realloc
|
||||
#define PvAlloc malloc
|
||||
#define PvExpand _expand
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_STACK_DEPTH = 32
|
||||
};
|
||||
|
||||
static uint8 *s_pBuf = NULL;
|
||||
static int s_pBufStackDepth[MAX_STACK_DEPTH];
|
||||
static int s_nBufDepth = -1;
|
||||
static int s_nBufCurSize = 0;
|
||||
static int s_nBufAllocSize = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Other DLL-exported methods for particular kinds of memory
|
||||
//-----------------------------------------------------------------------------
|
||||
void *MemAllocScratch( int nMemSize )
|
||||
{
|
||||
// Minimally allocate 1M scratch
|
||||
if (s_nBufAllocSize < s_nBufCurSize + nMemSize)
|
||||
{
|
||||
s_nBufAllocSize = s_nBufCurSize + nMemSize;
|
||||
if (s_nBufAllocSize < 2 * 1024)
|
||||
{
|
||||
s_nBufAllocSize = 2 * 1024;
|
||||
}
|
||||
|
||||
if (s_pBuf)
|
||||
{
|
||||
s_pBuf = (uint8*)PvRealloc( s_pBuf, s_nBufAllocSize );
|
||||
Assert( s_pBuf );
|
||||
}
|
||||
else
|
||||
{
|
||||
s_pBuf = (uint8*)PvAlloc( s_nBufAllocSize );
|
||||
}
|
||||
}
|
||||
|
||||
int nBase = s_nBufCurSize;
|
||||
s_nBufCurSize += nMemSize;
|
||||
++s_nBufDepth;
|
||||
Assert( s_nBufDepth < MAX_STACK_DEPTH );
|
||||
s_pBufStackDepth[s_nBufDepth] = nMemSize;
|
||||
|
||||
return &s_pBuf[nBase];
|
||||
}
|
||||
|
||||
void MemFreeScratch()
|
||||
{
|
||||
Assert( s_nBufDepth >= 0 );
|
||||
s_nBufCurSize -= s_pBufStackDepth[s_nBufDepth];
|
||||
--s_nBufDepth;
|
||||
}
|
||||
|
||||
#ifdef POSIX
|
||||
void ZeroMemory( void *mem, size_t length )
|
||||
{
|
||||
memset( mem, 0x0, length );
|
||||
}
|
||||
#endif
|
||||
|
||||
void MemOutOfMemory( size_t nBytesAttempted )
|
||||
{
|
||||
if ( Plat_IsInDebugSession() )
|
||||
{
|
||||
DebuggerBreak();
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMiniDump();
|
||||
Plat_ExitProcess( EXIT_FAILURE );
|
||||
}
|
||||
}
|
||||
186
tier0/mem_helpers.cpp
Normal file
186
tier0/mem_helpers.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/icommandline.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "mem_helpers.h"
|
||||
#include <string.h>
|
||||
//#include <malloc.h>
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// Needed for debugging
|
||||
const char *g_pszModule = "tier0";
|
||||
bool g_bInitMemory = true;
|
||||
|
||||
#if defined(PLATFORM_POSIX) || defined( PLATFORM_PS3)
|
||||
void DoApplyMemoryInitializations( void *pMem, size_t nSize )
|
||||
{
|
||||
}
|
||||
|
||||
size_t CalcHeapUsed()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
||||
unsigned long g_dwFeeFee = 0xffeeffee;
|
||||
|
||||
// Generated by Mathematica.
|
||||
unsigned char g_RandomValues[256] = {
|
||||
95, 126, 220, 71, 92, 179, 95, 219, 111, 150, 38, 155, 181, 62, 40, 231, 238,
|
||||
54, 47, 55, 186, 204, 64, 70, 118, 94, 107, 251, 199, 140, 67, 87, 86, 127,
|
||||
210, 41, 21, 90, 208, 24, 167, 204, 32, 254, 38, 51, 9, 11, 38, 33, 188, 104,
|
||||
0, 75, 119, 24, 122, 203, 24, 164, 250, 224, 241, 182, 213, 201, 173, 67,
|
||||
200, 255, 244, 227, 46, 219, 26, 149, 218, 132, 120, 154, 227, 244, 106, 198,
|
||||
109, 87, 150, 40, 16, 99, 169, 193, 100, 156, 78, 171, 246, 47, 84, 119, 10,
|
||||
52, 207, 171, 230, 90, 90, 127, 180, 153, 68, 140, 62, 14, 87, 57, 208, 154,
|
||||
116, 29, 131, 177, 224, 187, 51, 148, 142, 245, 152, 230, 184, 117, 91, 146,
|
||||
235, 153, 35, 104, 187, 177, 215, 131, 17, 49, 211, 244, 60, 152, 103, 248,
|
||||
51, 224, 237, 240, 51, 30, 10, 233, 253, 106, 252, 73, 134, 136, 178, 86,
|
||||
228, 107, 77, 255, 85, 242, 204, 119, 102, 53, 209, 35, 123, 32, 252, 210,
|
||||
43, 12, 136, 167, 155, 210, 71, 254, 178, 172, 3, 230, 93, 208, 196, 68, 235,
|
||||
16, 106, 189, 201, 177, 85, 78, 206, 187, 48, 68, 64, 190, 117, 236, 49, 174,
|
||||
105, 63, 207, 70, 170, 93, 6, 110, 52, 111, 169, 92, 247, 86, 10, 174, 207,
|
||||
240, 104, 209, 81, 177, 123, 189, 175, 212, 101, 219, 114, 243, 44, 91, 51,
|
||||
139, 91, 57, 120, 41, 98, 119 };
|
||||
|
||||
unsigned long g_iCurRandomValueOffset = 0;
|
||||
|
||||
|
||||
void InitializeToFeeFee( void *pMem, size_t nSize )
|
||||
{
|
||||
unsigned long *pCurDWord = (unsigned long*)pMem;
|
||||
size_t nDWords = nSize >> 2;
|
||||
while ( nDWords )
|
||||
{
|
||||
*pCurDWord = 0xffeeffee;
|
||||
++pCurDWord;
|
||||
--nDWords;
|
||||
}
|
||||
|
||||
unsigned char *pCurChar = (unsigned char*)pCurDWord;
|
||||
size_t nBytes = nSize & 3;
|
||||
size_t iOffset = 0;
|
||||
while ( nBytes )
|
||||
{
|
||||
*pCurChar = ((unsigned char*)&g_dwFeeFee)[iOffset];
|
||||
++iOffset;
|
||||
--nBytes;
|
||||
++pCurChar;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InitializeToRandom( void *pMem, size_t nSize )
|
||||
{
|
||||
unsigned char *pOut = (unsigned char *)pMem;
|
||||
for ( size_t i=0; i < nSize; i++ )
|
||||
{
|
||||
pOut[i] = g_RandomValues[(g_iCurRandomValueOffset & 255)];
|
||||
++g_iCurRandomValueOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DoApplyMemoryInitializations( void *pMem, size_t nSize )
|
||||
{
|
||||
if ( !pMem )
|
||||
return;
|
||||
|
||||
// If they passed -noinitmemory on the command line, don't do anything here.
|
||||
Assert( g_bInitMemory );
|
||||
|
||||
// First time we get in here, remember all the settings.
|
||||
static bool bDebuggerPresent = Plat_IsInDebugSession();
|
||||
static bool bCheckedCommandLine = false;
|
||||
static bool bRandomizeMemory = false;
|
||||
if ( !bCheckedCommandLine )
|
||||
{
|
||||
bCheckedCommandLine = true;
|
||||
|
||||
//APS
|
||||
char *pStr = (char*)Plat_GetCommandLineA();
|
||||
if ( pStr )
|
||||
{
|
||||
char tempStr[512];
|
||||
strncpy( tempStr, pStr, sizeof( tempStr ) - 1 );
|
||||
tempStr[ sizeof( tempStr ) - 1 ] = 0;
|
||||
_strupr( tempStr );
|
||||
|
||||
if ( strstr( tempStr, "-RANDOMIZEMEMORY" ) )
|
||||
bRandomizeMemory = true;
|
||||
|
||||
if ( strstr( tempStr, "-NOINITMEMORY" ) )
|
||||
g_bInitMemory = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bRandomizeMemory )
|
||||
{
|
||||
// They asked for it.. randomize all the memory.
|
||||
InitializeToRandom( pMem, nSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( bDebuggerPresent )
|
||||
{
|
||||
// Ok, it's already set to 0xbaadf00d, but we want something that will make floating-point #'s NANs.
|
||||
InitializeToFeeFee( pMem, nSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(_DEBUG) || defined(USE_LIGHT_MEM_DEBUG)
|
||||
#if !defined(_DEBUG) && defined(LIGHT_MEM_DEBUG_REQUIRES_CMD_LINE_SWITCH)
|
||||
extern bool g_bUsingLMD;
|
||||
if ( !g_bUsingLMD )
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Ok, it's already set to 0xcdcdcdcd, but we want something that will make floating-point #'s NANs.
|
||||
InitializeToFeeFee( pMem, nSize );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t CalcHeapUsed()
|
||||
{
|
||||
#if defined( _X360 )
|
||||
return 0;
|
||||
#else
|
||||
_HEAPINFO hinfo;
|
||||
int heapstatus;
|
||||
intp nTotal;
|
||||
|
||||
nTotal = 0;
|
||||
hinfo._pentry = NULL;
|
||||
while( ( heapstatus = _heapwalk( &hinfo ) ) == _HEAPOK )
|
||||
{
|
||||
nTotal += (hinfo._useflag == _USEDENTRY) ? hinfo._size : 0;
|
||||
}
|
||||
|
||||
switch (heapstatus)
|
||||
{
|
||||
case _HEAPEMPTY:
|
||||
case _HEAPEND:
|
||||
// success
|
||||
break;
|
||||
|
||||
default:
|
||||
// heap corrupted
|
||||
nTotal = -1;
|
||||
}
|
||||
|
||||
return nTotal;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // not PLATFORM_POSIX
|
||||
|
||||
45
tier0/mem_helpers.h
Normal file
45
tier0/mem_helpers.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MEM_HELPERS_H
|
||||
#define MEM_HELPERS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// Normally, the runtime libraries like to mess with the memory returned by malloc(),
|
||||
// which can create problems trying to repro bugs in debug builds or in the debugger.
|
||||
//
|
||||
// If the debugger is present, it initializes data to 0xbaadf00d, which makes floating
|
||||
// point numbers come out to about 0.1.
|
||||
//
|
||||
// If the debugger is not present, and it's a debug build, then you get 0xcdcdcdcd,
|
||||
// which is about 25 million.
|
||||
//
|
||||
// Otherwise, you get uninitialized memory.
|
||||
//
|
||||
// In here, we make sure the memory is either random garbage, or it's set to
|
||||
// 0xffeeffee, which casts to a NAN.
|
||||
extern bool g_bInitMemory;
|
||||
#define ApplyMemoryInitializations( pMem, nSize ) if ( !g_bInitMemory ) ; else { DoApplyMemoryInitializations( pMem, nSize ); }
|
||||
void DoApplyMemoryInitializations( void *pMem, size_t nSize );
|
||||
|
||||
#if IsPlatformWindowsPC()
|
||||
// Use this to override the allocator. This must be called before the first
|
||||
// allocation. This may not be available on all platforms.
|
||||
class IMemAlloc;
|
||||
void SetAllocatorObject( IMemAlloc* pAllocator );
|
||||
// Check for various allocator overrides such as -processheap and -reservelowmem.
|
||||
// Returns true if -processheap is enabled, by a command line switch or other method.
|
||||
bool CheckWindowsAllocSettings( const char* upperCommandLine );
|
||||
#else
|
||||
inline bool CheckWindowsAllocSettings( const char* upperCommandLine ) { return false; }
|
||||
#endif
|
||||
|
||||
size_t CalcHeapUsed();
|
||||
|
||||
#endif // MEM_HELPERS_H
|
||||
6
tier0/mem_impl_type.h
Normal file
6
tier0/mem_impl_type.h
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
#if ( (!defined( POSIX )||defined(_GAMECONSOLE)) && (defined(_DEBUG) || defined(USE_MEM_DEBUG) ) )
|
||||
#define MEM_IMPL_TYPE_DBG 1
|
||||
#else
|
||||
#define MEM_IMPL_TYPE_STD 1
|
||||
#endif
|
||||
2812
tier0/memdbg.cpp
Normal file
2812
tier0/memdbg.cpp
Normal file
File diff suppressed because it is too large
Load Diff
9
tier0/meminit.cpp
Normal file
9
tier0/meminit.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Memory allocation!
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "pch_tier0.h"
|
||||
682
tier0/memprocessheap.cpp
Normal file
682
tier0/memprocessheap.cpp
Normal file
@@ -0,0 +1,682 @@
|
||||
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Memory allocation!
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/memalloc.h"
|
||||
#include "memstd.h"
|
||||
|
||||
#if !defined(NO_MALLOC_OVERRIDE)
|
||||
|
||||
#if defined( _WIN32 )
|
||||
|
||||
#define OVERRIDE override
|
||||
// warning C4481: nonstandard extension used: override specifier 'override'
|
||||
#pragma warning( disable : 4481 )
|
||||
|
||||
#ifdef _WIN64
|
||||
// Set the new-style define that indicates a a 64-bit Windows PC
|
||||
#define PLATFORM_WINDOWS_PC64 1
|
||||
LONGLONG
|
||||
FORCEINLINE
|
||||
InterlockedExchangeAdd64(
|
||||
__inout LONGLONG volatile *Addend,
|
||||
__in LONGLONG Value
|
||||
);
|
||||
#else
|
||||
// Set the new-style define that indicates a a 32-bit Windows PC
|
||||
#define PLATFORM_WINDOWS_PC32 1
|
||||
#endif
|
||||
|
||||
// Support for CHeapMemAlloc for easy switching to using the process heap.
|
||||
|
||||
// Track this to decide how to handle out-of-memory.
|
||||
static bool s_bPageHeapEnabled = false;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// IMemAlloc must guarantee 16-byte alignment for 16n-byte allocations, so we just
|
||||
// force 16-byte alignment under win32 (the win64 system heap already 16-byte aligns).
|
||||
// TODO: this padding negates some of the buffer-overrun protection provided by pageheap, so...
|
||||
// we should fill padding bytes with a known pattern which is checked in realloc/free
|
||||
#ifdef PLATFORM_WINDOWS_PC32
|
||||
#define FORCED_ALIGNMENT 16
|
||||
#else
|
||||
#define FORCED_ALIGNMENT 0
|
||||
#endif
|
||||
|
||||
|
||||
// Round a size up to a multiple of 4 KB to aid in calculating how much
|
||||
// memory is required if full pageheap is enabled.
|
||||
static size_t RoundUpToPage( size_t nSize )
|
||||
{
|
||||
nSize += 0xFFF;
|
||||
nSize &= ~0xFFF;
|
||||
return nSize;
|
||||
}
|
||||
|
||||
static void InterlockedAddSizeT( size_t volatile *Addend, size_t Value )
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS_PC32
|
||||
// Convenience function to deal with the necessary type-casting
|
||||
InterlockedExchangeAdd( ( LONG* )Addend, LONG( Value ) );
|
||||
#else
|
||||
InterlockedExchangeAdd64( ( LONGLONG* )Addend, LONGLONG( Value ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
// CHeapDefault supplies default implementations for as many functions as
|
||||
// possible so that a heap implementation can be as simple as possible.
|
||||
class CHeapDefault : public IMemAlloc
|
||||
{
|
||||
// Since we define the debug versions of Alloc/Realloc/Free in this class but
|
||||
// not the release versions we implicitly hide the release implementations, which
|
||||
// makes it impossible for us to call them in order to implement the debug
|
||||
// versions. These using directives pull these three names into this namespace
|
||||
// so that we can call them.
|
||||
using IMemAlloc::Alloc;
|
||||
using IMemAlloc::Realloc;
|
||||
using IMemAlloc::Free;
|
||||
|
||||
// Release versions
|
||||
// Alloc, Realloc, and Free must be implemented
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize ) OVERRIDE { return 0; }
|
||||
|
||||
// Debug versions
|
||||
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return Alloc( nSize ); }
|
||||
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return Realloc(pMem, nSize); }
|
||||
virtual void Free( void *pMem, const char *pFileName, int nLine ) OVERRIDE { Free( pMem ); }
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return 0; }
|
||||
|
||||
// GetSize must be implemented
|
||||
|
||||
// Force file + line information for an allocation
|
||||
virtual void PushAllocDbgInfo( const char *pFileName, int nLine ) OVERRIDE {}
|
||||
virtual void PopAllocDbgInfo() OVERRIDE {}
|
||||
|
||||
// FIXME: Remove when we have our own allocator
|
||||
// these methods of the Crt debug code is used in our codebase currently
|
||||
virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc ) OVERRIDE { return 0; }
|
||||
virtual int CrtSetReportMode( int nReportType, int nReportMode ) OVERRIDE { return 0; }
|
||||
virtual int CrtIsValidHeapPointer( const void *pMem ) OVERRIDE { return 0; }
|
||||
virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access ) OVERRIDE { return 0; }
|
||||
virtual int CrtCheckMemory( void ) OVERRIDE { return 0; }
|
||||
virtual int CrtSetDbgFlag( int nNewFlag ) OVERRIDE { return 0; }
|
||||
virtual void CrtMemCheckpoint( _CrtMemState *pState ) OVERRIDE {}
|
||||
|
||||
// FIXME: Make a better stats interface
|
||||
virtual void DumpStats() OVERRIDE {}
|
||||
virtual void DumpStatsFileBase( char const *pchFileBase, DumpStatsFormat_t nFormat = FORMAT_TEXT ) OVERRIDE { DumpStats(); }
|
||||
virtual size_t ComputeMemoryUsedBy( char const *pchSubStr ) OVERRIDE { return 0; }
|
||||
|
||||
// FIXME: Remove when we have our own allocator
|
||||
virtual void* CrtSetReportFile( int nRptType, void* hFile ) OVERRIDE { return 0; }
|
||||
virtual void* CrtSetReportHook( void* pfnNewHook ) OVERRIDE { return 0; }
|
||||
virtual int CrtDbgReport( int nRptType, const char * szFile,
|
||||
int nLine, const char * szModule, const char * pMsg ) OVERRIDE { return 0; }
|
||||
|
||||
virtual int heapchk() OVERRIDE { return _HEAPOK; }
|
||||
virtual bool IsDebugHeap() OVERRIDE { return 0; }
|
||||
|
||||
virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) OVERRIDE { pFileName = ""; nLine = 0; }
|
||||
virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) OVERRIDE {}
|
||||
virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) OVERRIDE {}
|
||||
|
||||
virtual int GetVersion() OVERRIDE { return 0; }
|
||||
|
||||
virtual void CompactHeap() OVERRIDE {}
|
||||
|
||||
virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) OVERRIDE { return 0; }
|
||||
|
||||
virtual void DumpBlockStats( void * ) OVERRIDE {}
|
||||
|
||||
virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment ) OVERRIDE {}
|
||||
|
||||
// Returns 0 if no failure, otherwise the size_t of the last requested chunk
|
||||
virtual size_t MemoryAllocFailed() OVERRIDE { return 0; }
|
||||
|
||||
virtual void CompactIncremental() OVERRIDE {}
|
||||
|
||||
virtual void OutOfMemory( size_t nBytesAttempted = 0 ) OVERRIDE {}
|
||||
|
||||
// Region-based allocations
|
||||
virtual void *RegionAlloc( int region, size_t nSize ) OVERRIDE { return 0; }
|
||||
virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine ) OVERRIDE { return 0; }
|
||||
|
||||
// Replacement for ::GlobalMemoryStatus which accounts for unused memory in our system
|
||||
virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory ) OVERRIDE {}
|
||||
|
||||
// Obtain virtual memory manager interface
|
||||
virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes ) OVERRIDE { return 0; }
|
||||
|
||||
// Request 'generic' memory stats (returns a list of N named values; caller should assume this list will change over time)
|
||||
virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats ) { return 0; }
|
||||
|
||||
// handles storing allocation info for coroutines
|
||||
virtual uint32 GetDebugInfoSize() { return 0; }
|
||||
virtual void SaveDebugInfo( void *pvDebugInfo ) {}
|
||||
virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
|
||||
virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
|
||||
};
|
||||
|
||||
class CHeapMemAlloc : public CHeapDefault
|
||||
{
|
||||
public:
|
||||
CHeapMemAlloc()
|
||||
{
|
||||
// Do all allocations with the shared process heap so that we can still
|
||||
// allocate from one DLL and free in another.
|
||||
m_heap = GetProcessHeap();
|
||||
}
|
||||
|
||||
// minimal IMemAlloc implementation
|
||||
|
||||
// Release API
|
||||
public:
|
||||
virtual void *Alloc( size_t nSize ) OVERRIDE;
|
||||
virtual void *Realloc( void *pMem, size_t nSize ) OVERRIDE;
|
||||
virtual void Free( void *pMem ) OVERRIDE;
|
||||
|
||||
// Returns size of a particular allocation
|
||||
// BUGBUG: this function should be 'const'
|
||||
virtual size_t GetSize( void *pMem ) OVERRIDE;
|
||||
|
||||
// Return 1 to indicate a healthy heap.
|
||||
// BUGBUG: this function should be 'const'
|
||||
virtual int CrtCheckMemory( void ) OVERRIDE;
|
||||
|
||||
// BUGBUG: this function should be 'const'
|
||||
virtual void DumpStats() OVERRIDE;
|
||||
|
||||
void Init(bool bZeroMemory);
|
||||
|
||||
private:
|
||||
|
||||
void OutOfMemory( size_t nBytesAttempted = 0 );
|
||||
|
||||
// Internal allocation calls used to support alignment
|
||||
void * Alloc_Unaligned( size_t nSize );
|
||||
void * Realloc_Unaligned( void *pMem, size_t nSize );
|
||||
void Free_Unaligned( void *pMem );
|
||||
size_t GetSize_Unaligned( void *pMem ) const;
|
||||
|
||||
// Handle to the process heap.
|
||||
HANDLE m_heap;
|
||||
uint32 m_HeapFlags;
|
||||
|
||||
// Total outstanding bytes allocated.
|
||||
volatile size_t m_nOutstandingBytes;
|
||||
|
||||
// Total outstanding committed bytes assuming that all allocations are
|
||||
// put on individual 4-KB pages (true when using full PageHeap from
|
||||
// App Verifier).
|
||||
volatile size_t m_nOutstandingPageHeapBytes;
|
||||
|
||||
// Total outstanding allocations. With PageHeap enabled each allocation
|
||||
// requires an extra 4-KB page of address space.
|
||||
volatile LONG m_nOutstandingAllocations;
|
||||
LONG m_nOldOutstandingAllocations;
|
||||
|
||||
// Total allocations without subtracting freed memory.
|
||||
volatile LONG m_nLifetimeAllocations;
|
||||
LONG m_nOldLifetimeAllocations;
|
||||
};
|
||||
|
||||
void CHeapMemAlloc::Init( bool bZeroMemory )
|
||||
{
|
||||
m_HeapFlags = bZeroMemory ? HEAP_ZERO_MEMORY : 0;
|
||||
|
||||
// Can't use Msg here because it isn't necessarily initialized yet.
|
||||
if ( s_bPageHeapEnabled )
|
||||
{
|
||||
OutputDebugStringA("PageHeap is on. Memory use will be larger than normal.\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugStringA("PageHeap is off. Memory use will be normal.\n" );
|
||||
}
|
||||
if( bZeroMemory )
|
||||
{
|
||||
OutputDebugStringA( " HEAP_ZERO_MEMORY is specified.\n" );
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t CHeapMemAlloc::GetSize_Unaligned( void *pMem ) const
|
||||
{
|
||||
return HeapSize( m_heap, 0, pMem ) - FORCED_ALIGNMENT;
|
||||
}
|
||||
|
||||
inline void *CHeapMemAlloc::Alloc_Unaligned( size_t nSize )
|
||||
{
|
||||
// Ensure that the constructor has run already. Poorly defined
|
||||
// order of construction can result in the allocator being used
|
||||
// before it is constructed. Which could be bad.
|
||||
if ( !m_heap )
|
||||
__debugbreak();
|
||||
|
||||
size_t nAdjustedSize = nSize + FORCED_ALIGNMENT;
|
||||
|
||||
void* pMem = HeapAlloc( m_heap, m_HeapFlags, nAdjustedSize );
|
||||
if ( !pMem )
|
||||
{
|
||||
OutOfMemory( nSize );
|
||||
}
|
||||
|
||||
InterlockedAddSizeT( &m_nOutstandingBytes, nSize );
|
||||
InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, RoundUpToPage( nAdjustedSize ) );
|
||||
InterlockedIncrement( &m_nOutstandingAllocations );
|
||||
InterlockedIncrement( &m_nLifetimeAllocations );
|
||||
return pMem;
|
||||
}
|
||||
|
||||
inline void *CHeapMemAlloc::Realloc_Unaligned( void *pMem, size_t nSize )
|
||||
{
|
||||
size_t nOldSize = GetSize_Unaligned( pMem );
|
||||
size_t nOldAdjustedSize = nOldSize + FORCED_ALIGNMENT;
|
||||
size_t nAdjustedSize = nSize + FORCED_ALIGNMENT;
|
||||
|
||||
void* pNewMem = HeapReAlloc( m_heap, m_HeapFlags, pMem, nAdjustedSize );
|
||||
if ( !pNewMem )
|
||||
{
|
||||
OutOfMemory( nSize );
|
||||
}
|
||||
|
||||
InterlockedAddSizeT( &m_nOutstandingBytes, nSize - nOldSize );
|
||||
InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, RoundUpToPage( nAdjustedSize ) );
|
||||
InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, 0 - RoundUpToPage( nOldAdjustedSize ) );
|
||||
// Outstanding allocation count isn't affected by Realloc, but lifetime allocation count is
|
||||
InterlockedIncrement( &m_nLifetimeAllocations );
|
||||
|
||||
return pNewMem;
|
||||
}
|
||||
|
||||
inline void CHeapMemAlloc::Free_Unaligned( void *pMem )
|
||||
{
|
||||
size_t nOldSize = GetSize_Unaligned( pMem );
|
||||
size_t nOldAdjustedSize = nOldSize + FORCED_ALIGNMENT;
|
||||
InterlockedAddSizeT( &m_nOutstandingBytes, 0 - nOldSize );
|
||||
InterlockedAddSizeT( &m_nOutstandingPageHeapBytes, 0 - RoundUpToPage( nOldAdjustedSize ) );
|
||||
InterlockedDecrement( &m_nOutstandingAllocations );
|
||||
|
||||
HeapFree( m_heap, 0, pMem );
|
||||
}
|
||||
|
||||
inline void CHeapMemAlloc::OutOfMemory( size_t nBytesAttempted )
|
||||
{
|
||||
// It is crucial to stop here, before calling DumpStats, because if an OOM failure happens
|
||||
// in the logging system then DumpStats will trigger it again. This is made more complicated
|
||||
// because CUtlBuffer will have updated its size but not its pointer, leading to a buffer
|
||||
// that thinks it has more room than it actually does.
|
||||
DebuggerBreakIfDebugging();
|
||||
// Having PageHeap enabled leads to lots of allocation failures. These
|
||||
// these crashes we either need to halt immediately on allocation failures,
|
||||
// or print a message and exit. Printing a message and exiting is better
|
||||
// for stress testing purposes.
|
||||
|
||||
DumpStats();
|
||||
char buffer[256];
|
||||
_snprintf( buffer, sizeof( buffer ), FILE_LINE_STRING "***** OUT OF MEMORY! attempted allocation size: %I64d ****\n", (uint64)nBytesAttempted );
|
||||
buffer[ ARRAYSIZE(buffer) - 1 ] = 0;
|
||||
// Can't use Msg() in a situation like this.
|
||||
Plat_DebugString( buffer );
|
||||
|
||||
// If page heap is enabled then exit cleanly to simplify stress testing.
|
||||
if ( !s_bPageHeapEnabled )
|
||||
{
|
||||
DebuggerBreakIfDebugging();
|
||||
}
|
||||
|
||||
Plat_ExitProcess( EXIT_FAILURE );
|
||||
}
|
||||
|
||||
inline void CHeapMemAlloc::DumpStats()
|
||||
{
|
||||
const size_t MB = 1024 * 1024;
|
||||
Msg( "Sorry -- no stats saved to file memstats.txt when the heap allocator is enabled.\n" );
|
||||
// Print requested memory.
|
||||
Msg( "%u MB allocated.\n", ( unsigned )( m_nOutstandingBytes / MB ) );
|
||||
// Print memory after rounding up to pages.
|
||||
Msg( "%u MB memory used assuming maximum PageHeap overhead.\n", ( unsigned )( m_nOutstandingPageHeapBytes / MB ));
|
||||
// Print memory after adding in reserved page after every allocation. Do 64-bit calculations
|
||||
// because the pageHeap required memory can easily go over 4 GB.
|
||||
__int64 pageHeapBytes = m_nOutstandingPageHeapBytes + m_nOutstandingAllocations * 4096LL;
|
||||
Msg( "%u MB address space used assuming maximum PageHeap overhead.\n", ( unsigned )( pageHeapBytes / MB ));
|
||||
Msg( "%u outstanding allocations (%d delta).\n", ( unsigned )m_nOutstandingAllocations, ( int )( m_nOutstandingAllocations - m_nOldOutstandingAllocations ) );
|
||||
Msg( "%u lifetime allocations (%u delta).\n", ( unsigned )m_nLifetimeAllocations, ( unsigned )( m_nLifetimeAllocations - m_nOldLifetimeAllocations ) );
|
||||
|
||||
// Update the numbers on outstanding and lifetime allocation counts so
|
||||
// that we can print out deltas.
|
||||
m_nOldOutstandingAllocations = m_nOutstandingAllocations;
|
||||
m_nOldLifetimeAllocations = m_nLifetimeAllocations;
|
||||
}
|
||||
|
||||
int CHeapMemAlloc::CrtCheckMemory( void )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// HeapValidate is supposed to check the entire heap for validity. However testing with
|
||||
// intentional heap corruption suggests that it does not. If a block is corrupted and
|
||||
// then HeapValidate is called on that block then this is detected, but the same corruption
|
||||
// is not detected when passing NULL as the pointer. But, better to have this functionality
|
||||
// supported than not.
|
||||
BOOL result = HeapValidate( m_heap, 0, NULL );
|
||||
|
||||
return (result != 0) ? 1 : 0;
|
||||
#else
|
||||
// HeapValidate does not exist on the Xbox 360.
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Alignment-enforcing wrappers
|
||||
#if ( FORCED_ALIGNMENT > 0 )
|
||||
ASSERT_INVARIANT( FORCED_ALIGNMENT < 256 ); // Alignment offset has to fit in 1 byte
|
||||
inline void *AlignPointer( void *pUnaligned )
|
||||
{
|
||||
// Offset the pointer to align it and store the offset in the previous byte
|
||||
byte nOffset = FORCED_ALIGNMENT - ( ((uintptr_t)pUnaligned) & ( FORCED_ALIGNMENT - 1 ) );
|
||||
byte *pAligned = ((byte*)pUnaligned) + nOffset;
|
||||
pAligned[ -1 ] = nOffset;
|
||||
return pAligned;
|
||||
}
|
||||
inline void *UnalignPointer( void *pAligned )
|
||||
{
|
||||
// Get the original unaligned pointer, using the offset stored by AlignPointer()
|
||||
byte *pUnaligned = (byte *)pAligned;
|
||||
byte nOffset = pUnaligned[ -1 ];
|
||||
pUnaligned -= nOffset;
|
||||
// Detect corruption of the offset byte (valid offsets range from 1 to FORCED_ALIGNMENT):
|
||||
if ( ((uintptr_t)pAligned) % FORCED_ALIGNMENT ) DebuggerBreakIfDebugging();
|
||||
if ( ( nOffset < 1 ) || ( nOffset > FORCED_ALIGNMENT ) ) DebuggerBreakIfDebugging();
|
||||
return pUnaligned;
|
||||
}
|
||||
#else // FORCED_ALIGNMENT
|
||||
inline void *AlignPointer( void *pUnaligned ) { return pUnaligned; }
|
||||
inline void *UnalignPointer( void *pAligned ) { return pAligned; }
|
||||
#endif // FORCED_ALIGNMENT
|
||||
|
||||
inline void *CHeapMemAlloc::Alloc( size_t nSize )
|
||||
{
|
||||
// NOTE: see IMemAlloc 'API Rules'
|
||||
//if ( !nSize )
|
||||
// return NULL;
|
||||
if ( !nSize )
|
||||
nSize = 1;
|
||||
|
||||
return AlignPointer( Alloc_Unaligned( nSize ) );
|
||||
}
|
||||
|
||||
inline void CHeapMemAlloc::Free( void *pMem )
|
||||
{
|
||||
// NOTE: see IMemAlloc 'API Rules'
|
||||
if ( !pMem )
|
||||
return;
|
||||
return Free_Unaligned( UnalignPointer( pMem ) );
|
||||
}
|
||||
|
||||
inline void *CHeapMemAlloc::Realloc( void *pMem, size_t nSize )
|
||||
{
|
||||
// NOTE: see IMemAlloc 'API Rules'
|
||||
if ( !pMem )
|
||||
{
|
||||
return Alloc( nSize );
|
||||
}
|
||||
if ( !nSize )
|
||||
{
|
||||
Free( pMem );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if ( FORCED_ALIGNMENT == 0 )
|
||||
|
||||
return Realloc_Unaligned( pMem, nSize );
|
||||
|
||||
#else // FORCED_ALIGNMENT
|
||||
|
||||
// Can't use ReAlloc_Unaligned because the leading padding varies, so it will memcpy incorrectly.
|
||||
void * pUnaligned = UnalignPointer( pMem );
|
||||
size_t nOldSize = GetSize_Unaligned( pUnaligned );
|
||||
void * pAligned = AlignPointer( Alloc_Unaligned( nSize ) );
|
||||
memcpy( pAligned, pMem, MIN( nSize, nOldSize ) );
|
||||
Free_Unaligned( pUnaligned );
|
||||
return pAligned;
|
||||
|
||||
#endif // FORCED_ALIGNMENT
|
||||
}
|
||||
|
||||
inline size_t CHeapMemAlloc::GetSize( void *pMem )
|
||||
{
|
||||
// NOTE: see IMemAlloc 'API Rules'
|
||||
if ( !pMem )
|
||||
return 0;
|
||||
return GetSize_Unaligned( UnalignPointer( pMem ) );
|
||||
}
|
||||
|
||||
void EnableHeapMemAlloc( bool bZeroMemory )
|
||||
{
|
||||
// Place this here to guarantee it is constructed
|
||||
// before we call Init.
|
||||
static CHeapMemAlloc s_HeapMemAlloc;
|
||||
static bool s_initCalled = false;
|
||||
|
||||
if ( !s_initCalled )
|
||||
{
|
||||
s_HeapMemAlloc.Init( bZeroMemory );
|
||||
SetAllocatorObject( &s_HeapMemAlloc );
|
||||
s_initCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ReserveBottomMemory()
|
||||
{
|
||||
// If we are running a 64-bit build then reserve all addresses below the
|
||||
// 4 GB line to push as many pointers as possible above the line.
|
||||
#ifdef PLATFORM_WINDOWS_PC64
|
||||
// Avoid the cost of calling this multiple times.
|
||||
static bool s_initialized = false;
|
||||
if ( s_initialized )
|
||||
return;
|
||||
s_initialized = true;
|
||||
|
||||
// Start by reserving large blocks of memory. When those reservations
|
||||
// have exhausted the bottom 4 GB then halve the size and try again.
|
||||
// The granularity for reserving address space is 64 KB so if we wanted
|
||||
// to reserve every single page we would need to continue down to 64 KB.
|
||||
// However stopping at 1 MB is sufficient because it prevents the Windows
|
||||
// heap (and dlmalloc and the small block heap) from grabbing address space
|
||||
|
||||
// from the bottom 4 GB, while still allowing Steam to allocate a few pages
|
||||
// for setting up detours.
|
||||
const size_t LOW_MEM_LINE = 0x100000000LL;
|
||||
size_t totalReservation = 0;
|
||||
size_t numVAllocs = 0;
|
||||
size_t numHeapAllocs = 0;
|
||||
for ( size_t blockSize = 256 * 1024 * 1024; blockSize >= 1024 * 1024; blockSize /= 2 )
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
void* p = VirtualAlloc( 0, blockSize, MEM_RESERVE, PAGE_NOACCESS );
|
||||
if ( !p )
|
||||
break;
|
||||
|
||||
if ( (size_t)p >= LOW_MEM_LINE )
|
||||
{
|
||||
// We don't need this memory, so release it completely.
|
||||
VirtualFree( p, 0, MEM_RELEASE );
|
||||
break;
|
||||
}
|
||||
|
||||
totalReservation += blockSize;
|
||||
++numVAllocs;
|
||||
}
|
||||
}
|
||||
|
||||
// Now repeat the same process but making heap allocations, to use up the
|
||||
// already committed heap blocks that are below the 4 GB line. Now we start
|
||||
// with 64-KB allocations and proceed down to 16-byte allocations.
|
||||
HANDLE heap = GetProcessHeap();
|
||||
for ( size_t blockSize = 64 * 1024; blockSize >= 16; blockSize /= 2 )
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
void* p = HeapAlloc( heap, 0, blockSize );
|
||||
if ( !p )
|
||||
break;
|
||||
|
||||
if ( (size_t)p >= LOW_MEM_LINE )
|
||||
{
|
||||
// We don't need this memory, so release it completely.
|
||||
HeapFree( heap, 0, p );
|
||||
break;
|
||||
}
|
||||
|
||||
totalReservation += blockSize;
|
||||
++numHeapAllocs;
|
||||
}
|
||||
}
|
||||
|
||||
// Print diagnostics showing how many allocations we had to make in order to
|
||||
// reserve all of low memory. In one test run it took 55 virtual allocs and
|
||||
// 85 heap allocs. Note that since the process may have multiple heaps (each
|
||||
// CRT seems to have its own) there is likely to be a few MB of address space
|
||||
// that was previously reserved and is available to be handed out by some allocators.
|
||||
//char buffer[1000];
|
||||
//sprintf_s( buffer, "Reserved %1.3f MB (%d vallocs, %d heap allocs) to keep allocations out of low-memory.\n",
|
||||
// totalReservation / (1024 * 1024.0), (int)numVAllocs, (int)numHeapAllocs );
|
||||
// Can't use Msg here because it isn't necessarily initialized yet.
|
||||
//OutputDebugString( buffer );
|
||||
#endif
|
||||
}
|
||||
// Check whether PageHeap (part of App Verifier) has been enabled for this process.
|
||||
// It specifically checks whether it was enabled by the EnableAppVerifier.bat
|
||||
// batch file. This can be used to automatically enable -processheap when
|
||||
// App Verifier is in use.
|
||||
static bool IsPageHeapEnabled( bool& bETWHeapEnabled )
|
||||
{
|
||||
// Assume false.
|
||||
bool result = false;
|
||||
bETWHeapEnabled = false;
|
||||
|
||||
// First we get the application's name so we can look in the registry
|
||||
// for App Verifier settings.
|
||||
HMODULE exeHandle = GetModuleHandle( 0 );
|
||||
if ( exeHandle )
|
||||
{
|
||||
char appName[ MAX_PATH ];
|
||||
if ( GetModuleFileNameA( exeHandle, appName, ARRAYSIZE( appName ) ) )
|
||||
{
|
||||
// Guarantee null-termination -- not guaranteed on Windows XP!
|
||||
appName[ ARRAYSIZE( appName ) - 1 ] = 0;
|
||||
// Find the file part of the name.
|
||||
const char* pFilePart = strrchr( appName, '\\' );
|
||||
if ( pFilePart )
|
||||
{
|
||||
++pFilePart;
|
||||
size_t len = strlen( pFilePart );
|
||||
if ( len > 0 && pFilePart[ len - 1 ] == ' ' )
|
||||
{
|
||||
OutputDebugStringA( "Trailing space on executable name! This will cause Application Verifier and ETW Heap tracing to fail!\n" );
|
||||
DebuggerBreakIfDebugging();
|
||||
}
|
||||
|
||||
// Generate the key name for App Verifier settings for this process.
|
||||
char regPathName[ MAX_PATH ];
|
||||
_snprintf( regPathName, ARRAYSIZE( regPathName ),
|
||||
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\%s",
|
||||
pFilePart );
|
||||
regPathName[ ARRAYSIZE( regPathName ) - 1 ] = 0;
|
||||
|
||||
HKEY key;
|
||||
LONG regResult = RegOpenKeyA( HKEY_LOCAL_MACHINE,
|
||||
regPathName,
|
||||
&key );
|
||||
if ( regResult == ERROR_SUCCESS )
|
||||
{
|
||||
// If PageHeapFlags exists then that means that App Verifier is enabled
|
||||
// for this application. The StackTraceDatabaseSizeInMB is only
|
||||
// set by Valve's enabling batch file so this indicates that
|
||||
// a developer at Valve is using App Verifier.
|
||||
if ( RegQueryValueExA( key, "StackTraceDatabaseSizeInMB", 0, NULL, NULL, NULL ) == ERROR_SUCCESS &&
|
||||
RegQueryValueExA( key, "PageHeapFlags", 0, NULL, NULL, NULL) == ERROR_SUCCESS )
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
|
||||
if ( RegQueryValueExA( key, "TracingFlags", 0, NULL, NULL, NULL) == ERROR_SUCCESS )
|
||||
bETWHeapEnabled = true;
|
||||
|
||||
RegCloseKey( key );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check for various allocator overrides such as -processheap and -reservelowmem.
|
||||
// Returns true if -processheap is enabled, by a command line switch or other method.
|
||||
bool CheckWindowsAllocSettings( const char* upperCommandLine )
|
||||
{
|
||||
// Are we doing ETW heap profiling?
|
||||
bool bETWHeapEnabled = false;
|
||||
s_bPageHeapEnabled = IsPageHeapEnabled( bETWHeapEnabled );
|
||||
|
||||
// Should we reserve the bottom 4 GB of RAM in order to flush out pointer
|
||||
// truncation bugs? This helps ensure 64-bit compatibility.
|
||||
// However this needs to be off by default to avoid causing compatibility problems,
|
||||
// with Steam detours and other systems. It should also be disabled when PageHeap
|
||||
// is on because for some reason the combination turns into 4 GB of working set, which
|
||||
// can easily cause problems.
|
||||
if ( strstr( upperCommandLine, "-RESERVELOWMEM" ) && !s_bPageHeapEnabled )
|
||||
ReserveBottomMemory();
|
||||
|
||||
// Uninitialized data, including pointers, is often set to 0xFFEEFFEE.
|
||||
// If we reserve that block of memory then we can turn these pointer
|
||||
// dereferences into crashes a little bit earlier and more reliably.
|
||||
// We don't really care whether this allocation succeeds, but it's
|
||||
// worth trying. Note that we do this in all cases -- whether we are using
|
||||
// -processheap or not.
|
||||
VirtualAlloc( (void*)0xFFEEFFEE, 1, MEM_RESERVE, PAGE_NOACCESS );
|
||||
|
||||
// Enable application termination (breakpoint) on heap corruption. This is
|
||||
// better than trying to patch it up and continue, both from a security and
|
||||
// a bug-finding point of view. Do this always on Windows since the heap is
|
||||
// used by video drivers and other in-proc components.
|
||||
//HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
|
||||
// The HeapEnableTerminationOnCorruption requires a recent platform SDK,
|
||||
// so fake it up.
|
||||
#if defined(PLATFORM_WINDOWS_PC)
|
||||
HeapSetInformation( NULL, (HEAP_INFORMATION_CLASS)1, NULL, 0 );
|
||||
#endif
|
||||
|
||||
bool bZeroMemory = false;
|
||||
bool bProcessHeap = false;
|
||||
// Should we force using the process heap? This is handy for gathering memory
|
||||
// statistics with ETW/xperf. When using App Verifier -processheap is automatically
|
||||
// turned on.
|
||||
if ( strstr( upperCommandLine, "-PROCESSHEAP" ) )
|
||||
{
|
||||
bProcessHeap = true;
|
||||
bZeroMemory = !!strstr( upperCommandLine, "-PROCESSHEAPZEROMEM" );
|
||||
}
|
||||
|
||||
// Unless specifically disabled, turn on -processheap if pageheap or ETWHeap tracing
|
||||
// are enabled.
|
||||
if ( !strstr( upperCommandLine, "-NOPROCESSHEAP" ) && ( s_bPageHeapEnabled || bETWHeapEnabled ) )
|
||||
bProcessHeap = true;
|
||||
|
||||
if ( bProcessHeap )
|
||||
{
|
||||
// Now all allocations will go through the system heap.
|
||||
EnableHeapMemAlloc( bZeroMemory );
|
||||
}
|
||||
|
||||
return bProcessHeap;
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#endif // !NO_MALLOC_OVERRIDE
|
||||
2931
tier0/memstd.cpp
Normal file
2931
tier0/memstd.cpp
Normal file
File diff suppressed because it is too large
Load Diff
518
tier0/memstd.h
Normal file
518
tier0/memstd.h
Normal file
@@ -0,0 +1,518 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// NOTE! This should never be called directly from leaf code
|
||||
// Just use new,delete,malloc,free etc. They will call into this eventually
|
||||
// Note: we might want to move to Large Page memory for servers (GC). See https://msdn.microsoft.com/en-us/library/windows/desktop/aa366720(v=vs.85).aspx
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#if IS_WINDOWS_PC
|
||||
#define WIN_32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#define VA_COMMIT_FLAGS MEM_COMMIT
|
||||
#define VA_RESERVE_FLAGS MEM_RESERVE
|
||||
#elif defined( _X360 )
|
||||
#undef Verify
|
||||
#define _XBOX
|
||||
#include <xtl.h>
|
||||
#undef _XBOX
|
||||
#include "xbox/xbox_win32stubs.h"
|
||||
#define VA_COMMIT_FLAGS (MEM_COMMIT|MEM_NOZERO|MEM_LARGE_PAGES)
|
||||
#define VA_RESERVE_FLAGS (MEM_RESERVE|MEM_LARGE_PAGES)
|
||||
#elif defined( _PS3 )
|
||||
#include "sys/memory.h"
|
||||
#include "sys/mempool.h"
|
||||
#include "sys/process.h"
|
||||
#include <sys/vm.h>
|
||||
|
||||
#endif
|
||||
|
||||
//#include <malloc.h>
|
||||
#include <algorithm>
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/memalloc.h"
|
||||
#include "tier0/threadtools.h"
|
||||
#include "tier0/tslist.h"
|
||||
#include "mem_helpers.h"
|
||||
|
||||
#ifndef _PS3
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
|
||||
#define MIN_SBH_BLOCK 8
|
||||
#define MIN_SBH_ALIGN 8
|
||||
#ifdef PLATFORM_WINDOWS_PC64
|
||||
#define MAX_SBH_BLOCK (16*1024*1024)
|
||||
#define SBH_BLOCK_LOOKUP_GRANULARITY 4
|
||||
#else
|
||||
#define MAX_SBH_BLOCK 2048
|
||||
#define SBH_BLOCK_LOOKUP_GRANULARITY 2
|
||||
#endif
|
||||
//#define MAX_POOL_REGION (4*1024*1024)
|
||||
|
||||
#if defined( PLATFORM_WINDOWS_PC64 )
|
||||
#define SBH_LARGE_MEM 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef PLATFORM_WINDOWS_PC64
|
||||
#define NUM_POOLS 47 // block sizes are more granular on 64-bit
|
||||
#else
|
||||
#define NUM_POOLS 42
|
||||
#endif
|
||||
|
||||
#if defined( _WIN32 ) || defined( _PS3 )
|
||||
// Small block heap on win64 is expecting SLIST_HEADER to look different than it does on win64. It was disabled for a long time because of this.
|
||||
#define MEM_SBH_ENABLED 1
|
||||
#endif
|
||||
|
||||
#if !defined(_CERT) && ( defined(_X360) || defined(_PS3) || defined( PLATFORM_WINDOWS_PC64 ) || DEVELOPMENT_ONLY )
|
||||
#define TRACK_SBH_COUNTS
|
||||
#endif
|
||||
|
||||
#if defined(_X360)
|
||||
|
||||
// 360 uses a 48MB primary (physical) SBH and 10MB secondary (virtual) SBH, with no fallback
|
||||
#define MBYTES_PRIMARY_SBH 32
|
||||
#define MEMALLOC_USE_SECONDARY_SBH
|
||||
#define MBYTES_SECONDARY_SBH 10
|
||||
#define MEMALLOC_NO_FALLBACK
|
||||
|
||||
#elif defined(_PS3)
|
||||
|
||||
// PS3 uses just a 32MB SBH - this was enough to avoid overflow when Portal 2 shipped.
|
||||
// NOTE: when Steam uses the game's tier0 allocator (see memalloc.h), we increase the size
|
||||
// of the SBH and MBH (see memstd.cpp) to accommodate those extra allocations.
|
||||
#define MBYTES_PRIMARY_SBH ( 34 + MBYTES_STEAM_SBH_USAGE )
|
||||
#define MEMALLOC_NO_FALLBACK
|
||||
|
||||
#else // _X360 | _PS3
|
||||
|
||||
#ifdef PLATFORM_WINDOWS_PC64
|
||||
// in 64-bit server , with huge pages and many pools, we need a lot to start with, otherwise we'll start falling into fallback SBH right away
|
||||
#define MBYTES_PRIMARY_SBH 256
|
||||
#else
|
||||
|
||||
// Other platforms use a 48MB primary SBH and a (32MB) fallback SBH
|
||||
// CSGO was hitting falling out of the small block heap (many expensive calls
|
||||
// to CompactOnFail) with it set to 48 MB, so let's try higher.
|
||||
#define MBYTES_PRIMARY_SBH 64
|
||||
#endif
|
||||
|
||||
#endif // _X360 | _PS3
|
||||
|
||||
#define MEMSTD_COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Small block pool
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CFreeList : public CTSListBase
|
||||
{
|
||||
public:
|
||||
void Push( void *p ) { CTSListBase::Push( (TSLNodeBase_t *)p ); }
|
||||
byte *Pop() { return (byte *)CTSListBase::Pop(); }
|
||||
};
|
||||
|
||||
template <typename CAllocator>
|
||||
class CSmallBlockHeap;
|
||||
|
||||
template <typename CAllocator>
|
||||
class CSmallBlockPool
|
||||
{
|
||||
public:
|
||||
CSmallBlockPool()
|
||||
{
|
||||
m_nBlockSize = 0;
|
||||
m_nCommittedPages = 0;
|
||||
m_pFirstPage = NULL;
|
||||
}
|
||||
|
||||
void Init( unsigned nBlockSize );
|
||||
size_t GetBlockSize();
|
||||
void *Alloc();
|
||||
void Free( void *p );
|
||||
int CountFreeBlocks();
|
||||
size_t GetCommittedSize();
|
||||
int CountCommittedBlocks();
|
||||
int CountAllocatedBlocks();
|
||||
size_t Compact( bool bIncremental );
|
||||
bool Validate();
|
||||
|
||||
enum
|
||||
{
|
||||
BYTES_PAGE = CAllocator::BYTES_PAGE,
|
||||
NOT_COMMITTED = -1
|
||||
};
|
||||
|
||||
private:
|
||||
typedef CSmallBlockHeap<CAllocator> CHeap;
|
||||
friend class CSmallBlockHeap<CAllocator>;
|
||||
|
||||
struct PageStatus_t : public TSLNodeBase_t
|
||||
{
|
||||
PageStatus_t()
|
||||
{
|
||||
m_pPool = NULL;
|
||||
m_nAllocated = NOT_COMMITTED;
|
||||
m_pNextPageInPool = NULL;
|
||||
}
|
||||
|
||||
CSmallBlockPool<CAllocator> * m_pPool;
|
||||
PageStatus_t * m_pNextPageInPool;
|
||||
CInterlockedInt m_nAllocated;
|
||||
CTSListBase m_SortList;
|
||||
};
|
||||
|
||||
struct SharedData_t
|
||||
{
|
||||
CAllocator m_Allocator;
|
||||
CTSListBase m_FreePages;
|
||||
CThreadSpinRWLock m_Lock;
|
||||
size_t m_numPages;
|
||||
byte * m_pNextBlock;
|
||||
byte * m_pBase;
|
||||
byte * m_pLimit;
|
||||
PageStatus_t m_PageStatus[ 114688 ]; // This is sufficient to hold pages for SBH sized up to 1,792 MB (almost 2GB) in 32-bit SBH with 16 Kb pages; or up to 56Gb worth of data in 64-bit GC with fixed-size allocator with 512Kb pages
|
||||
};
|
||||
|
||||
static int PageSort( const void *p1, const void *p2 ) ;
|
||||
bool RemovePagesFromFreeList( byte **pPages, int nPages, bool bSortList );
|
||||
|
||||
void ValidateFreelist( SharedData_t *pSharedData );
|
||||
|
||||
CFreeList m_FreeList;
|
||||
|
||||
CInterlockedPtr<byte> m_pNextAlloc;
|
||||
|
||||
PageStatus_t * m_pFirstPage;
|
||||
unsigned m_nBlockSize;
|
||||
unsigned m_nCommittedPages;
|
||||
|
||||
CThreadFastMutex m_CommitMutex;
|
||||
|
||||
CInterlockedInt m_nIsCompact;
|
||||
|
||||
#ifdef TRACK_SBH_COUNTS
|
||||
CInterlockedInt m_nFreeBlocks;
|
||||
#endif
|
||||
|
||||
static SharedData_t *GetSharedData()
|
||||
{
|
||||
return &gm_SharedData;
|
||||
}
|
||||
|
||||
static SharedData_t gm_SharedData;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Small block heap (multi-pool)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template <typename CAllocator>
|
||||
class CSmallBlockHeap
|
||||
{
|
||||
public:
|
||||
CSmallBlockHeap();
|
||||
bool ShouldUse( size_t nBytes );
|
||||
bool IsOwner( void * p );
|
||||
void *Alloc( size_t nBytes );
|
||||
void *Realloc( void *p, size_t nBytes );
|
||||
void Free( void *p );
|
||||
size_t GetSize( void *p );
|
||||
void DumpStats( const char *pszTag, FILE *pFile = NULL, IMemAlloc::DumpStatsFormat_t nFormat = IMemAlloc::FORMAT_TEXT );
|
||||
void Usage( size_t &bytesCommitted, size_t &bytesAllocated );
|
||||
size_t Compact( bool bIncremental );
|
||||
bool Validate();
|
||||
void InitPools( const uint *pSizes );
|
||||
|
||||
enum
|
||||
{
|
||||
BYTES_PAGE = CAllocator::BYTES_PAGE
|
||||
};
|
||||
|
||||
private:
|
||||
typedef CSmallBlockPool<CAllocator> CPool;
|
||||
typedef struct CSmallBlockPool<CAllocator>::SharedData_t SharedData_t;
|
||||
|
||||
CPool *FindPool( size_t nBytes );
|
||||
CPool *FindPool( void *p );
|
||||
|
||||
// Map size to a pool address to a pool
|
||||
CPool *m_PoolLookup[ MAX_SBH_BLOCK >> SBH_BLOCK_LOOKUP_GRANULARITY ];
|
||||
CPool m_Pools[NUM_POOLS];
|
||||
|
||||
SharedData_t *m_pSharedData;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
class CStdMemAlloc : public IMemAlloc
|
||||
{
|
||||
public:
|
||||
CStdMemAlloc();
|
||||
|
||||
// Internal versions
|
||||
void *InternalAlloc( int region, size_t nSize );
|
||||
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
||||
void *InternalAllocAligned( int region, size_t nSize, size_t align );
|
||||
#endif
|
||||
void *InternalAllocFromPools( size_t nSize );
|
||||
void *InternalRealloc( void *pMem, size_t nSize );
|
||||
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
||||
void *InternalReallocAligned( void *pMem, size_t nSize, size_t align );
|
||||
#endif
|
||||
void InternalFree( void *pMem );
|
||||
|
||||
size_t InternalCompact( bool bSmallBlockOnly );
|
||||
void CompactOnFail();
|
||||
|
||||
// Release versions
|
||||
virtual void *Alloc( size_t nSize );
|
||||
virtual void *Realloc( void *pMem, size_t nSize );
|
||||
virtual void Free( void *pMem );
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
|
||||
|
||||
// Debug versions
|
||||
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
|
||||
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
||||
virtual void Free( void *pMem, const char *pFileName, int nLine );
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
||||
|
||||
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
||||
virtual void *AllocAlign( size_t nSize, size_t align );
|
||||
virtual void *AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine );
|
||||
virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align );
|
||||
virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine );
|
||||
#endif
|
||||
|
||||
virtual void *RegionAlloc( int region, size_t nSize );
|
||||
virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine );
|
||||
|
||||
// Returns size of a particular allocation
|
||||
virtual size_t GetSize( void *pMem );
|
||||
|
||||
// Force file + line information for an allocation
|
||||
virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
|
||||
virtual void PopAllocDbgInfo();
|
||||
|
||||
virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc );
|
||||
virtual int CrtSetReportMode( int nReportType, int nReportMode );
|
||||
virtual int CrtIsValidHeapPointer( const void *pMem );
|
||||
virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
|
||||
virtual int CrtCheckMemory( void );
|
||||
virtual int CrtSetDbgFlag( int nNewFlag );
|
||||
virtual void CrtMemCheckpoint( _CrtMemState *pState );
|
||||
void* CrtSetReportFile( int nRptType, void* hFile );
|
||||
void* CrtSetReportHook( void* pfnNewHook );
|
||||
int CrtDbgReport( int nRptType, const char * szFile,
|
||||
int nLine, const char * szModule, const char * pMsg );
|
||||
virtual int heapchk();
|
||||
|
||||
virtual void DumpStats();
|
||||
virtual void DumpStatsFileBase( char const *pchFileBase, DumpStatsFormat_t nFormat = FORMAT_TEXT ) OVERRIDE;
|
||||
virtual size_t ComputeMemoryUsedBy( char const *pchSubStr );
|
||||
virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory );
|
||||
|
||||
virtual bool IsDebugHeap() { return false; }
|
||||
|
||||
virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) {}
|
||||
virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) {}
|
||||
virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) {}
|
||||
|
||||
virtual int GetVersion() { return MEMALLOC_VERSION; }
|
||||
|
||||
virtual void OutOfMemory( size_t nBytesAttempted = 0 ) { SetCRTAllocFailed( nBytesAttempted ); }
|
||||
|
||||
virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes );
|
||||
|
||||
virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats );
|
||||
|
||||
virtual void CompactHeap();
|
||||
virtual void CompactIncremental();
|
||||
|
||||
virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler );
|
||||
size_t CallAllocFailHandler( size_t nBytes ) { return (*m_pfnFailHandler)( nBytes); }
|
||||
|
||||
virtual uint32 GetDebugInfoSize() { return 0; }
|
||||
virtual void SaveDebugInfo( void *pvDebugInfo ) { }
|
||||
virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
|
||||
virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
|
||||
|
||||
static size_t DefaultFailHandler( size_t );
|
||||
void DumpBlockStats( void *p ) {}
|
||||
|
||||
#if MEM_SBH_ENABLED
|
||||
class CVirtualAllocator
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
#if SBH_LARGE_MEM
|
||||
BYTES_PAGE = ( 16 * 1024 * 1024 ),
|
||||
TOTAL_BYTES = ( 1ull * 1024 * 1024 * 1024 ),
|
||||
#else
|
||||
BYTES_PAGE = (64*1024),
|
||||
TOTAL_BYTES = (32*1024*1024),
|
||||
#endif
|
||||
MIN_RESERVE_PAGES = 4,
|
||||
};
|
||||
|
||||
byte *AllocatePoolMemory()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (byte *)VirtualAlloc( NULL, TOTAL_BYTES, VA_RESERVE_FLAGS, PAGE_NOACCESS );
|
||||
#elif defined( _PS3 )
|
||||
Error( "" );
|
||||
return NULL;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
inline size_t GetTotalBytes() const { return TOTAL_BYTES; }
|
||||
inline size_t GetNumPages() const { return TOTAL_BYTES/BYTES_PAGE; }
|
||||
inline size_t GetMinReservePages() const { return MIN_RESERVE_PAGES; }
|
||||
|
||||
static bool IsVirtual()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Decommit( void *pPage )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ( VirtualFree( pPage, BYTES_PAGE, MEM_DECOMMIT ) != 0 );
|
||||
#elif defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Commit( void *pPage )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ( VirtualAlloc( pPage, BYTES_PAGE, VA_COMMIT_FLAGS, PAGE_READWRITE ) != NULL );
|
||||
#elif defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
typedef CSmallBlockHeap<CVirtualAllocator> CVirtualSmallBlockHeap;
|
||||
|
||||
template <size_t SIZE_MB, bool bPhysical>
|
||||
class CFixedAllocator
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
#if SBH_LARGE_MEM
|
||||
BYTES_PAGE = ( 16 * 1024 * 1024 ), // use really coarse pages on 64-bit GC
|
||||
#else
|
||||
BYTES_PAGE = (16*1024),
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
// Private info:
|
||||
enum
|
||||
{
|
||||
TOTAL_BYTES = (SIZE_MB*1024*1024),
|
||||
};
|
||||
|
||||
public:
|
||||
byte *AllocatePoolMemory()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef _X360
|
||||
if ( bPhysical )
|
||||
return (byte *)XPhysicalAlloc( TOTAL_BYTES, MAXULONG_PTR, 4096, PAGE_READWRITE | MEM_16MB_PAGES );
|
||||
#endif
|
||||
size_t numBytesToAllocate = TOTAL_BYTES;
|
||||
if ( bPhysical )
|
||||
{
|
||||
// Allow command line override
|
||||
extern size_t g_nSBHOverride;
|
||||
numBytesToAllocate = g_nSBHOverride;
|
||||
}
|
||||
return (byte *)VirtualAlloc( NULL, numBytesToAllocate, VA_COMMIT_FLAGS, PAGE_READWRITE );
|
||||
#elif defined( _PS3 )
|
||||
// TODO: release this section on shutdown (use GetMemorySectionForAddress)
|
||||
extern IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes );
|
||||
IVirtualMemorySection *pSection = VirtualMemoryManager_AllocateVirtualMemorySection( TOTAL_BYTES );
|
||||
if ( !pSection )
|
||||
Error( "CFixedAllocator::AllocatePoolMemory() failed in VirtualMemoryManager_AllocateVirtualMemorySection\n" );
|
||||
if ( !pSection->CommitPages( pSection->GetBaseAddress(), TOTAL_BYTES ) )
|
||||
Error( "CFixedAllocator::AllocatePoolMemory() failed in IVirtualMemorySection::CommitPages\n" );
|
||||
return reinterpret_cast<byte *>( pSection->GetBaseAddress() );
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
inline size_t GetTotalBytes() const
|
||||
{
|
||||
if ( bPhysical )
|
||||
{
|
||||
// Allow command line override
|
||||
extern size_t g_nSBHOverride;
|
||||
return g_nSBHOverride;
|
||||
}
|
||||
else
|
||||
return TOTAL_BYTES;
|
||||
}
|
||||
inline size_t GetNumPages() const { return GetTotalBytes()/BYTES_PAGE; }
|
||||
inline size_t GetMinReservePages() const { return GetNumPages(); }
|
||||
|
||||
static bool IsVirtual()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decommit( void *pPage )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Commit( void *pPage )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef CSmallBlockHeap<CFixedAllocator< MBYTES_PRIMARY_SBH, true> > CFixedSmallBlockHeap;
|
||||
#ifdef MEMALLOC_USE_SECONDARY_SBH
|
||||
typedef CSmallBlockHeap<CFixedAllocator< MBYTES_SECONDARY_SBH, false> > CFixedVirtualSmallBlockHeap; // @TODO: move back into above heap if number stays at 16 [7/15/2009 tom]
|
||||
#endif
|
||||
|
||||
CFixedSmallBlockHeap m_PrimarySBH;
|
||||
#ifdef MEMALLOC_USE_SECONDARY_SBH
|
||||
CFixedVirtualSmallBlockHeap m_SecondarySBH;
|
||||
#endif
|
||||
#ifndef MEMALLOC_NO_FALLBACK
|
||||
CVirtualSmallBlockHeap m_FallbackSBH;
|
||||
#endif
|
||||
|
||||
#endif // MEM_SBH_ENABLED
|
||||
|
||||
|
||||
virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment );
|
||||
|
||||
virtual size_t MemoryAllocFailed();
|
||||
|
||||
void SetCRTAllocFailed( size_t nMemSize );
|
||||
|
||||
MemAllocFailHandler_t m_pfnFailHandler;
|
||||
size_t m_sMemoryAllocFailed;
|
||||
CThreadFastMutex m_CompactMutex;
|
||||
bool m_bInCompact;
|
||||
};
|
||||
|
||||
#ifndef _PS3
|
||||
#pragma pack()
|
||||
#endif
|
||||
498
tier0/memvalidate.cpp
Normal file
498
tier0/memvalidate.cpp
Normal file
@@ -0,0 +1,498 @@
|
||||
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Memory allocation!
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#ifndef STEAM
|
||||
|
||||
#ifdef TIER0_VALIDATE_HEAP
|
||||
|
||||
#include <malloc.h>
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/memalloc.h"
|
||||
#include "mem_helpers.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
extern IMemAlloc *g_pActualAlloc;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NOTE! This should never be called directly from leaf code
|
||||
// Just use new,delete,malloc,free etc. They will call into this eventually
|
||||
//-----------------------------------------------------------------------------
|
||||
class CValidateAlloc : public IMemAlloc
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
HEAP_PREFIX_BUFFER_SIZE = 12,
|
||||
HEAP_SUFFIX_BUFFER_SIZE = 8,
|
||||
};
|
||||
|
||||
CValidateAlloc();
|
||||
|
||||
// Release versions
|
||||
virtual void *Alloc( size_t nSize );
|
||||
virtual void *Realloc( void *pMem, size_t nSize );
|
||||
virtual void Free( void *pMem );
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
|
||||
|
||||
// Debug versions
|
||||
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
|
||||
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
||||
virtual void Free( void *pMem, const char *pFileName, int nLine );
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
||||
|
||||
// Returns size of a particular allocation
|
||||
virtual size_t GetSize( void *pMem );
|
||||
|
||||
// Force file + line information for an allocation
|
||||
virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
|
||||
virtual void PopAllocDbgInfo();
|
||||
|
||||
virtual long CrtSetBreakAlloc( long lNewBreakAlloc );
|
||||
virtual int CrtSetReportMode( int nReportType, int nReportMode );
|
||||
virtual int CrtIsValidHeapPointer( const void *pMem );
|
||||
virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
|
||||
virtual int CrtCheckMemory( void );
|
||||
virtual int CrtSetDbgFlag( int nNewFlag );
|
||||
virtual void CrtMemCheckpoint( _CrtMemState *pState );
|
||||
void* CrtSetReportFile( int nRptType, void* hFile );
|
||||
void* CrtSetReportHook( void* pfnNewHook );
|
||||
int CrtDbgReport( int nRptType, const char * szFile,
|
||||
int nLine, const char * szModule, const char * pMsg );
|
||||
virtual int heapchk();
|
||||
|
||||
virtual void DumpStats() {}
|
||||
virtual void DumpStatsFileBase( char const *pchFileBase, DumpStatsFormat_t nFormat = FORMAT_TEXT ) OVERRIDE {}
|
||||
|
||||
virtual bool IsDebugHeap()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int GetVersion() { return MEMALLOC_VERSION; }
|
||||
|
||||
virtual void CompactHeap();
|
||||
virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler );
|
||||
|
||||
virtual uint32 GetDebugInfoSize() { return 0; }
|
||||
virtual void SaveDebugInfo( void *pvDebugInfo ) { }
|
||||
virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
|
||||
virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
|
||||
|
||||
private:
|
||||
struct HeapPrefix_t
|
||||
{
|
||||
HeapPrefix_t *m_pPrev;
|
||||
HeapPrefix_t *m_pNext;
|
||||
int m_nSize;
|
||||
unsigned char m_Prefix[HEAP_PREFIX_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
struct HeapSuffix_t
|
||||
{
|
||||
unsigned char m_Suffix[HEAP_SUFFIX_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
private:
|
||||
// Returns the actual debug info
|
||||
void GetActualDbgInfo( const char *&pFileName, int &nLine );
|
||||
|
||||
// Updates stats
|
||||
void RegisterAllocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime );
|
||||
void RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime );
|
||||
|
||||
HeapSuffix_t *Suffix( HeapPrefix_t *pPrefix );
|
||||
void *AllocationStart( HeapPrefix_t *pBase );
|
||||
HeapPrefix_t *PrefixFromAllocation( void *pAlloc );
|
||||
const HeapPrefix_t *PrefixFromAllocation( const void *pAlloc );
|
||||
|
||||
// Add to the list!
|
||||
void AddToList( HeapPrefix_t *pHeap, int nSize );
|
||||
|
||||
// Remove from the list!
|
||||
void RemoveFromList( HeapPrefix_t *pHeap );
|
||||
|
||||
// Validate the allocation
|
||||
bool ValidateAllocation( HeapPrefix_t *pHeap );
|
||||
|
||||
private:
|
||||
HeapPrefix_t *m_pFirstAllocation;
|
||||
char m_pPrefixImage[HEAP_PREFIX_BUFFER_SIZE];
|
||||
char m_pSuffixImage[HEAP_SUFFIX_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton...
|
||||
//-----------------------------------------------------------------------------
|
||||
static CValidateAlloc s_ValidateAlloc;
|
||||
|
||||
#ifdef _PS3
|
||||
|
||||
IMemAlloc *g_pMemAllocInternalPS3 = &s_ValidateAlloc;
|
||||
|
||||
#else // !_PS3
|
||||
|
||||
IMemAlloc *g_pMemAlloc = &s_ValidateAlloc;
|
||||
|
||||
#endif // _PS3
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor.
|
||||
//-----------------------------------------------------------------------------
|
||||
CValidateAlloc::CValidateAlloc()
|
||||
{
|
||||
m_pFirstAllocation = 0;
|
||||
memset( m_pPrefixImage, 0xBE, HEAP_PREFIX_BUFFER_SIZE );
|
||||
memset( m_pSuffixImage, 0xAF, HEAP_SUFFIX_BUFFER_SIZE );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Accessors...
|
||||
//-----------------------------------------------------------------------------
|
||||
inline CValidateAlloc::HeapSuffix_t *CValidateAlloc::Suffix( HeapPrefix_t *pPrefix )
|
||||
{
|
||||
return reinterpret_cast<HeapSuffix_t *>( (unsigned char*)( pPrefix + 1 ) + pPrefix->m_nSize );
|
||||
}
|
||||
|
||||
inline void *CValidateAlloc::AllocationStart( HeapPrefix_t *pBase )
|
||||
{
|
||||
return static_cast<void *>( pBase + 1 );
|
||||
}
|
||||
|
||||
inline CValidateAlloc::HeapPrefix_t *CValidateAlloc::PrefixFromAllocation( void *pAlloc )
|
||||
{
|
||||
if ( !pAlloc )
|
||||
return NULL;
|
||||
|
||||
return ((HeapPrefix_t*)pAlloc) - 1;
|
||||
}
|
||||
|
||||
inline const CValidateAlloc::HeapPrefix_t *CValidateAlloc::PrefixFromAllocation( const void *pAlloc )
|
||||
{
|
||||
return ((const HeapPrefix_t*)pAlloc) - 1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Add to the list!
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidateAlloc::AddToList( HeapPrefix_t *pHeap, int nSize )
|
||||
{
|
||||
pHeap->m_pPrev = NULL;
|
||||
pHeap->m_pNext = m_pFirstAllocation;
|
||||
if ( m_pFirstAllocation )
|
||||
{
|
||||
m_pFirstAllocation->m_pPrev = pHeap;
|
||||
}
|
||||
pHeap->m_nSize = nSize;
|
||||
|
||||
m_pFirstAllocation = pHeap;
|
||||
|
||||
HeapSuffix_t *pSuffix = Suffix( pHeap );
|
||||
memcpy( pHeap->m_Prefix, m_pPrefixImage, HEAP_PREFIX_BUFFER_SIZE );
|
||||
memcpy( pSuffix->m_Suffix, m_pSuffixImage, HEAP_SUFFIX_BUFFER_SIZE );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Remove from the list!
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidateAlloc::RemoveFromList( HeapPrefix_t *pHeap )
|
||||
{
|
||||
if ( !pHeap )
|
||||
return;
|
||||
|
||||
ValidateAllocation( pHeap );
|
||||
if ( pHeap->m_pPrev )
|
||||
{
|
||||
pHeap->m_pPrev->m_pNext = pHeap->m_pNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pFirstAllocation = pHeap->m_pNext;
|
||||
}
|
||||
|
||||
if ( pHeap->m_pNext )
|
||||
{
|
||||
pHeap->m_pNext->m_pPrev = pHeap->m_pPrev;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Validate the allocation
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CValidateAlloc::ValidateAllocation( HeapPrefix_t *pHeap )
|
||||
{
|
||||
HeapSuffix_t *pSuffix = Suffix( pHeap );
|
||||
|
||||
bool bOk = true;
|
||||
if ( memcmp( pHeap->m_Prefix, m_pPrefixImage, HEAP_PREFIX_BUFFER_SIZE ) )
|
||||
{
|
||||
bOk = false;
|
||||
}
|
||||
|
||||
if ( memcmp( pSuffix->m_Suffix, m_pSuffixImage, HEAP_SUFFIX_BUFFER_SIZE ) )
|
||||
{
|
||||
bOk = false;
|
||||
}
|
||||
|
||||
if ( !bOk )
|
||||
{
|
||||
Warning("Memory trash detected in allocation %X!\n", (void*)(pHeap+1) );
|
||||
Assert( 0 );
|
||||
}
|
||||
|
||||
return bOk;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Release versions
|
||||
//-----------------------------------------------------------------------------
|
||||
void *CValidateAlloc::Alloc( size_t nSize )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
HeapPrefix_t *pHeap = (HeapPrefix_t*)g_pActualAlloc->Alloc( nActualSize );
|
||||
AddToList( pHeap, nSize );
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
void *CValidateAlloc::Realloc( void *pMem, size_t nSize )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
pHeap = (HeapPrefix_t*)g_pActualAlloc->Realloc( pHeap, nActualSize );
|
||||
AddToList( pHeap, nSize );
|
||||
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
void CValidateAlloc::Free( void *pMem )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
g_pActualAlloc->Free( pHeap );
|
||||
}
|
||||
|
||||
void *CValidateAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
pHeap = (HeapPrefix_t*)g_pActualAlloc->Expand_NoLongerSupported( pHeap, nActualSize );
|
||||
AddToList( pHeap, nSize );
|
||||
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Debug versions
|
||||
//-----------------------------------------------------------------------------
|
||||
void *CValidateAlloc::Alloc( size_t nSize, const char *pFileName, int nLine )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
HeapPrefix_t *pHeap = (HeapPrefix_t*)g_pActualAlloc->Alloc( nActualSize, pFileName, nLine );
|
||||
AddToList( pHeap, nSize );
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
void *CValidateAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
pHeap = (HeapPrefix_t*)g_pActualAlloc->Realloc( pHeap, nActualSize, pFileName, nLine );
|
||||
AddToList( pHeap, nSize );
|
||||
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
void CValidateAlloc::Free( void *pMem, const char *pFileName, int nLine )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
g_pActualAlloc->Free( pHeap, pFileName, nLine );
|
||||
}
|
||||
|
||||
void *CValidateAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
pHeap = (HeapPrefix_t*)g_pActualAlloc->Expand_NoLongerSupported( pHeap, nActualSize, pFileName, nLine );
|
||||
AddToList( pHeap, nSize );
|
||||
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns size of a particular allocation
|
||||
//-----------------------------------------------------------------------------
|
||||
size_t CValidateAlloc::GetSize( void *pMem )
|
||||
{
|
||||
if ( !pMem )
|
||||
return CalcHeapUsed();
|
||||
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
return pHeap->m_nSize;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Force file + line information for an allocation
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidateAlloc::PushAllocDbgInfo( const char *pFileName, int nLine )
|
||||
{
|
||||
g_pActualAlloc->PushAllocDbgInfo( pFileName, nLine );
|
||||
}
|
||||
|
||||
void CValidateAlloc::PopAllocDbgInfo()
|
||||
{
|
||||
g_pActualAlloc->PopAllocDbgInfo( );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FIXME: Remove when we make our own heap! Crt stuff we're currently using
|
||||
//-----------------------------------------------------------------------------
|
||||
long CValidateAlloc::CrtSetBreakAlloc( long lNewBreakAlloc )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetBreakAlloc( lNewBreakAlloc );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtSetReportMode( int nReportType, int nReportMode )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetReportMode( nReportType, nReportMode );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtIsValidHeapPointer( const void *pMem )
|
||||
{
|
||||
const HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
return g_pActualAlloc->CrtIsValidHeapPointer( pHeap );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtIsValidPointer( const void *pMem, unsigned int size, int access )
|
||||
{
|
||||
const HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
return g_pActualAlloc->CrtIsValidPointer( pHeap, size, access );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtCheckMemory( void )
|
||||
{
|
||||
return g_pActualAlloc->CrtCheckMemory( );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtSetDbgFlag( int nNewFlag )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetDbgFlag( nNewFlag );
|
||||
}
|
||||
|
||||
void CValidateAlloc::CrtMemCheckpoint( _CrtMemState *pState )
|
||||
{
|
||||
g_pActualAlloc->CrtMemCheckpoint( pState );
|
||||
}
|
||||
|
||||
void* CValidateAlloc::CrtSetReportFile( int nRptType, void* hFile )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetReportFile( nRptType, hFile );
|
||||
}
|
||||
|
||||
void* CValidateAlloc::CrtSetReportHook( void* pfnNewHook )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetReportHook( pfnNewHook );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtDbgReport( int nRptType, const char * szFile,
|
||||
int nLine, const char * szModule, const char * pMsg )
|
||||
{
|
||||
return g_pActualAlloc->CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg );
|
||||
}
|
||||
|
||||
int CValidateAlloc::heapchk()
|
||||
{
|
||||
bool bOk = true;
|
||||
|
||||
// Validate the heap
|
||||
HeapPrefix_t *pHeap = m_pFirstAllocation;
|
||||
for( pHeap = m_pFirstAllocation; pHeap; pHeap = pHeap->m_pNext )
|
||||
{
|
||||
if ( !ValidateAllocation( pHeap ) )
|
||||
{
|
||||
bOk = false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
return bOk ? _HEAPOK : 0;
|
||||
#elif POSIX
|
||||
return bOk;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the actual debug info
|
||||
void CValidateAlloc::GetActualDbgInfo( const char *&pFileName, int &nLine )
|
||||
{
|
||||
g_pActualAlloc->GetActualDbgInfo( pFileName, nLine );
|
||||
}
|
||||
|
||||
// Updates stats
|
||||
void CValidateAlloc::RegisterAllocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime )
|
||||
{
|
||||
g_pActualAlloc->RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime );
|
||||
}
|
||||
|
||||
void CValidateAlloc::RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime )
|
||||
{
|
||||
g_pActualAlloc->RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime );
|
||||
}
|
||||
|
||||
void CValidateAlloc::CompactHeap()
|
||||
{
|
||||
g_pActualAlloc->CompactHeap();
|
||||
}
|
||||
|
||||
MemAllocFailHandler_t CValidateAlloc::SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler )
|
||||
{
|
||||
return g_pActualAlloc->SetAllocFailHandler( pfnMemAllocFailHandler );
|
||||
}
|
||||
|
||||
#endif // TIER0_VALIDATE_HEAP
|
||||
|
||||
#endif // STEAM
|
||||
665
tier0/memvirt.cpp
Normal file
665
tier0/memvirt.cpp
Normal file
@@ -0,0 +1,665 @@
|
||||
//===== Copyright © 2010, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose: Virtual memory sections management!
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
|
||||
//#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/stacktools.h"
|
||||
#include "tier0/memalloc.h"
|
||||
#include "tier0/memvirt.h"
|
||||
#include "tier0/fasttimer.h"
|
||||
#include "mem_helpers.h"
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
#ifdef OSX
|
||||
#include <malloc/malloc.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <limits.h>
|
||||
#include "tier0/threadtools.h"
|
||||
#ifdef _X360
|
||||
#include "xbox/xbox_console.h"
|
||||
#endif
|
||||
|
||||
#ifdef _PS3
|
||||
#include "tls_ps3.h"
|
||||
#include "memoverride_ps3.h"
|
||||
#include "sys/memory.h"
|
||||
#include "sys/mempool.h"
|
||||
#include "sys/process.h"
|
||||
#include "sys/vm.h"
|
||||
#endif
|
||||
|
||||
CInterlockedInt VmmMsgFlag = 0; // Prevents re-entrancy within VmmMsg (printf allocates a large buffer!)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#ifdef _PS3 // _DEBUG
|
||||
#define VmmMsg( mutex, ... ) ( (VmmMsgFlag|mutex.GetOwnerId()) ? 0 : ( ++VmmMsgFlag, Msg( __VA_ARGS__ ), VmmMsgFlag-- ) )
|
||||
#else
|
||||
#define VmmMsg( mutex, ... ) ( (VmmMsgFlag|mutex.GetOwnerId()) ? 0 : ( ++VmmMsgFlag, DevMsg( __VA_ARGS__ ), VmmMsgFlag-- ) )
|
||||
#endif
|
||||
#else
|
||||
#define VmmMsg( mutex, ... ) ((void)0)
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define TRACE_CALL( ... ) do { FILE *fPs3Trace = fopen( "/app_home/tracevmm.txt", "a+" ); if( fPs3Trace ) { fprintf( fPs3Trace, __VA_ARGS__ ); fclose( fPs3Trace ); } } while( 0 )
|
||||
#else
|
||||
#define TRACE_CALL( ... ) ((void)0)
|
||||
#endif
|
||||
|
||||
#ifdef _PS3
|
||||
#define VIRTUAL_MEMORY_MANAGER_SUPPORTED
|
||||
|
||||
#define VMM_SYSTEM_PAGE_POLICY SYS_MEMORY_PAGE_SIZE_64K
|
||||
#define VMM_SYSTEM_PAGE_ALLOCFLAGS SYS_MEMORY_GRANULARITY_64K
|
||||
|
||||
#define VMM_POLICY_SYS_VM 0 // sys_vm_* system is really buggy and console sometimes hardlocks or crashes in OS
|
||||
#define VMM_POLICY_SYS_MMAPPER 1 // sys_mmapper_* seems fairly stable
|
||||
|
||||
#define VMM_POLICY_SYS_VM_NO_RETURN 1 // looks like returning memory under sys_vm_* is the main reason for hardlocks
|
||||
|
||||
#if !(VMM_POLICY_SYS_VM) == !(VMM_POLICY_SYS_MMAPPER)
|
||||
#error
|
||||
#endif
|
||||
|
||||
class CVirtualMemoryManager
|
||||
{
|
||||
public:
|
||||
CVirtualMemoryManager();
|
||||
~CVirtualMemoryManager() { Shutdown(); }
|
||||
|
||||
void Shutdown();
|
||||
IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes );
|
||||
IVirtualMemorySection * NewSection( byte *pBase, size_t numMaxBytes );
|
||||
IVirtualMemorySection * GetMemorySectionForAddress( void *pAddress );
|
||||
void GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax );
|
||||
|
||||
public:
|
||||
byte *m_pBase; // base address of the virtual address space
|
||||
size_t m_nPhysicalSize; // physical memory committed
|
||||
size_t m_nPhysicalSizeMax; // physical memory committed (max since startup)
|
||||
size_t m_nVirtualSize; // virtual memory reserved
|
||||
size_t m_nVirtualSizeMax; // virtual memory reserved (max since startup)
|
||||
#if VMM_POLICY_SYS_VM
|
||||
size_t m_nSparePhysicalSize; // physical memory that has been decommitted, but hasn't been returned
|
||||
#endif
|
||||
#if VMM_POLICY_SYS_MMAPPER
|
||||
sys_memory_t m_sysMemPages[ VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE ];
|
||||
#endif
|
||||
IVirtualMemorySection *m_uiVirtualMap[ VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE ]; // map of pages to sections
|
||||
uint32 m_uiCommitMask[ VMM_VIRTUAL_SIZE / ( VMM_PAGE_SIZE * 32 ) ]; // mask of committed pages
|
||||
|
||||
inline bool CommitTest( int iPage ) const
|
||||
{
|
||||
const uint32 mask = 1u << ( iPage % 32 ), &word = m_uiCommitMask[ iPage / 32 ];
|
||||
return !!( word & mask );
|
||||
}
|
||||
inline void CommitSet( int iPage, bool bSet )
|
||||
{
|
||||
uint32 mask = 1u << ( iPage % 32 ), &word = m_uiCommitMask[ iPage / 32 ];
|
||||
word = ( word & ~mask ) | ( bSet ? mask : 0 );
|
||||
}
|
||||
inline size_t CountReservedPages()
|
||||
{
|
||||
size_t uResult = 0;
|
||||
for ( int k = 0; k < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ k )
|
||||
uResult += m_uiVirtualMap[k] ? 1 : 0;
|
||||
return uResult;
|
||||
}
|
||||
inline size_t CountCommittedPages() const
|
||||
{
|
||||
size_t uResult = 0;
|
||||
for ( int k = 0; k < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ k )
|
||||
uResult += CommitTest(k) ? 1 : 0;
|
||||
return uResult;
|
||||
}
|
||||
|
||||
public:
|
||||
bool CommitPage( byte *pbAddress );
|
||||
bool DecommitPage( byte *pbAddress );
|
||||
|
||||
public:
|
||||
CThreadFastMutex m_Mutex;
|
||||
};
|
||||
|
||||
static CVirtualMemoryManager& GetVirtualMemoryManager()
|
||||
{
|
||||
static CVirtualMemoryManager s_VirtualMemoryManager;
|
||||
return s_VirtualMemoryManager;
|
||||
}
|
||||
|
||||
CVirtualMemoryManager::CVirtualMemoryManager() :
|
||||
m_pBase( NULL ),
|
||||
#if VMM_POLICY_SYS_VM
|
||||
m_nSparePhysicalSize( 0 ),
|
||||
#endif
|
||||
m_nPhysicalSize( 0 ),
|
||||
m_nPhysicalSizeMax( 0 ),
|
||||
m_nVirtualSize( 0 ),
|
||||
m_nVirtualSizeMax( 0 )
|
||||
{
|
||||
memset( m_uiVirtualMap, 0, sizeof( m_uiVirtualMap ) );
|
||||
memset( m_uiCommitMask, 0, sizeof( m_uiCommitMask ) );
|
||||
|
||||
#if VMM_POLICY_SYS_VM
|
||||
|
||||
TRACE_CALL( "sys_vm_memory_map( 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X ) ... ",
|
||||
VMM_VIRTUAL_SIZE,
|
||||
VMM_PAGE_SIZE,
|
||||
SYS_MEMORY_CONTAINER_ID_INVALID,
|
||||
VMM_SYSTEM_PAGE_POLICY,
|
||||
SYS_VM_POLICY_AUTO_RECOMMENDED,
|
||||
reinterpret_cast<sys_addr_t*>( &m_pBase ) );
|
||||
|
||||
int retval = sys_vm_memory_map(
|
||||
VMM_VIRTUAL_SIZE,
|
||||
VMM_PAGE_SIZE,
|
||||
SYS_MEMORY_CONTAINER_ID_INVALID,
|
||||
VMM_SYSTEM_PAGE_POLICY,
|
||||
SYS_VM_POLICY_AUTO_RECOMMENDED,
|
||||
reinterpret_cast<sys_addr_t*>( &m_pBase ) );
|
||||
|
||||
TRACE_CALL( "ret = 0x%08X, base = 0x%08X\n",
|
||||
retval, m_pBase );
|
||||
|
||||
if ( retval < CELL_OK || !m_pBase )
|
||||
{
|
||||
Error( "sys_vm_memory_map failed( size=%dKB, page=%dKB ), error=0x%08X\n", VMM_VIRTUAL_SIZE / VMM_KB, VMM_PAGE_SIZE / VMM_KB, retval );
|
||||
Assert( 0 );
|
||||
m_pBase = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if VMM_POLICY_SYS_MMAPPER
|
||||
|
||||
memset( m_sysMemPages, 0, sizeof( m_sysMemPages ) );
|
||||
|
||||
TRACE_CALL( "sys_mmapper_allocate_address( 0x%08X, 0x%08X, 0x%08X, 0x%08X ) ... ",
|
||||
VMM_VIRTUAL_SIZE,
|
||||
VMM_SYSTEM_PAGE_POLICY,
|
||||
VMM_VIRTUAL_SIZE,
|
||||
reinterpret_cast<sys_addr_t*>( &m_pBase ) );
|
||||
|
||||
int retval = sys_mmapper_allocate_address(
|
||||
VMM_VIRTUAL_SIZE,
|
||||
VMM_SYSTEM_PAGE_POLICY,
|
||||
VMM_VIRTUAL_SIZE,
|
||||
reinterpret_cast<sys_addr_t*>( &m_pBase ) );
|
||||
|
||||
TRACE_CALL( "ret = 0x%08X, base = 0x%08X\n",
|
||||
retval, m_pBase );
|
||||
|
||||
if ( retval < CELL_OK || !m_pBase )
|
||||
{
|
||||
Error( "sys_mmapper_allocate_address failed( size=%dKB, page=%dKB ), error=0x%08X\n", VMM_VIRTUAL_SIZE / VMM_KB, VMM_PAGE_SIZE / VMM_KB, retval );
|
||||
Assert( 0 );
|
||||
m_pBase = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: reserved %uKB block at address 0x%08X.\n", VMM_VIRTUAL_SIZE / VMM_KB, m_pBase );
|
||||
}
|
||||
|
||||
void CVirtualMemoryManager::Shutdown()
|
||||
{
|
||||
if ( !m_pBase )
|
||||
return;
|
||||
|
||||
if ( m_nPhysicalSize )
|
||||
{
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: shutting down with %uKB allocated!\n", m_nPhysicalSize / VMM_KB );
|
||||
}
|
||||
|
||||
#if VMM_POLICY_SYS_VM
|
||||
TRACE_CALL( "sys_vm_unmap( 0x%08X ) ... ", (sys_addr_t) m_pBase );
|
||||
int retval = sys_vm_unmap( (sys_addr_t) m_pBase );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
#endif
|
||||
|
||||
#if VMM_POLICY_SYS_MMAPPER
|
||||
TRACE_CALL( "sys_mmapper_free_address( 0x%08X ) ... ", (sys_addr_t) m_pBase );
|
||||
int retval = sys_mmapper_free_address( (sys_addr_t) m_pBase );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
#endif
|
||||
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: unmapped %uKB block at address 0x%08X (result=0x%08X).\n", VMM_VIRTUAL_SIZE / VMM_KB, m_pBase, retval );
|
||||
m_pBase = NULL;
|
||||
}
|
||||
|
||||
IVirtualMemorySection * CVirtualMemoryManager::GetMemorySectionForAddress( void *pAddress )
|
||||
{
|
||||
Assert( ( pAddress >= m_pBase ) && ( pAddress < ( m_pBase + VMM_VIRTUAL_SIZE ) ) );
|
||||
int iPage = ( (byte*)pAddress - m_pBase ) / VMM_PAGE_SIZE;
|
||||
return m_uiVirtualMap[ iPage ];
|
||||
}
|
||||
|
||||
IVirtualMemorySection * CVirtualMemoryManager::AllocateVirtualMemorySection( size_t numMaxBytes )
|
||||
{
|
||||
Assert( m_pBase );
|
||||
if ( !m_pBase )
|
||||
return NULL;
|
||||
|
||||
// Find the smallest free block with size >= numMaxBytes
|
||||
IVirtualMemorySection *pResult = NULL;
|
||||
int iGoodPage = -1;
|
||||
int iGoodSize = 0;
|
||||
size_t numPages = numMaxBytes / VMM_PAGE_SIZE;
|
||||
if ( !numPages )
|
||||
return NULL;
|
||||
|
||||
{
|
||||
AUTO_LOCK( m_Mutex );
|
||||
|
||||
// TODO: fill the address range with reserved/free sections (so we iterate 50 sections rather than 8000 pages!)
|
||||
for ( int iTryPage = 0; iTryPage < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ iTryPage )
|
||||
{
|
||||
if ( m_uiVirtualMap[ iTryPage ] )
|
||||
continue; // page is taken
|
||||
|
||||
int iTryPageStart = iTryPage;
|
||||
int iTryPageStride = 1;
|
||||
for ( ++ iTryPage; iTryPage < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ iTryPage, ++ iTryPageStride )
|
||||
if ( m_uiVirtualMap[ iTryPage ] )
|
||||
break;
|
||||
|
||||
if ( iTryPageStride < numPages )
|
||||
continue;
|
||||
|
||||
if ( ( iGoodPage < 0 ) || ( iTryPageStride < iGoodSize ) )
|
||||
{
|
||||
iGoodPage = iTryPageStart;
|
||||
iGoodSize = iTryPageStride;
|
||||
}
|
||||
}
|
||||
|
||||
if ( iGoodPage >= 0 )
|
||||
{
|
||||
byte *pbAddress = m_pBase + iGoodPage * VMM_PAGE_SIZE;
|
||||
pResult = NewSection( pbAddress, numMaxBytes );
|
||||
m_nVirtualSize += numPages*VMM_PAGE_SIZE;
|
||||
m_nVirtualSizeMax = MAX( m_nVirtualSize, m_nVirtualSizeMax );
|
||||
|
||||
// Mark pages used
|
||||
for ( int k = 0; k < numPages; ++ k )
|
||||
m_uiVirtualMap[ iGoodPage + k ] = pResult;
|
||||
}
|
||||
}
|
||||
|
||||
if ( pResult )
|
||||
{
|
||||
// NOTE: don't spew while the mutex is held!
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: new reservation %uKB @ 0x%08X\n",
|
||||
CountReservedPages()*VMM_PAGE_SIZE / VMM_KB,
|
||||
CountCommittedPages()*VMM_PAGE_SIZE / VMM_KB,
|
||||
numMaxBytes / VMM_KB, pResult->GetBaseAddress() );
|
||||
|
||||
return pResult;
|
||||
}
|
||||
|
||||
Error( "CVirtualMemoryManager::AllocateVirtualMemorySection has no memory for %u bytes!\n", numMaxBytes );
|
||||
Assert( 0 );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CVirtualMemoryManager::CommitPage( byte *pbAddress )
|
||||
{
|
||||
Assert( m_pBase );
|
||||
int iPage = ( pbAddress - m_pBase ) / VMM_PAGE_SIZE;
|
||||
if ( CommitTest( iPage ) )
|
||||
return true;
|
||||
|
||||
CommitSet( iPage, true );
|
||||
|
||||
#if VMM_POLICY_SYS_VM
|
||||
if ( m_nPhysicalSize )
|
||||
{
|
||||
if ( m_nSparePhysicalSize > 0 )
|
||||
{
|
||||
m_nSparePhysicalSize -= VMM_PAGE_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
TRACE_CALL( "sys_vm_append_memory( 0x%08X, 0x%08X ) ... ", (sys_addr_t) m_pBase, VMM_PAGE_SIZE );
|
||||
int retval = sys_vm_append_memory( (sys_addr_t) m_pBase, VMM_PAGE_SIZE );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_vm_append_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_nPhysicalSize += VMM_PAGE_SIZE;
|
||||
m_nPhysicalSizeMax = MAX( m_nPhysicalSize, m_nPhysicalSizeMax );
|
||||
|
||||
TRACE_CALL( "sys_vm_touch( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pbAddress, VMM_PAGE_SIZE );
|
||||
int retval = sys_vm_touch( (sys_addr_t) pbAddress, VMM_PAGE_SIZE );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_vm_touch failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if VMM_POLICY_SYS_MMAPPER
|
||||
TRACE_CALL( "sys_mmapper_allocate_memory( 0x%08X, 0x%08X, page=%d ) ... ", VMM_PAGE_SIZE, VMM_SYSTEM_PAGE_ALLOCFLAGS, iPage );
|
||||
int retval = sys_mmapper_allocate_memory( VMM_PAGE_SIZE, VMM_SYSTEM_PAGE_ALLOCFLAGS, &m_sysMemPages[iPage] );
|
||||
TRACE_CALL( "ret = 0x%08X, mem = 0x%08X\n", retval, m_sysMemPages[iPage] );
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_mmapper_allocate_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
||||
return false;
|
||||
}
|
||||
m_nPhysicalSize += VMM_PAGE_SIZE;
|
||||
m_nPhysicalSizeMax = MAX( m_nPhysicalSize, m_nPhysicalSizeMax );
|
||||
|
||||
TRACE_CALL( "sys_mmapper_map_memory( 0x%08X, 0x%08X, 0x%08X, page=%d ) ... ", (sys_addr_t) pbAddress, m_sysMemPages[iPage], SYS_MEMORY_PROT_READ_WRITE, iPage );
|
||||
retval = sys_mmapper_map_memory( (sys_addr_t) pbAddress, m_sysMemPages[iPage], SYS_MEMORY_PROT_READ_WRITE );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_mmapper_map_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CVirtualMemoryManager::DecommitPage( byte *pbAddress )
|
||||
{
|
||||
Assert( m_pBase );
|
||||
int iPage = ( pbAddress - m_pBase ) / VMM_PAGE_SIZE;
|
||||
if ( !CommitTest( iPage ) )
|
||||
return false;
|
||||
|
||||
CommitSet( iPage, false );
|
||||
|
||||
#if VMM_POLICY_SYS_VM
|
||||
TRACE_CALL( "sys_vm_invalidate( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pbAddress, VMM_PAGE_SIZE );
|
||||
int retval = sys_vm_invalidate( (sys_addr_t) pbAddress, VMM_PAGE_SIZE );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: DecommitPage: sys_vm_invalidate failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_nPhysicalSize -= VMM_PAGE_SIZE;
|
||||
#if VMM_POLICY_SYS_VM_NO_RETURN
|
||||
m_nSparePhysicalSize += VMM_PAGE_SIZE;
|
||||
return false;
|
||||
#else
|
||||
return m_nPhysicalSize >= VMM_PAGE_SIZE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if VMM_POLICY_SYS_MMAPPER
|
||||
TRACE_CALL( "sys_mmapper_unmap_memory( 0x%08X, 0x%08X, page=%d ) ... ", (sys_addr_t) pbAddress, m_sysMemPages[iPage], iPage );
|
||||
int retval = sys_mmapper_unmap_memory( (sys_addr_t) pbAddress, &m_sysMemPages[iPage] );
|
||||
TRACE_CALL( "ret = 0x%08X, mem = 0x%08X\n", retval, m_sysMemPages[iPage] );
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: DecommitPage: sys_mmapper_unmap_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
||||
return false;
|
||||
}
|
||||
|
||||
TRACE_CALL( "sys_mmapper_free_memory( 0x%08X, page=%d ) ... ", m_sysMemPages[iPage], iPage );
|
||||
retval = sys_mmapper_free_memory( m_sysMemPages[iPage] );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
m_sysMemPages[iPage] = 0;
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( m_Mutex, "Virtual Memory Manager: DecommitPage: sys_mmapper_free_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
||||
return false;
|
||||
}
|
||||
m_nPhysicalSize -= VMM_PAGE_SIZE;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CVirtualMemoryManager::GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax )
|
||||
{
|
||||
AUTO_LOCK( m_Mutex );
|
||||
nReserved = m_nVirtualSize;
|
||||
nReservedMax = m_nVirtualSizeMax;
|
||||
nCommitted = m_nPhysicalSize;
|
||||
nCommittedMax = m_nPhysicalSizeMax;
|
||||
}
|
||||
|
||||
class CVirtualMemorySectionImpl : public IVirtualMemorySection
|
||||
{
|
||||
public:
|
||||
CVirtualMemorySectionImpl( byte *pbAddress, size_t numMaxBytes ) :
|
||||
m_pBase( pbAddress ),
|
||||
m_numMaxBytes( numMaxBytes ),
|
||||
m_numPhysical( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Information about memory section
|
||||
virtual void * GetBaseAddress() { return m_pBase; }
|
||||
virtual size_t GetPageSize() { return VMM_PAGE_SIZE; }
|
||||
virtual size_t GetTotalSize() { return m_numMaxBytes; }
|
||||
|
||||
// Functions to manage physical memory mapped to virtual memory
|
||||
virtual bool CommitPages( void *pvBase, size_t numBytes );
|
||||
virtual void DecommitPages( void *pvBase, size_t numBytes );
|
||||
bool CommitPages_Inner( void *pvBase, size_t numBytes );
|
||||
|
||||
// Release the physical memory and associated virtual address space
|
||||
virtual void Release();
|
||||
|
||||
|
||||
byte *m_pBase;
|
||||
size_t m_numMaxBytes;
|
||||
size_t m_numPhysical;
|
||||
};
|
||||
|
||||
bool CVirtualMemorySectionImpl::CommitPages_Inner( void *pvBase, size_t numBytes )
|
||||
{
|
||||
Assert( pvBase >= m_pBase );
|
||||
Assert( ( (byte*)pvBase ) + numBytes <= m_pBase + m_numMaxBytes );
|
||||
|
||||
{
|
||||
AUTO_LOCK( GetVirtualMemoryManager().m_Mutex );
|
||||
|
||||
int startPage = ( ( (byte*)pvBase ) - m_pBase ) / VMM_PAGE_SIZE;
|
||||
int endPage = ( ( (byte*)pvBase ) + numBytes + ( VMM_PAGE_SIZE - 1 ) - m_pBase ) / VMM_PAGE_SIZE;
|
||||
for ( int k = startPage; k < endPage; k++ )
|
||||
{
|
||||
if ( !GetVirtualMemoryManager().CommitPage( m_pBase + k * VMM_PAGE_SIZE ) )
|
||||
{
|
||||
// Failure! Decommit the pages we have committed so far:
|
||||
for ( k = k - 1; k >= startPage; k-- )
|
||||
{
|
||||
GetVirtualMemoryManager().DecommitPage( m_pBase + k * VMM_PAGE_SIZE );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if VMM_POLICY_SYS_VM
|
||||
TRACE_CALL( "sys_vm_sync( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pvBase, numBytes );
|
||||
int retval = sys_vm_sync( (sys_addr_t) pvBase, numBytes );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager: CommitPages: sys_vm_sync failed (result=0x%08X).\n", retval );
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: we don't spew while the mutex is held!
|
||||
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: committed %uKB @ 0x%08X\n",
|
||||
GetVirtualMemoryManager().CountReservedPages() * VMM_PAGE_SIZE / VMM_KB,
|
||||
GetVirtualMemoryManager().CountCommittedPages() * VMM_PAGE_SIZE / VMM_KB,
|
||||
numBytes / VMM_KB, pvBase );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CVirtualMemorySectionImpl::CommitPages( void *pvBase, size_t numBytes )
|
||||
{
|
||||
if ( CommitPages_Inner( pvBase, numBytes ) )
|
||||
return true;
|
||||
|
||||
// On failure, compact the heap and try one last time:
|
||||
Msg( "\n\nVirtual Memory Manager: COMMIT FAILED! (%d) Last-ditch effort: compacting the heap and re-trying...\n", numBytes );
|
||||
g_pMemAllocInternalPS3->CompactHeap();
|
||||
bool success = CommitPages_Inner( pvBase, numBytes );
|
||||
if ( !success )
|
||||
{
|
||||
Msg( "Virtual Memory Manager: COMMIT FAILED! (%d) Fatal error.\n\n\n", numBytes );
|
||||
g_pMemAllocInternalPS3->OutOfMemory( numBytes );
|
||||
}
|
||||
Msg("\n\n");
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void CVirtualMemorySectionImpl::DecommitPages( void *pvBase, size_t numBytes )
|
||||
{
|
||||
Assert( pvBase >= m_pBase );
|
||||
Assert( ( (byte*)pvBase ) + numBytes <= m_pBase + m_numMaxBytes );
|
||||
|
||||
{
|
||||
AUTO_LOCK( GetVirtualMemoryManager().m_Mutex );
|
||||
|
||||
int numPagesDecommitted = 0;
|
||||
|
||||
for ( int k = ( ( (byte*)pvBase ) - m_pBase ) / VMM_PAGE_SIZE;
|
||||
m_pBase + k * VMM_PAGE_SIZE < ( (byte*)pvBase ) + numBytes; ++ k )
|
||||
{
|
||||
numPagesDecommitted += !!GetVirtualMemoryManager().DecommitPage( m_pBase + k * VMM_PAGE_SIZE );
|
||||
}
|
||||
|
||||
#if VMM_POLICY_SYS_VM
|
||||
TRACE_CALL( "sys_vm_sync( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pvBase, numBytes );
|
||||
int retval = sys_vm_sync( (sys_addr_t) pvBase, numBytes );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager: DecommitPages: sys_vm_sync failed (result=0x%08X).\n", retval );
|
||||
}
|
||||
|
||||
if ( numPagesDecommitted > 0 )
|
||||
{
|
||||
TRACE_CALL( "sys_vm_return_memory( 0x%08X, 0x%08X ) ... ", (sys_addr_t) GetVirtualMemoryManager().m_pBase, numPagesDecommitted * VMM_PAGE_SIZE );
|
||||
retval = sys_vm_return_memory( (sys_addr_t) GetVirtualMemoryManager().m_pBase, numPagesDecommitted * VMM_PAGE_SIZE );
|
||||
TRACE_CALL( "ret = 0x%08X\n", retval );
|
||||
if ( retval < CELL_OK )
|
||||
{
|
||||
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager: DecommitPages: sys_vm_return_memory failed (result=0x%08X).\n", retval );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: we don't spew while the mutex is held!
|
||||
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: decommitted %uKB @ 0x%08X\n",
|
||||
GetVirtualMemoryManager().CountReservedPages() * VMM_PAGE_SIZE / VMM_KB,
|
||||
GetVirtualMemoryManager().CountCommittedPages() * VMM_PAGE_SIZE / VMM_KB,
|
||||
numBytes / VMM_KB, pvBase );
|
||||
}
|
||||
|
||||
void CVirtualMemorySectionImpl::Release()
|
||||
{
|
||||
DecommitPages( m_pBase, m_numMaxBytes );
|
||||
|
||||
{
|
||||
AUTO_LOCK( GetVirtualMemoryManager().m_Mutex );
|
||||
|
||||
int iPageStart = ( m_pBase - GetVirtualMemoryManager().m_pBase ) / VMM_PAGE_SIZE;
|
||||
for ( int k = 0; m_pBase + k * VMM_PAGE_SIZE < m_pBase + m_numMaxBytes; ++ k )
|
||||
{
|
||||
Assert( GetVirtualMemoryManager().m_uiVirtualMap[ iPageStart + k ] == this );
|
||||
GetVirtualMemoryManager().m_uiVirtualMap[ iPageStart + k ] = NULL;
|
||||
GetVirtualMemoryManager().m_nVirtualSize -= VMM_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: we don't spew while the mutex is held!
|
||||
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: released %uKB @ 0x%08X\n",
|
||||
GetVirtualMemoryManager().CountReservedPages() * VMM_PAGE_SIZE / VMM_KB,
|
||||
GetVirtualMemoryManager().CountCommittedPages() * VMM_PAGE_SIZE / VMM_KB,
|
||||
m_numMaxBytes / VMM_KB, m_pBase );
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
IVirtualMemorySection * CVirtualMemoryManager::NewSection( byte *pBase, size_t numMaxBytes )
|
||||
{
|
||||
return new CVirtualMemorySectionImpl( pBase, numMaxBytes );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef VIRTUAL_MEMORY_MANAGER_SUPPORTED
|
||||
|
||||
IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes )
|
||||
{
|
||||
return GetVirtualMemoryManager().AllocateVirtualMemorySection( numMaxBytes );
|
||||
}
|
||||
|
||||
void VirtualMemoryManager_Shutdown()
|
||||
{
|
||||
GetVirtualMemoryManager().Shutdown();
|
||||
}
|
||||
|
||||
IVirtualMemorySection *GetMemorySectionForAddress( void *pAddress )
|
||||
{
|
||||
return GetVirtualMemoryManager().GetMemorySectionForAddress( pAddress );
|
||||
}
|
||||
|
||||
void VirtualMemoryManager_GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax )
|
||||
{
|
||||
GetVirtualMemoryManager().GetStats( nReserved, nReservedMax, nCommitted, nCommittedMax );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void VirtualMemoryManager_Shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
IVirtualMemorySection *GetMemorySectionForAddress( void *pAddress )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // !STEAM && !NO_MALLOC_OVERRIDE
|
||||
|
||||
726
tier0/minidump.cpp
Normal file
726
tier0/minidump.cpp
Normal file
@@ -0,0 +1,726 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#include "tier0/minidump.h"
|
||||
#include "tier0/platform.h"
|
||||
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
|
||||
#if _MSC_VER >= 1300
|
||||
#include "tier0/valve_off.h"
|
||||
#define WIN_32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
// MiniDumpWriteDump() function declaration (so we can just get the function directly from windows)
|
||||
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)
|
||||
(
|
||||
HANDLE hProcess,
|
||||
DWORD dwPid,
|
||||
HANDLE hFile,
|
||||
MINIDUMP_TYPE DumpType,
|
||||
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
||||
);
|
||||
|
||||
|
||||
// counter used to make sure minidump names are unique
|
||||
static int g_nMinidumpsWritten = 0;
|
||||
|
||||
// process-wide prefix to use for minidumps
|
||||
static tchar g_rgchMinidumpFilenamePrefix[MAX_PATH];
|
||||
|
||||
// Process-wide comment to put into minidumps
|
||||
static char g_rgchMinidumpComment[2048];
|
||||
|
||||
#if defined( _WIN32 )
|
||||
static volatile LONG g_dwMinidumpWriteInProgress = 0;
|
||||
static DWORD g_dwThreadIdWritingMinidump = 0;
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Creates a new file and dumps the exception info into it
|
||||
// Input : uStructuredExceptionCode - windows exception code, unused.
|
||||
// pExceptionInfo - call stack.
|
||||
// minidumpType - type of minidump to write.
|
||||
// ptchMinidumpFileNameBuffer - if not-NULL points to a writable tchar buffer
|
||||
// of length at least _MAX_PATH to contain the name
|
||||
// of the written minidump file on return.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool WriteMiniDumpUsingExceptionInfo(
|
||||
unsigned int uStructuredExceptionCode,
|
||||
_EXCEPTION_POINTERS * pExceptionInfo,
|
||||
int minidumpType,
|
||||
const char *pszFilenameSuffix,
|
||||
tchar *ptchMinidumpFileNameBuffer /* = NULL */
|
||||
)
|
||||
{
|
||||
#if defined( _WIN32 )
|
||||
// If we are running on GC then we also want to synchronize writing minidumps with Steam
|
||||
struct CEnsureWriteMiniDumpNonConcurrentWithSteam
|
||||
{
|
||||
CEnsureWriteMiniDumpNonConcurrentWithSteam()
|
||||
{
|
||||
m_hTier0SteamDll = ::GetModuleHandle( "tier0_s.dll" );
|
||||
if ( m_hTier0SteamDll )
|
||||
{
|
||||
m_pfnBGetMiniDumpLock = ( bool (*)() ) ::GetProcAddress( m_hTier0SteamDll, "BGetMiniDumpLock" );
|
||||
m_pfnMiniDumpUnlock = ( void (*)() ) ::GetProcAddress( m_hTier0SteamDll, "MiniDumpUnlock" );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pfnBGetMiniDumpLock = NULL;
|
||||
m_pfnMiniDumpUnlock = NULL;
|
||||
}
|
||||
|
||||
if ( m_pfnBGetMiniDumpLock && m_pfnMiniDumpUnlock )
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
bool bLocked = m_pfnBGetMiniDumpLock();
|
||||
if ( bLocked )
|
||||
break;
|
||||
::Sleep( 100 ); // Other thread is in writing minidump at the moment, keep waiting
|
||||
}
|
||||
}
|
||||
}
|
||||
~CEnsureWriteMiniDumpNonConcurrentWithSteam()
|
||||
{
|
||||
if ( m_pfnBGetMiniDumpLock && m_pfnMiniDumpUnlock )
|
||||
{
|
||||
m_pfnMiniDumpUnlock();
|
||||
}
|
||||
}
|
||||
private:
|
||||
HMODULE m_hTier0SteamDll;
|
||||
bool (*m_pfnBGetMiniDumpLock)();
|
||||
void (*m_pfnMiniDumpUnlock)();
|
||||
} autoEnsureWriteMiniDumpNonConcurrentWithSteam;
|
||||
|
||||
if ( g_dwThreadIdWritingMinidump == ::GetCurrentThreadId() )
|
||||
return false; // we are already writing a minidump on the current thread :(
|
||||
|
||||
struct CEnsureWriteMiniDumpUsingExceptionInfoRunsNonConcurrent
|
||||
{
|
||||
//
|
||||
// Use basic spin sleep implementation to avoid any shenanigans
|
||||
// if we were to use synchronization objects like critical sections
|
||||
// due to initialization requirements or usage in various stages
|
||||
// of process/dll loading
|
||||
//
|
||||
CEnsureWriteMiniDumpUsingExceptionInfoRunsNonConcurrent()
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
if ( !g_dwMinidumpWriteInProgress )
|
||||
{ // cheap check, followed by thread-safe lock attempt, followed by validation re-check
|
||||
LONG dwMinidumpWrite = ::InterlockedIncrement( &g_dwMinidumpWriteInProgress );
|
||||
if ( dwMinidumpWrite == 1 )
|
||||
{
|
||||
// We are the thread that entered the minidump writing code
|
||||
g_dwThreadIdWritingMinidump = ::GetCurrentThreadId();
|
||||
break;
|
||||
}
|
||||
::InterlockedDecrement( &g_dwMinidumpWriteInProgress ); // since we attempted to thread-safely enter
|
||||
}
|
||||
::Sleep( 100 ); // Other thread is in writing minidump at the moment, keep waiting
|
||||
}
|
||||
}
|
||||
~CEnsureWriteMiniDumpUsingExceptionInfoRunsNonConcurrent()
|
||||
{
|
||||
g_dwThreadIdWritingMinidump = 0;
|
||||
::InterlockedDecrement( &g_dwMinidumpWriteInProgress ); // since we entered in constructor
|
||||
}
|
||||
} autoEnsureWriteMiniDumpUsingExceptionInfoRunsNonConcurrent;
|
||||
#endif
|
||||
|
||||
if ( ptchMinidumpFileNameBuffer )
|
||||
{
|
||||
*ptchMinidumpFileNameBuffer = tchar( 0 );
|
||||
}
|
||||
|
||||
// get the function pointer directly so that we don't have to include the .lib, and that
|
||||
// we can easily change it to using our own dll when this code is used on win98/ME/2K machines
|
||||
HMODULE hDbgHelpDll = ::LoadLibrary( "DbgHelp.dll" );
|
||||
if ( !hDbgHelpDll )
|
||||
return false;
|
||||
|
||||
bool bReturnValue = false;
|
||||
MINIDUMPWRITEDUMP pfnMiniDumpWrite = (MINIDUMPWRITEDUMP) ::GetProcAddress( hDbgHelpDll, "MiniDumpWriteDump" );
|
||||
|
||||
if ( pfnMiniDumpWrite )
|
||||
{
|
||||
// create a unique filename for the minidump based on the current time and module name
|
||||
time_t currTime = ::time( NULL );
|
||||
struct tm * pTime = ::localtime( &currTime );
|
||||
++g_nMinidumpsWritten;
|
||||
|
||||
// If they didn't set a dump prefix, then set one for them using the module name
|
||||
if ( g_rgchMinidumpFilenamePrefix[0] == TCHAR(0) )
|
||||
{
|
||||
tchar rgchModuleName[MAX_PATH];
|
||||
#ifdef TCHAR_IS_WCHAR
|
||||
::GetModuleFileNameW( NULL, rgchModuleName, sizeof(rgchModuleName) / sizeof(tchar) );
|
||||
#else
|
||||
::GetModuleFileName( NULL, rgchModuleName, sizeof(rgchModuleName) / sizeof(tchar) );
|
||||
#endif
|
||||
|
||||
// strip off the rest of the path from the .exe name
|
||||
tchar *pch = _tcsrchr( rgchModuleName, '.' );
|
||||
if ( pch )
|
||||
{
|
||||
*pch = 0;
|
||||
}
|
||||
pch = _tcsrchr( rgchModuleName, '\\' );
|
||||
if ( pch )
|
||||
{
|
||||
// move past the last slash
|
||||
pch++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pch = _T("unknown");
|
||||
}
|
||||
strcpy( g_rgchMinidumpFilenamePrefix, pch );
|
||||
}
|
||||
|
||||
|
||||
// can't use the normal string functions since we're in tier0
|
||||
tchar rgchFileName[MAX_PATH];
|
||||
_sntprintf( rgchFileName, sizeof(rgchFileName) / sizeof(tchar),
|
||||
_T("%s_%d%02d%02d_%02d%02d%02d_%d_x%p%hs%hs.mdmp"),
|
||||
g_rgchMinidumpFilenamePrefix,
|
||||
pTime->tm_year + 1900, /* Year less 2000 */
|
||||
pTime->tm_mon + 1, /* month (0 - 11 : 0 = January) */
|
||||
pTime->tm_mday, /* day of month (1 - 31) */
|
||||
pTime->tm_hour, /* hour (0 - 23) */
|
||||
pTime->tm_min, /* minutes (0 - 59) */
|
||||
pTime->tm_sec, /* seconds (0 - 59) */
|
||||
g_nMinidumpsWritten, // ensures the filename is unique
|
||||
&currTime, /* address of stack variable to ensure that different threads write to different files and to differentiate game side dumps from Steam dumps */
|
||||
( pszFilenameSuffix != NULL ) ? "_" : "",
|
||||
( pszFilenameSuffix != NULL ) ? pszFilenameSuffix : ""
|
||||
);
|
||||
// Ensure null-termination.
|
||||
rgchFileName[ Q_ARRAYSIZE(rgchFileName) - 1 ] = 0;
|
||||
|
||||
// Create directory, if our dump filename had a directory in it
|
||||
for ( char *pSlash = rgchFileName ; *pSlash != '\0' ; ++pSlash )
|
||||
{
|
||||
char c = *pSlash;
|
||||
if ( c == '/' || c == '\\' )
|
||||
{
|
||||
*pSlash = '\0';
|
||||
::CreateDirectory( rgchFileName, NULL );
|
||||
*pSlash = c;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL bMinidumpResult = FALSE;
|
||||
#ifdef TCHAR_IS_WCHAR
|
||||
HANDLE hFile = ::CreateFileW( rgchFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
#else
|
||||
HANDLE hFile = ::CreateFile( rgchFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
||||
#endif
|
||||
|
||||
if ( hFile )
|
||||
{
|
||||
// dump the exception information into the file
|
||||
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
|
||||
ExInfo.ThreadId = ::GetCurrentThreadId();
|
||||
ExInfo.ExceptionPointers = pExceptionInfo;
|
||||
ExInfo.ClientPointers = FALSE;
|
||||
|
||||
// Do we have a comment?
|
||||
MINIDUMP_USER_STREAM_INFORMATION StreamInformationHeader;
|
||||
MINIDUMP_USER_STREAM UserStreams[1];
|
||||
memset( &StreamInformationHeader, 0, sizeof(StreamInformationHeader) );
|
||||
StreamInformationHeader.UserStreamArray = UserStreams;
|
||||
|
||||
if ( g_rgchMinidumpComment[0] != '\0' )
|
||||
{
|
||||
MINIDUMP_USER_STREAM *pCommentStream = &UserStreams[StreamInformationHeader.UserStreamCount++];
|
||||
pCommentStream->Type = CommentStreamA;
|
||||
pCommentStream->Buffer = g_rgchMinidumpComment;
|
||||
pCommentStream->BufferSize = (ULONG)strlen(g_rgchMinidumpComment)+1;
|
||||
}
|
||||
|
||||
bMinidumpResult = (*pfnMiniDumpWrite)( ::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)minidumpType, &ExInfo, &StreamInformationHeader, NULL );
|
||||
::CloseHandle( hFile );
|
||||
|
||||
// Clear comment for next time
|
||||
g_rgchMinidumpComment[0] = '\0';
|
||||
|
||||
if ( bMinidumpResult )
|
||||
{
|
||||
bReturnValue = true;
|
||||
|
||||
if ( ptchMinidumpFileNameBuffer )
|
||||
{
|
||||
// Copy the file name from "pSrc = rgchFileName" into "pTgt = ptchMinidumpFileNameBuffer"
|
||||
tchar *pTgt = ptchMinidumpFileNameBuffer;
|
||||
tchar const *pSrc = rgchFileName;
|
||||
while ( ( *( pTgt ++ ) = *( pSrc ++ ) ) != tchar( 0 ) )
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// fall through to trying again
|
||||
}
|
||||
|
||||
// mark any failed minidump writes by renaming them
|
||||
if ( !bMinidumpResult )
|
||||
{
|
||||
tchar rgchFailedFileName[_MAX_PATH];
|
||||
_sntprintf( rgchFailedFileName, sizeof(rgchFailedFileName) / sizeof(tchar), "(failed)%s", rgchFileName );
|
||||
// Ensure null-termination.
|
||||
rgchFailedFileName[ Q_ARRAYSIZE(rgchFailedFileName) - 1 ] = 0;
|
||||
rename( rgchFileName, rgchFailedFileName );
|
||||
}
|
||||
}
|
||||
|
||||
::FreeLibrary( hDbgHelpDll );
|
||||
|
||||
// call the log flush function if one is registered to try to flush any logs
|
||||
//CallFlushLogFunc();
|
||||
|
||||
return bReturnValue;
|
||||
}
|
||||
|
||||
|
||||
void InternalWriteMiniDumpUsingExceptionInfo( unsigned int uStructuredExceptionCode, _EXCEPTION_POINTERS * pExceptionInfo, const char *pszFilenameSuffix )
|
||||
{
|
||||
// If this is is a real crash (not an assert or one we purposefully triggered), then try to write a full dump
|
||||
// only do this on our GC (currently GC is 64-bit, so we can use a #define rather than some run-time switch
|
||||
#ifdef _WIN64
|
||||
if ( uStructuredExceptionCode != EXCEPTION_BREAKPOINT )
|
||||
{
|
||||
bool bCanWriteFullMemoryDump = !!CommandLine()->FindParm( "-fullmemdumpallowed" );
|
||||
int nGbRequired = CommandLine()->ParmValue( "-fullmemdumprequiregb", int( 0 ) );
|
||||
if ( bCanWriteFullMemoryDump && ( nGbRequired > 0 ) )
|
||||
{
|
||||
tchar rgchModuleName[ MAX_PATH ];
|
||||
#ifdef TCHAR_IS_WCHAR
|
||||
::GetModuleFileNameW( NULL, rgchModuleName, sizeof( rgchModuleName ) / sizeof( tchar ) );
|
||||
#else
|
||||
::GetModuleFileName( NULL, rgchModuleName, sizeof( rgchModuleName ) / sizeof( tchar ) );
|
||||
#endif
|
||||
if ( tchar *ptch = _tcschr( rgchModuleName, '\\' ) )
|
||||
ptch[ 1 ] = 0; // must keep the trailing slash
|
||||
|
||||
ULARGE_INTEGER ullFreeBytesAvailable, ullTotalNumberOfBytes, ullTotalNumberOfFreeBytes;
|
||||
BOOL bCheckedDiskSpace = GetDiskFreeSpaceEx( rgchModuleName, &ullFreeBytesAvailable, &ullTotalNumberOfBytes, &ullTotalNumberOfFreeBytes );
|
||||
if ( !bCheckedDiskSpace
|
||||
|| ( ( ullFreeBytesAvailable.QuadPart / ( 1 * 1024 * 1024 * 1024 ) ) < ( ULONGLONG ) ( nGbRequired ) ) )
|
||||
bCanWriteFullMemoryDump = false;
|
||||
}
|
||||
|
||||
if ( bCanWriteFullMemoryDump && WriteMiniDumpUsingExceptionInfo( uStructuredExceptionCode, pExceptionInfo, MiniDumpWithFullMemory, pszFilenameSuffix ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// First try to write it with all the indirectly referenced memory (ie: a large file).
|
||||
// If that doesn't work, then write a smaller one.
|
||||
int iType = MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory;
|
||||
if ( !WriteMiniDumpUsingExceptionInfo( uStructuredExceptionCode, pExceptionInfo, (MINIDUMP_TYPE)iType, pszFilenameSuffix ) )
|
||||
{
|
||||
iType = MiniDumpWithDataSegs;
|
||||
WriteMiniDumpUsingExceptionInfo( uStructuredExceptionCode, pExceptionInfo, (MINIDUMP_TYPE)iType, pszFilenameSuffix );
|
||||
}
|
||||
}
|
||||
|
||||
// minidump function to use
|
||||
static FnMiniDump g_pfnWriteMiniDump = InternalWriteMiniDumpUsingExceptionInfo;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Set a function to call which will write our minidump, overriding
|
||||
// the default function
|
||||
// Input : pfn - Pointer to minidump function to set
|
||||
// Output : Previously set function
|
||||
//-----------------------------------------------------------------------------
|
||||
FnMiniDump SetMiniDumpFunction( FnMiniDump pfn )
|
||||
{
|
||||
FnMiniDump pfnTemp = g_pfnWriteMiniDump;
|
||||
g_pfnWriteMiniDump = pfn;
|
||||
return pfnTemp;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Unhandled exceptions
|
||||
//-----------------------------------------------------------------------------
|
||||
static FnMiniDump g_UnhandledExceptionFunction;
|
||||
static LONG STDCALL ValveUnhandledExceptionFilter( _EXCEPTION_POINTERS* pExceptionInfo )
|
||||
{
|
||||
uint uStructuredExceptionCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
|
||||
g_UnhandledExceptionFunction( uStructuredExceptionCode, pExceptionInfo, 0 );
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
void MinidumpSetUnhandledExceptionFunction( FnMiniDump pfn )
|
||||
{
|
||||
g_UnhandledExceptionFunction = pfn;
|
||||
SetUnhandledExceptionFilter( ValveUnhandledExceptionFilter );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: set prefix to use for filenames
|
||||
//-----------------------------------------------------------------------------
|
||||
void SetMinidumpFilenamePrefix( const char *pszPrefix )
|
||||
{
|
||||
#ifdef TCHAR_IS_WCHAR
|
||||
mbstowcs( g_rgchMinidumpFilenamePrefix, pszPrefix, sizeof(g_rgchMinidumpFilenamePrefix) / sizeof(g_rgchMinidumpFilenamePrefix[0]) - 1 );
|
||||
#else
|
||||
strncpy( g_rgchMinidumpFilenamePrefix, pszPrefix, sizeof(g_rgchMinidumpFilenamePrefix) / sizeof(g_rgchMinidumpFilenamePrefix[0]) - 1 );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: set comment to put into minidumps
|
||||
//-----------------------------------------------------------------------------
|
||||
void SetMinidumpComment( const char *pszComment )
|
||||
{
|
||||
if ( pszComment == NULL )
|
||||
pszComment = "";
|
||||
strncpy( g_rgchMinidumpComment, pszComment, sizeof(g_rgchMinidumpComment) - 1 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: writes out a minidump from the current process
|
||||
//-----------------------------------------------------------------------------
|
||||
void WriteMiniDump( const char *pszFilenameSuffix )
|
||||
{
|
||||
// throw an exception so we can catch it and get the stack info
|
||||
__try
|
||||
{
|
||||
::RaiseException
|
||||
(
|
||||
EXCEPTION_BREAKPOINT, // dwExceptionCode
|
||||
EXCEPTION_NONCONTINUABLE, // dwExceptionFlags
|
||||
0, // nNumberOfArguments,
|
||||
NULL // const ULONG_PTR* lpArguments
|
||||
);
|
||||
|
||||
// Never get here (non-continuable exception)
|
||||
}
|
||||
// Write the minidump from inside the filter (GetExceptionInformation() is only
|
||||
// valid in the filter)
|
||||
__except ( g_pfnWriteMiniDump( EXCEPTION_BREAKPOINT, GetExceptionInformation(), pszFilenameSuffix ), EXCEPTION_EXECUTE_HANDLER )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
DBG_OVERLOAD bool g_bInException = false;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Catches and writes out any exception throw by the specified function.
|
||||
// Input: pfn - Function to call within protective exception block
|
||||
// pv - Void pointer to pass that function
|
||||
//-----------------------------------------------------------------------------
|
||||
void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] )
|
||||
{
|
||||
CatchAndWriteMiniDumpEx( pfn, argc, argv, k_ECatchAndWriteMiniDumpAbort );
|
||||
}
|
||||
|
||||
// message types
|
||||
enum ECatchAndWriteFunctionType
|
||||
{
|
||||
k_eSCatchAndWriteFunctionTypeInvalid = 0,
|
||||
k_eSCatchAndWriteFunctionTypeWMain = 1, // typedef void (*FnWMain)( int , tchar *[] );
|
||||
k_eSCatchAndWriteFunctionTypeWMainIntReg = 2, // typedef int (*FnWMainIntRet)( int , tchar *[] );
|
||||
k_eSCatchAndWriteFunctionTypeVoidPtr = 3, // typedef void (*FnVoidPtrFn)( void * );
|
||||
};
|
||||
|
||||
struct CatchAndWriteContext_t
|
||||
{
|
||||
ECatchAndWriteFunctionType m_eType;
|
||||
void *m_pfn;
|
||||
int *m_pargc;
|
||||
tchar ***m_pargv;
|
||||
void **m_ppv;
|
||||
ECatchAndWriteMinidumpAction m_eAction;
|
||||
|
||||
void Set( ECatchAndWriteFunctionType eType, ECatchAndWriteMinidumpAction eAction, void *pfn, int *pargc, tchar **pargv[], void **ppv )
|
||||
{
|
||||
m_eType = eType;
|
||||
m_eAction = eAction;
|
||||
m_pfn = pfn;
|
||||
m_pargc = pargc;
|
||||
m_pargv = pargv;
|
||||
m_ppv = ppv;
|
||||
|
||||
ErrorIfNot( m_pfn, ( "CatchAndWriteContext_t::Set w/o a function pointer!" ) );
|
||||
}
|
||||
|
||||
int Invoke()
|
||||
{
|
||||
switch ( m_eType )
|
||||
{
|
||||
default:
|
||||
case k_eSCatchAndWriteFunctionTypeInvalid:
|
||||
break;
|
||||
case k_eSCatchAndWriteFunctionTypeWMain:
|
||||
ErrorIfNot( m_pargc && m_pargv, ( "CatchAndWriteContext_t::Invoke with bogus argc/argv" ) );
|
||||
((FnWMain)m_pfn)( *m_pargc, *m_pargv );
|
||||
break;
|
||||
case k_eSCatchAndWriteFunctionTypeWMainIntReg:
|
||||
ErrorIfNot( m_pargc && m_pargv, ( "CatchAndWriteContext_t::Invoke with bogus argc/argv" ) );
|
||||
return ((FnWMainIntRet)m_pfn)( *m_pargc, *m_pargv );
|
||||
case k_eSCatchAndWriteFunctionTypeVoidPtr:
|
||||
ErrorIfNot( m_ppv, ( "CatchAndWriteContext_t::Invoke with bogus void *ptr" ) );
|
||||
((FnVoidPtrFn)m_pfn)( *m_ppv );
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Catches and writes out any exception throw by the specified function
|
||||
// Input: pfn - Function to call within protective exception block
|
||||
// pv - Void pointer to pass that function
|
||||
// eAction - Specifies what to do if it catches an exception
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined(_PS3)
|
||||
|
||||
int CatchAndWriteMiniDump_Impl( CatchAndWriteContext_t &ctx )
|
||||
{
|
||||
// we dont handle minidumps on ps3
|
||||
return ctx.Invoke();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static const char *GetExceptionCodeName( unsigned long code )
|
||||
{
|
||||
switch ( code )
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION: return "accessviolation";
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "arrayboundsexceeded";
|
||||
case EXCEPTION_BREAKPOINT: return "breakpoint";
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT: return "datatypemisalignment";
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND: return "fltdenormaloperand";
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "fltdividebyzero";
|
||||
case EXCEPTION_FLT_INEXACT_RESULT: return "fltinexactresult";
|
||||
case EXCEPTION_FLT_INVALID_OPERATION: return "fltinvalidoperation";
|
||||
case EXCEPTION_FLT_OVERFLOW: return "fltoverflow";
|
||||
case EXCEPTION_FLT_STACK_CHECK: return "fltstackcheck";
|
||||
case EXCEPTION_FLT_UNDERFLOW: return "fltunderflow";
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO: return "intdividebyzero";
|
||||
case EXCEPTION_INT_OVERFLOW: return "intoverflow";
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "noncontinuableexception";
|
||||
case EXCEPTION_PRIV_INSTRUCTION: return "privinstruction";
|
||||
case EXCEPTION_SINGLE_STEP: return "singlestep";
|
||||
}
|
||||
|
||||
// Unknown exception
|
||||
return "crash";
|
||||
}
|
||||
|
||||
int CatchAndWriteMiniDump_Impl( CatchAndWriteContext_t &ctx )
|
||||
{
|
||||
// Sorry, this is the only action currently implemented!
|
||||
//Assert( ctx.m_eAction == k_ECatchAndWriteMiniDumpAbort );
|
||||
|
||||
if ( 0 && Plat_IsInDebugSession() )
|
||||
{
|
||||
// don't mask exceptions when running in the debugger
|
||||
return ctx.Invoke();
|
||||
}
|
||||
|
||||
// g_DumpHelper.Init();
|
||||
|
||||
// Win32 code gets to use a special handler
|
||||
#if defined( _WIN32 )
|
||||
__try
|
||||
{
|
||||
return ctx.Invoke();
|
||||
}
|
||||
__except ( g_pfnWriteMiniDump( GetExceptionCode(), GetExceptionInformation(), GetExceptionCodeName( GetExceptionCode() ) ), EXCEPTION_EXECUTE_HANDLER )
|
||||
{
|
||||
TerminateProcess( GetCurrentProcess(), EXIT_FAILURE ); // die, die RIGHT NOW! (don't call exit() so destructors will not get run)
|
||||
}
|
||||
|
||||
// if we get here, we definitely are not in an exception handler
|
||||
g_bInException = false;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
// if ( ctx.m_pargv != 0 )
|
||||
// {
|
||||
// g_DumpHelper.ComputeExeNameFromArgv0( (*ctx.m_pargv)[ 0 ] );
|
||||
// }
|
||||
//
|
||||
// ICrashHandler *handler = g_DumpHelper.GetHandlerAPI();
|
||||
// CCrashHandlerScope scope( handler, g_DumpHelper.GetProduct(), g_DumpHelper.GetVersion(), g_DumpHelper.GetBuildID(), false );
|
||||
// if ( handler )
|
||||
// handler->SetSteamID( g_DumpHelper.GetSteamID() );
|
||||
|
||||
return ctx.Invoke();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // _PS3
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Catches and writes out any exception throw by the specified function
|
||||
// Input: pfn - Function to call within protective exception block
|
||||
// pv - Void pointer to pass that function
|
||||
// eAction - Specifies what to do if it catches an exception
|
||||
//-----------------------------------------------------------------------------
|
||||
void CatchAndWriteMiniDumpEx( FnWMain pfn, int argc, tchar *argv[], ECatchAndWriteMinidumpAction eAction )
|
||||
{
|
||||
CatchAndWriteContext_t ctx;
|
||||
ctx.Set( k_eSCatchAndWriteFunctionTypeWMain, eAction, (void *)pfn, &argc, &argv, NULL );
|
||||
CatchAndWriteMiniDump_Impl( ctx );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Catches and writes out any exception throw by the specified function
|
||||
// Input: pfn - Function to call within protective exception block
|
||||
// pv - Void pointer to pass that function
|
||||
// eAction - Specifies what to do if it catches an exception
|
||||
//-----------------------------------------------------------------------------
|
||||
int CatchAndWriteMiniDumpExReturnsInt( FnWMainIntRet pfn, int argc, tchar *argv[], ECatchAndWriteMinidumpAction eAction )
|
||||
{
|
||||
CatchAndWriteContext_t ctx;
|
||||
ctx.Set( k_eSCatchAndWriteFunctionTypeWMainIntReg, eAction, (void *)pfn, &argc, &argv, NULL );
|
||||
return CatchAndWriteMiniDump_Impl( ctx );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Catches and writes out any exception throw by the specified function
|
||||
// Input: pfn - Function to call within protective exception block
|
||||
// pv - Void pointer to pass that function
|
||||
// eAction - Specifies what to do if it catches an exception
|
||||
//-----------------------------------------------------------------------------
|
||||
void CatchAndWriteMiniDumpExForVoidPtrFn( FnVoidPtrFn pfn, void *pv, ECatchAndWriteMinidumpAction eAction )
|
||||
{
|
||||
CatchAndWriteContext_t ctx;
|
||||
ctx.Set( k_eSCatchAndWriteFunctionTypeVoidPtr, eAction, (void *)pfn, NULL, NULL, &pv );
|
||||
CatchAndWriteMiniDump_Impl( ctx );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Catches and writes out any exception throw by the specified function
|
||||
// Input: pfn - Function to call within protective exception block
|
||||
// pv - Void pointer to pass that function
|
||||
// bExitQuietly - If true (for client) just exit after mindump, with no visible error for user
|
||||
// If false, re-throws.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CatchAndWriteMiniDumpForVoidPtrFn( FnVoidPtrFn pfn, void *pv, bool bExitQuietly )
|
||||
{
|
||||
return CatchAndWriteMiniDumpExForVoidPtrFn( pfn, pv, bExitQuietly ? k_ECatchAndWriteMiniDumpAbort : k_ECatchAndWriteMiniDumpReThrow );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Call this function to ensure that your program actually crashes when it crashes.
|
||||
|
||||
Oh my god.
|
||||
|
||||
When 64-bit Windows came out it turns out that it wasn't possible to throw
|
||||
and catch exceptions from user-mode, through kernel-mode, and back to user
|
||||
mode. Therefore, for crashes that happen in kernel callbacks such as Window
|
||||
procs Microsoft had to decide either to always crash when an exception
|
||||
is thrown (including an SEH such as an access violation) or else always silently
|
||||
swallow the exception.
|
||||
|
||||
They chose badly.
|
||||
|
||||
Therefore, for the last five or so years, programs on 64-bit Windows have been
|
||||
silently swallowing *some* exceptions. As a concrete example, consider this code:
|
||||
|
||||
case WM_PAINT:
|
||||
{
|
||||
hdc = BeginPaint(hWnd, &ps);
|
||||
char* p = new char;
|
||||
*(int*)0 = 0;
|
||||
delete p;
|
||||
EndPaint(hWnd, &ps);
|
||||
}
|
||||
break;
|
||||
|
||||
It's in a WindowProc handling a paint message so it will generally be called from
|
||||
kernel mode. Therefore the crash in the middle of it is, by default, 'handled' for
|
||||
us. The "delete p;" and EndPaint() never happen. This means that the process is
|
||||
left in an indeterminate state. It also means that our error reporting never sees
|
||||
the exception. It is effectively as though there is a __try/__except handler at the
|
||||
kernel boundary and any crashes cause the stack to be unwound (without destructors
|
||||
being run) to the kernel boundary where execution continues.
|
||||
|
||||
Charming.
|
||||
|
||||
The fix is to use the Get/SetProcessUserModeExceptionPolicy API to tell Windows
|
||||
that we don't want to struggle on after crashing.
|
||||
|
||||
For more scary details see this article. It actually suggests using the compatibility
|
||||
manifest, but that does not appear to work.
|
||||
http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/
|
||||
*/
|
||||
void EnableCrashingOnCrashes()
|
||||
{
|
||||
typedef BOOL (WINAPI *tGetProcessUserModeExceptionPolicy)(LPDWORD lpFlags);
|
||||
typedef BOOL (WINAPI *tSetProcessUserModeExceptionPolicy)(DWORD dwFlags);
|
||||
#define PROCESS_CALLBACK_FILTER_ENABLED 0x1
|
||||
|
||||
HMODULE kernel32 = LoadLibraryA("kernel32.dll");
|
||||
tGetProcessUserModeExceptionPolicy pGetProcessUserModeExceptionPolicy = (tGetProcessUserModeExceptionPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
|
||||
tSetProcessUserModeExceptionPolicy pSetProcessUserModeExceptionPolicy = (tSetProcessUserModeExceptionPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
|
||||
if (pGetProcessUserModeExceptionPolicy && pSetProcessUserModeExceptionPolicy)
|
||||
{
|
||||
DWORD dwFlags;
|
||||
if (pGetProcessUserModeExceptionPolicy(&dwFlags))
|
||||
{
|
||||
pSetProcessUserModeExceptionPolicy(dwFlags & ~PROCESS_CALLBACK_FILTER_ENABLED); // turn off bit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
PLATFORM_INTERFACE void WriteMiniDump( const char *pszFilenameSuffix )
|
||||
{
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] )
|
||||
{
|
||||
pfn( argc, argv );
|
||||
}
|
||||
|
||||
#endif
|
||||
#elif defined(_X360 )
|
||||
PLATFORM_INTERFACE void WriteMiniDump( const char *pszFilenameSuffix )
|
||||
{
|
||||
DmCrashDump(false);
|
||||
}
|
||||
|
||||
#else // !_WIN32
|
||||
#include "tier0/minidump.h"
|
||||
|
||||
PLATFORM_INTERFACE void WriteMiniDump( const char *pszFilenameSuffix )
|
||||
{
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] )
|
||||
{
|
||||
pfn( argc, argv );
|
||||
}
|
||||
|
||||
#endif
|
||||
330
tier0/miniprofiler.cpp
Normal file
330
tier0/miniprofiler.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/miniprofiler.h"
|
||||
#include "tier0/cache_hints.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/threadtools.h"
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _PS3
|
||||
#include "ps3/ps3_helpers.h"
|
||||
#endif
|
||||
|
||||
#if defined( PLATFORM_WINDOWS_PC )
|
||||
#define WIN_32_LEAN_AND_MEAN
|
||||
#include <windows.h> // Currently needed for LARGE_INTEGER
|
||||
#endif
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#ifdef IS_WINDOWS_PC
|
||||
CTHREADLOCALPTR( CMiniProfiler ) s_pLastMiniProfilerTS;
|
||||
#else
|
||||
CMiniProfiler *s_pLastMiniProfilerTS;
|
||||
#endif
|
||||
|
||||
static CLinkedMiniProfiler *s_pDummyList = NULL;
|
||||
|
||||
class CRootMiniProfiler : public CLinkedMiniProfiler
|
||||
{
|
||||
public:
|
||||
CRootMiniProfiler( void ) : CLinkedMiniProfiler( "DummyRoot", &s_pDummyList )
|
||||
{
|
||||
s_pLastMiniProfilerTS = this;
|
||||
}
|
||||
};
|
||||
CRootMiniProfiler g_rootMiniProfiler;
|
||||
|
||||
#if defined( STATIC_LINK ) || defined( _LINUX )
|
||||
// in static link scenario, we don't need any extra linkage specified where we define our variables
|
||||
#undef MINIPROFILER_DLL_LINKAGE
|
||||
#define MINIPROFILER_DLL_LINKAGE
|
||||
#else
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
MINIPROFILER_DLL_LINKAGE CMiniProfiler *g_pRootMiniProfiler = &g_rootMiniProfiler;
|
||||
MINIPROFILER_DLL_LINKAGE CLinkedMiniProfiler *g_pGlobalMiniProfilers = NULL;
|
||||
MINIPROFILER_DLL_LINKAGE CLinkedMiniProfiler *g_pAssertMiniProfilers = NULL;
|
||||
MINIPROFILER_DLL_LINKAGE CMiniProfiler *g_pLastMiniProfiler = &g_rootMiniProfiler;
|
||||
MINIPROFILER_DLL_LINKAGE uint32 g_nMiniProfilerFrame = 0;
|
||||
#if defined( STATIC_LINK ) || defined( _LINUX )
|
||||
#else
|
||||
}
|
||||
#endif
|
||||
|
||||
int64 GetHardwareClockReliably()
|
||||
{
|
||||
int64 res = 0;
|
||||
#if ENABLE_MINI_PROFILER && IS_WINDOWS_PC
|
||||
QueryPerformanceCounter( ( LARGE_INTEGER* )&res );
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
CMiniProfiler* PushMiniProfilerTS( CMiniProfiler *pProfiler )
|
||||
{
|
||||
CMiniProfiler *pLast = s_pLastMiniProfilerTS;
|
||||
if ( !pLast )
|
||||
{
|
||||
pLast = &g_rootMiniProfiler;
|
||||
}
|
||||
s_pLastMiniProfilerTS = pProfiler;
|
||||
return pLast;
|
||||
}
|
||||
|
||||
|
||||
CThreadFastMutex g_ProfilerListMutex;
|
||||
//CInterlockedInt g_LinkedMiniProfilerIdCount;
|
||||
|
||||
void AppendMiniProfilerToList( CLinkedMiniProfiler *pProfiler, CLinkedMiniProfiler **ppList )
|
||||
{
|
||||
#if ENABLE_MINI_PROFILER
|
||||
g_ProfilerListMutex.Lock();
|
||||
//pProfiler->m_nId = g_LinkedMiniProfilerIdCount++;
|
||||
if( IsDebug() )
|
||||
{
|
||||
int nProfilerCount = 0; NOTE_UNUSED( nProfilerCount );
|
||||
CLinkedMiniProfiler *pTest;
|
||||
for( pTest = *ppList; pTest; pTest = pTest->m_pNext )
|
||||
{
|
||||
nProfilerCount++;
|
||||
if( pTest == pProfiler )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( pProfiler == pTest && !pProfiler->m_ppPrev )
|
||||
DebuggerBreak(); // the miniprofiler is not yet added to any list (pprev == 0) but it's already in this list (pTest == pProfiler)
|
||||
if( !pTest && pProfiler->m_ppPrev )
|
||||
DebuggerBreak(); // the profiler is not in this list, but it's already in some list (pNext!=0)
|
||||
}
|
||||
|
||||
if( !pProfiler->m_ppPrev )
|
||||
{
|
||||
// the profiler is not yet in any list
|
||||
|
||||
if ( ppList )
|
||||
{
|
||||
pProfiler->m_pNext = *ppList;
|
||||
pProfiler->m_ppPrev = ppList;
|
||||
if( pProfiler->m_pNext )
|
||||
{
|
||||
pProfiler->m_pNext->m_ppPrev = &pProfiler->m_pNext;
|
||||
}
|
||||
*ppList = pProfiler;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !ppList )
|
||||
{
|
||||
// Warning: unhooking something that wasn't removed from the previous list?
|
||||
if( IsDebug() )
|
||||
{
|
||||
DebuggerBreak();
|
||||
}
|
||||
pProfiler->m_pNext = NULL;
|
||||
pProfiler->m_ppPrev = NULL;
|
||||
}
|
||||
}
|
||||
g_ProfilerListMutex.Unlock();
|
||||
#endif // ENABLE_MINI_PROFILER
|
||||
}
|
||||
|
||||
|
||||
void RemoveMiniProfilerFromList( CLinkedMiniProfiler *pProfiler )
|
||||
{
|
||||
#if ENABLE_MINI_PROFILER
|
||||
g_ProfilerListMutex.Lock();
|
||||
// We need to remove miniprofiler from the list properly. This is an issue because we unload DLLs sometimes.
|
||||
if ( pProfiler->m_ppPrev )
|
||||
{
|
||||
*pProfiler->m_ppPrev = pProfiler->m_pNext; // that's it: we just remove this object from the list by linking previous object with the next
|
||||
}
|
||||
if ( pProfiler->m_pNext )
|
||||
{
|
||||
pProfiler->m_pNext->m_ppPrev = pProfiler->m_ppPrev;
|
||||
}
|
||||
|
||||
// unhook the profiler from the list completely, so that we don't try to do it twice
|
||||
pProfiler->m_ppPrev = NULL;
|
||||
pProfiler->m_pNext = NULL;
|
||||
|
||||
g_ProfilerListMutex.Unlock();
|
||||
#endif // ENABLE_MINI_PROFILER
|
||||
}
|
||||
|
||||
#if ENABLE_HARDWARE_PROFILER
|
||||
DLL_CLASS_EXPORT void CMiniProfiler::Publish(const char *szMessage, ...)
|
||||
{
|
||||
#ifdef _X360
|
||||
if(m_numCalls >= 100 || m_numTimeBaseTicks > 50) // 500 timebase ticks is 1 microsecond
|
||||
{
|
||||
char szBuf[256];
|
||||
va_list args;
|
||||
va_start(args, szMessage);
|
||||
vsnprintf(szBuf, sizeof(szBuf), szMessage, args);
|
||||
PIXAddNamedCounter(float(INT32(m_numTimeBaseTicks-m_numTimeBaseTicksInCallees))*0.02f, "Ex:%s,mcs", szBuf);
|
||||
PIXAddNamedCounter(float(INT32(m_numTimeBaseTicks))*0.02f, "%s,mcs", szBuf);
|
||||
if(m_numCalls)
|
||||
PIXAddNamedCounter((float)(64*INT32(m_numTimeBaseTicks)/INT32(m_numCalls)), "%s,ticks", szBuf);
|
||||
PIXAddNamedCounter((float)(INT32(m_numCalls)), "%s,calls", szBuf);
|
||||
}
|
||||
#endif
|
||||
Reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void CLinkedMiniProfiler::Publish(uint nHistoryMax)
|
||||
{
|
||||
#if ENABLE_HARDWARE_PROFILER
|
||||
if(nHistoryMax != m_nHistoryMax)
|
||||
{
|
||||
PurgeHistory();
|
||||
delete[]m_pHistory;
|
||||
m_nHistoryMax = nHistoryMax;
|
||||
if(nHistoryMax)
|
||||
m_pHistory = new CMiniProfiler[nHistoryMax];
|
||||
else
|
||||
m_pHistory = NULL;
|
||||
m_nHistoryLength = 0;
|
||||
m_nFrameHistoryBegins = g_nMiniProfilerFrame;
|
||||
}
|
||||
else
|
||||
if(m_nHistoryLength >= nHistoryMax)
|
||||
{
|
||||
PurgeHistory();
|
||||
}
|
||||
if(m_pHistory)
|
||||
m_pHistory[m_nHistoryLength++] = *this;
|
||||
|
||||
CMiniProfiler::Publish(m_szName);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if ENABLE_HARDWARE_PROFILER
|
||||
static char g_szFileName[128] = "";
|
||||
static FILE *g_pPurgeFile = NULL;
|
||||
#endif
|
||||
|
||||
void CLinkedMiniProfiler::PurgeHistory()
|
||||
{
|
||||
#if ENABLE_HARDWARE_PROFILER
|
||||
if(m_nHistoryLength && g_pPurgeFile)
|
||||
{
|
||||
size_t len = (strlen(m_szName) + 3) & ~3;
|
||||
fwrite(&len, sizeof(len), 1, g_pPurgeFile);
|
||||
fwrite(m_szName, len, 1, g_pPurgeFile);
|
||||
fwrite(&m_nFrameHistoryBegins, sizeof(m_nFrameHistoryBegins), 1, g_pPurgeFile);
|
||||
fwrite(&m_nHistoryLength, sizeof(m_nHistoryLength), 1, g_pPurgeFile);
|
||||
fwrite(m_pHistory, sizeof(CMiniProfiler), m_nHistoryLength, g_pPurgeFile);
|
||||
m_nHistoryLength = 0; // reset the history, nothing else
|
||||
m_nFrameHistoryBegins = g_nMiniProfilerFrame;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C"
|
||||
MINIPROFILER_DLL_LINKAGE void PublishAll( CLinkedMiniProfiler*pList, uint32 nHistoryMax )
|
||||
{
|
||||
#if ENABLE_HARDWARE_PROFILER
|
||||
for(CLinkedMiniProfiler *prof = pList; prof; prof = prof->m_pNext)
|
||||
prof->Publish(nHistoryMax);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MicroProfilerAddTS( CMicroProfiler *pProfiler, uint64 numTimeBaseTicks )
|
||||
{
|
||||
#if ENABLE_MICRO_PROFILER > 0
|
||||
ThreadInterlockedExchangeAdd64( ( int64* )&pProfiler->m_numTimeBaseTicks, numTimeBaseTicks );
|
||||
ThreadInterlockedIncrement( ( int32* )&pProfiler->m_numCalls );
|
||||
#endif
|
||||
}
|
||||
|
||||
void PopMiniProfilerTS( CMiniProfiler *pProfiler )
|
||||
{
|
||||
s_pLastMiniProfilerTS = pProfiler;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void GetPerformanceFrequency( int64 *pFreqOut )
|
||||
{
|
||||
#ifdef PLATFORM_POSIX
|
||||
*pFreqOut = 2000000000;
|
||||
#elif defined( _PS3 )
|
||||
*pFreqOut = 3200000000ll;
|
||||
#else
|
||||
QueryPerformanceFrequency( ( LARGE_INTEGER* ) pFreqOut );
|
||||
#endif
|
||||
}
|
||||
|
||||
DLL_EXPORT void PublishAllMiniProfilers(int nHistoryMax)
|
||||
{
|
||||
#if ENABLE_HARDWARE_PROFILER
|
||||
if(nHistoryMax >= 0/*cv_phys_enable_PIX_counters.GetBool()*/)
|
||||
{
|
||||
if(nHistoryMax && !g_pPurgeFile)
|
||||
{
|
||||
if(!g_szFileName[0])
|
||||
{
|
||||
tm lt;
|
||||
Plat_GetLocalTime( < );
|
||||
//SYSTEMTIME st;
|
||||
//GetLocalTime(&st);
|
||||
//sprintf(g_szFileName, "D:\\mp%02d-%02d-%02d-%02d-%02d-%02d.dmp", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
|
||||
sprintf(g_szFileName, "miniprofile%02d%02d-%02d_%02d_%02d.prf", /*lt->tm_year+1900, */lt.tm_mon+1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec);
|
||||
}
|
||||
g_pPurgeFile = fopen(g_szFileName, "ab");
|
||||
if(g_pPurgeFile)
|
||||
{
|
||||
int nVersion = 0x0101;
|
||||
fwrite(&nVersion, 4, 1, g_pPurgeFile);
|
||||
#ifdef _X360
|
||||
int nFrequency = 49875; // ticks per millisecond
|
||||
#else
|
||||
// even though this is not correct on older computers, I think most modern computers have the multimedia clock that has the same frequency as the CPU
|
||||
int64 nActualFrequency = GetCPUInformation().m_Speed;
|
||||
int nFrequency = int((nActualFrequency+500) / 1000);
|
||||
#endif
|
||||
fwrite(&nFrequency, 4, 1, g_pPurgeFile);
|
||||
}
|
||||
}
|
||||
|
||||
PublishAll(g_pPhysicsMiniProfilers,nHistoryMax);
|
||||
PublishAll(g_pOtherMiniProfilers,nHistoryMax);
|
||||
g_rootMiniProfiler.Reset();
|
||||
g_nMiniProfilerFrame ++;
|
||||
|
||||
if(g_pPurgeFile)
|
||||
{
|
||||
if(nHistoryMax)
|
||||
fflush(g_pPurgeFile);
|
||||
else
|
||||
{
|
||||
Msg("Closing profile: '%s'\n", g_szFileName);
|
||||
fclose(g_pPurgeFile);
|
||||
g_pPurgeFile = NULL;
|
||||
g_szFileName[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
11
tier0/pch_tier0.cpp
Normal file
11
tier0/pch_tier0.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
|
||||
49
tier0/pch_tier0.h
Normal file
49
tier0/pch_tier0.h
Normal file
@@ -0,0 +1,49 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
|
||||
#if defined( PLATFORM_WINDOWS_PC )
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define _WIN32_WINNT 0x0403
|
||||
#include <windows.h>
|
||||
#elif defined( _PS3 )
|
||||
#include <cellstatus.h>
|
||||
#include <sys/prx.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
// First include standard libraries
|
||||
#include "tier0/valve_off.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#ifdef PLATFORM_POSIX
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#define _MAX_PATH PATH_MAX
|
||||
#endif
|
||||
|
||||
#include "tier0/valve_on.h"
|
||||
|
||||
#include "tier0/basetypes.h"
|
||||
#include "tier0/dbgflag.h"
|
||||
#include "tier0/dbg.h"
|
||||
#ifdef STEAM
|
||||
#include "tier0/memhook.h"
|
||||
#endif
|
||||
#include "tier0/validator.h"
|
||||
#include "tier0/fasttimer.h"
|
||||
50
tier0/perfstats.cpp
Normal file
50
tier0/perfstats.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#include "tier0/perfstats.h"
|
||||
|
||||
CPerfStatsData g_PerfStats;
|
||||
|
||||
CPerfStatsData::CPerfStatsData()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void CPerfStatsData::Tick()
|
||||
{
|
||||
if ( m_nFrames != 0 )
|
||||
{
|
||||
CPerfStatsSlotData* pMainSlot = &m_Slots[PERF_STATS_SLOT_MAINTHREAD];
|
||||
pMainSlot->EndTimer();
|
||||
|
||||
CPerfStatsSlotData* pMainNoWaitSlot = &m_Slots[PERF_STATS_SLOT_MAINTHREAD_NOWAIT];
|
||||
pMainNoWaitSlot->m_pszName = "MainThreadNoWait";
|
||||
CCycleCount::Sub(pMainSlot->m_CurrFrameTime, m_Slots[PERF_STATS_SLOT_END_FRAME].m_CurrFrameTime, pMainNoWaitSlot->m_CurrFrameTime);
|
||||
pMainNoWaitSlot->m_AccTotalTime += pMainNoWaitSlot->m_CurrFrameTime;
|
||||
}
|
||||
|
||||
++m_nFrames;
|
||||
for ( int iSlot = 0; iSlot < PERF_STATS_SLOT_MAX; ++iSlot )
|
||||
{
|
||||
m_Slots[iSlot].m_PrevFrameTime = m_Slots[iSlot].m_CurrFrameTime;
|
||||
m_Slots[iSlot].ResetFrameStats();
|
||||
}
|
||||
|
||||
m_Slots[PERF_STATS_SLOT_MAINTHREAD].StartTimer("MainThread");
|
||||
}
|
||||
|
||||
void CPerfStatsData::Reset()
|
||||
{
|
||||
m_nFrames = 0;
|
||||
|
||||
for ( int iSlot = 0; iSlot < PERF_STATS_SLOT_MAX; ++iSlot )
|
||||
{
|
||||
m_Slots[iSlot].Reset();
|
||||
}
|
||||
}
|
||||
782
tier0/platform.cpp
Normal file
782
tier0/platform.cpp
Normal file
@@ -0,0 +1,782 @@
|
||||
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include <time.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(_X360)
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define _WIN32_WINNT 0x0403
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include "tier0/platform.h"
|
||||
#if defined( _X360 )
|
||||
#include "xbox/xbox_console.h"
|
||||
#endif
|
||||
#include "tier0/threadtools.h"
|
||||
#include "tier0/minidump.h"
|
||||
|
||||
#include "tier0/memalloc.h"
|
||||
|
||||
#if defined( _PS3 )
|
||||
#include <cell/fios/fios_common.h>
|
||||
#include <cell/fios/fios_memory.h>
|
||||
#include <cell/fios/fios_configuration.h>
|
||||
#include <sys/process.h>
|
||||
#include <sysutil/sysutil_common.h>
|
||||
#include <sysutil/sysutil_sysparam.h>
|
||||
#include <sys/time.h>
|
||||
#include "tls_ps3.h"
|
||||
|
||||
#if !defined(_CERT)
|
||||
#include "sn/LibSN.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
#include <sys/types.h>
|
||||
#include <sys/process.h>
|
||||
#include <sys/prx.h>
|
||||
|
||||
#include <sysutil/sysutil_syscache.h>
|
||||
|
||||
#include <cell/sysmodule.h>
|
||||
*/
|
||||
#include <cell/fios/fios_time.h>
|
||||
#endif // _PS3
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// Benchmark mode uses this heavy-handed method
|
||||
static bool g_bBenchmarkMode = false;
|
||||
#ifdef _WIN32
|
||||
static double g_FakeBenchmarkTime = 0;
|
||||
static double g_FakeBenchmarkTimeInc = 1.0 / 66.0;
|
||||
#endif
|
||||
|
||||
static CThreadFastMutex g_LocalTimeMutex;
|
||||
|
||||
//our global error callback function. Note that this is not initialized, but static space guarantees this is NULL at app start.
|
||||
//If you initialize, it will set to zero again when the CPP runs its static initializers, which could stomp the value if another
|
||||
//CPP sets this value while initializing its static space
|
||||
static ExitProcessWithErrorCBFn g_pfnExitProcessWithErrorCB; //= NULL
|
||||
|
||||
bool Plat_IsInBenchmarkMode()
|
||||
{
|
||||
return g_bBenchmarkMode;
|
||||
}
|
||||
|
||||
void Plat_SetBenchmarkMode( bool bBenchmark )
|
||||
{
|
||||
g_bBenchmarkMode = bBenchmark;
|
||||
}
|
||||
|
||||
#if defined( PLATFORM_WINDOWS )
|
||||
|
||||
static LARGE_INTEGER g_PerformanceFrequency;
|
||||
static double g_PerformanceCounterToS;
|
||||
static double g_PerformanceCounterToMS;
|
||||
static double g_PerformanceCounterToUS;
|
||||
static LARGE_INTEGER g_ClockStart;
|
||||
static bool s_bTimeInitted;
|
||||
|
||||
static void InitTime()
|
||||
{
|
||||
if( !s_bTimeInitted )
|
||||
{
|
||||
s_bTimeInitted = true;
|
||||
QueryPerformanceFrequency(&g_PerformanceFrequency);
|
||||
g_PerformanceCounterToS = 1.0 / g_PerformanceFrequency.QuadPart;
|
||||
g_PerformanceCounterToMS = 1e3 / g_PerformanceFrequency.QuadPart;
|
||||
g_PerformanceCounterToUS = 1e6 / g_PerformanceFrequency.QuadPart;
|
||||
QueryPerformanceCounter(&g_ClockStart);
|
||||
}
|
||||
}
|
||||
|
||||
double Plat_FloatTime()
|
||||
{
|
||||
if (! s_bTimeInitted )
|
||||
InitTime();
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return g_FakeBenchmarkTime;
|
||||
}
|
||||
|
||||
LARGE_INTEGER CurrentTime;
|
||||
|
||||
QueryPerformanceCounter( &CurrentTime );
|
||||
|
||||
double fRawSeconds = (double)( CurrentTime.QuadPart - g_ClockStart.QuadPart ) * g_PerformanceCounterToS;
|
||||
|
||||
return fRawSeconds;
|
||||
}
|
||||
|
||||
uint32 Plat_MSTime()
|
||||
{
|
||||
if (! s_bTimeInitted )
|
||||
InitTime();
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return (uint32)(g_FakeBenchmarkTime * 1000.0);
|
||||
}
|
||||
|
||||
LARGE_INTEGER CurrentTime;
|
||||
|
||||
QueryPerformanceCounter( &CurrentTime );
|
||||
|
||||
return (uint32) ( ( CurrentTime.QuadPart - g_ClockStart.QuadPart ) * g_PerformanceCounterToMS );
|
||||
}
|
||||
|
||||
uint64 Plat_USTime()
|
||||
{
|
||||
if (! s_bTimeInitted )
|
||||
InitTime();
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return (uint64)(g_FakeBenchmarkTime * 1e6);
|
||||
}
|
||||
|
||||
LARGE_INTEGER CurrentTime;
|
||||
|
||||
QueryPerformanceCounter( &CurrentTime );
|
||||
|
||||
return (uint64) ( ( CurrentTime.QuadPart - g_ClockStart.QuadPart ) * g_PerformanceCounterToUS );
|
||||
}
|
||||
|
||||
uint64 Plat_GetClockStart()
|
||||
{
|
||||
if ( !s_bTimeInitted )
|
||||
InitTime();
|
||||
return g_ClockStart.QuadPart;
|
||||
}
|
||||
|
||||
#elif defined( PLATFORM_PS3 )
|
||||
|
||||
cell::fios::abstime_t g_fiosLaunchTime = 0;
|
||||
|
||||
double Plat_FloatTime()
|
||||
{
|
||||
return cell::fios::FIOSAbstimeToMicroseconds( cell::fios::FIOSGetCurrentTime() - g_fiosLaunchTime ) * 1e-6;
|
||||
}
|
||||
|
||||
uint32 Plat_MSTime()
|
||||
{
|
||||
return (uint32) cell::fios::FIOSAbstimeToMilliseconds( cell::fios::FIOSGetCurrentTime() - g_fiosLaunchTime );
|
||||
}
|
||||
|
||||
uint64 Plat_USTime()
|
||||
{
|
||||
return cell::fios::FIOSAbstimeToMicroseconds( cell::fios::FIOSGetCurrentTime() - g_fiosLaunchTime );
|
||||
}
|
||||
|
||||
uint64 Plat_GetClockStart()
|
||||
{
|
||||
return g_fiosLaunchTime;
|
||||
}
|
||||
|
||||
#endif // PLATFORM_WINDOWS/PLATFORM_PS3
|
||||
|
||||
uint64 Plat_GetTime()
|
||||
{
|
||||
// We just provide a wrapper on this function so we can protect access to time() everywhere.
|
||||
time_t ltime;
|
||||
time( <ime );
|
||||
return ltime;
|
||||
}
|
||||
|
||||
void Plat_GetLocalTime( struct tm *pNow )
|
||||
{
|
||||
// We just provide a wrapper on this function so we can protect access to time() everywhere.
|
||||
time_t ltime;
|
||||
time( <ime );
|
||||
|
||||
Plat_ConvertToLocalTime( ltime, pNow );
|
||||
}
|
||||
|
||||
void Plat_ConvertToLocalTime( uint64 nTime, struct tm *pNow )
|
||||
{
|
||||
// Since localtime() returns a global, we need to protect against multiple threads stomping it.
|
||||
g_LocalTimeMutex.Lock();
|
||||
|
||||
time_t ltime = (time_t)nTime;
|
||||
tm *pTime = localtime( <ime );
|
||||
if ( pTime )
|
||||
*pNow = *pTime;
|
||||
else
|
||||
memset( pNow, 0, sizeof( *pNow ) );
|
||||
|
||||
g_LocalTimeMutex.Unlock();
|
||||
}
|
||||
|
||||
void Plat_GetTimeString( struct tm *pTime, char *pOut, int nMaxBytes )
|
||||
{
|
||||
g_LocalTimeMutex.Lock();
|
||||
|
||||
char *pStr = asctime( pTime );
|
||||
strncpy( pOut, pStr, nMaxBytes );
|
||||
pOut[nMaxBytes-1] = 0;
|
||||
|
||||
g_LocalTimeMutex.Unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
// timezone
|
||||
int32 Plat_timezone( void )
|
||||
{
|
||||
#ifdef _PS3
|
||||
g_LocalTimeMutex.Lock();
|
||||
int timezone = 0;
|
||||
if ( cellSysutilGetSystemParamInt (CELL_SYSUTIL_SYSTEMPARAM_ID_TIMEZONE, &timezone ) < CELL_OK ) // Not Threadsafe
|
||||
timezone = 0;
|
||||
g_LocalTimeMutex.Unlock();
|
||||
return timezone;
|
||||
#else
|
||||
return timezone;
|
||||
#endif
|
||||
}
|
||||
|
||||
// daylight savings
|
||||
int32 Plat_daylight( void )
|
||||
{
|
||||
#ifdef _PS3
|
||||
g_LocalTimeMutex.Lock();
|
||||
int32 daylight = 0;
|
||||
if ( cellSysutilGetSystemParamInt (CELL_SYSUTIL_SYSTEMPARAM_ID_SUMMERTIME, &daylight ) < CELL_OK )
|
||||
daylight = 0;
|
||||
g_LocalTimeMutex.Unlock();
|
||||
return daylight;
|
||||
#else
|
||||
return daylight;
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
void Platform_gmtime( uint64 nTime, struct tm *pTime )
|
||||
{
|
||||
time_t tmtTime = nTime;
|
||||
#ifdef _PS3
|
||||
struct tm * tmp = gmtime( &tmtTime );
|
||||
* pTime = * tmp;
|
||||
#else
|
||||
gmtime_s( pTime, &tmtTime );
|
||||
#endif
|
||||
}
|
||||
|
||||
time_t Plat_timegm( struct tm *timeptr )
|
||||
{
|
||||
#ifndef _GAMECONSOLE
|
||||
return _mkgmtime( timeptr );
|
||||
#else
|
||||
int *pnCrashHereBecauseConsolesDontSupportMkGmTime = 0;
|
||||
*pnCrashHereBecauseConsolesDontSupportMkGmTime = 0;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Plat_GetModuleFilename( char *pOut, int nMaxBytes )
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
SetLastError( ERROR_SUCCESS ); // clear the error code
|
||||
GetModuleFileName( NULL, pOut, nMaxBytes );
|
||||
if ( GetLastError() != ERROR_SUCCESS )
|
||||
Error( "Plat_GetModuleFilename: The buffer given is too small (%d bytes).", nMaxBytes );
|
||||
#elif PLATFORM_X360
|
||||
pOut[0] = 0x00; // return null string on Xbox 360
|
||||
#else
|
||||
// We shouldn't need this on POSIX.
|
||||
Assert( false );
|
||||
pOut[0] = 0x00; // Null the returned string in release builds
|
||||
#endif
|
||||
}
|
||||
|
||||
void Plat_ExitProcess( int nCode )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
// We don't want global destructors in our process OR in any DLL to get executed.
|
||||
// _exit() avoids calling global destructors in our module, but not in other DLLs.
|
||||
const char *pchCmdLineA = Plat_GetCommandLineA();
|
||||
if ( nCode || ( strstr( pchCmdLineA, "gc.exe" ) && strstr( pchCmdLineA, "gc.dll" ) && strstr( pchCmdLineA, "-gc" ) ) )
|
||||
{
|
||||
int *x = NULL; *x = 1; // cause a hard crash, GC is not allowed to exit voluntarily from gc.dll
|
||||
}
|
||||
TerminateProcess( GetCurrentProcess(), nCode );
|
||||
#elif defined(_PS3)
|
||||
// We do not use this path to exit on PS3 (naturally), rather we want a clear crash:
|
||||
int *x = NULL; *x = 1;
|
||||
#else
|
||||
_exit( nCode );
|
||||
#endif
|
||||
}
|
||||
|
||||
void Plat_ExitProcessWithError( int nCode, bool bGenerateMinidump )
|
||||
{
|
||||
//try to delegate out if they have registered a callback
|
||||
if( g_pfnExitProcessWithErrorCB )
|
||||
{
|
||||
if( g_pfnExitProcessWithErrorCB( nCode ) )
|
||||
return;
|
||||
}
|
||||
|
||||
//handle default behavior
|
||||
if( bGenerateMinidump )
|
||||
{
|
||||
//don't generate mini dumps in the debugger
|
||||
if( !Plat_IsInDebugSession() )
|
||||
{
|
||||
WriteMiniDump();
|
||||
}
|
||||
}
|
||||
|
||||
//and exit our process
|
||||
Plat_ExitProcess( nCode );
|
||||
}
|
||||
|
||||
void Plat_SetExitProcessWithErrorCB( ExitProcessWithErrorCBFn pfnCB )
|
||||
{
|
||||
g_pfnExitProcessWithErrorCB = pfnCB;
|
||||
}
|
||||
|
||||
void GetCurrentDate( int *pDay, int *pMonth, int *pYear )
|
||||
{
|
||||
struct tm long_time;
|
||||
Plat_GetLocalTime( &long_time );
|
||||
|
||||
*pDay = long_time.tm_mday;
|
||||
*pMonth = long_time.tm_mon + 1;
|
||||
*pYear = long_time.tm_year + 1900;
|
||||
}
|
||||
|
||||
// Wraps the thread-safe versions of asctime. buf must be at least 26 bytes
|
||||
char *Plat_asctime( const struct tm *tm, char *buf, size_t bufsize )
|
||||
{
|
||||
#ifdef _PS3
|
||||
snprintf( buf, bufsize, "%s", asctime(tm) );
|
||||
return buf;
|
||||
#else
|
||||
if ( EINVAL == asctime_s( buf, bufsize, tm ) )
|
||||
return NULL;
|
||||
else
|
||||
return buf;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Wraps the thread-safe versions of ctime. buf must be at least 26 bytes
|
||||
char *Plat_ctime( const time_t *timep, char *buf, size_t bufsize )
|
||||
{
|
||||
#ifdef _PS3
|
||||
snprintf( buf, bufsize, "%s", ctime( timep ) );
|
||||
return buf;
|
||||
#else
|
||||
if ( EINVAL == ctime_s( buf, bufsize, timep ) )
|
||||
return NULL;
|
||||
else
|
||||
return buf;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Wraps the thread-safe versions of gmtime
|
||||
struct tm *Platform_gmtime( const time_t *timep, struct tm *result )
|
||||
{
|
||||
#ifdef _PS3
|
||||
*result = *gmtime( timep );
|
||||
return result;
|
||||
#else
|
||||
if ( EINVAL == gmtime_s( result, timep ) )
|
||||
return NULL;
|
||||
else
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Wraps the thread-safe versions of localtime
|
||||
struct tm *Plat_localtime( const time_t *timep, struct tm *result )
|
||||
{
|
||||
#ifdef _PS3
|
||||
*result = *localtime( timep );
|
||||
return result;
|
||||
#else
|
||||
if ( EINVAL == localtime_s( result, timep ) )
|
||||
return NULL;
|
||||
else
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool vtune( bool resume )
|
||||
{
|
||||
#if IS_WINDOWS_PC
|
||||
static bool bInitialized = false;
|
||||
static void (__cdecl *VTResume)(void) = NULL;
|
||||
static void (__cdecl *VTPause) (void) = NULL;
|
||||
|
||||
// Grab the Pause and Resume function pointers from the VTune DLL the first time through:
|
||||
if( !bInitialized )
|
||||
{
|
||||
bInitialized = true;
|
||||
|
||||
HINSTANCE pVTuneDLL = LoadLibrary( "vtuneapi.dll" );
|
||||
|
||||
if( pVTuneDLL )
|
||||
{
|
||||
VTResume = (void(__cdecl *)())GetProcAddress( pVTuneDLL, "VTResume" );
|
||||
VTPause = (void(__cdecl *)())GetProcAddress( pVTuneDLL, "VTPause" );
|
||||
}
|
||||
}
|
||||
|
||||
// Call the appropriate function, as indicated by the argument:
|
||||
if( resume && VTResume )
|
||||
{
|
||||
VTResume();
|
||||
return true;
|
||||
|
||||
}
|
||||
else if( !resume && VTPause )
|
||||
{
|
||||
VTPause();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Plat_IsInDebugSession()
|
||||
{
|
||||
#if defined( _X360 )
|
||||
return (XBX_IsDebuggerPresent() != 0);
|
||||
#elif defined( _WIN32 )
|
||||
return (IsDebuggerPresent() != 0);
|
||||
#elif defined( _PS3 ) && !defined(_CERT)
|
||||
return snIsDebuggerPresent();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Plat_DebugString( const char * psz )
|
||||
{
|
||||
#ifdef _CERT
|
||||
return; // do nothing!
|
||||
#endif
|
||||
|
||||
#if defined( _X360 )
|
||||
XBX_OutputDebugString( psz );
|
||||
#elif defined( _WIN32 )
|
||||
::OutputDebugStringA( psz );
|
||||
#elif defined(_PS3)
|
||||
printf("%s",psz);
|
||||
#else
|
||||
// do nothing?
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if defined( PLATFORM_WINDOWS_PC )
|
||||
void Plat_MessageBox( const char *pTitle, const char *pMessage )
|
||||
{
|
||||
MessageBox( NULL, pMessage, pTitle, MB_OK );
|
||||
}
|
||||
#endif
|
||||
|
||||
PlatOSVersion_t Plat_GetOSVersion()
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
OSVERSIONINFO info;
|
||||
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
if ( GetVersionEx( &info ) )
|
||||
{
|
||||
if ( info.dwMajorVersion > 24 )
|
||||
info.dwMajorVersion = 24; // clamp to fit the version in single byte
|
||||
return PlatOSVersion_t( ( info.dwMajorVersion*10 ) + ( info.dwMinorVersion%10 ) );
|
||||
}
|
||||
return PLAT_OS_VERSION_UNKNOWN;
|
||||
#elif defined( PLATFORM_X360 )
|
||||
return PLAT_OS_VERSION_XBOX360;
|
||||
#else
|
||||
return PLAT_OS_VERSION_UNKNOWN;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined( PLATFORM_PS3 )
|
||||
//copied from platform_posix.cpp
|
||||
static char g_CmdLine[ 2048 ] = "";
|
||||
PLATFORM_INTERFACE void Plat_SetCommandLine( const char *cmdLine )
|
||||
{
|
||||
strncpy( g_CmdLine, cmdLine, sizeof(g_CmdLine) );
|
||||
g_CmdLine[ sizeof(g_CmdLine) -1 ] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
PLATFORM_INTERFACE const tchar *Plat_GetCommandLine()
|
||||
{
|
||||
#if defined( _PS3 )
|
||||
#pragma message("Plat_GetCommandLine() not implemented on PS3") // ****
|
||||
return g_CmdLine;
|
||||
#elif defined( TCHAR_IS_WCHAR )
|
||||
return GetCommandLineW();
|
||||
#else
|
||||
return GetCommandLine();
|
||||
#endif
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE const char *Plat_GetCommandLineA()
|
||||
{
|
||||
#if defined( _PS3 )
|
||||
#pragma message("Plat_GetCommandLineA() not implemented on PS3") // ****
|
||||
return g_CmdLine;
|
||||
#else
|
||||
return GetCommandLineA();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dynamically load a function
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
void *Plat_GetProcAddress( const char *pszModule, const char *pszName )
|
||||
{
|
||||
HMODULE hModule = ::LoadLibrary( pszModule );
|
||||
return ( hModule ) ? ::GetProcAddress( hModule, pszName ) : NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- //
|
||||
// Memory stuff.
|
||||
//
|
||||
// DEPRECATED. Still here to support binary back compatability of tier0.dll
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------------- //
|
||||
#ifndef _X360
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
|
||||
typedef void (*Plat_AllocErrorFn)( unsigned long size );
|
||||
|
||||
void Plat_DefaultAllocErrorFn( unsigned long size )
|
||||
{
|
||||
}
|
||||
|
||||
Plat_AllocErrorFn g_AllocError = Plat_DefaultAllocErrorFn;
|
||||
#endif
|
||||
|
||||
#if !defined( _X360 ) && !defined( _PS3 )
|
||||
|
||||
|
||||
CRITICAL_SECTION g_AllocCS;
|
||||
class CAllocCSInit
|
||||
{
|
||||
public:
|
||||
CAllocCSInit()
|
||||
{
|
||||
InitializeCriticalSection( &g_AllocCS );
|
||||
}
|
||||
} g_AllocCSInit;
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void* Plat_Alloc( unsigned long size )
|
||||
{
|
||||
EnterCriticalSection( &g_AllocCS );
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
void *pRet = MemAlloc_Alloc( size );
|
||||
#else
|
||||
void *pRet = malloc( size );
|
||||
#endif
|
||||
LeaveCriticalSection( &g_AllocCS );
|
||||
if ( pRet )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
g_AllocError( size );
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void* Plat_Realloc( void *ptr, unsigned long size )
|
||||
{
|
||||
EnterCriticalSection( &g_AllocCS );
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
void *pRet = g_pMemAlloc->Realloc( ptr, size );
|
||||
#else
|
||||
void *pRet = realloc( ptr, size );
|
||||
#endif
|
||||
LeaveCriticalSection( &g_AllocCS );
|
||||
if ( pRet )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
g_AllocError( size );
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void Plat_Free( void *ptr )
|
||||
{
|
||||
EnterCriticalSection( &g_AllocCS );
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
g_pMemAlloc->Free( ptr );
|
||||
#else
|
||||
free( ptr );
|
||||
#endif
|
||||
LeaveCriticalSection( &g_AllocCS );
|
||||
}
|
||||
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
PLATFORM_INTERFACE void Plat_SetAllocErrorFn( Plat_AllocErrorFn fn )
|
||||
{
|
||||
g_AllocError = fn;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
void Plat_getwd( char *pWorkingDirectory, size_t nBufLen )
|
||||
{
|
||||
#if defined( PLATFORM_X360 )
|
||||
V_tier0_strncpy( pWorkingDirectory, s_pWorkingDir, nBufLen );
|
||||
#elif defined( PLATFORM_WINDOWS )
|
||||
_getcwd( pWorkingDirectory, ( int )nBufLen );
|
||||
V_tier0_strncat( pWorkingDirectory, "\\", ( int )nBufLen );
|
||||
#else // PLATFORM_X360/PLATFORM_WINDOWS
|
||||
if ( getcwd( pWorkingDirectory, nBufLen ) )
|
||||
{
|
||||
V_tier0_strncat( pWorkingDirectory, "/", ( int )nBufLen );
|
||||
}
|
||||
else
|
||||
{
|
||||
V_tier0_strncpy( pWorkingDirectory, "./", ( int )nBufLen );
|
||||
}
|
||||
#endif // PLATFORM_X360/PLATFORM_WINDOWS
|
||||
}
|
||||
|
||||
void Plat_chdir( const char *pDir )
|
||||
{
|
||||
#if !defined( PLATFORM_X360 )
|
||||
int nErr = _chdir( pDir );
|
||||
NOTE_UNUSED( nErr );
|
||||
#else // PLATFORM_X360
|
||||
V_tier0_strncpy( s_pWorkingDir, pDir, sizeof( s_pWorkingDir ) );
|
||||
#endif // PLATFORM_X360
|
||||
}
|
||||
|
||||
char const * Plat_GetEnv(char const *pEnvVarName)
|
||||
{
|
||||
return getenv(pEnvVarName);
|
||||
}
|
||||
|
||||
bool Plat_GetExecutablePath(char *pBuff, size_t nBuff)
|
||||
{
|
||||
return ::GetModuleFileNameA(NULL, pBuff, (DWORD)nBuff) > 0;
|
||||
}
|
||||
|
||||
int Plat_chmod(const char *filename, int pmode)
|
||||
{
|
||||
#if defined( PLATFORM_WINDOWS )
|
||||
return _chmod(filename, pmode);
|
||||
#else
|
||||
return chmod(filename, pmode);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plat_FileExists(const char *pFileName)
|
||||
{
|
||||
#if defined( PLATFORM_WINDOWS )
|
||||
// VS2015 CRT _stat on Windows XP always returns -1 (lol)
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/1557168/wstat64-returns-1-on-xp-always
|
||||
DWORD dwResult = GetFileAttributes(pFileName);
|
||||
return (dwResult != INVALID_FILE_ATTRIBUTES);
|
||||
#else
|
||||
struct _stat fileInfo;
|
||||
return (_stat(pFileName, &fileInfo) != -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t Plat_FileSize(const char *pFileName)
|
||||
{
|
||||
#if defined( PLATFORM_WINDOWS )
|
||||
// VS2015 CRT _stat on Windows XP always returns -1 (lol)
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/1557168/wstat64-returns-1-on-xp-always
|
||||
WIN32_FILE_ATTRIBUTE_DATA fileAttributes;
|
||||
BOOL bSuccess = GetFileAttributesEx(pFileName, GetFileExInfoStandard, &fileAttributes);
|
||||
if (!bSuccess)
|
||||
return 0;
|
||||
|
||||
uint64 ulFileSize;
|
||||
ulFileSize = fileAttributes.nFileSizeHigh;
|
||||
ulFileSize <<= 32;
|
||||
ulFileSize |= fileAttributes.nFileSizeLow;
|
||||
return (size_t)ulFileSize;
|
||||
#else
|
||||
struct _stat fileInfo;
|
||||
if (_stat(pFileName, &fileInfo) == -1)
|
||||
return 0;
|
||||
|
||||
return fileInfo.st_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plat_IsDirectory(const char *pFilepath)
|
||||
{
|
||||
#if defined( PLATFORM_WINDOWS )
|
||||
// VS2015 CRT _stat on Windows XP always returns -1 (lol)
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/1557168/wstat64-returns-1-on-xp-always
|
||||
DWORD dwResult = GetFileAttributes(pFilepath);
|
||||
if (dwResult == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
|
||||
return (dwResult & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
#else
|
||||
struct _stat fileInfo;
|
||||
if (_stat(pFilepath, &fileInfo) == -1)
|
||||
return false;
|
||||
|
||||
return ((fileInfo.st_mode & _S_IFDIR) != 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plat_FileIsReadOnly(const char *pFileName)
|
||||
{
|
||||
#if defined( PLATFORM_WINDOWS )
|
||||
// VS2015 CRT _stat on Windows XP always returns -1 (lol)
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/1557168/wstat64-returns-1-on-xp-always
|
||||
DWORD dwResult = GetFileAttributes(pFileName);
|
||||
if (dwResult == INVALID_FILE_ATTRIBUTES)
|
||||
return false; // See below.
|
||||
|
||||
return (dwResult & FILE_ATTRIBUTE_READONLY) != 0;
|
||||
#else
|
||||
struct _stat fileInfo;
|
||||
if (_stat(pFileName, &fileInfo) == -1)
|
||||
return false; //for the purposes of this test, a nonexistent file is more writable than it is readable.
|
||||
|
||||
return ((fileInfo.st_mode & _S_IWRITE) == 0);
|
||||
#endif
|
||||
}
|
||||
224
tier0/platform_com.cpp
Normal file
224
tier0/platform_com.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
//=========== (C) Copyright 2015 Valve, L.L.C. All rights reserved. ===========
|
||||
//
|
||||
// The copyright to the contents herein is the property of Valve, L.L.C.
|
||||
// The contents may be used and/or copied only with the written permission of
|
||||
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
|
||||
// the agreement/contract under which the contents have been supplied.
|
||||
//
|
||||
// Shares dynamic loading of COM/ole32/oleaut32 out of tier0.
|
||||
//=============================================================================
|
||||
|
||||
#include "tier0/platform_com.h"
|
||||
#include "tier0/threadtools.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
typedef int32 ThreadInitOnce_t;
|
||||
typedef class CSysModule* PlatModule_t;
|
||||
#define PLAT_MODULE_INVALID ( (PlatModule_t)0 )
|
||||
|
||||
static PlatCOMFunctions_t g_COMFunctions;
|
||||
static PlatModule_t g_hCOM;
|
||||
static ThreadInitOnce_t g_COMFunctionsInit;
|
||||
|
||||
static const char *g_COMFunctionNames[] =
|
||||
{
|
||||
"CoInitializeEx",
|
||||
"CoUninitialize",
|
||||
"CoCreateInstance",
|
||||
"CoTaskMemAlloc",
|
||||
"CoTaskMemRealloc",
|
||||
"CoTaskMemFree",
|
||||
};
|
||||
|
||||
enum PlatGetProcAddressesFlags_t
|
||||
{
|
||||
k_nPlatGetProcAddresses_Default = 0x00000000,
|
||||
k_nPlatGetProcAddresses_Optional = 0x00000001,
|
||||
};
|
||||
|
||||
// Plat_GetProcAddresses copied from Source2 platform.cpp
|
||||
// Added here to avoid changing platform.h...
|
||||
void *Plat_GetProcAddresses( const char *pModuleName,
|
||||
int nNames,
|
||||
const char **pNames,
|
||||
size_t nTotalPointerBytes,
|
||||
void *pPointers,
|
||||
size_t nTotalFlagsBytes,
|
||||
uint32 *pFlags,
|
||||
bool bFailFatal )
|
||||
{
|
||||
if ( (nTotalPointerBytes & (sizeof( void* ) - 1)) != 0 )
|
||||
{
|
||||
Plat_FatalError( "%s: invalid pointer byte count\n", __FUNCTION__ );
|
||||
}
|
||||
if ( (size_t)nNames != nTotalPointerBytes / sizeof( void* ) )
|
||||
{
|
||||
Plat_FatalError( "%s: different number of names and pointers\n", __FUNCTION__ );
|
||||
}
|
||||
if ( (nTotalFlagsBytes & (sizeof( *pFlags ) - 1)) != 0 )
|
||||
{
|
||||
Plat_FatalError( "%s: invalid flag byte count\n", __FUNCTION__ );
|
||||
}
|
||||
if ( pFlags )
|
||||
{
|
||||
if ( (size_t)nNames != nTotalFlagsBytes / sizeof( *pFlags ) )
|
||||
{
|
||||
Plat_FatalError( "%s: different number of names and flags\n", __FUNCTION__ );
|
||||
}
|
||||
}
|
||||
|
||||
HMODULE hModule = ::LoadLibrary( pModuleName );
|
||||
if ( hModule == 0 )
|
||||
{
|
||||
if ( bFailFatal )
|
||||
{
|
||||
int nErr = GetLastError();
|
||||
Plat_FatalError( "%s: unable to load '%s', error %d\n", __FUNCTION__, pModuleName, nErr );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void** pFunc = (void**)pPointers;
|
||||
for ( int i = 0; i < nNames; i++ )
|
||||
{
|
||||
uint32 nFlags = pFlags ? pFlags[i] : 0;
|
||||
*pFunc = ::GetProcAddress( hModule, pNames[i] );
|
||||
if ( !*pFunc &&
|
||||
(nFlags & k_nPlatGetProcAddresses_Optional) == 0 )
|
||||
{
|
||||
if ( bFailFatal )
|
||||
{
|
||||
Plat_FatalError( "%s: unable to find %s in '%s'\n", __FUNCTION__, pNames[i], pModuleName );
|
||||
}
|
||||
|
||||
::FreeLibrary( hModule );
|
||||
memset( pPointers, 0, nTotalPointerBytes );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pFunc++;
|
||||
}
|
||||
|
||||
return hModule;
|
||||
}
|
||||
|
||||
static bool Plat_LoadCOM_Once( ThreadInitOnce_t *pMarker )
|
||||
{
|
||||
const char *pCOMName = "ole32.dll";
|
||||
g_hCOM = (PlatModule_t)Plat_GetProcAddresses( pCOMName,
|
||||
ARRAYSIZE( g_COMFunctionNames ),
|
||||
g_COMFunctionNames,
|
||||
sizeof( g_COMFunctions ),
|
||||
&g_COMFunctions,
|
||||
0,
|
||||
NULL,
|
||||
false );
|
||||
// We always succeed in this routine as we
|
||||
// handle failure at a higher level.
|
||||
return true;
|
||||
}
|
||||
|
||||
PlatCOMFunctions_t *Plat_LoadCOM()
|
||||
{
|
||||
ThreadInitOnceCall( &g_COMFunctionsInit, Plat_LoadCOM_Once );
|
||||
return &g_COMFunctions;
|
||||
}
|
||||
|
||||
PlatCOMFunctions_t *Plat_CheckCOM()
|
||||
{
|
||||
if ( g_hCOM == PLAT_MODULE_INVALID )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &g_COMFunctions;
|
||||
}
|
||||
|
||||
PlatCOMFunctions_t *Plat_RequireCOM()
|
||||
{
|
||||
if ( g_hCOM == PLAT_MODULE_INVALID )
|
||||
{
|
||||
Plat_FatalError( "Unable to load COM\n" );
|
||||
}
|
||||
|
||||
return &g_COMFunctions;
|
||||
}
|
||||
|
||||
void Plat_UnloadCOM()
|
||||
{
|
||||
// We do not unload COM currently but we have a stub
|
||||
// that callers should use so that we can drop in unloading
|
||||
// if we need it in the future.
|
||||
}
|
||||
|
||||
static PlatOleAutFunctions_t g_OleAutFunctions;
|
||||
static PlatModule_t g_hOleAut;
|
||||
static ThreadInitOnce_t g_OleAutFunctionsInit;
|
||||
|
||||
static const char *g_OleAutFunctionNames[] =
|
||||
{
|
||||
"VariantInit",
|
||||
"VariantClear",
|
||||
"VariantCopy",
|
||||
"SysAllocString",
|
||||
"SysAllocStringByteLen",
|
||||
"SysAllocStringLen",
|
||||
"SysFreeString",
|
||||
"SysReAllocString",
|
||||
"SysReAllocStringLen",
|
||||
"SysStringByteLen",
|
||||
"SysStringLen",
|
||||
};
|
||||
|
||||
static bool Plat_LoadOleAut_Once( ThreadInitOnce_t *pMarker )
|
||||
{
|
||||
const char *pOleAutName = "oleaut32.dll";
|
||||
g_hOleAut = (PlatModule_t)Plat_GetProcAddresses( pOleAutName,
|
||||
ARRAYSIZE( g_OleAutFunctionNames ),
|
||||
g_OleAutFunctionNames,
|
||||
sizeof( g_OleAutFunctions ),
|
||||
&g_OleAutFunctions,
|
||||
0,
|
||||
NULL,
|
||||
false );
|
||||
// We always succeed in this routine as we
|
||||
// handle failure at a higher level.
|
||||
return true;
|
||||
}
|
||||
|
||||
PlatOleAutFunctions_t *Plat_LoadOleAut()
|
||||
{
|
||||
ThreadInitOnceCall( &g_OleAutFunctionsInit, Plat_LoadOleAut_Once );
|
||||
return &g_OleAutFunctions;
|
||||
}
|
||||
|
||||
PlatOleAutFunctions_t *Plat_CheckOleAut()
|
||||
{
|
||||
if ( g_hOleAut == PLAT_MODULE_INVALID )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &g_OleAutFunctions;
|
||||
}
|
||||
|
||||
PlatOleAutFunctions_t *Plat_RequireOleAut()
|
||||
{
|
||||
if ( g_hOleAut == PLAT_MODULE_INVALID )
|
||||
{
|
||||
Plat_FatalError( "Unable to load oleaut32\n" );
|
||||
}
|
||||
|
||||
return &g_OleAutFunctions;
|
||||
}
|
||||
|
||||
void Plat_UnloadOleAut()
|
||||
{
|
||||
// We do not unload oleaut32 currently but we have a stub
|
||||
// that callers should use so that we can drop in unloading
|
||||
// if we need it in the future.
|
||||
}
|
||||
|
||||
#endif // #ifdef PLATFORM_WINDOWS
|
||||
32
tier0/platform_independent.cpp
Normal file
32
tier0/platform_independent.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose: components of tier0 PLAT_ with (at least mostly) platform independent implementations.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include <time.h>
|
||||
|
||||
void GetCurrentDayOfTheWeek( int *pDay )
|
||||
{
|
||||
struct tm *pNewTime;
|
||||
time_t long_time;
|
||||
|
||||
time( &long_time ); /* Get time as long integer. */
|
||||
pNewTime = localtime( &long_time ); /* Convert to local time. */
|
||||
|
||||
*pDay = pNewTime->tm_wday;
|
||||
}
|
||||
|
||||
void GetCurrentDayOfTheYear( int *pDay )
|
||||
{
|
||||
struct tm *pNewTime;
|
||||
time_t long_time;
|
||||
|
||||
time( &long_time ); /* Get time as long integer. */
|
||||
pNewTime = localtime( &long_time ); /* Convert to local time. */
|
||||
|
||||
*pDay = pNewTime->tm_yday;
|
||||
}
|
||||
571
tier0/platform_posix.cpp
Normal file
571
tier0/platform_posix.cpp
Normal file
@@ -0,0 +1,571 @@
|
||||
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/memalloc.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/threadtools.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef OSX
|
||||
#include <mach-o/dyld.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
|
||||
static bool g_bBenchmarkMode = false;
|
||||
static double g_FakeBenchmarkTime = 0;
|
||||
static double g_FakeBenchmarkTimeInc = 1.0 / 66.0;
|
||||
|
||||
|
||||
bool Plat_IsInBenchmarkMode()
|
||||
{
|
||||
return g_bBenchmarkMode;
|
||||
}
|
||||
|
||||
void Plat_SetBenchmarkMode( bool bBenchmark )
|
||||
{
|
||||
g_bBenchmarkMode = bBenchmark;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef OSX
|
||||
|
||||
static uint64 start_time = 0;
|
||||
static mach_timebase_info_data_t sTimebaseInfo;
|
||||
static double conversion = 0.0;
|
||||
static double conversionMS = 0.0;
|
||||
static double conversionUS = 0.0;
|
||||
|
||||
void InitTime()
|
||||
{
|
||||
start_time = mach_absolute_time();
|
||||
mach_timebase_info(&sTimebaseInfo);
|
||||
conversion = 1e-9 * (double) sTimebaseInfo.numer / (double) sTimebaseInfo.denom;
|
||||
conversionMS = 1e-6 * (double) sTimebaseInfo.numer / (double) sTimebaseInfo.denom;
|
||||
conversionUS = 1e-3 * (double) sTimebaseInfo.numer / (double) sTimebaseInfo.denom;
|
||||
}
|
||||
|
||||
uint64 Plat_GetClockStart()
|
||||
{
|
||||
if ( !start_time )
|
||||
{
|
||||
InitTime();
|
||||
}
|
||||
|
||||
return start_time;
|
||||
}
|
||||
|
||||
double Plat_FloatTime()
|
||||
{
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return g_FakeBenchmarkTime;
|
||||
}
|
||||
|
||||
if ( !start_time )
|
||||
{
|
||||
InitTime();
|
||||
}
|
||||
|
||||
uint64 now = mach_absolute_time();
|
||||
|
||||
return ( now - start_time ) * conversion;
|
||||
}
|
||||
uint32 Plat_MSTime()
|
||||
{
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return g_FakeBenchmarkTime;
|
||||
}
|
||||
|
||||
if ( !start_time )
|
||||
{
|
||||
InitTime();
|
||||
}
|
||||
|
||||
uint64 now = mach_absolute_time();
|
||||
|
||||
return uint32( ( now - start_time ) * conversionMS );
|
||||
}
|
||||
|
||||
uint64 Plat_USTime()
|
||||
{
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return g_FakeBenchmarkTime;
|
||||
}
|
||||
|
||||
if ( !start_time )
|
||||
{
|
||||
InitTime();
|
||||
}
|
||||
|
||||
uint64 now = mach_absolute_time();
|
||||
|
||||
return uint64( ( now - start_time ) * conversionUS );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int secbase = 0;
|
||||
|
||||
void InitTime( struct timeval &tp )
|
||||
{
|
||||
secbase = tp.tv_sec;
|
||||
}
|
||||
|
||||
uint64 Plat_GetClockStart()
|
||||
{
|
||||
if ( !secbase )
|
||||
{
|
||||
struct timeval tp;
|
||||
|
||||
gettimeofday( &tp, NULL );
|
||||
InitTime( tp );
|
||||
}
|
||||
|
||||
return secbase;
|
||||
}
|
||||
|
||||
|
||||
double Plat_FloatTime()
|
||||
{
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return g_FakeBenchmarkTime;
|
||||
}
|
||||
|
||||
struct timeval tp;
|
||||
|
||||
gettimeofday( &tp, NULL );
|
||||
|
||||
if ( !secbase )
|
||||
{
|
||||
InitTime( tp );
|
||||
}
|
||||
|
||||
return (( tp.tv_sec - secbase ) + tp.tv_usec / 1000000.0 );
|
||||
}
|
||||
|
||||
uint32 Plat_MSTime()
|
||||
{
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return uint32( g_FakeBenchmarkTime * 1000.0 );
|
||||
}
|
||||
|
||||
struct timeval tp;
|
||||
|
||||
gettimeofday( &tp, NULL );
|
||||
|
||||
if ( !secbase )
|
||||
{
|
||||
InitTime( tp );
|
||||
}
|
||||
|
||||
return (( tp.tv_sec - secbase )*1000 + tp.tv_usec / 1000 );
|
||||
}
|
||||
|
||||
uint64 Plat_USTime()
|
||||
{
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return uint32( g_FakeBenchmarkTime * 1e6 );
|
||||
}
|
||||
|
||||
struct timeval tp;
|
||||
|
||||
gettimeofday( &tp, NULL );
|
||||
|
||||
if ( !secbase )
|
||||
{
|
||||
InitTime( tp );
|
||||
}
|
||||
|
||||
return ( uint64( tp.tv_sec - secbase )*1000000ull + tp.tv_usec );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Wraps the thread-safe versions of asctime. buf must be at least 26 bytes
|
||||
char *Plat_asctime( const struct tm *tm, char *buf, size_t bufsize )
|
||||
{
|
||||
return asctime_r( tm, buf );
|
||||
}
|
||||
|
||||
// Wraps the thread-safe versions of ctime. buf must be at least 26 bytes
|
||||
char *Plat_ctime( const time_t *timep, char *buf, size_t bufsize )
|
||||
{
|
||||
return ctime_r( timep, buf );
|
||||
}
|
||||
|
||||
// Wraps the thread-safe versions of gmtime
|
||||
struct tm *Platform_gmtime( const time_t *timep, struct tm *result )
|
||||
{
|
||||
return gmtime_r( timep, result );
|
||||
}
|
||||
|
||||
time_t Plat_timegm( struct tm *timeptr )
|
||||
{
|
||||
return timegm( timeptr );
|
||||
}
|
||||
|
||||
// Wraps the thread-safe versions of localtime
|
||||
struct tm *Plat_localtime( const time_t *timep, struct tm *result )
|
||||
{
|
||||
return localtime_r( timep, result );
|
||||
}
|
||||
|
||||
uint64 Plat_GetTime()
|
||||
{
|
||||
// We just provide a wrapper on this function so we can protect access to time() everywhere.
|
||||
time_t ltime;
|
||||
time( <ime );
|
||||
return ltime;
|
||||
}
|
||||
|
||||
bool vtune( bool resume )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- //
|
||||
// Memory stuff.
|
||||
// -------------------------------------------------------------------------------------------------- //
|
||||
|
||||
PLATFORM_INTERFACE void Plat_DefaultAllocErrorFn( unsigned long size )
|
||||
{
|
||||
}
|
||||
|
||||
typedef void (*Plat_AllocErrorFn)( unsigned long size );
|
||||
Plat_AllocErrorFn g_AllocError = Plat_DefaultAllocErrorFn;
|
||||
|
||||
PLATFORM_INTERFACE void* Plat_Alloc( unsigned long size )
|
||||
{
|
||||
void *pRet = g_pMemAlloc->Alloc( size );
|
||||
if ( pRet )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_AllocError( size );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void* Plat_Realloc( void *ptr, unsigned long size )
|
||||
{
|
||||
void *pRet = g_pMemAlloc->Realloc( ptr, size );
|
||||
if ( pRet )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_AllocError( size );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void Plat_Free( void *ptr )
|
||||
{
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
g_pMemAlloc->Free( ptr );
|
||||
#else
|
||||
free( ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void Plat_SetAllocErrorFn( Plat_AllocErrorFn fn )
|
||||
{
|
||||
g_AllocError = fn;
|
||||
}
|
||||
|
||||
static char g_CmdLine[ 2048 ];
|
||||
PLATFORM_INTERFACE void Plat_SetCommandLine( const char *cmdLine )
|
||||
{
|
||||
strncpy( g_CmdLine, cmdLine, sizeof(g_CmdLine) );
|
||||
g_CmdLine[ sizeof(g_CmdLine) -1 ] = 0;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void Plat_SetCommandLineArgs( char **argv, int argc )
|
||||
{
|
||||
g_CmdLine[0] = 0;
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
strncat( g_CmdLine, argv[i], sizeof(g_CmdLine) - strlen(g_CmdLine) );
|
||||
}
|
||||
|
||||
g_CmdLine[ sizeof(g_CmdLine) -1 ] = 0;
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE const tchar *Plat_GetCommandLine()
|
||||
{
|
||||
return g_CmdLine;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE bool Is64BitOS()
|
||||
{
|
||||
#if defined OSX
|
||||
return true;
|
||||
#elif defined LINUX
|
||||
FILE *pp = popen( "uname -m", "r" );
|
||||
if ( pp != NULL )
|
||||
{
|
||||
char rgchArchString[256];
|
||||
fgets( rgchArchString, sizeof( rgchArchString ), pp );
|
||||
pclose( pp );
|
||||
if ( !strncasecmp( rgchArchString, "x86_64", strlen( "x86_64" ) ) )
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
Assert( !"implement Is64BitOS" );
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Plat_IsInDebugSession()
|
||||
{
|
||||
#if defined(OSX)
|
||||
int mib[4];
|
||||
struct kinfo_proc info;
|
||||
size_t size;
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
size = sizeof(info);
|
||||
info.kp_proc.p_flag = 0;
|
||||
sysctl(mib,4,&info,&size,NULL,0);
|
||||
bool result = ((info.kp_proc.p_flag & P_TRACED) == P_TRACED);
|
||||
return result;
|
||||
#elif defined(LINUX)
|
||||
char s[256];
|
||||
snprintf(s, 256, "/proc/%d/cmdline", getppid());
|
||||
FILE * fp = fopen(s, "r");
|
||||
if (fp != NULL)
|
||||
{
|
||||
fread(s, 256, 1, fp);
|
||||
fclose(fp);
|
||||
return (0 == strncmp(s, "gdb", 3));
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Plat_ExitProcess( int nCode )
|
||||
{
|
||||
fflush( stdout );
|
||||
if ( nCode != 0 )
|
||||
{
|
||||
// Right now we want a non-zero exit code to cause a hard crash so
|
||||
// that we trigger minidump.
|
||||
int* x = NULL;
|
||||
*x = 1;
|
||||
}
|
||||
_exit( nCode );
|
||||
}
|
||||
|
||||
static int s_nWatchDogTimerTimeScale = 0;
|
||||
static bool s_bInittedWD = false;
|
||||
|
||||
static void WatchdogCoreDumpSigHandler( int nSignal )
|
||||
{
|
||||
signal( SIGALRM, SIG_IGN );
|
||||
printf( "**** WARNING: Watchdog timer exceeded, aborting!\n" );
|
||||
abort();
|
||||
}
|
||||
|
||||
static void InitWatchDogTimer( void )
|
||||
{
|
||||
if( !strstr( g_CmdLine, "-nowatchdog" ) )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
s_nWatchDogTimerTimeScale = 10; // debug is slow
|
||||
#else
|
||||
s_nWatchDogTimerTimeScale = 1;
|
||||
#endif
|
||||
|
||||
if( !strstr( g_CmdLine, "-nonabortingwatchdog" ) )
|
||||
{
|
||||
signal( SIGALRM, WatchdogCoreDumpSigHandler );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// watchdog timer support
|
||||
void BeginWatchdogTimer( int nSecs )
|
||||
{
|
||||
if (! s_bInittedWD )
|
||||
{
|
||||
s_bInittedWD = true;
|
||||
InitWatchDogTimer();
|
||||
}
|
||||
nSecs *= s_nWatchDogTimerTimeScale;
|
||||
nSecs = MIN( nSecs, 5 * 60 ); // no more than 5 minutes no matter what
|
||||
if ( nSecs )
|
||||
alarm( nSecs );
|
||||
}
|
||||
|
||||
void EndWatchdogTimer( void )
|
||||
{
|
||||
alarm( 0 );
|
||||
}
|
||||
|
||||
static CThreadMutex g_LocalTimeMutex;
|
||||
|
||||
|
||||
void Plat_GetLocalTime( struct tm *pNow )
|
||||
{
|
||||
// We just provide a wrapper on this function so we can protect access to time() everywhere.
|
||||
time_t ltime;
|
||||
time( <ime );
|
||||
|
||||
Plat_ConvertToLocalTime( ltime, pNow );
|
||||
}
|
||||
|
||||
void Plat_ConvertToLocalTime( uint64 nTime, struct tm *pNow )
|
||||
{
|
||||
// Since localtime() returns a global, we need to protect against multiple threads stomping it.
|
||||
g_LocalTimeMutex.Lock();
|
||||
|
||||
time_t ltime = (time_t)nTime;
|
||||
tm *pTime = localtime( <ime );
|
||||
if ( pTime )
|
||||
*pNow = *pTime;
|
||||
else
|
||||
memset( pNow, 0, sizeof( *pNow ) );
|
||||
|
||||
g_LocalTimeMutex.Unlock();
|
||||
}
|
||||
|
||||
void Plat_GetTimeString( struct tm *pTime, char *pOut, int nMaxBytes )
|
||||
{
|
||||
g_LocalTimeMutex.Lock();
|
||||
|
||||
char *pStr = asctime( pTime );
|
||||
strncpy( pOut, pStr, nMaxBytes );
|
||||
pOut[nMaxBytes-1] = 0;
|
||||
|
||||
g_LocalTimeMutex.Unlock();
|
||||
}
|
||||
|
||||
// timezone
|
||||
int32 Plat_timezone( void )
|
||||
{
|
||||
return timezone;
|
||||
}
|
||||
|
||||
// daylight savings
|
||||
int32 Plat_daylight( void )
|
||||
{
|
||||
return daylight;
|
||||
}
|
||||
|
||||
void Platform_gmtime( uint64 nTime, struct tm *pTime )
|
||||
{
|
||||
time_t tmtTime = nTime;
|
||||
struct tm * tmp = gmtime( &tmtTime );
|
||||
* pTime = * tmp;
|
||||
}
|
||||
|
||||
#ifdef LINUX
|
||||
/*
|
||||
From http://man7.org/linux/man-pages/man5/proc.5.html:
|
||||
/proc/[pid]/statm
|
||||
Provides information about memory usage, measured in pages.
|
||||
The columns are:
|
||||
|
||||
size (1) total program size
|
||||
(same as VmSize in /proc/[pid]/status)
|
||||
resident (2) resident set size
|
||||
(same as VmRSS in /proc/[pid]/status)
|
||||
share (3) shared pages (i.e., backed by a file)
|
||||
text (4) text (code)
|
||||
lib (5) library (unused in Linux 2.6)
|
||||
data (6) data + stack
|
||||
dt (7) dirty pages (unused in Linux 2.6)
|
||||
*/
|
||||
|
||||
// This returns the resident memory size (RES column in 'top') in bytes.
|
||||
size_t ApproximateProcessMemoryUsage( void )
|
||||
{
|
||||
size_t nRet = 0;
|
||||
FILE *pFile = fopen( "/proc/self/statm", "r" );
|
||||
if ( pFile )
|
||||
{
|
||||
size_t nSize, nResident, nShare, nText, nLib_Unused, nDataPlusStack, nDt_Unused;
|
||||
if ( fscanf( pFile, "%zu %zu %zu %zu %zu %zu %zu", &nSize, &nResident, &nShare, &nText, &nLib_Unused, &nDataPlusStack, &nDt_Unused ) >= 2 )
|
||||
{
|
||||
nRet = 4096 * nResident;
|
||||
}
|
||||
fclose( pFile );
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
#else
|
||||
|
||||
size_t ApproximateProcessMemoryUsage( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
char const * Plat_GetEnv(char const *pEnvVarName)
|
||||
{
|
||||
return getenv(pEnvVarName);
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE bool Plat_GetExecutablePath( char *pBuff, size_t nBuff )
|
||||
{
|
||||
#if defined OSX
|
||||
uint32_t _nBuff = nBuff;
|
||||
bool bSuccess = _NSGetExecutablePath(pBuff, &_nBuff) == 0;
|
||||
pBuff[nBuff-1] = '\0';
|
||||
return bSuccess;
|
||||
#elif defined LINUX
|
||||
ssize_t nRead = readlink("/proc/self/exe", pBuff, nBuff-1 );
|
||||
if ( nRead != -1 )
|
||||
{
|
||||
pBuff[ nRead ] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
pBuff[0] = 0;
|
||||
return false;
|
||||
#else
|
||||
AssertMsg( false, "Implement Plat_GetExecutablePath" );
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
230
tier0/platwindow.cpp
Normal file
230
tier0/platwindow.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
//========== Copyright © 2007, Valve Corporation, All rights reserved. ========
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/platwindow.h"
|
||||
|
||||
#if defined( PLATFORM_WINDOWS )
|
||||
#if !defined( PLATFORM_X360 )
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include "xbox/xbox_win32stubs.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Window creation
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatWindow_t Plat_CreateWindow( void *hInstance, const char *pTitle, int nWidth, int nHeight, int nFlags )
|
||||
{
|
||||
WNDCLASSEX wc;
|
||||
memset( &wc, 0, sizeof( wc ) );
|
||||
wc.cbSize = sizeof( wc );
|
||||
wc.style = CS_OWNDC | CS_DBLCLKS;
|
||||
wc.lpfnWndProc = DefWindowProc;
|
||||
wc.hInstance = (HINSTANCE)hInstance;
|
||||
wc.lpszClassName = "Valve001";
|
||||
wc.hIcon = NULL; //LoadIcon( s_HInstance, MAKEINTRESOURCE( IDI_LAUNCHER ) );
|
||||
wc.hIconSm = wc.hIcon;
|
||||
|
||||
RegisterClassEx( &wc );
|
||||
|
||||
// Note, it's hidden
|
||||
DWORD style = WS_POPUP | WS_CLIPSIBLINGS;
|
||||
style &= ~WS_MAXIMIZEBOX;
|
||||
|
||||
if ( ( nFlags & WINDOW_CREATE_FULLSCREEN ) == 0 )
|
||||
{
|
||||
// Give it a frame
|
||||
style |= WS_OVERLAPPEDWINDOW;
|
||||
if ( nFlags & WINDOW_CREATE_RESIZING )
|
||||
{
|
||||
style |= WS_THICKFRAME | WS_MAXIMIZEBOX;
|
||||
}
|
||||
else
|
||||
{
|
||||
style &= ~WS_THICKFRAME;
|
||||
}
|
||||
}
|
||||
|
||||
RECT windowRect;
|
||||
windowRect.top = 0;
|
||||
windowRect.left = 0;
|
||||
windowRect.right = nWidth;
|
||||
windowRect.bottom = nHeight;
|
||||
|
||||
// Compute rect needed for that size client area based on window style
|
||||
AdjustWindowRectEx( &windowRect, style, FALSE, 0 );
|
||||
|
||||
// Create the window
|
||||
void *hWnd = CreateWindow( wc.lpszClassName, pTitle, style, 0, 0,
|
||||
windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
|
||||
NULL, NULL, (HINSTANCE)hInstance, NULL );
|
||||
|
||||
return (PlatWindow_t)hWnd;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Window title
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_SetWindowTitle( PlatWindow_t hWindow, const char *pTitle )
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
SetWindowText( (HWND)hWindow, pTitle );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Window movement
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_SetWindowPos( PlatWindow_t hWindow, int x, int y )
|
||||
{
|
||||
SetWindowPos( (HWND)hWindow, NULL, x, y, 0, 0,
|
||||
SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets the desktop resolution
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_GetDesktopResolution( int *pWidth, int *pHeight )
|
||||
{
|
||||
*pWidth = GetSystemMetrics( SM_CXSCREEN );
|
||||
*pHeight = GetSystemMetrics( SM_CYSCREEN );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets a window size
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_GetWindowClientSize( PlatWindow_t hWindow, int *pWidth, int *pHeight )
|
||||
{
|
||||
RECT rect;
|
||||
GetClientRect( (HWND)hWindow, &rect );
|
||||
*pWidth = rect.right - rect.left;
|
||||
*pHeight = rect.bottom - rect.top;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Is the window minimized?
|
||||
//-----------------------------------------------------------------------------
|
||||
bool Plat_IsWindowMinimized( PlatWindow_t hWindow )
|
||||
{
|
||||
return IsIconic( (HWND)hWindow ) != 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets the shell window in a console app
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatWindow_t Plat_GetShellWindow( )
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
return (PlatWindow_t)GetShellWindow();
|
||||
#else
|
||||
return PLAT_WINDOW_INVALID;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Convert window -> Screen coordinates
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_WindowToScreenCoords( PlatWindow_t hWnd, int &x, int &y )
|
||||
{
|
||||
POINT pt;
|
||||
pt.x = x; pt.y = y;
|
||||
ClientToScreen( (HWND)hWnd, &pt );
|
||||
x = pt.x; y = pt.y;
|
||||
}
|
||||
|
||||
void Plat_ScreenToWindowCoords( PlatWindow_t hWnd, int &x, int &y )
|
||||
{
|
||||
POINT pt;
|
||||
pt.x = x; pt.y = y;
|
||||
ScreenToClient( (HWND)hWnd, &pt );
|
||||
x = pt.x; y = pt.y;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Window creation
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatWindow_t Plat_CreateWindow( void *hInstance, const char *pTitle, int nWidth, int nHeight, int nFlags )
|
||||
{
|
||||
return PLAT_WINDOW_INVALID;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Window title
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_SetWindowTitle( PlatWindow_t hWindow, const char *pTitle )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Window movement
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_SetWindowPos( PlatWindow_t hWindow, int x, int y )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets the desktop resolution
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_GetDesktopResolution( int *pWidth, int *pHeight )
|
||||
{
|
||||
*pWidth = 0;
|
||||
*pHeight = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets a window size
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_GetWindowClientSize( PlatWindow_t hWindow, int *pWidth, int *pHeight )
|
||||
{
|
||||
*pWidth = 0;
|
||||
*pHeight = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Is the window minimized?
|
||||
//-----------------------------------------------------------------------------
|
||||
bool Plat_IsWindowMinimized( PlatWindow_t hWindow )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets the shell window in a console app
|
||||
//-----------------------------------------------------------------------------
|
||||
PlatWindow_t Plat_GetShellWindow( )
|
||||
{
|
||||
return PLAT_WINDOW_INVALID;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Convert window -> Screen coordinates
|
||||
//-----------------------------------------------------------------------------
|
||||
void Plat_WindowToScreenCoords( PlatWindow_t hWnd, int &x, int &y )
|
||||
{
|
||||
}
|
||||
|
||||
void Plat_ScreenToWindowCoords( PlatWindow_t hWnd, int &x, int &y )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
88
tier0/pmc360.cpp
Normal file
88
tier0/pmc360.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Inner workings of Performance Monitor Counters on the xbox 360;
|
||||
// they let vprof track L2 dcache misses, LHS, etc.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#ifndef _X360
|
||||
#error pmc360.cpp must only be compiled for XBOX360!
|
||||
#else
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/vprof.h"
|
||||
#include <pmcpbsetup.h>
|
||||
#include "tier0/dbg.h"
|
||||
#include "pmc360.h"
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
static bool s_bInitialized = false;
|
||||
|
||||
CPMCData::CPMCData()
|
||||
{
|
||||
}
|
||||
|
||||
void CPMCData::InitializeOnceProgramWide( void )
|
||||
{
|
||||
#if !defined( _CERT )
|
||||
// Select a set of sixteen counters
|
||||
DmPMCInstallAndStart( PMC_SETUP_FLUSHREASONS_PB0T0 );
|
||||
// Reset the Performance Monitor Counters in preparation for a new sampling run.
|
||||
DmPMCResetCounters();
|
||||
#endif
|
||||
s_bInitialized = true;
|
||||
}
|
||||
|
||||
bool CPMCData::IsInitialized()
|
||||
{
|
||||
return s_bInitialized;
|
||||
}
|
||||
|
||||
void CPMCData::Start()
|
||||
{
|
||||
#if !defined( _CERT )
|
||||
// stop the stopwatches, save off the counter, start them again.
|
||||
DmPMCStop();
|
||||
|
||||
PMCState pmcstate;
|
||||
// Get the counters.
|
||||
DmPMCGetCounters( &pmcstate );
|
||||
|
||||
// in the default state as set up by InitializeOnceProgramWide,
|
||||
// counters 9 and 6 are L2 misses and LHS respectively
|
||||
m_OnStart.L2CacheMiss = pmcstate.pmc[9];
|
||||
m_OnStart.LHS = pmcstate.pmc[6];
|
||||
|
||||
DmPMCStart();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CPMCData::End()
|
||||
{
|
||||
#if !defined( _CERT )
|
||||
DmPMCStop();
|
||||
|
||||
// get end-state counters
|
||||
PMCState pmcstate;
|
||||
// Get the counters.
|
||||
DmPMCGetCounters( &pmcstate );
|
||||
|
||||
// in the default state as set up by InitializeOnceProgramWide,
|
||||
// counters 9 and 6 are l2 misses and LHS respectively
|
||||
const uint64 &endL2 = pmcstate.pmc[9];
|
||||
const uint64 &endLHS = pmcstate.pmc[6];
|
||||
|
||||
// compute delta between end and start. Because these are
|
||||
// unsigned nums, even in overflow this still works out
|
||||
// correctly under modular arithmetic.
|
||||
m_Delta.L2CacheMiss = endL2 - m_OnStart.L2CacheMiss;
|
||||
m_Delta.LHS = endLHS - m_OnStart.LHS;
|
||||
|
||||
DmPMCStart();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
162
tier0/pme.cpp
Normal file
162
tier0/pme.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#pragma warning( disable : 4530 ) // warning: exception handler -GX option
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/vprof.h"
|
||||
#include "tier0/pmelib.h"
|
||||
#include "tier0/l2cache.h"
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Initialization
|
||||
//-----------------------------------------------------------------------------
|
||||
void InitPME( void )
|
||||
{
|
||||
bool bInit = false;
|
||||
|
||||
PME *pPME = PME::Instance();
|
||||
if ( pPME )
|
||||
{
|
||||
if ( pPME->GetVendor() != INTEL )
|
||||
return;
|
||||
|
||||
if ( pPME->GetProcessorFamily() != PENTIUM4_FAMILY )
|
||||
return;
|
||||
|
||||
pPME->SetProcessPriority( ProcessPriorityHigh );
|
||||
|
||||
bInit = true;
|
||||
|
||||
DevMsg( 1, _T("PME Initialized.\n") );
|
||||
}
|
||||
else
|
||||
{
|
||||
DevMsg( 1, _T("PME Uninitialized.\n") );
|
||||
}
|
||||
|
||||
#ifdef VPROF_ENABLED
|
||||
g_VProfCurrentProfile.PMEInitialized( bInit );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Shutdown
|
||||
//-----------------------------------------------------------------------------
|
||||
void ShutdownPME( void )
|
||||
{
|
||||
PME *pPME = PME::Instance();
|
||||
if ( pPME )
|
||||
{
|
||||
pPME->SetProcessPriority( ProcessPriorityNormal );
|
||||
}
|
||||
|
||||
#ifdef VPROF_ENABLED
|
||||
g_VProfCurrentProfile.PMEInitialized( false );
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// CL2Cache Code.
|
||||
//
|
||||
|
||||
static int s_nCreateCount = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CL2Cache::CL2Cache()
|
||||
{
|
||||
m_nID = s_nCreateCount++;
|
||||
m_pL2CacheEvent = new P4Event_BSQ_cache_reference;
|
||||
m_iL2CacheMissCount = 0;
|
||||
m_i64Start = 0;
|
||||
m_i64End = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CL2Cache::~CL2Cache()
|
||||
{
|
||||
if ( m_pL2CacheEvent )
|
||||
{
|
||||
delete m_pL2CacheEvent;
|
||||
m_pL2CacheEvent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::Start( void )
|
||||
{
|
||||
if ( m_pL2CacheEvent )
|
||||
{
|
||||
// Set this up to check for L2 cache misses.
|
||||
m_pL2CacheEvent->eventMask->RD_2ndL_MISS = 1;
|
||||
|
||||
// Set the event mask and set the capture mode.
|
||||
// m_pL2CacheEvent->SetCaptureMode( USR_Only );
|
||||
m_pL2CacheEvent->SetCaptureMode( OS_and_USR );
|
||||
|
||||
// That's it, now sw capture events
|
||||
m_pL2CacheEvent->StopCounter();
|
||||
m_pL2CacheEvent->ClearCounter();
|
||||
|
||||
m_pL2CacheEvent->StartCounter();
|
||||
m_i64Start = m_pL2CacheEvent->ReadCounter();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::End( void )
|
||||
{
|
||||
if ( m_pL2CacheEvent )
|
||||
{
|
||||
// Stop the counter and find the delta.
|
||||
m_i64End = m_pL2CacheEvent->ReadCounter();
|
||||
int64 i64Delta = m_i64End - m_i64Start;
|
||||
m_pL2CacheEvent->StopCounter();
|
||||
|
||||
// Save the delta for later query.
|
||||
m_iL2CacheMissCount = ( int )i64Delta;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning( default : 4530 )
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Ensure that all of our internal structures are consistent, and
|
||||
// account for all memory that we've allocated.
|
||||
// Input: validator - Our global validator object
|
||||
// pchName - Our name (typically a member var in our container)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::Validate( CValidator &validator, tchar *pchName )
|
||||
{
|
||||
validator.Push( _T("CL2Cache"), this, pchName );
|
||||
|
||||
validator.ClaimMemory( m_pL2CacheEvent );
|
||||
|
||||
validator.Pop( );
|
||||
}
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
51
tier0/pme_posix.cpp
Normal file
51
tier0/pme_posix.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/vprof.h"
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Initialization
|
||||
//-----------------------------------------------------------------------------
|
||||
void InitPME( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Shutdown
|
||||
//-----------------------------------------------------------------------------
|
||||
void ShutdownPME( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CL2Cache::CL2Cache()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CL2Cache::~CL2Cache()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::Start( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::End( void )
|
||||
{
|
||||
}
|
||||
41
tier0/progressbar.cpp
Normal file
41
tier0/progressbar.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
//========= Copyright © 1996-2006, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(_X360)
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define _WIN32_WINNT 0x0403
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/progressbar.h"
|
||||
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
#include "tier0/memalloc.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
#endif
|
||||
|
||||
|
||||
static ProgressReportHandler_t pReportHandlerFN;
|
||||
|
||||
PLATFORM_INTERFACE void ReportProgress(char const *job_name, int total_units_to_do, int n_units_completed)
|
||||
{
|
||||
if ( pReportHandlerFN )
|
||||
(*pReportHandlerFN)( job_name, total_units_to_do, n_units_completed );
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE ProgressReportHandler_t InstallProgressReportHandler( ProgressReportHandler_t pfn)
|
||||
{
|
||||
ProgressReportHandler_t old = pReportHandlerFN;
|
||||
pReportHandlerFN = pfn;
|
||||
return old;
|
||||
}
|
||||
|
||||
412
tier0/prx.cpp
Normal file
412
tier0/prx.cpp
Normal file
@@ -0,0 +1,412 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <cellstatus.h>
|
||||
#include <sys/prx.h>
|
||||
#include <sys/exportcplusplus.h>
|
||||
#include <sys/ppu_thread.h>
|
||||
#include "tier0/platform.h"
|
||||
#include "logging.h"
|
||||
#include "dbg.h"
|
||||
|
||||
#include "memalloc.h"
|
||||
#include "platform.h"
|
||||
#include "threadtools.h"
|
||||
#include "ps3/ps3_win32stubs.h"
|
||||
#include "unitlib/unitlib.h"
|
||||
#include "tier0/fasttimer.h"
|
||||
#include "tier0/mem.h"
|
||||
#include "tier0/tslist.h"
|
||||
#include "tier0/vatoms.h"
|
||||
#include "tier0/vprof.h"
|
||||
#include "tier0/miniprofiler.h"
|
||||
|
||||
#include "tls_ps3.h"
|
||||
#include "ps3/ps3_helpers.h"
|
||||
#include "ps3/ps3_console.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define tier0_ps3 tier0_dbg
|
||||
#else
|
||||
#define tier0_ps3 tier0_rel
|
||||
#endif
|
||||
|
||||
|
||||
PS3_PRX_SYS_MODULE_INFO_FULLMACROREPLACEMENTHELPER( tier0 );
|
||||
SYS_MODULE_START( _tier0_ps3_prx_entry );
|
||||
SYS_MODULE_EXIT( _tier0_ps3_prx_exit );
|
||||
|
||||
SYS_LIB_DECLARE( tier0_ps3, SYS_LIB_AUTO_EXPORT | SYS_LIB_WEAK_IMPORT );
|
||||
|
||||
#ifndef _CERT
|
||||
SYS_LIB_EXPORT( GetTLSGlobals, tier0_ps3 );
|
||||
#endif
|
||||
SYS_LIB_EXPORT( PS3_PrxGetModulesList, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( LoggingSystem_RegisterLoggingChannel, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_RegisterLoggingListener, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_ResetCurrentLoggingState, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_LogAssert, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_LogDirect, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_IsChannelEnabled, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_SetChannelSpewLevel, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_SetChannelSpewLevelByName, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_SetChannelSpewLevelByTag, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_SetGlobalSpewLevel, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_SetChannelFlags, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_SetLoggingResponsePolicy, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_AddTagToCurrentChannel, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_GetChannelFlags, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_SetChannelColor, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_GetChannelColor, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_GetNextChannelID, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_GetChannelCount, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_GetChannel, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_GetFirstChannelID, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_PushLoggingState, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_PopLoggingState, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_FindChannel, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( LoggingSystem_Log, tier0_ps3 );
|
||||
SYS_LIB_EXPORTPICKUP_CPP_FUNC( "LoggingSystem_Log(int, LoggingSeverity_t, Color, char const*, ...)", tier0_ps3 );
|
||||
|
||||
|
||||
SYS_LIB_EXPORT( ShouldUseNewAssertDialog, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( DoNewAssertDialog, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( GetCPUInformation, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( _ExitOnFatalAssert, tier0_ps3 );
|
||||
|
||||
#if !defined( DBGFLAG_STRINGS_STRIP )
|
||||
SYS_LIB_EXPORT( COM_TimestampedLog, tier0_ps3 );
|
||||
#endif
|
||||
SYS_LIB_EXPORT( Plat_SetBenchmarkMode, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_IsInDebugSession, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_IsInBenchmarkMode, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_FloatTime, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_MSTime, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_GetClockStart, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_GetLocalTime, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_ConvertToLocalTime, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Platform_gmtime, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_timegm, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_timezone, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_daylight, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_GetTimeString, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_DebugString, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_SetWindowTitle, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_GetModuleFilename, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_ExitProcess, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_GetCommandLine, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( CommandLine, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( Plat_GetPagedPoolInfo, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Plat_GetMemPageSize, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( _AssertValidStringPtr, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( _AssertValidReadPtr, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( _AssertValidWritePtr, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( MemAllocScratch, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( MemFreeScratch, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ZeroMemory, tier0_ps3 );
|
||||
|
||||
#if !defined( DBGFLAG_STRINGS_STRIP )
|
||||
SYS_LIB_EXPORT( Msg, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( Warning, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( DevMsg, tier0_ps3 );
|
||||
SYS_LIB_EXPORTPICKUP_CPP_FUNC( "DevMsg(int, char const*, ...)", tier0_ps3 );
|
||||
SYS_LIB_EXPORT( DevWarning, tier0_ps3 );
|
||||
SYS_LIB_EXPORTPICKUP_CPP_FUNC( "DevWarning(int, char const*, ...)", tier0_ps3 );
|
||||
#endif
|
||||
SYS_LIB_EXPORT( Error, tier0_ps3 );
|
||||
#if !defined( DBGFLAG_STRINGS_STRIP )
|
||||
SYS_LIB_EXPORT( ConMsg, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ConDMsg, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ConColorMsg, tier0_ps3 );
|
||||
#endif
|
||||
|
||||
SYS_LIB_EXPORT( ThreadSetAffinity, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ThreadGetCurrentId, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ThreadGetPriority, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ThreadSetPriority, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ThreadSetDebugName, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ThreadSleep, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ThreadInMainThread, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ThreadGetCurrentHandle, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ReleaseThreadHandle, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( CreateSimpleThread, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( SetThreadedLoadLibraryFunc, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( GetThreadedLoadLibraryFunc, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ThreadJoin, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( vtune, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( PublishAllMiniProfilers, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( RunTSQueueTests, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( RunTSListTests, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( GetVAtom, tier0_ps3 );
|
||||
|
||||
// BEGIN PICKUP
|
||||
// You must run build_prxexport_snc.bat AFTER building tier0 to regenerate prxexport.c, and THEN rebuild tier0 again for the changes to take effect.
|
||||
// This is required to get C++ class exports working
|
||||
SYS_LIB_EXPORTPICKUP_CLASS( "CThreadSpinRWLock@", tier0_ps3 );
|
||||
SYS_LIB_EXPORTPICKUP_CLASS( "CThread@", tier0_ps3 );
|
||||
SYS_LIB_EXPORTPICKUP_CLASS( "CWorkerThread@", tier0_ps3 );
|
||||
SYS_LIB_EXPORTPICKUP_CLASS( "CVProfile@", tier0_ps3 );
|
||||
SYS_LIB_EXPORTPICKUP_CLASS( "CVProfNode@", tier0_ps3 );
|
||||
// END PICKUP
|
||||
|
||||
SYS_LIB_EXPORT_VAR( g_pMemAllocInternalPS3, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( GetCurThreadPS3, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( SetCurThreadPS3, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( AllocateThreadID, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( FreeThreadID, tier0_ps3 );
|
||||
|
||||
#ifdef VPROF_ENABLED
|
||||
SYS_LIB_EXPORT_VAR( g_VProfCurrentProfile, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_VProfSignalSpike, tier0_ps3 );
|
||||
#endif
|
||||
SYS_LIB_EXPORT_VAR( g_ClockSpeed, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_dwClockSpeed, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_ClockSpeedMicrosecondsMultiplier, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_ClockSpeedMillisecondsMultiplier, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_ClockSpeedSecondsMultiplier, tier0_ps3 );
|
||||
|
||||
|
||||
|
||||
// Event handling
|
||||
SYS_LIB_EXPORT( XBX_NotifyCreateListener, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_QueueEvent, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_ProcessEvents, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_DispatchEventsQueue, tier0_ps3 );
|
||||
|
||||
// Accessors
|
||||
SYS_LIB_EXPORT( XBX_GetLanguageString, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_IsLocalized, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_IsAudioLocalized, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_GetNextSupportedLanguage, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_IsRestrictiveLanguage, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_GetImageChangelist, tier0_ps3 );
|
||||
|
||||
//
|
||||
// Storage devices management
|
||||
//
|
||||
SYS_LIB_EXPORT( XBX_ResetStorageDeviceInfo, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_DescribeStorageDevice, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_MakeStorageContainerRoot, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( XBX_GetStorageDeviceId, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_SetStorageDeviceId, tier0_ps3 );
|
||||
|
||||
//
|
||||
// Information about game primary user
|
||||
//
|
||||
SYS_LIB_EXPORT( XBX_GetPrimaryUserId, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_SetPrimaryUserId, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( XBX_GetPrimaryUserIsGuest, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_SetPrimaryUserIsGuest, tier0_ps3 );
|
||||
|
||||
//
|
||||
// Disabling and enabling input from controllers
|
||||
//
|
||||
SYS_LIB_EXPORT( XBX_ResetUserIdSlots, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_ClearUserIdSlots, tier0_ps3 );
|
||||
|
||||
//
|
||||
// Mapping between game slots and controllers
|
||||
//
|
||||
SYS_LIB_EXPORT( XBX_GetUserId, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_GetSlotByUserId, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_SetUserId, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_ClearSlot, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_ClearUserId, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT( XBX_GetUserIsGuest, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_SetUserIsGuest, tier0_ps3 );
|
||||
|
||||
//
|
||||
// Number of game users
|
||||
//
|
||||
SYS_LIB_EXPORT( XBX_GetNumGameUsers, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_SetNumGameUsers, tier0_ps3 );
|
||||
|
||||
//
|
||||
// Invite related functions
|
||||
//
|
||||
SYS_LIB_EXPORT( XBX_GetInviteSessionId, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_SetInviteSessionId, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_GetInvitedUserXuid, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_SetInvitedUserXuid, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_GetInvitedUserId, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( XBX_SetInvitedUserId, tier0_ps3 );
|
||||
|
||||
//
|
||||
// VXConsole
|
||||
//
|
||||
SYS_LIB_EXPORT( ValvePS3ConsoleInit, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( ValvePS3ConsoleShutdown, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_pValvePS3Console, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( EncodeBinaryToString, tier0_ps3 );
|
||||
SYS_LIB_EXPORT( DecodeBinaryFromString, tier0_ps3 );
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// PS3 system info
|
||||
CPs3ContentPathInfo *g_pPS3PathInfo = NULL;
|
||||
SYS_LIB_EXPORT_VAR( g_pPS3PathInfo, tier0_ps3 );
|
||||
|
||||
|
||||
typedef void * ( *GetProcAddressFunc )( void *pUnused, const char *pFuncName );
|
||||
|
||||
#ifndef _CERT
|
||||
TLSGlobals * ( *g_pfnElfGetTlsGlobals )();
|
||||
extern "C" TLSGlobals *GetTLSGlobals()
|
||||
{
|
||||
Assert( g_pfnElfGetTlsGlobals || !"Accessing TLS from global constructors? Illegal on PS3!" );
|
||||
return g_pfnElfGetTlsGlobals();
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
void(*g_pfnPushMarker)( const char * pName );
|
||||
void(*g_pfnPopMarker)();
|
||||
void(*g_pfnSwapBufferMarker)();
|
||||
|
||||
|
||||
|
||||
void (*g_snRawSPULockHandler) (void);
|
||||
void (*g_snRawSPUUnlockHandler) (void);
|
||||
void (*g_snRawSPUNotifyCreation) (unsigned int uID);
|
||||
void (*g_snRawSPUNotifyDestruction) (unsigned int uID);
|
||||
void (*g_snRawSPUNotifyElfLoad) (unsigned int uID, unsigned int uEntry, const char *pFileName);
|
||||
void (*g_snRawSPUNotifyElfLoadNoWait) (unsigned int uID, unsigned int uEntry, const char *pFileName);
|
||||
void (*g_snRawSPUNotifyElfLoadAbs) (unsigned int uID, unsigned int uEntry, const char *pFileName);
|
||||
void (*g_snRawSPUNotifyElfLoadAbsNoWait) (unsigned int uID, unsigned int uEntry, const char *pFileName);
|
||||
void (*g_snRawSPUNotifySPUStopped) (unsigned int uID);
|
||||
void (*g_snRawSPUNotifySPUStarted) (unsigned int uID);
|
||||
|
||||
PS3_GcmSharedData *g_pGcmSharedData = NULL;
|
||||
|
||||
};
|
||||
|
||||
SYS_LIB_EXPORT_VAR( g_pfnPushMarker, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_pfnPopMarker, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_pfnSwapBufferMarker, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_pGcmSharedData, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT_VAR( g_pPhysicsMiniProfilers, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_pOtherMiniProfilers, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_pLastMiniProfiler, tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_nMiniProfilerFrame, tier0_ps3 );
|
||||
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPULockHandler , tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPUUnlockHandler , tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPUNotifyCreation , tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPUNotifyDestruction , tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPUNotifyElfLoad , tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPUNotifyElfLoadNoWait , tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPUNotifyElfLoadAbs , tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPUNotifyElfLoadAbsNoWait , tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPUNotifySPUStopped , tier0_ps3 );
|
||||
SYS_LIB_EXPORT_VAR( g_snRawSPUNotifySPUStarted , tier0_ps3 );
|
||||
|
||||
PS3_PrxModuleEntry_t ** g_ppPrxModuleEntryList;
|
||||
extern "C" PS3_PrxModuleEntry_t ** PS3_PrxGetModulesList()
|
||||
{
|
||||
return g_ppPrxModuleEntryList;
|
||||
}
|
||||
|
||||
static u32_t g_nCLNumber = 0;
|
||||
extern "C" u32_t XBX_GetImageChangelist()
|
||||
{
|
||||
return g_nCLNumber;
|
||||
}
|
||||
|
||||
extern void VirtualMemoryManager_Shutdown();
|
||||
void Tier0_ShutdownCallback()
|
||||
{
|
||||
Warning( "[PS3 SYSTEM] TIER0 IS SHUTTING DOWN @ %.3f\n", Plat_FloatTime() );
|
||||
|
||||
// Shutdown virtual memory
|
||||
VirtualMemoryManager_Shutdown();
|
||||
|
||||
// Shutdown memory allocator hooks
|
||||
malloc_managed_size mms;
|
||||
mms.current_inuse_size = 0x12345678;
|
||||
mms.current_system_size = 0x09ABCDEF;
|
||||
mms.max_system_size = 0;
|
||||
(void) malloc_stats( &mms );
|
||||
}
|
||||
|
||||
extern "C" int _tier0_ps3_prx_entry( unsigned int args, void *pArg )
|
||||
{
|
||||
Assert( args >= sizeof( PS3_LoadTier0_Parameters_t ) );
|
||||
|
||||
PS3_LoadTier0_Parameters_t *pParams = reinterpret_cast< PS3_LoadTier0_Parameters_t * >( pArg );
|
||||
Assert( pParams->cbSize >= sizeof( PS3_LoadTier0_Parameters_t ) );
|
||||
|
||||
// copy the launch time to use as the baseline time
|
||||
extern int64_t g_fiosLaunchTime;
|
||||
g_fiosLaunchTime = pParams->fiosLaunchTime;
|
||||
|
||||
if ( pParams->ppPrxModulesList )
|
||||
{
|
||||
g_ppPrxModuleEntryList = pParams->ppPrxModulesList;
|
||||
}
|
||||
g_pPS3PathInfo = pParams->pPS3PathInfo;
|
||||
g_pfnPopMarker = pParams->pfnPopMarker;
|
||||
g_pfnPushMarker = pParams->pfnPushMarker;
|
||||
g_pfnSwapBufferMarker = pParams->pfnSwapBufferMarker;
|
||||
g_pGcmSharedData = pParams->m_pGcmSharedData;
|
||||
g_nCLNumber = pParams->nCLNumber;
|
||||
|
||||
// snlilb.h
|
||||
|
||||
#ifndef _CERT
|
||||
|
||||
g_snRawSPULockHandler = pParams->snRawSPULockHandler;
|
||||
g_snRawSPUUnlockHandler = pParams->snRawSPUUnlockHandler;
|
||||
g_snRawSPUNotifyCreation = pParams->snRawSPUNotifyCreation;
|
||||
g_snRawSPUNotifyDestruction = pParams->snRawSPUNotifyDestruction;
|
||||
g_snRawSPUNotifyElfLoad = pParams->snRawSPUNotifyElfLoad;
|
||||
g_snRawSPUNotifyElfLoadNoWait = pParams->snRawSPUNotifyElfLoadNoWait;
|
||||
g_snRawSPUNotifyElfLoadAbs = pParams->snRawSPUNotifyElfLoadAbs;
|
||||
g_snRawSPUNotifyElfLoadAbsNoWait = pParams->snRawSPUNotifyElfLoadAbsNoWait;
|
||||
g_snRawSPUNotifySPUStopped = pParams->snRawSPUNotifySPUStopped;
|
||||
g_snRawSPUNotifySPUStarted = pParams->snRawSPUNotifySPUStarted;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Return tier0 shutdown callback
|
||||
pParams->pfnTier0Shutdown = Tier0_ShutdownCallback;
|
||||
|
||||
if ( pParams->pfnGetTlsGlobals )
|
||||
{
|
||||
#ifndef _CERT
|
||||
g_pfnElfGetTlsGlobals = pParams->pfnGetTlsGlobals;
|
||||
#else
|
||||
// Validate that TLS globals are located correctly in PRX
|
||||
// modules and main ELF binary:
|
||||
// See description of quick TLS access implementation in
|
||||
// memoverride.cpp comments
|
||||
if ( GetTLSGlobals() != pParams->pfnGetTlsGlobals() )
|
||||
Error( "<vitaliy>: TLS globals location mismatch!\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
return SYS_PRX_RESIDENT;
|
||||
}
|
||||
|
||||
extern "C" int _tier0_ps3_prx_exit( unsigned int args, void *pArg )
|
||||
{
|
||||
return SYS_PRX_STOP_OK;
|
||||
}
|
||||
|
||||
void _tier0_ps3_prx_required_for_linking()
|
||||
{
|
||||
}
|
||||
27
tier0/prxexport.cpp
Normal file
27
tier0/prxexport.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
#ifndef _PS3
|
||||
#error "Error: _PS3 not defined in PS3-specific file"
|
||||
#endif // _PS3
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define tier0_ps3 tier0_dbg
|
||||
#else
|
||||
#define tier0_ps3 tier0_rel
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include "Debug_PS3/prxexport.inl"
|
||||
#else
|
||||
#include "Release_PS3/prxexport.inl"
|
||||
#endif
|
||||
|
||||
extern void _tier0_ps3_prx_required_for_linking();
|
||||
void _tier0_ps3_prx_required_for_linking_prx()
|
||||
{
|
||||
_tier0_ps3_prx_required_for_linking();
|
||||
}
|
||||
|
||||
312
tier0/ps3/ps3_console.cpp
Normal file
312
tier0/ps3/ps3_console.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
// ps_console.cpp - for communicating with vxconsole_ps3
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/event.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ps3/ps3_console.h"
|
||||
#include "ps3/ps3_vxconsole.h"
|
||||
#include "ps3/ps3_win32stubs.h"
|
||||
#include "../utils/ps3/vpbdm/vpbdm_exports.h"
|
||||
#include "ps3/ps3_helpers.h"
|
||||
#include "ps3_pathinfo.h"
|
||||
// #include "cmd.h"
|
||||
|
||||
#ifdef _RETAIL
|
||||
//Stubs for retail
|
||||
bool PS3_DebugString( unsigned int color, const char* format, ... )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool PS3_IsConsoleConnected()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void PS3_InitConsoleMonitor( bool bWaitForConnect)
|
||||
{
|
||||
|
||||
}
|
||||
void PS3_UpdateConsoleMonitor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int XBX_rAddCommands(int numCommands, const char* commands[], const char* help[])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
//Development
|
||||
|
||||
static PS3_LoadAppSystemInterface_Parameters_t s_VXBDMPrxLoadParameters;
|
||||
IPS3Console *g_pValvePS3Console = NULL;
|
||||
|
||||
// an empty implementation to use if the debug PRX isn't available
|
||||
class CPS3DummyDebugConsole : public IPS3Console
|
||||
{
|
||||
public:
|
||||
virtual void SendRemoteCommand( const char *dbgCommand, bool bAsync ) {}
|
||||
virtual void SendPrefixedDECIMessage( const char *prefix, const char *message, bool async ) {};
|
||||
virtual void DebugString( unsigned int color, const char *format, ... ) {}
|
||||
virtual bool IsConsoleConnected() {return false;}
|
||||
virtual void InitConsoleMonitor( bool bWaitForConnect = false ) {}
|
||||
virtual void DisconnectConsoleMonitor() {}
|
||||
virtual void FlushDebugOutput() {}
|
||||
virtual bool GetXboxName( char *, unsigned * ) { return false; }
|
||||
virtual void CrashDump( bool ) {}
|
||||
virtual void CrashDumpFullHeap( bool ) {}
|
||||
virtual void DumpDllInfo( const char *pBasePath ) {}
|
||||
// virtual void OutputDebugString( const char * ) = 0;
|
||||
virtual bool IsDebuggerPresent() { return false; }
|
||||
|
||||
virtual int SetProfileAttributes( const char *pProfileName, int numCounters, const char *names[], COLORREF colors[] ) { return 0; }
|
||||
virtual void SetProfileData( const char *pProfileName, int numCounters, unsigned int *counters ) {}
|
||||
virtual int MemDump( const char *pDumpFileName ) { return -1; }
|
||||
virtual int TimeStampLog( float time, const char *pString ) { return -1; }
|
||||
virtual int MaterialList( int nMaterials, const xMaterialList_t *pXMaterialList ) { return -1; }
|
||||
virtual int TextureList( int nTextures, const xTextureList_t *pXTextureList ) { return -1; }
|
||||
virtual int SoundList( int nSounds, const xSoundList_t *pXSoundList ) { return -1; }
|
||||
virtual int MapInfo( const xMapInfo_t *pXMapInfo ) { return -1; }
|
||||
virtual int AddCommands( int numCommands, const char *commands[], const char* help[] ) { return -1; }
|
||||
virtual int ModelList( int nModels, const xModelList_t *pList ) { return -1; }
|
||||
virtual int DataCacheList( int nItems, const xDataCacheItem_t* pItems ) { return -1; }
|
||||
virtual int VProfNodeList( int nItems, const xVProfNodeItem_t *pItems ) { return -1; }
|
||||
virtual int TraceComplete( void ) { return -1; }
|
||||
virtual int BugReporter( void ) { return -1; }
|
||||
virtual bool SendBinaryData( const void *pData, int iDataSize, bool bAsync = true, DWORD dwSyncTimout = 15000 ) { return false; }
|
||||
virtual int SyncDvdDevCache() { return -1; }
|
||||
virtual int SyncShaderCache() { return -1; }
|
||||
virtual int Version( int nVersion ) { return -1; }
|
||||
virtual void TransmitScreenshot( char *pFrameBuffer, uint32 uWidth, uint32 uHeight, uint32 uPitch, uint32 uColorFmt ){ }
|
||||
|
||||
virtual void PumpMessage( fRemoteCommandSink_t ) {}
|
||||
virtual int SendBinaryDECI( const uint8 *dbgCommand, uint length, bool ){return 0;}
|
||||
|
||||
virtual void AddOnConnectDelegate( fOnConnectDelegate_t pOnConnectDelegate ) {};
|
||||
virtual void AddOnDisconnectDelegate( fOnDisconnectDelegate_t pOnDisconnectDelegate ) {};
|
||||
};
|
||||
CPS3DummyDebugConsole g_ValveDummyDebugConsoleForUseWhenThePRXContainingTheRealOneIsntAvailable;
|
||||
|
||||
// try to load the debug library
|
||||
void ValvePS3ConsoleInit()
|
||||
{
|
||||
if ( g_pValvePS3Console != NULL )
|
||||
{
|
||||
AssertMsg(false,"Called ValvePS3ConsoleInit twice!\n");
|
||||
// emergency cleanup
|
||||
ValvePS3ConsoleShutdown();
|
||||
g_pValvePS3Console = NULL;
|
||||
}
|
||||
|
||||
memset( &s_VXBDMPrxLoadParameters, 0, sizeof( s_VXBDMPrxLoadParameters ) );
|
||||
s_VXBDMPrxLoadParameters.cbSize = sizeof( s_VXBDMPrxLoadParameters );
|
||||
|
||||
char szAbsoluteModuleName[1024];
|
||||
// getcwd not supported on ps3; use PRX path instead (TODO: fallback to DISK path too)
|
||||
snprintf( szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/%s",
|
||||
g_pPS3PathInfo->PrxPath(), "vxbdm_ps3.sprx" );
|
||||
|
||||
int loadresult = IsCert() ? ENOENT : PS3_PrxLoad( szAbsoluteModuleName, &s_VXBDMPrxLoadParameters );
|
||||
if ( loadresult < CELL_OK )
|
||||
{
|
||||
// we failed to load the module. This might be because we're a cert build, so it's fine.
|
||||
Msg("VXBDM: not loaded\n");
|
||||
g_pValvePS3Console = &g_ValveDummyDebugConsoleForUseWhenThePRXContainingTheRealOneIsntAvailable;
|
||||
}
|
||||
else // loaded successfully
|
||||
{
|
||||
g_pValvePS3Console = reinterpret_cast< IPS3Console *> ( (*s_VXBDMPrxLoadParameters.pfnCreateInterface)(NULL, NULL) );
|
||||
Msg("VXBDM: loaded %x!\n", loadresult);
|
||||
}
|
||||
}
|
||||
|
||||
void ValvePS3ConsoleShutdown()
|
||||
{
|
||||
if ( !g_pValvePS3Console )
|
||||
return;
|
||||
|
||||
g_pValvePS3Console = NULL;
|
||||
PS3_PrxUnload( s_VXBDMPrxLoadParameters.sysPrxId );
|
||||
};
|
||||
|
||||
|
||||
//defined in ps3_events.cpp, needed to process commands sent from the VXConsole
|
||||
extern void XBX_ProcessXCommand(const char* command);
|
||||
|
||||
|
||||
bool PS3_DebugString( unsigned int color, const char* format, ... )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PS3_IsConsoleConnected()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int XBX_rAddCommands(int numCommands, const char* commands[], const char* help[])
|
||||
{
|
||||
#if 0
|
||||
// PS3_UpdateConsoleMonitor();
|
||||
|
||||
if(PS3_IsConsoleConnected())
|
||||
{
|
||||
/*
|
||||
for(int i=0;i<numCommands;i++)
|
||||
{
|
||||
sprintf(ps3ConsoleBuffer, "AddCommand() %s %s", commands[i], help[i]);
|
||||
sys_deci3_send(ps3ConsoleSessionId,(uint8_t*)ps3ConsoleBuffer,strlen(ps3ConsoleBuffer)+1);
|
||||
}
|
||||
return 1;
|
||||
*/
|
||||
}
|
||||
#else
|
||||
Error("Deprecated function called.\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PS3_InitConsoleMonitor( bool bWaitForConnect )
|
||||
{
|
||||
Error("You didn't implement this you idiot\n");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// XBX_ProcessXCommand
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void XBX_ProcessXCommand(const char* command)
|
||||
{
|
||||
#if 0 // This is the Windows way of doing things. We are not Windows.
|
||||
// if we WERE Windows, we would do this, which would call a function
|
||||
// which found a function pointer which called a function which
|
||||
// dispatched a switch which etc. etc....
|
||||
// by the way, function call overhead is at least 21 cycles on PS3.
|
||||
// remote command
|
||||
// pass it game via windows message
|
||||
HWND hWnd = GetFocus();
|
||||
WNDPROC windowProc = ( WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC );
|
||||
if ( windowProc )
|
||||
{
|
||||
windowProc( hWnd, WM_XREMOTECOMMAND, 0, (LPARAM)command );
|
||||
}
|
||||
#elif 0 // until finally just doing this:
|
||||
ECommandTarget_t t = Cbuf_GetCurrentPlayer();
|
||||
Cbuf_AddText( t, command );
|
||||
Cbuf_AddText( t, "\n" );
|
||||
#else
|
||||
#pragma message("You must implement XBX_ProcessXCommand")
|
||||
#endif
|
||||
}
|
||||
|
||||
void PS3_ReceiveCommand(const char *strCommand)
|
||||
{
|
||||
#if 0
|
||||
// skip over the command prefix and the exclamation mark
|
||||
strCommand += strlen( XBX_DBGCOMMANDPREFIX ) + 1;
|
||||
|
||||
if ( strCommand[0] == '\0' )
|
||||
{
|
||||
// just a ping
|
||||
char tmp[1]={'\0'};
|
||||
sys_deci3_send(ps3ConsoleSessionId,(uint8_t*)tmp,1);
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
if ( strncmp( strCommand, "__connect__", 11 ) ==0)
|
||||
{
|
||||
//respond that we're connected
|
||||
sys_deci3_send(ps3ConsoleSessionId,(uint8_t*)strCommand,strlen(strCommand)+1);
|
||||
|
||||
if(!isConsoleConnected)
|
||||
{
|
||||
//initial connect - get the console variables (used for autocomplete on the vxconsole)
|
||||
XBX_ProcessXCommand("getcvars");
|
||||
}
|
||||
isConsoleConnected=true;
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
if ( strncmp( strCommand, "__disconnect__", 14 ) ==0)
|
||||
{
|
||||
isConsoleConnected=false;
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
if ( strncmp( strCommand, "__complete__", 12 ) ==0)
|
||||
{
|
||||
//isn't used for ps3 vxconsole
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
if ( strncmp( strCommand, "__memory__", 10 ) ==0)
|
||||
{
|
||||
//...
|
||||
goto cleanUp;
|
||||
}
|
||||
|
||||
XBX_ProcessXCommand(strCommand);
|
||||
cleanUp:
|
||||
return;
|
||||
#else
|
||||
Warning("PS3_ReceiveCommand() is not implemented.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void PS3_UpdateConsoleMonitor()
|
||||
{
|
||||
#if 0
|
||||
if(ps3ConsoleSessionId==-1)
|
||||
return;
|
||||
|
||||
const int MAX_EVENTS=32;
|
||||
int numEvents;
|
||||
sys_event_t events[MAX_EVENTS];
|
||||
int ret = sys_event_queue_tryreceive(ps3ConsoleEventQueue, events,MAX_EVENTS, &numEvents);
|
||||
if(ret!=CELL_OK)
|
||||
{
|
||||
//fprintf(stderr, "sys_event_queue_tryreceive() failed: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i=0;i<numEvents;i++)
|
||||
{
|
||||
sys_event &event=events[i];
|
||||
switch(event.data1)
|
||||
{
|
||||
case SYS_DECI3_EVENT_COMM_ENABLED:
|
||||
//(wait for a "__connect__" packet instead of assuming an enabled connection means its working)
|
||||
//isConsoleConnected=true;
|
||||
break;
|
||||
case SYS_DECI3_EVENT_COMM_DISABLED:
|
||||
isConsoleConnected=false;
|
||||
break;
|
||||
case SYS_DECI3_EVENT_DATA_READY:
|
||||
ret = sys_deci3_receive(ps3ConsoleSessionId,(uint8_t*)ps3ConsoleBuffer,event.data2);
|
||||
if(ret==CELL_OK)
|
||||
{
|
||||
PS3_ReceiveCommand(ps3ConsoleBuffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int XBX_rSetProfileAttributes(const char *pProfileName, int numCounters, const char *names[], COLORREF colors[])
|
||||
{
|
||||
return ( !g_pValvePS3Console ) ? 0 : g_pValvePS3Console->SetProfileAttributes( pProfileName, numCounters, names, colors );
|
||||
}
|
||||
|
||||
void XBX_rSetProfileData( const char *pProfileName, int numCounters, unsigned int *counters )
|
||||
{
|
||||
g_pValvePS3Console->SetProfileData( pProfileName, numCounters, counters );
|
||||
}
|
||||
|
||||
void XBX_rVProfNodeList( int nItems, const xVProfNodeItem_t *pItems )
|
||||
{
|
||||
g_pValvePS3Console->VProfNodeList( nItems, pItems );
|
||||
}
|
||||
|
||||
#endif//!_RETAIL
|
||||
35
tier0/resource.h
Normal file
35
tier0/resource.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Developer Studio generated include file.
|
||||
// Used by assert_dialog.rc
|
||||
//
|
||||
#define IDD_ASSERT_DIALOG 101
|
||||
#define IDC_FILENAME_CONTROL 1000
|
||||
#define IDC_LINE_CONTROL 1001
|
||||
#define IDC_IGNORE_FILE 1002
|
||||
#define IDC_IGNORE_NEARBY 1003
|
||||
#define IDC_IGNORE_NUMLINES 1004
|
||||
#define IDC_IGNORE_THIS 1005
|
||||
#define IDC_BREAK 1006
|
||||
#define IDC_IGNORE_ALL 1008
|
||||
#define IDC_IGNORE_ALWAYS 1009
|
||||
#define IDC_IGNORE_NUMTIMES 1010
|
||||
#define IDC_ASSERT_MSG_CTRL 1011
|
||||
#define IDC_NOID -1
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 103
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1005
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
114
tier0/security.cpp
Normal file
114
tier0/security.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Platform level security functions.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
// Uncomment the following line to require the prescence of a hardware key.
|
||||
//#define REQUIRE_HARDWARE_KEY
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
#define WIN_32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/memalloc.h"
|
||||
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
// This is the previous key, which was compromised.
|
||||
//#define VALVE_DESKEY_ID "uW" // Identity Password, Uniquely identifies HL2 keys
|
||||
#define VALVE_DESKEY_ID "u$" // Identity Password, Uniquely identifies HL2 keys
|
||||
|
||||
// Include the key's API:
|
||||
#include "deskey/algo.h"
|
||||
#include "deskey/dk2win32.h"
|
||||
|
||||
#pragma comment(lib, "DESKey/algo32.lib" )
|
||||
#pragma comment(lib, "DESKey/dk2win32.lib" )
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
bool Plat_VerifyHardwareKey()
|
||||
{
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
|
||||
// Ensure that a key with our ID exists:
|
||||
if ( FindDK2( VALVE_DESKEY_ID, NULL ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plat_VerifyHardwareKeyDriver()
|
||||
{
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
// Ensure that the driver is at least installed:
|
||||
return DK2DriverInstalled() != 0;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plat_VerifyHardwareKeyPrompt()
|
||||
{
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
if ( !DK2DriverInstalled() )
|
||||
{
|
||||
if( IDCANCEL == MessageBox( NULL, "No drivers detected for the hardware key, please install them and re-run the application.\n", "No Driver Detected", MB_OKCANCEL ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while ( !Plat_VerifyHardwareKey() )
|
||||
{
|
||||
if ( IDCANCEL == MessageBox( NULL, "Please insert the hardware key and hit 'ok'.\n", "Insert Hardware Key", MB_OKCANCEL ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( int i=0; i < 2; i++ )
|
||||
{
|
||||
// Is the key in now?
|
||||
if( Plat_VerifyHardwareKey() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sleep 2 / 3 of a second before trying again, in case the os recognizes the key slightly after it's being inserted:
|
||||
Sleep(666);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plat_FastVerifyHardwareKey()
|
||||
{
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
static int nIterations = 0;
|
||||
|
||||
nIterations++;
|
||||
if( nIterations > 100 )
|
||||
{
|
||||
nIterations = 0;
|
||||
return Plat_VerifyHardwareKey();
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
132
tier0/security_linux.cpp
Normal file
132
tier0/security_linux.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Platform level security functions.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
// Uncomment the following line to require the prescence of a hardware key.
|
||||
//#define REQUIRE_HARDWARE_KEY
|
||||
// NOTE - this DOESN'T work under linux!!!!
|
||||
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/memalloc.h"
|
||||
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
|
||||
#define VALVE_DESKEY_ID "uW" // Uniquely identifies HL2 keys
|
||||
|
||||
// Include the key's API:
|
||||
#include "deskey/algo.h"
|
||||
#include "deskey/dk2win32.h"
|
||||
|
||||
// #pragma comment(lib, "DESKey/algo32.lib" )
|
||||
// #pragma comment(lib, "DESKey/dk2win32.lib" )
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// NOTE: This has to be the last file included! (turned off below, since this is included like a header)
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
bool Plat_VerifyHardwareKey()
|
||||
{
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
|
||||
// Ensure that a key with our ID exists:
|
||||
if( FindDK2( VALVE_DESKEY_ID, NULL ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
#else
|
||||
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plat_VerifyHardwareKeyDriver()
|
||||
{
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
|
||||
// Ensure that the driver is at least installed:
|
||||
return DK2DriverInstalled() != 0;
|
||||
|
||||
#else
|
||||
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plat_VerifyHardwareKeyPrompt()
|
||||
{
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
|
||||
if( !DK2DriverInstalled() )
|
||||
{
|
||||
if( IDCANCEL == MessageBox( NULL, "No drivers detected for the hardware key, please install them and re-run the application.\n", "No Driver Detected", MB_OKCANCEL ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while( !Plat_VerifyHardwareKey() )
|
||||
{
|
||||
if( IDCANCEL == MessageBox( NULL, "Please insert the hardware key and hit 'ok'.\n", "Insert Hardware Key", MB_OKCANCEL ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for( int i=0; i < 2; i++ )
|
||||
{
|
||||
// Is the key in now?
|
||||
if( Plat_VerifyHardwareKey() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sleep 2 / 3 of a second before trying again, in case the os recognizes the key slightly after it's being inserted:
|
||||
Sleep(666);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
#else
|
||||
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Plat_FastVerifyHardwareKey()
|
||||
{
|
||||
#ifdef REQUIRE_HARDWARE_KEY
|
||||
|
||||
static int nIterations = 0;
|
||||
|
||||
nIterations++;
|
||||
if( nIterations > 100 )
|
||||
{
|
||||
nIterations = 0;
|
||||
return Plat_VerifyHardwareKey();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
#else
|
||||
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Turn off memdbg macros (turned on up top) since this is included like a header
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
565
tier0/stackstats.cpp
Normal file
565
tier0/stackstats.cpp
Normal file
@@ -0,0 +1,565 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/stackstats.h"
|
||||
#include "tier0/threadtools.h"
|
||||
#include "tier0/icommandline.h"
|
||||
|
||||
#include "tier0/valve_off.h"
|
||||
|
||||
#if defined( PLATFORM_WINDOWS_PC )
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#endif
|
||||
|
||||
#if defined( PLATFORM_X360 )
|
||||
#include <xbdm.h>
|
||||
#include "xbox/xbox_console.h"
|
||||
#include "xbox/xbox_vxconsole.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
#endif
|
||||
|
||||
#include "tier0/valve_on.h"
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
bool _CCallStackStatsGatherer_Internal_DumpStatsToFile( const char *szFileName, const CCallStackStatsGatherer_Standardized_t &StatsGatherer, bool bAllowMemoryAllocations )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else //#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
|
||||
static void BufferedFwrite( FILE *pFile, uint8 *pBuffer, size_t iBufferSize, const void *pData, size_t iDataSize, size_t &iWriteMarker )
|
||||
{
|
||||
//write to buffer until it's full, then write to file
|
||||
if( (iWriteMarker + iDataSize) > iBufferSize )
|
||||
{
|
||||
//too big for the buffer, flush the buffer and try again
|
||||
if( iWriteMarker != 0 )
|
||||
{
|
||||
fwrite( pBuffer, 1, iWriteMarker, pFile );
|
||||
iWriteMarker = 0;
|
||||
}
|
||||
|
||||
if( iDataSize > iBufferSize )
|
||||
{
|
||||
//too big to ever hold in the buffer, write it now
|
||||
fwrite( pData, iDataSize, 1, pFile );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy( &pBuffer[iWriteMarker], pData, iDataSize );
|
||||
iWriteMarker += iDataSize;
|
||||
}
|
||||
|
||||
struct CCallStackStatsGatherer_DumpHelperVars_t
|
||||
{
|
||||
uint8 *pWriteBuffer;
|
||||
size_t iWriteBufferSize;
|
||||
size_t *iWriteMarker;
|
||||
FILE *pFile;
|
||||
bool bAllowMemoryAllocations;
|
||||
};
|
||||
|
||||
bool _CCallStackStatsGatherer_Internal_DumpSubTree( const CCallStackStatsGatherer_Standardized_t &StatsGatherer, void *pDumpHelpers )
|
||||
{
|
||||
size_t CapturedCallStackLength = 0;
|
||||
size_t iEntrySizeWithCallStack = 0;
|
||||
void *pEntries = NULL;
|
||||
size_t iEntryCount = 0;
|
||||
CCallStackStatsGatherer_Standardized_t *pSubTrees = NULL;
|
||||
size_t iSubTreeCount = 0;
|
||||
const char *szStructName = "";
|
||||
StatsGatherer.pFunctionTable->pfn_GetDumpInfo( StatsGatherer.pGatherer, szStructName, CapturedCallStackLength, iEntrySizeWithCallStack, pEntries, iEntryCount, pSubTrees, iSubTreeCount );
|
||||
|
||||
if( iEntryCount == 0 )
|
||||
return false; //nothing to write
|
||||
|
||||
CCallStackStatsGatherer_DumpHelperVars_t *pHelpers = (CCallStackStatsGatherer_DumpHelperVars_t *)pDumpHelpers;
|
||||
uint8 *pWriteBuffer = pHelpers->pWriteBuffer;
|
||||
size_t iWriteBufferSize = pHelpers->iWriteBufferSize;
|
||||
size_t &iWriteMarker = *pHelpers->iWriteMarker;
|
||||
FILE *pFile = pHelpers->pFile;
|
||||
|
||||
//struct header
|
||||
{
|
||||
size_t iStructNameLength = strlen( szStructName ) + 1;
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, szStructName, iStructNameLength, iWriteMarker );
|
||||
|
||||
//number of sub-trees
|
||||
uint32 iSubTreeCount32 = (uint32)iSubTreeCount;
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, &iSubTreeCount32, sizeof( uint32 ), iWriteMarker );
|
||||
|
||||
//sub-tree translation addresses
|
||||
for( size_t i = 0; i != iSubTreeCount; ++i )
|
||||
{
|
||||
//sub-trees will be written out immediately after we finish dumping this gatherer, depth first recursion
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, &pSubTrees[i].pGatherer, sizeof( void * ), iWriteMarker );
|
||||
}
|
||||
|
||||
//size of call stack array
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, &CapturedCallStackLength, sizeof( size_t ), iWriteMarker );
|
||||
|
||||
//size of each entry, including call stack
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, &iEntrySizeWithCallStack, sizeof( size_t ), iWriteMarker );
|
||||
|
||||
//flush the write buffer
|
||||
if( iWriteMarker != 0 )
|
||||
{
|
||||
fwrite( pWriteBuffer, 1, iWriteMarker, pFile );
|
||||
iWriteMarker = 0;
|
||||
}
|
||||
|
||||
//reserve space for writing out the description blob size since we don't know how big it is until after we write it.
|
||||
size_t iStructDescSizeWriteMarker = iWriteMarker;
|
||||
iWriteMarker += sizeof( uint32 );
|
||||
|
||||
//describe how the structure should be interpreted on the receiving end
|
||||
iWriteMarker += StatsGatherer.pFunctionTable->pfn_DescribeCallStackStatStruct( pWriteBuffer + iWriteMarker, iWriteBufferSize - iWriteMarker );
|
||||
|
||||
//write out the description size now
|
||||
*(uint32 *)(pWriteBuffer + iStructDescSizeWriteMarker) = (uint32)(iWriteMarker - (iStructDescSizeWriteMarker + sizeof( uint32 )));
|
||||
}
|
||||
|
||||
size_t iInfoPos;
|
||||
|
||||
//stats entries
|
||||
{
|
||||
//number of entries
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, &iEntryCount, sizeof( size_t ), iWriteMarker );
|
||||
|
||||
iInfoPos = iWriteMarker + ftell( pFile ); //where in the file we wrote out the stats entries. Need this in case we need to repurpose the stats memory for sorting later
|
||||
|
||||
//write them all out
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, pEntries, iEntrySizeWithCallStack * iEntryCount, iWriteMarker );
|
||||
}
|
||||
|
||||
//flush any remnants of the write buffer. We're going to repurpose it for a bit
|
||||
if( iWriteMarker != 0 )
|
||||
{
|
||||
fwrite( pWriteBuffer, 1, iWriteMarker, pFile );
|
||||
iWriteMarker = 0;
|
||||
}
|
||||
|
||||
|
||||
//now sort the individual addresses, throwing away duplicates. We need memory for this operation, but we might be in a state where we can't allocate any...
|
||||
//So we'll start with the stack memory used for buffered writing first, then overflow into a memory allocation that should be able to hold it all if we're allowed,
|
||||
//but if that fails we're going to repurpose our stat tracking memory we already own and just wrote to file. Then read in what we overwrote after we're done.
|
||||
int iUniqueAddresses = 0;
|
||||
int iSortSize = (int)iWriteBufferSize / sizeof( void * );
|
||||
void **pSortedAddresses = (void **)pWriteBuffer;
|
||||
{
|
||||
uint8 *pEntryRead = (uint8 *)pEntries;
|
||||
for( uint32 i = 0; i != iEntryCount; ++i )
|
||||
{
|
||||
for( size_t j = 0; j != CapturedCallStackLength; ++j )
|
||||
{
|
||||
void *pInsertAddress = ((void **)pEntryRead)[j];
|
||||
if( pInsertAddress == NULL )
|
||||
break;
|
||||
|
||||
//binary search
|
||||
int iHigh = iUniqueAddresses - 1;
|
||||
int iLow = 0;
|
||||
while( iHigh >= iLow )
|
||||
{
|
||||
int iMid = (iHigh + iLow) >> 1;
|
||||
if( pSortedAddresses[iMid] > pInsertAddress )
|
||||
{
|
||||
iHigh = iMid - 1;
|
||||
}
|
||||
else if( pSortedAddresses[iMid] < pInsertAddress )
|
||||
{
|
||||
iLow = iMid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//same address
|
||||
iLow = iMid;
|
||||
//iHigh = iLow - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( iLow > iUniqueAddresses )
|
||||
{
|
||||
//tack it onto the end
|
||||
if( iUniqueAddresses >= iSortSize )
|
||||
{
|
||||
size_t maxSize = sizeof( void * ) * CapturedCallStackLength * iEntryCount;
|
||||
//crap, grew past the temp stack buffer, use real memory...
|
||||
void **pTemp = pHelpers->bAllowMemoryAllocations ? new void * [maxSize] : NULL;
|
||||
if( pTemp == NULL )
|
||||
{
|
||||
//double crap, memory wasn't available, overwrite our stat tracking data and read it back from file later...
|
||||
pTemp = (void **)pEntries;
|
||||
}
|
||||
|
||||
memcpy( pTemp, pSortedAddresses, iUniqueAddresses * sizeof( void * ) );
|
||||
iSortSize = (int)maxSize;
|
||||
pSortedAddresses = pTemp;
|
||||
}
|
||||
pSortedAddresses[iLow] = pInsertAddress;
|
||||
++iUniqueAddresses;
|
||||
}
|
||||
else if( pSortedAddresses[iLow] != pInsertAddress )
|
||||
{
|
||||
//insert it here
|
||||
if( iUniqueAddresses >= iSortSize )
|
||||
{
|
||||
size_t maxSize = sizeof( void * ) * CapturedCallStackLength * iEntryCount;
|
||||
//crap, grew past the temp stack buffer, use real memory...
|
||||
void **pTemp = pHelpers->bAllowMemoryAllocations ? new void * [maxSize] : NULL;
|
||||
if( pTemp == NULL )
|
||||
{
|
||||
//double crap, memory wasn't available, overwrite our stat tracking data and read it back from file later...
|
||||
pTemp = (void **)pEntries;
|
||||
}
|
||||
|
||||
memcpy( pTemp, pSortedAddresses, iUniqueAddresses * sizeof( void * ) );
|
||||
iSortSize = (int)maxSize;
|
||||
pSortedAddresses = pTemp;
|
||||
}
|
||||
for( int k = iUniqueAddresses; --k >= iLow; )
|
||||
{
|
||||
pSortedAddresses[k + 1] = pSortedAddresses[k];
|
||||
}
|
||||
++iUniqueAddresses;
|
||||
pSortedAddresses[iLow] = pInsertAddress;
|
||||
}
|
||||
//else do nothing, it's a duplicate
|
||||
#ifdef DBGFLAG_ASSERT
|
||||
else
|
||||
{
|
||||
Assert( pSortedAddresses[iLow] == pInsertAddress );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
pEntryRead += iEntrySizeWithCallStack;
|
||||
}
|
||||
}
|
||||
|
||||
//now that we have them sorted, see if we need to fudge with the memory we own a bit...
|
||||
if( (uint8 *)pSortedAddresses == pWriteBuffer )
|
||||
{
|
||||
size_t iUniqueAddressSpace = iUniqueAddresses * sizeof( void * );
|
||||
pWriteBuffer += iUniqueAddressSpace;
|
||||
iWriteBufferSize -= iUniqueAddressSpace;
|
||||
}
|
||||
|
||||
//write the address to file/symbol/line translation table
|
||||
{
|
||||
Assert( iUniqueAddresses > 0 );
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, &iUniqueAddresses, sizeof( int ), iWriteMarker );
|
||||
|
||||
//now that they're sorted, we can assume that we have a basic grouping by symbol. And on top of that we can assume that a unique symbol is contained
|
||||
//in one file. But we're not going to use that information just yet...
|
||||
|
||||
//for first version, we'll store all the info for every address. It's excessive but stable. A future file protocol revision should try recognize file/symbol duplicates. The problem is finding them without using mallocs()
|
||||
for( int i = 0; i != iUniqueAddresses; ++i )
|
||||
{
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, &pSortedAddresses[i], sizeof( void * ), iWriteMarker );
|
||||
|
||||
char szBuff[1024];
|
||||
if( !GetModuleNameFromAddress( pSortedAddresses[i], szBuff, sizeof( szBuff ) ) )
|
||||
{
|
||||
szBuff[0] = '\0';
|
||||
}
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, szBuff, strlen( szBuff ) + 1, iWriteMarker );
|
||||
|
||||
if( !GetSymbolNameFromAddress( pSortedAddresses[i], szBuff, sizeof( szBuff ) ) )
|
||||
{
|
||||
szBuff[0] = '\0';
|
||||
}
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, szBuff, strlen( szBuff ) + 1, iWriteMarker );
|
||||
|
||||
uint32 iLine;
|
||||
if( !GetFileAndLineFromAddress( pSortedAddresses[i], szBuff, sizeof( szBuff ), iLine ) )
|
||||
{
|
||||
szBuff[0] = '\0';
|
||||
iLine = 0;
|
||||
}
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, szBuff, strlen( szBuff ) + 1, iWriteMarker );
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, &iLine, sizeof( uint32 ), iWriteMarker );
|
||||
}
|
||||
}
|
||||
|
||||
uint32 iSubToolDataSize = 0; //there's generally a lot of data to process in this file, save some space for any tools that want to save data back to the file in a lump format
|
||||
BufferedFwrite( pFile, pWriteBuffer, iWriteBufferSize, &iSubToolDataSize, sizeof( uint32 ), iWriteMarker );
|
||||
|
||||
//flush any remnants of the write buffer
|
||||
if( iWriteMarker != 0 )
|
||||
{
|
||||
fwrite( pWriteBuffer, 1, iWriteMarker, pFile );
|
||||
iWriteMarker = 0;
|
||||
}
|
||||
|
||||
pWriteBuffer = pHelpers->pWriteBuffer;
|
||||
iWriteBufferSize = pHelpers->iWriteBufferSize;
|
||||
|
||||
|
||||
|
||||
if( pSortedAddresses == (void **)pEntries )
|
||||
{
|
||||
// We trashed the data after we wrote it. We needed the memory for sorting unique addresses. Fix it by reading what we overwrote back into place
|
||||
fseek( pFile, (long)iInfoPos, SEEK_SET );
|
||||
fread( pEntries, sizeof( void * ) * iUniqueAddresses, 1, pFile ); //we overwrote iUniqueAddresses worth of void pointers, so we just need to fix that chunk
|
||||
fseek( pFile, 0, SEEK_END );
|
||||
}
|
||||
else if( (uint8 *)pSortedAddresses != pWriteBuffer )
|
||||
{
|
||||
//we overflowed the temp buffer, but got away with an allocation. Free it
|
||||
delete []pSortedAddresses;
|
||||
}
|
||||
|
||||
//dump sub-trees, depth first
|
||||
for( size_t i = 0; i != iSubTreeCount; ++i )
|
||||
{
|
||||
bool bRet = _CCallStackStatsGatherer_Internal_DumpSubTree( pSubTrees[i], pDumpHelpers );
|
||||
if( bRet == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t _CCallStackStatsGatherer_Write_FieldDescriptions( CallStackStatStructDescFuncs *pFieldDescriptions, uint8 *pWriteBuffer, size_t iWriteBufferSize )
|
||||
{
|
||||
size_t iWriteMarker = 0;
|
||||
|
||||
//lump ID
|
||||
*(uint32 *)(pWriteBuffer + iWriteMarker) = (uint32)SSDLID_FIELDDESC;
|
||||
iWriteMarker += sizeof( uint32 );
|
||||
|
||||
//lump size, currently unknown, so just save a spot to write it later
|
||||
size_t iLumpSizeMarker = iWriteMarker;
|
||||
iWriteMarker += sizeof( uint32 );
|
||||
|
||||
//number of fields described, currently unknown, so just save a spot to write it later
|
||||
size_t iNumFieldsWriteMarker = iWriteMarker;
|
||||
iWriteMarker += sizeof( uint32 );
|
||||
uint32 iNumFields = 0;
|
||||
|
||||
//describe the structure in the file
|
||||
CallStackStatStructDescFuncs *pDesc = pFieldDescriptions;
|
||||
while( pDesc != NULL )
|
||||
{
|
||||
size_t iFieldWrote = pDesc->DescribeField( pWriteBuffer + iWriteMarker, iWriteBufferSize - iWriteMarker );
|
||||
if( iFieldWrote != 0 )
|
||||
{
|
||||
++iNumFields;
|
||||
}
|
||||
iWriteMarker += iFieldWrote;
|
||||
pDesc = pDesc->m_pNext;
|
||||
}
|
||||
|
||||
*(uint32 *)(pWriteBuffer + iNumFieldsWriteMarker) = iNumFields;
|
||||
*(uint32 *)(pWriteBuffer + iLumpSizeMarker) = (uint32)(iWriteMarker - (iLumpSizeMarker + sizeof( uint32 )));
|
||||
|
||||
return iWriteMarker;
|
||||
}
|
||||
|
||||
#if 0 //embedded script handling not ready yet
|
||||
PLATFORM_INTERFACE size_t _CCallStackStatsGatherer_Write_FieldMergeScript( CallStackStatStructDescFuncs *pFieldDescriptions, CallStackStatStructDescFuncs::MergeScript_Language scriptMergeLanguage, uint8 *pWriteBuffer, size_t iWriteBufferSize )
|
||||
{
|
||||
Assert( scriptMergeLanguage == SSMSL_Squirrel ); //all we support so far
|
||||
|
||||
size_t iWriteMarker = 0;
|
||||
|
||||
*(uint32 *)(pWriteBuffer + iWriteMarker) = (uint32)SSDLID_EMBEDDEDSCRIPT; //at some point this should move to a more global location, for now we only support exporting the merge function and no other script code
|
||||
iWriteMarker += sizeof( uint32 );
|
||||
|
||||
//lump size, currently unknown, so just save a spot to write it later
|
||||
size_t iLumpSizeMarker = iWriteMarker;
|
||||
iWriteMarker += sizeof( uint32 );
|
||||
|
||||
*(uint8 *)(pWriteBuffer + iWriteMarker) = (uint8)scriptMergeLanguage;
|
||||
iWriteMarker += sizeof( uint8 );
|
||||
|
||||
|
||||
|
||||
//write out squirrel script
|
||||
iWriteMarker += _snprintf( (char *)pWriteBuffer + iWriteMarker, iWriteBufferSize - iWriteMarker, "function MergeStructs( mergeTo, mergeFrom )\n{\n" );
|
||||
CallStackStatStructDescFuncs *pDesc = pFieldDescriptions;
|
||||
while( pDesc != NULL )
|
||||
{
|
||||
iWriteMarker += pDesc->DescribeMergeOperation( scriptMergeLanguage, pWriteBuffer + iWriteMarker, iWriteBufferSize - iWriteMarker );
|
||||
if( iWriteMarker < (iWriteBufferSize - 2) )
|
||||
{
|
||||
pWriteBuffer[iWriteMarker] = '\n';
|
||||
++iWriteMarker;
|
||||
pWriteBuffer[iWriteMarker] = '\0';
|
||||
}
|
||||
pDesc = pDesc->m_pNext;
|
||||
}
|
||||
iWriteMarker += _snprintf( (char *)pWriteBuffer + iWriteMarker, iWriteBufferSize - iWriteMarker, "}\n" ) + 1;
|
||||
|
||||
|
||||
*(uint32 *)(pWriteBuffer + iLumpSizeMarker) = (uint32)(iWriteMarker - (iLumpSizeMarker + sizeof( uint32 )));
|
||||
|
||||
return iWriteMarker;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if 0 //embedded script handling not ready yet
|
||||
size_t BasicStatStructFieldDesc::DescribeMergeOperation( MergeScript_Language scriptLanguage, uint8 *pDescribeWriteBuffer, size_t iDescribeMaxLength )
|
||||
{
|
||||
Assert( scriptMergeLanguage == SSMSL_Squirrel ); //all we support so far
|
||||
|
||||
switch( m_Combine )
|
||||
{
|
||||
case BSSFCM_ADD:
|
||||
{
|
||||
return _snprintf( (char *)pDescribeWriteBuffer, iDescribeMaxLength, "mergeTo.%s += mergeFrom.%s\n", m_szFieldName, m_szFieldName );
|
||||
break;
|
||||
}
|
||||
|
||||
case BSSFCM_MAX:
|
||||
{
|
||||
return _snprintf( (char *)pDescribeWriteBuffer, iDescribeMaxLength, "if( mergeTo.%s < mergeFrom.%s ) mergeTo.%s = mergeFrom.%s\n", m_szFieldName, m_szFieldName, m_szFieldName, m_szFieldName );
|
||||
break;
|
||||
}
|
||||
|
||||
case BSSFCM_MIN:
|
||||
{
|
||||
return _snprintf( (char *)pDescribeWriteBuffer, iDescribeMaxLength, "if( mergeTo.%s > mergeFrom.%s ) mergeTo.%s = mergeFrom.%s\n", m_szFieldName, m_szFieldName, m_szFieldName, m_szFieldName );
|
||||
break;
|
||||
}
|
||||
|
||||
/*case BSSFCM_AND:
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BSSFCM_OR:
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BSSFCM_XOR:
|
||||
{
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BSSFCM_LIST: //add the values
|
||||
{
|
||||
Error( "BSSFCM_LIST is currently unsupported" );
|
||||
break;
|
||||
}*/
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool _CCallStackStatsGatherer_Internal_DumpStatsToFile( const char *szFileName, const CCallStackStatsGatherer_Standardized_t &StatsGatherer, bool bAllowMemoryAllocations )
|
||||
{
|
||||
if( !StatsGatherer.pGatherer )
|
||||
return false;
|
||||
|
||||
FILE *pFile = fopen(szFileName, "wb");
|
||||
if (!pFile)
|
||||
return false;
|
||||
|
||||
uint8 tempBuffer[256 * 1024]; //256kb
|
||||
size_t iWriteMarker = 0;
|
||||
|
||||
//File Header
|
||||
{
|
||||
//file format version
|
||||
uint8 version = 3;
|
||||
BufferedFwrite( pFile, tempBuffer, sizeof( tempBuffer ), &version, sizeof( uint8 ), iWriteMarker );
|
||||
|
||||
uint32 iEndian = 0x12345678;
|
||||
BufferedFwrite( pFile, tempBuffer, sizeof( tempBuffer ), &iEndian, sizeof( uint32 ), iWriteMarker );
|
||||
}
|
||||
|
||||
CCallStackStatsGatherer_DumpHelperVars_t helperVars;
|
||||
helperVars.pWriteBuffer = tempBuffer;
|
||||
helperVars.iWriteBufferSize = sizeof( tempBuffer );
|
||||
helperVars.iWriteMarker = &iWriteMarker;
|
||||
helperVars.pFile = pFile;
|
||||
helperVars.bAllowMemoryAllocations = bAllowMemoryAllocations;
|
||||
|
||||
bool bRetVal = _CCallStackStatsGatherer_Internal_DumpSubTree( StatsGatherer, &helperVars );
|
||||
|
||||
//flush any remnants of the write buffer
|
||||
if( iWriteMarker != 0 )
|
||||
{
|
||||
fwrite( tempBuffer, 1, iWriteMarker, pFile );
|
||||
iWriteMarker = 0;
|
||||
}
|
||||
|
||||
fclose( pFile );
|
||||
|
||||
return bRetVal;
|
||||
}
|
||||
#endif //#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
|
||||
|
||||
|
||||
size_t BasicStatStructFieldDesc::DescribeField( uint8 *pDescribeWriteBuffer, size_t iDescribeMaxLength )
|
||||
{
|
||||
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
//description file format
|
||||
//1 byte version
|
||||
//1 byte field type
|
||||
//1 byte combine method
|
||||
//4 byte field offset
|
||||
//null terminated string field name
|
||||
size_t iFieldNameStrLen = strlen( m_szFieldName ) + 1;
|
||||
const size_t iOutputLength = sizeof( uint8 ) + sizeof( uint8 ) + sizeof( uint8 ) + sizeof( uint32 ) + iFieldNameStrLen;
|
||||
if( iDescribeMaxLength < iOutputLength )
|
||||
return 0;
|
||||
|
||||
size_t iWriteOffset = 0;
|
||||
|
||||
//entry version
|
||||
*reinterpret_cast<uint8 *>(pDescribeWriteBuffer + iWriteOffset) = static_cast<uint8>(DFV_BasicStatStructFieldTypes_t);
|
||||
iWriteOffset += sizeof( uint8 );
|
||||
|
||||
//field type
|
||||
*reinterpret_cast<uint8 *>(pDescribeWriteBuffer + iWriteOffset) = static_cast<uint8>(m_Type);
|
||||
iWriteOffset += sizeof( uint8 );
|
||||
|
||||
//combine method
|
||||
*reinterpret_cast<uint8 *>(pDescribeWriteBuffer + iWriteOffset) = static_cast<uint8>(m_Combine);
|
||||
iWriteOffset += sizeof( uint8 );
|
||||
|
||||
//field offset
|
||||
*reinterpret_cast<uint32 *>(pDescribeWriteBuffer + iWriteOffset) = static_cast<uint32>(m_iFieldOffset);
|
||||
iWriteOffset += sizeof( uint32 );
|
||||
|
||||
//field name
|
||||
memcpy( pDescribeWriteBuffer + iWriteOffset, m_szFieldName, iFieldNameStrLen );
|
||||
iWriteOffset += iFieldNameStrLen;
|
||||
|
||||
Assert( iWriteOffset == iOutputLength );
|
||||
return iWriteOffset;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
1687
tier0/stacktools.cpp
Normal file
1687
tier0/stacktools.cpp
Normal file
File diff suppressed because it is too large
Load Diff
322
tier0/systeminformation.cpp
Normal file
322
tier0/systeminformation.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
#include "tier0/valve_off.h"
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include "tier0/valve_on.h"
|
||||
#endif
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/systeminformation.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PrivateType( xxx ) ValvePrivateType_##xxx
|
||||
|
||||
typedef enum { SystemPerformanceInformation = 2 }
|
||||
PrivateType( SYSTEM_INFORMATION_CLASS );
|
||||
|
||||
typedef LONG PrivateType( NTSTATUS );
|
||||
|
||||
typedef PrivateType( NTSTATUS ) ( WINAPI * PrivateType( NtQuerySystemInformation ) )
|
||||
(
|
||||
/*IN*/ PrivateType( SYSTEM_INFORMATION_CLASS ) SystemInformationClass,
|
||||
/*OUT*/ PVOID SystemInformation,
|
||||
/*IN*/ ULONG SystemInformationLength,
|
||||
/*OUT*/ PULONG ReturnLength /*OPTIONAL*/
|
||||
);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LARGE_INTEGER IdleProcessTime;
|
||||
LARGE_INTEGER IoTransferCount[3];
|
||||
ULONG IoOperationCount[3];
|
||||
ULONG AvailablePages;
|
||||
ULONG CommittedPages;
|
||||
ULONG CommitLimit;
|
||||
ULONG u00683;
|
||||
ULONG u00684;
|
||||
ULONG u00685;
|
||||
ULONG u00686;
|
||||
ULONG u00687;
|
||||
ULONG u00688;
|
||||
ULONG u00689;
|
||||
ULONG u00690;
|
||||
ULONG u00691;
|
||||
ULONG u00692;
|
||||
ULONG u00693;
|
||||
ULONG u00694;
|
||||
ULONG u00695;
|
||||
ULONG u00696;
|
||||
ULONG PagedPoolPages;
|
||||
ULONG NonPagedPoolPages;
|
||||
ULONG PagedPoolAllocs;
|
||||
ULONG PagedPoolFrees;
|
||||
ULONG NonPagedPoolAllocs;
|
||||
ULONG NonPagedPoolFrees;
|
||||
ULONG FreeSystemPtes;
|
||||
ULONG u00704;
|
||||
ULONG u00705;
|
||||
ULONG u00706;
|
||||
ULONG NonPagedPoolLookasideHits;
|
||||
ULONG PagedPoolLookasideHits;
|
||||
ULONG FreePagedPoolPages;
|
||||
ULONG u00710;
|
||||
ULONG u00711;
|
||||
ULONG u00712;
|
||||
ULONG uCounters[34];
|
||||
}
|
||||
PrivateType( SYSTEM_PERFORMANCE_INFORMATION );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Cached information about a dll proc
|
||||
//
|
||||
class CSysCallCacheEntry
|
||||
{
|
||||
public:
|
||||
CSysCallCacheEntry();
|
||||
~CSysCallCacheEntry();
|
||||
|
||||
public:
|
||||
bool IsInitialized() const;
|
||||
SYSTEM_CALL_RESULT_t CallResult() const;
|
||||
|
||||
SYSTEM_CALL_RESULT_t InitializeLoadModule( _TCHAR *pszModule, char *pszFunction );
|
||||
SYSTEM_CALL_RESULT_t InitializeFindModule( _TCHAR *pszModule, char *pszFunction );
|
||||
SYSTEM_CALL_RESULT_t InitializeFindProc( HMODULE hModule, char *pszFunction );
|
||||
|
||||
void SetFailed( SYSTEM_CALL_RESULT_t eResult );
|
||||
void Reset();
|
||||
|
||||
template < typename FN >
|
||||
FN GetFunction() const;
|
||||
|
||||
protected:
|
||||
SYSTEM_CALL_RESULT_t m_eResult;
|
||||
FARPROC m_pfnSysCall;
|
||||
HMODULE m_hModule;
|
||||
bool m_bInitialized;
|
||||
bool m_bFreeModule;
|
||||
};
|
||||
|
||||
struct CSysCallCacheEntry_LoadModule : public CSysCallCacheEntry
|
||||
{
|
||||
CSysCallCacheEntry_LoadModule( _TCHAR *pszModule, char *pszFunction ) { InitializeLoadModule( pszModule, pszFunction ); }
|
||||
};
|
||||
struct CSysCallCacheEntry_FindModule : public CSysCallCacheEntry
|
||||
{
|
||||
CSysCallCacheEntry_FindModule( _TCHAR *pszModule, char *pszFunction ) { InitializeFindModule( pszModule, pszFunction ); }
|
||||
};
|
||||
struct CSysCallCacheEntry_FindProc : public CSysCallCacheEntry
|
||||
{
|
||||
CSysCallCacheEntry_FindProc( HMODULE hModule, char *pszFunction ) { InitializeFindProc( hModule, pszFunction ); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
CSysCallCacheEntry::CSysCallCacheEntry() :
|
||||
m_eResult( SYSCALL_SUCCESS ),
|
||||
m_pfnSysCall( NULL ),
|
||||
m_hModule( NULL ),
|
||||
m_bInitialized( false ),
|
||||
m_bFreeModule( false )
|
||||
{
|
||||
NULL;
|
||||
}
|
||||
|
||||
CSysCallCacheEntry::~CSysCallCacheEntry()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool CSysCallCacheEntry::IsInitialized() const
|
||||
{
|
||||
return m_bInitialized;
|
||||
}
|
||||
|
||||
SYSTEM_CALL_RESULT_t CSysCallCacheEntry::CallResult() const
|
||||
{
|
||||
return m_eResult;
|
||||
}
|
||||
|
||||
SYSTEM_CALL_RESULT_t CSysCallCacheEntry::InitializeLoadModule( _TCHAR *pszModule, char *pszFunction )
|
||||
{
|
||||
m_bInitialized = true;
|
||||
|
||||
m_hModule = ::LoadLibrary( pszModule );
|
||||
m_bFreeModule = true;
|
||||
if ( !m_hModule )
|
||||
return m_eResult = SYSCALL_NODLL;
|
||||
|
||||
return InitializeFindProc( m_hModule, pszFunction );
|
||||
}
|
||||
|
||||
SYSTEM_CALL_RESULT_t CSysCallCacheEntry::InitializeFindModule( _TCHAR *pszModule, char *pszFunction )
|
||||
{
|
||||
m_bInitialized = true;
|
||||
|
||||
m_hModule = ::GetModuleHandle( pszModule );
|
||||
m_bFreeModule = false;
|
||||
if ( !m_hModule )
|
||||
return m_eResult = SYSCALL_NODLL;
|
||||
|
||||
return InitializeFindProc( m_hModule, pszFunction );
|
||||
}
|
||||
|
||||
SYSTEM_CALL_RESULT_t CSysCallCacheEntry::InitializeFindProc( HMODULE hModule, char *pszFunction )
|
||||
{
|
||||
m_bInitialized = true;
|
||||
|
||||
m_pfnSysCall = GetProcAddress( hModule, pszFunction );
|
||||
if ( !m_pfnSysCall )
|
||||
return m_eResult = SYSCALL_NOPROC;
|
||||
|
||||
return m_eResult = SYSCALL_SUCCESS;
|
||||
}
|
||||
|
||||
void CSysCallCacheEntry::Reset()
|
||||
{
|
||||
if ( m_bInitialized )
|
||||
{
|
||||
if ( m_bFreeModule && m_hModule )
|
||||
::FreeLibrary( m_hModule );
|
||||
m_eResult = SYSCALL_SUCCESS;
|
||||
m_hModule = NULL;
|
||||
m_pfnSysCall = NULL;
|
||||
m_bFreeModule = false;
|
||||
m_bInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CSysCallCacheEntry::SetFailed( SYSTEM_CALL_RESULT_t eResult )
|
||||
{
|
||||
m_eResult = eResult;
|
||||
}
|
||||
|
||||
template < typename FN >
|
||||
FN CSysCallCacheEntry::GetFunction() const
|
||||
{
|
||||
return reinterpret_cast< FN >( m_pfnSysCall );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Plat_GetMemPageSize
|
||||
// Returns the size of a memory page in bytes.
|
||||
//
|
||||
uint32 Plat_GetMemPageSize()
|
||||
{
|
||||
return 4; // On 32-bit systems memory page size is 4 Kb
|
||||
}
|
||||
|
||||
//
|
||||
// Plat_GetPagedPoolInfo
|
||||
// Fills in the paged pool info structure if successful.
|
||||
//
|
||||
SYSTEM_CALL_RESULT_t Plat_GetPagedPoolInfo( PAGED_POOL_INFO_t *pPPI )
|
||||
{
|
||||
memset( pPPI, 0, sizeof( *pPPI ) );
|
||||
|
||||
static CSysCallCacheEntry_FindModule qsi( _T( "ntdll.dll" ), "NtQuerySystemInformation" );
|
||||
|
||||
if ( qsi.CallResult() != SYSCALL_SUCCESS )
|
||||
return qsi.CallResult();
|
||||
|
||||
static bool s_bOsVersionValid = false;
|
||||
if ( !s_bOsVersionValid )
|
||||
{
|
||||
s_bOsVersionValid = true;
|
||||
OSVERSIONINFO osver;
|
||||
memset( &osver, 0, sizeof( osver ) );
|
||||
osver.dwOSVersionInfoSize = sizeof( osver );
|
||||
GetVersionEx( &osver );
|
||||
|
||||
// We should run it only on Windows XP or Windows 2003
|
||||
#define MAKEVER( high, low ) DWORD( MAKELONG( low, high ) )
|
||||
DWORD dwOsVer = MAKEVER( osver.dwMajorVersion, osver.dwMinorVersion );
|
||||
if ( dwOsVer < MAKEVER( 5, 1 ) || // Earlier than WinXP
|
||||
dwOsVer > MAKEVER( 5, 2 ) ) // Later than Win2003 (or 64-bit)
|
||||
{
|
||||
qsi.SetFailed( SYSCALL_UNSUPPORTED );
|
||||
}
|
||||
|
||||
// Don't care for 64-bit Windows
|
||||
CSysCallCacheEntry_FindModule wow64( _T( "kernel32.dll" ), "IsWow64Process" );
|
||||
if ( wow64.CallResult() == SYSCALL_SUCCESS )
|
||||
{
|
||||
typedef BOOL ( WINAPI * PFNWOW64 )( HANDLE, PBOOL );
|
||||
BOOL b64 = FALSE;
|
||||
if ( ( wow64.GetFunction< PFNWOW64 >() )( GetCurrentProcess(), &b64 ) &&
|
||||
b64 )
|
||||
{
|
||||
qsi.SetFailed( SYSCALL_UNSUPPORTED );
|
||||
}
|
||||
}
|
||||
|
||||
if ( qsi.CallResult() != SYSCALL_SUCCESS )
|
||||
return qsi.CallResult();
|
||||
}
|
||||
|
||||
// Invoke proc
|
||||
PrivateType( SYSTEM_PERFORMANCE_INFORMATION ) spi;
|
||||
ULONG ulLength = sizeof( spi );
|
||||
PrivateType( NTSTATUS ) lResult =
|
||||
( qsi.GetFunction< PrivateType( NtQuerySystemInformation ) >() )
|
||||
( SystemPerformanceInformation, &spi, ulLength, &ulLength );
|
||||
if ( lResult )
|
||||
return SYSCALL_FAILED;
|
||||
|
||||
// Return the result
|
||||
pPPI->numPagesUsed = spi.PagedPoolPages;
|
||||
pPPI->numPagesFree = spi.FreePagedPoolPages;
|
||||
return SYSCALL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
|
||||
//
|
||||
// Plat_GetMemPageSize
|
||||
// Returns the size of a memory page in bytes.
|
||||
//
|
||||
uint32 Plat_GetMemPageSize()
|
||||
{
|
||||
return 4; // Assume unknown page size is 4 Kb
|
||||
}
|
||||
|
||||
//
|
||||
// Plat_GetPagedPoolInfo
|
||||
// Fills in the paged pool info structure if successful.
|
||||
//
|
||||
SYSTEM_CALL_RESULT_t Plat_GetPagedPoolInfo( PAGED_POOL_INFO_t *pPPI )
|
||||
{
|
||||
memset( pPPI, 0, sizeof( *pPPI ) );
|
||||
return SYSCALL_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
3241
tier0/threadtools.cpp
Normal file
3241
tier0/threadtools.cpp
Normal file
File diff suppressed because it is too large
Load Diff
193
tier0/tier0.inc
Normal file
193
tier0/tier0.inc
Normal file
@@ -0,0 +1,193 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// TIER0.INC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "Source Files" [$WINDOWS||$X360||$PS3||$POSIX]
|
||||
{
|
||||
-$File "$SRCDIR\public\tier0\memoverride.cpp" [!$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "../unitlib/unitlib.cpp"
|
||||
$File "assert_dialog.cpp"
|
||||
$File "assert_dialog.rc" [$WIN32||$WIN64]
|
||||
$File "commandline.cpp"
|
||||
$File "cpu.cpp"
|
||||
$File "cpumonitoring.cpp"
|
||||
$File "cputopology.cpp"
|
||||
$File "cpu_posix.cpp" [$POSIX]
|
||||
$File "dbg.cpp"
|
||||
$File "etwprof.cpp" [$WINDOWS]
|
||||
$File "fasttimer.cpp"
|
||||
$File "logging.cpp"
|
||||
$File "mem.cpp"
|
||||
$File "mem_helpers.cpp"
|
||||
$File "memdbg.cpp"
|
||||
$File "memprocessheap.cpp" [$WINDOWS]
|
||||
$File "memvirt.cpp"
|
||||
$File "memstd.cpp"
|
||||
$File "memvalidate.cpp"
|
||||
$File "dynfunction.cpp"
|
||||
$File "minidump.cpp"
|
||||
$File "tier0_strtools.cpp"
|
||||
$File "miniprofiler.cpp"
|
||||
$File "perfstats.cpp"
|
||||
$File "platform.cpp" [$WINDOWS||$X360||$PS3]
|
||||
$File "platform_posix.cpp" [$POSIX]
|
||||
$File "platform_independent.cpp"
|
||||
$File "platwindow.cpp"
|
||||
$File "pmc360.cpp" [$X360]
|
||||
$File "pme.cpp" [$WIN32||$WIN64]
|
||||
$File "pme_posix.cpp" [$PS3||$POSIX]
|
||||
$File "PMELib.cpp" [$WIN32||$WIN64||$PS3]
|
||||
$File "progressbar.cpp"
|
||||
$File "prx.cpp" [$PS3]
|
||||
$File "prxexport.cpp" [$PS3]
|
||||
$File "security.cpp"
|
||||
$File "stackstats.cpp"
|
||||
$File "stacktools.cpp"
|
||||
$File "systeminformation.cpp"
|
||||
$File "threadtools.cpp"
|
||||
$File "tslist.cpp"
|
||||
$File "vatoms.cpp"
|
||||
$File "vprof.cpp"
|
||||
$File "vtuneinterface.cpp"
|
||||
$File "win32consoleio.cpp"
|
||||
|
||||
$Folder "dlmalloc"
|
||||
{
|
||||
$File "dlmalloc\malloc.cpp" [$WINDOWS||$X360||$PS3]
|
||||
$File "dlmalloc\malloc-2.8.3.h"
|
||||
}
|
||||
|
||||
$File "xbox/xbox_system.cpp" [$X360||$PS3]
|
||||
$File "../tier1/pathmatch.cpp" [$LINUXALL]
|
||||
|
||||
}
|
||||
|
||||
$folder "Header Files"
|
||||
{
|
||||
$File "$SRCDIR\public\tier0\basetypes.h"
|
||||
$File "$SRCDIR\public\tier0\commonmacros.h"
|
||||
$File "$SRCDIR\public\tier0\cache_hints.h"
|
||||
$File "$SRCDIR\public\tier0\cpumonitoring.h"
|
||||
$File "cputopology.h"
|
||||
$File "$SRCDIR\public\tier0\dbg.h"
|
||||
$File "$SRCDIR\public\tier0\dbgflag.h"
|
||||
$File "$SRCDIR\public\tier0\etwprof.h"
|
||||
$File "$SRCDIR\public\tier0\EventMasks.h"
|
||||
$File "$SRCDIR\public\tier0\EventModes.h"
|
||||
$File "$SRCDIR\public\tier0\fasttimer.h"
|
||||
$File "$SRCDIR\public\tier0\ia32detect.h"
|
||||
$File "$SRCDIR\public\tier0\icommandline.h"
|
||||
$File "$SRCDIR\public\tier0\IOCTLCodes.h"
|
||||
$File "$SRCDIR\public\tier0\K8PerformanceCounters.h"
|
||||
$File "$SRCDIR\public\tier0\l2cache.h"
|
||||
$File "$SRCDIR\public\tier0\pmc360.h" [$X360]
|
||||
$File "$SRCDIR\public\tier0\logging.h"
|
||||
$File "$SRCDIR\public\tier0\mem.h"
|
||||
$File "mem_helpers.h"
|
||||
$File "mem_impl_type.h"
|
||||
$File "$SRCDIR\public\tier0\memalloc.h"
|
||||
$File "$SRCDIR\public\tier0\memdbgoff.h"
|
||||
$File "$SRCDIR\public\tier0\memdbgon.h"
|
||||
$File "$SRCDIR\public\tier0\memoverride_ps3.h" [$PS3]
|
||||
$File "memstd.h"
|
||||
$File "$SRCDIR\public\tier0\memvirt.h"
|
||||
$File "$SRCDIR\public\tier0\minidump.h"
|
||||
$File "$SRCDIR\public\tier0\miniprofiler.h"
|
||||
$File "$SRCDIR\public\tier0\microprofiler.h"
|
||||
$File "$SRCDIR\public\tier0\P4PerformanceCounters.h"
|
||||
$File "$SRCDIR\public\tier0\P5P6PerformanceCounters.h"
|
||||
$File "pch_tier0.h"
|
||||
$File "tier0_strtools.h"
|
||||
$File "$SRCDIR\public\tier0\perfstats.h"
|
||||
$File "$SRCDIR\public\tier0\platform.h"
|
||||
$File "$SRCDIR\public\tier0\platform_override.h" [$PS3]
|
||||
$File "$SRCDIR\public\tier0\platwindow.h"
|
||||
$File "$SRCDIR\public\tier0\PMELib.h"
|
||||
$File "$SRCDIR\public\tier0\progressbar.h"
|
||||
$File "$SRCDIR\public\tier0\protected_things.h"
|
||||
$File "resource.h"
|
||||
$File "$SRCDIR\public\tier0\stackstats.h"
|
||||
$File "$SRCDIR\public\tier0\stacktools.h"
|
||||
$File "$SRCDIR\public\tier0\systeminformation.h"
|
||||
$File "$SRCDIR\public\tier0\threadtools.h"
|
||||
$File "$SRCDIR\public\tier0\threadtools.inl"
|
||||
$File "$SRCDIR\public\tier0\tslist.h"
|
||||
$File "$SRCDIR\public\tier0\validator.h"
|
||||
$File "$SRCDIR\public\tier0\valobject.h"
|
||||
$File "$SRCDIR\public\tier0\valve_off.h"
|
||||
$File "$SRCDIR\public\tier0\valve_on.h"
|
||||
$File "$SRCDIR\public\tier0\vatoms.h"
|
||||
$File "$SRCDIR\public\tier0\vprof.h"
|
||||
$File "$SRCDIR\public\tier0\vtuneinterface.h"
|
||||
$File "$SRCDIR\public\tier0\vprof_sn.h"
|
||||
$File "$SRCDIR\public\tier0\vprof_telemetry.h"
|
||||
$File "$SRCDIR\public\tier0\wchartypes.h"
|
||||
$File "$SRCDIR\public\tier0\win32consoleio.h"
|
||||
$File "$SRCDIR\public\tier0\xbox_codeline_defines.h"
|
||||
$File "$SRCDIR\public\tier0\dynfunction.h"
|
||||
}
|
||||
|
||||
$Folder "DESKey" [$WIN32||$WIN64]
|
||||
{
|
||||
$File "DESKey\ALGO.H"
|
||||
$File "DESKey\ALGO32.LIB"
|
||||
$File "DESKey\DK2WIN32.H"
|
||||
$File "DESKey\DK2WIN32.LIB"
|
||||
}
|
||||
|
||||
$Folder "Xbox" [$X360]
|
||||
{
|
||||
$folder "Source Files"
|
||||
{
|
||||
$File "xbox\xbox_console.cpp"
|
||||
$File "xbox\xbox_system.cpp"
|
||||
$File "xbox\xbox_win32stubs.cpp"
|
||||
}
|
||||
$folder "Header Files"
|
||||
{
|
||||
$File "$SRCDIR\common\xbox\xbox_console.h"
|
||||
$File "$SRCDIR\common\xbox\xbox_core.h"
|
||||
$File "$SRCDIR\common\xbox\xbox_win32stubs.h"
|
||||
}
|
||||
}
|
||||
|
||||
$Folder "PS3 debugger" [$PS3]
|
||||
{
|
||||
$folder "Source Files"
|
||||
{
|
||||
$File "ps3\ps3_console.cpp"
|
||||
}
|
||||
$folder "Header Files"
|
||||
{
|
||||
$File "$SRCDIR\common\ps3\ps3_console.h"
|
||||
$File "$SRCDIR\common\ps3\ps3_win32stubs.h"
|
||||
}
|
||||
}
|
||||
|
||||
$Folder "Manifest Files" [$WINDOWS]
|
||||
{
|
||||
$File "valveetwprovider.man"
|
||||
{
|
||||
$Configuration
|
||||
{
|
||||
$CustomBuildStep
|
||||
{
|
||||
$CommandLine "mc.exe -um $(InputFilename) -z $(InputName)Events"
|
||||
$Description "Compiling ETW manifest file"
|
||||
$Outputs "$(InputName)Events.h;$(InputName)Events.rc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
190
tier0/tier0.vpc
Normal file
190
tier0/tier0.vpc
Normal file
@@ -0,0 +1,190 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// TIER0.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
$Macro NOSCHEMACOMPILER "1"
|
||||
$Conditional VTUNE_ENABLED 0
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// VTune defines
|
||||
$Macro VTUNE_DIR "$SRCDIR\thirdparty\vtune"
|
||||
$Macro VTUNE_INCLUDE "$VTUNE_DIR\include"
|
||||
$Macro VTUNE_LIB "$VTUNE_DIR\lib32\libittnotify.lib"
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$General [$X360]
|
||||
{
|
||||
// X360 version publishes to some other directory then copies here so we need to tell VPC to track this
|
||||
// or else it won't know what depends on this project.
|
||||
$AdditionalOutputFiles "$LIBPUBLIC\$(TargetName).lib"
|
||||
}
|
||||
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;TIER0_DLL_EXPORT;CROSS_PLATFORM_VERSION=1;THREAD_MUTEX_TRACING_ENABLED"[$WINDOWS||$X360]
|
||||
$PreprocessorDefinitions "$BASE;TIER0_DLL_EXPORT;CROSS_PLATFORM_VERSION=1;POSIX" [$PS3||$POSIX]
|
||||
$PreprocessorDefinitions "$BASE;TIER0_FPO_DISABLED" [$NOFPO]
|
||||
}
|
||||
|
||||
$Compiler [$WINDOWS]
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE;$VTUNE_INCLUDE" [$VTUNE_ENABLED]
|
||||
}
|
||||
$Linker [$WINDOWS]
|
||||
{
|
||||
$AdditionalDependencies "$BASE;$VTUNE_LIB" [$VTUNE_ENABLED]
|
||||
}
|
||||
$Compiler [$PS3]
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE;"$(SN_PS3_PATH)/ppu/include""
|
||||
$PreprocessorDefinitions "$BASE;PLATFORM_OVERRIDE_TIER0"
|
||||
}
|
||||
$SNCCompiler [$PS3]
|
||||
{
|
||||
$ForceIncludes "platform_override.h"
|
||||
}
|
||||
|
||||
$Linker [$X360]
|
||||
{
|
||||
// 360 publishes the import library via a post build step
|
||||
$ImportLibrary "$(TargetDir)\$(TargetName).lib"
|
||||
|
||||
// 360 will auto generate a def file for this import library
|
||||
$ModuleDefinitionFile " "
|
||||
$AdditionalOptions "$BASE /AUTODEF:xbox\xbox.def"
|
||||
}
|
||||
|
||||
$Linker [!$X360]
|
||||
{
|
||||
// Everyone but 360 build right to the location.
|
||||
$ImportLibrary "$LIBPUBLIC\$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT"
|
||||
}
|
||||
|
||||
$Linker [$PS3]
|
||||
{
|
||||
$AdditionalDependencies "$BASE "$(SN_PS3_PATH)/ppu/lib/sn/libsn.a" "$(SN_PS3_PATH)/ppu/lib/sn/libsntuner.a""
|
||||
}
|
||||
|
||||
$Linker
|
||||
{
|
||||
$AdditionalDependencies "$BASE ws2_32.lib" [$WINDOWS]
|
||||
}
|
||||
|
||||
$PreLinkEvent [!$POSIX]
|
||||
{
|
||||
$CommandLine "call $SRCDIR\vpc_scripts\valve_p4_edit.cmd $LIBPUBLIC\$(TargetName).lib $SRCDIR" "\n" \
|
||||
"$BASE"
|
||||
}
|
||||
|
||||
$PreLinkEvent [$X360]
|
||||
{
|
||||
// Run a pre-link event to clean the .def file from the last link
|
||||
$CommandLine "if exist xbox\xbox.def del xbox\xbox.def" "\n" \
|
||||
"$BASE"
|
||||
}
|
||||
|
||||
$PreLinkEvent [$PS3]
|
||||
{
|
||||
$CommandLine "$BASE"
|
||||
$Description "NOTE - If PRX linking fails, make sure your tier0_staticlib is building in same solution configuration (debug/release) as tier0 prx."
|
||||
}
|
||||
|
||||
$PostBuildEvent [$X360]
|
||||
{
|
||||
// Publish the import lib
|
||||
$CommandLine "if exist $(TargetDir)$(TargetName).lib copy $(TargetDir)$(TargetName).lib $LIBPUBLIC\$(TargetName).lib" "\n" \
|
||||
"$BASE"
|
||||
}
|
||||
|
||||
$PostBuildEvent [$PS3]
|
||||
{
|
||||
// Publish the import lib
|
||||
$CommandLine "if exist $(TargetName)_stub.a move $(TargetName)_stub.a $LIBPUBLIC\$(TargetName).lib" "\n" \
|
||||
"if exist $(TargetName)_verlog.txt del $(TargetName)_verlog.txt" "\n" \
|
||||
"$BASE" "\n"
|
||||
}
|
||||
|
||||
$General [$POSIX]
|
||||
{
|
||||
$GameOutputFile "$OUTBINDIR/$_IMPLIB_PREFIX$OUTBINNAME$_DLL_EXT"
|
||||
}
|
||||
|
||||
$General [$PS3]
|
||||
{
|
||||
$AdditionalProjectDependencies "$BASE;tier0_staticlib"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$PreBuildEvent [$PS3]
|
||||
{
|
||||
// Clear potentially stale verlog files
|
||||
$CommandLine "ppu-lv2-prx-exportpickup -o Release_PS3/prxexport.inl Release_tier0staticlib_PS3/prx.obj Release_tier0staticlib_PS3/threadtools.obj Release_tier0staticlib_PS3/vprof.obj Release_tier0staticlib_PS3/dbg.obj Release_tier0staticlib_PS3/logging.obj" "\n" \
|
||||
"if exist tier0_rel_verlog.txt del tier0_rel_verlog.txt" "\n" \
|
||||
"$BASE" "\n"
|
||||
}
|
||||
|
||||
$Linker [$PS3]
|
||||
{
|
||||
$AdditionalDependencies "$BASE "Release_tier0staticlib_PS3/tier0_staticlib_ps3.lib""
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$PreBuildEvent [$PS3]
|
||||
{
|
||||
// Clear potentially stale verlog files
|
||||
$CommandLine "ppu-lv2-prx-exportpickup -o Debug_PS3/prxexport.inl Debug_tier0staticlib_PS3/prx.obj Debug_tier0staticlib_PS3/threadtools.obj Debug_tier0staticlib_PS3/vprof.obj Debug_tier0staticlib_PS3/dbg.obj Debug_tier0staticlib_PS3/logging.obj" "\n" \
|
||||
"if exist tier0_dbg_verlog.txt del tier0_dbg_verlog.txt" "\n" \
|
||||
"$BASE" "\n"
|
||||
}
|
||||
|
||||
$Linker [$PS3]
|
||||
{
|
||||
$AdditionalDependencies "$BASE "Debug_tier0staticlib_PS3/tier0_staticlib_ps3.lib""
|
||||
}
|
||||
}
|
||||
|
||||
$Include "tier0.inc"
|
||||
|
||||
$Project
|
||||
{
|
||||
|
||||
$Folder "PS3 Files" [$PS3]
|
||||
{
|
||||
$File "prxexport.cpp"
|
||||
}
|
||||
|
||||
$Folder "Source Files" [$PS3]
|
||||
{
|
||||
-$File "$SRCDIR\common\ps3\prx.cpp"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
-$ImpLib "$LIBPUBLIC\tier0"
|
||||
-$Lib "$LIBPUBLIC\tier1"
|
||||
-$implib "$LIBPUBLIC\vstdlib"
|
||||
-$Lib "$LIBPUBLIC\interfaces"
|
||||
|
||||
$LibExternal "$SRCDIR\thirdparty\telemetry\lib\telemetry32.link" [$WIN32 && !$RAD_TELEMETRY_DISABLED]
|
||||
$LibExternal "$SRCDIR\thirdparty\telemetry\lib\telemetry64.link" [$WIN64 && !$RAD_TELEMETRY_DISABLED]
|
||||
$LibExternal "$SRCDIR/thirdparty/telemetry/lib/libtelemetryx86.link" [$LINUX32 && !$RAD_TELEMETRY_DISABLED]
|
||||
$LibExternal "$SRCDIR/thirdparty/telemetry/lib/libtelemetryx64.link" [$LINUX64 && !$RAD_TELEMETRY_DISABLED]
|
||||
}
|
||||
}
|
||||
|
||||
$Project "tier0"
|
||||
{
|
||||
}
|
||||
13
tier0/tier0.vpc.vpc_cache
Normal file
13
tier0/tier0.vpc.vpc_cache
Normal file
@@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "tier0.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "tier0.vcxproj"
|
||||
"1" "tier0.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
||||
18
tier0/tier0_exclude.vpc
Normal file
18
tier0/tier0_exclude.vpc
Normal file
@@ -0,0 +1,18 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// tier0_s_exclude.vpc
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
-$File "$SRCDIR\lib\$PLATFORM\$_IMPLIB_PREFIXtier0$_IMPLIB_EXT" [!$WINDOWS]
|
||||
-$File "$SRCDIR\lib\public\$_IMPLIB_PREFIXtier0$_IMPLIB_EXT" [$WINDOWS]
|
||||
}
|
||||
$Folder "Source Files"
|
||||
{
|
||||
-$File "$SRCDIR\public\tier0\memoverride.cpp"
|
||||
}
|
||||
}
|
||||
69
tier0/tier0_staticlib.vpc
Normal file
69
tier0/tier0_staticlib.vpc
Normal file
@@ -0,0 +1,69 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// TIER0_staticlib.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR ".."
|
||||
$Macro OUTLIBNAME "tier0_staticlib"
|
||||
$Macro NOSCHEMACOMPILER "1"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$PreprocessorDefinitions "$BASE;TIER0_DLL_EXPORT;CROSS_PLATFORM_VERSION=1"[$WINDOWS||$X360||$PS3||$POSIX]
|
||||
$PreprocessorDefinitions "$BASE;POSIX" [$PS3||$POSIX]
|
||||
$PreprocessorDefinitions "$BASE;TIER0_FPO_DISABLED" [$NOFPO]
|
||||
}
|
||||
$Compiler [$PS3]
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE;"$(SN_PS3_PATH)/ppu/include""
|
||||
$PreprocessorDefinitions "$BASE;PLATFORM_OVERRIDE_TIER0"
|
||||
}
|
||||
$SNCCompiler [$PS3]
|
||||
{
|
||||
$ForceIncludes "platform_override.h"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$Configuration "Release"
|
||||
{
|
||||
$Librarian
|
||||
{
|
||||
$OutputFile "Release_tier0staticlib_PS3\$OUTLIBNAME_ps3.lib"
|
||||
}
|
||||
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Release_tier0staticlib_PS3" [$PS3]
|
||||
$IntermediateDirectory ".\Release_tier0staticlib_PS3" [$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Configuration "Debug"
|
||||
{
|
||||
$Librarian
|
||||
{
|
||||
$OutputFile "Debug_tier0staticlib_PS3\$OUTLIBNAME_ps3.lib"
|
||||
}
|
||||
|
||||
$General
|
||||
{
|
||||
$OutputDirectory ".\Debug_tier0staticlib_PS3" [$PS3]
|
||||
$IntermediateDirectory ".\Debug_tier0staticlib_PS3" [$PS3]
|
||||
}
|
||||
}
|
||||
|
||||
$Include "tier0.inc"
|
||||
|
||||
$Project "tier0_staticlib"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
-$File "prxexport.cpp" [$PS3]
|
||||
}
|
||||
}
|
||||
149
tier0/tier0_strtools.cpp
Normal file
149
tier0/tier0_strtools.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
//========= Copyright © Valve Corporation, All rights reserved. ============//
|
||||
#include "pch_tier0.h"
|
||||
|
||||
// This function is marked in the VPC file as always being optimized, even in debug
|
||||
// builds, because this gives a ~7% speedup on debug builds because this function is
|
||||
// used by the debug allocator.
|
||||
|
||||
#define TOLOWERC( x ) (( ( x >= 'A' ) && ( x <= 'Z' ) )?( x + 32 ) : x )
|
||||
|
||||
#if !defined( STATIC_LINK )
|
||||
#define FDECL extern "C"
|
||||
#else
|
||||
#define FDECL
|
||||
#endif
|
||||
|
||||
FDECL int V_tier0_stricmp(const char *s1, const char *s2 )
|
||||
{
|
||||
// A string is always equal to itself. This optimization is
|
||||
// surprisingly valuable.
|
||||
if ( s1 == s2 )
|
||||
return 0;
|
||||
|
||||
uint8 const *pS1 = ( uint8 const * ) s1;
|
||||
uint8 const *pS2 = ( uint8 const * ) s2;
|
||||
for(;;)
|
||||
{
|
||||
int c1 = *( pS1++ );
|
||||
int c2 = *( pS2++ );
|
||||
if ( c1 == c2 )
|
||||
{
|
||||
if ( !c1 ) return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! c2 )
|
||||
{
|
||||
return c1 - c2;
|
||||
}
|
||||
c1 = TOLOWERC( c1 );
|
||||
c2 = TOLOWERC( c2 );
|
||||
if ( c1 != c2 )
|
||||
{
|
||||
return c1 - c2;
|
||||
}
|
||||
}
|
||||
c1 = *( pS1++ );
|
||||
c2 = *( pS2++ );
|
||||
if ( c1 == c2 )
|
||||
{
|
||||
if ( !c1 ) return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! c2 )
|
||||
{
|
||||
return c1 - c2;
|
||||
}
|
||||
c1 = TOLOWERC( c1 );
|
||||
c2 = TOLOWERC( c2 );
|
||||
if ( c1 != c2 )
|
||||
{
|
||||
return c1 - c2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FDECL void V_tier0_strncpy( char *a, const char *b, int n )
|
||||
{
|
||||
Assert( n >= sizeof( *a ) );
|
||||
|
||||
// NOTE: Never never use strncpy! Here's what it actually does, which is not what we want!
|
||||
|
||||
// (from MSDN)
|
||||
// The strncpy function copies the initial count characters of strSource to strDest
|
||||
// and returns strDest. If count is less than or equal to the length of strSource,
|
||||
// a null character is not appended automatically to the copied string. If count
|
||||
// is greater than the length of strSource, the destination string is padded with
|
||||
// null characters up to length count. The behavior of strncpy is undefined
|
||||
// if the source and destination strings overlap.
|
||||
// strncpy( pDest, pSrc, maxLen );
|
||||
|
||||
char *pLast = a + n - 1;
|
||||
while ( (a < pLast) && (*b != 0) )
|
||||
{
|
||||
*a = *b;
|
||||
++a; ++b;
|
||||
}
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
FDECL char *V_tier0_strncat( char *pDest, const char *pSrc, int destBufferSize, int max_chars_to_copy )
|
||||
{
|
||||
int charstocopy = 0;
|
||||
|
||||
Assert( destBufferSize >= 0 );
|
||||
|
||||
int len = (int)strlen(pDest);
|
||||
int srclen = (int)strlen( pSrc );
|
||||
if ( max_chars_to_copy <= -1 )
|
||||
{
|
||||
charstocopy = srclen;
|
||||
}
|
||||
else
|
||||
{
|
||||
charstocopy = Min( max_chars_to_copy, (int)srclen );
|
||||
}
|
||||
|
||||
if ( len + charstocopy >= destBufferSize )
|
||||
{
|
||||
charstocopy = destBufferSize - len - 1;
|
||||
}
|
||||
|
||||
// charstocopy can end up negative if you fill a buffer and then pass in a smaller
|
||||
// buffer size. Yes, this actually happens.
|
||||
// Cast to ptrdiff_t is necessary in order to check for negative (size_t is unsigned)
|
||||
if ( charstocopy <= 0 )
|
||||
{
|
||||
return pDest;
|
||||
}
|
||||
|
||||
ANALYZE_SUPPRESS( 6059 ); // warning C6059: : Incorrect length parameter in call to 'strncat'. Pass the number of remaining characters, not the buffer size of 'argument 1'
|
||||
char *pOut = strncat( pDest, pSrc, charstocopy );
|
||||
return pOut;
|
||||
}
|
||||
|
||||
FDECL int V_tier0_vsnprintf( char *a, int n, const char *f, va_list l )
|
||||
{
|
||||
int len = _vsnprintf( a, n, f, l );
|
||||
|
||||
if ( ( len < 0 ) ||
|
||||
( n > 0 && len >= n ) )
|
||||
{
|
||||
len = n - 1;
|
||||
a[n - 1] = 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
FDECL int V_tier0_snprintf( char *a, int n, const char *f, ... )
|
||||
{
|
||||
va_list l;
|
||||
|
||||
va_start( l, f );
|
||||
int len = V_tier0_vsnprintf( a, n, f, l );
|
||||
va_end( l );
|
||||
return len;
|
||||
}
|
||||
3
tier0/tier0_strtools.h
Normal file
3
tier0/tier0_strtools.h
Normal file
@@ -0,0 +1,3 @@
|
||||
//========= Copyright © Valve Corporation, All rights reserved. ============//
|
||||
|
||||
extern "C" int V_tier0_stricmp(const char *s1, const char *s2 );
|
||||
570
tier0/tslist.cpp
Normal file
570
tier0/tslist.cpp
Normal file
@@ -0,0 +1,570 @@
|
||||
//========== Copyright © 2007, Valve Corporation, All rights reserved. ========
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/tslist.h"
|
||||
#if defined( _X360 )
|
||||
#include "xbox/xbox_win32stubs.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "tier0/threadtools.h" // for rand()
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
extern ThreadHandle_t * CreateTestThreads( ThreadFunc_t fnThread, int numThreads, int nProcessorsToDistribute );
|
||||
extern void JoinTestThreads( ThreadHandle_t *pHandles );
|
||||
|
||||
namespace TSListTests
|
||||
{
|
||||
int NUM_TEST = 10000;
|
||||
int NUM_THREADS;
|
||||
int MAX_THREADS = 8;
|
||||
int NUM_PROCESSORS = 1;
|
||||
|
||||
CInterlockedInt g_nTested;
|
||||
CInterlockedInt g_nThreads;
|
||||
CInterlockedInt g_nPushThreads;
|
||||
CInterlockedInt g_nPopThreads;
|
||||
CInterlockedInt g_nPushes;
|
||||
CInterlockedInt g_nPops;
|
||||
CTSQueue<int, true> g_TestQueue;
|
||||
CTSList<int> g_TestList;
|
||||
volatile bool g_bStart;
|
||||
|
||||
int *g_pTestBuckets;
|
||||
|
||||
CTSListBase g_Test;
|
||||
TSLNodeBase_t **g_nodes;
|
||||
int idx = 0;
|
||||
|
||||
const char *g_pListType;
|
||||
|
||||
class CTestOps
|
||||
{
|
||||
public:
|
||||
virtual void Push( int item ) = 0;
|
||||
virtual bool Pop( int *pResult ) = 0;
|
||||
virtual bool Validate() { return true; }
|
||||
virtual bool IsEmpty() = 0;
|
||||
};
|
||||
|
||||
bool g_bUseMutex = false;
|
||||
CThreadConditionalMutex< CThreadFastMutex, &g_bUseMutex > g_TestLock;
|
||||
|
||||
class CQueueOps : public CTestOps
|
||||
{
|
||||
void Push( int item )
|
||||
{
|
||||
g_TestLock.Lock();
|
||||
g_TestQueue.PushItem( item );
|
||||
g_TestLock.Unlock();
|
||||
g_nPushes++;
|
||||
}
|
||||
bool Pop( int *pResult )
|
||||
{
|
||||
g_TestLock.Lock();
|
||||
if ( g_TestQueue.PopItem( pResult ) )
|
||||
{
|
||||
g_TestLock.Unlock();
|
||||
g_nPops++;
|
||||
return true;
|
||||
}
|
||||
g_TestLock.Unlock();
|
||||
return false;
|
||||
}
|
||||
bool Validate()
|
||||
{
|
||||
return true; //g_TestQueue.Validate();
|
||||
}
|
||||
bool IsEmpty()
|
||||
{
|
||||
return ( g_TestQueue.Count() == 0 );
|
||||
}
|
||||
} g_QueueOps;
|
||||
|
||||
class CListOps : public CTestOps
|
||||
{
|
||||
void Push( int item )
|
||||
{
|
||||
g_TestLock.Lock();
|
||||
g_TestList.PushItem( item );
|
||||
g_nPushes++;
|
||||
}
|
||||
bool Pop( int *pResult )
|
||||
{
|
||||
g_TestLock.Lock();
|
||||
if ( g_TestList.PopItem( pResult ) )
|
||||
{
|
||||
g_TestLock.Unlock();
|
||||
g_nPops++;
|
||||
return true;
|
||||
}
|
||||
g_TestLock.Unlock();
|
||||
return false;
|
||||
}
|
||||
bool Validate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool IsEmpty()
|
||||
{
|
||||
return ( g_TestList.Count() == 0 );
|
||||
}
|
||||
} g_ListOps;
|
||||
|
||||
CTestOps *g_pTestOps;
|
||||
|
||||
void ClearBuckets()
|
||||
{
|
||||
memset( g_pTestBuckets, 0, sizeof(int) * NUM_TEST );
|
||||
}
|
||||
|
||||
void IncBucket( int i )
|
||||
{
|
||||
if ( i < NUM_TEST ) // tests can slop over a bit
|
||||
{
|
||||
ThreadInterlockedIncrement( &g_pTestBuckets[i] );
|
||||
}
|
||||
}
|
||||
|
||||
void DecBucket( int i )
|
||||
{
|
||||
if ( i < NUM_TEST ) // tests can slop over a bit
|
||||
{
|
||||
ThreadInterlockedDecrement( &g_pTestBuckets[i] );
|
||||
}
|
||||
}
|
||||
|
||||
void ValidateBuckets()
|
||||
{
|
||||
for ( int i = 0; i < NUM_TEST; i++ )
|
||||
{
|
||||
if ( g_pTestBuckets[i] != 0 )
|
||||
{
|
||||
Msg( "Test bucket %d has an invalid value %d\n", i, g_pTestBuckets[i] );
|
||||
DebuggerBreakIfDebugging();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uintp PopThreadFunc( void *)
|
||||
{
|
||||
//ThreadSetDebugName( "PopThread" );
|
||||
g_nPopThreads++;
|
||||
g_nThreads++;
|
||||
while ( !g_bStart )
|
||||
{
|
||||
ThreadSleep( 1 );
|
||||
}
|
||||
int ignored;
|
||||
for (;;)
|
||||
{
|
||||
if ( !g_pTestOps->Pop( &ignored ) )
|
||||
{
|
||||
ThreadPause();
|
||||
ThreadSleep(0);
|
||||
if ( g_nPushThreads == 0 )
|
||||
{
|
||||
// Pop the rest
|
||||
while ( g_pTestOps->Pop( &ignored ) )
|
||||
{
|
||||
ThreadPause();
|
||||
ThreadSleep( 0 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_nThreads--;
|
||||
g_nPopThreads--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintp PushThreadFunc( void * )
|
||||
{
|
||||
//ThreadSetDebugName( "PushThread" );
|
||||
g_nPushThreads++;
|
||||
g_nThreads++;
|
||||
while ( !g_bStart )
|
||||
{
|
||||
ThreadPause();
|
||||
ThreadSleep( 0 );
|
||||
}
|
||||
|
||||
while ( ++g_nTested <= NUM_TEST )
|
||||
{
|
||||
g_pTestOps->Push( g_nTested );
|
||||
}
|
||||
g_nThreads--;
|
||||
g_nPushThreads--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TestStart()
|
||||
{
|
||||
g_nTested = 0;
|
||||
g_nThreads = 0;
|
||||
g_nPushThreads = 0;
|
||||
g_nPopThreads = 0;
|
||||
g_bStart = false;
|
||||
g_nPops = g_nPushes = 0;
|
||||
ClearBuckets();
|
||||
}
|
||||
|
||||
void TestWait()
|
||||
{
|
||||
while ( g_nThreads < NUM_THREADS )
|
||||
{
|
||||
ThreadSleep( 0 );
|
||||
}
|
||||
g_bStart = true;
|
||||
while ( g_nThreads > 0 )
|
||||
{
|
||||
ThreadSleep( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
void TestEnd( bool bExpectEmpty = true )
|
||||
{
|
||||
ValidateBuckets();
|
||||
|
||||
if ( g_nPops != g_nPushes )
|
||||
{
|
||||
Msg( "FAIL: Not all items popped\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( g_pTestOps->Validate() )
|
||||
{
|
||||
if ( !bExpectEmpty || g_pTestOps->IsEmpty() )
|
||||
{
|
||||
Msg("pass\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg("FAIL: !IsEmpty()\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg("FAIL: !Validate()\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------
|
||||
//
|
||||
// Shared Tests for CTSQueue and CTSList
|
||||
//
|
||||
//--------------------------------------------------
|
||||
void PushPopTest()
|
||||
{
|
||||
Msg( "%s test: single thread push/pop, in order... ", g_pListType );
|
||||
ClearBuckets();
|
||||
g_nTested = 0;
|
||||
int value;
|
||||
while ( g_nTested < NUM_TEST )
|
||||
{
|
||||
value = g_nTested++;
|
||||
g_pTestOps->Push( value );
|
||||
IncBucket( value );
|
||||
}
|
||||
|
||||
g_pTestOps->Validate();
|
||||
|
||||
while ( g_pTestOps->Pop( &value ) )
|
||||
{
|
||||
DecBucket( value );
|
||||
}
|
||||
TestEnd();
|
||||
}
|
||||
|
||||
void PushPopInterleavedTestGuts()
|
||||
{
|
||||
int value;
|
||||
for (;;)
|
||||
{
|
||||
bool bPush = ( rand() % 2 == 0 );
|
||||
if ( bPush && ( value = g_nTested++ ) < NUM_TEST )
|
||||
{
|
||||
g_pTestOps->Push( value );
|
||||
IncBucket( value );
|
||||
}
|
||||
else if ( g_pTestOps->Pop( &value ) )
|
||||
{
|
||||
DecBucket( value );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( g_nTested >= NUM_TEST )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PushPopInterleavedTest()
|
||||
{
|
||||
Msg( "%s test: single thread push/pop, interleaved... ", g_pListType );
|
||||
srand( Plat_MSTime() );
|
||||
g_nTested = 0;
|
||||
ClearBuckets();
|
||||
PushPopInterleavedTestGuts();
|
||||
TestEnd();
|
||||
}
|
||||
|
||||
uintp PushPopInterleavedTestThreadFunc( void * )
|
||||
{
|
||||
ThreadSetDebugName( "PushPopThread" );
|
||||
g_nThreads++;
|
||||
while ( !g_bStart )
|
||||
{
|
||||
ThreadSleep( 0 );
|
||||
}
|
||||
PushPopInterleavedTestGuts();
|
||||
g_nThreads--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void STPushMTPop( bool bDistribute )
|
||||
{
|
||||
Msg( "%s test: single thread push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
|
||||
TestStart();
|
||||
ThreadHandle_t hPush = CreateSimpleThread( &PushThreadFunc, NULL );
|
||||
ThreadHandle_t *arrPops = CreateTestThreads( PopThreadFunc, NUM_THREADS - 1, ( bDistribute ) ? NUM_PROCESSORS : 0 );
|
||||
|
||||
TestWait();
|
||||
TestEnd();
|
||||
JoinTestThreads( arrPops );
|
||||
ThreadJoin( hPush );
|
||||
ReleaseThreadHandle( hPush );
|
||||
}
|
||||
|
||||
void MTPushSTPop( bool bDistribute )
|
||||
{
|
||||
Msg( "%s test: multithread push, single thread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
|
||||
TestStart();
|
||||
ThreadHandle_t hPop = CreateSimpleThread( &PopThreadFunc, NULL );
|
||||
ThreadHandle_t* arrPushes = CreateTestThreads( PushThreadFunc, NUM_THREADS - 1, ( bDistribute ) ? NUM_PROCESSORS : 0 );
|
||||
|
||||
TestWait();
|
||||
TestEnd();
|
||||
JoinTestThreads( arrPushes );
|
||||
ThreadJoin( hPop );
|
||||
ReleaseThreadHandle( hPop );
|
||||
}
|
||||
|
||||
|
||||
void MTPushMTPop( bool bDistribute )
|
||||
{
|
||||
Msg( "%s test: multithread push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
|
||||
TestStart();
|
||||
int ct = 0;
|
||||
ThreadHandle_t *threadHandles = (ThreadHandle_t *)stackalloc( NUM_THREADS * sizeof(ThreadHandle_t) );
|
||||
int nHandles = 0;
|
||||
|
||||
for ( int i = 0; i < NUM_THREADS / 2 ; i++ )
|
||||
{
|
||||
ThreadHandle_t hThread = CreateSimpleThread( &PopThreadFunc, NULL );
|
||||
threadHandles[nHandles++] = hThread;
|
||||
if ( bDistribute )
|
||||
{
|
||||
int32 mask = 1 << (ct++ % NUM_PROCESSORS);
|
||||
ThreadSetAffinity( hThread, mask );
|
||||
}
|
||||
}
|
||||
for ( int i = 0; i < NUM_THREADS / 2 ; i++ )
|
||||
{
|
||||
ThreadHandle_t hThread = CreateSimpleThread( &PushThreadFunc, NULL );
|
||||
threadHandles[nHandles++] = hThread;
|
||||
if ( bDistribute )
|
||||
{
|
||||
int32 mask = 1 << (ct++ % NUM_PROCESSORS);
|
||||
ThreadSetAffinity( hThread, mask );
|
||||
}
|
||||
}
|
||||
|
||||
TestWait();
|
||||
TestEnd();
|
||||
|
||||
for ( int i = 0; i < nHandles; i++ )
|
||||
{
|
||||
ReleaseThreadHandle( threadHandles[i] );
|
||||
}
|
||||
}
|
||||
|
||||
void MTPushPopPopInterleaved( bool bDistribute )
|
||||
{
|
||||
Msg( "%s test: multithread interleaved push/pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
|
||||
srand( Plat_MSTime() );
|
||||
TestStart();
|
||||
ThreadHandle_t * arrPushPops = CreateTestThreads( &PushPopInterleavedTestThreadFunc, NUM_THREADS, ( bDistribute ) ? NUM_PROCESSORS : 0 );
|
||||
TestWait();
|
||||
TestEnd();
|
||||
JoinTestThreads( arrPushPops );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MTPushSeqPop( bool bDistribute )
|
||||
{
|
||||
Msg( "%s test: multithread push, sequential pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
|
||||
TestStart();
|
||||
ThreadHandle_t * arrPushes = CreateTestThreads( PushThreadFunc, NUM_THREADS, ( bDistribute ) ? NUM_PROCESSORS : 0 );
|
||||
|
||||
TestWait();
|
||||
int ignored;
|
||||
g_pTestOps->Validate();
|
||||
int nPopped = 0;
|
||||
while ( g_pTestOps->Pop( &ignored ) )
|
||||
{
|
||||
nPopped++;
|
||||
}
|
||||
if ( nPopped != NUM_TEST )
|
||||
{
|
||||
Msg( "Pops != pushes?\n" );
|
||||
DebuggerBreakIfDebugging();
|
||||
}
|
||||
TestEnd();
|
||||
|
||||
JoinTestThreads( arrPushes );
|
||||
}
|
||||
|
||||
|
||||
void SeqPushMTPop( bool bDistribute )
|
||||
{
|
||||
Msg( "%s test: sequential push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
|
||||
TestStart();
|
||||
while ( g_nTested++ < NUM_TEST )
|
||||
{
|
||||
g_pTestOps->Push( g_nTested );
|
||||
}
|
||||
|
||||
ThreadHandle_t * arrPops = CreateTestThreads( PopThreadFunc, NUM_THREADS, ( bDistribute ) ? NUM_PROCESSORS : 0 );
|
||||
|
||||
TestWait();
|
||||
TestEnd();
|
||||
|
||||
JoinTestThreads( arrPops );
|
||||
}
|
||||
|
||||
|
||||
#ifdef _PS3
|
||||
void TestThreadProc( uint64_t id )
|
||||
{
|
||||
printf( "(TS)Hello from PPU thread %lld @%p\n", id, &id );
|
||||
sys_ppu_thread_exit( id );
|
||||
}
|
||||
uintp TestThreadProc2( void *p )
|
||||
{
|
||||
printf( "(TS)Hello from PPU thread %lld @%p\n", (int64)p, &p );
|
||||
return (uintp)p;
|
||||
}
|
||||
#endif
|
||||
|
||||
void TestThreads()
|
||||
{
|
||||
#ifdef _PS3
|
||||
printf("(TS)testing threads\n");
|
||||
const int numThreads = 40;
|
||||
ThreadHandle_t * arrTests = CreateTestThreads( TestThreadProc2, numThreads, false );
|
||||
JoinTestThreads( arrTests );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void RunSharedTests( int nTests )
|
||||
{
|
||||
using namespace TSListTests;
|
||||
TestThreads();
|
||||
const CPUInformation &pi = GetCPUInformation();
|
||||
NUM_PROCESSORS = pi.m_nLogicalProcessors;
|
||||
MAX_THREADS = NUM_PROCESSORS * 2;
|
||||
g_pTestBuckets = new int[NUM_TEST];
|
||||
while ( nTests-- )
|
||||
{
|
||||
for ( NUM_THREADS = 2; NUM_THREADS <= MAX_THREADS; NUM_THREADS *= 2)
|
||||
{
|
||||
Msg( "\nTesting %d threads:\n", NUM_THREADS );
|
||||
PushPopTest();
|
||||
PushPopInterleavedTest();
|
||||
SeqPushMTPop( false );
|
||||
STPushMTPop( false );
|
||||
MTPushSeqPop( false );
|
||||
MTPushSTPop( false );
|
||||
MTPushMTPop( false );
|
||||
MTPushPopPopInterleaved( false );
|
||||
if ( NUM_PROCESSORS > 1 )
|
||||
{
|
||||
SeqPushMTPop( true );
|
||||
STPushMTPop( true );
|
||||
MTPushSeqPop( true );
|
||||
MTPushSTPop( true );
|
||||
MTPushMTPop( true );
|
||||
MTPushPopPopInterleaved( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] g_pTestBuckets;
|
||||
}
|
||||
|
||||
bool RunTSListTests( int nListSize, int nTests )
|
||||
{
|
||||
using namespace TSListTests;
|
||||
NUM_TEST = nListSize;
|
||||
|
||||
#ifdef USE_NATIVE_SLIST
|
||||
|
||||
#ifdef _WIN64
|
||||
int maxSize = 65536; // FIXME: How should this be computed?
|
||||
#else
|
||||
int maxSize = ( 1 << (sizeof( ((TSLHead_t *)(0))->Depth ) * 8) ) - 1;
|
||||
#endif
|
||||
|
||||
#else
|
||||
int maxSize = ( 1 << (sizeof( ((TSLHead_t *)(0))->value.Depth ) * 8) ) - 1;
|
||||
#endif
|
||||
if ( NUM_TEST > maxSize )
|
||||
{
|
||||
Msg( "TSList cannot hold more that %d nodes\n", maxSize );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
g_pTestOps = &g_ListOps;
|
||||
g_pListType = "CTSList";
|
||||
|
||||
RunSharedTests( nTests );
|
||||
|
||||
Msg("Tests done, purging test memory..." );
|
||||
g_TestList.Purge();
|
||||
Msg( "done\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RunTSQueueTests( int nListSize, int nTests )
|
||||
{
|
||||
using namespace TSListTests;
|
||||
NUM_TEST = nListSize;
|
||||
|
||||
g_pTestOps = &g_QueueOps;
|
||||
g_pListType = "CTSQueue";
|
||||
|
||||
RunSharedTests( nTests );
|
||||
|
||||
Msg("Tests done, purging test memory..." );
|
||||
g_TestQueue.Purge();
|
||||
Msg( "done\n");
|
||||
return true;
|
||||
}
|
||||
270
tier0/validator.cpp
Normal file
270
tier0/validator.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#include "tier0/memblockhdr.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
|
||||
// we use malloc & free internally in our validation code; turn off the deprecation #defines
|
||||
#undef malloc
|
||||
#undef free
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CValidator::CValidator( )
|
||||
{
|
||||
m_pValObjectFirst = NULL;
|
||||
m_pValObjectLast = NULL;
|
||||
m_pValObjectCur = NULL;
|
||||
m_cpvOwned = 0;
|
||||
m_bMemLeaks = false;
|
||||
|
||||
// Mark all memory blocks as unclaimed, prior to starting the validation process
|
||||
CMemBlockHdr *pMemBlockHdr = CMemBlockHdr::PMemBlockHdrFirst( );
|
||||
pMemBlockHdr = pMemBlockHdr->PMemBlockHdrNext( ); // Head is just a placeholder
|
||||
while ( NULL != pMemBlockHdr )
|
||||
{
|
||||
pMemBlockHdr->SetBClaimed( false );
|
||||
|
||||
pMemBlockHdr = pMemBlockHdr->PMemBlockHdrNext( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CValidator::~CValidator( )
|
||||
{
|
||||
CValObject *pValObject = m_pValObjectFirst;
|
||||
CValObject *pValObjectNext;
|
||||
while ( NULL != pValObject )
|
||||
{
|
||||
pValObjectNext = pValObject->PValObjectNext( );
|
||||
Destruct<CValObject> (pValObject);
|
||||
free( pValObject );
|
||||
pValObject = pValObjectNext;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Call this each time you start a new Validate() function. It creates
|
||||
// a new CValObject to track the caller.
|
||||
// Input: pchType - The caller's type (typically a class name)
|
||||
// pvObj - The caller (typically an object pointer)
|
||||
// pchName - The caller's individual name (typically a member var of another class)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidator::Push( tchar *pchType, void *pvObj, tchar *pchName )
|
||||
{
|
||||
// Create a new ValObject and add it to the linked list
|
||||
|
||||
CValObject *pValObjectNew = (CValObject *) malloc( sizeof (CValObject ) );
|
||||
Construct<CValObject> (pValObjectNew);
|
||||
pValObjectNew->Init( pchType, pvObj, pchName, m_pValObjectCur, m_pValObjectLast );
|
||||
m_pValObjectLast = pValObjectNew;
|
||||
if ( NULL == m_pValObjectFirst )
|
||||
m_pValObjectFirst = pValObjectNew;
|
||||
|
||||
// Make this the current object
|
||||
m_pValObjectCur = pValObjectNew;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Call this each time you end a Validate() function. It decrements
|
||||
// our current structure depth.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidator::Pop( )
|
||||
{
|
||||
Assert( NULL != m_pValObjectCur );
|
||||
m_pValObjectCur = m_pValObjectCur->PValObjectParent( );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Call this to register each memory block you own.
|
||||
// Input: pvMem - Memory block you own
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidator::ClaimMemory( void *pvMem )
|
||||
{
|
||||
if ( NULL == pvMem )
|
||||
return;
|
||||
|
||||
// Mark the block as owned
|
||||
CMemBlockHdr *pMemBlockHdr = CMemBlockHdr::PMemBlockHdrFromPvUser( pvMem );
|
||||
pMemBlockHdr->CheckValid( );
|
||||
Assert( !pMemBlockHdr->BClaimed( ) );
|
||||
pMemBlockHdr->SetBClaimed( true );
|
||||
|
||||
// Let the current object know about it
|
||||
Assert( NULL != m_pValObjectCur );
|
||||
m_pValObjectCur->ClaimMemoryBlock( pvMem );
|
||||
|
||||
// Update our counter
|
||||
m_cpvOwned++;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: We're done enumerating our objects. Perform any final calculations.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidator::Finalize( void )
|
||||
{
|
||||
// Count our memory leaks
|
||||
CMemBlockHdr *pMemBlockHdr = CMemBlockHdr::PMemBlockHdrFirst( );
|
||||
pMemBlockHdr = pMemBlockHdr->PMemBlockHdrNext( );
|
||||
m_cpubLeaked = 0;
|
||||
m_cubLeaked = 0;
|
||||
while ( NULL != pMemBlockHdr )
|
||||
{
|
||||
if ( !pMemBlockHdr->BClaimed( ) )
|
||||
{
|
||||
m_cpubLeaked++;
|
||||
m_cubLeaked += pMemBlockHdr->CubUser( );
|
||||
m_bMemLeaks = true;
|
||||
}
|
||||
|
||||
pMemBlockHdr = pMemBlockHdr->PMemBlockHdrNext( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Render all reported objects to the console
|
||||
// Input: cubThreshold - Only render object whose children have at least
|
||||
// cubThreshold bytes allocated
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidator::RenderObjects( int cubThreshold )
|
||||
{
|
||||
// Walk our object list and render them all to the console
|
||||
CValObject *pValObject = m_pValObjectFirst;
|
||||
while ( NULL != pValObject )
|
||||
{
|
||||
if ( pValObject->CubMemTree( ) >= cubThreshold )
|
||||
{
|
||||
for ( int ich = 0; ich < pValObject->NLevel( ); ich++ )
|
||||
ConMsg( 2, _T(" ") );
|
||||
|
||||
ConMsg( 2, _T("%s at 0x%x--> %d blocks = %d bytes\n"),
|
||||
pValObject->PchType( ), pValObject->PvObj( ), pValObject->CpubMemTree( ),
|
||||
pValObject->CubMemTree( ) );
|
||||
}
|
||||
|
||||
pValObject = pValObject->PValObjectNext( );
|
||||
}
|
||||
|
||||
|
||||
// Dump a summary to the console
|
||||
ConMsg( 2, _T("Allocated:\t%d blocks\t%d bytes\n"), CpubAllocated( ), CubAllocated( ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Render any discovered memory leaks to the console
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidator::RenderLeaks( void )
|
||||
{
|
||||
if ( m_bMemLeaks )
|
||||
ConMsg( 1, _T("\n") );
|
||||
|
||||
// Render any leaked blocks to the console
|
||||
CMemBlockHdr *pMemBlockHdr = CMemBlockHdr::PMemBlockHdrFirst( );
|
||||
pMemBlockHdr = pMemBlockHdr->PMemBlockHdrNext( );
|
||||
while ( NULL != pMemBlockHdr )
|
||||
{
|
||||
if ( !pMemBlockHdr->BClaimed( ) )
|
||||
{
|
||||
ConMsg( 1, _T("Leaked mem block: Addr = 0x%x\tSize = %d\n"),
|
||||
pMemBlockHdr->PvUser( ), pMemBlockHdr->CubUser( ) );
|
||||
ConMsg( 1, _T("\tAlloc = %s, line %d\n"),
|
||||
pMemBlockHdr->PchFile( ), pMemBlockHdr->NLine( ) );
|
||||
}
|
||||
|
||||
pMemBlockHdr = pMemBlockHdr->PMemBlockHdrNext( );
|
||||
}
|
||||
|
||||
// Dump a summary to the console
|
||||
if ( 0 != m_cpubLeaked )
|
||||
ConMsg( 1, _T("!!!Leaked:\t%d blocks\t%d bytes\n"), m_cpubLeaked, m_cubLeaked );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Find the validator object associated with the given real object.
|
||||
//-----------------------------------------------------------------------------
|
||||
CValObject *CValidator::FindObject( void * pvObj )
|
||||
{
|
||||
CValObject *pValObject = m_pValObjectFirst;
|
||||
CValObject *pValObjectNext;
|
||||
while ( NULL != pValObject )
|
||||
{
|
||||
pValObjectNext = pValObject->PValObjectNext( );
|
||||
if( pvObj == pValObject->PvObj() )
|
||||
return pValObject;
|
||||
|
||||
pValObject = pValObjectNext;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Diff one CValidator against another. Each Validator object is
|
||||
// tagged with whether it is new since the last snapshot or not.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidator::DiffAgainst( CValidator *pOtherValidator ) // Removes any entries from this validator that are also present in the other.
|
||||
{
|
||||
// Render any leaked blocks to the console
|
||||
CValObject *pValObject = m_pValObjectFirst;
|
||||
CValObject *pValObjectNext;
|
||||
while ( NULL != pValObject )
|
||||
{
|
||||
pValObjectNext = pValObject->PValObjectNext( );
|
||||
pValObject->SetBNewSinceSnapshot( pOtherValidator->FindObject( pValObject->PvObj() ) == NULL );
|
||||
|
||||
if( pValObject->BNewSinceSnapshot() && pValObject->CubMemTree( ) )
|
||||
{
|
||||
for ( int ich = 0; ich < pValObject->NLevel( ); ich++ )
|
||||
ConMsg( 2, _T(" ") );
|
||||
|
||||
ConMsg( 2, _T("%s at 0x%x--> %d blocks = %d bytes\n"),
|
||||
pValObject->PchType( ), pValObject->PvObj( ), pValObject->CpubMemTree( ),
|
||||
pValObject->CubMemTree( ) );
|
||||
}
|
||||
|
||||
pValObject = pValObjectNext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CValidator::Validate( CValidator &validator, tchar *pchName )
|
||||
{
|
||||
validator.Push( _T("CValidator"), this, pchName );
|
||||
|
||||
validator.ClaimMemory( this );
|
||||
|
||||
// Render any leaked blocks to the console
|
||||
CValObject *pValObject = m_pValObjectFirst;
|
||||
CValObject *pValObjectNext;
|
||||
while ( NULL != pValObject )
|
||||
{
|
||||
pValObjectNext = pValObject->PValObjectNext( );
|
||||
validator.ClaimMemory( pValObject );
|
||||
pValObject = pValObjectNext;
|
||||
}
|
||||
|
||||
validator.Pop();
|
||||
}
|
||||
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
124
tier0/valobject.cpp
Normal file
124
tier0/valobject.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "vstdlib/pch_vstdlib.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Initializer
|
||||
// Input: pchType - Type of the object we represent.
|
||||
// WARNING: pchType must be a static (since we keep a copy of it around for a while)
|
||||
// pvObj - Pointer to the object we represent
|
||||
// pchName - Name of the individual object we represent
|
||||
// WARNING: pchName must be a static (since we keep a copy of it around for a while)
|
||||
// pValObjectparent- Our parent object (ie, the object that our object is a member of)
|
||||
// pValObjectPrev - Object that precedes us in the linked list (we're
|
||||
// always added to the end)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValObject::Init( tchar *pchType, void *pvObj, tchar *pchName,
|
||||
CValObject *pValObjectParent, CValObject *pValObjectPrev )
|
||||
{
|
||||
m_nUser = 0;
|
||||
|
||||
// Initialize pchType:
|
||||
if ( NULL != pchType )
|
||||
{
|
||||
Q_strncpy( m_rgchType, pchType, (int) ( sizeof(m_rgchType) / sizeof(*m_rgchType) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rgchType[0] = '\0';
|
||||
}
|
||||
|
||||
m_pvObj = pvObj;
|
||||
|
||||
// Initialize pchName:
|
||||
if ( NULL != pchName )
|
||||
{
|
||||
Q_strncpy( m_rgchName, pchName, sizeof(m_rgchName) / sizeof(*m_rgchName) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rgchName[0] = NULL;
|
||||
}
|
||||
|
||||
m_pValObjectParent = pValObjectParent;
|
||||
|
||||
if ( NULL == pValObjectParent )
|
||||
m_nLevel = 0;
|
||||
else
|
||||
m_nLevel = pValObjectParent->NLevel( ) + 1;
|
||||
|
||||
m_cpubMemSelf = 0;
|
||||
m_cubMemSelf = 0;
|
||||
m_cpubMemTree = 0;
|
||||
m_cubMemTree = 0;
|
||||
|
||||
// Insert us at the back of the linked list
|
||||
if ( NULL != pValObjectPrev )
|
||||
{
|
||||
Assert( NULL == pValObjectPrev->m_pValObjectNext );
|
||||
pValObjectPrev->m_pValObjectNext = this;
|
||||
}
|
||||
m_pValObjectNext = NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Destructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CValObject::~CValObject( )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: The object we represent has claimed direct ownership of a block of
|
||||
// memory. Record that we own it.
|
||||
// Input: pvMem - Address of the memory block
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValObject::ClaimMemoryBlock( void *pvMem )
|
||||
{
|
||||
// Get the memory block header
|
||||
CMemBlockHdr *pMemBlockHdr = CMemBlockHdr::PMemBlockHdrFromPvUser( pvMem );
|
||||
pMemBlockHdr->CheckValid( );
|
||||
|
||||
// Update our counters
|
||||
m_cpubMemSelf++;
|
||||
m_cubMemSelf+= pMemBlockHdr->CubUser( );
|
||||
m_cpubMemTree++;
|
||||
m_cubMemTree+= pMemBlockHdr->CubUser( );
|
||||
|
||||
// If we have a parent object, let it know about the memory (it'll recursively call up the tree)
|
||||
if ( NULL != m_pValObjectParent )
|
||||
m_pValObjectParent->ClaimChildMemoryBlock( pMemBlockHdr->CubUser( ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A child of ours has claimed ownership of a memory block. Make
|
||||
// a note of it, and pass the message back up the tree.
|
||||
// Input: cubUser - Size of the memory block
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValObject::ClaimChildMemoryBlock( int cubUser )
|
||||
{
|
||||
m_cpubMemTree++;
|
||||
m_cubMemTree += cubUser;
|
||||
|
||||
if ( NULL != m_pValObjectParent )
|
||||
m_pValObjectParent->ClaimChildMemoryBlock( cubUser );
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
288
tier0/valveetwprovider.man
Normal file
288
tier0/valveetwprovider.man
Normal file
@@ -0,0 +1,288 @@
|
||||
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
|
||||
<instrumentationManifest xmlns="http://schemas.microsoft.com/win/2004/08/events">
|
||||
<instrumentation
|
||||
xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<events xmlns="http://schemas.microsoft.com/win/2004/08/events">
|
||||
<!-- Main provider to emit low to medium frequency information about game performance.
|
||||
This includes Begin/End pairs, markers, and labeled thread IDs.
|
||||
Documentation on ETW is available at:
|
||||
http://msdn.microsoft.com/en-us/magazine/cc163437.aspx
|
||||
http://blogs.msdn.com/b/ryanmy/archive/2005/05/27/422772.aspx
|
||||
http://msdn.microsoft.com/en-us/library/aa363668(VS.85).aspx
|
||||
|
||||
Documentation of the data types available for event payload templates is here:
|
||||
http://msdn.microsoft.com/en-us/library/aa382774(v=VS.85).aspx
|
||||
|
||||
See also the Windows Platform SDK SimpleProvider sample and the Scenario
|
||||
libary at http://archive.msdn.microsoft.com/Scenario
|
||||
|
||||
Before recording traces you need to execute these commands to register the provider:
|
||||
xcopy /y %vgame%\bin\tier0.dll %temp%
|
||||
wevtutil um %vgame%\..\src\tier0\valveetwprovider.man
|
||||
wevtutil im %vgame%\..\src\tier0\valveetwprovider.man
|
||||
-->
|
||||
<provider
|
||||
guid="{3fa9201e-73b0-43fe-9821-7e145359bc6f}"
|
||||
name="Valve-Main"
|
||||
symbol="VALVE_MAIN"
|
||||
messageFileName="%temp%\tier0.dll"
|
||||
resourceFileName="%temp%\tier0.dll" >
|
||||
<templates>
|
||||
<template tid="T_Start">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Int32" name="Depth" />
|
||||
</template>
|
||||
<template tid="T_End">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Int32" name="Depth" />
|
||||
<data inType="win:Float" name="Duration (ms)" />
|
||||
</template>
|
||||
<template tid="T_Mark">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
</template>
|
||||
<template tid="T_Mark1F">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Float" name="Data 1" />
|
||||
</template>
|
||||
<template tid="T_Mark2F">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Float" name="Data 1" />
|
||||
<data inType="win:Float" name="Data 2" />
|
||||
</template>
|
||||
<template tid="T_Mark3F">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Float" name="Data 1" />
|
||||
<data inType="win:Float" name="Data 2" />
|
||||
<data inType="win:Float" name="Data 3" />
|
||||
</template>
|
||||
<template tid="T_Mark4F">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Float" name="Data 1" />
|
||||
<data inType="win:Float" name="Data 2" />
|
||||
<data inType="win:Float" name="Data 3" />
|
||||
<data inType="win:Float" name="Data 4" />
|
||||
</template>
|
||||
<template tid="T_Mark1I">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Int32" name="Data 1" />
|
||||
</template>
|
||||
<template tid="T_Mark2I">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Int32" name="Data 1" />
|
||||
<data inType="win:Int32" name="Data 2" />
|
||||
</template>
|
||||
<template tid="T_Mark3I">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Int32" name="Data 1" />
|
||||
<data inType="win:Int32" name="Data 2" />
|
||||
<data inType="win:Int32" name="Data 3" />
|
||||
</template>
|
||||
<template tid="T_Mark4I">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:Int32" name="Data 1" />
|
||||
<data inType="win:Int32" name="Data 2" />
|
||||
<data inType="win:Int32" name="Data 3" />
|
||||
<data inType="win:Int32" name="Data 4" />
|
||||
</template>
|
||||
<template tid="T_Mark1S">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:AnsiString" name="Data 1" />
|
||||
</template>
|
||||
<template tid="T_Mark2S">
|
||||
<data inType="win:AnsiString" name="Description" />
|
||||
<data inType="win:AnsiString" name="Data 1" />
|
||||
<data inType="win:AnsiString" name="Data 2" />
|
||||
</template>
|
||||
<template tid="T_ThreadID">
|
||||
<data inType="win:Int32" name="ThreadID" />
|
||||
<data inType="win:AnsiString" name="ThreadName" />
|
||||
</template>
|
||||
</templates>
|
||||
<events>
|
||||
<event symbol="Start" template="T_Start" value="100" task="Block" opcode="Begin" />
|
||||
<event symbol="Stop" template="T_End" value="101" task="Block" opcode="End" />
|
||||
<event symbol="Mark" template="T_Mark" value="102" task="Block" opcode="Mark" />
|
||||
<event symbol="Mark1F" template="T_Mark1F" value="103" task="Block" opcode="Mark1F" />
|
||||
<event symbol="Mark2F" template="T_Mark2F" value="104" task="Block" opcode="Mark2F" />
|
||||
<event symbol="Mark3F" template="T_Mark3F" value="105" task="Block" opcode="Mark3F" />
|
||||
<event symbol="Mark4F" template="T_Mark4F" value="106" task="Block" opcode="Mark4F" />
|
||||
<event symbol="Mark1I" template="T_Mark1I" value="107" task="Block" opcode="Mark1I" />
|
||||
<event symbol="Mark2I" template="T_Mark2I" value="108" task="Block" opcode="Mark2I" />
|
||||
<event symbol="Mark3I" template="T_Mark3I" value="109" task="Block" opcode="Mark3I" />
|
||||
<event symbol="Mark4I" template="T_Mark4I" value="110" task="Block" opcode="Mark4I" />
|
||||
<event symbol="Mark1S" template="T_Mark1S" value="111" task="Block" opcode="Mark1S" />
|
||||
<event symbol="Mark2S" template="T_Mark2S" value="112" task="Block" opcode="Mark2S" />
|
||||
<event level="win:Informational" symbol="Thread_ID" template="T_ThreadID" value="113" task="ThreadID" opcode="Information" />
|
||||
</events>
|
||||
<opcodes>
|
||||
<opcode name="Begin" symbol="_BeginOpcode" value="10"/>
|
||||
<opcode name="End" symbol="_EndOpcode" value="11"/>
|
||||
<opcode name="Step" symbol="_StepOpcode" value="12"/>
|
||||
<opcode name="Mark" symbol="_MarkOpcode" value="13"/>
|
||||
<opcode name="Mark1F" symbol="_MarkOpcode1F" value="14"/>
|
||||
<opcode name="Mark2F" symbol="_MarkOpcode2F" value="15"/>
|
||||
<opcode name="Mark3F" symbol="_MarkOpcode3F" value="16"/>
|
||||
<opcode name="Mark4F" symbol="_MarkOpcode4F" value="17"/>
|
||||
<opcode name="Mark1I" symbol="_MarkOpcode1I" value="18"/>
|
||||
<opcode name="Mark2I" symbol="_MarkOpcode2I" value="19"/>
|
||||
<opcode name="Mark3I" symbol="_MarkOpcode3I" value="20"/>
|
||||
<opcode name="Mark4I" symbol="_MarkOpcode4I" value="21"/>
|
||||
<opcode name="Mark1S" symbol="_MarkOpcode1S" value="22"/>
|
||||
<opcode name="Mark2S" symbol="_MarkOpcode2S" value="23"/>
|
||||
<opcode name="Information" symbol="_InformationOpcode" value="24"/>
|
||||
</opcodes>
|
||||
<tasks>
|
||||
<task name="Block" symbol="Block_Task" value="1" eventGUID="{F15F363A-49FD-4de3-967C-1732464945FF}"/>
|
||||
<task name="ThreadID" symbol="ThreadID_Task" value="2" eventGUID="{F15F363A-493D-4dea-967C-1123464945FF}"/>
|
||||
</tasks>
|
||||
</provider>
|
||||
|
||||
<!-- Additional provider, to emit high frequency information about game performance, mainly frame events. -->
|
||||
<provider
|
||||
guid="{47a9201e-73b0-42ce-9821-7e134361bc6f}"
|
||||
name="Valve-FrameRate"
|
||||
symbol="VALVE_FRAMERATE"
|
||||
messageFileName="%temp%\tier0.dll"
|
||||
resourceFileName="%temp%\tier0.dll"
|
||||
>
|
||||
<templates>
|
||||
<template tid="T_FrameMark">
|
||||
<data inType="win:Int32" name="Frame number" />
|
||||
<data inType="win:Float" name="Duration (ms)" />
|
||||
</template>
|
||||
</templates>
|
||||
<events>
|
||||
<event symbol="RenderFrameMark" template="T_FrameMark" value="200" task="Frame" opcode="RenderFrameMark" />
|
||||
<event symbol="SimFrameMark" template="T_FrameMark" value="201" task="Frame" opcode="SimFrameMark" />
|
||||
</events>
|
||||
<opcodes>
|
||||
<opcode name="RenderFrameMark" symbol="_RenderFrameMarkOpcode" value="10"/>
|
||||
<opcode name="SimFrameMark" symbol="_SimFrameMarkOpcode" value="11"/>
|
||||
</opcodes>
|
||||
<tasks>
|
||||
<task name="Frame" symbol="Frame_Task" value="1" eventGUID="{F15F363A-49FD-4FFa-967C-1739364945FF}"/>
|
||||
</tasks>
|
||||
</provider>
|
||||
|
||||
<!-- Additional provider, to emit high frequency information about server performance. -->
|
||||
<provider
|
||||
guid="{58a9201e-73b0-42ce-9821-7e134361bc70}"
|
||||
name="Valve-ServerFrameRate"
|
||||
symbol="VALVE_SERVERFRAMERATE"
|
||||
messageFileName="%temp%\tier0.dll"
|
||||
resourceFileName="%temp%\tier0.dll"
|
||||
>
|
||||
<templates>
|
||||
<template tid="T_FrameMark">
|
||||
<data inType="win:Int32" name="Frame number" />
|
||||
<data inType="win:Float" name="Duration (ms)" />
|
||||
</template>
|
||||
</templates>
|
||||
<events>
|
||||
<event symbol="ServerRenderFrameMark" template="T_FrameMark" value="300" task="ServerFrame" opcode="ServerRenderFrameMark" />
|
||||
<event symbol="ServerSimFrameMark" template="T_FrameMark" value="301" task="ServerFrame" opcode="ServerSimFrameMark" />
|
||||
</events>
|
||||
<opcodes>
|
||||
<opcode name="ServerRenderFrameMark" symbol="_ServerRenderFrameMarkOpcode" value="10"/>
|
||||
<opcode name="ServerSimFrameMark" symbol="_ServerSimFrameMarkOpcode" value="11"/>
|
||||
</opcodes>
|
||||
<tasks>
|
||||
<task name="ServerFrame" symbol="Frame_Task" value="1" eventGUID="{025F363A-49FD-4FFa-967C-173936494500}"/>
|
||||
</tasks>
|
||||
</provider>
|
||||
|
||||
<!-- Additional provider, to emit information about user input. -->
|
||||
<provider
|
||||
guid="{1432afee-73b0-42ce-9821-7e134361b433}"
|
||||
name="Valve-Input"
|
||||
symbol="VALVE_INPUT"
|
||||
messageFileName="%temp%\tier0.dll"
|
||||
resourceFileName="%temp%\tier0.dll"
|
||||
>
|
||||
<templates>
|
||||
<template tid="T_MouseClick">
|
||||
<data inType="win:Int32" name="x" />
|
||||
<data inType="win:Int32" name="y" />
|
||||
<data inType="win:Int32" name="Button Type" />
|
||||
</template>
|
||||
<template tid="T_KeyPress">
|
||||
<data inType="win:AnsiString" name="Character" />
|
||||
<data inType="win:Int32" name="Scan Code" />
|
||||
<data inType="win:Int32" name="Virtual Code" />
|
||||
</template>
|
||||
<template tid="T_MouseMove">
|
||||
<data inType="win:Int32" name="x" />
|
||||
<data inType="win:Int32" name="y" />
|
||||
</template>
|
||||
<template tid="T_MouseWheel">
|
||||
<data inType="win:Int32" name="x" />
|
||||
<data inType="win:Int32" name="y" />
|
||||
<data inType="win:Int32" name="wheelDelta" />
|
||||
</template>
|
||||
</templates>
|
||||
<events>
|
||||
<event symbol="Mouse_down" template="T_MouseClick" value="400" task="Mouse" opcode="MouseDown" />
|
||||
<event symbol="Mouse_up" template="T_MouseClick" value="401" task="Mouse" opcode="MouseUp" />
|
||||
<event symbol="Key_down" template="T_KeyPress" value="402" task="Keyboard" opcode="KeyDown" />
|
||||
<event symbol="Mouse_Move" template="T_MouseMove" value="403" task="Mouse" opcode="MouseMove" />
|
||||
<event symbol="Mouse_Wheel" template="T_MouseWheel" value="404" task="Mouse" opcode="MouseWheel" />
|
||||
</events>
|
||||
<opcodes>
|
||||
<opcode name="MouseDown" symbol="_MouseDownOpcode" value="10" />
|
||||
<opcode name="MouseUp" symbol="_MouseUpOpcode" value="11" />
|
||||
<opcode name="KeyDown" symbol="_KeyDownOpcode" value="12" />
|
||||
<opcode name="MouseMove" symbol="_MouseMoveOpcode" value="13" />
|
||||
<opcode name="MouseWheel" symbol="_MouseWheelOpcode" value="14" />
|
||||
</opcodes>
|
||||
<tasks>
|
||||
<task name="Mouse" symbol="Mouse_Task" value="1" eventGUID="{363A49FD-F15F-4FFa-967C-173936494433}"/>
|
||||
<task name="Keyboard" symbol="Keyboard_Task" value="2" eventGUID="{123A49FD-F15F-4FFa-967C-17393649BEAD}"/>
|
||||
</tasks>
|
||||
</provider>
|
||||
|
||||
<!-- Additional provider, to emit information about networking. -->
|
||||
<provider
|
||||
guid="{4372afee-73b0-42ce-9821-7e134361b519}"
|
||||
name="Valve-Network"
|
||||
symbol="VALVE_NETWORK"
|
||||
messageFileName="%temp%\tier0.dll"
|
||||
resourceFileName="%temp%\tier0.dll"
|
||||
>
|
||||
<templates>
|
||||
<template tid="T_SendPacket">
|
||||
<data inType="win:AnsiString" name="To" />
|
||||
<data inType="win:Int32" name="WireSize" />
|
||||
<data inType="win:Int32" name="outSequenceNR" />
|
||||
<data inType="win:Int32" name="outSequenceNrAck" />
|
||||
<data inType="win:Int32" name="CumulativeWireSize" />
|
||||
</template>
|
||||
<template tid="T_Throttled">
|
||||
</template>
|
||||
<template tid="T_ReadPacket">
|
||||
<data inType="win:AnsiString" name="From" />
|
||||
<data inType="win:Int32" name="WireSize" />
|
||||
<data inType="win:Int32" name="inSequenceNR" />
|
||||
<data inType="win:Int32" name="outSequenceNrAck" />
|
||||
<data inType="win:Int32" name="CumulativeWireSize" />
|
||||
</template>
|
||||
</templates>
|
||||
<events>
|
||||
<event symbol="SendPacket" template="T_SendPacket" value="500" task="Transmit" opcode="Send" />
|
||||
<event symbol="Throttled" template="T_Throttled" value="501" task="Throttled" opcode="Throttled" />
|
||||
<event symbol="ReadPacket" template="T_ReadPacket" value="502" task="Transmit" opcode="Read" />
|
||||
</events>
|
||||
<opcodes>
|
||||
<opcode name="Send" symbol="_SendOpcode" value="10" />
|
||||
<opcode name="Throttled" symbol="_ThrottledOpcode" value="11" />
|
||||
<opcode name="Read" symbol="_ReadOpcode" value="12" />
|
||||
</opcodes>
|
||||
<tasks>
|
||||
<task name="Transmit" symbol="Transmit_Task" value="1" eventGUID="{932A49FD-F15F-4FFa-967C-173936494901}"/>
|
||||
<task name="Throttled" symbol="Network_Task" value="2" eventGUID="{A32A49FD-F15F-4FFa-967C-173936494902}"/>
|
||||
</tasks>
|
||||
</provider>
|
||||
</events>
|
||||
</instrumentation>
|
||||
</instrumentationManifest>
|
||||
27
tier0/vatoms.cpp
Normal file
27
tier0/vatoms.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
//========== Copyright © Valve Corporation, All rights reserved. ========
|
||||
// NOTE: DO NOT INCLUDE vatoms.h, we don't want to rebuild tier0 every time
|
||||
// the header changes.
|
||||
//
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
PLATFORM_INTERFACE void** GetVAtom( int nAtomIndex );
|
||||
|
||||
static void* g_atoms[16] = {NULL}; // all pointers must be initialized to NULL
|
||||
|
||||
void** GetVAtom( int nAtomIndex )
|
||||
{
|
||||
if( uint( nAtomIndex ) >= ARRAYSIZE( g_atoms ) )
|
||||
{
|
||||
|
||||
ConMsg (
|
||||
"*******************************************************************\n"
|
||||
" *** ERROR *** \n"
|
||||
"VATOM index %d out of range, recompile tier0 with larger atom table\n"
|
||||
"*******************************************************************\n",
|
||||
nAtomIndex );
|
||||
return NULL;
|
||||
}
|
||||
return &g_atoms[nAtomIndex];
|
||||
}
|
||||
2419
tier0/vprof.cpp
Normal file
2419
tier0/vprof.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1
tier0/vsi.nul
Normal file
1
tier0/vsi.nul
Normal file
@@ -0,0 +1 @@
|
||||
IMPORTANT: Do not remove the custom build step for this file
|
||||
92
tier0/vtuneinterface.cpp
Normal file
92
tier0/vtuneinterface.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose: Real-Time Hierarchical Profiling
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#include "tier0/memalloc.h"
|
||||
#include "tier0/valve_off.h"
|
||||
#include "tier0/threadtools.h"
|
||||
|
||||
#include "vtuneinterface.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#ifdef VTUNE_ENABLED
|
||||
|
||||
class VTuneInterfaceImpl : public VTuneInterface
|
||||
{
|
||||
public:
|
||||
|
||||
VTuneInterfaceImpl()
|
||||
{
|
||||
m_pFrameDomain = NULL;
|
||||
}
|
||||
|
||||
virtual void Init();
|
||||
virtual void StartFrame();
|
||||
virtual void EndFrame();
|
||||
virtual __itt_event CreateEvent( const char *name );
|
||||
|
||||
private:
|
||||
|
||||
__itt_domain* m_pFrameDomain;
|
||||
CThreadFastMutex m_eventCreateMutex;
|
||||
};
|
||||
|
||||
VTuneInterfaceImpl g_VTuneInterface;
|
||||
VTuneInterface *g_pVTuneInterface = &g_VTuneInterface;
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* VTuneInterfaceImpl
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
void VTuneInterfaceImpl::Init()
|
||||
{
|
||||
if ( !m_pFrameDomain )
|
||||
{
|
||||
m_pFrameDomain = __itt_domain_create( "Main" );
|
||||
m_pFrameDomain->flags = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void VTuneInterfaceImpl::StartFrame()
|
||||
{
|
||||
if ( m_pFrameDomain == NULL )
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
__itt_frame_begin_v3( m_pFrameDomain, NULL);
|
||||
}
|
||||
|
||||
void VTuneInterfaceImpl::EndFrame()
|
||||
{
|
||||
__itt_frame_end_v3( m_pFrameDomain, NULL);
|
||||
}
|
||||
|
||||
__itt_event VTuneInterfaceImpl::CreateEvent( const char *name )
|
||||
{
|
||||
AUTO_LOCK_FM( m_eventCreateMutex );
|
||||
return __itt_event_create( name, strlen( name ) );
|
||||
}
|
||||
|
||||
void VTuneAutoEvent::Start()
|
||||
{
|
||||
__itt_event_start( m_event );
|
||||
}
|
||||
|
||||
void VTuneAutoEvent::End()
|
||||
{
|
||||
__itt_event_end( m_event );
|
||||
}
|
||||
|
||||
#endif // VTUNE_ENABLED
|
||||
|
||||
|
||||
161
tier0/win32consoleio.cpp
Normal file
161
tier0/win32consoleio.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ======
|
||||
//
|
||||
// Purpose: Win32 Console API helpers
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "win32consoleio.h"
|
||||
|
||||
#if defined( _WIN32 )
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#endif // defined( _WIN32 )
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Attach a console to a Win32 GUI process and setup stdin, stdout & stderr
|
||||
// along with the std::iostream (cout, cin, cerr) equivalents to read and
|
||||
// write to and from that console
|
||||
//
|
||||
// 1. Ensure the handle associated with stdio is FILE_TYPE_UNKNOWN
|
||||
// if it's anything else just return false. This supports cygwin
|
||||
// style command shells like rxvt which setup pipes to processes
|
||||
// they spawn
|
||||
//
|
||||
// 2. See if the Win32 function call AttachConsole exists in kernel32
|
||||
// It's a Windows 2000 and above call. If it does, call it and see
|
||||
// if it succeeds in attaching to the console of the parent process.
|
||||
// If that succeeds, return false (for no new console allocated).
|
||||
// This supports someone typing the command from a normal windows
|
||||
// command window and having the output go to the parent window.
|
||||
// It's a little funny because a GUI app detaches so the command
|
||||
// prompt gets intermingled with output from this process
|
||||
//
|
||||
// 3. If things get to here call AllocConsole which will pop open
|
||||
// a new window and allow output to go to that window. The
|
||||
// window will disappear when the process exists so if it's used
|
||||
// for something like a help message then do something like getchar()
|
||||
// from stdin to wait for a keypress. if AllocConsole is called
|
||||
// true is returned.
|
||||
//
|
||||
// Return: true if AllocConsole() was used to pop open a new windows console
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
bool SetupWin32ConsoleIO()
|
||||
{
|
||||
#if defined( _WIN32 )
|
||||
// Only useful on Windows platforms
|
||||
|
||||
bool newConsole( false );
|
||||
|
||||
if ( GetFileType( GetStdHandle( STD_OUTPUT_HANDLE ) ) == FILE_TYPE_UNKNOWN )
|
||||
{
|
||||
|
||||
HINSTANCE hInst = ::LoadLibrary( "kernel32.dll" );
|
||||
typedef BOOL ( WINAPI * pAttachConsole_t )( DWORD );
|
||||
pAttachConsole_t pAttachConsole( ( BOOL ( _stdcall * )( DWORD ) )GetProcAddress( hInst, "AttachConsole" ) );
|
||||
|
||||
if ( !( pAttachConsole && (*pAttachConsole)( ( DWORD ) - 1 ) ) )
|
||||
{
|
||||
newConsole = true;
|
||||
AllocConsole();
|
||||
}
|
||||
|
||||
*stdout = *_fdopen( _open_osfhandle( reinterpret_cast< intp >( GetStdHandle( STD_OUTPUT_HANDLE ) ), _O_TEXT ), "w" );
|
||||
setvbuf( stdout, NULL, _IONBF, 0 );
|
||||
|
||||
*stdin = *_fdopen( _open_osfhandle( reinterpret_cast< intp >( GetStdHandle( STD_INPUT_HANDLE ) ), _O_TEXT ), "r" );
|
||||
setvbuf( stdin, NULL, _IONBF, 0 );
|
||||
|
||||
*stderr = *_fdopen( _open_osfhandle( reinterpret_cast< intp >( GetStdHandle( STD_ERROR_HANDLE ) ), _O_TEXT ), "w" );
|
||||
setvbuf( stdout, NULL, _IONBF, 0 );
|
||||
|
||||
std::ios_base::sync_with_stdio();
|
||||
}
|
||||
|
||||
return newConsole;
|
||||
|
||||
#else // defined( _WIN32 )
|
||||
|
||||
return false;
|
||||
|
||||
#endif // defined( _WIN32 )
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Win32 Console Color API Helpers, originally from cmdlib.
|
||||
// Retrieves the current console color attributes.
|
||||
//-----------------------------------------------------------------------------
|
||||
void InitWin32ConsoleColorContext( Win32ConsoleColorContext_t *pContext )
|
||||
{
|
||||
#if PLATFORM_WINDOWS_PC
|
||||
// Get the old background attributes.
|
||||
CONSOLE_SCREEN_BUFFER_INFO oldInfo;
|
||||
GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo );
|
||||
pContext->m_InitialColor = pContext->m_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
|
||||
pContext->m_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY);
|
||||
|
||||
pContext->m_BadColor = 0;
|
||||
if (pContext->m_BackgroundFlags & BACKGROUND_RED)
|
||||
pContext->m_BadColor |= FOREGROUND_RED;
|
||||
if (pContext->m_BackgroundFlags & BACKGROUND_GREEN)
|
||||
pContext->m_BadColor |= FOREGROUND_GREEN;
|
||||
if (pContext->m_BackgroundFlags & BACKGROUND_BLUE)
|
||||
pContext->m_BadColor |= FOREGROUND_BLUE;
|
||||
if (pContext->m_BackgroundFlags & BACKGROUND_INTENSITY)
|
||||
pContext->m_BadColor |= FOREGROUND_INTENSITY;
|
||||
#else
|
||||
pContext->m_InitialColor = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sets the active console foreground color. This function is smart enough to
|
||||
// avoid setting the color to something that would be unreadable given
|
||||
// the user's potentially customized background color. It leaves the
|
||||
// background color unchanged.
|
||||
// Returns: The console's previous foreground color.
|
||||
//-----------------------------------------------------------------------------
|
||||
uint16 SetWin32ConsoleColor( Win32ConsoleColorContext_t *pContext, int nRed, int nGreen, int nBlue, int nIntensity )
|
||||
{
|
||||
#if PLATFORM_WINDOWS_PC
|
||||
uint16 ret = pContext->m_LastColor;
|
||||
pContext->m_LastColor = 0;
|
||||
if ( nRed ) pContext->m_LastColor |= FOREGROUND_RED;
|
||||
if ( nGreen ) pContext->m_LastColor |= FOREGROUND_GREEN;
|
||||
if ( nBlue ) pContext->m_LastColor |= FOREGROUND_BLUE;
|
||||
if ( nIntensity ) pContext->m_LastColor |= FOREGROUND_INTENSITY;
|
||||
|
||||
// Just use the initial color if there's a match...
|
||||
if ( pContext->m_LastColor == pContext->m_BadColor )
|
||||
pContext->m_LastColor = pContext->m_InitialColor;
|
||||
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), pContext->m_LastColor | pContext->m_BackgroundFlags );
|
||||
return ret;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Restore's the active foreground console color, without distributing the current
|
||||
// background color.
|
||||
//-----------------------------------------------------------------------------
|
||||
void RestoreWin32ConsoleColor( Win32ConsoleColorContext_t *pContext, uint16 prevColor )
|
||||
{
|
||||
#if PLATFORM_WINDOWS_PC
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), prevColor | pContext->m_BackgroundFlags );
|
||||
pContext->m_LastColor = prevColor;
|
||||
#endif
|
||||
}
|
||||
31
tier0/xbox/xbox_console.cpp
Normal file
31
tier0/xbox/xbox_console.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Xbox console link
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#include "../pch_tier0.h"
|
||||
#include "xbox/xbox_console.h"
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
IXboxConsole *g_pXboxConsole;
|
||||
|
||||
typedef IXboxConsole * (WINAPI *CONSOLEINTERFACEFUNC)( void );
|
||||
|
||||
void XboxConsoleInit()
|
||||
{
|
||||
g_pXboxConsole = NULL;
|
||||
|
||||
HMODULE hDLL = ::LoadLibrary( "game:\\bin\\vxbdm_360.dll" );
|
||||
if ( !hDLL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CONSOLEINTERFACEFUNC fpnGetConsoleInterface = (CONSOLEINTERFACEFUNC) ::GetProcAddress( hDLL, (LPSTR)1 );
|
||||
|
||||
if ( fpnGetConsoleInterface )
|
||||
{
|
||||
g_pXboxConsole = fpnGetConsoleInterface();
|
||||
}
|
||||
}
|
||||
9
tier0/xbox/xbox_profile.cpp
Normal file
9
tier0/xbox/xbox_profile.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Xbox profiling
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
1169
tier0/xbox/xbox_system.cpp
Normal file
1169
tier0/xbox/xbox_system.cpp
Normal file
File diff suppressed because it is too large
Load Diff
606
tier0/xbox/xbox_win32stubs.cpp
Normal file
606
tier0/xbox/xbox_win32stubs.cpp
Normal file
@@ -0,0 +1,606 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: XBox win32 replacements - Mocks trivial windows flow
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "../pch_tier0.h"
|
||||
#include "xbox/xbox_win32stubs.h"
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// On the 360, threads can run on any of 6 logical processors
|
||||
DWORD g_dwProcessAffinityMask = 0x3F;
|
||||
|
||||
#define HWND_MAGIC 0x12345678
|
||||
|
||||
struct xWndClass_t
|
||||
{
|
||||
char* pClassName;
|
||||
WNDPROC wndProc;
|
||||
xWndClass_t* pNext;
|
||||
};
|
||||
|
||||
struct xWnd_t
|
||||
{
|
||||
xWndClass_t* pWndClass;
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
long windowLongs[GWL_MAX];
|
||||
int show;
|
||||
int nMagic;
|
||||
xWnd_t* pNext;
|
||||
};
|
||||
|
||||
static xWndClass_t* g_pWndClasses;
|
||||
static xWnd_t* g_pWnds;
|
||||
static HWND g_focusWindow;
|
||||
|
||||
inline bool IsWndValid( HWND hWnd )
|
||||
{
|
||||
if ( !hWnd || ((xWnd_t*)hWnd)->nMagic != HWND_MAGIC )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
|
||||
{
|
||||
XBX_Error( lpText );
|
||||
Assert( 0 );
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
LONG GetWindowLong(HWND hWnd, int nIndex)
|
||||
{
|
||||
LONG oldLong;
|
||||
|
||||
if ( !IsWndValid( hWnd ) )
|
||||
return 0;
|
||||
|
||||
switch (nIndex)
|
||||
{
|
||||
case GWL_WNDPROC:
|
||||
case GWL_USERDATA:
|
||||
case GWL_STYLE:
|
||||
case GWL_EXSTYLE:
|
||||
oldLong = ((xWnd_t*)hWnd)->windowLongs[nIndex];
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
Assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return oldLong;
|
||||
}
|
||||
|
||||
LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex)
|
||||
{
|
||||
UINT idx;
|
||||
|
||||
switch ( nIndex )
|
||||
{
|
||||
case GWLP_WNDPROC:
|
||||
idx = GWL_WNDPROC;
|
||||
break;
|
||||
case GWLP_USERDATA:
|
||||
idx = GWL_USERDATA;
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
Assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return GetWindowLong( hWnd, idx );
|
||||
}
|
||||
|
||||
LONG_PTR GetWindowLongPtrW(HWND hWnd, int nIndex)
|
||||
{
|
||||
AssertMsg( false, "GetWindowLongPtrW does not exist on Xbox 360." );
|
||||
return GetWindowLongPtr( hWnd, nIndex );
|
||||
}
|
||||
|
||||
LONG SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong)
|
||||
{
|
||||
LONG oldLong;
|
||||
|
||||
if ( !IsWndValid( hWnd ) )
|
||||
return 0;
|
||||
|
||||
switch ( nIndex )
|
||||
{
|
||||
case GWL_WNDPROC:
|
||||
case GWL_USERDATA:
|
||||
case GWL_STYLE:
|
||||
oldLong = ((xWnd_t*)hWnd)->windowLongs[nIndex];
|
||||
((xWnd_t*)hWnd)->windowLongs[nIndex] = dwNewLong;
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
Assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return oldLong;
|
||||
}
|
||||
|
||||
LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
|
||||
{
|
||||
UINT idx;
|
||||
|
||||
switch ( nIndex )
|
||||
{
|
||||
case GWLP_WNDPROC:
|
||||
idx = GWL_WNDPROC;
|
||||
break;
|
||||
case GWLP_USERDATA:
|
||||
idx = GWL_USERDATA;
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
Assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return SetWindowLong( hWnd, idx, dwNewLong );
|
||||
}
|
||||
|
||||
LONG_PTR SetWindowLongPtrW(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
|
||||
{
|
||||
AssertMsg( false, "SetWindowLongPtrW does not exist on Xbox 360." );
|
||||
return SetWindowLongPtr( hWnd, nIndex, dwNewLong );
|
||||
}
|
||||
|
||||
HWND CreateWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
|
||||
{
|
||||
// find classname
|
||||
xWndClass_t* pWndClass = g_pWndClasses;
|
||||
while ( pWndClass )
|
||||
{
|
||||
if ( !V_tier0_stricmp( lpClassName, pWndClass->pClassName ) )
|
||||
break;
|
||||
pWndClass = pWndClass->pNext;
|
||||
}
|
||||
if ( !pWndClass )
|
||||
{
|
||||
// no such class
|
||||
return (HWND)NULL;
|
||||
}
|
||||
|
||||
// allocate and setup
|
||||
xWnd_t* pWnd = new xWnd_t;
|
||||
memset( pWnd, 0, sizeof(xWnd_t) );
|
||||
pWnd->pWndClass = pWndClass;
|
||||
pWnd->windowLongs[GWL_WNDPROC] = (LONG)pWndClass->wndProc;
|
||||
pWnd->windowLongs[GWL_STYLE] = dwStyle;
|
||||
pWnd->x = x;
|
||||
pWnd->y = y;
|
||||
pWnd->w = nWidth;
|
||||
pWnd->h = nHeight;
|
||||
pWnd->nMagic = HWND_MAGIC;
|
||||
|
||||
// link into list
|
||||
pWnd->pNext = g_pWnds;
|
||||
g_pWnds = pWnd;
|
||||
|
||||
// force the focus
|
||||
g_focusWindow = (HWND)pWnd;
|
||||
|
||||
// send the expected message sequence
|
||||
SendMessage( (HWND)pWnd, WM_CREATE, 0, 0 );
|
||||
SendMessage( (HWND)pWnd, WM_ACTIVATEAPP, TRUE, 0 );
|
||||
|
||||
return (HWND)pWnd;
|
||||
}
|
||||
|
||||
HWND CreateWindowEx(DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
|
||||
{
|
||||
return CreateWindow( lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam );
|
||||
}
|
||||
|
||||
BOOL DestroyWindow( HWND hWnd )
|
||||
{
|
||||
if ( !IsWndValid( hWnd ) )
|
||||
return FALSE;
|
||||
|
||||
xWnd_t* pPrev = g_pWnds;
|
||||
xWnd_t* pCur = g_pWnds;
|
||||
|
||||
while ( pCur )
|
||||
{
|
||||
if ( pCur == (xWnd_t*)hWnd )
|
||||
{
|
||||
if ( pPrev == g_pWnds )
|
||||
{
|
||||
// at head of list, fixup
|
||||
g_pWnds = pCur->pNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove from chain
|
||||
pPrev->pNext = pCur->pNext;
|
||||
}
|
||||
pCur->nMagic = 0;
|
||||
delete pCur;
|
||||
break;
|
||||
}
|
||||
|
||||
// advance through list
|
||||
pPrev = pCur;
|
||||
pCur = pCur->pNext;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ATOM RegisterClassEx(CONST WNDCLASSEX *lpwcx)
|
||||
{
|
||||
// create
|
||||
xWndClass_t* pWndClass = new xWndClass_t;
|
||||
memset(pWndClass, 0, sizeof(xWndClass_t));
|
||||
pWndClass->pClassName = new char[strlen(lpwcx->lpszClassName)+1];
|
||||
strcpy(pWndClass->pClassName, lpwcx->lpszClassName);
|
||||
pWndClass->wndProc = lpwcx->lpfnWndProc;
|
||||
|
||||
// insert into list
|
||||
pWndClass->pNext = g_pWndClasses;
|
||||
g_pWndClasses = pWndClass;
|
||||
|
||||
return (ATOM)pWndClass;
|
||||
}
|
||||
|
||||
ATOM RegisterClass(CONST WNDCLASS *lpwc)
|
||||
{
|
||||
// create
|
||||
xWndClass_t* pWndClass = new xWndClass_t;
|
||||
memset(pWndClass, 0, sizeof(xWndClass_t));
|
||||
pWndClass->pClassName = new char[strlen(lpwc->lpszClassName)+1];
|
||||
strcpy(pWndClass->pClassName, lpwc->lpszClassName);
|
||||
pWndClass->wndProc = lpwc->lpfnWndProc;
|
||||
|
||||
// insert into list
|
||||
pWndClass->pNext = g_pWndClasses;
|
||||
g_pWndClasses = pWndClass;
|
||||
|
||||
return (ATOM)pWndClass;
|
||||
}
|
||||
|
||||
HWND GetFocus(VOID)
|
||||
{
|
||||
if ( !IsWndValid( g_focusWindow ) )
|
||||
return NULL;
|
||||
|
||||
return g_focusWindow;
|
||||
}
|
||||
|
||||
HWND SetFocus( HWND hWnd )
|
||||
{
|
||||
HWND hOldFocus = g_focusWindow;
|
||||
|
||||
if ( IsWndValid( hWnd ) )
|
||||
{
|
||||
g_focusWindow = hWnd;
|
||||
}
|
||||
|
||||
return hOldFocus;
|
||||
}
|
||||
|
||||
|
||||
LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return (lpPrevWndFunc(hWnd, Msg, wParam, lParam));
|
||||
}
|
||||
|
||||
LRESULT CallWindowProcW(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
AssertMsg( false, "CallWindowProcW does not exist on Xbox 360." );
|
||||
return CallWindowProc( lpPrevWndFunc, hWnd, Msg, wParam, lParam );
|
||||
}
|
||||
|
||||
|
||||
int GetSystemMetrics(int nIndex)
|
||||
{
|
||||
XVIDEO_MODE videoMode;
|
||||
XGetVideoMode( &videoMode );
|
||||
// default to having the backbuffer the same as the mode resolution.
|
||||
int nFrameBufferWidth, nFrameBufferHeight;
|
||||
nFrameBufferWidth = videoMode.dwDisplayWidth;
|
||||
nFrameBufferHeight = videoMode.dwDisplayHeight;
|
||||
|
||||
// override for cases where we need to have a different backbuffer either for memory reasons
|
||||
// or for dealing with anamorphic modes.
|
||||
if ( !videoMode.fIsWideScreen && videoMode.dwDisplayWidth == 640 && videoMode.dwDisplayHeight == 576 )
|
||||
{
|
||||
// PAL normal
|
||||
nFrameBufferWidth = 640;
|
||||
nFrameBufferHeight = 480;
|
||||
}
|
||||
else if ( videoMode.fIsWideScreen && videoMode.dwDisplayWidth == 640 && videoMode.dwDisplayHeight == 576 )
|
||||
{
|
||||
// PAL widescreen
|
||||
nFrameBufferWidth = 848;
|
||||
nFrameBufferHeight = 480;
|
||||
}
|
||||
else if ( videoMode.fIsWideScreen && videoMode.dwDisplayWidth == 640 && videoMode.dwDisplayHeight == 480 )
|
||||
{
|
||||
// anamorphic
|
||||
nFrameBufferWidth = 848;
|
||||
nFrameBufferHeight = 480;
|
||||
}
|
||||
else if ( videoMode.fIsWideScreen && videoMode.dwDisplayWidth == 1024 && videoMode.dwDisplayHeight == 768 )
|
||||
{
|
||||
// anamorphic
|
||||
nFrameBufferWidth = 1280;
|
||||
nFrameBufferHeight = 720;
|
||||
}
|
||||
else if ( videoMode.dwDisplayWidth == 1280 && videoMode.dwDisplayHeight == 760 )
|
||||
{
|
||||
nFrameBufferWidth = 1280;
|
||||
nFrameBufferHeight = 720;
|
||||
}
|
||||
else if ( videoMode.dwDisplayWidth == 1280 && videoMode.dwDisplayHeight == 768 )
|
||||
{
|
||||
nFrameBufferWidth = 1280;
|
||||
nFrameBufferHeight = 720;
|
||||
}
|
||||
else if ( !videoMode.fIsWideScreen && videoMode.dwDisplayWidth == 1280 && videoMode.dwDisplayHeight == 1024 )
|
||||
{
|
||||
nFrameBufferWidth = 1024;
|
||||
nFrameBufferHeight = 768;
|
||||
}
|
||||
else if ( videoMode.fIsWideScreen && videoMode.dwDisplayWidth == 1280 && videoMode.dwDisplayHeight == 1024 )
|
||||
{
|
||||
// anamorphic
|
||||
nFrameBufferWidth = 1280;
|
||||
nFrameBufferHeight = 720;
|
||||
}
|
||||
else if ( videoMode.dwDisplayWidth == 1360 && videoMode.dwDisplayHeight == 768 )
|
||||
{
|
||||
nFrameBufferWidth = 1280;
|
||||
nFrameBufferHeight = 720;
|
||||
}
|
||||
else if ( videoMode.dwDisplayWidth == 1920 && videoMode.dwDisplayHeight == 1080 )
|
||||
{
|
||||
nFrameBufferWidth = 1280;
|
||||
nFrameBufferHeight = 720;
|
||||
}
|
||||
|
||||
switch ( nIndex )
|
||||
{
|
||||
case SM_CXFIXEDFRAME:
|
||||
case SM_CYFIXEDFRAME:
|
||||
case SM_CYSIZE:
|
||||
return 0;
|
||||
case SM_CXSCREEN:
|
||||
return nFrameBufferWidth;
|
||||
case SM_CYSCREEN:
|
||||
return nFrameBufferHeight;
|
||||
}
|
||||
|
||||
// not implemented
|
||||
Assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL ShowWindow(HWND hWnd, int nCmdShow)
|
||||
{
|
||||
if ( !IsWndValid( hWnd ) )
|
||||
return FALSE;
|
||||
|
||||
((xWnd_t*)hWnd)->show = nCmdShow;
|
||||
|
||||
if ((nCmdShow == SW_SHOWDEFAULT) || (nCmdShow == SW_SHOWNORMAL) || (nCmdShow == SW_SHOW))
|
||||
g_focusWindow = hWnd;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if ( !IsWndValid( hWnd ) )
|
||||
return 0L;
|
||||
|
||||
xWnd_t* pWnd = (xWnd_t*)hWnd;
|
||||
WNDPROC wndProc = (WNDPROC)pWnd->windowLongs[GWL_WNDPROC];
|
||||
Assert( wndProc );
|
||||
LRESULT result = wndProc(hWnd, Msg, wParam, lParam);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LRESULT SendMessageTimeout( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult )
|
||||
{
|
||||
*lpdwResult = SendMessage( hWnd, Msg, wParam, lParam );
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
BOOL GetClientRect(HWND hWnd, LPRECT lpRect)
|
||||
{
|
||||
if ( !IsWndValid( hWnd ) )
|
||||
return FALSE;
|
||||
|
||||
xWnd_t* pWnd = (xWnd_t*)hWnd;
|
||||
lpRect->left = 0;
|
||||
lpRect->top = 0;
|
||||
lpRect->right = pWnd->w;
|
||||
lpRect->bottom = pWnd->h;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int GetDeviceCaps(HDC hdc, int nIndex)
|
||||
{
|
||||
switch (nIndex)
|
||||
{
|
||||
case HORZRES:
|
||||
return GetSystemMetrics( SM_CXSCREEN );
|
||||
case VERTRES:
|
||||
return GetSystemMetrics( SM_CYSCREEN );
|
||||
case VREFRESH:
|
||||
return 60;
|
||||
}
|
||||
|
||||
Assert( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL SetWindowPos( HWND hWnd, HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags )
|
||||
{
|
||||
if ( !IsWndValid( hWnd ) )
|
||||
return FALSE;
|
||||
|
||||
xWnd_t* pWnd = (xWnd_t*)hWnd;
|
||||
|
||||
if ( !( uFlags & SWP_NOMOVE ) )
|
||||
{
|
||||
pWnd->x = x;
|
||||
pWnd->y = y;
|
||||
}
|
||||
|
||||
if ( !( uFlags & SWP_NOSIZE ) )
|
||||
{
|
||||
pWnd->w = cx;
|
||||
pWnd->h = cy;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int XBX_unlink( const char* filename )
|
||||
{
|
||||
bool bSuccess = DeleteFile( filename ) != 0;
|
||||
if ( !bSuccess && GetLastError() == ERROR_FILE_NOT_FOUND )
|
||||
{
|
||||
// not a real error
|
||||
bSuccess = true;
|
||||
}
|
||||
// 0 = sucess, -1 = failure
|
||||
return bSuccess ? 0 : -1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Xbox low level replacement for _mkdir().
|
||||
// Expects to be driven by a higher level path iterator.
|
||||
//-----------------------------------------------------------------------------
|
||||
int XBX_mkdir( const char *pszDir )
|
||||
{
|
||||
bool bSuccess = CreateDirectory( pszDir, XBOX_DONTCARE ) != 0;
|
||||
if ( !bSuccess && GetLastError() == ERROR_ALREADY_EXISTS )
|
||||
{
|
||||
// not a real error
|
||||
bSuccess = true;
|
||||
}
|
||||
// 0 = sucess, -1 = failure
|
||||
return ( bSuccess ? 0 : -1 );
|
||||
}
|
||||
|
||||
char *XBX_getcwd( char *buf, size_t size )
|
||||
{
|
||||
if ( !buf )
|
||||
{
|
||||
buf = (char*)malloc( 4 );
|
||||
}
|
||||
strncpy( buf, "D:", size );
|
||||
return buf;
|
||||
}
|
||||
|
||||
int XBX_access( const char *path, int mode )
|
||||
{
|
||||
if ( !path )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get the fatx attributes
|
||||
DWORD dwAttr = GetFileAttributes( path );
|
||||
if ( dwAttr == (DWORD)-1 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( mode == 0 )
|
||||
{
|
||||
// is file exist?
|
||||
return 0;
|
||||
}
|
||||
else if ( mode == 2 )
|
||||
{
|
||||
// is file write only?
|
||||
return -1;
|
||||
}
|
||||
else if ( mode == 4 )
|
||||
{
|
||||
// is file read only?
|
||||
if ( dwAttr & FILE_ATTRIBUTE_READONLY )
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else if ( mode == 6 )
|
||||
{
|
||||
// is file read and write?
|
||||
if ( !( dwAttr & FILE_ATTRIBUTE_READONLY ) )
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
DWORD XBX_GetCurrentDirectory( DWORD nBufferLength, LPTSTR lpBuffer )
|
||||
{
|
||||
XBX_getcwd( lpBuffer, nBufferLength );
|
||||
return strlen( lpBuffer );
|
||||
}
|
||||
|
||||
DWORD XBX_GetModuleFileName( HMODULE hModule, LPTSTR lpFilename, DWORD nSize )
|
||||
{
|
||||
int len;
|
||||
char *pStr;
|
||||
char *pEnd;
|
||||
char xexName[MAX_PATH];
|
||||
|
||||
if ( hModule == GetModuleHandle( NULL ) )
|
||||
{
|
||||
// isolate xex of command line
|
||||
pStr = GetCommandLine();
|
||||
if ( pStr )
|
||||
{
|
||||
// cull possible quotes around xex
|
||||
if ( pStr[0] == '\"' )
|
||||
{
|
||||
pStr++;
|
||||
pEnd = strchr( pStr, '\"' );
|
||||
if ( !pEnd )
|
||||
{
|
||||
// no ending matching quote
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// find possible first argument
|
||||
pEnd = strchr( lpFilename, ' ' );
|
||||
if ( !pEnd )
|
||||
{
|
||||
pEnd = pStr+strlen( pStr );
|
||||
}
|
||||
}
|
||||
len = pEnd-pStr;
|
||||
memcpy( xexName, pStr, len );
|
||||
xexName[len] = '\0';
|
||||
|
||||
len = _snprintf( lpFilename, nSize, "D:\\%s", xexName );
|
||||
if ( len == -1 )
|
||||
lpFilename[nSize-1] = '\0';
|
||||
|
||||
return strlen( lpFilename );
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user