Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 38 additions & 47 deletions src/coreclr/debug/crashreport/inproccrashreporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ static const char CRASHREPORT_ARCHITECTURE_NAME[] = "unknown";
// (blank between sections)
// --- thread 0xTID [(crashed)] --- (BeginConsoleThreadBlock)
// managed exception: <Type> (0x<HRESULT>) (only if EE provided one)
// #NN [<moduleIndex>] Class.Method + 0xILOFFSET (token=0xTOKEN) (managed frame; WriteFrameToConsole)
// #NN (in <name>) Class.Method + 0xILOFFSET (token=0xTOKEN) (overflow form: module didn't fit the table)
// #NN [<moduleIndex>] 0xIP (module + 0xOFFSET) (native frame; WriteFrameToConsole)
// #NN 0xIP (module + 0xOFFSET) (native frame not in module table)
// #NN [<moduleIndex>] 0xTOKEN 0xILOFFSET (managed frame; deferred symbolication)
// #NN [<moduleIndex>] 0xTOKEN (managed frame; IL offset unavailable)
// #NN (in <name>) 0xTOKEN 0xILOFFSET (overflow form: module didn't fit the table)
// #NN [<moduleIndex>] 0xIP (no method token: raw IP fallback)
Comment on lines +65 to +68
// (no managed frames) | ... +N more frames (EndConsoleThreadBlock)
// (blank between threads)
// modules: (EndConsoleReport)
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -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)
{
Expand All @@ -1507,53 +1497,56 @@ CrashReportHelpers::WriteFrameToConsole(
consoleWriter->AppendDecimal(static_cast<uint64_t>(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 <name>)" form so the frame is still attributable.
if (moduleIndex >= 0)
{
consoleWriter->AppendChar('[');
consoleWriter->AppendDecimal(static_cast<uint64_t>(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)
{
Comment on lines +1516 to +1517
// 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<uint64_t>(token));
if (ilOffset != CRASHREPORT_NO_IL_OFFSET)
{
BuildMethodName(methodNameBuffer, methodNameBufferSize, className, methodName);
fullMethodName = methodNameBuffer;
consoleWriter->AppendStr(" 0x");
consoleWriter->AppendHex(static_cast<uint64_t>(ilOffset));
}
consoleWriter->AppendStr(fullMethodName);
consoleWriter->AppendStr(" + 0x");
consoleWriter->AppendHex(static_cast<uint64_t>(ilOffset));
consoleWriter->AppendStr(" (token=0x");
consoleWriter->AppendHex(static_cast<uint64_t>(token));
consoleWriter->AppendChar(')');
}
else if (token != 0 && HasModuleName(fallbackModuleName))
{
consoleWriter->AppendStr("token=0x");
consoleWriter->AppendHex(static_cast<uint64_t>(token));
consoleWriter->AppendStr(" + 0x");
consoleWriter->AppendHex(static_cast<uint64_t>(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<uint64_t>(nativeOffset));
consoleWriter->AppendChar(')');
}
}
consoleWriter->EndLine();
}
Expand Down Expand Up @@ -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)
{
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/debug/crashreport/inproccrashreporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/vm/crashreportstackwalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down
Loading