1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5
6//
7// File: DebuggerModule.cpp
8//
9// Stuff for tracking DebuggerModules.
10//
11//*****************************************************************************
12
13#include "stdafx.h"
14#include "../inc/common.h"
15#include "perflog.h"
16#include "eeconfig.h" // This is here even for retail & free builds...
17#include "vars.hpp"
18#include <limits.h>
19#include "ilformatter.h"
20#include "debuginfostore.h"
21#include "../../vm/methoditer.h"
22
23#ifndef DACCESS_COMPILE
24
25bool DbgIsSpecialILOffset(DWORD offset)
26{
27 LIMITED_METHOD_CONTRACT;
28
29 return (offset == (ULONG) ICorDebugInfo::PROLOG ||
30 offset == (ULONG) ICorDebugInfo::EPILOG ||
31 offset == (ULONG) ICorDebugInfo::NO_MAPPING);
32}
33
34// Helper to use w/ the debug stores.
35BYTE* InteropSafeNew(void * , size_t cBytes)
36{
37 BYTE * p = new (interopsafe, nothrow) BYTE[cBytes];
38 return p;
39}
40
41
42//
43// This is only fur internal debugging.
44//
45#ifdef LOGGING
46static void _dumpVarNativeInfo(ICorDebugInfo::NativeVarInfo* vni)
47{
48 WRAPPER_NO_CONTRACT;
49
50 LOG((LF_CORDB, LL_INFO1000000, "Var %02d: 0x%04x-0x%04x vlt=",
51 vni->varNumber,
52 vni->startOffset, vni->endOffset,
53 vni->loc.vlType));
54
55 switch (vni->loc.vlType)
56 {
57 case ICorDebugInfo::VLT_REG:
58 LOG((LF_CORDB, LL_INFO1000000, "REG reg=%d\n", vni->loc.vlReg.vlrReg));
59 break;
60
61 case ICorDebugInfo::VLT_REG_BYREF:
62 LOG((LF_CORDB, LL_INFO1000000, "REG_BYREF reg=%d\n", vni->loc.vlReg.vlrReg));
63 break;
64
65 case ICorDebugInfo::VLT_STK:
66 LOG((LF_CORDB, LL_INFO1000000, "STK reg=%d off=0x%04x (%d)\n",
67 vni->loc.vlStk.vlsBaseReg,
68 vni->loc.vlStk.vlsOffset,
69 vni->loc.vlStk.vlsOffset));
70 break;
71
72 case ICorDebugInfo::VLT_STK_BYREF:
73 LOG((LF_CORDB, LL_INFO1000000, "STK_BYREF reg=%d off=0x%04x (%d)\n",
74 vni->loc.vlStk.vlsBaseReg,
75 vni->loc.vlStk.vlsOffset,
76 vni->loc.vlStk.vlsOffset));
77 break;
78
79 case ICorDebugInfo::VLT_REG_REG:
80 LOG((LF_CORDB, LL_INFO1000000, "REG_REG reg1=%d reg2=%d\n",
81 vni->loc.vlRegReg.vlrrReg1,
82 vni->loc.vlRegReg.vlrrReg2));
83 break;
84
85 case ICorDebugInfo::VLT_REG_STK:
86 LOG((LF_CORDB, LL_INFO1000000, "REG_STK reg=%d basereg=%d off=0x%04x (%d)\n",
87 vni->loc.vlRegStk.vlrsReg,
88 vni->loc.vlRegStk.vlrsStk.vlrssBaseReg,
89 vni->loc.vlRegStk.vlrsStk.vlrssOffset,
90 vni->loc.vlRegStk.vlrsStk.vlrssOffset));
91 break;
92
93 case ICorDebugInfo::VLT_STK_REG:
94 LOG((LF_CORDB, LL_INFO1000000, "STK_REG basereg=%d off=0x%04x (%d) reg=%d\n",
95 vni->loc.vlStkReg.vlsrStk.vlsrsBaseReg,
96 vni->loc.vlStkReg.vlsrStk.vlsrsOffset,
97 vni->loc.vlStkReg.vlsrStk.vlsrsOffset,
98 vni->loc.vlStkReg.vlsrReg));
99 break;
100
101 case ICorDebugInfo::VLT_STK2:
102 LOG((LF_CORDB, LL_INFO1000000, "STK_STK reg=%d off=0x%04x (%d)\n",
103 vni->loc.vlStk2.vls2BaseReg,
104 vni->loc.vlStk2.vls2Offset,
105 vni->loc.vlStk2.vls2Offset));
106 break;
107
108 case ICorDebugInfo::VLT_FPSTK:
109 LOG((LF_CORDB, LL_INFO1000000, "FPSTK reg=%d\n",
110 vni->loc.vlFPstk.vlfReg));
111 break;
112
113 case ICorDebugInfo::VLT_FIXED_VA:
114 LOG((LF_CORDB, LL_INFO1000000, "FIXED_VA offset=%d (%d)\n",
115 vni->loc.vlFixedVarArg.vlfvOffset,
116 vni->loc.vlFixedVarArg.vlfvOffset));
117 break;
118
119
120 default:
121 LOG((LF_CORDB, LL_INFO1000000, "???\n"));
122 break;
123 }
124}
125#endif
126
127#if defined(WIN64EXCEPTIONS)
128void DebuggerJitInfo::InitFuncletAddress()
129{
130 CONTRACTL
131 {
132 SO_INTOLERANT;
133 NOTHROW;
134 GC_NOTRIGGER;
135 }
136 CONTRACTL_END;
137
138 m_funcletCount = (int)g_pEEInterface->GetFuncletStartOffsets((const BYTE*)m_addrOfCode, NULL, 0);
139
140 if (m_funcletCount == 0)
141 {
142 _ASSERTE(m_rgFunclet == NULL);
143 return;
144 }
145
146 m_rgFunclet = (DWORD*)(new (interopsafe, nothrow) DWORD[m_funcletCount]);
147
148 // All bets are off for stepping this method.
149 if (m_rgFunclet == NULL)
150 {
151 m_funcletCount = 0;
152 return;
153 }
154
155 // This will get the offsets relative to the parent method start as if
156 // the funclet was in contiguous memory (i.e. not hot/cold split).
157 g_pEEInterface->GetFuncletStartOffsets((const BYTE*)m_addrOfCode, m_rgFunclet, m_funcletCount);
158}
159
160//
161// DebuggerJitInfo::GetFuncletOffsetByIndex()
162//
163// Given a funclet index, return its starting offset.
164//
165// parameters: index - index of the funclet
166//
167// return value: starting offset of the specified funclet, or -1 if the index is invalid
168//
169DWORD DebuggerJitInfo::GetFuncletOffsetByIndex(int index)
170{
171 LIMITED_METHOD_CONTRACT;
172
173 if (index < 0 || index >= m_funcletCount)
174 {
175 return (-1);
176 }
177
178 return m_rgFunclet[index];
179}
180
181//
182// DebuggerJitInfo::GetFuncletIndex()
183//
184// Given an offset or an absolute address, return the index of the funclet containing it.
185//
186// parameters: offsetOrAddr - an offset or an absolute address in the method
187// mode - whether the first argument is an offset or an absolute address
188//
189// return value: the index of the funclet containing the specified offset or address,
190// or -1 if it's invalid
191//
192int DebuggerJitInfo::GetFuncletIndex(CORDB_ADDRESS offsetOrAddr, GetFuncletIndexMode mode)
193{
194 WRAPPER_NO_CONTRACT;
195
196 DWORD offset = 0;
197 if (mode == GFIM_BYOFFSET)
198 {
199 offset = (DWORD)offsetOrAddr;
200 }
201
202 // If the address doesn't fall in any of the funclets (or if the
203 // method doesn't have any funclet at all), then return PARENT_METHOD_INDEX.
204 // <TODO>
205 // What if there's an overflow?
206 // </TODO>
207 if (!m_codeRegionInfo.IsMethodAddress((const BYTE *)(mode == GFIM_BYOFFSET ? (size_t)m_codeRegionInfo.OffsetToAddress(offset) : offsetOrAddr)))
208 {
209 return PARENT_METHOD_INDEX;
210 }
211
212 if ( ( m_funcletCount == 0 ) ||
213 ( (mode == GFIM_BYOFFSET) && (offset < m_rgFunclet[0]) ) ||
214 ( (mode == GFIM_BYADDRESS) && (offsetOrAddr < (size_t)m_codeRegionInfo.OffsetToAddress(m_rgFunclet[0])) ) )
215 {
216 return PARENT_METHOD_INDEX;
217 }
218
219 for (int i = 0; i < m_funcletCount; i++)
220 {
221 if (i == (m_funcletCount - 1))
222 {
223 return i;
224 }
225 else if ( ( (mode == GFIM_BYOFFSET) && (offset < m_rgFunclet[i+1]) ) ||
226 ( (mode == GFIM_BYADDRESS) && (offsetOrAddr < (size_t)m_codeRegionInfo.OffsetToAddress(m_rgFunclet[i+1])) ) )
227 {
228 return i;
229 }
230 }
231
232 UNREACHABLE();
233}
234
235#endif // WIN64EXCEPTIONS
236
237// It is entirely possible that we have multiple sequence points for the
238// same IL offset (because of funclets, optimization, etc.). Just to be
239// uniform in all cases, let's return the sequence point with the smallest
240// native offset if fWantFirst is TRUE.
241#if defined(WIN64EXCEPTIONS)
242#define ADJUST_MAP_ENTRY(_map, _wantFirst) \
243 if ((_wantFirst)) \
244 for ( ; (_map) > m_sequenceMap && (((_map)-1)->ilOffset == (_map)->ilOffset); (_map)--); \
245 else \
246 for ( ; (_map) < m_sequenceMap + (m_sequenceMapCount-1) && (((_map)+1)->ilOffset == (_map)->ilOffset); (_map)++);
247#else
248#define ADJUST_MAP_ENTRY(_map, _wantFirst)
249#endif // _WIN64
250
251DebuggerJitInfo::DebuggerJitInfo(DebuggerMethodInfo *minfo, MethodDesc *fd) :
252 m_fd(fd),
253 m_pLoaderModule(fd->GetLoaderModule()),
254 m_jitComplete(false),
255#ifdef EnC_SUPPORTED
256 m_encBreakpointsApplied(false),
257#endif //EnC_SUPPORTED
258 m_methodInfo(minfo),
259 m_addrOfCode(NULL),
260 m_sizeOfCode(0), m_prevJitInfo(NULL), m_nextJitInfo(NULL),
261 m_lastIL(0),
262 m_sequenceMap(NULL),
263 m_sequenceMapCount(0),
264 m_callsiteMap(NULL),
265 m_callsiteMapCount(0),
266 m_sequenceMapSorted(false),
267 m_varNativeInfo(NULL), m_varNativeInfoCount(0),
268 m_fAttemptInit(false)
269#if defined(WIN64EXCEPTIONS)
270 ,m_rgFunclet(NULL)
271 , m_funcletCount(0)
272#endif // defined(WIN64EXCEPTIONS)
273{
274 WRAPPER_NO_CONTRACT;
275
276 // A DJI is just the debugger's cache of interesting information +
277 // various debugger-specific state for a method (like Enc).
278 // So only be createing DJIs when a debugger is actually attached.
279 // The profiler also piggy-backs on the DJIs.
280 // @Todo - the managed stackwalker in the BCL also builds on DJIs.
281 //_ASSERTE(CORDebuggerAttached() || CORProfilerPresent());
282
283 _ASSERTE(minfo);
284 m_encVersion = minfo->GetCurrentEnCVersion();
285 _ASSERTE(m_encVersion >= CorDB_DEFAULT_ENC_FUNCTION_VERSION);
286 LOG((LF_CORDB,LL_EVERYTHING, "DJI::DJI : created at 0x%x\n", this));
287
288 // Debugger doesn't track LightWeight codegen methods.
289 // We should never even be creating a DJI for one.
290 _ASSERTE(!m_fd->IsDynamicMethod());
291}
292
293DebuggerILToNativeMap *DebuggerJitInfo::MapILOffsetToMapEntry(SIZE_T offset, BOOL *exact, BOOL fWantFirst)
294{
295 CONTRACTL
296 {
297 NOTHROW;
298 GC_NOTRIGGER;
299 MODE_ANY;
300 CAN_TAKE_LOCK; // GetSequenceMapCount calls LazyInitBounds() which can eventually
301 // call ExecutionManager::IncrementReader
302 }
303 CONTRACTL_END;
304
305 // Ideally we should be able to assert this, since the binary search in this function
306 // assumes that the sequence points are sorted by IL offset (NO_MAPPING, PROLOG, and EPILOG
307 // are actually -1, -2, and -3, respectively). However, the sequence points in pdb's use
308 // -1 to mean "end of the method", which is different from our semantics of using 0.
309 // _ASSERTE(offset != NO_MAPPING && offset != PROLOG && offset != EPILOG);
310
311 //
312 // Binary search for matching map element.
313 //
314
315 DebuggerILToNativeMap *mMin = GetSequenceMap();
316 DebuggerILToNativeMap *mMax = mMin + GetSequenceMapCount();
317
318 _ASSERTE(m_sequenceMapSorted);
319 _ASSERTE( mMin < mMax ); //otherwise we have no code
320
321 if (exact)
322 {
323 *exact = FALSE;
324 }
325
326 if (mMin)
327 {
328 while (mMin + 1 < mMax)
329 {
330 _ASSERTE(mMin>=m_sequenceMap);
331 DebuggerILToNativeMap *mMid = mMin + ((mMax - mMin)>>1);
332 _ASSERTE(mMid>=m_sequenceMap);
333
334 if (offset == mMid->ilOffset)
335 {
336 if (exact)
337 {
338 *exact = TRUE;
339 }
340 ADJUST_MAP_ENTRY(mMid, fWantFirst);
341 return mMid;
342 }
343 else if (offset < mMid->ilOffset && mMid->ilOffset != (ULONG) ICorDebugInfo::PROLOG)
344 {
345 mMax = mMid;
346 }
347 else
348 {
349 mMin = mMid;
350 }
351 }
352
353 if (exact && offset == mMin->ilOffset)
354 {
355 *exact = TRUE;
356 }
357 ADJUST_MAP_ENTRY(mMin, fWantFirst);
358 }
359 return mMin;
360}
361
362void DebuggerJitInfo::InitILToNativeOffsetIterator(ILToNativeOffsetIterator &iterator, SIZE_T ilOffset)
363{
364 WRAPPER_NO_CONTRACT;
365
366 iterator.Init(this, ilOffset);
367}
368
369
370DebuggerJitInfo::NativeOffset DebuggerJitInfo::MapILOffsetToNative(DebuggerJitInfo::ILOffset ilOffset)
371{
372 CONTRACTL
373 {
374 SO_NOT_MAINLINE;
375 NOTHROW;
376 GC_NOTRIGGER;
377 }
378 CONTRACTL_END;
379
380 NativeOffset resultOffset;
381
382 DebuggerILToNativeMap *map = MapILOffsetToMapEntry(ilOffset.m_ilOffset, &(resultOffset.m_fExact));
383
384#if defined(WIN64EXCEPTIONS)
385 // See if we want the map entry for the parent.
386 if (ilOffset.m_funcletIndex <= PARENT_METHOD_INDEX)
387 {
388#endif // _WIN64
389 PREFIX_ASSUME( map != NULL );
390 LOG((LF_CORDB, LL_INFO10000, "DJI::MILOTN: ilOff 0x%x to nat 0x%x exact:0x%x (Entry IL Off:0x%x)\n",
391 ilOffset.m_ilOffset, map->nativeStartOffset, resultOffset.m_fExact, map->ilOffset));
392
393 resultOffset.m_nativeOffset = map->nativeStartOffset;
394
395#if defined(WIN64EXCEPTIONS)
396 }
397 else
398 {
399 // funcletIndex is guaranteed to be >= 0 at this point.
400 if (ilOffset.m_funcletIndex > (m_funcletCount - 1))
401 {
402 resultOffset.m_fExact = FALSE;
403 resultOffset.m_nativeOffset = ((SIZE_T)-1);
404 }
405 else
406 {
407 // Initialize the funclet range.
408 // ASSUMES that funclets are contiguous which they currently are...
409 DWORD funcletStartOffset = GetFuncletOffsetByIndex(ilOffset.m_funcletIndex);
410 DWORD funcletEndOffset;
411 if (ilOffset.m_funcletIndex < (m_funcletCount - 1))
412 {
413 funcletEndOffset = GetFuncletOffsetByIndex(ilOffset.m_funcletIndex + 1);
414 }
415 else
416 {
417 funcletEndOffset = (DWORD)m_sizeOfCode;
418 }
419
420 SIZE_T ilTargetOffset = map->ilOffset;
421
422 DebuggerILToNativeMap *mapEnd = GetSequenceMap() + GetSequenceMapCount();
423
424 for (; map < mapEnd && map->ilOffset == ilTargetOffset; map++)
425 {
426 if ((map->nativeStartOffset >= funcletStartOffset) &&
427 (map->nativeStartOffset < funcletEndOffset))
428 {
429 // This is the normal case where the start offset falls in
430 // the range of the funclet.
431 resultOffset.m_nativeOffset = map->nativeStartOffset;
432 break;
433 }
434 }
435
436 if (map == mapEnd || map->ilOffset != ilTargetOffset)
437 {
438 resultOffset.m_fExact = FALSE;
439 resultOffset.m_nativeOffset = ((SIZE_T)-1);
440 }
441 }
442 }
443#endif // WIN64EXCEPTIONS
444
445 return resultOffset;
446}
447
448
449DebuggerJitInfo::ILToNativeOffsetIterator::ILToNativeOffsetIterator()
450{
451 LIMITED_METHOD_CONTRACT;
452
453 m_dji = NULL;
454 m_currentILOffset.m_ilOffset = INVALID_IL_OFFSET;
455#ifdef WIN64EXCEPTIONS
456 m_currentILOffset.m_funcletIndex = PARENT_METHOD_INDEX;
457#endif
458}
459
460void DebuggerJitInfo::ILToNativeOffsetIterator::Init(DebuggerJitInfo* dji, SIZE_T ilOffset)
461{
462 WRAPPER_NO_CONTRACT;
463
464 m_dji = dji;
465 m_currentILOffset.m_ilOffset = ilOffset;
466#ifdef WIN64EXCEPTIONS
467 m_currentILOffset.m_funcletIndex = PARENT_METHOD_INDEX;
468#endif
469
470 m_currentNativeOffset = m_dji->MapILOffsetToNative(m_currentILOffset);
471}
472
473bool DebuggerJitInfo::ILToNativeOffsetIterator::IsAtEnd()
474{
475 LIMITED_METHOD_CONTRACT;
476
477 return (m_currentILOffset.m_ilOffset == INVALID_IL_OFFSET);
478}
479
480SIZE_T DebuggerJitInfo::ILToNativeOffsetIterator::Current(BOOL* pfExact)
481{
482 LIMITED_METHOD_CONTRACT;
483
484 if (pfExact != NULL)
485 {
486 *pfExact = m_currentNativeOffset.m_fExact;
487 }
488 return m_currentNativeOffset.m_nativeOffset;
489}
490
491SIZE_T DebuggerJitInfo::ILToNativeOffsetIterator::CurrentAssertOnlyOne(BOOL* pfExact)
492{
493 WRAPPER_NO_CONTRACT;
494
495 SIZE_T nativeOffset = Current(pfExact);
496
497 Next();
498 _ASSERTE(IsAtEnd());
499
500 return nativeOffset;
501}
502
503void DebuggerJitInfo::ILToNativeOffsetIterator::Next()
504{
505#if defined(WIN64EXCEPTIONS)
506 NativeOffset tmpNativeOffset;
507
508 for (m_currentILOffset.m_funcletIndex += 1;
509 m_currentILOffset.m_funcletIndex < m_dji->GetFuncletCount();
510 m_currentILOffset.m_funcletIndex++)
511 {
512 tmpNativeOffset = m_dji->MapILOffsetToNative(m_currentILOffset);
513 if (tmpNativeOffset.m_nativeOffset != ((SIZE_T)-1) &&
514 tmpNativeOffset.m_nativeOffset != m_currentNativeOffset.m_nativeOffset)
515 {
516 m_currentNativeOffset = tmpNativeOffset;
517 break;
518 }
519 }
520
521 if (m_currentILOffset.m_funcletIndex == m_dji->GetFuncletCount())
522 {
523 m_currentILOffset.m_ilOffset = INVALID_IL_OFFSET;
524 }
525#else // !WIN64EXCEPTIONS
526 m_currentILOffset.m_ilOffset = INVALID_IL_OFFSET;
527#endif // !WIN64EXCEPTIONS
528}
529
530
531
532// SIZE_T DebuggerJitInfo::MapSpecialToNative(): Maps something like
533// a prolog to a native offset.
534// CordDebugMappingResult mapping: Mapping type to be looking for.
535// SIZE_T which: Which one. <TODO>For now, set to zero. <@todo Later, we'll
536// change this to some value that we get back from MapNativeToILOffset
537// to indicate which of the (possibly multiple epilogs) that may
538// be present.</TODO>
539
540SIZE_T DebuggerJitInfo::MapSpecialToNative(CorDebugMappingResult mapping,
541 SIZE_T which,
542 BOOL *pfAccurate)
543{
544 CONTRACTL
545 {
546 SO_NOT_MAINLINE;
547 NOTHROW;
548 GC_NOTRIGGER;
549 PRECONDITION(NULL != pfAccurate);
550 }
551 CONTRACTL_END;
552
553 LOG((LF_CORDB, LL_INFO10000, "DJI::MSTN map:0x%x which:0x%x\n", mapping, which));
554
555 bool fFound;
556 SIZE_T cFound = 0;
557
558 DebuggerILToNativeMap *m = GetSequenceMap();
559 DebuggerILToNativeMap *mEnd = m + GetSequenceMapCount();
560 if (m)
561 {
562 while(m < mEnd)
563 {
564 _ASSERTE(m>=GetSequenceMap());
565
566 fFound = false;
567
568 if (DbgIsSpecialILOffset(m->ilOffset))
569 cFound++;
570
571 if (cFound == which)
572 {
573 _ASSERTE( (mapping == MAPPING_PROLOG &&
574 m->ilOffset == (ULONG) ICorDebugInfo::PROLOG) ||
575 (mapping == MAPPING_EPILOG &&
576 m->ilOffset == (ULONG) ICorDebugInfo::EPILOG) ||
577 ((mapping == MAPPING_NO_INFO || mapping == MAPPING_UNMAPPED_ADDRESS) &&
578 m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
579 );
580
581 (*pfAccurate) = TRUE;
582 LOG((LF_CORDB, LL_INFO10000, "DJI::MSTN found mapping to nat:0x%x\n",
583 m->nativeStartOffset));
584 return m->nativeStartOffset;
585 }
586 m++;
587 }
588 }
589
590 LOG((LF_CORDB, LL_INFO10000, "DJI::MSTN No mapping found :(\n"));
591 (*pfAccurate) = FALSE;
592
593 return 0;
594}
595
596#if defined(WIN64EXCEPTIONS)
597//
598// DebuggerJitInfo::MapILOffsetToNativeForSetIP()
599//
600// This function maps an IL offset to a native offset, taking into account cloned finallys and nested EH clauses.
601//
602// parameters: offsetILTo - the destination IP, in IL offset
603// funcletIndexFrom - the funclet index of the source IP
604// pEHRT - tree structure for keeping track of EH clause information
605// pExact - pointer for returning whether the mapping is exact or not
606//
607// return value: destination IP, in native offset
608//
609SIZE_T DebuggerJitInfo::MapILOffsetToNativeForSetIP(SIZE_T offsetILTo, int funcletIndexFrom,
610 EHRangeTree* pEHRT, BOOL* pExact)
611{
612 CONTRACTL
613 {
614 SO_NOT_MAINLINE;
615 NOTHROW;
616 GC_NOTRIGGER;
617 MODE_ANY;
618 }
619 CONTRACTL_END;
620
621 DebuggerILToNativeMap* pMap = MapILOffsetToMapEntry(offsetILTo, pExact, TRUE);
622 DebuggerILToNativeMap* pMapEnd = GetSequenceMap() + GetSequenceMapCount();
623
624 _ASSERTE(pMap == m_sequenceMap ||
625 (pMap - 1)->ilOffset == (ULONG)ICorDebugInfo::NO_MAPPING ||
626 (pMap - 1)->ilOffset == (ULONG)ICorDebugInfo::PROLOG ||
627 (pMap - 1)->ilOffset == (ULONG)ICorDebugInfo::EPILOG ||
628 pMap->ilOffset > (pMap - 1)->ilOffset);
629
630 SIZE_T offsetNatTo = pMap->nativeStartOffset;
631
632 if (m_funcletCount == 0 ||
633 pEHRT == NULL ||
634 FAILED(pEHRT->m_hrInit))
635 {
636 return offsetNatTo;
637 }
638
639 // Multiple sequence points may have the same IL offset, which means that the code is duplicated in
640 // multiple funclets and/or in the parent method. If the destination offset maps to multiple sequence
641 // points (and hence to multiple funclets), we try to find the a sequence point which is in the same
642 // funclet as the source sequence point. If we can't find one, then the operation is going to fail
643 // anyway, so we just return the first sequence point we find.
644 for (DebuggerILToNativeMap* pMapCur = pMap + 1;
645 (pMapCur < pMapEnd) && (pMapCur->ilOffset == pMap->ilOffset);
646 pMapCur++)
647 {
648 int funcletIndexTo = GetFuncletIndex(pMapCur->nativeStartOffset, DebuggerJitInfo::GFIM_BYOFFSET);
649 if (funcletIndexFrom == funcletIndexTo)
650 {
651 return pMapCur->nativeStartOffset;
652 }
653 }
654
655 return offsetNatTo;
656}
657#endif // _WIN64
658
659// void DebuggerJitInfo::MapILRangeToMapEntryRange(): MIRTMER
660// calls MapILOffsetToNative for the startOffset (putting the
661// result into start), and the endOffset (putting the result into end).
662// SIZE_T startOffset: IL offset from beginning of function.
663// SIZE_T endOffset: IL offset from beginngin of function,
664// or zero to indicate that the end of the function should be used.
665// DebuggerILToNativeMap **start: Contains start & end
666// native offsets that correspond to startOffset. Set to NULL if
667// there is no mapping info.
668// DebuggerILToNativeMap **end: Contains start & end native
669// offsets that correspond to endOffset. Set to NULL if there
670// is no mapping info.
671void DebuggerJitInfo::MapILRangeToMapEntryRange(SIZE_T startOffset,
672 SIZE_T endOffset,
673 DebuggerILToNativeMap **start,
674 DebuggerILToNativeMap **end)
675{
676 CONTRACTL
677 {
678 SO_NOT_MAINLINE;
679 NOTHROW;
680 GC_NOTRIGGER;
681 }
682 CONTRACTL_END;
683
684 LOG((LF_CORDB, LL_INFO1000000,
685 "DJI::MIRTMER: IL 0x%04x-0x%04x\n",
686 startOffset, endOffset));
687
688 if (GetSequenceMapCount() == 0)
689 {
690 *start = NULL;
691 *end = NULL;
692 return;
693 }
694
695 *start = MapILOffsetToMapEntry(startOffset);
696
697 //
698 // end points to the last range that endOffset maps to, not past
699 // the last range.
700 // We want to return the last IL, and exclude the epilog
701 if (endOffset == 0)
702 {
703 *end = GetSequenceMap() + GetSequenceMapCount() - 1;
704 _ASSERTE(*end>=m_sequenceMap);
705
706 while ( ((*end)->ilOffset == (ULONG) ICorDebugInfo::EPILOG||
707 (*end)->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
708 && (*end) > m_sequenceMap)
709 {
710 (*end)--;
711 _ASSERTE(*end>=m_sequenceMap);
712
713 }
714 }
715 else
716 *end = MapILOffsetToMapEntry(endOffset - 1, NULL
717 WIN64_ARG(FALSE));
718
719 _ASSERTE(*end>=m_sequenceMap);
720
721
722 LOG((LF_CORDB, LL_INFO1000000,
723 "DJI::MIRTMER: IL 0x%04x-0x%04x --> 0x%04x 0x%08x-0x%08x\n"
724 " --> 0x%04x 0x%08x-0x%08x\n",
725 startOffset, endOffset,
726 (*start)->ilOffset,
727 (*start)->nativeStartOffset, (*start)->nativeEndOffset,
728 (*end)->ilOffset,
729 (*end)->nativeStartOffset, (*end)->nativeEndOffset));
730}
731
732// @dbgtodo Microsoft inspection: This function has been replicated in DacDbiStructures so
733// this version can be deleted when inspection is complete.
734
735// DWORD DebuggerJitInfo::MapNativeOffsetToIL(): Given a native
736// offset for the DebuggerJitInfo, compute
737// the IL offset from the beginning of the same method.
738// Returns: Offset of the IL instruction that contains
739// the native offset,
740// SIZE_T nativeOffset: [IN] Native Offset
741// CorDebugMappingResult *map: [OUT] explains the
742// quality of the matching & special cases
743// SIZE_T which: It's possible to have multiple EPILOGs, or
744// multiple unmapped regions within a method. This opaque value
745// specifies which special region we're talking about. This
746// param has no meaning if map & (MAPPING_EXACT|MAPPING_APPROXIMATE)
747// Basically, this gets handed back to MapSpecialToNative, later.
748DWORD DebuggerJitInfo::MapNativeOffsetToIL(SIZE_T nativeOffsetToMap,
749 CorDebugMappingResult *map,
750 DWORD *which,
751 BOOL skipPrologs)
752{
753 CONTRACTL
754 {
755 SO_INTOLERANT;
756 NOTHROW;
757 GC_NOTRIGGER;
758 PRECONDITION(map != NULL);
759 PRECONDITION(which != NULL);
760 }
761 CONTRACTL_END;
762
763 DWORD nativeOffset = (DWORD)nativeOffsetToMap;
764
765 (*which) = 0;
766 DebuggerILToNativeMap *m = GetSequenceMap();
767 DebuggerILToNativeMap *mEnd = m + GetSequenceMapCount();
768
769 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: nativeOffset = 0x%x\n", nativeOffset));
770
771 if (m)
772 {
773 while (m < mEnd)
774 {
775 _ASSERTE(m>=m_sequenceMap);
776
777#ifdef LOGGING
778 if (m->ilOffset == (ULONG) ICorDebugInfo::PROLOG )
779 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:PROLOG\n", m->nativeStartOffset, m->nativeEndOffset));
780 else if (m->ilOffset == (ULONG) ICorDebugInfo::EPILOG )
781 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:EPILOG\n", m->nativeStartOffset, m->nativeEndOffset));
782 else if (m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
783 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:NO MAP\n", m->nativeStartOffset, m->nativeEndOffset));
784 else
785 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:0x%x src:0x%x\n", m->nativeStartOffset, m->nativeEndOffset, m->ilOffset, m->source));
786#endif // LOGGING
787
788 if (m->ilOffset == (ULONG) ICorDebugInfo::PROLOG ||
789 m->ilOffset == (ULONG) ICorDebugInfo::EPILOG ||
790 m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
791 {
792 (*which)++;
793 }
794
795 if (nativeOffset >= m->nativeStartOffset
796 && ((m->nativeEndOffset == 0 &&
797 m->ilOffset != (ULONG) ICorDebugInfo::PROLOG)
798 || nativeOffset < m->nativeEndOffset))
799 {
800 ULONG ilOff = m->ilOffset;
801
802 if( m->ilOffset == (ULONG) ICorDebugInfo::PROLOG )
803 {
804 if (skipPrologs && nativeOffset < m->nativeEndOffset)
805 {
806 // If the caller requested to skip prologs, we simply restart the walk
807 // with the offset set to the end of the prolog.
808 nativeOffset = m->nativeEndOffset;
809 continue;
810 }
811
812 ilOff = 0;
813 (*map) = MAPPING_PROLOG;
814 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: MAPPING_PROLOG\n"));
815
816 }
817 else if (m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
818 {
819 ilOff = 0;
820 (*map) = MAPPING_UNMAPPED_ADDRESS ;
821 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_"
822 "UNMAPPED_ADDRESS\n"));
823 }
824 else if( m->ilOffset == (ULONG) ICorDebugInfo::EPILOG )
825 {
826 ilOff = m_lastIL;
827 (*map) = MAPPING_EPILOG;
828 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_EPILOG\n"));
829 }
830 else if (nativeOffset == m->nativeStartOffset)
831 {
832 (*map) = MAPPING_EXACT;
833 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_EXACT\n"));
834 }
835 else
836 {
837 (*map) = MAPPING_APPROXIMATE;
838 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_"
839 "APPROXIMATE\n"));
840 }
841
842 return ilOff;
843 }
844 m++;
845 }
846 }
847
848 (*map) = MAPPING_NO_INFO;
849 LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:NO_INFO\n"));
850 return 0;
851}
852
853/******************************************************************************
854 *
855 ******************************************************************************/
856DebuggerJitInfo::~DebuggerJitInfo()
857{
858 TRACE_FREE(m_sequenceMap);
859 if (m_sequenceMap != NULL)
860 {
861 DeleteInteropSafe(((BYTE *)m_sequenceMap));
862 }
863
864 TRACE_FREE(m_varNativeInfo);
865 if (m_varNativeInfo != NULL)
866 {
867 DeleteInteropSafe(m_varNativeInfo);
868 }
869
870#if defined(WIN64EXCEPTIONS)
871 if (m_rgFunclet)
872 {
873 DeleteInteropSafe(m_rgFunclet);
874 m_rgFunclet = NULL;
875 }
876#endif // WIN64EXCEPTIONS
877
878
879#ifdef _DEBUG
880 // Trash pointers to garbage.
881 // Don't null out since there may be runtime checks against NULL.
882 // Set to a non-null random pointer value that will cause an immediate AV on deref.
883 m_fd = (MethodDesc*) 0x1;
884 m_methodInfo = (DebuggerMethodInfo*) 0x1;
885 m_prevJitInfo = (DebuggerJitInfo*) 0x01;
886 m_nextJitInfo = (DebuggerJitInfo*) 0x01;
887#endif
888
889
890 LOG((LF_CORDB,LL_EVERYTHING, "DJI::~DJI : deleted at 0x%p\n", this));
891}
892
893// Lazy initialize the Debugger-Jit-Info
894void DebuggerJitInfo::LazyInitBounds()
895{
896 CONTRACTL
897 {
898 SO_INTOLERANT;
899 NOTHROW;
900 GC_NOTRIGGER;
901 PRECONDITION(ThisMaybeHelperThread());
902 PRECONDITION(!g_pDebugger->HasDebuggerDataLock());
903 } CONTRACTL_END;
904
905 LOG((LF_CORDB, LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x m_fAttemptInit %s\n", this, m_fAttemptInit == true ? "true": "false"));
906
907 // Only attempt lazy-init once
908 if (m_fAttemptInit)
909 {
910 return;
911 }
912
913 EX_TRY
914 {
915 LOG((LF_CORDB, LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x Initing\n", this));
916
917 // Should have already been jitted
918 _ASSERTE(this->m_jitComplete);
919
920 MethodDesc * mdesc = this->m_fd;
921 DebugInfoRequest request;
922
923 _ASSERTE(this->m_addrOfCode != NULL); // must have address to disambguate the Enc cases.
924 // Caller already resolved generics when they craeted the DJI, so we don't need to repeat.
925 // Note the MethodDesc may not yet have the jitted info, so we'll also use the starting address we got in the jit complete callback.
926 request.InitFromStartingAddr(mdesc, (PCODE)this->m_addrOfCode);
927
928 // Bounds info.
929 ULONG32 cMap = 0;
930 ICorDebugInfo::OffsetMapping *pMap = NULL;
931 ULONG32 cVars = 0;
932 ICorDebugInfo::NativeVarInfo *pVars = NULL;
933
934 BOOL fSuccess = DebugInfoManager::GetBoundariesAndVars(
935 request,
936 InteropSafeNew, NULL, // allocator
937 &cMap, &pMap,
938 &cVars, &pVars);
939
940 LOG((LF_CORDB,LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x GetBoundariesAndVars success=0x%x\n", this, fSuccess));
941
942 // SetBoundaries uses the CodeVersionManager, need to take it now for lock ordering reasons
943 CodeVersionManager::TableLockHolder lockHolder(m_fd->GetCodeVersionManager());
944 Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger);
945
946 if (!m_fAttemptInit)
947 {
948 if (fSuccess)
949 {
950 this->SetBoundaries(cMap, pMap);
951 this->SetVars(cVars, pVars);
952 }
953 m_fAttemptInit = true;
954 }
955 else
956 {
957 DeleteInteropSafe(pMap);
958 DeleteInteropSafe(pVars);
959 }
960 // DebuggerDataLockHolder out of scope - release implied
961 }
962 EX_CATCH
963 {
964 LOG((LF_CORDB,LL_WARNING, "DJI::LazyInitBounds: this=0x%x Exception was thrown and caught\n", this));
965 // Just catch the exception. The DJI maps may or may-not be intialized,
966 // but they should still be in a consistent state, so we should be ok.
967 }
968 EX_END_CATCH(SwallowAllExceptions)
969}
970
971/******************************************************************************
972 * SetVars() takes ownership of pVars
973 ******************************************************************************/
974void DebuggerJitInfo::SetVars(ULONG32 cVars, ICorDebugInfo::NativeVarInfo *pVars)
975{
976 LIMITED_METHOD_CONTRACT;
977
978 _ASSERTE(m_varNativeInfo == NULL);
979
980 m_varNativeInfo = pVars;
981 m_varNativeInfoCount = cVars;
982
983 LOG((LF_CORDB, LL_INFO1000000, "D::sV: var count is %d\n",
984 m_varNativeInfoCount));
985
986#ifdef LOGGING
987 for (unsigned int i = 0; i < m_varNativeInfoCount; i++)
988 {
989 ICorDebugInfo::NativeVarInfo* vni = &(m_varNativeInfo[i]);
990 _dumpVarNativeInfo(vni);
991 }
992#endif
993}
994
995CHECK DebuggerJitInfo::Check() const
996{
997 LIMITED_METHOD_CONTRACT;
998
999 CHECK_OK;
1000}
1001
1002// Invariants for a DebuggerJitInfo
1003// These should always be true at any well defined point.
1004CHECK DebuggerJitInfo::Invariant() const
1005{
1006 LIMITED_METHOD_CONTRACT;
1007 CHECK((m_sequenceMapCount == 0) == (m_sequenceMap == NULL));
1008 CHECK(m_methodInfo != NULL);
1009 CHECK(m_fd != NULL);
1010
1011 CHECK_OK;
1012}
1013
1014
1015#if !defined(DACCESS_COMPILE)
1016/******************************************************************************
1017 * SetBoundaries() takes ownership of pMap
1018 ******************************************************************************/
1019void DebuggerJitInfo::SetBoundaries(ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap)
1020{
1021 CONTRACTL
1022 {
1023 SO_INTOLERANT;
1024 THROWS;
1025 GC_NOTRIGGER;
1026 PRECONDITION(CheckPointer(this));
1027 }
1028 CONTRACTL_END;
1029
1030 LOG((LF_CORDB,LL_EVERYTHING, "DJI::SetBoundaries: this=0x%x cMap=0x%x pMap=0x%x\n", this, cMap, pMap));
1031 _ASSERTE((cMap == 0) == (pMap == NULL));
1032 _ASSERTE(m_sequenceMap == NULL);
1033
1034 if (cMap == 0)
1035 return;
1036
1037 ULONG ilLast = 0;
1038#ifdef _DEBUG
1039 // We assume that the map is sorted by native offset
1040 if (cMap > 1)
1041 {
1042 for(ICorDebugInfo::OffsetMapping * pEntry = pMap;
1043 pEntry < (pMap + cMap - 1);
1044 pEntry++)
1045 {
1046 _ASSERTE(pEntry->nativeOffset <= (pEntry+1)->nativeOffset);
1047 }
1048 }
1049#endif //_DEBUG
1050
1051 //
1052 // <TODO>@todo perf: allocating these on the heap is slow. We could do
1053 // better knowing that these live for the life of the run, just
1054 // like the DebuggerJitInfo's.</TODO>
1055 //
1056 m_sequenceMap = (DebuggerILToNativeMap *)new (interopsafe) DebuggerILToNativeMap[cMap];
1057 LOG((LF_CORDB,LL_EVERYTHING, "DJI::SetBoundaries: this=0x%x m_sequenceMap=0x%x\n", this, m_sequenceMap));
1058 _ASSERTE(m_sequenceMap != NULL); // we'll throw on null
1059
1060 m_sequenceMapCount = cMap;
1061
1062 DebuggerILToNativeMap *m = m_sequenceMap;
1063
1064 // For the instrumented-IL case, we need to remove all duplicate entries.
1065 // So we keep a record of the last old IL offset. If the current old IL
1066 // offset is the same as the last old IL offset, we remove it.
1067 // Pick a unique initial value (-10) so that the 1st doesn't accidentally match.
1068 int ilPrevOld = -10;
1069
1070 _ASSERTE(m_fd->GetCodeVersionManager()->LockOwnedByCurrentThread());
1071 CodeVersionManager *pCodeVersionManager = m_fd->GetCodeVersionManager();
1072 InstrumentedILOffsetMapping mapping;
1073
1074 NativeCodeVersion nativeVersion = pCodeVersionManager->GetNativeCodeVersion(m_fd, (PCODE)CORDB_ADDRESS_TO_PTR(m_addrOfCode));
1075 _ASSERTE(!nativeVersion.IsNull());
1076 ILCodeVersion ilVersion = nativeVersion.GetILCodeVersion();
1077 if (!ilVersion.IsDefaultVersion())
1078 {
1079 // Did the current rejit provide a map?
1080 const InstrumentedILOffsetMapping *pReJitMap = ilVersion.GetInstrumentedILMap();
1081 if (pReJitMap != NULL)
1082 {
1083 mapping = *pReJitMap;
1084 }
1085 }
1086 else if (m_methodInfo->HasInstrumentedILMap())
1087 {
1088 // If a ReJIT hasn't happened, check for a profiler provided map.
1089 mapping = m_methodInfo->GetRuntimeModule()->GetInstrumentedILOffsetMapping(m_methodInfo->m_token);
1090 }
1091
1092 //
1093 // <TODO>@todo perf: we could do the vast majority of this
1094 // post-processing work the first time the sequence point map is
1095 // demanded. That would allow us to simply hold the raw array for
1096 // 95% of the functions jitted while debugging, and 100% of them
1097 // when just running/tracking.</TODO>
1098 const DWORD call_inst = (DWORD)ICorDebugInfo::CALL_INSTRUCTION;
1099 for(ULONG32 idxJitMap = 0; idxJitMap < cMap; idxJitMap++)
1100 {
1101 const ICorDebugInfo::OffsetMapping * const pMapEntry = &pMap[idxJitMap];
1102 _ASSERTE(m >= m_sequenceMap);
1103 _ASSERTE(m < m_sequenceMap + m_sequenceMapCount);
1104
1105 ilLast = max((int)ilLast, (int)pMapEntry->ilOffset);
1106
1107 // Simply copy everything over, since we translate to
1108 // CorDebugMappingResults immediately prior to handing
1109 // back to user...
1110 m->nativeStartOffset = pMapEntry->nativeOffset;
1111 m->ilOffset = pMapEntry->ilOffset;
1112 m->source = pMapEntry->source;
1113
1114 // Keep in mind that if we have an instrumented code translation
1115 // table, we may have asked for completely different IL offsets
1116 // than the user thinks we did.....
1117
1118 // If we did instrument, then we can't have any sequence points that
1119 // are "in-between" the old-->new map that the profiler gave us.
1120 // Ex, if map is:
1121 // (6 old -> 36 new)
1122 // (8 old -> 50 new)
1123 // And the jit gives us an entry for 44 new, that will map back to 6 old.
1124 // Since the map can only have one entry for 6 old, we remove 44 new.
1125 if (!mapping.IsNull())
1126 {
1127 int ilThisOld = m_methodInfo->TranslateToInstIL(&mapping,
1128 pMapEntry->ilOffset,
1129 bInstrumentedToOriginal);
1130
1131 if (ilThisOld == ilPrevOld)
1132 {
1133 // If this translated to the same old IL offset as the last entry,
1134 // then this is "in between". Skip it.
1135 m_sequenceMapCount--; // one less seq point in the DJI's map
1136 continue;
1137 }
1138 m->ilOffset = ilThisOld;
1139 ilPrevOld = ilThisOld;
1140 }
1141
1142 if (m > m_sequenceMap && (m->source & call_inst) != call_inst)
1143 {
1144 DebuggerILToNativeMap *last = m-1;
1145 if ((last->source & call_inst) == call_inst)
1146 last = (last > m_sequenceMap) ? last - 1 : NULL;
1147
1148 if (last && (last->source & call_inst) != call_inst && m->ilOffset == last->ilOffset)
1149 {
1150 // JIT gave us an extra entry (probably zero), so mush
1151 // it into the one we've already got.
1152 // <TODO> Why does this happen?</TODO>
1153 m_sequenceMapCount--;
1154 continue;
1155 }
1156 }
1157
1158
1159 // Move to next entry in the debugger's table
1160 m++;
1161 } // end for
1162
1163 DeleteInteropSafe(pMap);
1164
1165 _ASSERTE(m == m_sequenceMap + m_sequenceMapCount);
1166
1167 m_lastIL = ilLast;
1168
1169 // Set nativeEndOffset in debugger's il->native map
1170 // Do this before we resort by IL.
1171 unsigned int i;
1172 for(i = 0; i < m_sequenceMapCount - 1; i++)
1173 {
1174 // We need to not use CALL_INSTRUCTION's IL start offset.
1175 unsigned int j = i + 1;
1176 while ((m_sequenceMap[j].source & call_inst) == call_inst && j < m_sequenceMapCount-1)
1177 j++;
1178
1179 m_sequenceMap[i].nativeEndOffset = m_sequenceMap[j].nativeStartOffset;
1180 }
1181
1182 m_sequenceMap[i].nativeEndOffset = 0;
1183 m_sequenceMap[i].source = (ICorDebugInfo::SourceTypes)
1184 ((DWORD) m_sequenceMap[i].source |
1185 (DWORD)ICorDebugInfo::NATIVE_END_OFFSET_UNKNOWN);
1186
1187 // Now resort by IL.
1188 MapSortIL isort(m_sequenceMap, m_sequenceMapCount);
1189
1190 isort.Sort();
1191
1192 m_sequenceMapSorted = true;
1193
1194 m_callsiteMapCount = m_sequenceMapCount;
1195 while (m_sequenceMapCount > 0 && (m_sequenceMap[m_sequenceMapCount-1].source & call_inst) == call_inst)
1196 m_sequenceMapCount--;
1197
1198 m_callsiteMap = m_sequenceMap + m_sequenceMapCount;
1199 m_callsiteMapCount -= m_sequenceMapCount;
1200
1201 LOG((LF_CORDB, LL_INFO100000, "DJI::SetBoundaries: this=0x%x boundary count is %d (%d callsites)\n",
1202 this, m_sequenceMapCount, m_callsiteMapCount));
1203
1204#ifdef LOGGING
1205 for (unsigned int count = 0; count < m_sequenceMapCount + m_callsiteMapCount; count++)
1206 {
1207 if( m_sequenceMap[count].ilOffset ==
1208 (ULONG) ICorDebugInfo::PROLOG )
1209 LOG((LF_CORDB, LL_INFO1000000,
1210 "D::sB: PROLOG --> 0x%08x -- 0x%08x",
1211 m_sequenceMap[count].nativeStartOffset,
1212 m_sequenceMap[count].nativeEndOffset));
1213 else if ( m_sequenceMap[count].ilOffset ==
1214 (ULONG) ICorDebugInfo::EPILOG )
1215 LOG((LF_CORDB, LL_INFO1000000,
1216 "D::sB: EPILOG --> 0x%08x -- 0x%08x",
1217 m_sequenceMap[count].nativeStartOffset,
1218 m_sequenceMap[count].nativeEndOffset));
1219 else if ( m_sequenceMap[count].ilOffset ==
1220 (ULONG) ICorDebugInfo::NO_MAPPING )
1221 LOG((LF_CORDB, LL_INFO1000000,
1222 "D::sB: NO MAP --> 0x%08x -- 0x%08x",
1223 m_sequenceMap[count].nativeStartOffset,
1224 m_sequenceMap[count].nativeEndOffset));
1225 else
1226 LOG((LF_CORDB, LL_INFO1000000,
1227 "D::sB: 0x%04x (Real:0x%04x) --> 0x%08x -- 0x%08x",
1228 m_sequenceMap[count].ilOffset,
1229 m_methodInfo->TranslateToInstIL(&mapping,
1230 m_sequenceMap[count].ilOffset,
1231 bOriginalToInstrumented),
1232 m_sequenceMap[count].nativeStartOffset,
1233 m_sequenceMap[count].nativeEndOffset));
1234
1235 LOG((LF_CORDB, LL_INFO1000000, " Src:0x%x\n", m_sequenceMap[count].source));
1236
1237 }
1238#endif //LOGGING
1239}
1240#endif // !DACCESS_COMPILE
1241
1242// Init a DJI after it's jitted.
1243void DebuggerJitInfo::Init(TADDR newAddress)
1244{
1245 // Shouldn't initialize while holding the lock b/c intialzing may call functions that lock,
1246 // and thus we'd have a locking violation.
1247 _ASSERTE(!g_pDebugger->HasDebuggerDataLock());
1248
1249 this->m_addrOfCode = (ULONG_PTR)PTR_TO_CORDB_ADDRESS((BYTE*) newAddress);
1250 this->m_jitComplete = true;
1251
1252 this->m_codeRegionInfo.InitializeFromStartAddress(PINSTRToPCODE((TADDR)this->m_addrOfCode));
1253 this->m_sizeOfCode = this->m_codeRegionInfo.getSizeOfTotalCode();
1254
1255 this->m_encVersion = this->m_methodInfo->GetCurrentEnCVersion();
1256
1257#if defined(WIN64EXCEPTIONS)
1258 this->InitFuncletAddress();
1259#endif // WIN64EXCEPTIONS
1260
1261 LOG((LF_CORDB,LL_INFO10000,"De::JITCo:Got DJI 0x%p(V %d),"
1262 "Hot section from 0x%p to 0x%p "
1263 "Cold section from 0x%p to 0x%p "
1264 "varCount=%d seqCount=%d\n",
1265 this, this->m_encVersion,
1266 this->m_codeRegionInfo.getAddrOfHotCode(),
1267 this->m_codeRegionInfo.getAddrOfHotCode() + this->m_codeRegionInfo.getSizeOfHotCode(),
1268 this->m_codeRegionInfo.getAddrOfColdCode(),
1269 this->m_codeRegionInfo.getAddrOfColdCode() + this->m_codeRegionInfo.getSizeOfColdCode(),
1270 (ULONG)this->m_addrOfCode,
1271 (ULONG)this->m_addrOfCode+(ULONG)this->m_sizeOfCode,
1272 this->GetVarNativeInfoCount(),
1273 this->GetSequenceMapCount()));
1274
1275#if defined(LOGGING)
1276 for (unsigned int i = 0; i < this->GetSequenceMapCount(); i++)
1277 {
1278 LOG((LF_CORDB, LL_INFO10000, "De::JITCo: seq map 0x%x - "
1279 "IL offset 0x%x native start offset 0x%x native end offset 0x%x source 0x%x\n",
1280 i, this->GetSequenceMap()[i].ilOffset,
1281 this->GetSequenceMap()[i].nativeStartOffset,
1282 this->GetSequenceMap()[i].nativeEndOffset,
1283 this->GetSequenceMap()[i].source));
1284 }
1285#endif // LOGGING
1286
1287}
1288
1289/******************************************************************************
1290 *
1291 ******************************************************************************/
1292ICorDebugInfo::SourceTypes DebuggerJitInfo::GetSrcTypeFromILOffset(SIZE_T ilOffset)
1293{
1294 CONTRACTL
1295 {
1296 SO_NOT_MAINLINE;
1297 NOTHROW;
1298 GC_NOTRIGGER;
1299 }
1300 CONTRACTL_END;
1301
1302 BOOL exact = FALSE;
1303 DebuggerILToNativeMap *pMap = MapILOffsetToMapEntry(ilOffset, &exact);
1304
1305 LOG((LF_CORDB, LL_INFO100000, "DJI::GSTFILO: for il 0x%x, got entry 0x%p,"
1306 "(il 0x%x) nat 0x%x to 0x%x, SourceTypes 0x%x, exact:%x\n", ilOffset, pMap,
1307 pMap->ilOffset, pMap->nativeStartOffset, pMap->nativeEndOffset, pMap->source,
1308 exact));
1309
1310 if (!exact)
1311 {
1312 return ICorDebugInfo::SOURCE_TYPE_INVALID;
1313 }
1314
1315 return pMap->source;
1316}
1317
1318/******************************************************************************
1319 *
1320 ******************************************************************************/
1321DebuggerMethodInfo::~DebuggerMethodInfo()
1322{
1323 CONTRACTL
1324 {
1325 SO_INTOLERANT;
1326 NOTHROW;
1327 GC_NOTRIGGER;
1328 DESTRUCTOR_CHECK;
1329 }
1330 CONTRACTL_END;
1331
1332 DeleteJitInfoList();
1333
1334 LOG((LF_CORDB,LL_EVERYTHING, "DMI::~DMI : deleted at 0x%p\n", this));
1335}
1336
1337// Translate between old & new offsets (w/ respect to Instrumented IL).
1338
1339// Don't interpolate
1340ULONG32 DebuggerMethodInfo::TranslateToInstIL(const InstrumentedILOffsetMapping * pMapping,
1341 ULONG32 offOrig,
1342 bool fOrigToInst)
1343{
1344 LIMITED_METHOD_CONTRACT;
1345
1346 SIZE_T iMap;
1347 SIZE_T cMap = pMapping->GetCount();
1348 // some negative IL offsets have special meaning. Don't translate
1349 // those (just return as is). See ICorDebugInfo::MappingTypes
1350 if ((cMap == 0) || (offOrig < 0))
1351 {
1352 return offOrig;
1353 }
1354
1355 ARRAY_PTR_COR_IL_MAP rgMap = pMapping->GetOffsets();
1356
1357 // This assumes:
1358 // - map is sorted in increasing order by both old & new
1359 // - round down.
1360 if (fOrigToInst)
1361 {
1362 // Translate: old --> new
1363
1364 // Treat it as prolog if offOrig is not in remapping range
1365 if ((offOrig < rgMap[0].oldOffset) || (offOrig == (ULONG32)ICorDebugInfo::PROLOG))
1366 {
1367 return (ULONG32)ICorDebugInfo::PROLOG;
1368 }
1369
1370 if (offOrig == (ULONG32)ICorDebugInfo::EPILOG)
1371 {
1372 return (ULONG32)ICorDebugInfo::EPILOG;
1373 }
1374
1375 if (offOrig == (ULONG32)ICorDebugInfo::NO_MAPPING)
1376 {
1377 return (ULONG32)ICorDebugInfo::NO_MAPPING;
1378 }
1379
1380 for(iMap = 1; iMap < cMap; iMap++)
1381 {
1382 if (offOrig < rgMap[iMap].oldOffset)
1383 return rgMap[iMap-1].newOffset;
1384 }
1385
1386 return rgMap[iMap - 1].newOffset;
1387 }
1388 else
1389 {
1390 // Translate: new --> old
1391
1392 // Treat it as prolog if offOrig is not in remapping range
1393 if ((offOrig < rgMap[0].newOffset) || (offOrig == (ULONG32)ICorDebugInfo::PROLOG))
1394 {
1395 return (ULONG32)ICorDebugInfo::PROLOG;
1396 }
1397
1398 if (offOrig == (ULONG32)ICorDebugInfo::EPILOG)
1399 {
1400 return (ULONG32)ICorDebugInfo::EPILOG;
1401 }
1402
1403 if (offOrig == (ULONG32)ICorDebugInfo::NO_MAPPING)
1404 {
1405 return (ULONG32)ICorDebugInfo::NO_MAPPING;
1406 }
1407
1408 for(iMap = 1; iMap < cMap; iMap++)
1409 {
1410 if (offOrig < rgMap[iMap].newOffset)
1411 return rgMap[iMap-1].oldOffset;
1412 }
1413
1414 return rgMap[iMap - 1].oldOffset;
1415 }
1416}
1417
1418/******************************************************************************
1419 * Constructor for DebuggerMethodInfo
1420 ******************************************************************************/
1421DebuggerMethodInfo::DebuggerMethodInfo(Module *module, mdMethodDef token) :
1422 m_currentEnCVersion(CorDB_DEFAULT_ENC_FUNCTION_VERSION),
1423 m_module(module),
1424 m_token(token),
1425 m_prevMethodInfo(NULL),
1426 m_nextMethodInfo(NULL),
1427 m_latestJitInfo(NULL),
1428 m_fHasInstrumentedILMap(false)
1429{
1430 CONTRACTL
1431 {
1432 SO_INTOLERANT;
1433 WRAPPER(THROWS);
1434 WRAPPER(GC_TRIGGERS);
1435 CONSTRUCTOR_CHECK;
1436 }
1437 CONTRACTL_END;
1438
1439 LOG((LF_CORDB,LL_EVERYTHING, "DMI::DMI : created at 0x%p\n", this));
1440
1441 _ASSERTE(g_pDebugger->HasDebuggerDataLock());
1442
1443 DebuggerModule * pModule = GetPrimaryModule();
1444
1445 m_fJMCStatus = false;
1446
1447 // If there's no module, then this isn't a JMC function.
1448 // This can happen since DMIs are created for debuggable code, and
1449 // Modules are only created if a debugger is actually attached.
1450 if (pModule != NULL)
1451 {
1452 // Use the accessor so that we keep the module's count properly updated.
1453 SetJMCStatus(pModule->GetRuntimeModule()->GetJMCStatus());
1454 }
1455 }
1456
1457
1458/******************************************************************************
1459 * Get the primary debugger module for this DMI. This is 1:1 w/ an EE Module.
1460 ******************************************************************************/
1461DebuggerModule* DebuggerMethodInfo::GetPrimaryModule()
1462{
1463 CONTRACTL
1464 {
1465 SO_INTOLERANT;
1466 NOTHROW;
1467 GC_NOTRIGGER;
1468 }
1469 CONTRACTL_END;
1470
1471 _ASSERTE(g_pDebugger->HasDebuggerDataLock());
1472
1473 DebuggerModuleTable * pTable = g_pDebugger->GetModuleTable();
1474
1475 // If we're tracking debug info but no debugger's attached, then
1476 // we won't have a table for the modules yet.
1477 if (pTable == NULL)
1478 return NULL;
1479
1480 DebuggerModule * pModule = pTable->GetModule(GetRuntimeModule());
1481 if (pModule == NULL)
1482 {
1483 // We may be missing the module even if we have the table.
1484 // 1.) If there's no debugger attached (so we're not getting ModuleLoad events).
1485 // 2.) If we're asking for this while in DllMain of the module we're currently loading,
1486 // we won't have gotten the ModuleLoad event yet.
1487 return NULL;
1488 }
1489
1490 // Only give back primary modules...
1491 DebuggerModule * p2 = pModule->GetPrimaryModule();
1492 _ASSERTE(p2 != NULL);
1493
1494 return p2;
1495}
1496
1497/******************************************************************************
1498 * Get the runtime module for this DMI
1499 ******************************************************************************/
1500Module * DebuggerMethodInfo::GetRuntimeModule()
1501{
1502 LIMITED_METHOD_CONTRACT;
1503
1504 return m_module;
1505}
1506
1507#endif // !DACCESS_COMPILE
1508
1509
1510//---------------------------------------------------------------------------------------
1511//
1512// Find the DebuggerJitInfo (DJI) for the given MethodDesc and native start address.
1513// We need the native start address because generic methods may have multiple instances
1514// of jitted code. This function does not create the DJI if it does not already exist.
1515//
1516// Arguments:
1517// pMD - the MD to lookup; must be non-NULL
1518// addrNativeStartAddr - the native start address of jitted code
1519//
1520// Return Value:
1521// Returns the DJI corresponding to the specified MD and native start address.
1522// NULL if the DJI is not found.
1523//
1524
1525DebuggerJitInfo * DebuggerMethodInfo::FindJitInfo(MethodDesc * pMD,
1526 TADDR addrNativeStartAddr)
1527{
1528 CONTRACTL
1529 {
1530 SO_INTOLERANT;
1531 SUPPORTS_DAC;
1532 NOTHROW;
1533 GC_NOTRIGGER;
1534 PRECONDITION(pMD != NULL);
1535 }
1536 CONTRACTL_END;
1537
1538
1539 DebuggerJitInfo * pCheck = m_latestJitInfo;
1540 while (pCheck != NULL)
1541 {
1542 if ( (pCheck->m_fd == dac_cast<PTR_MethodDesc>(pMD)) &&
1543 (pCheck->m_addrOfCode == addrNativeStartAddr) )
1544 {
1545 return pCheck;
1546 }
1547
1548 pCheck = pCheck->m_prevJitInfo;
1549 }
1550
1551 return NULL;
1552}
1553
1554
1555#if !defined(DACCESS_COMPILE)
1556
1557/*
1558 * FindOrCreateInitAndAddJitInfo
1559 *
1560 * This routine tries to find an existing DJI based on the method desc and start address, or allocates a new DJI, adding it to
1561 * the DMI.
1562 *
1563 * Parameters:
1564 * fd - the method desc to find or create a DJI for.
1565 * startAddr - the start address to find or create the DJI for.
1566 *
1567 * Returns
1568 * A pointer to the found or created DJI, or NULL.
1569 *
1570 */
1571
1572DebuggerJitInfo *DebuggerMethodInfo::FindOrCreateInitAndAddJitInfo(MethodDesc* fd, PCODE startAddr)
1573{
1574 CONTRACTL
1575 {
1576 SO_INTOLERANT;
1577 THROWS;
1578 GC_NOTRIGGER;
1579 }
1580 CONTRACTL_END;
1581
1582 _ASSERTE(fd != NULL);
1583
1584 // The debugger doesn't track Lightweight-codegen methods b/c they have no metadata.
1585 if (fd->IsDynamicMethod())
1586 {
1587 return NULL;
1588 }
1589
1590 if (startAddr == NULL)
1591 {
1592 // This will grab the start address for the current code version.
1593 startAddr = g_pEEInterface->GetFunctionAddress(fd);
1594 if (startAddr == NULL)
1595 {
1596 return NULL;
1597 }
1598 }
1599 else
1600 {
1601 _ASSERTE(g_pEEInterface->GetNativeCodeMethodDesc(startAddr) == fd);
1602 }
1603
1604 // Check the lsit to see if we've already populated an entry for this JitInfo.
1605 // If we didn't have a JitInfo before, lazily create it now.
1606 // We don't care if we were prejitted or not.
1607 //
1608 // We haven't got the lock yet so we'll repeat this lookup once
1609 // we've taken the lock.
1610 ARM_ONLY(_ASSERTE((startAddr & THUMB_CODE) == 1));
1611 DebuggerJitInfo * pResult = FindJitInfo(fd, startAddr);
1612 if (pResult != NULL)
1613 {
1614 return pResult;
1615 }
1616
1617 // The DJI may already be populated in the cache, if so CreateInitAndAddJitInfo is a no-op and that is fine.
1618 // CreateInitAndAddJitInfo takes a lock and checks the list again, which makes this thread-safe.
1619 BOOL jitInfoWasCreated;
1620 return CreateInitAndAddJitInfo(fd, startAddr, &jitInfoWasCreated);
1621}
1622
1623// Create a DJI around a method-desc. The EE already has all the information we need for a DJI,
1624// the DJI just serves as a cache of the information for the debugger.
1625// Caller makes no guarantees about whether the DJI is already in the table. (Caller should avoid this if
1626// it knows it's in the table, but b/c we can't expect caller to synchronize w/ the other threads).
1627DebuggerJitInfo *DebuggerMethodInfo::CreateInitAndAddJitInfo(MethodDesc* fd, TADDR startAddr, BOOL* jitInfoWasCreated)
1628{
1629 CONTRACTL
1630 {
1631 SO_INTOLERANT;
1632 THROWS;
1633 GC_NOTRIGGER;
1634 PRECONDITION(!g_pDebugger->HasDebuggerDataLock());
1635 }
1636 CONTRACTL_END;
1637
1638 _ASSERTE(fd != NULL);
1639
1640 // May or may-not be jitted, that's why we passed in the start addr & size explicitly.
1641 _ASSERTE(startAddr != NULL);
1642
1643 *jitInfoWasCreated = FALSE;
1644
1645 // No support for light-weight codegen methods.
1646 if (fd->IsDynamicMethod())
1647 {
1648 return NULL;
1649 }
1650
1651
1652 DebuggerJitInfo *dji = new (interopsafe) DebuggerJitInfo(this, fd);
1653 _ASSERTE(dji != NULL); // throws on oom error
1654
1655 _ASSERTE(dji->m_methodInfo == this); // this should be set
1656
1657 TRACE_ALLOC(dji);
1658
1659 // Init may take locks that violate the debugger-data lock, so we can't init while we hold that lock.
1660 // But we can't init after we add it to the table and release the lock b/c another thread may pick
1661 // if up in the uninitialized state.
1662 // So we initialize a private copy of the DJI before we take the debugger-data lock.
1663 dji->Init(startAddr);
1664
1665 dji->m_nextJitInfo = NULL;
1666
1667 //
1668 //<TODO>@TODO : _ASSERTE(EnC);</TODO>
1669 //
1670 {
1671 Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger);
1672
1673 // We need to ensure that another thread didn't go in and add this exact same DJI?
1674 {
1675 DebuggerJitInfo * pResult = FindJitInfo(dji->m_fd, (TADDR)dji->m_addrOfCode);
1676 if (pResult != NULL)
1677 {
1678 // Found!
1679 _ASSERTE(pResult->m_sizeOfCode == dji->m_sizeOfCode);
1680 DeleteInteropSafe(dji);
1681 return pResult;
1682 }
1683 else
1684 {
1685 *jitInfoWasCreated = TRUE;
1686 }
1687 }
1688
1689 // We know it's not in the table. Go add it!
1690 DebuggerJitInfo *djiPrev = m_latestJitInfo;
1691
1692 LOG((LF_CORDB,LL_INFO10000,"DMI:CAAJI: current head of dji list:0x%08x\n", djiPrev));
1693
1694 if (djiPrev != NULL)
1695 {
1696 dji->m_prevJitInfo = djiPrev;
1697 djiPrev->m_nextJitInfo = dji;
1698
1699 m_latestJitInfo = dji;
1700
1701 LOG((LF_CORDB,LL_INFO10000,"DMI:CAAJI: DJI version 0x%04x for %s\n",
1702 GetCurrentEnCVersion(),
1703 dji->m_fd->m_pszDebugMethodName));
1704 }
1705 else
1706 {
1707 m_latestJitInfo = dji;
1708 }
1709
1710 } // DebuggerDataLockHolder out of scope - release implied
1711
1712 // We've now added a new DJI into the table and released the lock. Thus any other thread
1713 // can come and use our DJI. Good thing we inited the DJI _before_ adding it to the table.
1714
1715 LOG((LF_CORDB,LL_INFO10000,"DMI:CAAJI: new head of dji list:0x%08x\n", m_latestJitInfo));
1716
1717 return dji;
1718}
1719
1720/*
1721 * DeleteJitInfo
1722 *
1723 * This routine remove a DJI from the DMI's list and deletes the memory.
1724 *
1725 * Parameters:
1726 * dji - The DJI to delete.
1727 *
1728 * Returns
1729 * None.
1730 *
1731 */
1732
1733void DebuggerMethodInfo::DeleteJitInfo(DebuggerJitInfo *dji)
1734{
1735 CONTRACTL
1736 {
1737 SO_INTOLERANT;
1738 NOTHROW;
1739 GC_NOTRIGGER;
1740 }
1741 CONTRACTL_END;
1742
1743 Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger);
1744
1745 LOG((LF_CORDB,LL_INFO10000,"DMI:DJI: dji:0x%08x\n", dji));
1746
1747 DebuggerJitInfo *djiPrev = dji->m_prevJitInfo;
1748
1749 if (djiPrev != NULL)
1750 {
1751 djiPrev->m_nextJitInfo = dji->m_nextJitInfo;
1752 }
1753
1754 if (dji->m_nextJitInfo != NULL)
1755 {
1756 dji->m_nextJitInfo->m_prevJitInfo = djiPrev;
1757 }
1758 else
1759 {
1760 //
1761 // This DJI is the head of the list
1762 //
1763 _ASSERTE(m_latestJitInfo == dji);
1764
1765 m_latestJitInfo = djiPrev;
1766 }
1767
1768 TRACE_FREE(dji);
1769
1770 DeleteInteropSafe(dji);
1771
1772 // DebuggerDataLockHolder out of scope - release implied
1773}
1774
1775/*
1776 * DeleteJitInfoList
1777 *
1778 * This routine removes all the DJIs from the current DMI.
1779 *
1780 * Parameters:
1781 * None.
1782 *
1783 * Returns
1784 * None.
1785 *
1786 */
1787
1788void DebuggerMethodInfo::DeleteJitInfoList(void)
1789{
1790 CONTRACTL
1791 {
1792 SO_INTOLERANT;
1793 NOTHROW;
1794 GC_NOTRIGGER;
1795 }
1796 CONTRACTL_END;
1797
1798 Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger);
1799
1800 while(m_latestJitInfo != NULL)
1801 {
1802 DeleteJitInfo(m_latestJitInfo);
1803 }
1804
1805 // DebuggerDataLockHolder out of scope - release implied
1806}
1807
1808
1809// Iterate through all existing DJIs. See header for expected usage.
1810DebuggerMethodInfo::DJIIterator::DJIIterator()
1811{
1812 LIMITED_METHOD_CONTRACT;
1813
1814 m_pCurrent = NULL;
1815 m_pLoaderModuleFilter = NULL;
1816}
1817
1818bool DebuggerMethodInfo::DJIIterator::IsAtEnd()
1819{
1820 LIMITED_METHOD_CONTRACT;
1821
1822 return m_pCurrent == NULL;
1823}
1824
1825DebuggerJitInfo * DebuggerMethodInfo::DJIIterator::Current()
1826{
1827 LIMITED_METHOD_CONTRACT;
1828
1829 return m_pCurrent;
1830}
1831
1832void DebuggerMethodInfo::DJIIterator::Next(BOOL fFirst /*=FALSE*/)
1833{
1834 CONTRACTL
1835 {
1836 NOTHROW;
1837 GC_NOTRIGGER;
1838 FORBID_FAULT;
1839 MODE_ANY;
1840 CANNOT_TAKE_LOCK;
1841 }
1842 CONTRACTL_END;
1843
1844 if (!fFirst)
1845 {
1846 PREFIX_ASSUME(m_pCurrent != NULL); // IsAtEnd() should have caught this.
1847 m_pCurrent = m_pCurrent->m_prevJitInfo;
1848 }
1849
1850 // Check if we're at the end of the list, in which case we're done.
1851 for ( ; m_pCurrent != NULL; m_pCurrent = m_pCurrent->m_prevJitInfo)
1852 {
1853 Module * pLoaderModule = m_pCurrent->m_pLoaderModule;
1854
1855 // Obey the module filter if it's provided
1856 if ((m_pLoaderModuleFilter != NULL) && (m_pLoaderModuleFilter != pLoaderModule))
1857 continue;
1858
1859 //Obey the methodDesc filter if it is provided
1860 if ((m_pMethodDescFilter != NULL) && (m_pMethodDescFilter != m_pCurrent->m_fd))
1861 continue;
1862
1863 // Skip modules that are unloaded, but still hanging around. Note that we can't use DebuggerModule for this check
1864 // because of it is deleted pretty early during unloading, and we do not want to recreate it.
1865 if (pLoaderModule->GetLoaderAllocator()->IsUnloaded())
1866 continue;
1867
1868 break;
1869 }
1870}
1871
1872
1873/******************************************************************************
1874 * Return true iff this method is jitted
1875 ******************************************************************************/
1876bool DebuggerMethodInfo::HasJitInfos()
1877{
1878 LIMITED_METHOD_CONTRACT;
1879 _ASSERTE(g_pDebugger->HasDebuggerDataLock());
1880 return (m_latestJitInfo != NULL);
1881}
1882
1883/******************************************************************************
1884 * Return true iff this has been EnCed since last time function was jitted.
1885 ******************************************************************************/
1886bool DebuggerMethodInfo::HasMoreRecentEnCVersion()
1887{
1888 LIMITED_METHOD_CONTRACT;
1889 return ((m_latestJitInfo != NULL) &&
1890 (m_currentEnCVersion > m_latestJitInfo->m_encVersion));
1891}
1892
1893/******************************************************************************
1894 * Updated the instrumented-IL map
1895 ******************************************************************************/
1896void DebuggerMethodInfo::SetInstrumentedILMap(COR_IL_MAP * pMap, SIZE_T cEntries)
1897{
1898 InstrumentedILOffsetMapping mapping;
1899 mapping.SetMappingInfo(cEntries, pMap);
1900
1901 GetRuntimeModule()->SetInstrumentedILOffsetMapping(m_token, mapping);
1902
1903 m_fHasInstrumentedILMap = true;
1904}
1905
1906/******************************************************************************
1907 * Get the JMC status for a given function.
1908 ******************************************************************************/
1909bool DebuggerMethodInfo::IsJMCFunction()
1910{
1911 LIMITED_METHOD_CONTRACT;
1912 return m_fJMCStatus;
1913}
1914
1915/******************************************************************************
1916 * Set the JMC status to a given value
1917 ******************************************************************************/
1918void DebuggerMethodInfo::SetJMCStatus(bool fStatus)
1919{
1920 CONTRACTL
1921 {
1922 SO_NOT_MAINLINE;
1923 NOTHROW;
1924 GC_NOTRIGGER;
1925 }
1926 CONTRACTL_END;
1927
1928 _ASSERTE(g_pDebugger->HasDebuggerDataLock());
1929
1930 // First check if this is a no-op.
1931 // Do this first b/c there may be some cases where we don't have a DebuggerModule
1932 // yet but are still calling SetJMCStatus(false), like if we detach before attach is complete.
1933 bool fOldStatus = IsJMCFunction();
1934
1935 if (fOldStatus == fStatus)
1936 {
1937 // if no change, then there's nothing to do.
1938 LOG((LF_CORDB,LL_EVERYTHING, "DMI::SetJMCStatus: %p, keeping old status, %d\n", this, fStatus));
1939 return;
1940 }
1941
1942 // For a perf-optimization, our Module needs to know if it has any user
1943 // code. If it doesn't, it shouldn't dispatch through the JMC probes.
1944 // So modules keep a count of # of JMC functions - if the count is 0, the
1945 // module can set is JMC probe flag to 0 and skip the JMC probes.
1946 Module * pRuntimeModule = this->GetRuntimeModule();
1947
1948 // Update the module's count.
1949 if (!fStatus)
1950 {
1951 LOG((LF_CORDB,LL_EVERYTHING, "DMI::SetJMCStatus: %p, changing to non-user code\n", this));
1952 _ASSERTE(pRuntimeModule->HasAnyJMCFunctions());
1953 pRuntimeModule->DecJMCFuncCount();
1954 }
1955 else
1956 {
1957 LOG((LF_CORDB,LL_EVERYTHING, "DMI::SetJMCStatus: %p, changing to user code\n", this));
1958 pRuntimeModule->IncJMCFuncCount();
1959 _ASSERTE(pRuntimeModule->HasAnyJMCFunctions());
1960 }
1961
1962 m_fJMCStatus = fStatus;
1963
1964 // We should update our module's JMC status...
1965 g_pDebugger->UpdateModuleJMCFlag(pRuntimeModule, DebuggerController::GetTotalMethodEnter() != 0);
1966
1967}
1968
1969// Get an iterator that will go through ALL native code-blobs (DJI) in the specified
1970// AppDomain, optionally filtered by loader module (if pLoaderModuleFilter != NULL).
1971// This is EnC/ Generics / Prejit aware.
1972void DebuggerMethodInfo::IterateAllDJIs(AppDomain * pAppDomain, Module * pLoaderModuleFilter, MethodDesc * pMethodDescFilter, DebuggerMethodInfo::DJIIterator * pEnum)
1973{
1974 CONTRACTL
1975 {
1976 SO_NOT_MAINLINE;
1977 THROWS;
1978 GC_NOTRIGGER;
1979 }
1980 CONTRACTL_END;
1981
1982 _ASSERTE(pEnum != NULL);
1983 _ASSERTE(pAppDomain != NULL || pMethodDescFilter != NULL);
1984
1985 // Esnure we have DJIs for everything.
1986 CreateDJIsForNativeBlobs(pAppDomain, pLoaderModuleFilter, pMethodDescFilter);
1987
1988 pEnum->m_pCurrent = m_latestJitInfo;
1989 pEnum->m_pLoaderModuleFilter = pLoaderModuleFilter;
1990 pEnum->m_pMethodDescFilter = pMethodDescFilter;
1991
1992 // Advance to the first DJI that passes the filter
1993 pEnum->Next(TRUE);
1994}
1995
1996//---------------------------------------------------------------------------------------
1997//
1998// Bring the DJI cache up to date.
1999//
2000// Arguments:
2001// * pAppDomain - Create DJIs only for this AppDomain
2002// * pLoaderModuleFilter - If non-NULL, create DJIs only for MethodDescs whose
2003// loader module matches this one. (This can be different from m_module in the
2004// case of generics defined in one module and instantiated in another). If
2005// non-NULL, create DJIs for all modules in pAppDomain.
2006// * pMethodDescFilter - If non-NULL, create DJIs only for this single MethodDesc.
2007//
2008
2009void DebuggerMethodInfo::CreateDJIsForNativeBlobs(AppDomain * pAppDomain, Module * pLoaderModuleFilter, MethodDesc* pMethodDescFilter)
2010{
2011 CONTRACTL
2012 {
2013 SO_NOT_MAINLINE;
2014 THROWS;
2015 GC_NOTRIGGER;
2016 }
2017 CONTRACTL_END;
2018
2019 // If we're not stopped and the module we're iterating over allows types to load,
2020 // then it's possible new native blobs are being created underneath us.
2021 _ASSERTE(g_pDebugger->IsStopped() ||
2022 ((pLoaderModuleFilter != NULL) && !pLoaderModuleFilter->IsReadyForTypeLoad()) ||
2023 pMethodDescFilter != NULL);
2024
2025 if (pMethodDescFilter != NULL)
2026 {
2027 CreateDJIsForMethodDesc(pMethodDescFilter);
2028 }
2029 else
2030 {
2031 // @todo - we really only need to do this if the stop-counter goes up (else we know nothing new is added).
2032 // B/c of generics, it's possible that new instantiations of a method may have been jitted.
2033 // So just loop through all known instantiations and ensure that we have all the DJIs.
2034 // Note that this iterator won't show previous EnC versions, but we're already guaranteed to
2035 // have DJIs for every verision of a method that was EnCed.
2036 // This also handles the possibility of getting the same methoddesc back from the iterator.
2037 // It also lets EnC + generics play nice together (including if an generic method was EnC-ed)
2038 LoadedMethodDescIterator it(pAppDomain, m_module, m_token);
2039 CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
2040 while (it.Next(pDomainAssembly.This()))
2041 {
2042 MethodDesc * pDesc = it.Current();
2043 if (!pDesc->HasNativeCode())
2044 {
2045 continue;
2046 }
2047
2048 Module * pLoaderModule = pDesc->GetLoaderModule();
2049
2050 // Obey the module filter if it's provided
2051 if ((pLoaderModuleFilter != NULL) && (pLoaderModuleFilter != pLoaderModule))
2052 continue;
2053
2054 // Skip modules that are unloaded, but still hanging around. Note that we can't use DebuggerModule for this check
2055 // because of it is deleted pretty early during unloading, and we do not want to recreate it.
2056 if (pLoaderModule->GetLoaderAllocator()->IsUnloaded())
2057 continue;
2058
2059 CreateDJIsForMethodDesc(pDesc);
2060 }
2061 }
2062}
2063
2064
2065//---------------------------------------------------------------------------------------
2066//
2067// Bring the DJI cache up to date for jitted code instances of a particular MethodDesc.
2068//
2069//
2070void DebuggerMethodInfo::CreateDJIsForMethodDesc(MethodDesc * pMethodDesc)
2071{
2072 CONTRACTL
2073 {
2074 SO_NOT_MAINLINE;
2075 THROWS;
2076 GC_NOTRIGGER;
2077 }
2078 CONTRACTL_END;
2079
2080
2081 // The debugger doesn't track Lightweight-codegen methods b/c they have no metadata.
2082 if (pMethodDesc->IsDynamicMethod())
2083 {
2084 return;
2085 }
2086
2087#ifdef FEATURE_CODE_VERSIONING
2088 CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager();
2089 // grab the code version lock to iterate available versions of the code
2090 {
2091 CodeVersionManager::TableLockHolder lock(pCodeVersionManager);
2092 NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(pMethodDesc);
2093
2094 for (NativeCodeVersionIterator itr = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); itr != end; itr++)
2095 {
2096 // Some versions may not be compiled yet - skip those for now
2097 // if they compile later the JitCompiled callback will add a DJI to our cache at that time
2098 PCODE codeAddr = itr->GetNativeCode();
2099 if (codeAddr)
2100 {
2101 // The DJI may already be populated in the cache, if so CreateInitAndAdd is
2102 // a no-op and that is fine.
2103 BOOL unusedDjiWasCreated;
2104 CreateInitAndAddJitInfo(pMethodDesc, codeAddr, &unusedDjiWasCreated);
2105 }
2106 }
2107 }
2108#else
2109 // We just ask for the DJI to ensure that it's lazily created.
2110 // This should only fail in an oom scenario.
2111 DebuggerJitInfo * djiTest = g_pDebugger->GetLatestJitInfoFromMethodDesc(pDesc);
2112 if (djiTest == NULL)
2113 {
2114 // We're oom. Give up.
2115 ThrowOutOfMemory();
2116 return;
2117 }
2118#endif
2119}
2120
2121/*
2122 * GetLatestJitInfo
2123 *
2124 * This routine returns the lastest DJI we have for a particular DMI.
2125 * DJIs are lazily created.
2126 * Parameters:
2127 * None.
2128 *
2129 * Returns
2130 * a possibly NULL pointer to a DJI.
2131 *
2132 */
2133
2134// For logging and other internal purposes, provide a non-initializing accessor.
2135DebuggerJitInfo* DebuggerMethodInfo::GetLatestJitInfo_NoCreate()
2136{
2137 return m_latestJitInfo;
2138}
2139
2140
2141DebuggerMethodInfoTable::DebuggerMethodInfoTable() : CHashTableAndData<CNewZeroData>(101)
2142{
2143 CONTRACTL
2144 {
2145 WRAPPER(THROWS);
2146 GC_NOTRIGGER;
2147
2148 CONSTRUCTOR_CHECK;
2149 }
2150 CONTRACTL_END;
2151
2152 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
2153 HRESULT hr = NewInit(101, sizeof(DebuggerMethodInfoEntry), 101);
2154
2155 if (FAILED(hr))
2156 {
2157 ThrowWin32(hr);
2158 }
2159}
2160
2161HRESULT DebuggerMethodInfoTable::AddMethodInfo(Module *pModule,
2162 mdMethodDef token,
2163 DebuggerMethodInfo *mi)
2164{
2165 CONTRACTL
2166 {
2167 THROWS;
2168 GC_NOTRIGGER;
2169
2170 INSTANCE_CHECK;
2171 PRECONDITION(CheckPointer(pModule));
2172 PRECONDITION(CheckPointer(mi));
2173 }
2174 CONTRACTL_END;
2175
2176 LOG((LF_CORDB, LL_INFO1000, "DMIT::AMI Adding dmi:0x%x Mod:0x%x tok:"
2177 "0x%x nVer:0x%x\n", mi, pModule, token, mi->GetCurrentEnCVersion()));
2178
2179 _ASSERTE(mi != NULL);
2180
2181 _ASSERTE(g_pDebugger->HasDebuggerDataLock());
2182
2183 HRESULT hr = OverwriteMethodInfo(pModule, token, mi, TRUE);
2184 if (hr == S_OK)
2185 return hr;
2186
2187 DebuggerMethodInfoKey dmik;
2188 dmik.pModule = pModule;
2189 dmik.token = token;
2190
2191 DebuggerMethodInfoEntry *dmie =
2192 (DebuggerMethodInfoEntry *) Add(HASH(&dmik));
2193
2194 if (dmie != NULL)
2195 {
2196 dmie->key.pModule = pModule;
2197 dmie->key.token = token;
2198 dmie->mi = mi;
2199
2200 LOG((LF_CORDB, LL_INFO1000, "DMIT::AJI: mod:0x%x tok:0%x ",
2201 pModule, token));
2202 return S_OK;
2203 }
2204
2205 ThrowOutOfMemory();
2206 return S_OK;
2207}
2208
2209HRESULT DebuggerMethodInfoTable::OverwriteMethodInfo(Module *pModule,
2210 mdMethodDef token,
2211 DebuggerMethodInfo *mi,
2212 BOOL fOnlyIfNull)
2213{
2214 CONTRACTL
2215 {
2216 NOTHROW;
2217 GC_NOTRIGGER;
2218
2219 PRECONDITION(CheckPointer(pModule));
2220 PRECONDITION(CheckPointer(mi));
2221 }
2222 CONTRACTL_END;
2223
2224 LOG((LF_CORDB, LL_INFO1000, "DMIT::OJI: dmi:0x%x mod:0x%x tok:0x%x\n", mi,
2225 pModule, token));
2226
2227 _ASSERTE(g_pDebugger->HasDebuggerDataLock());
2228
2229 DebuggerMethodInfoKey dmik;
2230 dmik.pModule = pModule;
2231 dmik.token = token;
2232
2233 DebuggerMethodInfoEntry *entry
2234 = (DebuggerMethodInfoEntry *) Find(HASH(&dmik), KEY(&dmik));
2235 if (entry != NULL)
2236 {
2237 if ( (fOnlyIfNull &&
2238 entry->mi == NULL) ||
2239 !fOnlyIfNull)
2240 {
2241 entry->mi = mi;
2242
2243 LOG((LF_CORDB, LL_INFO1000, "DMIT::OJI: mod:0x%x tok:0x%x remap"
2244 "nVer:0x%x\n", pModule, token, entry->nVersionLastRemapped));
2245 return S_OK;
2246 }
2247 }
2248
2249 return E_FAIL;
2250}
2251
2252// pModule is being destroyed - remove any entries that belong to it. Why?
2253// (a) Correctness: the module can be reloaded at the same address,
2254// which will cause accidental matches with our hashtable (indexed by
2255// {Module*,mdMethodDef}
2256// (b) Perf: don't waste the memory!
2257void DebuggerMethodInfoTable::ClearMethodsOfModule(Module *pModule)
2258{
2259 WRAPPER_NO_CONTRACT;
2260
2261 _ASSERTE(g_pDebugger->HasDebuggerDataLock());
2262
2263 LOG((LF_CORDB, LL_INFO1000000, "CMOM:mod:0x%x (%S)\n", pModule
2264 ,pModule->GetDebugName()));
2265
2266 HASHFIND info;
2267
2268 DebuggerMethodInfoEntry *entry
2269 = (DebuggerMethodInfoEntry *) FindFirstEntry(&info);
2270 while(entry != NULL)
2271 {
2272 Module *pMod = entry->key.pModule ;
2273 if (pMod == pModule)
2274 {
2275 // This method actually got mitted, at least
2276 // once - remove all version info.
2277 while(entry->mi != NULL)
2278 {
2279 DeleteEntryDMI(entry);
2280 }
2281
2282 Delete(HASH(&(entry->key)), (HASHENTRY*)entry);
2283 }
2284 else
2285 {
2286 //
2287 // Delete generic DJIs that have lifetime attached to this module
2288 //
2289 DebuggerMethodInfo * dmi = entry->mi;
2290 while (dmi != NULL)
2291 {
2292 DebuggerJitInfo * dji = dmi->GetLatestJitInfo_NoCreate();
2293 while (dji != NULL)
2294 {
2295 DebuggerJitInfo * djiPrev = dji->m_prevJitInfo;;
2296
2297 if (dji->m_pLoaderModule == pModule)
2298 dmi->DeleteJitInfo(dji);
2299
2300 dji = djiPrev;
2301 }
2302
2303 dmi = dmi->m_prevMethodInfo;
2304 }
2305 }
2306
2307 entry = (DebuggerMethodInfoEntry *) FindNextEntry(&info);
2308 }
2309}
2310
2311void DebuggerMethodInfoTable::DeleteEntryDMI(DebuggerMethodInfoEntry *entry)
2312{
2313 CONTRACTL
2314 {
2315 NOTHROW;
2316 GC_NOTRIGGER;
2317 MODE_ANY;
2318 CAN_TAKE_LOCK; // DeleteInteropSafe() eventually calls DebuggerMethodInfo::DeleteJitInfoList
2319 // which locks.
2320 }
2321 CONTRACTL_END;
2322
2323 DebuggerMethodInfo *dmiPrev = entry->mi->m_prevMethodInfo;
2324 TRACE_FREE(entry->mi);
2325 DeleteInteropSafe(entry->mi);
2326 entry->mi = dmiPrev;
2327 if ( dmiPrev != NULL )
2328 dmiPrev->m_nextMethodInfo = NULL;
2329}
2330
2331#endif // #ifndef DACCESS_COMPILE
2332
2333DebuggerJitInfo *DebuggerJitInfo::GetJitInfoByAddress(const BYTE *pbAddr )
2334{
2335 CONTRACTL
2336 {
2337 SO_INTOLERANT;
2338 NOTHROW;
2339 GC_NOTRIGGER;
2340 }
2341 CONTRACTL_END;
2342
2343 DebuggerJitInfo *dji = this;
2344
2345#ifdef LOGGING
2346 LOG((LF_CORDB,LL_INFO10000,"DJI:GJIBA finding DJI "
2347 "corresponding to addr 0x%p, starting with 0x%p\n", pbAddr, dji));
2348#endif //LOGGING
2349
2350 // If it's not NULL, but not in the range m_addrOfCode to end of function,
2351 // then get the previous one.
2352 while( dji != NULL &&
2353 !CodeRegionInfo::GetCodeRegionInfo(dji).IsMethodAddress(pbAddr))
2354 {
2355 LOG((LF_CORDB,LL_INFO10000,"DJI:GJIBA: pbAddr 0x%p is not in code "
2356 "0x%p (size:0x%p)\n", pbAddr, dji->m_addrOfCode,
2357 dji->m_sizeOfCode));
2358 dji = dji->m_prevJitInfo;
2359 }
2360
2361#ifdef LOGGING
2362 if (dji == NULL)
2363 {
2364 LOG((LF_CORDB,LL_INFO10000,"DJI:GJIBA couldn't find a DJI "
2365 "corresponding to addr 0x%p\n", pbAddr));
2366 }
2367#endif //LOGGING
2368 return dji;
2369}
2370
2371PTR_DebuggerJitInfo DebuggerMethodInfo::GetLatestJitInfo(MethodDesc *mdesc)
2372{
2373 // dac checks ngen'ed image content first, so
2374 // only check for existing JIT info.
2375#ifndef DACCESS_COMPILE
2376
2377 CONTRACTL
2378 {
2379 SO_INTOLERANT;
2380 THROWS;
2381 CALLED_IN_DEBUGGERDATALOCK_HOLDER_SCOPE_MAY_GC_TRIGGERS_CONTRACT;
2382 PRECONDITION(!g_pDebugger->HasDebuggerDataLock());
2383 }
2384 CONTRACTL_END;
2385
2386
2387 if (m_latestJitInfo && m_latestJitInfo->m_fd == mdesc && !m_latestJitInfo->m_fd->HasClassOrMethodInstantiation())
2388 return m_latestJitInfo;
2389
2390 // This ensures that there is an entry in the DJI list for this particular MethodDesc.
2391 // in the case of generic code it may not be the first entry in the list.
2392 FindOrCreateInitAndAddJitInfo(mdesc, NULL /* startAddr */);
2393
2394#endif // #ifndef DACCESS_COMPILE
2395
2396 return m_latestJitInfo;
2397}
2398
2399DebuggerMethodInfo *DebuggerMethodInfoTable::GetMethodInfo(Module *pModule, mdMethodDef token)
2400{
2401 WRAPPER_NO_CONTRACT;
2402 SUPPORTS_DAC;
2403
2404 // CHECK_DMI_TABLE;
2405
2406 // @review. One of the BVTs causes this to be called before the table is initialized
2407 // In particular, the changes to BREAKPOINT_ADD mean that this table is now consulted
2408 // to determine if we have ever seen the method, rather than a call to LookupMethodDesc,
2409 // which would have just returned NULL. In general it seems OK to consult this table
2410 // when it is empty, so I've added this....
2411 if (this == NULL)
2412 return NULL;
2413
2414 DebuggerMethodInfoKey dmik;
2415 dmik.pModule = dac_cast<PTR_Module>(pModule);
2416 dmik.token = token;
2417
2418 DebuggerMethodInfoEntry *entry = dac_cast<PTR_DebuggerMethodInfoEntry>(Find(HASH(&dmik), KEY(&dmik)));
2419
2420 if (entry == NULL )
2421 {
2422 return NULL;
2423 }
2424 else
2425 {
2426 LOG((LF_CORDB, LL_INFO1000, "DMI::GMI: for methodDef 0x%x, got 0x%x prev:0x%x\n",
2427 token, entry->mi, (entry->mi?entry->mi->m_prevMethodInfo:0)));
2428 return entry->mi;
2429 }
2430}
2431
2432
2433DebuggerMethodInfo *DebuggerMethodInfoTable::GetFirstMethodInfo(HASHFIND *info)
2434{
2435 CONTRACT(DebuggerMethodInfo*)
2436 {
2437 NOTHROW;
2438 GC_NOTRIGGER;
2439
2440 PRECONDITION(CheckPointer(info));
2441 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
2442 }
2443 CONTRACT_END;
2444
2445 _ASSERTE(g_pDebugger->HasDebuggerDataLock());
2446
2447 DebuggerMethodInfoEntry *entry = PTR_DebuggerMethodInfoEntry
2448 (PTR_HOST_TO_TADDR(FindFirstEntry(info)));
2449 if (entry == NULL)
2450 RETURN NULL;
2451 else
2452 RETURN entry->mi;
2453}
2454
2455DebuggerMethodInfo *DebuggerMethodInfoTable::GetNextMethodInfo(HASHFIND *info)
2456{
2457 CONTRACT(DebuggerMethodInfo*)
2458 {
2459 NOTHROW;
2460 GC_NOTRIGGER;
2461
2462 PRECONDITION(CheckPointer(info));
2463 POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
2464 }
2465 CONTRACT_END;
2466
2467 _ASSERTE(g_pDebugger->HasDebuggerDataLock());
2468
2469 DebuggerMethodInfoEntry *entry = PTR_DebuggerMethodInfoEntry
2470 (PTR_HOST_TO_TADDR(FindNextEntry(info)));
2471
2472 // We may have incremented the version number
2473 // for methods that never got JITted, so we should
2474 // pretend like they don't exist here.
2475 while (entry != NULL &&
2476 entry->mi == NULL)
2477 {
2478 entry = PTR_DebuggerMethodInfoEntry
2479 (PTR_HOST_TO_TADDR(FindNextEntry(info)));
2480 }
2481
2482 if (entry == NULL)
2483 RETURN NULL;
2484 else
2485 RETURN entry->mi;
2486}
2487
2488
2489
2490#ifdef DACCESS_COMPILE
2491void
2492DebuggerMethodInfoEntry::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2493{
2494 SUPPORTS_DAC;
2495
2496 // This structure is in an array in the hash
2497 // so the 'this' is implicitly enumerated by the
2498 // array enum in CHashTable.
2499
2500 // For a MiniDumpNormal, what is needed for modules is already enumerated elsewhere.
2501 // Don't waste time doing it here an extra time. Also, this will add many MB extra into the dump.
2502 if ((key.pModule.IsValid()) &&
2503 CLRDATA_ENUM_MEM_MINI != flags
2504 && CLRDATA_ENUM_MEM_TRIAGE != flags)
2505 {
2506 key.pModule->EnumMemoryRegions(flags, true);
2507 }
2508
2509 while (mi.IsValid())
2510 {
2511 mi->EnumMemoryRegions(flags);
2512 mi = mi->m_prevMethodInfo;
2513 }
2514}
2515
2516void
2517DebuggerMethodInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2518{
2519 DAC_ENUM_DTHIS();
2520 SUPPORTS_DAC;
2521
2522 if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
2523 {
2524 // Modules are enumerated already for minidumps, save the empty calls.
2525 if (m_module.IsValid())
2526 {
2527 m_module->EnumMemoryRegions(flags, true);
2528 }
2529
2530 }
2531
2532 PTR_DebuggerJitInfo jitInfo = m_latestJitInfo;
2533 while (jitInfo.IsValid())
2534 {
2535 jitInfo->EnumMemoryRegions(flags);
2536 jitInfo = jitInfo->m_prevJitInfo;
2537 }
2538}
2539
2540void
2541DebuggerJitInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2542{
2543 DAC_ENUM_DTHIS();
2544 SUPPORTS_DAC;
2545
2546 if (m_methodInfo.IsValid())
2547 {
2548 m_methodInfo->EnumMemoryRegions(flags);
2549 }
2550
2551 if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
2552 {
2553 if (m_fd.IsValid())
2554 {
2555 m_fd->EnumMemoryRegions(flags);
2556 }
2557
2558 DacEnumMemoryRegion(PTR_TO_TADDR(GetSequenceMap()),
2559 GetSequenceMapCount() * sizeof(DebuggerILToNativeMap));
2560 DacEnumMemoryRegion(PTR_TO_TADDR(GetVarNativeInfo()),
2561 GetVarNativeInfoCount() *
2562 sizeof(ICorDebugInfo::NativeVarInfo));
2563 }
2564}
2565
2566
2567void DebuggerMethodInfoTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
2568{
2569 WRAPPER_NO_CONTRACT;
2570
2571 DAC_ENUM_VTHIS();
2572 CHashTableAndData<CNewZeroData>::EnumMemoryRegions(flags);
2573
2574 for (ULONG i = 0; i < m_iEntries; i++)
2575 {
2576 DebuggerMethodInfoEntry* entry =
2577 PTR_DebuggerMethodInfoEntry(PTR_HOST_TO_TADDR(EntryPtr(i)));
2578 entry->EnumMemoryRegions(flags);
2579 }
2580}
2581#endif // #ifdef DACCESS_COMPILE
2582