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 | |