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// ShimStackWalk.cpp
6//
7
8//
9// This file contains the implementation of the Arrowhead stackwalking shim. This shim builds on top of
10// the public Arrowhead ICD stackwalking API, and it is intended to be backward-compatible with the existing
11// debuggers using the V2.0 ICD API.
12//
13// ======================================================================================
14
15#include "stdafx.h"
16#include "primitives.h"
17
18#if defined(_TARGET_X86_)
19static const ULONG32 REGISTER_X86_MAX = REGISTER_X86_FPSTACK_7 + 1;
20static const ULONG32 MAX_MASK_COUNT = (REGISTER_X86_MAX + 7) >> 3;
21#elif defined(_TARGET_AMD64_)
22static const ULONG32 REGISTER_AMD64_MAX = REGISTER_AMD64_XMM15 + 1;
23static const ULONG32 MAX_MASK_COUNT = (REGISTER_AMD64_MAX + 7) >> 3;
24#endif
25
26ShimStackWalk::ShimStackWalk(ShimProcess * pProcess, ICorDebugThread * pThread)
27 : m_pChainEnumList(NULL),
28 m_pFrameEnumList(NULL)
29{
30 // The following assignments increment the ref count.
31 m_pProcess.Assign(pProcess);
32 m_pThread.Assign(pThread);
33
34 Populate();
35}
36
37ShimStackWalk::~ShimStackWalk()
38{
39 Clear();
40}
41
42// ----------------------------------------------------------------------------
43// ShimStackWalk::Clear
44//
45// Description:
46// Clear all the memory used by this ShimStackWalk, including the array of frames, the array of chains,
47// the linked list of ShimChainEnums, and the linked list of ShimFrameEnums.
48//
49
50void ShimStackWalk::Clear()
51{
52 // call Release() on each of the ShimChains
53 for (int i = 0; i < m_stackChains.Count(); i++)
54 {
55 (*m_stackChains.Get(i))->Neuter();
56 (*m_stackChains.Get(i))->Release();
57 }
58 m_stackChains.Clear();
59
60 // call Release() on each of the ICDFrames
61 for (int i = 0; i < m_stackFrames.Count(); i++)
62 {
63 (*m_stackFrames.Get(i))->Release();
64 }
65 m_stackFrames.Clear();
66
67 // call Release() on each of the ShimChainEnums
68 while (m_pChainEnumList != NULL)
69 {
70 ShimChainEnum * pCur = m_pChainEnumList;
71 m_pChainEnumList = m_pChainEnumList->GetNext();
72 pCur->Neuter();
73 pCur->Release();
74 }
75
76 // call Release() on each of the ShimFrameEnums
77 while (m_pFrameEnumList != NULL)
78 {
79 ShimFrameEnum * pCur = m_pFrameEnumList;
80 m_pFrameEnumList = m_pFrameEnumList->GetNext();
81 pCur->Neuter();
82 pCur->Release();
83 }
84
85 // release the references
86 m_pProcess.Clear();
87 m_pThread.Clear();
88}
89
90//---------------------------------------------------------------------------------------
91//
92// Helper used by the stackwalker to determine whether a given UM chain should be tracked
93// during the stackwalk for eventual transmission to the debugger. This function is the
94// V4 equivalent of Whidbey's code:ShouldSendUMLeafChain (which ran on the LS, from
95// Debug\EE\frameinfo.cpp).
96//
97// Note that code:ShouldSendUMLeafChain still exists today (to facilitate some in-process
98// debugging stackwalks that are still necessary). So consult the comments in
99// code:ShouldSendUMLeafChain for a more thorough discussion of why we do the checks we
100// do to decide whether to track the chain.
101//
102// Arguments:
103// pswInfo - StackWalkInfo representing the frame in question
104//
105// Return Value:
106// nonzero iff the chain should be tracked
107//
108
109BOOL ShimStackWalk::ShouldTrackUMChain(StackWalkInfo * pswInfo)
110{
111 _ASSERTE (pswInfo != NULL);
112
113 // Always track chains for non-leaf UM frames
114 if (!pswInfo->IsLeafFrame())
115 return TRUE;
116
117 // Sometimes we want to track leaf UM chains, and sometimes we don't. Check all the
118 // reasons not to track the chain, and return FALSE if any of them are hit.
119
120 CorDebugUserState threadUserState;
121 HRESULT hr = m_pThread->GetUserState(&threadUserState);
122 IfFailThrow(hr);
123
124 // ShouldSendUMLeafChain checked IsInWaitSleepJoin which is just USER_WAIT_SLEEP_JOIN
125 if ((threadUserState & USER_WAIT_SLEEP_JOIN) != 0)
126 return FALSE;
127
128 // This check is the same as Thread::IsUnstarted() from ShouldSendUMLeafChain
129 if ((threadUserState & USER_UNSTARTED) != 0)
130 return FALSE;
131
132 // This check is the same as Thread::IsDead() from ShouldSendUMLeafChain
133 if ((threadUserState & USER_STOPPED) != 0)
134 return FALSE;
135
136 // #DacShimSwWorkAround
137 //
138 // This part cannot be determined using DBI alone. We must call through to the DAC
139 // because we have no other way to get at TS_Hijacked & TS_SyncSuspended. When the
140 // rearchitecture is complete, this DAC call should be able to go away, and we
141 // should be able to use DBI for all the info we need.
142 //
143 // One might think one could avoid the DAC for TS_SyncSuspended by just checking
144 // USER_SUSPENDED, but that won't work. Although USER_SUSPENDED will be returned some
145 // of the time when TS_SyncSuspended is set, that will not be the case when the
146 // debugger must suspend the thread. Example: if the given thread is in the middle of
147 // throwing a managed exception when the debugger breaks in, then TS_SyncSuspended
148 // will be set due to the debugger's breaking, but USER_SUSPENDED will not be set, so
149 // we'll think the UM chain should be tracked, resulting in a stack that differs from
150 // Whidbey.
151 if (m_pProcess->IsThreadSuspendedOrHijacked(m_pThread))
152 return FALSE;
153
154 return TRUE;
155}
156
157// ----------------------------------------------------------------------------
158// ShimStackWalk::Populate
159//
160// Description:
161// Walk the entire stack and populate the arrays of stack frames and stack chains.
162//
163
164void ShimStackWalk::Populate()
165{
166 HRESULT hr = S_OK;
167
168 // query for the ICDThread3 interface
169 RSExtSmartPtr<ICorDebugThread3> pThread3;
170 hr = m_pThread->QueryInterface(IID_ICorDebugThread3, reinterpret_cast<void **>(&pThread3));
171 IfFailThrow(hr);
172
173 // create the ICDStackWalk
174 RSExtSmartPtr<ICorDebugStackWalk> pSW;
175 hr = pThread3->CreateStackWalk(&pSW);
176 IfFailThrow(hr);
177
178 // structs used to store information during the stackwalk
179 ChainInfo chainInfo;
180 StackWalkInfo swInfo;
181
182 // use the ICDStackWalk to retrieve the internal frames
183 hr = pThread3->GetActiveInternalFrames(0, &(swInfo.m_cInternalFrames), NULL);
184 IfFailThrow(hr);
185
186 // allocate memory for the internal frames
187 if (swInfo.m_cInternalFrames > 0)
188 {
189 // allocate memory for the array of RSExtSmartPtrs
190 swInfo.m_ppInternalFrame2.AllocOrThrow(swInfo.m_cInternalFrames);
191
192 // create a temporary buffer of raw ICDInternalFrame2 to pass to the ICD API
193 NewArrayHolder<ICorDebugInternalFrame2 *> pTmpArray(new ICorDebugInternalFrame2* [swInfo.m_cInternalFrames]);
194 hr = pThread3->GetActiveInternalFrames(swInfo.m_cInternalFrames,
195 &(swInfo.m_cInternalFrames),
196 pTmpArray);
197 IfFailThrow(hr);
198
199 // transfer the raw array to the RSExtSmartPtr array
200 for (UINT32 i = 0; i < swInfo.m_cInternalFrames; i++)
201 {
202 // Assign() increments the ref count
203 swInfo.m_ppInternalFrame2.Assign(i, pTmpArray[i]);
204 pTmpArray[i]->Release();
205 }
206 pTmpArray.Clear();
207 }
208
209 //
210 // This is basically how the loop works:
211 // 1) Determine whether we should process the next internal frame or the next stack frame.
212 // 2) If we are skipping frames, the only thing we need to do is to check whether we have reached
213 // the parent frame.
214 // 3) Process CHAIN_ENTER_MANAGED/CHAIN_ENTER_UNMANAGED chains
215 // 4) Append the frame to the cache.
216 // 5) Handle other types of chains.
217 // 6) Advance to the next frame.
218 // 7) Check if we should exit the loop.
219 //
220 while (true)
221 {
222 // reset variables used in the loop
223 swInfo.ResetForNextFrame();
224
225 // retrieve the next stack frame if it's available
226 RSExtSmartPtr<ICorDebugFrame> pFrame;
227 if (!swInfo.ExhaustedAllStackFrames())
228 {
229 hr = pSW->GetFrame(&pFrame);
230 IfFailThrow(hr);
231 }
232
233 // This next clause processes the current frame, regardless of whether it's an internal frame or a
234 // stack frame. Normally, "pFrame != NULL" is a good enough check, except for the case where we
235 // have exhausted all the stack frames but still have internal frames to process.
236 if ((pFrame != NULL) || swInfo.ExhaustedAllStackFrames())
237 {
238 // prefetch the internal frame type
239 if (!swInfo.ExhaustedAllInternalFrames())
240 {
241 swInfo.m_internalFrameType = GetInternalFrameType(swInfo.GetCurrentInternalFrame());
242 }
243
244 // We cannot have exhausted both the stack frames and the internal frames when we get to here.
245 // We should have exited the loop if we have exhausted both types of frames.
246 if (swInfo.ExhaustedAllStackFrames())
247 {
248 swInfo.m_fProcessingInternalFrame = true;
249 }
250 else if (swInfo.ExhaustedAllInternalFrames())
251 {
252 swInfo.m_fProcessingInternalFrame = false;
253 }
254 else
255 {
256 // check whether we should process the next internal frame or the next stack frame
257 swInfo.m_fProcessingInternalFrame = (CheckInternalFrame(pFrame, &swInfo, pThread3, pSW) == TRUE);
258 }
259
260 // The only thing we do while we are skipping frames is to check whether we have reached the
261 // parent frame, and we only need to check if we are processing a stack frame.
262 if (swInfo.IsSkippingFrame())
263 {
264 if (!swInfo.m_fProcessingInternalFrame)
265 {
266 // Check whether we have reached the parent frame yet.
267 RSExtSmartPtr<ICorDebugNativeFrame2> pNFrame2;
268 hr = pFrame->QueryInterface(IID_ICorDebugNativeFrame2, reinterpret_cast<void **>(&pNFrame2));
269 IfFailThrow(hr);
270
271 BOOL fIsParent = FALSE;
272 hr = swInfo.m_pChildFrame->IsMatchingParentFrame(pNFrame2, &fIsParent);
273 IfFailThrow(hr);
274
275 if (fIsParent)
276 {
277 swInfo.m_pChildFrame.Clear();
278 }
279 }
280 }
281 else if(swInfo.m_fProcessingInternalFrame && !chainInfo.m_fLeafNativeContextIsValid &&
282 swInfo.m_internalFrameType == STUBFRAME_M2U)
283 {
284 // Filter this frame out entirely
285 // This occurs because InlinedCallFrames get placed inside leaf managed methods.
286 // The frame gets erected before the native call is made and destroyed afterwards
287 // but there is a window in which the debugger could stop where the internal frame
288 // is live but we are executing jitted code. See Dev10 issue 743230
289 // It is quite possible other frames have this same pattern if the debugger were
290 // stopped right at the spot where they are being constructed. And that is
291 // just a facet of the general data structure consistency problems the debugger
292 // will always face
293 }
294 else
295 {
296 // Don't add any frame just yet. We need to deal with any unmanaged chain
297 // we are tracking first.
298
299 // track the current enter-unmanaged chain and/or enter-managed chain
300 TrackUMChain(&chainInfo, &swInfo);
301
302 if (swInfo.m_fProcessingInternalFrame)
303 {
304 // Check if this is a leaf internal frame. If so, check its frame type.
305 // In V2, code:DebuggerWalkStackProc doesn't expose chains derived from leaf internal
306 // frames of type TYPE_INTERNAL. However, V2 still exposes leaf M2U and U2M internal
307 // frames.
308 if (swInfo.IsLeafFrame())
309 {
310 if (swInfo.m_internalFrameType == STUBFRAME_EXCEPTION)
311 {
312 // We need to make sure we don't accidentally send an enter-unmanaged chain
313 // because of the leaf STUBFRAME_EXCEPTION.
314 chainInfo.CancelUMChain();
315 swInfo.m_fSkipChain = true;
316 }
317 }
318
319 _ASSERTE(!swInfo.IsSkippingFrame());
320 if (ConvertInternalFrameToDynamicMethod(&swInfo))
321 {
322 // We have just converted a STUBFRAME_JIT_COMPILATION to a
323 // STUBFRAME_LIGHTWEIGHT_FUNCTION (or to NULL). Since the latter frame type doesn't
324 // map to any chain in V2, let's skip the chain handling.
325 swInfo.m_fSkipChain = true;
326
327 // We may have converted to NULL, which means that we are dealing with an IL stub
328 // and we shouldn't expose it.
329 if (swInfo.GetCurrentInternalFrame() != NULL)
330 {
331 AppendFrame(swInfo.GetCurrentInternalFrame(), &swInfo);
332 }
333 }
334 else
335 {
336 // One more check before we append the internal frame: make sure the frame type is a
337 // V2 frame type first.
338 if (!IsV3FrameType(swInfo.m_internalFrameType))
339 {
340 AppendFrame(swInfo.GetCurrentInternalFrame(), &swInfo);
341 }
342 }
343 }
344 else
345 {
346 if (!chainInfo.m_fNeedEnterManagedChain)
347 {
348 // If we have hit any managed stack frame, then we may need to send
349 // an enter-managed chain later. Save the CONTEXT now.
350 SaveChainContext(pSW, &chainInfo, &(chainInfo.m_leafManagedContext));
351 chainInfo.m_fNeedEnterManagedChain = true;
352 }
353
354 // We are processing a stack frame.
355 // Only append the frame if it's NOT a dynamic method.
356 _ASSERTE(!swInfo.IsSkippingFrame());
357 if (ConvertStackFrameToDynamicMethod(pFrame, &swInfo))
358 {
359 // We have converted a ICDNativeFrame for an IL method without metadata to an
360 // ICDInternalFrame of type STUBFRAME_LIGHTWEIGHT_FUNCTION (or to NULL).
361 // Fortunately, we don't have to update any state here
362 // (e.g. m_fProcessingInternalFrame) because the rest of the loop doesn't care.
363 if (swInfo.GetCurrentInternalFrame() != NULL)
364 {
365 AppendFrame(swInfo.GetCurrentInternalFrame(), &swInfo);
366 }
367 }
368 else
369 {
370 AppendFrame(pFrame, &swInfo);
371 }
372
373 // If we have just processed a child frame, we should start skipping.
374 // Get the ICDNativeFrame2 pointer to check.
375 RSExtSmartPtr<ICorDebugNativeFrame2> pNFrame2;
376 hr = pFrame->QueryInterface(IID_ICorDebugNativeFrame2, reinterpret_cast<void **>(&pNFrame2));
377 IfFailThrow(hr);
378
379 if (pNFrame2 != NULL)
380 {
381 BOOL fIsChild = FALSE;
382 hr = pNFrame2->IsChild(&fIsChild);
383 IfFailThrow(hr);
384
385 if (fIsChild)
386 {
387 swInfo.m_pChildFrame.Assign(pNFrame2);
388 }
389 }
390 }
391 }
392 } // process the current frame (managed stack frame or internal frame)
393
394 // We can take care of other types of chains here, but only do so if we are not currently skipping
395 // child frames.
396 if (!swInfo.IsSkippingFrame())
397 {
398 if ((pFrame == NULL) &&
399 !swInfo.ExhaustedAllStackFrames())
400 {
401 // We are here because we are processing a native marker stack frame, not because
402 // we have exhausted all the stack frames.
403
404 // We need to save the CONTEXT to start tracking an unmanaged chain.
405 SaveChainContext(pSW, &chainInfo, &(chainInfo.m_leafNativeContext));
406 chainInfo.m_fLeafNativeContextIsValid = true;
407
408 // begin tracking UM chain if we're supposed to
409 if (ShouldTrackUMChain(&swInfo))
410 {
411 chainInfo.m_reason = CHAIN_ENTER_UNMANAGED;
412 }
413 }
414 else
415 {
416 // handle other types of chains
417 if (swInfo.m_fProcessingInternalFrame)
418 {
419 if (!swInfo.m_fSkipChain)
420 {
421 BOOL fNewChain = FALSE;
422
423 switch (swInfo.m_internalFrameType)
424 {
425 case STUBFRAME_M2U: // fall through
426 case STUBFRAME_U2M: // fall through
427 // These frame types are tracked specially.
428 break;
429
430 case STUBFRAME_APPDOMAIN_TRANSITION: // fall through
431 case STUBFRAME_LIGHTWEIGHT_FUNCTION: // fall through
432 case STUBFRAME_INTERNALCALL:
433 // These frame types don't correspond to chains.
434 break;
435
436 case STUBFRAME_FUNC_EVAL:
437 chainInfo.m_reason = CHAIN_FUNC_EVAL;
438 fNewChain = TRUE;
439 break;
440
441 case STUBFRAME_CLASS_INIT: // fall through
442 case STUBFRAME_JIT_COMPILATION:
443 // In Whidbey, these two frame types are the same.
444 chainInfo.m_reason = CHAIN_CLASS_INIT;
445 fNewChain = TRUE;
446 break;
447
448 case STUBFRAME_EXCEPTION:
449 chainInfo.m_reason = CHAIN_EXCEPTION_FILTER;
450 fNewChain = TRUE;
451 break;
452
453 case STUBFRAME_SECURITY:
454 chainInfo.m_reason = CHAIN_SECURITY;
455 fNewChain = TRUE;
456 break;
457
458 default:
459 // We can only reach this case if we have converted an IL stub to NULL.
460 _ASSERTE(swInfo.HasConvertedFrame());
461 break;
462 }
463
464 if (fNewChain)
465 {
466 chainInfo.m_rootFP = GetFramePointerForChain(swInfo.GetCurrentInternalFrame());
467 AppendChain(&chainInfo, &swInfo);
468 }
469 }
470 } // chain handling for an internl frame
471 } // chain handling for a managed stack frame or an internal frame
472 } // chain handling
473
474 // Reset the flag for leaf frame if we have processed any frame. The only case where we should
475 // not reset this flag is if the ICDStackWalk is stopped at a native stack frame on creation.
476 if (swInfo.IsLeafFrame())
477 {
478 if (swInfo.m_fProcessingInternalFrame || (pFrame != NULL))
479 {
480 swInfo.m_fLeafFrame = false;
481 }
482 }
483
484 // advance to the next frame
485 if (swInfo.m_fProcessingInternalFrame)
486 {
487 swInfo.m_curInternalFrame += 1;
488 }
489 else
490 {
491 hr = pSW->Next();
492 IfFailThrow(hr);
493
494 // check for the end of stack condition
495 if (hr == CORDBG_S_AT_END_OF_STACK)
496 {
497 // By the time we finish the stackwalk, all child frames should have been matched with their
498 // respective parent frames.
499 _ASSERTE(!swInfo.IsSkippingFrame());
500
501 swInfo.m_fExhaustedAllStackFrames = true;
502 }
503 }
504
505 // Break out of the loop if we have exhausted all the frames.
506 if (swInfo.ExhaustedAllFrames())
507 {
508 break;
509 }
510 }
511
512 // top off the stackwalk with a thread start chain
513 chainInfo.m_reason = CHAIN_THREAD_START;
514 chainInfo.m_rootFP = ROOT_MOST_FRAME; // In Whidbey, we actually use the cached stack base value.
515 AppendChain(&chainInfo, &swInfo);
516}
517
518// the caller is responsible for addref and release
519ICorDebugThread * ShimStackWalk::GetThread()
520{
521 return m_pThread;
522}
523
524// the caller is responsible for addref and release
525ShimChain * ShimStackWalk::GetChain(UINT32 index)
526{
527 if (index >= (UINT32)(m_stackChains.Count()))
528 {
529 return NULL;
530 }
531 else
532 {
533 return *(m_stackChains.Get((int)index));
534 }
535}
536
537// the caller is responsible for addref and release
538ICorDebugFrame * ShimStackWalk::GetFrame(UINT32 index)
539{
540 if (index >= (UINT32)(m_stackFrames.Count()))
541 {
542 return NULL;
543 }
544 else
545 {
546 return *(m_stackFrames.Get((int)index));
547 }
548}
549
550ULONG ShimStackWalk::GetChainCount()
551{
552 return m_stackChains.Count();
553}
554
555ULONG ShimStackWalk::GetFrameCount()
556{
557 return m_stackFrames.Count();
558}
559
560RSLock * ShimStackWalk::GetShimLock()
561{
562 return m_pProcess->GetShimLock();
563}
564
565
566// ----------------------------------------------------------------------------
567// ShimStackWalk::AddChainEnum
568//
569// Description:
570// Add the specified ShimChainEnum to the head of the linked list of ShimChainEnums on the ShimStackWalk.
571//
572// Arguments:
573// * pChainEnum - the ShimChainEnum to be added
574//
575
576void ShimStackWalk::AddChainEnum(ShimChainEnum * pChainEnum)
577{
578 pChainEnum->SetNext(m_pChainEnumList);
579 if (m_pChainEnumList != NULL)
580 {
581 m_pChainEnumList->Release();
582 }
583
584 m_pChainEnumList = pChainEnum;
585 if (m_pChainEnumList != NULL)
586 {
587 m_pChainEnumList->AddRef();
588 }
589}
590
591// ----------------------------------------------------------------------------
592// ShimStackWalk::AddFrameEnum
593//
594// Description:
595// Add the specified ShimFrameEnum to the head of the linked list of ShimFrameEnums on the ShimStackWalk.
596//
597// Arguments:
598// * pFrameEnum - the ShimFrameEnum to be added
599//
600
601void ShimStackWalk::AddFrameEnum(ShimFrameEnum * pFrameEnum)
602{
603 pFrameEnum->SetNext(m_pFrameEnumList);
604 if (m_pFrameEnumList != NULL)
605 {
606 m_pFrameEnumList->Release();
607 }
608
609 m_pFrameEnumList = pFrameEnum;
610 if (m_pFrameEnumList != NULL)
611 {
612 m_pFrameEnumList->AddRef();
613 }
614}
615
616// Return the ICDThread associated with the current ShimStackWalk as a key for ShimStackWalkHashTableTraits.
617ICorDebugThread * ShimStackWalk::GetKey()
618{
619 return m_pThread;
620}
621
622// Hash a given ICDThread, which is used as the key for ShimStackWalkHashTableTraits.
623//static
624UINT32 ShimStackWalk::Hash(ICorDebugThread * pThread)
625{
626 // just return the pointer value
627 return (UINT32)(size_t)pThread;
628}
629
630// ----------------------------------------------------------------------------
631// ShimStackWalk::IsLeafFrame
632//
633// Description:
634// Check whether the specified frame is the leaf frame.
635//
636// Arguments:
637// * pFrame - frame to be checked
638//
639// Return Value:
640// Return TRUE if the specified frame is the leaf frame.
641// Return FALSE otherwise.
642//
643// Notes:
644// * The definition of the leaf frame in V2 is the frame at the leaf of the leaf chain.
645//
646
647BOOL ShimStackWalk::IsLeafFrame(ICorDebugFrame * pFrame)
648{
649 // check if we have any chain
650 if (GetChainCount() > 0)
651 {
652 // check if the leaf chain has any frame
653 if (GetChain(0)->GetLastFrameIndex() > 0)
654 {
655 return IsSameFrame(pFrame, GetFrame(0));
656 }
657 }
658 return FALSE;
659}
660
661// ----------------------------------------------------------------------------
662// ShimStackWalk::IsSameFrame
663//
664// Description:
665// Given two ICDFrames, check if they refer to the same frame.
666// This is much more than a pointer comparison. This function actually checks the frame address,
667// the stack pointer, etc. to make sure if the frames are the same.
668//
669// Arguments:
670// * pLeft - frame to be compared
671// * pRight - frame to be compared
672//
673// Return Value:
674// Return TRUE if the two ICDFrames represent the same frame.
675//
676
677BOOL ShimStackWalk::IsSameFrame(ICorDebugFrame * pLeft, ICorDebugFrame * pRight)
678{
679 HRESULT hr = E_FAIL;
680
681 // Quick check #1: If the pointers are the same then the two frames are the same (duh!).
682 if (pLeft == pRight)
683 {
684 return TRUE;
685 }
686
687 RSExtSmartPtr<ICorDebugNativeFrame> pLeftNativeFrame;
688 hr = pLeft->QueryInterface(IID_ICorDebugNativeFrame, reinterpret_cast<void **>(&pLeftNativeFrame));
689
690 if (SUCCEEDED(hr))
691 {
692 // The left frame is a stack frame.
693 RSExtSmartPtr<ICorDebugNativeFrame> pRightNativeFrame;
694 hr = pRight->QueryInterface(IID_ICorDebugNativeFrame, reinterpret_cast<void **>(&pRightNativeFrame));
695
696 if (FAILED(hr))
697 {
698 // The right frame is NOT a stack frame.
699 return FALSE;
700 }
701 else
702 {
703 // Quick check #2: If the IPs are different then the two frames are not the same (duh!).
704 ULONG32 leftOffset;
705 ULONG32 rightOffset;
706
707 hr = pLeftNativeFrame->GetIP(&leftOffset);
708 IfFailThrow(hr);
709
710 hr = pRightNativeFrame->GetIP(&rightOffset);
711 IfFailThrow(hr);
712
713 if (leftOffset != rightOffset)
714 {
715 return FALSE;
716 }
717
718 // real check
719 CORDB_ADDRESS leftStart;
720 CORDB_ADDRESS leftEnd;
721 CORDB_ADDRESS rightStart;
722 CORDB_ADDRESS rightEnd;
723
724 hr = pLeftNativeFrame->GetStackRange(&leftStart, &leftEnd);
725 IfFailThrow(hr);
726
727 hr = pRightNativeFrame->GetStackRange(&rightStart, &rightEnd);
728 IfFailThrow(hr);
729
730 return ((leftStart == rightStart) && (leftEnd == rightEnd));
731 }
732 }
733 else
734 {
735 RSExtSmartPtr<ICorDebugInternalFrame2> pLeftInternalFrame2;
736 hr = pLeft->QueryInterface(IID_ICorDebugInternalFrame2,
737 reinterpret_cast<void **>(&pLeftInternalFrame2));
738
739 if (SUCCEEDED(hr))
740 {
741 // The left frame is an internal frame.
742 RSExtSmartPtr<ICorDebugInternalFrame2> pRightInternalFrame2;
743 hr = pRight->QueryInterface(IID_ICorDebugInternalFrame2,
744 reinterpret_cast<void **>(&pRightInternalFrame2));
745
746 if (FAILED(hr))
747 {
748 return FALSE;
749 }
750 else
751 {
752 // The right frame is also an internal frame.
753
754 // Check the frame address.
755 CORDB_ADDRESS leftFrameAddr;
756 CORDB_ADDRESS rightFrameAddr;
757
758 hr = pLeftInternalFrame2->GetAddress(&leftFrameAddr);
759 IfFailThrow(hr);
760
761 hr = pRightInternalFrame2->GetAddress(&rightFrameAddr);
762 IfFailThrow(hr);
763
764 return (leftFrameAddr == rightFrameAddr);
765 }
766 }
767
768 return FALSE;
769 }
770}
771
772// This is the shim implementation of ICDThread::EnumerateChains().
773void ShimStackWalk::EnumerateChains(ICorDebugChainEnum ** ppChainEnum)
774{
775 NewHolder<ShimChainEnum> pChainEnum(new ShimChainEnum(this, GetShimLock()));
776
777 *ppChainEnum = pChainEnum;
778 (*ppChainEnum)->AddRef();
779 AddChainEnum(pChainEnum);
780
781 pChainEnum.SuppressRelease();
782}
783
784// This is the shim implementation of ICDThread::GetActiveChain().
785void ShimStackWalk::GetActiveChain(ICorDebugChain ** ppChain)
786{
787 if (GetChainCount() == 0)
788 {
789 *ppChain = NULL;
790 }
791 else
792 {
793 *ppChain = static_cast<ICorDebugChain *>(GetChain(0));
794 (*ppChain)->AddRef();
795 }
796}
797
798// This is the shim implementation of ICDThread::GetActiveFrame().
799void ShimStackWalk::GetActiveFrame(ICorDebugFrame ** ppFrame)
800{
801 //
802 // Make sure two things:
803 // 1) We have at least one frame.
804 // 2) The leaf frame is in the leaf chain, i.e. the leaf chain is not empty.
805 //
806 if ((GetFrameCount() == 0) ||
807 (GetChain(0)->GetLastFrameIndex() == 0))
808 {
809 *ppFrame = NULL;
810 }
811 else
812 {
813 *ppFrame = GetFrame(0);
814 (*ppFrame)->AddRef();
815 }
816}
817
818// This is the shim implementation of ICDThread::GetRegisterSet().
819void ShimStackWalk::GetActiveRegisterSet(ICorDebugRegisterSet ** ppRegisterSet)
820{
821 _ASSERTE(GetChainCount() != 0);
822 _ASSERTE(GetChain(0) != NULL);
823
824 // Return the register set of the leaf chain.
825 HRESULT hr = GetChain(0)->GetRegisterSet(ppRegisterSet);
826 IfFailThrow(hr);
827}
828
829// This is the shim implementation of ICDFrame::GetChain().
830void ShimStackWalk::GetChainForFrame(ICorDebugFrame * pFrame, ICorDebugChain ** ppChain)
831{
832 CORDB_ADDRESS frameStart;
833 CORDB_ADDRESS frameEnd;
834 IfFailThrow(pFrame->GetStackRange(&frameStart, &frameEnd));
835
836 for (UINT32 i = 0; i < GetChainCount(); i++)
837 {
838 ShimChain * pCurChain = GetChain(i);
839
840 CORDB_ADDRESS chainStart;
841 CORDB_ADDRESS chainEnd;
842 IfFailThrow(pCurChain->GetStackRange(&chainStart, &chainEnd));
843
844 if ((chainStart <= frameStart) && (frameEnd <= chainEnd))
845 {
846 // We need to check the next chain as well since some chains overlap at the boundary.
847 // If the current chain is the last one, no additional checking is required.
848 if (i < (GetChainCount() - 1))
849 {
850 ShimChain * pNextChain = GetChain(i + 1);
851
852 CORDB_ADDRESS nextChainStart;
853 CORDB_ADDRESS nextChainEnd;
854 IfFailThrow(pNextChain->GetStackRange(&nextChainStart, &nextChainEnd));
855
856 if ((nextChainStart <= frameStart) && (frameEnd <= nextChainEnd))
857 {
858 // The frame lies in the stack ranges of two chains. This can only happn at the boundary.
859 if (pCurChain->GetFirstFrameIndex() == pCurChain->GetLastFrameIndex())
860 {
861 // Make sure the next chain is not empty.
862 _ASSERTE(pNextChain->GetFirstFrameIndex() != pNextChain->GetLastFrameIndex());
863
864 // The current chain is empty, so the chain we want is the next one.
865 pCurChain = pNextChain;
866 }
867 // If the next chain is empty, then we'll just return the current chain and no additional
868 // work is needed. If the next chain is not empty, then we have more checking to do.
869 else if (pNextChain->GetFirstFrameIndex() != pNextChain->GetLastFrameIndex())
870 {
871 // Both chains are non-empty.
872 if (IsSameFrame(GetFrame(pNextChain->GetFirstFrameIndex()), pFrame))
873 {
874 // The same frame cannot be in both chains.
875 _ASSERTE(!IsSameFrame(GetFrame(pCurChain->GetLastFrameIndex() - 1), pFrame));
876 pCurChain = pNextChain;
877 }
878 else
879 {
880 _ASSERTE(IsSameFrame(GetFrame(pCurChain->GetLastFrameIndex() - 1), pFrame));
881 }
882 }
883 }
884 }
885
886 *ppChain = static_cast<ICorDebugChain *>(pCurChain);
887 (*ppChain)->AddRef();
888 return;
889 }
890 }
891}
892
893// This is the shim implementation of ICDFrame::GetCaller().
894void ShimStackWalk::GetCallerForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCallerFrame)
895{
896 for (UINT32 i = 0; i < GetChainCount(); i++)
897 {
898 ShimChain * pCurChain = GetChain(i);
899
900 for (UINT32 j = pCurChain->GetFirstFrameIndex(); j < pCurChain->GetLastFrameIndex(); j++)
901 {
902 if (IsSameFrame(GetFrame(j), pFrame))
903 {
904 // Check whether this is the last frame in the chain.
905 UINT32 callerFrameIndex = j + 1;
906 if (callerFrameIndex < pCurChain->GetLastFrameIndex())
907 {
908 *ppCallerFrame = static_cast<ICorDebugFrame *>(GetFrame(callerFrameIndex));
909 (*ppCallerFrame)->AddRef();
910 }
911 else
912 {
913 *ppCallerFrame = NULL;
914 }
915 return;
916 }
917 }
918 }
919}
920
921// This is the shim implementation of ICDFrame::GetCallee().
922void ShimStackWalk::GetCalleeForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCalleeFrame)
923{
924 for (UINT32 i = 0; i < GetChainCount(); i++)
925 {
926 ShimChain * pCurChain = GetChain(i);
927
928 for (UINT32 j = pCurChain->GetFirstFrameIndex(); j < pCurChain->GetLastFrameIndex(); j++)
929 {
930 if (IsSameFrame(GetFrame(j), pFrame))
931 {
932 // Check whether this is the first frame in the chain.
933 if (j > pCurChain->GetFirstFrameIndex())
934 {
935 UINT32 calleeFrameIndex = j - 1;
936 *ppCalleeFrame = static_cast<ICorDebugFrame *>(GetFrame(calleeFrameIndex));
937 (*ppCalleeFrame)->AddRef();
938 }
939 else
940 {
941 *ppCalleeFrame = NULL;
942 }
943 return;
944 }
945 }
946 }
947}
948
949FramePointer ShimStackWalk::GetFramePointerForChain(DT_CONTEXT * pContext)
950{
951 return FramePointer::MakeFramePointer(CORDbgGetSP(pContext));
952}
953
954FramePointer ShimStackWalk::GetFramePointerForChain(ICorDebugInternalFrame2 * pInternalFrame2)
955{
956 CORDB_ADDRESS frameAddr;
957 HRESULT hr = pInternalFrame2->GetAddress(&frameAddr);
958 IfFailThrow(hr);
959
960 return FramePointer::MakeFramePointer(reinterpret_cast<void *>(frameAddr));
961}
962
963CorDebugInternalFrameType ShimStackWalk::GetInternalFrameType(ICorDebugInternalFrame2 * pFrame2)
964{
965 HRESULT hr = E_FAIL;
966
967 // Retrieve the frame type of the internal frame.
968 RSExtSmartPtr<ICorDebugInternalFrame> pFrame;
969 hr = pFrame2->QueryInterface(IID_ICorDebugInternalFrame, reinterpret_cast<void **>(&pFrame));
970 IfFailThrow(hr);
971
972 CorDebugInternalFrameType type;
973 hr = pFrame->GetFrameType(&type);
974 IfFailThrow(hr);
975
976 return type;
977}
978
979// ----------------------------------------------------------------------------
980// ShimStackWalk::AppendFrame
981//
982// Description:
983// Append the specified frame to the array and increment the counter.
984//
985// Arguments:
986// * pFrame - the frame to be added
987// * pStackWalkInfo - contains information of the stackwalk
988//
989
990void ShimStackWalk::AppendFrame(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo)
991{
992 // grow the
993 ICorDebugFrame ** ppFrame = m_stackFrames.AppendThrowing();
994
995 // Be careful of the AddRef() below. Once we do the addref, we need to save the pointer and
996 // explicitly release it.
997 *ppFrame = pFrame;
998 (*ppFrame)->AddRef();
999
1000 pStackWalkInfo->m_cFrame += 1;
1001}
1002
1003// ----------------------------------------------------------------------------
1004// Refer to comment of the overloaded function.
1005//
1006
1007void ShimStackWalk::AppendFrame(ICorDebugInternalFrame2 * pInternalFrame2, StackWalkInfo * pStackWalkInfo)
1008{
1009 RSExtSmartPtr<ICorDebugFrame> pFrame;
1010 HRESULT hr = pInternalFrame2->QueryInterface(IID_ICorDebugFrame, reinterpret_cast<void **>(&pFrame));
1011 IfFailThrow(hr);
1012
1013 AppendFrame(pFrame, pStackWalkInfo);
1014}
1015
1016// ----------------------------------------------------------------------------
1017// ShimStackWalk::AppendChainWorker
1018//
1019// Description:
1020// Append the specified chain to the array.
1021//
1022// Arguments:
1023// * pStackWalkInfo - contains information regarding the stackwalk
1024// * pLeafContext - the leaf CONTEXT of the chain to be added
1025// * fpRoot - the root boundary of the chain to be added
1026// * chainReason - the chain reason of the chain to be added
1027// * fIsManagedChain - whether the chain to be added is managed
1028//
1029
1030void ShimStackWalk::AppendChainWorker(StackWalkInfo * pStackWalkInfo,
1031 DT_CONTEXT * pLeafContext,
1032 FramePointer fpRoot,
1033 CorDebugChainReason chainReason,
1034 BOOL fIsManagedChain)
1035{
1036 // first, create the chain
1037 NewHolder<ShimChain> pChain(new ShimChain(this,
1038 pLeafContext,
1039 fpRoot,
1040 pStackWalkInfo->m_cChain,
1041 pStackWalkInfo->m_firstFrameInChain,
1042 pStackWalkInfo->m_cFrame,
1043 chainReason,
1044 fIsManagedChain,
1045 GetShimLock()));
1046
1047 // Grow the array and add the newly created chain.
1048 // Once we call AddRef() we own the ShimChain and need to release it.
1049 ShimChain ** ppChain = m_stackChains.AppendThrowing();
1050 *ppChain = pChain;
1051 (*ppChain)->AddRef();
1052
1053 // update the counters on the StackWalkInfo
1054 pStackWalkInfo->m_cChain += 1;
1055 pStackWalkInfo->m_firstFrameInChain = pStackWalkInfo->m_cFrame;
1056
1057 // If all goes well, suppress the release so that the ShimChain won't go away.
1058 pChain.SuppressRelease();
1059}
1060
1061// ----------------------------------------------------------------------------
1062// ShimStackWalk::AppendChain
1063//
1064// Description:
1065// Append the chain to the array. This function is also smart enough to send an enter-managed chain
1066// if necessary. In other words, this function may append two chains at the same time.
1067//
1068// Arguments:
1069// * pChainInfo - information on the chain to be added
1070// * pStackWalkInfo - information regarding the current stackwalk
1071//
1072
1073void ShimStackWalk::AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo)
1074{
1075 // Check if the chain to be added is managed or not.
1076 BOOL fManagedChain = FALSE;
1077 if ((pChainInfo->m_reason == CHAIN_ENTER_MANAGED) ||
1078 (pChainInfo->m_reason == CHAIN_CLASS_INIT) ||
1079 (pChainInfo->m_reason == CHAIN_SECURITY) ||
1080 (pChainInfo->m_reason == CHAIN_FUNC_EVAL))
1081 {
1082 fManagedChain = TRUE;
1083 }
1084
1085 DT_CONTEXT * pChainContext = NULL;
1086 if (fManagedChain)
1087 {
1088 // The chain to be added is managed itself. So we don't need to send an enter-managed chain.
1089 pChainInfo->m_fNeedEnterManagedChain = false;
1090 pChainContext = &(pChainInfo->m_leafManagedContext);
1091 }
1092 else
1093 {
1094 // The chain to be added is unmanaged. Check if we need to send an enter-managed chain.
1095 if (pChainInfo->m_fNeedEnterManagedChain)
1096 {
1097 // We need to send an extra enter-managed chain.
1098 _ASSERTE(pChainInfo->m_fLeafNativeContextIsValid);
1099 BYTE * sp = reinterpret_cast<BYTE *>(CORDbgGetSP(&(pChainInfo->m_leafNativeContext)));
1100#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_)
1101 // Dev11 324806: on ARM we use the caller's SP for a frame's ending delimiter so we cannot
1102 // subtract 4 bytes from the chain's ending delimiter else the frame might never be in range.
1103 // TODO: revisit overlapping ranges on ARM, it would be nice to make it consistent with the other architectures.
1104 sp -= sizeof(LPVOID);
1105#endif
1106 FramePointer fp = FramePointer::MakeFramePointer(sp);
1107
1108 AppendChainWorker(pStackWalkInfo,
1109 &(pChainInfo->m_leafManagedContext),
1110 fp,
1111 CHAIN_ENTER_MANAGED,
1112 TRUE);
1113
1114 pChainInfo->m_fNeedEnterManagedChain = false;
1115 }
1116 _ASSERTE(pChainInfo->m_fLeafNativeContextIsValid);
1117 pChainContext = &(pChainInfo->m_leafNativeContext);
1118 }
1119
1120 // Add the actual chain.
1121 AppendChainWorker(pStackWalkInfo,
1122 pChainContext,
1123 pChainInfo->m_rootFP,
1124 pChainInfo->m_reason,
1125 fManagedChain);
1126}
1127
1128// ----------------------------------------------------------------------------
1129// ShimStackWalk::SaveChainContext
1130//
1131// Description:
1132// Save the current CONTEXT on the ICDStackWalk into the specified CONTEXT. Also update the root end
1133// of the chain on the ChainInfo.
1134//
1135// Arguments:
1136// * pSW - the ICDStackWalk for the current stackwalk
1137// * pChainInfo - the ChainInfo keeping track of the current chain
1138// * pContext - the destination CONTEXT
1139//
1140
1141void ShimStackWalk::SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, DT_CONTEXT * pContext)
1142{
1143 HRESULT hr = pSW->GetContext(CONTEXT_FULL,
1144 sizeof(*pContext),
1145 NULL,
1146 reinterpret_cast<BYTE *>(pContext));
1147 IfFailThrow(hr);
1148
1149 pChainInfo->m_rootFP = GetFramePointerForChain(pContext);
1150}
1151
1152// ----------------------------------------------------------------------------
1153// ShimStackWalk::CheckInternalFrame
1154//
1155// Description:
1156// Check whether the next frame to be processed should be the next internal frame or the next stack frame.
1157//
1158// Arguments:
1159// * pNextStackFrame - the next stack frame
1160// * pStackWalkInfo - information regarding the current stackwalk; also contains the next internal frame
1161// * pThread3 - the thread we are walking
1162// * pSW - the current stackwalk
1163//
1164// Return Value:
1165// Return TRUE if we should process an internal frame next.
1166//
1167
1168BOOL ShimStackWalk::CheckInternalFrame(ICorDebugFrame * pNextStackFrame,
1169 StackWalkInfo * pStackWalkInfo,
1170 ICorDebugThread3 * pThread3,
1171 ICorDebugStackWalk * pSW)
1172{
1173 _ASSERTE(pNextStackFrame != NULL);
1174 _ASSERTE(!pStackWalkInfo->ExhaustedAllInternalFrames());
1175
1176 HRESULT hr = E_FAIL;
1177 BOOL fIsInternalFrameFirst = FALSE;
1178
1179 // Special handling for the case where a managed method contains a M2U internal frame.
1180 // Normally only IL stubs contain M2U internal frames, but we may have inlined pinvoke calls in
1181 // optimized code. In that case, we would have an InlinedCallFrame in a normal managed method on x86.
1182 // On WIN64, we would have a normal NDirectMethodFrame* in a normal managed method.
1183 if (pStackWalkInfo->m_internalFrameType == STUBFRAME_M2U)
1184 {
1185 // create a temporary ICDStackWalk
1186 RSExtSmartPtr<ICorDebugStackWalk> pTmpSW;
1187 hr = pThread3->CreateStackWalk(&pTmpSW);
1188 IfFailThrow(hr);
1189
1190 // retrieve the current CONTEXT
1191 DT_CONTEXT ctx;
1192 ctx.ContextFlags = DT_CONTEXT_FULL;
1193 hr = pSW->GetContext(ctx.ContextFlags, sizeof(ctx), NULL, reinterpret_cast<BYTE *>(&ctx));
1194 IfFailThrow(hr);
1195
1196 // set the CONTEXT on the temporary ICDStackWalk
1197 hr = pTmpSW->SetContext(SET_CONTEXT_FLAG_ACTIVE_FRAME, sizeof(ctx), reinterpret_cast<BYTE *>(&ctx));
1198 IfFailThrow(hr);
1199
1200 // unwind the temporary ICDStackWalk by one frame
1201 hr = pTmpSW->Next();
1202 IfFailThrow(hr);
1203
1204 // Unwinding from a managed stack frame will land us either in a managed stack frame or a native
1205 // stack frame. In either case, we have a CONTEXT.
1206 hr = pTmpSW->GetContext(ctx.ContextFlags, sizeof(ctx), NULL, reinterpret_cast<BYTE *>(&ctx));
1207 IfFailThrow(hr);
1208
1209 // Get the SP from the CONTEXT. This is the caller SP.
1210 CORDB_ADDRESS sp = PTR_TO_CORDB_ADDRESS(CORDbgGetSP(&ctx));
1211
1212 // get the frame address
1213 CORDB_ADDRESS frameAddr = 0;
1214 hr = pStackWalkInfo->GetCurrentInternalFrame()->GetAddress(&frameAddr);
1215 IfFailThrow(hr);
1216
1217 // Compare the frame address with the caller SP of the stack frame for the IL method without metadata.
1218 fIsInternalFrameFirst = (frameAddr < sp);
1219 }
1220 else
1221 {
1222 hr = pStackWalkInfo->GetCurrentInternalFrame()->IsCloserToLeaf(pNextStackFrame, &fIsInternalFrameFirst);
1223 IfFailThrow(hr);
1224 }
1225
1226 return fIsInternalFrameFirst;
1227}
1228
1229// ----------------------------------------------------------------------------
1230// ShimStackWalk::ConvertInternalFrameToDynamicMethod
1231//
1232// Description:
1233// In V2, PrestubMethodFrames (PMFs) are exposed as one of two things: a chain of type
1234// CHAIN_CLASS_INIT in most cases, or an internal frame of type STUBFRAME_LIGHTWEIGHT_FUNCTION if
1235// the method being jitted is a dynamic method. On the other hand, in Arrowhead, we consistently expose
1236// PMFs as STUBFRAME_JIT_COMPILATION. This function determines if a STUBFRAME_JIT_COMPILATION should
1237// be exposed, and, if so, how to expose it. In the case where conversion is necessary, this function
1238// also updates the stackwalk information with the converted frame.
1239//
1240// Here are the rules for conversion:
1241// 1) If the method being jitted is an IL stub, we set the converted frame to NULL, and we return TRUE.
1242// 2) If the method being jitted is an LCG method, we set the converted frame to a
1243// STUBFRAME_LIGHTWEIGHT_FUNCTION, and we return NULL.
1244// 3) Otherwise, we return FALSE.
1245//
1246// Arguments:
1247// * pStackWalkInfo - information about the current stackwalk
1248//
1249// Return Value:
1250// Return TRUE if a conversion has taken place.
1251//
1252
1253BOOL ShimStackWalk::ConvertInternalFrameToDynamicMethod(StackWalkInfo * pStackWalkInfo)
1254{
1255 HRESULT hr = E_FAIL;
1256
1257 // QI for ICDFrame
1258 RSExtSmartPtr<ICorDebugFrame> pOriginalFrame;
1259 hr = pStackWalkInfo->GetCurrentInternalFrame()->QueryInterface(
1260 IID_ICorDebugFrame,
1261 reinterpret_cast<void **>(&pOriginalFrame));
1262 IfFailThrow(hr);
1263
1264 // Ask the RS to do the real work.
1265 CordbThread * pThread = static_cast<CordbThread *>(m_pThread.GetValue());
1266 pStackWalkInfo->m_fHasConvertedFrame = (TRUE == pThread->ConvertFrameForILMethodWithoutMetadata(
1267 pOriginalFrame,
1268 &(pStackWalkInfo->m_pConvertedInternalFrame2)));
1269
1270 if (pStackWalkInfo->HasConvertedFrame())
1271 {
1272 // We have a conversion.
1273 if (pStackWalkInfo->GetCurrentInternalFrame() != NULL)
1274 {
1275 // We have a converted internal frame, so let's update the internal frame type.
1276 RSExtSmartPtr<ICorDebugInternalFrame> pInternalFrame;
1277 hr = pStackWalkInfo->GetCurrentInternalFrame()->QueryInterface(
1278 IID_ICorDebugInternalFrame,
1279 reinterpret_cast<void **>(&pInternalFrame));
1280 IfFailThrow(hr);
1281
1282 hr = pInternalFrame->GetFrameType(&(pStackWalkInfo->m_internalFrameType));
1283 IfFailThrow(hr);
1284 }
1285 else
1286 {
1287 // The method being jitted is an IL stub, so let's not expose it.
1288 pStackWalkInfo->m_internalFrameType = STUBFRAME_NONE;
1289 }
1290 }
1291
1292 return pStackWalkInfo->HasConvertedFrame();
1293}
1294
1295// ----------------------------------------------------------------------------
1296// ShimStackWalk::ConvertInternalFrameToDynamicMethod
1297//
1298// Description:
1299// In V2, LCG methods are exposed as internal frames of type STUBFRAME_LIGHTWEIGHT_FUNCTION. However,
1300// in Arrowhead, LCG methods are exposed as first-class stack frames, not internal frames. Thus,
1301// the shim needs to convert an ICDNativeFrame for a dynamic method in Arrowhead to an
1302// ICDInternalFrame of type STUBFRAME_LIGHTWEIGHT_FUNCTION in V2. Furthermore, IL stubs are not exposed
1303// in V2 at all.
1304//
1305// Here are the rules for conversion:
1306// 1) If the stack frame is for an IL stub, we set the converted frame to NULL, and we return TRUE.
1307// 2) If the stack frame is for an LCG method, we set the converted frame to a
1308// STUBFRAME_LIGHTWEIGHT_FUNCTION, and we return NULL.
1309// 3) Otherwise, we return FALSE.
1310//
1311// Arguments:
1312// * pFrame - the frame to be checked and converted if necessary
1313// * pStackWalkInfo - information about the current stackwalk
1314//
1315// Return Value:
1316// Return TRUE if a conversion has taken place.
1317//
1318
1319BOOL ShimStackWalk::ConvertStackFrameToDynamicMethod(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo)
1320{
1321 // If this is not a dynamic method (i.e. LCG method or IL stub), then we don't need to do a conversion.
1322 if (!IsILFrameWithoutMetadata(pFrame))
1323 {
1324 return FALSE;
1325 }
1326
1327 // Ask the RS to do the real work.
1328 CordbThread * pThread = static_cast<CordbThread *>(m_pThread.GetValue());
1329 pStackWalkInfo->m_fHasConvertedFrame = (TRUE == pThread->ConvertFrameForILMethodWithoutMetadata(
1330 pFrame,
1331 &(pStackWalkInfo->m_pConvertedInternalFrame2)));
1332
1333 return pStackWalkInfo->HasConvertedFrame();
1334}
1335
1336// ----------------------------------------------------------------------------
1337// ShimStackWalk::TrackUMChain
1338//
1339// Description:
1340// Keep track of enter-unmanaged chains. Extend or cancel the chain as necesasry.
1341//
1342// Arguments:
1343// * pChainInfo - information on the current chain we are tracking
1344// * pStackWalkInfo - information regarding the current stackwalk
1345//
1346// Notes:
1347// * This logic is based on code:TrackUMChain on the LS.
1348//
1349
1350void ShimStackWalk::TrackUMChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo)
1351{
1352 if (!pChainInfo->IsTrackingUMChain())
1353 {
1354 if (pStackWalkInfo->m_fProcessingInternalFrame)
1355 {
1356 if (pStackWalkInfo->m_internalFrameType == STUBFRAME_M2U)
1357 {
1358 // If we hit an M2U frame out in the wild, convert it to an enter-unmanaged chain.
1359
1360 // We can't hit an M2U frame without hitting a native stack frame
1361 // first (we filter those). We should have already saved the CONTEXT.
1362 // So just update the chain reason.
1363 pChainInfo->m_reason = CHAIN_ENTER_UNMANAGED;
1364 }
1365 }
1366 }
1367
1368 BOOL fCreateUMChain = FALSE;
1369 if (pChainInfo->IsTrackingUMChain())
1370 {
1371 if (pStackWalkInfo->m_fProcessingInternalFrame)
1372 {
1373 // Extend the root end of the unmanaged chain.
1374 pChainInfo->m_rootFP = GetFramePointerForChain(pStackWalkInfo->GetCurrentInternalFrame());
1375
1376 // Sometimes we may not want to show an UM chain b/c we know it's just
1377 // code inside of mscorwks. (Eg: Funcevals & AD transitions both fall into this category).
1378 // These are perfectly valid UM chains and we could give them if we wanted to.
1379 if ((pStackWalkInfo->m_internalFrameType == STUBFRAME_APPDOMAIN_TRANSITION) ||
1380 (pStackWalkInfo->m_internalFrameType == STUBFRAME_FUNC_EVAL))
1381 {
1382 pChainInfo->CancelUMChain();
1383 }
1384 else if (pStackWalkInfo->m_internalFrameType == STUBFRAME_M2U)
1385 {
1386 // If we hit an M2U frame, then go ahead and dispatch the UM chain now.
1387 // This will likely also be an exit frame.
1388 fCreateUMChain = TRUE;
1389 }
1390 else if ((pStackWalkInfo->m_internalFrameType == STUBFRAME_CLASS_INIT) ||
1391 (pStackWalkInfo->m_internalFrameType == STUBFRAME_EXCEPTION) ||
1392 (pStackWalkInfo->m_internalFrameType == STUBFRAME_SECURITY) ||
1393 (pStackWalkInfo->m_internalFrameType == STUBFRAME_JIT_COMPILATION))
1394 {
1395 fCreateUMChain = TRUE;
1396 }
1397 }
1398 else
1399 {
1400 // If we hit a managed stack frame when we are processing an unmanaged chain, then
1401 // the chain is done.
1402 fCreateUMChain = TRUE;
1403 }
1404 }
1405
1406 if (fCreateUMChain)
1407 {
1408 // check whether we get any stack range
1409 _ASSERTE(pChainInfo->m_fLeafNativeContextIsValid);
1410 FramePointer fpLeaf = GetFramePointerForChain(&(pChainInfo->m_leafNativeContext));
1411
1412 // Don't bother creating an unmanaged chain if the stack range is empty.
1413 if (fpLeaf != pChainInfo->m_rootFP)
1414 {
1415 AppendChain(pChainInfo, pStackWalkInfo);
1416 }
1417 pChainInfo->CancelUMChain();
1418 }
1419}
1420
1421BOOL ShimStackWalk::IsV3FrameType(CorDebugInternalFrameType type)
1422{
1423 // These frame types are either new in Arrowhead or not used in V2.
1424 if ((type == STUBFRAME_INTERNALCALL) ||
1425 (type == STUBFRAME_CLASS_INIT) ||
1426 (type == STUBFRAME_EXCEPTION) ||
1427 (type == STUBFRAME_SECURITY) ||
1428 (type == STUBFRAME_JIT_COMPILATION))
1429 {
1430 return TRUE;
1431 }
1432 else
1433 {
1434 return FALSE;
1435 }
1436}
1437
1438// Check whether a stack frame is for a dynamic method. The way to tell is if the stack frame has
1439// an ICDNativeFrame but no ICDILFrame.
1440BOOL ShimStackWalk::IsILFrameWithoutMetadata(ICorDebugFrame * pFrame)
1441{
1442 HRESULT hr = E_FAIL;
1443
1444 RSExtSmartPtr<ICorDebugNativeFrame> pNativeFrame;
1445 hr = pFrame->QueryInterface(IID_ICorDebugNativeFrame, reinterpret_cast<void **>(&pNativeFrame));
1446 IfFailThrow(hr);
1447
1448 if (pNativeFrame != NULL)
1449 {
1450 RSExtSmartPtr<ICorDebugILFrame> pILFrame;
1451 hr = pFrame->QueryInterface(IID_ICorDebugILFrame, reinterpret_cast<void **>(&pILFrame));
1452
1453 if (FAILED(hr) || (pILFrame == NULL))
1454 {
1455 return TRUE;
1456 }
1457 }
1458
1459 return FALSE;
1460}
1461
1462ShimStackWalk::StackWalkInfo::StackWalkInfo()
1463 : m_cChain(0),
1464 m_cFrame(0),
1465 m_firstFrameInChain(0),
1466 m_cInternalFrames(0),
1467 m_curInternalFrame(0),
1468 m_internalFrameType(STUBFRAME_NONE),
1469 m_fExhaustedAllStackFrames(false),
1470 m_fProcessingInternalFrame(false),
1471 m_fSkipChain(false),
1472 m_fLeafFrame(true),
1473 m_fHasConvertedFrame(false)
1474{
1475 m_pChildFrame.Assign(NULL);
1476 m_pConvertedInternalFrame2.Assign(NULL);
1477}
1478
1479ShimStackWalk::StackWalkInfo::~StackWalkInfo()
1480{
1481 if (m_pChildFrame != NULL)
1482 {
1483 m_pChildFrame.Clear();
1484 }
1485
1486 if (m_pConvertedInternalFrame2 != NULL)
1487 {
1488 m_pConvertedInternalFrame2.Clear();
1489 }
1490
1491 if (!m_ppInternalFrame2.IsEmpty())
1492 {
1493 m_ppInternalFrame2.Clear();
1494 }
1495}
1496
1497void ShimStackWalk::StackWalkInfo::ResetForNextFrame()
1498{
1499 m_pConvertedInternalFrame2.Clear();
1500 m_internalFrameType = STUBFRAME_NONE;
1501 m_fProcessingInternalFrame = false;
1502 m_fSkipChain = false;
1503 m_fHasConvertedFrame = false;
1504}
1505
1506// Check whether we have exhausted both internal frames and stack frames.
1507bool ShimStackWalk::StackWalkInfo::ExhaustedAllFrames()
1508{
1509 return (ExhaustedAllStackFrames() && ExhaustedAllInternalFrames());
1510}
1511
1512bool ShimStackWalk::StackWalkInfo::ExhaustedAllStackFrames()
1513{
1514 return m_fExhaustedAllStackFrames;
1515}
1516
1517bool ShimStackWalk::StackWalkInfo::ExhaustedAllInternalFrames()
1518{
1519 return (m_curInternalFrame == m_cInternalFrames);
1520}
1521
1522ICorDebugInternalFrame2 * ShimStackWalk::StackWalkInfo::GetCurrentInternalFrame()
1523{
1524 _ASSERTE(!ExhaustedAllInternalFrames() || HasConvertedFrame());
1525
1526 if (HasConvertedFrame())
1527 {
1528 return m_pConvertedInternalFrame2;
1529 }
1530 else
1531 {
1532 return m_ppInternalFrame2[m_curInternalFrame];
1533 }
1534}
1535
1536BOOL ShimStackWalk::StackWalkInfo::IsLeafFrame()
1537{
1538 return m_fLeafFrame;
1539}
1540
1541BOOL ShimStackWalk::StackWalkInfo::IsSkippingFrame()
1542{
1543 return (m_pChildFrame != NULL);
1544}
1545
1546BOOL ShimStackWalk::StackWalkInfo::HasConvertedFrame()
1547{
1548 return m_fHasConvertedFrame;
1549}
1550
1551
1552ShimChain::ShimChain(ShimStackWalk * pSW,
1553 DT_CONTEXT * pContext,
1554 FramePointer fpRoot,
1555 UINT32 chainIndex,
1556 UINT32 frameStartIndex,
1557 UINT32 frameEndIndex,
1558 CorDebugChainReason chainReason,
1559 BOOL fIsManaged,
1560 RSLock * pShimLock)
1561 : m_context(*pContext),
1562 m_fpRoot(fpRoot),
1563 m_pStackWalk(pSW),
1564 m_refCount(0),
1565 m_chainIndex(chainIndex),
1566 m_frameStartIndex(frameStartIndex),
1567 m_frameEndIndex(frameEndIndex),
1568 m_chainReason(chainReason),
1569 m_fIsManaged(fIsManaged),
1570 m_fIsNeutered(FALSE),
1571 m_pShimLock(pShimLock)
1572{
1573}
1574
1575ShimChain::~ShimChain()
1576{
1577 _ASSERTE(IsNeutered());
1578}
1579
1580void ShimChain::Neuter()
1581{
1582 m_fIsNeutered = TRUE;
1583}
1584
1585BOOL ShimChain::IsNeutered()
1586{
1587 return m_fIsNeutered;
1588}
1589
1590ULONG STDMETHODCALLTYPE ShimChain::AddRef()
1591{
1592 return InterlockedIncrement((LONG *)&m_refCount);
1593}
1594
1595ULONG STDMETHODCALLTYPE ShimChain::Release()
1596{
1597 LONG newRefCount = InterlockedDecrement((LONG *)&m_refCount);
1598 _ASSERTE(newRefCount >= 0);
1599
1600 if (newRefCount == 0)
1601 {
1602 delete this;
1603 }
1604 return newRefCount;
1605}
1606
1607HRESULT ShimChain::QueryInterface(REFIID id, void ** pInterface)
1608{
1609 if (id == IID_ICorDebugChain)
1610 {
1611 *pInterface = static_cast<ICorDebugChain *>(this);
1612 }
1613 else if (id == IID_IUnknown)
1614 {
1615 *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugChain *>(this));
1616 }
1617 else
1618 {
1619 *pInterface = NULL;
1620 return E_NOINTERFACE;
1621 }
1622
1623 AddRef();
1624 return S_OK;
1625}
1626
1627// Returns the thread to which this chain belongs.
1628HRESULT ShimChain::GetThread(ICorDebugThread ** ppThread)
1629{
1630 RSLockHolder lockHolder(m_pShimLock);
1631 FAIL_IF_NEUTERED(this);
1632 VALIDATE_POINTER_TO_OBJECT(ppThread, ICorDebugThread **);
1633
1634 *ppThread = m_pStackWalk->GetThread();
1635 (*ppThread)->AddRef();
1636
1637 return S_OK;
1638}
1639
1640// Get the range on the stack that this chain matches against.
1641// pStart is the leafmost; pEnd is the rootmost.
1642// This is particularly used in interop-debugging to get native stack traces
1643// for the UM portions of the stack
1644HRESULT ShimChain::GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd)
1645{
1646 HRESULT hr = S_OK;
1647 EX_TRY
1648 {
1649 THROW_IF_NEUTERED(this);
1650
1651 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pStart, CORDB_ADDRESS *);
1652 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pEnd, CORDB_ADDRESS *);
1653
1654 // Return the leafmost end of the stack range.
1655 // The leafmost end is represented by the register set.
1656 if (pStart)
1657 {
1658 *pStart = PTR_TO_CORDB_ADDRESS(CORDbgGetSP(&m_context));
1659 }
1660
1661 // Return the rootmost end of the stack range. It is represented by the frame pointer of the chain.
1662 if (pEnd)
1663 {
1664 *pEnd = PTR_TO_CORDB_ADDRESS(m_fpRoot.GetSPValue());
1665 }
1666 }
1667 EX_CATCH_HRESULT(hr);
1668 return hr;
1669}
1670
1671HRESULT ShimChain::GetContext(ICorDebugContext ** ppContext)
1672{
1673 return E_NOTIMPL;
1674}
1675
1676// Return the next chain which is closer to the root.
1677// Currently this is just a wrapper over GetNext().
1678HRESULT ShimChain::GetCaller(ICorDebugChain ** ppChain)
1679{
1680 RSLockHolder lockHolder(m_pShimLock);
1681 FAIL_IF_NEUTERED(this);
1682 VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **);
1683
1684 return GetNext(ppChain);
1685}
1686
1687// Return the previous chain which is closer to the leaf.
1688// Currently this is just a wrapper over GetPrevious().
1689HRESULT ShimChain::GetCallee(ICorDebugChain ** ppChain)
1690{
1691 RSLockHolder lockHolder(m_pShimLock);
1692 FAIL_IF_NEUTERED(this);
1693 VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **);
1694
1695 return GetPrevious(ppChain);
1696}
1697
1698// Return the previous chain which is closer to the leaf.
1699HRESULT ShimChain::GetPrevious(ICorDebugChain ** ppChain)
1700{
1701 RSLockHolder lockHolder(m_pShimLock);
1702 FAIL_IF_NEUTERED(this);
1703 VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **);
1704
1705 *ppChain = NULL;
1706 if (m_chainIndex != 0)
1707 {
1708 *ppChain = m_pStackWalk->GetChain(m_chainIndex - 1);
1709 }
1710
1711 if (*ppChain != NULL)
1712 {
1713 (*ppChain)->AddRef();
1714 }
1715
1716 return S_OK;
1717}
1718
1719// Return the next chain which is closer to the root.
1720HRESULT ShimChain::GetNext(ICorDebugChain ** ppChain)
1721{
1722 RSLockHolder lockHolder(m_pShimLock);
1723 FAIL_IF_NEUTERED(this);
1724 VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **);
1725
1726 *ppChain = m_pStackWalk->GetChain(m_chainIndex + 1);
1727 if (*ppChain != NULL)
1728 {
1729 (*ppChain)->AddRef();
1730 }
1731
1732 return S_OK;
1733}
1734
1735// Return whether the chain contains frames running managed code.
1736HRESULT ShimChain::IsManaged(BOOL * pManaged)
1737{
1738 RSLockHolder lockHolder(m_pShimLock);
1739 FAIL_IF_NEUTERED(this);
1740 VALIDATE_POINTER_TO_OBJECT(pManaged, BOOL *);
1741
1742 *pManaged = m_fIsManaged;
1743
1744 return S_OK;
1745}
1746
1747// Return an enumerator to iterate through the frames contained in this chain.
1748HRESULT ShimChain::EnumerateFrames(ICorDebugFrameEnum ** ppFrames)
1749{
1750 RSLockHolder lockHolder(m_pShimLock);
1751 FAIL_IF_NEUTERED(this);
1752 VALIDATE_POINTER_TO_OBJECT(ppFrames, ICorDebugFrameEnum **);
1753
1754 HRESULT hr = S_OK;
1755 EX_TRY
1756 {
1757 ShimStackWalk * pSW = GetShimStackWalk();
1758 NewHolder<ShimFrameEnum> pFrameEnum(new ShimFrameEnum(pSW, this, m_frameStartIndex, m_frameEndIndex, m_pShimLock));
1759
1760 *ppFrames = pFrameEnum;
1761 (*ppFrames)->AddRef();
1762
1763 // link the new ShimFramEnum into the list on the ShimStackWalk
1764 pSW->AddFrameEnum(pFrameEnum);
1765
1766 pFrameEnum.SuppressRelease();
1767 }
1768 EX_CATCH_HRESULT(hr);
1769
1770 return hr;
1771}
1772
1773// Return an enumerator to iterate through the frames contained in this chain.
1774// Note that this function will only succeed if the cached stack trace is valid.
1775HRESULT ShimChain::GetActiveFrame(ICorDebugFrame ** ppFrame)
1776{
1777 RSLockHolder lockHolder(m_pShimLock);
1778 FAIL_IF_NEUTERED(this);
1779 VALIDATE_POINTER_TO_OBJECT(ppFrame, ICorDebugFrame **);
1780 (*ppFrame) = NULL;
1781
1782 HRESULT hr = S_OK;
1783
1784 // Chains may be empty, so they have no active frame.
1785 if (m_frameStartIndex == m_frameEndIndex)
1786 {
1787 *ppFrame = NULL;
1788 }
1789 else
1790 {
1791 *ppFrame = m_pStackWalk->GetFrame(m_frameStartIndex);
1792 (*ppFrame)->AddRef();
1793 }
1794
1795 return hr;
1796}
1797
1798// Return the register set of the leaf end of the chain
1799HRESULT ShimChain::GetRegisterSet(ICorDebugRegisterSet ** ppRegisters)
1800{
1801 FAIL_IF_NEUTERED(this);
1802 VALIDATE_POINTER_TO_OBJECT(ppRegisters, ICorDebugRegisterSet **);
1803
1804 HRESULT hr = S_OK;
1805 EX_TRY
1806 {
1807 CordbThread * pThread = static_cast<CordbThread *>(m_pStackWalk->GetThread());
1808
1809 // This is a private hook for calling back into the RS. Alternatively, we could have created a
1810 // ShimRegisterSet, but that's too much work for now.
1811 pThread->CreateCordbRegisterSet(&m_context,
1812 (m_chainIndex == 0),
1813 m_chainReason,
1814 ppRegisters);
1815 }
1816 EX_CATCH_HRESULT(hr);
1817 return hr;
1818}
1819
1820// Return the chain reason
1821HRESULT ShimChain::GetReason(CorDebugChainReason * pReason)
1822{
1823 RSLockHolder lockHolder(m_pShimLock);
1824 FAIL_IF_NEUTERED(this);
1825 VALIDATE_POINTER_TO_OBJECT(pReason, CorDebugChainReason *);
1826
1827 *pReason = m_chainReason;
1828
1829 return S_OK;
1830}
1831
1832ShimStackWalk * ShimChain::GetShimStackWalk()
1833{
1834 return m_pStackWalk;
1835}
1836
1837UINT32 ShimChain::GetFirstFrameIndex()
1838{
1839 return this->m_frameStartIndex;
1840}
1841
1842UINT32 ShimChain::GetLastFrameIndex()
1843{
1844 return this->m_frameEndIndex;
1845}
1846
1847
1848ShimChainEnum::ShimChainEnum(ShimStackWalk * pSW, RSLock * pShimLock)
1849 : m_pStackWalk(pSW),
1850 m_pNext(NULL),
1851 m_currentChainIndex(0),
1852 m_refCount(0),
1853 m_fIsNeutered(FALSE),
1854 m_pShimLock(pShimLock)
1855{
1856}
1857
1858ShimChainEnum::~ShimChainEnum()
1859{
1860 _ASSERTE(IsNeutered());
1861}
1862
1863void ShimChainEnum::Neuter()
1864{
1865 if (IsNeutered())
1866 {
1867 return;
1868 }
1869
1870 m_fIsNeutered = TRUE;
1871}
1872
1873BOOL ShimChainEnum::IsNeutered()
1874{
1875 return m_fIsNeutered;
1876}
1877
1878
1879ULONG STDMETHODCALLTYPE ShimChainEnum::AddRef()
1880{
1881 return InterlockedIncrement((LONG *)&m_refCount);
1882}
1883
1884ULONG STDMETHODCALLTYPE ShimChainEnum::Release()
1885{
1886 LONG newRefCount = InterlockedDecrement((LONG *)&m_refCount);
1887 _ASSERTE(newRefCount >= 0);
1888
1889 if (newRefCount == 0)
1890 {
1891 delete this;
1892 }
1893 return newRefCount;
1894}
1895
1896HRESULT ShimChainEnum::QueryInterface(REFIID id, void ** ppInterface)
1897{
1898 if (id == IID_ICorDebugChainEnum)
1899 {
1900 *ppInterface = static_cast<ICorDebugChainEnum *>(this);
1901 }
1902 else if (id == IID_ICorDebugEnum)
1903 {
1904 *ppInterface = static_cast<ICorDebugEnum *>(static_cast<ICorDebugChainEnum *>(this));
1905 }
1906 else if (id == IID_IUnknown)
1907 {
1908 *ppInterface = static_cast<IUnknown *>(static_cast<ICorDebugChainEnum *>(this));
1909 }
1910 else
1911 {
1912 *ppInterface = NULL;
1913 return E_NOINTERFACE;
1914 }
1915
1916 AddRef();
1917 return S_OK;
1918}
1919
1920// Skip the specified number of chains.
1921HRESULT ShimChainEnum::Skip(ULONG celt)
1922{
1923 RSLockHolder lockHolder(m_pShimLock);
1924 FAIL_IF_NEUTERED(this);
1925
1926 // increment the index by the specified amount
1927 m_currentChainIndex += celt;
1928 return S_OK;
1929}
1930
1931HRESULT ShimChainEnum::Reset()
1932{
1933 m_currentChainIndex = 0;
1934 return S_OK;
1935}
1936
1937// Clone the chain enumerator and set the new one to the same current chain
1938HRESULT ShimChainEnum::Clone(ICorDebugEnum ** ppEnum)
1939{
1940 RSLockHolder lockHolder(m_pShimLock);
1941 FAIL_IF_NEUTERED(this);
1942 VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorDebugEnum **);
1943
1944 HRESULT hr = S_OK;
1945 EX_TRY
1946 {
1947 NewHolder<ShimChainEnum> pChainEnum(new ShimChainEnum(m_pStackWalk, m_pShimLock));
1948
1949 // set the index in the new enumerator
1950 pChainEnum->m_currentChainIndex = this->m_currentChainIndex;
1951
1952 *ppEnum = pChainEnum;
1953 (*ppEnum)->AddRef();
1954 m_pStackWalk->AddChainEnum(pChainEnum);
1955
1956 pChainEnum.SuppressRelease();
1957 }
1958 EX_CATCH_HRESULT(hr);
1959 return hr;
1960}
1961
1962// Return the number of chains on the thread
1963HRESULT ShimChainEnum::GetCount(ULONG * pcChains)
1964{
1965 RSLockHolder lockHolder(m_pShimLock);
1966 FAIL_IF_NEUTERED(this);
1967 VALIDATE_POINTER_TO_OBJECT(pcChains, ULONG *);
1968
1969 *pcChains = m_pStackWalk->GetChainCount();
1970 return S_OK;
1971}
1972
1973// Retrieve the next x number of chains on the thread into "chains", where x is specified by "celt".
1974// "pcChainsFetched" is set to be the actual number of chains retrieved.
1975// Return S_FALSE if the number of chains actually retrieved is less than the number of chains requested.
1976HRESULT ShimChainEnum::Next(ULONG cChains, ICorDebugChain * rgpChains[], ULONG * pcChainsFetched)
1977{
1978 RSLockHolder lockHolder(m_pShimLock);
1979 FAIL_IF_NEUTERED(this);
1980 VALIDATE_POINTER_TO_OBJECT_ARRAY(rgpChains, ICorDebugChain *, cChains, true, true);
1981 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcChainsFetched, ULONG *);
1982
1983 // if the out parameter is NULL, then we can only return one chain at a time
1984 if ((pcChainsFetched == NULL) && (cChains != 1))
1985 {
1986 return E_INVALIDARG;
1987 }
1988
1989 // Check for the trivial case where no chain is actually requested.
1990 // This is probably a user error.
1991 if (cChains == 0)
1992 {
1993 if (pcChainsFetched != NULL)
1994 {
1995 *pcChainsFetched = 0;
1996 }
1997 return S_OK;
1998 }
1999
2000 ICorDebugChain ** ppCurrentChain = rgpChains;
2001
2002 while ((m_currentChainIndex < m_pStackWalk->GetChainCount()) &&
2003 (cChains > 0))
2004 {
2005 *ppCurrentChain = m_pStackWalk->GetChain(m_currentChainIndex);
2006 (*ppCurrentChain)->AddRef();
2007
2008 ppCurrentChain++; // increment the pointer into the buffer
2009 m_currentChainIndex++; // increment the index
2010 cChains--;
2011 }
2012
2013 // set the number of chains actually returned
2014 if (pcChainsFetched != NULL)
2015 {
2016 *pcChainsFetched = (ULONG)(ppCurrentChain - rgpChains);
2017 }
2018
2019 //
2020 // If we reached the end of the enumeration, but not the end
2021 // of the number of requested items, we return S_FALSE.
2022 //
2023 if (cChains > 0)
2024 {
2025 return S_FALSE;
2026 }
2027
2028 return S_OK;
2029}
2030
2031ShimChainEnum * ShimChainEnum::GetNext()
2032{
2033 return m_pNext;
2034}
2035
2036void ShimChainEnum::SetNext(ShimChainEnum * pNext)
2037{
2038 if (m_pNext != NULL)
2039 {
2040 m_pNext->Release();
2041 }
2042
2043 m_pNext = pNext;
2044
2045 if (m_pNext != NULL)
2046 {
2047 m_pNext->AddRef();
2048 }
2049}
2050
2051
2052ShimFrameEnum::ShimFrameEnum(ShimStackWalk * pSW,
2053 ShimChain * pChain,
2054 UINT32 frameStartIndex,
2055 UINT32 frameEndIndex,
2056 RSLock * pShimLock)
2057 : m_pStackWalk(pSW),
2058 m_pChain(pChain),
2059 m_pShimLock(pShimLock),
2060 m_pNext(NULL),
2061 m_currentFrameIndex(frameStartIndex),
2062 m_endFrameIndex(frameEndIndex),
2063 m_refCount(0),
2064 m_fIsNeutered(FALSE)
2065{
2066}
2067
2068ShimFrameEnum::~ShimFrameEnum()
2069{
2070 _ASSERTE(IsNeutered());
2071}
2072
2073void ShimFrameEnum::Neuter()
2074{
2075 if (IsNeutered())
2076 {
2077 return;
2078 }
2079
2080 m_fIsNeutered = TRUE;
2081}
2082
2083BOOL ShimFrameEnum::IsNeutered()
2084{
2085 return m_fIsNeutered;
2086}
2087
2088
2089ULONG STDMETHODCALLTYPE ShimFrameEnum::AddRef()
2090{
2091 return InterlockedIncrement((LONG *)&m_refCount);
2092}
2093
2094ULONG STDMETHODCALLTYPE ShimFrameEnum::Release()
2095{
2096 LONG newRefCount = InterlockedDecrement((LONG *)&m_refCount);
2097 _ASSERTE(newRefCount >= 0);
2098
2099 if (newRefCount == 0)
2100 {
2101 delete this;
2102 }
2103 return newRefCount;
2104}
2105
2106HRESULT ShimFrameEnum::QueryInterface(REFIID id, void ** ppInterface)
2107{
2108 if (id == IID_ICorDebugFrameEnum)
2109 {
2110 *ppInterface = static_cast<ICorDebugFrameEnum *>(this);
2111 }
2112 else if (id == IID_ICorDebugEnum)
2113 {
2114 *ppInterface = static_cast<ICorDebugEnum *>(static_cast<ICorDebugFrameEnum *>(this));
2115 }
2116 else if (id == IID_IUnknown)
2117 {
2118 *ppInterface = static_cast<IUnknown *>(static_cast<ICorDebugFrameEnum *>(this));
2119 }
2120 else
2121 {
2122 *ppInterface = NULL;
2123 return E_NOINTERFACE;
2124 }
2125
2126 AddRef();
2127 return S_OK;
2128}
2129
2130// Skip the specified number of chains.
2131HRESULT ShimFrameEnum::Skip(ULONG celt)
2132{
2133 RSLockHolder lockHolder(m_pShimLock);
2134 FAIL_IF_NEUTERED(this);
2135
2136 // increment the index by the specified amount
2137 m_currentFrameIndex += celt;
2138 return S_OK;
2139}
2140
2141HRESULT ShimFrameEnum::Reset()
2142{
2143 RSLockHolder lockHolder(m_pShimLock);
2144 FAIL_IF_NEUTERED(this);
2145
2146 m_currentFrameIndex = m_pChain->GetFirstFrameIndex();
2147 return S_OK;
2148}
2149
2150// Clone the chain enumerator and set the new one to the same current chain
2151HRESULT ShimFrameEnum::Clone(ICorDebugEnum ** ppEnum)
2152{
2153 RSLockHolder lockHolder(m_pShimLock);
2154 FAIL_IF_NEUTERED(this);
2155 VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorDebugEnum **);
2156
2157 HRESULT hr = S_OK;
2158 EX_TRY
2159 {
2160 NewHolder<ShimFrameEnum> pFrameEnum(new ShimFrameEnum(m_pStackWalk,
2161 m_pChain,
2162 m_currentFrameIndex,
2163 m_endFrameIndex,
2164 m_pShimLock));
2165
2166 *ppEnum = pFrameEnum;
2167 (*ppEnum)->AddRef();
2168 m_pStackWalk->AddFrameEnum(pFrameEnum);
2169
2170 pFrameEnum.SuppressRelease();
2171 }
2172 EX_CATCH_HRESULT(hr);
2173
2174 return hr;
2175}
2176
2177// Return the number of chains on the thread
2178HRESULT ShimFrameEnum::GetCount(ULONG * pcFrames)
2179{
2180 RSLockHolder lockHolder(m_pShimLock);
2181 FAIL_IF_NEUTERED(this);
2182 VALIDATE_POINTER_TO_OBJECT(pcFrames, ULONG *);
2183
2184 *pcFrames = m_pChain->GetLastFrameIndex() - m_pChain->GetFirstFrameIndex();
2185 return S_OK;
2186}
2187
2188// Retrieve the next x number of chains on the thread into "chains", where x is specified by "celt".
2189// "pcChainsFetched" is set to be the actual number of chains retrieved.
2190// Return S_FALSE if the number of chains actually retrieved is less than the number of chains requested.
2191HRESULT ShimFrameEnum::Next(ULONG cFrames, ICorDebugFrame * rgpFrames[], ULONG * pcFramesFetched)
2192{
2193 RSLockHolder lockHolder(m_pShimLock);
2194 FAIL_IF_NEUTERED(this);
2195 VALIDATE_POINTER_TO_OBJECT_ARRAY(rgpFrames, ICorDebugFrame *, cFrames, true, true);
2196 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcFramesFetched, ULONG *);
2197
2198 // if the out parameter is NULL, then we can only return one chain at a time
2199 if ((pcFramesFetched == NULL) && (cFrames != 1))
2200 {
2201 return E_INVALIDARG;
2202 }
2203
2204 // Check for the trivial case where no chain is actually requested.
2205 // This is probably a user error.
2206 if (cFrames == 0)
2207 {
2208 if (pcFramesFetched != NULL)
2209 {
2210 *pcFramesFetched = 0;
2211 }
2212 return S_OK;
2213 }
2214
2215 ICorDebugFrame ** ppCurrentFrame = rgpFrames;
2216
2217 while ((m_currentFrameIndex < m_endFrameIndex) &&
2218 (cFrames > 0))
2219 {
2220 *ppCurrentFrame = m_pStackWalk->GetFrame(m_currentFrameIndex);
2221 (*ppCurrentFrame)->AddRef();
2222
2223 ppCurrentFrame++; // increment the pointer into the buffer
2224 m_currentFrameIndex++; // increment the index
2225 cFrames--;
2226 }
2227
2228 // set the number of chains actually returned
2229 if (pcFramesFetched != NULL)
2230 {
2231 *pcFramesFetched = (ULONG)(ppCurrentFrame - rgpFrames);
2232 }
2233
2234 //
2235 // If we reached the end of the enumeration, but not the end
2236 // of the number of requested items, we return S_FALSE.
2237 //
2238 if (cFrames > 0)
2239 {
2240 return S_FALSE;
2241 }
2242
2243 return S_OK;
2244}
2245
2246ShimFrameEnum * ShimFrameEnum::GetNext()
2247{
2248 return m_pNext;
2249}
2250
2251void ShimFrameEnum::SetNext(ShimFrameEnum * pNext)
2252{
2253 if (m_pNext != NULL)
2254 {
2255 m_pNext->Release();
2256 }
2257
2258 m_pNext = pNext;
2259
2260 if (m_pNext != NULL)
2261 {
2262 m_pNext->AddRef();
2263 }
2264}
2265