summaryrefslogtreecommitdiff
path: root/src/vm/debuginfostore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/debuginfostore.cpp')
-rw-r--r--src/vm/debuginfostore.cpp711
1 files changed, 711 insertions, 0 deletions
diff --git a/src/vm/debuginfostore.cpp b/src/vm/debuginfostore.cpp
new file mode 100644
index 0000000000..04e23bdd98
--- /dev/null
+++ b/src/vm/debuginfostore.cpp
@@ -0,0 +1,711 @@
+// 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.
+// DebugInfoStore
+
+
+
+#include "common.h"
+#include "debuginfostore.h"
+#include "nibblestream.h"
+
+
+#ifdef _DEBUG
+// For debug builds only.
+static bool Dbg_ShouldUseCookies()
+{
+ SUPPORTS_DAC;
+
+ // Normally we want this as false b/c it would bloat the image.
+ // But give us a hook to enable it in case we need it.
+ return false;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// We have "Transfer" objects that sit on top of the streams.
+// The objects look identical, but one serializes and the other deserializes.
+// This lets the compression + restoration routines share all their compression
+// logic and just swap out Transfer objects.
+//
+// It's not ideal that we have a lot of redundancy maintaining both Transfer
+// objects, but at least the compiler can enforce that the Reader & Writer are
+// in sync. It can't enforce that a 2 separate routines for Compression &
+// restoration are in sync.
+//
+// We could have the TransferReader + Writer be polymorphic off a base class,
+// but the virtual function calls will be extra overhead. May as well use
+// templates and let the compiler resolve it all statically at compile time.
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Serialize to a NibbleWriter stream.
+//-----------------------------------------------------------------------------
+class TransferWriter
+{
+public:
+ TransferWriter(NibbleWriter & w) : m_w(w)
+ {
+ }
+
+ // Write an raw U32 in nibble encoded form.
+ void DoEncodedU32(DWORD dw) { m_w.WriteEncodedU32(dw); }
+
+ // Use to encode a monotonically increasing delta.
+ void DoEncodedDeltaU32(DWORD & dw, DWORD dwLast)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ _ASSERTE(dw >= dwLast);
+ DWORD dwDelta = dw - dwLast;
+ m_w.WriteEncodedU32(dwDelta);
+ }
+
+
+ // Some U32 may have a few sentinal negative values .
+ // We adjust it to be a real U32 and then encode that.
+ // dwAdjust should be the lower bound on the enum.
+ void DoEncodedAdjustedU32(DWORD dw, DWORD dwAdjust)
+ {
+ //_ASSERTE(dwAdjust < 0); // some negative lower bound.
+ m_w.WriteEncodedU32(dw - dwAdjust);
+ }
+
+ // Typesafe versions of EncodeU32.
+ void DoEncodedSourceType(ICorDebugInfo::SourceTypes & dw) { m_w.WriteEncodedU32(dw); }
+ void DoEncodedVarLocType(ICorDebugInfo::VarLocType & dw) { m_w.WriteEncodedU32(dw); }
+ void DoEncodedUnsigned(unsigned & dw) { m_w.WriteEncodedU32(dw); }
+
+ // Stack offsets are aligned on a DWORD boundary, so that lets us shave off 2 bits.
+ void DoEncodedStackOffset(signed & dwOffset)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+#ifdef _TARGET_X86_
+ _ASSERTE(dwOffset % sizeof(DWORD) == 0); // should be dword aligned. That'll save us 2 bits.
+ m_w.WriteEncodedI32(dwOffset / sizeof(DWORD));
+#else
+ // Non x86 platforms don't need it to be dword aligned.
+ m_w.WriteEncodedI32(dwOffset);
+#endif
+ }
+
+ void DoEncodedRegIdx(ICorDebugInfo::RegNum & reg) { m_w.WriteEncodedU32(reg); }
+
+ // For debugging purposes, inject cookies into the Compression.
+ void DoCookie(BYTE b) {
+#ifdef _DEBUG
+ if (Dbg_ShouldUseCookies())
+ {
+ m_w.WriteNibble(b);
+ }
+#endif
+ }
+
+protected:
+ NibbleWriter & m_w;
+
+};
+
+//-----------------------------------------------------------------------------
+// Deserializer that sits on top of a NibbleReader
+// This class interface matches TransferWriter exactly. See that for details.
+//-----------------------------------------------------------------------------
+class TransferReader
+{
+public:
+ TransferReader(NibbleReader & r) : m_r(r)
+ {
+ SUPPORTS_DAC;
+ }
+
+ void DoEncodedU32(DWORD & dw)
+ {
+ SUPPORTS_DAC;
+ dw = m_r.ReadEncodedU32();
+ }
+
+ // Use to decode a monotonically increasing delta.
+ // dwLast was the last value; we update it to the current value on output.
+ void DoEncodedDeltaU32(DWORD & dw, DWORD dwLast)
+ {
+ SUPPORTS_DAC;
+ DWORD dwDelta = m_r.ReadEncodedU32();
+ dw = dwLast + dwDelta;
+ }
+
+ void DoEncodedAdjustedU32(DWORD & dw, DWORD dwAdjust)
+ {
+ SUPPORTS_DAC;
+ //_ASSERTE(dwAdjust < 0);
+ dw = m_r.ReadEncodedU32() + dwAdjust;
+ }
+
+ void DoEncodedSourceType(ICorDebugInfo::SourceTypes & dw)
+ {
+ SUPPORTS_DAC;
+ dw = (ICorDebugInfo::SourceTypes) m_r.ReadEncodedU32();
+ }
+
+ void DoEncodedVarLocType(ICorDebugInfo::VarLocType & dw)
+ {
+ SUPPORTS_DAC;
+ dw = (ICorDebugInfo::VarLocType) m_r.ReadEncodedU32();
+ }
+
+ void DoEncodedUnsigned(unsigned & dw)
+ {
+ SUPPORTS_DAC;
+ dw = (unsigned) m_r.ReadEncodedU32();
+ }
+
+
+ // Stack offsets are aligned on a DWORD boundary, so that lets us shave off 2 bits.
+ void DoEncodedStackOffset(signed & dwOffset)
+ {
+ SUPPORTS_DAC;
+#ifdef _TARGET_X86_
+ dwOffset = m_r.ReadEncodedI32() * sizeof(DWORD);
+#else
+ // Non x86 platforms don't need it to be dword aligned.
+ dwOffset = m_r.ReadEncodedI32();
+#endif
+ }
+
+ void DoEncodedRegIdx(ICorDebugInfo::RegNum & reg)
+ {
+ SUPPORTS_DAC;
+ reg = (ICorDebugInfo::RegNum) m_r.ReadEncodedU32();
+ }
+
+ // For debugging purposes, inject cookies into the Compression.
+ void DoCookie(BYTE b)
+ {
+ SUPPORTS_DAC;
+
+#ifdef _DEBUG
+ if (Dbg_ShouldUseCookies())
+ {
+ BYTE b2 = m_r.ReadNibble();
+ _ASSERTE(b == b2);
+ }
+#endif
+ }
+
+
+protected:
+ NibbleReader & m_r;
+};
+
+
+#if defined(_DEBUG) && !defined(DACCESS_COMPILE)
+// Perf tracking
+static int g_CDI_TotalMethods = 0;
+static int g_CDI_bMethodTotalUncompress = 0;
+static int g_CDI_bMethodTotalCompress = 0;
+
+static int g_CDI_bVarsTotalUncompress = 0;
+static int g_CDI_bVarsTotalCompress = 0;
+#endif
+
+//-----------------------------------------------------------------------------
+// Serialize Bounds info.
+//-----------------------------------------------------------------------------
+template <class T>
+void DoBounds(
+ T trans, // transfer object.
+ ULONG32 cMap,
+ ICorDebugInfo::OffsetMapping *pMap
+)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+
+ // Bounds info contains (Native Offset, IL Offset, flags)
+ // - Sorted by native offset (so use a delta encoding for that).
+ // - IL offsets aren't sorted, but they should be close to each other (so a signed delta encoding)
+ // They may also include a sentinel value from MappingTypes.
+ // - flags is 3 indepedent bits.
+
+ // Loop through and transfer each Entry in the Mapping.
+ DWORD dwLastNativeOffset = 0;
+ for(DWORD i = 0; i < cMap; i++)
+ {
+ ICorDebugInfo::OffsetMapping * pBound = &pMap[i];
+
+ trans.DoEncodedDeltaU32(pBound->nativeOffset, dwLastNativeOffset);
+ dwLastNativeOffset = pBound->nativeOffset;
+
+
+ trans.DoEncodedAdjustedU32(pBound->ilOffset, (DWORD) ICorDebugInfo::MAX_MAPPING_VALUE);
+
+ trans.DoEncodedSourceType(pBound->source);
+
+ trans.DoCookie(0xA);
+ }
+}
+
+
+
+// Helper to write a compressed Native Var Info
+template<class T>
+void DoNativeVarInfo(
+ T trans,
+ ICorDebugInfo::NativeVarInfo * pVar
+)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+
+ // Each Varinfo has a:
+ // - native start +End offset. We can use a delta for the end offset.
+ // - Il variable number. These are usually small.
+ // - VarLoc information. This is a tagged variant.
+ // The entries aren't sorted in any particular order.
+ trans.DoCookie(0xB);
+ trans.DoEncodedU32(pVar->startOffset);
+
+
+ trans.DoEncodedDeltaU32(pVar->endOffset, pVar->startOffset);
+
+ // record var number.
+ trans.DoEncodedAdjustedU32(pVar->varNumber, (DWORD) ICorDebugInfo::MAX_ILNUM);
+
+
+ // Now write the VarLoc... This is a variant like structure and so we'll get different
+ // compressioned depending on what we've got.
+ trans.DoEncodedVarLocType(pVar->loc.vlType);
+
+ switch(pVar->loc.vlType)
+ {
+ case ICorDebugInfo::VLT_REG:
+ case ICorDebugInfo::VLT_REG_FP: // fall through
+ case ICorDebugInfo::VLT_REG_BYREF: // fall through
+ trans.DoEncodedRegIdx(pVar->loc.vlReg.vlrReg);
+ break;
+
+ case ICorDebugInfo::VLT_STK:
+ case ICorDebugInfo::VLT_STK_BYREF: // fall through
+ trans.DoEncodedRegIdx(pVar->loc.vlStk.vlsBaseReg);
+ trans.DoEncodedStackOffset(pVar->loc.vlStk.vlsOffset);
+ break;
+
+ case ICorDebugInfo::VLT_REG_REG:
+ trans.DoEncodedRegIdx(pVar->loc.vlRegReg.vlrrReg1);
+ trans.DoEncodedRegIdx(pVar->loc.vlRegReg.vlrrReg2);
+ break;
+
+ case ICorDebugInfo::VLT_REG_STK:
+ trans.DoEncodedRegIdx(pVar->loc.vlRegStk.vlrsReg);
+ trans.DoEncodedRegIdx(pVar->loc.vlRegStk.vlrsStk.vlrssBaseReg);
+ trans.DoEncodedStackOffset(pVar->loc.vlRegStk.vlrsStk.vlrssOffset);
+ break;
+
+ case ICorDebugInfo::VLT_STK_REG:
+ trans.DoEncodedStackOffset(pVar->loc.vlStkReg.vlsrStk.vlsrsOffset);
+ trans.DoEncodedRegIdx(pVar->loc.vlStkReg.vlsrStk.vlsrsBaseReg);
+ trans.DoEncodedRegIdx(pVar->loc.vlStkReg.vlsrReg);
+ break;
+
+ case ICorDebugInfo::VLT_STK2:
+ trans.DoEncodedRegIdx(pVar->loc.vlStk2.vls2BaseReg);
+ trans.DoEncodedStackOffset(pVar->loc.vlStk2.vls2Offset);
+ break;
+
+ case ICorDebugInfo::VLT_FPSTK:
+ trans.DoEncodedUnsigned(pVar->loc.vlFPstk.vlfReg);
+ break;
+
+ case ICorDebugInfo::VLT_FIXED_VA:
+ trans.DoEncodedUnsigned(pVar->loc.vlFixedVarArg.vlfvOffset);
+ break;
+
+ default:
+ _ASSERTE(!"Unknown varloc type!");
+ break;
+ }
+
+
+ trans.DoCookie(0xC);
+}
+
+
+#ifndef DACCESS_COMPILE
+
+void CompressDebugInfo::CompressBoundaries(
+ IN ULONG32 cMap,
+ IN ICorDebugInfo::OffsetMapping *pMap,
+ IN OUT NibbleWriter *pWriter
+)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pWriter != NULL);
+ _ASSERTE((pMap == NULL) == (cMap == 0));
+
+ if (cMap != 0)
+ {
+ pWriter->WriteEncodedU32(cMap);
+
+ TransferWriter t(*pWriter);
+ DoBounds(t, cMap, pMap);
+
+ pWriter->Flush();
+ }
+
+#ifdef _DEBUG
+ DWORD cbBlob;
+ PVOID pBlob = pWriter->GetBlob(&cbBlob);
+
+ // Track perf #s for compression...
+ g_CDI_TotalMethods++;
+ g_CDI_bMethodTotalUncompress += sizeof(ICorDebugInfo::OffsetMapping) * cMap;
+ g_CDI_bMethodTotalCompress += (int) cbBlob;
+#endif // _DEBUG
+}
+
+
+void CompressDebugInfo::CompressVars(
+ IN ULONG32 cVars,
+ IN ICorDebugInfo::NativeVarInfo *vars,
+ IN OUT NibbleWriter *pWriter
+)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pWriter != NULL);
+ _ASSERTE((cVars == 0) == (vars == NULL));
+
+ if (cVars != 0)
+ {
+ pWriter->WriteEncodedU32(cVars);
+
+ TransferWriter t(*pWriter);
+ for(ULONG32 i = 0; i < cVars; i ++)
+ {
+ DoNativeVarInfo(t, &vars[i]);
+ }
+
+ pWriter->Flush();
+ }
+
+#ifdef _DEBUG
+ DWORD cbBlob;
+ PVOID pBlob = pWriter->GetBlob(&cbBlob);
+
+ g_CDI_bVarsTotalUncompress += cVars * sizeof(ICorDebugInfo::NativeVarInfo);
+ g_CDI_bVarsTotalCompress += (int) cbBlob;
+#endif
+}
+
+PTR_BYTE CompressDebugInfo::CompressBoundariesAndVars(
+ IN ICorDebugInfo::OffsetMapping * pOffsetMapping,
+ IN ULONG iOffsetMapping,
+ IN ICorDebugInfo::NativeVarInfo * pNativeVarInfo,
+ IN ULONG iNativeVarInfo,
+ IN OUT SBuffer * pDebugInfoBuffer,
+ IN LoaderHeap * pLoaderHeap
+ )
+{
+ CONTRACTL {
+ THROWS; // compression routines throw
+ PRECONDITION((iOffsetMapping == 0) == (pOffsetMapping == NULL));
+ PRECONDITION((iNativeVarInfo == 0) == (pNativeVarInfo == NULL));
+ PRECONDITION((pDebugInfoBuffer != NULL) ^ (pLoaderHeap != NULL));
+ } CONTRACTL_END;
+
+ // Actually do the compression. These will throw on oom.
+ NibbleWriter boundsBuffer;
+ DWORD cbBounds = 0;
+ PVOID pBounds = NULL;
+ if (iOffsetMapping > 0)
+ {
+ CompressDebugInfo::CompressBoundaries(iOffsetMapping, pOffsetMapping, &boundsBuffer);
+ pBounds = boundsBuffer.GetBlob(&cbBounds);
+ }
+
+ NibbleWriter varsBuffer;
+ DWORD cbVars = 0;
+ PVOID pVars = NULL;
+ if (iNativeVarInfo > 0)
+ {
+ CompressDebugInfo::CompressVars(iNativeVarInfo, pNativeVarInfo, &varsBuffer);
+ pVars = varsBuffer.GetBlob(&cbVars);
+ }
+
+ // Now write it all out to the buffer in a compact fashion.
+ NibbleWriter w;
+ w.WriteEncodedU32(cbBounds);
+ w.WriteEncodedU32(cbVars);
+ w.Flush();
+
+ DWORD cbHeader;
+ PVOID pHeader = w.GetBlob(&cbHeader);
+
+ S_UINT32 cbFinalSize = S_UINT32(cbHeader) + S_UINT32(cbBounds) + S_UINT32(cbVars);
+ if (cbFinalSize.IsOverflow())
+ ThrowHR(COR_E_OVERFLOW);
+
+ BYTE *ptrStart = NULL;
+ if (pLoaderHeap != NULL)
+ {
+ ptrStart = (BYTE *)(void *)pLoaderHeap->AllocMem(S_SIZE_T(cbFinalSize.Value()));
+ }
+ else
+ {
+ // Create a conservatively large buffer to hold all the data.
+ ptrStart = pDebugInfoBuffer->OpenRawBuffer(cbFinalSize.Value());
+ }
+ _ASSERTE(ptrStart != NULL); // throws on oom.
+
+ BYTE *ptr = ptrStart;
+
+ memcpy(ptr, pHeader, cbHeader);
+ ptr += cbHeader;
+
+ memcpy(ptr, pBounds, cbBounds);
+ ptr += cbBounds;
+
+ memcpy(ptr, pVars, cbVars);
+ ptr += cbVars;
+
+ if (pLoaderHeap != NULL)
+ {
+ return ptrStart;
+ }
+ else
+ {
+ pDebugInfoBuffer->CloseRawBuffer(cbFinalSize.Value());
+ return NULL;
+ }
+}
+
+#endif // DACCESS_COMPILE
+
+//-----------------------------------------------------------------------------
+// Compression routines
+// DAC only needs to run the uncompression routines.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Uncompression (restore) routines
+//-----------------------------------------------------------------------------
+
+// Uncompress data supplied by Compress functions.
+void CompressDebugInfo::RestoreBoundariesAndVars(
+ IN FP_IDS_NEW fpNew, IN void * pNewData,
+ IN PTR_BYTE pDebugInfo,
+ OUT ULONG32 * pcMap, // number of entries in ppMap
+ OUT ICorDebugInfo::OffsetMapping **ppMap, // pointer to newly allocated array
+ OUT ULONG32 *pcVars,
+ OUT ICorDebugInfo::NativeVarInfo **ppVars
+ )
+{
+ CONTRACTL
+ {
+ THROWS; // reading from nibble stream may throw on invalid data.
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ if (pcMap != NULL) *pcMap = 0;
+ if (ppMap != NULL) *ppMap = NULL;
+ if (pcVars != NULL) *pcVars = 0;
+ if (ppVars != NULL) *ppVars = NULL;
+
+ NibbleReader r(pDebugInfo, 12 /* maximum size of compressed 2 UINT32s */);
+
+ ULONG cbBounds = r.ReadEncodedU32();
+ ULONG cbVars = r.ReadEncodedU32();
+
+ PTR_BYTE addrBounds = pDebugInfo + r.GetNextByteIndex();
+ PTR_BYTE addrVars = addrBounds + cbBounds;
+
+ if ((pcMap != NULL || ppMap != NULL) && (cbBounds != 0))
+ {
+ NibbleReader r(addrBounds, cbBounds);
+ TransferReader t(r);
+
+ UINT32 cNumEntries = r.ReadEncodedU32();
+ _ASSERTE(cNumEntries > 0);
+
+ if (pcMap != NULL)
+ *pcMap = cNumEntries;
+
+ if (ppMap != NULL)
+ {
+ ICorDebugInfo::OffsetMapping * pMap = reinterpret_cast<ICorDebugInfo::OffsetMapping *>
+ (fpNew(pNewData, cNumEntries * sizeof(ICorDebugInfo::OffsetMapping)));
+ if (pMap == NULL)
+ {
+ ThrowOutOfMemory();
+ }
+ *ppMap = pMap;
+
+ // Main decompression routine.
+ DoBounds(t, cNumEntries, pMap);
+ }
+ }
+
+ if ((pcVars != NULL || ppVars != NULL) && (cbVars != 0))
+ {
+ NibbleReader r(addrVars, cbVars);
+ TransferReader t(r);
+
+ UINT32 cNumEntries = r.ReadEncodedU32();
+ _ASSERTE(cNumEntries > 0);
+
+ if (pcVars != NULL)
+ *pcVars = cNumEntries;
+
+ if (ppVars != NULL)
+ {
+ ICorDebugInfo::NativeVarInfo * pVars = reinterpret_cast<ICorDebugInfo::NativeVarInfo *>
+ (fpNew(pNewData, cNumEntries * sizeof(ICorDebugInfo::NativeVarInfo)));
+ if (pVars == NULL)
+ {
+ ThrowOutOfMemory();
+ }
+ *ppVars = pVars;
+
+ for(UINT32 i = 0; i < cNumEntries; i++)
+ {
+ DoNativeVarInfo(t, &pVars[i]);
+ }
+ }
+ }
+}
+
+#ifdef DACCESS_COMPILE
+void CompressDebugInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, PTR_BYTE pDebugInfo)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ NibbleReader r(pDebugInfo, 12 /* maximum size of compressed 2 UINT32s */);
+
+ ULONG cbBounds = r.ReadEncodedU32();
+ ULONG cbVars = r.ReadEncodedU32();
+
+ DacEnumMemoryRegion(dac_cast<TADDR>(pDebugInfo), r.GetNextByteIndex() + cbBounds + cbVars);
+}
+#endif // DACCESS_COMPILE
+
+// Init given a starting address from the start of code.
+void DebugInfoRequest::InitFromStartingAddr(MethodDesc * pMD, PCODE addrCode)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pMD != NULL);
+ _ASSERTE(addrCode != NULL);
+
+ this->m_pMD = pMD;
+ this->m_addrStart = addrCode;
+}
+
+
+//-----------------------------------------------------------------------------
+// Impl for DebugInfoManager's IDebugInfoStore
+//-----------------------------------------------------------------------------
+BOOL DebugInfoManager::GetBoundariesAndVars(
+ const DebugInfoRequest & request,
+ IN FP_IDS_NEW fpNew, IN void * pNewData,
+ OUT ULONG32 * pcMap,
+ OUT ICorDebugInfo::OffsetMapping ** ppMap,
+ OUT ULONG32 * pcVars,
+ OUT ICorDebugInfo::NativeVarInfo ** ppVars)
+{
+ CONTRACTL
+ {
+ THROWS;
+ WRAPPER(GC_TRIGGERS); // depends on fpNew
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ IJitManager* pJitMan = ExecutionManager::FindJitMan(request.GetStartAddress());
+ if (pJitMan == NULL)
+ {
+ return FALSE; // no info available.
+ }
+
+ return pJitMan->GetBoundariesAndVars(request, fpNew, pNewData, pcMap, ppMap, pcVars, ppVars);
+}
+
+#ifdef DACCESS_COMPILE
+void DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ PCODE addrCode = pMD->GetNativeCode();
+ if (addrCode == NULL)
+ {
+ return;
+ }
+
+ IJitManager* pJitMan = ExecutionManager::FindJitMan(addrCode);
+ if (pJitMan == NULL)
+ {
+ return; // no info available.
+ }
+
+ pJitMan->EnumMemoryRegionsForMethodDebugInfo(flags, pMD);
+}
+#endif