From 527f1d1e7a085395ccbf8111066f030627659d10 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 8 Jul 2022 00:54:04 +0200 Subject: [PATCH] Add an ETW event for reporting richer debug information back (#71263) This adds some richer debug information in the form of inline trees and IL<->native mappings with inlinee information, plus an ETW event in the private runtime provider to report it back. A COMPlus variable is used to opt-in to generating and storing this larger debug information. On the JIT side, we now store a few more fields of data for each inline context in release builds: * Its ordinal which is required to be incrementally assigned * The actual IL offset of the IL instruction leading to the creation of the inline context * The method handle of the inlinee On the EE side we store the new debug information together with the normal debug info, reusing the flag byte used for patchpoint information. The hope is that the new richer format of debug info can eventually replace the old one, but this won't happen in .NET 7 as I expect it will take some time to come up with the right format for this data and the right set of fields to expose. In the meantime this environment variable and event/debugging APIs are mainly to be used as an opt-in way to start incrementally prototyping on the tooling side without needing special checked builds of the runtime/JIT. --- src/coreclr/inc/clrconfigvalues.h | 1 + src/coreclr/inc/cordebuginfo.h | 27 ++ src/coreclr/inc/corinfo.h | 10 + src/coreclr/inc/eventtracebase.h | 5 +- src/coreclr/inc/icorjitinfoimpl_generated.h | 6 + src/coreclr/jit/ICorJitInfo_API_names.h | 1 + src/coreclr/jit/ICorJitInfo_API_wrapper.hpp | 11 + src/coreclr/jit/codegen.h | 10 +- src/coreclr/jit/codegencommon.cpp | 147 +++++++++-- src/coreclr/jit/codegenlinear.cpp | 14 +- src/coreclr/jit/compiler.cpp | 4 +- src/coreclr/jit/compiler.h | 15 +- src/coreclr/jit/debuginfo.cpp | 26 ++ src/coreclr/jit/debuginfo.h | 2 + src/coreclr/jit/ee_il_dll.cpp | 6 +- src/coreclr/jit/emit.cpp | 6 +- src/coreclr/jit/fginline.cpp | 2 +- src/coreclr/jit/importer.cpp | 15 +- src/coreclr/jit/inline.cpp | 55 ++-- src/coreclr/jit/inline.h | 47 ++-- src/coreclr/jit/jitconfigvalues.h | 27 +- src/coreclr/jit/morph.cpp | 1 - .../tools/Common/JitInterface/CorInfoBase.cs | 167 ++++++------ .../tools/Common/JitInterface/CorInfoImpl.cs | 6 + .../tools/Common/JitInterface/CorInfoTypes.cs | 24 ++ .../ThunkGenerator/ThunkInput.txt | 3 + .../tools/aot/jitinterface/jitinterface.h | 12 + .../superpmi-shim-collector/icorjitinfo.cpp | 10 + .../superpmi-shim-counter/icorjitinfo.cpp | 10 + .../superpmi-shim-simple/icorjitinfo.cpp | 9 + .../tools/superpmi/superpmi/icorjitinfo.cpp | 12 + src/coreclr/vm/ClrEtwAll.man | 52 +++- src/coreclr/vm/codeman.cpp | 56 ++++ src/coreclr/vm/codeman.h | 34 +++ src/coreclr/vm/debuginfostore.cpp | 245 +++++++++++++----- src/coreclr/vm/debuginfostore.h | 25 +- src/coreclr/vm/eventtrace.cpp | 147 +++++++++-- src/coreclr/vm/jitinterface.cpp | 70 ++++- src/coreclr/vm/jitinterface.h | 41 ++- 39 files changed, 1065 insertions(+), 296 deletions(-) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 7e524b6e40f66..105a6504db4d0 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -233,6 +233,7 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_MiniMdBufferCapacity, W("MiniMdBufferCapacity" #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS CONFIG_DWORD_INFO(INTERNAL_DbgNativeCodeBpBindsAcrossVersions, W("DbgNativeCodeBpBindsAcrossVersions"), 0, "If non-zero causes native breakpoints at offset 0 to bind in all tiered compilation versions of the given method") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RichDebugInfo, W("RichDebugInfo"), 0, "If non-zero store some additional debug information for each jitted method") /// /// Diagnostics (internal general-purpose) diff --git a/src/coreclr/inc/cordebuginfo.h b/src/coreclr/inc/cordebuginfo.h index b3f6cb96bc56c..ee9a142608408 100644 --- a/src/coreclr/inc/cordebuginfo.h +++ b/src/coreclr/inc/cordebuginfo.h @@ -369,4 +369,31 @@ class ICorDebugInfo uint32_t varNumber; VarLoc loc; }; + + // Represents an individual entry in the inline tree. + // This is ordinarily stored as a flat array in which [0] is the root, and + // the indices below indicate the tree structure. + struct InlineTreeNode + { + // Method handle of inlinee (or root) + CORINFO_METHOD_HANDLE Method; + // IL offset of IL instruction resulting in the inline + uint32_t ILOffset; + // Index of child in tree, 0 if no children + uint32_t Child; + // Index of sibling in tree, 0 if no sibling + uint32_t Sibling; + }; + + struct RichOffsetMapping + { + // Offset in emitted code + uint32_t NativeOffset; + // Index of inline tree node containing the IL offset (0 for root) + uint32_t Inlinee; + // IL offset of IL instruction in inlinee that this mapping was created from + uint32_t ILOffset; + // Source information about the IL instruction in the inlinee + SourceTypes Source; + }; }; diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 91c5734c90588..a9188e0bb1c21 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2734,6 +2734,16 @@ class ICorStaticInfo // jit allocated with allocateArray, EE frees ) = 0; + // Report inline tree and rich offset mappings to EE. + // The arrays are expected to be allocated with allocateArray + // and ownership is transferred to the EE with this call. + virtual void reportRichMappings( + ICorDebugInfo::InlineTreeNode* inlineTreeNodes, // [IN] Nodes of the inline tree + uint32_t numInlineTreeNodes, // [IN] Number of nodes in the inline tree + ICorDebugInfo::RichOffsetMapping* mappings, // [IN] Rich mappings + uint32_t numMappings // [IN] Number of rich mappings + ) = 0; + /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/inc/eventtracebase.h b/src/coreclr/inc/eventtracebase.h index 22624808da18a..29dae12400121 100644 --- a/src/coreclr/inc/eventtracebase.h +++ b/src/coreclr/inc/eventtracebase.h @@ -769,6 +769,7 @@ namespace ETW MethodDCEndILToNativeMap= 0x00020000, JitMethodILToNativeMap= 0x00040000, TypeUnload= 0x00080000, + JittedMethodRichDebugInfo= 0x00100000, // Helpers ModuleRangeEnabledAny = ModuleRangeLoad | ModuleRangeDCStart | ModuleRangeDCEnd | ModuleRangeLoadPrivate, @@ -897,13 +898,14 @@ namespace ETW BOOL fUnloadOrDCEnd, BOOL fSendMethodEvent, BOOL fSendILToNativeMapEvent, + BOOL fSendRichDebugInfoEvent, BOOL fGetCodeIds); static VOID SendEventsForNgenMethods(Module *pModule, DWORD dwEventOptions); static VOID SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL); static VOID SendMethodILToNativeMapEvent(MethodDesc * pMethodDesc, DWORD dwEventOptions, PCODE pNativeCodeStartAddress, DWORD nativeCodeId, ReJITID ilCodeId); + static VOID SendMethodRichDebugInfo(MethodDesc * pMethodDesc, PCODE pNativeCodeStartAddress, DWORD nativeCodeId, ReJITID ilCodeId); static VOID SendMethodEvent(MethodDesc *pMethodDesc, DWORD dwEventOptions, BOOL bIsJit, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL, PCODE pNativeCodeStartAddress = 0, PrepareCodeConfig *pConfig = NULL); static VOID SendHelperEvent(ULONGLONG ullHelperStartAddress, ULONG ulHelperSize, LPCWSTR pHelperName); - static VOID SendMethodDetailsEvent(MethodDesc *pMethodDesc); public: typedef union _MethodStructs { @@ -934,6 +936,7 @@ namespace ETW static VOID GetR2RGetEntryPoint(MethodDesc *pMethodDesc, PCODE pEntryPoint); static VOID MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature); static VOID MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, PCODE pNativeCodeStartAddress, PrepareCodeConfig *pConfig); + static VOID SendMethodDetailsEvent(MethodDesc *pMethodDesc); static VOID StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName); static VOID StubsInitialized(PVOID *pHelperStartAddress, PVOID *pHelperNames, LONG ulNoOfHelpers); static VOID MethodRestored(MethodDesc * pMethodDesc); diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 4864b03309694..986a95c51eb22 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -410,6 +410,12 @@ void setVars( uint32_t cVars, ICorDebugInfo::NativeVarInfo* vars) override; +void reportRichMappings( + ICorDebugInfo::InlineTreeNode* inlineTreeNodes, + uint32_t numInlineTreeNodes, + ICorDebugInfo::RichOffsetMapping* mappings, + uint32_t numMappings) override; + void* allocateArray( size_t cBytes) override; diff --git a/src/coreclr/jit/ICorJitInfo_API_names.h b/src/coreclr/jit/ICorJitInfo_API_names.h index 2549835cefbf8..d63a33685fa5f 100644 --- a/src/coreclr/jit/ICorJitInfo_API_names.h +++ b/src/coreclr/jit/ICorJitInfo_API_names.h @@ -102,6 +102,7 @@ DEF_CLR_API(getBoundaries) DEF_CLR_API(setBoundaries) DEF_CLR_API(getVars) DEF_CLR_API(setVars) +DEF_CLR_API(reportRichMappings) DEF_CLR_API(allocateArray) DEF_CLR_API(freeArray) DEF_CLR_API(getArgNext) diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp index f6a7b39b1590a..6490669dc3d04 100644 --- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp +++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp @@ -967,6 +967,17 @@ void WrapICorJitInfo::setVars( API_LEAVE(setVars); } +void WrapICorJitInfo::reportRichMappings( + ICorDebugInfo::InlineTreeNode* inlineTreeNodes, + uint32_t numInlineTreeNodes, + ICorDebugInfo::RichOffsetMapping* mappings, + uint32_t numMappings) +{ + API_ENTER(reportRichMappings); + wrapHnd->reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); + API_LEAVE(reportRichMappings); +} + void* WrapICorJitInfo::allocateArray( size_t cBytes) { diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index c92b12a382b3a..fc9aae292d5e9 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -674,11 +674,15 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool isLabel); void genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, bool isLabel); void genIPmappingGen(); + void genAddRichIPMappingHere(const DebugInfo& di); + + void genReportRichDebugInfo(); + + void genRecordRichDebugInfoInlineTree(InlineContext* context, ICorDebugInfo::InlineTreeNode* tree); #ifdef DEBUG - void genDumpPreciseDebugInfo(); - void genDumpPreciseDebugInfoInlineTree(FILE* file, InlineContext* context, bool* first); - void genAddPreciseIPMappingHere(const DebugInfo& di); + void genReportRichDebugInfoToFile(); + void genReportRichDebugInfoInlineTreeToFile(FILE* file, InlineContext* context, bool* first); #endif void genEnsureCodeEmitted(const DebugInfo& di); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 72f2dd5bbefb8..19d36fa6e3daf 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2085,7 +2085,7 @@ void CodeGen::genEmitUnwindDebugGCandEH() genIPmappingGen(); - INDEBUG(genDumpPreciseDebugInfo()); + genReportRichDebugInfo(); /* Finalize the Local Var info in terms of generated code */ @@ -7416,12 +7416,7 @@ void CodeGen::genIPmappingGen() return; } -#ifdef DEBUG - if (verbose) - { - printf("*************** In genIPmappingGen()\n"); - } -#endif + JITDUMP("*************** In genIPmappingGen()\n"); if (compiler->genIPmappings.size() <= 0) { @@ -7560,11 +7555,20 @@ void CodeGen::genIPmappingGen() } #ifdef DEBUG -void CodeGen::genDumpPreciseDebugInfoInlineTree(FILE* file, InlineContext* context, bool* first) +//------------------------------------------------------------------------ +// genReportRichDebugInfoInlineTreeToFile: +// Recursively process a context in the inline tree and write information about it to a file. +// +// Parameters: +// file - the file +// context - the context +// first - whether this is the first of the siblings being written out +// +void CodeGen::genReportRichDebugInfoInlineTreeToFile(FILE* file, InlineContext* context, bool* first) { if (context->GetSibling() != nullptr) { - genDumpPreciseDebugInfoInlineTree(file, context->GetSibling(), first); + genReportRichDebugInfoInlineTreeToFile(file, context->GetSibling(), first); } if (context->IsSuccess()) @@ -7577,7 +7581,10 @@ void CodeGen::genDumpPreciseDebugInfoInlineTree(FILE* file, InlineContext* conte *first = false; fprintf(file, "{\"Ordinal\":%u,", context->GetOrdinal()); - fprintf(file, "\"MethodID\":%lld,", (INT64)context->GetCallee()); + fprintf(file, "\"MethodID\":%lld,", (int64_t)context->GetCallee()); + fprintf(file, "\"ILOffset\":%u,", context->GetLocation().GetOffset()); + fprintf(file, "\"LocationFlags\":%u,", (uint32_t)context->GetLocation().EncodeSourceTypes()); + fprintf(file, "\"ExactILOffset\":%u,", context->GetActualCallOffset()); const char* className; const char* methodName = compiler->eeGetMethodName(context->GetCallee(), &className); fprintf(file, "\"MethodName\":\"%s\",", methodName); @@ -7585,23 +7592,31 @@ void CodeGen::genDumpPreciseDebugInfoInlineTree(FILE* file, InlineContext* conte if (context->GetChild() != nullptr) { bool childFirst = true; - genDumpPreciseDebugInfoInlineTree(file, context->GetChild(), &childFirst); + genReportRichDebugInfoInlineTreeToFile(file, context->GetChild(), &childFirst); } fprintf(file, "]}"); } } -void CodeGen::genDumpPreciseDebugInfo() +//------------------------------------------------------------------------ +// genReportRichDebugInfoToFile: +// Write rich debug info in JSON format to file specified by environment variable. +// +void CodeGen::genReportRichDebugInfoToFile() { - if (JitConfig.JitDumpPreciseDebugInfoFile() == nullptr) + if (JitConfig.WriteRichDebugInfoFile() == nullptr) + { return; + } static CritSecObject s_critSect; CritSecHolder holder(s_critSect); - FILE* file = _wfopen(JitConfig.JitDumpPreciseDebugInfoFile(), W("a")); + FILE* file = _wfopen(JitConfig.WriteRichDebugInfoFile(), W("a")); if (file == nullptr) + { return; + } // MethodID in ETW events are the method handles. fprintf(file, "{\"MethodID\":%lld,", (INT64)compiler->info.compMethodHnd); @@ -7609,10 +7624,10 @@ void CodeGen::genDumpPreciseDebugInfo() fprintf(file, "\"InlineTree\":"); bool first = true; - genDumpPreciseDebugInfoInlineTree(file, compiler->compInlineContext, &first); + genReportRichDebugInfoInlineTreeToFile(file, compiler->compInlineContext, &first); fprintf(file, ",\"Mappings\":["); first = true; - for (PreciseIPMapping& mapping : compiler->genPreciseIPmappings) + for (RichIPMapping& mapping : compiler->genRichIPmappings) { if (!first) { @@ -7631,14 +7646,106 @@ void CodeGen::genDumpPreciseDebugInfo() fclose(file); } -void CodeGen::genAddPreciseIPMappingHere(const DebugInfo& di) +#endif + +//------------------------------------------------------------------------ +// genRecordRichDebugInfoInlineTree: +// Recursively process a context in the inline tree and record information +// about it. +// +// Parameters: +// context - the inline context +// nodes - the array to record into +// +void CodeGen::genRecordRichDebugInfoInlineTree(InlineContext* context, ICorDebugInfo::InlineTreeNode* nodes) +{ + if (context->IsSuccess()) + { + // We expect 1 + NumInlines unique ordinals + assert(context->GetOrdinal() <= compiler->m_inlineStrategy->GetInlineCount()); + + ICorDebugInfo::InlineTreeNode* node = &nodes[context->GetOrdinal()]; + node->Method = context->GetCallee(); + node->ILOffset = context->GetActualCallOffset(); + node->Child = context->GetChild() == nullptr ? 0 : context->GetChild()->GetOrdinal(); + node->Sibling = context->GetSibling() == nullptr ? 0 : context->GetSibling()->GetOrdinal(); + } + + if (context->GetSibling() != nullptr) + { + genRecordRichDebugInfoInlineTree(context->GetSibling(), nodes); + } + + if (context->GetChild() != nullptr) + { + genRecordRichDebugInfoInlineTree(context->GetChild(), nodes); + } +} + +//------------------------------------------------------------------------ +// genReportRichDebugInfo: +// If enabled, report rich debugging information to file and/or EE. +// +void CodeGen::genReportRichDebugInfo() +{ + INDEBUG(genReportRichDebugInfoToFile()); + + if (JitConfig.RichDebugInfo() == 0) + { + return; + } + + unsigned numContexts = 1 + compiler->m_inlineStrategy->GetInlineCount(); + unsigned numRichMappings = static_cast(compiler->genRichIPmappings.size()); + + ICorDebugInfo::InlineTreeNode* inlineTree = static_cast( + compiler->info.compCompHnd->allocateArray(numContexts * sizeof(ICorDebugInfo::InlineTreeNode))); + ICorDebugInfo::RichOffsetMapping* mappings = static_cast( + compiler->info.compCompHnd->allocateArray(numRichMappings * sizeof(ICorDebugInfo::RichOffsetMapping))); + + memset(inlineTree, 0, numContexts * sizeof(ICorDebugInfo::InlineTreeNode)); + memset(mappings, 0, numRichMappings * sizeof(ICorDebugInfo::RichOffsetMapping)); + + genRecordRichDebugInfoInlineTree(compiler->compInlineContext, inlineTree); + +#ifdef DEBUG + for (unsigned i = 0; i < numContexts; i++) + { + assert(inlineTree[i].Method != NO_METHOD_HANDLE); + } +#endif + + size_t mappingIndex = 0; + for (const RichIPMapping& richMapping : compiler->genRichIPmappings) + { + ICorDebugInfo::RichOffsetMapping* mapping = &mappings[mappingIndex]; + assert(richMapping.debugInfo.IsValid()); + mapping->NativeOffset = richMapping.nativeLoc.CodeOffset(GetEmitter()); + mapping->Inlinee = richMapping.debugInfo.GetInlineContext()->GetOrdinal(); + mapping->ILOffset = richMapping.debugInfo.GetLocation().GetOffset(); + mapping->Source = richMapping.debugInfo.GetLocation().EncodeSourceTypes(); + + mappingIndex++; + } + + compiler->info.compCompHnd->reportRichMappings(inlineTree, numContexts, mappings, numRichMappings); +} + +//------------------------------------------------------------------------ +// genAddRichIPMappingHere: +// Create a rich IP mapping at the current emit location using the specified +// debug information. +// +// Parameters: +// di - the debug information +// +void CodeGen::genAddRichIPMappingHere(const DebugInfo& di) { - PreciseIPMapping mapping; + RichIPMapping mapping; mapping.nativeLoc.CaptureLocation(GetEmitter()); mapping.debugInfo = di; - compiler->genPreciseIPmappings.push_back(mapping); + compiler->genRichIPmappings.push_back(mapping); } -#endif /*============================================================================ * diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 9119e8ef8b3b3..8045344441020 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -424,11 +424,13 @@ void CodeGen::genCodeForBBlist() } } } - - bool addPreciseMappings = - (JitConfig.JitDumpPreciseDebugInfoFile() != nullptr) || (JitConfig.JitDisasmWithDebugInfo() != 0); #endif // DEBUG + bool addRichMappings = JitConfig.RichDebugInfo() != 0; + + INDEBUG(addRichMappings |= JitConfig.JitDisasmWithDebugInfo() != 0); + INDEBUG(addRichMappings |= JitConfig.WriteRichDebugInfoFile() != nullptr); + DebugInfo currentDI; for (GenTree* node : LIR::AsRange(block)) { @@ -445,12 +447,12 @@ void CodeGen::genCodeForBBlist() firstMapping = false; } -#ifdef DEBUG - if (addPreciseMappings && ilOffset->gtStmtDI.IsValid()) + if (addRichMappings && ilOffset->gtStmtDI.IsValid()) { - genAddPreciseIPMappingHere(ilOffset->gtStmtDI); + genAddRichIPMappingHere(ilOffset->gtStmtDI); } +#ifdef DEBUG assert(ilOffset->gtStmtLastILoffs <= compiler->info.compILCodeSize || ilOffset->gtStmtLastILoffs == BAD_IL_OFFSET); diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index b6ef3d8564c48..ca265a12e0502 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -1869,9 +1869,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc, impSpillCliqueSuccMembers = JitExpandArray(getAllocator()); new (&genIPmappings, jitstd::placement_t()) jitstd::list(getAllocator(CMK_DebugInfo)); -#ifdef DEBUG - new (&genPreciseIPmappings, jitstd::placement_t()) jitstd::list(getAllocator(CMK_DebugOnly)); -#endif + new (&genRichIPmappings, jitstd::placement_t()) jitstd::list(getAllocator(CMK_DebugOnly)); lvMemoryPerSsaData = SsaDefArray(); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 2ada8efae2f18..8503b0250157b 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1772,7 +1772,7 @@ struct IPmappingDsc bool ipmdIsLabel; // Can this code be a branch label? }; -struct PreciseIPMapping +struct RichIPMapping { emitLocation nativeLoc; DebugInfo debugInfo; @@ -4196,12 +4196,14 @@ class Compiler void impMarkInlineCandidate(GenTree* call, CORINFO_CONTEXT_HANDLE exactContextHnd, bool exactContextNeedsRuntimeLookup, - CORINFO_CALL_INFO* callInfo); + CORINFO_CALL_INFO* callInfo, + IL_OFFSET ilOffset); void impMarkInlineCandidateHelper(GenTreeCall* call, CORINFO_CONTEXT_HANDLE exactContextHnd, bool exactContextNeedsRuntimeLookup, - CORINFO_CALL_INFO* callInfo); + CORINFO_CALL_INFO* callInfo, + IL_OFFSET ilOffset); bool impTailCallRetTypeCompatible(bool allowWidening, var_types callerRetType, @@ -7809,11 +7811,8 @@ class Compiler // Record the instr offset mapping to the generated code - jitstd::list genIPmappings; - -#ifdef DEBUG - jitstd::list genPreciseIPmappings; -#endif + jitstd::list genIPmappings; + jitstd::list genRichIPmappings; // Managed RetVal - A side hash table meant to record the mapping from a // GT_CALL node to its debug info. This info is used to emit sequence points diff --git a/src/coreclr/jit/debuginfo.cpp b/src/coreclr/jit/debuginfo.cpp index ed5edeb113da3..6b4e25635f2d3 100644 --- a/src/coreclr/jit/debuginfo.cpp +++ b/src/coreclr/jit/debuginfo.cpp @@ -4,6 +4,32 @@ #include "jitpch.h" #include "debuginfo.h" +//------------------------------------------------------------------------ +// EncodeSourceTypes: +// Encode the JIT-EE source type for an ILLocation. +// +// Returns: +// The JIT-EE interface source type. +// +// Remarks: +// We currently encode only calls and stack empty location. +// +ICorDebugInfo::SourceTypes ILLocation::EncodeSourceTypes() const +{ + int source = 0; + if (IsStackEmpty()) + { + source |= ICorDebugInfo::STACK_EMPTY; + } + + if (IsCall()) + { + source |= ICorDebugInfo::CALL_INSTRUCTION; + } + + return static_cast(source); +} + #ifdef DEBUG //------------------------------------------------------------------------ // Dump: Print a textual representation of this ILLocation. diff --git a/src/coreclr/jit/debuginfo.h b/src/coreclr/jit/debuginfo.h index c107c3dc519de..3f628840765dc 100644 --- a/src/coreclr/jit/debuginfo.h +++ b/src/coreclr/jit/debuginfo.h @@ -55,6 +55,8 @@ class ILLocation return !(*this == other); } + ICorDebugInfo::SourceTypes EncodeSourceTypes() const; + #ifdef DEBUG // Dump textual representation of this ILLocation to jitstdout. void Dump() const; diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index d09bffa0a5e9a..0828b79c899de 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -995,13 +995,9 @@ void Compiler::eeSetLIinfo(unsigned which, UNATIVE_OFFSET nativeOffset, IPmappin switch (kind) { - int source; - case IPmappingDscKind::Normal: eeBoundaries[which].ilOffset = loc.GetOffset(); - source = loc.IsStackEmpty() ? ICorDebugInfo::STACK_EMPTY : 0; - source |= loc.IsCall() ? ICorDebugInfo::CALL_INSTRUCTION : 0; - eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)source; + eeBoundaries[which].source = loc.EncodeSourceTypes(); break; case IPmappingDscKind::Prolog: eeBoundaries[which].ilOffset = ICorDebugInfo::PROLOG; diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 399d08856a028..29a0c71060c86 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -6616,8 +6616,8 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, #define DEFAULT_CODE_BUFFER_INIT 0xcc #ifdef DEBUG - *instrCount = 0; - jitstd::list::iterator nextMapping = emitComp->genPreciseIPmappings.begin(); + *instrCount = 0; + jitstd::list::iterator nextMapping = emitComp->genRichIPmappings.begin(); #endif for (insGroup* ig = emitIGlist; ig != nullptr; ig = ig->igNext) { @@ -6792,7 +6792,7 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, (id->idCodeSize() > 0)) { UNATIVE_OFFSET curCodeOffs = emitCurCodeOffs(cp); - while (nextMapping != emitComp->genPreciseIPmappings.end()) + while (nextMapping != emitComp->genRichIPmappings.end()) { UNATIVE_OFFSET mappingOffs = nextMapping->nativeLoc.CodeOffset(this); diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index d59301991aaa6..642a7a83a4e9b 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -857,7 +857,7 @@ Compiler::fgWalkResult Compiler::fgDebugCheckInlineCandidates(GenTree** pTree, f void Compiler::fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* inlineResult, InlineContext** createdContext) { noway_assert(call->gtOper == GT_CALL); - noway_assert((call->gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0); + noway_assert(call->IsInlineCandidate()); noway_assert(opts.OptEnabled(CLFLG_INLINING)); // This is the InlineInfo struct representing a method to be inlined. diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index a7c6df038d3db..a26d30fd3e6fe 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -10412,7 +10412,7 @@ var_types Compiler::impImportCall(OPCODE opcode, #endif // defined(DEBUG) || defined(INLINE_DATA) // Is it an inline candidate? - impMarkInlineCandidate(call, exactContextHnd, exactContextNeedsRuntimeLookup, callInfo); + impMarkInlineCandidate(call, exactContextHnd, exactContextNeedsRuntimeLookup, callInfo, rawILOffset); } // append the call node. @@ -10632,7 +10632,7 @@ var_types Compiler::impImportCall(OPCODE opcode, #endif // defined(DEBUG) || defined(INLINE_DATA) // Is it an inline candidate? - impMarkInlineCandidate(call, exactContextHnd, exactContextNeedsRuntimeLookup, callInfo); + impMarkInlineCandidate(call, exactContextHnd, exactContextNeedsRuntimeLookup, callInfo, rawILOffset); } // Extra checks for tail calls and tail recursion. @@ -21188,6 +21188,7 @@ bool Compiler::impInlineIsGuaranteedThisDerefBeforeAnySideEffects(GenTree* ad // exactContextHnd -- context handle for inlining // exactContextNeedsRuntimeLookup -- true if context required runtime lookup // callInfo -- call info from VM +// ilOffset -- the actual IL offset of the instruction that produced this inline candidate // // Notes: // Mostly a wrapper for impMarkInlineCandidateHelper that also undoes @@ -21197,12 +21198,13 @@ bool Compiler::impInlineIsGuaranteedThisDerefBeforeAnySideEffects(GenTree* ad void Compiler::impMarkInlineCandidate(GenTree* callNode, CORINFO_CONTEXT_HANDLE exactContextHnd, bool exactContextNeedsRuntimeLookup, - CORINFO_CALL_INFO* callInfo) + CORINFO_CALL_INFO* callInfo, + IL_OFFSET ilOffset) { GenTreeCall* call = callNode->AsCall(); // Do the actual evaluation - impMarkInlineCandidateHelper(call, exactContextHnd, exactContextNeedsRuntimeLookup, callInfo); + impMarkInlineCandidateHelper(call, exactContextHnd, exactContextNeedsRuntimeLookup, callInfo, ilOffset); // If this call is an inline candidate or is not a guarded devirtualization // candidate, we're done. @@ -21233,6 +21235,7 @@ void Compiler::impMarkInlineCandidate(GenTree* callNode, // exactContextHnd -- context handle for inlining // exactContextNeedsRuntimeLookup -- true if context required runtime lookup // callInfo -- call info from VM +// ilOffset -- IL offset of instruction creating the inline candidate // // Notes: // If callNode is an inline candidate, this method sets the flag @@ -21247,7 +21250,8 @@ void Compiler::impMarkInlineCandidate(GenTree* callNode, void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call, CORINFO_CONTEXT_HANDLE exactContextHnd, bool exactContextNeedsRuntimeLookup, - CORINFO_CALL_INFO* callInfo) + CORINFO_CALL_INFO* callInfo, + IL_OFFSET ilOffset) { // Let the strategy know there's another call impInlineRoot()->m_inlineStrategy->NoteCall(); @@ -21469,6 +21473,7 @@ void Compiler::impMarkInlineCandidateHelper(GenTreeCall* call, // The new value should not be null. assert(inlineCandidateInfo != nullptr); inlineCandidateInfo->exactContextNeedsRuntimeLookup = exactContextNeedsRuntimeLookup; + inlineCandidateInfo->ilOffset = ilOffset; call->gtInlineCandidateInfo = inlineCandidateInfo; // If we're in an inlinee compiler, and have a return spill temp, and this inline candidate diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp index 7f3c65fde97ff..9ab568357286b 100644 --- a/src/coreclr/jit/inline.cpp +++ b/src/coreclr/jit/inline.cpp @@ -330,20 +330,20 @@ InlineContext::InlineContext(InlineStrategy* strategy) , m_Child(nullptr) , m_Sibling(nullptr) , m_Code(nullptr) + , m_Callee(nullptr) , m_ILSize(0) , m_ImportedILSize(0) + , m_ActualCallOffset(BAD_IL_OFFSET) , m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL) , m_CodeSizeEstimate(0) + , m_Ordinal(0) , m_Success(true) , m_Devirtualized(false) , m_Guarded(false) , m_Unboxed(false) #if defined(DEBUG) || defined(INLINE_DATA) , m_Policy(nullptr) - , m_Callee(nullptr) , m_TreeID(0) - , m_Ordinal(0) - , m_ActualCallOffset(BAD_IL_OFFSET) #endif // defined(DEBUG) || defined(INLINE_DATA) #ifdef DEBUG , m_ILInstsSet(nullptr) @@ -415,16 +415,7 @@ void InlineContext::Dump(bool verbose, unsigned indent) const char* guarded = m_Guarded ? " GUARDED" : ""; const char* unboxed = m_Unboxed ? " UNBOXED" : ""; - IL_OFFSET offs = BAD_IL_OFFSET; - -#if defined(DEBUG) || defined(INLINE_DATA) - offs = m_ActualCallOffset; -#endif - - if (offs == BAD_IL_OFFSET && m_Location.IsValid()) - { - offs = m_Location.GetOffset(); - } + IL_OFFSET offs = m_ActualCallOffset; if (verbose) { @@ -1282,9 +1273,10 @@ InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statemen if (call->IsInlineCandidate()) { - InlineCandidateInfo* info = call->gtInlineCandidateInfo; - context->m_Code = info->methInfo.ILCode; - context->m_ILSize = info->methInfo.ILCodeSize; + InlineCandidateInfo* info = call->gtInlineCandidateInfo; + context->m_Code = info->methInfo.ILCode; + context->m_ILSize = info->methInfo.ILCodeSize; + context->m_ActualCallOffset = info->ilOffset; #ifdef DEBUG // All inline candidates should get their own statements that have @@ -1293,25 +1285,33 @@ InlineContext* InlineStrategy::NewContext(InlineContext* parentContext, Statemen assert(diInlineContext == nullptr || diInlineContext == parentContext); #endif } + else + { +// Should only get here in debug builds/build with inline data +#if defined(DEBUG) || defined(INLINE_DATA) + context->m_ActualCallOffset = call->gtRawILOffset; +#endif + } - // TODO-DEBUGINFO: Currently, to keep the same behavior as before, we use - // the location of the statement containing the call being inlined. This is - // not always the exact IL offset of the call instruction, consider e.g. + // We currently store both the statement location (used when reporting + // only-style mappings) and the actual call offset (used when reporting the + // inline tree for rich debug info). + // These are not always the same, consider e.g. // ldarg.0 // call // which becomes a single statement where the IL location points to the - // ldarg instruction. For SPGO purposes we should consider always storing - // the exact offset of the call instruction which will be more precise. We - // may consider storing the statement itself as well. - context->m_Location = stmt->GetDebugInfo().GetLocation(); + // ldarg instruction. + context->m_Location = stmt->GetDebugInfo().GetLocation(); + + assert(call->gtCallType == CT_USER_FUNC); + context->m_Callee = call->gtCallMethHnd; + context->m_Devirtualized = call->IsDevirtualized(); context->m_Guarded = call->IsGuarded(); context->m_Unboxed = call->IsUnboxed(); #if defined(DEBUG) || defined(INLINE_DATA) - context->m_TreeID = call->gtTreeID; - context->m_Callee = call->gtCallType == CT_INDIRECT ? nullptr : call->gtCallMethHnd; - context->m_ActualCallOffset = call->gtRawILOffset; + context->m_TreeID = call->gtTreeID; #endif return context; @@ -1327,9 +1327,10 @@ void InlineContext::SetSucceeded(const InlineInfo* info) #if defined(DEBUG) || defined(INLINE_DATA) m_Policy = info->inlineResult->GetPolicy(); m_CodeSizeEstimate = m_Policy->CodeSizeEstimate(); - m_Ordinal = m_InlineStrategy->m_InlineCount + 1; #endif + m_Ordinal = m_InlineStrategy->m_InlineCount + 1; + m_InlineStrategy->NoteOutcome(this); } diff --git a/src/coreclr/jit/inline.h b/src/coreclr/jit/inline.h index f21a77da4fa14..bd086b77175b4 100644 --- a/src/coreclr/jit/inline.h +++ b/src/coreclr/jit/inline.h @@ -614,6 +614,7 @@ struct InlineCandidateInfo : public GuardedDevirtualizationCandidateInfo unsigned preexistingSpillTemp; unsigned clsAttr; unsigned methAttr; + IL_OFFSET ilOffset; // actual IL offset of instruction that resulted in this inline candidate CorInfoInitClassResult initClassResult; var_types fncRetType; bool exactContextNeedsRuntimeLookup; @@ -742,6 +743,12 @@ class InlineContext // Dump full subtree in xml format void DumpXml(FILE* file = stderr, unsigned indent = 0); +#endif // defined(DEBUG) || defined(INLINE_DATA) + + IL_OFFSET GetActualCallOffset() + { + return m_ActualCallOffset; + } // Get callee handle CORINFO_METHOD_HANDLE GetCallee() const @@ -754,8 +761,6 @@ class InlineContext return m_Ordinal; } -#endif // defined(DEBUG) || defined(INLINE_DATA) - // Get the parent context for this context. InlineContext* GetParent() const { @@ -854,28 +859,28 @@ class InlineContext private: InlineContext(InlineStrategy* strategy); - InlineStrategy* m_InlineStrategy; // overall strategy - InlineContext* m_Parent; // logical caller (parent) - InlineContext* m_Child; // first child - InlineContext* m_Sibling; // next child of the parent - const BYTE* m_Code; // address of IL buffer for the method - unsigned m_ILSize; // size of IL buffer for the method - unsigned m_ImportedILSize; // estimated size of imported IL - ILLocation m_Location; // inlining statement location within parent - InlineObservation m_Observation; // what lead to this inline success or failure - int m_CodeSizeEstimate; // in bytes * 10 - bool m_Success : 1; // true if this was a successful inline - bool m_Devirtualized : 1; // true if this was a devirtualized call - bool m_Guarded : 1; // true if this was a guarded call - bool m_Unboxed : 1; // true if this call now invokes the unboxed entry + InlineStrategy* m_InlineStrategy; // overall strategy + InlineContext* m_Parent; // logical caller (parent) + InlineContext* m_Child; // first child + InlineContext* m_Sibling; // next child of the parent + const BYTE* m_Code; // address of IL buffer for the method + CORINFO_METHOD_HANDLE m_Callee; // handle to the method + unsigned m_ILSize; // size of IL buffer for the method + unsigned m_ImportedILSize; // estimated size of imported IL + ILLocation m_Location; // inlining statement location within parent + IL_OFFSET m_ActualCallOffset; // IL offset of actual call instruction leading to the inline + InlineObservation m_Observation; // what lead to this inline success or failure + int m_CodeSizeEstimate; // in bytes * 10 + unsigned m_Ordinal; // Ordinal number of this inline + bool m_Success : 1; // true if this was a successful inline + bool m_Devirtualized : 1; // true if this was a devirtualized call + bool m_Guarded : 1; // true if this was a guarded call + bool m_Unboxed : 1; // true if this call now invokes the unboxed entry #if defined(DEBUG) || defined(INLINE_DATA) - InlinePolicy* m_Policy; // policy that evaluated this inline - CORINFO_METHOD_HANDLE m_Callee; // handle to the method - unsigned m_TreeID; // ID of the GenTreeCall in the parent - unsigned m_Ordinal; // Ordinal number of this inline - IL_OFFSET m_ActualCallOffset; // IL offset of actual call instruction leading to the inline + InlinePolicy* m_Policy; // policy that evaluated this inline + unsigned m_TreeID; // ID of the GenTreeCall in the parent #endif // defined(DEBUG) || defined(INLINE_DATA) diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 94d2fe167848b..893cdbb51831f 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -187,14 +187,16 @@ CONFIG_INTEGER(TreesBeforeAfterMorph, W("JitDumpBeforeAfterMorph"), 0) // If 1, CONFIG_METHODSET(JitBreak, W("JitBreak")) // Stops in the importer when compiling a specified method CONFIG_METHODSET(JitDebugBreak, W("JitDebugBreak")) -CONFIG_METHODSET(JitDisasm, W("JitDisasm")) // Dumps disassembly for specified method -CONFIG_STRING(JitDisasmAssemblies, W("JitDisasmAssemblies")) // Only show JitDisasm and related info for methods - // from this semicolon-delimited list of assemblies. -CONFIG_INTEGER(JitDisasmWithGC, W("JitDisasmWithGC"), 0) // Dump interleaved GC Info for any method disassembled. -CONFIG_METHODSET(JitDump, W("JitDump")) // Dumps trees for specified method -CONFIG_INTEGER(JitDumpTier0, W("JitDumpTier0"), 1) // Dump tier0 requests -CONFIG_INTEGER(JitDumpAtOSROffset, W("JitDumpAtOSROffset"), -1) // Only dump OSR requests for this offset -CONFIG_INTEGER(JitDumpInlinePhases, W("JitDumpInlinePhases"), 1) // Dump inline compiler phases +CONFIG_METHODSET(JitDisasm, W("JitDisasm")) // Dumps disassembly for specified method +CONFIG_STRING(JitDisasmAssemblies, W("JitDisasmAssemblies")) // Only show JitDisasm and related info for methods + // from this semicolon-delimited list of assemblies. +CONFIG_INTEGER(JitDisasmWithGC, W("JitDisasmWithGC"), 0) // Dump interleaved GC Info for any method disassembled. +CONFIG_INTEGER(JitDisasmWithDebugInfo, W("JitDisasmWithDebugInfo"), 0) // Dump interleaved debug info for any method + // disassembled. +CONFIG_METHODSET(JitDump, W("JitDump")) // Dumps trees for specified method +CONFIG_INTEGER(JitDumpTier0, W("JitDumpTier0"), 1) // Dump tier0 requests +CONFIG_INTEGER(JitDumpAtOSROffset, W("JitDumpAtOSROffset"), -1) // Only dump OSR requests for this offset +CONFIG_INTEGER(JitDumpInlinePhases, W("JitDumpInlinePhases"), 1) // Dump inline compiler phases CONFIG_METHODSET(JitEHDump, W("JitEHDump")) // Dump the EH table for the method, as reported to the VM CONFIG_METHODSET(JitExclude, W("JitExclude")) CONFIG_INTEGER(JitFakeProcedureSplitting, W("JitFakeProcedureSplitting"), 0) // Do code splitting independent of VM. @@ -248,9 +250,6 @@ CONFIG_INTEGER(JitDumpFgLoopFlags, W("JitDumpFgLoopFlags"), 0) // 0 == don't d CONFIG_INTEGER(JitDumpFgBlockOrder, W("JitDumpFgBlockOrder"), 0) // 0 == bbNext order; 1 == bbNum order; 2 == bbID // order -CONFIG_STRING(JitDumpPreciseDebugInfoFile, W("JitDumpPreciseDebugInfoFile")) -CONFIG_INTEGER(JitDisasmWithDebugInfo, W("JitDisasmWithDebugInfo"), 0) - CONFIG_STRING(JitLateDisasmTo, W("JITLateDisasmTo")) CONFIG_STRING(JitRange, W("JitRange")) CONFIG_STRING(JitStressModeNames, W("JitStressModeNames")) // Internal Jit stress mode: stress using the given set of @@ -273,6 +272,12 @@ CONFIG_INTEGER(EnableIncompleteISAClass, W("EnableIncompleteISAClass"), 0) // En #endif // defined(DEBUG) +CONFIG_INTEGER(RichDebugInfo, W("RichDebugInfo"), 0) // If 1, keep rich debug info and report it back to the EE + +#ifdef DEBUG +CONFIG_STRING(WriteRichDebugInfoFile, W("WriteRichDebugInfoFile")) // Write rich debug info in JSON format to this file +#endif + CONFIG_INTEGER(JitEarlyExpandMDArrays, W("JitEarlyExpandMDArrays"), 1) // Enable early expansion of multi-dimensional // array access diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 20dcd09b6eadb..658b2390d0b74 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -5481,7 +5481,6 @@ void Compiler::fgMorphCallInline(GenTreeCall* call, InlineResult* inlineResult) // If we failed to inline, we have a bit of work to do to cleanup if (inlineResult->IsFailure()) { - if (createdContext != nullptr) { // We created a context before we got to the failure, so mark diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index c618b88859585..036a2497e9953 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -1460,6 +1460,20 @@ static void _setVars(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRU } } + [UnmanagedCallersOnly] + static void _reportRichMappings(IntPtr thisHandle, IntPtr* ppException, InlineTreeNode* inlineTreeNodes, uint numInlineTreeNodes, RichOffsetMapping* mappings, uint numMappings) + { + var _this = GetThis(thisHandle); + try + { + _this.reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + } + } + [UnmanagedCallersOnly] static void* _allocateArray(IntPtr thisHandle, IntPtr* ppException, UIntPtr cBytes) { @@ -2566,7 +2580,7 @@ static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_FLAGS* f static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 173); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 174); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_getMethodAttribs; @@ -2666,81 +2680,82 @@ static IntPtr GetUnmanagedCallbacks() callbacks[95] = (delegate* unmanaged)&_setBoundaries; callbacks[96] = (delegate* unmanaged)&_getVars; callbacks[97] = (delegate* unmanaged)&_setVars; - callbacks[98] = (delegate* unmanaged)&_allocateArray; - callbacks[99] = (delegate* unmanaged)&_freeArray; - callbacks[100] = (delegate* unmanaged)&_getArgNext; - callbacks[101] = (delegate* unmanaged)&_getArgType; - callbacks[102] = (delegate* unmanaged)&_getArgClass; - callbacks[103] = (delegate* unmanaged)&_getHFAType; - callbacks[104] = (delegate* unmanaged)&_GetErrorHRESULT; - callbacks[105] = (delegate* unmanaged)&_GetErrorMessage; - callbacks[106] = (delegate* unmanaged)&_FilterException; - callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[109] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[110] = (delegate* unmanaged)&_runWithSPMIErrorTrap; - callbacks[111] = (delegate* unmanaged)&_getEEInfo; - callbacks[112] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[113] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[114] = (delegate* unmanaged)&_getMethodName; - callbacks[115] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[116] = (delegate* unmanaged)&_getMethodHash; - callbacks[117] = (delegate* unmanaged)&_findNameOfToken; - callbacks[118] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[119] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[120] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[121] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[122] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[123] = (delegate* unmanaged)&_getHelperFtn; - callbacks[124] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[125] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[126] = (delegate* unmanaged)&_getMethodSync; - callbacks[127] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[128] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[129] = (delegate* unmanaged)&_embedClassHandle; - callbacks[130] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[131] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[132] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[133] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[134] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[135] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[136] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[137] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[138] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[139] = (delegate* unmanaged)&_getCallInfo; - callbacks[140] = (delegate* unmanaged)&_canAccessFamily; - callbacks[141] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[142] = (delegate* unmanaged)&_getClassDomainID; - callbacks[143] = (delegate* unmanaged)&_getFieldAddress; - callbacks[144] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[145] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[146] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[147] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[148] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[149] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[150] = (delegate* unmanaged)&_addActiveDependency; - callbacks[151] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[152] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[153] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[154] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[155] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[156] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[157] = (delegate* unmanaged)&_allocMem; - callbacks[158] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[159] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[160] = (delegate* unmanaged)&_allocGCInfo; - callbacks[161] = (delegate* unmanaged)&_setEHcount; - callbacks[162] = (delegate* unmanaged)&_setEHinfo; - callbacks[163] = (delegate* unmanaged)&_logMsg; - callbacks[164] = (delegate* unmanaged)&_doAssert; - callbacks[165] = (delegate* unmanaged)&_reportFatalError; - callbacks[166] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[167] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[168] = (delegate* unmanaged)&_recordCallSite; - callbacks[169] = (delegate* unmanaged)&_recordRelocation; - callbacks[170] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[171] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[172] = (delegate* unmanaged)&_getJitFlags; + callbacks[98] = (delegate* unmanaged)&_reportRichMappings; + callbacks[99] = (delegate* unmanaged)&_allocateArray; + callbacks[100] = (delegate* unmanaged)&_freeArray; + callbacks[101] = (delegate* unmanaged)&_getArgNext; + callbacks[102] = (delegate* unmanaged)&_getArgType; + callbacks[103] = (delegate* unmanaged)&_getArgClass; + callbacks[104] = (delegate* unmanaged)&_getHFAType; + callbacks[105] = (delegate* unmanaged)&_GetErrorHRESULT; + callbacks[106] = (delegate* unmanaged)&_GetErrorMessage; + callbacks[107] = (delegate* unmanaged)&_FilterException; + callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[109] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[110] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[111] = (delegate* unmanaged)&_runWithSPMIErrorTrap; + callbacks[112] = (delegate* unmanaged)&_getEEInfo; + callbacks[113] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[114] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[115] = (delegate* unmanaged)&_getMethodName; + callbacks[116] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[117] = (delegate* unmanaged)&_getMethodHash; + callbacks[118] = (delegate* unmanaged)&_findNameOfToken; + callbacks[119] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[120] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; + callbacks[121] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[122] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[123] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[124] = (delegate* unmanaged)&_getHelperFtn; + callbacks[125] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[126] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[127] = (delegate* unmanaged)&_getMethodSync; + callbacks[128] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[129] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[130] = (delegate* unmanaged)&_embedClassHandle; + callbacks[131] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[132] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[133] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[134] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[135] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[136] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[137] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[138] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[139] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[140] = (delegate* unmanaged)&_getCallInfo; + callbacks[141] = (delegate* unmanaged)&_canAccessFamily; + callbacks[142] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[143] = (delegate* unmanaged)&_getClassDomainID; + callbacks[144] = (delegate* unmanaged)&_getFieldAddress; + callbacks[145] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[146] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[147] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[148] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[149] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[150] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[151] = (delegate* unmanaged)&_addActiveDependency; + callbacks[152] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[153] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[154] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[155] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[156] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[157] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[158] = (delegate* unmanaged)&_allocMem; + callbacks[159] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[160] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[161] = (delegate* unmanaged)&_allocGCInfo; + callbacks[162] = (delegate* unmanaged)&_setEHcount; + callbacks[163] = (delegate* unmanaged)&_setEHinfo; + callbacks[164] = (delegate* unmanaged)&_logMsg; + callbacks[165] = (delegate* unmanaged)&_doAssert; + callbacks[166] = (delegate* unmanaged)&_reportFatalError; + callbacks[167] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[168] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[169] = (delegate* unmanaged)&_recordCallSite; + callbacks[170] = (delegate* unmanaged)&_recordRelocation; + callbacks[171] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[172] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[173] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 486ef40732481..7837d88536162 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3007,6 +3007,12 @@ private void getVars(CORINFO_METHOD_STRUCT_* ftn, ref uint cVars, ILVarInfo** va extendOthers = true; } + private void reportRichMappings(InlineTreeNode* inlineTree, uint numInlineTree, RichOffsetMapping* mappings, uint numMappings) + { + Marshal.FreeHGlobal((IntPtr)inlineTree); + Marshal.FreeHGlobal((IntPtr)mappings); + } + private void* allocateArray(UIntPtr cBytes) { return (void*)Marshal.AllocHGlobal((IntPtr)(void*)cBytes); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 1f65d5e5f8354..04b36e0f5be90 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1262,6 +1262,30 @@ public struct ILVarInfo public uint varNumber; }; + public unsafe struct InlineTreeNode + { + // Method handle for inlinee (or root) + public CORINFO_METHOD_STRUCT_* Method; + // IL offset of IL instruction resulting in the inline + public uint ILOffset; + // Index of child in tree, 0 if no children + public uint Child; + // Index of sibling in tree, 0 if no sibling + public uint Sibling; + } + + public struct RichOffsetMapping + { + // Offset in emitted code + public uint NativeOffset; + // Index of inline tree node containing the IL offset (0 for root) + public uint Inlinee; + // IL offset of IL instruction in inlinee that this mapping was created from + public uint ILOffset; + // Source information about the IL instruction in the inlinee + public SourceTypes Source; + } + // This enum is used for JIT to tell EE where this token comes from. // E.g. Depending on different opcodes, we might allow/disallow certain types of tokens or // return different types of handles (e.g. boxed vs. regular entrypoints) diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index f1393ad2095a7..2a2d068c6e5c2 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -148,6 +148,8 @@ ICorDebugInfo::OffsetMapping*,OffsetMapping* ICorDebugInfo::ILVarInfo**,ILVarInfo** ICorDebugInfo::NativeVarInfo*,NativeVarInfo* ICorDebugInfo::BoundaryTypes*,BoundaryTypes* +ICorDebugInfo::InlineTreeNode*,InlineTreeNode* +ICorDebugInfo::RichOffsetMapping*,RichOffsetMapping* struct _EXCEPTION_POINTERS*,_EXCEPTION_POINTERS* ICorJitInfo::errorTrapFunction,void* @@ -251,6 +253,7 @@ FUNCTIONS void setBoundaries(CORINFO_METHOD_HANDLE ftn, uint32_t cMap, ICorDebugInfo::OffsetMapping* pMap) void getVars(CORINFO_METHOD_HANDLE ftn, uint32_t* cVars, ICorDebugInfo::ILVarInfo** vars, bool* extendOthers) void setVars(CORINFO_METHOD_HANDLE ftn, uint32_t cVars, ICorDebugInfo::NativeVarInfo* vars) + void reportRichMappings(ICorDebugInfo::InlineTreeNode* inlineTreeNodes, uint32_t numInlineTreeNodes, ICorDebugInfo::RichOffsetMapping* mappings, uint32_t numMappings) void*allocateArray(size_t cBytes); void freeArray(void*array); CORINFO_ARG_LIST_HANDLE getArgNext(CORINFO_ARG_LIST_HANDLE args); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface.h b/src/coreclr/tools/aot/jitinterface/jitinterface.h index bad094900a090..1bade9211b2d3 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface.h @@ -109,6 +109,7 @@ struct JitInterfaceCallbacks void (* setBoundaries)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, uint32_t cMap, ICorDebugInfo::OffsetMapping* pMap); void (* getVars)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, uint32_t* cVars, ICorDebugInfo::ILVarInfo** vars, bool* extendOthers); void (* setVars)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, uint32_t cVars, ICorDebugInfo::NativeVarInfo* vars); + void (* reportRichMappings)(void * thisHandle, CorInfoExceptionClass** ppException, ICorDebugInfo::InlineTreeNode* inlineTreeNodes, uint32_t numInlineTreeNodes, ICorDebugInfo::RichOffsetMapping* mappings, uint32_t numMappings); void* (* allocateArray)(void * thisHandle, CorInfoExceptionClass** ppException, size_t cBytes); void (* freeArray)(void * thisHandle, CorInfoExceptionClass** ppException, void* array); CORINFO_ARG_LIST_HANDLE (* getArgNext)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_ARG_LIST_HANDLE args); @@ -1154,6 +1155,17 @@ class JitInterfaceWrapper : public ICorJitInfo if (pException != nullptr) throw pException; } + virtual void reportRichMappings( + ICorDebugInfo::InlineTreeNode* inlineTreeNodes, + uint32_t numInlineTreeNodes, + ICorDebugInfo::RichOffsetMapping* mappings, + uint32_t numMappings) +{ + CorInfoExceptionClass* pException = nullptr; + _callbacks->reportRichMappings(_thisHandle, &pException, inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); + if (pException != nullptr) throw pException; +} + virtual void* allocateArray( size_t cBytes) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index b939f4cb844fb..9692140086a5f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1167,6 +1167,16 @@ void interceptor_ICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] meth original_ICorJitInfo->setVars(ftn, cVars, vars); } +void interceptor_ICJI::reportRichMappings(ICorDebugInfo::InlineTreeNode* inlineTreeNodes, + uint32_t numInlineTreeNodes, + ICorDebugInfo::RichOffsetMapping* mappings, + uint32_t numMappings) +{ + mc->cr->AddCall("reportRichMappings"); + // TODO: record these mappings + original_ICorJitInfo->reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); +} + /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. // For eg, use this to allocated memory for reporting debug info, diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo.cpp index af768ca0933b1..1f524f561da8e 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -793,6 +793,16 @@ void interceptor_ICJI::setVars( original_ICorJitInfo->setVars(ftn, cVars, vars); } +void interceptor_ICJI::reportRichMappings( + ICorDebugInfo::InlineTreeNode* inlineTreeNodes, + uint32_t numInlineTreeNodes, + ICorDebugInfo::RichOffsetMapping* mappings, + uint32_t numMappings) +{ + mcs->AddCall("reportRichMappings"); + original_ICorJitInfo->reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); +} + void* interceptor_ICJI::allocateArray( size_t cBytes) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo.cpp index 957cadd8c57c3..acd860b915c6c 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -695,6 +695,15 @@ void interceptor_ICJI::setVars( original_ICorJitInfo->setVars(ftn, cVars, vars); } +void interceptor_ICJI::reportRichMappings( + ICorDebugInfo::InlineTreeNode* inlineTreeNodes, + uint32_t numInlineTreeNodes, + ICorDebugInfo::RichOffsetMapping* mappings, + uint32_t numMappings) +{ + original_ICorJitInfo->reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings); +} + void* interceptor_ICJI::allocateArray( size_t cBytes) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index bb6592bb8b39b..82ed2dd35fd71 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1018,6 +1018,18 @@ void MyICJI::setVars(CORINFO_METHOD_HANDLE ftn, // [IN] method of inte freeArray(vars); // See note in recSetVars... we own destroying this array } +void MyICJI::reportRichMappings( + ICorDebugInfo::InlineTreeNode* inlineTreeNodes, + uint32_t numInlineTreeNodes, + ICorDebugInfo::RichOffsetMapping* mappings, + uint32_t numMappings) +{ + jitInstance->mc->cr->AddCall("reportRichMappings"); + // TODO: record these mappings + freeArray(inlineTreeNodes); + freeArray(mappings); +} + /*-------------------------- Misc ---------------------------------------*/ // Used to allocate memory that needs to handed to the EE. diff --git a/src/coreclr/vm/ClrEtwAll.man b/src/coreclr/vm/ClrEtwAll.man index 1cb66be614039..cc2bf3cfa8db6 100644 --- a/src/coreclr/vm/ClrEtwAll.man +++ b/src/coreclr/vm/ClrEtwAll.man @@ -3070,7 +3070,7 @@ - + + +