forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathframeinfo.cpp
More file actions
2190 lines (1857 loc) · 81.8 KB
/
frameinfo.cpp
File metadata and controls
2190 lines (1857 loc) · 81.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//*****************************************************************************
// File: frameinfo.cpp
//
//
// Code to find control info about a stack frame.
//
//*****************************************************************************
#include "stdafx.h"
// Include so we can get information out of ComMethodFrame
#ifdef FEATURE_COMINTEROP
#include "COMToClrCall.h"
#endif
// Get a frame pointer from a RegDisplay.
// This is mostly used for chains and stub frames (i.e. internal frames), where we don't need an exact
// frame pointer. This is why it is okay to use the current SP instead of the caller SP on IA64.
// We should really rename this and possibly roll it into GetFramePointer() when we move the stackwalker
// to OOP.
FramePointer GetSP(REGDISPLAY * pRDSrc)
{
FramePointer fp = FramePointer::MakeFramePointer(
(LPVOID)GetRegdisplaySP(pRDSrc));
return fp;
}
// Get a frame pointer from a RegDisplay.
FramePointer GetFramePointer(REGDISPLAY * pRDSrc)
{
return FramePointer::MakeFramePointer(GetRegdisplaySP(pRDSrc));
}
//---------------------------------------------------------------------------------------
//
// Convert a FramePointer to a StackFrame and return it.
//
// Arguments:
// fp - the FramePointer to be converted
//
// Return Value:
// a StackFrame equivalent to the given FramePointer
//
// Notes:
// We really should consolidate the two abstractions for "stack frame identifiers"
// (StackFrame and FramePointer) when we move the debugger stackwalker to OOP.
//
FORCEINLINE StackFrame ConvertFPToStackFrame(FramePointer fp)
{
return StackFrame((UINT_PTR)fp.GetSPValue());
}
/* ------------------------------------------------------------------------- *
* DebuggerFrameInfo routines
* ------------------------------------------------------------------------- */
//struct DebuggerFrameData: Contains info used by the DebuggerWalkStackProc
// to do a stack walk. The info and pData fields are handed to the pCallback
// routine at each frame,
struct DebuggerFrameData
{
// Initialize this struct. Only done at the start of a stackwalk.
void Init(
Thread * _pThread,
FramePointer _targetFP,
BOOL fIgnoreNonmethodFrames, // generally true for stackwalking and false for stepping
DebuggerStackCallback _pCallback,
void *_pData
)
{
LIMITED_METHOD_CONTRACT;
this->pCallback = _pCallback;
this->pData = _pData;
this->cRealCounter = 0;
this->thread = _pThread;
this->targetFP = _targetFP;
this->targetFound = (_targetFP == LEAF_MOST_FRAME);
this->ignoreNonmethodFrames = fIgnoreNonmethodFrames;
// For now, we can tie these to flags together.
// In everett, we disable SIS (For backwards compat).
this->fProvideInternalFrames = (fIgnoreNonmethodFrames != 0);
this->fNeedToSendEnterManagedChain = false;
this->fTrackingUMChain = false;
this->fHitExitFrame = false;
this->info.eStubFrameType = STUBFRAME_NONE;
this->info.quickUnwind = false;
this->info.frame = NULL;
this->needParentInfo = false;
#ifdef FEATURE_EH_FUNCLETS
this->fpParent = LEAF_MOST_FRAME;
this->info.fIsLeaf = true;
this->info.fIsFunclet = false;
this->info.fIsFilter = false;
#endif // FEATURE_EH_FUNCLETS
// Look strange? Go to definition of this field. I dare you.
this->info.fIgnoreThisFrameIfSuppressingUMChainFromComPlusMethodFrameGeneric = false;
#if defined(_DEBUG)
this->previousFP = LEAF_MOST_FRAME;
#endif // _DEBUG
}
// True if we need the next CrawlFrame to fill out part of this FrameInfo's data.
bool needParentInfo;
// The FrameInfo that we'll dispatch to the pCallback. This matches against
// the CrawlFrame for that frame that the callback belongs too.
FrameInfo info;
// Regdisplay that the EE stackwalker is updating.
REGDISPLAY regDisplay;
#ifdef FEATURE_EH_FUNCLETS
// This is used to skip funclets in a stackwalk. It marks the frame pointer to which we should skip.
FramePointer fpParent;
#endif // FEATURE_EH_FUNCLETS
#if defined(_DEBUG)
// For debugging, track the previous FramePointer so we can assert that we're
// making progress through the stack.
FramePointer previousFP;
#endif // _DEBUG
// whether we have hit an exit frame or not (i.e. a M2U frame)
bool fHitExitFrame;
private:
// The scope of this field is each section of managed method frames on the stack.
bool fNeedToSendEnterManagedChain;
// Flag set when we first stack-walk to decide if we want to ignore certain frames.
// Stepping doesn't ignore these frames; end user stacktraces do.
BOOL ignoreNonmethodFrames;
// Do we want callbacks for internal frames?
// Steppers generally don't. User stack-walk does.
bool fProvideInternalFrames;
// Info for tracking unmanaged chains.
// We track the starting (leaf) context for an unmanaged chain, as well as the
// ending (root) framepointer.
bool fTrackingUMChain;
REGDISPLAY rdUMChainStart;
FramePointer fpUMChainEnd;
// Thread that the stackwalk is for.
Thread *thread;
// Target FP indicates at what point in the stackwalk we'll start dispatching callbacks.
// Naturally, if this is LEAF_MOST_FRAME, then all callbacks will be dispatched
FramePointer targetFP;
bool targetFound;
// Count # of callbacks we could have dispatched (assuming targetFP==LEAF_MOST_FRAME).
// Useful for detecting leaf.
int cRealCounter;
// Callback & user-data supplied to that callback.
DebuggerStackCallback pCallback;
void *pData;
private:
// Raw invoke. This just does some consistency asserts,
// and invokes the callback if we're in the requested target range.
StackWalkAction RawInvokeCallback(FrameInfo * pInfo)
{
#ifdef _DEBUG
_ASSERTE(pInfo != NULL);
MethodDesc * md = pInfo->md;
// Invoke the callback to the user. Log what we're invoking.
LOG((LF_CORDB, LL_INFO10000, "DSWCallback: MD=%s,0x%p, Chain=%x, Stub=%x, Frame=0x%p, Internal=%d\n",
((md == NULL) ? "None" : md->m_pszDebugMethodName), md,
pInfo->chainReason,
pInfo->eStubFrameType,
pInfo->frame, pInfo->internal));
// Make sure we're providing a valid FrameInfo for the callback.
pInfo->AssertValid();
#endif
// Update counter. This provides a convenient check for leaf FrameInfo.
this->cRealCounter++;
// Only invoke if we're past the target.
if (!this->targetFound && IsEqualOrCloserToLeaf(this->targetFP, this->info.fp))
{
this->targetFound = true;
}
if (this->targetFound)
{
return (pCallback)(pInfo, pData);
}
else
{
LOG((LF_CORDB, LL_INFO10000, "Not invoking yet.\n"));
}
return SWA_CONTINUE;
}
public:
// Invoke a callback. This may do extra logic to preserve the interface between
// the LS stackwalker and the LS:
// - don't invoke if we're not at the target yet
// - send EnterManagedChains if we need it.
StackWalkAction InvokeCallback(FrameInfo * pInfo)
{
// Track if we've sent any managed code yet.
// If we haven't, then don't send the enter-managed chain. This catches cases
// when we have leaf-most unmanaged chain.
if ((pInfo->frame == NULL) && (pInfo->md != NULL))
{
this->fNeedToSendEnterManagedChain = true;
}
// Do tracking to decide if we need to send a Enter-Managed chain.
if (pInfo->HasChainMarker())
{
if (pInfo->managed)
{
// If we're dispatching a managed-chain, then we don't need to send another one.
fNeedToSendEnterManagedChain = false;
}
else
{
// If we're dispatching an UM chain, then send the Managed one.
// Note that the only unmanaged chains are ThreadStart chains and UM chains.
if (fNeedToSendEnterManagedChain)
{
fNeedToSendEnterManagedChain = false;
FrameInfo f;
// Assume entry chain's FP is one pointer-width after the upcoming UM chain.
FramePointer fpRoot = FramePointer::MakeFramePointer(
(BYTE*) GetRegdisplaySP(&pInfo->registers) - sizeof(DWORD*));
f.InitForEnterManagedChain(fpRoot);
if (RawInvokeCallback(&f) == SWA_ABORT)
{
return SWA_ABORT;
}
}
}
}
return RawInvokeCallback(pInfo);
}
// Note that we should start tracking an Unmanaged Chain.
void BeginTrackingUMChain(FramePointer fpRoot, REGDISPLAY * pRDSrc)
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(!this->fTrackingUMChain);
CopyREGDISPLAY(&this->rdUMChainStart, pRDSrc);
this->fTrackingUMChain = true;
this->fpUMChainEnd = fpRoot;
this->fHitExitFrame = false;
LOG((LF_CORDB, LL_EVERYTHING, "UM Chain starting at Frame=0x%p\n", this->fpUMChainEnd.GetSPValue()));
// This UM chain may get cancelled later, so don't even worry about toggling the fNeedToSendEnterManagedChain bit here.
// Invoke() will track whether to send an Enter-Managed chain or not.
}
// For various heuristics, we may not want to send an UM chain.
void CancelUMChain()
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(this->fTrackingUMChain);
this->fTrackingUMChain = false;
}
// True iff we're currently tracking an unmanaged chain.
bool IsTrackingUMChain()
{
LIMITED_METHOD_CONTRACT;
return this->fTrackingUMChain;
}
// Get/Set Regdisplay that starts an Unmanaged chain.
REGDISPLAY * GetUMChainStartRD()
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(fTrackingUMChain);
return &rdUMChainStart;
}
// Get/Set FramePointer that ends an unmanaged chain.
void SetUMChainEnd(FramePointer fp)
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(fTrackingUMChain);
fpUMChainEnd = fp;
}
FramePointer GetUMChainEnd()
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(fTrackingUMChain);
return fpUMChainEnd;
}
// Get thread we're currently tracing.
Thread * GetThread()
{
LIMITED_METHOD_CONTRACT;
return thread;
}
// Returns true if we're on the leaf-callback (ie, we haven't dispatched a callback yet.
bool IsLeafCallback()
{
LIMITED_METHOD_CONTRACT;
return cRealCounter == 0;
}
bool ShouldProvideInternalFrames()
{
LIMITED_METHOD_CONTRACT;
return fProvideInternalFrames;
}
bool ShouldIgnoreNonmethodFrames()
{
LIMITED_METHOD_CONTRACT;
return ignoreNonmethodFrames != 0;
}
};
//---------------------------------------------------------------------------------------
//
// On IA64, the offset given by the OS during stackwalking is actually the offset at the call instruction.
// This is different from x86 and X64, where the offset is immediately after the call instruction. In order
// to have a uniform behaviour, we need to do adjust the relative offset on IA64. This function is a nop on
// other platforms.
//
// Arguments:
// pCF - the CrawlFrame for the current method frame
// pInfo - This is the FrameInfo for the current method frame. We need to use the fIsLeaf field,
// since no adjustment is necessary for leaf frames.
//
// Return Value:
// returns the adjusted relative offset
//
inline ULONG AdjustRelOffset(CrawlFrame *pCF,
FrameInfo *pInfo)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(pCF != NULL);
}
CONTRACTL_END;
#if defined(_TARGET_ARM_)
return pCF->GetRelOffset() & ~THUMB_CODE;
#else
return pCF->GetRelOffset();
#endif
}
//---------------------------------------------------------------------------------------
//
// Even when there is an exit frame in the explicit frame chain, it does not necessarily mean that we have
// actually called out to unmanaged code yet or that we actually have a managed call site. Given an exit
// frame, this function determines if we have a managed call site and have already called out to unmanaged
// code. If we have, then we return the caller SP as the potential frame pointer. Otherwise we return
// LEAF_MOST_FRAME.
//
// Arguments:
// pFrame - the exit frame to be checked
// pData - the state of the current frame maintained by the debugger stackwalker
// pPotentialFP - This is an out parameter. It returns the caller SP of the last managed caller if
// there is a managed call site and we have already called out to unmanaged code.
// Otherwise, LEAF_MOST_FRAME is returned.
//
// Return Value:
// true - we have a managed call site and we have called out to unmanaged code
// false - otherwise
//
bool HasExitRuntime(Frame *pFrame, DebuggerFrameData *pData, FramePointer *pPotentialFP)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER; // Callers demand this function be GC_NOTRIGGER.
MODE_ANY;
PRECONDITION(pFrame->GetFrameType() == Frame::TYPE_EXIT);
}
CONTRACTL_END;
#ifdef _TARGET_X86_
TADDR returnIP, returnSP;
EX_TRY
{
// This is a real issue. This may be called while holding GC-forbid locks, and so
// this function can't trigger a GC. However, the only impl we have calls GC-trigger functions.
CONTRACT_VIOLATION(GCViolation);
pFrame->GetUnmanagedCallSite(NULL, &returnIP, &returnSP);
}
EX_CATCH
{
// We never expect an actual exception here (maybe in oom).
// If we get an exception, then simulate the default behavior for GetUnmanagedCallSite.
returnIP = NULL;
returnSP = NULL; // this will cause us to return true.
}
EX_END_CATCH(SwallowAllExceptions);
LOG((LF_CORDB, LL_INFO100000,
"DWSP: TYPE_EXIT: returnIP=0x%08x, returnSP=0x%08x, frame=0x%08x, threadFrame=0x%08x, regSP=0x%08x\n",
returnIP, returnSP, pFrame, pData->GetThread()->GetFrame(), GetRegdisplaySP(&pData->regDisplay)));
if (pPotentialFP != NULL)
{
*pPotentialFP = FramePointer::MakeFramePointer((void*)returnSP);
}
return ((pFrame != pData->GetThread()->GetFrame()) ||
(returnSP == NULL) ||
((TADDR)GetRegdisplaySP(&pData->regDisplay) <= returnSP));
#else // _TARGET_X86_
// DebuggerExitFrame always return a NULL returnSP on x86.
if (pFrame->GetVTablePtr() == DebuggerExitFrame::GetMethodFrameVPtr())
{
if (pPotentialFP != NULL)
{
*pPotentialFP = LEAF_MOST_FRAME;
}
return true;
}
else if (pFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr())
{
InlinedCallFrame *pInlinedFrame = static_cast<InlinedCallFrame *>(pFrame);
LPVOID sp = (LPVOID)pInlinedFrame->GetCallSiteSP();
// The sp returned below is the sp of the caller, which is either an IL stub in the normal case
// or a normal managed method in the inlined pinvoke case.
// This sp may be the same as the frame's address, so we need to use the largest
// possible bsp value to make sure that this frame pointer is closer to the root than
// the frame pointer made from the frame address itself.
if (pPotentialFP != NULL)
{
*pPotentialFP = FramePointer::MakeFramePointer( (LPVOID)sp );
}
return ((pFrame != pData->GetThread()->GetFrame()) ||
InlinedCallFrame::FrameHasActiveCall(pInlinedFrame));
}
else
{
// It'll be nice if there's a way to assert that the current frame is indeed of a
// derived class of TransitionFrame.
TransitionFrame *pTransFrame = static_cast<TransitionFrame*>(pFrame);
LPVOID sp = (LPVOID)pTransFrame->GetSP();
// The sp returned below is the sp of the caller, which is either an IL stub in the normal case
// or a normal managed method in the inlined pinvoke case.
// This sp may be the same as the frame's address, so we need to use the largest
// possible bsp value to make sure that this frame pointer is closer to the root than
// the frame pointer made from the frame address itself.
if (pPotentialFP != NULL)
{
*pPotentialFP = FramePointer::MakeFramePointer( (LPVOID)sp );
}
return true;
}
#endif // _TARGET_X86_
}
#ifdef _DEBUG
//-----------------------------------------------------------------------------
// Debug helpers to get name of Frame.
//-----------------------------------------------------------------------------
LPCUTF8 FrameInfo::DbgGetClassName()
{
return (md == NULL) ? ("None") : (md->m_pszDebugClassName);
}
LPCUTF8 FrameInfo::DbgGetMethodName()
{
return (md == NULL) ? ("None") : (md->m_pszDebugMethodName);
}
//-----------------------------------------------------------------------------
// Debug helper to asserts invariants about a FrameInfo before we dispatch it.
//-----------------------------------------------------------------------------
void FrameInfo::AssertValid()
{
LIMITED_METHOD_CONTRACT;
bool fMethod = this->HasMethodFrame();
bool fStub = this->HasStubFrame();
bool fChain = this->HasChainMarker();
// Can't be both Stub & Chain
_ASSERTE(!fStub || !fChain);
// Must be at least a Method, Stub or Chain or Internal
_ASSERTE(fMethod || fStub || fChain || this->internal);
// Check Managed status is consistent
if (fMethod)
{
_ASSERTE(this->managed); // We only report managed methods
}
if (fChain)
{
if (!managed)
{
// Only certain chains can be unmanaged
_ASSERTE((this->chainReason == CHAIN_THREAD_START) ||
(this->chainReason == CHAIN_ENTER_UNMANAGED));
}
else
{
// UM chains can never be managed.
_ASSERTE((this->chainReason != CHAIN_ENTER_UNMANAGED));
}
}
// FramePointer should be valid
_ASSERTE(this->fp != LEAF_MOST_FRAME);
_ASSERTE((this->fp != ROOT_MOST_FRAME) || (chainReason== CHAIN_THREAD_START) || (chainReason == CHAIN_ENTER_UNMANAGED));
// If we have a Method, then we need an AppDomain.
// (RS will need it to do lookup)
if (fMethod)
{
_ASSERTE(currentAppDomain != NULL);
_ASSERTE(managed);
// Stubs may have a method w/o any code (eg, PInvoke wrapper).
// @todo - Frame::TYPE_TP_METHOD_FRAME breaks this assert. Are there other cases too?
//_ASSERTE(fStub || (pIJM != NULL));
}
if (fStub)
{
// All stubs (except LightWeightFunctions) match up w/a Frame.
_ASSERTE(this->frame || (eStubFrameType == STUBFRAME_LIGHTWEIGHT_FUNCTION));
}
}
#endif
//-----------------------------------------------------------------------------
// Get the DJI associated w/ this frame. This is a convenience function.
// This is recommended over using MethodDescs because DJI's are version-aware.
//-----------------------------------------------------------------------------
DebuggerJitInfo * FrameInfo::GetJitInfoFromFrame() const
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
}
CONTRACTL_END;
// Not all FrameInfo objects correspond to actual code.
if (HasChainMarker() || HasStubFrame() || (frame != NULL))
{
return NULL;
}
DebuggerJitInfo *ji = NULL;
// @todo - we shouldn't need both a MD and an IP here.
EX_TRY
{
_ASSERTE(this->md != NULL);
ji = g_pDebugger->GetJitInfo(this->md, (const BYTE*)GetControlPC(&(this->registers)));
_ASSERTE(ji != NULL);
_ASSERTE(ji->m_nativeCodeVersion.GetMethodDesc() == this->md);
}
EX_CATCH
{
ji = NULL;
}
EX_END_CATCH(SwallowAllExceptions);
return ji;
}
//-----------------------------------------------------------------------------
// Get the DMI associated w/ this frame. This is a convenience function.
// DMIs are 1:1 with the (token, module) pair.
//-----------------------------------------------------------------------------
DebuggerMethodInfo * FrameInfo::GetMethodInfoFromFrameOrThrow()
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
}
CONTRACTL_END;
MethodDesc * pDesc = this->md;
mdMethodDef token = pDesc-> GetMemberDef();
Module * pRuntimeModule = pDesc->GetModule();
DebuggerMethodInfo *dmi = g_pDebugger->GetOrCreateMethodInfo(pRuntimeModule, token);
return dmi;
}
//-----------------------------------------------------------------------------
// Init a FrameInfo for a UM chain.
// We need a stackrange to give to an unmanaged debugger.
// pRDSrc->Esp will provide the start (leaf) marker.
// fpRoot will provide the end (root) portion.
//-----------------------------------------------------------------------------
void FrameInfo::InitForUMChain(FramePointer fpRoot, REGDISPLAY * pRDSrc)
{
_ASSERTE(pRDSrc != NULL);
// Mark that we're an UM Chain (and nothing else).
this->frame = NULL;
this->md = NULL;
// Fp will be the end (root) of the stack range.
// pRDSrc->Sp will be the start (leaf) of the stack range.
CopyREGDISPLAY(&(this->registers), pRDSrc);
this->fp = fpRoot;
this->quickUnwind = false;
this->internal = false;
this->managed = false;
// These parts of the FrameInfo can be ignored for a UM chain.
this->relOffset = 0;
this->pIJM = NULL;
this->MethodToken = METHODTOKEN(NULL, 0);
this->currentAppDomain = NULL;
this->exactGenericArgsToken = NULL;
InitForScratchFrameInfo();
this->chainReason = CHAIN_ENTER_UNMANAGED;
this->eStubFrameType = STUBFRAME_NONE;
#ifdef _DEBUG
FramePointer fpLeaf = GetSP(pRDSrc);
_ASSERTE(IsCloserToLeaf(fpLeaf, fpRoot));
#endif
#ifdef _DEBUG
// After we just init it, it had better be valid.
this->AssertValid();
#endif
}
//---------------------------------------------------------------------------------------
//
// This is just a small helper to initialize the fields which are specific to 64-bit. Note that you should
// only call this function on a scratch FrameInfo. Never call it on the FrameInfo used by the debugger
// stackwalker to store information on the current frame.
//
void FrameInfo::InitForScratchFrameInfo()
{
#ifdef FEATURE_EH_FUNCLETS
// The following flags cannot be trashed when we are calling this function on the curret FrameInfo
// (the one we keep track of across multiple stackwalker callbacks). Thus, make sure you do not call
// this function from InitForDynamicMethod(). In all other cases, we can call this method after we
// call InitFromStubHelper() because we are working on a local scratch variable.
this->fIsLeaf = false;
this->fIsFunclet = false;
this->fIsFilter = false;
#endif // FEATURE_EH_FUNCLETS
}
//-----------------------------------------------------------------------------
//
// Init a FrameInfo for a stub. Stub frames map to internal frames on the RS. Stubs which we care about
// usually contain an explicit frame which translates to an internal frame on the RS. Dynamic method is
// the sole exception.
//
// Arguments:
// pCF - the CrawlFrame containing the state of the current frame
// pMDHint - some stubs have associated MethodDesc but others don't,
// which is why this argument can be NULL
// type - the type of the stub/internal frame
//
void FrameInfo::InitFromStubHelper(
CrawlFrame * pCF,
MethodDesc * pMDHint, // NULL ok
CorDebugInternalFrameType type
)
{
_ASSERTE(pCF != NULL);
Frame * pFrame = pCF->GetFrame();
LOG((LF_CORDB, LL_EVERYTHING, "InitFromStubHelper. Frame=0x%p, type=%d\n", pFrame, type));
// All Stubs have a Frame except for LightWeight methods
_ASSERTE((type == STUBFRAME_LIGHTWEIGHT_FUNCTION) || (pFrame != NULL));
REGDISPLAY *pRDSrc = pCF->GetRegisterSet();
this->frame = pFrame;
// Stub frames may be associated w/ a Method (as a hint). However this method
// will never have a JitManager b/c it will never have IL (if it had IL, we'd be a
// regulare frame, not a stub frame)
this->md = pMDHint;
CopyREGDISPLAY(&this->registers, pRDSrc);
// FramePointer must match up w/ an EE Frame b/c that's how we match
// we Exception callbacks.
if (pFrame != NULL)
{
this->fp = FramePointer::MakeFramePointer(
(LPVOID) pFrame);
}
else
{
this->fp = GetSP(pRDSrc);
}
this->quickUnwind = false;
this->internal = false;
this->managed = true;
this->relOffset = 0;
this->ambientSP = NULL;
// Method associated w/a stub will never have a JitManager.
this->pIJM = NULL;
this->MethodToken = METHODTOKEN(NULL, 0);
this->currentAppDomain = pCF->GetAppDomain();
this->exactGenericArgsToken = NULL;
// Stub frames are mutually exclusive with chain markers.
this->chainReason = CHAIN_NONE;
this->eStubFrameType = type;
#ifdef _DEBUG
// After we just init it, it had better be valid.
this->AssertValid();
#endif
}
//-----------------------------------------------------------------------------
// Initialize a FrameInfo to be used for an "InternalFrame"
// Frame should be a derived class of FramedMethodFrame.
// FrameInfo's MethodDesc will be for managed wrapper for native call.
//-----------------------------------------------------------------------------
void FrameInfo::InitForM2UInternalFrame(CrawlFrame * pCF)
{
// For a M2U call, there's a managed method wrapping the unmanaged call. Use that.
Frame * pFrame = pCF->GetFrame();
_ASSERTE(pFrame->GetTransitionType() == Frame::TT_M2U);
FramedMethodFrame * pM2U = static_cast<FramedMethodFrame*> (pFrame);
MethodDesc * pMDWrapper = pM2U->GetFunction();
// Soem M2U transitions may not have a function associated w/ them,
// so pMDWrapper may be NULL. PInvokeCalliFrame is an example.
InitFromStubHelper(pCF, pMDWrapper, STUBFRAME_M2U);
InitForScratchFrameInfo();
}
//-----------------------------------------------------------------------------
// Initialize for the U2M case...
//-----------------------------------------------------------------------------
void FrameInfo::InitForU2MInternalFrame(CrawlFrame * pCF)
{
PREFIX_ASSUME(pCF != NULL);
MethodDesc * pMDHint = NULL;
#ifdef FEATURE_COMINTEROP
Frame * pFrame = pCF->GetFrame();
PREFIX_ASSUME(pFrame != NULL);
// For regular U2M PInvoke cases, we don't care about MD b/c it's just going to
// be the next frame.
// If we're a COM2CLR call, perhaps we can get the MD for the interface.
if (pFrame->GetVTablePtr() == ComMethodFrame::GetMethodFrameVPtr())
{
ComMethodFrame* pCOMFrame = static_cast<ComMethodFrame*> (pFrame);
ComCallMethodDesc* pCMD = reinterpret_cast<ComCallMethodDesc *> (pCOMFrame->ComMethodFrame::GetDatum());
pMDHint = pCMD->GetInterfaceMethodDesc();
// Some COM-interop cases don't have an intermediate interface method desc, so
// pMDHint may be null.
}
#endif
InitFromStubHelper(pCF, pMDHint, STUBFRAME_U2M);
InitForScratchFrameInfo();
}
//-----------------------------------------------------------------------------
// Init for an AD transition
//-----------------------------------------------------------------------------
void FrameInfo::InitForADTransition(CrawlFrame * pCF)
{
Frame * pFrame;
pFrame = pCF->GetFrame();
_ASSERTE(pFrame->GetTransitionType() == Frame::TT_AppDomain);
MethodDesc * pMDWrapper = NULL;
InitFromStubHelper(pCF, pMDWrapper, STUBFRAME_APPDOMAIN_TRANSITION);
InitForScratchFrameInfo();
}
//-----------------------------------------------------------------------------
// Init frame for a dynamic method.
//-----------------------------------------------------------------------------
void FrameInfo::InitForDynamicMethod(CrawlFrame * pCF)
{
// These are just stack markers that there's a dynamic method on the callstack.
InitFromStubHelper(pCF, NULL, STUBFRAME_LIGHTWEIGHT_FUNCTION);
// Do not call InitForScratchFrameInfo() here! Please refer to the comment in that function.
}
//-----------------------------------------------------------------------------
// Init an internal frame to mark a func-eval.
//-----------------------------------------------------------------------------
void FrameInfo::InitForFuncEval(CrawlFrame * pCF)
{
// We don't store a MethodDesc hint referring to the method we're going to invoke because
// uses of stub frames will assume the MD is relative to the AppDomain the frame is in.
// For cross-AD funcevals, we're invoking a method in a domain other than the one this frame
// is in.
MethodDesc * pMDHint = NULL;
// Add a stub frame here to mark that there is a FuncEvalFrame on the stack.
InitFromStubHelper(pCF, pMDHint, STUBFRAME_FUNC_EVAL);
InitForScratchFrameInfo();
}
//---------------------------------------------------------------------------------------
//
// Initialize a FrameInfo for sending the CHAIN_THREAD_START reason.
// The common case is that the chain is NOT managed, since the lowest (closest to the root) managed method
// is usually called from unmanaged code. In fact, in Whidbey, we should never have a managed chain.
//
// Arguments:
// pRDSrc - a REGDISPLAY for the beginning (the leafmost frame) of the chain
//
void FrameInfo::InitForThreadStart(Thread * pThread, REGDISPLAY * pRDSrc)
{
this->frame = (Frame *) FRAME_TOP;
this->md = NULL;
CopyREGDISPLAY(&(this->registers), pRDSrc);
this->fp = FramePointer::MakeFramePointer(pThread->GetCachedStackBase());
this->quickUnwind = false;
this->internal = false;
this->managed = false;
this->relOffset = 0;
this->pIJM = NULL;
this->MethodToken = METHODTOKEN(NULL, 0);
this->currentAppDomain = NULL;
this->exactGenericArgsToken = NULL;
InitForScratchFrameInfo();
this->chainReason = CHAIN_THREAD_START;
this->eStubFrameType = STUBFRAME_NONE;
#ifdef _DEBUG
// After we just init it, it had better be valid.
this->AssertValid();
#endif
}
//---------------------------------------------------------------------------------------
//
// Initialize a FrameInfo for sending a CHAIN_ENTER_MANAGED.
// A Enter-Managed chain is always sent immediately before an UM chain, meaning that the Enter-Managed chain
// is closer to the leaf than the UM chain.
//
// Arguments:
// fpRoot - This is the frame pointer for the Enter-Managed chain. It is currently arbitrarily set
// to be one stack slot higher (closer to the leaf) than the frame pointer of the beginning
// of the upcoming UM chain.
//
void FrameInfo::InitForEnterManagedChain(FramePointer fpRoot)
{
// Nobody should use a EnterManagedChain's Frame*, but there's no
// good value to enforce that.
this->frame = (Frame *) FRAME_TOP;
this->md = NULL;
memset((void *)&this->registers, 0, sizeof(this->registers));
this->fp = fpRoot;
this->quickUnwind = true;
this->internal = false;
this->managed = true;
this->relOffset = 0;
this->pIJM = NULL;
this->MethodToken = METHODTOKEN(NULL, 0);
this->currentAppDomain = NULL;
this->exactGenericArgsToken = NULL;
InitForScratchFrameInfo();
this->chainReason = CHAIN_ENTER_MANAGED;
this->eStubFrameType = STUBFRAME_NONE;
}
//-----------------------------------------------------------------------------
// Do tracking for UM chains.
// This may invoke the UMChain callback and M2U callback.
//-----------------------------------------------------------------------------
StackWalkAction TrackUMChain(CrawlFrame *pCF, DebuggerFrameData *d)
{
Frame *frame = g_pEEInterface->GetFrame(pCF);
// If we encounter an ExitFrame out in the wild, then we'll convert it to an UM chain.
if (!d->IsTrackingUMChain())
{
if ((frame != NULL) && (frame != FRAME_TOP) && (frame->GetFrameType() == Frame::TYPE_EXIT))
{
LOG((LF_CORDB, LL_EVERYTHING, "DWSP. ExitFrame while not tracking\n"));
REGDISPLAY* pRDSrc = pCF->GetRegisterSet();
d->BeginTrackingUMChain(GetSP(pRDSrc), pRDSrc);
// fall through and we'll send the UM chain.
}
else
{
return SWA_CONTINUE;
}
}
_ASSERTE(d->IsTrackingUMChain());
// If we're tracking an UM chain, then we need to:
// - possibly refine the start & end values as we get new information in the stacktrace.
// - possibly cancel the UM chain for various heuristics.
// - possibly dispatch if we've hit managed code again.
bool fDispatchUMChain = false;
// UM Chain stops when managed code starts again.
if (frame != NULL)
{
// If it's just a EE Frame, then update this as a possible end of stack range for the UM chain.
// (The end of a stack range is closer to the root.)
d->SetUMChainEnd(FramePointer::MakeFramePointer((LPVOID)(frame)));
Frame::ETransitionType t = frame->GetTransitionType();
int ft = frame->GetFrameType();
// Sometimes we may not want to show an UM chain b/c we know it's just
// code inside of mscorwks. (Eg: Funcevals & AD transitions both fall into this category).