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// DacDbiImplStackWalk.cpp
6//
7
8//
9// This file contains the implementation of the stackwalking-related functions on the DacDbiInterface.
10//
11// ======================================================================================
12
13#include "stdafx.h"
14#include "dacdbiinterface.h"
15#include "dacdbiimpl.h"
16#include "excepcpu.h"
17
18#if defined(FEATURE_COMINTEROP)
19#include "comtoclrcall.h"
20#include "comcallablewrapper.h"
21#endif // FEATURE_COMINTEROP
22
23typedef IDacDbiInterface::StackWalkHandle StackWalkHandle;
24
25
26// Persistent data needed to do a stackwalk. This is allocated on the forDbi heap.
27// It can survive across multiple DD calls.
28// However, it has data structures that have raw pointers into the DAC cache, and so it must
29// be re-iniatialized after each time the Dac cache is flushed.
30struct StackWalkData
31{
32public:
33 StackWalkData(Thread * pThread, Frame * pFrame, ULONG32 flags) :
34 m_iterator(pThread, NULL, flags)
35 { SUPPORTS_DAC;
36 }
37
38 // Unwrap a handle to get StackWalkData instance.
39 static StackWalkData * FromHandle(StackWalkHandle handle)
40 {
41 SUPPORTS_DAC;
42 _ASSERTE(handle != NULL);
43 return reinterpret_cast<StackWalkData *>(handle);
44 }
45
46 // The stackwalk iterator. This has lots of pointers into the DAC cache.
47 StackFrameIterator m_iterator;
48
49 // The context buffer, which can be pointed to by the RegDisplay.
50 T_CONTEXT m_context;
51
52 // A regdisplay used by the stackwalker.
53 REGDISPLAY m_regdisplay;
54};
55
56// Helper to allocate stackwalk datastructures for given parameters.
57// This is allocated on the local heap (and not via the forDbi allocator on the dac-cache), and then
58// freed via code:DacDbiInterfaceImpl::DeleteStackWalk
59//
60// Throws on error (mainly OOM).
61void AllocateStackwalk(StackWalkHandle * pHandle, Thread * pThread, Frame * pFrame, ULONG32 flags)
62{
63 SUPPORTS_DAC;
64
65 StackWalkData * p = new StackWalkData(pThread, NULL, flags); // throews
66
67 StackWalkHandle h = reinterpret_cast<StackWalkHandle>(p);
68 *pHandle = h;
69}
70void DeleteStackwalk(StackWalkHandle pHandle)
71{
72 SUPPORTS_DAC;
73
74 StackWalkData * pBuffer = (StackWalkData *) pHandle;
75 _ASSERTE(pBuffer != NULL);
76 delete pBuffer;
77}
78
79
80// Helper to get the StackFrameIterator from a Stackwalker handle
81StackFrameIterator * GetIteratorFromHandle(StackWalkHandle pSFIHandle)
82{
83 SUPPORTS_DAC;
84
85 StackWalkData * pBuffer = StackWalkData::FromHandle(pSFIHandle);
86 return &(pBuffer->m_iterator);
87}
88
89// Helper to get a RegDisplay from a Stackwalker handle
90REGDISPLAY * GetRegDisplayFromHandle(StackWalkHandle pSFIHandle)
91{
92 SUPPORTS_DAC;
93 StackWalkData * pBuffer = StackWalkData::FromHandle(pSFIHandle);
94 return &(pBuffer->m_regdisplay);
95}
96
97// Helper to get a Context buffer from a Stackwalker handle
98T_CONTEXT * GetContextBufferFromHandle(StackWalkHandle pSFIHandle)
99{
100 SUPPORTS_DAC;
101 StackWalkData * pBuffer = StackWalkData::FromHandle(pSFIHandle);
102 return &(pBuffer->m_context);
103}
104
105
106// Create and return a stackwalker on the specified thread.
107void DacDbiInterfaceImpl::CreateStackWalk(VMPTR_Thread vmThread,
108 DT_CONTEXT * pInternalContextBuffer,
109 StackWalkHandle * ppSFIHandle)
110{
111 DD_ENTER_MAY_THROW;
112
113 _ASSERTE(ppSFIHandle != NULL);
114
115 Thread * pThread = vmThread.GetDacPtr();
116
117 // Set the stackwalk flags. We pretty much want to stop at everything.
118 DWORD dwFlags = (NOTIFY_ON_U2M_TRANSITIONS |
119 NOTIFY_ON_NO_FRAME_TRANSITIONS |
120 NOTIFY_ON_INITIAL_NATIVE_CONTEXT);
121
122 // allocate memory for various stackwalker buffers (StackFrameIterator, RegDisplay, Context)
123 AllocateStackwalk(ppSFIHandle, pThread, NULL, dwFlags);
124
125 // initialize the the CONTEXT.
126 // SetStackWalk will initial the RegDisplay from this context.
127 GetContext(vmThread, pInternalContextBuffer);
128
129 // initialize the stackwalker
130 SetStackWalkCurrentContext(vmThread,
131 *ppSFIHandle,
132 SET_CONTEXT_FLAG_ACTIVE_FRAME,
133 pInternalContextBuffer);
134}
135
136// Delete the stackwalk object allocated by code:AllocateStackwalk
137void DacDbiInterfaceImpl::DeleteStackWalk(StackWalkHandle ppSFIHandle)
138{
139 DeleteStackwalk(ppSFIHandle);
140}
141
142// Get the CONTEXT of the current frame at which the stackwalker is stopped.
143void DacDbiInterfaceImpl::GetStackWalkCurrentContext(StackWalkHandle pSFIHandle,
144 DT_CONTEXT * pContext)
145{
146 DD_ENTER_MAY_THROW;
147
148 StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
149
150 GetStackWalkCurrentContext(pIter, pContext);
151}
152
153// Internal Worker for GetStackWalkCurrentContext().
154void DacDbiInterfaceImpl::GetStackWalkCurrentContext(StackFrameIterator * pIter,
155 DT_CONTEXT * pContext)
156{
157 // convert the current REGDISPLAY to a CONTEXT
158 CrawlFrame * pCF = &(pIter->m_crawl);
159 UpdateContextFromRegDisp(pCF->GetRegisterSet(), reinterpret_cast<T_CONTEXT *>(pContext));
160}
161
162
163
164// Set the stackwalker to the specified CONTEXT.
165void DacDbiInterfaceImpl::SetStackWalkCurrentContext(VMPTR_Thread vmThread,
166 StackWalkHandle pSFIHandle,
167 CorDebugSetContextFlag flag,
168 DT_CONTEXT * pContext)
169{
170 DD_ENTER_MAY_THROW;
171
172 StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
173 REGDISPLAY * pRD = GetRegDisplayFromHandle(pSFIHandle);
174
175#if defined(_DEBUG)
176 // The caller should have checked this already.
177 _ASSERTE(CheckContext(vmThread, pContext) == S_OK);
178#endif // _DEBUG
179
180 // DD can't keep pointers back into the RS address space.
181 // Allocate a context in DDImpl's memory space. DDImpl can't contain raw pointers back into
182 // the client space since that may not marshal.
183 T_CONTEXT * pContext2 = GetContextBufferFromHandle(pSFIHandle);
184 *pContext2 = *reinterpret_cast<T_CONTEXT *>(pContext); // memcpy
185
186 // update the REGDISPLAY with the given CONTEXT.
187 // Be sure that the context is in DDImpl's memory space and not the Right-sides.
188 FillRegDisplay(pRD, pContext2);
189 BOOL fSuccess = pIter->ResetRegDisp(pRD, (flag == SET_CONTEXT_FLAG_ACTIVE_FRAME));
190 if (!fSuccess)
191 {
192 // ResetRegDisp() may fail for the same reason Init() may fail, i.e.
193 // because the stackwalker tries to unwind one frame ahead of time,
194 // or because the stackwalker needs to filter out some frames based on the stackwalk flags.
195 ThrowHR(E_FAIL);
196 }
197}
198
199
200// Unwind the stackwalker to the next frame.
201BOOL DacDbiInterfaceImpl::UnwindStackWalkFrame(StackWalkHandle pSFIHandle)
202{
203 DD_ENTER_MAY_THROW;
204
205 StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
206
207 CrawlFrame * pCF = &(pIter->m_crawl);
208
209 if ((pIter->GetFrameState() == StackFrameIterator::SFITER_INITIAL_NATIVE_CONTEXT) ||
210 (pIter->GetFrameState() == StackFrameIterator::SFITER_NATIVE_MARKER_FRAME))
211 {
212 if (IsRuntimeUnwindableStub(GetControlPC(pCF->GetRegisterSet())))
213 {
214 // This is a native stack frame which the StackFrameIterator doesn't know how to unwind.
215 // Use our special unwind logic.
216 return UnwindRuntimeStackFrame(pIter);
217 }
218 }
219
220 // On x86, we need to adjust the stack pointer for the callee parameter adjustment.
221 // This requires us to save the number of bytes used for the stack parameters of the callee.
222 // Thus, let's save it here before we unwind.
223 DWORD cbStackParameterSize = 0;
224 if (pIter->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD)
225 {
226 cbStackParameterSize = GetStackParameterSize(pCF->GetCodeInfo());
227 }
228
229 // If the stackwalker is invalid to begin with, we'll just say that it is at the end of the stack.
230 BOOL fIsAtEndOfStack = TRUE;
231 while (pIter->IsValid())
232 {
233 StackWalkAction swa = pIter->Next();
234
235 if (swa == SWA_FAILED)
236 {
237 // The stackwalker is valid to begin with, so this must be a failure case.
238 ThrowHR(E_FAIL);
239 }
240 else if (swa == SWA_CONTINUE)
241 {
242 if (pIter->GetFrameState() == StackFrameIterator::SFITER_DONE)
243 {
244 // We are at the end of the stack. We will break at the end of the loop and fIsAtEndOfStack
245 // will be TRUE.
246 }
247 else if ((pIter->GetFrameState() == StackFrameIterator::SFITER_FRAME_FUNCTION) ||
248 (pIter->GetFrameState() == StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION))
249 {
250 // If the stackwalker is stopped at an explicit frame, unwind directly to the next frame.
251 // The V3 stackwalker doesn't stop on explicit frames.
252 continue;
253 }
254 else if (pIter->GetFrameState() == StackFrameIterator::SFITER_NO_FRAME_TRANSITION)
255 {
256 // No frame transitions are not exposed in V2.
257 // Just continue onto the next managed stack frame.
258 continue;
259 }
260 else
261 {
262 fIsAtEndOfStack = FALSE;
263 }
264 }
265 else
266 {
267 UNREACHABLE();
268 }
269
270 // If we get here, then we want to stop at this current frame.
271 break;
272 }
273
274 if (fIsAtEndOfStack == FALSE)
275 {
276 // Currently the only case where we adjust the stack pointer is at M2U transitions.
277 if (pIter->GetFrameState() == StackFrameIterator::SFITER_NATIVE_MARKER_FRAME)
278 {
279 _ASSERTE(!pCF->IsActiveFrame());
280 AdjustRegDisplayForStackParameter(pCF->GetRegisterSet(),
281 cbStackParameterSize,
282 pCF->IsActiveFrame(),
283 kFromManagedToUnmanaged);
284 }
285 }
286
287 return (fIsAtEndOfStack == FALSE);
288}
289
290bool g_fSkipStackCheck = false;
291bool g_fSkipStackCheckInit = false;
292
293// Check whether the specified CONTEXT is valid. The only check we perform right now is whether the
294// SP in the specified CONTEXT is in the stack range of the thread.
295HRESULT DacDbiInterfaceImpl::CheckContext(VMPTR_Thread vmThread,
296 const DT_CONTEXT * pContext)
297{
298 DD_ENTER_MAY_THROW;
299
300 // If the SP in the CONTEXT isn't valid, then there's no point in checking.
301 if ((pContext->ContextFlags & CONTEXT_CONTROL) == 0)
302 {
303 return S_OK;
304 }
305
306 if (!g_fSkipStackCheckInit)
307 {
308 g_fSkipStackCheck = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DbgSkipStackCheck) != 0);
309 g_fSkipStackCheckInit = true;
310 }
311
312 // Skip this check if the customer has set the reg key/env var. This is necessary for AutoCad. They
313 // enable fiber mode by calling the Win32 API ConvertThreadToFiber(), but when a managed debugger is
314 // attached, they don't actually call into our hosting APIs such as SwitchInLogicalThreadState(). This
315 // leads to the cached stack range on the Thread object being stale.
316 if (!g_fSkipStackCheck)
317 {
318 // We don't have the backing store boundaries stored on the thread, but this is just
319 // a sanity check anyway.
320 Thread * pThread = vmThread.GetDacPtr();
321 PTR_VOID sp = GetSP(reinterpret_cast<const T_CONTEXT *>(pContext));
322
323 if ((sp < pThread->GetCachedStackLimit()) || (pThread->GetCachedStackBase() <= sp))
324 {
325 return CORDBG_E_NON_MATCHING_CONTEXT;
326 }
327 }
328
329 return S_OK;
330}
331
332// Retrieve information about the current frame from the stackwalker.
333IDacDbiInterface::FrameType DacDbiInterfaceImpl::GetStackWalkCurrentFrameInfo(StackWalkHandle pSFIHandle,
334 DebuggerIPCE_STRData * pFrameData)
335{
336 DD_ENTER_MAY_THROW;
337
338 _ASSERTE(pSFIHandle != NULL);
339
340 StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
341
342 FrameType ftResult = kInvalid;
343 if (pIter->GetFrameState() == StackFrameIterator::SFITER_DONE)
344 {
345 _ASSERTE(!pIter->IsValid());
346 ftResult = kAtEndOfStack;
347 }
348 else
349 {
350 BOOL fInitFrameData = FALSE;
351 switch (pIter->GetFrameState())
352 {
353 case StackFrameIterator::SFITER_UNINITIALIZED:
354 ftResult = kInvalid;
355 break;
356
357 case StackFrameIterator::SFITER_FRAMELESS_METHOD:
358 ftResult = kManagedStackFrame;
359 fInitFrameData = TRUE;
360 break;
361
362 case StackFrameIterator::SFITER_FRAME_FUNCTION:
363 //
364 // fall through
365 //
366 case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION:
367 ftResult = kExplicitFrame;
368 fInitFrameData = TRUE;
369 break;
370
371 case StackFrameIterator::SFITER_NO_FRAME_TRANSITION:
372 // no-frame transition represents an ExInfo for a native exception on x86.
373 // For all intents and purposes this should be treated just like another explicit frame.
374 ftResult = kExplicitFrame;
375 fInitFrameData = TRUE;
376 break;
377
378 case StackFrameIterator::SFITER_NATIVE_MARKER_FRAME:
379 //
380 // fall through
381 //
382 case StackFrameIterator::SFITER_INITIAL_NATIVE_CONTEXT:
383 if (IsRuntimeUnwindableStub(GetControlPC(pIter->m_crawl.GetRegisterSet())))
384 {
385 ftResult = kNativeRuntimeUnwindableStackFrame;
386 fInitFrameData = TRUE;
387 }
388 else
389 {
390 ftResult = kNativeStackFrame;
391 }
392 break;
393
394 default:
395 UNREACHABLE();
396 }
397
398 if ((fInitFrameData == TRUE) && (pFrameData != NULL))
399 {
400 InitFrameData(pIter, ftResult, pFrameData);
401 }
402 }
403
404 return ftResult;
405}
406
407//---------------------------------------------------------------------------------------
408//
409// Return the number of internal frames on the specified thread.
410//
411// Arguments:
412// vmThread - the thread to be walked
413//
414// Return Value:
415// Return the number of interesting internal frames on the thread.
416//
417// Notes:
418// Internal frames are interesting if they are not of type STUBFRAME_NONE.
419//
420
421ULONG32 DacDbiInterfaceImpl::GetCountOfInternalFrames(VMPTR_Thread vmThread)
422{
423 DD_ENTER_MAY_THROW;
424
425 Thread * pThread = vmThread.GetDacPtr();
426 Frame * pFrame = pThread->GetFrame();
427
428 // We could call EnumerateInternalFrames() here, but it would be a lot of overhead for what we need.
429 ULONG32 uCount = 0;
430 while (pFrame != FRAME_TOP)
431 {
432 CorDebugInternalFrameType ift = GetInternalFrameType(pFrame);
433 if (ift != STUBFRAME_NONE)
434 {
435 uCount++;
436 }
437 pFrame = pFrame->Next();
438 }
439 return uCount;
440}
441
442//---------------------------------------------------------------------------------------
443//
444// Enumerate the internal frames on the specified thread and invoke the provided callback on each of them.
445//
446// Arguments:
447// vmThread - the thread to be walked
448// fpCallback - callback function to be invoked for each interesting internal frame
449// pUserData - user-defined custom data to be passed to the callback
450//
451
452void DacDbiInterfaceImpl::EnumerateInternalFrames(VMPTR_Thread vmThread,
453 FP_INTERNAL_FRAME_ENUMERATION_CALLBACK fpCallback,
454 void * pUserData)
455{
456 DD_ENTER_MAY_THROW;
457
458 DebuggerIPCE_STRData frameData;
459
460 Thread * pThread = vmThread.GetDacPtr();
461 Frame * pFrame = pThread->GetFrame();
462 AppDomain * pAppDomain = pThread->GetDomain(INDEBUG(TRUE));
463
464 // This used to be only true for Enter-Managed chains.
465 // Since we don't have chains anymore, this can always be false.
466 frameData.quicklyUnwound = false;
467 frameData.eType = DebuggerIPCE_STRData::cStubFrame;
468
469 while (pFrame != FRAME_TOP)
470 {
471 // check if the internal frame is interesting
472 frameData.stubFrame.frameType = GetInternalFrameType(pFrame);
473 if (frameData.stubFrame.frameType != STUBFRAME_NONE)
474 {
475 frameData.fp = FramePointer::MakeFramePointer(PTR_HOST_TO_TADDR(pFrame));
476
477 frameData.vmCurrentAppDomainToken.SetHostPtr(pAppDomain);
478
479 MethodDesc * pMD = pFrame->GetFunction();
480#if defined(FEATURE_COMINTEROP)
481 if (frameData.stubFrame.frameType == STUBFRAME_U2M)
482 {
483 _ASSERTE(pMD == NULL);
484
485 // U2M transition frame generally don't store the target MD because we know what the target
486 // is by looking at the callee stack frame. However, for reverse COM interop, we can try
487 // to get the MD for the interface.
488 //
489 // Note that some reverse COM interop cases don't have an intermediate interface MD, so
490 // pMD may still be NULL.
491 //
492 // Even if there is an MD on the ComMethodFrame, it could be in a different appdomain than
493 // the ComMethodFrame itself. The only known scenario is a cross-appdomain reverse COM
494 // interop call. We need to check for this case. The end result is that GetFunction() and
495 // GetFunctionToken() on ICDInternalFrame will return NULL.
496
497 // Minidumps without full memory don't guarantee to capture the CCW since we can do without
498 // it. In this case, pMD will remain NULL.
499 EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY
500 {
501 if (pFrame->GetVTablePtr() == ComMethodFrame::GetMethodFrameVPtr())
502 {
503 ComMethodFrame * pCOMFrame = dac_cast<PTR_ComMethodFrame>(pFrame);
504 PTR_VOID pUnkStackSlot = pCOMFrame->GetPointerToArguments();
505 PTR_IUnknown pUnk = dac_cast<PTR_IUnknown>(*dac_cast<PTR_TADDR>(pUnkStackSlot));
506 ComCallWrapper * pCCW = ComCallWrapper::GetWrapperFromIP(pUnk);
507
508 ComCallMethodDesc * pCMD = NULL;
509 pCMD = dac_cast<PTR_ComCallMethodDesc>(pCOMFrame->ComMethodFrame::GetDatum());
510 pMD = pCMD->GetInterfaceMethodDesc();
511 }
512 }
513 EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY
514 }
515#endif // FEATURE_COMINTEROP
516
517 Module * pModule = (pMD ? pMD->GetModule() : NULL);
518 DomainFile * pDomainFile = (pModule ? pModule->GetDomainFile(pAppDomain) : NULL);
519
520 if (frameData.stubFrame.frameType == STUBFRAME_FUNC_EVAL)
521 {
522 FuncEvalFrame * pFEF = dac_cast<PTR_FuncEvalFrame>(pFrame);
523 DebuggerEval * pDE = pFEF->GetDebuggerEval();
524
525 frameData.stubFrame.funcMetadataToken = pDE->m_methodToken;
526 frameData.stubFrame.vmDomainFile.SetHostPtr(
527 pDE->m_debuggerModule ? pDE->m_debuggerModule->GetDomainFile() : NULL);
528 frameData.stubFrame.vmMethodDesc = VMPTR_MethodDesc::NullPtr();
529 }
530 else
531 {
532 frameData.stubFrame.funcMetadataToken = (pMD == NULL ? NULL : pMD->GetMemberDef());
533 frameData.stubFrame.vmDomainFile.SetHostPtr(pDomainFile);
534 frameData.stubFrame.vmMethodDesc.SetHostPtr(pMD);
535 }
536
537 // invoke the callback
538 fpCallback(&frameData, pUserData);
539 }
540
541 // update the current appdomain if necessary
542 AppDomain * pRetDomain = pFrame->GetReturnDomain();
543 if (pRetDomain != NULL)
544 {
545 pAppDomain = pRetDomain;
546 }
547
548 // move on to the next internal frame
549 pFrame = pFrame->Next();
550 }
551}
552
553// Given the FramePointer of the parent frame and the FramePointer of the current frame,
554// check if the current frame is the parent frame.
555BOOL DacDbiInterfaceImpl::IsMatchingParentFrame(FramePointer fpToCheck, FramePointer fpParent)
556{
557 DD_ENTER_MAY_THROW;
558
559#ifdef WIN64EXCEPTIONS
560 StackFrame sfToCheck = StackFrame((UINT_PTR)fpToCheck.GetSPValue());
561
562 StackFrame sfParent = StackFrame((UINT_PTR)fpParent.GetSPValue());
563
564 // Ask the ExceptionTracker to figure out the answer.
565 // Don't try to compare the StackFrames/FramePointers ourselves.
566 return ExceptionTracker::IsUnwoundToTargetParentFrame(sfToCheck, sfParent);
567
568#else // !WIN64EXCEPTIONS
569 return FALSE;
570
571#endif // WIN64EXCEPTIONS
572}
573
574// Return the stack parameter size of the given method.
575ULONG32 DacDbiInterfaceImpl::GetStackParameterSize(CORDB_ADDRESS controlPC)
576{
577 DD_ENTER_MAY_THROW;
578
579 PCODE currentPC = PCODE(controlPC);
580
581 EECodeInfo codeInfo(currentPC);
582 return GetStackParameterSize(&codeInfo);
583}
584
585// Return the FramePointer of the current frame at which the stackwalker is stopped.
586FramePointer DacDbiInterfaceImpl::GetFramePointer(StackWalkHandle pSFIHandle)
587{
588 DD_ENTER_MAY_THROW;
589
590 StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
591 return GetFramePointerWorker(pIter);
592}
593
594// Internal helper for GetFramePointer.
595FramePointer DacDbiInterfaceImpl::GetFramePointerWorker(StackFrameIterator * pIter)
596{
597 CrawlFrame * pCF = &(pIter->m_crawl);
598 REGDISPLAY * pRD = pCF->GetRegisterSet();
599
600 FramePointer fp;
601 switch (pIter->GetFrameState())
602 {
603 // For managed methods, we have the full CONTEXT. Additionally, we also have the caller CONTEXT
604 // on WIN64.
605 case StackFrameIterator::SFITER_FRAMELESS_METHOD:
606 fp = FramePointer::MakeFramePointer(GetRegdisplayStackMark(pRD));
607 break;
608
609 // In these cases, we only have the full CONTEXT, not the caller CONTEXT.
610 case StackFrameIterator::SFITER_NATIVE_MARKER_FRAME:
611 //
612 // fall through
613 //
614 case StackFrameIterator::SFITER_INITIAL_NATIVE_CONTEXT:
615 fp = FramePointer::MakeFramePointer(GetRegdisplayStackMark(pRD));
616 break;
617
618 // In these cases, we use the address of the explicit frame as the frame marker.
619 case StackFrameIterator::SFITER_FRAME_FUNCTION:
620 //
621 // fall through
622 //
623 case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION:
624 fp = FramePointer::MakeFramePointer(PTR_HOST_TO_TADDR(pCF->GetFrame()));
625 break;
626
627 // No-frame transition represents an ExInfo for a native exception on x86.
628 // For all intents and purposes this should be treated just like another explicit frame.
629 case StackFrameIterator::SFITER_NO_FRAME_TRANSITION:
630 fp = FramePointer::MakeFramePointer(pCF->GetNoFrameTransitionMarker());
631 break;
632
633 case StackFrameIterator::SFITER_UNINITIALIZED:
634 //
635 // fall through
636 //
637 default:
638 UNREACHABLE();
639 }
640
641 return fp;
642}
643
644// Return TRUE if the specified CONTEXT is the CONTEXT of the leaf frame.
645// @dbgtodo filter CONTEXT - Currently we check for the filter CONTEXT first.
646BOOL DacDbiInterfaceImpl::IsLeafFrame(VMPTR_Thread vmThread,
647 const DT_CONTEXT * pContext)
648{
649 DD_ENTER_MAY_THROW;
650
651 DT_CONTEXT ctxLeaf;
652 GetContext(vmThread, &ctxLeaf);
653
654 // Call a platform-specific helper to compare the two contexts.
655 return CompareControlRegisters(pContext, &ctxLeaf);
656}
657
658// This is a simple helper function to convert a CONTEXT to a DebuggerREGDISPLAY. We need to do this
659// inside DDI because the RS has no notion of REGDISPLAY.
660void DacDbiInterfaceImpl::ConvertContextToDebuggerRegDisplay(const DT_CONTEXT * pInContext,
661 DebuggerREGDISPLAY * pOutDRD,
662 BOOL fActive)
663{
664 DD_ENTER_MAY_THROW;
665
666 // This is a bit cumbersome. First we need to convert the CONTEXT into a REGDISPLAY. Then we need
667 // to convert the REGDISPLAY to a DebuggerREGDISPLAY.
668 REGDISPLAY rd;
669 FillRegDisplay(&rd, reinterpret_cast<T_CONTEXT *>(const_cast<DT_CONTEXT *>(pInContext)));
670 SetDebuggerREGDISPLAYFromREGDISPLAY(pOutDRD, &rd);
671}
672
673//---------------------------------------------------------------------------------------
674//
675// Fill in the structure with information about the current frame at which the stackwalker is stopped.
676//
677// Arguments:
678// pIter - the stackwalker
679// pFrameData - the structure to be filled out
680//
681
682void DacDbiInterfaceImpl::InitFrameData(StackFrameIterator * pIter,
683 FrameType ft,
684 DebuggerIPCE_STRData * pFrameData)
685{
686 CrawlFrame * pCF = &(pIter->m_crawl);
687
688 //
689 // do common initialization of DebuggerIPCE_STRData for both managed stack frames and explicit frames
690 //
691
692 pFrameData->fp = GetFramePointerWorker(pIter);
693
694 // This used to be only true for Enter-Managed chains.
695 // Since we don't have chains anymore, this can always be false.
696 pFrameData->quicklyUnwound = false;
697
698 AppDomain * pAppDomain = pCF->GetAppDomain();
699 pFrameData->vmCurrentAppDomainToken.SetHostPtr(pAppDomain);
700
701 if (ft == kNativeRuntimeUnwindableStackFrame)
702 {
703 pFrameData->eType = DebuggerIPCE_STRData::cRuntimeNativeFrame;
704
705 GetStackWalkCurrentContext(pIter, &(pFrameData->ctx));
706 }
707 else if (ft == kManagedStackFrame)
708 {
709 MethodDesc * pMD = pCF->GetFunction();
710 Module * pModule = (pMD ? pMD->GetModule() : NULL);
711 // Although MiniDumpNormal tries to dump all AppDomains, it's possible
712 // target corruption will keep one from being present. This should mean
713 // we'll just fail later, but struggle on for now.
714 DomainFile *pDomainFile = NULL;
715 EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY
716 {
717 pDomainFile = (pModule ? pModule->GetDomainFile(pAppDomain) : NULL);
718 _ASSERTE(pDomainFile != NULL);
719 }
720 EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY
721
722 //
723 // This is a managed stack frame.
724 //
725
726 _ASSERTE(pMD != NULL);
727 _ASSERTE(pModule != NULL);
728
729 //
730 // initialize the rest of the DebuggerIPCE_STRData
731 //
732
733 pFrameData->eType = DebuggerIPCE_STRData::cMethodFrame;
734
735 SetDebuggerREGDISPLAYFromREGDISPLAY(&(pFrameData->rd), pCF->GetRegisterSet());
736
737 GetStackWalkCurrentContext(pIter, &(pFrameData->ctx));
738
739 //
740 // initialize the fields in DebuggerIPCE_STRData::v
741 //
742
743 // These fields will be filled in later. We don't have the sequence point mapping information here.
744 pFrameData->v.ILOffset = (SIZE_T)(-1);
745 pFrameData->v.mapping = MAPPING_NO_INFO;
746
747 // Check if this is a vararg method by getting the managed calling convention from the signature.
748 // Strictly speaking, we can do this in CordbJITILFrame::Init(), but it's just easier and more
749 // efficiently to do it here. CordbJITILFrame::Init() will initialize the other vararg-related
750 // fields. We don't have the native var info here to fully initialize everything.
751 pFrameData->v.fVarArgs = (pMD->IsVarArg() == TRUE);
752
753 pFrameData->v.fNoMetadata = (pMD->IsNoMetadata() == TRUE);
754
755 pFrameData->v.taAmbientESP = pCF->GetAmbientSPFromCrawlFrame();
756 if (pMD->IsSharedByGenericInstantiations())
757 {
758 // This method has a generic type token which is required to figure out the exact instantiation
759 // of the method. CrawlFrame::GetExactGenericArgsToken() can't always successfully retrieve
760 // the token because the JIT doesn't generate the required information all the time. As such,
761 // we need to save the variable index of the generic type token in order to do the look up later.
762 ALLOW_DATATARGET_MISSING_MEMORY(
763 pFrameData->v.exactGenericArgsToken = (GENERICS_TYPE_TOKEN)(dac_cast<TADDR>(pCF->GetExactGenericArgsToken()));
764 );
765
766 if (pMD->AcquiresInstMethodTableFromThis())
767 {
768 // The generic type token is the "this" object.
769 pFrameData->v.dwExactGenericArgsTokenIndex = 0;
770 }
771 else
772 {
773 // The generic type token is one of the secret arguments.
774 pFrameData->v.dwExactGenericArgsTokenIndex = (DWORD)ICorDebugInfo::TYPECTXT_ILNUM;
775 }
776 }
777 else
778 {
779 pFrameData->v.exactGenericArgsToken = NULL;
780 pFrameData->v.dwExactGenericArgsTokenIndex = (DWORD)ICorDebugInfo::MAX_ILNUM;
781 }
782
783 //
784 // initialize the DebuggerIPCE_FuncData and DebuggerIPCE_JITFuncData
785 //
786
787 DebuggerIPCE_FuncData * pFuncData = &(pFrameData->v.funcData);
788 DebuggerIPCE_JITFuncData * pJITFuncData = &(pFrameData->v.jitFuncData);
789
790 //
791 // initialize the "easy" fields of DebuggerIPCE_FuncData
792 //
793
794 pFuncData->funcMetadataToken = pMD->GetMemberDef();
795 pFuncData->vmDomainFile.SetHostPtr(pDomainFile);
796
797 // PERF: this is expensive to get so I stopped fetching it eagerly
798 // It is only needed if we haven't already got a cached copy
799 pFuncData->classMetadataToken = mdTokenNil;
800
801 //
802 // initialize the remaining fields of DebuggerIPCE_FuncData to the default values
803 //
804
805 pFuncData->ilStartAddress = NULL;
806 pFuncData->ilSize = 0;
807 pFuncData->currentEnCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION;
808 pFuncData->localVarSigToken = mdSignatureNil;
809
810 //
811 // inititalize the fields of DebuggerIPCE_JITFuncData
812 //
813
814 // For MiniDumpNormal, we do not guarantee method region info for all JIT tokens
815 // is present in the dump.
816 ALLOW_DATATARGET_MISSING_MEMORY(
817 pJITFuncData->nativeStartAddressPtr = PCODEToPINSTR(pCF->GetCodeInfo()->GetStartAddress());
818 );
819
820 // PERF: this is expensive to get so I stopped fetching it eagerly
821 // It is only needed if we haven't already got a cached copy
822 pJITFuncData->nativeHotSize = 0;
823 pJITFuncData->nativeStartAddressColdPtr = 0;
824 pJITFuncData->nativeColdSize = 0;
825
826 pJITFuncData->nativeOffset = pCF->GetRelOffset();
827
828 // Here we detect (and set the appropriate flag) if the nativeOffset in the current frame points to the return address of IL_Throw()
829 // (or other exception related JIT helpers like IL_Throw, IL_Rethrow, JIT_RngChkFail, IL_VerificationError, JIT_Overflow etc).
830 // Since return addres point to the next(!) instruction after [call IL_Throw] this sometimes can lead to incorrect exception stacktraces
831 // where a next source line is spotted as an exception origin. This happends when the next instruction after [call IL_Throw] belongs to
832 // a sequence point and a source line different from a sequence point and a source line of [call IL_Throw].
833 // Later on this flag is used in order to adjust nativeOffset and make ICorDebugILFrame::GetIP return IL offset withing
834 // the same sequence point as an actuall IL throw instruction.
835
836 // Here is how we detect it:
837 // We can assume that nativeOffset points to an the instruction after [call IL_Throw] when these conditioins are met:
838 // 1. pCF->IsInterrupted() - Exception has been thrown by this managed frame (frame attr FRAME_ATTR_EXCEPTION)
839 // 2. !pCF->HasFaulted() - It wasn't a "hardware" exception (Access violation, dev by 0, etc.)
840 // 3. !pCF->IsIPadjusted() - It hasn't been previously adjusted to point to [call IL_Throw]
841 // 4. pJITFuncData->nativeOffset != 0 - nativeOffset contains something that looks like a real return address.
842 pJITFuncData->jsutAfterILThrow = pCF->IsInterrupted()
843 && !pCF->HasFaulted()
844 && !pCF->IsIPadjusted()
845 && pJITFuncData->nativeOffset != 0;
846
847 pJITFuncData->nativeCodeJITInfoToken.Set(NULL);
848 pJITFuncData->vmNativeCodeMethodDescToken.SetHostPtr(pMD);
849
850 InitParentFrameInfo(pCF, pJITFuncData);
851
852 ALLOW_DATATARGET_MISSING_MEMORY(
853 pJITFuncData->isInstantiatedGeneric = pMD->HasClassOrMethodInstantiation();
854 );
855 pJITFuncData->enCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION;
856
857 // PERF: this is expensive to get so I stopped fetching it eagerly
858 // It is only needed if we haven't already got a cached copy
859 pFuncData->localVarSigToken = 0;
860 pFuncData->ilStartAddress = 0;
861 pFuncData->ilSize = 0;
862
863
864 // See the comment for LookupEnCVersions().
865 // PERF: this is expensive to get so I stopped fetching it eagerly
866 pFuncData->currentEnCVersion = 0;
867 pJITFuncData->enCVersion = 0;
868 }
869 else
870 {
871 _ASSERTE(!"DDII::InitFrameData() - We should never stop at internal frames.");
872 ThrowHR(CORDBG_E_TARGET_INCONSISTENT);
873 }
874}
875
876//---------------------------------------------------------------------------------------
877//
878// Initialize the address and the size of the jitted code, including both hot and cold regions.
879//
880// Arguments:
881// methodToken - METHODTOKEN of the method in question; this should actually be the CodeHeader address
882// pJITFuncData - structure to be filled out
883//
884
885void DacDbiInterfaceImpl::InitNativeCodeAddrAndSize(TADDR taStartAddr,
886 DebuggerIPCE_JITFuncData * pJITFuncData)
887{
888 PTR_CORDB_ADDRESS_TYPE pAddr = dac_cast<PTR_CORDB_ADDRESS_TYPE>(taStartAddr);
889 CodeRegionInfo crInfo = CodeRegionInfo::GetCodeRegionInfo(NULL, NULL, pAddr);
890
891 pJITFuncData->nativeStartAddressPtr = PCODEToPINSTR(crInfo.getAddrOfHotCode());
892 pJITFuncData->nativeHotSize = crInfo.getSizeOfHotCode();
893
894 pJITFuncData->nativeStartAddressColdPtr = PCODEToPINSTR(crInfo.getAddrOfColdCode());
895 pJITFuncData->nativeColdSize = crInfo.getSizeOfColdCode();
896}
897
898//---------------------------------------------------------------------------------------
899//
900// Initialize the funclet-related fields of DebuggerIPCE_JITFuncData. This is an nop on non-WIN64 platforms.
901//
902// Arguments:
903// pCF - the CrawlFrame for the current frame
904// pJITFuncData - the structure to be filled out
905//
906
907void DacDbiInterfaceImpl::InitParentFrameInfo(CrawlFrame * pCF,
908 DebuggerIPCE_JITFuncData * pJITFuncData)
909{
910#ifdef WIN64EXCEPTIONS
911 pJITFuncData->fIsFilterFrame = pCF->IsFilterFunclet();
912
913 if (pCF->IsFunclet())
914 {
915 DWORD dwParentOffset;
916 StackFrame sfParent = ExceptionTracker::FindParentStackFrameEx(pCF, &dwParentOffset, NULL);
917
918 //
919 // For funclets, fpParentOrSelf is the FramePointer of the parent.
920 // Don't mess around with this FramePointer. The only thing we can do with it is to pass it back
921 // to the ExceptionTracker when we are checking if a particular frame is the parent frame.
922 //
923
924 pJITFuncData->fpParentOrSelf = FramePointer::MakeFramePointer(sfParent.SP);
925 pJITFuncData->parentNativeOffset = dwParentOffset;
926 }
927 else
928 {
929 StackFrame sfSelf = ExceptionTracker::GetStackFrameForParentCheck(pCF);
930
931 //
932 // For non-funclets, fpParentOrSelf is the FramePointer of the current frame itself.
933 // Don't mess around with this FramePointer. The only thing we can do with it is to pass it back
934 // to the ExceptionTracker when we are checking if a particular frame is the parent frame.
935 //
936
937 pJITFuncData->fpParentOrSelf = FramePointer::MakeFramePointer(sfSelf.SP);
938 pJITFuncData->parentNativeOffset = 0;
939 }
940#endif // WIN64EXCEPTIONS
941}
942
943// Return the stack parameter size of the given method.
944// Refer to the full comment for the overloaded version.
945ULONG32 DacDbiInterfaceImpl::GetStackParameterSize(EECodeInfo * pCodeInfo)
946{
947 return pCodeInfo->GetCodeManager()->GetStackParameterSize(pCodeInfo);
948}
949
950
951//---------------------------------------------------------------------------------------
952//
953// Adjust the stack pointer in the CONTEXT for the stack parameters.
954// This is a nop on non-x86 platforms.
955//
956// Arguments:
957// pRD - the REGDISPLAY to be adjusted
958// cbStackParameterSize - the number of bytes for the stack parameters
959// fIsActiveFrame - whether the CONTEXT is for an active frame
960// StackAdjustmentDirection - whether we are changing a CONTEXT from the managed convention
961// to the unmanaged convention
962//
963// Notes:
964// Consider this code:
965//
966// push 1
967// push 2
968// call Foo
969// -> inc eax
970//
971// Here we are assuming that the return instruction in Foo() pops the stack arguments.
972//
973// Suppose the IP in the CONTEXT is at the arrow. The question is, where should the stack pointer be?
974//
975// 0x0 ret addr for Foo
976// 0x4 2
977// 0x8 1
978// 0xc .....
979//
980// If the CONTEXT is the active frame, i.e. the IP is the active instruction,
981// not the instruction at the return address, then the SP should be at 0xc.
982// However, if the CONTEXT is not active, then the SP can be at either 0x4 or 0xc, depending on
983// the convention used by the stackwalker. The managed stackwalker reports 0xc, but dbghelp reports
984// 0x4. To bridge the gap we have to shim it in the DDI.
985//
986// Currently, we have no way to reliably shim the CONTEXT in all cases. Consider this stack,
987// where U* are native stack frames and M* are managed stack frames:
988//
989// [leaf]
990// U2
991// U1
992// ------- (M2U transition)
993// M2
994// M1
995// M0
996// ------- (U2M transition)
997// U0
998// [root]
999//
1000// There are only two transition cases where we can reliably adjust for the callee stack parameter size:
1001// 1) when the debugger calls SetContext() with the CONTEXT of the first managed stack frame in a
1002// managed stack chain (i.e. SetContext() with M2's CONTEXT)
1003// - the M2U transition is protected by an explicit frame (aka Frame-chain frame)
1004// 2) when the debugger calls GetContext() on the first native stack frame in a native stack chain
1005// (i.e. GetContext() at U0)
1006// - we unwind from M0 to U0, so we know the stack parameter size of M0
1007//
1008// If we want to do the adjustment in all cases, we need to ask the JIT to store the callee stack
1009// parameter size in either the unwind info.
1010//
1011
1012void DacDbiInterfaceImpl::AdjustRegDisplayForStackParameter(REGDISPLAY * pRD,
1013 DWORD cbStackParameterSize,
1014 BOOL fIsActiveFrame,
1015 StackAdjustmentDirection direction)
1016{
1017#if defined(_TARGET_X86_)
1018 // If the CONTEXT is active then no adjustment is needed.
1019 if (!fIsActiveFrame)
1020 {
1021 UINT_PTR sp = GetRegdisplaySP(pRD);
1022 if (direction == kFromManagedToUnmanaged)
1023 {
1024 // The CONTEXT comes from the managed world.
1025 sp -= cbStackParameterSize;
1026 }
1027 else
1028 {
1029 _ASSERTE(!"Currently, we should not hit this case.\n");
1030
1031 // The CONTEXT comes from the unmanaged world.
1032 sp += cbStackParameterSize;
1033 }
1034 SetRegdisplaySP(pRD, reinterpret_cast<LPVOID>(sp));
1035 }
1036#endif // _TARGET_X86_
1037}
1038
1039//---------------------------------------------------------------------------------------
1040//
1041// Given an explicit frame, return its frame type in terms of CorDebugInternalFrameType.
1042//
1043// Arguments:
1044// pFrame - the explicit frame in question
1045//
1046// Return Value:
1047// Return the CorDebugInternalFrameType of the explicit frame
1048//
1049// Notes:
1050// I wish this function were simpler, but it's not. The logic in this function is adopted
1051// from the logic in the old in-proc debugger stackwalker.
1052//
1053
1054CorDebugInternalFrameType DacDbiInterfaceImpl::GetInternalFrameType(Frame * pFrame)
1055{
1056 CorDebugInternalFrameType resultType = STUBFRAME_NONE;
1057
1058 Frame::ETransitionType tt = pFrame->GetTransitionType();
1059 Frame::Interception it = pFrame->GetInterception();
1060 int ft = pFrame->GetFrameType();
1061
1062 switch (tt)
1063 {
1064 case Frame::TT_NONE:
1065 if (it == Frame::INTERCEPTION_CLASS_INIT)
1066 {
1067 resultType = STUBFRAME_CLASS_INIT;
1068 }
1069 else if (it == Frame::INTERCEPTION_EXCEPTION)
1070 {
1071 resultType = STUBFRAME_EXCEPTION;
1072 }
1073 else if (it == Frame::INTERCEPTION_SECURITY)
1074 {
1075 resultType = STUBFRAME_SECURITY;
1076 }
1077 else if (it == Frame::INTERCEPTION_PRESTUB)
1078 {
1079 resultType = STUBFRAME_JIT_COMPILATION;
1080 }
1081 else
1082 {
1083 if (ft == Frame::TYPE_FUNC_EVAL)
1084 {
1085 resultType = STUBFRAME_FUNC_EVAL;
1086 }
1087 else if (ft == Frame::TYPE_EXIT)
1088 {
1089 if ((pFrame->GetVTablePtr() != InlinedCallFrame::GetMethodFrameVPtr()) ||
1090 InlinedCallFrame::FrameHasActiveCall(pFrame))
1091 {
1092 resultType = STUBFRAME_M2U;
1093 }
1094 }
1095 }
1096 break;
1097
1098 case Frame::TT_M2U:
1099 // Refer to the comment in DebuggerWalkStackProc() for StubDispatchFrame.
1100 if (pFrame->GetVTablePtr() != StubDispatchFrame::GetMethodFrameVPtr())
1101 {
1102 if (it == Frame::INTERCEPTION_SECURITY)
1103 {
1104 resultType = STUBFRAME_SECURITY;
1105 }
1106 else
1107 {
1108 resultType = STUBFRAME_M2U;
1109 }
1110 }
1111 break;
1112
1113 case Frame::TT_U2M:
1114 resultType = STUBFRAME_U2M;
1115 break;
1116
1117 case Frame::TT_AppDomain:
1118 resultType = STUBFRAME_APPDOMAIN_TRANSITION;
1119 break;
1120
1121 case Frame::TT_InternalCall:
1122 if (it == Frame::INTERCEPTION_EXCEPTION)
1123 {
1124 resultType = STUBFRAME_EXCEPTION;
1125 }
1126 else
1127 {
1128 resultType = STUBFRAME_INTERNALCALL;
1129 }
1130 break;
1131
1132 default:
1133 UNREACHABLE();
1134 break;
1135 }
1136
1137 return resultType;
1138}
1139
1140//---------------------------------------------------------------------------------------
1141//
1142// This is just a simpler helper function to convert a REGDISPLAY to a CONTEXT.
1143//
1144// Arguments:
1145// pRegDisp - the REGDISPLAY to be converted
1146// pContext - the buffer for storing the converted CONTEXT
1147//
1148
1149void DacDbiInterfaceImpl::UpdateContextFromRegDisp(REGDISPLAY * pRegDisp,
1150 T_CONTEXT * pContext)
1151{
1152#if defined(_TARGET_X86_) && !defined(WIN64EXCEPTIONS)
1153 // Do a partial copy first.
1154 pContext->ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL);
1155
1156 pContext->Edi = *pRegDisp->GetEdiLocation();
1157 pContext->Esi = *pRegDisp->GetEsiLocation();
1158 pContext->Ebx = *pRegDisp->GetEbxLocation();
1159 pContext->Ebp = *pRegDisp->GetEbpLocation();
1160 pContext->Eax = *pRegDisp->GetEaxLocation();
1161 pContext->Ecx = *pRegDisp->GetEcxLocation();
1162 pContext->Edx = *pRegDisp->GetEdxLocation();
1163 pContext->Esp = pRegDisp->SP;
1164 pContext->Eip = pRegDisp->ControlPC;
1165
1166 // If we still have the pointer to the leaf CONTEXT, and the leaf CONTEXT is the same as the CONTEXT for
1167 // the current frame (i.e. the stackwalker is at the leaf frame), then we do a full copy.
1168 if ((pRegDisp->pContext != NULL) &&
1169 (CompareControlRegisters(const_cast<const DT_CONTEXT *>(reinterpret_cast<DT_CONTEXT *>(pContext)),
1170 const_cast<const DT_CONTEXT *>(reinterpret_cast<DT_CONTEXT *>(pRegDisp->pContext)))))
1171 {
1172 *pContext = *pRegDisp->pContext;
1173 }
1174#else // _TARGET_X86_ && !WIN64EXCEPTIONS
1175 *pContext = *pRegDisp->pCurrentContext;
1176#endif // !_TARGET_X86_ || WIN64EXCEPTIONS
1177}
1178
1179//---------------------------------------------------------------------------------------
1180//
1181// Given the REGDISPLAY of a stack frame for one of the redirect functions, retrieve the original CONTEXT
1182// before the thread redirection.
1183//
1184// Arguments:
1185// pRD - the REGDISPLAY of the stack frame in question
1186//
1187// Return Value:
1188// Return the original CONTEXT before the thread got redirected.
1189//
1190// Assumptions:
1191// The caller has checked that the REGDISPLAY is indeed for one of the redirect functions.
1192//
1193
1194PTR_CONTEXT DacDbiInterfaceImpl::RetrieveHijackedContext(REGDISPLAY * pRD)
1195{
1196 CORDB_ADDRESS ContextPointerAddr = NULL;
1197
1198 TADDR controlPC = PCODEToPINSTR(GetControlPC(pRD));
1199
1200 // Check which thread redirection mechanism is used.
1201 if (g_pDebugger->m_rgHijackFunction[Debugger::kUnhandledException].IsInRange(controlPC))
1202 {
1203 // The thread is redirected because of an unhandled exception.
1204
1205 // The CONTEXT pointer is the last thing pushed onto the stack.
1206 // So just read the stack slot at ESP. That will be the TADDR to the CONTEXT.
1207 ContextPointerAddr = PTR_TO_CORDB_ADDRESS(GetRegdisplaySP(pRD));
1208
1209 // Read the CONTEXT from OOP.
1210 return *dac_cast<PTR_PTR_CONTEXT>((TADDR)ContextPointerAddr);
1211 }
1212 else
1213 {
1214 // The thread is redirected by the EE via code:Thread::RedirectThreadAtHandledJITCase.
1215
1216 // Convert the REGDISPLAY to a CONTEXT;
1217 T_CONTEXT * pContext = NULL;
1218
1219#if defined(_TARGET_X86_)
1220 T_CONTEXT ctx;
1221 pContext = &ctx;
1222 UpdateContextFromRegDisp(pRD, pContext);
1223#else
1224 pContext = pRD->pCurrentContext;
1225#endif
1226
1227 // Retrieve the original CONTEXT.
1228 return GetCONTEXTFromRedirectedStubStackFrame(pContext);
1229 }
1230}
1231
1232//---------------------------------------------------------------------------------------
1233//
1234// Unwind special native stack frame which the runtime knows how to unwind.
1235//
1236// Arguments:
1237// pIter - the StackFrameIterator we are currently using to walk the stack
1238//
1239// Return Value:
1240// Return TRUE if there are more frames to walk, i.e. if we are NOT at the end of the stack.
1241//
1242// Assumptions:
1243// pIter is currently stopped at a special stub which the runtime knows how to unwind.
1244//
1245// Notes:
1246// * Refer to code:DacDbiInterfaceImpl::IsRuntimeUnwindableStub to see how we determine whether a control
1247// PC is in a runtime-unwindable stub
1248//
1249
1250BOOL DacDbiInterfaceImpl::UnwindRuntimeStackFrame(StackFrameIterator * pIter)
1251{
1252 _ASSERTE(IsRuntimeUnwindableStub(GetControlPC(pIter->m_crawl.GetRegisterSet())));
1253
1254 T_CONTEXT * pContext = NULL;
1255 REGDISPLAY * pRD = pIter->m_crawl.GetRegisterSet();
1256
1257 //
1258 // Retrieve the CONTEXT to unwind to and unwind the REGDISPLAY.
1259 //
1260 pContext = RetrieveHijackedContext(pRD);
1261
1262 FillRegDisplay(pRD, pContext);
1263
1264 // Update the StackFrameIterator.
1265 BOOL fSuccess = pIter->ResetRegDisp(pRD, true);
1266 if (!fSuccess)
1267 {
1268 // ResetRegDisp() may fail for the same reason Init() may fail, i.e.
1269 // because the stackwalker tries to unwind one frame ahead of time,
1270 // or because the stackwalker needs to filter out some frames based on the stackwalk flags.
1271 ThrowHR(E_FAIL);
1272 }
1273
1274 // Currently we only unwind the hijack function, which will never be the last stack frame.
1275 // So return TRUE to indicate that this is not the end of stack.
1276 return TRUE;
1277}
1278
1279//---------------------------------------------------------------------------------------
1280//
1281// To aid in doing the stack walk, the shim needs to know if either TS_SyncSuspended or
1282// TS_Hijacked is set on a given thread. This DAC helper provides that access.
1283//
1284// Arguments:
1285// vmThread - Thread on which to check the TS_SyncSuspended & TS_Hijacked states
1286//
1287// Return Value:
1288// Return true iff TS_SyncSuspended or TS_Hijacked is set on the specified thread.
1289//
1290
1291bool DacDbiInterfaceImpl::IsThreadSuspendedOrHijacked(VMPTR_Thread vmThread)
1292{
1293 DD_ENTER_MAY_THROW;
1294
1295 Thread * pThread = vmThread.GetDacPtr();
1296 Thread::ThreadState ts = pThread->GetSnapshotState();
1297 if ((ts & Thread::TS_SyncSuspended) != 0)
1298 {
1299 return true;
1300 }
1301
1302#ifdef FEATURE_HIJACK
1303 if ((ts & Thread::TS_Hijacked) != 0)
1304 {
1305 return true;
1306 }
1307#endif
1308
1309 return false;
1310}
1311