Skip to content

Commit 527f1d1

Browse files
authored
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.
1 parent 641ef68 commit 527f1d1

39 files changed

Lines changed: 1065 additions & 296 deletions

src/coreclr/inc/clrconfigvalues.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_MiniMdBufferCapacity, W("MiniMdBufferCapacity"
233233
#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
234234

235235
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")
236+
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RichDebugInfo, W("RichDebugInfo"), 0, "If non-zero store some additional debug information for each jitted method")
236237

237238
///
238239
/// Diagnostics (internal general-purpose)

src/coreclr/inc/cordebuginfo.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,4 +369,31 @@ class ICorDebugInfo
369369
uint32_t varNumber;
370370
VarLoc loc;
371371
};
372+
373+
// Represents an individual entry in the inline tree.
374+
// This is ordinarily stored as a flat array in which [0] is the root, and
375+
// the indices below indicate the tree structure.
376+
struct InlineTreeNode
377+
{
378+
// Method handle of inlinee (or root)
379+
CORINFO_METHOD_HANDLE Method;
380+
// IL offset of IL instruction resulting in the inline
381+
uint32_t ILOffset;
382+
// Index of child in tree, 0 if no children
383+
uint32_t Child;
384+
// Index of sibling in tree, 0 if no sibling
385+
uint32_t Sibling;
386+
};
387+
388+
struct RichOffsetMapping
389+
{
390+
// Offset in emitted code
391+
uint32_t NativeOffset;
392+
// Index of inline tree node containing the IL offset (0 for root)
393+
uint32_t Inlinee;
394+
// IL offset of IL instruction in inlinee that this mapping was created from
395+
uint32_t ILOffset;
396+
// Source information about the IL instruction in the inlinee
397+
SourceTypes Source;
398+
};
372399
};

src/coreclr/inc/corinfo.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2734,6 +2734,16 @@ class ICorStaticInfo
27342734
// jit allocated with allocateArray, EE frees
27352735
) = 0;
27362736

2737+
// Report inline tree and rich offset mappings to EE.
2738+
// The arrays are expected to be allocated with allocateArray
2739+
// and ownership is transferred to the EE with this call.
2740+
virtual void reportRichMappings(
2741+
ICorDebugInfo::InlineTreeNode* inlineTreeNodes, // [IN] Nodes of the inline tree
2742+
uint32_t numInlineTreeNodes, // [IN] Number of nodes in the inline tree
2743+
ICorDebugInfo::RichOffsetMapping* mappings, // [IN] Rich mappings
2744+
uint32_t numMappings // [IN] Number of rich mappings
2745+
) = 0;
2746+
27372747
/*-------------------------- Misc ---------------------------------------*/
27382748

27392749
// Used to allocate memory that needs to handed to the EE.

src/coreclr/inc/eventtracebase.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,7 @@ namespace ETW
769769
MethodDCEndILToNativeMap= 0x00020000,
770770
JitMethodILToNativeMap= 0x00040000,
771771
TypeUnload= 0x00080000,
772+
JittedMethodRichDebugInfo= 0x00100000,
772773

773774
// Helpers
774775
ModuleRangeEnabledAny = ModuleRangeLoad | ModuleRangeDCStart | ModuleRangeDCEnd | ModuleRangeLoadPrivate,
@@ -897,13 +898,14 @@ namespace ETW
897898
BOOL fUnloadOrDCEnd,
898899
BOOL fSendMethodEvent,
899900
BOOL fSendILToNativeMapEvent,
901+
BOOL fSendRichDebugInfoEvent,
900902
BOOL fGetCodeIds);
901903
static VOID SendEventsForNgenMethods(Module *pModule, DWORD dwEventOptions);
902904
static VOID SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL);
903905
static VOID SendMethodILToNativeMapEvent(MethodDesc * pMethodDesc, DWORD dwEventOptions, PCODE pNativeCodeStartAddress, DWORD nativeCodeId, ReJITID ilCodeId);
906+
static VOID SendMethodRichDebugInfo(MethodDesc * pMethodDesc, PCODE pNativeCodeStartAddress, DWORD nativeCodeId, ReJITID ilCodeId);
904907
static VOID SendMethodEvent(MethodDesc *pMethodDesc, DWORD dwEventOptions, BOOL bIsJit, SString *namespaceOrClassName=NULL, SString *methodName=NULL, SString *methodSignature=NULL, PCODE pNativeCodeStartAddress = 0, PrepareCodeConfig *pConfig = NULL);
905908
static VOID SendHelperEvent(ULONGLONG ullHelperStartAddress, ULONG ulHelperSize, LPCWSTR pHelperName);
906-
static VOID SendMethodDetailsEvent(MethodDesc *pMethodDesc);
907909
public:
908910
typedef union _MethodStructs
909911
{
@@ -934,6 +936,7 @@ namespace ETW
934936
static VOID GetR2RGetEntryPoint(MethodDesc *pMethodDesc, PCODE pEntryPoint);
935937
static VOID MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature);
936938
static VOID MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, PCODE pNativeCodeStartAddress, PrepareCodeConfig *pConfig);
939+
static VOID SendMethodDetailsEvent(MethodDesc *pMethodDesc);
937940
static VOID StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName);
938941
static VOID StubsInitialized(PVOID *pHelperStartAddress, PVOID *pHelperNames, LONG ulNoOfHelpers);
939942
static VOID MethodRestored(MethodDesc * pMethodDesc);

src/coreclr/inc/icorjitinfoimpl_generated.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,12 @@ void setVars(
410410
uint32_t cVars,
411411
ICorDebugInfo::NativeVarInfo* vars) override;
412412

413+
void reportRichMappings(
414+
ICorDebugInfo::InlineTreeNode* inlineTreeNodes,
415+
uint32_t numInlineTreeNodes,
416+
ICorDebugInfo::RichOffsetMapping* mappings,
417+
uint32_t numMappings) override;
418+
413419
void* allocateArray(
414420
size_t cBytes) override;
415421

src/coreclr/jit/ICorJitInfo_API_names.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ DEF_CLR_API(getBoundaries)
102102
DEF_CLR_API(setBoundaries)
103103
DEF_CLR_API(getVars)
104104
DEF_CLR_API(setVars)
105+
DEF_CLR_API(reportRichMappings)
105106
DEF_CLR_API(allocateArray)
106107
DEF_CLR_API(freeArray)
107108
DEF_CLR_API(getArgNext)

src/coreclr/jit/ICorJitInfo_API_wrapper.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,17 @@ void WrapICorJitInfo::setVars(
967967
API_LEAVE(setVars);
968968
}
969969

970+
void WrapICorJitInfo::reportRichMappings(
971+
ICorDebugInfo::InlineTreeNode* inlineTreeNodes,
972+
uint32_t numInlineTreeNodes,
973+
ICorDebugInfo::RichOffsetMapping* mappings,
974+
uint32_t numMappings)
975+
{
976+
API_ENTER(reportRichMappings);
977+
wrapHnd->reportRichMappings(inlineTreeNodes, numInlineTreeNodes, mappings, numMappings);
978+
API_LEAVE(reportRichMappings);
979+
}
980+
970981
void* WrapICorJitInfo::allocateArray(
971982
size_t cBytes)
972983
{

src/coreclr/jit/codegen.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -674,11 +674,15 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
674674
void genIPmappingAdd(IPmappingDscKind kind, const DebugInfo& di, bool isLabel);
675675
void genIPmappingAddToFront(IPmappingDscKind kind, const DebugInfo& di, bool isLabel);
676676
void genIPmappingGen();
677+
void genAddRichIPMappingHere(const DebugInfo& di);
678+
679+
void genReportRichDebugInfo();
680+
681+
void genRecordRichDebugInfoInlineTree(InlineContext* context, ICorDebugInfo::InlineTreeNode* tree);
677682

678683
#ifdef DEBUG
679-
void genDumpPreciseDebugInfo();
680-
void genDumpPreciseDebugInfoInlineTree(FILE* file, InlineContext* context, bool* first);
681-
void genAddPreciseIPMappingHere(const DebugInfo& di);
684+
void genReportRichDebugInfoToFile();
685+
void genReportRichDebugInfoInlineTreeToFile(FILE* file, InlineContext* context, bool* first);
682686
#endif
683687

684688
void genEnsureCodeEmitted(const DebugInfo& di);

src/coreclr/jit/codegencommon.cpp

Lines changed: 127 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,7 +2085,7 @@ void CodeGen::genEmitUnwindDebugGCandEH()
20852085

20862086
genIPmappingGen();
20872087

2088-
INDEBUG(genDumpPreciseDebugInfo());
2088+
genReportRichDebugInfo();
20892089

20902090
/* Finalize the Local Var info in terms of generated code */
20912091

@@ -7416,12 +7416,7 @@ void CodeGen::genIPmappingGen()
74167416
return;
74177417
}
74187418

7419-
#ifdef DEBUG
7420-
if (verbose)
7421-
{
7422-
printf("*************** In genIPmappingGen()\n");
7423-
}
7424-
#endif
7419+
JITDUMP("*************** In genIPmappingGen()\n");
74257420

74267421
if (compiler->genIPmappings.size() <= 0)
74277422
{
@@ -7560,11 +7555,20 @@ void CodeGen::genIPmappingGen()
75607555
}
75617556

75627557
#ifdef DEBUG
7563-
void CodeGen::genDumpPreciseDebugInfoInlineTree(FILE* file, InlineContext* context, bool* first)
7558+
//------------------------------------------------------------------------
7559+
// genReportRichDebugInfoInlineTreeToFile:
7560+
// Recursively process a context in the inline tree and write information about it to a file.
7561+
//
7562+
// Parameters:
7563+
// file - the file
7564+
// context - the context
7565+
// first - whether this is the first of the siblings being written out
7566+
//
7567+
void CodeGen::genReportRichDebugInfoInlineTreeToFile(FILE* file, InlineContext* context, bool* first)
75647568
{
75657569
if (context->GetSibling() != nullptr)
75667570
{
7567-
genDumpPreciseDebugInfoInlineTree(file, context->GetSibling(), first);
7571+
genReportRichDebugInfoInlineTreeToFile(file, context->GetSibling(), first);
75687572
}
75697573

75707574
if (context->IsSuccess())
@@ -7577,42 +7581,53 @@ void CodeGen::genDumpPreciseDebugInfoInlineTree(FILE* file, InlineContext* conte
75777581
*first = false;
75787582

75797583
fprintf(file, "{\"Ordinal\":%u,", context->GetOrdinal());
7580-
fprintf(file, "\"MethodID\":%lld,", (INT64)context->GetCallee());
7584+
fprintf(file, "\"MethodID\":%lld,", (int64_t)context->GetCallee());
7585+
fprintf(file, "\"ILOffset\":%u,", context->GetLocation().GetOffset());
7586+
fprintf(file, "\"LocationFlags\":%u,", (uint32_t)context->GetLocation().EncodeSourceTypes());
7587+
fprintf(file, "\"ExactILOffset\":%u,", context->GetActualCallOffset());
75817588
const char* className;
75827589
const char* methodName = compiler->eeGetMethodName(context->GetCallee(), &className);
75837590
fprintf(file, "\"MethodName\":\"%s\",", methodName);
75847591
fprintf(file, "\"Inlinees\":[");
75857592
if (context->GetChild() != nullptr)
75867593
{
75877594
bool childFirst = true;
7588-
genDumpPreciseDebugInfoInlineTree(file, context->GetChild(), &childFirst);
7595+
genReportRichDebugInfoInlineTreeToFile(file, context->GetChild(), &childFirst);
75897596
}
75907597
fprintf(file, "]}");
75917598
}
75927599
}
75937600

7594-
void CodeGen::genDumpPreciseDebugInfo()
7601+
//------------------------------------------------------------------------
7602+
// genReportRichDebugInfoToFile:
7603+
// Write rich debug info in JSON format to file specified by environment variable.
7604+
//
7605+
void CodeGen::genReportRichDebugInfoToFile()
75957606
{
7596-
if (JitConfig.JitDumpPreciseDebugInfoFile() == nullptr)
7607+
if (JitConfig.WriteRichDebugInfoFile() == nullptr)
7608+
{
75977609
return;
7610+
}
75987611

75997612
static CritSecObject s_critSect;
76007613
CritSecHolder holder(s_critSect);
76017614

7602-
FILE* file = _wfopen(JitConfig.JitDumpPreciseDebugInfoFile(), W("a"));
7615+
FILE* file = _wfopen(JitConfig.WriteRichDebugInfoFile(), W("a"));
76037616
if (file == nullptr)
7617+
{
76047618
return;
7619+
}
76057620

76067621
// MethodID in ETW events are the method handles.
76077622
fprintf(file, "{\"MethodID\":%lld,", (INT64)compiler->info.compMethodHnd);
76087623
// Print inline tree.
76097624
fprintf(file, "\"InlineTree\":");
76107625

76117626
bool first = true;
7612-
genDumpPreciseDebugInfoInlineTree(file, compiler->compInlineContext, &first);
7627+
genReportRichDebugInfoInlineTreeToFile(file, compiler->compInlineContext, &first);
76137628
fprintf(file, ",\"Mappings\":[");
76147629
first = true;
7615-
for (PreciseIPMapping& mapping : compiler->genPreciseIPmappings)
7630+
for (RichIPMapping& mapping : compiler->genRichIPmappings)
76167631
{
76177632
if (!first)
76187633
{
@@ -7631,14 +7646,106 @@ void CodeGen::genDumpPreciseDebugInfo()
76317646
fclose(file);
76327647
}
76337648

7634-
void CodeGen::genAddPreciseIPMappingHere(const DebugInfo& di)
7649+
#endif
7650+
7651+
//------------------------------------------------------------------------
7652+
// genRecordRichDebugInfoInlineTree:
7653+
// Recursively process a context in the inline tree and record information
7654+
// about it.
7655+
//
7656+
// Parameters:
7657+
// context - the inline context
7658+
// nodes - the array to record into
7659+
//
7660+
void CodeGen::genRecordRichDebugInfoInlineTree(InlineContext* context, ICorDebugInfo::InlineTreeNode* nodes)
7661+
{
7662+
if (context->IsSuccess())
7663+
{
7664+
// We expect 1 + NumInlines unique ordinals
7665+
assert(context->GetOrdinal() <= compiler->m_inlineStrategy->GetInlineCount());
7666+
7667+
ICorDebugInfo::InlineTreeNode* node = &nodes[context->GetOrdinal()];
7668+
node->Method = context->GetCallee();
7669+
node->ILOffset = context->GetActualCallOffset();
7670+
node->Child = context->GetChild() == nullptr ? 0 : context->GetChild()->GetOrdinal();
7671+
node->Sibling = context->GetSibling() == nullptr ? 0 : context->GetSibling()->GetOrdinal();
7672+
}
7673+
7674+
if (context->GetSibling() != nullptr)
7675+
{
7676+
genRecordRichDebugInfoInlineTree(context->GetSibling(), nodes);
7677+
}
7678+
7679+
if (context->GetChild() != nullptr)
7680+
{
7681+
genRecordRichDebugInfoInlineTree(context->GetChild(), nodes);
7682+
}
7683+
}
7684+
7685+
//------------------------------------------------------------------------
7686+
// genReportRichDebugInfo:
7687+
// If enabled, report rich debugging information to file and/or EE.
7688+
//
7689+
void CodeGen::genReportRichDebugInfo()
7690+
{
7691+
INDEBUG(genReportRichDebugInfoToFile());
7692+
7693+
if (JitConfig.RichDebugInfo() == 0)
7694+
{
7695+
return;
7696+
}
7697+
7698+
unsigned numContexts = 1 + compiler->m_inlineStrategy->GetInlineCount();
7699+
unsigned numRichMappings = static_cast<unsigned>(compiler->genRichIPmappings.size());
7700+
7701+
ICorDebugInfo::InlineTreeNode* inlineTree = static_cast<ICorDebugInfo::InlineTreeNode*>(
7702+
compiler->info.compCompHnd->allocateArray(numContexts * sizeof(ICorDebugInfo::InlineTreeNode)));
7703+
ICorDebugInfo::RichOffsetMapping* mappings = static_cast<ICorDebugInfo::RichOffsetMapping*>(
7704+
compiler->info.compCompHnd->allocateArray(numRichMappings * sizeof(ICorDebugInfo::RichOffsetMapping)));
7705+
7706+
memset(inlineTree, 0, numContexts * sizeof(ICorDebugInfo::InlineTreeNode));
7707+
memset(mappings, 0, numRichMappings * sizeof(ICorDebugInfo::RichOffsetMapping));
7708+
7709+
genRecordRichDebugInfoInlineTree(compiler->compInlineContext, inlineTree);
7710+
7711+
#ifdef DEBUG
7712+
for (unsigned i = 0; i < numContexts; i++)
7713+
{
7714+
assert(inlineTree[i].Method != NO_METHOD_HANDLE);
7715+
}
7716+
#endif
7717+
7718+
size_t mappingIndex = 0;
7719+
for (const RichIPMapping& richMapping : compiler->genRichIPmappings)
7720+
{
7721+
ICorDebugInfo::RichOffsetMapping* mapping = &mappings[mappingIndex];
7722+
assert(richMapping.debugInfo.IsValid());
7723+
mapping->NativeOffset = richMapping.nativeLoc.CodeOffset(GetEmitter());
7724+
mapping->Inlinee = richMapping.debugInfo.GetInlineContext()->GetOrdinal();
7725+
mapping->ILOffset = richMapping.debugInfo.GetLocation().GetOffset();
7726+
mapping->Source = richMapping.debugInfo.GetLocation().EncodeSourceTypes();
7727+
7728+
mappingIndex++;
7729+
}
7730+
7731+
compiler->info.compCompHnd->reportRichMappings(inlineTree, numContexts, mappings, numRichMappings);
7732+
}
7733+
7734+
//------------------------------------------------------------------------
7735+
// genAddRichIPMappingHere:
7736+
// Create a rich IP mapping at the current emit location using the specified
7737+
// debug information.
7738+
//
7739+
// Parameters:
7740+
// di - the debug information
7741+
//
7742+
void CodeGen::genAddRichIPMappingHere(const DebugInfo& di)
76357743
{
7636-
PreciseIPMapping mapping;
7744+
RichIPMapping mapping;
76377745
mapping.nativeLoc.CaptureLocation(GetEmitter());
76387746
mapping.debugInfo = di;
7639-
compiler->genPreciseIPmappings.push_back(mapping);
7747+
compiler->genRichIPmappings.push_back(mapping);
76407748
}
7641-
#endif
76427749

76437750
/*============================================================================
76447751
*

src/coreclr/jit/codegenlinear.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -424,11 +424,13 @@ void CodeGen::genCodeForBBlist()
424424
}
425425
}
426426
}
427-
428-
bool addPreciseMappings =
429-
(JitConfig.JitDumpPreciseDebugInfoFile() != nullptr) || (JitConfig.JitDisasmWithDebugInfo() != 0);
430427
#endif // DEBUG
431428

429+
bool addRichMappings = JitConfig.RichDebugInfo() != 0;
430+
431+
INDEBUG(addRichMappings |= JitConfig.JitDisasmWithDebugInfo() != 0);
432+
INDEBUG(addRichMappings |= JitConfig.WriteRichDebugInfoFile() != nullptr);
433+
432434
DebugInfo currentDI;
433435
for (GenTree* node : LIR::AsRange(block))
434436
{
@@ -445,12 +447,12 @@ void CodeGen::genCodeForBBlist()
445447
firstMapping = false;
446448
}
447449

448-
#ifdef DEBUG
449-
if (addPreciseMappings && ilOffset->gtStmtDI.IsValid())
450+
if (addRichMappings && ilOffset->gtStmtDI.IsValid())
450451
{
451-
genAddPreciseIPMappingHere(ilOffset->gtStmtDI);
452+
genAddRichIPMappingHere(ilOffset->gtStmtDI);
452453
}
453454

455+
#ifdef DEBUG
454456
assert(ilOffset->gtStmtLastILoffs <= compiler->info.compILCodeSize ||
455457
ilOffset->gtStmtLastILoffs == BAD_IL_OFFSET);
456458

0 commit comments

Comments
 (0)