This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

68
tier0/DESKey/ALGO.H Normal file
View 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

Binary file not shown.

366
tier0/DESKey/DK2WIN32.H Normal file
View 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

Binary file not shown.

View File

@ -0,0 +1,78 @@
; call cpuid with args in eax, ecx
; store eax, ebx, ecx, edx to p
PUBLIC _InterlockedCompareExchange128
.CODE
_InterlockedCompareExchange128 PROC FRAME
; extern "C" unsigned char _InterlockedCompareExchange128( int64 volatile * Destination, int64 ExchangeHigh, int64 ExchangeLow, int64 * ComparandResult );
.endprolog
; fastcall conventions:
; RCX = Destination
; RDX = Exchange High
; R8 = Exchange Lo
; R9 = ComparandResult
; CMPXCHG16B refernece:
; http://download.intel.com/design/processor/manuals/253666.pdf
; Stash RBX in R11
mov r11, rbx
; Destination ptr to r10
mov r10, rcx
; RCX:RBX Exchange
mov rcx, rdx
mov rbx, r8
; RDX:RAX Comparand
mov rax, [r9]
mov rdx, [r9+8]
; Do the atomic operation
lock cmpxchg16b [r10]
; RDX:RAX now contains the value of the destination, before the atomic operation.
; (Either it already matched and was not changed, or it did not match, in which case
; the value has been loaded into RDX:RAX.) The _InterlockedCompareExchange128 intrinsic
; semantics specify that this is always written out to CompareResult, so give it
; back to the caller.
mov [r9], rax
mov [r9+8], rdx
; Return value is in AL, set it equal to the zero flag
setz al
; Restore RBX and get out
mov rbx, r11
ret
_InterlockedCompareExchange128 ENDP
; For reference, here's what VC2010 generated
;
; __declspec(noinline) unsigned char Test_InterlockedCompareExchange128( int64 volatile * Destination, int64 ExchangeHigh, int64 ExchangeLow, int64 * ComparandResult )
; {
; return _InterlockedCompareExchange128( Destination, ExchangeHigh, ExchangeLow, ComparandResult );
; }
;
;
;?Test_InterlockedCompareExchange128@GCSDK@@YAEPEC_J_J1PEA_J@Z (unsigned char __cdecl GCSDK::Test_InterlockedCompareExchange128(__int64 volatile *,__int64,__int64,__int64 *)):
; 0000000000000000: 48 89 5C 24 08 mov qword ptr [rsp+8],rbx
; 0000000000000005: 49 8B 01 mov rax,qword ptr [r9]
; 0000000000000008: 4C 8B D1 mov r10,rcx
; 000000000000000B: 48 8B CA mov rcx,rdx
; 000000000000000E: 49 8B 51 08 mov rdx,qword ptr [r9+8]
; 0000000000000012: 49 8B D8 mov rbx,r8
; 0000000000000015: F0 49 0F C7 0A lock cmpxchg16b oword ptr [r10]
; 000000000000001A: 48 8B 5C 24 08 mov rbx,qword ptr [rsp+8]
; 000000000000001F: 49 89 01 mov qword ptr [r9],rax
; 0000000000000022: 49 89 51 08 mov qword ptr [r9+8],rdx
; 0000000000000026: 0F 94 C0 sete al
; 0000000000000029: C3 ret
_TEXT ENDS
END

665
tier0/PMELib.cpp Normal file
View File

@ -0,0 +1,665 @@
//========= Copyright 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

616
tier0/assert_dialog.cpp Normal file
View File

@ -0,0 +1,616 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "pch_tier0.h"
#include "tier0/valve_off.h"
#ifdef _X360
#include "xbox/xbox_console.h"
#include "xbox/xbox_vxconsole.h"
#elif defined( _WIN32 )
#include <windows.h>
#elif defined( POSIX )
#include <stdlib.h>
#endif
#include "resource.h"
#include "tier0/valve_on.h"
#include "tier0/threadtools.h"
#if defined( POSIX )
#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 );
#endif
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 CAssertDisable *g_pAssertDisables = NULL;
#if ( defined( _WIN32 ) && !defined( _X360 ) )
static int g_iLastLineRange = 5;
static int g_nLastIgnoreNumTimes = 1;
#endif
#if defined( _X360 )
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;
// -------------------------------------------------------------------------------- //
// Internal functions.
// -------------------------------------------------------------------------------- //
#if defined(_WIN32) && !defined(STATIC_TIER0)
extern "C" BOOL APIENTRY MemDbgDllMain( HMODULE hDll, DWORD dwReason, PVOID pvReserved );
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to the DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpvReserved // reserved
)
{
g_hTier0Instance = hinstDLL;
#ifdef DEBUG
MemDbgDllMain( hinstDLL, fdwReason, lpvReserved );
#endif
return true;
}
#endif
static bool IsDebugBreakEnabled()
{
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-debugbreak") ) != NULL ) || \
( _tcsstr( Plat_GetCommandLine(), _T("-raiseonassert") ) != NULL ) || \
getenv( "RAISE_ON_ASSERT" );
return bResult;
}
static bool AreAssertsDisabled()
{
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-noassert") ) != 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 // ( defined( _WIN32 ) && !defined( _X360 ) )
// -------------------------------------------------------------------------------- //
// Interface functions.
// -------------------------------------------------------------------------------- //
// provides access to the global that turns asserts on and off
DBG_INTERFACE bool AreAllAssertsDisabled()
{
return !g_bAssertsEnabled;
}
DBG_INTERFACE void SetAllAssertsDisabled( bool bAssertsDisabled )
{
g_bAssertsEnabled = !bAssertsDisabled;
}
#if defined( LINUX ) || defined( USE_SDL )
SDL_Window *g_SDLWindow = NULL;
DBG_INTERFACE void SetAssertDialogParent( struct SDL_Window *window )
{
g_SDLWindow = window;
}
DBG_INTERFACE struct SDL_Window * GetAssertDialogParent()
{
return g_SDLWindow;
}
#endif
DBG_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
}
#if defined( POSIX )
#include <execinfo.h>
static void SpewBacktrace()
{
void *buffer[ 16 ];
int nptrs = backtrace( buffer, ARRAYSIZE( buffer ) );
if ( nptrs )
{
char **strings = backtrace_symbols(buffer, nptrs);
if ( strings )
{
for ( int i = 0; i < nptrs; i++)
{
const char *module = strrchr( strings[ i ], '/' );
module = module ? ( module + 1 ) : strings[ i ];
printf(" %s\n", module );
}
free( strings );
}
}
}
#endif
DBG_INTERFACE bool DoNewAssertDialog( const tchar *pFilename, int line, const tchar *pExpression )
{
LOCAL_THREAD_LOCK();
if ( AreAssertsDisabled() )
return false;
// Have ALL Asserts been disabled?
if ( !g_bAssertsEnabled )
return false;
// Has this specific Assert been disabled?
if ( !AreAssertsEnabledInFileLine( pFilename, line ) )
return false;
// Assert not suppressed. Spew it, and optionally a backtrace.
#if defined( POSIX )
if( isatty( STDERR_FILENO ) )
{
#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_END " " COLOR_RED "%s" COLOR_GREEN ":%i:" COLOR_END " " COLOR_RED "%s" COLOR_END "\n",
pFilename, line, pExpression);
if ( getenv( "POSIX_ASSERT_BACKTRACE" ) )
{
SpewBacktrace();
}
}
else
#endif
{
fprintf(stderr, "ASSERT: %s:%i: %s\n", pFilename, line, pExpression);
}
// 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;
// Now create the dialog. Just return true for old-style debug break upon failure.
g_Info.m_pFilename = pFilename;
g_Info.m_iLine = line;
g_Info.m_pExpression = pExpression;
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( _WIN32 )
if ( !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( POSIX )
static FUNC_SDL_ShowMessageBox *pfnSDLShowMessageBox = NULL;
if( !pfnSDLShowMessageBox )
{
#ifdef OSX
void *ret = dlopen( "libSDL2-2.0.0.dylib", RTLD_LAZY );
#else
void *ret = dlopen( "libSDL2-2.0.so.0", RTLD_LAZY );
#endif
if ( ret )
{ pfnSDLShowMessageBox = ( FUNC_SDL_ShowMessageBox * )dlsym( ret, "SDL_ShowMessageBox" ); }
}
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
{
// Couldn't SDL it up
g_bBreak = true;
}
#else
// No dialog mode on this platform
g_bBreak = true;
#endif
return g_bBreak;
}

124
tier0/assert_dialog.rc Normal file
View 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"

696
tier0/commandline.cpp Normal file
View File

@ -0,0 +1,696 @@
//========= Copyright 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/memdbgon.h"
#ifdef 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 OVERRIDE;
virtual int ParmValue( const char *psz, int nDefaultVal ) const OVERRIDE;
virtual float ParmValue( const char *psz, float flDefaultVal ) const OVERRIDE;
virtual const char *ParmValueByIndex( int nIndex, const char *pDefaultVal = 0 ) const OVERRIDE;
virtual void SetParm( int nIndex, char const *pParm );
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, int 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, int 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;
char *dest = cmdline;
size_t size = sizeof( cmdline );
const char *space = "";
for ( int i = 0; i < argc; ++i )
{
// We need room for: space, arg, 2 quotes, and a nil.
Assert( strlen( space ) + strlen( argv[ i ] ) + 2 + 1 <= size );
if ( size )
{
_snprintf( dest, size, "%s\"%s\"", space, argv[ i ] );
dest[ size - 1 ] = 0;
}
size_t len = strlen( dest );
size -= len;
dest += len;
space = " ";
}
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 )
{
if ( m_pszCmdLine )
{
delete[] m_pszCmdLine;
}
char szFull[ 4096 ];
szFull[0] = '\0';
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 ( *pSrc == '@' )
{
if ( pSrc == commandline || (!bInQuotes && 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';
int len = strlen( szFull ) + 1;
m_pszCmdLine = new char[len];
memcpy( m_pszCmdLine, szFull, len );
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;
int n;
int 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 )
{
int 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 )
{
int 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 && 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 ( isspace( *pChar ) )
continue;
pFirstLetter = pChar;
continue;
}
// Here, we're in the middle of a word. Look for the end of it.
if ( 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 ( !_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] );
}
const char *CCommandLine::ParmValueByIndex( int nIndex, const char *pDefaultVal ) const
{
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];
}

596
tier0/cpu.cpp Normal file
View File

@ -0,0 +1,596 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#if defined(_WIN32) && !defined(_X360)
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#elif defined(_LINUX)
#include <stdlib.h>
#elif defined(OSX)
#include <sys/sysctl.h>
#endif
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
const tchar* GetProcessorVendorId();
static bool cpuid(unsigned long function, unsigned long& out_eax, unsigned long& out_ebx, unsigned long& out_ecx, unsigned long& out_edx)
{
#if defined(GNUC)
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)
);
return true;
#elif defined( _X360 )
return false;
#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 local_eax, local_ebx, local_ecx, local_edx;
_asm pushad;
__try
{
_asm
{
xor edx, edx // Clue the compiler that EDX is about to be used.
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 local_eax, eax // features returned in eax
mov local_ebx, ebx // features returned in ebx
mov local_ecx, ecx // features returned in ecx
mov local_edx, edx // features returned in edx
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
retval = false;
}
out_eax = local_eax;
out_ebx = local_ebx;
out_ecx = local_ecx;
out_edx = local_edx;
_asm popad
return retval;
#endif
}
static bool CheckMMXTechnology(void)
{
#if defined( _X360 ) || defined( _PS3 )
return true;
#else
unsigned long eax,ebx,edx,unused;
if ( !cpuid(1,eax,ebx,unused,edx) )
return false;
return ( edx & 0x800000 ) != 0;
#endif
}
//-----------------------------------------------------------------------------
// 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;
}
unsigned long eax,ebx,edx,unused;
if ( !cpuid(1,eax,ebx,unused,edx) )
{
return false;
}
return ( edx & 0x2000000L ) != 0;
#endif
}
static bool CheckSSE2Technology(void)
{
#if defined( _X360 ) || defined( _PS3 )
return false;
#else
unsigned long eax,ebx,edx,unused;
if ( !cpuid(1,eax,ebx,unused,edx) )
return false;
return ( edx & 0x04000000 ) != 0;
#endif
}
bool CheckSSE3Technology(void)
{
#if defined( _X360 ) || defined( _PS3 )
return false;
#else
unsigned long eax,ebx,edx,ecx;
if( !cpuid(1,eax,ebx,ecx,edx) )
return false;
return ( 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
unsigned long eax,ebx,edx,ecx;
if( !cpuid(1,eax,ebx,ecx,edx) )
return false;
return ( 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
unsigned long eax,ebx,edx,ecx;
if( !cpuid(1,eax,ebx,ecx,edx) )
return false;
return ( 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;
unsigned long eax,ebx,edx,ecx;
if( !cpuid(1,eax,ebx,ecx,edx) )
return false;
return ( 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;
unsigned long eax,ebx,edx,ecx;
if( !cpuid( 0x80000001,eax,ebx,ecx,edx) )
return false;
return ( ecx & ( 1 << 6 ) ) != 0; // bit 6 of ECX
#endif
}
static bool Check3DNowTechnology(void)
{
#if defined( _X360 ) || defined( _PS3 )
return false;
#else
unsigned long eax, unused;
if ( !cpuid(0x80000000,eax,unused,unused,unused) )
return false;
if ( eax > 0x80000000L )
{
if ( !cpuid(0x80000001,unused,unused,unused,eax) )
return false;
return ( eax & 1<<31 ) != 0;
}
return false;
#endif
}
static bool CheckCMOVTechnology()
{
#if defined( _X360 ) || defined( _PS3 )
return false;
#else
unsigned long eax,ebx,edx,unused;
if ( !cpuid(1,eax,ebx,unused,edx) )
return false;
return ( edx & (1<<15) ) != 0;
#endif
}
static bool CheckFCMOVTechnology(void)
{
#if defined( _X360 ) || defined( _PS3 )
return false;
#else
unsigned long eax,ebx,edx,unused;
if ( !cpuid(1,eax,ebx,unused,edx) )
return false;
return ( edx & (1<<16) ) != 0;
#endif
}
static bool CheckRDTSCTechnology(void)
{
#if defined( _X360 ) || defined( _PS3 )
return false;
#else
unsigned long eax,ebx,edx,unused;
if ( !cpuid(1,eax,ebx,unused,edx) )
return false;
return ( edx & 0x10 ) != 0;
#endif
}
// 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
unsigned long unused, VendorIDRegisters[3];
static tchar VendorID[13];
memset( VendorID, 0, sizeof(VendorID) );
if ( !cpuid(0,unused, VendorIDRegisters[0], VendorIDRegisters[2], VendorIDRegisters[1] ) )
{
if ( IsPC() )
{
_tcscpy( VendorID, _T( "Generic_x86" ) );
}
else if ( IsX360() )
{
_tcscpy( VendorID, _T( "PowerPC" ) );
}
}
else
{
memcpy( VendorID+0, &(VendorIDRegisters[0]), sizeof( VendorIDRegisters[0] ) );
memcpy( VendorID+4, &(VendorIDRegisters[1]), sizeof( VendorIDRegisters[1] ) );
memcpy( VendorID+8, &(VendorIDRegisters[2]), sizeof( VendorIDRegisters[2] ) );
}
return VendorID;
#endif
}
// Returns non-zero if Hyper-Threading Technology is supported on the processors and zero if not. This does not mean that
// Hyper-Threading Technology is necessarily enabled.
static bool HTSupported(void)
{
#if defined( _X360 )
// 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
const unsigned int HT_BIT = 0x10000000; // EDX[28] - Bit 28 set indicates Hyper-Threading Technology is supported in hardware.
const unsigned int FAMILY_ID = 0x0f00; // EAX[11:8] - Bit 11 thru 8 contains family processor id
const unsigned int EXT_FAMILY_ID = 0x0f00000; // EAX[23:20] - Bit 23 thru 20 contains extended family processor id
const unsigned int PENTIUM4_ID = 0x0f00; // Pentium 4 family processor id
unsigned long unused,
reg_eax = 0,
reg_edx = 0,
vendor_id[3] = {0, 0, 0};
// verify cpuid instruction is supported
if( !cpuid(0,unused, vendor_id[0],vendor_id[2],vendor_id[1])
|| !cpuid(1,reg_eax,unused,unused,reg_edx) )
return false;
// Check to see if this is a Pentium 4 or later processor
if (((reg_eax & FAMILY_ID) == PENTIUM4_ID) || (reg_eax & EXT_FAMILY_ID))
if (vendor_id[0] == 'uneG' && vendor_id[1] == 'Ieni' && vendor_id[2] == 'letn')
return (reg_edx & HT_BIT) != 0; // Genuine Intel Processor with Hyper-Threading Technology
return false; // This is not a genuine Intel processor.
#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;
unsigned long unused, reg_ebx = 0;
if ( !HTSupported() )
return 1;
if ( !cpuid(1,unused,reg_ebx,unused,unused) )
return 1;
return (uint8) ((reg_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( _WIN32 )
#if !defined( _X360 )
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();
int64 freq = (end.m_Int64 - start.m_Int64) << scale;
if ( freq == 0 )
{
// Steam was seeing Divide-by-zero crashes on some Windows machines due to
// WIN64_AMD_DUALCORE_TIMER_WORKAROUND that can cause rdtsc to effectively
// stop. Staging doesn't have the workaround but I'm checking in the fix
// anyway. Return a plausible speed and get on with our day.
freq = 2000000000;
}
return freq;
#else
return 3200000000LL;
#endif
#elif defined(POSIX)
int64 freq =(int64)CalculateCPUFreq();
if ( freq == 0 ) // couldn't calculate clock speed
{
Error( "Unable to determine CPU Frequency\n" );
}
return freq;
#endif
}
const CPUInformation* GetCPUInformation()
{
static CPUInformation pi;
// 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();
#if defined(_WIN32) && !defined( _X360 )
SYSTEM_INFO si;
ZeroMemory( &si, sizeof(si) );
GetSystemInfo( &si );
pi.m_nPhysicalProcessors = (unsigned char)(si.dwNumberOfProcessors / pi.m_nLogicalProcessors);
pi.m_nLogicalProcessors = (unsigned char)(pi.m_nLogicalProcessors * pi.m_nPhysicalProcessors);
// 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 )
{
pi.m_nPhysicalProcessors = 1;
pi.m_nLogicalProcessors = 1;
}
#elif defined( _X360 )
pi.m_nPhysicalProcessors = 3;
pi.m_nLogicalProcessors = 6;
#elif defined(_LINUX)
// TODO: poll /dev/cpuinfo when we have some benefits from multithreading
FILE *fpCpuInfo = fopen( "/proc/cpuinfo", "r" );
if ( fpCpuInfo )
{
int nLogicalProcs = 0;
int nProcId = -1, nCoreId = -1;
const int kMaxPhysicalCores = 128;
int anKnownIds[kMaxPhysicalCores];
int nKnownIdCount = 0;
char buf[255];
while ( fgets( buf, ARRAYSIZE(buf), fpCpuInfo ) )
{
if ( char *value = strchr( buf, ':' ) )
{
for ( char *p = value - 1; p > buf && isspace((unsigned char)*p); --p )
{
*p = 0;
}
for ( char *p = buf; p < value && *p; ++p )
{
*p = tolower((unsigned char)*p);
}
if ( !strcmp( buf, "processor" ) )
{
++nLogicalProcs;
nProcId = nCoreId = -1;
}
else if ( !strcmp( buf, "physical id" ) )
{
nProcId = atoi( value+1 );
}
else if ( !strcmp( buf, "core id" ) )
{
nCoreId = atoi( value+1 );
}
if (nProcId != -1 && nCoreId != -1) // as soon as we have a complete id, process it
{
int i = 0, nId = (nProcId << 16) + nCoreId;
while ( i < nKnownIdCount && anKnownIds[i] != nId ) { ++i; }
if ( i == nKnownIdCount && nKnownIdCount < kMaxPhysicalCores )
anKnownIds[nKnownIdCount++] = nId;
nProcId = nCoreId = -1;
}
}
}
fclose( fpCpuInfo );
pi.m_nLogicalProcessors = MAX( 1, nLogicalProcs );
pi.m_nPhysicalProcessors = MAX( 1, nKnownIdCount );
}
else
{
pi.m_nPhysicalProcessors = 1;
pi.m_nLogicalProcessors = 1;
Assert( !"couldn't read cpu information from /proc/cpuinfo" );
}
#elif defined(OSX)
int mib[2], num_cpu = 1;
size_t len;
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
len = sizeof(num_cpu);
sysctl(mib, 2, &num_cpu, &len, NULL, 0);
pi.m_nPhysicalProcessors = num_cpu;
pi.m_nLogicalProcessors = num_cpu;
#endif
// Determine Processor Features:
pi.m_bRDTSC = CheckRDTSCTechnology();
pi.m_bCMOV = CheckCMOVTechnology();
pi.m_bFCMOV = CheckFCMOVTechnology();
pi.m_bMMX = CheckMMXTechnology();
pi.m_bSSE = CheckSSETechnology();
pi.m_bSSE2 = CheckSSE2Technology();
pi.m_bSSE3 = CheckSSE3Technology();
pi.m_bSSSE3 = CheckSSSE3Technology();
pi.m_bSSE4a = CheckSSE4aTechnology();
pi.m_bSSE41 = CheckSSE41Technology();
pi.m_bSSE42 = CheckSSE42Technology();
pi.m_b3DNow = Check3DNowTechnology();
pi.m_szProcessorID = (tchar*)GetProcessorVendorId();
pi.m_bHT = HTSupported();
unsigned long eax, ebx, edx, ecx;
if (cpuid(1, eax, ebx, ecx, edx))
{
pi.m_nModel = eax; // full CPU model info
pi.m_nFeatures[0] = edx; // x87+ features
pi.m_nFeatures[1] = ecx; // sse3+ features
pi.m_nFeatures[2] = ebx; // some additional features
}
return &pi;
}

177
tier0/cpu_posix.cpp Normal file
View File

@ -0,0 +1,177 @@
//========= Copyright 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>
#define rdtsc(x) \
__asm__ __volatile__ ("rdtsc" : "=A" (x))
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)
{
int64 d = v1 - v2;
if (d >= 0)
return d;
else
return -d;
}
#ifdef OSX
// Mac
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
// Linux
uint64 GetCPUFreqFromPROC()
{
double mhz = 0;
char line[1024], *s, search_str[] = "cpu MHz";
/* open proc/cpuinfo */
FILE *fp = fopen( "/proc/cpuinfo", "r" );
if (fp == 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;
}
}
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
// Try to open cpuinfo_max_freq. If the kernel was built with cpu scaling support disabled, this will fail.
FILE *fp = fopen( "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r" );
if ( fp )
{
char buf[ 256 ];
uint64 retVal = 0;
buf[ 0 ] = 0;
if( fread( buf, 1, ARRAYSIZE( buf ), fp ) )
{
retVal = ( uint64 )atoll( buf );
}
fclose(fp);
if( retVal )
{
return retVal * 1000;
}
}
// 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 );
rdtsc( start_tsc );
usleep( 5000 ); // sleep for 5 msec
gettimeofday( &end_time.m_TimeVal, 0 );
rdtsc( end_tsc );
// end_time - start_time calls into the overloaded TimeVal operator- way above, and returns a double.
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;
}

132
tier0/cpu_usage.cpp Normal file
View File

@ -0,0 +1,132 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: return the cpu usage as a float value
//
// On win32 this is 0.0 to 1.0 indicating the amount of CPU time used
// On posix its the load avg from the last minute
//
// On win32 you need to call this once in a while. Every few seconds.
// First call returns zero
//=============================================================================//
#include "pch_tier0.h"
#include "tier0/platform.h"
#ifdef WIN32
#include <windows.h>
#define SystemBasicInformation 0
#define SystemPerformanceInformation 2
#define SystemTimeInformation 3
#define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + (double)((x).LowPart))
typedef struct
{
DWORD dwUnknown1;
ULONG uKeMaximumIncrement;
ULONG uPageSize;
ULONG uMmNumberOfPhysicalPages;
ULONG uMmLowestPhysicalPage;
ULONG uMmHighestPhysicalPage;
ULONG uAllocationGranularity;
PVOID pLowestUserAddress;
PVOID pMmHighestUserAddress;
ULONG uKeActiveProcessors;
BYTE bKeNumberProcessors;
BYTE bUnknown2;
WORD wUnknown3;
} SYSTEM_BASIC_INFORMATION;
typedef struct
{
LARGE_INTEGER liIdleTime;
DWORD dwSpare[80];
} SYSTEM_PERFORMANCE_INFORMATION;
typedef struct
{
LARGE_INTEGER liKeBootTime;
LARGE_INTEGER liKeSystemTime;
LARGE_INTEGER liExpTimeZoneBias;
ULONG uCurrentTimeZoneId;
DWORD dwReserved;
} SYSTEM_TIME_INFORMATION;
typedef LONG (WINAPI *PROCNTQSI)(UINT,PVOID,ULONG,PULONG);
static PROCNTQSI NtQuerySystemInformation;
float GetCPUUsage()
{
SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
SYSTEM_TIME_INFORMATION SysTimeInfo;
SYSTEM_BASIC_INFORMATION SysBaseInfo;
double dbIdleTime;
double dbSystemTime;
LONG status;
static LARGE_INTEGER liOldIdleTime = {0,0};
static LARGE_INTEGER liOldSystemTime = {0,0};
if ( !NtQuerySystemInformation)
{
NtQuerySystemInformation = (PROCNTQSI)GetProcAddress( GetModuleHandle("ntdll"), "NtQuerySystemInformation" );
if ( !NtQuerySystemInformation )
return(0);
}
// get number of processors in the system
status = NtQuerySystemInformation( SystemBasicInformation,&SysBaseInfo,sizeof(SysBaseInfo),NULL );
if ( status != NO_ERROR )
return(0);
// get new system time
status = NtQuerySystemInformation( SystemTimeInformation,&SysTimeInfo,sizeof(SysTimeInfo),0 );
if ( status!=NO_ERROR )
return(0);
// get new CPU's idle time
status = NtQuerySystemInformation( SystemPerformanceInformation,&SysPerfInfo,sizeof(SysPerfInfo),NULL );
if ( status != NO_ERROR )
return(0);
// if it's a first call - skip it
if ( liOldIdleTime.QuadPart != 0 )
{
// CurrentValue = NewValue - OldValue
dbIdleTime = Li2Double(SysPerfInfo.liIdleTime) - Li2Double(liOldIdleTime);
dbSystemTime = Li2Double(SysTimeInfo.liKeSystemTime) - Li2Double(liOldSystemTime);
// CurrentCpuIdle = IdleTime / SystemTime
dbIdleTime = dbIdleTime / dbSystemTime / (double)SysBaseInfo.bKeNumberProcessors;
// CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
// dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SysBaseInfo.bKeNumberProcessors + 0.5;
}
else
{
dbIdleTime = 1.0f;
}
// store new CPU's idle and system time
liOldIdleTime = SysPerfInfo.liIdleTime;
liOldSystemTime = SysTimeInfo.liKeSystemTime;
return (float)(1.0f - dbIdleTime);
}
#endif // WIN32
#ifdef POSIX
#include <stdlib.h>
float GetCPUUsage()
{
double loadavg[3];
getloadavg( loadavg, 3 );
return loadavg[0];
}
#endif //POSIX

398
tier0/cpumonitoring.cpp Normal file
View File

@ -0,0 +1,398 @@
//============ 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
#undef min
#undef max
#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 = 0; //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 s_QPCfrequency;
static LARGE_INTEGER s_QPCbase;
static void InitializeGetTime()
{
QueryPerformanceFrequency( &s_QPCfrequency );
QueryPerformanceCounter( &s_QPCbase );
}
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 - s_QPCbase.QuadPart ) / double( s_QPCfrequency.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_frequencies[ 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_frequencies[ 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_frequencies[ 0 ];
float maxActualFreq = s_frequencies[ 0 ];
for ( DWORD i = 1; i < g_numCPUs; ++i )
{
minActualFreq = std::min( minActualFreq, s_frequencies[ i ] );
maxActualFreq = std::max( maxActualFreq, s_frequencies[ 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

948
tier0/dbg.cpp Normal file
View File

@ -0,0 +1,948 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "pch_tier0.h"
#include "tier0/minidump.h"
#if defined( _WIN32 ) && !defined( _X360 )
#include "tier0/valve_off.h"
#define WIN_32_LEAN_AND_MEAN
#include <windows.h> // Currently needed for IsBadReadPtr and IsBadWritePtr
#pragma comment(lib,"user32.lib") // For MessageBox
#endif
#include <assert.h>
#include <malloc.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 <math.h>
#if defined( _X360 )
#include "xbox/xbox_console.h"
#endif
#include "tier0/etwprof.h"
#ifndef STEAM
#define PvRealloc realloc
#define PvAlloc malloc
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// internal structures
//-----------------------------------------------------------------------------
enum
{
MAX_GROUP_NAME_LENGTH = 48
};
struct SpewGroup_t
{
tchar m_GroupName[MAX_GROUP_NAME_LENGTH];
int m_Level;
};
// 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;
}
//-----------------------------------------------------------------------------
DBG_INTERFACE SpewRetval_t DefaultSpewFunc( SpewType_t type, const tchar *pMsg )
{
#ifdef _X360
if ( XBX_IsConsoleConnected() )
{
// send to console
XBX_DebugString( XMAKECOLOR( 0,0,0 ), pMsg );
}
else
#endif
{
_tprintf( _T("%s"), pMsg );
#ifdef _WIN32
Plat_DebugString( pMsg );
#endif
}
if ( type == SPEW_ASSERT )
{
#ifndef WIN32
// Non-win32
bool bRaiseOnAssert = getenv( "RAISE_ON_ASSERT" ) || !!CommandLine()->FindParm( "-raiseonassert" );
#elif defined( _DEBUG )
// Win32 debug
bool bRaiseOnAssert = true;
#else
// Win32 release
bool bRaiseOnAssert = !!CommandLine()->FindParm( "-raiseonassert" );
#endif
return bRaiseOnAssert ? SPEW_DEBUGGER : SPEW_CONTINUE;
}
else if ( type == SPEW_ERROR )
return SPEW_ABORT;
else
return SPEW_CONTINUE;
}
//-----------------------------------------------------------------------------
DBG_INTERFACE SpewRetval_t DefaultSpewFuncAbortOnAsserts( SpewType_t type, const tchar *pMsg )
{
SpewRetval_t r = DefaultSpewFunc( type, pMsg );
if ( type == SPEW_ASSERT )
r = SPEW_ABORT;
return r;
}
//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
static SpewOutputFunc_t s_SpewOutputFunc = DefaultSpewFunc;
static AssertFailedNotifyFunc_t s_AssertFailedNotifyFunc = NULL;
static const tchar* s_pFileName;
static int s_Line;
static SpewType_t s_SpewType;
static SpewGroup_t* s_pSpewGroups = 0;
static int s_GroupCount = 0;
static int s_DefaultLevel = 0;
#if !defined( _X360 )
static Color s_DefaultOutputColor( 255, 255, 255, 255 );
#else
static Color s_DefaultOutputColor( 0, 0, 0, 255 );
#endif
// Only useable from within a spew function
struct SpewInfo_t
{
const Color* m_pSpewOutputColor;
const tchar* m_pSpewOutputGroup;
int m_nSpewOutputLevel;
};
CThreadLocalPtr<SpewInfo_t> g_pSpewInfo;
// Standard groups
static const tchar* s_pDeveloper = _T("developer");
static const tchar* s_pConsole = _T("console");
static const tchar* s_pNetwork = _T("network");
enum StandardSpewGroup_t
{
GROUP_DEVELOPER = 0,
GROUP_CONSOLE,
GROUP_NETWORK,
GROUP_COUNT,
};
static int s_pGroupIndices[GROUP_COUNT] = { -1, -1, -1 };
static const char *s_pGroupNames[GROUP_COUNT] = { s_pDeveloper, s_pConsole, s_pNetwork };
//-----------------------------------------------------------------------------
// Spew output management.
//-----------------------------------------------------------------------------
void SpewOutputFunc( SpewOutputFunc_t func )
{
s_SpewOutputFunc = func ? func : DefaultSpewFunc;
}
SpewOutputFunc_t GetSpewOutputFunc( void )
{
if( s_SpewOutputFunc )
return s_SpewOutputFunc;
return DefaultSpewFunc;
}
void _ExitOnFatalAssert( const tchar* pFile, int line )
{
_SpewMessage( _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() )
{
char rgchSuffix[512];
_snprintf( rgchSuffix, sizeof(rgchSuffix), "fatalassert_%s_%d", SkipToFname( pFile ), line );
WriteMiniDump( rgchSuffix );
}
DevMsg( 1, _T("_ExitOnFatalAssert\n") );
exit( EXIT_FAILURE );
}
//-----------------------------------------------------------------------------
// Templates to assist in validating pointers:
//-----------------------------------------------------------------------------
DBG_INTERFACE void _AssertValidReadPtr( void* ptr, int count/* = 1*/ )
{
Assert( !count || ptr );
}
DBG_INTERFACE void _AssertValidWritePtr( void* ptr, int count/* = 1*/ )
{
Assert( !count || ptr );
}
DBG_INTERFACE void _AssertValidReadWritePtr( void* ptr, int count/* = 1*/ )
{
Assert( !count || ptr );
}
#undef AssertValidStringPtr
DBG_INTERFACE void AssertValidStringPtr( const tchar* ptr, int maxchar/* = 0xFFFFFF */ )
{
Assert( ptr );
}
//-----------------------------------------------------------------------------
// Should be called only inside a SpewOutputFunc_t, returns groupname, level, color
//-----------------------------------------------------------------------------
const tchar* GetSpewOutputGroup( void )
{
SpewInfo_t *pSpewInfo = g_pSpewInfo;
assert( pSpewInfo );
if ( pSpewInfo )
return pSpewInfo->m_pSpewOutputGroup;
return NULL;
}
int GetSpewOutputLevel( void )
{
SpewInfo_t *pSpewInfo = g_pSpewInfo;
assert( pSpewInfo );
if ( pSpewInfo )
return pSpewInfo->m_nSpewOutputLevel;
return -1;
}
const Color* GetSpewOutputColor( void )
{
SpewInfo_t *pSpewInfo = g_pSpewInfo;
assert( pSpewInfo );
if ( pSpewInfo )
return pSpewInfo->m_pSpewOutputColor;
return &s_DefaultOutputColor;
}
//-----------------------------------------------------------------------------
// Spew functions
//-----------------------------------------------------------------------------
DBG_INTERFACE void _SpewInfo( SpewType_t type, const tchar* pFile, int line )
{
// Only grab the file name. Ignore the path.
s_pFileName = SkipToFname( pFile );
s_Line = line;
s_SpewType = type;
}
static SpewRetval_t _SpewMessage( SpewType_t spewType, const char *pGroupName, int nLevel, const Color *pColor, const tchar* pMsgFormat, va_list args )
{
tchar pTempBuffer[5020];
assert( _tcslen( pMsgFormat ) < sizeof( pTempBuffer) ); // check that we won't artifically truncate the string
/* Printf the file and line for warning + assert only... */
int len = 0;
if ( spewType == SPEW_ASSERT )
{
len = _sntprintf( pTempBuffer, sizeof( pTempBuffer ) - 1, _T("%s (%d) : "), s_pFileName, s_Line );
}
if ( len == -1 )
return SPEW_ABORT;
/* Create the message.... */
int val= _vsntprintf( &pTempBuffer[len], sizeof( pTempBuffer ) - len - 1, pMsgFormat, args );
if ( val == -1 )
return SPEW_ABORT;
len += val;
assert( len * sizeof(*pMsgFormat) < sizeof(pTempBuffer) ); /* use normal assert here; to avoid recursion. */
// Add \n for warning and assert
if ( spewType == SPEW_ASSERT )
{
len += _stprintf( &pTempBuffer[len], _T("\n") );
}
assert( len < sizeof(pTempBuffer)/sizeof(pTempBuffer[0]) - 1 ); /* use normal assert here; to avoid recursion. */
assert( s_SpewOutputFunc );
/* direct it to the appropriate target(s) */
SpewRetval_t ret;
assert( g_pSpewInfo == NULL );
SpewInfo_t spewInfo =
{
pColor,
pGroupName,
nLevel
};
g_pSpewInfo = &spewInfo;
ret = s_SpewOutputFunc( spewType, pTempBuffer );
g_pSpewInfo = (int)NULL;
switch (ret)
{
// Asserts put the break into the macro so it occurs in the right place
case SPEW_DEBUGGER:
if ( spewType != SPEW_ASSERT )
{
DebuggerBreak();
}
break;
case SPEW_ABORT:
{
// MessageBox(NULL,"Error in _SpewMessage","Error",MB_OK);
// ConMsg( _T("Exiting on SPEW_ABORT\n") );
exit(1);
}
}
return ret;
}
#include "tier0/valve_off.h"
FORCEINLINE SpewRetval_t _SpewMessage( SpewType_t spewType, const tchar* pMsgFormat, va_list args )
{
return _SpewMessage( spewType, "", 0, &s_DefaultOutputColor, pMsgFormat, args );
}
//-----------------------------------------------------------------------------
// Find a group, return true if found, false if not. Return in ind the
// index of the found group, or the index of the group right before where the
// group should be inserted into the list to maintain sorted order.
//-----------------------------------------------------------------------------
bool FindSpewGroup( const tchar* pGroupName, int* pInd )
{
int s = 0;
if (s_GroupCount)
{
int e = (int)(s_GroupCount - 1);
while ( s <= e )
{
int m = (s+e) >> 1;
int cmp = _tcsicmp( pGroupName, s_pSpewGroups[m].m_GroupName );
if ( !cmp )
{
*pInd = m;
return true;
}
if ( cmp < 0 )
e = m - 1;
else
s = m + 1;
}
}
*pInd = s;
return false;
}
//-----------------------------------------------------------------------------
// True if -hushasserts was passed on command line.
//-----------------------------------------------------------------------------
bool HushAsserts()
{
#ifdef DBGFLAG_ASSERT
static bool s_bHushAsserts = !!CommandLine()->FindParm( "-hushasserts" );
return s_bHushAsserts;
#else
return true;
#endif
}
//-----------------------------------------------------------------------------
// Tests to see if a particular spew is active
//-----------------------------------------------------------------------------
bool IsSpewActive( const tchar* pGroupName, int level )
{
// If we don't find the spew group, use the default level.
int ind;
if ( FindSpewGroup( pGroupName, &ind ) )
return s_pSpewGroups[ind].m_Level >= level;
else
return s_DefaultLevel >= level;
}
inline bool IsSpewActive( StandardSpewGroup_t group, int level )
{
// If we don't find the spew group, use the default level.
if ( s_pGroupIndices[group] >= 0 )
return s_pSpewGroups[ s_pGroupIndices[group] ].m_Level >= level;
return s_DefaultLevel >= level;
}
SpewRetval_t _SpewMessage( const tchar* pMsgFormat, ... )
{
va_list args;
va_start( args, pMsgFormat );
SpewRetval_t ret = _SpewMessage( s_SpewType, pMsgFormat, args );
va_end(args);
return ret;
}
SpewRetval_t _DSpewMessage( const tchar *pGroupName, int level, const tchar* pMsgFormat, ... )
{
if( !IsSpewActive( pGroupName, level ) )
return SPEW_CONTINUE;
va_list args;
va_start( args, pMsgFormat );
SpewRetval_t ret = _SpewMessage( s_SpewType, pGroupName, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
return ret;
}
DBG_INTERFACE SpewRetval_t ColorSpewMessage( SpewType_t type, const Color *pColor, const tchar* pMsgFormat, ... )
{
va_list args;
va_start( args, pMsgFormat );
SpewRetval_t ret = _SpewMessage( type, "", 0, pColor, pMsgFormat, args );
va_end(args);
return ret;
}
void Msg( const tchar* pMsgFormat, ... )
{
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, pMsgFormat, args );
va_end(args);
}
void DMsg( const tchar *pGroupName, int level, const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( pGroupName, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, pGroupName, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void MsgV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist )
{
_SpewMessage( SPEW_MESSAGE, pMsg, arglist );
}
void Warning( const tchar *pMsgFormat, ... )
{
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_WARNING, pMsgFormat, args );
va_end(args);
}
void DWarning( const tchar *pGroupName, int level, const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( pGroupName, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_WARNING, pGroupName, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void WarningV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist )
{
_SpewMessage( SPEW_WARNING, pMsg, arglist );
}
void Log( const tchar *pMsgFormat, ... )
{
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_LOG, pMsgFormat, args );
va_end(args);
}
void DLog( const tchar *pGroupName, int level, const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( pGroupName, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_LOG, pGroupName, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void LogV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist )
{
_SpewMessage( SPEW_LOG, pMsg, arglist );
}
void Error( const tchar *pMsgFormat, ... )
{
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_ERROR, pMsgFormat, args );
va_end(args);
}
void ErrorV( PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist )
{
_SpewMessage( SPEW_ERROR, pMsg, arglist );
}
//-----------------------------------------------------------------------------
// 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, ... )
{
if( !IsSpewActive( GROUP_DEVELOPER, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, s_pDeveloper, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void DevWarning( int level, const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_DEVELOPER, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_WARNING, s_pDeveloper, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void DevLog( int level, const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_DEVELOPER, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_LOG, s_pDeveloper, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void DevMsg( const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_DEVELOPER, 1 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, s_pDeveloper, 1, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void DevWarning( const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_DEVELOPER, 1 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_WARNING, s_pDeveloper, 1, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void DevLog( const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_DEVELOPER, 1 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_LOG, s_pDeveloper, 1, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
//-----------------------------------------------------------------------------
// A couple of super-common dynamic spew messages, here for convenience
// These looked at the "console" group, print if it's level 1 or higher
//-----------------------------------------------------------------------------
void ConColorMsg( int level, const Color& clr, const tchar* pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, s_pConsole, level, &clr, pMsgFormat, args );
va_end(args);
}
void ConMsg( int level, const tchar* pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, s_pConsole, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void ConWarning( int level, const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_WARNING, s_pConsole, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void ConLog( int level, const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_LOG, s_pConsole, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void ConColorMsg( const Color& clr, const tchar* pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, 1 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, s_pConsole, 1, &clr, pMsgFormat, args );
va_end(args);
}
void ConMsg( const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, 1 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, s_pConsole, 1, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void ConWarning( const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, 1 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_WARNING, s_pConsole, 1, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void ConLog( const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, 1 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_LOG, s_pConsole, 1, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void ConDColorMsg( const Color& clr, const tchar* pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, 2 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, s_pConsole, 2, &clr, pMsgFormat, args );
va_end(args);
}
void ConDMsg( const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, 2 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, s_pConsole, 2, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void ConDWarning( const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, 2 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_WARNING, s_pConsole, 2, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void ConDLog( const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_CONSOLE, 2 ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_LOG, s_pConsole, 2, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
//-----------------------------------------------------------------------------
// A couple of super-common dynamic spew messages, here for convenience
// These looked at the "network" group, print if it's level 1 or higher
//-----------------------------------------------------------------------------
void NetMsg( int level, const tchar* pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_NETWORK, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_MESSAGE, s_pNetwork, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void NetWarning( int level, const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_NETWORK, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_WARNING, s_pNetwork, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
void NetLog( int level, const tchar *pMsgFormat, ... )
{
if( !IsSpewActive( GROUP_NETWORK, level ) )
return;
va_list args;
va_start( args, pMsgFormat );
_SpewMessage( SPEW_LOG, s_pNetwork, level, &s_DefaultOutputColor, pMsgFormat, args );
va_end(args);
}
#include "tier0/valve_on.h"
//-----------------------------------------------------------------------------
// Sets the priority level for a spew group
//-----------------------------------------------------------------------------
void SpewActivate( const tchar* pGroupName, int level )
{
Assert( pGroupName );
// check for the default group first...
if ((pGroupName[0] == '*') && (pGroupName[1] == '\0'))
{
s_DefaultLevel = level;
return;
}
// Normal case, search in group list using binary search.
// If not found, grow the list of groups and insert it into the
// right place to maintain sorted order. Then set the level.
int ind;
if ( !FindSpewGroup( pGroupName, &ind ) )
{
// not defined yet, insert an entry.
++s_GroupCount;
if ( s_pSpewGroups )
{
s_pSpewGroups = (SpewGroup_t*)PvRealloc( s_pSpewGroups,
s_GroupCount * sizeof(SpewGroup_t) );
// shift elements down to preserve order
int numToMove = s_GroupCount - ind - 1;
memmove( &s_pSpewGroups[ind+1], &s_pSpewGroups[ind],
numToMove * sizeof(SpewGroup_t) );
// Update standard groups
for ( int i = 0; i < GROUP_COUNT; ++i )
{
if ( ( ind <= s_pGroupIndices[i] ) && ( s_pGroupIndices[i] >= 0 ) )
{
++s_pGroupIndices[i];
}
}
}
else
{
s_pSpewGroups = (SpewGroup_t*)PvAlloc( s_GroupCount * sizeof(SpewGroup_t) );
}
Assert( _tcslen( pGroupName ) < MAX_GROUP_NAME_LENGTH );
_tcscpy( s_pSpewGroups[ind].m_GroupName, pGroupName );
// Update standard groups
for ( int i = 0; i < GROUP_COUNT; ++i )
{
if ( ( s_pGroupIndices[i] < 0 ) && !_tcsicmp( s_pGroupNames[i], pGroupName ) )
{
s_pGroupIndices[i] = ind;
break;
}
}
}
s_pSpewGroups[ind].m_Level = level;
}
// 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.
DBG_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;
}
#ifdef DBGFLAG_VALIDATE
void ValidateSpew( CValidator &validator )
{
validator.Push( _T("Spew globals"), NULL, _T("Global") );
validator.ClaimMemory( s_pSpewGroups );
validator.Pop( );
}
#endif // DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// 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_bShouldLogToETW = false;
static bool s_bChecked = false;
static bool s_bFirstWrite = false;
if ( !s_bChecked )
{
s_bShouldLog = ( IsX360() || CommandLine()->CheckParm( "-profile" ) ) ? 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 );
_vsnprintf( string, sizeof( string ), fmt, argptr );
va_end( argptr );
float curStamp = Plat_FloatTime();
#if defined( _X360 )
XBX_rTimeStampLog( curStamp, 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 );
}
}
s_LastStamp = curStamp;
}
//-----------------------------------------------------------------------------
// 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 );
}

148
tier0/dynfunction.cpp Normal file
View File

@ -0,0 +1,148 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Shared library loading and symbol lookup.
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include "tier0/dynfunction.h"
#if defined(WIN32)
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
#if 1
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;
}

406
tier0/etwprof.cpp Normal file
View File

@ -0,0 +1,406 @@
//========= Copyright 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"
#ifdef ETW_MARKS_ENABLED
#include <memory>
// 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 );
// RegHandle is an _Out_ parameter and must always be initialized.
*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.
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

407
tier0/extendedtrace.cpp Normal file
View File

@ -0,0 +1,407 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//////////////////////////////////////////////////////////////////////////////////////
//
// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com
// For companies(Austin,TX): If you would like to get my resume, send an email.
//
// The source is free, but if you want to use it, mention my name and e-mail address
//
// History:
// 1.0 Initial version Zoltan Csizmadia
//
//////////////////////////////////////////////////////////////////////////////////////
//
// ExtendedTrace.cpp
//
// Include StdAfx.h, if you're using precompiled
// header through StdAfx.h
//#include "stdafx.h"
#if defined(_DEBUG) && defined(WIN32)
#include "tier0/valve_off.h"
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <ImageHlp.h>
#include "tier0/valve_on.h"
#include "ExtendedTrace.h"
#define BUFFERSIZE 0x200
extern void OutputDebugStringFormat( const char *pMsg, ... );
// Unicode safe char* -> TCHAR* conversion
void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut )
{
#if defined(UNICODE)||defined(_UNICODE)
ULONG index = 0;
PCSTR lpAct = lpszIn;
for( ; ; lpAct++ )
{
lpszOut[index++] = (TCHAR)(*lpAct);
if ( *lpAct == 0 )
break;
}
#else
// This is trivial :)
strcpy( lpszOut, lpszIn );
#endif
}
// Let's figure out the path for the symbol files
// Search path= ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + lpszIniPath
// Note: There is no size check for lpszSymbolPath!
void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath )
{
CHAR lpszPath[BUFFERSIZE];
// Creating the default path
// ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;"
strcpy( lpszSymbolPath, "." );
// environment variable _NT_SYMBOL_PATH
if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE ) )
{
strcat( lpszSymbolPath, ";" );
strcat( lpszSymbolPath, lpszPath );
}
// environment variable _NT_ALTERNATE_SYMBOL_PATH
if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE ) )
{
strcat( lpszSymbolPath, ";" );
strcat( lpszSymbolPath, lpszPath );
}
// environment variable SYSTEMROOT
if ( GetEnvironmentVariableA( "SYSTEMROOT", lpszPath, BUFFERSIZE ) )
{
strcat( lpszSymbolPath, ";" );
strcat( lpszSymbolPath, lpszPath );
strcat( lpszSymbolPath, ";" );
// SYSTEMROOT\System32
strcat( lpszSymbolPath, lpszPath );
strcat( lpszSymbolPath, "\\System32" );
}
// Add user defined path
if ( lpszIniPath != NULL )
if ( lpszIniPath[0] != '\0' )
{
strcat( lpszSymbolPath, ";" );
strcat( lpszSymbolPath, lpszIniPath );
}
}
// Uninitialize the loaded symbol files
BOOL UninitSymInfo()
{
return SymCleanup( GetCurrentProcess() );
}
// Initializes the symbol files
BOOL InitSymInfo( PCSTR lpszInitialSymbolPath )
{
CHAR lpszSymbolPath[BUFFERSIZE];
DWORD symOptions = SymGetOptions();
symOptions |= SYMOPT_LOAD_LINES;
symOptions &= ~SYMOPT_UNDNAME;
SymSetOptions( symOptions );
// Get the search path for the symbol files
InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath );
return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE);
}
// Get the module name from a given address
BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule )
{
BOOL ret = FALSE;
IMAGEHLP_MODULE moduleInfo;
::ZeroMemory( &moduleInfo, sizeof(moduleInfo) );
moduleInfo.SizeOfStruct = sizeof(moduleInfo);
if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) )
{
// Got it!
PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule );
ret = TRUE;
}
else
// Not found :(
_tcscpy( lpszModule, _T("?") );
return ret;
}
// Get function prototype and parameter info from ip address and stack address
BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol )
{
BOOL ret = FALSE;
DWORD dwDisp = 0;
DWORD dwSymSize = 10000;
TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?");
CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
LPTSTR lpszParamSep = NULL;
LPCTSTR lpszParsed = lpszUnDSymbol;
PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize );
::ZeroMemory( pSym, dwSymSize );
pSym->SizeOfStruct = dwSymSize;
pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL);
// Set the default to unknown
_tcscpy( lpszSymbol, _T("?") );
// Get symbol info for IP
if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) )
{
// Make the symbol readable for humans
UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE,
UNDNAME_COMPLETE |
UNDNAME_NO_THISTYPE |
UNDNAME_NO_SPECIAL_SYMS |
UNDNAME_NO_MEMBER_TYPE |
UNDNAME_NO_MS_KEYWORDS |
UNDNAME_NO_ACCESS_SPECIFIERS );
// Symbol information is ANSI string
PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol );
// I am just smarter than the symbol file :)
if ( _tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0 )
_tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)"));
else
if ( _tcscmp(lpszUnDSymbol, _T("_main")) == 0 )
_tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)"));
else
if ( _tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0 )
_tcscpy(lpszUnDSymbol, _T("mainCRTStartup()"));
else
if ( _tcscmp(lpszUnDSymbol, _T("_wmain")) == 0 )
_tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)"));
else
if ( _tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0 )
_tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()"));
lpszSymbol[0] = _T('\0');
// Let's go through the stack, and modify the function prototype, and insert the actual
// parameter values from the stack
if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL)
{
ULONG index = 0;
for( ; ; index++ )
{
lpszParamSep = _tcschr( lpszParsed, _T(',') );
if ( lpszParamSep == NULL )
break;
*lpszParamSep = _T('\0');
_tcscat( lpszSymbol, lpszParsed );
_stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) );
lpszParsed = lpszParamSep + 1;
}
lpszParamSep = _tcschr( lpszParsed, _T(')') );
if ( lpszParamSep != NULL )
{
*lpszParamSep = _T('\0');
_tcscat( lpszSymbol, lpszParsed );
_stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) );
lpszParsed = lpszParamSep + 1;
}
}
_tcscat( lpszSymbol, lpszParsed );
ret = TRUE;
}
GlobalFree( pSym );
return ret;
}
// Get source file name and line number from IP address
// The output format is: "sourcefile(linenumber)" or
// "modulename!address" or
// "address"
BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo )
{
BOOL ret = FALSE;
IMAGEHLP_LINE lineInfo;
DWORD dwDisp;
TCHAR lpszFileName[BUFFERSIZE] = _T("");
TCHAR lpModuleInfo[BUFFERSIZE] = _T("");
_tcscpy( lpszSourceInfo, _T("?(?)") );
::ZeroMemory( &lineInfo, sizeof( lineInfo ) );
lineInfo.SizeOfStruct = sizeof( lineInfo );
if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) )
{
// Got it. Let's use "sourcefile(linenumber)" format
PCSTR2LPTSTR( lineInfo.FileName, lpszFileName );
_stprintf( lpszSourceInfo, _T("%s(%d)"), lpszFileName, lineInfo.LineNumber );
ret = TRUE;
}
else
{
// There is no source file information. :(
// Let's use the "modulename!address" format
GetModuleNameFromAddress( address, lpModuleInfo );
if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0'))
// There is no modulename information. :((
// Let's use the "address" format
_stprintf( lpszSourceInfo, _T("?") );
else
_stprintf( lpszSourceInfo, _T("%s"), lpModuleInfo );
ret = FALSE;
}
return ret;
}
// TRACE message with source link.
// The format is: sourcefile(linenumber) : message
void SrcLinkTrace( LPCTSTR lpszMessage, LPCTSTR lpszFileName, ULONG nLineNumber )
{
OutputDebugStringFormat(
_T("%s(%d) : %s"),
lpszFileName,
nLineNumber,
lpszMessage );
}
void StackTrace( HANDLE hThread, LPCTSTR lpszMessage )
{
STACKFRAME callStack;
BOOL bResult;
CONTEXT context;
TCHAR symInfo[BUFFERSIZE] = _T("?");
TCHAR srcInfo[BUFFERSIZE] = _T("?");
HANDLE hProcess = GetCurrentProcess();
// If it's not this thread, let's suspend it, and resume it at the end
if ( hThread != GetCurrentThread() )
if ( SuspendThread( hThread ) == -1 )
{
// whaaat ?!
OutputDebugStringFormat( _T("Call stack info(thread=0x%X) failed.\n") );
return;
}
::ZeroMemory( &context, sizeof(context) );
context.ContextFlags = CONTEXT_FULL;
if ( !GetThreadContext( hThread, &context ) )
{
OutputDebugStringFormat( _T("Call stack info(thread=0x%X) failed.\n") );
return;
}
::ZeroMemory( &callStack, sizeof(callStack) );
callStack.AddrPC.Offset = context.Eip;
callStack.AddrStack.Offset = context.Esp;
callStack.AddrFrame.Offset = context.Ebp;
callStack.AddrPC.Mode = AddrModeFlat;
callStack.AddrStack.Mode = AddrModeFlat;
callStack.AddrFrame.Mode = AddrModeFlat;
for( ULONG index = 0; ; index++ )
{
bResult = StackWalk(
IMAGE_FILE_MACHINE_I386,
hProcess,
hThread,
&callStack,
NULL,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL);
if ( index == 0 )
continue;
if( !bResult || callStack.AddrFrame.Offset == 0 )
break;
GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo );
GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo );
OutputDebugStringFormat( _T(" %s : %s\n"), srcInfo, symInfo );
}
if ( hThread != GetCurrentThread() )
ResumeThread( hThread );
}
void FunctionParameterInfo()
{
STACKFRAME callStack;
BOOL bResult = FALSE;
CONTEXT context;
TCHAR lpszFnInfo[BUFFERSIZE];
HANDLE hProcess = GetCurrentProcess();
HANDLE hThread = GetCurrentThread();
::ZeroMemory( &context, sizeof(context) );
context.ContextFlags = CONTEXT_FULL;
if ( !GetThreadContext( hThread, &context ) )
{
OutputDebugStringFormat( _T("Function info(thread=0x%X) failed.\n") );
return;
}
::ZeroMemory( &callStack, sizeof(callStack) );
callStack.AddrPC.Offset = context.Eip;
callStack.AddrStack.Offset = context.Esp;
callStack.AddrFrame.Offset = context.Ebp;
callStack.AddrPC.Mode = AddrModeFlat;
callStack.AddrStack.Mode = AddrModeFlat;
callStack.AddrFrame.Mode = AddrModeFlat;
for( ULONG index = 0; index < 2; index++ )
{
bResult = StackWalk(
IMAGE_FILE_MACHINE_I386,
hProcess,
hThread,
&callStack,
NULL,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL);
}
if ( bResult && callStack.AddrFrame.Offset != 0)
{
GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, lpszFnInfo );
OutputDebugStringFormat( _T("Function info(thread=0x%X) : %s\n"), GetCurrentThreadId(), lpszFnInfo );
}
else
OutputDebugStringFormat( _T("Function info(thread=0x%X) failed.\n") );
}
#endif //_DEBUG && WIN32

63
tier0/extendedtrace.h Normal file
View File

@ -0,0 +1,63 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//////////////////////////////////////////////////////////////////////////////////////
//
// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com
// For companies(Austin,TX): If you would like to get my resume, send an email.
//
// The source is free, but if you want to use it, mention my name and e-mail address
//
// History:
// 1.0 Initial version Zoltan Csizmadia
//
//////////////////////////////////////////////////////////////////////////////////////
//
// ExtendedTrace.h
//
#ifndef EXTENDEDTRACE_H_INCLUDED
#define EXTENDEDTRACE_H_INCLUDED
#if defined(_DEBUG) && defined(WIN32)
#pragma comment( lib, "imagehlp.lib" )
#if defined(_AFX) || defined(_AFXDLL)
#define TRACEF TRACE
#else
#define TRACEF OutputDebugStringFormat
void OutputDebugStringFormat( PRINTF_FORMAT_STRING LPCTSTR, ... );
#endif
#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) InitSymInfo( IniSymbolPath )
#define EXTENDEDTRACEUNINITIALIZE() UninitSymInfo()
#define SRCLINKTRACECUSTOM( Msg, File, Line) SrcLinkTrace( Msg, File, Line )
#define SRCLINKTRACE( Msg ) SrcLinkTrace( Msg, __FILE__, __LINE__ )
#define FNPARAMTRACE() FunctionParameterInfo()
#define STACKTRACEMSG( Msg ) StackTrace( Msg )
#define STACKTRACE() StackTrace( GetCurrentThread(), _T("") )
#define THREADSTACKTRACEMSG( hThread, Msg ) StackTrace( hThread, Msg )
#define THREADSTACKTRACE( hThread ) StackTrace( hThread, _T("") )
BOOL InitSymInfo( PCSTR );
BOOL UninitSymInfo();
void SrcLinkTrace( LPCTSTR, LPCTSTR, ULONG );
void StackTrace( HANDLE, LPCTSTR );
void FunctionParameterInfo();
#else
#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) ((void)0)
#define EXTENDEDTRACEUNINITIALIZE() ((void)0)
#define TRACEF ((void)0)
#define SRCLINKTRACECUSTOM( Msg, File, Line) ((void)0)
#define SRCLINKTRACE( Msg ) ((void)0)
#define FNPARAMTRACE() ((void)0)
#define STACKTRACEMSG( Msg ) ((void)0)
#define STACKTRACE() ((void)0)
#define THREADSTACKTRACEMSG( hThread, Msg ) ((void)0)
#define THREADSTACKTRACE( hThread ) ((void)0)
#endif
#endif

51
tier0/fasttimer.cpp Normal file
View File

@ -0,0 +1,51 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include <stdio.h>
#include "tier0/fasttimer.h"
// g_dwClockSpeed is only exported for backwards compatibility.
PLATFORM_INTERFACE unsigned long g_dwClockSpeed;
uint64 g_ClockSpeed; // Clocks/sec
// Storing CPU clock speed in a 32-bit variable is dangerous and can already overflow
// on some CPUs. This variable is deprecated.
unsigned long g_dwClockSpeed;
#if defined( _X360 ) && defined( _CERT )
unsigned long g_dwFakeFastCounter;
#endif
double g_ClockSpeedMicrosecondsMultiplier;
double g_ClockSpeedMillisecondsMultiplier;
double g_ClockSpeedSecondsMultiplier;
// Constructor init the clock speed.
CClockSpeedInit g_ClockSpeedInit CONSTRUCT_EARLY;
void CClockSpeedInit::Init()
{
const CPUInformation& cpuinfo = *GetCPUInformation();
g_ClockSpeed = cpuinfo.m_Speed;
// cycle counter runs as doc'd at 1/64 Xbox 3.2GHz clock speed, thus 50 Mhz
if ( IsX360() )
{
g_ClockSpeed /= 64L;
}
// Avoid integer overflow when writing to g_dwClockSpeed
if ( g_ClockSpeed <= ULONG_MAX )
g_dwClockSpeed = (unsigned long)g_ClockSpeed;
else
g_dwClockSpeed = ULONG_MAX;
g_ClockSpeedMicrosecondsMultiplier = 1000000.0 / (double)g_ClockSpeed;
g_ClockSpeedMillisecondsMultiplier = 1000.0 / (double)g_ClockSpeed;
g_ClockSpeedSecondsMultiplier = 1.0 / (double)g_ClockSpeed;
}

96
tier0/mem.cpp Normal file
View File

@ -0,0 +1,96 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Memory allocation!
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include "tier0/mem.h"
#include <malloc.h>
#include "tier0/dbg.h"
#include "tier0/minidump.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.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;
static bool s_oomerror_called = false;
void MemAllocOOMError( size_t nSize )
{
if ( !s_oomerror_called )
{
s_oomerror_called = true;
MinidumpUserStreamInfoAppend( "MemAllocOOMError: %u bytes\n", (uint)nSize );
//$ TODO: Need a good error message here.
// A basic advice to try lowering texture settings is just most-likely to help users who are exhausting address
// space, but not necessarily the cause. Ideally the engine wouldn't let you get here because of too-high settings.
Error( "Out of memory or address space. Texture quality setting may be too high.\n" );
}
}
//-----------------------------------------------------------------------------
// 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 < 1024 * 1024)
{
s_nBufAllocSize = 1024 * 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

172
tier0/mem_helpers.cpp Normal file
View File

@ -0,0 +1,172 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "pch_tier0.h"
#include "mem_helpers.h"
#include <string.h>
#include <malloc.h>
bool g_bInitMemory = true;
#ifdef POSIX
void DoApplyMemoryInitializations( void *pMem, int 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, int nSize )
{
unsigned long *pCurDWord = (unsigned long*)pMem;
int nDWords = nSize >> 2;
while ( nDWords )
{
*pCurDWord = 0xffeeffee;
++pCurDWord;
--nDWords;
}
unsigned char *pCurChar = (unsigned char*)pCurDWord;
int nBytes = nSize & 3;
int iOffset = 0;
while ( nBytes )
{
*pCurChar = ((unsigned char*)&g_dwFeeFee)[iOffset];
++iOffset;
--nBytes;
++pCurChar;
}
}
void InitializeToRandom( void *pMem, int nSize )
{
unsigned char *pOut = (unsigned char *)pMem;
for ( int i=0; i < nSize; i++ )
{
pOut[i] = g_RandomValues[(g_iCurRandomValueOffset & 255)];
++g_iCurRandomValueOffset;
}
}
void DoApplyMemoryInitializations( void *pMem, int 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
{
#ifdef _DEBUG
// 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

38
tier0/mem_helpers.h Normal file
View File

@ -0,0 +1,38 @@
//========= Copyright 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, int nSize );
size_t CalcHeapUsed();
// Call this to reserve the bottom 4 GB of memory in order to ensure that we will
// get crashes if we put pointers in DWORDs or ints. This will be a NOP on some
// platforms (Xbox 360, PS3, 32-bit Windows, etc.)
void ReserveBottomMemory();
#endif // MEM_HELPERS_H

1955
tier0/memdbg.cpp Normal file

File diff suppressed because it is too large Load Diff

9
tier0/meminit.cpp Normal file
View File

@ -0,0 +1,9 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Memory allocation!
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"

1900
tier0/memstd.cpp Normal file

File diff suppressed because it is too large Load Diff

292
tier0/memstd.h Normal file
View File

@ -0,0 +1,292 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//-----------------------------------------------------------------------------
// NOTE! This should never be called directly from leaf code
// Just use new,delete,malloc,free etc. They will call into this eventually
//-----------------------------------------------------------------------------
#include "pch_tier0.h"
#if defined(_WIN32)
#if !defined(_X360)
#define WIN_32_LEAN_AND_MEAN
#include <windows.h>
#else
#undef Verify
#define _XBOX
#include <xtl.h>
#undef _XBOX
#include "xbox/xbox_win32stubs.h"
#endif
#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"
#pragma pack(4)
#ifdef _X360
#define USE_PHYSICAL_SMALL_BLOCK_HEAP 1
#endif
// #define NO_SBH 1
#define MIN_SBH_BLOCK 8
#define MIN_SBH_ALIGN 8
#define MAX_SBH_BLOCK 2048
#define MAX_POOL_REGION (4*1024*1024)
#if !defined(_X360)
#define SBH_PAGE_SIZE (4*1024)
#define COMMIT_SIZE (16*SBH_PAGE_SIZE)
#else
#define SBH_PAGE_SIZE (64*1024)
#define COMMIT_SIZE (SBH_PAGE_SIZE)
#endif
#if _M_X64
#define NUM_POOLS 34
#else
#define NUM_POOLS 42
#endif
// SBH not enabled for LINUX right now. Unlike on Windows, we can't globally hook malloc. Well,
// we can and did in override_init_hook(), but that unfortunately causes all malloc functions
// to get hooked - including the nVidia driver, etc. And these hooks appear to happen after
// nVidia has alloc'd some memory and it crashes when they try to free that.
// So we need things to work without this global hook - which means we rely on memdbgon.h / memdbgoff.h.
// Unfortunately, that stuff always comes in source files after the headers are included, and
// that means any alloc calls in the header files call the real libc functions. It's a mess.
// I believe I've cleaned most of it up, and it appears to be working. However right now we are totally
// gated on other performance issues, and the SBH doesn't give us any win, so I've disabled it for now.
// Once those perf issues are worked out, it might make sense to do perf tests with SBH, libc, and tcmalloc.
//
//$ #if defined( _WIN32 ) || defined( _PS3 ) || defined( LINUX )
#if defined( _WIN32 ) || defined( _PS3 )
#define MEM_SBH_ENABLED 1
#endif
class ALIGN16 CSmallBlockPool
{
public:
void Init( unsigned nBlockSize, byte *pBase, unsigned initialCommit = 0 );
size_t GetBlockSize();
bool IsOwner( void *p );
void *Alloc();
void Free( void *p );
int CountFreeBlocks();
int GetCommittedSize();
int CountCommittedBlocks();
int CountAllocatedBlocks();
int Compact();
private:
typedef TSLNodeBase_t FreeBlock_t;
class CFreeList : public CTSListBase
{
public:
void Push( void *p ) { CTSListBase::Push( (TSLNodeBase_t *)p ); }
};
CFreeList m_FreeList;
unsigned m_nBlockSize;
CInterlockedPtr<byte> m_pNextAlloc;
byte * m_pCommitLimit;
byte * m_pAllocLimit;
byte * m_pBase;
CThreadFastMutex m_CommitMutex;
} ALIGN16_POST;
class ALIGN16 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( FILE *pFile = NULL );
int Compact();
private:
CSmallBlockPool *FindPool( size_t nBytes );
CSmallBlockPool *FindPool( void *p );
CSmallBlockPool *m_PoolLookup[MAX_SBH_BLOCK >> 2];
CSmallBlockPool m_Pools[NUM_POOLS];
byte *m_pBase;
byte *m_pLimit;
} ALIGN16_POST;
#ifdef USE_PHYSICAL_SMALL_BLOCK_HEAP
#define BYTES_X360_SBH (32*1024*1024)
#define PAGESIZE_X360_SBH (64*1024)
class CX360SmallBlockPool
{
public:
void Init( unsigned nBlockSize );
size_t GetBlockSize();
bool IsOwner( void *p );
void *Alloc();
void Free( void *p );
int CountFreeBlocks();
int GetCommittedSize();
int CountCommittedBlocks();
int CountAllocatedBlocks();
static CX360SmallBlockPool *FindPool( void *p )
{
int index = (size_t)((byte *)p - gm_pPhysicalBase) / PAGESIZE_X360_SBH;
if ( index < 0 || index >= ARRAYSIZE(gm_AddressToPool) )
return NULL;
return gm_AddressToPool[ index ];
}
private:
friend class CX360SmallBlockHeap;
typedef TSLNodeBase_t FreeBlock_t;
class CFreeList : public CTSListBase
{
public:
void Push( void *p ) { CTSListBase::Push( (TSLNodeBase_t *)p ); }
};
CFreeList m_FreeList;
unsigned m_nBlockSize;
unsigned m_CommittedSize;
CInterlockedPtr<byte> m_pNextAlloc;
byte * m_pCurBlockEnd;
CThreadFastMutex m_CommitMutex;
static CX360SmallBlockPool *gm_AddressToPool[BYTES_X360_SBH/PAGESIZE_X360_SBH];
static byte *gm_pPhysicalBlock;
static byte *gm_pPhysicalBase;
static byte *gm_pPhysicalLimit;
};
class CX360SmallBlockHeap
{
public:
CX360SmallBlockHeap();
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( FILE *pFile = NULL );
CSmallBlockHeap *GetStandardSBH();
private:
CX360SmallBlockPool *FindPool( size_t nBytes );
CX360SmallBlockPool *FindPool( void *p );
CX360SmallBlockPool *m_PoolLookup[MAX_SBH_BLOCK >> 2];
CX360SmallBlockPool m_Pools[NUM_POOLS];
};
#endif
class ALIGN16 CStdMemAlloc : public IMemAlloc
{
public:
CStdMemAlloc()
: m_pfnFailHandler( DefaultFailHandler ),
m_sMemoryAllocFailed( (size_t)0 )
{
// Make sure that we return 64-bit addresses in 64-bit builds.
ReserveBottomMemory();
}
// 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 );
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, int nLogicalSize, int nActualSize, unsigned nTime ) {}
virtual void RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime ) {}
virtual int GetVersion() { return MEMALLOC_VERSION; }
virtual void CompactHeap();
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 ) {}
#ifdef MEM_SBH_ENABLED
CSmallBlockHeap m_SmallBlockHeap;
#ifdef USE_PHYSICAL_SMALL_BLOCK_HEAP
CX360SmallBlockHeap m_LargePageSmallBlockHeap;
#endif
#endif
#if defined( _MEMTEST )
virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment );
#endif
virtual size_t MemoryAllocFailed();
void SetCRTAllocFailed( size_t nMemSize );
MemAllocFailHandler_t m_pfnFailHandler;
size_t m_sMemoryAllocFailed;
} ALIGN16_POST;

485
tier0/memvalidate.cpp Normal file
View File

@ -0,0 +1,485 @@
//========= Copyright 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"
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 ) {}
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;
IMemAlloc *g_pMemAlloc = &s_ValidateAlloc;
//-----------------------------------------------------------------------------
// 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

687
tier0/minidump.cpp Normal file
View File

@ -0,0 +1,687 @@
//========= 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];
//-----------------------------------------------------------------------------
// 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 ( 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%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
( 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 )
{
if ( 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 ( 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
// User minidump stream info comment strings.
//
// Single header string of 512 bytes set via MinidumpUserStreamInfoSetHeader.
static char g_UserStreamInfoHeader[ 512 ];
// Array of 32 round robin 128 byte strings set via MinidumpUserStreamInfoAppend.
static char g_UserStreamInfo[ 64 ][ 128 ];
static int g_UserStreamInfoIndex = 0;
// Set the single g_UserStreamInfoHeader string.
void MinidumpUserStreamInfoSetHeader( const char *pFormat, ... )
{
va_list marker;
va_start( marker, pFormat );
_vsnprintf( g_UserStreamInfoHeader, ARRAYSIZE( g_UserStreamInfoHeader ), pFormat, marker );
g_UserStreamInfoHeader[ ARRAYSIZE( g_UserStreamInfoHeader ) - 1 ] = 0;
va_end( marker );
}
// Set the next comment in the g_UserStreamInfo array.
void MinidumpUserStreamInfoAppend( const char *pFormat, ... )
{
va_list marker;
char *pData = g_UserStreamInfo[ g_UserStreamInfoIndex ];
const int DataSize = ARRAYSIZE( g_UserStreamInfo[ g_UserStreamInfoIndex ] );
// Add tick count just so we have a general idea of when this event happened.
_snprintf( pData, DataSize, "[%x]", Plat_MSTime() );
pData[ DataSize - 1 ] = 0;
size_t HeaderLen = strlen( pData );
va_start( marker, pFormat );
_vsnprintf( pData + HeaderLen, DataSize - HeaderLen, pFormat, marker );
pData[ DataSize - 1 ] = 0;
va_end( marker );
// Bump up index, and go back to 0 if we've hit the end.
g_UserStreamInfoIndex++;
if( g_UserStreamInfoIndex >= ARRAYSIZE( g_UserStreamInfo ) )
{
g_UserStreamInfoIndex = 0;
}
}
// Retrieve the string given the Index.
// Index 0: header string
// Index 1+: comment string
// Returns NULL when you've reached the end of the comment string array
// Empty strings ("\0") can be returned if comment hasn't been set
const char *MinidumpUserStreamInfoGet( int Index )
{
if( ( Index < 0 ) || ( Index >= (ARRAYSIZE( g_UserStreamInfo ) + 1) ) ) //+1 because we map 0 to the header
return NULL;
if( Index == 0 )
return g_UserStreamInfoHeader;
Index = ( (Index + (ARRAYSIZE( g_UserStreamInfo ) - 1)) + //subtract 1 in a way that circularly wraps. Since 0 maps to the header, the comment indices are 1 based
g_UserStreamInfoIndex ) //start with our oldest comment
% ARRAYSIZE( g_UserStreamInfo ); //circular buffer wrapping
return g_UserStreamInfo[ Index ];
}

9
tier0/pch_tier0.cpp Normal file
View File

@ -0,0 +1,9 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"

57
tier0/pch_tier0.h Normal file
View File

@ -0,0 +1,57 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $NoKeywords: $
//=============================================================================//
#if defined(_WIN32) && !defined(_X360)
#define WIN32_LEAN_AND_MEAN
// 0x0601 gives us access to Windows 7 features. We have to be careful not to
// depend on these features because we still support Windows XP.
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <assert.h>
#endif
// tier0
#include "tier0/basetypes.h"
#include "tier0/dbgflag.h"
#include "tier0/dbg.h"
#ifdef STEAM
#include "tier0/memhook.h"
#endif
#include "tier0/validator.h"
// First include standard libraries
#include "tier0/valve_off.h"
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <malloc.h>
#include <memory.h>
#include <ctype.h>
#include <limits.h>
#include <assert.h>
#include "tier0/valve_minmax_off.h" // GCC 4.2.2 headers screw up our min/max defs.
#include <map>
#include "tier0/valve_minmax_on.h" // GCC 4.2.2 headers screw up our min/max defs.
#include <stddef.h>
#ifdef POSIX
#include <ctype.h>
#include <limits.h>
#define _MAX_PATH PATH_MAX
#endif
#include "tier0/valve_on.h"

478
tier0/platform.cpp Normal file
View File

@ -0,0 +1,478 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "pch_tier0.h"
#include <time.h>
#if defined(_WIN32) && !defined(_X360)
#include <errno.h>
#endif
#include <assert.h>
#include "tier0/platform.h"
#include "tier0/minidump.h"
#ifdef _X360
#include "xbox/xbox_console.h"
#include "xbox/xbox_win32stubs.h"
#else
#include "tier0/vcrmode.h"
#endif
#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
//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
#ifndef _X360
extern VCRMode_t g_VCRMode;
#endif
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;
// Benchmark mode uses this heavy-handed method
static bool g_bBenchmarkMode = false;
static double g_FakeBenchmarkTime = 0;
static double g_FakeBenchmarkTimeInc = 1.0 / 66.0;
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);
}
}
bool Plat_IsInBenchmarkMode()
{
return g_bBenchmarkMode;
}
void Plat_SetBenchmarkMode( bool bBenchmark )
{
g_bBenchmarkMode = bBenchmark;
}
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 );
}
void GetCurrentDate( int *pDay, int *pMonth, int *pYear )
{
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_mday;
*pMonth = pNewTime->tm_mon + 1;
*pYear = pNewTime->tm_year + 1900;
}
// 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 )
{
if ( EINVAL == ctime_s( buf, bufsize, timep ) )
return NULL;
else
return buf;
}
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;
}
// Wraps the thread-safe versions of gmtime
struct tm *Plat_gmtime( const time_t *timep, struct tm *result )
{
if ( EINVAL == gmtime_s( result, timep ) )
return NULL;
else
return result;
}
time_t Plat_timegm( struct tm *timeptr )
{
return _mkgmtime( timeptr );
}
// Wraps the thread-safe versions of localtime
struct tm *Plat_localtime( const time_t *timep, struct tm *result )
{
if ( EINVAL == localtime_s( result, timep ) )
return NULL;
else
return result;
}
bool vtune( bool resume )
{
#ifndef _X360
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( _WIN32 ) && !defined( _X360 )
return (IsDebuggerPresent() != 0);
#elif defined( _WIN32 ) && defined( _X360 )
return (XBX_IsDebuggerPresent() != 0);
#elif defined( LINUX )
#error This code is implemented in platform_posix.cpp
#else
return false;
#endif
}
void Plat_DebugString( const char * psz )
{
#if defined( _WIN32 ) && !defined( _X360 )
::OutputDebugStringA( psz );
#elif defined( _WIN32 ) && defined( _X360 )
XBX_OutputDebugString( psz );
#endif
}
const tchar *Plat_GetCommandLine()
{
#ifdef TCHAR_IS_WCHAR
return GetCommandLineW();
#else
return GetCommandLine();
#endif
}
bool GetMemoryInformation( MemoryInformation *pOutMemoryInfo )
{
if ( !pOutMemoryInfo )
return false;
MEMORYSTATUSEX memStat;
ZeroMemory( &memStat, sizeof( MEMORYSTATUSEX ) );
memStat.dwLength = sizeof( MEMORYSTATUSEX );
if ( !GlobalMemoryStatusEx( &memStat ) )
return false;
const uint cOneMb = 1024 * 1024;
switch ( pOutMemoryInfo->m_nStructVersion )
{
case 0:
( *pOutMemoryInfo ).m_nPhysicalRamMbTotal = memStat.ullTotalPhys / cOneMb;
( *pOutMemoryInfo ).m_nPhysicalRamMbAvailable = memStat.ullAvailPhys / cOneMb;
( *pOutMemoryInfo ).m_nVirtualRamMbTotal = memStat.ullTotalVirtual / cOneMb;
( *pOutMemoryInfo ).m_nVirtualRamMbAvailable = memStat.ullAvailVirtual / cOneMb;
break;
default:
return false;
};
return true;
}
const char *Plat_GetCommandLineA()
{
return GetCommandLineA();
}
//--------------------------------------------------------------------------------------------------
// Watchdog timer
//--------------------------------------------------------------------------------------------------
void Plat_BeginWatchdogTimer( int nSecs )
{
}
void Plat_EndWatchdogTimer( void )
{
}
int Plat_GetWatchdogTime( void )
{
return 0;
}
void Plat_SetWatchdogHandlerFunction( Plat_WatchDogHandlerFunction_t function )
{
}
bool Is64BitOS()
{
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
static LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( GetModuleHandle("kernel32"), "IsWow64Process" );
static BOOL bIs64bit = FALSE;
static bool bInitialized = false;
if ( bInitialized )
return bIs64bit == (BOOL)TRUE;
else
{
bInitialized = true;
return pfnIsWow64Process && pfnIsWow64Process(GetCurrentProcess(), &bIs64bit) && bIs64bit;
}
}
// -------------------------------------------------------------------------------------------------- //
// 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
#ifndef _X360
CRITICAL_SECTION g_AllocCS;
class CAllocCSInit
{
public:
CAllocCSInit()
{
InitializeCriticalSection( &g_AllocCS );
}
} g_AllocCSInit;
#endif
#ifndef _X360
PLATFORM_INTERFACE void* Plat_Alloc( unsigned long size )
{
EnterCriticalSection( &g_AllocCS );
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
void *pRet = g_pMemAlloc->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;
}
}
#endif
#ifndef _X360
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;
}
}
#endif
#ifndef _X360
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 );
}
#endif
#ifndef _X360
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
PLATFORM_INTERFACE void Plat_SetAllocErrorFn( Plat_AllocErrorFn fn )
{
g_AllocError = fn;
}
#endif
#endif
#endif

1083
tier0/platform_posix.cpp Normal file

File diff suppressed because it is too large Load Diff

88
tier0/pmc360.cpp Normal file
View File

@ -0,0 +1,88 @@
//========= Copyright 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

152
tier0/pme.cpp Normal file
View File

@ -0,0 +1,152 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#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"
//-----------------------------------------------------------------------------
// 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") );
}
g_VProfCurrentProfile.PMEInitialized( bInit );
}
//-----------------------------------------------------------------------------
// Purpose: Shutdown
//-----------------------------------------------------------------------------
void ShutdownPME( void )
{
PME *pPME = PME::Instance();
if ( pPME )
{
pPME->SetProcessPriority( ProcessPriorityNormal );
}
g_VProfCurrentProfile.PMEInitialized( false );
}
//=============================================================================
//
// 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
View File

@ -0,0 +1,51 @@
//========= Copyright 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 )
{
}

37
tier0/progressbar.cpp Normal file
View File

@ -0,0 +1,37 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include "vstdlib/pch_vstdlib.h"
#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;
}

35
tier0/resource.h Normal file
View File

@ -0,0 +1,35 @@
//========= Copyright 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

111
tier0/security.cpp Normal file
View File

@ -0,0 +1,111 @@
//========= Copyright 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/vcrmode.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
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
}

125
tier0/security_linux.cpp Normal file
View File

@ -0,0 +1,125 @@
//========= Copyright 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/vcrmode.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
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
}

1707
tier0/stacktools.cpp Normal file

File diff suppressed because it is too large Load Diff

310
tier0/systeminformation.cpp Normal file
View File

@ -0,0 +1,310 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "pch_tier0.h"
#include "tier0/platform.h"
#include "tier0/systeminformation.h"
#ifdef IS_WINDOWS_PC
#include <windows.h>
#include <tchar.h>
#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 )
{
}
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.
//
unsigned long 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.
//
unsigned long 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

219
tier0/thread.cpp Normal file
View File

@ -0,0 +1,219 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Thread management routines
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include "tier0/valve_off.h"
#ifdef _WIN32
#define WIN_32_LEAN_AND_MEAN
#include <windows.h>
#include <assert.h>
#include <tlhelp32.h>
#endif
#include "tier0/platform.h"
#include "tier0/dbg.h"
#include "tier0/threadtools.h"
unsigned long Plat_GetCurrentThreadID()
{
return ThreadGetCurrentId();
}
#if defined(_WIN32) && defined(_M_IX86)
static CThreadMutex s_BreakpointStateMutex;
struct X86HardwareBreakpointState_t
{
const void *pAddress[4];
char nWatchBytes[4];
bool bBreakOnRead[4];
};
static X86HardwareBreakpointState_t s_BreakpointState = { {0,0,0,0}, {0,0,0,0}, {false,false,false,false} };
static void X86ApplyBreakpointsToThread( DWORD dwThreadId )
{
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
X86HardwareBreakpointState_t *pState = &s_BreakpointState;
ctx.Dr0 = (DWORD) pState->pAddress[0];
ctx.Dr1 = (DWORD) pState->pAddress[1];
ctx.Dr2 = (DWORD) pState->pAddress[2];
ctx.Dr3 = (DWORD) pState->pAddress[3];
ctx.Dr7 = (DWORD) 0;
for ( int i = 0; i < 4; ++i )
{
if ( pState->pAddress[i] && pState->nWatchBytes[i] )
{
ctx.Dr7 |= 1 << (i*2);
if ( pState->bBreakOnRead[i] )
ctx.Dr7 |= 3 << (16 + i*4);
else
ctx.Dr7 |= 1 << (16 + i*4);
switch ( pState->nWatchBytes[i] )
{
case 1: ctx.Dr7 |= 0<<(18 + i*4); break;
case 2: ctx.Dr7 |= 1<<(18 + i*4); break;
case 4: ctx.Dr7 |= 3<<(18 + i*4); break;
case 8: ctx.Dr7 |= 2<<(18 + i*4); break;
}
}
}
// Freeze this thread, adjust its breakpoint state
HANDLE hThread = OpenThread( THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT, FALSE, dwThreadId );
if ( hThread != INVALID_HANDLE_VALUE )
{
if ( SuspendThread( hThread ) != -1 )
{
SetThreadContext( hThread, &ctx );
ResumeThread( hThread );
}
CloseHandle( hThread );
}
}
static DWORD STDCALL ThreadProcX86SetDataBreakpoints( LPVOID pvParam )
{
if ( pvParam )
{
X86ApplyBreakpointsToThread( *(unsigned long*)pvParam );
return 0;
}
// This function races against creation and destruction of new threads. Try to execute as quickly as possible.
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST );
DWORD dwProcId = GetCurrentProcessId();
DWORD dwThisThreadId = GetCurrentThreadId();
HANDLE hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
if ( hSnap != INVALID_HANDLE_VALUE )
{
THREADENTRY32 threadEntry;
// Thread32First/Thread32Next may adjust dwSize to be smaller. It's weird. Read the doc.
const DWORD dwMinSize = (char*)(&threadEntry.th32OwnerProcessID + 1) - (char*)&threadEntry;
threadEntry.dwSize = sizeof( THREADENTRY32 );
BOOL bContinue = Thread32First( hSnap, &threadEntry );
while ( bContinue )
{
if ( threadEntry.dwSize >= dwMinSize )
{
if ( threadEntry.th32OwnerProcessID == dwProcId && threadEntry.th32ThreadID != dwThisThreadId )
{
X86ApplyBreakpointsToThread( threadEntry.th32ThreadID );
}
}
threadEntry.dwSize = sizeof( THREADENTRY32 );
bContinue = Thread32Next( hSnap, &threadEntry );
}
CloseHandle( hSnap );
}
return 0;
}
void Plat_SetHardwareDataBreakpoint( const void *pAddress, int nWatchBytes, bool bBreakOnRead )
{
Assert( pAddress );
Assert( nWatchBytes == 0 || nWatchBytes == 1 || nWatchBytes == 2 || nWatchBytes == 4 || nWatchBytes == 8 );
s_BreakpointStateMutex.Lock();
if ( nWatchBytes == 0 )
{
for ( int i = 0; i < 4; ++i )
{
if ( pAddress == s_BreakpointState.pAddress[i] )
{
for ( ; i < 3; ++i )
{
s_BreakpointState.pAddress[i] = s_BreakpointState.pAddress[i+1];
s_BreakpointState.nWatchBytes[i] = s_BreakpointState.nWatchBytes[i+1];
s_BreakpointState.bBreakOnRead[i] = s_BreakpointState.bBreakOnRead[i+1];
}
s_BreakpointState.pAddress[3] = NULL;
s_BreakpointState.nWatchBytes[3] = 0;
s_BreakpointState.bBreakOnRead[3] = false;
break;
}
}
}
else
{
// Replace first null entry or first existing entry at this address, or bump all entries down
for ( int i = 0; i < 4; ++i )
{
if ( s_BreakpointState.pAddress[i] && s_BreakpointState.pAddress[i] != pAddress && i < 3 )
continue;
// Last iteration.
if ( s_BreakpointState.pAddress[i] && s_BreakpointState.pAddress[i] != pAddress )
{
// Full up. Shift table down, drop least recently set
for ( int j = 0; j < 3; ++j )
{
s_BreakpointState.pAddress[j] = s_BreakpointState.pAddress[j+1];
s_BreakpointState.nWatchBytes[j] = s_BreakpointState.nWatchBytes[j+1];
s_BreakpointState.bBreakOnRead[j] = s_BreakpointState.bBreakOnRead[j+1];
}
}
s_BreakpointState.pAddress[i] = pAddress;
s_BreakpointState.nWatchBytes[i] = nWatchBytes;
s_BreakpointState.bBreakOnRead[i] = bBreakOnRead;
break;
}
}
HANDLE hWorkThread = CreateThread( NULL, NULL, &ThreadProcX86SetDataBreakpoints, NULL, 0, NULL );
if ( hWorkThread != INVALID_HANDLE_VALUE )
{
WaitForSingleObject( hWorkThread, INFINITE );
CloseHandle( hWorkThread );
}
s_BreakpointStateMutex.Unlock();
}
void Plat_ApplyHardwareDataBreakpointsToNewThread( unsigned long dwThreadID )
{
s_BreakpointStateMutex.Lock();
if ( dwThreadID != GetCurrentThreadId() )
{
X86ApplyBreakpointsToThread( dwThreadID );
}
else
{
HANDLE hWorkThread = CreateThread( NULL, NULL, &ThreadProcX86SetDataBreakpoints, &dwThreadID, 0, NULL );
if ( hWorkThread != INVALID_HANDLE_VALUE )
{
WaitForSingleObject( hWorkThread, INFINITE );
CloseHandle( hWorkThread );
}
}
s_BreakpointStateMutex.Unlock();
}
#else
void Plat_SetHardwareDataBreakpoint( const void *pAddress, int nWatchBytes, bool bBreakOnRead )
{
// no impl on this platform yet
}
void Plat_ApplyHardwareDataBreakpointsToNewThread( unsigned long dwThreadID )
{
// no impl on this platform yet
}
#endif

2404
tier0/threadtools.cpp Normal file

File diff suppressed because it is too large Load Diff

267
tier0/tier0.vpc Normal file
View File

@ -0,0 +1,267 @@
//-----------------------------------------------------------------------------
// TIER0.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$MacroRequired "PLATSUBDIR"
$Macro SRCDIR ".."
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
$Configuration
{
$General
{
// 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 "$SRCDIR\lib\public\$(TargetName).lib" [$X360]
}
$Compiler
{
$PreprocessorDefinitions "$BASE;TIER0_DLL_EXPORT"
$Create/UsePrecompiledHeader "Use Precompiled Header (/Yu)"
$Create/UsePCHThroughFile "pch_tier0.h"
$PrecompiledHeaderFile "$(IntDir)/tier0.pch"
}
$Compiler [$WINDOWS]
{
$AdditionalIncludeDirectories "$BASE;..\public\WindowsSDK"
}
$Linker
{
$AdditionalDependencies "$BASE ws2_32.lib" [$WINDOWS]
// pc publishes the import library directly
$ImportLibrary "$LIBPUBLIC\$(TargetName).lib" [$WINDOWS]
// 360 publishes the import library via a post build step
$ImportLibrary "$(TargetDir)\$(TargetName).lib" [$X360]
$ImportLibrary "$LIBPUBLIC\$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT" [$POSIX]
// 360 will auto generate a def file for this import library
$ModuleDefinitionFile " " [$X360]
$AdditionalOptions "$BASE /AUTODEF:xbox\xbox.def" [$X360]
$SystemLibraries "rt" [$LINUXALL]
}
$PreLinkEvent [$WINDOWS]
{
$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"
}
$PostBuildEvent [$X360]
{
// Publish the import lib
$CommandLine "if exist $(TargetDir)$(TargetName).lib copy $(TargetDir)$(TargetName).lib $SRCDIR\lib\public\$(TargetName).lib" "\n" \
"$BASE"
}
// tier0/vstdlib traditionally used "lib" prefix though nobody else seems to.
$General [$POSIX]
{
$GameOutputFile "$OUTBINDIR/$_IMPLIB_PREFIX$OUTBINNAME$_DLL_EXT"
}
$Linker [$POSIX]
{
$OutputFile "$(OBJ_DIR)/$_IMPLIB_PREFIX$OUTBINNAME$_DLL_EXT"
}
}
$Project
{
$Folder "Source Files" [$WINDOWS||$X360]
{
-$File "$SRCDIR\public\tier0\memoverride.cpp"
}
$Folder "Link Libraries"
{
-$ImpLib tier0
-$Lib tier1
-$Implib vstdlib
$Lib "$SRCDIR\thirdparty\telemetry\lib\telemetry32.link" [$WIN32]
$Lib "$SRCDIR\thirdparty\telemetry\lib\telemetry64.link" [$WIN64]
$LibExternal "$SRCDIR/thirdparty/telemetry/lib/libtelemetryx86.link" [$LINUX32]
$LibExternal "$SRCDIR/thirdparty/telemetry/lib/libtelemetryx64.link" [$LINUX64]
}
}
$Project "tier0"
{
$Folder "Source Files"
{
$File "assert_dialog.cpp"
$File "assert_dialog.rc" [$WINDOWS]
$File "commandline.cpp"
$File "cpu.cpp"
$File "cpumonitoring.cpp"
$File "cpu_posix.cpp" [$POSIX]
$File "cpu_usage.cpp"
$File "dbg.cpp"
$File "dynfunction.cpp"
$File "etwprof.cpp" [$WINDOWS]
$File "fasttimer.cpp"
$File "InterlockedCompareExchange128.masm" [$WIN64]
{
$Configuration
{
$CustomBuildStep
{
// General
$CommandLine "$QUOTE$(VCInstallDir)bin\x86_amd64\ml64.exe$QUOTE /nologo /c /Fo$QUOTE$(IntDir)\$(InputName).obj$QUOTE $QUOTE$(InputPath)$QUOTE"
$Description "Compiling $(InputName).masm"
$Outputs "$(IntDir)\$(InputName).obj"
}
}
}
$File "mem.cpp"
$File "mem_helpers.cpp"
$File "memdbg.cpp"
$File "memstd.cpp"
$File "memvalidate.cpp"
$File "minidump.cpp"
$File "pch_tier0.cpp"
{
$Configuration
{
$Compiler
{
$Create/UsePrecompiledHeader "Create Precompiled Header (/Yc)"
}
}
}
$File "platform.cpp" [$WINDOWS||$X360]
$File "platform_posix.cpp" [$POSIX]
$File "pmc360.cpp" [$X360]
$File "pme.cpp" [$WINDOWS]
$File "pme_posix.cpp" [$POSIX]
$File "PMELib.cpp" [$WINDOWS||$POSIX]
{
$Configuration
{
$Compiler
{
$Create/UsePrecompiledHeader "Not Using Precompiled Headers"
}
}
}
$File "progressbar.cpp"
$File "security.cpp"
$File "systeminformation.cpp"
$File "stacktools.cpp"
$File "thread.cpp" [$WINDOWS||$POSIX]
$File "threadtools.cpp"
$File "tier0_strtools.cpp"
$File "tslist.cpp"
$File "vcrmode.cpp" [$WINDOWS]
$File "vcrmode_posix.cpp" [$POSIX]
$File "vprof.cpp"
$File "win32consoleio.cpp" [$WINDOWS]
$File "../tier1/pathmatch.cpp" [$LINUXALL]
}
$folder "Header Files"
{
$File "$SRCDIR\public\tier0\basetypes.h"
$File "$SRCDIR\public\tier0\commonmacros.h"
$File "$SRCDIR\public\tier0\cpumonitoring.h"
$File "$SRCDIR\public\tier0\dbg.h"
$File "$SRCDIR\public\tier0\dbgflag.h"
$File "$SRCDIR\public\tier0\EventMasks.h"
$File "$SRCDIR\public\tier0\EventModes.h"
$File "$SRCDIR\public\tier0\etwprof.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\mem.h"
$File "$SRCDIR\public\tier0\memalloc.h"
$File "$SRCDIR\public\tier0\memdbgoff.h"
$File "$SRCDIR\public\tier0\memdbgon.h"
$File "$SRCDIR\public\tier0\minidump.h"
$File "$SRCDIR\public\tier0\P4PerformanceCounters.h"
$File "$SRCDIR\public\tier0\P5P6PerformanceCounters.h"
$File "pch_tier0.h"
$File "$SRCDIR\public\tier0\platform.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\systeminformation.h"
$File "$SRCDIR\public\tier0\threadtools.h"
$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\vcr_shared.h"
$File "$SRCDIR\public\tier0\vcrmode.h"
$File "$SRCDIR\public\tier0\vprof.h"
$File "$SRCDIR\public\tier0\wchartypes.h"
$File "$SRCDIR\public\tier0\xbox_codeline_defines.h"
$File "mem_helpers.h"
}
$Folder "DESKey" [$WINDOWS]
{
$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 "Manifest Files" [$WINDOWS]
{
$File "ValveETWProvider.man"
{
$Configuration
{
$CustomBuildStep
{
$CommandLine "..\DevTools\bin\mc.exe -um $(InputFilename) -z $(InputName)Events"
$Description "Compiling ETW manifest file"
$Outputs "$(InputName)Events.h;$(InputName)Events.rc"
}
}
}
}
}

18
tier0/tier0_exclude.vpc Normal file
View File

@ -0,0 +1,18 @@
//-----------------------------------------------------------------------------
// tier0_exclude.vpc
//
// Project Script
//-----------------------------------------------------------------------------
$Project
{
$Folder "Link Libraries"
{
// Should match the sites that include this
-$Lib "$LIBPUBLIC\tier0" [$POSIX && !$IS_LIB_PROJECT]
}
$Folder "Source Files"
{
-$File "$SRCDIR\public\tier0\memoverride.cpp"
}
}

View File

@ -0,0 +1,3 @@
$Conditional STATIC_LINK "1"
$Include "tier0.vpc"

53
tier0/tier0_strtools.cpp Normal file
View File

@ -0,0 +1,53 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
#include "pch_tier0.h"
#include "tier0_strtools.h"
#define TOLOWERC( x ) (( ( x >= 'A' ) && ( x <= 'Z' ) )?( x + 32 ) : x )
extern "C"
int V_tier0_stricmp(const char *s1, const char *s2 )
{
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;
}
}
}
}

3
tier0/tier0_strtools.h Normal file
View File

@ -0,0 +1,3 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
extern "C" int V_tier0_stricmp(const char *s1, const char *s2 );

538
tier0/tslist.cpp Normal file
View File

@ -0,0 +1,538 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "pch_tier0.h"
#include "tier0/tslist.h"
#include <list>
#include <stdlib.h>
#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#endif
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;
std::list<ThreadHandle_t> g_ThreadHandles;
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;
};
class CQueueOps : public CTestOps
{
void Push( int item )
{
g_TestQueue.PushItem( item );
g_nPushes++;
}
bool Pop( int *pResult )
{
if ( g_TestQueue.PopItem( pResult ) )
{
g_nPops++;
return true;
}
return false;
}
bool Validate()
{
return g_TestQueue.ValidateQueue();
}
bool IsEmpty()
{
return ( g_TestQueue.Count() == 0 );
}
} g_QueueOps;
class CListOps : public CTestOps
{
void Push( int item )
{
g_TestList.PushItem( item );
g_nPushes++;
}
bool Pop( int *pResult )
{
if ( g_TestList.PopItem( pResult ) )
{
g_nPops++;
return true;
}
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;
}
}
}
unsigned PopThreadFunc( void *)
{
ThreadSetDebugName( "PopThread" );
g_nPopThreads++;
g_nThreads++;
while ( !g_bStart )
{
ThreadSleep( 0 );
}
int ignored;
for (;;)
{
if ( !g_pTestOps->Pop( &ignored ) )
{
if ( g_nPushThreads == 0 )
{
// Pop the rest
while ( g_pTestOps->Pop( &ignored ) )
{
ThreadSleep( 0 );
}
break;
}
}
}
g_nThreads--;
g_nPopThreads--;
return 0;
}
unsigned PushThreadFunc( void * )
{
ThreadSetDebugName( "PushThread" );
g_nPushThreads++;
g_nThreads++;
while ( !g_bStart )
{
ThreadSleep( 0 );
}
while ( g_nTested < NUM_TEST )
{
g_pTestOps->Push( g_nTested );
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( 50 );
}
}
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");
}
while ( g_ThreadHandles.size() )
{
ThreadJoin( g_ThreadHandles.front(), 0 );
ReleaseThreadHandle( g_ThreadHandles.front() );
g_ThreadHandles.pop_front();
}
}
//--------------------------------------------------
//
// 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();
}
unsigned 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();
g_ThreadHandles.push_back( CreateSimpleThread( &PushThreadFunc, NULL ) );
for ( int i = 0; i < NUM_THREADS - 1; i++ )
{
ThreadHandle_t hThread = CreateSimpleThread( &PopThreadFunc, NULL );
g_ThreadHandles.push_back( hThread );
if ( bDistribute )
{
int32 mask = 1 << (i % NUM_PROCESSORS);
ThreadSetAffinity( hThread, mask );
}
}
TestWait();
TestEnd();
}
void MTPushSTPop( bool bDistribute )
{
Msg( "%s test: multithread push, single thread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
TestStart();
g_ThreadHandles.push_back( CreateSimpleThread( &PopThreadFunc, NULL ) );
for ( int i = 0; i < NUM_THREADS - 1; i++ )
{
ThreadHandle_t hThread = CreateSimpleThread( &PushThreadFunc, NULL );
g_ThreadHandles.push_back( hThread );
if ( bDistribute )
{
int32 mask = 1 << (i % NUM_PROCESSORS);
ThreadSetAffinity( hThread, mask );
}
}
TestWait();
TestEnd();
}
void MTPushMTPop( bool bDistribute )
{
Msg( "%s test: multithread push, multithread pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
TestStart();
int ct = 0;
for ( int i = 0; i < NUM_THREADS / 2 ; i++ )
{
ThreadHandle_t hThread = CreateSimpleThread( &PopThreadFunc, NULL );
g_ThreadHandles.push_back( 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 );
g_ThreadHandles.push_back( hThread );
if ( bDistribute )
{
int32 mask = 1 << (ct++ % NUM_PROCESSORS);
ThreadSetAffinity( hThread, mask );
}
}
TestWait();
TestEnd();
}
void MTPushPopPopInterleaved( bool bDistribute )
{
Msg( "%s test: multithread interleaved push/pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
srand( Plat_MSTime() );
TestStart();
for ( int i = 0; i < NUM_THREADS; i++ )
{
ThreadHandle_t hThread = CreateSimpleThread( &PushPopInterleavedTestThreadFunc, NULL );
g_ThreadHandles.push_back( hThread );
if ( bDistribute )
{
int32 mask = 1 << (i % NUM_PROCESSORS);
ThreadSetAffinity( hThread, mask );
}
}
TestWait();
TestEnd();
}
void MTPushSeqPop( bool bDistribute )
{
Msg( "%s test: multithread push, sequential pop, %s", g_pListType, bDistribute ? "distributed..." : "no affinity..." );
TestStart();
for ( int i = 0; i < NUM_THREADS; i++ )
{
ThreadHandle_t hThread = CreateSimpleThread( &PushThreadFunc, NULL );
g_ThreadHandles.push_back( hThread );
if ( bDistribute )
{
int32 mask = 1 << (i % NUM_PROCESSORS);
ThreadSetAffinity( hThread, mask );
}
}
TestWait();
int ignored;
g_pTestOps->Validate();
while ( g_pTestOps->Pop( &ignored ) )
{
}
TestEnd();
}
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 );
}
for ( int i = 0; i < NUM_THREADS; i++ )
{
ThreadHandle_t hThread = CreateSimpleThread( &PopThreadFunc, NULL );
g_ThreadHandles.push_back( hThread );
if ( bDistribute )
{
int32 mask = 1 << (i % NUM_PROCESSORS);
ThreadSetAffinity( hThread, mask );
}
}
TestWait();
TestEnd();
}
}
void RunSharedTests( int nTests )
{
using namespace TSListTests;
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;
TSLHead_t foo;
(void)foo; // Avoid warning about unused variable.
#ifdef USE_NATIVE_SLIST
int maxSize = ( 1 << (sizeof( foo.Depth ) * 8) ) - 1;
#else
int maxSize = ( 1 << (sizeof( foo.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;
}

268
tier0/validator.cpp Normal file
View File

@ -0,0 +1,268 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include "tier0/memblockhdr.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

120
tier0/valobject.cpp Normal file
View File

@ -0,0 +1,120 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include "vstdlib/pch_vstdlib.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
View 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>

1778
tier0/vcrmode.cpp Normal file

File diff suppressed because it is too large Load Diff

970
tier0/vcrmode_posix.cpp Normal file
View File

@ -0,0 +1,970 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include <time.h>
#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "tier0/threadtools.h"
#define PROTECTED_THINGS_DISABLE
#include "tier0/vcrmode.h"
#include "tier0/dbg.h"
#include "extendedtrace.h"
// FIXME: We totally have a bad tier dependency here
#include "inputsystem/InputEnums.h"
#define VCRFILE_VERSION 2
#define VCR_RuntimeAssert(x) VCR_RuntimeAssertFn(x, #x)
#define PvAlloc malloc
bool g_bExpectingWindowProcCalls = false;
IVCRHelpers *g_pHelpers = 0;
FILE *g_pVCRFile = NULL;
VCRMode_t g_VCRMode = VCR_Disabled;
VCRMode_t g_OldVCRMode = (VCRMode_t)-1; // Stored temporarily between SetEnabled(0)/SetEnabled(1) blocks.
int g_iCurEvent = 0;
int g_CurFilePos = 0; // So it knows when we're done playing back.
int g_FileLen = 0;
VCREvent g_LastReadEvent = (VCREvent)-1; // Last VCR_ReadEvent() call.
int g_bVCREnabled = 0;
// ---------------------------------------------------------------------- //
// Internal functions.
// ---------------------------------------------------------------------- //
static void VCR_Error( const char *pFormat, ... )
{
#if defined( _DEBUG )
DebuggerBreak();
#endif
char str[256];
va_list marker;
va_start( marker, pFormat );
_snprintf( str, sizeof( str ), pFormat, marker );
va_end( marker );
g_pHelpers->ErrorMessage( str );
VCREnd();
}
static void VCR_RuntimeAssertFn(int bAssert, char const *pStr)
{
if(!bAssert)
{
VCR_Error( "*** VCR ASSERT FAILED: %s ***\n", pStr );
}
}
static void VCR_Read(void *pDest, int size)
{
if(!g_pVCRFile)
{
memset(pDest, 0, size);
return;
}
fread(pDest, 1, size, g_pVCRFile);
g_CurFilePos += size;
VCR_RuntimeAssert(g_CurFilePos <= g_FileLen);
if(g_CurFilePos >= g_FileLen)
{
VCREnd();
}
}
template<class T>
static void VCR_ReadVal(T &val)
{
VCR_Read(&val, sizeof(val));
}
static void VCR_Write(void const *pSrc, int size)
{
fwrite(pSrc, 1, size, g_pVCRFile);
fflush(g_pVCRFile);
}
template<class T>
static void VCR_WriteVal(T &val)
{
VCR_Write(&val, sizeof(val));
}
// Hook from ExtendedTrace.cpp
bool g_bTraceRead = false;
void OutputDebugStringFormat( const char *pMsg, ... )
{
char msg[4096];
va_list marker;
va_start( marker, pMsg );
_vsnprintf( msg, sizeof( msg )-1, pMsg, marker );
va_end( marker );
int len = strlen( msg );
if ( g_bTraceRead )
{
char tempData[4096];
int tempLen;
VCR_ReadVal( tempLen );
VCR_RuntimeAssert( tempLen <= sizeof( tempData ) );
VCR_Read( tempData, tempLen );
tempData[tempLen] = 0;
fprintf( stderr, "FILE: " );
fprintf( stderr, "%s", tempData );
VCR_RuntimeAssert( memcmp( msg, tempData, len ) == 0 );
}
else
{
VCR_WriteVal( len );
VCR_Write( msg, len );
}
}
static VCREvent VCR_ReadEvent()
{
g_bTraceRead = true;
//STACKTRACE();
char event;
VCR_Read(&event, 1);
g_LastReadEvent = (VCREvent)event;
return (VCREvent)event;
}
static void VCR_WriteEvent(VCREvent event)
{
g_bTraceRead = false;
//STACKTRACE();
// Write a stack trace.
char cEvent = (char)event;
VCR_Write(&cEvent, 1);
}
static void VCR_IncrementEvent()
{
++g_iCurEvent;
}
static void VCR_Event(VCREvent type)
{
if(g_VCRMode == VCR_Disabled)
return;
VCR_IncrementEvent();
if(g_VCRMode == VCR_Record)
{
VCR_WriteEvent(type);
}
else
{
VCREvent currentEvent = VCR_ReadEvent();
VCR_RuntimeAssert( currentEvent == type );
}
}
// ---------------------------------------------------------------------- //
// VCR trace interface.
// ---------------------------------------------------------------------- //
class CVCRTrace : public IVCRTrace
{
public:
virtual VCREvent ReadEvent()
{
return VCR_ReadEvent();
}
virtual void Read( void *pDest, int size )
{
VCR_Read( pDest, size );
}
};
static CVCRTrace g_VCRTrace;
// ---------------------------------------------------------------------- //
// VCR interface.
// ---------------------------------------------------------------------- //
static int VCR_Start( char const *pFilename, bool bRecord, IVCRHelpers *pHelpers )
{
unsigned long version;
g_pHelpers = pHelpers;
VCREnd();
EXTENDEDTRACEINITIALIZE( "/tmp/hl2" );
g_OldVCRMode = (VCRMode_t)-1;
if(bRecord)
{
g_pVCRFile = fopen( pFilename, "wb" );
if( g_pVCRFile )
{
// Write the version.
version = VCRFILE_VERSION;
VCR_Write(&version, sizeof(version));
g_VCRMode = VCR_Record;
return true;
}
else
{
return false;
}
}
else
{
g_pVCRFile = fopen( pFilename, "rb" );
if( g_pVCRFile )
{
// Get the file length.
fseek(g_pVCRFile, 0, SEEK_END);
g_FileLen = ftell(g_pVCRFile);
fseek(g_pVCRFile, 0, SEEK_SET);
g_CurFilePos = 0;
// Verify the file version.
VCR_Read(&version, sizeof(version));
if(version != VCRFILE_VERSION)
{
assert(!"VCR_Start: invalid file version");
VCREnd();
return FALSE;
}
g_VCRMode = VCR_Playback;
return TRUE;
}
else
{
return FALSE;
}
}
}
static void VCR_End()
{
if(g_pVCRFile)
{
fclose(g_pVCRFile);
g_pVCRFile = NULL;
}
g_VCRMode = VCR_Disabled;
EXTENDEDTRACEUNINITIALIZE();
}
static IVCRTrace* VCR_GetVCRTraceInterface()
{
return &g_VCRTrace;
}
static VCRMode_t VCR_GetMode()
{
return g_VCRMode;
}
static void VCR_SetEnabled(int bEnabled)
{
if(bEnabled)
{
VCR_RuntimeAssert(g_OldVCRMode != (VCRMode_t)-1);
g_VCRMode = g_OldVCRMode;
g_OldVCRMode = (VCRMode_t)-1;
}
else
{
VCR_RuntimeAssert(g_OldVCRMode == (VCRMode_t)-1);
g_OldVCRMode = g_VCRMode;
g_VCRMode = VCR_Disabled;
}
}
static void VCR_SyncToken(char const *pToken)
{
unsigned char len;
VCR_Event(VCREvent_SyncToken);
if(g_VCRMode == VCR_Record)
{
int intLen = strlen( pToken );
assert( intLen <= 255 );
len = (unsigned char)intLen;
VCR_Write(&len, 1);
VCR_Write(pToken, len);
}
else if(g_VCRMode == VCR_Playback)
{
char test[256];
VCR_Read(&len, 1);
VCR_Read(test, len);
VCR_RuntimeAssert( len == (unsigned char)strlen(pToken) );
VCR_RuntimeAssert( memcmp(pToken, test, len) == 0 );
}
}
static double VCR_Hook_Sys_FloatTime(double time)
{
VCR_Event(VCREvent_Sys_FloatTime);
if(g_VCRMode == VCR_Record)
{
VCR_Write(&time, sizeof(time));
}
else if(g_VCRMode == VCR_Playback)
{
VCR_Read(&time, sizeof(time));
}
return time;
}
static int VCR_Hook_PeekMessage(
struct tagMSG *msg,
void *hWnd,
unsigned int wMsgFilterMin,
unsigned int wMsgFilterMax,
unsigned int wRemoveMsg
)
{
Assert( "VCR_Hook_PeekMessage unsupported" );
return 0;
}
void VCR_Hook_RecordGameMsg( const InputEvent_t& event )
{
if ( g_VCRMode == VCR_Record )
{
VCR_Event( VCREvent_GameMsg );
char val = 1;
VCR_WriteVal( val );
VCR_WriteVal( event.m_nType );
VCR_WriteVal( event.m_nData );
VCR_WriteVal( event.m_nData2 );
VCR_WriteVal( event.m_nData3 );
}
}
void VCR_Hook_RecordEndGameMsg()
{
if ( g_VCRMode == VCR_Record )
{
VCR_Event( VCREvent_GameMsg );
char val = 0;
VCR_WriteVal( val ); // record that there are no more messages.
}
}
bool VCR_Hook_PlaybackGameMsg( InputEvent_t* pEvent )
{
if ( g_VCRMode == VCR_Playback )
{
VCR_Event( VCREvent_GameMsg );
char bMsg;
VCR_ReadVal( bMsg );
if ( bMsg )
{
VCR_ReadVal( pEvent->m_nType );
VCR_ReadVal( pEvent->m_nData );
VCR_ReadVal( pEvent->m_nData2 );
VCR_ReadVal( pEvent->m_nData3 );
return true;
}
}
return false;
}
static void VCR_Hook_GetCursorPos(struct tagPOINT *pt)
{
Assert( "VCR_Hook_GetCursorPos unsupported" );
}
static void VCR_Hook_ScreenToClient(void *hWnd, struct tagPOINT *pt)
{
Assert( "VCR_Hook_GetCursorPos unsupported" );
}
static int VCR_Hook_recvfrom(int s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
{
VCR_Event(VCREvent_recvfrom);
int ret;
if ( g_VCRMode == VCR_Playback )
{
// Get the result from our file.
VCR_Read(&ret, sizeof(ret));
if(ret == -1)
{
int err;
VCR_ReadVal(err);
errno = err;
}
else
{
VCR_Read( buf, ret );
char bFrom;
VCR_ReadVal( bFrom );
if ( bFrom )
{
VCR_Read( from, *fromlen );
}
}
}
else
{
ret = recvfrom(s, buf, len, flags, from, (socklen_t *)(fromlen));
if ( g_VCRMode == VCR_Record )
{
// Record the result.
VCR_Write(&ret, sizeof(ret));
if(ret == -1)
{
VCR_WriteVal(errno);
}
else
{
VCR_Write( buf, ret );
char bFrom = !!from;
VCR_WriteVal( bFrom );
if ( bFrom )
VCR_Write( from, *fromlen );
}
}
}
return ret;
}
static void VCR_Hook_Cmd_Exec(char **f)
{
VCR_Event(VCREvent_Cmd_Exec);
if(g_VCRMode == VCR_Playback)
{
int len;
VCR_Read(&len, sizeof(len));
if(len == -1)
{
*f = NULL;
}
else
{
*f = (char*)PvAlloc(len);
VCR_Read(*f, len);
}
}
else if(g_VCRMode == VCR_Record)
{
int len;
char *str = *f;
if(str)
{
len = strlen(str)+1;
VCR_Write(&len, sizeof(len));
VCR_Write(str, len);
}
else
{
len = -1;
VCR_Write(&len, sizeof(len));
}
}
}
#define MAX_LINUX_CMDLINE 512
static char linuxCmdline[ MAX_LINUX_CMDLINE +7 ]; // room for -steam
const char * BuildCmdLine( int argc, char **argv, bool fAddSteam )
{
int len;
int i;
for (len = 0, i = 0; i < argc; i++)
{
len += strlen(argv[i]);
}
if ( len > MAX_LINUX_CMDLINE )
{
printf( "command line too long, %i max\n", MAX_LINUX_CMDLINE );
exit(-1);
return "";
}
linuxCmdline[0] = '\0';
for ( i = 0; i < argc; i++ )
{
if ( i > 0 )
{
strcat( linuxCmdline, " " );
}
strcat( linuxCmdline, argv[ i ] );
}
if( fAddSteam )
{
strcat( linuxCmdline, " -steam" );
}
return linuxCmdline;
}
char *GetCommandLine()
{
return linuxCmdline;
}
static char* VCR_Hook_GetCommandLine()
{
VCR_Event(VCREvent_CmdLine);
int len;
char *ret;
if(g_VCRMode == VCR_Playback)
{
VCR_Read(&len, sizeof(len));
ret = new char[len];
VCR_Read(ret, len);
}
else
{
ret = GetCommandLine();
if(g_VCRMode == VCR_Record)
{
len = strlen(ret) + 1;
VCR_WriteVal(len);
VCR_Write(ret, len);
}
}
return ret;
}
static long VCR_Hook_RegOpenKeyEx( void *hKey, const char *lpSubKey, unsigned long ulOptions, unsigned long samDesired, void *pHKey )
{
Assert( "VCR_Hook_RegOpenKeyEx unsupported" );
return 0;
}
static long VCR_Hook_RegSetValueEx(void *hKey, char const *lpValueName, unsigned long Reserved, unsigned long dwType, unsigned char const *lpData, unsigned long cbData)
{
Assert( "VCR_Hook_RegSetValueEx unsupported" );
return 0;
}
static long VCR_Hook_RegQueryValueEx(void *hKey, char const *lpValueName, unsigned long *lpReserved, unsigned long *lpType, unsigned char *lpData, unsigned long *lpcbData)
{
Assert( "VCR_Hook_RegQueryValueEx unsupported" );
return 0;
}
static long VCR_Hook_RegCreateKeyEx(void *hKey, char const *lpSubKey, unsigned long Reserved, char *lpClass, unsigned long dwOptions,
unsigned long samDesired, void *lpSecurityAttributes, void *phkResult, unsigned long *lpdwDisposition)
{
Assert( "VCR_Hook_RegCreateKeyEx unsupported" );
return 0;
}
static void VCR_Hook_RegCloseKey(void *hKey)
{
Assert( "VCR_Hook_RegCloseKey unsupported" );
}
int VCR_Hook_GetNumberOfConsoleInputEvents( void *hInput, unsigned long *pNumEvents )
{
VCR_Event( VCREvent_GetNumberOfConsoleInputEvents );
char ret;
if ( g_VCRMode == VCR_Playback )
{
VCR_ReadVal( ret );
VCR_ReadVal( *pNumEvents );
}
else
{
ret = 1;
if ( g_VCRMode == VCR_Record )
{
VCR_WriteVal( ret );
VCR_WriteVal( *pNumEvents );
}
}
return ret;
}
int VCR_Hook_ReadConsoleInput( void *hInput, void *pRecs, int nMaxRecs, unsigned long *pNumRead )
{
Assert( "VCR_Hook_ReadConsoleInput unsupported" );
return 0;
}
void VCR_Hook_LocalTime( struct tm *today )
{
// We just provide a wrapper on this function so we can protect access to time() everywhere.
time_t ltime;
time( &ltime );
tm *pTime = localtime( &ltime );
memcpy( today, pTime, sizeof( *today ) );
}
short VCR_Hook_GetKeyState( int nVirtKey )
{
Assert( "VCREvent_GetKeyState unsupported" );
return 0;
}
void VCR_GenericRecord( const char *pEventName, const void *pData, int len )
{
VCR_Event( VCREvent_Generic );
if ( g_VCRMode != VCR_Record )
Error( "VCR_GenericRecord( %s ): not recording a VCR file", pEventName );
// Write the event name (or 255 if none).
int nameLen = 255;
if ( pEventName )
{
nameLen = strlen( pEventName ) + 1;
if ( nameLen >= 255 )
{
VCR_Error( "VCR_GenericRecord( %s ): nameLen too long (%d)", pEventName, nameLen );
return;
}
}
unsigned char ucNameLen = (unsigned char)nameLen;
VCR_WriteVal( ucNameLen );
VCR_Write( pEventName, ucNameLen );
// Write the data.
VCR_WriteVal( len );
VCR_Write( pData, len );
}
int VCR_GenericPlayback( const char *pEventName, void *pOutData, int maxLen, bool bForceSameLen )
{
VCR_Event( VCREvent_Generic );
if ( g_VCRMode != VCR_Playback )
Error( "VCR_Playback( %s ): not playing back a VCR file", pEventName );
unsigned char nameLen;
VCR_ReadVal( nameLen );
if ( nameLen != 255 )
{
char testName[512];
VCR_Read( testName, nameLen );
if ( strcmp( pEventName, testName ) != 0 )
{
VCR_Error( "VCR_GenericPlayback( %s ) - event name does not match '%s'", pEventName, testName );
return 0;
}
}
int dataLen;
VCR_ReadVal( dataLen );
if ( dataLen > maxLen )
{
VCR_Error( "VCR_GenericPlayback( %s ) - generic data too long (greater than maxLen: %d)", pEventName, maxLen );
return 0;
}
else if ( bForceSameLen && dataLen != maxLen )
{
VCR_Error( "VCR_GenericPlayback( %s ) - data size in file (%d) different than desired (%d)", pEventName, dataLen, maxLen );
return 0;
}
VCR_Read( pOutData, dataLen );
return dataLen;
}
void VCR_GenericValue( const char *pEventName, void *pData, int maxLen )
{
if ( g_VCRMode == VCR_Record )
VCR_GenericRecord( pEventName, pData, maxLen );
else if ( g_VCRMode == VCR_Playback )
VCR_GenericPlayback( pEventName, pData, maxLen, true );
}
static int VCR_Hook_recv(int s, char *buf, int len, int flags)
{
VCR_Event(VCREvent_recv);
int ret;
if ( g_VCRMode == VCR_Playback )
{
// Get the result from our file.
VCR_Read(&ret, sizeof(ret));
if(ret == -1)
{
int err;
VCR_ReadVal(err);
errno = err;
}
else
{
VCR_Read( buf, ret );
}
}
else
{
ret = recv( s, buf, len, flags );
if ( g_VCRMode == VCR_Record )
{
// Record the result.
VCR_Write(&ret, sizeof(ret));
if(ret == -1)
{
VCR_WriteVal(errno);
}
else
{
VCR_Write( buf, ret );
}
}
}
return ret;
}
static int VCR_Hook_send(int s, const char *buf, int len, int flags)
{
VCR_Event(VCREvent_send);
int ret;
if ( g_VCRMode == VCR_Playback )
{
// Get the result from our file.
VCR_Read(&ret, sizeof(ret));
if(ret == -1)
{
int err;
VCR_ReadVal(err);
errno = err;
}
}
else
{
ret = send( s, buf, len, flags );
if ( g_VCRMode == VCR_Record )
{
// Record the result.
VCR_Write(&ret, sizeof(ret));
if(ret == -1)
{
VCR_WriteVal(errno);
}
}
}
return ret;
}
double VCR_GetPercentCompleted()
{
if ( g_VCRMode == VCR_Playback )
{
return (double)g_CurFilePos / g_FileLen;
}
else
{
return 0;
}
}
void* VCR_CreateThread(
void *lpThreadAttributes,
unsigned long dwStackSize,
void *lpStartAddress,
void *lpParameter,
unsigned long dwCreationFlags,
unsigned long *lpThreadID )
{
return CreateSimpleThread( (ThreadFunc_t)lpStartAddress, lpParameter, lpThreadID, 0 );
}
unsigned long VCR_WaitForSingleObject(
void *handle,
unsigned long dwMilliseconds )
{
return -1;
}
unsigned long VCR_WaitForMultipleObjects( uint32 nHandles, const void **pHandles, int bWaitAll, uint32 timeout )
{
return -1;
}
void VCR_EnterCriticalSection( void *pInputCS )
{
}
void VCR_Hook_Time( long *today )
{
// We just provide a wrapper on this function so we can protect access to time() everywhere.
// NOTE: For 64-bit systems we should eventually get a function that takes a time_t, but we should have
// until about 2038 to do that before we overflow a long.
time_t curTime;
time( &curTime );
VCR_Event( VCREvent_Time );
if ( g_VCRMode == VCR_Playback )
{
VCR_Read( &curTime, sizeof( curTime ) );
}
else if ( g_VCRMode == VCR_Record )
{
VCR_Write( &curTime, sizeof( curTime ) );
}
*today = (long)curTime;
}
void VCR_GenericString( const char *pEventName, const char *pString )
{
}
void VCR_GenericValueVerify( const tchar *pEventName, const void *pData, int maxLen )
{
}
// ---------------------------------------------------------------------- //
// The global VCR interface.
// ---------------------------------------------------------------------- //
VCR_t g_VCR =
{
VCR_Start,
VCR_End,
VCR_GetVCRTraceInterface,
VCR_GetMode,
VCR_SetEnabled,
VCR_SyncToken,
VCR_Hook_Sys_FloatTime,
VCR_Hook_PeekMessage,
VCR_Hook_RecordGameMsg,
VCR_Hook_RecordEndGameMsg,
VCR_Hook_PlaybackGameMsg,
VCR_Hook_recvfrom,
VCR_Hook_GetCursorPos,
VCR_Hook_ScreenToClient,
VCR_Hook_Cmd_Exec,
VCR_Hook_GetCommandLine,
VCR_Hook_RegOpenKeyEx,
VCR_Hook_RegSetValueEx,
VCR_Hook_RegQueryValueEx,
VCR_Hook_RegCreateKeyEx,
VCR_Hook_RegCloseKey,
VCR_Hook_GetNumberOfConsoleInputEvents,
VCR_Hook_ReadConsoleInput,
VCR_Hook_LocalTime,
VCR_Hook_GetKeyState,
VCR_Hook_recv,
VCR_Hook_send,
VCR_GenericRecord,
VCR_GenericPlayback,
VCR_GenericValue,
VCR_GetPercentCompleted,
VCR_CreateThread,
VCR_WaitForSingleObject,
VCR_EnterCriticalSection,
VCR_Hook_Time,
VCR_GenericString,
VCR_GenericValueVerify,
VCR_WaitForMultipleObjects,
};
VCR_t *g_pVCR = &g_VCR;

290
tier0/vcrmode_xbox.cpp Normal file
View File

@ -0,0 +1,290 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: VCR mode records a client's game and allows you to
// play it back and reproduce it exactly. When playing it back, nothing
// is simulated on the server, but all server packets are recorded.
//
// Most of the VCR mode functionality is accomplished through hooks
// called at various points in the engine.
//
// $NoKeywords: $
//=============================================================================
#include "xbox/xbox_platform.h"
#include "xbox/xbox_win32stubs.h"
#include <time.h>
#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "tier0/vcrmode.h"
#include "tier0/dbg.h"
#define VCRFILE_VERSION 2
#define VCR_RuntimeAssert(x) VCR_RuntimeAssertFn(x, #x)
// ---------------------------------------------------------------------- //
// Internal functions.
// ---------------------------------------------------------------------- //
static void VCR_Error( const char *pFormat, ... )
{
}
static void VCR_RuntimeAssertFn(int bAssert, char const *pStr)
{
}
static void VCR_Read(void *pDest, int size)
{
}
template<class T>
static void VCR_ReadVal(T &val)
{
VCR_Read(&val, sizeof(val));
}
static void VCR_Write(void const *pSrc, int size)
{
}
template<class T>
static void VCR_WriteVal(T &val)
{
}
// Hook from ExtendedTrace.cpp
bool g_bTraceRead = false;
void OutputDebugStringFormat( const char *pMsg, ... )
{
}
static VCREvent VCR_ReadEvent()
{
return (VCREvent)-1;
}
static void VCR_WriteEvent(VCREvent event)
{
}
static void VCR_IncrementEvent()
{
}
static void VCR_Event(VCREvent type)
{
}
// ---------------------------------------------------------------------- //
// VCR trace interface.
// ---------------------------------------------------------------------- //
class CVCRTrace : public IVCRTrace
{
public:
virtual VCREvent ReadEvent()
{
return VCR_ReadEvent();
}
virtual void Read( void *pDest, int size )
{
VCR_Read( pDest, size );
}
};
static CVCRTrace g_VCRTrace;
// ---------------------------------------------------------------------- //
// VCR interface.
// ---------------------------------------------------------------------- //
static int VCR_Start( char const *pFilename, bool bRecord, IVCRHelpers *pHelpers )
{
return 0;
}
static void VCR_End()
{
}
static IVCRTrace* VCR_GetVCRTraceInterface()
{
return 0;
}
static VCRMode VCR_GetMode()
{
return VCR_Disabled;
}
static void VCR_SetEnabled(int bEnabled)
{
}
static void VCR_SyncToken(char const *pToken)
{
}
static double VCR_Hook_Sys_FloatTime(double time)
{
return 0;
}
static int VCR_Hook_PeekMessage(
struct tagMSG *msg,
void *hWnd,
unsigned int wMsgFilterMin,
unsigned int wMsgFilterMax,
unsigned int wRemoveMsg
)
{
return 0;
}
void VCR_Hook_RecordGameMsg( unsigned int uMsg, unsigned int wParam, long lParam )
{
}
void VCR_Hook_RecordEndGameMsg()
{
}
bool VCR_Hook_PlaybackGameMsg( unsigned int &uMsg, unsigned int &wParam, long &lParam )
{
return 0;
}
static void VCR_Hook_GetCursorPos(struct tagPOINT *pt)
{
}
static void VCR_Hook_ScreenToClient(void *hWnd, struct tagPOINT *pt)
{
}
static int VCR_Hook_recvfrom(int s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
{
return 0;
}
static int VCR_Hook_recv(int s, char *buf, int len, int flags)
{
return 0;
}
static int VCR_Hook_send(int s, const char *buf, int len, int flags)
{
return 0;
}
static void VCR_Hook_Cmd_Exec(char **f)
{
}
static char* VCR_Hook_GetCommandLine()
{
return GetCommandLine();
}
static long VCR_Hook_RegOpenKeyEx( void *hKey, const char *lpSubKey, unsigned long ulOptions, unsigned long samDesired, void *pHKey )
{
return 0;
}
static long VCR_Hook_RegSetValueEx(void *hKey, char const *lpValueName, unsigned long Reserved, unsigned long dwType, unsigned char const *lpData, unsigned long cbData)
{
return 0;
}
static long VCR_Hook_RegQueryValueEx(void *hKey, char const *lpValueName, unsigned long *lpReserved, unsigned long *lpType, unsigned char *lpData, unsigned long *lpcbData)
{
return 0;
}
static long VCR_Hook_RegCreateKeyEx(void *hKey, char const *lpSubKey, unsigned long Reserved, char *lpClass, unsigned long dwOptions,
unsigned long samDesired, void *lpSecurityAttributes, void *phkResult, unsigned long *lpdwDisposition)
{
return 0;
}
static void VCR_Hook_RegCloseKey(void *hKey)
{
}
int VCR_Hook_GetNumberOfConsoleInputEvents( void *hInput, unsigned long *pNumEvents )
{
return 0;
}
int VCR_Hook_ReadConsoleInput( void *hInput, void *pRecs, int nMaxRecs, unsigned long *pNumRead )
{
return 0;
}
void VCR_Hook_LocalTime( struct tm *today )
{
}
short VCR_Hook_GetKeyState( int nVirtKey )
{
return 0;
}
void VCR_GenericRecord( const char *pEventName, const void *pData, int len )
{
}
int VCR_GenericPlayback( const char *pEventName, void *pOutData, int maxLen, bool bForceSameLen )
{
return 0;
}
void VCR_GenericValue( const char *pEventName, void *pData, int maxLen )
{
}
// ---------------------------------------------------------------------- //
// The global VCR interface.
// ---------------------------------------------------------------------- //
VCR_t g_VCR =
{
VCR_Start,
VCR_End,
VCR_GetVCRTraceInterface,
VCR_GetMode,
VCR_SetEnabled,
VCR_SyncToken,
VCR_Hook_Sys_FloatTime,
VCR_Hook_PeekMessage,
VCR_Hook_RecordGameMsg,
VCR_Hook_RecordEndGameMsg,
VCR_Hook_PlaybackGameMsg,
VCR_Hook_recvfrom,
VCR_Hook_GetCursorPos,
VCR_Hook_ScreenToClient,
VCR_Hook_Cmd_Exec,
VCR_Hook_GetCommandLine,
VCR_Hook_RegOpenKeyEx,
VCR_Hook_RegSetValueEx,
VCR_Hook_RegQueryValueEx,
VCR_Hook_RegCreateKeyEx,
VCR_Hook_RegCloseKey,
VCR_Hook_GetNumberOfConsoleInputEvents,
VCR_Hook_ReadConsoleInput,
VCR_Hook_LocalTime,
VCR_Hook_GetKeyState,
VCR_Hook_recv,
VCR_Hook_send,
VCR_GenericRecord,
VCR_GenericPlayback,
VCR_GenericValue
};
VCR_t *g_pVCR = &g_VCR;

2094
tier0/vprof.cpp Normal file

File diff suppressed because it is too large Load Diff

91
tier0/win32consoleio.cpp Normal file
View File

@ -0,0 +1,91 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Attaches a console for I/O to a Win32 GUI application in a
// reasonably smart fashion
//
//=============================================================================
#include "pch_tier0.h"
#if defined( _WIN32 )
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <iostream>
#endif // defined( _WIN32 )
//-----------------------------------------------------------------------------
//
// 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 thigns 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< intptr_t >( GetStdHandle( STD_OUTPUT_HANDLE ) ), _O_TEXT ), "w" );
setvbuf( stdout, NULL, _IONBF, 0 );
*stdin = *_fdopen( _open_osfhandle( reinterpret_cast< intptr_t >( GetStdHandle( STD_INPUT_HANDLE ) ), _O_TEXT ), "r" );
setvbuf( stdin, NULL, _IONBF, 0 );
*stderr = *_fdopen( _open_osfhandle( reinterpret_cast< intptr_t >( 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 )
}

View File

@ -0,0 +1,31 @@
//========= Copyright 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( "vxbdm_360.dll" );
if ( !hDLL )
{
return;
}
CONSOLEINTERFACEFUNC fpnGetConsoleInterface = (CONSOLEINTERFACEFUNC) ::GetProcAddress( hDLL, "GetConsoleInterface" );
if ( fpnGetConsoleInterface )
{
g_pXboxConsole = fpnGetConsoleInterface();
}
}

View File

@ -0,0 +1,9 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Xbox profiling
//
//=====================================================================================//
#include "pch_tier0.h"
#include "tier0/memdbgon.h"

368
tier0/xbox/xbox_system.cpp Normal file
View File

@ -0,0 +1,368 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Xbox
//
//=====================================================================================//
#include "pch_tier0.h"
#include "xbox/xbox_console.h"
#include "xbox/xbox_win32stubs.h"
#include "xbox/xbox_launch.h"
#include "tier0/memdbgon.h"
#define XBX_MAX_EVENTS 32
xevent_t g_xbx_eventQueue[XBX_MAX_EVENTS];
int g_xbx_eventHead;
int g_xbx_eventTail;
DWORD g_iStorageDeviceId = XBX_INVALID_STORAGE_ID;
DWORD g_iPrimaryUserId = XBX_INVALID_USER_ID;
DWORD g_InvitedUserId = XBX_INVALID_USER_ID;
HANDLE g_hListenHandle = INVALID_HANDLE_VALUE;
ULONG64 g_ListenCategories = 0;
XNKID g_InviteSessionId = { 0, 0, 0, 0, 0, 0, 0, 0 };
//-----------------------------------------------------------------------------
// Convert an Xbox notification to a custom windows message
//-----------------------------------------------------------------------------
static int NotificationToWindowsMessage( DWORD id )
{
switch( id )
{
case XN_SYS_UI: return WM_SYS_UI;
case XN_SYS_SIGNINCHANGED: return WM_SYS_SIGNINCHANGED;
case XN_SYS_STORAGEDEVICESCHANGED: return WM_SYS_STORAGEDEVICESCHANGED;
case XN_SYS_PROFILESETTINGCHANGED: return WM_SYS_PROFILESETTINGCHANGED;
case XN_SYS_MUTELISTCHANGED: return WM_SYS_MUTELISTCHANGED;
case XN_SYS_INPUTDEVICESCHANGED: return WM_SYS_INPUTDEVICESCHANGED;
case XN_SYS_INPUTDEVICECONFIGCHANGED: return WM_SYS_INPUTDEVICECONFIGCHANGED;
case XN_LIVE_CONNECTIONCHANGED: return WM_LIVE_CONNECTIONCHANGED;
case XN_LIVE_INVITE_ACCEPTED: return WM_LIVE_INVITE_ACCEPTED;
case XN_LIVE_LINK_STATE_CHANGED: return WM_LIVE_LINK_STATE_CHANGED;
case XN_LIVE_CONTENT_INSTALLED: return WM_LIVE_CONTENT_INSTALLED;
case XN_LIVE_MEMBERSHIP_PURCHASED: return WM_LIVE_MEMBERSHIP_PURCHASED;
case XN_LIVE_VOICECHAT_AWAY: return WM_LIVE_VOICECHAT_AWAY;
case XN_LIVE_PRESENCE_CHANGED: return WM_LIVE_PRESENCE_CHANGED;
case XN_FRIENDS_PRESENCE_CHANGED: return WM_FRIENDS_PRESENCE_CHANGED;
case XN_FRIENDS_FRIEND_ADDED: return WM_FRIENDS_FRIEND_ADDED;
case XN_FRIENDS_FRIEND_REMOVED: return WM_FRIENDS_FRIEND_REMOVED;
//deprecated in Jun08 XDK: case XN_CUSTOM_GAMEBANNERPRESSED: return WM_CUSTOM_GAMEBANNERPRESSED;
case XN_CUSTOM_ACTIONPRESSED: return WM_CUSTOM_ACTIONPRESSED;
case XN_XMP_STATECHANGED: return WM_XMP_STATECHANGED;
case XN_XMP_PLAYBACKBEHAVIORCHANGED: return WM_XMP_PLAYBACKBEHAVIORCHANGED;
case XN_XMP_PLAYBACKCONTROLLERCHANGED: return WM_XMP_PLAYBACKCONTROLLERCHANGED;
default:
Warning( "Unrecognized notification id %d\n", id );
return 0;
}
}
//-----------------------------------------------------------------------------
// XBX_Error
//
//-----------------------------------------------------------------------------
void XBX_Error( const char* format, ... )
{
va_list args;
char message[XBX_MAX_MESSAGE];
va_start( args, format );
_vsnprintf( message, sizeof( message ), format, args );
va_end( args );
message[sizeof( message )-1] = '\0';
XBX_DebugString( XMAKECOLOR(255,0,0), message );
XBX_FlushDebugOutput();
DebugBreak();
static volatile int doReturn;
while ( !doReturn );
}
//-----------------------------------------------------------------------------
// XBX_OutputDebugStringA
//
// Replaces 'OutputDebugString' to send through debugging channel
//-----------------------------------------------------------------------------
void XBX_OutputDebugStringA( LPCSTR lpOutputString )
{
XBX_DebugString( XMAKECOLOR(0,0,0), lpOutputString );
}
//-----------------------------------------------------------------------------
// XBX_GetEvent
//
//-----------------------------------------------------------------------------
static xevent_t* XBX_GetEvent(void)
{
xevent_t* evPtr;
if ( g_xbx_eventHead == g_xbx_eventTail )
{
// empty
return NULL;
}
evPtr = &g_xbx_eventQueue[g_xbx_eventHead & (XBX_MAX_EVENTS-1)];
// advance to next event
g_xbx_eventHead++;
return evPtr;
}
//-----------------------------------------------------------------------------
// XBX_FlushEvents
//
//-----------------------------------------------------------------------------
static void XBX_FlushEvents(void)
{
g_xbx_eventHead = 0;
g_xbx_eventTail = 0;
}
//-----------------------------------------------------------------------------
// XBX_ProcessXCommand
//
//-----------------------------------------------------------------------------
static void XBX_ProcessXCommand( const char *pCommand )
{
// 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)pCommand );
}
}
//-----------------------------------------------------------------------------
// XBX_ProcessListenerNotification
//
//-----------------------------------------------------------------------------
static void XBX_ProcessListenerNotification( int notification, int parameter )
{
// pass it game via windows message
HWND hWnd = GetFocus();
WNDPROC windowProc = ( WNDPROC)GetWindowLong( hWnd, GWL_WNDPROC );
if ( windowProc )
{
windowProc( hWnd, notification, 0, (LPARAM)parameter );
}
}
//-----------------------------------------------------------------------------
// XBX_QueueEvent
//
//-----------------------------------------------------------------------------
void XBX_QueueEvent(xevent_e event, int arg1, int arg2, int arg3)
{
xevent_t* evPtr;
evPtr = &g_xbx_eventQueue[g_xbx_eventTail & (XBX_MAX_EVENTS-1)];
evPtr->event = event;
evPtr->arg1 = arg1;
evPtr->arg2 = arg2;
evPtr->arg3 = arg3;
// next slot, queue never fills just overwrite older events
g_xbx_eventTail++;
}
//-----------------------------------------------------------------------------
// XBX_ProcessEvents
//
// Assumed one per frame only!
//-----------------------------------------------------------------------------
void XBX_ProcessEvents(void)
{
xevent_t *evPtr;
DWORD id;
ULONG parameter;
while ( XNotifyGetNext( g_hListenHandle, 0, &id, &parameter ) )
{
// Special handling
switch( id )
{
case XN_SYS_STORAGEDEVICESCHANGED:
{
// Have we selected a storage device?
DWORD storageID = XBX_GetStorageDeviceId();
if ( storageID == XBX_INVALID_STORAGE_ID || storageID == XBX_STORAGE_DECLINED )
break;
// Validate the selected storage device
XDEVICE_DATA deviceData;
DWORD ret = XContentGetDeviceData( storageID, &deviceData );
if ( ret != ERROR_SUCCESS )
{
// Device was removed
XBX_SetStorageDeviceId( XBX_INVALID_STORAGE_ID );
XBX_QueueEvent( XEV_LISTENER_NOTIFICATION, NotificationToWindowsMessage( id ), 0, 0 );
}
break;
}
default:
XBX_QueueEvent( XEV_LISTENER_NOTIFICATION, NotificationToWindowsMessage( id ), parameter, 0 );
break;
}
}
// pump event queue
while ( 1 )
{
evPtr = XBX_GetEvent();
if ( !evPtr )
break;
switch ( evPtr->event )
{
case XEV_REMOTECMD:
XBX_ProcessXCommand( (char *)evPtr->arg1 );
// clear to mark as absorbed
((char *)evPtr->arg1)[0] = '\0';
break;
case XEV_LISTENER_NOTIFICATION:
XBX_ProcessListenerNotification( evPtr->arg1, evPtr->arg2 );
break;
}
}
}
//-----------------------------------------------------------------------------
// XBX_NotifyCreateListener
//
// Add notification categories to the listener object
//-----------------------------------------------------------------------------
bool XBX_NotifyCreateListener( ULONG64 categories )
{
if ( categories != 0 )
{
categories |= g_ListenCategories;
}
g_hListenHandle = XNotifyCreateListener( categories );
if ( g_hListenHandle == NULL || g_hListenHandle == INVALID_HANDLE_VALUE )
{
return false;
}
g_ListenCategories = categories;
return true;
}
//-----------------------------------------------------------------------------
// XBX_GetLanguageString
//
// Returns the xbox language setting as a string
//-----------------------------------------------------------------------------
const char* XBX_GetLanguageString( void )
{
switch( XGetLanguage() )
{
case XC_LANGUAGE_FRENCH:
return "french";
case XC_LANGUAGE_GERMAN:
return "german";
}
return "english";
}
bool XBX_IsLocalized( void )
{
switch( XGetLanguage() )
{
case XC_LANGUAGE_FRENCH:
case XC_LANGUAGE_GERMAN:
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// XBX_GetStorageDeviceId
//
// Returns the xbox storage device ID
//-----------------------------------------------------------------------------
DWORD XBX_GetStorageDeviceId( void )
{
return g_iStorageDeviceId;
}
//-----------------------------------------------------------------------------
// XBX_SetStorageDeviceId
//
// Sets the xbox storage device ID
//-----------------------------------------------------------------------------
void XBX_SetStorageDeviceId( DWORD id )
{
g_iStorageDeviceId = id;
}
//-----------------------------------------------------------------------------
// XBX_GetPrimaryUserId
//
// Returns the active user ID
//-----------------------------------------------------------------------------
DWORD XBX_GetPrimaryUserId( void )
{
return g_iPrimaryUserId;
}
//-----------------------------------------------------------------------------
// XBX_SetPrimaryUserId
//
// Sets the active user ID
//-----------------------------------------------------------------------------
void XBX_SetPrimaryUserId( DWORD idx )
{
g_iPrimaryUserId = idx;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the stored session ID for a cross-game invite
//-----------------------------------------------------------------------------
XNKID XBX_GetInviteSessionId( void )
{
return g_InviteSessionId;
}
//-----------------------------------------------------------------------------
// Purpose: Store a session ID for an invitation
//-----------------------------------------------------------------------------
void XBX_SetInviteSessionId( XNKID nSessionId )
{
g_InviteSessionId = nSessionId;
}
//-----------------------------------------------------------------------------
// Purpose: Get the Id of the user who received an invite
//-----------------------------------------------------------------------------
DWORD XBX_GetInvitedUserId( void )
{
return g_InvitedUserId;
}
//-----------------------------------------------------------------------------
// Purpose: Set the Id of the user who received an invite
//-----------------------------------------------------------------------------
void XBX_SetInvitedUserId( DWORD nUserId )
{
g_InvitedUserId = nUserId;
}
static CXboxLaunch g_XBoxLaunch;
CXboxLaunch *XboxLaunch()
{
return &g_XBoxLaunch;
}

View File

@ -0,0 +1,617 @@
//========= Copyright 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 ( !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));
}
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 )
{
if ( GetLastError() == ERROR_FILE_NOT_FOUND )
{
// not a real failure
return 0;
}
}
// 0 = sucess, -1 = failure
return bSuccess ? 0 : -1;
}
int XBX_mkdir( const char *pszDir )
{
char dirPath[MAX_PATH];
char* ptr;
BOOL bSuccess;
// prime and skip to first seperator after the drive path
// must create directory one path at a time
bSuccess = false;
strcpy( dirPath, pszDir );
ptr = strchr( dirPath, '\\' );
while ( ptr )
{
ptr = strchr( ptr+1, '\\' );
if ( ptr )
{
*ptr = '\0';
bSuccess = CreateDirectory( dirPath, XBOX_DONTCARE );
if ( !bSuccess && GetLastError() == ERROR_ALREADY_EXISTS )
{
// not a real error
bSuccess = true;
}
*ptr = '\\';
}
}
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;
}