| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | //***************************************************************************** |
| 5 | // File: process.cpp |
| 6 | // |
| 7 | |
| 8 | // |
| 9 | //***************************************************************************** |
| 10 | #include "stdafx.h" |
| 11 | #include "primitives.h" |
| 12 | #include "safewrap.h" |
| 13 | |
| 14 | #include "check.h" |
| 15 | |
| 16 | #ifndef SM_REMOTESESSION |
| 17 | #define SM_REMOTESESSION 0x1000 |
| 18 | #endif |
| 19 | |
| 20 | #include "corpriv.h" |
| 21 | #include "corexcep.h" |
| 22 | #include "../../dlls/mscorrc/resource.h" |
| 23 | #include <limits.h> |
| 24 | |
| 25 | #include <sstring.h> |
| 26 | |
| 27 | // @dbgtodo shim: process has some private hooks into the shim. |
| 28 | #include "shimpriv.h" |
| 29 | |
| 30 | #include "metadataexports.h" |
| 31 | #include "readonlydatatargetfacade.h" |
| 32 | #include "metahost.h" |
| 33 | |
| 34 | // Keep this around for retail debugging. It's very very useful because |
| 35 | // it's global state that we can always find, regardless of how many locals the compiler |
| 36 | // optimizes away ;) |
| 37 | struct RSDebuggingInfo; |
| 38 | extern RSDebuggingInfo * g_pRSDebuggingInfo; |
| 39 | |
| 40 | //--------------------------------------------------------------------------------------- |
| 41 | // |
| 42 | // OpenVirtualProcessImpl method called by the shim to get an ICorDebugProcess4 instance |
| 43 | // |
| 44 | // Arguments: |
| 45 | // clrInstanceId - target pointer identifying which CLR in the Target to debug. |
| 46 | // pDataTarget - data target abstraction. |
| 47 | // hDacModule - the handle of the appropriate DAC dll for this runtime |
| 48 | // riid - interface ID to query for. |
| 49 | // ppProcessOut - new object for target, interface ID matches riid. |
| 50 | // ppFlagsOut - currently only has 1 bit to indicate whether or not this runtime |
| 51 | // instance will send a managed event after attach |
| 52 | // |
| 53 | // Return Value: |
| 54 | // S_OK on success. Else failure |
| 55 | // |
| 56 | // Assumptions: |
| 57 | // |
| 58 | // Notes: |
| 59 | // The outgoing process object can be cleaned up by calling Detach (which |
| 60 | // will reset the Attach bit.) |
| 61 | // @dbgtodo attach-bit: need to determine fate of attach bit. |
| 62 | // |
| 63 | //--------------------------------------------------------------------------------------- |
| 64 | STDAPI OpenVirtualProcessImpl( |
| 65 | ULONG64 clrInstanceId, |
| 66 | IUnknown * pDataTarget, |
| 67 | HMODULE hDacModule, |
| 68 | CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion, |
| 69 | REFIID riid, |
| 70 | IUnknown ** ppInstance, |
| 71 | CLR_DEBUGGING_PROCESS_FLAGS* pFlagsOut) |
| 72 | { |
| 73 | HRESULT hr = S_OK; |
| 74 | RSExtSmartPtr<CordbProcess> pProcess; |
| 75 | PUBLIC_API_ENTRY(NULL); |
| 76 | EX_TRY |
| 77 | { |
| 78 | |
| 79 | if ( (pDataTarget == NULL) || (clrInstanceId == 0) || (pMaxDebuggerSupportedVersion == NULL) || |
| 80 | ((pFlagsOut == NULL) && (ppInstance == NULL)) |
| 81 | ) |
| 82 | { |
| 83 | ThrowHR(E_INVALIDARG); |
| 84 | } |
| 85 | |
| 86 | // We consider the top 8 bits of the struct version to be the only part that represents |
| 87 | // a breaking change. This gives us some freedom in the future to have the debugger |
| 88 | // opt into getting more data. |
| 89 | const WORD kMajorMask = 0xff00; |
| 90 | const WORD kMaxStructMajor = 0; |
| 91 | if ((pMaxDebuggerSupportedVersion->wStructVersion & kMajorMask) > kMaxStructMajor) |
| 92 | { |
| 93 | // Don't know how to interpret the version structure |
| 94 | ThrowHR(CORDBG_E_UNSUPPORTED_VERSION_STRUCT); |
| 95 | } |
| 96 | |
| 97 | // This process object is intended to be used for the V3 pipeline, and so |
| 98 | // much of the process from V2 is not being used. For example, |
| 99 | // - there is no ShimProcess object |
| 100 | // - there is no w32et thread (all threads are effectively an event thread) |
| 101 | // - the stop state is 'live', which corresponds to CordbProcess not knowing what |
| 102 | // its stop state really is (because that is now controlled by the shim). |
| 103 | ProcessDescriptor pd = ProcessDescriptor::CreateUninitialized(); |
| 104 | IfFailThrow(CordbProcess::OpenVirtualProcess( |
| 105 | clrInstanceId, |
| 106 | pDataTarget, // takes a reference |
| 107 | hDacModule, |
| 108 | NULL, // Cordb |
| 109 | &pd, // 0 for V3 cases (pShim == NULL). |
| 110 | NULL, // no Shim in V3 cases |
| 111 | &pProcess)); |
| 112 | |
| 113 | // CordbProcess::OpenVirtualProcess already did the external addref to pProcess. |
| 114 | // Since pProcess is a smart ptr, it will external release in this function. |
| 115 | // Living reference will be the one from the QI. |
| 116 | |
| 117 | // get the managed debug event pending flag |
| 118 | if(pFlagsOut != NULL) |
| 119 | { |
| 120 | hr = pProcess->GetAttachStateFlags(pFlagsOut); |
| 121 | if(FAILED(hr)) |
| 122 | { |
| 123 | ThrowHR(hr); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | // |
| 128 | // Check to make sure the debugger supports debugging this version |
| 129 | // Note that it's important that we still store the flags (above) in this case |
| 130 | // |
| 131 | if (!CordbProcess::IsCompatibleWith(pMaxDebuggerSupportedVersion->wMajor)) |
| 132 | { |
| 133 | // Not compatible - don't keep the process instance, and return this specific error-code |
| 134 | ThrowHR(CORDBG_E_UNSUPPORTED_FORWARD_COMPAT); |
| 135 | } |
| 136 | |
| 137 | // |
| 138 | // Now Query for the requested interface |
| 139 | // |
| 140 | if(ppInstance != NULL) |
| 141 | { |
| 142 | IfFailThrow(pProcess->QueryInterface(riid, reinterpret_cast<void**> (ppInstance))); |
| 143 | } |
| 144 | |
| 145 | // if you have to add code here that could fail make sure ppInstance gets released and NULL'ed at exit |
| 146 | } |
| 147 | EX_CATCH_HRESULT(hr); |
| 148 | |
| 149 | if((FAILED(hr) || ppInstance == NULL) && pProcess != NULL) |
| 150 | { |
| 151 | // The process has a strong reference to itself which is only released by neutering it. |
| 152 | // Since we aren't handing out the ref then we need to clean it up |
| 153 | _ASSERTE(ppInstance == NULL || *ppInstance == NULL); |
| 154 | pProcess->Neuter(); |
| 155 | } |
| 156 | return hr; |
| 157 | }; |
| 158 | |
| 159 | //--------------------------------------------------------------------------------------- |
| 160 | // |
| 161 | // OpenVirtualProcessImpl2 method called by the dbgshim to get an ICorDebugProcess4 instance |
| 162 | // |
| 163 | // Arguments: |
| 164 | // clrInstanceId - target pointer identifying which CLR in the Target to debug. |
| 165 | // pDataTarget - data target abstraction. |
| 166 | // pDacModulePath - the module path of the appropriate DAC dll for this runtime |
| 167 | // riid - interface ID to query for. |
| 168 | // ppProcessOut - new object for target, interface ID matches riid. |
| 169 | // ppFlagsOut - currently only has 1 bit to indicate whether or not this runtime |
| 170 | // instance will send a managed event after attach |
| 171 | // |
| 172 | // Return Value: |
| 173 | // S_OK on success. Else failure |
| 174 | //--------------------------------------------------------------------------------------- |
| 175 | STDAPI OpenVirtualProcessImpl2( |
| 176 | ULONG64 clrInstanceId, |
| 177 | IUnknown * pDataTarget, |
| 178 | LPCWSTR pDacModulePath, |
| 179 | CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion, |
| 180 | REFIID riid, |
| 181 | IUnknown ** ppInstance, |
| 182 | CLR_DEBUGGING_PROCESS_FLAGS* pFlagsOut) |
| 183 | { |
| 184 | HMODULE hDac = LoadLibraryW(pDacModulePath); |
| 185 | if (hDac == NULL) |
| 186 | { |
| 187 | return HRESULT_FROM_WIN32(GetLastError()); |
| 188 | } |
| 189 | return OpenVirtualProcessImpl(clrInstanceId, pDataTarget, hDac, pMaxDebuggerSupportedVersion, riid, ppInstance, pFlagsOut); |
| 190 | } |
| 191 | |
| 192 | //--------------------------------------------------------------------------------------- |
| 193 | // DEPRECATED - use OpenVirtualProcessImpl |
| 194 | // OpenVirtualProcess method used by the shim in CLR v4 Beta1 |
| 195 | // We'd like a beta1 shim/VS to still be able to open dumps using a CLR v4 Beta2+ mscordbi.dll, |
| 196 | // so we'll leave this in place (at least until after Beta2 is in wide use). |
| 197 | //--------------------------------------------------------------------------------------- |
| 198 | STDAPI OpenVirtualProcess2( |
| 199 | ULONG64 clrInstanceId, |
| 200 | IUnknown * pDataTarget, |
| 201 | HMODULE hDacModule, |
| 202 | REFIID riid, |
| 203 | IUnknown ** ppInstance, |
| 204 | CLR_DEBUGGING_PROCESS_FLAGS* pFlagsOut) |
| 205 | { |
| 206 | CLR_DEBUGGING_VERSION maxVersion = {0}; |
| 207 | maxVersion.wMajor = 4; |
| 208 | return OpenVirtualProcessImpl(clrInstanceId, pDataTarget, hDacModule, &maxVersion, riid, ppInstance, pFlagsOut); |
| 209 | } |
| 210 | |
| 211 | //--------------------------------------------------------------------------------------- |
| 212 | // DEPRECATED - use OpenVirtualProcessImpl |
| 213 | // Public OpenVirtualProcess method to get an ICorDebugProcess4 instance |
| 214 | // Used directly in CLR v4 pre Beta1 - can probably be safely removed now |
| 215 | //--------------------------------------------------------------------------------------- |
| 216 | STDAPI OpenVirtualProcess( |
| 217 | ULONG64 clrInstanceId, |
| 218 | IUnknown * pDataTarget, |
| 219 | REFIID riid, |
| 220 | IUnknown ** ppInstance) |
| 221 | { |
| 222 | return OpenVirtualProcess2(clrInstanceId, pDataTarget, NULL, riid, ppInstance, NULL); |
| 223 | }; |
| 224 | |
| 225 | //----------------------------------------------------------------------------- |
| 226 | // Most Hresults to Unrecoverable error indicate an internal error |
| 227 | // in the Right-Side. |
| 228 | // However, a few are legal (eg, "could actually happen in a retail scenario and |
| 229 | // not indicate an issue in mscorbi"). Track that here. |
| 230 | //----------------------------------------------------------------------------- |
| 231 | |
| 232 | bool IsLegalFatalError(HRESULT hr) |
| 233 | { |
| 234 | return |
| 235 | (hr == CORDBG_E_INCOMPATIBLE_PROTOCOL) || |
| 236 | (hr == CORDBG_E_CANNOT_DEBUG_FIBER_PROCESS) || |
| 237 | (hr == CORDBG_E_UNCOMPATIBLE_PLATFORMS) || |
| 238 | (hr == CORDBG_E_MISMATCHED_CORWKS_AND_DACWKS_DLLS) || |
| 239 | // This should only happen in the case of a security attack on us. |
| 240 | (hr == E_ACCESSDENIED) || |
| 241 | (hr == E_FAIL); |
| 242 | } |
| 243 | |
| 244 | //----------------------------------------------------------------------------- |
| 245 | // Safe wait. Use this anytime we're waiting on: |
| 246 | // - an event signaled by the helper thread. |
| 247 | // - something signaled by a thread that holds the process lock. |
| 248 | // Note that we must preserve GetLastError() semantics. |
| 249 | //----------------------------------------------------------------------------- |
| 250 | inline DWORD SafeWaitForSingleObject(CordbProcess * p, HANDLE h, DWORD dwTimeout) |
| 251 | { |
| 252 | // Can't hold process lock while blocking |
| 253 | _ASSERTE(!p->ThreadHoldsProcessLock()); |
| 254 | |
| 255 | return ::WaitForSingleObject(h, dwTimeout); |
| 256 | } |
| 257 | |
| 258 | #define CORDB_WAIT_TIMEOUT 360000 // milliseconds |
| 259 | |
| 260 | //--------------------------------------------------------------------------------------- |
| 261 | // |
| 262 | // Get the timeout value used in waits. |
| 263 | // |
| 264 | // Return Value: |
| 265 | // Number of milliseconds to waite or possible INFINITE (-1). |
| 266 | // |
| 267 | // |
| 268 | // Notes: |
| 269 | // Uses registry values for fine tuning. |
| 270 | // |
| 271 | |
| 272 | // static |
| 273 | static inline DWORD CordbGetWaitTimeout() |
| 274 | { |
| 275 | #ifdef _DEBUG |
| 276 | // 0 = Wait forever |
| 277 | // 1 = Wait for CORDB_WAIT_TIMEOUT |
| 278 | // n = Wait for n milliseconds |
| 279 | static ConfigDWORD cordbWaitTimeout; |
| 280 | DWORD dwTimeoutVal = cordbWaitTimeout.val(CLRConfig::INTERNAL_DbgWaitTimeout); |
| 281 | if (dwTimeoutVal == 0) |
| 282 | return DWORD(-1); |
| 283 | else if (dwTimeoutVal != 1) |
| 284 | return dwTimeoutVal; |
| 285 | else |
| 286 | #endif |
| 287 | { |
| 288 | return CORDB_WAIT_TIMEOUT; |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | //---------------------------------------------------------------------------- |
| 293 | // Implementation of IDacDbiInterface::IMetaDataLookup. |
| 294 | // lookup Internal Metadata Importer keyed by PEFile |
| 295 | // isILMetaDataForNGENImage is true iff the IMDInternalImport returned represents a pointer to |
| 296 | // metadata from an IL image when the module was an ngen'ed image. |
| 297 | IMDInternalImport * CordbProcess::LookupMetaData(VMPTR_PEFile vmPEFile, bool &isILMetaDataForNGENImage) |
| 298 | { |
| 299 | INTERNAL_DAC_CALLBACK(this); |
| 300 | |
| 301 | HASHFIND hashFindAppDomain; |
| 302 | HASHFIND hashFindModule; |
| 303 | IMDInternalImport * pMDII = NULL; |
| 304 | isILMetaDataForNGENImage = false; |
| 305 | |
| 306 | // Check to see if one of the cached modules has the metadata we need |
| 307 | // If not we will do a more exhaustive search below |
| 308 | for (CordbAppDomain * pAppDomain = m_appDomains.FindFirst(&hashFindAppDomain); |
| 309 | pAppDomain != NULL; |
| 310 | pAppDomain = m_appDomains.FindNext(&hashFindAppDomain)) |
| 311 | { |
| 312 | for (CordbModule * pModule = pAppDomain->m_modules.FindFirst(&hashFindModule); |
| 313 | pModule != NULL; |
| 314 | pModule = pAppDomain->m_modules.FindNext(&hashFindModule)) |
| 315 | { |
| 316 | if (pModule->GetPEFile() == vmPEFile) |
| 317 | { |
| 318 | pMDII = NULL; |
| 319 | ALLOW_DATATARGET_MISSING_MEMORY( |
| 320 | pMDII = pModule->GetInternalMD(); |
| 321 | ); |
| 322 | if(pMDII != NULL) |
| 323 | return pMDII; |
| 324 | } |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | // Cache didn't have it... time to search harder |
| 329 | PrepopulateAppDomainsOrThrow(); |
| 330 | |
| 331 | // There may be perf issues here. The DAC may make a lot of metadata requests, and so |
| 332 | // this may be an area for potential perf optimizations if we find things running slow. |
| 333 | |
| 334 | // enumerate through all Modules |
| 335 | for (CordbAppDomain * pAppDomain = m_appDomains.FindFirst(&hashFindAppDomain); |
| 336 | pAppDomain != NULL; |
| 337 | pAppDomain = m_appDomains.FindNext(&hashFindAppDomain)) |
| 338 | { |
| 339 | pAppDomain->PrepopulateModules(); |
| 340 | |
| 341 | for (CordbModule * pModule = pAppDomain->m_modules.FindFirst(&hashFindModule); |
| 342 | pModule != NULL; |
| 343 | pModule = pAppDomain->m_modules.FindNext(&hashFindModule)) |
| 344 | { |
| 345 | if (pModule->GetPEFile() == vmPEFile) |
| 346 | { |
| 347 | pMDII = NULL; |
| 348 | ALLOW_DATATARGET_MISSING_MEMORY( |
| 349 | pMDII = pModule->GetInternalMD(); |
| 350 | ); |
| 351 | |
| 352 | if ( pMDII == NULL) |
| 353 | { |
| 354 | // If we couldn't get metadata from the CordbModule, then we need to ask the |
| 355 | // debugger if it can find the metadata elsewhere. |
| 356 | // If this was live debugging, we should have just gotten the memory contents. |
| 357 | // Thus this code is for dump debugging, when you don't have the metadata in the dump. |
| 358 | pMDII = LookupMetaDataFromDebugger(vmPEFile, isILMetaDataForNGENImage, pModule); |
| 359 | } |
| 360 | return pMDII; |
| 361 | } |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | return NULL; |
| 366 | } |
| 367 | |
| 368 | |
| 369 | IMDInternalImport * CordbProcess::LookupMetaDataFromDebugger( |
| 370 | VMPTR_PEFile vmPEFile, |
| 371 | bool &isILMetaDataForNGENImage, |
| 372 | CordbModule * pModule) |
| 373 | { |
| 374 | DWORD dwImageTimeStamp = 0; |
| 375 | DWORD dwImageSize = 0; |
| 376 | bool isNGEN = false; |
| 377 | StringCopyHolder filePath; |
| 378 | IMDInternalImport * pMDII = NULL; |
| 379 | |
| 380 | // First, see if the debugger can locate the exact metadata we want. |
| 381 | if (this->GetDAC()->GetMetaDataFileInfoFromPEFile(vmPEFile, dwImageTimeStamp, dwImageSize, isNGEN, &filePath)) |
| 382 | { |
| 383 | _ASSERTE(filePath.IsSet()); |
| 384 | |
| 385 | // Since we track modules by their IL images, that presents a little bit of oddness here. The correct |
| 386 | // thing to do is preferentially load the NI content. |
| 387 | // We don't discriminate between timestamps & sizes becuase CLRv4 deterministic NGEN guarantees that the |
| 388 | // IL image and NGEN image have the same timestamp and size. Should that guarantee change, this code |
| 389 | // will be horribly broken. |
| 390 | |
| 391 | // If we happen to have an NI file path, use it instead. |
| 392 | const WCHAR * pwszFilePath = pModule->GetNGenImagePath(); |
| 393 | if (pwszFilePath) |
| 394 | { |
| 395 | // Force the issue, regardless of the older codepath's opinion. |
| 396 | isNGEN = true; |
| 397 | } |
| 398 | else |
| 399 | { |
| 400 | pwszFilePath = (WCHAR *)filePath; |
| 401 | } |
| 402 | |
| 403 | ALLOW_DATATARGET_MISSING_MEMORY( |
| 404 | pMDII = LookupMetaDataFromDebuggerForSingleFile(pModule, pwszFilePath, dwImageTimeStamp, dwImageSize); |
| 405 | ); |
| 406 | |
| 407 | // If it's an ngen'ed image and the debugger couldn't find it, we can use the metadata from |
| 408 | // the corresponding IL image if the debugger can locate it. |
| 409 | filePath.Clear(); |
| 410 | if ((pMDII == NULL) && |
| 411 | (isNGEN) && |
| 412 | (this->GetDAC()->GetILImageInfoFromNgenPEFile(vmPEFile, dwImageTimeStamp, dwImageSize, &filePath))) |
| 413 | { |
| 414 | _ASSERTE(filePath.IsSet()); |
| 415 | |
| 416 | WCHAR *mutableFilePath = (WCHAR *)filePath; |
| 417 | |
| 418 | #if defined(FEATURE_CORESYSTEM) |
| 419 | size_t pathLen = wcslen(mutableFilePath); |
| 420 | |
| 421 | const wchar_t *nidll = W(".ni.dll" ); |
| 422 | const wchar_t *niexe = W(".ni.exe" ); |
| 423 | const size_t dllLen = wcslen(nidll); // used for ni.exe as well |
| 424 | |
| 425 | const wchar_t *niwinmd = W(".ni.winmd" ); |
| 426 | const size_t winmdLen = wcslen(niwinmd); |
| 427 | |
| 428 | if (pathLen > dllLen && _wcsicmp(mutableFilePath+pathLen-dllLen, nidll) == 0) |
| 429 | { |
| 430 | wcscpy_s(mutableFilePath+pathLen-dllLen, dllLen, W(".dll" )); |
| 431 | } |
| 432 | else if (pathLen > dllLen && _wcsicmp(mutableFilePath+pathLen-dllLen, niexe) == 0) |
| 433 | { |
| 434 | wcscpy_s(mutableFilePath+pathLen-dllLen, dllLen, W(".exe" )); |
| 435 | } |
| 436 | else if (pathLen > winmdLen && _wcsicmp(mutableFilePath+pathLen-winmdLen, niwinmd) == 0) |
| 437 | { |
| 438 | wcscpy_s(mutableFilePath+pathLen-winmdLen, winmdLen, W(".winmd" )); |
| 439 | } |
| 440 | #endif//FEATURE_CORESYSTEM |
| 441 | |
| 442 | ALLOW_DATATARGET_MISSING_MEMORY( |
| 443 | pMDII = LookupMetaDataFromDebuggerForSingleFile(pModule, mutableFilePath, dwImageTimeStamp, dwImageSize); |
| 444 | ); |
| 445 | |
| 446 | if (pMDII != NULL) |
| 447 | { |
| 448 | isILMetaDataForNGENImage = true; |
| 449 | } |
| 450 | } |
| 451 | } |
| 452 | return pMDII; |
| 453 | } |
| 454 | |
| 455 | // We do not know if the image being sent to us is an IL image or ngen image. |
| 456 | // CordbProcess::LookupMetaDataFromDebugger() has this knowledge when it looks up the file to hand off |
| 457 | // to this function. |
| 458 | // DacDbiInterfaceImpl::GetMDImport() has this knowledge in the isNGEN flag. |
| 459 | // The CLR v2 code that windbg used made a distinction whether the metadata came from |
| 460 | // the exact binary or not (i.e. were we getting metadata from the IL image and using |
| 461 | // it against the ngen image?) but that information was never used and so not brought forward. |
| 462 | // It would probably be more interesting generally to track whether the debugger gives us back |
| 463 | // a file that bears some relationship to the file we asked for, which would catch the NI/IL case |
| 464 | // as well. |
| 465 | IMDInternalImport * CordbProcess::LookupMetaDataFromDebuggerForSingleFile( |
| 466 | CordbModule * pModule, |
| 467 | LPCWSTR pwszFilePath, |
| 468 | DWORD dwTimeStamp, |
| 469 | DWORD dwSize) |
| 470 | { |
| 471 | INTERNAL_DAC_CALLBACK(this); |
| 472 | |
| 473 | ULONG32 cchLocalImagePath = MAX_LONGPATH; |
| 474 | ULONG32 cchLocalImagePathRequired; |
| 475 | NewArrayHolder<WCHAR> pwszLocalFilePath = NULL; |
| 476 | IMDInternalImport * pMDII = NULL; |
| 477 | |
| 478 | const HRESULT E_NSF_BUFFER = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); |
| 479 | HRESULT hr = E_NSF_BUFFER; |
| 480 | for(unsigned i=0; i<2 && hr == E_NSF_BUFFER; i++) |
| 481 | { |
| 482 | if (pwszLocalFilePath != NULL) |
| 483 | pwszLocalFilePath.Release(); |
| 484 | |
| 485 | if (NULL == (pwszLocalFilePath = new (nothrow) WCHAR[cchLocalImagePath+1])) |
| 486 | ThrowHR(E_OUTOFMEMORY); |
| 487 | |
| 488 | cchLocalImagePathRequired = 0; |
| 489 | |
| 490 | hr = m_pMetaDataLocator->GetMetaData(pwszFilePath, |
| 491 | dwTimeStamp, |
| 492 | dwSize, |
| 493 | cchLocalImagePath, |
| 494 | &cchLocalImagePathRequired, |
| 495 | pwszLocalFilePath); |
| 496 | |
| 497 | pwszLocalFilePath[cchLocalImagePath] = W('\0'); |
| 498 | cchLocalImagePath = cchLocalImagePathRequired; |
| 499 | } |
| 500 | |
| 501 | if (SUCCEEDED(hr)) |
| 502 | { |
| 503 | hr = pModule->InitPublicMetaDataFromFile(pwszLocalFilePath, ofReadOnly, false); |
| 504 | if (SUCCEEDED(hr)) |
| 505 | { |
| 506 | // While we're successfully returning a metadata reader, remember that there's |
| 507 | // absolutely no guarantee this metadata is an exact match for the vmPEFile. |
| 508 | // The debugger could literally send us back a path to any managed file with |
| 509 | // metadata content that is readable and we'll 'succeed'. |
| 510 | // For now, this is by-design. A debugger should be allowed to decide if it wants |
| 511 | // to take a risk by returning 'mostly matching' metadata to see if debugging is |
| 512 | // possible in the absence of a true match. |
| 513 | pMDII = pModule->GetInternalMD(); |
| 514 | } |
| 515 | } |
| 516 | |
| 517 | return pMDII; |
| 518 | } |
| 519 | |
| 520 | |
| 521 | //--------------------------------------------------------------------------------------- |
| 522 | // |
| 523 | // Implement IDacDbiInterface::IAllocator::Alloc |
| 524 | // Expected to throws on error. |
| 525 | // |
| 526 | // Arguments: |
| 527 | // lenBytes - size of the byte array to allocate |
| 528 | // |
| 529 | // Return Value: |
| 530 | // Return the newly allocated byte array, or throw on OOM |
| 531 | // |
| 532 | // Notes: |
| 533 | // Since this function is a callback from DAC, it must not take the process lock. |
| 534 | // If it does, we may deadlock between the DD lock and the process lock. |
| 535 | // If we really need to take the process lock for whatever reason, we must take it in the DBI functions |
| 536 | // which call the DAC API that ends up calling this function. |
| 537 | // See code:InternalDacCallbackHolder for more information. |
| 538 | // |
| 539 | |
| 540 | void * CordbProcess::Alloc(SIZE_T lenBytes) |
| 541 | { |
| 542 | return new BYTE[lenBytes]; // throws |
| 543 | } |
| 544 | |
| 545 | //--------------------------------------------------------------------------------------- |
| 546 | // |
| 547 | // Implements IDacDbiInterface::IAllocator::Free |
| 548 | // |
| 549 | // Arguments: |
| 550 | // p - pointer to the memory to be released |
| 551 | // |
| 552 | // Notes: |
| 553 | // Since this function is a callback from DAC, it must not take the process lock. |
| 554 | // If it does, we may deadlock between the DD lock and the process lock. |
| 555 | // If we really need to take the process lock for whatever reason, we must take it in the DBI functions |
| 556 | // which call the DAC API that ends up calling this function. |
| 557 | // See code:InternalDacCallbackHolder for more information. |
| 558 | // |
| 559 | |
| 560 | void CordbProcess::Free(void * p) |
| 561 | { |
| 562 | // This shouldn't throw. |
| 563 | delete [] ((BYTE *) p); |
| 564 | } |
| 565 | |
| 566 | |
| 567 | //--------------------------------------------------------------------------------------- |
| 568 | // |
| 569 | // #DBIVersionChecking |
| 570 | // |
| 571 | // There are a few checks we need to do to make sure we are using the matching DBI and DAC for a particular |
| 572 | // version of the runtime. |
| 573 | // |
| 574 | // 1. Runtime vs. DBI |
| 575 | // - Desktop |
| 576 | // This is done by making sure that the CorDebugInterfaceVersion passed to code:CreateCordbObject is |
| 577 | // compatible with the version of the DBI. |
| 578 | // |
| 579 | // - Windows CoreCLR |
| 580 | // This is done by dbgshim.dll. It checks whether the runtime DLL and the DBI DLL have the same |
| 581 | // product version. See CreateDebuggingInterfaceForVersion() in dbgshim.cpp. |
| 582 | // |
| 583 | // - Remote transport (Mac CoreCLR + CoreSystem CoreCLR) |
| 584 | // Since there is no dbgshim.dll for a remote CoreCLR, we have to do this check in some other place. |
| 585 | // We do this in code:CordbProcess::CreateDacDbiInterface, by calling |
| 586 | // code:DacDbiInterfaceImpl::CheckDbiVersion right after we have created the DDMarshal. |
| 587 | // The IDacDbiInterface implementation on remote device checks the product version of the device |
| 588 | // coreclr by: |
| 589 | // mac - looking at the Info.plist file in the CoreCLR bundle. |
| 590 | // CoreSystem - this check is skipped at the moment, but should be implemented if we release it |
| 591 | // |
| 592 | // The one twist here is that the DBI needs to communicate with the IDacDbiInterface |
| 593 | // implementation on the device BEFORE it can verify the product versions. This means that we need to |
| 594 | // have one IDacDbiInterface API which is consistent across all versions of the IDacDbiInterface. |
| 595 | // This puts two constraints on CheckDbiVersion(): |
| 596 | // |
| 597 | // 1. It has to be the first API on the IDacDbiInterface. |
| 598 | // - Otherwise, a wrong version of the DBI may end up calling a different API on the |
| 599 | // IDacDbiInterface and getting random results. (Really what matters is that it is |
| 600 | // protocol message id 0, at present the source code position implies the message id) |
| 601 | // |
| 602 | // 2. Its parameters cannot change. |
| 603 | // - Otherwise, we may run into random errors when we marshal/unmarshal the arguments for the |
| 604 | // call to CheckDbiVersion(). Debugging will still fail, but we won't get the |
| 605 | // version mismatch error. (Again, the protocol is what ultimately matters) |
| 606 | // - To mitigate the impact of this constraint, we use the code:DbiVersion structure. |
| 607 | // In addition to the DBI version, it also contains a format number (in case we decide to |
| 608 | // check something else in the future), a breaking change number so that we can force |
| 609 | // breaking changes between a DBI and a DAC, and space reserved for future use. |
| 610 | // |
| 611 | // 2. DBI vs. DAC |
| 612 | // - Desktop and Windows CoreCLR (old architecture) |
| 613 | // No verification is done. There is a transitive implication that if DBI matches runtime and DAC matches |
| 614 | // runtime then DBI matches DAC. Technically because the DBI only matches runtime on major version number |
| 615 | // runtime and DAC could be from different builds. However because we service all three binaries together |
| 616 | // and DBI always loads the DAC that is sitting in the same directory DAC and DBI generally get tight |
| 617 | // version coupling. A user with admin privleges could put different builds together and no version check |
| 618 | // would ever fail though. |
| 619 | // |
| 620 | // - Desktop and Windows CoreCLR (new architecture) |
| 621 | // No verification is done. Similar to above its implied that if DBI matches runtime and runtime matches |
| 622 | // DAC then DBI matches DAC. The only difference is that here both the DBI and DAC are provided by the |
| 623 | // debugger. We provide timestamp and filesize for both binaries which are relatively strongly bound hints, |
| 624 | // but there is no enforcement on the returned binaries beyond the runtime compat checking. |
| 625 | // |
| 626 | // - Remote transport (Mac CoreCLR and CoreSystem CoreCLR) |
| 627 | // Because the transport exists between DBI and DAC it becomes much more important to do a versioning check |
| 628 | // |
| 629 | // Mac - currently does a tightly bound version check between DBI and the runtime (CheckDbiVersion() above), |
| 630 | // which transitively gives a tightly bound check to DAC. In same function there is also a check that is |
| 631 | // logically a DAC DBI protocol check, verifying that the m_dwProtocolBreakingChangeCounter of DbiVersion |
| 632 | // matches. However this check should be weaker than the build version check and doesn't add anything here. |
| 633 | // |
| 634 | // CoreSystem - currently skips the tightly bound version check to make internal deployment and usage easier. |
| 635 | // We want to use old desktop side debugger components to target newer CoreCLR builds, only forcing a desktop |
| 636 | // upgrade when the protocol actually does change. To do this we use two checks: |
| 637 | // 1. The breaking change counter in CheckDbiVersion() whenever a dev knows they are breaking back |
| 638 | // compat and wants to be explicit about it. This is the same as mac above. |
| 639 | // 2. During the auto-generation of the DDMarshal classes we take an MD5 hash of IDacDbiInterface source |
| 640 | // code and embed it in two DDMarshal functions, one which runs locally and one that runs remotely. |
| 641 | // If both DBI and DAC were built from the same source then the local and remote hashes will match. If the |
| 642 | // hashes don't match then we assume there has been a been a breaking change in the protocol. Note |
| 643 | // this hash could have both false-positives and false-negatives. False positives could occur when |
| 644 | // IDacDbiInterface is changed in a trivial way, such as changing a comment. False negatives could |
| 645 | // occur when the semantics of the protocol are changed even though the interface is not. Another |
| 646 | // case would be changing the DDMarshal proxy generation code. In addition to the hashes we also |
| 647 | // embed timestamps when the auto-generated code was produced. However this isn't used for version |
| 648 | // matching, only as a hint to indicate which of two mismatched versions is newer. |
| 649 | // |
| 650 | // |
| 651 | // 3. Runtime vs. DAC |
| 652 | // - Desktop, Windows CoreCLR, CoreSystem CoreCLR |
| 653 | // In both cases we check this by matching the timestamp in the debug directory of the runtime image |
| 654 | // and the timestamp we store in the DAC table when we generate the DAC dll. This is done in |
| 655 | // code:ClrDataAccess::VerifyDlls. |
| 656 | // |
| 657 | // - Mac CoreCLR |
| 658 | // On Mac, we don't have a timestamp in the runtime image. Instead, we rely on checking the 16-byte |
| 659 | // UUID in the image. This UUID is used to check whether a symbol file matches the image, so |
| 660 | // conceptually it's the same as the timestamp we use on Windows. This is also done in |
| 661 | // code:ClrDataAccess::VerifyDlls. |
| 662 | // |
| 663 | //--------------------------------------------------------------------------------------- |
| 664 | // |
| 665 | // Instantiates a DacDbi Interface object in a live-debugging scenario that matches |
| 666 | // the current instance of mscorwks in this process. |
| 667 | // |
| 668 | // Return Value: |
| 669 | // Returns on success. Else throws. |
| 670 | // |
| 671 | // Assumptions: |
| 672 | // Client will code:CordbProcess::FreeDac when its done with the DacDbi interface. |
| 673 | // Caller has initialized clrInstanceId. |
| 674 | // |
| 675 | // Notes: |
| 676 | // This looks for the DAC next to this current DBI. This assumes that Dac and Dbi are both on |
| 677 | // the local file system. That assumption will break in zero-copy deployment scenarios. |
| 678 | // |
| 679 | //--------------------------------------------------------------------------------------- |
| 680 | void |
| 681 | CordbProcess::CreateDacDbiInterface() |
| 682 | { |
| 683 | _ASSERTE(m_pDACDataTarget != NULL); |
| 684 | _ASSERTE(m_pDacPrimitives == NULL); // don't double-init |
| 685 | |
| 686 | // Caller has already determined which CLR in the target is being debugged. |
| 687 | _ASSERTE(m_clrInstanceId != 0); |
| 688 | |
| 689 | m_pDacPrimitives = NULL; |
| 690 | |
| 691 | HRESULT hrStatus = S_OK; |
| 692 | |
| 693 | // Non-marshalling path for live local dac. |
| 694 | // in the new arch we can get the module from OpenVirtualProcess2 but in the shim case |
| 695 | // and the deprecated OpenVirtualProcess case we must assume it comes from DAC in the |
| 696 | // same directory as DBI |
| 697 | if(m_hDacModule == NULL) |
| 698 | { |
| 699 | m_hDacModule.Assign(ShimProcess::GetDacModule()); |
| 700 | } |
| 701 | |
| 702 | // |
| 703 | // Get the access interface, passing our callback interfaces (data target, allocator and metadata lookup) |
| 704 | // |
| 705 | |
| 706 | IDacDbiInterface::IAllocator * pAllocator = this; |
| 707 | IDacDbiInterface::IMetaDataLookup * pMetaDataLookup = this; |
| 708 | |
| 709 | |
| 710 | typedef HRESULT (STDAPICALLTYPE * PFN_DacDbiInterfaceInstance)( |
| 711 | ICorDebugDataTarget *, |
| 712 | CORDB_ADDRESS, |
| 713 | IDacDbiInterface::IAllocator *, |
| 714 | IDacDbiInterface::IMetaDataLookup *, |
| 715 | IDacDbiInterface **); |
| 716 | |
| 717 | IDacDbiInterface* pInterfacePtr = NULL; |
| 718 | PFN_DacDbiInterfaceInstance pfnEntry = (PFN_DacDbiInterfaceInstance)GetProcAddress(m_hDacModule, "DacDbiInterfaceInstance" ); |
| 719 | if (!pfnEntry) |
| 720 | { |
| 721 | ThrowLastError(); |
| 722 | } |
| 723 | |
| 724 | hrStatus = pfnEntry(m_pDACDataTarget, m_clrInstanceId, pAllocator, pMetaDataLookup, &pInterfacePtr); |
| 725 | IfFailThrow(hrStatus); |
| 726 | |
| 727 | // We now have a resource, pInterfacePtr, that needs to be freed. |
| 728 | m_pDacPrimitives = pInterfacePtr; |
| 729 | |
| 730 | // Setup DAC target consistency checking based on what we're using for DBI |
| 731 | m_pDacPrimitives->DacSetTargetConsistencyChecks( m_fAssertOnTargetInconsistency ); |
| 732 | } |
| 733 | |
| 734 | //--------------------------------------------------------------------------------------- |
| 735 | // |
| 736 | // Is the DAC/DBI interface initialized? |
| 737 | // |
| 738 | // Return Value: |
| 739 | // TRUE iff init. |
| 740 | // |
| 741 | // Notes: |
| 742 | // The RS will try to initialize DD as soon as it detects the runtime as loaded. |
| 743 | // If the DD interface has not initialized, then it very likely the runtime has not |
| 744 | // been loaded into the target. |
| 745 | // |
| 746 | BOOL CordbProcess::IsDacInitialized() |
| 747 | { |
| 748 | return m_pDacPrimitives != NULL; |
| 749 | } |
| 750 | |
| 751 | //--------------------------------------------------------------------------------------- |
| 752 | // |
| 753 | // Get the DAC interface. |
| 754 | // |
| 755 | // Return Value: |
| 756 | // the Dac/Dbi interface pointer to the process. |
| 757 | // Never returns NULL. |
| 758 | // |
| 759 | // Assumptions: |
| 760 | // Caller is responsible for ensuring Data-Target is safe to access (eg, not |
| 761 | // currently running). |
| 762 | // Caller is responsible for ensuring DAC-cache is flushed. Call code:CordbProcess::ForceDacFlush |
| 763 | // as needed. |
| 764 | // |
| 765 | //--------------------------------------------------------------------------------------- |
| 766 | IDacDbiInterface * CordbProcess::GetDAC() |
| 767 | { |
| 768 | // Since the DD primitives may throw, easiest way to model that is to make this throw. |
| 769 | CONTRACTL |
| 770 | { |
| 771 | THROWS; |
| 772 | } |
| 773 | CONTRACTL_END; |
| 774 | |
| 775 | // We should always have the DAC/DBI interface. |
| 776 | _ASSERTE(m_pDacPrimitives != NULL); |
| 777 | return m_pDacPrimitives; |
| 778 | } |
| 779 | |
| 780 | //--------------------------------------------------------------------------------------- |
| 781 | // Get the Data-Target |
| 782 | // |
| 783 | // Returns: |
| 784 | // pointer to the data-target. Should be non-null. |
| 785 | // Lifetime of the pointer is until this process object is neutered. |
| 786 | // |
| 787 | ICorDebugDataTarget * CordbProcess::GetDataTarget() |
| 788 | { |
| 789 | return m_pDACDataTarget; |
| 790 | } |
| 791 | |
| 792 | //--------------------------------------------------------------------------------------- |
| 793 | // Create a CordbProcess object around an existing OS process. |
| 794 | // |
| 795 | // Arguments: |
| 796 | // pDataTarget - abstracts access to the debuggee. |
| 797 | // clrInstanceId - identifies the CLR instance within the debuggee. (This is the |
| 798 | // base address of mscorwks) |
| 799 | // pCordb - Pointer to the implementation of the owning Cordb object implementing the |
| 800 | // owning ICD interface. |
| 801 | // This should go away - we can get the functionality from the pShim. |
| 802 | // If this is null, then pShim must be null too. |
| 803 | // processID - OS process ID of target process. 0 if pShim == NULL. |
| 804 | // pShim - shim counter part object. This allows hooks back for v2 compat. This will |
| 805 | // go away once we no longer support V2 backwards compat. |
| 806 | // This must be non-null for any V2 paths (including non-DAC-ized code). |
| 807 | // If this is null, then we're in a V3 path. |
| 808 | // ppProcess - out parameter for new process object. This gets addreffed. |
| 809 | // |
| 810 | // Return Value: |
| 811 | // S_OK on success, and *ppProcess set to newly created debuggee object. Else error. |
| 812 | // |
| 813 | // Notes: |
| 814 | // @dbgtodo - , shim: Cordb, and pShim will all eventually go away. |
| 815 | // |
| 816 | //--------------------------------------------------------------------------------------- |
| 817 | |
| 818 | // static |
| 819 | HRESULT CordbProcess::OpenVirtualProcess( |
| 820 | ULONG64 clrInstanceId, |
| 821 | IUnknown * pDataTarget, |
| 822 | HMODULE hDacModule, |
| 823 | Cordb* pCordb, |
| 824 | const ProcessDescriptor * pProcessDescriptor, |
| 825 | ShimProcess * pShim, |
| 826 | CordbProcess ** ppProcess) |
| 827 | { |
| 828 | _ASSERTE(pDataTarget != NULL); |
| 829 | |
| 830 | // In DEBUG builds, verify that we do actually have an ICorDebugDataTarget (i.e. that |
| 831 | // someone hasn't messed up the COM interop marshalling, etc.). |
| 832 | #ifdef _DEBUG |
| 833 | { |
| 834 | IUnknown * pTempDt; |
| 835 | HRESULT hrQi = pDataTarget->QueryInterface(IID_ICorDebugDataTarget, (void**)&pTempDt); |
| 836 | _ASSERTE_MSG(SUCCEEDED(hrQi), "OpenVirtualProcess was passed something that isn't actually an ICorDebugDataTarget" ); |
| 837 | pTempDt->Release(); |
| 838 | } |
| 839 | #endif |
| 840 | |
| 841 | // If we're emulating V2, then both pCordb and pShim are non-NULL. |
| 842 | // If we're doing a real V3 path, then they're both NULL. |
| 843 | // Either way, they should have the same null-status. |
| 844 | _ASSERTE((pCordb == NULL) == (pShim == NULL)); |
| 845 | |
| 846 | // If we're doing real V3, then we must have a real instance ID |
| 847 | _ASSERTE(!((pShim == NULL) && (clrInstanceId == 0))); |
| 848 | |
| 849 | *ppProcess = NULL; |
| 850 | |
| 851 | HRESULT hr = S_OK; |
| 852 | RSUnsafeExternalSmartPtr<CordbProcess> pProcess; |
| 853 | pProcess.Assign(new (nothrow) CordbProcess(clrInstanceId, pDataTarget, hDacModule, pCordb, pProcessDescriptor, pShim)); |
| 854 | |
| 855 | if (pProcess == NULL) |
| 856 | { |
| 857 | return E_OUTOFMEMORY; |
| 858 | } |
| 859 | |
| 860 | ICorDebugProcess * pThis = pProcess; |
| 861 | (void)pThis; //prevent "unused variable" error from GCC |
| 862 | |
| 863 | // CordbProcess::Init may need shim hooks, so connect Shim now. |
| 864 | // This will bump reference count. |
| 865 | if (pShim != NULL) |
| 866 | { |
| 867 | pShim->SetProcess(pProcess); |
| 868 | |
| 869 | _ASSERTE(pShim->GetProcess() == pThis); |
| 870 | _ASSERTE(pShim->GetWin32EventThread() != NULL); |
| 871 | } |
| 872 | |
| 873 | hr = pProcess->Init(); |
| 874 | |
| 875 | if (SUCCEEDED(hr)) |
| 876 | { |
| 877 | *ppProcess = pProcess; |
| 878 | pProcess->ExternalAddRef(); |
| 879 | } |
| 880 | else |
| 881 | { |
| 882 | // handle failure path |
| 883 | pProcess->CleanupHalfBakedLeftSide(); |
| 884 | |
| 885 | if (pShim != NULL) |
| 886 | { |
| 887 | // Shim still needs to be disposed to clean up other resources. |
| 888 | pShim->SetProcess(NULL); |
| 889 | } |
| 890 | |
| 891 | // In failure case, pProcess's dtor will do the final release. |
| 892 | } |
| 893 | |
| 894 | |
| 895 | return hr; |
| 896 | } |
| 897 | |
| 898 | //--------------------------------------------------------------------------------------- |
| 899 | // CordbProcess constructor |
| 900 | // |
| 901 | // Arguments: |
| 902 | // pDataTarget - Pointer to an implementation of ICorDebugDataTarget |
| 903 | // (or ICorDebugMutableDataTarget), which virtualizes access to the process. |
| 904 | // clrInstanceId - representation of the CLR to debug in the process. Must be specified |
| 905 | // (non-zero) if pShim is NULL. If 0, use the first CLR that we see. |
| 906 | // pCordb - Pointer to the implementation of the owning Cordb object implementing the |
| 907 | // owning ICD interface. |
| 908 | // pW32 - Pointer to the Win32 event thread to use when processing events for this |
| 909 | // process. |
| 910 | // dwProcessID - For V3, 0. |
| 911 | // Else for shim codepaths, the processID of the process this object will represent. |
| 912 | // pShim - Pointer to the shim for handling V2 debuggers on the V3 architecture. |
| 913 | // |
| 914 | //--------------------------------------------------------------------------------------- |
| 915 | |
| 916 | CordbProcess::CordbProcess(ULONG64 clrInstanceId, |
| 917 | IUnknown * pDataTarget, |
| 918 | HMODULE hDacModule, |
| 919 | Cordb * pCordb, |
| 920 | const ProcessDescriptor * pProcessDescriptor, |
| 921 | ShimProcess * pShim) |
| 922 | : CordbBase(NULL, pProcessDescriptor->m_Pid, enumCordbProcess), |
| 923 | m_fDoDelayedManagedAttached(false), |
| 924 | m_cordb(pCordb), |
| 925 | m_handle(NULL), |
| 926 | m_processDescriptor(*pProcessDescriptor), |
| 927 | m_detached(false), |
| 928 | m_uninitializedStop(false), |
| 929 | m_exiting(false), |
| 930 | m_terminated(false), |
| 931 | m_unrecoverableError(false), |
| 932 | m_specialDeferment(false), |
| 933 | m_helperThreadDead(false), |
| 934 | m_loaderBPReceived(false), |
| 935 | m_cOutstandingEvals(0), |
| 936 | m_cOutstandingHandles(0), |
| 937 | m_clrInstanceId(clrInstanceId), |
| 938 | m_stopCount(0), |
| 939 | m_synchronized(false), |
| 940 | m_syncCompleteReceived(false), |
| 941 | m_pShim(pShim), |
| 942 | m_userThreads(11), |
| 943 | m_oddSync(false), |
| 944 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 945 | m_unmanagedThreads(11), |
| 946 | #endif |
| 947 | m_appDomains(11), |
| 948 | m_sharedAppDomain(0), |
| 949 | m_steppers(11), |
| 950 | m_continueCounter(1), |
| 951 | m_flushCounter(0), |
| 952 | m_leftSideEventAvailable(NULL), |
| 953 | m_leftSideEventRead(NULL), |
| 954 | #if defined(FEATURE_INTEROP_DEBUGGING) |
| 955 | m_leftSideUnmanagedWaitEvent(NULL), |
| 956 | #endif // FEATURE_INTEROP_DEBUGGING |
| 957 | m_initialized(false), |
| 958 | m_stopRequested(false), |
| 959 | m_stopWaitEvent(NULL), |
| 960 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 961 | m_cFirstChanceHijackedThreads(0), |
| 962 | m_unmanagedEventQueue(NULL), |
| 963 | m_lastQueuedUnmanagedEvent(NULL), |
| 964 | m_lastQueuedOOBEvent(NULL), |
| 965 | m_outOfBandEventQueue(NULL), |
| 966 | m_lastDispatchedIBEvent(NULL), |
| 967 | m_dispatchingUnmanagedEvent(false), |
| 968 | m_dispatchingOOBEvent(false), |
| 969 | m_doRealContinueAfterOOBBlock(false), |
| 970 | m_state(0), |
| 971 | #endif // FEATURE_INTEROP_DEBUGGING |
| 972 | m_helperThreadId(0), |
| 973 | m_pPatchTable(NULL), |
| 974 | m_cPatch(0), |
| 975 | m_rgData(NULL), |
| 976 | m_rgNextPatch(NULL), |
| 977 | m_rgUncommitedOpcode(NULL), |
| 978 | m_minPatchAddr(MAX_ADDRESS), |
| 979 | m_maxPatchAddr(MIN_ADDRESS), |
| 980 | m_iFirstPatch(0), |
| 981 | m_hHelperThread(NULL), |
| 982 | m_dispatchedEvent(DB_IPCE_DEBUGGER_INVALID), |
| 983 | m_pDefaultAppDomain(NULL), |
| 984 | m_hDacModule(hDacModule), |
| 985 | m_pDacPrimitives(NULL), |
| 986 | m_pEventChannel(NULL), |
| 987 | m_fAssertOnTargetInconsistency(false), |
| 988 | m_runtimeOffsetsInitialized(false), |
| 989 | m_writableMetadataUpdateMode(LegacyCompatPolicy) |
| 990 | { |
| 991 | _ASSERTE((m_id == 0) == (pShim == NULL)); |
| 992 | |
| 993 | HRESULT hr = pDataTarget->QueryInterface(IID_ICorDebugDataTarget, reinterpret_cast<void **>(&m_pDACDataTarget)); |
| 994 | IfFailThrow(hr); |
| 995 | |
| 996 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 997 | m_DbgSupport.m_DebugEventQueueIdx = 0; |
| 998 | m_DbgSupport.m_TotalNativeEvents = 0; |
| 999 | m_DbgSupport.m_TotalIB = 0; |
| 1000 | m_DbgSupport.m_TotalOOB = 0; |
| 1001 | m_DbgSupport.m_TotalCLR = 0; |
| 1002 | #endif // FEATURE_INTEROP_DEBUGGING |
| 1003 | |
| 1004 | g_pRSDebuggingInfo->m_MRUprocess = this; |
| 1005 | |
| 1006 | // This is a strong reference to ourselves. |
| 1007 | // This is cleared in code:CordbProcess::Neuter |
| 1008 | m_pProcess.Assign(this); |
| 1009 | |
| 1010 | #ifdef _DEBUG |
| 1011 | // On Debug builds, we'll ASSERT by default whenever the target appears to be corrupt or |
| 1012 | // otherwise inconsistent (both in DAC and DBI). But we also need the ability to |
| 1013 | // explicitly test corrupt targets. |
| 1014 | // Tests should set COMPlus_DbgIgnoreInconsistentTarget=1 to suppress these asserts |
| 1015 | // Note that this controls two things: |
| 1016 | // 1) DAC behavior - see code:IDacDbiInterface::DacSetTargetConsistencyChecks |
| 1017 | // 2) RS-only consistency asserts - see code:CordbProcess::TargetConsistencyCheck |
| 1018 | if( !CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDisableTargetConsistencyAsserts) ) |
| 1019 | { |
| 1020 | m_fAssertOnTargetInconsistency = true; |
| 1021 | } |
| 1022 | #endif |
| 1023 | } |
| 1024 | |
| 1025 | /* |
| 1026 | A list of which resources owned by this object are accounted for. |
| 1027 | |
| 1028 | UNKNOWN |
| 1029 | Cordb* m_cordb; |
| 1030 | CordbHashTable m_unmanagedThreads; // Released in CordbProcess but not removed from hash |
| 1031 | DebuggerIPCEvent* m_lastQueuedEvent; |
| 1032 | |
| 1033 | // CordbUnmannagedEvent is a struct which is not derrived from CordbBase. |
| 1034 | // It contains a CordbUnmannagedThread which may need to be released. |
| 1035 | CordbUnmanagedEvent *m_unmanagedEventQueue; |
| 1036 | CordbUnmanagedEvent *m_lastQueuedUnmanagedEvent; |
| 1037 | CordbUnmanagedEvent *m_outOfBandEventQueue; |
| 1038 | CordbUnmanagedEvent *m_lastQueuedOOBEvent; |
| 1039 | |
| 1040 | BYTE* m_pPatchTable; |
| 1041 | BYTE *m_rgData; |
| 1042 | void *m_pbRemoteBuf; |
| 1043 | |
| 1044 | RESOLVED |
| 1045 | // Nutered |
| 1046 | CordbHashTable m_userThreads; |
| 1047 | CordbHashTable m_appDomains; |
| 1048 | |
| 1049 | // Cleaned up in ExitProcess |
| 1050 | DebuggerIPCEvent* m_queuedEventList; |
| 1051 | |
| 1052 | CordbHashTable m_steppers; // Closed in ~CordbProcess |
| 1053 | |
| 1054 | // Closed in CloseIPCEventHandles called from ~CordbProcess |
| 1055 | HANDLE m_leftSideEventAvailable; |
| 1056 | HANDLE m_leftSideEventRead; |
| 1057 | |
| 1058 | // Closed in ~CordbProcess |
| 1059 | HANDLE m_handle; |
| 1060 | HANDLE m_leftSideUnmanagedWaitEvent; |
| 1061 | HANDLE m_stopWaitEvent; |
| 1062 | |
| 1063 | // Deleted in ~CordbProcess |
| 1064 | CRITICAL_SECTION m_processMutex; |
| 1065 | |
| 1066 | */ |
| 1067 | |
| 1068 | |
| 1069 | CordbProcess::~CordbProcess() |
| 1070 | { |
| 1071 | LOG((LF_CORDB, LL_INFO1000, "CP::~CP: deleting process 0x%08x\n" , this)); |
| 1072 | |
| 1073 | DTOR_ENTRY(this); |
| 1074 | |
| 1075 | _ASSERTE(IsNeutered()); |
| 1076 | |
| 1077 | _ASSERTE(m_cordb == NULL); |
| 1078 | |
| 1079 | // We shouldn't still be in Cordb's list of processes. Unfortunately, our root Cordb object |
| 1080 | // may have already been deleted b/c we're at the mercy of ref-counting, so we can't check. |
| 1081 | |
| 1082 | _ASSERTE(m_sharedAppDomain == NULL); |
| 1083 | |
| 1084 | m_processMutex.Destroy(); |
| 1085 | m_StopGoLock.Destroy(); |
| 1086 | |
| 1087 | // These handles were cleared in neuter |
| 1088 | _ASSERTE(m_handle == NULL); |
| 1089 | #if defined(FEATURE_INTEROP_DEBUGGING) |
| 1090 | _ASSERTE(m_leftSideUnmanagedWaitEvent == NULL); |
| 1091 | #endif // FEATURE_INTEROP_DEBUGGING |
| 1092 | _ASSERTE(m_stopWaitEvent == NULL); |
| 1093 | |
| 1094 | // Set this to mark that we really did cleanup. |
| 1095 | } |
| 1096 | |
| 1097 | //----------------------------------------------------------------------------- |
| 1098 | // Static build helper. |
| 1099 | // This will create a process under the pCordb root, and add it to the list. |
| 1100 | // We don't return the process - caller gets the pid and looks it up under |
| 1101 | // the Cordb object. |
| 1102 | // |
| 1103 | // Arguments: |
| 1104 | // pCordb - Pointer to the implementation of the owning Cordb object implementing the |
| 1105 | // owning ICD interface. |
| 1106 | // szProgramName - Name of the program to execute. |
| 1107 | // szProgramArgs - Command line arguments for the process. |
| 1108 | // lpProcessAttributes - OS-specific attributes for process creation. |
| 1109 | // lpThreadAttributes - OS-specific attributes for thread creation. |
| 1110 | // fInheritFlags - OS-specific flag for child process inheritance. |
| 1111 | // dwCreationFlags - OS-specific creation flags. |
| 1112 | // lpEnvironment - OS-specific environmental strings. |
| 1113 | // szCurrentDirectory - OS-specific string for directory to run in. |
| 1114 | // lpStartupInfo - OS-specific info on startup. |
| 1115 | // lpProcessInformation - OS-specific process information buffer. |
| 1116 | // corDebugFlags - What type of process to create, currently always managed. |
| 1117 | //----------------------------------------------------------------------------- |
| 1118 | HRESULT ShimProcess::CreateProcess( |
| 1119 | Cordb * pCordb, |
| 1120 | ICorDebugRemoteTarget * pRemoteTarget, |
| 1121 | LPCWSTR szProgramName, |
| 1122 | __in_z LPWSTR szProgramArgs, |
| 1123 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
| 1124 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
| 1125 | BOOL fInheritHandles, |
| 1126 | DWORD dwCreationFlags, |
| 1127 | PVOID lpEnvironment, |
| 1128 | LPCWSTR szCurrentDirectory, |
| 1129 | LPSTARTUPINFOW lpStartupInfo, |
| 1130 | LPPROCESS_INFORMATION lpProcessInformation, |
| 1131 | CorDebugCreateProcessFlags corDebugFlags |
| 1132 | ) |
| 1133 | { |
| 1134 | _ASSERTE(pCordb != NULL); |
| 1135 | |
| 1136 | #if defined(FEATURE_DBGIPC_TRANSPORT_DI) |
| 1137 | // The transport cannot deal with creating a suspended process (it needs the debugger to start up and |
| 1138 | // listen for connections). |
| 1139 | _ASSERTE((dwCreationFlags & CREATE_SUSPENDED) == 0); |
| 1140 | #endif // FEATURE_DBGIPC_TRANSPORT_DI |
| 1141 | |
| 1142 | HRESULT hr = S_OK; |
| 1143 | |
| 1144 | RSExtSmartPtr<ShimProcess> pShim; |
| 1145 | EX_TRY |
| 1146 | { |
| 1147 | pShim.Assign(new ShimProcess()); |
| 1148 | |
| 1149 | // Indicate that this process was started under the debugger as opposed to attaching later. |
| 1150 | pShim->m_attached = false; |
| 1151 | |
| 1152 | hr = pShim->CreateAndStartWin32ET(pCordb); |
| 1153 | IfFailThrow(hr); |
| 1154 | |
| 1155 | // Call out to newly created Win32-event Thread to create the process. |
| 1156 | // If this succeeds, new CordbProcess will add a ref to the ShimProcess |
| 1157 | hr = pShim->GetWin32EventThread()->SendCreateProcessEvent(pShim->GetMachineInfo(), |
| 1158 | szProgramName, |
| 1159 | szProgramArgs, |
| 1160 | lpProcessAttributes, |
| 1161 | lpThreadAttributes, |
| 1162 | fInheritHandles, |
| 1163 | dwCreationFlags, |
| 1164 | lpEnvironment, |
| 1165 | szCurrentDirectory, |
| 1166 | lpStartupInfo, |
| 1167 | lpProcessInformation, |
| 1168 | corDebugFlags); |
| 1169 | IfFailThrow(hr); |
| 1170 | } |
| 1171 | EX_CATCH_HRESULT(hr); |
| 1172 | |
| 1173 | // If this succeeds, then process takes ownership of thread. Else we need to kill it. |
| 1174 | if (FAILED(hr)) |
| 1175 | { |
| 1176 | if (pShim != NULL) |
| 1177 | { |
| 1178 | pShim->Dispose(); |
| 1179 | } |
| 1180 | } |
| 1181 | // Always release our ref to ShimProcess. If the Process was created, then it takes a reference. |
| 1182 | |
| 1183 | return hr; |
| 1184 | } |
| 1185 | |
| 1186 | //----------------------------------------------------------------------------- |
| 1187 | // Static build helper for the attach case. |
| 1188 | // On success, this will add the process to the pCordb list, and then |
| 1189 | // callers can look it up there by pid. |
| 1190 | // |
| 1191 | // Arguments: |
| 1192 | // pCordb - root under which this all lives |
| 1193 | // dwProcessID - OS process ID to attach to |
| 1194 | // fWin32Attach - are we interop debugging? |
| 1195 | //----------------------------------------------------------------------------- |
| 1196 | HRESULT ShimProcess::DebugActiveProcess( |
| 1197 | Cordb * pCordb, |
| 1198 | ICorDebugRemoteTarget * pRemoteTarget, |
| 1199 | const ProcessDescriptor * pProcessDescriptor, |
| 1200 | BOOL fWin32Attach |
| 1201 | ) |
| 1202 | { |
| 1203 | _ASSERTE(pCordb != NULL); |
| 1204 | |
| 1205 | HRESULT hr = S_OK; |
| 1206 | |
| 1207 | RSExtSmartPtr<ShimProcess> pShim; |
| 1208 | |
| 1209 | EX_TRY |
| 1210 | { |
| 1211 | pShim.Assign(new ShimProcess()); |
| 1212 | |
| 1213 | // Indicate that this process was attached to, asopposed to being started under the debugger. |
| 1214 | pShim->m_attached = true; |
| 1215 | |
| 1216 | hr = pShim->CreateAndStartWin32ET(pCordb); |
| 1217 | IfFailThrow(hr); |
| 1218 | |
| 1219 | // If this succeeds, new CordbProcess will add a ref to the ShimProcess |
| 1220 | hr = pShim->GetWin32EventThread()->SendDebugActiveProcessEvent(pShim->GetMachineInfo(), |
| 1221 | pProcessDescriptor, |
| 1222 | fWin32Attach == TRUE, |
| 1223 | NULL); |
| 1224 | IfFailThrow(hr); |
| 1225 | |
| 1226 | _ASSERTE(SUCCEEDED(hr)); |
| 1227 | |
| 1228 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
| 1229 | // Don't do this when we are remote debugging since we won't be getting the loader breakpoint. |
| 1230 | // We don't support JIT attach in remote debugging scenarios anyway. |
| 1231 | // |
| 1232 | // When doing jit attach for pure managed debugging we allow the native attach event to be signaled |
| 1233 | // after DebugActiveProcess completes which means we must wait here long enough to have set the debuggee |
| 1234 | // bit indicating managed attach is coming. |
| 1235 | // However in interop debugging we can't do that because there are debug events which come before the |
| 1236 | // loader breakpoint (which is how far we need to get to set the debuggee bit). If we blocked |
| 1237 | // DebugActiveProcess there then the debug events would be refering to an ICorDebugProcess that hasn't |
| 1238 | // yet been returned to the caller of DebugActiveProcess. Instead, for interop debugging we force the |
| 1239 | // native debugger to wait until it gets the loader breakpoint to set the event. Note we can't converge |
| 1240 | // on that solution for the pure managed case because there is no loader breakpoint event. Hence pure |
| 1241 | // managed and interop debugging each require their own solution |
| 1242 | // |
| 1243 | // See bugs Dev10 600873 and 595322 for examples of what happens if we wait in interop or don't wait |
| 1244 | // in pure managed respectively |
| 1245 | // |
| 1246 | // Long term this should all go away because we won't need to set a managed attach pending bit because |
| 1247 | // there shouldn't be any IPC events involved in managed attach. There might not even be a notion of |
| 1248 | // being 'managed attached' |
| 1249 | if(!pShim->m_fIsInteropDebugging) |
| 1250 | { |
| 1251 | DWORD dwHandles = 2; |
| 1252 | HANDLE arrHandles[2]; |
| 1253 | |
| 1254 | arrHandles[0] = pShim->m_terminatingEvent; |
| 1255 | arrHandles[1] = pShim->m_markAttachPendingEvent; |
| 1256 | |
| 1257 | // Wait for the completion of marking pending attach bit or debugger detaching |
| 1258 | WaitForMultipleObjectsEx(dwHandles, arrHandles, FALSE, INFINITE, FALSE); |
| 1259 | } |
| 1260 | #endif //!FEATURE_DBGIPC_TRANSPORT_DI |
| 1261 | } |
| 1262 | EX_CATCH_HRESULT(hr); |
| 1263 | |
| 1264 | // If this succeeds, then process takes ownership of thread. Else we need to kill it. |
| 1265 | if (FAILED(hr)) |
| 1266 | { |
| 1267 | if (pShim!= NULL) |
| 1268 | { |
| 1269 | pShim->Dispose(); |
| 1270 | } |
| 1271 | } |
| 1272 | |
| 1273 | // Always release our ref to ShimProcess. If the Process was created, then it takes a reference. |
| 1274 | |
| 1275 | return hr; |
| 1276 | } |
| 1277 | |
| 1278 | //----------------------------------------------------------------------------- |
| 1279 | // Neuter all of all children, but not the actual process object. |
| 1280 | // |
| 1281 | // Assumptions: |
| 1282 | // This clears Right-side state. Assumptions about left-side state are either: |
| 1283 | // 1. We're in a shutdown scenario, where all left-side state is already |
| 1284 | // freed. |
| 1285 | // 2. Caller already verified there are no left-side resources (eg, by calling |
| 1286 | // code:CordbProcess::IsReadyForDetach) |
| 1287 | // 3. Caller did code:CordbProcess::NeuterLeftSideResources first |
| 1288 | // to clean up left-side resources. |
| 1289 | // |
| 1290 | // Notes: |
| 1291 | // This could be called multiple times (code:CordbProcess::FlushAll), so |
| 1292 | // be sure to null out any potential dangling pointers. State may be rebuilt |
| 1293 | // up after each time. |
| 1294 | void CordbProcess::NeuterChildren() |
| 1295 | { |
| 1296 | _ASSERTE(GetProcessLock()->HasLock()); |
| 1297 | |
| 1298 | // Frees left-side resources. See assumptions above. |
| 1299 | m_LeftSideResourceCleanupList.NeuterAndClear(this); |
| 1300 | |
| 1301 | |
| 1302 | m_EvalTable.Clear(); |
| 1303 | |
| 1304 | |
| 1305 | // Sweep neuter lists. |
| 1306 | m_ExitNeuterList.NeuterAndClear(this); |
| 1307 | m_ContinueNeuterList.NeuterAndClear(this); |
| 1308 | |
| 1309 | m_userThreads.NeuterAndClear(GetProcessLock()); |
| 1310 | |
| 1311 | m_pDefaultAppDomain = NULL; |
| 1312 | |
| 1313 | // Frees per-appdomain left-side resources. See assumptions above. |
| 1314 | m_appDomains.NeuterAndClear(GetProcessLock()); |
| 1315 | if (m_sharedAppDomain != NULL) |
| 1316 | { |
| 1317 | m_sharedAppDomain->Neuter(); |
| 1318 | m_sharedAppDomain->InternalRelease(); |
| 1319 | m_sharedAppDomain = NULL; |
| 1320 | } |
| 1321 | |
| 1322 | m_steppers.NeuterAndClear(GetProcessLock()); |
| 1323 | |
| 1324 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 1325 | m_unmanagedThreads.NeuterAndClear(GetProcessLock()); |
| 1326 | #endif // FEATURE_INTEROP_DEBUGGING |
| 1327 | |
| 1328 | // Explicitly keep the Win32EventThread alive so that we can use it in the window |
| 1329 | // between NeuterChildren + Neuter. |
| 1330 | } |
| 1331 | |
| 1332 | //----------------------------------------------------------------------------- |
| 1333 | // Neuter |
| 1334 | // |
| 1335 | // When the process dies, remove all the resources associated with this object. |
| 1336 | // |
| 1337 | // Notes: |
| 1338 | // Once we neuter ourself, we can no longer send IPC events. So this is useful |
| 1339 | // on detach. This will be called on FlushAll (which has Whidbey detach |
| 1340 | // semantics) |
| 1341 | //----------------------------------------------------------------------------- |
| 1342 | void CordbProcess::Neuter() |
| 1343 | { |
| 1344 | // Process's Neuter is at the top of the neuter tree. So we take the process-lock |
| 1345 | // here and then all child items (appdomains, modules, etc) will assert |
| 1346 | // that they hold the lock. |
| 1347 | _ASSERTE(!this->ThreadHoldsProcessLock()); |
| 1348 | |
| 1349 | // Take the process lock. |
| 1350 | RSLockHolder lockHolder(GetProcessLock()); |
| 1351 | |
| 1352 | |
| 1353 | NeuterChildren(); |
| 1354 | |
| 1355 | // Release the metadata interfaces |
| 1356 | m_pMetaDispenser.Clear(); |
| 1357 | |
| 1358 | |
| 1359 | if (m_hHelperThread != NULL) |
| 1360 | { |
| 1361 | CloseHandle(m_hHelperThread); |
| 1362 | m_hHelperThread = NULL; |
| 1363 | } |
| 1364 | |
| 1365 | { |
| 1366 | lockHolder.Release(); |
| 1367 | { |
| 1368 | // We may still hold the Stop-Go lock. |
| 1369 | // @dbgtodo - left-side resources / shutdown, shim: Currently |
| 1370 | // the shim shutdown is too interwoven with CordbProcess to split |
| 1371 | // it out from the locks. Must fully hoist the W32ET and make |
| 1372 | // it safely outside the RS, and outside the protection of RS |
| 1373 | // locks. |
| 1374 | PUBLIC_API_UNSAFE_ENTRY_FOR_SHIM(this); |
| 1375 | |
| 1376 | // Now that all of our children are neutered, it should be safe to kill the W32ET. |
| 1377 | // Shutdown the shim, and this will also shutdown the W32ET. |
| 1378 | // Do this outside of the process-lock so that we can shutdown the |
| 1379 | // W23ET. |
| 1380 | if (m_pShim != NULL) |
| 1381 | { |
| 1382 | m_pShim->Dispose(); |
| 1383 | m_pShim.Clear(); |
| 1384 | } |
| 1385 | } |
| 1386 | |
| 1387 | lockHolder.Acquire(); |
| 1388 | } |
| 1389 | |
| 1390 | // Unload DAC, and then release our final data target references |
| 1391 | FreeDac(); |
| 1392 | m_pDACDataTarget.Clear(); |
| 1393 | m_pMutableDataTarget.Clear(); |
| 1394 | m_pMetaDataLocator.Clear(); |
| 1395 | |
| 1396 | if (m_pEventChannel != NULL) |
| 1397 | { |
| 1398 | m_pEventChannel->Delete(); |
| 1399 | m_pEventChannel = NULL; |
| 1400 | } |
| 1401 | |
| 1402 | // Need process lock to clear the patch table |
| 1403 | ClearPatchTable(); |
| 1404 | |
| 1405 | CordbProcess::CloseIPCHandles(); |
| 1406 | |
| 1407 | CordbBase::Neuter(); |
| 1408 | |
| 1409 | m_cordb.Clear(); |
| 1410 | |
| 1411 | // Need to release this reference to ourselves. Other leaf objects may still hold |
| 1412 | // strong references back to this CordbProcess object. |
| 1413 | _ASSERTE(m_pProcess == this); |
| 1414 | m_pProcess.Clear(); |
| 1415 | } |
| 1416 | |
| 1417 | // Wrapper to return metadata dispenser. |
| 1418 | // |
| 1419 | // Notes: |
| 1420 | // Does not adjust reference count of dispenser. |
| 1421 | // Dispenser is destroyed in code:CordbProcess::Neuter |
| 1422 | // Dispenser is non-null. |
| 1423 | IMetaDataDispenserEx * CordbProcess::GetDispenser() |
| 1424 | { |
| 1425 | _ASSERTE(m_pMetaDispenser != NULL); |
| 1426 | return m_pMetaDispenser; |
| 1427 | } |
| 1428 | |
| 1429 | |
| 1430 | void CordbProcess::CloseIPCHandles() |
| 1431 | { |
| 1432 | INTERNAL_API_ENTRY(this); |
| 1433 | |
| 1434 | // Close off Right Side's handles. |
| 1435 | if (m_leftSideEventAvailable != NULL) |
| 1436 | { |
| 1437 | CloseHandle(m_leftSideEventAvailable); |
| 1438 | m_leftSideEventAvailable = NULL; |
| 1439 | } |
| 1440 | |
| 1441 | if (m_leftSideEventRead != NULL) |
| 1442 | { |
| 1443 | CloseHandle(m_leftSideEventRead); |
| 1444 | m_leftSideEventRead = NULL; |
| 1445 | } |
| 1446 | |
| 1447 | if (m_handle != NULL) |
| 1448 | { |
| 1449 | // @dbgtodo - We should probably add asserts to all calls to CloseHandles(), but this has been |
| 1450 | // a particularly problematic spot in the past for Mac debugging. |
| 1451 | BOOL fSuccess = CloseHandle(m_handle); |
| 1452 | (void)fSuccess; //prevent "unused variable" error from GCC |
| 1453 | _ASSERTE(fSuccess); |
| 1454 | |
| 1455 | m_handle = NULL; |
| 1456 | } |
| 1457 | |
| 1458 | #if defined(FEATURE_INTEROP_DEBUGGING) |
| 1459 | if (m_leftSideUnmanagedWaitEvent != NULL) |
| 1460 | { |
| 1461 | CloseHandle(m_leftSideUnmanagedWaitEvent); |
| 1462 | m_leftSideUnmanagedWaitEvent = NULL; |
| 1463 | } |
| 1464 | #endif // FEATURE_INTEROP_DEBUGGING |
| 1465 | |
| 1466 | if (m_stopWaitEvent != NULL) |
| 1467 | { |
| 1468 | CloseHandle(m_stopWaitEvent); |
| 1469 | m_stopWaitEvent = NULL; |
| 1470 | } |
| 1471 | } |
| 1472 | |
| 1473 | |
| 1474 | //----------------------------------------------------------------------------- |
| 1475 | // Create new OS Thread for the Win32 Event Thread (the thread used in interop-debugging to sniff |
| 1476 | // native debug events). This is 1:1 w/ a CordbProcess object. |
| 1477 | // This will then be used to actuall create the CordbProcess object. |
| 1478 | // The process object will then take ownership of the thread. |
| 1479 | // |
| 1480 | // Arguments: |
| 1481 | // pCordb - the root object that the process lives under |
| 1482 | // |
| 1483 | // Return values: |
| 1484 | // S_OK on success. |
| 1485 | //----------------------------------------------------------------------------- |
| 1486 | HRESULT ShimProcess::CreateAndStartWin32ET(Cordb * pCordb) |
| 1487 | { |
| 1488 | |
| 1489 | // |
| 1490 | // Create the win32 event listening thread |
| 1491 | // |
| 1492 | CordbWin32EventThread * pWin32EventThread = new (nothrow) CordbWin32EventThread(pCordb, this); |
| 1493 | |
| 1494 | HRESULT hr = S_OK; |
| 1495 | |
| 1496 | if (pWin32EventThread != NULL) |
| 1497 | { |
| 1498 | hr = pWin32EventThread->Init(); |
| 1499 | |
| 1500 | if (SUCCEEDED(hr)) |
| 1501 | { |
| 1502 | hr = pWin32EventThread->Start(); |
| 1503 | } |
| 1504 | |
| 1505 | if (FAILED(hr)) |
| 1506 | { |
| 1507 | delete pWin32EventThread; |
| 1508 | pWin32EventThread = NULL; |
| 1509 | } |
| 1510 | } |
| 1511 | else |
| 1512 | { |
| 1513 | hr = E_OUTOFMEMORY; |
| 1514 | } |
| 1515 | |
| 1516 | m_pWin32EventThread = pWin32EventThread; |
| 1517 | return ErrWrapper(hr); |
| 1518 | } |
| 1519 | |
| 1520 | |
| 1521 | //--------------------------------------------------------------------------------------- |
| 1522 | // |
| 1523 | // Try to initialize the DAC. Called in scenarios where it may fail. |
| 1524 | // |
| 1525 | // Return Value: |
| 1526 | // TRUE - DAC is initialized. |
| 1527 | // FALSE - Not initialized, but can try again later. Common case if |
| 1528 | // target has not yet loaded the runtime. |
| 1529 | // Throws exception - fatal. |
| 1530 | // |
| 1531 | // Assumptions: |
| 1532 | // Target is stopped by OS, so we can safely inspect it without it moving on us. |
| 1533 | // |
| 1534 | // Notes: |
| 1535 | // This can be called eagerly to sniff if the LS is initialized. |
| 1536 | // |
| 1537 | //--------------------------------------------------------------------------------------- |
| 1538 | BOOL CordbProcess::TryInitializeDac() |
| 1539 | { |
| 1540 | CONTRACTL |
| 1541 | { |
| 1542 | THROWS; |
| 1543 | } |
| 1544 | CONTRACTL_END; |
| 1545 | |
| 1546 | // Target is stopped by OS, so we can safely inspect it without it moving on us. |
| 1547 | |
| 1548 | // We want to avoid exceptions in the normal case, so we do some pre-checks |
| 1549 | // to detect failure without relying on exceptions. |
| 1550 | // Can't initialize DAC until mscorwks is loaded. So that's a sanity test. |
| 1551 | HRESULT hr = EnsureClrInstanceIdSet(); |
| 1552 | if (FAILED(hr)) |
| 1553 | { |
| 1554 | return FALSE; |
| 1555 | } |
| 1556 | |
| 1557 | // By this point, we know which CLR in the target to debug. That means there is a CLR |
| 1558 | // in the target, and it's safe to initialize DAC. |
| 1559 | _ASSERTE(m_clrInstanceId != 0); |
| 1560 | |
| 1561 | // Now expect it to succeed |
| 1562 | InitializeDac(); |
| 1563 | return TRUE; |
| 1564 | } |
| 1565 | |
| 1566 | //--------------------------------------------------------------------------------------- |
| 1567 | // |
| 1568 | // Load & Init DAC, expecting to succeed. |
| 1569 | // |
| 1570 | // Return Value: |
| 1571 | // Throws on failure. |
| 1572 | // |
| 1573 | // Assumptions: |
| 1574 | // Caller invokes this at a point where they can expect it to succeed. |
| 1575 | // This is called early in the startup path because DAC is needed for accessing |
| 1576 | // data in the target. |
| 1577 | // |
| 1578 | // Notes: |
| 1579 | // This needs to succeed, and should always succeed (baring a bad installation) |
| 1580 | // so we assert on failure paths. |
| 1581 | // This may be called mutliple times. |
| 1582 | // |
| 1583 | //--------------------------------------------------------------------------------------- |
| 1584 | void CordbProcess::InitializeDac() |
| 1585 | { |
| 1586 | CONTRACTL |
| 1587 | { |
| 1588 | THROWS; |
| 1589 | } |
| 1590 | CONTRACTL_END; |
| 1591 | INTERNAL_API_ENTRY(this); |
| 1592 | |
| 1593 | // For Mac debugginger, m_hDacModule is not used, and it will always be NULL. To check whether DAC has |
| 1594 | // been initialized, we need to check something else, namely m_pDacPrimitives. |
| 1595 | if (m_pDacPrimitives == NULL) |
| 1596 | { |
| 1597 | LOG((LF_CORDB, LL_INFO1000, "About to load DAC\n" )); |
| 1598 | CreateDacDbiInterface(); // throws |
| 1599 | } |
| 1600 | else |
| 1601 | { |
| 1602 | LOG((LF_CORDB, LL_INFO1000, "Dac already loaded, 0x%p\n" , (HMODULE)m_hDacModule)); |
| 1603 | } |
| 1604 | |
| 1605 | // Always flush dac. |
| 1606 | ForceDacFlush(); |
| 1607 | } |
| 1608 | |
| 1609 | //--------------------------------------------------------------------------------------- |
| 1610 | // |
| 1611 | // Free DAC resources |
| 1612 | // |
| 1613 | // Notes: |
| 1614 | // This should clean up state such that code:CordbProcess::InitializeDac could be called again. |
| 1615 | // |
| 1616 | //--------------------------------------------------------------------------------------- |
| 1617 | void CordbProcess::FreeDac() |
| 1618 | { |
| 1619 | CONTRACTL |
| 1620 | { |
| 1621 | NOTHROW; // backout code. |
| 1622 | } |
| 1623 | CONTRACTL_END; |
| 1624 | |
| 1625 | if (m_pDacPrimitives != NULL) |
| 1626 | { |
| 1627 | m_pDacPrimitives->Destroy(); |
| 1628 | m_pDacPrimitives = NULL; |
| 1629 | } |
| 1630 | |
| 1631 | if (m_hDacModule != NULL) |
| 1632 | { |
| 1633 | LOG((LF_CORDB, LL_INFO1000, "Unloading DAC\n" )); |
| 1634 | m_hDacModule.Clear(); |
| 1635 | } |
| 1636 | } |
| 1637 | |
| 1638 | IEventChannel * CordbProcess::GetEventChannel() |
| 1639 | { |
| 1640 | _ASSERTE(m_pEventChannel != NULL); |
| 1641 | return m_pEventChannel; |
| 1642 | } |
| 1643 | |
| 1644 | //--------------------------------------------------------------------------------------- |
| 1645 | // Mark that the process is being interop-debugged. |
| 1646 | // |
| 1647 | // Notes: |
| 1648 | // @dbgtodo shim: this should eventually move into the shim or go away. |
| 1649 | // It's only to support V2 legacy interop-debugging. |
| 1650 | // Called after code:CordbProcess::Init if we want to enable interop debugging. |
| 1651 | // This allows us to separate out Interop-debugging flags from the core initialization, |
| 1652 | // and paves the way for us to eventually remove it. |
| 1653 | // |
| 1654 | // Since we're always on the naitve-pipeline, the Enabling interop debugging just changes |
| 1655 | // how the native debug events are being handled. So this must be called after Init, but |
| 1656 | // before any events are actually handled. |
| 1657 | // This mus be calle on the win32 event thread to gaurantee that it's called before WFDE. |
| 1658 | void CordbProcess::EnableInteropDebugging() |
| 1659 | { |
| 1660 | CONTRACTL |
| 1661 | { |
| 1662 | THROWS; |
| 1663 | PRECONDITION(m_pShim != NULL); |
| 1664 | } |
| 1665 | CONTRACTL_END; |
| 1666 | |
| 1667 | // Must be on W32ET to gaurantee that we're called after Init yet before WFDE (which |
| 1668 | // are both called on the W32et). |
| 1669 | _ASSERTE(IsWin32EventThread()); |
| 1670 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 1671 | |
| 1672 | m_state |= PS_WIN32_ATTACHED; |
| 1673 | if (GetDCB() != NULL) |
| 1674 | { |
| 1675 | GetDCB()->m_rightSideIsWin32Debugger = true; |
| 1676 | UpdateLeftSideDCBField(&(GetDCB()->m_rightSideIsWin32Debugger), sizeof(GetDCB()->m_rightSideIsWin32Debugger)); |
| 1677 | } |
| 1678 | |
| 1679 | // Tell the Shim we're interop-debugging. |
| 1680 | m_pShim->SetIsInteropDebugging(true); |
| 1681 | #else |
| 1682 | ThrowHR(CORDBG_E_INTEROP_NOT_SUPPORTED); |
| 1683 | #endif |
| 1684 | } |
| 1685 | |
| 1686 | //--------------------------------------------------------------------------------------- |
| 1687 | // |
| 1688 | // Init -- create any objects that the process object needs to operate. |
| 1689 | // |
| 1690 | // Arguments: |
| 1691 | // |
| 1692 | // Return Value: |
| 1693 | // S_OK on success |
| 1694 | // |
| 1695 | // Assumptions: |
| 1696 | // Called on Win32 Event Thread, after OS debugging pipeline is established but |
| 1697 | // before WaitForDebugEvent / ContinueDebugEvent. This means the target is stopped. |
| 1698 | // |
| 1699 | // Notes: |
| 1700 | // To enable interop-debugging, call code:CordbProcess::EnableInteropDebugging |
| 1701 | //--------------------------------------------------------------------------------------- |
| 1702 | HRESULT CordbProcess::Init() |
| 1703 | { |
| 1704 | INTERNAL_API_ENTRY(this); |
| 1705 | |
| 1706 | HRESULT hr = S_OK; |
| 1707 | BOOL fIsLSStarted = FALSE; // see meaning below. |
| 1708 | |
| 1709 | FAIL_IF_NEUTERED(this); |
| 1710 | |
| 1711 | |
| 1712 | EX_TRY |
| 1713 | { |
| 1714 | m_processMutex.Init("Process Lock" , RSLock::cLockReentrant, RSLock::LL_PROCESS_LOCK); |
| 1715 | m_StopGoLock.Init("Stop-Go Lock" , RSLock::cLockReentrant, RSLock::LL_STOP_GO_LOCK); |
| 1716 | |
| 1717 | #ifdef _DEBUG |
| 1718 | m_appDomains.DebugSetRSLock(GetProcessLock()); |
| 1719 | m_userThreads.DebugSetRSLock(GetProcessLock()); |
| 1720 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 1721 | m_unmanagedThreads.DebugSetRSLock(GetProcessLock()); |
| 1722 | #endif |
| 1723 | m_steppers.DebugSetRSLock(GetProcessLock()); |
| 1724 | #endif |
| 1725 | |
| 1726 | // See if the data target is mutable, and cache the mutable interface if it is |
| 1727 | // We must initialize this before we try to use the data target to access the memory in the target process. |
| 1728 | m_pMutableDataTarget.Clear(); // if we were called already, release |
| 1729 | hr = m_pDACDataTarget->QueryInterface(IID_ICorDebugMutableDataTarget, (void**)&m_pMutableDataTarget); |
| 1730 | if (!SUCCEEDED(hr)) |
| 1731 | { |
| 1732 | // The data target doesn't support mutation. We'll fail any requests that require mutation. |
| 1733 | m_pMutableDataTarget.Assign(new ReadOnlyDataTargetFacade()); |
| 1734 | } |
| 1735 | |
| 1736 | m_pMetaDataLocator.Clear(); |
| 1737 | hr = m_pDACDataTarget->QueryInterface(IID_ICorDebugMetaDataLocator, reinterpret_cast<void **>(&m_pMetaDataLocator)); |
| 1738 | |
| 1739 | // Get the metadata dispenser. |
| 1740 | hr = InternalCreateMetaDataDispenser(IID_IMetaDataDispenserEx, (void **)&m_pMetaDispenser); |
| 1741 | |
| 1742 | // We statically link in the dispenser. We expect it to succeed, except for OOM, which |
| 1743 | // debugger doesn't yet handle. |
| 1744 | SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr); |
| 1745 | IfFailThrow(hr); |
| 1746 | |
| 1747 | _ASSERTE(m_pMetaDispenser != NULL); |
| 1748 | |
| 1749 | // In order to allow users to call the metadata reader from multiple threads we need to set |
| 1750 | // a flag on the dispenser to create threadsafe readers. This is done best-effort but |
| 1751 | // really shouldn't ever fail. See issue 696511. |
| 1752 | VARIANT optionValue; |
| 1753 | VariantInit(&optionValue); |
| 1754 | V_VT(&optionValue) = VT_UI4; |
| 1755 | V_UI4(&optionValue) = MDThreadSafetyOn; |
| 1756 | m_pMetaDispenser->SetOption(MetaDataThreadSafetyOptions, &optionValue); |
| 1757 | |
| 1758 | // |
| 1759 | // Setup internal events. |
| 1760 | // @dbgtodo shim: these events should eventually be in the shim. |
| 1761 | // |
| 1762 | |
| 1763 | |
| 1764 | // Managed debugging is built on the native-pipeline, and that will detect against double-attaches. |
| 1765 | |
| 1766 | // @dbgtodo shim: In V2, LSEA + LSER were used by the LS's helper thread. Now with the V3 pipeline, |
| 1767 | // that helper-thread uses native-debug events. The W32ET gets those events and then uses LSEA, LSER to |
| 1768 | // signal existing RS infrastructure. Eventually get rid of LSEA, LSER completely. |
| 1769 | // |
| 1770 | |
| 1771 | m_leftSideEventAvailable = WszCreateEvent(NULL, FALSE, FALSE, NULL); |
| 1772 | if (m_leftSideEventAvailable == NULL) |
| 1773 | { |
| 1774 | ThrowLastError(); |
| 1775 | } |
| 1776 | |
| 1777 | m_leftSideEventRead = WszCreateEvent(NULL, FALSE, FALSE, NULL); |
| 1778 | if (m_leftSideEventRead == NULL) |
| 1779 | { |
| 1780 | ThrowLastError(); |
| 1781 | } |
| 1782 | |
| 1783 | m_stopWaitEvent = WszCreateEvent(NULL, TRUE, FALSE, NULL); |
| 1784 | if (m_stopWaitEvent == NULL) |
| 1785 | { |
| 1786 | ThrowLastError(); |
| 1787 | } |
| 1788 | |
| 1789 | if (m_pShim != NULL) |
| 1790 | { |
| 1791 | // Get a handle to the debuggee. |
| 1792 | // This is not needed in the V3 pipeline because we don't assume we have a live, local, process. |
| 1793 | m_handle = GetShim()->GetNativePipeline()->GetProcessHandle(); |
| 1794 | |
| 1795 | if (m_handle == NULL) |
| 1796 | { |
| 1797 | ThrowLastError(); |
| 1798 | } |
| 1799 | } |
| 1800 | |
| 1801 | // The LS startup goes through the following phases: |
| 1802 | // 1) mscorwks not yet loaded (eg, any unmanaged app) |
| 1803 | // 2) mscorwks loaded (DAC can now be used) |
| 1804 | // 3) IPC Block created at OS level |
| 1805 | // 4) IPC block data initialized (so we can read meainingful data from it) |
| 1806 | // 5) LS marks that it's initialized (queryable by a DAC primitive) (may not be atomic) |
| 1807 | // 6) LS fires a "Startup" exception (sniffed by WFDE). |
| 1808 | // |
| 1809 | // LS is currently stopped by OS debugging, so it's doesn't shift phases. |
| 1810 | // From the RS's perspective: |
| 1811 | // - after phase 5 is an attach |
| 1812 | // - before phase 6 is a launch. |
| 1813 | // This means there's an overlap: if we catch it at phase 5, we'll just get |
| 1814 | // an extra Startup exception from phase 6, which is safe. This overlap is good |
| 1815 | // because it means there's no bad window to do an attach in. |
| 1816 | |
| 1817 | // fIsLSStarted means before phase 6 (eg, RS should expect a startup exception) |
| 1818 | |
| 1819 | // Determines if the LS is started. |
| 1820 | |
| 1821 | { |
| 1822 | BOOL fReady = TryInitializeDac(); |
| 1823 | |
| 1824 | if (fReady) |
| 1825 | { |
| 1826 | // Invoke DAC primitive. |
| 1827 | _ASSERTE(m_pDacPrimitives != NULL); |
| 1828 | fIsLSStarted = m_pDacPrimitives->IsLeftSideInitialized(); |
| 1829 | } |
| 1830 | else |
| 1831 | { |
| 1832 | _ASSERTE(m_pDacPrimitives == NULL); |
| 1833 | |
| 1834 | // DAC is not yet loaded, so we're at least before phase 2, which is before phase 6. |
| 1835 | // So leave fIsLSStarted = false. We'll get a startup exception later. |
| 1836 | _ASSERTE(!fIsLSStarted); |
| 1837 | } |
| 1838 | } |
| 1839 | |
| 1840 | |
| 1841 | if (fIsLSStarted) |
| 1842 | { |
| 1843 | // Left-side has started up. This is common for Attach cases when managed-code is already running. |
| 1844 | |
| 1845 | if (m_pShim != NULL) |
| 1846 | { |
| 1847 | FinishInitializeIPCChannelWorker(); // throws |
| 1848 | |
| 1849 | // At this point, the control block is complete and all four |
| 1850 | // events are available and valid for the remote process. |
| 1851 | |
| 1852 | // Request that the process object send an Attach IPC event. |
| 1853 | // This is only used in an attach case. |
| 1854 | // @dbgtodo sync: this flag can go away once the |
| 1855 | // shim can use real sync APIs. |
| 1856 | m_fDoDelayedManagedAttached = true; |
| 1857 | } |
| 1858 | else |
| 1859 | { |
| 1860 | // In the V3 pipeline case, if we have the DD-interface, then the runtime is loaded |
| 1861 | // and we consider it initialized. |
| 1862 | if (IsDacInitialized()) |
| 1863 | { |
| 1864 | m_initialized = true; |
| 1865 | } |
| 1866 | } |
| 1867 | } |
| 1868 | else |
| 1869 | { |
| 1870 | // LS is not started yet. This would be common for "Launch" cases. |
| 1871 | // We will get a Startup Exception notification when it does start. |
| 1872 | } |
| 1873 | } |
| 1874 | EX_CATCH_HRESULT(hr); |
| 1875 | |
| 1876 | if (FAILED(hr)) |
| 1877 | { |
| 1878 | CleanupHalfBakedLeftSide(); |
| 1879 | } |
| 1880 | |
| 1881 | return hr; |
| 1882 | } |
| 1883 | |
| 1884 | |
| 1885 | COM_METHOD CordbProcess::CanCommitChanges(ULONG cSnapshots, |
| 1886 | ICorDebugEditAndContinueSnapshot *pSnapshots[], |
| 1887 | ICorDebugErrorInfoEnum **pError) |
| 1888 | { |
| 1889 | return E_NOTIMPL; |
| 1890 | } |
| 1891 | |
| 1892 | COM_METHOD CordbProcess::CommitChanges(ULONG cSnapshots, |
| 1893 | ICorDebugEditAndContinueSnapshot *pSnapshots[], |
| 1894 | ICorDebugErrorInfoEnum **pError) |
| 1895 | { |
| 1896 | return E_NOTIMPL; |
| 1897 | } |
| 1898 | |
| 1899 | |
| 1900 | // |
| 1901 | // Terminating -- places the process into the terminated state. This should |
| 1902 | // also get any blocking process functions unblocked so they'll return |
| 1903 | // a failure code. |
| 1904 | // |
| 1905 | void CordbProcess::Terminating(BOOL fDetach) |
| 1906 | { |
| 1907 | INTERNAL_API_ENTRY(this); |
| 1908 | |
| 1909 | LOG((LF_CORDB, LL_INFO1000,"CP::T: Terminating process 0x%x detach=%d\n" , m_id, fDetach)); |
| 1910 | m_terminated = true; |
| 1911 | |
| 1912 | m_cordb->ProcessStateChanged(); |
| 1913 | |
| 1914 | // Set events that may be blocking stuff. |
| 1915 | // But don't set RSER unless we actually read the event. We don't block on RSER |
| 1916 | // since that wait also checks the leftside's process handle. |
| 1917 | SetEvent(m_leftSideEventRead); |
| 1918 | SetEvent(m_leftSideEventAvailable); |
| 1919 | SetEvent(m_stopWaitEvent); |
| 1920 | |
| 1921 | if (m_pShim != NULL) |
| 1922 | m_pShim->SetTerminatingEvent(); |
| 1923 | |
| 1924 | if (fDetach && (m_pEventChannel != NULL)) |
| 1925 | { |
| 1926 | m_pEventChannel->Detach(); |
| 1927 | } |
| 1928 | } |
| 1929 | |
| 1930 | |
| 1931 | // Wrapper to give shim access to code:CordbProcess::QueueManagedAttachIfNeededWorker |
| 1932 | void CordbProcess::QueueManagedAttachIfNeeded() |
| 1933 | { |
| 1934 | PUBLIC_API_ENTRY_FOR_SHIM(this); |
| 1935 | QueueManagedAttachIfNeededWorker(); |
| 1936 | } |
| 1937 | |
| 1938 | //--------------------------------------------------------------------------------------- |
| 1939 | // Hook from Shim to request a managed attach IPC event |
| 1940 | // |
| 1941 | // Notes: |
| 1942 | // Called by shim after the loader-breakpoint is handled. |
| 1943 | // @dbgtodo sync: ths should go away once the shim can initiate |
| 1944 | // a sync |
| 1945 | void CordbProcess::QueueManagedAttachIfNeededWorker() |
| 1946 | { |
| 1947 | HRESULT hrQueue = S_OK; |
| 1948 | |
| 1949 | // m_fDoDelayedManagedAttached ensures that we only send an Attach event if the LS is actually present. |
| 1950 | if (m_fDoDelayedManagedAttached && GetShim()->GetAttached()) |
| 1951 | { |
| 1952 | RSLockHolder lockHolder(&this->m_processMutex); |
| 1953 | GetDAC()->MarkDebuggerAttachPending(); |
| 1954 | |
| 1955 | hrQueue = this->QueueManagedAttach(); |
| 1956 | } |
| 1957 | |
| 1958 | if (m_pShim != NULL) |
| 1959 | m_pShim->SetMarkAttachPendingEvent(); |
| 1960 | |
| 1961 | IfFailThrow(hrQueue); |
| 1962 | } |
| 1963 | |
| 1964 | //--------------------------------------------------------------------------------------- |
| 1965 | // |
| 1966 | // QueueManagedAttach |
| 1967 | // |
| 1968 | // Send a managed attach. This is asynchronous and will return immediately. |
| 1969 | // |
| 1970 | // Return Value: |
| 1971 | // S_OK on success |
| 1972 | // |
| 1973 | //--------------------------------------------------------------------------------------- |
| 1974 | HRESULT CordbProcess::QueueManagedAttach() |
| 1975 | { |
| 1976 | INTERNAL_API_ENTRY(this); |
| 1977 | |
| 1978 | _ASSERTE(ThreadHoldsProcessLock()); |
| 1979 | |
| 1980 | _ASSERTE(m_fDoDelayedManagedAttached); |
| 1981 | m_fDoDelayedManagedAttached = false; |
| 1982 | |
| 1983 | _ASSERTE(IsDacInitialized()); |
| 1984 | |
| 1985 | // We don't know what Queue it. |
| 1986 | SendAttachProcessWorkItem * pItem = new (nothrow) SendAttachProcessWorkItem(this); |
| 1987 | |
| 1988 | if (pItem == NULL) |
| 1989 | { |
| 1990 | return E_OUTOFMEMORY; |
| 1991 | } |
| 1992 | |
| 1993 | this->m_cordb->m_rcEventThread->QueueAsyncWorkItem(pItem); |
| 1994 | |
| 1995 | return S_OK; |
| 1996 | } |
| 1997 | |
| 1998 | // However, we still want to synchronize. |
| 1999 | // @dbgtodo sync: when we hoist attaching, we can send an DB_IPCE_ASYNC_BREAK event instead or Attach |
| 2000 | // (for V2 semantics, we still need to synchronize the process)? |
| 2001 | void SendAttachProcessWorkItem::Do() |
| 2002 | { |
| 2003 | HRESULT hr; |
| 2004 | |
| 2005 | // This is being processed on the RCET, where it's safe to take the Stop-Go lock. |
| 2006 | RSLockHolder ch(this->GetProcess()->GetStopGoLock()); |
| 2007 | |
| 2008 | DebuggerIPCEvent *event = (DebuggerIPCEvent*) _alloca(CorDBIPC_BUFFER_SIZE); |
| 2009 | |
| 2010 | // This just acts like an async-break, which will kick off things. |
| 2011 | // This will not induce any faked attach events from the VM (like it did in V2). |
| 2012 | // The Left-side will still slip foward allowing the async-break to happen, so |
| 2013 | // we may get normal debug events in addition to the sync-complete. |
| 2014 | // |
| 2015 | // 1. In the common attach case, we should just get a sync-complete. |
| 2016 | // 2. In Jit-attach cases, the LS is sending an event, and so we'll get that event and then the sync-complete. |
| 2017 | GetProcess()->InitAsyncIPCEvent(event, DB_IPCE_ATTACHING, VMPTR_AppDomain::NullPtr()); |
| 2018 | |
| 2019 | // This should result in a sync-complete from the Left-side, which will be raised as an exception |
| 2020 | // that the debugger passes into Filter and then internally goes through code:CordbProcess::TriageSyncComplete |
| 2021 | // and that triggers code:CordbRCEventThread::FlushQueuedEvents to be called on the RCET. |
| 2022 | // We already pre-queued a fake CreateProcess event. |
| 2023 | |
| 2024 | // The left-side will also mark itself as attached in response to this event. |
| 2025 | // We explicitly don't mark it as attached from the right-side because we want to let the left-side |
| 2026 | // synchronize first (to stop all running threads) before marking the debugger as attached. |
| 2027 | LOG((LF_CORDB, LL_INFO1000, "[%x] CP::S: sending attach.\n" , GetCurrentThreadId())); |
| 2028 | |
| 2029 | hr = GetProcess()->SendIPCEvent(event, CorDBIPC_BUFFER_SIZE); |
| 2030 | |
| 2031 | LOG((LF_CORDB, LL_INFO1000, "[%x] CP::S: sent attach.\n" , GetCurrentThreadId())); |
| 2032 | } |
| 2033 | |
| 2034 | //--------------------------------------------------------------------------------------- |
| 2035 | // Try to lookup a cached thread object |
| 2036 | // |
| 2037 | // Arguments: |
| 2038 | // vmThread - vm identifier for thread. |
| 2039 | // |
| 2040 | // Returns: |
| 2041 | // Thread object if cached; null if not yet cached. |
| 2042 | // |
| 2043 | // Notes: |
| 2044 | // This does not create the thread object if it's not cached. Caching is unpredictable, |
| 2045 | // and so this may appear to randomly return NULL. |
| 2046 | // Callers should prefer code:CordbProcess::LookupOrCreateThread unless they expicitly |
| 2047 | // want to check RS state. |
| 2048 | CordbThread * CordbProcess::TryLookupThread(VMPTR_Thread vmThread) |
| 2049 | { |
| 2050 | return m_userThreads.GetBase(VmPtrToCookie(vmThread)); |
| 2051 | } |
| 2052 | |
| 2053 | //--------------------------------------------------------------------------------------- |
| 2054 | // Lookup (or create) a CordbThread object by the given volatile OS id. Returns null if not a manged thread |
| 2055 | // |
| 2056 | // Arguments: |
| 2057 | // dwThreadId - os thread id that a managed thread may be using. |
| 2058 | // |
| 2059 | // Returns: |
| 2060 | // Thread instance if there is currently a managed thread scheduled to run on dwThreadId. |
| 2061 | // NULL if this tid is not a valid Managed thread. (This is considered a common case) |
| 2062 | // Throws on error. |
| 2063 | // |
| 2064 | // Notes: |
| 2065 | // OS Thread ID is not fiber-safe, so this is a dangerous function to call. |
| 2066 | // Avoid this as much as possible. Prefer using VMPTR_Thread and |
| 2067 | // code:CordbProcess::LookupOrCreateThread instead of OS thread IDs. |
| 2068 | // See code:CordbThread::GetID for details. |
| 2069 | CordbThread * CordbProcess::TryLookupOrCreateThreadByVolatileOSId(DWORD dwThreadId) |
| 2070 | { |
| 2071 | PrepopulateThreadsOrThrow(); |
| 2072 | return TryLookupThreadByVolatileOSId(dwThreadId); |
| 2073 | } |
| 2074 | |
| 2075 | //--------------------------------------------------------------------------------------- |
| 2076 | // Lookup a cached CordbThread object by the tid. Returns null if not in the cache (which |
| 2077 | // includes unmanged thread) |
| 2078 | // |
| 2079 | // Arguments: |
| 2080 | // dwThreadId - os thread id that a managed thread may be using. |
| 2081 | // |
| 2082 | // Returns: |
| 2083 | // Thread instance if there is currently a managed thread scheduled to run on dwThreadId. |
| 2084 | // NULL if this tid is not a valid Managed thread. (This is considered a common case) |
| 2085 | // Throws on error. |
| 2086 | // |
| 2087 | // Notes: |
| 2088 | // Avoids this method: |
| 2089 | // * OS Thread ID is not fiber-safe, so this is a dangerous function to call. |
| 2090 | // * This is juts a Lookup, not LookupOrCreate, so it should only be used by methods |
| 2091 | // that care about the RS state (instead of just LS state). |
| 2092 | // Prefer using VMPTR_Thread and code:CordbProcess::LookupOrCreateThread |
| 2093 | // |
| 2094 | CordbThread * CordbProcess::TryLookupThreadByVolatileOSId(DWORD dwThreadId) |
| 2095 | { |
| 2096 | HASHFIND find; |
| 2097 | for (CordbThread * pThread = m_userThreads.FindFirst(&find); |
| 2098 | pThread != NULL; |
| 2099 | pThread = m_userThreads.FindNext(&find)) |
| 2100 | { |
| 2101 | _ASSERTE(pThread != NULL); |
| 2102 | |
| 2103 | // Get the OS tid. This returns 0 if the thread is switched out. |
| 2104 | DWORD dwThreadId2 = GetDAC()->TryGetVolatileOSThreadID(pThread->m_vmThreadToken); |
| 2105 | if (dwThreadId2 == dwThreadId) |
| 2106 | { |
| 2107 | return pThread; |
| 2108 | } |
| 2109 | } |
| 2110 | |
| 2111 | // This OS thread ID does not match any managed thread id. |
| 2112 | return NULL; |
| 2113 | } |
| 2114 | |
| 2115 | //--------------------------------------------------------------------------------------- |
| 2116 | // Preferred CordbThread lookup routine. |
| 2117 | // |
| 2118 | // Arguments: |
| 2119 | // vmThread - LS thread to lookup. Must be non-null. |
| 2120 | // |
| 2121 | // Returns: |
| 2122 | // CordbThread instance for given vmThread. May return a previously cached |
| 2123 | // instance or create a new instance. Never returns NULL. |
| 2124 | // Throw on error. |
| 2125 | CordbThread * CordbProcess::LookupOrCreateThread(VMPTR_Thread vmThread) |
| 2126 | { |
| 2127 | _ASSERTE(!vmThread.IsNull()); |
| 2128 | |
| 2129 | // Return if we have an existing instance. |
| 2130 | CordbThread * pReturn = TryLookupThread(vmThread); |
| 2131 | if (pReturn != NULL) |
| 2132 | { |
| 2133 | return pReturn; |
| 2134 | } |
| 2135 | |
| 2136 | RSInitHolder<CordbThread> pThread(new CordbThread(this, vmThread)); // throws |
| 2137 | pReturn = pThread.TransferOwnershipToHash(&m_userThreads); |
| 2138 | |
| 2139 | return pReturn; |
| 2140 | } |
| 2141 | |
| 2142 | |
| 2143 | |
| 2144 | |
| 2145 | HRESULT CordbProcess::QueryInterface(REFIID id, void **pInterface) |
| 2146 | { |
| 2147 | if (id == IID_ICorDebugProcess) |
| 2148 | { |
| 2149 | *pInterface = static_cast<ICorDebugProcess*>(this); |
| 2150 | } |
| 2151 | else if (id == IID_ICorDebugController) |
| 2152 | { |
| 2153 | *pInterface = static_cast<ICorDebugController*>(static_cast<ICorDebugProcess*>(this)); |
| 2154 | } |
| 2155 | else if (id == IID_ICorDebugProcess2) |
| 2156 | |
| 2157 | { |
| 2158 | *pInterface = static_cast<ICorDebugProcess2*>(this); |
| 2159 | } |
| 2160 | else if (id == IID_ICorDebugProcess3) |
| 2161 | { |
| 2162 | *pInterface = static_cast<ICorDebugProcess3*>(this); |
| 2163 | } |
| 2164 | else if (id == IID_ICorDebugProcess4) |
| 2165 | { |
| 2166 | *pInterface = static_cast<ICorDebugProcess4*>(this); |
| 2167 | } |
| 2168 | else if (id == IID_ICorDebugProcess5) |
| 2169 | { |
| 2170 | *pInterface = static_cast<ICorDebugProcess5*>(this); |
| 2171 | } |
| 2172 | else if (id == IID_ICorDebugProcess7) |
| 2173 | { |
| 2174 | *pInterface = static_cast<ICorDebugProcess7*>(this); |
| 2175 | } |
| 2176 | else if (id == IID_ICorDebugProcess8) |
| 2177 | { |
| 2178 | *pInterface = static_cast<ICorDebugProcess8*>(this); |
| 2179 | } |
| 2180 | else if (id == IID_ICorDebugProcess10) |
| 2181 | { |
| 2182 | *pInterface = static_cast<ICorDebugProcess10*>(this); |
| 2183 | } |
| 2184 | #ifdef FEATURE_LEGACYNETCF_DBG_HOST_CONTROL |
| 2185 | else if (id == IID_ICorDebugLegacyNetCFHostCallbackInvoker_PrivateWindowsPhoneOnly) |
| 2186 | { |
| 2187 | *pInterface = static_cast<ICorDebugLegacyNetCFHostCallbackInvoker_PrivateWindowsPhoneOnly*>(this); |
| 2188 | } |
| 2189 | #endif |
| 2190 | else if (id == IID_IUnknown) |
| 2191 | { |
| 2192 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugProcess*>(this)); |
| 2193 | } |
| 2194 | |
| 2195 | else |
| 2196 | { |
| 2197 | *pInterface = NULL; |
| 2198 | return E_NOINTERFACE; |
| 2199 | } |
| 2200 | |
| 2201 | ExternalAddRef(); |
| 2202 | return S_OK; |
| 2203 | } |
| 2204 | |
| 2205 | |
| 2206 | |
| 2207 | |
| 2208 | // Public implementation of ICorDebugProcess4::ProcessStateChanged |
| 2209 | HRESULT CordbProcess::ProcessStateChanged(CorDebugStateChange eChange) |
| 2210 | { |
| 2211 | HRESULT hr = S_OK; |
| 2212 | PUBLIC_API_BEGIN(this) |
| 2213 | { |
| 2214 | switch(eChange) |
| 2215 | { |
| 2216 | case PROCESS_RUNNING: |
| 2217 | FlushProcessRunning(); |
| 2218 | break; |
| 2219 | |
| 2220 | case FLUSH_ALL: |
| 2221 | FlushAll(); |
| 2222 | break; |
| 2223 | |
| 2224 | default: |
| 2225 | ThrowHR(E_INVALIDARG); |
| 2226 | |
| 2227 | } |
| 2228 | } |
| 2229 | PUBLIC_API_END(hr); |
| 2230 | return hr; |
| 2231 | } |
| 2232 | |
| 2233 | |
| 2234 | HRESULT CordbProcess::EnumerateHeap(ICorDebugHeapEnum **ppObjects) |
| 2235 | { |
| 2236 | if (!ppObjects) |
| 2237 | return E_POINTER; |
| 2238 | |
| 2239 | HRESULT hr = S_OK; |
| 2240 | PUBLIC_API_ENTRY(this); |
| 2241 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 2242 | |
| 2243 | EX_TRY |
| 2244 | { |
| 2245 | if (m_pDacPrimitives->AreGCStructuresValid()) |
| 2246 | { |
| 2247 | CordbHeapEnum *pHeapEnum = new CordbHeapEnum(this); |
| 2248 | GetContinueNeuterList()->Add(this, pHeapEnum); |
| 2249 | hr = pHeapEnum->QueryInterface(__uuidof(ICorDebugHeapEnum), (void**)ppObjects); |
| 2250 | } |
| 2251 | else |
| 2252 | { |
| 2253 | hr = CORDBG_E_GC_STRUCTURES_INVALID; |
| 2254 | } |
| 2255 | } |
| 2256 | EX_CATCH_HRESULT(hr); |
| 2257 | |
| 2258 | return hr; |
| 2259 | } |
| 2260 | |
| 2261 | HRESULT CordbProcess::GetGCHeapInformation(COR_HEAPINFO *pHeapInfo) |
| 2262 | { |
| 2263 | if (!pHeapInfo) |
| 2264 | return E_INVALIDARG; |
| 2265 | |
| 2266 | HRESULT hr = S_OK; |
| 2267 | PUBLIC_API_ENTRY(this); |
| 2268 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 2269 | |
| 2270 | EX_TRY |
| 2271 | { |
| 2272 | GetDAC()->GetGCHeapInformation(pHeapInfo); |
| 2273 | } |
| 2274 | EX_CATCH_HRESULT(hr); |
| 2275 | |
| 2276 | return hr; |
| 2277 | } |
| 2278 | |
| 2279 | HRESULT CordbProcess::EnumerateHeapRegions(ICorDebugHeapSegmentEnum **ppRegions) |
| 2280 | { |
| 2281 | if (!ppRegions) |
| 2282 | return E_INVALIDARG; |
| 2283 | |
| 2284 | HRESULT hr = S_OK; |
| 2285 | PUBLIC_API_ENTRY(this); |
| 2286 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 2287 | |
| 2288 | EX_TRY |
| 2289 | { |
| 2290 | DacDbiArrayList<COR_SEGMENT> segments; |
| 2291 | hr = GetDAC()->GetHeapSegments(&segments); |
| 2292 | |
| 2293 | if (SUCCEEDED(hr)) |
| 2294 | { |
| 2295 | if (!segments.IsEmpty()) |
| 2296 | { |
| 2297 | CordbHeapSegmentEnumerator *segEnum = new CordbHeapSegmentEnumerator(this, &segments[0], (DWORD)segments.Count()); |
| 2298 | GetContinueNeuterList()->Add(this, segEnum); |
| 2299 | hr = segEnum->QueryInterface(__uuidof(ICorDebugHeapSegmentEnum), (void**)ppRegions); |
| 2300 | } |
| 2301 | else |
| 2302 | { |
| 2303 | hr = E_OUTOFMEMORY; |
| 2304 | } |
| 2305 | } |
| 2306 | } |
| 2307 | EX_CATCH_HRESULT(hr); |
| 2308 | |
| 2309 | return hr; |
| 2310 | } |
| 2311 | |
| 2312 | HRESULT CordbProcess::GetObject(CORDB_ADDRESS addr, ICorDebugObjectValue **ppObject) |
| 2313 | { |
| 2314 | return this->GetObjectInternal(addr, nullptr, ppObject); |
| 2315 | } |
| 2316 | |
| 2317 | HRESULT CordbProcess::GetObjectInternal(CORDB_ADDRESS addr, CordbAppDomain* pAppDomainOverride, ICorDebugObjectValue **pObject) |
| 2318 | { |
| 2319 | HRESULT hr = S_OK; |
| 2320 | |
| 2321 | PUBLIC_REENTRANT_API_ENTRY(this); |
| 2322 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 2323 | |
| 2324 | EX_TRY |
| 2325 | { |
| 2326 | if (!m_pDacPrimitives->IsValidObject(addr)) |
| 2327 | { |
| 2328 | hr = CORDBG_E_CORRUPT_OBJECT; |
| 2329 | } |
| 2330 | else if (pObject == NULL) |
| 2331 | { |
| 2332 | hr = E_INVALIDARG; |
| 2333 | } |
| 2334 | else |
| 2335 | { |
| 2336 | RSLockHolder ch(GetProcess()->GetStopGoLock()); |
| 2337 | RSLockHolder procLock(this->GetProcess()->GetProcessLock()); |
| 2338 | |
| 2339 | CordbAppDomain *cdbAppDomain = NULL; |
| 2340 | CordbType *pType = NULL; |
| 2341 | hr = GetTypeForObject(addr, pAppDomainOverride, &pType, &cdbAppDomain); |
| 2342 | |
| 2343 | if (SUCCEEDED(hr)) |
| 2344 | { |
| 2345 | _ASSERTE(pType != NULL); |
| 2346 | _ASSERTE(cdbAppDomain != NULL); |
| 2347 | |
| 2348 | DebuggerIPCE_ObjectData objData; |
| 2349 | m_pDacPrimitives->GetBasicObjectInfo(addr, ELEMENT_TYPE_CLASS, cdbAppDomain->GetADToken(), &objData); |
| 2350 | |
| 2351 | NewHolder<CordbObjectValue> pNewObjectValue(new CordbObjectValue(cdbAppDomain, pType, TargetBuffer(addr, (ULONG)objData.objSize), &objData)); |
| 2352 | hr = pNewObjectValue->Init(); |
| 2353 | |
| 2354 | if (SUCCEEDED(hr)) |
| 2355 | { |
| 2356 | hr = pNewObjectValue->QueryInterface(__uuidof(ICorDebugObjectValue), (void**)pObject); |
| 2357 | if (SUCCEEDED(hr)) |
| 2358 | pNewObjectValue.SuppressRelease(); |
| 2359 | } |
| 2360 | } |
| 2361 | } |
| 2362 | } |
| 2363 | EX_CATCH_HRESULT(hr); |
| 2364 | |
| 2365 | return hr; |
| 2366 | } |
| 2367 | |
| 2368 | |
| 2369 | HRESULT CordbProcess::EnumerateGCReferences(BOOL enumerateWeakReferences, ICorDebugGCReferenceEnum **ppEnum) |
| 2370 | { |
| 2371 | if (!ppEnum) |
| 2372 | return E_POINTER; |
| 2373 | |
| 2374 | HRESULT hr = S_OK; |
| 2375 | PUBLIC_API_ENTRY(this); |
| 2376 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 2377 | |
| 2378 | EX_TRY |
| 2379 | { |
| 2380 | CordbRefEnum *pRefEnum = new CordbRefEnum(this, enumerateWeakReferences); |
| 2381 | GetContinueNeuterList()->Add(this, pRefEnum); |
| 2382 | hr = pRefEnum->QueryInterface(IID_ICorDebugGCReferenceEnum, (void**)ppEnum); |
| 2383 | } |
| 2384 | EX_CATCH_HRESULT(hr); |
| 2385 | return hr; |
| 2386 | } |
| 2387 | |
| 2388 | HRESULT CordbProcess::EnumerateHandles(CorGCReferenceType types, ICorDebugGCReferenceEnum **ppEnum) |
| 2389 | { |
| 2390 | if (!ppEnum) |
| 2391 | return E_POINTER; |
| 2392 | |
| 2393 | HRESULT hr = S_OK; |
| 2394 | PUBLIC_API_ENTRY(this); |
| 2395 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 2396 | |
| 2397 | EX_TRY |
| 2398 | { |
| 2399 | CordbRefEnum *pRefEnum = new CordbRefEnum(this, types); |
| 2400 | GetContinueNeuterList()->Add(this, pRefEnum); |
| 2401 | hr = pRefEnum->QueryInterface(IID_ICorDebugGCReferenceEnum, (void**)ppEnum); |
| 2402 | } |
| 2403 | EX_CATCH_HRESULT(hr); |
| 2404 | |
| 2405 | return hr; |
| 2406 | } |
| 2407 | |
| 2408 | HRESULT CordbProcess::EnableNGENPolicy(CorDebugNGENPolicy ePolicy) |
| 2409 | { |
| 2410 | return E_NOTIMPL; |
| 2411 | } |
| 2412 | |
| 2413 | |
| 2414 | HRESULT CordbProcess::GetTypeID(CORDB_ADDRESS obj, COR_TYPEID *pId) |
| 2415 | { |
| 2416 | if (pId == NULL) |
| 2417 | return E_POINTER; |
| 2418 | |
| 2419 | HRESULT hr = S_OK; |
| 2420 | PUBLIC_API_ENTRY(this); |
| 2421 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 2422 | |
| 2423 | EX_TRY |
| 2424 | { |
| 2425 | hr = GetProcess()->GetDAC()->GetTypeID(obj, pId); |
| 2426 | } |
| 2427 | EX_CATCH_HRESULT(hr); |
| 2428 | |
| 2429 | return hr; |
| 2430 | } |
| 2431 | |
| 2432 | HRESULT CordbProcess::GetTypeForTypeID(COR_TYPEID id, ICorDebugType **ppType) |
| 2433 | { |
| 2434 | if (ppType == NULL) |
| 2435 | return E_POINTER; |
| 2436 | |
| 2437 | HRESULT hr = S_OK; |
| 2438 | |
| 2439 | PUBLIC_API_ENTRY(this); |
| 2440 | RSLockHolder stopGoLock(this->GetProcess()->GetStopGoLock()); |
| 2441 | RSLockHolder procLock(this->GetProcess()->GetProcessLock()); |
| 2442 | |
| 2443 | EX_TRY |
| 2444 | { |
| 2445 | DebuggerIPCE_ExpandedTypeData data; |
| 2446 | GetDAC()->GetObjectExpandedTypeInfoFromID(AllBoxed, VMPTR_AppDomain::NullPtr(), id, &data); |
| 2447 | |
| 2448 | CordbType *type = 0; |
| 2449 | hr = CordbType::TypeDataToType(GetSharedAppDomain(), &data, &type); |
| 2450 | |
| 2451 | if (SUCCEEDED(hr)) |
| 2452 | hr = type->QueryInterface(IID_ICorDebugType, (void**)ppType); |
| 2453 | } |
| 2454 | EX_CATCH_HRESULT(hr); |
| 2455 | |
| 2456 | return hr; |
| 2457 | } |
| 2458 | |
| 2459 | |
| 2460 | COM_METHOD CordbProcess::GetArrayLayout(COR_TYPEID id, COR_ARRAY_LAYOUT *pLayout) |
| 2461 | { |
| 2462 | if (pLayout == NULL) |
| 2463 | return E_POINTER; |
| 2464 | |
| 2465 | HRESULT hr = S_OK; |
| 2466 | PUBLIC_API_BEGIN(this); |
| 2467 | |
| 2468 | hr = GetProcess()->GetDAC()->GetArrayLayout(id, pLayout); |
| 2469 | |
| 2470 | PUBLIC_API_END(hr); |
| 2471 | return hr; |
| 2472 | } |
| 2473 | |
| 2474 | COM_METHOD CordbProcess::GetTypeLayout(COR_TYPEID id, COR_TYPE_LAYOUT *pLayout) |
| 2475 | { |
| 2476 | if (pLayout == NULL) |
| 2477 | return E_POINTER; |
| 2478 | |
| 2479 | HRESULT hr = S_OK; |
| 2480 | PUBLIC_API_BEGIN(this); |
| 2481 | |
| 2482 | hr = GetProcess()->GetDAC()->GetTypeLayout(id, pLayout); |
| 2483 | |
| 2484 | PUBLIC_API_END(hr); |
| 2485 | return hr; |
| 2486 | } |
| 2487 | |
| 2488 | COM_METHOD CordbProcess::GetTypeFields(COR_TYPEID id, ULONG32 celt, COR_FIELD fields[], ULONG32 *pceltNeeded) |
| 2489 | { |
| 2490 | HRESULT hr = S_OK; |
| 2491 | PUBLIC_API_BEGIN(this); |
| 2492 | |
| 2493 | hr = GetProcess()->GetDAC()->GetObjectFields(id, celt, fields, pceltNeeded); |
| 2494 | |
| 2495 | PUBLIC_API_END(hr); |
| 2496 | return hr; |
| 2497 | } |
| 2498 | |
| 2499 | COM_METHOD CordbProcess::SetWriteableMetadataUpdateMode(WriteableMetadataUpdateMode flags) |
| 2500 | { |
| 2501 | HRESULT hr = S_OK; |
| 2502 | PUBLIC_API_BEGIN(this); |
| 2503 | |
| 2504 | if(flags != LegacyCompatPolicy && |
| 2505 | flags != AlwaysShowUpdates) |
| 2506 | { |
| 2507 | hr = E_INVALIDARG; |
| 2508 | } |
| 2509 | else if(m_pShim != NULL) |
| 2510 | { |
| 2511 | if(flags != LegacyCompatPolicy) |
| 2512 | { |
| 2513 | hr = CORDBG_E_UNSUPPORTED; |
| 2514 | } |
| 2515 | } |
| 2516 | |
| 2517 | if(SUCCEEDED(hr)) |
| 2518 | { |
| 2519 | m_writableMetadataUpdateMode = flags; |
| 2520 | } |
| 2521 | |
| 2522 | PUBLIC_API_END(hr); |
| 2523 | return hr; |
| 2524 | } |
| 2525 | |
| 2526 | COM_METHOD CordbProcess::EnableExceptionCallbacksOutsideOfMyCode(BOOL enableExceptionsOutsideOfJMC) |
| 2527 | { |
| 2528 | HRESULT hr = S_OK; |
| 2529 | PUBLIC_API_BEGIN(this); |
| 2530 | |
| 2531 | hr = GetProcess()->GetDAC()->SetSendExceptionsOutsideOfJMC(enableExceptionsOutsideOfJMC); |
| 2532 | |
| 2533 | PUBLIC_API_END(hr); |
| 2534 | return hr; |
| 2535 | } |
| 2536 | |
| 2537 | COM_METHOD CordbProcess::EnableGCNotificationEvents(BOOL fEnable) |
| 2538 | { |
| 2539 | HRESULT hr = S_OK; |
| 2540 | PUBLIC_API_BEGIN(this) |
| 2541 | { |
| 2542 | hr = this->m_pDacPrimitives->EnableGCNotificationEvents(fEnable); |
| 2543 | } |
| 2544 | PUBLIC_API_END(hr); |
| 2545 | return hr; |
| 2546 | } |
| 2547 | |
| 2548 | #ifdef FEATURE_LEGACYNETCF_DBG_HOST_CONTROL |
| 2549 | |
| 2550 | COM_METHOD CordbProcess::InvokePauseCallback() |
| 2551 | { |
| 2552 | return S_OK; |
| 2553 | } |
| 2554 | |
| 2555 | COM_METHOD CordbProcess::InvokeResumeCallback() |
| 2556 | { |
| 2557 | return S_OK; |
| 2558 | } |
| 2559 | |
| 2560 | #endif |
| 2561 | |
| 2562 | HRESULT CordbProcess::GetTypeForObject(CORDB_ADDRESS addr, CordbAppDomain* pAppDomainOverride, CordbType **ppType, CordbAppDomain **pAppDomain) |
| 2563 | { |
| 2564 | VMPTR_AppDomain appDomain; |
| 2565 | VMPTR_Module mod; |
| 2566 | VMPTR_DomainFile domainFile; |
| 2567 | |
| 2568 | HRESULT hr = E_FAIL; |
| 2569 | if (GetDAC()->GetAppDomainForObject(addr, &appDomain, &mod, &domainFile)) |
| 2570 | { |
| 2571 | if (pAppDomainOverride) |
| 2572 | { |
| 2573 | appDomain = pAppDomainOverride->GetADToken(); |
| 2574 | } |
| 2575 | CordbAppDomain *cdbAppDomain = appDomain.IsNull() ? GetSharedAppDomain() : LookupOrCreateAppDomain(appDomain); |
| 2576 | |
| 2577 | _ASSERTE(cdbAppDomain); |
| 2578 | |
| 2579 | DebuggerIPCE_ExpandedTypeData data; |
| 2580 | GetDAC()->GetObjectExpandedTypeInfo(AllBoxed, appDomain, addr, &data); |
| 2581 | |
| 2582 | CordbType *type = 0; |
| 2583 | hr = CordbType::TypeDataToType(cdbAppDomain, &data, &type); |
| 2584 | |
| 2585 | if (SUCCEEDED(hr)) |
| 2586 | { |
| 2587 | *ppType = type; |
| 2588 | if (pAppDomain) |
| 2589 | *pAppDomain = cdbAppDomain; |
| 2590 | } |
| 2591 | } |
| 2592 | |
| 2593 | return hr; |
| 2594 | } |
| 2595 | |
| 2596 | |
| 2597 | // ****************************************** |
| 2598 | // CordbRefEnum |
| 2599 | // ****************************************** |
| 2600 | CordbRefEnum::CordbRefEnum(CordbProcess *proc, BOOL walkWeakRefs) |
| 2601 | : CordbBase(proc, 0, enumCordbHeap), mRefHandle(0), mEnumStacksFQ(TRUE), |
| 2602 | mHandleMask((UINT32)(walkWeakRefs ? CorHandleAll : CorHandleStrongOnly)) |
| 2603 | { |
| 2604 | } |
| 2605 | |
| 2606 | CordbRefEnum::CordbRefEnum(CordbProcess *proc, CorGCReferenceType types) |
| 2607 | : CordbBase(proc, 0, enumCordbHeap), mRefHandle(0), mEnumStacksFQ(FALSE), |
| 2608 | mHandleMask((UINT32)types) |
| 2609 | { |
| 2610 | } |
| 2611 | |
| 2612 | void CordbRefEnum::Neuter() |
| 2613 | { |
| 2614 | EX_TRY |
| 2615 | { |
| 2616 | if (mRefHandle) |
| 2617 | { |
| 2618 | GetProcess()->GetDAC()->DeleteRefWalk(mRefHandle); |
| 2619 | mRefHandle = 0; |
| 2620 | } |
| 2621 | } |
| 2622 | EX_CATCH |
| 2623 | { |
| 2624 | _ASSERTE(!"Hit an error freeing a ref walk." ); |
| 2625 | } |
| 2626 | EX_END_CATCH(SwallowAllExceptions) |
| 2627 | |
| 2628 | CordbBase::Neuter(); |
| 2629 | } |
| 2630 | |
| 2631 | HRESULT CordbRefEnum::QueryInterface(REFIID riid, void **ppInterface) |
| 2632 | { |
| 2633 | if (ppInterface == NULL) |
| 2634 | return E_INVALIDARG; |
| 2635 | |
| 2636 | if (riid == IID_ICorDebugGCReferenceEnum) |
| 2637 | { |
| 2638 | *ppInterface = static_cast<ICorDebugGCReferenceEnum*>(this); |
| 2639 | } |
| 2640 | else if (riid == IID_IUnknown) |
| 2641 | { |
| 2642 | *ppInterface = static_cast<IUnknown*>(static_cast<ICorDebugGCReferenceEnum*>(this)); |
| 2643 | } |
| 2644 | else |
| 2645 | { |
| 2646 | *ppInterface = NULL; |
| 2647 | return E_NOINTERFACE; |
| 2648 | } |
| 2649 | |
| 2650 | ExternalAddRef(); |
| 2651 | return S_OK; |
| 2652 | } |
| 2653 | |
| 2654 | HRESULT CordbRefEnum::Skip(ULONG celt) |
| 2655 | { |
| 2656 | return E_NOTIMPL; |
| 2657 | } |
| 2658 | |
| 2659 | HRESULT CordbRefEnum::Reset() |
| 2660 | { |
| 2661 | PUBLIC_API_ENTRY(this); |
| 2662 | HRESULT hr = S_OK; |
| 2663 | EX_TRY |
| 2664 | { |
| 2665 | if (mRefHandle) |
| 2666 | { |
| 2667 | GetProcess()->GetDAC()->DeleteRefWalk(mRefHandle); |
| 2668 | mRefHandle = 0; |
| 2669 | } |
| 2670 | } |
| 2671 | EX_CATCH_HRESULT(hr); |
| 2672 | |
| 2673 | return hr; |
| 2674 | } |
| 2675 | |
| 2676 | HRESULT CordbRefEnum::Clone(ICorDebugEnum **ppEnum) |
| 2677 | { |
| 2678 | return E_NOTIMPL; |
| 2679 | } |
| 2680 | |
| 2681 | HRESULT CordbRefEnum::GetCount(ULONG *pcelt) |
| 2682 | { |
| 2683 | return E_NOTIMPL; |
| 2684 | } |
| 2685 | |
| 2686 | |
| 2687 | // |
| 2688 | |
| 2689 | HRESULT CordbRefEnum::Next(ULONG celt, COR_GC_REFERENCE refs[], ULONG *pceltFetched) |
| 2690 | { |
| 2691 | if (refs == NULL || pceltFetched == NULL) |
| 2692 | return E_POINTER; |
| 2693 | |
| 2694 | CordbProcess *process = GetProcess(); |
| 2695 | HRESULT hr = S_OK; |
| 2696 | |
| 2697 | PUBLIC_API_ENTRY(this); |
| 2698 | FAIL_IF_NEUTERED(this); |
| 2699 | ATT_REQUIRE_STOPPED_MAY_FAIL(process); |
| 2700 | |
| 2701 | RSLockHolder procLockHolder(process->GetProcessLock()); |
| 2702 | |
| 2703 | EX_TRY |
| 2704 | { |
| 2705 | if (!mRefHandle) |
| 2706 | hr = process->GetDAC()->CreateRefWalk(&mRefHandle, mEnumStacksFQ, mEnumStacksFQ, mHandleMask); |
| 2707 | |
| 2708 | if (SUCCEEDED(hr)) |
| 2709 | { |
| 2710 | DacGcReference dacRefs[32]; |
| 2711 | ULONG toFetch = _countof(dacRefs); |
| 2712 | ULONG total = 0; |
| 2713 | |
| 2714 | for (ULONG c = 0; SUCCEEDED(hr) && c < (celt/_countof(dacRefs) + 1); ++c) |
| 2715 | { |
| 2716 | // Fetch 32 references at a time, the last time, only fetch the remainder (that is, if |
| 2717 | // the user didn't fetch a multiple of 32). |
| 2718 | if (c == celt/_countof(dacRefs)) |
| 2719 | toFetch = celt % _countof(dacRefs); |
| 2720 | |
| 2721 | ULONG fetched = 0; |
| 2722 | hr = process->GetDAC()->WalkRefs(mRefHandle, toFetch, dacRefs, &fetched); |
| 2723 | |
| 2724 | if (SUCCEEDED(hr)) |
| 2725 | { |
| 2726 | for (ULONG i = 0; i < fetched; ++i) |
| 2727 | { |
| 2728 | CordbAppDomain *pDomain = process->LookupOrCreateAppDomain(dacRefs[i].vmDomain); |
| 2729 | |
| 2730 | ICorDebugAppDomain *pAppDomain; |
| 2731 | ICorDebugValue *pOutObject = NULL; |
| 2732 | if (dacRefs[i].pObject & 1) |
| 2733 | { |
| 2734 | dacRefs[i].pObject &= ~1; |
| 2735 | ICorDebugObjectValue *pObjValue = NULL; |
| 2736 | |
| 2737 | hr = process->GetObject(dacRefs[i].pObject, &pObjValue); |
| 2738 | |
| 2739 | if (SUCCEEDED(hr)) |
| 2740 | { |
| 2741 | hr = pObjValue->QueryInterface(IID_ICorDebugValue, (void**)&pOutObject); |
| 2742 | pObjValue->Release(); |
| 2743 | } |
| 2744 | } |
| 2745 | else |
| 2746 | { |
| 2747 | ICorDebugReferenceValue *tmpValue = NULL; |
| 2748 | IfFailThrow(CordbReferenceValue::BuildFromGCHandle(pDomain, |
| 2749 | dacRefs[i].objHnd, |
| 2750 | &tmpValue)); |
| 2751 | |
| 2752 | if (SUCCEEDED(hr)) |
| 2753 | { |
| 2754 | hr = tmpValue->QueryInterface(IID_ICorDebugValue, (void**)&pOutObject); |
| 2755 | tmpValue->Release(); |
| 2756 | } |
| 2757 | } |
| 2758 | |
| 2759 | if (SUCCEEDED(hr) && pDomain) |
| 2760 | { |
| 2761 | hr = pDomain->QueryInterface(IID_ICorDebugAppDomain, (void**)&pAppDomain); |
| 2762 | } |
| 2763 | |
| 2764 | if (FAILED(hr)) |
| 2765 | break; |
| 2766 | |
| 2767 | refs[total].Domain = pAppDomain; |
| 2768 | refs[total].Location = pOutObject; |
| 2769 | refs[total].Type = (CorGCReferenceType)dacRefs[i].dwType; |
| 2770 | refs[total].ExtraData = dacRefs[i].i64ExtraData; |
| 2771 | |
| 2772 | total++; |
| 2773 | } |
| 2774 | } |
| 2775 | } |
| 2776 | |
| 2777 | *pceltFetched = total; |
| 2778 | } |
| 2779 | } |
| 2780 | EX_CATCH_HRESULT(hr); |
| 2781 | |
| 2782 | return hr; |
| 2783 | } |
| 2784 | |
| 2785 | |
| 2786 | // ****************************************** |
| 2787 | // CordbHeapEnum |
| 2788 | // ****************************************** |
| 2789 | CordbHeapEnum::CordbHeapEnum(CordbProcess *proc) |
| 2790 | : CordbBase(proc, 0, enumCordbHeap), mHeapHandle(0) |
| 2791 | { |
| 2792 | } |
| 2793 | |
| 2794 | HRESULT CordbHeapEnum::QueryInterface(REFIID riid, void **ppInterface) |
| 2795 | { |
| 2796 | if (ppInterface == NULL) |
| 2797 | return E_INVALIDARG; |
| 2798 | |
| 2799 | if (riid == IID_ICorDebugHeapEnum) |
| 2800 | { |
| 2801 | *ppInterface = static_cast<ICorDebugHeapEnum*>(this); |
| 2802 | } |
| 2803 | else if (riid == IID_IUnknown) |
| 2804 | { |
| 2805 | *ppInterface = static_cast<IUnknown*>(static_cast<ICorDebugHeapEnum*>(this)); |
| 2806 | } |
| 2807 | else |
| 2808 | { |
| 2809 | *ppInterface = NULL; |
| 2810 | return E_NOINTERFACE; |
| 2811 | } |
| 2812 | |
| 2813 | ExternalAddRef(); |
| 2814 | return S_OK; |
| 2815 | } |
| 2816 | |
| 2817 | HRESULT CordbHeapEnum::Skip(ULONG celt) |
| 2818 | { |
| 2819 | return E_NOTIMPL; |
| 2820 | } |
| 2821 | |
| 2822 | HRESULT CordbHeapEnum::Reset() |
| 2823 | { |
| 2824 | Clear(); |
| 2825 | return S_OK; |
| 2826 | } |
| 2827 | |
| 2828 | void CordbHeapEnum::Clear() |
| 2829 | { |
| 2830 | EX_TRY |
| 2831 | { |
| 2832 | if (mHeapHandle) |
| 2833 | { |
| 2834 | GetProcess()->GetDAC()->DeleteHeapWalk(mHeapHandle); |
| 2835 | mHeapHandle = 0; |
| 2836 | } |
| 2837 | } |
| 2838 | EX_CATCH |
| 2839 | { |
| 2840 | _ASSERTE(!"Hit an error freeing the heap walk." ); |
| 2841 | } |
| 2842 | EX_END_CATCH(SwallowAllExceptions) |
| 2843 | } |
| 2844 | |
| 2845 | HRESULT CordbHeapEnum::Clone(ICorDebugEnum **ppEnum) |
| 2846 | { |
| 2847 | return E_NOTIMPL; |
| 2848 | } |
| 2849 | |
| 2850 | HRESULT CordbHeapEnum::GetCount(ULONG *pcelt) |
| 2851 | { |
| 2852 | return E_NOTIMPL; |
| 2853 | } |
| 2854 | |
| 2855 | HRESULT CordbHeapEnum::Next(ULONG celt, COR_HEAPOBJECT objects[], ULONG *pceltFetched) |
| 2856 | { |
| 2857 | HRESULT hr = S_OK; |
| 2858 | PUBLIC_API_ENTRY(this); |
| 2859 | RSLockHolder stopGoLock(this->GetProcess()->GetStopGoLock()); |
| 2860 | RSLockHolder procLock(this->GetProcess()->GetProcessLock()); |
| 2861 | ULONG fetched = 0; |
| 2862 | |
| 2863 | EX_TRY |
| 2864 | { |
| 2865 | if (mHeapHandle == 0) |
| 2866 | { |
| 2867 | hr = GetProcess()->GetDAC()->CreateHeapWalk(&mHeapHandle); |
| 2868 | } |
| 2869 | |
| 2870 | if (SUCCEEDED(hr)) |
| 2871 | { |
| 2872 | hr = GetProcess()->GetDAC()->WalkHeap(mHeapHandle, celt, objects, &fetched); |
| 2873 | _ASSERTE(fetched <= celt); |
| 2874 | } |
| 2875 | |
| 2876 | if (SUCCEEDED(hr)) |
| 2877 | { |
| 2878 | // Return S_FALSE if we've reached the end of the enum. |
| 2879 | if (fetched < celt) |
| 2880 | hr = S_FALSE; |
| 2881 | } |
| 2882 | } |
| 2883 | EX_CATCH_HRESULT(hr); |
| 2884 | |
| 2885 | // Set the fetched parameter to reflect the number of elements (if any) |
| 2886 | // that were successfully saved to "objects" |
| 2887 | if (pceltFetched) |
| 2888 | *pceltFetched = fetched; |
| 2889 | |
| 2890 | return hr; |
| 2891 | } |
| 2892 | |
| 2893 | //--------------------------------------------------------------------------------------- |
| 2894 | // Flush state for when the process starts running. |
| 2895 | // |
| 2896 | // Notes: |
| 2897 | // Helper for code:CordbProcess::ProcessStateChanged. |
| 2898 | // Since ICD Arrowhead does not own the eventing pipeline, it needs the debugger to |
| 2899 | // notifying it of when the process is running again. This is like the counterpart |
| 2900 | // to code:CordbProcess::Filter |
| 2901 | void CordbProcess::FlushProcessRunning() |
| 2902 | { |
| 2903 | _ASSERTE(GetProcessLock()->HasLock()); |
| 2904 | |
| 2905 | // Update the continue counter. |
| 2906 | m_continueCounter++; |
| 2907 | |
| 2908 | // Safely dispose anything that should be neutered on continue. |
| 2909 | MarkAllThreadsDirty(); |
| 2910 | ForceDacFlush(); |
| 2911 | } |
| 2912 | |
| 2913 | //--------------------------------------------------------------------------------------- |
| 2914 | // Flush all cached state and bring us back to "cold startup" |
| 2915 | // |
| 2916 | // Notes: |
| 2917 | // Helper for code:CordbProcess::ProcessStateChanged. |
| 2918 | // This is used if the data-target changes underneath us in a way that is |
| 2919 | // not consistent with the process running forward. For example, if for |
| 2920 | // a time-travel debugger, the data-target may flow "backwards" in time. |
| 2921 | // |
| 2922 | void CordbProcess::FlushAll() |
| 2923 | { |
| 2924 | CONTRACTL |
| 2925 | { |
| 2926 | THROWS; |
| 2927 | } |
| 2928 | CONTRACTL_END; |
| 2929 | |
| 2930 | HRESULT hr; |
| 2931 | _ASSERTE(GetProcessLock()->HasLock()); |
| 2932 | |
| 2933 | // |
| 2934 | // First, determine if it's safe to Flush |
| 2935 | // |
| 2936 | |
| 2937 | hr = IsReadyForDetach(); |
| 2938 | IfFailThrow(hr); |
| 2939 | |
| 2940 | // Check for outstanding CordbHandle values. |
| 2941 | if (OutstandingHandles()) |
| 2942 | { |
| 2943 | ThrowHR(CORDBG_E_DETACH_FAILED_OUTSTANDING_TARGET_RESOURCES); |
| 2944 | } |
| 2945 | |
| 2946 | // FlushAll is a superset of FlushProcessRunning. |
| 2947 | // This will also ensure we clear the DAC cache. |
| 2948 | FlushProcessRunning(); |
| 2949 | |
| 2950 | // If we detach before the CLR is loaded into the debuggee, then we can no-op a lot of work. |
| 2951 | // We sure can't be sending IPC events to the LS before it exists. |
| 2952 | NeuterChildren(); |
| 2953 | } |
| 2954 | |
| 2955 | //--------------------------------------------------------------------------------------- |
| 2956 | // |
| 2957 | // Detach the Debugger from the LS process. |
| 2958 | // |
| 2959 | // |
| 2960 | // Return Value: |
| 2961 | // S_OK on successful detach. Else errror. |
| 2962 | // |
| 2963 | // Assumptions: |
| 2964 | // Target is stopped. |
| 2965 | // |
| 2966 | // Notes: |
| 2967 | // Once we're detached, the LS can resume running and exit. |
| 2968 | // So it's possible to get an ExitProcess callback in the middle of the Detach phase. If that happens, |
| 2969 | // we must return CORDBG_E_PROCESS_TERMINATED and pretend that the exit happened before we tried to detach. |
| 2970 | // Else if we detach successfully, return S_OK. |
| 2971 | // |
| 2972 | // @dbgtodo attach-bit: need to figure out semantics of Detach |
| 2973 | // in V3, especially w.r.t to an attach bit. |
| 2974 | //--------------------------------------------------------------------------------------- |
| 2975 | HRESULT CordbProcess::Detach() |
| 2976 | { |
| 2977 | PUBLIC_API_ENTRY(this); |
| 2978 | |
| 2979 | FAIL_IF_NEUTERED(this); |
| 2980 | |
| 2981 | if (IsInteropDebugging()) |
| 2982 | { |
| 2983 | return CORDBG_E_INTEROP_NOT_SUPPORTED; |
| 2984 | } |
| 2985 | |
| 2986 | |
| 2987 | HRESULT hr = S_OK; |
| 2988 | // A very important note: we require that the process is synchronized before doing a detach. This ensures |
| 2989 | // that no events are on their way from the Left Side. We also require that the user has drained the |
| 2990 | // managed event queue, but there is currently no way to really enforce that here. |
| 2991 | // @todo- why can't we enforce that the managed event Q is drained? |
| 2992 | ATT_REQUIRE_SYNCED_OR_NONINIT_MAY_FAIL(this); |
| 2993 | |
| 2994 | |
| 2995 | hr = IsReadyForDetach(); |
| 2996 | if (FAILED(hr)) |
| 2997 | { |
| 2998 | // Avoid neutering. Gives client a chance to fix detach issue and retry. |
| 2999 | return hr; |
| 3000 | } |
| 3001 | |
| 3002 | // Since the detach may resume the LS and allow it to exit, which may invoke the EP callback |
| 3003 | // which may destroy this process object, be sure to protect us w/ an extra AddRef/Release |
| 3004 | RSSmartPtr<CordbProcess> pRef(this); |
| 3005 | |
| 3006 | |
| 3007 | |
| 3008 | LOG((LF_CORDB, LL_INFO1000, "CP::Detach - beginning\n" )); |
| 3009 | if (m_pShim == NULL) // This API is moved off to the shim |
| 3010 | { |
| 3011 | |
| 3012 | // This is still invasive. |
| 3013 | // Ignore failures. This will fail for a non-invasive target. |
| 3014 | if (IsDacInitialized()) |
| 3015 | { |
| 3016 | HRESULT hrIgnore = S_OK; |
| 3017 | EX_TRY |
| 3018 | { |
| 3019 | GetDAC()->MarkDebuggerAttached(FALSE); |
| 3020 | } |
| 3021 | EX_CATCH_HRESULT(hrIgnore); |
| 3022 | } |
| 3023 | } |
| 3024 | else |
| 3025 | { |
| 3026 | EX_TRY |
| 3027 | { |
| 3028 | DetachShim(); |
| 3029 | } |
| 3030 | EX_CATCH_HRESULT(hr); |
| 3031 | } |
| 3032 | |
| 3033 | // Either way, neuter everything. |
| 3034 | this->Neuter(); |
| 3035 | |
| 3036 | // Implicit release on pRef |
| 3037 | LOG((LF_CORDB, LL_INFO1000, "CP::Detach - returning w/ hr=0x%x\n" , hr)); |
| 3038 | return hr; |
| 3039 | } |
| 3040 | |
| 3041 | // Free up key left-side resources |
| 3042 | // |
| 3043 | // Called on detach |
| 3044 | // This does key neutering of objects that hold left-side resources and require |
| 3045 | // preemptively freeing the resources. |
| 3046 | // After this, code:CordbProcess::Neuter should only affect right-side state. |
| 3047 | void CordbProcess::NeuterChildrenLeftSideResources() |
| 3048 | { |
| 3049 | _ASSERTE(GetStopGoLock()->HasLock()); |
| 3050 | |
| 3051 | _ASSERTE(!GetProcessLock()->HasLock()); |
| 3052 | RSLockHolder lockHolder(GetProcessLock()); |
| 3053 | |
| 3054 | |
| 3055 | // Need process-lock to operate on hashtable, but can't yet Neuter under process-lock, |
| 3056 | // so we have to copy the contents to an auxilary list which we can then traverse outside the lock. |
| 3057 | RSPtrArray<CordbAppDomain> listAppDomains; |
| 3058 | m_appDomains.CopyToArray(&listAppDomains); |
| 3059 | |
| 3060 | |
| 3061 | |
| 3062 | // Must not hold process lock so that we can be safe to send IPC events |
| 3063 | // to cleanup left-side resources. |
| 3064 | lockHolder.Release(); |
| 3065 | _ASSERTE(!GetProcessLock()->HasLock()); |
| 3066 | |
| 3067 | // Frees left-side resources. This may send IPC events. |
| 3068 | // This will make normal neutering a nop. |
| 3069 | m_LeftSideResourceCleanupList.NeuterLeftSideResourcesAndClear(this); |
| 3070 | |
| 3071 | for(unsigned int idx = 0; idx < listAppDomains.Length(); idx++) |
| 3072 | { |
| 3073 | CordbAppDomain * pAppDomain = listAppDomains[idx]; |
| 3074 | |
| 3075 | // CordbHandleValue is in the appdomain exit list, and that needs |
| 3076 | // to send an IPC event to cleanup and release the handle from |
| 3077 | // the GCs handle table. |
| 3078 | pAppDomain->GetSweepableExitNeuterList()->NeuterLeftSideResourcesAndClear(this); |
| 3079 | } |
| 3080 | listAppDomains.Clear(); |
| 3081 | |
| 3082 | } |
| 3083 | |
| 3084 | //--------------------------------------------------------------------------------------- |
| 3085 | // Detach the Debugger from the LS process for the V2 case |
| 3086 | // |
| 3087 | // Assumptions: |
| 3088 | // This will NeuterChildren(), caller will do the real Neuter() |
| 3089 | // Caller has already ensured that detach is safe. |
| 3090 | // |
| 3091 | // @dbgtodo attach-bit: this should be moved into the shim; need |
| 3092 | // to figure out semantics for freeing left-side resources (especially GC |
| 3093 | // handles) on detach. |
| 3094 | void CordbProcess::DetachShim() |
| 3095 | { |
| 3096 | |
| 3097 | HASHFIND hashFind; |
| 3098 | HRESULT hr = S_OK; |
| 3099 | |
| 3100 | // If we detach before the CLR is loaded into the debuggee, then we can no-op a lot of work. |
| 3101 | // We sure can't be sending IPC events to the LS before it exists. |
| 3102 | if (m_initialized) |
| 3103 | { |
| 3104 | // The managed event queue is not necessarily drained. Cordbg could call detach between any callback. |
| 3105 | // While the process is still stopped, neuter all of our children. |
| 3106 | // This will make our Neuter() a nop and saves the W32ET from having to do dangerous work. |
| 3107 | this->NeuterChildrenLeftSideResources(); |
| 3108 | { |
| 3109 | RSLockHolder lockHolder(GetProcessLock()); |
| 3110 | this->NeuterChildren(); |
| 3111 | } |
| 3112 | |
| 3113 | // Go ahead and detach from the entire process now. This is like sending a "Continue". |
| 3114 | DebuggerIPCEvent * pIPCEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE); |
| 3115 | InitIPCEvent(pIPCEvent, DB_IPCE_DETACH_FROM_PROCESS, true, VMPTR_AppDomain::NullPtr()); |
| 3116 | |
| 3117 | hr = m_cordb->SendIPCEvent(this, pIPCEvent, CorDBIPC_BUFFER_SIZE); |
| 3118 | hr = WORST_HR(hr, pIPCEvent->hr); |
| 3119 | IfFailThrow(hr); |
| 3120 | } |
| 3121 | else |
| 3122 | { |
| 3123 | // @dbgtodo attach-bit: push this up, once detach IPC event is hoisted. |
| 3124 | RSLockHolder lockHolder(GetProcessLock()); |
| 3125 | |
| 3126 | // Shouldn't have any appdomains. |
| 3127 | (void)hashFind; //prevent "unused variable" error from GCC |
| 3128 | _ASSERTE(m_appDomains.FindFirst(&hashFind) == NULL); |
| 3129 | } |
| 3130 | |
| 3131 | LOG((LF_CORDB, LL_INFO10000, "CP::Detach - got reply from LS\n" )); |
| 3132 | |
| 3133 | // It's possible that the LS may exit after they reply to our detach_from_process, but |
| 3134 | // before we update our internal state that they're detached. So still have to check |
| 3135 | // failure codes here. |
| 3136 | hr = this->m_pShim->GetWin32EventThread()->SendDetachProcessEvent(this); |
| 3137 | |
| 3138 | |
| 3139 | // Since we're auto-continuing when we detach, we should set the stop count back to zero. |
| 3140 | // This (along w/ m_detached) prevents anyone from calling Continue on this process |
| 3141 | // after this call returns. |
| 3142 | m_stopCount = 0; |
| 3143 | |
| 3144 | if (hr != CORDBG_E_PROCESS_TERMINATED) |
| 3145 | { |
| 3146 | // Remember that we've detached from this process object. This will prevent any further operations on |
| 3147 | // this process, just in case... :) |
| 3148 | // If LS exited, then don't set this flag because it overrides m_terminated when reporting errors; |
| 3149 | // and we want to provide a consistent story about whether we detached or whether the LS exited. |
| 3150 | m_detached = true; |
| 3151 | } |
| 3152 | IfFailThrow(hr); |
| 3153 | |
| 3154 | |
| 3155 | // Now that all complicated cleanup is done, caller can do a final neuter. |
| 3156 | // This will implicitly stop our Win32 event thread as well. |
| 3157 | } |
| 3158 | |
| 3159 | // Delete all events from the queue without dispatching. This is useful in shutdown. |
| 3160 | // An event that is currently dispatching is not on the queue. |
| 3161 | void CordbProcess::DeleteQueuedEvents() |
| 3162 | { |
| 3163 | INTERNAL_API_ENTRY(this); |
| 3164 | // We must have the process lock to ensure that no one is trying to add an event |
| 3165 | _ASSERTE(!ThreadHoldsProcessLock()); |
| 3166 | |
| 3167 | if (m_pShim != NULL) |
| 3168 | { |
| 3169 | PUBLIC_CALLBACK_IN_THIS_SCOPE0_NO_LOCK(this); |
| 3170 | |
| 3171 | // DeleteAll() is part of the shim, and it will change external ref counts, so must really |
| 3172 | // be marked as outside the RS. |
| 3173 | m_pShim->GetManagedEventQueue()->DeleteAll(); |
| 3174 | } |
| 3175 | } |
| 3176 | |
| 3177 | //--------------------------------------------------------------------------------------- |
| 3178 | // |
| 3179 | // Track that we're about to dispatch a managed event. |
| 3180 | // |
| 3181 | // Arguments: |
| 3182 | // event - event being dispatched |
| 3183 | // |
| 3184 | // Assumptions: |
| 3185 | // This is used to support code:CordbProcess::AreDispatchingEvent |
| 3186 | // This is always called on the same thread as code:CordbProcess::FinishEventDispatch |
| 3187 | void CordbProcess::StartEventDispatch(DebuggerIPCEventType event) |
| 3188 | { |
| 3189 | LIMITED_METHOD_CONTRACT; |
| 3190 | |
| 3191 | _ASSERTE(m_dispatchedEvent == DB_IPCE_DEBUGGER_INVALID); |
| 3192 | _ASSERTE(event != DB_IPCE_DEBUGGER_INVALID); |
| 3193 | m_dispatchedEvent = event; |
| 3194 | } |
| 3195 | |
| 3196 | //--------------------------------------------------------------------------------------- |
| 3197 | // |
| 3198 | // Track that we're done dispatching a managed event. |
| 3199 | // |
| 3200 | // |
| 3201 | // Assumptions: |
| 3202 | // This is always called on the same thread as code:CordbProcess::StartEventDispatch |
| 3203 | // |
| 3204 | // Notes: |
| 3205 | // @dbgtodo shim: eventually this goes into the shim when we hoist Continue |
| 3206 | void CordbProcess::FinishEventDispatch() |
| 3207 | { |
| 3208 | LIMITED_METHOD_CONTRACT; |
| 3209 | |
| 3210 | _ASSERTE(m_dispatchedEvent != DB_IPCE_DEBUGGER_INVALID); |
| 3211 | m_dispatchedEvent = DB_IPCE_DEBUGGER_INVALID; |
| 3212 | } |
| 3213 | |
| 3214 | //--------------------------------------------------------------------------------------- |
| 3215 | // |
| 3216 | // Are we in the middle of dispatching an event? |
| 3217 | // |
| 3218 | // Notes: |
| 3219 | // This is used by code::CordbProcess::ContinueInternal. Continue logic takes |
| 3220 | // a shortcut if the continue is called on the dispatch thread. |
| 3221 | // It doesn't matter which event is being dispatch; only that we're on the dispatch thread. |
| 3222 | // @dbgtodo shim: eventually this goes into the shim when we hoist Continue |
| 3223 | bool CordbProcess::AreDispatchingEvent() |
| 3224 | { |
| 3225 | LIMITED_METHOD_CONTRACT; |
| 3226 | |
| 3227 | return m_dispatchedEvent != DB_IPCE_DEBUGGER_INVALID; |
| 3228 | } |
| 3229 | |
| 3230 | |
| 3231 | |
| 3232 | |
| 3233 | |
| 3234 | // Terminate the app. We'll still dispatch an ExitProcess callback, so the app |
| 3235 | // must wait for that before calling Cordb::Terminate. |
| 3236 | // If this fails, the client can always call the OS's TerminateProcess command |
| 3237 | // to rudely kill the debuggee. |
| 3238 | HRESULT CordbProcess::Terminate(unsigned int exitCode) |
| 3239 | { |
| 3240 | PUBLIC_API_ENTRY(this); |
| 3241 | |
| 3242 | LOG((LF_CORDB, LL_INFO1000, "CP::Terminate: with exitcode %u\n" , exitCode)); |
| 3243 | FAIL_IF_NEUTERED(this); |
| 3244 | |
| 3245 | |
| 3246 | // @dbgtodo shutdown: eventually, all of Terminate() will be in the Shim. |
| 3247 | // Free all the remaining events. Since this will call into the shim, do this outside of any locks. |
| 3248 | // (ATT_ takes locks). |
| 3249 | DeleteQueuedEvents(); |
| 3250 | |
| 3251 | ATT_REQUIRE_SYNCED_OR_NONINIT_MAY_FAIL(this); |
| 3252 | |
| 3253 | // When we terminate the process, it's handle will become signaled and |
| 3254 | // Win32 Event Thread will leap into action and call CordbWin32EventThread::ExitProcess |
| 3255 | // Unfortunately, that may destroy this object if the ExitProcess callback |
| 3256 | // decides to call Release() on the process. |
| 3257 | |
| 3258 | |
| 3259 | // Indicate that the process is exiting so that (among other things) we don't try and |
| 3260 | // send messages to the left side while it's being deleted. |
| 3261 | Lock(); |
| 3262 | |
| 3263 | // In case we're continuing from the loader bp, we don't want to try and kick off an attach. :) |
| 3264 | m_fDoDelayedManagedAttached = false; |
| 3265 | m_exiting = true; |
| 3266 | |
| 3267 | |
| 3268 | |
| 3269 | // We'd like to just take a lock around everything here, but that may deadlock us |
| 3270 | // since W32ET will wait on the lock, and Continue may wait on W32ET. |
| 3271 | // So we just do an extra AddRef/Release to make sure we're still around. |
| 3272 | // @todo - could we move this smartptr up so that it's well-nested w/ the lock? |
| 3273 | RSSmartPtr<CordbProcess> pRef(this); |
| 3274 | |
| 3275 | Unlock(); |
| 3276 | |
| 3277 | |
| 3278 | // At any point after this call, the w32 ET may run the ExitProcess code which will race w/ the continue call. |
| 3279 | // This call only posts a request that the process terminate and does not guarantee the process actually |
| 3280 | // terminates. In particular, the process can not exit until any outstanding IO requests are done (on cancelled). |
| 3281 | // It also can not exit if we have an outstanding not-continued native-debug event. |
| 3282 | // Fortunately, the interesting work in terminate is done in ExitProcessWorkItem::Do, which can take the Stop-Go lock. |
| 3283 | // Since we're currently holding the stop-go lock, that means we at least get some serialization. |
| 3284 | // |
| 3285 | // Note that on Windows, the process isn't really terminated until we receive the EXIT_PROCESS_DEBUG_EVENT. |
| 3286 | // Before then, we can still still access the debuggee's address space. On the other, for Mac debugging, |
| 3287 | // the process can die any time after this call, and so we can no longer call into the DAC. |
| 3288 | GetShim()->GetNativePipeline()->TerminateProcess(exitCode); |
| 3289 | |
| 3290 | // We just call Continue() so that the debugger doesn't have to. (It's arguably odd |
| 3291 | // to call Continue() after Terminate). |
| 3292 | // We're stopped & Synced. |
| 3293 | // For interop-debugging this is very important because the Terminate may not really kill the process |
| 3294 | // until after we continue from the current native debug event. |
| 3295 | ContinueInternal(FALSE); |
| 3296 | |
| 3297 | // Implicit release on pRef here (since it's going out of scope)... |
| 3298 | // After this release, this object may be destroyed. So don't use any member functions |
| 3299 | // (including Locks) after here. |
| 3300 | |
| 3301 | |
| 3302 | return S_OK; |
| 3303 | } |
| 3304 | |
| 3305 | // This can be called at any time, even if we're in an unrecoverable error state. |
| 3306 | HRESULT CordbProcess::GetID(DWORD *pdwProcessId) |
| 3307 | { |
| 3308 | PUBLIC_REENTRANT_API_ENTRY(this); |
| 3309 | OK_IF_NEUTERED(this); |
| 3310 | VALIDATE_POINTER_TO_OBJECT(pdwProcessId, DWORD *); |
| 3311 | |
| 3312 | HRESULT hr = S_OK; |
| 3313 | EX_TRY |
| 3314 | { |
| 3315 | // This shouldn't be used in V3 paths. Normally, we can enforce that by checking against |
| 3316 | // m_pShim. However, this API can be called after being neutered, in which case m_pShim is cleared. |
| 3317 | // So check against 0 instead. |
| 3318 | if (m_id == 0) |
| 3319 | { |
| 3320 | *pdwProcessId = 0; |
| 3321 | ThrowHR(E_NOTIMPL); |
| 3322 | } |
| 3323 | *pdwProcessId = GetProcessDescriptor()->m_Pid; |
| 3324 | } |
| 3325 | EX_CATCH_HRESULT(hr); |
| 3326 | return hr; |
| 3327 | } |
| 3328 | |
| 3329 | // Helper to get process descriptor internally. We know we'll always succeed. |
| 3330 | // This is more convient for internal callers since they can just use it as an expression |
| 3331 | // without having to check HRESULTS. |
| 3332 | const ProcessDescriptor* CordbProcess::GetProcessDescriptor() |
| 3333 | { |
| 3334 | // This shouldn't be used in V3 paths, in which case it's set to 0. Only the shim should be |
| 3335 | // calling this. Assert to catch anybody else. |
| 3336 | _ASSERTE(m_processDescriptor.IsInitialized()); |
| 3337 | |
| 3338 | return &m_processDescriptor; |
| 3339 | } |
| 3340 | |
| 3341 | |
| 3342 | HRESULT CordbProcess::GetHandle(HANDLE *phProcessHandle) |
| 3343 | { |
| 3344 | PUBLIC_REENTRANT_API_ENTRY(this); |
| 3345 | FAIL_IF_NEUTERED(this); // Once we neuter the process, we close our OS handle to it. |
| 3346 | VALIDATE_POINTER_TO_OBJECT(phProcessHandle, HANDLE *); |
| 3347 | |
| 3348 | if (m_pShim == NULL) |
| 3349 | { |
| 3350 | _ASSERTE(!"CordbProcess::GetHandle() should be not be called on the new architecture" ); |
| 3351 | *phProcessHandle = NULL; |
| 3352 | return E_NOTIMPL; |
| 3353 | } |
| 3354 | else |
| 3355 | { |
| 3356 | *phProcessHandle = m_handle; |
| 3357 | return S_OK; |
| 3358 | } |
| 3359 | } |
| 3360 | |
| 3361 | HRESULT CordbProcess::IsRunning(BOOL *pbRunning) |
| 3362 | { |
| 3363 | PUBLIC_API_ENTRY(this); |
| 3364 | FAIL_IF_NEUTERED(this); |
| 3365 | VALIDATE_POINTER_TO_OBJECT(pbRunning, BOOL*); |
| 3366 | |
| 3367 | *pbRunning = !GetSynchronized(); |
| 3368 | |
| 3369 | return S_OK; |
| 3370 | } |
| 3371 | |
| 3372 | HRESULT CordbProcess::EnableSynchronization(BOOL bEnableSynchronization) |
| 3373 | { |
| 3374 | /* !!! */ |
| 3375 | PUBLIC_API_ENTRY(this); |
| 3376 | return E_NOTIMPL; |
| 3377 | } |
| 3378 | |
| 3379 | HRESULT CordbProcess::Stop(DWORD dwTimeout) |
| 3380 | { |
| 3381 | PUBLIC_API_ENTRY(this); |
| 3382 | CORDBRequireProcessStateOK(this); |
| 3383 | |
| 3384 | HRESULT hr = StopInternal(dwTimeout, VMPTR_AppDomain::NullPtr()); |
| 3385 | |
| 3386 | return ErrWrapper(hr); |
| 3387 | } |
| 3388 | |
| 3389 | HRESULT CordbProcess::StopInternal(DWORD dwTimeout, VMPTR_AppDomain pAppDomainToken) |
| 3390 | { |
| 3391 | LOG((LF_CORDB, LL_INFO1000, "CP::S: stopping process 0x%x(%d) with timeout %d\n" , m_id, m_id, dwTimeout)); |
| 3392 | |
| 3393 | INTERNAL_API_ENTRY(this); |
| 3394 | |
| 3395 | // Stop + Continue are executed under the Stop-Go lock. This makes them atomic. |
| 3396 | // We'll toggle the process-lock (b/c we communicate w/ the W32et, so just the process-lock is |
| 3397 | // not sufficient to make this atomic). |
| 3398 | // It's ok to take this lock before checking if the CordbProcess has been neutered because |
| 3399 | // the lock is destroyed in the dtor after neutering. |
| 3400 | RSLockHolder ch(&m_StopGoLock); |
| 3401 | |
| 3402 | // Check if this CordbProcess has been neutered under the SG lock. |
| 3403 | // Otherwise it's possible to race with Detach() and Terminate(). |
| 3404 | FAIL_IF_NEUTERED(this); |
| 3405 | CORDBFailIfOnWin32EventThread(this); |
| 3406 | |
| 3407 | if (m_pShim == NULL) // Stop/Go is moved off to the shim |
| 3408 | { |
| 3409 | return E_NOTIMPL; |
| 3410 | } |
| 3411 | |
| 3412 | |
| 3413 | DebuggerIPCEvent* event; |
| 3414 | HRESULT hr = S_OK; |
| 3415 | |
| 3416 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "CP::SI, timeout=%d, this=%p\n" , dwTimeout, this); |
| 3417 | |
| 3418 | // Stop() is a syncronous (blocking) operation. Furthermore, we have no way to cancel the async-break request. |
| 3419 | // Thus if we returned early on a timeout, then we'll be in a random state b/c the LS may get stopped at any |
| 3420 | // later spot. |
| 3421 | // One solution just require the param is INFINITE until we fix this and E_INVALIDARG if it's not. |
| 3422 | // But that could be a breaking change (what if a debugger passes in a really large value that's effectively |
| 3423 | // INFINITE). |
| 3424 | // So we'll just ignore it and always treat it as infinite. |
| 3425 | dwTimeout = INFINITE; |
| 3426 | |
| 3427 | // Do the checks on the process state under the SG lock. This ensures that another thread cannot come in |
| 3428 | // after we do the checks and take the lock before we do. For example, Detach() can race with Stop() such |
| 3429 | // that: |
| 3430 | // 1. Thread A calls CordbProcess::Detach() and takes the stop-go lock |
| 3431 | // 2. Thread B calls CordbProcess::Stop(), passes all the checks, and then blocks on the stop-go lock |
| 3432 | // 3. Thread A finishes the detach, invalides the process state, cleans all the resources, and then |
| 3433 | // releases the stop-go lock |
| 3434 | // 4. Thread B gets the lock, but everything has changed |
| 3435 | CORDBRequireProcessStateOK(this); |
| 3436 | |
| 3437 | Lock(); |
| 3438 | |
| 3439 | ASSERT_SINGLE_THREAD_ONLY(HoldsLock(&m_StopGoLock)); |
| 3440 | |
| 3441 | // Don't need to stop if the process hasn't even executed any managed code yet. |
| 3442 | if (!m_initialized) |
| 3443 | { |
| 3444 | LOG((LF_CORDB, LL_INFO1000, "CP::S: process isn't initialized yet.\n" )); |
| 3445 | |
| 3446 | // Mark the process as synchronized so no events will be dispatched until the thing is continued. |
| 3447 | SetSynchronized(true); |
| 3448 | |
| 3449 | // Remember uninitialized stop... |
| 3450 | m_uninitializedStop = true; |
| 3451 | |
| 3452 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 3453 | // If we're Win32 attached, then suspend all the unmanaged threads in the process. |
| 3454 | // We may or may not be stopped at a native debug event. |
| 3455 | if (IsInteropDebugging()) |
| 3456 | { |
| 3457 | SuspendUnmanagedThreads(); |
| 3458 | } |
| 3459 | #endif // FEATURE_INTEROP_DEBUGGING |
| 3460 | |
| 3461 | // Get the RC Event Thread to stop listening to the process. |
| 3462 | m_cordb->ProcessStateChanged(); |
| 3463 | |
| 3464 | hr = S_OK; |
| 3465 | goto Exit; |
| 3466 | } |
| 3467 | |
| 3468 | // Don't need to stop if the process is already synchronized. |
| 3469 | // @todo - Issue 129917. It's possible that we'll get a call to Stop when the LS is already stopped. |
| 3470 | // Sending an AsyncBreak would deadlock here (b/c the LS will ignore the frivilous request, |
| 3471 | // and thus never send a SyncComplete, and thus our Waiting on the SyncComplete will deadlock). |
| 3472 | // We avoid this case by checking m_syncCompleteReceived (which should roughly correspond to |
| 3473 | // the LS's m_stopped variable). |
| 3474 | // One window this can happen is after a Continue() pings the RCET but before the RCET actually sweeps + flushes. |
| 3475 | |
| 3476 | if (GetSynchronized() || GetSyncCompleteRecv()) |
| 3477 | { |
| 3478 | LOG((LF_CORDB, LL_INFO1000, "CP::S: process was already synchronized. m_syncCompleteReceived=%d\n" , GetSyncCompleteRecv())); |
| 3479 | |
| 3480 | if (GetSyncCompleteRecv()) |
| 3481 | { |
| 3482 | // We must be in that window alluded to above (while the RCET is sweeping). Re-ping the RCET. |
| 3483 | SetSynchronized(true); |
| 3484 | m_cordb->ProcessStateChanged(); |
| 3485 | } |
| 3486 | hr = S_OK; |
| 3487 | goto Exit; |
| 3488 | } |
| 3489 | |
| 3490 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::S: process not sync'd, requesting stop.\n" ); |
| 3491 | |
| 3492 | m_stopRequested = true; |
| 3493 | |
| 3494 | // We don't want to dispatch any Win32 debug events while we're trying to stop. |
| 3495 | // Setting m_specialDeferment=true means that any debug event we get will be queued and not dispatched. |
| 3496 | // We do this to avoid a nested call to Continue. |
| 3497 | // These defered events will get dispatched when somebody calls continue (and since they're calling |
| 3498 | // stop now, they must call continue eventually). |
| 3499 | // Note that if we got a Win32 debug event between when we took the Stop-Go lock above and now, |
| 3500 | // that even may have been dispatched. We're ok because SSFW32Stop will hijack that event and continue it, |
| 3501 | // and then all future events will be queued. |
| 3502 | m_specialDeferment = true; |
| 3503 | Unlock(); |
| 3504 | |
| 3505 | BOOL asyncBreakSent; |
| 3506 | |
| 3507 | // We need to ensure that the helper thread is alive. |
| 3508 | hr = this->StartSyncFromWin32Stop(&asyncBreakSent); |
| 3509 | if (FAILED(hr)) |
| 3510 | { |
| 3511 | return hr; |
| 3512 | } |
| 3513 | |
| 3514 | |
| 3515 | if (asyncBreakSent) |
| 3516 | { |
| 3517 | hr = S_OK; |
| 3518 | Lock(); |
| 3519 | |
| 3520 | m_stopRequested = false; |
| 3521 | |
| 3522 | goto Exit; |
| 3523 | } |
| 3524 | |
| 3525 | // Send the async break event to the RC. |
| 3526 | event = (DebuggerIPCEvent*) _alloca(CorDBIPC_BUFFER_SIZE); |
| 3527 | InitIPCEvent(event, DB_IPCE_ASYNC_BREAK, false, pAppDomainToken); |
| 3528 | |
| 3529 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CP::S: sending async stop to appd 0x%x.\n" , VmPtrToCookie(pAppDomainToken)); |
| 3530 | |
| 3531 | hr = m_cordb->SendIPCEvent(this, event, CorDBIPC_BUFFER_SIZE); |
| 3532 | hr = WORST_HR(hr, event->hr); |
| 3533 | if (FAILED(hr)) |
| 3534 | { |
| 3535 | // We don't hold the lock so just return immediately. Don't adjust stop-count. |
| 3536 | _ASSERTE(!ThreadHoldsProcessLock()); |
| 3537 | return hr; |
| 3538 | } |
| 3539 | |
| 3540 | LOG((LF_CORDB, LL_INFO1000, "CP::S: sent async stop to appd 0x%x.\n" , VmPtrToCookie(pAppDomainToken))); |
| 3541 | |
| 3542 | // Wait for the sync complete message to come in. Note: when the sync complete message arrives to the RCEventThread, |
| 3543 | // it will mark the process as synchronized and _not_ dispatch any events. Instead, it will set m_stopWaitEvent |
| 3544 | // which will let this function return. If the user wants to process any queued events, they will need to call |
| 3545 | // Continue. |
| 3546 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::S: waiting for event.\n" ); |
| 3547 | |
| 3548 | DWORD ret; |
| 3549 | ret = SafeWaitForSingleObject(this, m_stopWaitEvent, dwTimeout); |
| 3550 | |
| 3551 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CP::S: got event, %d.\n" , ret); |
| 3552 | |
| 3553 | if (m_terminated) |
| 3554 | { |
| 3555 | return CORDBG_E_PROCESS_TERMINATED; |
| 3556 | } |
| 3557 | |
| 3558 | if (ret == WAIT_OBJECT_0) |
| 3559 | { |
| 3560 | LOG((LF_CORDB, LL_INFO1000, "CP::S: process stopped.\n" )); |
| 3561 | |
| 3562 | m_stopRequested = false; |
| 3563 | m_cordb->ProcessStateChanged(); |
| 3564 | |
| 3565 | hr = S_OK; |
| 3566 | Lock(); |
| 3567 | goto Exit; |
| 3568 | } |
| 3569 | else if (ret == WAIT_TIMEOUT) |
| 3570 | { |
| 3571 | hr = ErrWrapper(CORDBG_E_TIMEOUT); |
| 3572 | } |
| 3573 | else |
| 3574 | hr = HRESULT_FROM_GetLastError(); |
| 3575 | |
| 3576 | // We came out of the wait, but we weren't signaled because a sync complete event came in. Re-check the process and |
| 3577 | // remove the stop requested flag. |
| 3578 | Lock(); |
| 3579 | m_stopRequested = false; |
| 3580 | |
| 3581 | if (GetSynchronized()) |
| 3582 | { |
| 3583 | LOG((LF_CORDB, LL_INFO1000, "CP::S: process stopped.\n" )); |
| 3584 | |
| 3585 | m_cordb->ProcessStateChanged(); |
| 3586 | |
| 3587 | hr = S_OK; |
| 3588 | } |
| 3589 | |
| 3590 | Exit: |
| 3591 | _ASSERTE(ThreadHoldsProcessLock()); |
| 3592 | |
| 3593 | // Stop queuing any Win32 Debug events. We should be synchronized now. |
| 3594 | m_specialDeferment = false; |
| 3595 | |
| 3596 | if (SUCCEEDED(hr)) |
| 3597 | { |
| 3598 | IncStopCount(); |
| 3599 | } |
| 3600 | |
| 3601 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "CP::S: returning from Stop, hr=0x%08x, m_stopCount=%d.\n" , hr, GetStopCount()); |
| 3602 | |
| 3603 | Unlock(); |
| 3604 | |
| 3605 | return hr; |
| 3606 | } |
| 3607 | |
| 3608 | //--------------------------------------------------------------------------------------- |
| 3609 | // Clear all RS state on all CordbThread objects. |
| 3610 | // |
| 3611 | // Notes: |
| 3612 | // This clears all the thread-related state that the RS may have cached, |
| 3613 | // such as locals, frames, etc. |
| 3614 | // This would be called if the debugger is resuming execution. |
| 3615 | void CordbProcess::MarkAllThreadsDirty() |
| 3616 | { |
| 3617 | INTERNAL_API_ENTRY(this); |
| 3618 | _ASSERTE(ThreadHoldsProcessLock()); |
| 3619 | |
| 3620 | CordbThread * pThread; |
| 3621 | HASHFIND find; |
| 3622 | |
| 3623 | // We don't need to prepopulate here (to collect LS state) because we're just updating RS state. |
| 3624 | for (pThread = m_userThreads.FindFirst(&find); |
| 3625 | pThread != NULL; |
| 3626 | pThread = m_userThreads.FindNext(&find)) |
| 3627 | { |
| 3628 | _ASSERTE(pThread != NULL); |
| 3629 | pThread->MarkStackFramesDirty(); |
| 3630 | } |
| 3631 | |
| 3632 | ClearPatchTable(); |
| 3633 | } |
| 3634 | |
| 3635 | HRESULT CordbProcess::Continue(BOOL fIsOutOfBand) |
| 3636 | { |
| 3637 | PUBLIC_API_ENTRY(this); |
| 3638 | |
| 3639 | if (m_pShim == NULL) // This API is moved off to the shim |
| 3640 | { |
| 3641 | // bias towards failing with CORDBG_E_NUETERED. |
| 3642 | FAIL_IF_NEUTERED(this); |
| 3643 | return E_NOTIMPL; |
| 3644 | } |
| 3645 | |
| 3646 | HRESULT hr; |
| 3647 | |
| 3648 | if (fIsOutOfBand) |
| 3649 | { |
| 3650 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 3651 | hr = ContinueOOB(); |
| 3652 | #else |
| 3653 | hr = E_INVALIDARG; |
| 3654 | #endif // FEATURE_INTEROP_DEBUGGING |
| 3655 | } |
| 3656 | else |
| 3657 | { |
| 3658 | hr = ContinueInternal(fIsOutOfBand); |
| 3659 | } |
| 3660 | |
| 3661 | return hr; |
| 3662 | } |
| 3663 | |
| 3664 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 3665 | //--------------------------------------------------------------------------------------- |
| 3666 | // |
| 3667 | // ContinueOOB |
| 3668 | // |
| 3669 | // Continue the Win32 event as an out-of-band event. |
| 3670 | // |
| 3671 | // Return Value: |
| 3672 | // S_OK on successful continue. Else error. |
| 3673 | // |
| 3674 | //--------------------------------------------------------------------------------------- |
| 3675 | HRESULT CordbProcess::ContinueOOB() |
| 3676 | { |
| 3677 | INTERNAL_API_ENTRY(this); |
| 3678 | FAIL_IF_NEUTERED(this); |
| 3679 | |
| 3680 | HRESULT hr = S_OK; |
| 3681 | |
| 3682 | // If we're continuing from an out-of-band unmanaged event, then just go |
| 3683 | // ahead and get the Win32 event thread to continue the process. No other |
| 3684 | // work needs to be done (i.e., don't need to send a managed continue message |
| 3685 | // or dispatch any events) because any processing done due to the out-of-band |
| 3686 | // message can't alter the synchronized state of the process. |
| 3687 | |
| 3688 | Lock(); |
| 3689 | _ASSERTE(m_outOfBandEventQueue != NULL); |
| 3690 | |
| 3691 | // Are we calling this from the unmanaged callback? |
| 3692 | if (m_dispatchingOOBEvent) |
| 3693 | { |
| 3694 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: continue while dispatching unmanaged out-of-band event.\n" ); |
| 3695 | // We don't know what thread we're on here. |
| 3696 | |
| 3697 | // Tell the Win32 event thread to continue when it returns from handling its unmanaged callback. |
| 3698 | m_dispatchingOOBEvent = false; |
| 3699 | |
| 3700 | Unlock(); |
| 3701 | } |
| 3702 | else |
| 3703 | { |
| 3704 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: continue outside of dispatching.\n" ); |
| 3705 | |
| 3706 | // If we're not dispatching this, then they shouldn't be on the win32 event thread. |
| 3707 | _ASSERTE(!this->IsWin32EventThread()); |
| 3708 | |
| 3709 | Unlock(); |
| 3710 | |
| 3711 | // Send an event to the Win32 event thread to do the continue. This is an out-of-band continue. |
| 3712 | hr = this->m_pShim->GetWin32EventThread()->SendUnmanagedContinue(this, cOobUMContinue); |
| 3713 | } |
| 3714 | |
| 3715 | return hr; |
| 3716 | |
| 3717 | |
| 3718 | } |
| 3719 | #endif // FEATURE_INTEROP_DEBUGGING |
| 3720 | |
| 3721 | //--------------------------------------------------------------------------------------- |
| 3722 | // |
| 3723 | // ContinueInternal |
| 3724 | // |
| 3725 | // Continue the Win32 event. |
| 3726 | // |
| 3727 | // Return Value: |
| 3728 | // S_OK on success. Else error. |
| 3729 | // |
| 3730 | //--------------------------------------------------------------------------------------- |
| 3731 | HRESULT CordbProcess::ContinueInternal(BOOL fIsOutOfBand) |
| 3732 | { |
| 3733 | INTERNAL_API_ENTRY(this); |
| 3734 | FAIL_IF_NEUTERED(this); |
| 3735 | |
| 3736 | // Continue has an ATT similar to ATT_REQUIRE_STOPPED_MAY_FAIL, but w/ some subtle differences. |
| 3737 | // - if we're stopped at a native DE, but not synchronized, we don't want to sync. |
| 3738 | // - We may get Debug events (especially native ones) at weird times, and thus we have to continue |
| 3739 | // at weird times. |
| 3740 | |
| 3741 | // External APIs should not have the process lock. |
| 3742 | _ASSERTE(!ThreadHoldsProcessLock()); |
| 3743 | _ASSERTE(m_pShim != NULL); |
| 3744 | |
| 3745 | // OutOfBand should use ContinueOOB |
| 3746 | _ASSERTE(!fIsOutOfBand); |
| 3747 | |
| 3748 | // Since Continue is process-wide, just use a null appdomain pointer. |
| 3749 | VMPTR_AppDomain pAppDomainToken = VMPTR_AppDomain::NullPtr(); |
| 3750 | |
| 3751 | HRESULT hr = S_OK; |
| 3752 | |
| 3753 | if (m_unrecoverableError) |
| 3754 | { |
| 3755 | return CORDBHRFromProcessState(this, NULL); |
| 3756 | } |
| 3757 | |
| 3758 | |
| 3759 | // We can't call ContinueInternal for an inband event on the win32 event thread. |
| 3760 | // This is an issue in the CLR (or an API design decision, depending on your perspective). |
| 3761 | // Continue() may send an IPC event and we can't do that on the win32 event thread. |
| 3762 | |
| 3763 | CORDBFailIfOnWin32EventThread(this); |
| 3764 | |
| 3765 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CP::CI: continuing IB, this=0x%X\n" , this); |
| 3766 | |
| 3767 | // Stop + Continue are executed under the Stop-Go lock. This makes them atomic. |
| 3768 | // We'll toggle the process-lock (b/c we communicate w/ the W32et, so that's not sufficient). |
| 3769 | RSLockHolder rsLockHolder(&m_StopGoLock); |
| 3770 | |
| 3771 | // Check for other failures (do these after we have the SG lock). |
| 3772 | if (m_terminated) |
| 3773 | { |
| 3774 | return CORDBG_E_PROCESS_TERMINATED; |
| 3775 | } |
| 3776 | if (m_detached) |
| 3777 | { |
| 3778 | return CORDBG_E_PROCESS_DETACHED; |
| 3779 | } |
| 3780 | |
| 3781 | Lock(); |
| 3782 | |
| 3783 | ASSERT_SINGLE_THREAD_ONLY(HoldsLock(&m_StopGoLock)); |
| 3784 | _ASSERTE(fIsOutOfBand == FALSE); |
| 3785 | |
| 3786 | // If we've got multiple Stop calls, we need a Continue for each one. So, if the stop count > 1, just go ahead and |
| 3787 | // return without doing anything. Note: this is only for in-band or managed events. OOB events are still handled as |
| 3788 | // normal above. |
| 3789 | _ASSERTE(GetStopCount() > 0); |
| 3790 | |
| 3791 | if (GetStopCount() == 0) |
| 3792 | { |
| 3793 | Unlock(); |
| 3794 | _ASSERTE(!"Superflous Continue. ICorDebugProcess.Continue() called too many times" ); |
| 3795 | return CORDBG_E_SUPERFLOUS_CONTINUE; |
| 3796 | } |
| 3797 | |
| 3798 | DecStopCount(); |
| 3799 | |
| 3800 | // We give managed events priority over unmanaged events. That way, the entire queued managed state can drain before |
| 3801 | // we let any other unmanaged events through. |
| 3802 | |
| 3803 | // Every stop or event must be matched by a corresponding Continue. m_stopCount counts outstanding stopping events |
| 3804 | // along with calls to Stop. If the count is high at this point, we simply return. This ensures that even if someone |
| 3805 | // calls Stop just as they're receiving an event that they can call Continue for that Stop and for that event |
| 3806 | // without problems. |
| 3807 | if (GetStopCount() > 0) |
| 3808 | { |
| 3809 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CP::CI: m_stopCount=%d, Continue just returning S_OK...\n" , GetStopCount()); |
| 3810 | |
| 3811 | Unlock(); |
| 3812 | return S_OK; |
| 3813 | } |
| 3814 | |
| 3815 | // We're no longer stopped, so reset the m_stopWaitEvent. |
| 3816 | ResetEvent(m_stopWaitEvent); |
| 3817 | |
| 3818 | // If we're continuing from an uninitialized stop, then we don't need to do much at all. No event need be sent to |
| 3819 | // the Left Side (duh, it isn't even there yet.) We just need to get the RC Event Thread to start listening to the |
| 3820 | // process again, and resume any unmanaged threads if necessary. |
| 3821 | if (m_uninitializedStop) |
| 3822 | { |
| 3823 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: continuing from uninitialized stop.\n" ); |
| 3824 | |
| 3825 | // No longer synchronized (it was a partial sync in the first place.) |
| 3826 | SetSynchronized(false); |
| 3827 | MarkAllThreadsDirty(); |
| 3828 | |
| 3829 | // No longer in an uninitialized stop. |
| 3830 | m_uninitializedStop = false; |
| 3831 | |
| 3832 | // Notify the RC Event Thread. |
| 3833 | m_cordb->ProcessStateChanged(); |
| 3834 | |
| 3835 | Unlock(); |
| 3836 | |
| 3837 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 3838 | // We may or may not have a native debug event queued here. |
| 3839 | // If Cordbg called Stop() from a native debug event (to get the process Synchronized), then |
| 3840 | // we'll have a native debug event, and we need to continue it. |
| 3841 | // If Cordbg called Stop() to do an AsyncBreak, then there's no native-debug event. |
| 3842 | |
| 3843 | // If we're Win32 attached, resume all the unmanaged threads. |
| 3844 | if (IsInteropDebugging()) |
| 3845 | { |
| 3846 | if(m_lastDispatchedIBEvent != NULL) |
| 3847 | { |
| 3848 | m_lastDispatchedIBEvent->SetState(CUES_UserContinued); |
| 3849 | } |
| 3850 | |
| 3851 | // Send to the Win32 event thread to do the unmanaged continue for us. |
| 3852 | // If we're at a debug event, this will continue it. |
| 3853 | // Else it will degenerate into ResumeUnmanagedThreads(); |
| 3854 | this->m_pShim->GetWin32EventThread()->SendUnmanagedContinue(this, cRealUMContinue); |
| 3855 | } |
| 3856 | #endif // FEATURE_INTEROP_DEBUGGING |
| 3857 | |
| 3858 | |
| 3859 | return S_OK; |
| 3860 | } |
| 3861 | |
| 3862 | // If there are more managed events, get them dispatched now. |
| 3863 | if (!m_pShim->GetManagedEventQueue()->IsEmpty() && GetSynchronized()) |
| 3864 | { |
| 3865 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: managed event queued.\n" ); |
| 3866 | |
| 3867 | // Mark that we're not synchronized anymore. |
| 3868 | SetSynchronized(false); |
| 3869 | |
| 3870 | // If the callback queue is not empty, then the LS is not actually continuing, and so our cached |
| 3871 | // state is still valid. |
| 3872 | |
| 3873 | // If we're in the middle of dispatching a managed event, then simply return. This indicates to HandleRCEvent |
| 3874 | // that the user called Continue and HandleRCEvent will dispatch the next queued event. But if Continue was |
| 3875 | // called outside the managed callback, all we have to do is tell the RC event thread that something about the |
| 3876 | // process has changed and it will dispatch the next managed event. |
| 3877 | if (!AreDispatchingEvent()) |
| 3878 | { |
| 3879 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: continuing while not dispatching managed event.\n" ); |
| 3880 | |
| 3881 | m_cordb->ProcessStateChanged(); |
| 3882 | } |
| 3883 | |
| 3884 | Unlock(); |
| 3885 | return S_OK; |
| 3886 | } |
| 3887 | |
| 3888 | // Neuter if we have an outstanding object. |
| 3889 | // Only do this if we're really continuining the debuggee. So don't do this if our stop-count is high b/c we |
| 3890 | // shouldn't neuter until we're done w/ the current event. And don't do this until we drain the current callback queue. |
| 3891 | // Note that we can't hold the process lock while we do this b/c Neutering may send IPC events. |
| 3892 | // However, we're still under the StopGo lock b/c that may help us serialize things. |
| 3893 | |
| 3894 | // Sweep neuter list. This will catch anything that's marked as 'safe to neuter'. This includes |
| 3895 | // all objects added to the 'neuter-on-Continue'. |
| 3896 | // Only do this if we're synced- we don't want to do this if we're continuing from a Native Debug event. |
| 3897 | if (GetSynchronized()) |
| 3898 | { |
| 3899 | // Need process-lock to operate on hashtable, but can't yet Neuter under process-lock, |
| 3900 | // so we have to copy the contents to an auxilary list which we can then traverse outside the lock. |
| 3901 | RSPtrArray<CordbAppDomain> listAppDomains; |
| 3902 | HRESULT hrCopy = S_OK; |
| 3903 | EX_TRY // @dbgtodo cleanup: push this up |
| 3904 | { |
| 3905 | m_appDomains.CopyToArray(&listAppDomains); |
| 3906 | } |
| 3907 | EX_CATCH_HRESULT(hrCopy); |
| 3908 | SetUnrecoverableIfFailed(GetProcess(), hrCopy); |
| 3909 | |
| 3910 | m_ContinueNeuterList.NeuterAndClear(this); |
| 3911 | |
| 3912 | // @dbgtodo left-side resources: eventually (once |
| 3913 | // NeuterLeftSideResources is process-lock safe), do this all under the |
| 3914 | // lock. Can't hold process lock b/c neutering left-side resources |
| 3915 | // may send events. |
| 3916 | Unlock(); |
| 3917 | |
| 3918 | // This may send IPC events. |
| 3919 | // This will make normal neutering a nop. |
| 3920 | // This will toggle the process lock. |
| 3921 | m_LeftSideResourceCleanupList.SweepNeuterLeftSideResources(this); |
| 3922 | |
| 3923 | |
| 3924 | // Many objects (especially CordbValue, FuncEval) don't have clear lifetime semantics and |
| 3925 | // so they must be put into an exit-neuter list (Process/AppDomain) for worst-case scenarios. |
| 3926 | // These objects are likely released early, and so we sweep them aggressively on each Continue (kind of like a mini-GC). |
| 3927 | // |
| 3928 | // One drawback is that there may be a lot of useless sweeping if the debugger creates a lot of |
| 3929 | // objects that it holds onto. Consider instead of sweeping, have the object explicitly post itself |
| 3930 | // to a list that's guaranteed to be cleared. This would let us avoid sweeping not-yet-ready objects. |
| 3931 | // This will toggle the process lock |
| 3932 | m_ExitNeuterList.SweepAllNeuterAtWillObjects(this); |
| 3933 | |
| 3934 | |
| 3935 | for(unsigned int idx = 0; idx < listAppDomains.Length(); idx++) |
| 3936 | { |
| 3937 | CordbAppDomain * pAppDomain = listAppDomains[idx]; |
| 3938 | |
| 3939 | // CordbHandleValue is in the appdomain exit list, and that needs |
| 3940 | // to send an IPC event to cleanup and release the handle from |
| 3941 | // the GCs handle table. |
| 3942 | // This will toggle the process lock. |
| 3943 | pAppDomain->GetSweepableExitNeuterList()->SweepNeuterLeftSideResources(this); |
| 3944 | } |
| 3945 | listAppDomains.Clear(); |
| 3946 | |
| 3947 | Lock(); |
| 3948 | } |
| 3949 | |
| 3950 | |
| 3951 | // At this point, if the managed event queue is empty, m_synchronized may still be true if we had previously |
| 3952 | // synchronized. |
| 3953 | |
| 3954 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 3955 | // Next, check for unmanaged events that may be queued. If there are some queued, then we need to get the Win32 |
| 3956 | // event thread to go ahead and dispatch the next one. If there aren't any queued, then we can just fall through and |
| 3957 | // send the continue message to the left side. This works even if we have an outstanding ownership request, because |
| 3958 | // until that answer is received, its just like the event hasn't happened yet. |
| 3959 | // |
| 3960 | // If we're terminated, then we've already continued from the last win32 event and so don't continue. |
| 3961 | // @todo - or we could ensure the PS_SOME_THREADS_SUSPENDED | PS_HIJACKS_IN_PLACE are removed. |
| 3962 | // Either way, we're just protecting against exit-process at strange times. |
| 3963 | bool fDoWin32Continue = !m_terminated && ((m_state & (PS_WIN32_STOPPED | PS_SOME_THREADS_SUSPENDED | PS_HIJACKS_IN_PLACE)) != 0); |
| 3964 | |
| 3965 | // We need to store this before marking the event user continued below |
| 3966 | BOOL fHasUserUncontinuedEvents = HasUserUncontinuedNativeEvents(); |
| 3967 | |
| 3968 | if(m_lastDispatchedIBEvent != NULL) |
| 3969 | { |
| 3970 | m_lastDispatchedIBEvent->SetState(CUES_UserContinued); |
| 3971 | } |
| 3972 | |
| 3973 | if (fHasUserUncontinuedEvents) |
| 3974 | { |
| 3975 | // ExitProcess is the last debug event we'll get. The Process Handle is not signaled until |
| 3976 | // after we continue from ExitProcess. m_terminated is only set once we know the process is signaled. |
| 3977 | // (This isn't 100% true for the detach case, but since you can't do interop detach, we don't care) |
| 3978 | //_ASSERTE(!m_terminated); |
| 3979 | |
| 3980 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CP::CI: there are queued uncontinued events. m_dispatchingUnmanagedEvent = %d\n" , m_dispatchingUnmanagedEvent); |
| 3981 | |
| 3982 | // Are we being called while in the unmanaged event callback? |
| 3983 | if (m_dispatchingUnmanagedEvent) |
| 3984 | { |
| 3985 | LOG((LF_CORDB, LL_INFO1000, "CP::CI: continue while dispatching.\n" )); |
| 3986 | // The Win32ET could have made a cross-thread call to Continue while dispatching, |
| 3987 | // so we don't know if this is the win32 ET. |
| 3988 | |
| 3989 | // Tell the Win32 thread to continue when it returns from handling its unmanaged callback. |
| 3990 | m_dispatchingUnmanagedEvent = false; |
| 3991 | |
| 3992 | // If there are no more unmanaged events, then we fall through and continue the process for real. Otherwise, |
| 3993 | // we can simply return. |
| 3994 | if (HasUndispatchedNativeEvents()) |
| 3995 | { |
| 3996 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: more unmanaged events need dispatching.\n" ); |
| 3997 | |
| 3998 | // Note: if we tried to access the Left Side while stopped but couldn't, then m_oddSync will be true. We |
| 3999 | // need to reset it to false since we're continuing now. |
| 4000 | m_oddSync = false; |
| 4001 | |
| 4002 | Unlock(); |
| 4003 | return S_OK; |
| 4004 | } |
| 4005 | else |
| 4006 | { |
| 4007 | // Also, if there are no more unmanaged events, then when DispatchUnmanagedInBandEvent sees that |
| 4008 | // m_dispatchingUnmanagedEvent is false, it will continue the process. So we set doWin32Continue to |
| 4009 | // false here so that we don't try to double continue the process below. |
| 4010 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: no more unmanaged events to dispatch.\n" ); |
| 4011 | |
| 4012 | fDoWin32Continue = false; |
| 4013 | } |
| 4014 | } |
| 4015 | else |
| 4016 | { |
| 4017 | // after the DebugEvent callback returned the continue still had no been issued. Then later |
| 4018 | // on another thread the user called back to continue the event, which gets us to right here |
| 4019 | LOG((LF_CORDB, LL_INFO1000, "CP::CI: continue outside of dispatching.\n" )); |
| 4020 | |
| 4021 | // This should be the common place to Dispatch an IB event that was hijacked for sync. |
| 4022 | |
| 4023 | // If we're not dispatching, this better not be the win32 event thread. |
| 4024 | _ASSERTE(!IsWin32EventThread()); |
| 4025 | |
| 4026 | // If the event at the head of the queue is really the last event, or if the event at the head of the queue |
| 4027 | // hasn't been dispatched yet, then we simply fall through and continue the process for real. However, if |
| 4028 | // its not the last event, we send to the Win32 event thread and get it to continue, then we return. |
| 4029 | if (HasUndispatchedNativeEvents()) |
| 4030 | { |
| 4031 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: more unmanaged events need dispatching.\n" ); |
| 4032 | |
| 4033 | // Note: if we tried to access the Left Side while stopped but couldn't, then m_oddSync will be true. We |
| 4034 | // need to reset it to false since we're continuing now. |
| 4035 | m_oddSync = false; |
| 4036 | |
| 4037 | Unlock(); |
| 4038 | |
| 4039 | hr = this->m_pShim->GetWin32EventThread()->SendUnmanagedContinue(this, cRealUMContinue); |
| 4040 | |
| 4041 | return hr; |
| 4042 | } |
| 4043 | } |
| 4044 | } |
| 4045 | #endif // FEATURE_INTEROP_DEBUGGING |
| 4046 | |
| 4047 | // Both the managed and unmanaged event queues are now empty. Go |
| 4048 | // ahead and continue the process for real. |
| 4049 | LOG((LF_CORDB, LL_INFO1000, "CP::CI: headed for true continue.\n" )); |
| 4050 | |
| 4051 | // We need to check these while under the lock, but action must be |
| 4052 | // taked outside of the lock. |
| 4053 | bool fIsExiting = m_exiting; |
| 4054 | bool fWasSynchronized = GetSynchronized(); |
| 4055 | |
| 4056 | // Mark that we're no longer synchronized. |
| 4057 | if (fWasSynchronized) |
| 4058 | { |
| 4059 | LOG((LF_CORDB, LL_INFO1000, "CP::CI: process was synchronized.\n" )); |
| 4060 | |
| 4061 | SetSynchronized(false); |
| 4062 | SetSyncCompleteRecv(false); |
| 4063 | |
| 4064 | // we're no longer in a callback, so set flags to indicate that we've finished. |
| 4065 | GetShim()->NotifyOnContinue(); |
| 4066 | |
| 4067 | // Flush will update state, including continue counter and marking |
| 4068 | // frames dirty. |
| 4069 | this->FlushProcessRunning(); |
| 4070 | |
| 4071 | |
| 4072 | // Tell the RC event thread that something about this process has changed. |
| 4073 | m_cordb->ProcessStateChanged(); |
| 4074 | } |
| 4075 | |
| 4076 | m_continueCounter++; |
| 4077 | |
| 4078 | // If m_oddSync is set, then out last synchronization was due to us syncing the process because we were Win32 |
| 4079 | // stopped. Therefore, while we do need to do most of the work to continue the process below, we don't actually have |
| 4080 | // to send the managed continue event. Setting wasSynchronized to false here helps us do that. |
| 4081 | if (m_oddSync) |
| 4082 | { |
| 4083 | fWasSynchronized = false; |
| 4084 | m_oddSync = false; |
| 4085 | } |
| 4086 | |
| 4087 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 4088 | // We must ensure that all managed threads are suspended here. We're about to let all managed threads run free via |
| 4089 | // the managed continue message to the Left Side. If we don't suspend the managed threads, then they may start |
| 4090 | // slipping forward even if we receive an in-band unmanaged event. We have to hijack in-band unmanaged events while |
| 4091 | // getting the managed continue message over to the Left Side to keep the process running free. Otherwise, the |
| 4092 | // SendIPCEvent will hang below. But in doing so, we could let managed threads slip to far. So we ensure they're all |
| 4093 | // suspended here. |
| 4094 | // |
| 4095 | // Note: we only do this suspension if the helper thread hasn't died yet. If the helper thread has died, then we |
| 4096 | // know that we're loosing the Runtime. No more managed code is going to run, so we don't bother trying to prevent |
| 4097 | // managed threads from slipping via the call below. |
| 4098 | // |
| 4099 | // Note: we just remember here, under the lock, so we can unlock then wait for the syncing thread to free the |
| 4100 | // debugger lock. Otherwise, we may block here and prevent someone from continuing from an OOB event, which also |
| 4101 | // prevents the syncing thread from releasing the debugger lock like we want it to. |
| 4102 | bool fNeedSuspend = fWasSynchronized && fDoWin32Continue && !m_helperThreadDead; |
| 4103 | |
| 4104 | // If we receive a new in-band event once we unlock, we need to know to hijack it and keep going while we're still |
| 4105 | // trying to send the managed continue event to the process. |
| 4106 | if (fWasSynchronized && fDoWin32Continue && !fIsExiting) |
| 4107 | { |
| 4108 | m_specialDeferment = true; |
| 4109 | } |
| 4110 | |
| 4111 | if (fNeedSuspend) |
| 4112 | { |
| 4113 | // @todo - what does this actually accomplish? We already suspended everything when we first synced. |
| 4114 | |
| 4115 | // Any thread that may hold a lock blocking the helper is |
| 4116 | // inside of a can't stop region, and thus we won't suspend it. |
| 4117 | SuspendUnmanagedThreads(); |
| 4118 | } |
| 4119 | #endif // FEATURE_INTEROP_DEBUGGING |
| 4120 | |
| 4121 | Unlock(); |
| 4122 | |
| 4123 | // Although we've released the Process-lock, we still have the Stop-Go lock. |
| 4124 | _ASSERTE(m_StopGoLock.HasLock()); |
| 4125 | |
| 4126 | // If we're processing an ExitProcess managed event, then we don't want to really continue the process, so just fall |
| 4127 | // thru. Note: we did let the unmanaged continue go through above for this case. |
| 4128 | if (fIsExiting) |
| 4129 | { |
| 4130 | LOG((LF_CORDB, LL_INFO1000, "CP::CI: continuing from exit case.\n" )); |
| 4131 | } |
| 4132 | else if (fWasSynchronized) |
| 4133 | { |
| 4134 | LOG((LF_CORDB, LL_INFO1000, "CP::CI: Sending continue to AppD:0x%x.\n" , VmPtrToCookie(pAppDomainToken))); |
| 4135 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 4136 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "Continue flags:special=%d, dowin32=%d\n" , m_specialDeferment, fDoWin32Continue); |
| 4137 | #endif |
| 4138 | // Send to the RC to continue the process. |
| 4139 | DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE); |
| 4140 | |
| 4141 | InitIPCEvent(pEvent, DB_IPCE_CONTINUE, false, pAppDomainToken); |
| 4142 | |
| 4143 | hr = m_cordb->SendIPCEvent(this, pEvent, CorDBIPC_BUFFER_SIZE); |
| 4144 | |
| 4145 | // It is possible that we continue and then the process immediately exits before the helper |
| 4146 | // thread is finished continuing and can report success back to us. That's arguably a success |
| 4147 | // case sinceu the process did indeed continue, but since we didn't get the acknowledgement, |
| 4148 | // we can't be sure it's success. So we call it S_FALSE instead of S_OK. |
| 4149 | // @todo - how do we handle other failure here? |
| 4150 | if (hr == CORDBG_E_PROCESS_TERMINATED) |
| 4151 | { |
| 4152 | hr = S_FALSE; |
| 4153 | } |
| 4154 | _ASSERTE(SUCCEEDED(pEvent->hr)); |
| 4155 | |
| 4156 | LOG((LF_CORDB, LL_INFO1000, "CP::CI: Continue sent to AppD:0x%x.\n" , VmPtrToCookie(pAppDomainToken))); |
| 4157 | } |
| 4158 | |
| 4159 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 4160 | // If we're win32 attached to the Left side, then we need to win32 continue the process too (unless, of course, it's |
| 4161 | // already been done above.) |
| 4162 | // |
| 4163 | // Note: we do this here because we want to get the Left Side to receive and ack our continue message above if we |
| 4164 | // were sync'd. If we were sync'd, then by definition the process (and the helper thread) is running anyway, so all |
| 4165 | // this continue is going to do is to let the threads that have been suspended go. |
| 4166 | if (fDoWin32Continue) |
| 4167 | { |
| 4168 | #ifdef _DEBUG |
| 4169 | { |
| 4170 | // A little pause here extends the special deferment region and thus causes native-debug |
| 4171 | // events to get hijacked. This test some wildly different corner case paths. |
| 4172 | // See VSWhidbey bugs 131905, 168971 |
| 4173 | static DWORD dwRace = -1; |
| 4174 | if (dwRace == -1) |
| 4175 | dwRace = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgRace); |
| 4176 | |
| 4177 | if ((dwRace & 1) == 1) |
| 4178 | { |
| 4179 | Sleep(30); |
| 4180 | } |
| 4181 | } |
| 4182 | #endif |
| 4183 | |
| 4184 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: sending unmanaged continue.\n" ); |
| 4185 | |
| 4186 | // Send to the Win32 event thread to do the unmanaged continue for us. |
| 4187 | hr = this->m_pShim->GetWin32EventThread()->SendUnmanagedContinue(this, cRealUMContinue); |
| 4188 | } |
| 4189 | #endif // FEATURE_INTEROP_DEBUGGING |
| 4190 | |
| 4191 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::CI: continue done, returning.\n" ); |
| 4192 | |
| 4193 | return hr; |
| 4194 | } |
| 4195 | |
| 4196 | HRESULT CordbProcess::HasQueuedCallbacks(ICorDebugThread *pThread, |
| 4197 | BOOL *pbQueued) |
| 4198 | { |
| 4199 | PUBLIC_REENTRANT_API_ENTRY(this); |
| 4200 | FAIL_IF_NEUTERED(this); |
| 4201 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pThread,ICorDebugThread *); |
| 4202 | VALIDATE_POINTER_TO_OBJECT(pbQueued,BOOL *); |
| 4203 | |
| 4204 | // Shim owns the event queue |
| 4205 | if (m_pShim != NULL) |
| 4206 | { |
| 4207 | PUBLIC_CALLBACK_IN_THIS_SCOPE0_NO_LOCK(this); // Calling to shim, leaving RS. |
| 4208 | *pbQueued = m_pShim->GetManagedEventQueue()->HasQueuedCallbacks(pThread); |
| 4209 | return S_OK; |
| 4210 | } |
| 4211 | return E_NOTIMPL; // Not implemented in V3. |
| 4212 | } |
| 4213 | |
| 4214 | // |
| 4215 | // A small helper function to convert a CordbBreakpoint to an ICorDebugBreakpoint based on its type. |
| 4216 | // |
| 4217 | static ICorDebugBreakpoint *CordbBreakpointToInterface(CordbBreakpoint * pBreakpoint) |
| 4218 | { |
| 4219 | _ASSERTE(pBreakpoint != NULL); |
| 4220 | |
| 4221 | // |
| 4222 | // I really dislike this. We've got three subclasses of CordbBreakpoint, but we store them all into the same hash |
| 4223 | // (m_breakpoints), so when we get one out of the hash, we don't really know what type it is. But we need to know |
| 4224 | // what type it is because we need to cast it to the proper interface before passing it out. I.e., when we create a |
| 4225 | // function breakpoint, we return the breakpoint casted to an ICorDebugFunctionBreakpoint. But if we grab that same |
| 4226 | // breakpoint out of the hash as a CordbBreakpoint and pass it out as an ICorDebugBreakpoint, then that's a |
| 4227 | // different pointer, and its wrong. So I've added the type to the breakpoint so we can cast properly here. I'd love |
| 4228 | // to do this a different way, though... |
| 4229 | // |
| 4230 | // -- Mon Dec 14 21:06:46 1998 |
| 4231 | // |
| 4232 | switch(pBreakpoint->GetBPType()) |
| 4233 | { |
| 4234 | case CBT_FUNCTION: |
| 4235 | return static_cast<ICorDebugFunctionBreakpoint *>(static_cast<CordbFunctionBreakpoint *> (pBreakpoint)); |
| 4236 | break; |
| 4237 | |
| 4238 | case CBT_MODULE: |
| 4239 | return static_cast<ICorDebugModuleBreakpoint*>(static_cast<CordbModuleBreakpoint *> (pBreakpoint)); |
| 4240 | break; |
| 4241 | |
| 4242 | case CBT_VALUE: |
| 4243 | return static_cast<ICorDebugValueBreakpoint *>(static_cast<CordbValueBreakpoint *> (pBreakpoint)); |
| 4244 | break; |
| 4245 | |
| 4246 | default: |
| 4247 | _ASSERTE(!"Invalid breakpoint type!" ); |
| 4248 | } |
| 4249 | |
| 4250 | return NULL; |
| 4251 | } |
| 4252 | |
| 4253 | |
| 4254 | // Callback data for code:CordbProcess::GetAssembliesInLoadOrder |
| 4255 | class ShimAssemblyCallbackData |
| 4256 | { |
| 4257 | public: |
| 4258 | // Ctor to intialize callback data |
| 4259 | // |
| 4260 | // Arguments: |
| 4261 | // pAppDomain - appdomain that the assemblies are in. |
| 4262 | // pAssemblies - preallocated array of smart pointers to hold assemblies |
| 4263 | // countAssemblies - size of pAssemblies in elements. |
| 4264 | ShimAssemblyCallbackData( |
| 4265 | CordbAppDomain * pAppDomain, |
| 4266 | RSExtSmartPtr<ICorDebugAssembly>* pAssemblies, |
| 4267 | ULONG countAssemblies) |
| 4268 | { |
| 4269 | _ASSERTE(pAppDomain != NULL); |
| 4270 | _ASSERTE(pAssemblies != NULL); |
| 4271 | |
| 4272 | m_pProcess = pAppDomain->GetProcess(); |
| 4273 | m_pAppDomain = pAppDomain; |
| 4274 | m_pAssemblies = pAssemblies; |
| 4275 | m_countElements = countAssemblies; |
| 4276 | m_index = 0; |
| 4277 | |
| 4278 | // Just to be safe, clear them all out |
| 4279 | for(ULONG i = 0; i < countAssemblies; i++) |
| 4280 | { |
| 4281 | pAssemblies[i].Clear(); |
| 4282 | } |
| 4283 | } |
| 4284 | |
| 4285 | // Dtor |
| 4286 | // |
| 4287 | // Notes: |
| 4288 | // This can assert end-of-enumeration invariants. |
| 4289 | ~ShimAssemblyCallbackData() |
| 4290 | { |
| 4291 | // Ensure that we went through all assemblies. |
| 4292 | _ASSERTE(m_index == m_countElements); |
| 4293 | } |
| 4294 | |
| 4295 | // Callback invoked from DAC enumeration. |
| 4296 | // |
| 4297 | // arguments: |
| 4298 | // vmDomainAssembly - VMPTR for assembly |
| 4299 | // pData - a 'this' pointer |
| 4300 | // |
| 4301 | static void Callback(VMPTR_DomainAssembly vmDomainAssembly, void * pData) |
| 4302 | { |
| 4303 | ShimAssemblyCallbackData * pThis = static_cast<ShimAssemblyCallbackData *> (pData); |
| 4304 | INTERNAL_DAC_CALLBACK(pThis->m_pProcess); |
| 4305 | |
| 4306 | CordbAssembly * pAssembly = pThis->m_pAppDomain->LookupOrCreateAssembly(vmDomainAssembly); |
| 4307 | |
| 4308 | pThis->SetAndMoveNext(pAssembly); |
| 4309 | } |
| 4310 | |
| 4311 | // Set the current index in the table and increment the cursor. |
| 4312 | // |
| 4313 | // Arguments: |
| 4314 | // pAssembly - assembly from DAC enumerator |
| 4315 | void SetAndMoveNext(CordbAssembly * pAssembly) |
| 4316 | { |
| 4317 | _ASSERTE(pAssembly != NULL); |
| 4318 | |
| 4319 | if (m_index >= m_countElements) |
| 4320 | { |
| 4321 | // Enumerating the assemblies in the target should be fixed since |
| 4322 | // the target is not running. |
| 4323 | // We should never get here unless the target is unstable. |
| 4324 | // The caller (the shim) pre-allocated the table of assemblies. |
| 4325 | m_pProcess->TargetConsistencyCheck(!"Target changed assembly count" ); |
| 4326 | return; |
| 4327 | } |
| 4328 | |
| 4329 | m_pAssemblies[m_index].Assign(pAssembly); |
| 4330 | m_index++; |
| 4331 | } |
| 4332 | |
| 4333 | protected: |
| 4334 | CordbProcess * m_pProcess; |
| 4335 | CordbAppDomain * m_pAppDomain; |
| 4336 | RSExtSmartPtr<ICorDebugAssembly>* m_pAssemblies; |
| 4337 | ULONG m_countElements; |
| 4338 | ULONG m_index; |
| 4339 | }; |
| 4340 | |
| 4341 | //--------------------------------------------------------------------------------------- |
| 4342 | // Shim Helper to enumerate the assemblies in the load-order |
| 4343 | // |
| 4344 | // Arguments: |
| 4345 | // pAppdomain - non-null appdomain to enumerate assemblies. |
| 4346 | // pAssemblies - caller pre-allocated array to hold assemblies |
| 4347 | // countAssemblies - size of the array. |
| 4348 | // |
| 4349 | // Notes: |
| 4350 | // Caller preallocated array (likely from ICorDebugAssemblyEnum::GetCount), |
| 4351 | // and now this function fills in the assemblies in the order they were |
| 4352 | // loaded. |
| 4353 | // |
| 4354 | // The target should be stable, such that the number of assemblies in the |
| 4355 | // target is stable, and therefore countAssemblies as determined by the |
| 4356 | // shim via ICorDebugAssemblyEnum::GetCount should match the number of |
| 4357 | // assemblies enumerated here. |
| 4358 | // |
| 4359 | // Called by code:ShimProcess::QueueFakeAttachEvents. |
| 4360 | // This provides the assemblies in load-order. In contrast, |
| 4361 | // ICorDebugAppDomain::EnumerateAssemblies is a random order. The shim needs |
| 4362 | // load-order to match Whidbey semantics for dispatching fake load-assembly |
| 4363 | // callbacks on attach. The debugger then uses the order |
| 4364 | // in its module display window. |
| 4365 | // |
| 4366 | void CordbProcess::GetAssembliesInLoadOrder( |
| 4367 | ICorDebugAppDomain * pAppDomain, |
| 4368 | RSExtSmartPtr<ICorDebugAssembly>* pAssemblies, |
| 4369 | ULONG countAssemblies) |
| 4370 | { |
| 4371 | PUBLIC_API_ENTRY_FOR_SHIM(this); |
| 4372 | RSLockHolder lockHolder(GetProcessLock()); |
| 4373 | |
| 4374 | _ASSERTE(GetShim() != NULL); |
| 4375 | |
| 4376 | CordbAppDomain * pAppDomainInternal = static_cast<CordbAppDomain *> (pAppDomain); |
| 4377 | |
| 4378 | ShimAssemblyCallbackData data(pAppDomainInternal, pAssemblies, countAssemblies); |
| 4379 | |
| 4380 | // Enumerate through and fill out pAssemblies table. |
| 4381 | GetDAC()->EnumerateAssembliesInAppDomain( |
| 4382 | pAppDomainInternal->GetADToken(), |
| 4383 | ShimAssemblyCallbackData::Callback, |
| 4384 | &data); // user data |
| 4385 | |
| 4386 | // pAssemblies array has now been updated. |
| 4387 | } |
| 4388 | |
| 4389 | // Callback data for code:CordbProcess::GetModulesInLoadOrder |
| 4390 | class ShimModuleCallbackData |
| 4391 | { |
| 4392 | public: |
| 4393 | // Ctor to intialize callback data |
| 4394 | // |
| 4395 | // Arguments: |
| 4396 | // pAssembly - assembly that the Modules are in. |
| 4397 | // pModules - preallocated array of smart pointers to hold Modules |
| 4398 | // countModules - size of pModules in elements. |
| 4399 | ShimModuleCallbackData( |
| 4400 | CordbAssembly * pAssembly, |
| 4401 | RSExtSmartPtr<ICorDebugModule>* pModules, |
| 4402 | ULONG countModules) |
| 4403 | { |
| 4404 | _ASSERTE(pAssembly != NULL); |
| 4405 | _ASSERTE(pModules != NULL); |
| 4406 | |
| 4407 | m_pProcess = pAssembly->GetAppDomain()->GetProcess(); |
| 4408 | m_pAssembly = pAssembly; |
| 4409 | m_pModules = pModules; |
| 4410 | m_countElements = countModules; |
| 4411 | m_index = 0; |
| 4412 | |
| 4413 | // Just to be safe, clear them all out |
| 4414 | for(ULONG i = 0; i < countModules; i++) |
| 4415 | { |
| 4416 | pModules[i].Clear(); |
| 4417 | } |
| 4418 | } |
| 4419 | |
| 4420 | // Dtor |
| 4421 | // |
| 4422 | // Notes: |
| 4423 | // This can assert end-of-enumeration invariants. |
| 4424 | ~ShimModuleCallbackData() |
| 4425 | { |
| 4426 | // Ensure that we went through all Modules. |
| 4427 | _ASSERTE(m_index == m_countElements); |
| 4428 | } |
| 4429 | |
| 4430 | // Callback invoked from DAC enumeration. |
| 4431 | // |
| 4432 | // arguments: |
| 4433 | // vmDomainFile - VMPTR for Module |
| 4434 | // pData - a 'this' pointer |
| 4435 | // |
| 4436 | static void Callback(VMPTR_DomainFile vmDomainFile, void * pData) |
| 4437 | { |
| 4438 | ShimModuleCallbackData * pThis = static_cast<ShimModuleCallbackData *> (pData); |
| 4439 | INTERNAL_DAC_CALLBACK(pThis->m_pProcess); |
| 4440 | |
| 4441 | CordbModule * pModule = pThis->m_pAssembly->GetAppDomain()->LookupOrCreateModule(vmDomainFile); |
| 4442 | |
| 4443 | pThis->SetAndMoveNext(pModule); |
| 4444 | } |
| 4445 | |
| 4446 | // Set the current index in the table and increment the cursor. |
| 4447 | // |
| 4448 | // Arguments: |
| 4449 | // pModule - Module from DAC enumerator |
| 4450 | void SetAndMoveNext(CordbModule * pModule) |
| 4451 | { |
| 4452 | _ASSERTE(pModule != NULL); |
| 4453 | |
| 4454 | if (m_index >= m_countElements) |
| 4455 | { |
| 4456 | // Enumerating the Modules in the target should be fixed since |
| 4457 | // the target is not running. |
| 4458 | // We should never get here unless the target is unstable. |
| 4459 | // The caller (the shim) pre-allocated the table of Modules. |
| 4460 | m_pProcess->TargetConsistencyCheck(!"Target changed Module count" ); |
| 4461 | return; |
| 4462 | } |
| 4463 | |
| 4464 | m_pModules[m_index].Assign(pModule); |
| 4465 | m_index++; |
| 4466 | } |
| 4467 | |
| 4468 | protected: |
| 4469 | CordbProcess * m_pProcess; |
| 4470 | CordbAssembly * m_pAssembly; |
| 4471 | RSExtSmartPtr<ICorDebugModule>* m_pModules; |
| 4472 | ULONG m_countElements; |
| 4473 | ULONG m_index; |
| 4474 | }; |
| 4475 | |
| 4476 | //--------------------------------------------------------------------------------------- |
| 4477 | // Shim Helper to enumerate the Modules in the load-order |
| 4478 | // |
| 4479 | // Arguments: |
| 4480 | // pAppdomain - non-null appdomain to enumerate Modules. |
| 4481 | // pModules - caller pre-allocated array to hold Modules |
| 4482 | // countModules - size of the array. |
| 4483 | // |
| 4484 | // Notes: |
| 4485 | // Caller preallocated array (likely from ICorDebugModuleEnum::GetCount), |
| 4486 | // and now this function fills in the Modules in the order they were |
| 4487 | // loaded. |
| 4488 | // |
| 4489 | // The target should be stable, such that the number of Modules in the |
| 4490 | // target is stable, and therefore countModules as determined by the |
| 4491 | // shim via ICorDebugModuleEnum::GetCount should match the number of |
| 4492 | // Modules enumerated here. |
| 4493 | // |
| 4494 | // Called by code:ShimProcess::QueueFakeAssemblyAndModuleEvent. |
| 4495 | // This provides the Modules in load-order. In contrast, |
| 4496 | // ICorDebugAssembly::EnumerateModules is a random order. The shim needs |
| 4497 | // load-order to match Whidbey semantics for dispatching fake load-Module |
| 4498 | // callbacks on attach. The most important thing is that the manifest module |
| 4499 | // gets a LodModule callback before any secondary modules. For dynamic |
| 4500 | // modules, this is necessary for operations on the secondary module |
| 4501 | // that rely on manifest metadata (eg. GetSimpleName). |
| 4502 | // |
| 4503 | // @dbgtodo : This is almost identical to GetAssembliesInLoadOrder, and |
| 4504 | // (together wih the CallbackData classes) seems a HUGE amount of code and |
| 4505 | // complexity for such a simple thing. We also have extra code to order |
| 4506 | // AppDomains and Threads. We should try and rip all of this extra complexity |
| 4507 | // out, and replace it with better data structures for storing these items. |
| 4508 | // Eg., if we used std::map, we could have efficient lookups and ordered |
| 4509 | // enumerations. However, we do need to be careful about exposing new invariants |
| 4510 | // through ICorDebug that customers may depend on, which could place a long-term |
| 4511 | // compatibility burden on us. We could have a simple generic data structure |
| 4512 | // (eg. built on std::hash_map and std::list) which provided efficient look-up |
| 4513 | // and both in-order and random enumeration. |
| 4514 | // |
| 4515 | void CordbProcess::GetModulesInLoadOrder( |
| 4516 | ICorDebugAssembly * pAssembly, |
| 4517 | RSExtSmartPtr<ICorDebugModule>* pModules, |
| 4518 | ULONG countModules) |
| 4519 | { |
| 4520 | PUBLIC_API_ENTRY_FOR_SHIM(this); |
| 4521 | RSLockHolder lockHolder(GetProcessLock()); |
| 4522 | |
| 4523 | _ASSERTE(GetShim() != NULL); |
| 4524 | |
| 4525 | CordbAssembly * pAssemblyInternal = static_cast<CordbAssembly *> (pAssembly); |
| 4526 | |
| 4527 | ShimModuleCallbackData data(pAssemblyInternal, pModules, countModules); |
| 4528 | |
| 4529 | // Enumerate through and fill out pModules table. |
| 4530 | GetDAC()->EnumerateModulesInAssembly( |
| 4531 | pAssemblyInternal->GetDomainAssemblyPtr(), |
| 4532 | ShimModuleCallbackData::Callback, |
| 4533 | &data); // user data |
| 4534 | |
| 4535 | // pModules array has now been updated. |
| 4536 | } |
| 4537 | |
| 4538 | |
| 4539 | //--------------------------------------------------------------------------------------- |
| 4540 | // Callback to count the number of enumerations in a process. |
| 4541 | // |
| 4542 | // Arguments: |
| 4543 | // id - the connection id. |
| 4544 | // pName - name of the connection |
| 4545 | // pUserData - an EnumerateConnectionsData |
| 4546 | // |
| 4547 | // Notes: |
| 4548 | // Helper function for code:CordbProcess::QueueFakeConnectionEvents |
| 4549 | // |
| 4550 | // static |
| 4551 | void CordbProcess::CountConnectionsCallback(DWORD id, LPCWSTR pName, void * pUserData) |
| 4552 | { |
| 4553 | } |
| 4554 | |
| 4555 | //--------------------------------------------------------------------------------------- |
| 4556 | // Callback to enumerate all the connections in a process. |
| 4557 | // |
| 4558 | // Arguments: |
| 4559 | // id - the connection id. |
| 4560 | // pName - name of the connection |
| 4561 | // pUserData - an EnumerateConnectionsData |
| 4562 | // |
| 4563 | // Notes: |
| 4564 | // Helper function for code:CordbProcess::QueueFakeConnectionEvents |
| 4565 | // |
| 4566 | // static |
| 4567 | void CordbProcess::EnumerateConnectionsCallback(DWORD id, LPCWSTR pName, void * pUserData) |
| 4568 | { |
| 4569 | } |
| 4570 | |
| 4571 | //--------------------------------------------------------------------------------------- |
| 4572 | // Callback from Shim to queue fake Connection events on attach. |
| 4573 | // |
| 4574 | // Notes: |
| 4575 | // See code:ShimProcess::QueueFakeAttachEvents |
| 4576 | void CordbProcess::QueueFakeConnectionEvents() |
| 4577 | { |
| 4578 | PUBLIC_API_ENTRY_FOR_SHIM(this); |
| 4579 | |
| 4580 | } |
| 4581 | |
| 4582 | // |
| 4583 | // DispatchRCEvent -- dispatches a previously queued IPC event received |
| 4584 | // from the runtime controller. This represents the last amount of processing |
| 4585 | // the DI gets to do on an event before giving it to the user. |
| 4586 | // |
| 4587 | void CordbProcess::DispatchRCEvent() |
| 4588 | { |
| 4589 | INTERNAL_API_ENTRY(this); |
| 4590 | |
| 4591 | CONTRACTL |
| 4592 | { |
| 4593 | // This is happening on the RCET thread, so there's no place to propogate an error back up. |
| 4594 | NOTHROW; |
| 4595 | } |
| 4596 | CONTRACTL_END; |
| 4597 | |
| 4598 | _ASSERTE(m_pShim != NULL); // V2 case |
| 4599 | |
| 4600 | // |
| 4601 | // Note: the current thread should have the process locked when it |
| 4602 | // enters this method. |
| 4603 | // |
| 4604 | _ASSERTE(ThreadHoldsProcessLock()); |
| 4605 | |
| 4606 | // Create/Launch paths already ensured that we had a callback. |
| 4607 | _ASSERTE(m_cordb != NULL); |
| 4608 | _ASSERTE(m_cordb->m_managedCallback != NULL); |
| 4609 | _ASSERTE(m_cordb->m_managedCallback2 != NULL); |
| 4610 | _ASSERTE(m_cordb->m_managedCallback3 != NULL); |
| 4611 | _ASSERTE(m_cordb->m_managedCallback4 != NULL); |
| 4612 | |
| 4613 | |
| 4614 | // Bump up the stop count. Either we'll dispatch a managed event, |
| 4615 | // or the logic below will decide not to dispatch one and call |
| 4616 | // Continue itself. Either way, the stop count needs to go up by |
| 4617 | // one... |
| 4618 | _ASSERTE(this->GetSyncCompleteRecv()); |
| 4619 | SetSynchronized(true); |
| 4620 | IncStopCount(); |
| 4621 | |
| 4622 | // As soon as we call Unlock(), we might get neutered and lose our reference to |
| 4623 | // the shim. Grab it now for use later. |
| 4624 | RSExtSmartPtr<ShimProcess> pShim(m_pShim); |
| 4625 | |
| 4626 | Unlock(); |
| 4627 | |
| 4628 | _ASSERTE(!ThreadHoldsProcessLock()); |
| 4629 | |
| 4630 | |
| 4631 | // We want to stay synced until after the callbacks return. This is b/c we're on the RCET, |
| 4632 | // and we may deadlock if we send IPC events on the RCET if we're not synced (see SendIPCEvent for details). |
| 4633 | // So here, stopcount=1. The StopContinueHolder bumps it up to 2. |
| 4634 | // - If Cordbg calls continue in the callback, that bumps it back down to 1, but doesn't actually continue. |
| 4635 | // The holder dtor then bumps it down to 0, doing the real continue. |
| 4636 | // - If Cordbg doesn't call continue in the callback, then stopcount stays at 2, holder dtor drops it down to 1, |
| 4637 | // and then the holder was just a nop. |
| 4638 | // This gives us delayed continues w/ no extra state flags. |
| 4639 | |
| 4640 | |
| 4641 | // The debugger may call Detach() immediately after it returns from the callback, but before this thread returns |
| 4642 | // from this function. Thus after we execute the callbacks, it's possible the CordbProcess object has been neutered. |
| 4643 | |
| 4644 | // Since we're already sycned, the Stop from the holder here is practically a nop that just bumps up a count. |
| 4645 | // Create an extra scope for the StopContinueHolder. |
| 4646 | { |
| 4647 | StopContinueHolder h; |
| 4648 | HRESULT hr = h.Init(this); |
| 4649 | if (FAILED(hr)) |
| 4650 | { |
| 4651 | CORDBSetUnrecoverableError(this, hr, 0); |
| 4652 | } |
| 4653 | |
| 4654 | HRESULT hrCallback = S_OK; |
| 4655 | // It's possible a ICorDebugProcess::Detach() may have occurred by now. |
| 4656 | { |
| 4657 | // @dbgtodo shim: eventually the entire RCET should be considered outside the RS. |
| 4658 | PUBLIC_CALLBACK_IN_THIS_SCOPE0_NO_LOCK(this); |
| 4659 | |
| 4660 | |
| 4661 | // Snag the first event off the queue. |
| 4662 | // Holder will call Delete, which will invoke virtual Dtor that will release ICD objects. |
| 4663 | // Since these are external refs, we want to do it while "outside" the RS. |
| 4664 | NewHolder<ManagedEvent> pEvent(pShim->DequeueManagedEvent()); |
| 4665 | |
| 4666 | // Normally pEvent shouldn't be NULL, since this method is called when the queue is not empty. |
| 4667 | // But due to a race between CordbProcess::Terminate(), CordbWin32EventThread::ExitProcess() and this method |
| 4668 | // it is totally possible that the queue has already been cleaned up and we can't expect that event is always available. |
| 4669 | if (pEvent != NULL) |
| 4670 | { |
| 4671 | // Since we need to access a member (m_cordb), protect this block with a |
| 4672 | // lock and a check for Neutering (in case process detach has just |
| 4673 | // occurred). We'll release the lock around the dispatch later on. |
| 4674 | RSLockHolder lockHolder(GetProcessLock()); |
| 4675 | if (!IsNeutered()) |
| 4676 | { |
| 4677 | #ifdef _DEBUG |
| 4678 | // On a debug build, keep track of the last IPC event we dispatched. |
| 4679 | m_pDBGLastIPCEventType = pEvent->GetDebugCookie(); |
| 4680 | #endif |
| 4681 | |
| 4682 | ManagedEvent::DispatchArgs args(m_cordb->m_managedCallback, m_cordb->m_managedCallback2, m_cordb->m_managedCallback3, m_cordb->m_managedCallback4); |
| 4683 | |
| 4684 | { |
| 4685 | // Release lock around the dispatch of the event |
| 4686 | RSInverseLockHolder inverseLockHolder(GetProcessLock()); |
| 4687 | |
| 4688 | EX_TRY |
| 4689 | { |
| 4690 | // This dispatches almost directly into the user's callbacks. |
| 4691 | // It does not update any RS state. |
| 4692 | hrCallback = pEvent->Dispatch(args); |
| 4693 | } |
| 4694 | EX_CATCH_HRESULT(hrCallback); |
| 4695 | } |
| 4696 | } |
| 4697 | } |
| 4698 | |
| 4699 | } // we're now back inside the RS |
| 4700 | |
| 4701 | if (hrCallback == E_NOTIMPL) |
| 4702 | { |
| 4703 | ContinueInternal(FALSE); |
| 4704 | } |
| 4705 | |
| 4706 | |
| 4707 | } // forces Continue to be called |
| 4708 | |
| 4709 | Lock(); |
| 4710 | |
| 4711 | }; |
| 4712 | |
| 4713 | #ifdef _DEBUG |
| 4714 | //--------------------------------------------------------------------------------------- |
| 4715 | // Debug-only callback to ensure that an appdomain is not available after the ExitAppDomain event. |
| 4716 | // |
| 4717 | // Arguments: |
| 4718 | // vmAppDomain - appdomain from enumeration |
| 4719 | // pUserData - pointer to a DbgAssertAppDomainDeletedData which contains the VMAppDomain that was just deleted. |
| 4720 | // notes: |
| 4721 | // see code:CordbProcess::DbgAssertAppDomainDeleted for details. |
| 4722 | void CordbProcess::DbgAssertAppDomainDeletedCallback(VMPTR_AppDomain vmAppDomain, void * pUserData) |
| 4723 | { |
| 4724 | DbgAssertAppDomainDeletedData * pCallbackData = reinterpret_cast<DbgAssertAppDomainDeletedData *>(pUserData); |
| 4725 | INTERNAL_DAC_CALLBACK(pCallbackData->m_pThis); |
| 4726 | |
| 4727 | VMPTR_AppDomain vmAppDomainDeleted = pCallbackData->m_vmAppDomainDeleted; |
| 4728 | CONSISTENCY_CHECK_MSGF((vmAppDomain != vmAppDomainDeleted), |
| 4729 | ("An ExitAppDomain event was sent for appdomain, but it still shows up in the enumeration.\n vmAppDomain=%p\n" , |
| 4730 | VmPtrToCookie(vmAppDomainDeleted))); |
| 4731 | } |
| 4732 | |
| 4733 | //--------------------------------------------------------------------------------------- |
| 4734 | // Debug-only helper to Assert that VMPTR is actually removed. |
| 4735 | // |
| 4736 | // Arguments: |
| 4737 | // vmAppDomainDeleted - vmptr of appdomain that we just got exit event for. |
| 4738 | // This should not be discoverable from the RS. |
| 4739 | // |
| 4740 | // Notes: |
| 4741 | // See code:IDacDbiInterface#Enumeration for rules that we're asserting. |
| 4742 | // Once the exit appdomain event is dispatched, the appdomain should not be discoverable by the RS. |
| 4743 | // Else the RS may use the AppDomain* after it's deleted. |
| 4744 | // This asserts that the AppDomain* is not discoverable. |
| 4745 | // |
| 4746 | // Since this is a debug-only function, it should have no side-effects. |
| 4747 | void CordbProcess::DbgAssertAppDomainDeleted(VMPTR_AppDomain vmAppDomainDeleted) |
| 4748 | { |
| 4749 | DbgAssertAppDomainDeletedData callbackData; |
| 4750 | callbackData.m_pThis = this; |
| 4751 | callbackData.m_vmAppDomainDeleted = vmAppDomainDeleted; |
| 4752 | |
| 4753 | GetDAC()->EnumerateAppDomains( |
| 4754 | CordbProcess::DbgAssertAppDomainDeletedCallback, |
| 4755 | &callbackData); |
| 4756 | } |
| 4757 | |
| 4758 | #endif // _DEBUG |
| 4759 | |
| 4760 | //--------------------------------------------------------------------------------------- |
| 4761 | // Update state and potentially Dispatch a single event. |
| 4762 | // |
| 4763 | // Arguments: |
| 4764 | // pEvent - non-null pointer to debug event. |
| 4765 | // pCallback1 - callback object to dispatch on (for V1 callbacks) |
| 4766 | // pCallback2 - 2nd callback object to dispatch on (for new V2 callbacks) |
| 4767 | // pCallback3 - 3rd callback object to dispatch on (for new V4 callbacks) |
| 4768 | // |
| 4769 | // |
| 4770 | // Returns: |
| 4771 | // Nothing. Throws on error. |
| 4772 | // |
| 4773 | // Notes: |
| 4774 | // Generally, this will dispatch exactly 1 callback. It may dispatch 0 callbacks if there is an error |
| 4775 | // or in other corner cases (documented within the dispatch code below). |
| 4776 | // Errors could occur because: |
| 4777 | // - the event is corrupted (exceptional case) |
| 4778 | // - the RS is corrupted / OOM (exceptional case) |
| 4779 | // Exception errors here will propogate back to the Filter() call, and there's not really anything |
| 4780 | // a debugger can do about an error here (perhaps report it to the user). |
| 4781 | // Errors must leave IcorDebug in a consistent state. |
| 4782 | // |
| 4783 | // This is dispatched directly on the Win32Event Thread in response to calling Filter. |
| 4784 | // Therefore, this can't send any IPC events (Not an issue once everything is DAC-ized). |
| 4785 | // A V2 shim can provide a proxy calllack that takes these events and queues them and |
| 4786 | // does the real dispatch to the user to emulate V2 semantics. |
| 4787 | // |
| 4788 | #ifdef _PREFAST_ |
| 4789 | #pragma warning(push) |
| 4790 | #pragma warning(disable:21000) // Suppress PREFast warning about overly large function |
| 4791 | #endif |
| 4792 | void CordbProcess::RawDispatchEvent( |
| 4793 | DebuggerIPCEvent * pEvent, |
| 4794 | RSLockHolder * pLockHolder, |
| 4795 | ICorDebugManagedCallback * pCallback1, |
| 4796 | ICorDebugManagedCallback2 * pCallback2, |
| 4797 | ICorDebugManagedCallback3 * pCallback3, |
| 4798 | ICorDebugManagedCallback4 * pCallback4) |
| 4799 | { |
| 4800 | CONTRACTL |
| 4801 | { |
| 4802 | THROWS; |
| 4803 | } |
| 4804 | CONTRACTL_END; |
| 4805 | |
| 4806 | HRESULT hr = S_OK; |
| 4807 | // We start off with the lock, and we'll toggle it. |
| 4808 | _ASSERTE(ThreadHoldsProcessLock()); |
| 4809 | |
| 4810 | |
| 4811 | // |
| 4812 | // Call StartEventDispatch to true to guard against calls to Continue() |
| 4813 | // from within the user's callback. We need Continue() to behave a little |
| 4814 | // bit differently in such a case. |
| 4815 | // |
| 4816 | // Also note that Win32EventThread::ExitProcess will take the lock and free all |
| 4817 | // events in the queue. (the current event is already off the queue, so |
| 4818 | // it will be ok). But we can't do the EP callback in the middle of this dispatch |
| 4819 | // so if this flag is set, EP will wait on the miscWaitEvent (which will |
| 4820 | // get set in FlushQueuedEvents when we return from here) and let us finish here. |
| 4821 | // |
| 4822 | StartEventDispatch(pEvent->type); |
| 4823 | |
| 4824 | // Keep strong references to these objects in case a callback deletes them from underneath us. |
| 4825 | RSSmartPtr<CordbAppDomain> pAppDomain; |
| 4826 | CordbThread * pThread = NULL; |
| 4827 | |
| 4828 | |
| 4829 | // Get thread that this event is on. In attach scenarios, this may be the first time ICorDebug has seen this thread. |
| 4830 | if (!pEvent->vmThread.IsNull()) |
| 4831 | { |
| 4832 | pThread = LookupOrCreateThread(pEvent->vmThread); |
| 4833 | } |
| 4834 | |
| 4835 | if (!pEvent->vmAppDomain.IsNull()) |
| 4836 | { |
| 4837 | pAppDomain.Assign(LookupOrCreateAppDomain(pEvent->vmAppDomain)); |
| 4838 | } |
| 4839 | |
| 4840 | DWORD dwVolatileThreadId = 0; |
| 4841 | if (pThread != NULL) |
| 4842 | { |
| 4843 | dwVolatileThreadId = pThread->GetUniqueId(); |
| 4844 | } |
| 4845 | |
| 4846 | |
| 4847 | // |
| 4848 | // Update the app domain that this thread lives in. |
| 4849 | // |
| 4850 | if ((pThread != NULL) && (pAppDomain != NULL)) |
| 4851 | { |
| 4852 | // It shouldn't be possible for us to see an exited AppDomain here |
| 4853 | _ASSERTE( !pAppDomain->IsNeutered() ); |
| 4854 | |
| 4855 | pThread->m_pAppDomain = pAppDomain; |
| 4856 | } |
| 4857 | |
| 4858 | _ASSERTE(pEvent != NULL); |
| 4859 | _ASSERTE(pCallback1 != NULL); |
| 4860 | _ASSERTE(pCallback2 != NULL); |
| 4861 | _ASSERTE(pCallback3 != NULL); |
| 4862 | _ASSERTE(pCallback4 != NULL); |
| 4863 | |
| 4864 | STRESS_LOG1(LF_CORDB, LL_EVERYTHING, "Pre-Dispatch IPC event: %s\n" , IPCENames::GetName(pEvent->type)); |
| 4865 | |
| 4866 | switch (pEvent->type & DB_IPCE_TYPE_MASK) |
| 4867 | { |
| 4868 | case DB_IPCE_CREATE_PROCESS: |
| 4869 | { |
| 4870 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 4871 | pCallback1->CreateProcess(static_cast<ICorDebugProcess*> (this)); |
| 4872 | } |
| 4873 | break; |
| 4874 | |
| 4875 | case DB_IPCE_BREAKPOINT: |
| 4876 | { |
| 4877 | _ASSERTE(pThread != NULL); |
| 4878 | _ASSERTE(pAppDomain != NULL); |
| 4879 | |
| 4880 | // Find the breakpoint object on this side. |
| 4881 | CordbBreakpoint *pBreakpoint = NULL; |
| 4882 | |
| 4883 | // We've found cases out in the wild where we get this event on a thread we don't recognize. |
| 4884 | // We're not sure how this happens. Add a runtime check to protect ourselves to avoid the |
| 4885 | // an AV. We still assert because this should not be happening. |
| 4886 | // It likely means theres some issue where we failed to send a CreateThread notification. |
| 4887 | TargetConsistencyCheck(pThread != NULL); |
| 4888 | pBreakpoint = pAppDomain->m_breakpoints.GetBase(LsPtrToCookie(pEvent->BreakpointData.breakpointToken)); |
| 4889 | |
| 4890 | if (pBreakpoint != NULL) |
| 4891 | { |
| 4892 | ICorDebugBreakpoint * pIBreakpoint = CordbBreakpointToInterface(pBreakpoint); |
| 4893 | _ASSERTE(pIBreakpoint != NULL); |
| 4894 | |
| 4895 | { |
| 4896 | PUBLIC_CALLBACK_IN_THIS_SCOPE2(this, pLockHolder, pEvent, "thread=0x%p, bp=0x%p" , pThread, pBreakpoint); |
| 4897 | pCallback1->Breakpoint(pAppDomain, pThread, pIBreakpoint); |
| 4898 | } |
| 4899 | } |
| 4900 | } |
| 4901 | break; |
| 4902 | |
| 4903 | case DB_IPCE_BEFORE_GARBAGE_COLLECTION: |
| 4904 | { |
| 4905 | { |
| 4906 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 4907 | pCallback4->BeforeGarbageCollection(static_cast<ICorDebugProcess*>(this)); |
| 4908 | } |
| 4909 | break; |
| 4910 | } |
| 4911 | |
| 4912 | case DB_IPCE_AFTER_GARBAGE_COLLECTION: |
| 4913 | { |
| 4914 | { |
| 4915 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 4916 | pCallback4->AfterGarbageCollection(static_cast<ICorDebugProcess*>(this)); |
| 4917 | } |
| 4918 | break; |
| 4919 | } |
| 4920 | #ifdef FEATURE_DATABREAKPOINT |
| 4921 | case DB_IPCE_DATA_BREAKPOINT: |
| 4922 | { |
| 4923 | _ASSERTE(pThread != NULL); |
| 4924 | |
| 4925 | { |
| 4926 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 4927 | pCallback4->DataBreakpoint(static_cast<ICorDebugProcess*>(this), pThread, reinterpret_cast<BYTE*>(&(pEvent->DataBreakpointData.context)), sizeof(CONTEXT)); |
| 4928 | } |
| 4929 | break; |
| 4930 | } |
| 4931 | break; |
| 4932 | #endif |
| 4933 | case DB_IPCE_USER_BREAKPOINT: |
| 4934 | { |
| 4935 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "[%x] RCET::DRCE: user breakpoint.\n" , |
| 4936 | GetCurrentThreadId()); |
| 4937 | |
| 4938 | _ASSERTE(pThread != NULL); |
| 4939 | _ASSERTE(pAppDomain != NULL); |
| 4940 | _ASSERTE(pThread->m_pAppDomain != NULL); |
| 4941 | |
| 4942 | { |
| 4943 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 4944 | pCallback1->Break(pThread->m_pAppDomain, pThread); |
| 4945 | } |
| 4946 | |
| 4947 | } |
| 4948 | break; |
| 4949 | |
| 4950 | case DB_IPCE_STEP_COMPLETE: |
| 4951 | { |
| 4952 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "[%x] RCET::DRCE: step complete.\n" , |
| 4953 | GetCurrentThreadId()); |
| 4954 | |
| 4955 | PREFIX_ASSUME(pThread != NULL); |
| 4956 | |
| 4957 | CordbStepper * pStepper = m_steppers.GetBase(LsPtrToCookie(pEvent->StepData.stepperToken)); |
| 4958 | |
| 4959 | // It's possible the stepper is NULL if: |
| 4960 | // - event X & step-complete are both in the queue |
| 4961 | // - during dispatch for event X, Cordbg cancels the stepper (thus removing it from m_steppers) |
| 4962 | // - the Step-Complete still stays in the queue, and so we're here, but out stepper's been removed. |
| 4963 | // (This could happen for breakpoints too) |
| 4964 | // Don't dispatch a callback if the stepper is NULL. |
| 4965 | if (pStepper != NULL) |
| 4966 | { |
| 4967 | RSSmartPtr<CordbStepper> pRef(pStepper); |
| 4968 | pStepper->m_active = false; |
| 4969 | m_steppers.RemoveBase((ULONG_PTR)pStepper->m_id); |
| 4970 | |
| 4971 | { |
| 4972 | _ASSERTE(pThread->m_pAppDomain != NULL); |
| 4973 | PUBLIC_CALLBACK_IN_THIS_SCOPE2(this, pLockHolder, pEvent, "thrad=0x%p, stepper=0x%p" , pThread, pStepper); |
| 4974 | pCallback1->StepComplete(pThread->m_pAppDomain, pThread, pStepper, pEvent->StepData.reason); |
| 4975 | } |
| 4976 | |
| 4977 | // implicit Release on pRef |
| 4978 | } |
| 4979 | } |
| 4980 | break; |
| 4981 | |
| 4982 | case DB_IPCE_EXCEPTION: |
| 4983 | { |
| 4984 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "[%x] RCET::DRCE: exception.\n" , |
| 4985 | GetCurrentThreadId()); |
| 4986 | |
| 4987 | _ASSERTE(pAppDomain != NULL); |
| 4988 | |
| 4989 | // For some exceptions very early in startup (eg, TypeLoad), this may have occurred before we |
| 4990 | // even executed jitted code on the thread. We may have not received a CreateThread yet. |
| 4991 | // In V2, we detected this and sent a LogMessage on a random thread. |
| 4992 | // In V3, we lazily create the CordbThread objects (possibly before the CreateThread event), |
| 4993 | // and so we know we should have one. |
| 4994 | _ASSERTE(pThread != NULL); |
| 4995 | |
| 4996 | pThread->SetExInfo(pEvent->Exception.vmExceptionHandle); |
| 4997 | |
| 4998 | _ASSERTE(pThread->m_pAppDomain != NULL); |
| 4999 | |
| 5000 | { |
| 5001 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5002 | pCallback1->Exception(pThread->m_pAppDomain, pThread, !pEvent->Exception.firstChance); |
| 5003 | } |
| 5004 | |
| 5005 | } |
| 5006 | break; |
| 5007 | |
| 5008 | case DB_IPCE_SYNC_COMPLETE: |
| 5009 | _ASSERTE(!"Should have never queued a sync complete pEvent." ); |
| 5010 | break; |
| 5011 | |
| 5012 | case DB_IPCE_THREAD_ATTACH: |
| 5013 | { |
| 5014 | STRESS_LOG1(LF_CORDB, LL_INFO100, "RCET::DRCE: thread attach : ID=%x.\n" , dwVolatileThreadId); |
| 5015 | |
| 5016 | TargetConsistencyCheck(pThread != NULL); |
| 5017 | { |
| 5018 | PUBLIC_CALLBACK_IN_THIS_SCOPE1(this, pLockHolder, pEvent, "thread=0x%p" , pThread); |
| 5019 | pCallback1->CreateThread(pAppDomain, pThread); |
| 5020 | } |
| 5021 | } |
| 5022 | break; |
| 5023 | |
| 5024 | case DB_IPCE_THREAD_DETACH: |
| 5025 | { |
| 5026 | STRESS_LOG2(LF_CORDB, LL_INFO100, "[%x] RCET::HRCE: thread detach : ID=%x \n" , |
| 5027 | GetCurrentThreadId(), dwVolatileThreadId); |
| 5028 | |
| 5029 | // If the runtime thread never entered managed code, there |
| 5030 | // won't be a CordbThread, and CreateThread was never |
| 5031 | // called, so don't bother calling ExitThread. |
| 5032 | if (pThread != NULL) |
| 5033 | { |
| 5034 | AddToNeuterOnContinueList(pThread); |
| 5035 | |
| 5036 | RSSmartPtr<CordbThread> pRefThread(pThread); |
| 5037 | |
| 5038 | _ASSERTE(pAppDomain != NULL); |
| 5039 | |
| 5040 | // A thread is reported as dead before we get the exit event. |
| 5041 | // See code:IDacDbiInterface#IsThreadMarkedDead for the invariant being asserted here. |
| 5042 | TargetConsistencyCheck(pThread->IsThreadDead()); |
| 5043 | |
| 5044 | // Enforce the enumeration invariants (see code:IDacDbiInterface#Enumeration)that the thread is not discoverable. |
| 5045 | INDEBUG(pThread->DbgAssertThreadDeleted()); |
| 5046 | |
| 5047 | // Remove the thread from the hash. If we've removed it from the hash, we really should |
| 5048 | // neuter it ... but that causes test failures. |
| 5049 | // We'll neuter it in continue. |
| 5050 | m_userThreads.RemoveBase(VmPtrToCookie(pThread->m_vmThreadToken)); |
| 5051 | |
| 5052 | |
| 5053 | LOG((LF_CORDB, LL_INFO1000, "[%x] RCET::HRCE: sending thread detach.\n" , GetCurrentThreadId())); |
| 5054 | |
| 5055 | { |
| 5056 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5057 | pCallback1->ExitThread(pAppDomain, pThread); |
| 5058 | } |
| 5059 | |
| 5060 | // Implicit release on thread & pAppDomain |
| 5061 | } |
| 5062 | } |
| 5063 | break; |
| 5064 | |
| 5065 | case DB_IPCE_METADATA_UPDATE: |
| 5066 | { |
| 5067 | CordbModule * pModule = pAppDomain->LookupOrCreateModule(pEvent->MetadataUpdateData.vmDomainFile); |
| 5068 | pModule->RefreshMetaData(); |
| 5069 | } |
| 5070 | break; |
| 5071 | |
| 5072 | case DB_IPCE_LOAD_MODULE: |
| 5073 | { |
| 5074 | _ASSERTE (pAppDomain != NULL); |
| 5075 | CordbModule * pModule = pAppDomain->LookupOrCreateModule(pEvent->LoadModuleData.vmDomainFile); |
| 5076 | |
| 5077 | { |
| 5078 | pModule->SetLoadEventContinueMarker(); |
| 5079 | |
| 5080 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5081 | pCallback1->LoadModule(pAppDomain, pModule); |
| 5082 | } |
| 5083 | |
| 5084 | } |
| 5085 | break; |
| 5086 | |
| 5087 | case DB_IPCE_CREATE_CONNECTION: |
| 5088 | { |
| 5089 | STRESS_LOG1(LF_CORDB, LL_INFO100, |
| 5090 | "RCET::HRCE: Connection change %d \n" , |
| 5091 | pEvent->CreateConnection.connectionId); |
| 5092 | |
| 5093 | // pass back the connection id and the connection name. |
| 5094 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5095 | pCallback2->CreateConnection( |
| 5096 | this, |
| 5097 | pEvent->CreateConnection.connectionId, |
| 5098 | const_cast<WCHAR*> (pEvent->CreateConnection.wzConnectionName.GetString())); |
| 5099 | } |
| 5100 | break; |
| 5101 | |
| 5102 | case DB_IPCE_DESTROY_CONNECTION: |
| 5103 | { |
| 5104 | STRESS_LOG1(LF_CORDB, LL_INFO100, |
| 5105 | "RCET::HRCE: Connection destroyed %d \n" , |
| 5106 | pEvent->ConnectionChange.connectionId); |
| 5107 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5108 | pCallback2->DestroyConnection(this, pEvent->ConnectionChange.connectionId); |
| 5109 | } |
| 5110 | break; |
| 5111 | |
| 5112 | case DB_IPCE_CHANGE_CONNECTION: |
| 5113 | { |
| 5114 | STRESS_LOG1(LF_CORDB, LL_INFO100, |
| 5115 | "RCET::HRCE: Connection changed %d \n" , |
| 5116 | pEvent->ConnectionChange.connectionId); |
| 5117 | |
| 5118 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5119 | pCallback2->ChangeConnection(this, pEvent->ConnectionChange.connectionId); |
| 5120 | } |
| 5121 | break; |
| 5122 | |
| 5123 | case DB_IPCE_UNLOAD_MODULE: |
| 5124 | { |
| 5125 | STRESS_LOG3(LF_CORDB, LL_INFO100, "RCET::HRCE: unload module on thread %#x Mod:0x%x AD:0x%08x\n" , |
| 5126 | dwVolatileThreadId, |
| 5127 | VmPtrToCookie(pEvent->UnloadModuleData.vmDomainFile), |
| 5128 | VmPtrToCookie(pEvent->vmAppDomain)); |
| 5129 | |
| 5130 | PREFIX_ASSUME (pAppDomain != NULL); |
| 5131 | |
| 5132 | CordbModule *module = pAppDomain->LookupOrCreateModule(pEvent->UnloadModuleData.vmDomainFile); |
| 5133 | |
| 5134 | if (module == NULL) |
| 5135 | { |
| 5136 | LOG((LF_CORDB, LL_INFO100, "Already unloaded Module - continue()ing!" )); |
| 5137 | break; |
| 5138 | } |
| 5139 | _ASSERTE(module != NULL); |
| 5140 | INDEBUG(module->DbgAssertModuleDeleted()); |
| 5141 | |
| 5142 | // The appdomain we're unloading in must be the appdomain we were loaded in. Otherwise, we've got mismatched |
| 5143 | // module and appdomain pointers. Bugs 65943 & 81728. |
| 5144 | _ASSERTE(pAppDomain == module->GetAppDomain()); |
| 5145 | |
| 5146 | // Ensure the module gets neutered once we call continue. |
| 5147 | AddToNeuterOnContinueList(module); // throws |
| 5148 | { |
| 5149 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5150 | pCallback1->UnloadModule(pAppDomain, module); |
| 5151 | } |
| 5152 | |
| 5153 | pAppDomain->m_modules.RemoveBase(VmPtrToCookie(pEvent->UnloadModuleData.vmDomainFile)); |
| 5154 | } |
| 5155 | break; |
| 5156 | |
| 5157 | case DB_IPCE_LOAD_CLASS: |
| 5158 | { |
| 5159 | CordbClass *pClass = NULL; |
| 5160 | |
| 5161 | LOG((LF_CORDB, LL_INFO10000, |
| 5162 | "RCET::HRCE: load class on thread %#x Tok:0x%08x Mod:0x%08x Asm:0x%08x AD:0x%08x\n" , |
| 5163 | dwVolatileThreadId, |
| 5164 | pEvent->LoadClass.classMetadataToken, |
| 5165 | VmPtrToCookie(pEvent->LoadClass.vmDomainFile), |
| 5166 | LsPtrToCookie(pEvent->LoadClass.classDebuggerAssemblyToken), |
| 5167 | VmPtrToCookie(pEvent->vmAppDomain))); |
| 5168 | |
| 5169 | _ASSERTE (pAppDomain != NULL); |
| 5170 | |
| 5171 | CordbModule* pModule = pAppDomain->LookupOrCreateModule(pEvent->LoadClass.vmDomainFile); |
| 5172 | if (pModule == NULL) |
| 5173 | { |
| 5174 | LOG((LF_CORDB, LL_INFO100, "Load Class on not-loaded Module - continue()ing!" )); |
| 5175 | break; |
| 5176 | } |
| 5177 | _ASSERTE(pModule != NULL); |
| 5178 | |
| 5179 | BOOL fDynamic = pModule->IsDynamic(); |
| 5180 | |
| 5181 | // If this is a class load in a dynamic module, the metadata has become invalid. |
| 5182 | if (fDynamic) |
| 5183 | { |
| 5184 | pModule->RefreshMetaData(); |
| 5185 | } |
| 5186 | |
| 5187 | hr = pModule->LookupOrCreateClass(pEvent->LoadClass.classMetadataToken, &pClass); |
| 5188 | _ASSERTE(SUCCEEDED(hr) == (pClass != NULL)); |
| 5189 | IfFailThrow(hr); |
| 5190 | |
| 5191 | // Prevent class load from being sent twice. |
| 5192 | // @dbgtodo - Microsoft, cordbclass: this is legacy. Can this really happen? Investigate as we dac-ize CordbClass. |
| 5193 | if (pClass->LoadEventSent()) |
| 5194 | { |
| 5195 | // Dynamic modules are dynamic at the module level - |
| 5196 | // you can't add a new version of a class once the module |
| 5197 | // is baked. |
| 5198 | // EnC adds completely new classes. |
| 5199 | // There shouldn't be any other way to send multiple |
| 5200 | // ClassLoad events. |
| 5201 | // Except that there are race conditions between loading |
| 5202 | // an appdomain, and loading a class, so if we get the extra |
| 5203 | // class load, we should ignore it. |
| 5204 | break; //out of the switch statement |
| 5205 | } |
| 5206 | pClass->SetLoadEventSent(TRUE); |
| 5207 | |
| 5208 | |
| 5209 | if (pClass != NULL) |
| 5210 | { |
| 5211 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5212 | pCallback1->LoadClass(pAppDomain, pClass); |
| 5213 | } |
| 5214 | } |
| 5215 | break; |
| 5216 | |
| 5217 | case DB_IPCE_UNLOAD_CLASS: |
| 5218 | { |
| 5219 | LOG((LF_CORDB, LL_INFO10000, |
| 5220 | "RCET::HRCE: unload class on thread %#x Tok:0x%08x Mod:0x%08x AD:0x%08x\n" , |
| 5221 | dwVolatileThreadId, |
| 5222 | pEvent->UnloadClass.classMetadataToken, |
| 5223 | VmPtrToCookie(pEvent->UnloadClass.vmDomainFile), |
| 5224 | VmPtrToCookie(pEvent->vmAppDomain))); |
| 5225 | |
| 5226 | // get the appdomain object |
| 5227 | _ASSERTE (pAppDomain != NULL); |
| 5228 | |
| 5229 | CordbModule *pModule = pAppDomain->LookupOrCreateModule(pEvent->UnloadClass.vmDomainFile); |
| 5230 | if (pModule == NULL) |
| 5231 | { |
| 5232 | LOG((LF_CORDB, LL_INFO100, "Unload Class on not-loaded Module - continue()ing!" )); |
| 5233 | break; |
| 5234 | } |
| 5235 | _ASSERTE(pModule != NULL); |
| 5236 | |
| 5237 | CordbClass *pClass = pModule->LookupClass(pEvent->UnloadClass.classMetadataToken); |
| 5238 | |
| 5239 | if (pClass != NULL && !pClass->HasBeenUnloaded()) |
| 5240 | { |
| 5241 | pClass->SetHasBeenUnloaded(true); |
| 5242 | |
| 5243 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5244 | pCallback1->UnloadClass(pAppDomain, pClass); |
| 5245 | } |
| 5246 | } |
| 5247 | break; |
| 5248 | |
| 5249 | case DB_IPCE_FIRST_LOG_MESSAGE: |
| 5250 | { |
| 5251 | _ASSERTE(pThread != NULL); |
| 5252 | _ASSERTE(pAppDomain != NULL); |
| 5253 | |
| 5254 | const WCHAR * pszContent = pEvent->FirstLogMessage.szContent.GetString(); |
| 5255 | { |
| 5256 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5257 | pCallback1->LogMessage( |
| 5258 | pAppDomain, |
| 5259 | pThread, |
| 5260 | pEvent->FirstLogMessage.iLevel, |
| 5261 | const_cast<WCHAR*> (pEvent->FirstLogMessage.szCategory.GetString()), |
| 5262 | const_cast<WCHAR*> (pszContent)); |
| 5263 | } |
| 5264 | } |
| 5265 | break; |
| 5266 | |
| 5267 | case DB_IPCE_LOGSWITCH_SET_MESSAGE: |
| 5268 | { |
| 5269 | |
| 5270 | LOG((LF_CORDB, LL_INFO10000, |
| 5271 | "[%x] RCET::DRCE: Log Switch Setting Message.\n" , |
| 5272 | GetCurrentThreadId())); |
| 5273 | |
| 5274 | _ASSERTE(pThread != NULL); |
| 5275 | |
| 5276 | const WCHAR *pstrLogSwitchName = pEvent->LogSwitchSettingMessage.szSwitchName.GetString(); |
| 5277 | const WCHAR *pstrParentName = pEvent->LogSwitchSettingMessage.szParentSwitchName.GetString(); |
| 5278 | |
| 5279 | // from the thread object get the appdomain object |
| 5280 | _ASSERTE(pAppDomain == pThread->m_pAppDomain); |
| 5281 | _ASSERTE (pAppDomain != NULL); |
| 5282 | |
| 5283 | { |
| 5284 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5285 | pCallback1->LogSwitch( |
| 5286 | pAppDomain, |
| 5287 | pThread, |
| 5288 | pEvent->LogSwitchSettingMessage.iLevel, |
| 5289 | pEvent->LogSwitchSettingMessage.iReason, |
| 5290 | const_cast<WCHAR*> (pstrLogSwitchName), |
| 5291 | const_cast<WCHAR*> (pstrParentName)); |
| 5292 | |
| 5293 | } |
| 5294 | } |
| 5295 | |
| 5296 | break; |
| 5297 | case DB_IPCE_CUSTOM_NOTIFICATION: |
| 5298 | { |
| 5299 | _ASSERTE(pThread != NULL); |
| 5300 | _ASSERTE(pAppDomain != NULL); |
| 5301 | |
| 5302 | |
| 5303 | // determine first whether custom notifications for this type are enabled -- if not |
| 5304 | // we just return without doing anything. |
| 5305 | CordbClass * pNotificationClass = LookupClass(pAppDomain, |
| 5306 | pEvent->CustomNotification.vmDomainFile, |
| 5307 | pEvent->CustomNotification.classToken); |
| 5308 | |
| 5309 | // if the class is NULL, that means the debugger never enabled notifications for it. Otherwise, |
| 5310 | // the CordbClass instance would already have been created when the notifications were |
| 5311 | // enabled. |
| 5312 | if ((pNotificationClass != NULL) && pNotificationClass->CustomNotificationsEnabled()) |
| 5313 | |
| 5314 | { |
| 5315 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5316 | pCallback3->CustomNotification(pThread, pAppDomain); |
| 5317 | } |
| 5318 | } |
| 5319 | |
| 5320 | break; |
| 5321 | |
| 5322 | case DB_IPCE_CREATE_APP_DOMAIN: |
| 5323 | { |
| 5324 | STRESS_LOG2(LF_CORDB, LL_INFO100, |
| 5325 | "RCET::HRCE: create appdomain on thread %#x AD:0x%08x \n" , |
| 5326 | dwVolatileThreadId, |
| 5327 | VmPtrToCookie(pEvent->vmAppDomain)); |
| 5328 | |
| 5329 | |
| 5330 | // Enumerate may have prepopulated the appdomain, so check if it already exists. |
| 5331 | // Either way, still send the CreateEvent. (We don't want to skip the Create event |
| 5332 | // just because the debugger did an enumerate) |
| 5333 | // We remove AppDomains from the hash as soon as they are exited. |
| 5334 | pAppDomain.Assign(LookupOrCreateAppDomain(pEvent->AppDomainData.vmAppDomain)); |
| 5335 | _ASSERTE(pAppDomain != NULL); // throws on failure |
| 5336 | |
| 5337 | { |
| 5338 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5339 | hr = pCallback1->CreateAppDomain(this, pAppDomain); |
| 5340 | } |
| 5341 | } |
| 5342 | |
| 5343 | |
| 5344 | break; |
| 5345 | |
| 5346 | case DB_IPCE_EXIT_APP_DOMAIN: |
| 5347 | { |
| 5348 | STRESS_LOG2(LF_CORDB, LL_INFO100, "RCET::HRCE: exit appdomain on thread %#x AD:0x%08x \n" , |
| 5349 | dwVolatileThreadId, |
| 5350 | VmPtrToCookie(pEvent->vmAppDomain)); |
| 5351 | |
| 5352 | // In debug-only builds, assert that the appdomain is indeed deleted and not discoverable. |
| 5353 | INDEBUG(DbgAssertAppDomainDeleted(pEvent->vmAppDomain)); |
| 5354 | |
| 5355 | // If we get an ExitAD message for which we have no AppDomain, then ignore it. |
| 5356 | // This can happen if an AD gets torn down very early (before the LS AD is to the |
| 5357 | // point that it can be published). |
| 5358 | // This could also happen if we attach a debugger right before the Exit event is sent. |
| 5359 | // In this case, the debuggee is no longer publishing the appdomain. |
| 5360 | if (pAppDomain == NULL) |
| 5361 | { |
| 5362 | break; |
| 5363 | } |
| 5364 | _ASSERTE (pAppDomain != NULL); |
| 5365 | |
| 5366 | // See if this is the default AppDomain exiting. This should only happen very late in |
| 5367 | // the shutdown cycle, and so we shouldn't do anything significant with m_pDefaultDomain==NULL. |
| 5368 | // We should try and remove m_pDefaultDomain entirely since we can't count on it always existing. |
| 5369 | if (pAppDomain == m_pDefaultAppDomain) |
| 5370 | { |
| 5371 | m_pDefaultAppDomain = NULL; |
| 5372 | } |
| 5373 | |
| 5374 | // Update any threads which were last seen in this AppDomain. We don't |
| 5375 | // get any notification when a thread leaves an AppDomain, so our idea |
| 5376 | // of what AppDomain the thread is in may be out of date. |
| 5377 | UpdateThreadsForAdUnload( pAppDomain ); |
| 5378 | |
| 5379 | // This will still maintain weak references so we could call Continue. |
| 5380 | AddToNeuterOnContinueList(pAppDomain); |
| 5381 | |
| 5382 | { |
| 5383 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5384 | hr = pCallback1->ExitAppDomain(this, pAppDomain); |
| 5385 | } |
| 5386 | |
| 5387 | // @dbgtodo appdomain: This should occur before the callback. |
| 5388 | // Even after ExitAppDomain, the outside world will want to continue calling |
| 5389 | // Continue (and thus they may need to call CordbAppDomain::GetProcess(), which Neutering |
| 5390 | // would clear). Thus we can't neuter yet. |
| 5391 | |
| 5392 | // Remove this app domain. This means any attempt to lookup the AppDomain |
| 5393 | // will fail (which we do at the top of this method). Since any threads (incorrectly) referring |
| 5394 | // to this AppDomain have been moved to the default AppDomain, no one should be |
| 5395 | // interested in looking this AppDomain up anymore. |
| 5396 | m_appDomains.RemoveBase(VmPtrToCookie(pEvent->vmAppDomain)); |
| 5397 | } |
| 5398 | |
| 5399 | break; |
| 5400 | |
| 5401 | case DB_IPCE_LOAD_ASSEMBLY: |
| 5402 | { |
| 5403 | LOG((LF_CORDB, LL_INFO100, |
| 5404 | "RCET::HRCE: load assembly on thread %#x Asm:0x%08x AD:0x%08x \n" , |
| 5405 | dwVolatileThreadId, |
| 5406 | VmPtrToCookie(pEvent->AssemblyData.vmDomainAssembly), |
| 5407 | VmPtrToCookie(pEvent->vmAppDomain))); |
| 5408 | |
| 5409 | _ASSERTE (pAppDomain != NULL); |
| 5410 | |
| 5411 | // Determine if this Assembly is cached. |
| 5412 | CordbAssembly * pAssembly = pAppDomain->LookupOrCreateAssembly(pEvent->AssemblyData.vmDomainAssembly); |
| 5413 | _ASSERTE(pAssembly != NULL); // throws on error |
| 5414 | |
| 5415 | // If created, or have, an Assembly, notify callback. |
| 5416 | { |
| 5417 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5418 | hr = pCallback1->LoadAssembly(pAppDomain, pAssembly); |
| 5419 | } |
| 5420 | } |
| 5421 | |
| 5422 | break; |
| 5423 | |
| 5424 | case DB_IPCE_UNLOAD_ASSEMBLY: |
| 5425 | { |
| 5426 | LOG((LF_CORDB, LL_INFO100, "RCET::DRCE: unload assembly on thread %#x Asm:0x%x AD:0x%x\n" , |
| 5427 | dwVolatileThreadId, |
| 5428 | VmPtrToCookie(pEvent->AssemblyData.vmDomainAssembly), |
| 5429 | VmPtrToCookie(pEvent->vmAppDomain))); |
| 5430 | |
| 5431 | _ASSERTE (pAppDomain != NULL); |
| 5432 | |
| 5433 | CordbAssembly * pAssembly = pAppDomain->LookupOrCreateAssembly(pEvent->AssemblyData.vmDomainAssembly); |
| 5434 | |
| 5435 | if (pAssembly == NULL) |
| 5436 | { |
| 5437 | // No assembly. This could happen if we attach right before an unload event is sent. |
| 5438 | return; |
| 5439 | } |
| 5440 | _ASSERTE(pAssembly != NULL); |
| 5441 | INDEBUG(pAssembly->DbgAssertAssemblyDeleted()); |
| 5442 | |
| 5443 | // Ensure the assembly gets neutered when we call continue. |
| 5444 | AddToNeuterOnContinueList(pAssembly); // throws |
| 5445 | |
| 5446 | { |
| 5447 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5448 | hr = pCallback1->UnloadAssembly(pAppDomain, pAssembly); |
| 5449 | } |
| 5450 | |
| 5451 | pAppDomain->RemoveAssemblyFromCache(pEvent->AssemblyData.vmDomainAssembly); |
| 5452 | } |
| 5453 | |
| 5454 | break; |
| 5455 | |
| 5456 | case DB_IPCE_FUNC_EVAL_COMPLETE: |
| 5457 | { |
| 5458 | LOG((LF_CORDB, LL_INFO1000, "RCET::DRCE: func eval complete.\n" )); |
| 5459 | |
| 5460 | CordbEval *pEval = NULL; |
| 5461 | { |
| 5462 | pEval = pEvent->FuncEvalComplete.funcEvalKey.UnWrapAndRemove(this); |
| 5463 | if (pEval == NULL) |
| 5464 | { |
| 5465 | _ASSERTE(!"Bogus FuncEval handle in IPC block." ); |
| 5466 | // Bogus handle in IPC block. |
| 5467 | break; |
| 5468 | } |
| 5469 | } |
| 5470 | _ASSERTE(pEval != NULL); |
| 5471 | |
| 5472 | _ASSERTE(pThread != NULL); |
| 5473 | _ASSERTE(pAppDomain != NULL); |
| 5474 | |
| 5475 | CONSISTENCY_CHECK_MSGF(pEval->m_DbgAppDomainStarted == pAppDomain, |
| 5476 | ("AppDomain changed from Func-Eval. Eval=%p, Started=%p, Now=%p\n" , |
| 5477 | pEval, pEval->m_DbgAppDomainStarted, (void*) pAppDomain)); |
| 5478 | |
| 5479 | // Hold the data about the result in the CordbEval for later. |
| 5480 | pEval->m_complete = true; |
| 5481 | pEval->m_successful = !!pEvent->FuncEvalComplete.successful; |
| 5482 | pEval->m_aborted = !!pEvent->FuncEvalComplete.aborted; |
| 5483 | pEval->m_resultAddr = pEvent->FuncEvalComplete.resultAddr; |
| 5484 | pEval->m_vmObjectHandle = pEvent->FuncEvalComplete.vmObjectHandle; |
| 5485 | pEval->m_resultType = pEvent->FuncEvalComplete.resultType; |
| 5486 | pEval->m_resultAppDomainToken = pEvent->FuncEvalComplete.vmAppDomain; |
| 5487 | |
| 5488 | CordbAppDomain *pResultAppDomain = LookupOrCreateAppDomain(pEvent->FuncEvalComplete.vmAppDomain); |
| 5489 | |
| 5490 | _ASSERTE(OutstandingEvalCount() > 0); |
| 5491 | DecrementOutstandingEvalCount(); |
| 5492 | |
| 5493 | CONSISTENCY_CHECK_MSGF(pEval->m_DbgAppDomainStarted == pAppDomain, |
| 5494 | ("AppDomain changed from Func-Eval. Eval=%p, Started=%p, Now=%p\n" , |
| 5495 | pEval, pEval->m_DbgAppDomainStarted, (void*) pAppDomain)); |
| 5496 | |
| 5497 | // If we did this func eval with this thread stopped at an excpetion, then we need to pretend as if we |
| 5498 | // really didn't continue from the exception, since, of course, we really didn't on the Left Side. |
| 5499 | if (pEval->IsEvalDuringException()) |
| 5500 | { |
| 5501 | pThread->SetExInfo(pEval->m_vmThreadOldExceptionHandle); |
| 5502 | } |
| 5503 | |
| 5504 | bool fEvalCompleted = pEval->m_successful || pEval->m_aborted; |
| 5505 | |
| 5506 | // If a CallFunction() is aborted, the LHS may not complete the abort |
| 5507 | // immediately and hence we cant do a SendCleanup() at that point. Also, |
| 5508 | // the debugger may (incorrectly) release the CordbEval before this |
| 5509 | // DB_IPCE_FUNC_EVAL_COMPLETE event is received. Hence, we maintain an |
| 5510 | // extra ref-count to determine when this can be done. |
| 5511 | // Note that this can cause a two-way DB_IPCE_FUNC_EVAL_CLEANUP event |
| 5512 | // to be sent. Hence, it has to be done before the Continue (see issue 102745). |
| 5513 | |
| 5514 | |
| 5515 | // Note that if the debugger has already (incorrectly) released the CordbEval, |
| 5516 | // pEval will be pointing to garbage and should not be used by the debugger. |
| 5517 | if (fEvalCompleted) |
| 5518 | { |
| 5519 | PUBLIC_CALLBACK_IN_THIS_SCOPE2(this, pLockHolder, pEvent, "thread=0x%p, eval=0x%p. (Complete)" , pThread, pEval); |
| 5520 | pCallback1->EvalComplete(pResultAppDomain, pThread, pEval); |
| 5521 | } |
| 5522 | else |
| 5523 | { |
| 5524 | PUBLIC_CALLBACK_IN_THIS_SCOPE2(this, pLockHolder, pEvent, "pThread=0x%p, eval=0x%p. (Exception)" , pThread, pEval); |
| 5525 | pCallback1->EvalException(pResultAppDomain, pThread, pEval); |
| 5526 | } |
| 5527 | |
| 5528 | // This release may send an DB_IPCE_FUNC_EVAL_CLEANUP IPC event. That's ok b/c |
| 5529 | // we're still synced even if if Continue was called inside the callback. |
| 5530 | // That's because the StopContinueHolder bumped up the stopcount. |
| 5531 | // Corresponding AddRef() in CallFunction(). |
| 5532 | // @todo - this is leaked if we don't get an EvalComplete event (eg, process exits with |
| 5533 | // in middle of func-eval). |
| 5534 | pEval->Release(); |
| 5535 | } |
| 5536 | break; |
| 5537 | |
| 5538 | |
| 5539 | case DB_IPCE_NAME_CHANGE: |
| 5540 | { |
| 5541 | LOG((LF_CORDB, LL_INFO1000, "RCET::HRCE: Name Change %d 0x%p\n" , |
| 5542 | dwVolatileThreadId, |
| 5543 | VmPtrToCookie(pEvent->NameChange.vmAppDomain))); |
| 5544 | |
| 5545 | pThread = NULL; |
| 5546 | pAppDomain.Clear(); |
| 5547 | if (pEvent->NameChange.eventType == THREAD_NAME_CHANGE) |
| 5548 | { |
| 5549 | // Lookup the CordbThread that matches this runtime thread. |
| 5550 | if (!pEvent->NameChange.vmThread.IsNull()) |
| 5551 | { |
| 5552 | pThread = LookupOrCreateThread(pEvent->NameChange.vmThread); |
| 5553 | } |
| 5554 | } |
| 5555 | else |
| 5556 | { |
| 5557 | _ASSERTE (pEvent->NameChange.eventType == APP_DOMAIN_NAME_CHANGE); |
| 5558 | pAppDomain.Assign(LookupOrCreateAppDomain(pEvent->NameChange.vmAppDomain)); |
| 5559 | if (pAppDomain) |
| 5560 | { |
| 5561 | pAppDomain->InvalidateName(); |
| 5562 | } |
| 5563 | } |
| 5564 | |
| 5565 | if (pThread || pAppDomain) |
| 5566 | { |
| 5567 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5568 | pCallback1->NameChange(pAppDomain, pThread); |
| 5569 | } |
| 5570 | } |
| 5571 | |
| 5572 | break; |
| 5573 | |
| 5574 | case DB_IPCE_UPDATE_MODULE_SYMS: |
| 5575 | { |
| 5576 | RSExtSmartPtr<IStream> pStream; |
| 5577 | |
| 5578 | // Find the app domain the module lives in. |
| 5579 | _ASSERTE (pAppDomain != NULL); |
| 5580 | |
| 5581 | // Find the Right Side module for this module. |
| 5582 | CordbModule * pModule = pAppDomain->LookupOrCreateModule(pEvent->UpdateModuleSymsData.vmDomainFile); |
| 5583 | _ASSERTE(pModule != NULL); |
| 5584 | |
| 5585 | // This is a legacy event notification for updated PDBs. |
| 5586 | // Creates a new IStream object. Ownership is handed off via callback. |
| 5587 | IDacDbiInterface::SymbolFormat symFormat = pModule->GetInMemorySymbolStream(&pStream); |
| 5588 | |
| 5589 | // We shouldn't get this event if there aren't PDB symbols waiting. Specifically we don't want |
| 5590 | // to incur the cost of copying over ILDB symbols here without the debugger asking for them. |
| 5591 | // Eventually we may remove this callback as well and always rely on explicit requests. |
| 5592 | _ASSERTE(symFormat == IDacDbiInterface::kSymbolFormatPDB); |
| 5593 | |
| 5594 | if (symFormat == IDacDbiInterface::kSymbolFormatPDB) |
| 5595 | { |
| 5596 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5597 | |
| 5598 | _ASSERTE(pStream != NULL); // Shouldn't send the event if we don't have a stream. |
| 5599 | |
| 5600 | pCallback1->UpdateModuleSymbols(pAppDomain, pModule, pStream); |
| 5601 | } |
| 5602 | |
| 5603 | } |
| 5604 | break; |
| 5605 | |
| 5606 | case DB_IPCE_MDA_NOTIFICATION: |
| 5607 | { |
| 5608 | RSInitHolder<CordbMDA> pMDA(new CordbMDA(this, &pEvent->MDANotification)); // throws |
| 5609 | |
| 5610 | // Ctor leaves both internal + ext Ref at 0, adding to neuter list bumps int-ref up to 1. |
| 5611 | // Neutering will dump it back down to zero. |
| 5612 | this->AddToNeuterOnExitList(pMDA); |
| 5613 | |
| 5614 | // We bump up and down the external ref so that even if the callback doensn't touch the refs, |
| 5615 | // our Ext-Release here will still cause a 1->0 ext-ref transition, which will get it |
| 5616 | // swept on the neuter list. |
| 5617 | RSExtSmartPtr<ICorDebugMDA> pExternalMDARef; |
| 5618 | pMDA.TransferOwnershipExternal(&pExternalMDARef); |
| 5619 | { |
| 5620 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5621 | |
| 5622 | pCallback2->MDANotification( |
| 5623 | this, |
| 5624 | pThread, // may be null |
| 5625 | pExternalMDARef); |
| 5626 | |
| 5627 | // pExternalMDARef's dtor will do an external release, |
| 5628 | // which is very significant because it may be the one that does the 1->0 ext ref transition, |
| 5629 | // which may mean cause the "NeuterAtWill" bit to get flipped on this CordbMDA object. |
| 5630 | // Since this is an external release, do it in the PUBLIC_CALLBACK scope. |
| 5631 | pExternalMDARef.Clear(); |
| 5632 | } |
| 5633 | |
| 5634 | break; |
| 5635 | } |
| 5636 | |
| 5637 | case DB_IPCE_CONTROL_C_EVENT: |
| 5638 | { |
| 5639 | hr = S_FALSE; |
| 5640 | |
| 5641 | { |
| 5642 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5643 | hr = pCallback1->ControlCTrap((ICorDebugProcess*) this); |
| 5644 | } |
| 5645 | |
| 5646 | { |
| 5647 | RSLockHolder ch(this->GetStopGoLock()); |
| 5648 | |
| 5649 | DebuggerIPCEvent eventControlCResult; |
| 5650 | |
| 5651 | InitIPCEvent(&eventControlCResult, |
| 5652 | DB_IPCE_CONTROL_C_EVENT_RESULT, |
| 5653 | false, |
| 5654 | VMPTR_AppDomain::NullPtr()); |
| 5655 | |
| 5656 | // Indicate whether the debugger has handled the event. |
| 5657 | eventControlCResult.hr = hr; |
| 5658 | |
| 5659 | // Send the reply to the LS. |
| 5660 | SendIPCEvent(&eventControlCResult, sizeof(eventControlCResult)); |
| 5661 | } // release SG lock |
| 5662 | |
| 5663 | } |
| 5664 | break; |
| 5665 | |
| 5666 | // EnC Remap opportunity |
| 5667 | case DB_IPCE_ENC_REMAP: |
| 5668 | { |
| 5669 | LOG((LF_CORDB, LL_INFO1000, "[%x] RCET::DRCE: EnC Remap!.\n" , |
| 5670 | GetCurrentThreadId())); |
| 5671 | |
| 5672 | _ASSERTE(NULL != pAppDomain); |
| 5673 | |
| 5674 | CordbModule * pModule = pAppDomain->LookupOrCreateModule(pEvent->EnCRemap.vmDomainFile); |
| 5675 | PREFIX_ASSUME(pModule != NULL); |
| 5676 | |
| 5677 | CordbFunction * pCurFunction = NULL; |
| 5678 | CordbFunction * pResumeFunction = NULL; |
| 5679 | |
| 5680 | // lookup the version of the function that we are mapping from |
| 5681 | // this is the one that is currently running |
| 5682 | pCurFunction = pModule->LookupOrCreateFunction( |
| 5683 | pEvent->EnCRemap.funcMetadataToken, pEvent->EnCRemap.currentVersionNumber); |
| 5684 | |
| 5685 | // lookup the version of the function that we are mapping to |
| 5686 | // it will always be the most recent |
| 5687 | pResumeFunction = pModule->LookupOrCreateFunction( |
| 5688 | pEvent->EnCRemap.funcMetadataToken, pEvent->EnCRemap.resumeVersionNumber); |
| 5689 | |
| 5690 | _ASSERTE(pCurFunction->GetEnCVersionNumber() < pResumeFunction->GetEnCVersionNumber()); |
| 5691 | |
| 5692 | RSSmartPtr<CordbFunction> pRefCurFunction(pCurFunction); |
| 5693 | RSSmartPtr<CordbFunction> pRefResumeFunction(pResumeFunction); |
| 5694 | |
| 5695 | // Verify we're not about to overwrite an outstanding remap IP |
| 5696 | // This should only be set while a remap opportunity is being handled, |
| 5697 | // and cleared (by CordbThread::MarkStackFramesDirty) on Continue. |
| 5698 | // We want to be absolutely sure we don't accidentally keep a stale pointer |
| 5699 | // around because it would point to arbitrary stack space in the CLR potentially |
| 5700 | // leading to stack corruption. |
| 5701 | _ASSERTE( pThread->m_EnCRemapFunctionIP == NULL ); |
| 5702 | |
| 5703 | // Stash the address of the remap IP buffer. This indicates that calling |
| 5704 | // RemapFunction is valid and provides a communications channel between the RS |
| 5705 | // and LS for the remap IL offset. |
| 5706 | pThread->m_EnCRemapFunctionIP = pEvent->EnCRemap.resumeILOffset; |
| 5707 | |
| 5708 | { |
| 5709 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5710 | pCallback2->FunctionRemapOpportunity( |
| 5711 | pAppDomain, |
| 5712 | pThread, |
| 5713 | pCurFunction, |
| 5714 | pResumeFunction, |
| 5715 | (ULONG32)pEvent->EnCRemap.currentILOffset); |
| 5716 | } |
| 5717 | |
| 5718 | // Implicit release on pCurFunction and pResumeFunction. |
| 5719 | } |
| 5720 | break; |
| 5721 | |
| 5722 | // EnC Remap complete |
| 5723 | case DB_IPCE_ENC_REMAP_COMPLETE: |
| 5724 | { |
| 5725 | LOG((LF_CORDB, LL_INFO1000, "[%x] RCET::DRCE: EnC Remap Complete!.\n" , |
| 5726 | GetCurrentThreadId())); |
| 5727 | |
| 5728 | _ASSERTE(NULL != pAppDomain); |
| 5729 | |
| 5730 | CordbModule* pModule = pAppDomain->LookupOrCreateModule(pEvent->EnCRemap.vmDomainFile); |
| 5731 | PREFIX_ASSUME(pModule != NULL); |
| 5732 | |
| 5733 | // Find the function we're remapping to, which must be the latest version |
| 5734 | CordbFunction *pRemapFunction= |
| 5735 | pModule->LookupFunctionLatestVersion(pEvent->EnCRemapComplete.funcMetadataToken); |
| 5736 | PREFIX_ASSUME(pRemapFunction != NULL); |
| 5737 | |
| 5738 | // Dispatch the FunctionRemapComplete callback |
| 5739 | RSSmartPtr<CordbFunction> pRef(pRemapFunction); |
| 5740 | { |
| 5741 | PUBLIC_CALLBACK_IN_THIS_SCOPE(this, pLockHolder, pEvent); |
| 5742 | pCallback2->FunctionRemapComplete(pAppDomain, pThread, pRemapFunction); |
| 5743 | } |
| 5744 | // Implicit release on pRemapFunction via holder |
| 5745 | } |
| 5746 | break; |
| 5747 | |
| 5748 | case DB_IPCE_BREAKPOINT_SET_ERROR: |
| 5749 | { |
| 5750 | LOG((LF_CORDB, LL_INFO1000, "RCET::DRCE: breakpoint set error.\n" )); |
| 5751 | |
| 5752 | RSSmartPtr<CordbBreakpoint> pRef; |
| 5753 | |
| 5754 | _ASSERTE(pThread != NULL); |
| 5755 | _ASSERTE(pAppDomain != NULL); |
| 5756 | |
| 5757 | // Find the breakpoint object on this side. |
| 5758 | CordbBreakpoint * pBreakpoint = NULL; |
| 5759 | |
| 5760 | |
| 5761 | if (pThread == NULL) |
| 5762 | { |
| 5763 | // We've found cases out in the wild where we get this event on a thread we don't recognize. |
| 5764 | // We're not sure how this happens. Add a runtime check to protect ourselves to avoid the |
| 5765 | // an AV. We still assert because this should not be happening. |
| 5766 | // It likely means theres some issue where we failed to send a CreateThread notification. |
| 5767 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "BreakpointSetError on unrecognized thread. %p\n" , pBreakpoint); |
| 5768 | |
| 5769 | _ASSERTE(!"Missing thread on bp set error" ); |
| 5770 | break; |
| 5771 | } |
| 5772 | |
| 5773 | pBreakpoint = pAppDomain->m_breakpoints.GetBase(LsPtrToCookie(pEvent->BreakpointSetErrorData.breakpointToken)); |
| 5774 | |
| 5775 | if (pBreakpoint != NULL) |
| 5776 | { |
| 5777 | ICorDebugBreakpoint * pIBreakpoint = CordbBreakpointToInterface(pBreakpoint); |
| 5778 | _ASSERTE(pIBreakpoint != NULL); |
| 5779 | { |
| 5780 | PUBLIC_CALLBACK_IN_THIS_SCOPE2(this, pLockHolder, pEvent, "thread=0x%p, bp=0x%p" , pThread, pBreakpoint); |
| 5781 | pCallback1->BreakpointSetError(pAppDomain, pThread, pIBreakpoint, 0); |
| 5782 | } |
| 5783 | } |
| 5784 | // Implicit release on pRef. |
| 5785 | } |
| 5786 | break; |
| 5787 | |
| 5788 | |
| 5789 | case DB_IPCE_EXCEPTION_CALLBACK2: |
| 5790 | { |
| 5791 | STRESS_LOG4(LF_CORDB, LL_INFO100, |
| 5792 | "RCET::DRCE: Exception2 0x%p 0x%X 0x%X 0x%X\n" , |
| 5793 | pEvent->ExceptionCallback2.framePointer.GetSPValue(), |
| 5794 | pEvent->ExceptionCallback2.nOffset, |
| 5795 | pEvent->ExceptionCallback2.eventType, |
| 5796 | pEvent->ExceptionCallback2.dwFlags |
| 5797 | ); |
| 5798 | |
| 5799 | if (pThread == NULL) |
| 5800 | { |
| 5801 | // We've got an exception on a thread we don't know about. This could be a thread that |
| 5802 | // has never run any managed code, so let's just ignore the exception. We should have |
| 5803 | // already sent a log message about this situation for the EXCEPTION callback above. |
| 5804 | _ASSERTE( pEvent->ExceptionCallback2.eventType == DEBUG_EXCEPTION_UNHANDLED ); |
| 5805 | break; |
| 5806 | } |
| 5807 | |
| 5808 | pThread->SetExInfo(pEvent->ExceptionCallback2.vmExceptionHandle); |
| 5809 | |
| 5810 | // |
| 5811 | // Send all the information back to the debugger. |
| 5812 | // |
| 5813 | RSSmartPtr<CordbFrame> pFrame; |
| 5814 | |
| 5815 | FramePointer fp = pEvent->ExceptionCallback2.framePointer; |
| 5816 | if (fp != LEAF_MOST_FRAME) |
| 5817 | { |
| 5818 | // The interface forces us to to pass a FramePointer via an ICorDebugFrame. |
| 5819 | // However, we can't get a real ICDFrame without a stackwalk, and we don't |
| 5820 | // want to do a stackwalk now. so pass a netuered proxy frame. The shim |
| 5821 | // can map this to a real frame. |
| 5822 | // See comments at CordbPlaceHolderFrame class for details. |
| 5823 | pFrame.Assign(new CordbPlaceholderFrame(this, fp)); |
| 5824 | } |
| 5825 | |
| 5826 | CorDebugExceptionCallbackType type = pEvent->ExceptionCallback2.eventType; |
| 5827 | { |
| 5828 | PUBLIC_CALLBACK_IN_THIS_SCOPE3(this, pLockHolder, pEvent, "pThread=0x%p, frame=%p, type=%d" , pThread, (ICorDebugFrame*) pFrame, type); |
| 5829 | hr = pCallback2->Exception( |
| 5830 | pThread->m_pAppDomain, |
| 5831 | pThread, |
| 5832 | pFrame, |
| 5833 | (ULONG32)(pEvent->ExceptionCallback2.nOffset), |
| 5834 | type, |
| 5835 | pEvent->ExceptionCallback2.dwFlags); |
| 5836 | } |
| 5837 | } |
| 5838 | break; |
| 5839 | |
| 5840 | case DB_IPCE_EXCEPTION_UNWIND: |
| 5841 | { |
| 5842 | STRESS_LOG2(LF_CORDB, LL_INFO100, |
| 5843 | "RCET::DRCE: Exception Unwind 0x%X 0x%X\n" , |
| 5844 | pEvent->ExceptionCallback2.eventType, |
| 5845 | pEvent->ExceptionCallback2.dwFlags |
| 5846 | ); |
| 5847 | |
| 5848 | if (pThread == NULL) |
| 5849 | { |
| 5850 | // We've got an exception on a thread we don't know about. This probably should never |
| 5851 | // happen (if it's unwinding, then we expect a managed frame on the stack, and so we should |
| 5852 | // know about the thread), but if it does fall back to ignoring the exception. |
| 5853 | _ASSERTE( !"Got unwind event for unknown exception" ); |
| 5854 | break; |
| 5855 | } |
| 5856 | |
| 5857 | // |
| 5858 | // Send all the information back to the debugger. |
| 5859 | // |
| 5860 | { |
| 5861 | PUBLIC_CALLBACK_IN_THIS_SCOPE1(this, pLockHolder, pEvent, "pThread=0x%p" , pThread); |
| 5862 | hr = pCallback2->ExceptionUnwind( |
| 5863 | pThread->m_pAppDomain, |
| 5864 | pThread, |
| 5865 | pEvent->ExceptionUnwind.eventType, |
| 5866 | pEvent->ExceptionUnwind.dwFlags); |
| 5867 | } |
| 5868 | } |
| 5869 | break; |
| 5870 | |
| 5871 | |
| 5872 | case DB_IPCE_INTERCEPT_EXCEPTION_COMPLETE: |
| 5873 | { |
| 5874 | STRESS_LOG0(LF_CORDB, LL_INFO100, "RCET::DRCE: Exception Interception Complete.\n" ); |
| 5875 | |
| 5876 | if (pThread == NULL) |
| 5877 | { |
| 5878 | // We've got an exception on a thread we don't know about. This probably should never |
| 5879 | // happen (if it's unwinding, then we expect a managed frame on the stack, and so we should |
| 5880 | // know about the thread), but if it does fall back to ignoring the exception. |
| 5881 | _ASSERTE( !"Got complete event for unknown exception" ); |
| 5882 | break; |
| 5883 | } |
| 5884 | |
| 5885 | // |
| 5886 | // Tell the debugger that the exception has been intercepted. This is similar to the |
| 5887 | // notification we give when we start unwinding for a non-intercepted exception, except that the |
| 5888 | // interception has been completed at this point, which means that we are conceptually at the end |
| 5889 | // of the second pass. |
| 5890 | // |
| 5891 | { |
| 5892 | PUBLIC_CALLBACK_IN_THIS_SCOPE1(this, pLockHolder, pEvent, "pThread=0x%p" , pThread); |
| 5893 | hr = pCallback2->ExceptionUnwind( |
| 5894 | pThread->m_pAppDomain, |
| 5895 | pThread, |
| 5896 | DEBUG_EXCEPTION_INTERCEPTED, |
| 5897 | 0); |
| 5898 | } |
| 5899 | } |
| 5900 | break; |
| 5901 | #ifdef TEST_DATA_CONSISTENCY |
| 5902 | case DB_IPCE_TEST_CRST: |
| 5903 | { |
| 5904 | EX_TRY |
| 5905 | { |
| 5906 | // the left side has signaled that we should test whether pEvent->TestCrstData.vmCrst is held |
| 5907 | GetDAC()->TestCrst(pEvent->TestCrstData.vmCrst); |
| 5908 | } |
| 5909 | EX_CATCH_HRESULT(hr); |
| 5910 | |
| 5911 | if (pEvent->TestCrstData.fOkToTake) |
| 5912 | { |
| 5913 | _ASSERTE(hr == S_OK); |
| 5914 | if (hr != S_OK) |
| 5915 | { |
| 5916 | // we want to catch this in retail builds too |
| 5917 | ThrowHR(E_FAIL); |
| 5918 | } |
| 5919 | } |
| 5920 | else // the lock was already held |
| 5921 | { |
| 5922 | // see if we threw because the lock was held |
| 5923 | _ASSERTE(hr == CORDBG_E_PROCESS_NOT_SYNCHRONIZED); |
| 5924 | if (hr != CORDBG_E_PROCESS_NOT_SYNCHRONIZED) |
| 5925 | { |
| 5926 | // we want to catch this in retail builds too |
| 5927 | ThrowHR(E_FAIL); |
| 5928 | } |
| 5929 | } |
| 5930 | |
| 5931 | } |
| 5932 | break; |
| 5933 | |
| 5934 | case DB_IPCE_TEST_RWLOCK: |
| 5935 | { |
| 5936 | EX_TRY |
| 5937 | { |
| 5938 | // the left side has signaled that we should test whether pEvent->TestRWLockData.vmRWLock is held |
| 5939 | GetDAC()->TestRWLock(pEvent->TestRWLockData.vmRWLock); |
| 5940 | } |
| 5941 | EX_CATCH_HRESULT(hr); |
| 5942 | |
| 5943 | if (pEvent->TestRWLockData.fOkToTake) |
| 5944 | { |
| 5945 | _ASSERTE(hr == S_OK); |
| 5946 | if (hr != S_OK) |
| 5947 | { |
| 5948 | // we want to catch this in retail builds too |
| 5949 | ThrowHR(E_FAIL); |
| 5950 | } |
| 5951 | } |
| 5952 | else // the lock was already held |
| 5953 | { |
| 5954 | // see if we threw because the lock was held |
| 5955 | _ASSERTE(hr == CORDBG_E_PROCESS_NOT_SYNCHRONIZED); |
| 5956 | if (hr != CORDBG_E_PROCESS_NOT_SYNCHRONIZED) |
| 5957 | { |
| 5958 | // we want to catch this in retail builds too |
| 5959 | ThrowHR(E_FAIL); |
| 5960 | } |
| 5961 | } |
| 5962 | } |
| 5963 | break; |
| 5964 | #endif |
| 5965 | |
| 5966 | default: |
| 5967 | _ASSERTE(!"Unknown event" ); |
| 5968 | LOG((LF_CORDB, LL_INFO1000, |
| 5969 | "[%x] RCET::HRCE: Unknown event: 0x%08x\n" , |
| 5970 | GetCurrentThreadId(), pEvent->type)); |
| 5971 | } |
| 5972 | |
| 5973 | |
| 5974 | FinishEventDispatch(); |
| 5975 | } |
| 5976 | #ifdef _PREFAST_ |
| 5977 | #pragma warning(pop) |
| 5978 | #endif |
| 5979 | |
| 5980 | //--------------------------------------------------------------------------------------- |
| 5981 | // Callback for prepopulating threads. |
| 5982 | // |
| 5983 | // Arugments: |
| 5984 | // vmThread - thread as part of the eunmeration. |
| 5985 | // pUserData - data supplied with callback. It's a CordbProcess* object. |
| 5986 | // |
| 5987 | |
| 5988 | // static |
| 5989 | void CordbProcess::ThreadEnumerationCallback(VMPTR_Thread vmThread, void * pUserData) |
| 5990 | { |
| 5991 | CordbProcess * pThis = reinterpret_cast<CordbProcess *> (pUserData); |
| 5992 | INTERNAL_DAC_CALLBACK(pThis); |
| 5993 | |
| 5994 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "ThreadEnumerationCallback()\n" ); |
| 5995 | |
| 5996 | // Do lookup / lazy-create. |
| 5997 | pThis->LookupOrCreateThread(vmThread); |
| 5998 | } |
| 5999 | |
| 6000 | //--------------------------------------------------------------------------------------- |
| 6001 | // Fully build up the CordbThread cache to match VM state. |
| 6002 | void CordbProcess::PrepopulateThreadsOrThrow() |
| 6003 | { |
| 6004 | RSLockHolder lockHolder(GetProcessLock()); |
| 6005 | if (IsDacInitialized()) |
| 6006 | { |
| 6007 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "PrepopulateThreadsOrThrow()\n" ); |
| 6008 | GetDAC()->EnumerateThreads(ThreadEnumerationCallback, this); |
| 6009 | } |
| 6010 | } |
| 6011 | |
| 6012 | //--------------------------------------------------------------------------------------- |
| 6013 | // Create a Thread enumerator |
| 6014 | // |
| 6015 | // Arguments: |
| 6016 | // pOwnerObj - object (a CordbProcess or CordbThread) that will own the enumerator. |
| 6017 | // pOwnerList - the neuter list that the enumerator will live on |
| 6018 | // pHolder - an outparameter for the enumerator to be initialized. |
| 6019 | // |
| 6020 | void CordbProcess::BuildThreadEnum(CordbBase * pOwnerObj, NeuterList * pOwnerList, RSInitHolder<CordbHashTableEnum> * pHolder) |
| 6021 | { |
| 6022 | CordbHashTableEnum::BuildOrThrow( |
| 6023 | pOwnerObj, |
| 6024 | pOwnerList, |
| 6025 | &m_userThreads, |
| 6026 | IID_ICorDebugThreadEnum, |
| 6027 | pHolder); |
| 6028 | } |
| 6029 | |
| 6030 | // Public implementation of ICorDebugProcess::EnumerateThreads |
| 6031 | HRESULT CordbProcess::EnumerateThreads(ICorDebugThreadEnum **ppThreads) |
| 6032 | { |
| 6033 | HRESULT hr = S_OK; |
| 6034 | PUBLIC_API_BEGIN(this); |
| 6035 | { |
| 6036 | if (m_detached) |
| 6037 | { |
| 6038 | // #Detach_Check: |
| 6039 | // |
| 6040 | // FUTURE: Consider adding this IF block to the PUBLIC_API macros so that |
| 6041 | // typical public APIs fail quickly if we're trying to do a detach. For |
| 6042 | // now, I'm hand-adding this check only to the few problematic APIs that get |
| 6043 | // called while queuing the fake attach events. In these cases, it is not |
| 6044 | // enough to check if CordbProcess::IsNeutered(), as the detaching thread |
| 6045 | // may have begun the detaching and neutering process, but not be |
| 6046 | // finished--in which case m_detached is true, but |
| 6047 | // CordbProcess::IsNeutered() is still false. |
| 6048 | ThrowHR(CORDBG_E_PROCESS_DETACHED); |
| 6049 | } |
| 6050 | |
| 6051 | ValidateOrThrow(ppThreads); |
| 6052 | |
| 6053 | RSInitHolder<CordbHashTableEnum> pEnum; |
| 6054 | InternalEnumerateThreads(pEnum.GetAddr()); |
| 6055 | |
| 6056 | pEnum.TransferOwnershipExternal(ppThreads); |
| 6057 | } |
| 6058 | PUBLIC_API_END(hr); |
| 6059 | return hr; |
| 6060 | } |
| 6061 | |
| 6062 | // Internal implementation of EnumerateThreads |
| 6063 | VOID CordbProcess::InternalEnumerateThreads(RSInitHolder<CordbHashTableEnum> *ppThreads) |
| 6064 | { |
| 6065 | INTERNAL_API_ENTRY(this); |
| 6066 | // Needs to prepopulate |
| 6067 | PrepopulateThreadsOrThrow(); |
| 6068 | BuildThreadEnum(this, this->GetContinueNeuterList(), ppThreads); |
| 6069 | } |
| 6070 | |
| 6071 | // Implementation of ICorDebugProcess::GetThread |
| 6072 | HRESULT CordbProcess::GetThread(DWORD dwThreadId, ICorDebugThread **ppThread) |
| 6073 | { |
| 6074 | PUBLIC_API_ENTRY(this); |
| 6075 | VALIDATE_POINTER_TO_OBJECT(ppThread, ICorDebugThread **); |
| 6076 | |
| 6077 | // No good pre-existing ATT_* contract for this. |
| 6078 | // Because for legacy, we have to allow this on the win32 event thread. |
| 6079 | *ppThread = NULL; |
| 6080 | |
| 6081 | HRESULT hr = S_OK; |
| 6082 | EX_TRY |
| 6083 | { |
| 6084 | RSLockHolder lockHolder(GetProcessLock()); |
| 6085 | if (m_detached) |
| 6086 | { |
| 6087 | // See code:CordbProcess::EnumerateThreads#Detach_Check |
| 6088 | ThrowHR(CORDBG_E_PROCESS_DETACHED); |
| 6089 | } |
| 6090 | CordbThread * pThread = TryLookupOrCreateThreadByVolatileOSId(dwThreadId); |
| 6091 | if (pThread == NULL) |
| 6092 | { |
| 6093 | // This is a common case because we may be looking up an unmanaged thread. |
| 6094 | hr = E_INVALIDARG; |
| 6095 | } |
| 6096 | else |
| 6097 | { |
| 6098 | *ppThread = static_cast<ICorDebugThread*> (pThread); |
| 6099 | pThread->ExternalAddRef(); |
| 6100 | } |
| 6101 | } |
| 6102 | EX_CATCH_HRESULT(hr); |
| 6103 | |
| 6104 | LOG((LF_CORDB, LL_INFO10000, "CP::GT returns id=0x%x hr=0x%x ppThread=0x%p" , |
| 6105 | dwThreadId, hr, *ppThread)); |
| 6106 | return hr; |
| 6107 | } |
| 6108 | |
| 6109 | HRESULT CordbProcess::ThreadForFiberCookie(DWORD fiberCookie, |
| 6110 | ICorDebugThread **ppThread) |
| 6111 | { |
| 6112 | return E_NOTIMPL; |
| 6113 | } |
| 6114 | |
| 6115 | HRESULT CordbProcess::GetHelperThreadID(DWORD *pThreadID) |
| 6116 | { |
| 6117 | PUBLIC_API_ENTRY(this); |
| 6118 | FAIL_IF_NEUTERED(this); |
| 6119 | |
| 6120 | _ASSERTE(m_pShim != NULL); |
| 6121 | if (pThreadID == NULL) |
| 6122 | { |
| 6123 | return (E_INVALIDARG); |
| 6124 | } |
| 6125 | |
| 6126 | HRESULT hr = S_OK; |
| 6127 | // Return the ID of the current helper thread. There may be no thread in the process, or there may be a true helper |
| 6128 | // thread. |
| 6129 | if ((m_helperThreadId != 0) && !m_helperThreadDead) |
| 6130 | { |
| 6131 | *pThreadID = m_helperThreadId; |
| 6132 | } |
| 6133 | else if ((GetDCB() != NULL) && (GetDCB()->m_helperThreadId != 0)) |
| 6134 | { |
| 6135 | EX_TRY |
| 6136 | { |
| 6137 | // be sure we have the latest information |
| 6138 | UpdateRightSideDCB(); |
| 6139 | *pThreadID = GetDCB()->m_helperThreadId; |
| 6140 | } |
| 6141 | EX_CATCH_HRESULT(hr); |
| 6142 | |
| 6143 | } |
| 6144 | else |
| 6145 | { |
| 6146 | *pThreadID = 0; |
| 6147 | } |
| 6148 | |
| 6149 | return hr; |
| 6150 | } |
| 6151 | |
| 6152 | //--------------------------------------------------------------------------------------- |
| 6153 | // |
| 6154 | // Sends IPC event to set all the managed threads, except for the one given, to the given state |
| 6155 | // |
| 6156 | // Arguments: |
| 6157 | // state - The state to set the threads to. |
| 6158 | // pExceptThread - The thread to not set. This is usually the thread that is currently |
| 6159 | // sending an IPC event to the RS, and should be excluded. |
| 6160 | // |
| 6161 | // Return Value: |
| 6162 | // Typical HRESULT symantics, nothing abnormal. |
| 6163 | // |
| 6164 | HRESULT CordbProcess::SetAllThreadsDebugState(CorDebugThreadState state, |
| 6165 | ICorDebugThread * pExceptThread) |
| 6166 | { |
| 6167 | PUBLIC_REENTRANT_API_ENTRY(this); |
| 6168 | FAIL_IF_NEUTERED(this); |
| 6169 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pExceptThread, ICorDebugThread *); |
| 6170 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 6171 | |
| 6172 | if (GetShim() == NULL) |
| 6173 | { |
| 6174 | return E_NOTIMPL; |
| 6175 | } |
| 6176 | CordbThread * pCordbExceptThread = static_cast<CordbThread *> (pExceptThread); |
| 6177 | |
| 6178 | LOG((LF_CORDB, LL_INFO1000, "CP::SATDS: except thread=0x%08x 0x%x\n" , |
| 6179 | pExceptThread, |
| 6180 | (pCordbExceptThread != NULL) ? pCordbExceptThread->m_id : 0)); |
| 6181 | |
| 6182 | // Send one event to the Left Side to twiddle each thread's state. |
| 6183 | DebuggerIPCEvent event; |
| 6184 | |
| 6185 | InitIPCEvent(&event, DB_IPCE_SET_ALL_DEBUG_STATE, true, VMPTR_AppDomain::NullPtr()); |
| 6186 | |
| 6187 | event.SetAllDebugState.vmThreadToken = ((pCordbExceptThread != NULL) ? |
| 6188 | pCordbExceptThread->m_vmThreadToken : VMPTR_Thread::NullPtr()); |
| 6189 | |
| 6190 | event.SetAllDebugState.debugState = state; |
| 6191 | |
| 6192 | HRESULT hr = SendIPCEvent(&event, sizeof(DebuggerIPCEvent)); |
| 6193 | |
| 6194 | hr = WORST_HR(hr, event.hr); |
| 6195 | |
| 6196 | // If that worked, then loop over all the threads on this side and set their states. |
| 6197 | if (SUCCEEDED(hr)) |
| 6198 | { |
| 6199 | RSLockHolder lockHolder(GetProcessLock()); |
| 6200 | HASHFIND hashFind; |
| 6201 | CordbThread * pThread; |
| 6202 | |
| 6203 | // We don't need to prepopulate here (to collect LS state) because we're just updating RS state. |
| 6204 | for (pThread = m_userThreads.FindFirst(&hashFind); |
| 6205 | pThread != NULL; |
| 6206 | pThread = m_userThreads.FindNext(&hashFind)) |
| 6207 | { |
| 6208 | if (pThread != pCordbExceptThread) |
| 6209 | { |
| 6210 | pThread->m_debugState = state; |
| 6211 | } |
| 6212 | } |
| 6213 | } |
| 6214 | |
| 6215 | return hr; |
| 6216 | } |
| 6217 | |
| 6218 | |
| 6219 | HRESULT CordbProcess::EnumerateObjects(ICorDebugObjectEnum **ppObjects) |
| 6220 | { |
| 6221 | /* !!! */ |
| 6222 | PUBLIC_API_ENTRY(this); |
| 6223 | FAIL_IF_NEUTERED(this); |
| 6224 | VALIDATE_POINTER_TO_OBJECT(ppObjects, ICorDebugObjectEnum **); |
| 6225 | |
| 6226 | return E_NOTIMPL; |
| 6227 | } |
| 6228 | |
| 6229 | //--------------------------------------------------------------------------------------- |
| 6230 | // |
| 6231 | // Determines if the target address is a "CLR transition stub". |
| 6232 | // |
| 6233 | // Arguments: |
| 6234 | // address - The address of an instruction to check in the target address space. |
| 6235 | // pfTransitionStub - Space to store the result, TRUE if the address belongs to a |
| 6236 | // transition stub, FALSE if not. Only valid if this method returns a success code. |
| 6237 | // |
| 6238 | // Return Value: |
| 6239 | // Typical HRESULT symantics, nothing abnormal. |
| 6240 | // |
| 6241 | //--------------------------------------------------------------------------------------- |
| 6242 | HRESULT CordbProcess::IsTransitionStub(CORDB_ADDRESS address, BOOL *pfTransitionStub) |
| 6243 | { |
| 6244 | PUBLIC_API_ENTRY(this); |
| 6245 | FAIL_IF_NEUTERED(this); |
| 6246 | VALIDATE_POINTER_TO_OBJECT(pfTransitionStub, BOOL *); |
| 6247 | |
| 6248 | // Default to FALSE |
| 6249 | *pfTransitionStub = FALSE; |
| 6250 | |
| 6251 | if (this->m_helperThreadDead) |
| 6252 | { |
| 6253 | return S_OK; |
| 6254 | } |
| 6255 | |
| 6256 | // If we're not initialized, then it can't be a stub... |
| 6257 | if (!m_initialized) |
| 6258 | { |
| 6259 | return S_OK; |
| 6260 | } |
| 6261 | |
| 6262 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 6263 | |
| 6264 | HRESULT hr = S_OK; |
| 6265 | EX_TRY |
| 6266 | { |
| 6267 | DebuggerIPCEvent eventData; |
| 6268 | |
| 6269 | InitIPCEvent(&eventData, DB_IPCE_IS_TRANSITION_STUB, true, VMPTR_AppDomain::NullPtr()); |
| 6270 | |
| 6271 | eventData.IsTransitionStub.address = CORDB_ADDRESS_TO_PTR(address); |
| 6272 | |
| 6273 | hr = SendIPCEvent(&eventData, sizeof(eventData)); |
| 6274 | hr = WORST_HR(hr, eventData.hr); |
| 6275 | IfFailThrow(hr); |
| 6276 | |
| 6277 | _ASSERTE(eventData.type == DB_IPCE_IS_TRANSITION_STUB_RESULT); |
| 6278 | |
| 6279 | *pfTransitionStub = eventData.IsTransitionStubResult.isStub; |
| 6280 | LOG((LF_CORDB, LL_INFO1000, "CP::ITS: addr=0x%p result=%d\n" , address, *pfTransitionStub)); |
| 6281 | // @todo - beware that IsTransitionStub has a very important sideeffect - it synchronizes the runtime! |
| 6282 | // This for example covers an OS bug where SetThreadContext may silently fail if we're not synchronized. |
| 6283 | // (See IMDArocess::SetThreadContext for details on that bug). |
| 6284 | // If we ever stop using IPC events here and only use DAC; we need to be aware of that. |
| 6285 | |
| 6286 | // Check against DAC primitives |
| 6287 | { |
| 6288 | BOOL fIsStub2 = GetDAC()->IsTransitionStub(address); |
| 6289 | (void)fIsStub2; //prevent "unused variable" error from GCC |
| 6290 | CONSISTENCY_CHECK_MSGF(*pfTransitionStub == fIsStub2, ("IsStub2 failed, DAC2:%d, IPC:%d, addr:0x%p" , (int) fIsStub2, (int) *pfTransitionStub, CORDB_ADDRESS_TO_PTR(address))); |
| 6291 | |
| 6292 | } |
| 6293 | } |
| 6294 | EX_CATCH_HRESULT(hr); |
| 6295 | if(FAILED(hr)) |
| 6296 | { |
| 6297 | LOG((LF_CORDB, LL_INFO1000, "CP::ITS: FAILED hr=0x%x\n" , hr)); |
| 6298 | } |
| 6299 | return hr; |
| 6300 | } |
| 6301 | |
| 6302 | |
| 6303 | HRESULT CordbProcess::SetStopState(DWORD threadID, CorDebugThreadState state) |
| 6304 | { |
| 6305 | PUBLIC_API_ENTRY(this); |
| 6306 | FAIL_IF_NEUTERED(this); |
| 6307 | return E_NOTIMPL; |
| 6308 | } |
| 6309 | |
| 6310 | HRESULT CordbProcess::IsOSSuspended(DWORD threadID, BOOL *pbSuspended) |
| 6311 | { |
| 6312 | PUBLIC_API_ENTRY(this); |
| 6313 | // Gotta have a place for the result! |
| 6314 | if (!pbSuspended) |
| 6315 | return E_INVALIDARG; |
| 6316 | |
| 6317 | FAIL_IF_NEUTERED(this); |
| 6318 | |
| 6319 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 6320 | RSLockHolder lockHolder(GetProcessLock()); |
| 6321 | |
| 6322 | // Have we seen this thread? |
| 6323 | CordbUnmanagedThread *ut = GetUnmanagedThread(threadID); |
| 6324 | |
| 6325 | // If we have, and if we've suspended it, then say so. |
| 6326 | if (ut && ut->IsSuspended()) |
| 6327 | { |
| 6328 | *pbSuspended = TRUE; |
| 6329 | } |
| 6330 | else |
| 6331 | { |
| 6332 | *pbSuspended = FALSE; |
| 6333 | } |
| 6334 | #else |
| 6335 | // Not interop-debugging, we never OS suspend. |
| 6336 | *pbSuspended = FALSE; |
| 6337 | #endif |
| 6338 | return S_OK; |
| 6339 | } |
| 6340 | |
| 6341 | // |
| 6342 | // This routine reads a thread context from the process being debugged, taking into account the fact that the context |
| 6343 | // record may be a different size than the one we compiled with. On systems < NT5, then OS doesn't usually allocate |
| 6344 | // space for the extended registers. However, the CONTEXT struct that we compile with does have this space. |
| 6345 | // |
| 6346 | HRESULT CordbProcess::SafeReadThreadContext(LSPTR_CONTEXT pContext, DT_CONTEXT * pCtx) |
| 6347 | { |
| 6348 | HRESULT hr = S_OK; |
| 6349 | |
| 6350 | INTERNAL_API_ENTRY(this); |
| 6351 | FAIL_IF_NEUTERED(this); |
| 6352 | |
| 6353 | EX_TRY |
| 6354 | { |
| 6355 | |
| 6356 | void *pRemoteContext = pContext.UnsafeGet(); |
| 6357 | TargetBuffer tbFull(pRemoteContext, sizeof(DT_CONTEXT)); |
| 6358 | |
| 6359 | // The context may have 2 parts: |
| 6360 | // 1. Base register, which are always present. |
| 6361 | // 2. Optional extended registers, which are only present if CONTEXT_EXTENDED_REGISTERS is set |
| 6362 | // in the flags. |
| 6363 | |
| 6364 | // At a minimum we have room for a whole context up to the extended registers. |
| 6365 | #if defined(DT_CONTEXT_EXTENDED_REGISTERS) |
| 6366 | ULONG32 minContextSize = offsetof(DT_CONTEXT, ExtendedRegisters); |
| 6367 | #else |
| 6368 | ULONG32 minContextSize = sizeof(DT_CONTEXT); |
| 6369 | #endif |
| 6370 | |
| 6371 | // Read the minimum part. |
| 6372 | TargetBuffer tbMin = tbFull.SubBuffer(0, minContextSize); |
| 6373 | SafeReadBuffer(tbMin, (BYTE*) pCtx); |
| 6374 | |
| 6375 | #if defined(DT_CONTEXT_EXTENDED_REGISTERS) |
| 6376 | void *pCurExtReg = (void*)((UINT_PTR)pCtx + minContextSize); |
| 6377 | TargetBuffer tbExtended = tbFull.SubBuffer(minContextSize); |
| 6378 | |
| 6379 | // Now, read the extended registers if the context contains them. If the context does not have extended registers, |
| 6380 | // just set them to zero. |
| 6381 | if (SUCCEEDED(hr) && (pCtx->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) |
| 6382 | { |
| 6383 | SafeReadBuffer(tbExtended, (BYTE*) pCurExtReg); |
| 6384 | } |
| 6385 | else |
| 6386 | { |
| 6387 | memset(pCurExtReg, 0, tbExtended.cbSize); |
| 6388 | } |
| 6389 | #endif |
| 6390 | |
| 6391 | } |
| 6392 | EX_CATCH_HRESULT(hr); |
| 6393 | return hr; |
| 6394 | } |
| 6395 | |
| 6396 | // |
| 6397 | // This routine writes a thread context to the process being debugged, taking into account the fact that the context |
| 6398 | // record may be a different size than the one we compiled with. On systems < NT5, then OS doesn't usually allocate |
| 6399 | // space for the extended registers. However, the CONTEXT struct that we compile with does have this space. |
| 6400 | // |
| 6401 | HRESULT CordbProcess::SafeWriteThreadContext(LSPTR_CONTEXT pContext, const DT_CONTEXT * pCtx) |
| 6402 | { |
| 6403 | INTERNAL_API_ENTRY(this); |
| 6404 | FAIL_IF_NEUTERED(this); |
| 6405 | |
| 6406 | HRESULT hr = S_OK; |
| 6407 | DWORD sizeToWrite = sizeof(DT_CONTEXT); |
| 6408 | |
| 6409 | BYTE * pRemoteContext = (BYTE*) pContext.UnsafeGet(); |
| 6410 | BYTE * pCtxSource = (BYTE*) pCtx; |
| 6411 | |
| 6412 | |
| 6413 | #if defined(DT_CONTEXT_EXTENDED_REGISTERS) |
| 6414 | // If our context has extended registers, then write the whole thing. Otherwise, just write the minimum part. |
| 6415 | if ((pCtx->ContextFlags & DT_CONTEXT_EXTENDED_REGISTERS) != DT_CONTEXT_EXTENDED_REGISTERS) |
| 6416 | { |
| 6417 | sizeToWrite = offsetof(DT_CONTEXT, ExtendedRegisters); |
| 6418 | } |
| 6419 | #endif |
| 6420 | |
| 6421 | // 64 bit windows puts space for the first 6 stack parameters in the CONTEXT structure so that |
| 6422 | // kernel to usermode transitions don't have to allocate a CONTEXT and do a seperate sub rsp |
| 6423 | // to allocate stack spill space for the arguments. This means that writing to P1Home - P6Home |
| 6424 | // will overwrite the arguments of some function higher on the stack, very bad. Conceptually you |
| 6425 | // can think of these members as not being part of the context, ie they don't represent something |
| 6426 | // which gets saved or restored on context switches. They are just space we shouldn't overwrite. |
| 6427 | // See issue 630276 for more details. |
| 6428 | #if defined DBG_TARGET_AMD64 |
| 6429 | pRemoteContext += offsetof(CONTEXT, ContextFlags); // immediately follows the 6 parameters P1-P6 |
| 6430 | pCtxSource += offsetof(CONTEXT, ContextFlags); |
| 6431 | sizeToWrite -= offsetof(CONTEXT, ContextFlags); |
| 6432 | #endif |
| 6433 | |
| 6434 | EX_TRY |
| 6435 | { |
| 6436 | // Write the context. |
| 6437 | TargetBuffer tb(pRemoteContext, sizeToWrite); |
| 6438 | SafeWriteBuffer(tb, (const BYTE*) pCtxSource); |
| 6439 | } |
| 6440 | EX_CATCH_HRESULT(hr); |
| 6441 | |
| 6442 | return hr; |
| 6443 | } |
| 6444 | |
| 6445 | |
| 6446 | HRESULT CordbProcess::GetThreadContext(DWORD threadID, ULONG32 contextSize, BYTE context[]) |
| 6447 | { |
| 6448 | PUBLIC_REENTRANT_API_ENTRY(this); |
| 6449 | FAIL_IF_NEUTERED(this); |
| 6450 | LOG((LF_CORDB, LL_INFO10000, "CP::GTC: thread=0x%x\n" , threadID)); |
| 6451 | |
| 6452 | DT_CONTEXT * pContext; |
| 6453 | |
| 6454 | if (contextSize != sizeof(DT_CONTEXT)) |
| 6455 | { |
| 6456 | LOG((LF_CORDB, LL_INFO10000, "CP::GTC: thread=0x%x, context size is invalid.\n" , threadID)); |
| 6457 | return E_INVALIDARG; |
| 6458 | } |
| 6459 | |
| 6460 | pContext = reinterpret_cast<DT_CONTEXT *>(context); |
| 6461 | |
| 6462 | VALIDATE_POINTER_TO_OBJECT_ARRAY(context, BYTE, contextSize, true, true); |
| 6463 | |
| 6464 | if (this->IsInteropDebugging()) |
| 6465 | { |
| 6466 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 6467 | RSLockHolder lockHolder(GetProcessLock()); |
| 6468 | |
| 6469 | // Find the unmanaged thread |
| 6470 | CordbUnmanagedThread *ut = GetUnmanagedThread(threadID); |
| 6471 | |
| 6472 | if (ut == NULL) |
| 6473 | { |
| 6474 | LOG((LF_CORDB, LL_INFO10000, "CP::GTC: thread=0x%x, thread id is invalid.\n" , threadID)); |
| 6475 | |
| 6476 | return E_INVALIDARG; |
| 6477 | } |
| 6478 | |
| 6479 | return ut->GetThreadContext((DT_CONTEXT*)context); |
| 6480 | #else |
| 6481 | return E_NOTIMPL; |
| 6482 | #endif |
| 6483 | } |
| 6484 | else |
| 6485 | { |
| 6486 | RSLockHolder ch(GetProcess()->GetStopGoLock()); |
| 6487 | RSLockHolder lockHolder(GetProcessLock()); |
| 6488 | |
| 6489 | HRESULT hr = S_OK; |
| 6490 | EX_TRY |
| 6491 | { |
| 6492 | CordbThread* thread = this->TryLookupThreadByVolatileOSId(threadID); |
| 6493 | if (thread == NULL) |
| 6494 | { |
| 6495 | LOG((LF_CORDB, LL_INFO10000, "CP::GTC: thread=0x%x, thread id is invalid.\n" , threadID)); |
| 6496 | |
| 6497 | hr = E_INVALIDARG; |
| 6498 | } |
| 6499 | else |
| 6500 | { |
| 6501 | DT_CONTEXT* managedContext; |
| 6502 | hr = thread->GetManagedContext(&managedContext); |
| 6503 | *pContext = *managedContext; |
| 6504 | } |
| 6505 | } |
| 6506 | EX_CATCH_HRESULT(hr) |
| 6507 | return hr; |
| 6508 | } |
| 6509 | } |
| 6510 | |
| 6511 | // Public implementation of ICorDebugProcess::SetThreadContext. |
| 6512 | // @dbgtodo interop-debugging: this should go away in V3. Use the data-target instead. This is |
| 6513 | // interop-debugging aware (and cooperates with hijacks) |
| 6514 | HRESULT CordbProcess::SetThreadContext(DWORD threadID, ULONG32 contextSize, BYTE context[]) |
| 6515 | { |
| 6516 | PUBLIC_REENTRANT_API_ENTRY(this); |
| 6517 | |
| 6518 | HRESULT hr = S_OK; |
| 6519 | |
| 6520 | // @todo - could we look at the context flags and return E_INVALIDARG if they're bad? |
| 6521 | FAIL_IF_NEUTERED(this); |
| 6522 | VALIDATE_POINTER_TO_OBJECT_ARRAY(context, BYTE, contextSize, true, true); |
| 6523 | |
| 6524 | if (contextSize != sizeof(DT_CONTEXT)) |
| 6525 | { |
| 6526 | LOG((LF_CORDB, LL_INFO10000, "CP::STC: thread=0x%x, context size is invalid.\n" , threadID)); |
| 6527 | return E_INVALIDARG; |
| 6528 | } |
| 6529 | |
| 6530 | DT_CONTEXT* pContext = (DT_CONTEXT*)context; |
| 6531 | |
| 6532 | if (this->IsInteropDebugging()) |
| 6533 | { |
| 6534 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 6535 | RSLockHolder lockHolder(GetProcessLock()); |
| 6536 | |
| 6537 | CordbUnmanagedThread *ut = NULL; |
| 6538 | |
| 6539 | // Find the unmanaged thread |
| 6540 | ut = GetUnmanagedThread(threadID); |
| 6541 | |
| 6542 | if (ut == NULL) |
| 6543 | { |
| 6544 | LOG((LF_CORDB, LL_INFO10000, "CP::STC: thread=0x%x, thread is invalid.\n" , threadID)); |
| 6545 | return E_INVALIDARG; |
| 6546 | } |
| 6547 | |
| 6548 | hr = ut->SetThreadContext(pContext); |
| 6549 | |
| 6550 | // Update the register set for the leaf-unmanaged chain so that it's consistent w/ the context. |
| 6551 | // We may not necessarily be synchronized, and so these frames may be stale. Even so, no harm done. |
| 6552 | if (SUCCEEDED(hr)) |
| 6553 | { |
| 6554 | // @dbgtodo stackwalk: this should all disappear with V3 stackwalker and getting rid of SetThreadContext. |
| 6555 | EX_TRY |
| 6556 | { |
| 6557 | // Find the managed thread. Returns NULL if thread is not managed. |
| 6558 | // If we don't have a thread prveiously cached, then there's no state to update. |
| 6559 | CordbThread * pThread = TryLookupThreadByVolatileOSId(threadID); |
| 6560 | |
| 6561 | if (pThread != NULL) |
| 6562 | { |
| 6563 | // In V2, we used to update the CONTEXT of the leaf chain if the chain is an unmanaged chain. |
| 6564 | // In Arrowhead, we just force a cleanup of the stackwalk cache. This is a more correct |
| 6565 | // thing to do anyway, since the CONTEXT being set could be anything. |
| 6566 | pThread->CleanupStack(); |
| 6567 | } |
| 6568 | } |
| 6569 | EX_CATCH_HRESULT(hr); |
| 6570 | } |
| 6571 | #else |
| 6572 | return E_NOTIMPL; |
| 6573 | #endif |
| 6574 | } |
| 6575 | else |
| 6576 | { |
| 6577 | RSLockHolder ch(GetProcess()->GetStopGoLock()); |
| 6578 | RSLockHolder lockHolder(GetProcessLock()); |
| 6579 | |
| 6580 | EX_TRY |
| 6581 | { |
| 6582 | CordbThread* thread = this->TryLookupThreadByVolatileOSId(threadID); |
| 6583 | if (thread == NULL) |
| 6584 | { |
| 6585 | LOG((LF_CORDB, LL_INFO10000, "CP::GTC: thread=0x%x, thread id is invalid.\n" , threadID)); |
| 6586 | |
| 6587 | hr = E_INVALIDARG; |
| 6588 | } |
| 6589 | |
| 6590 | hr = thread->SetManagedContext(pContext); |
| 6591 | } |
| 6592 | EX_CATCH |
| 6593 | { |
| 6594 | hr = E_FAIL; |
| 6595 | } |
| 6596 | EX_END_CATCH(SwallowAllExceptions) |
| 6597 | |
| 6598 | |
| 6599 | } |
| 6600 | return hr; |
| 6601 | } |
| 6602 | |
| 6603 | |
| 6604 | // @dbgtodo ICDProcess - When we DACize this function, we should use code:DacReplacePatches |
| 6605 | HRESULT CordbProcess::ReadMemory(CORDB_ADDRESS address, |
| 6606 | DWORD size, |
| 6607 | BYTE buffer[], |
| 6608 | SIZE_T *read) |
| 6609 | { |
| 6610 | PUBLIC_REENTRANT_API_ENTRY(this); |
| 6611 | FAIL_IF_NEUTERED(this); |
| 6612 | |
| 6613 | // A read of 0 bytes is okay. |
| 6614 | if (size == 0) |
| 6615 | return S_OK; |
| 6616 | |
| 6617 | VALIDATE_POINTER_TO_OBJECT_ARRAY(buffer, BYTE, size, true, true); |
| 6618 | VALIDATE_POINTER_TO_OBJECT(buffer, SIZE_T *); |
| 6619 | |
| 6620 | if (address == NULL) |
| 6621 | return E_INVALIDARG; |
| 6622 | |
| 6623 | // If no read parameter is supplied, we ignore it. This matches the semantics of kernel32!ReadProcessMemory. |
| 6624 | SIZE_T dummyRead; |
| 6625 | if (read == NULL) |
| 6626 | { |
| 6627 | read = &dummyRead; |
| 6628 | } |
| 6629 | *read = 0; |
| 6630 | |
| 6631 | HRESULT hr = S_OK; |
| 6632 | |
| 6633 | CORDBRequireProcessStateOK(this); |
| 6634 | |
| 6635 | // Grab the memory we want to read |
| 6636 | // Note that this will return success on a partial read |
| 6637 | ULONG32 cbRead; |
| 6638 | hr = GetDataTarget()->ReadVirtual(address, buffer, size, &cbRead); |
| 6639 | if (FAILED(hr)) |
| 6640 | { |
| 6641 | hr = CORDBG_E_READVIRTUAL_FAILURE; |
| 6642 | goto LExit; |
| 6643 | } |
| 6644 | |
| 6645 | // Read at least one byte |
| 6646 | *read = (SIZE_T) cbRead; |
| 6647 | |
| 6648 | // There seem to be strange cases where ReadProcessMemory will return a seemingly negative number into *read, which |
| 6649 | // is an unsigned value. So we check the sanity of *read by ensuring that its no bigger than the size we tried to |
| 6650 | // read. |
| 6651 | if ((*read > 0) && (*read <= size)) |
| 6652 | { |
| 6653 | LOG((LF_CORDB, LL_INFO100000, "CP::RM: read %d bytes from 0x%08x, first byte is 0x%x\n" , |
| 6654 | *read, (DWORD)address, buffer[0])); |
| 6655 | |
| 6656 | if (m_initialized) |
| 6657 | { |
| 6658 | RSLockHolder ch(&this->m_processMutex); |
| 6659 | |
| 6660 | // If m_pPatchTable is NULL, then it's been cleaned out b/c of a Continue for the left side. Get the table |
| 6661 | // again. Only do this, of course, if the managed state of the process is initialized. |
| 6662 | if (m_pPatchTable == NULL) |
| 6663 | { |
| 6664 | hr = RefreshPatchTable(address, *read, buffer); |
| 6665 | } |
| 6666 | else |
| 6667 | { |
| 6668 | // The previously fetched table is still good, so run through it & see if any patches are applicable |
| 6669 | hr = AdjustBuffer(address, *read, buffer, NULL, AB_READ); |
| 6670 | } |
| 6671 | } |
| 6672 | } |
| 6673 | |
| 6674 | LExit: |
| 6675 | if (FAILED(hr)) |
| 6676 | { |
| 6677 | RSLockHolder ch(&this->m_processMutex); |
| 6678 | ClearPatchTable(); |
| 6679 | } |
| 6680 | else if (*read < size) |
| 6681 | { |
| 6682 | // Unlike the DT api, our API is supposed to return an error on partial read |
| 6683 | hr = HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); |
| 6684 | } |
| 6685 | return hr; |
| 6686 | } |
| 6687 | |
| 6688 | // Update patches & buffer to make the left-side's usage of patches transparent |
| 6689 | // to our client. Behavior depends on AB_MODE: |
| 6690 | // AB_READ: |
| 6691 | // - use the RS patch table structure to replace patch opcodes in buffer. |
| 6692 | // AB_WRITE: |
| 6693 | // - update the RS patch table structure w/ new replace-opcode values |
| 6694 | // if we've written over them. And put the int3 back in for write-memory. |
| 6695 | // |
| 6696 | // Note: If we're writing memory over top of a patch, then it must be JITted or stub code. |
| 6697 | // Writing over JITed or Stub code can be dangerous since the CLR may not expect it |
| 6698 | // (eg. JIT data structures about the code layout may be incorrect), but in certain |
| 6699 | // narrow cases it may be safe (eg. replacing a constant). VS says they wouldn't expect |
| 6700 | // this to work, but we'll keep the support in for legacy reasons. |
| 6701 | // |
| 6702 | // address, size - describe buffer in LS memory |
| 6703 | // buffer - local copy of buffer that will be read/written from/to LS. |
| 6704 | // bufferCopy - for writeprocessmemory, copy of original buffer (w/o injected patches) |
| 6705 | // pbUpdatePatchTable - flag if patchtable got dirty and needs to be updated. |
| 6706 | HRESULT CordbProcess::AdjustBuffer( CORDB_ADDRESS address, |
| 6707 | SIZE_T size, |
| 6708 | BYTE buffer[], |
| 6709 | BYTE **bufferCopy, |
| 6710 | AB_MODE mode, |
| 6711 | BOOL *pbUpdatePatchTable) |
| 6712 | { |
| 6713 | INTERNAL_API_ENTRY(this); |
| 6714 | |
| 6715 | _ASSERTE(m_initialized); |
| 6716 | _ASSERTE(this->ThreadHoldsProcessLock()); |
| 6717 | |
| 6718 | if ( address == NULL |
| 6719 | || size == NULL |
| 6720 | || buffer == NULL |
| 6721 | || (mode != AB_READ && mode != AB_WRITE) ) |
| 6722 | return E_INVALIDARG; |
| 6723 | |
| 6724 | if (pbUpdatePatchTable != NULL ) |
| 6725 | *pbUpdatePatchTable = FALSE; |
| 6726 | |
| 6727 | // If we don't have a patch table loaded, then return S_OK since there are no patches to adjust |
| 6728 | if (m_pPatchTable == NULL) |
| 6729 | return S_OK; |
| 6730 | |
| 6731 | //is the requested memory completely out-of-range? |
| 6732 | if ((m_minPatchAddr > (address + (size - 1))) || |
| 6733 | (m_maxPatchAddr < address)) |
| 6734 | { |
| 6735 | return S_OK; |
| 6736 | } |
| 6737 | |
| 6738 | // Without runtime offsets, we can't adjust - this should only ever happen on dumps, where there's |
| 6739 | // no W32ET to get the offsets, and so they stay zeroed |
| 6740 | if (!m_runtimeOffsetsInitialized) |
| 6741 | return S_OK; |
| 6742 | |
| 6743 | LOG((LF_CORDB,LL_INFO10000, "CordbProcess::AdjustBuffer at addr 0x%p\n" , address)); |
| 6744 | |
| 6745 | if (mode == AB_WRITE) |
| 6746 | { |
| 6747 | // We don't want to mess up the original copy of the buffer, so |
| 6748 | // for right now, just copy it wholesale. |
| 6749 | (*bufferCopy) = new (nothrow) BYTE[size]; |
| 6750 | if (NULL == (*bufferCopy)) |
| 6751 | return E_OUTOFMEMORY; |
| 6752 | |
| 6753 | memmove((*bufferCopy), buffer, size); |
| 6754 | } |
| 6755 | |
| 6756 | ULONG iNextFree = m_iFirstPatch; |
| 6757 | while( iNextFree != DPT_TERMINATING_INDEX ) |
| 6758 | { |
| 6759 | BYTE *DebuggerControllerPatch = m_pPatchTable + m_runtimeOffsets.m_cbPatch*iNextFree; |
| 6760 | PRD_TYPE opcode = *(PRD_TYPE *)(DebuggerControllerPatch + m_runtimeOffsets.m_offOpcode); |
| 6761 | CORDB_ADDRESS patchAddress = PTR_TO_CORDB_ADDRESS(*(BYTE**)(DebuggerControllerPatch + m_runtimeOffsets.m_offAddr)); |
| 6762 | |
| 6763 | if (IsPatchInRequestedRange(address, size, patchAddress)) |
| 6764 | { |
| 6765 | if (mode == AB_READ) |
| 6766 | { |
| 6767 | CORDbgSetInstructionEx(buffer, address, patchAddress, opcode, size); |
| 6768 | } |
| 6769 | else if (mode == AB_WRITE) |
| 6770 | { |
| 6771 | _ASSERTE( pbUpdatePatchTable != NULL ); |
| 6772 | _ASSERTE( bufferCopy != NULL ); |
| 6773 | |
| 6774 | //There can be multiple patches at the same address: we don't want 2nd+ patches to get the |
| 6775 | // break opcode, so we read from the unmodified copy. |
| 6776 | m_rgUncommitedOpcode[iNextFree] = |
| 6777 | CORDbgGetInstructionEx(*bufferCopy, address, patchAddress, opcode, size); |
| 6778 | |
| 6779 | //put the breakpoint into the memory itself |
| 6780 | CORDbgInsertBreakpointEx(buffer, address, patchAddress, opcode, size); |
| 6781 | |
| 6782 | *pbUpdatePatchTable = TRUE; |
| 6783 | } |
| 6784 | else |
| 6785 | _ASSERTE( !"CordbProcess::AdjustBuffergiven non(Read|Write) mode!" ); |
| 6786 | } |
| 6787 | |
| 6788 | iNextFree = m_rgNextPatch[iNextFree]; |
| 6789 | } |
| 6790 | |
| 6791 | // If we created a copy of the buffer but didn't modify it, then free it now. |
| 6792 | if( ( mode == AB_WRITE ) && ( !*pbUpdatePatchTable ) ) |
| 6793 | { |
| 6794 | delete [] *bufferCopy; |
| 6795 | *bufferCopy = NULL; |
| 6796 | } |
| 6797 | |
| 6798 | return S_OK; |
| 6799 | } |
| 6800 | |
| 6801 | |
| 6802 | void CordbProcess::CommitBufferAdjustments( CORDB_ADDRESS start, |
| 6803 | CORDB_ADDRESS end ) |
| 6804 | { |
| 6805 | INTERNAL_API_ENTRY(this); |
| 6806 | |
| 6807 | _ASSERTE(m_initialized); |
| 6808 | _ASSERTE(this->ThreadHoldsProcessLock()); |
| 6809 | _ASSERTE(m_runtimeOffsetsInitialized); |
| 6810 | |
| 6811 | ULONG iPatch = m_iFirstPatch; |
| 6812 | while( iPatch != DPT_TERMINATING_INDEX ) |
| 6813 | { |
| 6814 | BYTE *DebuggerControllerPatch = m_pPatchTable + |
| 6815 | m_runtimeOffsets.m_cbPatch*iPatch; |
| 6816 | |
| 6817 | BYTE *patchAddress = *(BYTE**)(DebuggerControllerPatch + m_runtimeOffsets.m_offAddr); |
| 6818 | |
| 6819 | if (IsPatchInRequestedRange(start, (SIZE_T)(end - start), PTR_TO_CORDB_ADDRESS(patchAddress)) && |
| 6820 | !PRDIsBreakInst(&(m_rgUncommitedOpcode[iPatch]))) |
| 6821 | { |
| 6822 | //copy this back to the copy of the patch table |
| 6823 | *(PRD_TYPE *)(DebuggerControllerPatch + m_runtimeOffsets.m_offOpcode) = |
| 6824 | m_rgUncommitedOpcode[iPatch]; |
| 6825 | } |
| 6826 | |
| 6827 | iPatch = m_rgNextPatch[iPatch]; |
| 6828 | } |
| 6829 | } |
| 6830 | |
| 6831 | void CordbProcess::ClearBufferAdjustments( ) |
| 6832 | { |
| 6833 | INTERNAL_API_ENTRY(this); |
| 6834 | _ASSERTE(this->ThreadHoldsProcessLock()); |
| 6835 | |
| 6836 | ULONG iPatch = m_iFirstPatch; |
| 6837 | while( iPatch != DPT_TERMINATING_INDEX ) |
| 6838 | { |
| 6839 | InitializePRDToBreakInst(&(m_rgUncommitedOpcode[iPatch])); |
| 6840 | iPatch = m_rgNextPatch[iPatch]; |
| 6841 | } |
| 6842 | } |
| 6843 | |
| 6844 | void CordbProcess::ClearPatchTable(void ) |
| 6845 | { |
| 6846 | INTERNAL_API_ENTRY(this); |
| 6847 | _ASSERTE(this->ThreadHoldsProcessLock()); |
| 6848 | |
| 6849 | if (m_pPatchTable != NULL ) |
| 6850 | { |
| 6851 | delete [] m_pPatchTable; |
| 6852 | m_pPatchTable = NULL; |
| 6853 | |
| 6854 | delete [] m_rgNextPatch; |
| 6855 | m_rgNextPatch = NULL; |
| 6856 | |
| 6857 | delete [] m_rgUncommitedOpcode; |
| 6858 | m_rgUncommitedOpcode = NULL; |
| 6859 | |
| 6860 | m_iFirstPatch = DPT_TERMINATING_INDEX; |
| 6861 | m_minPatchAddr = MAX_ADDRESS; |
| 6862 | m_maxPatchAddr = MIN_ADDRESS; |
| 6863 | m_rgData = NULL; |
| 6864 | m_cPatch = 0; |
| 6865 | } |
| 6866 | } |
| 6867 | |
| 6868 | HRESULT CordbProcess::RefreshPatchTable(CORDB_ADDRESS address, SIZE_T size, BYTE buffer[]) |
| 6869 | { |
| 6870 | CONTRACTL |
| 6871 | { |
| 6872 | NOTHROW; |
| 6873 | } |
| 6874 | CONTRACTL_END; |
| 6875 | |
| 6876 | INTERNAL_API_ENTRY(this); |
| 6877 | _ASSERTE(m_initialized); |
| 6878 | _ASSERTE(this->ThreadHoldsProcessLock()); |
| 6879 | |
| 6880 | HRESULT hr = S_OK; |
| 6881 | BYTE *rgb = NULL; |
| 6882 | |
| 6883 | // All of m_runtimeOffsets will be zeroed out if there's been no call to code:CordbProcess::GetRuntimeOffsets. |
| 6884 | // Thus for things to work, we'd have to have a live target that went and got the real values. |
| 6885 | // For dumps, things are still all zeroed out because we don't have any events sent to the W32ET, don't |
| 6886 | // have a live process to investigate, etc. |
| 6887 | if (!m_runtimeOffsetsInitialized) |
| 6888 | return S_OK; |
| 6889 | |
| 6890 | _ASSERTE( m_runtimeOffsets.m_cbOpcode == sizeof(PRD_TYPE) ); |
| 6891 | |
| 6892 | CORDBRequireProcessStateOK(this); |
| 6893 | |
| 6894 | if (m_pPatchTable == NULL ) |
| 6895 | { |
| 6896 | // First, check to be sure the patch table is valid on the Left Side. If its not, then we won't read it. |
| 6897 | BOOL fPatchTableValid = FALSE; |
| 6898 | |
| 6899 | hr = SafeReadStruct(PTR_TO_CORDB_ADDRESS(m_runtimeOffsets.m_pPatchTableValid), &fPatchTableValid); |
| 6900 | if (FAILED(hr) || !fPatchTableValid) |
| 6901 | { |
| 6902 | LOG((LF_CORDB, LL_INFO10000, "Wont refresh patch table because its not valid now.\n" )); |
| 6903 | return S_OK; |
| 6904 | } |
| 6905 | |
| 6906 | SIZE_T offStart = 0; |
| 6907 | SIZE_T offEnd = 0; |
| 6908 | UINT cbTableSlice = 0; |
| 6909 | |
| 6910 | // Grab the patch table info |
| 6911 | offStart = min(m_runtimeOffsets.m_offRgData, m_runtimeOffsets.m_offCData); |
| 6912 | offEnd = max(m_runtimeOffsets.m_offRgData, m_runtimeOffsets.m_offCData) + sizeof(SIZE_T); |
| 6913 | cbTableSlice = (UINT)(offEnd - offStart); |
| 6914 | |
| 6915 | if (cbTableSlice == 0) |
| 6916 | { |
| 6917 | LOG((LF_CORDB, LL_INFO10000, "Wont refresh patch table because its not valid now.\n" )); |
| 6918 | return S_OK; |
| 6919 | } |
| 6920 | |
| 6921 | EX_TRY |
| 6922 | { |
| 6923 | rgb = new BYTE[cbTableSlice]; // throws |
| 6924 | |
| 6925 | TargetBuffer tbSlice((BYTE*)m_runtimeOffsets.m_pPatches + offStart, cbTableSlice); |
| 6926 | this->SafeReadBuffer(tbSlice, rgb); // Throws; |
| 6927 | |
| 6928 | // Note that rgData is a pointer in the left side address space |
| 6929 | m_rgData = *(BYTE**)(rgb + m_runtimeOffsets.m_offRgData - offStart); |
| 6930 | m_cPatch = *(ULONG*)(rgb + m_runtimeOffsets.m_offCData - offStart); |
| 6931 | |
| 6932 | // Grab the patch table |
| 6933 | UINT cbPatchTable = (UINT)(m_cPatch * m_runtimeOffsets.m_cbPatch); |
| 6934 | |
| 6935 | if (cbPatchTable == 0) |
| 6936 | { |
| 6937 | LOG((LF_CORDB, LL_INFO10000, "Wont refresh patch table because its not valid now.\n" )); |
| 6938 | _ASSERTE(hr == S_OK); |
| 6939 | goto LExit; // can't return since we're in a Try/Catch |
| 6940 | } |
| 6941 | |
| 6942 | // Throwing news |
| 6943 | m_pPatchTable = new BYTE[ cbPatchTable ]; |
| 6944 | m_rgNextPatch = new ULONG[m_cPatch]; |
| 6945 | m_rgUncommitedOpcode = new PRD_TYPE[m_cPatch]; |
| 6946 | |
| 6947 | TargetBuffer tb(m_rgData, cbPatchTable); |
| 6948 | this->SafeReadBuffer(tb, m_pPatchTable); // Throws |
| 6949 | |
| 6950 | //As we go through the patch table we do a number of things: |
| 6951 | // |
| 6952 | // 1. collect min,max address seen for quick fail check |
| 6953 | // |
| 6954 | // 2. Link all valid entries into a linked list, the first entry of which is m_iFirstPatch |
| 6955 | // |
| 6956 | // 3. Initialize m_rgUncommitedOpcode, so that we can undo local patch table changes if WriteMemory can't write |
| 6957 | // atomically. |
| 6958 | // |
| 6959 | // 4. If the patch is in the memory we grabbed, unapply it. |
| 6960 | |
| 6961 | ULONG iDebuggerControllerPatchPrev = DPT_TERMINATING_INDEX; |
| 6962 | |
| 6963 | m_minPatchAddr = MAX_ADDRESS; |
| 6964 | m_maxPatchAddr = MIN_ADDRESS; |
| 6965 | m_iFirstPatch = DPT_TERMINATING_INDEX; |
| 6966 | |
| 6967 | for (ULONG iPatch = 0; iPatch < m_cPatch;iPatch++) |
| 6968 | { |
| 6969 | // <REVISIT_TODO>@todo port: we're making assumptions about the size of opcodes,address pointers, etc</REVISIT_TODO> |
| 6970 | BYTE *DebuggerControllerPatch = m_pPatchTable + m_runtimeOffsets.m_cbPatch * iPatch; |
| 6971 | PRD_TYPE opcode = *(PRD_TYPE*)(DebuggerControllerPatch + m_runtimeOffsets.m_offOpcode); |
| 6972 | CORDB_ADDRESS patchAddress = PTR_TO_CORDB_ADDRESS(*(BYTE**)(DebuggerControllerPatch + m_runtimeOffsets.m_offAddr)); |
| 6973 | |
| 6974 | // A non-zero opcode indicates to us that this patch is valid. |
| 6975 | if (!PRDIsEmpty(opcode)) |
| 6976 | { |
| 6977 | _ASSERTE( patchAddress != 0 ); |
| 6978 | |
| 6979 | // (1), above |
| 6980 | // Note that GetPatchEndAddr() returns the address immediately AFTER the patch, |
| 6981 | // so we have to subtract 1 from it below. |
| 6982 | if (m_minPatchAddr > patchAddress ) |
| 6983 | m_minPatchAddr = patchAddress; |
| 6984 | if (m_maxPatchAddr < patchAddress ) |
| 6985 | m_maxPatchAddr = GetPatchEndAddr(patchAddress) - 1; |
| 6986 | |
| 6987 | // (2), above |
| 6988 | if ( m_iFirstPatch == DPT_TERMINATING_INDEX) |
| 6989 | { |
| 6990 | m_iFirstPatch = iPatch; |
| 6991 | _ASSERTE( iPatch != DPT_TERMINATING_INDEX); |
| 6992 | } |
| 6993 | |
| 6994 | if (iDebuggerControllerPatchPrev != DPT_TERMINATING_INDEX) |
| 6995 | { |
| 6996 | m_rgNextPatch[iDebuggerControllerPatchPrev] = iPatch; |
| 6997 | } |
| 6998 | |
| 6999 | iDebuggerControllerPatchPrev = iPatch; |
| 7000 | |
| 7001 | // (3), above |
| 7002 | InitializePRDToBreakInst(&(m_rgUncommitedOpcode[iPatch])); |
| 7003 | |
| 7004 | // (4), above |
| 7005 | if (IsPatchInRequestedRange(address, size, patchAddress)) |
| 7006 | { |
| 7007 | _ASSERTE( buffer != NULL ); |
| 7008 | _ASSERTE( size != NULL ); |
| 7009 | |
| 7010 | |
| 7011 | //unapply the patch here. |
| 7012 | CORDbgSetInstructionEx(buffer, address, patchAddress, opcode, size); |
| 7013 | } |
| 7014 | |
| 7015 | } |
| 7016 | } |
| 7017 | |
| 7018 | if (iDebuggerControllerPatchPrev != DPT_TERMINATING_INDEX) |
| 7019 | { |
| 7020 | m_rgNextPatch[iDebuggerControllerPatchPrev] = DPT_TERMINATING_INDEX; |
| 7021 | } |
| 7022 | } |
| 7023 | LExit: |
| 7024 | ; |
| 7025 | EX_CATCH_HRESULT(hr); |
| 7026 | } |
| 7027 | |
| 7028 | |
| 7029 | if (rgb != NULL ) |
| 7030 | { |
| 7031 | delete [] rgb; |
| 7032 | } |
| 7033 | |
| 7034 | if (FAILED( hr ) ) |
| 7035 | { |
| 7036 | ClearPatchTable(); |
| 7037 | } |
| 7038 | |
| 7039 | return hr; |
| 7040 | } |
| 7041 | |
| 7042 | //--------------------------------------------------------------------------------------- |
| 7043 | // |
| 7044 | // Given an address, see if there is a patch in the patch table that matches it and return |
| 7045 | // if its an unmanaged patch or not. |
| 7046 | // |
| 7047 | // Arguments: |
| 7048 | // address - The address of an instruction to check in the target address space. |
| 7049 | // pfPatchFound - Space to store the result, TRUE if the address belongs to a |
| 7050 | // patch, FALSE if not. Only valid if this method returns a success code. |
| 7051 | // pfPatchIsUnmanaged - Space to store the result, TRUE if the address is a patch |
| 7052 | // and the patch is unmanaged, FALSE if not. Only valid if this method returns a |
| 7053 | // success code. |
| 7054 | // |
| 7055 | // Return Value: |
| 7056 | // Typical HRESULT symantics, nothing abnormal. |
| 7057 | // |
| 7058 | // Note: this method is pretty in-efficient. It refreshes the patch table, then scans it. |
| 7059 | // Refreshing the patch table involves a scan, too, so this method could be folded |
| 7060 | // with that. |
| 7061 | // |
| 7062 | //--------------------------------------------------------------------------------------- |
| 7063 | HRESULT CordbProcess::FindPatchByAddress(CORDB_ADDRESS address, bool *pfPatchFound, bool *pfPatchIsUnmanaged) |
| 7064 | { |
| 7065 | INTERNAL_API_ENTRY(this); |
| 7066 | _ASSERTE(ThreadHoldsProcessLock()); |
| 7067 | _ASSERTE((pfPatchFound != NULL) && (pfPatchIsUnmanaged != NULL)); |
| 7068 | _ASSERTE(m_runtimeOffsetsInitialized); |
| 7069 | FAIL_IF_NEUTERED(this); |
| 7070 | |
| 7071 | *pfPatchFound = false; |
| 7072 | *pfPatchIsUnmanaged = false; |
| 7073 | |
| 7074 | // First things first. If the process isn't initialized, then there can be no patch table, so we know the breakpoint |
| 7075 | // doesn't belong to the Runtime. |
| 7076 | if (!m_initialized) |
| 7077 | { |
| 7078 | return S_OK; |
| 7079 | } |
| 7080 | |
| 7081 | // This method is called from the main loop of the win32 event thread in response to a first chance breakpoint event |
| 7082 | // that we know is not a flare. The process has been runnning, and it may have invalidated the patch table, so we'll |
| 7083 | // flush it here before refreshing it to make sure we've got the right thing. |
| 7084 | // |
| 7085 | // Note: we really should have the Left Side mark the patch table dirty to help optimize this. |
| 7086 | ClearPatchTable(); |
| 7087 | |
| 7088 | // Refresh the patch table. |
| 7089 | HRESULT hr = RefreshPatchTable(); |
| 7090 | |
| 7091 | if (FAILED(hr)) |
| 7092 | { |
| 7093 | LOG((LF_CORDB, LL_INFO1000, "CP::FPBA: failed to refresh the patch table\n" )); |
| 7094 | return hr; |
| 7095 | } |
| 7096 | |
| 7097 | // If there is no patch table yet, then we know there is no patch at the given address, so return S_OK with |
| 7098 | // *patchFound = false. |
| 7099 | if (m_pPatchTable == NULL) |
| 7100 | { |
| 7101 | LOG((LF_CORDB, LL_INFO1000, "CP::FPBA: no patch table\n" )); |
| 7102 | return S_OK; |
| 7103 | } |
| 7104 | |
| 7105 | // Scan the patch table for a matching patch. |
| 7106 | for (ULONG iNextPatch = m_iFirstPatch; iNextPatch != DPT_TERMINATING_INDEX; iNextPatch = m_rgNextPatch[iNextPatch]) |
| 7107 | { |
| 7108 | BYTE *patch = m_pPatchTable + (m_runtimeOffsets.m_cbPatch * iNextPatch); |
| 7109 | BYTE *patchAddress = *(BYTE**)(patch + m_runtimeOffsets.m_offAddr); |
| 7110 | DWORD traceType = *(DWORD*)(patch + m_runtimeOffsets.m_offTraceType); |
| 7111 | |
| 7112 | if (address == PTR_TO_CORDB_ADDRESS(patchAddress)) |
| 7113 | { |
| 7114 | *pfPatchFound = true; |
| 7115 | |
| 7116 | if (traceType == m_runtimeOffsets.m_traceTypeUnmanaged) |
| 7117 | { |
| 7118 | *pfPatchIsUnmanaged = true; |
| 7119 | |
| 7120 | #if defined(_DEBUG) |
| 7121 | HRESULT hrDac = S_OK; |
| 7122 | EX_TRY |
| 7123 | { |
| 7124 | // We should be able to double check w/ DAC that this really is outside of the runtime. |
| 7125 | IDacDbiInterface::AddressType addrType = GetDAC()->GetAddressType(address); |
| 7126 | CONSISTENCY_CHECK_MSGF(addrType == IDacDbiInterface::kAddressUnrecognized, ("Bad address type = %d" , addrType)); |
| 7127 | } |
| 7128 | EX_CATCH_HRESULT(hrDac); |
| 7129 | CONSISTENCY_CHECK_MSGF(SUCCEEDED(hrDac), ("DAC::GetAddressType failed, hr=0x%08x" , hrDac)); |
| 7130 | #endif |
| 7131 | } |
| 7132 | |
| 7133 | break; |
| 7134 | } |
| 7135 | } |
| 7136 | |
| 7137 | // If we didn't find a patch, its actually still possible that this breakpoint exception belongs to us. There are |
| 7138 | // races with very large numbers of threads entering the Runtime through the same managed function. We will have |
| 7139 | // multiple threads adding and removing ref counts to an int 3 in the code stream. Sometimes, this count will go to |
| 7140 | // zero and the int 3 will be removed, then it will come back up and the int 3 will be replaced. The in-process |
| 7141 | // logic takes pains to ensure that such cases are handled properly, therefore we need to perform the same check |
| 7142 | // here to make the correct decision. Basically, the check is to see if there is indeed an int 3 at the exception |
| 7143 | // address. If there is _not_ an int 3 there, then we've hit this race. We will lie and say a managed patch was |
| 7144 | // found to cover this case. This is tracking the logic in DebuggerController::ScanForTriggers, where we call |
| 7145 | // IsPatched. |
| 7146 | if (*pfPatchFound == false) |
| 7147 | { |
| 7148 | // Read one instruction from the faulting address... |
| 7149 | #if defined(DBG_TARGET_ARM) || defined(DBG_TARGET_ARM64) |
| 7150 | PRD_TYPE TrapCheck = 0; |
| 7151 | #else |
| 7152 | BYTE TrapCheck = 0; |
| 7153 | #endif |
| 7154 | |
| 7155 | HRESULT hr2 = SafeReadStruct(address, &TrapCheck); |
| 7156 | |
| 7157 | if (SUCCEEDED(hr2) && (TrapCheck != CORDbg_BREAK_INSTRUCTION)) |
| 7158 | { |
| 7159 | LOG((LF_CORDB, LL_INFO1000, "CP::FPBA: patchFound=true based on odd missing int 3 case.\n" )); |
| 7160 | |
| 7161 | *pfPatchFound = true; |
| 7162 | } |
| 7163 | } |
| 7164 | |
| 7165 | LOG((LF_CORDB, LL_INFO1000, "CP::FPBA: patchFound=%d, patchIsUnmanaged=%d\n" , *pfPatchFound, *pfPatchIsUnmanaged)); |
| 7166 | |
| 7167 | return S_OK; |
| 7168 | } |
| 7169 | |
| 7170 | HRESULT CordbProcess::WriteMemory(CORDB_ADDRESS address, DWORD size, |
| 7171 | BYTE buffer[], SIZE_T *written) |
| 7172 | { |
| 7173 | PUBLIC_REENTRANT_API_ENTRY(this); |
| 7174 | FAIL_IF_NEUTERED(this); |
| 7175 | CORDBRequireProcessStateOK(this); |
| 7176 | _ASSERTE(m_runtimeOffsetsInitialized); |
| 7177 | |
| 7178 | |
| 7179 | if (size == 0 || address == NULL) |
| 7180 | return E_INVALIDARG; |
| 7181 | |
| 7182 | VALIDATE_POINTER_TO_OBJECT_ARRAY(buffer, BYTE, size, true, true); |
| 7183 | VALIDATE_POINTER_TO_OBJECT(written, SIZE_T *); |
| 7184 | |
| 7185 | |
| 7186 | #if defined(_DEBUG) && defined(FEATURE_INTEROP_DEBUGGING) |
| 7187 | // Shouldn't be using this to write int3. Use UM BP API instead. |
| 7188 | // This is technically legal (what if the '0xcc' is data or something), so we can't fail in retail. |
| 7189 | // But we can add this debug-only check to help VS migrate to the new API. |
| 7190 | static ConfigDWORD configCheckInt3; |
| 7191 | DWORD fCheckInt3 = configCheckInt3.val(CLRConfig::INTERNAL_DbgCheckInt3); |
| 7192 | if (fCheckInt3) |
| 7193 | { |
| 7194 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64) |
| 7195 | if (size == 1 && buffer[0] == 0xCC) |
| 7196 | { |
| 7197 | CONSISTENCY_CHECK_MSGF(false, |
| 7198 | ("You're using ICorDebugProcess::WriteMemory() to write an 'int3' (1 byte 0xCC) at address 0x%p.\n" |
| 7199 | "If you're trying to set a breakpoint, you should be using ICorDebugProcess::SetUnmanagedBreakpoint() instead.\n" |
| 7200 | "(This assert is only enabled under the COM+ knob DbgCheckInt3.)\n" , |
| 7201 | CORDB_ADDRESS_TO_PTR(address))); |
| 7202 | } |
| 7203 | #endif // DBG_TARGET_X86 || DBG_TARGET_AMD64 |
| 7204 | |
| 7205 | // check if we're replaced an opcode. |
| 7206 | if (size == 1) |
| 7207 | { |
| 7208 | RSLockHolder ch(&this->m_processMutex); |
| 7209 | |
| 7210 | NativePatch * p = GetNativePatch(CORDB_ADDRESS_TO_PTR(address)); |
| 7211 | if (p != NULL) |
| 7212 | { |
| 7213 | CONSISTENCY_CHECK_MSGF(false, |
| 7214 | ("You're using ICorDebugProcess::WriteMemory() to write an 'opcode (0x%x)' at address 0x%p.\n" |
| 7215 | "There's already a native patch at that address from ICorDebugProcess::SetUnmanagedBreakpoint().\n" |
| 7216 | "If you're trying to remove the breakpoint, use ICDProcess::ClearUnmanagedBreakpoint() instead.\n" |
| 7217 | "(This assert is only enabled under the COM+ knob DbgCheckInt3.)\n" , |
| 7218 | (DWORD) (buffer[0]), CORDB_ADDRESS_TO_PTR(address))); |
| 7219 | } |
| 7220 | } |
| 7221 | } |
| 7222 | #endif // _DEBUG && FEATURE_INTEROP_DEBUGGING |
| 7223 | |
| 7224 | |
| 7225 | *written = 0; |
| 7226 | |
| 7227 | HRESULT hr = S_OK; |
| 7228 | HRESULT hrSaved = hr; // this will hold the 'real' hresult in case of a |
| 7229 | // partially completed operation |
| 7230 | HRESULT hrPartialCopy = HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); |
| 7231 | |
| 7232 | BOOL bUpdateOriginalPatchTable = FALSE; |
| 7233 | BYTE *bufferCopy = NULL; |
| 7234 | |
| 7235 | // Only update the patch table if the managed state of the process |
| 7236 | // is initialized. |
| 7237 | if (m_initialized) |
| 7238 | { |
| 7239 | RSLockHolder ch(&this->m_processMutex); |
| 7240 | |
| 7241 | if (m_pPatchTable == NULL ) |
| 7242 | { |
| 7243 | if (!SUCCEEDED( hr = RefreshPatchTable() ) ) |
| 7244 | { |
| 7245 | goto LExit; |
| 7246 | } |
| 7247 | } |
| 7248 | |
| 7249 | if ( !SUCCEEDED( hr = AdjustBuffer( address, |
| 7250 | size, |
| 7251 | buffer, |
| 7252 | &bufferCopy, |
| 7253 | AB_WRITE, |
| 7254 | &bUpdateOriginalPatchTable))) |
| 7255 | { |
| 7256 | goto LExit; |
| 7257 | } |
| 7258 | } |
| 7259 | |
| 7260 | //conveniently enough, SafeWriteBuffer will throw if it can't complete the entire operation |
| 7261 | EX_TRY |
| 7262 | { |
| 7263 | TargetBuffer tb(address, size); |
| 7264 | SafeWriteBuffer(tb, buffer); // throws |
| 7265 | *written = tb.cbSize; // DT's Write does everything or fails. |
| 7266 | } |
| 7267 | EX_CATCH_HRESULT(hr); |
| 7268 | |
| 7269 | if (FAILED(hr)) |
| 7270 | { |
| 7271 | if(hr != hrPartialCopy) |
| 7272 | goto LExit; |
| 7273 | else |
| 7274 | hrSaved = hr; |
| 7275 | } |
| 7276 | |
| 7277 | |
| 7278 | LOG((LF_CORDB, LL_INFO100000, "CP::WM: wrote %d bytes at 0x%08x, first byte is 0x%x\n" , |
| 7279 | *written, (DWORD)address, buffer[0])); |
| 7280 | |
| 7281 | if (bUpdateOriginalPatchTable == TRUE ) |
| 7282 | { |
| 7283 | { |
| 7284 | RSLockHolder ch(&this->m_processMutex); |
| 7285 | |
| 7286 | //don't tweak patch table for stuff that isn't written to LeftSide |
| 7287 | CommitBufferAdjustments(address, address + *written); |
| 7288 | } |
| 7289 | |
| 7290 | // The only way this should be able to fail is if |
| 7291 | //someone else fiddles with the memory protections on the |
| 7292 | //left side while it's frozen |
| 7293 | EX_TRY |
| 7294 | { |
| 7295 | TargetBuffer tb(m_rgData, (ULONG) (m_cPatch*m_runtimeOffsets.m_cbPatch)); |
| 7296 | SafeWriteBuffer(tb, m_pPatchTable); |
| 7297 | } |
| 7298 | EX_CATCH_HRESULT(hr); |
| 7299 | SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr); |
| 7300 | } |
| 7301 | |
| 7302 | // Since we may have |
| 7303 | // overwritten anything (objects, code, etc), we should mark |
| 7304 | // everything as needing to be re-cached. |
| 7305 | m_continueCounter++; |
| 7306 | |
| 7307 | LExit: |
| 7308 | if (m_initialized) |
| 7309 | { |
| 7310 | RSLockHolder ch(&this->m_processMutex); |
| 7311 | ClearBufferAdjustments( ); |
| 7312 | } |
| 7313 | |
| 7314 | //we messed up our local copy, so get a clean copy the next time |
| 7315 | //we need it |
| 7316 | if (bUpdateOriginalPatchTable==TRUE) |
| 7317 | { |
| 7318 | if (bufferCopy != NULL) |
| 7319 | { |
| 7320 | memmove(buffer, bufferCopy, size); |
| 7321 | delete bufferCopy; |
| 7322 | } |
| 7323 | } |
| 7324 | |
| 7325 | if (FAILED( hr )) |
| 7326 | { |
| 7327 | //we messed up our local copy, so get a clean copy the next time |
| 7328 | //we need it |
| 7329 | if (bUpdateOriginalPatchTable==TRUE) |
| 7330 | { |
| 7331 | RSLockHolder ch(&this->m_processMutex); |
| 7332 | ClearPatchTable(); |
| 7333 | } |
| 7334 | } |
| 7335 | else if( FAILED(hrSaved) ) |
| 7336 | { |
| 7337 | hr = hrSaved; |
| 7338 | } |
| 7339 | |
| 7340 | return hr; |
| 7341 | } |
| 7342 | |
| 7343 | HRESULT CordbProcess::ClearCurrentException(DWORD threadID) |
| 7344 | { |
| 7345 | #ifndef FEATURE_INTEROP_DEBUGGING |
| 7346 | return E_INVALIDARG; |
| 7347 | #else |
| 7348 | PUBLIC_API_ENTRY(this); |
| 7349 | |
| 7350 | RSLockHolder lockHolder(GetProcessLock()); |
| 7351 | |
| 7352 | // There's something wrong if you're calling this an there are no queued unmanaged events. |
| 7353 | if ((m_unmanagedEventQueue == NULL) && (m_outOfBandEventQueue == NULL)) |
| 7354 | return E_INVALIDARG; |
| 7355 | |
| 7356 | // Grab the unmanaged thread object. |
| 7357 | CordbUnmanagedThread *pUThread = GetUnmanagedThread(threadID); |
| 7358 | |
| 7359 | if (pUThread == NULL) |
| 7360 | return E_INVALIDARG; |
| 7361 | |
| 7362 | LOG((LF_CORDB, LL_INFO1000, "CP::CCE: tid=0x%x\n" , threadID)); |
| 7363 | |
| 7364 | // We clear both the IB and OOB event. |
| 7365 | if (pUThread->HasIBEvent() && !pUThread->IBEvent()->IsEventUserContinued()) |
| 7366 | { |
| 7367 | pUThread->IBEvent()->SetState(CUES_ExceptionCleared); |
| 7368 | } |
| 7369 | |
| 7370 | if (pUThread->HasOOBEvent()) |
| 7371 | { |
| 7372 | // must decide exception status _before_ we continue the event. |
| 7373 | _ASSERTE(!pUThread->OOBEvent()->IsEventContinuedUnhijacked()); |
| 7374 | pUThread->OOBEvent()->SetState(CUES_ExceptionCleared); |
| 7375 | } |
| 7376 | |
| 7377 | // If the thread is hijacked, then set the thread's debugger word to 0 to indicate to it that the |
| 7378 | // exception has been cleared. |
| 7379 | if (pUThread->IsGenericHijacked()) |
| 7380 | { |
| 7381 | HRESULT hr = pUThread->SetEEDebuggerWord(0); |
| 7382 | _ASSERTE(SUCCEEDED(hr)); |
| 7383 | } |
| 7384 | |
| 7385 | return S_OK; |
| 7386 | #endif // FEATURE_INTEROP_DEBUGGING |
| 7387 | } |
| 7388 | |
| 7389 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 7390 | CordbUnmanagedThread *CordbProcess::HandleUnmanagedCreateThread(DWORD dwThreadId, HANDLE hThread, void *lpThreadLocalBase) |
| 7391 | { |
| 7392 | INTERNAL_API_ENTRY(this); |
| 7393 | CordbUnmanagedThread *ut = new (nothrow) CordbUnmanagedThread(this, dwThreadId, hThread, lpThreadLocalBase); |
| 7394 | |
| 7395 | if (ut != NULL) |
| 7396 | { |
| 7397 | HRESULT hr = m_unmanagedThreads.AddBase(ut); // InternalAddRef, release on EXIT_THREAD events. |
| 7398 | |
| 7399 | if (!SUCCEEDED(hr)) |
| 7400 | { |
| 7401 | delete ut; |
| 7402 | ut = NULL; |
| 7403 | |
| 7404 | LOG((LF_CORDB, LL_INFO10000, "Failed adding unmanaged thread to process!\n" )); |
| 7405 | CORDBSetUnrecoverableError(this, hr, 0); |
| 7406 | } |
| 7407 | } |
| 7408 | else |
| 7409 | { |
| 7410 | LOG((LF_CORDB, LL_INFO10000, "New CordbThread failed!\n" )); |
| 7411 | CORDBSetUnrecoverableError(this, E_OUTOFMEMORY, 0); |
| 7412 | } |
| 7413 | |
| 7414 | return ut; |
| 7415 | } |
| 7416 | #endif // FEATURE_INTEROP_DEBUGGING |
| 7417 | |
| 7418 | |
| 7419 | //----------------------------------------------------------------------------- |
| 7420 | // Initializes the DAC |
| 7421 | // Arguments: none--initializes the DAC for this CordbProcess instance |
| 7422 | // Note: Throws on error |
| 7423 | //----------------------------------------------------------------------------- |
| 7424 | void CordbProcess::InitDac() |
| 7425 | { |
| 7426 | // Go-Go DAC power!! |
| 7427 | HRESULT hr = S_OK; |
| 7428 | EX_TRY |
| 7429 | { |
| 7430 | InitializeDac(); |
| 7431 | } |
| 7432 | EX_CATCH_HRESULT(hr); |
| 7433 | |
| 7434 | // We Need DAC to debug for both Managed & Interop. |
| 7435 | if (FAILED(hr)) |
| 7436 | { |
| 7437 | // We assert here b/c we're trying to be friendly. Most likely, the cause is either: |
| 7438 | // - a bad installation |
| 7439 | // - a CLR dev built mscorwks but didn't build DAC. |
| 7440 | SIMPLIFYING_ASSUMPTION_MSGF(false, ("Failed to load DAC while for debugging. hr=0x%08x" , hr)); |
| 7441 | ThrowHR(hr); |
| 7442 | } |
| 7443 | } //CordbProcess::InitDac |
| 7444 | |
| 7445 | // Update the entire RS copy of the debugger control block by reading the LS copy. The RS copy is treated as |
| 7446 | // a throw-away temporary buffer, rather than a true cache. That is, we make no assumptions about the |
| 7447 | // validity of the information over time. Thus, before using any of the values, we need to update it. We |
| 7448 | // update everything for simplicity; any perf hit we take by doing this instead of updating the individual |
| 7449 | // fields we want at any given point isn't significant, particularly if we are updating multiple fields. |
| 7450 | |
| 7451 | // Arguments: |
| 7452 | // none, but reads process memory from the LS debugger control block |
| 7453 | // Return Value: none (copies from LS DCB to RS buffer GetDCB()) |
| 7454 | // Note: throws if SafeReadBuffer fails |
| 7455 | void CordbProcess::UpdateRightSideDCB() |
| 7456 | { |
| 7457 | IfFailThrow(m_pEventChannel->UpdateRightSideDCB()); |
| 7458 | } // CordbProcess::UpdateRightSideDCB |
| 7459 | |
| 7460 | // Update a single field with a value stored in the RS copy of the DCB. We can't update the entire LS DCB |
| 7461 | // because in some cases, the LS and RS are simultaneously initializing the DCB. If we initialize a field on |
| 7462 | // the RS and write back the whole thing, we may overwrite something the LS has initialized in the interim. |
| 7463 | |
| 7464 | // Arguments: |
| 7465 | // input: rsFieldAddr - the address of the field in the RS copy of the DCB that we want to write back to |
| 7466 | // the LS DCB. We use this to compute the offset of the field from the beginning of the |
| 7467 | // DCB and then add this offset to the starting address of the LS DCB to get the LS |
| 7468 | // address of the field we are updating |
| 7469 | // size - the size of the field we're updating. |
| 7470 | // Return value: none |
| 7471 | // Note: throws if SafeWriteBuffer fails |
| 7472 | void CordbProcess::UpdateLeftSideDCBField(void * rsFieldAddr, SIZE_T size) |
| 7473 | { |
| 7474 | IfFailThrow(m_pEventChannel->UpdateLeftSideDCBField(rsFieldAddr, size)); |
| 7475 | } // CordbProcess::UpdateRightSideDCB |
| 7476 | |
| 7477 | |
| 7478 | //----------------------------------------------------------------------------- |
| 7479 | // Gets the remote address of the event block for the Target and verifies that it's valid. |
| 7480 | // We use this address when we need to read from or write to the debugger control block. |
| 7481 | // Also allocates the RS buffer used for temporary storage for information from the DCB and |
| 7482 | // copies the LS DCB into the RS buffer. |
| 7483 | // Arguments: |
| 7484 | // output: pfBlockExists - true iff the LS DCB has been successfully allocated. Note that |
| 7485 | // we need this information even if the function throws, so we can't simply send it back |
| 7486 | // as a return value. |
| 7487 | // Return value: |
| 7488 | // None, but allocates GetDCB() on success. If the LS DCB has not |
| 7489 | // been successfully initialized or if this throws, GetDCB() will be NULL. |
| 7490 | // |
| 7491 | // Notes: |
| 7492 | // Throws on error |
| 7493 | // |
| 7494 | //----------------------------------------------------------------------------- |
| 7495 | void CordbProcess::GetEventBlock(BOOL * pfBlockExists) |
| 7496 | { |
| 7497 | if (GetDCB() == NULL) // we only need to do this once |
| 7498 | { |
| 7499 | _ASSERTE(m_pShim != NULL); |
| 7500 | _ASSERTE(ThreadHoldsProcessLock()); |
| 7501 | |
| 7502 | // This will Initialize the DAC/DBI interface. |
| 7503 | BOOL fDacReady = TryInitializeDac(); |
| 7504 | |
| 7505 | if (fDacReady) |
| 7506 | { |
| 7507 | // Ensure that we have a DAC interface. |
| 7508 | _ASSERTE(m_pDacPrimitives != NULL); |
| 7509 | |
| 7510 | // This is not technically necessary for Mac debugging. The event channel doesn't rely on |
| 7511 | // knowing the target address of the DCB on the LS. |
| 7512 | CORDB_ADDRESS pLeftSideDCB = NULL; |
| 7513 | pLeftSideDCB = (GetDAC()->GetDebuggerControlBlockAddress()); |
| 7514 | if (pLeftSideDCB == NULL) |
| 7515 | { |
| 7516 | *pfBlockExists = false; |
| 7517 | ThrowHR(CORDBG_E_DEBUGGING_NOT_POSSIBLE); |
| 7518 | } |
| 7519 | |
| 7520 | IfFailThrow(NewEventChannelForThisPlatform(pLeftSideDCB, |
| 7521 | m_pMutableDataTarget, |
| 7522 | GetProcessDescriptor(), |
| 7523 | m_pShim->GetMachineInfo(), |
| 7524 | &m_pEventChannel)); |
| 7525 | _ASSERTE(m_pEventChannel != NULL); |
| 7526 | |
| 7527 | // copy information from left side DCB |
| 7528 | UpdateRightSideDCB(); |
| 7529 | |
| 7530 | // Verify that the control block is valid. |
| 7531 | // This will throw on error. |
| 7532 | VerifyControlBlock(); |
| 7533 | |
| 7534 | *pfBlockExists = true; |
| 7535 | } |
| 7536 | else |
| 7537 | { |
| 7538 | // we can't initialize the DAC, so we can't get the block |
| 7539 | *pfBlockExists = false; |
| 7540 | } |
| 7541 | } |
| 7542 | else // we got the block before |
| 7543 | { |
| 7544 | *pfBlockExists = true; |
| 7545 | } |
| 7546 | |
| 7547 | } // CordbProcess::GetEventBlock() |
| 7548 | |
| 7549 | |
| 7550 | // |
| 7551 | // Verify that the version info in the control block matches what we expect. The minimum supported protocol from the |
| 7552 | // Left Side must be greater or equal to the minimum required protocol of the Right Side. Note: its the Left Side's job |
| 7553 | // to conform to whatever protocol the Right Side requires, so long as minimum is supported. |
| 7554 | // |
| 7555 | void CordbProcess::VerifyControlBlock() |
| 7556 | { |
| 7557 | INTERNAL_API_ENTRY(this); |
| 7558 | _ASSERTE(m_pShim != NULL); |
| 7559 | |
| 7560 | if (GetDCB()->m_DCBSize == 0) |
| 7561 | { |
| 7562 | // the LS is still initializing the DCB |
| 7563 | ThrowHR(CORDBG_E_DEBUGGING_NOT_POSSIBLE); |
| 7564 | } |
| 7565 | |
| 7566 | // Fill in the protocol numbers for the Right Side and update the LS DCB. |
| 7567 | GetDCB()->m_rightSideProtocolCurrent = CorDB_RightSideProtocolCurrent; |
| 7568 | UpdateLeftSideDCBField(&(GetDCB()->m_rightSideProtocolCurrent), sizeof(GetDCB()->m_rightSideProtocolCurrent)); |
| 7569 | |
| 7570 | GetDCB()->m_rightSideProtocolMinSupported = CorDB_RightSideProtocolMinSupported; |
| 7571 | UpdateLeftSideDCBField(&(GetDCB()->m_rightSideProtocolMinSupported), |
| 7572 | sizeof(GetDCB()->m_rightSideProtocolMinSupported)); |
| 7573 | |
| 7574 | // For Telesto, Dbi and Wks have a more flexible versioning allowed, as described by the Debugger |
| 7575 | // Version Protocol String in DEBUGGER_PROTOCOL_STRING in DbgIpcEvents.h. This allows different build |
| 7576 | // numbers, but the other protocol numbers should still match. |
| 7577 | |
| 7578 | // These assertions verify that the debug manager is behaving correctly. |
| 7579 | // An assertion failure here means that the runtime version of the debuggee is different from the runtime version of |
| 7580 | // the debugger is capable of debugging. |
| 7581 | |
| 7582 | // The Debug Manager should properly match LS & RS, and thus guarantee that this assert should never fire. |
| 7583 | // But just in case the installation is corrupted, we'll check it. |
| 7584 | if (GetDCB()->m_DCBSize != sizeof(DebuggerIPCControlBlock)) |
| 7585 | { |
| 7586 | CONSISTENCY_CHECK_MSGF(false, ("DCB in LS is %d bytes, in RS is %d bytes. Version mismatch!!\n" , |
| 7587 | GetDCB()->m_DCBSize, sizeof(DebuggerIPCControlBlock))); |
| 7588 | ThrowHR(CORDBG_E_INCOMPATIBLE_PROTOCOL); |
| 7589 | } |
| 7590 | |
| 7591 | // The Left Side has to support at least our minimum required protocol. |
| 7592 | if (GetDCB()->m_leftSideProtocolCurrent < GetDCB()->m_rightSideProtocolMinSupported) |
| 7593 | { |
| 7594 | _ASSERTE(GetDCB()->m_leftSideProtocolCurrent >= GetDCB()->m_rightSideProtocolMinSupported); |
| 7595 | ThrowHR(CORDBG_E_INCOMPATIBLE_PROTOCOL); |
| 7596 | } |
| 7597 | |
| 7598 | // The Left Side has to be able to emulate at least our minimum required protocol. |
| 7599 | if (GetDCB()->m_leftSideProtocolMinSupported > GetDCB()->m_rightSideProtocolCurrent) |
| 7600 | { |
| 7601 | _ASSERTE(GetDCB()->m_leftSideProtocolMinSupported <= GetDCB()->m_rightSideProtocolCurrent); |
| 7602 | ThrowHR(CORDBG_E_INCOMPATIBLE_PROTOCOL); |
| 7603 | } |
| 7604 | |
| 7605 | #ifdef _DEBUG |
| 7606 | char buf[MAX_LONGPATH]; |
| 7607 | DWORD len = GetEnvironmentVariableA("CORDBG_NotCompatibleTest" , buf, sizeof(buf)); |
| 7608 | _ASSERTE(len < sizeof(buf)); |
| 7609 | |
| 7610 | if (len > 0) |
| 7611 | ThrowHR(CORDBG_E_INCOMPATIBLE_PROTOCOL); |
| 7612 | #endif |
| 7613 | |
| 7614 | if (GetDCB()->m_bHostingInFiber) |
| 7615 | { |
| 7616 | ThrowHR(CORDBG_E_CANNOT_DEBUG_FIBER_PROCESS); |
| 7617 | } |
| 7618 | |
| 7619 | _ASSERTE(!GetDCB()->m_rightSideShouldCreateHelperThread); |
| 7620 | } // CordbProcess::VerifyControlBlock |
| 7621 | |
| 7622 | //----------------------------------------------------------------------------- |
| 7623 | // This is the CordbProcess objects chance to inspect the DCB and intialize stuff |
| 7624 | // |
| 7625 | // Return Value: |
| 7626 | // Typical HRESULT return values, nothing abnormal. |
| 7627 | // If succeeded, then the block exists and is valid. |
| 7628 | // |
| 7629 | //----------------------------------------------------------------------------- |
| 7630 | HRESULT CordbProcess::GetRuntimeOffsets() |
| 7631 | { |
| 7632 | INTERNAL_API_ENTRY(this); |
| 7633 | |
| 7634 | _ASSERTE(m_pShim != NULL); |
| 7635 | UpdateRightSideDCB(); |
| 7636 | |
| 7637 | // Can't get a handle to the helper thread if the target is remote. |
| 7638 | |
| 7639 | // If we got this far w/o failing, then we should be able to get the helper thread handle. |
| 7640 | // RS will handle not having the helper-thread handle, so we just make a best effort here. |
| 7641 | DWORD dwHelperTid = GetDCB()->m_realHelperThreadId; |
| 7642 | _ASSERTE(dwHelperTid != 0); |
| 7643 | |
| 7644 | |
| 7645 | { |
| 7646 | #if !defined FEATURE_CORESYSTEM |
| 7647 | // kernel32!OpenThread does not exist on all platforms (missing on Win98). |
| 7648 | // So we need to delay load it. |
| 7649 | typedef HANDLE (WINAPI *FPOPENTHREAD)(DWORD dwDesiredAccess, |
| 7650 | BOOL bInheritHandle, |
| 7651 | DWORD dwThreadId); |
| 7652 | |
| 7653 | |
| 7654 | |
| 7655 | HMODULE mod = WszGetModuleHandle(W("kernel32.dll" )); |
| 7656 | |
| 7657 | _ASSERTE(mod != NULL); // can't fail since Kernel32.dll is already loaded. |
| 7658 | |
| 7659 | const FPOPENTHREAD pfnOpenThread = (FPOPENTHREAD)GetProcAddress(mod, "OpenThread" ); |
| 7660 | |
| 7661 | if (pfnOpenThread != NULL) |
| 7662 | { |
| 7663 | m_hHelperThread = pfnOpenThread(SYNCHRONIZE, FALSE, dwHelperTid); |
| 7664 | CONSISTENCY_CHECK_MSGF(m_hHelperThread != NULL, ("Failed to get helper-thread handle. tid=0x%x\n" , dwHelperTid)); |
| 7665 | } |
| 7666 | #elif FEATURE_PAL |
| 7667 | m_hHelperThread = NULL; //RS is supposed to be able to live without a helper thread handle. |
| 7668 | #else |
| 7669 | m_hHelperThread = OpenThread(SYNCHRONIZE, FALSE, dwHelperTid); |
| 7670 | CONSISTENCY_CHECK_MSGF(m_hHelperThread != NULL, ("Failed to get helper-thread handle. tid=0x%x\n" , dwHelperTid)); |
| 7671 | #endif |
| 7672 | } |
| 7673 | |
| 7674 | // get the remote address of the runtime offsets structure and read the structure itself |
| 7675 | HRESULT hrRead = SafeReadStruct(PTR_TO_CORDB_ADDRESS(GetDCB()->m_pRuntimeOffsets), &m_runtimeOffsets); |
| 7676 | |
| 7677 | if (FAILED(hrRead)) |
| 7678 | { |
| 7679 | return hrRead; |
| 7680 | } |
| 7681 | |
| 7682 | LOG((LF_CORDB, LL_INFO10000, "CP::GRO: got runtime offsets: \n" )); |
| 7683 | |
| 7684 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 7685 | LOG((LF_CORDB, LL_INFO10000, " m_genericHijackFuncAddr= 0x%p\n" , |
| 7686 | m_runtimeOffsets.m_genericHijackFuncAddr)); |
| 7687 | LOG((LF_CORDB, LL_INFO10000, " m_signalHijackStartedBPAddr= 0x%p\n" , |
| 7688 | m_runtimeOffsets.m_signalHijackStartedBPAddr)); |
| 7689 | LOG((LF_CORDB, LL_INFO10000, " m_excepNotForRuntimeBPAddr= 0x%p\n" , |
| 7690 | m_runtimeOffsets.m_excepNotForRuntimeBPAddr)); |
| 7691 | LOG((LF_CORDB, LL_INFO10000, " m_notifyRSOfSyncCompleteBPAddr= 0x%p\n" , |
| 7692 | m_runtimeOffsets.m_notifyRSOfSyncCompleteBPAddr)); |
| 7693 | LOG((LF_CORDB, LL_INFO10000, " m_raiseException= 0x%p\n" , |
| 7694 | m_runtimeOffsets.m_raiseExceptionAddr)); |
| 7695 | LOG((LF_CORDB, LL_INFO10000, " m_debuggerWordTLSIndex= 0x%08x\n" , |
| 7696 | m_runtimeOffsets.m_debuggerWordTLSIndex)); |
| 7697 | #endif // FEATURE_INTEROP_DEBUGGING |
| 7698 | |
| 7699 | LOG((LF_CORDB, LL_INFO10000, " m_TLSIndex= 0x%08x\n" , |
| 7700 | m_runtimeOffsets.m_TLSIndex)); |
| 7701 | LOG((LF_CORDB, LL_INFO10000, " m_EEThreadStateOffset= 0x%08x\n" , |
| 7702 | m_runtimeOffsets.m_EEThreadStateOffset)); |
| 7703 | LOG((LF_CORDB, LL_INFO10000, " m_EEThreadStateNCOffset= 0x%08x\n" , |
| 7704 | m_runtimeOffsets.m_EEThreadStateNCOffset)); |
| 7705 | LOG((LF_CORDB, LL_INFO10000, " m_EEThreadPGCDisabledOffset= 0x%08x\n" , |
| 7706 | m_runtimeOffsets.m_EEThreadPGCDisabledOffset)); |
| 7707 | LOG((LF_CORDB, LL_INFO10000, " m_EEThreadPGCDisabledValue= 0x%08x\n" , |
| 7708 | m_runtimeOffsets.m_EEThreadPGCDisabledValue)); |
| 7709 | LOG((LF_CORDB, LL_INFO10000, " m_EEThreadFrameOffset= 0x%08x\n" , |
| 7710 | m_runtimeOffsets.m_EEThreadFrameOffset)); |
| 7711 | LOG((LF_CORDB, LL_INFO10000, " m_EEThreadMaxNeededSize= 0x%08x\n" , |
| 7712 | m_runtimeOffsets.m_EEThreadMaxNeededSize)); |
| 7713 | LOG((LF_CORDB, LL_INFO10000, " m_EEThreadSteppingStateMask= 0x%08x\n" , |
| 7714 | m_runtimeOffsets.m_EEThreadSteppingStateMask)); |
| 7715 | LOG((LF_CORDB, LL_INFO10000, " m_EEMaxFrameValue= 0x%08x\n" , |
| 7716 | m_runtimeOffsets.m_EEMaxFrameValue)); |
| 7717 | LOG((LF_CORDB, LL_INFO10000, " m_EEThreadDebuggerFilterContextOffset= 0x%08x\n" , |
| 7718 | m_runtimeOffsets.m_EEThreadDebuggerFilterContextOffset)); |
| 7719 | LOG((LF_CORDB, LL_INFO10000, " m_EEThreadCantStopOffset= 0x%08x\n" , |
| 7720 | m_runtimeOffsets.m_EEThreadCantStopOffset)); |
| 7721 | LOG((LF_CORDB, LL_INFO10000, " m_EEFrameNextOffset= 0x%08x\n" , |
| 7722 | m_runtimeOffsets.m_EEFrameNextOffset)); |
| 7723 | LOG((LF_CORDB, LL_INFO10000, " m_EEIsManagedExceptionStateMask= 0x%08x\n" , |
| 7724 | m_runtimeOffsets.m_EEIsManagedExceptionStateMask)); |
| 7725 | LOG((LF_CORDB, LL_INFO10000, " m_pPatches= 0x%08x\n" , |
| 7726 | m_runtimeOffsets.m_pPatches)); |
| 7727 | LOG((LF_CORDB, LL_INFO10000, " m_offRgData= 0x%08x\n" , |
| 7728 | m_runtimeOffsets.m_offRgData)); |
| 7729 | LOG((LF_CORDB, LL_INFO10000, " m_offCData= 0x%08x\n" , |
| 7730 | m_runtimeOffsets.m_offCData)); |
| 7731 | LOG((LF_CORDB, LL_INFO10000, " m_cbPatch= 0x%08x\n" , |
| 7732 | m_runtimeOffsets.m_cbPatch)); |
| 7733 | LOG((LF_CORDB, LL_INFO10000, " m_offAddr= 0x%08x\n" , |
| 7734 | m_runtimeOffsets.m_offAddr)); |
| 7735 | LOG((LF_CORDB, LL_INFO10000, " m_offOpcode= 0x%08x\n" , |
| 7736 | m_runtimeOffsets.m_offOpcode)); |
| 7737 | LOG((LF_CORDB, LL_INFO10000, " m_cbOpcode= 0x%08x\n" , |
| 7738 | m_runtimeOffsets.m_cbOpcode)); |
| 7739 | LOG((LF_CORDB, LL_INFO10000, " m_offTraceType= 0x%08x\n" , |
| 7740 | m_runtimeOffsets.m_offTraceType)); |
| 7741 | LOG((LF_CORDB, LL_INFO10000, " m_traceTypeUnmanaged= 0x%08x\n" , |
| 7742 | m_runtimeOffsets.m_traceTypeUnmanaged)); |
| 7743 | |
| 7744 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 7745 | // Flares are only used for interop debugging. |
| 7746 | |
| 7747 | // Do check that the flares are all at unique offsets. |
| 7748 | // Since this is determined at link-time, we need a run-time check (an |
| 7749 | // assert isn't good enough, since this would only happen in a super |
| 7750 | // optimized / bbt run). |
| 7751 | { |
| 7752 | const void * flares[] = { |
| 7753 | m_runtimeOffsets.m_signalHijackStartedBPAddr, |
| 7754 | m_runtimeOffsets.m_excepForRuntimeHandoffStartBPAddr, |
| 7755 | m_runtimeOffsets.m_excepForRuntimeHandoffCompleteBPAddr, |
| 7756 | m_runtimeOffsets.m_signalHijackCompleteBPAddr, |
| 7757 | m_runtimeOffsets.m_excepNotForRuntimeBPAddr, |
| 7758 | m_runtimeOffsets.m_notifyRSOfSyncCompleteBPAddr, |
| 7759 | }; |
| 7760 | |
| 7761 | const int NumFlares = NumItems(flares); |
| 7762 | |
| 7763 | // Ensure that all of the flares are unique. |
| 7764 | for(int i = 0; i < NumFlares; i++) |
| 7765 | { |
| 7766 | for(int j = i+1; j < NumFlares; j++) |
| 7767 | { |
| 7768 | if (flares[i] == flares[j]) |
| 7769 | { |
| 7770 | // If we ever fail here, that means the LS build is busted. |
| 7771 | |
| 7772 | // This assert is useful if we drop a checked RS onto a retail |
| 7773 | // LS (that's legal). |
| 7774 | _ASSERTE(!"LS has matching Flares." ); |
| 7775 | LOG((LF_CORDB, LL_ALWAYS, "Failing because of matching flares.\n" )); |
| 7776 | return CORDBG_E_INCOMPATIBLE_PROTOCOL; |
| 7777 | } |
| 7778 | } |
| 7779 | } |
| 7780 | } |
| 7781 | |
| 7782 | #endif // FEATURE_INTEROP_DEBUGGING |
| 7783 | m_runtimeOffsetsInitialized = true; |
| 7784 | return S_OK; |
| 7785 | } |
| 7786 | |
| 7787 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 7788 | |
| 7789 | //----------------------------------------------------------------------------- |
| 7790 | // Resume hijacked threads. |
| 7791 | //----------------------------------------------------------------------------- |
| 7792 | void CordbProcess::ResumeHijackedThreads() |
| 7793 | { |
| 7794 | INTERNAL_API_ENTRY(this); |
| 7795 | _ASSERTE(m_pShim != NULL); |
| 7796 | _ASSERTE(ThreadHoldsProcessLock()); |
| 7797 | |
| 7798 | LOG((LF_CORDB, LL_INFO10000, "CP::RHT: entered\n" )); |
| 7799 | if (this->m_state & (CordbProcess::PS_SOME_THREADS_SUSPENDED | CordbProcess::PS_HIJACKS_IN_PLACE)) |
| 7800 | { |
| 7801 | // On XP, This will also resume the threads suspended for Sync. |
| 7802 | this->ResumeUnmanagedThreads(); |
| 7803 | } |
| 7804 | |
| 7805 | // Hijacks send their ownership flares and then wait on this event. By setting this |
| 7806 | // we let the hijacks run free. |
| 7807 | if (this->m_leftSideUnmanagedWaitEvent != NULL) |
| 7808 | { |
| 7809 | SetEvent(this->m_leftSideUnmanagedWaitEvent); |
| 7810 | } |
| 7811 | else |
| 7812 | { |
| 7813 | // Only reason we expect to not have this event is if the CLR hasn't been loaded yet. |
| 7814 | // In that case, we won't hijack, so nobody's listening for this event either. |
| 7815 | _ASSERTE(!m_initialized); |
| 7816 | } |
| 7817 | } |
| 7818 | |
| 7819 | //----------------------------------------------------------------------------- |
| 7820 | // For debugging support, record the win32 events. |
| 7821 | // Note that although this is for debugging, we want it in retail because we'll |
| 7822 | // be debugging retail most of the time :( |
| 7823 | // pEvent - the win32 debug event we just received |
| 7824 | // pUThread - our unmanaged thread object for the event. We could look it up |
| 7825 | // from pEvent->dwThreadId, but passed in for perf reasons. |
| 7826 | //----------------------------------------------------------------------------- |
| 7827 | void CordbProcess::DebugRecordWin32Event(const DEBUG_EVENT * pEvent, CordbUnmanagedThread * pUThread) |
| 7828 | { |
| 7829 | _ASSERTE(ThreadHoldsProcessLock()); |
| 7830 | |
| 7831 | // Although we could look up the Unmanaged thread, it's faster to have it just passed in. |
| 7832 | // So here we do a consistency check. |
| 7833 | _ASSERTE(pUThread != NULL); |
| 7834 | _ASSERTE(pUThread->m_id == pEvent->dwThreadId); |
| 7835 | |
| 7836 | m_DbgSupport.m_TotalNativeEvents++; // bump up the counter. |
| 7837 | |
| 7838 | MiniDebugEvent * pMiniEvent = &m_DbgSupport.m_DebugEventQueue[m_DbgSupport.m_DebugEventQueueIdx]; |
| 7839 | pMiniEvent->code = (BYTE) pEvent->dwDebugEventCode; |
| 7840 | pMiniEvent->pUThread = pUThread; |
| 7841 | |
| 7842 | DWORD tid = pEvent->dwThreadId; |
| 7843 | |
| 7844 | // Record debug-event specific data. |
| 7845 | switch(pEvent->dwDebugEventCode) |
| 7846 | { |
| 7847 | case LOAD_DLL_DEBUG_EVENT: |
| 7848 | pMiniEvent->u.ModuleData.pBaseAddress = pEvent->u.LoadDll.lpBaseOfDll; |
| 7849 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "Win32 Debug Event received: tid=0x%8x, Load Dll. Addr=%p\n" , |
| 7850 | tid, |
| 7851 | pEvent->u.LoadDll.lpBaseOfDll); |
| 7852 | break; |
| 7853 | case UNLOAD_DLL_DEBUG_EVENT: |
| 7854 | pMiniEvent->u.ModuleData.pBaseAddress = pEvent->u.UnloadDll.lpBaseOfDll; |
| 7855 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "Win32 Debug Event received: tid=0x%8x, Unload Dll. Addr=%p\n" , |
| 7856 | tid, |
| 7857 | pEvent->u.UnloadDll.lpBaseOfDll); |
| 7858 | break; |
| 7859 | case EXCEPTION_DEBUG_EVENT: |
| 7860 | pMiniEvent->u.ExceptionData.pAddress = pEvent->u.Exception.ExceptionRecord.ExceptionAddress; |
| 7861 | pMiniEvent->u.ExceptionData.dwCode = pEvent->u.Exception.ExceptionRecord.ExceptionCode; |
| 7862 | |
| 7863 | STRESS_LOG3(LF_CORDB, LL_INFO1000, "Win32 Debug Event received: tid=%8x, (1) Exception. Code=0x%08x, Addr=%p\n" , |
| 7864 | tid, |
| 7865 | pMiniEvent->u.ExceptionData.dwCode, |
| 7866 | pMiniEvent->u.ExceptionData.pAddress |
| 7867 | ); |
| 7868 | break; |
| 7869 | default: |
| 7870 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "Win32 Debug Event received tid=%8x, %d\n" , tid, pEvent->dwDebugEventCode); |
| 7871 | break; |
| 7872 | } |
| 7873 | |
| 7874 | |
| 7875 | // Go to the next entry in the queue. |
| 7876 | m_DbgSupport.m_DebugEventQueueIdx = (m_DbgSupport.m_DebugEventQueueIdx + 1) % DEBUG_EVENTQUEUE_SIZE; |
| 7877 | } |
| 7878 | |
| 7879 | void CordbProcess::QueueUnmanagedEvent(CordbUnmanagedThread *pUThread, const DEBUG_EVENT *pEvent) |
| 7880 | { |
| 7881 | INTERNAL_API_ENTRY(this); |
| 7882 | _ASSERTE(ThreadHoldsProcessLock()); |
| 7883 | _ASSERTE(m_pShim != NULL); |
| 7884 | |
| 7885 | LOG((LF_CORDB, LL_INFO10000, "CP::QUE: queued unmanaged event %d for thread 0x%x\n" , |
| 7886 | pEvent->dwDebugEventCode, pUThread->m_id)); |
| 7887 | |
| 7888 | |
| 7889 | _ASSERTE(pEvent->dwDebugEventCode == EXCEPTION_DEBUG_EVENT); |
| 7890 | |
| 7891 | // Copy the event into the given thread |
| 7892 | CordbUnmanagedEvent *ue; |
| 7893 | |
| 7894 | // Use the primary IB event slot unless this is the special stack overflow event case. |
| 7895 | if (!pUThread->HasSpecialStackOverflowCase()) |
| 7896 | ue = pUThread->IBEvent(); |
| 7897 | else |
| 7898 | ue = pUThread->IBEvent2(); |
| 7899 | |
| 7900 | if(pUThread->HasIBEvent() && !pUThread->HasSpecialStackOverflowCase()) |
| 7901 | { |
| 7902 | // Any event being replaced should at least have been continued outside of the hijack |
| 7903 | // We don't track whether or not we expect the exception to retrigger but if we are replacing |
| 7904 | // the event then it did not. |
| 7905 | _ASSERTE(ue->IsEventContinuedUnhijacked()); |
| 7906 | LOG((LF_CORDB, LL_INFO10000, "CP::QUE: A previously seen event is being discarded 0x%x 0x%p\n" , |
| 7907 | ue->m_currentDebugEvent.u.Exception.ExceptionRecord.ExceptionCode, |
| 7908 | ue->m_currentDebugEvent.u.Exception.ExceptionRecord.ExceptionAddress)); |
| 7909 | DequeueUnmanagedEvent(ue->m_owner); |
| 7910 | } |
| 7911 | |
| 7912 | memcpy(&(ue->m_currentDebugEvent), pEvent, sizeof(DEBUG_EVENT)); |
| 7913 | ue->m_state = CUES_IsIBEvent; |
| 7914 | ue->m_next = NULL; |
| 7915 | |
| 7916 | // Enqueue the event. |
| 7917 | pUThread->SetState(CUTS_HasIBEvent); |
| 7918 | |
| 7919 | if (m_unmanagedEventQueue == NULL) |
| 7920 | m_unmanagedEventQueue = ue; |
| 7921 | else |
| 7922 | m_lastQueuedUnmanagedEvent->m_next = ue; |
| 7923 | |
| 7924 | m_lastQueuedUnmanagedEvent = ue; |
| 7925 | } |
| 7926 | |
| 7927 | void CordbProcess::DequeueUnmanagedEvent(CordbUnmanagedThread *ut) |
| 7928 | { |
| 7929 | INTERNAL_API_ENTRY(this); |
| 7930 | |
| 7931 | _ASSERTE(m_unmanagedEventQueue != NULL); |
| 7932 | _ASSERTE(ut->HasIBEvent() || ut->HasSpecialStackOverflowCase()); |
| 7933 | _ASSERTE(ThreadHoldsProcessLock()); |
| 7934 | |
| 7935 | |
| 7936 | CordbUnmanagedEvent *ue; |
| 7937 | |
| 7938 | if (ut->HasIBEvent()) |
| 7939 | ue = ut->IBEvent(); |
| 7940 | else |
| 7941 | { |
| 7942 | ue = ut->IBEvent2(); |
| 7943 | |
| 7944 | // Since we're dequeuing the special stack overflow event, we're no longer in the special stack overflow case. |
| 7945 | ut->ClearState(CUTS_HasSpecialStackOverflowCase); |
| 7946 | } |
| 7947 | |
| 7948 | DWORD ec = ue->m_currentDebugEvent.dwDebugEventCode; |
| 7949 | LOG((LF_CORDB, LL_INFO10000, "CP::DUE: dequeue unmanaged event %d for thread 0x%x\n" , ec, ut->m_id)); |
| 7950 | |
| 7951 | _ASSERTE(ec == EXCEPTION_DEBUG_EVENT); |
| 7952 | |
| 7953 | CordbUnmanagedEvent **tmp = &m_unmanagedEventQueue; |
| 7954 | CordbUnmanagedEvent **prev = NULL; |
| 7955 | |
| 7956 | // Note: this supports out-of-order dequeing of unmanaged events. This is necessary because we queue events even if |
| 7957 | // we're not clear on the ownership question. When we get the answer, and if the event belongs to the Runtime, we go |
| 7958 | // ahead and yank the event out of the queue, wherever it may be. |
| 7959 | while (*tmp && *tmp != ue) |
| 7960 | { |
| 7961 | prev = tmp; |
| 7962 | tmp = &((*tmp)->m_next); |
| 7963 | } |
| 7964 | |
| 7965 | _ASSERTE(*tmp == ue); |
| 7966 | |
| 7967 | *tmp = (*tmp)->m_next; |
| 7968 | |
| 7969 | if (m_unmanagedEventQueue == NULL) |
| 7970 | m_lastQueuedUnmanagedEvent = NULL; |
| 7971 | else if (m_lastQueuedUnmanagedEvent == ue) |
| 7972 | { |
| 7973 | _ASSERTE(prev != NULL); |
| 7974 | m_lastQueuedUnmanagedEvent = *prev; |
| 7975 | } |
| 7976 | |
| 7977 | ut->ClearState(CUTS_HasIBEvent); |
| 7978 | |
| 7979 | } |
| 7980 | |
| 7981 | void CordbProcess::QueueOOBUnmanagedEvent(CordbUnmanagedThread *pUThread, const DEBUG_EVENT * pEvent) |
| 7982 | { |
| 7983 | INTERNAL_API_ENTRY(this); |
| 7984 | _ASSERTE(ThreadHoldsProcessLock()); |
| 7985 | _ASSERTE(!pUThread->HasOOBEvent()); |
| 7986 | _ASSERTE(IsWin32EventThread()); |
| 7987 | _ASSERTE(m_pShim != NULL); |
| 7988 | |
| 7989 | LOG((LF_CORDB, LL_INFO10000, "CP::QUE: queued OOB unmanaged event %d for thread 0x%x\n" , |
| 7990 | pEvent->dwDebugEventCode, pUThread->m_id)); |
| 7991 | |
| 7992 | // Copy the event into the given thread |
| 7993 | CordbUnmanagedEvent *ue = pUThread->OOBEvent(); |
| 7994 | memcpy(&(ue->m_currentDebugEvent), pEvent, sizeof(DEBUG_EVENT)); |
| 7995 | ue->m_state = CUES_None; |
| 7996 | ue->m_next = NULL; |
| 7997 | |
| 7998 | // Enqueue the event. |
| 7999 | pUThread->SetState(CUTS_HasOOBEvent); |
| 8000 | |
| 8001 | if (m_outOfBandEventQueue == NULL) |
| 8002 | m_outOfBandEventQueue = ue; |
| 8003 | else |
| 8004 | m_lastQueuedOOBEvent->m_next = ue; |
| 8005 | |
| 8006 | m_lastQueuedOOBEvent = ue; |
| 8007 | } |
| 8008 | |
| 8009 | void CordbProcess::DequeueOOBUnmanagedEvent(CordbUnmanagedThread *ut) |
| 8010 | { |
| 8011 | INTERNAL_API_ENTRY(this); |
| 8012 | _ASSERTE(m_outOfBandEventQueue != NULL); |
| 8013 | _ASSERTE(ut->HasOOBEvent()); |
| 8014 | _ASSERTE(ThreadHoldsProcessLock()); |
| 8015 | |
| 8016 | CordbUnmanagedEvent *ue = ut->OOBEvent(); |
| 8017 | DWORD ec = ue->m_currentDebugEvent.dwDebugEventCode; |
| 8018 | |
| 8019 | LOG((LF_CORDB, LL_INFO10000, "CP::DUE: dequeue OOB unmanaged event %d for thread 0x%x\n" , ec, ut->m_id)); |
| 8020 | |
| 8021 | CordbUnmanagedEvent **tmp = &m_outOfBandEventQueue; |
| 8022 | CordbUnmanagedEvent **prev = NULL; |
| 8023 | |
| 8024 | // Note: this supports out-of-order dequeing of unmanaged events. This is necessary because we queue events even if |
| 8025 | // we're not clear on the ownership question. When we get the answer, and if the event belongs to the Runtime, we go |
| 8026 | // ahead and yank the event out of the queue, wherever it may be. |
| 8027 | while (*tmp && *tmp != ue) |
| 8028 | { |
| 8029 | prev = tmp; |
| 8030 | tmp = &((*tmp)->m_next); |
| 8031 | } |
| 8032 | |
| 8033 | _ASSERTE(*tmp == ue); |
| 8034 | |
| 8035 | *tmp = (*tmp)->m_next; |
| 8036 | |
| 8037 | if (m_outOfBandEventQueue == NULL) |
| 8038 | m_lastQueuedOOBEvent = NULL; |
| 8039 | else if (m_lastQueuedOOBEvent == ue) |
| 8040 | { |
| 8041 | _ASSERTE(prev != NULL); |
| 8042 | m_lastQueuedOOBEvent = *prev; |
| 8043 | } |
| 8044 | |
| 8045 | ut->ClearState(CUTS_HasOOBEvent); |
| 8046 | } |
| 8047 | |
| 8048 | HRESULT CordbProcess::SuspendUnmanagedThreads() |
| 8049 | { |
| 8050 | INTERNAL_API_ENTRY(this); |
| 8051 | |
| 8052 | _ASSERTE(ThreadHoldsProcessLock()); |
| 8053 | |
| 8054 | // Iterate over all unmanaged threads... |
| 8055 | CordbUnmanagedThread* ut; |
| 8056 | HASHFIND find; |
| 8057 | |
| 8058 | for (ut = m_unmanagedThreads.FindFirst(&find); ut != NULL; ut = m_unmanagedThreads.FindNext(&find)) |
| 8059 | { |
| 8060 | |
| 8061 | // Don't suspend any thread in a can't stop region. This includes cooperative mode threads & preemptive |
| 8062 | // threads that haven't pushed a NativeTransitionFrame. The ultimate problem here is that a thread |
| 8063 | // in this state is effectively inside the runtime, and thus may take a lock that blocks the helper thread. |
| 8064 | // IsCan'tStop also includes the helper thread & hijacked threads - which we shouldn't suspend anyways. |
| 8065 | |
| 8066 | // Only suspend those unmanaged threads that aren't already suspended by us and that aren't already hijacked by |
| 8067 | // us. |
| 8068 | |
| 8069 | if (!ut->IsSuspended() && |
| 8070 | !ut->IsDeleted() && |
| 8071 | !ut->IsCantStop() && |
| 8072 | !ut->IsBlockingForSync() |
| 8073 | ) |
| 8074 | { |
| 8075 | LOG((LF_CORDB, LL_INFO1000, "CP::SUT: suspending unmanaged thread 0x%x, handle 0x%x\n" , ut->m_id, ut->m_handle)); |
| 8076 | |
| 8077 | DWORD succ = SuspendThread(ut->m_handle); |
| 8078 | |
| 8079 | if (succ == 0xFFFFFFFF) |
| 8080 | { |
| 8081 | // This is okay... the thread may be dying after an ExitThread event. |
| 8082 | LOG((LF_CORDB, LL_INFO1000, "CP::SUT: failed to suspend thread 0x%x\n" , ut->m_id)); |
| 8083 | } |
| 8084 | else |
| 8085 | { |
| 8086 | m_state |= PS_SOME_THREADS_SUSPENDED; |
| 8087 | |
| 8088 | ut->SetState(CUTS_Suspended); |
| 8089 | } |
| 8090 | } |
| 8091 | } |
| 8092 | |
| 8093 | return S_OK; |
| 8094 | } |
| 8095 | |
| 8096 | HRESULT CordbProcess::ResumeUnmanagedThreads() |
| 8097 | { |
| 8098 | INTERNAL_API_ENTRY(this); |
| 8099 | _ASSERTE(ThreadHoldsProcessLock()); |
| 8100 | FAIL_IF_NEUTERED(this); |
| 8101 | |
| 8102 | // Iterate over all unmanaged threads... |
| 8103 | CordbUnmanagedThread* ut; |
| 8104 | HASHFIND find; |
| 8105 | |
| 8106 | for (ut = m_unmanagedThreads.FindFirst(&find); ut != NULL; ut = m_unmanagedThreads.FindNext(&find)) |
| 8107 | { |
| 8108 | // Only resume those unmanaged threads that were suspended by us. |
| 8109 | if (ut->IsSuspended()) |
| 8110 | { |
| 8111 | LOG((LF_CORDB, LL_INFO1000, "CP::RUT: resuming unmanaged thread 0x%x\n" , ut->m_id)); |
| 8112 | |
| 8113 | DWORD succ = ResumeThread(ut->m_handle); |
| 8114 | |
| 8115 | if (succ == 0xFFFFFFFF) |
| 8116 | { |
| 8117 | LOG((LF_CORDB, LL_INFO1000, "CP::RUT: failed to resume thread 0x%x\n" , ut->m_id)); |
| 8118 | } |
| 8119 | else |
| 8120 | ut->ClearState(CUTS_Suspended); |
| 8121 | } |
| 8122 | } |
| 8123 | |
| 8124 | m_state &= ~PS_SOME_THREADS_SUSPENDED; |
| 8125 | |
| 8126 | return S_OK; |
| 8127 | } |
| 8128 | |
| 8129 | //----------------------------------------------------------------------------- |
| 8130 | // DispatchUnmanagedInBandEvent |
| 8131 | // |
| 8132 | // Handler for Win32 events already known to be Unmanaged and in-band. |
| 8133 | //----------------------------------------------------------------------------- |
| 8134 | void CordbProcess::DispatchUnmanagedInBandEvent() |
| 8135 | { |
| 8136 | INTERNAL_API_ENTRY(this); |
| 8137 | _ASSERTE(ThreadHoldsProcessLock()); |
| 8138 | |
| 8139 | // There should be no queued OOB events!!! If there are, then we have a breakdown in our protocol, since all OOB |
| 8140 | // events should be dispatched before attempting to really continue from any in-band event. |
| 8141 | _ASSERTE(m_outOfBandEventQueue == NULL); |
| 8142 | _ASSERTE(m_cordb != NULL); |
| 8143 | _ASSERTE(m_cordb->m_unmanagedCallback != NULL); |
| 8144 | _ASSERTE(!m_dispatchingUnmanagedEvent); |
| 8145 | |
| 8146 | CordbUnmanagedThread * pUnmanagedThread = NULL; |
| 8147 | CordbUnmanagedEvent * pUnmanagedEvent = m_unmanagedEventQueue; |
| 8148 | |
| 8149 | while (true) |
| 8150 | { |
| 8151 | // get the next queued event that isn't dispatched yet |
| 8152 | while(pUnmanagedEvent != NULL && pUnmanagedEvent->IsDispatched()) |
| 8153 | { |
| 8154 | pUnmanagedEvent = pUnmanagedEvent->m_next; |
| 8155 | } |
| 8156 | |
| 8157 | if(pUnmanagedEvent == NULL) |
| 8158 | break; |
| 8159 | |
| 8160 | // Get the thread for this event |
| 8161 | _ASSERTE(pUnmanagedThread == NULL); |
| 8162 | pUnmanagedThread = pUnmanagedEvent->m_owner; |
| 8163 | _ASSERTE(pUnmanagedThread != NULL); |
| 8164 | |
| 8165 | // We better not have dispatched it yet! |
| 8166 | _ASSERTE(!pUnmanagedEvent->IsDispatched()); |
| 8167 | |
| 8168 | // We shouldn't be dispatching IB events on a thread that has exited. |
| 8169 | // Though it's possible that the thread may exit *after* the IB event has been dispatched |
| 8170 | // if it gets hijacked. |
| 8171 | _ASSERTE(!pUnmanagedThread->IsDeleted()); |
| 8172 | |
| 8173 | // Make sure we keep the thread alive while we're playing with it. |
| 8174 | pUnmanagedThread->InternalAddRef(); |
| 8175 | |
| 8176 | LOG((LF_CORDB, LL_INFO10000, "CP::DUE: dispatching unmanaged event %d for thread 0x%x\n" , |
| 8177 | pUnmanagedEvent->m_currentDebugEvent.dwDebugEventCode, pUnmanagedThread->m_id)); |
| 8178 | |
| 8179 | m_dispatchingUnmanagedEvent = true; |
| 8180 | |
| 8181 | // Add/Remove a reference which is scoped to the time that m_lastDispatchedIBEvent |
| 8182 | // is set to pUnmanagedEvent (it is an interior pointer) |
| 8183 | // see DevDiv issue 818301 for more details |
| 8184 | if(m_lastDispatchedIBEvent != NULL) |
| 8185 | { |
| 8186 | m_lastDispatchedIBEvent->m_owner->InternalRelease(); |
| 8187 | m_lastDispatchedIBEvent = NULL; |
| 8188 | } |
| 8189 | pUnmanagedThread->InternalAddRef(); |
| 8190 | m_lastDispatchedIBEvent = pUnmanagedEvent; |
| 8191 | pUnmanagedEvent->SetState(CUES_Dispatched); |
| 8192 | |
| 8193 | IncStopCount(); |
| 8194 | |
| 8195 | Unlock(); |
| 8196 | |
| 8197 | { |
| 8198 | // Interface is semantically const, but does not include const in signature. |
| 8199 | DEBUG_EVENT * pEvent = const_cast<DEBUG_EVENT *> (&(pUnmanagedEvent->m_currentDebugEvent)); |
| 8200 | PUBLIC_WIN32_CALLBACK_IN_THIS_SCOPE(this,pEvent, FALSE); |
| 8201 | m_cordb->m_unmanagedCallback->DebugEvent(pEvent, FALSE); |
| 8202 | } |
| 8203 | |
| 8204 | Lock(); |
| 8205 | |
| 8206 | // Calling IMDA::Continue() will set m_dispatchingUnmanagedEvent = false. |
| 8207 | // So if Continue() was called && we have more events, we'll loop and dispatch more events. |
| 8208 | // Else we'll break out of the while loop. |
| 8209 | if(m_dispatchingUnmanagedEvent) |
| 8210 | break; |
| 8211 | |
| 8212 | // Continue was called in the dispatch callback, but that continue path just |
| 8213 | // clears the dispatch flag and returns. The continue right here is the logical |
| 8214 | // completion of the user's continue request |
| 8215 | // Note it is sometimes the case that these events have already been continued because |
| 8216 | // they had defered dispatching. At the time of deferal they were immediately continued. |
| 8217 | // If the event is already continued then this continue becomes a no-op. |
| 8218 | m_pShim->GetWin32EventThread()->DoDbgContinue(this, pUnmanagedEvent); |
| 8219 | |
| 8220 | // Release our reference to the unmanaged thread that we dispatched |
| 8221 | // This event should have been continued long ago... |
| 8222 | _ASSERTE(!pUnmanagedThread->IBEvent()->IsEventWaitingForContinue()); |
| 8223 | pUnmanagedThread->InternalRelease(); |
| 8224 | pUnmanagedThread = NULL; |
| 8225 | } |
| 8226 | |
| 8227 | m_dispatchingUnmanagedEvent = false; |
| 8228 | |
| 8229 | // Release our reference to the last thread that we dispatched now... |
| 8230 | if(pUnmanagedThread) |
| 8231 | { |
| 8232 | pUnmanagedThread->InternalRelease(); |
| 8233 | pUnmanagedThread = NULL; |
| 8234 | } |
| 8235 | } |
| 8236 | |
| 8237 | //----------------------------------------------------------------------------- |
| 8238 | // DispatchUnmanagedOOBEvent |
| 8239 | // |
| 8240 | // Handler for Win32 events already known to be Unmanaged and out-of-band. |
| 8241 | //----------------------------------------------------------------------------- |
| 8242 | void CordbProcess::DispatchUnmanagedOOBEvent() |
| 8243 | { |
| 8244 | INTERNAL_API_ENTRY(this); |
| 8245 | _ASSERTE(ThreadHoldsProcessLock()); |
| 8246 | _ASSERTE(IsWin32EventThread()); |
| 8247 | |
| 8248 | // There should be OOB events queued... |
| 8249 | _ASSERTE(m_outOfBandEventQueue != NULL); |
| 8250 | _ASSERTE(m_cordb->m_unmanagedCallback != NULL); |
| 8251 | |
| 8252 | do |
| 8253 | { |
| 8254 | // Get the first event in the OOB Queue... |
| 8255 | CordbUnmanagedEvent * pUnmanagedEvent = m_outOfBandEventQueue; |
| 8256 | CordbUnmanagedThread * pUnmanagedThread = pUnmanagedEvent->m_owner; |
| 8257 | |
| 8258 | // Make sure we keep the thread alive while we're playing with it. |
| 8259 | RSSmartPtr<CordbUnmanagedThread> pRef(pUnmanagedThread); |
| 8260 | |
| 8261 | LOG((LF_CORDB, LL_INFO10000, "[%x] CP::DUE: dispatching OOB unmanaged event %d for thread 0x%x\n" , |
| 8262 | GetCurrentThreadId(), pUnmanagedEvent->m_currentDebugEvent.dwDebugEventCode, pUnmanagedThread->m_id)); |
| 8263 | |
| 8264 | m_dispatchingOOBEvent = true; |
| 8265 | pUnmanagedEvent->SetState(CUES_Dispatched); |
| 8266 | Unlock(); |
| 8267 | |
| 8268 | { |
| 8269 | // Interface is semantically const, but does not include const in signature. |
| 8270 | DEBUG_EVENT * pEvent = const_cast<DEBUG_EVENT *> (&(pUnmanagedEvent->m_currentDebugEvent)); |
| 8271 | PUBLIC_WIN32_CALLBACK_IN_THIS_SCOPE(this, pEvent, TRUE); |
| 8272 | m_cordb->m_unmanagedCallback->DebugEvent(pEvent, TRUE); |
| 8273 | } |
| 8274 | |
| 8275 | Lock(); |
| 8276 | |
| 8277 | // If they called Continue from the callback, then continue the OOB event right now before dispatching the next |
| 8278 | // one. |
| 8279 | if (!m_dispatchingOOBEvent) |
| 8280 | { |
| 8281 | DequeueOOBUnmanagedEvent(pUnmanagedThread); |
| 8282 | |
| 8283 | // Should not have continued from this debug event yet. |
| 8284 | _ASSERTE(pUnmanagedEvent->IsEventWaitingForContinue()); |
| 8285 | |
| 8286 | // Do a little extra work if that was an OOB exception event... |
| 8287 | HRESULT hr = pUnmanagedEvent->m_owner->FixupAfterOOBException(pUnmanagedEvent); |
| 8288 | _ASSERTE(SUCCEEDED(hr)); |
| 8289 | |
| 8290 | // Go ahead and continue now... |
| 8291 | this->m_pShim->GetWin32EventThread()->DoDbgContinue(this, pUnmanagedEvent); |
| 8292 | } |
| 8293 | |
| 8294 | // Implicit release of pUnmanagedThread via pRef |
| 8295 | } |
| 8296 | while (!m_dispatchingOOBEvent && (m_outOfBandEventQueue != NULL)); |
| 8297 | |
| 8298 | m_dispatchingOOBEvent = false; |
| 8299 | |
| 8300 | LOG((LF_CORDB, LL_INFO10000, "CP::DUE: done dispatching OOB events. Queue=0x%08x\n" , m_outOfBandEventQueue)); |
| 8301 | } |
| 8302 | #endif // FEATURE_INTEROP_DEBUGGING |
| 8303 | |
| 8304 | //----------------------------------------------------------------------------- |
| 8305 | // StartSyncFromWin32Stop |
| 8306 | // |
| 8307 | // Get the process from a Fozen state or a Live state to a Synchronized State. |
| 8308 | // Note that Process Exit is considered to be synchronized. |
| 8309 | // This is a nop if we're not Interop Debugging. |
| 8310 | // If this function succeeds, we're in a synchronized state. |
| 8311 | // |
| 8312 | // Arguments: |
| 8313 | // pfAsyncBreakSent - returns if this method sent an async-break or not. |
| 8314 | // |
| 8315 | // Return value: |
| 8316 | // typical HRESULT return values, nothing sinister here. |
| 8317 | //----------------------------------------------------------------------------- |
| 8318 | HRESULT CordbProcess::StartSyncFromWin32Stop(BOOL * pfAsyncBreakSent) |
| 8319 | { |
| 8320 | INTERNAL_API_ENTRY(this); |
| 8321 | if (m_pShim == NULL) // This API is moved off to the shim |
| 8322 | { |
| 8323 | return E_NOTIMPL; |
| 8324 | } |
| 8325 | |
| 8326 | HRESULT hr = S_OK; |
| 8327 | |
| 8328 | // Caller should have taken the stop-go lock. This prevents us from racing w/ a continue. |
| 8329 | _ASSERTE(m_StopGoLock.HasLock()); |
| 8330 | |
| 8331 | // Process should be init before we try to sync it. |
| 8332 | _ASSERTE(this->m_initialized); |
| 8333 | |
| 8334 | // If nobody's listening for an AsyncBreak, and we're not stopped, then our caller |
| 8335 | // doesn't know if we're sending an AsyncBreak or not; and thus we may not continue. |
| 8336 | // Failing this assert means that we're stopping but we don't think we're going to get a continue |
| 8337 | // down the road, and thus we're headed for a deadlock. |
| 8338 | _ASSERTE((pfAsyncBreakSent != NULL) || (m_stopCount > 0)); |
| 8339 | |
| 8340 | if (pfAsyncBreakSent) |
| 8341 | { |
| 8342 | *pfAsyncBreakSent = FALSE; |
| 8343 | } |
| 8344 | |
| 8345 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 8346 | |
| 8347 | // If we're win32 stopped (but not out-of-band win32 stopped), or if we're running free on the Left Side but we're |
| 8348 | // just not synchronized (and we're win32 attached), then go ahead and do an internal continue and send an async |
| 8349 | // break event to get the Left Side sync'd up. |
| 8350 | // |
| 8351 | // The process can be running free as far as Win32 events are concerned, but still not synchronized as far as the |
| 8352 | // Runtime is concerned. This can happen in a lot of cases where we end up with the Runtime not sync'd but with the |
| 8353 | // process running free due to hijacking, etc... |
| 8354 | if (((m_state & CordbProcess::PS_WIN32_STOPPED) && (m_outOfBandEventQueue == NULL)) || |
| 8355 | (!GetSynchronized() && IsInteropDebugging())) |
| 8356 | { |
| 8357 | Lock(); |
| 8358 | |
| 8359 | if (((m_state & CordbProcess::PS_WIN32_STOPPED) && (m_outOfBandEventQueue == NULL)) || |
| 8360 | (!GetSynchronized() && IsInteropDebugging())) |
| 8361 | { |
| 8362 | // This can't be the win32 ET b/c we need that thread to be alive and pumping win32 DE so that |
| 8363 | // our Async Break can get across. |
| 8364 | // So nobody should ever be calling this on the w32 ET. But they could, since we do trickle in from |
| 8365 | // outside APIs. So we need a retail check. |
| 8366 | if (IsWin32EventThread()) |
| 8367 | { |
| 8368 | _ASSERTE(!"Don't call this API on the W32 Event Thread" ); |
| 8369 | |
| 8370 | Unlock(); |
| 8371 | return ErrWrapper(CORDBG_E_CANT_CALL_ON_THIS_THREAD); |
| 8372 | } |
| 8373 | |
| 8374 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "[%x] CP::SSFW32S: sending internal continue\n" , GetCurrentThreadId()); |
| 8375 | |
| 8376 | // Can't do this on the win32 event thread. |
| 8377 | _ASSERTE(!this->IsWin32EventThread()); |
| 8378 | |
| 8379 | // If the helper thread is already dead, then we just return as if we sync'd the process. |
| 8380 | if (m_helperThreadDead) |
| 8381 | { |
| 8382 | if (pfAsyncBreakSent) |
| 8383 | { |
| 8384 | *pfAsyncBreakSent = TRUE; |
| 8385 | } |
| 8386 | |
| 8387 | // Mark the process as synchronized so no events will be dispatched until the thing is |
| 8388 | // continued. However, the marking here is not a usual marking for synchronized. It has special |
| 8389 | // semantics when we're interop debugging. We use m_oddSync to remember this so that we can take special |
| 8390 | // action in Continue(). |
| 8391 | SetSynchronized(true); |
| 8392 | m_oddSync = true; |
| 8393 | |
| 8394 | // Get the RC Event Thread to stop listening to the process. |
| 8395 | m_cordb->ProcessStateChanged(); |
| 8396 | |
| 8397 | Unlock(); |
| 8398 | |
| 8399 | return S_OK; |
| 8400 | } |
| 8401 | |
| 8402 | m_stopRequested = true; |
| 8403 | |
| 8404 | // See ::Stop for why we defer this. The delayed events will be dispatched when some one calls continue. |
| 8405 | // And we know they'll call continue b/c (stopCount > 0) || (our caller knows we're sending an AsyncBreak). |
| 8406 | m_specialDeferment = true; |
| 8407 | |
| 8408 | Unlock(); |
| 8409 | |
| 8410 | // If the process gets synchronized between the Unlock() and here, then SendUnmanagedContinue() will end up |
| 8411 | // not doing anything at all since a) it holds the process lock when working and b) it gates everything on |
| 8412 | // if the process is sync'd or not. This is exactly what we want. |
| 8413 | hr = this->m_pShim->GetWin32EventThread()->SendUnmanagedContinue(this, cInternalUMContinue); |
| 8414 | |
| 8415 | LOG((LF_CORDB, LL_INFO1000, "[%x] CP::SSFW32S: internal continue returned\n" , GetCurrentThreadId())); |
| 8416 | |
| 8417 | // Send an async break to the left side now that its running. |
| 8418 | DebuggerIPCEvent * pEvent = (DebuggerIPCEvent *) _alloca(CorDBIPC_BUFFER_SIZE); |
| 8419 | InitIPCEvent(pEvent, DB_IPCE_ASYNC_BREAK, false, VMPTR_AppDomain::NullPtr()); |
| 8420 | |
| 8421 | LOG((LF_CORDB, LL_INFO1000, "[%x] CP::SSFW32S: sending async stop\n" , GetCurrentThreadId())); |
| 8422 | |
| 8423 | // If the process gets synchronized between the Unlock() and here, then this message will do nothing (Left |
| 8424 | // Side catches it) and we'll never get a response, and it won't hurt anything. |
| 8425 | hr = m_cordb->SendIPCEvent(this, pEvent, CorDBIPC_BUFFER_SIZE); |
| 8426 | // @Todo- how do we handle a failure here? |
| 8427 | |
| 8428 | // If the send returns with the helper thread being dead, then we know we don't need to wait for the process |
| 8429 | // to sync. |
| 8430 | if (!m_helperThreadDead) |
| 8431 | { |
| 8432 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "[%x] CP::SSFW32S: sent async stop, waiting for event\n" , GetCurrentThreadId()); |
| 8433 | |
| 8434 | // If we got synchronized between the Unlock() and here its okay since m_stopWaitEvent is still high |
| 8435 | // from the last sync. |
| 8436 | DWORD dwWaitResult = SafeWaitForSingleObject(this, m_stopWaitEvent, INFINITE); |
| 8437 | |
| 8438 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "[%x] CP::SSFW32S: got event, %d\n" , GetCurrentThreadId(), dwWaitResult); |
| 8439 | |
| 8440 | _ASSERTE(dwWaitResult == WAIT_OBJECT_0); |
| 8441 | } |
| 8442 | |
| 8443 | Lock(); |
| 8444 | |
| 8445 | m_specialDeferment = false; |
| 8446 | |
| 8447 | if (pfAsyncBreakSent) |
| 8448 | { |
| 8449 | *pfAsyncBreakSent = TRUE; |
| 8450 | } |
| 8451 | |
| 8452 | // If the helper thread died while we were trying to send an event to it, then we just do the same odd sync |
| 8453 | // logic we do above. |
| 8454 | if (m_helperThreadDead) |
| 8455 | { |
| 8456 | SetSynchronized(true); |
| 8457 | m_oddSync = true; |
| 8458 | hr = S_OK; |
| 8459 | } |
| 8460 | |
| 8461 | m_stopRequested = false; |
| 8462 | m_cordb->ProcessStateChanged(); |
| 8463 | } |
| 8464 | |
| 8465 | Unlock(); |
| 8466 | } |
| 8467 | #endif // FEATURE_INTEROP_DEBUGGING |
| 8468 | |
| 8469 | return hr; |
| 8470 | } |
| 8471 | |
| 8472 | // Check if the left side has exited. If so, get the right-side |
| 8473 | // into shutdown mode. Only use this to avert us from going into |
| 8474 | // an unrecoverable error. |
| 8475 | bool CordbProcess::CheckIfLSExited() |
| 8476 | { |
| 8477 | // Check by waiting on the handle with no timeout. |
| 8478 | if (WaitForSingleObject(m_handle, 0) == WAIT_OBJECT_0) |
| 8479 | { |
| 8480 | Lock(); |
| 8481 | m_terminated = true; |
| 8482 | m_exiting = true; |
| 8483 | Unlock(); |
| 8484 | } |
| 8485 | |
| 8486 | LOG((LF_CORDB, LL_INFO10, "CP::IsLSExited() returning '%s'\n" , |
| 8487 | m_exiting ? "true" : "false" )); |
| 8488 | |
| 8489 | return m_exiting; |
| 8490 | } |
| 8491 | |
| 8492 | // Call this if something really bad happened and we can't do |
| 8493 | // anything meaningful with the CordbProcess. |
| 8494 | void CordbProcess::UnrecoverableError(HRESULT errorHR, |
| 8495 | unsigned int errorCode, |
| 8496 | const char *errorFile, |
| 8497 | unsigned int errorLine) |
| 8498 | { |
| 8499 | LOG((LF_CORDB, LL_INFO10, "[%x] CP::UE: unrecoverable error 0x%08x " |
| 8500 | "(%d) %s:%d\n" , |
| 8501 | GetCurrentThreadId(), |
| 8502 | errorHR, errorCode, errorFile, errorLine)); |
| 8503 | |
| 8504 | // We definitely want to know about any of these. |
| 8505 | STRESS_LOG3(LF_CORDB, LL_EVERYTHING, "Unrecoverable Error:0x%08x, File=%s, line=%d\n" , errorHR, errorFile, errorLine); |
| 8506 | |
| 8507 | // It's possible for an unrecoverable error to occur if the user detaches the |
| 8508 | // debugger while inside CordbProcess::DispatchRCEvent() (as that function deliberately |
| 8509 | // calls Unlock() while calling into the Shim). Detect such cases here & bail before we |
| 8510 | // try to access invalid fields on this CordbProcess. |
| 8511 | // |
| 8512 | // Normally, we'd need to take the cordb process lock around the IsNeutered check |
| 8513 | // (and the code that follows). And perhaps this is a good thing to do in the |
| 8514 | // future. But for now we're not for two reasons: |
| 8515 | // |
| 8516 | // 1) It's scary. We're in UnrecoverableError() for gosh sake. I don't know all |
| 8517 | // the possible bad states we can be in to get here. Will taking the process lock |
| 8518 | // have ordering issues? Will the process lock even be valid to take here (or might |
| 8519 | // we AV)? Since this is error handling, we should probably be as light as we can |
| 8520 | // not to cause more errors. |
| 8521 | // |
| 8522 | // 2) It's unnecessary. For the Watson dump I investigated that caused this fix in |
| 8523 | // the first place, we already detached before entering UnrecoverableError() |
| 8524 | // (indeed, the only reason we're in UnrecoverableError is that we already detached |
| 8525 | // and that caused a prior API to fail). Thus, there's no timing issue (in that |
| 8526 | // case, anyway), wrt to entering UnrecoverableError() and detaching / neutering. |
| 8527 | if (IsNeutered()) |
| 8528 | return; |
| 8529 | |
| 8530 | #ifdef _DEBUG |
| 8531 | // Ping our error trapping logic |
| 8532 | HRESULT hrDummy; |
| 8533 | hrDummy = ErrWrapper(errorHR); |
| 8534 | #endif |
| 8535 | |
| 8536 | if (m_pShim == NULL) |
| 8537 | { |
| 8538 | // @dbgtodo - , shim: Once everything is hoisted, we can remove |
| 8539 | // this code. |
| 8540 | // In the v3 case, we should never get an unrecoverable error. Instead, the HR should be propogated |
| 8541 | // and returned at the top-level public API. |
| 8542 | _ASSERTE(!"Unrecoverable error dispatched in V3 case." ); |
| 8543 | } |
| 8544 | |
| 8545 | CONSISTENCY_CHECK_MSGF(IsLegalFatalError(errorHR), ("Unrecoverable internal error: hr=0x%08x!" , errorHR)); |
| 8546 | |
| 8547 | if (!IsLegalFatalError(errorHR) || (errorHR != CORDBG_E_CANNOT_DEBUG_FIBER_PROCESS)) |
| 8548 | { |
| 8549 | // This will throw everything into a Zombie state. The ATT_ macros will check this and fail immediately. |
| 8550 | m_unrecoverableError = true; |
| 8551 | |
| 8552 | // |
| 8553 | // Mark the process as no longer synchronized. |
| 8554 | // |
| 8555 | Lock(); |
| 8556 | SetSynchronized(false); |
| 8557 | IncStopCount(); |
| 8558 | Unlock(); |
| 8559 | } |
| 8560 | |
| 8561 | // Set the error flags in the process so that if parts of it are |
| 8562 | // still alive, it will realize that its in this mode and do the |
| 8563 | // right thing. |
| 8564 | if (GetDCB() != NULL) |
| 8565 | { |
| 8566 | GetDCB()->m_errorHR = errorHR; |
| 8567 | GetDCB()->m_errorCode = errorCode; |
| 8568 | EX_TRY |
| 8569 | { |
| 8570 | UpdateLeftSideDCBField(&(GetDCB()->m_errorHR), sizeof(GetDCB()->m_errorHR)); |
| 8571 | UpdateLeftSideDCBField(&(GetDCB()->m_errorCode), sizeof(GetDCB()->m_errorCode)); |
| 8572 | } |
| 8573 | EX_CATCH |
| 8574 | { |
| 8575 | _ASSERTE(!"Writing process memory failed, perhaps due to an unexpected disconnection from the target." ); |
| 8576 | } |
| 8577 | EX_END_CATCH(SwallowAllExceptions); |
| 8578 | } |
| 8579 | |
| 8580 | // |
| 8581 | // Let the user know that we've hit an unrecoverable error. |
| 8582 | // |
| 8583 | if (m_cordb->m_managedCallback) |
| 8584 | { |
| 8585 | // We are about to send DebuggerError call back. The state of RS is undefined. |
| 8586 | // So we use the special Public Callback. We may be holding locks and stuff. |
| 8587 | // We may also be deeply nested within the RS. |
| 8588 | PUBLIC_CALLBACK_IN_THIS_SCOPE_DEBUGGERERROR(this); |
| 8589 | m_cordb->m_managedCallback->DebuggerError((ICorDebugProcess*) this, |
| 8590 | errorHR, |
| 8591 | errorCode); |
| 8592 | } |
| 8593 | } |
| 8594 | |
| 8595 | |
| 8596 | HRESULT CordbProcess::CheckForUnrecoverableError() |
| 8597 | { |
| 8598 | HRESULT hr = S_OK; |
| 8599 | |
| 8600 | if (GetDCB() != NULL) |
| 8601 | { |
| 8602 | // be sure we have the latest information |
| 8603 | UpdateRightSideDCB(); |
| 8604 | |
| 8605 | if (GetDCB()->m_errorHR != S_OK) |
| 8606 | { |
| 8607 | UnrecoverableError(GetDCB()->m_errorHR, |
| 8608 | GetDCB()->m_errorCode, |
| 8609 | __FILE__, __LINE__); |
| 8610 | |
| 8611 | hr = GetDCB()->m_errorHR; |
| 8612 | } |
| 8613 | } |
| 8614 | |
| 8615 | return hr; |
| 8616 | } |
| 8617 | |
| 8618 | |
| 8619 | /* |
| 8620 | * EnableLogMessages enables/disables sending of log messages to the |
| 8621 | * debugger for logging. |
| 8622 | */ |
| 8623 | HRESULT CordbProcess::EnableLogMessages(BOOL fOnOff) |
| 8624 | { |
| 8625 | PUBLIC_API_ENTRY(this); |
| 8626 | FAIL_IF_NEUTERED(this); |
| 8627 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 8628 | HRESULT hr = S_OK; |
| 8629 | |
| 8630 | DebuggerIPCEvent *event = (DebuggerIPCEvent*) _alloca(CorDBIPC_BUFFER_SIZE); |
| 8631 | InitIPCEvent(event, DB_IPCE_ENABLE_LOG_MESSAGES, false, VMPTR_AppDomain::NullPtr()); |
| 8632 | event->LogSwitchSettingMessage.iLevel = (int)fOnOff; |
| 8633 | |
| 8634 | hr = m_cordb->SendIPCEvent(this, event, CorDBIPC_BUFFER_SIZE); |
| 8635 | hr = WORST_HR(hr, event->hr); |
| 8636 | |
| 8637 | LOG((LF_CORDB, LL_INFO10000, "[%x] CP::EnableLogMessages: EnableLogMessages=%d sent.\n" , |
| 8638 | GetCurrentThreadId(), fOnOff)); |
| 8639 | |
| 8640 | return hr; |
| 8641 | } |
| 8642 | |
| 8643 | /* |
| 8644 | * ModifyLogSwitch modifies the specified switch's severity level. |
| 8645 | */ |
| 8646 | COM_METHOD CordbProcess::ModifyLogSwitch(__in_z WCHAR *pLogSwitchName, LONG lLevel) |
| 8647 | { |
| 8648 | PUBLIC_API_ENTRY(this); |
| 8649 | FAIL_IF_NEUTERED(this); |
| 8650 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 8651 | |
| 8652 | HRESULT hr = S_OK; |
| 8653 | |
| 8654 | _ASSERTE (pLogSwitchName != NULL); |
| 8655 | |
| 8656 | DebuggerIPCEvent *event = (DebuggerIPCEvent*) _alloca(CorDBIPC_BUFFER_SIZE); |
| 8657 | InitIPCEvent(event, DB_IPCE_MODIFY_LOGSWITCH, false, VMPTR_AppDomain::NullPtr()); |
| 8658 | event->LogSwitchSettingMessage.iLevel = lLevel; |
| 8659 | event->LogSwitchSettingMessage.szSwitchName.SetStringTruncate(pLogSwitchName); |
| 8660 | |
| 8661 | hr = m_cordb->SendIPCEvent(this, event, CorDBIPC_BUFFER_SIZE); |
| 8662 | hr = WORST_HR(hr, event->hr); |
| 8663 | |
| 8664 | LOG((LF_CORDB, LL_INFO10000, "[%x] CP::ModifyLogSwitch: ModifyLogSwitch sent.\n" , |
| 8665 | GetCurrentThreadId())); |
| 8666 | |
| 8667 | return hr; |
| 8668 | } |
| 8669 | |
| 8670 | //----------------------------------------------------------------------------- |
| 8671 | // Writes a buffer from the target and performs checks similar to SafeWriteStruct |
| 8672 | // |
| 8673 | // Arguments: |
| 8674 | // tb - TargetBuffer which represents the target memory we want to write to |
| 8675 | // pLocalBuffer - local pointer into source buffer |
| 8676 | // cbSize - the size of local buffer |
| 8677 | // |
| 8678 | // Exceptions |
| 8679 | // On error throws the result of WriteVirtual unless a short write is performed, |
| 8680 | // in which case throws ERROR_PARTIAL_COPY |
| 8681 | // |
| 8682 | void CordbProcess::SafeWriteBuffer(TargetBuffer tb, |
| 8683 | const BYTE * pLocalBuffer) |
| 8684 | { |
| 8685 | _ASSERTE(m_pMutableDataTarget != NULL); |
| 8686 | HRESULT hr = m_pMutableDataTarget->WriteVirtual(tb.pAddress, |
| 8687 | pLocalBuffer, |
| 8688 | tb.cbSize); |
| 8689 | IfFailThrow(hr); |
| 8690 | } |
| 8691 | |
| 8692 | //----------------------------------------------------------------------------- |
| 8693 | // Reads a buffer from the target and performs checks similar to SafeWriteStruct |
| 8694 | // |
| 8695 | // Arguments: |
| 8696 | // tb - TargetBuffer which represents the target memory to read from |
| 8697 | // pLocalBuffer - local pointer into source buffer |
| 8698 | // cbSize - the size of the remote buffer |
| 8699 | // throwOnError - determines whether the function throws exceptions or returns HRESULTs |
| 8700 | // in failure cases |
| 8701 | // |
| 8702 | // Exceptions: |
| 8703 | // If throwOnError is TRUE |
| 8704 | // On error always throws the special CORDBG_E_READVIRTUAL_FAILURE, unless a short write is performed |
| 8705 | // in which case throws ERROR_PARTIAL_COPY |
| 8706 | // If throwOnError is FALSE |
| 8707 | // No exceptions are thrown, and instead the same error codes are returned as HRESULTs |
| 8708 | // |
| 8709 | HRESULT CordbProcess::SafeReadBuffer(TargetBuffer tb, BYTE * pLocalBuffer, BOOL throwOnError) |
| 8710 | { |
| 8711 | ULONG32 cbRead; |
| 8712 | HRESULT hr = m_pDACDataTarget->ReadVirtual(tb.pAddress, |
| 8713 | pLocalBuffer, |
| 8714 | tb.cbSize, |
| 8715 | &cbRead); |
| 8716 | |
| 8717 | if (FAILED(hr)) |
| 8718 | { |
| 8719 | if (throwOnError) |
| 8720 | ThrowHR(CORDBG_E_READVIRTUAL_FAILURE); |
| 8721 | else |
| 8722 | return CORDBG_E_READVIRTUAL_FAILURE; |
| 8723 | } |
| 8724 | |
| 8725 | if (cbRead != tb.cbSize) |
| 8726 | { |
| 8727 | if (throwOnError) |
| 8728 | ThrowWin32(ERROR_PARTIAL_COPY); |
| 8729 | else |
| 8730 | return HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); |
| 8731 | } |
| 8732 | return S_OK; |
| 8733 | } |
| 8734 | |
| 8735 | |
| 8736 | //--------------------------------------------------------------------------------------- |
| 8737 | // Lookup or create an appdomain. |
| 8738 | // |
| 8739 | // Arguments: |
| 8740 | // vmAppDomain - CLR appdomain to lookup |
| 8741 | // |
| 8742 | // Returns: |
| 8743 | // Instance of CordbAppDomain for the given appdomain. This is a cached instance. |
| 8744 | // If the CordbAppDomain does not yet exist, it will be created and added to the cache. |
| 8745 | // Never returns NULL. Throw on error. |
| 8746 | CordbAppDomain * CordbProcess::LookupOrCreateAppDomain(VMPTR_AppDomain vmAppDomain) |
| 8747 | { |
| 8748 | CordbAppDomain * pAppDomain = m_appDomains.GetBase(VmPtrToCookie(vmAppDomain)); |
| 8749 | if (pAppDomain != NULL) |
| 8750 | { |
| 8751 | return pAppDomain; |
| 8752 | } |
| 8753 | return CacheAppDomain(vmAppDomain); |
| 8754 | } |
| 8755 | |
| 8756 | CordbAppDomain * CordbProcess::GetSharedAppDomain() |
| 8757 | { |
| 8758 | if (m_sharedAppDomain == NULL) |
| 8759 | { |
| 8760 | CordbAppDomain *pAD = new CordbAppDomain(this, VMPTR_AppDomain::NullPtr()); |
| 8761 | if (InterlockedCompareExchangeT<CordbAppDomain*>(&m_sharedAppDomain, pAD, NULL) != NULL) |
| 8762 | { |
| 8763 | delete pAD; |
| 8764 | } |
| 8765 | m_sharedAppDomain->InternalAddRef(); |
| 8766 | } |
| 8767 | |
| 8768 | return m_sharedAppDomain; |
| 8769 | } |
| 8770 | |
| 8771 | //--------------------------------------------------------------------------------------- |
| 8772 | // |
| 8773 | // Add a new appdomain to the cache. |
| 8774 | // |
| 8775 | // Arguments: |
| 8776 | // vmAppDomain - appdomain to add. |
| 8777 | // |
| 8778 | // Return Value: |
| 8779 | // Pointer to newly created appdomain, which should be the normal case. |
| 8780 | // Throws on failure. Never returns null. |
| 8781 | // |
| 8782 | // Assumptions: |
| 8783 | // Caller ensure the appdomain is not already cached. |
| 8784 | // Caller should have stop-go lock, which provides thread-safety. |
| 8785 | // |
| 8786 | // Notes: |
| 8787 | // This sets unrecoverable error on failure. |
| 8788 | // |
| 8789 | //--------------------------------------------------------------------------------------- |
| 8790 | CordbAppDomain * CordbProcess::CacheAppDomain(VMPTR_AppDomain vmAppDomain) |
| 8791 | { |
| 8792 | INTERNAL_API_ENTRY(GetProcess()); |
| 8793 | |
| 8794 | _ASSERTE(GetProcessLock()->HasLock()); |
| 8795 | |
| 8796 | RSInitHolder<CordbAppDomain> pAppDomain; |
| 8797 | pAppDomain.Assign(new CordbAppDomain(this, vmAppDomain)); // throws |
| 8798 | |
| 8799 | // Add to the hash. This will addref the pAppDomain. |
| 8800 | // Caller ensures we're not already cached. |
| 8801 | // The cache will take ownership. |
| 8802 | m_appDomains.AddBaseOrThrow(pAppDomain); |
| 8803 | |
| 8804 | // see if this is the default AppDomain |
| 8805 | IDacDbiInterface * pDac = m_pProcess->GetDAC(); |
| 8806 | BOOL fIsDefaultDomain = FALSE; |
| 8807 | |
| 8808 | fIsDefaultDomain = pDac->IsDefaultDomain(vmAppDomain); // throws |
| 8809 | |
| 8810 | if (fIsDefaultDomain) |
| 8811 | { |
| 8812 | // If this assert fires, then it likely means the target is corrupted. |
| 8813 | TargetConsistencyCheck(m_pDefaultAppDomain == NULL); |
| 8814 | m_pDefaultAppDomain = pAppDomain; |
| 8815 | } |
| 8816 | |
| 8817 | CordbAppDomain * pReturn = pAppDomain; |
| 8818 | pAppDomain.ClearAndMarkDontNeuter(); |
| 8819 | |
| 8820 | _ASSERTE(pReturn != NULL); |
| 8821 | return pReturn; |
| 8822 | } |
| 8823 | |
| 8824 | //--------------------------------------------------------------------------------------- |
| 8825 | // |
| 8826 | // Callback for Appdomain enumeration. |
| 8827 | // |
| 8828 | // Arguments: |
| 8829 | // vmAppDomain - new appdomain to add to enumeration |
| 8830 | // pUserData - data passed with callback (a 'this' ptr for CordbProcess) |
| 8831 | // |
| 8832 | // |
| 8833 | // Assumptions: |
| 8834 | // Invoked as callback from code:CordbProcess::PrepopulateAppDomains |
| 8835 | // |
| 8836 | // |
| 8837 | //--------------------------------------------------------------------------------------- |
| 8838 | |
| 8839 | // static |
| 8840 | void CordbProcess::AppDomainEnumerationCallback(VMPTR_AppDomain vmAppDomain, void * pUserData) |
| 8841 | { |
| 8842 | CONTRACTL |
| 8843 | { |
| 8844 | THROWS; |
| 8845 | } |
| 8846 | CONTRACTL_END; |
| 8847 | |
| 8848 | CordbProcess * pProcess = static_cast<CordbProcess *> (pUserData); |
| 8849 | INTERNAL_DAC_CALLBACK(pProcess); |
| 8850 | |
| 8851 | pProcess->LookupOrCreateAppDomain(vmAppDomain); |
| 8852 | } |
| 8853 | |
| 8854 | //--------------------------------------------------------------------------------------- |
| 8855 | // |
| 8856 | // Traverse appdomains in the target and build up our list. |
| 8857 | // |
| 8858 | // Arguments: |
| 8859 | // |
| 8860 | // Return Value: |
| 8861 | // returns on success. |
| 8862 | // Throws on error. AppDomain cache may be partially populated. |
| 8863 | // |
| 8864 | // Assumptions: |
| 8865 | // This is an non-invasive inspection operation called when the debuggee is stopped. |
| 8866 | // |
| 8867 | // Notes: |
| 8868 | // This can be called multiple times. If the list is non-empty, it will nop. |
| 8869 | //--------------------------------------------------------------------------------------- |
| 8870 | void CordbProcess::PrepopulateAppDomainsOrThrow() |
| 8871 | { |
| 8872 | CONTRACTL |
| 8873 | { |
| 8874 | THROWS; |
| 8875 | } |
| 8876 | CONTRACTL_END; |
| 8877 | |
| 8878 | INTERNAL_API_ENTRY(this); |
| 8879 | |
| 8880 | if (!IsDacInitialized()) |
| 8881 | { |
| 8882 | return; |
| 8883 | } |
| 8884 | |
| 8885 | // DD-primitive that invokes a callback. This may throw. |
| 8886 | GetDAC()->EnumerateAppDomains( |
| 8887 | CordbProcess::AppDomainEnumerationCallback, |
| 8888 | this); |
| 8889 | } |
| 8890 | |
| 8891 | //--------------------------------------------------------------------------------------- |
| 8892 | // |
| 8893 | // EnumerateAppDomains enumerates all app domains in the process. |
| 8894 | // |
| 8895 | // Arguments: |
| 8896 | // ppAppDomains - get appdomain enumerator |
| 8897 | // |
| 8898 | // Return Value: |
| 8899 | // S_OK on success. |
| 8900 | // |
| 8901 | // Assumptions: |
| 8902 | // |
| 8903 | // |
| 8904 | // Notes: |
| 8905 | // This operation is non-invasive target. |
| 8906 | // |
| 8907 | //--------------------------------------------------------------------------------------- |
| 8908 | HRESULT CordbProcess::EnumerateAppDomains(ICorDebugAppDomainEnum **ppAppDomains) |
| 8909 | { |
| 8910 | HRESULT hr = S_OK; |
| 8911 | PUBLIC_API_BEGIN(this); |
| 8912 | { |
| 8913 | ValidateOrThrow(ppAppDomains); |
| 8914 | |
| 8915 | // Ensure list is populated. |
| 8916 | PrepopulateAppDomainsOrThrow(); |
| 8917 | |
| 8918 | RSInitHolder<CordbHashTableEnum> pEnum; |
| 8919 | CordbHashTableEnum::BuildOrThrow( |
| 8920 | this, |
| 8921 | GetContinueNeuterList(), |
| 8922 | &m_appDomains, |
| 8923 | IID_ICorDebugAppDomainEnum, |
| 8924 | pEnum.GetAddr()); |
| 8925 | |
| 8926 | *ppAppDomains = static_cast<ICorDebugAppDomainEnum*> (pEnum); |
| 8927 | pEnum->ExternalAddRef(); |
| 8928 | |
| 8929 | pEnum.ClearAndMarkDontNeuter(); |
| 8930 | } |
| 8931 | PUBLIC_API_END(hr); |
| 8932 | return hr; |
| 8933 | } |
| 8934 | |
| 8935 | /* |
| 8936 | * GetObject returns the runtime process object. |
| 8937 | * Note: This method is not yet implemented. |
| 8938 | */ |
| 8939 | HRESULT CordbProcess::GetObject(ICorDebugValue **ppObject) |
| 8940 | { |
| 8941 | PUBLIC_API_ENTRY(this); |
| 8942 | FAIL_IF_NEUTERED(this); |
| 8943 | VALIDATE_POINTER_TO_OBJECT(ppObject, ICorDebugObjectValue **); |
| 8944 | |
| 8945 | return E_NOTIMPL; |
| 8946 | } |
| 8947 | |
| 8948 | |
| 8949 | //--------------------------------------------------------------------------------------- |
| 8950 | // |
| 8951 | // Given a taskid, finding the corresponding thread. The function can fail if we do not |
| 8952 | // find any thread with the given taskid |
| 8953 | // |
| 8954 | // Arguments: |
| 8955 | // taskId - The task ID to look for. |
| 8956 | // ppThread - OUT: Space for storing the thread corresponding to the taskId given. |
| 8957 | // |
| 8958 | // Return Value: |
| 8959 | // Typical HRESULT symantics, nothing abnormal. |
| 8960 | // |
| 8961 | HRESULT CordbProcess::GetThreadForTaskID(TASKID taskId, ICorDebugThread2 ** ppThread) |
| 8962 | { |
| 8963 | PUBLIC_API_ENTRY(this); |
| 8964 | FAIL_IF_NEUTERED(this); |
| 8965 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
| 8966 | |
| 8967 | HRESULT hr = S_OK; |
| 8968 | |
| 8969 | EX_TRY |
| 8970 | { |
| 8971 | RSLockHolder lockHolder(GetProcessLock()); |
| 8972 | |
| 8973 | if (ppThread == NULL) |
| 8974 | { |
| 8975 | ThrowHR(E_INVALIDARG); |
| 8976 | } |
| 8977 | |
| 8978 | // On initialization, the task ID of every thread is INVALID_TASK_ID, unless a host is present and |
| 8979 | // the host calls IClrTask::SetTaskIdentifier(). So we need to explicitly check for INVALID_TASK_ID |
| 8980 | // here and return NULL if necessary. We return S_FALSE because that's the return value for the case |
| 8981 | // where we can't find a thread for the specified task ID. |
| 8982 | if (taskId == INVALID_TASK_ID) |
| 8983 | { |
| 8984 | *ppThread = NULL; |
| 8985 | hr = S_FALSE; |
| 8986 | } |
| 8987 | else |
| 8988 | { |
| 8989 | PrepopulateThreadsOrThrow(); |
| 8990 | |
| 8991 | // now find the ICorDebugThread corresponding to it |
| 8992 | CordbThread * pThread; |
| 8993 | HASHFIND hashFind; |
| 8994 | |
| 8995 | |
| 8996 | for (pThread = m_userThreads.FindFirst(&hashFind); |
| 8997 | pThread != NULL; |
| 8998 | pThread = m_userThreads.FindNext(&hashFind)) |
| 8999 | { |
| 9000 | if (pThread->GetTaskID() == taskId) |
| 9001 | { |
| 9002 | break; |
| 9003 | } |
| 9004 | } |
| 9005 | |
| 9006 | if (pThread == NULL) |
| 9007 | { |
| 9008 | *ppThread = NULL; |
| 9009 | hr = S_FALSE; |
| 9010 | } |
| 9011 | else |
| 9012 | { |
| 9013 | *ppThread = pThread; |
| 9014 | pThread->ExternalAddRef(); |
| 9015 | } |
| 9016 | } |
| 9017 | } |
| 9018 | EX_CATCH_HRESULT(hr); |
| 9019 | return hr; |
| 9020 | } // CordbProcess::GetThreadForTaskid |
| 9021 | |
| 9022 | HRESULT |
| 9023 | CordbProcess::GetVersion(COR_VERSION* pVersion) |
| 9024 | { |
| 9025 | if (NULL == pVersion) |
| 9026 | { |
| 9027 | return E_INVALIDARG; |
| 9028 | } |
| 9029 | |
| 9030 | // |
| 9031 | // Because we require a matching version of mscordbi.dll to debug a certain version of the runtime, |
| 9032 | // we can just use constants found in this particular mscordbi.dll to determine the version of the left side. |
| 9033 | pVersion->dwMajor = CLR_MAJOR_VERSION; |
| 9034 | pVersion->dwMinor = CLR_MINOR_VERSION; |
| 9035 | pVersion->dwBuild = CLR_BUILD_VERSION; |
| 9036 | pVersion->dwSubBuild = CLR_BUILD_VERSION_QFE; |
| 9037 | |
| 9038 | return S_OK; |
| 9039 | } |
| 9040 | |
| 9041 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 9042 | //----------------------------------------------------------------------------- |
| 9043 | // Search for a native patch given the address. Return null if not found. |
| 9044 | // Since we return an address, this is only valid until the table is disturbed. |
| 9045 | //----------------------------------------------------------------------------- |
| 9046 | NativePatch * CordbProcess::GetNativePatch(const void * pAddress) |
| 9047 | { |
| 9048 | _ASSERTE(ThreadHoldsProcessLock()); |
| 9049 | |
| 9050 | int cTotal = m_NativePatchList.Count(); |
| 9051 | NativePatch * pTable = m_NativePatchList.Table(); |
| 9052 | if (pTable == NULL) |
| 9053 | { |
| 9054 | return NULL; |
| 9055 | } |
| 9056 | |
| 9057 | for(int i = 0; i < cTotal; i++) |
| 9058 | { |
| 9059 | if (pTable[i].pAddress == pAddress) |
| 9060 | { |
| 9061 | return &pTable[i]; |
| 9062 | } |
| 9063 | } |
| 9064 | return NULL; |
| 9065 | } |
| 9066 | |
| 9067 | //----------------------------------------------------------------------------- |
| 9068 | // Is there an break-opcode (int3 on x86) at the address in the debuggee? |
| 9069 | //----------------------------------------------------------------------------- |
| 9070 | bool CordbProcess::IsBreakOpcodeAtAddress(const void * address) |
| 9071 | { |
| 9072 | // There should have been an int3 there already. Since we already put it in there, |
| 9073 | // we should be able to safely read it out. |
| 9074 | #if defined(DBG_TARGET_ARM) || defined(DBG_TARGET_ARM64) |
| 9075 | PRD_TYPE opcodeTest = 0; |
| 9076 | #elif defined(DBG_TARGET_AMD64) || defined(DBG_TARGET_X86) |
| 9077 | BYTE opcodeTest = 0; |
| 9078 | #else |
| 9079 | PORTABILITY_ASSERT("NYI: Architecture specific opcode type to read" ); |
| 9080 | #endif |
| 9081 | |
| 9082 | HRESULT hr = SafeReadStruct(PTR_TO_CORDB_ADDRESS(address), &opcodeTest); |
| 9083 | SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr); |
| 9084 | |
| 9085 | return (opcodeTest == CORDbg_BREAK_INSTRUCTION); |
| 9086 | } |
| 9087 | #endif // FEATURE_INTEROP_DEBUGGING |
| 9088 | |
| 9089 | //----------------------------------------------------------------------------- |
| 9090 | // CordbProcess::SetUnmanagedBreakpoint |
| 9091 | // Called by a native debugger to add breakpoints during Interop. |
| 9092 | // address - remote address into the debuggee |
| 9093 | // bufsize, buffer[] - initial size & buffer for the opcode that we're replacing. |
| 9094 | // buflen - size of the buffer that we write to. |
| 9095 | //----------------------------------------------------------------------------- |
| 9096 | HRESULT |
| 9097 | CordbProcess::SetUnmanagedBreakpoint(CORDB_ADDRESS address, ULONG32 bufsize, BYTE buffer[], ULONG32 * bufLen) |
| 9098 | { |
| 9099 | LOG((LF_CORDB, LL_INFO100, "CP::SetUnBP: pProcess=%x, address=%p.\n" , this, CORDB_ADDRESS_TO_PTR(address))); |
| 9100 | #ifndef FEATURE_INTEROP_DEBUGGING |
| 9101 | return E_NOTIMPL; |
| 9102 | #else |
| 9103 | PUBLIC_API_ENTRY(this); |
| 9104 | FAIL_IF_NEUTERED(this); |
| 9105 | FAIL_IF_MANAGED_ONLY(this); |
| 9106 | _ASSERTE(!ThreadHoldsProcessLock()); |
| 9107 | Lock(); |
| 9108 | HRESULT hr = SetUnmanagedBreakpointInternal(address, bufsize, buffer, bufLen); |
| 9109 | Unlock(); |
| 9110 | return hr; |
| 9111 | #endif |
| 9112 | } |
| 9113 | |
| 9114 | //----------------------------------------------------------------------------- |
| 9115 | // CordbProcess::SetUnmanagedBreakpointInternal |
| 9116 | // The worker behind SetUnmanagedBreakpoint, this function can set both public |
| 9117 | // breakpoints used by the debugger and internal breakpoints used for utility |
| 9118 | // purposes in interop debugging. |
| 9119 | // address - remote address into the debuggee |
| 9120 | // bufsize, buffer[] - initial size & buffer for the opcode that we're replacing. |
| 9121 | // buflen - size of the buffer that we write to. |
| 9122 | //----------------------------------------------------------------------------- |
| 9123 | HRESULT |
| 9124 | CordbProcess::SetUnmanagedBreakpointInternal(CORDB_ADDRESS address, ULONG32 bufsize, BYTE buffer[], ULONG32 * bufLen) |
| 9125 | { |
| 9126 | LOG((LF_CORDB, LL_INFO100, "CP::SetUnBPI: pProcess=%x, address=%p.\n" , this, CORDB_ADDRESS_TO_PTR(address))); |
| 9127 | #ifndef FEATURE_INTEROP_DEBUGGING |
| 9128 | return E_NOTIMPL; |
| 9129 | #else |
| 9130 | |
| 9131 | INTERNAL_API_ENTRY(this); |
| 9132 | FAIL_IF_NEUTERED(this); |
| 9133 | FAIL_IF_MANAGED_ONLY(this); |
| 9134 | _ASSERTE(ThreadHoldsProcessLock()); |
| 9135 | |
| 9136 | HRESULT hr = S_OK; |
| 9137 | |
| 9138 | NativePatch * p = NULL; |
| 9139 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64) |
| 9140 | const BYTE patch = CORDbg_BREAK_INSTRUCTION; |
| 9141 | BYTE opcode; |
| 9142 | #elif defined(DBG_TARGET_ARM64) |
| 9143 | const PRD_TYPE patch = CORDbg_BREAK_INSTRUCTION; |
| 9144 | PRD_TYPE opcode; |
| 9145 | #else |
| 9146 | PORTABILITY_ASSERT("NYI: CordbProcess::SetUnmanagedBreakpoint, interop debugging NYI on this platform" ); |
| 9147 | hr = E_NOTIMPL; |
| 9148 | goto ErrExit; |
| 9149 | #endif |
| 9150 | |
| 9151 | // Make sure args are good |
| 9152 | if ((buffer == NULL) || (bufsize < sizeof(patch)) || (bufLen == NULL)) |
| 9153 | { |
| 9154 | hr = E_INVALIDARG; |
| 9155 | goto ErrExit; |
| 9156 | } |
| 9157 | |
| 9158 | // Fail if there's already a patch at this address. |
| 9159 | if (GetNativePatch(CORDB_ADDRESS_TO_PTR(address)) != NULL) |
| 9160 | { |
| 9161 | hr = CORDBG_E_NATIVE_PATCH_ALREADY_AT_ADDR; |
| 9162 | goto ErrExit; |
| 9163 | } |
| 9164 | |
| 9165 | // Preallocate this now so that if are oom, we can fail before we get half-way through. |
| 9166 | p = m_NativePatchList.Append(); |
| 9167 | if (p == NULL) |
| 9168 | { |
| 9169 | hr = E_OUTOFMEMORY; |
| 9170 | goto ErrExit; |
| 9171 | } |
| 9172 | |
| 9173 | |
| 9174 | // Read out opcode. 1 byte on x86 |
| 9175 | |
| 9176 | hr = ApplyRemotePatch(this, CORDB_ADDRESS_TO_PTR(address), &p->opcode); |
| 9177 | if (FAILED(hr)) |
| 9178 | goto ErrExit; |
| 9179 | |
| 9180 | // It's all successful, so now update our out-params & internal bookkeaping. |
| 9181 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64) |
| 9182 | opcode = (BYTE)p->opcode; |
| 9183 | buffer[0] = opcode; |
| 9184 | #elif defined(DBG_TARGET_ARM64) |
| 9185 | opcode = p->opcode; |
| 9186 | memcpy_s(buffer, bufsize, &opcode, sizeof(opcode)); |
| 9187 | #else |
| 9188 | PORTABILITY_ASSERT("NYI: CordbProcess::SetUnmanagedBreakpoint, interop debugging NYI on this platform" ); |
| 9189 | #endif |
| 9190 | *bufLen = sizeof(opcode); |
| 9191 | |
| 9192 | p->pAddress = CORDB_ADDRESS_TO_PTR(address); |
| 9193 | p->opcode = opcode; |
| 9194 | |
| 9195 | _ASSERTE(SUCCEEDED(hr)); |
| 9196 | |
| 9197 | ErrExit: |
| 9198 | // If we failed, then free the patch |
| 9199 | if (FAILED(hr) && (p != NULL)) |
| 9200 | { |
| 9201 | m_NativePatchList.Delete(*p); |
| 9202 | } |
| 9203 | |
| 9204 | return hr; |
| 9205 | |
| 9206 | #endif // FEATURE_INTEROP_DEBUGGING |
| 9207 | } |
| 9208 | |
| 9209 | |
| 9210 | //----------------------------------------------------------------------------- |
| 9211 | // CordbProcess::ClearUnmanagedBreakpoint |
| 9212 | // Called by a native debugger to remove breakpoints during Interop. |
| 9213 | // The patch is deleted even if the function fails. |
| 9214 | //----------------------------------------------------------------------------- |
| 9215 | HRESULT |
| 9216 | CordbProcess::ClearUnmanagedBreakpoint(CORDB_ADDRESS address) |
| 9217 | { |
| 9218 | LOG((LF_CORDB, LL_INFO100, "CP::ClearUnBP: pProcess=%x, address=%p.\n" , this, CORDB_ADDRESS_TO_PTR(address))); |
| 9219 | #ifndef FEATURE_INTEROP_DEBUGGING |
| 9220 | return E_NOTIMPL; |
| 9221 | #else |
| 9222 | PUBLIC_API_ENTRY(this); |
| 9223 | FAIL_IF_NEUTERED(this); |
| 9224 | FAIL_IF_MANAGED_ONLY(this); |
| 9225 | |
| 9226 | _ASSERTE(!ThreadHoldsProcessLock()); |
| 9227 | |
| 9228 | HRESULT hr = S_OK; |
| 9229 | PRD_TYPE opcode; |
| 9230 | |
| 9231 | Lock(); |
| 9232 | |
| 9233 | // Make sure this is a valid patch. |
| 9234 | int cTotal = m_NativePatchList.Count(); |
| 9235 | NativePatch * pTable = m_NativePatchList.Table(); |
| 9236 | if (pTable == NULL) |
| 9237 | { |
| 9238 | hr = CORDBG_E_NO_NATIVE_PATCH_AT_ADDR; |
| 9239 | goto ErrExit; |
| 9240 | } |
| 9241 | |
| 9242 | int i; |
| 9243 | for(i = 0; i < cTotal; i++) |
| 9244 | { |
| 9245 | if (pTable[i].pAddress == CORDB_ADDRESS_TO_PTR(address)) |
| 9246 | break; |
| 9247 | } |
| 9248 | |
| 9249 | if (i >= cTotal) |
| 9250 | { |
| 9251 | hr = CORDBG_E_NO_NATIVE_PATCH_AT_ADDR; |
| 9252 | goto ErrExit; |
| 9253 | } |
| 9254 | |
| 9255 | // Found it! Remove it from our table. Note that this may shuffle table contents |
| 9256 | // around, so don't keep pointers into the table. |
| 9257 | opcode = pTable[i].opcode; |
| 9258 | |
| 9259 | m_NativePatchList.Delete(pTable[i]); |
| 9260 | _ASSERTE(m_NativePatchList.Count() == cTotal - 1); |
| 9261 | |
| 9262 | // Now remove the patch. |
| 9263 | |
| 9264 | |
| 9265 | |
| 9266 | // Just call through to Write ProcessMemory |
| 9267 | hr = RemoveRemotePatch(this, CORDB_ADDRESS_TO_PTR(address), opcode); |
| 9268 | if (FAILED(hr)) |
| 9269 | goto ErrExit; |
| 9270 | |
| 9271 | |
| 9272 | // Our internal bookeaping was already updated to remove the patch, so now we're done. |
| 9273 | // If we had a failure, we should have already bailed. |
| 9274 | _ASSERTE(SUCCEEDED(hr)); |
| 9275 | |
| 9276 | ErrExit: |
| 9277 | Unlock(); |
| 9278 | return hr; |
| 9279 | #endif // FEATURE_INTEROP_DEBUGGING |
| 9280 | } |
| 9281 | |
| 9282 | |
| 9283 | //------------------------------------------------------------------------------------ |
| 9284 | // StopCount, Sync, SyncReceived form our stop-status. This status is super-critical |
| 9285 | // to most hangs, so we stress log it. |
| 9286 | //------------------------------------------------------------------------------------ |
| 9287 | void CordbProcess::SetSynchronized(bool fSynch) |
| 9288 | { |
| 9289 | _ASSERTE(ThreadHoldsProcessLock() || !"Must have process lock to toggle SyncStatus" ); |
| 9290 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CP:: set sync=%d\n" , fSynch); |
| 9291 | m_synchronized = fSynch; |
| 9292 | } |
| 9293 | |
| 9294 | bool CordbProcess::GetSynchronized() |
| 9295 | { |
| 9296 | // This can be accessed whether we're Locked or not. This means that the result |
| 9297 | // may change underneath us. |
| 9298 | return m_synchronized; |
| 9299 | } |
| 9300 | |
| 9301 | void CordbProcess::IncStopCount() |
| 9302 | { |
| 9303 | _ASSERTE(ThreadHoldsProcessLock()); |
| 9304 | m_stopCount++; |
| 9305 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CP:: Inc StopCount=%d\n" , m_stopCount); |
| 9306 | } |
| 9307 | void CordbProcess::DecStopCount() |
| 9308 | { |
| 9309 | // We can inc w/ just the process lock (b/c we can dispatch events from the W32ET) |
| 9310 | // But decrementing (eg, Continue), requires the stop-go lock. |
| 9311 | // This if an operation takes the SG lock, it ensures we don't continue from underneath it. |
| 9312 | ASSERT_SINGLE_THREAD_ONLY(HoldsLock(&m_StopGoLock)); |
| 9313 | _ASSERTE(ThreadHoldsProcessLock()); |
| 9314 | |
| 9315 | m_stopCount--; |
| 9316 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CP:: Dec StopCount=%d\n" , m_stopCount); |
| 9317 | } |
| 9318 | |
| 9319 | // Just gets whether we're stopped or not (m_stopped > 0). |
| 9320 | // You only need the StopGo lock for this. |
| 9321 | bool CordbProcess::IsStopped() |
| 9322 | { |
| 9323 | // We don't require the process-lock, just the SG-lock. |
| 9324 | // Holding the SG lock prevents another thread from continuing underneath you. |
| 9325 | // (see DecStopCount()). |
| 9326 | // But you could still be running free, and have another thread stop-underneath you. |
| 9327 | // Thus IsStopped() leans towards returning false. |
| 9328 | ASSERT_SINGLE_THREAD_ONLY(HoldsLock(&m_StopGoLock)); |
| 9329 | |
| 9330 | return (m_stopCount > 0); |
| 9331 | } |
| 9332 | |
| 9333 | int CordbProcess::GetStopCount() |
| 9334 | { |
| 9335 | _ASSERTE(ThreadHoldsProcessLock()); |
| 9336 | return m_stopCount; |
| 9337 | } |
| 9338 | |
| 9339 | bool CordbProcess::GetSyncCompleteRecv() |
| 9340 | { |
| 9341 | _ASSERTE(ThreadHoldsProcessLock()); |
| 9342 | return m_syncCompleteReceived; |
| 9343 | } |
| 9344 | |
| 9345 | void CordbProcess::SetSyncCompleteRecv(bool fSyncRecv) |
| 9346 | { |
| 9347 | _ASSERTE(ThreadHoldsProcessLock()); |
| 9348 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CP:: set syncRecv=%d\n" , fSyncRecv); |
| 9349 | m_syncCompleteReceived = fSyncRecv; |
| 9350 | } |
| 9351 | |
| 9352 | // This can be used if we ever need the RS to emulate old behavior of previous versions. |
| 9353 | // This can not be used in QIs to deny queries for new interfaces. |
| 9354 | // QIs must be consistent across the lifetime of an object. Say CordbThread used this in a QI |
| 9355 | // do deny returning a ICorDebugThread2 interface when emulating v1.1. Once that Thread is neutered, |
| 9356 | // it no longer has a pointer to the process, and it no longer knows if it should be denying |
| 9357 | // the v2.0 query. An object's QI can't start returning new interfaces onces its neutered. |
| 9358 | bool CordbProcess::SupportsVersion(CorDebugInterfaceVersion featureVersion) |
| 9359 | { |
| 9360 | _ASSERTE(featureVersion == CorDebugVersion_2_0); |
| 9361 | return true; |
| 9362 | } |
| 9363 | |
| 9364 | |
| 9365 | //--------------------------------------------------------------------------------------- |
| 9366 | // Add an object to the process's Left-Side resource cleanup list |
| 9367 | // |
| 9368 | // Arguments: |
| 9369 | // pObject - non-null object to be added |
| 9370 | // |
| 9371 | // Notes: |
| 9372 | // This list tracks objects with process-scope that hold left-side |
| 9373 | // resources (like func-eval). |
| 9374 | // See code:CordbAppDomain::GetSweepableExitNeuterList for per-appdomain |
| 9375 | // objects with left-side resources. |
| 9376 | void CordbProcess::AddToLeftSideResourceCleanupList(CordbBase * pObject) |
| 9377 | { |
| 9378 | INTERNAL_API_ENTRY(this); |
| 9379 | _ASSERTE(pObject != NULL); |
| 9380 | |
| 9381 | m_LeftSideResourceCleanupList.Add(this, pObject); |
| 9382 | } |
| 9383 | |
| 9384 | // This list will get actively swept (looking for objects w/ external ref = 0) between continues. |
| 9385 | void CordbProcess::AddToNeuterOnExitList(CordbBase *pObject) |
| 9386 | { |
| 9387 | INTERNAL_API_ENTRY(this); |
| 9388 | _ASSERTE(pObject != NULL); |
| 9389 | |
| 9390 | HRESULT hr = S_OK; |
| 9391 | EX_TRY |
| 9392 | { |
| 9393 | this->m_ExitNeuterList.Add(this, pObject); |
| 9394 | } |
| 9395 | EX_CATCH_HRESULT(hr); |
| 9396 | SetUnrecoverableIfFailed(GetProcess(), hr); |
| 9397 | } |
| 9398 | |
| 9399 | // Mark that this object should be neutered the next time we Continue the process. |
| 9400 | void CordbProcess::AddToNeuterOnContinueList(CordbBase *pObject) |
| 9401 | { |
| 9402 | INTERNAL_API_ENTRY(this); |
| 9403 | _ASSERTE(pObject != NULL); |
| 9404 | |
| 9405 | m_ContinueNeuterList.Add(this, pObject); // throws |
| 9406 | } |
| 9407 | |
| 9408 | |
| 9409 | /* ------------------------------------------------------------------------- * |
| 9410 | * Runtime Controller Event Thread class |
| 9411 | * ------------------------------------------------------------------------- */ |
| 9412 | |
| 9413 | // |
| 9414 | // Constructor |
| 9415 | // |
| 9416 | CordbRCEventThread::CordbRCEventThread(Cordb* cordb) |
| 9417 | { |
| 9418 | _ASSERTE(cordb != NULL); |
| 9419 | |
| 9420 | m_cordb.Assign(cordb); |
| 9421 | m_thread = NULL; |
| 9422 | m_threadId = 0; |
| 9423 | m_run = TRUE; |
| 9424 | m_threadControlEvent = NULL; |
| 9425 | m_processStateChanged = FALSE; |
| 9426 | |
| 9427 | g_pRSDebuggingInfo->m_RCET = this; |
| 9428 | } |
| 9429 | |
| 9430 | |
| 9431 | // |
| 9432 | // Destructor. Cleans up all of the open handles and such. |
| 9433 | // This expects that the thread has been stopped and has terminated |
| 9434 | // before being called. |
| 9435 | // |
| 9436 | CordbRCEventThread::~CordbRCEventThread() |
| 9437 | { |
| 9438 | if (m_threadControlEvent != NULL) |
| 9439 | CloseHandle(m_threadControlEvent); |
| 9440 | |
| 9441 | if (m_thread != NULL) |
| 9442 | CloseHandle(m_thread); |
| 9443 | |
| 9444 | g_pRSDebuggingInfo->m_RCET = NULL; |
| 9445 | } |
| 9446 | |
| 9447 | // |
| 9448 | // Init sets up all the objects that the thread will need to run. |
| 9449 | // |
| 9450 | HRESULT CordbRCEventThread::Init() |
| 9451 | { |
| 9452 | if (m_cordb == NULL) |
| 9453 | return E_INVALIDARG; |
| 9454 | |
| 9455 | m_threadControlEvent = WszCreateEvent(NULL, FALSE, FALSE, NULL); |
| 9456 | |
| 9457 | if (m_threadControlEvent == NULL) |
| 9458 | return HRESULT_FROM_GetLastError(); |
| 9459 | |
| 9460 | return S_OK; |
| 9461 | } |
| 9462 | |
| 9463 | |
| 9464 | #if defined(FEATURE_INTEROP_DEBUGGING) |
| 9465 | // |
| 9466 | // Helper to duplicate a handle or thorw |
| 9467 | // |
| 9468 | // Arguments: |
| 9469 | // pLocalHandle - handle to duplicate into the remote process |
| 9470 | // pRemoteHandle - RemoteHandle structure in IPC block to hold the remote handle. |
| 9471 | // Return value: |
| 9472 | // None. Throws on error. |
| 9473 | // |
| 9474 | void CordbProcess::DuplicateHandleToLocalProcess(HANDLE * pLocalHandle, RemoteHANDLE * pRemoteHandle) |
| 9475 | { |
| 9476 | _ASSERTE(m_pShim != NULL); |
| 9477 | |
| 9478 | // Dup RSEA and RSER into this process if we don't already have them. |
| 9479 | // On Launch, we don't have them yet, but on attach we do. |
| 9480 | if (*pLocalHandle == NULL) |
| 9481 | { |
| 9482 | BOOL fSuccess = pRemoteHandle->DuplicateToLocalProcess(m_handle, pLocalHandle); |
| 9483 | if (!fSuccess) |
| 9484 | { |
| 9485 | ThrowLastError(); |
| 9486 | } |
| 9487 | } |
| 9488 | |
| 9489 | } |
| 9490 | #endif // FEATURE_INTEROP_DEBUGGING |
| 9491 | |
| 9492 | // Public entry wrapper for code:CordbProcess::FinishInitializeIPCChannelWorker |
| 9493 | void CordbProcess::FinishInitializeIPCChannel() |
| 9494 | { |
| 9495 | // This is called directly from a shim callback. |
| 9496 | PUBLIC_API_ENTRY_FOR_SHIM(this); |
| 9497 | FinishInitializeIPCChannelWorker(); |
| 9498 | } |
| 9499 | |
| 9500 | // |
| 9501 | // Initialize the IPC channel. After this, IPC events can flow in both ways. |
| 9502 | // |
| 9503 | // Return value: |
| 9504 | // Returns S_OK on success. |
| 9505 | // |
| 9506 | // Notes: |
| 9507 | // This will dispatch an UnrecoverableError callback if it fails. |
| 9508 | // This will also initialize key state in the CordbProcess object. |
| 9509 | // |
| 9510 | // @dbgtodo remove helper-thread: this should eventually go away once we get rid of IPC events. |
| 9511 | // |
| 9512 | void CordbProcess::FinishInitializeIPCChannelWorker() |
| 9513 | { |
| 9514 | CONTRACTL |
| 9515 | { |
| 9516 | THROWS; |
| 9517 | } |
| 9518 | CONTRACTL_END; |
| 9519 | |
| 9520 | HRESULT hr = S_OK; |
| 9521 | _ASSERTE(m_pShim != NULL); |
| 9522 | |
| 9523 | RSLockHolder lockHolder(&this->m_processMutex); |
| 9524 | |
| 9525 | // If it's already initialized, then nothing left to do. |
| 9526 | // this protects us if this function is called multiple times. |
| 9527 | if (m_initialized) |
| 9528 | { |
| 9529 | _ASSERTE(GetDCB() != NULL); |
| 9530 | return; |
| 9531 | } |
| 9532 | |
| 9533 | EX_TRY |
| 9534 | { |
| 9535 | LOG((LF_CORDB, LL_INFO1000, "[%x] RCET::HFRCE: first event..., process %p\n" , GetCurrentThreadId(), this)); |
| 9536 | |
| 9537 | BOOL fBlockExists; |
| 9538 | GetEventBlock(&fBlockExists); // throws on error |
| 9539 | |
| 9540 | LOG((LF_CORDB, LL_EVERYTHING, "Size of CdbP is %d\n" , sizeof(CordbProcess))); |
| 9541 | |
| 9542 | m_pEventChannel->Init(m_handle); |
| 9543 | |
| 9544 | #if defined(FEATURE_INTEROP_DEBUGGING) |
| 9545 | DuplicateHandleToLocalProcess(&m_leftSideUnmanagedWaitEvent, &GetDCB()->m_leftSideUnmanagedWaitEvent); |
| 9546 | #endif // FEATURE_INTEROP_DEBUGGING |
| 9547 | |
| 9548 | // Read the Runtime Offsets struct out of the debuggee. |
| 9549 | hr = GetRuntimeOffsets(); |
| 9550 | IfFailThrow(hr); |
| 9551 | |
| 9552 | // we need to be careful here. The LS will have a thread running free that may be initializing |
| 9553 | // fields of the DCB (specifically it may be setting up the helper thread), so we need to make sure |
| 9554 | // we don't overwrite any fields that the LS is writing. We need to be sure we only write to RS |
| 9555 | // status fields. |
| 9556 | m_initialized = true; |
| 9557 | GetDCB()->m_rightSideIsWin32Debugger = IsInteropDebugging(); |
| 9558 | UpdateLeftSideDCBField(&(GetDCB()->m_rightSideIsWin32Debugger), sizeof(GetDCB()->m_rightSideIsWin32Debugger)); |
| 9559 | |
| 9560 | LOG((LF_CORDB, LL_INFO1000, "[%x] RCET::HFRCE: ...went fine\n" , GetCurrentThreadId())); |
| 9561 | _ASSERTE(SUCCEEDED(hr)); |
| 9562 | |
| 9563 | } EX_CATCH_HRESULT(hr); |
| 9564 | if (SUCCEEDED(hr)) |
| 9565 | { |
| 9566 | return; |
| 9567 | } |
| 9568 | |
| 9569 | // We only land here on failure cases. |
| 9570 | // We must have jumped to this label. Maybe we didn't set HR, so check now. |
| 9571 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "HFCR: FAILED hr=0x%08x\n" , hr); |
| 9572 | |
| 9573 | CloseIPCHandles(); |
| 9574 | |
| 9575 | // Rethrow |
| 9576 | ThrowHR(hr); |
| 9577 | } |
| 9578 | |
| 9579 | |
| 9580 | //--------------------------------------------------------------------------------------- |
| 9581 | // Marshals over a string buffer in a managed event |
| 9582 | // |
| 9583 | // Arguments: |
| 9584 | // pTarget - data-target for read the buffer from the LeftSide. |
| 9585 | // |
| 9586 | // Throws on error |
| 9587 | void Ls_Rs_BaseBuffer::CopyLSDataToRSWorker(ICorDebugDataTarget * pTarget) |
| 9588 | { |
| 9589 | // |
| 9590 | const DWORD cbCacheSize = m_cbSize; |
| 9591 | |
| 9592 | // SHOULD not happen for more than once in well-behaved case. |
| 9593 | if (m_pbRS != NULL) |
| 9594 | { |
| 9595 | SIMPLIFYING_ASSUMPTION(!"m_pbRS is non-null; is this a corrupted event?" ); |
| 9596 | ThrowHR(E_INVALIDARG); |
| 9597 | } |
| 9598 | |
| 9599 | NewHolder<BYTE> pData(new BYTE[cbCacheSize]); |
| 9600 | |
| 9601 | ULONG32 cbRead; |
| 9602 | HRESULT hrRead = pTarget->ReadVirtual(PTR_TO_CORDB_ADDRESS(m_pbLS), pData, cbCacheSize , &cbRead); |
| 9603 | |
| 9604 | if(FAILED(hrRead)) |
| 9605 | { |
| 9606 | hrRead = CORDBG_E_READVIRTUAL_FAILURE; |
| 9607 | } |
| 9608 | |
| 9609 | if (SUCCEEDED(hrRead) && (cbCacheSize != cbRead)) |
| 9610 | { |
| 9611 | hrRead = HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); |
| 9612 | } |
| 9613 | IfFailThrow(hrRead); |
| 9614 | |
| 9615 | // Now do Transfer |
| 9616 | m_pbRS = pData; |
| 9617 | pData.SuppressRelease(); |
| 9618 | } |
| 9619 | |
| 9620 | //--------------------------------------------------------------------------------------- |
| 9621 | // Marshals over a Byte buffer in a managed event |
| 9622 | // |
| 9623 | // Arguments: |
| 9624 | // pTarget - data-target for read the buffer from the LeftSide. |
| 9625 | // |
| 9626 | // Throws on error |
| 9627 | void Ls_Rs_ByteBuffer::CopyLSDataToRS(ICorDebugDataTarget * pTarget) |
| 9628 | { |
| 9629 | CopyLSDataToRSWorker(pTarget); |
| 9630 | } |
| 9631 | |
| 9632 | //--------------------------------------------------------------------------------------- |
| 9633 | // Marshals over a string buffer in a managed event |
| 9634 | // |
| 9635 | // Arguments: |
| 9636 | // pTarget - data-target for read the buffer from the LeftSide. |
| 9637 | // |
| 9638 | // Throws on error |
| 9639 | void Ls_Rs_StringBuffer::CopyLSDataToRS(ICorDebugDataTarget * pTarget) |
| 9640 | { |
| 9641 | CopyLSDataToRSWorker(pTarget); |
| 9642 | |
| 9643 | // Ensure we're a valid, well-formed string. |
| 9644 | // @dbgtodo - this should only happen in corrupted scenarios. Perhaps a better HR here? |
| 9645 | // - null terminated. |
| 9646 | // - no embedded nulls. |
| 9647 | |
| 9648 | const WCHAR * pString = GetString(); |
| 9649 | SIZE_T dwExpectedLenWithNull = m_cbSize / sizeof(WCHAR); |
| 9650 | |
| 9651 | // Should at least have 1 character for the null-terminator. |
| 9652 | if (dwExpectedLenWithNull == 0) |
| 9653 | { |
| 9654 | ThrowHR(CORDBG_E_TARGET_INCONSISTENT); |
| 9655 | } |
| 9656 | |
| 9657 | // Ensure that there's a null where we expect it to be. |
| 9658 | if (pString[dwExpectedLenWithNull-1] != 0) |
| 9659 | { |
| 9660 | ThrowHR(CORDBG_E_TARGET_INCONSISTENT); |
| 9661 | } |
| 9662 | |
| 9663 | // Now we know it's safe to call wcslen. The buffer is local, so we know the pages are there. |
| 9664 | // And we know there's a null capping the max length of the string. |
| 9665 | SIZE_T dwActualLenWithNull = wcslen(pString) + 1; |
| 9666 | if (dwActualLenWithNull != dwExpectedLenWithNull) |
| 9667 | { |
| 9668 | ThrowHR(CORDBG_E_TARGET_INCONSISTENT); |
| 9669 | } |
| 9670 | } |
| 9671 | |
| 9672 | //--------------------------------------------------------------------------------------- |
| 9673 | // Marshals the arguments in a managed-debug event. |
| 9674 | // |
| 9675 | // Arguments: |
| 9676 | // pManagedEvent - (IN/OUT) debug event to marshal. Events are not usable in the host process |
| 9677 | // until they are marshalled. This will marshal the event in-place, and may convert |
| 9678 | // some target addresses to host addresses. |
| 9679 | // |
| 9680 | // Return Value: |
| 9681 | // S_OK on success. Else Error. |
| 9682 | // |
| 9683 | // Assumptions: |
| 9684 | // Target is currently stopped and inspectable. |
| 9685 | // After the event is marshalled, it has resources that must be cleaned up |
| 9686 | // by calling code:DeleteIPCEventHelper. |
| 9687 | // |
| 9688 | // Notes: |
| 9689 | // Call a Copy function (CopyManagedEventFromTarget, CopyRCEventFromIPCBlock)to |
| 9690 | // get the event to marshal. |
| 9691 | // This will marshal args from the target into the host. |
| 9692 | // The debug event is fixed size. But since the debuggee is stopped, this can copy |
| 9693 | // arbitrary-length buffers out of of the debuggee. |
| 9694 | // |
| 9695 | // This could be rolled into code:CordbProcess::RawDispatchEvent |
| 9696 | //--------------------------------------------------------------------------------------- |
| 9697 | void CordbProcess::MarshalManagedEvent(DebuggerIPCEvent * pManagedEvent) |
| 9698 | { |
| 9699 | CONTRACTL |
| 9700 | { |
| 9701 | THROWS; |
| 9702 | |
| 9703 | // Event has already been copied, now we do some quick Marshalling. |
| 9704 | // Thsi should be a private local copy, and not the one in the IPC block or Target. |
| 9705 | PRECONDITION(CheckPointer(pManagedEvent)); |
| 9706 | } |
| 9707 | CONTRACTL_END; |
| 9708 | |
| 9709 | IfFailThrow(pManagedEvent->hr); |
| 9710 | |
| 9711 | // This may throw part way through marshalling. But that's ok because |
| 9712 | // code:DeleteIPCEventHelper can cleanup a partially-marshalled event. |
| 9713 | |
| 9714 | // Do a pre-processing on the event |
| 9715 | switch (pManagedEvent->type & DB_IPCE_TYPE_MASK) |
| 9716 | { |
| 9717 | case DB_IPCE_MDA_NOTIFICATION: |
| 9718 | { |
| 9719 | pManagedEvent->MDANotification.szName.CopyLSDataToRS(this->m_pDACDataTarget); |
| 9720 | pManagedEvent->MDANotification.szDescription.CopyLSDataToRS(this->m_pDACDataTarget); |
| 9721 | pManagedEvent->MDANotification.szXml.CopyLSDataToRS(this->m_pDACDataTarget); |
| 9722 | break; |
| 9723 | } |
| 9724 | |
| 9725 | case DB_IPCE_FIRST_LOG_MESSAGE: |
| 9726 | { |
| 9727 | pManagedEvent->FirstLogMessage.szContent.CopyLSDataToRS(this->m_pDACDataTarget); |
| 9728 | break; |
| 9729 | } |
| 9730 | |
| 9731 | default: |
| 9732 | break; |
| 9733 | } |
| 9734 | |
| 9735 | |
| 9736 | } |
| 9737 | |
| 9738 | |
| 9739 | //--------------------------------------------------------------------------------------- |
| 9740 | // Copy a managed debug event from the target process into this local process |
| 9741 | // |
| 9742 | // Arguments: |
| 9743 | // pRecord - native-debug event serving as the envelope for the managed event. |
| 9744 | // pLocalManagedEvent - (dst) required local buffer to hold managed event. |
| 9745 | // |
| 9746 | // Return Value: |
| 9747 | // * True if the event belongs to this runtime. This is very useful when multiple CLRs are |
| 9748 | // loaded into the target and all sending events wit the same exception code. |
| 9749 | // * False if this does not belong to this instance of ICorDebug. (perhaps it's an event |
| 9750 | // intended for another instance of the CLR in the target, or some rogue user code happening |
| 9751 | // to use our exception code). |
| 9752 | // In either case, the event can still be cleaned up via code:DeleteIPCEventHelper. |
| 9753 | // |
| 9754 | // Throws on error. In the error case, the contents of pLocalManagedEvent are undefined. |
| 9755 | // They may have been partially copied from the target. The local managed event does not own |
| 9756 | // any resources until it's marshalled, so the buffer can be ignored if this function fails. |
| 9757 | // |
| 9758 | // Assumptions: |
| 9759 | // |
| 9760 | // Notes: |
| 9761 | // The events are sent form the target via code:Debugger::SendRawEvent |
| 9762 | // This just does a raw Byte copy, but does not do any Marshalling. |
| 9763 | // This should always succeed in the well-behaved case. However, A bad debuggee can |
| 9764 | // always send a poor-formed debug event. |
| 9765 | // We don't distinguish between a badly formed event and an event that's not ours. |
| 9766 | // The event still needs to be Marshaled before being used. (see code:CordbProcess::MarshalManagedEvent) |
| 9767 | // |
| 9768 | //--------------------------------------------------------------------------------------- |
| 9769 | #if defined(_MSC_VER) && defined(_TARGET_ARM_) |
| 9770 | // This is a temporary workaround for an ARM specific MS C++ compiler bug (internal LKG build 18.1). |
| 9771 | // Branch < if (ptrRemoteManagedEvent == NULL) > was always taken and the function always returned false. |
| 9772 | // TODO: It should be removed once the bug is fixed. |
| 9773 | #pragma optimize("", off) |
| 9774 | #endif |
| 9775 | bool CordbProcess::CopyManagedEventFromTarget( |
| 9776 | const EXCEPTION_RECORD * pRecord, |
| 9777 | DebuggerIPCEvent * pLocalManagedEvent) |
| 9778 | { |
| 9779 | _ASSERTE(pRecord != NULL); |
| 9780 | _ASSERTE(pLocalManagedEvent != NULL); |
| 9781 | |
| 9782 | // Initialize the event enough such backout code can call code:DeleteIPCEventHelper. |
| 9783 | pLocalManagedEvent->type = DB_IPCE_DEBUGGER_INVALID; |
| 9784 | |
| 9785 | // Ensure we have a CLR instance ID by now. Either we had one already, or we're in |
| 9786 | // V2 mode and this is the startup event, and so we'll set it now. |
| 9787 | HRESULT hr = EnsureClrInstanceIdSet(); |
| 9788 | IfFailThrow(hr); |
| 9789 | _ASSERTE(m_clrInstanceId != 0); |
| 9790 | |
| 9791 | // Determine if the event is really a debug event, and for our instance. |
| 9792 | CORDB_ADDRESS ptrRemoteManagedEvent = IsEventDebuggerNotification(pRecord, m_clrInstanceId); |
| 9793 | |
| 9794 | if (ptrRemoteManagedEvent == NULL) |
| 9795 | { |
| 9796 | return false; |
| 9797 | } |
| 9798 | |
| 9799 | // What we are doing on Windows here is dangerous. Any buffer for IPC events must be at least |
| 9800 | // CorDBIPC_BUFFER_SIZE big, but here we are only copying sizeof(DebuggerIPCEvent). Fortunately, the |
| 9801 | // only case where an IPC event is bigger than sizeof(DebuggerIPCEvent) is for the second category |
| 9802 | // described in the comment for code:IEventChannel. In this case, we are just transferring the IPC |
| 9803 | // event from the native pipeline to the event channel, and the event channel will read it directly from |
| 9804 | // the send buffer on the LS. See code:CordbRCEventThread::WaitForIPCEventFromProcess. |
| 9805 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
| 9806 | hr = SafeReadStruct(ptrRemoteManagedEvent, pLocalManagedEvent); |
| 9807 | #else |
| 9808 | // For Mac remote debugging the address returned above is actually a local address. |
| 9809 | // Also, we need to copy the entire buffer because once a debug event is read from the debugger |
| 9810 | // transport, it won't be available afterwards. |
| 9811 | memcpy(reinterpret_cast<BYTE *>(pLocalManagedEvent), |
| 9812 | CORDB_ADDRESS_TO_PTR(ptrRemoteManagedEvent), |
| 9813 | CorDBIPC_BUFFER_SIZE); |
| 9814 | hr = S_OK; |
| 9815 | #endif |
| 9816 | SIMPLIFYING_ASSUMPTION(SUCCEEDED(hr)); |
| 9817 | IfFailThrow(hr); |
| 9818 | |
| 9819 | return true; |
| 9820 | } |
| 9821 | #if defined(_MSC_VER) && defined(_TARGET_ARM_) |
| 9822 | #pragma optimize("", on) |
| 9823 | #endif |
| 9824 | |
| 9825 | //--------------------------------------------------------------------------------------- |
| 9826 | // EnsureClrInstanceIdSet - Ensure we have a CLR Instance ID to debug |
| 9827 | // |
| 9828 | // In Arrowhead scenarios, the debugger is required to pass a valid CLR instance ID |
| 9829 | // to us in OpenVirtualProcess. In V2 scenarios, for compatibility, we'll allow a |
| 9830 | // CordbProcess object to exist for a process that doesn't yet have the CLR loaded. |
| 9831 | // In this case the CLR instance ID will start off as 0, but be filled in when we see the |
| 9832 | // startup exception indicating the CLR has been loaded. |
| 9833 | // |
| 9834 | // If we don't already have an instance ID, this function sets it to the only CLR in the |
| 9835 | // target process. This requires that a CLR be loaded in the target process. |
| 9836 | // |
| 9837 | // Return Value: |
| 9838 | // S_OK - if m_clrInstanceId was already set, or is now set to a valid CLR instance ID |
| 9839 | // an error HRESULT - if m_clrInstanceId was 0, and cannot be set to a valid value |
| 9840 | // (i.e. because we cannot find a CLR in the target process). |
| 9841 | // |
| 9842 | // Note that we need to probe for this on attach, and it's common to attach before the |
| 9843 | // CLR has been loaded, so we avoid using exceptions for this common case. |
| 9844 | // |
| 9845 | HRESULT CordbProcess::EnsureClrInstanceIdSet() |
| 9846 | { |
| 9847 | // If we didn't expect a specific CLR, then attempt to attach to any. |
| 9848 | if (m_clrInstanceId == 0) |
| 9849 | { |
| 9850 | |
| 9851 | #ifdef FEATURE_CORESYSTEM |
| 9852 | if(m_cordb->GetTargetCLR() != 0) |
| 9853 | { |
| 9854 | m_clrInstanceId = PTR_TO_CORDB_ADDRESS(m_cordb->GetTargetCLR()); |
| 9855 | return S_OK; |
| 9856 | } |
| 9857 | #endif |
| 9858 | |
| 9859 | // The only case in which we're allowed to request the "default" CLR instance |
| 9860 | // ID is when we're running in V2 mode. In V3, the client is required to pass |
| 9861 | // a non-zero value to OpenVirtualProcess. |
| 9862 | _ASSERTE(m_pShim != NULL); |
| 9863 | |
| 9864 | HRESULT hr = m_pShim->FindLoadedCLR(&m_clrInstanceId); |
| 9865 | if (FAILED(hr)) |
| 9866 | { |
| 9867 | // Couldn't find a loaded clr - no CLR instance ID yet |
| 9868 | _ASSERTE(m_clrInstanceId == 0); |
| 9869 | return hr; |
| 9870 | } |
| 9871 | } |
| 9872 | |
| 9873 | // We've (now) got a valid CLR instance id |
| 9874 | return S_OK; |
| 9875 | } |
| 9876 | |
| 9877 | //--------------------------------------------------------------------------------------- |
| 9878 | // // Copy event from IPC block into local. |
| 9879 | // |
| 9880 | // Arguments: |
| 9881 | // pLocalManagedEvent - required local buffer to hold managed event. |
| 9882 | // |
| 9883 | // Return Value: |
| 9884 | // None. Always succeeds. |
| 9885 | // |
| 9886 | // Assumptions: |
| 9887 | // The IPC block has already been opened and filled in with an event. |
| 9888 | // |
| 9889 | // Notes: |
| 9890 | // This is copying from a shared-memory block, which is treated as local memory. |
| 9891 | // This just does a raw Byte copy, but does not do any Marshalling. |
| 9892 | // This does no validation on the event. |
| 9893 | // The event still needs to be Marshaled before being used. (see code:CordbProcess::MarshalManagedEvent) |
| 9894 | // |
| 9895 | //--------------------------------------------------------------------------------------- |
| 9896 | void inline CordbProcess::CopyRCEventFromIPCBlock(DebuggerIPCEvent * pLocalManagedEvent) |
| 9897 | { |
| 9898 | _ASSERTE(pLocalManagedEvent != NULL); |
| 9899 | |
| 9900 | IfFailThrow(m_pEventChannel->GetEventFromLeftSide(pLocalManagedEvent)); |
| 9901 | } |
| 9902 | |
| 9903 | // Return true if this is the RCEvent thread, else false. |
| 9904 | bool CordbRCEventThread::IsRCEventThread() |
| 9905 | { |
| 9906 | return (m_threadId == GetCurrentThreadId()); |
| 9907 | } |
| 9908 | |
| 9909 | //--------------------------------------------------------------------------------------- |
| 9910 | // Runtime assert, throws CORDBG_E_TARGET_INCONSISTENT if the expression is not true. |
| 9911 | // |
| 9912 | // Arguments: |
| 9913 | // fExpression - assert parameter. If true, this function is a nop. If false, |
| 9914 | // this will throw a CORDBG_E_TARGET_INCONSISTENT error. |
| 9915 | // |
| 9916 | // Notes: |
| 9917 | // Use this for runtime checks to validate assumptions about the data-target. |
| 9918 | // IcorDebug can't trust that data from the debugee is consistent (perhaps it's |
| 9919 | // corrupted). |
| 9920 | void CordbProcess::TargetConsistencyCheck(bool fExpression) |
| 9921 | { |
| 9922 | if (!fExpression) |
| 9923 | { |
| 9924 | STRESS_LOG0(LF_CORDB, LL_INFO10000, "Target consistency check failed" ); |
| 9925 | |
| 9926 | // When debugging possibly corrupt targets, this failure may be expected. For debugging purposes, |
| 9927 | // assert if we're not expecting any target inconsistencies. |
| 9928 | CONSISTENCY_CHECK_MSG( !m_fAssertOnTargetInconsistency, "Target consistency check failed unexpectedly" ); |
| 9929 | |
| 9930 | ThrowHR(CORDBG_E_TARGET_INCONSISTENT); |
| 9931 | } |
| 9932 | } |
| 9933 | |
| 9934 | // |
| 9935 | // SendIPCEvent -- send an IPC event to the runtime controller. All this |
| 9936 | // really does is copy the event into the process's send buffer and sets |
| 9937 | // the RSEA then waits on RSER. |
| 9938 | // |
| 9939 | // Note: when sending a two-way event (replyRequired = true), the |
| 9940 | // eventSize must be large enough for both the event sent and the |
| 9941 | // result event. |
| 9942 | // |
| 9943 | // Returns whether the event was sent successfully. This is different than event->eventHr. |
| 9944 | // |
| 9945 | HRESULT CordbRCEventThread::SendIPCEvent(CordbProcess* process, |
| 9946 | DebuggerIPCEvent* event, |
| 9947 | SIZE_T eventSize) |
| 9948 | { |
| 9949 | |
| 9950 | _ASSERTE(process != NULL); |
| 9951 | _ASSERTE(event != NULL); |
| 9952 | _ASSERTE(process->GetShim() != NULL); |
| 9953 | |
| 9954 | #ifdef _DEBUG |
| 9955 | // We need to be synchronized whenever we're sending an IPC Event. |
| 9956 | // This may require our callers' using a Stop-Continue holder. |
| 9957 | // Attach + AsyncBreak are the only (obvious) exceptions. |
| 9958 | // For continue, we set Sync-Status to false before sending, so we exclude that too. |
| 9959 | // Everybody else should only be sending events when synced. We should never ever ever |
| 9960 | // send an event from a CorbXYZ dtor (b/c that would be called at any random time). Instead, |
| 9961 | // use a NeuterList. |
| 9962 | switch (event->type) |
| 9963 | { |
| 9964 | case DB_IPCE_ATTACHING: |
| 9965 | case DB_IPCE_ASYNC_BREAK: |
| 9966 | case DB_IPCE_CONTINUE: |
| 9967 | break; |
| 9968 | |
| 9969 | default: |
| 9970 | CONSISTENCY_CHECK_MSGF(process->GetSynchronized(), ("Must by synced while sending IPC event: %s (0x%x)" , |
| 9971 | IPCENames::GetName(event->type), event->type)); |
| 9972 | } |
| 9973 | #endif |
| 9974 | |
| 9975 | |
| 9976 | LOG((LF_CORDB, LL_EVERYTHING, "SendIPCEvent in CordbRCEventThread called\n" )); |
| 9977 | |
| 9978 | // For simplicity sake, we have the following conservative invariants when sending IPC events: |
| 9979 | // - Always hold the Stop-Go lock. |
| 9980 | // - never on the W32ET. |
| 9981 | // - Never hold the Process-lock (this allows the w32et to take that lock to pump) |
| 9982 | |
| 9983 | // Must have the stop-go lock to send an IPC event. |
| 9984 | CONSISTENCY_CHECK_MSGF(process->GetStopGoLock()->HasLock(), ("Must have stop-go lock to send event. proc=%p, event=%s" , |
| 9985 | process, IPCENames::GetName(event->type))); |
| 9986 | |
| 9987 | // The w32 ET will need to take the process lock. So if we're holding it here, then we'll |
| 9988 | // deadlock (since W32 ET is blocked on lock, which we would hold; and we're blocked on W32 ET |
| 9989 | // to keep pumping. |
| 9990 | _ASSERTE(!process->ThreadHoldsProcessLock() || !"Can't hold P-lock while sending blocking IPC event" ); |
| 9991 | |
| 9992 | |
| 9993 | // Can't be on the w32 ET, or we can't be pumping. |
| 9994 | // Although we can trickle in here from public APIs, our caller should have validated |
| 9995 | // that we weren't on the w32et, so the assert here is justified. But just in case there's something we missed, |
| 9996 | // we have a runtime check (as a final backstop against a deadlock). |
| 9997 | _ASSERTE(!process->IsWin32EventThread()); |
| 9998 | CORDBFailIfOnWin32EventThread(process); |
| 9999 | |
| 10000 | |
| 10001 | // If this is an async event, then we expect it to be sent while the process is locked. |
| 10002 | if (event->asyncSend) |
| 10003 | { |
| 10004 | // This may be on the w32et, so we can't hold the stop-go lock. |
| 10005 | _ASSERTE(event->type == DB_IPCE_ATTACHING); // only async event should be attaching. |
| 10006 | } |
| 10007 | |
| 10008 | |
| 10009 | // This will catch us if we've detached or exited. |
| 10010 | // Note if we exited, then we should have been neutered and so shouldn't even be sending an IPC event, |
| 10011 | // but just in case, we'll check. |
| 10012 | CORDBRequireProcessStateOK(process); |
| 10013 | |
| 10014 | |
| 10015 | #ifdef _DEBUG |
| 10016 | // We should never send an Async Break on the RCET. This will deadlock. |
| 10017 | // - if we're on the RCET, we should be stopped, and thus Stop() should just bump up a stop count, |
| 10018 | // and not actually send an AsyncBreak. |
| 10019 | // - Delayed-Continues help enforce this. |
| 10020 | // This is a special case of the deadlock check below. |
| 10021 | if (IsRCEventThread()) |
| 10022 | { |
| 10023 | _ASSERTE(event->type != DB_IPCE_ASYNC_BREAK); |
| 10024 | } |
| 10025 | #endif |
| 10026 | |
| 10027 | #ifdef _DEBUG |
| 10028 | // This assert protects us against a deadlock. |
| 10029 | // 1) (RCET) blocked on (This function): If we're on the RCET, then the RCET is blocked until we return (duh). |
| 10030 | // 2) (LS) blocked on (RCET): If the LS is not synchronized, then it may be sending an event to the RCET, and thus blocked on the RCET. |
| 10031 | // 3) (Helper thread) blocked on (LS): That LS thread may be holding a lock that the helper thread needs, thus blocking the helper thread. |
| 10032 | // 4) (This function) blocked on (Helper Thread): We block until the helper thread can process our IPC event. |
| 10033 | // #4 is not true for async events. |
| 10034 | // |
| 10035 | // If we hit this assert, it means we may get the deadlock above and we're calling SendIPCEvent at a time we shouldn't. |
| 10036 | // Note this race is as old as dirt. |
| 10037 | if (IsRCEventThread() && !event->asyncSend) |
| 10038 | { |
| 10039 | // Note that w/ Continue & Attach, GetSynchronized() has a different meaning and the race above won't happen. |
| 10040 | BOOL fPossibleDeadlock = process->GetSynchronized() || (event->type == DB_IPCE_CONTINUE) || (event->type == DB_IPCE_ATTACHING); |
| 10041 | CONSISTENCY_CHECK_MSGF(fPossibleDeadlock, ("Possible deadlock while sending: '%s'\n" , IPCENames::GetName(event->type))); |
| 10042 | } |
| 10043 | #endif |
| 10044 | |
| 10045 | |
| 10046 | |
| 10047 | // Cache this process into the MRU so that we can find it if we're debugging in retail. |
| 10048 | g_pRSDebuggingInfo->m_MRUprocess = process; |
| 10049 | |
| 10050 | HRESULT hr = S_OK; |
| 10051 | HRESULT hrEvent = S_OK; |
| 10052 | _ASSERTE(event != NULL); |
| 10053 | |
| 10054 | // NOTE: the eventSize parameter is only so you can specify an event size that is SMALLER than the process send |
| 10055 | // buffer size!! |
| 10056 | if (eventSize > CorDBIPC_BUFFER_SIZE) |
| 10057 | return E_INVALIDARG; |
| 10058 | |
| 10059 | STRESS_LOG4(LF_CORDB, LL_INFO1000, "CRCET::SIPCE: sending %s to AD 0x%x, proc 0x%x(%d)\n" , |
| 10060 | IPCENames::GetName(event->type), VmPtrToCookie(event->vmAppDomain), process->m_id, process->m_id); |
| 10061 | |
| 10062 | // For 2-way events, this check is unnecessary (since we already check for LS exit) |
| 10063 | // But for async events, we need this. |
| 10064 | // So just check it up here and make everyone's life easier. |
| 10065 | if (process->m_terminated) |
| 10066 | { |
| 10067 | STRESS_LOG0(LF_CORDB, LL_INFO10000, "CRCET::SIPCE: LS already terminated, shortcut exiting\n" ); |
| 10068 | return CORDBG_E_PROCESS_TERMINATED; |
| 10069 | } |
| 10070 | |
| 10071 | // If the helper thread has died, we can't send an IPC event (and it's never coming back either). |
| 10072 | // Although we do wait on the thread's handle, there are strange windows where the thread's handle |
| 10073 | // is not yet signaled even though we've continued from the exit-thread event for the helper. |
| 10074 | if (process->m_helperThreadDead) |
| 10075 | { |
| 10076 | STRESS_LOG0(LF_CORDB, LL_INFO10000, "CRCET::SIPCE: Helper-thread dead, shortcut exiting\n" ); |
| 10077 | return CORDBG_E_PROCESS_TERMINATED; |
| 10078 | } |
| 10079 | |
| 10080 | BOOL fUnrecoverableError = TRUE; |
| 10081 | EX_TRY |
| 10082 | { |
| 10083 | hr = process->GetEventChannel()->SendEventToLeftSide(event, eventSize); |
| 10084 | fUnrecoverableError = FALSE; |
| 10085 | } |
| 10086 | EX_CATCH_HRESULT(hr); |
| 10087 | |
| 10088 | |
| 10089 | // If we're sending a Continue() event, then after this, the LS may run free. |
| 10090 | // If this is the last managed event before the LS exits, (which is the case |
| 10091 | // if we're responding to either an Exit-Thread or if we respond to a Detach) |
| 10092 | // the LS may exit at anytime from here on, so we need to be careful. |
| 10093 | |
| 10094 | |
| 10095 | if (fUnrecoverableError) |
| 10096 | { |
| 10097 | _ASSERTE(FAILED(hr)); |
| 10098 | CORDBSetUnrecoverableError(process, hr, 0); |
| 10099 | } |
| 10100 | else |
| 10101 | { |
| 10102 | // Get a handle to the target process - this call always succeeds |
| 10103 | HANDLE hLSProcess = NULL; |
| 10104 | process->GetHandle(&hLSProcess); |
| 10105 | |
| 10106 | // We take locks to ensure that the CordbProcess object is still alive, |
| 10107 | // even if the OS process exited. |
| 10108 | _ASSERTE(hLSProcess != NULL); |
| 10109 | |
| 10110 | // Check if Sending the IPC event failed |
| 10111 | if (FAILED(hr)) |
| 10112 | { |
| 10113 | // The failure to send an event may be due to the target process terminating |
| 10114 | // (especially, but not exclusively, in the case of async events). |
| 10115 | // There is a race here - we can't rely on any check above SendEventToLeftSide |
| 10116 | // to tell us whether the process has exited yet. |
| 10117 | // Check for that case and return an accurate hresult. |
| 10118 | DWORD ret = WaitForSingleObject(hLSProcess, 0); |
| 10119 | if (ret == WAIT_OBJECT_0) |
| 10120 | { |
| 10121 | return CORDBG_E_PROCESS_TERMINATED; |
| 10122 | } |
| 10123 | |
| 10124 | // Some other failure sending the IPC event - just return it. |
| 10125 | return hr; |
| 10126 | } |
| 10127 | |
| 10128 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CRCET::SIPCE: sent...\n" ); |
| 10129 | |
| 10130 | // If this is an async send, then don't wait for the left side to acknowledge that its read the event. |
| 10131 | _ASSERTE(!event->asyncSend || !event->replyRequired); |
| 10132 | |
| 10133 | if (process->GetEventChannel()->NeedToWaitForAck(event)) |
| 10134 | { |
| 10135 | STRESS_LOG0(LF_CORDB, LL_INFO1000,"CRCET::SIPCE: waiting for left side to read event. (on RSER)\n" ); |
| 10136 | |
| 10137 | DWORD ret; |
| 10138 | |
| 10139 | // Wait for either a reply (common case) or the left side to go away. |
| 10140 | // We can't detach while waiting for a reply (because detach needs to send events). |
| 10141 | // All of the outcomes from this wait are completely disjoint. |
| 10142 | // It's possible for the LS to reply and then exit normally (Thread_Detach, Process_Detach) |
| 10143 | // and so ExitProcess may have been called, but it doesn't matter. |
| 10144 | |
| 10145 | enum { |
| 10146 | ID_RSER = WAIT_OBJECT_0, |
| 10147 | ID_LSPROCESS, |
| 10148 | ID_HELPERTHREAD, |
| 10149 | }; |
| 10150 | |
| 10151 | // Only wait on the helper thread for cases where the process is stopped (and thus we don't expect it do exit on us). |
| 10152 | // If the process is running and we lose our helper thread, it ought to be during shutdown and we ough to |
| 10153 | // follow up with an exit. |
| 10154 | // This includes when we've dispatch Native events, and it includes the AsyncBreak sent to get us from a |
| 10155 | // win32 frozen state to a synchronized state). |
| 10156 | HANDLE hHelperThread = NULL; |
| 10157 | if (process->IsStopped()) |
| 10158 | { |
| 10159 | hHelperThread = process->GetHelperThreadHandle(); |
| 10160 | } |
| 10161 | |
| 10162 | |
| 10163 | // Note that in case of a tie (multiple handles signaled), WaitForMultipleObjects gives |
| 10164 | // priority to the handle earlier in the array. |
| 10165 | HANDLE waitSet[] = { process->GetEventChannel()->GetRightSideEventAckHandle(), hLSProcess, hHelperThread}; |
| 10166 | DWORD cWaitSet = NumItems(waitSet); |
| 10167 | if (hHelperThread == NULL) |
| 10168 | { |
| 10169 | cWaitSet--; |
| 10170 | } |
| 10171 | |
| 10172 | do |
| 10173 | { |
| 10174 | ret = WaitForMultipleObjectsEx(cWaitSet, waitSet, FALSE, CordbGetWaitTimeout(), FALSE); |
| 10175 | // If we timeout because we're waiting for an uncontinued OOB event, we need to just keep waiting. |
| 10176 | } while ((ret == WAIT_TIMEOUT) && process->IsWaitingForOOBEvent()); |
| 10177 | |
| 10178 | switch(ret) |
| 10179 | { |
| 10180 | case ID_RSER: |
| 10181 | // Normal reply from LS. |
| 10182 | // This is set iff the LS replied to our event. The LS may have exited since it replied |
| 10183 | // but we don't care. We still have the reply and we'll pass it on. |
| 10184 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CRCET::SIPCE: left side read the event.\n" ); |
| 10185 | |
| 10186 | // If this was a two-way event, then the result is already ready for us. Simply copy the result back |
| 10187 | // over the original event that was sent. Otherwise, the left side has simply read the event and is |
| 10188 | // processing it... |
| 10189 | if (event->replyRequired) |
| 10190 | { |
| 10191 | process->GetEventChannel()->GetReplyFromLeftSide(event, eventSize); |
| 10192 | hrEvent = event->hr; |
| 10193 | } |
| 10194 | break; |
| 10195 | |
| 10196 | case ID_LSPROCESS: |
| 10197 | // Left side exited on us. |
| 10198 | // ExitProcess may or may not have been called here (since it's on a different thread). |
| 10199 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CRCET::SIPCE: left side exiting while RS was waiting for reply.\n" ); |
| 10200 | hr = CORDBG_E_PROCESS_TERMINATED; |
| 10201 | break; |
| 10202 | |
| 10203 | case ID_HELPERTHREAD: |
| 10204 | // We can only send most IPC events while the LS is synchronized. We shouldn't lose our helper thread |
| 10205 | // when synced under any sort of normal conditions. |
| 10206 | // This won't fire if the process already exited, because LSPROCESS gets higher priority in the wait |
| 10207 | // (since it was placed earlier). |
| 10208 | // Thus the only "legitimate" window where this could happen would be in a shutdown scenario after |
| 10209 | // the helper is dead but before the process has died. We shouldn't be synced in that scenario, |
| 10210 | // so we shouldn't be sending IPC events during it. |
| 10211 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CRCET::SIPCE: lost helper thread.\n" ); |
| 10212 | |
| 10213 | |
| 10214 | // Assert because we want to know if we ever actually hit this in any detectable scenario. |
| 10215 | // However, shutdown can occur in preemptive mode. Thus if the RS does an AsyncBreak late |
| 10216 | // enough, then the LS will appear to be stopped but may still shutdown. |
| 10217 | // Since the debuggee can exit asynchronously at any time (eg, suppose somebody forcefully |
| 10218 | // kills it with taskman), this doesn't introduce a new case. |
| 10219 | // That aside, it would be great to be able to assert this: |
| 10220 | //_ASSERTE(!"Potential deadlock - Randomly Lost helper thread"); |
| 10221 | |
| 10222 | // We'll piggy back this on the terminated case. |
| 10223 | hr = CORDBG_E_PROCESS_TERMINATED; |
| 10224 | break; |
| 10225 | |
| 10226 | default: |
| 10227 | { |
| 10228 | // If we timed out/failed, check the left side to see if it is in the unrecoverable error mode. If it is, |
| 10229 | // return the HR from the left side that caused the error. Otherwise, return that we timed out and that |
| 10230 | // we don't really know why. |
| 10231 | HRESULT realHR = (ret == WAIT_FAILED) ? HRESULT_FROM_GetLastError() : ErrWrapper(CORDBG_E_TIMEOUT); |
| 10232 | |
| 10233 | hr = process->CheckForUnrecoverableError(); |
| 10234 | |
| 10235 | if (hr == S_OK) |
| 10236 | { |
| 10237 | CORDBSetUnrecoverableError(process, realHR, 0); |
| 10238 | hr = realHR; |
| 10239 | } |
| 10240 | |
| 10241 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CRCET::SIPCE: left side timeout/fail while RS waiting for reply. hr = 0x%08x\n" , hr); |
| 10242 | } |
| 10243 | break; |
| 10244 | } |
| 10245 | |
| 10246 | // If the LS picked up RSEA, it will be reset (since it's an auto event). |
| 10247 | // But in the case that the wait failed or that the LS exited, we need to explicitly reset RSEA |
| 10248 | if (hr != S_OK) |
| 10249 | { |
| 10250 | process->GetEventChannel()->ClearEventForLeftSide(); |
| 10251 | } |
| 10252 | |
| 10253 | // Done waiting for reply. |
| 10254 | |
| 10255 | } |
| 10256 | } |
| 10257 | |
| 10258 | process->ForceDacFlush(); |
| 10259 | |
| 10260 | // The hr and hrEvent are 2 very different things. |
| 10261 | // hr tells us whether the event was sent successfully. |
| 10262 | // hrEvent tells us how the LS responded to it. |
| 10263 | // if FAILED(hr), then hrEvent is useless b/c the LS never got it. |
| 10264 | // But if SUCCEEDED(hr), then hrEvent may still have failed and that could be |
| 10265 | // valuable information. |
| 10266 | |
| 10267 | return hr; |
| 10268 | } |
| 10269 | |
| 10270 | //--------------------------------------------------------------------------------------- |
| 10271 | // FlushQueuedEvents flushes a process's event queue. |
| 10272 | // |
| 10273 | // Arguments: |
| 10274 | // pProcess - non-null process object whose queue will be drained |
| 10275 | // |
| 10276 | // Notes: |
| 10277 | // @dbgtodo shim: this should be part of the shim. |
| 10278 | // This dispatches events that are queued up. The queue is populated by |
| 10279 | // the shim's proxy callback (see code:ShimProxyCallback). This will dispatch events |
| 10280 | // to the 'real' callback supplied by the debugger. This will dispatch events |
| 10281 | // as long as the debugger keeps calling continue. |
| 10282 | // |
| 10283 | // This requires that the process lock be held, although it will toggle the lock. |
| 10284 | void CordbRCEventThread::FlushQueuedEvents(CordbProcess* process) |
| 10285 | { |
| 10286 | CONTRACTL |
| 10287 | { |
| 10288 | NOTHROW; // This is happening on the RCET thread, so there's no place to propogate an error back up. |
| 10289 | } |
| 10290 | CONTRACTL_END; |
| 10291 | |
| 10292 | STRESS_LOG0(LF_CORDB,LL_INFO10000, "CRCET::FQE: Beginning to flush queue\n" ); |
| 10293 | |
| 10294 | _ASSERTE(process->GetShim() != NULL); |
| 10295 | |
| 10296 | // We should only call this is we already have queued events |
| 10297 | _ASSERTE(!process->GetShim()->GetManagedEventQueue()->IsEmpty()); |
| 10298 | |
| 10299 | // |
| 10300 | // Dispatch queued events so long as they keep calling Continue() |
| 10301 | // before returning from their callback. If they call Continue(), |
| 10302 | // process->m_synchronized will be false again and we know to |
| 10303 | // loop around and dispatch the next event. |
| 10304 | // |
| 10305 | _ASSERTE(process->ThreadHoldsProcessLock()); |
| 10306 | |
| 10307 | |
| 10308 | // Give shim a chance to queue any faked attach events. Grab a pointer to the |
| 10309 | // ShimProcess now, while we still hold the process lock. Once we release the lock, |
| 10310 | // GetShim() may not work. |
| 10311 | RSExtSmartPtr<ShimProcess> pShim(process->GetShim()); |
| 10312 | |
| 10313 | // Release lock before we call out to shim to Queue fake events. |
| 10314 | { |
| 10315 | RSInverseLockHolder inverseLockHolder(process->GetProcessLock()); |
| 10316 | { |
| 10317 | PUBLIC_CALLBACK_IN_THIS_SCOPE0_NO_LOCK(pProcess); |
| 10318 | |
| 10319 | // Because we've released the lock, at any point from here forward the |
| 10320 | // CorDbProcess may suddenly get neutered if the user detaches the debugger. |
| 10321 | |
| 10322 | pShim->QueueFakeAttachEventsIfNeeded(false); |
| 10323 | } |
| 10324 | } |
| 10325 | |
| 10326 | // Now that we're holding the process lock again, we can safely check whether |
| 10327 | // process has become neutered |
| 10328 | if (process->IsNeutered()) |
| 10329 | { |
| 10330 | return; |
| 10331 | } |
| 10332 | |
| 10333 | { |
| 10334 | |
| 10335 | // Main dispatch loop here. DispatchRCEvent will take events out of the |
| 10336 | // queue and invoke callbacks |
| 10337 | do |
| 10338 | { |
| 10339 | // DispatchRCEvent will mark the process as stopped before dispatching. |
| 10340 | process->DispatchRCEvent(); |
| 10341 | |
| 10342 | LOG((LF_CORDB,LL_INFO10000, "CRCET::FQE: Finished w/ " |
| 10343 | "DispatchRCEvent\n" )); |
| 10344 | } |
| 10345 | while (process->GetSyncCompleteRecv() && |
| 10346 | (process->GetSynchronized() == false) && |
| 10347 | (process->GetShim() != NULL) && // may have lost Shim if we detached while dispatch |
| 10348 | (!process->GetShim()->GetManagedEventQueue()->IsEmpty()) && |
| 10349 | (process->m_unrecoverableError == false)); |
| 10350 | } |
| 10351 | |
| 10352 | // |
| 10353 | // If they returned from a callback without calling Continue() then |
| 10354 | // the process is still synchronized, so let the rc event thread |
| 10355 | // know that it need to update its process list and remove the |
| 10356 | // process's event. |
| 10357 | // |
| 10358 | if (process->GetSynchronized()) |
| 10359 | { |
| 10360 | ProcessStateChanged(); |
| 10361 | } |
| 10362 | |
| 10363 | LOG((LF_CORDB,LL_INFO10000, "CRCET::FQE: finished\n" )); |
| 10364 | } |
| 10365 | |
| 10366 | //--------------------------------------------------------------------------------------- |
| 10367 | // Preliminary Handle an Notification event from the target. This may queue the event, |
| 10368 | // but does not actually dispatch the event. |
| 10369 | // |
| 10370 | // Arguments: |
| 10371 | // pManagedEvent - local managed-event. On success, this function assumes ownership of the |
| 10372 | // event and will delete its memory. Assumed that caller allocated via 'new'. |
| 10373 | // pCallback - callback obecjt to dispatch events on. |
| 10374 | // |
| 10375 | // Return Value: |
| 10376 | // None. Throws on error. On error, caller still owns the pManagedEvent and must free it. |
| 10377 | // |
| 10378 | // Assumptions: |
| 10379 | // This should be called once a notification event is received from the target. |
| 10380 | // |
| 10381 | // Notes: |
| 10382 | // HandleRCEvent -- handle an IPC event received from the runtime controller. |
| 10383 | // This will update ICorDebug state and immediately dispatch the event. |
| 10384 | // |
| 10385 | //--------------------------------------------------------------------------------------- |
| 10386 | void CordbProcess::HandleRCEvent( |
| 10387 | DebuggerIPCEvent * pManagedEvent, |
| 10388 | RSLockHolder * pLockHolder, |
| 10389 | ICorDebugManagedCallback * pCallback) |
| 10390 | { |
| 10391 | CONTRACTL |
| 10392 | { |
| 10393 | THROWS; |
| 10394 | PRECONDITION(CheckPointer(pManagedEvent)); |
| 10395 | PRECONDITION(CheckPointer(pCallback)); |
| 10396 | PRECONDITION(ThreadHoldsProcessLock()); |
| 10397 | } |
| 10398 | CONTRACTL_END; |
| 10399 | |
| 10400 | if (!this->IsSafeToSendEvents() || this->m_exiting) |
| 10401 | { |
| 10402 | return; |
| 10403 | } |
| 10404 | |
| 10405 | // Marshals over some standard data from event. |
| 10406 | MarshalManagedEvent(pManagedEvent); |
| 10407 | |
| 10408 | STRESS_LOG4(LF_CORDB, LL_INFO1000, "RCET::TP: Got %s for AD 0x%x, proc 0x%x(%d)\n" , |
| 10409 | IPCENames::GetName(pManagedEvent->type), VmPtrToCookie(pManagedEvent->vmAppDomain), this->m_id, this->m_id); |
| 10410 | |
| 10411 | RSExtSmartPtr<ICorDebugManagedCallback2> pCallback2; |
| 10412 | pCallback->QueryInterface(IID_ICorDebugManagedCallback2, reinterpret_cast<void **> (&pCallback2)); |
| 10413 | |
| 10414 | RSExtSmartPtr<ICorDebugManagedCallback3> pCallback3; |
| 10415 | pCallback->QueryInterface(IID_ICorDebugManagedCallback3, reinterpret_cast<void **> (&pCallback3)); |
| 10416 | |
| 10417 | RSExtSmartPtr<ICorDebugManagedCallback4> pCallback4; |
| 10418 | pCallback->QueryInterface(IID_ICorDebugManagedCallback4, reinterpret_cast<void **> (&pCallback4)); |
| 10419 | |
| 10420 | // Dispatch directly. May not necessarily dispatch an event. |
| 10421 | // Toggles the lock to dispatch callbacks. |
| 10422 | RawDispatchEvent(pManagedEvent, pLockHolder, pCallback, pCallback2, pCallback3, pCallback4); |
| 10423 | } |
| 10424 | |
| 10425 | // |
| 10426 | // ProcessStateChanged -- tell the rc event thread that the ICorDebug's |
| 10427 | // process list has changed by setting its flag and thread control event. |
| 10428 | // This will cause the rc event thread to update its set of handles to wait |
| 10429 | // on. |
| 10430 | // |
| 10431 | void CordbRCEventThread::ProcessStateChanged() |
| 10432 | { |
| 10433 | m_cordb->LockProcessList(); |
| 10434 | STRESS_LOG0(LF_CORDB, LL_INFO100000, "CRCET::ProcessStateChanged\n" ); |
| 10435 | m_processStateChanged = TRUE; |
| 10436 | SetEvent(m_threadControlEvent); |
| 10437 | m_cordb->UnlockProcessList(); |
| 10438 | } |
| 10439 | |
| 10440 | |
| 10441 | //--------------------------------------------------------------------------------------- |
| 10442 | // Primary loop of the Runtime Controller event thread. This routine loops during the |
| 10443 | // debug session taking IPC events from the IPC block and calling out to process them. |
| 10444 | // |
| 10445 | // Arguments: |
| 10446 | // None. |
| 10447 | // |
| 10448 | // Return Value: |
| 10449 | // None. |
| 10450 | // |
| 10451 | // Notes: |
| 10452 | // @dbgtodo shim: eventually hoist the entire RCET into the shim. |
| 10453 | //--------------------------------------------------------------------------------------- |
| 10454 | void CordbRCEventThread::ThreadProc() |
| 10455 | { |
| 10456 | HANDLE waitSet[MAXIMUM_WAIT_OBJECTS]; |
| 10457 | CordbProcess * rgProcessSet[MAXIMUM_WAIT_OBJECTS]; |
| 10458 | unsigned int waitCount; |
| 10459 | |
| 10460 | #ifdef _DEBUG |
| 10461 | memset(&rgProcessSet, NULL, MAXIMUM_WAIT_OBJECTS * sizeof(CordbProcess *)); |
| 10462 | memset(&waitSet, NULL, MAXIMUM_WAIT_OBJECTS * sizeof(HANDLE)); |
| 10463 | #endif |
| 10464 | |
| 10465 | |
| 10466 | // First event to wait on is always the thread control event. |
| 10467 | waitSet[0] = m_threadControlEvent; |
| 10468 | rgProcessSet[0] = NULL; |
| 10469 | waitCount = 1; |
| 10470 | |
| 10471 | while (m_run) |
| 10472 | { |
| 10473 | DWORD dwStatus = WaitForMultipleObjectsEx(waitCount, waitSet, FALSE, 2000, FALSE); |
| 10474 | |
| 10475 | if (dwStatus == WAIT_FAILED) |
| 10476 | { |
| 10477 | STRESS_LOG1(LF_CORDB, LL_INFO10000, "CordbRCEventThread::ThreadProc WaitFor" |
| 10478 | "MultipleObjects failed: 0x%x\n" , GetLastError()); |
| 10479 | } |
| 10480 | #ifdef _DEBUG |
| 10481 | else if ((dwStatus >= WAIT_OBJECT_0) && (dwStatus < WAIT_OBJECT_0 + waitCount) && m_run) |
| 10482 | { |
| 10483 | // Got an event. Figure out which process it came from. |
| 10484 | unsigned int procNumber = dwStatus - WAIT_OBJECT_0; |
| 10485 | |
| 10486 | if (procNumber != 0) |
| 10487 | { |
| 10488 | // @dbgtodo shim: rip all of this out. Leave the assert in for now to verify that we're not accidentally |
| 10489 | // going down this codepath. Once we rip this out, we can also simplify some of the code below. |
| 10490 | // Notification events (including Sync-complete) should be coming from Win32 event thread via |
| 10491 | // V3 pipeline. |
| 10492 | _ASSERTE(!"Shouldn't be here" ); |
| 10493 | |
| 10494 | } |
| 10495 | } |
| 10496 | #endif |
| 10497 | |
| 10498 | // Empty any queued work items. |
| 10499 | DrainWorkerQueue(); |
| 10500 | |
| 10501 | // Check a flag to see if we need to update our list of processes to wait on. |
| 10502 | if (m_processStateChanged) |
| 10503 | { |
| 10504 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "RCET::TP: refreshing process list.\n" ); |
| 10505 | |
| 10506 | unsigned int i; |
| 10507 | |
| 10508 | // |
| 10509 | // free the old wait list |
| 10510 | // |
| 10511 | for (i = 1; i < waitCount; i++) |
| 10512 | { |
| 10513 | rgProcessSet[i]->InternalRelease(); |
| 10514 | } |
| 10515 | |
| 10516 | // Pass 1: iterate the hash of all processes and collect the unsynchronized ones into the wait list. |
| 10517 | // Note that Stop / Continue can still be called on a different thread while we're doing this. |
| 10518 | m_cordb->LockProcessList(); |
| 10519 | m_processStateChanged = FALSE; |
| 10520 | |
| 10521 | waitCount = 1; |
| 10522 | |
| 10523 | CordbSafeHashTable<CordbProcess> * pHashTable = m_cordb->GetProcessList(); |
| 10524 | HASHFIND hashFind; |
| 10525 | CordbProcess * pProcess; |
| 10526 | |
| 10527 | for (pProcess = pHashTable->FindFirst(&hashFind); pProcess != NULL; pProcess = pHashTable->FindNext(&hashFind)) |
| 10528 | { |
| 10529 | _ASSERTE(waitCount < MAXIMUM_WAIT_OBJECTS); |
| 10530 | |
| 10531 | if( waitCount >= MAXIMUM_WAIT_OBJECTS ) |
| 10532 | { |
| 10533 | break; |
| 10534 | } |
| 10535 | |
| 10536 | // Only listen to unsynchronized processes. Processes that are synchronized will not send events without |
| 10537 | // being asked by us first, so there is no need to async listen to them. |
| 10538 | // |
| 10539 | // Note: if a process is not synchronized then there is no way for it to transition to the syncrhonized |
| 10540 | // state without this thread receiving an event and taking action. So there is no need to lock the |
| 10541 | // per-process mutex when checking the process's synchronized flag here. |
| 10542 | if (!pProcess->GetSynchronized() && pProcess->IsSafeToSendEvents()) |
| 10543 | { |
| 10544 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "RCET::TP: listening to process 0x%x(%d)\n" , |
| 10545 | pProcess->m_id, pProcess->m_id); |
| 10546 | |
| 10547 | waitSet[waitCount] = pProcess->m_leftSideEventAvailable; |
| 10548 | rgProcessSet[waitCount] = pProcess; |
| 10549 | rgProcessSet[waitCount]->InternalAddRef(); |
| 10550 | waitCount++; |
| 10551 | } |
| 10552 | } |
| 10553 | |
| 10554 | m_cordb->UnlockProcessList(); |
| 10555 | |
| 10556 | // Pass 2: for each process that we placed in the wait list, determine if there are any existing queued |
| 10557 | // events that need to be flushed. |
| 10558 | |
| 10559 | // Start i at 1 to skip the control event... |
| 10560 | i = 1; |
| 10561 | |
| 10562 | while(i < waitCount) |
| 10563 | { |
| 10564 | pProcess = rgProcessSet[i]; |
| 10565 | |
| 10566 | // Take the process lock so we can check the queue safely |
| 10567 | pProcess->Lock(); |
| 10568 | |
| 10569 | // Now that we've just locked the processes, we can safely inspect it and dispatch events. |
| 10570 | // The process may have changed since when we first added it to the process list in Pass 1, |
| 10571 | // so we can't make any assumptions about whether it's sync, live, or exiting. |
| 10572 | |
| 10573 | // Flush the queue if necessary. Note, we only do this if we've actually received a SyncComplete message |
| 10574 | // from this process. If we haven't received a SyncComplete yet, then we don't attempt to drain any |
| 10575 | // queued events yet. They'll be drained when the SyncComplete event is actually received. |
| 10576 | if (pProcess->GetSyncCompleteRecv() && |
| 10577 | (pProcess->GetShim() != NULL) && |
| 10578 | !pProcess->GetSynchronized()) |
| 10579 | { |
| 10580 | if (pProcess->GetShim()->GetManagedEventQueue()->IsEmpty()) |
| 10581 | { |
| 10582 | // Effectively what we are doing here is to continue everything without actually |
| 10583 | // handling an event. We can get here if the event raised by the LS is a duplicate |
| 10584 | // creation event, which the shim discards without adding it to the event queue. |
| 10585 | // See code:ShimProcess::IsDuplicateCreationEvent. |
| 10586 | // |
| 10587 | // To continue, we need to increment the stop count first. Also, we can't call |
| 10588 | // Continue() while holding the process lock. |
| 10589 | pProcess->SetSynchronized(true); |
| 10590 | pProcess->IncStopCount(); |
| 10591 | pProcess->Unlock(); |
| 10592 | pProcess->ContinueInternal(FALSE); |
| 10593 | pProcess->Lock(); |
| 10594 | } |
| 10595 | else |
| 10596 | { |
| 10597 | // This may toggle the process-lock |
| 10598 | FlushQueuedEvents(pProcess); |
| 10599 | } |
| 10600 | } |
| 10601 | |
| 10602 | // Flushing could have left the process synchronized... |
| 10603 | // Common case is if the callback didn't call Continue(). |
| 10604 | if (pProcess->GetSynchronized()) |
| 10605 | { |
| 10606 | // remove the process from the wait list by moving all the other processes down one. |
| 10607 | if ((i + 1) < waitCount) |
| 10608 | { |
| 10609 | memcpy(&rgProcessSet[i], &(rgProcessSet[i+1]), sizeof(rgProcessSet[0]) * (waitCount - i - 1)); |
| 10610 | memcpy(&waitSet[i], &waitSet[i+1], sizeof(waitSet[0]) * (waitCount - i - 1)); |
| 10611 | } |
| 10612 | |
| 10613 | // drop the count of processes to wait on |
| 10614 | waitCount--; |
| 10615 | |
| 10616 | pProcess->Unlock(); |
| 10617 | |
| 10618 | // make sure to release the reference we added when the process was added to the wait list. |
| 10619 | pProcess->InternalRelease(); |
| 10620 | |
| 10621 | // We don't have to increment i because we've copied the next element into |
| 10622 | // the current value at i. |
| 10623 | } |
| 10624 | else |
| 10625 | { |
| 10626 | // Even after flushing, its still not syncd, so leave it in the wait list. |
| 10627 | pProcess->Unlock(); |
| 10628 | |
| 10629 | // Increment i normally. |
| 10630 | i++; |
| 10631 | } |
| 10632 | } |
| 10633 | } // end ProcessStateChanged |
| 10634 | } // while (m_run) |
| 10635 | |
| 10636 | #ifdef _DEBUG_IMPL |
| 10637 | // We intentionally return while leaking some CordbProcess objects inside |
| 10638 | // rgProcessSet, in some cases (e.g., I've seen this happen when detaching from a |
| 10639 | // debuggee almost immediately after attaching to it). In the future, we should |
| 10640 | // really consider not leaking these anymore. However, I'm unsure how safe it is to just |
| 10641 | // go and InternalRelease() those guys, as above we intentionally DON'T release them when |
| 10642 | // they're not synchronized. So for now, to make debug builds happy, exclude those |
| 10643 | // references when we run CheckMemLeaks() later on. In our next side-by-side release, |
| 10644 | // consider actually doing InternalRelease() on the remaining CordbProcesses on |
| 10645 | // retail, and then we can remove the following loop. |
| 10646 | for (UINT i=1; i < waitCount; i++) |
| 10647 | { |
| 10648 | InterlockedDecrement(&Cordb::s_DbgMemTotalOutstandingInternalRefs); |
| 10649 | } |
| 10650 | #endif //_DEBUG_IMPL |
| 10651 | } |
| 10652 | |
| 10653 | |
| 10654 | // |
| 10655 | // This is the thread's real thread proc. It simply calls to the |
| 10656 | // thread proc on the given object. |
| 10657 | // |
| 10658 | /*static*/ |
| 10659 | DWORD WINAPI CordbRCEventThread::ThreadProc(LPVOID parameter) |
| 10660 | { |
| 10661 | CordbRCEventThread * pThread = (CordbRCEventThread *) parameter; |
| 10662 | |
| 10663 | INTERNAL_THREAD_ENTRY(pThread); |
| 10664 | pThread->ThreadProc(); |
| 10665 | return 0; |
| 10666 | } |
| 10667 | |
| 10668 | template<typename T> |
| 10669 | InterlockedStack<T>::InterlockedStack() |
| 10670 | { |
| 10671 | m_pHead = NULL; |
| 10672 | } |
| 10673 | |
| 10674 | template<typename T> |
| 10675 | InterlockedStack<T>::~InterlockedStack() |
| 10676 | { |
| 10677 | // This is an arbitrary choice. We expect the stacks be drained. |
| 10678 | _ASSERTE(m_pHead == NULL); |
| 10679 | } |
| 10680 | |
| 10681 | // Thread safe pushes + pops. |
| 10682 | // Many threads can push simultaneously. |
| 10683 | // Only 1 thread can pop. |
| 10684 | template<typename T> |
| 10685 | void InterlockedStack<T>::Push(T * pItem) |
| 10686 | { |
| 10687 | // InterlockedCompareExchangePointer(&dest, ex, comp). |
| 10688 | // Really behaves like: |
| 10689 | // val = *dest; |
| 10690 | // if (*dest == comp) { *dest = ex; } |
| 10691 | // return val; |
| 10692 | // |
| 10693 | // We can do a thread-safe assign { comp = dest; dest = ex } via: |
| 10694 | // do { comp = dest } while (ICExPtr(&dest, ex, comp) != comp)); |
| 10695 | |
| 10696 | |
| 10697 | do |
| 10698 | { |
| 10699 | pItem->m_next = m_pHead; |
| 10700 | } |
| 10701 | while(InterlockedCompareExchangeT(&m_pHead, pItem, pItem->m_next) != pItem->m_next); |
| 10702 | } |
| 10703 | |
| 10704 | // Returns NULL on empty, |
| 10705 | // else returns the head of the list. |
| 10706 | template<typename T> |
| 10707 | T * InterlockedStack<T>::Pop() |
| 10708 | { |
| 10709 | if (m_pHead == NULL) |
| 10710 | { |
| 10711 | return NULL; |
| 10712 | } |
| 10713 | |
| 10714 | // This allows 1 thread to Pop() and race against N threads doing a Push(). |
| 10715 | T * pItem = NULL; |
| 10716 | do |
| 10717 | { |
| 10718 | pItem = m_pHead; |
| 10719 | } while(InterlockedCompareExchangeT(&m_pHead, pItem->m_next, pItem) != pItem); |
| 10720 | |
| 10721 | return pItem; |
| 10722 | } |
| 10723 | |
| 10724 | |
| 10725 | // RCET will take ownership of this item and delete it. |
| 10726 | // This can be done w/o taking any locks (thus it can be called from any lock context) |
| 10727 | // This may race w/ the RCET draining the queue. |
| 10728 | void CordbRCEventThread::QueueAsyncWorkItem(RCETWorkItem * pItem) |
| 10729 | { |
| 10730 | // @todo - |
| 10731 | // Non-blocking insert into queue. |
| 10732 | |
| 10733 | _ASSERTE(pItem != NULL); |
| 10734 | |
| 10735 | m_WorkerStack.Push(pItem); |
| 10736 | |
| 10737 | // Ping the RCET so that it drains the queue. |
| 10738 | SetEvent(m_threadControlEvent); |
| 10739 | } |
| 10740 | |
| 10741 | // Execute & delete all workitems in the queue. |
| 10742 | // This can be done w/o taking any locks. (though individual items may take locks). |
| 10743 | void CordbRCEventThread::DrainWorkerQueue() |
| 10744 | { |
| 10745 | _ASSERTE(IsRCEventThread()); |
| 10746 | |
| 10747 | while(true) |
| 10748 | { |
| 10749 | RCETWorkItem* pCur = m_WorkerStack.Pop(); |
| 10750 | if (pCur == NULL) |
| 10751 | { |
| 10752 | break; |
| 10753 | } |
| 10754 | |
| 10755 | pCur->Do(); |
| 10756 | delete pCur; |
| 10757 | } |
| 10758 | } |
| 10759 | |
| 10760 | |
| 10761 | //--------------------------------------------------------------------------------------- |
| 10762 | // Wait for an reply from the debuggee. |
| 10763 | // |
| 10764 | // Arguments: |
| 10765 | // pProcess - process for debuggee. |
| 10766 | // pAppDomain - not used. |
| 10767 | // pEvent - caller-allocated event to be filled out. |
| 10768 | // This is expected to be at least as big as CorDBIPC_BUFFER_SIZE. |
| 10769 | // |
| 10770 | // Return Value: |
| 10771 | // S_OK on success. else failure. |
| 10772 | // |
| 10773 | // Assumptions: |
| 10774 | // Caller allocates |
| 10775 | // |
| 10776 | // Notes: |
| 10777 | // WaitForIPCEventFromProcess waits for an event from just the specified |
| 10778 | // process. This should only be called when the process is in a synchronized |
| 10779 | // state, which ensures that the RCEventThread isn't listening to the |
| 10780 | // process's event, too, which would get confusing. |
| 10781 | // |
| 10782 | // @dbgtodo - this function should eventually be obsolete once everything |
| 10783 | // is using DAC calls instead of helper-thread. |
| 10784 | // |
| 10785 | //--------------------------------------------------------------------------------------- |
| 10786 | HRESULT CordbRCEventThread::WaitForIPCEventFromProcess(CordbProcess * pProcess, |
| 10787 | CordbAppDomain * pAppDomain, |
| 10788 | DebuggerIPCEvent * pEvent) |
| 10789 | { |
| 10790 | CORDBRequireProcessStateOKAndSync(pProcess, pAppDomain); |
| 10791 | |
| 10792 | DWORD dwStatus; |
| 10793 | HRESULT hr = S_OK; |
| 10794 | |
| 10795 | do |
| 10796 | { |
| 10797 | dwStatus = SafeWaitForSingleObject(pProcess, |
| 10798 | pProcess->m_leftSideEventAvailable, |
| 10799 | CordbGetWaitTimeout()); |
| 10800 | |
| 10801 | if (pProcess->m_terminated) |
| 10802 | { |
| 10803 | return CORDBG_E_PROCESS_TERMINATED; |
| 10804 | } |
| 10805 | // If we timeout because we're waiting for an uncontinued OOB event, we need to just keep waiting. |
| 10806 | } while ((dwStatus == WAIT_TIMEOUT) && pProcess->IsWaitingForOOBEvent()); |
| 10807 | |
| 10808 | |
| 10809 | |
| 10810 | |
| 10811 | if (dwStatus == WAIT_OBJECT_0) |
| 10812 | { |
| 10813 | pProcess->CopyRCEventFromIPCBlock(pEvent); |
| 10814 | |
| 10815 | EX_TRY |
| 10816 | { |
| 10817 | pProcess->MarshalManagedEvent(pEvent); |
| 10818 | |
| 10819 | STRESS_LOG4(LF_CORDB, LL_INFO1000, "CRCET::SIPCE: Got %s for AD 0x%x, proc 0x%x(%d)\n" , |
| 10820 | IPCENames::GetName(pEvent->type), |
| 10821 | VmPtrToCookie(pEvent->vmAppDomain), |
| 10822 | pProcess->m_id, |
| 10823 | pProcess->m_id); |
| 10824 | |
| 10825 | } |
| 10826 | EX_CATCH_HRESULT(hr) |
| 10827 | |
| 10828 | SetEvent(pProcess->m_leftSideEventRead); |
| 10829 | |
| 10830 | return hr; |
| 10831 | } |
| 10832 | else if (dwStatus == WAIT_TIMEOUT) |
| 10833 | { |
| 10834 | // |
| 10835 | // If we timed out, check the left side to see if it is in the |
| 10836 | // unrecoverable error mode. If it is, return the HR from the |
| 10837 | // left side that caused the error. Otherwise, return that we timed |
| 10838 | // out and that we don't really know why. |
| 10839 | // |
| 10840 | HRESULT realHR = ErrWrapper(CORDBG_E_TIMEOUT); |
| 10841 | |
| 10842 | hr = pProcess->CheckForUnrecoverableError(); |
| 10843 | |
| 10844 | if (hr == S_OK) |
| 10845 | { |
| 10846 | CORDBSetUnrecoverableError(pProcess, realHR, 0); |
| 10847 | return realHR; |
| 10848 | } |
| 10849 | else |
| 10850 | return hr; |
| 10851 | } |
| 10852 | else |
| 10853 | { |
| 10854 | _ASSERTE(dwStatus == WAIT_FAILED); |
| 10855 | |
| 10856 | hr = HRESULT_FROM_GetLastError(); |
| 10857 | |
| 10858 | CORDBSetUnrecoverableError(pProcess, hr, 0); |
| 10859 | |
| 10860 | return hr; |
| 10861 | } |
| 10862 | } |
| 10863 | |
| 10864 | |
| 10865 | // |
| 10866 | // Start actually creates and starts the thread. |
| 10867 | // |
| 10868 | HRESULT CordbRCEventThread::Start() |
| 10869 | { |
| 10870 | if (m_threadControlEvent == NULL) |
| 10871 | { |
| 10872 | return E_INVALIDARG; |
| 10873 | } |
| 10874 | |
| 10875 | m_thread = CreateThread(NULL, |
| 10876 | 0, |
| 10877 | &CordbRCEventThread::ThreadProc, |
| 10878 | (LPVOID) this, |
| 10879 | 0, |
| 10880 | &m_threadId); |
| 10881 | |
| 10882 | if (m_thread == NULL) |
| 10883 | { |
| 10884 | return HRESULT_FROM_GetLastError(); |
| 10885 | } |
| 10886 | |
| 10887 | return S_OK; |
| 10888 | } |
| 10889 | |
| 10890 | |
| 10891 | // |
| 10892 | // Stop causes the thread to stop receiving events and exit. It |
| 10893 | // waits for it to exit before returning. |
| 10894 | // |
| 10895 | HRESULT CordbRCEventThread::Stop() |
| 10896 | { |
| 10897 | if (m_thread != NULL) |
| 10898 | { |
| 10899 | LOG((LF_CORDB, LL_INFO100000, "CRCET::Stop\n" )); |
| 10900 | |
| 10901 | m_run = FALSE; |
| 10902 | |
| 10903 | SetEvent(m_threadControlEvent); |
| 10904 | |
| 10905 | DWORD ret = WaitForSingleObject(m_thread, INFINITE); |
| 10906 | |
| 10907 | if (ret != WAIT_OBJECT_0) |
| 10908 | { |
| 10909 | return HRESULT_FROM_GetLastError(); |
| 10910 | } |
| 10911 | } |
| 10912 | |
| 10913 | m_cordb.Clear(); |
| 10914 | |
| 10915 | return S_OK; |
| 10916 | } |
| 10917 | |
| 10918 | |
| 10919 | /* ------------------------------------------------------------------------- * |
| 10920 | * Win32 Event Thread class |
| 10921 | * ------------------------------------------------------------------------- */ |
| 10922 | |
| 10923 | enum |
| 10924 | { |
| 10925 | W32ETA_NONE = 0, |
| 10926 | W32ETA_CREATE_PROCESS = 1, |
| 10927 | W32ETA_ATTACH_PROCESS = 2, |
| 10928 | W32ETA_CONTINUE = 3, |
| 10929 | W32ETA_DETACH = 4 |
| 10930 | }; |
| 10931 | |
| 10932 | |
| 10933 | |
| 10934 | //--------------------------------------------------------------------------------------- |
| 10935 | // Constructor |
| 10936 | // |
| 10937 | // Arguments: |
| 10938 | // pCordb - Pointer to the owning cordb object for this event thread. |
| 10939 | // pShim - Pointer to the shim for supporting V2 debuggers on V3 architecture. |
| 10940 | // |
| 10941 | //--------------------------------------------------------------------------------------- |
| 10942 | CordbWin32EventThread::CordbWin32EventThread( |
| 10943 | Cordb * pCordb, |
| 10944 | ShimProcess * pShim |
| 10945 | ) : |
| 10946 | m_thread(NULL), m_threadControlEvent(NULL), |
| 10947 | m_actionTakenEvent(NULL), m_run(TRUE), |
| 10948 | m_action(W32ETA_NONE) |
| 10949 | { |
| 10950 | m_cordb.Assign(pCordb); |
| 10951 | _ASSERTE(pCordb != NULL); |
| 10952 | |
| 10953 | m_pShim = pShim; |
| 10954 | |
| 10955 | m_pNativePipeline = NULL; |
| 10956 | } |
| 10957 | |
| 10958 | |
| 10959 | // |
| 10960 | // Destructor. Cleans up all of the open handles and such. |
| 10961 | // This expects that the thread has been stopped and has terminated |
| 10962 | // before being called. |
| 10963 | // |
| 10964 | CordbWin32EventThread::~CordbWin32EventThread() |
| 10965 | { |
| 10966 | if (m_thread != NULL) |
| 10967 | CloseHandle(m_thread); |
| 10968 | |
| 10969 | if (m_threadControlEvent != NULL) |
| 10970 | CloseHandle(m_threadControlEvent); |
| 10971 | |
| 10972 | if (m_actionTakenEvent != NULL) |
| 10973 | CloseHandle(m_actionTakenEvent); |
| 10974 | |
| 10975 | if (m_pNativePipeline != NULL) |
| 10976 | { |
| 10977 | m_pNativePipeline->Delete(); |
| 10978 | m_pNativePipeline = NULL; |
| 10979 | } |
| 10980 | |
| 10981 | m_sendToWin32EventThreadMutex.Destroy(); |
| 10982 | } |
| 10983 | |
| 10984 | |
| 10985 | // |
| 10986 | // Init sets up all the objects that the thread will need to run. |
| 10987 | // |
| 10988 | HRESULT CordbWin32EventThread::Init() |
| 10989 | { |
| 10990 | if (m_cordb == NULL) |
| 10991 | return E_INVALIDARG; |
| 10992 | |
| 10993 | m_sendToWin32EventThreadMutex.Init("Win32-Send lock" , RSLock::cLockFlat, RSLock::LL_WIN32_SEND_LOCK); |
| 10994 | |
| 10995 | m_threadControlEvent = WszCreateEvent(NULL, FALSE, FALSE, NULL); |
| 10996 | if (m_threadControlEvent == NULL) |
| 10997 | return HRESULT_FROM_GetLastError(); |
| 10998 | |
| 10999 | m_actionTakenEvent = WszCreateEvent(NULL, FALSE, FALSE, NULL); |
| 11000 | if (m_actionTakenEvent == NULL) |
| 11001 | return HRESULT_FROM_GetLastError(); |
| 11002 | |
| 11003 | m_pNativePipeline = NewPipelineWithDebugChecks(); |
| 11004 | if (m_pNativePipeline == NULL) |
| 11005 | { |
| 11006 | return E_OUTOFMEMORY; |
| 11007 | } |
| 11008 | |
| 11009 | return S_OK; |
| 11010 | } |
| 11011 | |
| 11012 | // |
| 11013 | // Main function of the Win32 Event Thread |
| 11014 | // |
| 11015 | void CordbWin32EventThread::ThreadProc() |
| 11016 | { |
| 11017 | #if defined(RSCONTRACTS) |
| 11018 | DbgRSThread::GetThread()->SetThreadType(DbgRSThread::cW32ET); |
| 11019 | |
| 11020 | // The win32 ET conceptually holds a lock (all threads do). |
| 11021 | DbgRSThread::GetThread()->TakeVirtualLock(RSLock::LL_WIN32_EVENT_THREAD); |
| 11022 | #endif |
| 11023 | |
| 11024 | // In V2, the debuggee decides what to do if the debugger rudely exits / detaches. (This is |
| 11025 | // handled by host policy). With the OS native-debuggging pipeline, the debugger by default |
| 11026 | // kills the debuggee if it exits. To emulate V2 behavior, we need to override that default. |
| 11027 | BOOL fOk = m_pNativePipeline->DebugSetProcessKillOnExit(FALSE); |
| 11028 | (void)fOk; //prevent "unused variable" error from GCC |
| 11029 | _ASSERTE(fOk); |
| 11030 | |
| 11031 | |
| 11032 | // Run the top-level event loop. |
| 11033 | Win32EventLoop(); |
| 11034 | |
| 11035 | #if defined(RSCONTRACTS) |
| 11036 | // The win32 ET conceptually holds a lock (all threads do). |
| 11037 | DbgRSThread::GetThread()->ReleaseVirtualLock(RSLock::LL_WIN32_EVENT_THREAD); |
| 11038 | #endif |
| 11039 | } |
| 11040 | |
| 11041 | // Define a holder that calls code:DeleteIPCEventHelper |
| 11042 | NEW_WRAPPER_TEMPLATE1(DeleteIPCEventHolderHelper, DeleteIPCEventHelper); |
| 11043 | typedef DeleteIPCEventHolderHelper<DebuggerIPCEvent> DeleteIPCEventHolder; |
| 11044 | |
| 11045 | //--------------------------------------------------------------------------------------- |
| 11046 | // |
| 11047 | // Helper to clean up IPCEvent before deleting it. |
| 11048 | // This must be called after an event is marshalled via code:CordbProcess::MarshalManagedEvent |
| 11049 | // |
| 11050 | // Arguments: |
| 11051 | // pManagedEvent - managed event to delete. |
| 11052 | // |
| 11053 | // Notes: |
| 11054 | // This can delete a partially marshalled event. |
| 11055 | // |
| 11056 | void DeleteIPCEventHelper(DebuggerIPCEvent *pManagedEvent) |
| 11057 | { |
| 11058 | CONTRACTL |
| 11059 | { |
| 11060 | // This is backout code that shouldn't need to throw. |
| 11061 | NOTHROW; |
| 11062 | } |
| 11063 | CONTRACTL_END; |
| 11064 | if (pManagedEvent == NULL) |
| 11065 | { |
| 11066 | return; |
| 11067 | } |
| 11068 | switch (pManagedEvent->type & DB_IPCE_TYPE_MASK) |
| 11069 | { |
| 11070 | // so far only this event need to cleanup. |
| 11071 | case DB_IPCE_MDA_NOTIFICATION: |
| 11072 | pManagedEvent->MDANotification.szName.CleanUp(); |
| 11073 | pManagedEvent->MDANotification.szDescription.CleanUp(); |
| 11074 | pManagedEvent->MDANotification.szXml.CleanUp(); |
| 11075 | break; |
| 11076 | |
| 11077 | case DB_IPCE_FIRST_LOG_MESSAGE: |
| 11078 | pManagedEvent->FirstLogMessage.szContent.CleanUp(); |
| 11079 | break; |
| 11080 | |
| 11081 | default: |
| 11082 | break; |
| 11083 | } |
| 11084 | delete [] (BYTE *)pManagedEvent; |
| 11085 | } |
| 11086 | |
| 11087 | //--------------------------------------------------------------------------------------- |
| 11088 | // Handle a CLR specific notification event. |
| 11089 | // |
| 11090 | // Arguments: |
| 11091 | // pManagedEvent - non-null pointer to a managed event. |
| 11092 | // pLockHolder - hold to process lock that gets toggled if this dispatches an event. |
| 11093 | // pCallback - callback to dispatch potential managed events. |
| 11094 | // |
| 11095 | // Return Value: |
| 11096 | // Throws on error. |
| 11097 | // |
| 11098 | // Assumptions: |
| 11099 | // Target is stopped. Record was already determined to be a CLR event. |
| 11100 | // |
| 11101 | // Notes: |
| 11102 | // This is called after caller does WaitForDebugEvent. |
| 11103 | // Any exception this Filter does not recognize is treated as kNotClr. |
| 11104 | // Currently, this includes both managed-exceptions and unmanaged ones. |
| 11105 | // For interop-debugging, the interop logic will handle all kNotClr and triage if |
| 11106 | // it's really a non-CLR exception. |
| 11107 | // |
| 11108 | //--------------------------------------------------------------------------------------- |
| 11109 | void CordbProcess::FilterClrNotification( |
| 11110 | DebuggerIPCEvent * pManagedEvent, |
| 11111 | RSLockHolder * pLockHolder, |
| 11112 | ICorDebugManagedCallback * pCallback) |
| 11113 | { |
| 11114 | CONTRACTL |
| 11115 | { |
| 11116 | THROWS; |
| 11117 | PRECONDITION(CheckPointer(pManagedEvent)); |
| 11118 | PRECONDITION(CheckPointer(pCallback)); |
| 11119 | PRECONDITION(ThreadHoldsProcessLock()); |
| 11120 | } |
| 11121 | CONTRACTL_END; |
| 11122 | |
| 11123 | // There are 3 types of events from the LS: |
| 11124 | // 1) Replies (eg, corresponding to WaitForIPCEvent) |
| 11125 | // we need to set LSEA/wait on LSER. |
| 11126 | // 2) Sync-Complete (kind of like a special notification) |
| 11127 | // Ping the helper |
| 11128 | // 3) Notifications (eg, Module-load): |
| 11129 | // these are dispatched immediately. |
| 11130 | // 4) Left-side Startup event |
| 11131 | |
| 11132 | |
| 11133 | // IF we're synced, then we must be getting a "Reply". |
| 11134 | bool fReply = this->GetSynchronized(); |
| 11135 | |
| 11136 | LOG((LF_CORDB, LL_INFO10000, "CP::FCN - Received event %s; fReply: %d\n" , |
| 11137 | IPCENames::GetName(pManagedEvent->type), |
| 11138 | fReply)); |
| 11139 | |
| 11140 | if (fReply) |
| 11141 | { |
| 11142 | // |
| 11143 | _ASSERTE(m_pShim != NULL); |
| 11144 | // |
| 11145 | // Case 1: Reply |
| 11146 | // |
| 11147 | |
| 11148 | pLockHolder->Release(); |
| 11149 | _ASSERTE(!ThreadHoldsProcessLock()); |
| 11150 | |
| 11151 | // Save the IPC event and wake up the thread which is waiting for it from the LS. |
| 11152 | GetEventChannel()->SaveEventFromLeftSide(pManagedEvent); |
| 11153 | SetEvent(this->m_leftSideEventAvailable); |
| 11154 | |
| 11155 | // Some other thread called code:CordbRCEventThread::WaitForIPCEventFromProcess, and |
| 11156 | // that will respond here and set the event. |
| 11157 | |
| 11158 | DWORD dwResult = WaitForSingleObject(this->m_leftSideEventRead, CordbGetWaitTimeout()); |
| 11159 | pLockHolder->Acquire(); |
| 11160 | if (dwResult != WAIT_OBJECT_0) |
| 11161 | { |
| 11162 | // The wait failed. This is probably WAIT_TIMEOUT which suggests a deadlock/assert on |
| 11163 | // the RCEventThread. |
| 11164 | CONSISTENCY_CHECK_MSGF(false, ("WaitForSingleObject failed: %d" , dwResult)); |
| 11165 | ThrowHR(CORDBG_E_TIMEOUT); |
| 11166 | } |
| 11167 | } |
| 11168 | else |
| 11169 | { |
| 11170 | if (pManagedEvent->type == DB_IPCE_LEFTSIDE_STARTUP) |
| 11171 | { |
| 11172 | // |
| 11173 | // Case 4: Left-side startup event. We'll mark that we're attached from oop. |
| 11174 | // |
| 11175 | |
| 11176 | // Now that LS is started, we should definitely be able to instantiate DAC. |
| 11177 | InitializeDac(); |
| 11178 | |
| 11179 | // @dbgtodo 'attach-bit': we don't want the debugger automatically invading the process. |
| 11180 | GetDAC()->MarkDebuggerAttached(TRUE); |
| 11181 | } |
| 11182 | else if (pManagedEvent->type == DB_IPCE_SYNC_COMPLETE) |
| 11183 | { |
| 11184 | // Since V3 doesn't request syncs, it shouldn't get sync-complete. |
| 11185 | // @dbgtodo sync: this changes when V3 can explicitly request an AsyncBreak. |
| 11186 | _ASSERTE(m_pShim != NULL); |
| 11187 | |
| 11188 | // |
| 11189 | // Case 2: Sync Complete |
| 11190 | // |
| 11191 | |
| 11192 | HandleSyncCompleteRecieved(); |
| 11193 | } |
| 11194 | else |
| 11195 | { |
| 11196 | // |
| 11197 | // Case 3: Notification. This will dispatch the event immediately. |
| 11198 | // |
| 11199 | |
| 11200 | // Toggles the process-lock if it dispatches callbacks. |
| 11201 | HandleRCEvent(pManagedEvent, pLockHolder, pCallback); |
| 11202 | |
| 11203 | } // end Notification |
| 11204 | } |
| 11205 | } |
| 11206 | |
| 11207 | |
| 11208 | |
| 11209 | // |
| 11210 | // If the thread has an unhandled managed exception, hijack it. |
| 11211 | // |
| 11212 | // Arguments: |
| 11213 | // dwThreadId - OS Thread id. |
| 11214 | // |
| 11215 | // Returns: |
| 11216 | // True if hijacked; false if not. |
| 11217 | // |
| 11218 | // Notes: |
| 11219 | // This is called from shim to emulate being synchronized at an unhandled |
| 11220 | // exception. |
| 11221 | // Other ICorDebug operations could calls this (eg, func-eval at 2nd chance). |
| 11222 | BOOL CordbProcess::HijackThreadForUnhandledExceptionIfNeeded(DWORD dwThreadId) |
| 11223 | { |
| 11224 | PUBLIC_API_ENTRY(this); // from Shim |
| 11225 | |
| 11226 | BOOL fHijacked = FALSE; |
| 11227 | HRESULT hr = S_OK; |
| 11228 | EX_TRY |
| 11229 | { |
| 11230 | RSLockHolder lockHolder(GetProcessLock()); |
| 11231 | |
| 11232 | // OS will not execute the Unhandled Exception Filter under native debugger, so |
| 11233 | // we need to hijack the thread to get it to execute the UEF, which will then do |
| 11234 | // work for unhandled managed exceptions. |
| 11235 | CordbThread * pThread = TryLookupOrCreateThreadByVolatileOSId(dwThreadId); |
| 11236 | if (pThread != NULL) |
| 11237 | { |
| 11238 | // If the thread has a managed exception, then we should have a pThread object. |
| 11239 | |
| 11240 | if (pThread->HasUnhandledNativeException()) |
| 11241 | { |
| 11242 | _ASSERTE(pThread->IsThreadExceptionManaged()); // should have been marked earlier |
| 11243 | |
| 11244 | pThread->HijackForUnhandledException(); |
| 11245 | fHijacked = TRUE; |
| 11246 | } |
| 11247 | } |
| 11248 | } |
| 11249 | EX_CATCH_HRESULT(hr); |
| 11250 | SIMPLIFYING_ASSUMPTION(SUCCEEDED(hr)); |
| 11251 | |
| 11252 | return fHijacked; |
| 11253 | } |
| 11254 | |
| 11255 | //--------------------------------------------------------------------------------------- |
| 11256 | // Validate the given exception record or throw. |
| 11257 | // |
| 11258 | // Arguments: |
| 11259 | // pRawRecord - non-null raw bytes of the exception |
| 11260 | // countBytes - number of bytes in pRawRecord buffer. |
| 11261 | // format - format of pRawRecord |
| 11262 | // |
| 11263 | // Returns: |
| 11264 | // A type-safe exception record from the raw buffer. |
| 11265 | // |
| 11266 | // Notes: |
| 11267 | // This is a helper for code:CordbProcess::Filter. |
| 11268 | // This can do consistency checks on the incoming parameters such as: |
| 11269 | // * verify countBytes matches the expected size for the given format. |
| 11270 | // * verify the format is supported. |
| 11271 | // |
| 11272 | // If we let a given ICD understand multiple formats (eg, have x86 understand both Exr32 and |
| 11273 | // Exr64), this would be the spot to allow the conversion. |
| 11274 | // |
| 11275 | const EXCEPTION_RECORD * CordbProcess::ValidateExceptionRecord( |
| 11276 | const BYTE pRawRecord[], |
| 11277 | DWORD countBytes, |
| 11278 | CorDebugRecordFormat format) |
| 11279 | { |
| 11280 | ValidateOrThrow(pRawRecord); |
| 11281 | |
| 11282 | // |
| 11283 | // Check format against expected platform. |
| 11284 | // |
| 11285 | |
| 11286 | // @dbgtodo - , cross-plat: Once we do cross-plat, these should be based off target-architecture not host's. |
| 11287 | #if defined(_WIN64) |
| 11288 | if (format != FORMAT_WINDOWS_EXCEPTIONRECORD64) |
| 11289 | { |
| 11290 | ThrowHR(E_INVALIDARG); |
| 11291 | } |
| 11292 | #else |
| 11293 | if (format != FORMAT_WINDOWS_EXCEPTIONRECORD32) |
| 11294 | { |
| 11295 | ThrowHR(E_INVALIDARG); |
| 11296 | } |
| 11297 | #endif |
| 11298 | |
| 11299 | // @dbgtodo cross-plat: once we do cross-plat, need to use correct EXCEPTION_RECORD variant. |
| 11300 | if (countBytes != sizeof(EXCEPTION_RECORD)) |
| 11301 | { |
| 11302 | ThrowHR(E_INVALIDARG); |
| 11303 | } |
| 11304 | |
| 11305 | |
| 11306 | const EXCEPTION_RECORD * pRecord = reinterpret_cast<const EXCEPTION_RECORD *> (pRawRecord); |
| 11307 | |
| 11308 | return pRecord; |
| 11309 | }; |
| 11310 | |
| 11311 | // Return value: S_OK or indication that no more room exists for enabled types |
| 11312 | HRESULT CordbProcess::SetEnableCustomNotification(ICorDebugClass * pClass, BOOL fEnable) |
| 11313 | { |
| 11314 | HRESULT hr = S_OK; |
| 11315 | PUBLIC_API_BEGIN(this); // takes the lock |
| 11316 | |
| 11317 | ValidateOrThrow(pClass); |
| 11318 | |
| 11319 | ((CordbClass *)pClass)->SetCustomNotifications(fEnable); |
| 11320 | |
| 11321 | PUBLIC_API_END(hr); |
| 11322 | return hr; |
| 11323 | } // CordbProcess::SetEnableCustomNotification |
| 11324 | |
| 11325 | //--------------------------------------------------------------------------------------- |
| 11326 | // Public implementation of ICDProcess4::Filter |
| 11327 | // |
| 11328 | // Arguments: |
| 11329 | // pRawRecord - non-null raw bytes of the exception |
| 11330 | // countBytes - number of bytes in pRawRecord buffer. |
| 11331 | // format - format of pRawRecord |
| 11332 | // dwFlags - flags providing auxillary info for exception record. |
| 11333 | // dwThreadId - thread that exception occurred on. |
| 11334 | // pCallback - callback to dispatch potential managed events on. |
| 11335 | // pContinueStatus - Continuation status for exception. This dictates what |
| 11336 | // to pass to kernel32!ContinueDebugEvent(). |
| 11337 | // |
| 11338 | // Return Value: |
| 11339 | // S_OK on success. |
| 11340 | // |
| 11341 | // Assumptions: |
| 11342 | // Target is stopped. |
| 11343 | // |
| 11344 | // Notes: |
| 11345 | // The exception could be anything, including: |
| 11346 | // - a CLR notification, |
| 11347 | // - a random managed exception (both from managed code or the runtime), |
| 11348 | // - a non-CLR exception |
| 11349 | // |
| 11350 | // This is cross-platform. The {pRawRecord, countBytes, format} describe events |
| 11351 | // on an arbitrary target architecture. On windows, this will be an EXCEPTION_RECORD. |
| 11352 | // |
| 11353 | HRESULT CordbProcess::Filter( |
| 11354 | const BYTE pRawRecord[], |
| 11355 | DWORD countBytes, |
| 11356 | CorDebugRecordFormat format, |
| 11357 | DWORD dwFlags, |
| 11358 | DWORD dwThreadId, |
| 11359 | ICorDebugManagedCallback * pCallback, |
| 11360 | DWORD * pContinueStatus |
| 11361 | ) |
| 11362 | { |
| 11363 | HRESULT hr = S_OK; |
| 11364 | PUBLIC_API_BEGIN(this); // takes the lock |
| 11365 | { |
| 11366 | // |
| 11367 | // Validate parameters |
| 11368 | // |
| 11369 | |
| 11370 | // If we don't care about the continue status, we leave it untouched. |
| 11371 | ValidateOrThrow(pContinueStatus); |
| 11372 | ValidateOrThrow(pCallback); |
| 11373 | |
| 11374 | const EXCEPTION_RECORD * pRecord = ValidateExceptionRecord(pRawRecord, countBytes, format); |
| 11375 | |
| 11376 | DWORD dwFirstChance = (dwFlags & IS_FIRST_CHANCE); |
| 11377 | |
| 11378 | // |
| 11379 | // Deal with 2nd-chance exceptions. Don't actually hijack now (that's too invasive), |
| 11380 | // but mark that we have the exception in case a future operation (eg, func-eval) needs to hijack. |
| 11381 | // |
| 11382 | if (!dwFirstChance) |
| 11383 | { |
| 11384 | CordbThread * pThread = TryLookupOrCreateThreadByVolatileOSId(dwThreadId); |
| 11385 | |
| 11386 | // If we don't have a managed-thread object, then it certainly can't have a throwable. |
| 11387 | // It's possible this is still an exception from the native portion of the runtime, |
| 11388 | // but that's ok, we'll just treat it as a native exception. |
| 11389 | // This could be expensive, don't want to do it often... (definitely not on every Filter). |
| 11390 | |
| 11391 | |
| 11392 | // OS will not execute the Unhandled Exception Filter under native debugger, so |
| 11393 | // we need to hijack the thread to get it to execute the UEF, which will then do |
| 11394 | // work for unhandled managed exceptions. |
| 11395 | if ((pThread != NULL) && pThread->IsThreadExceptionManaged()) |
| 11396 | { |
| 11397 | // Copy exception record for future use in case we decide to hijack. |
| 11398 | pThread->SetUnhandledNativeException(pRecord); |
| 11399 | } |
| 11400 | // we don't care about 2nd-chance exceptions, unless we decide to hijack it later. |
| 11401 | } |
| 11402 | |
| 11403 | // |
| 11404 | // Deal with CLR notifications |
| 11405 | // |
| 11406 | else if (pRecord->ExceptionCode == CLRDBG_NOTIFICATION_EXCEPTION_CODE) // Special event code |
| 11407 | { |
| 11408 | // |
| 11409 | // This may not be for us, or we may not have a managed thread object: |
| 11410 | // 1. Anybody can raise an exception with this exception code, so can't assume this belongs to us yet. |
| 11411 | // 2. Notifications may come on unmanaged threads if they're coming from MDAs or CLR internal events |
| 11412 | // fired before the thread is created. |
| 11413 | // |
| 11414 | BYTE * pManagedEventBuffer = new BYTE[CorDBIPC_BUFFER_SIZE]; |
| 11415 | DeleteIPCEventHolder pManagedEvent(reinterpret_cast<DebuggerIPCEvent *>(pManagedEventBuffer)); |
| 11416 | |
| 11417 | bool fOwner = CopyManagedEventFromTarget(pRecord, pManagedEvent); |
| 11418 | if (fOwner) |
| 11419 | { |
| 11420 | // This toggles the lock if it dispatches callbacks |
| 11421 | FilterClrNotification(pManagedEvent, GET_PUBLIC_LOCK_HOLDER(), pCallback); |
| 11422 | |
| 11423 | // Cancel any notification events from target. These are just supposed to notify ICD and not |
| 11424 | // actually be real exceptions in the target. |
| 11425 | // Canceling here also prevents a VectoredExceptionHandler in the target from picking |
| 11426 | // up exceptions for the CLR. |
| 11427 | *pContinueStatus = DBG_CONTINUE; |
| 11428 | } |
| 11429 | |
| 11430 | // holder will invoke DeleteIPCEventHelper(pManagedEvent). |
| 11431 | } |
| 11432 | |
| 11433 | } |
| 11434 | PUBLIC_API_END(hr); |
| 11435 | // we may not find the correct mscordacwks so fail gracefully |
| 11436 | _ASSERTE(SUCCEEDED(hr) || (hr != HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND))); |
| 11437 | |
| 11438 | return hr; |
| 11439 | } |
| 11440 | |
| 11441 | //--------------------------------------------------------------------------------------- |
| 11442 | // Wrapper to invoke ICorDebugMutableDataTarget::ContinueStatusChanged |
| 11443 | // |
| 11444 | // Arguments: |
| 11445 | // dwContinueStatus - new continue status |
| 11446 | // |
| 11447 | // Returns: |
| 11448 | // None. Throw on error. |
| 11449 | // |
| 11450 | // Notes: |
| 11451 | // Initial continue status is returned from code:CordbProcess::Filter. |
| 11452 | // Some operations (mainly hijacking on a 2nd-chance exception), may need to |
| 11453 | // override that continue status. |
| 11454 | // ICorDebug operations invoke a callback on the data-target to notify the debugger |
| 11455 | // of a change in status. Debugger may fail the request. |
| 11456 | // |
| 11457 | void CordbProcess::ContinueStatusChanged(DWORD dwThreadId, CORDB_CONTINUE_STATUS dwContinueStatus) |
| 11458 | { |
| 11459 | HRESULT hr = m_pMutableDataTarget->ContinueStatusChanged(dwThreadId, dwContinueStatus); |
| 11460 | IfFailThrow(hr); |
| 11461 | } |
| 11462 | |
| 11463 | //--------------------------------------------------------------------------------------- |
| 11464 | // Request a synchronization to occur after a debug event is dispatched. |
| 11465 | // |
| 11466 | // Note: |
| 11467 | // This is called in response to a managed debug event, and so we know that we have |
| 11468 | // a worker thread in the process (the one that just sent the event!) |
| 11469 | // This can not be called asynchronously. |
| 11470 | //--------------------------------------------------------------------------------------- |
| 11471 | void CordbProcess::RequestSyncAtEvent() |
| 11472 | { |
| 11473 | GetDAC()->RequestSyncAtEvent(); |
| 11474 | } |
| 11475 | |
| 11476 | //--------------------------------------------------------------------------------------- |
| 11477 | // |
| 11478 | // Primary loop of the Win32 debug event thread. |
| 11479 | // |
| 11480 | // |
| 11481 | // Arguments: |
| 11482 | // None. |
| 11483 | // |
| 11484 | // Return Value: |
| 11485 | // None. |
| 11486 | // |
| 11487 | // Notes: |
| 11488 | // This is it, you've found it, the main guy. This function loops as long as the |
| 11489 | // debugger is around calling the OS WaitForDebugEvent() API. It takes the OS Debug |
| 11490 | // Event and filters it thru the right-side, continuing the process if not recognized. |
| 11491 | // |
| 11492 | // @dbgtodo shim: this will become part of the shim. |
| 11493 | //--------------------------------------------------------------------------------------- |
| 11494 | void CordbWin32EventThread::Win32EventLoop() |
| 11495 | { |
| 11496 | // This must be called from the win32 event thread. |
| 11497 | _ASSERTE(IsWin32EventThread()); |
| 11498 | |
| 11499 | LOG((LF_CORDB, LL_INFO1000, "W32ET::W32EL: entered win32 event loop\n" )); |
| 11500 | |
| 11501 | |
| 11502 | DEBUG_EVENT event; |
| 11503 | |
| 11504 | // Allow the timeout for WFDE to be adjustable. Default to 25 ms based off perf numbers (see issue VSWhidbey 132368). |
| 11505 | DWORD dwWFDETimeout = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DbgWFDETimeout); |
| 11506 | |
| 11507 | while (m_run) |
| 11508 | { |
| 11509 | BOOL fEventAvailable = FALSE; |
| 11510 | |
| 11511 | // We should not have any locks right now. |
| 11512 | |
| 11513 | |
| 11514 | // Have to wait on 2 sources: |
| 11515 | // WaitForMultipleObjects - ping for messages (create, attach, Continue, detach) and also |
| 11516 | // process exits in the managed-only case. |
| 11517 | // Native Debug Events - This is a huge perf hit so we want to avoid it whenever we can. |
| 11518 | // Only wait on these if we're interop debugging and if the process is not frozen. |
| 11519 | // A frozen process can't send any debug events, so don't bother looking for them. |
| 11520 | |
| 11521 | |
| 11522 | unsigned int cWaitCount = 1; |
| 11523 | |
| 11524 | HANDLE rghWaitSet[2]; |
| 11525 | |
| 11526 | rghWaitSet[0] = m_threadControlEvent; |
| 11527 | |
| 11528 | DWORD dwWaitTimeout = INFINITE; |
| 11529 | |
| 11530 | if (m_pProcess != NULL) |
| 11531 | { |
| 11532 | // Process is always built on Native debugging pipeline, so it needs to always be prepared to call WFDE |
| 11533 | // As an optimization, if the target is stopped, then we can avoid calling WFDE. |
| 11534 | { |
| 11535 | #ifndef FEATURE_INTEROP_DEBUGGING |
| 11536 | // Managed-only, never win32 stopped, so always check for an event. |
| 11537 | dwWaitTimeout = 0; |
| 11538 | fEventAvailable = m_pNativePipeline->WaitForDebugEvent(&event, dwWFDETimeout, m_pProcess); |
| 11539 | #else |
| 11540 | // Wait for a Win32 debug event from any processes that we may be attached to as the Win32 debugger. |
| 11541 | const bool fIsWin32Stopped = (m_pProcess->m_state & CordbProcess::PS_WIN32_STOPPED) != 0; |
| 11542 | const bool fSkipWFDE = fIsWin32Stopped; |
| 11543 | |
| 11544 | |
| 11545 | const bool fIsInteropDebugging = m_pProcess->IsInteropDebugging(); |
| 11546 | (void)fIsInteropDebugging; //prevent "unused variable" error from GCC |
| 11547 | |
| 11548 | // Assert checks |
| 11549 | _ASSERTE(fIsInteropDebugging == m_pShim->IsInteropDebugging()); |
| 11550 | |
| 11551 | if (!fSkipWFDE) |
| 11552 | { |
| 11553 | dwWaitTimeout = 0; |
| 11554 | fEventAvailable = m_pNativePipeline->WaitForDebugEvent(&event, dwWFDETimeout, m_pProcess); |
| 11555 | } |
| 11556 | else |
| 11557 | { |
| 11558 | // If we're managed-only debugging, then the process should always be running, |
| 11559 | // which means we always need to be calling WFDE to pump potential debug events. |
| 11560 | // If we're interop-debugging, then the process can be stopped at a native-debug event, |
| 11561 | // in which case we don't have to call WFDE until we resume it again. |
| 11562 | // So we can only skip the WFDE when we're interop-debugging. |
| 11563 | _ASSERTE(fIsInteropDebugging); |
| 11564 | } |
| 11565 | #endif // FEATURE_INTEROP_DEBUGGING |
| 11566 | } |
| 11567 | |
| 11568 | |
| 11569 | } // end m_pProcess != NULL |
| 11570 | |
| 11571 | #if defined(FEATURE_INTEROP_DEBUGGING) |
| 11572 | // While interop-debugging, the process may get killed rudely underneath us, even if we haven't |
| 11573 | // continued the last debug event. In such cases, The process object will get signalled normally. |
| 11574 | // If we didn't just get a native-exitProcess event, then listen on the process handle for exit. |
| 11575 | // (this includes all managed-only debugging) |
| 11576 | // It's very important to establish this before we go into the WaitForMutlipleObjects below |
| 11577 | // because the debuggee may exit while we're sitting in that loop (waiting for the debugger to call Continue). |
| 11578 | bool fDidNotJustGetExitProcessEvent = !fEventAvailable || (event.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT); |
| 11579 | #else |
| 11580 | // In non-interop scenarios, we'll never get any native debug events, let alone an ExitProcess native event. |
| 11581 | bool fDidNotJustGetExitProcessEvent = true; |
| 11582 | #endif // FEATURE_INTEROP_DEBUGGING |
| 11583 | |
| 11584 | |
| 11585 | // The m_pProcess handle will get nulled out after we process the ExitProcess event, and |
| 11586 | // that will ensure that we only wait for an Exit event once. |
| 11587 | if ((m_pProcess != NULL) && fDidNotJustGetExitProcessEvent) |
| 11588 | { |
| 11589 | rghWaitSet[1] = m_pProcess->UnsafeGetProcessHandle(); |
| 11590 | cWaitCount = 2; |
| 11591 | } |
| 11592 | |
| 11593 | // See if any process that we aren't attached to as the Win32 debugger have exited. (Note: this is a |
| 11594 | // polling action if we are also waiting for Win32 debugger events. We're also looking at the thread |
| 11595 | // control event here, too, to see if we're supposed to do something, like attach. |
| 11596 | DWORD dwStatus = WaitForMultipleObjectsEx(cWaitCount, rghWaitSet, FALSE, dwWaitTimeout, FALSE); |
| 11597 | |
| 11598 | _ASSERTE((dwStatus == WAIT_TIMEOUT) || (dwStatus < cWaitCount)); |
| 11599 | |
| 11600 | if (!m_run) |
| 11601 | { |
| 11602 | _ASSERTE(m_action == W32ETA_NONE); |
| 11603 | break; |
| 11604 | } |
| 11605 | |
| 11606 | LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL - got event , ret=%d, has w32 dbg event=%d\n" , |
| 11607 | dwStatus, fEventAvailable)); |
| 11608 | |
| 11609 | // If we haven't timed out, or if it wasn't the thread control event |
| 11610 | // that was set, then a process has |
| 11611 | // exited... |
| 11612 | if ((dwStatus != WAIT_TIMEOUT) && (dwStatus != WAIT_OBJECT_0)) |
| 11613 | { |
| 11614 | // Grab the process that exited. |
| 11615 | _ASSERTE((dwStatus - WAIT_OBJECT_0) == 1); |
| 11616 | ExitProcess(false); // not detach |
| 11617 | fEventAvailable = false; |
| 11618 | } |
| 11619 | // Should we create a process? |
| 11620 | else if (m_action == W32ETA_CREATE_PROCESS) |
| 11621 | { |
| 11622 | CreateProcess(); |
| 11623 | } |
| 11624 | // Should we attach to a process? |
| 11625 | else if (m_action == W32ETA_ATTACH_PROCESS) |
| 11626 | { |
| 11627 | AttachProcess(); |
| 11628 | } |
| 11629 | // Should we detach from a process? |
| 11630 | else if (m_action == W32ETA_DETACH) |
| 11631 | { |
| 11632 | ExitProcess(true); // detach case |
| 11633 | |
| 11634 | // Once we detach, we don't need to continue any outstanding event. |
| 11635 | // So act like we never got the event. |
| 11636 | fEventAvailable = false; |
| 11637 | PREFIX_ASSUME(m_pProcess == NULL); // W32 cleared process pointer |
| 11638 | } |
| 11639 | |
| 11640 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 11641 | // Should we continue the process? |
| 11642 | else if (m_action == W32ETA_CONTINUE) |
| 11643 | { |
| 11644 | HandleUnmanagedContinue(); |
| 11645 | } |
| 11646 | #endif // FEATURE_INTEROP_DEBUGGING |
| 11647 | |
| 11648 | // We don't need to sweep the FCH threads since we never hijack a thread in cooperative mode. |
| 11649 | |
| 11650 | |
| 11651 | // Only process an event if one is available. |
| 11652 | if (!fEventAvailable) |
| 11653 | { |
| 11654 | continue; |
| 11655 | } |
| 11656 | |
| 11657 | // The only ref we have is the one in the ProcessList hash; |
| 11658 | // If we dispatch an ExitProcess event, we may even lose that. |
| 11659 | // But since the CordbProcess is our parent object, we know it won't go away until |
| 11660 | // it neuters us, so we can safely proceed. |
| 11661 | // Find the process this event is for. |
| 11662 | PREFIX_ASSUME(m_pProcess != NULL); |
| 11663 | _ASSERTE(m_pProcess->m_id == GetProcessId(&event)); // should only get events for our proc |
| 11664 | g_pRSDebuggingInfo->m_MRUprocess = m_pProcess; |
| 11665 | |
| 11666 | // Must flush the dac cache since we were just running. |
| 11667 | m_pProcess->ForceDacFlush(); |
| 11668 | |
| 11669 | // So we've filtered out CLR events. |
| 11670 | // Let the shim handle the remaining events. This will call back into Filter() if appropriate. |
| 11671 | // This will also ensure the debug event gets continued. |
| 11672 | HRESULT hrShim = S_OK; |
| 11673 | { |
| 11674 | PUBLIC_CALLBACK_IN_THIS_SCOPE0_NO_LOCK(NULL); |
| 11675 | hrShim = m_pShim->HandleWin32DebugEvent(&event); |
| 11676 | } |
| 11677 | // Any errors from the shim (eg. failure to load DAC) are unrecoverable |
| 11678 | SetUnrecoverableIfFailed(m_pProcess, hrShim); |
| 11679 | |
| 11680 | } // loop |
| 11681 | |
| 11682 | LOG((LF_CORDB, LL_INFO1000, "W32ET::W32EL: exiting event loop\n" )); |
| 11683 | |
| 11684 | return; |
| 11685 | } |
| 11686 | |
| 11687 | //--------------------------------------------------------------------------------------- |
| 11688 | // |
| 11689 | // Returns if the current thread is the win32 thread. |
| 11690 | // |
| 11691 | // Return Value: |
| 11692 | // true iff this is the win32 event thread. |
| 11693 | // |
| 11694 | //--------------------------------------------------------------------------------------- |
| 11695 | bool CordbProcess::IsWin32EventThread() |
| 11696 | { |
| 11697 | _ASSERTE((m_pShim != NULL) || !"Don't check win32 event thread in V3 cases" ); |
| 11698 | return m_pShim->IsWin32EventThread(); |
| 11699 | } |
| 11700 | |
| 11701 | //--------------------------------------------------------------------------------------- |
| 11702 | // Call when the sync complete event is received and can be processed. |
| 11703 | // |
| 11704 | // Notes: |
| 11705 | // This is called when the RS gets the sync-complete from the LS and can process it. |
| 11706 | // |
| 11707 | // This has a somewhat elaborate contract to fill between Interop-debugging, Async-Break, draining the |
| 11708 | // managed event-queue, and coordinating with the dispatch thread (RCET). |
| 11709 | // |
| 11710 | // @dbgtodo - this should eventually get hoisted into the shim. |
| 11711 | void CordbProcess::HandleSyncCompleteRecieved() |
| 11712 | { |
| 11713 | _ASSERTE(ThreadHoldsProcessLock()); |
| 11714 | |
| 11715 | this->SetSyncCompleteRecv(true); |
| 11716 | |
| 11717 | // If some thread is waiting for the process to sync, notify that it can go now. |
| 11718 | if (this->m_stopRequested) |
| 11719 | { |
| 11720 | this->SetSynchronized(true); |
| 11721 | SetEvent(this->m_stopWaitEvent); |
| 11722 | } |
| 11723 | else |
| 11724 | { |
| 11725 | // Note: we set the m_stopWaitEvent all the time and leave it high while we're stopped. This |
| 11726 | // must be done after we've checked m_stopRequested. |
| 11727 | SetEvent(this->m_stopWaitEvent); |
| 11728 | |
| 11729 | // Otherwise, simply mark that the state of the process has changed and let the |
| 11730 | // managed event dispatch logic take over. |
| 11731 | // |
| 11732 | // Note: process->m_synchronized remains false, which indicates to the RC event |
| 11733 | // thread that it can dispatch the next managed event. |
| 11734 | m_cordb->ProcessStateChanged(); |
| 11735 | } |
| 11736 | } |
| 11737 | |
| 11738 | |
| 11739 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 11740 | |
| 11741 | //--------------------------------------------------------------------------------------- |
| 11742 | // |
| 11743 | // Get (create if needed) the unmanaged thread for an unmanaged debug event. |
| 11744 | // |
| 11745 | // Arguments: |
| 11746 | // event - native debug event. |
| 11747 | // |
| 11748 | // Return Value: |
| 11749 | // Unmanaged thread corresponding to the native debug event. |
| 11750 | // |
| 11751 | // |
| 11752 | // Notes: |
| 11753 | // Thread may be newly allocated, or may be existing. CordbProcess holds |
| 11754 | // list of all CordbUnmanagedThreads, and will handle freeing memory. |
| 11755 | // |
| 11756 | //--------------------------------------------------------------------------------------- |
| 11757 | CordbUnmanagedThread * CordbProcess::GetUnmanagedThreadFromEvent(const DEBUG_EVENT * pEvent) |
| 11758 | { |
| 11759 | _ASSERTE(ThreadHoldsProcessLock()); |
| 11760 | HRESULT hr; |
| 11761 | |
| 11762 | CordbUnmanagedThread * pUnmanagedThread = NULL; |
| 11763 | |
| 11764 | // Remember newly created threads. |
| 11765 | if (pEvent->dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) |
| 11766 | { |
| 11767 | // We absolutely should have an unmanaged callback by this point. |
| 11768 | // That means that the client debugger should have called ICorDebug::SetUnmanagedHandler by now. |
| 11769 | // However, we can't actually enforce that (see comment in ICorDebug::SetUnmanagedHandler for details), |
| 11770 | // so we do a runtime check to check this. |
| 11771 | // This is an extremely gross API misuse and an issue in the client if the callback is not set yet. |
| 11772 | // Without the unmanaged callback, we absolutely can't do interop-debugging. We assert (checked builds) and |
| 11773 | // dispatch unrecoverable error (retail builds) to avoid an AV. |
| 11774 | |
| 11775 | |
| 11776 | if (this->m_cordb->m_unmanagedCallback == NULL) |
| 11777 | { |
| 11778 | CONSISTENCY_CHECK_MSGF((this->m_cordb->m_unmanagedCallback != NULL), |
| 11779 | ("GROSS API misuse!!\nNo unmanaged callback set by the time we've received CreateProcess debug event for proces 0x%x.\n" , |
| 11780 | pEvent->dwProcessId)); |
| 11781 | |
| 11782 | CORDBSetUnrecoverableError(this, CORDBG_E_INTEROP_NOT_SUPPORTED, 0); |
| 11783 | |
| 11784 | // Returning NULL will tell caller not to dispatch event to client. We have no callback object to dispatch upon. |
| 11785 | return NULL; |
| 11786 | } |
| 11787 | |
| 11788 | pUnmanagedThread = this->HandleUnmanagedCreateThread(pEvent->dwThreadId, |
| 11789 | pEvent->u.CreateProcessInfo.hThread, |
| 11790 | pEvent->u.CreateProcessInfo.lpThreadLocalBase); |
| 11791 | |
| 11792 | // Managed-attach won't start until after Cordbg continues from the loader-bp. |
| 11793 | } |
| 11794 | else if (pEvent->dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT) |
| 11795 | { |
| 11796 | pUnmanagedThread = this->HandleUnmanagedCreateThread(pEvent->dwThreadId, |
| 11797 | pEvent->u.CreateThread.hThread, |
| 11798 | pEvent->u.CreateThread.lpThreadLocalBase); |
| 11799 | |
| 11800 | BOOL fBlockExists = FALSE; |
| 11801 | hr = S_OK; |
| 11802 | EX_TRY |
| 11803 | { |
| 11804 | // See if we have the debugger control block yet... |
| 11805 | |
| 11806 | this->GetEventBlock(&fBlockExists); |
| 11807 | |
| 11808 | // If we have the debugger control block, and if that control block has the address of the thread proc for |
| 11809 | // the helper thread, then we're initialized enough on the Left Side to recgonize the helper thread based on |
| 11810 | // its thread proc's address. |
| 11811 | if (this->GetDCB() != NULL) |
| 11812 | { |
| 11813 | // get the latest LS DCB information |
| 11814 | UpdateRightSideDCB(); |
| 11815 | if ((this->GetDCB()->m_helperThreadStartAddr != NULL) && (pUnmanagedThread != NULL)) |
| 11816 | { |
| 11817 | void * pStartAddr = pEvent->u.CreateThread.lpStartAddress; |
| 11818 | |
| 11819 | if (pStartAddr == this->GetDCB()->m_helperThreadStartAddr) |
| 11820 | { |
| 11821 | // Remember the ID of the helper thread. |
| 11822 | this->m_helperThreadId = pEvent->dwThreadId; |
| 11823 | |
| 11824 | LOG((LF_CORDB, LL_INFO1000, "W32ET::W32EL: Left Side Helper Thread is 0x%x\n" , pEvent->dwThreadId)); |
| 11825 | } |
| 11826 | } |
| 11827 | } |
| 11828 | } |
| 11829 | EX_CATCH_HRESULT(hr) |
| 11830 | { |
| 11831 | if (fBlockExists && FAILED(hr)) |
| 11832 | { |
| 11833 | _ASSERTE(IsLegalFatalError(hr)); |
| 11834 | // Send up the DebuggerError event |
| 11835 | this->UnrecoverableError(hr, 0, NULL, 0); |
| 11836 | |
| 11837 | // Kill the process. |
| 11838 | // RS will pump events until we LS process exits. |
| 11839 | TerminateProcess(this->m_handle, hr); |
| 11840 | |
| 11841 | return pUnmanagedThread; |
| 11842 | } |
| 11843 | } |
| 11844 | } |
| 11845 | else |
| 11846 | { |
| 11847 | // Find the unmanaged thread that this event is for. |
| 11848 | pUnmanagedThread = this->GetUnmanagedThread(pEvent->dwThreadId); |
| 11849 | } |
| 11850 | |
| 11851 | return pUnmanagedThread; |
| 11852 | } |
| 11853 | |
| 11854 | //--------------------------------------------------------------------------------------- |
| 11855 | // |
| 11856 | // Handle a native-debug event representing a managed sync-complete event. |
| 11857 | // |
| 11858 | // |
| 11859 | // Return Value: |
| 11860 | // Reaction telling caller how to respond to the native-debug event. |
| 11861 | // |
| 11862 | // Assumptions: |
| 11863 | // Called within the Triage process after receiving a native-debug event. |
| 11864 | // |
| 11865 | //--------------------------------------------------------------------------------------- |
| 11866 | Reaction CordbProcess::TriageSyncComplete() |
| 11867 | { |
| 11868 | _ASSERTE(ThreadHoldsProcessLock()); |
| 11869 | |
| 11870 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::TSC: received 'sync complete' flare.\n" ); |
| 11871 | |
| 11872 | _ASSERTE(IsInteropDebugging()); |
| 11873 | |
| 11874 | // Note: we really don't need to be suspending Runtime threads that we know have tripped |
| 11875 | // here. If we ever end up with a nice, quick way to know that about each unmanaged thread, then |
| 11876 | // we should put that to good use here. |
| 11877 | this->SuspendUnmanagedThreads(); |
| 11878 | |
| 11879 | this->HandleSyncCompleteRecieved(); |
| 11880 | |
| 11881 | // Let the process run free. |
| 11882 | return REACTION(cIgnore); |
| 11883 | |
| 11884 | // At this point, all managed threads are stopped at safe places and all unmanaged |
| 11885 | // threads are either suspended or hijacked. All stopped managed threads are also hard |
| 11886 | // suspended (due to the call to SuspendUnmanagedThreads above) except for the thread |
| 11887 | // that sent the sync complete flare. |
| 11888 | |
| 11889 | // We've handled this exception, so skip all further processing. |
| 11890 | UNREACHABLE(); |
| 11891 | } |
| 11892 | |
| 11893 | //----------------------------------------------------------------------------- |
| 11894 | // Triage a breakpoint (non-flare) on a "normal" thread. |
| 11895 | //----------------------------------------------------------------------------- |
| 11896 | Reaction CordbProcess::TriageBreakpoint(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent) |
| 11897 | { |
| 11898 | _ASSERTE(ThreadHoldsProcessLock()); |
| 11899 | |
| 11900 | HRESULT hr = S_OK; |
| 11901 | |
| 11902 | DWORD dwExCode = pEvent->u.Exception.ExceptionRecord.ExceptionCode; |
| 11903 | const void * pExAddress = pEvent->u.Exception.ExceptionRecord.ExceptionAddress; |
| 11904 | |
| 11905 | _ASSERTE(dwExCode == STATUS_BREAKPOINT); |
| 11906 | |
| 11907 | // There are three cases here: |
| 11908 | // |
| 11909 | // 1. The breakpoint definetly belongs to the Runtime. (I.e., a BP in our patch table that |
| 11910 | // is in managed code.) In this case, we continue the process with |
| 11911 | // DBG_EXCEPTION_NOT_HANDLED, which lets the in-process exception logic kick in as if we |
| 11912 | // weren't here. |
| 11913 | // |
| 11914 | // 2. The breakpoint is definetly not ours. (I.e., a BP that is not in our patch table.) We |
| 11915 | // pass these up as regular exception events, doing the can't stop check as usual. |
| 11916 | // |
| 11917 | // 3. We're not sure. (I.e., a BP in our patch table, but set in unmangaed code.) In this |
| 11918 | // case, we hijack as usual, also with can't stop check as usual. |
| 11919 | |
| 11920 | bool fPatchFound = false; |
| 11921 | bool fPatchIsUnmanaged = false; |
| 11922 | |
| 11923 | hr = this->FindPatchByAddress(PTR_TO_CORDB_ADDRESS(pExAddress), |
| 11924 | &fPatchFound, |
| 11925 | &fPatchIsUnmanaged); |
| 11926 | |
| 11927 | if (SUCCEEDED(hr)) |
| 11928 | { |
| 11929 | if (fPatchFound) |
| 11930 | { |
| 11931 | #ifdef _DEBUG |
| 11932 | // What if managed & native patch the same address? That could happen on a step out M --> U. |
| 11933 | { |
| 11934 | NativePatch * pNativePatch = GetNativePatch(pExAddress); |
| 11935 | SIMPLIFYING_ASSUMPTION_MSGF(pNativePatch == NULL, ("Have Managed & native patch at 0x%p" , pExAddress)); |
| 11936 | } |
| 11937 | #endif |
| 11938 | |
| 11939 | // BP could be ours... if its unmanaged, then we still need to hijack, so fall |
| 11940 | // through to that logic. Otherwise, its ours. |
| 11941 | if (!fPatchIsUnmanaged) |
| 11942 | { |
| 11943 | LOG((LF_CORDB, LL_INFO1000, "W32ET::W32EL: breakpoint exception " |
| 11944 | "belongs to runtime due to patch table match.\n" )); |
| 11945 | |
| 11946 | return REACTION(cCLR); |
| 11947 | } |
| 11948 | else |
| 11949 | { |
| 11950 | LOG((LF_CORDB, LL_INFO1000, "W32ET::W32EL: breakpoint exception " |
| 11951 | "matched in patch table, but its unmanaged so might hijack anyway.\n" )); |
| 11952 | |
| 11953 | // If we're in cooperative mode, then we must have a inproc handler, and don't need to hijack |
| 11954 | // One way this can happen is the patch placed for a func-eval complete is hit in coop-mode. |
| 11955 | if (pUnmanagedThread->GetEEPGCDisabled()) |
| 11956 | { |
| 11957 | LOG((LF_CORDB, LL_INFO10000, "Already in coop-mode, don't need to hijack\n" )); |
| 11958 | return REACTION(cCLR); |
| 11959 | } |
| 11960 | else |
| 11961 | { |
| 11962 | return REACTION(cBreakpointRequiringHijack); |
| 11963 | } |
| 11964 | } |
| 11965 | |
| 11966 | UNREACHABLE(); |
| 11967 | } |
| 11968 | else // Patch not found |
| 11969 | { |
| 11970 | // If we're here, then we have a BP that's not in the managed patch table, and not |
| 11971 | // in the native patch list. This should be rare. Perhaps an int3 / DebugBreak() / Assert in |
| 11972 | // the native code stream. |
| 11973 | // Anyway, we don't know about this patch so we can't skip it. The only thing we can do |
| 11974 | // is chuck it up to Cordbg and hope they can help us. Note that this is the same case |
| 11975 | // we were in w. V1. |
| 11976 | |
| 11977 | // BP doesn't belong to CLR ... so dispatch it to Cordbg as either make it IB or OOB. |
| 11978 | // @todo - make the runtime 1 giant Can't stop region. |
| 11979 | bool fCantStop = pUnmanagedThread->IsCantStop(); |
| 11980 | |
| 11981 | #ifdef _DEBUG |
| 11982 | // We rarely expect a raw int3 here. Add a debug check that will assert. |
| 11983 | // Tests that know they don't have raw int3 can enable this regkey to get |
| 11984 | // extra coverage. |
| 11985 | static DWORD s_fBreakOnRawInt3 = -1; |
| 11986 | |
| 11987 | if (s_fBreakOnRawInt3 == -1) |
| 11988 | s_fBreakOnRawInt3 = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgBreakOnRawInt3); |
| 11989 | |
| 11990 | if (s_fBreakOnRawInt3) |
| 11991 | { |
| 11992 | CONSISTENCY_CHECK_MSGF(false, ("Unexpected Raw int3 at:%p on tid 0x%x (%d). CantStop=%d." |
| 11993 | "This assert is used by specific tests to get extra checks." |
| 11994 | "For normal cases it's ignorable and is enabled by setting DbgBreakOnRawInt3==1." , |
| 11995 | pExAddress, pEvent->dwThreadId, pEvent->dwThreadId, fCantStop)); |
| 11996 | } |
| 11997 | #endif |
| 11998 | |
| 11999 | if (fCantStop) |
| 12000 | { |
| 12001 | // If we're in a can't stop region, then its OOB no matter what at this point. |
| 12002 | return REACTION(cOOB); |
| 12003 | } |
| 12004 | else |
| 12005 | { |
| 12006 | // PGC must be enabled if we're going to stop for an IB event. |
| 12007 | bool PGCDisabled = pUnmanagedThread->GetEEPGCDisabled(); |
| 12008 | _ASSERTE(!PGCDisabled); |
| 12009 | |
| 12010 | // Bp is definitely not ours, and PGC is not disabled, so in-band exception. |
| 12011 | LOG((LF_CORDB, LL_INFO1000, "W32ET::W32EL: breakpoint exception " |
| 12012 | "does not belong to the runtime due to failed patch table match.\n" )); |
| 12013 | |
| 12014 | return REACTION(cInband); |
| 12015 | } |
| 12016 | |
| 12017 | UNREACHABLE(); |
| 12018 | } |
| 12019 | |
| 12020 | UNREACHABLE(); |
| 12021 | } |
| 12022 | else |
| 12023 | { |
| 12024 | // Patch table lookup failed? Only on OOM or if ReadProcessMemory fails... |
| 12025 | _ASSERTE(!"Patch table lookup failed!" ); |
| 12026 | CORDBSetUnrecoverableError(this, hr, 0); |
| 12027 | return REACTION(cOOB); |
| 12028 | } |
| 12029 | |
| 12030 | UNREACHABLE(); |
| 12031 | } |
| 12032 | |
| 12033 | //--------------------------------------------------------------------------------------- |
| 12034 | // |
| 12035 | // Triage a "normal" 1st chance exception on a "normal" thread. |
| 12036 | // Not hijacked, not the helper thread, not a flare, etc.. This is the common |
| 12037 | // case for a native exception from native code. |
| 12038 | // |
| 12039 | // Arguments: |
| 12040 | // pUnmanagedThread - Pointer to the CordbUnmanagedThread object that we want to hijack. |
| 12041 | // pEvent - Pointer to the debug event which contains the exception code and address. |
| 12042 | // |
| 12043 | // Return Value: |
| 12044 | // The Reaction tells if the event is in-band, out-of-band, CLR specific or ignorable. |
| 12045 | // |
| 12046 | //--------------------------------------------------------------------------------------- |
| 12047 | Reaction CordbProcess::Triage1stChanceNonSpecial(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent) |
| 12048 | { |
| 12049 | _ASSERTE(ThreadHoldsProcessLock()); |
| 12050 | CONTRACTL |
| 12051 | { |
| 12052 | THROWS; |
| 12053 | } |
| 12054 | CONTRACTL_END; |
| 12055 | |
| 12056 | HRESULT hr = S_OK; |
| 12057 | |
| 12058 | DWORD dwExCode = pEvent->u.Exception.ExceptionRecord.ExceptionCode; |
| 12059 | const void * pExAddress = pEvent->u.Exception.ExceptionRecord.ExceptionAddress; |
| 12060 | |
| 12061 | // This had better not be a flare. If it is, that means we have some race that unmarked |
| 12062 | // the hijacks. |
| 12063 | _ASSERTE(!ExceptionIsFlare(dwExCode, pExAddress)); |
| 12064 | |
| 12065 | // Any first chance exception could belong to the Runtime, so long as the Runtime has actually been |
| 12066 | // initialized. Here we'll setup a first-chance hijack for this thread so that it can give us the |
| 12067 | // true answer that we need. |
| 12068 | |
| 12069 | // But none of those exceptions could possibly be ours unless we have a managed thread to go with |
| 12070 | // this unmanaged thread. A non-NULL EEThreadPtr tells us that there is indeed a managed thread for |
| 12071 | // this unmanaged thread, even if the Right Side hasn't received a managed ThreadCreate message yet. |
| 12072 | REMOTE_PTR pEEThread; |
| 12073 | hr = pUnmanagedThread->GetEEThreadPtr(&pEEThread); |
| 12074 | _ASSERTE(SUCCEEDED(hr)); |
| 12075 | |
| 12076 | if (pEEThread == NULL) |
| 12077 | { |
| 12078 | // No managed thread, so it can't possibly belong to the runtime! |
| 12079 | // But it may still be in a can't-stop region (think some goofy shutdown case). |
| 12080 | if (pUnmanagedThread->IsCantStop()) |
| 12081 | { |
| 12082 | return REACTION(cOOB); |
| 12083 | } |
| 12084 | else |
| 12085 | { |
| 12086 | return REACTION(cInband); |
| 12087 | } |
| 12088 | } |
| 12089 | |
| 12090 | |
| 12091 | |
| 12092 | // We have to be careful here. A Runtime thread may be in a place where we cannot let an |
| 12093 | // unmanaged exception stop it. For instance, an unmanaged user breakpoint set on |
| 12094 | // WaitForSingleObject will prevent Runtime threads from sending events to the Right Side. So at |
| 12095 | // various points below, we check to see if this Runtime thread is in a place were we can't let |
| 12096 | // it stop, and if so then we jump over to the out-of-band dispatch logic and treat this |
| 12097 | // exception as out-of-band. The debugger is supposed to continue from the out-of-band event |
| 12098 | // properly and help us avoid this problem altogether. |
| 12099 | |
| 12100 | // Grab a few flags from the thread's state... |
| 12101 | bool fThreadStepping = false; |
| 12102 | bool fSpecialManagedException = false; |
| 12103 | |
| 12104 | pUnmanagedThread->GetEEState(&fThreadStepping, &fSpecialManagedException); |
| 12105 | |
| 12106 | // If we've got a single step exception, and if the Left Side has indicated that it was |
| 12107 | // stepping the thread, then the exception is ours. |
| 12108 | if (dwExCode == STATUS_SINGLE_STEP) |
| 12109 | { |
| 12110 | if (fThreadStepping) |
| 12111 | { |
| 12112 | // Yup, its the Left Side that was stepping the thread... |
| 12113 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "W32ET::W32EL: single step exception belongs to the runtime.\n" ); |
| 12114 | |
| 12115 | return REACTION(cCLR); |
| 12116 | } |
| 12117 | |
| 12118 | // Any single step that is triggered when the thread's state doesn't indicate that |
| 12119 | // we were stepping the thread automatically gets passed out as an unmanged event. |
| 12120 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "W32ET::W32EL: single step exception " |
| 12121 | "does not belong to the runtime.\n" ); |
| 12122 | |
| 12123 | if (pUnmanagedThread->IsCantStop()) |
| 12124 | { |
| 12125 | return REACTION(cOOB); |
| 12126 | } |
| 12127 | else |
| 12128 | { |
| 12129 | return REACTION(cInband); |
| 12130 | } |
| 12131 | |
| 12132 | UNREACHABLE(); |
| 12133 | } |
| 12134 | |
| 12135 | #ifdef CorDB_Short_Circuit_First_Chance_Ownership |
| 12136 | // If the runtime indicates that this is a special exception being thrown within the runtime, |
| 12137 | // then its ours no matter what. |
| 12138 | else if (fSpecialManagedException) |
| 12139 | { |
| 12140 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "W32ET::W32EL: exception belongs to the runtime due to " |
| 12141 | "special managed exception marking.\n" ); |
| 12142 | |
| 12143 | return REACTION(cCLR); |
| 12144 | } |
| 12145 | else if ((dwExCode == EXCEPTION_COMPLUS) || (dwExCode == EXCEPTION_HIJACK)) |
| 12146 | { |
| 12147 | STRESS_LOG0(LF_CORDB, LL_INFO1000, |
| 12148 | "W32ET::W32EL: exception belongs to Runtime due to match on built in exception code\n" ); |
| 12149 | |
| 12150 | return REACTION(cCLR); |
| 12151 | } |
| 12152 | else if (dwExCode == EXCEPTION_MSVC) |
| 12153 | { |
| 12154 | // The runtime may use C++ exceptions internally. We can still report these |
| 12155 | // to the debugger as long as we're outside of a can't-stop region. |
| 12156 | if (pUnmanagedThread->IsCantStop()) |
| 12157 | { |
| 12158 | return REACTION(cCLR); |
| 12159 | } |
| 12160 | else |
| 12161 | { |
| 12162 | return REACTION(cInband); |
| 12163 | } |
| 12164 | } |
| 12165 | else if (dwExCode == STATUS_BREAKPOINT) |
| 12166 | { |
| 12167 | return TriageBreakpoint(pUnmanagedThread, pEvent); |
| 12168 | }// end BP case |
| 12169 | #endif |
| 12170 | |
| 12171 | // It's not a breakpoint or single-step. Now it just comes down to the address from where |
| 12172 | // the exception is coming from. If it's managed, we give it back to the CLR. If it's |
| 12173 | // from native, then we dispatch to Cordbg. |
| 12174 | // We can use DAC to figure this out from Out-of-process. |
| 12175 | _ASSERTE(dwExCode != STATUS_BREAKPOINT); // BP were already handled. |
| 12176 | |
| 12177 | |
| 12178 | // Use DAC to decide if it's ours or not w/o going inproc. |
| 12179 | CORDB_ADDRESS address = PTR_TO_CORDB_ADDRESS(pExAddress); |
| 12180 | |
| 12181 | IDacDbiInterface::AddressType addrType; |
| 12182 | |
| 12183 | addrType = GetDAC()->GetAddressType(address); |
| 12184 | bool fIsCorCode =((addrType == IDacDbiInterface::kAddressManagedMethod) || |
| 12185 | (addrType == IDacDbiInterface::kAddressRuntimeManagedCode) || |
| 12186 | (addrType == IDacDbiInterface::kAddressRuntimeUnmanagedCode)); |
| 12187 | |
| 12188 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "W32ET::W32EL: IsCorCode(0x%I64p)=%d\n" , address, fIsCorCode); |
| 12189 | |
| 12190 | |
| 12191 | if (fIsCorCode) |
| 12192 | { |
| 12193 | return REACTION(cCLR); |
| 12194 | } |
| 12195 | else |
| 12196 | { |
| 12197 | if (pUnmanagedThread->IsCantStop()) |
| 12198 | { |
| 12199 | return REACTION(cOOB); |
| 12200 | } |
| 12201 | else |
| 12202 | { |
| 12203 | return REACTION(cInband); |
| 12204 | } |
| 12205 | } |
| 12206 | |
| 12207 | UNREACHABLE(); |
| 12208 | } |
| 12209 | |
| 12210 | //--------------------------------------------------------------------------------------- |
| 12211 | // |
| 12212 | // Triage a 1st-chance exception when the CLR is initialized. |
| 12213 | // |
| 12214 | // Arguments: |
| 12215 | // pUnmanagedThread - thread that the event has occurred on. |
| 12216 | // pEvent - native debug event for the exception that occurred that this is triaging. |
| 12217 | // |
| 12218 | // Return Value: |
| 12219 | // Reaction for how to handle this event. |
| 12220 | // |
| 12221 | // Assumptions: |
| 12222 | // Called when receiving a debug event when the process is stopped. |
| 12223 | // |
| 12224 | // Notes: |
| 12225 | // A 1st-chance event has a wide spectrum of possibility including: |
| 12226 | // - It may be unmanaged or managed. |
| 12227 | // - Or it may be an execution control exception for managed-exceution |
| 12228 | // - thread skipping an OOB event. |
| 12229 | // |
| 12230 | //--------------------------------------------------------------------------------------- |
| 12231 | Reaction CordbProcess::TriageExcep1stChanceAndInit(CordbUnmanagedThread * pUnmanagedThread, |
| 12232 | const DEBUG_EVENT * pEvent) |
| 12233 | { |
| 12234 | _ASSERTE(ThreadHoldsProcessLock()); |
| 12235 | _ASSERTE(m_runtimeOffsetsInitialized); |
| 12236 | |
| 12237 | NativePatch * pNativePatch = NULL; |
| 12238 | DebuggerIPCRuntimeOffsets * pIPCRuntimeOffsets = &(this->m_runtimeOffsets); |
| 12239 | |
| 12240 | DWORD dwExCode = pEvent->u.Exception.ExceptionRecord.ExceptionCode; |
| 12241 | const void * pExAddress = pEvent->u.Exception.ExceptionRecord.ExceptionAddress; |
| 12242 | |
| 12243 | LOG((LF_CORDB, LL_INFO1000, "CP::TE1stCAI: Enter\n" )); |
| 12244 | |
| 12245 | #ifdef _DEBUG |
| 12246 | // Some Interop bugs involve threads that land at a crazy IP. Since we're interop-debugging, we can't |
| 12247 | // attach a debugger to the LS. So we have some debug mode where we enable the SS flag and thus |
| 12248 | // produce a trace of where a thread is going. |
| 12249 | if (pUnmanagedThread->IsDEBUGTrace() && (dwExCode == STATUS_SINGLE_STEP)) |
| 12250 | { |
| 12251 | pUnmanagedThread->ClearState(CUTS_DEBUG_SingleStep); |
| 12252 | LOG((LF_CORDB, LL_INFO10000, "DEBUG TRACE, thread %4x at IP: 0x%p\n" , pUnmanagedThread->m_id, pExAddress)); |
| 12253 | |
| 12254 | // Clear the exception and pretend this never happened. |
| 12255 | return REACTION(cIgnore); |
| 12256 | } |
| 12257 | #endif |
| 12258 | |
| 12259 | // If we were stepping for exception retrigger and got the single step and it should be hidden then just ignore it. |
| 12260 | // Anything that isn't cInbandExceptionRetrigger will cause the debug event to be dequeued, stepping turned off, and |
| 12261 | // it will count as not retriggering |
| 12262 | // TODO: I don't think the IsSSFlagNeeded() check is needed here though it doesn't break anything |
| 12263 | if (pUnmanagedThread->IsSSFlagNeeded() && pUnmanagedThread->IsSSFlagHidden() && (dwExCode == STATUS_SINGLE_STEP)) |
| 12264 | { |
| 12265 | LOG((LF_CORDB, LL_INFO10000, "CP::TE1stCAI: ignoring hidden single step\n" )); |
| 12266 | return REACTION(cIgnore); |
| 12267 | } |
| 12268 | |
| 12269 | // Is this a breakpoint indicating that the Left Side is now synchronized? |
| 12270 | if ((dwExCode == STATUS_BREAKPOINT) && |
| 12271 | (pExAddress == pIPCRuntimeOffsets->m_notifyRSOfSyncCompleteBPAddr)) |
| 12272 | { |
| 12273 | return TriageSyncComplete(); |
| 12274 | } |
| 12275 | else if ((dwExCode == STATUS_BREAKPOINT) && |
| 12276 | (pExAddress == pIPCRuntimeOffsets->m_excepForRuntimeHandoffCompleteBPAddr)) |
| 12277 | { |
| 12278 | _ASSERTE(!"This should be unused now" ); |
| 12279 | |
| 12280 | // This notification means that a thread that had been first-chance hijacked is now |
| 12281 | // finally leaving the hijack. |
| 12282 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::TE1stCAI: received 'first chance hijack handoff complete' flare.\n" ); |
| 12283 | |
| 12284 | // Let the process run. |
| 12285 | return REACTION(cIgnore); |
| 12286 | } |
| 12287 | else if ((dwExCode == STATUS_BREAKPOINT) && |
| 12288 | (pExAddress == pIPCRuntimeOffsets->m_signalHijackCompleteBPAddr)) |
| 12289 | { |
| 12290 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::TE1stCAI: received 'hijack complete' flare.\n" ); |
| 12291 | return REACTION(cInbandHijackComplete); |
| 12292 | } |
| 12293 | else if ((dwExCode == STATUS_BREAKPOINT) && |
| 12294 | (pExAddress == m_runtimeOffsets.m_signalHijackStartedBPAddr)) |
| 12295 | { |
| 12296 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::TE1stCAI: received 'hijack started' flare.\n" ); |
| 12297 | return REACTION(cFirstChanceHijackStarted); |
| 12298 | } |
| 12299 | else if ((dwExCode == STATUS_BREAKPOINT) && ((pNativePatch = GetNativePatch(pExAddress)) != NULL) ) |
| 12300 | { |
| 12301 | // We hit a native BP placed by Cordbg. This could happen on any thread (including helper) |
| 12302 | bool fCantStop = pUnmanagedThread->IsCantStop(); |
| 12303 | |
| 12304 | // REVISIT_TODO: if the user also set a breakpoint here then we should dispatch to the debugger |
| 12305 | // and rely on the debugger to get us past this. Should be a rare case though. |
| 12306 | if (fCantStop) |
| 12307 | { |
| 12308 | // Need to skip it completely; never dispatch. |
| 12309 | pUnmanagedThread->SetupForSkipBreakpoint(pNativePatch); |
| 12310 | |
| 12311 | // Debuggee will single step over the patch, and fire a SS exception. |
| 12312 | // We'll then call FixupForSkipBreakpoint, and continue the process. |
| 12313 | return REACTION(cIgnore); |
| 12314 | } |
| 12315 | else |
| 12316 | { |
| 12317 | // Native patch in native code. A very common scenario. |
| 12318 | // Dispatch as an IB event to Cordbg. |
| 12319 | STRESS_LOG1(LF_CORDB, LL_INFO10000, "Native patch in native code (at %p), dispatching as IB event.\n" , pExAddress); |
| 12320 | return REACTION(cInband); |
| 12321 | } |
| 12322 | |
| 12323 | UNREACHABLE(); |
| 12324 | } |
| 12325 | |
| 12326 | else if ((dwExCode == STATUS_BREAKPOINT) && !IsBreakOpcodeAtAddress(pExAddress)) |
| 12327 | { |
| 12328 | // If we got an int3 exception, but there's not actually an int3 at the address, then just reset the IP |
| 12329 | // to the address. This can happen if the int 3 is cleared after the thread has dispatched it (in which case |
| 12330 | // WFDE will pick it up) but before we realize it's one of ours. |
| 12331 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "CP::TE1stCAI: Phantom Int3: Tid=0x%x, addr=%p\n" , pEvent->dwThreadId, pExAddress); |
| 12332 | |
| 12333 | DT_CONTEXT context; |
| 12334 | |
| 12335 | context.ContextFlags = DT_CONTEXT_FULL; |
| 12336 | |
| 12337 | BOOL fSuccess = DbiGetThreadContext(pUnmanagedThread->m_handle, &context); |
| 12338 | |
| 12339 | _ASSERTE(fSuccess); |
| 12340 | |
| 12341 | if (fSuccess) |
| 12342 | { |
| 12343 | // Backup IP to point to the instruction we need to execute. Continuing from a breakpoint exception |
| 12344 | // continues execution at the instruction after the breakpoint, but we need to continue where the |
| 12345 | // breakpoint was. |
| 12346 | CORDbgSetIP(&context, (LPVOID) pExAddress); |
| 12347 | |
| 12348 | fSuccess = DbiSetThreadContext(pUnmanagedThread->m_handle, &context); |
| 12349 | _ASSERTE(fSuccess); |
| 12350 | } |
| 12351 | |
| 12352 | return REACTION(cIgnore); |
| 12353 | } |
| 12354 | else if (pUnmanagedThread->IsSkippingNativePatch()) |
| 12355 | { |
| 12356 | // If we Single-Step over an exception, then the OS never gives us the single-step event. |
| 12357 | // Thus if we're skipping a native patch, we don't care what exception event we got. |
| 12358 | LOG((LF_CORDB, LL_INFO100000, "Done skipping native patch. Ex=0x%x\n, IsSS=%d" , |
| 12359 | dwExCode, |
| 12360 | (dwExCode == STATUS_SINGLE_STEP))); |
| 12361 | |
| 12362 | // This is the 2nd half of skipping a native patch. |
| 12363 | // This could happen on any thread (including helper) |
| 12364 | // We've already removed the opcode and now we just finished a single-step over it. |
| 12365 | // So put the patch back in, and continue the process. |
| 12366 | pUnmanagedThread->FixupForSkipBreakpoint(); |
| 12367 | |
| 12368 | return REACTION(cIgnore); |
| 12369 | } |
| 12370 | else if (this->IsHelperThreadWorked(pUnmanagedThread->GetOSTid())) |
| 12371 | { |
| 12372 | // We should never ever get a single-step event from the helper thread. |
| 12373 | CONSISTENCY_CHECK_MSGF(dwExCode != STATUS_SINGLE_STEP, ( |
| 12374 | "Single-Step exception on helper thread (tid=0x%x/%d) in debuggee process (pid=0x%x/%d).\n" |
| 12375 | "For more information, attach a debuggee non-invasively to the LS to get the callstack.\n" , |
| 12376 | pUnmanagedThread->m_id, |
| 12377 | pUnmanagedThread->m_id, |
| 12378 | this->m_id, |
| 12379 | this->m_id)); |
| 12380 | |
| 12381 | // We ignore any first chance exceptions from the helper thread. There are lots of places |
| 12382 | // on the left side where we attempt to dereference bad object refs and such that will be |
| 12383 | // handled by exception handlers already in place. |
| 12384 | // |
| 12385 | // Note: we check this after checking for the sync complete notification, since that can |
| 12386 | // come from the helper thread. |
| 12387 | // |
| 12388 | // Note: we do let single step and breakpoint exceptions go through to the debugger for processing. |
| 12389 | if ((dwExCode != STATUS_BREAKPOINT) && (dwExCode != STATUS_SINGLE_STEP)) |
| 12390 | { |
| 12391 | return REACTION(cCLR); |
| 12392 | } |
| 12393 | else |
| 12394 | { |
| 12395 | // Since the helper thread is part of the "can't stop" region, we should have already |
| 12396 | // skipped any BPs on it. |
| 12397 | // However, any Assert on the helper thread will hit this case. |
| 12398 | CONSISTENCY_CHECK_MSGF((dwExCode != STATUS_BREAKPOINT), ( |
| 12399 | "Assert on helper thread (tid=0x%x/%d) in debuggee process (pid=0x%x/%d).\n" |
| 12400 | "For more information, attach a debuggee non-invasively to the LS to get the callstack.\n" , |
| 12401 | pUnmanagedThread->m_id, |
| 12402 | pUnmanagedThread->m_id, |
| 12403 | this->m_id, |
| 12404 | this->m_id)); |
| 12405 | |
| 12406 | // These breakpoint and single step exceptions have to be dispatched to the debugger as |
| 12407 | // out-of-band events. This tells the debugger that they must continue from these events |
| 12408 | // immediatly, and that no interaction with the Left Side is allowed until they do so. This |
| 12409 | // makes sense, since these events are on the helper thread. |
| 12410 | return REACTION(cOOB); |
| 12411 | } |
| 12412 | UNREACHABLE(); |
| 12413 | } |
| 12414 | else if (pUnmanagedThread->IsFirstChanceHijacked() && this->ExceptionIsFlare(dwExCode, pExAddress)) |
| 12415 | { |
| 12416 | _ASSERTE(!"This should be unused now" ); |
| 12417 | } |
| 12418 | else if (pUnmanagedThread->IsGenericHijacked()) |
| 12419 | { |
| 12420 | if (this->ExceptionIsFlare(dwExCode, pExAddress)) |
| 12421 | { |
| 12422 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::TE1stCAI: fixing up from generic hijack.\n" ); |
| 12423 | |
| 12424 | _ASSERTE(dwExCode == STATUS_BREAKPOINT); |
| 12425 | |
| 12426 | // Fixup the thread from the generic hijack. |
| 12427 | pUnmanagedThread->FixupFromGenericHijack(); |
| 12428 | |
| 12429 | // We force continue from this flare, since its only purpose was to notify us that we had to |
| 12430 | // fixup the thread from a generic hijack. |
| 12431 | return REACTION(cIgnore); |
| 12432 | } |
| 12433 | else |
| 12434 | { |
| 12435 | // We might reach here due to the stack overflow issue, due to target |
| 12436 | // memory corruption, or even due to an exception thrown during hijacking |
| 12437 | |
| 12438 | BOOL bStackOverflow = FALSE; |
| 12439 | |
| 12440 | if (dwExCode == STATUS_ACCESS_VIOLATION || dwExCode == STATUS_STACK_OVERFLOW) |
| 12441 | { |
| 12442 | CORDB_ADDRESS stackLimit; |
| 12443 | CORDB_ADDRESS stackBase; |
| 12444 | if (pUnmanagedThread->GetStackRange(&stackBase, &stackLimit)) |
| 12445 | { |
| 12446 | TADDR addr = pEvent->u.Exception.ExceptionRecord.ExceptionInformation[1]; |
| 12447 | if (stackLimit <= addr && addr < stackBase) |
| 12448 | bStackOverflow = TRUE; |
| 12449 | } |
| 12450 | else |
| 12451 | { |
| 12452 | // to limit the impact of the change we'll consider failure to retrieve the stack |
| 12453 | // bounds as stack overflow as well |
| 12454 | bStackOverflow = TRUE; |
| 12455 | } |
| 12456 | } |
| 12457 | |
| 12458 | if (!bStackOverflow) |
| 12459 | { |
| 12460 | // generic hijack means we're in CantStop, so return cOOB |
| 12461 | return REACTION(cOOB); |
| 12462 | } |
| 12463 | |
| 12464 | // If generichijacked and its not a flare, and the address referenced is on the stack then we've |
| 12465 | // got our special stack overflow case. Take off generic hijacked, mark that the helper thread |
| 12466 | // is dead, throw this event on the floor, and pop anyone in SendIPCEvent out of their wait. |
| 12467 | pUnmanagedThread->ClearState(CUTS_GenericHijacked); |
| 12468 | |
| 12469 | this->m_helperThreadDead = true; |
| 12470 | |
| 12471 | // This only works on Windows, not on Mac. We don't support interop-debugging on Mac anyway. |
| 12472 | SetEvent(m_pEventChannel->GetRightSideEventAckHandle()); |
| 12473 | |
| 12474 | // Note: we remember that this was a second chance event from one of the special stack overflow |
| 12475 | // cases with CUES_ExceptionUnclearable. This tells us to force the process to terminate when we |
| 12476 | // continue from the event. Since for some odd reason the OS decides to re-raise this exception |
| 12477 | // (first chance then second chance) infinitely. |
| 12478 | |
| 12479 | _ASSERTE(pUnmanagedThread->HasIBEvent()); |
| 12480 | |
| 12481 | pUnmanagedThread->IBEvent()->SetState(CUES_ExceptionUnclearable); |
| 12482 | |
| 12483 | //newEvent = false; |
| 12484 | return REACTION(cInband_NotNewEvent); |
| 12485 | } |
| 12486 | } |
| 12487 | else |
| 12488 | { |
| 12489 | LOG((LF_CORDB, LL_INFO1000, "CP::TE1stCAI: Triage1stChanceNonSpecial\n" )); |
| 12490 | |
| 12491 | Reaction r(REACTION(cOOB)); |
| 12492 | HRESULT hrCheck = S_OK;; |
| 12493 | EX_TRY |
| 12494 | { |
| 12495 | r = Triage1stChanceNonSpecial(pUnmanagedThread, pEvent); |
| 12496 | } |
| 12497 | EX_CATCH_HRESULT(hrCheck); |
| 12498 | SIMPLIFYING_ASSUMPTION(SUCCEEDED(hrCheck)); |
| 12499 | SetUnrecoverableIfFailed(this, hrCheck); |
| 12500 | |
| 12501 | return r; |
| 12502 | |
| 12503 | } |
| 12504 | |
| 12505 | // At this point, any first-chance exceptions that could be special have been handled. Any |
| 12506 | // first-chance exception that we're still processing at this point is destined to be |
| 12507 | // dispatched as an unmanaged event. |
| 12508 | UNREACHABLE(); |
| 12509 | } |
| 12510 | |
| 12511 | |
| 12512 | //--------------------------------------------------------------------------------------- |
| 12513 | // |
| 12514 | // Triage a 2nd-chance exception when the CLR is initialized. |
| 12515 | // |
| 12516 | // Arguments: |
| 12517 | // pUnmanagedThread - thread that the event has occurred on. |
| 12518 | // pEvent - native debug event for the exception that occurred that this is triaging. |
| 12519 | // |
| 12520 | // Return Value: |
| 12521 | // Reaction for how to handle this event. |
| 12522 | // |
| 12523 | // Assumptions: |
| 12524 | // Called when receiving a debug event when the process is stopped. |
| 12525 | // |
| 12526 | // Notes: |
| 12527 | // We already hijacked 2nd-chance managed exceptions, so this is just handling |
| 12528 | // some V2 Interop corner cases. |
| 12529 | // @dbgtodo interop: this should eventually completely go away with the V3 design. |
| 12530 | // |
| 12531 | //--------------------------------------------------------------------------------------- |
| 12532 | Reaction CordbProcess::TriageExcep2ndChanceAndInit(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent) |
| 12533 | { |
| 12534 | _ASSERTE(ThreadHoldsProcessLock()); |
| 12535 | |
| 12536 | DWORD dwExCode = pEvent->u.Exception.ExceptionRecord.ExceptionCode; |
| 12537 | |
| 12538 | #ifdef _DEBUG |
| 12539 | // For debugging, add an extra knob that let us break on any 2nd chance exceptions. |
| 12540 | // Most tests don't throw 2nd-chance, so we could have this enabled most of the time and |
| 12541 | // catch bogus 2nd chance exceptions |
| 12542 | static DWORD dwNo2ndChance = -1; |
| 12543 | |
| 12544 | if (dwNo2ndChance == -1) |
| 12545 | { |
| 12546 | dwNo2ndChance = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgNo2ndChance); |
| 12547 | } |
| 12548 | |
| 12549 | if (dwNo2ndChance) |
| 12550 | { |
| 12551 | CONSISTENCY_CHECK_MSGF(false, ("2nd chance exception occurred on LS thread=0x%x, code=0x%08x, address=0x%p\n" |
| 12552 | "This assert is firing b/c you explicitly requested it by having the 'DbgNo2ndChance' knob enabled.\n" |
| 12553 | "Disable it to avoid asserts on 2nd chance." , |
| 12554 | pUnmanagedThread->m_id, |
| 12555 | dwExCode, |
| 12556 | pEvent->u.Exception.ExceptionRecord.ExceptionAddress)); |
| 12557 | } |
| 12558 | #endif |
| 12559 | |
| 12560 | |
| 12561 | // Second chance exception, Runtime initialized. It could belong to the Runtime, so we'll check. If it |
| 12562 | // does, then we'll hijack the thread. Otherwise, well just fall through and let it get |
| 12563 | // dispatched. Note: we do this so that the CLR's unhandled exception logic gets a chance to run even |
| 12564 | // though we've got a win32 debugger attached. But the unhandled exception logic never touches |
| 12565 | // breakpoint or single step exceptions, so we ignore those here, too. |
| 12566 | |
| 12567 | // There are strange cases with stack overflow exceptions. If a nieve application catches a stack |
| 12568 | // overflow exception and handles it, without resetting the guard page, then the app will get an AV when |
| 12569 | // it overflows the stack a second time. We will get the first chance AV, but when we continue from it the |
| 12570 | // OS won't run any SEH handlers, so our FCH won't actually work. Instead, we'll get the AV back on |
| 12571 | // second chance right away, and we'll end up right here. |
| 12572 | if (this->IsSpecialStackOverflowCase(pUnmanagedThread, pEvent)) |
| 12573 | { |
| 12574 | // IsSpecialStackOverflowCase will queue the event for us, so its no longer a "new event". Setting |
| 12575 | // newEvent = false here basically prevents us from playing with the event anymore and we fall down |
| 12576 | // to the dispatch logic below, which will get our already queued first chance AV dispatched for |
| 12577 | // this thread. |
| 12578 | //newEvent = false; |
| 12579 | return REACTION(cInband_NotNewEvent); |
| 12580 | } |
| 12581 | else if (this->IsHelperThreadWorked(pUnmanagedThread->GetOSTid())) |
| 12582 | { |
| 12583 | // A second chance exception from the helper thread. This is pretty bad... we just force continue |
| 12584 | // from them and hope for the best. |
| 12585 | return REACTION(cCLR); |
| 12586 | } |
| 12587 | |
| 12588 | if(pUnmanagedThread->IsCantStop()) |
| 12589 | { |
| 12590 | return REACTION(cOOB); |
| 12591 | } |
| 12592 | else |
| 12593 | { |
| 12594 | return REACTION(cInband); |
| 12595 | } |
| 12596 | } |
| 12597 | |
| 12598 | |
| 12599 | //--------------------------------------------------------------------------------------- |
| 12600 | // |
| 12601 | // Triage a win32 Debug event to get a reaction |
| 12602 | // |
| 12603 | // Arguments: |
| 12604 | // pUnmanagedThread - thread that the event has occurred on. |
| 12605 | // pEvent - native debug event for the exception that occurred that this is triaging. |
| 12606 | // |
| 12607 | // Return Value: |
| 12608 | // Reaction for how to handle this event. |
| 12609 | // |
| 12610 | // Assumptions: |
| 12611 | // Called when receiving a debug event when the process is stopped. |
| 12612 | // |
| 12613 | // Notes: |
| 12614 | // This is the main triage routine for Win32 debug events, this delegates to the |
| 12615 | // 1st and 2nd chance routines above appropriately. |
| 12616 | // |
| 12617 | //--------------------------------------------------------------------------------------- |
| 12618 | Reaction CordbProcess::TriageWin32DebugEvent(CordbUnmanagedThread * pUnmanagedThread, const DEBUG_EVENT * pEvent) |
| 12619 | { |
| 12620 | _ASSERTE(ThreadHoldsProcessLock()); |
| 12621 | |
| 12622 | // Lots of special cases for exception events. The vast majority of hybrid debugging work that takes |
| 12623 | // place is in response to exception events. The work below will consider certian exception events |
| 12624 | // special cases and rather than letting them be queued and dispatched, they will be handled right |
| 12625 | // here. |
| 12626 | if (pEvent->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) |
| 12627 | { |
| 12628 | STRESS_LOG4(LF_CORDB, LL_INFO1000, "CP::TW32DE: unmanaged exception on " |
| 12629 | "tid 0x%x, code 0x%08x, addr 0x%08x, chance %d\n" , |
| 12630 | pEvent->dwThreadId, |
| 12631 | pEvent->u.Exception.ExceptionRecord.ExceptionCode, |
| 12632 | pEvent->u.Exception.ExceptionRecord.ExceptionAddress, |
| 12633 | 2-pEvent->u.Exception.dwFirstChance); |
| 12634 | |
| 12635 | #ifdef LOGGING |
| 12636 | if (pEvent->u.Exception.ExceptionRecord.ExceptionCode == STATUS_ACCESS_VIOLATION) |
| 12637 | { |
| 12638 | LOG((LF_CORDB, LL_INFO1000, "\t<%s> address 0x%08x\n" , |
| 12639 | pEvent->u.Exception.ExceptionRecord.ExceptionInformation[0] ? "write to" : "read from" , |
| 12640 | pEvent->u.Exception.ExceptionRecord.ExceptionInformation[1])); |
| 12641 | } |
| 12642 | #endif |
| 12643 | |
| 12644 | // Mark the loader bp for kicks. We won't start managed attach until native attach is finished. |
| 12645 | if (!this->m_loaderBPReceived) |
| 12646 | { |
| 12647 | // If its a first chance breakpoint, and its the first one, then its the loader breakpoint. |
| 12648 | if (pEvent->u.Exception.dwFirstChance && |
| 12649 | (pEvent->u.Exception.ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)) |
| 12650 | { |
| 12651 | LOG((LF_CORDB, LL_INFO1000, "CP::TW32DE: loader breakpoint received.\n" )); |
| 12652 | |
| 12653 | // Remember that we've received the loader BP event. |
| 12654 | this->m_loaderBPReceived = true; |
| 12655 | |
| 12656 | // We never hijack the loader BP anymore (CLR 2.0+). |
| 12657 | // This is b/c w/ interop-attach, we don't start the managed-attach until _after_ Cordbg |
| 12658 | // continues from the loader-bp. |
| 12659 | } |
| 12660 | } // end of loader bp. |
| 12661 | |
| 12662 | // This event might be the retriggering of an event we already saw but previously had to hijack |
| 12663 | if(pUnmanagedThread->HasIBEvent()) |
| 12664 | { |
| 12665 | const EXCEPTION_RECORD* pRecord1 = &(pEvent->u.Exception.ExceptionRecord); |
| 12666 | const EXCEPTION_RECORD* pRecord2 = &(pUnmanagedThread->IBEvent()->m_currentDebugEvent.u.Exception.ExceptionRecord); |
| 12667 | if(pRecord1->ExceptionCode == pRecord2->ExceptionCode && |
| 12668 | pRecord1->ExceptionFlags == pRecord2->ExceptionFlags && |
| 12669 | pRecord1->ExceptionAddress == pRecord2->ExceptionAddress) |
| 12670 | { |
| 12671 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "CP::TW32DE: event is continuation of previously hijacked event.\n" ); |
| 12672 | // if we continued from the hijack then we should have already dispatched this event |
| 12673 | _ASSERTE(pUnmanagedThread->IBEvent()->IsDispatched()); |
| 12674 | return REACTION(cInbandExceptionRetrigger); |
| 12675 | } |
| 12676 | } |
| 12677 | |
| 12678 | // We only care about exception events if they are first chance events and if the Runtime is |
| 12679 | // initialized within the process. Otherwise, we don't do anything special with them. |
| 12680 | if (pEvent->u.Exception.dwFirstChance && this->m_initialized) |
| 12681 | { |
| 12682 | return TriageExcep1stChanceAndInit(pUnmanagedThread, pEvent); |
| 12683 | } |
| 12684 | else if (!pEvent->u.Exception.dwFirstChance && this->m_initialized) |
| 12685 | { |
| 12686 | return TriageExcep2ndChanceAndInit(pUnmanagedThread, pEvent); |
| 12687 | } |
| 12688 | else |
| 12689 | { |
| 12690 | // An exception event, but the Runtime hasn't been initialize. I.e., its an exception event |
| 12691 | // that we will never try to hijack. |
| 12692 | return REACTION(cInband); |
| 12693 | } |
| 12694 | |
| 12695 | UNREACHABLE(); |
| 12696 | } |
| 12697 | else |
| 12698 | // OOB |
| 12699 | { |
| 12700 | return REACTION(cOOB); |
| 12701 | } |
| 12702 | |
| 12703 | } |
| 12704 | |
| 12705 | //--------------------------------------------------------------------------------------- |
| 12706 | // |
| 12707 | // Top-level handler for a win32 debug event during Interop-debugging. |
| 12708 | // |
| 12709 | // Arguments: |
| 12710 | // event - native debug event to handle. |
| 12711 | // |
| 12712 | // Assumptions: |
| 12713 | // The process just got a native debug event via WaitForDebugEvent |
| 12714 | // |
| 12715 | // Notes: |
| 12716 | // The function will Triage the excpetion and then handle it based on the |
| 12717 | // appropriate reaction (see: code:Reaction). |
| 12718 | // |
| 12719 | // @dbgtodo interop: this should all go into the shim. |
| 12720 | //--------------------------------------------------------------------------------------- |
| 12721 | void CordbProcess::HandleDebugEventForInteropDebugging(const DEBUG_EVENT * pEvent) |
| 12722 | { |
| 12723 | PUBLIC_API_ENTRY_FOR_SHIM(this); |
| 12724 | _ASSERTE(IsInteropDebugging() || !"Only do this in real interop handling path" ); |
| 12725 | |
| 12726 | |
| 12727 | STRESS_LOG3(LF_CORDB, LL_INFO1000, "W32ET::W32EL: got unmanaged event %d on thread 0x%x, proc 0x%x\n" , |
| 12728 | pEvent->dwDebugEventCode, pEvent->dwThreadId, pEvent->dwProcessId); |
| 12729 | |
| 12730 | // Get the Lock. |
| 12731 | _ASSERTE(!this->ThreadHoldsProcessLock()); |
| 12732 | |
| 12733 | RSSmartPtr<CordbProcess> pRef(this); // make sure we're alive... |
| 12734 | |
| 12735 | RSLockHolder processLockHolder(&this->m_processMutex); |
| 12736 | |
| 12737 | // If we get a new Win32 Debug event, then we need to flush any cached oop data structures. |
| 12738 | // This includes refreshing DAC and our patch table. |
| 12739 | ForceDacFlush(); |
| 12740 | ClearPatchTable(); |
| 12741 | |
| 12742 | #ifdef _DEBUG |
| 12743 | // We want to detect if we've deadlocked. Unfortunately, w/ interop debugging, there can be a lot of |
| 12744 | // deadtime since we need to wait for a debug event. Thus the CPU usage may appear to be at 0%, but |
| 12745 | // we're not deadlocked b/c we're still receiving debug events. |
| 12746 | // So ping every X debug events. |
| 12747 | static int s_cCount = 0; |
| 12748 | static int s_iPingLevel = -1; |
| 12749 | if (s_iPingLevel == -1) |
| 12750 | { |
| 12751 | s_iPingLevel = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgPingInterop); |
| 12752 | } |
| 12753 | if (s_iPingLevel != 0) |
| 12754 | { |
| 12755 | s_cCount++; |
| 12756 | if (s_cCount >= s_iPingLevel) |
| 12757 | { |
| 12758 | s_cCount = 0; |
| 12759 | ::Beep(1000,100); |
| 12760 | |
| 12761 | // Refresh so we can adjust ping level midstream. |
| 12762 | s_iPingLevel = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgPingInterop); |
| 12763 | } |
| 12764 | } |
| 12765 | #endif |
| 12766 | |
| 12767 | bool fNewEvent = true; |
| 12768 | |
| 12769 | // Mark the process as stopped. |
| 12770 | this->m_state |= CordbProcess::PS_WIN32_STOPPED; |
| 12771 | |
| 12772 | CordbUnmanagedThread * pUnmanagedThread = GetUnmanagedThreadFromEvent(pEvent); |
| 12773 | |
| 12774 | // In retail, if there is no unmanaged thread then we just continue and loop back around. UnrecoverableError has |
| 12775 | // already been set in this case. Note: there is an issue in the Win32 debugging API that can cause duplicate |
| 12776 | // ExitThread events. We therefore must handle not finding an unmanaged thread gracefully. |
| 12777 | |
| 12778 | _ASSERTE((pUnmanagedThread != NULL) || (pEvent->dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT)); |
| 12779 | |
| 12780 | if (pUnmanagedThread == NULL) |
| 12781 | { |
| 12782 | // Note: we use ContinueDebugEvent directly here since our continue is very simple and all of our other |
| 12783 | // continue mechanisms rely on having an UnmanagedThread object to play with ;) |
| 12784 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "W32ET::W32EL: Continuing without thread on tid 0x%x, code=0x%x\n" , |
| 12785 | pEvent->dwThreadId, |
| 12786 | pEvent->dwDebugEventCode); |
| 12787 | |
| 12788 | this->m_state &= ~CordbProcess::PS_WIN32_STOPPED; |
| 12789 | |
| 12790 | BOOL fOk = ContinueDebugEvent(pEvent->dwProcessId, pEvent->dwThreadId, DBG_EXCEPTION_NOT_HANDLED); |
| 12791 | |
| 12792 | _ASSERTE(fOk || !"ContinueDebugEvent failed when he have no thread. Debuggee is likely hung" ); |
| 12793 | |
| 12794 | return; |
| 12795 | } |
| 12796 | |
| 12797 | // There's an innate race such that we can get a Debug Event even after we've suspended a thread. |
| 12798 | // This can happen if the thread has already dispatched the debug event but we haven't called WFDE to pick it up |
| 12799 | // yet. This is sufficiently goofy that we want to stress log it. |
| 12800 | if (pUnmanagedThread->IsSuspended()) |
| 12801 | { |
| 12802 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "W32ET::W32EL: Thread 0x%x is suspended\n" , pEvent->dwThreadId); |
| 12803 | } |
| 12804 | |
| 12805 | // For debugging crazy races in retail, we'll keep a rolling queue of win32 debug events. |
| 12806 | this->DebugRecordWin32Event(pEvent, pUnmanagedThread); |
| 12807 | |
| 12808 | |
| 12809 | // Check to see if shutdown of the in-proc debugging services has begun. If it has, then we know we'll no longer |
| 12810 | // be running any managed code, and we know that we can stop hijacking threads. We remember this by setting |
| 12811 | // m_initialized to false, thus preventing most things from happening elsewhere. |
| 12812 | // Don't even bother checking the DCB fields until it's been verified (m_initialized == true) |
| 12813 | if (this->m_initialized && (this->GetDCB() != NULL)) |
| 12814 | { |
| 12815 | UpdateRightSideDCB(); |
| 12816 | if (this->GetDCB()->m_shutdownBegun) |
| 12817 | { |
| 12818 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "W32ET::W32EL: shutdown begun...\n" ); |
| 12819 | this->m_initialized = false; |
| 12820 | } |
| 12821 | } |
| 12822 | |
| 12823 | #ifdef _DEBUG |
| 12824 | //Verify that GetThreadContext agrees with the exception address |
| 12825 | if (pEvent->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) |
| 12826 | { |
| 12827 | DT_CONTEXT tempDebugContext; |
| 12828 | tempDebugContext.ContextFlags = DT_CONTEXT_FULL; |
| 12829 | DbiGetThreadContext(pUnmanagedThread->m_handle, &tempDebugContext); |
| 12830 | CordbUnmanagedThread::LogContext(&tempDebugContext); |
| 12831 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64) |
| 12832 | const ULONG_PTR breakpointOpcodeSize = 1; |
| 12833 | #elif defined(DBG_TARGET_ARM64) |
| 12834 | const ULONG_PTR breakpointOpcodeSize = 4; |
| 12835 | #else |
| 12836 | const ULONG_PTR breakpointOpcodeSize = 1; |
| 12837 | PORTABILITY_ASSERT("NYI: Breakpoint size offset for this platform" ); |
| 12838 | #endif |
| 12839 | _ASSERTE(CORDbgGetIP(&tempDebugContext) == pEvent->u.Exception.ExceptionRecord.ExceptionAddress || |
| 12840 | (DWORD)CORDbgGetIP(&tempDebugContext) == ((DWORD)pEvent->u.Exception.ExceptionRecord.ExceptionAddress)+breakpointOpcodeSize); |
| 12841 | } |
| 12842 | #endif |
| 12843 | |
| 12844 | // This call will decide what to do w/ the the win32 event we just got. It does a lot of work. |
| 12845 | Reaction reaction = TriageWin32DebugEvent(pUnmanagedThread, pEvent); |
| 12846 | |
| 12847 | |
| 12848 | // Stress-log the reaction. |
| 12849 | #ifdef _DEBUG |
| 12850 | STRESS_LOG3(LF_CORDB, LL_INFO1000, "Reaction: %d (%s), line=%d\n" , |
| 12851 | reaction.GetType(), |
| 12852 | reaction.GetReactionName(), |
| 12853 | reaction.GetLine()); |
| 12854 | #else |
| 12855 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "Reaction: %d\n" , reaction.GetType()); |
| 12856 | #endif |
| 12857 | |
| 12858 | // Make sure the lock wasn't accidentally released. |
| 12859 | _ASSERTE(ThreadHoldsProcessLock()); |
| 12860 | CordbWin32EventThread * pW32EventThread = this->m_pShim->GetWin32EventThread(); |
| 12861 | _ASSERTE(pW32EventThread != NULL); |
| 12862 | |
| 12863 | // if we were waiting for a retriggered exception but recieved any other event then turn |
| 12864 | // off the single stepping and dequeue the IB event. Right now we only use the SS flag internally |
| 12865 | // for stepping during possible retrigger. |
| 12866 | if(reaction.GetType() != Reaction::cInbandExceptionRetrigger && pUnmanagedThread->IsSSFlagNeeded()) |
| 12867 | { |
| 12868 | _ASSERTE(pUnmanagedThread->HasIBEvent()); |
| 12869 | CordbUnmanagedEvent* pUnmanagedEvent = pUnmanagedThread->IBEvent(); |
| 12870 | _ASSERTE(pUnmanagedEvent->IsIBEvent()); |
| 12871 | _ASSERTE(pUnmanagedEvent->IsEventContinuedUnhijacked()); |
| 12872 | _ASSERTE(pUnmanagedEvent->IsDispatched()); |
| 12873 | LOG((LF_CORDB, LL_INFO100000, "CP::HDEFID: IB event did not retrigger ue=0x%p\n" , pUnmanagedEvent)); |
| 12874 | |
| 12875 | DequeueUnmanagedEvent(pUnmanagedThread); |
| 12876 | pUnmanagedThread->EndStepping(); |
| 12877 | } |
| 12878 | |
| 12879 | switch(reaction.GetType()) |
| 12880 | { |
| 12881 | // Common for flares. |
| 12882 | case Reaction::cIgnore: |
| 12883 | |
| 12884 | // Shouldn't be suspending in the first place with outstanding flares. |
| 12885 | _ASSERTE(!pUnmanagedThread->IsSuspended()); |
| 12886 | |
| 12887 | pW32EventThread->ForceDbgContinue(this, pUnmanagedThread, DBG_CONTINUE, false); |
| 12888 | goto LDone; |
| 12889 | |
| 12890 | case Reaction::cCLR: |
| 12891 | // Don't care if thread is suspended here. We'll just let the thread continue whatever it's doing. |
| 12892 | |
| 12893 | this->m_DbgSupport.m_TotalCLR++; |
| 12894 | |
| 12895 | // If this is for the CLR, then we just continue unhandled and know that the CLR has |
| 12896 | // a handler inplace to deal w/ this exception. |
| 12897 | pW32EventThread->ForceDbgContinue(this, pUnmanagedThread, DBG_EXCEPTION_NOT_HANDLED, false); |
| 12898 | goto LDone; |
| 12899 | |
| 12900 | |
| 12901 | case Reaction::cInband_NotNewEvent: |
| 12902 | fNewEvent = false; |
| 12903 | |
| 12904 | // fall through to Inband case... |
| 12905 | |
| 12906 | case Reaction::cInband: |
| 12907 | { |
| 12908 | this->m_DbgSupport.m_TotalIB++; |
| 12909 | |
| 12910 | // Hijack in-band events (exception events, exit threads) if there is already an event at the head |
| 12911 | // of the queue or if the process is currently synchronized. Of course, we only do this if the |
| 12912 | // process is initialized. |
| 12913 | // |
| 12914 | // Note: we also hijack these left over in-band events if we're actively trying to send the |
| 12915 | // managed continue message to the Left Side. This is controlled by m_specialDeferment below. |
| 12916 | |
| 12917 | // Only exceptions can be IB events - everything else is OOB. |
| 12918 | _ASSERTE(pEvent->dwDebugEventCode == EXCEPTION_DEBUG_EVENT); |
| 12919 | |
| 12920 | // CLR internal exceptions should be sent back to the CLR and never treated as inband events. |
| 12921 | // If this assert fires, the event was triaged wrong. |
| 12922 | CONSISTENCY_CHECK_MSGF((pEvent->u.Exception.ExceptionRecord.ExceptionCode != EXCEPTION_COMPLUS), |
| 12923 | ("Attempting to dispatch a CLR internal exception as an Inband event. Reaction line=%d\n" , |
| 12924 | reaction.GetLine())); |
| 12925 | |
| 12926 | |
| 12927 | _ASSERTE(!pUnmanagedThread->IsCantStop()); |
| 12928 | |
| 12929 | // We need to decide whether or not to dispatch this event immediately |
| 12930 | // We defer it to enforce that we only dispatch 1 IB event at a time (managed events are |
| 12931 | // considered IB here). |
| 12932 | // This means if: |
| 12933 | // 1) there's already an outstanding unmanaged inband event (an event the user has not continued from) |
| 12934 | // 2) If the process is synchronized (since that means we've already dispatched a managed event). |
| 12935 | // 3) If we've received a SyncComplete event, but aren't yet Sync. This will almost always be the same as |
| 12936 | // whether we're synced, but has a distict quality. It's always set by the w32 event thread in Interop, |
| 12937 | // and so it's guaranteed to be serialized against this check here (also on the w32et). |
| 12938 | // 4) Special deferment - This covers the region where we're sending a Stop/Continue IPC event across. |
| 12939 | // We defer it here to keep the Helper thread alive so that it can handle these IPC events. |
| 12940 | // Queued events will be dispatched when continue is called. |
| 12941 | BOOL fHasUserUncontinuedNativeEvents = HasUserUncontinuedNativeEvents(); |
| 12942 | bool fDeferInbandEvent = (fHasUserUncontinuedNativeEvents || |
| 12943 | GetSynchronized() || |
| 12944 | GetSyncCompleteRecv() || |
| 12945 | m_specialDeferment); |
| 12946 | |
| 12947 | // If we've got a new event, queue it. |
| 12948 | if (fNewEvent) |
| 12949 | { |
| 12950 | this->QueueUnmanagedEvent(pUnmanagedThread, pEvent); |
| 12951 | } |
| 12952 | |
| 12953 | if (fNewEvent && this->m_initialized && fDeferInbandEvent) |
| 12954 | { |
| 12955 | STRESS_LOG4(LF_CORDB, LL_INFO1000, "W32ET::W32EL: Needed to defer dispatching event: %d %d %d %d\n" , |
| 12956 | fHasUserUncontinuedNativeEvents, |
| 12957 | GetSynchronized(), |
| 12958 | GetSyncCompleteRecv(), |
| 12959 | m_specialDeferment); |
| 12960 | |
| 12961 | // this continues the IB debug event into the hijack |
| 12962 | // the process is now running again |
| 12963 | pW32EventThread->DoDbgContinue(this, pUnmanagedThread->IBEvent()); |
| 12964 | |
| 12965 | // Since we've hijacked this event, we don't need to do any further processing. |
| 12966 | goto LDone; |
| 12967 | } |
| 12968 | else |
| 12969 | { |
| 12970 | // No need to defer the dispatch, do it now |
| 12971 | this->DispatchUnmanagedInBandEvent(); |
| 12972 | |
| 12973 | goto LDone; |
| 12974 | } |
| 12975 | UNREACHABLE(); |
| 12976 | } |
| 12977 | |
| 12978 | case Reaction::cFirstChanceHijackStarted: |
| 12979 | { |
| 12980 | // determine the logical event we are handling, if any |
| 12981 | CordbUnmanagedEvent* pUnmanagedEvent = NULL; |
| 12982 | if(pUnmanagedThread->HasIBEvent()) |
| 12983 | { |
| 12984 | pUnmanagedEvent = pUnmanagedThread->IBEvent(); |
| 12985 | } |
| 12986 | LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL: IB hijack starting, ue=0x%p\n" , pUnmanagedEvent)); |
| 12987 | |
| 12988 | // fetch the LS memory set up for this hijack |
| 12989 | REMOTE_PTR pDebuggerWord = NULL; |
| 12990 | DebuggerIPCFirstChanceData fcd; |
| 12991 | pUnmanagedThread->GetEEDebuggerWord(&pDebuggerWord); |
| 12992 | SafeReadStruct(PTR_TO_CORDB_ADDRESS(pDebuggerWord), &fcd); |
| 12993 | |
| 12994 | LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL: old fcd DebugCounter=0x%x\n" , fcd.debugCounter)); |
| 12995 | |
| 12996 | // determine what action the LS should take |
| 12997 | if(pUnmanagedThread->IsBlockingForSync()) |
| 12998 | { |
| 12999 | // there should be an event we hijacked in this case |
| 13000 | _ASSERTE(pUnmanagedEvent != NULL); |
| 13001 | |
| 13002 | // block that event |
| 13003 | LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL: blocking\n" )); |
| 13004 | fcd.action = HIJACK_ACTION_WAIT; |
| 13005 | fcd.debugCounter = 0x2; |
| 13006 | SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pDebuggerWord), &fcd); |
| 13007 | } |
| 13008 | else |
| 13009 | { |
| 13010 | // we don't need to block. We want the vectored handler to just exit |
| 13011 | // as if it wasn't there |
| 13012 | _ASSERTE(fcd.action == HIJACK_ACTION_EXIT_UNHANDLED); |
| 13013 | LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL: not blocking\n" )); |
| 13014 | } |
| 13015 | |
| 13016 | LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL: continuing from flare\n" )); |
| 13017 | pW32EventThread->ForceDbgContinue(this, pUnmanagedThread, DBG_CONTINUE, false); |
| 13018 | goto LDone; |
| 13019 | } |
| 13020 | |
| 13021 | case Reaction::cInbandHijackComplete: |
| 13022 | { |
| 13023 | // We now execute the hijack worker even when not actually hijacked |
| 13024 | // so can't assert this |
| 13025 | //_ASSERTE(pUnmanagedThread->IsFirstChanceHijacked()); |
| 13026 | |
| 13027 | // we should not be stepping at the end of hijacks |
| 13028 | _ASSERTE(!pUnmanagedThread->IsSSFlagHidden()); |
| 13029 | _ASSERTE(!pUnmanagedThread->IsSSFlagNeeded()); |
| 13030 | |
| 13031 | // if we were hijacked then clean up |
| 13032 | if(pUnmanagedThread->IsFirstChanceHijacked()) |
| 13033 | { |
| 13034 | LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL: hijack complete will restore context...\n" )); |
| 13035 | DT_CONTEXT tempContext = { 0 }; |
| 13036 | tempContext.ContextFlags = DT_CONTEXT_FULL; |
| 13037 | HRESULT hr = pUnmanagedThread->GetThreadContext(&tempContext); |
| 13038 | _ASSERTE(SUCCEEDED(hr)); |
| 13039 | |
| 13040 | // The sync hijack returns normally but the m2uHandoff hijack needs to have the IP |
| 13041 | // deliberately restored |
| 13042 | if(!pUnmanagedThread->IsBlockingForSync()) |
| 13043 | { |
| 13044 | // restore the context to the current un-hijacked context |
| 13045 | BOOL succ = DbiSetThreadContext(pUnmanagedThread->m_handle, &tempContext); |
| 13046 | _ASSERTE(succ); |
| 13047 | |
| 13048 | // Because hijacks don't return normally they might have pushed handlers without poping them |
| 13049 | // back off. To take care of that we explicitly restore the old SEH chain. |
| 13050 | #ifdef DBG_TARGET_X86 |
| 13051 | hr = pUnmanagedThread->RestoreLeafSeh(); |
| 13052 | _ASSERTE(SUCCEEDED(hr)); |
| 13053 | #endif |
| 13054 | } |
| 13055 | else |
| 13056 | { |
| 13057 | _ASSERTE(pUnmanagedThread->HasIBEvent()); |
| 13058 | CordbUnmanagedEvent* pUnmanagedEvent = pUnmanagedThread->IBEvent(); |
| 13059 | LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL: IB hijack completing, continuing unhijacked ue=0x%p\n" , pUnmanagedEvent)); |
| 13060 | _ASSERTE(pUnmanagedEvent->IsEventContinuedHijacked()); |
| 13061 | _ASSERTE(pUnmanagedEvent->IsDispatched()); |
| 13062 | _ASSERTE(pUnmanagedEvent->IsEventUserContinued()); |
| 13063 | _ASSERTE(!pUnmanagedEvent->IsEventContinuedUnhijacked()); |
| 13064 | pUnmanagedEvent->SetState(CUES_EventContinuedUnhijacked); |
| 13065 | |
| 13066 | // fetch the LS memory set up for this hijack |
| 13067 | REMOTE_PTR pDebuggerWord = NULL; |
| 13068 | DebuggerIPCFirstChanceData fcd; |
| 13069 | pUnmanagedThread->GetEEDebuggerWord(&pDebuggerWord); |
| 13070 | SafeReadStruct(PTR_TO_CORDB_ADDRESS(pDebuggerWord), &fcd); |
| 13071 | |
| 13072 | LOG((LF_CORDB, LL_INFO10000, "W32ET::W32EL: pDebuggerWord is 0x%p\n" , pDebuggerWord)); |
| 13073 | |
| 13074 | //set the correct continuation action based upon the user's selection |
| 13075 | if(pUnmanagedEvent->IsExceptionCleared()) |
| 13076 | { |
| 13077 | LOG((LF_CORDB, LL_INFO10000, "W32ET::W32EL: exception cleared\n" )); |
| 13078 | fcd.action = HIJACK_ACTION_EXIT_HANDLED; |
| 13079 | } |
| 13080 | else |
| 13081 | { |
| 13082 | LOG((LF_CORDB, LL_INFO10000, "W32ET::W32EL: exception not cleared\n" )); |
| 13083 | fcd.action = HIJACK_ACTION_EXIT_UNHANDLED; |
| 13084 | } |
| 13085 | |
| 13086 | // |
| 13087 | // LS context is restored here so that execution continues from next instruction that caused the hijack. |
| 13088 | // We shouldn't always restore the LS context though. |
| 13089 | // Consider the following case where this can cause issues: |
| 13090 | // Debuggee process hits an exception and calls KERNELBASE!RaiseException, debugger gets the notification and |
| 13091 | // prepares for first-chance hijack. Debugger(DBI) saves the current thread context (see SetupFirstChanceHijackForSync) which is restored |
| 13092 | // later below (see SafeWriteThreadContext call) when the process is in VEH (CLRVectoredExceptionHandlerShim->FirstChanceSuspendHijackWorker). |
| 13093 | // The thread context that got saved(by SetupFirstChanceHijackForSync) was for when the thread was executing RaiseException and when |
| 13094 | // this context gets restored in VEH, the thread resumes after the exception handler with a context that is not same as one with which |
| 13095 | // it entered. This inconsistency can lead to bad execution code-paths or even a debuggee crash. |
| 13096 | // |
| 13097 | // Example case where we should definitely update the LS context: |
| 13098 | // After a DbgBreakPoint call, IP gets updated to point to the instruction after int 3 and this is the context saved by debugger. |
| 13099 | // The IP in context passed to VEH still points to int 3 though and if we don't update the LS context in VEH, the breakpoint |
| 13100 | // instruction will get executed again. |
| 13101 | // |
| 13102 | // Here's a list of cases when we update the LS context: |
| 13103 | // * we know that context was explicitly updated during this hijack, OR |
| 13104 | // * if single-stepping flag was set on it originally, OR |
| 13105 | // * if this was a breakpoint event |
| 13106 | // Note that above list is a heuristic and it is possible that we need to add more such cases in future. |
| 13107 | // |
| 13108 | BOOL isBreakPointEvent = (pUnmanagedEvent->m_currentDebugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT && |
| 13109 | pUnmanagedEvent->m_currentDebugEvent.u.Exception.ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT); |
| 13110 | if (pUnmanagedThread->IsContextSet() || IsSSFlagEnabled(&tempContext) || isBreakPointEvent) |
| 13111 | { |
| 13112 | _ASSERTE(fcd.pLeftSideContext != NULL); |
| 13113 | LOG((LF_CORDB, LL_INFO10000, "W32ET::W32EL: updating LS context at 0x%p\n" , fcd.pLeftSideContext)); |
| 13114 | // write the new context over the old one on the LS |
| 13115 | SafeWriteThreadContext(fcd.pLeftSideContext, &tempContext); |
| 13116 | } |
| 13117 | |
| 13118 | // Write the new Fcd data to the LS |
| 13119 | fcd.debugCounter = 0x1; |
| 13120 | SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pDebuggerWord), &fcd); |
| 13121 | |
| 13122 | fcd.debugCounter = 0; |
| 13123 | SafeReadStruct(PTR_TO_CORDB_ADDRESS(pDebuggerWord), &fcd); |
| 13124 | _ASSERTE(fcd.debugCounter == 1); |
| 13125 | |
| 13126 | DequeueUnmanagedEvent(pUnmanagedThread); |
| 13127 | } |
| 13128 | |
| 13129 | _ASSERTE(m_cFirstChanceHijackedThreads > 0); |
| 13130 | m_cFirstChanceHijackedThreads--; |
| 13131 | if(m_cFirstChanceHijackedThreads == 0) |
| 13132 | { |
| 13133 | m_state &= ~PS_HIJACKS_IN_PLACE; |
| 13134 | } |
| 13135 | |
| 13136 | pUnmanagedThread->ClearState(CUTS_FirstChanceHijacked); |
| 13137 | pUnmanagedThread->ClearState(CUTS_BlockingForSync); |
| 13138 | |
| 13139 | // if the user set the context it either was already applied (m2uHandoff hijack) |
| 13140 | // or is about to be applied when the hijack returns (sync hijack). |
| 13141 | // There may still a small window where it won't appear accurate that |
| 13142 | // we just have to live with |
| 13143 | pUnmanagedThread->ClearState(CUTS_HasContextSet); |
| 13144 | } |
| 13145 | |
| 13146 | pW32EventThread->ForceDbgContinue(this, pUnmanagedThread, DBG_CONTINUE, false); |
| 13147 | |
| 13148 | // We've handled this event. Skip further processing. |
| 13149 | goto LDone; |
| 13150 | } |
| 13151 | |
| 13152 | case Reaction::cBreakpointRequiringHijack: |
| 13153 | { |
| 13154 | HRESULT hr = pUnmanagedThread->SetupFirstChanceHijack(EHijackReason::kM2UHandoff, &(pEvent->u.Exception.ExceptionRecord)); |
| 13155 | _ASSERTE(SUCCEEDED(hr)); |
| 13156 | pW32EventThread->ForceDbgContinue(this, pUnmanagedThread, DBG_CONTINUE, false); |
| 13157 | goto LDone; |
| 13158 | } |
| 13159 | |
| 13160 | case Reaction::cInbandExceptionRetrigger: |
| 13161 | { |
| 13162 | // this should be unused now |
| 13163 | _ASSERTE(FALSE); |
| 13164 | _ASSERTE(pUnmanagedThread->HasIBEvent()); |
| 13165 | CordbUnmanagedEvent* pUnmanagedEvent = pUnmanagedThread->IBEvent(); |
| 13166 | _ASSERTE(pUnmanagedEvent->IsIBEvent()); |
| 13167 | _ASSERTE(pUnmanagedEvent->IsEventContinuedUnhijacked()); |
| 13168 | _ASSERTE(pUnmanagedEvent->IsDispatched()); |
| 13169 | LOG((LF_CORDB, LL_INFO100000, "W32ET::W32EL: IB event completing, continuing ue=0x%p\n" , pUnmanagedEvent)); |
| 13170 | |
| 13171 | DequeueUnmanagedEvent(pUnmanagedThread); |
| 13172 | // If this event came from RaiseException then flush the context to ensure we won't use it until we re-enter |
| 13173 | if(pUnmanagedEvent->m_owner->IsRaiseExceptionHijacked()) |
| 13174 | { |
| 13175 | pUnmanagedEvent->m_owner->RestoreFromRaiseExceptionHijack(); |
| 13176 | pUnmanagedEvent->m_owner->ClearRaiseExceptionEntryContext(); |
| 13177 | } |
| 13178 | else // otherwise we should have been stepping |
| 13179 | { |
| 13180 | pUnmanagedThread->EndStepping(); |
| 13181 | } |
| 13182 | pW32EventThread->ForceDbgContinue(this, pUnmanagedThread, |
| 13183 | pUnmanagedEvent->IsExceptionCleared() ? DBG_CONTINUE : DBG_EXCEPTION_NOT_HANDLED, false); |
| 13184 | |
| 13185 | // We've handled this event. Skip further processing. |
| 13186 | goto LDone; |
| 13187 | } |
| 13188 | |
| 13189 | case Reaction::cOOB: |
| 13190 | { |
| 13191 | // Don't care if this thread claimed to be suspended or not. Dispatch event anyways. After all, |
| 13192 | // OOB events can come at *any* time. |
| 13193 | |
| 13194 | // This thread may be suspended. We don't care. |
| 13195 | this->m_DbgSupport.m_TotalOOB++; |
| 13196 | |
| 13197 | // Not an inband event. This includes ALL non-exception events (including EXIT_THREAD) as |
| 13198 | // well as any exception that can't be hijacked (ex, an exception on the helper thread). |
| 13199 | |
| 13200 | // If this is an exit thread or exit process event, then we need to mark the unmanaged thread as |
| 13201 | // exited for later. |
| 13202 | if ((pEvent->dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) || |
| 13203 | (pEvent->dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT)) |
| 13204 | { |
| 13205 | pUnmanagedThread->SetState(CUTS_Deleted); |
| 13206 | } |
| 13207 | |
| 13208 | // If we get an exit process or exit thread event on the helper thread, then we know we're loosing |
| 13209 | // the Left Side, so go ahead and remember that the helper thread has died. |
| 13210 | if (this->IsHelperThreadWorked(pUnmanagedThread->GetOSTid())) |
| 13211 | { |
| 13212 | if ((pEvent->dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) || |
| 13213 | (pEvent->dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT)) |
| 13214 | { |
| 13215 | this->m_helperThreadDead = true; |
| 13216 | } |
| 13217 | } |
| 13218 | |
| 13219 | // Queue the current out-of-band event. |
| 13220 | this->QueueOOBUnmanagedEvent(pUnmanagedThread, pEvent); |
| 13221 | |
| 13222 | // Go ahead and dispatch the event if its the first one. |
| 13223 | if (this->m_outOfBandEventQueue == pUnmanagedThread->OOBEvent()) |
| 13224 | { |
| 13225 | // Set this to true to indicate to Continue() that we're in the unamnaged callback. |
| 13226 | CordbUnmanagedEvent * pUnmanagedEvent = pUnmanagedThread->OOBEvent(); |
| 13227 | |
| 13228 | this->m_dispatchingOOBEvent = true; |
| 13229 | |
| 13230 | pUnmanagedEvent->SetState(CUES_Dispatched); |
| 13231 | |
| 13232 | this->Unlock(); |
| 13233 | |
| 13234 | // Handler should have been registered by now. |
| 13235 | _ASSERTE(this->m_cordb->m_unmanagedCallback != NULL); |
| 13236 | |
| 13237 | // Call the callback with fIsOutOfBand = TRUE. |
| 13238 | { |
| 13239 | PUBLIC_WIN32_CALLBACK_IN_THIS_SCOPE(this, pEvent, TRUE); |
| 13240 | this->m_cordb->m_unmanagedCallback->DebugEvent(const_cast<DEBUG_EVENT*> (pEvent), TRUE); |
| 13241 | } |
| 13242 | |
| 13243 | this->Lock(); |
| 13244 | |
| 13245 | // If m_dispatchingOOBEvent is false, that means that the user called Continue() from within |
| 13246 | // the callback. We know that we can go ahead and continue the process now. |
| 13247 | if (this->m_dispatchingOOBEvent == false) |
| 13248 | { |
| 13249 | // Note: this call will dispatch more OOB events if necessary. |
| 13250 | pW32EventThread->UnmanagedContinue(this, cOobUMContinue); |
| 13251 | } |
| 13252 | else |
| 13253 | { |
| 13254 | // We're not dispatching anymore, so set this back to false. |
| 13255 | this->m_dispatchingOOBEvent = false; |
| 13256 | } |
| 13257 | } |
| 13258 | |
| 13259 | // We've handled this event. Skip further processing. |
| 13260 | goto LDone; |
| 13261 | } |
| 13262 | } // end Switch on Reaction |
| 13263 | |
| 13264 | UNREACHABLE(); |
| 13265 | |
| 13266 | LDone: |
| 13267 | // Process Lock implicitly released by holder. |
| 13268 | |
| 13269 | STRESS_LOG0(LF_CORDB, LL_INFO1000, "W32ET::W32EL: done processing event.\n" ); |
| 13270 | |
| 13271 | return; |
| 13272 | } |
| 13273 | |
| 13274 | // |
| 13275 | // Returns true if the exception is a flare from the left side, false otherwise. |
| 13276 | // |
| 13277 | bool CordbProcess::ExceptionIsFlare(DWORD exceptionCode, const void *exceptionAddress) |
| 13278 | { |
| 13279 | _ASSERTE(m_runtimeOffsetsInitialized); |
| 13280 | |
| 13281 | // Can't have a flare if the left side isn't initialized |
| 13282 | if (m_initialized) |
| 13283 | { |
| 13284 | DebuggerIPCRuntimeOffsets *pRO = &m_runtimeOffsets; |
| 13285 | |
| 13286 | // All flares are breakpoints... |
| 13287 | if (exceptionCode == STATUS_BREAKPOINT) |
| 13288 | { |
| 13289 | // Does the breakpoint address match a flare address? |
| 13290 | if ((exceptionAddress == pRO->m_signalHijackStartedBPAddr) || |
| 13291 | (exceptionAddress == pRO->m_excepForRuntimeHandoffStartBPAddr) || |
| 13292 | (exceptionAddress == pRO->m_excepForRuntimeHandoffCompleteBPAddr) || |
| 13293 | (exceptionAddress == pRO->m_signalHijackCompleteBPAddr) || |
| 13294 | (exceptionAddress == pRO->m_excepNotForRuntimeBPAddr) || |
| 13295 | (exceptionAddress == pRO->m_notifyRSOfSyncCompleteBPAddr)) |
| 13296 | return true; |
| 13297 | } |
| 13298 | } |
| 13299 | |
| 13300 | return false; |
| 13301 | } |
| 13302 | #endif // FEATURE_INTEROP_DEBUGGING |
| 13303 | |
| 13304 | // Allocate a buffer in the target and copy data into it. |
| 13305 | // |
| 13306 | // Arguments: |
| 13307 | // pDomain - an appdomain associated with the allocation request. |
| 13308 | // bufferSize - size of the buffer in bytes |
| 13309 | // bufferFrom - local buffer of data (bufferSize bytes) to copy data from. |
| 13310 | // ppRes - address into target of allocated buffer |
| 13311 | // |
| 13312 | // Returns: |
| 13313 | // S_OK on success, else error. |
| 13314 | HRESULT CordbProcess::GetAndWriteRemoteBuffer(CordbAppDomain *pDomain, unsigned int bufferSize, const void *bufferFrom, void **ppRes) |
| 13315 | { |
| 13316 | _ASSERTE(ppRes != NULL); |
| 13317 | *ppRes = NULL; |
| 13318 | |
| 13319 | HRESULT hr = S_OK; |
| 13320 | |
| 13321 | EX_TRY |
| 13322 | { |
| 13323 | TargetBuffer tbTarget = GetRemoteBuffer(bufferSize); // throws |
| 13324 | SafeWriteBuffer(tbTarget, (const BYTE*) bufferFrom); // throws |
| 13325 | |
| 13326 | // Succeeded. |
| 13327 | *ppRes = CORDB_ADDRESS_TO_PTR(tbTarget.pAddress); |
| 13328 | } |
| 13329 | EX_CATCH_HRESULT(hr); |
| 13330 | return hr; |
| 13331 | } |
| 13332 | |
| 13333 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 13334 | |
| 13335 | // |
| 13336 | // Checks to see if the given second chance exception event actually signifies the death of the process due to a second |
| 13337 | // stack overflow special case. |
| 13338 | // |
| 13339 | // There are strange cases with stack overflow exceptions. If a nieve application catches a stack overflow exception and |
| 13340 | // handles it, without resetting the guard page, then the app will get an AV when it overflows the stack a second time. We |
| 13341 | // will get the first chance AV, but when we continue from it the OS won't run any SEH handlers, so our FCH won't |
| 13342 | // actually work. Instead, we'll get the AV back on second chance right away. |
| 13343 | // |
| 13344 | bool CordbProcess::IsSpecialStackOverflowCase(CordbUnmanagedThread *pUThread, const DEBUG_EVENT *pEvent) |
| 13345 | { |
| 13346 | _ASSERTE(pEvent->dwDebugEventCode == EXCEPTION_DEBUG_EVENT); |
| 13347 | _ASSERTE(pEvent->u.Exception.dwFirstChance == 0); |
| 13348 | |
| 13349 | // If this is not an AV, it can't be our special case. |
| 13350 | if (pEvent->u.Exception.ExceptionRecord.ExceptionCode != STATUS_ACCESS_VIOLATION) |
| 13351 | return false; |
| 13352 | |
| 13353 | // If the thread isn't already first chance hijacked, it can't be our special case. |
| 13354 | if (!pUThread->IsFirstChanceHijacked()) |
| 13355 | return false; |
| 13356 | |
| 13357 | // The first chance hijack didn't take, so we're not FCH anymore and we're not waiting for an answer |
| 13358 | // anymore... Note: by leaving this thread completely unhijacked, we'll report its true context, which is correct. |
| 13359 | pUThread->ClearState(CUTS_FirstChanceHijacked); |
| 13360 | |
| 13361 | // The process is techincally dead as a door nail here, so we'll mark that the helper thread is dead so our managed |
| 13362 | // API bails nicely. |
| 13363 | m_helperThreadDead = true; |
| 13364 | |
| 13365 | // Remember we're in our special case. |
| 13366 | pUThread->SetState(CUTS_HasSpecialStackOverflowCase); |
| 13367 | |
| 13368 | // Now, remember the second chance AV event in the second IB event slot for this thread and add it to the end of the |
| 13369 | // IB event queue. |
| 13370 | QueueUnmanagedEvent(pUThread, pEvent); |
| 13371 | |
| 13372 | // Note: returning true will ensure that the queued first chance AV for this thread is dispatched. |
| 13373 | return true; |
| 13374 | } |
| 13375 | |
| 13376 | //----------------------------------------------------------------------------- |
| 13377 | // Longhorn broke ContinueDebugEvent. |
| 13378 | // In previous OS releases, DBG_CONTINUE would continue a non-continuable exception. |
| 13379 | // In longhorn, we need to pass the DBG_FORCE_CONTINUE flag to do that. |
| 13380 | // Note that all CLR exceptions are non-continuable. |
| 13381 | // Now instead of DBG_CONTINUE, we need to pass DBG_FORCE_CONTINUE. |
| 13382 | //----------------------------------------------------------------------------- |
| 13383 | |
| 13384 | // Currently we don't have headers for the longhorn winnt.h. So we need to privately declare |
| 13385 | // this here. We have a check such that if we do get headers, the value won't change underneath us. |
| 13386 | #define MY_DBG_FORCE_CONTINUE ((DWORD )0x00010003L) |
| 13387 | #ifndef DBG_FORCE_CONTINUE |
| 13388 | #define DBG_FORCE_CONTINUE MY_DBG_FORCE_CONTINUE |
| 13389 | #else |
| 13390 | static_assert_no_msg(DBG_FORCE_CONTINUE == MY_DBG_FORCE_CONTINUE); |
| 13391 | #endif |
| 13392 | |
| 13393 | DWORD GetDbgContinueFlag() |
| 13394 | { |
| 13395 | // Currently, default to not using the new DBG_FORCE_CONTINUE flag. |
| 13396 | static ConfigDWORD fNoFlagKey; |
| 13397 | bool fNoFlag = fNoFlagKey.val(CLRConfig::UNSUPPORTED_DbgNoForceContinue) != 0; |
| 13398 | |
| 13399 | |
| 13400 | if (!fNoFlag) |
| 13401 | { |
| 13402 | return DBG_FORCE_CONTINUE; |
| 13403 | } |
| 13404 | else |
| 13405 | { |
| 13406 | return DBG_CONTINUE; |
| 13407 | } |
| 13408 | } |
| 13409 | |
| 13410 | |
| 13411 | // Some Interop bugs involve threads that land at a crazy IP. Since we're interop-debugging, we can't |
| 13412 | // attach a debugger to the LS. So we have some debug mode where we enable the SS flag and thus |
| 13413 | // produce a trace of where a thread is going. |
| 13414 | #ifdef _DEBUG |
| 13415 | void EnableDebugTrace(CordbUnmanagedThread *ut) |
| 13416 | { |
| 13417 | // To enable, attach w/ a debugger and either set fTrace==true, or setip. |
| 13418 | static bool fTrace = false; |
| 13419 | if (!fTrace) |
| 13420 | return; |
| 13421 | |
| 13422 | // Give us a nop so that we can setip in the optimized case. |
| 13423 | #ifdef _TARGET_X86_ |
| 13424 | __asm { |
| 13425 | nop |
| 13426 | } |
| 13427 | #endif |
| 13428 | |
| 13429 | fTrace = true; |
| 13430 | CordbProcess *pProcess = ut->GetProcess(); |
| 13431 | |
| 13432 | // Get the context |
| 13433 | HRESULT hr = S_OK; |
| 13434 | DT_CONTEXT context; |
| 13435 | context.ContextFlags = DT_CONTEXT_FULL; |
| 13436 | |
| 13437 | |
| 13438 | hr = pProcess->GetThreadContext((DWORD) ut->m_id, sizeof(context), (BYTE*)&context); |
| 13439 | if (FAILED(hr)) |
| 13440 | return; |
| 13441 | |
| 13442 | // If the flag is already set, then don't set it again - that will just get confusing. |
| 13443 | if (IsSSFlagEnabled(&context)) |
| 13444 | { |
| 13445 | return; |
| 13446 | } |
| 13447 | _ASSERTE(CORDbgGetIP(&context) != 0); |
| 13448 | SetSSFlag(&context); |
| 13449 | |
| 13450 | // If SS flag not set, enable it. And remeber that it's us so we know how to handle |
| 13451 | // it when we get the debug event. |
| 13452 | hr = pProcess->SetThreadContext((DWORD)ut->m_id, sizeof(context), (BYTE*)&context); |
| 13453 | ut->SetState(CUTS_DEBUG_SingleStep); |
| 13454 | } |
| 13455 | #endif // _DEBUG |
| 13456 | |
| 13457 | //----------------------------------------------------------------------------- |
| 13458 | // DoDbgContinue |
| 13459 | // |
| 13460 | // Continues from a specific Win32 DEBUG_EVENT. |
| 13461 | // |
| 13462 | // Arguments: |
| 13463 | // pProcess - The process to continue. |
| 13464 | // pUnmanagedEvent - The event to continue. |
| 13465 | // |
| 13466 | //----------------------------------------------------------------------------- |
| 13467 | void CordbWin32EventThread::DoDbgContinue(CordbProcess *pProcess, |
| 13468 | CordbUnmanagedEvent *pUnmanagedEvent) |
| 13469 | { |
| 13470 | _ASSERTE(pProcess->ThreadHoldsProcessLock()); |
| 13471 | _ASSERTE(IsWin32EventThread()); |
| 13472 | _ASSERTE(pUnmanagedEvent != NULL); |
| 13473 | _ASSERTE(!pUnmanagedEvent->IsEventContinuedUnhijacked()); |
| 13474 | |
| 13475 | STRESS_LOG3(LF_CORDB, LL_INFO1000, |
| 13476 | "W32ET::DDC: continue with ue=0x%p, thread=0x%p, tid=0x%x\n" , |
| 13477 | pUnmanagedEvent, |
| 13478 | pUnmanagedEvent->m_owner, |
| 13479 | pUnmanagedEvent->m_owner->m_id); |
| 13480 | |
| 13481 | #ifdef _DEBUG |
| 13482 | EnableDebugTrace(pUnmanagedEvent->m_owner); |
| 13483 | #endif |
| 13484 | |
| 13485 | |
| 13486 | if (pUnmanagedEvent->IsEventContinuedHijacked()) |
| 13487 | { |
| 13488 | LOG((LF_CORDB, LL_INFO100000, "W32ET::DDC: Skiping DoDbgContinue because event was already" |
| 13489 | " continued hijacked, ue=0x%p\n" , pUnmanagedEvent)); |
| 13490 | return; |
| 13491 | } |
| 13492 | |
| 13493 | BOOL threadIsHijacked = (pUnmanagedEvent->m_owner->IsFirstChanceHijacked() || |
| 13494 | pUnmanagedEvent->m_owner->IsGenericHijacked()); |
| 13495 | |
| 13496 | BOOL eventIsIB = (pUnmanagedEvent->m_owner->HasIBEvent() && |
| 13497 | pUnmanagedEvent->m_owner->IBEvent() == pUnmanagedEvent); |
| 13498 | |
| 13499 | _ASSERTE((DWORD) pProcess->m_id == pUnmanagedEvent->m_currentDebugEvent.dwProcessId); |
| 13500 | _ASSERTE(pProcess->m_state & CordbProcess::PS_WIN32_STOPPED); |
| 13501 | |
| 13502 | DWORD dwContType; |
| 13503 | if(eventIsIB) |
| 13504 | { |
| 13505 | // 3 cases here... |
| 13506 | // event was already hijacked |
| 13507 | if(threadIsHijacked) |
| 13508 | { |
| 13509 | LOG((LF_CORDB, LL_INFO100000, "W32ET::DDC: Continuing IB, already hijacked, ue=0x%p\n" , pUnmanagedEvent)); |
| 13510 | pUnmanagedEvent->SetState(CUES_EventContinuedHijacked); |
| 13511 | dwContType = !pUnmanagedEvent->m_owner->IsBlockingForSync() ? GetDbgContinueFlag() : DBG_EXCEPTION_NOT_HANDLED; |
| 13512 | } |
| 13513 | // event was not hijacked but has been dispatched |
| 13514 | else if(!threadIsHijacked && pUnmanagedEvent->IsDispatched()) |
| 13515 | { |
| 13516 | LOG((LF_CORDB, LL_INFO100000, "W32ET::DDC: Continuing IB, not hijacked, ue=0x%p\n" , pUnmanagedEvent)); |
| 13517 | _ASSERTE(pUnmanagedEvent->IsDispatched()); |
| 13518 | _ASSERTE(pUnmanagedEvent->IsEventUserContinued()); |
| 13519 | _ASSERTE(!pUnmanagedEvent->IsEventContinuedUnhijacked()); |
| 13520 | pUnmanagedEvent->SetState(CUES_EventContinuedUnhijacked); |
| 13521 | dwContType = pUnmanagedEvent->IsExceptionCleared() ? GetDbgContinueFlag() : DBG_EXCEPTION_NOT_HANDLED; |
| 13522 | |
| 13523 | // The event was never hijacked and so will never need to retrigger, get rid |
| 13524 | // of it right now. If it had been hijacked then we would dequeue it either after the |
| 13525 | // hijack complete flare or one instruction after that when it has had a chance to retrigger |
| 13526 | pProcess->DequeueUnmanagedEvent(pUnmanagedEvent->m_owner); |
| 13527 | } |
| 13528 | // event was not hijacked nor dispatched |
| 13529 | else // if(!threadIsHijacked && !pUnmanagedEvent->IsDispatched()) |
| 13530 | { |
| 13531 | LOG((LF_CORDB, LL_INFO100000, "W32ET::DDC: Continuing IB, now hijacked, ue=0x%p\n" , pUnmanagedEvent)); |
| 13532 | HRESULT hr = pProcess->HijackIBEvent(pUnmanagedEvent); |
| 13533 | _ASSERTE(SUCCEEDED(hr)); |
| 13534 | pUnmanagedEvent->SetState(CUES_EventContinuedHijacked); |
| 13535 | dwContType = !pUnmanagedEvent->m_owner->IsBlockingForSync() ? GetDbgContinueFlag() : DBG_EXCEPTION_NOT_HANDLED; |
| 13536 | } |
| 13537 | } |
| 13538 | else |
| 13539 | { |
| 13540 | LOG((LF_CORDB, LL_INFO100000, "W32ET::DDC: Continuing OB, ue=0x%p\n" , pUnmanagedEvent)); |
| 13541 | // we might actually be hijacked here, but if we are it should be for a previous IB event |
| 13542 | // we just mark all OB events as continued unhijacked |
| 13543 | pUnmanagedEvent->SetState(CUES_EventContinuedUnhijacked); |
| 13544 | dwContType = pUnmanagedEvent->IsExceptionCleared() ? GetDbgContinueFlag() : DBG_EXCEPTION_NOT_HANDLED; |
| 13545 | } |
| 13546 | |
| 13547 | // If the exception is marked as unclearable, then make sure the continue type is correct and force the process |
| 13548 | // to terminate. |
| 13549 | if (pUnmanagedEvent->IsExceptionUnclearable()) |
| 13550 | { |
| 13551 | TerminateProcess(pProcess->UnsafeGetProcessHandle(), pUnmanagedEvent->m_currentDebugEvent.u.Exception.ExceptionRecord.ExceptionCode); |
| 13552 | dwContType = DBG_EXCEPTION_NOT_HANDLED; |
| 13553 | } |
| 13554 | |
| 13555 | // If we're continuing from the loader-bp, then send the managed attach here. |
| 13556 | // (Note this will only be set if the runtime was loaded when we first tried to attach). |
| 13557 | // We assume that the loader-bp is the 1st BP exception. This is naive, |
| 13558 | // since it's not 100% accurate (someone could CreateThread w/ a threadproc of DebugBreak). |
| 13559 | // But it's the best we can do. |
| 13560 | // Note that it's critical we do this BEFORE continuing the process. If this is mixed-mode, we've already |
| 13561 | // told VS about this breakpoint, and so it's set the attach-complete event. As soon as we continue this debug |
| 13562 | // event the process can start moving again, so the CLR needs to know to wait for a managed attach. |
| 13563 | DWORD dwEventCode = pUnmanagedEvent->m_currentDebugEvent.dwDebugEventCode; |
| 13564 | if (dwEventCode == EXCEPTION_DEBUG_EVENT) |
| 13565 | { |
| 13566 | EXCEPTION_DEBUG_INFO * pDebugInfo = &pUnmanagedEvent->m_currentDebugEvent.u.Exception; |
| 13567 | if (pDebugInfo->dwFirstChance && pDebugInfo->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT) |
| 13568 | { |
| 13569 | HRESULT hrIgnore = S_OK; |
| 13570 | EX_TRY |
| 13571 | { |
| 13572 | LOG((LF_CORDB, LL_INFO1000, "W32ET::DDC: Continuing from LdrBp, doing managed attach.\n" )); |
| 13573 | pProcess->QueueManagedAttachIfNeededWorker(); |
| 13574 | } |
| 13575 | EX_CATCH_HRESULT(hrIgnore); |
| 13576 | SIMPLIFYING_ASSUMPTION(SUCCEEDED(hrIgnore)); |
| 13577 | } |
| 13578 | } |
| 13579 | |
| 13580 | STRESS_LOG4(LF_CORDB, LL_INFO1000, |
| 13581 | "W32ET::DDC: calling ContinueDebugEvent(0x%x, 0x%x, 0x%x), process state=0x%x\n" , |
| 13582 | pProcess->m_id, pUnmanagedEvent->m_owner->m_id, dwContType, pProcess->m_state); |
| 13583 | |
| 13584 | // Actually continue the debug event |
| 13585 | pProcess->m_state &= ~CordbProcess::PS_WIN32_STOPPED; |
| 13586 | BOOL fSuccess = m_pNativePipeline->ContinueDebugEvent((DWORD)pProcess->m_id, (DWORD)pUnmanagedEvent->m_owner->m_id, dwContType); |
| 13587 | |
| 13588 | // ContinueDebugEvent may 'fail' if we force kill the debuggee while stopped at the exit-process event. |
| 13589 | if (!fSuccess && (dwEventCode != EXIT_PROCESS_DEBUG_EVENT)) |
| 13590 | { |
| 13591 | _ASSERTE(!"ContinueDebugEvent failed!" ); |
| 13592 | CORDBSetUnrecoverableError(pProcess, HRESULT_FROM_GetLastError(), 0); |
| 13593 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "W32ET::DDC: Last error after ContinueDebugEvent is %d\n" , GetLastError()); |
| 13594 | } |
| 13595 | |
| 13596 | // If this thread is marked for deletion (exit thread or exit process event on it), then we need to delete the |
| 13597 | // unmanaged thread object. |
| 13598 | if ((dwEventCode == EXIT_PROCESS_DEBUG_EVENT) || (dwEventCode == EXIT_THREAD_DEBUG_EVENT)) |
| 13599 | { |
| 13600 | CordbUnmanagedThread * pUnmanagedThread = pUnmanagedEvent->m_owner; |
| 13601 | _ASSERTE(pUnmanagedThread->IsDeleted()); |
| 13602 | |
| 13603 | |
| 13604 | // Thread may have a hijacked inband event on it. Thus it's actually running free from the OS perspective, |
| 13605 | // and fair game to be terminated. In that case, we need to auto-dequeue the event. |
| 13606 | // This will just prevent the RS from making the underlying call to ContinueDebugEvent on this thread |
| 13607 | // for the inband event. Since we've already lost the thread, that's actually exactly what we want. |
| 13608 | if (pUnmanagedThread->HasIBEvent()) |
| 13609 | { |
| 13610 | pProcess->DequeueUnmanagedEvent(pUnmanagedThread); |
| 13611 | } |
| 13612 | |
| 13613 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "Removing thread 0x%x (%d) from process list\n" , pUnmanagedThread->m_id); |
| 13614 | pProcess->m_unmanagedThreads.RemoveBase((ULONG_PTR)pUnmanagedThread->m_id); |
| 13615 | } |
| 13616 | |
| 13617 | |
| 13618 | // If we just continued from an exit process event, then its time to do the exit processing. |
| 13619 | if (dwEventCode == EXIT_PROCESS_DEBUG_EVENT) |
| 13620 | { |
| 13621 | pProcess->Unlock(); |
| 13622 | ExitProcess(false); // not detach case |
| 13623 | pProcess->Lock(); |
| 13624 | } |
| 13625 | |
| 13626 | } |
| 13627 | |
| 13628 | //--------------------------------------------------------------------------------------- |
| 13629 | // |
| 13630 | // ForceDbgContinue continues from the last Win32 DEBUG_EVENT on the given thread, no matter what it was. |
| 13631 | // |
| 13632 | // Arguments: |
| 13633 | // pProcess - process object to continue |
| 13634 | // pUnmanagedThread - unmanaged thread object (maybe null if we're doing a raw cotninue) |
| 13635 | // contType - continuation status (DBG_CONTINUE or DBG_EXCEPTION_NOT_HANDLED) |
| 13636 | // fContinueProcess - do we resume hijacks? |
| 13637 | // |
| 13638 | void CordbWin32EventThread::ForceDbgContinue(CordbProcess *pProcess, CordbUnmanagedThread *pUnmanagedThread, DWORD contType, |
| 13639 | bool fContinueProcess) |
| 13640 | { |
| 13641 | _ASSERTE(pProcess->ThreadHoldsProcessLock()); |
| 13642 | _ASSERTE(pUnmanagedThread != NULL); |
| 13643 | STRESS_LOG4(LF_CORDB, LL_INFO1000, |
| 13644 | "W32ET::FDC: force continue with 0x%x (%s), contProcess=%d, tid=0x%x\n" , |
| 13645 | contType, |
| 13646 | (contType == DBG_CONTINUE) ? "DBG_CONTINUE" : "DBG_EXCEPTION_NOT_HANDLED" , |
| 13647 | fContinueProcess, |
| 13648 | pUnmanagedThread->m_id); |
| 13649 | |
| 13650 | if (fContinueProcess) |
| 13651 | { |
| 13652 | pProcess->ResumeHijackedThreads(); |
| 13653 | } |
| 13654 | |
| 13655 | if (contType == DBG_CONTINUE) |
| 13656 | { |
| 13657 | contType = GetDbgContinueFlag(); |
| 13658 | } |
| 13659 | |
| 13660 | _ASSERTE(pProcess->m_state & CordbProcess::PS_WIN32_STOPPED); |
| 13661 | |
| 13662 | // Remove the Win32 stopped flag so long as the OOB event queue is empty. We're forcing a continue here, so by |
| 13663 | // definition this should be the case... |
| 13664 | _ASSERTE(pProcess->m_outOfBandEventQueue == NULL); |
| 13665 | |
| 13666 | pProcess->m_state &= ~CordbProcess::PS_WIN32_STOPPED; |
| 13667 | |
| 13668 | STRESS_LOG4(LF_CORDB, LL_INFO1000, "W32ET::FDC: calling ContinueDebugEvent(0x%x, 0x%x, 0x%x), process state=0x%x\n" , |
| 13669 | pProcess->m_id, pUnmanagedThread->m_id, contType, pProcess->m_state); |
| 13670 | |
| 13671 | |
| 13672 | #ifdef _DEBUG |
| 13673 | EnableDebugTrace(pUnmanagedThread); |
| 13674 | #endif |
| 13675 | BOOL ret = m_pNativePipeline->ContinueDebugEvent((DWORD)pProcess->m_id, (DWORD)pUnmanagedThread->m_id, contType); |
| 13676 | |
| 13677 | if (!ret) |
| 13678 | { |
| 13679 | // This could in theory fail from Process exit, but that really would only be on the DoDbgContinue path. |
| 13680 | _ASSERTE(!"ContinueDebugEvent failed #2!" ); |
| 13681 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "W32ET::DDC: Last error after ContinueDebugEvent is %d\n" , GetLastError()); |
| 13682 | } |
| 13683 | } |
| 13684 | #endif // FEATURE_INTEROP_DEBUGGING |
| 13685 | |
| 13686 | // |
| 13687 | // This is the thread's real thread proc. It simply calls to the |
| 13688 | // thread proc on the given object. |
| 13689 | // |
| 13690 | /*static*/ DWORD WINAPI CordbWin32EventThread::ThreadProc(LPVOID parameter) |
| 13691 | { |
| 13692 | CordbWin32EventThread* t = (CordbWin32EventThread*) parameter; |
| 13693 | INTERNAL_THREAD_ENTRY(t); |
| 13694 | t->ThreadProc(); |
| 13695 | return 0; |
| 13696 | } |
| 13697 | |
| 13698 | |
| 13699 | // |
| 13700 | // Send a CreateProcess event to the Win32 thread to have it create us |
| 13701 | // a new process. |
| 13702 | // |
| 13703 | HRESULT CordbWin32EventThread::SendCreateProcessEvent( |
| 13704 | MachineInfo machineInfo, |
| 13705 | LPCWSTR programName, |
| 13706 | __in_z LPWSTR programArgs, |
| 13707 | LPSECURITY_ATTRIBUTES lpProcessAttributes, |
| 13708 | LPSECURITY_ATTRIBUTES lpThreadAttributes, |
| 13709 | BOOL bInheritHandles, |
| 13710 | DWORD dwCreationFlags, |
| 13711 | PVOID lpEnvironment, |
| 13712 | LPCWSTR lpCurrentDirectory, |
| 13713 | LPSTARTUPINFOW lpStartupInfo, |
| 13714 | LPPROCESS_INFORMATION lpProcessInformation, |
| 13715 | CorDebugCreateProcessFlags corDebugFlags) |
| 13716 | { |
| 13717 | HRESULT hr = S_OK; |
| 13718 | |
| 13719 | LockSendToWin32EventThreadMutex(); |
| 13720 | LOG((LF_CORDB, LL_EVERYTHING, "CordbWin32EventThread::SCPE Called\n" )); |
| 13721 | m_actionData.createData.machineInfo = machineInfo; |
| 13722 | m_actionData.createData.programName = programName; |
| 13723 | m_actionData.createData.programArgs = programArgs; |
| 13724 | m_actionData.createData.lpProcessAttributes = lpProcessAttributes; |
| 13725 | m_actionData.createData.lpThreadAttributes = lpThreadAttributes; |
| 13726 | m_actionData.createData.bInheritHandles = bInheritHandles; |
| 13727 | m_actionData.createData.dwCreationFlags = dwCreationFlags; |
| 13728 | m_actionData.createData.lpEnvironment = lpEnvironment; |
| 13729 | m_actionData.createData.lpCurrentDirectory = lpCurrentDirectory; |
| 13730 | m_actionData.createData.lpStartupInfo = lpStartupInfo; |
| 13731 | m_actionData.createData.lpProcessInformation = lpProcessInformation; |
| 13732 | m_actionData.createData.corDebugFlags = corDebugFlags; |
| 13733 | |
| 13734 | // m_action is set last so that the win32 event thread can inspect |
| 13735 | // it and take action without actually having to take any |
| 13736 | // locks. The lock around this here is simply to prevent multiple |
| 13737 | // threads from making requests at the same time. |
| 13738 | m_action = W32ETA_CREATE_PROCESS; |
| 13739 | |
| 13740 | BOOL succ = SetEvent(m_threadControlEvent); |
| 13741 | |
| 13742 | if (succ) |
| 13743 | { |
| 13744 | DWORD ret = WaitForSingleObject(m_actionTakenEvent, INFINITE); |
| 13745 | |
| 13746 | LOG((LF_CORDB, LL_EVERYTHING, "Process Handle is: %x, m_threadControlEvent is %x\n" , |
| 13747 | (UINT_PTR)m_actionData.createData.lpProcessInformation->hProcess, (UINT_PTR)m_threadControlEvent)); |
| 13748 | |
| 13749 | if (ret == WAIT_OBJECT_0) |
| 13750 | hr = m_actionResult; |
| 13751 | else |
| 13752 | hr = HRESULT_FROM_GetLastError(); |
| 13753 | } |
| 13754 | else |
| 13755 | hr = HRESULT_FROM_GetLastError(); |
| 13756 | |
| 13757 | UnlockSendToWin32EventThreadMutex(); |
| 13758 | |
| 13759 | return hr; |
| 13760 | } |
| 13761 | |
| 13762 | |
| 13763 | //--------------------------------------------------------------------------------------- |
| 13764 | // |
| 13765 | // Create a process |
| 13766 | // |
| 13767 | // Assumptions: |
| 13768 | // This occurs on the win32 event thread. It is invokved via |
| 13769 | // a message sent from code:CordbWin32EventThread::SendCreateProcessEvent |
| 13770 | // |
| 13771 | // Notes: |
| 13772 | // Create a new process. This is called in the context of the Win32 |
| 13773 | // event thread to ensure that if we're Win32 debugging the process |
| 13774 | // that the same thread that waits for debugging events will be the |
| 13775 | // thread that creates the process. |
| 13776 | // |
| 13777 | //--------------------------------------------------------------------------------------- |
| 13778 | void CordbWin32EventThread::CreateProcess() |
| 13779 | { |
| 13780 | m_action = W32ETA_NONE; |
| 13781 | HRESULT hr = S_OK; |
| 13782 | |
| 13783 | DWORD dwCreationFlags = m_actionData.createData.dwCreationFlags; |
| 13784 | |
| 13785 | // If the creation flags has DEBUG_PROCESS in them, then we're |
| 13786 | // Win32 debugging this process. Otherwise, we have to create |
| 13787 | // suspended to give us time to setup up our side of the IPC |
| 13788 | // channel. |
| 13789 | BOOL fInteropDebugging = |
| 13790 | #if defined(FEATURE_INTEROP_DEBUGGING) |
| 13791 | (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)); |
| 13792 | #else |
| 13793 | false; // Interop not supported. |
| 13794 | #endif |
| 13795 | |
| 13796 | // Have Win32 create the process... |
| 13797 | hr = m_pNativePipeline->CreateProcessUnderDebugger( |
| 13798 | m_actionData.createData.machineInfo, |
| 13799 | m_actionData.createData.programName, |
| 13800 | m_actionData.createData.programArgs, |
| 13801 | m_actionData.createData.lpProcessAttributes, |
| 13802 | m_actionData.createData.lpThreadAttributes, |
| 13803 | m_actionData.createData.bInheritHandles, |
| 13804 | dwCreationFlags, |
| 13805 | m_actionData.createData.lpEnvironment, |
| 13806 | m_actionData.createData.lpCurrentDirectory, |
| 13807 | m_actionData.createData.lpStartupInfo, |
| 13808 | m_actionData.createData.lpProcessInformation); |
| 13809 | |
| 13810 | if (SUCCEEDED(hr)) |
| 13811 | { |
| 13812 | // Process ID is filled in after process is succesfully created. |
| 13813 | DWORD dwProcessId = m_actionData.createData.lpProcessInformation->dwProcessId; |
| 13814 | ProcessDescriptor pd = ProcessDescriptor::FromPid(dwProcessId); |
| 13815 | |
| 13816 | RSUnsafeExternalSmartPtr<CordbProcess> pProcess; |
| 13817 | hr = m_pShim->InitializeDataTarget(&pd); |
| 13818 | |
| 13819 | if (SUCCEEDED(hr)) |
| 13820 | { |
| 13821 | // To emulate V2 semantics, we pass 0 for the clrInstanceID into |
| 13822 | // OpenVirtualProcess. This will then connect to the first CLR |
| 13823 | // loaded. |
| 13824 | const ULONG64 cFirstClrLoaded = 0; |
| 13825 | hr = CordbProcess::OpenVirtualProcess(cFirstClrLoaded, m_pShim->GetDataTarget(), NULL, m_cordb, &pd, m_pShim, &pProcess); |
| 13826 | } |
| 13827 | |
| 13828 | // Shouldn't happen on a create, only an attach |
| 13829 | _ASSERTE(hr != CORDBG_E_DEBUGGER_ALREADY_ATTACHED); |
| 13830 | |
| 13831 | // Remember the process in the global list of processes. |
| 13832 | if (SUCCEEDED(hr)) |
| 13833 | { |
| 13834 | EX_TRY |
| 13835 | { |
| 13836 | // Mark if we're interop-debugging |
| 13837 | if (fInteropDebugging) |
| 13838 | { |
| 13839 | pProcess->EnableInteropDebugging(); |
| 13840 | } |
| 13841 | |
| 13842 | m_cordb->AddProcess(pProcess); // will take ref if it succeeds |
| 13843 | } |
| 13844 | EX_CATCH_HRESULT(hr); |
| 13845 | } |
| 13846 | |
| 13847 | // If we're Win32 attached to this process, then increment the |
| 13848 | // proper count, otherwise add this process to the wait set |
| 13849 | // and resume the process's main thread. |
| 13850 | if (SUCCEEDED(hr)) |
| 13851 | { |
| 13852 | _ASSERTE(m_pProcess == NULL); |
| 13853 | m_pProcess.Assign(pProcess); |
| 13854 | } |
| 13855 | } |
| 13856 | |
| 13857 | |
| 13858 | // |
| 13859 | // Signal the hr to the caller. |
| 13860 | // |
| 13861 | m_actionResult = hr; |
| 13862 | SetEvent(m_actionTakenEvent); |
| 13863 | } |
| 13864 | |
| 13865 | |
| 13866 | // |
| 13867 | // Send a DebugActiveProcess event to the Win32 thread to have it attach to |
| 13868 | // a new process. |
| 13869 | // |
| 13870 | HRESULT CordbWin32EventThread::SendDebugActiveProcessEvent( |
| 13871 | MachineInfo machineInfo, |
| 13872 | const ProcessDescriptor *pProcessDescriptor, |
| 13873 | bool fWin32Attach, |
| 13874 | CordbProcess *pProcess) |
| 13875 | { |
| 13876 | HRESULT hr = S_OK; |
| 13877 | |
| 13878 | LockSendToWin32EventThreadMutex(); |
| 13879 | |
| 13880 | m_actionData.attachData.machineInfo = machineInfo; |
| 13881 | m_actionData.attachData.processDescriptor = *pProcessDescriptor; |
| 13882 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
| 13883 | m_actionData.attachData.fWin32Attach = fWin32Attach; |
| 13884 | #endif |
| 13885 | m_actionData.attachData.pProcess = pProcess; |
| 13886 | |
| 13887 | // m_action is set last so that the win32 event thread can inspect |
| 13888 | // it and take action without actually having to take any |
| 13889 | // locks. The lock around this here is simply to prevent multiple |
| 13890 | // threads from making requests at the same time. |
| 13891 | m_action = W32ETA_ATTACH_PROCESS; |
| 13892 | |
| 13893 | BOOL succ = SetEvent(m_threadControlEvent); |
| 13894 | |
| 13895 | if (succ) |
| 13896 | { |
| 13897 | DWORD ret = WaitForSingleObject(m_actionTakenEvent, INFINITE); |
| 13898 | |
| 13899 | if (ret == WAIT_OBJECT_0) |
| 13900 | hr = m_actionResult; |
| 13901 | else |
| 13902 | hr = HRESULT_FROM_GetLastError(); |
| 13903 | } |
| 13904 | else |
| 13905 | hr = HRESULT_FROM_GetLastError(); |
| 13906 | |
| 13907 | UnlockSendToWin32EventThreadMutex(); |
| 13908 | |
| 13909 | return hr; |
| 13910 | } |
| 13911 | |
| 13912 | //----------------------------------------------------------------------------- |
| 13913 | // Is the given thread id a helper thread (real or worker?) |
| 13914 | //----------------------------------------------------------------------------- |
| 13915 | bool CordbProcess::IsHelperThreadWorked(DWORD tid) |
| 13916 | { |
| 13917 | // Check against the id gained by sniffing Thread-Create events. |
| 13918 | if (tid == this->m_helperThreadId) |
| 13919 | { |
| 13920 | return true; |
| 13921 | } |
| 13922 | |
| 13923 | // Now check for potential datate in the IPC block. If not there, |
| 13924 | // then we know it can't be the helper. |
| 13925 | DebuggerIPCControlBlock * pDCB = this->GetDCB(); |
| 13926 | |
| 13927 | if (pDCB == NULL) |
| 13928 | { |
| 13929 | return false; |
| 13930 | } |
| 13931 | |
| 13932 | // get the latest information from the LS DCB |
| 13933 | UpdateRightSideDCB(); |
| 13934 | return |
| 13935 | (tid == pDCB->m_realHelperThreadId) || |
| 13936 | (tid == pDCB->m_temporaryHelperThreadId); |
| 13937 | |
| 13938 | } |
| 13939 | |
| 13940 | //--------------------------------------------------------------------------------------- |
| 13941 | // |
| 13942 | // Cleans up the Left Side's DCB after a failed attach attempt. |
| 13943 | // |
| 13944 | // Assumptions: |
| 13945 | // Called when the left-site failed initialization |
| 13946 | // |
| 13947 | // Notes: |
| 13948 | // This can be called multiple times. |
| 13949 | //--------------------------------------------------------------------------------------- |
| 13950 | void CordbProcess::CleanupHalfBakedLeftSide() |
| 13951 | { |
| 13952 | if (GetDCB() != NULL) |
| 13953 | { |
| 13954 | EX_TRY |
| 13955 | { |
| 13956 | GetDCB()->m_rightSideIsWin32Debugger = false; |
| 13957 | UpdateLeftSideDCBField(&(GetDCB()->m_rightSideIsWin32Debugger), sizeof(GetDCB()->m_rightSideIsWin32Debugger)); |
| 13958 | |
| 13959 | if (m_pEventChannel != NULL) |
| 13960 | { |
| 13961 | m_pEventChannel->Delete(); |
| 13962 | m_pEventChannel = NULL; |
| 13963 | } |
| 13964 | } |
| 13965 | EX_CATCH |
| 13966 | { |
| 13967 | _ASSERTE(!"Writing process memory failed, perhaps due to an unexpected disconnection from the target." ); |
| 13968 | } |
| 13969 | EX_END_CATCH(SwallowAllExceptions); |
| 13970 | } |
| 13971 | |
| 13972 | // Close and null out the various handles and events, including our process handle m_handle. |
| 13973 | CloseIPCHandles(); |
| 13974 | |
| 13975 | m_cordb.Clear(); |
| 13976 | |
| 13977 | // This process object is Dead-On-Arrival, so it doesn't really have anything to neuter. |
| 13978 | // But for safekeeping, we'll mark it as neutered. |
| 13979 | UnsafeNeuterDeadObject(); |
| 13980 | } |
| 13981 | |
| 13982 | |
| 13983 | //--------------------------------------------------------------------------------------- |
| 13984 | // |
| 13985 | // Attach to an existing process. |
| 13986 | // |
| 13987 | // |
| 13988 | // Assumptions: |
| 13989 | // Called on W32Event Thread, in response to event sent by |
| 13990 | // code:CordbWin32EventThread::SendDebugActiveProcessEvent |
| 13991 | // |
| 13992 | // Notes: |
| 13993 | // Attach to a process. This is called in the context of the Win32 |
| 13994 | // event thread to ensure that if we're Win32 debugging the process |
| 13995 | // that the same thread that waits for debugging events will be the |
| 13996 | // thread that attaches the process. |
| 13997 | // |
| 13998 | // @dbgtodo shim: this will be part of the shim |
| 13999 | //--------------------------------------------------------------------------------------- |
| 14000 | void CordbWin32EventThread::AttachProcess() |
| 14001 | { |
| 14002 | _ASSERTE(IsWin32EventThread()); |
| 14003 | |
| 14004 | RSUnsafeExternalSmartPtr<CordbProcess> pProcess; |
| 14005 | |
| 14006 | m_action = W32ETA_NONE; |
| 14007 | |
| 14008 | HRESULT hr = S_OK; |
| 14009 | |
| 14010 | ProcessDescriptor processDescriptor = m_actionData.attachData.processDescriptor; |
| 14011 | bool fNativeAttachSucceeded = false; |
| 14012 | |
| 14013 | // Always do OS attach to the target. |
| 14014 | // By this point, the pid should be valid (because OpenProcess above), pending some race where the process just exited. |
| 14015 | // The OS will enforce that only 1 debugger is attached. |
| 14016 | // Common failure paths here would be: access denied, double-attach |
| 14017 | { |
| 14018 | hr = m_pNativePipeline->DebugActiveProcess(m_actionData.attachData.machineInfo, |
| 14019 | processDescriptor); |
| 14020 | if (FAILED(hr)) |
| 14021 | { |
| 14022 | goto LExit; |
| 14023 | } |
| 14024 | fNativeAttachSucceeded = true; |
| 14025 | } |
| 14026 | |
| 14027 | |
| 14028 | hr = m_pShim->InitializeDataTarget(&processDescriptor); |
| 14029 | if (FAILED(hr)) |
| 14030 | { |
| 14031 | goto LExit; |
| 14032 | } |
| 14033 | |
| 14034 | // To emulate V2 semantics, we pass 0 for the clrInstanceID into |
| 14035 | // OpenVirtualProcess. This will then connect to the first CLR |
| 14036 | // loaded. |
| 14037 | { |
| 14038 | const ULONG64 cFirstClrLoaded = 0; |
| 14039 | hr = CordbProcess::OpenVirtualProcess(cFirstClrLoaded, m_pShim->GetDataTarget(), NULL, m_cordb, &processDescriptor, m_pShim, &pProcess); |
| 14040 | if (FAILED(hr)) |
| 14041 | { |
| 14042 | goto LExit; |
| 14043 | } |
| 14044 | } |
| 14045 | |
| 14046 | // Remember the process in the global list of processes. |
| 14047 | // The caller back in code:Cordb::DebugActiveProcess will then get this by fetching it from the list. |
| 14048 | |
| 14049 | EX_TRY |
| 14050 | { |
| 14051 | // Mark interop-debugging |
| 14052 | if (m_actionData.attachData.IsInteropDebugging()) |
| 14053 | { |
| 14054 | pProcess->EnableInteropDebugging(); // Throwing |
| 14055 | } |
| 14056 | |
| 14057 | m_cordb->AddProcess(pProcess); // will take ref if it succeeds |
| 14058 | |
| 14059 | |
| 14060 | // Queue fake Attach event for CreateProcess |
| 14061 | { |
| 14062 | PUBLIC_CALLBACK_IN_THIS_SCOPE0_NO_LOCK(pProcess); |
| 14063 | m_pShim->BeginQueueFakeAttachEvents(); |
| 14064 | } |
| 14065 | } |
| 14066 | EX_CATCH_HRESULT(hr); |
| 14067 | if (FAILED(hr)) |
| 14068 | { |
| 14069 | goto LExit; |
| 14070 | } |
| 14071 | |
| 14072 | _ASSERTE(m_pProcess == NULL); |
| 14073 | m_pProcess.Assign(pProcess); |
| 14074 | pProcess.Clear(); // ownership transfered to m_pProcess |
| 14075 | |
| 14076 | // Should have succeeded if we got to this point. |
| 14077 | _ASSERTE(SUCCEEDED(hr)); |
| 14078 | |
| 14079 | |
| 14080 | LExit: |
| 14081 | if (FAILED(hr)) |
| 14082 | { |
| 14083 | // If we succeed to do a native-attach, but then failed elsewhere, try to native-detach. |
| 14084 | if (fNativeAttachSucceeded) |
| 14085 | { |
| 14086 | m_pNativePipeline->DebugActiveProcessStop(processDescriptor.m_Pid); |
| 14087 | } |
| 14088 | |
| 14089 | if (pProcess != NULL) |
| 14090 | { |
| 14091 | // Safe to call this even if the process wasn't added. |
| 14092 | m_cordb->RemoveProcess(pProcess); |
| 14093 | pProcess->CleanupHalfBakedLeftSide(); |
| 14094 | pProcess.Clear(); |
| 14095 | } |
| 14096 | m_pProcess.Clear(); |
| 14097 | } |
| 14098 | |
| 14099 | // |
| 14100 | // Signal the hr to the caller. |
| 14101 | // |
| 14102 | m_actionResult = hr; |
| 14103 | SetEvent(m_actionTakenEvent); |
| 14104 | } |
| 14105 | |
| 14106 | |
| 14107 | // Note that the actual 'DetachProcess' method is really ExitProcess with CW32ET_UNKNOWN_PROCESS_SLOT == |
| 14108 | // processSlot |
| 14109 | HRESULT CordbWin32EventThread::SendDetachProcessEvent(CordbProcess *pProcess) |
| 14110 | { |
| 14111 | LOG((LF_CORDB, LL_INFO1000, "W32ET::SDPE\n" )); |
| 14112 | HRESULT hr = S_OK; |
| 14113 | |
| 14114 | LockSendToWin32EventThreadMutex(); |
| 14115 | |
| 14116 | m_actionData.detachData.pProcess = pProcess; |
| 14117 | |
| 14118 | // m_action is set last so that the win32 event thread can inspect it and take action without actually |
| 14119 | // having to take any locks. The lock around this here is simply to prevent multiple threads from making |
| 14120 | // requests at the same time. |
| 14121 | m_action = W32ETA_DETACH; |
| 14122 | |
| 14123 | BOOL succ = SetEvent(m_threadControlEvent); |
| 14124 | |
| 14125 | if (succ) |
| 14126 | { |
| 14127 | DWORD ret = WaitForSingleObject(m_actionTakenEvent, INFINITE); |
| 14128 | |
| 14129 | if (ret == WAIT_OBJECT_0) |
| 14130 | hr = m_actionResult; |
| 14131 | else |
| 14132 | hr = HRESULT_FROM_GetLastError(); |
| 14133 | } |
| 14134 | else |
| 14135 | hr = HRESULT_FROM_GetLastError(); |
| 14136 | |
| 14137 | UnlockSendToWin32EventThreadMutex(); |
| 14138 | |
| 14139 | return hr; |
| 14140 | } |
| 14141 | |
| 14142 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 14143 | // |
| 14144 | // Send a UnmanagedContinue event to the Win32 thread to have it |
| 14145 | // continue from an unmanged debug event. |
| 14146 | // |
| 14147 | HRESULT CordbWin32EventThread::SendUnmanagedContinue(CordbProcess *pProcess, |
| 14148 | EUMContinueType eContType) |
| 14149 | { |
| 14150 | HRESULT hr = S_OK; |
| 14151 | |
| 14152 | // If this were being called on the win32 EventThread, we'd deadlock. |
| 14153 | _ASSERTE(!IsWin32EventThread()); |
| 14154 | |
| 14155 | // This can't hold the process lock, b/c we're making a cross-thread call, |
| 14156 | // and our target will need the process lock. |
| 14157 | _ASSERTE(!pProcess->ThreadHoldsProcessLock()); |
| 14158 | |
| 14159 | LockSendToWin32EventThreadMutex(); |
| 14160 | |
| 14161 | m_actionData.continueData.process = pProcess; |
| 14162 | m_actionData.continueData.eContType = eContType; |
| 14163 | |
| 14164 | // m_action is set last so that the win32 event thread can inspect |
| 14165 | // it and take action without actually having to take any |
| 14166 | // locks. The lock around this here is simply to prevent multiple |
| 14167 | // threads from making requests at the same time. |
| 14168 | m_action = W32ETA_CONTINUE; |
| 14169 | |
| 14170 | BOOL succ = SetEvent(m_threadControlEvent); |
| 14171 | |
| 14172 | if (succ) |
| 14173 | { |
| 14174 | DWORD ret = WaitForSingleObject(m_actionTakenEvent, INFINITE); |
| 14175 | |
| 14176 | if (ret == WAIT_OBJECT_0) |
| 14177 | hr = m_actionResult; |
| 14178 | else |
| 14179 | hr = HRESULT_FROM_GetLastError(); |
| 14180 | } |
| 14181 | else |
| 14182 | hr = HRESULT_FROM_GetLastError(); |
| 14183 | |
| 14184 | UnlockSendToWin32EventThreadMutex(); |
| 14185 | |
| 14186 | return hr; |
| 14187 | } |
| 14188 | |
| 14189 | |
| 14190 | // |
| 14191 | // Handle unmanaged continue. Continue an unmanaged debug |
| 14192 | // event. Deferes to UnmanagedContinue. This is called in the context |
| 14193 | // of the Win32 event thread to ensure that if we're Win32 debugging |
| 14194 | // the process that the same thread that waits for debugging events |
| 14195 | // will be the thread that continues the process. |
| 14196 | // |
| 14197 | void CordbWin32EventThread::HandleUnmanagedContinue() |
| 14198 | { |
| 14199 | _ASSERTE(IsWin32EventThread()); |
| 14200 | |
| 14201 | m_action = W32ETA_NONE; |
| 14202 | HRESULT hr = S_OK; |
| 14203 | |
| 14204 | // Continue the process |
| 14205 | CordbProcess *pProcess = m_actionData.continueData.process; |
| 14206 | |
| 14207 | // If we lost the process object, we must have exited. |
| 14208 | if (m_pProcess != NULL) |
| 14209 | { |
| 14210 | _ASSERTE(m_pProcess != NULL); |
| 14211 | _ASSERTE(pProcess == m_pProcess); |
| 14212 | |
| 14213 | _ASSERTE(!pProcess->ThreadHoldsProcessLock()); |
| 14214 | |
| 14215 | RSSmartPtr<CordbProcess> proc(pProcess); |
| 14216 | RSLockHolder ch(&pProcess->m_processMutex); |
| 14217 | |
| 14218 | hr = UnmanagedContinue(pProcess, m_actionData.continueData.eContType); |
| 14219 | } |
| 14220 | |
| 14221 | // Signal the hr to the caller. |
| 14222 | m_actionResult = hr; |
| 14223 | SetEvent(m_actionTakenEvent); |
| 14224 | } |
| 14225 | |
| 14226 | // |
| 14227 | // Continue an unmanaged debug event. This is called in the context of the Win32 Event thread to ensure that the same |
| 14228 | // thread that waits for debug events will be the thread that continues the process. |
| 14229 | // |
| 14230 | HRESULT CordbWin32EventThread::UnmanagedContinue(CordbProcess *pProcess, |
| 14231 | EUMContinueType eContType) |
| 14232 | { |
| 14233 | _ASSERTE(pProcess->ThreadHoldsProcessLock()); |
| 14234 | _ASSERTE(IsWin32EventThread()); |
| 14235 | _ASSERTE(m_pShim != NULL); |
| 14236 | |
| 14237 | HRESULT hr = S_OK; |
| 14238 | |
| 14239 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "UM Continue. type=%d\n" , eContType); |
| 14240 | |
| 14241 | if (eContType == cOobUMContinue) |
| 14242 | { |
| 14243 | _ASSERTE(pProcess->m_outOfBandEventQueue != NULL); |
| 14244 | |
| 14245 | // Dequeue the OOB event. |
| 14246 | CordbUnmanagedEvent *ue = pProcess->m_outOfBandEventQueue; |
| 14247 | CordbUnmanagedThread *ut = ue->m_owner; |
| 14248 | pProcess->DequeueOOBUnmanagedEvent(ut); |
| 14249 | |
| 14250 | // Do a little extra work if that was an OOB exception event... |
| 14251 | hr = ue->m_owner->FixupAfterOOBException(ue); |
| 14252 | _ASSERTE(SUCCEEDED(hr)); |
| 14253 | |
| 14254 | // Continue from the event. |
| 14255 | DoDbgContinue(pProcess, ue); |
| 14256 | |
| 14257 | // If there are more queued OOB events, dispatch them now. |
| 14258 | if (pProcess->m_outOfBandEventQueue != NULL) |
| 14259 | pProcess->DispatchUnmanagedOOBEvent(); |
| 14260 | |
| 14261 | // Note: if we previously skipped letting the entire process go on an IB continue due to a blocking OOB event, |
| 14262 | // and if the OOB event queue is now empty, then go ahead and let the process continue now... |
| 14263 | if ((pProcess->m_doRealContinueAfterOOBBlock == true) && |
| 14264 | (pProcess->m_outOfBandEventQueue == NULL)) |
| 14265 | goto doRealContinue; |
| 14266 | } |
| 14267 | else if (eContType == cInternalUMContinue) |
| 14268 | { |
| 14269 | // We're trying to get into a synced state which means we need the process running (potentially |
| 14270 | // with some threads hijacked) in order to have the helper thread do the sync. |
| 14271 | LOG((LF_CORDB, LL_INFO1000, "W32ET::UC: internal continue.\n" )); |
| 14272 | |
| 14273 | if (!pProcess->GetSynchronized()) |
| 14274 | { |
| 14275 | LOG((LF_CORDB, LL_INFO1000, "W32ET::UC: internal continue, !sync'd.\n" )); |
| 14276 | pProcess->ResumeUnmanagedThreads(); |
| 14277 | |
| 14278 | // the event we may need to hijack and continue; |
| 14279 | CordbUnmanagedEvent* pEvent = pProcess->m_lastQueuedUnmanagedEvent; |
| 14280 | |
| 14281 | // It is possible to be stopped at either an IB or an OOB event here. We only want to |
| 14282 | // continue from an IB event here though |
| 14283 | if(pProcess->m_state & CordbProcess::PS_WIN32_STOPPED && pEvent != NULL && |
| 14284 | pEvent->IsEventWaitingForContinue()) |
| 14285 | { |
| 14286 | LOG((LF_CORDB, LL_INFO1000, "W32ET::UC: internal continue, frozen on IB event.\n" )); |
| 14287 | |
| 14288 | // There should be a uncontinued IB event at the head of the queue |
| 14289 | _ASSERTE(pEvent->IsIBEvent()); |
| 14290 | _ASSERTE(!pEvent->IsEventContinuedUnhijacked()); |
| 14291 | _ASSERTE(!pEvent->IsEventContinuedHijacked()); |
| 14292 | |
| 14293 | // Ensure that the event is hijacked now (it may not have been before) so that the |
| 14294 | // thread does not slip forward during the sync process. After that we can safely continue |
| 14295 | // it. |
| 14296 | pProcess->HijackIBEvent(pEvent); |
| 14297 | m_pShim->GetWin32EventThread()->DoDbgContinue(pProcess, pEvent); |
| 14298 | } |
| 14299 | } |
| 14300 | |
| 14301 | LOG((LF_CORDB, LL_INFO1000, "W32ET::UC: internal continue, done.\n" )); |
| 14302 | } |
| 14303 | else |
| 14304 | { |
| 14305 | // If we're here, then we know 100% for sure that we've successfully gotten the managed continue event to the |
| 14306 | // Left Side, so we can stop force hijacking left over in-band events now. Note: if we had hijacked any such |
| 14307 | // events, they'll be dispatched below since they're properly queued. |
| 14308 | pProcess->m_specialDeferment = false; |
| 14309 | |
| 14310 | // We don't actually do any work if there is an outstanding out-of-band event. When we do continue from the |
| 14311 | // out-of-band event, we'll do this work, too. |
| 14312 | if (pProcess->m_outOfBandEventQueue != NULL) |
| 14313 | { |
| 14314 | LOG((LF_CORDB, LL_INFO1000, "W32ET::UC: ignoring real continue due to block by out-of-band event(s).\n" )); |
| 14315 | |
| 14316 | _ASSERTE(pProcess->m_doRealContinueAfterOOBBlock == false); |
| 14317 | pProcess->m_doRealContinueAfterOOBBlock = true; |
| 14318 | } |
| 14319 | else |
| 14320 | { |
| 14321 | doRealContinue: |
| 14322 | // This is either the Frozen -> Running transition or a |
| 14323 | // Synced -> Running transition |
| 14324 | _ASSERTE(pProcess->m_outOfBandEventQueue == NULL); |
| 14325 | |
| 14326 | |
| 14327 | pProcess->m_doRealContinueAfterOOBBlock = false; |
| 14328 | |
| 14329 | LOG((LF_CORDB, LL_INFO1000, "W32ET::UC: continuing the process.\n" )); |
| 14330 | // Dispatch any more queued in-band events, or if there are none then just continue the process. |
| 14331 | // |
| 14332 | // Note: don't dispatch more events if we've already sent up the ExitProcess event... those events are just |
| 14333 | // lost. |
| 14334 | if ((pProcess->HasUndispatchedNativeEvents()) && (pProcess->m_exiting == false)) |
| 14335 | { |
| 14336 | pProcess->DispatchUnmanagedInBandEvent(); |
| 14337 | } |
| 14338 | else |
| 14339 | { |
| 14340 | // If the unmanaged event queue is empty now, and the process is synchronized, and there are queued |
| 14341 | // managed events, then go ahead and get more managed events dispatched. |
| 14342 | // |
| 14343 | // Note: don't dispatch more events if we've already sent up the ExitProcess event... those events are |
| 14344 | // just lost. |
| 14345 | if (pProcess->GetSynchronized() && (!m_pShim->GetManagedEventQueue()->IsEmpty()) && (pProcess->m_exiting == false)) |
| 14346 | { |
| 14347 | if(pProcess->m_state & CordbProcess::PS_WIN32_STOPPED) |
| 14348 | { |
| 14349 | DoDbgContinue(pProcess, pProcess->m_lastDispatchedIBEvent); |
| 14350 | |
| 14351 | // This if should not be necessary, I am just being extra careful because this |
| 14352 | // fix is going in late - see issue 818301 |
| 14353 | _ASSERTE(pProcess->m_lastDispatchedIBEvent != NULL); |
| 14354 | if(pProcess->m_lastDispatchedIBEvent != NULL) |
| 14355 | { |
| 14356 | pProcess->m_lastDispatchedIBEvent->m_owner->InternalRelease(); |
| 14357 | pProcess->m_lastDispatchedIBEvent = NULL; |
| 14358 | } |
| 14359 | } |
| 14360 | |
| 14361 | // Now, get more managed events dispatched. |
| 14362 | pProcess->SetSynchronized(false); |
| 14363 | pProcess->MarkAllThreadsDirty(); |
| 14364 | m_cordb->ProcessStateChanged(); |
| 14365 | } |
| 14366 | else |
| 14367 | { |
| 14368 | // free all the hijacked threads that hit native debug events |
| 14369 | pProcess->ResumeHijackedThreads(); |
| 14370 | |
| 14371 | // after continuing the here the process should be running completely |
| 14372 | // free... no hijacks, no suspended threads, and of course not frozen |
| 14373 | if(pProcess->m_state & CordbProcess::PS_WIN32_STOPPED) |
| 14374 | { |
| 14375 | DoDbgContinue(pProcess, pProcess->m_lastDispatchedIBEvent); |
| 14376 | // This if should not be necessary, I am just being extra careful because this |
| 14377 | // fix is going in late - see issue 818301 |
| 14378 | _ASSERTE(pProcess->m_lastDispatchedIBEvent != NULL); |
| 14379 | if(pProcess->m_lastDispatchedIBEvent != NULL) |
| 14380 | { |
| 14381 | pProcess->m_lastDispatchedIBEvent->m_owner->InternalRelease(); |
| 14382 | pProcess->m_lastDispatchedIBEvent = NULL; |
| 14383 | } |
| 14384 | } |
| 14385 | } |
| 14386 | } |
| 14387 | |
| 14388 | // Implicit Release on UT |
| 14389 | } |
| 14390 | } |
| 14391 | |
| 14392 | return hr; |
| 14393 | } |
| 14394 | #endif // FEATURE_INTEROP_DEBUGGING |
| 14395 | |
| 14396 | void ExitProcessWorkItem::Do() |
| 14397 | { |
| 14398 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "ExitProcessWorkItem proc=%p\n" , GetProcess()); |
| 14399 | |
| 14400 | // This is being called on the RCET. |
| 14401 | // That's the thread that dispatches managed events. Since it's calling us now, we know |
| 14402 | // it can't be dispatching a managed event, and so we don't need to both waiting for it |
| 14403 | |
| 14404 | { |
| 14405 | // Get the SG lock here to coordinate against any other continues. |
| 14406 | RSLockHolder ch(GetProcess()->GetStopGoLock()); |
| 14407 | RSLockHolder ch2(&(GetProcess()->m_processMutex)); |
| 14408 | |
| 14409 | LOG((LF_CORDB, LL_INFO1000,"W32ET::EP: ExitProcess callback\n" )); |
| 14410 | |
| 14411 | // We're synchronized now, so mark the process as such. |
| 14412 | GetProcess()->SetSynchronized(true); |
| 14413 | GetProcess()->IncStopCount(); |
| 14414 | |
| 14415 | // By the time we release the SG + Process locks here, the process object has been |
| 14416 | // marked as exiting + terminated (by the w32et which queued us). Future attemps to |
| 14417 | // continue should fail, and thus we should remain synchronized. |
| 14418 | } |
| 14419 | |
| 14420 | |
| 14421 | // Just to be safe, neuter any children before the exit process callback. |
| 14422 | { |
| 14423 | RSLockHolder ch(GetProcess()->GetProcessLock()); |
| 14424 | |
| 14425 | // Release the process. |
| 14426 | GetProcess()->NeuterChildren(); |
| 14427 | } |
| 14428 | |
| 14429 | RSSmartPtr<Cordb> pCordb(NULL); |
| 14430 | |
| 14431 | // There is a race condition here where the debuggee process is killed while we are processing a process |
| 14432 | // detach. We queue the process exit event for the Win32 event thread before queueing the process detach |
| 14433 | // event. By the time this function is executed, we may have neutered the CordbProcess already as a |
| 14434 | // result of code:CordbProcess::Detach. Detect that case here under the SG lock. |
| 14435 | { |
| 14436 | RSLockHolder ch(GetProcess()->GetStopGoLock()); |
| 14437 | if (!GetProcess()->IsNeutered()) |
| 14438 | { |
| 14439 | _ASSERTE(GetProcess()->m_cordb != NULL); |
| 14440 | pCordb.Assign(GetProcess()->m_cordb); |
| 14441 | } |
| 14442 | } |
| 14443 | |
| 14444 | // Move this into Shim? |
| 14445 | |
| 14446 | // Invoke the ExitProcess callback. This is very important since the a shell |
| 14447 | // may rely on it for proper shutdown and may hang if they don't get it. |
| 14448 | // We don't expect Cordbg to continue from this (we're certainly not going to wait for it). |
| 14449 | if ((pCordb != NULL) && (pCordb->m_managedCallback != NULL)) |
| 14450 | { |
| 14451 | PUBLIC_CALLBACK_IN_THIS_SCOPE0_NO_LOCK(GetProcess()); |
| 14452 | pCordb->m_managedCallback->ExitProcess(GetProcess()); |
| 14453 | } |
| 14454 | |
| 14455 | // This CordbProcess object now has no reservations against a client calling ICorDebug::Terminate. |
| 14456 | // That call may race against the CordbProcess::Neuter below, but since we already neutered the children, |
| 14457 | // that neuter call will not do anything interesting that will conflict with Terminate. |
| 14458 | |
| 14459 | LOG((LF_CORDB, LL_INFO1000,"W32ET::EP: returned from ExitProcess callback\n" )); |
| 14460 | |
| 14461 | { |
| 14462 | RSLockHolder ch(GetProcess()->GetStopGoLock()); |
| 14463 | |
| 14464 | // Release the process. |
| 14465 | GetProcess()->Neuter(); |
| 14466 | } |
| 14467 | |
| 14468 | // Our dtor will release the Process object. |
| 14469 | // This may be the final release on the process. |
| 14470 | } |
| 14471 | |
| 14472 | |
| 14473 | //--------------------------------------------------------------------------------------- |
| 14474 | // |
| 14475 | // Handles process exiting and detach cases |
| 14476 | // |
| 14477 | // Arguments: |
| 14478 | // fDetach - true if detaching, false if process is exiting. |
| 14479 | // |
| 14480 | // Return Value: |
| 14481 | // The type of the next argument in the signature, |
| 14482 | // normalized. |
| 14483 | // |
| 14484 | // Assumptions: |
| 14485 | // On exit, the process has already exited and we detected this by either an EXIT_PROCESS |
| 14486 | // native debug event, or by waiting on the process handle. |
| 14487 | // On detach, the process is stil live. |
| 14488 | // |
| 14489 | // Notes: |
| 14490 | // ExitProcess is called when a process exits or detaches. |
| 14491 | // This does our final cleanup and removes the process from our wait sets. |
| 14492 | // We're either here because we're detaching (fDetach == TRUE), or because the process has really exited, |
| 14493 | // and we're doing shutdown logic. |
| 14494 | // |
| 14495 | //--------------------------------------------------------------------------------------- |
| 14496 | void CordbWin32EventThread::ExitProcess(bool fDetach) |
| 14497 | { |
| 14498 | INTERNAL_API_ENTRY(this); |
| 14499 | |
| 14500 | // Consider the following when you're modifying this function: |
| 14501 | // - The OS can kill the debuggee at any time. |
| 14502 | // - ExitProcess can race with detach. |
| 14503 | |
| 14504 | LOG((LF_CORDB, LL_INFO1000,"W32ET::EP: begin ExitProcess, detach=%d\n" , fDetach)); |
| 14505 | |
| 14506 | |
| 14507 | // For the Mac remote debugging transport, DebugActiveProcessStop() is a nop. The transport will be |
| 14508 | // shut down later when we neuter the CordbProcess. |
| 14509 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
| 14510 | // @dbgtodo shim: this is a primitive workaround for interop-detach |
| 14511 | // Eventually, the Debugger owns the detach pipeline, so this won't be necessary. |
| 14512 | if (fDetach && (m_pProcess != NULL)) |
| 14513 | { |
| 14514 | HRESULT hr = m_pNativePipeline->DebugActiveProcessStop(m_pProcess->GetProcessDescriptor()->m_Pid); |
| 14515 | |
| 14516 | // We don't expect detach to fail (we check earlier for common conditions that |
| 14517 | // may cause it to fail) |
| 14518 | SIMPLIFYING_ASSUMPTION(SUCCEEDED(hr)); |
| 14519 | if( FAILED(hr) ) |
| 14520 | { |
| 14521 | m_actionResult = hr; |
| 14522 | SetEvent(m_actionTakenEvent); |
| 14523 | return; |
| 14524 | } |
| 14525 | } |
| 14526 | #endif // !FEATURE_DBGIPC_TRANSPORT_DI |
| 14527 | |
| 14528 | |
| 14529 | // We don't really care if we're on the Win32 thread or not here. We just want to be sure that |
| 14530 | // the LS Exit case and the Detach case both occur on the same thread. This makes it much easier |
| 14531 | // to assert that if we exit while detaching, EP is only called once. |
| 14532 | // If we ever decide to make the RCET listen on the LS process handle for EP(exit), then we should also |
| 14533 | // make the EP(detach) handled on the RCET (via DoFavor() ). |
| 14534 | _ASSERTE(IsWin32EventThread()); |
| 14535 | |
| 14536 | // So either the Exit case or Detach case must happen first. |
| 14537 | // 1) If Detach first, then LS process is removed from wait set and so EP(Exit) will never happen |
| 14538 | // because we check wait set after returning from EP(Detach). |
| 14539 | // 2) If Exit is first, m_pProcess gets set=NULL. EP(detach) will still get called, so explicitly check that. |
| 14540 | if (fDetach && ((m_pProcess == NULL) || m_pProcess->m_terminated)) |
| 14541 | { |
| 14542 | // m_terminated is only set after the LS exits. |
| 14543 | // So the only way (fDetach && m_terminated) is true is if the LS exited while detaching. In that case |
| 14544 | // we already called EP(exit) and we don't want to call it again for EP(detach). So return here. |
| 14545 | LOG((LF_CORDB, LL_INFO1000,"W32ET::EP: In EP(detach), but EP(exit) already called. Early failure\n" )); |
| 14546 | |
| 14547 | m_actionResult = CORDBG_E_PROCESS_TERMINATED; |
| 14548 | SetEvent(m_actionTakenEvent); |
| 14549 | |
| 14550 | return; |
| 14551 | } |
| 14552 | |
| 14553 | // We null m_pProcess at the end here, so |
| 14554 | // Only way we could get here w/ null process is if we're called twice. We can only be called |
| 14555 | // by detach or exit. Can't detach twice, can't exit twice, so must have been one of each. |
| 14556 | // If exit is first, we got removed from the wait set, so 2nd call must be detach and we'd catch |
| 14557 | // that above. If detach is first, we'd get removed from the wait set and so exit would never happen. |
| 14558 | _ASSERTE(m_pProcess != NULL); |
| 14559 | _ASSERTE(!m_pProcess->ThreadHoldsProcessLock()); |
| 14560 | |
| 14561 | |
| 14562 | |
| 14563 | // Mark the process teminated. After this, the RCET will never call FlushQueuedEvents. It will |
| 14564 | // ignore all events it receives (including a SyncComplete) and the RCET also does not listen |
| 14565 | // to terminated processes (so ProcessStateChange() won't cause a FQE either). |
| 14566 | m_pProcess->Terminating(fDetach); |
| 14567 | |
| 14568 | // Take care of the race where the process exits right after the user calls Continue() from the last |
| 14569 | // managed event but before the handler has actually returned. |
| 14570 | // |
| 14571 | // Also, To get through this lock means that either: |
| 14572 | // 1. FlushQueuedEvents is not currently executing and no one will call FQE. |
| 14573 | // 2. FQE is exiting but is in the middle of a callback (so AreDispatchingEvent = true) |
| 14574 | // |
| 14575 | m_pProcess->Lock(); |
| 14576 | |
| 14577 | m_pProcess->m_exiting = true; |
| 14578 | |
| 14579 | if (fDetach) |
| 14580 | { |
| 14581 | m_pProcess->SetSynchronized(false); |
| 14582 | } |
| 14583 | |
| 14584 | // If we are exiting, we *must* dispatch the ExitProcess callback, but we will delete all the events |
| 14585 | // in the queue and not bother dispatching anything else. If (and only if) we are currently dispatching |
| 14586 | // an event, then we will wait while that event is finished before invoking ExitProcess. |
| 14587 | // (Note that a dispatched event has already been removed from the queue) |
| 14588 | |
| 14589 | // Remove the process from the global list of processes. |
| 14590 | m_cordb->RemoveProcess(m_pProcess); |
| 14591 | |
| 14592 | if (fDetach) |
| 14593 | { |
| 14594 | // Signal the hr to the caller. |
| 14595 | LOG((LF_CORDB, LL_INFO1000,"W32ET::EP: Detach: send result back!\n" )); |
| 14596 | |
| 14597 | m_actionResult = S_OK; |
| 14598 | SetEvent(m_actionTakenEvent); |
| 14599 | } |
| 14600 | |
| 14601 | m_pProcess->Unlock(); |
| 14602 | |
| 14603 | // Delete all queued events |
| 14604 | m_pProcess->DeleteQueuedEvents(); |
| 14605 | |
| 14606 | |
| 14607 | // If we're detaching, then the Detach already neutered everybody, so nothing here. |
| 14608 | // If we're exiting, then we still need to neuter things, but we can't do that on this thread, |
| 14609 | // so we queue it. We also need to dispatch an exit process callback. We'll queue that onto the RCET |
| 14610 | // and dispatch it inband w/the other callbacks. |
| 14611 | if (!fDetach) |
| 14612 | { |
| 14613 | #ifdef FEATURE_PAL |
| 14614 | // Cleanup the transport pipe and semaphore files that might be left by the target (LS) process. |
| 14615 | m_pNativePipeline->CleanupTargetProcess(); |
| 14616 | #endif |
| 14617 | ExitProcessWorkItem * pItem = new (nothrow) ExitProcessWorkItem(m_pProcess); |
| 14618 | if (pItem != NULL) |
| 14619 | { |
| 14620 | m_cordb->m_rcEventThread->QueueAsyncWorkItem(pItem); |
| 14621 | } |
| 14622 | } |
| 14623 | |
| 14624 | // This will remove the process from our wait lists (so that we don't send multiple ExitProcess events). |
| 14625 | m_pProcess.Clear(); |
| 14626 | } |
| 14627 | |
| 14628 | |
| 14629 | // |
| 14630 | // Start actually creates and starts the thread. |
| 14631 | // |
| 14632 | HRESULT CordbWin32EventThread::Start() |
| 14633 | { |
| 14634 | HRESULT hr = S_OK; |
| 14635 | if (m_threadControlEvent == NULL) |
| 14636 | return E_INVALIDARG; |
| 14637 | |
| 14638 | // Create the thread suspended to make sure that m_threadId is set |
| 14639 | // before CordbWin32EventThread::ThreadProc runs |
| 14640 | // Stack size = 0x80000 = 512KB |
| 14641 | m_thread = CreateThread(NULL, 0x80000, &CordbWin32EventThread::ThreadProc, |
| 14642 | (LPVOID) this, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, &m_threadId); |
| 14643 | |
| 14644 | if (m_thread == NULL) |
| 14645 | return HRESULT_FROM_GetLastError(); |
| 14646 | |
| 14647 | DWORD succ = ResumeThread(m_thread); |
| 14648 | if (succ == (DWORD)-1) |
| 14649 | return HRESULT_FROM_GetLastError(); |
| 14650 | return hr; |
| 14651 | } |
| 14652 | |
| 14653 | |
| 14654 | // |
| 14655 | // Stop causes the thread to stop receiving events and exit. It |
| 14656 | // waits for it to exit before returning. |
| 14657 | // |
| 14658 | HRESULT CordbWin32EventThread::Stop() |
| 14659 | { |
| 14660 | HRESULT hr = S_OK; |
| 14661 | |
| 14662 | // m_pProcess may be NULL from CordbWin32EventThread::ExitProcess |
| 14663 | |
| 14664 | // Can't block on W32ET while holding the process-lock since the W32ET may need that to exit. |
| 14665 | // But since m_pProcess may be null, we can't enforce that. |
| 14666 | |
| 14667 | if (m_thread != NULL) |
| 14668 | { |
| 14669 | LockSendToWin32EventThreadMutex(); |
| 14670 | m_action = W32ETA_NONE; |
| 14671 | m_run = FALSE; |
| 14672 | |
| 14673 | SetEvent(m_threadControlEvent); |
| 14674 | UnlockSendToWin32EventThreadMutex(); |
| 14675 | |
| 14676 | DWORD ret = WaitForSingleObject(m_thread, INFINITE); |
| 14677 | |
| 14678 | if (ret != WAIT_OBJECT_0) |
| 14679 | hr = HRESULT_FROM_GetLastError(); |
| 14680 | } |
| 14681 | |
| 14682 | m_pProcess.Clear(); |
| 14683 | m_cordb.Clear(); |
| 14684 | |
| 14685 | return hr; |
| 14686 | } |
| 14687 | |
| 14688 | |
| 14689 | |
| 14690 | |
| 14691 | |
| 14692 | |
| 14693 | |
| 14694 | |
| 14695 | // Allocate a buffer of cbBuffer bytes in the target. |
| 14696 | // |
| 14697 | // Arguments: |
| 14698 | // cbBuffer - count of bytes for the buffer. |
| 14699 | // |
| 14700 | // Returns: |
| 14701 | // a TargetBuffer describing the new memory region in the target. |
| 14702 | // Throws on error. |
| 14703 | TargetBuffer CordbProcess::GetRemoteBuffer(ULONG cbBuffer) |
| 14704 | { |
| 14705 | INTERNAL_SYNC_API_ENTRY(this); // |
| 14706 | |
| 14707 | // Create and initialize the event as synchronous |
| 14708 | DebuggerIPCEvent event; |
| 14709 | InitIPCEvent(&event, |
| 14710 | DB_IPCE_GET_BUFFER, |
| 14711 | true, |
| 14712 | VMPTR_AppDomain::NullPtr()); |
| 14713 | |
| 14714 | // Indicate the buffer size wanted |
| 14715 | event.GetBuffer.bufSize = cbBuffer; |
| 14716 | |
| 14717 | // Make the request, which is synchronous |
| 14718 | HRESULT hr = SendIPCEvent(&event, sizeof(event)); |
| 14719 | IfFailThrow(hr); |
| 14720 | _ASSERTE(event.type == DB_IPCE_GET_BUFFER_RESULT); |
| 14721 | |
| 14722 | IfFailThrow(event.GetBufferResult.hr); |
| 14723 | |
| 14724 | // The request succeeded. Return the newly allocated range. |
| 14725 | return TargetBuffer(event.GetBufferResult.pBuffer, cbBuffer); |
| 14726 | } |
| 14727 | |
| 14728 | /* |
| 14729 | * This will release a previously allocated left side buffer. |
| 14730 | */ |
| 14731 | HRESULT CordbProcess::ReleaseRemoteBuffer(void **ppBuffer) |
| 14732 | { |
| 14733 | INTERNAL_SYNC_API_ENTRY(this); // |
| 14734 | |
| 14735 | _ASSERTE(m_pShim != NULL); |
| 14736 | |
| 14737 | // Create and initialize the event as synchronous |
| 14738 | DebuggerIPCEvent event; |
| 14739 | InitIPCEvent(&event, |
| 14740 | DB_IPCE_RELEASE_BUFFER, |
| 14741 | true, |
| 14742 | VMPTR_AppDomain::NullPtr()); |
| 14743 | |
| 14744 | // Indicate the buffer to release |
| 14745 | event.ReleaseBuffer.pBuffer = (*ppBuffer); |
| 14746 | |
| 14747 | // Make the request, which is synchronous |
| 14748 | HRESULT hr = SendIPCEvent(&event, sizeof(event)); |
| 14749 | TESTANDRETURNHR(hr); |
| 14750 | |
| 14751 | (*ppBuffer) = NULL; |
| 14752 | |
| 14753 | // Indicate success |
| 14754 | return event.ReleaseBufferResult.hr; |
| 14755 | } |
| 14756 | |
| 14757 | HRESULT CordbProcess::SetDesiredNGENCompilerFlags(DWORD dwFlags) |
| 14758 | { |
| 14759 | PUBLIC_API_ENTRY(this); |
| 14760 | FAIL_IF_NEUTERED(this); |
| 14761 | |
| 14762 | #if defined(FEATURE_PREJIT) |
| 14763 | if ((dwFlags != CORDEBUG_JIT_DEFAULT) && (dwFlags != CORDEBUG_JIT_DISABLE_OPTIMIZATION)) |
| 14764 | { |
| 14765 | return E_INVALIDARG; |
| 14766 | } |
| 14767 | |
| 14768 | CordbProcess *pProcess = GetProcess(); |
| 14769 | ATT_REQUIRE_STOPPED_MAY_FAIL(pProcess); |
| 14770 | HRESULT hr = S_OK; |
| 14771 | EX_TRY |
| 14772 | { |
| 14773 | // Left-side checks that this is a valid time to set the Ngen flags. |
| 14774 | hr = pProcess->GetDAC()->SetNGENCompilerFlags(dwFlags); |
| 14775 | if (!SUCCEEDED(hr) && GetShim() != NULL) |
| 14776 | { |
| 14777 | // Emulate V2 error semantics. |
| 14778 | hr = GetShim()->FilterSetNgenHresult(hr); |
| 14779 | } |
| 14780 | } |
| 14781 | EX_CATCH_HRESULT(hr); |
| 14782 | return hr; |
| 14783 | |
| 14784 | #else // !FEATURE_PREJIT |
| 14785 | return CORDBG_E_NGEN_NOT_SUPPORTED; |
| 14786 | |
| 14787 | #endif // FEATURE_PREJIT |
| 14788 | } |
| 14789 | |
| 14790 | HRESULT CordbProcess::GetDesiredNGENCompilerFlags(DWORD *pdwFlags ) |
| 14791 | { |
| 14792 | PUBLIC_API_ENTRY(this); |
| 14793 | FAIL_IF_NEUTERED(this); |
| 14794 | VALIDATE_POINTER_TO_OBJECT(pdwFlags, DWORD*); |
| 14795 | *pdwFlags = 0; |
| 14796 | |
| 14797 | CordbProcess *pProcess = GetProcess(); |
| 14798 | ATT_REQUIRE_STOPPED_MAY_FAIL(pProcess); |
| 14799 | HRESULT hr = S_OK; |
| 14800 | EX_TRY |
| 14801 | { |
| 14802 | hr = pProcess->GetDAC()->GetNGENCompilerFlags(pdwFlags); |
| 14803 | } |
| 14804 | EX_CATCH_HRESULT(hr); |
| 14805 | return hr; |
| 14806 | } |
| 14807 | |
| 14808 | //----------------------------------------------------------------------------- |
| 14809 | // Get an ICorDebugReference Value for the GC handle. |
| 14810 | // handle - raw bits for the GC handle. |
| 14811 | // pOutHandle |
| 14812 | //----------------------------------------------------------------------------- |
| 14813 | HRESULT CordbProcess::GetReferenceValueFromGCHandle( |
| 14814 | UINT_PTR gcHandle, |
| 14815 | ICorDebugReferenceValue **pOutValue) |
| 14816 | { |
| 14817 | PUBLIC_API_ENTRY(this); |
| 14818 | FAIL_IF_NEUTERED(this); |
| 14819 | ATT_REQUIRE_STOPPED_MAY_FAIL(this); |
| 14820 | VALIDATE_POINTER_TO_OBJECT(pOutValue, ICorDebugReferenceValue*); |
| 14821 | |
| 14822 | *pOutValue = NULL; |
| 14823 | HRESULT hr = S_OK; |
| 14824 | |
| 14825 | EX_TRY |
| 14826 | { |
| 14827 | if (gcHandle == NULL) |
| 14828 | { |
| 14829 | ThrowHR(CORDBG_E_BAD_REFERENCE_VALUE); |
| 14830 | } |
| 14831 | |
| 14832 | IDacDbiInterface* pDAC = GetProcess()->GetDAC(); |
| 14833 | VMPTR_OBJECTHANDLE vmObjHandle = pDAC->GetVmObjectHandle(gcHandle); |
| 14834 | if(!pDAC->IsVmObjectHandleValid(vmObjHandle)) |
| 14835 | { |
| 14836 | ThrowHR(CORDBG_E_BAD_REFERENCE_VALUE); |
| 14837 | } |
| 14838 | ULONG appDomainId = pDAC->GetAppDomainIdFromVmObjectHandle(vmObjHandle); |
| 14839 | VMPTR_AppDomain vmAppDomain = pDAC->GetAppDomainFromId(appDomainId); |
| 14840 | |
| 14841 | RSLockHolder lockHolder(GetProcessLock()); |
| 14842 | CordbAppDomain * pAppDomain = LookupOrCreateAppDomain(vmAppDomain); |
| 14843 | lockHolder.Release(); |
| 14844 | |
| 14845 | // Now that we finally have the AppDomain, we can go ahead and get a ReferenceValue |
| 14846 | // from the ObjectHandle. |
| 14847 | hr = CordbReferenceValue::BuildFromGCHandle(pAppDomain, vmObjHandle, pOutValue); |
| 14848 | _ASSERTE(SUCCEEDED(hr) == (*pOutValue != NULL)); |
| 14849 | IfFailThrow(hr); |
| 14850 | } |
| 14851 | EX_CATCH_HRESULT(hr); |
| 14852 | return hr; |
| 14853 | } |
| 14854 | |
| 14855 | //----------------------------------------------------------------------------- |
| 14856 | // Return count of outstanding GC handles held by CordbHandleValue objects |
| 14857 | LONG CordbProcess::OutstandingHandles() |
| 14858 | { |
| 14859 | return m_cOutstandingHandles; |
| 14860 | } |
| 14861 | |
| 14862 | //----------------------------------------------------------------------------- |
| 14863 | // Increment the outstanding handle count for code:CordbProces::OutstandingHandles |
| 14864 | // This is the inverse of code:CordbProces::DecrementOutstandingHandles |
| 14865 | void CordbProcess::IncrementOutstandingHandles() |
| 14866 | { |
| 14867 | _ASSERTE(ThreadHoldsProcessLock()); |
| 14868 | m_cOutstandingHandles++; |
| 14869 | } |
| 14870 | |
| 14871 | //----------------------------------------------------------------------------- |
| 14872 | // Decrement the outstanding handle count for code:CordbProces::OutstandingHandles |
| 14873 | // This is the inverse of code:CordbProces::IncrementOutstandingHandles |
| 14874 | void CordbProcess::DecrementOutstandingHandles() |
| 14875 | { |
| 14876 | _ASSERTE(ThreadHoldsProcessLock()); |
| 14877 | m_cOutstandingHandles--; |
| 14878 | } |
| 14879 | |
| 14880 | |
| 14881 | /* |
| 14882 | * IsReadyForDetach |
| 14883 | * |
| 14884 | * This method encapsulates all logic for deciding if it is ok for a debugger to |
| 14885 | * detach from the process at this time. |
| 14886 | * |
| 14887 | * Parameters: None. |
| 14888 | * |
| 14889 | * Returns: S_OK if it is ok to detach, else a specific HRESULT describing why it |
| 14890 | * is not ok to detach. |
| 14891 | * |
| 14892 | */ |
| 14893 | HRESULT CordbProcess::IsReadyForDetach() |
| 14894 | { |
| 14895 | INTERNAL_API_ENTRY(this); |
| 14896 | |
| 14897 | // Always safe to detach in V3 case. |
| 14898 | if (m_pShim == NULL) |
| 14899 | { |
| 14900 | return S_OK; |
| 14901 | } |
| 14902 | |
| 14903 | // If not initialized yet, then there are no detach liabilities. |
| 14904 | if (!m_initialized) |
| 14905 | { |
| 14906 | return S_OK; |
| 14907 | } |
| 14908 | |
| 14909 | RSLockHolder lockHolder(&this->m_processMutex); |
| 14910 | |
| 14911 | // |
| 14912 | // If there are any outstanding func-evals then fail the detach. |
| 14913 | // |
| 14914 | if (OutstandingEvalCount() != 0) |
| 14915 | { |
| 14916 | return CORDBG_E_DETACH_FAILED_OUTSTANDING_EVALS; |
| 14917 | } |
| 14918 | |
| 14919 | // V2 didn't check outstanding handles (code:CordbProcess::OutstandingHandles) |
| 14920 | // because it could automatically clean those up on detach. |
| 14921 | |
| 14922 | // |
| 14923 | // If there are any outstanding steppers then fail the detach. |
| 14924 | // |
| 14925 | if (m_steppers.IsInitialized() && (m_steppers.GetCount() > 0)) |
| 14926 | { |
| 14927 | return CORDBG_E_DETACH_FAILED_OUTSTANDING_STEPPERS; |
| 14928 | } |
| 14929 | |
| 14930 | // |
| 14931 | // If there are any outstanding breakpoints then fail the detach. |
| 14932 | // |
| 14933 | HASHFIND foundAppDomain; |
| 14934 | CordbAppDomain *pAppDomain = m_appDomains.FindFirst(&foundAppDomain); |
| 14935 | |
| 14936 | while (pAppDomain != NULL) |
| 14937 | { |
| 14938 | if (pAppDomain->m_breakpoints.IsInitialized() && (pAppDomain->m_breakpoints.GetCount() > 0)) |
| 14939 | { |
| 14940 | return CORDBG_E_DETACH_FAILED_OUTSTANDING_BREAKPOINTS; |
| 14941 | } |
| 14942 | |
| 14943 | // Check for any outstanding EnC modules. |
| 14944 | HASHFIND foundModule; |
| 14945 | CordbModule * pModule = pAppDomain->m_modules.FindFirst(&foundModule); |
| 14946 | while (pModule != NULL) |
| 14947 | { |
| 14948 | if (pModule->m_EnCCount > 0) |
| 14949 | { |
| 14950 | return CORDBG_E_DETACH_FAILED_ON_ENC; |
| 14951 | } |
| 14952 | pModule = pAppDomain->m_modules.FindNext(&foundModule); |
| 14953 | } |
| 14954 | |
| 14955 | |
| 14956 | pAppDomain = m_appDomains.FindNext(&foundAppDomain); |
| 14957 | } |
| 14958 | |
| 14959 | // If we're using the shim, give a chance to early-out if the OS doesn't support detach |
| 14960 | // so that the user can continue to debug in that case. |
| 14961 | // Ideally we'd just rely on the failure from DebugActiveProcessStop, but by then it's too late |
| 14962 | // to recover. This function is our only chance to distinguish between graceful detach failures |
| 14963 | // and hard detach failures (after which the process object is neutered). |
| 14964 | if (m_pShim != NULL) |
| 14965 | { |
| 14966 | #if !defined(FEATURE_CORESYSTEM) // CORESYSTEM TODO |
| 14967 | HModuleHolder hKernel32; |
| 14968 | hKernel32 = WszLoadLibrary(W("kernel32" )); |
| 14969 | if (hKernel32 == NULL) |
| 14970 | return HRESULT_FROM_GetLastError(); |
| 14971 | typedef BOOL (*DebugActiveProcessStopSig) (DWORD); |
| 14972 | DebugActiveProcessStopSig pDebugActiveProcessStop = |
| 14973 | reinterpret_cast<DebugActiveProcessStopSig>(GetProcAddress(hKernel32, "DebugActiveProcessStop" )); |
| 14974 | if (pDebugActiveProcessStop == NULL) |
| 14975 | return COR_E_PLATFORMNOTSUPPORTED; |
| 14976 | #endif |
| 14977 | } |
| 14978 | |
| 14979 | return S_OK; |
| 14980 | } |
| 14981 | |
| 14982 | |
| 14983 | /* |
| 14984 | * Look for any thread which was last seen in the specified AppDomain. |
| 14985 | * The CordbAppDomain object is about to be neutered due to an AD Unload |
| 14986 | * So the thread must no longer be considered to be in that domain. |
| 14987 | * Note that this is a workaround due to the existance of the (possibly incorrect) |
| 14988 | * cached AppDomain value. Ideally we would remove the cached value entirely |
| 14989 | * and there would be no need for this. |
| 14990 | * |
| 14991 | * @dbgtodo: , appdomain: We should remove CordbThread::m_pAppDomain in the V3 architecture. |
| 14992 | * If we need the thread's current domain, we should get it accurately with DAC. |
| 14993 | */ |
| 14994 | void CordbProcess::UpdateThreadsForAdUnload(CordbAppDomain * pAppDomain) |
| 14995 | { |
| 14996 | INTERNAL_API_ENTRY(this); |
| 14997 | |
| 14998 | // If we're doing an AD unload then we should have already seen the ATTACH |
| 14999 | // notification for the default domain. |
| 15000 | //_ASSERTE( m_pDefaultAppDomain != NULL ); |
| 15001 | // @dbgtodo appdomain: fix Default domain invariants with DAC-izing Appdomain work. |
| 15002 | |
| 15003 | RSLockHolder lockHolder(GetProcessLock()); |
| 15004 | |
| 15005 | CordbThread* t; |
| 15006 | HASHFIND find; |
| 15007 | |
| 15008 | // We don't need to prepopulate here (to collect LS state) because we're just updating RS state. |
| 15009 | for (t = m_userThreads.FindFirst(&find); |
| 15010 | t != NULL; |
| 15011 | t = m_userThreads.FindNext(&find)) |
| 15012 | { |
| 15013 | if( t->GetAppDomain() == pAppDomain ) |
| 15014 | { |
| 15015 | // This thread cannot actually be in this AppDomain anymore (since it's being |
| 15016 | // unloaded). Reset it to point to the default AppDomain |
| 15017 | t->m_pAppDomain = m_pDefaultAppDomain; |
| 15018 | } |
| 15019 | } |
| 15020 | } |
| 15021 | |
| 15022 | // CordbProcess::LookupClass |
| 15023 | // Looks up a previously constructed CordbClass instance without creating. May return NULL if the |
| 15024 | // CordbClass instance doesn't exist. |
| 15025 | // Argument: (in) vmDomainFile - pointer to the domainfile for the module |
| 15026 | // (in) mdTypeDef - metadata token for the class |
| 15027 | // Return value: pointer to a previously created CordbClass instance or NULL in none exists |
| 15028 | CordbClass * CordbProcess::LookupClass(ICorDebugAppDomain * pAppDomain, VMPTR_DomainFile vmDomainFile, mdTypeDef classToken) |
| 15029 | { |
| 15030 | _ASSERTE(ThreadHoldsProcessLock()); |
| 15031 | |
| 15032 | if (pAppDomain != NULL) |
| 15033 | { |
| 15034 | CordbModule * pModule = ((CordbAppDomain *)pAppDomain)->m_modules.GetBase(VmPtrToCookie(vmDomainFile)); |
| 15035 | if (pModule != NULL) |
| 15036 | { |
| 15037 | return pModule->LookupClass(classToken); |
| 15038 | } |
| 15039 | } |
| 15040 | return NULL; |
| 15041 | } // CordbProcess::LookupClass |
| 15042 | |
| 15043 | //--------------------------------------------------------------------------------------- |
| 15044 | // Look for a specific module in the process. |
| 15045 | // |
| 15046 | // Arguments: |
| 15047 | // vmDomainFile - non-null module to lookup |
| 15048 | // |
| 15049 | // Returns: |
| 15050 | // a CordbModule object for the given cookie. Object may be from the cache, or created |
| 15051 | // lazily. |
| 15052 | // Never returns null. Throws on error. |
| 15053 | // |
| 15054 | // Notes: |
| 15055 | // A VMPTR_DomainFile has appdomain affinity, but is ultimately scoped to a process. |
| 15056 | // So if we get a raw VMPTR_DomainFile (eg, from the stackwalker or from some other |
| 15057 | // lookup function), then we need to do a process wide lookup since we don't know which |
| 15058 | // appdomain it's in. If you know the appdomain, you can use code:CordbAppDomain::LookupOrCreateModule. |
| 15059 | // |
| 15060 | CordbModule * CordbProcess::LookupOrCreateModule(VMPTR_DomainFile vmDomainFile) |
| 15061 | { |
| 15062 | INTERNAL_API_ENTRY(this); |
| 15063 | |
| 15064 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
| 15065 | _ASSERTE(!vmDomainFile.IsNull()); |
| 15066 | |
| 15067 | DomainFileInfo data; |
| 15068 | GetDAC()->GetDomainFileData(vmDomainFile, &data); // throws |
| 15069 | |
| 15070 | CordbAppDomain * pAppDomain = LookupOrCreateAppDomain(data.vmAppDomain); |
| 15071 | return pAppDomain->LookupOrCreateModule(vmDomainFile); |
| 15072 | } |
| 15073 | |
| 15074 | //--------------------------------------------------------------------------------------- |
| 15075 | // Determine if the process has any in-band queued events which have not been dispatched |
| 15076 | // |
| 15077 | // Returns: |
| 15078 | // TRUE iff there are undispatched IB events |
| 15079 | // |
| 15080 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 15081 | BOOL CordbProcess::HasUndispatchedNativeEvents() |
| 15082 | { |
| 15083 | INTERNAL_API_ENTRY(this); |
| 15084 | |
| 15085 | CordbUnmanagedEvent* pEvent = m_unmanagedEventQueue; |
| 15086 | while(pEvent != NULL && pEvent->IsDispatched()) |
| 15087 | { |
| 15088 | pEvent = pEvent->m_next; |
| 15089 | } |
| 15090 | |
| 15091 | return pEvent != NULL; |
| 15092 | } |
| 15093 | #endif |
| 15094 | |
| 15095 | //--------------------------------------------------------------------------------------- |
| 15096 | // Determine if the process has any in-band queued events which have not been user continued |
| 15097 | // |
| 15098 | // Returns: |
| 15099 | // TRUE iff there are user uncontinued IB events |
| 15100 | // |
| 15101 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 15102 | BOOL CordbProcess::HasUserUncontinuedNativeEvents() |
| 15103 | { |
| 15104 | INTERNAL_API_ENTRY(this); |
| 15105 | |
| 15106 | CordbUnmanagedEvent* pEvent = m_unmanagedEventQueue; |
| 15107 | while(pEvent != NULL && pEvent->IsEventUserContinued()) |
| 15108 | { |
| 15109 | pEvent = pEvent->m_next; |
| 15110 | } |
| 15111 | |
| 15112 | return pEvent != NULL; |
| 15113 | } |
| 15114 | #endif |
| 15115 | |
| 15116 | //--------------------------------------------------------------------------------------- |
| 15117 | // Hijack the thread which had this event. If the thread is already hijacked this method |
| 15118 | // has no effect. |
| 15119 | // |
| 15120 | // Arguments: |
| 15121 | // pUnmanagedEvent - the debug event which requires us to hijack |
| 15122 | // |
| 15123 | // Returns: |
| 15124 | // S_OK on success, failing HRESULT if the hijack could not be set up |
| 15125 | // |
| 15126 | #ifdef FEATURE_INTEROP_DEBUGGING |
| 15127 | HRESULT CordbProcess::HijackIBEvent(CordbUnmanagedEvent * pUnmanagedEvent) |
| 15128 | { |
| 15129 | // Can't hijack after the event has already been continued hijacked |
| 15130 | _ASSERTE(!pUnmanagedEvent->IsEventContinuedHijacked()); |
| 15131 | // Can only hijack IB events |
| 15132 | _ASSERTE(pUnmanagedEvent->IsIBEvent()); |
| 15133 | |
| 15134 | // If we already hijacked the event then there is nothing left to do |
| 15135 | if(pUnmanagedEvent->m_owner->IsFirstChanceHijacked() || |
| 15136 | pUnmanagedEvent->m_owner->IsGenericHijacked()) |
| 15137 | { |
| 15138 | return S_OK; |
| 15139 | } |
| 15140 | |
| 15141 | ResetEvent(this->m_leftSideUnmanagedWaitEvent); |
| 15142 | if (pUnmanagedEvent->m_currentDebugEvent.u.Exception.dwFirstChance) |
| 15143 | { |
| 15144 | HRESULT hr = pUnmanagedEvent->m_owner->SetupFirstChanceHijackForSync(); |
| 15145 | SIMPLIFYING_ASSUMPTION(SUCCEEDED(hr)); |
| 15146 | return hr; |
| 15147 | } |
| 15148 | else // Second chance exceptions must be generic hijacked. |
| 15149 | { |
| 15150 | HRESULT hr = pUnmanagedEvent->m_owner->SetupGenericHijack(pUnmanagedEvent->m_currentDebugEvent.dwDebugEventCode, &pUnmanagedEvent->m_currentDebugEvent.u.Exception.ExceptionRecord); |
| 15151 | SIMPLIFYING_ASSUMPTION(SUCCEEDED(hr)); |
| 15152 | return hr; |
| 15153 | } |
| 15154 | } |
| 15155 | #endif |
| 15156 | |
| 15157 | // Sets a bitfield reflecting the managed debugging state at the time of |
| 15158 | // the jit attach. |
| 15159 | HRESULT CordbProcess::GetAttachStateFlags(CLR_DEBUGGING_PROCESS_FLAGS *pFlags) |
| 15160 | { |
| 15161 | HRESULT hr = S_OK; |
| 15162 | PUBLIC_REENTRANT_API_BEGIN(this) |
| 15163 | { |
| 15164 | if(pFlags == NULL) |
| 15165 | hr = E_POINTER; |
| 15166 | else |
| 15167 | *pFlags = GetDAC()->GetAttachStateFlags(); |
| 15168 | } |
| 15169 | PUBLIC_API_END(hr); |
| 15170 | |
| 15171 | return hr; |
| 15172 | } |
| 15173 | |
| 15174 | // Determine if this version of ICorDebug is compatibile with the ICorDebug in the specified major CLR version |
| 15175 | bool CordbProcess::IsCompatibleWith(DWORD clrMajorVersion) |
| 15176 | { |
| 15177 | // The debugger versioning policy is that debuggers generally need to opt-in to supporting major new |
| 15178 | // versions of the CLR. Often new versions of the CLR violate some invariant that previous debuggers assume |
| 15179 | // (eg. hot/cold splitting in Whidbey, multiple CLRs in a process in CLR v4), and neither VS or the CLR |
| 15180 | // teams generally want the support burden of forward compatibility. |
| 15181 | |
| 15182 | // |
| 15183 | // If this assert is firing for you, its probably because the major version |
| 15184 | // number of the clr.dll has changed. This assert is here to remind you to do a bit of other |
| 15185 | // work you may not have realized you needed to do so that our versioning works smoothly |
| 15186 | // for debugging. You probably want to contact the CLR DST team if you are a |
| 15187 | // non-debugger person hitting this. DON'T JUST DELETE THIS ASSERT!!! |
| 15188 | // |
| 15189 | // 1) You should ensure new versions of all ICorDebug users in DevDiv (VS Debugger, MDbg, etc.) |
| 15190 | // are using a creation path that explicitly specifies that they support this new major |
| 15191 | // version of the CLR. |
| 15192 | // 2) You should file an issue to track blocking earlier debuggers from targetting this |
| 15193 | // version of the CLR (i.e. update requiredVersion to the new CLR major |
| 15194 | // version). To enable a smooth internal transition, this often isn't done until absolutely |
| 15195 | // necessary (sometimes as late as Beta2). |
| 15196 | // 3) You can consider updating the CLR_ID guid used by the shim to recognize a CLR, but only |
| 15197 | // if it's important to completely hide newer CLRs from the shim. The expectation now |
| 15198 | // is that we won't need to do this (i.e. we'd like VS to give a nice error message about |
| 15199 | // needed a newer version of the debugger, rather than just acting as if a process has no CLR). |
| 15200 | // 4) Update this assert so that it no longer fires for your new CLR version or any of |
| 15201 | // the previous versions, but don't delete the assert... |
| 15202 | // the next CLR version after yours will probably need the same reminder |
| 15203 | |
| 15204 | _ASSERTE_MSG(clrMajorVersion <= 4, |
| 15205 | "Found major CLR version greater than 4 in mscordbi.dll from CLRv4 - contact CLRDST" ); |
| 15206 | |
| 15207 | // This knob lets us enable forward compatibility for internal scenarios, and also simulate new |
| 15208 | // versions of the runtime for testing the failure user-experience in a version of the debugger |
| 15209 | // before it is shipped. |
| 15210 | // We don't want to risk customers getting this, so for RTM builds this must be CHK-only. |
| 15211 | // To aid in internal transition, we may temporarily enable this in RET builds, but when |
| 15212 | // doing so must file a bug to track making it CHK only again before RTM. |
| 15213 | // For example, Dev10 Beta2 shipped with this knob, but it was made CHK-only at the start of RC. |
| 15214 | // In theory we might have a point release someday where we break debugger compat, but |
| 15215 | // it seems unlikely and since this knob is unsupported anyway we can always extend it |
| 15216 | // then (support reading a string value, etc.). So for now we just map the number |
| 15217 | // to the major CLR version number. |
| 15218 | DWORD requiredVersion = 0; |
| 15219 | #ifdef _DEBUG |
| 15220 | requiredVersion = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_Debugging_RequiredVersion); |
| 15221 | #endif |
| 15222 | |
| 15223 | // If unset (the only supported configuration), then we require a debugger designed for CLRv4 |
| 15224 | // for desktop, where we do not allow forward compat. |
| 15225 | // For SL, we allow forward compat. Right now, that means SLv2+ debugger requests can be |
| 15226 | // honored for SLv4. |
| 15227 | if (requiredVersion <= 0) |
| 15228 | { |
| 15229 | requiredVersion = 2; |
| 15230 | } |
| 15231 | |
| 15232 | // Compare the version we were created for against the minimum required |
| 15233 | return (clrMajorVersion >= requiredVersion); |
| 15234 | } |
| 15235 | |
| 15236 | bool CordbProcess::IsThreadSuspendedOrHijacked(ICorDebugThread * pICorDebugThread) |
| 15237 | { |
| 15238 | // An RS lock can be held while this is called. Specifically, |
| 15239 | // CordbThread::EnumerateChains may be on the stack, and it uses |
| 15240 | // ATT_REQUIRE_STOPPED_MAY_FAIL, which holds the CordbProcess::m_StopGoLock lock for |
| 15241 | // its entire duration. As a result, this needs to be considered a reentrant API. See |
| 15242 | // comments above code:PrivateShimCallbackHolder for more info. |
| 15243 | PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM(this); |
| 15244 | |
| 15245 | CordbThread * pCordbThread = static_cast<CordbThread *> (pICorDebugThread); |
| 15246 | return GetDAC()->IsThreadSuspendedOrHijacked(pCordbThread->m_vmThreadToken); |
| 15247 | } |
| 15248 | |