This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions
+573
View File
@@ -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;
}
+676
View File
@@ -0,0 +1,676 @@
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $Workfile: $
// $NoKeywords: $
//===========================================================================//
#include "pch_tier0.h"
#include "tier0/icommandline.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "tier0/dbg.h"
#include "tier0_strtools.h"
#include "tier1/strtools.h" // this is included for the definition of V_isspace()
#ifdef PLATFORM_POSIX
#include <limits.h>
#define _MAX_PATH PATH_MAX
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static const int MAX_PARAMETER_LEN = 128;
//-----------------------------------------------------------------------------
// Purpose: Implements ICommandLine
//-----------------------------------------------------------------------------
class CCommandLine : public ICommandLine
{
public:
// Construction
CCommandLine( void );
virtual ~CCommandLine( void );
// Implements ICommandLine
virtual void CreateCmdLine( const char *commandline );
virtual void CreateCmdLine( int argc, char **argv );
virtual const char *GetCmdLine( void ) const;
virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const;
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] );
}
+697
View File
@@ -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;
}
+143
View File
@@ -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;
}
+1018
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
//-------------------------------------------------------------------------------------
// CpuTopology.h
//
// CpuToplogy class declaration.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-------------------------------------------------------------------------------------
#pragma once
#ifndef CPU_TOPOLOGY_H
#define CPU_TOPOLOGY_H
#include "winlite.h"
class ICpuTopology;
//---------------------------------------------------------------------------------
// Name: CpuToplogy
// Desc: This class constructs a supported cpu topology implementation object on
// initialization and forwards calls to it. This is the Abstraction class
// in the traditional Bridge Pattern.
//---------------------------------------------------------------------------------
class CpuTopology
{
public:
CpuTopology( BOOL bForceCpuid = FALSE );
~CpuTopology();
BOOL IsDefaultImpl() const;
DWORD NumberOfProcessCores() const;
DWORD NumberOfSystemCores() const;
DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const;
void ForceCpuid( BOOL bForce );
private:
void Destroy_();
ICpuTopology* m_pImpl;
};
#endif // CPU_TOPOLOGY_H
+627
View File
@@ -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
+377
View File
@@ -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
+23
View File
@@ -0,0 +1,23 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include <stdio.h>
#include "tier0/fasttimer.h"
// NOTE: This has to be the last file included!
//#include "tier0/memdbgon.h"
uint64 g_ClockSpeed; // Clocks/sec
unsigned long g_dwClockSpeed;
double g_ClockSpeedMicrosecondsMultiplier;
double g_ClockSpeedMillisecondsMultiplier;
double g_ClockSpeedSecondsMultiplier;
// Constructor init the clock speed.
CClockSpeedInit g_ClockSpeedInit;
+698
View File
@@ -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 );
}
+79
View File
@@ -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
+186
View File
@@ -0,0 +1,186 @@
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "tier0/platform.h"
#include "tier0/icommandline.h"
#include "tier0/dbg.h"
#include "mem_helpers.h"
#include <string.h>
//#include <malloc.h>
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
// Needed for debugging
const char *g_pszModule = "tier0";
bool g_bInitMemory = true;
#if defined(PLATFORM_POSIX) || defined( PLATFORM_PS3)
void DoApplyMemoryInitializations( void *pMem, size_t nSize )
{
}
size_t CalcHeapUsed()
{
return 0;
}
#else
unsigned long g_dwFeeFee = 0xffeeffee;
// Generated by Mathematica.
unsigned char g_RandomValues[256] = {
95, 126, 220, 71, 92, 179, 95, 219, 111, 150, 38, 155, 181, 62, 40, 231, 238,
54, 47, 55, 186, 204, 64, 70, 118, 94, 107, 251, 199, 140, 67, 87, 86, 127,
210, 41, 21, 90, 208, 24, 167, 204, 32, 254, 38, 51, 9, 11, 38, 33, 188, 104,
0, 75, 119, 24, 122, 203, 24, 164, 250, 224, 241, 182, 213, 201, 173, 67,
200, 255, 244, 227, 46, 219, 26, 149, 218, 132, 120, 154, 227, 244, 106, 198,
109, 87, 150, 40, 16, 99, 169, 193, 100, 156, 78, 171, 246, 47, 84, 119, 10,
52, 207, 171, 230, 90, 90, 127, 180, 153, 68, 140, 62, 14, 87, 57, 208, 154,
116, 29, 131, 177, 224, 187, 51, 148, 142, 245, 152, 230, 184, 117, 91, 146,
235, 153, 35, 104, 187, 177, 215, 131, 17, 49, 211, 244, 60, 152, 103, 248,
51, 224, 237, 240, 51, 30, 10, 233, 253, 106, 252, 73, 134, 136, 178, 86,
228, 107, 77, 255, 85, 242, 204, 119, 102, 53, 209, 35, 123, 32, 252, 210,
43, 12, 136, 167, 155, 210, 71, 254, 178, 172, 3, 230, 93, 208, 196, 68, 235,
16, 106, 189, 201, 177, 85, 78, 206, 187, 48, 68, 64, 190, 117, 236, 49, 174,
105, 63, 207, 70, 170, 93, 6, 110, 52, 111, 169, 92, 247, 86, 10, 174, 207,
240, 104, 209, 81, 177, 123, 189, 175, 212, 101, 219, 114, 243, 44, 91, 51,
139, 91, 57, 120, 41, 98, 119 };
unsigned long g_iCurRandomValueOffset = 0;
void InitializeToFeeFee( void *pMem, size_t nSize )
{
unsigned long *pCurDWord = (unsigned long*)pMem;
size_t nDWords = nSize >> 2;
while ( nDWords )
{
*pCurDWord = 0xffeeffee;
++pCurDWord;
--nDWords;
}
unsigned char *pCurChar = (unsigned char*)pCurDWord;
size_t nBytes = nSize & 3;
size_t iOffset = 0;
while ( nBytes )
{
*pCurChar = ((unsigned char*)&g_dwFeeFee)[iOffset];
++iOffset;
--nBytes;
++pCurChar;
}
}
void InitializeToRandom( void *pMem, size_t nSize )
{
unsigned char *pOut = (unsigned char *)pMem;
for ( size_t i=0; i < nSize; i++ )
{
pOut[i] = g_RandomValues[(g_iCurRandomValueOffset & 255)];
++g_iCurRandomValueOffset;
}
}
void DoApplyMemoryInitializations( void *pMem, size_t nSize )
{
if ( !pMem )
return;
// If they passed -noinitmemory on the command line, don't do anything here.
Assert( g_bInitMemory );
// First time we get in here, remember all the settings.
static bool bDebuggerPresent = Plat_IsInDebugSession();
static bool bCheckedCommandLine = false;
static bool bRandomizeMemory = false;
if ( !bCheckedCommandLine )
{
bCheckedCommandLine = true;
//APS
char *pStr = (char*)Plat_GetCommandLineA();
if ( pStr )
{
char tempStr[512];
strncpy( tempStr, pStr, sizeof( tempStr ) - 1 );
tempStr[ sizeof( tempStr ) - 1 ] = 0;
_strupr( tempStr );
if ( strstr( tempStr, "-RANDOMIZEMEMORY" ) )
bRandomizeMemory = true;
if ( strstr( tempStr, "-NOINITMEMORY" ) )
g_bInitMemory = false;
}
}
if ( bRandomizeMemory )
{
// They asked for it.. randomize all the memory.
InitializeToRandom( pMem, nSize );
}
else
{
if ( bDebuggerPresent )
{
// Ok, it's already set to 0xbaadf00d, but we want something that will make floating-point #'s NANs.
InitializeToFeeFee( pMem, nSize );
}
else
{
#if defined(_DEBUG) || defined(USE_LIGHT_MEM_DEBUG)
#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
+33
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
#if ( (!defined( POSIX )||defined(_GAMECONSOLE)) && (defined(_DEBUG) || defined(USE_MEM_DEBUG) ) )
#define MEM_IMPL_TYPE_DBG 1
#else
#define MEM_IMPL_TYPE_STD 1
#endif
+2762
View File
File diff suppressed because it is too large Load Diff
+2493
View File
File diff suppressed because it is too large Load Diff
+452
View File
@@ -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
+498
View File
@@ -0,0 +1,498 @@
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Memory allocation!
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#ifndef STEAM
#ifdef TIER0_VALIDATE_HEAP
#include <malloc.h>
#include "tier0/dbg.h"
#include "tier0/memalloc.h"
#include "mem_helpers.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
extern IMemAlloc *g_pActualAlloc;
//-----------------------------------------------------------------------------
// NOTE! This should never be called directly from leaf code
// Just use new,delete,malloc,free etc. They will call into this eventually
//-----------------------------------------------------------------------------
class CValidateAlloc : public IMemAlloc
{
public:
enum
{
HEAP_PREFIX_BUFFER_SIZE = 12,
HEAP_SUFFIX_BUFFER_SIZE = 8,
};
CValidateAlloc();
// Release versions
virtual void *Alloc( size_t nSize );
virtual void *Realloc( void *pMem, size_t nSize );
virtual void Free( void *pMem );
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
// Debug versions
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
virtual void Free( void *pMem, const char *pFileName, int nLine );
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
// Returns size of a particular allocation
virtual size_t GetSize( void *pMem );
// Force file + line information for an allocation
virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
virtual void PopAllocDbgInfo();
virtual long CrtSetBreakAlloc( long lNewBreakAlloc );
virtual int CrtSetReportMode( int nReportType, int nReportMode );
virtual int CrtIsValidHeapPointer( const void *pMem );
virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
virtual int CrtCheckMemory( void );
virtual int CrtSetDbgFlag( int nNewFlag );
virtual void CrtMemCheckpoint( _CrtMemState *pState );
void* CrtSetReportFile( int nRptType, void* hFile );
void* CrtSetReportHook( void* pfnNewHook );
int CrtDbgReport( int nRptType, const char * szFile,
int nLine, const char * szModule, const char * pMsg );
virtual int heapchk();
virtual void DumpStats() {}
virtual void DumpStatsFileBase( char const *pchFileBase ) {}
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
+317
View File
@@ -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
+11
View File
@@ -0,0 +1,11 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
+49
View File
@@ -0,0 +1,49 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $NoKeywords: $
//===========================================================================//
#if defined( PLATFORM_WINDOWS_PC )
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0403
#include <windows.h>
#elif defined( _PS3 )
#include <cellstatus.h>
#include <sys/prx.h>
#endif
#include "tier0/platform.h"
// First include standard libraries
#include "tier0/valve_off.h"
#include <assert.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#ifdef PLATFORM_POSIX
#include <unistd.h>
#include <ctype.h>
#include <limits.h>
#define _MAX_PATH PATH_MAX
#endif
#include "tier0/valve_on.h"
#include "tier0/basetypes.h"
#include "tier0/dbgflag.h"
#include "tier0/dbg.h"
#ifdef STEAM
#include "tier0/memhook.h"
#endif
#include "tier0/validator.h"
#include "tier0/fasttimer.h"
+559
View File
@@ -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( &ltime );
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( &ltime );
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
View File
@@ -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( &ltime );
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( &ltime );
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
+162
View File
@@ -0,0 +1,162 @@
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "tier0/platform.h"
#include "pch_tier0.h"
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#pragma warning( disable : 4530 ) // warning: exception handler -GX option
#include "tier0/platform.h"
#include "tier0/vprof.h"
#include "tier0/pmelib.h"
#include "tier0/l2cache.h"
#include "tier0/dbg.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose: Initialization
//-----------------------------------------------------------------------------
void InitPME( void )
{
bool bInit = false;
PME *pPME = PME::Instance();
if ( pPME )
{
if ( pPME->GetVendor() != INTEL )
return;
if ( pPME->GetProcessorFamily() != PENTIUM4_FAMILY )
return;
pPME->SetProcessPriority( ProcessPriorityHigh );
bInit = true;
DevMsg( 1, _T("PME Initialized.\n") );
}
else
{
DevMsg( 1, _T("PME Uninitialized.\n") );
}
#ifdef VPROF_ENABLED
g_VProfCurrentProfile.PMEInitialized( bInit );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Shutdown
//-----------------------------------------------------------------------------
void ShutdownPME( void )
{
PME *pPME = PME::Instance();
if ( pPME )
{
pPME->SetProcessPriority( ProcessPriorityNormal );
}
#ifdef VPROF_ENABLED
g_VProfCurrentProfile.PMEInitialized( false );
#endif
}
//=============================================================================
//
// CL2Cache Code.
//
static int s_nCreateCount = 0;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CL2Cache::CL2Cache()
{
m_nID = s_nCreateCount++;
m_pL2CacheEvent = new P4Event_BSQ_cache_reference;
m_iL2CacheMissCount = 0;
m_i64Start = 0;
m_i64End = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CL2Cache::~CL2Cache()
{
if ( m_pL2CacheEvent )
{
delete m_pL2CacheEvent;
m_pL2CacheEvent = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CL2Cache::Start( void )
{
if ( m_pL2CacheEvent )
{
// Set this up to check for L2 cache misses.
m_pL2CacheEvent->eventMask->RD_2ndL_MISS = 1;
// Set the event mask and set the capture mode.
// m_pL2CacheEvent->SetCaptureMode( USR_Only );
m_pL2CacheEvent->SetCaptureMode( OS_and_USR );
// That's it, now sw capture events
m_pL2CacheEvent->StopCounter();
m_pL2CacheEvent->ClearCounter();
m_pL2CacheEvent->StartCounter();
m_i64Start = m_pL2CacheEvent->ReadCounter();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CL2Cache::End( void )
{
if ( m_pL2CacheEvent )
{
// Stop the counter and find the delta.
m_i64End = m_pL2CacheEvent->ReadCounter();
int64 i64Delta = m_i64End - m_i64Start;
m_pL2CacheEvent->StopCounter();
// Save the delta for later query.
m_iL2CacheMissCount = ( int )i64Delta;
}
}
#pragma warning( default : 4530 )
#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Ensure that all of our internal structures are consistent, and
// account for all memory that we've allocated.
// Input: validator - Our global validator object
// pchName - Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void CL2Cache::Validate( CValidator &validator, tchar *pchName )
{
validator.Push( _T("CL2Cache"), this, pchName );
validator.ClaimMemory( m_pL2CacheEvent );
validator.Pop( );
}
#endif // DBGFLAG_VALIDATE
+51
View File
@@ -0,0 +1,51 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "tier0/platform.h"
#include "tier0/vprof.h"
#include "tier0/dbg.h"
//-----------------------------------------------------------------------------
// Purpose: Initialization
//-----------------------------------------------------------------------------
void InitPME( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Shutdown
//-----------------------------------------------------------------------------
void ShutdownPME( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CL2Cache::CL2Cache()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CL2Cache::~CL2Cache()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CL2Cache::Start( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CL2Cache::End( void )
{
}
+665
View File
@@ -0,0 +1,665 @@
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#ifdef _WIN32
#include <windows.h>
#pragma warning( disable : 4530 ) // warning: exception handler -GX option
#include "tier0/valve_off.h"
#include "tier0/pmelib.h"
#if _MSC_VER >=1300
#else
#include "winioctl.h"
#endif
#include "tier0/valve_on.h"
#include "tier0/ioctlcodes.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
PME* PME::_singleton = 0;
// Single interface.
PME* PME::Instance()
{
if (_singleton == 0)
{
_singleton = new PME;
}
return _singleton;
}
//---------------------------------------------------------------------------
// Open the device driver and detect the processor
//---------------------------------------------------------------------------
HRESULT PME::Init( void )
{
OSVERSIONINFO OS;
if ( bDriverOpen )
return E_DRIVER_ALREADY_OPEN;
switch( vendor )
{
case INTEL:
case AMD:
break;
default:
bDriverOpen = FALSE; // not an Intel or Athlon processor so return false
return E_UNKNOWN_CPU_VENDOR;
}
//-----------------------------------------------------------------------
// Get the operating system version
//-----------------------------------------------------------------------
OS.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
GetVersionEx( &OS );
if ( OS.dwPlatformId == VER_PLATFORM_WIN32_NT )
{
hFile = CreateFile( // WINDOWS NT
"\\\\.\\GDPERF",
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
else
{
hFile = CreateFile( // WINDOWS 95
"\\\\.\\GDPERF.VXD",
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
if (hFile == INVALID_HANDLE_VALUE )
return E_CANT_OPEN_DRIVER;
bDriverOpen = TRUE;
//-------------------------------------------------------------------
// We have successfully opened the device driver, get the family
// of the processor.
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// We need to write to counter 0 on the pro family to enable both
// of the performance counters. We write to both so they start in a
// known state. For the pentium this is not necessary.
//-------------------------------------------------------------------
if (vendor == INTEL && version.Family == PENTIUMPRO_FAMILY)
{
SelectP5P6PerformanceEvent(P6_CLOCK, 0, TRUE, TRUE);
SelectP5P6PerformanceEvent(P6_CLOCK, 1, TRUE, TRUE);
}
return S_OK;
}
//---------------------------------------------------------------------------
// Close the device driver
//---------------------------------------------------------------------------
HRESULT PME::Close(void)
{
if (bDriverOpen == false) // driver is not going
return E_DRIVER_NOT_OPEN;
bDriverOpen = false;
if (hFile) // if we have no driver handle, return FALSE
{
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
+35
View File
@@ -0,0 +1,35 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by assert_dialog.rc
//
#define IDD_ASSERT_DIALOG 101
#define IDC_FILENAME_CONTROL 1000
#define IDC_LINE_CONTROL 1001
#define IDC_IGNORE_FILE 1002
#define IDC_IGNORE_NEARBY 1003
#define IDC_IGNORE_NUMLINES 1004
#define IDC_IGNORE_THIS 1005
#define IDC_BREAK 1006
#define IDC_IGNORE_ALL 1008
#define IDC_IGNORE_ALWAYS 1009
#define IDC_IGNORE_NUMTIMES 1010
#define IDC_ASSERT_MSG_CTRL 1011
#define IDC_NOID -1
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1005
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
+1682
View File
File diff suppressed because it is too large Load Diff
+3096
View File
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
//========= Copyright © Valve Corporation, All rights reserved. ============//
#include "pch_tier0.h"
#include "tier0_strtools.h"
#define TOLOWERC( x ) (( ( x >= 'A' ) && ( x <= 'Z' ) )?( x + 32 ) : x )
extern "C"
int V_tier0_stricmp(const char *s1, const char *s2 )
{
uint8 const *pS1 = ( uint8 const * ) s1;
uint8 const *pS2 = ( uint8 const * ) s2;
for(;;)
{
int c1 = *( pS1++ );
int c2 = *( pS2++ );
if ( c1 == c2 )
{
if ( !c1 ) return 0;
}
else
{
if ( ! c2 )
{
return c1 - c2;
}
c1 = TOLOWERC( c1 );
c2 = TOLOWERC( c2 );
if ( c1 != c2 )
{
return c1 - c2;
}
}
c1 = *( pS1++ );
c2 = *( pS2++ );
if ( c1 == c2 )
{
if ( !c1 ) return 0;
}
else
{
if ( ! c2 )
{
return c1 - c2;
}
c1 = TOLOWERC( c1 );
c2 = TOLOWERC( c2 );
if ( c1 != c2 )
{
return c1 - c2;
}
}
}
}
+3
View File
@@ -0,0 +1,3 @@
//========= Copyright © Valve Corporation, All rights reserved. ============//
extern "C" int V_tier0_stricmp(const char *s1, const char *s2 );
+124
View File
@@ -0,0 +1,124 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "pch_tier0.h"
#include "vstdlib/pch_vstdlib.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Initializer
// Input: pchType - Type of the object we represent.
// WARNING: pchType must be a static (since we keep a copy of it around for a while)
// pvObj - Pointer to the object we represent
// pchName - Name of the individual object we represent
// WARNING: pchName must be a static (since we keep a copy of it around for a while)
// pValObjectparent- Our parent object (ie, the object that our object is a member of)
// pValObjectPrev - Object that precedes us in the linked list (we're
// always added to the end)
//-----------------------------------------------------------------------------
void CValObject::Init( tchar *pchType, void *pvObj, tchar *pchName,
CValObject *pValObjectParent, CValObject *pValObjectPrev )
{
m_nUser = 0;
// Initialize pchType:
if ( NULL != pchType )
{
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
+1752
View File
File diff suppressed because it is too large Load Diff
+161
View File
@@ -0,0 +1,161 @@
//======= Copyright © 1996-2006, Valve Corporation, All rights reserved. ======
//
// Purpose: Win32 Console API helpers
//
//=============================================================================
#include "pch_tier0.h"
#include "win32consoleio.h"
#if defined( _WIN32 )
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <iostream>
#endif // defined( _WIN32 )
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
//
// Attach a console to a Win32 GUI process and setup stdin, stdout & stderr
// along with the std::iostream (cout, cin, cerr) equivalents to read and
// write to and from that console
//
// 1. Ensure the handle associated with stdio is FILE_TYPE_UNKNOWN
// if it's anything else just return false. This supports cygwin
// style command shells like rxvt which setup pipes to processes
// they spawn
//
// 2. See if the Win32 function call AttachConsole exists in kernel32
// It's a Windows 2000 and above call. If it does, call it and see
// if it succeeds in attaching to the console of the parent process.
// If that succeeds, return false (for no new console allocated).
// This supports someone typing the command from a normal windows
// command window and having the output go to the parent window.
// It's a little funny because a GUI app detaches so the command
// prompt gets intermingled with output from this process
//
// 3. If things get to here call AllocConsole which will pop open
// a new window and allow output to go to that window. The
// window will disappear when the process exists so if it's used
// for something like a help message then do something like getchar()
// from stdin to wait for a keypress. if AllocConsole is called
// true is returned.
//
// Return: true if AllocConsole() was used to pop open a new windows console
//
//-----------------------------------------------------------------------------
bool SetupWin32ConsoleIO()
{
#if defined( _WIN32 )
// Only useful on Windows platforms
bool newConsole( false );
if ( GetFileType( GetStdHandle( STD_OUTPUT_HANDLE ) ) == FILE_TYPE_UNKNOWN )
{
HINSTANCE hInst = ::LoadLibrary( "kernel32.dll" );
typedef BOOL ( WINAPI * pAttachConsole_t )( DWORD );
pAttachConsole_t pAttachConsole( ( BOOL ( _stdcall * )( DWORD ) )GetProcAddress( hInst, "AttachConsole" ) );
if ( !( pAttachConsole && (*pAttachConsole)( ( DWORD ) - 1 ) ) )
{
newConsole = true;
AllocConsole();
}
*stdout = *_fdopen( _open_osfhandle( reinterpret_cast< intp >( GetStdHandle( STD_OUTPUT_HANDLE ) ), _O_TEXT ), "w" );
setvbuf( stdout, NULL, _IONBF, 0 );
*stdin = *_fdopen( _open_osfhandle( reinterpret_cast< intp >( GetStdHandle( STD_INPUT_HANDLE ) ), _O_TEXT ), "r" );
setvbuf( stdin, NULL, _IONBF, 0 );
*stderr = *_fdopen( _open_osfhandle( reinterpret_cast< intp >( GetStdHandle( STD_ERROR_HANDLE ) ), _O_TEXT ), "w" );
setvbuf( stdout, NULL, _IONBF, 0 );
std::ios_base::sync_with_stdio();
}
return newConsole;
#else // defined( _WIN32 )
return false;
#endif // defined( _WIN32 )
}
//-----------------------------------------------------------------------------
// Win32 Console Color API Helpers, originally from cmdlib.
// Retrieves the current console color attributes.
//-----------------------------------------------------------------------------
void InitWin32ConsoleColorContext( Win32ConsoleColorContext_t *pContext )
{
#if PLATFORM_WINDOWS_PC
// Get the old background attributes.
CONSOLE_SCREEN_BUFFER_INFO oldInfo;
GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo );
pContext->m_InitialColor = pContext->m_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
pContext->m_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY);
pContext->m_BadColor = 0;
if (pContext->m_BackgroundFlags & BACKGROUND_RED)
pContext->m_BadColor |= FOREGROUND_RED;
if (pContext->m_BackgroundFlags & BACKGROUND_GREEN)
pContext->m_BadColor |= FOREGROUND_GREEN;
if (pContext->m_BackgroundFlags & BACKGROUND_BLUE)
pContext->m_BadColor |= FOREGROUND_BLUE;
if (pContext->m_BackgroundFlags & BACKGROUND_INTENSITY)
pContext->m_BadColor |= FOREGROUND_INTENSITY;
#else
pContext->m_InitialColor = 0;
#endif
}
//-----------------------------------------------------------------------------
// Sets the active console foreground color. This function is smart enough to
// avoid setting the color to something that would be unreadable given
// the user's potentially customized background color. It leaves the
// background color unchanged.
// Returns: The console's previous foreground color.
//-----------------------------------------------------------------------------
uint16 SetWin32ConsoleColor( Win32ConsoleColorContext_t *pContext, int nRed, int nGreen, int nBlue, int nIntensity )
{
#if PLATFORM_WINDOWS_PC
uint16 ret = pContext->m_LastColor;
pContext->m_LastColor = 0;
if ( nRed ) pContext->m_LastColor |= FOREGROUND_RED;
if ( nGreen ) pContext->m_LastColor |= FOREGROUND_GREEN;
if ( nBlue ) pContext->m_LastColor |= FOREGROUND_BLUE;
if ( nIntensity ) pContext->m_LastColor |= FOREGROUND_INTENSITY;
// Just use the initial color if there's a match...
if ( pContext->m_LastColor == pContext->m_BadColor )
pContext->m_LastColor = pContext->m_InitialColor;
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), pContext->m_LastColor | pContext->m_BackgroundFlags );
return ret;
#else
return 0;
#endif
}
//-----------------------------------------------------------------------------
// Restore's the active foreground console color, without distributing the current
// background color.
//-----------------------------------------------------------------------------
void RestoreWin32ConsoleColor( Win32ConsoleColorContext_t *pContext, uint16 prevColor )
{
#if PLATFORM_WINDOWS_PC
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), prevColor | pContext->m_BackgroundFlags );
pContext->m_LastColor = prevColor;
#endif
}