mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-13 04:09:09 +00:00
1
This commit is contained in:
Vendored
+573
@@ -0,0 +1,573 @@
|
||||
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
#include "tier0/valve_off.h"
|
||||
#ifdef _X360
|
||||
#include "xbox/xbox_console.h"
|
||||
#include "xbox/xbox_vxconsole.h"
|
||||
#elif defined( _PS3 )
|
||||
#include "ps3/ps3_console.h"
|
||||
#elif defined( _WIN32 )
|
||||
#include <windows.h>
|
||||
#elif POSIX
|
||||
char *GetCommandLine();
|
||||
#endif
|
||||
#include "resource.h"
|
||||
#include "tier0/valve_on.h"
|
||||
#include "tier0/threadtools.h"
|
||||
#include "tier0/icommandline.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
class CDialogInitInfo
|
||||
{
|
||||
public:
|
||||
const tchar *m_pFilename;
|
||||
int m_iLine;
|
||||
const tchar *m_pExpression;
|
||||
};
|
||||
|
||||
|
||||
class CAssertDisable
|
||||
{
|
||||
public:
|
||||
tchar m_Filename[512];
|
||||
|
||||
// If these are not -1, then this CAssertDisable only disables asserts on lines between
|
||||
// these values (inclusive).
|
||||
int m_LineMin;
|
||||
int m_LineMax;
|
||||
|
||||
// Decremented each time we hit this assert and ignore it, until it's 0.
|
||||
// Then the CAssertDisable is removed.
|
||||
// If this is -1, then we always ignore this assert.
|
||||
int m_nIgnoreTimes;
|
||||
|
||||
CAssertDisable *m_pNext;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
static HINSTANCE g_hTier0Instance = 0;
|
||||
#endif
|
||||
|
||||
static bool g_bAssertsEnabled = true;
|
||||
|
||||
static CAssertDisable *g_pAssertDisables = NULL;
|
||||
|
||||
#if ( defined( _WIN32 ) && !defined( _X360 ) )
|
||||
static int g_iLastLineRange = 5;
|
||||
static int g_nLastIgnoreNumTimes = 1;
|
||||
#endif
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
static int g_VXConsoleAssertReturnValue = -1;
|
||||
#endif
|
||||
|
||||
// Set to true if they want to break in the debugger.
|
||||
static bool g_bBreak = false;
|
||||
|
||||
static CDialogInitInfo g_Info;
|
||||
|
||||
static bool g_bDisableAsserts = false;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// Internal functions.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
#if defined(_WIN32) && !defined(STATIC_TIER0)
|
||||
BOOL WINAPI DllMain(
|
||||
HINSTANCE hinstDLL, // handle to the DLL module
|
||||
DWORD fdwReason, // reason for calling function
|
||||
LPVOID lpvReserved // reserved
|
||||
)
|
||||
{
|
||||
g_hTier0Instance = hinstDLL;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool IsDebugBreakEnabled()
|
||||
{
|
||||
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-debugbreak") ) != NULL );
|
||||
return bResult;
|
||||
}
|
||||
|
||||
static bool AssertStack()
|
||||
{
|
||||
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-assertstack") ) != NULL );
|
||||
return bResult;
|
||||
}
|
||||
|
||||
static bool AreAssertsDisabled()
|
||||
{
|
||||
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-noassert") ) != NULL );
|
||||
return bResult || g_bDisableAsserts;
|
||||
}
|
||||
|
||||
static bool AllAssertOnce()
|
||||
{
|
||||
static bool bResult = ( _tcsstr( Plat_GetCommandLine(), _T("-assertonce") ) != NULL );
|
||||
return bResult;
|
||||
}
|
||||
|
||||
static bool AreAssertsEnabledInFileLine( const tchar *pFilename, int iLine )
|
||||
{
|
||||
CAssertDisable **pPrev = &g_pAssertDisables;
|
||||
CAssertDisable *pNext;
|
||||
for ( CAssertDisable *pCur=g_pAssertDisables; pCur; pCur=pNext )
|
||||
{
|
||||
pNext = pCur->m_pNext;
|
||||
|
||||
if ( _tcsicmp( pFilename, pCur->m_Filename ) == 0 )
|
||||
{
|
||||
// Are asserts disabled in the whole file?
|
||||
bool bAssertsEnabled = true;
|
||||
if ( pCur->m_LineMin == -1 && pCur->m_LineMax == -1 )
|
||||
bAssertsEnabled = false;
|
||||
|
||||
// Are asserts disabled on the specified line?
|
||||
if ( iLine >= pCur->m_LineMin && iLine <= pCur->m_LineMax )
|
||||
bAssertsEnabled = false;
|
||||
|
||||
if ( !bAssertsEnabled )
|
||||
{
|
||||
// If this assert is only disabled for the next N times, then countdown..
|
||||
if ( pCur->m_nIgnoreTimes > 0 )
|
||||
{
|
||||
--pCur->m_nIgnoreTimes;
|
||||
if ( pCur->m_nIgnoreTimes == 0 )
|
||||
{
|
||||
// Remove this one from the list.
|
||||
*pPrev = pNext;
|
||||
delete pCur;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pPrev = &pCur->m_pNext;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CAssertDisable* CreateNewAssertDisable( const tchar *pFilename )
|
||||
{
|
||||
CAssertDisable *pDisable = new CAssertDisable;
|
||||
pDisable->m_pNext = g_pAssertDisables;
|
||||
g_pAssertDisables = pDisable;
|
||||
|
||||
pDisable->m_LineMin = pDisable->m_LineMax = -1;
|
||||
pDisable->m_nIgnoreTimes = -1;
|
||||
|
||||
_tcsncpy( pDisable->m_Filename, g_Info.m_pFilename, sizeof( pDisable->m_Filename ) - 1 );
|
||||
pDisable->m_Filename[ sizeof( pDisable->m_Filename ) - 1 ] = 0;
|
||||
|
||||
return pDisable;
|
||||
}
|
||||
|
||||
|
||||
void IgnoreAssertsInCurrentFile()
|
||||
{
|
||||
CreateNewAssertDisable( g_Info.m_pFilename );
|
||||
}
|
||||
|
||||
|
||||
CAssertDisable* IgnoreAssertsNearby( int nRange )
|
||||
{
|
||||
CAssertDisable *pDisable = CreateNewAssertDisable( g_Info.m_pFilename );
|
||||
pDisable->m_LineMin = g_Info.m_iLine - nRange;
|
||||
pDisable->m_LineMax = g_Info.m_iLine - nRange;
|
||||
return pDisable;
|
||||
}
|
||||
|
||||
|
||||
#if ( defined( _WIN32 ) && !defined( _X360 ) )
|
||||
INT_PTR CALLBACK AssertDialogProc(
|
||||
HWND hDlg, // handle to dialog box
|
||||
UINT uMsg, // message
|
||||
WPARAM wParam, // first message parameter
|
||||
LPARAM lParam // second message parameter
|
||||
)
|
||||
{
|
||||
switch( uMsg )
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
#ifdef TCHAR_IS_WCHAR
|
||||
SetDlgItemTextW( hDlg, IDC_ASSERT_MSG_CTRL, g_Info.m_pExpression );
|
||||
SetDlgItemTextW( hDlg, IDC_FILENAME_CONTROL, g_Info.m_pFilename );
|
||||
#else
|
||||
SetDlgItemText( hDlg, IDC_ASSERT_MSG_CTRL, g_Info.m_pExpression );
|
||||
SetDlgItemText( hDlg, IDC_FILENAME_CONTROL, g_Info.m_pFilename );
|
||||
#endif
|
||||
SetDlgItemInt( hDlg, IDC_LINE_CONTROL, g_Info.m_iLine, false );
|
||||
SetDlgItemInt( hDlg, IDC_IGNORE_NUMLINES, g_iLastLineRange, false );
|
||||
SetDlgItemInt( hDlg, IDC_IGNORE_NUMTIMES, g_nLastIgnoreNumTimes, false );
|
||||
|
||||
// Center the dialog.
|
||||
RECT rcDlg, rcDesktop;
|
||||
GetWindowRect( hDlg, &rcDlg );
|
||||
GetWindowRect( GetDesktopWindow(), &rcDesktop );
|
||||
SetWindowPos(
|
||||
hDlg,
|
||||
HWND_TOP,
|
||||
((rcDesktop.right-rcDesktop.left) - (rcDlg.right-rcDlg.left)) / 2,
|
||||
((rcDesktop.bottom-rcDesktop.top) - (rcDlg.bottom-rcDlg.top)) / 2,
|
||||
0,
|
||||
0,
|
||||
SWP_NOSIZE );
|
||||
}
|
||||
return true;
|
||||
|
||||
case WM_COMMAND:
|
||||
{
|
||||
switch( LOWORD( wParam ) )
|
||||
{
|
||||
case IDC_IGNORE_FILE:
|
||||
{
|
||||
IgnoreAssertsInCurrentFile();
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ignore this assert N times.
|
||||
case IDC_IGNORE_THIS:
|
||||
{
|
||||
BOOL bTranslated = false;
|
||||
UINT value = GetDlgItemInt( hDlg, IDC_IGNORE_NUMTIMES, &bTranslated, false );
|
||||
if ( bTranslated && value > 1 )
|
||||
{
|
||||
CAssertDisable *pDisable = IgnoreAssertsNearby( 0 );
|
||||
pDisable->m_nIgnoreTimes = value - 1;
|
||||
g_nLastIgnoreNumTimes = value;
|
||||
}
|
||||
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
// Always ignore this assert.
|
||||
case IDC_IGNORE_ALWAYS:
|
||||
{
|
||||
IgnoreAssertsNearby( 0 );
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
case IDC_IGNORE_NEARBY:
|
||||
{
|
||||
BOOL bTranslated = false;
|
||||
UINT value = GetDlgItemInt( hDlg, IDC_IGNORE_NUMLINES, &bTranslated, false );
|
||||
if ( !bTranslated || value < 1 )
|
||||
return true;
|
||||
|
||||
IgnoreAssertsNearby( value );
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
case IDC_IGNORE_ALL:
|
||||
{
|
||||
g_bAssertsEnabled = false;
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
case IDC_BREAK:
|
||||
{
|
||||
g_bBreak = true;
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
case WM_KEYDOWN:
|
||||
{
|
||||
// Escape?
|
||||
if ( wParam == 2 )
|
||||
{
|
||||
// Ignore this assert.
|
||||
EndDialog( hDlg, 0 );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static HWND g_hBestParentWindow;
|
||||
|
||||
|
||||
static BOOL CALLBACK ParentWindowEnumProc(
|
||||
HWND hWnd, // handle to parent window
|
||||
LPARAM lParam // application-defined value
|
||||
)
|
||||
{
|
||||
if ( IsWindowVisible( hWnd ) )
|
||||
{
|
||||
DWORD procID;
|
||||
GetWindowThreadProcessId( hWnd, &procID );
|
||||
if ( procID == (DWORD)lParam )
|
||||
{
|
||||
g_hBestParentWindow = hWnd;
|
||||
return FALSE; // don't iterate any more.
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static HWND FindLikelyParentWindow()
|
||||
{
|
||||
// Enumerate top-level windows and take the first visible one with our processID.
|
||||
g_hBestParentWindow = NULL;
|
||||
EnumWindows( ParentWindowEnumProc, GetCurrentProcessId() );
|
||||
return g_hBestParentWindow;
|
||||
}
|
||||
#endif
|
||||
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// Interface functions.
|
||||
// -------------------------------------------------------------------------------- //
|
||||
|
||||
// provides access to the global that turns asserts on and off
|
||||
PLATFORM_INTERFACE bool AreAllAssertsDisabled()
|
||||
{
|
||||
return !g_bAssertsEnabled;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void SetAllAssertsDisabled( bool bAssertsDisabled )
|
||||
{
|
||||
g_bAssertsEnabled = !bAssertsDisabled;
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE bool ShouldUseNewAssertDialog()
|
||||
{
|
||||
static bool bMPIWorker = ( _tcsstr( Plat_GetCommandLine(), _T("-mpi_worker") ) != NULL );
|
||||
if ( bMPIWorker )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DBGFLAG_ASSERTDLG
|
||||
return true; // always show an assert dialog
|
||||
#else
|
||||
return Plat_IsInDebugSession(); // only show an assert dialog if the process is being debugged
|
||||
#endif // DBGFLAG_ASSERTDLG
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE bool DoNewAssertDialog( const tchar *pFilename, int line, const tchar *pExpression )
|
||||
{
|
||||
LOCAL_THREAD_LOCK();
|
||||
|
||||
if ( AreAssertsDisabled() )
|
||||
return false;
|
||||
|
||||
// If they have the old mode enabled (always break immediately), then just break right into
|
||||
// the debugger like we used to do.
|
||||
if ( IsDebugBreakEnabled() )
|
||||
return true;
|
||||
|
||||
// Have ALL Asserts been disabled?
|
||||
if ( !g_bAssertsEnabled )
|
||||
return false;
|
||||
|
||||
// Has this specific Assert been disabled?
|
||||
if ( !AreAssertsEnabledInFileLine( pFilename, line ) )
|
||||
return false;
|
||||
|
||||
// Now create the dialog.
|
||||
g_Info.m_pFilename = pFilename;
|
||||
g_Info.m_iLine = line;
|
||||
g_Info.m_pExpression = pExpression;
|
||||
|
||||
if ( AssertStack() )
|
||||
{
|
||||
IgnoreAssertsNearby( 0 );
|
||||
// @TODO: add-back callstack spew support
|
||||
Warning( "%s (%d) : Assertion callstack...(NOT IMPLEMENTED IN NEW LOGGING SYSTEM.)\n", pFilename, line );
|
||||
// Warning_SpewCallStack( 10, "%s (%d) : Assertion callstack...\n", pFilename, line );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( AllAssertOnce() )
|
||||
{
|
||||
IgnoreAssertsNearby( 0 );
|
||||
}
|
||||
|
||||
g_bBreak = false;
|
||||
|
||||
#if defined( _X360 )
|
||||
|
||||
char cmdString[XBX_MAX_RCMDLENGTH];
|
||||
|
||||
// Before calling VXConsole, init the global variable that receives the result
|
||||
g_VXConsoleAssertReturnValue = -1;
|
||||
|
||||
// Message VXConsole to pop up a PC-side Assert dialog
|
||||
_snprintf( cmdString, sizeof(cmdString), "Assert() 0x%.8x File: %s\tLine: %d\t%s",
|
||||
&g_VXConsoleAssertReturnValue, pFilename, line, pExpression );
|
||||
XBX_SendRemoteCommand( cmdString, false );
|
||||
|
||||
// We sent a synchronous message, so g_xbx_dbgVXConsoleAssertReturnValue should have been overwritten by now
|
||||
if ( g_VXConsoleAssertReturnValue == -1 )
|
||||
{
|
||||
// VXConsole isn't connected/running - default to the old behaviour (break)
|
||||
g_bBreak = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Respond to what the user selected
|
||||
switch( g_VXConsoleAssertReturnValue )
|
||||
{
|
||||
case ASSERT_ACTION_IGNORE_FILE:
|
||||
IgnoreAssertsInCurrentFile();
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_THIS:
|
||||
// Ignore this Assert once
|
||||
break;
|
||||
case ASSERT_ACTION_BREAK:
|
||||
// Break on this Assert
|
||||
g_bBreak = true;
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_ALL:
|
||||
// Ignore all Asserts from now on
|
||||
g_bAssertsEnabled = false;
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_ALWAYS:
|
||||
// Ignore this Assert from now on
|
||||
IgnoreAssertsNearby( 0 );
|
||||
break;
|
||||
case ASSERT_ACTION_OTHER:
|
||||
default:
|
||||
// Error... just break
|
||||
XBX_Error( "DoNewAssertDialog: invalid Assert response returned from VXConsole - breaking to debugger" );
|
||||
g_bBreak = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#elif defined( _PS3 )
|
||||
// There are a few ways to handle this sort of assert behavior with the PS3 / Target Manager API.
|
||||
// One is to use a DebuggerBreak per usual, and then SNProcessContinue in the TMAPI to make
|
||||
// the game resume after a breakpoint. (You can use snIsDebuggerPresent() to determine if
|
||||
// the debugger is attached, although really it doesn't matter here.)
|
||||
// This doesn't work because the DebuggerBreak() is actually an interrupt op, and so Continue()
|
||||
// won't continue past it -- you need to do that from inside the ProDG debugger itself.
|
||||
// Another is to wait on a mutex here and then trip it from the TMAPI, but there isn't
|
||||
// a clean way to trip sync primitives from TMAPI.
|
||||
// Another way is to suspend the thread here and have TMAPI resume it.
|
||||
// The simplest way is to spin-wait on a shared variable that you expect the
|
||||
// TMAPI to poke into memory. I'm trying that.
|
||||
|
||||
char cmdString[XBX_MAX_RCMDLENGTH];
|
||||
|
||||
// Before calling VXConsole, init the global variable that receives the result
|
||||
g_VXConsoleAssertReturnValue = -1;
|
||||
|
||||
// Message VXConsole to pop up a PC-side Assert dialog
|
||||
_snprintf( cmdString, sizeof(cmdString), "Assert() 0x%.8x File: %s\tLine: %d\t%s",
|
||||
&g_VXConsoleAssertReturnValue, pFilename, line, pExpression );
|
||||
XBX_SendRemoteCommand( cmdString, false );
|
||||
|
||||
if ( g_pValvePS3Console->IsConsoleConnected() )
|
||||
{
|
||||
// DebuggerBreak();
|
||||
|
||||
while ( g_VXConsoleAssertReturnValue == -1 )
|
||||
{
|
||||
ThreadSleep( 1000 );
|
||||
}
|
||||
|
||||
// assume that the VX has poked the return value
|
||||
// Respond to what the user selected
|
||||
switch( g_VXConsoleAssertReturnValue )
|
||||
{
|
||||
case ASSERT_ACTION_IGNORE_FILE:
|
||||
IgnoreAssertsInCurrentFile();
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_THIS:
|
||||
// Ignore this Assert once
|
||||
break;
|
||||
case ASSERT_ACTION_BREAK:
|
||||
// Break on this Assert
|
||||
g_bBreak = true;
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_ALL:
|
||||
// Ignore all Asserts from now on
|
||||
g_bAssertsEnabled = false;
|
||||
break;
|
||||
case ASSERT_ACTION_IGNORE_ALWAYS:
|
||||
// Ignore this Assert from now on
|
||||
IgnoreAssertsNearby( 0 );
|
||||
break;
|
||||
case ASSERT_ACTION_OTHER:
|
||||
default:
|
||||
// nothing.
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( g_pValvePS3Console->IsDebuggerPresent() )
|
||||
{
|
||||
g_bBreak = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ignore the assert
|
||||
}
|
||||
|
||||
|
||||
#elif defined( POSIX )
|
||||
|
||||
fprintf(stderr, "%s %i %s\n", pFilename, line, pExpression);
|
||||
if ( getenv( "RAISE_ON_ASSERT" ) )
|
||||
{
|
||||
DebuggerBreak();
|
||||
g_bBreak = true;
|
||||
}
|
||||
|
||||
#elif defined( _WIN32 )
|
||||
|
||||
if ( !g_hTier0Instance || !ThreadInMainThread() )
|
||||
{
|
||||
int result = MessageBox( NULL, pExpression, "Assertion Failed", MB_SYSTEMMODAL | MB_CANCELTRYCONTINUE );
|
||||
|
||||
if ( result == IDCANCEL )
|
||||
{
|
||||
IgnoreAssertsNearby( 0 );
|
||||
}
|
||||
else if ( result == IDCONTINUE )
|
||||
{
|
||||
g_bBreak = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HWND hParentWindow = FindLikelyParentWindow();
|
||||
|
||||
DialogBox( g_hTier0Instance, MAKEINTRESOURCE( IDD_ASSERT_DIALOG ), hParentWindow, AssertDialogProc );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return g_bBreak;
|
||||
}
|
||||
|
||||
Vendored
+676
@@ -0,0 +1,676 @@
|
||||
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/icommandline.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0_strtools.h"
|
||||
#include "tier1/strtools.h" // this is included for the definition of V_isspace()
|
||||
|
||||
#ifdef PLATFORM_POSIX
|
||||
#include <limits.h>
|
||||
#define _MAX_PATH PATH_MAX
|
||||
#endif
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
static const int MAX_PARAMETER_LEN = 128;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Implements ICommandLine
|
||||
//-----------------------------------------------------------------------------
|
||||
class CCommandLine : public ICommandLine
|
||||
{
|
||||
public:
|
||||
// Construction
|
||||
CCommandLine( void );
|
||||
virtual ~CCommandLine( void );
|
||||
|
||||
// Implements ICommandLine
|
||||
virtual void CreateCmdLine( const char *commandline );
|
||||
virtual void CreateCmdLine( int argc, char **argv );
|
||||
virtual const char *GetCmdLine( void ) const;
|
||||
virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const;
|
||||
|
||||
virtual void RemoveParm( const char *parm );
|
||||
virtual void AppendParm( const char *pszParm, const char *pszValues );
|
||||
|
||||
virtual int ParmCount() const;
|
||||
virtual int FindParm( const char *psz ) const;
|
||||
virtual const char* GetParm( int nIndex ) const;
|
||||
|
||||
virtual const char *ParmValue( const char *psz, const char *pDefaultVal = NULL ) const;
|
||||
virtual int ParmValue( const char *psz, int nDefaultVal ) const;
|
||||
virtual float ParmValue( const char *psz, float flDefaultVal ) const;
|
||||
virtual void SetParm( int nIndex, char const *pParm );
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
MAX_PARAMETER_LEN = 128,
|
||||
MAX_PARAMETERS = 256,
|
||||
};
|
||||
|
||||
// When the commandline contains @name, it reads the parameters from that file
|
||||
void LoadParametersFromFile( const char *&pSrc, char *&pDst, intp maxDestLen, bool bInQuotes );
|
||||
|
||||
// Parse command line...
|
||||
void ParseCommandLine();
|
||||
|
||||
// Frees the command line arguments
|
||||
void CleanUpParms();
|
||||
|
||||
// Adds an argument..
|
||||
void AddArgument( const char *pFirst, const char *pLast );
|
||||
|
||||
// Copy of actual command line
|
||||
char *m_pszCmdLine;
|
||||
|
||||
// Pointers to each argument...
|
||||
int m_nParmCount;
|
||||
char *m_ppParms[MAX_PARAMETERS];
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Instance singleton and expose interface to rest of code
|
||||
//-----------------------------------------------------------------------------
|
||||
static CCommandLine g_CmdLine;
|
||||
ICommandLine *CommandLine()
|
||||
{
|
||||
return &g_CmdLine;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CCommandLine::CCommandLine( void )
|
||||
{
|
||||
m_pszCmdLine = NULL;
|
||||
m_nParmCount = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CCommandLine::~CCommandLine( void )
|
||||
{
|
||||
CleanUpParms();
|
||||
delete[] m_pszCmdLine;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Read commandline from file instead...
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::LoadParametersFromFile( const char *&pSrc, char *&pDst, intp maxDestLen, bool bInQuotes )
|
||||
{
|
||||
// Suck out the file name
|
||||
char szFileName[ MAX_PATH ];
|
||||
char *pOut;
|
||||
char *pDestStart = pDst;
|
||||
|
||||
if ( maxDestLen < 3 )
|
||||
return;
|
||||
|
||||
// Skip the @ sign
|
||||
pSrc++;
|
||||
|
||||
pOut = szFileName;
|
||||
|
||||
char terminatingChar = ' ';
|
||||
if ( bInQuotes )
|
||||
terminatingChar = '\"';
|
||||
|
||||
while ( *pSrc && *pSrc != terminatingChar )
|
||||
{
|
||||
*pOut++ = *pSrc++;
|
||||
if ( (pOut - szFileName) >= (MAX_PATH-1) )
|
||||
break;
|
||||
}
|
||||
|
||||
*pOut = '\0';
|
||||
|
||||
// Skip the space after the file name
|
||||
if ( *pSrc )
|
||||
pSrc++;
|
||||
|
||||
// Now read in parameters from file
|
||||
FILE *fp = fopen( szFileName, "r" );
|
||||
if ( fp )
|
||||
{
|
||||
char c;
|
||||
c = (char)fgetc( fp );
|
||||
while ( c != EOF )
|
||||
{
|
||||
// Turn return characters into spaces
|
||||
if ( c == '\n' )
|
||||
c = ' ';
|
||||
|
||||
*pDst++ = c;
|
||||
|
||||
// Don't go past the end, and allow for our terminating space character AND a terminating null character.
|
||||
if ( (pDst - pDestStart) >= (maxDestLen-2) )
|
||||
break;
|
||||
|
||||
// Get the next character, if there are more
|
||||
c = (char)fgetc( fp );
|
||||
}
|
||||
|
||||
// Add a terminating space character
|
||||
*pDst++ = ' ';
|
||||
|
||||
fclose( fp );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "Parameter file '%s' not found, skipping...", szFileName );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Creates a command line from the arguments passed in
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::CreateCmdLine( int argc, char **argv )
|
||||
{
|
||||
char cmdline[2048];
|
||||
cmdline[0] = 0;
|
||||
const int MAX_CHARS = sizeof(cmdline) - 1;
|
||||
cmdline[MAX_CHARS] = 0;
|
||||
for ( int i = 0; i < argc; ++i )
|
||||
{
|
||||
strncat( cmdline, "\"", MAX_CHARS );
|
||||
strncat( cmdline, argv[i], MAX_CHARS );
|
||||
strncat( cmdline, "\"", MAX_CHARS );
|
||||
strncat( cmdline, " ", MAX_CHARS );
|
||||
}
|
||||
|
||||
CreateCmdLine( cmdline );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Create a command line from the passed in string
|
||||
// Note that if you pass in a @filename, then the routine will read settings
|
||||
// from a file instead of the command line
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::CreateCmdLine( const char *commandline )
|
||||
{
|
||||
if ( m_pszCmdLine )
|
||||
{
|
||||
delete[] m_pszCmdLine;
|
||||
}
|
||||
|
||||
char szFull[ 4096 ];
|
||||
|
||||
char *pDst = szFull;
|
||||
const char *pSrc = commandline;
|
||||
|
||||
bool bInQuotes = false;
|
||||
const char *pInQuotesStart = 0;
|
||||
while ( *pSrc )
|
||||
{
|
||||
// Is this an unslashed quote?
|
||||
if ( *pSrc == '"' )
|
||||
{
|
||||
if ( pSrc == commandline || ( pSrc[-1] != '/' && pSrc[-1] != '\\' ) )
|
||||
{
|
||||
bInQuotes = !bInQuotes;
|
||||
pInQuotesStart = pSrc + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( *pSrc == '@' )
|
||||
{
|
||||
if ( pSrc == commandline || (!bInQuotes && V_isspace( pSrc[-1] )) || (bInQuotes && pSrc == pInQuotesStart) )
|
||||
{
|
||||
LoadParametersFromFile( pSrc, pDst, sizeof( szFull ) - (pDst - szFull), bInQuotes );
|
||||
if ( bInQuotes )
|
||||
{
|
||||
// Back up over the opening quote which has already been copied to pDst.
|
||||
// Otherwise we end up with an orphaned single quote which causes later
|
||||
// parsing problems.
|
||||
--pDst;
|
||||
Assert( *pDst == '\"' );
|
||||
}
|
||||
// The opening quote, if any, is now gone.
|
||||
bInQuotes = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't go past the end.
|
||||
if ( (pDst - szFull) >= (sizeof( szFull ) - 1) )
|
||||
break;
|
||||
|
||||
*pDst++ = *pSrc++;
|
||||
}
|
||||
|
||||
*pDst = '\0';
|
||||
|
||||
size_t len = strlen( szFull ) + 1;
|
||||
m_pszCmdLine = new char[len];
|
||||
memcpy( m_pszCmdLine, szFull, len );
|
||||
|
||||
#if defined( PLATFORM_PS3 )
|
||||
Plat_SetCommandLine( m_pszCmdLine );
|
||||
#endif
|
||||
|
||||
ParseCommandLine();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finds a string in another string with a case insensitive test
|
||||
//-----------------------------------------------------------------------------
|
||||
static char * _stristr( char * pStr, const char * pSearch )
|
||||
{
|
||||
AssertValidStringPtr(pStr);
|
||||
AssertValidStringPtr(pSearch);
|
||||
|
||||
if (!pStr || !pSearch)
|
||||
return 0;
|
||||
|
||||
char* pLetter = pStr;
|
||||
|
||||
// Check the entire string
|
||||
while (*pLetter != 0)
|
||||
{
|
||||
// Skip over non-matches
|
||||
if (tolower((unsigned char)*pLetter) == tolower((unsigned char)*pSearch))
|
||||
{
|
||||
// Check for match
|
||||
char const* pMatch = pLetter + 1;
|
||||
char const* pTest = pSearch + 1;
|
||||
while (*pTest != 0)
|
||||
{
|
||||
// We've run off the end; don't bother.
|
||||
if (*pMatch == 0)
|
||||
return 0;
|
||||
|
||||
if (tolower((unsigned char)*pMatch) != tolower((unsigned char)*pTest))
|
||||
break;
|
||||
|
||||
++pMatch;
|
||||
++pTest;
|
||||
}
|
||||
|
||||
// Found a match!
|
||||
if (*pTest == 0)
|
||||
return pLetter;
|
||||
}
|
||||
|
||||
++pLetter;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Remove specified string ( and any args attached to it ) from command line
|
||||
// Input : *pszParm -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::RemoveParm( const char *pszParm )
|
||||
{
|
||||
if ( !m_pszCmdLine )
|
||||
return;
|
||||
|
||||
// Search for first occurrence of pszParm
|
||||
char *p, *found;
|
||||
char *pnextparam;
|
||||
intp n;
|
||||
size_t curlen;
|
||||
|
||||
p = m_pszCmdLine;
|
||||
while ( *p )
|
||||
{
|
||||
curlen = strlen( p );
|
||||
|
||||
found = _stristr( p, pszParm );
|
||||
if ( !found )
|
||||
break;
|
||||
|
||||
pnextparam = found + 1;
|
||||
bool bHadQuote = false;
|
||||
if ( found > m_pszCmdLine && found[-1] == '\"' )
|
||||
bHadQuote = true;
|
||||
|
||||
while ( pnextparam && *pnextparam && (*pnextparam != ' ') && (*pnextparam != '\"') )
|
||||
pnextparam++;
|
||||
|
||||
if ( pnextparam && ( static_cast<size_t>( pnextparam - found ) > strlen( pszParm ) ) )
|
||||
{
|
||||
p = pnextparam;
|
||||
continue;
|
||||
}
|
||||
|
||||
while ( pnextparam && *pnextparam && (*pnextparam != '-') && (*pnextparam != '+') )
|
||||
pnextparam++;
|
||||
|
||||
if ( bHadQuote )
|
||||
{
|
||||
found--;
|
||||
}
|
||||
|
||||
if ( pnextparam && *pnextparam )
|
||||
{
|
||||
// We are either at the end of the string, or at the next param. Just chop out the current param.
|
||||
n = curlen - ( pnextparam - p ); // # of characters after this param.
|
||||
memmove( found, pnextparam, n );
|
||||
|
||||
found[n] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear out rest of string.
|
||||
n = pnextparam - found;
|
||||
memset( found, 0, n );
|
||||
}
|
||||
}
|
||||
|
||||
// Strip and trailing ' ' characters left over.
|
||||
while ( 1 )
|
||||
{
|
||||
intp len = strlen( m_pszCmdLine );
|
||||
if ( len == 0 || m_pszCmdLine[ len - 1 ] != ' ' )
|
||||
break;
|
||||
|
||||
m_pszCmdLine[len - 1] = '\0';
|
||||
}
|
||||
|
||||
ParseCommandLine();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Append parameter and argument values to command line
|
||||
// Input : *pszParm -
|
||||
// *pszValues -
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::AppendParm( const char *pszParm, const char *pszValues )
|
||||
{
|
||||
intp nNewLength = 0;
|
||||
char *pCmdString;
|
||||
|
||||
nNewLength = strlen( pszParm ); // Parameter.
|
||||
if ( pszValues )
|
||||
nNewLength += strlen( pszValues ) + 1; // Values + leading space character.
|
||||
nNewLength++; // Terminal 0;
|
||||
|
||||
if ( !m_pszCmdLine )
|
||||
{
|
||||
m_pszCmdLine = new char[ nNewLength ];
|
||||
strcpy( m_pszCmdLine, pszParm );
|
||||
if ( pszValues )
|
||||
{
|
||||
strcat( m_pszCmdLine, " " );
|
||||
strcat( m_pszCmdLine, pszValues );
|
||||
}
|
||||
|
||||
ParseCommandLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any remnants from the current Cmd Line.
|
||||
RemoveParm( pszParm );
|
||||
|
||||
nNewLength += strlen( m_pszCmdLine ) + 1 + 1;
|
||||
|
||||
pCmdString = new char[ nNewLength ];
|
||||
memset( pCmdString, 0, nNewLength );
|
||||
|
||||
strcpy ( pCmdString, m_pszCmdLine ); // Copy old command line.
|
||||
strcat ( pCmdString, " " ); // Put in a space
|
||||
strcat ( pCmdString, pszParm );
|
||||
if ( pszValues )
|
||||
{
|
||||
strcat( pCmdString, " " );
|
||||
strcat( pCmdString, pszValues );
|
||||
}
|
||||
|
||||
// Kill off the old one
|
||||
delete[] m_pszCmdLine;
|
||||
|
||||
// Point at the new command line.
|
||||
m_pszCmdLine = pCmdString;
|
||||
|
||||
ParseCommandLine();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Return current command line
|
||||
// Output : const char
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *CCommandLine::GetCmdLine( void ) const
|
||||
{
|
||||
return m_pszCmdLine;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Search for the parameter in the current commandline
|
||||
// Input : *psz -
|
||||
// **ppszValue -
|
||||
// Output : char
|
||||
//-----------------------------------------------------------------------------
|
||||
const char *CCommandLine::CheckParm( const char *psz, const char **ppszValue ) const
|
||||
{
|
||||
if ( ppszValue )
|
||||
*ppszValue = NULL;
|
||||
|
||||
int i = FindParm( psz );
|
||||
if ( i == 0 )
|
||||
return NULL;
|
||||
|
||||
if ( ppszValue )
|
||||
{
|
||||
if ( (i+1) >= m_nParmCount )
|
||||
{
|
||||
*ppszValue = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppszValue = m_ppParms[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
return m_ppParms[i];
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Adds an argument..
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::AddArgument( const char *pFirst, const char *pLast )
|
||||
{
|
||||
if ( pLast == pFirst )
|
||||
return;
|
||||
|
||||
if ( m_nParmCount >= MAX_PARAMETERS )
|
||||
Error( "CCommandLine::AddArgument: exceeded %d parameters", MAX_PARAMETERS );
|
||||
|
||||
size_t nLen = ( pLast - pFirst ) + 1;
|
||||
m_ppParms[m_nParmCount] = new char[nLen];
|
||||
memcpy( m_ppParms[m_nParmCount], pFirst, nLen - 1 );
|
||||
m_ppParms[m_nParmCount][nLen - 1] = 0;
|
||||
|
||||
++m_nParmCount;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parse command line...
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::ParseCommandLine()
|
||||
{
|
||||
CleanUpParms();
|
||||
if (!m_pszCmdLine)
|
||||
return;
|
||||
|
||||
const char *pChar = m_pszCmdLine;
|
||||
while ( *pChar && V_isspace(*pChar) )
|
||||
{
|
||||
++pChar;
|
||||
}
|
||||
|
||||
bool bInQuotes = false;
|
||||
const char *pFirstLetter = NULL;
|
||||
for ( ; *pChar; ++pChar )
|
||||
{
|
||||
if ( bInQuotes )
|
||||
{
|
||||
if ( *pChar != '\"' )
|
||||
continue;
|
||||
|
||||
AddArgument( pFirstLetter, pChar );
|
||||
pFirstLetter = NULL;
|
||||
bInQuotes = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Haven't started a word yet...
|
||||
if ( !pFirstLetter )
|
||||
{
|
||||
if ( *pChar == '\"' )
|
||||
{
|
||||
bInQuotes = true;
|
||||
pFirstLetter = pChar + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( V_isspace( *pChar ) )
|
||||
continue;
|
||||
|
||||
pFirstLetter = pChar;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Here, we're in the middle of a word. Look for the end of it.
|
||||
if ( V_isspace( *pChar ) )
|
||||
{
|
||||
AddArgument( pFirstLetter, pChar );
|
||||
pFirstLetter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ( pFirstLetter )
|
||||
{
|
||||
AddArgument( pFirstLetter, pChar );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Individual command line arguments
|
||||
//-----------------------------------------------------------------------------
|
||||
void CCommandLine::CleanUpParms()
|
||||
{
|
||||
for ( int i = 0; i < m_nParmCount; ++i )
|
||||
{
|
||||
delete [] m_ppParms[i];
|
||||
m_ppParms[i] = NULL;
|
||||
}
|
||||
m_nParmCount = 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns individual command line arguments
|
||||
//-----------------------------------------------------------------------------
|
||||
int CCommandLine::ParmCount() const
|
||||
{
|
||||
return m_nParmCount;
|
||||
}
|
||||
|
||||
int CCommandLine::FindParm( const char *psz ) const
|
||||
{
|
||||
// Start at 1 so as to not search the exe name
|
||||
for ( int i = 1; i < m_nParmCount; ++i )
|
||||
{
|
||||
if ( !V_tier0_stricmp( psz, m_ppParms[i] ) )
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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] );
|
||||
}
|
||||
Vendored
+697
@@ -0,0 +1,697 @@
|
||||
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(_X360)
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include "cputopology.h"
|
||||
#elif defined( PLATFORM_OSX )
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#ifndef _PS3
|
||||
#include "tier0_strtools.h"
|
||||
#endif
|
||||
|
||||
//#include "tier1/strtools.h" // this is included for the definition of V_isspace()
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
const tchar* GetProcessorVendorId();
|
||||
|
||||
static bool cpuid(uint32 function, uint32& out_eax, uint32& out_ebx, uint32& out_ecx, uint32& out_edx)
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 )
|
||||
return false;
|
||||
#elif 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(_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 false;
|
||||
#else
|
||||
bool retval = true;
|
||||
uint32 local_eax, local_ebx, local_ecx, local_edx;
|
||||
_asm pushad;
|
||||
|
||||
__try
|
||||
{
|
||||
_asm
|
||||
{
|
||||
xor edx, edx // Clue the compiler that EDX & others is about to be used.
|
||||
xor ecx, ecx
|
||||
xor ebx, ebx // <Sergiy> Note: if I don't zero these out, cpuid sometimes won't work, I didn't find out why yet
|
||||
mov eax, function // set up CPUID to return processor version and features
|
||||
// 0 = vendor string, 1 = version info, 2 = cache info
|
||||
cpuid // code bytes = 0fh, 0a2h
|
||||
mov 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
|
||||
uint32 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;
|
||||
}
|
||||
|
||||
uint32 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
|
||||
uint32 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
|
||||
uint32 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
|
||||
uint32 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
|
||||
|
||||
uint32 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;
|
||||
|
||||
uint32 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;
|
||||
|
||||
uint32 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
|
||||
uint32 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
|
||||
uint32 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
|
||||
uint32 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
|
||||
uint32 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
|
||||
uint32 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.
|
||||
// If it's supported, it does not mean that it's been enabled. So we test another flag to see if it's enabled
|
||||
// See Intel Processor Identification and the CPUID instruction Application Note 485
|
||||
// http://www.intel.com/Assets/PDF/appnote/241618.pdf
|
||||
static bool HTSupported(void)
|
||||
{
|
||||
#if ( defined( _X360 ) || defined( _PS3 ) )
|
||||
// not entirtely sure about the semantic of HT support, it being an intel name
|
||||
// are we asking about HW threads or HT?
|
||||
return true;
|
||||
#else
|
||||
enum {
|
||||
HT_BIT = 0x10000000, // EDX[28] - Bit 28 set indicates Hyper-Threading Technology is supported in hardware.
|
||||
FAMILY_ID = 0x0f00, // EAX[11:8] - Bit 11 thru 8 contains family processor id
|
||||
EXT_FAMILY_ID = 0x0f00000, // EAX[23:20] - Bit 23 thru 20 contains extended family processor id
|
||||
FAMILY_ID_386 = 0x0300,
|
||||
FAMILY_ID_486 = 0x0400, // EAX[8:12] - 486, 487 and overdrive
|
||||
FAMILY_ID_PENTIUM = 0x0500, // Pentium, Pentium OverDrive 60 - 200
|
||||
FAMILY_ID_PENTIUM_PRO = 0x0600,// P Pro, P II, P III, P M, Celeron M, Core Duo, Core Solo, Core2 Duo, Core2 Extreme, P D, Xeon model F,
|
||||
// also 45-nm : Intel Atom, Core i7, Xeon MP ; see Intel Processor Identification and the CPUID instruction pg 20,21
|
||||
|
||||
FAMILY_ID_EXTENDED = 0x0F00 // P IV, Xeon, Celeron D, P D,
|
||||
};
|
||||
|
||||
uint32 unused,
|
||||
reg_eax = 0,
|
||||
reg_ebx = 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,reg_ebx,unused,reg_edx) )
|
||||
return false;
|
||||
|
||||
// <Sergiy> Previously, we detected P4 specifically; now, we detect GenuineIntel with HT enabled in general
|
||||
// if (((reg_eax & FAMILY_ID) == FAMILY_ID_EXTENDED) || (reg_eax & EXT_FAMILY_ID))
|
||||
|
||||
// Check to see if this is an Intel Processor with HT or CMT capability , and if HT/CMT is enabled
|
||||
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 implemented
|
||||
((reg_ebx >> 16) & 0xFF) > 1 ; // Hyper-Threading OR Core Multi-Processing has been enabled
|
||||
|
||||
return false; // This is not a genuine Intel processor.
|
||||
#endif
|
||||
}
|
||||
|
||||
// See Intel Processor Identification and the CPUID instruction Application Note 485
|
||||
// http://www.intel.com/Assets/PDF/appnote/241618.pdf
|
||||
int LogicalProcessorsPerCore()
|
||||
{
|
||||
#if defined( _X360 ) || defined( _PS3 ) || defined( LINUX )
|
||||
return 2; //
|
||||
#elif defined(_WIN32)
|
||||
uint32 nMaxStandardFnSupported, nVendorId[3];
|
||||
if( !cpuid( 0, nMaxStandardFnSupported,nVendorId[0],nVendorId[2],nVendorId[1] ) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32 nFn1_Eax, nFn1_Ebx, nFn1_Ecx, nFn1_Edx;
|
||||
if( !cpuid( 1, nFn1_Eax, nFn1_Ebx, nFn1_Ecx, nFn1_Edx) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum CpuidFnMasks
|
||||
{
|
||||
HTT = 0x10000000, // Fn0000_0001 EDX[28]
|
||||
LogicalProcessorCount = 0x00FF0000, // Fn0000_0001 EBX[23:16]
|
||||
ApicId = 0xFF000000, // Fn0000_0001 EBX[31:24]
|
||||
NC_Intel = 0xFC000000, // Fn0000_0004 EAX[31:26]
|
||||
NC_Amd = 0x000000FF, // Fn8000_0008 ECX[7:0]
|
||||
CmpLegacy_Amd = 0x00000002, // Fn8000_0001 ECX[1]
|
||||
ApicIdCoreIdSize_Amd = 0x0000F000 // Fn8000_0008 ECX[15:12]
|
||||
};
|
||||
|
||||
// Determine if hardware threading is enabled.
|
||||
if( nFn1_Edx & HTT )
|
||||
{
|
||||
// Determine the total number of logical processors per package.
|
||||
int nLogProcsPerPkg = ( nFn1_Ebx & LogicalProcessorCount ) >> 16;
|
||||
int nCoresPerPkg = 1;
|
||||
|
||||
if( ( ( nFn1_Ebx >> 16 ) & 0xFF ) <= 1 ) // Has Hyper-Threading OR Core Multi-Processing not been enabled ?
|
||||
{
|
||||
// NOTE: This is only tested on Intel CPUs; I don't know if it's true on AMD, as I have no HT AMD to test on
|
||||
return 1; // HT was turned off, for all intents and purposes in our engine it means one logical CPU per core
|
||||
}
|
||||
|
||||
// Determine the total number of cores per package. This info
|
||||
// is extracted differently dependending on the cpu vendor.
|
||||
if( nVendorId[0] == 'uneG' && nVendorId[1] == 'Ieni' && nVendorId[2] == 'letn' ) // GenuineIntel
|
||||
{
|
||||
if( nMaxStandardFnSupported >= 4 )
|
||||
{
|
||||
uint32 nFn4_Eax, nFn4_Ebx, nFn4_Ecx, nFn4_Edx ;
|
||||
if( cpuid( 4, nFn4_Eax, nFn4_Ebx, nFn4_Ecx, nFn4_Edx ) )
|
||||
{
|
||||
nCoresPerPkg = ( ( nFn4_Eax & NC_Intel ) >> 26 ) + 1;
|
||||
|
||||
}
|
||||
}
|
||||
// <Sergiy> as the DirectX CoreDetection sample goes, the logic is that on old processors where
|
||||
// the functions aren't supported, we assume one core per package, multiple logical processors per package
|
||||
// I suspect this may be wrong, especially for AMD processors.
|
||||
return nLogProcsPerPkg / nCoresPerPkg;
|
||||
}
|
||||
#if 0 // <Sergiy> To make as concervative change as possible now, I'll skip AMD hyperthread detection
|
||||
else
|
||||
{
|
||||
if( nVendorId[0] == 'htuA' && nVendorId[1] == 'itne' && nVendorId[2] == 'DMAc' ) // AuthenticAMD
|
||||
{
|
||||
uint32 nFnx8_Eax, nFnx8_Ebx, nFnx8_Ecx, nFnx8_Edx ;
|
||||
if( cpuid( 0x80000008, nFnx8_Eax, nFnx8_Ebx, nFnx8_Ecx, nFnx8_Edx ) )
|
||||
{
|
||||
// AMD reports the msb width of the CORE_ID bit field of the APIC ID
|
||||
// in ApicIdCoreIdSize_Amd. The maximum value represented by the msb
|
||||
// width is the theoretical number of cores the processor can support
|
||||
// and not the actual number of current cores, which is how the msb width
|
||||
// of the CORE_ID bit field has been traditionally determined. If the
|
||||
// ApicIdCoreIdSize_Amd value is zero, then you use the traditional method
|
||||
// to determine the CORE_ID msb width.
|
||||
DWORD msbWidth = nFnx8_Ecx & ApicIdCoreIdSize_Amd;
|
||||
if( msbWidth )
|
||||
{
|
||||
// Set nCoresPerPkg to the maximum theortical number of cores
|
||||
// the processor package can support (2 ^ width) so the APIC
|
||||
// extractor object can be configured to extract the proper
|
||||
// values from an APIC.
|
||||
nCoresPerPkg = 1 << ( msbWidth >> 12 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set nCoresPerPkg to the actual number of cores being reported
|
||||
// by the CPUID instruction.
|
||||
nCoresPerPkg = ( nFnx8_Ecx & NC_Amd ) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// <Sergiy> as the DirectX CoreDetection sample goes, the logic is that on old processors where
|
||||
// the functions aren't supported, we assume one core per package, multiple logical processors per package
|
||||
// I suspect this may be wrong, especially for AMD processors.
|
||||
return nLogProcsPerPkg / nCoresPerPkg;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Measure the processor clock speed by sampling the cycle count, waiting
|
||||
// for some fraction of a second, then measuring the elapsed number of cycles.
|
||||
static int64 CalculateClockSpeed()
|
||||
{
|
||||
#if defined( _X360 ) || defined(_PS3)
|
||||
// Xbox360 and PS3 have the same clock speed and share a lot of characteristics on PPU
|
||||
return 3200000000LL;
|
||||
#else
|
||||
#if defined( _WIN32 )
|
||||
LARGE_INTEGER waitTime, startCount, curCount;
|
||||
CCycleCount start, end;
|
||||
|
||||
// Take 1/32 of a second for the measurement.
|
||||
QueryPerformanceFrequency( &waitTime );
|
||||
int scale = 5;
|
||||
waitTime.QuadPart >>= scale;
|
||||
|
||||
QueryPerformanceCounter( &startCount );
|
||||
start.Sample();
|
||||
do
|
||||
{
|
||||
QueryPerformanceCounter( &curCount );
|
||||
}
|
||||
while ( curCount.QuadPart - startCount.QuadPart < waitTime.QuadPart );
|
||||
end.Sample();
|
||||
|
||||
return (end.m_Int64 - start.m_Int64) << scale;
|
||||
#elif defined(POSIX)
|
||||
uint64 CalculateCPUFreq(); // from cpu_linux.cpp
|
||||
int64 freq =(int64)CalculateCPUFreq();
|
||||
if ( freq == 0 ) // couldn't calculate clock speed
|
||||
{
|
||||
Error( "Unable to determine CPU Frequency\n" );
|
||||
}
|
||||
return freq;
|
||||
#else
|
||||
#error "Please implement Clock Speed function for this platform"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static CPUInformation s_cpuInformation;
|
||||
|
||||
const CPUInformation& GetCPUInformation()
|
||||
{
|
||||
CPUInformation &pi = s_cpuInformation;
|
||||
// Has the structure already been initialized and filled out?
|
||||
if ( pi.m_Size == sizeof(pi) )
|
||||
return pi;
|
||||
|
||||
// Redundant, but just in case the user somehow messes with the size.
|
||||
memset(&pi, 0x0, sizeof(pi));
|
||||
|
||||
// Fill out the structure, and return it:
|
||||
pi.m_Size = sizeof(pi);
|
||||
|
||||
// Grab the processor frequency:
|
||||
pi.m_Speed = CalculateClockSpeed();
|
||||
|
||||
// Get the logical and physical processor counts:
|
||||
|
||||
#if defined( _X360 )
|
||||
pi.m_nPhysicalProcessors = 3;
|
||||
pi.m_nLogicalProcessors = 6;
|
||||
#elif defined( _PS3 )
|
||||
pi.m_nPhysicalProcessors = 1;
|
||||
pi.m_nLogicalProcessors = 2;
|
||||
#elif defined(_WIN32) && !defined( _X360 )
|
||||
SYSTEM_INFO si;
|
||||
ZeroMemory( &si, sizeof(si) );
|
||||
|
||||
GetSystemInfo( &si );
|
||||
|
||||
// Sergiy: fixing: si.dwNumberOfProcessors is the number of logical processors according to experiments on i7, P4 and a DirectX sample (Aug'09)
|
||||
// this is contrary to MSDN documentation on GetSystemInfo()
|
||||
//
|
||||
pi.m_nLogicalProcessors = si.dwNumberOfProcessors;
|
||||
if ( 0 == V_tier0_stricmp( GetProcessorVendorId(), "AuthenticAMD" ) )
|
||||
{
|
||||
// quick fix for AMD Phenom: it reports 3 logical cores and 4 physical cores;
|
||||
// no AMD CPUs by the end of 2009 have HT, so we'll override HT detection here
|
||||
pi.m_nPhysicalProcessors = pi.m_nLogicalProcessors;
|
||||
}
|
||||
else
|
||||
{
|
||||
CpuTopology topo;
|
||||
pi.m_nPhysicalProcessors = topo.NumberOfSystemCores();
|
||||
}
|
||||
|
||||
// Make sure I always report at least one, when running WinXP with the /ONECPU switch,
|
||||
// it likes to report 0 processors for some reason.
|
||||
if ( pi.m_nPhysicalProcessors == 0 && pi.m_nLogicalProcessors == 0 )
|
||||
{
|
||||
Assert( !"Sergiy: apparently I didn't fix some CPU detection code completely. Let me know and I'll do my best to fix it soon." );
|
||||
pi.m_nPhysicalProcessors = 1;
|
||||
pi.m_nLogicalProcessors = 1;
|
||||
}
|
||||
#elif defined(LINUX)
|
||||
pi.m_nLogicalProcessors = 0;
|
||||
pi.m_nPhysicalProcessors = 0;
|
||||
const int k_cMaxProcessors = 256;
|
||||
bool rgbProcessors[k_cMaxProcessors];
|
||||
memset( rgbProcessors, 0, sizeof( rgbProcessors ) );
|
||||
int cMaxCoreId = 0;
|
||||
|
||||
FILE *fpCpuInfo = fopen( "/proc/cpuinfo", "r" );
|
||||
if ( fpCpuInfo )
|
||||
{
|
||||
char rgchLine[256];
|
||||
while ( fgets( rgchLine, sizeof( rgchLine ), fpCpuInfo ) )
|
||||
{
|
||||
if ( !strncasecmp( rgchLine, "processor", strlen( "processor" ) ) )
|
||||
{
|
||||
pi.m_nLogicalProcessors++;
|
||||
}
|
||||
if ( !strncasecmp( rgchLine, "core id", strlen( "core id" ) ) )
|
||||
{
|
||||
char *pchValue = strchr( rgchLine, ':' );
|
||||
cMaxCoreId = MAX( cMaxCoreId, atoi( pchValue + 1 ) );
|
||||
}
|
||||
if ( !strncasecmp( rgchLine, "physical id", strlen( "physical id" ) ) )
|
||||
{
|
||||
// it seems (based on survey data) that we can see
|
||||
// processor N (N > 0) when it's the only processor in
|
||||
// the system. so keep track of each processor
|
||||
char *pchValue = strchr( rgchLine, ':' );
|
||||
int cPhysicalId = atoi( pchValue + 1 );
|
||||
if ( cPhysicalId < k_cMaxProcessors )
|
||||
rgbProcessors[cPhysicalId] = true;
|
||||
}
|
||||
/* this code will tell us how many physical chips are in the machine, but we want
|
||||
core count, so for the moment, each processor counts as both logical and physical.
|
||||
if ( !strncasecmp( rgchLine, "physical id ", strlen( "physical id " ) ) )
|
||||
{
|
||||
char *pchValue = strchr( rgchLine, ':' );
|
||||
pi.m_nPhysicalProcessors = MAX( pi.m_nPhysicalProcessors, atol( pchValue ) );
|
||||
}
|
||||
*/
|
||||
}
|
||||
fclose( fpCpuInfo );
|
||||
for ( int i = 0; i < k_cMaxProcessors; i++ )
|
||||
if ( rgbProcessors[i] )
|
||||
pi.m_nPhysicalProcessors++;
|
||||
pi.m_nPhysicalProcessors *= ( cMaxCoreId + 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
pi.m_nLogicalProcessors = 1;
|
||||
pi.m_nPhysicalProcessors = 1;
|
||||
Assert( !"couldn't read cpu information from /proc/cpuinfo" );
|
||||
}
|
||||
|
||||
#elif defined(OSX)
|
||||
int 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 = pi.m_nPhysicalProcessors < pi.m_nLogicalProcessors; //HTSupported();
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
Vendored
+143
@@ -0,0 +1,143 @@
|
||||
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: determine CPU speed under linux
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <tier0/platform.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define rdtsc(x) \
|
||||
__asm__ __volatile__ ("rdtsc" : "=A" (x))
|
||||
|
||||
class TimeVal
|
||||
{
|
||||
public:
|
||||
TimeVal() {}
|
||||
TimeVal& operator=(const TimeVal &val) { m_TimeVal = val.m_TimeVal; }
|
||||
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 = left_us - right_us;
|
||||
return diff_us/1000000;
|
||||
}
|
||||
|
||||
timeval m_TimeVal;
|
||||
};
|
||||
|
||||
// Compute the positive difference between two 64 bit numbers.
|
||||
static inline uint64 diff(uint64 v1, uint64 v2)
|
||||
{
|
||||
uint64 d = v1 - v2;
|
||||
if (d >= 0) return d; else return -d;
|
||||
}
|
||||
|
||||
#ifdef OSX
|
||||
uint64 GetCPUFreqFromPROC()
|
||||
{
|
||||
int mib[2] = {CTL_HW, HW_CPU_FREQ};
|
||||
uint64 frequency = 0;
|
||||
size_t len = sizeof(frequency);
|
||||
|
||||
if (sysctl(mib, 2, &frequency, &len, NULL, 0) == -1)
|
||||
return 0;
|
||||
return frequency;
|
||||
}
|
||||
#else
|
||||
uint64 GetCPUFreqFromPROC()
|
||||
{
|
||||
double mhz = 0;
|
||||
char line[1024], *s, search_str[] = "cpu MHz";
|
||||
FILE *fp;
|
||||
|
||||
/* open proc/cpuinfo */
|
||||
if ((fp = fopen("/proc/cpuinfo", "r")) == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ignore all lines until we reach MHz information */
|
||||
while (fgets(line, 1024, fp) != NULL)
|
||||
{
|
||||
if (strstr(line, search_str) != NULL)
|
||||
{
|
||||
/* ignore all characters in line up to : */
|
||||
for (s = line; *s && (*s != ':'); ++s);
|
||||
/* get MHz number */
|
||||
if (*s && (sscanf(s+1, "%lf", &mhz) == 1))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fp!=NULL) fclose(fp);
|
||||
|
||||
return (uint64)(mhz*1000000);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
uint64 CalculateCPUFreq()
|
||||
{
|
||||
#ifdef LINUX
|
||||
char const *pFreq = getenv("CPU_MHZ");
|
||||
if ( pFreq )
|
||||
{
|
||||
uint64 retVal = 1000000;
|
||||
return retVal * atoi( pFreq );
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compute the period. Loop until we get 3 consecutive periods that
|
||||
// are the same to within a small error. The error is chosen
|
||||
// to be +/- 0.02% on a P-200.
|
||||
const uint64 error = 40000;
|
||||
const int max_iterations = 600;
|
||||
int count;
|
||||
uint64 period, period1 = error * 2, period2 = 0, period3 = 0;
|
||||
|
||||
for (count = 0; count < max_iterations; count++)
|
||||
{
|
||||
TimeVal start_time, end_time;
|
||||
uint64 start_tsc, end_tsc;
|
||||
gettimeofday (&start_time.m_TimeVal, 0);
|
||||
rdtsc (start_tsc);
|
||||
usleep (5000); // sleep for 5 msec
|
||||
gettimeofday (&end_time.m_TimeVal, 0);
|
||||
rdtsc (end_tsc);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Vendored
+1018
File diff suppressed because it is too large
Load Diff
Vendored
+40
@@ -0,0 +1,40 @@
|
||||
//-------------------------------------------------------------------------------------
|
||||
// CpuTopology.h
|
||||
//
|
||||
// CpuToplogy class declaration.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-------------------------------------------------------------------------------------
|
||||
#pragma once
|
||||
#ifndef CPU_TOPOLOGY_H
|
||||
#define CPU_TOPOLOGY_H
|
||||
|
||||
#include "winlite.h"
|
||||
|
||||
class ICpuTopology;
|
||||
|
||||
//---------------------------------------------------------------------------------
|
||||
// Name: CpuToplogy
|
||||
// Desc: This class constructs a supported cpu topology implementation object on
|
||||
// initialization and forwards calls to it. This is the Abstraction class
|
||||
// in the traditional Bridge Pattern.
|
||||
//---------------------------------------------------------------------------------
|
||||
class CpuTopology
|
||||
{
|
||||
public:
|
||||
CpuTopology( BOOL bForceCpuid = FALSE );
|
||||
~CpuTopology();
|
||||
|
||||
BOOL IsDefaultImpl() const;
|
||||
DWORD NumberOfProcessCores() const;
|
||||
DWORD NumberOfSystemCores() const;
|
||||
DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const;
|
||||
|
||||
void ForceCpuid( BOOL bForce );
|
||||
private:
|
||||
void Destroy_();
|
||||
|
||||
ICpuTopology* m_pImpl;
|
||||
};
|
||||
|
||||
#endif // CPU_TOPOLOGY_H
|
||||
Vendored
+627
@@ -0,0 +1,627 @@
|
||||
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
#if defined( PLATFORM_WINDOWS_PC )
|
||||
#define WIN_32_LEAN_AND_MEAN
|
||||
#include <windows.h> // Currently needed for IsBadReadPtr and IsBadWritePtr
|
||||
#pragma comment(lib,"user32.lib") // For MessageBox
|
||||
#endif
|
||||
|
||||
#include "tier0/minidump.h"
|
||||
#include "tier0/stacktools.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include "color.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/threadtools.h"
|
||||
#include "tier0/icommandline.h"
|
||||
#include <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"
|
||||
|
||||
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
#pragma optimize( "g", off ) //variable argument functions seem to screw up stack walking unless this optimization is disabled
|
||||
#pragma warning( disable: 4748 ) // Turn off the warning telling us that optimizations are off if /GS is on
|
||||
#endif
|
||||
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_LOADING, "LOADING" );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Stack attachment management
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
|
||||
static bool s_bCallStacksWithAllWarnings = false; //if true, attach a call stack to every SPEW_WARNING message. Warning()/DevWarning()/...
|
||||
static int s_iWarningMaxCallStackLength = 5;
|
||||
#define AutomaticWarningCallStackLength() (s_bCallStacksWithAllWarnings ? s_iWarningMaxCallStackLength : 0)
|
||||
|
||||
void _Warning_AlwaysSpewCallStack_Enable( bool bEnable )
|
||||
{
|
||||
s_bCallStacksWithAllWarnings = bEnable;
|
||||
}
|
||||
|
||||
void _Warning_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
||||
{
|
||||
s_iWarningMaxCallStackLength = iMaxCallStackLength;
|
||||
}
|
||||
|
||||
static bool s_bCallStacksWithAllErrors = false; //if true, attach a call stack to every SPEW_ERROR message. Mostly just Error()
|
||||
static int s_iErrorMaxCallStackLength = 20; //default to higher output with an error since we're quitting anyways
|
||||
#define AutomaticErrorCallStackLength() (s_bCallStacksWithAllErrors ? s_iErrorMaxCallStackLength : 0)
|
||||
|
||||
void _Error_AlwaysSpewCallStack_Enable( bool bEnable )
|
||||
{
|
||||
s_bCallStacksWithAllErrors = bEnable;
|
||||
}
|
||||
|
||||
void _Error_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
||||
{
|
||||
s_iErrorMaxCallStackLength = iMaxCallStackLength;
|
||||
}
|
||||
|
||||
#else //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
|
||||
#define AutomaticWarningCallStackLength() 0
|
||||
#define AutomaticErrorCallStackLength() 0
|
||||
|
||||
void _Warning_AlwaysSpewCallStack_Enable( bool bEnable )
|
||||
{
|
||||
}
|
||||
|
||||
void _Warning_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
||||
{
|
||||
}
|
||||
|
||||
void _Error_AlwaysSpewCallStack_Enable( bool bEnable )
|
||||
{
|
||||
}
|
||||
|
||||
void _Error_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
||||
{
|
||||
}
|
||||
|
||||
#endif //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
|
||||
void _ExitOnFatalAssert( const tchar* pFile, int line )
|
||||
{
|
||||
Log_Msg( LOG_ASSERT, _T("Fatal assert failed: %s, line %d. Application exiting.\n"), pFile, line );
|
||||
|
||||
// only write out minidumps if we're not in the debugger
|
||||
if ( !Plat_IsInDebugSession() )
|
||||
{
|
||||
WriteMiniDump();
|
||||
}
|
||||
|
||||
Log_Msg( LOG_DEVELOPER, _T("_ExitOnFatalAssert\n") );
|
||||
Plat_ExitProcess( EXIT_FAILURE );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Templates to assist in validating pointers:
|
||||
//-----------------------------------------------------------------------------
|
||||
PLATFORM_INTERFACE void _AssertValidReadPtr( void* ptr, int count/* = 1*/ )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
Assert( !IsBadReadPtr( ptr, count ) );
|
||||
#else
|
||||
Assert( !count || ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void _AssertValidWritePtr( void* ptr, int count/* = 1*/ )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
Assert( !IsBadWritePtr( ptr, count ) );
|
||||
#else
|
||||
Assert( !count || ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void _AssertValidReadWritePtr( void* ptr, int count/* = 1*/ )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
Assert(!( IsBadWritePtr(ptr, count) || IsBadReadPtr(ptr,count)));
|
||||
#else
|
||||
Assert( !count || ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void _AssertValidStringPtr( const tchar* ptr, int maxchar/* = 0xFFFFFF */ )
|
||||
{
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
#ifdef TCHAR_IS_CHAR
|
||||
Assert( !IsBadStringPtr( ptr, maxchar ) );
|
||||
#else
|
||||
Assert( !IsBadStringPtrW( ptr, maxchar ) );
|
||||
#endif
|
||||
#else
|
||||
Assert( ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
void AppendCallStackToLogMessage( tchar *formattedMessage, int iMessageLength, int iAppendCallStackLength )
|
||||
{
|
||||
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
||||
# if defined( TCHAR_IS_CHAR ) //I'm horrible with unicode and I don't plan on testing this with wide characters just yet
|
||||
if( iAppendCallStackLength > 0 )
|
||||
{
|
||||
int iExistingMessageLength = (int)strlen( formattedMessage ); //no V_strlen in tier 0, plus we're only compiling this for windows and 360. Seems safe
|
||||
formattedMessage += iExistingMessageLength;
|
||||
iMessageLength -= iExistingMessageLength;
|
||||
|
||||
if( iMessageLength <= 32 )
|
||||
return; //no room for anything useful
|
||||
|
||||
//append directly to the spew message
|
||||
if( (iExistingMessageLength > 0) && (formattedMessage[-1] == '\n') )
|
||||
{
|
||||
--formattedMessage;
|
||||
++iMessageLength;
|
||||
}
|
||||
|
||||
//append preface
|
||||
int iAppendedLength = _snprintf( formattedMessage, iMessageLength, _T("\nCall Stack:\n\t") );
|
||||
|
||||
void **CallStackBuffer = (void **)stackalloc( iAppendCallStackLength * sizeof( void * ) );
|
||||
int iCount = GetCallStack( CallStackBuffer, iAppendCallStackLength, 2 );
|
||||
if( TranslateStackInfo( CallStackBuffer, iCount, formattedMessage + iAppendedLength, iMessageLength - iAppendedLength, _T("\n\t") ) == 0 )
|
||||
{
|
||||
//failure
|
||||
formattedMessage[0] = '\0'; //this is pointing at where we wrote "\nCall Stack:\n\t"
|
||||
}
|
||||
else
|
||||
{
|
||||
iAppendedLength += (int)strlen( formattedMessage + iAppendedLength ); //no V_strlen in tier 0, plus we're only compiling this for windows and 360. Seems safe
|
||||
|
||||
if( iAppendedLength < iMessageLength )
|
||||
{
|
||||
formattedMessage[iAppendedLength] = '\n'; //Add another newline.
|
||||
++iAppendedLength;
|
||||
|
||||
formattedMessage[iAppendedLength] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
# else
|
||||
AssertMsg( false, "Fixme" );
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Forward declare for internal use only.
|
||||
CLoggingSystem *GetGlobalLoggingSystem();
|
||||
|
||||
#define Log_LegacyHelperColor_Stack( Channel, Severity, Color, MessageFormat, AppendCallStackLength ) \
|
||||
do \
|
||||
{ \
|
||||
CLoggingSystem *pLoggingSystem = GetGlobalLoggingSystem(); \
|
||||
if ( pLoggingSystem->IsChannelEnabled( Channel, Severity ) ) \
|
||||
{ \
|
||||
tchar formattedMessage[MAX_LOGGING_MESSAGE_LENGTH]; \
|
||||
va_list args; \
|
||||
va_start( args, MessageFormat ); \
|
||||
Tier0Internal_vsntprintf( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, MessageFormat, args ); \
|
||||
va_end( args ); \
|
||||
AppendCallStackToLogMessage( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, AppendCallStackLength ); \
|
||||
pLoggingSystem->LogDirect( Channel, Severity, Color, formattedMessage ); \
|
||||
} \
|
||||
} while( 0 )
|
||||
|
||||
#define Log_LegacyHelperColor( Channel, Severity, Color, MessageFormat ) Log_LegacyHelperColor_Stack( Channel, Severity, Color, MessageFormat, 0 )
|
||||
|
||||
#define Log_LegacyHelper_Stack( Channel, Severity, MessageFormat, AppendCallStackLength ) Log_LegacyHelperColor_Stack( Channel, Severity, pLoggingSystem->GetChannelColor( Channel ), MessageFormat, AppendCallStackLength )
|
||||
#define Log_LegacyHelper( Channel, Severity, MessageFormat ) Log_LegacyHelperColor( Channel, Severity, pLoggingSystem->GetChannelColor( Channel ), MessageFormat )
|
||||
|
||||
|
||||
void Msg( const tchar* pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_GENERAL, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
void Warning( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper_Stack( LOG_GENERAL, LS_WARNING, pMsgFormat, AutomaticWarningCallStackLength() );
|
||||
}
|
||||
|
||||
void Warning_SpewCallStack( int iMaxCallStackLength, const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper_Stack( LOG_GENERAL, LS_WARNING, pMsgFormat, iMaxCallStackLength );
|
||||
}
|
||||
|
||||
|
||||
void Error( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper_Stack( LOG_GENERAL, LS_ERROR, pMsgFormat, AutomaticErrorCallStackLength() );
|
||||
}
|
||||
|
||||
void Error_SpewCallStack( int iMaxCallStackLength, const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper_Stack( LOG_GENERAL, LS_ERROR, pMsgFormat, iMaxCallStackLength );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A couple of super-common dynamic spew messages, here for convenience
|
||||
// These looked at the "developer" group, print if it's level 1 or higher
|
||||
//-----------------------------------------------------------------------------
|
||||
void DevMsg( int level, const tchar* pMsgFormat, ... )
|
||||
{
|
||||
LoggingChannelID_t channel = level >= 2 ? LOG_DEVELOPER_VERBOSE : LOG_DEVELOPER;
|
||||
Log_LegacyHelper( channel, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
|
||||
void DevWarning( int level, const tchar *pMsgFormat, ... )
|
||||
{
|
||||
LoggingChannelID_t channel = level >= 2 ? LOG_DEVELOPER_VERBOSE : LOG_DEVELOPER;
|
||||
Log_LegacyHelper( channel, LS_WARNING, pMsgFormat );
|
||||
}
|
||||
|
||||
void DevMsg( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_DEVELOPER, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
void DevWarning( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_DEVELOPER, LS_WARNING, pMsgFormat );
|
||||
}
|
||||
|
||||
void ConColorMsg( const Color& clr, const tchar* pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelperColor( LOG_CONSOLE, LS_MESSAGE, clr, pMsgFormat );
|
||||
}
|
||||
|
||||
void ConMsg( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_CONSOLE, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
void ConDMsg( const tchar *pMsgFormat, ... )
|
||||
{
|
||||
Log_LegacyHelper( LOG_DEVELOPER_CONSOLE, LS_MESSAGE, pMsgFormat );
|
||||
}
|
||||
|
||||
// If we don't have a function from math.h, then it doesn't link certain floating-point
|
||||
// functions in and printfs with %f cause runtime errors in the C libraries.
|
||||
PLATFORM_INTERFACE float CrackSmokingCompiler( float a )
|
||||
{
|
||||
return (float)fabs( a );
|
||||
}
|
||||
|
||||
void* Plat_SimpleLog( const tchar* file, int line )
|
||||
{
|
||||
FILE* f = _tfopen( _T("simple.log"), _T("at+") );
|
||||
_ftprintf( f, _T("%s:%i\n"), file, line );
|
||||
fclose( f );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: For debugging startup times, etc.
|
||||
// Input : *fmt -
|
||||
// ... -
|
||||
//-----------------------------------------------------------------------------
|
||||
void COM_TimestampedLog( char const *fmt, ... )
|
||||
{
|
||||
static float s_LastStamp = 0.0;
|
||||
static bool s_bShouldLog = false;
|
||||
static bool s_bShouldLogToConsole = false;
|
||||
static bool s_bShouldLogToETW = false;
|
||||
static bool s_bChecked = false;
|
||||
static bool s_bFirstWrite = false;
|
||||
|
||||
if ( !s_bChecked )
|
||||
{
|
||||
s_bShouldLog = ( CommandLine()->CheckParm( "-profile" ) ) ? true : false;
|
||||
s_bShouldLogToConsole = ( CommandLine()->ParmValue( "-profile", 0.0f ) != 0.0f ) ? true : false;
|
||||
|
||||
s_bShouldLogToETW = ( CommandLine()->CheckParm( "-etwprofile" ) ) ? true : false;
|
||||
if ( s_bShouldLogToETW )
|
||||
{
|
||||
s_bShouldLog = true;
|
||||
}
|
||||
|
||||
s_bChecked = true;
|
||||
}
|
||||
if ( !s_bShouldLog )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char string[1024];
|
||||
va_list argptr;
|
||||
va_start( argptr, fmt );
|
||||
Tier0Internal_vsnprintf( string, sizeof( string ), fmt, argptr );
|
||||
va_end( argptr );
|
||||
|
||||
float curStamp = Plat_FloatTime();
|
||||
|
||||
#if defined( _X360 )
|
||||
XBX_rTimeStampLog( curStamp, string );
|
||||
#elif defined( _PS3 )
|
||||
Log_Warning( LOG_LOADING, "%8.4f / %8.4f: %s\n", curStamp, curStamp - s_LastStamp, string );
|
||||
#endif
|
||||
|
||||
if ( IsPC() )
|
||||
{
|
||||
// If ETW profiling is enabled then do it only.
|
||||
if ( s_bShouldLogToETW )
|
||||
{
|
||||
ETWMark( string );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !s_bFirstWrite )
|
||||
{
|
||||
unlink( "timestamped.log" );
|
||||
s_bFirstWrite = true;
|
||||
}
|
||||
|
||||
FILE* fp = fopen( "timestamped.log", "at+" );
|
||||
fprintf( fp, "%8.4f / %8.4f: %s\n", curStamp, curStamp - s_LastStamp, string );
|
||||
fclose( fp );
|
||||
}
|
||||
|
||||
if ( s_bShouldLogToConsole )
|
||||
{
|
||||
Msg( "%8.4f / %8.4f: %s\n", curStamp, curStamp - s_LastStamp, string );
|
||||
}
|
||||
}
|
||||
|
||||
s_LastStamp = curStamp;
|
||||
}
|
||||
|
||||
#ifdef IS_WINDOWS_PC
|
||||
|
||||
class CHardwareBreakPoint
|
||||
{
|
||||
public:
|
||||
|
||||
enum EOpCode
|
||||
{
|
||||
BRK_SET = 0,
|
||||
BRK_UNSET,
|
||||
};
|
||||
|
||||
CHardwareBreakPoint()
|
||||
{
|
||||
m_eOperation = BRK_SET;
|
||||
m_pvAddress = 0;
|
||||
m_hThread = 0;
|
||||
m_hThreadEvent = 0;
|
||||
m_nRegister = 0;
|
||||
m_bSuccess = false;
|
||||
}
|
||||
|
||||
const void *m_pvAddress;
|
||||
HANDLE m_hThread;
|
||||
EHardwareBreakpointType m_eType;
|
||||
EHardwareBreakpointSize m_eSize;
|
||||
HANDLE m_hThreadEvent;
|
||||
int m_nRegister;
|
||||
EOpCode m_eOperation;
|
||||
bool m_bSuccess;
|
||||
|
||||
static void SetBits( DWORD_PTR& dw, int lowBit, int bits, int newValue );
|
||||
static DWORD WINAPI ThreadProc( LPVOID lpParameter );
|
||||
};
|
||||
|
||||
void CHardwareBreakPoint::SetBits( DWORD_PTR& dw, int lowBit, int bits, int newValue )
|
||||
{
|
||||
DWORD_PTR mask = (1 << bits) - 1;
|
||||
dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
|
||||
}
|
||||
|
||||
DWORD WINAPI CHardwareBreakPoint::ThreadProc( LPVOID lpParameter )
|
||||
{
|
||||
CHardwareBreakPoint *h = reinterpret_cast< CHardwareBreakPoint * >( lpParameter );
|
||||
SuspendThread( h->m_hThread );
|
||||
|
||||
// Get current context
|
||||
CONTEXT ct = {0};
|
||||
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
GetThreadContext(h->m_hThread,&ct);
|
||||
|
||||
int FlagBit = 0;
|
||||
|
||||
bool Dr0Busy = false;
|
||||
bool Dr1Busy = false;
|
||||
bool Dr2Busy = false;
|
||||
bool Dr3Busy = false;
|
||||
if (ct.Dr7 & 1)
|
||||
Dr0Busy = true;
|
||||
if (ct.Dr7 & 4)
|
||||
Dr1Busy = true;
|
||||
if (ct.Dr7 & 16)
|
||||
Dr2Busy = true;
|
||||
if (ct.Dr7 & 64)
|
||||
Dr3Busy = true;
|
||||
|
||||
if ( h->m_eOperation == CHardwareBreakPoint::BRK_UNSET )
|
||||
{
|
||||
// Remove
|
||||
if (h->m_nRegister == 0)
|
||||
{
|
||||
FlagBit = 0;
|
||||
ct.Dr0 = 0;
|
||||
Dr0Busy = false;
|
||||
}
|
||||
if (h->m_nRegister == 1)
|
||||
{
|
||||
FlagBit = 2;
|
||||
ct.Dr1 = 0;
|
||||
Dr1Busy = false;
|
||||
}
|
||||
if (h->m_nRegister == 2)
|
||||
{
|
||||
FlagBit = 4;
|
||||
ct.Dr2 = 0;
|
||||
Dr2Busy = false;
|
||||
}
|
||||
if (h->m_nRegister == 3)
|
||||
{
|
||||
FlagBit = 6;
|
||||
ct.Dr3 = 0;
|
||||
Dr3Busy = false;
|
||||
}
|
||||
ct.Dr7 &= ~(1 << FlagBit);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Dr0Busy)
|
||||
{
|
||||
h->m_nRegister = 0;
|
||||
ct.Dr0 = (DWORD_PTR)h->m_pvAddress;
|
||||
Dr0Busy = true;
|
||||
}
|
||||
else if (!Dr1Busy)
|
||||
{
|
||||
h->m_nRegister = 1;
|
||||
ct.Dr1 = (DWORD_PTR)h->m_pvAddress;
|
||||
Dr1Busy = true;
|
||||
}
|
||||
else if (!Dr2Busy)
|
||||
{
|
||||
h->m_nRegister = 2;
|
||||
ct.Dr2 = (DWORD_PTR)h->m_pvAddress;
|
||||
Dr2Busy = true;
|
||||
}
|
||||
else if (!Dr3Busy)
|
||||
{
|
||||
h->m_nRegister = 3;
|
||||
ct.Dr3 = (DWORD_PTR)h->m_pvAddress;
|
||||
Dr3Busy = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
h->m_bSuccess = false;
|
||||
ResumeThread(h->m_hThread);
|
||||
SetEvent(h->m_hThreadEvent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ct.Dr6 = 0;
|
||||
int st = 0;
|
||||
if (h->m_eType == BREAKPOINT_EXECUTE)
|
||||
st = 0;
|
||||
if (h->m_eType == BREAKPOINT_READWRITE)
|
||||
st = 3;
|
||||
if (h->m_eType == BREAKPOINT_WRITE)
|
||||
st = 1;
|
||||
|
||||
int le = 0;
|
||||
if (h->m_eSize == BREAKPOINT_SIZE_1)
|
||||
le = 0;
|
||||
if (h->m_eSize == BREAKPOINT_SIZE_2)
|
||||
le = 1;
|
||||
if (h->m_eSize == BREAKPOINT_SIZE_4)
|
||||
le = 3;
|
||||
if (h->m_eSize == BREAKPOINT_SIZE_8)
|
||||
le = 2;
|
||||
|
||||
SetBits( ct.Dr7, 16 + h->m_nRegister*4, 2, st );
|
||||
SetBits( ct.Dr7, 18 + h->m_nRegister*4, 2, le );
|
||||
SetBits( ct.Dr7, h->m_nRegister*2,1,1);
|
||||
}
|
||||
|
||||
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
SetThreadContext(h->m_hThread,&ct);
|
||||
|
||||
ResumeThread( h->m_hThread );
|
||||
h->m_bSuccess = true;
|
||||
SetEvent( h->m_hThreadEvent );
|
||||
return 0;
|
||||
}
|
||||
|
||||
HardwareBreakpointHandle_t SetHardwareBreakpoint( EHardwareBreakpointType eType, EHardwareBreakpointSize eSize, const void *pvLocation )
|
||||
{
|
||||
CHardwareBreakPoint *h = new CHardwareBreakPoint();
|
||||
h->m_pvAddress = pvLocation;
|
||||
h->m_eSize = eSize;
|
||||
h->m_eType = eType;
|
||||
HANDLE hThread = GetCurrentThread();
|
||||
h->m_hThread = hThread;
|
||||
|
||||
if ( hThread == GetCurrentThread() )
|
||||
{
|
||||
DWORD nThreadId = GetCurrentThreadId();
|
||||
h->m_hThread = OpenThread( THREAD_ALL_ACCESS, 0, nThreadId );
|
||||
}
|
||||
|
||||
h->m_hThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
h->m_eOperation = CHardwareBreakPoint::BRK_SET; // Set Break
|
||||
CreateThread( 0, 0, CHardwareBreakPoint::ThreadProc, (LPVOID)h, 0, 0 );
|
||||
WaitForSingleObject( h->m_hThreadEvent,INFINITE );
|
||||
CloseHandle( h->m_hThreadEvent );
|
||||
h->m_hThreadEvent = 0;
|
||||
if ( hThread == GetCurrentThread() )
|
||||
{
|
||||
CloseHandle( h->m_hThread );
|
||||
}
|
||||
h->m_hThread = hThread;
|
||||
if ( !h->m_bSuccess )
|
||||
{
|
||||
delete h;
|
||||
return (HardwareBreakpointHandle_t)0;
|
||||
}
|
||||
return (HardwareBreakpointHandle_t)h;
|
||||
}
|
||||
|
||||
bool ClearHardwareBreakpoint( HardwareBreakpointHandle_t handle )
|
||||
{
|
||||
CHardwareBreakPoint *h = reinterpret_cast< CHardwareBreakPoint* >( handle );
|
||||
if ( !h )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bOpened = false;
|
||||
if ( h->m_hThread == GetCurrentThread() )
|
||||
{
|
||||
DWORD nThreadId = GetCurrentThreadId();
|
||||
h->m_hThread = OpenThread( THREAD_ALL_ACCESS, 0, nThreadId );
|
||||
bOpened = true;
|
||||
}
|
||||
|
||||
h->m_hThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
||||
h->m_eOperation = CHardwareBreakPoint::BRK_UNSET; // Remove Break
|
||||
CreateThread( 0,0,CHardwareBreakPoint::ThreadProc, (LPVOID)h, 0,0 );
|
||||
WaitForSingleObject( h->m_hThreadEvent, INFINITE );
|
||||
CloseHandle( h->m_hThreadEvent );
|
||||
h->m_hThreadEvent = 0;
|
||||
if ( bOpened )
|
||||
{
|
||||
CloseHandle( h->m_hThread );
|
||||
}
|
||||
delete h;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // IS_WINDOWS_PC
|
||||
|
||||
Vendored
+377
@@ -0,0 +1,377 @@
|
||||
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
||||
//
|
||||
// ETW (Event Tracing for Windows) profiling helpers.
|
||||
// This allows easy insertion of Generic Event markers into ETW/xperf tracing
|
||||
// which then aids in analyzing the traces and finding performance problems.
|
||||
//
|
||||
//===============================================================================
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/etwprof.h"
|
||||
#include <memory>
|
||||
|
||||
#ifdef ETW_MARKS_ENABLED
|
||||
|
||||
// After building the DLL if it has never been registered on this machine or
|
||||
// if the providers have changed you need to go:
|
||||
// xcopy /y %vgame%\bin\tier0.dll %temp%
|
||||
// wevtutil um %vgame%\..\src\tier0\ValveETWProvider.man
|
||||
// wevtutil im %vgame%\..\src\tier0\ValveETWProvider.man
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
// These are defined in evntrace.h but you need a Vista+ Windows
|
||||
// SDK to have them available, so I define them here.
|
||||
#define EVENT_CONTROL_CODE_DISABLE_PROVIDER 0
|
||||
#define EVENT_CONTROL_CODE_ENABLE_PROVIDER 1
|
||||
#define EVENT_CONTROL_CODE_CAPTURE_STATE 2
|
||||
|
||||
// EVNTAPI is used in evntprov.h which is included by ValveETWProviderEvents.h
|
||||
// We define EVNTAPI without the DECLSPEC_IMPORT specifier so that
|
||||
// we can implement these functions locally instead of using the import library,
|
||||
// and can therefore still run on Windows XP.
|
||||
#define EVNTAPI __stdcall
|
||||
// Include the event register/write/unregister macros compiled from the manifest file.
|
||||
// Note that this includes evntprov.h which requires a Vista+ Windows SDK
|
||||
// which we don't currently have, so evntprov.h is checked in.
|
||||
#include "ValveETWProviderEvents.h"
|
||||
|
||||
// Typedefs for use with GetProcAddress
|
||||
typedef ULONG (__stdcall *tEventRegister)( LPCGUID ProviderId, PENABLECALLBACK EnableCallback, PVOID CallbackContext, PREGHANDLE RegHandle);
|
||||
typedef ULONG (__stdcall *tEventWrite)( REGHANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData);
|
||||
typedef ULONG (__stdcall *tEventUnregister)( REGHANDLE RegHandle );
|
||||
|
||||
// Helper class to dynamically load Advapi32.dll, find the ETW functions,
|
||||
// register the providers if possible, and get the performance counter frequency.
|
||||
class CETWRegister
|
||||
{
|
||||
public:
|
||||
CETWRegister()
|
||||
{
|
||||
QueryPerformanceFrequency( &m_frequency );
|
||||
|
||||
// Find Advapi32.dll. This should always succeed.
|
||||
HMODULE pAdvapiDLL = LoadLibraryW( L"Advapi32.dll" );
|
||||
if ( pAdvapiDLL )
|
||||
{
|
||||
// Try to find the ETW functions. This will fail on XP.
|
||||
m_pEventRegister = ( tEventRegister )GetProcAddress( pAdvapiDLL, "EventRegister" );
|
||||
m_pEventWrite = ( tEventWrite )GetProcAddress( pAdvapiDLL, "EventWrite" );
|
||||
m_pEventUnregister = ( tEventUnregister )GetProcAddress( pAdvapiDLL, "EventUnregister" );
|
||||
|
||||
// Register two ETW providers. If registration fails then the event logging calls will fail.
|
||||
// On XP these calls will do nothing.
|
||||
// On Vista and above, if these providers have been enabled by xperf or logman then
|
||||
// the VALVE_FRAMERATE_Context and VALVE_MAIN_Context globals will be modified
|
||||
// like this:
|
||||
// MatchAnyKeyword: 0xffffffffffffffff
|
||||
// IsEnabled: 1
|
||||
// Level: 255
|
||||
// In other words, fully enabled.
|
||||
|
||||
EventRegisterValve_FrameRate();
|
||||
EventRegisterValve_ServerFrameRate();
|
||||
EventRegisterValve_Main();
|
||||
EventRegisterValve_Input();
|
||||
EventRegisterValve_Network();
|
||||
|
||||
// Emit the thread ID for the main thread. This also indicates that
|
||||
// the main provider is initialized.
|
||||
EventWriteThread_ID( GetCurrentThreadId(), "Main thread" );
|
||||
// Emit an input system event so we know that it is active.
|
||||
EventWriteKey_down( "Valve input provider initialized.", 0, 0 );
|
||||
}
|
||||
}
|
||||
~CETWRegister()
|
||||
{
|
||||
// Unregister our providers.
|
||||
EventUnregisterValve_Network();
|
||||
EventUnregisterValve_Input();
|
||||
EventUnregisterValve_Main();
|
||||
EventUnregisterValve_ServerFrameRate();
|
||||
EventUnregisterValve_FrameRate();
|
||||
}
|
||||
|
||||
tEventRegister m_pEventRegister;
|
||||
tEventWrite m_pEventWrite;
|
||||
tEventUnregister m_pEventUnregister;
|
||||
|
||||
// QPC frequency
|
||||
LARGE_INTEGER m_frequency;
|
||||
|
||||
} g_ETWRegister;
|
||||
|
||||
// Redirector function for EventRegister. Called by macros in ValveETWProviderEvents.h
|
||||
ULONG EVNTAPI EventRegister( LPCGUID ProviderId, PENABLECALLBACK EnableCallback, PVOID CallbackContext, PREGHANDLE RegHandle )
|
||||
{
|
||||
if ( g_ETWRegister.m_pEventRegister )
|
||||
return g_ETWRegister.m_pEventRegister( ProviderId, EnableCallback, CallbackContext, RegHandle );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int64 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 0;
|
||||
}
|
||||
|
||||
char buffer[1000];
|
||||
va_list args;
|
||||
va_start( args, pMessage );
|
||||
vsprintf_s( buffer, pMessage, args );
|
||||
va_end( args );
|
||||
|
||||
int64 nTime = GetQPCTime();
|
||||
EventWriteMark( buffer );
|
||||
return nTime;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int s_nRenderFrameCount;
|
||||
|
||||
int ETWGetRenderFrameNumber()
|
||||
{
|
||||
return s_nRenderFrameCount;
|
||||
}
|
||||
|
||||
// Insert a render frame marker using the Valve-FrameRate provider. Automatically
|
||||
// count the frame number and frame time. Since the frame count and elapsed time
|
||||
// are tracked without paying attention to the bIsServerProcess flag the results
|
||||
// will be 'unexpected' if bIsServerProcess changes value within a process.
|
||||
void ETWRenderFrameMark( bool bIsServerProcess )
|
||||
{
|
||||
static int64 s_lastFrameTime;
|
||||
|
||||
int64 nCurrentFrameTime = GetQPCTime();
|
||||
float flElapsedFrameTime = 0.0f;
|
||||
if ( s_nRenderFrameCount )
|
||||
{
|
||||
flElapsedFrameTime = QPCToMS( nCurrentFrameTime - s_lastFrameTime );
|
||||
}
|
||||
|
||||
if ( bIsServerProcess )
|
||||
{
|
||||
EventWriteServerRenderFrameMark( s_nRenderFrameCount, flElapsedFrameTime );
|
||||
}
|
||||
else
|
||||
{
|
||||
EventWriteRenderFrameMark( s_nRenderFrameCount, flElapsedFrameTime );
|
||||
}
|
||||
|
||||
++s_nRenderFrameCount;
|
||||
s_lastFrameTime = nCurrentFrameTime;
|
||||
}
|
||||
|
||||
// Insert a simulation frame marker using the Valve-FrameRate provider. Automatically
|
||||
// count the frame number and frame time. Since the frame count and elapsed time
|
||||
// are tracked without paying attention to the bIsServerProcess flag the results
|
||||
// will be 'unexpected' if bIsServerProcess changes value within a process.
|
||||
void ETWSimFrameMark( bool bIsServerProcess )
|
||||
{
|
||||
static int s_nFrameCount;
|
||||
static int64 s_lastFrameTime;
|
||||
|
||||
int64 nCurrentFrameTime = GetQPCTime();
|
||||
float flElapsedFrameTime = 0.0f;
|
||||
if ( s_nFrameCount )
|
||||
{
|
||||
flElapsedFrameTime = QPCToMS( nCurrentFrameTime - s_lastFrameTime );
|
||||
}
|
||||
|
||||
if ( bIsServerProcess )
|
||||
{
|
||||
EventWriteServerSimFrameMark( s_nFrameCount, flElapsedFrameTime );
|
||||
}
|
||||
else
|
||||
{
|
||||
EventWriteSimFrameMark( s_nFrameCount, flElapsedFrameTime );
|
||||
}
|
||||
|
||||
++s_nFrameCount;
|
||||
s_lastFrameTime = nCurrentFrameTime;
|
||||
}
|
||||
|
||||
void ETWMouseDown( int whichButton, int x, int y )
|
||||
{
|
||||
EventWriteMouse_down( whichButton, x, y );
|
||||
}
|
||||
|
||||
void ETWMouseUp( int whichButton, int x, int y )
|
||||
{
|
||||
EventWriteMouse_up( whichButton, x, y );
|
||||
}
|
||||
|
||||
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
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tier0/fasttimer.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
//#include "tier0/memdbgon.h"
|
||||
|
||||
uint64 g_ClockSpeed; // Clocks/sec
|
||||
unsigned long g_dwClockSpeed;
|
||||
double g_ClockSpeedMicrosecondsMultiplier;
|
||||
double g_ClockSpeedMillisecondsMultiplier;
|
||||
double g_ClockSpeedSecondsMultiplier;
|
||||
|
||||
// Constructor init the clock speed.
|
||||
CClockSpeedInit g_ClockSpeedInit;
|
||||
Vendored
+698
@@ -0,0 +1,698 @@
|
||||
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
||||
//
|
||||
// Logging system definitions.
|
||||
//
|
||||
//===============================================================================
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "logging.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "dbg.h"
|
||||
#include "threadtools.h"
|
||||
#include "tier0_strtools.h" // this is from tier1, but only included for inline definition of V_isspace
|
||||
|
||||
#ifdef _PS3
|
||||
#include <sys/tty.h>
|
||||
#endif
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Define commonly used channels here
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_GENERAL, "General" );
|
||||
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_ASSERT, "Assert" );
|
||||
|
||||
// Corresponds to ConMsg/ConWarning/etc. with a level <= 1.
|
||||
// Only errors are spewed by default.
|
||||
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_CONSOLE, "Console", LCF_CONSOLE_ONLY, LS_ERROR );
|
||||
ADD_LOGGING_CHANNEL_TAG( "Console" );
|
||||
END_DEFINE_LOGGING_CHANNEL();
|
||||
|
||||
// Corresponds to DevMsg/DevWarning/etc. with a level <= 1.
|
||||
// Only errors are spewed by default.
|
||||
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_DEVELOPER, "Developer", LCF_CONSOLE_ONLY, LS_ERROR );
|
||||
ADD_LOGGING_CHANNEL_TAG( "Developer" );
|
||||
END_DEFINE_LOGGING_CHANNEL();
|
||||
|
||||
// Corresponds to ConMsg/ConWarning/etc. with a level >= 2.
|
||||
// Only errors are spewed by default.
|
||||
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_DEVELOPER_CONSOLE, "DeveloperConsole", LCF_CONSOLE_ONLY, LS_ERROR );
|
||||
ADD_LOGGING_CHANNEL_TAG( "DeveloperVerbose" );
|
||||
ADD_LOGGING_CHANNEL_TAG( "Console" );
|
||||
END_DEFINE_LOGGING_CHANNEL();
|
||||
|
||||
// Corresponds to DevMsg/DevWarning/etc, with a level >= 2.
|
||||
// Only errors are spewed by default.
|
||||
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_DEVELOPER_VERBOSE, "DeveloperVerbose", LCF_CONSOLE_ONLY, LS_ERROR, Color( 192, 128, 192, 255 ) );
|
||||
ADD_LOGGING_CHANNEL_TAG( "DeveloperVerbose" );
|
||||
END_DEFINE_LOGGING_CHANNEL();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Globals
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The index of the logging state used by the current thread. This defaults to 0 across all threads,
|
||||
// which indicates that the global listener set should be used (CLoggingSystem::m_nGlobalStateIndex).
|
||||
//
|
||||
// NOTE:
|
||||
// Because our linux TLS implementation does not support embedding a thread local
|
||||
// integer in a class, the logging system must use a global thread-local integer.
|
||||
// This means that we can only have one instance of CLoggingSystem, although
|
||||
// we could support additional instances if we are willing to lose support for
|
||||
// thread-local spew handling.
|
||||
// There is no other reason why this class must be a singleton, except
|
||||
// for the fact that there's no reason to have more than one in existence.
|
||||
bool g_bEnforceLoggingSystemSingleton = false;
|
||||
|
||||
#ifdef _PS3
|
||||
#include "tls_ps3.h"
|
||||
#else // _PS3
|
||||
CTHREADLOCALINT g_nThreadLocalStateIndex;
|
||||
#endif // _PS3
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Implementation
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CLoggingSystem *g_pGlobalLoggingSystem = NULL;
|
||||
|
||||
// This function does not get inlined due to the static variable :(
|
||||
CLoggingSystem *GetGlobalLoggingSystem_Internal()
|
||||
{
|
||||
static CLoggingSystem globalLoggingSystem;
|
||||
g_pGlobalLoggingSystem = &globalLoggingSystem;
|
||||
return &globalLoggingSystem;
|
||||
}
|
||||
|
||||
// This function can get inlined
|
||||
CLoggingSystem *GetGlobalLoggingSystem()
|
||||
{
|
||||
return ( g_pGlobalLoggingSystem == NULL ) ? GetGlobalLoggingSystem_Internal() : g_pGlobalLoggingSystem;
|
||||
}
|
||||
|
||||
CLoggingSystem::CLoggingSystem() :
|
||||
m_nChannelCount( 0 ),
|
||||
m_nChannelTagCount( 0 ),
|
||||
m_nTagNamePoolIndex( 0 ),
|
||||
m_nGlobalStateIndex( 0 )
|
||||
{
|
||||
Assert( !g_bEnforceLoggingSystemSingleton );
|
||||
g_bEnforceLoggingSystemSingleton = true;
|
||||
#if !defined( _PS3 ) && !defined(POSIX) && !defined(PLATFORM_WINDOWS)
|
||||
// Due to uncertain constructor ordering (g_nThreadLocalStateIndex
|
||||
// may not be constructed yet so TLS index may not be available yet)
|
||||
// we cannot initialize the state index here without risking
|
||||
// AppVerifier errors and undefined behavior. Luckily TlsAlloc values
|
||||
// are guaranteed to be zero-initialized so we don't need to zero-init,
|
||||
// this, and in fact we can't for all threads.
|
||||
// TLS on PS3 is zero-initialized in global ELF section
|
||||
// TLS is also not accessible at this point before PRX entry point runs
|
||||
g_nThreadLocalStateIndex = 0;
|
||||
#endif
|
||||
|
||||
m_LoggingStates[0].m_nPreviousStackEntry = -1;
|
||||
|
||||
m_LoggingStates[0].m_nListenerCount = 1;
|
||||
m_LoggingStates[0].m_RegisteredListeners[0] = &m_DefaultLoggingListener;
|
||||
m_LoggingStates[0].m_pLoggingResponse = &m_DefaultLoggingResponse;
|
||||
|
||||
// Mark all other logging state blocks as unused.
|
||||
for ( int i = 1; i < MAX_LOGGING_STATE_COUNT; ++ i )
|
||||
{
|
||||
m_LoggingStates[i].m_nListenerCount = -1;
|
||||
}
|
||||
|
||||
m_pStateMutex = NULL;
|
||||
}
|
||||
|
||||
CLoggingSystem::~CLoggingSystem()
|
||||
{
|
||||
g_bEnforceLoggingSystemSingleton = false;
|
||||
delete m_pStateMutex;
|
||||
}
|
||||
|
||||
LoggingChannelID_t CLoggingSystem::RegisterLoggingChannel( const char *pChannelName, RegisterTagsFunc registerTagsFunc, int flags, LoggingSeverity_t severity, Color spewColor )
|
||||
{
|
||||
if ( m_nChannelCount >= MAX_LOGGING_CHANNEL_COUNT )
|
||||
{
|
||||
// Out of logging channels... catastrophic fail!
|
||||
Log_Error( LOG_GENERAL, "Out of logging channels.\n" );
|
||||
Assert( 0 );
|
||||
return INVALID_LOGGING_CHANNEL_ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Channels can be multiply defined, in which case return the ID of the existing channel.
|
||||
for ( int i = 0; i < m_nChannelCount; ++ i )
|
||||
{
|
||||
if ( V_tier0_stricmp( m_RegisteredChannels[i].m_Name, pChannelName ) == 0 )
|
||||
{
|
||||
// OK to call the tag registration callback; duplicates will be culled away.
|
||||
// This allows multiple people to register a logging channel, and the union of all tags will be registered.
|
||||
if ( registerTagsFunc != NULL )
|
||||
{
|
||||
registerTagsFunc();
|
||||
}
|
||||
|
||||
// If a logging channel is registered multiple times, only one of the registrations should specify flags/severity/color.
|
||||
if ( m_RegisteredChannels[i].m_Flags == 0 && m_RegisteredChannels[i].m_MinimumSeverity == LS_MESSAGE && m_RegisteredChannels[i].m_SpewColor == UNSPECIFIED_LOGGING_COLOR )
|
||||
{
|
||||
m_RegisteredChannels[i].m_Flags = ( LoggingChannelFlags_t )flags;
|
||||
m_RegisteredChannels[i].m_MinimumSeverity = severity;
|
||||
m_RegisteredChannels[i].m_SpewColor = spewColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertMsg( flags == 0 || flags == m_RegisteredChannels[i].m_Flags, "Non-zero or mismatched flags specified in logging channel re-registration!" );
|
||||
AssertMsg( severity == LS_MESSAGE || severity == m_RegisteredChannels[i].m_MinimumSeverity, "Non-default or mismatched severity specified in logging channel re-registration!" );
|
||||
AssertMsg( spewColor == UNSPECIFIED_LOGGING_COLOR || spewColor == m_RegisteredChannels[i].m_SpewColor, "Non-default or mismatched color specified in logging channel re-registration!" );
|
||||
}
|
||||
|
||||
return m_RegisteredChannels[i].m_ID;
|
||||
}
|
||||
}
|
||||
|
||||
m_RegisteredChannels[m_nChannelCount].m_ID = m_nChannelCount;
|
||||
m_RegisteredChannels[m_nChannelCount].m_Flags = ( LoggingChannelFlags_t )flags;
|
||||
m_RegisteredChannels[m_nChannelCount].m_MinimumSeverity = severity;
|
||||
m_RegisteredChannels[m_nChannelCount].m_SpewColor = spewColor;
|
||||
strncpy( m_RegisteredChannels[m_nChannelCount].m_Name, pChannelName, MAX_LOGGING_IDENTIFIER_LENGTH );
|
||||
|
||||
if ( registerTagsFunc != NULL )
|
||||
{
|
||||
registerTagsFunc();
|
||||
}
|
||||
return m_nChannelCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
LoggingChannelID_t CLoggingSystem::FindChannel( const char *pChannelName ) const
|
||||
{
|
||||
for ( int i = 0; i < m_nChannelCount; ++ i )
|
||||
{
|
||||
if ( V_tier0_stricmp( m_RegisteredChannels[i].m_Name, pChannelName ) == 0 )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return INVALID_LOGGING_CHANNEL_ID;
|
||||
}
|
||||
|
||||
void CLoggingSystem::AddTagToCurrentChannel( const char *pTagName )
|
||||
{
|
||||
// Add tags at the head of the tag-list of the most recently added channel.
|
||||
LoggingChannel_t *pChannel = &m_RegisteredChannels[m_nChannelCount];
|
||||
|
||||
// First check for duplicates
|
||||
if ( pChannel->HasTag( pTagName ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LoggingTag_t *pTag = AllocTag( pTagName );
|
||||
|
||||
pTag->m_pNextTag = pChannel->m_pFirstTag;
|
||||
pChannel->m_pFirstTag = pTag;
|
||||
}
|
||||
|
||||
void CLoggingSystem::SetChannelSpewLevel( LoggingChannelID_t channelID, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
GetChannel( channelID )->SetSpewLevel( minimumSeverity );
|
||||
}
|
||||
|
||||
void CLoggingSystem::SetChannelSpewLevelByName( const char *pName, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
for ( int i = 0; i < m_nChannelCount; ++ i )
|
||||
{
|
||||
if ( V_tier0_stricmp( m_RegisteredChannels[i].m_Name, pName ) == 0 )
|
||||
{
|
||||
m_RegisteredChannels[i].SetSpewLevel( minimumSeverity );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLoggingSystem::SetChannelSpewLevelByTag( const char *pTag, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
for ( int i = 0; i < m_nChannelCount; ++ i )
|
||||
{
|
||||
if ( m_RegisteredChannels[i].HasTag( pTag ) )
|
||||
{
|
||||
m_RegisteredChannels[i].SetSpewLevel( minimumSeverity );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLoggingSystem::PushLoggingState( bool bThreadLocal, bool bClearState )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
|
||||
int nNewState = FindUnusedStateIndex();
|
||||
// Ensure we're not out of state blocks.
|
||||
Assert( nNewState != -1 );
|
||||
|
||||
int nCurrentState = bThreadLocal ? (int)g_nThreadLocalStateIndex : m_nGlobalStateIndex;
|
||||
|
||||
if ( bClearState )
|
||||
{
|
||||
m_LoggingStates[nNewState].m_nListenerCount = 0;
|
||||
m_LoggingStates[nNewState].m_pLoggingResponse = &m_DefaultLoggingResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LoggingStates[nNewState] = m_LoggingStates[nCurrentState];
|
||||
}
|
||||
|
||||
m_LoggingStates[nNewState].m_nPreviousStackEntry = nCurrentState;
|
||||
|
||||
if ( bThreadLocal )
|
||||
{
|
||||
g_nThreadLocalStateIndex = nNewState;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nGlobalStateIndex = nNewState;
|
||||
}
|
||||
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
void CLoggingSystem::PopLoggingState( bool bThreadLocal )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
|
||||
int nCurrentState = bThreadLocal ? (int)g_nThreadLocalStateIndex : m_nGlobalStateIndex;
|
||||
|
||||
// Shouldn't be less than 0 (implies error during Push()) or 0 (implies that Push() was never called)
|
||||
Assert( nCurrentState > 0 );
|
||||
|
||||
// Mark the current state as unused.
|
||||
m_LoggingStates[nCurrentState].m_nListenerCount = -1;
|
||||
|
||||
if ( bThreadLocal )
|
||||
{
|
||||
g_nThreadLocalStateIndex = m_LoggingStates[nCurrentState].m_nPreviousStackEntry;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nGlobalStateIndex = m_LoggingStates[nCurrentState].m_nPreviousStackEntry;
|
||||
}
|
||||
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
void CLoggingSystem::RegisterLoggingListener( ILoggingListener *pListener )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
LoggingState_t *pState = GetCurrentState();
|
||||
if ( pState->m_nListenerCount > MAX_LOGGING_CHANNEL_COUNT )
|
||||
{
|
||||
// Out of logging listener slots... catastrophic fail!
|
||||
Assert( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
pState->m_RegisteredListeners[pState->m_nListenerCount] = pListener;
|
||||
++ pState->m_nListenerCount;
|
||||
}
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
void CLoggingSystem::UnregisterLoggingListener( ILoggingListener *pListener )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
LoggingState_t *pState = GetCurrentState();
|
||||
for ( int i = 0; i < pState->m_nListenerCount; ++ i )
|
||||
{
|
||||
if ( pState->m_RegisteredListeners[i] == pListener )
|
||||
{
|
||||
// Shuffle all the listeners ahead over these, and reduce the count.
|
||||
for ( int j = i; j < (pState->m_nListenerCount-1); ++ j )
|
||||
{
|
||||
pState->m_RegisteredListeners[j] = pState->m_RegisteredListeners[j+1];
|
||||
}
|
||||
pState->m_nListenerCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
bool CLoggingSystem::IsListenerRegistered( ILoggingListener *pListener )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
const LoggingState_t *pState = GetCurrentState();
|
||||
bool bFound = false;
|
||||
for ( int i = 0; i < pState->m_nListenerCount; ++ i )
|
||||
{
|
||||
if ( pState->m_RegisteredListeners[i] == pListener )
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_pStateMutex->Unlock();
|
||||
return bFound;
|
||||
}
|
||||
|
||||
void CLoggingSystem::ResetCurrentLoggingState()
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
LoggingState_t *pState = GetCurrentState();
|
||||
pState->m_nListenerCount = 0;
|
||||
pState->m_pLoggingResponse = &m_DefaultLoggingResponse;
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
void CLoggingSystem::SetLoggingResponsePolicy( ILoggingResponsePolicy *pLoggingResponse )
|
||||
{
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
LoggingState_t *pState = GetCurrentState();
|
||||
if ( pLoggingResponse == NULL )
|
||||
{
|
||||
pState->m_pLoggingResponse = &m_DefaultLoggingResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
pState->m_pLoggingResponse = pLoggingResponse;
|
||||
}
|
||||
m_pStateMutex->Unlock();
|
||||
}
|
||||
|
||||
LoggingResponse_t CLoggingSystem::LogDirect( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color color, const tchar *pMessage )
|
||||
{
|
||||
Assert( IsValidChannelID( channelID ) );
|
||||
if ( !IsValidChannelID( channelID ) )
|
||||
return LR_CONTINUE;
|
||||
|
||||
LoggingContext_t context;
|
||||
context.m_ChannelID = channelID;
|
||||
context.m_Flags = m_RegisteredChannels[channelID].m_Flags;
|
||||
context.m_Severity = severity;
|
||||
context.m_Color = ( color == UNSPECIFIED_LOGGING_COLOR ) ? m_RegisteredChannels[channelID].m_SpewColor : color;
|
||||
|
||||
// It is assumed that the mutex is reentrant safe on all platforms.
|
||||
if ( !m_pStateMutex )
|
||||
m_pStateMutex = new CThreadFastMutex();
|
||||
|
||||
m_pStateMutex->Lock();
|
||||
|
||||
LoggingState_t *pState = GetCurrentState();
|
||||
|
||||
for ( int i = 0; i < pState->m_nListenerCount; ++ i )
|
||||
{
|
||||
pState->m_RegisteredListeners[i]->Log( &context, pMessage );
|
||||
}
|
||||
|
||||
#if defined( _PS3 ) && !defined( _CERT )
|
||||
if ( !pState->m_nListenerCount )
|
||||
{
|
||||
unsigned int unBytesWritten;
|
||||
sys_tty_write( SYS_TTYP15, pMessage, strlen( pMessage ), &unBytesWritten );
|
||||
}
|
||||
#endif
|
||||
|
||||
LoggingResponse_t response = pState->m_pLoggingResponse->OnLog( &context );
|
||||
|
||||
m_pStateMutex->Unlock();
|
||||
|
||||
switch( response )
|
||||
{
|
||||
case LR_DEBUGGER:
|
||||
// Asserts put the debug break in the macro itself so the code breaks at the failure point.
|
||||
if ( severity != LS_ASSERT )
|
||||
{
|
||||
DebuggerBreakIfDebugging();
|
||||
}
|
||||
break;
|
||||
|
||||
case LR_ABORT:
|
||||
Log_Msg( LOG_DEVELOPER_VERBOSE, "Exiting due to logging LR_ABORT request.\n" );
|
||||
Plat_ExitProcess( EXIT_FAILURE );
|
||||
break;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
CLoggingSystem::LoggingChannel_t *CLoggingSystem::GetChannel( LoggingChannelID_t channelID )
|
||||
{
|
||||
Assert( IsValidChannelID( channelID ) );
|
||||
return &m_RegisteredChannels[channelID];
|
||||
}
|
||||
|
||||
const CLoggingSystem::LoggingChannel_t *CLoggingSystem::GetChannel( LoggingChannelID_t channelID ) const
|
||||
{
|
||||
Assert( IsValidChannelID( channelID ) );
|
||||
return &m_RegisteredChannels[channelID];
|
||||
}
|
||||
|
||||
CLoggingSystem::LoggingState_t *CLoggingSystem::GetCurrentState()
|
||||
{
|
||||
// Assume the caller grabbed the mutex.
|
||||
int nState = g_nThreadLocalStateIndex;
|
||||
if ( nState != 0 )
|
||||
{
|
||||
Assert( nState > 0 && nState < MAX_LOGGING_STATE_COUNT );
|
||||
return &m_LoggingStates[nState];
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( m_nGlobalStateIndex >= 0 && m_nGlobalStateIndex < MAX_LOGGING_STATE_COUNT );
|
||||
return &m_LoggingStates[m_nGlobalStateIndex];
|
||||
}
|
||||
}
|
||||
|
||||
const CLoggingSystem::LoggingState_t *CLoggingSystem::GetCurrentState() const
|
||||
{
|
||||
// Assume the caller grabbed the mutex.
|
||||
int nState = g_nThreadLocalStateIndex;
|
||||
if ( nState != 0 )
|
||||
{
|
||||
Assert( nState > 0 && nState < MAX_LOGGING_STATE_COUNT );
|
||||
return &m_LoggingStates[nState];
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( m_nGlobalStateIndex >= 0 && m_nGlobalStateIndex < MAX_LOGGING_STATE_COUNT );
|
||||
return &m_LoggingStates[m_nGlobalStateIndex];
|
||||
}
|
||||
}
|
||||
|
||||
int CLoggingSystem::FindUnusedStateIndex()
|
||||
{
|
||||
for ( int i = 0; i < MAX_LOGGING_STATE_COUNT; ++ i )
|
||||
{
|
||||
if ( m_LoggingStates[i].m_nListenerCount < 0 )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
CLoggingSystem::LoggingTag_t *CLoggingSystem::AllocTag( const char *pTagName )
|
||||
{
|
||||
Assert( m_nChannelTagCount < MAX_LOGGING_TAG_COUNT );
|
||||
LoggingTag_t *pTag = &m_ChannelTags[m_nChannelTagCount ++];
|
||||
|
||||
pTag->m_pNextTag = NULL;
|
||||
pTag->m_pTagName = m_TagNamePool + m_nTagNamePoolIndex;
|
||||
|
||||
// Copy string into pool.
|
||||
size_t nTagLength = strlen( pTagName );
|
||||
Assert( m_nTagNamePoolIndex + nTagLength + 1 <= MAX_LOGGING_TAG_CHARACTER_COUNT );
|
||||
strcpy( m_TagNamePool + m_nTagNamePoolIndex, pTagName );
|
||||
m_nTagNamePoolIndex += ( int )nTagLength + 1;
|
||||
|
||||
return pTag;
|
||||
}
|
||||
|
||||
LoggingChannelID_t LoggingSystem_RegisterLoggingChannel( const char *pName, RegisterTagsFunc registerTagsFunc, int flags, LoggingSeverity_t severity, Color color )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->RegisterLoggingChannel( pName, registerTagsFunc, flags, severity, color );
|
||||
}
|
||||
|
||||
void LoggingSystem_ResetCurrentLoggingState()
|
||||
{
|
||||
GetGlobalLoggingSystem()->ResetCurrentLoggingState();
|
||||
}
|
||||
|
||||
void LoggingSystem_RegisterLoggingListener( ILoggingListener *pListener )
|
||||
{
|
||||
GetGlobalLoggingSystem()->RegisterLoggingListener( pListener );
|
||||
}
|
||||
|
||||
void LoggingSystem_UnregisterLoggingListener( ILoggingListener *pListener )
|
||||
{
|
||||
GetGlobalLoggingSystem()->UnregisterLoggingListener( pListener );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetLoggingResponsePolicy( ILoggingResponsePolicy *pResponsePolicy )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetLoggingResponsePolicy( pResponsePolicy );
|
||||
}
|
||||
|
||||
void LoggingSystem_PushLoggingState( bool bThreadLocal, bool bClearState )
|
||||
{
|
||||
GetGlobalLoggingSystem()->PushLoggingState( bThreadLocal, bClearState );
|
||||
}
|
||||
|
||||
void LoggingSystem_PopLoggingState( bool bThreadLocal )
|
||||
{
|
||||
GetGlobalLoggingSystem()->PopLoggingState( bThreadLocal );
|
||||
}
|
||||
|
||||
void LoggingSystem_AddTagToCurrentChannel( const char *pTagName )
|
||||
{
|
||||
GetGlobalLoggingSystem()->AddTagToCurrentChannel( pTagName );
|
||||
}
|
||||
|
||||
LoggingChannelID_t LoggingSystem_FindChannel( const char *pChannelName )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->FindChannel( pChannelName );
|
||||
}
|
||||
|
||||
int LoggingSystem_GetChannelCount()
|
||||
{
|
||||
return GetGlobalLoggingSystem()->GetChannelCount();
|
||||
}
|
||||
|
||||
LoggingChannelID_t LoggingSystem_GetFirstChannelID()
|
||||
{
|
||||
return ( GetGlobalLoggingSystem()->GetChannelCount() > 0 ) ? 0 : INVALID_LOGGING_CHANNEL_ID;
|
||||
}
|
||||
|
||||
LoggingChannelID_t LoggingSystem_GetNextChannelID( LoggingChannelID_t channelID )
|
||||
{
|
||||
int nChannelCount = GetGlobalLoggingSystem()->GetChannelCount();
|
||||
int nNextChannel = channelID + 1;
|
||||
return ( nNextChannel < nChannelCount ) ? nNextChannel : INVALID_LOGGING_CHANNEL_ID;
|
||||
}
|
||||
|
||||
const CLoggingSystem::LoggingChannel_t *LoggingSystem_GetChannel( LoggingChannelID_t channelIndex )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->GetChannel( channelIndex );
|
||||
}
|
||||
|
||||
bool LoggingSystem_HasTag( LoggingChannelID_t channelID, const char *pTag )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->HasTag( channelID, pTag );
|
||||
}
|
||||
|
||||
bool LoggingSystem_IsChannelEnabled( LoggingChannelID_t channelID, LoggingSeverity_t severity )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->IsChannelEnabled( channelID, severity );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelSpewLevel( LoggingChannelID_t channelID, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetChannelSpewLevel( channelID, minimumSeverity );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelSpewLevelByName( const char *pName, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetChannelSpewLevelByName( pName, minimumSeverity );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelSpewLevelByTag( const char *pTag, LoggingSeverity_t minimumSeverity )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetChannelSpewLevelByTag( pTag, minimumSeverity );
|
||||
}
|
||||
|
||||
int32 LoggingSystem_GetChannelColor( LoggingChannelID_t channelID )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->GetChannelColor( channelID ).GetRawColor();
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelColor( LoggingChannelID_t channelID, int color )
|
||||
{
|
||||
Color c;
|
||||
c.SetRawColor( color );
|
||||
GetGlobalLoggingSystem()->SetChannelColor( channelID, c );
|
||||
}
|
||||
|
||||
LoggingChannelFlags_t LoggingSystem_GetChannelFlags( LoggingChannelID_t channelID )
|
||||
{
|
||||
return GetGlobalLoggingSystem()->GetChannelFlags( channelID );
|
||||
}
|
||||
|
||||
void LoggingSystem_SetChannelFlags( LoggingChannelID_t channelID, LoggingChannelFlags_t flags )
|
||||
{
|
||||
GetGlobalLoggingSystem()->SetChannelFlags( channelID, flags );
|
||||
}
|
||||
|
||||
LoggingResponse_t LoggingSystem_Log( LoggingChannelID_t channelID, LoggingSeverity_t severity, const char *pMessageFormat, ... )
|
||||
{
|
||||
if ( !GetGlobalLoggingSystem()->IsChannelEnabled( channelID, severity ) )
|
||||
return LR_CONTINUE;
|
||||
|
||||
tchar formattedMessage[MAX_LOGGING_MESSAGE_LENGTH];
|
||||
|
||||
va_list args;
|
||||
va_start( args, pMessageFormat );
|
||||
Tier0Internal_vsntprintf( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, pMessageFormat, args );
|
||||
va_end( args );
|
||||
|
||||
return GetGlobalLoggingSystem()->LogDirect( channelID, severity, UNSPECIFIED_LOGGING_COLOR, formattedMessage );
|
||||
}
|
||||
|
||||
LoggingResponse_t LoggingSystem_Log( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color spewColor, const char *pMessageFormat, ... )
|
||||
{
|
||||
if ( !GetGlobalLoggingSystem()->IsChannelEnabled( channelID, severity ) )
|
||||
return LR_CONTINUE;
|
||||
|
||||
tchar formattedMessage[MAX_LOGGING_MESSAGE_LENGTH];
|
||||
|
||||
va_list args;
|
||||
va_start( args, pMessageFormat );
|
||||
Tier0Internal_vsntprintf( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, pMessageFormat, args );
|
||||
va_end( args );
|
||||
|
||||
return GetGlobalLoggingSystem()->LogDirect( channelID, severity, spewColor, formattedMessage );
|
||||
}
|
||||
|
||||
LoggingResponse_t LoggingSystem_LogDirect( LoggingChannelID_t channelID, LoggingSeverity_t severity, Color spewColor, const char *pMessage )
|
||||
{
|
||||
if ( !GetGlobalLoggingSystem()->IsChannelEnabled( channelID, severity ) )
|
||||
return LR_CONTINUE;
|
||||
return GetGlobalLoggingSystem()->LogDirect( channelID, severity, spewColor, pMessage );
|
||||
}
|
||||
|
||||
LoggingResponse_t LoggingSystem_LogAssert( const char *pMessageFormat, ... )
|
||||
{
|
||||
if ( !GetGlobalLoggingSystem()->IsChannelEnabled( LOG_ASSERT, LS_ASSERT ) )
|
||||
return LR_CONTINUE;
|
||||
|
||||
tchar formattedMessage[MAX_LOGGING_MESSAGE_LENGTH];
|
||||
|
||||
va_list args;
|
||||
va_start( args, pMessageFormat );
|
||||
Tier0Internal_vsntprintf( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, pMessageFormat, args );
|
||||
va_end( args );
|
||||
|
||||
return GetGlobalLoggingSystem()->LogDirect( LOG_ASSERT, LS_ASSERT, UNSPECIFIED_LOGGING_COLOR, formattedMessage );
|
||||
}
|
||||
Vendored
+79
@@ -0,0 +1,79 @@
|
||||
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Memory allocation!
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/mem.h"
|
||||
//#include <malloc.h>
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#ifndef STEAM
|
||||
#define PvRealloc realloc
|
||||
#define PvAlloc malloc
|
||||
#define PvExpand _expand
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_STACK_DEPTH = 32
|
||||
};
|
||||
|
||||
static uint8 *s_pBuf = NULL;
|
||||
static int s_pBufStackDepth[MAX_STACK_DEPTH];
|
||||
static int s_nBufDepth = -1;
|
||||
static int s_nBufCurSize = 0;
|
||||
static int s_nBufAllocSize = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Other DLL-exported methods for particular kinds of memory
|
||||
//-----------------------------------------------------------------------------
|
||||
void *MemAllocScratch( int nMemSize )
|
||||
{
|
||||
// Minimally allocate 1M scratch
|
||||
if (s_nBufAllocSize < s_nBufCurSize + nMemSize)
|
||||
{
|
||||
s_nBufAllocSize = s_nBufCurSize + nMemSize;
|
||||
if (s_nBufAllocSize < 2 * 1024)
|
||||
{
|
||||
s_nBufAllocSize = 2 * 1024;
|
||||
}
|
||||
|
||||
if (s_pBuf)
|
||||
{
|
||||
s_pBuf = (uint8*)PvRealloc( s_pBuf, s_nBufAllocSize );
|
||||
Assert( s_pBuf );
|
||||
}
|
||||
else
|
||||
{
|
||||
s_pBuf = (uint8*)PvAlloc( s_nBufAllocSize );
|
||||
}
|
||||
}
|
||||
|
||||
int nBase = s_nBufCurSize;
|
||||
s_nBufCurSize += nMemSize;
|
||||
++s_nBufDepth;
|
||||
Assert( s_nBufDepth < MAX_STACK_DEPTH );
|
||||
s_pBufStackDepth[s_nBufDepth] = nMemSize;
|
||||
|
||||
return &s_pBuf[nBase];
|
||||
}
|
||||
|
||||
void MemFreeScratch()
|
||||
{
|
||||
Assert( s_nBufDepth >= 0 );
|
||||
s_nBufCurSize -= s_pBufStackDepth[s_nBufDepth];
|
||||
--s_nBufDepth;
|
||||
}
|
||||
|
||||
#ifdef POSIX
|
||||
void ZeroMemory( void *mem, size_t length )
|
||||
{
|
||||
memset( mem, 0x0, length );
|
||||
}
|
||||
#endif
|
||||
Vendored
+186
@@ -0,0 +1,186 @@
|
||||
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/icommandline.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "mem_helpers.h"
|
||||
#include <string.h>
|
||||
//#include <malloc.h>
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
// Needed for debugging
|
||||
const char *g_pszModule = "tier0";
|
||||
bool g_bInitMemory = true;
|
||||
|
||||
#if defined(PLATFORM_POSIX) || defined( PLATFORM_PS3)
|
||||
void DoApplyMemoryInitializations( void *pMem, size_t nSize )
|
||||
{
|
||||
}
|
||||
|
||||
size_t CalcHeapUsed()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
||||
unsigned long g_dwFeeFee = 0xffeeffee;
|
||||
|
||||
// Generated by Mathematica.
|
||||
unsigned char g_RandomValues[256] = {
|
||||
95, 126, 220, 71, 92, 179, 95, 219, 111, 150, 38, 155, 181, 62, 40, 231, 238,
|
||||
54, 47, 55, 186, 204, 64, 70, 118, 94, 107, 251, 199, 140, 67, 87, 86, 127,
|
||||
210, 41, 21, 90, 208, 24, 167, 204, 32, 254, 38, 51, 9, 11, 38, 33, 188, 104,
|
||||
0, 75, 119, 24, 122, 203, 24, 164, 250, 224, 241, 182, 213, 201, 173, 67,
|
||||
200, 255, 244, 227, 46, 219, 26, 149, 218, 132, 120, 154, 227, 244, 106, 198,
|
||||
109, 87, 150, 40, 16, 99, 169, 193, 100, 156, 78, 171, 246, 47, 84, 119, 10,
|
||||
52, 207, 171, 230, 90, 90, 127, 180, 153, 68, 140, 62, 14, 87, 57, 208, 154,
|
||||
116, 29, 131, 177, 224, 187, 51, 148, 142, 245, 152, 230, 184, 117, 91, 146,
|
||||
235, 153, 35, 104, 187, 177, 215, 131, 17, 49, 211, 244, 60, 152, 103, 248,
|
||||
51, 224, 237, 240, 51, 30, 10, 233, 253, 106, 252, 73, 134, 136, 178, 86,
|
||||
228, 107, 77, 255, 85, 242, 204, 119, 102, 53, 209, 35, 123, 32, 252, 210,
|
||||
43, 12, 136, 167, 155, 210, 71, 254, 178, 172, 3, 230, 93, 208, 196, 68, 235,
|
||||
16, 106, 189, 201, 177, 85, 78, 206, 187, 48, 68, 64, 190, 117, 236, 49, 174,
|
||||
105, 63, 207, 70, 170, 93, 6, 110, 52, 111, 169, 92, 247, 86, 10, 174, 207,
|
||||
240, 104, 209, 81, 177, 123, 189, 175, 212, 101, 219, 114, 243, 44, 91, 51,
|
||||
139, 91, 57, 120, 41, 98, 119 };
|
||||
|
||||
unsigned long g_iCurRandomValueOffset = 0;
|
||||
|
||||
|
||||
void InitializeToFeeFee( void *pMem, size_t nSize )
|
||||
{
|
||||
unsigned long *pCurDWord = (unsigned long*)pMem;
|
||||
size_t nDWords = nSize >> 2;
|
||||
while ( nDWords )
|
||||
{
|
||||
*pCurDWord = 0xffeeffee;
|
||||
++pCurDWord;
|
||||
--nDWords;
|
||||
}
|
||||
|
||||
unsigned char *pCurChar = (unsigned char*)pCurDWord;
|
||||
size_t nBytes = nSize & 3;
|
||||
size_t iOffset = 0;
|
||||
while ( nBytes )
|
||||
{
|
||||
*pCurChar = ((unsigned char*)&g_dwFeeFee)[iOffset];
|
||||
++iOffset;
|
||||
--nBytes;
|
||||
++pCurChar;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InitializeToRandom( void *pMem, size_t nSize )
|
||||
{
|
||||
unsigned char *pOut = (unsigned char *)pMem;
|
||||
for ( size_t i=0; i < nSize; i++ )
|
||||
{
|
||||
pOut[i] = g_RandomValues[(g_iCurRandomValueOffset & 255)];
|
||||
++g_iCurRandomValueOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DoApplyMemoryInitializations( void *pMem, size_t nSize )
|
||||
{
|
||||
if ( !pMem )
|
||||
return;
|
||||
|
||||
// If they passed -noinitmemory on the command line, don't do anything here.
|
||||
Assert( g_bInitMemory );
|
||||
|
||||
// First time we get in here, remember all the settings.
|
||||
static bool bDebuggerPresent = Plat_IsInDebugSession();
|
||||
static bool bCheckedCommandLine = false;
|
||||
static bool bRandomizeMemory = false;
|
||||
if ( !bCheckedCommandLine )
|
||||
{
|
||||
bCheckedCommandLine = true;
|
||||
|
||||
//APS
|
||||
char *pStr = (char*)Plat_GetCommandLineA();
|
||||
if ( pStr )
|
||||
{
|
||||
char tempStr[512];
|
||||
strncpy( tempStr, pStr, sizeof( tempStr ) - 1 );
|
||||
tempStr[ sizeof( tempStr ) - 1 ] = 0;
|
||||
_strupr( tempStr );
|
||||
|
||||
if ( strstr( tempStr, "-RANDOMIZEMEMORY" ) )
|
||||
bRandomizeMemory = true;
|
||||
|
||||
if ( strstr( tempStr, "-NOINITMEMORY" ) )
|
||||
g_bInitMemory = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bRandomizeMemory )
|
||||
{
|
||||
// They asked for it.. randomize all the memory.
|
||||
InitializeToRandom( pMem, nSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( bDebuggerPresent )
|
||||
{
|
||||
// Ok, it's already set to 0xbaadf00d, but we want something that will make floating-point #'s NANs.
|
||||
InitializeToFeeFee( pMem, nSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(_DEBUG) || defined(USE_LIGHT_MEM_DEBUG)
|
||||
#ifdef LIGHT_MEM_DEBUG_REQUIRES_CMD_LINE_SWITCH
|
||||
extern bool g_bUsingLMD;
|
||||
if ( !g_bUsingLMD )
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Ok, it's already set to 0xcdcdcdcd, but we want something that will make floating-point #'s NANs.
|
||||
InitializeToFeeFee( pMem, nSize );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t CalcHeapUsed()
|
||||
{
|
||||
#if defined( _X360 )
|
||||
return 0;
|
||||
#else
|
||||
_HEAPINFO hinfo;
|
||||
int heapstatus;
|
||||
intp nTotal;
|
||||
|
||||
nTotal = 0;
|
||||
hinfo._pentry = NULL;
|
||||
while( ( heapstatus = _heapwalk( &hinfo ) ) == _HEAPOK )
|
||||
{
|
||||
nTotal += (hinfo._useflag == _USEDENTRY) ? hinfo._size : 0;
|
||||
}
|
||||
|
||||
switch (heapstatus)
|
||||
{
|
||||
case _HEAPEMPTY:
|
||||
case _HEAPEND:
|
||||
// success
|
||||
break;
|
||||
|
||||
default:
|
||||
// heap corrupted
|
||||
nTotal = -1;
|
||||
}
|
||||
|
||||
return nTotal;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // not PLATFORM_POSIX
|
||||
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifndef MEM_HELPERS_H
|
||||
#define MEM_HELPERS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// Normally, the runtime libraries like to mess with the memory returned by malloc(),
|
||||
// which can create problems trying to repro bugs in debug builds or in the debugger.
|
||||
//
|
||||
// If the debugger is present, it initializes data to 0xbaadf00d, which makes floating
|
||||
// point numbers come out to about 0.1.
|
||||
//
|
||||
// If the debugger is not present, and it's a debug build, then you get 0xcdcdcdcd,
|
||||
// which is about 25 million.
|
||||
//
|
||||
// Otherwise, you get uninitialized memory.
|
||||
//
|
||||
// In here, we make sure the memory is either random garbage, or it's set to
|
||||
// 0xffeeffee, which casts to a NAN.
|
||||
extern bool g_bInitMemory;
|
||||
#define ApplyMemoryInitializations( pMem, nSize ) if ( !g_bInitMemory ) ; else { DoApplyMemoryInitializations( pMem, nSize ); }
|
||||
void DoApplyMemoryInitializations( void *pMem, size_t nSize );
|
||||
|
||||
size_t CalcHeapUsed();
|
||||
|
||||
#endif // MEM_HELPERS_H
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
|
||||
#if ( (!defined( POSIX )||defined(_GAMECONSOLE)) && (defined(_DEBUG) || defined(USE_MEM_DEBUG) ) )
|
||||
#define MEM_IMPL_TYPE_DBG 1
|
||||
#else
|
||||
#define MEM_IMPL_TYPE_STD 1
|
||||
#endif
|
||||
Vendored
+2762
File diff suppressed because it is too large
Load Diff
Vendored
+2493
File diff suppressed because it is too large
Load Diff
Vendored
+452
@@ -0,0 +1,452 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 IS_WINDOWS_PC
|
||||
#define WIN_32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#define VA_COMMIT_FLAGS MEM_COMMIT
|
||||
#define VA_RESERVE_FLAGS MEM_RESERVE
|
||||
#elif defined( _X360 )
|
||||
#undef Verify
|
||||
#define _XBOX
|
||||
#include <xtl.h>
|
||||
#undef _XBOX
|
||||
#include "xbox/xbox_win32stubs.h"
|
||||
#define VA_COMMIT_FLAGS (MEM_COMMIT|MEM_NOZERO|MEM_LARGE_PAGES)
|
||||
#define VA_RESERVE_FLAGS (MEM_RESERVE|MEM_LARGE_PAGES)
|
||||
#elif defined( _PS3 )
|
||||
#include "sys/memory.h"
|
||||
#include "sys/mempool.h"
|
||||
#include "sys/process.h"
|
||||
#include <sys/vm.h>
|
||||
|
||||
#endif
|
||||
|
||||
//#include <malloc.h>
|
||||
#include <algorithm>
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/memalloc.h"
|
||||
#include "tier0/threadtools.h"
|
||||
#include "tier0/tslist.h"
|
||||
#include "mem_helpers.h"
|
||||
|
||||
#ifndef _PS3
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
|
||||
#define MIN_SBH_BLOCK 8
|
||||
#define MIN_SBH_ALIGN 8
|
||||
#define MAX_SBH_BLOCK 2048
|
||||
#define MAX_POOL_REGION (4*1024*1024)
|
||||
|
||||
|
||||
#define NUM_POOLS 42
|
||||
|
||||
#if defined( _WIN32 ) || defined( _PS3 )
|
||||
// FIXME: Disable small block heap on win64 for now; it's busted because
|
||||
// it's expecting SLIST_HEADER to look different than it does on win64
|
||||
#if !defined( PLATFORM_WINDOWS_PC64 )
|
||||
#define MEM_SBH_ENABLED 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(_CERT) && ( defined(_X360) || defined(_PS3) )
|
||||
#define TRACK_SBH_COUNTS
|
||||
#endif
|
||||
|
||||
#if defined(_X360)
|
||||
|
||||
// 360 uses a 48MB primary (physical) SBH and 10MB secondary (virtual) SBH, with no fallback
|
||||
#define MBYTES_PRIMARY_SBH 48
|
||||
#define MEMALLOC_USE_SECONDARY_SBH
|
||||
#define MBYTES_SECONDARY_SBH 10
|
||||
#define MEMALLOC_NO_FALLBACK
|
||||
|
||||
#elif defined(_PS3)
|
||||
|
||||
// PS3 uses just a 32MB SBH - this was enough to avoid overflow when Portal 2 shipped.
|
||||
// NOTE: when Steam uses the game's tier0 allocator (see memalloc.h), we increase the size
|
||||
// of the SBH and MBH (see memstd.cpp) to accommodate those extra allocations.
|
||||
#define MBYTES_PRIMARY_SBH ( 32 + MBYTES_STEAM_SBH_USAGE )
|
||||
#define MEMALLOC_NO_FALLBACK
|
||||
|
||||
#else // _X360 | _PS3
|
||||
|
||||
// Other platforms use a 48MB primary SBH and a (32MB) fallback SBH
|
||||
#define MBYTES_PRIMARY_SBH 48
|
||||
|
||||
#endif // _X360 | _PS3
|
||||
|
||||
#define MEMSTD_COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Small block pool
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CFreeList : public CTSListBase
|
||||
{
|
||||
public:
|
||||
void Push( void *p ) { CTSListBase::Push( (TSLNodeBase_t *)p ); }
|
||||
byte *Pop() { return (byte *)CTSListBase::Pop(); }
|
||||
};
|
||||
|
||||
template <typename CAllocator>
|
||||
class CSmallBlockHeap;
|
||||
|
||||
template <typename CAllocator>
|
||||
class CSmallBlockPool
|
||||
{
|
||||
public:
|
||||
CSmallBlockPool()
|
||||
{
|
||||
m_nBlockSize = 0;
|
||||
m_nCommittedPages = 0;
|
||||
m_pFirstPage = NULL;
|
||||
}
|
||||
|
||||
void Init( unsigned nBlockSize );
|
||||
size_t GetBlockSize();
|
||||
void *Alloc();
|
||||
void Free( void *p );
|
||||
int CountFreeBlocks();
|
||||
int GetCommittedSize();
|
||||
int CountCommittedBlocks();
|
||||
int CountAllocatedBlocks();
|
||||
size_t Compact( bool bIncremental );
|
||||
bool Validate();
|
||||
|
||||
enum
|
||||
{
|
||||
BYTES_PAGE = CAllocator::BYTES_PAGE,
|
||||
NOT_COMMITTED = -1
|
||||
};
|
||||
|
||||
private:
|
||||
typedef CSmallBlockHeap<CAllocator> CHeap;
|
||||
friend class CSmallBlockHeap<CAllocator>;
|
||||
|
||||
struct PageStatus_t : public TSLNodeBase_t
|
||||
{
|
||||
PageStatus_t()
|
||||
{
|
||||
m_pPool = NULL;
|
||||
m_nAllocated = NOT_COMMITTED;
|
||||
m_pNextPageInPool = NULL;
|
||||
}
|
||||
|
||||
CSmallBlockPool<CAllocator> * m_pPool;
|
||||
PageStatus_t * m_pNextPageInPool;
|
||||
CInterlockedInt m_nAllocated;
|
||||
CTSListBase m_SortList;
|
||||
};
|
||||
|
||||
struct SharedData_t
|
||||
{
|
||||
CAllocator m_Allocator;
|
||||
CTSListBase m_FreePages;
|
||||
CThreadSpinRWLock m_Lock;
|
||||
PageStatus_t m_PageStatus[CAllocator::TOTAL_BYTES/CAllocator::BYTES_PAGE];
|
||||
byte * m_pNextBlock;
|
||||
byte * m_pBase;
|
||||
byte * m_pLimit;
|
||||
};
|
||||
|
||||
static int PageSort( const void *p1, const void *p2 ) ;
|
||||
bool RemovePagesFromFreeList( byte **pPages, int nPages, bool bSortList );
|
||||
|
||||
void ValidateFreelist( SharedData_t *pSharedData );
|
||||
|
||||
CFreeList m_FreeList;
|
||||
|
||||
CInterlockedPtr<byte> m_pNextAlloc;
|
||||
|
||||
PageStatus_t * m_pFirstPage;
|
||||
unsigned m_nBlockSize;
|
||||
unsigned m_nCommittedPages;
|
||||
|
||||
CThreadFastMutex m_CommitMutex;
|
||||
|
||||
#ifdef TRACK_SBH_COUNTS
|
||||
CInterlockedInt m_nFreeBlocks;
|
||||
#endif
|
||||
|
||||
static SharedData_t *GetSharedData()
|
||||
{
|
||||
return &gm_SharedData;
|
||||
}
|
||||
|
||||
static SharedData_t gm_SharedData;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Small block heap (multi-pool)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template <typename CAllocator>
|
||||
class CSmallBlockHeap
|
||||
{
|
||||
public:
|
||||
CSmallBlockHeap();
|
||||
bool ShouldUse( size_t nBytes );
|
||||
bool IsOwner( void * p );
|
||||
void *Alloc( size_t nBytes );
|
||||
void *Realloc( void *p, size_t nBytes );
|
||||
void Free( void *p );
|
||||
size_t GetSize( void *p );
|
||||
void DumpStats( const char *pszTag, FILE *pFile = NULL );
|
||||
void Usage( size_t &bytesCommitted, size_t &bytesAllocated );
|
||||
size_t Compact( bool bIncremental );
|
||||
bool Validate();
|
||||
|
||||
enum
|
||||
{
|
||||
BYTES_PAGE = CAllocator::BYTES_PAGE
|
||||
};
|
||||
|
||||
private:
|
||||
typedef CSmallBlockPool<CAllocator> CPool;
|
||||
typedef struct CSmallBlockPool<CAllocator>::SharedData_t SharedData_t;
|
||||
|
||||
CPool *FindPool( size_t nBytes );
|
||||
CPool *FindPool( void *p );
|
||||
|
||||
// Map size to a pool address to a pool
|
||||
CPool *m_PoolLookup[MAX_SBH_BLOCK >> 2];
|
||||
CPool m_Pools[NUM_POOLS];
|
||||
|
||||
SharedData_t *m_pSharedData;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
class CStdMemAlloc : public IMemAlloc
|
||||
{
|
||||
public:
|
||||
CStdMemAlloc();
|
||||
|
||||
// Internal versions
|
||||
void *InternalAlloc( int region, size_t nSize );
|
||||
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
||||
void *InternalAllocAligned( int region, size_t nSize, size_t align );
|
||||
#endif
|
||||
void *InternalAllocFromPools( size_t nSize );
|
||||
void *InternalRealloc( void *pMem, size_t nSize );
|
||||
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
||||
void *InternalReallocAligned( void *pMem, size_t nSize, size_t align );
|
||||
#endif
|
||||
void InternalFree( void *pMem );
|
||||
|
||||
void CompactOnFail();
|
||||
|
||||
// Release versions
|
||||
virtual void *Alloc( size_t nSize );
|
||||
virtual void *Realloc( void *pMem, size_t nSize );
|
||||
virtual void Free( void *pMem );
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
|
||||
|
||||
// Debug versions
|
||||
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
|
||||
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
||||
virtual void Free( void *pMem, const char *pFileName, int nLine );
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
||||
|
||||
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
||||
virtual void *AllocAlign( size_t nSize, size_t align );
|
||||
virtual void *AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine );
|
||||
virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align );
|
||||
virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine );
|
||||
#endif
|
||||
|
||||
virtual void *RegionAlloc( int region, size_t nSize );
|
||||
virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine );
|
||||
|
||||
// Returns size of a particular allocation
|
||||
virtual size_t GetSize( void *pMem );
|
||||
|
||||
// Force file + line information for an allocation
|
||||
virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
|
||||
virtual void PopAllocDbgInfo();
|
||||
|
||||
virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc );
|
||||
virtual int CrtSetReportMode( int nReportType, int nReportMode );
|
||||
virtual int CrtIsValidHeapPointer( const void *pMem );
|
||||
virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
|
||||
virtual int CrtCheckMemory( void );
|
||||
virtual int CrtSetDbgFlag( int nNewFlag );
|
||||
virtual void CrtMemCheckpoint( _CrtMemState *pState );
|
||||
void* CrtSetReportFile( int nRptType, void* hFile );
|
||||
void* CrtSetReportHook( void* pfnNewHook );
|
||||
int CrtDbgReport( int nRptType, const char * szFile,
|
||||
int nLine, const char * szModule, const char * pMsg );
|
||||
virtual int heapchk();
|
||||
|
||||
virtual void DumpStats();
|
||||
virtual void DumpStatsFileBase( char const *pchFileBase );
|
||||
virtual size_t ComputeMemoryUsedBy( char const *pchSubStr );
|
||||
virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory );
|
||||
|
||||
virtual bool IsDebugHeap() { return false; }
|
||||
|
||||
virtual void GetActualDbgInfo( const char *&pFileName, int &nLine ) {}
|
||||
virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) {}
|
||||
virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime ) {}
|
||||
|
||||
virtual int GetVersion() { return MEMALLOC_VERSION; }
|
||||
|
||||
virtual void OutOfMemory( size_t nBytesAttempted = 0 ) { SetCRTAllocFailed( nBytesAttempted ); }
|
||||
|
||||
virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes );
|
||||
|
||||
virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats );
|
||||
|
||||
virtual void CompactHeap();
|
||||
virtual void CompactIncremental();
|
||||
|
||||
virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler );
|
||||
size_t CallAllocFailHandler( size_t nBytes ) { return (*m_pfnFailHandler)( nBytes); }
|
||||
|
||||
virtual uint32 GetDebugInfoSize() { return 0; }
|
||||
virtual void SaveDebugInfo( void *pvDebugInfo ) { }
|
||||
virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
|
||||
virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
|
||||
|
||||
static size_t DefaultFailHandler( size_t );
|
||||
void DumpBlockStats( void *p ) {}
|
||||
|
||||
#if MEM_SBH_ENABLED
|
||||
class CVirtualAllocator
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
BYTES_PAGE = (64*1024),
|
||||
TOTAL_BYTES = (32*1024*1024),
|
||||
MIN_RESERVE_PAGES = 4,
|
||||
};
|
||||
|
||||
byte *AllocatePoolMemory()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (byte *)VirtualAlloc( NULL, TOTAL_BYTES, VA_RESERVE_FLAGS, PAGE_NOACCESS );
|
||||
#elif defined( _PS3 )
|
||||
Error( "" );
|
||||
return NULL;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsVirtual()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Decommit( void *pPage )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ( VirtualFree( pPage, BYTES_PAGE, MEM_DECOMMIT ) != 0 );
|
||||
#elif defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Commit( void *pPage )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ( VirtualAlloc( pPage, BYTES_PAGE, VA_COMMIT_FLAGS, PAGE_READWRITE ) != NULL );
|
||||
#elif defined( _PS3 )
|
||||
return false;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
typedef CSmallBlockHeap<CVirtualAllocator> CVirtualSmallBlockHeap;
|
||||
|
||||
template <size_t SIZE_MB, bool bPhysical>
|
||||
class CFixedAllocator
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
BYTES_PAGE = (16*1024),
|
||||
TOTAL_BYTES = (SIZE_MB*1024*1024),
|
||||
MIN_RESERVE_PAGES = TOTAL_BYTES/BYTES_PAGE,
|
||||
};
|
||||
|
||||
byte *AllocatePoolMemory()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef _X360
|
||||
if ( bPhysical )
|
||||
return (byte *)XPhysicalAlloc( TOTAL_BYTES, MAXULONG_PTR, 4096, PAGE_READWRITE | MEM_16MB_PAGES );
|
||||
#endif
|
||||
return (byte *)VirtualAlloc( NULL, TOTAL_BYTES, VA_COMMIT_FLAGS, PAGE_READWRITE );
|
||||
#elif defined( _PS3 )
|
||||
// TODO: release this section on shutdown (use GetMemorySectionForAddress)
|
||||
extern IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes );
|
||||
IVirtualMemorySection *pSection = VirtualMemoryManager_AllocateVirtualMemorySection( TOTAL_BYTES );
|
||||
if ( !pSection )
|
||||
Error( "CFixedAllocator::AllocatePoolMemory() failed in VirtualMemoryManager_AllocateVirtualMemorySection\n" );
|
||||
if ( !pSection->CommitPages( pSection->GetBaseAddress(), TOTAL_BYTES ) )
|
||||
Error( "CFixedAllocator::AllocatePoolMemory() failed in IVirtualMemorySection::CommitPages\n" );
|
||||
return reinterpret_cast<byte *>( pSection->GetBaseAddress() );
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsVirtual()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decommit( void *pPage )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Commit( void *pPage )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef CSmallBlockHeap<CFixedAllocator< MBYTES_PRIMARY_SBH, true> > CFixedSmallBlockHeap;
|
||||
#ifdef MEMALLOC_USE_SECONDARY_SBH
|
||||
typedef CSmallBlockHeap<CFixedAllocator< MBYTES_SECONDARY_SBH, false> > CFixedVirtualSmallBlockHeap; // @TODO: move back into above heap if number stays at 16 [7/15/2009 tom]
|
||||
#endif
|
||||
|
||||
CFixedSmallBlockHeap m_PrimarySBH;
|
||||
#ifdef MEMALLOC_USE_SECONDARY_SBH
|
||||
CFixedVirtualSmallBlockHeap m_SecondarySBH;
|
||||
#endif
|
||||
#ifndef MEMALLOC_NO_FALLBACK
|
||||
CVirtualSmallBlockHeap m_FallbackSBH;
|
||||
#endif
|
||||
|
||||
#endif // MEM_SBH_ENABLED
|
||||
|
||||
|
||||
virtual void SetStatsExtraInfo( const char *pMapName, const char *pComment );
|
||||
|
||||
virtual size_t MemoryAllocFailed();
|
||||
|
||||
void SetCRTAllocFailed( size_t nMemSize );
|
||||
|
||||
MemAllocFailHandler_t m_pfnFailHandler;
|
||||
size_t m_sMemoryAllocFailed;
|
||||
CThreadFastMutex m_CompactMutex;
|
||||
bool m_bInCompact;
|
||||
};
|
||||
|
||||
#ifndef _PS3
|
||||
#pragma pack()
|
||||
#endif
|
||||
Vendored
+498
@@ -0,0 +1,498 @@
|
||||
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Memory allocation!
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
#ifndef STEAM
|
||||
|
||||
#ifdef TIER0_VALIDATE_HEAP
|
||||
|
||||
#include <malloc.h>
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/memalloc.h"
|
||||
#include "mem_helpers.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
extern IMemAlloc *g_pActualAlloc;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// NOTE! This should never be called directly from leaf code
|
||||
// Just use new,delete,malloc,free etc. They will call into this eventually
|
||||
//-----------------------------------------------------------------------------
|
||||
class CValidateAlloc : public IMemAlloc
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
HEAP_PREFIX_BUFFER_SIZE = 12,
|
||||
HEAP_SUFFIX_BUFFER_SIZE = 8,
|
||||
};
|
||||
|
||||
CValidateAlloc();
|
||||
|
||||
// Release versions
|
||||
virtual void *Alloc( size_t nSize );
|
||||
virtual void *Realloc( void *pMem, size_t nSize );
|
||||
virtual void Free( void *pMem );
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
|
||||
|
||||
// Debug versions
|
||||
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
|
||||
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
||||
virtual void Free( void *pMem, const char *pFileName, int nLine );
|
||||
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
||||
|
||||
// Returns size of a particular allocation
|
||||
virtual size_t GetSize( void *pMem );
|
||||
|
||||
// Force file + line information for an allocation
|
||||
virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
|
||||
virtual void PopAllocDbgInfo();
|
||||
|
||||
virtual long CrtSetBreakAlloc( long lNewBreakAlloc );
|
||||
virtual int CrtSetReportMode( int nReportType, int nReportMode );
|
||||
virtual int CrtIsValidHeapPointer( const void *pMem );
|
||||
virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
|
||||
virtual int CrtCheckMemory( void );
|
||||
virtual int CrtSetDbgFlag( int nNewFlag );
|
||||
virtual void CrtMemCheckpoint( _CrtMemState *pState );
|
||||
void* CrtSetReportFile( int nRptType, void* hFile );
|
||||
void* CrtSetReportHook( void* pfnNewHook );
|
||||
int CrtDbgReport( int nRptType, const char * szFile,
|
||||
int nLine, const char * szModule, const char * pMsg );
|
||||
virtual int heapchk();
|
||||
|
||||
virtual void DumpStats() {}
|
||||
virtual void DumpStatsFileBase( char const *pchFileBase ) {}
|
||||
|
||||
virtual bool IsDebugHeap()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int GetVersion() { return MEMALLOC_VERSION; }
|
||||
|
||||
virtual void CompactHeap();
|
||||
virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler );
|
||||
|
||||
virtual uint32 GetDebugInfoSize() { return 0; }
|
||||
virtual void SaveDebugInfo( void *pvDebugInfo ) { }
|
||||
virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
|
||||
virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
|
||||
|
||||
private:
|
||||
struct HeapPrefix_t
|
||||
{
|
||||
HeapPrefix_t *m_pPrev;
|
||||
HeapPrefix_t *m_pNext;
|
||||
int m_nSize;
|
||||
unsigned char m_Prefix[HEAP_PREFIX_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
struct HeapSuffix_t
|
||||
{
|
||||
unsigned char m_Suffix[HEAP_SUFFIX_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
private:
|
||||
// Returns the actual debug info
|
||||
void GetActualDbgInfo( const char *&pFileName, int &nLine );
|
||||
|
||||
// Updates stats
|
||||
void RegisterAllocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime );
|
||||
void RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime );
|
||||
|
||||
HeapSuffix_t *Suffix( HeapPrefix_t *pPrefix );
|
||||
void *AllocationStart( HeapPrefix_t *pBase );
|
||||
HeapPrefix_t *PrefixFromAllocation( void *pAlloc );
|
||||
const HeapPrefix_t *PrefixFromAllocation( const void *pAlloc );
|
||||
|
||||
// Add to the list!
|
||||
void AddToList( HeapPrefix_t *pHeap, int nSize );
|
||||
|
||||
// Remove from the list!
|
||||
void RemoveFromList( HeapPrefix_t *pHeap );
|
||||
|
||||
// Validate the allocation
|
||||
bool ValidateAllocation( HeapPrefix_t *pHeap );
|
||||
|
||||
private:
|
||||
HeapPrefix_t *m_pFirstAllocation;
|
||||
char m_pPrefixImage[HEAP_PREFIX_BUFFER_SIZE];
|
||||
char m_pSuffixImage[HEAP_SUFFIX_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Singleton...
|
||||
//-----------------------------------------------------------------------------
|
||||
static CValidateAlloc s_ValidateAlloc;
|
||||
|
||||
#ifdef _PS3
|
||||
|
||||
IMemAlloc *g_pMemAllocInternalPS3 = &s_ValidateAlloc;
|
||||
|
||||
#else // !_PS3
|
||||
|
||||
IMemAlloc *g_pMemAlloc = &s_ValidateAlloc;
|
||||
|
||||
#endif // _PS3
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor.
|
||||
//-----------------------------------------------------------------------------
|
||||
CValidateAlloc::CValidateAlloc()
|
||||
{
|
||||
m_pFirstAllocation = 0;
|
||||
memset( m_pPrefixImage, 0xBE, HEAP_PREFIX_BUFFER_SIZE );
|
||||
memset( m_pSuffixImage, 0xAF, HEAP_SUFFIX_BUFFER_SIZE );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Accessors...
|
||||
//-----------------------------------------------------------------------------
|
||||
inline CValidateAlloc::HeapSuffix_t *CValidateAlloc::Suffix( HeapPrefix_t *pPrefix )
|
||||
{
|
||||
return reinterpret_cast<HeapSuffix_t *>( (unsigned char*)( pPrefix + 1 ) + pPrefix->m_nSize );
|
||||
}
|
||||
|
||||
inline void *CValidateAlloc::AllocationStart( HeapPrefix_t *pBase )
|
||||
{
|
||||
return static_cast<void *>( pBase + 1 );
|
||||
}
|
||||
|
||||
inline CValidateAlloc::HeapPrefix_t *CValidateAlloc::PrefixFromAllocation( void *pAlloc )
|
||||
{
|
||||
if ( !pAlloc )
|
||||
return NULL;
|
||||
|
||||
return ((HeapPrefix_t*)pAlloc) - 1;
|
||||
}
|
||||
|
||||
inline const CValidateAlloc::HeapPrefix_t *CValidateAlloc::PrefixFromAllocation( const void *pAlloc )
|
||||
{
|
||||
return ((const HeapPrefix_t*)pAlloc) - 1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Add to the list!
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidateAlloc::AddToList( HeapPrefix_t *pHeap, int nSize )
|
||||
{
|
||||
pHeap->m_pPrev = NULL;
|
||||
pHeap->m_pNext = m_pFirstAllocation;
|
||||
if ( m_pFirstAllocation )
|
||||
{
|
||||
m_pFirstAllocation->m_pPrev = pHeap;
|
||||
}
|
||||
pHeap->m_nSize = nSize;
|
||||
|
||||
m_pFirstAllocation = pHeap;
|
||||
|
||||
HeapSuffix_t *pSuffix = Suffix( pHeap );
|
||||
memcpy( pHeap->m_Prefix, m_pPrefixImage, HEAP_PREFIX_BUFFER_SIZE );
|
||||
memcpy( pSuffix->m_Suffix, m_pSuffixImage, HEAP_SUFFIX_BUFFER_SIZE );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Remove from the list!
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidateAlloc::RemoveFromList( HeapPrefix_t *pHeap )
|
||||
{
|
||||
if ( !pHeap )
|
||||
return;
|
||||
|
||||
ValidateAllocation( pHeap );
|
||||
if ( pHeap->m_pPrev )
|
||||
{
|
||||
pHeap->m_pPrev->m_pNext = pHeap->m_pNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pFirstAllocation = pHeap->m_pNext;
|
||||
}
|
||||
|
||||
if ( pHeap->m_pNext )
|
||||
{
|
||||
pHeap->m_pNext->m_pPrev = pHeap->m_pPrev;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Validate the allocation
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CValidateAlloc::ValidateAllocation( HeapPrefix_t *pHeap )
|
||||
{
|
||||
HeapSuffix_t *pSuffix = Suffix( pHeap );
|
||||
|
||||
bool bOk = true;
|
||||
if ( memcmp( pHeap->m_Prefix, m_pPrefixImage, HEAP_PREFIX_BUFFER_SIZE ) )
|
||||
{
|
||||
bOk = false;
|
||||
}
|
||||
|
||||
if ( memcmp( pSuffix->m_Suffix, m_pSuffixImage, HEAP_SUFFIX_BUFFER_SIZE ) )
|
||||
{
|
||||
bOk = false;
|
||||
}
|
||||
|
||||
if ( !bOk )
|
||||
{
|
||||
Warning("Memory trash detected in allocation %X!\n", (void*)(pHeap+1) );
|
||||
Assert( 0 );
|
||||
}
|
||||
|
||||
return bOk;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Release versions
|
||||
//-----------------------------------------------------------------------------
|
||||
void *CValidateAlloc::Alloc( size_t nSize )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
HeapPrefix_t *pHeap = (HeapPrefix_t*)g_pActualAlloc->Alloc( nActualSize );
|
||||
AddToList( pHeap, nSize );
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
void *CValidateAlloc::Realloc( void *pMem, size_t nSize )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
pHeap = (HeapPrefix_t*)g_pActualAlloc->Realloc( pHeap, nActualSize );
|
||||
AddToList( pHeap, nSize );
|
||||
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
void CValidateAlloc::Free( void *pMem )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
g_pActualAlloc->Free( pHeap );
|
||||
}
|
||||
|
||||
void *CValidateAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
pHeap = (HeapPrefix_t*)g_pActualAlloc->Expand_NoLongerSupported( pHeap, nActualSize );
|
||||
AddToList( pHeap, nSize );
|
||||
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Debug versions
|
||||
//-----------------------------------------------------------------------------
|
||||
void *CValidateAlloc::Alloc( size_t nSize, const char *pFileName, int nLine )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
HeapPrefix_t *pHeap = (HeapPrefix_t*)g_pActualAlloc->Alloc( nActualSize, pFileName, nLine );
|
||||
AddToList( pHeap, nSize );
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
void *CValidateAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
pHeap = (HeapPrefix_t*)g_pActualAlloc->Realloc( pHeap, nActualSize, pFileName, nLine );
|
||||
AddToList( pHeap, nSize );
|
||||
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
void CValidateAlloc::Free( void *pMem, const char *pFileName, int nLine )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
g_pActualAlloc->Free( pHeap, pFileName, nLine );
|
||||
}
|
||||
|
||||
void *CValidateAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine )
|
||||
{
|
||||
Assert( heapchk() == _HEAPOK );
|
||||
Assert( CrtCheckMemory() );
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
RemoveFromList( pHeap );
|
||||
|
||||
int nActualSize = nSize + sizeof(HeapPrefix_t) + sizeof(HeapSuffix_t);
|
||||
pHeap = (HeapPrefix_t*)g_pActualAlloc->Expand_NoLongerSupported( pHeap, nActualSize, pFileName, nLine );
|
||||
AddToList( pHeap, nSize );
|
||||
|
||||
return AllocationStart( pHeap );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns size of a particular allocation
|
||||
//-----------------------------------------------------------------------------
|
||||
size_t CValidateAlloc::GetSize( void *pMem )
|
||||
{
|
||||
if ( !pMem )
|
||||
return CalcHeapUsed();
|
||||
|
||||
HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
return pHeap->m_nSize;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Force file + line information for an allocation
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValidateAlloc::PushAllocDbgInfo( const char *pFileName, int nLine )
|
||||
{
|
||||
g_pActualAlloc->PushAllocDbgInfo( pFileName, nLine );
|
||||
}
|
||||
|
||||
void CValidateAlloc::PopAllocDbgInfo()
|
||||
{
|
||||
g_pActualAlloc->PopAllocDbgInfo( );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FIXME: Remove when we make our own heap! Crt stuff we're currently using
|
||||
//-----------------------------------------------------------------------------
|
||||
long CValidateAlloc::CrtSetBreakAlloc( long lNewBreakAlloc )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetBreakAlloc( lNewBreakAlloc );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtSetReportMode( int nReportType, int nReportMode )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetReportMode( nReportType, nReportMode );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtIsValidHeapPointer( const void *pMem )
|
||||
{
|
||||
const HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
return g_pActualAlloc->CrtIsValidHeapPointer( pHeap );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtIsValidPointer( const void *pMem, unsigned int size, int access )
|
||||
{
|
||||
const HeapPrefix_t *pHeap = PrefixFromAllocation( pMem );
|
||||
return g_pActualAlloc->CrtIsValidPointer( pHeap, size, access );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtCheckMemory( void )
|
||||
{
|
||||
return g_pActualAlloc->CrtCheckMemory( );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtSetDbgFlag( int nNewFlag )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetDbgFlag( nNewFlag );
|
||||
}
|
||||
|
||||
void CValidateAlloc::CrtMemCheckpoint( _CrtMemState *pState )
|
||||
{
|
||||
g_pActualAlloc->CrtMemCheckpoint( pState );
|
||||
}
|
||||
|
||||
void* CValidateAlloc::CrtSetReportFile( int nRptType, void* hFile )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetReportFile( nRptType, hFile );
|
||||
}
|
||||
|
||||
void* CValidateAlloc::CrtSetReportHook( void* pfnNewHook )
|
||||
{
|
||||
return g_pActualAlloc->CrtSetReportHook( pfnNewHook );
|
||||
}
|
||||
|
||||
int CValidateAlloc::CrtDbgReport( int nRptType, const char * szFile,
|
||||
int nLine, const char * szModule, const char * pMsg )
|
||||
{
|
||||
return g_pActualAlloc->CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg );
|
||||
}
|
||||
|
||||
int CValidateAlloc::heapchk()
|
||||
{
|
||||
bool bOk = true;
|
||||
|
||||
// Validate the heap
|
||||
HeapPrefix_t *pHeap = m_pFirstAllocation;
|
||||
for( pHeap = m_pFirstAllocation; pHeap; pHeap = pHeap->m_pNext )
|
||||
{
|
||||
if ( !ValidateAllocation( pHeap ) )
|
||||
{
|
||||
bOk = false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
return bOk ? _HEAPOK : 0;
|
||||
#elif POSIX
|
||||
return bOk;
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the actual debug info
|
||||
void CValidateAlloc::GetActualDbgInfo( const char *&pFileName, int &nLine )
|
||||
{
|
||||
g_pActualAlloc->GetActualDbgInfo( pFileName, nLine );
|
||||
}
|
||||
|
||||
// Updates stats
|
||||
void CValidateAlloc::RegisterAllocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime )
|
||||
{
|
||||
g_pActualAlloc->RegisterAllocation( pFileName, nLine, nLogicalSize, nActualSize, nTime );
|
||||
}
|
||||
|
||||
void CValidateAlloc::RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime )
|
||||
{
|
||||
g_pActualAlloc->RegisterDeallocation( pFileName, nLine, nLogicalSize, nActualSize, nTime );
|
||||
}
|
||||
|
||||
void CValidateAlloc::CompactHeap()
|
||||
{
|
||||
g_pActualAlloc->CompactHeap();
|
||||
}
|
||||
|
||||
MemAllocFailHandler_t CValidateAlloc::SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler )
|
||||
{
|
||||
return g_pActualAlloc->SetAllocFailHandler( pfnMemAllocFailHandler );
|
||||
}
|
||||
|
||||
#endif // TIER0_VALIDATE_HEAP
|
||||
|
||||
#endif // STEAM
|
||||
Vendored
+317
@@ -0,0 +1,317 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/minidump.h"
|
||||
#include "tier0/platform.h"
|
||||
|
||||
|
||||
#if defined( _WIN32 ) && !defined(_X360 ) && ( _MSC_VER >= 1300 )
|
||||
|
||||
#include "tier0/valve_off.h"
|
||||
#define WIN_32_LEAN_AND_MEAN
|
||||
#define _WIN32_WINNT 0x0403
|
||||
#include <windows.h>
|
||||
#include <time.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#endif
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
#if defined( _WIN32 ) && !defined( _X360 )
|
||||
|
||||
#if _MSC_VER >= 1300
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
|
||||
// true if we're currently writing a minidump caused by an assert
|
||||
static bool g_bWritingNonfatalMinidump = false;
|
||||
// counter used to make sure minidump names are unique
|
||||
static int g_nMinidumpsWritten = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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,
|
||||
ExceptionInfo_t * pExceptionInfo,
|
||||
uint32 minidumpType,
|
||||
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
|
||||
struct tm curtime;
|
||||
Plat_GetLocalTime( &curtime );
|
||||
++g_nMinidumpsWritten;
|
||||
|
||||
// strip off the rest of the path from the .exe name
|
||||
tchar rgchModuleName[MAX_PATH];
|
||||
#ifdef TCHAR_IS_WCHAR
|
||||
::GetModuleFileNameW( NULL, rgchModuleName, sizeof(rgchModuleName) / sizeof(tchar) );
|
||||
#else
|
||||
::GetModuleFileName( NULL, rgchModuleName, sizeof(rgchModuleName) / sizeof(tchar) );
|
||||
#endif
|
||||
tchar *pch = _tcsrchr( rgchModuleName, '.' );
|
||||
if ( pch )
|
||||
{
|
||||
*pch = 0;
|
||||
}
|
||||
pch = _tcsrchr( rgchModuleName, '\\' );
|
||||
if ( pch )
|
||||
{
|
||||
// move past the last slash
|
||||
pch++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pch = _T("unknown");
|
||||
}
|
||||
|
||||
|
||||
// can't use the normal string functions since we're in tier0
|
||||
tchar rgchFileName[MAX_PATH];
|
||||
_sntprintf( rgchFileName, sizeof(rgchFileName) / sizeof(tchar),
|
||||
_T("%s_%s_%d%.2d%2d%.2d%.2d%.2d_%d.mdmp"),
|
||||
pch,
|
||||
g_bWritingNonfatalMinidump ? "assert" : "crash",
|
||||
curtime.tm_year + 1900, /* Year less 2000 */
|
||||
curtime.tm_mon + 1, /* month (0 - 11 : 0 = January) */
|
||||
curtime.tm_mday, /* day of month (1 - 31) */
|
||||
curtime.tm_hour, /* hour (0 - 23) */
|
||||
curtime.tm_min, /* minutes (0 - 59) */
|
||||
curtime.tm_sec, /* seconds (0 - 59) */
|
||||
g_nMinidumpsWritten // ensures the filename is unique
|
||||
);
|
||||
|
||||
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 = (PEXCEPTION_POINTERS)pExceptionInfo;
|
||||
ExInfo.ClientPointers = FALSE;
|
||||
|
||||
bMinidumpResult = (*pfnMiniDumpWrite)( ::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, (MINIDUMP_TYPE)minidumpType, &ExInfo, NULL, NULL );
|
||||
::CloseHandle( hFile );
|
||||
|
||||
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 );
|
||||
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, ExceptionInfo_t * pExceptionInfo )
|
||||
{
|
||||
// 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.
|
||||
uint32 iType = MINIDUMP_WithDataSegs | MINIDUMP_WithIndirectlyReferencedMemory;
|
||||
if ( !WriteMiniDumpUsingExceptionInfo( uStructuredExceptionCode, pExceptionInfo, iType ) )
|
||||
{
|
||||
iType = MINIDUMP_WithDataSegs;
|
||||
WriteMiniDumpUsingExceptionInfo( uStructuredExceptionCode, pExceptionInfo, iType );
|
||||
}
|
||||
}
|
||||
|
||||
// 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, (ExceptionInfo_t*)pExceptionInfo );
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
void MinidumpSetUnhandledExceptionFunction( FnMiniDump pfn )
|
||||
{
|
||||
g_UnhandledExceptionFunction = pfn;
|
||||
SetUnhandledExceptionFilter( ValveUnhandledExceptionFilter );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: writes out a minidump from the current process
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef void (*FnMiniDumpInternal_t)( unsigned int uStructuredExceptionCode, _EXCEPTION_POINTERS * pExceptionInfo );
|
||||
|
||||
void WriteMiniDump()
|
||||
{
|
||||
// throw an exception so we can catch it and get the stack info
|
||||
g_bWritingNonfatalMinidump = true;
|
||||
__try
|
||||
{
|
||||
::RaiseException
|
||||
(
|
||||
0, // 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( 0, (ExceptionInfo_t*)GetExceptionInformation() ), EXCEPTION_EXECUTE_HANDLER )
|
||||
{
|
||||
}
|
||||
g_bWritingNonfatalMinidump = false;
|
||||
}
|
||||
|
||||
PLATFORM_OVERLOAD bool g_bInException = false;
|
||||
#include <eh.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Catches and writes out any exception throw by the specified function
|
||||
//-----------------------------------------------------------------------------
|
||||
void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] )
|
||||
{
|
||||
if ( Plat_IsInDebugSession() )
|
||||
{
|
||||
// don't mask exceptions when running in the debugger
|
||||
pfn( argc, argv );
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4535) // warning C4535: calling _set_se_translator() requires /EHa
|
||||
_set_se_translator( (FnMiniDumpInternal_t)g_pfnWriteMiniDump );
|
||||
#pragma warning(pop)
|
||||
pfn( argc, argv );
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
g_bInException = true;
|
||||
Log_Msg( LOG_CONSOLE, _T("Fatal exception caught, minidump written\n") );
|
||||
// handle everything and just quit, we've already written out our minidump
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
PLATFORM_INTERFACE void WriteMiniDump()
|
||||
{
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] )
|
||||
{
|
||||
pfn( argc, argv );
|
||||
}
|
||||
|
||||
#endif
|
||||
#elif defined(_X360 )
|
||||
PLATFORM_INTERFACE void WriteMiniDump()
|
||||
{
|
||||
#if !defined( _CERT )
|
||||
DmCrashDump(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else // !_WIN32
|
||||
|
||||
PLATFORM_INTERFACE void WriteMiniDump()
|
||||
{
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void CatchAndWriteMiniDump( FnWMain pfn, int argc, tchar *argv[] )
|
||||
{
|
||||
pfn( argc, argv );
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
|
||||
#include "pch_tier0.h"
|
||||
|
||||
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
|
||||
#if defined( PLATFORM_WINDOWS_PC )
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define _WIN32_WINNT 0x0403
|
||||
#include <windows.h>
|
||||
#elif defined( _PS3 )
|
||||
#include <cellstatus.h>
|
||||
#include <sys/prx.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
// First include standard libraries
|
||||
#include "tier0/valve_off.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#ifdef PLATFORM_POSIX
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#define _MAX_PATH PATH_MAX
|
||||
#endif
|
||||
|
||||
#include "tier0/valve_on.h"
|
||||
|
||||
#include "tier0/basetypes.h"
|
||||
#include "tier0/dbgflag.h"
|
||||
#include "tier0/dbg.h"
|
||||
#ifdef STEAM
|
||||
#include "tier0/memhook.h"
|
||||
#endif
|
||||
#include "tier0/validator.h"
|
||||
#include "tier0/fasttimer.h"
|
||||
Vendored
+559
@@ -0,0 +1,559 @@
|
||||
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//===========================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include <time.h>
|
||||
|
||||
#if defined(_WIN32) && !defined(_X360)
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#define _WIN32_WINNT 0x0403
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include "tier0/platform.h"
|
||||
#if defined( _X360 )
|
||||
#include "xbox/xbox_console.h"
|
||||
#endif
|
||||
#include "tier0/threadtools.h"
|
||||
|
||||
#include "tier0/memalloc.h"
|
||||
|
||||
#if defined( _PS3 )
|
||||
#include <cell/fios/fios_common.h>
|
||||
#include <cell/fios/fios_memory.h>
|
||||
#include <cell/fios/fios_configuration.h>
|
||||
#include <sys/process.h>
|
||||
|
||||
#if !defined(_CERT)
|
||||
#include "sn/LibSN.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
#include <sys/types.h>
|
||||
#include <sys/process.h>
|
||||
#include <sys/prx.h>
|
||||
|
||||
#include <sysutil/sysutil_syscache.h>
|
||||
|
||||
#include <cell/sysmodule.h>
|
||||
*/
|
||||
#include <cell/fios/fios_time.h>
|
||||
#endif // _PS3
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
static LARGE_INTEGER g_PerformanceFrequency;
|
||||
static LARGE_INTEGER g_MSPerformanceFrequency;
|
||||
static LARGE_INTEGER g_ClockStart;
|
||||
static bool s_bTimeInitted;
|
||||
#endif
|
||||
|
||||
// Benchmark mode uses this heavy-handed method
|
||||
static bool g_bBenchmarkMode = false;
|
||||
#ifdef _WIN32
|
||||
static double g_FakeBenchmarkTime = 0;
|
||||
static double g_FakeBenchmarkTimeInc = 1.0 / 66.0;
|
||||
#endif
|
||||
|
||||
static CThreadFastMutex g_LocalTimeMutex;
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
static void InitTime()
|
||||
{
|
||||
if( !s_bTimeInitted )
|
||||
{
|
||||
s_bTimeInitted = true;
|
||||
QueryPerformanceFrequency(&g_PerformanceFrequency);
|
||||
g_MSPerformanceFrequency.QuadPart = g_PerformanceFrequency.QuadPart / 1000;
|
||||
QueryPerformanceCounter(&g_ClockStart);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Plat_IsInBenchmarkMode()
|
||||
{
|
||||
return g_bBenchmarkMode;
|
||||
}
|
||||
|
||||
void Plat_SetBenchmarkMode( bool bBenchmark )
|
||||
{
|
||||
g_bBenchmarkMode = bBenchmark;
|
||||
}
|
||||
|
||||
#ifdef _PS3
|
||||
cell::fios::abstime_t g_fiosLaunchTime = 0;
|
||||
#endif
|
||||
|
||||
|
||||
double Plat_FloatTime()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
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 ) / (double)(g_PerformanceFrequency.QuadPart);
|
||||
|
||||
return fRawSeconds;
|
||||
#else
|
||||
return cell::fios::FIOSAbstimeToMicroseconds( cell::fios::FIOSGetCurrentTime() - g_fiosLaunchTime ) * 1e-6;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
uint32 Plat_MSTime()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
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_MSPerformanceFrequency.QuadPart);
|
||||
#elif defined(_PS3)
|
||||
return (uint32) cell::fios::FIOSAbstimeToMilliseconds( cell::fios::FIOSGetCurrentTime() - g_fiosLaunchTime );
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64 Timer_GetTimeUS()
|
||||
{
|
||||
#ifdef _PS3
|
||||
return cell::fios::FIOSAbstimeToMicroseconds( cell::fios::FIOSGetCurrentTime() - g_fiosLaunchTime );
|
||||
#else
|
||||
return uint64( Plat_FloatTime() * 1000000 );
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64 Plat_GetClockStart()
|
||||
{
|
||||
#if defined( _WIN32 )
|
||||
if ( !s_bTimeInitted )
|
||||
InitTime();
|
||||
|
||||
return g_ClockStart.QuadPart;
|
||||
#elif defined( _PS3 )
|
||||
return g_fiosLaunchTime;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Plat_GetLocalTime( struct tm *pNow )
|
||||
{
|
||||
// We just provide a wrapper on this function so we can protect access to time() everywhere.
|
||||
time_t ltime;
|
||||
time( <ime );
|
||||
|
||||
Plat_ConvertToLocalTime( ltime, pNow );
|
||||
}
|
||||
|
||||
void Plat_ConvertToLocalTime( uint64 nTime, struct tm *pNow )
|
||||
{
|
||||
// Since localtime() returns a global, we need to protect against multiple threads stomping it.
|
||||
g_LocalTimeMutex.Lock();
|
||||
|
||||
time_t ltime = (time_t)nTime;
|
||||
tm *pTime = localtime( <ime );
|
||||
if ( pTime )
|
||||
*pNow = *pTime;
|
||||
else
|
||||
memset( pNow, 0, sizeof( *pNow ) );
|
||||
|
||||
g_LocalTimeMutex.Unlock();
|
||||
}
|
||||
|
||||
void Plat_GetTimeString( struct tm *pTime, char *pOut, int nMaxBytes )
|
||||
{
|
||||
g_LocalTimeMutex.Lock();
|
||||
|
||||
char *pStr = asctime( pTime );
|
||||
strncpy( pOut, pStr, nMaxBytes );
|
||||
pOut[nMaxBytes-1] = 0;
|
||||
|
||||
g_LocalTimeMutex.Unlock();
|
||||
}
|
||||
|
||||
|
||||
void Plat_gmtime( uint64 nTime, struct tm *pTime )
|
||||
{
|
||||
time_t tmtTime = nTime;
|
||||
#ifdef _PS3
|
||||
struct tm * tmp = gmtime( &tmtTime );
|
||||
* pTime = * tmp;
|
||||
#else
|
||||
gmtime_s( pTime, &tmtTime );
|
||||
#endif
|
||||
}
|
||||
|
||||
time_t Plat_timegm( struct tm *timeptr )
|
||||
{
|
||||
#ifndef _GAMECONSOLE
|
||||
return _mkgmtime( timeptr );
|
||||
#else
|
||||
int *pnCrashHereBecauseConsolesDontSupportMkGmTime = 0;
|
||||
*pnCrashHereBecauseConsolesDontSupportMkGmTime = 0;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Plat_GetModuleFilename( char *pOut, int nMaxBytes )
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
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.
|
||||
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 GetCurrentDate( int *pDay, int *pMonth, int *pYear )
|
||||
{
|
||||
struct tm long_time;
|
||||
Plat_GetLocalTime( &long_time );
|
||||
|
||||
*pDay = long_time.tm_mday;
|
||||
*pMonth = long_time.tm_mon + 1;
|
||||
*pYear = long_time.tm_year + 1900;
|
||||
}
|
||||
|
||||
// Wraps the thread-safe versions of asctime. buf must be at least 26 bytes
|
||||
char *Plat_asctime( const struct tm *tm, char *buf, size_t bufsize )
|
||||
{
|
||||
#ifdef _PS3
|
||||
snprintf( buf, bufsize, "%s", asctime(tm) );
|
||||
return buf;
|
||||
#else
|
||||
if ( EINVAL == asctime_s( buf, bufsize, tm ) )
|
||||
return NULL;
|
||||
else
|
||||
return buf;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Wraps the thread-safe versions of ctime. buf must be at least 26 bytes
|
||||
char *Plat_ctime( const time_t *timep, char *buf, size_t bufsize )
|
||||
{
|
||||
#ifdef _PS3
|
||||
snprintf( buf, bufsize, "%s", ctime( timep ) );
|
||||
return buf;
|
||||
#else
|
||||
if ( EINVAL == ctime_s( buf, bufsize, timep ) )
|
||||
return NULL;
|
||||
else
|
||||
return buf;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Wraps the thread-safe versions of gmtime
|
||||
struct tm *Plat_gmtime( const time_t *timep, struct tm *result )
|
||||
{
|
||||
#ifdef _PS3
|
||||
*result = *gmtime( timep );
|
||||
return result;
|
||||
#else
|
||||
if ( EINVAL == gmtime_s( result, timep ) )
|
||||
return NULL;
|
||||
else
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Wraps the thread-safe versions of localtime
|
||||
struct tm *Plat_localtime( const time_t *timep, struct tm *result )
|
||||
{
|
||||
#ifdef _PS3
|
||||
*result = *localtime( timep );
|
||||
return result;
|
||||
#else
|
||||
if ( EINVAL == localtime_s( result, timep ) )
|
||||
return NULL;
|
||||
else
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool vtune( bool resume )
|
||||
{
|
||||
#if IS_WINDOWS_PC
|
||||
static bool bInitialized = false;
|
||||
static void (__cdecl *VTResume)(void) = NULL;
|
||||
static void (__cdecl *VTPause) (void) = NULL;
|
||||
|
||||
// Grab the Pause and Resume function pointers from the VTune DLL the first time through:
|
||||
if( !bInitialized )
|
||||
{
|
||||
bInitialized = true;
|
||||
|
||||
HINSTANCE pVTuneDLL = LoadLibrary( "vtuneapi.dll" );
|
||||
|
||||
if( pVTuneDLL )
|
||||
{
|
||||
VTResume = (void(__cdecl *)())GetProcAddress( pVTuneDLL, "VTResume" );
|
||||
VTPause = (void(__cdecl *)())GetProcAddress( pVTuneDLL, "VTPause" );
|
||||
}
|
||||
}
|
||||
|
||||
// Call the appropriate function, as indicated by the argument:
|
||||
if( resume && VTResume )
|
||||
{
|
||||
VTResume();
|
||||
return true;
|
||||
|
||||
}
|
||||
else if( !resume && VTPause )
|
||||
{
|
||||
VTPause();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Plat_IsInDebugSession()
|
||||
{
|
||||
#if defined( _X360 )
|
||||
return (XBX_IsDebuggerPresent() != 0);
|
||||
#elif defined( _WIN32 )
|
||||
return (IsDebuggerPresent() != 0);
|
||||
#elif defined( _PS3 ) && !defined(_CERT)
|
||||
return snIsDebuggerPresent();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Plat_DebugString( const char * psz )
|
||||
{
|
||||
#ifdef _CERT
|
||||
return; // do nothing!
|
||||
#endif
|
||||
|
||||
#if defined( _X360 )
|
||||
XBX_OutputDebugString( psz );
|
||||
#elif defined( _WIN32 )
|
||||
::OutputDebugStringA( psz );
|
||||
#elif defined(_PS3)
|
||||
printf("%s",psz);
|
||||
#else
|
||||
// do nothing?
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if defined( PLATFORM_WINDOWS_PC )
|
||||
void Plat_MessageBox( const char *pTitle, const char *pMessage )
|
||||
{
|
||||
MessageBox( NULL, pMessage, pTitle, MB_OK );
|
||||
}
|
||||
#endif
|
||||
|
||||
PlatOSVersion_t Plat_GetOSVersion()
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS_PC
|
||||
OSVERSIONINFO info;
|
||||
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
if ( GetVersionEx( &info ) )
|
||||
return (PlatOSVersion_t)info.dwMajorVersion;
|
||||
return PLAT_OS_VERSION_UNKNOWN;
|
||||
#elif defined( PLATFORM_X360 )
|
||||
return PLAT_OS_VERSION_XBOX360;
|
||||
#else
|
||||
return PLAT_OS_VERSION_UNKNOWN;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined( PLATFORM_PS3 )
|
||||
//copied from platform_posix.cpp
|
||||
static char g_CmdLine[ 2048 ] = "";
|
||||
PLATFORM_INTERFACE void Plat_SetCommandLine( const char *cmdLine )
|
||||
{
|
||||
strncpy( g_CmdLine, cmdLine, sizeof(g_CmdLine) );
|
||||
g_CmdLine[ sizeof(g_CmdLine) -1 ] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
PLATFORM_INTERFACE const tchar *Plat_GetCommandLine()
|
||||
{
|
||||
#if defined( _PS3 )
|
||||
#pragma message("Plat_GetCommandLine() not implemented on PS3") // ****
|
||||
return g_CmdLine;
|
||||
#elif defined( TCHAR_IS_WCHAR )
|
||||
return GetCommandLineW();
|
||||
#else
|
||||
return GetCommandLine();
|
||||
#endif
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE const char *Plat_GetCommandLineA()
|
||||
{
|
||||
#if defined( _PS3 )
|
||||
#pragma message("Plat_GetCommandLineA() not implemented on PS3") // ****
|
||||
return g_CmdLine;
|
||||
#else
|
||||
return GetCommandLineA();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Dynamically load a function
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
void *Plat_GetProcAddress( const char *pszModule, const char *pszName )
|
||||
{
|
||||
HMODULE hModule = ::LoadLibrary( pszModule );
|
||||
return ( hModule ) ? ::GetProcAddress( hModule, pszName ) : NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- //
|
||||
// Memory stuff.
|
||||
//
|
||||
// DEPRECATED. Still here to support binary back compatability of tier0.dll
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------------- //
|
||||
#ifndef _X360
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
|
||||
typedef void (*Plat_AllocErrorFn)( unsigned long size );
|
||||
|
||||
void Plat_DefaultAllocErrorFn( unsigned long size )
|
||||
{
|
||||
}
|
||||
|
||||
Plat_AllocErrorFn g_AllocError = Plat_DefaultAllocErrorFn;
|
||||
#endif
|
||||
|
||||
#if !defined( _X360 ) && !defined( _PS3 )
|
||||
|
||||
|
||||
CRITICAL_SECTION g_AllocCS;
|
||||
class CAllocCSInit
|
||||
{
|
||||
public:
|
||||
CAllocCSInit()
|
||||
{
|
||||
InitializeCriticalSection( &g_AllocCS );
|
||||
}
|
||||
} g_AllocCSInit;
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void* Plat_Alloc( unsigned long size )
|
||||
{
|
||||
EnterCriticalSection( &g_AllocCS );
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
void *pRet = MemAlloc_Alloc( size );
|
||||
#else
|
||||
void *pRet = malloc( size );
|
||||
#endif
|
||||
LeaveCriticalSection( &g_AllocCS );
|
||||
if ( pRet )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
g_AllocError( size );
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void* Plat_Realloc( void *ptr, unsigned long size )
|
||||
{
|
||||
EnterCriticalSection( &g_AllocCS );
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
void *pRet = g_pMemAlloc->Realloc( ptr, size );
|
||||
#else
|
||||
void *pRet = realloc( ptr, size );
|
||||
#endif
|
||||
LeaveCriticalSection( &g_AllocCS );
|
||||
if ( pRet )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
g_AllocError( size );
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void Plat_Free( void *ptr )
|
||||
{
|
||||
EnterCriticalSection( &g_AllocCS );
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
g_pMemAlloc->Free( ptr );
|
||||
#else
|
||||
free( ptr );
|
||||
#endif
|
||||
LeaveCriticalSection( &g_AllocCS );
|
||||
}
|
||||
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
PLATFORM_INTERFACE void Plat_SetAllocErrorFn( Plat_AllocErrorFn fn )
|
||||
{
|
||||
g_AllocError = fn;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/memalloc.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/threadtools.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef OSX
|
||||
#include <sys/sysctl.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
|
||||
static bool g_bBenchmarkMode = false;
|
||||
static double g_FakeBenchmarkTime = 0;
|
||||
static double g_FakeBenchmarkTimeInc = 1.0 / 66.0;
|
||||
|
||||
|
||||
bool Plat_IsInBenchmarkMode()
|
||||
{
|
||||
return g_bBenchmarkMode;
|
||||
}
|
||||
|
||||
void Plat_SetBenchmarkMode( bool bBenchmark )
|
||||
{
|
||||
g_bBenchmarkMode = bBenchmark;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef OSX
|
||||
|
||||
static uint64 start_time = 0;
|
||||
static mach_timebase_info_data_t sTimebaseInfo;
|
||||
static double conversion = 0.0;
|
||||
|
||||
void InitTime()
|
||||
{
|
||||
start_time = mach_absolute_time();
|
||||
mach_timebase_info(&sTimebaseInfo);
|
||||
conversion = 1e-9 * (double) sTimebaseInfo.numer / (double) sTimebaseInfo.denom;
|
||||
}
|
||||
|
||||
uint64 Plat_GetClockStart()
|
||||
{
|
||||
if ( !start_time )
|
||||
{
|
||||
InitTime();
|
||||
}
|
||||
|
||||
return start_time * conversion;
|
||||
}
|
||||
|
||||
double Plat_FloatTime()
|
||||
{
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return g_FakeBenchmarkTime;
|
||||
}
|
||||
|
||||
if ( !start_time )
|
||||
{
|
||||
InitTime();
|
||||
}
|
||||
|
||||
uint64 now = mach_absolute_time();
|
||||
|
||||
return ( now - start_time ) * conversion;
|
||||
}
|
||||
#else
|
||||
|
||||
static int secbase = 0;
|
||||
|
||||
void InitTime( struct timeval &tp )
|
||||
{
|
||||
secbase = tp.tv_sec;
|
||||
}
|
||||
|
||||
uint64 Plat_GetClockStart()
|
||||
{
|
||||
if ( !secbase )
|
||||
{
|
||||
struct timeval tp;
|
||||
gettimeofday( &tp, NULL );
|
||||
InitTime( tp );
|
||||
}
|
||||
|
||||
return secbase;
|
||||
}
|
||||
|
||||
|
||||
double Plat_FloatTime()
|
||||
{
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return g_FakeBenchmarkTime;
|
||||
}
|
||||
|
||||
struct timeval tp;
|
||||
|
||||
gettimeofday( &tp, NULL );
|
||||
|
||||
if ( !secbase )
|
||||
{
|
||||
InitTime( tp );
|
||||
return ( tp.tv_usec / 1000000.0 );
|
||||
}
|
||||
|
||||
return (( tp.tv_sec - secbase ) + tp.tv_usec / 1000000.0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
uint32 Plat_MSTime()
|
||||
{
|
||||
if ( g_bBenchmarkMode )
|
||||
{
|
||||
g_FakeBenchmarkTime += g_FakeBenchmarkTimeInc;
|
||||
return (unsigned long)(g_FakeBenchmarkTime * 1000.0);
|
||||
}
|
||||
|
||||
struct timeval tp;
|
||||
static int secbase = 0;
|
||||
|
||||
gettimeofday( &tp, NULL );
|
||||
|
||||
if ( !secbase )
|
||||
{
|
||||
secbase = tp.tv_sec;
|
||||
return ( tp.tv_usec / 1000.0 );
|
||||
}
|
||||
|
||||
return (unsigned long)(( tp.tv_sec - secbase )*1000.0 + tp.tv_usec / 1000.0 );
|
||||
|
||||
}
|
||||
|
||||
// Wraps the thread-safe versions of asctime. buf must be at least 26 bytes
|
||||
char *Plat_asctime( const struct tm *tm, char *buf, size_t bufsize )
|
||||
{
|
||||
return asctime_r( tm, buf );
|
||||
}
|
||||
|
||||
// Wraps the thread-safe versions of ctime. buf must be at least 26 bytes
|
||||
char *Plat_ctime( const time_t *timep, char *buf, size_t bufsize )
|
||||
{
|
||||
return ctime_r( timep, buf );
|
||||
}
|
||||
|
||||
// Wraps the thread-safe versions of gmtime
|
||||
struct tm *Plat_gmtime( const time_t *timep, struct tm *result )
|
||||
{
|
||||
return gmtime_r( timep, result );
|
||||
}
|
||||
|
||||
time_t Plat_timegm( struct tm *timeptr )
|
||||
{
|
||||
return timegm( timeptr );
|
||||
}
|
||||
|
||||
// Wraps the thread-safe versions of localtime
|
||||
struct tm *Plat_localtime( const time_t *timep, struct tm *result )
|
||||
{
|
||||
return localtime_r( timep, result );
|
||||
}
|
||||
|
||||
bool vtune( bool resume )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- //
|
||||
// Memory stuff.
|
||||
// -------------------------------------------------------------------------------------------------- //
|
||||
|
||||
PLATFORM_INTERFACE void Plat_DefaultAllocErrorFn( unsigned long size )
|
||||
{
|
||||
}
|
||||
|
||||
typedef void (*Plat_AllocErrorFn)( unsigned long size );
|
||||
Plat_AllocErrorFn g_AllocError = Plat_DefaultAllocErrorFn;
|
||||
|
||||
PLATFORM_INTERFACE void* Plat_Alloc( unsigned long size )
|
||||
{
|
||||
void *pRet = g_pMemAlloc->Alloc( size );
|
||||
if ( pRet )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_AllocError( size );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void* Plat_Realloc( void *ptr, unsigned long size )
|
||||
{
|
||||
void *pRet = g_pMemAlloc->Realloc( ptr, size );
|
||||
if ( pRet )
|
||||
{
|
||||
return pRet;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_AllocError( size );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void Plat_Free( void *ptr )
|
||||
{
|
||||
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
||||
g_pMemAlloc->Free( ptr );
|
||||
#else
|
||||
free( ptr );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE void Plat_SetAllocErrorFn( Plat_AllocErrorFn fn )
|
||||
{
|
||||
g_AllocError = fn;
|
||||
}
|
||||
|
||||
static char g_CmdLine[ 2048 ];
|
||||
PLATFORM_INTERFACE void Plat_SetCommandLine( const char *cmdLine )
|
||||
{
|
||||
strncpy( g_CmdLine, cmdLine, sizeof(g_CmdLine) );
|
||||
g_CmdLine[ sizeof(g_CmdLine) -1 ] = 0;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE void Plat_SetCommandLineArgs( char **argv, int argc )
|
||||
{
|
||||
g_CmdLine[0] = 0;
|
||||
for ( int i = 0; i < argc; i++ )
|
||||
{
|
||||
strncat( g_CmdLine, argv[i], sizeof(g_CmdLine) - strlen(g_CmdLine) );
|
||||
}
|
||||
|
||||
g_CmdLine[ sizeof(g_CmdLine) -1 ] = 0;
|
||||
}
|
||||
|
||||
|
||||
PLATFORM_INTERFACE const tchar *Plat_GetCommandLine()
|
||||
{
|
||||
return g_CmdLine;
|
||||
}
|
||||
|
||||
PLATFORM_INTERFACE bool Is64BitOS()
|
||||
{
|
||||
#if defined OSX
|
||||
return true;
|
||||
#elif defined LINUX
|
||||
FILE *pp = popen( "uname -m", "r" );
|
||||
if ( pp != NULL )
|
||||
{
|
||||
char rgchArchString[256];
|
||||
fgets( rgchArchString, sizeof( rgchArchString ), pp );
|
||||
pclose( pp );
|
||||
if ( !strncasecmp( rgchArchString, "x86_64", strlen( "x86_64" ) ) )
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
Assert( !"implement Is64BitOS" );
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Plat_IsInDebugSession()
|
||||
{
|
||||
#if defined(OSX)
|
||||
int mib[4];
|
||||
struct kinfo_proc info;
|
||||
size_t size;
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
size = sizeof(info);
|
||||
info.kp_proc.p_flag = 0;
|
||||
sysctl(mib,4,&info,&size,NULL,0);
|
||||
bool result = ((info.kp_proc.p_flag & P_TRACED) == P_TRACED);
|
||||
return result;
|
||||
#elif defined(LINUX)
|
||||
char s[256];
|
||||
snprintf(s, 256, "/proc/%d/cmdline", getppid());
|
||||
FILE * fp = fopen(s, "r");
|
||||
if (fp != NULL)
|
||||
{
|
||||
fread(s, 256, 1, fp);
|
||||
fclose(fp);
|
||||
return (0 == strncmp(s, "gdb", 3));
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Plat_ExitProcess( int nCode )
|
||||
{
|
||||
_exit( nCode );
|
||||
}
|
||||
|
||||
static int s_nWatchDogTimerTimeScale = 0;
|
||||
static bool s_bInittedWD = false;
|
||||
|
||||
|
||||
static void InitWatchDogTimer( void )
|
||||
{
|
||||
if( !strstr( g_CmdLine, "-nowatchdog" ) )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
s_nWatchDogTimerTimeScale = 10; // debug is slow
|
||||
#else
|
||||
s_nWatchDogTimerTimeScale = 1;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// watchdog timer support
|
||||
void BeginWatchdogTimer( int nSecs )
|
||||
{
|
||||
if (! s_bInittedWD )
|
||||
{
|
||||
s_bInittedWD = true;
|
||||
InitWatchDogTimer();
|
||||
}
|
||||
nSecs *= s_nWatchDogTimerTimeScale;
|
||||
nSecs = MIN( nSecs, 5 * 60 ); // no more than 5 minutes no matter what
|
||||
if ( nSecs )
|
||||
alarm( nSecs );
|
||||
}
|
||||
|
||||
void EndWatchdogTimer( void )
|
||||
{
|
||||
alarm( 0 );
|
||||
}
|
||||
|
||||
static CThreadMutex g_LocalTimeMutex;
|
||||
|
||||
|
||||
void Plat_GetLocalTime( struct tm *pNow )
|
||||
{
|
||||
// We just provide a wrapper on this function so we can protect access to time() everywhere.
|
||||
time_t ltime;
|
||||
time( <ime );
|
||||
|
||||
Plat_ConvertToLocalTime( ltime, pNow );
|
||||
}
|
||||
|
||||
void Plat_ConvertToLocalTime( uint64 nTime, struct tm *pNow )
|
||||
{
|
||||
// Since localtime() returns a global, we need to protect against multiple threads stomping it.
|
||||
g_LocalTimeMutex.Lock();
|
||||
|
||||
time_t ltime = (time_t)nTime;
|
||||
tm *pTime = localtime( <ime );
|
||||
if ( pTime )
|
||||
*pNow = *pTime;
|
||||
else
|
||||
memset( pNow, 0, sizeof( *pNow ) );
|
||||
|
||||
g_LocalTimeMutex.Unlock();
|
||||
}
|
||||
|
||||
void Plat_GetTimeString( struct tm *pTime, char *pOut, int nMaxBytes )
|
||||
{
|
||||
g_LocalTimeMutex.Lock();
|
||||
|
||||
char *pStr = asctime( pTime );
|
||||
strncpy( pOut, pStr, nMaxBytes );
|
||||
pOut[nMaxBytes-1] = 0;
|
||||
|
||||
g_LocalTimeMutex.Unlock();
|
||||
}
|
||||
|
||||
|
||||
void Plat_gmtime( uint64 nTime, struct tm *pTime )
|
||||
{
|
||||
time_t tmtTime = nTime;
|
||||
struct tm * tmp = gmtime( &tmtTime );
|
||||
* pTime = * tmp;
|
||||
}
|
||||
|
||||
#ifdef LINUX
|
||||
size_t ApproximateProcessMemoryUsage( void )
|
||||
{
|
||||
int nRet = 0;
|
||||
FILE *pFile = fopen( "/proc/self/statm", "r" );
|
||||
if ( pFile )
|
||||
{
|
||||
int nSize, nTotalProgramSize, nResident, nResidentSetSize, nShare, nSharedPagesTotal, nDummy0;
|
||||
if ( fscanf( pFile, "%d %d %d %d %d %d %d", &nSize, &nTotalProgramSize, &nResident, &nResidentSetSize, &nShare, &nSharedPagesTotal, &nDummy0 ) )
|
||||
{
|
||||
nRet = 4096 * nSize;
|
||||
}
|
||||
fclose( pFile );
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
#else
|
||||
|
||||
size_t ApproximateProcessMemoryUsage( void )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+162
@@ -0,0 +1,162 @@
|
||||
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#define WINDOWS_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#pragma warning( disable : 4530 ) // warning: exception handler -GX option
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/vprof.h"
|
||||
#include "tier0/pmelib.h"
|
||||
#include "tier0/l2cache.h"
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Initialization
|
||||
//-----------------------------------------------------------------------------
|
||||
void InitPME( void )
|
||||
{
|
||||
bool bInit = false;
|
||||
|
||||
PME *pPME = PME::Instance();
|
||||
if ( pPME )
|
||||
{
|
||||
if ( pPME->GetVendor() != INTEL )
|
||||
return;
|
||||
|
||||
if ( pPME->GetProcessorFamily() != PENTIUM4_FAMILY )
|
||||
return;
|
||||
|
||||
pPME->SetProcessPriority( ProcessPriorityHigh );
|
||||
|
||||
bInit = true;
|
||||
|
||||
DevMsg( 1, _T("PME Initialized.\n") );
|
||||
}
|
||||
else
|
||||
{
|
||||
DevMsg( 1, _T("PME Uninitialized.\n") );
|
||||
}
|
||||
|
||||
#ifdef VPROF_ENABLED
|
||||
g_VProfCurrentProfile.PMEInitialized( bInit );
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Shutdown
|
||||
//-----------------------------------------------------------------------------
|
||||
void ShutdownPME( void )
|
||||
{
|
||||
PME *pPME = PME::Instance();
|
||||
if ( pPME )
|
||||
{
|
||||
pPME->SetProcessPriority( ProcessPriorityNormal );
|
||||
}
|
||||
|
||||
#ifdef VPROF_ENABLED
|
||||
g_VProfCurrentProfile.PMEInitialized( false );
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// CL2Cache Code.
|
||||
//
|
||||
|
||||
static int s_nCreateCount = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CL2Cache::CL2Cache()
|
||||
{
|
||||
m_nID = s_nCreateCount++;
|
||||
m_pL2CacheEvent = new P4Event_BSQ_cache_reference;
|
||||
m_iL2CacheMissCount = 0;
|
||||
m_i64Start = 0;
|
||||
m_i64End = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CL2Cache::~CL2Cache()
|
||||
{
|
||||
if ( m_pL2CacheEvent )
|
||||
{
|
||||
delete m_pL2CacheEvent;
|
||||
m_pL2CacheEvent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::Start( void )
|
||||
{
|
||||
if ( m_pL2CacheEvent )
|
||||
{
|
||||
// Set this up to check for L2 cache misses.
|
||||
m_pL2CacheEvent->eventMask->RD_2ndL_MISS = 1;
|
||||
|
||||
// Set the event mask and set the capture mode.
|
||||
// m_pL2CacheEvent->SetCaptureMode( USR_Only );
|
||||
m_pL2CacheEvent->SetCaptureMode( OS_and_USR );
|
||||
|
||||
// That's it, now sw capture events
|
||||
m_pL2CacheEvent->StopCounter();
|
||||
m_pL2CacheEvent->ClearCounter();
|
||||
|
||||
m_pL2CacheEvent->StartCounter();
|
||||
m_i64Start = m_pL2CacheEvent->ReadCounter();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::End( void )
|
||||
{
|
||||
if ( m_pL2CacheEvent )
|
||||
{
|
||||
// Stop the counter and find the delta.
|
||||
m_i64End = m_pL2CacheEvent->ReadCounter();
|
||||
int64 i64Delta = m_i64End - m_i64Start;
|
||||
m_pL2CacheEvent->StopCounter();
|
||||
|
||||
// Save the delta for later query.
|
||||
m_iL2CacheMissCount = ( int )i64Delta;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning( default : 4530 )
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Ensure that all of our internal structures are consistent, and
|
||||
// account for all memory that we've allocated.
|
||||
// Input: validator - Our global validator object
|
||||
// pchName - Our name (typically a member var in our container)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::Validate( CValidator &validator, tchar *pchName )
|
||||
{
|
||||
validator.Push( _T("CL2Cache"), this, pchName );
|
||||
|
||||
validator.ClaimMemory( m_pL2CacheEvent );
|
||||
|
||||
validator.Pop( );
|
||||
}
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "tier0/vprof.h"
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Initialization
|
||||
//-----------------------------------------------------------------------------
|
||||
void InitPME( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Shutdown
|
||||
//-----------------------------------------------------------------------------
|
||||
void ShutdownPME( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CL2Cache::CL2Cache()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
CL2Cache::~CL2Cache()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::Start( void )
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void CL2Cache::End( void )
|
||||
{
|
||||
}
|
||||
Vendored
+665
@@ -0,0 +1,665 @@
|
||||
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#pragma warning( disable : 4530 ) // warning: exception handler -GX option
|
||||
|
||||
#include "tier0/valve_off.h"
|
||||
#include "tier0/pmelib.h"
|
||||
#if _MSC_VER >=1300
|
||||
#else
|
||||
#include "winioctl.h"
|
||||
#endif
|
||||
#include "tier0/valve_on.h"
|
||||
|
||||
#include "tier0/ioctlcodes.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
PME* PME::_singleton = 0;
|
||||
|
||||
// Single interface.
|
||||
PME* PME::Instance()
|
||||
{
|
||||
if (_singleton == 0)
|
||||
{
|
||||
_singleton = new PME;
|
||||
}
|
||||
return _singleton;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Open the device driver and detect the processor
|
||||
//---------------------------------------------------------------------------
|
||||
HRESULT PME::Init( void )
|
||||
{
|
||||
OSVERSIONINFO OS;
|
||||
|
||||
if ( bDriverOpen )
|
||||
return E_DRIVER_ALREADY_OPEN;
|
||||
|
||||
switch( vendor )
|
||||
{
|
||||
case INTEL:
|
||||
case AMD:
|
||||
break;
|
||||
default:
|
||||
bDriverOpen = FALSE; // not an Intel or Athlon processor so return false
|
||||
return E_UNKNOWN_CPU_VENDOR;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Get the operating system version
|
||||
//-----------------------------------------------------------------------
|
||||
OS.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
|
||||
GetVersionEx( &OS );
|
||||
|
||||
if ( OS.dwPlatformId == VER_PLATFORM_WIN32_NT )
|
||||
{
|
||||
hFile = CreateFile( // WINDOWS NT
|
||||
"\\\\.\\GDPERF",
|
||||
GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
hFile = CreateFile( // WINDOWS 95
|
||||
"\\\\.\\GDPERF.VXD",
|
||||
GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE )
|
||||
return E_CANT_OPEN_DRIVER;
|
||||
|
||||
|
||||
bDriverOpen = TRUE;
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// We have successfully opened the device driver, get the family
|
||||
// of the processor.
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// We need to write to counter 0 on the pro family to enable both
|
||||
// of the performance counters. We write to both so they start in a
|
||||
// known state. For the pentium this is not necessary.
|
||||
//-------------------------------------------------------------------
|
||||
if (vendor == INTEL && version.Family == PENTIUMPRO_FAMILY)
|
||||
{
|
||||
SelectP5P6PerformanceEvent(P6_CLOCK, 0, TRUE, TRUE);
|
||||
SelectP5P6PerformanceEvent(P6_CLOCK, 1, TRUE, TRUE);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Close the device driver
|
||||
//---------------------------------------------------------------------------
|
||||
HRESULT PME::Close(void)
|
||||
{
|
||||
if (bDriverOpen == false) // driver is not going
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
bDriverOpen = false;
|
||||
|
||||
if (hFile) // if we have no driver handle, return FALSE
|
||||
{
|
||||
HRESULT hr = CloseHandle(hFile);
|
||||
|
||||
hFile = NULL;
|
||||
return hr;
|
||||
}
|
||||
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)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD dw_ret_len;
|
||||
|
||||
if (bDriverOpen == false) // driver is not going
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
hr = 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
|
||||
);
|
||||
|
||||
if (hr == S_OK && dw_ret_len != sizeof(int64))
|
||||
hr = E_BAD_DATA;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT PME::ReadMSR(uint32 dw_reg, uint64 * pi64_value)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD dw_ret_len;
|
||||
|
||||
if (bDriverOpen == false) // driver is not going
|
||||
return E_DRIVER_NOT_OPEN;
|
||||
|
||||
hr = 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
|
||||
);
|
||||
|
||||
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)
|
||||
{
|
||||
HRESULT hr;
|
||||
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;
|
||||
|
||||
hr = 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.
|
||||
);
|
||||
|
||||
if (hr == S_OK && dw_ret_len != 0)
|
||||
hr = E_BAD_DATA;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
HRESULT PME::WriteMSR(uint32 dw_reg, const uint64 & i64_value)
|
||||
{
|
||||
HRESULT hr;
|
||||
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;
|
||||
|
||||
hr = 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
|
||||
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
|
||||
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Developer Studio generated include file.
|
||||
// Used by assert_dialog.rc
|
||||
//
|
||||
#define IDD_ASSERT_DIALOG 101
|
||||
#define IDC_FILENAME_CONTROL 1000
|
||||
#define IDC_LINE_CONTROL 1001
|
||||
#define IDC_IGNORE_FILE 1002
|
||||
#define IDC_IGNORE_NEARBY 1003
|
||||
#define IDC_IGNORE_NUMLINES 1004
|
||||
#define IDC_IGNORE_THIS 1005
|
||||
#define IDC_BREAK 1006
|
||||
#define IDC_IGNORE_ALL 1008
|
||||
#define IDC_IGNORE_ALWAYS 1009
|
||||
#define IDC_IGNORE_NUMTIMES 1010
|
||||
#define IDC_ASSERT_MSG_CTRL 1011
|
||||
#define IDC_NOID -1
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 103
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1005
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
Vendored
+1682
File diff suppressed because it is too large
Load Diff
Vendored
+3096
File diff suppressed because it is too large
Load Diff
+53
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
//========= Copyright © Valve Corporation, All rights reserved. ============//
|
||||
|
||||
extern "C" int V_tier0_stricmp(const char *s1, const char *s2 );
|
||||
Vendored
+124
@@ -0,0 +1,124 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "vstdlib/pch_vstdlib.h"
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Initializer
|
||||
// Input: pchType - Type of the object we represent.
|
||||
// WARNING: pchType must be a static (since we keep a copy of it around for a while)
|
||||
// pvObj - Pointer to the object we represent
|
||||
// pchName - Name of the individual object we represent
|
||||
// WARNING: pchName must be a static (since we keep a copy of it around for a while)
|
||||
// pValObjectparent- Our parent object (ie, the object that our object is a member of)
|
||||
// pValObjectPrev - Object that precedes us in the linked list (we're
|
||||
// always added to the end)
|
||||
//-----------------------------------------------------------------------------
|
||||
void CValObject::Init( tchar *pchType, void *pvObj, tchar *pchName,
|
||||
CValObject *pValObjectParent, CValObject *pValObjectPrev )
|
||||
{
|
||||
m_nUser = 0;
|
||||
|
||||
// Initialize pchType:
|
||||
if ( NULL != pchType )
|
||||
{
|
||||
V_strncpy( m_rgchType, pchType, (int) ( sizeof(m_rgchType) / sizeof(*m_rgchType) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rgchType[0] = '\0';
|
||||
}
|
||||
|
||||
m_pvObj = pvObj;
|
||||
|
||||
// Initialize pchName:
|
||||
if ( NULL != pchName )
|
||||
{
|
||||
V_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
|
||||
Vendored
+1752
File diff suppressed because it is too large
Load Diff
+161
@@ -0,0 +1,161 @@
|
||||
//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ======
|
||||
//
|
||||
// Purpose: Win32 Console API helpers
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "pch_tier0.h"
|
||||
#include "win32consoleio.h"
|
||||
|
||||
#if defined( _WIN32 )
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#endif // defined( _WIN32 )
|
||||
|
||||
// NOTE: This has to be the last file included!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Attach a console to a Win32 GUI process and setup stdin, stdout & stderr
|
||||
// along with the std::iostream (cout, cin, cerr) equivalents to read and
|
||||
// write to and from that console
|
||||
//
|
||||
// 1. Ensure the handle associated with stdio is FILE_TYPE_UNKNOWN
|
||||
// if it's anything else just return false. This supports cygwin
|
||||
// style command shells like rxvt which setup pipes to processes
|
||||
// they spawn
|
||||
//
|
||||
// 2. See if the Win32 function call AttachConsole exists in kernel32
|
||||
// It's a Windows 2000 and above call. If it does, call it and see
|
||||
// if it succeeds in attaching to the console of the parent process.
|
||||
// If that succeeds, return false (for no new console allocated).
|
||||
// This supports someone typing the command from a normal windows
|
||||
// command window and having the output go to the parent window.
|
||||
// It's a little funny because a GUI app detaches so the command
|
||||
// prompt gets intermingled with output from this process
|
||||
//
|
||||
// 3. If things get to here call AllocConsole which will pop open
|
||||
// a new window and allow output to go to that window. The
|
||||
// window will disappear when the process exists so if it's used
|
||||
// for something like a help message then do something like getchar()
|
||||
// from stdin to wait for a keypress. if AllocConsole is called
|
||||
// true is returned.
|
||||
//
|
||||
// Return: true if AllocConsole() was used to pop open a new windows console
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
bool SetupWin32ConsoleIO()
|
||||
{
|
||||
#if defined( _WIN32 )
|
||||
// Only useful on Windows platforms
|
||||
|
||||
bool newConsole( false );
|
||||
|
||||
if ( GetFileType( GetStdHandle( STD_OUTPUT_HANDLE ) ) == FILE_TYPE_UNKNOWN )
|
||||
{
|
||||
|
||||
HINSTANCE hInst = ::LoadLibrary( "kernel32.dll" );
|
||||
typedef BOOL ( WINAPI * pAttachConsole_t )( DWORD );
|
||||
pAttachConsole_t pAttachConsole( ( BOOL ( _stdcall * )( DWORD ) )GetProcAddress( hInst, "AttachConsole" ) );
|
||||
|
||||
if ( !( pAttachConsole && (*pAttachConsole)( ( DWORD ) - 1 ) ) )
|
||||
{
|
||||
newConsole = true;
|
||||
AllocConsole();
|
||||
}
|
||||
|
||||
*stdout = *_fdopen( _open_osfhandle( reinterpret_cast< intp >( GetStdHandle( STD_OUTPUT_HANDLE ) ), _O_TEXT ), "w" );
|
||||
setvbuf( stdout, NULL, _IONBF, 0 );
|
||||
|
||||
*stdin = *_fdopen( _open_osfhandle( reinterpret_cast< intp >( GetStdHandle( STD_INPUT_HANDLE ) ), _O_TEXT ), "r" );
|
||||
setvbuf( stdin, NULL, _IONBF, 0 );
|
||||
|
||||
*stderr = *_fdopen( _open_osfhandle( reinterpret_cast< intp >( GetStdHandle( STD_ERROR_HANDLE ) ), _O_TEXT ), "w" );
|
||||
setvbuf( stdout, NULL, _IONBF, 0 );
|
||||
|
||||
std::ios_base::sync_with_stdio();
|
||||
}
|
||||
|
||||
return newConsole;
|
||||
|
||||
#else // defined( _WIN32 )
|
||||
|
||||
return false;
|
||||
|
||||
#endif // defined( _WIN32 )
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Win32 Console Color API Helpers, originally from cmdlib.
|
||||
// Retrieves the current console color attributes.
|
||||
//-----------------------------------------------------------------------------
|
||||
void InitWin32ConsoleColorContext( Win32ConsoleColorContext_t *pContext )
|
||||
{
|
||||
#if PLATFORM_WINDOWS_PC
|
||||
// Get the old background attributes.
|
||||
CONSOLE_SCREEN_BUFFER_INFO oldInfo;
|
||||
GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo );
|
||||
pContext->m_InitialColor = pContext->m_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
|
||||
pContext->m_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY);
|
||||
|
||||
pContext->m_BadColor = 0;
|
||||
if (pContext->m_BackgroundFlags & BACKGROUND_RED)
|
||||
pContext->m_BadColor |= FOREGROUND_RED;
|
||||
if (pContext->m_BackgroundFlags & BACKGROUND_GREEN)
|
||||
pContext->m_BadColor |= FOREGROUND_GREEN;
|
||||
if (pContext->m_BackgroundFlags & BACKGROUND_BLUE)
|
||||
pContext->m_BadColor |= FOREGROUND_BLUE;
|
||||
if (pContext->m_BackgroundFlags & BACKGROUND_INTENSITY)
|
||||
pContext->m_BadColor |= FOREGROUND_INTENSITY;
|
||||
#else
|
||||
pContext->m_InitialColor = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sets the active console foreground color. This function is smart enough to
|
||||
// avoid setting the color to something that would be unreadable given
|
||||
// the user's potentially customized background color. It leaves the
|
||||
// background color unchanged.
|
||||
// Returns: The console's previous foreground color.
|
||||
//-----------------------------------------------------------------------------
|
||||
uint16 SetWin32ConsoleColor( Win32ConsoleColorContext_t *pContext, int nRed, int nGreen, int nBlue, int nIntensity )
|
||||
{
|
||||
#if PLATFORM_WINDOWS_PC
|
||||
uint16 ret = pContext->m_LastColor;
|
||||
pContext->m_LastColor = 0;
|
||||
if ( nRed ) pContext->m_LastColor |= FOREGROUND_RED;
|
||||
if ( nGreen ) pContext->m_LastColor |= FOREGROUND_GREEN;
|
||||
if ( nBlue ) pContext->m_LastColor |= FOREGROUND_BLUE;
|
||||
if ( nIntensity ) pContext->m_LastColor |= FOREGROUND_INTENSITY;
|
||||
|
||||
// Just use the initial color if there's a match...
|
||||
if ( pContext->m_LastColor == pContext->m_BadColor )
|
||||
pContext->m_LastColor = pContext->m_InitialColor;
|
||||
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), pContext->m_LastColor | pContext->m_BackgroundFlags );
|
||||
return ret;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Restore's the active foreground console color, without distributing the current
|
||||
// background color.
|
||||
//-----------------------------------------------------------------------------
|
||||
void RestoreWin32ConsoleColor( Win32ConsoleColorContext_t *pContext, uint16 prevColor )
|
||||
{
|
||||
#if PLATFORM_WINDOWS_PC
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), prevColor | pContext->m_BackgroundFlags );
|
||||
pContext->m_LastColor = prevColor;
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user