687 lines
19 KiB
C++
687 lines
19 KiB
C++
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "tier0/platform.h"
|
|
|
|
#if defined( PLATFORM_WINDOWS_PC )
|
|
#define WIN_32_LEAN_AND_MEAN
|
|
#include <windows.h> // Currently needed for IsBadReadPtr and IsBadWritePtr
|
|
#pragma comment(lib,"user32.lib") // For MessageBox
|
|
#endif
|
|
|
|
#include "tier0/minidump.h"
|
|
#include "tier0/stacktools.h"
|
|
#include "tier0/etwprof.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include "color.h"
|
|
#include "tier0/dbg.h"
|
|
#include "tier0/threadtools.h"
|
|
#include "tier0/icommandline.h"
|
|
#include "tier0/vprof.h"
|
|
#include <math.h>
|
|
|
|
#if defined( _X360 )
|
|
#include "xbox/xbox_console.h"
|
|
#endif
|
|
|
|
#ifndef STEAM
|
|
#define PvRealloc realloc
|
|
#define PvAlloc malloc
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
|
#pragma optimize( "g", off ) //variable argument functions seem to screw up stack walking unless this optimization is disabled
|
|
// Disable this warning: dbg.cpp(479): warning C4748: /GS can not protect parameters and local variables from local buffer overrun because optimizations are disabled in function
|
|
#pragma warning( disable : 4748 )
|
|
#endif
|
|
|
|
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_LOADING, "LOADING" );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Stack attachment management
|
|
//-----------------------------------------------------------------------------
|
|
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
|
|
|
static bool s_bCallStacksWithAllWarnings = false; //if true, attach a call stack to every SPEW_WARNING message. Warning()/DevWarning()/...
|
|
static int s_iWarningMaxCallStackLength = 5;
|
|
#define AutomaticWarningCallStackLength() (s_bCallStacksWithAllWarnings ? s_iWarningMaxCallStackLength : 0)
|
|
|
|
void _Warning_AlwaysSpewCallStack_Enable( bool bEnable )
|
|
{
|
|
s_bCallStacksWithAllWarnings = bEnable;
|
|
}
|
|
|
|
void _Warning_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
|
{
|
|
s_iWarningMaxCallStackLength = iMaxCallStackLength;
|
|
}
|
|
|
|
static bool s_bCallStacksWithAllErrors = false; //if true, attach a call stack to every SPEW_ERROR message. Mostly just Error()
|
|
static int s_iErrorMaxCallStackLength = 20; //default to higher output with an error since we're quitting anyways
|
|
#define AutomaticErrorCallStackLength() (s_bCallStacksWithAllErrors ? s_iErrorMaxCallStackLength : 0)
|
|
|
|
void _Error_AlwaysSpewCallStack_Enable( bool bEnable )
|
|
{
|
|
s_bCallStacksWithAllErrors = bEnable;
|
|
}
|
|
|
|
void _Error_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
|
{
|
|
s_iErrorMaxCallStackLength = iMaxCallStackLength;
|
|
}
|
|
|
|
#else //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
|
|
|
#define AutomaticWarningCallStackLength() 0
|
|
#define AutomaticErrorCallStackLength() 0
|
|
|
|
void _Warning_AlwaysSpewCallStack_Enable( bool bEnable )
|
|
{
|
|
}
|
|
|
|
void _Warning_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
|
{
|
|
}
|
|
|
|
void _Error_AlwaysSpewCallStack_Enable( bool bEnable )
|
|
{
|
|
}
|
|
|
|
void _Error_AlwaysSpewCallStack_Length( int iMaxCallStackLength )
|
|
{
|
|
}
|
|
|
|
#endif //#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
|
|
|
// Skip forward past the directory
|
|
static const char *SkipToFname( const tchar* pFile )
|
|
{
|
|
if ( pFile == NULL )
|
|
return "unknown";
|
|
const tchar* pSlash = _tcsrchr( pFile, '\\' );
|
|
const tchar* pSlash2 = _tcsrchr( pFile, '/' );
|
|
if (pSlash < pSlash2) pSlash = pSlash2;
|
|
return pSlash ? pSlash + 1: pFile;
|
|
}
|
|
|
|
void _ExitOnFatalAssert( const tchar* pFile, int line )
|
|
{
|
|
Log_Msg( LOG_ASSERT, _T("Fatal assert failed: %s, line %d. Application exiting.\n"), pFile, line );
|
|
|
|
// only write out minidumps if we're not in the debugger
|
|
if ( !Plat_IsInDebugSession() )
|
|
{
|
|
WriteMiniDump();
|
|
}
|
|
|
|
Log_Msg( LOG_DEVELOPER, _T("_ExitOnFatalAssert\n") );
|
|
Plat_ExitProcess( EXIT_FAILURE );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Templates to assist in validating pointers:
|
|
//-----------------------------------------------------------------------------
|
|
PLATFORM_INTERFACE void _AssertValidReadPtr( void* ptr, int count/* = 1*/ )
|
|
{
|
|
#if defined( _WIN32 ) && !defined( _X360 )
|
|
Assert( !IsBadReadPtr( ptr, count ) );
|
|
#else
|
|
Assert( !count || ptr );
|
|
#endif
|
|
}
|
|
|
|
PLATFORM_INTERFACE void _AssertValidWritePtr( void* ptr, int count/* = 1*/ )
|
|
{
|
|
#if defined( _WIN32 ) && !defined( _X360 )
|
|
Assert( !IsBadWritePtr( ptr, count ) );
|
|
#else
|
|
Assert( !count || ptr );
|
|
#endif
|
|
}
|
|
|
|
PLATFORM_INTERFACE void _AssertValidReadWritePtr( void* ptr, int count/* = 1*/ )
|
|
{
|
|
#if defined( _WIN32 ) && !defined( _X360 )
|
|
Assert(!( IsBadWritePtr(ptr, count) || IsBadReadPtr(ptr,count)));
|
|
#else
|
|
Assert( !count || ptr );
|
|
#endif
|
|
}
|
|
|
|
PLATFORM_INTERFACE void _AssertValidStringPtr( const tchar* ptr, int maxchar/* = 0xFFFFFF */ )
|
|
{
|
|
#if defined( _WIN32 ) && !defined( _X360 )
|
|
#ifdef TCHAR_IS_CHAR
|
|
Assert( !IsBadStringPtr( ptr, maxchar ) );
|
|
#else
|
|
Assert( !IsBadStringPtrW( ptr, maxchar ) );
|
|
#endif
|
|
#else
|
|
Assert( ptr );
|
|
#endif
|
|
}
|
|
|
|
PLATFORM_INTERFACE void AssertValidWStringPtr( const wchar_t* ptr, int maxchar/* = 0xFFFFFF */ )
|
|
{
|
|
#if defined( _WIN32 ) && !defined( _X360 )
|
|
Assert( !IsBadStringPtrW( ptr, maxchar ) );
|
|
#else
|
|
Assert( ptr );
|
|
#endif
|
|
}
|
|
|
|
void AppendCallStackToLogMessage( tchar *formattedMessage, int iMessageLength, int iAppendCallStackLength )
|
|
{
|
|
#if defined( ENABLE_RUNTIME_STACK_TRANSLATION )
|
|
# if defined( TCHAR_IS_CHAR ) //I'm horrible with unicode and I don't plan on testing this with wide characters just yet
|
|
if( iAppendCallStackLength > 0 )
|
|
{
|
|
int iExistingMessageLength = (int)strlen( formattedMessage ); //no V_strlen in tier 0, plus we're only compiling this for windows and 360. Seems safe
|
|
formattedMessage += iExistingMessageLength;
|
|
iMessageLength -= iExistingMessageLength;
|
|
|
|
if( iMessageLength <= 32 )
|
|
return; //no room for anything useful
|
|
|
|
//append directly to the spew message
|
|
if( (iExistingMessageLength > 0) && (formattedMessage[-1] == '\n') )
|
|
{
|
|
--formattedMessage;
|
|
++iMessageLength;
|
|
}
|
|
|
|
//append preface
|
|
int iAppendedLength = _snprintf( formattedMessage, iMessageLength, _T("\nCall Stack:\n\t") );
|
|
|
|
void **CallStackBuffer = (void **)stackalloc( iAppendCallStackLength * sizeof( void * ) );
|
|
int iCount = GetCallStack( CallStackBuffer, iAppendCallStackLength, 2 );
|
|
if( TranslateStackInfo( CallStackBuffer, iCount, formattedMessage + iAppendedLength, iMessageLength - iAppendedLength, _T("\n\t") ) == 0 )
|
|
{
|
|
//failure
|
|
formattedMessage[0] = '\0'; //this is pointing at where we wrote "\nCall Stack:\n\t"
|
|
}
|
|
else
|
|
{
|
|
iAppendedLength += (int)strlen( formattedMessage + iAppendedLength ); //no V_strlen in tier 0, plus we're only compiling this for windows and 360. Seems safe
|
|
|
|
if( iAppendedLength < iMessageLength )
|
|
{
|
|
formattedMessage[iAppendedLength] = '\n'; //Add another newline.
|
|
++iAppendedLength;
|
|
|
|
formattedMessage[iAppendedLength] = '\0';
|
|
}
|
|
}
|
|
}
|
|
# else
|
|
AssertMsg( false, "Fixme" );
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
// Forward declare for internal use only.
|
|
CLoggingSystem *GetGlobalLoggingSystem();
|
|
|
|
#define Log_LegacyHelperColor_Stack( Channel, Severity, Color, MessageFormat, AppendCallStackLength ) \
|
|
do \
|
|
{ \
|
|
CLoggingSystem *pLoggingSystem = GetGlobalLoggingSystem(); \
|
|
if ( pLoggingSystem->IsChannelEnabled( Channel, Severity ) ) \
|
|
{ \
|
|
tchar formattedMessage[MAX_LOGGING_MESSAGE_LENGTH]; \
|
|
va_list args; \
|
|
va_start( args, MessageFormat ); \
|
|
Tier0Internal_vsntprintf( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, MessageFormat, args ); \
|
|
va_end( args ); \
|
|
AppendCallStackToLogMessage( formattedMessage, MAX_LOGGING_MESSAGE_LENGTH, AppendCallStackLength ); \
|
|
pLoggingSystem->LogDirect( Channel, Severity, Color, formattedMessage ); \
|
|
} \
|
|
} while( 0 )
|
|
|
|
#define Log_LegacyHelperColor( Channel, Severity, Color, MessageFormat ) Log_LegacyHelperColor_Stack( Channel, Severity, Color, MessageFormat, 0 )
|
|
|
|
#define Log_LegacyHelper_Stack( Channel, Severity, MessageFormat, AppendCallStackLength ) Log_LegacyHelperColor_Stack( Channel, Severity, pLoggingSystem->GetChannelColor( Channel ), MessageFormat, AppendCallStackLength )
|
|
#define Log_LegacyHelper( Channel, Severity, MessageFormat ) Log_LegacyHelperColor( Channel, Severity, pLoggingSystem->GetChannelColor( Channel ), MessageFormat )
|
|
|
|
#if !defined( DBGFLAG_STRINGS_STRIP )
|
|
|
|
void Msg( const tchar* pMsgFormat, ... )
|
|
{
|
|
Log_LegacyHelper( LOG_GENERAL, LS_MESSAGE, pMsgFormat );
|
|
}
|
|
|
|
void Warning( const tchar *pMsgFormat, ... )
|
|
{
|
|
Log_LegacyHelper_Stack( LOG_GENERAL, LS_WARNING, pMsgFormat, AutomaticWarningCallStackLength() );
|
|
}
|
|
|
|
void Warning_SpewCallStack( int iMaxCallStackLength, const tchar *pMsgFormat, ... )
|
|
{
|
|
Log_LegacyHelper_Stack( LOG_GENERAL, LS_WARNING, pMsgFormat, iMaxCallStackLength );
|
|
}
|
|
|
|
#endif // !DBGFLAG_STRINGS_STRIP
|
|
|
|
void Error( const tchar *pMsgFormat, ... )
|
|
{
|
|
#if !defined( DBGFLAG_STRINGS_STRIP )
|
|
Log_LegacyHelper_Stack( LOG_GENERAL, LS_ERROR, pMsgFormat, AutomaticErrorCallStackLength() );
|
|
// Many places that call Error assume that execution will not continue afterwards so it
|
|
// is important to exit here. The function prototype promises that this will happen.
|
|
Plat_ExitProcess( 100 );
|
|
#endif
|
|
}
|
|
|
|
void Error_SpewCallStack( int iMaxCallStackLength, const tchar *pMsgFormat, ... )
|
|
{
|
|
#if !defined( DBGFLAG_STRINGS_STRIP )
|
|
Log_LegacyHelper_Stack( LOG_GENERAL, LS_ERROR, pMsgFormat, iMaxCallStackLength );
|
|
// Many places that call Error_SpewCallStack assume that execution will not continue afterwards so it
|
|
// is important to exit here. The function prototype promises that this will happen.
|
|
Plat_ExitProcess( 100 );
|
|
#endif
|
|
}
|
|
|
|
#if !defined( DBGFLAG_STRINGS_STRIP )
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A couple of super-common dynamic spew messages, here for convenience
|
|
// These looked at the "developer" group, print if it's level 1 or higher
|
|
//-----------------------------------------------------------------------------
|
|
void DevMsg( int level, const tchar* pMsgFormat, ... )
|
|
{
|
|
LoggingChannelID_t channel = level >= 2 ? LOG_DEVELOPER_VERBOSE : LOG_DEVELOPER;
|
|
Log_LegacyHelper( channel, LS_MESSAGE, pMsgFormat );
|
|
}
|
|
|
|
|
|
void DevWarning( int level, const tchar *pMsgFormat, ... )
|
|
{
|
|
LoggingChannelID_t channel = level >= 2 ? LOG_DEVELOPER_VERBOSE : LOG_DEVELOPER;
|
|
Log_LegacyHelper( channel, LS_WARNING, pMsgFormat );
|
|
}
|
|
|
|
void DevMsg( const tchar *pMsgFormat, ... )
|
|
{
|
|
Log_LegacyHelper( LOG_DEVELOPER, LS_MESSAGE, pMsgFormat );
|
|
}
|
|
|
|
void DevWarning( const tchar *pMsgFormat, ... )
|
|
{
|
|
Log_LegacyHelper( LOG_DEVELOPER, LS_WARNING, pMsgFormat );
|
|
}
|
|
|
|
void ConColorMsg( const Color& clr, const tchar* pMsgFormat, ... )
|
|
{
|
|
Log_LegacyHelperColor( LOG_CONSOLE, LS_MESSAGE, clr, pMsgFormat );
|
|
}
|
|
|
|
void ConMsg( const tchar *pMsgFormat, ... )
|
|
{
|
|
Log_LegacyHelper( LOG_CONSOLE, LS_MESSAGE, pMsgFormat );
|
|
}
|
|
|
|
void ConDMsg( const tchar *pMsgFormat, ... )
|
|
{
|
|
Log_LegacyHelper( LOG_DEVELOPER_CONSOLE, LS_MESSAGE, pMsgFormat );
|
|
}
|
|
|
|
#endif // !DBGFLAG_STRINGS_STRIP
|
|
|
|
// If we don't have a function from math.h, then it doesn't link certain floating-point
|
|
// functions in and printfs with %f cause runtime errors in the C libraries.
|
|
PLATFORM_INTERFACE float CrackSmokingCompiler( float a )
|
|
{
|
|
return (float)fabs( a );
|
|
}
|
|
|
|
void* Plat_SimpleLog( const tchar* file, int line )
|
|
{
|
|
FILE* f = _tfopen( _T("simple.log"), _T("at+") );
|
|
_ftprintf( f, _T("%s:%i\n"), file, line );
|
|
fclose( f );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if !defined( DBGFLAG_STRINGS_STRIP )
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: For debugging startup times, etc.
|
|
// Input : *fmt -
|
|
// ... -
|
|
//-----------------------------------------------------------------------------
|
|
void COM_TimestampedLog( char const *fmt, ... )
|
|
{
|
|
static float s_LastStamp = 0.0;
|
|
static bool s_bShouldLog = false;
|
|
static bool s_bShouldLogToConsole = false;
|
|
static bool s_bShouldLogToETW = false;
|
|
static bool s_bChecked = false;
|
|
static bool s_bFirstWrite = false;
|
|
|
|
if ( !s_bChecked )
|
|
{
|
|
s_bShouldLog = ( CommandLine()->CheckParm( "-profile" ) ) ? true : false;
|
|
s_bShouldLogToConsole = ( CommandLine()->ParmValue( "-profile", 0.0f ) != 0.0f ) ? true : false;
|
|
s_bShouldLogToETW = ( CommandLine()->CheckParm( "-etwprofile" ) ) ? true : false;
|
|
if ( s_bShouldLogToETW )
|
|
{
|
|
s_bShouldLog = true;
|
|
}
|
|
s_bChecked = true;
|
|
}
|
|
if ( !s_bShouldLog )
|
|
{
|
|
return;
|
|
}
|
|
|
|
char string[1024];
|
|
va_list argptr;
|
|
va_start( argptr, fmt );
|
|
Tier0Internal_vsnprintf( string, sizeof( string ), fmt, argptr );
|
|
va_end( argptr );
|
|
|
|
float curStamp = Plat_FloatTime();
|
|
|
|
#if defined( _X360 )
|
|
XBX_rTimeStampLog( curStamp, string );
|
|
#elif defined( _PS3 )
|
|
Log_Warning( LOG_LOADING, "%8.4f / %8.4f: %s\n", curStamp, curStamp - s_LastStamp, string );
|
|
#endif
|
|
|
|
if ( IsPC() )
|
|
{
|
|
// If ETW profiling is enabled then do it only.
|
|
if ( s_bShouldLogToETW )
|
|
{
|
|
ETWMark( string );
|
|
}
|
|
else
|
|
{
|
|
if ( !s_bFirstWrite )
|
|
{
|
|
unlink( "timestamped.log" );
|
|
s_bFirstWrite = true;
|
|
}
|
|
|
|
FILE* fp = fopen( "timestamped.log", "at+" );
|
|
fprintf( fp, "%8.4f / %8.4f: %s\n", curStamp, curStamp - s_LastStamp, string );
|
|
fclose( fp );
|
|
|
|
if ( s_bShouldLogToConsole )
|
|
{
|
|
Msg( "%8.4f / %8.4f: %s\n", curStamp, curStamp - s_LastStamp, string );
|
|
}
|
|
}
|
|
}
|
|
|
|
s_LastStamp = curStamp;
|
|
}
|
|
|
|
#endif // !DBGFLAG_STRINGS_STRIP
|
|
|
|
static AssertFailedNotifyFunc_t s_AssertFailedNotifyFunc = NULL;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets an assert failed notify handler
|
|
//-----------------------------------------------------------------------------
|
|
void SetAssertFailedNotifyFunc( AssertFailedNotifyFunc_t func )
|
|
{
|
|
s_AssertFailedNotifyFunc = func;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Calls the assert failed notify handler if one has been set
|
|
//-----------------------------------------------------------------------------
|
|
void CallAssertFailedNotifyFunc( const char *pchFile, int nLine, const char *pchMessage )
|
|
{
|
|
if ( s_AssertFailedNotifyFunc )
|
|
s_AssertFailedNotifyFunc( pchFile, nLine, pchMessage );
|
|
}
|
|
|
|
|
|
#ifdef IS_WINDOWS_PC
|
|
|
|
class CHardwareBreakPoint
|
|
{
|
|
public:
|
|
|
|
enum EOpCode
|
|
{
|
|
BRK_SET = 0,
|
|
BRK_UNSET,
|
|
};
|
|
|
|
CHardwareBreakPoint()
|
|
{
|
|
m_eOperation = BRK_SET;
|
|
m_pvAddress = 0;
|
|
m_hThread = 0;
|
|
m_hThreadEvent = 0;
|
|
m_nRegister = 0;
|
|
m_bSuccess = false;
|
|
}
|
|
|
|
const void *m_pvAddress;
|
|
HANDLE m_hThread;
|
|
EHardwareBreakpointType m_eType;
|
|
EHardwareBreakpointSize m_eSize;
|
|
HANDLE m_hThreadEvent;
|
|
int m_nRegister;
|
|
EOpCode m_eOperation;
|
|
bool m_bSuccess;
|
|
|
|
static void SetBits( DWORD_PTR& dw, int lowBit, int bits, int newValue );
|
|
static DWORD WINAPI ThreadProc( LPVOID lpParameter );
|
|
};
|
|
|
|
void CHardwareBreakPoint::SetBits( DWORD_PTR& dw, int lowBit, int bits, int newValue )
|
|
{
|
|
DWORD_PTR mask = (1 << bits) - 1;
|
|
dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
|
|
}
|
|
|
|
DWORD WINAPI CHardwareBreakPoint::ThreadProc( LPVOID lpParameter )
|
|
{
|
|
CHardwareBreakPoint *h = reinterpret_cast< CHardwareBreakPoint * >( lpParameter );
|
|
SuspendThread( h->m_hThread );
|
|
|
|
// Get current context
|
|
CONTEXT ct = {0};
|
|
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
|
GetThreadContext(h->m_hThread,&ct);
|
|
|
|
int FlagBit = 0;
|
|
|
|
bool Dr0Busy = false;
|
|
bool Dr1Busy = false;
|
|
bool Dr2Busy = false;
|
|
bool Dr3Busy = false;
|
|
if (ct.Dr7 & 1)
|
|
Dr0Busy = true;
|
|
if (ct.Dr7 & 4)
|
|
Dr1Busy = true;
|
|
if (ct.Dr7 & 16)
|
|
Dr2Busy = true;
|
|
if (ct.Dr7 & 64)
|
|
Dr3Busy = true;
|
|
|
|
if ( h->m_eOperation == CHardwareBreakPoint::BRK_UNSET )
|
|
{
|
|
// Remove
|
|
if (h->m_nRegister == 0)
|
|
{
|
|
FlagBit = 0;
|
|
ct.Dr0 = 0;
|
|
Dr0Busy = false;
|
|
}
|
|
if (h->m_nRegister == 1)
|
|
{
|
|
FlagBit = 2;
|
|
ct.Dr1 = 0;
|
|
Dr1Busy = false;
|
|
}
|
|
if (h->m_nRegister == 2)
|
|
{
|
|
FlagBit = 4;
|
|
ct.Dr2 = 0;
|
|
Dr2Busy = false;
|
|
}
|
|
if (h->m_nRegister == 3)
|
|
{
|
|
FlagBit = 6;
|
|
ct.Dr3 = 0;
|
|
Dr3Busy = false;
|
|
}
|
|
ct.Dr7 &= ~(1 << FlagBit);
|
|
}
|
|
else
|
|
{
|
|
if (!Dr0Busy)
|
|
{
|
|
h->m_nRegister = 0;
|
|
ct.Dr0 = (DWORD_PTR)h->m_pvAddress;
|
|
Dr0Busy = true;
|
|
}
|
|
else if (!Dr1Busy)
|
|
{
|
|
h->m_nRegister = 1;
|
|
ct.Dr1 = (DWORD_PTR)h->m_pvAddress;
|
|
Dr1Busy = true;
|
|
}
|
|
else if (!Dr2Busy)
|
|
{
|
|
h->m_nRegister = 2;
|
|
ct.Dr2 = (DWORD_PTR)h->m_pvAddress;
|
|
Dr2Busy = true;
|
|
}
|
|
else if (!Dr3Busy)
|
|
{
|
|
h->m_nRegister = 3;
|
|
ct.Dr3 = (DWORD_PTR)h->m_pvAddress;
|
|
Dr3Busy = true;
|
|
}
|
|
else
|
|
{
|
|
h->m_bSuccess = false;
|
|
ResumeThread(h->m_hThread);
|
|
SetEvent(h->m_hThreadEvent);
|
|
return 0;
|
|
}
|
|
|
|
ct.Dr6 = 0;
|
|
int st = 0;
|
|
if (h->m_eType == BREAKPOINT_EXECUTE)
|
|
st = 0;
|
|
if (h->m_eType == BREAKPOINT_READWRITE)
|
|
st = 3;
|
|
if (h->m_eType == BREAKPOINT_WRITE)
|
|
st = 1;
|
|
|
|
int le = 0;
|
|
if (h->m_eSize == BREAKPOINT_SIZE_1)
|
|
le = 0;
|
|
if (h->m_eSize == BREAKPOINT_SIZE_2)
|
|
le = 1;
|
|
if (h->m_eSize == BREAKPOINT_SIZE_4)
|
|
le = 3;
|
|
if (h->m_eSize == BREAKPOINT_SIZE_8)
|
|
le = 2;
|
|
|
|
SetBits( ct.Dr7, 16 + h->m_nRegister*4, 2, st );
|
|
SetBits( ct.Dr7, 18 + h->m_nRegister*4, 2, le );
|
|
SetBits( ct.Dr7, h->m_nRegister*2,1,1);
|
|
}
|
|
|
|
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
|
SetThreadContext(h->m_hThread,&ct);
|
|
|
|
ResumeThread( h->m_hThread );
|
|
h->m_bSuccess = true;
|
|
SetEvent( h->m_hThreadEvent );
|
|
return 0;
|
|
}
|
|
|
|
HardwareBreakpointHandle_t SetHardwareBreakpoint( EHardwareBreakpointType eType, EHardwareBreakpointSize eSize, const void *pvLocation )
|
|
{
|
|
CHardwareBreakPoint *h = new CHardwareBreakPoint();
|
|
h->m_pvAddress = pvLocation;
|
|
h->m_eSize = eSize;
|
|
h->m_eType = eType;
|
|
HANDLE hThread = GetCurrentThread();
|
|
h->m_hThread = hThread;
|
|
|
|
if ( hThread == GetCurrentThread() )
|
|
{
|
|
DWORD nThreadId = GetCurrentThreadId();
|
|
h->m_hThread = OpenThread( THREAD_ALL_ACCESS, 0, nThreadId );
|
|
}
|
|
|
|
h->m_hThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
h->m_eOperation = CHardwareBreakPoint::BRK_SET; // Set Break
|
|
CreateThread( 0, 0, CHardwareBreakPoint::ThreadProc, (LPVOID)h, 0, 0 );
|
|
WaitForSingleObject( h->m_hThreadEvent,INFINITE );
|
|
CloseHandle( h->m_hThreadEvent );
|
|
h->m_hThreadEvent = 0;
|
|
if ( hThread == GetCurrentThread() )
|
|
{
|
|
CloseHandle( h->m_hThread );
|
|
}
|
|
h->m_hThread = hThread;
|
|
if ( !h->m_bSuccess )
|
|
{
|
|
delete h;
|
|
return (HardwareBreakpointHandle_t)0;
|
|
}
|
|
return (HardwareBreakpointHandle_t)h;
|
|
}
|
|
|
|
bool ClearHardwareBreakpoint( HardwareBreakpointHandle_t handle )
|
|
{
|
|
CHardwareBreakPoint *h = reinterpret_cast< CHardwareBreakPoint* >( handle );
|
|
if ( !h )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bOpened = false;
|
|
if ( h->m_hThread == GetCurrentThread() )
|
|
{
|
|
DWORD nThreadId = GetCurrentThreadId();
|
|
h->m_hThread = OpenThread( THREAD_ALL_ACCESS, 0, nThreadId );
|
|
bOpened = true;
|
|
}
|
|
|
|
h->m_hThreadEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
h->m_eOperation = CHardwareBreakPoint::BRK_UNSET; // Remove Break
|
|
CreateThread( 0,0,CHardwareBreakPoint::ThreadProc, (LPVOID)h, 0,0 );
|
|
WaitForSingleObject( h->m_hThreadEvent, INFINITE );
|
|
CloseHandle( h->m_hThreadEvent );
|
|
h->m_hThreadEvent = 0;
|
|
if ( bOpened )
|
|
{
|
|
CloseHandle( h->m_hThread );
|
|
}
|
|
delete h;
|
|
return true;
|
|
}
|
|
|
|
#endif // IS_WINDOWS_PC
|
|
|