Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
5.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Thread management routines
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "pch_tier0.h"
  8. #include "tier0/valve_off.h"
  9. #ifdef _WIN32
  10. #define WIN_32_LEAN_AND_MEAN
  11. #include <windows.h>
  12. #include <assert.h>
  13. #include <tlhelp32.h>
  14. #endif
  15. #include "tier0/platform.h"
  16. #include "tier0/dbg.h"
  17. #include "tier0/threadtools.h"
  18. unsigned long Plat_GetCurrentThreadID()
  19. {
  20. return ThreadGetCurrentId();
  21. }
  22. #if defined(_WIN32) && defined(_M_IX86)
  23. static CThreadMutex s_BreakpointStateMutex;
  24. struct X86HardwareBreakpointState_t
  25. {
  26. const void *pAddress[4];
  27. char nWatchBytes[4];
  28. bool bBreakOnRead[4];
  29. };
  30. static X86HardwareBreakpointState_t s_BreakpointState = { {0,0,0,0}, {0,0,0,0}, {false,false,false,false} };
  31. static void X86ApplyBreakpointsToThread( DWORD dwThreadId )
  32. {
  33. CONTEXT ctx;
  34. ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
  35. X86HardwareBreakpointState_t *pState = &s_BreakpointState;
  36. ctx.Dr0 = (DWORD) pState->pAddress[0];
  37. ctx.Dr1 = (DWORD) pState->pAddress[1];
  38. ctx.Dr2 = (DWORD) pState->pAddress[2];
  39. ctx.Dr3 = (DWORD) pState->pAddress[3];
  40. ctx.Dr7 = (DWORD) 0;
  41. for ( int i = 0; i < 4; ++i )
  42. {
  43. if ( pState->pAddress[i] && pState->nWatchBytes[i] )
  44. {
  45. ctx.Dr7 |= 1 << (i*2);
  46. if ( pState->bBreakOnRead[i] )
  47. ctx.Dr7 |= 3 << (16 + i*4);
  48. else
  49. ctx.Dr7 |= 1 << (16 + i*4);
  50. switch ( pState->nWatchBytes[i] )
  51. {
  52. case 1: ctx.Dr7 |= 0<<(18 + i*4); break;
  53. case 2: ctx.Dr7 |= 1<<(18 + i*4); break;
  54. case 4: ctx.Dr7 |= 3<<(18 + i*4); break;
  55. case 8: ctx.Dr7 |= 2<<(18 + i*4); break;
  56. }
  57. }
  58. }
  59. // Freeze this thread, adjust its breakpoint state
  60. HANDLE hThread = OpenThread( THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT, FALSE, dwThreadId );
  61. if ( hThread != INVALID_HANDLE_VALUE )
  62. {
  63. if ( SuspendThread( hThread ) != -1 )
  64. {
  65. SetThreadContext( hThread, &ctx );
  66. ResumeThread( hThread );
  67. }
  68. CloseHandle( hThread );
  69. }
  70. }
  71. static DWORD STDCALL ThreadProcX86SetDataBreakpoints( LPVOID pvParam )
  72. {
  73. if ( pvParam )
  74. {
  75. X86ApplyBreakpointsToThread( *(unsigned long*)pvParam );
  76. return 0;
  77. }
  78. // This function races against creation and destruction of new threads. Try to execute as quickly as possible.
  79. SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST );
  80. DWORD dwProcId = GetCurrentProcessId();
  81. DWORD dwThisThreadId = GetCurrentThreadId();
  82. HANDLE hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
  83. if ( hSnap != INVALID_HANDLE_VALUE )
  84. {
  85. THREADENTRY32 threadEntry;
  86. // Thread32First/Thread32Next may adjust dwSize to be smaller. It's weird. Read the doc.
  87. const DWORD dwMinSize = (char*)(&threadEntry.th32OwnerProcessID + 1) - (char*)&threadEntry;
  88. threadEntry.dwSize = sizeof( THREADENTRY32 );
  89. BOOL bContinue = Thread32First( hSnap, &threadEntry );
  90. while ( bContinue )
  91. {
  92. if ( threadEntry.dwSize >= dwMinSize )
  93. {
  94. if ( threadEntry.th32OwnerProcessID == dwProcId && threadEntry.th32ThreadID != dwThisThreadId )
  95. {
  96. X86ApplyBreakpointsToThread( threadEntry.th32ThreadID );
  97. }
  98. }
  99. threadEntry.dwSize = sizeof( THREADENTRY32 );
  100. bContinue = Thread32Next( hSnap, &threadEntry );
  101. }
  102. CloseHandle( hSnap );
  103. }
  104. return 0;
  105. }
  106. void Plat_SetHardwareDataBreakpoint( const void *pAddress, int nWatchBytes, bool bBreakOnRead )
  107. {
  108. Assert( pAddress );
  109. Assert( nWatchBytes == 0 || nWatchBytes == 1 || nWatchBytes == 2 || nWatchBytes == 4 || nWatchBytes == 8 );
  110. s_BreakpointStateMutex.Lock();
  111. if ( nWatchBytes == 0 )
  112. {
  113. for ( int i = 0; i < 4; ++i )
  114. {
  115. if ( pAddress == s_BreakpointState.pAddress[i] )
  116. {
  117. for ( ; i < 3; ++i )
  118. {
  119. s_BreakpointState.pAddress[i] = s_BreakpointState.pAddress[i+1];
  120. s_BreakpointState.nWatchBytes[i] = s_BreakpointState.nWatchBytes[i+1];
  121. s_BreakpointState.bBreakOnRead[i] = s_BreakpointState.bBreakOnRead[i+1];
  122. }
  123. s_BreakpointState.pAddress[3] = NULL;
  124. s_BreakpointState.nWatchBytes[3] = 0;
  125. s_BreakpointState.bBreakOnRead[3] = false;
  126. break;
  127. }
  128. }
  129. }
  130. else
  131. {
  132. // Replace first null entry or first existing entry at this address, or bump all entries down
  133. for ( int i = 0; i < 4; ++i )
  134. {
  135. if ( s_BreakpointState.pAddress[i] && s_BreakpointState.pAddress[i] != pAddress && i < 3 )
  136. continue;
  137. // Last iteration.
  138. if ( s_BreakpointState.pAddress[i] && s_BreakpointState.pAddress[i] != pAddress )
  139. {
  140. // Full up. Shift table down, drop least recently set
  141. for ( int j = 0; j < 3; ++j )
  142. {
  143. s_BreakpointState.pAddress[j] = s_BreakpointState.pAddress[j+1];
  144. s_BreakpointState.nWatchBytes[j] = s_BreakpointState.nWatchBytes[j+1];
  145. s_BreakpointState.bBreakOnRead[j] = s_BreakpointState.bBreakOnRead[j+1];
  146. }
  147. }
  148. s_BreakpointState.pAddress[i] = pAddress;
  149. s_BreakpointState.nWatchBytes[i] = nWatchBytes;
  150. s_BreakpointState.bBreakOnRead[i] = bBreakOnRead;
  151. break;
  152. }
  153. }
  154. HANDLE hWorkThread = CreateThread( NULL, NULL, &ThreadProcX86SetDataBreakpoints, NULL, 0, NULL );
  155. if ( hWorkThread != INVALID_HANDLE_VALUE )
  156. {
  157. WaitForSingleObject( hWorkThread, INFINITE );
  158. CloseHandle( hWorkThread );
  159. }
  160. s_BreakpointStateMutex.Unlock();
  161. }
  162. void Plat_ApplyHardwareDataBreakpointsToNewThread( unsigned long dwThreadID )
  163. {
  164. s_BreakpointStateMutex.Lock();
  165. if ( dwThreadID != GetCurrentThreadId() )
  166. {
  167. X86ApplyBreakpointsToThread( dwThreadID );
  168. }
  169. else
  170. {
  171. HANDLE hWorkThread = CreateThread( NULL, NULL, &ThreadProcX86SetDataBreakpoints, &dwThreadID, 0, NULL );
  172. if ( hWorkThread != INVALID_HANDLE_VALUE )
  173. {
  174. WaitForSingleObject( hWorkThread, INFINITE );
  175. CloseHandle( hWorkThread );
  176. }
  177. }
  178. s_BreakpointStateMutex.Unlock();
  179. }
  180. #else
  181. void Plat_SetHardwareDataBreakpoint( const void *pAddress, int nWatchBytes, bool bBreakOnRead )
  182. {
  183. // no impl on this platform yet
  184. }
  185. void Plat_ApplyHardwareDataBreakpointsToNewThread( unsigned long dwThreadID )
  186. {
  187. // no impl on this platform yet
  188. }
  189. #endif