diff --git a/src/coreclr/debug/crashreport/inproccrashreporter.cpp b/src/coreclr/debug/crashreport/inproccrashreporter.cpp index 95d62e95a38651..802272c5b67688 100644 --- a/src/coreclr/debug/crashreport/inproccrashreporter.cpp +++ b/src/coreclr/debug/crashreport/inproccrashreporter.cpp @@ -62,10 +62,10 @@ static const char CRASHREPORT_ARCHITECTURE_NAME[] = "unknown"; // (blank between sections) // --- thread 0xTID [(crashed)] --- (BeginConsoleThreadBlock) // managed exception: (0x) (only if EE provided one) -// #NN [] Class.Method + 0xILOFFSET (token=0xTOKEN) (managed frame; WriteFrameToConsole) -// #NN (in ) Class.Method + 0xILOFFSET (token=0xTOKEN) (overflow form: module didn't fit the table) -// #NN [] 0xIP (module + 0xOFFSET) (native frame; WriteFrameToConsole) -// #NN 0xIP (module + 0xOFFSET) (native frame not in module table) +// #NN [] 0xTOKEN 0xILOFFSET (managed frame; deferred symbolication) +// #NN [] 0xTOKEN (managed frame; IL offset unavailable) +// #NN (in ) 0xTOKEN 0xILOFFSET (overflow form: module didn't fit the table) +// #NN [] 0xIP (no method token: raw IP fallback) // (no managed frames) | ... +N more frames (EndConsoleThreadBlock) // (blank between threads) // modules: (EndConsoleReport) @@ -504,15 +504,10 @@ class CrashReportHelpers static void WriteFrameToConsole( SignalSafeConsoleWriter* consoleWriter, - char* methodNameBuffer, - size_t methodNameBufferSize, uint32_t frameIndex, int moduleIndex, uint64_t ip, - const char* methodName, - const char* className, const char* fallbackModuleName, - uint32_t nativeOffset, uint32_t token, uint32_t ilOffset); @@ -1445,7 +1440,7 @@ CrashReportHelpers::WriteFrameToJson( if (methodName != nullptr || token != 0) { writer->WriteHexAsString("token", token); - writer->WriteHexAsString("il_offset", ilOffset); + writer->WriteHexAsString("il_offset", ilOffset == CRASHREPORT_NO_IL_OFFSET ? 0u : ilOffset); } if (HasModuleName(moduleName)) { @@ -1482,15 +1477,10 @@ CrashReportHelpers::WriteFrameToJson( void CrashReportHelpers::WriteFrameToConsole( SignalSafeConsoleWriter* consoleWriter, - char* methodNameBuffer, - size_t methodNameBufferSize, uint32_t frameIndex, int moduleIndex, uint64_t ip, - const char* methodName, - const char* className, const char* fallbackModuleName, - uint32_t nativeOffset, uint32_t token, uint32_t ilOffset) { @@ -1507,53 +1497,56 @@ CrashReportHelpers::WriteFrameToConsole( consoleWriter->AppendDecimal(static_cast(frameIndex)); consoleWriter->AppendChar(' '); + // Module reference: index into the trailing "modules:" table (which maps the + // index to a name + MVID). When the module didn't fit the table, fall back to + // the inline "(in )" form so the frame is still attributable. if (moduleIndex >= 0) { consoleWriter->AppendChar('['); consoleWriter->AppendDecimal(static_cast(moduleIndex)); consoleWriter->AppendStr("] "); } - else if ((methodName != nullptr || (token != 0 && HasModuleName(fallbackModuleName))) && HasModuleName(fallbackModuleName)) + else if (HasModuleName(fallbackModuleName)) { consoleWriter->AppendStr("(in "); consoleWriter->AppendStr(GetFilename(fallbackModuleName)); consoleWriter->AppendStr(") "); } - if (methodName != nullptr) - { - const char* fullMethodName = methodName; - if (methodNameBuffer != nullptr && methodNameBufferSize != 0) + if (token != 0) + { + // Deferred symbolication: emit only the stable keys an offline tool needs + // (module MVID from the trailing table + method token + IL offset) and + // leave the Namespace.Class.Method resolution to that tool. The resolved + // name is still recorded in the JSON report. + // + // The grammar is intentionally terse to conserve logcat bytes: after the + // module reference, the first "0x" hex is always the MethodDef token and + // the second "0x" hex, when present, is the IL offset. An offline tool + // validates the first value against the module's MethodDef table (method + // tokens live in the 0x06xxxxxx range) to tell it apart from the raw-IP + // fallback form below. + // + // The JIT instruction pointer and the native code offset are intentionally + // omitted: both are process-dynamic addresses absent from any PDB, so + // neither aids off-device symbolication. When the IL offset cannot be + // resolved the line simply stops after the token. + consoleWriter->AppendStr("0x"); + consoleWriter->AppendHex(static_cast(token)); + if (ilOffset != CRASHREPORT_NO_IL_OFFSET) { - BuildMethodName(methodNameBuffer, methodNameBufferSize, className, methodName); - fullMethodName = methodNameBuffer; + consoleWriter->AppendStr(" 0x"); + consoleWriter->AppendHex(static_cast(ilOffset)); } - consoleWriter->AppendStr(fullMethodName); - consoleWriter->AppendStr(" + 0x"); - consoleWriter->AppendHex(static_cast(ilOffset)); - consoleWriter->AppendStr(" (token=0x"); - consoleWriter->AppendHex(static_cast(token)); - consoleWriter->AppendChar(')'); - } - else if (token != 0 && HasModuleName(fallbackModuleName)) - { - consoleWriter->AppendStr("token=0x"); - consoleWriter->AppendHex(static_cast(token)); - consoleWriter->AppendStr(" + 0x"); - consoleWriter->AppendHex(static_cast(ilOffset)); } else { + // No managed method token (e.g. certain dynamic methods / stubs): there is + // nothing to defer-symbolicate, so fall back to the raw instruction + // pointer to keep the frame locatable in-process. The native offset is + // dropped for the same reason as above: it is useless off-device. consoleWriter->AppendStr("0x"); consoleWriter->AppendHex(ip); - if (HasModuleName(fallbackModuleName)) - { - consoleWriter->AppendStr(" ("); - consoleWriter->AppendStr(GetFilename(fallbackModuleName)); - consoleWriter->AppendStr(" + 0x"); - consoleWriter->AppendHex(static_cast(nativeOffset)); - consoleWriter->AppendChar(')'); - } } consoleWriter->EndLine(); } @@ -1814,10 +1807,8 @@ CrashReportHelpers::WriteFrameToReport( } } WriteFrameToConsole(consoleWriter, - methodNameBuffer, - methodNameBufferSize, - frameIndex, moduleIndex, ip, methodName, className, moduleName, - nativeOffset, token, ilOffset); + frameIndex, moduleIndex, ip, moduleName, + token, ilOffset); } else if (currentThreadDroppedCount != nullptr) { diff --git a/src/coreclr/debug/crashreport/inproccrashreporter.h b/src/coreclr/debug/crashreport/inproccrashreporter.h index 71e9148bec8a4d..70cbea065b92a8 100644 --- a/src/coreclr/debug/crashreport/inproccrashreporter.h +++ b/src/coreclr/debug/crashreport/inproccrashreporter.h @@ -22,6 +22,12 @@ static constexpr size_t CRASHREPORT_PATH_BUFFER_SIZE = 1024; static constexpr size_t CRASHREPORT_STRING_BUFFER_SIZE = 256; +// IL-offset sentinel: ilOffset starts here and is overwritten only on a +// successful native->IL mapping, so a real IL offset of 0 stays distinct from +// "unavailable". Matches ICorDebugInfo::NO_MAPPING (0xffffffff is not a valid +// IL offset). +static constexpr uint32_t CRASHREPORT_NO_IL_OFFSET = 0xFFFFFFFFu; + #if defined(__ANDROID__) static const char CRASHREPORT_LOG_TAG[] = "DOTNET_CRASH"; #endif diff --git a/src/coreclr/vm/crashreportstackwalker.cpp b/src/coreclr/vm/crashreportstackwalker.cpp index 1a259cb2a23cec..1207903c5351b1 100644 --- a/src/coreclr/vm/crashreportstackwalker.cpp +++ b/src/coreclr/vm/crashreportstackwalker.cpp @@ -253,7 +253,7 @@ FrameCallbackAdapter( Module* pModule = pMD->GetModule(); uint32_t nativeOffset = pCF->HasFaulted() ? 0 : pCF->GetRelOffset(); - uint32_t ilOffset = 0; + uint32_t ilOffset = CRASHREPORT_NO_IL_OFFSET; PCODE ip = (PCODE)0; TADDR stackPointer = (TADDR)0; PREGDISPLAY pRD = pCF->GetRegisterSet(); @@ -282,8 +282,8 @@ FrameCallbackAdapter( } EX_CATCH { - // Best-effort: if IL-offset resolution throws, leave ilOffset = 0 - // and continue with the native frame metadata we already have. + // Best-effort: if IL-offset resolution throws, leave ilOffset at the + // sentinel and continue with the native frame metadata we already have. } EX_END_CATCH if (haveILOffset)