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 | |
6 | // |
7 | // File: rsthread.cpp |
8 | // |
9 | //***************************************************************************** |
10 | #include "stdafx.h" |
11 | #include "primitives.h" |
12 | #include <float.h> |
13 | #include <tls.h> |
14 | |
15 | // Stack-based holder for RSPTRs that we allocated to give to the LS. |
16 | // If LS successfully takes ownership of them, then call SuppressRelease(). |
17 | // Else, dtor will free them up. |
18 | // This is using a table protected by the ProcessLock(). |
19 | template <class T> |
20 | class RsPtrHolder |
21 | { |
22 | T * m_pObject; |
23 | RsPointer<T> m_ptr; |
24 | public: |
25 | RsPtrHolder(T* pObject) |
26 | { |
27 | _ASSERTE(pObject != NULL); |
28 | m_ptr.AllocHandle(pObject->GetProcess(), pObject); |
29 | m_pObject = pObject; |
30 | } |
31 | |
32 | // If owner didn't call SuppressRelease() to take ownership, then have dtor free it. |
33 | ~RsPtrHolder() |
34 | { |
35 | if (!m_ptr.IsNull()) |
36 | { |
37 | // @dbgtodo synchronization - push this up. Note that since this is in a dtor; |
38 | // need to order it well against RSLockHolder. |
39 | RSLockHolder lockHolder(m_pObject->GetProcess()->GetProcessLock()); |
40 | T* pObjTest = m_ptr.UnWrapAndRemove(m_pObject->GetProcess()); |
41 | (void)pObjTest; //prevent "unused variable" error from GCC |
42 | _ASSERTE(pObjTest == m_pObject); |
43 | } |
44 | } |
45 | |
46 | RsPointer<T> Ptr() |
47 | { |
48 | return m_ptr; |
49 | } |
50 | void SuppressRelease() |
51 | { |
52 | m_ptr = RsPointer<T>::NullPtr(); |
53 | } |
54 | |
55 | }; |
56 | |
57 | /* ------------------------------------------------------------------------- * |
58 | * Managed Thread classes |
59 | * ------------------------------------------------------------------------- */ |
60 | |
61 | |
62 | //--------------------------------------------------------------------------------------- |
63 | // |
64 | // Instantiate a CordbThread object, which represents a managed thread. |
65 | // |
66 | // Arguments: |
67 | // process - non-null process object that this thread lives in. |
68 | // id - OS thread id of this thread. |
69 | // handle - OS Handle to the native thread in the debuggee. |
70 | // |
71 | //--------------------------------------------------------------------------------------- |
72 | |
73 | CordbThread::CordbThread(CordbProcess * pProcess, VMPTR_Thread vmThread) : |
74 | CordbBase(pProcess, |
75 | VmPtrToCookie(vmThread), |
76 | enumCordbThread), |
77 | m_pContext(NULL), |
78 | m_fContextFresh(false), |
79 | m_pAppDomain(NULL), |
80 | m_debugState(THREAD_RUN), |
81 | m_fFramesFresh(false), |
82 | m_fFloatStateValid(false), |
83 | m_floatStackTop(0), |
84 | m_fException(false), |
85 | m_fCreationEventQueued(false), |
86 | m_EnCRemapFunctionIP(NULL), |
87 | m_userState(kInvalidUserState), |
88 | m_hCachedThread(INVALID_HANDLE_VALUE), |
89 | m_hCachedOutOfProcThread(INVALID_HANDLE_VALUE) |
90 | { |
91 | m_fHasUnhandledException = FALSE; |
92 | m_pExceptionRecord = NULL; |
93 | |
94 | // Thread id may be a "fake" OS id for a CLRHosted thread. |
95 | m_vmThreadToken = vmThread; |
96 | |
97 | // This id must be unique for the thread. V2 uses the current OS thread id. |
98 | // If we ever support fibers, then we need to use something more unique than that. |
99 | m_dwUniqueID = pProcess->GetDAC()->GetUniqueThreadID(vmThread); // may throw |
100 | |
101 | LOG((LF_CORDB, LL_INFO1000, "CT::CT new thread 0x%p vmptr=0x%p id=0x%x\n" , |
102 | this, m_vmThreadToken, m_dwUniqueID)); |
103 | |
104 | // Unique ID should never be 0. |
105 | _ASSERTE(m_dwUniqueID != 0); |
106 | |
107 | m_vmLeftSideContext = VMPTR_CONTEXT::NullPtr(); |
108 | m_vmExcepObjHandle = VMPTR_OBJECTHANDLE::NullPtr(); |
109 | |
110 | #if defined(_DEBUG) |
111 | for (unsigned int i = 0; |
112 | i < (sizeof(m_floatValues) / sizeof(m_floatValues[0])); |
113 | i++) |
114 | { |
115 | m_floatValues[i] = 0; |
116 | } |
117 | #endif |
118 | |
119 | // Set AppDomain |
120 | VMPTR_AppDomain vmAppDomain = pProcess->GetDAC()->GetCurrentAppDomain(vmThread); |
121 | m_pAppDomain = pProcess->LookupOrCreateAppDomain(vmAppDomain); |
122 | _ASSERTE(m_pAppDomain != NULL); |
123 | } |
124 | |
125 | |
126 | CordbThread::~CordbThread() |
127 | { |
128 | // We've already been neutered, thus we don't need to call CleanupStack(). |
129 | // That will have neutered + cleared frames + chains. |
130 | _ASSERTE(IsNeutered()); |
131 | |
132 | // Cleared in neuter |
133 | _ASSERTE(m_pContext == NULL); |
134 | _ASSERTE(m_hCachedThread == INVALID_HANDLE_VALUE); |
135 | _ASSERTE(m_pExceptionRecord == NULL); |
136 | } |
137 | |
138 | // Neutered by the CordbProcess |
139 | void CordbThread::Neuter() |
140 | { |
141 | if (IsNeutered()) |
142 | { |
143 | return; |
144 | } |
145 | |
146 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
147 | |
148 | delete m_pExceptionRecord; |
149 | m_pExceptionRecord = NULL; |
150 | |
151 | // Neuter frames & Chains. |
152 | CleanupStack(); |
153 | |
154 | |
155 | if (m_hCachedThread != INVALID_HANDLE_VALUE) |
156 | { |
157 | CloseHandle(m_hCachedThread); |
158 | m_hCachedThread = INVALID_HANDLE_VALUE; |
159 | } |
160 | |
161 | if( m_pContext != NULL ) |
162 | { |
163 | delete [] m_pContext; |
164 | m_pContext = NULL; |
165 | } |
166 | |
167 | ClearStackFrameCache(); |
168 | |
169 | CordbBase::Neuter(); |
170 | } |
171 | |
172 | HRESULT CordbThread::QueryInterface(REFIID id, void ** ppInterface) |
173 | { |
174 | if (id == IID_ICorDebugThread) |
175 | { |
176 | *ppInterface = static_cast<ICorDebugThread *>(this); |
177 | } |
178 | else if (id == IID_ICorDebugThread2) |
179 | { |
180 | *ppInterface = static_cast<ICorDebugThread2 *>(this); |
181 | } |
182 | else if (id == IID_ICorDebugThread3) |
183 | { |
184 | *ppInterface = static_cast<ICorDebugThread3*>(this); |
185 | } |
186 | else if (id == IID_ICorDebugThread4) |
187 | { |
188 | *ppInterface = static_cast<ICorDebugThread4*>(this); |
189 | } |
190 | else if (id == IID_IUnknown) |
191 | { |
192 | *ppInterface = static_cast<IUnknown *>(static_cast<ICorDebugThread *>(this)); |
193 | } |
194 | else |
195 | { |
196 | *ppInterface = NULL; |
197 | return E_NOINTERFACE; |
198 | } |
199 | |
200 | ExternalAddRef(); |
201 | return S_OK; |
202 | } |
203 | |
204 | |
205 | #ifdef _DEBUG |
206 | // Callback helper for code:CordbThread::DbgAssertThreadDeleted |
207 | // |
208 | // Arguments: |
209 | // vmThread - thread from enumeration of threads in the target. |
210 | // pUserData - the CordbThread for the thread that's deleted |
211 | // |
212 | // static |
213 | void CordbThread::DbgAssertThreadDeletedCallback(VMPTR_Thread vmThread, void * pUserData) |
214 | { |
215 | CordbThread * pThis = reinterpret_cast<CordbThread *>(pUserData); |
216 | INTERNAL_DAC_CALLBACK(pThis->GetProcess()); |
217 | |
218 | VMPTR_Thread vmThreadDelete = pThis->m_vmThreadToken; |
219 | |
220 | CONSISTENCY_CHECK_MSGF((vmThread != vmThreadDelete), |
221 | ("A Thread Exit event was sent, but it still shows up in the enumeration.\n vmThreadDelete=%p\n" , |
222 | VmPtrToCookie(vmThreadDelete))); |
223 | } |
224 | |
225 | // Debug-only helper to Assert that this thread is no longer discoverable in DacDbi enumerations |
226 | // This is designed to enforce the code:IDacDbiInterface#Enumeration rules for enumerations. |
227 | void CordbThread::DbgAssertThreadDeleted() |
228 | { |
229 | // Enumerate through all threads and ensure the deleted threads don't show up. |
230 | GetProcess()->GetDAC()->EnumerateThreads( |
231 | DbgAssertThreadDeletedCallback, |
232 | this); |
233 | } |
234 | #endif // _DEBUG |
235 | |
236 | |
237 | //--------------------------------------------------------------------------------------- |
238 | // Mark that this thread has an unhandled native exception on it. |
239 | // |
240 | // Arguments |
241 | // pRecord - exception record of 2nd-chance exception that we're hijacking at. This will |
242 | // get deep copied into the CordbThread object in case it's needed for hijacking later. |
243 | // |
244 | // Notes: |
245 | // This bit is cleared in code:CordbThread::HijackForUnhandledException |
246 | void CordbThread::SetUnhandledNativeException(const EXCEPTION_RECORD * pExceptionRecord) |
247 | { |
248 | m_fHasUnhandledException = true; |
249 | |
250 | if (m_pExceptionRecord == NULL) |
251 | { |
252 | m_pExceptionRecord = new EXCEPTION_RECORD(); // throws |
253 | } |
254 | memcpy(m_pExceptionRecord, pExceptionRecord, sizeof(EXCEPTION_RECORD)); |
255 | } |
256 | |
257 | //----------------------------------------------------------------------------- |
258 | // Returns true if the thread has an unhandled exception |
259 | // This is during the window after code:CordbThread::SetUnhandledNativeException is called, |
260 | // but before code:CordbThread::HijackForUnhandledException |
261 | bool CordbThread::HasUnhandledNativeException() |
262 | { |
263 | return m_fHasUnhandledException; |
264 | } |
265 | |
266 | |
267 | //--------------------------------------------------------------------------------------- |
268 | // Determine if the thread's latest exception is a managed exception |
269 | // |
270 | // Notes: |
271 | // The CLR's UnhandledExceptionFilter has to make this same determination. |
272 | // |
273 | BOOL CordbThread::IsThreadExceptionManaged() |
274 | { |
275 | CONTRACTL |
276 | { |
277 | THROWS; |
278 | } |
279 | CONTRACTL_END; |
280 | |
281 | // A Thread's latest exception is managed if the VM Thread object has a managed object |
282 | // for the thread's Current Exception property. The CLR's Exception system is very diligent |
283 | // about tracking and clearing the thread's managed exception property. The runtime will clear |
284 | // the object if the exception is caught by unmanaged code (it can do this in the 2nd-pass). |
285 | |
286 | // It's the presence of a throwable that makes the difference between a managed |
287 | // exception event and an unmanaged exception event. |
288 | |
289 | VMPTR_OBJECTHANDLE vmObject = GetProcess()->GetDAC()->GetCurrentException(m_vmThreadToken); |
290 | |
291 | bool fHasThrowable = !vmObject.IsNull(); |
292 | |
293 | return fHasThrowable; |
294 | |
295 | } |
296 | |
297 | // ---------------------------------------------------------------------------- |
298 | // CordbThread::CreateCordbRegisterSet |
299 | // |
300 | // Description: |
301 | // This is a private hook for the shim to create a CordbRegisterSet for a ShimChain. |
302 | // |
303 | // Arguments: |
304 | // * pContext - the CONTEXT to be converted; this must be the leaf CONTEXT of a chain |
305 | // * fLeaf - whether the chain is the leaf chain or not |
306 | // * reason - the chain reason; this is needed for legacy reasons (see below) |
307 | // * ppRegSet - out parameter; return the newly created ICDRegisterSet |
308 | // |
309 | // Notes: |
310 | // * Note that the fQuickUnwind argument of the ctor of CordbRegisterSet is only true |
311 | // for an enter-managed chain. We need to keep the same behaviour here. That's why we need the |
312 | // chain reason. |
313 | // |
314 | |
315 | void CordbThread::CreateCordbRegisterSet(DT_CONTEXT * pContext, |
316 | BOOL fLeaf, |
317 | CorDebugChainReason reason, |
318 | ICorDebugRegisterSet ** ppRegSet) |
319 | { |
320 | CONTRACTL |
321 | { |
322 | THROWS; |
323 | } |
324 | CONTRACTL_END; |
325 | |
326 | PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM(GetProcess()); |
327 | |
328 | IfFailThrow(EnsureThreadIsAlive()); |
329 | |
330 | // The CordbRegisterSet is responsible for freeing this memory. |
331 | NewHolder<DebuggerREGDISPLAY> pDRD(new DebuggerREGDISPLAY()); |
332 | |
333 | // convert the CONTEXT to a DebuggerREGDISPLAY |
334 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
335 | pDAC->ConvertContextToDebuggerRegDisplay(pContext, pDRD, fLeaf); |
336 | |
337 | // create the CordbRegisterSet |
338 | RSInitHolder<CordbRegisterSet> pRS(new CordbRegisterSet(pDRD, |
339 | this, |
340 | (fLeaf == TRUE), |
341 | (reason == CHAIN_ENTER_MANAGED), |
342 | true)); |
343 | pDRD.SuppressRelease(); |
344 | |
345 | pRS.TransferOwnershipExternal(ppRegSet); |
346 | } |
347 | |
348 | // ---------------------------------------------------------------------------- |
349 | // CordbThread::ConvertFrameForILMethodWithoutMetadata |
350 | // |
351 | // Description: |
352 | // This is a private hook for the shim to convert an ICDFrame into an ICDInternalFrame for a dynamic |
353 | // method. There are two cases where we need this: |
354 | // 1) In Arrowhead, dynamic methods are exposed as first-class stack frames, not internal frames. Thus, |
355 | // the shim needs a way to convert an ICDNativeFrame for a dynamic method in Arrowhead to an |
356 | // ICDInternalFrame of type STUBFRAME_LIGHTWEIGHT_FUNCTION in V2. Furthermore, IL stubs, |
357 | // which are also considered as a type of dynamic methods, are not exposed in V2 at all. |
358 | // |
359 | // 2) In V2, PrestubMethodFrames (PMFs) can be exposed as one of two things: a chain of type |
360 | // CHAIN_CLASS_INIT in most cases, or an internal frame of type STUBFRAME_LIGHTWEIGHT_FUNCTION if |
361 | // the method being jitted is a dynamic method. There is no way to make this distinction at the |
362 | // public ICD level. |
363 | // |
364 | // Arguments: |
365 | // * pNativeFrame - the native frame to be converted |
366 | // * ppInternalFrame - out parameter; the converted internal frame; could be NULL (see Notes below) |
367 | // |
368 | // Returns: |
369 | // Return TRUE if conversion has occurred. Note that even if the return value is TRUE, ppInternalFrame |
370 | // could be NULL. See Notes below. |
371 | // |
372 | // Notes: |
373 | // * There are two main types of dynamic methods: ones which are generated by the runtime itself for |
374 | // internal purposes (i.e. IL stubs), and ones which are generated by the user. ppInternalFrame |
375 | // is NULL for IL stubs. We need this functionality because IL stubs are not exposed at all in V2. |
376 | // |
377 | |
378 | BOOL CordbThread::ConvertFrameForILMethodWithoutMetadata(ICorDebugFrame * pFrame, |
379 | ICorDebugInternalFrame2 ** ppInternalFrame2) |
380 | { |
381 | PUBLIC_REENTRANT_API_ENTRY_FOR_SHIM(GetProcess()); |
382 | |
383 | _ASSERTE(ppInternalFrame2 != NULL); |
384 | *ppInternalFrame2 = NULL; |
385 | |
386 | HRESULT hr = E_FAIL; |
387 | |
388 | CordbFrame * pRealFrame = CordbFrame::GetCordbFrameFromInterface(pFrame); |
389 | |
390 | CordbInternalFrame * pInternalFrame = pRealFrame->GetAsInternalFrame(); |
391 | if (pInternalFrame != NULL) |
392 | { |
393 | // The input is an internal frame. |
394 | |
395 | // Check its frame type. |
396 | CorDebugInternalFrameType type; |
397 | hr = pInternalFrame->GetFrameType(&type); |
398 | IfFailThrow(hr); |
399 | |
400 | if (type != STUBFRAME_JIT_COMPILATION) |
401 | { |
402 | // No conversion is necessary. |
403 | return FALSE; |
404 | } |
405 | else |
406 | { |
407 | // We are indeed dealing with a PrestubMethodFrame. |
408 | return pInternalFrame->ConvertInternalFrameForILMethodWithoutMetadata(ppInternalFrame2); |
409 | } |
410 | } |
411 | else |
412 | { |
413 | // The input is a native frame. |
414 | CordbNativeFrame * pNativeFrame = pRealFrame->GetAsNativeFrame(); |
415 | _ASSERTE(pNativeFrame != NULL); |
416 | |
417 | return pNativeFrame->ConvertNativeFrameForILMethodWithoutMetadata(ppInternalFrame2); |
418 | } |
419 | } |
420 | |
421 | //----------------------------------------------------------------------------- |
422 | // Hijack a thread at an unhandled exception. This lets it execute the |
423 | // CLR's Unhandled Exception Filter (which will send the managed 2nd-chance exception event) |
424 | // |
425 | // Notes: |
426 | // OS will not execute Unhandled Exception Filter (UEF) when debugger is attached. |
427 | // The CLR's UEF does useful work, like dispatching 2nd-chance managed exception event |
428 | // and allowing Func-eval and Continuable Exceptions for unhandled exceptions. |
429 | // So hijack the thread, and the hijack will then execute the CLR's UEF just |
430 | // like the OS would. |
431 | void CordbThread::HijackForUnhandledException() |
432 | { |
433 | CONTRACTL |
434 | { |
435 | THROWS; |
436 | } |
437 | CONTRACTL_END; |
438 | |
439 | _ASSERTE(m_pExceptionRecord != NULL); |
440 | |
441 | _ASSERTE(m_fHasUnhandledException); |
442 | m_fHasUnhandledException = false; |
443 | |
444 | |
445 | ULONG32 dwThreadId = GetVolatileOSThreadID(); |
446 | |
447 | // Note that the data-target is not atomic, and we have no rollback mechanism. |
448 | // We have to do several writes. If the data-target fails the writes half-way through the |
449 | // target will be inconsistent. |
450 | |
451 | // We don't bother remembering the original context. LS hijack will have the |
452 | // context on its stack and will pass it to RS just like it does for filter-context. |
453 | GetProcess()->GetDAC()->Hijack( |
454 | m_vmThreadToken, |
455 | dwThreadId, |
456 | m_pExceptionRecord, |
457 | NULL, // LS will have the context. |
458 | 0, // size of context |
459 | EHijackReason::kUnhandledException, |
460 | NULL, |
461 | NULL); |
462 | |
463 | // Notify debugger to clear the exception. |
464 | // This will invoke the data-target. |
465 | GetProcess()->ContinueStatusChanged(dwThreadId, DBG_CONTINUE); |
466 | } |
467 | |
468 | |
469 | HRESULT CordbThread::GetProcess(ICorDebugProcess ** ppProcess) |
470 | { |
471 | PUBLIC_API_ENTRY(this); |
472 | VALIDATE_POINTER_TO_OBJECT(ppProcess, ICorDebugProcess **); |
473 | FAIL_IF_NEUTERED(this); |
474 | |
475 | *ppProcess = GetProcess(); |
476 | GetProcess()->ExternalAddRef(); |
477 | |
478 | return S_OK; |
479 | } |
480 | |
481 | // Public implementation of ICorDebugThread::GetID |
482 | // Back in V1.0, GetID originally meant the OS thread ID that this managed thread was running on. |
483 | // In theory, that can change (fibers, logical thread scheduling, etc). However, in practice, in V1.0, it would |
484 | // not. Thus debuggers took a depedency on GetID being constant. |
485 | // In V2, this returns an opaque handle that is unique to this thread and stable for this thread's lifetime. |
486 | // |
487 | // Compare to code:CordbThread::GetVolatileOSThreadID, which returns the actual OS thread Id (which may change). |
488 | HRESULT CordbThread::GetID(DWORD * pdwThreadId) |
489 | { |
490 | PUBLIC_API_ENTRY(this); |
491 | VALIDATE_POINTER_TO_OBJECT(pdwThreadId, DWORD *); |
492 | FAIL_IF_NEUTERED(this); |
493 | |
494 | *pdwThreadId = GetUniqueId(); |
495 | |
496 | return S_OK; |
497 | } |
498 | |
499 | // Returns a unique ID that's stable for the life of this thread. |
500 | // In a non-hosted scenarios, this can be the OS thread id. |
501 | DWORD CordbThread::GetUniqueId() |
502 | { |
503 | return m_dwUniqueID; |
504 | } |
505 | |
506 | // Implementation of public API, ICorDebugThread::GetHandle |
507 | // @dbgtodo ICDThread - deprecate in V3, offload to Shim |
508 | HRESULT CordbThread::GetHandle(HANDLE * phThreadHandle) |
509 | { |
510 | PUBLIC_API_ENTRY(this); |
511 | VALIDATE_POINTER_TO_OBJECT(phThreadHandle, HANDLE *); |
512 | FAIL_IF_NEUTERED(this); |
513 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
514 | |
515 | if (GetProcess()->GetShim() == NULL) |
516 | { |
517 | _ASSERTE(!"CordbThread::GetHandle() should be not be called on the new architecture" ); |
518 | *phThreadHandle = NULL; |
519 | return E_NOTIMPL; |
520 | } |
521 | |
522 | #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) |
523 | HRESULT hr = S_OK; |
524 | EX_TRY |
525 | { |
526 | HANDLE hThread; |
527 | InternalGetHandle(&hThread); // throws on error |
528 | *phThreadHandle = hThread; |
529 | } |
530 | EX_CATCH_HRESULT(hr); |
531 | #else // FEATURE_DBGIPC_TRANSPORT_DI |
532 | // In the old SL implementation of Mac debugging, we return a thread handle faked up by the PAL on the Mac. |
533 | // The returned handle is meaningless. Here we explicitly return E_NOTIMPL. We plan to deprecate this |
534 | // function in Dev10 anyway. |
535 | // |
536 | // @dbgtodo Mac - Check with VS to see if they need the thread handle, e.g. for waiting on thread |
537 | // termination. |
538 | HRESULT hr = E_NOTIMPL; |
539 | #endif // !FEATURE_DBGIPC_TRANSPORT_DI |
540 | |
541 | return hr; |
542 | } |
543 | |
544 | // Note that we can return invalid handle |
545 | void CordbThread::InternalGetHandle(HANDLE * phThread) |
546 | { |
547 | INTERNAL_SYNC_API_ENTRY(GetProcess()); |
548 | |
549 | RefreshHandle(phThread); |
550 | } |
551 | |
552 | //--------------------------------------------------------------------------------------- |
553 | // |
554 | // This is a simple helper to check if a frame lives on the stack of the current thread. |
555 | // |
556 | // Arguments: |
557 | // pFrame - the stack frame to check |
558 | // |
559 | // Return Value: |
560 | // whether the frame lives on the stack of the current thread |
561 | // |
562 | // Assumption: |
563 | // This function assumes that the stack frames are valid, i.e. the stack frames have not been |
564 | // made dirty since the last stackwalk. |
565 | // |
566 | |
567 | bool CordbThread::OwnsFrame(CordbFrame * pFrame) |
568 | { |
569 | // preliminary checking |
570 | if ( (pFrame != NULL) && |
571 | (!pFrame->IsNeutered()) && |
572 | (pFrame->m_pThread == this) |
573 | ) |
574 | { |
575 | // |
576 | // Note that this is one of the two remaining places where we need to use the cached stack frames. |
577 | // Theoretically, since this is not an exact check anyway, we could just use the thread's stack |
578 | // range instead of looping through all the individual frames. However, since we need to maintain |
579 | // the stack frame cache for code:CordbThread::GetActiveFunctions, we might as well use the cache here. |
580 | // |
581 | |
582 | // make sure this thread actually have frames to check |
583 | if (m_stackFrames.Count() != 0) |
584 | { |
585 | // get the stack range of this thread |
586 | FramePointer fpLeaf = (*(m_stackFrames.Get(0)))->GetFramePointer(); |
587 | FramePointer fpRoot = (*(m_stackFrames.Get(m_stackFrames.Count() - 1)))->GetFramePointer(); |
588 | |
589 | FramePointer fpCurrent = pFrame->GetFramePointer(); |
590 | |
591 | // compare the stack range against the frame pointer of the specified frame |
592 | if (IsEqualOrCloserToLeaf(fpLeaf, fpCurrent) && IsEqualOrCloserToRoot(fpRoot, fpCurrent)) |
593 | { |
594 | return true; |
595 | } |
596 | } |
597 | } |
598 | |
599 | return false; |
600 | } |
601 | |
602 | //--------------------------------------------------------------------------------------- |
603 | // |
604 | // This routine is a internal helper function for ICorDebugThread2::GetTaskId. |
605 | // |
606 | // Arguments: |
607 | // pHandle - return thread handle here after fetching from the left side. Can return SWITCHOUT_HANDLE_VALUE. |
608 | // |
609 | // Return Value: |
610 | // hr - It can fail with CORDBG_E_THREAD_NOT_SCHEDULED. |
611 | // |
612 | // Notes: |
613 | // This method will most likely be deprecated in V3.0. We can't always return the thread handle. |
614 | // For example, what does it mean to return a thread handle in remote debugging scenarios? |
615 | // |
616 | void CordbThread::RefreshHandle(HANDLE * phThread) |
617 | { |
618 | // here is where we will put code in to fetch the thread handle from the left side. |
619 | // This should only happen when CLRTask is hosted. |
620 | // Make sure that we are setting the right HR when thread is being switched out. |
621 | THROW_IF_NEUTERED(this); |
622 | INTERNAL_SYNC_API_ENTRY(GetProcess()); |
623 | |
624 | if (phThread == NULL) |
625 | { |
626 | ThrowHR(E_INVALIDARG); |
627 | } |
628 | *phThread = INVALID_HANDLE_VALUE; |
629 | |
630 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
631 | HANDLE hThread = pDAC->GetThreadHandle(m_vmThreadToken); |
632 | |
633 | if (hThread == SWITCHOUT_HANDLE_VALUE) |
634 | { |
635 | *phThread = SWITCHOUT_HANDLE_VALUE; |
636 | ThrowHR(CORDBG_E_THREAD_NOT_SCHEDULED); |
637 | } |
638 | |
639 | _ASSERTE(hThread != INVALID_HANDLE_VALUE); |
640 | PREFAST_ASSUME(hThread != NULL); |
641 | |
642 | // need to dup handle here |
643 | if (hThread == m_hCachedOutOfProcThread) |
644 | { |
645 | *phThread = m_hCachedThread; |
646 | } |
647 | else |
648 | { |
649 | BOOL fSuccess = TRUE; |
650 | if (m_hCachedThread != INVALID_HANDLE_VALUE) |
651 | { |
652 | // clear the previous cache |
653 | CloseHandle(m_hCachedThread); |
654 | m_hCachedOutOfProcThread = INVALID_HANDLE_VALUE; |
655 | m_hCachedThread = INVALID_HANDLE_VALUE; |
656 | } |
657 | |
658 | // now duplicate the out-of-proc handle |
659 | fSuccess = DuplicateHandle(GetProcess()->UnsafeGetProcessHandle(), |
660 | hThread, |
661 | GetCurrentProcess(), |
662 | &m_hCachedThread, |
663 | NULL, |
664 | FALSE, |
665 | DUPLICATE_SAME_ACCESS); |
666 | *phThread = m_hCachedThread; |
667 | |
668 | if (fSuccess) |
669 | { |
670 | m_hCachedOutOfProcThread = hThread; |
671 | } |
672 | else |
673 | { |
674 | ThrowLastError(); |
675 | } |
676 | } |
677 | } // CordbThread::RefreshHandle |
678 | |
679 | |
680 | //--------------------------------------------------------------------------------------- |
681 | // |
682 | // This routine sets the debug state of a thread. |
683 | // |
684 | // Arguments: |
685 | // state - The debug state to set to. |
686 | // |
687 | // Return Value: |
688 | // Normal HRESULT semantics. |
689 | // |
690 | HRESULT CordbThread::SetDebugState(CorDebugThreadState state) |
691 | { |
692 | PUBLIC_API_ENTRY(this); |
693 | FAIL_IF_NEUTERED(this); |
694 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
695 | |
696 | LOG((LF_CORDB, LL_INFO1000, "CT::SDS: thread=0x%08x 0x%x, state=%d\n" , this, m_id, state)); |
697 | |
698 | // @dbgtodo- , sync - decide on how to suspend a thread. V2 leverages synchronization |
699 | // (see below). For V3, do we just hard suspend the thread? |
700 | if (GetProcess()->GetShim() == NULL) |
701 | { |
702 | return E_NOTIMPL; |
703 | } |
704 | |
705 | HRESULT hr = S_OK; |
706 | EX_TRY |
707 | { |
708 | hr = EnsureThreadIsAlive(); |
709 | |
710 | if (SUCCEEDED(hr)) |
711 | { |
712 | // This lets the debugger suspend / resume threads. This is only called when when the |
713 | // target is already synchronized. That means all the threads are already suspended. So |
714 | // setting the suspend bit here just means that the debugger's continue logic won't resume |
715 | // this thread when we do a Continue. |
716 | if ((state != THREAD_SUSPEND) && (state != THREAD_RUN)) |
717 | { |
718 | ThrowHR(E_INVALIDARG); |
719 | } |
720 | |
721 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
722 | pDAC->SetDebugState(m_vmThreadToken, state); |
723 | |
724 | m_debugState = state; |
725 | } |
726 | } |
727 | EX_CATCH_HRESULT(hr); |
728 | |
729 | return hr; |
730 | } |
731 | |
732 | HRESULT CordbThread::GetDebugState(CorDebugThreadState * pState) |
733 | { |
734 | PUBLIC_API_ENTRY(this); |
735 | FAIL_IF_NEUTERED(this); |
736 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
737 | VALIDATE_POINTER_TO_OBJECT(pState, CorDebugThreadState *); |
738 | |
739 | *pState = m_debugState; |
740 | |
741 | return S_OK; |
742 | } |
743 | |
744 | |
745 | // Public implementation of ICorDebugThread::GetUserState |
746 | // Arguments: |
747 | // pState - out parameter; return the user state |
748 | // |
749 | // Return Value: |
750 | // Return S_OK if the operation is successful. |
751 | // Return E_INVALIDARG if the out parameter is NULL. |
752 | // Return other failure HRs returned by the call to the DDI. |
753 | HRESULT CordbThread::GetUserState(CorDebugUserState * pState) |
754 | { |
755 | PUBLIC_REENTRANT_API_ENTRY(this); |
756 | FAIL_IF_NEUTERED(this); |
757 | VALIDATE_POINTER_TO_OBJECT(pState, CorDebugUserState *); |
758 | |
759 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
760 | |
761 | HRESULT hr = S_OK; |
762 | EX_TRY |
763 | { |
764 | if (pState == NULL) |
765 | { |
766 | ThrowHR(E_INVALIDARG); |
767 | } |
768 | *pState = GetUserState(); |
769 | } |
770 | EX_CATCH_HRESULT(hr); |
771 | return hr; |
772 | } |
773 | |
774 | //--------------------------------------------------------------------------------------- |
775 | // |
776 | // Retrieve the user state of the current thread. |
777 | // |
778 | // Notes: |
779 | // This caches results between continues. The cache is cleared when the target continues or is flushed. |
780 | // See code:CordbThread::CleanupStack, code:CordbThread::MarkStackFramesDirty |
781 | // |
782 | CorDebugUserState CordbThread::GetUserState() |
783 | { |
784 | if (m_userState == kInvalidUserState) |
785 | { |
786 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
787 | m_userState = pDAC->GetUserState(m_vmThreadToken); |
788 | } |
789 | |
790 | return m_userState; |
791 | } |
792 | |
793 | |
794 | //--------------------------------------------------------------------------------------- |
795 | // |
796 | // This routine finds and returns the current exception off of a thread. |
797 | // |
798 | // Arguments: |
799 | // ppExceptionObject - OUT: Space for storing the exception found on the thread as a value. |
800 | // |
801 | // Return Value: |
802 | // Normal HRESULT semantics. |
803 | // |
804 | HRESULT CordbThread::GetCurrentException(ICorDebugValue ** ppExceptionObject) |
805 | { |
806 | PUBLIC_API_ENTRY(this); |
807 | FAIL_IF_NEUTERED(this); |
808 | |
809 | HRESULT hr = S_OK; |
810 | |
811 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
812 | |
813 | VALIDATE_POINTER_TO_OBJECT(ppExceptionObject, ICorDebugValue **); |
814 | *ppExceptionObject = NULL; |
815 | |
816 | EX_TRY |
817 | { |
818 | if (!HasException()) |
819 | { |
820 | // |
821 | // Go to the LS and retrieve any exception object. |
822 | // |
823 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
824 | VMPTR_OBJECTHANDLE vmObjHandle = pDAC->GetCurrentException(m_vmThreadToken); |
825 | |
826 | if (vmObjHandle.IsNull()) |
827 | { |
828 | hr = S_FALSE; |
829 | } |
830 | else |
831 | { |
832 | #if defined(_DEBUG) |
833 | // Since we know an exception is in progress on this thread, our assumption about the |
834 | // thread's current AppDomain should be correct |
835 | VMPTR_AppDomain vmAppDomain = pDAC->GetCurrentAppDomain(m_vmThreadToken); |
836 | _ASSERTE(GetAppDomain()->GetADToken() == vmAppDomain); |
837 | #endif // _DEBUG |
838 | |
839 | m_vmExcepObjHandle = vmObjHandle; |
840 | } |
841 | } |
842 | |
843 | if (hr == S_OK) |
844 | { |
845 | // We've believe this assert may fire in the wild. |
846 | // We've seen m_vmExcepObjHandle null in retail builds after stack overflow. |
847 | _ASSERTE(!m_vmExcepObjHandle.IsNull()); |
848 | |
849 | ICorDebugReferenceValue * pRefValue = NULL; |
850 | hr = CordbReferenceValue::BuildFromGCHandle(GetAppDomain(), m_vmExcepObjHandle, &pRefValue); |
851 | *ppExceptionObject = pRefValue; |
852 | } |
853 | } |
854 | EX_CATCH_HRESULT(hr); |
855 | |
856 | return hr; |
857 | } |
858 | |
859 | HRESULT CordbThread::ClearCurrentException() |
860 | { |
861 | PUBLIC_API_ENTRY(this); |
862 | FAIL_IF_NEUTERED(this); |
863 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
864 | |
865 | // This API is not implemented. For Continuable Exceptions, see InterceptCurrentException. |
866 | // @todo - should it return E_NOTIMPL? |
867 | return S_OK; |
868 | } |
869 | |
870 | HRESULT CordbThread::CreateStepper(ICorDebugStepper ** ppStepper) |
871 | { |
872 | PUBLIC_API_ENTRY(this); |
873 | FAIL_IF_NEUTERED(this); |
874 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
875 | |
876 | VALIDATE_POINTER_TO_OBJECT(ppStepper, ICorDebugStepper **); |
877 | |
878 | CordbStepper * pStepper = new (nothrow) CordbStepper(this, NULL); |
879 | |
880 | if (pStepper == NULL) |
881 | { |
882 | return E_OUTOFMEMORY; |
883 | } |
884 | |
885 | pStepper->ExternalAddRef(); |
886 | *ppStepper = pStepper; |
887 | |
888 | return S_OK; |
889 | } |
890 | |
891 | //Returns true if current user state of a thread is USER_WAIT_SLEEP_JOIN |
892 | bool CordbThread::IsThreadWaitingOrSleeping() |
893 | { |
894 | CorDebugUserState userState = m_userState; |
895 | if (userState == kInvalidUserState) |
896 | { |
897 | //If m_userState is not ready, we'll read from DAC only part of it which |
898 | //is important for us now, bacuase we don't want possible side effects |
899 | //of reading USER_UNSAFE_POINT flag. |
900 | //We don't cache the value, because it's potentially incomplete. |
901 | IDacDbiInterface *pDAC = GetProcess()->GetDAC(); |
902 | userState = pDAC->GetPartialUserState(m_vmThreadToken); |
903 | } |
904 | |
905 | return (userState & USER_WAIT_SLEEP_JOIN) != 0; |
906 | } |
907 | |
908 | //---------------------------------------------------------------------------- |
909 | // check if the thread is dead |
910 | // |
911 | // Returns: true if the thread is dead. |
912 | // |
913 | bool CordbThread::IsThreadDead() |
914 | { |
915 | return GetProcess()->GetDAC()->IsThreadMarkedDead(m_vmThreadToken); |
916 | } |
917 | |
918 | // Helper to return CORDBG_E_BAD_THREAD_STATE if IsThreadDead |
919 | // |
920 | // Notes: |
921 | // IsThreadDead queries the VM Thread's actual state, regardless of what ExitThread |
922 | // callbacks have or have not been sent / queued / dispatched. |
923 | HRESULT CordbThread::EnsureThreadIsAlive() |
924 | { |
925 | if (IsThreadDead()) |
926 | { |
927 | return CORDBG_E_BAD_THREAD_STATE; |
928 | } |
929 | else |
930 | { |
931 | return S_OK; |
932 | } |
933 | } |
934 | |
935 | // ---------------------------------------------------------------------------- |
936 | // CordbThread::EnumerateChains |
937 | // |
938 | // Description: |
939 | // Create and return an ICDChainEnum for enumerating chains on the stack. Since chains have been |
940 | // deprecated in Arrowhead, this function returns E_NOTIMPL unless there is a shim. |
941 | // |
942 | // Arguments: |
943 | // * ppChains - out parameter; return the ICDChainEnum |
944 | // |
945 | // Return Value: |
946 | // Return S_OK on success. |
947 | // Return E_INVALIDARG if ppChains is NULL. |
948 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbThread is neutered. |
949 | // Return E_NOTIMPL if there is no shim. |
950 | // |
951 | |
952 | HRESULT CordbThread::EnumerateChains(ICorDebugChainEnum ** ppChains) |
953 | { |
954 | PUBLIC_API_ENTRY(this); |
955 | FAIL_IF_NEUTERED(this); |
956 | |
957 | VALIDATE_POINTER_TO_OBJECT(ppChains, ICorDebugChainEnum **); |
958 | |
959 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
960 | |
961 | HRESULT hr = S_OK; |
962 | EX_TRY |
963 | { |
964 | *ppChains = NULL; |
965 | |
966 | if (GetProcess()->GetShim() != NULL) |
967 | { |
968 | hr = EnsureThreadIsAlive(); |
969 | |
970 | if (SUCCEEDED(hr)) |
971 | { |
972 | // use the shim to create an ICDChainEnum |
973 | PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(GetProcess()); |
974 | ShimStackWalk * pSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(this); |
975 | pSW->EnumerateChains(ppChains); |
976 | } |
977 | } |
978 | else |
979 | { |
980 | // This is the Arrowhead case, where ICDChain has been deprecated. |
981 | hr = E_NOTIMPL; |
982 | } |
983 | } |
984 | EX_CATCH_HRESULT(hr); |
985 | |
986 | return hr; |
987 | } |
988 | |
989 | // ---------------------------------------------------------------------------- |
990 | // CordbThread::GetActiveChain |
991 | // |
992 | // Description: |
993 | // Retrieve the leaf chain on this thread. Since chains have been deprecated in Arrowhead, |
994 | // this function returns E_NOTIMPL unless there is a shim. |
995 | // |
996 | // Arguments: |
997 | // * ppChain - out parameter; return the leaf chain |
998 | // |
999 | // Return Value: |
1000 | // Return S_OK on success. |
1001 | // Return E_INVALIDARG if ppChain is NULL. |
1002 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbThread is neutered. |
1003 | // Return E_NOTIMPL if there is no shim. |
1004 | // |
1005 | |
1006 | HRESULT CordbThread::GetActiveChain(ICorDebugChain ** ppChain) |
1007 | { |
1008 | PUBLIC_REENTRANT_API_ENTRY(this); |
1009 | FAIL_IF_NEUTERED(this); |
1010 | |
1011 | VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **); |
1012 | |
1013 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1014 | |
1015 | HRESULT hr = S_OK; |
1016 | EX_TRY |
1017 | { |
1018 | *ppChain = NULL; |
1019 | hr = EnsureThreadIsAlive(); |
1020 | |
1021 | if (SUCCEEDED(hr)) |
1022 | { |
1023 | if (GetProcess()->GetShim() != NULL) |
1024 | { |
1025 | // use the shim to retrieve the leaf chain |
1026 | PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(GetProcess()); |
1027 | ShimStackWalk * pSSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(this); |
1028 | pSSW->GetActiveChain(ppChain); |
1029 | } |
1030 | else |
1031 | { |
1032 | // This is the Arrowhead case, where ICDChain has been deprecated. |
1033 | hr = E_NOTIMPL; |
1034 | } |
1035 | } |
1036 | } |
1037 | EX_CATCH_HRESULT(hr); |
1038 | return hr; |
1039 | } |
1040 | |
1041 | // ---------------------------------------------------------------------------- |
1042 | // CordbThread::GetActiveFrame |
1043 | // |
1044 | // Description: |
1045 | // Retrieve the leaf frame on this thread. Unfortunately, this is one of the cases where we need to |
1046 | // do different things depending on whether there is a shim. See the Notes below. |
1047 | // |
1048 | // Arguments: |
1049 | // * ppFrame - out parameter; return the leaf frame |
1050 | // |
1051 | // Return Value: |
1052 | // Return S_OK on success. |
1053 | // Return E_INVALIDARG if ppFrame is NULL. |
1054 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbThread is neutered. |
1055 | // Also return whatever CreateStackWalk() and GetFrame() return if they fail. |
1056 | // |
1057 | // Notes: |
1058 | // In V2, we return NULL if the leaf frame is not in the leaf chain, i.e. if the leaf chain is |
1059 | // empty. Note that managed chains are never empty. Also, in V2 it is possible that this API |
1060 | // will return an internal frame as the active frame on a thread. |
1061 | // |
1062 | // The Arrowhead implementation two breaking changes: |
1063 | // 1) It never returns an internal frame. |
1064 | // 2) We return a frame if the leaf frame is managed. Otherwise, we return NULL. |
1065 | // |
1066 | |
1067 | HRESULT CordbThread::GetActiveFrame(ICorDebugFrame ** ppFrame) |
1068 | { |
1069 | PUBLIC_REENTRANT_API_ENTRY(this); |
1070 | FAIL_IF_NEUTERED(this); |
1071 | |
1072 | VALIDATE_POINTER_TO_OBJECT(ppFrame, ICorDebugFrame **); |
1073 | |
1074 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1075 | |
1076 | HRESULT hr = S_OK; |
1077 | EX_TRY |
1078 | { |
1079 | *ppFrame = NULL; |
1080 | hr = EnsureThreadIsAlive(); |
1081 | |
1082 | if (SUCCEEDED(hr)) |
1083 | { |
1084 | if (GetProcess()->GetShim() != NULL) |
1085 | { |
1086 | PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(GetProcess()); |
1087 | ShimStackWalk * pSSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(this); |
1088 | pSSW->GetActiveFrame(ppFrame); |
1089 | } |
1090 | else |
1091 | { |
1092 | // This is the Arrowhead case. We could call RefreshStack() here, but since we only need the |
1093 | // leaf frame, there is no point in walking the entire stack. |
1094 | RSExtSmartPtr<ICorDebugStackWalk> pSW; |
1095 | hr = CreateStackWalk(&pSW); |
1096 | IfFailThrow(hr); |
1097 | |
1098 | hr = pSW->GetFrame(ppFrame); |
1099 | IfFailThrow(hr); |
1100 | } |
1101 | } |
1102 | } |
1103 | EX_CATCH_HRESULT(hr); |
1104 | return hr; |
1105 | } |
1106 | |
1107 | // ---------------------------------------------------------------------------- |
1108 | // CordbThread::GetActiveRegister |
1109 | // |
1110 | // Description: |
1111 | // In V2, retrieve the ICDRegisterSet for the leaf chain. In Arrowhead, retrieve the ICDRegisterSet |
1112 | // for the leaf CONTEXT. |
1113 | // |
1114 | // Arguments: |
1115 | // * ppRegisters - out parameter; return the ICDRegister |
1116 | // |
1117 | // Return Value: |
1118 | // Return S_OK on success. |
1119 | // Return E_INVALIDARG if ppFrame is NULL. |
1120 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbThread is neutered. |
1121 | // Also return whatever CreateStackWalk() and GetContext() return if they fail. |
1122 | // |
1123 | |
1124 | HRESULT CordbThread::GetRegisterSet(ICorDebugRegisterSet ** ppRegisters) |
1125 | { |
1126 | PUBLIC_API_ENTRY(this); |
1127 | FAIL_IF_NEUTERED(this); |
1128 | |
1129 | VALIDATE_POINTER_TO_OBJECT(ppRegisters, ICorDebugRegisterSet **); |
1130 | |
1131 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1132 | |
1133 | HRESULT hr = S_OK; |
1134 | EX_TRY |
1135 | { |
1136 | *ppRegisters = NULL; |
1137 | hr = EnsureThreadIsAlive(); |
1138 | |
1139 | if (SUCCEEDED(hr)) |
1140 | { |
1141 | if (GetProcess()->GetShim() != NULL) |
1142 | { |
1143 | // use the shim to retrieve the active ICDRegisterSet |
1144 | PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(GetProcess()); |
1145 | ShimStackWalk * pSSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(this); |
1146 | pSSW->GetActiveRegisterSet(ppRegisters); |
1147 | } |
1148 | else |
1149 | { |
1150 | // This is the Arrowhead case. We could call RefreshStack() here, but since we only need the |
1151 | // leaf frame, there is no point in walking the entire stack. |
1152 | RSExtSmartPtr<ICorDebugStackWalk> pSW; |
1153 | hr = CreateStackWalk(&pSW); |
1154 | IfFailThrow(hr); |
1155 | |
1156 | // retrieve the leaf CONTEXT |
1157 | DT_CONTEXT ctx; |
1158 | hr = pSW->GetContext(CONTEXT_FULL, sizeof(ctx), NULL, reinterpret_cast<BYTE *>(&ctx)); |
1159 | IfFailThrow(hr); |
1160 | |
1161 | // the CordbRegisterSet is responsible for freeing this memory |
1162 | NewHolder<DebuggerREGDISPLAY> pDRD(new DebuggerREGDISPLAY()); |
1163 | |
1164 | // convert the CONTEXT to a DebuggerREGDISPLAY |
1165 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
1166 | pDAC->ConvertContextToDebuggerRegDisplay(&ctx, pDRD, true); |
1167 | |
1168 | // create the CordbRegisterSet |
1169 | RSInitHolder<CordbRegisterSet> pRS(new CordbRegisterSet(pDRD, |
1170 | this, |
1171 | true, // active |
1172 | false, // !fQuickUnwind |
1173 | true)); // own DRD memory |
1174 | pDRD.SuppressRelease(); |
1175 | |
1176 | pRS.TransferOwnershipExternal(ppRegisters); |
1177 | } |
1178 | } |
1179 | } |
1180 | EX_CATCH_HRESULT(hr); |
1181 | return hr; |
1182 | } |
1183 | |
1184 | HRESULT CordbThread::CreateEval(ICorDebugEval ** ppEval) |
1185 | { |
1186 | PUBLIC_API_ENTRY(this); |
1187 | FAIL_IF_NEUTERED(this); |
1188 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1189 | |
1190 | VALIDATE_POINTER_TO_OBJECT(ppEval, ICorDebugEval **); |
1191 | |
1192 | CordbEval * pEval = new (nothrow) CordbEval(this); |
1193 | if (pEval == NULL) |
1194 | { |
1195 | return E_OUTOFMEMORY; |
1196 | } |
1197 | |
1198 | pEval->ExternalAddRef(); |
1199 | *ppEval = static_cast<ICorDebugEval *>(pEval); |
1200 | |
1201 | return S_OK; |
1202 | } |
1203 | |
1204 | // DAC check |
1205 | |
1206 | // Double check our results w/ DAC. |
1207 | // This gives DAC some great coverage. |
1208 | // Given an IP and the md token (that the RS obtained), use DAC to lookup the md token. Then |
1209 | // we can compare DAC & the RS and make sure DACs working. |
1210 | void CheckAgainstDAC(CordbFunction * pFunc, void * pIP, mdMethodDef mdExpected) |
1211 | { |
1212 | // This is a hook to add DAC checks against a {function, ip} |
1213 | } |
1214 | |
1215 | |
1216 | //--------------------------------------------------------------------------------------- |
1217 | // |
1218 | // Internal function to build up a stack trace. |
1219 | // |
1220 | // |
1221 | // Return Value: |
1222 | // S_OK on success. |
1223 | // |
1224 | // Assumptions: |
1225 | // Process is stopped. |
1226 | // |
1227 | // Notes: |
1228 | // Send a IPC events to the LS to build up the stack. |
1229 | // |
1230 | //--------------------------------------------------------------------------------------- |
1231 | void CordbThread::RefreshStack() |
1232 | { |
1233 | THROW_IF_NEUTERED(this); |
1234 | |
1235 | // We must have the Stop-Go lock to change our thread's stack-state. |
1236 | // Also, our caller should have guaranteed that we're synced. And b/c we hold the stop-go lock, |
1237 | // that shouldn't have changed. |
1238 | // INTERNAL_SYNC_API_ENTRY() checks that we have the lock and that we are synced. |
1239 | INTERNAL_SYNC_API_ENTRY(GetProcess()); |
1240 | |
1241 | // bail out early if the stack hasn't changed |
1242 | if (m_fFramesFresh) |
1243 | { |
1244 | return; |
1245 | } |
1246 | |
1247 | HRESULT hr = S_OK; |
1248 | |
1249 | // |
1250 | // Clean up old snapshot. |
1251 | // |
1252 | |
1253 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
1254 | |
1255 | // clear the stack frame cache |
1256 | ClearStackFrameCache(); |
1257 | |
1258 | // |
1259 | // If we don't have a debugger thread token, then this thread has never |
1260 | // executed managed code and we have no frame information for it. |
1261 | // |
1262 | if (m_vmThreadToken.IsNull()) |
1263 | { |
1264 | ThrowHR(E_FAIL); |
1265 | } |
1266 | |
1267 | // walk the stack using the V3 API and populate the stack frame cache |
1268 | RSInitHolder<CordbStackWalk> pSW(new CordbStackWalk(this)); |
1269 | pSW->Init(); |
1270 | do |
1271 | { |
1272 | RSExtSmartPtr<ICorDebugFrame> pIFrame; |
1273 | hr = pSW->GetFrame(&pIFrame); |
1274 | IfFailThrow(hr); |
1275 | |
1276 | if (pIFrame != NULL) |
1277 | { |
1278 | // add the stack frame to the cache |
1279 | CordbFrame ** ppCFrame = m_stackFrames.AppendThrowing(); |
1280 | *ppCFrame = CordbFrame::GetCordbFrameFromInterface(pIFrame); |
1281 | |
1282 | // Now that we have saved the pointer, increment the ref count. |
1283 | // This has to match the InternalRelease() in code:CordbThread::ClearStackFrameCache. |
1284 | (*ppCFrame)->InternalAddRef(); |
1285 | } |
1286 | |
1287 | // advance to the next frame |
1288 | hr = pSW->Next(); |
1289 | IfFailThrow(hr); |
1290 | } |
1291 | while (hr != CORDBG_S_AT_END_OF_STACK); |
1292 | |
1293 | m_fFramesFresh = true; |
1294 | } |
1295 | |
1296 | |
1297 | //--------------------------------------------------------------------------------------- |
1298 | // |
1299 | // This function is used to invalidate and clean up the cached stack trace. |
1300 | // |
1301 | |
1302 | void CordbThread::CleanupStack() |
1303 | { |
1304 | _ASSERTE(GetProcess()->GetProcessLock()->HasLock()); |
1305 | |
1306 | // Neuter outstanding CordbChainEnums, CordbFrameEnums, some CordbTypeEnums, and some CordbValueEnums. |
1307 | m_RefreshStackNeuterList.NeuterAndClear(GetProcess()); |
1308 | |
1309 | m_fContextFresh = false; // invalidate the cached active CONTEXT |
1310 | m_vmLeftSideContext = VMPTR_CONTEXT::NullPtr(); // set the LS pointer to the active CONTEXT to NULL |
1311 | m_fFramesFresh = false; // invalidate the cached stack trace (frames & chains) |
1312 | m_userState = kInvalidUserState; // clear the cached user state |
1313 | |
1314 | // tell the shim to flush its caches as well |
1315 | if (GetProcess()->GetShim() != NULL) |
1316 | { |
1317 | GetProcess()->GetShim()->NotifyOnStackInvalidate(); |
1318 | } |
1319 | } |
1320 | |
1321 | // Notifying the thread that the process is being continued. |
1322 | // This will cause our caches to get invalidated without actually cleaning the caches. |
1323 | void CordbThread::MarkStackFramesDirty() |
1324 | { |
1325 | LIMITED_METHOD_CONTRACT; |
1326 | |
1327 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
1328 | |
1329 | // invalidate the cached floating point state |
1330 | m_fFloatStateValid = false; |
1331 | |
1332 | // This flag is only true between the window when we get an exception callback and |
1333 | // when we call continue. Since this function is only called when we continue, we |
1334 | // need to reset this flag here. Note that in the case of an outstanding funceval, |
1335 | // we'll set this flag again when the funceval is completed. |
1336 | m_fException = false; |
1337 | |
1338 | // Clear the stashed EnC remap IP address if any |
1339 | // This is important to ensure we don't try to write into LS memory which is no longer |
1340 | // being used to hold the remap IP. |
1341 | m_EnCRemapFunctionIP = NULL; |
1342 | |
1343 | m_fContextFresh = false; // invalidate the cached active CONTEXT |
1344 | m_vmLeftSideContext = VMPTR_CONTEXT::NullPtr(); // set the LS pointer to the active CONTEXT to NULL |
1345 | m_fFramesFresh = false; // invalidate the cached stack trace (frames & chains) |
1346 | m_userState = kInvalidUserState; // clear the cached user state |
1347 | |
1348 | m_RefreshStackNeuterList.NeuterAndClear(GetProcess()); |
1349 | |
1350 | // tell the shim to flush its caches as well |
1351 | if (GetProcess()->GetShim() != NULL) |
1352 | { |
1353 | GetProcess()->GetShim()->NotifyOnStackInvalidate(); |
1354 | } |
1355 | } |
1356 | |
1357 | // Set that there's an outstanding exception on this thread. |
1358 | // This can be called when the process object receives an exception notification. |
1359 | // This is cleared in code:CordbThread::MarkStackFramesDirty. |
1360 | void CordbThread::SetExInfo(VMPTR_OBJECTHANDLE vmExcepObjHandle) |
1361 | { |
1362 | m_fException = true; |
1363 | m_vmExcepObjHandle = vmExcepObjHandle; |
1364 | |
1365 | // CordbThread::GetCurrentException assumes that we always have a m_vmExcepObjHandle when at an exception. |
1366 | // Push that assert up here. |
1367 | _ASSERTE(!m_vmExcepObjHandle.IsNull()); |
1368 | } |
1369 | |
1370 | |
1371 | // ---------------------------------------------------------------------------- |
1372 | // CordbThread::FindFrame |
1373 | // |
1374 | // Description: |
1375 | // Given a FramePointer, find the matching CordbFrame. |
1376 | // |
1377 | // Arguments: |
1378 | // * ppFrame - out parameter; the CordbFrame to be returned |
1379 | // * fp - the input FramePointer |
1380 | // |
1381 | // Return Value: |
1382 | // Return S_OK on success. |
1383 | // Return E_FAIL on failure. |
1384 | // |
1385 | // Assumptions: |
1386 | // * This function is only called from the shim. |
1387 | // |
1388 | // Notes: |
1389 | // * Currently this function is only used by the shim to map the FramePointer it gets via the |
1390 | // DB_IPCE_EXCEPTION_CALLBACK2 callback. When we figure out what to do with the |
1391 | // DB_IPCE_EXCEPTION_CALLBACK2, we should remove this function. |
1392 | // |
1393 | |
1394 | HRESULT CordbThread::FindFrame(ICorDebugFrame ** ppFrame, FramePointer fp) |
1395 | { |
1396 | FAIL_IF_NEUTERED(this); |
1397 | |
1398 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1399 | |
1400 | _ASSERTE(ppFrame != NULL); |
1401 | *ppFrame = NULL; |
1402 | |
1403 | _ASSERTE(GetProcess()->GetShim() != NULL); |
1404 | |
1405 | PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(GetProcess()); |
1406 | ShimStackWalk * pSSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(this); |
1407 | |
1408 | for (UINT32 i = 0; i < pSSW->GetFrameCount(); i++) |
1409 | { |
1410 | ICorDebugFrame * pIFrame = pSSW->GetFrame(i); |
1411 | CordbFrame * pCFrame = CordbFrame::GetCordbFrameFromInterface(pIFrame); |
1412 | |
1413 | #if defined(_WIN64) |
1414 | // On 64-bit we can simply compare the FramePointer. |
1415 | if (pCFrame->GetFramePointer() == fp) |
1416 | #else // !_WIN64 |
1417 | // On other platforms, we need to do a more elaborate check. |
1418 | if (pCFrame->IsContainedInFrame(fp)) |
1419 | #endif // _WIN64 |
1420 | { |
1421 | *ppFrame = pIFrame; |
1422 | (*ppFrame)->AddRef(); |
1423 | return S_OK; |
1424 | } |
1425 | } |
1426 | |
1427 | // Cannot find the frame. |
1428 | return E_FAIL; |
1429 | } |
1430 | |
1431 | |
1432 | |
1433 | #if defined(CROSS_COMPILE) && (defined(_TARGET_ARM64_) || defined(_TARGET_ARM_)) |
1434 | extern "C" double FPFillR8(void* pFillSlot) |
1435 | { |
1436 | _ASSERTE(!"nyi for platform" ); |
1437 | return 0; |
1438 | } |
1439 | #elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_ARM_) |
1440 | extern "C" double FPFillR8(void* pFillSlot); |
1441 | #endif |
1442 | |
1443 | |
1444 | #if defined(_TARGET_X86_) |
1445 | |
1446 | // CordbThread::Get32bitFPRegisters |
1447 | // Converts the values in the floating point register area of the context to real number values. See |
1448 | // code:CordbThread::LoadFloatState for more details. |
1449 | // Arguments: |
1450 | // input: pContext |
1451 | // output: none (initializes m_floatValues) |
1452 | |
1453 | void CordbThread::Get32bitFPRegisters(CONTEXT * pContext) |
1454 | { |
1455 | // On X86, we get the values by saving our current FPU state, loading |
1456 | // the other thread's FPU state into our own, saving out each |
1457 | // value off the FPU stack, and then restoring our FPU state. |
1458 | // |
1459 | FLOATING_SAVE_AREA floatarea = pContext->FloatSave; // copy FloatSave |
1460 | |
1461 | // |
1462 | // Take the TOP out of the FPU status word. Note, our version of the |
1463 | // stack runs from 0->7, not 7->0... |
1464 | // |
1465 | unsigned int floatStackTop = 7 - ((floatarea.StatusWord & 0x3800) >> 11); |
1466 | |
1467 | FLOATING_SAVE_AREA currentFPUState; |
1468 | |
1469 | #ifdef _MSC_VER |
1470 | __asm fnsave currentFPUState // save the current FPU state. |
1471 | #else |
1472 | __asm__ __volatile__ |
1473 | ( |
1474 | " fnsave %0\n" \ |
1475 | : "=m" (currentFPUState) |
1476 | ); |
1477 | #endif |
1478 | |
1479 | floatarea.StatusWord &= 0xFF00; // remove any error codes. |
1480 | floatarea.ControlWord |= 0x3F; // mask all exceptions. |
1481 | |
1482 | // the x86 FPU stores real numbers as 10 byte values in IEEE format. Here we use |
1483 | // the hardware to convert these to doubles. |
1484 | |
1485 | // @dbgtodo Microsoft crossplat: the conversion from a series of bytes to a floating |
1486 | // point value will need to be done with an explicit conversion routine to unpack |
1487 | // the IEEE format and compute the real number value represented. |
1488 | |
1489 | #ifdef _MSC_VER |
1490 | __asm |
1491 | { |
1492 | fninit |
1493 | frstor floatarea ;; reload the threads FPU state. |
1494 | } |
1495 | #else |
1496 | __asm__ |
1497 | ( |
1498 | " fninit\n" \ |
1499 | " frstor %0\n" \ |
1500 | : /* no outputs */ |
1501 | : "m" (floatarea) |
1502 | ); |
1503 | #endif |
1504 | |
1505 | unsigned int i; |
1506 | |
1507 | for (i = 0; i <= floatStackTop; i++) |
1508 | { |
1509 | double td = 0.0; |
1510 | __asm fstp td // copy out the double |
1511 | m_floatValues[i] = td; |
1512 | } |
1513 | |
1514 | #ifdef _MSC_VER |
1515 | __asm |
1516 | { |
1517 | fninit |
1518 | frstor currentFPUState ;; restore our saved FPU state. |
1519 | } |
1520 | #else |
1521 | __asm__ |
1522 | ( |
1523 | " fninit\n" \ |
1524 | " frstor %0\n" \ |
1525 | : /* no outputs */ |
1526 | : "m" (currentFPUState) |
1527 | ); |
1528 | #endif |
1529 | |
1530 | m_fFloatStateValid = true; |
1531 | m_floatStackTop = floatStackTop; |
1532 | } // CordbThread::Get32bitFPRegisters |
1533 | |
1534 | #elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_ARM_) |
1535 | |
1536 | // CordbThread::Get64bitFPRegisters |
1537 | // Converts the values in the floating point register area of the context to real number values. See |
1538 | // code:CordbThread::LoadFloatState for more details. |
1539 | // Arguments: |
1540 | // input: pFPRegisterBase - starting address of the floating point register storage of the CONTEXT |
1541 | // registerSize - the size of a floating point register |
1542 | // start - the index into m_floatValues where we start initializing. For amd64, we start |
1543 | // at the beginning, but for ia64, the first two registers have fixed values, |
1544 | // so we start at two. |
1545 | // nRegisters - the number of registers to be initialized |
1546 | // output: none (initializes m_floatValues) |
1547 | |
1548 | void CordbThread::Get64bitFPRegisters(FPRegister64 * rgContextFPRegisters, int start, int nRegisters) |
1549 | { |
1550 | // make sure no one has changed the type definition for 64-bit FP registers |
1551 | _ASSERTE(sizeof(FPRegister64) == 16); |
1552 | // We convert and copy all the fp registers. |
1553 | for (int reg = start; reg < nRegisters; reg++) |
1554 | { |
1555 | // @dbgtodo Microsoft crossplat: the conversion from a FLOAT128 or M128A struct to a floating |
1556 | // point value will need to be done with an explicit conversion routine instead |
1557 | // of the call to FPFillR8 |
1558 | m_floatValues[reg] = FPFillR8(&rgContextFPRegisters[reg - start]); |
1559 | } |
1560 | } // CordbThread::Get64bitFPRegisters |
1561 | |
1562 | #endif // _TARGET_X86_ |
1563 | |
1564 | // CordbThread::LoadFloatState |
1565 | // Initializes the float state members of this instance of CordbThread. This function gets the context and |
1566 | // converts the floating point values from their context representation to a real number value. Floating |
1567 | // point numbers are represented in IEEE format on all current platforms. We store them in the context as a |
1568 | // pair of 64-bit integers (IA64 and AMD64) or a series of bytes (x86). Rather than unpack them explicitly |
1569 | // and do the appropriate mathematical operations to produce the corresponding floating point value, we let |
1570 | // the hardware do it instead. We load a floating point register with the representation from the context |
1571 | // and then store it in m_floatValues. Using the hardware is obviously a huge perf win. If/when we make |
1572 | // cross-plat work, we should at least code necessary conversion routines in assembly. Even with cross-plat, |
1573 | // we can probably still use the hardware in most cases, as long as the size is appropriate. |
1574 | // |
1575 | // Arguments: none |
1576 | // Return Value: none (initializes data members) |
1577 | // Note: Throws |
1578 | |
1579 | void CordbThread::LoadFloatState() |
1580 | { |
1581 | THROW_IF_NEUTERED(this); |
1582 | INTERNAL_SYNC_API_ENTRY(GetProcess()); |
1583 | |
1584 | DT_CONTEXT tempContext; |
1585 | GetProcess()->GetDAC()->GetContext(m_vmThreadToken, &tempContext); |
1586 | |
1587 | #if defined(_TARGET_X86_) |
1588 | Get32bitFPRegisters((CONTEXT*) &tempContext); |
1589 | #elif defined(_TARGET_AMD64_) |
1590 | // we have no fixed-value registers, so we begin with the first one and initialize all 16 |
1591 | Get64bitFPRegisters((FPRegister64*) &(tempContext.Xmm0), 0, 16); |
1592 | #elif defined(_TARGET_ARM64_) |
1593 | Get64bitFPRegisters((FPRegister64*) &(tempContext.V), 0, 32); |
1594 | #elif defined (_TARGET_ARM_) |
1595 | Get64bitFPRegisters((FPRegister64*) &(tempContext.D), 0, 32); |
1596 | #else |
1597 | _ASSERTE(!"nyi for platform" ); |
1598 | #endif // !_TARGET_X86_ |
1599 | |
1600 | m_fFloatStateValid = true; |
1601 | } // CordbThread::LoadFloatState |
1602 | |
1603 | |
1604 | const bool SetIP_fCanSetIPOnly = TRUE; |
1605 | const bool SetIP_fSetIP = FALSE; |
1606 | |
1607 | const bool SetIP_fIL = TRUE; |
1608 | const bool SetIP_fNative = FALSE; |
1609 | |
1610 | //--------------------------------------------------------------------------------------- |
1611 | // |
1612 | // Issues a SetIP command to the left-side and returns the result |
1613 | // |
1614 | // Arguments: |
1615 | // fCanSetIPOnly - TRUE if only to do the setip command and not refresh stacks as well. |
1616 | // debuggerModule - LS token to the debugger module. |
1617 | // mdMethod - Metadata token for the method. |
1618 | // nativeCodeJITInfoToken - LS token to the DebuggerJitInfo for the method. |
1619 | // offset - Offset within the method to set the IP to. |
1620 | // fIsIl - Is this an IL offset? |
1621 | // |
1622 | // Return Value: |
1623 | // S_OK on success. |
1624 | // |
1625 | HRESULT CordbThread::SetIP(bool fCanSetIPOnly, |
1626 | CordbNativeCode * pNativeCode, |
1627 | SIZE_T offset, |
1628 | bool fIsIL) |
1629 | { |
1630 | PUBLIC_REENTRANT_API_ENTRY(this); |
1631 | FAIL_IF_NEUTERED(this); |
1632 | |
1633 | |
1634 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1635 | |
1636 | VMPTR_DomainFile vmDomainFile = pNativeCode->GetModule()->m_vmDomainFile; |
1637 | _ASSERTE(!vmDomainFile.IsNull()); |
1638 | |
1639 | // If this thread is stopped due to an exception, never allow SetIP |
1640 | if (HasException()) |
1641 | { |
1642 | return (CORDBG_E_SET_IP_NOT_ALLOWED_ON_EXCEPTION); |
1643 | } |
1644 | |
1645 | DebuggerIPCEvent event; |
1646 | GetProcess()->InitIPCEvent(&event, DB_IPCE_SET_IP, true, GetAppDomain()->GetADToken()); |
1647 | event.SetIP.fCanSetIPOnly = fCanSetIPOnly; |
1648 | event.SetIP.vmThreadToken = m_vmThreadToken; |
1649 | event.SetIP.vmDomainFile = vmDomainFile; |
1650 | event.SetIP.mdMethod = pNativeCode->GetMetadataToken(); |
1651 | event.SetIP.vmMethodDesc = pNativeCode->GetVMNativeCodeMethodDescToken(); |
1652 | event.SetIP.startAddress = pNativeCode->GetAddress(); |
1653 | event.SetIP.offset = offset; |
1654 | event.SetIP.fIsIL = fIsIL; |
1655 | |
1656 | |
1657 | LOG((LF_CORDB, LL_INFO10000, "[%x] CT::SIP: Info:thread:0x%x" |
1658 | "mod:0x%x MethodDef:0x%x offset:0x%x il?:0x%x\n" , |
1659 | GetCurrentThreadId(), |
1660 | VmPtrToCookie(m_vmThreadToken), |
1661 | VmPtrToCookie(vmDomainFile), |
1662 | pNativeCode->GetMetadataToken(), |
1663 | offset, |
1664 | fIsIL)); |
1665 | |
1666 | LOG((LF_CORDB, LL_INFO10000, "[%x] CT::SIP: sizeof(DebuggerIPCEvent):0x%x **********\n" , |
1667 | sizeof(DebuggerIPCEvent))); |
1668 | |
1669 | HRESULT hr = GetProcess()->m_cordb->SendIPCEvent(GetProcess(), &event, sizeof(DebuggerIPCEvent)); |
1670 | |
1671 | if (FAILED(hr)) |
1672 | { |
1673 | return hr; |
1674 | } |
1675 | |
1676 | _ASSERTE(event.type == DB_IPCE_SET_IP); |
1677 | |
1678 | if (!fCanSetIPOnly && SUCCEEDED(event.hr)) |
1679 | { |
1680 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
1681 | CleanupStack(); |
1682 | } |
1683 | |
1684 | return ErrWrapper(event.hr); |
1685 | } |
1686 | |
1687 | // Get the context from a thread in managed code. |
1688 | // This thread should be stopped gracefully by the LS in managed code. |
1689 | HRESULT CordbThread::GetManagedContext(DT_CONTEXT ** ppContext) |
1690 | { |
1691 | FAIL_IF_NEUTERED(this); |
1692 | INTERNAL_SYNC_API_ENTRY(GetProcess()); |
1693 | |
1694 | if (ppContext == NULL) |
1695 | { |
1696 | ThrowHR(E_INVALIDARG); |
1697 | } |
1698 | |
1699 | *ppContext = NULL; |
1700 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1701 | |
1702 | // Each CordbThread object allocates the m_pContext's DT_CONTEXT structure only once, the first time GetContext is |
1703 | // invoked. |
1704 | if(m_pContext == NULL) |
1705 | { |
1706 | // Throw if the allocation fails. |
1707 | m_pContext = reinterpret_cast<DT_CONTEXT *>(new BYTE[sizeof(DT_CONTEXT)]); |
1708 | } |
1709 | |
1710 | HRESULT hr = S_OK; |
1711 | |
1712 | if (m_fContextFresh == false) |
1713 | { |
1714 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
1715 | m_vmLeftSideContext = pDAC->GetManagedStoppedContext(m_vmThreadToken); |
1716 | |
1717 | if (m_vmLeftSideContext.IsNull()) |
1718 | { |
1719 | // We don't have a context in managed code. |
1720 | ThrowHR(CORDBG_E_CONTEXT_UNVAILABLE); |
1721 | } |
1722 | else |
1723 | { |
1724 | LOG((LF_CORDB, LL_INFO1000, "CT::GC: getting context from left side pointer.\n" )); |
1725 | |
1726 | // The thread we're examining IS handling an exception, So grab the CONTEXT of the exception, NOT the |
1727 | // currently executing thread's CONTEXT (which would be the context of the exception handler.) |
1728 | hr = GetProcess()->SafeReadThreadContext(m_vmLeftSideContext.ToLsPtr(), m_pContext); |
1729 | IfFailThrow(hr); |
1730 | } |
1731 | |
1732 | // m_fContextFresh should be marked false when CleanupStack, MarkAllFramesAsDirty, etc get called. |
1733 | m_fContextFresh = true; |
1734 | } |
1735 | |
1736 | _ASSERTE(SUCCEEDED(hr)); |
1737 | (*ppContext) = m_pContext; |
1738 | |
1739 | return hr; |
1740 | } |
1741 | |
1742 | HRESULT CordbThread::SetManagedContext(DT_CONTEXT * pContext) |
1743 | { |
1744 | INTERNAL_API_ENTRY(this); |
1745 | FAIL_IF_NEUTERED(this); |
1746 | |
1747 | if(pContext == NULL) |
1748 | { |
1749 | ThrowHR(E_INVALIDARG); |
1750 | } |
1751 | |
1752 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1753 | |
1754 | HRESULT hr = S_OK; |
1755 | |
1756 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
1757 | m_vmLeftSideContext = pDAC->GetManagedStoppedContext(m_vmThreadToken); |
1758 | |
1759 | if (m_vmLeftSideContext.IsNull()) |
1760 | { |
1761 | ThrowHR(CORDBG_E_CONTEXT_UNVAILABLE); |
1762 | } |
1763 | else |
1764 | { |
1765 | // The thread we're examining IS handling an exception, So set the CONTEXT of the exception, NOT the currently |
1766 | // executing thread's CONTEXT (which would be the context of the exception handler.) |
1767 | // |
1768 | // Note: we read the remote context and merge the new one in, then write it back. This ensures that we don't |
1769 | // write too much information into the remote process. |
1770 | DT_CONTEXT tempContext = { 0 }; |
1771 | hr = GetProcess()->SafeReadThreadContext(m_vmLeftSideContext.ToLsPtr(), &tempContext); |
1772 | IfFailThrow(hr); |
1773 | |
1774 | CORDbgCopyThreadContext(&tempContext, pContext); |
1775 | |
1776 | hr = GetProcess()->SafeWriteThreadContext(m_vmLeftSideContext.ToLsPtr(), &tempContext); |
1777 | IfFailThrow(hr); |
1778 | |
1779 | // @todo - who's updating the regdisplay to guarantee that's in sync w/ our new context? |
1780 | } |
1781 | |
1782 | _ASSERTE(SUCCEEDED(hr)); |
1783 | if (m_fContextFresh && (m_pContext != NULL)) |
1784 | { |
1785 | *m_pContext = *pContext; |
1786 | } |
1787 | |
1788 | return hr; |
1789 | } |
1790 | |
1791 | |
1792 | HRESULT CordbThread::GetAppDomain(ICorDebugAppDomain ** ppAppDomain) |
1793 | { |
1794 | // We don't use the cached m_pAppDomain pointer here because it might be incorrect |
1795 | // if the thread has transitioned to another domain but we haven't received any events |
1796 | // from it yet. So we need to ask the left-side for the current domain. |
1797 | HRESULT hr = S_OK; |
1798 | PUBLIC_API_BEGIN(this); |
1799 | { |
1800 | ValidateOrThrow(ppAppDomain); |
1801 | *ppAppDomain = NULL; |
1802 | hr = EnsureThreadIsAlive(); |
1803 | |
1804 | if (SUCCEEDED(hr)) |
1805 | { |
1806 | CordbAppDomain * pAppDomain = NULL; |
1807 | hr = GetCurrentAppDomain(&pAppDomain); |
1808 | IfFailThrow(hr); |
1809 | _ASSERTE( pAppDomain != NULL ); |
1810 | |
1811 | *ppAppDomain = static_cast<ICorDebugAppDomain *> (pAppDomain); |
1812 | pAppDomain->ExternalAddRef(); |
1813 | } |
1814 | } |
1815 | PUBLIC_API_END(hr); |
1816 | return hr; |
1817 | } |
1818 | |
1819 | //--------------------------------------------------------------------------------------- |
1820 | // |
1821 | // Issues a get appdomain command and returns it. |
1822 | // |
1823 | // Arguments: |
1824 | // ppAppDomain - OUT: Space for storing the app domain of this thread. |
1825 | // |
1826 | // Return Value: |
1827 | // S_OK on success. |
1828 | // |
1829 | HRESULT CordbThread::GetCurrentAppDomain(CordbAppDomain ** ppAppDomain) |
1830 | { |
1831 | FAIL_IF_NEUTERED(this); |
1832 | INTERNAL_API_ENTRY(GetProcess()); |
1833 | |
1834 | *ppAppDomain = NULL; |
1835 | |
1836 | HRESULT hr = S_OK; |
1837 | EX_TRY |
1838 | { |
1839 | // @dbgtodo ICDThread - push this up |
1840 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
1841 | |
1842 | hr = EnsureThreadIsAlive(); |
1843 | |
1844 | if (SUCCEEDED(hr)) |
1845 | { |
1846 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
1847 | VMPTR_AppDomain vmAppDomain = pDAC->GetCurrentAppDomain(m_vmThreadToken); |
1848 | |
1849 | CordbAppDomain * pAppDomain = GetProcess()->LookupOrCreateAppDomain(vmAppDomain); |
1850 | _ASSERTE(pAppDomain != NULL); // we should be aware of all AppDomains |
1851 | |
1852 | *ppAppDomain = pAppDomain; |
1853 | } |
1854 | } |
1855 | EX_CATCH_HRESULT(hr); |
1856 | |
1857 | return S_OK; |
1858 | } |
1859 | |
1860 | //--------------------------------------------------------------------------------------- |
1861 | // |
1862 | // Issues a get_object command and returns the thread object as a value. |
1863 | // |
1864 | // Arguments: |
1865 | // ppThreadObject - OUT: Space for storing the thread object of this thread as a value |
1866 | // |
1867 | // Return Value: |
1868 | // S_OK on success. |
1869 | // |
1870 | HRESULT CordbThread::GetObject(ICorDebugValue ** ppThreadObject) |
1871 | { |
1872 | PUBLIC_API_ENTRY(this); |
1873 | FAIL_IF_NEUTERED(this); |
1874 | |
1875 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1876 | |
1877 | VALIDATE_POINTER_TO_OBJECT(ppThreadObject, ICorDebugObjectValue **); |
1878 | |
1879 | // Default to NULL |
1880 | *ppThreadObject = NULL; |
1881 | |
1882 | HRESULT hr = S_OK; |
1883 | EX_TRY |
1884 | { |
1885 | // @dbgtodo ICDThread - push this up |
1886 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
1887 | |
1888 | hr = EnsureThreadIsAlive(); |
1889 | |
1890 | if (SUCCEEDED(hr)) |
1891 | { |
1892 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
1893 | VMPTR_OBJECTHANDLE vmObjHandle = pDAC->GetThreadObject(m_vmThreadToken); |
1894 | if (vmObjHandle.IsNull()) |
1895 | { |
1896 | ThrowHR(E_FAIL); |
1897 | } |
1898 | |
1899 | // We create the object relative to the current AppDomain of the thread |
1900 | // Thread objects aren't really agile (eg. their m_Context field is domain-bound and |
1901 | // fixed up manually during transitions). This means that a thread object can only |
1902 | // be used in the domain the thread was in when the object was created. |
1903 | VMPTR_AppDomain vmAppDomain = pDAC->GetCurrentAppDomain(m_vmThreadToken); |
1904 | |
1905 | CordbAppDomain * pThreadCurrentDomain = NULL; |
1906 | pThreadCurrentDomain = GetProcess()->m_appDomains.GetBaseOrThrow(VmPtrToCookie(vmAppDomain)); |
1907 | _ASSERTE(pThreadCurrentDomain != NULL); // we should be aware of all AppDomains |
1908 | |
1909 | if (pThreadCurrentDomain == NULL) |
1910 | { |
1911 | // fall back to some domain to avoid crashes in retail - |
1912 | // safe enough for getting the name of the thread etc. |
1913 | pThreadCurrentDomain = GetProcess()->GetDefaultAppDomain(); |
1914 | } |
1915 | |
1916 | lockHolder.Release(); |
1917 | |
1918 | ICorDebugReferenceValue * pRefValue = NULL; |
1919 | hr = CordbReferenceValue::BuildFromGCHandle(pThreadCurrentDomain, vmObjHandle, &pRefValue); |
1920 | *ppThreadObject = pRefValue; |
1921 | } |
1922 | } |
1923 | EX_CATCH_HRESULT(hr); |
1924 | |
1925 | // Don't return a null pointer with S_OK. |
1926 | _ASSERTE((hr != S_OK) || (*ppThreadObject != NULL)); |
1927 | return hr; |
1928 | } |
1929 | |
1930 | /* |
1931 | * |
1932 | * GetActiveFunctions |
1933 | * |
1934 | * This routine is the interface function for ICorDebugThread2::GetActiveFunctions. |
1935 | * |
1936 | * Parameters: |
1937 | * cFunctions - the count of the number of COR_ACTIVE_FUNCTION in pFunctions. Zero |
1938 | * indicates no pFunctions buffer. |
1939 | * pcFunctions - pointer to storage for the count of elements filled in to pFunctions, or |
1940 | * count that would be needed to fill pFunctions, if cFunctions is 0. |
1941 | * pFunctions - buffer to store results. May be NULL. |
1942 | * |
1943 | * Return Value: |
1944 | * HRESULT from the helper routine. |
1945 | * |
1946 | */ |
1947 | |
1948 | HRESULT CordbThread::GetActiveFunctions( |
1949 | ULONG32 cFunctions, |
1950 | ULONG32 * pcFunctions, |
1951 | COR_ACTIVE_FUNCTION pFunctions[]) |
1952 | { |
1953 | PUBLIC_API_ENTRY(this); |
1954 | FAIL_IF_NEUTERED(this); |
1955 | |
1956 | ULONG32 index; |
1957 | ULONG32 iRealIndex; |
1958 | ULONG32 last; |
1959 | |
1960 | if (((cFunctions != 0) && (pFunctions == NULL)) || (pcFunctions == NULL)) |
1961 | { |
1962 | return E_INVALIDARG; |
1963 | } |
1964 | |
1965 | // |
1966 | // Default to 0 |
1967 | // |
1968 | *pcFunctions = 0; |
1969 | |
1970 | // @dbgtodo synchronization - The ATT macro may slip the thread to a sychronized state. The |
1971 | // synchronization feature crew needs to figure out what to do here. Then we can use the |
1972 | // PUBLIC_API_BEGIN macro in this function. |
1973 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
1974 | |
1975 | HRESULT hr = S_OK; |
1976 | EX_TRY |
1977 | { |
1978 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
1979 | |
1980 | if (IsThreadDead()) |
1981 | { |
1982 | // |
1983 | // Return zero active functions on this thread. |
1984 | // |
1985 | hr = S_OK; |
1986 | } |
1987 | else |
1988 | { |
1989 | ULONG32 cAllFrames = 0; // the total number of frames (stack frames and internal frames) |
1990 | ULONG32 cStackFrames = 0; // the number of stack frames |
1991 | ShimStackWalk * pSSW = NULL; |
1992 | |
1993 | if (GetProcess()->GetShim() != NULL) |
1994 | { |
1995 | PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(GetProcess()); |
1996 | pSSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(this); |
1997 | |
1998 | // initialize the frame counts |
1999 | cAllFrames = pSSW->GetFrameCount(); |
2000 | for (ULONG32 i = 0; i < cAllFrames; i++) |
2001 | { |
2002 | // filter out internal frames |
2003 | if (CordbFrame::GetCordbFrameFromInterface(pSSW->GetFrame(i))->GetAsNativeFrame() != NULL) |
2004 | { |
2005 | cStackFrames += 1; |
2006 | } |
2007 | } |
2008 | |
2009 | _ASSERTE(cStackFrames <= cAllFrames); |
2010 | } |
2011 | else |
2012 | { |
2013 | RefreshStack(); |
2014 | |
2015 | cAllFrames = m_stackFrames.Count(); |
2016 | cStackFrames = cAllFrames; |
2017 | |
2018 | // In Arrowhead, the stackwalking API doesn't return internal frames, |
2019 | // so the frame counts should be equal. |
2020 | _ASSERTE(cStackFrames == cAllFrames); |
2021 | } |
2022 | |
2023 | *pcFunctions = cStackFrames; |
2024 | |
2025 | // |
2026 | // If all we want is the count, then return that. |
2027 | // |
2028 | if ((pFunctions == NULL) || (cFunctions == 0)) |
2029 | { |
2030 | hr = S_OK; |
2031 | } |
2032 | else |
2033 | { |
2034 | // |
2035 | // Now go down list of frames, storing information |
2036 | // |
2037 | last = (cFunctions < cStackFrames) ? cFunctions : cStackFrames; |
2038 | iRealIndex = 0; |
2039 | index =0; |
2040 | |
2041 | while((index < last) && (iRealIndex < cAllFrames)) |
2042 | { |
2043 | CordbFrame * pThisFrame = NULL; |
2044 | if (GetProcess()->GetShim()) |
2045 | { |
2046 | _ASSERTE(pSSW != NULL); |
2047 | pThisFrame = CordbFrame::GetCordbFrameFromInterface(pSSW->GetFrame(iRealIndex)); |
2048 | } |
2049 | else |
2050 | { |
2051 | pThisFrame = *(m_stackFrames.Get(iRealIndex)); |
2052 | _ASSERTE(pThisFrame->GetAsNativeFrame() != NULL); |
2053 | } |
2054 | |
2055 | iRealIndex++; |
2056 | |
2057 | CordbNativeFrame * pNativeFrame = pThisFrame->GetAsNativeFrame(); |
2058 | if (pNativeFrame == NULL) |
2059 | { |
2060 | // filter out internal frames |
2061 | _ASSERTE(pThisFrame->GetAsInternalFrame() != NULL); |
2062 | continue; |
2063 | } |
2064 | |
2065 | // |
2066 | // Fill in the easy stuff. |
2067 | // |
2068 | CordbFunction * pFunction; |
2069 | |
2070 | pFunction = (static_cast<CordbFrame *>(pNativeFrame))->GetFunction(); |
2071 | ASSERT(pFunction != NULL); |
2072 | |
2073 | hr = pFunction->QueryInterface(IID_ICorDebugFunction2, |
2074 | reinterpret_cast<void **>(&(pFunctions[index].pFunction))); |
2075 | ASSERT(!FAILED(hr)); |
2076 | |
2077 | CordbModule * pModule = pFunction->GetModule(); |
2078 | pFunctions[index].pModule = pModule; |
2079 | pModule->ExternalAddRef(); |
2080 | |
2081 | CordbAppDomain * pAppDomain = pNativeFrame->GetCurrentAppDomain(); |
2082 | pFunctions[index].pAppDomain = pAppDomain; |
2083 | pAppDomain->ExternalAddRef(); |
2084 | |
2085 | pFunctions[index].flags = 0; |
2086 | |
2087 | // |
2088 | // Now go to the IL frame (if one exists) to the get the offset. |
2089 | // |
2090 | CordbJITILFrame * pJITILFrame; |
2091 | |
2092 | pJITILFrame = pNativeFrame->m_JITILFrame; |
2093 | |
2094 | if (pJITILFrame != NULL) |
2095 | { |
2096 | hr = pJITILFrame->GetIP(&(pFunctions[index].ilOffset), NULL); |
2097 | ASSERT(!FAILED(hr)); |
2098 | } |
2099 | else |
2100 | { |
2101 | pFunctions[index].ilOffset = (DWORD) NO_MAPPING; |
2102 | } |
2103 | |
2104 | // Update to the next count. |
2105 | index++; |
2106 | } |
2107 | |
2108 | // @todo - The spec says that pcFunctions == # of elements in pFunctions, |
2109 | // but the behavior here is that it's always the total. |
2110 | // If we want to fix that, we should uncomment the assignment here: |
2111 | //*pcFunctions = index; |
2112 | } |
2113 | } |
2114 | } |
2115 | EX_CATCH_HRESULT(hr); |
2116 | return hr; |
2117 | } |
2118 | |
2119 | |
2120 | //--------------------------------------------------------------------------------------- |
2121 | // |
2122 | // This is the entry point for continuable exceptions. |
2123 | // It implements ICorDebugThread2::InterceptCurrentException. |
2124 | // |
2125 | // Arguments: |
2126 | // pFrame - the stack frame to intercept at |
2127 | // |
2128 | // Return Value: |
2129 | // HRESULT indicating success or failure |
2130 | // |
2131 | // Notes: |
2132 | // Since we cannot intercept an exception at an internal frame, |
2133 | // pFrame should not be an ICorDebugInternalFrame. |
2134 | // |
2135 | |
2136 | HRESULT CordbThread::InterceptCurrentException(ICorDebugFrame * pFrame) |
2137 | { |
2138 | PUBLIC_API_ENTRY(this); |
2139 | FAIL_IF_NEUTERED(this); |
2140 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2141 | |
2142 | #if defined(FEATURE_DBGIPC_TRANSPORT_DI) |
2143 | // Continuable exceptions are not implemented on Unix-like platforms. |
2144 | return E_NOTIMPL; |
2145 | |
2146 | #else // !FEATURE_DBGIPC_TRANSPORT_DI |
2147 | HRESULT hr = S_OK; |
2148 | EX_TRY |
2149 | { |
2150 | DebuggerIPCEvent event; |
2151 | |
2152 | if (pFrame == NULL) |
2153 | { |
2154 | ThrowHR(E_INVALIDARG); |
2155 | } |
2156 | |
2157 | // |
2158 | // Verify we were passed a real stack frame, and not an internal |
2159 | // CLR mocked up one. |
2160 | // |
2161 | { |
2162 | RSExtSmartPtr<ICorDebugInternalFrame> pInternalFrame; |
2163 | hr = pFrame->QueryInterface(IID_ICorDebugInternalFrame, (void **)&pInternalFrame); |
2164 | |
2165 | if (!FAILED(hr)) |
2166 | { |
2167 | ThrowHR(E_INVALIDARG); |
2168 | } |
2169 | } |
2170 | |
2171 | |
2172 | // |
2173 | // If the thread is detached, then there should be no frames on its stack. |
2174 | // |
2175 | hr = EnsureThreadIsAlive(); |
2176 | |
2177 | if (SUCCEEDED(hr)) |
2178 | { |
2179 | // |
2180 | // Refresh the stack frames for this thread and verify pFrame is on it. |
2181 | // |
2182 | |
2183 | RefreshStack(); |
2184 | |
2185 | // |
2186 | // Now check if the frame actually lives on the stack of the current thread. |
2187 | // |
2188 | |
2189 | // "Cast" the ICDFrame pointer to a CordbFrame pointer. |
2190 | CordbFrame * pRealFrame = CordbFrame::GetCordbFrameFromInterface(pFrame); |
2191 | if (!OwnsFrame(pRealFrame)) |
2192 | { |
2193 | ThrowHR(E_INVALIDARG); |
2194 | } |
2195 | |
2196 | // |
2197 | // pFrame is on the stack - good. Now tell the LS to intercept at that frame. |
2198 | // |
2199 | |
2200 | GetProcess()->InitIPCEvent(&event, DB_IPCE_INTERCEPT_EXCEPTION, true, VMPTR_AppDomain::NullPtr()); |
2201 | |
2202 | event.InterceptException.vmThreadToken = m_vmThreadToken; |
2203 | event.InterceptException.frameToken = pRealFrame->GetFramePointer(); |
2204 | |
2205 | hr = GetProcess()->m_cordb->SendIPCEvent(GetProcess(), &event, sizeof(DebuggerIPCEvent)); |
2206 | |
2207 | // |
2208 | // Stop now if we can't even send the event. |
2209 | // |
2210 | if (!SUCCEEDED(hr)) |
2211 | { |
2212 | ThrowHR(hr); |
2213 | } |
2214 | |
2215 | _ASSERTE(event.type == DB_IPCE_INTERCEPT_EXCEPTION_RESULT); |
2216 | |
2217 | hr = event.hr; |
2218 | // Since we are going to exit anyway, we don't need to throw here. |
2219 | } |
2220 | } |
2221 | EX_CATCH_HRESULT(hr); |
2222 | return hr; |
2223 | #endif // FEATURE_DBGIPC_TRANSPORT_DI |
2224 | } |
2225 | |
2226 | //--------------------------------------------------------------------------------------- |
2227 | // |
2228 | // Return S_OK if there is a current exception and it is unhandled, otherwise |
2229 | // return S_FALSE |
2230 | // |
2231 | HRESULT CordbThread::HasUnhandledException() |
2232 | { |
2233 | FAIL_IF_NEUTERED(this); |
2234 | |
2235 | HRESULT hr = S_FALSE; |
2236 | PUBLIC_REENTRANT_API_BEGIN(this) |
2237 | { |
2238 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
2239 | if(pDAC->HasUnhandledException(m_vmThreadToken)) |
2240 | { |
2241 | hr = S_OK; |
2242 | } |
2243 | } |
2244 | PUBLIC_REENTRANT_API_END(hr); |
2245 | |
2246 | return hr; |
2247 | } |
2248 | |
2249 | //--------------------------------------------------------------------------------------- |
2250 | // |
2251 | // Create a stackwalker on the current thread. Initially, the stackwalker is stopped at the |
2252 | // managed filter CONTEXT if there is one. Otherwise it is stopped at the leaf CONTEXT. |
2253 | // |
2254 | // Arguments: |
2255 | // ppStackWalk - out parameter; return the new stackwalker |
2256 | // |
2257 | // Return Value: |
2258 | // Return S_OK on succcess. |
2259 | // Return E_FAIL on error. |
2260 | // |
2261 | // Notes: |
2262 | // The filter CONTEXT will be removed in V3.0. |
2263 | // |
2264 | |
2265 | HRESULT CordbThread::CreateStackWalk(ICorDebugStackWalk ** ppStackWalk) |
2266 | { |
2267 | PUBLIC_REENTRANT_API_ENTRY(this); |
2268 | FAIL_IF_NEUTERED(this); |
2269 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2270 | |
2271 | VALIDATE_POINTER_TO_OBJECT(ppStackWalk, ICorDebugStackWalk **); |
2272 | |
2273 | HRESULT hr = S_OK; |
2274 | |
2275 | EX_TRY |
2276 | { |
2277 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
2278 | hr = EnsureThreadIsAlive(); |
2279 | |
2280 | if (SUCCEEDED(hr)) |
2281 | { |
2282 | RSInitHolder<CordbStackWalk> pSW(new CordbStackWalk(this)); |
2283 | pSW->Init(); |
2284 | pSW.TransferOwnershipExternal(ppStackWalk); |
2285 | } |
2286 | } |
2287 | EX_CATCH_HRESULT(hr); |
2288 | |
2289 | return hr; |
2290 | } |
2291 | |
2292 | |
2293 | //--------------------------------------------------------------------------------------- |
2294 | // |
2295 | // This is a callback function used to enumerate the internal frames on a thread. |
2296 | // Each time this callback is invoked, we'll create a new CordbInternalFrame and store it |
2297 | // in an array. See code:DacDbiInterfaceImpl::EnumerateInternalFrames for more information. |
2298 | // |
2299 | // Arguments: |
2300 | // pFrameData - contains information about the current internal frame in the enumeration |
2301 | // pUserData - This is a GetActiveInternalFramesData. |
2302 | // It contains an array of internl frames to be filled. |
2303 | // |
2304 | |
2305 | // static |
2306 | void CordbThread::GetActiveInternalFramesCallback(const DebuggerIPCE_STRData * pFrameData, |
2307 | void * pUserData) |
2308 | { |
2309 | // Retrieve the CordbThread. |
2310 | GetActiveInternalFramesData * pCallbackData = reinterpret_cast<GetActiveInternalFramesData *>(pUserData); |
2311 | CordbThread * pThis = pCallbackData->pThis; |
2312 | INTERNAL_DAC_CALLBACK(pThis->GetProcess()); |
2313 | |
2314 | // Make sure we are getting invoked for internal frames. |
2315 | _ASSERTE(pFrameData->eType == DebuggerIPCE_STRData::cStubFrame); |
2316 | |
2317 | // Look up the CordbAppDomain. |
2318 | CordbAppDomain * pAppDomain = NULL; |
2319 | VMPTR_AppDomain vmCurrentAppDomain = pFrameData->vmCurrentAppDomainToken; |
2320 | if (!vmCurrentAppDomain.IsNull()) |
2321 | { |
2322 | pAppDomain = pThis->GetProcess()->LookupOrCreateAppDomain(vmCurrentAppDomain); |
2323 | } |
2324 | |
2325 | // Create a CordbInternalFrame. |
2326 | CordbInternalFrame * pInternalFrame = new CordbInternalFrame(pThis, |
2327 | pFrameData->fp, |
2328 | pAppDomain, |
2329 | pFrameData); |
2330 | |
2331 | // Store the internal frame in the array and update the index to prepare for the next one. |
2332 | pCallbackData->pInternalFrames.Assign(pCallbackData->uIndex, pInternalFrame); |
2333 | pCallbackData->uIndex++; |
2334 | } |
2335 | |
2336 | //--------------------------------------------------------------------------------------- |
2337 | // |
2338 | // This function returns an array of ICDInternalFrame2. Each element represents an internal frame |
2339 | // on the thread. If ppInternalFrames is NULL or cInternalFrames is 0, then we just return |
2340 | // the number of internal frames on the thread. |
2341 | // |
2342 | // Arguments: |
2343 | // cInternalFrames - the number of elements in ppInternalFrames |
2344 | // pcInternalFrames - out parameter; return the number of internal frames on the thread |
2345 | // ppInternalFrames - a buffer to store the array of internal frames |
2346 | // |
2347 | // Return Value: |
2348 | // S_OK on success. |
2349 | // E_INVALIDARG if |
2350 | // - ppInternalFrames is NULL but cInternalFrames is not 0 |
2351 | // - pcInternalFrames is NULL |
2352 | // - cInternalFrames is smaller than the number of internal frames actually on the thread |
2353 | // |
2354 | |
2355 | HRESULT CordbThread::GetActiveInternalFrames(ULONG32 cInternalFrames, |
2356 | ULONG32 * pcInternalFrames, |
2357 | ICorDebugInternalFrame2 * ppInternalFrames[]) |
2358 | { |
2359 | HRESULT hr = S_OK; |
2360 | PUBLIC_REENTRANT_API_BEGIN(this); |
2361 | { |
2362 | if ( ((cInternalFrames != 0) && (ppInternalFrames == NULL)) || |
2363 | (pcInternalFrames == NULL) ) |
2364 | { |
2365 | ThrowHR(E_INVALIDARG); |
2366 | } |
2367 | |
2368 | *pcInternalFrames = 0; |
2369 | |
2370 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
2371 | ULONG32 cActiveInternalFrames = pDAC->GetCountOfInternalFrames(m_vmThreadToken); |
2372 | |
2373 | // Set the count. |
2374 | *pcInternalFrames = cActiveInternalFrames; |
2375 | |
2376 | // Don't need to do anything else if the user is only asking for the count. |
2377 | if ((cInternalFrames != 0) && (ppInternalFrames != NULL)) |
2378 | { |
2379 | if (cInternalFrames < cActiveInternalFrames) |
2380 | { |
2381 | ThrowWin32(ERROR_INSUFFICIENT_BUFFER); |
2382 | } |
2383 | else |
2384 | { |
2385 | // initialize the callback data |
2386 | GetActiveInternalFramesData data; |
2387 | data.pThis = this; |
2388 | data.uIndex = 0; |
2389 | data.pInternalFrames.AllocOrThrow(cActiveInternalFrames); |
2390 | // We want to ensure it's automatically cleaned up in all cases |
2391 | // e.g. if we're debugging a MiniDumpNormal and we fail to |
2392 | // retrieve memory from the target. The exception will be |
2393 | // caught above this frame. |
2394 | data.pInternalFrames.EnableAutoClear(); |
2395 | |
2396 | pDAC->EnumerateInternalFrames(m_vmThreadToken, |
2397 | &CordbThread::GetActiveInternalFramesCallback, |
2398 | &data); |
2399 | _ASSERTE(cActiveInternalFrames == data.pInternalFrames.Length()); |
2400 | |
2401 | // Copy the internal frames we have accumulated in GetActiveInternalFramesData to the out |
2402 | // argument. |
2403 | for (unsigned int i = 0; i < data.pInternalFrames.Length(); i++) |
2404 | { |
2405 | RSInitHolder<CordbInternalFrame> pInternalFrame(data.pInternalFrames[i]); |
2406 | pInternalFrame.TransferOwnershipExternal(&(ppInternalFrames[i])); |
2407 | } |
2408 | } |
2409 | } |
2410 | } |
2411 | PUBLIC_REENTRANT_API_END(hr); |
2412 | return hr; |
2413 | } |
2414 | |
2415 | |
2416 | // ICorDebugThread4 |
2417 | |
2418 | // ------------------------------------------------------------------------------- |
2419 | // Gets the current custom notification on this thread or NULL if no such object exists |
2420 | // Arguments: |
2421 | // output: ppNotificationObject - current CustomNotification object. |
2422 | // if we aren't currently inside a CustomNotification callback, this will |
2423 | // always return NULL. |
2424 | // return value: |
2425 | // S_OK on success |
2426 | // S_FALSE if no object exists |
2427 | // CORDBG_E_BAD_REFERENCE_VALUE if the reference is bad |
2428 | HRESULT CordbThread::GetCurrentCustomDebuggerNotification(ICorDebugValue ** ppNotificationObject) |
2429 | { |
2430 | HRESULT hr = S_OK; |
2431 | PUBLIC_API_NO_LOCK_BEGIN(this); |
2432 | { |
2433 | ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW(GetProcess(), ThrowHR); |
2434 | |
2435 | if (ppNotificationObject == NULL) |
2436 | { |
2437 | ThrowHR(E_INVALIDARG); |
2438 | } |
2439 | |
2440 | *ppNotificationObject = NULL; |
2441 | |
2442 | // |
2443 | // Go to the LS and retrieve any notification object. |
2444 | // |
2445 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
2446 | VMPTR_OBJECTHANDLE vmObjHandle = pDAC->GetCurrentCustomDebuggerNotification(m_vmThreadToken); |
2447 | |
2448 | #if defined(_DEBUG) |
2449 | // Since we know a notification has occurred on this thread, our assumption about the |
2450 | // thread's current AppDomain should be correct |
2451 | VMPTR_AppDomain vmAppDomain = pDAC->GetCurrentAppDomain(m_vmThreadToken); |
2452 | |
2453 | _ASSERTE(GetAppDomain()->GetADToken() == vmAppDomain); |
2454 | #endif // _DEBUG |
2455 | |
2456 | if (!vmObjHandle.IsNull()) |
2457 | { |
2458 | ICorDebugReferenceValue * pRefValue = NULL; |
2459 | IfFailThrow(CordbReferenceValue::BuildFromGCHandle(GetAppDomain(), vmObjHandle, &pRefValue)); |
2460 | *ppNotificationObject = pRefValue; |
2461 | } |
2462 | } |
2463 | PUBLIC_API_END(hr); |
2464 | return hr; |
2465 | } |
2466 | |
2467 | /* |
2468 | * |
2469 | * SetRemapIP |
2470 | * |
2471 | * This routine communicate the EnC remap IP to the LS by writing it to process memory using |
2472 | * the pointer that was set in the thread. If the address is null, then we haven't seen |
2473 | * a RemapOpportunity call for this frame/function combo yet, so invalid to Remap the function. |
2474 | * |
2475 | * Parameters: |
2476 | * offset - the IL offset to set the IP to |
2477 | * |
2478 | * Return Value: |
2479 | * S_OK or CORDBG_E_NO_REMAP_BREAKPIONT. |
2480 | * |
2481 | */ |
2482 | HRESULT CordbThread::SetRemapIP(SIZE_T offset) |
2483 | { |
2484 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
2485 | |
2486 | // This is only set when we're prepared to do a remap |
2487 | if (! m_EnCRemapFunctionIP) |
2488 | { |
2489 | return CORDBG_E_NO_REMAP_BREAKPIONT; |
2490 | } |
2491 | |
2492 | // Write the value of the remap offset into the left side |
2493 | HRESULT hr = GetProcess()->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(m_EnCRemapFunctionIP), &offset); |
2494 | |
2495 | // Prevent SetRemapIP from being called twice for the same RemapOpportunity |
2496 | // If we don't get any calls to RemapFunction, this member will be cleared in |
2497 | // code:CordbThread::MarkStackFramesDirty when Continue is called |
2498 | m_EnCRemapFunctionIP = NULL; |
2499 | |
2500 | return hr; |
2501 | } |
2502 | |
2503 | |
2504 | //--------------------------------------------------------------------------------------- |
2505 | // |
2506 | // This routine is the interface function for ICorDebugThread2::GetConnectionID. |
2507 | // |
2508 | // Arguments: |
2509 | // pdwConnectionId - return connection id set on the thread. Can return INVALID_CONNECTION_ID |
2510 | // |
2511 | // Return Value: |
2512 | // HRESULT indicating success or failure |
2513 | // |
2514 | HRESULT CordbThread::GetConnectionID(CONNID * pConnectionID) |
2515 | { |
2516 | PUBLIC_API_ENTRY(this); |
2517 | FAIL_IF_NEUTERED(this); |
2518 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2519 | |
2520 | // now retrieve the connection id |
2521 | HRESULT hr = S_OK; |
2522 | EX_TRY |
2523 | { |
2524 | if (pConnectionID == NULL) |
2525 | { |
2526 | ThrowHR(E_INVALIDARG); |
2527 | } |
2528 | |
2529 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
2530 | *pConnectionID = pDAC->GetConnectionID(m_vmThreadToken); |
2531 | |
2532 | if (*pConnectionID == INVALID_CONNECTION_ID) |
2533 | { |
2534 | hr = S_FALSE; |
2535 | } |
2536 | } |
2537 | EX_CATCH_HRESULT(hr); |
2538 | |
2539 | return hr; |
2540 | } // CordbThread::GetConnectionID |
2541 | |
2542 | //--------------------------------------------------------------------------------------- |
2543 | // |
2544 | // This routine is the interface function for ICorDebugThread2::GetTaskID. |
2545 | // |
2546 | // Arguments: |
2547 | // pTaskId - return task id set on the thread. Can return INVALID_TASK_ID |
2548 | // |
2549 | // Return Value: |
2550 | // HRESULT indicating success or failure |
2551 | // |
2552 | HRESULT CordbThread::GetTaskID(TASKID * pTaskID) |
2553 | { |
2554 | PUBLIC_API_ENTRY(this); |
2555 | FAIL_IF_NEUTERED(this); |
2556 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2557 | |
2558 | // now retrieve the task id |
2559 | HRESULT hr = S_OK; |
2560 | EX_TRY |
2561 | { |
2562 | if (pTaskID == NULL) |
2563 | { |
2564 | ThrowHR(E_INVALIDARG); |
2565 | } |
2566 | |
2567 | *pTaskID = this->GetTaskID(); |
2568 | |
2569 | if (*pTaskID == INVALID_TASK_ID) |
2570 | { |
2571 | hr = S_FALSE; |
2572 | } |
2573 | } |
2574 | EX_CATCH_HRESULT(hr); |
2575 | |
2576 | return hr; |
2577 | } // CordbThread::GetTaskID |
2578 | |
2579 | //--------------------------------------------------------------------------------------- |
2580 | // Get the task ID for this thread |
2581 | // |
2582 | // return: |
2583 | // task id set on the thread. Can return INVALID_TASK_ID |
2584 | // |
2585 | TASKID CordbThread::GetTaskID() |
2586 | { |
2587 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
2588 | return pDAC->GetTaskID(m_vmThreadToken); |
2589 | } |
2590 | |
2591 | |
2592 | |
2593 | //--------------------------------------------------------------------------------------- |
2594 | // |
2595 | // This routine is the interface function for ICorDebugThread2::GetVolatileOSThreadID. |
2596 | // |
2597 | // Arguments: |
2598 | // pdwTid - return os thread id |
2599 | // |
2600 | // Return Value: |
2601 | // HRESULT indicating success or failure |
2602 | // |
2603 | // Notes: |
2604 | // Compare with code:CordbThread::GetID |
2605 | HRESULT CordbThread::GetVolatileOSThreadID(DWORD * pdwTID) |
2606 | { |
2607 | PUBLIC_API_ENTRY(this); |
2608 | FAIL_IF_NEUTERED(this); |
2609 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2610 | |
2611 | // now retrieve the OS thread ID |
2612 | HRESULT hr = S_OK; |
2613 | EX_TRY |
2614 | { |
2615 | if (pdwTID == NULL) |
2616 | { |
2617 | ThrowHR(E_INVALIDARG); |
2618 | } |
2619 | |
2620 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
2621 | *pdwTID = pDAC->TryGetVolatileOSThreadID(m_vmThreadToken); |
2622 | |
2623 | if (*pdwTID == 0) |
2624 | { |
2625 | hr = S_FALSE; // Switched out |
2626 | } |
2627 | } |
2628 | EX_CATCH_HRESULT(hr); |
2629 | |
2630 | return hr; |
2631 | } // CordbThread::GetOSThreadID |
2632 | |
2633 | //--------------------------------------------------------------------------------------- |
2634 | // Get the thread's volatile OS ID. (this is fiber aware) |
2635 | // |
2636 | // Returns: |
2637 | // Thread's current OS id. For fibers / "logical threads", This may change as a thread executes. |
2638 | // Throws if the managed thread currently is not mapped to an OS thread (ie, not scheduled) |
2639 | // |
2640 | DWORD CordbThread::GetVolatileOSThreadID() |
2641 | { |
2642 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
2643 | DWORD dwThreadID = pDAC->TryGetVolatileOSThreadID(m_vmThreadToken); |
2644 | |
2645 | if (dwThreadID == 0) |
2646 | { |
2647 | ThrowHR(CORDBG_E_THREAD_NOT_SCHEDULED); |
2648 | } |
2649 | return dwThreadID; |
2650 | } |
2651 | |
2652 | // ---------------------------------------------------------------------------- |
2653 | // CordbThread::ClearStackFrameCache |
2654 | // |
2655 | // Description: |
2656 | // Clear the cache of stack frames maintained by the CordbThread. |
2657 | // |
2658 | // Notes: |
2659 | // We are doing an InternalRelease() here to match the InternalAddRef() in code:CordbThread::RefreshStack. |
2660 | // |
2661 | |
2662 | void CordbThread::ClearStackFrameCache() |
2663 | { |
2664 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
2665 | |
2666 | for (int i = 0; i < m_stackFrames.Count(); i++) |
2667 | { |
2668 | (*m_stackFrames.Get(i))->Neuter(); |
2669 | (*m_stackFrames.Get(i))->InternalRelease(); |
2670 | } |
2671 | m_stackFrames.Clear(); |
2672 | } |
2673 | |
2674 | // ---------------------------------------------------------------------------- |
2675 | // EnumerateBlockingObjectsCallback |
2676 | // |
2677 | // Description: |
2678 | // A small helper used by CordbThread::GetBlockingObjects. This callback adds the enumerated items |
2679 | // to a list |
2680 | // |
2681 | // Arguments: |
2682 | // blockingObject - the object to add to the list |
2683 | // pUserData - the list to add it to |
2684 | |
2685 | VOID EnumerateBlockingObjectsCallback(DacBlockingObject blockingObject, CALLBACK_DATA pUserData) |
2686 | { |
2687 | CQuickArrayList<DacBlockingObject>* pDacBlockingObjs = (CQuickArrayList<DacBlockingObject>*)pUserData; |
2688 | pDacBlockingObjs->Push(blockingObject); |
2689 | } |
2690 | |
2691 | // ---------------------------------------------------------------------------- |
2692 | // CordbThread::GetBlockingObjects |
2693 | // |
2694 | // Description: |
2695 | // Returns a list of objects that a thread is blocking on by using Monitor.Enter and |
2696 | // Monitor.Wait |
2697 | // |
2698 | // Arguments: |
2699 | // ppBlockingObjectEnum - on return this is an enumerator for the list of blocking objects |
2700 | // |
2701 | // Return: |
2702 | // S_OK on success or an appropriate failing HRESULT |
2703 | |
2704 | HRESULT CordbThread::GetBlockingObjects(ICorDebugBlockingObjectEnum **ppBlockingObjectEnum) |
2705 | { |
2706 | PUBLIC_API_ENTRY(this); |
2707 | FAIL_IF_NEUTERED(this); |
2708 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
2709 | VALIDATE_POINTER_TO_OBJECT(ppBlockingObjectEnum, ICorDebugBlockingObjectEnum **); |
2710 | |
2711 | HRESULT hr = S_OK; |
2712 | CorDebugBlockingObject* blockingObjs = NULL; |
2713 | EX_TRY |
2714 | { |
2715 | CQuickArrayList<DacBlockingObject> dacBlockingObjects; |
2716 | IDacDbiInterface* pDac = GetProcess()->GetDAC(); |
2717 | pDac->EnumerateBlockingObjects(m_vmThreadToken, |
2718 | (IDacDbiInterface::FP_BLOCKINGOBJECT_ENUMERATION_CALLBACK) EnumerateBlockingObjectsCallback, |
2719 | (CALLBACK_DATA) &dacBlockingObjects); |
2720 | blockingObjs = new CorDebugBlockingObject[dacBlockingObjects.Size()]; |
2721 | for(SIZE_T i = 0 ; i < dacBlockingObjects.Size(); i++) |
2722 | { |
2723 | // ICorDebug API needs to flip the direction of the list from the way DAC stores it |
2724 | SIZE_T dacObjIndex = dacBlockingObjects.Size()-i-1; |
2725 | switch(dacBlockingObjects[dacObjIndex].blockingReason) |
2726 | { |
2727 | case DacBlockReason_MonitorCriticalSection: |
2728 | blockingObjs[i].blockingReason = BLOCKING_MONITOR_CRITICAL_SECTION; |
2729 | break; |
2730 | case DacBlockReason_MonitorEvent: |
2731 | blockingObjs[i].blockingReason = BLOCKING_MONITOR_EVENT; |
2732 | break; |
2733 | default: |
2734 | _ASSERTE(!"Should not get here" ); |
2735 | ThrowHR(E_FAIL); |
2736 | break; |
2737 | } |
2738 | blockingObjs[i].dwTimeout = dacBlockingObjects[dacObjIndex].dwTimeout; |
2739 | CordbAppDomain* pAppDomain; |
2740 | { |
2741 | RSLockHolder holder(GetProcess()->GetProcessLock()); |
2742 | pAppDomain = GetProcess()->LookupOrCreateAppDomain(dacBlockingObjects[dacObjIndex].vmAppDomain); |
2743 | } |
2744 | blockingObjs[i].pBlockingObject = CordbValue::CreateHeapValue(pAppDomain, |
2745 | dacBlockingObjects[dacObjIndex].vmBlockingObject); |
2746 | } |
2747 | |
2748 | CordbBlockingObjectEnumerator* objEnum = new CordbBlockingObjectEnumerator(GetProcess(), |
2749 | blockingObjs, |
2750 | (DWORD)dacBlockingObjects.Size()); |
2751 | GetProcess()->GetContinueNeuterList()->Add(GetProcess(), objEnum); |
2752 | hr = objEnum->QueryInterface(__uuidof(ICorDebugBlockingObjectEnum), (void**)ppBlockingObjectEnum); |
2753 | _ASSERTE(SUCCEEDED(hr)); |
2754 | } |
2755 | EX_CATCH_HRESULT(hr); |
2756 | delete [] blockingObjs; |
2757 | return hr; |
2758 | } |
2759 | |
2760 | // ---------------------------------------------------------------------------- |
2761 | // CordbThread::SetCreateEventQueued |
2762 | void CordbThread::SetCreateEventQueued() |
2763 | { |
2764 | m_fCreationEventQueued = true; |
2765 | } |
2766 | |
2767 | // ---------------------------------------------------------------------------- |
2768 | // CordbThread::CreateEventWasQueued |
2769 | bool CordbThread::CreateEventWasQueued() |
2770 | { |
2771 | return m_fCreationEventQueued; |
2772 | } |
2773 | |
2774 | |
2775 | #ifdef FEATURE_INTEROP_DEBUGGING |
2776 | /* ------------------------------------------------------------------------- * |
2777 | * Unmanaged Thread classes |
2778 | * ------------------------------------------------------------------------- */ |
2779 | |
2780 | CordbUnmanagedThread::CordbUnmanagedThread(CordbProcess *pProcess, DWORD dwThreadId, HANDLE hThread, void *lpThreadLocalBase) |
2781 | : CordbBase(pProcess, dwThreadId, enumCordbUnmanagedThread), |
2782 | m_handle(hThread), |
2783 | m_threadLocalBase(lpThreadLocalBase), |
2784 | m_pTLSArray(NULL), |
2785 | m_pTLSExtendedArray(NULL), |
2786 | m_state(CUTS_None), |
2787 | m_originalHandler(NULL), |
2788 | #ifdef DBG_TARGET_X86 |
2789 | m_pSavedLeafSeh(NULL), |
2790 | #endif |
2791 | m_stackBase(0), |
2792 | m_stackLimit(0), |
2793 | m_continueCountCached(0) |
2794 | { |
2795 | m_pLeftSideContext.Set(NULL); |
2796 | |
2797 | IBEvent()->m_state = CUES_None; |
2798 | IBEvent()->m_next = NULL; |
2799 | IBEvent()->m_owner = this; |
2800 | |
2801 | IBEvent2()->m_state = CUES_None; |
2802 | IBEvent2()->m_next = NULL; |
2803 | IBEvent2()->m_owner = this; |
2804 | |
2805 | OOBEvent()->m_state = CUES_None; |
2806 | OOBEvent()->m_next = NULL; |
2807 | OOBEvent()->m_owner = this; |
2808 | |
2809 | m_pPatchSkipAddress = NULL; |
2810 | |
2811 | this->GetStackRange(NULL, NULL); |
2812 | } |
2813 | |
2814 | CordbUnmanagedThread::~CordbUnmanagedThread() |
2815 | { |
2816 | // CordbUnmanagedThread objects will: |
2817 | // - never send IPC events. |
2818 | // - never be exposed to the public. (we assert external-ref is always == 0) |
2819 | // - always manipulated on W32ET (where we can't do IPC stuff) |
2820 | |
2821 | UnsafeNeuterDeadObject(); |
2822 | |
2823 | _ASSERTE(this->IsNeutered()); |
2824 | |
2825 | // by the time the thread is deleted, it shouldn't have any outstanding debug events. |
2826 | |
2827 | // Actually, the thread could get deleted while we have an outstanding IB debug event. We could get the IB event, hijack that thread, |
2828 | // and then since the process is continued, something could go off and kill the hijacked thread. |
2829 | // If the event is still in the process's queued list, and it still refers back to a thread, then we'll AV when we try to access the event |
2830 | // (or continue it). |
2831 | CONSISTENCY_CHECK_MSGF(!HasIBEvent(), ("Deleting thread w/ outstanding IB event:this=%p,event-code=%d\n" , this, IBEvent()->m_currentDebugEvent.dwDebugEventCode)); |
2832 | |
2833 | CONSISTENCY_CHECK_MSGF(!HasOOBEvent(), ("Deleting thread w/ outstanding OOB event:this=%p,event-code=%d\n" , this, OOBEvent()->m_currentDebugEvent.dwDebugEventCode)); |
2834 | } |
2835 | |
2836 | #define WINNT_TLS_OFFSET_X86 0xe10 // TLS[0] at fs:[WINNT_TLS_OFFSET] |
2837 | #define WINNT_TLS_OFFSET_AMD64 0x1480 |
2838 | #define WINNT_TLS_OFFSET_ARM 0xe10 |
2839 | #define WINNT_TLS_OFFSET_ARM64 0x1480 |
2840 | #define WINNT5_TLSEXPANSIONPTR_OFFSET_X86 0xf94 // TLS[64] at [fs:[WINNT5_TLSEXPANSIONPTR_OFFSET]] |
2841 | #define WINNT5_TLSEXPANSIONPTR_OFFSET_AMD64 0x1780 |
2842 | #define WINNT5_TLSEXPANSIONPTR_OFFSET_ARM 0xf94 |
2843 | #define WINNT5_TLSEXPANSIONPTR_OFFSET_ARM64 0x1780 |
2844 | |
2845 | HRESULT CordbUnmanagedThread::LoadTLSArrayPtr(void) |
2846 | { |
2847 | FAIL_IF_NEUTERED(this); |
2848 | |
2849 | HRESULT hr = S_OK; |
2850 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
2851 | |
2852 | |
2853 | // Just simple math on NT with a small tls index. |
2854 | // The TLS slots for 0-63 are embedded in the TIB. |
2855 | #if defined(DBG_TARGET_X86) |
2856 | m_pTLSArray = (BYTE*) m_threadLocalBase + WINNT_TLS_OFFSET_X86; |
2857 | #elif defined(DBG_TARGET_AMD64) |
2858 | m_pTLSArray = (BYTE*) m_threadLocalBase + WINNT_TLS_OFFSET_AMD64; |
2859 | #elif defined(DBG_TARGET_ARM) |
2860 | m_pTLSArray = (BYTE*) m_threadLocalBase + WINNT_TLS_OFFSET_ARM; |
2861 | #elif defined(DBG_TARGET_ARM64) |
2862 | m_pTLSArray = (BYTE*) m_threadLocalBase + WINNT_TLS_OFFSET_ARM64; |
2863 | #else |
2864 | PORTABILITY_ASSERT("Implement OOP TLS on your platform" ); |
2865 | #endif |
2866 | |
2867 | // Extended slot is lazily initialized, so check every time. |
2868 | if (m_pTLSExtendedArray == NULL) |
2869 | { |
2870 | // On NT 5 you can have TLS index's greater than 63, so we |
2871 | // have to grab the ptr to the TLS expansion array first, |
2872 | // then use that as the base to index off of. This will |
2873 | // never move once we find it for a given thread, so we |
2874 | // cache it here so we don't always have to perform two |
2875 | // ReadProcessMemory's. |
2876 | #if defined(DBG_TARGET_X86) |
2877 | void *ppTLSArray = (BYTE*) m_threadLocalBase + WINNT5_TLSEXPANSIONPTR_OFFSET_X86; |
2878 | #elif defined(DBG_TARGET_AMD64) |
2879 | void *ppTLSArray = (BYTE*) m_threadLocalBase + WINNT5_TLSEXPANSIONPTR_OFFSET_AMD64; |
2880 | #elif defined(DBG_TARGET_ARM) |
2881 | void *ppTLSArray = (BYTE*) m_threadLocalBase + WINNT5_TLSEXPANSIONPTR_OFFSET_ARM; |
2882 | #elif defined(DBG_TARGET_ARM64) |
2883 | void *ppTLSArray = (BYTE*) m_threadLocalBase + WINNT5_TLSEXPANSIONPTR_OFFSET_ARM64; |
2884 | #else |
2885 | PORTABILITY_ASSERT("Implement OOP TLS on your platform" ); |
2886 | #endif |
2887 | |
2888 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(ppTLSArray), &m_pTLSExtendedArray); |
2889 | } |
2890 | |
2891 | |
2892 | return hr; |
2893 | } |
2894 | |
2895 | /* |
2896 | VOID CordbUnmanagedThread::VerifyFSChain() |
2897 | { |
2898 | #if defined(DBG_TARGET_X86) |
2899 | DT_CONTEXT temp; |
2900 | temp.ContextFlags = DT_CONTEXT_FULL; |
2901 | DbiGetThreadContext(m_handle, &temp); |
2902 | LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: 0x%x fs=0x%x TIB=0x%x\n", |
2903 | m_id, temp.SegFs, m_threadLocalBase)); |
2904 | REMOTE_PTR pExceptionRegRecordPtr; |
2905 | HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(m_threadLocalBase), &pExceptionRegRecordPtr); |
2906 | if(FAILED(hr)) |
2907 | { |
2908 | LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x failed to read fs:0 value: computed addr=0x%p err=%x\n", |
2909 | m_id, m_threadLocalBase, hr)); |
2910 | _ASSERTE(FALSE); |
2911 | return; |
2912 | } |
2913 | while(pExceptionRegRecordPtr != EXCEPTION_CHAIN_END && pExceptionRegRecordPtr != NULL) |
2914 | { |
2915 | REMOTE_PTR prev; |
2916 | REMOTE_PTR handler; |
2917 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pExceptionRegRecordPtr), &prev); |
2918 | if(FAILED(hr)) |
2919 | { |
2920 | LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x failed to read prev value: computed addr=0x%p err=%x\n", |
2921 | m_id, pExceptionRegRecordPtr, hr)); |
2922 | return; |
2923 | } |
2924 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS( (VOID*)((DWORD)pExceptionRegRecordPtr+4) ), &handler); |
2925 | if(FAILED(hr)) |
2926 | { |
2927 | LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x failed to read handler value: computed addr=0x%p err=%x\n", |
2928 | m_id, (DWORD)pExceptionRegRecordPtr+4, hr)); |
2929 | return; |
2930 | } |
2931 | LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: OK 0x%x record=0x%x prev=0x%x handler=0x%x\n", |
2932 | m_id, pExceptionRegRecordPtr, prev, handler)); |
2933 | if(handler == NULL) |
2934 | { |
2935 | LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x NULL handler found\n", m_id)); |
2936 | _ASSERTE(FALSE); |
2937 | return; |
2938 | } |
2939 | if(prev == NULL) |
2940 | { |
2941 | LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x NULL prev found\n", m_id)); |
2942 | _ASSERTE(FALSE); |
2943 | return; |
2944 | } |
2945 | if(prev == pExceptionRegRecordPtr) |
2946 | { |
2947 | LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: ERROR 0x%x cyclic prev found\n", m_id)); |
2948 | _ASSERTE(FALSE); |
2949 | return; |
2950 | } |
2951 | pExceptionRegRecordPtr = prev; |
2952 | } |
2953 | |
2954 | LOG((LF_CORDB, LL_INFO1000, "CUT::VFSC: OK 0x%x\n", m_id)); |
2955 | #endif |
2956 | return; |
2957 | }*/ |
2958 | |
2959 | #ifdef DBG_TARGET_X86 |
2960 | HRESULT CordbUnmanagedThread::SaveCurrentLeafSeh() |
2961 | { |
2962 | _ASSERTE(m_pSavedLeafSeh == NULL); |
2963 | REMOTE_PTR pExceptionRegRecordPtr; |
2964 | HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(m_threadLocalBase), &pExceptionRegRecordPtr); |
2965 | if(FAILED(hr)) |
2966 | { |
2967 | LOG((LF_CORDB, LL_INFO1000, "CUT::SCLS: failed to read fs:0 value: computed addr=0x%p err=%x\n" , m_threadLocalBase, hr)); |
2968 | return hr; |
2969 | } |
2970 | m_pSavedLeafSeh = pExceptionRegRecordPtr; |
2971 | return S_OK; |
2972 | } |
2973 | |
2974 | HRESULT CordbUnmanagedThread::RestoreLeafSeh() |
2975 | { |
2976 | _ASSERTE(m_pSavedLeafSeh != NULL); |
2977 | HRESULT hr = GetProcess()->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(m_threadLocalBase), &m_pSavedLeafSeh); |
2978 | if(FAILED(hr)) |
2979 | { |
2980 | LOG((LF_CORDB, LL_INFO1000, "CUT::RLS: failed to write fs:0 value: computed addr=0x%p err=%x\n" , m_threadLocalBase, hr)); |
2981 | return hr; |
2982 | } |
2983 | m_pSavedLeafSeh = NULL; |
2984 | return S_OK; |
2985 | } |
2986 | #endif |
2987 | |
2988 | // Read the contents from the LS's Predefined TLS block. |
2989 | // This is an auxillary TLS storage array-of-void*, indexed off the TLS. |
2990 | // pRead is optional. This makes sense when '0' is a valid default value. |
2991 | // 1) On success (block exists in LS, we can read it), |
2992 | // return value of data in the slot, *pRead = true |
2993 | // 2) On failure to read block (block doens't exist yet, any other failure) |
2994 | // return value == 0 (assumed default, *pRead = false |
2995 | REMOTE_PTR CordbUnmanagedThread::GetPreDefTlsSlot(SIZE_T slot, bool * pRead) |
2996 | { |
2997 | REMOTE_PTR pBlock = (REMOTE_PTR) GetEETlsDataBlock(); |
2998 | |
2999 | REMOTE_PTR data = 0; |
3000 | |
3001 | // We don't have a maximum size, but we know it's less than ~200. This assert |
3002 | // will catch if we're just passsing Garbage. |
3003 | _ASSERTE(slot < 200); |
3004 | |
3005 | bool dummy; |
3006 | if (pRead == NULL) |
3007 | { |
3008 | pRead = &dummy; |
3009 | } |
3010 | |
3011 | if (pBlock != NULL) |
3012 | { |
3013 | REMOTE_PTR p = ((BYTE*) pBlock) + slot * sizeof(data); |
3014 | |
3015 | // Now read the "special" status out of the PreDef block. |
3016 | HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(p), &data); |
3017 | |
3018 | // The predef block should be valid at this point, so the ReadProcessMemory ought to work. |
3019 | SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr); |
3020 | |
3021 | if (SUCCEEDED(hr)) |
3022 | { |
3023 | *pRead = true; |
3024 | return data; |
3025 | } |
3026 | } |
3027 | |
3028 | *pRead = false; |
3029 | return 0; |
3030 | } |
3031 | |
3032 | // Read the contents from a LS threads's TLS slot. |
3033 | HRESULT CordbUnmanagedThread::GetTlsSlot(DWORD slot, REMOTE_PTR * pValue) |
3034 | { |
3035 | // Compute the address of the necessary TLS value. |
3036 | HRESULT hr = LoadTLSArrayPtr(); |
3037 | if (FAILED(hr)) |
3038 | { |
3039 | return hr; |
3040 | } |
3041 | |
3042 | void * pBase = NULL; |
3043 | SIZE_T slotAdjusted = slot; |
3044 | |
3045 | if (slot < TLS_MINIMUM_AVAILABLE) |
3046 | { |
3047 | pBase = m_pTLSArray; |
3048 | } |
3049 | else if (slot < TLS_MINIMUM_AVAILABLE + TLS_EXPANSION_SLOTS) |
3050 | { |
3051 | pBase = m_pTLSExtendedArray; |
3052 | slotAdjusted -= TLS_MINIMUM_AVAILABLE; |
3053 | |
3054 | // Expansion slot is lazily allocated. If we're trying to read from it, but hasn't been allocated, |
3055 | // then the TLS slot is still the default value, which is 0 (NULL). |
3056 | if (pBase == NULL) |
3057 | { |
3058 | *pValue = NULL; |
3059 | return S_OK; |
3060 | } |
3061 | } |
3062 | else |
3063 | { |
3064 | // Slot is out of range. Shouldn't happen unless debuggee is corrupted. |
3065 | _ASSERTE(!"Invalid TLS slot" ); |
3066 | return E_UNEXPECTED; |
3067 | } |
3068 | |
3069 | void *pEEThreadTLS = (BYTE*)pBase + (slotAdjusted * sizeof(void*)); |
3070 | |
3071 | // Read the thread's TLS value. |
3072 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEThreadTLS), pValue); |
3073 | if (FAILED(hr)) |
3074 | { |
3075 | LOG((LF_CORDB, LL_INFO1000, "CUT::GTS: failed to read TLS value: computed addr=0x%p index=%d, err=%x\n" , |
3076 | pEEThreadTLS, slot, hr)); |
3077 | return hr; |
3078 | } |
3079 | |
3080 | LOG((LF_CORDB, LL_INFO1000000, "CUT::GTS: EE Thread TLS value is 0x%p for thread 0x%x, slot 0x%x\n" , *pValue, m_id, slot)); |
3081 | return S_OK; |
3082 | } |
3083 | |
3084 | // This does a WriteProcessMemory to write to the debuggee's TLS slot |
3085 | // |
3086 | // Notes: |
3087 | // This is very brittle because the OS can lazily allocates storage for TLS slots. |
3088 | // In order to gaurantee the storage is available, it must have been written to by the debuggee. |
3089 | // For managed threads, that's easy because the Thread* is already written to the slot. |
3090 | // But for pure native threads where GetThread() == NULL, the storage may not yet be allocated. |
3091 | // |
3092 | // The saving grace is that the debuggee's hijack filters will force the TLS to be allocated before it |
3093 | // sends a flare. |
3094 | // |
3095 | // Therefore, this function can only be called: |
3096 | // 1) on a managed thread |
3097 | // 2) on a native thread after that thread has been hijacked and sent a flare. |
3098 | // |
3099 | // This is brittle reasoning, but so is the rest of interop-debugging. |
3100 | // |
3101 | HRESULT CordbUnmanagedThread::SetTlsSlot(DWORD slot, REMOTE_PTR value) |
3102 | { |
3103 | FAIL_IF_NEUTERED(this); |
3104 | |
3105 | // Compute the address of the necessary TLS value. |
3106 | HRESULT hr = LoadTLSArrayPtr(); |
3107 | if (FAILED(hr)) |
3108 | { |
3109 | return hr; |
3110 | } |
3111 | |
3112 | void * pBase = NULL; |
3113 | SIZE_T slotAdjusted = slot; |
3114 | if (slot < TLS_MINIMUM_AVAILABLE) |
3115 | { |
3116 | pBase = m_pTLSArray; |
3117 | } |
3118 | else if (slot < TLS_MINIMUM_AVAILABLE + TLS_EXPANSION_SLOTS) |
3119 | { |
3120 | pBase = m_pTLSExtendedArray; |
3121 | slotAdjusted -= TLS_MINIMUM_AVAILABLE; |
3122 | |
3123 | // Expansion slot is lazily allocated. If we're trying to read from it, but hasn't been allocated, |
3124 | // then the TLS slot is still the default value, which is 0. |
3125 | if (pBase == NULL) |
3126 | { |
3127 | // See reasoning in header for why this should succeed. |
3128 | _ASSERTE(!"Can't set to expansion slots because they haven't been allocated" ); |
3129 | return E_FAIL; |
3130 | } |
3131 | } |
3132 | else |
3133 | { |
3134 | // Slot is out of range. Shouldn't happen unless debuggee is corrupted. |
3135 | _ASSERTE(!"Invalid TLS slot" ); |
3136 | return E_INVALIDARG; |
3137 | } |
3138 | |
3139 | void *pEEThreadTLS = (BYTE*)pBase + (slotAdjusted * sizeof(void*)); |
3140 | |
3141 | // Write the thread's TLS value. |
3142 | hr = GetProcess()->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pEEThreadTLS), &value); |
3143 | |
3144 | if (FAILED(hr)) |
3145 | { |
3146 | LOG((LF_CORDB, LL_INFO1000, "CUT::SEETV: failed to set TLS value: computed addr=0x%p slot=%d, err=%x\n" , pEEThreadTLS, slot, hr)); |
3147 | return hr; |
3148 | } |
3149 | |
3150 | LOG((LF_CORDB, LL_INFO1000000, "CUT::SEETV: EE Thread TLS value is now 0x%p for 0x%x\n" , value, m_id)); |
3151 | return S_OK; |
3152 | } |
3153 | |
3154 | // gets the value of gCurrentThreadInfo.m_pThread |
3155 | DWORD_PTR CordbUnmanagedThread::GetEEThreadValue() |
3156 | { |
3157 | DWORD_PTR ret = NULL; |
3158 | |
3159 | REMOTE_PTR tlsDataAddress; |
3160 | HRESULT hr = GetClrModuleTlsDataAddress(&tlsDataAddress); |
3161 | if (FAILED(hr)) |
3162 | { |
3163 | LOG((LF_CORDB, LL_INFO1000, "CUT::GEETV: GetClrModuleTlsDataAddress FAILED %x for 0x%x\n" , hr, m_id)); |
3164 | return NULL; |
3165 | } |
3166 | |
3167 | // Read the thread's TLS value. |
3168 | REMOTE_PTR EEThreadAddr = (BYTE*)tlsDataAddress + OFFSETOF__TLS__tls_CurrentThread; |
3169 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(EEThreadAddr), &ret); |
3170 | if (FAILED(hr)) |
3171 | { |
3172 | LOG((LF_CORDB, LL_INFO1000, "CUT::GEETV: failed to get TLS value: computed addr=0x%p index=%d, err=%x\n" , |
3173 | EEThreadAddr, GetProcess()->m_runtimeOffsets.m_TLSIndex, hr)); |
3174 | return NULL; |
3175 | } |
3176 | |
3177 | LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETV: EE Thread TLS value is 0x%p for 0x%x\n" , ret, m_id)); |
3178 | return ret; |
3179 | } |
3180 | |
3181 | // returns the remote address of gCurrentThreadInfo |
3182 | HRESULT CordbUnmanagedThread::GetClrModuleTlsDataAddress(REMOTE_PTR* pAddress) |
3183 | { |
3184 | *pAddress = NULL; |
3185 | |
3186 | REMOTE_PTR tlsArrayAddr; |
3187 | HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_threadLocalBase + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer), &tlsArrayAddr); |
3188 | if (FAILED(hr)) |
3189 | { |
3190 | return hr; |
3191 | } |
3192 | |
3193 | // This is the special break-in thread case: TEB.ThreadLocalStoragePointer == NULL |
3194 | if (tlsArrayAddr == NULL) |
3195 | { |
3196 | return E_FAIL; |
3197 | } |
3198 | |
3199 | DWORD slot = (DWORD)(GetProcess()->m_runtimeOffsets.m_TLSIndex); |
3200 | |
3201 | REMOTE_PTR clrModuleTlsDataAddr; |
3202 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)tlsArrayAddr + (slot & 0xFFFF) * sizeof(void*)), &clrModuleTlsDataAddr); |
3203 | if (FAILED(hr)) |
3204 | { |
3205 | return hr; |
3206 | } |
3207 | |
3208 | if (clrModuleTlsDataAddr == NULL) |
3209 | { |
3210 | _ASSERTE(!"No clr module data present at _tls_index for this thread" ); |
3211 | return E_FAIL; |
3212 | } |
3213 | |
3214 | *pAddress = (BYTE*) clrModuleTlsDataAddr + ((slot & 0x7FFF0000) >> 16); |
3215 | return S_OK; |
3216 | } |
3217 | |
3218 | // Gets the value of gCurrentThreadInfo.m_EETlsData |
3219 | REMOTE_PTR CordbUnmanagedThread::GetEETlsDataBlock() |
3220 | { |
3221 | REMOTE_PTR ret; |
3222 | |
3223 | REMOTE_PTR tlsDataAddress; |
3224 | HRESULT hr = GetClrModuleTlsDataAddress(&tlsDataAddress); |
3225 | if (FAILED(hr)) |
3226 | { |
3227 | LOG((LF_CORDB, LL_INFO1000, "CUT::GEETDB: GetClrModuleTlsDataAddress FAILED %x for 0x%x\n" , hr, m_id)); |
3228 | return NULL; |
3229 | } |
3230 | |
3231 | REMOTE_PTR blockAddr = (BYTE*)tlsDataAddress + OFFSETOF__TLS__tls_EETlsData; |
3232 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(blockAddr), &ret); |
3233 | if (FAILED(hr)) |
3234 | { |
3235 | LOG((LF_CORDB, LL_INFO1000, "CUT::GEETDB: failed to read EETlsData address: computed addr=0x%p offset=%d, err=%x\n" , |
3236 | blockAddr, OFFSETOF__TLS__tls_EETlsData, hr)); |
3237 | return NULL; |
3238 | } |
3239 | |
3240 | LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETDB: EETlsData address value is 0x%p for 0x%x\n" , ret, m_id)); |
3241 | return ret; |
3242 | } |
3243 | |
3244 | /* |
3245 | * GetEEDebuggerWord |
3246 | * |
3247 | * This routine returns the value read from the thread |
3248 | * |
3249 | * Parameters: |
3250 | * pValue - Location to store value. |
3251 | * |
3252 | * Returns: |
3253 | * E_INVALIDARG, E_FAIL, S_OK |
3254 | */ |
3255 | HRESULT CordbUnmanagedThread::GetEEDebuggerWord(REMOTE_PTR *pValue) |
3256 | { |
3257 | LOG((LF_CORDB, LL_INFO1000, "CUT::GEEDW: Entered\n" )); |
3258 | if (pValue == NULL) |
3259 | { |
3260 | return E_INVALIDARG; |
3261 | } |
3262 | return GetTlsSlot(GetProcess()->m_runtimeOffsets.m_debuggerWordTLSIndex, pValue); |
3263 | } |
3264 | |
3265 | // SetEEDebuggerWord |
3266 | // |
3267 | // This routine writes the value to the thread |
3268 | // |
3269 | // Parameters: |
3270 | // pValue - Value to write. |
3271 | // |
3272 | // Returns: |
3273 | // HRESULT failure code or S_OK |
3274 | // |
3275 | // Notes: |
3276 | // This function is very dangerous. See code:CordbUnmanagedThread::SetEETlsValue for why. |
3277 | HRESULT CordbUnmanagedThread::SetEEDebuggerWord(REMOTE_PTR value) |
3278 | { |
3279 | LOG((LF_CORDB, LL_INFO1000, "CUT::SEEDW: Entered - value is 0x%p\n" , value)); |
3280 | return SetTlsSlot(GetProcess()->m_runtimeOffsets.m_debuggerWordTLSIndex, value); |
3281 | } |
3282 | |
3283 | /* |
3284 | * GetEEThreadPtr |
3285 | * |
3286 | * This routine returns the value read from the thread |
3287 | * |
3288 | * Parameters: |
3289 | * ppEEThread - Location to store value. |
3290 | * |
3291 | * Returns: |
3292 | * E_INVALIDARG, E_FAIL, S_OK |
3293 | */ |
3294 | HRESULT CordbUnmanagedThread::GetEEThreadPtr(REMOTE_PTR *ppEEThread) |
3295 | { |
3296 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
3297 | |
3298 | if (ppEEThread == NULL) |
3299 | { |
3300 | return E_INVALIDARG; |
3301 | } |
3302 | |
3303 | *ppEEThread = (REMOTE_PTR)GetEEThreadValue(); |
3304 | |
3305 | return S_OK; |
3306 | } |
3307 | |
3308 | |
3309 | void CordbUnmanagedThread::GetEEState(bool *threadStepping, bool *specialManagedException) |
3310 | { |
3311 | REMOTE_PTR pEEThread; |
3312 | |
3313 | HRESULT hr = GetEEThreadPtr(&pEEThread); |
3314 | |
3315 | _ASSERTE(SUCCEEDED(hr)); |
3316 | _ASSERTE(pEEThread != NULL); |
3317 | |
3318 | *threadStepping = false; |
3319 | *specialManagedException = false; |
3320 | |
3321 | // Compute the address of the thread's state |
3322 | DebuggerIPCRuntimeOffsets *pRO = &(GetProcess()->m_runtimeOffsets); |
3323 | void *pEEThreadStateNC = (BYTE*) pEEThread + pRO->m_EEThreadStateNCOffset; |
3324 | |
3325 | // Grab the thread state out of the EE Thread. |
3326 | DWORD EEThreadStateNC; |
3327 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEThreadStateNC), &EEThreadStateNC); |
3328 | if (FAILED(hr)) |
3329 | { |
3330 | LOG((LF_CORDB, LL_INFO1000, "CUT::GEETS: failed to read thread state NC: 0x%p + 0x%x = 0x%p, err=%d\n" , |
3331 | pEEThread, pRO->m_EEThreadStateNCOffset, pEEThreadStateNC, GetLastError())); |
3332 | return; |
3333 | } |
3334 | |
3335 | LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETS: EE Thread state NC is 0x%08x\n" , EEThreadStateNC)); |
3336 | |
3337 | // Looks like we've got the state of the thread. |
3338 | *threadStepping = ((EEThreadStateNC & pRO->m_EEThreadSteppingStateMask) != 0); |
3339 | *specialManagedException = ((EEThreadStateNC & pRO->m_EEIsManagedExceptionStateMask) != 0); |
3340 | |
3341 | return; |
3342 | } |
3343 | |
3344 | // Currently, the EE manually tracks its "can't-stop" regions. This retrieves that manual tracking value. |
3345 | // @todo - This should eventually become deprecated since the Entire EE will be a can't-stop region. |
3346 | bool CordbUnmanagedThread::GetEEThreadCantStopHelper() |
3347 | { |
3348 | // Note: any failure to read memory is okay for this method. We simply say that the thread is not is a can't stop |
3349 | // state, and that's okay. |
3350 | |
3351 | REMOTE_PTR pEEThread; |
3352 | |
3353 | HRESULT hr = GetEEThreadPtr(&pEEThread); |
3354 | |
3355 | _ASSERTE(SUCCEEDED(hr)); |
3356 | _ASSERTE(pEEThread != NULL); |
3357 | |
3358 | // Compute the address of the thread's debugger word #1 |
3359 | DebuggerIPCRuntimeOffsets *pRO = &(GetProcess()->m_runtimeOffsets); |
3360 | void *pEEThreadCantStop = (BYTE*) pEEThread + pRO->m_EEThreadCantStopOffset; |
3361 | |
3362 | // Grab the debugger word #1 out of the EE Thread. |
3363 | DWORD EEThreadCantStop; |
3364 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEThreadCantStop), &EEThreadCantStop); |
3365 | |
3366 | if (FAILED(hr)) |
3367 | { |
3368 | LOG((LF_CORDB, LL_INFO1000, "CUT::GEETS: failed to read thread cant stop: 0x%08x + 0x%x = 0x%08x, err=%d\n" , |
3369 | pEEThread, pRO->m_EEThreadCantStopOffset, pEEThreadCantStop, GetLastError())); |
3370 | |
3371 | return false; |
3372 | } |
3373 | |
3374 | LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETS: EE Thread cant stop is 0x%08x\n" , EEThreadCantStop)); |
3375 | |
3376 | // Looks like we've got it. |
3377 | if (EEThreadCantStop != 0) |
3378 | return true; |
3379 | else |
3380 | return false; |
3381 | } |
3382 | |
3383 | |
3384 | // Is the thread in a "can't stop" region? |
3385 | // "Can't-Stop" regions include anything that's "inside" the runtime; ie, the runtime has some |
3386 | // synchronization mechanism that will halt this thread, and so we don't need to suspend it. |
3387 | // The interop debugger should leave anything in a can't-stop region alone and just let the runtime |
3388 | // handle it. |
3389 | bool CordbUnmanagedThread::IsCantStop() |
3390 | { |
3391 | CONTRACTL |
3392 | { |
3393 | NOTHROW; |
3394 | } |
3395 | CONTRACTL_END; |
3396 | |
3397 | // Definition of a can't stop region: |
3398 | // - Any "Special" thread that doesn't have an EE Thread (includes the real Helper Thread, |
3399 | // Concurrent GC thread, ThreadPool thread, etc). |
3400 | // - Any thread in Cooperative code. |
3401 | // - Any thread w/ a can't-stop count > 0. |
3402 | // - Any thread holding a "Debugger" Crst. (This is actually a subset of the |
3403 | // can't-stop count b/c Enter/Leave adjust that count). |
3404 | // - Any generic, first chance or RaiseException hijacked thread |
3405 | |
3406 | // If the runtime isn't init yet, not a can't-stop. |
3407 | // We don't even have the DCB yet. |
3408 | if (!GetProcess()->m_initialized) |
3409 | { |
3410 | return false; |
3411 | } |
3412 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
3413 | |
3414 | if (IsRaiseExceptionHijacked()) |
3415 | { |
3416 | return true; |
3417 | } |
3418 | |
3419 | REMOTE_PTR pEEThread; |
3420 | HRESULT hr = this->GetEEThreadPtr(&pEEThread); |
3421 | if (FAILED(hr)) |
3422 | { |
3423 | _ASSERTE(!"Failed to EEThreadPtr in IsCantStop" ); |
3424 | return true; |
3425 | } |
3426 | |
3427 | DebuggerIPCRuntimeOffsets *pRO = &(GetProcess()->m_runtimeOffsets); |
3428 | |
3429 | // @todo- remove this and use the CantStop index below. |
3430 | // Is this a "special" thread? |
3431 | // Any thread that can take CLR locks w/o having an EE Thread object should |
3432 | // be marked as special. These threads are in "can't-stop" regions b/c if we suspend |
3433 | // them, they may be holding a lock that blocks the helper thread. |
3434 | // The helper thread is marked as "special". |
3435 | { |
3436 | SIZE_T idx = pRO->m_TLSIsSpecialIndex; |
3437 | REMOTE_PTR special = GetPreDefTlsSlot(idx, NULL); |
3438 | |
3439 | // If it's a special thread |
3440 | if ((special != 0) && (pEEThread == NULL)) |
3441 | { |
3442 | return true; |
3443 | } |
3444 | } |
3445 | |
3446 | // Check for CantStop regions off the FLS. |
3447 | // This is the biggest way to describe can't-stop regions when we're in preemptive mode |
3448 | // (or when we don't have a thread object). |
3449 | // If a LS thread takes a debugger lock, it will increment the Can't-Stop count. |
3450 | { |
3451 | SIZE_T idx = pRO->m_TLSCantStopIndex; |
3452 | REMOTE_PTR count = (REMOTE_PTR) GetPreDefTlsSlot(idx, NULL); |
3453 | |
3454 | // Just a sanity check here. There's nothing special about 1000, but if the |
3455 | // stop-count gets this big, 99% chance it's: |
3456 | // - we're accessing the wrong memory (an issue) |
3457 | // - someone on the LS is leaking stop-counts. (an issue). |
3458 | _ASSERTE(count < (REMOTE_PTR)1000); |
3459 | |
3460 | if (count > 0) |
3461 | { |
3462 | LOG((LF_CORDB, LL_INFO1000000, "Thread 0x%x is can't-stop b/c count=%d\n" , m_id, count)); |
3463 | return true; |
3464 | } |
3465 | } |
3466 | |
3467 | EX_TRY |
3468 | { |
3469 | GetProcess()->UpdateRightSideDCB(); |
3470 | } |
3471 | EX_CATCH |
3472 | { |
3473 | _ASSERTE(!"IsCantStop: Failed updating debugger control block" ); |
3474 | } |
3475 | EX_END_CATCH(SwallowAllExceptions); |
3476 | |
3477 | // Helper's canary thread is always can't-stop. |
3478 | if (this->m_id == GetProcess()->GetDCB()->m_CanaryThreadId) |
3479 | { |
3480 | return true; |
3481 | } |
3482 | |
3483 | // Check helper thread / or anyone pretending to be the helper thread. |
3484 | if ((this->m_id == GetProcess()->GetDCB()->m_helperThreadId) || |
3485 | (this->m_id == GetProcess()->GetDCB()->m_temporaryHelperThreadId) || |
3486 | (this->m_id == GetProcess()->m_helperThreadId)) |
3487 | { |
3488 | return true; |
3489 | } |
3490 | |
3491 | if (IsGenericHijacked() || IsFirstChanceHijacked()) |
3492 | return true; |
3493 | |
3494 | // If this isn't a EE thread (and not the helper thread, and not hijacked), then it's ok to stop. |
3495 | if (pEEThread == NULL) |
3496 | return false; |
3497 | |
3498 | // This checks for an explicit "can't" stop region. |
3499 | // Eventually, these explicit regions should become a complete subset of the other checks. |
3500 | if (GetEEThreadCantStopHelper()) |
3501 | return true; |
3502 | |
3503 | |
3504 | // If we're in cooperative mode (either managed code or parts inside the runtime), then don't stop. |
3505 | // Note we could remove this since the check is made in side of the DAC request below, |
3506 | // but it's faster to look here. |
3507 | if (GetEEPGCDisabled()) |
3508 | return true; |
3509 | |
3510 | return false; |
3511 | } |
3512 | |
3513 | bool CordbUnmanagedThread::GetEEPGCDisabled() |
3514 | { |
3515 | // Note: any failure to read memory is okay for this method. We simply say that the thread has PGC disabled, which |
3516 | // is always the worst case scenario. |
3517 | |
3518 | REMOTE_PTR pEEThread; |
3519 | |
3520 | HRESULT hr = GetEEThreadPtr(&pEEThread); |
3521 | |
3522 | _ASSERTE(SUCCEEDED(hr)); |
3523 | |
3524 | // Compute the address of the thread's PGC disabled word |
3525 | DebuggerIPCRuntimeOffsets *pRO = &(GetProcess()->m_runtimeOffsets); |
3526 | void *pEEThreadPGCDisabled = (BYTE*) pEEThread + pRO->m_EEThreadPGCDisabledOffset; |
3527 | |
3528 | // Grab the PGC disabled word out of the EE Thread. |
3529 | DWORD EEThreadPGCDisabled; |
3530 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEThreadPGCDisabled), &EEThreadPGCDisabled); |
3531 | |
3532 | if (FAILED(hr)) |
3533 | { |
3534 | LOG((LF_CORDB, LL_INFO1000, "CUT::GEETS: failed to read thread PGC Disabled: 0x%p + 0x%x = 0x%p, err=%d\n" , |
3535 | pEEThread, pRO->m_EEThreadPGCDisabledOffset, pEEThreadPGCDisabled, GetLastError())); |
3536 | |
3537 | return true; |
3538 | } |
3539 | |
3540 | LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETS: EE Thread PGC Disabled is 0x%08x\n" , EEThreadPGCDisabled)); |
3541 | |
3542 | // Looks like we've got it. |
3543 | if (EEThreadPGCDisabled == pRO->m_EEThreadPGCDisabledValue) |
3544 | return true; |
3545 | else |
3546 | return false; |
3547 | } |
3548 | |
3549 | bool CordbUnmanagedThread::GetEEFrame() |
3550 | { |
3551 | REMOTE_PTR pEEThread; |
3552 | |
3553 | HRESULT hr = GetEEThreadPtr(&pEEThread); |
3554 | |
3555 | _ASSERTE(SUCCEEDED(hr)); |
3556 | _ASSERTE(pEEThread != NULL); |
3557 | |
3558 | // Compute the address of the thread's frame ptr |
3559 | DebuggerIPCRuntimeOffsets *pRO = &(GetProcess()->m_runtimeOffsets); |
3560 | void *pEEThreadFrame = (BYTE*) pEEThread + pRO->m_EEThreadFrameOffset; |
3561 | |
3562 | // Grab the thread's frame out of the EE Thread. |
3563 | DWORD EEThreadFrame; |
3564 | hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEThreadFrame), &EEThreadFrame); |
3565 | |
3566 | if (FAILED(hr)) |
3567 | { |
3568 | LOG((LF_CORDB, LL_INFO1000, "CUT::GEETF: failed to read thread frame: 0x%p + 0x%x = 0x%p, err=%d\n" , |
3569 | pEEThread, pRO->m_EEThreadFrameOffset, pEEThreadFrame, GetLastError())); |
3570 | |
3571 | return false; |
3572 | } |
3573 | |
3574 | LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETF: EE Thread's frame is 0x%08x\n" , EEThreadFrame)); |
3575 | |
3576 | // Looks like we've got the frame of the thread. |
3577 | if (EEThreadFrame != pRO->m_EEMaxFrameValue) |
3578 | return true; |
3579 | else |
3580 | return false; |
3581 | } |
3582 | |
3583 | // Gets the thread context as if the thread were unhijacked, regardless |
3584 | // of whether it really is |
3585 | HRESULT CordbUnmanagedThread::GetThreadContext(DT_CONTEXT* pContext) |
3586 | { |
3587 | // While hijacked there are 3 potential contexts we could be resuming back to |
3588 | // 1) A context provided in SetThreadContext that we defered applying |
3589 | // 2) The LS copy of the context on the stack being modified in the handler |
3590 | // 3) The original context present when the hijack was started |
3591 | // |
3592 | // Both #1 and #3 are stored in the GetHijackCtx() space so of course you can't |
3593 | // have them both. You have have #1 if IsContextSet() is true, otherwise it holds #3 |
3594 | // |
3595 | // GenericHijack, FirstChanceHijackForSync, and RaiseExceptionHijack use #1 if available |
3596 | // and fallback to #3 if not. In other words they use GetHijackCtx() regardless of which thing it holds |
3597 | // M2UHandoff uses #1 if available and then falls back to #2. |
3598 | // |
3599 | // The reasoning here is that the first three hijacks are intended to be transparent. Since |
3600 | // the debugger shouldn't know they are occuring then it shouldn't see changes potentially |
3601 | // made on the LS. The M2UHandoff is not transparent, it has to update the context in order |
3602 | // to get clear of a bp. |
3603 | // |
3604 | // If not hijacked call the normal Win32 function. |
3605 | |
3606 | HRESULT hr = S_OK; |
3607 | |
3608 | LOG((LF_CORDB, LL_INFO10000, "CUT::GTC: thread=0x%p, flags=0x%x.\n" , this, pContext->ContextFlags)); |
3609 | |
3610 | if(IsContextSet() || IsGenericHijacked() || (IsFirstChanceHijacked() && IsBlockingForSync()) |
3611 | || IsRaiseExceptionHijacked()) |
3612 | { |
3613 | _ASSERTE(IsFirstChanceHijacked() || IsGenericHijacked() || IsRaiseExceptionHijacked()); |
3614 | LOG((LF_CORDB, LL_INFO10000, "CUT::GTC: hijackCtx case IsContextSet=%d IsGenericHijacked=%d" |
3615 | "HijackedForSync=%d RaiseExceptionHijacked=%d.\n" , |
3616 | IsContextSet(), IsGenericHijacked(), IsBlockingForSync(), IsRaiseExceptionHijacked())); |
3617 | LOG((LF_CORDB, LL_INFO10000, "CUT::GTC: hijackCtx is:\n" )); |
3618 | LogContext(GetHijackCtx()); |
3619 | CORDbgCopyThreadContext(pContext, GetHijackCtx()); |
3620 | } |
3621 | // use the LS for M2UHandoff |
3622 | else if (IsFirstChanceHijacked() && !IsBlockingForSync()) |
3623 | { |
3624 | LOG((LF_CORDB, LL_INFO10000, "CUT::GTC: getting LS context for first chance hijack, addr=0x%08x.\n" , |
3625 | m_pLeftSideContext.UnsafeGet())); |
3626 | |
3627 | // Read the context into a temp context then copy to the out param. |
3628 | DT_CONTEXT tempContext = { 0 }; |
3629 | |
3630 | hr = GetProcess()->SafeReadThreadContext(m_pLeftSideContext, &tempContext); |
3631 | |
3632 | if (SUCCEEDED(hr)) |
3633 | CORDbgCopyThreadContext(pContext, &tempContext); |
3634 | } |
3635 | // no hijack in place so just call straight through |
3636 | else |
3637 | { |
3638 | LOG((LF_CORDB, LL_INFO10000, "CUT::GTC: getting context from win32.\n" )); |
3639 | |
3640 | BOOL succ = DbiGetThreadContext(m_handle, pContext); |
3641 | |
3642 | if (!succ) |
3643 | hr = HRESULT_FROM_GetLastError(); |
3644 | } |
3645 | |
3646 | if(IsSSFlagHidden()) |
3647 | { |
3648 | UnsetSSFlag(pContext); |
3649 | } |
3650 | LogContext(pContext); |
3651 | |
3652 | return hr; |
3653 | } |
3654 | |
3655 | // Sets the thread context as if the thread were unhijacked, regardless |
3656 | // of whether it really is. See GetThreadContext above for more details |
3657 | // on this abstraction |
3658 | HRESULT CordbUnmanagedThread::SetThreadContext(DT_CONTEXT* pContext) |
3659 | { |
3660 | HRESULT hr = S_OK; |
3661 | |
3662 | LOG((LF_CORDB, LL_INFO10000, |
3663 | "CUT::STC: thread=0x%p, flags=0x%x.\n" , this, pContext->ContextFlags)); |
3664 | |
3665 | LogContext(pContext); |
3666 | |
3667 | // If the thread is first chance hijacked, then write the context into the remote process. If the thread is generic |
3668 | // hijacked, then update the copy of the context that we already have. Otherwise call the normal Win32 function. |
3669 | |
3670 | if (IsGenericHijacked() || IsFirstChanceHijacked() || IsRaiseExceptionHijacked()) |
3671 | { |
3672 | if(IsGenericHijacked()) |
3673 | { |
3674 | LOG((LF_CORDB, LL_INFO10000, "CUT::STC: setting context from generic/2nd chance hijack.\n" )); |
3675 | } |
3676 | else if(IsFirstChanceHijacked()) |
3677 | { |
3678 | LOG((LF_CORDB, LL_INFO10000, "CUT::STC: setting context from 1st chance hijack.\n" )); |
3679 | } |
3680 | else |
3681 | { |
3682 | LOG((LF_CORDB, LL_INFO10000, "CUT::STC: setting context from RaiseException hijack.\n" )); |
3683 | } |
3684 | SetState(CUTS_HasContextSet); |
3685 | CORDbgCopyThreadContext(GetHijackCtx(), pContext); |
3686 | } |
3687 | else |
3688 | { |
3689 | LOG((LF_CORDB, LL_INFO10000, "CUT::STC: setting context from win32.\n" )); |
3690 | |
3691 | // If the user is also setting the SS flag then we no longer have to hide it |
3692 | if(IsSSFlagEnabled(pContext)) |
3693 | { |
3694 | ClearState(CUTS_IsSSFlagHidden); |
3695 | } |
3696 | // if the user is turning off the SS flag but we still want it on then leave it on |
3697 | // but hidden |
3698 | if(!IsSSFlagEnabled(pContext) && IsSSFlagNeeded()) |
3699 | { |
3700 | SetState(CUTS_IsSSFlagHidden); |
3701 | SetSSFlag(pContext); |
3702 | } |
3703 | |
3704 | BOOL succ = DbiSetThreadContext(m_handle, pContext); |
3705 | |
3706 | if (!succ) |
3707 | { |
3708 | hr = HRESULT_FROM_GetLastError(); |
3709 | } |
3710 | } |
3711 | |
3712 | return hr; |
3713 | } |
3714 | |
3715 | // Turns on the stepping flag internally and tracks whether or not the flag |
3716 | // should also be seen by the user |
3717 | VOID CordbUnmanagedThread::BeginStepping() |
3718 | { |
3719 | _ASSERTE(!IsGenericHijacked() && !IsFirstChanceHijacked()); |
3720 | _ASSERTE(!IsSSFlagNeeded()); |
3721 | _ASSERTE(!IsSSFlagHidden()); |
3722 | |
3723 | DT_CONTEXT tempContext; |
3724 | tempContext.ContextFlags = DT_CONTEXT_FULL; |
3725 | BOOL succ = DbiGetThreadContext(m_handle, &tempContext); |
3726 | _ASSERTE(succ); |
3727 | |
3728 | if(!IsSSFlagEnabled(&tempContext)) |
3729 | { |
3730 | SetSSFlag(&tempContext); |
3731 | SetState(CUTS_IsSSFlagHidden); |
3732 | } |
3733 | SetState(CUTS_IsSSFlagNeeded); |
3734 | |
3735 | succ = DbiSetThreadContext(m_handle, &tempContext); |
3736 | _ASSERTE(succ); |
3737 | } |
3738 | |
3739 | // Turns off the stepping flag internally. If the user was also not using it then |
3740 | // the flag is turned off on the context |
3741 | VOID CordbUnmanagedThread::EndStepping() |
3742 | { |
3743 | _ASSERTE(!IsGenericHijacked() && !IsFirstChanceHijacked()); |
3744 | _ASSERTE(IsSSFlagNeeded()); |
3745 | |
3746 | DT_CONTEXT tempContext; |
3747 | tempContext.ContextFlags = DT_CONTEXT_FULL; |
3748 | BOOL succ = DbiGetThreadContext(m_handle, &tempContext); |
3749 | _ASSERTE(succ); |
3750 | |
3751 | if(IsSSFlagHidden()) |
3752 | { |
3753 | UnsetSSFlag(&tempContext); |
3754 | ClearState(CUTS_IsSSFlagHidden); |
3755 | } |
3756 | ClearState(CUTS_IsSSFlagNeeded); |
3757 | |
3758 | succ = DbiSetThreadContext(m_handle, &tempContext); |
3759 | _ASSERTE(succ); |
3760 | } |
3761 | |
3762 | |
3763 | // Writes some details of the given context into the debugger log |
3764 | VOID CordbUnmanagedThread::LogContext(DT_CONTEXT* pContext) |
3765 | { |
3766 | #if defined(DBG_TARGET_X86) |
3767 | LOG((LF_CORDB, LL_INFO10000, |
3768 | "CUT::LC: Eip=0x%08x, Esp=0x%08x, Eflags=0x%08x\n" , pContext->Eip, pContext->Esp, |
3769 | pContext->EFlags)); |
3770 | #elif defined(DBG_TARGET_AMD64) |
3771 | LOG((LF_CORDB, LL_INFO10000, |
3772 | "CUT::LC: Rip=" FMT_ADDR ", Rsp=" FMT_ADDR ", Eflags=0x%08x\n" , |
3773 | DBG_ADDR(pContext->Rip), |
3774 | DBG_ADDR(pContext->Rsp), |
3775 | pContext->EFlags)); // EFlags is still 32bits on AMD64 |
3776 | #elif defined(DBG_TARGET_ARM64) |
3777 | LOG((LF_CORDB, LL_INFO10000, |
3778 | "CUT::LC: Pc=" FMT_ADDR ", Sp=" FMT_ADDR ", Lr=" FMT_ADDR ", Cpsr=" FMT_ADDR "\n" , |
3779 | DBG_ADDR(pContext->Pc), |
3780 | DBG_ADDR(pContext->Sp), |
3781 | DBG_ADDR(pContext->Lr), |
3782 | DBG_ADDR(pContext->Cpsr))); |
3783 | #else // DBG_TARGET_X86 |
3784 | PORTABILITY_ASSERT("LogContext needs a PC and stack pointer." ); |
3785 | #endif // DBG_TARGET_X86 |
3786 | } |
3787 | |
3788 | // Hijacks this thread using the FirstChanceSuspend hijack |
3789 | HRESULT CordbUnmanagedThread::SetupFirstChanceHijackForSync() |
3790 | { |
3791 | HRESULT hr = S_OK; |
3792 | |
3793 | CONSISTENCY_CHECK(!IsBlockingForSync()); // Shouldn't double hijack |
3794 | CONSISTENCY_CHECK(!IsCantStop()); // must be in stoppable-region. |
3795 | _ASSERTE(HasIBEvent()); |
3796 | |
3797 | // We used to hijack for real here but now we have a vectored exception handler that will always be |
3798 | // triggered. So we don't have hijack in the sense that we overwrite the thread's IP. However we still |
3799 | // set the flag so that when we receive the HijackStartedSignal from the LS we know that this thread |
3800 | // should block in there rather than continuing. |
3801 | //hr = SetupFirstChanceHijack(EHijackReason::kFirstChanceSuspend, &(IBEvent()->m_currentDebugEvent.u.Exception.ExceptionRecord)); |
3802 | |
3803 | _ASSERTE(!IsFirstChanceHijacked()); |
3804 | _ASSERTE(!IsGenericHijacked()); |
3805 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
3806 | |
3807 | // We'd better not be hijacking in a can't stop region! |
3808 | // This also means we can't hijack in coopeative (since that's a can't-stop) |
3809 | _ASSERTE(!IsCantStop()); |
3810 | |
3811 | // we should not be stepping into hijacks |
3812 | _ASSERTE(!IsSSFlagHidden()); |
3813 | _ASSERTE(!IsSSFlagNeeded()); |
3814 | _ASSERTE(!IsContextSet()); |
3815 | |
3816 | // snapshot the current context so we can start spoofing it |
3817 | LOG((LF_CORDB, LL_INFO10000, "CUT::SFCHFS: hijackCtx started as:\n" )); |
3818 | LogContext(GetHijackCtx()); |
3819 | |
3820 | // Save the thread's full context. |
3821 | DT_CONTEXT context; |
3822 | context.ContextFlags = DT_CONTEXT_FULL; |
3823 | BOOL succ = DbiGetThreadContext(m_handle, &context); |
3824 | _ASSERTE(succ); |
3825 | // for debugging when GetThreadContext fails |
3826 | if(!succ) |
3827 | { |
3828 | DWORD error = GetLastError(); |
3829 | LOG((LF_CORDB, LL_ERROR, "CUT::SFCHFS: DbiGetThreadContext error=0x%x\n" , error)); |
3830 | } |
3831 | |
3832 | GetHijackCtx()->ContextFlags = DT_CONTEXT_FULL; |
3833 | CORDbgCopyThreadContext(GetHijackCtx(), &context); |
3834 | LOG((LF_CORDB, LL_INFO10000, "CUT::SFCHFS: thread=0x%x Hijacking for sync. Original context is:\n" , this)); |
3835 | LogContext(GetHijackCtx()); |
3836 | |
3837 | // We're hijacking now... |
3838 | SetState(CUTS_FirstChanceHijacked); |
3839 | GetProcess()->m_state |= CordbProcess::PS_HIJACKS_IN_PLACE; |
3840 | |
3841 | // We'll decrement this once the hijack returns |
3842 | GetProcess()->m_cFirstChanceHijackedThreads++; |
3843 | this->SetState(CUTS_BlockingForSync); |
3844 | |
3845 | // we don't want to single step into the vectored exception handler |
3846 | // we will restore the SS flag after returning from the hijack |
3847 | if(IsSSFlagEnabled(&context)) |
3848 | { |
3849 | LOG((LF_CORDB, LL_INFO10000, "CUT::SFCHFS: thread=0x%x Clearing SS flag\n" , this)); |
3850 | UnsetSSFlag(&context); |
3851 | succ = DbiSetThreadContext(m_handle, &context); |
3852 | _ASSERTE(succ); |
3853 | } |
3854 | |
3855 | |
3856 | |
3857 | // There's a bizarre race where the thread was suspended right as the thread was about to dispatch a |
3858 | // debug event. We still get the debug event, and then may try to hijack. Resume the thread so that |
3859 | // it can run to the hijack. |
3860 | if (this->IsSuspended()) |
3861 | { |
3862 | LOG((LF_CORDB, LL_ERROR, "CUT::SFCHFS: thread was suspended... resuming\n" )); |
3863 | DWORD success = ResumeThread(this->m_handle); |
3864 | |
3865 | if (success == 0xFFFFFFFF) |
3866 | { |
3867 | // Since we suspended it, we should be able to resume it in this window. |
3868 | CONSISTENCY_CHECK_MSGF(false, ("Failed to resume thread: tid=0x%x!" , this->m_id)); |
3869 | } |
3870 | else |
3871 | { |
3872 | this->ClearState(CUTS_Suspended); |
3873 | } |
3874 | } |
3875 | |
3876 | return hr; |
3877 | |
3878 | } |
3879 | |
3880 | HRESULT CordbUnmanagedThread::SetupFirstChanceHijack(EHijackReason::EHijackReason reason, const EXCEPTION_RECORD * pExceptionRecord) |
3881 | { |
3882 | _ASSERTE(!IsFirstChanceHijacked()); |
3883 | _ASSERTE(!IsGenericHijacked()); |
3884 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
3885 | |
3886 | // We'd better not be hijacking in a can't stop region! |
3887 | // This also means we can't hijack in coopeative (since that's a can't-stop) |
3888 | _ASSERTE(!IsCantStop()); |
3889 | |
3890 | // we should not be stepping into hijacks |
3891 | _ASSERTE(!IsSSFlagHidden()); |
3892 | _ASSERTE(!IsSSFlagNeeded()); |
3893 | |
3894 | // There's a bizarre race where the thread was suspended right as the thread was about to dispatch a |
3895 | // debug event. We still get the debug event, and then may try to hijack. Resume the thread so that |
3896 | // it can run to the hijack. |
3897 | if (this->IsSuspended()) |
3898 | { |
3899 | DWORD succ = ResumeThread(this->m_handle); |
3900 | |
3901 | if (succ == 0xFFFFFFFF) |
3902 | { |
3903 | // Since we suspended it, we should be able to resume it in this window. |
3904 | CONSISTENCY_CHECK_MSGF(false, ("Failed to resume thread: tid=0x%x!" , this->m_id)); |
3905 | } |
3906 | else |
3907 | { |
3908 | this->ClearState(CUTS_Suspended); |
3909 | } |
3910 | } |
3911 | |
3912 | HRESULT hr = S_OK; |
3913 | EX_TRY |
3914 | { |
3915 | // We save off the SEH handler on X86 to make sure we restore it properly after the hijack is complete |
3916 | // The hijacks don't return normally and the SEH chain might have handlers added that don't get removed by default |
3917 | #ifdef DBG_TARGET_X86 |
3918 | hr = SaveCurrentLeafSeh(); |
3919 | if(FAILED(hr)) |
3920 | ThrowHR(hr); |
3921 | #endif |
3922 | CORDB_ADDRESS LSContextAddr; |
3923 | GetProcess()->GetDAC()->Hijack(VMPTR_Thread::NullPtr(), |
3924 | GetOSTid(), |
3925 | pExceptionRecord, |
3926 | (T_CONTEXT*) GetHijackCtx(), |
3927 | sizeof(T_CONTEXT), |
3928 | reason, |
3929 | NULL, |
3930 | &LSContextAddr); |
3931 | LOG((LF_CORDB, LL_INFO10000, "CUT::SFCH: pLeftSideContext=0x%p\n" , LSContextAddr)); |
3932 | m_pLeftSideContext.Set(CORDB_ADDRESS_TO_PTR(LSContextAddr)); |
3933 | } |
3934 | EX_CATCH_HRESULT(hr); |
3935 | if(FAILED(hr)) |
3936 | { |
3937 | LOG((LF_CORDB, LL_INFO10000, "CUT::SFCH: Error setting up hijack context hr=0x%x\n" , hr)); |
3938 | return hr; |
3939 | } |
3940 | |
3941 | |
3942 | // We're hijacked now... |
3943 | SetState(CUTS_FirstChanceHijacked); |
3944 | GetProcess()->m_state |= CordbProcess::PS_HIJACKS_IN_PLACE; |
3945 | |
3946 | // We'll decrement this once the hijack returns |
3947 | GetProcess()->m_cFirstChanceHijackedThreads++; |
3948 | |
3949 | return S_OK; |
3950 | } |
3951 | |
3952 | HRESULT CordbUnmanagedThread::SetupGenericHijack(DWORD eventCode, const EXCEPTION_RECORD * pRecord) |
3953 | { |
3954 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
3955 | |
3956 | _ASSERTE(eventCode == EXCEPTION_DEBUG_EVENT); |
3957 | |
3958 | _ASSERTE(!IsFirstChanceHijacked()); |
3959 | _ASSERTE(!IsGenericHijacked()); |
3960 | _ASSERTE(!IsContextSet()); |
3961 | |
3962 | // Save the thread's full context. |
3963 | GetHijackCtx()->ContextFlags = DT_CONTEXT_FULL; |
3964 | |
3965 | BOOL succ = DbiGetThreadContext(m_handle, GetHijackCtx()); |
3966 | |
3967 | if (!succ) |
3968 | { |
3969 | LOG((LF_CORDB, LL_INFO1000, "CUT::SGH: couldn't get thread context: %d\n" , GetLastError())); |
3970 | return HRESULT_FROM_WIN32(GetLastError()); |
3971 | } |
3972 | |
3973 | #if defined(DBG_TARGET_AMD64) || defined(DBG_TARGET_ARM64) |
3974 | |
3975 | // On X86 Debugger::GenericHijackFunc() ensures the stack is walkable |
3976 | // by simply using the EBP chain, therefore we can execute the hijack |
3977 | // by setting the thread's context EIP to point to this function. |
3978 | // On X64, however, we first attempt to set up a "proper" hijack, with |
3979 | // a function that allows the OS to unwind the stack (ExceptionHijack). |
3980 | // If this fails we'll use the same method as on X86, even though the |
3981 | // stack will become un-walkable |
3982 | |
3983 | ULONG32 dwThreadId = GetOSTid(); |
3984 | CordbThread * pThread = GetProcess()->TryLookupOrCreateThreadByVolatileOSId(dwThreadId); |
3985 | |
3986 | // For threads in the thread store we set up the full size |
3987 | // hijack, otherwise we fallback to hijacking by SetIP. |
3988 | if (pThread != NULL) |
3989 | { |
3990 | HRESULT hr = S_OK; |
3991 | EX_TRY |
3992 | { |
3993 | // Note that the data-target is not atomic, and we have no rollback mechanism. |
3994 | // We have to do several writes. If the data-target fails the writes half-way through the |
3995 | // target will be inconsistent. |
3996 | GetProcess()->GetDAC()->Hijack( |
3997 | pThread->m_vmThreadToken, |
3998 | dwThreadId, |
3999 | pRecord, |
4000 | (T_CONTEXT*) GetHijackCtx(), |
4001 | sizeof(T_CONTEXT), |
4002 | EHijackReason::kGenericHijack, |
4003 | NULL, |
4004 | NULL); |
4005 | } |
4006 | EX_CATCH_HRESULT(hr); |
4007 | if (SUCCEEDED(hr)) |
4008 | { |
4009 | // Remember that we've hijacked the thread. |
4010 | SetState(CUTS_GenericHijacked); |
4011 | |
4012 | return S_OK; |
4013 | } |
4014 | |
4015 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CUT::SGH: Error setting up hijack context hr=0x%x\n" , hr); |
4016 | // fallthrough (above hijack might have failed due to stack overflow, for example) |
4017 | |
4018 | } |
4019 | // else (non-threadstore threads) fallthrough |
4020 | |
4021 | #endif // DBG_TARGET_AMD64 || defined(DBG_TARGET_ARM64) |
4022 | |
4023 | // Remember that we've hijacked the guy. |
4024 | SetState(CUTS_GenericHijacked); |
4025 | |
4026 | LOG((LF_CORDB, LL_INFO1000000, "CUT::SGH: Current IP is 0x%08x\n" , CORDbgGetIP(GetHijackCtx()))); |
4027 | |
4028 | DebuggerIPCRuntimeOffsets *pRO = &(GetProcess()->m_runtimeOffsets); |
4029 | |
4030 | // Wack the IP over to our generic hijack function. |
4031 | LPVOID holdIP = CORDbgGetIP(GetHijackCtx()); |
4032 | CORDbgSetIP(GetHijackCtx(), pRO->m_genericHijackFuncAddr); |
4033 | |
4034 | LOG((LF_CORDB, LL_INFO1000000, "CUT::SGH: New IP is 0x%08x\n" , CORDbgGetIP(GetHijackCtx()))); |
4035 | |
4036 | // We should never single step into the hijack |
4037 | BOOL isSSFlagOn = IsSSFlagEnabled(GetHijackCtx()); |
4038 | if(isSSFlagOn) |
4039 | { |
4040 | UnsetSSFlag(GetHijackCtx()); |
4041 | } |
4042 | |
4043 | succ = DbiSetThreadContext(m_handle, GetHijackCtx()); |
4044 | |
4045 | if (!succ) |
4046 | { |
4047 | LOG((LF_CORDB, LL_INFO1000, "CUT::SGH: couldn't set thread context: %d\n" , GetLastError())); |
4048 | |
4049 | return HRESULT_FROM_WIN32(GetLastError()); |
4050 | } |
4051 | |
4052 | // Put the original IP back into the local context copy for later. |
4053 | CORDbgSetIP(GetHijackCtx(), holdIP); |
4054 | // Set the original SS flag into the local context copy for later |
4055 | if(isSSFlagOn) |
4056 | { |
4057 | SetSSFlag(GetHijackCtx()); |
4058 | } |
4059 | return S_OK; |
4060 | } |
4061 | |
4062 | HRESULT CordbUnmanagedThread::FixupFromGenericHijack() |
4063 | { |
4064 | LOG((LF_CORDB, LL_INFO1000, "CUT::FFGH: fixing up from generic hijack. Eip=0x%p, Esp=0x%p\n" , |
4065 | CORDbgGetIP(GetHijackCtx()), CORDbgGetSP(GetHijackCtx()))); |
4066 | |
4067 | // We're no longer hijacked |
4068 | _ASSERTE(IsGenericHijacked()); |
4069 | ClearState(CUTS_GenericHijacked); |
4070 | |
4071 | // Clear the exception so we do a DBG_CONTINUE with the original context. Note: we only do generic hijacks on |
4072 | // in-band events. |
4073 | IBEvent()->SetState(CUES_ExceptionCleared); |
4074 | |
4075 | // Using the context we saved when the event came in originally or the new context if set by user, |
4076 | // reset the thread as if it were never hijacked. |
4077 | BOOL succ = DbiSetThreadContext(m_handle, GetHijackCtx()); |
4078 | // if the user set the context it has been applied now |
4079 | ClearState(CUTS_HasContextSet); |
4080 | |
4081 | if (!succ) |
4082 | { |
4083 | LOG((LF_CORDB, LL_INFO1000, "CUT::FFGH: couldn't set thread context: %d\n" , GetLastError())); |
4084 | |
4085 | return HRESULT_FROM_WIN32(GetLastError()); |
4086 | } |
4087 | |
4088 | return S_OK; |
4089 | } |
4090 | |
4091 | DT_CONTEXT * CordbUnmanagedThread::GetHijackCtx() |
4092 | { |
4093 | return &m_context; |
4094 | } |
4095 | |
4096 | |
4097 | // Enable Single-Step (and bump the eip back one) |
4098 | // This can only be called after a bp. (because we assume that we executed a bp when we adjust the eip). |
4099 | HRESULT CordbUnmanagedThread::EnableSSAfterBP() |
4100 | { |
4101 | DT_CONTEXT c; |
4102 | c.ContextFlags = DT_CONTEXT_FULL; |
4103 | |
4104 | BOOL succ = DbiGetThreadContext(m_handle, &c); |
4105 | |
4106 | if (!succ) |
4107 | return HRESULT_FROM_WIN32(GetLastError()); |
4108 | |
4109 | SetSSFlag(&c); |
4110 | |
4111 | // Backup IP to point to the instruction we need to execute. Continuing from a breakpoint exception |
4112 | // continues execution at the instruction after the breakpoint, but we need to continue where the |
4113 | // breakpoint was. |
4114 | CORDbgAdjustPCForBreakInstruction(&c); |
4115 | |
4116 | succ = DbiSetThreadContext(m_handle, &c); |
4117 | |
4118 | if (!succ) |
4119 | { |
4120 | return HRESULT_FROM_WIN32(GetLastError()); |
4121 | } |
4122 | |
4123 | return S_OK; |
4124 | } |
4125 | |
4126 | // |
4127 | // FixupAfterOOBException automatically gets the debuggee past an OOB exception event. These are only BP or SS |
4128 | // events. For SS, we just clear it, assuming that the only reason the thread was stepped in such place was to get it |
4129 | // off of a BP. For a BP, we clear and backup the IP by one, and turn the trace flag on under the assumption that the |
4130 | // only thing a debugger is allowed to do with an OOB BP exception is to get us off of it. |
4131 | // |
4132 | HRESULT CordbUnmanagedThread::FixupAfterOOBException(CordbUnmanagedEvent *ue) |
4133 | { |
4134 | // We really should only be doing things to single steps and breakpoint exceptions. |
4135 | if (ue->m_currentDebugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) |
4136 | { |
4137 | DWORD ec = ue->m_currentDebugEvent.u.Exception.ExceptionRecord.ExceptionCode; |
4138 | |
4139 | if ((ec == STATUS_BREAKPOINT) || (ec == STATUS_SINGLE_STEP)) |
4140 | { |
4141 | // Automatically clear the exception. |
4142 | ue->SetState(CUES_ExceptionCleared); |
4143 | |
4144 | // Don't bother about toggling the single-step flag. OOB BPs should only be called |
4145 | // for raw int3 instructions, so no need to rewind and reexecute. |
4146 | } |
4147 | } |
4148 | |
4149 | return S_OK; |
4150 | } |
4151 | |
4152 | |
4153 | //----------------------------------------------------------------------------- |
4154 | // Setup to skip an native breakpoint |
4155 | //----------------------------------------------------------------------------- |
4156 | void CordbUnmanagedThread::SetupForSkipBreakpoint(NativePatch * pNativePatch) |
4157 | { |
4158 | _ASSERTE(pNativePatch != NULL); |
4159 | _ASSERTE(!IsSkippingNativePatch()); |
4160 | _ASSERTE(m_pPatchSkipAddress == NULL); |
4161 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
4162 | |
4163 | SetState(CUTS_SkippingNativePatch); |
4164 | |
4165 | #ifdef _DEBUG |
4166 | // For debugging, provide a way that Cordbg devs can see if we're silently skipping BPs. |
4167 | static DWORD fTrapOnSkip = -1; |
4168 | if (fTrapOnSkip == -1) |
4169 | fTrapOnSkip = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgTrapOnSkip); |
4170 | |
4171 | if (fTrapOnSkip) |
4172 | { |
4173 | CONSISTENCY_CHECK_MSGF(false, ("The CLR is skipping a native BP at %p on thread 0x%x (%d)." |
4174 | "\nYou're getting this notification in debug builds b/c you have com+ var 'DbgTrapOnSkip' enabled." , |
4175 | pNativePatch->pAddress, this->m_id, this->m_id)); |
4176 | |
4177 | // We skipped this BP b/c IsCantStop was true. For debugging convenience, call IsCantStop here |
4178 | // (in case we break at the assert above and want to trace why we're in a CS region) |
4179 | bool fCantStop = this->IsCantStop(); |
4180 | LOG((LF_CORDB, LL_INFO1000, "In Can'tStopRegion = %d\n" , fCantStop)); |
4181 | |
4182 | // Refresh the reg key |
4183 | fTrapOnSkip = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgTrapOnSkip); |
4184 | } |
4185 | #endif |
4186 | #if defined(DBG_TARGET_X86) |
4187 | STRESS_LOG2(LF_CORDB, LL_INFO100, "CUT::SetupSkip. addr=%p. Opcode=%x\n" , pNativePatch->pAddress, (DWORD) pNativePatch->opcode); |
4188 | #endif |
4189 | |
4190 | // Replace the BP w/ the opcode. |
4191 | RemoveRemotePatch(GetProcess(), pNativePatch->pAddress, pNativePatch->opcode); |
4192 | |
4193 | // Enable the SS flag & Adjust IP. |
4194 | HRESULT hr = this->EnableSSAfterBP(); |
4195 | SIMPLIFYING_ASSUMPTION(SUCCEEDED(hr)); |
4196 | |
4197 | |
4198 | // Now we return, |
4199 | // Process continues, LS will single step past BP, and fire a SS exception. |
4200 | // When we get the SS, we res |
4201 | |
4202 | |
4203 | // We need to remember this so we can make sure we fixup at the proper address. |
4204 | // The address of a ss exception is the instruction we finish on, not where |
4205 | // we originally placed the BP. Since instructions can be variable length, |
4206 | // we can't work backwards. |
4207 | m_pPatchSkipAddress = pNativePatch->pAddress; |
4208 | } |
4209 | |
4210 | //----------------------------------------------------------------------------- |
4211 | // Second half of skipping a native bp. |
4212 | // Note we pass the address in b/c our caller has (from the debug_evet), and |
4213 | // we don't want to waste storage to remember it ourselves. |
4214 | //----------------------------------------------------------------------------- |
4215 | void CordbUnmanagedThread::FixupForSkipBreakpoint() |
4216 | { |
4217 | _ASSERTE(m_pPatchSkipAddress != NULL); |
4218 | _ASSERTE(IsSkippingNativePatch()); |
4219 | _ASSERTE(GetProcess()->ThreadHoldsProcessLock()); |
4220 | |
4221 | ClearState(CUTS_SkippingNativePatch); |
4222 | |
4223 | // Only reapply the int3 if it hasn't been removed yet. |
4224 | if (GetProcess()->GetNativePatch(m_pPatchSkipAddress) != NULL) |
4225 | { |
4226 | ApplyRemotePatch(GetProcess(), m_pPatchSkipAddress); |
4227 | STRESS_LOG1(LF_CORDB, LL_INFO100, "CUT::FixupSetupSkip. addr=%p\n" , m_pPatchSkipAddress); |
4228 | } |
4229 | else |
4230 | { |
4231 | STRESS_LOG1(LF_CORDB, LL_INFO100, "CUT::FixupSetupSkip. Patch removed. Not-reading. addr=%p\n" , m_pPatchSkipAddress); |
4232 | } |
4233 | |
4234 | m_pPatchSkipAddress = NULL; |
4235 | } |
4236 | |
4237 | inline TADDR GetSP(DT_CONTEXT* context) |
4238 | { |
4239 | #if defined(DBG_TARGET_X86) |
4240 | return (TADDR)context->Esp; |
4241 | #elif defined(DBG_TARGET_AMD64) |
4242 | return (TADDR)context->Rsp; |
4243 | #elif defined(DBG_TARGET_ARM) || defined(DBG_TARGET_ARM64) |
4244 | return (TADDR)context->Sp; |
4245 | #else |
4246 | _ASSERTE(!"nyi for platform" ); |
4247 | #endif |
4248 | } |
4249 | |
4250 | BOOL CordbUnmanagedThread::GetStackRange(CORDB_ADDRESS *pBase, CORDB_ADDRESS *pLimit) |
4251 | { |
4252 | #if !defined(FEATURE_DBGIPC_TRANSPORT) |
4253 | |
4254 | if (m_stackBase == 0 && m_stackLimit == 0) |
4255 | { |
4256 | HANDLE hProc; |
4257 | DT_CONTEXT tempContext; |
4258 | MEMORY_BASIC_INFORMATION mbi; |
4259 | |
4260 | tempContext.ContextFlags = DT_CONTEXT_FULL; |
4261 | if (SUCCEEDED(GetProcess()->GetHandle(&hProc)) && |
4262 | SUCCEEDED(GetThreadContext(&tempContext)) && |
4263 | ::VirtualQueryEx(hProc, (LPCVOID)GetSP(&tempContext), &mbi, sizeof(mbi)) != 0) |
4264 | { |
4265 | // the lowest stack address is the AllocationBase |
4266 | TADDR limit = PTR_TO_TADDR(mbi.AllocationBase); |
4267 | |
4268 | // Now, on to find the stack base: |
4269 | // Closest to the AllocationBase we might have a MEM_RESERVED block |
4270 | // for all the as yet unallocated pages... |
4271 | TADDR regionBase = limit; |
4272 | if (::VirtualQueryEx(hProc, (LPCVOID) regionBase, &mbi, sizeof(mbi)) == 0 |
4273 | || mbi.Type != MEM_PRIVATE) |
4274 | goto Exit; |
4275 | |
4276 | if (mbi.State == MEM_RESERVE) |
4277 | regionBase += mbi.RegionSize; |
4278 | |
4279 | // Next we might have a few guard pages |
4280 | if (::VirtualQueryEx(hProc, (LPCVOID) regionBase, &mbi, sizeof(mbi)) == 0 |
4281 | || mbi.Type != MEM_PRIVATE) |
4282 | goto Exit; |
4283 | |
4284 | if (mbi.State == MEM_COMMIT && (mbi.Protect & PAGE_GUARD) != 0) |
4285 | regionBase += mbi.RegionSize; |
4286 | |
4287 | // And finally the "regular" stack region |
4288 | if (::VirtualQueryEx(hProc, (LPCVOID) regionBase, &mbi, sizeof(mbi)) == 0 |
4289 | || mbi.Type != MEM_PRIVATE) |
4290 | goto Exit; |
4291 | |
4292 | if (mbi.State == MEM_COMMIT && (mbi.Protect & PAGE_READWRITE) != 0) |
4293 | regionBase += mbi.RegionSize; |
4294 | |
4295 | if (limit == regionBase) |
4296 | goto Exit; |
4297 | |
4298 | m_stackLimit = limit; |
4299 | m_stackBase = regionBase; |
4300 | } |
4301 | } |
4302 | |
4303 | Exit: |
4304 | if (pBase != NULL) |
4305 | *pBase = m_stackBase; |
4306 | if (pLimit != NULL) |
4307 | *pLimit = m_stackLimit; |
4308 | |
4309 | return (m_stackBase != 0 || m_stackLimit != 0); |
4310 | |
4311 | #else |
4312 | |
4313 | if (pBase != NULL) |
4314 | *pBase = 0; |
4315 | if (pLimit != NULL) |
4316 | *pLimit = 0; |
4317 | |
4318 | return FALSE; |
4319 | |
4320 | #endif // FEATURE_DBGIPC_TRANSPORT |
4321 | } |
4322 | |
4323 | //----------------------------------------------------------------------------- |
4324 | // Returns the thread context to the state it was in when it last entered RaiseException |
4325 | // This allows the thread to retrigger an exception caused by RaiseException |
4326 | //----------------------------------------------------------------------------- |
4327 | void CordbUnmanagedThread::HijackToRaiseException() |
4328 | { |
4329 | LOG((LF_CORDB, LL_INFO1000, "CP::HTRE: hijacking to RaiseException\n" )); |
4330 | _ASSERTE(HasRaiseExceptionEntryCtx()); |
4331 | _ASSERTE(!IsRaiseExceptionHijacked()); |
4332 | _ASSERTE(!IsGenericHijacked()); |
4333 | _ASSERTE(!IsFirstChanceHijacked()); |
4334 | _ASSERTE(!IsContextSet()); |
4335 | |
4336 | BOOL succ = DbiGetThreadContext(m_handle, GetHijackCtx()); |
4337 | _ASSERTE(succ); |
4338 | succ = DbiSetThreadContext(m_handle, &m_raiseExceptionEntryContext); |
4339 | _ASSERTE(succ); |
4340 | SetState(CUTS_IsRaiseExceptionHijacked); |
4341 | } |
4342 | |
4343 | //---------------------------------------------------------------------------- |
4344 | // Returns the context to its unhijacked state. |
4345 | //---------------------------------------------------------------------------- |
4346 | void CordbUnmanagedThread::RestoreFromRaiseExceptionHijack() |
4347 | { |
4348 | LOG((LF_CORDB, LL_INFO1000, "CP::RFREH: ending RaiseException hijack\n" )); |
4349 | _ASSERTE(IsRaiseExceptionHijacked()); |
4350 | |
4351 | DT_CONTEXT restoreContext; |
4352 | restoreContext.ContextFlags = DT_CONTEXT_FULL; |
4353 | HRESULT hr = GetThreadContext(&restoreContext); |
4354 | _ASSERTE(SUCCEEDED(hr)); |
4355 | |
4356 | ClearState(CUTS_IsRaiseExceptionHijacked); |
4357 | hr = SetThreadContext(&restoreContext); |
4358 | _ASSERTE(SUCCEEDED(hr)); |
4359 | } |
4360 | |
4361 | //----------------------------------------------------------------------------- |
4362 | // Attempts to store the state of a thread currently entering RaiseException |
4363 | // This grabs both a full context and enough state to determine what exception |
4364 | // RaiseException should be raising. If any of the state can not be retrieved |
4365 | // then this entrance to RaiseException is silently ignored |
4366 | //----------------------------------------------------------------------------- |
4367 | void CordbUnmanagedThread::SaveRaiseExceptionEntryContext() |
4368 | { |
4369 | _ASSERTE(FALSE); // should be unused now |
4370 | LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: saving raise exception context.\n" )); |
4371 | _ASSERTE(!HasRaiseExceptionEntryCtx()); |
4372 | _ASSERTE(!IsRaiseExceptionHijacked()); |
4373 | HRESULT hr = S_OK; |
4374 | DT_CONTEXT context; |
4375 | context.ContextFlags = DT_CONTEXT_FULL; |
4376 | DbiGetThreadContext(m_handle, &context); |
4377 | // if the flag is set, unset it |
4378 | // we don't want to be single stepping through RaiseException the second time |
4379 | // sending out OOB SS events. Ultimately we will rethrow the exception which would |
4380 | // cleared the SS flag anyways. |
4381 | UnsetSSFlag(&context); |
4382 | memcpy(&m_raiseExceptionEntryContext, &context, sizeof(DT_CONTEXT)); |
4383 | |
4384 | // calculate the exception that we would expect to come from this invocation of RaiseException |
4385 | REMOTE_PTR pExceptionInformation = NULL; |
4386 | #if defined(DBG_TARGET_AMD64) |
4387 | m_raiseExceptionExceptionCode = (DWORD)m_raiseExceptionEntryContext.Rcx; |
4388 | m_raiseExceptionExceptionFlags = (DWORD)m_raiseExceptionEntryContext.Rdx; |
4389 | m_raiseExceptionNumberParameters = (DWORD)m_raiseExceptionEntryContext.R8; |
4390 | pExceptionInformation = (REMOTE_PTR)m_raiseExceptionEntryContext.R9; |
4391 | #elif defined(DBG_TARGET_ARM64) |
4392 | m_raiseExceptionExceptionCode = (DWORD)m_raiseExceptionEntryContext.X0; |
4393 | m_raiseExceptionExceptionFlags = (DWORD)m_raiseExceptionEntryContext.X1; |
4394 | m_raiseExceptionNumberParameters = (DWORD)m_raiseExceptionEntryContext.X2; |
4395 | pExceptionInformation = (REMOTE_PTR)m_raiseExceptionEntryContext.X3; |
4396 | #elif defined(DBG_TARGET_X86) |
4397 | hr = m_pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_raiseExceptionEntryContext.Esp+4), &m_raiseExceptionExceptionCode); |
4398 | if(FAILED(hr)) |
4399 | { |
4400 | LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read exception code.\n" )); |
4401 | return; |
4402 | } |
4403 | hr = m_pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_raiseExceptionEntryContext.Esp+8), &m_raiseExceptionExceptionFlags); |
4404 | if(FAILED(hr)) |
4405 | { |
4406 | LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read exception flags.\n" )); |
4407 | return; |
4408 | } |
4409 | hr = m_pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_raiseExceptionEntryContext.Esp+12), &m_raiseExceptionNumberParameters); |
4410 | if(FAILED(hr)) |
4411 | { |
4412 | LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read number of parameters.\n" )); |
4413 | return; |
4414 | } |
4415 | hr = m_pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_raiseExceptionEntryContext.Esp+16), &pExceptionInformation); |
4416 | if(FAILED(hr)) |
4417 | { |
4418 | LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read exception information pointer.\n" )); |
4419 | return; |
4420 | } |
4421 | #else |
4422 | _ASSERTE(!"Implement this for your platform" ); |
4423 | return; |
4424 | #endif |
4425 | LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: RaiseException parameters are 0x%x 0x%x 0x%x 0x%p.\n" , |
4426 | m_raiseExceptionExceptionCode, m_raiseExceptionExceptionFlags, |
4427 | m_raiseExceptionNumberParameters, pExceptionInformation)); |
4428 | TargetBuffer exceptionInfoTargetBuffer(pExceptionInformation, sizeof(REMOTE_PTR)*m_raiseExceptionNumberParameters); |
4429 | EX_TRY |
4430 | { |
4431 | m_pProcess->SafeReadBuffer(exceptionInfoTargetBuffer, (BYTE*)m_raiseExceptionExceptionInformation); |
4432 | } |
4433 | EX_CATCH_HRESULT(hr); |
4434 | if(FAILED(hr)) |
4435 | { |
4436 | LOG((LF_CORDB, LL_INFO1000, "CP::SREEC: failed to read exception information.\n" )); |
4437 | return; |
4438 | } |
4439 | |
4440 | // If everything was succesful then set this flag, otherwise none of the above data is considered valid |
4441 | SetState(CUTS_HasRaiseExceptionEntryCtx); |
4442 | return; |
4443 | } |
4444 | |
4445 | //----------------------------------------------------------------------------- |
4446 | // Clears all the state saved in SaveRaiseExceptionContext and returns the thread |
4447 | // to the state as if RaiseException has yet to be called. This is typically called |
4448 | // after an exception retriggers or after determining that the exception never will |
4449 | // retrigger. |
4450 | //----------------------------------------------------------------------------- |
4451 | void CordbUnmanagedThread::ClearRaiseExceptionEntryContext() |
4452 | { |
4453 | _ASSERTE(FALSE); // should be unused now |
4454 | LOG((LF_CORDB, LL_INFO1000, "CP::CREEC: clearing raise exception context.\n" )); |
4455 | _ASSERTE(HasRaiseExceptionEntryCtx()); |
4456 | ClearState(CUTS_HasRaiseExceptionEntryCtx); |
4457 | } |
4458 | |
4459 | //----------------------------------------------------------------------------- |
4460 | // Uses a heuristic to determine if the given exception record is likely to be the exception |
4461 | // raised by the last invocation of RaiseException on this thread. The current heuristic compares |
4462 | // ExceptionCode, ExceptionFlags, and all ExceptionInformation. |
4463 | //----------------------------------------------------------------------------- |
4464 | BOOL CordbUnmanagedThread::IsExceptionFromLastRaiseException(const EXCEPTION_RECORD* pExceptionRecord) |
4465 | { |
4466 | _ASSERTE(FALSE); // should be unused now |
4467 | if(!HasRaiseExceptionEntryCtx()) |
4468 | { |
4469 | LOG((LF_CORDB, LL_INFO1000, "CP::IEFLRE: not a match - no previous raise context\n" )); |
4470 | return FALSE; |
4471 | } |
4472 | |
4473 | if (pExceptionRecord->ExceptionCode != m_raiseExceptionExceptionCode) |
4474 | { |
4475 | LOG((LF_CORDB, LL_INFO1000, "CP::IEFLRE: not a match - exception codes differ 0x%x 0x%x\n" , |
4476 | pExceptionRecord->ExceptionCode, m_raiseExceptionExceptionCode)); |
4477 | return FALSE; |
4478 | } |
4479 | |
4480 | if (pExceptionRecord->ExceptionFlags != m_raiseExceptionExceptionFlags) |
4481 | { |
4482 | LOG((LF_CORDB, LL_INFO1000, "CP::IEFLRE: not a match - exception flags differ 0x%x 0x%x\n" , |
4483 | pExceptionRecord->ExceptionFlags, m_raiseExceptionExceptionFlags)); |
4484 | return FALSE; |
4485 | } |
4486 | |
4487 | if (pExceptionRecord->NumberParameters != m_raiseExceptionNumberParameters) |
4488 | { |
4489 | LOG((LF_CORDB, LL_INFO1000, "CP::IEFLRE: not a match - number parameters differ 0x%x 0x%x\n" , |
4490 | pExceptionRecord->NumberParameters, m_raiseExceptionNumberParameters)); |
4491 | return FALSE; |
4492 | } |
4493 | |
4494 | for(DWORD i = 0; i < pExceptionRecord->NumberParameters; i++) |
4495 | { |
4496 | if(m_raiseExceptionExceptionInformation[i] != pExceptionRecord->ExceptionInformation[i]) |
4497 | { |
4498 | LOG((LF_CORDB, LL_INFO1000, "CP::IEFLRE: not a match - param %d differs 0x%x 0x%x\n" , |
4499 | i, pExceptionRecord->ExceptionInformation[i], m_raiseExceptionExceptionInformation[i])); |
4500 | return FALSE; |
4501 | } |
4502 | } |
4503 | |
4504 | LOG((LF_CORDB, LL_INFO1000, "CP::IEFLRE: match\n" )); |
4505 | return TRUE; |
4506 | } |
4507 | |
4508 | |
4509 | //----------------------------------------------------------------------------- |
4510 | // Inject an int3 at the given remote address |
4511 | //----------------------------------------------------------------------------- |
4512 | |
4513 | // This flavor is assuming our caller already knows the opcode. |
4514 | HRESULT ApplyRemotePatch(CordbProcess * pProcess, const void * pRemoteAddress) |
4515 | { |
4516 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64) |
4517 | const BYTE patch = CORDbg_BREAK_INSTRUCTION; |
4518 | #elif defined(DBG_TARGET_ARM64) |
4519 | const PRD_TYPE patch = CORDbg_BREAK_INSTRUCTION; |
4520 | #else |
4521 | const BYTE patch = 0; |
4522 | PORTABILITY_ASSERT("NYI: ApplyRemotePatch for this platform" ); |
4523 | #endif |
4524 | HRESULT hr = pProcess->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pRemoteAddress), &patch); |
4525 | SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr); |
4526 | return S_OK; |
4527 | } |
4528 | |
4529 | |
4530 | // Get the opcode that we're replacing. |
4531 | HRESULT ApplyRemotePatch(CordbProcess * pProcess, const void * pRemoteAddress, PRD_TYPE * pOpcode) |
4532 | { |
4533 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64) |
4534 | // Read out opcode. 1 byte on x86 |
4535 | BYTE opcode; |
4536 | #elif defined(DBG_TARGET_ARM64) |
4537 | // Read out opcode. 4 bytes on arm64 |
4538 | PRD_TYPE opcode; |
4539 | #else |
4540 | BYTE opcode; |
4541 | PORTABILITY_ASSERT("NYI: ApplyRemotePatch for this platform" ); |
4542 | #endif |
4543 | |
4544 | HRESULT hr = pProcess->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pRemoteAddress), &opcode); |
4545 | if (FAILED(hr)) |
4546 | { |
4547 | return hr; |
4548 | } |
4549 | |
4550 | *pOpcode = (PRD_TYPE) opcode; |
4551 | ApplyRemotePatch(pProcess, pRemoteAddress); |
4552 | return S_OK; |
4553 | } |
4554 | |
4555 | //----------------------------------------------------------------------------- |
4556 | // Remove the int3 from the remote address |
4557 | //----------------------------------------------------------------------------- |
4558 | HRESULT RemoveRemotePatch(CordbProcess * pProcess, const void * pRemoteAddress, PRD_TYPE opcode) |
4559 | { |
4560 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_AMD64) |
4561 | // Replace the BP w/ the opcode. |
4562 | BYTE opcode2 = (BYTE) opcode; |
4563 | #elif defined(DBG_TARGET_ARM64) |
4564 | // 4 bytes on arm64 |
4565 | PRD_TYPE opcode2 = opcode; |
4566 | #else |
4567 | PRD_TYPE opcode2 = opcode; |
4568 | PORTABILITY_ASSERT("NYI: RemoveRemotePatch for this platform" ); |
4569 | #endif |
4570 | |
4571 | pProcess->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pRemoteAddress), &opcode2); |
4572 | |
4573 | // This may fail because the module has been unloaded. In which case, the patch is also |
4574 | // gone so it makes sense to return success. |
4575 | return S_OK; |
4576 | } |
4577 | #endif // FEATURE_INTEROP_DEBUGGING |
4578 | |
4579 | //--------------------------------------------------------------------------------------- |
4580 | // |
4581 | // Simple helper to return the SP value stored in a DebuggerREGDISPLAY. |
4582 | // |
4583 | // Arguments: |
4584 | // pDRD - the DebuggerREGDISPLAY in question |
4585 | // |
4586 | // Return Value: |
4587 | // the SP value |
4588 | // |
4589 | |
4590 | inline CORDB_ADDRESS GetSPFromDebuggerREGDISPLAY(DebuggerREGDISPLAY* pDRD) |
4591 | { |
4592 | return pDRD->SP; |
4593 | } |
4594 | |
4595 | |
4596 | HRESULT CordbContext::QueryInterface(REFIID id, void **pInterface) |
4597 | { |
4598 | if (id == IID_ICorDebugContext) |
4599 | *pInterface = static_cast<ICorDebugContext*>(this); |
4600 | else if (id == IID_IUnknown) |
4601 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugContext*>(this)); |
4602 | else |
4603 | { |
4604 | *pInterface = NULL; |
4605 | return E_NOINTERFACE; |
4606 | } |
4607 | |
4608 | AddRef(); |
4609 | return S_OK; |
4610 | } |
4611 | |
4612 | |
4613 | /* ------------------------------------------------------------------------- * |
4614 | * Frame class |
4615 | * ------------------------------------------------------------------------- */ |
4616 | |
4617 | |
4618 | // This is just used as a proxy object to pass a FramePointer around. |
4619 | CordbFrame::CordbFrame(CordbProcess * pProcess, FramePointer fp) |
4620 | : CordbBase(pProcess, 0, enumCordbFrame), |
4621 | m_fp(fp) |
4622 | { |
4623 | UnsafeNeuterDeadObject(); // mark as neutered. |
4624 | } |
4625 | |
4626 | |
4627 | CordbFrame::CordbFrame(CordbThread * pThread, |
4628 | FramePointer fp, |
4629 | SIZE_T ip, |
4630 | CordbAppDomain * pCurrentAppDomain) |
4631 | : CordbBase(pThread->GetProcess(), 0, enumCordbFrame), |
4632 | m_ip(ip), |
4633 | m_pThread(pThread), |
4634 | m_currentAppDomain(pCurrentAppDomain), |
4635 | m_fp(fp) |
4636 | { |
4637 | #ifdef _DEBUG |
4638 | // For debugging purposes, track what Continue session these frames were created in. |
4639 | m_DbgContinueCounter = GetProcess()->m_continueCounter; |
4640 | #endif |
4641 | |
4642 | HRESULT hr = S_OK; |
4643 | EX_TRY |
4644 | { |
4645 | m_pThread->GetRefreshStackNeuterList()->Add(GetProcess(), this); |
4646 | } |
4647 | EX_CATCH_HRESULT(hr); |
4648 | SetUnrecoverableIfFailed(GetProcess(), hr); |
4649 | } |
4650 | |
4651 | |
4652 | CordbFrame::~CordbFrame() |
4653 | { |
4654 | _ASSERTE(IsNeutered()); |
4655 | } |
4656 | |
4657 | // Neutered by DerivedClasses |
4658 | void CordbFrame::Neuter() |
4659 | { |
4660 | CordbBase::Neuter(); |
4661 | } |
4662 | |
4663 | |
4664 | HRESULT CordbFrame::QueryInterface(REFIID id, void **pInterface) |
4665 | { |
4666 | if (id == IID_ICorDebugFrame) |
4667 | *pInterface = static_cast<ICorDebugFrame*>(this); |
4668 | else if (id == IID_IUnknown) |
4669 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugFrame*>(this)); |
4670 | else |
4671 | { |
4672 | *pInterface = NULL; |
4673 | return E_NOINTERFACE; |
4674 | } |
4675 | |
4676 | ExternalAddRef(); |
4677 | return S_OK; |
4678 | } |
4679 | |
4680 | // ---------------------------------------------------------------------------- |
4681 | // CordbFrame::GetChain |
4682 | // |
4683 | // Description: |
4684 | // Return the owning chain. Since chains have been deprecated in Arrowhead, |
4685 | // this function returns E_NOTIMPL unless there is a shim. |
4686 | // |
4687 | // Arguments: |
4688 | // * ppChain - out parameter; return the owning chain |
4689 | // |
4690 | // Return Value: |
4691 | // Return S_OK on success. |
4692 | // Return E_INVALIDARG if ppChain is NULL. |
4693 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbFrame is neutered. |
4694 | // Return E_NOTIMPL if there is no shim. |
4695 | // Return E_FAIL if failed to find the chain |
4696 | // |
4697 | |
4698 | HRESULT CordbFrame::GetChain(ICorDebugChain **ppChain) |
4699 | { |
4700 | HRESULT hr = S_OK; |
4701 | PUBLIC_REENTRANT_API_BEGIN(this) |
4702 | { |
4703 | ValidateOrThrow(ppChain); |
4704 | *ppChain = NULL; |
4705 | |
4706 | if (GetProcess()->GetShim() != NULL) |
4707 | { |
4708 | PUBLIC_CALLBACK_IN_THIS_SCOPE0(GetProcess(), GET_PUBLIC_LOCK_HOLDER()); |
4709 | ShimStackWalk * pSSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(m_pThread); |
4710 | pSSW->GetChainForFrame(static_cast<ICorDebugFrame *>(this), ppChain); |
4711 | |
4712 | if (*ppChain == NULL) |
4713 | hr = E_FAIL; |
4714 | } |
4715 | else |
4716 | { |
4717 | // This is the Arrowhead case, where ICDChain has been deprecated. |
4718 | hr = E_NOTIMPL; |
4719 | } |
4720 | } |
4721 | PUBLIC_REENTRANT_API_END(hr); |
4722 | return hr; |
4723 | } |
4724 | |
4725 | |
4726 | // Return the stack range taken up by this frame. |
4727 | // Note that this is not implemented in the base CordbFrame class. |
4728 | // Instead, this is implemented by the derived classes. |
4729 | // The start of the stack range is the leafmost boundary, and the end is the rootmost boundary. |
4730 | // |
4731 | // Notes: see code:#GetStackRange |
4732 | HRESULT CordbFrame::GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd) |
4733 | { |
4734 | HRESULT hr = S_OK; |
4735 | PUBLIC_REENTRANT_API_BEGIN(this) |
4736 | { |
4737 | ValidateOrThrow(pStart); |
4738 | ValidateOrThrow(pEnd); |
4739 | |
4740 | hr = E_NOTIMPL; |
4741 | } |
4742 | PUBLIC_REENTRANT_API_END(hr); |
4743 | return hr; |
4744 | } |
4745 | |
4746 | // Return the ICorDebugFunction associated with this frame. |
4747 | // There is one ICorDebugFunction for each EnC version of a method. |
4748 | HRESULT CordbFrame::GetFunction(ICorDebugFunction **ppFunction) |
4749 | { |
4750 | HRESULT hr = S_OK; |
4751 | PUBLIC_REENTRANT_API_BEGIN(this) |
4752 | { |
4753 | ValidateOrThrow(ppFunction); |
4754 | |
4755 | CordbFunction * pFunc = this->GetFunction(); |
4756 | |
4757 | if (pFunc == NULL) |
4758 | { |
4759 | ThrowHR(CORDBG_E_CODE_NOT_AVAILABLE); |
4760 | } |
4761 | |
4762 | // @dbgtodo LCG methods, IL stubs, dynamic language debugging |
4763 | // Don't return an ICDFunction if we are dealing with a dynamic method. |
4764 | // The dynamic debugging feature crew needs to decide exactly what to hand out for dynamic methods. |
4765 | if (pFunc->GetMetadataToken() == mdMethodDefNil) |
4766 | { |
4767 | ThrowHR(CORDBG_E_CODE_NOT_AVAILABLE); |
4768 | } |
4769 | |
4770 | *ppFunction = static_cast<ICorDebugFunction *>(pFunc); |
4771 | pFunc->ExternalAddRef(); |
4772 | } |
4773 | PUBLIC_REENTRANT_API_END(hr); |
4774 | return hr; |
4775 | } |
4776 | |
4777 | // Return the token of the ICorDebugFunction associated with this frame. |
4778 | // There is one ICorDebugFunction for each EnC version of a method. |
4779 | HRESULT CordbFrame::GetFunctionToken(mdMethodDef *pToken) |
4780 | { |
4781 | HRESULT hr = S_OK; |
4782 | PUBLIC_REENTRANT_API_BEGIN(this) |
4783 | { |
4784 | ValidateOrThrow(pToken); |
4785 | |
4786 | CordbFunction * pFunc = GetFunction(); |
4787 | if (pFunc == NULL) |
4788 | { |
4789 | hr = CORDBG_E_CODE_NOT_AVAILABLE; |
4790 | } |
4791 | else |
4792 | { |
4793 | *pToken = pFunc->GetMetadataToken(); |
4794 | } |
4795 | } |
4796 | PUBLIC_REENTRANT_API_END(hr); |
4797 | return hr; |
4798 | } |
4799 | |
4800 | // ---------------------------------------------------------------------------- |
4801 | // CordbFrame::GetCaller |
4802 | // |
4803 | // Description: |
4804 | // Return the caller of this frame. The caller is closer to the root. |
4805 | // This function has been deprecated in Arrowhead, and so it returns E_NOTIMPL unless there is a shim. |
4806 | // |
4807 | // Arguments: |
4808 | // * ppFrame - out parameter; return the caller frame |
4809 | // |
4810 | // Return Value: |
4811 | // Return S_OK on success. |
4812 | // Return E_INVALIDARG if ppFrame is NULL. |
4813 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbFrame is neutered. |
4814 | // Return E_NOTIMPL if there is no shim. |
4815 | // |
4816 | |
4817 | HRESULT CordbFrame::GetCaller(ICorDebugFrame **ppFrame) |
4818 | { |
4819 | HRESULT hr = S_OK; |
4820 | PUBLIC_REENTRANT_API_BEGIN(this) |
4821 | { |
4822 | ValidateOrThrow(ppFrame); |
4823 | |
4824 | *ppFrame = NULL; |
4825 | |
4826 | if (GetProcess()->GetShim() != NULL) |
4827 | { |
4828 | PUBLIC_CALLBACK_IN_THIS_SCOPE0(GetProcess(), GET_PUBLIC_LOCK_HOLDER()); |
4829 | ShimStackWalk * pSSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(m_pThread); |
4830 | pSSW->GetCallerForFrame(this, ppFrame); |
4831 | } |
4832 | else |
4833 | { |
4834 | *ppFrame = NULL; |
4835 | hr = E_NOTIMPL; |
4836 | } |
4837 | } |
4838 | PUBLIC_REENTRANT_API_END(hr); |
4839 | return hr; |
4840 | } |
4841 | |
4842 | // ---------------------------------------------------------------------------- |
4843 | // CordbFrame::GetCallee |
4844 | // |
4845 | // Description: |
4846 | // Return the callee of this frame. The callee is closer to the leaf. |
4847 | // This function has been deprecated in Arrowhead, and so it returns E_NOTIMPL unless there is a shim. |
4848 | // |
4849 | // Arguments: |
4850 | // * ppFrame - out parameter; return the callee frame |
4851 | // |
4852 | // Return Value: |
4853 | // Return S_OK on success. |
4854 | // Return E_INVALIDARG if ppFrame is NULL. |
4855 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbFrame is neutered. |
4856 | // Return E_NOTIMPL if there is no shim. |
4857 | // |
4858 | |
4859 | HRESULT CordbFrame::GetCallee(ICorDebugFrame **ppFrame) |
4860 | { |
4861 | HRESULT hr = S_OK; |
4862 | PUBLIC_REENTRANT_API_BEGIN(this) |
4863 | { |
4864 | ValidateOrThrow(ppFrame); |
4865 | |
4866 | *ppFrame = NULL; |
4867 | |
4868 | if (GetProcess()->GetShim() != NULL) |
4869 | { |
4870 | PUBLIC_CALLBACK_IN_THIS_SCOPE0(GetProcess(), GET_PUBLIC_LOCK_HOLDER()); |
4871 | ShimStackWalk * pSSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(m_pThread); |
4872 | pSSW->GetCalleeForFrame(static_cast<ICorDebugFrame *>(this), ppFrame); |
4873 | } |
4874 | else |
4875 | { |
4876 | *ppFrame = NULL; |
4877 | hr = E_NOTIMPL; |
4878 | } |
4879 | } |
4880 | PUBLIC_REENTRANT_API_END(hr); |
4881 | return hr; |
4882 | } |
4883 | |
4884 | // Create a stepper on the frame. |
4885 | HRESULT CordbFrame::CreateStepper(ICorDebugStepper **ppStepper) |
4886 | { |
4887 | PUBLIC_REENTRANT_API_ENTRY(this); |
4888 | FAIL_IF_NEUTERED(this); |
4889 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
4890 | VALIDATE_POINTER_TO_OBJECT(ppStepper, ICorDebugStepper **); |
4891 | |
4892 | HRESULT hr = S_OK; |
4893 | EX_TRY |
4894 | { |
4895 | RSInitHolder<CordbStepper> pStepper(new CordbStepper(m_pThread, this)); |
4896 | pStepper.TransferOwnershipExternal(ppStepper); |
4897 | } |
4898 | EX_CATCH_HRESULT(hr); |
4899 | |
4900 | return hr; |
4901 | } |
4902 | |
4903 | //--------------------------------------------------------------------------------------- |
4904 | // |
4905 | // Given a frame pointer, determine if it is in the stack range owned by the frame. |
4906 | // |
4907 | // Arguments: |
4908 | // fp - frame pointer to check |
4909 | // |
4910 | // Return Value: |
4911 | // whether the specified frame pointer is in the stack range or not |
4912 | // |
4913 | |
4914 | bool CordbFrame::IsContainedInFrame(FramePointer fp) |
4915 | { |
4916 | CORDB_ADDRESS stackStart; |
4917 | CORDB_ADDRESS stackEnd; |
4918 | |
4919 | // get the stack range |
4920 | HRESULT hr; |
4921 | hr = GetStackRange(&stackStart, &stackEnd); |
4922 | _ASSERTE(SUCCEEDED(hr)); |
4923 | |
4924 | CORDB_ADDRESS sp = PTR_TO_CORDB_ADDRESS(fp.GetSPValue()); |
4925 | |
4926 | if ((stackStart <= sp) && (sp <= stackEnd)) |
4927 | { |
4928 | return true; |
4929 | } |
4930 | else |
4931 | { |
4932 | return false; |
4933 | } |
4934 | } |
4935 | |
4936 | //--------------------------------------------------------------------------------------- |
4937 | // |
4938 | // Given an ICorDebugFrame interface pointer, return a pointer to the base class CordbFrame. |
4939 | // |
4940 | // Arguments: |
4941 | // pFrame - the ICorDebugFrame interface pointer |
4942 | // |
4943 | // Return Value: |
4944 | // the CordbFrame pointer corresponding to the specified interface pointer |
4945 | // |
4946 | // Note: |
4947 | // This is currently only used for continuable exceptions. |
4948 | // |
4949 | |
4950 | // static |
4951 | CordbFrame* CordbFrame::GetCordbFrameFromInterface(ICorDebugFrame *pFrame) |
4952 | { |
4953 | CordbFrame* pTargetFrame = NULL; |
4954 | |
4955 | if (pFrame != NULL) |
4956 | { |
4957 | // test for CordbNativeFrame |
4958 | RSExtSmartPtr<ICorDebugNativeFrame> pNativeFrame; |
4959 | pFrame->QueryInterface(IID_ICorDebugNativeFrame, (void**)&pNativeFrame); |
4960 | if (pNativeFrame != NULL) |
4961 | { |
4962 | pTargetFrame = static_cast<CordbFrame*>(static_cast<CordbNativeFrame*>(pNativeFrame.GetValue())); |
4963 | } |
4964 | else |
4965 | { |
4966 | // test for CordbJITILFrame |
4967 | RSExtSmartPtr<ICorDebugILFrame> pILFrame; |
4968 | pFrame->QueryInterface(IID_ICorDebugILFrame, (void**)&pILFrame); |
4969 | if (pILFrame != NULL) |
4970 | { |
4971 | pTargetFrame = (static_cast<CordbJITILFrame*>(pILFrame.GetValue()))->m_nativeFrame; |
4972 | } |
4973 | else |
4974 | { |
4975 | // test for CordbInternalFrame |
4976 | RSExtSmartPtr<ICorDebugInternalFrame> pInternalFrame; |
4977 | pFrame->QueryInterface(IID_ICorDebugInternalFrame, (void**)&pInternalFrame); |
4978 | if (pInternalFrame != NULL) |
4979 | { |
4980 | pTargetFrame = static_cast<CordbFrame*>(static_cast<CordbInternalFrame*>(pInternalFrame.GetValue())); |
4981 | } |
4982 | else |
4983 | { |
4984 | // when all else fails, this is just a CordbFrame |
4985 | pTargetFrame = static_cast<CordbFrame*>(pFrame); |
4986 | } |
4987 | } |
4988 | } |
4989 | } |
4990 | return pTargetFrame; |
4991 | } |
4992 | |
4993 | |
4994 | /* ------------------------------------------------------------------------- * |
4995 | |
4996 | * Value Enumerator class |
4997 | * |
4998 | * Used by CordbJITILFrame for EnumLocalVars & EnumArgs. |
4999 | * NOTE NOTE NOTE WE ASSUME that the 'frame' argument is actually the |
5000 | * CordbJITILFrame's native frame member variable. |
5001 | * ------------------------------------------------------------------------- */ |
5002 | |
5003 | CordbValueEnum::CordbValueEnum(CordbNativeFrame *frame, ValueEnumMode mode) : |
5004 | CordbBase(frame->GetProcess(), 0) |
5005 | { |
5006 | _ASSERTE( frame != NULL ); |
5007 | _ASSERTE( mode == LOCAL_VARS_ORIGINAL_IL || mode == LOCAL_VARS_REJIT_IL || mode == ARGS); |
5008 | |
5009 | m_frame = frame; |
5010 | m_mode = mode; |
5011 | m_iCurrent = 0; |
5012 | m_iMax = 0; |
5013 | } |
5014 | |
5015 | /* |
5016 | * CordbValueEnum::Init |
5017 | * |
5018 | * Initialize a CordbValueEnum object. Must be called after allocating the object and before using it. If Init |
5019 | * fails, then destroy the object and release the memory. |
5020 | * |
5021 | * Parameters: |
5022 | * none. |
5023 | * |
5024 | * Returns: |
5025 | * HRESULT for success or failure. |
5026 | * |
5027 | */ |
5028 | HRESULT CordbValueEnum::Init() |
5029 | { |
5030 | HRESULT hr = S_OK; |
5031 | CordbNativeFrame *nil = m_frame; |
5032 | CordbJITILFrame *jil = nil->m_JITILFrame; |
5033 | |
5034 | switch (m_mode) |
5035 | { |
5036 | case ARGS: |
5037 | { |
5038 | // Get the function signature |
5039 | CordbFunction *func = m_frame->GetFunction(); |
5040 | ULONG methodArgCount; |
5041 | |
5042 | IfFailRet(func->GetSig(NULL, &methodArgCount, NULL)); |
5043 | |
5044 | // Grab the argument count for the size of the enumeration. |
5045 | m_iMax = methodArgCount; |
5046 | if (jil->m_fVarArgFnx && !jil->m_sigParserCached.IsNull()) |
5047 | { |
5048 | m_iMax = jil->m_allArgsCount; |
5049 | } |
5050 | break; |
5051 | } |
5052 | case LOCAL_VARS_ORIGINAL_IL: |
5053 | { |
5054 | // Get the locals signature. |
5055 | ULONG localsCount; |
5056 | IfFailRet(jil->GetOriginalILCode()->GetLocalVarSig(NULL, &localsCount)); |
5057 | |
5058 | // Grab the number of locals for the size of the enumeration. |
5059 | m_iMax = localsCount; |
5060 | break; |
5061 | } |
5062 | case LOCAL_VARS_REJIT_IL: |
5063 | { |
5064 | // Get the locals signature. |
5065 | ULONG localsCount; |
5066 | CordbReJitILCode* pCode = jil->GetReJitILCode(); |
5067 | if (pCode == NULL) |
5068 | { |
5069 | m_iMax = 0; |
5070 | } |
5071 | else |
5072 | { |
5073 | IfFailRet(pCode->GetLocalVarSig(NULL, &localsCount)); |
5074 | |
5075 | // Grab the number of locals for the size of the enumeration. |
5076 | m_iMax = localsCount; |
5077 | } |
5078 | break; |
5079 | } |
5080 | } |
5081 | // Everything worked okay, so add this object to the neuter list for objects that are tied to the stack trace. |
5082 | EX_TRY |
5083 | { |
5084 | m_frame->m_pThread->GetRefreshStackNeuterList()->Add(GetProcess(), this); |
5085 | } |
5086 | EX_CATCH_HRESULT(hr); |
5087 | SetUnrecoverableIfFailed(GetProcess(), hr); |
5088 | |
5089 | return hr; |
5090 | } |
5091 | |
5092 | CordbValueEnum::~CordbValueEnum() |
5093 | { |
5094 | _ASSERTE(this->IsNeutered()); |
5095 | _ASSERTE(m_frame == NULL); |
5096 | } |
5097 | |
5098 | void CordbValueEnum::Neuter() |
5099 | { |
5100 | m_frame = NULL; |
5101 | CordbBase::Neuter(); |
5102 | } |
5103 | |
5104 | |
5105 | |
5106 | HRESULT CordbValueEnum::QueryInterface(REFIID id, void **pInterface) |
5107 | { |
5108 | if (id == IID_ICorDebugEnum) |
5109 | *pInterface = static_cast<ICorDebugEnum*>(this); |
5110 | else if (id == IID_ICorDebugValueEnum) |
5111 | *pInterface = static_cast<ICorDebugValueEnum*>(this); |
5112 | else if (id == IID_IUnknown) |
5113 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugValueEnum*>(this)); |
5114 | else |
5115 | { |
5116 | *pInterface = NULL; |
5117 | return E_NOINTERFACE; |
5118 | } |
5119 | |
5120 | ExternalAddRef(); |
5121 | return S_OK; |
5122 | } |
5123 | |
5124 | HRESULT CordbValueEnum::Skip(ULONG celt) |
5125 | { |
5126 | PUBLIC_REENTRANT_API_ENTRY(this); |
5127 | FAIL_IF_NEUTERED(this); |
5128 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
5129 | |
5130 | HRESULT hr = E_FAIL; |
5131 | if ( (m_iCurrent+celt) < m_iMax || |
5132 | celt == 0) |
5133 | { |
5134 | m_iCurrent += celt; |
5135 | hr = S_OK; |
5136 | } |
5137 | |
5138 | return hr; |
5139 | } |
5140 | |
5141 | HRESULT CordbValueEnum::Reset() |
5142 | { |
5143 | PUBLIC_REENTRANT_API_ENTRY(this); |
5144 | FAIL_IF_NEUTERED(this); |
5145 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
5146 | |
5147 | m_iCurrent = 0; |
5148 | return S_OK; |
5149 | } |
5150 | |
5151 | HRESULT CordbValueEnum::Clone(ICorDebugEnum **ppEnum) |
5152 | { |
5153 | PUBLIC_REENTRANT_API_ENTRY(this); |
5154 | FAIL_IF_NEUTERED(this); |
5155 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
5156 | |
5157 | VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorDebugEnum **); |
5158 | |
5159 | HRESULT hr = S_OK; |
5160 | EX_TRY |
5161 | { |
5162 | *ppEnum = NULL; |
5163 | RSInitHolder<CordbValueEnum> pCVE(new CordbValueEnum(m_frame, m_mode)); |
5164 | |
5165 | // Initialize the new enum |
5166 | hr = pCVE->Init(); |
5167 | IfFailThrow(hr); |
5168 | |
5169 | pCVE.TransferOwnershipExternal(ppEnum); |
5170 | } |
5171 | EX_CATCH_HRESULT(hr); |
5172 | |
5173 | return hr; |
5174 | } |
5175 | |
5176 | HRESULT CordbValueEnum::GetCount(ULONG *pcelt) |
5177 | { |
5178 | PUBLIC_REENTRANT_API_ENTRY(this); |
5179 | FAIL_IF_NEUTERED(this); |
5180 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
5181 | |
5182 | VALIDATE_POINTER_TO_OBJECT(pcelt, ULONG *); |
5183 | |
5184 | if( pcelt == NULL) |
5185 | { |
5186 | return E_INVALIDARG; |
5187 | } |
5188 | |
5189 | (*pcelt) = m_iMax; |
5190 | return S_OK; |
5191 | } |
5192 | |
5193 | // |
5194 | // In the event of failure, the current pointer will be left at |
5195 | // one element past the troublesome element. Thus, if one were |
5196 | // to repeatedly ask for one element to iterate through the |
5197 | // array, you would iterate exactly m_iMax times, regardless |
5198 | // of individual failures. |
5199 | HRESULT CordbValueEnum::Next(ULONG celt, ICorDebugValue *values[], ULONG *pceltFetched) |
5200 | { |
5201 | PUBLIC_API_ENTRY(this); |
5202 | FAIL_IF_NEUTERED(this); |
5203 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
5204 | |
5205 | VALIDATE_POINTER_TO_OBJECT_ARRAY(values, ICorDebugValue *, |
5206 | celt, true, true); |
5207 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *); |
5208 | |
5209 | if ((pceltFetched == NULL) && (celt != 1)) |
5210 | { |
5211 | return E_INVALIDARG; |
5212 | } |
5213 | |
5214 | if (celt == 0) |
5215 | { |
5216 | if (pceltFetched != NULL) |
5217 | { |
5218 | *pceltFetched = 0; |
5219 | } |
5220 | return S_OK; |
5221 | } |
5222 | |
5223 | HRESULT hr = S_OK; |
5224 | |
5225 | int iMax = min( m_iMax, m_iCurrent+celt); |
5226 | int i; |
5227 | for (i = m_iCurrent; i< iMax;i++) |
5228 | { |
5229 | switch ( m_mode ) |
5230 | { |
5231 | case ARGS: |
5232 | { |
5233 | hr = m_frame->m_JITILFrame->GetArgument( i, &(values[i-m_iCurrent]) ); |
5234 | break; |
5235 | } |
5236 | case LOCAL_VARS_ORIGINAL_IL: |
5237 | { |
5238 | hr = m_frame->m_JITILFrame->GetLocalVariableEx(ILCODE_ORIGINAL_IL, i, &(values[i-m_iCurrent]) ); |
5239 | break; |
5240 | } |
5241 | case LOCAL_VARS_REJIT_IL: |
5242 | { |
5243 | hr = m_frame->m_JITILFrame->GetLocalVariableEx(ILCODE_REJIT_IL, i, &(values[i - m_iCurrent])); |
5244 | break; |
5245 | } |
5246 | } |
5247 | if ( FAILED( hr ) ) |
5248 | { |
5249 | break; |
5250 | } |
5251 | } |
5252 | |
5253 | int count = (i - m_iCurrent); |
5254 | |
5255 | if ( FAILED( hr ) ) |
5256 | { |
5257 | // |
5258 | // we failed: +1 pushes us past troublesome element |
5259 | // |
5260 | m_iCurrent += 1 + count; |
5261 | } |
5262 | else |
5263 | { |
5264 | m_iCurrent += count; |
5265 | } |
5266 | |
5267 | if (pceltFetched != NULL) |
5268 | { |
5269 | *pceltFetched = count; |
5270 | } |
5271 | |
5272 | if (FAILED(hr)) |
5273 | { |
5274 | return hr; |
5275 | } |
5276 | |
5277 | |
5278 | // |
5279 | // If we reached the end of the enumeration, but not the end |
5280 | // of the number of requested items, we return S_FALSE. |
5281 | // |
5282 | if (((ULONG)count) < celt) |
5283 | { |
5284 | return S_FALSE; |
5285 | } |
5286 | |
5287 | return hr; |
5288 | } |
5289 | |
5290 | |
5291 | //----------------------------------------------------------------------------- |
5292 | // CordbInternalFrame |
5293 | //----------------------------------------------------------------------------- |
5294 | CordbInternalFrame::CordbInternalFrame(CordbThread * pThread, |
5295 | FramePointer fp, |
5296 | CordbAppDomain * pCurrentAppDomain, |
5297 | const DebuggerIPCE_STRData * pData) |
5298 | : CordbFrame(pThread, fp, 0, pCurrentAppDomain) |
5299 | { |
5300 | CONTRACTL |
5301 | { |
5302 | THROWS; |
5303 | } |
5304 | CONTRACTL_END; |
5305 | |
5306 | m_eFrameType = pData->stubFrame.frameType; |
5307 | m_funcMetadataToken = pData->stubFrame.funcMetadataToken; |
5308 | m_vmMethodDesc = pData->stubFrame.vmMethodDesc; |
5309 | |
5310 | // Some internal frames may not have a Function associated w/ them. |
5311 | if (!IsNilToken(m_funcMetadataToken)) |
5312 | { |
5313 | // Find the module of the function. Note that this module isn't necessarily in the same domain as our frame. |
5314 | // FuncEval frames can point to methods they are going to invoke in another domain. |
5315 | CordbModule * pModule = NULL; |
5316 | pModule = GetProcess()->LookupOrCreateModule(pData->stubFrame.vmDomainFile); |
5317 | _ASSERTE(pModule != NULL); |
5318 | |
5319 | // |
5320 | if( pModule != NULL ) |
5321 | { |
5322 | _ASSERTE( (pModule->GetAppDomain() == pCurrentAppDomain) || (m_eFrameType == STUBFRAME_FUNC_EVAL) ); |
5323 | |
5324 | |
5325 | |
5326 | mdMethodDef token = pData->stubFrame.funcMetadataToken; |
5327 | |
5328 | // @dbgtodo synchronization - push this up. |
5329 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
5330 | |
5331 | // CordbInternalFrame could handle a null function. |
5332 | // But if we fail to lookup, things are not in a good state anyways. |
5333 | CordbFunction * pFunction = pModule->LookupOrCreateFunctionLatestVersion(token); |
5334 | m_function.Assign(pFunction); |
5335 | |
5336 | } |
5337 | } |
5338 | } |
5339 | |
5340 | CordbInternalFrame::CordbInternalFrame(CordbThread * pThread, |
5341 | FramePointer fp, |
5342 | CordbAppDomain * pCurrentAppDomain, |
5343 | CorDebugInternalFrameType frameType, |
5344 | mdMethodDef funcMetadataToken, |
5345 | CordbFunction * pFunction, |
5346 | VMPTR_MethodDesc vmMethodDesc) |
5347 | : CordbFrame(pThread, fp, 0, pCurrentAppDomain) |
5348 | { |
5349 | m_eFrameType = frameType; |
5350 | m_funcMetadataToken = funcMetadataToken; |
5351 | m_function.Assign(pFunction); |
5352 | m_vmMethodDesc = vmMethodDesc; |
5353 | } |
5354 | |
5355 | HRESULT CordbInternalFrame::QueryInterface(REFIID id, void **pInterface) |
5356 | { |
5357 | if (id == IID_ICorDebugFrame) |
5358 | { |
5359 | *pInterface = static_cast<ICorDebugFrame*>(static_cast<ICorDebugInternalFrame*>(this)); |
5360 | } |
5361 | else if (id == IID_ICorDebugInternalFrame) |
5362 | { |
5363 | *pInterface = static_cast<ICorDebugInternalFrame*>(this); |
5364 | } |
5365 | else if (id == IID_ICorDebugInternalFrame2) |
5366 | { |
5367 | *pInterface = static_cast<ICorDebugInternalFrame2*>(this); |
5368 | } |
5369 | else if (id == IID_IUnknown) |
5370 | { |
5371 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugInternalFrame*>(this)); |
5372 | } |
5373 | else |
5374 | { |
5375 | *pInterface = NULL; |
5376 | return E_NOINTERFACE; |
5377 | } |
5378 | |
5379 | ExternalAddRef(); |
5380 | return S_OK; |
5381 | } |
5382 | |
5383 | void CordbInternalFrame::Neuter() |
5384 | { |
5385 | m_function.Clear(); |
5386 | CordbFrame::Neuter(); |
5387 | } |
5388 | |
5389 | |
5390 | // ---------------------------------------------------------------------------- |
5391 | // CordbInternalFrame::GetStackRange |
5392 | // |
5393 | // Description: |
5394 | // Return the stack range owned by this frame. |
5395 | // The start of the stack range is the leafmost boundary, and the end is the rootmost boundary. |
5396 | // |
5397 | // Arguments: |
5398 | // * pStart - out parameter; return the leaf end of the frame |
5399 | // * pEnd - out parameter; return the root end of the frame |
5400 | // |
5401 | // Return Value: |
5402 | // Return S_OK on success. |
5403 | // |
5404 | // Notes: |
5405 | // #GetStackRange |
5406 | // This is a virtual function and so there are multiple implementations for different types of frames. |
5407 | // It's very important to note that GetStackRange() can work when even after the frame is neutered. |
5408 | // Debuggers may rely on this to map old frames up to new frames across Continue() calls. |
5409 | // |
5410 | |
5411 | HRESULT CordbInternalFrame::GetStackRange(CORDB_ADDRESS *pStart, |
5412 | CORDB_ADDRESS *pEnd) |
5413 | { |
5414 | PUBLIC_REENTRANT_API_ENTRY(this); |
5415 | |
5416 | // Callers explicit require GetStackRange() to be callable when neutered so that they |
5417 | // can line up ICorDebugFrame objects across continues. We only return stack ranges |
5418 | // here and don't access any special data. |
5419 | OK_IF_NEUTERED(this); |
5420 | |
5421 | if (GetProcess()->GetShim() != NULL) |
5422 | { |
5423 | CORDB_ADDRESS pFramePointer = PTR_TO_CORDB_ADDRESS(GetFramePointer().GetSPValue()); |
5424 | if (pStart) |
5425 | { |
5426 | *pStart = pFramePointer; |
5427 | } |
5428 | if (pEnd) |
5429 | { |
5430 | *pEnd = pFramePointer; |
5431 | } |
5432 | return S_OK; |
5433 | } |
5434 | else |
5435 | { |
5436 | if (pStart != NULL) |
5437 | { |
5438 | *pStart = NULL; |
5439 | } |
5440 | if (pEnd != NULL) |
5441 | { |
5442 | *pEnd = NULL; |
5443 | } |
5444 | return E_NOTIMPL; |
5445 | } |
5446 | } |
5447 | |
5448 | |
5449 | // This may return NULL if there's no Method associated w/ this Frame. |
5450 | // For FuncEval frames, the function returned might also be in a different AppDomain |
5451 | // than the frame itself. |
5452 | CordbFunction * CordbInternalFrame::GetFunction() |
5453 | { |
5454 | return m_function; |
5455 | } |
5456 | |
5457 | // Accessor for the shim private hook code:CordbThread::ConvertFrameForILMethodWithoutMetadata. |
5458 | // Refer to that function for comments on the return value, the argument, etc. |
5459 | BOOL CordbInternalFrame::ConvertInternalFrameForILMethodWithoutMetadata( |
5460 | ICorDebugInternalFrame2 ** ppInternalFrame2) |
5461 | { |
5462 | _ASSERTE(ppInternalFrame2 != NULL); |
5463 | *ppInternalFrame2 = NULL; |
5464 | |
5465 | // The only internal frame conversion we need to perform is from STUBFRAME_JIT_COMPILATION to |
5466 | // STUBFRAME_LIGTHWEIGHT_FUNCTION. |
5467 | if (m_eFrameType != STUBFRAME_JIT_COMPILATION) |
5468 | { |
5469 | return FALSE; |
5470 | } |
5471 | |
5472 | // Check whether the internal frame has an associated MethodDesc. |
5473 | // Currently, the only STUBFRAME_JIT_COMPILATION frame with a NULL MethodDesc is ComPrestubMethodFrame, |
5474 | // which is not exposed in Whidbey. So convert it according to rule #2 below. |
5475 | if (m_vmMethodDesc.IsNull()) |
5476 | { |
5477 | return TRUE; |
5478 | } |
5479 | |
5480 | // Retrieve the type of the method associated with the STUBFRAME_JIT_COMPILATION. |
5481 | IDacDbiInterface::DynamicMethodType type = GetProcess()->GetDAC()->IsILStubOrLCGMethod(m_vmMethodDesc); |
5482 | |
5483 | // Here are the conversion rules: |
5484 | // 1) For a normal managed method, we don't convert, and we return FALSE. |
5485 | // 2) For an IL stub, we convert to NULL, and we return TRUE. |
5486 | // 3) For a dynamic method, we convert to a STUBFRAME_LIGHTWEIGHT_FUNCTION, and we return TRUE. |
5487 | if (type == IDacDbiInterface::kNone) |
5488 | { |
5489 | return FALSE; |
5490 | } |
5491 | else if (type == IDacDbiInterface::kILStub) |
5492 | { |
5493 | return TRUE; |
5494 | } |
5495 | else if (type == IDacDbiInterface::kLCGMethod) |
5496 | { |
5497 | // Here we are basically cloning another CordbInternalFrame. |
5498 | RSInitHolder<CordbInternalFrame> pInternalFrame(new CordbInternalFrame(m_pThread, |
5499 | m_fp, |
5500 | m_currentAppDomain, |
5501 | STUBFRAME_LIGHTWEIGHT_FUNCTION, |
5502 | m_funcMetadataToken, |
5503 | m_function.GetValue(), |
5504 | m_vmMethodDesc)); |
5505 | pInternalFrame.TransferOwnershipExternal(ppInternalFrame2); |
5506 | return TRUE; |
5507 | } |
5508 | |
5509 | UNREACHABLE(); |
5510 | } |
5511 | |
5512 | |
5513 | //--------------------------------------------------------------------------------------- |
5514 | // |
5515 | // Returns the address of an internal frame. The address is a stack pointer, even on IA64. |
5516 | // |
5517 | // Arguments: |
5518 | // pAddress - out parameter; return the frame marker address |
5519 | // |
5520 | // Return Value: |
5521 | // S_OK on success. |
5522 | // E_INVALIDARG if pAddress is NULL. |
5523 | // |
5524 | |
5525 | HRESULT CordbInternalFrame::GetAddress(CORDB_ADDRESS * pAddress) |
5526 | { |
5527 | PUBLIC_REENTRANT_API_ENTRY(this); |
5528 | FAIL_IF_NEUTERED(this); |
5529 | |
5530 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
5531 | |
5532 | if (pAddress == NULL) |
5533 | { |
5534 | return E_INVALIDARG; |
5535 | } |
5536 | |
5537 | *pAddress = PTR_TO_CORDB_ADDRESS(GetFramePointer().GetSPValue()); |
5538 | return S_OK; |
5539 | } |
5540 | |
5541 | //--------------------------------------------------------------------------------------- |
5542 | // |
5543 | // Refer to the comment for code:CordbInternalFrame::IsCloserToLeaf |
5544 | // |
5545 | |
5546 | BOOL CordbInternalFrame::IsCloserToLeafWorker(ICorDebugFrame * pFrameToCompare) |
5547 | { |
5548 | // Get the address of the "this" internal frame. |
5549 | CORDB_ADDRESS thisFrameAddr = PTR_TO_CORDB_ADDRESS(this->GetFramePointer().GetSPValue()); |
5550 | |
5551 | // Note that a QI on ICorDebugJITILFrame for ICorDebugNativeFrame will work. |
5552 | RSExtSmartPtr<ICorDebugNativeFrame> pNativeFrame; |
5553 | pFrameToCompare->QueryInterface(IID_ICorDebugNativeFrame, (void **)&pNativeFrame); |
5554 | if (pNativeFrame != NULL) |
5555 | { |
5556 | // The frame to compare is a CordbNativeFrame. |
5557 | CordbNativeFrame * pCNativeFrame = static_cast<CordbNativeFrame *>(pNativeFrame.GetValue()); |
5558 | |
5559 | // Compare the address of the "this" internal frame to the SP of the stack frame. |
5560 | // We can't compare frame pointers because the frame pointer means different things on |
5561 | // different platforms. |
5562 | CORDB_ADDRESS stackFrameSP = GetSPFromDebuggerREGDISPLAY(&(pCNativeFrame->m_rd)); |
5563 | return (thisFrameAddr < stackFrameSP); |
5564 | } |
5565 | |
5566 | RSExtSmartPtr<ICorDebugRuntimeUnwindableFrame> pRUFrame; |
5567 | pFrameToCompare->QueryInterface(IID_ICorDebugRuntimeUnwindableFrame, (void **)&pRUFrame); |
5568 | if (pRUFrame != NULL) |
5569 | { |
5570 | // The frame to compare is a CordbRuntimeUnwindableFrame. |
5571 | CordbRuntimeUnwindableFrame * pCRUFrame = |
5572 | static_cast<CordbRuntimeUnwindableFrame *>(pRUFrame.GetValue()); |
5573 | |
5574 | DT_CONTEXT * pResumeContext = const_cast<DT_CONTEXT *>(pCRUFrame->GetContext()); |
5575 | CORDB_ADDRESS stackFrameSP = PTR_TO_CORDB_ADDRESS(CORDbgGetSP(pResumeContext)); |
5576 | return (thisFrameAddr < stackFrameSP); |
5577 | } |
5578 | |
5579 | RSExtSmartPtr<ICorDebugInternalFrame> pInternalFrame; |
5580 | pFrameToCompare->QueryInterface(IID_ICorDebugInternalFrame, (void **)&pInternalFrame); |
5581 | if (pInternalFrame != NULL) |
5582 | { |
5583 | // The frame to compare is a CordbInternalFrame. |
5584 | CordbInternalFrame * pCInternalFrame = |
5585 | static_cast<CordbInternalFrame *>(pInternalFrame.GetValue()); |
5586 | |
5587 | CORDB_ADDRESS frameAddr = PTR_TO_CORDB_ADDRESS(pCInternalFrame->GetFramePointer().GetSPValue()); |
5588 | return (thisFrameAddr < frameAddr); |
5589 | } |
5590 | |
5591 | // What does this mean? This is unexpected. |
5592 | _ASSERTE(!"CIF::ICTLW - Unexpected frame type.\n" ); |
5593 | ThrowHR(E_FAIL); |
5594 | } |
5595 | |
5596 | //--------------------------------------------------------------------------------------- |
5597 | // |
5598 | // Checks whether the "this" internal frame is closer to the leaf than the specified ICDFrame. |
5599 | // If the specified ICDFrame represents a stack frame, then we compare the address of the "this" |
5600 | // internal frame against the SP of the stack frame. |
5601 | // |
5602 | // Arguments: |
5603 | // pFrameToCompare - the ICDFrame to compare against |
5604 | // pIsCloser - out parameter; returns TRUE if the "this" internal frame is closer to the leaf |
5605 | // |
5606 | // Return Value: |
5607 | // S_OK on success. |
5608 | // E_INVALIDARG if pFrameToCompare or pIsCloser is NULL. |
5609 | // E_FAIL if pFrameToCompare is bogus. |
5610 | // |
5611 | // Notes: |
5612 | // This function doesn't deal with the backing store at all. |
5613 | // |
5614 | |
5615 | HRESULT CordbInternalFrame::IsCloserToLeaf(ICorDebugFrame * pFrameToCompare, |
5616 | BOOL * pIsCloser) |
5617 | { |
5618 | HRESULT hr = S_OK; |
5619 | PUBLIC_REENTRANT_API_BEGIN(this); |
5620 | { |
5621 | ValidateOrThrow(pFrameToCompare); |
5622 | ValidateOrThrow(pIsCloser); |
5623 | |
5624 | *pIsCloser = IsCloserToLeafWorker(pFrameToCompare); |
5625 | } |
5626 | PUBLIC_REENTRANT_API_END(hr); |
5627 | return hr; |
5628 | } |
5629 | |
5630 | |
5631 | CordbRuntimeUnwindableFrame::CordbRuntimeUnwindableFrame(CordbThread * pThread, |
5632 | FramePointer fp, |
5633 | CordbAppDomain * pCurrentAppDomain, |
5634 | DT_CONTEXT * pContext) |
5635 | : CordbFrame(pThread, fp, 0, pCurrentAppDomain), |
5636 | m_context(*pContext) |
5637 | { |
5638 | } |
5639 | |
5640 | void CordbRuntimeUnwindableFrame::Neuter() |
5641 | { |
5642 | CordbFrame::Neuter(); |
5643 | } |
5644 | |
5645 | HRESULT CordbRuntimeUnwindableFrame::QueryInterface(REFIID id, void ** ppInterface) |
5646 | { |
5647 | if (id == IID_ICorDebugFrame) |
5648 | { |
5649 | *ppInterface = static_cast<ICorDebugFrame *>(static_cast<ICorDebugRuntimeUnwindableFrame *>(this)); |
5650 | } |
5651 | else if (id == IID_ICorDebugRuntimeUnwindableFrame) |
5652 | { |
5653 | *ppInterface = static_cast<ICorDebugRuntimeUnwindableFrame *>(this); |
5654 | } |
5655 | else if (id == IID_IUnknown) |
5656 | { |
5657 | *ppInterface = static_cast<IUnknown *>(static_cast<ICorDebugRuntimeUnwindableFrame *>(this)); |
5658 | } |
5659 | else |
5660 | { |
5661 | *ppInterface = NULL; |
5662 | return E_NOINTERFACE; |
5663 | } |
5664 | |
5665 | ExternalAddRef(); |
5666 | return S_OK; |
5667 | } |
5668 | |
5669 | //--------------------------------------------------------------------------------------- |
5670 | // |
5671 | // Returns the CONTEXT corresponding to this CordbRuntimeUnwindableFrame. |
5672 | // |
5673 | // Return Value: |
5674 | // Return a pointer to the CONTEXT. |
5675 | // |
5676 | |
5677 | const DT_CONTEXT * CordbRuntimeUnwindableFrame::GetContext() const |
5678 | { |
5679 | return &m_context; |
5680 | } |
5681 | |
5682 | |
5683 | // default constructor to make the compiler happy |
5684 | CordbMiscFrame::CordbMiscFrame() |
5685 | { |
5686 | #ifdef WIN64EXCEPTIONS |
5687 | this->parentIP = 0; |
5688 | this->fpParentOrSelf = LEAF_MOST_FRAME; |
5689 | this->fIsFilterFunclet = false; |
5690 | #endif // WIN64EXCEPTIONS |
5691 | } |
5692 | |
5693 | // the real constructor which stores the funclet-related information in the CordbMiscFrame |
5694 | CordbMiscFrame::CordbMiscFrame(DebuggerIPCE_JITFuncData * pJITFuncData) |
5695 | { |
5696 | #ifdef WIN64EXCEPTIONS |
5697 | this->parentIP = pJITFuncData->parentNativeOffset; |
5698 | this->fpParentOrSelf = pJITFuncData->fpParentOrSelf; |
5699 | this->fIsFilterFunclet = (pJITFuncData->fIsFilterFrame == TRUE); |
5700 | #endif // WIN64EXCEPTIONS |
5701 | } |
5702 | |
5703 | /* ------------------------------------------------------------------------- * |
5704 | * Native Frame class |
5705 | * ------------------------------------------------------------------------- */ |
5706 | |
5707 | |
5708 | CordbNativeFrame::CordbNativeFrame(CordbThread * pThread, |
5709 | FramePointer fp, |
5710 | CordbNativeCode * pNativeCode, |
5711 | SIZE_T ip, |
5712 | DebuggerREGDISPLAY * pDRD, |
5713 | TADDR taAmbientESP, |
5714 | bool fQuicklyUnwound, |
5715 | CordbAppDomain * pCurrentAppDomain, |
5716 | CordbMiscFrame * pMisc /*= NULL*/, |
5717 | DT_CONTEXT * pContext /*= NULL*/) |
5718 | : CordbFrame(pThread, fp, ip, pCurrentAppDomain), |
5719 | m_rd(*pDRD), |
5720 | m_quicklyUnwound(fQuicklyUnwound), |
5721 | m_JITILFrame(NULL), |
5722 | m_nativeCode(pNativeCode), // implicit InternalAddRef |
5723 | m_taAmbientESP(taAmbientESP) |
5724 | { |
5725 | m_misc = *pMisc; |
5726 | |
5727 | // Only new CordbNativeFrames created by the new stackwalk contain a CONTEXT. |
5728 | _ASSERTE(pContext != NULL); |
5729 | m_context = *pContext; |
5730 | } |
5731 | |
5732 | /* |
5733 | A list of which resources owned by this object are accounted for. |
5734 | |
5735 | RESOLVED: |
5736 | CordbJITILFrame* m_JITILFrame; // Neutered |
5737 | */ |
5738 | |
5739 | CordbNativeFrame::~CordbNativeFrame() |
5740 | { |
5741 | _ASSERTE(IsNeutered()); |
5742 | } |
5743 | |
5744 | // Neutered by CordbThread::CleanupStack |
5745 | void CordbNativeFrame::Neuter() |
5746 | { |
5747 | // Neuter may be called multiple times so be sure to set ptrs to NULL so that we don't |
5748 | // double release them. |
5749 | if (IsNeutered()) |
5750 | { |
5751 | return; |
5752 | } |
5753 | |
5754 | m_nativeCode.Clear(); |
5755 | |
5756 | if (m_JITILFrame != NULL) |
5757 | { |
5758 | m_JITILFrame->Neuter(); |
5759 | m_JITILFrame.Clear(); |
5760 | } |
5761 | |
5762 | CordbFrame::Neuter(); |
5763 | } |
5764 | |
5765 | // CordbNativeFrame::QueryInterface |
5766 | // |
5767 | // Description |
5768 | // interface query for this COM object |
5769 | // |
5770 | // NOTE: the COM object associated with this CordbNativeFrame may consist of |
5771 | // two C++ objects (the CordbNativeFrame and the CordbJITILFrame). |
5772 | // |
5773 | // Parameters |
5774 | // id the GUID associated with the requested interface |
5775 | // pInterface [out] the interface pointer |
5776 | // |
5777 | // Returns |
5778 | // HRESULT |
5779 | // S_OK If this CordbJITILFrame supports the interface |
5780 | // E_NOINTERFACE If this object does not support the interface |
5781 | // |
5782 | // Exceptions |
5783 | // None |
5784 | // |
5785 | // |
5786 | HRESULT CordbNativeFrame::QueryInterface(REFIID id, void **pInterface) |
5787 | { |
5788 | if (id == IID_ICorDebugFrame) |
5789 | { |
5790 | *pInterface = static_cast<ICorDebugFrame*>(static_cast<ICorDebugNativeFrame*>(this)); |
5791 | } |
5792 | else if (id == IID_ICorDebugNativeFrame) |
5793 | { |
5794 | *pInterface = static_cast<ICorDebugNativeFrame*>(this); |
5795 | } |
5796 | else if (id == IID_ICorDebugNativeFrame2) |
5797 | { |
5798 | *pInterface = static_cast<ICorDebugNativeFrame2*>(this); |
5799 | } |
5800 | else if (id == IID_IUnknown) |
5801 | { |
5802 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugNativeFrame*>(this)); |
5803 | } |
5804 | else |
5805 | { |
5806 | // might be searching for an IL Frame. delegate that search to the |
5807 | // JITILFrame |
5808 | if (m_JITILFrame != NULL) |
5809 | { |
5810 | return m_JITILFrame->QueryInterfaceInternal(id, pInterface); |
5811 | } |
5812 | else |
5813 | { |
5814 | *pInterface = NULL; |
5815 | return E_NOINTERFACE; |
5816 | } |
5817 | } |
5818 | |
5819 | ExternalAddRef(); |
5820 | return S_OK; |
5821 | } |
5822 | |
5823 | // Return the CordbNativeCode object associated with this native frame. |
5824 | // This is just a wrapper around the real helper. |
5825 | HRESULT CordbNativeFrame::GetCode(ICorDebugCode **ppCode) |
5826 | { |
5827 | PUBLIC_API_ENTRY(this); |
5828 | VALIDATE_POINTER_TO_OBJECT(ppCode, ICorDebugCode **); |
5829 | FAIL_IF_NEUTERED(this); |
5830 | |
5831 | CordbNativeCode * pCode = GetNativeCode(); |
5832 | *ppCode = static_cast<ICorDebugCode*> (pCode); |
5833 | pCode->ExternalAddRef(); |
5834 | |
5835 | return S_OK;; |
5836 | } |
5837 | |
5838 | //--------------------------------------------------------------------------------------- |
5839 | // |
5840 | // Returns the CONTEXT corresponding to this CordbNativeFrame. |
5841 | // |
5842 | // Return Value: |
5843 | // Return a pointer to the CONTEXT. |
5844 | // |
5845 | |
5846 | const DT_CONTEXT * CordbNativeFrame::GetContext() const |
5847 | { |
5848 | return &m_context; |
5849 | } |
5850 | |
5851 | //--------------------------------------------------------------------------------------- |
5852 | // |
5853 | // This is an internal helper to get the CordbNativeCode object associated with this native frame. |
5854 | // |
5855 | // Return Value: |
5856 | // the associated CordbNativeCode object |
5857 | // |
5858 | |
5859 | CordbNativeCode * CordbNativeFrame::GetNativeCode() |
5860 | { |
5861 | return this->m_nativeCode; |
5862 | } |
5863 | |
5864 | //--------------------------------------------------------------------------------------- |
5865 | // |
5866 | // This is an internal helper to get the CordbFunction object associated with this native frame. |
5867 | // |
5868 | // Return Value: |
5869 | // the associated CordbFunction object |
5870 | // |
5871 | |
5872 | CordbFunction *CordbNativeFrame::GetFunction() |
5873 | { |
5874 | return this->m_nativeCode->GetFunction(); |
5875 | } |
5876 | |
5877 | |
5878 | |
5879 | // Return the native offset. |
5880 | HRESULT CordbNativeFrame::GetIP(ULONG32 *pnOffset) |
5881 | { |
5882 | PUBLIC_REENTRANT_API_ENTRY(this); |
5883 | FAIL_IF_NEUTERED(this); |
5884 | VALIDATE_POINTER_TO_OBJECT(pnOffset, ULONG32 *); |
5885 | |
5886 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
5887 | |
5888 | *pnOffset = (ULONG32)m_ip; |
5889 | |
5890 | return S_OK; |
5891 | } |
5892 | |
5893 | ULONG32 CordbNativeFrame::GetIPOffset() |
5894 | { |
5895 | return (ULONG32)m_ip; |
5896 | } |
5897 | |
5898 | TADDR CordbNativeFrame::GetReturnRegisterValue() |
5899 | { |
5900 | #if defined(DBG_TARGET_X86) |
5901 | return (TADDR)m_context.Eax; |
5902 | #elif defined(DBG_TARGET_AMD64) |
5903 | return (TADDR)m_context.Rax; |
5904 | #elif defined(DBG_TARGET_ARM) |
5905 | return (TADDR)m_context.R0; |
5906 | #elif defined(DBG_TARGET_ARM64) |
5907 | return (TADDR)m_context.X0; |
5908 | #else |
5909 | _ASSERTE(!"nyi for platform" ); |
5910 | return 0; |
5911 | #endif |
5912 | } |
5913 | |
5914 | // Determine if we can set IP at this point. The specified offset is the native offset. |
5915 | HRESULT CordbNativeFrame::CanSetIP(ULONG32 nOffset) |
5916 | { |
5917 | PUBLIC_API_ENTRY(this); |
5918 | FAIL_IF_NEUTERED(this); |
5919 | |
5920 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
5921 | |
5922 | HRESULT hr = S_OK; |
5923 | EX_TRY |
5924 | { |
5925 | if (!IsLeafFrame()) |
5926 | { |
5927 | ThrowHR(CORDBG_E_SET_IP_NOT_ALLOWED_ON_NONLEAF_FRAME); |
5928 | } |
5929 | |
5930 | hr = m_pThread->SetIP(SetIP_fCanSetIPOnly, |
5931 | m_nativeCode, |
5932 | nOffset, |
5933 | SetIP_fNative ); |
5934 | } |
5935 | EX_CATCH_HRESULT(hr); |
5936 | |
5937 | return hr; |
5938 | } |
5939 | |
5940 | // Try to set the IP to the specified offset. The specified offset is the native offset. |
5941 | HRESULT CordbNativeFrame::SetIP(ULONG32 nOffset) |
5942 | { |
5943 | PUBLIC_API_ENTRY(this); |
5944 | FAIL_IF_NEUTERED(this); |
5945 | |
5946 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
5947 | |
5948 | HRESULT hr = S_OK; |
5949 | EX_TRY |
5950 | { |
5951 | if (!IsLeafFrame()) |
5952 | { |
5953 | ThrowHR(CORDBG_E_SET_IP_NOT_ALLOWED_ON_NONLEAF_FRAME); |
5954 | } |
5955 | |
5956 | hr = m_pThread->SetIP(SetIP_fSetIP, |
5957 | m_nativeCode, |
5958 | nOffset, |
5959 | SetIP_fNative ); |
5960 | } |
5961 | EX_CATCH_HRESULT(hr); |
5962 | |
5963 | return hr; |
5964 | } |
5965 | |
5966 | |
5967 | // Given a (register,offset) description of a stack location, compute |
5968 | // the real memory address for it. |
5969 | // This will also handle ambient SP values (which are encoded with regNum == REGNUM_AMBIENT_SP). |
5970 | CORDB_ADDRESS CordbNativeFrame::GetLSStackAddress( |
5971 | ICorDebugInfo::RegNum regNum, |
5972 | signed offset) |
5973 | { |
5974 | UINT_PTR *pRegAddr; |
5975 | |
5976 | CORDB_ADDRESS pRemoteValue; |
5977 | |
5978 | if (regNum != DBG_TARGET_REGNUM_AMBIENT_SP) |
5979 | { |
5980 | // Even if we're inside a funclet, variables (in both x64 and ARM) are still |
5981 | // relative to the frame pointer or stack pointer, which are accurate in the |
5982 | // funclet, after the funclet prolog; the frame pointer is re-established in the |
5983 | // funclet prolog using the PSP. Thus, we just look up the frame pointer in the |
5984 | // current native frame. |
5985 | |
5986 | pRegAddr = this->GetAddressOfRegister( |
5987 | ConvertRegNumToCorDebugRegister(regNum)); |
5988 | |
5989 | // This should never be null as long as regNum is a member of the RegNum enum. |
5990 | // If it is, an AV dereferencing a null-pointer in retail builds, or an assert in debug |
5991 | // builds is exactly the behavior we want. |
5992 | PREFIX_ASSUME(pRegAddr != NULL); |
5993 | |
5994 | pRemoteValue = PTR_TO_CORDB_ADDRESS(*pRegAddr + offset); |
5995 | } |
5996 | else |
5997 | { |
5998 | // Use the ambient ESP. At this point we're decoding an ambient-sp var, so |
5999 | // we should definitely have an ambient-sp. If this is null, then the jit |
6000 | // likely gave us an inconsistent data. |
6001 | TADDR taAmbient = this->GetAmbientESP(); |
6002 | _ASSERTE(taAmbient != NULL); |
6003 | |
6004 | pRemoteValue = PTR_TO_CORDB_ADDRESS(taAmbient + offset); |
6005 | } |
6006 | |
6007 | return pRemoteValue; |
6008 | } |
6009 | |
6010 | |
6011 | // ---------------------------------------------------------------------------- |
6012 | // CordbNativeFrame::GetStackRange |
6013 | // |
6014 | // Description: |
6015 | // Return the stack range owned by this native frame. |
6016 | // The start of the stack range is the leafmost boundary, and the end is the rootmost boundary. |
6017 | // |
6018 | // Arguments: |
6019 | // * pStart - out parameter; return the leaf end of the frame |
6020 | // * pEnd - out parameter; return the root end of the frame |
6021 | // |
6022 | // Return Value: |
6023 | // Return S_OK on success. |
6024 | // |
6025 | // Notes: see code:#GetStackRange |
6026 | |
6027 | HRESULT CordbNativeFrame::GetStackRange(CORDB_ADDRESS *pStart, |
6028 | CORDB_ADDRESS *pEnd) |
6029 | { |
6030 | PUBLIC_REENTRANT_API_ENTRY(this); |
6031 | |
6032 | // Callers explicit require GetStackRange() to be callable when neutered so that they |
6033 | // can line up ICorDebugFrame objects across continues. We only return stack ranges |
6034 | // here and don't access any special data. |
6035 | OK_IF_NEUTERED(this); |
6036 | |
6037 | if (GetProcess()->GetShim() != NULL) |
6038 | { |
6039 | if (pStart) |
6040 | { |
6041 | // From register set. |
6042 | *pStart = GetSPFromDebuggerREGDISPLAY(&m_rd); |
6043 | } |
6044 | |
6045 | if (pEnd) |
6046 | { |
6047 | // The rootmost boundary is the frame pointer. |
6048 | // <NOTE> |
6049 | // This is not true on AMD64, on which we use the stack pointer as the frame pointer. |
6050 | // </NOTE> |
6051 | *pEnd = PTR_TO_CORDB_ADDRESS(GetFramePointer().GetSPValue()); |
6052 | } |
6053 | return S_OK; |
6054 | } |
6055 | else |
6056 | { |
6057 | if (pStart != NULL) |
6058 | { |
6059 | *pStart = NULL; |
6060 | } |
6061 | if (pEnd != NULL) |
6062 | { |
6063 | *pEnd = NULL; |
6064 | } |
6065 | return E_NOTIMPL; |
6066 | } |
6067 | } |
6068 | |
6069 | // Return the register set of the native frame. |
6070 | HRESULT CordbNativeFrame::GetRegisterSet(ICorDebugRegisterSet **ppRegisters) |
6071 | { |
6072 | PUBLIC_REENTRANT_API_ENTRY(this); |
6073 | FAIL_IF_NEUTERED(this); |
6074 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6075 | |
6076 | VALIDATE_POINTER_TO_OBJECT(ppRegisters, ICorDebugRegisterSet **); |
6077 | |
6078 | HRESULT hr = S_OK; |
6079 | EX_TRY |
6080 | { |
6081 | // allocate a new CordbRegisterSet object |
6082 | RSInitHolder<CordbRegisterSet> pRegisterSet(new CordbRegisterSet(&m_rd, |
6083 | m_pThread, |
6084 | IsLeafFrame(), |
6085 | m_quicklyUnwound)); |
6086 | |
6087 | pRegisterSet.TransferOwnershipExternal(ppRegisters); |
6088 | } |
6089 | EX_CATCH_HRESULT(hr); |
6090 | return hr; |
6091 | } |
6092 | |
6093 | //--------------------------------------------------------------------------------------- |
6094 | // |
6095 | // Checks whether the frame is a child frame or not. |
6096 | // |
6097 | // Arguments: |
6098 | // pIsChild - out parameter; returns whether the frame is a child frame |
6099 | // |
6100 | // Return Value: |
6101 | // S_OK on success. |
6102 | // E_INVALIDARG if the out parmater is NULL. |
6103 | // |
6104 | |
6105 | HRESULT CordbNativeFrame::IsChild(BOOL * pIsChild) |
6106 | { |
6107 | HRESULT hr = S_OK; |
6108 | PUBLIC_REENTRANT_API_BEGIN(this) |
6109 | { |
6110 | if (pIsChild == NULL) |
6111 | { |
6112 | ThrowHR(E_INVALIDARG); |
6113 | } |
6114 | else |
6115 | { |
6116 | *pIsChild = ((this->IsFunclet() && !this->IsFilterFunclet()) ? TRUE : FALSE); |
6117 | } |
6118 | } |
6119 | PUBLIC_REENTRANT_API_END(hr); |
6120 | return hr; |
6121 | } |
6122 | |
6123 | //--------------------------------------------------------------------------------------- |
6124 | // |
6125 | // Given an ICDNativeFrame2, check whether it is the parent frame of the current frame. |
6126 | // |
6127 | // Arguments: |
6128 | // pPotentialParentFrame - the ICDNativeFrame2 to check |
6129 | // pIsParent - out paramter; returns whether the specified frame is indeed the parent frame |
6130 | // |
6131 | // Return Value: |
6132 | // S_OK on success. |
6133 | // CORDBG_E_NOT_CHILD_FRAME if the current frame is not a child frame. |
6134 | // E_INVALIDARG if either of the incoming argument is NULL. |
6135 | // E_FAIL on other failures. |
6136 | // |
6137 | |
6138 | HRESULT CordbNativeFrame::IsMatchingParentFrame(ICorDebugNativeFrame2 * pPotentialParentFrame, |
6139 | BOOL * pIsParent) |
6140 | { |
6141 | PUBLIC_REENTRANT_API_ENTRY(this); |
6142 | FAIL_IF_NEUTERED(this); |
6143 | VALIDATE_POINTER_TO_OBJECT(pPotentialParentFrame, ICorDebugNativeFrame2 *); |
6144 | |
6145 | HRESULT hr = S_OK; |
6146 | EX_TRY |
6147 | { |
6148 | if ((pPotentialParentFrame == NULL) || (pIsParent == NULL)) |
6149 | { |
6150 | ThrowHR(E_INVALIDARG); |
6151 | } |
6152 | |
6153 | *pIsParent = FALSE; |
6154 | |
6155 | if (!this->IsFunclet()) |
6156 | { |
6157 | ThrowHR(CORDBG_E_NOT_CHILD_FRAME); |
6158 | } |
6159 | |
6160 | #ifdef WIN64EXCEPTIONS |
6161 | CordbNativeFrame * pFrameToCheck = static_cast<CordbNativeFrame *>(pPotentialParentFrame); |
6162 | if (pFrameToCheck->IsFunclet()) |
6163 | { |
6164 | *pIsParent = FALSE; |
6165 | } |
6166 | else |
6167 | { |
6168 | FramePointer fpParent = this->m_misc.fpParentOrSelf; |
6169 | FramePointer fpToCheck = pFrameToCheck->m_misc.fpParentOrSelf; |
6170 | |
6171 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
6172 | *pIsParent = pDAC->IsMatchingParentFrame(fpToCheck, fpParent); |
6173 | } |
6174 | #endif // WIN64EXCEPTIONS |
6175 | } |
6176 | EX_CATCH_HRESULT(hr); |
6177 | |
6178 | return hr; |
6179 | } |
6180 | |
6181 | //--------------------------------------------------------------------------------------- |
6182 | // |
6183 | // Return the stack parameter size of the current frame. Since this information is only used on x86, |
6184 | // we return S_FALSE and a size of 0 on WIN64 platforms. |
6185 | // |
6186 | // Arguments: |
6187 | // pSize - out parameter; return the size of the stack parameter |
6188 | // |
6189 | // Return Value: |
6190 | // S_OK on success. |
6191 | // S_FALSE on WIN64 platforms. |
6192 | // E_INVALIDARG if pSize is NULL. |
6193 | // |
6194 | // Notes: |
6195 | // Always return S_FALSE on WIN64. |
6196 | // |
6197 | |
6198 | HRESULT CordbNativeFrame::GetStackParameterSize(ULONG32 * pSize) |
6199 | { |
6200 | PUBLIC_API_ENTRY(this); |
6201 | FAIL_IF_NEUTERED(this); |
6202 | |
6203 | HRESULT hr = S_OK; |
6204 | EX_TRY |
6205 | { |
6206 | if (pSize == NULL) |
6207 | { |
6208 | ThrowHR(E_INVALIDARG); |
6209 | } |
6210 | |
6211 | #if defined(_TARGET_X86_) |
6212 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
6213 | *pSize = pDAC->GetStackParameterSize(PTR_TO_CORDB_ADDRESS(CORDbgGetIP(&m_context))); |
6214 | #else // !_TARGET_X86_ |
6215 | hr = S_FALSE; |
6216 | *pSize = 0; |
6217 | #endif // _TARGET_X86_ |
6218 | } |
6219 | EX_CATCH_HRESULT(hr); |
6220 | |
6221 | return hr; |
6222 | } |
6223 | |
6224 | // |
6225 | // GetAddressOfRegister returns the address of the given register in the |
6226 | // frame's current register display (eg, a local address). This is usually used to build a |
6227 | // ICorDebugValue from. |
6228 | // |
6229 | UINT_PTR * CordbNativeFrame::GetAddressOfRegister(CorDebugRegister regNum) const |
6230 | { |
6231 | UINT_PTR* ret = NULL; |
6232 | |
6233 | switch (regNum) |
6234 | { |
6235 | case REGISTER_STACK_POINTER: |
6236 | ret = (UINT_PTR*)GetSPAddress(&m_rd); |
6237 | break; |
6238 | |
6239 | #if !defined(DBG_TARGET_AMD64) && !defined(DBG_TARGET_ARM) // @ARMTODO |
6240 | case REGISTER_FRAME_POINTER: |
6241 | ret = (UINT_PTR*)GetFPAddress(&m_rd); |
6242 | break; |
6243 | #endif |
6244 | |
6245 | #if defined(DBG_TARGET_X86) |
6246 | case REGISTER_X86_EAX: |
6247 | ret = (UINT_PTR*)&m_rd.Eax; |
6248 | break; |
6249 | |
6250 | case REGISTER_X86_ECX: |
6251 | ret = (UINT_PTR*)&m_rd.Ecx; |
6252 | break; |
6253 | |
6254 | case REGISTER_X86_EDX: |
6255 | ret = (UINT_PTR*)&m_rd.Edx; |
6256 | break; |
6257 | |
6258 | case REGISTER_X86_EBX: |
6259 | ret = (UINT_PTR*)&m_rd.Ebx; |
6260 | break; |
6261 | |
6262 | case REGISTER_X86_ESI: |
6263 | ret = (UINT_PTR*)&m_rd.Esi; |
6264 | break; |
6265 | |
6266 | case REGISTER_X86_EDI: |
6267 | ret = (UINT_PTR*)&m_rd.Edi; |
6268 | break; |
6269 | |
6270 | #elif defined(DBG_TARGET_AMD64) |
6271 | case REGISTER_AMD64_RBP: |
6272 | ret = (UINT_PTR*)&m_rd.Rbp; |
6273 | break; |
6274 | |
6275 | case REGISTER_AMD64_RAX: |
6276 | ret = (UINT_PTR*)&m_rd.Rax; |
6277 | break; |
6278 | |
6279 | case REGISTER_AMD64_RCX: |
6280 | ret = (UINT_PTR*)&m_rd.Rcx; |
6281 | break; |
6282 | |
6283 | case REGISTER_AMD64_RDX: |
6284 | ret = (UINT_PTR*)&m_rd.Rdx; |
6285 | break; |
6286 | |
6287 | case REGISTER_AMD64_RBX: |
6288 | ret = (UINT_PTR*)&m_rd.Rbx; |
6289 | break; |
6290 | |
6291 | case REGISTER_AMD64_RSI: |
6292 | ret = (UINT_PTR*)&m_rd.Rsi; |
6293 | break; |
6294 | |
6295 | case REGISTER_AMD64_RDI: |
6296 | ret = (UINT_PTR*)&m_rd.Rdi; |
6297 | break; |
6298 | |
6299 | case REGISTER_AMD64_R8: |
6300 | ret = (UINT_PTR*)&m_rd.R8; |
6301 | break; |
6302 | |
6303 | case REGISTER_AMD64_R9: |
6304 | ret = (UINT_PTR*)&m_rd.R9; |
6305 | break; |
6306 | |
6307 | case REGISTER_AMD64_R10: |
6308 | ret = (UINT_PTR*)&m_rd.R10; |
6309 | break; |
6310 | |
6311 | case REGISTER_AMD64_R11: |
6312 | ret = (UINT_PTR*)&m_rd.R11; |
6313 | break; |
6314 | |
6315 | case REGISTER_AMD64_R12: |
6316 | ret = (UINT_PTR*)&m_rd.R12; |
6317 | break; |
6318 | |
6319 | case REGISTER_AMD64_R13: |
6320 | ret = (UINT_PTR*)&m_rd.R13; |
6321 | break; |
6322 | |
6323 | case REGISTER_AMD64_R14: |
6324 | ret = (UINT_PTR*)&m_rd.R14; |
6325 | break; |
6326 | |
6327 | case REGISTER_AMD64_R15: |
6328 | ret = (UINT_PTR*)&m_rd.R15; |
6329 | break; |
6330 | #elif defined(DBG_TARGET_ARM) |
6331 | case REGISTER_ARM_R0: |
6332 | ret = (UINT_PTR*)&m_rd.R0; |
6333 | break; |
6334 | |
6335 | case REGISTER_ARM_R1: |
6336 | ret = (UINT_PTR*)&m_rd.R1; |
6337 | break; |
6338 | |
6339 | case REGISTER_ARM_R2: |
6340 | ret = (UINT_PTR*)&m_rd.R2; |
6341 | break; |
6342 | |
6343 | case REGISTER_ARM_R3: |
6344 | ret = (UINT_PTR*)&m_rd.R3; |
6345 | break; |
6346 | |
6347 | case REGISTER_ARM_R4: |
6348 | ret = (UINT_PTR*)&m_rd.R4; |
6349 | break; |
6350 | |
6351 | case REGISTER_ARM_R5: |
6352 | ret = (UINT_PTR*)&m_rd.R5; |
6353 | break; |
6354 | |
6355 | case REGISTER_ARM_R6: |
6356 | ret = (UINT_PTR*)&m_rd.R6; |
6357 | break; |
6358 | |
6359 | case REGISTER_ARM_R7: |
6360 | ret = (UINT_PTR*)&m_rd.R7; |
6361 | break; |
6362 | |
6363 | case REGISTER_ARM_R8: |
6364 | ret = (UINT_PTR*)&m_rd.R8; |
6365 | break; |
6366 | |
6367 | case REGISTER_ARM_R9: |
6368 | ret = (UINT_PTR*)&m_rd.R9; |
6369 | break; |
6370 | |
6371 | case REGISTER_ARM_R10: |
6372 | ret = (UINT_PTR*)&m_rd.R10; |
6373 | break; |
6374 | |
6375 | case REGISTER_ARM_R11: |
6376 | ret = (UINT_PTR*)&m_rd.R11; |
6377 | break; |
6378 | |
6379 | case REGISTER_ARM_R12: |
6380 | ret = (UINT_PTR*)&m_rd.R12; |
6381 | break; |
6382 | |
6383 | case REGISTER_ARM_LR: |
6384 | ret = (UINT_PTR*)&m_rd.LR; |
6385 | break; |
6386 | |
6387 | case REGISTER_ARM_PC: |
6388 | ret = (UINT_PTR*)&m_rd.PC; |
6389 | break; |
6390 | #elif defined(DBG_TARGET_ARM64) |
6391 | case REGISTER_ARM64_X0: |
6392 | case REGISTER_ARM64_X1: |
6393 | case REGISTER_ARM64_X2: |
6394 | case REGISTER_ARM64_X3: |
6395 | case REGISTER_ARM64_X4: |
6396 | case REGISTER_ARM64_X5: |
6397 | case REGISTER_ARM64_X6: |
6398 | case REGISTER_ARM64_X7: |
6399 | case REGISTER_ARM64_X8: |
6400 | case REGISTER_ARM64_X9: |
6401 | case REGISTER_ARM64_X10: |
6402 | case REGISTER_ARM64_X11: |
6403 | case REGISTER_ARM64_X12: |
6404 | case REGISTER_ARM64_X13: |
6405 | case REGISTER_ARM64_X14: |
6406 | case REGISTER_ARM64_X15: |
6407 | case REGISTER_ARM64_X16: |
6408 | case REGISTER_ARM64_X17: |
6409 | case REGISTER_ARM64_X18: |
6410 | case REGISTER_ARM64_X19: |
6411 | case REGISTER_ARM64_X20: |
6412 | case REGISTER_ARM64_X21: |
6413 | case REGISTER_ARM64_X22: |
6414 | case REGISTER_ARM64_X23: |
6415 | case REGISTER_ARM64_X24: |
6416 | case REGISTER_ARM64_X25: |
6417 | case REGISTER_ARM64_X26: |
6418 | case REGISTER_ARM64_X27: |
6419 | case REGISTER_ARM64_X28: |
6420 | ret = (UINT_PTR*)&m_rd.X[regNum - REGISTER_ARM64_X0]; |
6421 | break; |
6422 | |
6423 | case REGISTER_ARM64_LR: |
6424 | ret = (UINT_PTR*)&m_rd.LR; |
6425 | break; |
6426 | |
6427 | case REGISTER_ARM64_PC: |
6428 | ret = (UINT_PTR*)&m_rd.PC; |
6429 | break; |
6430 | #endif |
6431 | |
6432 | default: |
6433 | _ASSERT(!"Invalid register number!" ); |
6434 | } |
6435 | |
6436 | return ret; |
6437 | } |
6438 | |
6439 | // |
6440 | // GetLeftSideAddressOfRegister returns the Left Side address of the given register in the frames current register |
6441 | // display. |
6442 | // |
6443 | CORDB_ADDRESS CordbNativeFrame::GetLeftSideAddressOfRegister(CorDebugRegister regNum) const |
6444 | { |
6445 | #if !defined(USE_REMOTE_REGISTER_ADDRESS) |
6446 | // Use marker values as the register address. This is to implement the funceval breaking change. |
6447 | // |
6448 | if (IsLeafFrame()) |
6449 | { |
6450 | return kLeafFrameRegAddr; |
6451 | } |
6452 | else |
6453 | { |
6454 | return kNonLeafFrameRegAddr; |
6455 | } |
6456 | |
6457 | #else // USE_REMOTE_REGISTER_ADDRESS |
6458 | void* ret = 0; |
6459 | |
6460 | switch (regNum) |
6461 | { |
6462 | |
6463 | #if !defined(DBG_TARGET_AMD64) |
6464 | case REGISTER_FRAME_POINTER: |
6465 | ret = m_rd.pFP; |
6466 | break; |
6467 | #endif |
6468 | |
6469 | #if defined(DBG_TARGET_X86) |
6470 | case REGISTER_X86_EAX: |
6471 | ret = m_rd.pEax; |
6472 | break; |
6473 | |
6474 | case REGISTER_X86_ECX: |
6475 | ret = m_rd.pEcx; |
6476 | break; |
6477 | |
6478 | case REGISTER_X86_EDX: |
6479 | ret = m_rd.pEdx; |
6480 | break; |
6481 | |
6482 | case REGISTER_X86_EBX: |
6483 | ret = m_rd.pEbx; |
6484 | break; |
6485 | |
6486 | case REGISTER_X86_ESI: |
6487 | ret = m_rd.pEsi; |
6488 | break; |
6489 | |
6490 | case REGISTER_X86_EDI: |
6491 | ret = m_rd.pEdi; |
6492 | break; |
6493 | |
6494 | #elif defined(DBG_TARGET_AMD64) |
6495 | case REGISTER_AMD64_RBP: |
6496 | ret = m_rd.pRbp; |
6497 | break; |
6498 | |
6499 | case REGISTER_AMD64_RAX: |
6500 | ret = m_rd.pRax; |
6501 | break; |
6502 | |
6503 | case REGISTER_AMD64_RCX: |
6504 | ret = m_rd.pRcx; |
6505 | break; |
6506 | |
6507 | case REGISTER_AMD64_RDX: |
6508 | ret = m_rd.pRdx; |
6509 | break; |
6510 | |
6511 | case REGISTER_AMD64_RBX: |
6512 | ret = m_rd.pRbx; |
6513 | break; |
6514 | |
6515 | case REGISTER_AMD64_RSI: |
6516 | ret = m_rd.pRsi; |
6517 | break; |
6518 | |
6519 | case REGISTER_AMD64_RDI: |
6520 | ret = m_rd.pRdi; |
6521 | break; |
6522 | |
6523 | case REGISTER_AMD64_R8: |
6524 | ret = m_rd.pR8; |
6525 | break; |
6526 | |
6527 | case REGISTER_AMD64_R9: |
6528 | ret = m_rd.pR9; |
6529 | break; |
6530 | |
6531 | case REGISTER_AMD64_R10: |
6532 | ret = m_rd.pR10; |
6533 | break; |
6534 | |
6535 | case REGISTER_AMD64_R11: |
6536 | ret = m_rd.pR11; |
6537 | break; |
6538 | |
6539 | case REGISTER_AMD64_R12: |
6540 | ret = m_rd.pR12; |
6541 | break; |
6542 | |
6543 | case REGISTER_AMD64_R13: |
6544 | ret = m_rd.pR13; |
6545 | break; |
6546 | |
6547 | case REGISTER_AMD64_R14: |
6548 | ret = m_rd.pR14; |
6549 | break; |
6550 | |
6551 | case REGISTER_AMD64_R15: |
6552 | ret = m_rd.pR15; |
6553 | break; |
6554 | #endif |
6555 | default: |
6556 | _ASSERT(!"Invalid register number!" ); |
6557 | } |
6558 | |
6559 | return PTR_TO_CORDB_ADDRESS(ret); |
6560 | #endif // !USE_REMOTE_REGISTER_ADDRESS |
6561 | } |
6562 | |
6563 | |
6564 | //--------------------------------------------------------------------------------------- |
6565 | // |
6566 | // Given the native variable information of a variable, return its value. |
6567 | // |
6568 | // Arguments: |
6569 | // pNativeVarInfo - the variable information of the variable to be retrieved |
6570 | // |
6571 | // Returns: |
6572 | // Return the specified value. |
6573 | // Throw on error. |
6574 | // |
6575 | // Assumption: |
6576 | // This function assumes that the value is either in a register or on the stack |
6577 | // (i.e. VLT_REG or VLT_STK). |
6578 | // |
6579 | // Notes: |
6580 | // Eventually we should make this more general-purpose. |
6581 | // |
6582 | |
6583 | SIZE_T CordbNativeFrame::GetRegisterOrStackValue(const ICorDebugInfo::NativeVarInfo * pNativeVarInfo) |
6584 | { |
6585 | SIZE_T uResult; |
6586 | |
6587 | if (pNativeVarInfo->loc.vlType == ICorDebugInfo::VLT_REG) |
6588 | { |
6589 | CorDebugRegister reg = ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlReg.vlrReg); |
6590 | uResult = *(reinterpret_cast<SIZE_T *>(GetAddressOfRegister(reg))); |
6591 | } |
6592 | else if (pNativeVarInfo->loc.vlType == ICorDebugInfo::VLT_STK) |
6593 | { |
6594 | CORDB_ADDRESS remoteAddr = GetLSStackAddress(pNativeVarInfo->loc.vlStk.vlsBaseReg, |
6595 | pNativeVarInfo->loc.vlStk.vlsOffset); |
6596 | |
6597 | HRESULT hr = GetProcess()->SafeReadStruct(remoteAddr, &uResult); |
6598 | IfFailThrow(hr); |
6599 | } |
6600 | else |
6601 | { |
6602 | ThrowHR(E_FAIL); |
6603 | } |
6604 | |
6605 | return uResult; |
6606 | } |
6607 | |
6608 | |
6609 | //--------------------------------------------------------------------------------------- |
6610 | // |
6611 | // Looks in a register and retrieves the value as a specific type, returning it |
6612 | // as an ICorDebugValue. |
6613 | // |
6614 | // Arguments: |
6615 | // reg - The register to use. |
6616 | // cbSigBlob - The number of bytes in the signature given. |
6617 | // pvSigBlob - A signature stream that describes the type of the value in the register. |
6618 | // ppValue - OUT: Space to store the resulting ICorDebugValue |
6619 | // |
6620 | // Returns: |
6621 | // S_OK on success, else an error code. |
6622 | // |
6623 | HRESULT CordbNativeFrame::GetLocalRegisterValue(CorDebugRegister reg, |
6624 | ULONG cbSigBlob, |
6625 | PCCOR_SIGNATURE pvSigBlob, |
6626 | ICorDebugValue ** ppValue) |
6627 | { |
6628 | PUBLIC_REENTRANT_API_ENTRY(this); |
6629 | FAIL_IF_NEUTERED(this); |
6630 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6631 | |
6632 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pvSigBlob, BYTE, cbSigBlob, true, false); |
6633 | |
6634 | CordbType * pType; |
6635 | |
6636 | SigParser sigParser(pvSigBlob, cbSigBlob); |
6637 | |
6638 | Instantiation emptyInst; |
6639 | |
6640 | HRESULT hr = CordbType::SigToType(m_JITILFrame->GetModule(), &sigParser, &emptyInst, &pType); |
6641 | |
6642 | if (FAILED(hr)) |
6643 | { |
6644 | return hr; |
6645 | } |
6646 | |
6647 | return GetLocalRegisterValue(reg, pType, ppValue); |
6648 | } |
6649 | |
6650 | //--------------------------------------------------------------------------------------- |
6651 | // |
6652 | // Looks in two registers and retrieves the value as a specific type, returning it |
6653 | // as an ICorDebugValue. |
6654 | // |
6655 | // Arguments: |
6656 | // highWordReg - The register to use for the high word. |
6657 | // lowWordReg - The register to use for the low word. |
6658 | // cbSigBlob - The number of bytes in the signature given. |
6659 | // pvSigBlob - A signature stream that describes the type of the value in the register. |
6660 | // ppValue - OUT: Space to store the resulting ICorDebugValue |
6661 | // |
6662 | // Returns: |
6663 | // S_OK on success, else an error code. |
6664 | // |
6665 | HRESULT CordbNativeFrame::GetLocalDoubleRegisterValue(CorDebugRegister highWordReg, |
6666 | CorDebugRegister lowWordReg, |
6667 | ULONG cbSigBlob, |
6668 | PCCOR_SIGNATURE pvSigBlob, |
6669 | ICorDebugValue ** ppValue) |
6670 | { |
6671 | PUBLIC_REENTRANT_API_ENTRY(this); |
6672 | FAIL_IF_NEUTERED(this); |
6673 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6674 | |
6675 | if (cbSigBlob == 0) |
6676 | { |
6677 | return E_INVALIDARG; |
6678 | } |
6679 | |
6680 | CordbType * pType; |
6681 | |
6682 | SigParser sigParser(pvSigBlob, cbSigBlob); |
6683 | |
6684 | Instantiation emptyInst; |
6685 | |
6686 | HRESULT hr = CordbType::SigToType(m_JITILFrame->GetModule(), &sigParser, &emptyInst, &pType); |
6687 | |
6688 | if (FAILED(hr)) |
6689 | { |
6690 | return hr; |
6691 | } |
6692 | |
6693 | return GetLocalDoubleRegisterValue(highWordReg, lowWordReg, pType, ppValue); |
6694 | } |
6695 | |
6696 | |
6697 | //--------------------------------------------------------------------------------------- |
6698 | // |
6699 | // Uses an address and retrieves the value as a specific type, returning it |
6700 | // as an ICorDebugValue. |
6701 | // |
6702 | // Arguments: |
6703 | // address - A local memory address. |
6704 | // cbSigBlob - The number of bytes in the signature given. |
6705 | // pvSigBlob - A signature stream that describes the type of the value in the register. |
6706 | // ppValue - OUT: Space to store the resulting ICorDebugValue |
6707 | // |
6708 | // Returns: |
6709 | // S_OK on success, else an error code. |
6710 | // |
6711 | HRESULT CordbNativeFrame::GetLocalMemoryValue(CORDB_ADDRESS address, |
6712 | ULONG cbSigBlob, |
6713 | PCCOR_SIGNATURE pvSigBlob, |
6714 | ICorDebugValue ** ppValue) |
6715 | { |
6716 | PUBLIC_REENTRANT_API_ENTRY(this); |
6717 | FAIL_IF_NEUTERED(this); |
6718 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6719 | |
6720 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pvSigBlob, BYTE, cbSigBlob, true, false); |
6721 | |
6722 | CordbType * pType; |
6723 | |
6724 | SigParser sigParser(pvSigBlob, cbSigBlob); |
6725 | |
6726 | Instantiation emptyInst; |
6727 | |
6728 | HRESULT hr = CordbType::SigToType(m_JITILFrame->GetModule(), &sigParser, &emptyInst, &pType); |
6729 | |
6730 | if (FAILED(hr)) |
6731 | { |
6732 | return hr; |
6733 | } |
6734 | |
6735 | return GetLocalMemoryValue(address, pType, ppValue); |
6736 | } |
6737 | |
6738 | |
6739 | //--------------------------------------------------------------------------------------- |
6740 | // |
6741 | // Uses a register and an address, retrieving the value as a specific type, returning it |
6742 | // as an ICorDebugValue. |
6743 | // |
6744 | // Arguments: |
6745 | // highWordReg - Register to use as the high word. |
6746 | // lowWordAddress - A local memory address containing the low word. |
6747 | // cbSigBlob - The number of bytes in the signature given. |
6748 | // pvSigBlob - A signature stream that describes the type of the value in the register. |
6749 | // ppValue - OUT: Space to store the resulting ICorDebugValue |
6750 | // |
6751 | // Returns: |
6752 | // S_OK on success, else an error code. |
6753 | // |
6754 | HRESULT CordbNativeFrame::GetLocalRegisterMemoryValue(CorDebugRegister highWordReg, |
6755 | CORDB_ADDRESS lowWordAddress, |
6756 | ULONG cbSigBlob, |
6757 | PCCOR_SIGNATURE pvSigBlob, |
6758 | ICorDebugValue ** ppValue) |
6759 | { |
6760 | PUBLIC_REENTRANT_API_ENTRY(this); |
6761 | FAIL_IF_NEUTERED(this); |
6762 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6763 | |
6764 | if (cbSigBlob == 0) |
6765 | { |
6766 | return E_INVALIDARG; |
6767 | } |
6768 | |
6769 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pvSigBlob, BYTE, cbSigBlob, true, true); |
6770 | |
6771 | CordbType * pType; |
6772 | |
6773 | SigParser sigParser(pvSigBlob, cbSigBlob); |
6774 | |
6775 | Instantiation emptyInst; |
6776 | |
6777 | HRESULT hr = CordbType::SigToType(m_JITILFrame->GetModule(), &sigParser, &emptyInst, &pType); |
6778 | |
6779 | if (FAILED(hr)) |
6780 | { |
6781 | return hr; |
6782 | } |
6783 | |
6784 | return GetLocalRegisterMemoryValue(highWordReg, lowWordAddress, pType, ppValue); |
6785 | } |
6786 | |
6787 | |
6788 | //--------------------------------------------------------------------------------------- |
6789 | // |
6790 | // Uses a register and an address, retrieving the value as a specific type, returning it |
6791 | // as an ICorDebugValue. |
6792 | // |
6793 | // Arguments: |
6794 | // highWordReg - A local memory address to use as the high word. |
6795 | // lowWordAddress - Register containing the low word. |
6796 | // cbSigBlob - The number of bytes in the signature given. |
6797 | // pvSigBlob - A signature stream that describes the type of the value in the register. |
6798 | // ppValue - OUT: Space to store the resulting ICorDebugValue |
6799 | // |
6800 | // Returns: |
6801 | // S_OK on success, else an error code. |
6802 | // |
6803 | HRESULT CordbNativeFrame::GetLocalMemoryRegisterValue(CORDB_ADDRESS highWordAddress, |
6804 | CorDebugRegister lowWordRegister, |
6805 | ULONG cbSigBlob, |
6806 | PCCOR_SIGNATURE pvSigBlob, |
6807 | ICorDebugValue ** ppValue) |
6808 | { |
6809 | PUBLIC_REENTRANT_API_ENTRY(this); |
6810 | FAIL_IF_NEUTERED(this); |
6811 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6812 | |
6813 | if (cbSigBlob == 0) |
6814 | { |
6815 | return E_INVALIDARG; |
6816 | } |
6817 | |
6818 | VALIDATE_POINTER_TO_OBJECT_ARRAY(pvSigBlob, BYTE, cbSigBlob, true, true); |
6819 | |
6820 | CordbType * pType; |
6821 | |
6822 | SigParser sigParser(pvSigBlob, cbSigBlob); |
6823 | |
6824 | Instantiation emptyInst; |
6825 | |
6826 | HRESULT hr = CordbType::SigToType(m_JITILFrame->GetModule(), &sigParser, &emptyInst, &pType); |
6827 | |
6828 | if (FAILED(hr)) |
6829 | { |
6830 | return hr; |
6831 | } |
6832 | |
6833 | return GetLocalMemoryRegisterValue(highWordAddress, lowWordRegister, pType, ppValue); |
6834 | } |
6835 | |
6836 | |
6837 | |
6838 | HRESULT CordbNativeFrame::GetLocalRegisterValue(CorDebugRegister reg, |
6839 | CordbType * pType, |
6840 | ICorDebugValue **ppValue) |
6841 | { |
6842 | PUBLIC_REENTRANT_API_ENTRY(this); |
6843 | FAIL_IF_NEUTERED(this); |
6844 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
6845 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6846 | |
6847 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_WIN64) |
6848 | #if defined(DBG_TARGET_X86) |
6849 | if ((reg >= REGISTER_X86_FPSTACK_0) && (reg <= REGISTER_X86_FPSTACK_7)) |
6850 | #elif defined(DBG_TARGET_AMD64) |
6851 | if ((reg >= REGISTER_AMD64_XMM0) && (reg <= REGISTER_AMD64_XMM15)) |
6852 | #elif defined(DBG_TARGET_ARM64) |
6853 | if ((reg >= REGISTER_ARM64_V0) && (reg <= REGISTER_ARM64_V31)) |
6854 | #endif |
6855 | { |
6856 | return GetLocalFloatingPointValue(reg, pType, ppValue); |
6857 | } |
6858 | #endif |
6859 | |
6860 | // The address of the given register is the address of the value |
6861 | // in this process. We have no remote address here. |
6862 | void *pLocalValue = (void*)GetAddressOfRegister(reg); |
6863 | HRESULT hr = S_OK; |
6864 | |
6865 | EX_TRY |
6866 | { |
6867 | // Provide the register info as we create the value. CreateValueByType will transfer ownership of this to |
6868 | // the new instance of CordbValue. |
6869 | EnregisteredValueHomeHolder pRemoteReg(new RegValueHome(this, reg)); |
6870 | EnregisteredValueHomeHolder * pRegHolder = pRemoteReg.GetAddr(); |
6871 | |
6872 | ICorDebugValue *pValue; |
6873 | CordbValue::CreateValueByType(GetCurrentAppDomain(), |
6874 | pType, |
6875 | false, |
6876 | EMPTY_BUFFER, |
6877 | MemoryRange(pLocalValue, REG_SIZE), |
6878 | pRegHolder, |
6879 | &pValue); // throws |
6880 | |
6881 | *ppValue = pValue; |
6882 | } |
6883 | EX_CATCH_HRESULT(hr); |
6884 | return hr; |
6885 | } |
6886 | |
6887 | HRESULT CordbNativeFrame::GetLocalDoubleRegisterValue( |
6888 | CorDebugRegister highWordReg, |
6889 | CorDebugRegister lowWordReg, |
6890 | CordbType * pType, |
6891 | ICorDebugValue **ppValue) |
6892 | { |
6893 | PUBLIC_REENTRANT_API_ENTRY(this); |
6894 | FAIL_IF_NEUTERED(this); |
6895 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
6896 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6897 | |
6898 | HRESULT hr = S_OK; |
6899 | EX_TRY |
6900 | { |
6901 | // Provide the register info as we create the value. CreateValueByType will transfer ownership of this to |
6902 | // the new instance of CordbValue. |
6903 | EnregisteredValueHomeHolder pRemoteReg(new RegRegValueHome(this, highWordReg, lowWordReg)); |
6904 | EnregisteredValueHomeHolder * pRegHolder = pRemoteReg.GetAddr(); |
6905 | |
6906 | CordbValue::CreateValueByType(GetCurrentAppDomain(), |
6907 | pType, |
6908 | false, |
6909 | EMPTY_BUFFER, |
6910 | MemoryRange(NULL, 0), |
6911 | pRegHolder, |
6912 | ppValue); // throws |
6913 | } |
6914 | EX_CATCH_HRESULT(hr); |
6915 | |
6916 | #ifdef _DEBUG |
6917 | { |
6918 | // sanity check object size |
6919 | if (SUCCEEDED(hr)) |
6920 | { |
6921 | ULONG32 objectSize; |
6922 | hr = (*ppValue)->GetSize(&objectSize); |
6923 | _ASSERTE(SUCCEEDED(hr)); |
6924 | // |
6925 | // nickbe |
6926 | // 10/31/2002 11:09:42 |
6927 | // |
6928 | // This assert assumes that the JIT will only partially enregister |
6929 | // objects that have a size equal to twice the size of a register. |
6930 | // |
6931 | _ASSERTE(objectSize == 2 * sizeof(void*)); |
6932 | } |
6933 | } |
6934 | #endif |
6935 | return hr; |
6936 | } |
6937 | |
6938 | HRESULT |
6939 | CordbNativeFrame::GetLocalMemoryValue(CORDB_ADDRESS address, |
6940 | CordbType * pType, |
6941 | ICorDebugValue **ppValue) |
6942 | { |
6943 | PUBLIC_REENTRANT_API_ENTRY(this); |
6944 | FAIL_IF_NEUTERED(this); |
6945 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
6946 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6947 | |
6948 | _ASSERTE(m_nativeCode->GetFunction() != NULL); |
6949 | HRESULT hr = S_OK; |
6950 | |
6951 | ICorDebugValue *pValue; |
6952 | EX_TRY |
6953 | { |
6954 | CordbValue::CreateValueByType(GetCurrentAppDomain(), |
6955 | pType, |
6956 | false, |
6957 | TargetBuffer(address, CordbValue::GetSizeForType(pType, kUnboxed)), |
6958 | MemoryRange(NULL, 0), |
6959 | NULL, |
6960 | &pValue); // throws |
6961 | } |
6962 | EX_CATCH_HRESULT(hr); |
6963 | |
6964 | if (SUCCEEDED(hr)) |
6965 | *ppValue = pValue; |
6966 | |
6967 | return hr; |
6968 | } |
6969 | |
6970 | HRESULT |
6971 | CordbNativeFrame::GetLocalByRefMemoryValue(CORDB_ADDRESS address, |
6972 | CordbType * pType, |
6973 | ICorDebugValue **ppValue) |
6974 | { |
6975 | INTERNAL_API_ENTRY(this); |
6976 | FAIL_IF_NEUTERED(this); |
6977 | |
6978 | LPVOID actualAddress = NULL; |
6979 | HRESULT hr = GetProcess()->SafeReadStruct(address, &actualAddress); |
6980 | if (FAILED(hr)) |
6981 | { |
6982 | return hr; |
6983 | } |
6984 | |
6985 | return GetLocalMemoryValue(PTR_TO_CORDB_ADDRESS(actualAddress), pType, ppValue); |
6986 | } |
6987 | |
6988 | HRESULT |
6989 | CordbNativeFrame::GetLocalRegisterMemoryValue(CorDebugRegister highWordReg, |
6990 | CORDB_ADDRESS lowWordAddress, |
6991 | CordbType * pType, |
6992 | ICorDebugValue **ppValue) |
6993 | { |
6994 | PUBLIC_REENTRANT_API_ENTRY(this); |
6995 | FAIL_IF_NEUTERED(this); |
6996 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
6997 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
6998 | |
6999 | HRESULT hr = S_OK; |
7000 | EX_TRY |
7001 | { |
7002 | // Provide the register info as we create the value. CreateValueByType will transfer ownership of this to |
7003 | // the new instance of CordbValue. |
7004 | EnregisteredValueHomeHolder pRemoteReg(new RegMemValueHome(this, |
7005 | highWordReg, |
7006 | lowWordAddress)); |
7007 | EnregisteredValueHomeHolder * pRegHolder = pRemoteReg.GetAddr(); |
7008 | |
7009 | CordbValue::CreateValueByType(GetCurrentAppDomain(), |
7010 | pType, |
7011 | false, |
7012 | EMPTY_BUFFER, |
7013 | MemoryRange(NULL, 0), |
7014 | pRegHolder, |
7015 | ppValue); // throws |
7016 | } |
7017 | EX_CATCH_HRESULT(hr); |
7018 | |
7019 | #ifdef _DEBUG |
7020 | { |
7021 | if (SUCCEEDED(hr)) |
7022 | { |
7023 | ULONG32 objectSize; |
7024 | hr = (*ppValue)->GetSize(&objectSize); |
7025 | _ASSERTE(SUCCEEDED(hr)); |
7026 | // See the comment in CordbNativeFrame::GetLocalDoubleRegisterValue |
7027 | // for more information on this assertion |
7028 | _ASSERTE(objectSize == 2 * sizeof(void*)); |
7029 | } |
7030 | } |
7031 | #endif |
7032 | return hr; |
7033 | } |
7034 | |
7035 | HRESULT |
7036 | CordbNativeFrame::GetLocalMemoryRegisterValue(CORDB_ADDRESS highWordAddress, |
7037 | CorDebugRegister lowWordRegister, |
7038 | CordbType * pType, |
7039 | ICorDebugValue **ppValue) |
7040 | { |
7041 | PUBLIC_REENTRANT_API_ENTRY(this); |
7042 | FAIL_IF_NEUTERED(this); |
7043 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
7044 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
7045 | |
7046 | HRESULT hr = S_OK; |
7047 | EX_TRY |
7048 | { |
7049 | // Provide the register info as we create the value. CreateValueByType will transfer ownership of this to |
7050 | // the new instance of CordbValue. |
7051 | EnregisteredValueHomeHolder pRemoteReg(new MemRegValueHome(this, |
7052 | lowWordRegister, |
7053 | highWordAddress)); |
7054 | EnregisteredValueHomeHolder * pRegHolder = pRemoteReg.GetAddr(); |
7055 | |
7056 | CordbValue::CreateValueByType(GetCurrentAppDomain(), |
7057 | pType, |
7058 | false, |
7059 | EMPTY_BUFFER, |
7060 | MemoryRange(NULL, 0), |
7061 | pRegHolder, |
7062 | ppValue); // throws |
7063 | } |
7064 | EX_CATCH_HRESULT(hr); |
7065 | |
7066 | #ifdef _DEBUG |
7067 | { |
7068 | if (SUCCEEDED(hr)) |
7069 | { |
7070 | ULONG32 objectSize; |
7071 | hr = (*ppValue)->GetSize(&objectSize); |
7072 | _ASSERTE(SUCCEEDED(hr)); |
7073 | // See the comment in CordbNativeFrame::GetLocalDoubleRegisterValue |
7074 | // for more information on this assertion |
7075 | _ASSERTE(objectSize == 2 * sizeof(void*)); |
7076 | } |
7077 | } |
7078 | #endif |
7079 | return hr; |
7080 | } |
7081 | |
7082 | HRESULT CordbNativeFrame::GetLocalFloatingPointValue(DWORD index, |
7083 | CordbType * pType, |
7084 | ICorDebugValue **ppValue) |
7085 | { |
7086 | PUBLIC_REENTRANT_API_ENTRY(this); |
7087 | FAIL_IF_NEUTERED(this); |
7088 | HRESULT hr = S_OK; |
7089 | |
7090 | CorElementType et = pType->m_elementType; |
7091 | |
7092 | if ((et != ELEMENT_TYPE_R4) && |
7093 | (et != ELEMENT_TYPE_R8)) |
7094 | return E_INVALIDARG; |
7095 | |
7096 | #if defined(DBG_TARGET_AMD64) |
7097 | if (!((index >= REGISTER_AMD64_XMM0) && |
7098 | (index <= REGISTER_AMD64_XMM15))) |
7099 | return E_INVALIDARG; |
7100 | index -= REGISTER_AMD64_XMM0; |
7101 | #elif defined(DBG_TARGET_ARM64) |
7102 | if (!((index >= REGISTER_ARM64_V0) && |
7103 | (index <= REGISTER_ARM64_V31))) |
7104 | return E_INVALIDARG; |
7105 | index -= REGISTER_ARM64_V0; |
7106 | #elif defined(DBG_TARGET_ARM) |
7107 | if (!((index >= REGISTER_ARM_D0) && |
7108 | (index <= REGISTER_ARM_D31))) |
7109 | return E_INVALIDARG; |
7110 | index -= REGISTER_ARM_D0; |
7111 | #else |
7112 | if (!((index >= REGISTER_X86_FPSTACK_0) && |
7113 | (index <= REGISTER_X86_FPSTACK_7))) |
7114 | return E_INVALIDARG; |
7115 | index -= REGISTER_X86_FPSTACK_0; |
7116 | #endif |
7117 | |
7118 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
7119 | |
7120 | |
7121 | // Make sure the thread's floating point stack state is loaded |
7122 | // over from the left side. |
7123 | // |
7124 | CordbThread *pThread = m_pThread; |
7125 | |
7126 | EX_TRY |
7127 | { |
7128 | if (!pThread->m_fFloatStateValid) |
7129 | { |
7130 | pThread->LoadFloatState(); |
7131 | } |
7132 | } |
7133 | EX_CATCH_HRESULT(hr); |
7134 | if (SUCCEEDED(hr)) |
7135 | { |
7136 | #if !defined(DBG_TARGET_WIN64) |
7137 | // This is needed on x86 because we are dealing with a stack. |
7138 | index = pThread->m_floatStackTop - index; |
7139 | #endif |
7140 | |
7141 | if (index >= (sizeof(pThread->m_floatValues) / |
7142 | sizeof(pThread->m_floatValues[0]))) |
7143 | return E_INVALIDARG; |
7144 | |
7145 | #ifdef DBG_TARGET_X86 |
7146 | // A workaround (sort of) to get around the difference in format between |
7147 | // a float value and a double value. We can't simply cast a double pointer to |
7148 | // a float pointer. Instead, we have to cast the double itself to a float. |
7149 | if (pType->m_elementType == ELEMENT_TYPE_R4) |
7150 | *(float *)&(pThread->m_floatValues[index]) = (float)pThread->m_floatValues[index]; |
7151 | #endif |
7152 | |
7153 | ICorDebugValue* pValue; |
7154 | |
7155 | EX_TRY |
7156 | { |
7157 | // Provide the register info as we create the value. CreateValueByType will transfer ownership of this to |
7158 | // the new instance of CordbValue. |
7159 | EnregisteredValueHomeHolder pRemoteReg(new FloatRegValueHome(this, index)); |
7160 | EnregisteredValueHomeHolder * pRegHolder = pRemoteReg.GetAddr(); |
7161 | |
7162 | CordbValue::CreateValueByType(GetCurrentAppDomain(), |
7163 | pType, |
7164 | false, |
7165 | EMPTY_BUFFER, |
7166 | MemoryRange(&(pThread->m_floatValues[index]), sizeof(double)), |
7167 | pRegHolder, |
7168 | &pValue); // throws |
7169 | |
7170 | *ppValue = pValue; |
7171 | } |
7172 | EX_CATCH_HRESULT(hr); |
7173 | |
7174 | } |
7175 | |
7176 | return hr; |
7177 | } |
7178 | |
7179 | //--------------------------------------------------------------------------------------- |
7180 | // |
7181 | // Quick accessor to tell if we're the leaf frame. |
7182 | // |
7183 | // Return Value: |
7184 | // whether we are the leaf frame or not |
7185 | // |
7186 | |
7187 | bool CordbNativeFrame::IsLeafFrame() const |
7188 | { |
7189 | CONTRACTL |
7190 | { |
7191 | THROWS; |
7192 | } |
7193 | CONTRACTL_END; |
7194 | |
7195 | // Should only be called by non-neutered stuff. |
7196 | // Also, since we're not neutered, we know we have a Thread object, and we know it's state is current. |
7197 | _ASSERTE(!this->IsNeutered()); |
7198 | |
7199 | // If the thread's state is sleeping, then there's no frame below us, but we're actually |
7200 | // not the leaf frame. |
7201 | // @todo- consider having Sleep / Wait / Join be an ICDInternalFrame. |
7202 | _ASSERTE(m_pThread != NULL); // not neutered, so should have a thread |
7203 | if (m_pThread->IsThreadWaitingOrSleeping()) |
7204 | { |
7205 | return false; |
7206 | } |
7207 | |
7208 | if (!m_optfIsLeafFrame.HasValue()) |
7209 | { |
7210 | if (GetProcess()->GetShim() != NULL) |
7211 | { |
7212 | // In V2, the definition of "leaf frame" is the leaf frame in the leaf chain in the stackwalk. |
7213 | PRIVATE_SHIM_CALLBACK_IN_THIS_SCOPE0(GetProcess()); |
7214 | ShimStackWalk * pSW = GetProcess()->GetShim()->LookupOrCreateShimStackWalk(m_pThread); |
7215 | |
7216 | // check if there is any chain |
7217 | if (pSW->GetChainCount() > 0) |
7218 | { |
7219 | // check if the leaf chain has any frame |
7220 | if (pSW->GetChain(0)->GetLastFrameIndex() > 0) |
7221 | { |
7222 | CordbFrame * pCFrame = GetCordbFrameFromInterface(pSW->GetFrame(0)); |
7223 | CordbNativeFrame * pNFrame = pCFrame->GetAsNativeFrame(); |
7224 | if (pNFrame != NULL) |
7225 | { |
7226 | // check if the leaf frame in the leaf chain is "this" |
7227 | if (CompareControlRegisters(GetContext(), pNFrame->GetContext())) |
7228 | { |
7229 | m_optfIsLeafFrame = TRUE; |
7230 | } |
7231 | } |
7232 | } |
7233 | } |
7234 | |
7235 | if (!m_optfIsLeafFrame.HasValue()) |
7236 | { |
7237 | m_optfIsLeafFrame = FALSE; |
7238 | } |
7239 | } |
7240 | else |
7241 | { |
7242 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
7243 | m_optfIsLeafFrame = (pDAC->IsLeafFrame(m_pThread->m_vmThreadToken, &m_context) == TRUE); |
7244 | } |
7245 | } |
7246 | return m_optfIsLeafFrame.GetValue(); |
7247 | } |
7248 | |
7249 | //--------------------------------------------------------------------------------------- |
7250 | // |
7251 | // Get the offset used to determine if a variable is live in a particular method frame. |
7252 | // |
7253 | // Return Value: |
7254 | // the offset used for inspection purposes |
7255 | // |
7256 | // Notes: |
7257 | // On WIN64, variables used in funclets are always homed on the stack. Morever, the variable lifetime |
7258 | // information only covers the parent method. The idea is that the variables which are live in a funclet |
7259 | // will be the variables which are live in the parent method at the offset at which the exception occurs. |
7260 | // Thus, to determine if a variable is live in a funclet frame, we need to use the offset of the parent |
7261 | // method frame at which the exception occurs. |
7262 | // |
7263 | |
7264 | SIZE_T CordbNativeFrame::GetInspectionIP() |
7265 | { |
7266 | #ifdef WIN64EXCEPTIONS |
7267 | // On 64-bit, if this is a funclet, then return the offset of the parent method frame at which |
7268 | // the exception occurs. Otherwise just return the normal offset. |
7269 | return (IsFunclet() ? GetParentIP() : m_ip); |
7270 | #else |
7271 | // Always return the normal offset on all other platforms. |
7272 | return m_ip; |
7273 | #endif // WIN64EXCEPTIONS |
7274 | } |
7275 | |
7276 | //--------------------------------------------------------------------------------------- |
7277 | // |
7278 | // Return whether this is a funclet method frame. |
7279 | // |
7280 | // Return Value: |
7281 | // whether this is a funclet method frame. |
7282 | // |
7283 | |
7284 | bool CordbNativeFrame::IsFunclet() |
7285 | { |
7286 | #ifdef WIN64EXCEPTIONS |
7287 | return (m_misc.parentIP != NULL); |
7288 | #else |
7289 | return false; |
7290 | #endif // WIN64EXCEPTIONS |
7291 | } |
7292 | |
7293 | //--------------------------------------------------------------------------------------- |
7294 | // |
7295 | // Return whether this is a filter funclet method frame. |
7296 | // |
7297 | // Return Value: |
7298 | // whether this is a filter funclet method frame. |
7299 | // |
7300 | |
7301 | bool CordbNativeFrame::IsFilterFunclet() |
7302 | { |
7303 | #ifdef WIN64EXCEPTIONS |
7304 | return (IsFunclet() && m_misc.fIsFilterFunclet); |
7305 | #else |
7306 | return false; |
7307 | #endif // WIN64EXCEPTIONS |
7308 | } |
7309 | |
7310 | |
7311 | #ifdef WIN64EXCEPTIONS |
7312 | //--------------------------------------------------------------------------------------- |
7313 | // |
7314 | // Return the offset of the parent method frame at which the exception occurs. |
7315 | // |
7316 | // Return Value: |
7317 | // the offset of the parent method frame at which the exception occurs |
7318 | // |
7319 | |
7320 | SIZE_T CordbNativeFrame::GetParentIP() |
7321 | { |
7322 | return m_misc.parentIP; |
7323 | } |
7324 | #endif // WIN64EXCEPTIONS |
7325 | |
7326 | // Accessor for the shim private hook code:CordbThread::ConvertFrameForILMethodWithoutMetadata. |
7327 | // Refer to that function for comments on the return value, the argument, etc. |
7328 | BOOL CordbNativeFrame::ConvertNativeFrameForILMethodWithoutMetadata( |
7329 | ICorDebugInternalFrame2 ** ppInternalFrame2) |
7330 | { |
7331 | _ASSERTE(ppInternalFrame2 != NULL); |
7332 | *ppInternalFrame2 = NULL; |
7333 | |
7334 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
7335 | IDacDbiInterface::DynamicMethodType type = |
7336 | pDAC->IsILStubOrLCGMethod(GetNativeCode()->GetVMNativeCodeMethodDescToken()); |
7337 | |
7338 | // Here are the conversion rules: |
7339 | // 1) For a normal managed method, we don't convert, and we return FALSE. |
7340 | // 2) For an IL stub, we convert to NULL, and we return TRUE. |
7341 | // 3) For a dynamic method, we convert to a STUBFRAME_LIGHTWEIGHT_FUNCTION, and we return TRUE. |
7342 | if (type == IDacDbiInterface::kNone) |
7343 | { |
7344 | return FALSE; |
7345 | } |
7346 | else if (type == IDacDbiInterface::kILStub) |
7347 | { |
7348 | return TRUE; |
7349 | } |
7350 | else if (type == IDacDbiInterface::kLCGMethod) |
7351 | { |
7352 | RSInitHolder<CordbInternalFrame> pInternalFrame( |
7353 | new CordbInternalFrame(m_pThread, |
7354 | m_fp, |
7355 | m_currentAppDomain, |
7356 | STUBFRAME_LIGHTWEIGHT_FUNCTION, |
7357 | GetNativeCode()->GetMetadataToken(), |
7358 | GetNativeCode()->GetFunction(), |
7359 | GetNativeCode()->GetVMNativeCodeMethodDescToken())); |
7360 | |
7361 | pInternalFrame.TransferOwnershipExternal(ppInternalFrame2); |
7362 | return TRUE; |
7363 | } |
7364 | |
7365 | UNREACHABLE(); |
7366 | } |
7367 | |
7368 | /* ------------------------------------------------------------------------- * |
7369 | * JIT-IL Frame class |
7370 | * ------------------------------------------------------------------------- */ |
7371 | |
7372 | CordbJITILFrame::CordbJITILFrame(CordbNativeFrame * pNativeFrame, |
7373 | CordbILCode * pCode, |
7374 | UINT_PTR ip, |
7375 | CorDebugMappingResult mapping, |
7376 | GENERICS_TYPE_TOKEN exactGenericArgsToken, |
7377 | DWORD dwExactGenericArgsTokenIndex, |
7378 | bool fVarArgFnx, |
7379 | CordbReJitILCode * pRejitCode) |
7380 | : CordbBase(pNativeFrame->GetProcess(), 0, enumCordbJITILFrame), |
7381 | m_nativeFrame(pNativeFrame), |
7382 | m_ilCode(pCode), |
7383 | m_ip(ip), |
7384 | m_mapping(mapping), |
7385 | m_fVarArgFnx(fVarArgFnx), |
7386 | m_allArgsCount(0), |
7387 | m_rgbSigParserBuf(NULL), |
7388 | m_FirstArgAddr(NULL), |
7389 | m_rgNVI(NULL), |
7390 | m_genericArgs(), |
7391 | m_genericArgsLoaded(false), |
7392 | m_frameParamsToken(exactGenericArgsToken), |
7393 | m_dwFrameParamsTokenIndex(dwExactGenericArgsTokenIndex), |
7394 | m_pReJitCode(pRejitCode) |
7395 | { |
7396 | // We'll initialize the SigParser in CordbJITILFrame::Init(). |
7397 | m_sigParserCached = SigParser(NULL, 0); |
7398 | _ASSERTE(m_sigParserCached.IsNull()); |
7399 | |
7400 | HRESULT hr = S_OK; |
7401 | EX_TRY |
7402 | { |
7403 | m_nativeFrame->m_pThread->GetRefreshStackNeuterList()->Add(GetProcess(), this); |
7404 | } |
7405 | EX_CATCH_HRESULT(hr); |
7406 | SetUnrecoverableIfFailed(GetProcess(), hr); |
7407 | } |
7408 | |
7409 | //--------------------------------------------------------------------------------------- |
7410 | // |
7411 | // Initialize a CordbJITILFrame object. Must be called after allocating the object and before using it. |
7412 | // If Init fails, then destroy the object and release the memory. |
7413 | // |
7414 | // Return Value: |
7415 | // HRESULT for the operation |
7416 | // |
7417 | // Notes: |
7418 | // This is a nop if the function is not a vararg function. |
7419 | // |
7420 | |
7421 | HRESULT CordbJITILFrame::Init() |
7422 | { |
7423 | // ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
7424 | HRESULT hr = S_OK; |
7425 | |
7426 | |
7427 | EX_TRY |
7428 | { |
7429 | _ASSERTE(m_ilCode != NULL); |
7430 | |
7431 | if (m_fVarArgFnx) |
7432 | { |
7433 | // First, we need to find the VASigCookie. Use the native var info to do so. |
7434 | const ICorDebugInfo::NativeVarInfo * pNativeVarInfo = NULL; |
7435 | CordbNativeFrame * pNativeFrame = this->m_nativeFrame; |
7436 | |
7437 | pNativeFrame->m_nativeCode->LoadNativeInfo(); |
7438 | hr = pNativeFrame->m_nativeCode->ILVariableToNative((DWORD)ICorDebugInfo::VARARGS_HND_ILNUM, |
7439 | pNativeFrame->GetInspectionIP(), |
7440 | &pNativeVarInfo); |
7441 | IfFailThrow(hr); |
7442 | |
7443 | // Check for the case where the VASigCookie isn't pushed on the stack yet. |
7444 | // This should only be a problem with optimized code. |
7445 | if (pNativeVarInfo->loc.vlType != ICorDebugInfo::VLT_STK) |
7446 | { |
7447 | ThrowHR(E_FAIL); |
7448 | } |
7449 | |
7450 | // Retrieve the target address. |
7451 | CORDB_ADDRESS pRemoteValue = pNativeFrame->GetLSStackAddress( |
7452 | pNativeVarInfo->loc.vlStk.vlsBaseReg, |
7453 | pNativeVarInfo->loc.vlStk.vlsOffset); |
7454 | |
7455 | CORDB_ADDRESS argBase; |
7456 | // Now is the time to ask DacDbi to retrieve the information based on the VASigCookie. |
7457 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
7458 | TargetBuffer sigTargetBuf = pDAC->GetVarArgSig(pRemoteValue, &argBase); |
7459 | |
7460 | // make sure we are not leaking any memory |
7461 | _ASSERTE(m_rgbSigParserBuf == NULL); |
7462 | |
7463 | m_rgbSigParserBuf = new BYTE[sigTargetBuf.cbSize]; |
7464 | GetProcess()->SafeReadBuffer(sigTargetBuf, m_rgbSigParserBuf); |
7465 | m_sigParserCached = SigParser(m_rgbSigParserBuf, sigTargetBuf.cbSize); |
7466 | |
7467 | // Note that we should never mutate the SigParser. |
7468 | // Instead, make a copy and work with the copy instead. |
7469 | if (!m_sigParserCached.IsNull()) |
7470 | { |
7471 | SigParser sigParser = m_sigParserCached; |
7472 | |
7473 | // get the actual count of arguments, including the var args |
7474 | IfFailThrow(sigParser.SkipMethodHeaderSignature(&m_allArgsCount)); |
7475 | |
7476 | BOOL methodIsStatic; |
7477 | |
7478 | m_ilCode->GetSig(NULL, NULL, &methodIsStatic); // throws |
7479 | |
7480 | if (!methodIsStatic) |
7481 | { |
7482 | m_allArgsCount++; // skip the "this" object |
7483 | } |
7484 | |
7485 | // initialize the variable lifetime information |
7486 | m_rgNVI = new ICorDebugInfo::NativeVarInfo[m_allArgsCount]; // throws |
7487 | |
7488 | _ASSERTE(ICorDebugInfo::VLT_COUNT <= ICorDebugInfo::VLT_INVALID); |
7489 | |
7490 | for (ULONG i = 0; i < m_allArgsCount; i++) |
7491 | { |
7492 | m_rgNVI[i].loc.vlType = ICorDebugInfo::VLT_INVALID; |
7493 | } |
7494 | } |
7495 | |
7496 | // GetVarArgSig gets the address of the beginning of the arguments pushed for this frame. |
7497 | // We'll need the address of the first argument, which will depend on its size and the |
7498 | // calling convention, so we'll commpute that now that we have the SigParser. |
7499 | CordbType * pArgType; |
7500 | IfFailThrow(GetArgumentType(0, &pArgType)); |
7501 | ULONG32 argSize = 0; |
7502 | IfFailThrow(pArgType->GetUnboxedObjectSize(&argSize)); |
7503 | #if defined(_TARGET_X86_) // (STACK_GROWS_DOWN_ON_ARGS_WALK) |
7504 | m_FirstArgAddr = argBase - argSize; |
7505 | #else // !_TARGET_X86_ (STACK_GROWS_UP_ON_ARGS_WALK) |
7506 | AlignAddressForType(pArgType, argBase); |
7507 | m_FirstArgAddr = argBase; |
7508 | #endif // !_TARGET_X86_ (STACK_GROWS_UP_ON_ARGS_WALK) |
7509 | } |
7510 | |
7511 | // The stackwalking code can't always successfully retrieve the generics type token. |
7512 | // For example, on 64-bit, the JIT only encodes the generics type token location if |
7513 | // a method has catch clause for a generic exception (e.g. "catch(MyException<string> e)"). |
7514 | if ((m_dwFrameParamsTokenIndex != (DWORD)ICorDebugInfo::MAX_ILNUM) && (m_frameParamsToken == NULL)) |
7515 | { |
7516 | // All variables are unavailable in the prolog and the epilog. |
7517 | // This includes the generics type token. Failing to get the token just means that |
7518 | // we won't have full generics information. This should not be a disastrous failure. |
7519 | // |
7520 | // Currently, on X64, the JIT is reporting that the variables are live even in the epilog. |
7521 | // That's why we need this check here. I need to follow up on this. |
7522 | if ((m_mapping != MAPPING_PROLOG) && (m_mapping != MAPPING_EPILOG)) |
7523 | { |
7524 | // Find the generics type token using the variable lifetime information. |
7525 | const ICorDebugInfo::NativeVarInfo * pNativeVarInfo = NULL; |
7526 | CordbNativeFrame * pNativeFrame = this->m_nativeFrame; |
7527 | |
7528 | pNativeFrame->m_nativeCode->LoadNativeInfo(); |
7529 | HRESULT hrTmp = pNativeFrame->m_nativeCode->ILVariableToNative(m_dwFrameParamsTokenIndex, |
7530 | pNativeFrame->GetInspectionIP(), |
7531 | &pNativeVarInfo); |
7532 | |
7533 | // It's not a disaster if we can't find the generics token, so don't throw an exception here. |
7534 | // In fact, it's fairly common in retail code. Even if we can't find the generics token, |
7535 | // we may still be able to look up the generics type information later by using the MethodDesc, |
7536 | // the "this" object, etc. If not, we'll at least get the representative type information |
7537 | // (e.g. Foo<T> instead of Foo<string>). |
7538 | if (SUCCEEDED(hrTmp)) |
7539 | { |
7540 | _ASSERTE(pNativeVarInfo != NULL); |
7541 | |
7542 | // The generics type token should be stored either in a register or on the stack. |
7543 | SIZE_T uRawToken = pNativeFrame->GetRegisterOrStackValue(pNativeVarInfo); |
7544 | |
7545 | // Ask DAC to resolve the token for us. We really don't want to deal with all the logic here. |
7546 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
7547 | // On a minidump, we'll throw if we're missing the memory. |
7548 | ALLOW_DATATARGET_MISSING_MEMORY( |
7549 | m_frameParamsToken = pDAC->ResolveExactGenericArgsToken(m_dwFrameParamsTokenIndex, uRawToken); |
7550 | ); |
7551 | } |
7552 | } |
7553 | } |
7554 | } |
7555 | EX_CATCH_HRESULT(hr); |
7556 | |
7557 | return hr; |
7558 | } |
7559 | |
7560 | /* |
7561 | A list of which resources owned by this object are accounted for. |
7562 | |
7563 | UNKNOWN: |
7564 | CordbNativeFrame* m_nativeFrame; |
7565 | CordbILCode * m_ilCode; |
7566 | CorDebugMappingResult m_mapping; |
7567 | CORDB_ADDRESS m_FirstArgAddr; |
7568 | ICorDebugInfo::NativeVarInfo * m_rgNVI; // Deleted in neuter |
7569 | CordbClass **m_genericArgs; |
7570 | */ |
7571 | |
7572 | CordbJITILFrame::~CordbJITILFrame() |
7573 | { |
7574 | _ASSERTE(IsNeutered()); |
7575 | } |
7576 | |
7577 | // Neutered by CordbNativeFrame |
7578 | void CordbJITILFrame::Neuter() |
7579 | { |
7580 | // Since neutering here calls Release directly, we don't want to double-release |
7581 | // if neuter is called multiple times. |
7582 | if (IsNeutered()) |
7583 | { |
7584 | return; |
7585 | } |
7586 | |
7587 | // Frames include pointers across to other types that specify the |
7588 | // representation instantiation - reduce the reference counts on these.... |
7589 | for (unsigned int i = 0; i < m_genericArgs.m_cInst; i++) |
7590 | { |
7591 | m_genericArgs.m_ppInst[i]->Release(); |
7592 | } |
7593 | |
7594 | if (m_rgNVI != NULL) |
7595 | { |
7596 | delete [] m_rgNVI; |
7597 | m_rgNVI = NULL; |
7598 | } |
7599 | |
7600 | if (m_rgbSigParserBuf != NULL) |
7601 | { |
7602 | delete [] m_rgbSigParserBuf; |
7603 | m_rgbSigParserBuf = NULL; |
7604 | } |
7605 | |
7606 | m_pReJitCode.Clear(); |
7607 | |
7608 | // If this class ever inherits from the CordbFrame we'll need a call |
7609 | // to CordbFrame::Neuter() here instead of to CordbBase::Neuter(); |
7610 | CordbBase::Neuter(); |
7611 | } |
7612 | |
7613 | //--------------------------------------------------------------------------------------- |
7614 | // |
7615 | // Load the generic type and method arguments and store them into the frame if possible. |
7616 | // |
7617 | // Return Value: |
7618 | // HRESULT for the operation |
7619 | // |
7620 | |
7621 | void CordbJITILFrame::LoadGenericArgs() |
7622 | { |
7623 | THROW_IF_NEUTERED(this); |
7624 | INTERNAL_SYNC_API_ENTRY(GetProcess()); // |
7625 | |
7626 | // The case where there are no type parameters, or the case where we've |
7627 | // already feched the realInst, is easy. |
7628 | if (m_genericArgsLoaded) |
7629 | { |
7630 | return; |
7631 | } |
7632 | |
7633 | _ASSERTE(m_nativeFrame->m_nativeCode != NULL); |
7634 | |
7635 | if (!m_nativeFrame->m_nativeCode->IsInstantiatedGeneric()) |
7636 | { |
7637 | m_genericArgs = Instantiation(0, NULL,0); |
7638 | m_genericArgsLoaded = true; |
7639 | return; |
7640 | } |
7641 | |
7642 | // Find the exact generic arguments for a frame that is executing |
7643 | // a generic method. The left-side will fetch these from arguments |
7644 | // given on the stack and/or from the IP. |
7645 | |
7646 | |
7647 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
7648 | |
7649 | UINT32 cGenericClassTypeParams = 0; |
7650 | DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> rgGenericTypeParams; |
7651 | |
7652 | pDAC->GetMethodDescParams(GetCurrentAppDomain()->GetADToken(), |
7653 | m_nativeFrame->GetNativeCode()->GetVMNativeCodeMethodDescToken(), |
7654 | m_frameParamsToken, |
7655 | &cGenericClassTypeParams, |
7656 | &rgGenericTypeParams); |
7657 | |
7658 | UINT32 cTotalGenericTypeParams = rgGenericTypeParams.Count(); |
7659 | |
7660 | // @dbgtodo reliability - This holder doesn't actually work in this case because it just deletes |
7661 | // each element on error. The RS classes are all expected to be neutered before the destructor is called. |
7662 | NewArrayHolder<CordbType *> ppGenericArgs(new CordbType *[cTotalGenericTypeParams]); |
7663 | |
7664 | for (UINT32 i = 0; i < cTotalGenericTypeParams;i++) |
7665 | { |
7666 | // creates a CordbType object for the generic argument |
7667 | HRESULT hr = CordbType::TypeDataToType(GetCurrentAppDomain(), |
7668 | &(rgGenericTypeParams[i]), |
7669 | &ppGenericArgs[i]); |
7670 | IfFailThrow(hr); |
7671 | |
7672 | // We add a ref as the instantiation will be stored away in the |
7673 | // ref-counted data structure associated with the JITILFrame |
7674 | ppGenericArgs[i]->AddRef(); |
7675 | } |
7676 | |
7677 | // initialize the generics information |
7678 | m_genericArgs = Instantiation(cTotalGenericTypeParams, ppGenericArgs, cGenericClassTypeParams); |
7679 | m_genericArgsLoaded = true; |
7680 | |
7681 | ppGenericArgs.SuppressRelease(); |
7682 | } |
7683 | |
7684 | |
7685 | // |
7686 | // CordbJITILFrame::QueryInterface |
7687 | // |
7688 | // Description |
7689 | // Interface query for this COM object |
7690 | // |
7691 | // NOTE: the COM object associated with this CordbJITILFrame may consist of two |
7692 | // C++ objects (a CordbJITILFrame and its associated CordbNativeFrame) |
7693 | // |
7694 | // Parameters |
7695 | // id the GUID associated with the requested interface |
7696 | // pInterface [out] the interface pointer |
7697 | // |
7698 | // Returns |
7699 | // HRESULT |
7700 | // S_OK If this CordbJITILFrame supports the interface |
7701 | // E_NOINTERFACE If this object does not support the interface |
7702 | // |
7703 | // Exceptions |
7704 | // None |
7705 | // |
7706 | HRESULT CordbJITILFrame::QueryInterface(REFIID id, void **pInterface) |
7707 | { |
7708 | if (NULL != m_nativeFrame) |
7709 | { |
7710 | // If the native frame does not support the requested interface, then |
7711 | // the native fram is responsible for delegating the query back to this |
7712 | // object through QueryInterfaceInternal(...) |
7713 | return m_nativeFrame->QueryInterface(id, pInterface); |
7714 | } |
7715 | |
7716 | // no native frame. Check for interfaces common to CordbNativeFrame and |
7717 | // CordbJITILFrame |
7718 | if (id == IID_IUnknown) |
7719 | { |
7720 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugILFrame*>(this)); |
7721 | } |
7722 | else if (id == IID_ICorDebugFrame) |
7723 | { |
7724 | *pInterface = static_cast<ICorDebugFrame*>(this); |
7725 | } |
7726 | else |
7727 | { |
7728 | // didn't find an interface yet. Since there's no native frame |
7729 | // associated with this IL frame, go ahead and check for the IL frame |
7730 | return this->QueryInterfaceInternal(id, pInterface); |
7731 | } |
7732 | |
7733 | ExternalAddRef(); |
7734 | return S_OK; |
7735 | } |
7736 | |
7737 | // |
7738 | // CordbJITILFrame::QueryInterfaceInternal |
7739 | // |
7740 | // Description |
7741 | // Interface query for interfaces implemented ONLY by CordbJITILFrame (as |
7742 | // opposed to interfaces implemented by both CordbNativeFrame and |
7743 | // CordbJITILFrame) |
7744 | // |
7745 | // Parameters |
7746 | // id the GUID associated with the requested interface |
7747 | // pInterface [out] the interface pointer |
7748 | // NOTE: id must not be IUnknown or ICorDebugFrame |
7749 | // NOTE: if this object is in "forward compatibility mode", passing in |
7750 | // IID_ICorDebugILFrame2 for the id will result in a failure (returns |
7751 | // E_NOINTERFACE) |
7752 | // |
7753 | // Returns |
7754 | // HRESULT |
7755 | // S_OK If this CordbJITILFrame supports the interface |
7756 | // E_NOINTERFACE If this object does not support the interface |
7757 | // |
7758 | // Exceptions |
7759 | // None |
7760 | // |
7761 | HRESULT |
7762 | CordbJITILFrame::QueryInterfaceInternal(REFIID id, void** pInterface) |
7763 | { |
7764 | _ASSERTE(IID_ICorDebugFrame != id); |
7765 | _ASSERTE(IID_IUnknown != id); |
7766 | |
7767 | // don't query for IUnknown or ICorDebugFrame! Someone else should have |
7768 | // already taken care of that. |
7769 | if (id == IID_ICorDebugILFrame) |
7770 | { |
7771 | *pInterface = static_cast<ICorDebugILFrame*>(this); |
7772 | } |
7773 | else if (id == IID_ICorDebugILFrame2) |
7774 | { |
7775 | *pInterface = static_cast<ICorDebugILFrame2*>(this); |
7776 | } |
7777 | else if (id == IID_ICorDebugILFrame3) |
7778 | { |
7779 | *pInterface = static_cast<ICorDebugILFrame3*>(this); |
7780 | } |
7781 | else if (id == IID_ICorDebugILFrame4) |
7782 | { |
7783 | *pInterface = static_cast<ICorDebugILFrame4*>(this); |
7784 | } |
7785 | else |
7786 | { |
7787 | *pInterface = NULL; |
7788 | return E_NOINTERFACE; |
7789 | } |
7790 | |
7791 | ExternalAddRef(); |
7792 | return S_OK; |
7793 | } |
7794 | |
7795 | //--------------------------------------------------------------------------------------- |
7796 | // |
7797 | // Get an enumerator for the generic type and method arguments on this frame. |
7798 | // |
7799 | // Arguments: |
7800 | // ppTypeParameterEnum - out parameter; return the enumerator |
7801 | // |
7802 | // Return Value: |
7803 | // HRESULT for the operation |
7804 | // |
7805 | HRESULT CordbJITILFrame::EnumerateTypeParameters(ICorDebugTypeEnum **ppTypeParameterEnum) |
7806 | { |
7807 | PUBLIC_API_ENTRY(this); |
7808 | FAIL_IF_NEUTERED(this); |
7809 | VALIDATE_POINTER_TO_OBJECT(ppTypeParameterEnum, ICorDebugTypeEnum **); |
7810 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
7811 | |
7812 | (*ppTypeParameterEnum) = NULL; |
7813 | |
7814 | HRESULT hr = S_OK; |
7815 | EX_TRY |
7816 | { |
7817 | |
7818 | // load the generic arguments, which may be cached |
7819 | LoadGenericArgs(); |
7820 | |
7821 | // create the enumerator |
7822 | RSInitHolder<CordbTypeEnum> pEnum( |
7823 | CordbTypeEnum::Build(GetCurrentAppDomain(), m_nativeFrame->m_pThread->GetRefreshStackNeuterList(), m_genericArgs.m_cInst, m_genericArgs.m_ppInst)); |
7824 | if ( pEnum == NULL ) |
7825 | { |
7826 | ThrowOutOfMemory(); |
7827 | } |
7828 | |
7829 | pEnum.TransferOwnershipExternal(ppTypeParameterEnum); |
7830 | } |
7831 | EX_CATCH_HRESULT(hr); |
7832 | return hr; |
7833 | } |
7834 | |
7835 | |
7836 | // ---------------------------------------------------------------------------- |
7837 | // CordbJITILFrame::GetChain |
7838 | // |
7839 | // Description: |
7840 | // Return the owning chain. Since chains have been deprecated in Arrowhead, |
7841 | // this function returns E_NOTIMPL unless there is a shim. |
7842 | // |
7843 | // Arguments: |
7844 | // * ppChain - out parameter; return the owning chain |
7845 | // |
7846 | // Return Value: |
7847 | // Return S_OK on success. |
7848 | // Return E_INVALIDARG if ppChain is NULL. |
7849 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbJITILFrame is neutered. |
7850 | // Return E_NOTIMPL if there is no shim. |
7851 | // |
7852 | |
7853 | HRESULT CordbJITILFrame::GetChain(ICorDebugChain **ppChain) |
7854 | { |
7855 | PUBLIC_REENTRANT_API_ENTRY(this); |
7856 | FAIL_IF_NEUTERED(this); |
7857 | VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **); |
7858 | |
7859 | HRESULT hr = S_OK; |
7860 | EX_TRY |
7861 | { |
7862 | hr = m_nativeFrame->GetChain(ppChain); |
7863 | // Since we are returning anyway, let's not throw even if the call fails. |
7864 | } |
7865 | EX_CATCH_HRESULT(hr); |
7866 | return hr; |
7867 | } |
7868 | |
7869 | // Return the IL code blob associated with this IL frame. |
7870 | // Each IL frame corresponds to exactly one IL code blob. |
7871 | HRESULT CordbJITILFrame::GetCode(ICorDebugCode **ppCode) |
7872 | { |
7873 | FAIL_IF_NEUTERED(this); |
7874 | VALIDATE_POINTER_TO_OBJECT(ppCode, ICorDebugCode **); |
7875 | |
7876 | *ppCode = static_cast<ICorDebugCode*> (m_ilCode); |
7877 | m_ilCode->ExternalAddRef(); |
7878 | |
7879 | return S_OK;; |
7880 | } |
7881 | |
7882 | // Return the function associated with this IL frame. |
7883 | // Each IL frame corresponds to exactly one function. |
7884 | HRESULT CordbJITILFrame::GetFunction(ICorDebugFunction **ppFunction) |
7885 | { |
7886 | HRESULT hr = S_OK; |
7887 | PUBLIC_API_BEGIN(this) |
7888 | { |
7889 | ValidateOrThrow(ppFunction); |
7890 | |
7891 | CordbFunction * pFunc = m_nativeFrame->GetFunction(); |
7892 | *ppFunction = static_cast<ICorDebugFunction *>(pFunc); |
7893 | pFunc->ExternalAddRef(); |
7894 | } |
7895 | PUBLIC_API_END(hr); |
7896 | return hr; |
7897 | } |
7898 | |
7899 | // Return the token of the function associated with this IL frame. |
7900 | // Each IL frame corresponds to exactly one function. |
7901 | HRESULT CordbJITILFrame::GetFunctionToken(mdMethodDef *pToken) |
7902 | { |
7903 | FAIL_IF_NEUTERED(this); |
7904 | VALIDATE_POINTER_TO_OBJECT(pToken, mdMethodDef *); |
7905 | |
7906 | *pToken = m_nativeFrame->m_nativeCode->GetMetadataToken(); |
7907 | |
7908 | return S_OK; |
7909 | } |
7910 | |
7911 | // ---------------------------------------------------------------------------- |
7912 | // CordJITILFrame::GetStackRange |
7913 | // |
7914 | // Description: |
7915 | // Get the stack range owned by the associated native frame. |
7916 | // IL frames and native frames are 1:1 for normal jitted managed methods. |
7917 | // Dynamic methods are an exception. |
7918 | // |
7919 | // Arguments: |
7920 | // * pStart - out parameter; return the leaf end of the frame |
7921 | // * pEnd - out parameter; return the root end of the frame |
7922 | // |
7923 | // Return Value: |
7924 | // Return S_OK on success. |
7925 | // |
7926 | // Notes: see code:#GetStackRange |
7927 | |
7928 | HRESULT CordbJITILFrame::GetStackRange(CORDB_ADDRESS *pStart, CORDB_ADDRESS *pEnd) |
7929 | { |
7930 | PUBLIC_REENTRANT_API_ENTRY(this); |
7931 | |
7932 | // The access of m_nativeFrame is not safe here. It's a weak reference. |
7933 | OK_IF_NEUTERED(this); |
7934 | |
7935 | HRESULT hr = S_OK; |
7936 | EX_TRY |
7937 | { |
7938 | hr = m_nativeFrame->GetStackRange(pStart, pEnd); |
7939 | // Since we are returning anyway, let's not throw even if the call fails. |
7940 | } |
7941 | EX_CATCH_HRESULT(hr); |
7942 | return hr; |
7943 | } |
7944 | |
7945 | // ---------------------------------------------------------------------------- |
7946 | // CordbJITILFrame::GetCaller |
7947 | // |
7948 | // Description: |
7949 | // Delegate to the associated native frame to return the caller, which is closer to the root. |
7950 | // This function has been deprecated in Arrowhead, and so it returns E_NOTIMPL unless there is a shim. |
7951 | // |
7952 | // Arguments: |
7953 | // * ppFrame - out parameter; return the caller frame |
7954 | // |
7955 | // Return Value: |
7956 | // Return S_OK on success. |
7957 | // Return E_INVALIDARG if ppFrame is NULL. |
7958 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbJITILFrame is neutered. |
7959 | // Return E_NOTIMPL if there is no shim. |
7960 | // |
7961 | |
7962 | HRESULT CordbJITILFrame::GetCaller(ICorDebugFrame **ppFrame) |
7963 | { |
7964 | PUBLIC_REENTRANT_API_ENTRY(this); |
7965 | FAIL_IF_NEUTERED(this); |
7966 | VALIDATE_POINTER_TO_OBJECT(ppFrame, ICorDebugFrame **); |
7967 | |
7968 | HRESULT hr = S_OK; |
7969 | EX_TRY |
7970 | { |
7971 | hr = m_nativeFrame->GetCaller(ppFrame); |
7972 | // Since we are returning anyway, let's not throw even if the call fails. |
7973 | } |
7974 | EX_CATCH_HRESULT(hr); |
7975 | return hr; |
7976 | } |
7977 | |
7978 | // ---------------------------------------------------------------------------- |
7979 | // CordbJITILFrame::GetCallee |
7980 | // |
7981 | // Description: |
7982 | // Delegate to the associated native frame to return the callee, which is closer to the leaf. |
7983 | // This function has been deprecated in Arrowhead, and so it returns E_NOTIMPL unless there is a shim. |
7984 | // |
7985 | // Arguments: |
7986 | // * ppFrame - out parameter; return the callee frame |
7987 | // |
7988 | // Return Value: |
7989 | // Return S_OK on success. |
7990 | // Return E_INVALIDARG if ppFrame is NULL. |
7991 | // Return CORDBG_E_OBJECT_NEUTERED if the CordbJITILFrame is neutered. |
7992 | // Return E_NOTIMPL if there is no shim. |
7993 | // |
7994 | |
7995 | HRESULT CordbJITILFrame::GetCallee(ICorDebugFrame **ppFrame) |
7996 | { |
7997 | PUBLIC_REENTRANT_API_ENTRY(this); |
7998 | FAIL_IF_NEUTERED(this); |
7999 | VALIDATE_POINTER_TO_OBJECT(ppFrame, ICorDebugFrame **); |
8000 | |
8001 | HRESULT hr = S_OK; |
8002 | EX_TRY |
8003 | { |
8004 | hr = m_nativeFrame->GetCallee(ppFrame); |
8005 | // Since we are returning anyway, let's not throw even if the call fails. |
8006 | } |
8007 | EX_CATCH_HRESULT(hr); |
8008 | return hr; |
8009 | } |
8010 | |
8011 | // Create a stepper on the frame. |
8012 | HRESULT CordbJITILFrame::CreateStepper(ICorDebugStepper **ppStepper) |
8013 | { |
8014 | PUBLIC_API_ENTRY(this); |
8015 | FAIL_IF_NEUTERED(this); |
8016 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8017 | |
8018 | // by default, a stepper operates on the IL level, using IL offsets |
8019 | return m_nativeFrame->CreateStepper(ppStepper); |
8020 | } |
8021 | |
8022 | // Return the IL offset and the mapping result. |
8023 | HRESULT CordbJITILFrame::GetIP(ULONG32 *pnOffset, |
8024 | CorDebugMappingResult *pMappingResult) |
8025 | { |
8026 | PUBLIC_REENTRANT_API_ENTRY(this); |
8027 | FAIL_IF_NEUTERED(this); |
8028 | VALIDATE_POINTER_TO_OBJECT(pnOffset, ULONG32 *); |
8029 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pMappingResult, CorDebugMappingResult *); |
8030 | |
8031 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8032 | |
8033 | *pnOffset = (ULONG32)m_ip; |
8034 | if (pMappingResult) |
8035 | *pMappingResult = m_mapping; |
8036 | |
8037 | return S_OK; |
8038 | } |
8039 | |
8040 | // Determine if we can set IP at this point. The specified offset is the IL offset. |
8041 | HRESULT CordbJITILFrame::CanSetIP(ULONG32 nOffset) |
8042 | { |
8043 | PUBLIC_API_ENTRY(this); |
8044 | FAIL_IF_NEUTERED(this); |
8045 | |
8046 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8047 | |
8048 | HRESULT hr = S_OK; |
8049 | EX_TRY |
8050 | { |
8051 | // Check to see that this is a leaf frame |
8052 | if (!m_nativeFrame->IsLeafFrame()) |
8053 | { |
8054 | ThrowHR(CORDBG_E_SET_IP_NOT_ALLOWED_ON_NONLEAF_FRAME); |
8055 | } |
8056 | |
8057 | // delegate to the associated native frame |
8058 | CordbNativeCode * pNativeCode = m_nativeFrame->m_nativeCode; |
8059 | hr = m_nativeFrame->m_pThread->SetIP(SetIP_fCanSetIPOnly, // specify that this is for checking only |
8060 | pNativeCode, |
8061 | nOffset, |
8062 | SetIP_fIL ); |
8063 | } |
8064 | EX_CATCH_HRESULT(hr); |
8065 | |
8066 | return hr; |
8067 | } |
8068 | |
8069 | // Try to set the IP to the specified offset. The specified offset is the IL offset. |
8070 | HRESULT CordbJITILFrame::SetIP(ULONG32 nOffset) |
8071 | { |
8072 | PUBLIC_API_ENTRY(this); |
8073 | FAIL_IF_NEUTERED(this); |
8074 | |
8075 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8076 | |
8077 | HRESULT hr = S_OK; |
8078 | EX_TRY |
8079 | { |
8080 | // Check to see that this is a leaf frame |
8081 | if (!m_nativeFrame->IsLeafFrame()) |
8082 | { |
8083 | ThrowHR(CORDBG_E_SET_IP_NOT_ALLOWED_ON_NONLEAF_FRAME); |
8084 | } |
8085 | |
8086 | // delegate to the native frame |
8087 | CordbNativeCode * pNativeCode = m_nativeFrame->m_nativeCode; |
8088 | hr = m_nativeFrame->m_pThread->SetIP(SetIP_fSetIP, // specify that this is a real SetIP operation |
8089 | pNativeCode, |
8090 | nOffset, |
8091 | SetIP_fIL ); |
8092 | } |
8093 | EX_CATCH_HRESULT(hr); |
8094 | |
8095 | return hr; |
8096 | } |
8097 | |
8098 | //--------------------------------------------------------------------------------------- |
8099 | // |
8100 | // This routine creates backing native info for a local variable, returning an ICorDebugInfo |
8101 | // object for the local variable when successful. |
8102 | // |
8103 | // Arguments: |
8104 | // dwIndex - Index of the local variable to create native info for. |
8105 | // ppNativeInfo - OUT: Space for storing the resulting pointer to native variable info. |
8106 | // |
8107 | // Return Value: |
8108 | // HRESULT for the operation |
8109 | // |
8110 | HRESULT CordbJITILFrame::FabricateNativeInfo(DWORD dwIndex, |
8111 | const ICorDebugInfo::NativeVarInfo ** ppNativeInfo) |
8112 | { |
8113 | HRESULT hr = S_OK; |
8114 | EX_TRY |
8115 | { |
8116 | THROW_IF_NEUTERED(this); |
8117 | INTERNAL_SYNC_API_ENTRY(this->GetProcess()); |
8118 | _ASSERTE(m_fVarArgFnx); |
8119 | |
8120 | // This array should have been populated in CordbJITILFrame::Init(). |
8121 | _ASSERTE(m_rgNVI != NULL); |
8122 | |
8123 | // check if we have already fabricated all the information |
8124 | if (m_rgNVI[dwIndex].loc.vlType != ICorDebugInfo::VLT_INVALID) |
8125 | { |
8126 | (*ppNativeInfo) = &m_rgNVI[dwIndex]; |
8127 | } |
8128 | else |
8129 | { |
8130 | // We'll initialize everything at once |
8131 | ULONG cbArchitectureMin; |
8132 | |
8133 | // m_FirstArgAddr will already be aligned on platforms that require alignment |
8134 | CORDB_ADDRESS rpCur = m_FirstArgAddr; |
8135 | |
8136 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_ARM) |
8137 | cbArchitectureMin = 4; |
8138 | #elif defined(DBG_TARGET_WIN64) |
8139 | cbArchitectureMin = 8; |
8140 | #else |
8141 | cbArchitectureMin = 8; //REVISIT_TODO not sure if this is correct |
8142 | PORTABILITY_ASSERT("What is the architecture-dependent minimum word size?" ); |
8143 | #endif // DBG_TARGET_X86 |
8144 | |
8145 | // make a copy of the cached SigParser |
8146 | SigParser sigParser = m_sigParserCached; |
8147 | |
8148 | IfFailThrow(sigParser.SkipMethodHeaderSignature(NULL)); |
8149 | |
8150 | ULONG32 cbType; |
8151 | |
8152 | CordbType * pArgType; |
8153 | |
8154 | // make sure all the generic type and method arguments are loaded |
8155 | LoadGenericArgs(); |
8156 | |
8157 | // get a CordbType object for the generic argument |
8158 | IfFailThrow(CordbType::SigToType(GetModule(), &sigParser, &(this->m_genericArgs), &pArgType)); |
8159 | |
8160 | IfFailThrow(pArgType->GetUnboxedObjectSize(&cbType)); |
8161 | |
8162 | #if defined(DBG_TARGET_X86) // STACK_GROWS_DOWN_ON_ARGS_WALK |
8163 | // The the rpCur pointer starts off in the right spot for the |
8164 | // first argument, but thereafter we have to decrement it |
8165 | // before getting the variable's location from it. So increment |
8166 | // it here to be consistent later. |
8167 | rpCur += max(cbType, cbArchitectureMin); |
8168 | #endif |
8169 | |
8170 | // Grab the IL code's function's method signature so we can see if it's static. |
8171 | BOOL fMethodIsStatic; |
8172 | |
8173 | m_ilCode->GetSig(NULL, NULL, &fMethodIsStatic); // throws |
8174 | |
8175 | ULONG i; |
8176 | |
8177 | if (fMethodIsStatic) |
8178 | { |
8179 | i = 0; |
8180 | } |
8181 | else |
8182 | { |
8183 | i = 1; |
8184 | } |
8185 | |
8186 | for ( ; i < m_allArgsCount; i++) |
8187 | { |
8188 | m_rgNVI[i].startOffset = 0; |
8189 | m_rgNVI[i].endOffset = 0xFFffFFff; |
8190 | m_rgNVI[i].varNumber = i; |
8191 | m_rgNVI[i].loc.vlType = ICorDebugInfo::VLT_FIXED_VA; |
8192 | |
8193 | LoadGenericArgs(); |
8194 | |
8195 | IfFailThrow(CordbType::SigToType(GetModule(), &sigParser, &(this->m_genericArgs), &pArgType)); |
8196 | |
8197 | IfFailThrow(pArgType->GetUnboxedObjectSize(&cbType)); |
8198 | |
8199 | #if defined(DBG_TARGET_X86) // STACK_GROWS_DOWN_ON_ARGS_WALK |
8200 | rpCur -= max(cbType, cbArchitectureMin); |
8201 | m_rgNVI[i].loc.vlFixedVarArg.vlfvOffset = |
8202 | (unsigned)(m_FirstArgAddr - rpCur); |
8203 | |
8204 | // Since the JIT adds in the size of this field, we do too to |
8205 | // be consistent. |
8206 | m_rgNVI[i].loc.vlFixedVarArg.vlfvOffset += sizeof(((CORINFO_VarArgInfo*)0)->argBytes); |
8207 | #else // STACK_GROWS_UP_ON_ARGS_WALK |
8208 | m_rgNVI[i].loc.vlFixedVarArg.vlfvOffset = |
8209 | (unsigned)(rpCur - m_FirstArgAddr); |
8210 | rpCur += max(cbType, cbArchitectureMin); |
8211 | AlignAddressForType(pArgType, rpCur); |
8212 | #endif |
8213 | |
8214 | IfFailThrow(sigParser.SkipExactlyOne()); |
8215 | } // for ( ; i M m_allArgsCount; i++) |
8216 | |
8217 | (*ppNativeInfo) = &m_rgNVI[dwIndex]; |
8218 | } // else (m_rgNVI[dwIndex].loc.vlType == ICorDebugInfo::VLT_INVALID) |
8219 | } |
8220 | EX_CATCH_HRESULT(hr); |
8221 | |
8222 | return hr; |
8223 | } |
8224 | |
8225 | HRESULT CordbJITILFrame::ILVariableToNative(DWORD dwVarNumber, |
8226 | const ICorDebugInfo::NativeVarInfo **ppNativeInfo) |
8227 | { |
8228 | FAIL_IF_NEUTERED(this); |
8229 | INTERNAL_SYNC_API_ENTRY(GetProcess()); // |
8230 | |
8231 | _ASSERTE(m_nativeFrame->m_nativeCode->IsNativeCodeValid()); |
8232 | // We keep the fixed argument native var infos in the |
8233 | // CordbFunction, which only is an issue for var args info: |
8234 | if (!m_fVarArgFnx || //not a var args function |
8235 | (dwVarNumber < m_nativeFrame->m_nativeCode->GetFixedArgCount()) || // var args,fixed arg |
8236 | // note that this include the implicit 'this' for nonstatic fnxs |
8237 | (dwVarNumber >= m_allArgsCount) ||// var args, local variable |
8238 | (m_sigParserCached.IsNull())) //we don't have any VA info |
8239 | { |
8240 | // If we're in a var args fnx, but we're actually looking |
8241 | // for a local variable, then we want to use the variable |
8242 | // index as the function sees it - fixed (but not var) |
8243 | // args are added to local var number to get native info |
8244 | // We are really trying to find a variable by it's number, |
8245 | // but "special" variables have a negative number which we |
8246 | // don't use. We "number" them conceptually between the |
8247 | // arguments and locals: |
8248 | // |
8249 | // arguments special locals |
8250 | // ----------------------------------------- |
8251 | // Actual numbers: 1 2 3 . . . 4 5 6 7 |
8252 | // Logical numbers: 0 1 2 3 4 5 6 7 8 |
8253 | // |
8254 | // We have two different counts for the number of arguments: the fixedArgCount |
8255 | // gives the actual number of arguments and the allArgsCount is the number of |
8256 | // of fixed arguments plus the number of var args. |
8257 | // |
8258 | // Thus, to get the correct actual number for locals we have to compute it as |
8259 | // logicalNumber - allArgsCount + fixedArgCount |
8260 | |
8261 | if (m_fVarArgFnx && (dwVarNumber >= m_allArgsCount) && !m_sigParserCached.IsNull()) |
8262 | { |
8263 | dwVarNumber -= m_allArgsCount; |
8264 | dwVarNumber += m_nativeFrame->m_nativeCode->GetFixedArgCount(); |
8265 | } |
8266 | |
8267 | return m_nativeFrame->m_nativeCode->ILVariableToNative(dwVarNumber, |
8268 | m_nativeFrame->GetInspectionIP(), |
8269 | ppNativeInfo); |
8270 | } |
8271 | |
8272 | return FabricateNativeInfo(dwVarNumber,ppNativeInfo); |
8273 | } |
8274 | |
8275 | //--------------------------------------------------------------------------------------- |
8276 | // |
8277 | // This routine get the type of a particular argument. |
8278 | // |
8279 | // Arguments: |
8280 | // dwIndex - Index of the argument. |
8281 | // ppResultType - OUT: Space for storing the type of the argument. |
8282 | // |
8283 | // Return Value: |
8284 | // HRESULT for the operation |
8285 | // |
8286 | HRESULT CordbJITILFrame::GetArgumentType(DWORD dwIndex, |
8287 | CordbType ** ppResultType) |
8288 | { |
8289 | HRESULT hr = S_OK; |
8290 | THROW_IF_NEUTERED(this); |
8291 | INTERNAL_SYNC_API_ENTRY(GetProcess()); |
8292 | |
8293 | LoadGenericArgs(); |
8294 | |
8295 | if (m_fVarArgFnx && !m_sigParserCached.IsNull()) |
8296 | { |
8297 | SigParser sigParser = m_sigParserCached; |
8298 | |
8299 | IfFailThrow(sigParser.SkipMethodHeaderSignature(NULL)); |
8300 | |
8301 | // Grab the IL code's function's method signature so we can see if it's static. |
8302 | BOOL fMethodIsStatic; |
8303 | |
8304 | m_ilCode->GetSig(NULL, NULL, &fMethodIsStatic); // throws |
8305 | if (!fMethodIsStatic) |
8306 | { |
8307 | if (dwIndex == 0) |
8308 | { |
8309 | // Return the signature for the 'this' pointer for the |
8310 | // class this method is in. |
8311 | |
8312 | IfFailThrow(m_ilCode->GetClass()->GetThisType(&(this->m_genericArgs), ppResultType)); |
8313 | return hr; |
8314 | } |
8315 | else |
8316 | { |
8317 | dwIndex--; |
8318 | } |
8319 | } |
8320 | for (ULONG i = 0; i < dwIndex; i++) |
8321 | { |
8322 | IfFailThrow(sigParser.SkipExactlyOne()); |
8323 | } |
8324 | |
8325 | IfFailThrow(sigParser.SkipFunkyAndCustomModifiers()); |
8326 | |
8327 | IfFailThrow(sigParser.SkipAnyVASentinel()); |
8328 | |
8329 | IfFailThrow(CordbType::SigToType(GetModule(), &sigParser, &(this->m_genericArgs), ppResultType)); |
8330 | } |
8331 | else // (!m_fVarArgFnx || m_sigParserCached.IsNull()) |
8332 | { |
8333 | m_nativeFrame->m_nativeCode->GetArgumentType(dwIndex, &(this->m_genericArgs), ppResultType); |
8334 | } |
8335 | |
8336 | return hr; |
8337 | } |
8338 | |
8339 | // |
8340 | // GetNativeVariable uses the JIT variable information to delegate to |
8341 | // the native frame when the value is really created. |
8342 | // |
8343 | HRESULT CordbJITILFrame::GetNativeVariable(CordbType *type, |
8344 | const ICorDebugInfo::NativeVarInfo *pNativeVarInfo, |
8345 | ICorDebugValue **ppValue) |
8346 | { |
8347 | INTERNAL_API_ENTRY(this); |
8348 | FAIL_IF_NEUTERED(this); |
8349 | |
8350 | HRESULT hr = S_OK; |
8351 | |
8352 | #ifdef WIN64EXCEPTIONS |
8353 | if (m_nativeFrame->IsFunclet()) |
8354 | { |
8355 | if ( (pNativeVarInfo->loc.vlType != ICorDebugInfo::VLT_STK) && |
8356 | (pNativeVarInfo->loc.vlType != ICorDebugInfo::VLT_STK2) && |
8357 | (pNativeVarInfo->loc.vlType != ICorDebugInfo::VLT_STK_BYREF) ) |
8358 | { |
8359 | _ASSERTE(!"CordbJITILFrame::GetNativeVariable()" |
8360 | " - Variables used in funclets should always be homed on the stack.\n" ); |
8361 | return E_FAIL; |
8362 | } |
8363 | } |
8364 | #endif // WIN64EXCEPTIONS |
8365 | |
8366 | switch (pNativeVarInfo->loc.vlType) |
8367 | { |
8368 | case ICorDebugInfo::VLT_REG: |
8369 | hr = m_nativeFrame->GetLocalRegisterValue( |
8370 | ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlReg.vlrReg), |
8371 | type, ppValue); |
8372 | break; |
8373 | |
8374 | case ICorDebugInfo::VLT_REG_BYREF: |
8375 | { |
8376 | CORDB_ADDRESS pRemoteByRefAddr = PTR_TO_CORDB_ADDRESS( |
8377 | *( m_nativeFrame->GetAddressOfRegister(ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlReg.vlrReg))) ); |
8378 | |
8379 | hr = m_nativeFrame->GetLocalMemoryValue(pRemoteByRefAddr, |
8380 | type, |
8381 | ppValue); |
8382 | } |
8383 | break; |
8384 | |
8385 | #if defined(DBG_TARGET_WIN64) || defined(DBG_TARGET_ARM) |
8386 | case ICorDebugInfo::VLT_REG_FP: |
8387 | #if defined(DBG_TARGET_ARM) // @ARMTODO |
8388 | hr = E_NOTIMPL; |
8389 | #else // DBG_TARGET_ARM @ARMTODO |
8390 | hr = m_nativeFrame->GetLocalFloatingPointValue(pNativeVarInfo->loc.vlReg.vlrReg + REGISTER_AMD64_XMM0, |
8391 | type, ppValue); |
8392 | |
8393 | #endif // DBG_TARGET_ARM @ARMTODO |
8394 | break; |
8395 | #endif // DBG_TARGET_WIN64 || DBG_TARGET_ARM |
8396 | |
8397 | case ICorDebugInfo::VLT_STK_BYREF: |
8398 | { |
8399 | CORDB_ADDRESS pRemoteByRefAddr = m_nativeFrame->GetLSStackAddress( |
8400 | pNativeVarInfo->loc.vlStk.vlsBaseReg, pNativeVarInfo->loc.vlStk.vlsOffset) ; |
8401 | |
8402 | hr = m_nativeFrame->GetLocalByRefMemoryValue(pRemoteByRefAddr, |
8403 | type, |
8404 | ppValue); |
8405 | } |
8406 | break; |
8407 | |
8408 | case ICorDebugInfo::VLT_STK: |
8409 | { |
8410 | CORDB_ADDRESS pRemoteValue = m_nativeFrame->GetLSStackAddress( |
8411 | pNativeVarInfo->loc.vlStk.vlsBaseReg, pNativeVarInfo->loc.vlStk.vlsOffset) ; |
8412 | |
8413 | hr = m_nativeFrame->GetLocalMemoryValue(pRemoteValue, |
8414 | type, |
8415 | ppValue); |
8416 | } |
8417 | break; |
8418 | |
8419 | case ICorDebugInfo::VLT_REG_REG: |
8420 | hr = m_nativeFrame->GetLocalDoubleRegisterValue( |
8421 | ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlRegReg.vlrrReg2), |
8422 | ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlRegReg.vlrrReg1), |
8423 | type, ppValue); |
8424 | break; |
8425 | |
8426 | case ICorDebugInfo::VLT_REG_STK: |
8427 | { |
8428 | CORDB_ADDRESS pRemoteValue = m_nativeFrame->GetLSStackAddress( |
8429 | pNativeVarInfo->loc.vlRegStk.vlrsStk.vlrssBaseReg, pNativeVarInfo->loc.vlRegStk.vlrsStk.vlrssOffset); |
8430 | |
8431 | hr = m_nativeFrame->GetLocalMemoryRegisterValue( |
8432 | pRemoteValue, |
8433 | ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlRegStk.vlrsReg), |
8434 | type, ppValue); |
8435 | } |
8436 | break; |
8437 | |
8438 | case ICorDebugInfo::VLT_STK_REG: |
8439 | { |
8440 | CORDB_ADDRESS pRemoteValue = m_nativeFrame->GetLSStackAddress( |
8441 | pNativeVarInfo->loc.vlStkReg.vlsrStk.vlsrsBaseReg, pNativeVarInfo->loc.vlStkReg.vlsrStk.vlsrsOffset); |
8442 | |
8443 | hr = m_nativeFrame->GetLocalRegisterMemoryValue( |
8444 | ConvertRegNumToCorDebugRegister(pNativeVarInfo->loc.vlStkReg.vlsrReg), |
8445 | pRemoteValue, type, ppValue); |
8446 | } |
8447 | break; |
8448 | |
8449 | case ICorDebugInfo::VLT_STK2: |
8450 | { |
8451 | CORDB_ADDRESS pRemoteValue = m_nativeFrame->GetLSStackAddress( |
8452 | pNativeVarInfo->loc.vlStk2.vls2BaseReg, pNativeVarInfo->loc.vlStk2.vls2Offset); |
8453 | |
8454 | hr = m_nativeFrame->GetLocalMemoryValue(pRemoteValue, |
8455 | type, |
8456 | ppValue); |
8457 | } |
8458 | break; |
8459 | |
8460 | case ICorDebugInfo::VLT_FPSTK: |
8461 | #if defined(DBG_TARGET_ARM) // @ARMTODO |
8462 | hr = E_NOTIMPL; |
8463 | #else |
8464 | /* |
8465 | @TODO [Microsoft] We have to make this work!!!!!!!!!!!!! |
8466 | hr = m_nativeFrame->GetLocalFloatingPointValue( |
8467 | pNativeVarInfo->loc.vlFPstk.vlfReg + REGISTER_X86_FPSTACK_0, |
8468 | type, ppValue); |
8469 | */ |
8470 | hr = CORDBG_E_IL_VAR_NOT_AVAILABLE; |
8471 | #endif |
8472 | break; |
8473 | |
8474 | case ICorDebugInfo::VLT_FIXED_VA: |
8475 | if (m_sigParserCached.IsNull()) //no var args info |
8476 | return CORDBG_E_IL_VAR_NOT_AVAILABLE; |
8477 | |
8478 | CORDB_ADDRESS pRemoteValue; |
8479 | |
8480 | |
8481 | #if defined(DBG_TARGET_X86) // STACK_GROWS_DOWN_ON_ARGS_WALK |
8482 | pRemoteValue = m_FirstArgAddr - pNativeVarInfo->loc.vlFixedVarArg.vlfvOffset; |
8483 | // Remember to subtract out this amount |
8484 | pRemoteValue += sizeof(((CORINFO_VarArgInfo*)0)->argBytes); |
8485 | #else // STACK_GROWS_UP_ON_ARGS_WALK |
8486 | pRemoteValue = m_FirstArgAddr + pNativeVarInfo->loc.vlFixedVarArg.vlfvOffset; |
8487 | #endif |
8488 | |
8489 | hr = m_nativeFrame->GetLocalMemoryValue(pRemoteValue, |
8490 | type, |
8491 | ppValue); |
8492 | |
8493 | break; |
8494 | |
8495 | |
8496 | default: |
8497 | _ASSERTE(!"Invalid locVarType" ); |
8498 | hr = E_FAIL; |
8499 | break; |
8500 | } |
8501 | |
8502 | return hr; |
8503 | } |
8504 | |
8505 | HRESULT CordbJITILFrame::EnumerateLocalVariables(ICorDebugValueEnum **ppValueEnum) |
8506 | { |
8507 | PUBLIC_REENTRANT_API_ENTRY(this); |
8508 | FAIL_IF_NEUTERED(this); |
8509 | VALIDATE_POINTER_TO_OBJECT(ppValueEnum, ICorDebugValueEnum **); |
8510 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8511 | |
8512 | return EnumerateLocalVariablesEx(ILCODE_ORIGINAL_IL, ppValueEnum); |
8513 | } |
8514 | |
8515 | HRESULT CordbJITILFrame::GetLocalVariable(DWORD dwIndex, |
8516 | ICorDebugValue **ppValue) |
8517 | { |
8518 | PUBLIC_REENTRANT_API_ENTRY(this); |
8519 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
8520 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8521 | |
8522 | return GetLocalVariableEx(ILCODE_ORIGINAL_IL, dwIndex, ppValue); |
8523 | } |
8524 | |
8525 | |
8526 | HRESULT CordbJITILFrame::EnumerateArguments(ICorDebugValueEnum **ppValueEnum) |
8527 | { |
8528 | PUBLIC_API_ENTRY(this); |
8529 | FAIL_IF_NEUTERED(this); |
8530 | VALIDATE_POINTER_TO_OBJECT(ppValueEnum, ICorDebugValueEnum **); |
8531 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8532 | |
8533 | HRESULT hr = S_OK; |
8534 | |
8535 | EX_TRY |
8536 | { |
8537 | RSInitHolder<CordbValueEnum> cdVE(new CordbValueEnum(m_nativeFrame, CordbValueEnum::ARGS)); |
8538 | |
8539 | // Initialize the new enum |
8540 | hr = cdVE->Init(); |
8541 | IfFailThrow(hr); |
8542 | |
8543 | cdVE.TransferOwnershipExternal(ppValueEnum); |
8544 | |
8545 | } |
8546 | EX_CATCH_HRESULT(hr); |
8547 | return hr; |
8548 | } |
8549 | |
8550 | //--------------------------------------------------------------------------------------- |
8551 | // |
8552 | // This routine gets the value of a particular argument |
8553 | // |
8554 | // Arguments: |
8555 | // dwIndex - Index of the argument. |
8556 | // ppValue - OUT: Space for storing the value of the argument |
8557 | // |
8558 | // Return Value: |
8559 | // HRESULT for the operation |
8560 | // |
8561 | HRESULT CordbJITILFrame::GetArgument(DWORD dwIndex, ICorDebugValue ** ppValue) |
8562 | { |
8563 | PUBLIC_REENTRANT_API_ENTRY(this); |
8564 | FAIL_IF_NEUTERED(this); |
8565 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
8566 | |
8567 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8568 | |
8569 | const ICorDebugInfo::NativeVarInfo * pNativeInfo; |
8570 | |
8571 | // |
8572 | // First, make sure that we've got the jitted variable location data |
8573 | // loaded from the left side. |
8574 | // |
8575 | HRESULT hr = S_OK; |
8576 | EX_TRY |
8577 | { |
8578 | m_nativeFrame->m_nativeCode->LoadNativeInfo(); //throws |
8579 | |
8580 | hr = ILVariableToNative(dwIndex, &pNativeInfo); |
8581 | IfFailThrow(hr); |
8582 | |
8583 | // Get the type of this argument from the function |
8584 | CordbType * pType; |
8585 | |
8586 | hr = GetArgumentType(dwIndex, &pType); |
8587 | IfFailThrow(hr); |
8588 | |
8589 | hr = GetNativeVariable(pType, pNativeInfo, ppValue); |
8590 | IfFailThrow(hr); |
8591 | } |
8592 | EX_CATCH_HRESULT(hr); |
8593 | |
8594 | return hr; |
8595 | } |
8596 | |
8597 | |
8598 | HRESULT CordbJITILFrame::GetStackDepth(ULONG32 *pDepth) |
8599 | { |
8600 | PUBLIC_API_ENTRY(this); |
8601 | FAIL_IF_NEUTERED(this); |
8602 | VALIDATE_POINTER_TO_OBJECT(pDepth, ULONG32 *); |
8603 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8604 | |
8605 | |
8606 | /* !!! */ |
8607 | |
8608 | return E_NOTIMPL; |
8609 | } |
8610 | |
8611 | HRESULT CordbJITILFrame::GetStackValue(DWORD dwIndex, ICorDebugValue **ppValue) |
8612 | { |
8613 | PUBLIC_API_ENTRY(this); |
8614 | FAIL_IF_NEUTERED(this); |
8615 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
8616 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8617 | |
8618 | /* !!! */ |
8619 | |
8620 | return E_NOTIMPL; |
8621 | } |
8622 | |
8623 | //--------------------------------------------------------------------------------------- |
8624 | // |
8625 | // Remaps the active frame to the latest EnC version of the function, preserving the |
8626 | // execution state of the method such as the values of locals. |
8627 | // Can only be called when the leaf frame is at a remap opportunity. |
8628 | // |
8629 | // Arguments: |
8630 | // nOffset - the IL offset in the new version of the function to remap to |
8631 | // |
8632 | |
8633 | HRESULT CordbJITILFrame::RemapFunction(ULONG32 nOffset) |
8634 | { |
8635 | HRESULT hr = S_OK; |
8636 | PUBLIC_API_BEGIN(this) |
8637 | { |
8638 | #if !defined(EnC_SUPPORTED) |
8639 | ThrowHR(E_NOTIMPL); |
8640 | |
8641 | #else // EnC_SUPPORTED |
8642 | // Can only be called on leaf frame. |
8643 | if (!m_nativeFrame->IsLeafFrame()) |
8644 | { |
8645 | ThrowHR(E_INVALIDARG); |
8646 | } |
8647 | |
8648 | // mark frames as not fresh, because this frame has been updated. |
8649 | m_nativeFrame->m_pThread->CleanupStack(); |
8650 | |
8651 | // Since we may have overwritten anything (objects, code, etc), we should mark |
8652 | // everything as needing to be re-cached. |
8653 | m_nativeFrame->m_pThread->GetProcess()->m_continueCounter++; |
8654 | |
8655 | // Tell the left-side to do the remap |
8656 | hr = m_nativeFrame->m_pThread->SetRemapIP(nOffset); |
8657 | |
8658 | #endif // EnC_SUPPORTED |
8659 | } |
8660 | PUBLIC_API_END(hr); |
8661 | |
8662 | return hr; |
8663 | } |
8664 | HRESULT CordbJITILFrame::GetReturnValueForILOffset(ULONG32 ILoffset, ICorDebugValue** ppReturnValue) |
8665 | { |
8666 | HRESULT hr = S_OK; |
8667 | PUBLIC_API_ENTRY(this); |
8668 | FAIL_IF_NEUTERED(this); |
8669 | |
8670 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8671 | EX_TRY |
8672 | { |
8673 | hr = GetReturnValueForILOffsetImpl(ILoffset, ppReturnValue); |
8674 | } |
8675 | EX_CATCH_HRESULT(hr); |
8676 | |
8677 | return hr; |
8678 | } |
8679 | |
8680 | |
8681 | HRESULT CordbJITILFrame::BuildInstantiationForCallsite(CordbModule * pModule, NewArrayHolder<CordbType*> &types, Instantiation &inst, Instantiation *currentInstantiation, mdToken targetClass, SigParser genericSig) |
8682 | { |
8683 | // This function builds an Instantiation object (and backing "types" array) for a given |
8684 | // class and method signature. |
8685 | HRESULT hr = S_OK; |
8686 | RSExtSmartPtr<IMetaDataImport2> pImport2; |
8687 | IfFailRet(pModule->GetMetaDataImporter()->QueryInterface(IID_IMetaDataImport2, (void**)&pImport2)); |
8688 | |
8689 | // If the targetClass is a TypeSpec that means its first element is GENERICINST. |
8690 | // We only need to build types for the Instantiation if targetClass is a TypeSpec. |
8691 | ULONG classGenerics = 0; |
8692 | SigParser typeSig; |
8693 | if (TypeFromToken(targetClass) == mdtTypeSpec) |
8694 | { |
8695 | // Our goal with this is to full "classGenerics" with the number of |
8696 | // generics, and move "typeSig" to the start of the first generic type. |
8697 | PCCOR_SIGNATURE sig = 0; |
8698 | ULONG sigCount = 0; |
8699 | |
8700 | IfFailRet(pImport2->GetTypeSpecFromToken(targetClass, &sig, &sigCount)); |
8701 | |
8702 | typeSig = SigParser(sig, sigCount); |
8703 | CorElementType elemType; |
8704 | IfFailRet(typeSig.GetElemType(&elemType)); |
8705 | |
8706 | if (elemType != ELEMENT_TYPE_GENERICINST) |
8707 | return META_E_BAD_SIGNATURE; |
8708 | |
8709 | IfFailRet(typeSig.GetElemType(&elemType)); |
8710 | if (elemType != ELEMENT_TYPE_VALUETYPE && elemType != ELEMENT_TYPE_CLASS) |
8711 | return META_E_BAD_SIGNATURE; |
8712 | |
8713 | IfFailRet(typeSig.GetToken(NULL)); |
8714 | IfFailRet(typeSig.GetData(&classGenerics)); |
8715 | } |
8716 | |
8717 | // Similarly for method generics. Simply fill "methodGenerics" with the number |
8718 | // of generics, and move "genericSig" to the start of the first generic param. |
8719 | ULONG methodGenerics = 0; |
8720 | if (!genericSig.IsNull()) |
8721 | { |
8722 | ULONG callingConv = 0; |
8723 | IfFailRet(genericSig.GetCallingConvInfo(&callingConv)); |
8724 | if (callingConv == IMAGE_CEE_CS_CALLCONV_GENERICINST) |
8725 | IfFailRet(genericSig.GetData(&methodGenerics)); |
8726 | } |
8727 | |
8728 | |
8729 | // Now build "types" and "inst". |
8730 | CordbType *pType = 0; |
8731 | types = new CordbType*[methodGenerics+classGenerics]; |
8732 | ULONG i = 0; |
8733 | for (;i < classGenerics; ++i) |
8734 | { |
8735 | CorElementType et; |
8736 | IfFailRet(typeSig.PeekElemType(&et)); |
8737 | if ((et == ELEMENT_TYPE_VAR || et == ELEMENT_TYPE_MVAR) && currentInstantiation->m_cInst == 0) |
8738 | return E_FAIL; |
8739 | |
8740 | CordbType::SigToType(pModule, &typeSig, currentInstantiation, &pType); |
8741 | types[i] = pType; |
8742 | typeSig.SkipExactlyOne(); |
8743 | } |
8744 | |
8745 | for (; i < methodGenerics+classGenerics; ++i) |
8746 | { |
8747 | CorElementType et; |
8748 | IfFailRet(genericSig.PeekElemType(&et)); |
8749 | if ((et == ELEMENT_TYPE_VAR || et == ELEMENT_TYPE_MVAR) && currentInstantiation->m_cInst == 0) |
8750 | return E_FAIL; |
8751 | |
8752 | CordbType::SigToType(pModule, &genericSig, currentInstantiation, &pType); |
8753 | types[i] = pType; |
8754 | genericSig.SkipExactlyOne(); |
8755 | } |
8756 | |
8757 | inst = Instantiation(methodGenerics+classGenerics, types, classGenerics); |
8758 | return S_OK; |
8759 | } |
8760 | |
8761 | HRESULT CordbJITILFrame::GetReturnValueForILOffsetImpl(ULONG32 ILoffset, ICorDebugValue** ppReturnValue) |
8762 | { |
8763 | if (ppReturnValue == NULL) |
8764 | return E_INVALIDARG; |
8765 | |
8766 | if (!m_genericArgsLoaded) |
8767 | LoadGenericArgs(); |
8768 | |
8769 | // First verify that we're stopped at the correct native offset |
8770 | // by calling ICorDebugCode3::GetReturnValueLiveOffset and |
8771 | // compare the returned native offset to our current location. |
8772 | HRESULT hr = S_OK; |
8773 | CordbNativeCode *pCode = m_nativeFrame->m_nativeCode; |
8774 | pCode->LoadNativeInfo(); |
8775 | |
8776 | ULONG32 count = 0; |
8777 | IfFailRet(pCode->GetReturnValueLiveOffsetImpl(&m_genericArgs, ILoffset, 0, &count, NULL)); |
8778 | |
8779 | NewArrayHolder<ULONG32> offsets(new ULONG32[count]); |
8780 | IfFailRet(pCode->GetReturnValueLiveOffsetImpl(&m_genericArgs, ILoffset, count, &count, offsets)); |
8781 | |
8782 | bool found = false; |
8783 | ULONG32 currentOffset = m_nativeFrame->GetIPOffset(); |
8784 | for (ULONG32 i = 0; i < count; ++i) |
8785 | { |
8786 | if (currentOffset == offsets[i]) |
8787 | { |
8788 | found = true; |
8789 | break; |
8790 | } |
8791 | } |
8792 | |
8793 | if (!found) |
8794 | return E_UNEXPECTED; |
8795 | |
8796 | // Get the signatures and mdToken for the callee. |
8797 | SigParser methodSig, genericSig; |
8798 | mdToken mdFunction = 0, targetClass = 0; |
8799 | IfFailRet(pCode->GetCallSignature(ILoffset, &targetClass, &mdFunction, methodSig, genericSig)); |
8800 | IfFailRet(CordbNativeCode::SkipToReturn(methodSig)); |
8801 | |
8802 | |
8803 | |
8804 | |
8805 | // Create the Instantiation, type and then return value |
8806 | NewArrayHolder<CordbType*> types; |
8807 | Instantiation inst; |
8808 | CordbType *pType = 0; |
8809 | IfFailRet(BuildInstantiationForCallsite(GetModule(), types, inst, &m_genericArgs, targetClass, genericSig)); |
8810 | IfFailRet(CordbType::SigToType(GetModule(), &methodSig, &inst, &pType)); |
8811 | return GetReturnValueForType(pType, ppReturnValue); |
8812 | } |
8813 | |
8814 | |
8815 | HRESULT CordbJITILFrame::GetReturnValueForType(CordbType *pType, ICorDebugValue **ppReturnValue) |
8816 | { |
8817 | |
8818 | |
8819 | #if defined(DBG_TARGET_X86) |
8820 | const CorDebugRegister floatRegister = REGISTER_X86_FPSTACK_0; |
8821 | #elif defined(DBG_TARGET_AMD64) |
8822 | const CorDebugRegister floatRegister = REGISTER_AMD64_XMM0; |
8823 | #elif defined(DBG_TARGET_ARM64) |
8824 | const CorDebugRegister floatRegister = REGISTER_ARM64_V0; |
8825 | #elif defined(DBG_TARGET_ARM) |
8826 | const CorDebugRegister floatRegister = REGISTER_ARM_D0; |
8827 | #endif |
8828 | |
8829 | #if defined(DBG_TARGET_X86) |
8830 | const CorDebugRegister ptrRegister = REGISTER_X86_EAX; |
8831 | const CorDebugRegister ptrHighWordRegister = REGISTER_X86_EDX; |
8832 | #elif defined(DBG_TARGET_AMD64) |
8833 | const CorDebugRegister ptrRegister = REGISTER_AMD64_RAX; |
8834 | #elif defined(DBG_TARGET_ARM64) |
8835 | const CorDebugRegister ptrRegister = REGISTER_ARM64_X0; |
8836 | #elif defined(DBG_TARGET_ARM) |
8837 | const CorDebugRegister ptrRegister = REGISTER_ARM_R0; |
8838 | const CorDebugRegister ptrHighWordRegister = REGISTER_ARM_R1; |
8839 | |
8840 | #endif |
8841 | |
8842 | CorElementType corReturnType = pType->GetElementType(); |
8843 | switch (corReturnType) |
8844 | { |
8845 | default: |
8846 | return m_nativeFrame->GetLocalRegisterValue(ptrRegister, pType, ppReturnValue); |
8847 | |
8848 | case ELEMENT_TYPE_R4: |
8849 | case ELEMENT_TYPE_R8: |
8850 | return m_nativeFrame->GetLocalFloatingPointValue(floatRegister, pType, ppReturnValue); |
8851 | |
8852 | #if defined(DBG_TARGET_X86) || defined(DBG_TARGET_ARM) |
8853 | case ELEMENT_TYPE_I8: |
8854 | case ELEMENT_TYPE_U8: |
8855 | return m_nativeFrame->GetLocalDoubleRegisterValue(ptrHighWordRegister, ptrRegister, pType, ppReturnValue); |
8856 | #endif |
8857 | } |
8858 | } |
8859 | |
8860 | HRESULT CordbJITILFrame::EnumerateLocalVariablesEx(ILCodeKind flags, ICorDebugValueEnum **ppValueEnum) |
8861 | { |
8862 | PUBLIC_REENTRANT_API_ENTRY(this); |
8863 | FAIL_IF_NEUTERED(this); |
8864 | VALIDATE_POINTER_TO_OBJECT(ppValueEnum, ICorDebugValueEnum **); |
8865 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8866 | |
8867 | HRESULT hr = S_OK; |
8868 | if (flags != ILCODE_ORIGINAL_IL && flags != ILCODE_REJIT_IL) |
8869 | return E_INVALIDARG; |
8870 | |
8871 | EX_TRY |
8872 | { |
8873 | RSInitHolder<CordbValueEnum> cdVE(new CordbValueEnum(m_nativeFrame, |
8874 | flags == ILCODE_ORIGINAL_IL ? CordbValueEnum::LOCAL_VARS_ORIGINAL_IL : CordbValueEnum::LOCAL_VARS_REJIT_IL)); |
8875 | |
8876 | // Initialize the new enum |
8877 | hr = cdVE->Init(); |
8878 | IfFailThrow(hr); |
8879 | |
8880 | cdVE.TransferOwnershipExternal(ppValueEnum); |
8881 | } |
8882 | EX_CATCH_HRESULT(hr); |
8883 | |
8884 | return hr; |
8885 | } |
8886 | HRESULT CordbJITILFrame::GetLocalVariableEx(ILCodeKind flags, DWORD dwIndex, ICorDebugValue **ppValue) |
8887 | { |
8888 | PUBLIC_REENTRANT_API_ENTRY(this); |
8889 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
8890 | FAIL_IF_NEUTERED(this); |
8891 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8892 | |
8893 | if (flags != ILCODE_ORIGINAL_IL && flags != ILCODE_REJIT_IL) |
8894 | return E_INVALIDARG; |
8895 | if (flags == ILCODE_REJIT_IL && m_pReJitCode == NULL) |
8896 | return E_INVALIDARG; |
8897 | |
8898 | const ICorDebugInfo::NativeVarInfo *pNativeInfo; |
8899 | |
8900 | // |
8901 | // First, make sure that we've got the jitted variable location data |
8902 | // loaded from the left side. |
8903 | // |
8904 | |
8905 | HRESULT hr = S_OK; |
8906 | EX_TRY |
8907 | { |
8908 | m_nativeFrame->m_nativeCode->LoadNativeInfo(); //throws |
8909 | |
8910 | ULONG cArgs; |
8911 | if (m_fVarArgFnx && (!m_sigParserCached.IsNull())) |
8912 | { |
8913 | cArgs = m_allArgsCount; |
8914 | } |
8915 | else |
8916 | { |
8917 | cArgs = m_nativeFrame->m_nativeCode->GetFixedArgCount(); |
8918 | } |
8919 | |
8920 | hr = ILVariableToNative(dwIndex + cArgs, &pNativeInfo); |
8921 | IfFailThrow(hr); |
8922 | |
8923 | LoadGenericArgs(); |
8924 | |
8925 | // Get the type of this argument from the function |
8926 | CordbType *type; |
8927 | CordbILCode* pActiveCode = m_pReJitCode != NULL ? m_pReJitCode : m_ilCode; |
8928 | hr = pActiveCode->GetLocalVariableType(dwIndex, &(this->m_genericArgs), &type); |
8929 | IfFailThrow(hr); |
8930 | |
8931 | // if the caller wants the original IL local, it should implicitly map to the same index |
8932 | // variable in the profiler instrumented code. We can't determine whether the instrumented code |
8933 | // really adhered to this, but we can check two things: |
8934 | // a) the requested index was valid in the original signature |
8935 | // (GetLocalVariableType will return E_INVALIDARG if not) |
8936 | // b) the type of local in the original signature matches the type of local in the instrumented signature |
8937 | // (the code below will return CORDBG_E_IL_VAR_NOT_AVAILABLE) |
8938 | if (flags == ILCODE_ORIGINAL_IL && m_pReJitCode != NULL) |
8939 | { |
8940 | CordbType* pOriginalType; |
8941 | hr = m_ilCode->GetLocalVariableType(dwIndex, &(this->m_genericArgs), &pOriginalType); |
8942 | IfFailThrow(hr); |
8943 | if (pOriginalType != type) |
8944 | { |
8945 | IfFailThrow(CORDBG_E_IL_VAR_NOT_AVAILABLE); // bad profiler, it shouldn't have changed types |
8946 | } |
8947 | } |
8948 | |
8949 | |
8950 | hr = GetNativeVariable(type, pNativeInfo, ppValue); |
8951 | IfFailThrow(hr); |
8952 | } |
8953 | EX_CATCH_HRESULT(hr); |
8954 | |
8955 | return hr; |
8956 | } |
8957 | |
8958 | HRESULT CordbJITILFrame::GetCodeEx(ILCodeKind flags, ICorDebugCode **ppCode) |
8959 | { |
8960 | HRESULT hr = S_OK; |
8961 | PUBLIC_API_ENTRY(this); |
8962 | FAIL_IF_NEUTERED(this); |
8963 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
8964 | |
8965 | if (flags != ILCODE_ORIGINAL_IL && flags != ILCODE_REJIT_IL) |
8966 | return E_INVALIDARG; |
8967 | |
8968 | if (flags == ILCODE_ORIGINAL_IL) |
8969 | { |
8970 | return GetCode(ppCode); |
8971 | } |
8972 | else |
8973 | { |
8974 | *ppCode = m_pReJitCode; |
8975 | if (m_pReJitCode != NULL) |
8976 | { |
8977 | m_pReJitCode->ExternalAddRef(); |
8978 | } |
8979 | } |
8980 | return S_OK; |
8981 | } |
8982 | |
8983 | CordbILCode* CordbJITILFrame::GetOriginalILCode() |
8984 | { |
8985 | return m_ilCode; |
8986 | } |
8987 | |
8988 | CordbReJitILCode* CordbJITILFrame::GetReJitILCode() |
8989 | { |
8990 | return m_pReJitCode; |
8991 | } |
8992 | |
8993 | /* ------------------------------------------------------------------------- * |
8994 | * Eval class |
8995 | * ------------------------------------------------------------------------- */ |
8996 | |
8997 | CordbEval::CordbEval(CordbThread *pThread) |
8998 | : CordbBase(pThread->GetProcess(), 0, enumCordbEval), |
8999 | m_thread(pThread), // implicit InternalAddRef |
9000 | m_function(NULL), |
9001 | m_complete(false), |
9002 | m_successful(false), |
9003 | m_aborted(false), |
9004 | m_resultAddr(NULL), |
9005 | m_evalDuringException(false) |
9006 | { |
9007 | m_vmObjectHandle = VMPTR_OBJECTHANDLE::NullPtr(); |
9008 | m_debuggerEvalKey = LSPTR_DEBUGGEREVAL::NullPtr(); |
9009 | |
9010 | m_resultType.elementType = ELEMENT_TYPE_VOID; |
9011 | m_resultAppDomainToken = VMPTR_AppDomain::NullPtr(); |
9012 | |
9013 | CordbAppDomain * pDomain = m_thread->GetAppDomain(); |
9014 | (void)pDomain; //prevent "unused variable" error from GCC |
9015 | #ifdef _DEBUG |
9016 | // Remember what AD we started in so that we can check that we finish there too. |
9017 | m_DbgAppDomainStarted = pDomain; |
9018 | #endif |
9019 | |
9020 | // Place ourselves on the processes neuter-list. |
9021 | HRESULT hr = S_OK; |
9022 | EX_TRY |
9023 | { |
9024 | GetProcess()->AddToLeftSideResourceCleanupList(this); |
9025 | } |
9026 | EX_CATCH_HRESULT(hr); |
9027 | SetUnrecoverableIfFailed(GetProcess(), hr); |
9028 | } |
9029 | |
9030 | CordbEval::~CordbEval() |
9031 | { |
9032 | _ASSERTE(IsNeutered()); |
9033 | } |
9034 | |
9035 | // Free the left-side resources for the eval. |
9036 | void CordbEval::NeuterLeftSideResources() |
9037 | { |
9038 | SendCleanup(); |
9039 | |
9040 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
9041 | Neuter(); |
9042 | } |
9043 | |
9044 | // Neuter the CordbEval |
9045 | // |
9046 | // Assumptions: |
9047 | // By the time we neuter the eval, it's associated left-side resources |
9048 | // are already cleaned up (either explicitly from calling code:CordbEval::SendCleanup |
9049 | // or implicitly from the left-side exiting). |
9050 | // |
9051 | // Notes: |
9052 | // We place ourselves on a neuter list. This gets called when the neuterlist sweeps. |
9053 | void CordbEval::Neuter() |
9054 | { |
9055 | // By now, we should have freed our target-resources (code:CordbEval::NeuterLeftSideResources |
9056 | // or code:CordbEval::SendCleanup), unless the target is dead (terminated or about to exit). |
9057 | BOOL fTargetIsDead = !GetProcess()->IsSafeToSendEvents() || GetProcess()->m_exiting; |
9058 | (void)fTargetIsDead; //prevent "unused variable" error from GCC |
9059 | _ASSERTE(fTargetIsDead || (m_debuggerEvalKey == NULL)); |
9060 | |
9061 | m_thread.Clear(); |
9062 | |
9063 | CordbBase::Neuter(); |
9064 | } |
9065 | |
9066 | HRESULT CordbEval::SendCleanup() |
9067 | { |
9068 | FAIL_IF_NEUTERED(this); |
9069 | INTERNAL_SYNC_API_ENTRY(GetProcess()); // |
9070 | |
9071 | HRESULT hr = S_OK; |
9072 | |
9073 | // Send a message to the left side to release the eval object over |
9074 | // there if one exists. |
9075 | if ((m_debuggerEvalKey != NULL) && |
9076 | GetProcess()->IsSafeToSendEvents()) |
9077 | { |
9078 | // Call Abort() before doing new CallFunction() |
9079 | if (!m_complete) |
9080 | return CORDBG_E_FUNC_EVAL_NOT_COMPLETE; |
9081 | |
9082 | // Release the left side handle to the object |
9083 | DebuggerIPCEvent event; |
9084 | |
9085 | GetProcess()->InitIPCEvent( |
9086 | &event, |
9087 | DB_IPCE_FUNC_EVAL_CLEANUP, |
9088 | true, |
9089 | m_thread->GetAppDomain()->GetADToken()); |
9090 | |
9091 | event.FuncEvalCleanup.debuggerEvalKey = m_debuggerEvalKey; |
9092 | |
9093 | hr = GetProcess()->SendIPCEvent(&event, sizeof(DebuggerIPCEvent)); |
9094 | IfFailRet(hr); |
9095 | |
9096 | #if _DEBUG |
9097 | if (SUCCEEDED(hr)) |
9098 | _ASSERTE(event.type == DB_IPCE_FUNC_EVAL_CLEANUP_RESULT); |
9099 | #endif |
9100 | |
9101 | // Null out the key so we don't try to do this again. |
9102 | m_debuggerEvalKey = LSPTR_DEBUGGEREVAL::NullPtr(); |
9103 | |
9104 | hr = event.hr; |
9105 | } |
9106 | |
9107 | // Release the cached HandleValue for the result. This may cleanup resources, |
9108 | // like our object handle to the func-eval result. |
9109 | m_pHandleValue.Clear(); |
9110 | |
9111 | |
9112 | return hr; |
9113 | } |
9114 | |
9115 | HRESULT CordbEval::QueryInterface(REFIID id, void **pInterface) |
9116 | { |
9117 | if (id == IID_ICorDebugEval) |
9118 | { |
9119 | *pInterface = static_cast<ICorDebugEval*>(this); |
9120 | } |
9121 | else if (id == IID_ICorDebugEval2) |
9122 | { |
9123 | *pInterface = static_cast<ICorDebugEval2*>(this); |
9124 | } |
9125 | else if (id == IID_IUnknown) |
9126 | { |
9127 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugEval*>(this)); |
9128 | } |
9129 | else |
9130 | { |
9131 | *pInterface = NULL; |
9132 | return E_NOINTERFACE; |
9133 | } |
9134 | |
9135 | ExternalAddRef(); |
9136 | return S_OK; |
9137 | } |
9138 | |
9139 | // |
9140 | // Gather data about an argument to either CallFunction or NewObject |
9141 | // and place it into a DebuggerIPCE_FuncEvalArgData struct for passing |
9142 | // to the Left Side. |
9143 | // |
9144 | HRESULT CordbEval::GatherArgInfo(ICorDebugValue *pValue, |
9145 | DebuggerIPCE_FuncEvalArgData *argData) |
9146 | { |
9147 | FAIL_IF_NEUTERED(this); |
9148 | INTERNAL_SYNC_API_ENTRY(GetProcess()); // |
9149 | |
9150 | HRESULT hr; |
9151 | CORDB_ADDRESS addr; |
9152 | CorElementType ty; |
9153 | bool needRelease = false; |
9154 | |
9155 | pValue->GetType(&ty); |
9156 | |
9157 | // Note: if the value passed in is in fact a byref, then we need to dereference it to get to the real thing. Passing |
9158 | // a byref as a byref to a func eval is never right. |
9159 | if ((ty == ELEMENT_TYPE_BYREF) || (ty == ELEMENT_TYPE_TYPEDBYREF)) |
9160 | { |
9161 | ICorDebugReferenceValue *prv = NULL; |
9162 | |
9163 | // The value had better implement ICorDebugReference value. |
9164 | IfFailRet(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&prv)); |
9165 | |
9166 | // This really should always work for a byref, unless we're out of memory. |
9167 | hr = prv->Dereference(&pValue); |
9168 | prv->Release(); |
9169 | |
9170 | IfFailRet(hr); |
9171 | |
9172 | // Make sure to get the type we were referencing for use below. |
9173 | pValue->GetType(&ty); |
9174 | needRelease = true; |
9175 | } |
9176 | |
9177 | // We should never have a byref by this point. |
9178 | _ASSERTE((ty != ELEMENT_TYPE_BYREF) && (ty != ELEMENT_TYPE_TYPEDBYREF)); |
9179 | |
9180 | pValue->GetAddress(&addr); |
9181 | |
9182 | argData->argAddr = CORDB_ADDRESS_TO_PTR(addr); |
9183 | argData->argElementType = ty; |
9184 | |
9185 | argData->argIsHandleValue = false; |
9186 | argData->argIsLiteral = false; |
9187 | argData->fullArgType = NULL; |
9188 | argData->fullArgTypeNodeCount = 0; |
9189 | |
9190 | // We have to have knowledge of our value implementation here, |
9191 | // which it would nice if we didn't have to know. |
9192 | CordbValue *cv = NULL; |
9193 | |
9194 | switch(ty) |
9195 | { |
9196 | |
9197 | case ELEMENT_TYPE_CLASS: |
9198 | case ELEMENT_TYPE_OBJECT: |
9199 | case ELEMENT_TYPE_STRING: |
9200 | case ELEMENT_TYPE_PTR: |
9201 | case ELEMENT_TYPE_ARRAY: |
9202 | case ELEMENT_TYPE_SZARRAY: |
9203 | { |
9204 | ICorDebugHandleValue *pHandle = NULL; |
9205 | pValue->QueryInterface(IID_ICorDebugHandleValue, (void **) &pHandle); |
9206 | if (pHandle == NULL) |
9207 | { |
9208 | // A reference value |
9209 | cv = static_cast<CordbValue*> (static_cast<CordbReferenceValue*> (pValue)); |
9210 | argData->argIsHandleValue = !(((CordbReferenceValue *)pValue)->m_valueHome.ObjHandleIsNull()); |
9211 | |
9212 | // Is this a literal value? If, we'll copy the data to the |
9213 | // buffer area so the left side can get it. |
9214 | CordbReferenceValue *rv; |
9215 | rv = static_cast<CordbReferenceValue*>(pValue); |
9216 | argData->argIsLiteral = rv->CopyLiteralData(argData->argLiteralData); |
9217 | if (rv->GetValueHome()) |
9218 | { |
9219 | rv->GetValueHome()->CopyToIPCEType(&(argData->argHome)); |
9220 | } |
9221 | } |
9222 | else |
9223 | { |
9224 | argData->argIsHandleValue = true; |
9225 | argData->argIsLiteral = false; |
9226 | pHandle->Release(); |
9227 | argData->argHome.kind = RAK_NONE; |
9228 | } |
9229 | } |
9230 | break; |
9231 | |
9232 | case ELEMENT_TYPE_VALUETYPE: // OK: this E_T_VALUETYPE comes ICorDebugValue::GetType |
9233 | |
9234 | // A value class object |
9235 | cv = static_cast<CordbValue*> (static_cast<CordbVCObjectValue*>(static_cast<ICorDebugObjectValue*> (pValue))); |
9236 | |
9237 | // The EE does not guarantee to have exact type information |
9238 | // available for all struct types, so we indicate the type by using a |
9239 | // DebuggerIPCE_TypeArgData serialization of a type. |
9240 | // |
9241 | // At the moment the LHS only cares about this data |
9242 | // when boxing the "this" pointer. |
9243 | { |
9244 | CordbVCObjectValue * pVCObjVal = |
9245 | static_cast<CordbVCObjectValue *>(static_cast<ICorDebugObjectValue*> (pValue)); |
9246 | |
9247 | unsigned int fullArgTypeNodeCount = 0; |
9248 | cv->m_type->CountTypeDataNodes(&fullArgTypeNodeCount); |
9249 | |
9250 | _ASSERTE(fullArgTypeNodeCount > 0); |
9251 | unsigned int bufferSize = sizeof(DebuggerIPCE_TypeArgData) * fullArgTypeNodeCount; |
9252 | DebuggerIPCE_TypeArgData *bufferFrom = (DebuggerIPCE_TypeArgData *) _alloca(bufferSize); |
9253 | |
9254 | DebuggerIPCE_TypeArgData *curr = bufferFrom; |
9255 | CordbType::GatherTypeData(cv->m_type, &curr); |
9256 | |
9257 | void *buffer = NULL; |
9258 | IfFailRet(m_thread->GetProcess()->GetAndWriteRemoteBuffer(m_thread->GetAppDomain(), bufferSize, bufferFrom, &buffer)); |
9259 | |
9260 | argData->fullArgType = buffer; |
9261 | argData->fullArgTypeNodeCount = fullArgTypeNodeCount; |
9262 | // Is it enregistered? |
9263 | if ((addr == NULL) && (pVCObjVal->GetValueHome() != NULL)) |
9264 | { |
9265 | pVCObjVal->GetValueHome()->CopyToIPCEType(&(argData->argHome)); |
9266 | } |
9267 | |
9268 | } |
9269 | break; |
9270 | |
9271 | default: |
9272 | |
9273 | // A generic value |
9274 | cv = static_cast<CordbValue*> (static_cast<CordbGenericValue*> (pValue)); |
9275 | |
9276 | // Is this a literal value? If, we'll copy the data to the |
9277 | // buffer area so the left side can get it. |
9278 | CordbGenericValue *gv = (CordbGenericValue*)pValue; |
9279 | argData->argIsLiteral = gv->CopyLiteralData(argData->argLiteralData); |
9280 | // Is it enregistered? |
9281 | if ((addr == NULL) && (gv->GetValueHome() != NULL)) |
9282 | { |
9283 | gv->GetValueHome()->CopyToIPCEType(&(argData->argHome)); |
9284 | } |
9285 | break; |
9286 | } |
9287 | |
9288 | |
9289 | // Release pValue if we got it via a dereference from above. |
9290 | if (needRelease) |
9291 | pValue->Release(); |
9292 | |
9293 | return S_OK; |
9294 | } |
9295 | |
9296 | |
9297 | HRESULT CordbEval::SendFuncEval(unsigned int genericArgsCount, |
9298 | ICorDebugType *genericArgs[], |
9299 | void *argData1, unsigned int argData1Size, |
9300 | void *argData2, unsigned int argData2Size, |
9301 | DebuggerIPCEvent * event) |
9302 | { |
9303 | FAIL_IF_NEUTERED(this); |
9304 | INTERNAL_SYNC_API_ENTRY(GetProcess()); // |
9305 | unsigned int genericArgsNodeCount = 0; |
9306 | |
9307 | DebuggerIPCE_TypeArgData *tyargData = NULL; |
9308 | CordbType::CountTypeDataNodesForInstantiation(genericArgsCount,genericArgs,&genericArgsNodeCount); |
9309 | |
9310 | unsigned int tyargDataSize = sizeof(DebuggerIPCE_TypeArgData) * genericArgsNodeCount; |
9311 | |
9312 | if (genericArgsNodeCount > 0) |
9313 | { |
9314 | tyargData = new (nothrow) DebuggerIPCE_TypeArgData[genericArgsNodeCount]; |
9315 | if (tyargData == NULL) |
9316 | { |
9317 | return E_OUTOFMEMORY; |
9318 | } |
9319 | |
9320 | DebuggerIPCE_TypeArgData *curr_tyargData = tyargData; |
9321 | CordbType::GatherTypeDataForInstantiation(genericArgsCount, genericArgs, &curr_tyargData); |
9322 | |
9323 | } |
9324 | event->FuncEval.genericArgsNodeCount = genericArgsNodeCount; |
9325 | |
9326 | |
9327 | // Are we doing an eval during an exception? If so, we need to remember |
9328 | // that over here and also tell the Left Side. |
9329 | event->FuncEval.evalDuringException = m_thread->HasException(); |
9330 | m_evalDuringException = !!event->FuncEval.evalDuringException; |
9331 | m_vmThreadOldExceptionHandle = m_thread->GetThreadExceptionRawObjectHandle(); |
9332 | |
9333 | // Corresponding Release() on DB_IPCE_FUNC_EVAL_COMPLETE. |
9334 | // If a func eval is aborted, the LHS may not complete the abort |
9335 | // immediately and hence we cant do a SendCleanup(). Hence, we maintain |
9336 | // an extra ref-count to determine when this can be done. |
9337 | AddRef(); |
9338 | |
9339 | HRESULT hr = m_thread->GetProcess()->SendIPCEvent(event, sizeof(DebuggerIPCEvent)); |
9340 | |
9341 | // If the send failed, return that failure. |
9342 | if (FAILED(hr)) |
9343 | goto LExit; |
9344 | |
9345 | _ASSERTE(event->type == DB_IPCE_FUNC_EVAL_SETUP_RESULT); |
9346 | |
9347 | hr = event->hr; |
9348 | |
9349 | // Memory has been allocated to hold info about each argument on |
9350 | // the left side now, so copy the argument data over to the left |
9351 | // side. No need to send another event, since the left side won't |
9352 | // take any more action on this evaluation until the process is |
9353 | // continued anyway. |
9354 | // |
9355 | // The type arguments come first, followed by up to two blobs of data |
9356 | // for other arguments. |
9357 | if (SUCCEEDED(hr)) |
9358 | { |
9359 | EX_TRY |
9360 | { |
9361 | CORDB_ADDRESS argdata = event->FuncEvalSetupComplete.argDataArea; |
9362 | |
9363 | if ((tyargData != NULL) && (tyargDataSize != 0)) |
9364 | { |
9365 | |
9366 | TargetBuffer tb(argdata, tyargDataSize); |
9367 | m_thread->GetProcess()->SafeWriteBuffer(tb, (const BYTE*) tyargData); // throws |
9368 | |
9369 | argdata += tyargDataSize; |
9370 | } |
9371 | |
9372 | if ((argData1 != NULL) && (argData1Size != 0)) |
9373 | { |
9374 | TargetBuffer tb(argdata, argData1Size); |
9375 | m_thread->GetProcess()->SafeWriteBuffer(tb, (const BYTE*) argData1); // throws |
9376 | |
9377 | argdata += argData1Size; |
9378 | } |
9379 | |
9380 | if ((argData2 != NULL) && (argData2Size != 0)) |
9381 | { |
9382 | TargetBuffer tb(argdata, argData2Size); |
9383 | m_thread->GetProcess()->SafeWriteBuffer(tb, (const BYTE*) argData2); // throws |
9384 | |
9385 | argdata += argData2Size; |
9386 | } |
9387 | } |
9388 | EX_CATCH_HRESULT(hr); |
9389 | } |
9390 | |
9391 | LExit: |
9392 | if (tyargData) |
9393 | { |
9394 | delete [] tyargData; |
9395 | } |
9396 | |
9397 | // Save the key to the eval on the left side for future reference. |
9398 | if (SUCCEEDED(hr)) |
9399 | { |
9400 | m_debuggerEvalKey = event->FuncEvalSetupComplete.debuggerEvalKey; |
9401 | m_thread->GetProcess()->IncrementOutstandingEvalCount(); |
9402 | } |
9403 | else |
9404 | { |
9405 | // We dont expect to receive a DB_IPCE_FUNC_EVAL_COMPLETE, so just release here |
9406 | Release(); |
9407 | } |
9408 | |
9409 | return hr; |
9410 | } |
9411 | |
9412 | |
9413 | // Get the AppDomain that an object lives in. |
9414 | // This does not adjust any reference counts. |
9415 | // Returns NULL if we can't determine the appdomain, or if the value is known to be agile. |
9416 | CordbAppDomain * GetAppDomainFromValue(ICorDebugValue * pValue) |
9417 | { |
9418 | // Unfortunately, there's no direct way to cast from an ICDValue to a CordbValue. |
9419 | // So we need to QI for the culprit interfaces and check specifically. |
9420 | |
9421 | { |
9422 | RSExtSmartPtr<ICorDebugHandleValue> handleP; |
9423 | pValue->QueryInterface(IID_ICorDebugHandleValue, (void**)&handleP); |
9424 | if (handleP != NULL) |
9425 | { |
9426 | CordbHandleValue * chp = static_cast<CordbHandleValue *> (handleP.GetValue()); |
9427 | return chp->GetAppDomain(); |
9428 | } |
9429 | } |
9430 | |
9431 | { |
9432 | RSExtSmartPtr<ICorDebugReferenceValue> refP; |
9433 | pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&refP); |
9434 | if (refP != NULL) |
9435 | { |
9436 | CordbReferenceValue * crp = static_cast<CordbReferenceValue *> (refP.GetValue()); |
9437 | return crp->GetAppDomain(); |
9438 | } |
9439 | } |
9440 | |
9441 | { |
9442 | RSExtSmartPtr<ICorDebugObjectValue> objP; |
9443 | pValue->QueryInterface(IID_ICorDebugObjectValue, (void**)&objP); |
9444 | if (objP != NULL) |
9445 | { |
9446 | CordbVCObjectValue * crp = static_cast<CordbVCObjectValue*> (objP.GetValue()); |
9447 | return crp->GetAppDomain(); |
9448 | } |
9449 | } |
9450 | |
9451 | // Assume nothing else has AD affinity. |
9452 | return NULL; |
9453 | } |
9454 | |
9455 | HRESULT CordbEval::CallFunction(ICorDebugFunction *pFunction, |
9456 | ULONG32 nArgs, |
9457 | ICorDebugValue *pArgs[]) |
9458 | { |
9459 | PUBLIC_API_ENTRY(this); |
9460 | FAIL_IF_NEUTERED(this); |
9461 | if (GetProcess()->GetShim() == NULL) |
9462 | { |
9463 | return E_NOTIMPL; |
9464 | } |
9465 | return CallParameterizedFunction(pFunction,0,NULL,nArgs,pArgs); |
9466 | } |
9467 | |
9468 | //----------------------------------------------------------------------------- |
9469 | // See if we can convert general Func-eval failure HRs (which are usually based on EE-invariants that |
9470 | // may be meaningless to the user) into a more specific user-friendly hr. |
9471 | // Doing the conversions here in the RS (instead of in the LS) makes it more clear that these |
9472 | // HRs definitely map to concepts described by the ICorDebugAPI instead of EE-invariants. |
9473 | // It also lets us clearly prioritize the HRs in case of ambiguity. |
9474 | //----------------------------------------------------------------------------- |
9475 | HRESULT CordbEval::FilterHR(HRESULT hr) |
9476 | { |
9477 | // Currently, we only make CORDBG_E_ILLEGAL_AT_GC_UNSAFE_POINT more specific. |
9478 | // If it's not that HR, then shortcut our work. |
9479 | if (hr != CORDBG_E_ILLEGAL_AT_GC_UNSAFE_POINT) |
9480 | { |
9481 | return hr; |
9482 | } |
9483 | |
9484 | // In the case of conflicting HRs (if the func-eval fails for multiple reasons), |
9485 | // we'll try to give priority to the more general HR. |
9486 | // This communicates the quickest action for the user to be able to get to a |
9487 | // func-eval friendly spot. It also means less churn in the hrs we return |
9488 | // because specific hrs are more likely to change than general ones. |
9489 | |
9490 | // If we got CORDBG_E_ILLEGAL_AT_GC_UNSAFE_POINT, check the common reasons. |
9491 | // We'll use the Right-Side's intimate knowledge of the Left-Side to guess _why_ |
9492 | // it's a GC-unsafe spot, and then we'll communicate that back w/ a more meaningful HR. |
9493 | // If GC safe-spots change, then these errors should be updated. |
9494 | |
9495 | |
9496 | // |
9497 | // Most likely is if we're in native code. Check that first. |
9498 | // |
9499 | // In V2, we do this check by checking if the leaf chain is native. Since we have no chain in Arrowhead, |
9500 | // we can't do this check. Instead, we check whether the active frame is NULL or not. If it's NULL, |
9501 | // then we are stopped in native code. |
9502 | // |
9503 | HRESULT hrTemp = S_OK; |
9504 | if (GetProcess()->GetShim() != NULL) |
9505 | { |
9506 | // the V2 case |
9507 | RSExtSmartPtr<ICorDebugChain> pChain; |
9508 | hrTemp = m_thread->GetActiveChain(&pChain); |
9509 | if (FAILED(hrTemp)) |
9510 | { |
9511 | // just return the original HR if this call fails |
9512 | return hr; |
9513 | } |
9514 | |
9515 | // pChain should never be NULL here, since we should have at least one thread start chain even if |
9516 | // there is no managed code on the stack, but let's just be extra careful here. |
9517 | if (pChain == NULL) |
9518 | { |
9519 | return hr; |
9520 | } |
9521 | |
9522 | BOOL fManagedChain; |
9523 | hrTemp = pChain->IsManaged(&fManagedChain); |
9524 | if (FAILED(hrTemp)) |
9525 | { |
9526 | // just return the original HR if this call fails |
9527 | return hr; |
9528 | } |
9529 | |
9530 | if (fManagedChain == FALSE) |
9531 | { |
9532 | return CORDBG_E_ILLEGAL_IN_NATIVE_CODE; |
9533 | } |
9534 | } |
9535 | |
9536 | RSExtSmartPtr<ICorDebugFrame> pIFrame; |
9537 | hrTemp = m_thread->GetActiveFrame(&pIFrame); |
9538 | if (FAILED(hrTemp)) |
9539 | { |
9540 | // just return the original HR if this call fails |
9541 | return hr; |
9542 | } |
9543 | |
9544 | CordbFrame * pFrame = NULL; |
9545 | pFrame = CordbFrame::GetCordbFrameFromInterface(pIFrame); |
9546 | |
9547 | if (GetProcess()->GetShim() == NULL) |
9548 | { |
9549 | // the Arrowhead case |
9550 | if (pFrame == NULL) |
9551 | { |
9552 | return CORDBG_E_ILLEGAL_IN_NATIVE_CODE; |
9553 | } |
9554 | } |
9555 | |
9556 | // Next, check if we're in optimized code. |
9557 | // Optimized code doesn't directly mean that func-evals are illegal; but it greatly |
9558 | // increases the odds of being at a GC-unsafe point. |
9559 | // We give this failure higher precedence than the "Is in prolog" failure. |
9560 | |
9561 | if (pFrame != NULL) |
9562 | { |
9563 | CordbNativeFrame * pNativeFrame = pFrame->GetAsNativeFrame(); |
9564 | if (pNativeFrame != NULL) |
9565 | { |
9566 | CordbNativeCode * pCode = pNativeFrame->GetNativeCode(); |
9567 | if (pCode != NULL) |
9568 | { |
9569 | DWORD flags; |
9570 | hrTemp = pCode->GetModule()->GetJITCompilerFlags(&flags); |
9571 | |
9572 | if (SUCCEEDED(hrTemp)) |
9573 | { |
9574 | if ((flags & CORDEBUG_JIT_DISABLE_OPTIMIZATION) != CORDEBUG_JIT_DISABLE_OPTIMIZATION) |
9575 | { |
9576 | return CORDBG_E_ILLEGAL_IN_OPTIMIZED_CODE; |
9577 | } |
9578 | |
9579 | } // GetCompilerFlags |
9580 | } // Code |
9581 | |
9582 | CordbJITILFrame * pILFrame = pNativeFrame->m_JITILFrame; |
9583 | if (pILFrame != NULL) |
9584 | { |
9585 | if (pILFrame->m_mapping == MAPPING_PROLOG) |
9586 | { |
9587 | return CORDBG_E_ILLEGAL_IN_PROLOG; |
9588 | } |
9589 | } |
9590 | } // Native Frame |
9591 | } |
9592 | |
9593 | // No filtering. |
9594 | return hr; |
9595 | |
9596 | } |
9597 | |
9598 | //--------------------------------------------------------------------------------------- |
9599 | // |
9600 | // This routine calls a function with the given set of type arguments and actual arguments. |
9601 | // This is the jumping off point for func-eval. |
9602 | // |
9603 | // Arguments: |
9604 | // pFunction - The function to call. |
9605 | // nTypeArgs - The number of type-arguments for the method in rgpTypeArgs |
9606 | // rgpTypeArgs - An array of pointers to types. |
9607 | // nArgs - The number of arguments for the method in rgpArgs |
9608 | // rgpArgs - An array of pointers to values for the arguments to the method. |
9609 | // |
9610 | // Return Value: |
9611 | // HRESULT for the operation |
9612 | // |
9613 | HRESULT CordbEval::CallParameterizedFunction(ICorDebugFunction *pFunction, |
9614 | ULONG32 nTypeArgs, |
9615 | ICorDebugType * rgpTypeArgs[], |
9616 | ULONG32 nArgs, |
9617 | ICorDebugValue * rgpArgs[]) |
9618 | { |
9619 | PUBLIC_REENTRANT_API_ENTRY(this); |
9620 | FAIL_IF_NEUTERED(this); |
9621 | |
9622 | VALIDATE_POINTER_TO_OBJECT(pFunction, ICorDebugFunction *); |
9623 | |
9624 | if (nArgs > 0) |
9625 | { |
9626 | VALIDATE_POINTER_TO_OBJECT_ARRAY(rgpArgs, ICorDebugValue *, nArgs, true, true); |
9627 | } |
9628 | |
9629 | HRESULT hr = E_FAIL; |
9630 | |
9631 | { |
9632 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
9633 | |
9634 | // The LS will assume that all of the ICorDebugValues and ICorDebugTypes are in |
9635 | // the same appdomain as the function. Verify this. |
9636 | CordbAppDomain * pMethodAppDomain = (static_cast<CordbFunction *> (pFunction))->GetAppDomain(); |
9637 | |
9638 | if (!DoAppDomainsMatch(pMethodAppDomain, nTypeArgs, rgpTypeArgs, nArgs, rgpArgs)) |
9639 | { |
9640 | return ErrWrapper(CORDBG_E_APPDOMAIN_MISMATCH); |
9641 | } |
9642 | |
9643 | // Callers are free to reuse an ICorDebugEval object for multiple |
9644 | // evals. Since we create a Left Side eval representation each |
9645 | // time, we need to be sure to clean it up now that we know we're |
9646 | // done with it. |
9647 | hr = SendCleanup(); |
9648 | |
9649 | if (FAILED(hr)) |
9650 | { |
9651 | return hr; |
9652 | } |
9653 | |
9654 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
9655 | |
9656 | // Must be locked to get a cookie |
9657 | RsPtrHolder<CordbEval> hFuncEval(this); |
9658 | |
9659 | if (hFuncEval.Ptr().IsNull()) |
9660 | { |
9661 | return E_OUTOFMEMORY; |
9662 | } |
9663 | lockHolder.Release(); // release to send an IPC event. |
9664 | |
9665 | // Remember the function that we're evaluating. |
9666 | m_function = static_cast<CordbFunction *>(pFunction); |
9667 | m_evalType = DB_IPCE_FET_NORMAL; |
9668 | |
9669 | |
9670 | // Arrange the arguments into a form that the left side can deal |
9671 | // with. We do this before starting the func eval setup to ensure |
9672 | // that we can complete this step before mutating the left |
9673 | // side. |
9674 | DebuggerIPCE_FuncEvalArgData * pArgData = NULL; |
9675 | |
9676 | if (nArgs > 0) |
9677 | { |
9678 | // We need to make the same type of array that the left side |
9679 | // holds. |
9680 | pArgData = new (nothrow) DebuggerIPCE_FuncEvalArgData[nArgs]; |
9681 | |
9682 | if (pArgData == NULL) |
9683 | { |
9684 | return E_OUTOFMEMORY; |
9685 | } |
9686 | |
9687 | // For each argument, convert its home into something the left |
9688 | // side can understand. |
9689 | for (unsigned int i = 0; i < nArgs; i++) |
9690 | { |
9691 | hr = GatherArgInfo(rgpArgs[i], &(pArgData[i])); |
9692 | |
9693 | if (FAILED(hr)) |
9694 | { |
9695 | delete [] pArgData; |
9696 | return hr; |
9697 | } |
9698 | } |
9699 | } |
9700 | |
9701 | // Send over to the left side and get it to setup this eval. |
9702 | DebuggerIPCEvent event; |
9703 | m_thread->GetProcess()->InitIPCEvent(&event, DB_IPCE_FUNC_EVAL, true, m_thread->GetAppDomain()->GetADToken()); |
9704 | |
9705 | event.FuncEval.vmThreadToken = m_thread->m_vmThreadToken; |
9706 | event.FuncEval.funcEvalType = m_evalType; |
9707 | event.FuncEval.funcMetadataToken = m_function->GetMetadataToken(); |
9708 | event.FuncEval.vmDomainFile = m_function->GetModule()->GetRuntimeDomainFile(); |
9709 | event.FuncEval.funcEvalKey = hFuncEval.Ptr(); |
9710 | event.FuncEval.argCount = nArgs; |
9711 | event.FuncEval.genericArgsCount = nTypeArgs; |
9712 | |
9713 | |
9714 | |
9715 | hr = SendFuncEval(nTypeArgs, |
9716 | rgpTypeArgs, |
9717 | reinterpret_cast<void *>(pArgData), |
9718 | sizeof(DebuggerIPCE_FuncEvalArgData) * nArgs, |
9719 | NULL, |
9720 | 0, |
9721 | &event); |
9722 | |
9723 | // Cleanup |
9724 | |
9725 | if (pArgData) |
9726 | { |
9727 | delete [] pArgData; |
9728 | } |
9729 | |
9730 | if (SUCCEEDED(hr)) |
9731 | { |
9732 | hFuncEval.SuppressRelease(); // Now LS owns. |
9733 | } |
9734 | } |
9735 | |
9736 | // Convert from LS EE-centric failure code to something more friendly to end-users. |
9737 | // Success HRs will not be converted. |
9738 | hr = FilterHR(hr); |
9739 | |
9740 | // Return any failure the Left Side may have told us about. |
9741 | return hr; |
9742 | } |
9743 | |
9744 | BOOL CordbEval::DoAppDomainsMatch( CordbAppDomain * pAppDomain, |
9745 | ULONG32 nTypes, |
9746 | ICorDebugType *pTypes[], |
9747 | ULONG32 nValues, |
9748 | ICorDebugValue *pValues[] ) |
9749 | { |
9750 | _ASSERTE( !(pTypes == NULL && nTypes != 0) ); |
9751 | _ASSERTE( !(pValues == NULL && nValues != 0) ); |
9752 | |
9753 | // Make sure each value is in the appdomain. |
9754 | for(unsigned int i = 0; i < nValues; i++) |
9755 | { |
9756 | // Assuming that only Ref Values have AD affinity |
9757 | CordbAppDomain * pValueAppDomain = GetAppDomainFromValue( pValues[i] ); |
9758 | |
9759 | if ((pValueAppDomain != NULL) && (pValueAppDomain != pAppDomain)) |
9760 | { |
9761 | LOG((LF_CORDB,LL_INFO1000, "CordbEval::DADM - AD mismatch. appDomain=0x%08x, param #%d=0x%08x, must fail.\n" , |
9762 | pAppDomain, i, pValueAppDomain)); |
9763 | return FALSE; |
9764 | } |
9765 | } |
9766 | |
9767 | for(unsigned int i = 0; i < nTypes; i++ ) |
9768 | { |
9769 | CordbType* t = static_cast<CordbType*>( pTypes[i] ); |
9770 | CordbAppDomain * pTypeAppDomain = t->GetAppDomain(); |
9771 | |
9772 | if( pTypeAppDomain != NULL && pTypeAppDomain != pAppDomain ) |
9773 | { |
9774 | LOG((LF_CORDB,LL_INFO1000, "CordbEval::DADM - AD mismatch. appDomain=0x%08x, type param #%d=0x%08x, must fail.\n" , |
9775 | pAppDomain, i, pTypeAppDomain)); |
9776 | return FALSE; |
9777 | } |
9778 | } |
9779 | |
9780 | return TRUE; |
9781 | } |
9782 | |
9783 | HRESULT CordbEval::NewObject(ICorDebugFunction *pConstructor, |
9784 | ULONG32 nArgs, |
9785 | ICorDebugValue *pArgs[]) |
9786 | { |
9787 | PUBLIC_API_ENTRY(this); |
9788 | FAIL_IF_NEUTERED(this); |
9789 | return NewParameterizedObject(pConstructor,0,NULL,nArgs,pArgs); |
9790 | } |
9791 | |
9792 | //--------------------------------------------------------------------------------------- |
9793 | // |
9794 | // This routine calls a constructor with the given set of type arguments and actual arguments. |
9795 | // This is the jumping off point for func-evaling "new". |
9796 | // |
9797 | // Arguments: |
9798 | // pConstructor - The function to call. |
9799 | // nTypeArgs - The number of type-arguments for the method in rgpTypeArgs |
9800 | // rgpTypeArgs - An array of pointers to types. |
9801 | // nArgs - The number of arguments for the method in rgpArgs |
9802 | // rgpArgs - An array of pointers to values for the arguments to the method. |
9803 | // |
9804 | // Return Value: |
9805 | // HRESULT for the operation |
9806 | // |
9807 | HRESULT CordbEval::NewParameterizedObject(ICorDebugFunction * pConstructor, |
9808 | ULONG32 nTypeArgs, |
9809 | ICorDebugType * rgpTypeArgs[], |
9810 | ULONG32 nArgs, |
9811 | ICorDebugValue * rgpArgs[]) |
9812 | { |
9813 | PUBLIC_REENTRANT_API_ENTRY(this); |
9814 | FAIL_IF_NEUTERED(this); |
9815 | VALIDATE_POINTER_TO_OBJECT(pConstructor, ICorDebugFunction *); |
9816 | VALIDATE_POINTER_TO_OBJECT_ARRAY(rgpArgs, ICorDebugValue *, nArgs, true, true); |
9817 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
9818 | |
9819 | // The LS will assume that all of the ICorDebugValues and ICorDebugTypes are in |
9820 | // the same appdomain as the constructor. Verify this. |
9821 | CordbAppDomain * pConstructorAppDomain = (static_cast<CordbFunction *> (pConstructor))->GetAppDomain(); |
9822 | |
9823 | if (!DoAppDomainsMatch(pConstructorAppDomain, nTypeArgs, rgpTypeArgs, nArgs, rgpArgs)) |
9824 | { |
9825 | return ErrWrapper(CORDBG_E_APPDOMAIN_MISMATCH); |
9826 | } |
9827 | |
9828 | // Callers are free to reuse an ICorDebugEval object for multiple |
9829 | // evals. Since we create a Left Side eval representation each |
9830 | // time, we need to be sure to clean it up now that we know we're |
9831 | // done with it. |
9832 | HRESULT hr = SendCleanup(); |
9833 | |
9834 | if (FAILED(hr)) |
9835 | { |
9836 | return hr; |
9837 | } |
9838 | |
9839 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
9840 | RsPtrHolder<CordbEval> hFuncEval(this); |
9841 | |
9842 | if (hFuncEval.Ptr().IsNull()) |
9843 | { |
9844 | return E_OUTOFMEMORY; |
9845 | } |
9846 | lockHolder.Release(); |
9847 | |
9848 | // Remember the function that we're evaluating. |
9849 | m_function = static_cast<CordbFunction *>(pConstructor); |
9850 | m_evalType = DB_IPCE_FET_NEW_OBJECT; |
9851 | |
9852 | // Arrange the arguments into a form that the left side can deal |
9853 | // with. We do this before starting the func eval setup to ensure |
9854 | // that we can complete this step before mutating up the left |
9855 | // side. |
9856 | DebuggerIPCE_FuncEvalArgData * pArgData = NULL; |
9857 | |
9858 | if (nArgs > 0) |
9859 | { |
9860 | // We need to make the same type of array that the left side |
9861 | // holds. |
9862 | pArgData = new (nothrow) DebuggerIPCE_FuncEvalArgData[nArgs]; |
9863 | |
9864 | if (pArgData == NULL) |
9865 | { |
9866 | return E_OUTOFMEMORY; |
9867 | } |
9868 | |
9869 | // For each argument, convert its home into something the left |
9870 | // side can understand. |
9871 | for (unsigned int i = 0; i < nArgs; i++) |
9872 | { |
9873 | hr = GatherArgInfo(rgpArgs[i], &(pArgData[i])); |
9874 | |
9875 | if (FAILED(hr)) |
9876 | { |
9877 | return hr; |
9878 | } |
9879 | } |
9880 | } |
9881 | |
9882 | // Send over to the left side and get it to setup this eval. |
9883 | DebuggerIPCEvent event; |
9884 | |
9885 | m_thread->GetProcess()->InitIPCEvent(&event, DB_IPCE_FUNC_EVAL, true, m_thread->GetAppDomain()->GetADToken()); |
9886 | |
9887 | event.FuncEval.vmThreadToken = m_thread->m_vmThreadToken; |
9888 | event.FuncEval.funcEvalType = m_evalType; |
9889 | event.FuncEval.funcMetadataToken = m_function->GetMetadataToken(); |
9890 | event.FuncEval.vmDomainFile = m_function->GetModule()->GetRuntimeDomainFile(); |
9891 | event.FuncEval.funcEvalKey = hFuncEval.Ptr(); |
9892 | event.FuncEval.argCount = nArgs; |
9893 | event.FuncEval.genericArgsCount = nTypeArgs; |
9894 | |
9895 | hr = SendFuncEval(nTypeArgs, |
9896 | rgpTypeArgs, |
9897 | reinterpret_cast<void *>(pArgData), |
9898 | sizeof(DebuggerIPCE_FuncEvalArgData) * nArgs, |
9899 | NULL, |
9900 | 0, |
9901 | &event); |
9902 | |
9903 | // Cleanup |
9904 | |
9905 | if (pArgData) |
9906 | { |
9907 | delete [] pArgData; |
9908 | } |
9909 | |
9910 | if (SUCCEEDED(hr)) |
9911 | { |
9912 | hFuncEval.SuppressRelease(); // Now LS owns. |
9913 | } |
9914 | |
9915 | |
9916 | // Return any failure the Left Side may have told us about. |
9917 | return hr; |
9918 | } |
9919 | |
9920 | HRESULT CordbEval::NewObjectNoConstructor(ICorDebugClass *pClass) |
9921 | { |
9922 | PUBLIC_API_ENTRY(this); |
9923 | FAIL_IF_NEUTERED(this); |
9924 | return NewParameterizedObjectNoConstructor(pClass,0,NULL); |
9925 | } |
9926 | |
9927 | //--------------------------------------------------------------------------------------- |
9928 | // |
9929 | // This routine creates an object of a certain type, but does not call the constructor |
9930 | // for the type on the object. |
9931 | // |
9932 | // Arguments: |
9933 | // pClass - the type of the object to create. |
9934 | // nTypeArgs - The number of type-arguments for the method in rgpTypeArgs |
9935 | // rgpTypeArgs - An array of pointers to types. |
9936 | // |
9937 | // Return Value: |
9938 | // HRESULT for the operation |
9939 | // |
9940 | HRESULT CordbEval::NewParameterizedObjectNoConstructor(ICorDebugClass * pClass, |
9941 | ULONG32 nTypeArgs, |
9942 | ICorDebugType * rgpTypeArgs[]) |
9943 | { |
9944 | PUBLIC_REENTRANT_API_ENTRY(this); |
9945 | FAIL_IF_NEUTERED(this); |
9946 | VALIDATE_POINTER_TO_OBJECT(pClass, ICorDebugClass *); |
9947 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
9948 | |
9949 | // The LS will assume that all of the ICorDebugTypes are in |
9950 | // the same appdomain as the class. Verify this. |
9951 | CordbAppDomain * pClassAppDomain = (static_cast<CordbClass *> (pClass))->GetAppDomain(); |
9952 | |
9953 | if (!DoAppDomainsMatch(pClassAppDomain, nTypeArgs, rgpTypeArgs, 0, NULL)) |
9954 | { |
9955 | return ErrWrapper(CORDBG_E_APPDOMAIN_MISMATCH); |
9956 | } |
9957 | |
9958 | // Callers are free to reuse an ICorDebugEval object for multiple |
9959 | // evals. Since we create a Left Side eval representation each |
9960 | // time, we need to be sure to clean it up now that we know we're |
9961 | // done with it. |
9962 | HRESULT hr = SendCleanup(); |
9963 | |
9964 | if (FAILED(hr)) |
9965 | { |
9966 | return hr; |
9967 | } |
9968 | |
9969 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
9970 | RsPtrHolder<CordbEval> hFuncEval(this); |
9971 | lockHolder.Release(); // release to send an IPC event. |
9972 | |
9973 | if (hFuncEval.Ptr().IsNull()) |
9974 | { |
9975 | return E_OUTOFMEMORY; |
9976 | } |
9977 | |
9978 | // Remember the function that we're evaluating. |
9979 | m_class = (CordbClass*)pClass; |
9980 | m_evalType = DB_IPCE_FET_NEW_OBJECT_NC; |
9981 | |
9982 | // Send over to the left side and get it to setup this eval. |
9983 | DebuggerIPCEvent event; |
9984 | |
9985 | m_thread->GetProcess()->InitIPCEvent(&event, DB_IPCE_FUNC_EVAL, true, m_thread->GetAppDomain()->GetADToken()); |
9986 | |
9987 | event.FuncEval.vmThreadToken = m_thread->m_vmThreadToken; |
9988 | event.FuncEval.funcEvalType = m_evalType; |
9989 | event.FuncEval.funcMetadataToken = mdMethodDefNil; |
9990 | event.FuncEval.funcClassMetadataToken = (mdTypeDef)m_class->m_id; |
9991 | event.FuncEval.vmDomainFile = m_class->GetModule()->GetRuntimeDomainFile(); |
9992 | event.FuncEval.funcEvalKey = hFuncEval.Ptr(); |
9993 | event.FuncEval.argCount = 0; |
9994 | event.FuncEval.genericArgsCount = nTypeArgs; |
9995 | |
9996 | hr = SendFuncEval(nTypeArgs, rgpTypeArgs, NULL, 0, NULL, 0, &event); |
9997 | |
9998 | if (SUCCEEDED(hr)) |
9999 | { |
10000 | hFuncEval.SuppressRelease(); // Now LS owns. |
10001 | } |
10002 | |
10003 | // Return any failure the Left Side may have told us about. |
10004 | return hr; |
10005 | } |
10006 | |
10007 | /* |
10008 | * |
10009 | * NewString |
10010 | * |
10011 | * This routine is the interface function for ICorDebugEval::NewString |
10012 | * |
10013 | * Parameters: |
10014 | * string - the string to create - must be null-terminated |
10015 | * |
10016 | * Return Value: |
10017 | * HRESULT from the helper routines on RS and LS. |
10018 | * |
10019 | */ |
10020 | HRESULT CordbEval::NewString(LPCWSTR string) |
10021 | { |
10022 | PUBLIC_API_ENTRY(this); |
10023 | FAIL_IF_NEUTERED(this); |
10024 | return NewStringWithLength(string, (UINT)wcslen(string)); |
10025 | } |
10026 | |
10027 | //--------------------------------------------------------------------------------------- |
10028 | // |
10029 | // This routine is the interface function for ICorDebugEval::NewStringWithLength. |
10030 | // |
10031 | // Arguments: |
10032 | // wszString - the string to create |
10033 | // iLength - the number of characters that you want to create. Can include embedded nulls. |
10034 | // |
10035 | // Return Value: |
10036 | // HRESULT for the operation |
10037 | // |
10038 | HRESULT CordbEval::NewStringWithLength(LPCWSTR wszString, UINT iLength) |
10039 | { |
10040 | PUBLIC_REENTRANT_API_ENTRY(this); |
10041 | FAIL_IF_NEUTERED(this); |
10042 | VALIDATE_POINTER_TO_OBJECT(wszString, LPCWSTR); // Gotta have a string... |
10043 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
10044 | |
10045 | // Callers are free to reuse an ICorDebugEval object for multiple |
10046 | // evals. Since we create a Left Side eval representation each |
10047 | // time, we need to be sure to clean it up now that we know we're |
10048 | // done with it. |
10049 | HRESULT hr = SendCleanup(); |
10050 | |
10051 | if (FAILED(hr)) |
10052 | { |
10053 | return hr; |
10054 | } |
10055 | |
10056 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
10057 | RsPtrHolder<CordbEval> hFuncEval(this); |
10058 | lockHolder.Release(); // release to send an IPC event. |
10059 | |
10060 | if (hFuncEval.Ptr().IsNull()) |
10061 | { |
10062 | return E_OUTOFMEMORY; |
10063 | } |
10064 | |
10065 | |
10066 | // Length of the string? Don't account for null as COMString::NewString is length-based |
10067 | SIZE_T cbString = iLength * sizeof(WCHAR); |
10068 | |
10069 | // Remember that we're doing a func eval for a new string. |
10070 | m_function = NULL; |
10071 | m_evalType = DB_IPCE_FET_NEW_STRING; |
10072 | |
10073 | // Send over to the left side and get it to setup this eval. |
10074 | DebuggerIPCEvent event; |
10075 | |
10076 | m_thread->GetProcess()->InitIPCEvent(&event, DB_IPCE_FUNC_EVAL, true, m_thread->GetAppDomain()->GetADToken()); |
10077 | |
10078 | event.FuncEval.vmThreadToken = m_thread->m_vmThreadToken; |
10079 | event.FuncEval.funcEvalType = m_evalType; |
10080 | event.FuncEval.funcEvalKey = hFuncEval.Ptr(); |
10081 | event.FuncEval.stringSize = cbString; |
10082 | |
10083 | // Note: no function or module here... |
10084 | event.FuncEval.funcMetadataToken = mdMethodDefNil; |
10085 | event.FuncEval.funcClassMetadataToken = mdTypeDefNil; |
10086 | event.FuncEval.vmDomainFile = VMPTR_DomainFile::NullPtr(); |
10087 | event.FuncEval.argCount = 0; |
10088 | event.FuncEval.genericArgsCount = 0; |
10089 | event.FuncEval.genericArgsNodeCount = 0; |
10090 | |
10091 | hr = SendFuncEval(0, NULL, (void *)wszString, (unsigned int)cbString, NULL, 0, &event); |
10092 | |
10093 | if (SUCCEEDED(hr)) |
10094 | { |
10095 | hFuncEval.SuppressRelease(); // Now LS owns. |
10096 | } |
10097 | |
10098 | // Return any failure the Left Side may have told us about. |
10099 | return hr; |
10100 | } |
10101 | |
10102 | HRESULT CordbEval::NewArray(CorElementType elementType, |
10103 | ICorDebugClass *pElementClass, |
10104 | ULONG32 rank, |
10105 | ULONG32 dims[], |
10106 | ULONG32 lowBounds[]) |
10107 | { |
10108 | PUBLIC_API_ENTRY(this); |
10109 | FAIL_IF_NEUTERED(this); |
10110 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pElementClass, ICorDebugClass *); |
10111 | |
10112 | // If you want a class, you gotta pass a class. |
10113 | if ((elementType == ELEMENT_TYPE_CLASS) && (pElementClass == NULL)) |
10114 | return E_INVALIDARG; |
10115 | |
10116 | // If you want an array of objects, then why pass a class? |
10117 | if ((elementType == ELEMENT_TYPE_OBJECT) && (pElementClass != NULL)) |
10118 | return E_INVALIDARG; |
10119 | |
10120 | // Arg check... |
10121 | if (elementType == ELEMENT_TYPE_VOID) |
10122 | return E_INVALIDARG; |
10123 | |
10124 | CordbType *typ; |
10125 | HRESULT hr = S_OK; |
10126 | hr = CordbType::MkUnparameterizedType(m_thread->GetAppDomain(), elementType, (CordbClass *) pElementClass, &typ); |
10127 | |
10128 | if (FAILED(hr)) |
10129 | return hr; |
10130 | |
10131 | return NewParameterizedArray(typ, rank,dims,lowBounds); |
10132 | |
10133 | } |
10134 | |
10135 | |
10136 | //--------------------------------------------------------------------------------------- |
10137 | // |
10138 | // This routine sets up a func-eval to create a new array of the given type. |
10139 | // |
10140 | // Arguments: |
10141 | // pElementType - The type of each element of the array. |
10142 | // rank - Rank of the array. |
10143 | // rgDimensions - Array of dimensions for the array. |
10144 | // rmLowBounds - Array of lower bounds on the array. |
10145 | // |
10146 | // Return Value: |
10147 | // HRESULT for the operation |
10148 | // |
10149 | HRESULT CordbEval::NewParameterizedArray(ICorDebugType * pElementType, |
10150 | ULONG32 rank, |
10151 | ULONG32 rgDimensions[], |
10152 | ULONG32 rgLowBounds[]) |
10153 | { |
10154 | PUBLIC_REENTRANT_API_ENTRY(this); |
10155 | FAIL_IF_NEUTERED(this); |
10156 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pElementType, ICorDebugType *); |
10157 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
10158 | |
10159 | |
10160 | // Callers are free to reuse an ICorDebugEval object for multiple evals. Since we create a Left Side eval |
10161 | // representation each time, we need to be sure to clean it up now that we know we're done with it. |
10162 | HRESULT hr = SendCleanup(); |
10163 | |
10164 | if (FAILED(hr)) |
10165 | { |
10166 | return hr; |
10167 | } |
10168 | |
10169 | // Arg check... |
10170 | if ((rank == 0) || (rgDimensions == NULL)) |
10171 | { |
10172 | return E_INVALIDARG; |
10173 | } |
10174 | |
10175 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
10176 | RsPtrHolder<CordbEval> hFuncEval(this); |
10177 | lockHolder.Release(); // release to send an IPC event. |
10178 | |
10179 | if (hFuncEval.Ptr().IsNull()) |
10180 | { |
10181 | return E_OUTOFMEMORY; |
10182 | } |
10183 | |
10184 | |
10185 | // Remember that we're doing a func eval for a new string. |
10186 | m_function = NULL; |
10187 | m_evalType = DB_IPCE_FET_NEW_ARRAY; |
10188 | |
10189 | // Send over to the left side and get it to setup this eval. |
10190 | DebuggerIPCEvent event; |
10191 | |
10192 | m_thread->GetProcess()->InitIPCEvent(&event, DB_IPCE_FUNC_EVAL, true, m_thread->GetAppDomain()->GetADToken()); |
10193 | |
10194 | event.FuncEval.vmThreadToken = m_thread->m_vmThreadToken; |
10195 | event.FuncEval.funcEvalType = m_evalType; |
10196 | event.FuncEval.funcEvalKey = hFuncEval.Ptr(); |
10197 | |
10198 | event.FuncEval.arrayRank = rank; |
10199 | |
10200 | // Note: no function or module here... |
10201 | event.FuncEval.funcMetadataToken = mdMethodDefNil; |
10202 | event.FuncEval.funcClassMetadataToken = mdTypeDefNil; |
10203 | event.FuncEval.vmDomainFile = VMPTR_DomainFile::NullPtr(); |
10204 | event.FuncEval.argCount = 0; |
10205 | event.FuncEval.genericArgsCount = 1; |
10206 | |
10207 | // Prefast overflow sanity check. |
10208 | S_UINT32 allocSize = S_UINT32(rank) * S_UINT32(sizeof(SIZE_T)); |
10209 | |
10210 | if (allocSize.IsOverflow()) |
10211 | { |
10212 | return E_INVALIDARG; |
10213 | } |
10214 | |
10215 | // Just in case sizeof(SIZE_T) != sizeof(ULONG32) |
10216 | SIZE_T * rgDimensionsSizeT = reinterpret_cast<SIZE_T *>(_alloca(allocSize.Value())); |
10217 | |
10218 | for (unsigned int i = 0; i < rank; i++) |
10219 | { |
10220 | rgDimensionsSizeT[i] = rgDimensions[i]; |
10221 | } |
10222 | |
10223 | ICorDebugType * rgpGenericArgs[1]; |
10224 | |
10225 | rgpGenericArgs[0] = pElementType; |
10226 | |
10227 | // @dbgtodo funceval : lower bounds were ignored in V1 - fix this. |
10228 | hr = SendFuncEval(1, |
10229 | rgpGenericArgs, |
10230 | reinterpret_cast<void *>(rgDimensionsSizeT), |
10231 | rank * sizeof(SIZE_T), |
10232 | NULL, // (void*)lowBounds, |
10233 | 0, // ((lowBounds == NULL) ? 0 : rank * sizeof(SIZE_T)), |
10234 | &event); |
10235 | |
10236 | if (SUCCEEDED(hr)) |
10237 | { |
10238 | hFuncEval.SuppressRelease(); // Now LS owns. |
10239 | } |
10240 | |
10241 | // Return any failure the Left Side may have told us about. |
10242 | return hr; |
10243 | } |
10244 | |
10245 | HRESULT CordbEval::IsActive(BOOL *pbActive) |
10246 | { |
10247 | PUBLIC_API_ENTRY(this); |
10248 | FAIL_IF_NEUTERED(this); |
10249 | VALIDATE_POINTER_TO_OBJECT(pbActive, BOOL *); |
10250 | |
10251 | *pbActive = (m_complete == true); |
10252 | return S_OK; |
10253 | } |
10254 | |
10255 | /* |
10256 | * This routine submits an abort request to the LS. |
10257 | * |
10258 | * Parameters: |
10259 | * None. |
10260 | * |
10261 | * Returns: |
10262 | * The HRESULT as returned by the LS. |
10263 | * |
10264 | */ |
10265 | |
10266 | HRESULT |
10267 | CordbEval::Abort( |
10268 | void |
10269 | ) |
10270 | { |
10271 | PUBLIC_API_ENTRY(this); |
10272 | FAIL_IF_NEUTERED(this); |
10273 | |
10274 | ATT_ALLOW_LIVE_DO_STOPGO(GetProcess()); |
10275 | |
10276 | |
10277 | // |
10278 | // No need to abort if its already completed. |
10279 | // |
10280 | if (m_complete) |
10281 | { |
10282 | return S_OK; |
10283 | } |
10284 | |
10285 | |
10286 | // |
10287 | // Can't abort if its never even been started. |
10288 | // |
10289 | if (m_debuggerEvalKey == NULL) |
10290 | { |
10291 | return E_INVALIDARG; |
10292 | } |
10293 | |
10294 | CORDBRequireProcessStateOK(m_thread->GetProcess()); |
10295 | |
10296 | // |
10297 | // Send over to the left side to get the eval aborted. |
10298 | // |
10299 | DebuggerIPCEvent event; |
10300 | |
10301 | m_thread->GetProcess()->InitIPCEvent(&event, |
10302 | DB_IPCE_FUNC_EVAL_ABORT, |
10303 | true, |
10304 | m_thread->GetAppDomain()->GetADToken() |
10305 | ); |
10306 | |
10307 | event.FuncEvalAbort.debuggerEvalKey = m_debuggerEvalKey; |
10308 | |
10309 | HRESULT hr = m_thread->GetProcess()->SendIPCEvent(&event, |
10310 | sizeof(DebuggerIPCEvent) |
10311 | ); |
10312 | |
10313 | |
10314 | // |
10315 | // If the send failed, return that failure. |
10316 | // |
10317 | if (FAILED(hr)) |
10318 | { |
10319 | return hr; |
10320 | } |
10321 | |
10322 | _ASSERTE(event.type == DB_IPCE_FUNC_EVAL_ABORT_RESULT); |
10323 | |
10324 | // |
10325 | // Since we may have |
10326 | // overwritten anything (objects, code, etc), we should mark |
10327 | // everything as needing to be re-cached. |
10328 | // |
10329 | m_thread->GetProcess()->m_continueCounter++; |
10330 | |
10331 | hr = event.hr; |
10332 | |
10333 | return hr; |
10334 | } |
10335 | |
10336 | HRESULT CordbEval::GetResult(ICorDebugValue **ppResult) |
10337 | { |
10338 | PUBLIC_API_ENTRY(this); |
10339 | FAIL_IF_NEUTERED(this); |
10340 | VALIDATE_POINTER_TO_OBJECT(ppResult, ICorDebugValue **); |
10341 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
10342 | |
10343 | *ppResult = NULL; |
10344 | |
10345 | // Is the evaluation complete? |
10346 | if (!m_complete) |
10347 | { |
10348 | return CORDBG_E_FUNC_EVAL_NOT_COMPLETE; |
10349 | } |
10350 | |
10351 | if (m_aborted) |
10352 | { |
10353 | return CORDBG_S_FUNC_EVAL_ABORTED; |
10354 | } |
10355 | |
10356 | // Does the evaluation have a result? |
10357 | if (m_resultType.elementType == ELEMENT_TYPE_VOID) |
10358 | { |
10359 | return CORDBG_S_FUNC_EVAL_HAS_NO_RESULT; |
10360 | } |
10361 | |
10362 | HRESULT hr = S_OK; |
10363 | EX_TRY |
10364 | { |
10365 | // Make a ICorDebugValue out of the result. |
10366 | CordbAppDomain * pAppDomain; |
10367 | |
10368 | if (!m_resultAppDomainToken.IsNull()) |
10369 | { |
10370 | // @dbgtodo funceval - push this up |
10371 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
10372 | |
10373 | pAppDomain = m_thread->GetProcess()->LookupOrCreateAppDomain(m_resultAppDomainToken); |
10374 | } |
10375 | else |
10376 | { |
10377 | pAppDomain = m_thread->GetAppDomain(); |
10378 | } |
10379 | PREFIX_ASSUME(pAppDomain != NULL); |
10380 | |
10381 | CordbType * pType = NULL; |
10382 | hr = CordbType::TypeDataToType(pAppDomain, &m_resultType, &pType); |
10383 | IfFailThrow(hr); |
10384 | |
10385 | bool resultInHandle = |
10386 | ((m_resultType.elementType == ELEMENT_TYPE_CLASS) || |
10387 | (m_resultType.elementType == ELEMENT_TYPE_SZARRAY) || |
10388 | (m_resultType.elementType == ELEMENT_TYPE_OBJECT) || |
10389 | (m_resultType.elementType == ELEMENT_TYPE_ARRAY) || |
10390 | (m_resultType.elementType == ELEMENT_TYPE_STRING)); |
10391 | |
10392 | if (resultInHandle) |
10393 | { |
10394 | // if object handle is null here, something has gone wrong!!! |
10395 | _ASSERTE(!m_vmObjectHandle.IsNull()); |
10396 | |
10397 | if (m_pHandleValue == NULL) |
10398 | { |
10399 | // Create CordbHandleValue for result |
10400 | RSInitHolder<CordbHandleValue> pHandleValue(new CordbHandleValue(pAppDomain, pType, HANDLE_STRONG)); |
10401 | |
10402 | // Initialize the handle value object. The HandleValue will now |
10403 | // own the m_objectHandle. |
10404 | hr = pHandleValue->Init(m_vmObjectHandle); |
10405 | |
10406 | if (!SUCCEEDED(hr)) |
10407 | { |
10408 | // Neuter the new object we've been working on. This will |
10409 | // call Dispose(), and that will go back to the left side |
10410 | // and free the handle that we got above. |
10411 | pHandleValue->NeuterLeftSideResources(); |
10412 | |
10413 | // |
10414 | |
10415 | // Do not delete chv here. The neuter list still has a reference to it, and it will be cleaned up automatically. |
10416 | ThrowHR(hr); |
10417 | } |
10418 | m_pHandleValue.Assign(pHandleValue); |
10419 | pHandleValue.ClearAndMarkDontNeuter(); |
10420 | } |
10421 | |
10422 | // This AddRef is for caller to release |
10423 | // |
10424 | *ppResult = m_pHandleValue; |
10425 | m_pHandleValue->ExternalAddRef(); |
10426 | } |
10427 | else if (CorIsPrimitiveType(m_resultType.elementType) && (m_resultType.elementType != ELEMENT_TYPE_STRING)) |
10428 | { |
10429 | // create a CordbGenericValue flagged as a literal |
10430 | hr = CordbEval::CreatePrimitiveLiteral(pType, ppResult); |
10431 | } |
10432 | else |
10433 | { |
10434 | TargetBuffer remoteValue(m_resultAddr, CordbValue::GetSizeForType(pType, kBoxed)); |
10435 | // Now that we have the module, go ahead and create the result. |
10436 | |
10437 | CordbValue::CreateValueByType(pAppDomain, |
10438 | pType, |
10439 | true, |
10440 | remoteValue, |
10441 | MemoryRange(NULL, 0), |
10442 | NULL, |
10443 | ppResult); // throws |
10444 | } |
10445 | |
10446 | } |
10447 | EX_CATCH_HRESULT(hr); |
10448 | return hr; |
10449 | } |
10450 | |
10451 | HRESULT CordbEval::GetThread(ICorDebugThread **ppThread) |
10452 | { |
10453 | PUBLIC_API_ENTRY(this); |
10454 | FAIL_IF_NEUTERED(this); |
10455 | VALIDATE_POINTER_TO_OBJECT(ppThread, ICorDebugThread **); |
10456 | |
10457 | *ppThread = static_cast<ICorDebugThread*> (m_thread); |
10458 | m_thread->ExternalAddRef(); |
10459 | |
10460 | return S_OK; |
10461 | } |
10462 | |
10463 | // Create a RS literal for primitive type funceval result. In case the result is used as an argument for |
10464 | // another funceval, we need to make sure that we're not relying on the LS value, which will be freed and |
10465 | // thus unavailable. |
10466 | // Arguments: |
10467 | // input: pType - CordbType instance representing the type of the primitive value |
10468 | // output: ppValue - ICorDebugValue representing the result as a literal CordbGenericValue |
10469 | // Return Value: |
10470 | // hr: may fail for OOM, ReadProcessMemory failures |
10471 | HRESULT CordbEval::CreatePrimitiveLiteral(CordbType * pType, |
10472 | ICorDebugValue ** ppValue) |
10473 | { |
10474 | CordbGenericValue * gv = NULL; |
10475 | HRESULT hr = S_OK; |
10476 | EX_TRY |
10477 | { |
10478 | // Create a generic value. |
10479 | gv = new CordbGenericValue(pType); |
10480 | |
10481 | // initialize the local value |
10482 | int size = CordbValue::GetSizeForType(pType, kBoxed); |
10483 | if (size > 8) |
10484 | { |
10485 | ThrowHR(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); |
10486 | } |
10487 | TargetBuffer remoteValue(m_resultAddr, size); |
10488 | BYTE localBuffer[8] = {0}; |
10489 | |
10490 | GetProcess()->SafeReadBuffer (remoteValue, localBuffer); |
10491 | gv->SetValue(localBuffer); |
10492 | |
10493 | // Do not delete gv here even if the initialization fails. |
10494 | // The neuter list still has a reference to it, and it will be cleaned up automatically. |
10495 | gv->ExternalAddRef(); |
10496 | *ppValue = (ICorDebugValue*)(ICorDebugGenericValue*)gv; |
10497 | } |
10498 | EX_CATCH_HRESULT(hr); |
10499 | return hr; |
10500 | } |
10501 | |
10502 | HRESULT CordbEval::CreateValue(CorElementType elementType, |
10503 | ICorDebugClass *pElementClass, |
10504 | ICorDebugValue **ppValue) |
10505 | { |
10506 | PUBLIC_API_ENTRY(this); |
10507 | FAIL_IF_NEUTERED(this); |
10508 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
10509 | |
10510 | CordbType *typ; |
10511 | |
10512 | // @todo: only primitive values right now. |
10513 | if (((elementType < ELEMENT_TYPE_BOOLEAN) || |
10514 | (elementType > ELEMENT_TYPE_R8)) && |
10515 | !(elementType == ELEMENT_TYPE_CLASS)) |
10516 | return E_INVALIDARG; |
10517 | |
10518 | HRESULT hr = S_OK; |
10519 | |
10520 | // MkUnparameterizedType now works if you give it ELEMENT_TYPE_CLASS and |
10521 | // a null pElementClass - it returns the type for ELEMENT_TYPE_OBJECT. |
10522 | |
10523 | hr = CordbType::MkUnparameterizedType(m_thread->GetAppDomain(), elementType, (CordbClass *) pElementClass, &typ); |
10524 | |
10525 | if (FAILED(hr)) |
10526 | return hr; |
10527 | |
10528 | return CreateValueForType(typ, ppValue); |
10529 | } |
10530 | |
10531 | // create an ICDValue to represent a value for a funceval |
10532 | // Arguments: |
10533 | // input: pIType - the type for the new value |
10534 | // output: ppValue - the new ICDValue. If there is a failure of some sort, this will be NULL |
10535 | // ReturnValue: S_OK on success (ppValue should contain a non-NULL address) |
10536 | // E_OUTOFMEMORY, if we can't allocate space for the new ICDValue |
10537 | // Notes: We can also get read process memory errors or E_INVALIDARG if errors occur during initialization, |
10538 | // but in that case, we don't return the hresult. Instead, we just never update ppValue, so it will still be |
10539 | // NULL on exit. |
10540 | HRESULT CordbEval::CreateValueForType(ICorDebugType * pIType, |
10541 | ICorDebugValue ** ppValue) |
10542 | { |
10543 | HRESULT hr = S_OK; |
10544 | |
10545 | PUBLIC_REENTRANT_API_ENTRY(this); |
10546 | FAIL_IF_NEUTERED(this); |
10547 | ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); |
10548 | |
10549 | VALIDATE_POINTER_TO_OBJECT(ppValue, ICorDebugValue **); |
10550 | VALIDATE_POINTER_TO_OBJECT(pIType, ICorDebugType*); |
10551 | |
10552 | *ppValue = NULL; |
10553 | CordbType *pType = static_cast<CordbType *> (pIType); |
10554 | |
10555 | CorElementType elementType = pType->m_elementType; |
10556 | // We don't support IntPtr and UIntPtr types as arguments here, but we do support these types as results |
10557 | // (see code:CordbEval::CreatePrimitiveLiteral) and we have changed the LS to support them as well |
10558 | if (((elementType < ELEMENT_TYPE_BOOLEAN) || |
10559 | (elementType > ELEMENT_TYPE_R8)) && |
10560 | !((elementType == ELEMENT_TYPE_CLASS) || (elementType == ELEMENT_TYPE_OBJECT))) |
10561 | return E_INVALIDARG; |
10562 | |
10563 | // Note: ELEMENT_TYPE_OBJECT is what we'll get for the null reference case, so allow that. |
10564 | if ((elementType == ELEMENT_TYPE_CLASS) || (elementType == ELEMENT_TYPE_OBJECT)) |
10565 | { |
10566 | EX_TRY |
10567 | { |
10568 | // create a reference value |
10569 | CordbReferenceValue *rv = new CordbReferenceValue(pType); |
10570 | |
10571 | if (SUCCEEDED(rv->InitRef(MemoryRange(NULL,0)))) |
10572 | { |
10573 | // Do not delete rv here even if the initialization fails. |
10574 | // The neuter list still has a reference to it, and it will be cleaned up automatically. |
10575 | rv->ExternalAddRef(); |
10576 | *ppValue = (ICorDebugValue*)(ICorDebugReferenceValue*)rv; |
10577 | } |
10578 | } |
10579 | EX_CATCH_HRESULT(hr); |
10580 | } |
10581 | else |
10582 | { |
10583 | CordbGenericValue * gv = NULL; |
10584 | EX_TRY |
10585 | { |
10586 | // Create a generic value. |
10587 | gv = new CordbGenericValue(pType); |
10588 | |
10589 | gv->Init(MemoryRange(NULL,0)); |
10590 | // Do not delete gv here even if the initialization fails. |
10591 | // The neuter list still has a reference to it, and it will be cleaned up automatically. |
10592 | gv->ExternalAddRef(); |
10593 | *ppValue = (ICorDebugValue*)(ICorDebugGenericValue*)gv; |
10594 | } |
10595 | EX_CATCH_HRESULT(hr); |
10596 | } |
10597 | |
10598 | return hr; |
10599 | } // CordbEval::CreateValueForType |
10600 | |
10601 | |
10602 | /* ------------------------------------------------------------------------- * |
10603 | * CordbEval2 |
10604 | * |
10605 | * Extentions to the CordbEval class for Whidbey |
10606 | * |
10607 | * ------------------------------------------------------------------------- */ |
10608 | |
10609 | |
10610 | /* |
10611 | * This routine submits a rude abort request to the LS. |
10612 | * |
10613 | * Parameters: |
10614 | * None. |
10615 | * |
10616 | * Returns: |
10617 | * The HRESULT as returned by the LS. |
10618 | * |
10619 | */ |
10620 | |
10621 | HRESULT |
10622 | CordbEval::RudeAbort( |
10623 | void |
10624 | ) |
10625 | { |
10626 | PUBLIC_API_ENTRY(this); |
10627 | FAIL_IF_NEUTERED(this); |
10628 | |
10629 | |
10630 | ATT_ALLOW_LIVE_DO_STOPGO(GetProcess()); |
10631 | |
10632 | // |
10633 | // No need to abort if its already completed. |
10634 | // |
10635 | if (m_complete) |
10636 | { |
10637 | return S_OK; |
10638 | } |
10639 | |
10640 | // |
10641 | // Can't abort if its never even been started. |
10642 | // |
10643 | if (m_debuggerEvalKey == NULL) |
10644 | { |
10645 | return E_INVALIDARG; |
10646 | } |
10647 | |
10648 | CORDBRequireProcessStateOK(m_thread->GetProcess()); |
10649 | |
10650 | // |
10651 | // Send over to the left side to get the eval aborted. |
10652 | // |
10653 | DebuggerIPCEvent event; |
10654 | |
10655 | m_thread->GetProcess()->InitIPCEvent(&event, |
10656 | DB_IPCE_FUNC_EVAL_RUDE_ABORT, |
10657 | true, |
10658 | m_thread->GetAppDomain()->GetADToken() |
10659 | ); |
10660 | |
10661 | event.FuncEvalRudeAbort.debuggerEvalKey = m_debuggerEvalKey; |
10662 | |
10663 | HRESULT hr = m_thread->GetProcess()->SendIPCEvent(&event, |
10664 | sizeof(DebuggerIPCEvent) |
10665 | ); |
10666 | |
10667 | // |
10668 | // If the send failed, return that failure. |
10669 | // |
10670 | if (FAILED(hr)) |
10671 | { |
10672 | return hr; |
10673 | } |
10674 | |
10675 | _ASSERTE(event.type == DB_IPCE_FUNC_EVAL_RUDE_ABORT_RESULT); |
10676 | |
10677 | // |
10678 | // Since we may have |
10679 | // overwritten anything (objects, code, etc), we should mark |
10680 | // everything as needing to be re-cached. |
10681 | // |
10682 | m_thread->GetProcess()->m_continueCounter++; |
10683 | |
10684 | hr = event.hr; |
10685 | |
10686 | return hr; |
10687 | } |
10688 | |
10689 | |
10690 | |
10691 | |
10692 | /* ------------------------------------------------------------------------- * |
10693 | * CodeParameter Enumerator class |
10694 | * ------------------------------------------------------------------------- */ |
10695 | |
10696 | CordbCodeEnum::(unsigned int cCodes, RSSmartPtr<CordbCode> * ppCodes) : |
10697 | CordbBase(NULL, 0) |
10698 | { |
10699 | // Because the array is of smart-ptrs, the elements are already reffed |
10700 | // We now take ownership of the array itself too. |
10701 | m_ppCodes = ppCodes; |
10702 | |
10703 | m_iCurrent = 0; |
10704 | m_iMax = cCodes; |
10705 | } |
10706 | |
10707 | |
10708 | CordbCodeEnum::~CordbCodeEnum() |
10709 | { |
10710 | // This will invoke the SmartPtr dtors on each element and call release. |
10711 | delete [] m_ppCodes; |
10712 | } |
10713 | |
10714 | HRESULT CordbCodeEnum::QueryInterface(REFIID id, void **pInterface) |
10715 | { |
10716 | if (id == IID_ICorDebugEnum) |
10717 | *pInterface = static_cast<ICorDebugEnum*>(this); |
10718 | else if (id == IID_ICorDebugCodeEnum) |
10719 | *pInterface = static_cast<ICorDebugCodeEnum*>(this); |
10720 | else if (id == IID_IUnknown) |
10721 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugCodeEnum*>(this)); |
10722 | else |
10723 | { |
10724 | *pInterface = NULL; |
10725 | return E_NOINTERFACE; |
10726 | } |
10727 | |
10728 | ExternalAddRef(); |
10729 | return S_OK; |
10730 | } |
10731 | |
10732 | HRESULT CordbCodeEnum::Skip(ULONG celt) |
10733 | { |
10734 | HRESULT hr = E_FAIL; |
10735 | if ( (m_iCurrent+celt) < m_iMax || |
10736 | celt == 0) |
10737 | { |
10738 | m_iCurrent += celt; |
10739 | hr = S_OK; |
10740 | } |
10741 | |
10742 | return hr; |
10743 | } |
10744 | |
10745 | HRESULT CordbCodeEnum::Reset() |
10746 | { |
10747 | m_iCurrent = 0; |
10748 | return S_OK; |
10749 | } |
10750 | |
10751 | HRESULT CordbCodeEnum::Clone(ICorDebugEnum **ppEnum) |
10752 | { |
10753 | VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorDebugEnum **); |
10754 | (*ppEnum) = NULL; |
10755 | |
10756 | HRESULT hr = S_OK; |
10757 | |
10758 | // Create a new copy of the array because the CordbCodeEnum will |
10759 | // take ownership of it. |
10760 | RSSmartPtr<CordbCode> * ppCodes = new (nothrow) RSSmartPtr<CordbCode> [m_iMax]; |
10761 | if (ppCodes == NULL) |
10762 | { |
10763 | return E_OUTOFMEMORY; |
10764 | } |
10765 | for(UINT i = 0; i < m_iMax; i++) |
10766 | { |
10767 | ppCodes[i].Assign(m_ppCodes[i]); |
10768 | } |
10769 | |
10770 | |
10771 | CordbCodeEnum *pCVE = new (nothrow) CordbCodeEnum( m_iMax, ppCodes); |
10772 | if ( pCVE == NULL ) |
10773 | { |
10774 | delete [] ppCodes; |
10775 | hr = E_OUTOFMEMORY; |
10776 | goto LExit; |
10777 | } |
10778 | |
10779 | pCVE->ExternalAddRef(); |
10780 | (*ppEnum) = (ICorDebugEnum*)pCVE; |
10781 | |
10782 | LExit: |
10783 | return hr; |
10784 | } |
10785 | |
10786 | HRESULT CordbCodeEnum::GetCount(ULONG *pcelt) |
10787 | { |
10788 | VALIDATE_POINTER_TO_OBJECT(pcelt, ULONG *); |
10789 | |
10790 | if( pcelt == NULL) |
10791 | return E_INVALIDARG; |
10792 | |
10793 | (*pcelt) = m_iMax; |
10794 | return S_OK; |
10795 | } |
10796 | |
10797 | // |
10798 | // In the event of failure, the current pointer will be left at |
10799 | // one element past the troublesome element. Thus, if one were |
10800 | // to repeatedly ask for one element to iterate through the |
10801 | // array, you would iterate exactly m_iMax times, regardless |
10802 | // of individual failures. |
10803 | HRESULT CordbCodeEnum::Next(ULONG celt, ICorDebugCode *values[], ULONG *pceltFetched) |
10804 | { |
10805 | VALIDATE_POINTER_TO_OBJECT_ARRAY(values, ICorDebugClass *, |
10806 | celt, true, true); |
10807 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *); |
10808 | |
10809 | if ((pceltFetched == NULL) && (celt != 1)) |
10810 | { |
10811 | return E_INVALIDARG; |
10812 | } |
10813 | |
10814 | if (celt == 0) |
10815 | { |
10816 | if (pceltFetched != NULL) |
10817 | { |
10818 | *pceltFetched = 0; |
10819 | } |
10820 | return S_OK; |
10821 | } |
10822 | |
10823 | HRESULT hr = S_OK; |
10824 | |
10825 | int iMax = min( m_iMax, m_iCurrent+celt); |
10826 | int i; |
10827 | |
10828 | for (i = m_iCurrent; i < iMax; i++) |
10829 | { |
10830 | values[i-m_iCurrent] = m_ppCodes[i]; |
10831 | values[i-m_iCurrent]->AddRef(); |
10832 | } |
10833 | |
10834 | int count = (i - m_iCurrent); |
10835 | |
10836 | if ( FAILED( hr ) ) |
10837 | { //we failed: +1 pushes us past troublesome element |
10838 | m_iCurrent += 1 + count; |
10839 | } |
10840 | else |
10841 | { |
10842 | m_iCurrent += count; |
10843 | } |
10844 | |
10845 | if (pceltFetched != NULL) |
10846 | { |
10847 | *pceltFetched = count; |
10848 | } |
10849 | |
10850 | // |
10851 | // If we reached the end of the enumeration, but not the end |
10852 | // of the number of requested items, we return S_FALSE. |
10853 | // |
10854 | if (((ULONG)count) < celt) |
10855 | { |
10856 | return S_FALSE; |
10857 | } |
10858 | |
10859 | return hr; |
10860 | } |
10861 | |
10862 | |