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