summaryrefslogtreecommitdiff
path: root/src/debug/ee/functioninfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/ee/functioninfo.cpp')
-rw-r--r--src/debug/ee/functioninfo.cpp2472
1 files changed, 2472 insertions, 0 deletions
diff --git a/src/debug/ee/functioninfo.cpp b/src/debug/ee/functioninfo.cpp
new file mode 100644
index 0000000000..83c185cfc9
--- /dev/null
+++ b/src/debug/ee/functioninfo.cpp
@@ -0,0 +1,2472 @@
+// 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: DebuggerModule.cpp
+//
+// Stuff for tracking DebuggerModules.
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "../inc/common.h"
+#include "perflog.h"
+#include "eeconfig.h" // This is here even for retail & free builds...
+#include "vars.hpp"
+#include <limits.h>
+#include "ilformatter.h"
+#include "debuginfostore.h"
+#include "../../vm/methoditer.h"
+
+#ifndef DACCESS_COMPILE
+
+bool DbgIsSpecialILOffset(DWORD offset)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (offset == (ULONG) ICorDebugInfo::PROLOG ||
+ offset == (ULONG) ICorDebugInfo::EPILOG ||
+ offset == (ULONG) ICorDebugInfo::NO_MAPPING);
+}
+
+// Helper to use w/ the debug stores.
+BYTE* InteropSafeNew(void * , size_t cBytes)
+{
+ BYTE * p = new (interopsafe, nothrow) BYTE[cBytes];
+ return p;
+}
+
+
+//
+// This is only fur internal debugging.
+//
+#ifdef LOGGING
+static void _dumpVarNativeInfo(ICorDebugInfo::NativeVarInfo* vni)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LOG((LF_CORDB, LL_INFO1000000, "Var %02d: 0x%04x-0x%04x vlt=",
+ vni->varNumber,
+ vni->startOffset, vni->endOffset,
+ vni->loc.vlType));
+
+ switch (vni->loc.vlType)
+ {
+ case ICorDebugInfo::VLT_REG:
+ LOG((LF_CORDB, LL_INFO1000000, "REG reg=%d\n", vni->loc.vlReg.vlrReg));
+ break;
+
+ case ICorDebugInfo::VLT_REG_BYREF:
+ LOG((LF_CORDB, LL_INFO1000000, "REG_BYREF reg=%d\n", vni->loc.vlReg.vlrReg));
+ break;
+
+ case ICorDebugInfo::VLT_STK:
+ LOG((LF_CORDB, LL_INFO1000000, "STK reg=%d off=0x%04x (%d)\n",
+ vni->loc.vlStk.vlsBaseReg,
+ vni->loc.vlStk.vlsOffset,
+ vni->loc.vlStk.vlsOffset));
+ break;
+
+ case ICorDebugInfo::VLT_STK_BYREF:
+ LOG((LF_CORDB, LL_INFO1000000, "STK_BYREF reg=%d off=0x%04x (%d)\n",
+ vni->loc.vlStk.vlsBaseReg,
+ vni->loc.vlStk.vlsOffset,
+ vni->loc.vlStk.vlsOffset));
+ break;
+
+ case ICorDebugInfo::VLT_REG_REG:
+ LOG((LF_CORDB, LL_INFO1000000, "REG_REG reg1=%d reg2=%d\n",
+ vni->loc.vlRegReg.vlrrReg1,
+ vni->loc.vlRegReg.vlrrReg2));
+ break;
+
+ case ICorDebugInfo::VLT_REG_STK:
+ LOG((LF_CORDB, LL_INFO1000000, "REG_STK reg=%d basereg=%d off=0x%04x (%d)\n",
+ vni->loc.vlRegStk.vlrsReg,
+ vni->loc.vlRegStk.vlrsStk.vlrssBaseReg,
+ vni->loc.vlRegStk.vlrsStk.vlrssOffset,
+ vni->loc.vlRegStk.vlrsStk.vlrssOffset));
+ break;
+
+ case ICorDebugInfo::VLT_STK_REG:
+ LOG((LF_CORDB, LL_INFO1000000, "STK_REG basereg=%d off=0x%04x (%d) reg=%d\n",
+ vni->loc.vlStkReg.vlsrStk.vlsrsBaseReg,
+ vni->loc.vlStkReg.vlsrStk.vlsrsOffset,
+ vni->loc.vlStkReg.vlsrStk.vlsrsOffset,
+ vni->loc.vlStkReg.vlsrReg));
+ break;
+
+ case ICorDebugInfo::VLT_STK2:
+ LOG((LF_CORDB, LL_INFO1000000, "STK_STK reg=%d off=0x%04x (%d)\n",
+ vni->loc.vlStk2.vls2BaseReg,
+ vni->loc.vlStk2.vls2Offset,
+ vni->loc.vlStk2.vls2Offset));
+ break;
+
+ case ICorDebugInfo::VLT_FPSTK:
+ LOG((LF_CORDB, LL_INFO1000000, "FPSTK reg=%d\n",
+ vni->loc.vlFPstk.vlfReg));
+ break;
+
+ case ICorDebugInfo::VLT_FIXED_VA:
+ LOG((LF_CORDB, LL_INFO1000000, "FIXED_VA offset=%d (%d)\n",
+ vni->loc.vlFixedVarArg.vlfvOffset,
+ vni->loc.vlFixedVarArg.vlfvOffset));
+ break;
+
+
+ default:
+ LOG((LF_CORDB, LL_INFO1000000, "???\n"));
+ break;
+ }
+}
+#endif
+
+#if defined(WIN64EXCEPTIONS)
+void DebuggerJitInfo::InitFuncletAddress()
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ m_funcletCount = (int)g_pEEInterface->GetFuncletStartOffsets((const BYTE*)m_addrOfCode, NULL, 0);
+
+ if (m_funcletCount == 0)
+ {
+ _ASSERTE(m_rgFunclet == NULL);
+ return;
+ }
+
+ m_rgFunclet = (DWORD*)(new (interopsafe, nothrow) DWORD[m_funcletCount]);
+
+ // All bets are off for stepping this method.
+ if (m_rgFunclet == NULL)
+ {
+ m_funcletCount = 0;
+ return;
+ }
+
+ // This will get the offsets relative to the parent method start as if
+ // the funclet was in contiguous memory (i.e. not hot/cold split).
+ g_pEEInterface->GetFuncletStartOffsets((const BYTE*)m_addrOfCode, m_rgFunclet, m_funcletCount);
+}
+
+//
+// DebuggerJitInfo::GetFuncletOffsetByIndex()
+//
+// Given a funclet index, return its starting offset.
+//
+// parameters: index - index of the funclet
+//
+// return value: starting offset of the specified funclet, or -1 if the index is invalid
+//
+DWORD DebuggerJitInfo::GetFuncletOffsetByIndex(int index)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (index < 0 || index >= m_funcletCount)
+ {
+ return (-1);
+ }
+
+ return m_rgFunclet[index];
+}
+
+//
+// DebuggerJitInfo::GetFuncletIndex()
+//
+// Given an offset or an absolute address, return the index of the funclet containing it.
+//
+// parameters: offsetOrAddr - an offset or an absolute address in the method
+// mode - whether the first argument is an offset or an absolute address
+//
+// return value: the index of the funclet containing the specified offset or address,
+// or -1 if it's invalid
+//
+int DebuggerJitInfo::GetFuncletIndex(CORDB_ADDRESS offsetOrAddr, GetFuncletIndexMode mode)
+{
+ WRAPPER_NO_CONTRACT;
+
+ DWORD offset = 0;
+ if (mode == GFIM_BYOFFSET)
+ {
+ offset = (DWORD)offsetOrAddr;
+ }
+
+ // If the address doesn't fall in any of the funclets (or if the
+ // method doesn't have any funclet at all), then return PARENT_METHOD_INDEX.
+ // <TODO>
+ // What if there's an overflow?
+ // </TODO>
+ if (!m_codeRegionInfo.IsMethodAddress((const BYTE *)(mode == GFIM_BYOFFSET ? (size_t)m_codeRegionInfo.OffsetToAddress(offset) : offsetOrAddr)))
+ {
+ return PARENT_METHOD_INDEX;
+ }
+
+ if ( ( m_funcletCount == 0 ) ||
+ ( (mode == GFIM_BYOFFSET) && (offset < m_rgFunclet[0]) ) ||
+ ( (mode == GFIM_BYADDRESS) && (offsetOrAddr < (size_t)m_codeRegionInfo.OffsetToAddress(m_rgFunclet[0])) ) )
+ {
+ return PARENT_METHOD_INDEX;
+ }
+
+ for (int i = 0; i < m_funcletCount; i++)
+ {
+ if (i == (m_funcletCount - 1))
+ {
+ return i;
+ }
+ else if ( ( (mode == GFIM_BYOFFSET) && (offset < m_rgFunclet[i+1]) ) ||
+ ( (mode == GFIM_BYADDRESS) && (offsetOrAddr < (size_t)m_codeRegionInfo.OffsetToAddress(m_rgFunclet[i+1])) ) )
+ {
+ return i;
+ }
+ }
+
+ UNREACHABLE();
+}
+
+#endif // WIN64EXCEPTIONS
+
+// It is entirely possible that we have multiple sequence points for the
+// same IL offset (because of funclets, optimization, etc.). Just to be
+// uniform in all cases, let's return the sequence point with the smallest
+// native offset if fWantFirst is TRUE.
+#if defined(WIN64EXCEPTIONS)
+#define ADJUST_MAP_ENTRY(_map, _wantFirst) \
+ if ((_wantFirst)) \
+ for ( ; (_map) > m_sequenceMap && (((_map)-1)->ilOffset == (_map)->ilOffset); (_map)--); \
+ else \
+ for ( ; (_map) < m_sequenceMap + (m_sequenceMapCount-1) && (((_map)+1)->ilOffset == (_map)->ilOffset); (_map)++);
+#else
+#define ADJUST_MAP_ENTRY(_map, _wantFirst)
+#endif // _WIN64
+
+DebuggerJitInfo::DebuggerJitInfo(DebuggerMethodInfo *minfo, MethodDesc *fd) :
+ m_fd(fd),
+ m_pLoaderModule(fd->GetLoaderModule()),
+ m_jitComplete(false),
+#ifdef EnC_SUPPORTED
+ m_encBreakpointsApplied(false),
+#endif //EnC_SUPPORTED
+ m_methodInfo(minfo),
+ m_addrOfCode(NULL),
+ m_sizeOfCode(0), m_prevJitInfo(NULL), m_nextJitInfo(NULL),
+ m_lastIL(0),
+ m_sequenceMap(NULL),
+ m_sequenceMapCount(0),
+ m_callsiteMap(NULL),
+ m_callsiteMapCount(0),
+ m_sequenceMapSorted(false),
+ m_varNativeInfo(NULL), m_varNativeInfoCount(0),
+ m_fAttemptInit(false)
+#if defined(WIN64EXCEPTIONS)
+ ,m_rgFunclet(NULL)
+ , m_funcletCount(0)
+#endif // defined(WIN64EXCEPTIONS)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // A DJI is just the debugger's cache of interesting information +
+ // various debugger-specific state for a method (like Enc).
+ // So only be createing DJIs when a debugger is actually attached.
+ // The profiler also piggy-backs on the DJIs.
+ // @Todo - the managed stackwalker in the BCL also builds on DJIs.
+ //_ASSERTE(CORDebuggerAttached() || CORProfilerPresent());
+
+ _ASSERTE(minfo);
+ m_encVersion = minfo->GetCurrentEnCVersion();
+ _ASSERTE(m_encVersion >= CorDB_DEFAULT_ENC_FUNCTION_VERSION);
+ LOG((LF_CORDB,LL_EVERYTHING, "DJI::DJI : created at 0x%x\n", this));
+
+ // Debugger doesn't track LightWeight codegen methods.
+ // We should never even be creating a DJI for one.
+ _ASSERTE(!m_fd->IsDynamicMethod());
+}
+
+DebuggerILToNativeMap *DebuggerJitInfo::MapILOffsetToMapEntry(SIZE_T offset, BOOL *exact, BOOL fWantFirst)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CAN_TAKE_LOCK; // GetSequenceMapCount calls LazyInitBounds() which can eventually
+ // call ExecutionManager::IncrementReader
+ }
+ CONTRACTL_END;
+
+ // Ideally we should be able to assert this, since the binary search in this function
+ // assumes that the sequence points are sorted by IL offset (NO_MAPPING, PROLOG, and EPILOG
+ // are actually -1, -2, and -3, respectively). However, the sequence points in pdb's use
+ // -1 to mean "end of the method", which is different from our semantics of using 0.
+ // _ASSERTE(offset != NO_MAPPING && offset != PROLOG && offset != EPILOG);
+
+ //
+ // Binary search for matching map element.
+ //
+
+ DebuggerILToNativeMap *mMin = GetSequenceMap();
+ DebuggerILToNativeMap *mMax = mMin + GetSequenceMapCount();
+
+ _ASSERTE(m_sequenceMapSorted);
+ _ASSERTE( mMin < mMax ); //otherwise we have no code
+
+ if (exact)
+ {
+ *exact = FALSE;
+ }
+
+ if (mMin)
+ {
+ while (mMin + 1 < mMax)
+ {
+ _ASSERTE(mMin>=m_sequenceMap);
+ DebuggerILToNativeMap *mMid = mMin + ((mMax - mMin)>>1);
+ _ASSERTE(mMid>=m_sequenceMap);
+
+ if (offset == mMid->ilOffset)
+ {
+ if (exact)
+ {
+ *exact = TRUE;
+ }
+ ADJUST_MAP_ENTRY(mMid, fWantFirst);
+ return mMid;
+ }
+ else if (offset < mMid->ilOffset && mMid->ilOffset != (ULONG) ICorDebugInfo::PROLOG)
+ {
+ mMax = mMid;
+ }
+ else
+ {
+ mMin = mMid;
+ }
+ }
+
+ if (exact && offset == mMin->ilOffset)
+ {
+ *exact = TRUE;
+ }
+ ADJUST_MAP_ENTRY(mMin, fWantFirst);
+ }
+ return mMin;
+}
+
+void DebuggerJitInfo::InitILToNativeOffsetIterator(ILToNativeOffsetIterator &iterator, SIZE_T ilOffset)
+{
+ WRAPPER_NO_CONTRACT;
+
+ iterator.Init(this, ilOffset);
+}
+
+
+DebuggerJitInfo::NativeOffset DebuggerJitInfo::MapILOffsetToNative(DebuggerJitInfo::ILOffset ilOffset)
+{
+ CONTRACTL
+ {
+ SO_NOT_MAINLINE;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ NativeOffset resultOffset;
+
+ DebuggerILToNativeMap *map = MapILOffsetToMapEntry(ilOffset.m_ilOffset, &(resultOffset.m_fExact));
+
+#if defined(WIN64EXCEPTIONS)
+ // See if we want the map entry for the parent.
+ if (ilOffset.m_funcletIndex <= PARENT_METHOD_INDEX)
+ {
+#endif // _WIN64
+ PREFIX_ASSUME( map != NULL );
+ LOG((LF_CORDB, LL_INFO10000, "DJI::MILOTN: ilOff 0x%x to nat 0x%x exact:0x%x (Entry IL Off:0x%x)\n",
+ ilOffset.m_ilOffset, map->nativeStartOffset, resultOffset.m_fExact, map->ilOffset));
+
+ resultOffset.m_nativeOffset = map->nativeStartOffset;
+
+#if defined(WIN64EXCEPTIONS)
+ }
+ else
+ {
+ // funcletIndex is guaranteed to be >= 0 at this point.
+ if (ilOffset.m_funcletIndex > (m_funcletCount - 1))
+ {
+ resultOffset.m_fExact = FALSE;
+ resultOffset.m_nativeOffset = ((SIZE_T)-1);
+ }
+ else
+ {
+ // Initialize the funclet range.
+ // ASSUMES that funclets are contiguous which they currently are...
+ DWORD funcletStartOffset = GetFuncletOffsetByIndex(ilOffset.m_funcletIndex);
+ DWORD funcletEndOffset;
+ if (ilOffset.m_funcletIndex < (m_funcletCount - 1))
+ {
+ funcletEndOffset = GetFuncletOffsetByIndex(ilOffset.m_funcletIndex + 1);
+ }
+ else
+ {
+ funcletEndOffset = (DWORD)m_sizeOfCode;
+ }
+
+ SIZE_T ilTargetOffset = map->ilOffset;
+
+ DebuggerILToNativeMap *mapEnd = GetSequenceMap() + GetSequenceMapCount();
+
+ for (; map < mapEnd && map->ilOffset == ilTargetOffset; map++)
+ {
+ if ((map->nativeStartOffset >= funcletStartOffset) &&
+ (map->nativeStartOffset < funcletEndOffset))
+ {
+ // This is the normal case where the start offset falls in
+ // the range of the funclet.
+ resultOffset.m_nativeOffset = map->nativeStartOffset;
+ break;
+ }
+ }
+
+ if (map == mapEnd || map->ilOffset != ilTargetOffset)
+ {
+ resultOffset.m_fExact = FALSE;
+ resultOffset.m_nativeOffset = ((SIZE_T)-1);
+ }
+ }
+ }
+#endif // WIN64EXCEPTIONS
+
+ return resultOffset;
+}
+
+
+DebuggerJitInfo::ILToNativeOffsetIterator::ILToNativeOffsetIterator()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_dji = NULL;
+ m_currentILOffset.m_ilOffset = INVALID_IL_OFFSET;
+#ifdef WIN64EXCEPTIONS
+ m_currentILOffset.m_funcletIndex = PARENT_METHOD_INDEX;
+#endif
+}
+
+void DebuggerJitInfo::ILToNativeOffsetIterator::Init(DebuggerJitInfo* dji, SIZE_T ilOffset)
+{
+ WRAPPER_NO_CONTRACT;
+
+ m_dji = dji;
+ m_currentILOffset.m_ilOffset = ilOffset;
+#ifdef WIN64EXCEPTIONS
+ m_currentILOffset.m_funcletIndex = PARENT_METHOD_INDEX;
+#endif
+
+ m_currentNativeOffset = m_dji->MapILOffsetToNative(m_currentILOffset);
+}
+
+bool DebuggerJitInfo::ILToNativeOffsetIterator::IsAtEnd()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (m_currentILOffset.m_ilOffset == INVALID_IL_OFFSET);
+}
+
+SIZE_T DebuggerJitInfo::ILToNativeOffsetIterator::Current(BOOL* pfExact)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (pfExact != NULL)
+ {
+ *pfExact = m_currentNativeOffset.m_fExact;
+ }
+ return m_currentNativeOffset.m_nativeOffset;
+}
+
+SIZE_T DebuggerJitInfo::ILToNativeOffsetIterator::CurrentAssertOnlyOne(BOOL* pfExact)
+{
+ WRAPPER_NO_CONTRACT;
+
+ SIZE_T nativeOffset = Current(pfExact);
+
+ Next();
+ _ASSERTE(IsAtEnd());
+
+ return nativeOffset;
+}
+
+void DebuggerJitInfo::ILToNativeOffsetIterator::Next()
+{
+#if defined(WIN64EXCEPTIONS)
+ NativeOffset tmpNativeOffset;
+
+ for (m_currentILOffset.m_funcletIndex += 1;
+ m_currentILOffset.m_funcletIndex < m_dji->GetFuncletCount();
+ m_currentILOffset.m_funcletIndex++)
+ {
+ tmpNativeOffset = m_dji->MapILOffsetToNative(m_currentILOffset);
+ if (tmpNativeOffset.m_nativeOffset != ((SIZE_T)-1) &&
+ tmpNativeOffset.m_nativeOffset != m_currentNativeOffset.m_nativeOffset)
+ {
+ m_currentNativeOffset = tmpNativeOffset;
+ break;
+ }
+ }
+
+ if (m_currentILOffset.m_funcletIndex == m_dji->GetFuncletCount())
+ {
+ m_currentILOffset.m_ilOffset = INVALID_IL_OFFSET;
+ }
+#else // !WIN64EXCEPTIONS
+ m_currentILOffset.m_ilOffset = INVALID_IL_OFFSET;
+#endif // !WIN64EXCEPTIONS
+}
+
+
+
+// SIZE_T DebuggerJitInfo::MapSpecialToNative(): Maps something like
+// a prolog to a native offset.
+// CordDebugMappingResult mapping: Mapping type to be looking for.
+// SIZE_T which: Which one. <TODO>For now, set to zero. <@todo Later, we'll
+// change this to some value that we get back from MapNativeToILOffset
+// to indicate which of the (possibly multiple epilogs) that may
+// be present.</TODO>
+
+SIZE_T DebuggerJitInfo::MapSpecialToNative(CorDebugMappingResult mapping,
+ SIZE_T which,
+ BOOL *pfAccurate)
+{
+ CONTRACTL
+ {
+ SO_NOT_MAINLINE;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(NULL != pfAccurate);
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CORDB, LL_INFO10000, "DJI::MSTN map:0x%x which:0x%x\n", mapping, which));
+
+ bool fFound;
+ SIZE_T cFound = 0;
+
+ DebuggerILToNativeMap *m = GetSequenceMap();
+ DebuggerILToNativeMap *mEnd = m + GetSequenceMapCount();
+ if (m)
+ {
+ while(m < mEnd)
+ {
+ _ASSERTE(m>=GetSequenceMap());
+
+ fFound = false;
+
+ if (DbgIsSpecialILOffset(m->ilOffset))
+ cFound++;
+
+ if (cFound == which)
+ {
+ _ASSERTE( (mapping == MAPPING_PROLOG &&
+ m->ilOffset == (ULONG) ICorDebugInfo::PROLOG) ||
+ (mapping == MAPPING_EPILOG &&
+ m->ilOffset == (ULONG) ICorDebugInfo::EPILOG) ||
+ ((mapping == MAPPING_NO_INFO || mapping == MAPPING_UNMAPPED_ADDRESS) &&
+ m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
+ );
+
+ (*pfAccurate) = TRUE;
+ LOG((LF_CORDB, LL_INFO10000, "DJI::MSTN found mapping to nat:0x%x\n",
+ m->nativeStartOffset));
+ return m->nativeStartOffset;
+ }
+ m++;
+ }
+ }
+
+ LOG((LF_CORDB, LL_INFO10000, "DJI::MSTN No mapping found :(\n"));
+ (*pfAccurate) = FALSE;
+
+ return 0;
+}
+
+#if defined(WIN64EXCEPTIONS)
+//
+// DebuggerJitInfo::MapILOffsetToNativeForSetIP()
+//
+// This function maps an IL offset to a native offset, taking into account cloned finallys and nested EH clauses.
+//
+// parameters: offsetILTo - the destination IP, in IL offset
+// funcletIndexFrom - the funclet index of the source IP
+// pEHRT - tree structure for keeping track of EH clause information
+// pExact - pointer for returning whether the mapping is exact or not
+//
+// return value: destination IP, in native offset
+//
+SIZE_T DebuggerJitInfo::MapILOffsetToNativeForSetIP(SIZE_T offsetILTo, int funcletIndexFrom,
+ EHRangeTree* pEHRT, BOOL* pExact)
+{
+ CONTRACTL
+ {
+ SO_NOT_MAINLINE;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ DebuggerILToNativeMap* pMap = MapILOffsetToMapEntry(offsetILTo, pExact, TRUE);
+ DebuggerILToNativeMap* pMapEnd = GetSequenceMap() + GetSequenceMapCount();
+
+ _ASSERTE(pMap == m_sequenceMap ||
+ (pMap - 1)->ilOffset == (ULONG)ICorDebugInfo::NO_MAPPING ||
+ (pMap - 1)->ilOffset == (ULONG)ICorDebugInfo::PROLOG ||
+ (pMap - 1)->ilOffset == (ULONG)ICorDebugInfo::EPILOG ||
+ pMap->ilOffset > (pMap - 1)->ilOffset);
+
+ SIZE_T offsetNatTo = pMap->nativeStartOffset;
+
+ if (m_funcletCount == 0 ||
+ pEHRT == NULL ||
+ FAILED(pEHRT->m_hrInit))
+ {
+ return offsetNatTo;
+ }
+
+ // Multiple sequence points may have the same IL offset, which means that the code is duplicated in
+ // multiple funclets and/or in the parent method. If the destination offset maps to multiple sequence
+ // points (and hence to multiple funclets), we try to find the a sequence point which is in the same
+ // funclet as the source sequence point. If we can't find one, then the operation is going to fail
+ // anyway, so we just return the first sequence point we find.
+ for (DebuggerILToNativeMap* pMapCur = pMap + 1;
+ (pMapCur < pMapEnd) && (pMapCur->ilOffset == pMap->ilOffset);
+ pMapCur++)
+ {
+ int funcletIndexTo = GetFuncletIndex(pMapCur->nativeStartOffset, DebuggerJitInfo::GFIM_BYOFFSET);
+ if (funcletIndexFrom == funcletIndexTo)
+ {
+ return pMapCur->nativeStartOffset;
+ }
+ }
+
+ return offsetNatTo;
+}
+#endif // _WIN64
+
+// void DebuggerJitInfo::MapILRangeToMapEntryRange(): MIRTMER
+// calls MapILOffsetToNative for the startOffset (putting the
+// result into start), and the endOffset (putting the result into end).
+// SIZE_T startOffset: IL offset from beginning of function.
+// SIZE_T endOffset: IL offset from beginngin of function,
+// or zero to indicate that the end of the function should be used.
+// DebuggerILToNativeMap **start: Contains start & end
+// native offsets that correspond to startOffset. Set to NULL if
+// there is no mapping info.
+// DebuggerILToNativeMap **end: Contains start & end native
+// offsets that correspond to endOffset. Set to NULL if there
+// is no mapping info.
+void DebuggerJitInfo::MapILRangeToMapEntryRange(SIZE_T startOffset,
+ SIZE_T endOffset,
+ DebuggerILToNativeMap **start,
+ DebuggerILToNativeMap **end)
+{
+ CONTRACTL
+ {
+ SO_NOT_MAINLINE;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CORDB, LL_INFO1000000,
+ "DJI::MIRTMER: IL 0x%04x-0x%04x\n",
+ startOffset, endOffset));
+
+ if (GetSequenceMapCount() == 0)
+ {
+ *start = NULL;
+ *end = NULL;
+ return;
+ }
+
+ *start = MapILOffsetToMapEntry(startOffset);
+
+ //
+ // end points to the last range that endOffset maps to, not past
+ // the last range.
+ // We want to return the last IL, and exclude the epilog
+ if (endOffset == 0)
+ {
+ *end = GetSequenceMap() + GetSequenceMapCount() - 1;
+ _ASSERTE(*end>=m_sequenceMap);
+
+ while ( ((*end)->ilOffset == (ULONG) ICorDebugInfo::EPILOG||
+ (*end)->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
+ && (*end) > m_sequenceMap)
+ {
+ (*end)--;
+ _ASSERTE(*end>=m_sequenceMap);
+
+ }
+ }
+ else
+ *end = MapILOffsetToMapEntry(endOffset - 1, NULL
+ WIN64_ARG(FALSE));
+
+ _ASSERTE(*end>=m_sequenceMap);
+
+
+ LOG((LF_CORDB, LL_INFO1000000,
+ "DJI::MIRTMER: IL 0x%04x-0x%04x --> 0x%04x 0x%08x-0x%08x\n"
+ " --> 0x%04x 0x%08x-0x%08x\n",
+ startOffset, endOffset,
+ (*start)->ilOffset,
+ (*start)->nativeStartOffset, (*start)->nativeEndOffset,
+ (*end)->ilOffset,
+ (*end)->nativeStartOffset, (*end)->nativeEndOffset));
+}
+
+// @dbgtodo Microsoft inspection: This function has been replicated in DacDbiStructures so
+// this version can be deleted when inspection is complete.
+
+// DWORD DebuggerJitInfo::MapNativeOffsetToIL(): Given a native
+// offset for the DebuggerJitInfo, compute
+// the IL offset from the beginning of the same method.
+// Returns: Offset of the IL instruction that contains
+// the native offset,
+// SIZE_T nativeOffset: [IN] Native Offset
+// CorDebugMappingResult *map: [OUT] explains the
+// quality of the matching & special cases
+// SIZE_T which: It's possible to have multiple EPILOGs, or
+// multiple unmapped regions within a method. This opaque value
+// specifies which special region we're talking about. This
+// param has no meaning if map & (MAPPING_EXACT|MAPPING_APPROXIMATE)
+// Basically, this gets handed back to MapSpecialToNative, later.
+DWORD DebuggerJitInfo::MapNativeOffsetToIL(SIZE_T nativeOffsetToMap,
+ CorDebugMappingResult *map,
+ DWORD *which,
+ BOOL skipPrologs)
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(map != NULL);
+ PRECONDITION(which != NULL);
+ }
+ CONTRACTL_END;
+
+ DWORD nativeOffset = (DWORD)nativeOffsetToMap;
+
+ (*which) = 0;
+ DebuggerILToNativeMap *m = GetSequenceMap();
+ DebuggerILToNativeMap *mEnd = m + GetSequenceMapCount();
+
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: nativeOffset = 0x%x\n", nativeOffset));
+
+ if (m)
+ {
+ while (m < mEnd)
+ {
+ _ASSERTE(m>=m_sequenceMap);
+
+#ifdef LOGGING
+ if (m->ilOffset == (ULONG) ICorDebugInfo::PROLOG )
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:PROLOG\n", m->nativeStartOffset, m->nativeEndOffset));
+ else if (m->ilOffset == (ULONG) ICorDebugInfo::EPILOG )
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:EPILOG\n", m->nativeStartOffset, m->nativeEndOffset));
+ else if (m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:NO MAP\n", m->nativeStartOffset, m->nativeEndOffset));
+ else
+ 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));
+#endif // LOGGING
+
+ if (m->ilOffset == (ULONG) ICorDebugInfo::PROLOG ||
+ m->ilOffset == (ULONG) ICorDebugInfo::EPILOG ||
+ m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
+ {
+ (*which)++;
+ }
+
+ if (nativeOffset >= m->nativeStartOffset
+ && ((m->nativeEndOffset == 0 &&
+ m->ilOffset != (ULONG) ICorDebugInfo::PROLOG)
+ || nativeOffset < m->nativeEndOffset))
+ {
+ ULONG ilOff = m->ilOffset;
+
+ if( m->ilOffset == (ULONG) ICorDebugInfo::PROLOG )
+ {
+ if (skipPrologs && nativeOffset < m->nativeEndOffset)
+ {
+ // If the caller requested to skip prologs, we simply restart the walk
+ // with the offset set to the end of the prolog.
+ nativeOffset = m->nativeEndOffset;
+ continue;
+ }
+
+ ilOff = 0;
+ (*map) = MAPPING_PROLOG;
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: MAPPING_PROLOG\n"));
+
+ }
+ else if (m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
+ {
+ ilOff = 0;
+ (*map) = MAPPING_UNMAPPED_ADDRESS ;
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_"
+ "UNMAPPED_ADDRESS\n"));
+ }
+ else if( m->ilOffset == (ULONG) ICorDebugInfo::EPILOG )
+ {
+ ilOff = m_lastIL;
+ (*map) = MAPPING_EPILOG;
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_EPILOG\n"));
+ }
+ else if (nativeOffset == m->nativeStartOffset)
+ {
+ (*map) = MAPPING_EXACT;
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_EXACT\n"));
+ }
+ else
+ {
+ (*map) = MAPPING_APPROXIMATE;
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_"
+ "APPROXIMATE\n"));
+ }
+
+ return ilOff;
+ }
+ m++;
+ }
+ }
+
+ (*map) = MAPPING_NO_INFO;
+ LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:NO_INFO\n"));
+ return 0;
+}
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+DebuggerJitInfo::~DebuggerJitInfo()
+{
+ TRACE_FREE(m_sequenceMap);
+ if (m_sequenceMap != NULL)
+ {
+ DeleteInteropSafe(((BYTE *)m_sequenceMap));
+ }
+
+ TRACE_FREE(m_varNativeInfo);
+ if (m_varNativeInfo != NULL)
+ {
+ DeleteInteropSafe(m_varNativeInfo);
+ }
+
+#if defined(WIN64EXCEPTIONS)
+ if (m_rgFunclet)
+ {
+ DeleteInteropSafe(m_rgFunclet);
+ m_rgFunclet = NULL;
+ }
+#endif // WIN64EXCEPTIONS
+
+
+#ifdef _DEBUG
+ // Trash pointers to garbage.
+ // Don't null out since there may be runtime checks against NULL.
+ // Set to a non-null random pointer value that will cause an immediate AV on deref.
+ m_fd = (MethodDesc*) 0x1;
+ m_methodInfo = (DebuggerMethodInfo*) 0x1;
+ m_prevJitInfo = (DebuggerJitInfo*) 0x01;
+ m_nextJitInfo = (DebuggerJitInfo*) 0x01;
+#endif
+
+
+ LOG((LF_CORDB,LL_EVERYTHING, "DJI::~DJI : deleted at 0x%p\n", this));
+}
+
+
+// Lazy initialize the Debugger-Jit-Info
+void DebuggerJitInfo::LazyInitBounds()
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(ThisMaybeHelperThread());
+ PRECONDITION(!g_pDebugger->HasDebuggerDataLock());
+ } CONTRACTL_END;
+
+ //@todo: this method is not synchronized. Mei-chin's recent work should cover this one
+ // Only attempt lazy-init once
+ // new LOG message
+ LOG((LF_CORDB,LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x m_fAttemptInit %s\n", this, m_fAttemptInit == true? "true": "false"));
+ if (m_fAttemptInit)
+ {
+ return;
+ }
+ m_fAttemptInit = true;
+
+ EX_TRY
+ {
+ LOG((LF_CORDB,LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x Initing\n", this));
+ // Should have already been jitted
+ _ASSERTE(this->m_jitComplete);
+
+ MethodDesc * mdesc = this->m_fd;
+
+ DebugInfoRequest request;
+
+ _ASSERTE(this->m_addrOfCode != NULL); // must have address to disambguate the Enc cases.
+ // Caller already resolved generics when they craeted the DJI, so we don't need to repeat.
+ // 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.
+ request.InitFromStartingAddr(mdesc, (PCODE)this->m_addrOfCode);
+
+
+ // Bounds info.
+ ULONG32 cMap = 0;
+ ICorDebugInfo::OffsetMapping *pMap = NULL;
+ ULONG32 cVars = 0;
+ ICorDebugInfo::NativeVarInfo *pVars = NULL;
+
+ BOOL fSuccess = DebugInfoManager::GetBoundariesAndVars(
+ request,
+ InteropSafeNew, NULL, // allocator
+ &cMap, &pMap,
+ &cVars, &pVars);
+ LOG((LF_CORDB,LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x GetBoundariesAndVars success=0x%x\n", this, fSuccess));
+ if (fSuccess)
+ {
+ this->SetBoundaries(cMap, pMap);
+ this->SetVars(cVars, pVars);
+ }
+ }
+ EX_CATCH
+ {
+ LOG((LF_CORDB,LL_WARNING, "DJI::LazyInitBounds: this=0x%x Exception was thrown and caught\n", this));
+ // Just catch the exception. The DJI maps may or may-not be intialized,
+ // but they should still be in a consistent state, so we should be ok.
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+}
+
+/******************************************************************************
+ * SetVars() takes ownership of pVars
+ ******************************************************************************/
+void DebuggerJitInfo::SetVars(ULONG32 cVars, ICorDebugInfo::NativeVarInfo *pVars)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_varNativeInfo)
+ {
+ return;
+ }
+
+ m_varNativeInfo = pVars;
+ m_varNativeInfoCount = cVars;
+
+ LOG((LF_CORDB, LL_INFO1000000, "D::sV: var count is %d\n",
+ m_varNativeInfoCount));
+
+#ifdef LOGGING
+ for (unsigned int i = 0; i < m_varNativeInfoCount; i++)
+ {
+ ICorDebugInfo::NativeVarInfo* vni = &(m_varNativeInfo[i]);
+ _dumpVarNativeInfo(vni);
+ }
+#endif
+}
+
+CHECK DebuggerJitInfo::Check() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ CHECK_OK;
+}
+
+// Invariants for a DebuggerJitInfo
+// These should always be true at any well defined point.
+CHECK DebuggerJitInfo::Invariant() const
+{
+ LIMITED_METHOD_CONTRACT;
+ CHECK((m_sequenceMapCount == 0) == (m_sequenceMap == NULL));
+ CHECK(m_methodInfo != NULL);
+ CHECK(m_fd != NULL);
+
+ CHECK_OK;
+}
+
+
+#if !defined(DACCESS_COMPILE)
+/******************************************************************************
+ * SetBoundaries() takes ownership of pMap
+ ******************************************************************************/
+void DebuggerJitInfo::SetBoundaries(ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap)
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CORDB,LL_EVERYTHING, "DJI::SetBoundaries: this=0x%x cMap=0x%x pMap=0x%x\n", this, cMap, pMap));
+ _ASSERTE((cMap == 0) == (pMap == NULL));
+
+ if (cMap == 0)
+ return;
+
+ if (m_sequenceMap)
+ {
+ return;
+ }
+
+ ULONG ilLast = 0;
+#ifdef _DEBUG
+ // We assume that the map is sorted by native offset
+ if (cMap > 1)
+ {
+ for(ICorDebugInfo::OffsetMapping * pEntry = pMap;
+ pEntry < (pMap + cMap - 1);
+ pEntry++)
+ {
+ _ASSERTE(pEntry->nativeOffset <= (pEntry+1)->nativeOffset);
+ }
+ }
+#endif //_DEBUG
+
+ //
+ // <TODO>@todo perf: allocating these on the heap is slow. We could do
+ // better knowing that these live for the life of the run, just
+ // like the DebuggerJitInfo's.</TODO>
+ //
+ m_sequenceMap = (DebuggerILToNativeMap *)new (interopsafe) DebuggerILToNativeMap[cMap];
+ LOG((LF_CORDB,LL_EVERYTHING, "DJI::SetBoundaries: this=0x%x m_sequenceMap=0x%x\n", this, m_sequenceMap));
+ _ASSERTE(m_sequenceMap != NULL); // we'll throw on null
+
+ m_sequenceMapCount = cMap;
+
+ DebuggerILToNativeMap *m = m_sequenceMap;
+
+ // For the instrumented-IL case, we need to remove all duplicate entries.
+ // So we keep a record of the last old IL offset. If the current old IL
+ // offset is the same as the last old IL offset, we remove it.
+ // Pick a unique initial value (-10) so that the 1st doesn't accidentally match.
+ int ilPrevOld = -10;
+
+ InstrumentedILOffsetMapping mapping =
+ m_methodInfo->GetRuntimeModule()->GetInstrumentedILOffsetMapping(m_methodInfo->m_token);
+
+ //
+ // <TODO>@todo perf: we could do the vast majority of this
+ // post-processing work the first time the sequence point map is
+ // demanded. That would allow us to simply hold the raw array for
+ // 95% of the functions jitted while debugging, and 100% of them
+ // when just running/tracking.</TODO>
+ const DWORD call_inst = (DWORD)ICorDebugInfo::CALL_INSTRUCTION;
+ for(ULONG32 idxJitMap = 0; idxJitMap < cMap; idxJitMap++)
+ {
+ const ICorDebugInfo::OffsetMapping * const pMapEntry = &pMap[idxJitMap];
+ _ASSERTE(m >= m_sequenceMap);
+ _ASSERTE(m < m_sequenceMap + m_sequenceMapCount);
+
+ ilLast = max((int)ilLast, (int)pMapEntry->ilOffset);
+
+ // Simply copy everything over, since we translate to
+ // CorDebugMappingResults immediately prior to handing
+ // back to user...
+ m->nativeStartOffset = pMapEntry->nativeOffset;
+ m->ilOffset = pMapEntry->ilOffset;
+ m->source = pMapEntry->source;
+
+ // Keep in mind that if we have an instrumented code translation
+ // table, we may have asked for completely different IL offsets
+ // than the user thinks we did.....
+
+ // If we did instrument, then we can't have any sequence points that
+ // are "in-between" the old-->new map that the profiler gave us.
+ // Ex, if map is:
+ // (6 old -> 36 new)
+ // (8 old -> 50 new)
+ // And the jit gives us an entry for 44 new, that will map back to 6 old.
+ // Since the map can only have one entry for 6 old, we remove 44 new.
+ if (m_methodInfo->HasInstrumentedILMap())
+ {
+ int ilThisOld = m_methodInfo->TranslateToInstIL(&mapping,
+ pMapEntry->ilOffset,
+ bInstrumentedToOriginal);
+
+ if (ilThisOld == ilPrevOld)
+ {
+ // If this translated to the same old IL offset as the last entry,
+ // then this is "in between". Skip it.
+ m_sequenceMapCount--; // one less seq point in the DJI's map
+ continue;
+ }
+ m->ilOffset = ilThisOld;
+ ilPrevOld = ilThisOld;
+ }
+
+ if (m > m_sequenceMap && (m->source & call_inst) != call_inst)
+ {
+ DebuggerILToNativeMap *last = m-1;
+ if ((last->source & call_inst) == call_inst)
+ last = (last > m_sequenceMap) ? last - 1 : NULL;
+
+ if (last && (last->source & call_inst) != call_inst && m->ilOffset == last->ilOffset)
+ {
+ // JIT gave us an extra entry (probably zero), so mush
+ // it into the one we've already got.
+ // <TODO> Why does this happen?</TODO>
+ m_sequenceMapCount--;
+ continue;
+ }
+ }
+
+
+ // Move to next entry in the debugger's table
+ m++;
+ } // end for
+
+ DeleteInteropSafe(pMap);
+
+ _ASSERTE(m == m_sequenceMap + m_sequenceMapCount);
+
+ m_lastIL = ilLast;
+
+ // Set nativeEndOffset in debugger's il->native map
+ // Do this before we resort by IL.
+ unsigned int i;
+ for(i = 0; i < m_sequenceMapCount - 1; i++)
+ {
+ // We need to not use CALL_INSTRUCTION's IL start offset.
+ unsigned int j = i + 1;
+ while ((m_sequenceMap[j].source & call_inst) == call_inst && j < m_sequenceMapCount-1)
+ j++;
+
+ m_sequenceMap[i].nativeEndOffset = m_sequenceMap[j].nativeStartOffset;
+ }
+
+ m_sequenceMap[i].nativeEndOffset = 0;
+ m_sequenceMap[i].source = (ICorDebugInfo::SourceTypes)
+ ((DWORD) m_sequenceMap[i].source |
+ (DWORD)ICorDebugInfo::NATIVE_END_OFFSET_UNKNOWN);
+
+ // Now resort by IL.
+ MapSortIL isort(m_sequenceMap, m_sequenceMapCount);
+
+ isort.Sort();
+
+ m_sequenceMapSorted = true;
+
+ m_callsiteMapCount = m_sequenceMapCount;
+ while (m_sequenceMapCount > 0 && (m_sequenceMap[m_sequenceMapCount-1].source & call_inst) == call_inst)
+ m_sequenceMapCount--;
+
+ m_callsiteMap = m_sequenceMap + m_sequenceMapCount;
+ m_callsiteMapCount -= m_sequenceMapCount;
+
+ LOG((LF_CORDB, LL_INFO100000, "DJI::SetBoundaries: this=0x%x boundary count is %d (%d callsites)\n",
+ this, m_sequenceMapCount, m_callsiteMapCount));
+
+#ifdef LOGGING
+ for (unsigned int count = 0; count < m_sequenceMapCount + m_callsiteMapCount; count++)
+ {
+ if( m_sequenceMap[count].ilOffset ==
+ (ULONG) ICorDebugInfo::PROLOG )
+ LOG((LF_CORDB, LL_INFO1000000,
+ "D::sB: PROLOG --> 0x%08x -- 0x%08x",
+ m_sequenceMap[count].nativeStartOffset,
+ m_sequenceMap[count].nativeEndOffset));
+ else if ( m_sequenceMap[count].ilOffset ==
+ (ULONG) ICorDebugInfo::EPILOG )
+ LOG((LF_CORDB, LL_INFO1000000,
+ "D::sB: EPILOG --> 0x%08x -- 0x%08x",
+ m_sequenceMap[count].nativeStartOffset,
+ m_sequenceMap[count].nativeEndOffset));
+ else if ( m_sequenceMap[count].ilOffset ==
+ (ULONG) ICorDebugInfo::NO_MAPPING )
+ LOG((LF_CORDB, LL_INFO1000000,
+ "D::sB: NO MAP --> 0x%08x -- 0x%08x",
+ m_sequenceMap[count].nativeStartOffset,
+ m_sequenceMap[count].nativeEndOffset));
+ else
+ LOG((LF_CORDB, LL_INFO1000000,
+ "D::sB: 0x%04x (Real:0x%04x) --> 0x%08x -- 0x%08x",
+ m_sequenceMap[count].ilOffset,
+ m_methodInfo->TranslateToInstIL(&mapping,
+ m_sequenceMap[count].ilOffset,
+ bOriginalToInstrumented),
+ m_sequenceMap[count].nativeStartOffset,
+ m_sequenceMap[count].nativeEndOffset));
+
+ LOG((LF_CORDB, LL_INFO1000000, " Src:0x%x\n", m_sequenceMap[count].source));
+
+ }
+#endif //LOGGING
+}
+#endif // !DACCESS_COMPILE
+
+// Init a DJI after it's jitted.
+void DebuggerJitInfo::Init(TADDR newAddress)
+{
+ // Shouldn't initialize while holding the lock b/c intialzing may call functions that lock,
+ // and thus we'd have a locking violation.
+ _ASSERTE(!g_pDebugger->HasDebuggerDataLock());
+
+ this->m_addrOfCode = (ULONG_PTR)PTR_TO_CORDB_ADDRESS((BYTE*) newAddress);
+ this->m_jitComplete = true;
+
+ this->m_codeRegionInfo.InitializeFromStartAddress((PCODE)this->m_addrOfCode);
+ this->m_sizeOfCode = this->m_codeRegionInfo.getSizeOfTotalCode();
+
+ this->m_encVersion = this->m_methodInfo->GetCurrentEnCVersion();
+
+#if defined(WIN64EXCEPTIONS)
+ this->InitFuncletAddress();
+#endif // WIN64EXCEPTIONS
+
+ LOG((LF_CORDB,LL_INFO10000,"De::JITCo:Got DJI 0x%p(V %d),"
+ "Hot section from 0x%p to 0x%p "
+ "Cold section from 0x%p to 0x%p "
+ "varCount=%d seqCount=%d\n",
+ this, this->m_encVersion,
+ this->m_codeRegionInfo.getAddrOfHotCode(),
+ this->m_codeRegionInfo.getAddrOfHotCode() + this->m_codeRegionInfo.getSizeOfHotCode(),
+ this->m_codeRegionInfo.getAddrOfColdCode(),
+ this->m_codeRegionInfo.getAddrOfColdCode() + this->m_codeRegionInfo.getSizeOfColdCode(),
+ (ULONG)this->m_addrOfCode,
+ (ULONG)this->m_addrOfCode+(ULONG)this->m_sizeOfCode,
+ this->GetVarNativeInfoCount(),
+ this->GetSequenceMapCount()));
+
+#if defined(LOGGING)
+ for (unsigned int i = 0; i < this->GetSequenceMapCount(); i++)
+ {
+ LOG((LF_CORDB, LL_INFO10000, "De::JITCo: seq map 0x%x - "
+ "IL offset 0x%x native start offset 0x%x native end offset 0x%x source 0x%x\n",
+ i, this->GetSequenceMap()[i].ilOffset,
+ this->GetSequenceMap()[i].nativeStartOffset,
+ this->GetSequenceMap()[i].nativeEndOffset,
+ this->GetSequenceMap()[i].source));
+ }
+#endif // LOGGING
+
+}
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+ICorDebugInfo::SourceTypes DebuggerJitInfo::GetSrcTypeFromILOffset(SIZE_T ilOffset)
+{
+ CONTRACTL
+ {
+ SO_NOT_MAINLINE;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ BOOL exact = FALSE;
+ DebuggerILToNativeMap *pMap = MapILOffsetToMapEntry(ilOffset, &exact);
+
+ LOG((LF_CORDB, LL_INFO100000, "DJI::GSTFILO: for il 0x%x, got entry 0x%p,"
+ "(il 0x%x) nat 0x%x to 0x%x, SourceTypes 0x%x, exact:%x\n", ilOffset, pMap,
+ pMap->ilOffset, pMap->nativeStartOffset, pMap->nativeEndOffset, pMap->source,
+ exact));
+
+ if (!exact)
+ {
+ return ICorDebugInfo::SOURCE_TYPE_INVALID;
+ }
+
+ return pMap->source;
+}
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+DebuggerMethodInfo::~DebuggerMethodInfo()
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ DESTRUCTOR_CHECK;
+ }
+ CONTRACTL_END;
+
+ DeleteJitInfoList();
+
+ LOG((LF_CORDB,LL_EVERYTHING, "DMI::~DMI : deleted at 0x%p\n", this));
+}
+
+// Translate between old & new offsets (w/ respect to Instrumented IL).
+
+// Don't interpolate
+ULONG32 DebuggerMethodInfo::TranslateToInstIL(const InstrumentedILOffsetMapping * pMapping,
+ ULONG32 offOrig,
+ bool fOrigToInst)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ SIZE_T iMap;
+ SIZE_T cMap = pMapping->GetCount();
+ // some negative IL offsets have special meaning. Don't translate
+ // those (just return as is). See ICorDebugInfo::MappingTypes
+ if ((cMap == 0) || (offOrig < 0))
+ {
+ return offOrig;
+ }
+
+ ARRAY_PTR_COR_IL_MAP rgMap = pMapping->GetOffsets();
+
+ // This assumes:
+ // - map is sorted in increasing order by both old & new
+ // - round down.
+ if (fOrigToInst)
+ {
+ // Translate: old --> new
+
+ // Treat it as prolog if offOrig is not in remapping range
+ if ((offOrig < rgMap[0].oldOffset) || (offOrig == (ULONG32)ICorDebugInfo::PROLOG))
+ {
+ return (ULONG32)ICorDebugInfo::PROLOG;
+ }
+
+ if (offOrig == (ULONG32)ICorDebugInfo::EPILOG)
+ {
+ return (ULONG32)ICorDebugInfo::EPILOG;
+ }
+
+ if (offOrig == (ULONG32)ICorDebugInfo::NO_MAPPING)
+ {
+ return (ULONG32)ICorDebugInfo::NO_MAPPING;
+ }
+
+ for(iMap = 1; iMap < cMap; iMap++)
+ {
+ if (offOrig < rgMap[iMap].oldOffset)
+ return rgMap[iMap-1].newOffset;
+ }
+
+ return rgMap[iMap - 1].newOffset;
+ }
+ else
+ {
+ // Translate: new --> old
+
+ // Treat it as prolog if offOrig is not in remapping range
+ if ((offOrig < rgMap[0].newOffset) || (offOrig == (ULONG32)ICorDebugInfo::PROLOG))
+ {
+ return (ULONG32)ICorDebugInfo::PROLOG;
+ }
+
+ if (offOrig == (ULONG32)ICorDebugInfo::EPILOG)
+ {
+ return (ULONG32)ICorDebugInfo::EPILOG;
+ }
+
+ if (offOrig == (ULONG32)ICorDebugInfo::NO_MAPPING)
+ {
+ return (ULONG32)ICorDebugInfo::NO_MAPPING;
+ }
+
+ for(iMap = 1; iMap < cMap; iMap++)
+ {
+ if (offOrig < rgMap[iMap].newOffset)
+ return rgMap[iMap-1].oldOffset;
+ }
+
+ return rgMap[iMap - 1].oldOffset;
+ }
+}
+
+/******************************************************************************
+ * Constructor for DebuggerMethodInfo
+ ******************************************************************************/
+DebuggerMethodInfo::DebuggerMethodInfo(Module *module, mdMethodDef token) :
+ m_currentEnCVersion(CorDB_DEFAULT_ENC_FUNCTION_VERSION),
+ m_module(module),
+ m_token(token),
+ m_prevMethodInfo(NULL),
+ m_nextMethodInfo(NULL),
+ m_latestJitInfo(NULL),
+ m_fHasInstrumentedILMap(false)
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ WRAPPER(THROWS);
+ WRAPPER(GC_TRIGGERS);
+ CONSTRUCTOR_CHECK;
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CORDB,LL_EVERYTHING, "DMI::DMI : created at 0x%p\n", this));
+
+ _ASSERTE(g_pDebugger->HasDebuggerDataLock());
+
+ DebuggerModule * pModule = GetPrimaryModule();
+
+ m_fJMCStatus = false;
+
+ // If there's no module, then this isn't a JMC function.
+ // This can happen since DMIs are created for debuggable code, and
+ // Modules are only created if a debugger is actually attached.
+ if (pModule != NULL)
+ {
+ // Use the accessor so that we keep the module's count properly updated.
+ SetJMCStatus(pModule->GetRuntimeModule()->GetJMCStatus());
+ }
+ }
+
+
+/******************************************************************************
+ * Get the primary debugger module for this DMI. This is 1:1 w/ an EE Module.
+ ******************************************************************************/
+DebuggerModule* DebuggerMethodInfo::GetPrimaryModule()
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(g_pDebugger->HasDebuggerDataLock());
+
+ DebuggerModuleTable * pTable = g_pDebugger->GetModuleTable();
+
+ // If we're tracking debug info but no debugger's attached, then
+ // we won't have a table for the modules yet.
+ if (pTable == NULL)
+ return NULL;
+
+ DebuggerModule * pModule = pTable->GetModule(GetRuntimeModule());
+ if (pModule == NULL)
+ {
+ // We may be missing the module even if we have the table.
+ // 1.) If there's no debugger attached (so we're not getting ModuleLoad events).
+ // 2.) If we're asking for this while in DllMain of the module we're currently loading,
+ // we won't have gotten the ModuleLoad event yet.
+ return NULL;
+ }
+
+ // Only give back primary modules...
+ DebuggerModule * p2 = pModule->GetPrimaryModule();
+ _ASSERTE(p2 != NULL);
+
+ return p2;
+}
+
+/******************************************************************************
+ * Get the runtime module for this DMI
+ ******************************************************************************/
+Module * DebuggerMethodInfo::GetRuntimeModule()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_module;
+}
+
+#endif // !DACCESS_COMPILE
+
+
+//---------------------------------------------------------------------------------------
+//
+// Find the DebuggerJitInfo (DJI) for the given MethodDesc and native start address.
+// We need the native start address because generic methods may have multiple instances
+// of jitted code. This function does not create the DJI if it does not already exist.
+//
+// Arguments:
+// pMD - the MD to lookup; must be non-NULL
+// addrNativeStartAddr - the native start address of jitted code
+//
+// Return Value:
+// Returns the DJI corresponding to the specified MD and native start address.
+// NULL if the DJI is not found.
+//
+
+DebuggerJitInfo * DebuggerMethodInfo::FindJitInfo(MethodDesc * pMD,
+ TADDR addrNativeStartAddr)
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ SUPPORTS_DAC;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(pMD != NULL);
+ }
+ CONTRACTL_END;
+
+
+ DebuggerJitInfo * pCheck = m_latestJitInfo;
+ while (pCheck != NULL)
+ {
+ if ( (pCheck->m_fd == dac_cast<PTR_MethodDesc>(pMD)) &&
+ (pCheck->m_addrOfCode == addrNativeStartAddr) )
+ {
+ return pCheck;
+ }
+
+ pCheck = pCheck->m_prevJitInfo;
+ }
+
+ return NULL;
+}
+
+
+#if !defined(DACCESS_COMPILE)
+
+/*
+ * FindOrCreateInitAndAddJitInfo
+ *
+ * This routine allocates a new DJI, adding it to the DMI.
+ *
+ * Parameters:
+ * fd - the method desc to create a DJI for.
+ *
+ * Returns
+ * A pointer to the created DJI, or NULL.
+ *
+ */
+
+DebuggerJitInfo *DebuggerMethodInfo::FindOrCreateInitAndAddJitInfo(MethodDesc* fd)
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(fd != NULL);
+
+ // This will grab the latest EnC version.
+ TADDR addr = (TADDR) g_pEEInterface->GetFunctionAddress(fd);
+
+ if (addr == NULL)
+ return NULL;
+
+ // Check the lsit to see if we've already populated an entry for this JitInfo.
+ // If we didn't have a JitInfo before, lazily create it now.
+ // We don't care if we were prejitted or not.
+ //
+ // We haven't got the lock yet so we'll repeat this lookup once
+ // we've taken the lock.
+ DebuggerJitInfo * pResult = FindJitInfo(fd, addr);
+ if (pResult != NULL)
+ {
+ // Found!
+ return pResult;
+ }
+
+
+ // CreateInitAndAddJitInfo takes a lock and checks the list again, which
+ // makes this thread-safe.
+ return CreateInitAndAddJitInfo(fd, addr);
+}
+
+// Create a DJI around a method-desc. The EE already has all the information we need for a DJI,
+// the DJI just serves as a cache of the information for the debugger.
+// Caller makes no guarantees about whether the DJI is already in the table. (Caller should avoid this if
+// it knows it's in the table, but b/c we can't expect caller to synchronize w/ the other threads).
+DebuggerJitInfo *DebuggerMethodInfo::CreateInitAndAddJitInfo(MethodDesc* fd, TADDR startAddr)
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(!g_pDebugger->HasDebuggerDataLock());
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(fd != NULL);
+
+ // May or may-not be jitted, that's why we passed in the start addr & size explicitly.
+ _ASSERTE(startAddr != NULL);
+
+
+ // No support for light-weight codegen methods.
+ if (fd->IsDynamicMethod())
+ {
+ return NULL;
+ }
+
+
+ DebuggerJitInfo *dji = new (interopsafe) DebuggerJitInfo(this, fd);
+ _ASSERTE(dji != NULL); // throws on oom error
+
+ _ASSERTE(dji->m_methodInfo == this); // this should be set
+
+ TRACE_ALLOC(dji);
+
+ // Init may take locks that violate the debugger-data lock, so we can't init while we hold that lock.
+ // But we can't init after we add it to the table and release the lock b/c another thread may pick
+ // if up in the uninitialized state.
+ // So we initialize a private copy of the DJI before we take the debugger-data lock.
+ dji->Init(startAddr);
+
+ dji->m_nextJitInfo = NULL;
+
+ //
+ //<TODO>@TODO : _ASSERTE(EnC);</TODO>
+ //
+ {
+ Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger);
+
+ // We need to ensure that another thread didn't go in and add this exact same DJI?
+ {
+ DebuggerJitInfo * pResult = FindJitInfo(dji->m_fd, (TADDR)dji->m_addrOfCode);
+ if (pResult != NULL)
+ {
+ // Found!
+ _ASSERTE(pResult->m_sizeOfCode == dji->m_sizeOfCode);
+ DeleteInteropSafe(dji);
+ return pResult;
+ }
+ }
+
+ // We know it's not in the table. Go add it!
+ DebuggerJitInfo *djiPrev = m_latestJitInfo;
+
+ LOG((LF_CORDB,LL_INFO10000,"DMI:CAAJI: current head of dji list:0x%08x\n", djiPrev));
+
+ if (djiPrev != NULL)
+ {
+ dji->m_prevJitInfo = djiPrev;
+ djiPrev->m_nextJitInfo = dji;
+
+ m_latestJitInfo = dji;
+
+ LOG((LF_CORDB,LL_INFO10000,"DMI:CAAJI: DJI version 0x%04x for %s\n",
+ GetCurrentEnCVersion(),
+ dji->m_fd->m_pszDebugMethodName));
+ }
+ else
+ {
+ m_latestJitInfo = dji;
+ }
+
+ } // DebuggerDataLockHolder out of scope - release implied
+
+ // We've now added a new DJI into the table and released the lock. Thus any other thread
+ // can come and use our DJI. Good thing we inited the DJI _before_ adding it to the table.
+
+ LOG((LF_CORDB,LL_INFO10000,"DMI:CAAJI: new head of dji list:0x%08x\n", m_latestJitInfo));
+
+ return dji;
+}
+
+/*
+ * DeleteJitInfo
+ *
+ * This routine remove a DJI from the DMI's list and deletes the memory.
+ *
+ * Parameters:
+ * dji - The DJI to delete.
+ *
+ * Returns
+ * None.
+ *
+ */
+
+void DebuggerMethodInfo::DeleteJitInfo(DebuggerJitInfo *dji)
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger);
+
+ LOG((LF_CORDB,LL_INFO10000,"DMI:DJI: dji:0x%08x\n", dji));
+
+ DebuggerJitInfo *djiPrev = dji->m_prevJitInfo;
+
+ if (djiPrev != NULL)
+ {
+ djiPrev->m_nextJitInfo = dji->m_nextJitInfo;
+ }
+
+ if (dji->m_nextJitInfo != NULL)
+ {
+ dji->m_nextJitInfo->m_prevJitInfo = djiPrev;
+ }
+ else
+ {
+ //
+ // This DJI is the head of the list
+ //
+ _ASSERTE(m_latestJitInfo == dji);
+
+ m_latestJitInfo = djiPrev;
+ }
+
+ TRACE_FREE(dji);
+
+ DeleteInteropSafe(dji);
+
+ // DebuggerDataLockHolder out of scope - release implied
+}
+
+/*
+ * DeleteJitInfoList
+ *
+ * This routine removes all the DJIs from the current DMI.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns
+ * None.
+ *
+ */
+
+void DebuggerMethodInfo::DeleteJitInfoList(void)
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger);
+
+ while(m_latestJitInfo != NULL)
+ {
+ DeleteJitInfo(m_latestJitInfo);
+ }
+
+ // DebuggerDataLockHolder out of scope - release implied
+}
+
+
+// Iterate through all existing DJIs. See header for expected usage.
+DebuggerMethodInfo::DJIIterator::DJIIterator()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pCurrent = NULL;
+ m_pLoaderModuleFilter = NULL;
+}
+
+bool DebuggerMethodInfo::DJIIterator::IsAtEnd()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pCurrent == NULL;
+}
+
+DebuggerJitInfo * DebuggerMethodInfo::DJIIterator::Current()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pCurrent;
+}
+
+void DebuggerMethodInfo::DJIIterator::Next(BOOL fFirst /*=FALSE*/)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ if (!fFirst)
+ {
+ PREFIX_ASSUME(m_pCurrent != NULL); // IsAtEnd() should have caught this.
+ m_pCurrent = m_pCurrent->m_prevJitInfo;
+ }
+
+ // Check if we're at the end of the list, in which case we're done.
+ for ( ; m_pCurrent != NULL; m_pCurrent = m_pCurrent->m_prevJitInfo)
+ {
+ Module * pLoaderModule = m_pCurrent->m_pLoaderModule;
+
+ // Obey the module filter if it's provided
+ if ((m_pLoaderModuleFilter != NULL) && (m_pLoaderModuleFilter != pLoaderModule))
+ continue;
+
+ // Skip modules that are unloaded, but still hanging around. Note that we can't use DebuggerModule for this check
+ // because of it is deleted pretty early during unloading, and we do not want to recreate it.
+ if (pLoaderModule->GetLoaderAllocator()->IsUnloaded())
+ continue;
+
+ break;
+ }
+}
+
+
+/******************************************************************************
+ * Return true iff this method is jitted
+ ******************************************************************************/
+bool DebuggerMethodInfo::HasJitInfos()
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(g_pDebugger->HasDebuggerDataLock());
+ return (m_latestJitInfo != NULL);
+}
+
+/******************************************************************************
+ * Return true iff this has been EnCed since last time function was jitted.
+ ******************************************************************************/
+bool DebuggerMethodInfo::HasMoreRecentEnCVersion()
+{
+ LIMITED_METHOD_CONTRACT;
+ return ((m_latestJitInfo != NULL) &&
+ (m_currentEnCVersion > m_latestJitInfo->m_encVersion));
+}
+
+/******************************************************************************
+ * Updated the instrumented-IL map
+ ******************************************************************************/
+void DebuggerMethodInfo::SetInstrumentedILMap(COR_IL_MAP * pMap, SIZE_T cEntries)
+{
+ InstrumentedILOffsetMapping mapping;
+ mapping.SetMappingInfo(cEntries, pMap);
+
+ GetRuntimeModule()->SetInstrumentedILOffsetMapping(m_token, mapping);
+
+ m_fHasInstrumentedILMap = true;
+}
+
+/******************************************************************************
+ * Get the JMC status for a given function.
+ ******************************************************************************/
+bool DebuggerMethodInfo::IsJMCFunction()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_fJMCStatus;
+}
+
+/******************************************************************************
+ * Set the JMC status to a given value
+ ******************************************************************************/
+void DebuggerMethodInfo::SetJMCStatus(bool fStatus)
+{
+ CONTRACTL
+ {
+ SO_NOT_MAINLINE;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(g_pDebugger->HasDebuggerDataLock());
+
+ // First check if this is a no-op.
+ // Do this first b/c there may be some cases where we don't have a DebuggerModule
+ // yet but are still calling SetJMCStatus(false), like if we detach before attach is complete.
+ bool fOldStatus = IsJMCFunction();
+
+ if (fOldStatus == fStatus)
+ {
+ // if no change, then there's nothing to do.
+ LOG((LF_CORDB,LL_EVERYTHING, "DMI::SetJMCStatus: %p, keeping old status, %d\n", this, fStatus));
+ return;
+ }
+
+ // For a perf-optimization, our Module needs to know if it has any user
+ // code. If it doesn't, it shouldn't dispatch through the JMC probes.
+ // So modules keep a count of # of JMC functions - if the count is 0, the
+ // module can set is JMC probe flag to 0 and skip the JMC probes.
+ Module * pRuntimeModule = this->GetRuntimeModule();
+
+ // Update the module's count.
+ if (!fStatus)
+ {
+ LOG((LF_CORDB,LL_EVERYTHING, "DMI::SetJMCStatus: %p, changing to non-user code\n", this));
+ _ASSERTE(pRuntimeModule->HasAnyJMCFunctions());
+ pRuntimeModule->DecJMCFuncCount();
+ }
+ else
+ {
+ LOG((LF_CORDB,LL_EVERYTHING, "DMI::SetJMCStatus: %p, changing to user code\n", this));
+ pRuntimeModule->IncJMCFuncCount();
+ _ASSERTE(pRuntimeModule->HasAnyJMCFunctions());
+ }
+
+ m_fJMCStatus = fStatus;
+
+ // We should update our module's JMC status...
+ g_pDebugger->UpdateModuleJMCFlag(pRuntimeModule, DebuggerController::GetTotalMethodEnter() != 0);
+
+}
+
+// Get an iterator that will go through ALL native code-blobs (DJI) in the specified
+// AppDomain, optionally filtered by loader module (if pLoaderModuleFilter != NULL).
+// This is EnC/ Generics / Prejit aware.
+void DebuggerMethodInfo::IterateAllDJIs(AppDomain * pAppDomain, Module * pLoaderModuleFilter, DebuggerMethodInfo::DJIIterator * pEnum)
+{
+ CONTRACTL
+ {
+ SO_NOT_MAINLINE;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pEnum != NULL);
+ _ASSERTE(pAppDomain != NULL);
+
+ // Esnure we have DJIs for everything.
+ CreateDJIsForNativeBlobs(pAppDomain, pLoaderModuleFilter);
+
+ pEnum->m_pCurrent = m_latestJitInfo;
+ pEnum->m_pLoaderModuleFilter = pLoaderModuleFilter;
+
+ // Advance to the first DJI that passes the filter
+ pEnum->Next(TRUE);
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Bring the DJI cache up to date.
+//
+// Arguments:
+// * pAppDomain - Create DJIs only for this AppDomain
+// * pLoaderModuleFilter - If non-NULL, create DJIs only for MethodDescs whose
+// loader module matches this one. (This can be different from m_module in the
+// case of generics defined in one module and instantiated in another). If
+// non-NULL, create DJIs for all modules in pAppDomain.
+//
+
+void DebuggerMethodInfo::CreateDJIsForNativeBlobs(AppDomain * pAppDomain, Module * pLoaderModuleFilter /* = NULL */)
+{
+ CONTRACTL
+ {
+ SO_NOT_MAINLINE;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // If we're not stopped and the module we're iterating over allows types to load,
+ // then it's possible new native blobs are being created underneath us.
+ _ASSERTE(g_pDebugger->IsStopped() || ((pLoaderModuleFilter != NULL) && !pLoaderModuleFilter->IsReadyForTypeLoad()));
+
+ // @todo - we really only need to do this if the stop-counter goes up (else we know nothing new is added).
+ // B/c of generics, it's possible that new instantiations of a method may have been jitted.
+ // So just loop through all known instantiations and ensure that we have all the DJIs.
+ // Note that this iterator won't show previous EnC versions, but we're already guaranteed to
+ // have DJIs for every verision of a method that was EnCed.
+ // This also handles the possibility of getting the same methoddesc back from the iterator.
+ // It also lets EnC + generics play nice together (including if an generic method was EnC-ed)
+ LoadedMethodDescIterator it(pAppDomain, m_module, m_token);
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+ while (it.Next(pDomainAssembly.This()))
+ {
+ MethodDesc * pDesc = it.Current();
+ if (!pDesc->HasNativeCode())
+ {
+ continue;
+ }
+
+ Module * pLoaderModule = pDesc->GetLoaderModule();
+
+ // Obey the module filter if it's provided
+ if ((pLoaderModuleFilter != NULL) && (pLoaderModuleFilter != pLoaderModule))
+ continue;
+
+ // Skip modules that are unloaded, but still hanging around. Note that we can't use DebuggerModule for this check
+ // because of it is deleted pretty early during unloading, and we do not want to recreate it.
+ if (pLoaderModule->GetLoaderAllocator()->IsUnloaded())
+ continue;
+
+ // We just ask for the DJI to ensure that it's lazily created.
+ // This should only fail in an oom scenario.
+ DebuggerJitInfo * djiTest = g_pDebugger->GetLatestJitInfoFromMethodDesc(pDesc);
+ if (djiTest == NULL)
+ {
+ // We're oom. Give up.
+ ThrowOutOfMemory();
+ return;
+ }
+ }
+}
+
+/*
+ * GetLatestJitInfo
+ *
+ * This routine returns the lastest DJI we have for a particular DMI.
+ * DJIs are lazily created.
+ * Parameters:
+ * None.
+ *
+ * Returns
+ * a possibly NULL pointer to a DJI.
+ *
+ */
+
+// For logging and other internal purposes, provide a non-initializing accessor.
+DebuggerJitInfo* DebuggerMethodInfo::GetLatestJitInfo_NoCreate()
+{
+ return m_latestJitInfo;
+}
+
+
+DebuggerMethodInfoTable::DebuggerMethodInfoTable() : CHashTableAndData<CNewZeroData>(101)
+{
+ CONTRACTL
+ {
+ WRAPPER(THROWS);
+ GC_NOTRIGGER;
+
+ CONSTRUCTOR_CHECK;
+ }
+ CONTRACTL_END;
+
+ SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
+ HRESULT hr = NewInit(101, sizeof(DebuggerMethodInfoEntry), 101);
+
+ if (FAILED(hr))
+ {
+ ThrowWin32(hr);
+ }
+}
+
+HRESULT DebuggerMethodInfoTable::AddMethodInfo(Module *pModule,
+ mdMethodDef token,
+ DebuggerMethodInfo *mi)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(pModule));
+ PRECONDITION(CheckPointer(mi));
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CORDB, LL_INFO1000, "DMIT::AMI Adding dmi:0x%x Mod:0x%x tok:"
+ "0x%x nVer:0x%x\n", mi, pModule, token, mi->GetCurrentEnCVersion()));
+
+ _ASSERTE(mi != NULL);
+
+ _ASSERTE(g_pDebugger->HasDebuggerDataLock());
+
+ HRESULT hr = OverwriteMethodInfo(pModule, token, mi, TRUE);
+ if (hr == S_OK)
+ return hr;
+
+ DebuggerMethodInfoKey dmik;
+ dmik.pModule = pModule;
+ dmik.token = token;
+
+ DebuggerMethodInfoEntry *dmie =
+ (DebuggerMethodInfoEntry *) Add(HASH(&dmik));
+
+ if (dmie != NULL)
+ {
+ dmie->key.pModule = pModule;
+ dmie->key.token = token;
+ dmie->mi = mi;
+
+ LOG((LF_CORDB, LL_INFO1000, "DMIT::AJI: mod:0x%x tok:0%x ",
+ pModule, token));
+ return S_OK;
+ }
+
+ ThrowOutOfMemory();
+ return S_OK;
+}
+
+HRESULT DebuggerMethodInfoTable::OverwriteMethodInfo(Module *pModule,
+ mdMethodDef token,
+ DebuggerMethodInfo *mi,
+ BOOL fOnlyIfNull)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+
+ PRECONDITION(CheckPointer(pModule));
+ PRECONDITION(CheckPointer(mi));
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CORDB, LL_INFO1000, "DMIT::OJI: dmi:0x%x mod:0x%x tok:0x%x\n", mi,
+ pModule, token));
+
+ _ASSERTE(g_pDebugger->HasDebuggerDataLock());
+
+ DebuggerMethodInfoKey dmik;
+ dmik.pModule = pModule;
+ dmik.token = token;
+
+ DebuggerMethodInfoEntry *entry
+ = (DebuggerMethodInfoEntry *) Find(HASH(&dmik), KEY(&dmik));
+ if (entry != NULL)
+ {
+ if ( (fOnlyIfNull &&
+ entry->mi == NULL) ||
+ !fOnlyIfNull)
+ {
+ entry->mi = mi;
+
+ LOG((LF_CORDB, LL_INFO1000, "DMIT::OJI: mod:0x%x tok:0x%x remap"
+ "nVer:0x%x\n", pModule, token, entry->nVersionLastRemapped));
+ return S_OK;
+ }
+ }
+
+ return E_FAIL;
+}
+
+// pModule is being destroyed - remove any entries that belong to it. Why?
+// (a) Correctness: the module can be reloaded at the same address,
+// which will cause accidental matches with our hashtable (indexed by
+// {Module*,mdMethodDef}
+// (b) Perf: don't waste the memory!
+void DebuggerMethodInfoTable::ClearMethodsOfModule(Module *pModule)
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(g_pDebugger->HasDebuggerDataLock());
+
+ LOG((LF_CORDB, LL_INFO1000000, "CMOM:mod:0x%x (%S)\n", pModule
+ ,pModule->GetDebugName()));
+
+ HASHFIND info;
+
+ DebuggerMethodInfoEntry *entry
+ = (DebuggerMethodInfoEntry *) FindFirstEntry(&info);
+ while(entry != NULL)
+ {
+ Module *pMod = entry->key.pModule ;
+ if (pMod == pModule)
+ {
+ // This method actually got mitted, at least
+ // once - remove all version info.
+ while(entry->mi != NULL)
+ {
+ DeleteEntryDMI(entry);
+ }
+
+ Delete(HASH(&(entry->key)), (HASHENTRY*)entry);
+ }
+ else
+ {
+ //
+ // Delete generic DJIs that have lifetime attached to this module
+ //
+ DebuggerMethodInfo * dmi = entry->mi;
+ while (dmi != NULL)
+ {
+ DebuggerJitInfo * dji = dmi->GetLatestJitInfo_NoCreate();
+ while (dji != NULL)
+ {
+ DebuggerJitInfo * djiPrev = dji->m_prevJitInfo;;
+
+ if (dji->m_pLoaderModule == pModule)
+ dmi->DeleteJitInfo(dji);
+
+ dji = djiPrev;
+ }
+
+ dmi = dmi->m_prevMethodInfo;
+ }
+ }
+
+ entry = (DebuggerMethodInfoEntry *) FindNextEntry(&info);
+ }
+}
+
+void DebuggerMethodInfoTable::DeleteEntryDMI(DebuggerMethodInfoEntry *entry)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ CAN_TAKE_LOCK; // DeleteInteropSafe() eventually calls DebuggerMethodInfo::DeleteJitInfoList
+ // which locks.
+ }
+ CONTRACTL_END;
+
+ DebuggerMethodInfo *dmiPrev = entry->mi->m_prevMethodInfo;
+ TRACE_FREE(entry->mi);
+ DeleteInteropSafe(entry->mi);
+ entry->mi = dmiPrev;
+ if ( dmiPrev != NULL )
+ dmiPrev->m_nextMethodInfo = NULL;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+DebuggerJitInfo *DebuggerJitInfo::GetJitInfoByAddress(const BYTE *pbAddr )
+{
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ DebuggerJitInfo *dji = this;
+
+#ifdef LOGGING
+ LOG((LF_CORDB,LL_INFO10000,"DJI:GJIBA finding DJI "
+ "corresponding to addr 0x%p, starting with 0x%p\n", pbAddr, dji));
+#endif //LOGGING
+
+ // If it's not NULL, but not in the range m_addrOfCode to end of function,
+ // then get the previous one.
+ while( dji != NULL &&
+ !CodeRegionInfo::GetCodeRegionInfo(dji).IsMethodAddress(pbAddr))
+ {
+ LOG((LF_CORDB,LL_INFO10000,"DJI:GJIBA: pbAddr 0x%p is not in code "
+ "0x%p (size:0x%p)\n", pbAddr, dji->m_addrOfCode,
+ dji->m_sizeOfCode));
+ dji = dji->m_prevJitInfo;
+ }
+
+#ifdef LOGGING
+ if (dji == NULL)
+ {
+ LOG((LF_CORDB,LL_INFO10000,"DJI:GJIBA couldn't find a DJI "
+ "corresponding to addr 0x%p\n", pbAddr));
+ }
+#endif //LOGGING
+ return dji;
+}
+
+PTR_DebuggerJitInfo DebuggerMethodInfo::GetLatestJitInfo(MethodDesc *mdesc)
+{
+ // dac checks ngen'ed image content first, so
+ // only check for existing JIT info.
+#ifndef DACCESS_COMPILE
+
+ CONTRACTL
+ {
+ SO_INTOLERANT;
+ THROWS;
+ CALLED_IN_DEBUGGERDATALOCK_HOLDER_SCOPE_MAY_GC_TRIGGERS_CONTRACT;
+ PRECONDITION(!g_pDebugger->HasDebuggerDataLock());
+ }
+ CONTRACTL_END;
+
+
+ if (m_latestJitInfo && m_latestJitInfo->m_fd == mdesc && !m_latestJitInfo->m_fd->HasClassOrMethodInstantiation())
+ return m_latestJitInfo;
+
+ // This ensures that there is an entry in the DJI list for this particular MethodDesc.
+ // in the case of generic code it may not be the first entry in the list.
+ FindOrCreateInitAndAddJitInfo(mdesc);
+
+#endif // #ifndef DACCESS_COMPILE
+
+ return m_latestJitInfo;
+}
+
+DebuggerMethodInfo *DebuggerMethodInfoTable::GetMethodInfo(Module *pModule, mdMethodDef token)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ // CHECK_DMI_TABLE;
+
+ // @review. One of the BVTs causes this to be called before the table is initialized
+ // In particular, the changes to BREAKPOINT_ADD mean that this table is now consulted
+ // to determine if we have ever seen the method, rather than a call to LookupMethodDesc,
+ // which would have just returned NULL. In general it seems OK to consult this table
+ // when it is empty, so I've added this....
+ if (this == NULL)
+ return NULL;
+
+ DebuggerMethodInfoKey dmik;
+ dmik.pModule = dac_cast<PTR_Module>(pModule);
+ dmik.token = token;
+
+ DebuggerMethodInfoEntry *entry = dac_cast<PTR_DebuggerMethodInfoEntry>(Find(HASH(&dmik), KEY(&dmik)));
+
+ if (entry == NULL )
+ {
+ return NULL;
+ }
+ else
+ {
+ LOG((LF_CORDB, LL_INFO1000, "DMI::GMI: for methodDef 0x%x, got 0x%x prev:0x%x\n",
+ token, entry->mi, (entry->mi?entry->mi->m_prevMethodInfo:0)));
+ return entry->mi;
+ }
+}
+
+
+DebuggerMethodInfo *DebuggerMethodInfoTable::GetFirstMethodInfo(HASHFIND *info)
+{
+ CONTRACT(DebuggerMethodInfo*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+
+ PRECONDITION(CheckPointer(info));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ _ASSERTE(g_pDebugger->HasDebuggerDataLock());
+
+ DebuggerMethodInfoEntry *entry = PTR_DebuggerMethodInfoEntry
+ (PTR_HOST_TO_TADDR(FindFirstEntry(info)));
+ if (entry == NULL)
+ RETURN NULL;
+ else
+ RETURN entry->mi;
+}
+
+DebuggerMethodInfo *DebuggerMethodInfoTable::GetNextMethodInfo(HASHFIND *info)
+{
+ CONTRACT(DebuggerMethodInfo*)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+
+ PRECONDITION(CheckPointer(info));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ _ASSERTE(g_pDebugger->HasDebuggerDataLock());
+
+ DebuggerMethodInfoEntry *entry = PTR_DebuggerMethodInfoEntry
+ (PTR_HOST_TO_TADDR(FindNextEntry(info)));
+
+ // We may have incremented the version number
+ // for methods that never got JITted, so we should
+ // pretend like they don't exist here.
+ while (entry != NULL &&
+ entry->mi == NULL)
+ {
+ entry = PTR_DebuggerMethodInfoEntry
+ (PTR_HOST_TO_TADDR(FindNextEntry(info)));
+ }
+
+ if (entry == NULL)
+ RETURN NULL;
+ else
+ RETURN entry->mi;
+}
+
+
+
+#ifdef DACCESS_COMPILE
+void
+DebuggerMethodInfoEntry::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+
+ // This structure is in an array in the hash
+ // so the 'this' is implicitly enumerated by the
+ // array enum in CHashTable.
+
+ // For a MiniDumpNormal, what is needed for modules is already enumerated elsewhere.
+ // Don't waste time doing it here an extra time. Also, this will add many MB extra into the dump.
+ if ((key.pModule.IsValid()) &&
+ CLRDATA_ENUM_MEM_MINI != flags
+ && CLRDATA_ENUM_MEM_TRIAGE != flags)
+ {
+ key.pModule->EnumMemoryRegions(flags, true);
+ }
+
+ while (mi.IsValid())
+ {
+ mi->EnumMemoryRegions(flags);
+ mi = mi->m_prevMethodInfo;
+ }
+}
+
+void
+DebuggerMethodInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ DAC_ENUM_DTHIS();
+ SUPPORTS_DAC;
+
+ if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
+ {
+ // Modules are enumerated already for minidumps, save the empty calls.
+ if (m_module.IsValid())
+ {
+ m_module->EnumMemoryRegions(flags, true);
+ }
+
+ }
+
+ PTR_DebuggerJitInfo jitInfo = m_latestJitInfo;
+ while (jitInfo.IsValid())
+ {
+ jitInfo->EnumMemoryRegions(flags);
+ jitInfo = jitInfo->m_prevJitInfo;
+ }
+}
+
+void
+DebuggerJitInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ DAC_ENUM_DTHIS();
+ SUPPORTS_DAC;
+
+ if (m_methodInfo.IsValid())
+ {
+ m_methodInfo->EnumMemoryRegions(flags);
+ }
+
+ if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
+ {
+ if (m_fd.IsValid())
+ {
+ m_fd->EnumMemoryRegions(flags);
+ }
+
+ DacEnumMemoryRegion(PTR_TO_TADDR(GetSequenceMap()),
+ GetSequenceMapCount() * sizeof(DebuggerILToNativeMap));
+ DacEnumMemoryRegion(PTR_TO_TADDR(GetVarNativeInfo()),
+ GetVarNativeInfoCount() *
+ sizeof(ICorDebugInfo::NativeVarInfo));
+ }
+}
+
+
+void DebuggerMethodInfoTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ WRAPPER_NO_CONTRACT;
+
+ DAC_ENUM_VTHIS();
+ CHashTableAndData<CNewZeroData>::EnumMemoryRegions(flags);
+
+ for (ULONG i = 0; i < m_iEntries; i++)
+ {
+ DebuggerMethodInfoEntry* entry =
+ PTR_DebuggerMethodInfoEntry(PTR_HOST_TO_TADDR(EntryPtr(i)));
+ entry->EnumMemoryRegions(flags);
+ }
+}
+#endif // #ifdef DACCESS_COMPILE