summaryrefslogtreecommitdiff
path: root/external/vpc/tier0
diff options
context:
space:
mode:
Diffstat (limited to 'external/vpc/tier0')
-rw-r--r--external/vpc/tier0/assert_dialog.cpp573
-rw-r--r--external/vpc/tier0/commandline.cpp676
-rw-r--r--external/vpc/tier0/cpu.cpp697
-rw-r--r--external/vpc/tier0/cpu_posix.cpp143
-rw-r--r--external/vpc/tier0/cputopology.cpp1018
-rw-r--r--external/vpc/tier0/cputopology.h40
-rw-r--r--external/vpc/tier0/dbg.cpp627
-rw-r--r--external/vpc/tier0/etwprof.cpp377
-rw-r--r--external/vpc/tier0/fasttimer.cpp23
-rw-r--r--external/vpc/tier0/logging.cpp698
-rw-r--r--external/vpc/tier0/mem.cpp79
-rw-r--r--external/vpc/tier0/mem_helpers.cpp186
-rw-r--r--external/vpc/tier0/mem_helpers.h33
-rw-r--r--external/vpc/tier0/mem_impl_type.h6
-rw-r--r--external/vpc/tier0/memdbg.cpp2762
-rw-r--r--external/vpc/tier0/memstd.cpp2493
-rw-r--r--external/vpc/tier0/memstd.h452
-rw-r--r--external/vpc/tier0/memvalidate.cpp498
-rw-r--r--external/vpc/tier0/minidump.cpp317
-rw-r--r--external/vpc/tier0/pch_tier0.cpp11
-rw-r--r--external/vpc/tier0/pch_tier0.h49
-rw-r--r--external/vpc/tier0/platform.cpp559
-rw-r--r--external/vpc/tier0/platform_posix.cpp426
-rw-r--r--external/vpc/tier0/pme.cpp162
-rw-r--r--external/vpc/tier0/pme_posix.cpp51
-rw-r--r--external/vpc/tier0/pmelib.cpp665
-rw-r--r--external/vpc/tier0/resource.h35
-rw-r--r--external/vpc/tier0/stacktools.cpp1682
-rw-r--r--external/vpc/tier0/threadtools.cpp3096
-rw-r--r--external/vpc/tier0/tier0_strtools.cpp53
-rw-r--r--external/vpc/tier0/tier0_strtools.h3
-rw-r--r--external/vpc/tier0/valobject.cpp124
-rw-r--r--external/vpc/tier0/vprof.cpp1752
-rw-r--r--external/vpc/tier0/win32consoleio.cpp161
34 files changed, 20527 insertions, 0 deletions
diff --git a/external/vpc/tier0/assert_dialog.cpp b/external/vpc/tier0/assert_dialog.cpp
new file mode 100644
index 0000000..8df5e02
--- /dev/null
+++ b/external/vpc/tier0/assert_dialog.cpp
@@ -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;
+}
+
diff --git a/external/vpc/tier0/commandline.cpp b/external/vpc/tier0/commandline.cpp
new file mode 100644
index 0000000..2620b2e
--- /dev/null
+++ b/external/vpc/tier0/commandline.cpp
@@ -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] );
+}
diff --git a/external/vpc/tier0/cpu.cpp b/external/vpc/tier0/cpu.cpp
new file mode 100644
index 0000000..420e1a1
--- /dev/null
+++ b/external/vpc/tier0/cpu.cpp
@@ -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;
+}
+
diff --git a/external/vpc/tier0/cpu_posix.cpp b/external/vpc/tier0/cpu_posix.cpp
new file mode 100644
index 0000000..4fe2db8
--- /dev/null
+++ b/external/vpc/tier0/cpu_posix.cpp
@@ -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;
+}
+
diff --git a/external/vpc/tier0/cputopology.cpp b/external/vpc/tier0/cputopology.cpp
new file mode 100644
index 0000000..1125cae
--- /dev/null
+++ b/external/vpc/tier0/cputopology.cpp
@@ -0,0 +1,1018 @@
+//-------------------------------------------------------------------------------------
+// CpuTopology.cpp
+//
+// CpuToplogy class implementation.
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//-------------------------------------------------------------------------------------
+#include "pch_tier0.h"
+
+#if defined(_WIN32) && !defined(_X360) && !defined( _PS3 )
+#include "cputopology.h"
+#include <stdlib.h>
+#include <crtdbg.h>
+
+#undef malloc
+#undef free
+#ifdef _WIN64
+ // Inline assembly is not supported in 64-bit. Ideally, we would prefer to
+ // use the __cpuid intrinsic to avoid having to drop to assembly altogether.
+ // However, as of MSVC 9.0, the __cpuid intrinsic does not enable the
+ // ability to set the ECX register, which is required for obtaining certain
+ // extended CPU information. To overcome these issues, we must call the
+ // Cpuid64() external function, which is written in assembly and located in
+ // the cpuid64.asm file included in this project. This project contains a build
+ // step that invokes 64-bit MASM on cpuid64.asm when building a 64-bit target.
+ extern "C" void Cpuid64(void* argsPtr);
+#endif
+
+//---------------------------------------------------------------------------------
+// Name: ICpuToplogy
+// Desc: Specifies the interface that each class that provides an implementation
+// for extracting cpu topology must conform to. This is the Implementor
+// class in the traditional Bridge Pattern.
+//---------------------------------------------------------------------------------
+class ICpuTopology
+{
+public:
+ virtual ~ICpuTopology()
+ {
+ }
+ virtual BOOL IsDefaultImpl() const = 0;
+ virtual DWORD NumberOfProcessCores() const = 0;
+ virtual DWORD NumberOfSystemCores() const = 0;
+ virtual DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const = 0;
+};
+
+
+namespace
+{
+///////////////////////////////////////////////////////////////////////////////////
+// Local Class Definitions
+///////////////////////////////////////////////////////////////////////////////////
+
+//---------------------------------------------------------------------------------
+// Name: DefaultImpl
+// Desc: Provides a default implementation for the ICpuTopology interface when
+// GetLogicalProcessorInformation and CPUID are not supported for whatever
+// reason. This is a ConcreteImplementor class in the traditional Bridge
+// Pattern.
+//---------------------------------------------------------------------------------
+class DefaultImpl : public ICpuTopology
+{
+public:
+ //-----------------------------------------------------------------------------
+ // DefaultImpl::IsDefaultImpl
+ //-----------------------------------------------------------------------------
+ /*virtual*/ BOOL IsDefaultImpl() const
+ {
+ return TRUE;
+ }
+
+ //-----------------------------------------------------------------------------
+ // DefaultImpl::NumberOfProcessCores
+ //-----------------------------------------------------------------------------
+ /*virtual*/ DWORD NumberOfProcessCores() const
+ {
+ return 1;
+ }
+
+ //-----------------------------------------------------------------------------
+ // DefaultImpl::IsNumberOfSystemCores
+ //-----------------------------------------------------------------------------
+ /*virtual*/ DWORD NumberOfSystemCores() const
+ {
+ return 1;
+ }
+
+ //-----------------------------------------------------------------------------
+ // DefaultImpl::CoreAffinityMask
+ //-----------------------------------------------------------------------------
+ /*virtual*/ DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const
+ {
+ DWORD_PTR coreAffinity = 0;
+ if( 1 == coreIdx )
+ {
+ DWORD_PTR dwSystemAffinity;
+ GetProcessAffinityMask( GetCurrentProcess(), &coreAffinity, &dwSystemAffinity );
+ }
+ return coreAffinity;
+ }
+};
+
+//---------------------------------------------------------------------------------
+// Name: GlpiImpl
+// Desc: Provides the GetLogicalProcessorInformation implementation for the
+// ICpuTopology interface. This is a ConcreteImplementor class in the
+// traditional Bridge Pattern.
+//---------------------------------------------------------------------------------
+class GlpiImpl : public ICpuTopology
+{
+public:
+
+ //-----------------------------------------------------------------------------
+ // Name: GlpiImpl::GlpiImpl
+ // Desc: Initializes the internal structures/data with information retrieved
+ // from a call to GetLogicalProcessorInformation.
+ //-----------------------------------------------------------------------------
+ GlpiImpl() : m_pSlpi( NULL ),
+ m_nItems( 0 )
+ {
+ _ASSERT( IsSupported() );
+
+ GlpiFnPtr pGlpi = GetGlpiFn_();
+ _ASSERT( pGlpi );
+
+ DWORD cbBuffer = 0;
+ pGlpi( 0, &cbBuffer );
+
+ m_pSlpi = ( SYSTEM_LOGICAL_PROCESSOR_INFORMATION* )malloc( cbBuffer );
+ pGlpi( m_pSlpi, &cbBuffer );
+ m_nItems = cbBuffer / sizeof( SYSTEM_LOGICAL_PROCESSOR_INFORMATION );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: GlpiImpl::~GlpiImpl
+ //-----------------------------------------------------------------------------
+ /*virtual*/ ~GlpiImpl()
+ {
+ free( m_pSlpi );
+ m_pSlpi = 0;
+ m_nItems = 0;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: GlpiImpl::IsDefaultImpl
+ //-----------------------------------------------------------------------------
+ /*virtual*/ BOOL IsDefaultImpl() const
+ {
+ return FALSE;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: GlpiImpl::NumberOfProcessCores
+ // Desc: Gets the total number of physical processor cores available to the
+ // current process.
+ //-----------------------------------------------------------------------------
+ /*virtual*/ DWORD NumberOfProcessCores() const
+ {
+ DWORD_PTR dwProcessAffinity, dwSystemAffinity;
+ GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity );
+
+ DWORD nCores = 0;
+ for( DWORD i = 0; i < m_nItems; ++i )
+ {
+ if( ( RelationProcessorCore == m_pSlpi[i].Relationship ) &&
+ ( m_pSlpi[i].ProcessorMask & dwProcessAffinity ) )
+ {
+ ++nCores;
+ }
+ }
+ return nCores;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: GlpiImpl::NumberOfSystemCores
+ // Desc: Gets the total number of physical processor cores enabled on the
+ // system.
+ //-----------------------------------------------------------------------------
+ /*virtual*/ DWORD NumberOfSystemCores() const
+ {
+ DWORD nCores = 0;
+ for( DWORD i = 0; i < m_nItems; ++i )
+ {
+ if( RelationProcessorCore == m_pSlpi[i].Relationship )
+ ++nCores;
+ }
+ return nCores;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: GlpiImpl::CoreAffinityMask
+ // Desc: Gets an affinity mask that corresponds to the requested processor
+ // core.
+ //-----------------------------------------------------------------------------
+ /*virtual*/ DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const
+ {
+ DWORD_PTR dwProcessAffinity, dwSystemAffinity;
+ GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity );
+
+ for( DWORD i = 0; i < m_nItems; ++i )
+ {
+ if( RelationProcessorCore == m_pSlpi[i].Relationship )
+ {
+ if( !coreIdx-- )
+ {
+ return m_pSlpi[i].ProcessorMask & dwProcessAffinity;
+ }
+ }
+ }
+ return 0;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: GlpiImpl::IsSupported
+ //-----------------------------------------------------------------------------
+ static BOOL IsSupported()
+ {
+ return NULL != GetGlpiFn_();
+ }
+
+private:
+ // GetLogicalProcessorInformation function pointer
+ typedef BOOL( WINAPI* GlpiFnPtr )(
+SYSTEM_LOGICAL_PROCESSOR_INFORMATION*,
+PDWORD
+);
+
+ //-----------------------------------------------------------------------------
+ // Name: GlpiImpl::VerifyGlpiFn_
+ // Desc: Gets a pointer to the GetLogicalProcessorInformation function only if
+ // it is supported on the current platform.
+ // GetLogicalProcessorInformation is supported on Windows Server 2003 and
+ // XP64, however there is a bug with the implementation. Therefore, only
+ // GetLogicalProcessorInformation on Windows Vista is supported in this
+ // sample.
+ //-----------------------------------------------------------------------------
+ static GlpiFnPtr VerifyGlpiFn_()
+ {
+ // VerifyVersionInfo function pointer
+ typedef BOOL ( WINAPI* VviFnPtr )( LPOSVERSIONINFOEX,
+ DWORD,
+ DWORDLONG );
+
+ HMODULE hMod = GetModuleHandle( TEXT( "kernel32" ) );
+#ifdef _UNICODE
+ VviFnPtr pVvi = (VviFnPtr) GetProcAddress( hMod, "VerifyVersionInfoW" );
+ #else
+ VviFnPtr pVvi = ( VviFnPtr )GetProcAddress( hMod, "VerifyVersionInfoA" );
+#endif
+ GlpiFnPtr pGlpi = NULL;
+
+ if( pVvi )
+ {
+ // VerSetConditionMask function pointer
+ typedef ULONGLONG ( WINAPI* VscmFnPtr )( ULONGLONG,
+ DWORD,
+ BYTE );
+
+ VscmFnPtr pVscm = ( VscmFnPtr )GetProcAddress( hMod, "VerSetConditionMask" );
+
+ _ASSERT( pVscm );
+
+ // Check for Windows Vista
+ OSVERSIONINFOEX osvi = { sizeof( OSVERSIONINFOEX ) };
+ osvi.dwMajorVersion = 6;
+ osvi.dwMinorVersion = 0;
+ osvi.wServicePackMajor = 0;
+ osvi.wServicePackMinor = 0;
+
+ ULONGLONG dwlMask = 0;
+ dwlMask = pVscm( dwlMask, VER_MAJORVERSION, VER_GREATER_EQUAL );
+ dwlMask = pVscm( dwlMask, VER_MINORVERSION, VER_GREATER_EQUAL );
+ dwlMask = pVscm( dwlMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL );
+ dwlMask = pVscm( dwlMask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL );
+
+ if( pVvi( &osvi, VER_MAJORVERSION
+ | VER_MINORVERSION
+ | VER_SERVICEPACKMAJOR
+ | VER_SERVICEPACKMINOR,
+ dwlMask ) )
+ {
+ pGlpi = ( GlpiFnPtr )GetProcAddress( hMod, "GetLogicalProcessorInformation" );
+ _ASSERT( pGlpi );
+ }
+ }
+
+ return pGlpi;
+
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: GlpiImpl::GetGlpiFn_
+ // Desc: Gets a cached pointer to the GetLogicalProcessorInformation function.
+ //-----------------------------------------------------------------------------
+ static GlpiFnPtr GetGlpiFn_()
+ {
+ static GlpiFnPtr pGlpi = VerifyGlpiFn_();
+ return pGlpi;
+ }
+
+ // Private Members
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION* m_pSlpi;
+ DWORD m_nItems;
+};
+
+//---------------------------------------------------------------------------------
+// Name: ApicExtractor
+// Desc: A utility class that provides an interface for decoding a processor
+// APIC ID. An APIC ID is an 8-bit identifier given to each logical
+// processor on system boot and can be retrieved by the CPUID instruction.
+// Each APIC ID is composed of a PACKAGE_ID, CORE_ID and SMT_ID that describe
+// the relationship of a logical processor within the processor topology of
+// the system.
+//---------------------------------------------------------------------------------
+class ApicExtractor
+{
+public:
+ //-----------------------------------------------------------------------------
+ // Name: ApicExtractor::ApicExtractor
+ //-----------------------------------------------------------------------------
+ ApicExtractor( DWORD nLogProcsPerPkg = 1, DWORD nCoresPerPkg = 1 )
+ {
+ SetPackageTopology( nLogProcsPerPkg, nCoresPerPkg );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: ApicExtractor::SmtId
+ //-----------------------------------------------------------------------------
+ BYTE SmtId( BYTE apicId ) const
+ {
+ return apicId & m_smtIdMask.mask;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: ApicExtractor::CoreId
+ //-----------------------------------------------------------------------------
+ BYTE CoreId( BYTE apicId ) const
+ {
+ return ( apicId & m_coreIdMask.mask ) >> m_smtIdMask.width;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: ApicExtractor::PackageId
+ //-----------------------------------------------------------------------------
+ BYTE PackageId( BYTE apicId ) const
+ {
+ return ( apicId & m_pkgIdMask.mask ) >>
+ ( m_smtIdMask.width + m_coreIdMask.width );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: ApicExtractor::PackageCoreId
+ //-----------------------------------------------------------------------------
+ BYTE PackageCoreId( BYTE apicId ) const
+ {
+ return ( apicId & ( m_pkgIdMask.mask | m_coreIdMask.mask ) ) >>
+ m_smtIdMask.width;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: ApicExtractor::GetLogProcsPerPkg
+ //-----------------------------------------------------------------------------
+ DWORD GetLogProcsPerPkg() const
+ {
+ return m_nLogProcsPerPkg;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: ApicExtractor::GetCoresPerPkg
+ //-----------------------------------------------------------------------------
+ DWORD GetCoresPerPkg() const
+ {
+ return m_nCoresPerPkg;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: ApicExtractor::SetPackageTopology
+ // Desc: You should call SetPackageTopology with the number of logical
+ // processors per package and number of cores per package before calling
+ // the sub id accessors (SmtId(), CoreId(), PackageId(), PackageCoreId())
+ // as this information is required to effectively decode an APIC ID into
+ // its sub parts.
+ //-----------------------------------------------------------------------------
+ void SetPackageTopology( DWORD nLogProcsPerPkg, DWORD nCoresPerPkg )
+ {
+ m_nLogProcsPerPkg = ( BYTE )nLogProcsPerPkg;
+ m_nCoresPerPkg = ( BYTE )nCoresPerPkg;
+
+ // fix for Phenom x3 and similar CPUs - it reports 3 logical processors per package, and 4 cores per package
+ // so one core is probably just disabled for yield, but it causes a bug in GetMaskWidth that propagates
+ if( m_nCoresPerPkg > m_nLogProcsPerPkg )
+ {
+ m_nCoresPerPkg = m_nLogProcsPerPkg;
+ }
+
+ m_smtIdMask.width = GetMaskWidth_( m_nLogProcsPerPkg / m_nCoresPerPkg );
+ m_coreIdMask.width = GetMaskWidth_( m_nCoresPerPkg );
+ m_pkgIdMask.width = 8 - ( m_smtIdMask.width + m_coreIdMask.width );
+
+ m_pkgIdMask.mask = ( BYTE )( 0xFF << ( m_smtIdMask.width + m_coreIdMask.width ) );
+ m_coreIdMask.mask = ( BYTE )( ( 0xFF << m_smtIdMask.width ) ^ m_pkgIdMask.mask );
+ m_smtIdMask.mask = ( BYTE )~( 0xFF << m_smtIdMask.width );
+
+ }
+
+private:
+ //-----------------------------------------------------------------------------
+ // Name: ApicExtractor::GetMaskWidth_
+ // Desc: Gets the width of a sub id bit field in an APIC ID. The width of a
+ // sub id (CORE_ID, SMT_ID) is only wide enough to support the maximum
+ // number of ids that needs to be represented in the topology.
+ //-----------------------------------------------------------------------------
+ static BYTE GetMaskWidth_( BYTE maxIds )
+ {
+ --maxIds;
+
+ // find index of msb
+ BYTE msbIdx = 8;
+ BYTE msbMask = 0x80;
+ while( msbMask && !( msbMask & maxIds ) )
+ {
+ --msbIdx;
+ msbMask >>= 1;
+ }
+ return msbIdx;
+ }
+
+ struct IdMask
+ {
+ BYTE width;
+ BYTE mask;
+ };
+
+ // Private Members
+ BYTE m_nLogProcsPerPkg;
+ BYTE m_nCoresPerPkg;
+ IdMask m_smtIdMask;
+ IdMask m_coreIdMask;
+ IdMask m_pkgIdMask;
+};
+
+//---------------------------------------------------------------------------------
+// Name: Cpuid
+// Desc: A utility class that wraps the functionality of the CPUID instruction.
+// Call the Call() method with the desired CPUID function, and use the
+// register accessors to retrieve the register values.
+//---------------------------------------------------------------------------------
+class Cpuid
+{
+public:
+ // FnSet values are used to indicate a CPUID function set.
+ enum FnSet
+ {
+ Std = 0x00000000,
+ Ext = 0x80000000
+ };
+
+ //-----------------------------------------------------------------------------
+ // Name: Cpuid::Cpuid
+ //-----------------------------------------------------------------------------
+ Cpuid() : m_eax( 0 ),
+ m_ebx( 0 ),
+ m_ecx( 0 ),
+ m_edx( 0 )
+ {
+ }
+
+ // Register accessors
+ DWORD Eax() const
+ {
+ return m_eax;
+ }
+ DWORD Ebx() const
+ {
+ return m_ebx;
+ }
+ DWORD Ecx() const
+ {
+ return m_ecx;
+ }
+ DWORD Edx() const
+ {
+ return m_edx;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: Cpuid::Call
+ // Desc: Calls the CPUID instruction with the specified function. Returns TRUE
+ // if the CPUID function was supported, FALSE if it wasn't.
+ //-----------------------------------------------------------------------------
+ BOOL Call( FnSet fnSet, DWORD fn )
+ {
+ if( IsFnSupported( fnSet, fn ) )
+ {
+ UncheckedCall_( fnSet, fn );
+ return true;
+ }
+ return false;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: Cpuid::IsVendor
+ // Desc: Compares a string with the vendor string encoded in the CPUID
+ // instruction.
+ //-----------------------------------------------------------------------------
+ static BOOL IsVendor( const char* strVendor )
+ {
+ // Cache the vendor string
+ static const Cpuid cpu( Std );
+ return cpu.Ebx() == *reinterpret_cast<const DWORD*>( strVendor )
+ && cpu.Ecx() == *reinterpret_cast<const DWORD*>( strVendor + 8 )
+ && cpu.Edx() == *reinterpret_cast<const DWORD*>( strVendor + 4 );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: Cpuid::IsFnSupported
+ // Desc: Checks to see if a CPUID function is supported. Different processors
+ // support different functions. This method is automatically called from
+ // the Call() method, so you don't need to call it beforehand.
+ //-----------------------------------------------------------------------------
+ static BOOL IsFnSupported( FnSet fnSet, DWORD fn )
+ {
+ // Cache the maximum supported standard function
+ static const DWORD MaxStdFn = Cpuid( Std ).Eax();
+ // Cache the maximum supported extended function
+ static const DWORD MaxExtFn = Cpuid( Ext ).Eax();
+
+ bool ret = false;
+ switch( fnSet )
+ {
+ case Std:
+ ret = ( fn <= MaxStdFn );
+ break;
+ case Ext:
+ ret = ( fn <= MaxExtFn );
+ break;
+ default:
+ _ASSERT( 0 ); // should never get here
+ break;
+ }
+ return ret;
+ }
+
+private:
+ //-----------------------------------------------------------------------------
+ // Name: Cpuid::Cpuid
+ // Desc: This constructor is private and is only used to set a Cpuid object to
+ // initial values retrieved from CPUID functions 0x00000000 and
+ // 0x80000000. Good for caching values from the CPUID instruction that
+ // are not variable, like the encoded vendor string and the maximum
+ // supported CPUID function values.
+ //-----------------------------------------------------------------------------
+ explicit Cpuid( FnSet fnSet )
+ {
+ UncheckedCall_( fnSet, 0 );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: Cpuid::UncheckedCall_
+ // Desc: Calls the CPUID instruction without checking for CPUID function
+ // support.
+ //-----------------------------------------------------------------------------
+ void UncheckedCall_( FnSet fnSet, DWORD fn )
+ {
+#ifdef _WIN64
+ // Inline assembly is not supported in 64-bit. Ideally, we would prefer to
+ // use the __cpuid intrinsic to avoid having to drop to assembly altogether.
+ // However, as of MSVC 9.0, the __cpuid intrinsic does not enable the
+ // ability to set the ECX register, which is required for obtaining certain
+ // extended CPU information. To overcome these issues, we must call the
+ // Cpuid64() external function, which is written in assembly and located in
+ // the cpuid64.asm file included in this project. This project contains a build
+ // step that invokes 64-bit MASM on cpuid64.asm when building a 64-bit target.
+ m_eax = fnSet | fn;
+ m_ecx = 0;
+ Cpuid64(this);
+ #else
+ __asm
+ {
+ mov ecx, 0
+ mov eax, fn
+ or eax, fnSet
+ cpuid
+ mov edi, this
+ mov [edi].m_eax, eax
+ mov [edi].m_ebx, ebx
+ mov [edi].m_ecx, ecx
+ mov [edi].m_edx, edx
+ }
+#endif
+ }
+
+ // Private Members
+ DWORD m_eax;
+ DWORD m_ebx;
+ DWORD m_ecx;
+ DWORD m_edx;
+};
+
+//---------------------------------------------------------------------------------
+// Name: CpuidImpl
+// Desc: Provides the CPUID instruction implementation for the ICpuTopology
+// interface. This is a ConcreteImplementor class in the traditional Bridge
+// Pattern.
+//---------------------------------------------------------------------------------
+class CpuidImpl : public ICpuTopology
+{
+public:
+ // CpuidFnMasks are used when extracting bit-encoded information retrieved from
+ // the CPUID instruction
+ 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]
+ };
+
+ enum
+ {
+ MaxLogicalProcessors = sizeof( DWORD_PTR ) * 8
+ };
+
+ //-----------------------------------------------------------------------------
+ // Name: CpuidImpl::CpuidImpl
+ // Desc: Initializes internal structures/data with information retrieved from
+ // calling the CPUID instruction.
+ //-----------------------------------------------------------------------------
+ CpuidImpl() : m_nItems( 0 )
+ {
+ _ASSERT( IsSupported() );
+
+ DWORD nLogProcsPerPkg = 1;
+ DWORD nCoresPerPkg = 1;
+
+ Cpuid cpu;
+
+ // Determine if hardware threading is enabled.
+ cpu.Call( Cpuid::Std, 1 );
+ if( cpu.Edx() & HTT )
+ {
+ // Determine the total number of logical processors per package.
+ nLogProcsPerPkg = ( cpu.Ebx() & LogicalProcessorCount ) >> 16;
+
+ // Determine the total number of cores per package. This info
+ // is extracted differently dependending on the cpu vendor.
+ if( Cpuid::IsVendor( GenuineIntel ) )
+ {
+ if( cpu.Call( Cpuid::Std, 4 ) )
+ {
+ nCoresPerPkg = ( ( cpu.Eax() & NC_Intel ) >> 26 ) + 1;
+ }
+ }
+ else
+ {
+ _ASSERT( Cpuid::IsVendor( AuthenticAMD ) );
+ if( cpu.Call( Cpuid::Ext, 8 ) )
+ {
+ // 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 = cpu.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 = ( cpu.Ecx() & NC_Amd ) + 1;
+ }
+ }
+ }
+ }
+
+ // Configure the APIC extractor object with the information it needs to
+ // be able to decode the APIC.
+ m_apicExtractor.SetPackageTopology( nLogProcsPerPkg, nCoresPerPkg );
+
+ DWORD_PTR dwProcessAffinity, dwSystemAffinity;
+ HANDLE hProcess = GetCurrentProcess();
+ HANDLE hThread = GetCurrentThread();
+ GetProcessAffinityMask( hProcess, &dwProcessAffinity, &dwSystemAffinity );
+ if( 1 == dwSystemAffinity )
+ {
+ // Since we only have 1 logical processor present on the system, we
+ // can explicitly set a single APIC ID to zero.
+ _ASSERT( 1 == nLogProcsPerPkg );
+ m_apicIds[m_nItems++] = 0;
+ }
+ else
+ {
+ // Set the process affinity to the system affinity if they are not
+ // equal so that all logical processors can be accounted for.
+ if( dwProcessAffinity != dwSystemAffinity )
+ {
+ SetProcessAffinityMask( hProcess, dwSystemAffinity );
+ }
+
+ // Call cpuid on each active logical processor in the system affinity.
+ DWORD_PTR dwPrevThreadAffinity = 0;
+ for( DWORD_PTR dwThreadAffinity = 1;
+ dwThreadAffinity && dwThreadAffinity <= dwSystemAffinity;
+ dwThreadAffinity <<= 1 )
+ {
+ if( dwSystemAffinity & dwThreadAffinity )
+ {
+ if( 0 == dwPrevThreadAffinity )
+ {
+ // Save the previous thread affinity so we can return
+ // the executing thread affinity back to this state.
+ _ASSERT( 0 == m_nItems );
+ dwPrevThreadAffinity = SetThreadAffinityMask( hThread,
+ dwThreadAffinity );
+ }
+ else
+ {
+ _ASSERT( m_nItems > 0 );
+ SetThreadAffinityMask( hThread, dwThreadAffinity );
+ }
+
+ // Allow the thread to switch to masked logical processor.
+ Sleep( 0 );
+
+ // Store the APIC ID
+ cpu.Call( Cpuid::Std, 1 );
+ m_apicIds[m_nItems++] = ( BYTE )( ( cpu.Ebx() & ApicId ) >> 24 );
+ }
+ }
+
+ // Restore the previous process and thread affinity state.
+ SetProcessAffinityMask( hProcess, dwProcessAffinity );
+ SetThreadAffinityMask( hThread, dwPrevThreadAffinity );
+ Sleep( 0 );
+ }
+
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: CpuidImpl::IsDefaultImpl
+ //-----------------------------------------------------------------------------
+ /*virtual*/ BOOL IsDefaultImpl() const
+ {
+ return FALSE;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: CpuidImpl::NumberOfProcessCores
+ // Desc: Gets the number of processor cores available to the current process.
+ // The total accounts for cores that may have been masked out by process
+ // affinity.
+ //-----------------------------------------------------------------------------
+ /*virtual*/ DWORD NumberOfProcessCores() const
+ {
+ DWORD_PTR dwProcessAffinity, dwSystemAffinity;
+ GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity );
+
+ BYTE pkgCoreIds[MaxLogicalProcessors] = { 0 };
+ DWORD nPkgCoreIds = 0;
+
+ for( DWORD i = 0; i < m_nItems; ++i )
+ {
+ if( dwProcessAffinity & ( ( DWORD_PTR )1 << i ) )
+ {
+ AddUniquePkgCoreId_( i, pkgCoreIds, nPkgCoreIds );
+ }
+ }
+ return nPkgCoreIds;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: CpuidImpl::NumberOfSystemCores
+ // Desc: Gets the number of processor cores on the system.
+ //-----------------------------------------------------------------------------
+ /*virtual*/ DWORD NumberOfSystemCores() const
+ {
+ BYTE pkgCoreIds[MaxLogicalProcessors] = { 0 };
+ DWORD nPkgCoreIds = 0;
+ for( DWORD i = 0; i < m_nItems; ++i )
+ {
+ AddUniquePkgCoreId_( i, pkgCoreIds, nPkgCoreIds );
+ }
+ return nPkgCoreIds;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: CpuidImpl::CoreAffinityMask
+ // Desc: Gets an affinity mask that corresponds to a specific processor core.
+ // coreIdx must be less than the total number of processor cores
+ // recognized by the operating system (NumberOfSystemCores()).
+ //-----------------------------------------------------------------------------
+ /*virtual*/ DWORD_PTR CoreAffinityMask( DWORD coreIdx ) const
+ {
+ BYTE pkgCoreIds[MaxLogicalProcessors] = { 0 };
+ DWORD nPkgCoreIds = 0;
+ for( DWORD i = 0; i < m_nItems; ++i )
+ {
+ AddUniquePkgCoreId_( i, pkgCoreIds, nPkgCoreIds );
+ }
+
+ DWORD_PTR dwProcessAffinity, dwSystemAffinity;
+ GetProcessAffinityMask( GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity );
+
+ DWORD_PTR coreAffinity = 0;
+ if( coreIdx < nPkgCoreIds )
+ {
+ for( DWORD i = 0; i < m_nItems; ++i )
+ {
+ if( m_apicExtractor.PackageCoreId( m_apicIds[i] ) == pkgCoreIds[coreIdx] )
+ {
+ coreAffinity |= ( dwProcessAffinity & ( ( DWORD_PTR )1 << i ) );
+ }
+ }
+ }
+ return coreAffinity;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Name: CpuidImpl::IsSupported
+ // Desc: Indicates if a CpuidImpl object is supported on this platform.
+ // Support is only granted on Intel and AMD platforms where the current
+ // calling process has security rights to query process affinity and
+ // change it if the process and system affinity differ. CpuidImpl is
+ // also not supported if thread affinity cannot be set on systems with
+ // more than 1 logical processor.
+ //-----------------------------------------------------------------------------
+ static BOOL IsSupported()
+ {
+ BOOL bSupported = Cpuid::IsVendor( GenuineIntel )
+ || Cpuid::IsVendor( AuthenticAMD );
+
+ if( bSupported )
+ {
+ DWORD_PTR dwProcessAffinity, dwSystemAffinity;
+ HANDLE hProcess = GetCurrentProcess();
+
+ // Query process affinity mask
+ bSupported = GetProcessAffinityMask( hProcess, &dwProcessAffinity, &dwSystemAffinity );
+ if( bSupported )
+ {
+ if( dwProcessAffinity != dwSystemAffinity )
+ {
+ // The process and system affinities differ. Attempt to set
+ // the process affinity to the system affinity.
+ bSupported = SetProcessAffinityMask( hProcess, dwSystemAffinity );
+ if( bSupported )
+ {
+ // Restore previous process affinity
+ bSupported = SetProcessAffinityMask( hProcess, dwProcessAffinity );
+ }
+ }
+
+ if( bSupported && ( dwSystemAffinity > 1 ) )
+ {
+ // Attempt to set the thread affinity
+ HANDLE hThread = GetCurrentThread();
+ DWORD_PTR dwThreadAffinity = SetThreadAffinityMask( hThread, dwProcessAffinity );
+ if( dwThreadAffinity )
+ {
+ // Restore the previous thread affinity
+ bSupported = 0 != SetThreadAffinityMask( hThread, dwThreadAffinity );
+ }
+ else
+ {
+ bSupported = FALSE;
+ }
+ }
+ }
+ }
+ return bSupported;
+ }
+
+private:
+
+ //-----------------------------------------------------------------------------
+ // Name: CpuidImpl::AddUniquePkgCoreId_
+ // Desc: Adds the package/core id extracted from the APIC ID at m_apicIds[idx]
+ // in the if the package/core id is unique to the pkgCoreIds array.
+ // nPkgCore is an in/out parm that will reflect the total number of items
+ // in pkgCoreIds array. It will be incrememted if a unique package/core
+ // id is found and added.
+ //-----------------------------------------------------------------------------
+ void AddUniquePkgCoreId_( DWORD idx, BYTE* pkgCoreIds, DWORD& nPkgCoreIds ) const
+ {
+ _ASSERT( idx < m_nItems );
+ _ASSERT( NULL != pkgCoreIds );
+
+ DWORD j;
+ for( j = 0; j < nPkgCoreIds; ++j )
+ {
+ if( pkgCoreIds[j] == m_apicExtractor.PackageCoreId( m_apicIds[idx] ) )
+ break;
+ }
+ if( j == nPkgCoreIds )
+ {
+ pkgCoreIds[j] = m_apicExtractor.PackageCoreId( m_apicIds[idx] );
+ ++nPkgCoreIds;
+ }
+ }
+
+ // Private Members
+ BYTE m_apicIds[MaxLogicalProcessors];
+ BYTE m_nItems;
+ ApicExtractor m_apicExtractor;
+
+ // Supported Vendor Strings
+ static const char GenuineIntel[];
+ static const char AuthenticAMD[];
+};
+
+// Static initialization of vendor strings
+const char CpuidImpl::GenuineIntel[] = "GenuineIntel";
+const char CpuidImpl::AuthenticAMD[] = "AuthenticAMD";
+
+} // unnamed-namespace
+
+//-------------------------------------------------------------------------------------
+// Name: CpuTopology::CpuTopology
+// Desc: Initializes this object with the appropriately supported cpu topology
+// implementation object.
+//-------------------------------------------------------------------------------------
+CpuTopology::CpuTopology( BOOL bForceCpuid ) : m_pImpl( NULL )
+{
+ ForceCpuid( bForceCpuid );
+}
+
+//-------------------------------------------------------------------------------------
+// Name: CpuTopology::~CpuTopology
+//-------------------------------------------------------------------------------------
+CpuTopology::~CpuTopology()
+{
+ Destroy_();
+}
+
+//-------------------------------------------------------------------------------------
+// Name: CpuTopology::NumberOfProcessCores
+// Desc: Gets the total number of physical processor cores available to the current
+// process.
+//-------------------------------------------------------------------------------------
+DWORD CpuTopology::NumberOfProcessCores() const
+{
+ return m_pImpl->NumberOfProcessCores();
+}
+
+//-------------------------------------------------------------------------------------
+// Name: CpuTopology::NumberOfSystemCores
+// Desc: Gets the total number of physical processor cores enabled on the system.
+//-------------------------------------------------------------------------------------
+DWORD CpuTopology::NumberOfSystemCores() const
+{
+ return m_pImpl->NumberOfSystemCores();
+}
+
+//-------------------------------------------------------------------------------------
+// Name: CpuTopology::CoreAffinityMask
+// Desc: Gets an affinity mask that corresponds to the requested processor core.
+//-------------------------------------------------------------------------------------
+DWORD_PTR CpuTopology::CoreAffinityMask( DWORD coreIdx ) const
+{
+ return m_pImpl->CoreAffinityMask( coreIdx );
+}
+
+//-------------------------------------------------------------------------------------
+// Name: CpuTopology::IsDefaultImpl
+// Desc: Returns TRUE if m_pImpl is a DefaultImpl object, FALSE if not. Used to
+// indicate whether or not the prescribed methods (CPUID or
+// GetLogicalProcessorInformation) are supported on the system.
+//-------------------------------------------------------------------------------------
+BOOL CpuTopology::IsDefaultImpl() const
+{
+ return m_pImpl->IsDefaultImpl();
+}
+
+//-------------------------------------------------------------------------------------
+// Name: CpuTopology::ForceCpuid
+// Desc: Constructs a cpu topology object. If bForce is FALSE, then a GlpiImpl object
+// is first attempted, then CpuidImpl, then finally DefaultImpl. If bForce is
+// TRUE, then GlpiImpl is never attempted.
+//-------------------------------------------------------------------------------------
+void CpuTopology::ForceCpuid( BOOL bForce )
+{
+ Destroy_();
+
+ if( !bForce && GlpiImpl::IsSupported() )
+ {
+ m_pImpl = new GlpiImpl();
+ }
+ else if( CpuidImpl::IsSupported() )
+ {
+ m_pImpl = new CpuidImpl();
+ }
+ else
+ {
+ m_pImpl = new DefaultImpl();
+ }
+}
+
+//-------------------------------------------------------------------------------------
+// Name: CpuTopology::Destroy_
+//-------------------------------------------------------------------------------------
+void CpuTopology::Destroy_()
+{
+ delete m_pImpl;
+ m_pImpl = NULL;
+}
+#endif \ No newline at end of file
diff --git a/external/vpc/tier0/cputopology.h b/external/vpc/tier0/cputopology.h
new file mode 100644
index 0000000..5da2890
--- /dev/null
+++ b/external/vpc/tier0/cputopology.h
@@ -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
diff --git a/external/vpc/tier0/dbg.cpp b/external/vpc/tier0/dbg.cpp
new file mode 100644
index 0000000..8effafb
--- /dev/null
+++ b/external/vpc/tier0/dbg.cpp
@@ -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
+
diff --git a/external/vpc/tier0/etwprof.cpp b/external/vpc/tier0/etwprof.cpp
new file mode 100644
index 0000000..ccd2c76
--- /dev/null
+++ b/external/vpc/tier0/etwprof.cpp
@@ -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
diff --git a/external/vpc/tier0/fasttimer.cpp b/external/vpc/tier0/fasttimer.cpp
new file mode 100644
index 0000000..8ea732c
--- /dev/null
+++ b/external/vpc/tier0/fasttimer.cpp
@@ -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;
diff --git a/external/vpc/tier0/logging.cpp b/external/vpc/tier0/logging.cpp
new file mode 100644
index 0000000..9edf7d2
--- /dev/null
+++ b/external/vpc/tier0/logging.cpp
@@ -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 );
+}
diff --git a/external/vpc/tier0/mem.cpp b/external/vpc/tier0/mem.cpp
new file mode 100644
index 0000000..96468c3
--- /dev/null
+++ b/external/vpc/tier0/mem.cpp
@@ -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
diff --git a/external/vpc/tier0/mem_helpers.cpp b/external/vpc/tier0/mem_helpers.cpp
new file mode 100644
index 0000000..52f2003
--- /dev/null
+++ b/external/vpc/tier0/mem_helpers.cpp
@@ -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
+
diff --git a/external/vpc/tier0/mem_helpers.h b/external/vpc/tier0/mem_helpers.h
new file mode 100644
index 0000000..cdf2bfc
--- /dev/null
+++ b/external/vpc/tier0/mem_helpers.h
@@ -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
diff --git a/external/vpc/tier0/mem_impl_type.h b/external/vpc/tier0/mem_impl_type.h
new file mode 100644
index 0000000..f01b653
--- /dev/null
+++ b/external/vpc/tier0/mem_impl_type.h
@@ -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
diff --git a/external/vpc/tier0/memdbg.cpp b/external/vpc/tier0/memdbg.cpp
new file mode 100644
index 0000000..ad9c153
--- /dev/null
+++ b/external/vpc/tier0/memdbg.cpp
@@ -0,0 +1,2762 @@
+//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
+//
+// Purpose: Memory allocation!
+//
+// $NoKeywords: $
+//===========================================================================//
+
+
+#include "pch_tier0.h"
+
+#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
+
+//#include <malloc.h>
+#include <string.h>
+#include "tier0/dbg.h"
+#include "tier0/stackstats.h"
+#include "tier0/memalloc.h"
+#include "tier0/fasttimer.h"
+#include "mem_helpers.h"
+#ifdef PLATFORM_WINDOWS_PC
+#undef WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <crtdbg.h>
+#endif
+#ifdef OSX
+#include <malloc/malloc.h>
+#include <stdlib.h>
+#endif
+
+#include <map>
+#include <set>
+#include <limits.h>
+#include "tier0/threadtools.h"
+#ifdef _X360
+#include "xbox/xbox_console.h"
+#endif
+
+#ifdef _PS3
+#include "sys/memory.h"
+#include "tls_ps3.h"
+#include "ps3/ps3_helpers.h"
+#include "memoverride_ps3.h"
+#endif
+
+#ifdef USE_LIGHT_MEM_DEBUG
+#undef USE_MEM_DEBUG
+#endif
+
+#if (!defined( POSIX ) && (defined(_DEBUG) || defined(USE_MEM_DEBUG)))
+#pragma message ("USE_MEM_DEBUG is enabled in a release build. Don't check this in!")
+#endif
+
+#include "mem_impl_type.h"
+
+#if MEM_IMPL_TYPE_DBG
+
+#if defined(_WIN32) && ( !defined(_X360) && !defined(_WIN64) )
+//be sure to disable frame pointer omission for all projects. "vpc /nofpo" when using stack traces
+//#define USE_STACK_TRACES
+// or:
+//#define USE_STACK_TRACES_DETAILED
+const size_t STACK_TRACE_LENGTH = 32;
+#endif
+
+//prevent stupid bugs from checking one and not the other
+#if defined( USE_STACK_TRACES_DETAILED ) && !defined( USE_STACK_TRACES )
+#define USE_STACK_TRACES //don't comment me. I'm a safety check
+#endif
+
+#if defined( USE_STACK_TRACES )
+#define SORT_STACK_TRACE_DESCRIPTION_DUMPS
+#endif
+
+#if (defined( USE_STACK_TRACES )) && !(defined( TIER0_FPO_DISABLED ) || defined( _DEBUG ))
+#error Stack traces will not work unless FPO is disabled for every function traced through. Rebuild everything with FPO disabled "vpc /nofpo"
+#endif
+
+//-----------------------------------------------------------------------------
+
+#ifdef _PS3
+MemOverrideRawCrtFunctions_t *g_pMemOverrideRawCrtFns;
+#define DebugAlloc (g_pMemOverrideRawCrtFns->pfn_malloc)
+#define DebugFree (g_pMemOverrideRawCrtFns->pfn_free)
+#elif defined( _X360 )
+#define DebugAlloc DmAllocatePool
+#define DebugFree DmFreePool
+#else
+#define DebugAlloc malloc
+#define DebugFree free
+#endif
+
+#ifdef _WIN32
+int g_DefaultHeapFlags = _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_ALLOC_MEM_DF );
+#else
+int g_DefaultHeapFlags = 0;
+#endif // win32
+
+#if defined( _MEMTEST )
+static char s_szStatsMapName[32];
+static char s_szStatsComment[256];
+#endif
+
+#pragma optimize( "", off )
+//-----------------------------------------------------------------------------
+
+#if defined( USE_STACK_TRACES )
+
+bool GetModuleFromAddress( void *address, char *pResult, int iLength )
+{
+ return GetModuleNameFromAddress( address, pResult, iLength );
+}
+
+bool GetCallerModule( char *pDest, int iLength )
+{
+ void *pCaller;
+ GetCallStack_Fast( &pCaller, 1, 2 );
+
+ return ( pCaller != 0 && GetModuleFromAddress( pCaller, pDest, iLength ) );
+}
+
+//
+// Note: StackDescribe function is non-reentrant:
+// Reason: Stack description is stored in a static buffer.
+// Solution: Passing caller-allocated buffers would allow the
+// function to become reentrant, however the current only client (FindOrCreateFilename)
+// is synchronized with a heap mutex, after retrieving stack description the
+// heap memory will be allocated to copy the text.
+//
+
+char * StackDescribe( void * const *ppAddresses, int nMaxAddresses )
+{
+ static char s_chStackDescription[ 32 * 1024 ];
+ char *pchBuffer = s_chStackDescription;
+
+#if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS ) //Assuming StackDescribe is called iteratively on a sorted set of stacks (as in DumpStackStats()). We can save work by skipping unchanged parts at the beginning of the string.
+ static void *LastCallStack[STACK_TRACE_LENGTH] = { NULL };
+ static char *pEndPos[STACK_TRACE_LENGTH] = { NULL };
+ bool bUseExistingString = true;
+#else
+ s_chStackDescription[ 0 ] = 0;
+#endif
+
+ int k;
+ for ( k = 0; k < nMaxAddresses; ++ k )
+ {
+ if ( !ppAddresses[k] )
+ break;
+
+#if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS )
+ if( bUseExistingString && (k < STACK_TRACE_LENGTH) )
+ {
+ if( ppAddresses[k] == LastCallStack[k] )
+ {
+ pchBuffer = pEndPos[k];
+ continue;
+ }
+ else
+ {
+ //everything from here on is invalidated
+ bUseExistingString = false;
+ for( int clearEntries = k; clearEntries < STACK_TRACE_LENGTH; ++clearEntries ) //wipe out unused entries
+ {
+ LastCallStack[clearEntries] = NULL;
+ pEndPos[clearEntries] = NULL;
+ }
+ //fall through to existing code
+
+ if( k == 0 )
+ *pchBuffer = '\0';
+ else
+ sprintf( pchBuffer, "<--" );
+ }
+ }
+#endif
+ {
+ pchBuffer += strlen( pchBuffer );
+
+ char szTemp[MAX_PATH];
+ szTemp[0] = '\0';
+ uint32 iLine = 0;
+ uint32 iLineDisplacement = 0;
+ uint64 iSymbolDisplacement = 0;
+ if ( GetFileAndLineFromAddress( ppAddresses[k], szTemp, MAX_PATH, iLine, &iLineDisplacement ) )
+ {
+ char const *pchFileName = szTemp + strlen( szTemp );
+ for ( size_t numSlashesAllowed = 2; pchFileName > szTemp; --pchFileName )
+ {
+ if ( *pchFileName == '\\' )
+ {
+ if ( numSlashesAllowed-- )
+ continue;
+ else
+ break;
+ }
+ }
+ sprintf( pchBuffer, iLineDisplacement ? "%s:%d+0x%I32X" : "%s:%d", pchFileName, iLine, iLineDisplacement );
+ }
+ else if ( GetSymbolNameFromAddress( ppAddresses[k], szTemp, MAX_PATH, &iSymbolDisplacement ) )
+ {
+ sprintf( pchBuffer, ( iSymbolDisplacement > 0 && !( iSymbolDisplacement >> 63 ) ) ? "%s+0x%llX" : "%s", szTemp, iSymbolDisplacement );
+ }
+ else
+ {
+ sprintf( pchBuffer, "#0x%08p", ppAddresses[k] );
+ }
+
+ pchBuffer += strlen( pchBuffer );
+ sprintf( pchBuffer, "<--" );
+
+#if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS )
+ if( k < STACK_TRACE_LENGTH )
+ {
+ LastCallStack[k] = ppAddresses[k];
+ pEndPos[k] = pchBuffer;
+ }
+#endif
+ }
+ }
+ *pchBuffer = 0;
+
+#if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS )
+ for( ; k < STACK_TRACE_LENGTH; ++k ) //wipe out unused entries
+ {
+ LastCallStack[k] = NULL;
+ pEndPos[k] = NULL;
+ }
+#endif
+
+ return s_chStackDescription;
+}
+
+#else
+
+#define GetModuleFromAddress( address, pResult, iLength ) ( ( *pResult = 0 ), 0)
+#define GetCallerModule( pDest, iLength ) false
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+
+// NOTE: This exactly mirrors the dbg header in the MSDEV crt
+// eventually when we write our own allocator, we can kill this
+struct CrtDbgMemHeader_t
+{
+ unsigned char m_Reserved[8];
+ const char *m_pFileName;
+ int m_nLineNumber;
+ unsigned char m_Reserved2[16];
+};
+
+struct Sentinal_t
+{
+ DWORD value[4];
+};
+
+Sentinal_t g_HeadSentinelAllocated =
+{
+ 0xeee1beef,
+ 0xeee1f00d,
+ 0xbd122969,
+ 0xbeefbeef,
+};
+
+Sentinal_t g_HeadSentinelFree =
+{
+ 0xdeadbeef,
+ 0xbaadf00d,
+ 0xbd122969,
+ 0xdeadbeef,
+};
+
+Sentinal_t g_TailSentinel =
+{
+ 0xbaadf00d,
+ 0xbd122969,
+ 0xdeadbeef,
+ 0xbaadf00d,
+};
+
+const byte g_FreeFill = 0xdd;
+
+enum DbgMemHeaderBlockType_t
+{
+ BLOCKTYPE_FREE,
+ BLOCKTYPE_ALLOCATED
+};
+
+struct DbgMemHeader_t
+#if !defined( _DEBUG ) || defined( _PS3 )
+ : CrtDbgMemHeader_t
+#endif
+{
+ size_t nLogicalSize;
+#if defined( USE_STACK_TRACES )
+ unsigned int nStatIndex;
+ byte reserved[16 - (sizeof(unsigned int) * 2)]; // MS allocator always returns mem aligned on 16 bytes, which some of our code depends on
+#else
+ byte reserved[16 - sizeof(unsigned int)]; // MS allocator always returns mem aligned on 16 bytes, which some of our code depends on
+#endif
+ Sentinal_t sentinal;
+};
+
+const int g_nRecentFrees = ( IsPC() ) ? 8192 : 512;
+DbgMemHeader_t ** GetRecentFrees() { static DbgMemHeader_t **g_pRecentFrees = (DbgMemHeader_t**)
+#ifdef _PS3
+g_pMemOverrideRawCrtFns->pfn_calloc
+#else
+calloc
+#endif
+( g_nRecentFrees, sizeof(DbgMemHeader_t *) );
+return g_pRecentFrees; }
+uint32 volatile g_iNextFreeSlot;
+
+uint32 volatile g_break_BytesFree = 0xffffffff;
+
+void LMDReportInvalidBlock( DbgMemHeader_t *pHeader, const char *pszMessage )
+{
+ char szMsg[256];
+ if ( pHeader )
+ {
+ sprintf( szMsg, "HEAP IS CORRUPT: %s (block 0x%x, %d bytes)\n", pszMessage, (size_t)( ((byte*) pHeader) + sizeof( DbgMemHeader_t ) ), pHeader->nLogicalSize );
+ }
+ else
+ {
+ sprintf( szMsg, "HEAP IS CORRUPT: %s\n", pszMessage );
+ }
+ Assert( !"HEAP IS CORRUPT!" );
+ DebuggerBreak();
+}
+
+void LMDValidateBlock( DbgMemHeader_t *pHeader, bool bFreeList )
+{
+ if ( memcmp( &pHeader->sentinal, bFreeList ? &g_HeadSentinelFree : &g_HeadSentinelAllocated, sizeof(Sentinal_t) ) != 0 )
+ {
+ LMDReportInvalidBlock( pHeader, "Head sentinel corrupt" );
+ }
+ if ( memcmp( ((Sentinal_t *)(( ((byte*) pHeader) + sizeof( DbgMemHeader_t ) + pHeader->nLogicalSize ))), &g_TailSentinel, sizeof(Sentinal_t) ) != 0 )
+ {
+ LMDReportInvalidBlock( pHeader, "Tail sentinel corrupt" );
+ }
+ if ( bFreeList )
+ {
+ byte *pCur = (byte *)pHeader + sizeof(DbgMemHeader_t);
+ byte *pLimit = pCur + pHeader->nLogicalSize;
+ while ( pCur != pLimit )
+ {
+ if ( *pCur++ != g_FreeFill )
+ {
+ LMDReportInvalidBlock( pHeader, "Write after free" );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+
+#if defined( _DEBUG ) && !defined( POSIX )
+#define GetCrtDbgMemHeader( pMem ) ((CrtDbgMemHeader_t*)((DbgMemHeader_t*)pMem - 1) - 1)
+#elif defined( OSX )
+DbgMemHeader_t *GetCrtDbgMemHeader( void *pMem );
+#else
+#define GetCrtDbgMemHeader( pMem ) ((DbgMemHeader_t*)(pMem) - 1)
+#endif
+
+#if defined( USE_STACK_TRACES )
+#define GetAllocationStatIndex_Internal( pMem ) ( ((DbgMemHeader_t*)pMem - 1)->nStatIndex )
+#endif
+
+#ifdef OSX
+DbgMemHeader_t *GetCrtDbgMemHeader( void *pMem )
+{
+ size_t msize = malloc_size( pMem );
+ return (DbgMemHeader_t *)( (char *)pMem + msize - sizeof(DbgMemHeader_t) );
+}
+#endif
+
+
+inline void *InternalMalloc( size_t nSize, const char *pFileName, int nLine )
+{
+#if defined( POSIX ) || defined( _PS3 )
+ void *pAllocedMem = NULL;
+#ifdef OSX
+ pAllocedMem = malloc_zone_malloc( malloc_default_zone(), nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pAllocedMem );
+#elif defined( _PS3 )
+ pAllocedMem = (g_pMemOverrideRawCrtFns->pfn_malloc)( nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pAllocedMem;
+ *((void**)pInternalMem->m_Reserved2) = pAllocedMem;
+#else
+ pAllocedMem = malloc( nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pAllocedMem;
+#endif
+
+ pInternalMem->m_pFileName = pFileName;
+ pInternalMem->m_nLineNumber = nLine;
+ pInternalMem->nLogicalSize = nSize;
+ *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
+
+ pInternalMem->sentinal = g_HeadSentinelAllocated;
+ *( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nSize ) ) = g_TailSentinel;
+ LMDValidateBlock( pInternalMem, false );
+
+#ifdef OSX
+ return pAllocedMem;
+#else
+ return pInternalMem + 1;
+#endif
+
+#else // WIN32
+ DbgMemHeader_t *pInternalMem;
+#if !defined( _DEBUG )
+ pInternalMem = (DbgMemHeader_t *)malloc( nSize + sizeof(DbgMemHeader_t) );
+ pInternalMem->m_pFileName = pFileName;
+ pInternalMem->m_nLineNumber = nLine;
+#else
+ pInternalMem = (DbgMemHeader_t *)_malloc_dbg( nSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
+#endif
+
+ pInternalMem->nLogicalSize = nSize;
+ return pInternalMem + 1;
+#endif // WIN32
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+inline void *InternalMallocAligned( size_t nSize, size_t align, const char *pFileName, int nLine )
+{
+#if defined( POSIX ) || defined( _PS3 )
+ void *pAllocedMem = NULL;
+#ifdef OSX
+ pAllocedMem = malloc_zone_malloc( malloc_default_zone(), nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pAllocedMem );
+#elif defined( _PS3 )
+ size_t numWastedAlignPages = ( sizeof( DbgMemHeader_t ) / align );
+ if ( align * numWastedAlignPages < sizeof( DbgMemHeader_t ) )
+ ++ numWastedAlignPages;
+ size_t nSizeRequired = nSize + numWastedAlignPages*align + sizeof( Sentinal_t );
+ pAllocedMem = (g_pMemOverrideRawCrtFns->pfn_memalign)( align, nSizeRequired );
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( ((char*)pAllocedMem) + numWastedAlignPages*align );
+ *((void**)pInternalMem->m_Reserved2) = pAllocedMem;
+#else
+ pAllocedMem = malloc( nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pAllocedMem;
+#endif
+
+ pInternalMem->m_pFileName = pFileName;
+ pInternalMem->m_nLineNumber = nLine;
+ pInternalMem->nLogicalSize = nSize;
+ *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
+
+ pInternalMem->sentinal = g_HeadSentinelAllocated;
+ *( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nSize ) ) = g_TailSentinel;
+ LMDValidateBlock( pInternalMem, false );
+
+#ifdef OSX
+ return pAllocedMem;
+#else
+ return pInternalMem + 1;
+#endif
+
+#else // WIN32
+ DbgMemHeader_t *pInternalMem;
+#if !defined( _DEBUG )
+ pInternalMem = (DbgMemHeader_t *)malloc( nSize + sizeof(DbgMemHeader_t) );
+ pInternalMem->m_pFileName = pFileName;
+ pInternalMem->m_nLineNumber = nLine;
+#else
+ pInternalMem = (DbgMemHeader_t *)_malloc_dbg( nSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
+#endif
+
+ pInternalMem->nLogicalSize = nSize;
+ return pInternalMem + 1;
+#endif // WIN32
+}
+#endif
+
+inline void *InternalRealloc( void *pMem, size_t nNewSize, const char *pFileName, int nLine )
+{
+ if ( !pMem )
+ return InternalMalloc( nNewSize, pFileName, nLine );
+
+#ifdef POSIX
+ void *pNewAllocedMem = NULL;
+#ifdef OSX
+ pNewAllocedMem = (DbgMemHeader_t *)malloc_zone_realloc( malloc_default_zone(), pMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pNewAllocedMem );
+#elif defined( _PS3 )
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
+ pNewAllocedMem = (DbgMemHeader_t *)(g_pMemOverrideRawCrtFns->pfn_realloc)( *((void**)pInternalMem->m_Reserved2), nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ pInternalMem = (DbgMemHeader_t *)pNewAllocedMem;
+ *((void**)pInternalMem->m_Reserved2) = pNewAllocedMem;
+#else
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
+ pNewAllocedMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ pInternalMem = (DbgMemHeader_t *)pNewAllocedMem;
+#endif
+
+ pInternalMem->m_pFileName = pFileName;
+ pInternalMem->m_nLineNumber = nLine;
+ pInternalMem->nLogicalSize = static_cast<unsigned int>( nNewSize );
+ *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
+
+ pInternalMem->sentinal = g_HeadSentinelAllocated;
+ *( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nNewSize ) ) = g_TailSentinel;
+ LMDValidateBlock( pInternalMem, false );
+
+#ifdef OSX
+ return pNewAllocedMem;
+#else
+ return pInternalMem + 1;
+#endif
+
+#else // WIN32
+ DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
+#if !defined( _DEBUG )
+ pInternalMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) );
+ pInternalMem->m_pFileName = pFileName;
+ pInternalMem->m_nLineNumber = nLine;
+#else
+ pInternalMem = (DbgMemHeader_t *)_realloc_dbg( pInternalMem, nNewSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
+#endif
+
+ pInternalMem->nLogicalSize = nNewSize;
+ return pInternalMem + 1;
+#endif // WIN32
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+inline void *InternalReallocAligned( void *pMem, size_t nNewSize, size_t align, const char *pFileName, int nLine )
+{
+ if ( !pMem )
+ return InternalMallocAligned( nNewSize, align, pFileName, nLine );
+
+#ifdef POSIX
+ void *pNewAllocedMem = NULL;
+#ifdef OSX
+ pNewAllocedMem = (DbgMemHeader_t *)malloc_zone_realloc( malloc_default_zone(), pMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pNewAllocedMem );
+#elif defined( _PS3 )
+ size_t numWastedAlignPages = ( sizeof( DbgMemHeader_t ) / align );
+ if ( align * numWastedAlignPages < sizeof( DbgMemHeader_t ) )
+ ++ numWastedAlignPages;
+ size_t nSizeRequired = nNewSize + numWastedAlignPages*align + sizeof( Sentinal_t );
+
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
+ pNewAllocedMem = (DbgMemHeader_t *)(g_pMemOverrideRawCrtFns->pfn_reallocalign)( *((void**)pInternalMem->m_Reserved2), nSizeRequired, align );
+ pInternalMem = GetCrtDbgMemHeader( ((char*)pNewAllocedMem) + numWastedAlignPages*align );
+ *((void**)pInternalMem->m_Reserved2) = pNewAllocedMem;
+#else
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
+ pNewAllocedMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
+ pInternalMem = (DbgMemHeader_t *)pNewAllocedMem;
+#endif
+
+ pInternalMem->m_pFileName = pFileName;
+ pInternalMem->m_nLineNumber = nLine;
+ pInternalMem->nLogicalSize = static_cast<unsigned int>( nNewSize );
+ *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
+
+ pInternalMem->sentinal = g_HeadSentinelAllocated;
+ *( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nNewSize ) ) = g_TailSentinel;
+ LMDValidateBlock( pInternalMem, false );
+
+#ifdef OSX
+ return pNewAllocedMem;
+#else
+ return pInternalMem + 1;
+#endif
+
+#else // WIN32
+ DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
+#if !defined( _DEBUG )
+ pInternalMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) );
+ pInternalMem->m_pFileName = pFileName;
+ pInternalMem->m_nLineNumber = nLine;
+#else
+ pInternalMem = (DbgMemHeader_t *)_realloc_dbg( pInternalMem, nNewSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
+#endif
+
+ pInternalMem->nLogicalSize = nNewSize;
+ return pInternalMem + 1;
+#endif // WIN32
+}
+#endif
+
+inline void InternalFree( void *pMem )
+{
+ if ( !pMem )
+ return;
+
+ DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
+
+#if defined( POSIX )
+ // Record it in recent free blocks list
+ DbgMemHeader_t **pRecentFrees = GetRecentFrees();
+ uint32 iNextSlot = ThreadInterlockedIncrement( &g_iNextFreeSlot );
+ iNextSlot %= g_nRecentFrees;
+
+ if ( memcmp( &pInternalMem->sentinal, &g_HeadSentinelAllocated, sizeof( Sentinal_t ) ) != 0 )
+ {
+ Assert( !"Double Free or Corrupt Block Header!" );
+ DebuggerBreak();
+ }
+ LMDValidateBlock( pInternalMem, false );
+ if ( g_break_BytesFree == pInternalMem->nLogicalSize )
+ {
+ DebuggerBreak();
+ }
+ pInternalMem->sentinal = g_HeadSentinelFree;
+ memset( pMem, g_FreeFill, pInternalMem->nLogicalSize );
+
+ DbgMemHeader_t *pToFree = pInternalMem;
+ if ( pInternalMem->nLogicalSize < 16*1024 )
+ {
+ pToFree = pRecentFrees[iNextSlot];
+ pRecentFrees[iNextSlot] = pInternalMem;
+
+ if ( pToFree )
+ {
+ LMDValidateBlock( pToFree, true );
+ }
+ }
+
+ // Validate several last frees
+ for ( uint32 k = iNextSlot - 1, iteration = 0; iteration < 10; ++ iteration, -- k )
+ {
+ if ( DbgMemHeader_t *pLastFree = pRecentFrees[ k % g_nRecentFrees ] )
+ {
+ LMDValidateBlock( pLastFree, true );
+ }
+ }
+
+ if ( !pToFree )
+ return;
+
+#ifdef OSX
+ malloc_zone_free( malloc_default_zone(), pToFree );
+#elif defined( _PS3 )
+ (g_pMemOverrideRawCrtFns->pfn_free)( *((void**)pToFree->m_Reserved2) );
+#elif LINUX
+ free( pToFree );
+#else
+ free( pToFree );
+#endif
+#elif defined( _DEBUG )
+ _free_dbg( pInternalMem, _NORMAL_BLOCK );
+#else
+ free( pInternalMem );
+#endif
+}
+
+inline size_t InternalMSize( void *pMem )
+{
+#if defined( _PS3 )
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
+ return pInternalMem->nLogicalSize;
+#elif defined(POSIX)
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
+ return pInternalMem->nLogicalSize;
+#elif !defined(_DEBUG)
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
+ return _msize( pInternalMem ) - sizeof(DbgMemHeader_t);
+#else
+ DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
+ return _msize_dbg( pInternalMem, _NORMAL_BLOCK ) - sizeof(DbgMemHeader_t);
+#endif
+}
+
+inline size_t InternalLogicalSize( void *pMem )
+{
+#if defined(POSIX)
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
+#elif !defined(_DEBUG)
+ DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
+#else
+ DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
+#endif
+ return pInternalMem->nLogicalSize;
+}
+
+#ifndef _DEBUG
+#define _CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg ) 0
+#endif
+
+//-----------------------------------------------------------------------------
+
+
+// Custom allocator protects this module from recursing on operator new
+template <class T>
+class CNoRecurseAllocator
+{
+public:
+ // type definitions
+ typedef T value_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+
+ CNoRecurseAllocator() {}
+ CNoRecurseAllocator(const CNoRecurseAllocator&) {}
+ template <class U> CNoRecurseAllocator(const CNoRecurseAllocator<U>&) {}
+ ~CNoRecurseAllocator(){}
+
+ // rebind allocator to type U
+ template <class U > struct rebind { typedef CNoRecurseAllocator<U> other; };
+
+ // return address of values
+ pointer address (reference value) const { return &value; }
+
+ const_pointer address (const_reference value) const { return &value;}
+ size_type max_size() const { return INT_MAX; }
+
+ pointer allocate(size_type num, const void* = 0) { return (pointer)DebugAlloc(num * sizeof(T)); }
+ void deallocate (pointer p, size_type num) { DebugFree(p); }
+ void construct(pointer p, const T& value) { new((void*)p)T(value); }
+ void destroy (pointer p) { p->~T(); }
+};
+
+template <class T1, class T2>
+bool operator==(const CNoRecurseAllocator<T1>&, const CNoRecurseAllocator<T2>&)
+{
+ return true;
+}
+
+template <class T1, class T2>
+bool operator!=(const CNoRecurseAllocator<T1>&, const CNoRecurseAllocator<T2>&)
+{
+ return false;
+}
+
+class CStringLess
+{
+public:
+ bool operator()(const char *pszLeft, const char *pszRight ) const
+ {
+ return ( V_tier0_stricmp( pszLeft, pszRight ) < 0 );
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+#pragma warning( disable:4074 ) // warning C4074: initializers put in compiler reserved initialization area
+#pragma init_seg( compiler )
+
+//-----------------------------------------------------------------------------
+// NOTE! This should never be called directly from leaf code
+// Just use new,delete,malloc,free etc. They will call into this eventually
+//-----------------------------------------------------------------------------
+class CDbgMemAlloc : public IMemAlloc
+{
+public:
+ CDbgMemAlloc();
+ virtual ~CDbgMemAlloc();
+
+ // 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 );
+
+#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
+
+ // 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 );
+
+ virtual void *RegionAlloc( int region, size_t nSize ) { return Alloc( nSize ); }
+ virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine ) { return Alloc( nSize, pFileName, nLine ); }
+
+ // Returns the size of a particular allocation (NOTE: may be larger than the size requested!)
+ 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 );
+
+ // handles storing allocation info for coroutines
+ virtual uint32 GetDebugInfoSize();
+ virtual void SaveDebugInfo( void *pvDebugInfo );
+ virtual void RestoreDebugInfo( const void *pvDebugInfo );
+ virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine );
+
+ // FIXME: Remove when we have our own allocator
+ virtual void* CrtSetReportFile( int nRptType, void* hFile );
+ virtual void* CrtSetReportHook( void* pfnNewHook );
+ virtual int CrtDbgReport( int nRptType, const char * szFile,
+ int nLine, const char * szModule, const char * szFormat );
+
+ virtual int heapchk();
+
+ virtual bool IsDebugHeap() { return true; }
+
+ virtual int GetVersion() { return MEMALLOC_VERSION; }
+
+ virtual void CompactHeap()
+ {
+#if defined( _X360 ) && defined( _DEBUG )
+ HeapCompact( GetProcessHeap(), 0 );
+#endif
+ }
+
+ virtual void CompactIncremental() {}
+ virtual void OutOfMemory( size_t nBytesAttempted = 0 ) {}
+
+ virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) { return NULL; } // debug heap doesn't attempt retries
+
+ void SetStatsExtraInfo( const char *pMapName, const char *pComment )
+ {
+#if defined( _MEMTEST )
+ strncpy( s_szStatsMapName, pMapName, sizeof( s_szStatsMapName ) );
+ s_szStatsMapName[sizeof( s_szStatsMapName ) - 1] = '\0';
+
+ strncpy( s_szStatsComment, pComment, sizeof( s_szStatsComment ) );
+ s_szStatsComment[sizeof( s_szStatsComment ) - 1] = '\0';
+#endif
+ }
+
+ virtual size_t MemoryAllocFailed();
+ void SetCRTAllocFailed( size_t nMemSize );
+
+ enum
+ {
+ BYTE_COUNT_16 = 0,
+ BYTE_COUNT_32,
+ BYTE_COUNT_128,
+ BYTE_COUNT_2048,
+ BYTE_COUNT_GREATER,
+
+ NUM_BYTE_COUNT_BUCKETS
+ };
+
+private:
+ struct MemInfo_t
+ {
+#if defined( USE_STACK_TRACES )
+ DECLARE_CALLSTACKSTATSTRUCT();
+ DECLARE_CALLSTACKSTATSTRUCT_FIELDDESCRIPTION();
+#endif
+
+ MemInfo_t()
+ {
+ memset( this, 0, sizeof(*this) );
+ }
+
+ // Size in bytes
+ size_t m_nCurrentSize;
+ size_t m_nPeakSize;
+ size_t m_nTotalSize;
+ size_t m_nOverheadSize;
+ size_t m_nPeakOverheadSize;
+
+ // Count in terms of # of allocations
+ int m_nCurrentCount;
+ int m_nPeakCount;
+ int m_nTotalCount;
+
+ int m_nSumTargetRange;
+ int m_nCurTargetRange;
+ int m_nMaxTargetRange;
+
+ // Count in terms of # of allocations of a particular size
+ int m_pCount[NUM_BYTE_COUNT_BUCKETS];
+
+ // Time spent allocating + deallocating (microseconds)
+ int64 m_nTime;
+ };
+
+ struct MemInfoKey_FileLine_t
+ {
+ MemInfoKey_FileLine_t( const char *pFileName, int line ) : m_pFileName(pFileName), m_nLine(line) {}
+ bool operator<( const MemInfoKey_FileLine_t &key ) const
+ {
+ int iret = V_tier0_stricmp( m_pFileName, key.m_pFileName );
+ if ( iret < 0 )
+ return true;
+
+ if ( iret > 0 )
+ return false;
+
+ return m_nLine < key.m_nLine;
+ }
+
+ const char *m_pFileName;
+ int m_nLine;
+ };
+
+ // NOTE: Deliberately using STL here because the UTL stuff
+ // is a client of this library; want to avoid circular dependency
+
+ // Maps file name to info
+ typedef std::map< MemInfoKey_FileLine_t, MemInfo_t, std::less<MemInfoKey_FileLine_t>, CNoRecurseAllocator<std::pair<const MemInfoKey_FileLine_t, MemInfo_t> > > StatMap_FileLine_t;
+ typedef StatMap_FileLine_t::iterator StatMapIter_FileLine_t;
+ typedef StatMap_FileLine_t::value_type StatMapEntry_FileLine_t;
+
+ typedef std::set<const char *, CStringLess, CNoRecurseAllocator<const char *> > Filenames_t;
+
+ // Heap reporting method
+ typedef void (*HeapReportFunc_t)( char const *pFormat, ... );
+
+private:
+ // Returns the actual debug info
+ virtual void GetActualDbgInfo( const char *&pFileName, int &nLine );
+
+ // Finds the file in our map
+ MemInfo_t &FindOrCreateEntry( const char *pFileName, int line );
+ const char *FindOrCreateFilename( const char *pFileName );
+
+#if defined( USE_STACK_TRACES )
+ int GetCallStackForIndex( unsigned int index, void **pCallStackOut, int iMaxEntriesOut );
+ friend int GetAllocationCallStack( void *mem, void **pCallStackOut, int iMaxEntriesOut );
+#endif
+
+ // Updates stats
+ 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 );
+#if defined( USE_STACK_TRACES )
+ void RegisterAllocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
+ void RegisterDeallocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
+#endif
+ void RegisterAllocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
+ void RegisterDeallocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
+
+ // Gets the allocation file name
+ const char *GetAllocatonFileName( void *pMem );
+ int GetAllocatonLineNumber( void *pMem );
+
+ // FIXME: specify a spew output func for dumping stats
+ // Stat output
+ void DumpMemInfo( const char *pAllocationName, int line, const MemInfo_t &info );
+ void DumpFileStats();
+#if defined( USE_STACK_TRACES )
+ void DumpMemInfo( void * const CallStack[STACK_TRACE_LENGTH], const MemInfo_t &info );
+ void DumpCallStackFlow( char const *pchFileBase );
+#endif
+ virtual void DumpStats();
+ virtual void DumpStatsFileBase( char const *pchFileBase );
+ virtual void DumpBlockStats( void *p );
+ virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory );
+
+ virtual size_t ComputeMemoryUsedBy( char const *pchSubStr );
+
+ virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes )
+ {
+#if defined( _GAMECONSOLE ) || defined( _WIN32 )
+ extern IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes );
+ return VirtualMemoryManager_AllocateVirtualMemorySection( numMaxBytes );
+#else
+ return NULL;
+#endif
+ }
+
+ virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats )
+ {
+ // TODO: reuse code from GlobalMemoryStatus (though this is only really useful when using CStdMemAlloc...)
+ return 0;
+ }
+
+private:
+ StatMap_FileLine_t m_StatMap_FileLine;
+#if defined( USE_STACK_TRACES )
+ typedef CCallStackStatsGatherer<MemInfo_t, STACK_TRACE_LENGTH, GetCallStack_Fast, CCallStackStatsGatherer_StatMutexPool<128>, CNoRecurseAllocator> CallStackStatsType_t;
+ CallStackStatsType_t m_CallStackStats;
+#endif
+
+ MemInfo_t m_GlobalInfo;
+ CFastTimer m_Timer;
+ bool m_bInitialized;
+ Filenames_t m_Filenames;
+
+ HeapReportFunc_t m_OutputFunc;
+
+ static size_t s_pCountSizes[NUM_BYTE_COUNT_BUCKETS];
+ static const char *s_pCountHeader[NUM_BYTE_COUNT_BUCKETS];
+
+ size_t m_sMemoryAllocFailed;
+};
+
+static char const *g_pszUnknown = "unknown";
+
+#if defined( USE_STACK_TRACES )
+BEGIN_STATSTRUCTDESCRIPTION( CDbgMemAlloc::MemInfo_t )
+ WRITE_STATSTRUCT_FIELDDESCRIPTION();
+END_STATSTRUCTDESCRIPTION()
+
+
+BEGIN_STATSTRUCTFIELDDESCRIPTION( CDbgMemAlloc::MemInfo_t )
+ DEFINE_STATSTRUCTFIELD( m_nCurrentSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nPeakSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nTotalSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nOverheadSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nPeakOverheadSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nCurrentCount, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nPeakCount, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nTotalCount, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nSumTargetRange, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nCurTargetRange, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nMaxTargetRange, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_16, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_32, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_128, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_2048, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_GREATER, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
+ DEFINE_STATSTRUCTFIELD( m_nTime, BasicStatStructFieldDesc, ( BSSFT_INT64, BSSFCM_ADD ) )
+END_STATSTRUCTFIELDDESCRIPTION()
+#endif
+
+
+//-----------------------------------------------------------------------------
+
+const int DBG_INFO_STACK_DEPTH = 32;
+
+struct DbgInfoStack_t
+{
+ const char *m_pFileName;
+ int m_nLine;
+};
+
+#ifdef _PS3
+#ifndef _CERT
+extern TLSGlobals * ( *g_pfnElfGetTlsGlobals )();
+#define IfDbgInfoIsReady() if ( TLSGlobals *IfDbgInfoIsReady_pTlsGlobals = g_pfnElfGetTlsGlobals ? g_pfnElfGetTlsGlobals() : NULL )
+#else
+#define IfDbgInfoIsReady() if ( TLSGlobals *IfDbgInfoIsReady_pTlsGlobals = GetTLSGlobals() )
+#endif
+#define g_DbgInfoStack ( ( DbgInfoStack_t *& ) IfDbgInfoIsReady_pTlsGlobals->pMallocDbgInfoStack )
+#define g_nDbgInfoStackDepth ( IfDbgInfoIsReady_pTlsGlobals->nMallocDbgInfoStackDepth )
+#else
+CTHREADLOCALPTR( DbgInfoStack_t) g_DbgInfoStack CONSTRUCT_EARLY;
+CTHREADLOCALINT g_nDbgInfoStackDepth CONSTRUCT_EARLY;
+#define IfDbgInfoIsReady() if (true)
+#endif
+
+#ifdef _PS3
+struct CDbgMemAlloc_GetRawCrtMemOverrideFuncs_Early
+{
+ CDbgMemAlloc_GetRawCrtMemOverrideFuncs_Early()
+ {
+ malloc_managed_size mms;
+ mms.current_inuse_size = 0x12345678;
+ mms.current_system_size = 0x09ABCDEF;
+ mms.max_system_size = 0;
+ int iResult = malloc_stats( &mms );
+ g_pMemOverrideRawCrtFns = reinterpret_cast< MemOverrideRawCrtFunctions_t * >( iResult );
+ }
+}
+g_CDbgMemAlloc_GetRawCrtMemOverrideFuncs_Early CONSTRUCT_EARLY;
+#endif
+
+//-----------------------------------------------------------------------------
+// Singleton...
+//-----------------------------------------------------------------------------
+static CDbgMemAlloc s_DbgMemAlloc CONSTRUCT_EARLY;
+
+#ifdef _PS3
+
+IMemAlloc *g_pMemAllocInternalPS3 = &s_DbgMemAlloc;
+PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3_IMPL
+
+#else // !_PS3
+
+#ifndef TIER0_VALIDATE_HEAP
+IMemAlloc *g_pMemAlloc CONSTRUCT_EARLY = &s_DbgMemAlloc;
+#else
+IMemAlloc *g_pActualAlloc = &s_DbgMemAlloc;
+#endif
+
+#endif // _PS3
+
+
+//-----------------------------------------------------------------------------
+
+CThreadMutex g_DbgMemMutex CONSTRUCT_EARLY;
+
+#define HEAP_LOCK() AUTO_LOCK( g_DbgMemMutex )
+
+
+//-----------------------------------------------------------------------------
+// Byte count buckets
+//-----------------------------------------------------------------------------
+size_t CDbgMemAlloc::s_pCountSizes[CDbgMemAlloc::NUM_BYTE_COUNT_BUCKETS] =
+{
+ 16, 32, 128, 2048, INT_MAX
+};
+
+const char *CDbgMemAlloc::s_pCountHeader[CDbgMemAlloc::NUM_BYTE_COUNT_BUCKETS] =
+{
+ "<=16 byte allocations",
+ "17-32 byte allocations",
+ "33-128 byte allocations",
+ "129-2048 byte allocations",
+ ">2048 byte allocations"
+};
+
+
+size_t g_TargetCountRangeMin = 0, g_TargetCountRangeMax = 0;
+
+//-----------------------------------------------------------------------------
+// Standard output
+//-----------------------------------------------------------------------------
+static FILE* s_DbgFile;
+
+static void DefaultHeapReportFunc( char const *pFormat, ... )
+{
+ va_list args;
+ va_start( args, pFormat );
+ vfprintf( s_DbgFile, pFormat, args );
+ va_end( args );
+}
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CDbgMemAlloc::CDbgMemAlloc() : m_sMemoryAllocFailed( (size_t)0 )
+{
+ CClockSpeedInit::Init();
+
+ m_OutputFunc = DefaultHeapReportFunc;
+ m_bInitialized = true;
+
+ if ( !IsDebug() && !IsX360() )
+ {
+ Plat_DebugString( "USE_MEM_DEBUG is enabled in a release build. Don't check this in!\n" );
+ }
+
+#ifdef _PS3
+ g_pMemAllocInternalPS3 = &s_DbgMemAlloc;
+ PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3.m_pMemAllocCached = &s_DbgMemAlloc;
+ malloc_managed_size mms;
+ mms.current_inuse_size = 0x12345678;
+ mms.current_system_size = 0x09ABCDEF;
+ mms.max_system_size = reinterpret_cast< size_t >( this );
+ int iResult = malloc_stats( &mms );
+ g_pMemOverrideRawCrtFns = reinterpret_cast< MemOverrideRawCrtFunctions_t * >( iResult );
+#endif
+}
+
+CDbgMemAlloc::~CDbgMemAlloc()
+{
+ Filenames_t::const_iterator iter = m_Filenames.begin();
+ while(iter != m_Filenames.end())
+ {
+ char *pFileName = (char*)(*iter);
+ free( pFileName );
+ iter++;
+ }
+ m_bInitialized = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Release versions
+//-----------------------------------------------------------------------------
+
+void *CDbgMemAlloc::Alloc( size_t nSize )
+{
+/*
+ // NOTE: Uncomment this to find unknown allocations
+ const char *pFileName = g_pszUnknown;
+ int nLine;
+ GetActualDbgInfo( pFileName, nLine );
+ if (pFileName == g_pszUnknown)
+ {
+ int x = 3;
+ }
+*/
+ char szModule[MAX_PATH];
+ if ( GetCallerModule( szModule, MAX_PATH ) )
+ {
+ return Alloc( nSize, szModule, 0 );
+ }
+ else
+ {
+ return Alloc( nSize, g_pszUnknown, 0 );
+ }
+// return malloc( nSize );
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+void *CDbgMemAlloc::AllocAlign( size_t nSize, size_t align )
+{
+/*
+ // NOTE: Uncomment this to find unknown allocations
+ const char *pFileName = g_pszUnknown;
+ int nLine;
+ GetActualDbgInfo( pFileName, nLine );
+ if (pFileName == g_pszUnknown)
+ {
+ int x = 3;
+ }
+*/
+ char szModule[MAX_PATH];
+ if ( GetCallerModule( szModule, MAX_PATH ) )
+ {
+ return AllocAlign( nSize, align, szModule, 0 );
+ }
+ else
+ {
+ return AllocAlign( nSize, align, g_pszUnknown, 0 );
+ }
+// return malloc( nSize );
+}
+#endif
+
+void *CDbgMemAlloc::Realloc( void *pMem, size_t nSize )
+{
+/*
+ // NOTE: Uncomment this to find unknown allocations
+ const char *pFileName = g_pszUnknown;
+ int nLine;
+ GetActualDbgInfo( pFileName, nLine );
+ if (pFileName == g_pszUnknown)
+ {
+ int x = 3;
+ }
+*/
+ // FIXME: Should these gather stats?
+ char szModule[MAX_PATH];
+ if ( GetCallerModule( szModule, MAX_PATH ) )
+ {
+ return Realloc( pMem, nSize, szModule, 0 );
+ }
+ else
+ {
+ return Realloc( pMem, nSize, g_pszUnknown, 0 );
+ }
+// return realloc( pMem, nSize );
+}
+
+void CDbgMemAlloc::Free( void *pMem )
+{
+ // FIXME: Should these gather stats?
+ Free( pMem, g_pszUnknown, 0 );
+// free( pMem );
+}
+
+void *CDbgMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize )
+{
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Force file + line information for an allocation
+//-----------------------------------------------------------------------------
+void CDbgMemAlloc::PushAllocDbgInfo( const char *pFileName, int nLine )
+{
+ IfDbgInfoIsReady()
+ {
+
+ if ( g_DbgInfoStack == NULL )
+ {
+ g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
+ g_nDbgInfoStackDepth = -1;
+ }
+
+ ++g_nDbgInfoStackDepth;
+ Assert( g_nDbgInfoStackDepth < DBG_INFO_STACK_DEPTH );
+ g_DbgInfoStack[g_nDbgInfoStackDepth].m_pFileName = FindOrCreateFilename( pFileName );
+ g_DbgInfoStack[g_nDbgInfoStackDepth].m_nLine = nLine;
+
+ }
+}
+
+void CDbgMemAlloc::PopAllocDbgInfo()
+{
+ IfDbgInfoIsReady()
+ {
+
+ if ( g_DbgInfoStack == NULL )
+ {
+ g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
+ g_nDbgInfoStackDepth = -1;
+ }
+
+ --g_nDbgInfoStackDepth;
+ Assert( g_nDbgInfoStackDepth >= -1 );
+
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// handles storing allocation info for coroutines
+//-----------------------------------------------------------------------------
+uint32 CDbgMemAlloc::GetDebugInfoSize()
+{
+ return sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH + sizeof( int32 );
+}
+
+void CDbgMemAlloc::SaveDebugInfo( void *pvDebugInfo )
+{
+ IfDbgInfoIsReady()
+ {
+ if ( g_DbgInfoStack == NULL )
+ {
+ g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
+ g_nDbgInfoStackDepth = -1;
+ }
+
+ int32 *pnStackDepth = (int32*) pvDebugInfo;
+ *pnStackDepth = g_nDbgInfoStackDepth;
+ memcpy( pnStackDepth+1, &g_DbgInfoStack[0], sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH );
+ }
+}
+
+void CDbgMemAlloc::RestoreDebugInfo( const void *pvDebugInfo )
+{
+ IfDbgInfoIsReady()
+ {
+ if ( g_DbgInfoStack == NULL )
+ {
+ g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
+ g_nDbgInfoStackDepth = -1;
+ }
+
+ const int32 *pnStackDepth = (const int32*) pvDebugInfo;
+ g_nDbgInfoStackDepth = *pnStackDepth;
+ memcpy( &g_DbgInfoStack[0], pnStackDepth+1, sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH );
+ }
+}
+
+void CDbgMemAlloc::InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine )
+{
+ int32 *pnStackDepth = (int32*) pvDebugInfo;
+
+ if( pchRootFileName )
+ {
+ *pnStackDepth = 0;
+
+ DbgInfoStack_t *pStackRoot = (DbgInfoStack_t *)(pnStackDepth + 1);
+ pStackRoot->m_pFileName = FindOrCreateFilename( pchRootFileName );
+ pStackRoot->m_nLine = nLine;
+ }
+ else
+ {
+ *pnStackDepth = -1;
+ }
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the actual debug info
+//-----------------------------------------------------------------------------
+void CDbgMemAlloc::GetActualDbgInfo( const char *&pFileName, int &nLine )
+{
+#if defined( USE_STACK_TRACES_DETAILED )
+ return;
+#endif
+
+ IfDbgInfoIsReady()
+ {
+
+ if ( g_DbgInfoStack == NULL )
+ {
+ g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
+ g_nDbgInfoStackDepth = -1;
+ }
+
+ if ( g_nDbgInfoStackDepth >= 0 && g_DbgInfoStack[0].m_pFileName)
+ {
+ pFileName = g_DbgInfoStack[0].m_pFileName;
+ nLine = g_DbgInfoStack[0].m_nLine;
+ }
+
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const char *CDbgMemAlloc::FindOrCreateFilename( const char *pFileName )
+{
+ // If we created it for the first time, actually *allocate* the filename memory
+ HEAP_LOCK();
+ // This is necessary for shutdown conditions: the file name is stored
+ // in some piece of memory in a DLL; if that DLL becomes unloaded,
+ // we'll have a pointer to crap memory
+
+ if ( !pFileName )
+ {
+ pFileName = g_pszUnknown;
+ }
+
+#if defined( USE_STACK_TRACES_DETAILED )
+{
+
+ // Walk the stack to determine what's causing the allocation
+ void *arrStackAddresses[ 10 ] = { 0 };
+ int numStackAddrRetrieved = GetCallStack_Fast( arrStackAddresses, 10, 2 ); //Skip this function, and either CDbgMemAlloc::Alloc() or CDbgMemAlloc::Realloc()
+ char *szStack = StackDescribe( arrStackAddresses, numStackAddrRetrieved );
+ if ( szStack && *szStack )
+ {
+ pFileName = szStack; // Use the stack description for the allocation
+ }
+
+}
+#endif // #if defined( USE_STACK_TRACES_DETAILED )
+
+ char *pszFilenameCopy;
+ Filenames_t::const_iterator iter = m_Filenames.find( pFileName );
+ if ( iter == m_Filenames.end() )
+ {
+ size_t nLen = strlen(pFileName) + 1;
+ pszFilenameCopy = (char *)DebugAlloc( nLen );
+ memcpy( pszFilenameCopy, pFileName, nLen );
+ m_Filenames.insert( pszFilenameCopy );
+ }
+ else
+ {
+ pszFilenameCopy = (char *)(*iter);
+ }
+
+ return pszFilenameCopy;
+}
+
+//-----------------------------------------------------------------------------
+// Finds the file in our map
+//-----------------------------------------------------------------------------
+CDbgMemAlloc::MemInfo_t &CDbgMemAlloc::FindOrCreateEntry( const char *pFileName, int line )
+{
+ // Oh how I love crazy STL. retval.first == the StatMapIter_t in the std::pair
+ // retval.first->second == the MemInfo_t that's part of the StatMapIter_t
+ std::pair<StatMapIter_FileLine_t, bool> retval;
+ retval = m_StatMap_FileLine.insert( StatMapEntry_FileLine_t( MemInfoKey_FileLine_t( pFileName, line ), MemInfo_t() ) );
+ return retval.first->second;
+}
+
+#if defined( USE_STACK_TRACES )
+int CDbgMemAlloc::GetCallStackForIndex( unsigned int index, void **pCallStackOut, int iMaxEntriesOut )
+{
+ if( iMaxEntriesOut > STACK_TRACE_LENGTH )
+ iMaxEntriesOut = STACK_TRACE_LENGTH;
+
+ CallStackStatsType_t::StackReference stackRef = m_CallStackStats.GetCallStackForIndex( index );
+
+ memcpy( pCallStackOut, stackRef, iMaxEntriesOut * sizeof( void * ) );
+ for( int i = 0; i != iMaxEntriesOut; ++i )
+ {
+ if( pCallStackOut[i] == NULL )
+ return i;
+ }
+ return iMaxEntriesOut;
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Updates stats
+//-----------------------------------------------------------------------------
+void CDbgMemAlloc::RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
+{
+ HEAP_LOCK();
+ RegisterAllocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
+ RegisterAllocation( FindOrCreateEntry( pFileName, nLine ), nLogicalSize, nActualSize, nTime );
+}
+
+void CDbgMemAlloc::RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
+{
+ HEAP_LOCK();
+ RegisterDeallocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
+ RegisterDeallocation( FindOrCreateEntry( pFileName, nLine ), nLogicalSize, nActualSize, nTime );
+}
+
+#if defined( USE_STACK_TRACES )
+void CDbgMemAlloc::RegisterAllocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
+{
+ HEAP_LOCK();
+ RegisterAllocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
+ CCallStackStatsGatherer_StructAccessor_AutoLock<MemInfo_t> entryAccessor = m_CallStackStats.GetEntry( nStatIndex );
+ RegisterAllocation( *entryAccessor.GetStruct(), nLogicalSize, nActualSize, nTime );
+}
+
+void CDbgMemAlloc::RegisterDeallocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
+{
+ HEAP_LOCK();
+ RegisterDeallocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
+ CCallStackStatsGatherer_StructAccessor_AutoLock<MemInfo_t> entryAccessor = m_CallStackStats.GetEntry( nStatIndex );
+ RegisterDeallocation( *entryAccessor.GetStruct(), nLogicalSize, nActualSize, nTime );
+}
+#endif
+
+void CDbgMemAlloc::RegisterAllocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
+{
+ ++info.m_nCurrentCount;
+ ++info.m_nTotalCount;
+ if (info.m_nCurrentCount > info.m_nPeakCount)
+ {
+ info.m_nPeakCount = info.m_nCurrentCount;
+ }
+
+ info.m_nCurrentSize += nLogicalSize;
+ info.m_nTotalSize += nLogicalSize;
+ if (info.m_nCurrentSize > info.m_nPeakSize)
+ {
+ info.m_nPeakSize = info.m_nCurrentSize;
+ }
+
+ if ( nLogicalSize > g_TargetCountRangeMin && nLogicalSize <= g_TargetCountRangeMax )
+ {
+ info.m_nSumTargetRange++;
+ info.m_nCurTargetRange++;
+ if ( info.m_nCurTargetRange > info.m_nMaxTargetRange )
+ {
+ info.m_nMaxTargetRange = info.m_nCurTargetRange;
+ }
+ }
+
+ for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
+ {
+ if (nLogicalSize <= s_pCountSizes[i])
+ {
+ ++info.m_pCount[i];
+ break;
+ }
+ }
+
+ Assert( info.m_nPeakCount >= info.m_nCurrentCount );
+ Assert( info.m_nPeakSize >= info.m_nCurrentSize );
+
+ info.m_nOverheadSize += (nActualSize - nLogicalSize);
+ if (info.m_nOverheadSize > info.m_nPeakOverheadSize)
+ {
+ info.m_nPeakOverheadSize = info.m_nOverheadSize;
+ }
+
+ info.m_nTime += nTime;
+}
+
+void CDbgMemAlloc::RegisterDeallocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
+{
+ --info.m_nCurrentCount;
+ info.m_nCurrentSize -= nLogicalSize;
+
+ for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
+ {
+ if (nLogicalSize <= s_pCountSizes[i])
+ {
+ --info.m_pCount[i];
+ break;
+ }
+ }
+
+ if ( nLogicalSize > g_TargetCountRangeMin && nLogicalSize <= g_TargetCountRangeMax )
+ {
+ info.m_nCurTargetRange--;
+ }
+
+ Assert( info.m_nPeakCount >= info.m_nCurrentCount );
+ Assert( info.m_nPeakSize >= info.m_nCurrentSize );
+ Assert( info.m_nCurrentCount >= 0 );
+ Assert( info.m_nCurrentSize >= 0 );
+
+ info.m_nOverheadSize -= (nActualSize - nLogicalSize);
+
+ info.m_nTime += nTime;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the allocation file name
+//-----------------------------------------------------------------------------
+
+const char *CDbgMemAlloc::GetAllocatonFileName( void *pMem )
+{
+ if (!pMem)
+ return "";
+
+ CrtDbgMemHeader_t *pHeader = GetCrtDbgMemHeader( pMem );
+ if ( pHeader->m_pFileName )
+ return pHeader->m_pFileName;
+ else
+ return g_pszUnknown;
+}
+
+//-----------------------------------------------------------------------------
+// Gets the allocation file name
+//-----------------------------------------------------------------------------
+int CDbgMemAlloc::GetAllocatonLineNumber( void *pMem )
+{
+ if ( !pMem )
+ return 0;
+
+ CrtDbgMemHeader_t *pHeader = GetCrtDbgMemHeader( pMem );
+ return pHeader->m_nLineNumber;
+}
+
+//-----------------------------------------------------------------------------
+// Debug versions of the main allocation methods
+//-----------------------------------------------------------------------------
+void *CDbgMemAlloc::Alloc( size_t nSize, const char *pFileName, int nLine )
+{
+ HEAP_LOCK();
+
+#if defined( USE_STACK_TRACES )
+ unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndex( CCallStackStorage( m_CallStackStats.StackFunction, 1 ) );
+#endif
+
+ if ( !m_bInitialized )
+ {
+ void *pRetval = InternalMalloc( nSize, pFileName, nLine );
+
+#if defined( USE_STACK_TRACES )
+ if( pRetval )
+ {
+ GetAllocationStatIndex_Internal( pRetval ) = iStatEntryIndex;
+ }
+#endif
+
+ return pRetval;
+ }
+
+
+
+ if ( pFileName != g_pszUnknown )
+ pFileName = FindOrCreateFilename( pFileName );
+
+ GetActualDbgInfo( pFileName, nLine );
+
+ /*
+ if ( strcmp( pFileName, "class CUtlVector<int,class CUtlMemory<int> >" ) == 0)
+ {
+ GetActualDbgInfo( pFileName, nLine );
+ }
+ */
+
+ m_Timer.Start();
+ void *pMem = InternalMalloc( nSize, pFileName, nLine );
+ m_Timer.End();
+
+#if defined( USE_STACK_TRACES )
+ if( pMem )
+ {
+ GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
+ }
+#endif
+
+ ApplyMemoryInitializations( pMem, nSize );
+
+#if defined( USE_STACK_TRACES )
+ RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
+#else
+ RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
+#endif
+
+ if ( !pMem )
+ {
+ SetCRTAllocFailed( nSize );
+ }
+ return pMem;
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+void *CDbgMemAlloc::AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine )
+{
+ HEAP_LOCK();
+
+#if defined( USE_STACK_TRACES )
+ unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndexForCurrentCallStack( 1 );
+#endif
+
+ if ( !m_bInitialized )
+ {
+ void *pRetval = InternalMalloc( nSize, pFileName, nLine );
+
+#if defined( USE_STACK_TRACES )
+ if( pRetval )
+ {
+ GetAllocationStatIndex_Internal( pRetval ) = iStatEntryIndex;
+ }
+#endif
+
+ return pRetval;
+ }
+
+
+
+ if ( pFileName != g_pszUnknown )
+ pFileName = FindOrCreateFilename( pFileName );
+
+ GetActualDbgInfo( pFileName, nLine );
+
+ /*
+ if ( strcmp( pFileName, "class CUtlVector<int,class CUtlMemory<int> >" ) == 0)
+ {
+ GetActualDbgInfo( pFileName, nLine );
+ }
+ */
+
+ m_Timer.Start();
+ void *pMem = InternalMallocAligned( nSize, align, pFileName, nLine );
+ m_Timer.End();
+
+#if defined( USE_STACK_TRACES )
+ if( pMem )
+ {
+ GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
+ }
+#endif
+
+ ApplyMemoryInitializations( pMem, nSize );
+
+#if defined( USE_STACK_TRACES )
+ RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
+#else
+ RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
+#endif
+
+ if ( !pMem )
+ {
+ SetCRTAllocFailed( nSize );
+ }
+ return pMem;
+}
+#endif
+
+void *CDbgMemAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine )
+{
+ HEAP_LOCK();
+
+ pFileName = FindOrCreateFilename( pFileName );
+
+#if defined( USE_STACK_TRACES )
+ unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndex( CCallStackStorage( m_CallStackStats.StackFunction, 1 ) );
+#endif
+
+ if ( !m_bInitialized )
+ {
+ pMem = InternalRealloc( pMem, nSize, pFileName, nLine );
+
+#if defined( USE_STACK_TRACES )
+ if( pMem )
+ {
+ GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
+ }
+#endif
+ return pMem;
+ }
+
+ if ( pMem != 0 )
+ {
+#if defined( USE_STACK_TRACES )
+ RegisterDeallocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
+#else
+ RegisterDeallocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
+#endif
+ }
+
+ GetActualDbgInfo( pFileName, nLine );
+
+ m_Timer.Start();
+ pMem = InternalRealloc( pMem, nSize, pFileName, nLine );
+ m_Timer.End();
+
+#if defined( USE_STACK_TRACES )
+ if( pMem )
+ {
+ GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
+ }
+#endif
+
+#if defined( USE_STACK_TRACES )
+ RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
+#else
+ RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
+#endif
+
+ if ( !pMem )
+ {
+ SetCRTAllocFailed( nSize );
+ }
+ return pMem;
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+void *CDbgMemAlloc::ReallocAlign( void *pMem, size_t nSize, size_t align )
+{
+/*
+ // NOTE: Uncomment this to find unknown allocations
+ const char *pFileName = g_pszUnknown;
+ int nLine;
+ GetActualDbgInfo( pFileName, nLine );
+ if (pFileName == g_pszUnknown)
+ {
+ int x = 3;
+ }
+*/
+ char szModule[MAX_PATH];
+ if ( GetCallerModule( szModule, MAX_PATH ) )
+ {
+ return ReallocAlign( pMem, nSize, align, szModule, 0 );
+ }
+ else
+ {
+ return ReallocAlign( pMem, nSize, align, g_pszUnknown, 0 );
+ }
+// return malloc( nSize );
+}
+void *CDbgMemAlloc::ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine )
+{
+ HEAP_LOCK();
+
+ pFileName = FindOrCreateFilename( pFileName );
+
+#if defined( USE_STACK_TRACES )
+ unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndexForCurrentCallStack( 1 );
+#endif
+
+ if ( !m_bInitialized )
+ {
+ pMem = InternalReallocAligned( pMem, nSize, align, pFileName, nLine );
+
+#if defined( USE_STACK_TRACES )
+ if( pMem )
+ {
+ GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
+ }
+#endif
+ return pMem;
+ }
+
+ if ( pMem != 0 )
+ {
+#if defined( USE_STACK_TRACES )
+ RegisterDeallocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
+#else
+ RegisterDeallocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
+#endif
+ }
+
+ GetActualDbgInfo( pFileName, nLine );
+
+ m_Timer.Start();
+ pMem = InternalReallocAligned( pMem, nSize, align, pFileName, nLine );
+ m_Timer.End();
+
+#if defined( USE_STACK_TRACES )
+ if( pMem )
+ {
+ GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
+ }
+#endif
+
+#if defined( USE_STACK_TRACES )
+ RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
+#else
+ RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
+#endif
+
+ if ( !pMem )
+ {
+ SetCRTAllocFailed( nSize );
+ }
+ return pMem;
+}
+#endif
+
+void CDbgMemAlloc::Free( void *pMem, const char * /*pFileName*/, int nLine )
+{
+ if ( !pMem )
+ return;
+
+ HEAP_LOCK();
+
+ if ( !m_bInitialized )
+ {
+ InternalFree( pMem );
+ return;
+ }
+
+ size_t nOldLogicalSize = InternalLogicalSize( pMem );
+ size_t nOldSize = InternalMSize( pMem );
+
+#if defined( USE_STACK_TRACES )
+ unsigned int oldStatIndex = GetAllocationStatIndex_Internal( pMem );
+#else
+ const char *pOldFileName = GetAllocatonFileName( pMem );
+ int oldLine = GetAllocatonLineNumber( pMem );
+#endif
+
+
+ m_Timer.Start();
+ InternalFree( pMem );
+ m_Timer.End();
+
+#if defined( USE_STACK_TRACES )
+ RegisterDeallocation( oldStatIndex, nOldLogicalSize, nOldSize, m_Timer.GetDuration().GetMicroseconds() );
+#else
+ RegisterDeallocation( pOldFileName, oldLine, nOldLogicalSize, nOldSize, m_Timer.GetDuration().GetMicroseconds() );
+#endif
+}
+
+void *CDbgMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine )
+{
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the size of a particular allocation (NOTE: may be larger than the size requested!)
+//-----------------------------------------------------------------------------
+size_t CDbgMemAlloc::GetSize( void *pMem )
+{
+ HEAP_LOCK();
+
+ if ( !pMem )
+ return m_GlobalInfo.m_nCurrentSize;
+
+ return InternalMSize( pMem );
+}
+
+
+//-----------------------------------------------------------------------------
+// FIXME: Remove when we make our own heap! Crt stuff we're currently using
+//-----------------------------------------------------------------------------
+int32 CDbgMemAlloc::CrtSetBreakAlloc( int32 lNewBreakAlloc )
+{
+#ifdef POSIX
+ return 0;
+#else
+ return _CrtSetBreakAlloc( lNewBreakAlloc );
+#endif
+}
+
+int CDbgMemAlloc::CrtSetReportMode( int nReportType, int nReportMode )
+{
+#ifdef POSIX
+ return 0;
+#else
+ return _CrtSetReportMode( nReportType, nReportMode );
+#endif
+}
+
+int CDbgMemAlloc::CrtIsValidHeapPointer( const void *pMem )
+{
+#ifdef POSIX
+ return 0;
+#else
+ return _CrtIsValidHeapPointer( pMem );
+#endif
+}
+
+int CDbgMemAlloc::CrtIsValidPointer( const void *pMem, unsigned int size, int access )
+{
+#ifdef POSIX
+ return 0;
+#else
+ return _CrtIsValidPointer( pMem, size, access );
+#endif
+}
+
+#define DBGMEM_CHECKMEMORY 1
+
+int CDbgMemAlloc::CrtCheckMemory( void )
+{
+#if !defined( DBGMEM_CHECKMEMORY ) || defined( POSIX )
+ return 1;
+#elif defined( _WIN32 )
+ if ( !_CrtCheckMemory())
+ {
+ Msg( "Memory check failed!\n" );
+ return 0;
+ }
+ return 1;
+#else
+ return 1;
+#endif
+}
+
+int CDbgMemAlloc::CrtSetDbgFlag( int nNewFlag )
+{
+#ifdef POSIX
+ return 0;
+#else
+ return _CrtSetDbgFlag( nNewFlag );
+#endif
+}
+
+void CDbgMemAlloc::CrtMemCheckpoint( _CrtMemState *pState )
+{
+#ifndef POSIX
+ _CrtMemCheckpoint( pState );
+#endif
+}
+
+// FIXME: Remove when we have our own allocator
+void* CDbgMemAlloc::CrtSetReportFile( int nRptType, void* hFile )
+{
+#ifdef POSIX
+ return 0;
+#else
+ return (void*)_CrtSetReportFile( nRptType, (_HFILE)hFile );
+#endif
+}
+
+void* CDbgMemAlloc::CrtSetReportHook( void* pfnNewHook )
+{
+#ifdef POSIX
+ return 0;
+#else
+ return (void*)_CrtSetReportHook( (_CRT_REPORT_HOOK)pfnNewHook );
+#endif
+}
+
+int CDbgMemAlloc::CrtDbgReport( int nRptType, const char * szFile,
+ int nLine, const char * szModule, const char * pMsg )
+{
+#ifdef POSIX
+ return 0;
+#else
+ return _CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg );
+#endif
+}
+
+int CDbgMemAlloc::heapchk()
+{
+#ifdef POSIX
+ return 0;
+#else
+ if ( CrtCheckMemory() )
+ return _HEAPOK;
+ else
+ return _HEAPBADPTR;
+#endif
+}
+
+void CDbgMemAlloc::DumpBlockStats( void *p )
+{
+ DbgMemHeader_t *pBlock = (DbgMemHeader_t *)p - 1;
+ if ( !CrtIsValidHeapPointer( pBlock ) )
+ {
+ Msg( "0x%x is not valid heap pointer\n", p );
+ return;
+ }
+
+ const char *pFileName = GetAllocatonFileName( p );
+ int line = GetAllocatonLineNumber( p );
+
+ Msg( "0x%x allocated by %s line %d, %d bytes\n", p, pFileName, line, GetSize( p ) );
+}
+
+//-----------------------------------------------------------------------------
+// Stat output
+//-----------------------------------------------------------------------------
+void CDbgMemAlloc::DumpMemInfo( const char *pAllocationName, int line, const MemInfo_t &info )
+{
+ m_OutputFunc("%s, line %i\t%.1f\t%.1f\t%.1f\t%.1f\t%.1f\t%d\t%d\t%d\t%d\t%d\t%d\t%d",
+ pAllocationName,
+ line,
+ info.m_nCurrentSize / 1024.0f,
+ info.m_nPeakSize / 1024.0f,
+ info.m_nTotalSize / 1024.0f,
+ info.m_nOverheadSize / 1024.0f,
+ info.m_nPeakOverheadSize / 1024.0f,
+ (int)(info.m_nTime / 1000),
+ info.m_nCurrentCount,
+ info.m_nPeakCount,
+ info.m_nTotalCount,
+ info.m_nSumTargetRange,
+ info.m_nCurTargetRange,
+ info.m_nMaxTargetRange
+ );
+
+ for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
+ {
+ m_OutputFunc( "\t%d", info.m_pCount[i] );
+ }
+
+ m_OutputFunc("\n");
+}
+
+
+//-----------------------------------------------------------------------------
+// Stat output
+//-----------------------------------------------------------------------------
+size_t CDbgMemAlloc::ComputeMemoryUsedBy( char const *pchSubStr)
+{
+ size_t total = 0;
+ StatMapIter_FileLine_t iter = m_StatMap_FileLine.begin();
+ while(iter != m_StatMap_FileLine.end())
+ {
+ if(!pchSubStr || strstr(iter->first.m_pFileName,pchSubStr))
+ {
+ total += iter->second.m_nCurrentSize;
+ }
+ iter++;
+ }
+ return total;
+}
+
+void CDbgMemAlloc::DumpFileStats()
+{
+ StatMapIter_FileLine_t iter = m_StatMap_FileLine.begin();
+ while(iter != m_StatMap_FileLine.end())
+ {
+ DumpMemInfo( iter->first.m_pFileName, iter->first.m_nLine, iter->second );
+ iter++;
+ }
+}
+
+void CDbgMemAlloc::DumpStatsFileBase( char const *pchFileBase )
+{
+ char szFileName[MAX_PATH];
+ static int s_FileCount = 0;
+ if (m_OutputFunc == DefaultHeapReportFunc)
+ {
+ char *pPath = "";
+#ifdef _X360
+ pPath = "D:\\";
+#elif defined( _PS3 )
+ pPath = "/app_home/";
+#endif
+
+
+#if defined( _MEMTEST ) && defined( _WIN32 )
+ char szXboxName[32];
+ strcpy( szXboxName, "xbox" );
+ DWORD numChars = sizeof( szXboxName );
+ DmGetXboxName( szXboxName, &numChars );
+ char *pXboxName = strstr( szXboxName, "_360" );
+ if ( pXboxName )
+ {
+ *pXboxName = '\0';
+ }
+
+ SYSTEMTIME systemTime;
+ GetLocalTime( &systemTime );
+ //_snprintf( szFileName, sizeof( szFileName ), "%s%s_%2.2d%2.2d_%2.2d%2.2d%2.2d_%d.txt", pPath, s_szStatsMapName, systemTime.wMonth, systemTime.wDay, systemTime.wHour, systemTime.wMinute, systemTime.wSecond, s_FileCount );
+ _snprintf( szFileName, sizeof( szFileName ), "%s%s_%d.txt", pPath, s_szStatsMapName, s_FileCount );
+#else
+ _snprintf( szFileName, sizeof( szFileName ), "%s%s_%d.txt", pPath, pchFileBase, s_FileCount );
+#endif
+
+ ++s_FileCount;
+
+ s_DbgFile = fopen(szFileName, "wt");
+ if (!s_DbgFile)
+ return;
+ }
+
+ {
+ HEAP_LOCK();
+
+ m_OutputFunc("Allocation type\tCurrent Size(k)\tPeak Size(k)\tTotal Allocations(k)\tOverhead Size(k)\tPeak Overhead Size(k)\tTime(ms)\tCurrent Count\tPeak Count\tTotal Count\tTNum\tTCur\tTMax");
+
+ for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
+ {
+ m_OutputFunc( "\t%s", s_pCountHeader[i] );
+ }
+
+ m_OutputFunc("\n");
+
+ MemInfo_t totals = m_GlobalInfo;
+#ifdef _PS3
+ {
+ // Add a line for system heap stats
+ static malloc_managed_size mms;
+ (g_pMemOverrideRawCrtFns->pfn_malloc_stats)( &mms );
+
+ MemInfo_t info;
+ info.m_nCurrentSize = mms.current_inuse_size;
+ info.m_nPeakSize = mms.max_system_size;
+ info.m_nOverheadSize = mms.current_system_size - mms.current_inuse_size;
+ DumpMemInfo( "||PS3 malloc_stats||", 0, info );
+
+ // Add a line for PRXs
+ char prxFilename[256];
+ sys_prx_id_t prxIDs[256];
+ sys_prx_segment_info_t prxSegments[32];
+ sys_prx_get_module_list_t prxList = { sizeof( sys_prx_get_module_list_t ), ARRAYSIZE( prxIDs ), 0, prxIDs, NULL };
+ sys_prx_get_module_list( 0, &prxList );
+ Assert( prxList.count < ARRAYSIZE( prxIDs ) );
+ memset( &info, 0, sizeof( info ) );
+ for ( int i = 0; i < prxList.count; i++ )
+ {
+ sys_prx_module_info_t prxInfo;
+ prxInfo.size = sizeof( sys_prx_module_info_t );
+ prxInfo.filename = prxFilename;
+ prxInfo.filename_size = sizeof( prxFilename );
+ prxInfo.segments = prxSegments;
+ prxInfo.segments_num = ARRAYSIZE( prxSegments );
+ sys_prx_get_module_info( prxList.idlist[i], 0, &prxInfo );
+ Assert( prxInfo.segments_num < ARRAYSIZE( prxSegments ) );
+ for ( int j = 0; j < prxInfo.segments_num; j++ )
+ {
+ info.m_nCurrentSize += prxInfo.segments[j].memsz;
+ }
+ }
+ DumpMemInfo( "PS3 PRXs", 0, info );
+
+ // Add PRX sizes to our global tracked total:
+ totals.m_nCurrentSize += info.m_nCurrentSize;
+ }
+#endif // _PS3
+
+ // The total of all memory usage we know about:
+ DumpMemInfo( "||Totals||", 0, totals );
+
+ if ( IsGameConsole() )
+ {
+ // Add a line showing total system memory usage from the OS (if this is more than
+ // "||Totals||", then there is unknown memory usage that we need to track down):
+ size_t usedMemory, freeMemory;
+ GlobalMemoryStatus( &usedMemory, &freeMemory );
+ MemInfo_t info;
+ info.m_nCurrentSize = usedMemory;
+ DumpMemInfo( "||Used Memory||", 0, info );
+ }
+
+#ifdef _MEMTEST
+ {
+ // Add lines for GPU allocations
+ int nGPUMemSize, nGPUMemFree, nTextureSize, nRTSize, nVBSize, nIBSize, nUnknown;
+ if ( 7 == sscanf( s_szStatsComment, "%d %d %d %d %d %d %d", &nGPUMemSize, &nGPUMemFree, &nTextureSize, &nRTSize, &nVBSize, &nIBSize, &nUnknown ) )
+ {
+ int nTotalUsed = nTextureSize + nRTSize + nVBSize + nIBSize + nUnknown;
+ int nOverhead = ( nGPUMemSize - nTotalUsed ) - nGPUMemFree;
+ m_OutputFunc( "||PS3 RSX: total used||, line 0\t%.1f\n", nTotalUsed / 1024.0f );
+ m_OutputFunc( "PS3 RSX: textures, line 0\t%.1f\n", nTextureSize / 1024.0f );
+ m_OutputFunc( "PS3 RSX: render targets, line 0\t%.1f\n", nRTSize / 1024.0f );
+ m_OutputFunc( "PS3 RSX: vertex buffers, line 0\t%.1f\n", nVBSize / 1024.0f );
+ m_OutputFunc( "PS3 RSX: index buffers, line 0\t%.1f\n", nIBSize / 1024.0f );
+ m_OutputFunc( "PS3 RSX: unknown, line 0\t%.1f\n", nUnknown / 1024.0f );
+ m_OutputFunc( "PS3 RSX: overhead, line 0\t%.1f\n", nOverhead / 1024.0f );
+ }
+ }
+#endif
+
+ //m_OutputFunc("File/Line Based\n");
+ DumpFileStats();
+ }
+
+ if (m_OutputFunc == DefaultHeapReportFunc)
+ {
+ fclose(s_DbgFile);
+
+#if defined( _X360 )
+ XBX_rMemDump( szFileName );
+#endif
+ }
+}
+
+void CDbgMemAlloc::GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory )
+{
+ if ( !pUsedMemory || !pFreeMemory )
+ return;
+
+#if defined ( _X360 )
+
+ // GlobalMemoryStatus tells us how much physical memory is free
+ MEMORYSTATUS stat;
+ ::GlobalMemoryStatus( &stat );
+ *pFreeMemory = stat.dwAvailPhys;
+
+ // Used is total minus free (discount the 32MB system reservation)
+ *pUsedMemory = ( stat.dwTotalPhys - 32*1024*1024 ) - *pFreeMemory;
+
+#elif defined( _PS3 )
+
+ // need to factor in how much empty space there is in the heap
+ // (since it NEVER returns pages back to the OS after hitting a high-watermark)
+ static malloc_managed_size mms;
+ (g_pMemOverrideRawCrtFns->pfn_malloc_stats)( &mms );
+ int heapFree = mms.current_system_size - mms.current_inuse_size;
+ Assert( heapFree >= 0 );
+
+ // sys_memory_get_user_memory_size tells us how much PPU memory is used/free
+ static sys_memory_info stat;
+ sys_memory_get_user_memory_size( &stat );
+ *pFreeMemory = stat.available_user_memory;
+ *pFreeMemory += heapFree;
+ *pUsedMemory = stat.total_user_memory - *pFreeMemory;
+ // 213MB are available in retail mode, so adjust free mem to reflect that even if we're in devkit mode
+ const size_t RETAIL_SIZE = 213*1024*1024;
+ if ( stat.total_user_memory > RETAIL_SIZE )
+ *pFreeMemory -= stat.total_user_memory - RETAIL_SIZE;
+
+#else
+
+ // no data
+ *pFreeMemory = 0;
+ *pUsedMemory = 0;
+
+#endif
+}
+
+#ifdef USE_STACK_TRACES
+void CDbgMemAlloc::DumpCallStackFlow( char const *pchFileBase )
+{
+ HEAP_LOCK();
+
+ char szFileName[MAX_PATH];
+ static int s_FileCount = 0;
+
+ char *pPath = "";
+ if ( IsX360() )
+ {
+ pPath = "D:\\";
+ }
+
+#if defined( _MEMTEST ) && defined( _WIN32 )
+ char szXboxName[32];
+ strcpy( szXboxName, "xbox" );
+ DWORD numChars = sizeof( szXboxName );
+ DmGetXboxName( szXboxName, &numChars );
+ char *pXboxName = strstr( szXboxName, "_360" );
+ if ( pXboxName )
+ {
+ *pXboxName = '\0';
+ }
+
+ SYSTEMTIME systemTime;
+ GetLocalTime( &systemTime );
+ _snprintf( szFileName, sizeof( szFileName ), "%s%s_%2.2d%2.2d_%2.2d%2.2d%2.2d_%d.csf", pPath, s_szStatsMapName, systemTime.wMonth, systemTime.wDay, systemTime.wHour, systemTime.wMinute, systemTime.wSecond, s_FileCount );
+#else
+ _snprintf( szFileName, sizeof( szFileName ), "%s%s%d.vcsf", pPath, pchFileBase, s_FileCount );
+#endif
+
+ ++s_FileCount;
+ m_CallStackStats.DumpToFile( szFileName, false );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Stat output
+//-----------------------------------------------------------------------------
+void CDbgMemAlloc::DumpStats()
+{
+ DumpStatsFileBase( "memstats" );
+#ifdef USE_STACK_TRACES
+ DumpCallStackFlow( "memflow" );
+#endif
+}
+
+void CDbgMemAlloc::SetCRTAllocFailed( size_t nSize )
+{
+ m_sMemoryAllocFailed = nSize;
+ DebuggerBreakIfDebugging();
+ char buffer[256];
+ _snprintf( buffer, sizeof( buffer ), "***** OUT OF MEMORY! attempted allocation size: %u ****\n", nSize );
+#if defined( _PS3 ) && defined( _DEBUG )
+ DebuggerBreak();
+#endif // _PS3
+
+#ifdef _X360
+ XBX_OutputDebugString( buffer );
+ if ( !Plat_IsInDebugSession() )
+ {
+ XBX_CrashDump( true );
+#if defined( _DEMO )
+ XLaunchNewImage( XLAUNCH_KEYWORD_DEFAULT_APP, 0 );
+#else
+ XLaunchNewImage( "default.xex", 0 );
+#endif
+ }
+#elif defined(_WIN32 )
+ OutputDebugString( buffer );
+ if ( !Plat_IsInDebugSession() )
+ {
+ AssertFatalMsg( false, buffer );
+ abort();
+ }
+#else
+ printf( "%s\n", buffer );
+ if ( !Plat_IsInDebugSession() )
+ {
+ AssertFatalMsg( false, buffer );
+ exit( 0 );
+ }
+#endif
+}
+
+size_t CDbgMemAlloc::MemoryAllocFailed()
+{
+ return m_sMemoryAllocFailed;
+}
+
+
+
+#ifdef LINUX
+//
+// Under linux we can ask GLIBC to override malloc for us
+// Base on code from Ryan, http://hg.icculus.org/icculus/mallocmonitor/file/29c4b0d049f7/monitor_client/malloc_hook_glibc.c
+//
+//
+static void *glibc_malloc_hook = NULL;
+static void *glibc_realloc_hook = NULL;
+static void *glibc_memalign_hook = NULL;
+static void *glibc_free_hook = NULL;
+
+/* convenience functions for setting the hooks... */
+static inline void save_glibc_hooks(void);
+static inline void set_glibc_hooks(void);
+static inline void set_override_hooks(void);
+
+CThreadMutex g_HookMutex;
+/*
+ * Our overriding hooks...they call through to the original C runtime
+ * implementations and report to the monitoring daemon.
+ */
+
+static void *override_malloc_hook(size_t s, const void *caller)
+{
+ void *retval;
+ AUTO_LOCK( g_HookMutex );
+ set_glibc_hooks(); /* put glibc back in control. */
+ retval = InternalMalloc( s, NULL, 0 );
+ save_glibc_hooks(); /* update in case glibc changed them. */
+
+ set_override_hooks(); /* only restore hooks if daemon is listening */
+
+ return(retval);
+} /* override_malloc_hook */
+
+
+static void *override_realloc_hook(void *ptr, size_t s, const void *caller)
+{
+ void *retval;
+ AUTO_LOCK( g_HookMutex );
+
+ set_glibc_hooks(); /* put glibc back in control. */
+ retval = InternalRealloc(ptr, s, NULL, 0); /* call glibc version. */
+ save_glibc_hooks(); /* update in case glibc changed them. */
+
+ set_override_hooks(); /* only restore hooks if daemon is listening */
+
+ return(retval);
+} /* override_realloc_hook */
+
+
+static void *override_memalign_hook(size_t a, size_t s, const void *caller)
+{
+ void *retval;
+ AUTO_LOCK( g_HookMutex );
+
+ set_glibc_hooks(); /* put glibc back in control. */
+ retval = memalign(a, s); /* call glibc version. */
+ save_glibc_hooks(); /* update in case glibc changed them. */
+
+ set_override_hooks(); /* only restore hooks if daemon is listening */
+
+ return(retval);
+} /* override_memalign_hook */
+
+
+static void override_free_hook(void *ptr, const void *caller)
+{
+ AUTO_LOCK( g_HookMutex );
+
+ set_glibc_hooks(); /* put glibc back in control. */
+ InternalFree(ptr); /* call glibc version. */
+ save_glibc_hooks(); /* update in case glibc changed them. */
+
+ set_override_hooks(); /* only restore hooks if daemon is listening */
+} /* override_free_hook */
+
+
+
+/*
+ * Convenience functions for swapping the hooks around...
+ */
+
+/*
+ * Save a copy of the original allocation hooks, so we can call into them
+ * from our overriding functions. It's possible that glibc might change
+ * these hooks under various conditions (so the manual's examples seem
+ * to suggest), so we update them whenever we finish calling into the
+ * the originals.
+ */
+static inline void save_glibc_hooks(void)
+{
+ glibc_malloc_hook = (void *)__malloc_hook;
+ glibc_realloc_hook = (void *)__realloc_hook;
+ glibc_memalign_hook = (void *)__memalign_hook;
+ glibc_free_hook = (void *)__free_hook;
+} /* save_glibc_hooks */
+
+/*
+ * Restore the hooks to the glibc versions. This is needed since, say,
+ * their realloc() might call malloc() or free() under the hood, etc, so
+ * it's safer to let them have complete control over the subsystem, which
+ * also makes our logging saner, too.
+ */
+static inline void set_glibc_hooks(void)
+{
+ __malloc_hook = (void* (*)(size_t, const void*))glibc_malloc_hook;
+ __realloc_hook = (void* (*)(void*, size_t, const void*))glibc_realloc_hook;
+ __memalign_hook = (void* (*)(size_t, size_t, const void*))glibc_memalign_hook;
+ __free_hook = (void (*)(void*, const void*))glibc_free_hook;
+} /* set_glibc_hooks */
+
+
+/*
+ * Put our hooks back in place. This should be done after the original
+ * glibc version has been called and we've finished any logging (which
+ * may call glibc functions, too). This sets us up for the next calls from
+ * the application.
+ */
+static inline void set_override_hooks(void)
+{
+ __malloc_hook = override_malloc_hook;
+ __realloc_hook = override_realloc_hook;
+ __memalign_hook = override_memalign_hook;
+ __free_hook = override_free_hook;
+} /* set_override_hooks */
+
+
+
+/*
+ * The Hook Of All Hooks...how we get in there in the first place.
+ */
+
+/*
+ * glibc will call this when the malloc subsystem is initializing, giving
+ * us a chance to install hooks that override the functions.
+ */
+static void override_init_hook(void)
+{
+ AUTO_LOCK( g_HookMutex );
+
+ /* install our hooks. Will connect to daemon on first malloc, etc. */
+ save_glibc_hooks();
+ set_override_hooks();
+} /* override_init_hook */
+
+
+/*
+ * __malloc_initialize_hook is apparently a "weak variable", so you can
+ * define and assign it here even though it's in glibc, too. This lets
+ * us hook into malloc as soon as the runtime initializes, and before
+ * main() is called. Basically, this whole trick depends on this.
+ */
+void (*__malloc_initialize_hook)(void) __attribute__((visibility("default")))= override_init_hook;
+
+#elif defined( OSX )
+//
+// pointers to the osx versions of these functions
+static void *osx_malloc_hook = NULL;
+static void *osx_realloc_hook = NULL;
+static void *osx_free_hook = NULL;
+
+// convenience functions for setting the hooks...
+static inline void save_osx_hooks(void);
+static inline void set_osx_hooks(void);
+static inline void set_override_hooks(void);
+
+CThreadMutex g_HookMutex;
+//
+// Our overriding hooks...they call through to the original C runtime
+// implementations and report to the monitoring daemon.
+//
+
+static void *override_malloc_hook(struct _malloc_zone_t *zone, size_t s)
+{
+ void *retval;
+ set_osx_hooks();
+ retval = InternalMalloc( s, NULL, 0 );
+ set_override_hooks();
+
+ return(retval);
+}
+
+
+static void *override_realloc_hook(struct _malloc_zone_t *zone, void *ptr, size_t s)
+{
+ void *retval;
+
+ set_osx_hooks();
+ retval = InternalRealloc(ptr, s, NULL, 0);
+ set_override_hooks();
+
+ return(retval);
+}
+
+
+static void override_free_hook(struct _malloc_zone_t *zone, void *ptr)
+{
+ // sometime they pass in a null pointer from higher level calls, just ignore it
+ if ( !ptr )
+ return;
+
+ set_osx_hooks();
+
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( ptr );
+ if ( *((int*)pInternalMem->m_Reserved) == 0xf00df00d )
+ {
+ InternalFree( ptr );
+ }
+
+ set_override_hooks();
+}
+
+
+/*
+
+ These are func's we could optionally override right now on OSX but don't need to
+
+ static size_t override_size_hook(struct _malloc_zone_t *zone, const void *ptr)
+ {
+ set_osx_hooks();
+ DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( (void *)ptr );
+ set_override_hooks();
+ if ( *((int*)pInternalMem->m_Reserved) == 0xf00df00d )
+ {
+ return pInternalMem->nLogicalSize;
+ }
+ return 0;
+ }
+
+
+ static void *override_calloc_hook(struct _malloc_zone_t *zone, size_t num_items, size_t size )
+ {
+ void *ans = override_malloc_hook( zone, num_items*size );
+ if ( !ans )
+ return 0;
+ memset( ans, 0x0, num_items*size );
+ return ans;
+ }
+
+ static void *override_valloc_hook(struct _malloc_zone_t *zone, size_t size )
+ {
+ return override_calloc_hook( zone, 1, size );
+ }
+
+ static void override_destroy_hook(struct _malloc_zone_t *zone)
+ {
+ }
+ */
+
+
+
+//
+// Save a copy of the original allocation hooks, so we can call into them
+// from our overriding functions. It's possible that osx might change
+// these hooks under various conditions (so the manual's examples seem
+// to suggest), so we update them whenever we finish calling into the
+// the originals.
+//
+static inline void save_osx_hooks(void)
+{
+ malloc_zone_t *malloc_zone = malloc_default_zone();
+
+ osx_malloc_hook = (void *)malloc_zone->malloc;
+ osx_realloc_hook = (void *)malloc_zone->realloc;
+ osx_free_hook = (void *)malloc_zone->free;
+
+ // These are func's we could optionally override right now on OSX but don't need to
+ // osx_size_hook = (void *)malloc_zone->size;
+ // osx_calloc_hook = (void *)malloc_zone->calloc;
+ // osx_valloc_hook = (void *)malloc_zone->valloc;
+ // osx_destroy_hook = (void *)malloc_zone->destroy;
+}
+
+//
+// Restore the hooks to the osx versions. This is needed since, say,
+// their realloc() might call malloc() or free() under the hood, etc, so
+// it's safer to let them have complete control over the subsystem, which
+// also makes our logging saner, too.
+//
+static inline void set_osx_hooks(void)
+{
+ malloc_zone_t *malloc_zone = malloc_default_zone();
+ malloc_zone->malloc = (void* (*)(_malloc_zone_t*, size_t))osx_malloc_hook;
+ malloc_zone->realloc = (void* (*)(_malloc_zone_t*, void*, size_t))osx_realloc_hook;
+ malloc_zone->free = (void (*)(_malloc_zone_t*, void*))osx_free_hook;
+
+ // These are func's we could optionally override right now on OSX but don't need to
+
+ //malloc_zone->size = (size_t (*)(_malloc_zone_t*, const void *))osx_size_hook;
+ //malloc_zone->calloc = (void* (*)(_malloc_zone_t*, size_t, size_t))osx_calloc_hook;
+ //malloc_zone->valloc = (void* (*)(_malloc_zone_t*, size_t))osx_valloc_hook;
+ //malloc_zone->destroy = (void (*)(_malloc_zone_t*))osx_destroy_hook;
+}
+
+
+/*
+ * Put our hooks back in place. This should be done after the original
+ * osx version has been called and we've finished any logging (which
+ * may call osx functions, too). This sets us up for the next calls from
+ * the application.
+ */
+static inline void set_override_hooks(void)
+{
+ malloc_zone_t *malloc_zone = malloc_default_zone();
+
+ malloc_zone->malloc = override_malloc_hook;
+ malloc_zone->realloc = override_realloc_hook;
+ malloc_zone->free = override_free_hook;
+
+ // These are func's we could optionally override right now on OSX but don't need to
+ //malloc_zone->size = override_size_hook;
+ //malloc_zone->calloc = override_calloc_hook;
+ // malloc_zone->valloc = override_valloc_hook;
+ //malloc_zone->destroy = override_destroy_hook;
+}
+
+
+//
+// The Hook Of All Hooks...how we get in there in the first place.
+//
+// osx will call this when the malloc subsystem is initializing, giving
+// us a chance to install hooks that override the functions.
+//
+
+void __attribute__ ((constructor)) mem_init(void)
+{
+ AUTO_LOCK( g_HookMutex );
+ save_osx_hooks();
+ set_override_hooks();
+}
+
+void *operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
+{
+ set_osx_hooks();
+ void *pMem = g_pMemAlloc->Alloc(nSize, pFileName, nLine);
+ set_override_hooks();
+ return pMem;
+}
+
+void *operator new[] ( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
+{
+ set_osx_hooks();
+ void *pMem = g_pMemAlloc->Alloc(nSize, pFileName, nLine);
+ set_override_hooks();
+ return pMem;
+}
+
+#endif // OSX
+
+int GetAllocationCallStack( void *mem, void **pCallStackOut, int iMaxEntriesOut )
+{
+#if defined( USE_MEM_DEBUG ) && (defined( USE_STACK_TRACES ))
+ return s_DbgMemAlloc.GetCallStackForIndex( GetAllocationStatIndex_Internal( mem ), pCallStackOut, iMaxEntriesOut );
+#else
+ return 0;
+#endif
+}
+
+
+#endif // MEM_IMPL_TYPE_DBG
+
+#endif // !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
diff --git a/external/vpc/tier0/memstd.cpp b/external/vpc/tier0/memstd.cpp
new file mode 100644
index 0000000..20a8aec
--- /dev/null
+++ b/external/vpc/tier0/memstd.cpp
@@ -0,0 +1,2493 @@
+//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Memory allocation!
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "tier0/platform.h"
+
+
+#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
+
+
+//#include <malloc.h>
+
+#include <algorithm>
+
+#include "tier0/dbg.h"
+#include "tier0/memalloc.h"
+#include "tier0/threadtools.h"
+#include "mem_helpers.h"
+#include "memstd.h"
+#include "tier0/stacktools.h"
+#include "tier0/minidump.h"
+#ifdef _X360
+#include "xbox/xbox_console.h"
+#endif
+
+#ifdef _PS3
+#include "memoverride_ps3.h"
+#endif
+
+#ifndef _WIN32
+#define IsDebuggerPresent() false
+#endif
+
+#ifdef USE_LIGHT_MEM_DEBUG
+#undef USE_MEM_DEBUG
+#pragma message("*** USE_LIGHT_MEM_DEBUG is ON ***")
+#pragma optimize( "", off )
+#endif
+
+#define DEF_REGION 0
+
+#if defined( _WIN32 ) || defined( _PS3 )
+#define USE_DLMALLOC
+#define MEMALLOC_SEGMENT_MIXED
+#define MBH_SIZE_MB ( 45 + MBYTES_STEAM_MBH_USAGE )
+//#define MEMALLOC_REGIONS
+#endif // _WIN32 || _PS3
+
+#ifndef USE_DLMALLOC
+#ifdef _PS3
+#define malloc_internal( region, bytes ) (g_pMemOverrideRawCrtFns->pfn_malloc)(bytes)
+#define malloc_aligned_internal( region, bytes, align ) (g_pMemOverrideRawCrtFns->pfn_memalign)(align, bytes)
+#define realloc_internal (g_pMemOverrideRawCrtFns->pfn_realloc)
+#define realloc_aligned_internal (g_pMemOverrideRawCrtFns->pfn_reallocalign)
+#define free_internal (g_pMemOverrideRawCrtFns->pfn_free)
+#define msize_internal (g_pMemOverrideRawCrtFns->pfn_malloc_usable_size)
+#define compact_internal() (0)
+#define heapstats_internal(p) (void)(0)
+#else // _PS3
+#define malloc_internal( region, bytes) malloc(bytes)
+#define malloc_aligned_internal( region, bytes, align ) memalign(align, bytes)
+#define realloc_internal realloc
+#define realloc_aligned_internal realloc
+#define free_internal free
+#ifdef POSIX
+#define msize_internal malloc_usable_size
+#else // POSIX
+#define msize_internal _msize
+#endif // POSIX
+#define compact_internal() (0)
+#define heapstats_internal(p) (void)(0)
+#endif // _PS3
+#else // USE_DLMALLOC
+#define MSPACES 1
+#include "dlmalloc/malloc-2.8.3.h"
+
+void *g_AllocRegions[] =
+{
+#ifndef MEMALLOC_REGIONS
+#ifdef MEMALLOC_SEGMENT_MIXED
+ create_mspace( 0, 1 ), // unified
+ create_mspace( MBH_SIZE_MB*1024*1024, 1 ),
+#else
+ create_mspace( 100*1024*1024, 1 ),
+#endif
+#else // MEMALLOC_REGIONS
+ // @TODO: per DLL regions didn't work out very well. flux of usage left too much overhead. need to try lifetime-based management [6/9/2009 tom]
+ create_mspace( 82*1024*1024, 1 ), // unified
+#endif // MEMALLOC_REGIONS
+};
+
+#ifndef MEMALLOC_REGIONS
+#ifndef MEMALLOC_SEGMENT_MIXED
+#define SelectRegion( region, bytes ) 0
+#else
+// NOTE: this split is designed to force the 'large block' heap to ONLY perform virtual allocs (see
+// DEFAULT_MMAP_THRESHOLD in malloc.cpp), to avoid ANY fragmentation or waste in an internal arena
+#define REGION_SPLIT (256*1024)
+#define SelectRegion( region, bytes ) g_AllocRegions[bytes < REGION_SPLIT]
+#endif
+#else // MEMALLOC_REGIONS
+#define SelectRegion( region, bytes ) g_AllocRegions[region]
+#endif // MEMALLOC_REGIONS
+
+#define malloc_internal( region, bytes ) mspace_malloc(SelectRegion(region,bytes), bytes)
+#define malloc_aligned_internal( region, bytes, align ) mspace_memalign(SelectRegion(region,bytes), align, bytes)
+FORCEINLINE void *realloc_aligned_internal( void *mem, size_t bytes, size_t align )
+{
+ // TODO: implement realloc_aligned inside dlmalloc (requires splitting realloc's existing
+ // 'grow in-place' code into a new function, then call that w/ alloc_align/copy/free on failure)
+ byte *newMem = (byte *)dlrealloc( mem, bytes );
+ if ( ((size_t)newMem&(align-1)) == 0 )
+ return newMem;
+ // realloc broke alignment...
+ byte *fallback = (byte *)malloc_aligned_internal( DEF_REGION, bytes, align );
+ if ( !fallback )
+ return NULL;
+ memcpy( fallback, newMem, bytes );
+ dlfree( newMem );
+ return fallback;
+}
+
+inline size_t compact_internal()
+{
+ size_t start = 0, end = 0;
+
+ for ( int i = 0; i < ARRAYSIZE(g_AllocRegions); i++ )
+ {
+ start += mspace_footprint( g_AllocRegions[i] );
+ mspace_trim( g_AllocRegions[i], 0 );
+ end += mspace_footprint( g_AllocRegions[i] );
+ }
+
+ return ( start - end );
+}
+
+inline void heapstats_internal( FILE *pFile )
+{
+ // @TODO: improve this presentation, as a table [6/1/2009 tom]
+ char buf[1024];
+ for ( int i = 0; i < ARRAYSIZE( g_AllocRegions ); i++ )
+ {
+ struct mallinfo info = mspace_mallinfo( g_AllocRegions[ i ] );
+ size_t footPrint = mspace_footprint( g_AllocRegions[ i ] );
+ size_t maxFootPrint = mspace_max_footprint( g_AllocRegions[ i ] );
+ _snprintf( buf, sizeof(buf),
+ "\ndlmalloc mspace %d (%s)\n"
+ " %d:footprint -%10d (total space used by the mspace)\n"
+ " %d:footprint_max -%10d (maximum total space used by the mspace)\n"
+ " %d:arena -%10d (non-mmapped space allocated from system)\n"
+ " %d:ordblks -%10d (number of free chunks)\n"
+ " %d:hblkhd -%10d (space in mmapped regions)\n"
+ " %d:usmblks -%10d (maximum total allocated space)\n"
+ " %d:uordblks -%10d (total allocated space)\n"
+ " %d:fordblks -%10d (total free space)\n"
+ " %d:keepcost -%10d (releasable (via malloc_trim) space)\n",
+ i, i?"medium-block":"large-block", i,footPrint, i,maxFootPrint, i,info.arena, i,info.ordblks, i,info.hblkhd, i,info.usmblks, i,info.uordblks, i,info.fordblks, i,info.keepcost );
+ if ( pFile )
+ fprintf( pFile, "%s", buf );
+ else
+ Msg( "%s", buf );
+ }
+}
+
+#define realloc_internal dlrealloc
+#define free_internal dlfree
+#define msize_internal dlmalloc_usable_size
+#endif // USE_DLMALLOC
+
+#ifdef TIME_ALLOC
+CAverageCycleCounter g_MallocCounter;
+CAverageCycleCounter g_ReallocCounter;
+CAverageCycleCounter g_FreeCounter;
+
+#define PrintOne( name ) \
+ Msg("%-48s: %6.4f avg (%8.1f total, %7.3f peak, %5d iters)\n", \
+ #name, \
+ g_##name##Counter.GetAverageMilliseconds(), \
+ g_##name##Counter.GetTotalMilliseconds(), \
+ g_##name##Counter.GetPeakMilliseconds(), \
+ g_##name##Counter.GetIters() ); \
+ memset( &g_##name##Counter, 0, sizeof(g_##name##Counter) )
+
+void PrintAllocTimes()
+{
+ PrintOne( Malloc );
+ PrintOne( Realloc );
+ PrintOne( Free );
+}
+
+#define PROFILE_ALLOC(name) CAverageTimeMarker name##_ATM( &g_##name##Counter )
+
+#else // TIME_ALLOC
+#define PROFILE_ALLOC( name ) ((void)0)
+#define PrintAllocTimes() ((void)0)
+#endif // TIME_ALLOC
+
+#if _MSC_VER < 1400 && defined( MSVC ) && !defined(_STATIC_LINKED) && (defined(_DEBUG) || defined(USE_MEM_DEBUG))
+void *operator new( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine )
+{
+ return ::operator new( nSize );
+}
+
+void *operator new[] ( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine )
+{
+ return ::operator new[]( nSize );
+}
+#endif
+
+#include "mem_impl_type.h"
+#if MEM_IMPL_TYPE_STD
+
+//-----------------------------------------------------------------------------
+// Singleton...
+//-----------------------------------------------------------------------------
+#pragma warning( disable:4074 ) // warning C4074: initializers put in compiler reserved initialization area
+#pragma init_seg( compiler )
+
+#if MEM_SBH_ENABLED
+CSmallBlockPool< CStdMemAlloc::CFixedAllocator< MBYTES_PRIMARY_SBH, true> >::SharedData_t CSmallBlockPool< CStdMemAlloc::CFixedAllocator< MBYTES_PRIMARY_SBH, true> >::gm_SharedData CONSTRUCT_EARLY;
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+CSmallBlockPool< CStdMemAlloc::CFixedAllocator< MBYTES_SECONDARY_SBH, false> >::SharedData_t CSmallBlockPool< CStdMemAlloc::CFixedAllocator< MBYTES_SECONDARY_SBH, false> >::gm_SharedData CONSTRUCT_EARLY;
+#endif
+#ifndef MEMALLOC_NO_FALLBACK
+CSmallBlockPool< CStdMemAlloc::CVirtualAllocator >::SharedData_t CSmallBlockPool< CStdMemAlloc::CVirtualAllocator >::gm_SharedData CONSTRUCT_EARLY;
+#endif
+#endif // MEM_SBH_ENABLED
+
+static CStdMemAlloc s_StdMemAlloc CONSTRUCT_EARLY;
+
+#ifdef _PS3
+
+MemOverrideRawCrtFunctions_t *g_pMemOverrideRawCrtFns;
+IMemAlloc *g_pMemAllocInternalPS3 = &s_StdMemAlloc;
+PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3_IMPL
+
+#else // !_PS3
+
+#ifndef TIER0_VALIDATE_HEAP
+IMemAlloc *g_pMemAlloc = &s_StdMemAlloc;
+#else
+IMemAlloc *g_pActualAlloc = &s_StdMemAlloc;
+#endif
+
+#endif // _PS3
+
+CStdMemAlloc::CStdMemAlloc()
+: m_pfnFailHandler( DefaultFailHandler ),
+ m_sMemoryAllocFailed( (size_t)0 ),
+ m_bInCompact( false )
+{
+#ifdef _PS3
+ g_pMemAllocInternalPS3 = &s_StdMemAlloc;
+ PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3.m_pMemAllocCached = &s_StdMemAlloc;
+ malloc_managed_size mms;
+ mms.current_inuse_size = 0x12345678;
+ mms.current_system_size = 0x09ABCDEF;
+ mms.max_system_size = reinterpret_cast< size_t >( this );
+ int iResult = malloc_stats( &mms );
+ g_pMemOverrideRawCrtFns = reinterpret_cast< MemOverrideRawCrtFunctions_t * >( iResult );
+#endif
+}
+
+#if MEM_SBH_ENABLED
+//-----------------------------------------------------------------------------
+// Small block heap (multi-pool)
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template <typename T>
+inline T MemAlign( T val, unsigned alignment )
+{
+ return (T)( ( (unsigned)val + alignment - 1 ) & ~( alignment - 1 ) );
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+template <typename CAllocator>
+void CSmallBlockPool<CAllocator>::Init( unsigned nBlockSize )
+{
+ SharedData_t *pSharedData = GetSharedData();
+ if ( !pSharedData->m_pBase )
+ {
+ pSharedData->m_pBase = pSharedData->m_Allocator.AllocatePoolMemory();
+ pSharedData->m_pLimit = pSharedData->m_pBase + CAllocator::TOTAL_BYTES;
+ pSharedData->m_pNextBlock = pSharedData->m_pBase;
+ }
+
+ if ( !( nBlockSize % MIN_SBH_ALIGN == 0 && nBlockSize >= MIN_SBH_BLOCK && nBlockSize >= sizeof(TSLNodeBase_t) ) )
+ DebuggerBreak();
+
+ m_nBlockSize = nBlockSize;
+ m_pNextAlloc = NULL;
+ m_nCommittedPages = 0;
+}
+
+template <typename CAllocator>
+size_t CSmallBlockPool<CAllocator>::GetBlockSize()
+{
+ return m_nBlockSize;
+}
+
+// Define VALIDATE_SBH_FREE_LIST to a given block size to validate that pool's freelist (it'll crash on the next alloc/free after the list is corrupted)
+// NOTE: this may affect perf more than USE_LIGHT_MEM_DEBUG
+//#define VALIDATE_SBH_FREE_LIST 320
+template <typename CAllocator>
+void CSmallBlockPool<CAllocator>::ValidateFreelist( SharedData_t *pSharedData )
+{
+#ifdef VALIDATE_SBH_FREE_LIST
+ if ( m_nBlockSize != VALIDATE_SBH_FREE_LIST )
+ return;
+ static int count = 0;
+ count++; // Track when the corruption occurs, if repeatable
+ pSharedData->m_Lock.LockForWrite();
+#ifdef USE_NATIVE_SLIST
+ TSLNodeBase_t *pNode = (TSLNodeBase_t *)(m_FreeList.AccessUnprotected()->Next.Next);
+#else
+ TSLNodeBase_t *pNode = (TSLNodeBase_t *)(m_FreeList.AccessUnprotected()->value.Next);
+#endif
+ while( pNode )
+ pNode = pNode->Next;
+ pSharedData->m_Lock.UnlockWrite();
+#endif // VALIDATE_SBH_FREE_LIST
+}
+
+template <typename CAllocator>
+void *CSmallBlockPool<CAllocator>::Alloc()
+{
+ SharedData_t *pSharedData = GetSharedData();
+
+ ValidateFreelist( pSharedData );
+
+ CThreadSpinRWLock &sharedLock = pSharedData->m_Lock;
+ if ( !sharedLock.TryLockForRead() )
+ {
+ sharedLock.LockForRead();
+ }
+ byte *pResult;
+ intp iPage = -1;
+ int iThreadPriority = INT_MAX;
+
+ while (1)
+ {
+ pResult = m_FreeList.Pop();
+ if ( !pResult )
+ {
+ int nBlockSize = m_nBlockSize;
+ byte *pNextAlloc;
+ while (1)
+ {
+ pResult = m_pNextAlloc;
+ if ( pResult )
+ {
+ pNextAlloc = pResult + nBlockSize;
+ if ( ( ( (uintp)(pNextAlloc) - 1 ) % BYTES_PAGE ) + nBlockSize > BYTES_PAGE )
+ {
+ // Crossed a page boundary
+ pNextAlloc = 0;
+ }
+ if ( m_pNextAlloc.AssignIf( pResult, pNextAlloc ) )
+ {
+ iPage = (size_t)((byte *)pResult - pSharedData->m_pBase) / BYTES_PAGE;
+ break;
+ }
+ }
+ else if ( m_CommitMutex.TryLock() )
+ {
+ if ( !m_pNextAlloc )
+ {
+ PageStatus_t *pAllocatedPageStatus = (PageStatus_t *)pSharedData->m_FreePages.Pop();
+ if ( pAllocatedPageStatus )
+ {
+ iPage = pAllocatedPageStatus - &pSharedData->m_PageStatus[0];
+ }
+ else
+ {
+ while (1)
+ {
+ byte *pBlock = pSharedData->m_pNextBlock;
+ if ( pBlock >= pSharedData->m_pLimit )
+ {
+ break;
+ }
+ if ( ThreadInterlockedAssignPointerIf( (void **)&pSharedData->m_pNextBlock, (void *)( pBlock + BYTES_PAGE ), (void *)pBlock ) )
+ {
+ iPage = (size_t)((byte *)pBlock - pSharedData->m_pBase) / BYTES_PAGE;
+ pAllocatedPageStatus = &pSharedData->m_PageStatus[iPage];
+ break;
+ }
+ }
+ }
+
+ if ( pAllocatedPageStatus )
+ {
+ byte *pBlock = pSharedData->m_pBase + ( iPage * BYTES_PAGE );
+ if ( pAllocatedPageStatus->m_nAllocated == NOT_COMMITTED )
+ {
+ pSharedData->m_Allocator.Commit( pBlock );
+ }
+
+ pAllocatedPageStatus->m_pPool = this;
+ pAllocatedPageStatus->m_nAllocated = 0;
+ pAllocatedPageStatus->m_pNextPageInPool = m_pFirstPage;
+ m_pFirstPage = pAllocatedPageStatus;
+#ifdef TRACK_SBH_COUNTS
+ m_nFreeBlocks += ( BYTES_PAGE / m_nBlockSize );
+#endif
+ m_nCommittedPages++;
+ m_pNextAlloc = pBlock;
+ }
+ else
+ {
+ m_pNextAlloc = NULL;
+ m_CommitMutex.Unlock();
+ sharedLock.UnlockRead();
+ return NULL;
+ }
+ }
+ m_CommitMutex.Unlock();
+ }
+ else
+ {
+ if ( iThreadPriority == INT_MAX)
+ {
+ iThreadPriority = ThreadGetPriority();
+ }
+
+ if ( iThreadPriority > 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+ }
+
+ if ( pResult )
+ {
+ break;
+ }
+ }
+ else
+ {
+ iPage = (size_t)((byte *)pResult - pSharedData->m_pBase) / BYTES_PAGE;
+ break;
+ }
+ }
+
+#ifdef TRACK_SBH_COUNTS
+ --m_nFreeBlocks;
+#endif
+ ++pSharedData->m_PageStatus[iPage].m_nAllocated;
+ sharedLock.UnlockRead();
+
+ return pResult;
+}
+
+template <typename CAllocator>
+void CSmallBlockPool<CAllocator>::Free( void *p )
+{
+ SharedData_t *pSharedData = GetSharedData();
+ size_t iPage = (size_t)((byte *)p - pSharedData->m_pBase) / BYTES_PAGE;
+
+ CThreadSpinRWLock &sharedLock = pSharedData->m_Lock;
+ if ( !sharedLock.TryLockForRead() )
+ {
+ sharedLock.LockForRead();
+ }
+ --pSharedData->m_PageStatus[iPage].m_nAllocated;
+#ifdef TRACK_SBH_COUNTS
+ ++m_nFreeBlocks;
+#endif
+ m_FreeList.Push( p );
+ pSharedData->m_Lock.UnlockRead();
+
+ ValidateFreelist( pSharedData );
+}
+
+// Count the free blocks.
+template <typename CAllocator>
+int CSmallBlockPool<CAllocator>::CountFreeBlocks()
+{
+#ifdef TRACK_SBH_COUNTS
+ return m_nFreeBlocks;
+#else
+ return 0;
+#endif
+}
+
+// Size of committed memory managed by this heap:
+template <typename CAllocator>
+int CSmallBlockPool<CAllocator>::GetCommittedSize()
+{
+ return m_nCommittedPages * BYTES_PAGE;
+}
+
+// Return the total blocks memory is committed for in the heap
+template <typename CAllocator>
+int CSmallBlockPool<CAllocator>::CountCommittedBlocks()
+{
+ return m_nCommittedPages * ( BYTES_PAGE / m_nBlockSize );
+}
+
+// Count the number of allocated blocks in the heap:
+template <typename CAllocator>
+int CSmallBlockPool<CAllocator>::CountAllocatedBlocks()
+{
+#ifdef TRACK_SBH_COUNTS
+ return CountCommittedBlocks() - CountFreeBlocks();
+#else
+ return 0;
+#endif
+}
+
+template <typename CAllocator>
+int CSmallBlockPool<CAllocator>::PageSort( const void *p1, const void *p2 )
+{
+ SharedData_t *pSharedData = GetSharedData();
+ return pSharedData->m_PageStatus[*((int *)p1)].m_SortList.Count() - pSharedData->m_PageStatus[*((int *)p2)].m_SortList.Count();
+}
+
+
+template <typename CAllocator>
+bool CSmallBlockPool<CAllocator>::RemovePagesFromFreeList( byte **pPages, int nPages, bool bSortList )
+{
+ // Since we don't use the depth of the tslist, and sequence is only used for push, we can remove in-place
+ int i;
+ byte **pLimits = (byte **)stackalloc( nPages * sizeof(byte *) );
+ int nBlocksNotInFreeList = 0;
+ for ( i = 0; i < nPages; i++ )
+ {
+ pLimits[i] = pPages[i] + BYTES_PAGE;
+
+ if ( m_pNextAlloc >= pPages[i] && m_pNextAlloc < pLimits[i] )
+ {
+ nBlocksNotInFreeList = ( pLimits[i] - m_pNextAlloc ) / m_nBlockSize;
+ m_pNextAlloc = NULL;
+ }
+ }
+
+ int iTarget = ( ( BYTES_PAGE/m_nBlockSize ) * nPages ) - nBlocksNotInFreeList;
+ int iCount = 0;
+
+ TSLHead_t *pRawFreeList = m_FreeList.AccessUnprotected();
+ bool bRemove;
+ if ( !bSortList || m_nCommittedPages - nPages == 1 )
+ {
+#ifdef USE_NATIVE_SLIST
+ TSLNodeBase_t **ppPrevNext = (TSLNodeBase_t **)&(pRawFreeList->Next);
+#else
+ TSLNodeBase_t **ppPrevNext = (TSLNodeBase_t **)&(pRawFreeList->value.Next);
+#endif
+ TSLNodeBase_t *pNode = *ppPrevNext;
+ while ( pNode && iCount != iTarget )
+ {
+ bRemove = false;
+ for ( i = 0; i < nPages; i++ )
+ {
+ if ( (byte *)pNode >= pPages[i] && (byte *)pNode < pLimits[i] )
+ {
+ bRemove = true;
+ break;
+ }
+ }
+
+ if ( bRemove )
+ {
+ iCount++;
+ *ppPrevNext = pNode->Next;
+ }
+ else
+ {
+ *ppPrevNext = pNode;
+ ppPrevNext = &pNode->Next;
+ }
+ pNode = pNode->Next;
+ }
+ }
+ else
+ {
+ SharedData_t *pSharedData = GetSharedData();
+ byte *pSharedBase = pSharedData->m_pBase;
+ TSLNodeBase_t *pNode = m_FreeList.Detach();
+ TSLNodeBase_t *pNext;
+ int iSortPage;
+
+ int nSortPages = 0;
+ int *sortPages = (int *)stackalloc( m_nCommittedPages * sizeof(int) );
+ while ( pNode )
+ {
+ pNext = pNode->Next;
+ bRemove = false;
+ for ( i = 0; i < nPages; i++ )
+ {
+ if ( (byte *)pNode >= pPages[i] && (byte *)pNode < pLimits[i] )
+ {
+ iCount++;
+ bRemove = true;
+ break;
+ }
+ }
+
+ if ( !bRemove )
+ {
+ iSortPage = ( (byte *)pNode - pSharedBase ) / BYTES_PAGE;
+ if ( !pSharedData->m_PageStatus[iSortPage].m_SortList.Count() )
+ {
+ sortPages[nSortPages++] = iSortPage;
+ }
+ pSharedData->m_PageStatus[iSortPage].m_SortList.Push( pNode );
+ }
+
+ pNode = pNext;
+ }
+
+ if ( nSortPages > 1 )
+ {
+ qsort( sortPages, nSortPages, sizeof(int), &PageSort );
+ }
+ for ( i = 0; i < nSortPages; i++ )
+ {
+ while ( ( pNode = pSharedData->m_PageStatus[sortPages[i]].m_SortList.Pop() ) != NULL )
+ {
+ m_FreeList.Push( pNode );
+ }
+ }
+ }
+ if ( iTarget != iCount )
+ {
+ DebuggerBreakIfDebugging();
+ }
+
+ return ( iTarget == iCount );
+}
+
+
+template <typename CAllocator>
+size_t CSmallBlockPool<CAllocator>::Compact( bool bIncremental )
+{
+ static bool bWarnedCorruption;
+ bool bIsCorrupt = false;
+ int i;
+ size_t nFreed = 0;
+ SharedData_t *pSharedData = GetSharedData();
+ pSharedData->m_Lock.LockForWrite();
+
+ if ( m_pFirstPage )
+ {
+ PageStatus_t **pReleasedPages = (PageStatus_t **)stackalloc( m_nCommittedPages * sizeof(PageStatus_t *) );
+ PageStatus_t **pReleasedPagesPrevs = (PageStatus_t **)stackalloc( m_nCommittedPages * sizeof(PageStatus_t *) );
+ byte **pPageBases = (byte **)stackalloc( m_nCommittedPages * sizeof(byte *) );
+ int nPages = 0;
+
+ // Gather the pages to return to the backing pool
+ PageStatus_t *pPage = m_pFirstPage;
+ PageStatus_t *pPagePrev = NULL;
+ while ( pPage )
+ {
+ if ( pPage->m_nAllocated == 0 )
+ {
+ pReleasedPages[nPages] = pPage;
+ pPageBases[nPages] = pSharedData->m_pBase + ( pPage - &pSharedData->m_PageStatus[0] ) * BYTES_PAGE;
+ pReleasedPagesPrevs[nPages] = pPagePrev;
+ nPages++;
+
+ if ( bIncremental )
+ {
+ break;
+ }
+ }
+ pPagePrev = pPage;
+ pPage = pPage->m_pNextPageInPool;
+ }
+
+ if ( nPages )
+ {
+ // Remove the pages from the pool's free list
+ if ( !RemovePagesFromFreeList( pPageBases, nPages, !bIncremental ) && !bWarnedCorruption )
+ {
+ // We don't know which of the pages encountered an incomplete free list
+ // so we'll just push them all back in and hope for the best. This isn't
+ // ventilator control software!
+ bWarnedCorruption = true;
+ bIsCorrupt = true;
+ }
+
+ nFreed = nPages * BYTES_PAGE;
+ m_nCommittedPages -= nPages;
+
+#ifdef TRACK_SBH_COUNTS
+ m_nFreeBlocks -= nPages * ( BYTES_PAGE / m_nBlockSize );
+#endif
+
+ // Unlink the pages
+ for ( i = nPages - 1; i >= 0; --i )
+ {
+ if ( pReleasedPagesPrevs[i] )
+ {
+ pReleasedPagesPrevs[i]->m_pNextPageInPool = pReleasedPages[i]->m_pNextPageInPool;
+ }
+ else
+ {
+ m_pFirstPage = pReleasedPages[i]->m_pNextPageInPool;
+ }
+ pReleasedPages[i]->m_pNextPageInPool = NULL;
+ pReleasedPages[i]->m_pPool = NULL;
+ }
+
+ // Push them onto the backing free lists
+ if ( !pSharedData->m_Allocator.IsVirtual() )
+ {
+ for ( i = 0; i < nPages; i++ )
+ {
+ pSharedData->m_FreePages.Push( pReleasedPages[i] );
+ }
+ }
+ else
+ {
+ int nMinReserve = ( bIncremental ) ? CAllocator::MIN_RESERVE_PAGES * 8 : CAllocator::MIN_RESERVE_PAGES;
+ int nReserveNeeded = nMinReserve - pSharedData->m_FreePages.Count();
+ if ( nReserveNeeded > 0 )
+ {
+ int nToKeepCommitted = MIN( nReserveNeeded, nPages );
+ while ( nToKeepCommitted-- )
+ {
+ nPages--;
+ pSharedData->m_FreePages.Push( pReleasedPages[nPages] );
+ }
+ }
+
+ if ( nPages )
+ {
+ // Detach the list, push the decommitted page on, iterate up to previous
+ // decommits, but them on, then push the committed pages on
+ TSLNodeBase_t *pNodes = pSharedData->m_FreePages.Detach();
+ for ( i = 0; i < nPages; i++ )
+ {
+ pReleasedPages[i]->m_nAllocated = NOT_COMMITTED;
+ pSharedData->m_Allocator.Decommit( pPageBases[i] );
+ pSharedData->m_FreePages.Push( pReleasedPages[i] );
+ }
+
+ TSLNodeBase_t *pCur, *pTemp = NULL;
+ pCur = pNodes;
+ while ( pCur )
+ {
+ if ( ((PageStatus_t *)pCur)->m_nAllocated == NOT_COMMITTED )
+ {
+ if ( pTemp )
+ {
+ pTemp->Next = NULL;
+ }
+ else
+ {
+ pNodes = NULL; // The list only has decommitted pages, don't go circular
+ }
+
+ while ( pCur )
+ {
+ pTemp = pCur->Next;
+ pSharedData->m_FreePages.Push( pCur );
+ pCur = pTemp;
+ }
+ break;
+ }
+ pTemp = pCur;
+ pCur = pCur->Next;
+ }
+
+ while ( pNodes )
+ {
+ pTemp = pNodes->Next;
+ pSharedData->m_FreePages.Push( pNodes );
+ pNodes = pTemp;
+ }
+ }
+ }
+ }
+ }
+ pSharedData->m_Lock.UnlockWrite();
+ if ( bIsCorrupt )
+ {
+ Warning( "***** HEAP IS CORRUPT (free compromised for block size %d,in %s heap, possible write after free *****)\n", m_nBlockSize, ( pSharedData->m_Allocator.IsVirtual() ) ? "virtual" : "physical" );
+ }
+ return nFreed;
+}
+
+template <typename CAllocator>
+bool CSmallBlockPool<CAllocator>::Validate()
+{
+#ifdef NO_SBH
+ return true;
+#else
+ int invalid = 0;
+
+ SharedData_t *pSharedData = GetSharedData();
+ pSharedData->m_Lock.LockForWrite();
+
+ byte **pPageBases = (byte **)stackalloc( m_nCommittedPages * sizeof(byte *) );
+ unsigned *pageCounts = (unsigned *)stackalloc( m_nCommittedPages * sizeof(unsigned) );
+ memset( pageCounts, 0, m_nCommittedPages * sizeof(int) );
+ unsigned nPages = 0;
+ unsigned sumAllocated = 0;
+ unsigned freeNotInFreeList = 0;
+
+ // Validate page list is consistent
+ if ( !m_pFirstPage )
+ {
+ if ( m_nCommittedPages != 0 )
+ {
+ invalid = __LINE__;
+ goto notValid;
+ }
+ }
+ else
+ {
+ PageStatus_t *pPage = m_pFirstPage;
+ while ( pPage )
+ {
+ pPageBases[nPages] = pSharedData->m_pBase + ( pPage - &pSharedData->m_PageStatus[0] ) * BYTES_PAGE;
+ if ( pPage->m_pPool != this )
+ {
+ invalid = __LINE__;
+ goto notValid;
+ }
+ if ( nPages > m_nCommittedPages )
+ {
+ invalid = __LINE__;
+ goto notValid;
+ }
+ sumAllocated += pPage->m_nAllocated;
+ if ( m_pNextAlloc >= pPageBases[nPages] && m_pNextAlloc < pPageBases[nPages] + BYTES_PAGE )
+ {
+ freeNotInFreeList = pageCounts[nPages] = ( ( pPageBases[nPages] + BYTES_PAGE ) - m_pNextAlloc ) / m_nBlockSize;
+ }
+
+ nPages++;
+ pPage = pPage->m_pNextPageInPool;
+ };
+
+ if ( nPages != m_nCommittedPages )
+ {
+ invalid = __LINE__;
+ goto notValid;
+ }
+ }
+
+ // Validate block counts
+ {
+ unsigned blocksPerPage = ( BYTES_PAGE / m_nBlockSize );
+#ifdef USE_NATIVE_SLIST
+ TSLNodeBase_t *pNode = (TSLNodeBase_t *)(m_FreeList.AccessUnprotected()->Next.Next);
+#else
+ TSLNodeBase_t *pNode = (TSLNodeBase_t *)(m_FreeList.AccessUnprotected()->value.Next);
+#endif
+ unsigned i;
+ while ( pNode )
+ {
+ for ( i = 0; i < nPages; i++ )
+ {
+ if ( (byte *)pNode >= pPageBases[i] && (byte *)pNode < pPageBases[i] + BYTES_PAGE )
+ {
+ pageCounts[i]++;
+ break;
+ }
+ }
+
+ if ( i == nPages )
+ {
+ invalid = __LINE__;
+ goto notValid;
+ }
+
+ pNode = pNode->Next;
+ }
+
+ PageStatus_t *pPage = m_pFirstPage;
+ i = 0;
+ while ( pPage )
+ {
+ unsigned nFreeOnPage = blocksPerPage - pPage->m_nAllocated;
+ if ( nFreeOnPage != pageCounts[i++] )
+ {
+ invalid = __LINE__;
+ goto notValid;
+ }
+ pPage = pPage->m_pNextPageInPool;
+ }
+ }
+
+notValid:
+ pSharedData->m_Lock.UnlockWrite();
+
+ if ( invalid != 0 )
+ {
+ return false;
+ }
+
+ return true;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template <typename CAllocator>
+CSmallBlockHeap<CAllocator>::CSmallBlockHeap()
+{
+ m_pSharedData = CPool::GetSharedData();
+
+ // Build a lookup table used to find the correct pool based on size
+ const int MAX_TABLE = MAX_SBH_BLOCK >> 2;
+ int i = 0;
+ int nBytesElement = 0;
+ CPool *pCurPool = NULL;
+ int iCurPool = 0;
+
+ // Blocks sized 0 - 128 are in pools in increments of 8
+ for ( ; i < 32; i++ )
+ {
+ if ( (i + 1) % 2 == 1)
+ {
+ nBytesElement += 8;
+ pCurPool = &m_Pools[iCurPool];
+ pCurPool->Init( nBytesElement );
+ iCurPool++;
+ m_PoolLookup[i] = pCurPool;
+ }
+ else
+ {
+ m_PoolLookup[i] = pCurPool;
+ }
+ }
+
+ // Blocks sized 129 - 256 are in pools in increments of 16
+ for ( ; i < 64; i++ )
+ {
+ if ( (i + 1) % 4 == 1)
+ {
+ nBytesElement += 16;
+ pCurPool = &m_Pools[iCurPool];
+ pCurPool->Init( nBytesElement );
+ iCurPool++;
+ m_PoolLookup[i] = pCurPool;
+ }
+ else
+ {
+ m_PoolLookup[i] = pCurPool;
+ }
+ }
+
+
+ // Blocks sized 257 - 512 are in pools in increments of 32
+ for ( ; i < 128; i++ )
+ {
+ if ( (i + 1) % 8 == 1)
+ {
+ nBytesElement += 32;
+ pCurPool = &m_Pools[iCurPool];
+ pCurPool->Init( nBytesElement );
+ iCurPool++;
+ m_PoolLookup[i] = pCurPool;
+ }
+ else
+ {
+ m_PoolLookup[i] = pCurPool;
+ }
+ }
+
+ // Blocks sized 513 - 768 are in pools in increments of 64
+ for ( ; i < 192; i++ )
+ {
+ if ( (i + 1) % 16 == 1)
+ {
+ nBytesElement += 64;
+ pCurPool = &m_Pools[iCurPool];
+ pCurPool->Init( nBytesElement );
+ iCurPool++;
+ m_PoolLookup[i] = pCurPool;
+ }
+ else
+ {
+ m_PoolLookup[i] = pCurPool;
+ }
+ }
+
+ // Blocks sized 769 - 1024 are in pools in increments of 128
+ for ( ; i < 256; i++ )
+ {
+ if ( (i + 1) % 32 == 1)
+ {
+ nBytesElement += 128;
+ pCurPool = &m_Pools[iCurPool];
+ pCurPool->Init( nBytesElement );
+ iCurPool++;
+ m_PoolLookup[i] = pCurPool;
+ }
+ else
+ {
+ m_PoolLookup[i] = pCurPool;
+ }
+ }
+
+ // Blocks sized 1025 - 2048 are in pools in increments of 256
+ for ( ; i < MAX_TABLE; i++ )
+ {
+ if ( (i + 1) % 64 == 1)
+ {
+ nBytesElement += 256;
+ pCurPool = &m_Pools[iCurPool];
+ pCurPool->Init( nBytesElement );
+ iCurPool++;
+ m_PoolLookup[i] = pCurPool;
+ }
+ else
+ {
+ m_PoolLookup[i] = pCurPool;
+ }
+ }
+
+ Assert( iCurPool == NUM_POOLS );
+}
+
+template <typename CAllocator>
+bool CSmallBlockHeap<CAllocator>::ShouldUse( size_t nBytes )
+{
+ return ( nBytes <= MAX_SBH_BLOCK );
+}
+
+template <typename CAllocator>
+bool CSmallBlockHeap<CAllocator>::IsOwner( void * p )
+{
+ if ( uintp(p) >= uintp(m_pSharedData->m_pBase) )
+ {
+ intp index = (intp)((byte *)p - m_pSharedData->m_pBase) / BYTES_PAGE;
+ return ( index < ARRAYSIZE(m_pSharedData->m_PageStatus) );
+ }
+ return false;
+}
+
+template <typename CAllocator>
+void *CSmallBlockHeap<CAllocator>::Alloc( size_t nBytes )
+{
+ if ( nBytes == 0)
+ {
+ nBytes = 1;
+ }
+ Assert( ShouldUse( nBytes ) );
+ CPool *pPool = FindPool( nBytes );
+ void *p = pPool->Alloc();
+ return p;
+}
+
+template <typename CAllocator>
+void *CSmallBlockHeap<CAllocator>::Realloc( void *p, size_t nBytes )
+{
+ if ( nBytes == 0)
+ {
+ nBytes = 1;
+ }
+
+ CPool *pOldPool = FindPool( p );
+ CPool *pNewPool = ( ShouldUse( nBytes ) ) ? FindPool( nBytes ) : NULL;
+
+ if ( pOldPool == pNewPool )
+ {
+ return p;
+ }
+
+ void *pNewBlock = NULL;
+
+ if ( !pNewBlock )
+ {
+ pNewBlock = MemAlloc_Alloc( nBytes ); // Call back out so blocks can move from the secondary to the primary pools
+ }
+
+ if ( !pNewBlock )
+ {
+ pNewBlock = malloc_internal( DEF_REGION, nBytes );
+ }
+
+ if ( pNewBlock )
+ {
+ size_t nBytesCopy = MIN( nBytes, pOldPool->GetBlockSize() );
+ memcpy( pNewBlock, p, nBytesCopy );
+ }
+ else if ( nBytes < pOldPool->GetBlockSize() )
+ {
+ return p;
+ }
+
+ pOldPool->Free( p );
+
+ return pNewBlock;
+}
+
+template <typename CAllocator>
+void CSmallBlockHeap<CAllocator>::Free( void *p )
+{
+ CPool *pPool = FindPool( p );
+ if ( pPool )
+ {
+ pPool->Free( p );
+ }
+ else
+ {
+ // we probably didn't hook some allocation and now we're freeing it or the heap has been trashed!
+ DebuggerBreakIfDebugging();
+ }
+}
+
+template <typename CAllocator>
+size_t CSmallBlockHeap<CAllocator>::GetSize( void *p )
+{
+ CPool *pPool = FindPool( p );
+ return pPool->GetBlockSize();
+}
+
+template <typename CAllocator>
+void CSmallBlockHeap<CAllocator>::Usage( size_t &bytesCommitted, size_t &bytesAllocated )
+{
+ bytesCommitted = 0;
+ bytesAllocated = 0;
+ for ( int i = 0; i < NUM_POOLS; i++ )
+ {
+ bytesCommitted += m_Pools[i].GetCommittedSize();
+ bytesAllocated += ( m_Pools[i].CountAllocatedBlocks() * m_Pools[i].GetBlockSize() );
+ }
+}
+
+template <typename CAllocator>
+void CSmallBlockHeap<CAllocator>::DumpStats( const char *pszTag, FILE *pFile )
+{
+ size_t bytesCommitted, bytesAllocated;
+ Usage( bytesCommitted, bytesAllocated );
+
+ if ( pFile )
+ {
+
+ for ( int i = 0; i < NUM_POOLS; i++ )
+ {
+ // output for vxconsole parsing
+ fprintf( pFile, "Pool %2i: (size: %4u) blocks: allocated:%5i free:%5i committed:%5i (committed size:%4u kb)\n",
+ i,
+ m_Pools[i].GetBlockSize(),
+ m_Pools[i].CountAllocatedBlocks(),
+ m_Pools[i].CountFreeBlocks(),
+ m_Pools[i].CountCommittedBlocks(),
+ m_Pools[i].GetCommittedSize() );
+ }
+ fprintf( pFile, "Totals (%s): Committed:%5u kb Allocated:%5u kb\n", pszTag, bytesCommitted / 1024, bytesAllocated / 1024 );
+ }
+ else
+ {
+ for ( int i = 0; i < NUM_POOLS; i++ )
+ {
+ Msg( "Pool %2i: (size: %4u) blocks: allocated:%5i free:%5i committed:%5i (committed size:%4u kb)\n",i, m_Pools[i].GetBlockSize(),m_Pools[i].CountAllocatedBlocks(), m_Pools[i].CountFreeBlocks(),m_Pools[i].CountCommittedBlocks(), m_Pools[i].GetCommittedSize() / 1024);
+ }
+
+ Msg( "Totals (%s): Committed:%5u kb Allocated:%5u kb\n", pszTag, bytesCommitted / 1024, bytesAllocated / 1024 );
+ }
+}
+
+template <typename CAllocator>
+CSmallBlockPool<CAllocator> *CSmallBlockHeap<CAllocator>::FindPool( size_t nBytes )
+{
+ return m_PoolLookup[(nBytes - 1) >> 2];
+}
+
+template <typename CAllocator>
+CSmallBlockPool<CAllocator> *CSmallBlockHeap<CAllocator>::FindPool( void *p )
+{
+ // NOTE: If p < m_pBase, cast to unsigned size_t will cause it to be large
+ size_t index = (size_t)((byte *)p - m_pSharedData->m_pBase) / BYTES_PAGE;
+ if ( index < ARRAYSIZE(m_pSharedData->m_PageStatus) )
+ return m_pSharedData->m_PageStatus[index].m_pPool;
+ return NULL;
+}
+
+template <typename CAllocator>
+size_t CSmallBlockHeap<CAllocator>::Compact( bool bIncremental )
+{
+ size_t nRecovered = 0;
+ if ( bIncremental )
+ {
+ static int iLastIncremental;
+
+ iLastIncremental++;
+ for ( int i = 0; i < NUM_POOLS; i++ )
+ {
+ int idx = ( i + iLastIncremental ) % NUM_POOLS;
+ nRecovered = m_Pools[idx].Compact( bIncremental );
+ if ( nRecovered )
+ {
+ iLastIncremental = idx;
+ break;
+ }
+
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < NUM_POOLS; i++ )
+ {
+ nRecovered += m_Pools[i].Compact( bIncremental );
+ }
+ }
+ return nRecovered;
+}
+
+template <typename CAllocator>
+bool CSmallBlockHeap<CAllocator>::Validate()
+{
+ bool valid = true;
+ for ( int i = 0; i < NUM_POOLS; i++ )
+ {
+ valid = m_Pools[i].Validate() && valid;
+ }
+ return valid;
+}
+
+#endif // MEM_SBH_ENABLED
+
+
+//-----------------------------------------------------------------------------
+// Lightweight memory tracking
+//-----------------------------------------------------------------------------
+
+#ifdef USE_LIGHT_MEM_DEBUG
+
+#ifndef LIGHT_MEM_DEBUG_REQUIRES_CMD_LINE_SWITCH
+#define UsingLMD() true
+#else // LIGHT_MEM_DEBUG_REQUIRES_CMD_LINE_SWITCH
+bool g_bUsingLMD = ( Plat_GetCommandLineA() ) ? ( strstr( Plat_GetCommandLineA(), "-uselmd" ) != NULL ) : false;
+#define UsingLMD() g_bUsingLMD
+#if defined( _PS3 )
+#error "Plat_GetCommandLineA() not implemented on PS3"
+#endif
+#endif // LIGHT_MEM_DEBUG_REQUIRES_CMD_LINE_SWITCH
+
+const char *g_pszUnknown = "unknown";
+
+struct Sentinal_t
+{
+ DWORD value[4];
+};
+
+Sentinal_t g_HeadSentinel =
+{
+ 0xdeadbeef,
+ 0xbaadf00d,
+ 0xbd122969,
+ 0xdeadbeef,
+};
+
+Sentinal_t g_TailSentinel =
+{
+ 0xbaadf00d,
+ 0xbd122969,
+ 0xdeadbeef,
+ 0xbaadf00d,
+};
+
+const byte g_FreeFill = 0xdd;
+
+static const uint LWD_FREE = 0;
+static const uint LWD_ALLOCATED = 1;
+
+#define LMD_STATUS_BITS ( 1 )
+#define LMD_ALIGN_BITS ( 32 - LMD_STATUS_BITS )
+#define LMD_MAX_ALIGN ( 1 << ( LMD_ALIGN_BITS - 1) )
+
+struct AllocHeader_t
+{
+ const char *pszModule;
+ int line;
+ size_t nBytes;
+ uint status : LMD_STATUS_BITS;
+ uint align : LMD_ALIGN_BITS;
+ Sentinal_t sentinal;
+};
+
+const int g_nRecentFrees = ( IsPC() ) ? 8192 : 512;
+AllocHeader_t **g_pRecentFrees = (AllocHeader_t **)calloc( g_nRecentFrees, sizeof(AllocHeader_t *) );
+int g_iNextFreeSlot;
+
+#define INTERNAL_INLINE
+
+#define LMDToHeader( pUserPtr ) ( ((AllocHeader_t *)(pUserPtr)) - 1 )
+#define LMDFromHeader( pHeader ) ( (byte *)((pHeader) + 1) )
+
+CThreadFastMutex g_LMDMutex;
+
+const char *g_pLMDFileName = NULL;
+int g_nLMDLine;
+int g_iLMDDepth;
+
+void LMDPushAllocDbgInfo( const char *pFileName, int nLine )
+{
+ if ( ThreadInMainThread() )
+ {
+ if ( !g_iLMDDepth )
+ {
+ g_pLMDFileName = pFileName;
+ g_nLMDLine = nLine;
+ }
+ g_iLMDDepth++;
+ }
+}
+
+void LMDPopAllocDbgInfo()
+{
+ if ( ThreadInMainThread() && g_iLMDDepth > 0 )
+ {
+ g_iLMDDepth--;
+ if ( g_iLMDDepth == 0 )
+ {
+ g_pLMDFileName = NULL;
+ g_nLMDLine = 0;
+ }
+ }
+}
+
+
+void LMDReportInvalidBlock( AllocHeader_t *pHeader, const char *pszMessage )
+{
+ char szMsg[256];
+ if ( pHeader )
+ {
+ sprintf( szMsg, "HEAP IS CORRUPT: %s (block 0x%x, size %d, alignment %d)\n", pszMessage, (size_t)LMDFromHeader( pHeader ), pHeader->nBytes, pHeader->align );
+ }
+ else
+ {
+ sprintf( szMsg, "HEAP IS CORRUPT: %s\n", pszMessage );
+ }
+ if ( Plat_IsInDebugSession() )
+ {
+ DebuggerBreak();
+ }
+ else
+ {
+ WriteMiniDump();
+ }
+#ifdef IS_WINDOWS_PC
+ ::MessageBox( NULL, szMsg, "Error", MB_SYSTEMMODAL | MB_OK );
+#else
+ Warning( szMsg );
+#endif
+}
+
+void LMDValidateBlock( AllocHeader_t *pHeader, bool bFreeList )
+{
+ if ( !pHeader )
+ return;
+
+ if ( memcmp( &pHeader->sentinal, &g_HeadSentinel, sizeof(Sentinal_t) ) != 0 )
+ {
+ LMDReportInvalidBlock( pHeader, "Head sentinel corrupt" );
+ }
+ if ( memcmp( ((Sentinal_t *)(LMDFromHeader( pHeader ) + pHeader->nBytes)), &g_TailSentinel, sizeof(Sentinal_t) ) != 0 )
+ {
+ LMDReportInvalidBlock( pHeader, "Tail sentinel corrupt" );
+ }
+ if ( bFreeList )
+ {
+ byte *pCur = (byte *)pHeader + sizeof(AllocHeader_t);
+ byte *pLimit = pCur + pHeader->nBytes;
+ while ( pCur != pLimit )
+ {
+ if ( *pCur++ != g_FreeFill )
+ {
+ LMDReportInvalidBlock( pHeader, "Write after free" );
+ }
+ }
+ }
+}
+
+
+size_t LMDComputeHeaderSize( size_t align = 0 )
+{
+ if ( !align )
+ return sizeof(AllocHeader_t);
+ // For aligned allocs, the header is preceded by padding which maintains alignment
+ if ( align > LMD_MAX_ALIGN )
+ s_StdMemAlloc.SetCRTAllocFailed( align ); // TODO: could convert alignment to exponent to get around this, or use a flag for alignments over 1KB or 1MB...
+ return ( ( sizeof( AllocHeader_t ) + (align-1) ) & ~(align-1) );
+}
+
+size_t LMDAdjustSize( size_t &nBytes, size_t align = 0 )
+{
+ if ( !UsingLMD() )
+ return nBytes;
+ // Add data before+after each alloc
+ return ( nBytes + LMDComputeHeaderSize( align ) + sizeof(Sentinal_t) );
+}
+
+void *LMDNoteAlloc( void *p, size_t nBytes, size_t align = 0, const char *pszModule = g_pszUnknown, int line = 0 )
+{
+ if ( !UsingLMD() )
+ {
+ return p;
+ }
+
+ if ( g_pLMDFileName )
+ {
+ pszModule = g_pLMDFileName;
+ line = g_nLMDLine;
+ }
+
+ if ( p )
+ {
+ byte *pUserPtr = ((byte*)p) + LMDComputeHeaderSize( align );
+ AllocHeader_t *pHeader = LMDToHeader( pUserPtr );
+ pHeader->pszModule = pszModule;
+ pHeader->line = line;
+ pHeader->status = LWD_ALLOCATED;
+ pHeader->nBytes = nBytes;
+ pHeader->align = (uint)align;
+ pHeader->sentinal = g_HeadSentinel;
+ *((Sentinal_t *)(pUserPtr + pHeader->nBytes)) = g_TailSentinel;
+ LMDValidateBlock( pHeader, false );
+ return pUserPtr;
+ }
+ return NULL;
+
+ // Some SBH clients rely on allocations > 16 bytes being 16-byte aligned, so we mustn't break that assumption:
+ MEMSTD_COMPILE_TIME_ASSERT( sizeof( AllocHeader_t ) % 16 == 0 );
+}
+
+void *LMDNoteFree( void *p )
+{
+ if ( !UsingLMD() )
+ {
+ return p;
+ }
+
+ AUTO_LOCK( g_LMDMutex );
+ if ( !p )
+ {
+ return NULL;
+ }
+
+ AllocHeader_t *pHeader = LMDToHeader( p );
+ if ( pHeader->status == LWD_FREE )
+ {
+ LMDReportInvalidBlock( pHeader, "Double free" );
+ }
+ LMDValidateBlock( pHeader, false );
+
+ AllocHeader_t *pToReturn;
+ if ( pHeader->nBytes < 16*1024 )
+ {
+ pToReturn = g_pRecentFrees[g_iNextFreeSlot];
+ LMDValidateBlock( pToReturn, true );
+
+ g_pRecentFrees[g_iNextFreeSlot] = pHeader;
+ g_iNextFreeSlot = (g_iNextFreeSlot + 1 ) % g_nRecentFrees;
+ }
+ else
+ {
+ pToReturn = pHeader;
+ LMDValidateBlock( g_pRecentFrees[rand() % g_nRecentFrees], true );
+ }
+
+ pHeader->status = LWD_FREE;
+ memset( pHeader + 1, g_FreeFill, pHeader->nBytes );
+
+ if ( pToReturn && ( pToReturn->align ) )
+ {
+ // For aligned allocations, the actual system allocation starts *before* the LMD header:
+ size_t headerPadding = LMDComputeHeaderSize( pToReturn->align ) - sizeof( AllocHeader_t );
+ return ( ((byte*)pToReturn) - headerPadding );
+ }
+
+ return pToReturn;
+}
+
+size_t LMDGetSize( void *p )
+{
+ if ( !UsingLMD() )
+ {
+ return (size_t)(-1);
+ }
+
+ AllocHeader_t *pHeader = LMDToHeader( p );
+ return pHeader->nBytes;
+}
+
+bool LMDValidateHeap()
+{
+ if ( !UsingLMD() )
+ {
+ return true;
+ }
+
+ AUTO_LOCK( g_LMDMutex );
+ for ( int i = 0; i < g_nRecentFrees && g_pRecentFrees[i]; i++ )
+ {
+ LMDValidateBlock( g_pRecentFrees[i], true );
+ }
+ return true;
+}
+
+void *LMDRealloc( void *pMem, size_t nSize, size_t align = 0, const char *pszModule = g_pszUnknown, int line = 0 )
+{
+ if ( nSize == 0 )
+ {
+ s_StdMemAlloc.Free( pMem );
+ return NULL;
+ }
+ void *pNew;
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+ if ( align )
+ pNew = s_StdMemAlloc.AllocAlign( nSize, align, pszModule, line );
+ else
+#endif // MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+ pNew = s_StdMemAlloc.Alloc( nSize, pszModule, line );
+ if ( !pMem )
+ {
+ return pNew;
+ }
+ AllocHeader_t *pHeader = LMDToHeader( pMem );
+ if ( align != pHeader->align )
+ {
+ LMDReportInvalidBlock( pHeader, "Realloc changed alignment!" );
+ }
+ size_t nCopySize = MIN( nSize, pHeader->nBytes );
+ memcpy( pNew, pMem, nCopySize );
+ s_StdMemAlloc.Free( pMem, pszModule, line );
+ return pNew;
+}
+
+#else // USE_LIGHT_MEM_DEBUG
+
+#define INTERNAL_INLINE FORCEINLINE
+#define UsingLMD() false
+FORCEINLINE size_t LMDAdjustSize( size_t &nBytes, size_t align = 0 ) { return nBytes; }
+#define LMDNoteAlloc( pHeader, ... ) (pHeader)
+#define LMDNoteFree( pHeader, ... ) (pHeader)
+#define LMDGetSize( pHeader ) (size_t)(-1)
+#define LMDToHeader( pHeader ) (pHeader)
+#define LMDFromHeader( pHeader ) (pHeader)
+#define LMDValidateHeap() (true)
+#define LMDPushAllocDbgInfo( pFileName, nLine ) ((void)0)
+#define LMDPopAllocDbgInfo() ((void)0)
+FORCEINLINE void *LMDRealloc( void *pMem, size_t nSize, size_t align = 0, const char *pszModule = NULL, int line = 0 ) { return NULL; }
+
+#endif // USE_LIGHT_MEM_DEBUG
+
+//-----------------------------------------------------------------------------
+// Internal versions
+//-----------------------------------------------------------------------------
+
+INTERNAL_INLINE void *CStdMemAlloc::InternalAllocFromPools( size_t nSize )
+{
+#if MEM_SBH_ENABLED
+ void *pMem;
+
+ pMem = m_PrimarySBH.Alloc( nSize );
+ if ( pMem )
+ {
+ return pMem;
+ }
+
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ pMem = m_SecondarySBH.Alloc( nSize );
+ if ( pMem )
+ {
+ return pMem;
+ }
+#endif // MEMALLOC_USE_SECONDARY_SBH
+
+#ifndef MEMALLOC_NO_FALLBACK
+ pMem = m_FallbackSBH.Alloc( nSize );
+ if ( pMem )
+ {
+ return pMem;
+ }
+#endif // MEMALLOC_NO_FALLBACK
+
+ CallAllocFailHandler( nSize );
+#endif // MEM_SBH_ENABLED
+ return NULL;
+}
+
+INTERNAL_INLINE void *CStdMemAlloc::InternalAlloc( int region, size_t nSize )
+{
+ PROFILE_ALLOC(Malloc);
+
+ void *pMem;
+
+#if MEM_SBH_ENABLED
+ if ( m_PrimarySBH.ShouldUse( nSize ) ) // test valid for either pool
+ {
+ pMem = InternalAllocFromPools( nSize );
+ if ( !pMem )
+ {
+ CompactOnFail();
+ pMem = InternalAllocFromPools( nSize );
+ }
+ if ( pMem )
+ {
+ ApplyMemoryInitializations( pMem, nSize );
+ return pMem;
+ }
+
+ ExecuteOnce( DevWarning( "\n\nDRASTIC MEMORY OVERFLOW: Fell out of small block heap!\n\n\n") );
+ }
+#endif // MEM_SBH_ENABLED
+
+ pMem = malloc_internal( region, nSize );
+ if ( !pMem )
+ {
+ CompactOnFail();
+ pMem = malloc_internal( region, nSize );
+ if ( !pMem )
+ {
+ SetCRTAllocFailed( nSize );
+ return NULL;
+ }
+ }
+
+ ApplyMemoryInitializations( pMem, nSize );
+ return pMem;
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+INTERNAL_INLINE void *CStdMemAlloc::InternalAllocAligned( int region, size_t nSize, size_t align )
+{
+ PROFILE_ALLOC(MallocAligned);
+
+ void *pMem;
+
+#if MEM_SBH_ENABLED
+ size_t nSizeAligned = ( nSize + align - 1 ) & ~( align - 1 );
+ if ( m_PrimarySBH.ShouldUse( nSizeAligned ) ) // test valid for either pool
+ {
+ pMem = InternalAllocFromPools( nSizeAligned );
+ if ( !pMem )
+ {
+ CompactOnFail();
+ pMem = InternalAllocFromPools( nSizeAligned );
+ }
+ if ( pMem )
+ {
+ ApplyMemoryInitializations( pMem, nSizeAligned );
+ return pMem;
+ }
+
+ ExecuteOnce( DevWarning( "Warning: Fell out of small block heap!\n") );
+ }
+#endif // MEM_SBH_ENABLED
+
+ pMem = malloc_aligned_internal( region, nSize, align );
+ if ( !pMem )
+ {
+ CompactOnFail();
+ pMem = malloc_aligned_internal( region, nSize, align );
+ if ( !pMem )
+ {
+ SetCRTAllocFailed( nSize );
+ return NULL;
+ }
+ }
+
+ ApplyMemoryInitializations( pMem, nSize );
+ return pMem;
+}
+#endif // MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+
+INTERNAL_INLINE void *CStdMemAlloc::InternalRealloc( void *pMem, size_t nSize )
+{
+ if ( !pMem )
+ {
+ return RegionAlloc( DEF_REGION, nSize );
+ }
+
+ PROFILE_ALLOC(Realloc);
+
+#if MEM_SBH_ENABLED
+ if ( m_PrimarySBH.IsOwner( pMem ) )
+ {
+ return m_PrimarySBH.Realloc( pMem, nSize );
+ }
+
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ if ( m_SecondarySBH.IsOwner( pMem ) )
+ {
+ return m_SecondarySBH.Realloc( pMem, nSize );
+ }
+
+#endif // MEMALLOC_USE_SECONDARY_SBH
+
+#ifndef MEMALLOC_NO_FALLBACK
+ if ( m_FallbackSBH.IsOwner( pMem ) )
+ {
+ return m_FallbackSBH.Realloc( pMem, nSize );
+ }
+#endif // MEMALLOC_NO_FALLBACK
+
+#endif // MEM_SBH_ENABLED
+
+ void *pRet = realloc_internal( pMem, nSize );
+ if ( !pRet )
+ {
+ CompactOnFail();
+ pRet = realloc_internal( pMem, nSize );
+ if ( !pRet )
+ {
+ SetCRTAllocFailed( nSize );
+ }
+ }
+
+ return pRet;
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+INTERNAL_INLINE void *CStdMemAlloc::InternalReallocAligned( void *pMem, size_t nSize, size_t align )
+{
+ if ( !pMem )
+ {
+ return InternalAllocAligned( DEF_REGION, nSize, align );
+ }
+
+ PROFILE_ALLOC(ReallocAligned);
+
+#if MEM_SBH_ENABLED
+ if ( m_PrimarySBH.IsOwner( pMem ) )
+ {
+ return m_PrimarySBH.Realloc( pMem, nSize );
+ }
+
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ if ( m_SecondarySBH.IsOwner( pMem ) )
+ {
+ return m_SecondarySBH.Realloc( pMem, nSize );
+ }
+#endif // MEMALLOC_USE_SECONDARY_SBH
+
+#ifndef MEMALLOC_NO_FALLBACK
+ if ( m_FallbackSBH.IsOwner( pMem ) )
+ {
+ return m_FallbackSBH.Realloc( pMem, nSize );
+ }
+#endif // MEMALLOC_NO_FALLBACK
+
+#endif // MEM_SBH_ENABLED
+
+ void *pRet = realloc_aligned_internal( pMem, nSize, align );
+ if ( !pRet )
+ {
+ CompactOnFail();
+ pRet = realloc_aligned_internal( pMem, nSize, align );
+ if ( !pRet )
+ {
+ SetCRTAllocFailed( nSize );
+ }
+ }
+
+ return pRet;
+}
+#endif
+
+INTERNAL_INLINE void CStdMemAlloc::InternalFree( void *pMem )
+{
+ if ( !pMem )
+ {
+ return;
+ }
+
+ PROFILE_ALLOC(Free);
+
+#if MEM_SBH_ENABLED
+ if ( m_PrimarySBH.IsOwner( pMem ) )
+ {
+ m_PrimarySBH.Free( pMem );
+ return;
+ }
+
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ if ( m_SecondarySBH.IsOwner( pMem ) )
+ {
+ return m_SecondarySBH.Free( pMem );
+ }
+#endif // MEMALLOC_USE_SECONDARY_SBH
+
+#ifndef MEMALLOC_NO_FALLBACK
+ if ( m_FallbackSBH.IsOwner( pMem ) )
+ {
+ m_FallbackSBH.Free( pMem );
+ return;
+ }
+#endif // MEMALLOC_NO_FALLBACK
+
+#endif // MEM_SBH_ENABLED
+
+ free_internal( pMem );
+}
+
+void CStdMemAlloc::CompactOnFail()
+{
+ CompactHeap();
+}
+
+//-----------------------------------------------------------------------------
+// Release versions
+//-----------------------------------------------------------------------------
+
+void *CStdMemAlloc::Alloc( size_t nSize )
+{
+ size_t nAdjustedSize = LMDAdjustSize( nSize );
+ return LMDNoteAlloc( CStdMemAlloc::InternalAlloc( DEF_REGION, nAdjustedSize ), nSize );
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+void * CStdMemAlloc::AllocAlign( size_t nSize, size_t align )
+{
+ size_t nAdjustedSize = LMDAdjustSize( nSize, align );
+ return LMDNoteAlloc( CStdMemAlloc::InternalAllocAligned( DEF_REGION, nAdjustedSize, align ), nSize, align );
+}
+#endif // MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+
+void *CStdMemAlloc::Realloc( void *pMem, size_t nSize )
+{
+ if ( UsingLMD() )
+ return LMDRealloc( pMem, nSize );
+ return CStdMemAlloc::InternalRealloc( pMem, nSize );
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+void * CStdMemAlloc::ReallocAlign( void *pMem, size_t nSize, size_t align )
+{
+ if ( UsingLMD() )
+ return LMDRealloc( pMem, nSize, align );
+ return CStdMemAlloc::InternalReallocAligned( pMem, nSize, align );
+}
+#endif // MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+
+void CStdMemAlloc::Free( void *pMem )
+{
+ pMem = LMDNoteFree( pMem );
+ CStdMemAlloc::InternalFree( pMem );
+}
+
+void *CStdMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize )
+{
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Debug versions
+//-----------------------------------------------------------------------------
+void *CStdMemAlloc::Alloc( size_t nSize, const char *pFileName, int nLine )
+{
+ size_t nAdjustedSize = LMDAdjustSize( nSize );
+ return LMDNoteAlloc( CStdMemAlloc::InternalAlloc( DEF_REGION, nAdjustedSize ), nSize, 0, pFileName, nLine );
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+void *CStdMemAlloc::AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine )
+{
+ size_t nAdjustedSize = LMDAdjustSize( nSize, align );
+ return LMDNoteAlloc( CStdMemAlloc::InternalAllocAligned( DEF_REGION, nAdjustedSize, align ), nSize, align, pFileName, nLine );
+}
+#endif // MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+
+void *CStdMemAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine )
+{
+ if ( UsingLMD() )
+ return LMDRealloc( pMem, nSize, 0, pFileName, nLine );
+ return CStdMemAlloc::InternalRealloc( pMem, nSize );
+}
+
+#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+void * CStdMemAlloc::ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine )
+{
+ if ( UsingLMD() )
+ return LMDRealloc( pMem, nSize, align, pFileName, nLine );
+ return CStdMemAlloc::InternalReallocAligned( pMem, nSize, align );
+}
+#endif // MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
+
+void CStdMemAlloc::Free( void *pMem, const char *pFileName, int nLine )
+{
+ pMem = LMDNoteFree( pMem );
+ CStdMemAlloc::InternalFree( pMem );
+}
+
+void *CStdMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine )
+{
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Region support
+//-----------------------------------------------------------------------------
+void *CStdMemAlloc::RegionAlloc( int region, size_t nSize )
+{
+ size_t nAdjustedSize = LMDAdjustSize( nSize );
+ return LMDNoteAlloc( CStdMemAlloc::InternalAlloc( region, nAdjustedSize ), nSize );
+}
+
+void *CStdMemAlloc::RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine )
+{
+ size_t nAdjustedSize = LMDAdjustSize( nSize );
+ return LMDNoteAlloc( CStdMemAlloc::InternalAlloc( region, nAdjustedSize ), nSize, 0, pFileName, nLine );
+}
+
+#if defined (LINUX)
+#include <malloc.h>
+#elif defined (OSX)
+#define malloc_usable_size( ptr ) malloc_size( ptr )
+extern "C" {
+ extern size_t malloc_size( const void *ptr );
+}
+#endif // LINUX/OSX
+
+//-----------------------------------------------------------------------------
+// Returns the size of a particular allocation (NOTE: may be larger than the size requested!)
+//-----------------------------------------------------------------------------
+size_t CStdMemAlloc::GetSize( void *pMem )
+{
+ if ( !pMem )
+ return CalcHeapUsed();
+
+ if ( UsingLMD() )
+ {
+ return LMDGetSize( pMem );
+ }
+
+#if MEM_SBH_ENABLED
+ if ( m_PrimarySBH.IsOwner( pMem ) )
+ {
+ return m_PrimarySBH.GetSize( pMem );
+ }
+
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ if ( m_SecondarySBH.IsOwner( pMem ) )
+ {
+ return m_SecondarySBH.GetSize( pMem );
+ }
+#endif // MEMALLOC_USE_SECONDARY_SBH
+
+#ifndef MEMALLOC_NO_FALLBACK
+ if ( m_FallbackSBH.IsOwner( pMem ) )
+ {
+ return m_FallbackSBH.GetSize( pMem );
+ }
+#endif // MEMALLOC_NO_FALLBACK
+
+#endif // MEM_SBH_ENABLED
+
+ return msize_internal( pMem );
+}
+
+
+//-----------------------------------------------------------------------------
+// Force file + line information for an allocation
+//-----------------------------------------------------------------------------
+void CStdMemAlloc::PushAllocDbgInfo( const char *pFileName, int nLine )
+{
+ LMDPushAllocDbgInfo( pFileName, nLine );
+}
+
+void CStdMemAlloc::PopAllocDbgInfo()
+{
+ LMDPopAllocDbgInfo();
+}
+
+//-----------------------------------------------------------------------------
+// FIXME: Remove when we make our own heap! Crt stuff we're currently using
+//-----------------------------------------------------------------------------
+int32 CStdMemAlloc::CrtSetBreakAlloc( int32 lNewBreakAlloc )
+{
+ return 0;
+}
+
+int CStdMemAlloc::CrtSetReportMode( int nReportType, int nReportMode )
+{
+ return 0;
+}
+
+int CStdMemAlloc::CrtIsValidHeapPointer( const void *pMem )
+{
+ return 1;
+}
+
+int CStdMemAlloc::CrtIsValidPointer( const void *pMem, unsigned int size, int access )
+{
+ return 1;
+}
+
+int CStdMemAlloc::CrtCheckMemory( void )
+{
+#ifndef _CERT
+ LMDValidateHeap();
+#if MEM_SBH_ENABLED
+ if ( !m_PrimarySBH.Validate() )
+ {
+ ExecuteOnce( Msg( "Small block heap is corrupt (primary)\n " ) );
+ }
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ if ( !m_SecondarySBH.Validate() )
+ {
+ ExecuteOnce( Msg( "Small block heap is corrupt (secondary)\n " ) );
+ }
+#endif // MEMALLOC_USE_SECONDARY_SBH
+#ifndef MEMALLOC_NO_FALLBACK
+ if ( !m_FallbackSBH.Validate() )
+ {
+ ExecuteOnce( Msg( "Small block heap is corrupt (fallback)\n " ) );
+ }
+#endif // MEMALLOC_NO_FALLBACK
+#endif // MEM_SBH_ENABLED
+#endif // _CERT
+ return 1;
+}
+
+int CStdMemAlloc::CrtSetDbgFlag( int nNewFlag )
+{
+ return 0;
+}
+
+void CStdMemAlloc::CrtMemCheckpoint( _CrtMemState *pState )
+{
+}
+
+// FIXME: Remove when we have our own allocator
+void* CStdMemAlloc::CrtSetReportFile( int nRptType, void* hFile )
+{
+ return 0;
+}
+
+void* CStdMemAlloc::CrtSetReportHook( void* pfnNewHook )
+{
+ return 0;
+}
+
+int CStdMemAlloc::CrtDbgReport( int nRptType, const char * szFile,
+ int nLine, const char * szModule, const char * pMsg )
+{
+ return 0;
+}
+
+int CStdMemAlloc::heapchk()
+{
+#ifdef _WIN32
+ CrtCheckMemory();
+ return _HEAPOK;
+#else
+ return 1;
+#endif
+}
+
+void CStdMemAlloc::DumpStats()
+{
+ DumpStatsFileBase( "memstats" );
+}
+
+void CStdMemAlloc::DumpStatsFileBase( char const *pchFileBase )
+{
+#if defined( _WIN32 ) || defined( _GAMECONSOLE )
+ char filename[ 512 ];
+ _snprintf( filename, sizeof( filename ) - 1,
+#ifdef _X360
+ "D:\\%s.txt",
+#elif defined( _PS3 )
+ "/app_home/%s.txt",
+#else
+ "%s.txt",
+#endif
+ pchFileBase );
+ filename[ sizeof( filename ) - 1 ] = 0;
+ FILE *pFile = ( IsGameConsole() ) ? NULL : fopen( filename, "wt" );
+
+#if MEM_SBH_ENABLED
+ if ( pFile )
+ fprintf( pFile, "Fixed Page SBH:\n" );
+ else
+ Msg( "Fixed Page SBH:\n" );
+ m_PrimarySBH.DumpStats("Fixed Page SBH", pFile);
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ if ( pFile )
+ fprintf( pFile, "Secondary Fixed Page SBH:\n" );
+ else
+ Msg( "Secondary Page SBH:\n" );
+ m_SecondarySBH.DumpStats("Secondary Page SBH", pFile);
+#endif // MEMALLOC_USE_SECONDARY_SBH
+#ifndef MEMALLOC_NO_FALLBACK
+ if ( pFile )
+ fprintf( pFile, "\nFallback SBH:\n" );
+ else
+ Msg( "\nFallback SBH:\n" );
+ m_FallbackSBH.DumpStats("Fallback SBH", pFile); // Dump statistics to small block heap
+#endif // MEMALLOC_NO_FALLBACK
+#endif // MEM_SBH_ENABLED
+
+#ifdef _PS3
+ malloc_managed_size mms;
+ (g_pMemOverrideRawCrtFns->pfn_malloc_stats)( &mms );
+ Msg( "PS3 malloc_stats: %u / %u / %u \n", mms.current_inuse_size, mms.current_system_size, mms.max_system_size );
+#endif // _PS3
+
+ heapstats_internal( pFile );
+#if defined( _X360 )
+ XBX_rMemDump( filename );
+#endif
+
+ if ( pFile )
+ fclose( pFile );
+#endif // _WIN32 || _GAMECONSOLE
+}
+
+IVirtualMemorySection * CStdMemAlloc::AllocateVirtualMemorySection( size_t numMaxBytes )
+{
+#if defined( _GAMECONSOLE ) || defined( _WIN32 )
+ extern IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes );
+ return VirtualMemoryManager_AllocateVirtualMemorySection( numMaxBytes );
+#else
+ return NULL;
+#endif
+}
+
+size_t CStdMemAlloc::ComputeMemoryUsedBy( char const *pchSubStr )
+{
+ return 0;//dbg heap only.
+}
+
+static inline size_t ExtraDevkitMemory( void )
+{
+#if defined( _PS3 )
+ // 213MB are available in retail mode, so adjust free mem to reflect that even if we're in devkit mode
+ const size_t RETAIL_SIZE = 213*1024*1024;
+ static sys_memory_info stat;
+ sys_memory_get_user_memory_size( &stat );
+ if ( stat.total_user_memory > RETAIL_SIZE )
+ return ( stat.total_user_memory - RETAIL_SIZE );
+#elif defined( _X360 )
+ // TODO: detect the new 1GB devkit...
+#endif // _PS3/_X360
+ return 0;
+}
+
+void CStdMemAlloc::GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory )
+{
+ if ( !pUsedMemory || !pFreeMemory )
+ return;
+
+ size_t dlMallocFree = 0;
+#if defined( USE_DLMALLOC )
+ // Account for free memory contained within DLMalloc's FIRST region. The rationale is as follows:
+ // - the first region is supposed to service large allocations via virtual allocation, and to grow as
+ // needed (until all physical pages are used), so true 'out of memory' failures should occur there.
+ // - other regions (the 2-256kb 'medium block heap', or per-DLL heaps, and the Small Block Heap)
+ // are sized to a pre-determined high watermark, and not intended to grow. Free memory within
+ // those regions is not available for large allocations, so adding that to the 'free memory'
+ // yields confusing data which does not correspond well with out-of-memory failures.
+ mallinfo info = mspace_mallinfo( g_AllocRegions[ 0 ] );
+ dlMallocFree += info.fordblks;
+#endif // USE_DLMALLOC
+
+#if defined ( _X360 )
+
+ // GlobalMemoryStatus tells us how much physical memory is free
+ MEMORYSTATUS stat;
+ ::GlobalMemoryStatus( &stat );
+ *pFreeMemory = stat.dwAvailPhys;
+ *pFreeMemory += dlMallocFree;
+ // Adjust free mem to reflect a retail box, even if we're using a devkit with extra memory
+ *pFreeMemory -= ExtraDevkitMemory();
+
+ // Used is total minus free (discount the 32MB system reservation)
+ *pUsedMemory = ( stat.dwTotalPhys - 32*1024*1024 ) - *pFreeMemory;
+
+#elif defined( _PS3 )
+
+ // NOTE: we use dlmalloc instead of the system heap, so we do NOT count the system heap's free space!
+ //static malloc_managed_size mms;
+ //(g_pMemOverrideRawCrtFns->pfn_malloc_stats)( &mms );
+ //int heapFree = mms.current_system_size - mms.current_inuse_size;
+
+ // sys_memory_get_user_memory_size tells us how much PPU memory is used/free
+ static sys_memory_info stat;
+ sys_memory_get_user_memory_size( &stat );
+ *pFreeMemory = stat.available_user_memory;
+ *pFreeMemory += dlMallocFree;
+ *pUsedMemory = stat.total_user_memory - *pFreeMemory;
+ // Adjust free mem to reflect a retail box, even if we're using a devkit with extra memory
+ *pFreeMemory -= ExtraDevkitMemory();
+
+#else // _X360/_PS3/other
+
+ // no data
+ *pFreeMemory = 0;
+ *pUsedMemory = 0;
+
+#endif // _X360/_PS3//other
+}
+
+#define MAX_GENERIC_MEMORY_STATS 64
+GenericMemoryStat_t g_MemStats[MAX_GENERIC_MEMORY_STATS];
+int g_nMemStats = 0;
+static inline int AddGenericMemoryStat( const char *name, int value )
+{
+ Assert( g_nMemStats < MAX_GENERIC_MEMORY_STATS );
+ if ( g_nMemStats < MAX_GENERIC_MEMORY_STATS )
+ {
+ g_MemStats[ g_nMemStats ].name = name;
+ g_MemStats[ g_nMemStats ].value = value;
+ g_nMemStats++;
+ }
+ return g_nMemStats;
+}
+
+int CStdMemAlloc::GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats )
+{
+ if ( !ppMemoryStats )
+ return 0;
+ g_nMemStats = 0;
+
+#if MEM_SBH_ENABLED
+ {
+ // Small block heap
+ size_t SBHCommitted = 0, SBHAllocated = 0;
+ size_t commitTmp, allocTmp;
+#if MEM_SBH_ENABLED
+ m_PrimarySBH.Usage( commitTmp, allocTmp );
+ SBHCommitted += commitTmp; SBHAllocated += allocTmp;
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ m_SecondarySBH.Usage( commitTmp, allocTmp );
+ SBHCommitted += commitTmp; SBHAllocated += allocTmp;
+#endif // MEMALLOC_USE_SECONDARY_SBH
+#ifndef MEMALLOC_NO_FALLBACK
+ m_FallbackSBH.Usage( commitTmp, allocTmp );
+ SBHCommitted += commitTmp; SBHAllocated += allocTmp;
+#endif // MEMALLOC_NO_FALLBACK
+#endif // MEM_SBH_ENABLED
+
+ static size_t SBHMaxCommitted = 0; SBHMaxCommitted = MAX( SBHMaxCommitted, SBHCommitted );
+ AddGenericMemoryStat( "SBH_cur", (int)SBHCommitted );
+ AddGenericMemoryStat( "SBH_max", (int)SBHMaxCommitted );
+ }
+#endif // MEM_SBH_ENABLED
+
+#if defined( USE_DLMALLOC )
+#if !defined( MEMALLOC_REGIONS ) && defined( MEMALLOC_SEGMENT_MIXED )
+ {
+ // Medium block heap
+ mallinfo infoMBH = mspace_mallinfo( g_AllocRegions[ 1 ] );
+ size_t nMBHCurUsed = infoMBH.uordblks;// nMBH_WRONG_MaxUsed = infoMBH.usmblks; // TODO: figure out why dlmalloc mis-reports MBH max usage (it just returns the footprint)
+ static size_t nMBHMaxUsed = 0; nMBHMaxUsed = MAX( nMBHMaxUsed, nMBHCurUsed );
+ AddGenericMemoryStat( "MBH_cur", (int)nMBHCurUsed );
+ AddGenericMemoryStat( "MBH_max", (int)nMBHMaxUsed );
+
+ // Large block heap
+ mallinfo infoLBH = mspace_mallinfo( g_AllocRegions[ 0 ] );
+ size_t nLBHCurUsed = mspace_footprint( g_AllocRegions[ 0 ] ), nLBHMaxUsed = mspace_max_footprint( g_AllocRegions[ 0 ] ), nLBHArenaSize = infoLBH.arena, nLBHFree = infoLBH.fordblks;
+ AddGenericMemoryStat( "LBH_cur", (int)nLBHCurUsed );
+ AddGenericMemoryStat( "LBH_max", (int)nLBHMaxUsed );
+ // LBH arena used+free (these are non-virtual allocations - there should be none, since we only allocate 256KB+ items in the LBH)
+ // TODO: I currently see the arena grow to 320KB due to a larger allocation being realloced down... if this gets worse, add an 'ALWAYS use VMM' flag to the mspace.
+ AddGenericMemoryStat( "LBH_arena", (int)nLBHArenaSize );
+ AddGenericMemoryStat( "LBH_free", (int)nLBHFree );
+ }
+#else // (!MEMALLOC_REGIONS && MEMALLOC_SEGMENT_MIXED)
+ {
+ // Single dlmalloc heap (TODO: per-DLL heap stats, if we resurrect that)
+ mallinfo info = mspace_mallinfo( g_AllocRegions[ 0 ] );
+ AddGenericMemoryStat( "mspace_cur", (int)info.uordblks );
+ AddGenericMemoryStat( "mspace_max", (int)info.usmblks );
+ AddGenericMemoryStat( "mspace_size", (int)mspace_footprint( g_AllocRegions[ 0 ] ) );
+ }
+#endif // (!MEMALLOC_REGIONS && MEMALLOC_SEGMENT_MIXED)
+#endif // USE_DLMALLOC
+
+ size_t nMaxPhysMemUsed_Delta;
+ nMaxPhysMemUsed_Delta = 0;
+#ifdef _PS3
+ {
+ // System heap (should not exist!)
+ static malloc_managed_size mms;
+ (g_pMemOverrideRawCrtFns->pfn_malloc_stats)( &mms );
+ if ( mms.current_system_size )
+ AddGenericMemoryStat( "sys_heap", (int)mms.current_system_size );
+
+ // Virtual Memory Manager
+ size_t nReserved = 0, nReservedMax = 0, nCommitted = 0, nCommittedMax = 0;
+ extern void VirtualMemoryManager_GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax );
+ VirtualMemoryManager_GetStats( nReserved, nReservedMax, nCommitted, nCommittedMax );
+ AddGenericMemoryStat( "VMM_reserved", (int)nReserved );
+ AddGenericMemoryStat( "VMM_reserved_max", (int)nReservedMax );
+ AddGenericMemoryStat( "VMM_committed", (int)nCommitted );
+ AddGenericMemoryStat( "VMM_committed_max", (int)nCommittedMax );
+
+ // Estimate memory committed by memory stacks (these account for all VMM allocations other than the SBH/MBH/LBH)
+ size_t nHeapTotal = 1024*1024*MBYTES_PRIMARY_SBH;
+#if defined( USE_DLMALLOC )
+ for ( int i = 0; i < ARRAYSIZE(g_AllocRegions); i++ )
+ {
+ nHeapTotal += mspace_footprint( g_AllocRegions[i] );
+ }
+#endif // USE_DLMALLOC
+ size_t nMemStackTotal = nCommitted - nHeapTotal;
+ AddGenericMemoryStat( "MemStacks", (int)nMemStackTotal );
+
+ // On PS3, we can more accurately determine 'phys_free_min', since we know nCommittedMax
+ // (otherwise nPhysFreeMin is only updated intermittently; when this function is called):
+ nMaxPhysMemUsed_Delta = nCommittedMax - nCommitted;
+ }
+#endif // _PS3
+
+#if defined( _GAMECONSOLE )
+ // Total/free/min-free physical pages
+ {
+#if defined( _X360 )
+ MEMORYSTATUS stat;
+ ::GlobalMemoryStatus( &stat );
+ size_t nPhysTotal = stat.dwTotalPhys, nPhysFree = stat.dwAvailPhys - ExtraDevkitMemory();
+#elif defined( _PS3 )
+ static sys_memory_info stat;
+ sys_memory_get_user_memory_size( &stat );
+ size_t nPhysTotal = stat.total_user_memory, nPhysFree = stat.available_user_memory - ExtraDevkitMemory();
+#endif // _X360/_PS3
+ static size_t nPhysFreeMin = nPhysTotal;
+ nPhysFreeMin = MIN( nPhysFreeMin, ( nPhysFree - nMaxPhysMemUsed_Delta ) );
+ AddGenericMemoryStat( "phys_total", (int)nPhysTotal );
+ AddGenericMemoryStat( "phys_free", (int)nPhysFree );
+ AddGenericMemoryStat( "phys_free_min", (int)nPhysFreeMin );
+ }
+#endif // _GAMECONSOLE
+
+ *ppMemoryStats = &g_MemStats[0];
+ return g_nMemStats;
+}
+
+void CStdMemAlloc::CompactHeap()
+{
+#if MEM_SBH_ENABLED
+ if ( !m_CompactMutex.TryLock() )
+ {
+ return;
+ }
+ if ( m_bInCompact )
+ {
+ m_CompactMutex.Unlock();
+ return;
+ }
+
+ m_bInCompact = true;
+ size_t nBytesRecovered;
+#ifndef MEMALLOC_NO_FALLBACK
+ nBytesRecovered = m_FallbackSBH.Compact( false );
+ if ( nBytesRecovered && IsGameConsole() )
+ {
+ Msg( "Compact freed %d bytes from virtual heap (up to 256k still committed)\n", nBytesRecovered );
+ }
+#endif // MEMALLOC_NO_FALLBACK
+ nBytesRecovered = m_PrimarySBH.Compact( false );
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ nBytesRecovered += m_SecondarySBH.Compact( false );
+#endif
+ if ( nBytesRecovered && IsGameConsole() )
+ {
+ Msg( "Compact released %d bytes from the SBH\n", nBytesRecovered );
+ }
+
+ nBytesRecovered = compact_internal();
+ if ( nBytesRecovered && IsGameConsole() )
+ {
+ Msg( "Compact released %d bytes from the mixed block heap\n", nBytesRecovered );
+ }
+
+ m_bInCompact = false;
+ m_CompactMutex.Unlock();
+#endif // MEM_SBH_ENABLED
+}
+
+void CStdMemAlloc::CompactIncremental()
+{
+#if MEM_SBH_ENABLED
+ if ( !m_CompactMutex.TryLock() )
+ {
+ return;
+ }
+ if ( m_bInCompact )
+ {
+ m_CompactMutex.Unlock();
+ return;
+ }
+
+ m_bInCompact = true;
+#ifndef MEMALLOC_NO_FALLBACK
+ m_FallbackSBH.Compact( true );
+#endif
+ m_PrimarySBH.Compact( true );
+#ifdef MEMALLOC_USE_SECONDARY_SBH
+ m_SecondarySBH.Compact( true );
+#endif
+ m_bInCompact = false;
+ m_CompactMutex.Unlock();
+#endif // MEM_SBH_ENABLED
+}
+
+MemAllocFailHandler_t CStdMemAlloc::SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler )
+{
+ MemAllocFailHandler_t pfnPrevious = m_pfnFailHandler;
+ m_pfnFailHandler = pfnMemAllocFailHandler;
+ return pfnPrevious;
+}
+
+size_t CStdMemAlloc::DefaultFailHandler( size_t nBytes )
+{
+ if ( IsX360() )
+ {
+#ifdef _X360
+ ExecuteOnce(
+ {
+ char buffer[256];
+ _snprintf( buffer, sizeof( buffer ), "***** Memory pool overflow, attempted allocation size: %u (not a critical error)\n", nBytes );
+ XBX_OutputDebugString( buffer );
+ }
+ );
+#endif // _X360
+ }
+ return 0;
+}
+
+void CStdMemAlloc::SetStatsExtraInfo( const char *pMapName, const char *pComment )
+{
+}
+
+void CStdMemAlloc::SetCRTAllocFailed( size_t nSize )
+{
+ m_sMemoryAllocFailed = nSize;
+
+ DebuggerBreakIfDebugging();
+#if defined( _PS3 ) && defined( _DEBUG )
+ DebuggerBreak();
+#endif // _PS3
+
+ char buffer[256];
+#ifdef COMPILER_GCC
+ _snprintf( buffer, sizeof( buffer ), "***** OUT OF MEMORY! attempted allocation size: %u ****\n", nSize );
+#else
+ _snprintf( buffer, sizeof( buffer ), "***** OUT OF MEMORY! attempted allocation size: %u ****\n", nSize );
+#endif // COMPILER_GCC
+
+#ifdef _X360
+ XBX_OutputDebugString( buffer );
+ if ( !Plat_IsInDebugSession() )
+ {
+ XBX_CrashDump( true );
+#if defined( _DEMO )
+ XLaunchNewImage( XLAUNCH_KEYWORD_DEFAULT_APP, 0 );
+#else
+ XLaunchNewImage( "default.xex", 0 );
+#endif // _DEMO
+ }
+#elif defined(_WIN32 )
+ OutputDebugString( buffer );
+ if ( !Plat_IsInDebugSession() )
+ {
+ WriteMiniDump();
+ abort();
+ }
+#else // _X360/_WIN32/other
+ printf( "%s\n", buffer );
+ if ( !Plat_IsInDebugSession() )
+ {
+ WriteMiniDump();
+#if defined( _PS3 )
+ DumpStats();
+#endif
+ Plat_ExitProcess( 0 );
+ }
+#endif // _X360/_WIN32/other
+
+}
+
+size_t CStdMemAlloc::MemoryAllocFailed()
+{
+ return m_sMemoryAllocFailed;
+}
+
+#endif // MEM_IMPL_TYPE_STD
+
+#endif // STEAM
diff --git a/external/vpc/tier0/memstd.h b/external/vpc/tier0/memstd.h
new file mode 100644
index 0000000..ea594c4
--- /dev/null
+++ b/external/vpc/tier0/memstd.h
@@ -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
diff --git a/external/vpc/tier0/memvalidate.cpp b/external/vpc/tier0/memvalidate.cpp
new file mode 100644
index 0000000..17c9514
--- /dev/null
+++ b/external/vpc/tier0/memvalidate.cpp
@@ -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
diff --git a/external/vpc/tier0/minidump.cpp b/external/vpc/tier0/minidump.cpp
new file mode 100644
index 0000000..7d2a85b
--- /dev/null
+++ b/external/vpc/tier0/minidump.cpp
@@ -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
diff --git a/external/vpc/tier0/pch_tier0.cpp b/external/vpc/tier0/pch_tier0.cpp
new file mode 100644
index 0000000..1cf6ecc
--- /dev/null
+++ b/external/vpc/tier0/pch_tier0.cpp
@@ -0,0 +1,11 @@
+//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+
+#include "pch_tier0.h"
+
+
diff --git a/external/vpc/tier0/pch_tier0.h b/external/vpc/tier0/pch_tier0.h
new file mode 100644
index 0000000..6156ad4
--- /dev/null
+++ b/external/vpc/tier0/pch_tier0.h
@@ -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"
diff --git a/external/vpc/tier0/platform.cpp b/external/vpc/tier0/platform.cpp
new file mode 100644
index 0000000..15cb1f6
--- /dev/null
+++ b/external/vpc/tier0/platform.cpp
@@ -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 \ No newline at end of file
diff --git a/external/vpc/tier0/platform_posix.cpp b/external/vpc/tier0/platform_posix.cpp
new file mode 100644
index 0000000..90a8c76
--- /dev/null
+++ b/external/vpc/tier0/platform_posix.cpp
@@ -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
diff --git a/external/vpc/tier0/pme.cpp b/external/vpc/tier0/pme.cpp
new file mode 100644
index 0000000..23a5442
--- /dev/null
+++ b/external/vpc/tier0/pme.cpp
@@ -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
+
diff --git a/external/vpc/tier0/pme_posix.cpp b/external/vpc/tier0/pme_posix.cpp
new file mode 100644
index 0000000..ff782ce
--- /dev/null
+++ b/external/vpc/tier0/pme_posix.cpp
@@ -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 )
+{
+}
diff --git a/external/vpc/tier0/pmelib.cpp b/external/vpc/tier0/pmelib.cpp
new file mode 100644
index 0000000..c4f1aad
--- /dev/null
+++ b/external/vpc/tier0/pmelib.cpp
@@ -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
+
diff --git a/external/vpc/tier0/resource.h b/external/vpc/tier0/resource.h
new file mode 100644
index 0000000..6a4ef65
--- /dev/null
+++ b/external/vpc/tier0/resource.h
@@ -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
diff --git a/external/vpc/tier0/stacktools.cpp b/external/vpc/tier0/stacktools.cpp
new file mode 100644
index 0000000..7a93d61
--- /dev/null
+++ b/external/vpc/tier0/stacktools.cpp
@@ -0,0 +1,1682 @@
+//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
+//
+// Purpose:
+//
+// $Workfile: $
+// $NoKeywords: $
+//===========================================================================//
+
+#include "pch_tier0.h"
+#include "tier0/stacktools.h"
+#include "tier0/threadtools.h"
+#include "tier0/icommandline.h"
+
+#include "tier0/valve_off.h"
+
+#if defined( PLATFORM_WINDOWS_PC )
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <dbghelp.h>
+#endif
+
+#if defined( PLATFORM_X360 )
+#include <xbdm.h>
+#include "xbox/xbox_console.h"
+#include "xbox/xbox_vxconsole.h"
+#include <map>
+#include <set>
+#endif
+
+#include "tier0/valve_on.h"
+
+#include "tier0/memdbgon.h"
+
+
+#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION ) //disable the whole toolset
+
+int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ return 0;
+}
+
+int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ return 0;
+}
+
+//where we'll find our PDB's for win32. Translation will not work until this has been called once (even if with NULL)
+void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
+{
+}
+
+void StackToolsNotify_LoadedLibrary( const char *szLibName )
+{
+}
+
+int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style )
+{
+ if( iOutBufferSize > 0 )
+ *szOutput = '\0';
+
+ return 0;
+}
+
+void PreloadStackInformation( const void **pAddresses, int iAddressCount )
+{
+}
+
+bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+{
+ if( iMaxFileNameLength > 0 )
+ *pFileNameOut = '\0';
+
+ return false;
+}
+
+bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+{
+ if( iMaxSymbolNameLength > 0 )
+ *pSymbolNameOut = '\0';
+
+ return false;
+}
+
+bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+{
+ if( iMaxModuleNameLength > 0 )
+ *pModuleNameOut = '\0';
+
+ return false;
+}
+
+#else //#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+
+//===============================================================================================================
+// Shared Windows/X360 code
+//===============================================================================================================
+
+CTHREADLOCALPTR( CStackTop_Base ) g_StackTop;
+
+class CStackTop_FriendFuncs : public CStackTop_Base
+{
+public:
+ friend int AppendParentStackTrace( void **pReturnAddressesOut, int iArrayCount, int iAlreadyFilled );
+ friend int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount );
+};
+
+
+inline int AppendParentStackTrace( void **pReturnAddressesOut, int iArrayCount, int iAlreadyFilled )
+{
+ CStackTop_FriendFuncs *pTop = (CStackTop_FriendFuncs *)(CStackTop_Base *)g_StackTop;
+ if( pTop != NULL )
+ {
+ if( pTop->m_pReplaceAddress != NULL )
+ {
+ for( int i = iAlreadyFilled; --i >= 0; )
+ {
+ if( pReturnAddressesOut[i] == pTop->m_pReplaceAddress )
+ {
+ iAlreadyFilled = i;
+ break;
+ }
+ }
+ }
+
+ if( pTop->m_iParentStackTraceLength != 0 )
+ {
+ int iCopy = MIN( iArrayCount - iAlreadyFilled, pTop->m_iParentStackTraceLength );
+ memcpy( pReturnAddressesOut + iAlreadyFilled, pTop->m_pParentStackTrace, iCopy * sizeof( void * ) );
+ iAlreadyFilled += iCopy;
+ }
+ }
+
+ return iAlreadyFilled;
+}
+
+inline bool ValidStackAddress( void *pAddress, const void *pNoLessThan, const void *pNoGreaterThan )
+{
+ if( (uint64)pAddress & 3 )
+ return false;
+ if( pAddress < pNoLessThan ) //frame pointer traversal should always increase the pointer
+ return false;
+ if( pAddress > pNoGreaterThan ) //never traverse outside the stack (Oh 0xCCCCCCCC, how I hate you)
+ return false;
+
+#if defined( WIN32 ) && !defined( _X360 ) && 1
+ if( IsBadReadPtr( pAddress, (sizeof( void * ) * 2) ) ) //safety net, but also throws an exception (handled internally) to stop bad access
+ return false;
+#endif
+
+ return true;
+}
+
+#pragma auto_inline( off )
+int GetCallStack_Fast( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ //Only tested in windows. This function won't work with frame pointer omission enabled. "vpc /nofpo" all projects
+#if (defined( TIER0_FPO_DISABLED ) || defined( _DEBUG )) &&\
+ (defined( WIN32 ) && !defined( _X360 ))
+ void *pStackCrawlEBP;
+ __asm
+ {
+ mov [pStackCrawlEBP], ebp;
+ }
+
+ /*
+ With frame pointer omission disabled, this should be the pattern all the way up the stack
+ [ebp+00] Old ebp value
+ [ebp+04] Return address
+ */
+
+ void *pNoLessThan = pStackCrawlEBP; //impossible for a valid stack to traverse before this address
+ int i;
+
+ CStackTop_FriendFuncs *pTop = (CStackTop_FriendFuncs *)(CStackTop_Base *)g_StackTop;
+ if( pTop != NULL ) //we can do fewer error checks if we have a valid reference point for the top of the stack
+ {
+ void *pNoGreaterThan = pTop->m_pStackBase;
+
+ //skips
+ for( i = 0; i != iSkipCount; ++i )
+ {
+ if( (pStackCrawlEBP < pNoLessThan) || (pStackCrawlEBP > pNoGreaterThan) )
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, 0 );
+
+ pNoLessThan = pStackCrawlEBP;
+ pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
+ }
+
+ //store
+ for( i = 0; i != iArrayCount; ++i )
+ {
+ if( (pStackCrawlEBP < pNoLessThan) || (pStackCrawlEBP > pNoGreaterThan) )
+ break;
+
+ pReturnAddressesOut[i] = *((void **)pStackCrawlEBP + 1);
+
+ pNoLessThan = pStackCrawlEBP;
+ pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
+ }
+
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, i );
+ }
+ else
+ {
+ void *pNoGreaterThan = ((unsigned char *)pNoLessThan) + (1024 * 1024); //standard stack is 1MB. TODO: Get actual stack end address if available since this check isn't foolproof
+
+ //skips
+ for( i = 0; i != iSkipCount; ++i )
+ {
+ if( !ValidStackAddress( pStackCrawlEBP, pNoLessThan, pNoGreaterThan ) )
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, 0 );
+
+ pNoLessThan = pStackCrawlEBP;
+ pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
+ }
+
+ //store
+ for( i = 0; i != iArrayCount; ++i )
+ {
+ if( !ValidStackAddress( pStackCrawlEBP, pNoLessThan, pNoGreaterThan ) )
+ break;
+
+ pReturnAddressesOut[i] = *((void **)pStackCrawlEBP + 1);
+
+ pNoLessThan = pStackCrawlEBP;
+ pStackCrawlEBP = *(void **)pStackCrawlEBP; //should be pointing at old ebp value
+ }
+
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, i );
+ }
+#endif
+
+ return 0;
+}
+#pragma auto_inline( on )
+
+
+
+
+
+
+#if defined( WIN32 ) && !defined( _X360 )
+//===============================================================================================================
+// Windows version of the toolset
+//===============================================================================================================
+
+
+#if defined( TIER0_FPO_DISABLED )
+//# define USE_CAPTURESTACKBACKTRACE //faster than StackWalk64, but only works on XP or newer and only with Frame Pointer Omission optimization disabled(/Oy-) for every function it traces through
+#endif
+
+
+#if defined(_M_IX86) || defined(_M_X64)
+# define USE_STACKWALK64
+# if defined(_M_IX86)
+# define STACKWALK64_MACHINETYPE IMAGE_FILE_MACHINE_I386
+# else
+# define STACKWALK64_MACHINETYPE IMAGE_FILE_MACHINE_AMD64
+# endif
+#endif
+
+typedef DWORD (WINAPI *PFN_SymGetOptions)( VOID );
+typedef DWORD (WINAPI *PFN_SymSetOptions)( IN DWORD SymOptions );
+typedef BOOL (WINAPI *PFN_SymSetSearchPath)( IN HANDLE hProcess, IN PSTR SearchPath );
+typedef BOOL (WINAPI *PFN_SymInitialize)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
+typedef BOOL (WINAPI *PFN_SymCleanup)( IN HANDLE hProcess );
+typedef BOOL (WINAPI *PFN_SymEnumerateModules64)( IN HANDLE hProcess, IN PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback, IN PVOID UserContext );
+typedef BOOL (WINAPI *PFN_EnumerateLoadedModules64)( IN HANDLE hProcess, IN PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, IN PVOID UserContext );
+typedef DWORD64 (WINAPI *PFN_SymLoadModule64)( IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
+typedef BOOL (WINAPI *PFN_SymUnloadModule64)( IN HANDLE hProcess, IN DWORD64 BaseOfDll );
+typedef BOOL (WINAPI *PFN_SymFromAddr)( IN HANDLE hProcess, IN DWORD64 Address, OUT PDWORD64 Displacement, IN OUT PSYMBOL_INFO Symbol );
+typedef BOOL (WINAPI *PFN_SymGetLineFromAddr64)( IN HANDLE hProcess, IN DWORD64 qwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line64 );
+typedef BOOL (WINAPI *PFN_SymGetModuleInfo64)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo );
+typedef BOOL (WINAPI *PFN_StackWalk64)( DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
+typedef USHORT (WINAPI *PFN_CaptureStackBackTrace)( IN ULONG FramesToSkip, IN ULONG FramesToCapture, OUT PVOID *BackTrace, OUT OPTIONAL PULONG BackTraceHash );
+
+
+DWORD WINAPI SymGetOptions_DummyFn( VOID )
+{
+ return 0;
+}
+
+DWORD WINAPI SymSetOptions_DummyFn( IN DWORD SymOptions )
+{
+ return 0;
+}
+
+BOOL WINAPI SymSetSearchPath_DummyFn( IN HANDLE hProcess, IN PSTR SearchPath )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymInitialize_DummyFn( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymCleanup_DummyFn( IN HANDLE hProcess )
+{
+ return TRUE;
+}
+
+BOOL WINAPI SymEnumerateModules64_DummyFn( IN HANDLE hProcess, IN PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback, IN PVOID UserContext )
+{
+ return FALSE;
+}
+
+BOOL WINAPI EnumerateLoadedModules64_DummyFn( IN HANDLE hProcess, IN PENUMLOADED_MODULES_CALLBACK64 EnumLoadedModulesCallback, IN PVOID UserContext )
+{
+ return FALSE;
+}
+
+DWORD64 WINAPI SymLoadModule64_DummyFn( IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll )
+{
+ return 0;
+}
+
+BOOL WINAPI SymUnloadModule64_DummyFn( IN HANDLE hProcess, IN DWORD64 BaseOfDll )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymFromAddr_DummyFn( IN HANDLE hProcess, IN DWORD64 Address, OUT PDWORD64 Displacement, IN OUT PSYMBOL_INFO Symbol )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymGetLineFromAddr64_DummyFn( IN HANDLE hProcess, IN DWORD64 qwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line64 )
+{
+ return FALSE;
+}
+
+BOOL WINAPI SymGetModuleInfo64_DummyFn( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PIMAGEHLP_MODULE64 ModuleInfo )
+{
+ return FALSE;
+}
+
+
+
+BOOL WINAPI StackWalk64_DummyFn( DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress )
+{
+ return FALSE;
+}
+
+USHORT WINAPI CaptureStackBackTrace_DummyFn( IN ULONG FramesToSkip, IN ULONG FramesToCapture, OUT PVOID *BackTrace, OUT OPTIONAL PULONG BackTraceHash )
+{
+ return 0;
+}
+
+class CHelperFunctionsLoader
+{
+public:
+ CHelperFunctionsLoader( void )
+ {
+ m_bIsInitialized = false;
+ m_bShouldReloadSymbols = false;
+ m_hDbgHelpDll = NULL;
+ m_szPDBSearchPath = NULL;
+ m_pSymInitialize = SymInitialize_DummyFn;
+ m_pSymCleanup = SymCleanup_DummyFn;
+ m_pSymSetOptions = SymSetOptions_DummyFn;
+ m_pSymGetOptions = SymGetOptions_DummyFn;
+ m_pSymSetSearchPath = SymSetSearchPath_DummyFn;
+ m_pSymEnumerateModules64 = SymEnumerateModules64_DummyFn;
+ m_pEnumerateLoadedModules64 = EnumerateLoadedModules64_DummyFn;
+ m_pSymLoadModule64 = SymLoadModule64_DummyFn;
+ m_pSymUnloadModule64 = SymUnloadModule64_DummyFn;
+ m_pSymFromAddr = SymFromAddr_DummyFn;
+ m_pSymGetLineFromAddr64 = SymGetLineFromAddr64_DummyFn;
+ m_pSymGetModuleInfo64 = SymGetModuleInfo64_DummyFn;
+
+#if defined( USE_STACKWALK64 )
+ m_pStackWalk64 = StackWalk64_DummyFn;
+#endif
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ m_pCaptureStackBackTrace = CaptureStackBackTrace_DummyFn;
+ m_hNTDllDll = NULL;
+#endif
+ }
+
+ ~CHelperFunctionsLoader( void )
+ {
+ m_pSymCleanup( m_hProcess );
+
+ if( m_hDbgHelpDll != NULL )
+ ::FreeLibrary( m_hDbgHelpDll );
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ if( m_hNTDllDll != NULL )
+ ::FreeLibrary( m_hNTDllDll );
+#endif
+
+ if( m_szPDBSearchPath != NULL )
+ delete []m_szPDBSearchPath;
+ }
+
+ static BOOL CALLBACK UnloadSymbolsCallback( PSTR ModuleName, DWORD64 BaseOfDll, PVOID UserContext )
+ {
+ const CHelperFunctionsLoader *pThis = ((CHelperFunctionsLoader *)UserContext);
+ pThis->m_pSymUnloadModule64( pThis->m_hProcess, BaseOfDll );
+ return TRUE;
+ }
+
+#if _MSC_VER >= 1600
+ static BOOL CALLBACK LoadSymbolsCallback( PCSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext )
+#else
+ static BOOL CALLBACK LoadSymbolsCallback( PSTR ModuleName, DWORD64 ModuleBase, ULONG ModuleSize, PVOID UserContext )
+#endif
+ {
+ const CHelperFunctionsLoader *pThis = ((CHelperFunctionsLoader *)UserContext);
+ //SymLoadModule64( IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll );
+ pThis->m_pSymLoadModule64( pThis->m_hProcess, NULL, (PSTR)ModuleName, (PSTR)ModuleName, ModuleBase, ModuleSize );
+ return TRUE;
+ }
+
+ void TryLoadingNewSymbols( void )
+ {
+ AUTO_LOCK_FM( m_Mutex );
+
+ if( m_bIsInitialized )
+ {
+ //m_pSymEnumerateModules64( m_hProcess, UnloadSymbolsCallback, this ); //unloaded modules we've already loaded
+ m_pEnumerateLoadedModules64( m_hProcess, LoadSymbolsCallback, this ); //load everything
+ m_bShouldReloadSymbols = false;
+ }
+ }
+
+ void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
+ {
+ AUTO_LOCK_FM( m_Mutex );
+
+ if( m_szPDBSearchPath != NULL )
+ delete []m_szPDBSearchPath;
+
+ if( szSemicolonSeparatedList == NULL )
+ {
+ m_szPDBSearchPath = NULL;
+ return;
+ }
+
+ int iLength = (int)strlen( szSemicolonSeparatedList ) + 1;
+ char *pNewPath = new char [iLength];
+ memcpy( pNewPath, szSemicolonSeparatedList, iLength );
+ m_szPDBSearchPath = pNewPath;
+
+ //re-init search paths. Or if we haven't yet loaded dbghelp.dll, this will go to the dummy function and do nothing
+ m_pSymSetSearchPath( m_hProcess, m_szPDBSearchPath );
+ //TryLoadingNewSymbols();
+ m_bShouldReloadSymbols = true;
+ }
+
+ bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+ {
+ if( pAddress == NULL )
+ return false;
+
+ AUTO_LOCK_FM( m_Mutex );
+
+ unsigned char genericbuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME*sizeof(TCHAR)];
+
+ ((PSYMBOL_INFO)genericbuffer)->SizeOfStruct = sizeof(SYMBOL_INFO);
+ ((PSYMBOL_INFO)genericbuffer)->MaxNameLen = MAX_SYM_NAME;
+
+ DWORD64 dwDisplacement;
+ if( m_pSymFromAddr( m_hProcess, (DWORD64)pAddress, &dwDisplacement, (PSYMBOL_INFO)genericbuffer) )
+ {
+ strncpy( pSymbolNameOut, ((PSYMBOL_INFO)genericbuffer)->Name, iMaxSymbolNameLength );
+ if( pDisplacementOut != NULL )
+ *pDisplacementOut = dwDisplacement;
+ return true;
+ }
+
+ return false;
+ }
+
+ bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+ {
+ if( pAddress == NULL )
+ return false;
+
+ AUTO_LOCK_FM( m_Mutex );
+
+ tchar szBuffer[1024];
+ szBuffer[0] = _T('\0');
+
+ IMAGEHLP_LINE64 imageHelpLine64;
+ imageHelpLine64.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ imageHelpLine64.FileName = szBuffer;
+
+ DWORD dwDisplacement;
+ if( m_pSymGetLineFromAddr64( m_hProcess, (DWORD64)pAddress, &dwDisplacement, &imageHelpLine64 ) )
+ {
+ strncpy( pFileNameOut, imageHelpLine64.FileName, iMaxFileNameLength );
+ iLineNumberOut = imageHelpLine64.LineNumber;
+
+ if( pDisplacementOut != NULL )
+ *pDisplacementOut = dwDisplacement;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+ {
+ AUTO_LOCK_FM( m_Mutex );
+ IMAGEHLP_MODULE64 moduleInfo;
+
+ moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
+
+ if ( m_pSymGetModuleInfo64( m_hProcess, (DWORD64)pAddress, &moduleInfo ) )
+ {
+ strncpy( pModuleNameOut, moduleInfo.ModuleName, iMaxModuleNameLength );
+ return true;
+ }
+
+ return false;
+ }
+
+ //only returns false if we ran out of buffer space.
+ bool TranslatePointer( const void * const pAddress, tchar *pTranslationOut, int iTranslationBufferLength, TranslateStackInfo_StyleFlags_t style )
+ {
+ //AUTO_LOCK( m_Mutex );
+
+ if( pTranslationOut == NULL )
+ return false;
+
+ if( iTranslationBufferLength <= 0 )
+ return false;
+
+ //sample desired output
+ // valid translation - "tier0.dll!CHelperFunctionsLoader::TranslatePointer - u:\Dev\L4D\src\tier0\stacktools.cpp(162) + 4 bytes"
+ // fallback translation - "tier0.dll!0x01234567"
+
+ tchar *pWrite = pTranslationOut;
+ *pWrite = '\0';
+ int iLength;
+
+ if( style & TSISTYLEFLAG_MODULENAME )
+ {
+ if( !this->GetModuleNameFromAddress( pAddress, pWrite, iTranslationBufferLength ) )
+ strncpy( pWrite, "unknown_module", iTranslationBufferLength );
+
+ iLength = (int)strlen( pWrite );
+ pWrite += iLength;
+ iTranslationBufferLength -= iLength;
+
+ if( iTranslationBufferLength < 2 )
+ return false; //need more buffer
+
+ if( style & TSISTYLEFLAG_SYMBOLNAME )
+ {
+ *pWrite = '!';
+ ++pWrite;
+ --iTranslationBufferLength;
+ *pWrite = '\0';
+ }
+ }
+
+ //use symbol name to test if the rest is going to work. So grab it whether they want it or not
+ if( !this->GetSymbolNameFromAddress( pAddress, pWrite, iTranslationBufferLength, NULL ) )
+ {
+ int nBytesWritten = _snprintf( pWrite, iTranslationBufferLength, "0x%p", pAddress );
+ if ( nBytesWritten < 0 )
+ {
+ *pWrite = '\0'; // if we can't write all of the line/lineandoffset, don't write any at all
+ return false;
+ }
+ return true;
+ }
+ else if( style & TSISTYLEFLAG_SYMBOLNAME )
+ {
+ iLength = (int)strlen( pWrite );
+ pWrite += iLength;
+ iTranslationBufferLength -= iLength;
+ }
+ else
+ {
+ *pWrite = '\0'; //symbol name lookup worked, but unwanted, discard
+ }
+
+ if( style & (TSISTYLEFLAG_FULLPATH | TSISTYLEFLAG_SHORTPATH | TSISTYLEFLAG_LINE | TSISTYLEFLAG_LINEANDOFFSET) )
+ {
+ if( pWrite != pTranslationOut ) //if we've written anything yet, separate the printed data from the file name and line
+ {
+ if( iTranslationBufferLength < 6 )
+ return false; //need more buffer
+
+ pWrite[0] = ' '; //append " - "
+ pWrite[1] = '-';
+ pWrite[2] = ' ';
+ pWrite[3] = '\0';
+ pWrite += 3;
+ iTranslationBufferLength -= 3;
+ }
+
+ uint32 iLine;
+ uint32 iDisplacement;
+ char szFileName[MAX_PATH];
+ if( this->GetFileAndLineFromAddress( pAddress, szFileName, MAX_PATH, iLine, &iDisplacement ) )
+ {
+ if( style & TSISTYLEFLAG_FULLPATH )
+ {
+ iLength = (int)strlen( szFileName );
+ if ( iTranslationBufferLength < iLength + 1 )
+ return false;
+
+ memcpy( pWrite, szFileName, iLength + 1 );
+ pWrite += iLength;
+ iTranslationBufferLength -= iLength;
+ }
+ else if( style & TSISTYLEFLAG_SHORTPATH )
+ {
+ //shorten the path and copy
+ iLength = (int)strlen( szFileName );
+ char *pShortened = szFileName + iLength;
+ int iSlashesAllowed = 3;
+ while( pShortened > szFileName )
+ {
+ if( (*pShortened == '\\') || (*pShortened == '/') )
+ {
+ --iSlashesAllowed;
+ if( iSlashesAllowed == 0 )
+ break;
+ }
+
+ --pShortened;
+ }
+
+ iLength = (int)strlen( pShortened );
+ if( iTranslationBufferLength < iLength + 1 )
+ {
+ //Remove the " - " that we can't append to
+ pWrite -= 3;
+ iTranslationBufferLength += 3;
+ *pWrite = '\0';
+ return false;
+ }
+
+ memcpy( pWrite, szFileName, iLength + 1 );
+ pWrite += iLength;
+ iTranslationBufferLength -= iLength;
+ }
+
+ if( style & (TSISTYLEFLAG_LINE | TSISTYLEFLAG_LINEANDOFFSET) )
+ {
+ int nBytesWritten = _snprintf( pWrite, iTranslationBufferLength, ((style & TSISTYLEFLAG_LINEANDOFFSET) && (iDisplacement != 0)) ? "(%d) + %d bytes" : "(%d)", iLine, iDisplacement );
+ if ( nBytesWritten < 0 )
+ {
+ *pWrite = '\0'; // if we can't write all of the line/lineandoffset, don't write any at all
+ return false;
+ }
+
+ pWrite += nBytesWritten;
+ iTranslationBufferLength -= nBytesWritten;
+ }
+ }
+ else
+ {
+ //Remove the " - " that we didn't append to
+ pWrite -= 3;
+ iTranslationBufferLength += 3;
+ *pWrite = '\0';
+ }
+ }
+
+ return true;
+ }
+
+
+ //about to actually use the functions, load if necessary
+ void EnsureReady( void )
+ {
+ if( m_bIsInitialized )
+ {
+ if( m_bShouldReloadSymbols )
+ TryLoadingNewSymbols();
+
+ return;
+ }
+
+ AUTO_LOCK_FM( m_Mutex );
+
+ //Only enabled for P4 and Steam Beta builds
+ if( (CommandLine()->FindParm( "-steam" ) != 0) && //is steam
+ (CommandLine()->FindParm( "-internalbuild" ) == 0) ) //is not steam beta
+ {
+ //disable the toolset by falsifying initialized state
+ m_bIsInitialized = true;
+ return;
+ }
+
+ m_hProcess = GetCurrentProcess();
+ if( m_hProcess == NULL )
+ return;
+
+ m_bIsInitialized = true;
+
+ // 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
+ m_hDbgHelpDll = ::LoadLibrary( "DbgHelp.dll" );
+ if ( !m_hDbgHelpDll )
+ {
+ //it's possible it's just way too early to initialize (as shown with attempts at using these tools in the memory allocator)
+ if( m_szPDBSearchPath == NULL ) //not a rock solid check, but pretty good compromise between endless failing initialization and general failure due to trying too early
+ m_bIsInitialized = false;
+
+ return;
+ }
+
+ m_pSymInitialize = (PFN_SymInitialize) ::GetProcAddress( m_hDbgHelpDll, "SymInitialize" );
+ if( m_pSymInitialize == NULL )
+ {
+ //very bad
+ ::FreeLibrary( m_hDbgHelpDll );
+ m_hDbgHelpDll = NULL;
+ m_pSymInitialize = SymInitialize_DummyFn;
+ return;
+ }
+
+ m_pSymCleanup = (PFN_SymCleanup) ::GetProcAddress( m_hDbgHelpDll, "SymCleanup" );
+ if( m_pSymCleanup == NULL )
+ m_pSymCleanup = SymCleanup_DummyFn;
+
+ m_pSymGetOptions = (PFN_SymGetOptions) ::GetProcAddress( m_hDbgHelpDll, "SymGetOptions" );
+ if( m_pSymGetOptions == NULL )
+ m_pSymGetOptions = SymGetOptions_DummyFn;
+
+ m_pSymSetOptions = (PFN_SymSetOptions) ::GetProcAddress( m_hDbgHelpDll, "SymSetOptions" );
+ if( m_pSymSetOptions == NULL )
+ m_pSymSetOptions = SymSetOptions_DummyFn;
+
+ m_pSymSetSearchPath = (PFN_SymSetSearchPath) ::GetProcAddress( m_hDbgHelpDll, "SymSetSearchPath" );
+ if( m_pSymSetSearchPath == NULL )
+ m_pSymSetSearchPath = SymSetSearchPath_DummyFn;
+
+ m_pSymEnumerateModules64 = (PFN_SymEnumerateModules64) ::GetProcAddress( m_hDbgHelpDll, "SymEnumerateModules64" );
+ if( m_pSymEnumerateModules64 == NULL )
+ m_pSymEnumerateModules64 = SymEnumerateModules64_DummyFn;
+
+ m_pEnumerateLoadedModules64 = (PFN_EnumerateLoadedModules64) ::GetProcAddress( m_hDbgHelpDll, "EnumerateLoadedModules64" );
+ if( m_pEnumerateLoadedModules64 == NULL )
+ m_pEnumerateLoadedModules64 = EnumerateLoadedModules64_DummyFn;
+
+ m_pSymLoadModule64 = (PFN_SymLoadModule64) ::GetProcAddress( m_hDbgHelpDll, "SymLoadModule64" );
+ if( m_pSymLoadModule64 == NULL )
+ m_pSymLoadModule64 = SymLoadModule64_DummyFn;
+
+ m_pSymUnloadModule64 = (PFN_SymUnloadModule64) ::GetProcAddress( m_hDbgHelpDll, "SymUnloadModule64" );
+ if( m_pSymUnloadModule64 == NULL )
+ m_pSymUnloadModule64 = SymUnloadModule64_DummyFn;
+
+ m_pSymFromAddr = (PFN_SymFromAddr) ::GetProcAddress( m_hDbgHelpDll, "SymFromAddr" );
+ if( m_pSymFromAddr == NULL )
+ m_pSymFromAddr = SymFromAddr_DummyFn;
+
+ m_pSymGetLineFromAddr64 = (PFN_SymGetLineFromAddr64) ::GetProcAddress( m_hDbgHelpDll, "SymGetLineFromAddr64" );
+ if( m_pSymGetLineFromAddr64 == NULL )
+ m_pSymGetLineFromAddr64 = SymGetLineFromAddr64_DummyFn;
+
+ m_pSymGetModuleInfo64 = (PFN_SymGetModuleInfo64) ::GetProcAddress( m_hDbgHelpDll, "SymGetModuleInfo64" );
+ if( m_pSymGetModuleInfo64 == NULL )
+ m_pSymGetModuleInfo64 = SymGetModuleInfo64_DummyFn;
+
+#if defined( USE_STACKWALK64 )
+ m_pStackWalk64 = (PFN_StackWalk64) ::GetProcAddress( m_hDbgHelpDll, "StackWalk64" );
+ if( m_pStackWalk64 == NULL )
+ m_pStackWalk64 = StackWalk64_DummyFn;
+#endif
+
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ m_hNTDllDll = ::LoadLibrary( "ntdll.dll" );
+
+ m_pCaptureStackBackTrace = (PFN_CaptureStackBackTrace) ::GetProcAddress( m_hNTDllDll, "RtlCaptureStackBackTrace" );
+ if( m_pCaptureStackBackTrace == NULL )
+ m_pCaptureStackBackTrace = CaptureStackBackTrace_DummyFn;
+#endif
+
+
+ m_pSymSetOptions( m_pSymGetOptions() |
+ SYMOPT_DEFERRED_LOADS | //load on demand
+ SYMOPT_EXACT_SYMBOLS | //don't load the wrong file
+ SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS | //don't prompt ever
+ SYMOPT_LOAD_LINES ); //load line info
+
+ m_pSymInitialize( m_hProcess, m_szPDBSearchPath, FALSE );
+ TryLoadingNewSymbols();
+ }
+
+ bool m_bIsInitialized;
+ bool m_bShouldReloadSymbols;
+ HANDLE m_hProcess;
+ HMODULE m_hDbgHelpDll;
+ char *m_szPDBSearchPath;
+ CThreadFastMutex m_Mutex; //DbgHelp functions are all single threaded.
+
+ PFN_SymInitialize m_pSymInitialize;
+ PFN_SymCleanup m_pSymCleanup;
+ PFN_SymGetOptions m_pSymGetOptions;
+ PFN_SymSetOptions m_pSymSetOptions;
+ PFN_SymSetSearchPath m_pSymSetSearchPath;
+ PFN_SymEnumerateModules64 m_pSymEnumerateModules64;
+ PFN_EnumerateLoadedModules64 m_pEnumerateLoadedModules64;
+ PFN_SymLoadModule64 m_pSymLoadModule64;
+ PFN_SymUnloadModule64 m_pSymUnloadModule64;
+
+ PFN_SymFromAddr m_pSymFromAddr;
+ PFN_SymGetLineFromAddr64 m_pSymGetLineFromAddr64;
+ PFN_SymGetModuleInfo64 m_pSymGetModuleInfo64;
+
+#if defined( USE_STACKWALK64 )
+ PFN_StackWalk64 m_pStackWalk64;
+#endif
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ HMODULE m_hNTDllDll;
+ PFN_CaptureStackBackTrace m_pCaptureStackBackTrace;
+#endif
+};
+static CHelperFunctionsLoader s_HelperFunctions;
+
+
+
+#if defined( USE_STACKWALK64 ) //most reliable method thanks to boatloads of windows helper functions. Also the slowest.
+int CrawlStack_StackWalk64( CONTEXT *pExceptionContext, void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ s_HelperFunctions.EnsureReady();
+
+ AUTO_LOCK_FM( s_HelperFunctions.m_Mutex );
+
+ CONTEXT currentContext;
+ memcpy( &currentContext, pExceptionContext, sizeof( CONTEXT ) );
+
+ STACKFRAME64 sfFrame = { 0 }; //memset(&sfFrame, 0x0, sizeof(sfFrame));
+ sfFrame.AddrPC.Mode = sfFrame.AddrFrame.Mode = AddrModeFlat;
+ sfFrame.AddrPC.Offset = currentContext.Eip;
+ sfFrame.AddrFrame.Offset = currentContext.Ebp;
+
+ HANDLE hThread = GetCurrentThread();
+
+ int i;
+ for( i = 0; i != iSkipCount; ++i ) //skip entries that the requesting function thinks are uninformative
+ {
+ if(!s_HelperFunctions.m_pStackWalk64( STACKWALK64_MACHINETYPE, s_HelperFunctions.m_hProcess, hThread, &sfFrame, &currentContext, NULL, NULL, NULL, NULL ) ||
+ (sfFrame.AddrFrame.Offset == 0) )
+ {
+ return 0;
+ }
+ }
+
+ for( i = 0; i != iArrayCount; ++i )
+ {
+ if(!s_HelperFunctions.m_pStackWalk64( STACKWALK64_MACHINETYPE, s_HelperFunctions.m_hProcess, hThread, &sfFrame, &currentContext, NULL, NULL, NULL, NULL ) ||
+ (sfFrame.AddrFrame.Offset == 0) )
+ {
+ break;
+ }
+ pReturnAddressesOut[i] = (void *)sfFrame.AddrPC.Offset;
+ }
+
+ return i;
+}
+
+void GetCallStackReturnAddresses_Exception( void **CallStackReturnAddresses, int *pRetCount, int iSkipCount, _EXCEPTION_POINTERS * pExceptionInfo )
+{
+ int iCount = CrawlStack_StackWalk64( pExceptionInfo->ContextRecord, CallStackReturnAddresses, *pRetCount, iSkipCount + 1 ); //skipping RaiseException()
+ *pRetCount = iCount;
+}
+#endif //#if defined( USE_STACKWALK64 )
+
+
+
+int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ s_HelperFunctions.EnsureReady();
+
+ ++iSkipCount; //skip this function
+
+#if defined( USE_CAPTURESTACKBACKTRACE )
+ if( s_HelperFunctions.m_pCaptureStackBackTrace != CaptureStackBackTrace_DummyFn )
+ {
+ //docs state a total limit of 63 back traces between skipped and stored
+ int iRetVal = s_HelperFunctions.m_pCaptureStackBackTrace( iSkipCount, MIN( iArrayCount, 63 - iSkipCount ), pReturnAddressesOut, NULL );
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, iRetVal );
+ }
+#endif
+#if defined( USE_STACKWALK64 )
+ if( s_HelperFunctions.m_pStackWalk64 != StackWalk64_DummyFn )
+ {
+ int iInOutArrayCount = iArrayCount; //array count becomes both input and output with exception handler version
+ __try
+ {
+ ::RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, NULL );
+ }
+ __except ( GetCallStackReturnAddresses_Exception( pReturnAddressesOut, &iInOutArrayCount, iSkipCount, GetExceptionInformation() ), EXCEPTION_EXECUTE_HANDLER )
+ {
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, iInOutArrayCount );
+ }
+ }
+#endif
+
+ return GetCallStack_Fast( pReturnAddressesOut, iArrayCount, iSkipCount );
+}
+
+void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
+{
+ s_HelperFunctions.SetStackTranslationSymbolSearchPath( szSemicolonSeparatedList );
+}
+
+void StackToolsNotify_LoadedLibrary( const char *szLibName )
+{
+ s_HelperFunctions.m_bShouldReloadSymbols = true;
+}
+
+int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style )
+{
+ s_HelperFunctions.EnsureReady();
+ tchar *szStartOutput = szOutput;
+
+ if( szEntrySeparator == NULL )
+ szEntrySeparator = _T("");
+
+ int iSeparatorSize = (int)strlen( szEntrySeparator );
+
+ for( int i = 0; i < iCallStackCount; ++i )
+ {
+ if( !s_HelperFunctions.TranslatePointer( pCallStack[i], szOutput, iOutBufferSize, style ) )
+ {
+ return i;
+ }
+
+ int iLength = (int)strlen( szOutput );
+ szOutput += iLength;
+ iOutBufferSize -= iLength;
+
+ if( iOutBufferSize > iSeparatorSize )
+ {
+ memcpy( szOutput, szEntrySeparator, iSeparatorSize * sizeof( tchar ) );
+ szOutput += iSeparatorSize;
+ iOutBufferSize -= iSeparatorSize;
+ }
+ *szOutput = '\0';
+ }
+
+ szOutput -= iSeparatorSize;
+ if( szOutput >= szStartOutput )
+ *szOutput = '\0';
+
+ return iCallStackCount;
+}
+
+void PreloadStackInformation( void * const *pAddresses, int iAddressCount )
+{
+ //nop on anything but 360
+}
+
+bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+{
+ s_HelperFunctions.EnsureReady();
+ return s_HelperFunctions.GetFileAndLineFromAddress( pAddress, pFileNameOut, iMaxFileNameLength, iLineNumberOut, pDisplacementOut );
+}
+
+bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+{
+ s_HelperFunctions.EnsureReady();
+ return s_HelperFunctions.GetSymbolNameFromAddress( pAddress, pSymbolNameOut, iMaxSymbolNameLength, pDisplacementOut );
+}
+
+bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+{
+ s_HelperFunctions.EnsureReady();
+ return s_HelperFunctions.GetModuleNameFromAddress( pAddress, pModuleNameOut, iMaxModuleNameLength );
+}
+
+#else //#if defined( WIN32 ) && !defined( _X360 )
+
+//===============================================================================================================
+// X360 version of the toolset
+//===============================================================================================================
+
+class C360StackTranslationHelper
+{
+public:
+ C360StackTranslationHelper( void )
+ {
+ m_bInitialized = true;
+ }
+
+ ~C360StackTranslationHelper( void )
+ {
+ StringSet_t::const_iterator iter;
+
+ //module names
+ {
+ iter = m_ModuleNameSet.begin();
+ while(iter != m_ModuleNameSet.end())
+ {
+ char *pModuleName = (char*)(*iter);
+ delete []pModuleName;
+ iter++;
+ }
+ m_ModuleNameSet.clear();
+ }
+
+ //file names
+ {
+ iter = m_FileNameSet.begin();
+ while(iter != m_FileNameSet.end())
+ {
+ char *pFileName = (char*)(*iter);
+ delete []pFileName;
+ iter++;
+ }
+ m_FileNameSet.clear();
+ }
+
+ //symbol names
+ {
+ iter = m_SymbolNameSet.begin();
+ while(iter != m_SymbolNameSet.end())
+ {
+ char *pSymbolName = (char*)(*iter);
+ delete []pSymbolName;
+ iter++;
+ }
+ m_SymbolNameSet.clear();
+ }
+
+ m_bInitialized = false;
+ }
+
+private:
+ struct StackAddressInfo_t;
+public:
+
+ inline StackAddressInfo_t *CreateEntry( const FullStackInfo_t &info )
+ {
+ std::pair<AddressInfoMapIter_t, bool> retval = m_AddressInfoMap.insert( AddressInfoMapEntry_t( info.pAddress, StackAddressInfo_t() ) );
+ if( retval.first->second.szModule != NULL )
+ return &retval.first->second; //already initialized
+
+ retval.first->second.iLine = info.iLine;
+
+
+ //share strings
+
+ //module
+ {
+ const char *pModuleName;
+ StringSet_t::const_iterator iter = m_ModuleNameSet.find( info.szModuleName );
+ if ( iter == m_ModuleNameSet.end() )
+ {
+ int nLen = strlen(info.szModuleName) + 1;
+ pModuleName = new char [nLen];
+ memcpy( (char *)pModuleName, info.szModuleName, nLen );
+ m_ModuleNameSet.insert( pModuleName );
+ }
+ else
+ {
+ pModuleName = (char *)(*iter);
+ }
+
+ retval.first->second.szModule = pModuleName;
+ }
+
+ //file
+ {
+ const char *pFileName;
+ StringSet_t::const_iterator iter = m_FileNameSet.find( info.szFileName );
+ if ( iter == m_FileNameSet.end() )
+ {
+ int nLen = strlen(info.szFileName) + 1;
+ pFileName = new char [nLen];
+ memcpy( (char *)pFileName, info.szFileName, nLen );
+ m_FileNameSet.insert( pFileName );
+ }
+ else
+ {
+ pFileName = (char *)(*iter);
+ }
+
+ retval.first->second.szFileName = pFileName;
+ }
+
+ //symbol
+ {
+ const char *pSymbolName;
+ StringSet_t::const_iterator iter = m_SymbolNameSet.find( info.szSymbol );
+ if ( iter == m_SymbolNameSet.end() )
+ {
+ int nLen = strlen(info.szSymbol) + 1;
+ pSymbolName = new char [nLen];
+ memcpy( (char *)pSymbolName, info.szSymbol, nLen );
+ m_SymbolNameSet.insert( pSymbolName );
+ }
+ else
+ {
+ pSymbolName = (char *)(*iter);
+ }
+
+ retval.first->second.szSymbol = pSymbolName;
+ }
+
+ return &retval.first->second;
+ }
+
+ inline StackAddressInfo_t *FindInfoEntry( const void *pAddress )
+ {
+ AddressInfoMapIter_t Iter = m_AddressInfoMap.find( pAddress );
+ if( Iter != m_AddressInfoMap.end() )
+ return &Iter->second;
+
+ return NULL;
+ }
+
+ inline int RetrieveStackInfo( const void * const *pAddresses, FullStackInfo_t *pReturnedStructs, int iAddressCount )
+ {
+ int ReturnedTranslatedCount = -1;
+ //construct the message
+ // Header Finished Count(out) Input Count Input Array Returned data write address
+ int iMessageSize = 2 + sizeof( int * ) + sizeof( uint32 ) + (sizeof( void * ) * iAddressCount) + sizeof( FullStackInfo_t * );
+ uint8 *pMessage = (uint8 *)stackalloc( iMessageSize );
+ uint8 *pMessageWrite = pMessage;
+ pMessageWrite[0] = XBX_DBG_BNH_STACKTRANSLATOR; //have this message handled by stack translator handler
+ pMessageWrite[1] = ST_BHC_GETTRANSLATIONINFO;
+ pMessageWrite += 2;
+ *(int **)pMessageWrite = (int *)BigDWord( (DWORD)&ReturnedTranslatedCount );
+ pMessageWrite += sizeof( int * );
+ *(uint32 *)pMessageWrite = (uint32)BigDWord( (DWORD)iAddressCount );
+ pMessageWrite += sizeof( uint32 );
+ memcpy( pMessageWrite, pAddresses, iAddressCount * sizeof( void * ) );
+ pMessageWrite += iAddressCount * sizeof( void * );
+ *(FullStackInfo_t **)pMessageWrite = (FullStackInfo_t *)BigDWord( (DWORD)pReturnedStructs );
+ bool bSuccess = XBX_SendBinaryData( pMessage, iMessageSize, false, 30000 );
+ ReturnedTranslatedCount = BigDWord( ReturnedTranslatedCount );
+
+ if( bSuccess && (ReturnedTranslatedCount > 0) )
+ {
+ return ReturnedTranslatedCount;
+ }
+ return 0;
+ }
+
+ inline StackAddressInfo_t *CreateEntry( const void *pAddress )
+ {
+ //ask VXConsole for information about the addresses we're clueless about
+
+ FullStackInfo_t ReturnedData;
+ ReturnedData.pAddress = pAddress;
+ ReturnedData.szFileName[0] = '\0'; //strncpy( ReturnedData.szFileName, "FileUninitialized", sizeof( ReturnedData.szFileName ) );
+ ReturnedData.szModuleName[0] = '\0'; //strncpy( ReturnedData.szModuleName, "ModuleUninitialized", sizeof( ReturnedData.szModuleName ) );
+ ReturnedData.szSymbol[0] = '\0'; //strncpy( ReturnedData.szSymbol, "SymbolUninitialized", sizeof( ReturnedData.szSymbol ) );
+ ReturnedData.iLine = 0;
+ ReturnedData.iSymbolOffset = 0;
+
+ int iTranslated = RetrieveStackInfo( &pAddress, &ReturnedData, 1 );
+
+ if( iTranslated == 1 )
+ {
+ //store
+ return CreateEntry( ReturnedData );
+ }
+
+ return FindInfoEntry( pAddress ); //probably won't work, but last ditch.
+ }
+
+ inline StackAddressInfo_t *FindOrCreateEntry( const void *pAddress )
+ {
+ StackAddressInfo_t *pReturn = FindInfoEntry( pAddress );
+ if( pReturn == NULL )
+ {
+ pReturn = CreateEntry( pAddress );
+ }
+
+ return pReturn;
+ }
+
+ inline void LoadStackInformation( void * const *pAddresses, int iAddressCount )
+ {
+ Assert( (iAddressCount > 0) && (pAddresses != NULL) );
+
+ int iNeedLoading = 0;
+ void **pNeedLoading = (void **)stackalloc( sizeof( const void * ) * iAddressCount ); //addresses we need to ask VXConsole about
+
+ for( int i = 0; i != iAddressCount; ++i )
+ {
+ if( FindInfoEntry( pAddresses[i] ) == NULL )
+ {
+ //need to load this address
+ pNeedLoading[iNeedLoading] = pAddresses[i];
+ ++iNeedLoading;
+ }
+ }
+
+ if( iNeedLoading != 0 )
+ {
+ //ask VXConsole for information about the addresses we're clueless about
+ FullStackInfo_t *pReturnedStructs = (FullStackInfo_t *)stackalloc( sizeof( FullStackInfo_t ) * iNeedLoading );
+
+ for( int i = 0; i < iNeedLoading; ++i )
+ {
+ pReturnedStructs[i].pAddress = 0;
+ pReturnedStructs[i].szFileName[0] = '\0'; //strncpy( pReturnedStructs[i].szFileName, "FileUninitialized", sizeof( pReturnedStructs[i].szFileName ) );
+ pReturnedStructs[i].szModuleName[0] = '\0'; //strncpy( pReturnedStructs[i].szModuleName, "ModuleUninitialized", sizeof( pReturnedStructs[i].szModuleName ) );
+ pReturnedStructs[i].szSymbol[0] = '\0'; //strncpy( pReturnedStructs[i].szSymbol, "SymbolUninitialized", sizeof( pReturnedStructs[i].szSymbol ) );
+ pReturnedStructs[i].iLine = 0;
+ pReturnedStructs[i].iSymbolOffset = 0;
+ }
+
+ int iTranslated = RetrieveStackInfo( pNeedLoading, pReturnedStructs, iNeedLoading );
+
+ if( iTranslated == iNeedLoading )
+ {
+ //store
+ for( int i = 0; i < iTranslated; ++i )
+ {
+ CreateEntry( pReturnedStructs[i] );
+ }
+ }
+ }
+ }
+
+ inline bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+ {
+ StackAddressInfo_t *pInfo = FindOrCreateEntry( pAddress );
+ if( pInfo && (pInfo->szFileName[0] != '\0') )
+ {
+ strncpy( pFileNameOut, pInfo->szFileName, iMaxFileNameLength );
+ iLineNumberOut = pInfo->iLine;
+
+ if( pDisplacementOut )
+ *pDisplacementOut = 0; //can't get line displacement on 360
+
+ return true;
+ }
+
+ return false;
+ }
+
+ inline bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+ {
+ StackAddressInfo_t *pInfo = FindOrCreateEntry( pAddress );
+ if( pInfo && (pInfo->szSymbol[0] != '\0') )
+ {
+ strncpy( pSymbolNameOut, pInfo->szSymbol, iMaxSymbolNameLength );
+
+ if( pDisplacementOut )
+ *pDisplacementOut = pInfo->iSymbolOffset;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ inline bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+ {
+ StackAddressInfo_t *pInfo = FindOrCreateEntry( pAddress );
+ if( pInfo && (pInfo->szModule[0] != '\0') )
+ {
+ strncpy( pModuleNameOut, pInfo->szModule, iMaxModuleNameLength );
+ return true;
+ }
+
+ return false;
+ }
+
+
+ CThreadFastMutex m_hMutex;
+
+private:
+
+#pragma pack(push)
+#pragma pack(1)
+ struct StackAddressInfo_t
+ {
+ StackAddressInfo_t( void ) : szModule(NULL), szFileName(NULL), szSymbol(NULL), iLine(0), iSymbolOffset(0) {}
+ const char *szModule;
+ const char *szFileName;
+ const char *szSymbol;
+ uint32 iLine;
+ uint32 iSymbolOffset;
+ };
+#pragma pack(pop)
+
+ typedef std::map< const void *, StackAddressInfo_t, std::less<const void *>> AddressInfoMap_t;
+ typedef AddressInfoMap_t::iterator AddressInfoMapIter_t;
+ typedef AddressInfoMap_t::value_type AddressInfoMapEntry_t;
+
+ class CStringLess
+ {
+ public:
+ bool operator()(const char *pszLeft, const char *pszRight ) const
+ {
+ return ( V_tier0_stricmp( pszLeft, pszRight ) < 0 );
+ }
+ };
+
+ typedef std::set<const char *, CStringLess> StringSet_t;
+
+ AddressInfoMap_t m_AddressInfoMap; //TODO: retire old entries?
+ StringSet_t m_ModuleNameSet;
+ StringSet_t m_FileNameSet;
+ StringSet_t m_SymbolNameSet;
+ bool m_bInitialized;
+
+};
+static C360StackTranslationHelper s_360StackTranslator;
+
+
+int GetCallStack( void **pReturnAddressesOut, int iArrayCount, int iSkipCount )
+{
+ ++iSkipCount; //skip this function
+
+ //DmCaptureStackBackTrace() has no skip functionality, so we need to grab everything and skip within that list
+ void **pAllResults = (void **)stackalloc( sizeof( void * ) * (iArrayCount + iSkipCount) );
+ if( DmCaptureStackBackTrace( iArrayCount + iSkipCount, pAllResults ) == XBDM_NOERR )
+ {
+ for( int i = 0; i != iSkipCount; ++i )
+ {
+ if( *pAllResults == NULL ) //DmCaptureStackBackTrace() NULL terminates the list instead of telling us how many were returned
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, 0 );
+
+ ++pAllResults; //move the pointer forward so the second loop indices match up
+ }
+
+ for( int i = 0; i != iArrayCount; ++i )
+ {
+ if( pAllResults[i] == NULL ) //DmCaptureStackBackTrace() NULL terminates the list instead of telling us how many were returned
+ return AppendParentStackTrace( pReturnAddressesOut, iArrayCount, i );
+
+ pReturnAddressesOut[i] = pAllResults[i];
+ }
+
+ return iArrayCount; //no room to append parent
+ }
+
+ return GetCallStack_Fast( pReturnAddressesOut, iArrayCount, iSkipCount );
+}
+
+
+void SetStackTranslationSymbolSearchPath( const char *szSemicolonSeparatedList )
+{
+ //nop on 360
+}
+
+
+void StackToolsNotify_LoadedLibrary( const char *szLibName )
+{
+ //send off the notice to VXConsole
+ uint8 message[2];
+ message[0] = XBX_DBG_BNH_STACKTRANSLATOR; //have this message handled by stack translator handler
+ message[1] = ST_BHC_LOADEDLIBARY; //loaded a library notification
+ XBX_SendBinaryData( message, 2 );
+}
+
+
+int TranslateStackInfo( const void * const *pCallStack, int iCallStackCount, tchar *szOutput, int iOutBufferSize, const tchar *szEntrySeparator, TranslateStackInfo_StyleFlags_t style )
+{
+ if( iCallStackCount == 0 )
+ {
+ if( iOutBufferSize > 1 )
+ *szOutput = '\0';
+
+ return 0;
+ }
+
+ if( szEntrySeparator == NULL )
+ szEntrySeparator = "";
+
+ int iSeparatorLength = strlen( szEntrySeparator ) + 1;
+ int iDataSize = (sizeof( void * ) * iCallStackCount) + (iSeparatorLength * sizeof( tchar )) + 1; //1 for style flags
+
+ //360 is incapable of translation on it's own. Encode the stack for translation in VXConsole
+ //Encoded section is as such ":CSDECODE[encoded binary]"
+ int iEncodedSize = -EncodeBinaryToString( NULL, iDataSize, NULL, 0 ); //get needed buffer size
+ const tchar cControlPrefix[] = XBX_CALLSTACKDECODEPREFIX;
+ const size_t cControlLength = (sizeof( cControlPrefix )/sizeof(tchar)) - 1; //-1 to remove null terminator
+
+ if( iOutBufferSize > (iEncodedSize + (int)cControlLength + 2) ) //+2 for ']' and null term
+ {
+ COMPILE_TIME_ASSERT( TSISTYLEFLAG_LAST < (1<<8) ); //need to update the encoder/decoder to use more than a byte for style flags
+
+ uint8 *pData = (uint8 *)stackalloc( iDataSize );
+ pData[0] = (uint8)style;
+ memcpy( pData + 1, szEntrySeparator, iSeparatorLength * sizeof( tchar ) );
+ memcpy( pData + 1 + (iSeparatorLength * sizeof( tchar )), pCallStack, iCallStackCount * sizeof( void * ) );
+
+ memcpy( szOutput, XBX_CALLSTACKDECODEPREFIX, cControlLength * sizeof( tchar ) );
+ int iLength = cControlLength + EncodeBinaryToString( pData, iDataSize, &szOutput[cControlLength], (iOutBufferSize - cControlLength) );
+
+ szOutput[iLength] = ']';
+ szOutput[iLength + 1] = '\0';
+ }
+
+ return iCallStackCount;
+}
+
+void PreloadStackInformation( void * const *pAddresses, int iAddressCount )
+{
+ AUTO_LOCK_FM( s_360StackTranslator.m_hMutex );
+ s_360StackTranslator.LoadStackInformation( pAddresses, iAddressCount );
+}
+
+bool GetFileAndLineFromAddress( const void *pAddress, tchar *pFileNameOut, int iMaxFileNameLength, uint32 &iLineNumberOut, uint32 *pDisplacementOut )
+{
+ AUTO_LOCK_FM( s_360StackTranslator.m_hMutex );
+ return s_360StackTranslator.GetFileAndLineFromAddress( pAddress, pFileNameOut, iMaxFileNameLength, iLineNumberOut, pDisplacementOut );
+}
+
+bool GetSymbolNameFromAddress( const void *pAddress, tchar *pSymbolNameOut, int iMaxSymbolNameLength, uint64 *pDisplacementOut )
+{
+ AUTO_LOCK_FM( s_360StackTranslator.m_hMutex );
+ return s_360StackTranslator.GetSymbolNameFromAddress( pAddress, pSymbolNameOut, iMaxSymbolNameLength, pDisplacementOut );
+}
+
+bool GetModuleNameFromAddress( const void *pAddress, tchar *pModuleNameOut, int iMaxModuleNameLength )
+{
+ AUTO_LOCK_FM( s_360StackTranslator.m_hMutex );
+ return s_360StackTranslator.GetModuleNameFromAddress( pAddress, pModuleNameOut, iMaxModuleNameLength );
+}
+
+#endif //#else //#if defined( WIN32 ) && !defined( _X360 )
+
+#endif //#if !defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+
+
+
+
+CCallStackStorage::CCallStackStorage( FN_GetCallStack GetStackFunction, uint32 iSkipCalls )
+{
+ iValidEntries = GetStackFunction( pStack, ARRAYSIZE( pStack ), iSkipCalls + 1 );
+}
+
+
+
+CStackTop_CopyParentStack::CStackTop_CopyParentStack( void * const *pParentStackTrace, int iParentStackTraceLength )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ //miniature version of GetCallStack_Fast()
+#if (defined( TIER0_FPO_DISABLED ) || defined( _DEBUG )) &&\
+ (defined( WIN32 ) && !defined( _X360 ))
+ void *pStackCrawlEBP;
+ __asm
+ {
+ mov [pStackCrawlEBP], ebp;
+ }
+ pStackCrawlEBP = *(void **)pStackCrawlEBP;
+ m_pReplaceAddress = *((void **)pStackCrawlEBP + 1);
+ m_pStackBase = (void *)((void **)pStackCrawlEBP + 1);
+#else
+ m_pReplaceAddress = NULL;
+ m_pStackBase = this;
+#endif
+
+ m_pParentStackTrace = NULL;
+
+ if( (pParentStackTrace != NULL) && (iParentStackTraceLength > 0) )
+ {
+ while( (iParentStackTraceLength > 0) && (pParentStackTrace[iParentStackTraceLength - 1] == NULL) )
+ {
+ --iParentStackTraceLength;
+ }
+
+ if( iParentStackTraceLength > 0 )
+ {
+ m_pParentStackTrace = new void * [iParentStackTraceLength];
+ memcpy( (void **)m_pParentStackTrace, pParentStackTrace, sizeof( void * ) * iParentStackTraceLength );
+ }
+ }
+
+ m_iParentStackTraceLength = iParentStackTraceLength;
+
+ m_pPrevTop = g_StackTop;
+ g_StackTop = this;
+ Assert( (CStackTop_Base *)g_StackTop == this );
+#endif //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+}
+
+CStackTop_CopyParentStack::~CStackTop_CopyParentStack( void )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ Assert( (CStackTop_Base *)g_StackTop == this );
+ g_StackTop = m_pPrevTop;
+
+ if( m_pParentStackTrace != NULL )
+ {
+ delete []m_pParentStackTrace;
+ }
+#endif
+}
+
+
+
+CStackTop_ReferenceParentStack::CStackTop_ReferenceParentStack( void * const *pParentStackTrace, int iParentStackTraceLength )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ //miniature version of GetCallStack_Fast()
+#if (defined( TIER0_FPO_DISABLED ) || defined( _DEBUG )) &&\
+ (defined( WIN32 ) && !defined( _X360 ))
+ void *pStackCrawlEBP;
+ __asm
+ {
+ mov [pStackCrawlEBP], ebp;
+ }
+ pStackCrawlEBP = *(void **)pStackCrawlEBP;
+ m_pReplaceAddress = *((void **)pStackCrawlEBP + 1);
+ m_pStackBase = (void *)((void **)pStackCrawlEBP + 1);
+#else
+ m_pReplaceAddress = NULL;
+ m_pStackBase = this;
+#endif
+
+ m_pParentStackTrace = pParentStackTrace;
+
+ if( (pParentStackTrace != NULL) && (iParentStackTraceLength > 0) )
+ {
+ while( (iParentStackTraceLength > 0) && (pParentStackTrace[iParentStackTraceLength - 1] == NULL) )
+ {
+ --iParentStackTraceLength;
+ }
+ }
+
+ m_iParentStackTraceLength = iParentStackTraceLength;
+
+ m_pPrevTop = g_StackTop;
+ g_StackTop = this;
+ Assert( (CStackTop_Base *)g_StackTop == this );
+#endif //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+}
+
+CStackTop_ReferenceParentStack::~CStackTop_ReferenceParentStack( void )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ Assert( (CStackTop_Base *)g_StackTop == this );
+ g_StackTop = m_pPrevTop;
+
+ ReleaseParentStackReferences();
+#endif
+}
+
+void CStackTop_ReferenceParentStack::ReleaseParentStackReferences( void )
+{
+#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
+ m_pParentStackTrace = NULL;
+ m_iParentStackTraceLength = 0;
+#endif
+}
+
+
+
+
+//Encodes data so that every byte's most significant bit is a 1. Ensuring no null terminators.
+//This puts the encoded data in the 128-255 value range. Leaving all standard ascii characters for control.
+//Returns string length (not including the written null terminator as is standard).
+//Or if the buffer is too small. Returns negative of necessary buffer size (including room needed for null terminator)
+int EncodeBinaryToString( const void *pToEncode, int iDataLength, char *pEncodeOut, int iEncodeBufferSize )
+{
+ int iEncodedSize = iDataLength;
+ iEncodedSize += (iEncodedSize + 6) / 7; //Have 1 control byte for every 7 actual bytes
+ iEncodedSize += sizeof( uint32 ) + 1; //data size at the beginning of the blob and null terminator at the end
+
+ if( (iEncodedSize > iEncodeBufferSize) || (pEncodeOut == NULL) || (pToEncode == NULL) )
+ return -iEncodedSize; //not enough room
+
+ uint8 *pEncodeWrite = (uint8 *)pEncodeOut;
+
+ //first encode the data size. Encodes lowest 28 bits and discards the high 4
+ pEncodeWrite[0] = ((iDataLength >> 21) & 0xFF) | 0x80;
+ pEncodeWrite[1] = ((iDataLength >> 14) & 0xFF) | 0x80;
+ pEncodeWrite[2] = ((iDataLength >> 7) & 0xFF) | 0x80;
+ pEncodeWrite[3] = ((iDataLength >> 0) & 0xFF) | 0x80;
+ pEncodeWrite += 4;
+
+ const uint8 *pEncodeRead = (const uint8 *)pToEncode;
+ const uint8 *pEncodeStop = pEncodeRead + iDataLength;
+ uint8 *pEncodeWriteLastControlByte = pEncodeWrite;
+ int iControl = 0;
+
+ //Encode the data
+ while( pEncodeRead < pEncodeStop )
+ {
+ if( iControl == 0 )
+ {
+ pEncodeWriteLastControlByte = pEncodeWrite;
+ *pEncodeWriteLastControlByte = 0x80;
+ }
+ else
+ {
+ *pEncodeWrite = *pEncodeRead | 0x80; //encoded data always has the MSB bit set (cheap avoidance of null terminators)
+ *pEncodeWriteLastControlByte |= (((*pEncodeRead) & 0x80) ^ 0x80) >> iControl; //We use the control byte to XOR the MSB back to original values on decode
+ ++pEncodeRead;
+ }
+
+ ++pEncodeWrite;
+ ++iControl;
+ iControl &= 7; //8->0
+ }
+ *pEncodeWrite = '\0';
+
+ return iEncodedSize - 1;
+}
+
+//Decodes a string produced by EncodeBinaryToString(). Safe to decode in place if you don't mind trashing your string, binary byte count always less than string byte count.
+//Returns:
+// >= 0 is the decoded data size
+// INT_MIN (most negative value possible) indicates an improperly formatted string (not our data)
+// all other negative values are the negative of how much dest buffer size is necessary.
+int DecodeBinaryFromString( const char *pString, void *pDestBuffer, int iDestBufferSize, char **ppParseFinishOut )
+{
+ const uint8 *pDecodeRead = (const uint8 *)pString;
+
+ if( (pDecodeRead[0] < 0x80) || (pDecodeRead[1] < 0x80) || (pDecodeRead[2] < 0x80) || (pDecodeRead[3] < 0x80) )
+ {
+ if( ppParseFinishOut != NULL )
+ *ppParseFinishOut = (char *)pString;
+
+ return INT_MIN; //Don't know what the string is, but it's not our format
+ }
+
+ int iDecodedSize = 0;
+ iDecodedSize |= (pDecodeRead[0] & 0x7F) << 21;
+ iDecodedSize |= (pDecodeRead[1] & 0x7F) << 14;
+ iDecodedSize |= (pDecodeRead[2] & 0x7F) << 7;
+ iDecodedSize |= (pDecodeRead[3] & 0x7F) << 0;
+ pDecodeRead += 4;
+
+ int iTextLength = iDecodedSize;
+ iTextLength += (iTextLength + 6) / 7; //Have 1 control byte for every 7 actual bytes
+
+ //make sure it's formatted properly
+ for( int i = 0; i != iTextLength; ++i )
+ {
+ if( pDecodeRead[i] < 0x80 ) //encoded data always has MSB set
+ {
+ if( ppParseFinishOut != NULL )
+ *ppParseFinishOut = (char *)pString;
+
+ return INT_MIN; //either not our data, or part of the string is missing
+ }
+ }
+
+ if( iDestBufferSize < iDecodedSize )
+ {
+ if( ppParseFinishOut != NULL )
+ *ppParseFinishOut = (char *)pDecodeRead;
+
+ return -iDecodedSize; //dest buffer not big enough to hold the data
+ }
+
+ const uint8 *pStopDecoding = pDecodeRead + iTextLength;
+ uint8 *pDecodeWrite = (uint8 *)pDestBuffer;
+ int iControl = 0;
+ int iLSBXOR = 0;
+
+ while( pDecodeRead < pStopDecoding )
+ {
+ if( iControl == 0 )
+ {
+ iLSBXOR = *pDecodeRead;
+ }
+ else
+ {
+ *pDecodeWrite = *pDecodeRead ^ ((iLSBXOR << iControl) & 0x80);
+ ++pDecodeWrite;
+ }
+
+ ++pDecodeRead;
+ ++iControl;
+ iControl &= 7; //8->0
+ }
+
+ if( ppParseFinishOut != NULL )
+ *ppParseFinishOut = (char *)pDecodeRead;
+
+ return iDecodedSize;
+}
+
+
diff --git a/external/vpc/tier0/threadtools.cpp b/external/vpc/tier0/threadtools.cpp
new file mode 100644
index 0000000..735f233
--- /dev/null
+++ b/external/vpc/tier0/threadtools.cpp
@@ -0,0 +1,3096 @@
+//========== Copyright 2005, Valve Corporation, All rights reserved. ========
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "tier0/platform.h"
+
+#if defined( PLATFORM_WINDOWS_PC )
+#define WIN32_LEAN_AND_MEAN
+#define _WIN32_WINNT 0x0403
+#include <windows.h>
+#endif
+
+#ifdef PLATFORM_WINDOWS
+ #include <process.h>
+ #ifdef PLATFORM_WINDOWS_PC
+ #include <Mmsystem.h>
+ #pragma comment(lib, "winmm.lib")
+ #endif
+#elif PLATFORM_PS3
+ #include <sched.h>
+ #include <unistd.h>
+ #include <exception>
+ #include <errno.h>
+ #include <pthread.h>
+ #include <sys/time.h>
+ #include <sys/timer.h>
+ #define GetLastError() errno
+ typedef void *LPVOID;
+#elif PLATFORM_POSIX
+ #include <sched.h>
+ #include <exception>
+ #include <errno.h>
+ #include <signal.h>
+ #include <pthread.h>
+ #include <sys/time.h>
+ #define GetLastError() errno
+ typedef void *LPVOID;
+#if !defined(OSX)
+ #include <sys/fcntl.h>
+ #include <sys/unistd.h>
+ #define sem_unlink( arg )
+#else
+ #define pthread_yield pthread_yield_np
+ #include <mach/thread_act.h>
+#endif // !OSX
+
+#endif
+
+#ifndef _PS3
+#include <memory.h>
+#endif
+#include "tier0/threadtools.h"
+
+#ifdef _X360
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+#include <map>
+
+// Must be last header...
+#include "tier0/memdbgon.h"
+
+#ifdef _PS3
+#include "ps3/ps3_win32stubs.h"
+#define NEW_WAIT_FOR_MULTIPLE_OBJECTS
+bool gbCheckNotMultithreaded = true;
+#endif
+
+#define THREADS_DEBUG 1
+
+#define DEBUG_ERROR(XX) Assert(0)
+
+// Need to ensure initialized before other clients call in for main thread ID
+#ifdef _WIN32
+#pragma warning(disable:4073)
+#pragma init_seg(lib)
+#endif
+
+#ifdef _WIN32
+ASSERT_INVARIANT(TT_SIZEOF_CRITICALSECTION == sizeof(CRITICAL_SECTION));
+ASSERT_INVARIANT(TT_INFINITE == INFINITE);
+#endif
+
+// thread creation counter.
+// this is used to provide a unique threadid for each running thread in g_nThreadID ( a thread local variable ).
+
+const int MAX_THREAD_IDS = 128;
+
+static volatile bool s_bThreadIDAllocated[MAX_THREAD_IDS];
+
+#ifdef LINUX
+
+DLL_CLASS_EXPORT __thread int g_nThreadID;
+
+#elif defined(_PS3)
+ #include "tls_ps3.h"
+#else
+ CTHREADLOCALINT g_nThreadID;
+#endif
+
+
+static CThreadFastMutex s_ThreadIDMutex;
+
+PLATFORM_INTERFACE void AllocateThreadID( void )
+{
+ AUTO_LOCK( s_ThreadIDMutex );
+ for( int i = 1; i < MAX_THREAD_IDS; i++ )
+ {
+ if ( ! s_bThreadIDAllocated[i] )
+ {
+ g_nThreadID = i;
+ s_bThreadIDAllocated[i] = true;
+ return;
+ }
+ }
+ Error( "Out of thread ids. Decrease the number of threads or increase MAX_THREAD_IDS\n" );
+}
+
+PLATFORM_INTERFACE void FreeThreadID( void )
+{
+ AUTO_LOCK( s_ThreadIDMutex );
+ int nThread = g_nThreadID;
+ if ( nThread )
+ s_bThreadIDAllocated[nThread] = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Simple thread functions.
+// Because _beginthreadex uses stdcall, we need to convert to cdecl
+//-----------------------------------------------------------------------------
+struct ThreadProcInfo_t
+{
+ ThreadProcInfo_t( ThreadFunc_t pfnThread, void *pParam )
+ : pfnThread( pfnThread),
+ pParam( pParam )
+ {
+ }
+
+ ThreadFunc_t pfnThread;
+ void * pParam;
+};
+
+//---------------------------------------------------------
+
+#ifdef PLATFORM_WINDOWS
+static DWORD WINAPI ThreadProcConvert( void *pParam )
+{
+ ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
+ AllocateThreadID();
+ delete ((ThreadProcInfo_t *)pParam);
+ unsigned nRet = (*info.pfnThread)(info.pParam);
+ FreeThreadID();
+ return nRet;
+}
+#elif defined( PLATFORM_PS3 )
+union ThreadProcInfoUnion_t
+{
+ struct Val_t
+ {
+ ThreadFunc_t pfnThread;
+ void * pParam;
+ }
+ val;
+ uint64_t val64;
+};
+static void ThreadProcConvertUnion( uint64_t param )
+{
+ COMPILE_TIME_ASSERT( sizeof( ThreadProcInfoUnion_t ) == 8 );
+ ThreadProcInfoUnion_t info;
+ info.val64 = param;
+ AllocateThreadID();
+ unsigned nRet = (*info.val.pfnThread)(info.val.pParam);
+ FreeThreadID();
+ sys_ppu_thread_exit( nRet );
+}
+static void* ThreadProcConvert( void *pParam )
+{
+ ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
+ AllocateThreadID();
+ delete ((ThreadProcInfo_t *)pParam);
+ unsigned nRet = (*info.pfnThread)(info.pParam);
+ FreeThreadID();
+ return ( void * ) nRet;
+}
+
+#else
+static void* ThreadProcConvert( void *pParam )
+{
+ ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam);
+ AllocateThreadID();
+ delete ((ThreadProcInfo_t *)pParam);
+ unsigned nRet = (*info.pfnThread)(info.pParam);
+ FreeThreadID();
+ return ( void * ) nRet;
+}
+#endif
+
+
+
+#if defined( _PS3 )
+
+/*******************************************************************************
+* Thread Local Storage globals and functions
+*******************************************************************************/
+#ifndef _PS3
+__thread void *gTLSValues[ MAX_TLS_VALUES ] = { NULL };
+__thread bool gTLSFlags[ MAX_TLS_VALUES ] = { false };
+__thread bool gbWaitObjectsCreated = false;
+__thread sys_semaphore_t gWaitObjectsSemaphore;
+#endif // !_PS3
+
+static char gThreadName[28] = "";
+
+// Simple TLS allocator. Linearly searches for a free slot.
+uint32 TlsAlloc()
+{
+ for ( int i = 0; i < MAX_TLS_VALUES; ++i )
+ {
+ if ( !gTLSFlags[i] )
+ {
+ gTLSFlags[i] = true;
+ return i;
+ }
+ }
+
+#ifdef _PS3
+ DEBUG_ERROR("TlsAlloc(): Out of TLS\n");
+#endif
+
+ return 0xFFFFFFFF;
+}
+
+void TlsFree( uint32 index )
+{
+ gTLSValues[ index ] = NULL;
+ gTLSFlags[ index ] = false;
+}
+
+void *TlsGetValue( uint32 index )
+{
+ return gTLSValues[ index ];
+}
+
+void TlsSetValue( uint32 index, void *pValue )
+{
+ gTLSValues[ index ] = pValue;
+}
+#endif //_PS3
+
+
+
+#ifdef PLATFORM_WINDOWS
+class CThreadHandleToIDMap
+{
+public:
+ HANDLE m_hThread;
+ uint m_ThreadID;
+ CThreadHandleToIDMap *m_pNext;
+};
+static CThreadHandleToIDMap *g_pThreadHandleToIDMaps = NULL;
+static CThreadMutex g_ThreadHandleToIDMapMutex;
+static volatile int g_nThreadHandleToIDMaps = 0;
+
+static void AddThreadHandleToIDMap( HANDLE hThread, uint threadID )
+{
+ if ( !hThread )
+ return;
+
+ // Remember this handle/id combo.
+ CThreadHandleToIDMap *pMap = new CThreadHandleToIDMap;
+ pMap->m_hThread = hThread;
+ pMap->m_ThreadID = threadID;
+
+ // Add it to the global list.
+ g_ThreadHandleToIDMapMutex.Lock();
+ pMap->m_pNext = g_pThreadHandleToIDMaps;
+ g_pThreadHandleToIDMaps = pMap;
+ ++g_nThreadHandleToIDMaps;
+
+ g_ThreadHandleToIDMapMutex.Unlock();
+
+ if ( g_nThreadHandleToIDMaps > 500 )
+ Error( "ThreadHandleToIDMap overflow." );
+}
+
+// This assumes you've got g_ThreadHandleToIDMapMutex locked!!
+static bool InternalLookupHandleToThreadIDMap( HANDLE hThread, CThreadHandleToIDMap* &pMap, CThreadHandleToIDMap** &ppPrev )
+{
+ ppPrev = &g_pThreadHandleToIDMaps;
+ for ( pMap=g_pThreadHandleToIDMaps; pMap; pMap=pMap->m_pNext )
+ {
+ if ( pMap->m_hThread == hThread )
+ return true;
+
+ ppPrev = &pMap->m_pNext;
+ }
+
+ return false;
+}
+
+static void RemoveThreadHandleToIDMap( HANDLE hThread )
+{
+ if ( !hThread )
+ return;
+
+ CThreadHandleToIDMap *pMap, **ppPrev;
+
+ g_ThreadHandleToIDMapMutex.Lock();
+
+ if ( g_nThreadHandleToIDMaps <= 0 )
+ Error( "ThreadHandleToIDMap underflow." );
+
+ if ( InternalLookupHandleToThreadIDMap( hThread, pMap, ppPrev ) )
+ {
+ *ppPrev = pMap->m_pNext;
+ delete pMap;
+ --g_nThreadHandleToIDMaps;
+ }
+
+ g_ThreadHandleToIDMapMutex.Unlock();
+}
+
+static uint LookupThreadIDFromHandle( HANDLE hThread )
+{
+ if ( hThread == NULL || hThread == GetCurrentThread() )
+ return GetCurrentThreadId();
+
+ float flStartTime = Plat_FloatTime();
+ while ( Plat_FloatTime() - flStartTime < 2 )
+ {
+ CThreadHandleToIDMap *pMap, **ppPrev;
+
+ g_ThreadHandleToIDMapMutex.Lock();
+ bool bRet = InternalLookupHandleToThreadIDMap( hThread, pMap, ppPrev );
+ g_ThreadHandleToIDMapMutex.Unlock();
+
+ if ( bRet )
+ return pMap->m_ThreadID;
+
+ // We should only get here if a thread that is just starting up is currently in AddThreadHandleToIDMap.
+ // Give up the timeslice and try again.
+ ThreadSleep( 1 );
+ }
+
+ Assert( !"LookupThreadIDFromHandle failed!" );
+ Warning( "LookupThreadIDFromHandle couldn't find thread ID for handle." );
+ return 0;
+}
+#endif
+
+
+//---------------------------------------------------------
+
+ThreadHandle_t * CreateTestThreads( ThreadFunc_t fnThread, int numThreads, int nProcessorsToDistribute )
+{
+ ThreadHandle_t *pHandles = (new ThreadHandle_t[numThreads+1]) + 1;
+ pHandles[-1] = (ThreadHandle_t)INT_TO_POINTER( numThreads );
+ for( int i = 0; i < numThreads; ++i )
+ {
+ //TestThreads();
+ ThreadHandle_t hThread;
+ const unsigned int nDefaultStackSize = 64 * 1024; // this stack size is used in case stackSize == 0
+ hThread = CreateSimpleThread( fnThread, INT_TO_POINTER( i ), nDefaultStackSize );
+
+ if ( nProcessorsToDistribute )
+ {
+ int32 mask = 1 << (i % nProcessorsToDistribute);
+ ThreadSetAffinity( hThread, mask );
+ }
+
+/*
+ ThreadProcInfoUnion_t info;
+ info.val.pfnThread = fnThread;
+ info.val.pParam = (void*)(i);
+ if ( int nError = sys_ppu_thread_create( &hThread, ThreadProcConvertUnion, info.val64, 1001, nDefaultStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, "SimpleThread" ) != CELL_OK )
+ {
+ printf( "PROBLEM!\n" );
+ Error( "Cannot create thread, error %d\n", nError );
+ return 0;
+ }
+*/
+ //ThreadHandle_t hThread = CreateSimpleThread( fnThread, (void*)i );
+ pHandles[i] = hThread;
+ }
+// printf("Finishinged CreateTestThreads(%p,%d)\n", (void*)fnThread, numThreads );
+ return pHandles;
+}
+
+void JoinTestThreads( ThreadHandle_t *pHandles )
+{
+ int nCount = POINTER_TO_INT( pHandles[-1] );
+// printf("Joining test threads @%p[%d]:\n", pHandles, nCount );
+// for( int i = 0; i < nCount; ++i )
+// {
+// printf(" %p,\n", (void*)pHandles[i] );
+// }
+ for( int i = 0; i < nCount; ++i )
+ {
+// printf( "Joining %p", (void*) pHandles[i] );
+// if( !i ) sys_timer_usleep(100000);
+ ThreadJoin( pHandles[i] );
+ ReleaseThreadHandle( pHandles[i] );
+ }
+ delete[]( pHandles - 1 );
+}
+
+
+
+ThreadHandle_t CreateSimpleThread( ThreadFunc_t pfnThread, void *pParam, unsigned stackSize )
+{
+#ifdef PLATFORM_WINDOWS
+ DWORD threadID;
+ HANDLE hThread = (HANDLE)CreateThread( NULL, stackSize, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ), 0, &threadID );
+ AddThreadHandleToIDMap( hThread, threadID );
+ return (ThreadHandle_t)hThread;
+#elif PLATFORM_PS3
+ //TestThreads();
+ ThreadHandle_t th;
+ ThreadProcInfoUnion_t info;
+ info.val.pfnThread = pfnThread;
+ info.val.pParam = pParam;
+ const unsigned int nDefaultStackSize = 64 * 1024; // this stack size is used in case stackSize == 0
+ if ( sys_ppu_thread_create( &th, ThreadProcConvertUnion, info.val64, 1001, stackSize ? stackSize : nDefaultStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, "SimpleThread" ) != CELL_OK )
+ {
+ AssertMsg1( 0, "Failed to create thread (error 0x%x)", errno );
+ return 0;
+ }
+ return th;
+#elif PLATFORM_POSIX
+ pthread_t tid;
+ pthread_create( &tid, NULL, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ) );
+ return ( ThreadHandle_t ) tid;
+#else
+ Assert( 0 );
+ DebuggerBreak();
+ return 0;
+#endif
+}
+
+
+bool ReleaseThreadHandle( ThreadHandle_t hThread )
+{
+#ifdef _WIN32
+ bool bRetVal = ( CloseHandle( hThread ) != 0 );
+ RemoveThreadHandleToIDMap( (HANDLE)hThread );
+ return bRetVal;
+#else
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//
+// Wrappers for other simple threading operations
+//
+//-----------------------------------------------------------------------------
+
+void ThreadSleep(unsigned nMilliseconds)
+{
+#ifdef _WIN32
+
+#ifdef PLATFORM_WINDOWS_PC
+ static bool bInitialized = false;
+ if ( !bInitialized )
+ {
+ bInitialized = true;
+ // Set the timer resolution to 1 ms (default is 10.0, 15.6, 2.5, 1.0 or
+ // some other value depending on hardware and software) so that we can
+ // use Sleep( 1 ) to avoid wasting CPU time without missing our frame
+ // rate.
+ timeBeginPeriod( 1 );
+ }
+#endif
+
+ Sleep( nMilliseconds );
+#elif PLATFORM_PS3
+ if( nMilliseconds == 0 )
+ {
+ // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
+ sys_timer_usleep( 60 );
+ }
+ else
+ {
+ sys_timer_usleep( nMilliseconds * 1000 );
+ }
+#elif defined(POSIX)
+ usleep( nMilliseconds * 1000 );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+#ifndef ThreadGetCurrentId
+ThreadId_t ThreadGetCurrentId()
+{
+#ifdef _WIN32
+ return GetCurrentThreadId();
+#elif defined( _PS3 )
+ sys_ppu_thread_t th = 0;
+ sys_ppu_thread_get_id( &th );
+ return th;
+#elif defined(POSIX)
+ return (ThreadId_t)pthread_self();
+#else
+ Assert(0);
+ DebuggerBreak();
+ return 0;
+#endif
+}
+#endif
+
+//-----------------------------------------------------------------------------
+ThreadHandle_t ThreadGetCurrentHandle()
+{
+#ifdef _WIN32
+ return (ThreadHandle_t)GetCurrentThread();
+#elif defined( _PS3 )
+ sys_ppu_thread_t th = 0;
+ sys_ppu_thread_get_id( &th );
+ return th;
+#elif defined(POSIX)
+ return (ThreadHandle_t)pthread_self();
+#else
+ Assert(0);
+ DebuggerBreak();
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+int ThreadGetPriority( ThreadHandle_t hThread )
+{
+ if ( !hThread )
+ {
+ hThread = ThreadGetCurrentHandle();
+ }
+
+#ifdef _WIN32
+ return ::GetThreadPriority( (HANDLE)hThread );
+#elif defined( _PS3 )
+ int iPri = 0;
+ sys_ppu_thread_get_priority( hThread, &iPri );
+ return iPri;
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+bool ThreadSetPriority( ThreadHandle_t hThread, int priority )
+{
+ if ( !hThread )
+ {
+ hThread = ThreadGetCurrentHandle();
+ }
+
+#ifdef _WIN32
+ return ( SetThreadPriority(hThread, priority) != 0 );
+#elif defined( _PS3 )
+ int retval = sys_ppu_thread_set_priority( hThread, priority );
+ return retval >= CELL_OK;
+#elif defined(POSIX)
+ struct sched_param thread_param;
+ thread_param.sched_priority = priority;
+ //pthread_setschedparam( (pthread_t ) hThread, SCHED_RR, &thread_param );
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+void ThreadSetAffinity( ThreadHandle_t hThread, int nAffinityMask )
+{
+ if ( !hThread )
+ {
+ hThread = ThreadGetCurrentHandle();
+ }
+
+#ifdef _WIN32
+ SetThreadAffinityMask( hThread, nAffinityMask );
+#elif defined(POSIX)
+// cpu_set_t cpuSet;
+// CPU_ZERO( cpuSet );
+// for( int i = 0 ; i < 32; i++ )
+// if ( nAffinityMask & ( 1 << i ) )
+// CPU_SET( cpuSet, i );
+// sched_setaffinity( hThread, sizeof( cpuSet ), &cpuSet );
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+
+#ifndef _X360
+uint InitMainThread()
+{
+ ThreadSetDebugName( "MainThrd" );
+
+ return ThreadGetCurrentId();
+}
+
+unsigned long g_ThreadMainThreadID = InitMainThread();
+
+bool ThreadInMainThread()
+{
+ return ( ThreadGetCurrentId() == g_ThreadMainThreadID );
+}
+
+void DeclareCurrentThreadIsMainThread()
+{
+ g_ThreadMainThreadID = ThreadGetCurrentId();
+}
+
+#else
+byte *InitMainThread()
+{
+ byte b;
+
+ return AlignValue( &b, 64*1024 );
+}
+#define STACK_SIZE_360 327680
+byte *g_pBaseMainStack = InitMainThread();
+byte *g_pLimitMainStack = InitMainThread() - STACK_SIZE_360;
+#endif
+
+//-----------------------------------------------------------------------------
+bool ThreadJoin( ThreadHandle_t hThread, unsigned timeout )
+{
+ if ( !hThread )
+ {
+ return false;
+ }
+
+#ifdef _WIN32
+ DWORD dwWait = WaitForSingleObject( (HANDLE)hThread, timeout );
+ if ( dwWait == WAIT_TIMEOUT)
+ return false;
+ if ( dwWait != WAIT_OBJECT_0 && ( dwWait != WAIT_FAILED && GetLastError() != 0 ) )
+ {
+ Assert( 0 );
+ return false;
+ }
+#elif defined( _PS3 )
+ uint64 uiExitCode = 0;
+ int retval = sys_ppu_thread_join( hThread, &uiExitCode );
+ return ( retval >= CELL_OK );
+#elif defined(POSIX)
+ if ( pthread_join( (pthread_t)hThread, NULL ) != 0 )
+ return false;
+#else
+ Assert(0);
+ DebuggerBreak();
+#endif
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+void ThreadSetDebugName( ThreadHandle_t hThread, const char *pszName )
+{
+#ifdef WIN32
+ if ( Plat_IsInDebugSession() )
+ {
+#define MS_VC_EXCEPTION 0x406d1388
+
+ typedef struct tagTHREADNAME_INFO
+ {
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in same addr space)
+ DWORD dwThreadID; // thread ID (-1 caller thread)
+ DWORD dwFlags; // reserved for future use, most be zero
+ } THREADNAME_INFO;
+
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = pszName;
+ info.dwThreadID = LookupThreadIDFromHandle( hThread );
+
+ if ( info.dwThreadID != 0 )
+ {
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
+ }
+ __except (EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+ }
+ }
+#endif
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Used to thread LoadLibrary on the 360
+//-----------------------------------------------------------------------------
+static ThreadedLoadLibraryFunc_t s_ThreadedLoadLibraryFunc = 0;
+PLATFORM_INTERFACE void SetThreadedLoadLibraryFunc( ThreadedLoadLibraryFunc_t func )
+{
+ s_ThreadedLoadLibraryFunc = func;
+}
+
+PLATFORM_INTERFACE ThreadedLoadLibraryFunc_t GetThreadedLoadLibraryFunc()
+{
+ return s_ThreadedLoadLibraryFunc;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CThreadSyncObject (note nothing uses this directly (I think) )
+//
+//-----------------------------------------------------------------------------
+
+
+#ifdef _PS3
+uint32_t CThreadSyncObject::m_bstaticMutexInitialized = false;
+uint32_t CThreadSyncObject::m_bstaticMutexInitializing = false;
+sys_lwmutex_t CThreadSyncObject::m_staticMutex;
+#endif
+
+
+CThreadSyncObject::CThreadSyncObject()
+#ifdef _WIN32
+ : m_hSyncObject( NULL ), m_bCreatedHandle(false)
+#elif defined(POSIX) && !defined(PLATFORM_PS3)
+ : m_bInitalized( false )
+#endif
+{
+#ifdef _PS3
+ //Do we nee to initialise the staticMutex?
+ if (m_bstaticMutexInitialized) return;
+
+ //If we are the first thread then create the mutex
+ if ( cellAtomicCompareAndSwap32(&m_bstaticMutexInitializing, false, true) == false )
+ {
+ sys_lwmutex_attribute_t mutexAttr;
+ sys_lwmutex_attribute_initialize( mutexAttr );
+ mutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
+ int err = sys_lwmutex_create( &m_staticMutex, &mutexAttr );
+ Assert(err == CELL_OK);
+ m_bstaticMutexInitialized = true;
+ }
+ else
+ {
+ //Another thread is already in the process of initialising the mutex, wait for it
+ while ( !m_bstaticMutexInitialized )
+ {
+ // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
+ sys_timer_usleep( 60 );
+ }
+ }
+#endif
+}
+
+//---------------------------------------------------------
+
+CThreadSyncObject::~CThreadSyncObject()
+{
+#ifdef _WIN32
+ if ( m_hSyncObject && m_bCreatedHandle )
+ {
+ if ( !CloseHandle(m_hSyncObject) )
+ {
+ Assert( 0 );
+ }
+ }
+#elif defined(POSIX) && !defined( PLATFORM_PS3 )
+ if ( m_bInitalized )
+ {
+ pthread_cond_destroy( &m_Condition );
+ pthread_mutex_destroy( &m_Mutex );
+ m_bInitalized = false;
+ }
+#endif
+}
+
+//---------------------------------------------------------
+
+bool CThreadSyncObject::operator!() const
+{
+#if PLATFORM_PS3
+ return m_bstaticMutexInitialized;
+#elif defined( _WIN32 )
+ return !m_hSyncObject;
+#elif defined(POSIX)
+ return !m_bInitalized;
+#endif
+}
+
+//---------------------------------------------------------
+
+void CThreadSyncObject::AssertUseable()
+{
+#ifdef THREADS_DEBUG
+#if PLATFORM_PS3
+ AssertMsg( m_bstaticMutexInitialized, "Thread synchronization object is unuseable" );
+#elif defined( _WIN32 )
+ AssertMsg( m_hSyncObject, "Thread synchronization object is unuseable" );
+#elif defined(POSIX)
+ AssertMsg( m_bInitalized, "Thread synchronization object is unuseable" );
+#endif
+#endif
+}
+
+//---------------------------------------------------------
+
+#if defined(_WIN32) || ( defined(POSIX) && !defined( _PS3 ) )
+bool CThreadSyncObject::Wait( uint32 dwTimeout )
+{
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+#ifdef _WIN32
+ return ( WaitForSingleObject( m_hSyncObject, dwTimeout ) == WAIT_OBJECT_0 );
+#elif defined( POSIX ) && !defined( PLATFORM_PS3 )
+ pthread_mutex_lock( &m_Mutex );
+ bool bRet = false;
+ if ( m_cSet > 0 )
+ {
+ bRet = true;
+ m_bWakeForEvent = false;
+ }
+ else
+ {
+ volatile int ret = 0;
+
+ while ( !m_bWakeForEvent && ret != ETIMEDOUT )
+ {
+ struct timeval tv;
+ gettimeofday( &tv, NULL );
+ volatile struct timespec tm;
+
+ uint64 actualTimeout = dwTimeout;
+
+ if ( dwTimeout == TT_INFINITE && m_bManualReset )
+ actualTimeout = 10; // just wait 10 msec at most for manual reset events and loop instead
+
+ volatile uint64 nNanoSec = (uint64)tv.tv_usec*1000 + (uint64)actualTimeout*1000000;
+ tm.tv_sec = tv.tv_sec + nNanoSec /1000000000;
+ tm.tv_nsec = nNanoSec % 1000000000;
+
+ do
+ {
+ ret = pthread_cond_timedwait( &m_Condition, &m_Mutex, (const timespec *)&tm );
+ }
+ while( ret == EINTR );
+
+ bRet = ( ret == 0 );
+
+ if ( m_bManualReset )
+ {
+ if ( m_cSet )
+ break;
+ if ( dwTimeout == TT_INFINITE && ret == ETIMEDOUT )
+ ret = 0; // force the loop to spin back around
+ }
+ }
+
+ if ( bRet )
+ m_bWakeForEvent = false;
+ }
+ if ( !m_bManualReset && bRet )
+ m_cSet = 0;
+ pthread_mutex_unlock( &m_Mutex );
+ return bRet;
+#endif
+}
+#endif
+
+#ifdef _WIN32
+#pragma optimize( "", off ) // The compiler optimizer screws up the function below.
+#endif
+
+uint32 CThreadSyncObject::WaitForMultiple( int nObjects, CThreadSyncObject **ppObjects, bool bWaitAll, uint32 dwTimeout )
+{
+#if defined( _WIN32 )
+
+ CThreadSyncObject *pHandles = (CThreadSyncObject*)stackalloc( sizeof(CThreadSyncObject) * nObjects );
+ for ( int i=0; i < nObjects; i++ )
+ {
+ pHandles[i].m_hSyncObject = ppObjects[i]->m_hSyncObject;
+ }
+
+ return WaitForMultiple( nObjects, pHandles, bWaitAll, dwTimeout );
+
+#else
+
+ // TODO: Need a more efficient implementation of this.
+ uint32 dwStartTime = 0;
+
+ if ( dwTimeout != TT_INFINITE )
+ dwStartTime = Plat_MSTime();
+
+ // If bWaitAll = true, then we need to track which ones were triggered.
+ char *pWasTriggered = NULL;
+ int nTriggered = 0;
+ if ( bWaitAll )
+ {
+ pWasTriggered = (char*)stackalloc( nObjects );
+ memset( pWasTriggered, 0, nObjects );
+ }
+
+ while ( 1 )
+ {
+ for ( int i=0; i < nObjects; i++ )
+ {
+ if ( bWaitAll && pWasTriggered[i] )
+ continue;
+
+#ifdef _PS3
+ Assert( !"Not implemented!" );
+ if ( false )
+#else
+ if ( ppObjects[i]->Wait( 0 ) )
+#endif
+ {
+ ++nTriggered;
+ if ( bWaitAll )
+ {
+ if ( nTriggered == nObjects )
+ return 0;
+ else
+ pWasTriggered[i] = 1;
+ }
+ else
+ {
+ return i;
+ }
+ }
+ }
+
+ // Timeout?
+ if ( dwTimeout != TT_INFINITE )
+ {
+ if ( Plat_MSTime() - dwStartTime >= dwTimeout )
+ return TW_TIMEOUT;
+ }
+
+ ThreadSleep( 0 );
+ }
+
+#endif
+}
+
+#ifdef _WIN32
+#pragma optimize( "", on )
+#endif
+
+uint32 CThreadSyncObject::WaitForMultiple( int nObjects, CThreadSyncObject *pObjects, bool bWaitAll, uint32 dwTimeout )
+{
+#if defined(_WIN32 )
+
+ HANDLE *pHandles = (HANDLE*)stackalloc( sizeof(HANDLE) * nObjects );
+ for ( int i=0; i < nObjects; i++ )
+ {
+ pHandles[i] = pObjects[i].m_hSyncObject;
+ }
+
+ DWORD ret = WaitForMultipleObjects( nObjects, pHandles, bWaitAll, dwTimeout );
+ if ( ret == WAIT_TIMEOUT )
+ return TW_TIMEOUT;
+ else if ( ret >= WAIT_OBJECT_0 && (ret-WAIT_OBJECT_0) < (uint32)nObjects )
+ return (int)(ret - WAIT_OBJECT_0);
+ else if ( ret >= WAIT_ABANDONED_0 && (ret - WAIT_ABANDONED_0) < (uint32)nObjects )
+ Error( "Unhandled WAIT_ABANDONED in WaitForMultipleObjects" );
+ else if ( ret == WAIT_FAILED )
+ return TW_FAILED;
+ else
+ Error( "Unknown return value (%lu) from WaitForMultipleObjects", ret );
+
+ // We'll never get here..
+ return 0;
+
+#else
+
+ CThreadSyncObject **ppObjects = (CThreadSyncObject**)stackalloc( sizeof( CThreadSyncObject* ) * nObjects );
+ for ( int i=0; i < nObjects; i++ )
+ {
+ ppObjects[i] = &pObjects[i];
+ }
+
+ return WaitForMultiple( nObjects, ppObjects, bWaitAll, dwTimeout );
+
+#endif
+}
+
+// To implement these, I need to check that casts are safe
+uint32 CThreadEvent::WaitForMultiple( int nObjects, CThreadEvent *pObjects, bool bWaitAll, uint32 dwTimeout )
+{
+ // If data ever gets added to CThreadEvent, then we need a different implementation.
+#ifdef _PS3
+ CThreadEvent **ppObjects = (CThreadEvent**)stackalloc( sizeof( CThreadEvent* ) * nObjects );
+ for ( int i=0; i < nObjects; i++ )
+ {
+ ppObjects[i] = &pObjects[i];
+ }
+ return WaitForMultipleObjects( nObjects, ppObjects, bWaitAll, dwTimeout );
+#else
+ COMPILE_TIME_ASSERT( sizeof( CThreadSyncObject ) == 0 || sizeof( CThreadEvent ) == sizeof( CThreadSyncObject ) );
+ return CThreadSyncObject::WaitForMultiple( nObjects, (CThreadSyncObject*)pObjects, bWaitAll, dwTimeout );
+#endif
+}
+
+
+uint32 CThreadEvent::WaitForMultiple( int nObjects, CThreadEvent **ppObjects, bool bWaitAll, uint32 dwTimeout )
+{
+#ifdef _PS3
+ return WaitForMultipleObjects( nObjects, ppObjects, bWaitAll, dwTimeout );
+#else
+ // If data ever gets added to CThreadEvent, then we need a different implementation.
+ COMPILE_TIME_ASSERT( sizeof( CThreadSyncObject )== 0 || sizeof( CThreadEvent ) == sizeof( CThreadSyncObject ) );
+ return CThreadSyncObject::WaitForMultiple( nObjects, (CThreadSyncObject**)ppObjects, bWaitAll, dwTimeout );
+#endif
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+CThreadEvent::CThreadEvent( bool bManualReset )
+{
+#ifdef _WIN32
+ m_hSyncObject = CreateEvent( NULL, bManualReset, FALSE, NULL );
+ m_bCreatedHandle = true;
+ AssertMsg1(m_hSyncObject, "Failed to create event (error 0x%x)", GetLastError() );
+#elif defined( _PS3 )
+
+ m_bManualReset = bManualReset;
+ m_bSet = 0;
+ m_bInitalized = false;
+ m_numWaitingThread = 0;
+
+ // set up linked list of wait objects
+
+ memset(&m_waitObjects[0], 0, sizeof(m_waitObjects));
+ m_pWaitObjectsList = &m_waitObjects[0];
+ m_pWaitObjectsPool = &m_waitObjects[1];
+
+ for (int i = 2; i < CTHREADEVENT_MAX_WAITING_THREADS + 2; i++)
+ {
+ LLLinkNode(m_pWaitObjectsPool, &m_waitObjects[i]);
+ }
+#elif defined( POSIX )
+ pthread_mutexattr_t Attr;
+ pthread_mutexattr_init( &Attr );
+ pthread_mutex_init( &m_Mutex, &Attr );
+ pthread_mutexattr_destroy( &Attr );
+ pthread_cond_init( &m_Condition, NULL );
+ m_bInitalized = true;
+ m_cSet = 0;
+ m_bWakeForEvent = false;
+ m_bManualReset = bManualReset;
+#else
+#error "Implement me"
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+#ifdef _PS3
+
+//
+// linked list functionality
+//
+
+//-----------------------------------------------------------------------------
+// Purpose: Linked list implementation
+//-----------------------------------------------------------------------------
+
+CThreadEventWaitObject* CThreadEvent::LLUnlinkNode(CThreadEventWaitObject *node)
+{
+ // <sergiy> Note: if you have a null-access crash here, it may mean that CTHREADEVENT_MAX_WAITING_THREADS is not high enough
+ // and the linked list pool is simply exhausted
+ node->m_pPrev->m_pNext = node->m_pNext;
+ if (node->m_pNext) node->m_pNext->m_pPrev = node->m_pPrev;
+ node->m_pNext = node->m_pPrev = NULL;
+
+ return node;
+}
+
+CThreadEventWaitObject* CThreadEvent::LLLinkNode(CThreadEventWaitObject* list, CThreadEventWaitObject *node)
+{
+ node->m_pNext = list->m_pNext;
+ if (node->m_pNext)
+ {
+ node->m_pNext->m_pPrev = node;
+ }
+
+ list->m_pNext = node;
+ node->m_pPrev = list;
+
+ return node;
+}
+
+//-----------------------------------------------------------------------------
+// Helper function to atomically write index into destination and set semaphore
+// This is used by WaitForMultipleObjects(WAIT_ANY) because once the semaphore
+// is set, the waiting thread also needs to know which event triggered it
+// We do NOT need this to be atomic because if a number of events fire it doesn't
+// matter which one of these we pick
+//-----------------------------------------------------------------------------
+void CThreadEventWaitObject::Set()
+{
+ *m_pFlag = m_index;
+ sys_semaphore_post(*m_pSemaphore, 1);
+}
+
+//
+// CThreadEvent::RegisterWaitingThread
+//
+void CThreadEvent::RegisterWaitingThread(sys_semaphore_t *pSemaphore, int index, int *flag)
+{
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ // if we are already set, then signal this semaphore
+ if (m_bSet)
+ {
+ CThreadEventWaitObject waitObject;
+ waitObject.Init(pSemaphore, index, flag);
+ waitObject.Set();
+
+ if (!m_bManualReset)
+ {
+ m_bSet = false;
+ }
+ }
+ else
+ {
+ if (!m_pWaitObjectsPool->m_pNext)
+ {
+ DEBUG_ERROR("CThreadEvent: Ran out of events; cannot register waiting thread\n");
+ }
+
+ // add this semaphore to linked list - can be added more than once it doesn't matter
+
+ CThreadEventWaitObject *pWaitObject = LLUnlinkNode(m_pWaitObjectsPool->m_pNext);
+
+ pWaitObject->Init(pSemaphore, index, flag);
+
+ LLLinkNode(m_pWaitObjectsList, pWaitObject);
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+}
+
+//
+// CThreadEvent::UnregisterWaitingThread
+//
+void CThreadEvent::UnregisterWaitingThread(sys_semaphore_t *pSemaphore)
+{
+ // remove all instances of this semaphore from linked list
+
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ CThreadEventWaitObject *pWaitObject = m_pWaitObjectsList->m_pNext;
+
+ while (pWaitObject)
+ {
+ CThreadEventWaitObject *pNext = pWaitObject->m_pNext;
+
+ if (pWaitObject->m_pSemaphore == pSemaphore)
+ {
+ LLUnlinkNode(pWaitObject);
+ LLLinkNode(m_pWaitObjectsPool, pWaitObject);
+ }
+
+ pWaitObject = pNext;
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+}
+
+#endif // _PS3
+
+
+#ifdef PLATFORM_WINDOWS
+ CThreadEvent::CThreadEvent( const char *name, bool initialState, bool bManualReset )
+ {
+ m_hSyncObject = CreateEvent( NULL, bManualReset, (BOOL) initialState, name );
+ AssertMsg1( m_hSyncObject, "Failed to create event (error 0x%x)", GetLastError() );
+ }
+
+
+ NamedEventResult_t CThreadEvent::CheckNamedEvent( const char *name, uint32 dwTimeout )
+ {
+ HANDLE eHandle = OpenEvent( SYNCHRONIZE, FALSE, name );
+
+ if ( eHandle == NULL ) return TT_EventDoesntExist;
+
+ DWORD result = WaitForSingleObject( eHandle, dwTimeout );
+
+ return ( result == WAIT_OBJECT_0 ) ? TT_EventSignaled : TT_EventNotSignaled;
+ }
+#endif
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+
+//---------------------------------------------------------
+
+bool CThreadEvent::Set()
+{
+//////////////////////////////////////////////////////////////
+#ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+ AssertUseable();
+#ifdef _WIN32
+ return ( SetEvent( m_hSyncObject ) != 0 );
+#elif defined( _PS3 )
+
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ if (m_bManualReset)
+ {
+ //Mark event as set
+ m_bSet = true;
+
+ //If any threads are already waiting then signal them to run
+ if (m_bInitalized)
+ {
+ int err = sys_semaphore_post( m_Semaphore, m_numWaitingThread);
+ Assert(err == CELL_OK);
+ }
+ }
+ else
+ {
+ //If any threads are already waiting then signal ONE to run, else signal next to run
+
+ if (m_numWaitingThread>0)
+ {
+ int err = sys_semaphore_post( m_Semaphore, 1);
+ Assert(err == CELL_OK);
+ }
+ else
+ {
+ m_bSet=true;
+ }
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+
+ return true;
+
+
+#elif defined(POSIX)
+ pthread_mutex_lock( &m_Mutex );
+ m_cSet = 1;
+ m_bWakeForEvent = true;
+ int ret = pthread_cond_signal( &m_Condition );
+ pthread_mutex_unlock( &m_Mutex );
+ return ret == 0;
+#endif
+
+
+//////////////////////////////////////////////////////////////
+#else // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ //Mark event as set
+ m_bSet = true;
+
+ // signal registered semaphores
+ while (m_pWaitObjectsList->m_pNext)
+ {
+ CThreadEventWaitObject *pWaitObject = LLUnlinkNode(m_pWaitObjectsList->m_pNext);
+
+ pWaitObject->Set();
+
+ LLLinkNode(m_pWaitObjectsPool, pWaitObject);
+
+ if (!m_bManualReset)
+ {
+ m_bSet = false;
+ break;
+ }
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+
+ return true;
+
+//////////////////////////////////////////////////////////////
+#endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+}
+
+//---------------------------------------------------------
+
+bool CThreadEvent::Reset()
+{
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+#ifdef _WIN32
+ return ( ResetEvent( m_hSyncObject ) != 0 );
+#elif defined( _PS3 )
+
+ //Just mark us as no longer signaled
+ m_bSet = 0;
+
+ return true;
+#elif defined(POSIX)
+ pthread_mutex_lock( &m_Mutex );
+ m_cSet = 0;
+ m_bWakeForEvent = false;
+ pthread_mutex_unlock( &m_Mutex );
+ return true;
+#endif
+}
+
+//---------------------------------------------------------
+
+bool CThreadEvent::Check()
+{
+ #ifdef _PS3
+ return m_bSet; // Please, use for debugging only!
+ #endif
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+ return Wait( 0 );
+}
+
+
+
+bool CThreadEvent::Wait( uint32 dwTimeout )
+{
+//////////////////////////////////////////////////////////////
+#ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+
+
+#if defined( _WIN32 ) || ( defined( POSIX ) && !defined( _PS3 ) )
+ return CThreadSyncObject::Wait( dwTimeout );
+#elif defined( _PS3 )
+
+ {
+
+ if (dwTimeout == 0)
+ {
+ //If timeout is 0 then just test it now (and reset it if manual )
+ if (m_bSet)
+ {
+ if ( !m_bManualReset ) m_bSet=false;
+ return true;
+ }
+ return false;
+ }
+
+ if (!AddWaitingThread())
+ {
+ //Waiting thread NOT added because m_bSet was already set
+ if ( !m_bManualReset ) m_bSet=false;
+ return true;
+ }
+
+ uint32 timeout;
+ int countTimeout = 0;
+ int ret = ETIMEDOUT;
+ while ( timeout=MIN(1, dwTimeout) )
+ {
+ // on the PS3, "infinite timeout" is specified by zero, not
+ // 0xFFFFFFFF, so we need to perform that ternary here.
+#error Untested code:
+ ret = sys_semaphore_wait( m_Semaphore, timeout == TT_INFINITE ? 0 : timeout * 1000 );
+ Assert( (ret == CELL_OK) || (ret == ETIMEDOUT) );
+
+ if ( ret == CELL_OK )
+ break;
+
+ dwTimeout -= timeout;
+ countTimeout++;
+ if (countTimeout > 30)
+ {
+ printf("WARNING: possible deadlock in CThreadEvent::Wait() !!!\n");
+ }
+ }
+
+ RemoveWaitingThread();
+
+ if ( !m_bManualReset ) m_bSet=false;
+
+ return ret == CELL_OK;
+ }
+
+#endif
+
+//////////////////////////////////////////////////////////////
+#else // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+
+
+ CThreadEvent *pThis = this;
+ DWORD res = WaitForMultipleObjects(1, &pThis, true, dwTimeout);
+ return res == WAIT_OBJECT_0;
+
+
+//////////////////////////////////////////////////////////////
+#endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+//////////////////////////////////////////////////////////////
+}
+
+#ifdef _WIN32
+//-----------------------------------------------------------------------------
+//
+// CThreadSemaphore
+//
+// To get Posix implementation, try http://www-128.ibm.com/developerworks/eserver/library/es-win32linux-sem.html
+//
+//-----------------------------------------------------------------------------
+
+CThreadSemaphore::CThreadSemaphore( int32 initialValue, int32 maxValue )
+{
+#ifdef _WIN32
+ if ( maxValue )
+ {
+ AssertMsg( maxValue > 0, "Invalid max value for semaphore" );
+ AssertMsg( initialValue >= 0 && initialValue <= maxValue, "Invalid initial value for semaphore" );
+
+ m_hSyncObject = CreateSemaphore( NULL, initialValue, maxValue, NULL );
+
+ AssertMsg1(m_hSyncObject, "Failed to create semaphore (error 0x%x)", GetLastError());
+ }
+ else
+ {
+ m_hSyncObject = NULL;
+ }
+#elif defined( _PS3 )
+ if ( maxValue )
+ {
+ m_sema_max_val = maxValue;
+ m_semaCount = initialValue;
+ }
+#endif
+}
+
+
+#ifdef _PS3
+//---------------------------------------------------------
+
+bool CThreadSemaphore::AddWaitingThread()
+{
+ bool result;
+
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ if (cellAtomicTestAndDecr32(&m_semaCount) > 0)
+ {
+ result=false;
+ }
+ else
+ {
+ result=true;
+ m_numWaitingThread++;
+
+ if ( m_numWaitingThread == 1 )
+ {
+ sys_semaphore_attribute_t semAttr;
+ sys_semaphore_attribute_initialize( semAttr );
+ Assert(m_semaCount == 0);
+ int err = sys_semaphore_create( &m_Semaphore, &semAttr, 0, m_sema_max_val );
+ Assert( err == CELL_OK );
+ m_bInitalized = true;
+ }
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+ return result;
+}
+
+void CThreadSemaphore::RemoveWaitingThread()
+{
+ sys_lwmutex_lock(&m_staticMutex, 0);
+
+ m_numWaitingThread--;
+
+ if ( m_numWaitingThread == 0)
+ {
+ int err = sys_semaphore_destroy( m_Semaphore );
+ Assert( err == CELL_OK );
+ m_bInitalized = false;
+ }
+
+ sys_lwmutex_unlock(&m_staticMutex);
+}
+
+#endif
+
+#ifdef _PS3
+
+bool CThreadSemaphore::Wait( uint32 dwTimeout )
+{
+#ifdef THREADS_DEBUG
+ AssertUseable();
+#endif
+
+
+#ifndef NO_THREAD_SYNC
+ if (!AddWaitingThread())
+ {
+ //Waiting thread NOT added because semaphore was already in a signaled state
+ return true;
+ }
+
+ int ret = sys_semaphore_wait( m_Semaphore, dwTimeout == TT_INFINITE ? 0 : dwTimeout * 1000 );
+ Assert( (ret == CELL_OK) || (ret == ETIMEDOUT) );
+
+ RemoveWaitingThread();
+
+ int old = cellAtomicDecr32(&m_semaCount);
+ Assert(old>0);
+#else
+ int ret = CELL_OK;
+#endif
+
+ // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead.
+ sys_timer_usleep( 60 );
+
+
+
+ return ret == CELL_OK;
+}
+
+#endif
+
+//---------------------------------------------------------
+
+bool CThreadSemaphore::Release( int32 releaseCount, int32 *pPreviousCount )
+{
+#ifdef THRDTOOL_DEBUG
+ AssertUseable();
+#endif
+#ifdef _WIN32
+ return ( ReleaseSemaphore( m_hSyncObject, releaseCount, (LPLONG)pPreviousCount ) != 0 );
+#elif defined( _PS3 )
+
+#ifndef NO_THREAD_SYNC
+
+ if (m_bInitalized)
+ {
+ sys_semaphore_value_t previousVal;
+ sys_semaphore_get_value( m_Semaphore, &previousVal );
+
+ cellAtomicAdd32(&m_semaCount, releaseCount);
+
+ *pPreviousCount = previousVal;
+
+ int err = sys_semaphore_post( m_Semaphore, releaseCount );
+ Assert(err == CELL_OK);
+ }
+
+#endif
+
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+CThreadFullMutex::CThreadFullMutex( bool bEstablishInitialOwnership, const char *pszName )
+{
+ m_hSyncObject = CreateMutex( NULL, bEstablishInitialOwnership, pszName );
+
+ AssertMsg1( m_hSyncObject, "Failed to create mutex (error 0x%x)", GetLastError() );
+}
+
+//---------------------------------------------------------
+
+bool CThreadFullMutex::Release()
+{
+#ifdef THRDTOOL_DEBUG
+ AssertUseable();
+#endif
+ return ( ReleaseMutex( m_hSyncObject ) != 0 );
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+#if ( defined(WIN32) || defined(OSX) ) && !defined(PLATFORM_PS3)
+namespace GenericThreadLocals
+{
+CThreadLocalBase::CThreadLocalBase()
+{
+#ifdef _WIN32
+ m_index = TlsAlloc();
+ AssertMsg( m_index != 0xFFFFFFFF, "Bad thread local" );
+ if ( m_index == 0xFFFFFFFF )
+ Error( "Out of thread local storage!\n" );
+#elif defined(POSIX)
+ if ( pthread_key_create( (pthread_key_t *)&m_index, NULL ) != 0 )
+ Error( "Out of thread local storage!\n" );
+#endif
+}
+
+//---------------------------------------------------------
+
+CThreadLocalBase::~CThreadLocalBase()
+{
+#ifdef _WIN32
+ if ( m_index != 0xFFFFFFFF )
+ TlsFree( m_index );
+ m_index = 0xFFFFFFFF;
+#elif defined(POSIX)
+ pthread_key_delete( m_index );
+#endif
+}
+
+//---------------------------------------------------------
+
+void * CThreadLocalBase::Get() const
+{
+#ifdef _WIN32
+ if ( m_index != 0xFFFFFFFF )
+ return TlsGetValue( m_index );
+ AssertMsg( 0, "Bad thread local" );
+ return NULL;
+#elif defined(POSIX)
+ void *value = pthread_getspecific( m_index );
+ return value;
+#endif
+}
+
+//---------------------------------------------------------
+
+void CThreadLocalBase::Set( void *value )
+{
+#ifdef _WIN32
+ if (m_index != 0xFFFFFFFF)
+ TlsSetValue(m_index, value);
+ else
+ AssertMsg( 0, "Bad thread local" );
+#elif defined(POSIX)
+ if ( pthread_setspecific( m_index, value ) != 0 )
+ AssertMsg( 0, "Bad thread local" );
+#endif
+}
+} // namespace GenericThreadLocals
+#endif // ( defined(WIN32) || defined(POSIX) ) && !defined(PLATFORM_PS3)
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+
+#ifdef MSVC
+//#ifdef _X360
+#define TO_INTERLOCK_PARAM(p) ((volatile long *)p)
+#define TO_INTERLOCK_PTR_PARAM(p) ((void **)p)
+//#else
+//#define TO_INTERLOCK_PARAM(p) (p)
+//#define TO_INTERLOCK_PTR_PARAM(p) (p)
+//#endif
+
+#if !defined(USE_INTRINSIC_INTERLOCKED) && !defined(_X360)
+int32 ThreadInterlockedIncrement( int32 volatile *pDest )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedIncrement( TO_INTERLOCK_PARAM(pDest) );
+}
+
+int32 ThreadInterlockedDecrement( int32 volatile *pDest )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedDecrement( TO_INTERLOCK_PARAM(pDest) );
+}
+
+int32 ThreadInterlockedExchange( int32 volatile *pDest, int32 value )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedExchange( TO_INTERLOCK_PARAM(pDest), value );
+}
+
+int32 ThreadInterlockedExchangeAdd( int32 volatile *pDest, int32 value )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedExchangeAdd( TO_INTERLOCK_PARAM(pDest), value );
+}
+
+int32 ThreadInterlockedCompareExchange( int32 volatile *pDest, int32 value, int32 comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedCompareExchange( TO_INTERLOCK_PARAM(pDest), value, comperand );
+}
+
+bool ThreadInterlockedAssignIf( int32 volatile *pDest, int32 value, int32 comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+
+#if !(defined(_WIN64) || defined (_X360))
+ __asm
+ {
+ mov eax,comperand
+ mov ecx,pDest
+ mov edx,value
+ lock cmpxchg [ecx],edx
+ mov eax,0
+ setz al
+ }
+#else
+ return ( InterlockedCompareExchange( TO_INTERLOCK_PARAM(pDest), value, comperand ) == comperand );
+#endif
+}
+
+#endif
+
+#if !defined( USE_INTRINSIC_INTERLOCKED ) || defined( _WIN64 )
+void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value );
+}
+
+void *ThreadInterlockedCompareExchangePointer( void * volatile *pDest, void *value, void *comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+ return InterlockedCompareExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value, comperand );
+}
+
+bool ThreadInterlockedAssignPointerIf( void * volatile *pDest, void *value, void *comperand )
+{
+ Assert( (size_t)pDest % 4 == 0 );
+#if !(defined(_WIN64) || defined (_X360))
+ __asm
+ {
+ mov eax,comperand
+ mov ecx,pDest
+ mov edx,value
+ lock cmpxchg [ecx],edx
+ mov eax,0
+ setz al
+ }
+#else
+ return ( InterlockedCompareExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value, comperand ) == comperand );
+#endif
+}
+#endif
+
+int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+
+#if defined(_WIN64) || defined (_X360)
+ return InterlockedCompareExchange64( pDest, value, comperand );
+#else
+ __asm
+ {
+ lea esi,comperand;
+ lea edi,value;
+
+ mov eax,[esi];
+ mov edx,4[esi];
+ mov ebx,[edi];
+ mov ecx,4[edi];
+ mov esi,pDest;
+ lock CMPXCHG8B [esi];
+ }
+#endif
+}
+
+bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 comperand )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+
+#if defined(_X360) || defined(_WIN64)
+ return ( ThreadInterlockedCompareExchange64( pDest, value, comperand ) == comperand );
+#else
+ __asm
+ {
+ lea esi,comperand;
+ lea edi,value;
+
+ mov eax,[esi];
+ mov edx,4[esi];
+ mov ebx,[edi];
+ mov ecx,4[edi];
+ mov esi,pDest;
+ lock CMPXCHG8B [esi];
+ mov eax,0;
+ setz al;
+ }
+#endif
+}
+
+#elif defined(GNUC)
+
+#ifdef OSX
+#include <libkern/OSAtomic.h>
+#endif
+
+
+long ThreadInterlockedIncrement( long volatile *pDest )
+{
+ return __sync_fetch_and_add( pDest, 1 ) + 1;
+}
+
+long ThreadInterlockedDecrement( long volatile *pDest )
+{
+ return __sync_fetch_and_sub( pDest, 1 ) - 1;
+}
+
+long ThreadInterlockedExchange( long volatile *pDest, long value )
+{
+ return __sync_lock_test_and_set( pDest, value );
+}
+
+long ThreadInterlockedExchangeAdd( long volatile *pDest, long value )
+{
+ return __sync_fetch_and_add( pDest, value );
+}
+
+long ThreadInterlockedCompareExchange( long volatile *pDest, long value, long comperand )
+{
+ return __sync_val_compare_and_swap( pDest, comperand, value );
+}
+
+bool ThreadInterlockedAssignIf( long volatile *pDest, long value, long comperand )
+{
+ return __sync_bool_compare_and_swap( pDest, comperand, value );
+}
+
+#if !defined( USE_INTRINSIC_INTERLOCKED )
+void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value )
+{
+ return __sync_lock_test_and_set( pDest, value );
+}
+
+void *ThreadInterlockedCompareExchangePointer( void *volatile *pDest, void *value, void *comperand )
+{
+ return __sync_val_compare_and_swap( pDest, comperand, value );
+}
+
+bool ThreadInterlockedAssignPointerIf( void * volatile *pDest, void *value, void *comperand )
+{
+ return __sync_bool_compare_and_swap( pDest, comperand, value );
+}
+#endif
+
+int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand )
+{
+#if defined(OSX)
+ int64 retVal = *pDest;
+ if ( OSAtomicCompareAndSwap64( comperand, value, pDest ) )
+ retVal = *pDest;
+
+ return retVal;
+#else
+ return __sync_val_compare_and_swap( pDest, comperand, value );
+#endif
+}
+
+bool ThreadInterlockedAssignIf64( int64 volatile * pDest, int64 value, int64 comperand )
+{
+ return __sync_bool_compare_and_swap( pDest, comperand, value );
+}
+
+
+#elif defined( _PS3 )
+
+// This is defined in the header!
+
+#else
+// This will perform horribly,
+#error "Falling back to mutexed interlocked operations, you really don't have intrinsics you can use?"ß
+CThreadMutex g_InterlockedMutex;
+
+long ThreadInterlockedIncrement( long volatile *pDest )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ return ++(*pDest);
+}
+
+long ThreadInterlockedDecrement( long volatile *pDest )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ return --(*pDest);
+}
+
+long ThreadInterlockedExchange( long volatile *pDest, long value )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ long retVal = *pDest;
+ *pDest = value;
+ return retVal;
+}
+
+void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ void *retVal = *pDest;
+ *pDest = value;
+ return retVal;
+}
+
+long ThreadInterlockedExchangeAdd( long volatile *pDest, long value )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ long retVal = *pDest;
+ *pDest += value;
+ return retVal;
+}
+
+long ThreadInterlockedCompareExchange( long volatile *pDest, long value, long comperand )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ long retVal = *pDest;
+ if ( *pDest == comperand )
+ *pDest = value;
+ return retVal;
+}
+
+void *ThreadInterlockedCompareExchangePointer( void * volatile *pDest, void *value, void *comperand )
+{
+ AUTO_LOCK( g_InterlockedMutex );
+ void *retVal = *pDest;
+ if ( *pDest == comperand )
+ *pDest = value;
+ return retVal;
+}
+
+
+int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+ AUTO_LOCK( g_InterlockedMutex );
+ int64 retVal = *pDest;
+ if ( *pDest == comperand )
+ *pDest = value;
+ return retVal;
+}
+
+#endif
+
+int64 ThreadInterlockedExchange64( int64 volatile *pDest, int64 value )
+{
+ Assert( (size_t)pDest % 8 == 0 );
+ int64 Old;
+
+ do
+ {
+ Old = *pDest;
+ } while (ThreadInterlockedCompareExchange64(pDest, value, Old) != Old);
+
+ return Old;
+}
+
+
+//-----------------------------------------------------------------------------
+
+#if defined(_WIN32) && defined(THREAD_PROFILER)
+void ThreadNotifySyncNoop(void *p) {}
+
+#define MAP_THREAD_PROFILER_CALL( from, to ) \
+ void from(void *p) \
+ { \
+ static CDynamicFunction<void (*)(void *)> dynFunc( "libittnotify.dll", #to, ThreadNotifySyncNoop ); \
+ (*dynFunc)(p); \
+ }
+
+MAP_THREAD_PROFILER_CALL( ThreadNotifySyncPrepare, __itt_notify_sync_prepare );
+MAP_THREAD_PROFILER_CALL( ThreadNotifySyncCancel, __itt_notify_sync_cancel );
+MAP_THREAD_PROFILER_CALL( ThreadNotifySyncAcquired, __itt_notify_sync_acquired );
+MAP_THREAD_PROFILER_CALL( ThreadNotifySyncReleasing, __itt_notify_sync_releasing );
+
+#endif
+
+//-----------------------------------------------------------------------------
+//
+// CThreadMutex
+//
+//-----------------------------------------------------------------------------
+
+#ifdef _PS3
+CThreadMutex::CThreadMutex()
+{
+ // sys_mutex with recursion enabled is like a win32 critical section
+ sys_mutex_attribute_t mutexAttr;
+ sys_mutex_attribute_initialize( mutexAttr );
+ mutexAttr.attr_recursive = SYS_SYNC_RECURSIVE;
+ sys_mutex_create( &m_Mutex, &mutexAttr );
+}
+CThreadMutex::~CThreadMutex()
+{
+ sys_mutex_destroy( m_Mutex );
+}
+#elif !defined( POSIX )
+CThreadMutex::CThreadMutex()
+{
+#ifdef THREAD_MUTEX_TRACING_ENABLED
+ memset( &m_CriticalSection, 0, sizeof(m_CriticalSection) );
+#endif
+ InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION *)&m_CriticalSection, 4000);
+#ifdef THREAD_MUTEX_TRACING_SUPPORTED
+ // These need to be initialized unconditionally in case mixing release & debug object modules
+ // Lock and unlock may be emitted as COMDATs, in which case may get spurious output
+ m_currentOwnerID = m_lockCount = 0;
+ m_bTrace = false;
+#endif
+}
+
+CThreadMutex::~CThreadMutex()
+{
+ DeleteCriticalSection((CRITICAL_SECTION *)&m_CriticalSection);
+}
+#endif // !POSIX
+
+#ifdef IS_WINDOWS_PC
+typedef BOOL (WINAPI*TryEnterCriticalSectionFunc_t)(LPCRITICAL_SECTION);
+static CDynamicFunction<TryEnterCriticalSectionFunc_t> DynTryEnterCriticalSection( "Kernel32.dll", "TryEnterCriticalSection" );
+#elif defined( _X360 )
+#define DynTryEnterCriticalSection TryEnterCriticalSection
+#endif
+
+bool CThreadMutex::TryLock()
+{
+#if defined( MSVC )
+#ifdef THREAD_MUTEX_TRACING_ENABLED
+ uint thisThreadID = ThreadGetCurrentId();
+ if ( m_bTrace && m_currentOwnerID && ( m_currentOwnerID != thisThreadID ) )
+ Msg( "Thread %u about to try-wait for lock %x owned by %u\n", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID );
+#endif
+ if ( DynTryEnterCriticalSection != NULL )
+ {
+ if ( (*DynTryEnterCriticalSection )( (CRITICAL_SECTION *)&m_CriticalSection ) != FALSE )
+ {
+#ifdef THREAD_MUTEX_TRACING_ENABLED
+ if (m_lockCount == 0)
+ {
+ // we now own it for the first time. Set owner information
+ m_currentOwnerID = thisThreadID;
+ if ( m_bTrace )
+ Msg( "Thread %u now owns lock 0x%x\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection );
+ }
+ m_lockCount++;
+#endif
+ return true;
+ }
+ return false;
+ }
+ Lock();
+ return true;
+#elif defined( _PS3 )
+
+#ifndef NO_THREAD_SYNC
+ if ( sys_mutex_trylock( m_Mutex ) == CELL_OK )
+#endif
+
+ return true;
+
+ return false; // ?? moved from EA code
+
+#elif defined( POSIX )
+ return pthread_mutex_trylock( &m_Mutex ) == 0;
+#else
+#error "Implement me!"
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//
+// CThreadFastMutex
+//
+//-----------------------------------------------------------------------------
+
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+// This is meant to be used in combination with breakpoints and in-debugee, so we turn the optimizer off
+#pragma optimize( "", off )
+CThreadFastMutex *g_pIgnoredMutexes[256]; // Ignore noisy non-problem mutex. Probably could be an array. Right now needed only for sound thread
+float g_MutexTimingTolerance = 5;
+bool g_bMutexTimingOutput;
+
+void TrapMutexTimings( uint32 probableBlocker, uint32 thisThread, volatile CThreadFastMutex *pMutex, CFastTimer &spikeTimer, CAverageCycleCounter &sleepTimer )
+{
+ spikeTimer.End();
+ if ( spikeTimer.GetDuration().GetMillisecondsF() > g_MutexTimingTolerance )
+ {
+ bool bIgnore = false;
+ for ( int j = 0; j < ARRAYSIZE( g_pIgnoredMutexes ) && g_pIgnoredMutexes[j]; j++ )
+ {
+ if ( g_pIgnoredMutexes[j] == pMutex )
+ {
+ bIgnore = true;
+ break;
+ }
+ }
+
+ if ( !bIgnore && spikeTimer.GetDuration().GetMillisecondsF() < 100 )
+ {
+ volatile float FastMutexDuration = spikeTimer.GetDuration().GetMillisecondsF();
+ volatile float average = sleepTimer.GetAverageMilliseconds();
+ volatile float peak = sleepTimer.GetPeakMilliseconds(); volatile int xx = 6;
+ if ( g_bMutexTimingOutput )
+ {
+ char szBuf[256];
+ Msg( "M (%.8x): [%.8x <-- %.8x] (%f,%f,%f)\n", pMutex, probableBlocker, thisThread, FastMutexDuration, average, peak );
+ }
+ }
+ }
+}
+
+#else
+#define TrapMutexTimings( a, b, c, d, e ) ((void)0)
+#endif
+
+//-------------------------------------
+
+#define THREAD_SPIN (8*1024)
+
+void CThreadFastMutex::Lock( const uint32 threadId, unsigned nSpinSleepTime ) volatile
+{
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+ CAverageCycleCounter sleepTimer;
+ CFastTimer spikeTimer;
+ uint32 currentOwner = m_ownerID;
+ spikeTimer.Start();
+ sleepTimer.Init();
+#endif
+ int i;
+ if ( nSpinSleepTime != TT_INFINITE )
+ {
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+ CAverageTimeMarker marker( &sleepTimer );
+#endif
+ ThreadSleep( 0 );
+ }
+ }
+
+#ifdef _WIN32
+ if ( !nSpinSleepTime && GetThreadPriority( GetCurrentThread() ) > THREAD_PRIORITY_NORMAL )
+ {
+ nSpinSleepTime = 1;
+ }
+#endif
+
+ if ( nSpinSleepTime )
+ {
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+ CAverageTimeMarker marker( &sleepTimer );
+#endif
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ }
+
+ for ( ;; )
+ {
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+ CAverageTimeMarker marker( &sleepTimer );
+#endif
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( nSpinSleepTime );
+ }
+ }
+ else
+ {
+ for ( ;; )
+ {
+ if ( TryLock( threadId ) )
+ {
+ TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer );
+ return;
+ }
+
+ ThreadPause();
+ }
+ }
+}
+
+#ifdef THREAD_FAST_MUTEX_TIMINGS
+#pragma optimize( "", on )
+#endif
+
+//-----------------------------------------------------------------------------
+//
+// CThreadRWLock
+//
+//-----------------------------------------------------------------------------
+
+void CThreadRWLock::WaitForRead()
+{
+ m_nPendingReaders++;
+
+ do
+ {
+ m_mutex.Unlock();
+ m_CanRead.Wait();
+ m_mutex.Lock();
+ }
+ while (m_nWriters);
+
+ m_nPendingReaders--;
+}
+
+
+void CThreadRWLock::LockForWrite()
+{
+ m_mutex.Lock();
+ bool bWait = ( m_nWriters != 0 || m_nActiveReaders != 0 );
+ m_nWriters++;
+ m_CanRead.Reset();
+ m_mutex.Unlock();
+
+ if ( bWait )
+ {
+ m_CanWrite.Wait();
+ }
+}
+
+void CThreadRWLock::UnlockWrite()
+{
+ m_mutex.Lock();
+ m_nWriters--;
+ if ( m_nWriters == 0)
+ {
+ if ( m_nPendingReaders )
+ {
+ m_CanRead.Set();
+ }
+ }
+ else
+ {
+ m_CanWrite.Set();
+ }
+ m_mutex.Unlock();
+}
+
+//-----------------------------------------------------------------------------
+//
+// CThreadSpinRWLock
+//
+//-----------------------------------------------------------------------------
+#ifndef OLD_SPINRWLOCK
+
+void CThreadSpinRWLock::SpinLockForWrite()
+{
+ int i;
+
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLockForWrite_UnforcedInline() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 1 );
+ }
+}
+
+void CThreadSpinRWLock::SpinLockForRead()
+{
+ int i;
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForRead_UnforcedInline() )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForRead_UnforcedInline() )
+ {
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if ( TryLockForRead_UnforcedInline() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLockForRead_UnforcedInline() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 1 );
+ }
+}
+
+#else
+/* (commented out to reduce distraction in colorized editor, remove entirely when new implementation settles)
+void CThreadSpinRWLock::SpinLockForWrite( const uint32 threadId )
+{
+ int i;
+
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLockForWrite( threadId ) )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 1 );
+ }
+}
+
+void CThreadSpinRWLock::LockForRead()
+{
+ int i;
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+ ThreadPause();
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+ ThreadPause();
+ if ( i % 1024 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 0 );
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if ( TryLockForRead() )
+ {
+ return;
+ }
+
+ ThreadPause();
+ ThreadSleep( 1 );
+ }
+}
+
+void CThreadSpinRWLock::UnlockRead()
+{
+ int i;
+
+ Assert( m_lockInfo.m_nReaders > 0 && m_lockInfo.m_writerId == 0 );
+
+ //uint32 nLockInfoReaders = m_lockInfo.m_nReaders;
+ LockInfo_t oldValue;
+ LockInfo_t newValue;
+
+ if( IsX360() )
+ {
+ // this is the code equivalent to original code (see below) that doesn't cause LHS on Xbox360
+ // WARNING: This code assumes BIG Endian CPU
+ oldValue.m_i64 = uint32( m_lockInfo.m_nReaders );
+ newValue.m_i64 = oldValue.m_i64 - 1; // NOTE: when we have -1 (or 0xFFFFFFFF) readers, this will result in non-equivalent code
+ }
+ else
+ {
+ // this is the original code that worked here for a while
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ oldValue.m_writerId = 0;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ newValue.m_writerId = 0;
+ }
+ ThreadMemoryBarrier();
+ if( AssignIf( newValue, oldValue ) )
+ return;
+
+ ThreadPause();
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if( AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ }
+
+ for ( i = THREAD_SPIN; i != 0; --i )
+ {
+ if( AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ if ( i % 512 == 0 )
+ {
+ ThreadSleep( 0 );
+ }
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ }
+
+ for ( i = THREAD_SPIN * 4; i != 0; --i )
+ {
+ if( AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ ThreadSleep( 0 );
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ }
+
+ for ( ;; ) // coded as for instead of while to make easy to breakpoint success
+ {
+ if( AssignIf( newValue, oldValue ) )
+ return;
+ ThreadPause();
+ ThreadSleep( 1 );
+ oldValue.m_nReaders = m_lockInfo.m_nReaders;
+ newValue.m_nReaders = oldValue.m_nReaders - 1;
+ }
+}
+
+void CThreadSpinRWLock::UnlockWrite()
+{
+ Assert( m_lockInfo.m_writerId == ThreadGetCurrentId() && m_lockInfo.m_nReaders == 0 );
+ static const LockInfo_t newValue = { { 0, 0 } };
+ ThreadMemoryBarrier();
+ ThreadInterlockedExchange64( (int64 *)&m_lockInfo, *((int64 *)&newValue) );
+ m_nWriters--;
+}
+*/
+#endif
+
+#if defined( _PS3 )
+// All CThread code is inline in the header for PS3
+
+// This function is implemented here rather than the header because g_pCurThread resolves to GetCurThread() on PS3
+// and we don't want to create a dependency on the ELF stub for everyone who includes the header.
+PLATFORM_INTERFACE CThread *GetCurThreadPS3()
+{
+ return (CThread*)g_pCurThread;
+}
+
+PLATFORM_INTERFACE void SetCurThreadPS3( CThread *pThread )
+{
+ g_pCurThread = pThread;
+}
+#else
+// The CThread implementation needs to be inlined for performance on the PS3 - It makes a difference of more than 1ms/frame
+// for other platforms, we include the .inl in the .cpp file where it existed before
+#include "../public/tier0/threadtools.inl"
+#endif
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CWorkerThread::CWorkerThread()
+: m_EventSend(true), // must be manual-reset for PeekCall()
+ m_EventComplete(true), // must be manual-reset to handle multiple wait with thread properly
+ m_Param(0),
+ m_ReturnVal(0)
+{
+}
+
+//---------------------------------------------------------
+
+int CWorkerThread::CallWorker(unsigned dw, unsigned timeout, bool fBoostWorkerPriorityToMaster)
+{
+ return Call(dw, timeout, fBoostWorkerPriorityToMaster);
+}
+
+//---------------------------------------------------------
+
+int CWorkerThread::CallMaster(unsigned dw, unsigned timeout)
+{
+ return Call(dw, timeout, false);
+}
+
+//---------------------------------------------------------
+
+CThreadEvent &CWorkerThread::GetCallHandle()
+{
+ return m_EventSend;
+}
+
+//---------------------------------------------------------
+
+unsigned CWorkerThread::GetCallParam() const
+{
+ return m_Param;
+}
+
+//---------------------------------------------------------
+
+int CWorkerThread::BoostPriority()
+{
+ int iInitialPriority = GetPriority();
+
+#ifdef WIN32
+ const int iNewPriority = ThreadGetPriority( GetThreadHandle() );
+ if (iNewPriority > iInitialPriority)
+ ThreadSetPriority( GetThreadHandle(), iNewPriority);
+#elif !defined( _PS3 )
+ const int iNewPriority = ThreadGetPriority( (ThreadHandle_t)GetThreadID() );
+ if (iNewPriority > iInitialPriority)
+ ThreadSetPriority( (ThreadHandle_t)GetThreadID(), iNewPriority);
+#endif
+
+ return iInitialPriority;
+}
+
+//---------------------------------------------------------
+static uint32 DefaultWaitFunc( uint32 nHandles, CThreadEvent** ppHandles, int bWaitAll, uint32 timeout )
+{
+ return CThreadEvent::WaitForMultiple( nHandles, ppHandles, bWaitAll!=0, timeout ) ;
+}
+
+
+int CWorkerThread::Call(unsigned dwParam, unsigned timeout, bool fBoostPriority, WaitFunc_t waitFunc)
+{
+ AssertMsg(!m_EventSend.Check(), "Cannot perform call if there's an existing call pending" );
+
+ AUTO_LOCK( m_Lock );
+
+ if (!IsAlive())
+ return WTCR_FAIL;
+
+ int iInitialPriority = 0;
+ if (fBoostPriority)
+ {
+ iInitialPriority = BoostPriority();
+ }
+
+ // set the parameter, signal the worker thread, wait for the completion to be signaled
+ m_Param = dwParam;
+
+ m_EventComplete.Reset();
+ m_EventSend.Set();
+
+ WaitForReply( timeout, waitFunc );
+
+ if (fBoostPriority)
+ SetPriority(iInitialPriority);
+
+ return m_ReturnVal;
+}
+
+//---------------------------------------------------------
+//
+// Wait for a request from the client
+//
+//---------------------------------------------------------
+int CWorkerThread::WaitForReply( unsigned timeout )
+{
+ return WaitForReply( timeout, NULL );
+}
+
+int CWorkerThread::WaitForReply( unsigned timeout, WaitFunc_t pfnWait )
+{
+ if (!pfnWait)
+ {
+ pfnWait = &DefaultWaitFunc;
+ }
+
+ CThreadEvent *waits[] =
+ {
+ &m_EventComplete,
+ &m_ExitEvent
+ };
+
+ unsigned result;
+ bool bInDebugger = Plat_IsInDebugSession();
+
+ uint32 dwActualTimeout = ( (timeout==TT_INFINITE) ? 30000 : timeout );
+
+ do
+ {
+#ifdef WIN32
+ // Make sure the thread handle hasn't been closed
+ if ( !GetThreadHandle() )
+ {
+ result = 1;
+ break;
+ }
+#endif
+
+ result = (*pfnWait)( ARRAYSIZE( waits ), waits, false, dwActualTimeout );
+
+ AssertMsg(timeout != TT_INFINITE || result != TW_TIMEOUT, "Possible hung thread, call to thread timed out");
+
+ } while ( bInDebugger && ( timeout == TT_INFINITE && result == TW_TIMEOUT ) );
+
+ if ( result != 0 )
+ {
+ if (result == TW_TIMEOUT)
+ {
+ m_ReturnVal = WTCR_TIMEOUT;
+ }
+ else if (result == 1)
+ {
+ DevMsg( 2, "Thread failed to respond, probably exited\n");
+ m_EventSend.Reset();
+ m_ReturnVal = WTCR_TIMEOUT;
+ }
+ else
+ {
+ m_EventSend.Reset();
+ m_ReturnVal = WTCR_THREAD_GONE;
+ }
+ }
+
+ return m_ReturnVal;
+}
+
+
+//---------------------------------------------------------
+//
+// Wait for a request from the client
+//
+//---------------------------------------------------------
+
+bool CWorkerThread::WaitForCall(unsigned * pResult)
+{
+ return WaitForCall(TT_INFINITE, pResult);
+}
+
+//---------------------------------------------------------
+
+bool CWorkerThread::WaitForCall(unsigned dwTimeout, unsigned * pResult)
+{
+ bool returnVal = m_EventSend.Wait(dwTimeout);
+ if (pResult)
+ *pResult = m_Param;
+ return returnVal;
+}
+
+//---------------------------------------------------------
+//
+// is there a request?
+//
+
+bool CWorkerThread::PeekCall(unsigned * pParam)
+{
+ if (!m_EventSend.Check())
+ {
+ return false;
+ }
+ else
+ {
+ if (pParam)
+ {
+ *pParam = m_Param;
+ }
+ return true;
+ }
+}
+
+//---------------------------------------------------------
+//
+// Reply to the request
+//
+
+void CWorkerThread::Reply(unsigned dw)
+{
+ m_Param = 0;
+ m_ReturnVal = dw;
+
+ // The request is now complete so PeekCall() should fail from
+ // now on
+ //
+ // This event should be reset BEFORE we signal the client
+ m_EventSend.Reset();
+
+ // Tell the client we're finished
+ m_EventComplete.Set();
+}
+
+
+//-----------------------------------------------------------------------------
+
+
+#if defined( _PS3 )
+
+/*******************************************************************************
+* PS3 equivalent to Win32 function for setting events
+*******************************************************************************/
+BOOL SetEvent( CThreadEvent *pEvent )
+{
+ bool bRetVal = pEvent->Set();
+ if ( !bRetVal )
+ Assert(0);
+
+ return bRetVal;
+}
+
+/*******************************************************************************
+* PS3 equivalent to Win32 function for resetting events
+*******************************************************************************/
+BOOL ResetEvent( CThreadEvent *pEvent )
+{
+ return pEvent->Reset();
+}
+
+#define MAXIMUM_WAIT_OBJECTS 64
+
+/*******************************************************************************
+* Wait for a selection of events to terminate
+*******************************************************************************/
+DWORD WaitForMultipleObjects( DWORD nCount, CThreadEvent **lppHandles, BOOL bWaitAll, DWORD dwMilliseconds )
+{
+ //////////////////////////////////////////////////////////////
+#ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS
+ //////////////////////////////////////////////////////////////
+
+
+ // Support for a limited amount of events
+ if ( nCount >= MAXIMUM_WAIT_OBJECTS )
+ {
+ Assert(0);
+ return false;
+ }
+
+ bool bRunning = true;
+ unsigned int result = TW_FAILED;
+
+ // For bWaitAll
+ int numEvent = 0;
+ int eventComplete[ MAXIMUM_WAIT_OBJECTS ] = {0};
+
+ uint64_t timeDiffMS = 0;
+ uint64_t startTimeMS = Plat_MSTime();
+ uint64_t endTimeMS = 0;
+
+ while ( bRunning )
+ {
+ // Check for a timeout
+ if ( bRunning && ( dwMilliseconds != INFINITE ) && ( timeDiffMS > dwMilliseconds ) )
+ {
+ result = TW_TIMEOUT;
+ bRunning = false;
+ }
+
+ // Wait for all the events to be set
+ if ( bWaitAll )
+ {
+ for ( int event = 0; event < nCount; ++event )
+ {
+ if ( lppHandles[event]->Wait(1) )
+ {
+ // If an event is complete, mark it as complete in our list
+ if ( eventComplete[ event ] == 0 )
+ {
+ numEvent++;
+ eventComplete[ event ] = 1;
+ }
+ }
+ }
+
+ // If all the events have been set, terminate the function
+ if ( numEvent >= nCount )
+ {
+ result = WAIT_OBJECT_0;
+ bRunning = false;
+ }
+ }
+
+ // Wait for one event to be set
+ else
+ {
+ for ( int event = 0; event < nCount; ++event )
+ {
+ if ( lppHandles[event]->Wait(1) )
+ {
+ result = WAIT_OBJECT_0 + event;
+ bRunning = false;
+ break;
+ }
+ }
+ }
+
+ endTimeMS = Plat_MSTime();
+ timeDiffMS = endTimeMS - startTimeMS;
+ }
+
+ return result;
+
+
+
+ //////////////////////////////////////////////////////////////
+#else // NEW_WAIT_FOR_MULTIPLE_OBJECTS // (expected PS3 only)
+ //////////////////////////////////////////////////////////////
+#ifndef _PS3
+#error This code was written expecting to be run on PS3.
+#endif
+
+ // check if we have a wait objects semaphore
+ if (!gbWaitObjectsCreated)
+ {
+ sys_semaphore_attribute_t semAttr;
+ sys_semaphore_attribute_initialize(semAttr);
+ sys_semaphore_create(&gWaitObjectsSemaphore, &semAttr, 0, 0xFFFF);
+
+ gbWaitObjectsCreated = true;
+ }
+
+ // Support for a limited amount of events
+ if ( nCount >= MAXIMUM_WAIT_OBJECTS )
+ {
+ Assert(0);
+ return false;
+ }
+
+ unsigned int result = WAIT_FAILED;
+ int res = CELL_OK;
+ int event = -1;
+ int numEvent = 0;
+
+ // run through events registering this thread with each one
+ for (int i = 0; i < nCount; i++)
+ {
+ lppHandles[i]->RegisterWaitingThread(&gWaitObjectsSemaphore, i, &event);
+ }
+
+
+ // in the Source API, a timeOut of 0 means very short timeOut, not (as in the PS3 spec) an infinite timeout.
+ // TT_INFINITE is #defined to 2^31-1, which means "infinite timeout" on PC and "72 minutes, 35 seconds" on PS3.
+ // conversely, the code below (around deltaTime) expects to be able to compare against the timeout
+ // value given here, so we cannot just replace 0 with 1 and TT_INFINITE with 0.
+ // So, we replace 0 with 1, meaning "a very short time", and test for the special value TT_INFINITE
+ // at the moment of calling sys_semaphore_wait, where we replace it with the real "infinite timeout"
+ // value. It isn't safe to simply increase the declaration size of TT_INFINITE, because as you can
+ // see it is often assigned to uint32s.
+ // Also, Source timeouts are specified in milliseconds, and PS3 timeouts are in microseconds,
+ // so we need to multiply by one thousand.
+ uint32 timeOut = dwMilliseconds;
+ if ( timeOut == 0 )
+ {
+ timeOut = 1;
+ }
+ else if ( timeOut != TT_INFINITE )
+ {
+ timeOut *= 1000;
+ // note that it's impossible for dwMilliseconds * 1000
+ // to coincidentally equal TT_INFINITE since TT_INFINITE
+ // is not divisible by 1000.
+ COMPILE_TIME_ASSERT( TT_INFINITE % 1000 != 0 );
+ }
+
+ COMPILE_TIME_ASSERT( TT_INFINITE != 0 ); // The code here was written expecting (working around) that TT_INFINITE is
+ // MAXINT, so if you changed this number, please read the comment above and
+ // carefully examine the code here to make sure that timeouts still work
+ // correctly on PS3. Be aware that in many places in Source, a timeout of
+ // 0 has some special meaning other than "infinite timeout", so track those
+ // down too.
+
+
+ // Wait for all the events to be set
+ if ( bWaitAll )
+ {
+ while (numEvent < nCount)
+ {
+ uint64_t deltaTime = Timer_GetTimeUS();
+
+ res = sys_semaphore_wait(gWaitObjectsSemaphore, timeOut == TT_INFINITE ? 0 : timeOut );
+
+ deltaTime = Timer_GetTimeUS() - deltaTime;
+
+ if (res == ETIMEDOUT)
+ {
+ result = TW_TIMEOUT;
+ break;
+ }
+ else if (res == CELL_OK)
+ {
+ numEvent++;
+
+ if (deltaTime >= timeOut)
+ {
+ // note - if this is not truly a time out
+ // then it will be set to WAIT_OBJECT_0
+ // after this loop
+ result = TW_TIMEOUT;
+ break;
+ }
+ else
+ {
+ timeOut -= deltaTime;
+ }
+ }
+ else
+ {
+ result = TW_FAILED;
+ break;
+ }
+ }
+
+ if (numEvent >= nCount)
+ {
+ result = WAIT_OBJECT_0;
+ }
+ }
+ else // Wait for one event to be set
+ {
+ // no event fired yet, wait on semaphore
+ res = sys_semaphore_wait( gWaitObjectsSemaphore, timeOut == TT_INFINITE ? 0 : timeOut );
+
+ if (res == ETIMEDOUT)
+ {
+ result = TW_TIMEOUT;
+ }
+ else if (res == CELL_OK)
+ {
+ if ((event < 0) || (event >= nCount))
+ {
+ DEBUG_ERROR("Bad event\n");
+ }
+
+ result = WAIT_OBJECT_0 + event;
+ }
+ }
+
+ // run through events unregistering this thread, for benefit
+ // of those events that did not fire, or fired before semaphore
+ // was registered
+ for (int i = 0; i < nCount; i++)
+ {
+ lppHandles[i]->UnregisterWaitingThread(&gWaitObjectsSemaphore);
+ }
+
+ // reset semaphore
+ while (sys_semaphore_trywait(gWaitObjectsSemaphore) != EBUSY);
+
+ return result;
+
+
+ //////////////////////////////////////////////////////////////
+#endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS
+ //////////////////////////////////////////////////////////////
+}
+
+#endif
diff --git a/external/vpc/tier0/tier0_strtools.cpp b/external/vpc/tier0/tier0_strtools.cpp
new file mode 100644
index 0000000..a651f26
--- /dev/null
+++ b/external/vpc/tier0/tier0_strtools.cpp
@@ -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;
+ }
+ }
+ }
+}
diff --git a/external/vpc/tier0/tier0_strtools.h b/external/vpc/tier0/tier0_strtools.h
new file mode 100644
index 0000000..d65e789
--- /dev/null
+++ b/external/vpc/tier0/tier0_strtools.h
@@ -0,0 +1,3 @@
+//========= Copyright � Valve Corporation, All rights reserved. ============//
+
+extern "C" int V_tier0_stricmp(const char *s1, const char *s2 ); \ No newline at end of file
diff --git a/external/vpc/tier0/valobject.cpp b/external/vpc/tier0/valobject.cpp
new file mode 100644
index 0000000..caf4283
--- /dev/null
+++ b/external/vpc/tier0/valobject.cpp
@@ -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 \ No newline at end of file
diff --git a/external/vpc/tier0/vprof.cpp b/external/vpc/tier0/vprof.cpp
new file mode 100644
index 0000000..cf6d7ba
--- /dev/null
+++ b/external/vpc/tier0/vprof.cpp
@@ -0,0 +1,1752 @@
+//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
+//
+// Purpose: Real-Time Hierarchical Profiling
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "pch_tier0.h"
+
+#include "tier0/memalloc.h"
+#include "tier0/valve_off.h"
+
+#if defined(_WIN32) && !defined(_X360)
+#define WIN_32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include <assert.h>
+
+#ifdef _WIN32
+#pragma warning(disable:4073)
+#pragma init_seg( lib )
+#endif
+
+#pragma warning(push, 1)
+#pragma warning(disable:4786)
+#pragma warning(disable:4530)
+#include <map>
+#include <vector>
+#include <algorithm>
+#pragma warning(pop)
+
+#include "tier0/valve_on.h"
+#include "tier0/vprof.h"
+#include "tier0/l2cache.h"
+#include "strtools.h"
+
+
+#ifdef _X360
+
+#include "xbox/xbox_console.h"
+
+#elif defined(_PS3)
+#include "ps3/ps3_console.h"
+
+#else // NOT _X360:
+
+#include "tier0/memdbgon.h"
+
+#endif
+
+// NOTE: Explicitly and intentionally using STL in here to not generate any
+// cyclical dependencies between the low-level debug library and the higher
+// level data structures (toml 01-27-03)
+using namespace std;
+
+#ifdef VPROF_ENABLED
+
+
+#if defined(_X360) && !defined(_CERT) // enable PIX CPU trace:
+#include "tracerecording.h"
+#pragma comment( lib, "tracerecording.lib" )
+#pragma comment( lib, "xbdm.lib" )
+#endif
+
+//-----------------------------------------------------------------------------
+bool g_VProfSignalSpike;
+
+//-----------------------------------------------------------------------------
+
+CVProfile g_VProfCurrentProfile;
+
+int CVProfNode::s_iCurrentUniqueNodeID = 0;
+
+
+
+CVProfNode::~CVProfNode()
+{
+#if !defined( _WIN32 ) && !defined( POSIX )
+ delete m_pChild;
+ delete m_pSibling;
+#endif
+}
+
+CVProfNode *CVProfNode::GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, int budgetFlags )
+{
+ // Try to find this sub node
+ CVProfNode * child = m_pChild;
+ while ( child )
+ {
+ if ( child->m_pszName == pszName )
+ {
+ return child;
+ }
+ child = child->m_pSibling;
+ }
+
+ // We didn't find it, so add it
+ CVProfNode * node = new CVProfNode( pszName, detailLevel, this, pBudgetGroupName, budgetFlags );
+ node->m_pSibling = m_pChild;
+ m_pChild = node;
+ return node;
+}
+
+CVProfNode *CVProfNode::GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName )
+{
+ return GetSubNode( pszName, detailLevel, pBudgetGroupName, BUDGETFLAG_OTHER );
+}
+
+
+//-------------------------------------
+
+void CVProfNode::EnterScope()
+{
+ m_nCurFrameCalls++;
+ if ( m_nRecursions++ == 0 )
+ {
+ m_Timer.Start();
+#ifndef _X360
+ if ( g_VProfCurrentProfile.UsePME() )
+ {
+ m_L2Cache.Start();
+ }
+#else // 360 code:
+ if ( g_VProfCurrentProfile.UsePME() || ((m_iBitFlags & kRecordL2) != 0) )
+ {
+ m_PMCData.Start();
+ }
+
+ if ( (m_iBitFlags & kCPUTrace) != 0)
+ {
+ // this node is to be recorded. Which recording mode are we in?
+ switch ( g_VProfCurrentProfile.GetCPUTraceMode() )
+ {
+ case CVProfile::kFirstHitNode:
+ case CVProfile::kAllNodesInFrame_Recording:
+ case CVProfile::kAllNodesInFrame_RecordingMultiFrame:
+ // we are presently recording.
+ if ( !XTraceStartRecording( g_VProfCurrentProfile.GetCPUTraceFilename() ) )
+ {
+ Msg( "XTraceStartRecording failed, error code %d\n", GetLastError() );
+ }
+
+ default:
+ // no default.
+ break;
+ }
+
+ }
+
+#endif
+
+#ifdef VPROF_VTUNE_GROUP
+ g_VProfCurrentProfile.PushGroup( m_BudgetGroupID );
+#endif
+ }
+}
+
+//-------------------------------------
+
+bool CVProfNode::ExitScope()
+{
+ if ( --m_nRecursions == 0 && m_nCurFrameCalls != 0 )
+ {
+ m_Timer.End();
+ m_CurFrameTime += m_Timer.GetDuration();
+#ifndef _X360
+ if ( g_VProfCurrentProfile.UsePME() )
+ {
+ m_L2Cache.End();
+ m_iCurL2CacheMiss += m_L2Cache.GetL2CacheMisses();
+ }
+#else // 360 code:
+ if ( g_VProfCurrentProfile.UsePME() || ((m_iBitFlags & kRecordL2) != 0) )
+ {
+ m_PMCData.End();
+ m_iCurL2CacheMiss += m_PMCData.GetL2CacheMisses();
+ m_iCurLoadHitStores += m_PMCData.GetLHS();
+ }
+
+ if ( (m_iBitFlags & kCPUTrace) != 0 )
+ {
+ // this node is enabled to be recorded. What mode are we in?
+ switch ( g_VProfCurrentProfile.GetCPUTraceMode() )
+ {
+ case CVProfile::kFirstHitNode:
+ {
+ // one-off recording. stop now.
+ if ( XTraceStopRecording() )
+ {
+ Msg( "CPU trace finished.\n" );
+ if ( g_VProfCurrentProfile.TraceCompleteEvent() )
+ {
+ // signal VXConsole that trace is completed
+ XBX_rTraceComplete();
+ }
+ }
+ // don't trace again next frame, overwriting the file.
+ g_VProfCurrentProfile.SetCPUTraceEnabled( CVProfile::kDisabled );
+ break;
+ }
+
+ case CVProfile::kAllNodesInFrame_Recording:
+ case CVProfile::kAllNodesInFrame_RecordingMultiFrame:
+ {
+ // one-off recording. stop now.
+ if ( XTraceStopRecording() )
+ {
+ if ( g_VProfCurrentProfile.GetCPUTraceMode() == CVProfile::kAllNodesInFrame_RecordingMultiFrame )
+ {
+ Msg( "%.3f msec in %s\n", m_CurFrameTime.GetMillisecondsF(), g_VProfCurrentProfile.GetCPUTraceFilename() );
+ }
+ else
+ {
+ Msg( "CPU trace finished.\n" );
+ }
+ }
+
+ // Spew time info for file to allow figuring it out later
+ g_VProfCurrentProfile.LatchMultiFrame( m_CurFrameTime.GetLongCycles() );
+
+#if 0 // This doesn't want to work on the xbox360-- MoveFile not available or file still being put down to disk?
+ char suffix[ 32 ];
+ _snprintf( suffix, sizeof( suffix ), "_%.3f_msecs", flMsecs );
+
+ char fn[ 512 ];
+
+ strncpy( fn, g_VProfCurrentProfile.GetCPUTraceFilename(), sizeof( fn ) );
+
+ char *p = strrchr( fn, '.' );
+ if ( *p )
+ {
+ *p = 0;
+ }
+ strncat( fn, suffix, sizeof( fn ) );
+ strncat( fn, ".pix2", sizeof( fn ) );
+
+ BOOL bSuccess = MoveFile( g_VProfCurrentProfile.GetCPUTraceFilename(), fn );
+ if ( !bSuccess )
+ {
+ DWORD eCode = GetLastError();
+ Msg( "Error %d\n", eCode );
+ }
+#endif
+
+ // we're still recording until the frame is done.
+ // but, increment the index.
+ g_VProfCurrentProfile.IncrementMultiTraceIndex();
+ break;
+ }
+
+ }
+
+ // g_VProfCurrentProfile.IsCPUTraceEnabled() &&
+
+
+ }
+
+#endif
+
+#ifdef VPROF_VTUNE_GROUP
+ g_VProfCurrentProfile.PopGroup();
+#endif
+ }
+ return ( m_nRecursions == 0 );
+}
+
+//-------------------------------------
+
+void CVProfNode::Pause()
+{
+ if ( m_nRecursions > 0 )
+ {
+ m_Timer.End();
+ m_CurFrameTime += m_Timer.GetDuration();
+
+#ifndef _X360
+ if ( g_VProfCurrentProfile.UsePME() )
+ {
+ m_L2Cache.End();
+ m_iCurL2CacheMiss += m_L2Cache.GetL2CacheMisses();
+ }
+#else // 360 code:
+ if ( g_VProfCurrentProfile.UsePME() || ((m_iBitFlags & kRecordL2) != 0) )
+ {
+ m_PMCData.End();
+ m_iCurL2CacheMiss += m_PMCData.GetL2CacheMisses();
+ m_iCurLoadHitStores += m_PMCData.GetLHS();
+ }
+#endif
+ }
+ if ( m_pChild )
+ {
+ m_pChild->Pause();
+ }
+ if ( m_pSibling )
+ {
+ m_pSibling->Pause();
+ }
+}
+
+//-------------------------------------
+
+void CVProfNode::Resume()
+{
+ if ( m_nRecursions > 0 )
+ {
+ m_Timer.Start();
+
+#ifndef _X360
+ if ( g_VProfCurrentProfile.UsePME() )
+ {
+ m_L2Cache.Start();
+ }
+#else
+ if ( g_VProfCurrentProfile.UsePME() || ((m_iBitFlags & kRecordL2) != 0) )
+ {
+ m_PMCData.Start();
+ }
+#endif
+ }
+ if ( m_pChild )
+ {
+ m_pChild->Resume();
+ }
+ if ( m_pSibling )
+ {
+ m_pSibling->Resume();
+ }
+}
+
+//-------------------------------------
+
+void CVProfNode::Reset()
+{
+ m_nPrevFrameCalls = 0;
+ m_PrevFrameTime.Init();
+
+ m_nCurFrameCalls = 0;
+ m_CurFrameTime.Init();
+
+ m_nTotalCalls = 0;
+ m_TotalTime.Init();
+
+ m_PeakTime.Init();
+
+ m_iPrevL2CacheMiss = 0;
+ m_iCurL2CacheMiss = 0;
+ m_iTotalL2CacheMiss = 0;
+
+#ifdef _X360
+ m_iPrevLoadHitStores = 0;
+ m_iCurLoadHitStores = 0;
+ m_iTotalLoadHitStores = 0;
+#endif
+
+ if ( m_pChild )
+ {
+ m_pChild->Reset();
+ }
+ if ( m_pSibling )
+ {
+ m_pSibling->Reset();
+ }
+}
+
+
+//-------------------------------------
+
+void CVProfNode::MarkFrame()
+{
+ m_nPrevFrameCalls = m_nCurFrameCalls;
+ m_PrevFrameTime = m_CurFrameTime;
+ m_iPrevL2CacheMiss = m_iCurL2CacheMiss;
+#ifdef _X360
+ m_iPrevLoadHitStores = m_iCurLoadHitStores;
+#endif
+ m_nTotalCalls += m_nCurFrameCalls;
+ m_TotalTime += m_CurFrameTime;
+
+ if ( m_PeakTime.IsLessThan( m_CurFrameTime ) )
+ {
+ m_PeakTime = m_CurFrameTime;
+ }
+
+ m_CurFrameTime.Init();
+ m_nCurFrameCalls = 0;
+ m_iTotalL2CacheMiss += m_iCurL2CacheMiss;
+ m_iCurL2CacheMiss = 0;
+#ifdef _X360
+ m_iTotalLoadHitStores += m_iCurLoadHitStores;
+ m_iCurLoadHitStores = 0;
+#endif
+ if ( m_pChild )
+ {
+ m_pChild->MarkFrame();
+ }
+ if ( m_pSibling )
+ {
+ m_pSibling->MarkFrame();
+ }
+}
+
+//-------------------------------------
+
+void CVProfNode::ResetPeak()
+{
+ m_PeakTime.Init();
+
+ if ( m_pChild )
+ {
+ m_pChild->ResetPeak();
+ }
+ if ( m_pSibling )
+ {
+ m_pSibling->ResetPeak();
+ }
+}
+
+
+void CVProfNode::SetCurFrameTime( unsigned long milliseconds )
+{
+ m_CurFrameTime.Init( (float)milliseconds );
+}
+#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 CVProfNode::Validate( CValidator &validator, tchar *pchName )
+{
+ validator.Push( _T("CVProfNode"), this, pchName );
+
+ m_L2Cache.Validate( validator, _T("m_L2Cache") );
+
+ if ( m_pSibling )
+ m_pSibling->Validate( validator, _T("m_pSibling") );
+ if ( m_pChild )
+ m_pChild->Validate( validator, _T("m_pChild") );
+
+ validator.Pop( );
+}
+#endif // DBGFLAG_VALIDATE
+
+
+
+//-----------------------------------------------------------------------------
+
+struct TimeSums_t
+{
+ const tchar *pszProfileScope;
+ unsigned calls;
+ double time;
+ double timeLessChildren;
+ double peak;
+};
+
+static bool TimeCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
+{
+ return ( lhs.time > rhs.time );
+}
+
+static bool TimeLessChildrenCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
+{
+ return ( lhs.timeLessChildren > rhs.timeLessChildren );
+}
+
+static bool PeakCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
+{
+ return ( lhs.peak > rhs.peak );
+}
+
+static bool AverageTimeCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
+{
+ double avgLhs = ( lhs.calls ) ? lhs.time / (double)lhs.calls : 0.0;
+ double avgRhs = ( rhs.calls ) ? rhs.time / (double)rhs.calls : 0.0;
+ return ( avgLhs > avgRhs );
+}
+
+static bool AverageTimeLessChildrenCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
+{
+ double avgLhs = ( lhs.calls ) ? lhs.timeLessChildren / (double)lhs.calls : 0.0;
+ double avgRhs = ( rhs.calls ) ? rhs.timeLessChildren / (double)rhs.calls : 0.0;
+ return ( avgLhs > avgRhs );
+
+}
+static bool PeakOverAverageCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
+{
+ double avgLhs = ( lhs.calls ) ? lhs.time / (double)lhs.calls : 0.0;
+ double avgRhs = ( rhs.calls ) ? rhs.time / (double)rhs.calls : 0.0;
+
+ double lhsPoA = ( avgLhs != 0 ) ? lhs.peak / avgLhs : 0.0;
+ double rhsPoA = ( avgRhs != 0 ) ? rhs.peak / avgRhs : 0.0;
+
+ return ( lhsPoA > rhsPoA );
+}
+
+map<CVProfNode *, double> g_TimesLessChildren;
+int g_TotalFrames;
+map<const tchar *, uintp> g_TimeSumsMap;
+vector<TimeSums_t> g_TimeSums;
+CVProfNode * g_pStartNode;
+const tchar * g_pszSumNode;
+
+//-------------------------------------
+
+void CVProfile::SumTimes( CVProfNode *pNode, int budgetGroupID )
+{
+ if ( !pNode )
+ return; // this generally only happens on a failed FindNode()
+
+ bool bSetStartNode;
+ if ( !g_pStartNode && _tcscmp( pNode->GetName(), g_pszSumNode ) == 0 )
+ {
+ g_pStartNode = pNode;
+ bSetStartNode = true;
+ }
+ else
+ bSetStartNode = false;
+
+ if ( GetRoot() != pNode )
+ {
+ if ( g_pStartNode && pNode->GetTotalCalls() > 0 && ( budgetGroupID == -1 || pNode->GetBudgetGroupID() == budgetGroupID ) )
+ {
+ double timeLessChildren = pNode->GetTotalTimeLessChildren();
+
+ g_TimesLessChildren.insert( make_pair( pNode, timeLessChildren ) );
+
+ map<const tchar *, uintp>::iterator iter;
+ iter = g_TimeSumsMap.find( pNode->GetName() ); // intenionally using address of string rather than string compare (toml 01-27-03)
+ if ( iter == g_TimeSumsMap.end() )
+ {
+ TimeSums_t timeSums = { pNode->GetName(), pNode->GetTotalCalls(), pNode->GetTotalTime(), timeLessChildren, pNode->GetPeakTime() };
+ g_TimeSumsMap.insert( make_pair( pNode->GetName(), g_TimeSums.size() ) );
+ g_TimeSums.push_back( timeSums );
+ }
+ else
+ {
+ TimeSums_t &timeSums = g_TimeSums[iter->second];
+ timeSums.calls += pNode->GetTotalCalls();
+ timeSums.time += pNode->GetTotalTime();
+ timeSums.timeLessChildren += timeLessChildren;
+ if ( pNode->GetPeakTime() > timeSums.peak )
+ timeSums.peak = pNode->GetPeakTime();
+ }
+ }
+
+ if( ( !g_pStartNode || pNode != g_pStartNode ) && pNode->GetSibling() )
+ {
+ SumTimes( pNode->GetSibling(), budgetGroupID );
+ }
+ }
+
+ if( pNode->GetChild() )
+ {
+ SumTimes( pNode->GetChild(), budgetGroupID );
+ }
+
+ if ( bSetStartNode )
+ g_pStartNode = NULL;
+}
+
+//-------------------------------------
+
+CVProfNode *CVProfile::FindNode( CVProfNode *pStartNode, const tchar *pszNode )
+{
+ if ( _tcscmp( pStartNode->GetName(), pszNode ) != 0 )
+ {
+ CVProfNode *pFoundNode = NULL;
+ if ( pStartNode->GetSibling() )
+ {
+ pFoundNode = FindNode( pStartNode->GetSibling(), pszNode );
+ }
+
+ if ( !pFoundNode && pStartNode->GetChild() )
+ {
+ pFoundNode = FindNode( pStartNode->GetChild(), pszNode );
+ }
+
+ return pFoundNode;
+ }
+ return pStartNode;
+}
+
+//-------------------------------------
+#ifdef _X360
+
+void CVProfile::PMCDisableAllNodes(CVProfNode *pStartNode)
+{
+ if (pStartNode == NULL)
+ {
+ pStartNode = GetRoot();
+ }
+
+ pStartNode->EnableL2andLHS(false);
+
+ if ( pStartNode->GetSibling() )
+ {
+ PMCDisableAllNodes(pStartNode->GetSibling());
+ }
+
+ if ( pStartNode->GetChild() )
+ {
+ PMCDisableAllNodes(pStartNode->GetChild());
+ }
+}
+
+// recursively set l2/lhs recording state for a node and all children AND SIBLINGS
+static void PMCRecursiveL2Set(CVProfNode *pNode, bool enableState)
+{
+ if ( pNode )
+ {
+ pNode->EnableL2andLHS(enableState);
+ if ( pNode->GetSibling() )
+ {
+ PMCRecursiveL2Set( pNode->GetSibling(), enableState );
+ }
+ if ( pNode->GetChild() )
+ {
+ PMCRecursiveL2Set( pNode->GetChild(), enableState );
+ }
+ }
+}
+
+bool CVProfile::PMCEnableL2Upon(const tchar *pszNodeName, bool bRecursive)
+{
+ // PMCDisableAllNodes();
+ CVProfNode *pNode = FindNode( GetRoot(), pszNodeName );
+ if (pNode)
+ {
+ pNode->EnableL2andLHS(true);
+ if (bRecursive)
+ {
+ PMCRecursiveL2Set(pNode->GetChild(), true);
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool CVProfile::PMCDisableL2Upon(const tchar *pszNodeName, bool bRecursive)
+{
+ // PMCDisableAllNodes();
+ CVProfNode *pNode = FindNode( GetRoot(), pszNodeName );
+ if ( pNode )
+ {
+ pNode->EnableL2andLHS( false );
+ if ( bRecursive )
+ {
+ PMCRecursiveL2Set( pNode->GetChild(), false );
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+static void DumpEnabledPMCNodesInner(CVProfNode* pNode)
+{
+ if (!pNode)
+ return;
+
+ if (pNode->IsL2andLHSEnabled())
+ {
+ Msg( _T("\t%s\n"), pNode->GetName() );
+ }
+
+ // depth first printing clearer
+ if ( pNode->GetChild() )
+ {
+ DumpEnabledPMCNodesInner(pNode->GetChild());
+ }
+
+ if ( pNode->GetSibling() )
+ {
+ DumpEnabledPMCNodesInner(pNode->GetChild());
+ }
+}
+
+void CVProfile::DumpEnabledPMCNodes( void )
+{
+ Msg( _T("Nodes enabled for PMC counters:\n") );
+ CVProfNode *pNode = GetRoot();
+ DumpEnabledPMCNodesInner( pNode );
+
+ Msg( _T("(end)\n") );
+}
+
+CVProfNode *CVProfile::CPUTraceGetEnabledNode(CVProfNode *pStartNode)
+{
+ if (!pStartNode)
+ {
+ pStartNode = GetRoot();
+ }
+
+ if ( (pStartNode->m_iBitFlags & CVProfNode::kCPUTrace) != 0 )
+ {
+ return pStartNode;
+ }
+
+ if (pStartNode->GetSibling())
+ {
+ CVProfNode *retval = CPUTraceGetEnabledNode(pStartNode->GetSibling());
+ if (retval)
+ return retval;
+ }
+
+ if (pStartNode->GetChild())
+ {
+ CVProfNode *retval = CPUTraceGetEnabledNode(pStartNode->GetChild());
+ if (retval)
+ return retval;
+ }
+
+ return NULL;
+}
+
+const char *CVProfile::SetCPUTraceFilename( const char *filename )
+{
+ strncpy( m_CPUTraceFilename, filename, sizeof( m_CPUTraceFilename ) );
+ return GetCPUTraceFilename();
+}
+
+/// Returns a pointer to an internal static, so you don't need to
+/// make temporary char buffers for this to write into. What of it?
+/// You're not hanging on to that pointer. That would be foolish.
+const char *CVProfile::GetCPUTraceFilename()
+{
+ static char retBuf[256];
+
+ switch ( m_iCPUTraceEnabled )
+ {
+ case kAllNodesInFrame_WaitingForMark:
+ case kAllNodesInFrame_Recording:
+ _snprintf( retBuf, sizeof( retBuf ), "e:\\%.128s%.4d.pix2", m_CPUTraceFilename, m_iSuccessiveTraceIndex );
+ break;
+
+ case kAllNodesInFrame_WaitingForMarkMultiFrame:
+ case kAllNodesInFrame_RecordingMultiFrame:
+ _snprintf( retBuf, sizeof( retBuf ), "e:\\%.128s_%.4d_%.4d.pix2", m_CPUTraceFilename, m_nFrameCount, m_iSuccessiveTraceIndex );
+ break;
+
+ default:
+ _snprintf( retBuf, sizeof( retBuf ), "e:\\%.128s.pix2", m_CPUTraceFilename );
+ }
+
+ return retBuf;
+}
+
+bool CVProfile::TraceCompleteEvent( void )
+{
+ return m_bTraceCompleteEvent;
+}
+
+CVProfNode *CVProfile::CPUTraceEnableForNode(const tchar *pszNodeName)
+{
+ // disable whatever may be enabled already (we can only trace one node at a time)
+ CPUTraceDisableAllNodes();
+
+ CVProfNode *which = FindNode(GetRoot(), pszNodeName);
+ if (which)
+ {
+ which->m_iBitFlags |= CVProfNode::kCPUTrace;
+ return which;
+ }
+ else
+ return NULL;
+}
+
+void CVProfile::CPUTraceDisableAllNodes(CVProfNode *pStartNode)
+{
+ if (!pStartNode)
+ {
+ pStartNode = GetRoot();
+ }
+
+ pStartNode->m_iBitFlags &= ~CVProfNode::kCPUTrace;
+
+ if (pStartNode->GetSibling())
+ {
+ CPUTraceDisableAllNodes(pStartNode->GetSibling());
+ }
+
+ if (pStartNode->GetChild())
+ {
+ CPUTraceDisableAllNodes(pStartNode->GetChild());
+ }
+
+}
+
+#endif
+
+//-------------------------------------
+
+void CVProfile::SumTimes( const tchar *pszStartNode, int budgetGroupID )
+{
+ if ( GetRoot()->GetChild() )
+ {
+ if ( pszStartNode == NULL )
+ g_pStartNode = GetRoot();
+ else
+ g_pStartNode = NULL;
+
+ g_pszSumNode = pszStartNode;
+ SumTimes( GetRoot(), budgetGroupID );
+ g_pStartNode = NULL;
+ }
+
+}
+
+//-------------------------------------
+
+void CVProfile::DumpNodes( CVProfNode *pNode, int indent, bool bAverageAndCountOnly )
+{
+ if ( !pNode )
+ return; // this generally only happens on a failed FindNode()
+
+ bool fIsRoot = ( pNode == GetRoot() );
+
+ if ( fIsRoot || pNode == g_pStartNode )
+ {
+ if( bAverageAndCountOnly )
+ {
+ Msg( _T(" Avg Time/Frame (ms)\n") );
+ Msg( _T("[ func+child func ] Count\n") );
+ Msg( _T(" ---------- --------- --------\n") );
+ }
+ else
+ {
+ Msg( _T(" Sum (ms) Avg Time/Frame (ms) Avg Time/Call (ms)\n") );
+ Msg( _T("[ func+child func ] [ func+child func ] [ func+child func ] Count Peak\n") );
+ Msg( _T(" ---------- --------- ---------- ------ ---------- ------ -------- ------\n") );
+ }
+ }
+
+ if ( !fIsRoot )
+ {
+ map<CVProfNode *, double>::iterator iterTimeLessChildren = g_TimesLessChildren.find( pNode );
+
+ double dNodeTime = 0;
+ if(iterTimeLessChildren != g_TimesLessChildren.end())
+ dNodeTime = iterTimeLessChildren->second;
+
+ if( bAverageAndCountOnly )
+ {
+ Msg( _T(" %10.3f %9.2f %8d"),
+ ( pNode->GetTotalCalls() > 0 ) ? pNode->GetTotalTime() / (double)NumFramesSampled() : 0,
+ ( pNode->GetTotalCalls() > 0 ) ? dNodeTime / (double)NumFramesSampled() : 0,
+ pNode->GetTotalCalls() );
+ }
+ else
+ {
+ Msg( _T(" %10.3f %9.2f %10.3f %6.2f %10.3f %6.2f %8d %6.2f"),
+ pNode->GetTotalTime(), dNodeTime,
+ ( pNode->GetTotalCalls() > 0 ) ? pNode->GetTotalTime() / (double)NumFramesSampled() : 0,
+ ( pNode->GetTotalCalls() > 0 ) ? dNodeTime / (double)NumFramesSampled() : 0,
+ ( pNode->GetTotalCalls() > 0 ) ? pNode->GetTotalTime() / (double)pNode->GetTotalCalls() : 0,
+ ( pNode->GetTotalCalls() > 0 ) ? dNodeTime / (double)pNode->GetTotalCalls() : 0,
+ pNode->GetTotalCalls(), pNode->GetPeakTime() );
+ }
+
+ Msg( _T(" ") );
+ for ( int i = 1; i < indent; i++ )
+ {
+ Msg( _T("| ") );
+ }
+
+ Msg( _T("%s\n"), pNode->GetName() );
+ }
+
+ if( pNode->GetChild() )
+ {
+ DumpNodes( pNode->GetChild(), indent + 1, bAverageAndCountOnly );
+ }
+
+ if( !( fIsRoot || pNode == g_pStartNode ) && pNode->GetSibling() )
+ {
+ DumpNodes( pNode->GetSibling(), indent, bAverageAndCountOnly );
+ }
+}
+
+//-------------------------------------
+
+#if defined( VPROF_VXCONSOLE_EXISTS )
+static void CalcBudgetGroupTimes_Recursive( CVProfNode *pNode, unsigned int *groupTimes, int numGroups, float flScale )
+{
+ int groupID;
+ CVProfNode *nodePtr;
+
+ groupID = pNode->GetBudgetGroupID();
+ if ( groupID >= numGroups )
+ {
+ return;
+ }
+
+ groupTimes[groupID] += flScale*pNode->GetPrevTimeLessChildren();
+
+ nodePtr = pNode->GetSibling();
+ if ( nodePtr )
+ {
+ CalcBudgetGroupTimes_Recursive( nodePtr, groupTimes, numGroups, flScale );
+ }
+
+ nodePtr = pNode->GetChild();
+ if ( nodePtr )
+ {
+ CalcBudgetGroupTimes_Recursive( nodePtr, groupTimes, numGroups, flScale );
+ }
+}
+
+static void CalcBudgetGroupL2CacheMisses_Recursive( CVProfNode *pNode, unsigned int *groupTimes, int numGroups, float flScale )
+{
+ int groupID;
+ CVProfNode *nodePtr;
+
+ groupID = pNode->GetBudgetGroupID();
+ if ( groupID >= numGroups )
+ {
+ return;
+ }
+
+ groupTimes[groupID] += flScale*pNode->GetPrevL2CacheMissLessChildren();
+
+ nodePtr = pNode->GetSibling();
+ if ( nodePtr )
+ {
+ CalcBudgetGroupL2CacheMisses_Recursive( nodePtr, groupTimes, numGroups, flScale );
+ }
+
+ nodePtr = pNode->GetChild();
+ if ( nodePtr )
+ {
+ CalcBudgetGroupL2CacheMisses_Recursive( nodePtr, groupTimes, numGroups, flScale );
+ }
+}
+
+static void CalcBudgetGroupLHS_Recursive( CVProfNode *pNode, unsigned int *groupTimes, int numGroups, float flScale )
+{
+ int groupID;
+ CVProfNode *nodePtr;
+
+ groupID = pNode->GetBudgetGroupID();
+ if ( groupID >= numGroups )
+ {
+ return;
+ }
+
+ groupTimes[groupID] += flScale*pNode->GetPrevLoadHitStoreLessChildren();
+
+ nodePtr = pNode->GetSibling();
+ if ( nodePtr )
+ {
+ CalcBudgetGroupLHS_Recursive( nodePtr, groupTimes, numGroups, flScale );
+ }
+
+ nodePtr = pNode->GetChild();
+ if ( nodePtr )
+ {
+ CalcBudgetGroupLHS_Recursive( nodePtr, groupTimes, numGroups, flScale );
+ }
+}
+
+
+void CVProfile::VXConsoleReportMode( VXConsoleReportMode_t mode )
+{
+ m_ReportMode = mode;
+}
+
+void CVProfile::VXConsoleReportScale( VXConsoleReportMode_t mode, float flScale )
+{
+ m_pReportScale[mode] = flScale;
+}
+
+
+//-----------------------------------------------------------------------------
+// Send the all the counter attributes once to VXConsole at profiling start
+//-----------------------------------------------------------------------------
+void CVProfile::VXProfileStart()
+{
+ const char *names[XBX_MAX_PROFILE_COUNTERS];
+ COLORREF colors[XBX_MAX_PROFILE_COUNTERS];
+ int numGroups;
+ int counterGroup;
+ const char *pGroupName;
+ int i;
+ int r,g,b,a;
+
+ // vprof system must be running
+ if ( m_enabled <= 0 || !m_UpdateMode )
+ {
+ return;
+ }
+
+ if ( m_UpdateMode & VPROF_UPDATE_BUDGET )
+ {
+ // update budget profiling
+ numGroups = g_VProfCurrentProfile.GetNumBudgetGroups();
+ if ( numGroups > XBX_MAX_PROFILE_COUNTERS )
+ {
+ numGroups = XBX_MAX_PROFILE_COUNTERS;
+ }
+ for ( i=0; i<numGroups; i++ )
+ {
+ names[i] = g_VProfCurrentProfile.GetBudgetGroupName( i );
+ g_VProfCurrentProfile.GetBudgetGroupColor( i, r, g, b, a );
+ colors[i] = XMAKECOLOR( r, g, b );
+ }
+
+ // send all the profile attributes
+ XBX_rSetProfileAttributes( "cpu", numGroups, names, colors );
+ }
+
+ if ( m_UpdateMode & (VPROF_UPDATE_TEXTURE_GLOBAL|VPROF_UPDATE_TEXTURE_PERFRAME) )
+ {
+ // update texture profiling
+ numGroups = 0;
+ counterGroup = (m_UpdateMode & VPROF_UPDATE_TEXTURE_GLOBAL) ? COUNTER_GROUP_TEXTURE_GLOBAL : COUNTER_GROUP_TEXTURE_PER_FRAME;
+ for ( i=0; i<g_VProfCurrentProfile.GetNumCounters(); i++ )
+ {
+ if ( g_VProfCurrentProfile.GetCounterGroup( i ) == counterGroup )
+ {
+ // strip undesired prefix
+ pGroupName = g_VProfCurrentProfile.GetCounterName( i );
+ if ( !stricmp( pGroupName, "texgroup_frame_" ) )
+ {
+ pGroupName += 15;
+ }
+ else if ( !stricmp( pGroupName, "texgroup_global_" ) )
+ {
+ pGroupName += 16;
+ }
+ names[numGroups] = pGroupName;
+
+ g_VProfCurrentProfile.GetBudgetGroupColor( numGroups, r, g, b, a );
+ colors[numGroups] = XMAKECOLOR( r, g, b );
+
+ numGroups++;
+ if ( numGroups == XBX_MAX_PROFILE_COUNTERS )
+ {
+ break;
+ }
+ }
+ }
+
+ // send all the profile attributes
+ XBX_rSetProfileAttributes( "texture", numGroups, names, colors );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Send the counters to VXConsole
+//-----------------------------------------------------------------------------
+void CVProfile::VXProfileUpdate()
+{
+ int i;
+ int counterGroup;
+ int numGroups;
+ unsigned int groupData[XBX_MAX_PROFILE_COUNTERS];
+
+ // vprof system must be running
+ if ( m_enabled <= 0 || !m_UpdateMode )
+ {
+ return;
+ }
+
+ if ( m_UpdateMode & VPROF_UPDATE_BUDGET )
+ {
+ // send the cpu counters
+ numGroups = g_VProfCurrentProfile.GetNumBudgetGroups();
+ if ( numGroups > XBX_MAX_PROFILE_COUNTERS )
+ {
+ numGroups = XBX_MAX_PROFILE_COUNTERS;
+ }
+ memset( groupData, 0, numGroups * sizeof( unsigned int ) );
+
+ CVProfNode *pNode = g_VProfCurrentProfile.GetRoot();
+ if ( pNode && pNode->GetChild() )
+ {
+ switch ( m_ReportMode )
+ {
+ default:
+ case VXCONSOLE_REPORT_TIME:
+ CalcBudgetGroupTimes_Recursive( pNode->GetChild(), groupData, numGroups, m_pReportScale[VXCONSOLE_REPORT_TIME] );
+ break;
+
+ case VXCONSOLE_REPORT_L2CACHE_MISSES:
+ CalcBudgetGroupL2CacheMisses_Recursive( pNode->GetChild(), groupData, numGroups, m_pReportScale[VXCONSOLE_REPORT_L2CACHE_MISSES] );
+ break;
+
+ case VXCONSOLE_REPORT_LOAD_HIT_STORE:
+ CalcBudgetGroupLHS_Recursive( pNode->GetChild(), groupData, numGroups, m_pReportScale[VXCONSOLE_REPORT_LOAD_HIT_STORE] );
+ break;
+ }
+ }
+
+ XBX_rSetProfileData( "cpu", numGroups, groupData );
+ }
+
+ if ( m_UpdateMode & ( VPROF_UPDATE_TEXTURE_GLOBAL|VPROF_UPDATE_TEXTURE_PERFRAME ) )
+ {
+ // send the texture counters
+ numGroups = 0;
+ counterGroup = ( m_UpdateMode & VPROF_UPDATE_TEXTURE_GLOBAL ) ? COUNTER_GROUP_TEXTURE_GLOBAL : COUNTER_GROUP_TEXTURE_PER_FRAME;
+ for ( i = 0; i < g_VProfCurrentProfile.GetNumCounters(); i++ )
+ {
+ if ( g_VProfCurrentProfile.GetCounterGroup( i ) == counterGroup )
+ {
+ // get the size in bytes
+ groupData[numGroups++] = g_VProfCurrentProfile.GetCounterValue( i );
+ if ( numGroups == XBX_MAX_PROFILE_COUNTERS )
+ {
+ break;
+ }
+ }
+ }
+
+ XBX_rSetProfileData( "texture", numGroups, groupData );
+ }
+}
+
+void CVProfile::VXEnableUpdateMode( int event, bool bEnable )
+{
+ // enable or disable the updating of specified events
+ if ( bEnable )
+ {
+ m_UpdateMode |= event;
+ }
+ else
+ {
+ m_UpdateMode &= ~event;
+ }
+
+ // force a resend of possibly affected attributes
+ VXProfileStart();
+}
+
+#define MAX_VPROF_NODES_IN_LIST 4096
+static void VXBuildNodeList_r( CVProfNode *pNode, xVProfNodeItem_t *pNodeList, int *pNumNodes )
+{
+ if ( !pNode )
+ {
+ return;
+ }
+ if ( *pNumNodes >= MAX_VPROF_NODES_IN_LIST )
+ {
+ // list full
+ return;
+ }
+
+ // add to list
+ pNodeList[*pNumNodes].pName = (const char *)pNode->GetName();
+
+ pNodeList[*pNumNodes].pBudgetGroupName = g_VProfCurrentProfile.GetBudgetGroupName( pNode->GetBudgetGroupID() );
+ int r, g, b, a;
+ g_VProfCurrentProfile.GetBudgetGroupColor( pNode->GetBudgetGroupID(), r, g, b, a );
+ pNodeList[*pNumNodes].budgetGroupColor = XMAKECOLOR( r, g, b );
+
+ pNodeList[*pNumNodes].totalCalls = pNode->GetTotalCalls();
+ pNodeList[*pNumNodes].inclusiveTime = pNode->GetTotalTime();
+ pNodeList[*pNumNodes].exclusiveTime = pNode->GetTotalTimeLessChildren();
+ (*pNumNodes)++;
+
+ CVProfNode *nodePtr = pNode->GetSibling();
+ if ( nodePtr )
+ {
+ VXBuildNodeList_r( nodePtr, pNodeList, pNumNodes );
+ }
+
+ nodePtr = pNode->GetChild();
+ if ( nodePtr )
+ {
+ VXBuildNodeList_r( nodePtr, pNodeList, pNumNodes );
+ }
+}
+void CVProfile::VXSendNodes( void )
+{
+ Pause();
+
+ xVProfNodeItem_t *pNodeList = (xVProfNodeItem_t *)stackalloc( MAX_VPROF_NODES_IN_LIST * sizeof(xVProfNodeItem_t) );
+ int numNodes = 0;
+ VXBuildNodeList_r( GetRoot(), pNodeList, &numNodes );
+
+ // send to vxconsole
+ XBX_rVProfNodeList( numNodes, pNodeList );
+
+ Resume();
+}
+#endif
+
+//-------------------------------------
+static void DumpSorted( const tchar *pszHeading, double totalTime, bool (*pfnSort)( const TimeSums_t &, const TimeSums_t & ), int maxLen = 999999 )
+{
+ unsigned i;
+ vector<TimeSums_t> sortedSums;
+ sortedSums = g_TimeSums;
+ sort( sortedSums.begin(), sortedSums.end(), pfnSort );
+
+ Msg( _T("%s\n"), pszHeading);
+ Msg( _T(" Scope Calls Calls/Frame Time+Child Pct Time Pct Avg/Frame Avg/Call Avg-NoChild Peak\n"));
+ Msg( _T(" ---------------------------------------------------- ----------- ----------- ----------- ------ ----------- ------ ----------- ----------- ----------- -----------\n"));
+ for ( i = 0; i < sortedSums.size() && i < (unsigned)maxLen; i++ )
+ {
+ double avg = ( sortedSums[i].calls ) ? sortedSums[i].time / (double)sortedSums[i].calls : 0.0;
+ double avgLessChildren = ( sortedSums[i].calls ) ? sortedSums[i].timeLessChildren / (double)sortedSums[i].calls : 0.0;
+
+ Msg( _T(" %52.52s%12d%12.3f%12.3f%7.2f%12.3f%7.2f%12.3f%12.3f%12.3f%12.3f\n"),
+ sortedSums[i].pszProfileScope,
+ sortedSums[i].calls,
+ (float)sortedSums[i].calls / (float)g_TotalFrames,
+ sortedSums[i].time,
+ min( ( sortedSums[i].time / totalTime ) * 100.0, 100.0 ),
+ sortedSums[i].timeLessChildren,
+ min( ( sortedSums[i].timeLessChildren / totalTime ) * 100.0, 100.0 ),
+ sortedSums[i].time / (float)g_TotalFrames,
+ avg,
+ avgLessChildren,
+ sortedSums[i].peak );
+ }
+}
+
+#if defined( _X360 )
+// Dump information on all nodes with PMC recording
+static void DumpPMC( CVProfNode *pNode, bool &bPrintHeader, uint64 L2thresh = 1, uint64 LHSthresh = 1 )
+{
+ if (!pNode) return;
+
+ uint64 l2 = pNode->GetL2CacheMisses();
+ uint64 lhs = pNode->GetLoadHitStores();
+ if ( l2 > L2thresh &&
+ lhs > LHSthresh )
+ {
+ // met threshold.
+ if (bPrintHeader)
+ {
+ // print header
+ Msg( _T("-- 360 PMC information --\n") );
+ Msg( _T("Scope L2/call L2/frame LHS/call LHS/frame\n") );
+ Msg( _T("---------------------------------------------------- --------- --------- --------- ---------\n") );
+
+ bPrintHeader = false;
+ }
+
+ // print
+ float calls = pNode->GetTotalCalls();
+ float frames = g_TotalFrames;
+ Msg( _T("%52.52s %9.2f %9.2f %9.2f %9.2f\n"), pNode->GetName(), l2/calls, l2/frames, lhs/calls, lhs/frames );
+ }
+
+ if ( pNode->GetSibling() )
+ {
+ DumpPMC( pNode->GetSibling(), bPrintHeader, L2thresh, LHSthresh );
+ }
+
+ if ( pNode->GetChild() )
+ {
+ DumpPMC( pNode->GetChild(), bPrintHeader, L2thresh, LHSthresh );
+ }
+}
+#endif
+
+//-------------------------------------
+
+void CVProfile::OutputReport( int type, const tchar *pszStartNode, int budgetGroupID )
+{
+ Msg( _T("******** BEGIN VPROF REPORT ********\n"));
+#ifdef _MSC_VER
+#if (_MSC_VER < 1300)
+ Msg( _T(" (note: this report exceeds the output capacity of MSVC debug window. Use console window or console log.) \n"));
+#endif
+#endif
+
+ g_TotalFrames = max( NumFramesSampled() - 1, 1 );
+
+ if ( NumFramesSampled() == 0 || GetTotalTimeSampled() == 0)
+ Msg( _T("No samples\n") );
+ else
+ {
+ if ( type & VPRT_SUMMARY )
+ {
+ Msg( _T("-- Summary --\n") );
+ Msg( _T("%d frames sampled for %.2f seconds\n"), g_TotalFrames, GetTotalTimeSampled() / 1000.0 );
+ Msg( _T("Average %.2f fps, %.2f ms per frame\n"), 1000.0 / ( GetTotalTimeSampled() / g_TotalFrames ), GetTotalTimeSampled() / g_TotalFrames );
+ Msg( _T("Peak %.2f ms frame\n"), GetPeakFrameTime() );
+
+ double timeAccountedFor = 100.0 - ( m_Root.GetTotalTimeLessChildren() / m_Root.GetTotalTime() );
+ Msg( _T("%.0f pct of time accounted for\n"), min( 100.0, timeAccountedFor ) );
+ Msg( _T("\n") );
+ }
+
+ if ( pszStartNode == NULL )
+ {
+ pszStartNode = GetRoot()->GetName();
+ }
+
+ SumTimes( pszStartNode, budgetGroupID );
+
+ // Dump the hierarchy
+ if ( type & VPRT_HIERARCHY )
+ {
+ Msg( _T("-- Hierarchical Call Graph --\n"));
+ if ( pszStartNode == NULL )
+ g_pStartNode = NULL;
+ else
+ g_pStartNode = FindNode( GetRoot(), pszStartNode );
+
+ DumpNodes( (!g_pStartNode) ? GetRoot() : g_pStartNode, 0, false );
+ Msg( _T("\n") );
+ }
+
+ if ( type & VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY )
+ {
+ Msg( _T("-- Hierarchical Call Graph --\n"));
+ if ( pszStartNode == NULL )
+ g_pStartNode = NULL;
+ else
+ g_pStartNode = FindNode( GetRoot(), pszStartNode );
+
+ DumpNodes( (!g_pStartNode) ? GetRoot() : g_pStartNode, 0, true );
+ Msg( _T("\n") );
+ }
+
+ int maxLen = ( type & VPRT_LIST_TOP_ITEMS_ONLY ) ? 25 : 999999;
+
+ if ( type & VPRT_LIST_BY_TIME )
+ {
+ DumpSorted( _T("-- Profile scopes sorted by time (including children) --"), GetTotalTimeSampled(), TimeCompare, maxLen );
+ Msg( _T("\n") );
+ }
+ if ( type & VPRT_LIST_BY_TIME_LESS_CHILDREN )
+ {
+ DumpSorted( _T("-- Profile scopes sorted by time (without children) --"), GetTotalTimeSampled(), TimeLessChildrenCompare, maxLen );
+ Msg( _T("\n") );
+ }
+ if ( type & VPRT_LIST_BY_AVG_TIME )
+ {
+ DumpSorted( _T("-- Profile scopes sorted by average time (including children) --"), GetTotalTimeSampled(), AverageTimeCompare, maxLen );
+ Msg( _T("\n") );
+ }
+ if ( type & VPRT_LIST_BY_AVG_TIME_LESS_CHILDREN )
+ {
+ DumpSorted( _T("-- Profile scopes sorted by average time (without children) --"), GetTotalTimeSampled(), AverageTimeLessChildrenCompare, maxLen );
+ Msg( _T("\n") );
+ }
+ if ( type & VPRT_LIST_BY_PEAK_TIME )
+ {
+ DumpSorted( _T("-- Profile scopes sorted by peak --"), GetTotalTimeSampled(), PeakCompare, maxLen);
+ Msg( _T("\n") );
+ }
+ if ( type & VPRT_LIST_BY_PEAK_OVER_AVERAGE )
+ {
+ DumpSorted( _T("-- Profile scopes sorted by peak over average (including children) --"), GetTotalTimeSampled(), PeakOverAverageCompare, maxLen );
+ Msg( _T("\n") );
+ }
+
+ // TODO: Functions by time less children
+ // TODO: Functions by time averages
+ // TODO: Functions by peak
+ // TODO: Peak deviation from average
+ g_TimesLessChildren.clear();
+ g_TimeSumsMap.clear();
+ g_TimeSums.clear();
+
+#ifdef _X360
+ bool bPrintedHeader = true;
+ DumpPMC( FindNode( GetRoot(), pszStartNode ), bPrintedHeader );
+#endif
+
+ }
+ Msg( _T("******** END VPROF REPORT ********\n"));
+
+}
+
+//=============================================================================
+
+CVProfile::CVProfile()
+ : m_Root( _T("Root"), 0, NULL, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, 0 ),
+ m_pCurNode( &m_Root ),
+ m_nFrames( 0 ),
+ m_enabled( 0 ), // don't change this. if m_enabled is anything but zero coming out of this constructor, vprof will break.
+ m_pausedEnabledDepth( 0 ),
+ m_fAtRoot( true )
+{
+
+#ifdef VPROF_VTUNE_GROUP
+ m_GroupIDStackDepth = 1;
+ m_GroupIDStack[0] = 0; // VPROF_BUDGETGROUP_OTHER_UNACCOUNTED
+#endif
+
+ m_TargetThreadId = ThreadGetCurrentId();
+
+ // Go ahead and allocate 32 slots for budget group names
+ MEM_ALLOC_CREDIT();
+ m_pBudgetGroups = new CVProfile::CBudgetGroup[32];
+ m_nBudgetGroupNames = 0;
+ m_nBudgetGroupNamesAllocated = 32;
+
+ // Add these here so that they will always be in the same order.
+ // VPROF_BUDGETGROUP_OTHER_UNACCOUNTED has to be FIRST!!!!
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_WORLD_RENDERING, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_DISPLACEMENT_RENDERING, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_GAME, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_PLAYER, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_NPCS, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_SERVER_ANIM, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_CLIENT_ANIMATION, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_PHYSICS, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_STATICPROP_RENDERING, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_MODEL_RENDERING, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_MODEL_FAST_PATH_RENDERING,BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_LIGHTCACHE, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_BRUSHMODEL_RENDERING, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_SHADOW_RENDERING, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_DETAILPROP_RENDERING, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_PARTICLE_RENDERING, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_ROPES, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_DLIGHT_RENDERING, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_NETWORKING, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_SOUND, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_VGUI, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_FILESYSTEM, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_PREDICTION, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_INTERPOLATION, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_SWAP_BUFFERS, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OCCLUSION, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OVERLAYS, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_TOOLS, BUDGETFLAG_OTHER | BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_TEXTURE_CACHE, BUDGETFLAG_CLIENT );
+ BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_REPLAY, BUDGETFLAG_SERVER );
+// BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_DISP_HULLTRACES );
+
+ m_bPMEInit = false;
+ m_bPMEEnabled = false;
+
+#ifdef VPROF_VXCONSOLE_EXISTS
+ m_bTraceCompleteEvent = false;
+ m_iSuccessiveTraceIndex = 0;
+ m_ReportMode = VXCONSOLE_REPORT_TIME;
+ m_pReportScale[VXCONSOLE_REPORT_TIME] = 1000.0f;
+ m_pReportScale[VXCONSOLE_REPORT_L2CACHE_MISSES] = 1.0f;
+ m_pReportScale[VXCONSOLE_REPORT_LOAD_HIT_STORE] = 0.1f;
+ m_nFrameCount = 0;
+ m_nFramesRemaining = 1;
+ m_WorstCycles = 0;
+ m_WorstTraceFilename[ 0 ] = 0;
+ m_UpdateMode = 0;
+#endif
+#ifdef _X360
+ m_iCPUTraceEnabled = kDisabled;
+#endif
+}
+
+
+CVProfile::~CVProfile()
+{
+ Term();
+}
+
+
+void CVProfile::FreeNodes_R( CVProfNode *pNode )
+{
+ CVProfNode *pNext;
+ for ( CVProfNode *pChild = pNode->GetChild(); pChild; pChild = pNext )
+ {
+ pNext = pChild->GetSibling();
+ FreeNodes_R( pChild );
+ }
+
+ if ( pNode == GetRoot() )
+ {
+ pNode->m_pChild = NULL;
+ }
+ else
+ {
+ delete pNode;
+ }
+}
+
+
+void CVProfile::Term()
+{
+ int i;
+ for( i = 0; i < m_nBudgetGroupNames; i++ )
+ {
+ delete [] m_pBudgetGroups[i].m_pName;
+ }
+ delete m_pBudgetGroups;
+ m_nBudgetGroupNames = m_nBudgetGroupNamesAllocated = 0;
+ m_pBudgetGroups = NULL;
+
+ int n;
+ for( n = 0; n < m_NumCounters; n++ )
+ {
+ delete [] m_CounterNames[n];
+ m_CounterNames[n] = NULL;
+ }
+ m_NumCounters = 0;
+
+ // Free the nodes.
+ if ( GetRoot() )
+ {
+ FreeNodes_R( GetRoot() );
+ }
+}
+
+
+#define COLORMIN 160
+#define COLORMAX 255
+
+static int g_ColorLookup[4] =
+{
+ COLORMIN,
+ COLORMAX,
+ COLORMIN+(COLORMAX-COLORMIN)/3,
+ COLORMIN+((COLORMAX-COLORMIN)*2)/3,
+};
+
+#define GET_BIT( val, bitnum ) ( ( val >> bitnum ) & 0x1 )
+
+void CVProfile::GetBudgetGroupColor( int budgetGroupID, int &r, int &g, int &b, int &a )
+{
+ budgetGroupID = budgetGroupID % ( 1 << 6 );
+
+ int index;
+ index = GET_BIT( budgetGroupID, 0 ) | ( GET_BIT( budgetGroupID, 5 ) << 1 );
+ r = g_ColorLookup[index];
+ index = GET_BIT( budgetGroupID, 1 ) | ( GET_BIT( budgetGroupID, 4 ) << 1 );
+ g = g_ColorLookup[index];
+ index = GET_BIT( budgetGroupID, 2 ) | ( GET_BIT( budgetGroupID, 3 ) << 1 );
+ b = g_ColorLookup[index];
+ a = 255;
+}
+
+// return -1 if it doesn't exist.
+int CVProfile::FindBudgetGroupName( const tchar *pBudgetGroupName )
+{
+ int i;
+ for( i = 0; i < m_nBudgetGroupNames; i++ )
+ {
+ if( _tcsicmp( pBudgetGroupName, m_pBudgetGroups[i].m_pName ) == 0 )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int CVProfile::AddBudgetGroupName( const tchar *pBudgetGroupName, int budgetFlags )
+{
+ MEM_ALLOC_CREDIT();
+ tchar *pNewString = new tchar[ _tcslen( pBudgetGroupName ) + 1 ];
+ _tcscpy( pNewString, pBudgetGroupName );
+ if( m_nBudgetGroupNames + 1 > m_nBudgetGroupNamesAllocated )
+ {
+ m_nBudgetGroupNamesAllocated *= 2;
+ m_nBudgetGroupNamesAllocated = max( m_nBudgetGroupNames + 6, m_nBudgetGroupNamesAllocated );
+
+ CBudgetGroup *pNew = new CBudgetGroup[ m_nBudgetGroupNamesAllocated ];
+ for ( int i=0; i < m_nBudgetGroupNames; i++ )
+ pNew[i] = m_pBudgetGroups[i];
+
+ delete [] m_pBudgetGroups;
+ m_pBudgetGroups = pNew;
+ }
+
+ m_pBudgetGroups[m_nBudgetGroupNames].m_pName = pNewString;
+ m_pBudgetGroups[m_nBudgetGroupNames].m_BudgetFlags = budgetFlags;
+ m_nBudgetGroupNames++;
+ if( m_pNumBudgetGroupsChangedCallBack )
+ {
+ (*m_pNumBudgetGroupsChangedCallBack)();
+ }
+
+#if defined( VPROF_VXCONSOLE_EXISTS )
+ // re-start with all the known budgets
+ VXProfileStart();
+#endif
+ return m_nBudgetGroupNames - 1;
+}
+
+int CVProfile::BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName, int budgetFlagsToORIn )
+{
+ int budgetGroupID = FindBudgetGroupName( pBudgetGroupName );
+ if( budgetGroupID == -1 )
+ {
+ budgetGroupID = AddBudgetGroupName( pBudgetGroupName, budgetFlagsToORIn );
+ }
+ else
+ {
+ m_pBudgetGroups[budgetGroupID].m_BudgetFlags |= budgetFlagsToORIn;
+ }
+
+ return budgetGroupID;
+}
+
+int CVProfile::BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName )
+{
+ return BudgetGroupNameToBudgetGroupID( pBudgetGroupName, BUDGETFLAG_OTHER );
+}
+
+int CVProfile::GetNumBudgetGroups( void )
+{
+ return m_nBudgetGroupNames;
+}
+
+void CVProfile::RegisterNumBudgetGroupsChangedCallBack( void (*pCallBack)(void) )
+{
+ m_pNumBudgetGroupsChangedCallBack = pCallBack;
+}
+
+void CVProfile::HideBudgetGroup( int budgetGroupID, bool bHide )
+{
+ if( budgetGroupID != -1 )
+ {
+ if ( bHide )
+ m_pBudgetGroups[budgetGroupID].m_BudgetFlags |= BUDGETFLAG_HIDDEN;
+ else
+ m_pBudgetGroups[budgetGroupID].m_BudgetFlags &= ~BUDGETFLAG_HIDDEN;
+ }
+}
+
+int *CVProfile::FindOrCreateCounter( const tchar *pName, CounterGroup_t eCounterGroup )
+{
+ Assert( m_NumCounters+1 < MAXCOUNTERS );
+ if ( m_NumCounters + 1 >= MAXCOUNTERS || !InTargetThread() )
+ {
+ static int dummy;
+ return &dummy;
+ }
+ int i;
+ for( i = 0; i < m_NumCounters; i++ )
+ {
+ if( _tcsicmp( m_CounterNames[i], pName ) == 0 )
+ {
+ // found it!
+ return &m_Counters[i];
+ }
+ }
+
+ // NOTE: These get freed in ~CVProfile.
+ MEM_ALLOC_CREDIT();
+ tchar *pNewName = new tchar[_tcslen( pName ) + 1];
+ _tcscpy( pNewName, pName );
+ m_Counters[m_NumCounters] = 0;
+ m_CounterGroups[m_NumCounters] = (char)eCounterGroup;
+ m_CounterNames[m_NumCounters++] = pNewName;
+ return &m_Counters[m_NumCounters-1];
+}
+
+void CVProfile::ResetCounters( CounterGroup_t eCounterGroup )
+{
+ int i;
+ for( i = 0; i < m_NumCounters; i++ )
+ {
+ if ( m_CounterGroups[i] == eCounterGroup )
+ m_Counters[i] = 0;
+ }
+}
+
+int CVProfile::GetNumCounters() const
+{
+ return m_NumCounters;
+}
+
+const tchar *CVProfile::GetCounterName( int index ) const
+{
+ Assert( index >= 0 && index < m_NumCounters );
+ return m_CounterNames[index];
+}
+
+int CVProfile::GetCounterValue( int index ) const
+{
+ Assert( index >= 0 && index < m_NumCounters );
+ return m_Counters[index];
+}
+
+const tchar *CVProfile::GetCounterNameAndValue( int index, int &val ) const
+{
+ Assert( index >= 0 && index < m_NumCounters );
+ val = m_Counters[index];
+ return m_CounterNames[index];
+}
+
+CounterGroup_t CVProfile::GetCounterGroup( int index ) const
+{
+ Assert( index >= 0 && index < m_NumCounters );
+ return (CounterGroup_t)m_CounterGroups[index];
+}
+
+#ifdef _X360
+void CVProfile::LatchMultiFrame( int64 cycles )
+{
+ if ( cycles > m_WorstCycles )
+ {
+ strncpy( m_WorstTraceFilename, GetCPUTraceFilename(), sizeof( m_WorstTraceFilename ) );
+ m_WorstCycles = cycles;
+ }
+}
+
+void CVProfile::SpewWorstMultiFrame()
+{
+ CCycleCount cc( m_WorstCycles );
+ Msg( "%s == %.3f msec\n", m_WorstTraceFilename, cc.GetMillisecondsF() );
+}
+#endif
+
+#ifdef DBGFLAG_VALIDATE
+
+#ifdef _WIN64
+#error the below is presumably broken on 64 bit
+#endif // _WIN64
+
+const int k_cSTLMapAllocOffset = 4;
+#define GET_INTERNAL_MAP_ALLOC_PTR( pMap ) \
+ ( * ( (void **) ( ( ( byte * ) ( pMap ) ) + k_cSTLMapAllocOffset ) ) )
+//-----------------------------------------------------------------------------
+// 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 CVProfile::Validate( CValidator &validator, tchar *pchName )
+{
+ validator.Push( _T("CVProfile"), this, pchName );
+
+ m_Root.Validate( validator, _T("m_Root") );
+
+ for ( int iBudgetGroup=0; iBudgetGroup < m_nBudgetGroupNames; iBudgetGroup++ )
+ validator.ClaimMemory( m_pBudgetGroups[iBudgetGroup].m_pName );
+
+ validator.ClaimMemory( m_pBudgetGroups );
+
+ // The std template map class allocates memory internally, but offer no way to get
+ // access to their pointer. Since this is for debug purposes only and the
+ // std template classes don't change, just look at the well-known offset. This
+ // is arguably sick and wrong, kind of like marrying a squirrel.
+ validator.ClaimMemory( GET_INTERNAL_MAP_ALLOC_PTR( &g_TimesLessChildren ) );
+ validator.ClaimMemory( GET_INTERNAL_MAP_ALLOC_PTR( &g_TimeSumsMap ) );
+
+ validator.Pop( );
+}
+#endif // DBGFLAG_VALIDATE
+
+#endif
+
diff --git a/external/vpc/tier0/win32consoleio.cpp b/external/vpc/tier0/win32consoleio.cpp
new file mode 100644
index 0000000..8ec6fa9
--- /dev/null
+++ b/external/vpc/tier0/win32consoleio.cpp
@@ -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
+}