From ffb92ccb8586ae13725ba4d3deb9609a775de09a Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Fri, 30 Jan 2026 17:16:56 -0500 Subject: [PATCH 01/18] [Android CoreCLR] Log managed callstacks on native crash --- src/coreclr/pal/src/thread/process.cpp | 11 ++++++++++- src/coreclr/vm/eepolicy.cpp | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 42897270a9456f..f39ef848e0b8d6 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2739,13 +2739,22 @@ static void DoNotOptimize(const void* p) --*/ #ifdef HOST_ANDROID #include +extern "C" void LogCallstackForAndroidNativeCrash() __attribute__((weak)); VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize) { // Preserve context pointer to prevent optimization DoNotOptimize(&context); - // TODO: Dump all managed threads callstacks into logcat and/or file? + if (LogCallstackForAndroidNativeCrash != nullptr) + { + minipal_log_write_fatal("\n=================================================================\n"); + minipal_log_write_fatal("\tManaged Stacktrace:\n"); + minipal_log_write_fatal("=================================================================\n"); + LogCallstackForAndroidNativeCrash(); + minipal_log_write_fatal("=================================================================\n"); + } + // TODO: Dump stress log into logcat and/or file when enabled? minipal_log_write_fatal("Aborting process.\n"); } diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index f839a962e2ebe9..ea8a8440cbdcd1 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -896,3 +896,17 @@ int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR UNREACHABLE(); return -1; } + +#ifdef HOST_ANDROID +// Until Android CoreCLR is able to create dumps, provide a way for PROCCreateCrashDumpIfEnabled to log the managed callstack. +extern "C" void LogCallstackForAndroidNativeCrash() +{ + WRAPPER_NO_CONTRACT; + + Thread *pThread = GetThreadNULLOk(); + if (pThread != NULL) + { + LogCallstackForLogWorker(pThread, NULL); + } +} +#endif // HOST_ANDROID From c1902a7e411e625897b1f5dc8831e57d9306b781 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 10 Feb 2026 12:33:39 -0500 Subject: [PATCH 02/18] Update Android crash report formatting and labeling Remove separator lines and change header from 'Managed Stacktrace' to '.NET runtime crash report' for cleaner logcat output. --- src/coreclr/pal/src/thread/process.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index f39ef848e0b8d6..07b5ed34d84388 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2748,11 +2748,8 @@ PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool if (LogCallstackForAndroidNativeCrash != nullptr) { - minipal_log_write_fatal("\n=================================================================\n"); - minipal_log_write_fatal("\tManaged Stacktrace:\n"); - minipal_log_write_fatal("=================================================================\n"); + minipal_log_write_fatal(".NET runtime crash report\n"); LogCallstackForAndroidNativeCrash(); - minipal_log_write_fatal("=================================================================\n"); } // TODO: Dump stress log into logcat and/or file when enabled? From 821491b59f847fe203d8007e9bcb5d8cc251c7cb Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 10 Feb 2026 12:34:16 -0500 Subject: [PATCH 03/18] Extract crash report logic into PROCCreateCrashReportAndDumpIfEnabled Create a new function PROCCreateCrashReportAndDumpIfEnabled that logs the crash report on Android before calling PROCCreateCrashDumpIfEnabled. Structure: - Android-specific setup (#include, weak symbol) at top - PROCCreateCrashReportAndDumpIfEnabled: one definition with #ifdef inside body - PROCCreateCrashDumpIfEnabled: one comment, then #ifdef/#else for implementations --- src/coreclr/pal/src/include/pal/process.h | 17 +++++++++++++ src/coreclr/pal/src/thread/process.cpp | 31 +++++++++++++++++------ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/coreclr/pal/src/include/pal/process.h b/src/coreclr/pal/src/include/pal/process.h index fde6cce8d88956..6133fc7209091f 100644 --- a/src/coreclr/pal/src/include/pal/process.h +++ b/src/coreclr/pal/src/include/pal/process.h @@ -190,6 +190,23 @@ VOID PROCNotifyProcessShutdown(bool isExecutingOnAltStack = false); --*/ VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize); +/*++ +Function: + PROCCreateCrashReportAndDumpIfEnabled + + Creates crash report and dump of the process if enabled. + Should be called from crash handling code paths. + +Parameters: + signal - POSIX signal number + siginfo - POSIX signal info or nullptr + context - signal context or nullptr + serialize - allow only one thread to generate core dump + +(no return value) +--*/ +VOID PROCCreateCrashReportAndDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize); + #ifdef __cplusplus } #endif // __cplusplus diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 07b5ed34d84388..d1af2bcd802f37 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2722,6 +2722,29 @@ static void DoNotOptimize(const void* p) (void)p; } +#ifdef HOST_ANDROID +#include +extern "C" void LogCallstackForAndroidNativeCrash() __attribute__((weak)); +#endif // HOST_ANDROID + +VOID +PROCCreateCrashReportAndDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize) +{ + // Preserve context pointer to prevent optimization + DoNotOptimize(&context); + +#ifdef HOST_ANDROID + // Android CoreCLR currently does not support CreateDump, so log a crash report until then. + if (LogCallstackForAndroidNativeCrash != nullptr) + { + minipal_log_write_fatal(".NET runtime crash report\n"); + LogCallstackForAndroidNativeCrash(); + } +#endif //HOST_ANDROID + + PROCCreateCrashDumpIfEnabled(signal, siginfo, context, serialize); +} + /*++ Function: PROCCreateCrashDumpIfEnabled @@ -2738,20 +2761,12 @@ static void DoNotOptimize(const void* p) (no return value) --*/ #ifdef HOST_ANDROID -#include -extern "C" void LogCallstackForAndroidNativeCrash() __attribute__((weak)); VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize) { // Preserve context pointer to prevent optimization DoNotOptimize(&context); - if (LogCallstackForAndroidNativeCrash != nullptr) - { - minipal_log_write_fatal(".NET runtime crash report\n"); - LogCallstackForAndroidNativeCrash(); - } - // TODO: Dump stress log into logcat and/or file when enabled? minipal_log_write_fatal("Aborting process.\n"); } From a119af9368b5f32d4e1cb633cd905049679ad632 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 10 Feb 2026 12:34:43 -0500 Subject: [PATCH 04/18] Update call sites to use PROCCreateCrashReportAndDumpIfEnabled Update signal handlers and PROCAbort to call the new PROCCreateCrashReportAndDumpIfEnabled function instead of PROCCreateCrashDumpIfEnabled directly. Call sites updated: - signal.cpp: sigsegv_handler (2 locations), sigterm_handler - process.cpp: PROCAbort --- src/coreclr/pal/src/exception/signal.cpp | 6 +++--- src/coreclr/pal/src/thread/process.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp index b13c286590091e..b92266dd83434a 100644 --- a/src/coreclr/pal/src/exception/signal.cpp +++ b/src/coreclr/pal/src/exception/signal.cpp @@ -433,7 +433,7 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t // Shutdown and create the core dump before we restore the signal to the default handler. PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCCreateCrashDumpIfEnabled(code, siginfo, context, true); + PROCCreateCrashReportAndDumpIfEnabled(code, siginfo, context, true); // Restore the original and restart h/w exception. restore_signal(code, action); @@ -462,7 +462,7 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCCreateCrashDumpIfEnabled(code, siginfo, context, true); + PROCCreateCrashReportAndDumpIfEnabled(code, siginfo, context, true); } /*++ @@ -851,7 +851,7 @@ static void sigterm_handler(int code, siginfo_t *siginfo, void *context) DWORD val = 0; if (enableDumpOnSigTerm.IsSet() && enableDumpOnSigTerm.TryAsInteger(10, val) && val == 1) { - PROCCreateCrashDumpIfEnabled(code, siginfo, context, false); + PROCCreateCrashReportAndDumpIfEnabled(code, siginfo, context, false); } } diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index d1af2bcd802f37..f5a5703a080aa6 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2871,7 +2871,7 @@ PROCAbort(int signal, siginfo_t* siginfo, void* context) // Do any shutdown cleanup before aborting or creating a core dump PROCNotifyProcessShutdown(); - PROCCreateCrashDumpIfEnabled(signal, siginfo, context, true); + PROCCreateCrashReportAndDumpIfEnabled(signal, siginfo, context, true); // Restore all signals; the SIGABORT handler to prevent recursion and // the others to prevent multiple core dumps from being generated. From db11d2fec89cb66333af4c34f1c08ffe1407ed23 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 10 Feb 2026 12:35:36 -0500 Subject: [PATCH 05/18] Add deduplication to prevent duplicate crash reports Add PAL_MarkCrashReportAlreadyLogged() to track when a crash report has already been logged. This prevents duplicate stack traces when the native crash handler fires after managed code has already printed the exception. - pal.h: Add PAL_MarkCrashReportAlreadyLogged declaration (Android only) - process.cpp: Add s_crashReportAlreadyLogged flag and implementation - excep.cpp: Call PAL_MarkCrashReportAlreadyLogged after unhandled exception - eepolicy.cpp: Call PAL_MarkCrashReportAlreadyLogged after FailFast stack trace --- src/coreclr/pal/inc/pal.h | 7 +++++++ src/coreclr/pal/src/thread/process.cpp | 19 ++++++++++++++++++- src/coreclr/vm/eepolicy.cpp | 5 +++++ src/coreclr/vm/excep.cpp | 4 ++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 5e5ea9173ae6de..e69d0f2392ace6 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -262,6 +262,13 @@ PAL_GenerateCoreDump( LPSTR errorMessageBuffer, INT cbErrorMessageBuffer); +#ifdef HOST_ANDROID +PALIMPORT +VOID +PALAPI +PAL_MarkCrashReportAlreadyLogged(); +#endif + PALIMPORT BOOL PALAPI diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index f5a5703a080aa6..14986943c512d3 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2725,6 +2725,23 @@ static void DoNotOptimize(const void* p) #ifdef HOST_ANDROID #include extern "C" void LogCallstackForAndroidNativeCrash() __attribute__((weak)); + +static bool s_crashReportAlreadyLogged = false; + +/*++ +Function: + PAL_MarkCrashReportAlreadyLogged + +Abstract: + Signals that a crash report has already been logged for this crash. + Android currently does not support CreateDump, so a crash report is manually logged. + This prevents duplicate stack traces from being logged (e.g. unhandled exception) +--*/ +VOID +PAL_MarkCrashReportAlreadyLogged() +{ + s_crashReportAlreadyLogged = true; +} #endif // HOST_ANDROID VOID @@ -2735,7 +2752,7 @@ PROCCreateCrashReportAndDumpIfEnabled(int signal, siginfo_t* siginfo, void* cont #ifdef HOST_ANDROID // Android CoreCLR currently does not support CreateDump, so log a crash report until then. - if (LogCallstackForAndroidNativeCrash != nullptr) + if (!s_crashReportAlreadyLogged && LogCallstackForAndroidNativeCrash != nullptr) { minipal_log_write_fatal(".NET runtime crash report\n"); LogCallstackForAndroidNativeCrash(); diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index ea8a8440cbdcd1..28fc8547fe5a8b 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -351,6 +351,11 @@ inline void LogCallstackForLogWorker(Thread* pThread, PEXCEPTION_POINTERS pExcep pThread->StackWalkFrames(&CallStackLogger::LogCallstackForLogCallback, &logger, QUICKUNWIND | FUNCTIONSONLY | ALLOW_ASYNC_STACK_WALK); logger.PrintStackTrace(WordAt.GetUnicode()); + +#ifdef HOST_ANDROID + PAL_MarkCrashReportAlreadyLogged(); +#endif + #ifdef _DEBUG if (g_LogStackOverflowExit) PrintToStdErrA("@Exiting stack trace printing thread.\n"); diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index a324e4d83041e8..5be8a74835015d 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -4383,6 +4383,10 @@ DefaultCatchHandlerExceptionMessageWorker(Thread* pThread, PrintToStdErrW(message.GetUnicode()); PrintToStdErrA("\n"); +#ifdef HOST_ANDROID + PAL_MarkCrashReportAlreadyLogged(); +#endif + #if defined(FEATURE_EVENT_TRACE) && !defined(TARGET_UNIX) // Send the log to Windows Event Log if (sendWindowsEventLog && ShouldLogInEventLog()) From bad4ec57b7a2314804f7d8f323303ff3840fa4c3 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 10 Feb 2026 12:35:47 -0500 Subject: [PATCH 06/18] Add [Native Code] markers for native frame transitions on Android Use NOTIFY_ON_U2M_TRANSITIONS flag to get callbacks when stack walker crosses managed/native boundaries. Store nullptr for native markers and print as [Native Code] in the stack trace. Changes to CallStackLogger: - LogCallstackForLogCallbackWorker: Check IsNativeMarker(), store nullptr - PrintFrame: Check for nullptr, print [Native Code] - PrintStackTrace: Skip nullptr in repetition detection pattern matching - LogCallstackForLogWorker: Add NOTIFY_ON_U2M_TRANSITIONS flag on Android --- src/coreclr/vm/eepolicy.cpp | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index 28fc8547fe5a8b..b52a08cfe3d3b1 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -183,7 +183,13 @@ class CallStackLogger return SWA_ABORT; } +#ifdef HOST_ANDROID + // Android tracks native frames for its crash report in PROCCreateCrashReportAndDumpIfEnabled. + // Indicate them with nullptr MethodDesc. + *itemPtr = pCF->IsNativeMarker() ? nullptr : pCF->GetFunction(); +#else *itemPtr = pCF->GetFunction(); +#endif return SWA_CONTINUE; } @@ -192,9 +198,17 @@ class CallStackLogger { WRAPPER_NO_CONTRACT; - SString str(pWordAt); - MethodDesc* pMD = m_frames[index]; + +#ifdef HOST_ANDROID + if (pMD == nullptr) + { + PrintToStdErrA(" at [Native Code]\n"); + return; + } +#endif + + SString str(pWordAt); TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst|TypeString::FormatSignature); str.Append(W("\n")); @@ -246,6 +260,14 @@ class CallStackLogger largestCommonLength = 0; largestCommonRepeat = 0; +#ifdef HOST_ANDROID + // Android includes nullptr frames to print native markers, don't start repetition search on those frames. + if (m_frames[largestCommonStartOffset] == nullptr) + { + continue; + } +#endif + for (int i = largestCommonStartOffset; i < m_frames.Count(); i++) { MethodDesc* pMD = m_frames[i]; @@ -348,7 +370,12 @@ inline void LogCallstackForLogWorker(Thread* pThread, PEXCEPTION_POINTERS pExcep CallStackLogger logger(pExceptionInfo); - pThread->StackWalkFrames(&CallStackLogger::LogCallstackForLogCallback, &logger, QUICKUNWIND | FUNCTIONSONLY | ALLOW_ASYNC_STACK_WALK); + unsigned int flags = QUICKUNWIND | FUNCTIONSONLY | ALLOW_ASYNC_STACK_WALK; +#ifdef HOST_ANDROID + flags |= NOTIFY_ON_U2M_TRANSITIONS; +#endif + + pThread->StackWalkFrames(&CallStackLogger::LogCallstackForLogCallback, &logger, flags); logger.PrintStackTrace(WordAt.GetUnicode()); From d0d4804157dc99175a34e332d29fa7776767a98c Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 10 Feb 2026 13:32:31 -0500 Subject: [PATCH 07/18] Definition consistency with other PAL APIs --- src/coreclr/pal/src/thread/process.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 14986943c512d3..85d74019571a1d 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2684,6 +2684,7 @@ PROCAbortInitialize() FALSE failed --*/ BOOL +PALAPI PAL_GenerateCoreDump( LPCSTR dumpName, INT dumpType, @@ -2738,6 +2739,7 @@ static bool s_crashReportAlreadyLogged = false; This prevents duplicate stack traces from being logged (e.g. unhandled exception) --*/ VOID +PALAPI PAL_MarkCrashReportAlreadyLogged() { s_crashReportAlreadyLogged = true; From c9d71d44be25ceb0c0f6ae7eb872ab3a96bab16c Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 10 Feb 2026 18:12:52 -0500 Subject: [PATCH 08/18] Addess thread safety and formatting feedback --- src/coreclr/pal/src/thread/process.cpp | 8 +++++--- src/coreclr/vm/eepolicy.cpp | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 85d74019571a1d..eb316681c8d9eb 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2727,7 +2727,7 @@ static void DoNotOptimize(const void* p) #include extern "C" void LogCallstackForAndroidNativeCrash() __attribute__((weak)); -static bool s_crashReportAlreadyLogged = false; +static Volatile s_crashReportAlreadyLogged = FALSE; /*++ Function: @@ -2742,7 +2742,7 @@ VOID PALAPI PAL_MarkCrashReportAlreadyLogged() { - s_crashReportAlreadyLogged = true; + InterlockedExchange(&s_crashReportAlreadyLogged, TRUE); } #endif // HOST_ANDROID @@ -2754,7 +2754,9 @@ PROCCreateCrashReportAndDumpIfEnabled(int signal, siginfo_t* siginfo, void* cont #ifdef HOST_ANDROID // Android CoreCLR currently does not support CreateDump, so log a crash report until then. - if (!s_crashReportAlreadyLogged && LogCallstackForAndroidNativeCrash != nullptr) + // Use atomic exchange to ensure only one thread logs the crash report to avoid interleaved output. + if (InterlockedCompareExchange(&s_crashReportAlreadyLogged, TRUE, FALSE) == FALSE && + LogCallstackForAndroidNativeCrash != nullptr) { minipal_log_write_fatal(".NET runtime crash report\n"); LogCallstackForAndroidNativeCrash(); diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index b52a08cfe3d3b1..0d9b5800cd6201 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -203,7 +203,9 @@ class CallStackLogger #ifdef HOST_ANDROID if (pMD == nullptr) { - PrintToStdErrA(" at [Native Code]\n"); + SString str(pWordAt); + str.Append(W("[Native Code]\n")); + PrintToStdErrW(str.GetUnicode()); return; } #endif From 33a8d38e061a49e0899310580be9552f829ebf60 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Wed, 11 Feb 2026 17:35:06 -0500 Subject: [PATCH 09/18] Refactor Android crash report to use PAL callback pattern - Add PAL_SetLogCallstackForFatalErrorCallback API following existing PAL_Set*Callback patterns - Move crash report state (s_callstackForFatalErrorLogged) to VM - Add EEPolicy::LogCallstackForFatalErrorOnce and CallstackForFatalErrorLogged - Remove PROCCreateCrashReportAndDumpIfEnabled, keep PROCCreateCrashDumpIfEnabled unchanged - Change message from ".NET runtime crash report" to "Fatal error." to match Windows --- src/coreclr/pal/inc/pal.h | 18 +++--- src/coreclr/pal/src/exception/signal.cpp | 9 ++- src/coreclr/pal/src/include/pal/process.h | 14 ++--- src/coreclr/pal/src/thread/process.cpp | 68 +++++++++++------------ src/coreclr/vm/ceemain.cpp | 4 ++ src/coreclr/vm/eepolicy.cpp | 25 ++++++--- src/coreclr/vm/eepolicy.h | 6 ++ src/coreclr/vm/excep.cpp | 2 +- 8 files changed, 84 insertions(+), 62 deletions(-) diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index e69d0f2392ace6..7a06275856d9c6 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -252,6 +252,17 @@ PALAPI PAL_SetCreateDumpCallback( IN PCREATEDUMP_CALLBACK callback); +/// +/// Callback invoked when a fatal error occurs. The callback should log the callstack for the fatal error. +/// +typedef VOID (*PLOGCALLSTACKFORFATALERROR_CALLBACK)(void); + +PALIMPORT +VOID +PALAPI +PAL_SetLogCallstackForFatalErrorCallback( + IN PLOGCALLSTACKFORFATALERROR_CALLBACK callback); + PALIMPORT BOOL PALAPI @@ -262,13 +273,6 @@ PAL_GenerateCoreDump( LPSTR errorMessageBuffer, INT cbErrorMessageBuffer); -#ifdef HOST_ANDROID -PALIMPORT -VOID -PALAPI -PAL_MarkCrashReportAlreadyLogged(); -#endif - PALIMPORT BOOL PALAPI diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp index b92266dd83434a..e1d2e4007d807a 100644 --- a/src/coreclr/pal/src/exception/signal.cpp +++ b/src/coreclr/pal/src/exception/signal.cpp @@ -433,7 +433,8 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t // Shutdown and create the core dump before we restore the signal to the default handler. PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCCreateCrashReportAndDumpIfEnabled(code, siginfo, context, true); + PROCLogCallstackForFatalError(); + PROCCreateCrashDumpIfEnabled(code, siginfo, context, true); // Restore the original and restart h/w exception. restore_signal(code, action); @@ -462,7 +463,8 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCCreateCrashReportAndDumpIfEnabled(code, siginfo, context, true); + PROCLogCallstackForFatalError(); + PROCCreateCrashDumpIfEnabled(code, siginfo, context, true); } /*++ @@ -851,7 +853,8 @@ static void sigterm_handler(int code, siginfo_t *siginfo, void *context) DWORD val = 0; if (enableDumpOnSigTerm.IsSet() && enableDumpOnSigTerm.TryAsInteger(10, val) && val == 1) { - PROCCreateCrashReportAndDumpIfEnabled(code, siginfo, context, false); + PROCLogCallstackForFatalError(); + PROCCreateCrashDumpIfEnabled(code, siginfo, context, false); } } diff --git a/src/coreclr/pal/src/include/pal/process.h b/src/coreclr/pal/src/include/pal/process.h index 6133fc7209091f..ddbac58fcd342f 100644 --- a/src/coreclr/pal/src/include/pal/process.h +++ b/src/coreclr/pal/src/include/pal/process.h @@ -192,20 +192,14 @@ VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, /*++ Function: - PROCCreateCrashReportAndDumpIfEnabled + PROCLogCallstackForFatalError - Creates crash report and dump of the process if enabled. - Should be called from crash handling code paths. - -Parameters: - signal - POSIX signal number - siginfo - POSIX signal info or nullptr - context - signal context or nullptr - serialize - allow only one thread to generate core dump + Invokes the registered callback to log the callstack for a fatal error. + Used by Android since CreateDump is not supported there. (no return value) --*/ -VOID PROCCreateCrashReportAndDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize); +VOID PROCLogCallstackForFatalError(); #ifdef __cplusplus } diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index eb316681c8d9eb..7ae787f2d1b7a4 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -195,6 +195,9 @@ Volatile g_shutdownCallback = nullptr; // Function to call instead of exec'ing the createdump binary. Used by single-file and native AOT hosts. Volatile g_createdumpCallback = nullptr; +// Function to call to log the callstack for a fatal error. Used by Android since CoreCLR doesn't support CreateDump on Android. +Volatile g_logCallstackForFatalErrorCallback = nullptr; + // Crash dump generating program arguments. Initialized in PROCAbortInitialize(). #define MAX_ARGV_ENTRIES 32 const char* g_argvCreateDump[MAX_ARGV_ENTRIES] = { nullptr }; @@ -1358,6 +1361,26 @@ PAL_SetCreateDumpCallback( g_createdumpCallback = callback; } +/*++ +Function: + PAL_SetLogCallstackForFatalErrorCallback + +Abstract: + Sets a callback that is executed when a fatal error (crash) occurs to log the callstack. + Used by Android CoreCLR since CreateDump is not supported on Android. + + NOTE: Currently only one callback can be set at a time. +--*/ +PALIMPORT +VOID +PALAPI +PAL_SetLogCallstackForFatalErrorCallback( + IN PLOGCALLSTACKFORFATALERROR_CALLBACK callback) +{ + _ASSERTE(g_logCallstackForFatalErrorCallback == nullptr); + g_logCallstackForFatalErrorCallback = callback; +} + // Build the semaphore names using the PID and a value that can be used for distinguishing // between processes with the same PID (which ran at different times). This is to avoid // cases where a prior process with the same PID exited abnormally without having a chance @@ -2723,47 +2746,22 @@ static void DoNotOptimize(const void* p) (void)p; } -#ifdef HOST_ANDROID -#include -extern "C" void LogCallstackForAndroidNativeCrash() __attribute__((weak)); - -static Volatile s_crashReportAlreadyLogged = FALSE; - /*++ Function: - PAL_MarkCrashReportAlreadyLogged + PROCLogCallstackForFatalError -Abstract: - Signals that a crash report has already been logged for this crash. - Android currently does not support CreateDump, so a crash report is manually logged. - This prevents duplicate stack traces from being logged (e.g. unhandled exception) ---*/ -VOID -PALAPI -PAL_MarkCrashReportAlreadyLogged() -{ - InterlockedExchange(&s_crashReportAlreadyLogged, TRUE); -} -#endif // HOST_ANDROID + Invokes the registered callback to log the callstack for a fatal error. + Used by Android since CreateDump is not supported there. +(no return value) +--*/ VOID -PROCCreateCrashReportAndDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize) +PROCLogCallstackForFatalError() { - // Preserve context pointer to prevent optimization - DoNotOptimize(&context); - -#ifdef HOST_ANDROID - // Android CoreCLR currently does not support CreateDump, so log a crash report until then. - // Use atomic exchange to ensure only one thread logs the crash report to avoid interleaved output. - if (InterlockedCompareExchange(&s_crashReportAlreadyLogged, TRUE, FALSE) == FALSE && - LogCallstackForAndroidNativeCrash != nullptr) + if (g_logCallstackForFatalErrorCallback != nullptr) { - minipal_log_write_fatal(".NET runtime crash report\n"); - LogCallstackForAndroidNativeCrash(); + g_logCallstackForFatalErrorCallback(); } -#endif //HOST_ANDROID - - PROCCreateCrashDumpIfEnabled(signal, siginfo, context, serialize); } /*++ @@ -2782,6 +2780,7 @@ PROCCreateCrashReportAndDumpIfEnabled(int signal, siginfo_t* siginfo, void* cont (no return value) --*/ #ifdef HOST_ANDROID +#include VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize) { @@ -2892,7 +2891,8 @@ PROCAbort(int signal, siginfo_t* siginfo, void* context) // Do any shutdown cleanup before aborting or creating a core dump PROCNotifyProcessShutdown(); - PROCCreateCrashReportAndDumpIfEnabled(signal, siginfo, context, true); + PROCLogCallstackForFatalError(); + PROCCreateCrashDumpIfEnabled(signal, siginfo, context, true); // Restore all signals; the SIGABORT handler to prevent recursion and // the others to prevent multiple core dumps from being generated. diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index f039940ec375db..54631edf44eb90 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -694,6 +694,10 @@ void EEStartupHelper() PAL_SetShutdownCallback(EESocketCleanupHelper); #endif // TARGET_UNIX +#ifdef HOST_ANDROID + PAL_SetLogCallstackForFatalErrorCallback(EEPolicy::LogCallstackForFatalErrorOnce); +#endif // HOST_ANDROID + #ifdef STRESS_LOG if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLog, g_pConfig->StressLog()) != 0) { unsigned facilities = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFacility, LF_ALL); diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index 0d9b5800cd6201..9a59ef673ff6a1 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -184,7 +184,7 @@ class CallStackLogger } #ifdef HOST_ANDROID - // Android tracks native frames for its crash report in PROCCreateCrashReportAndDumpIfEnabled. + // Android tracks native frames for its crash report in LogCallstackForFatalErrorCallback. // Indicate them with nullptr MethodDesc. *itemPtr = pCF->IsNativeMarker() ? nullptr : pCF->GetFunction(); #else @@ -381,10 +381,6 @@ inline void LogCallstackForLogWorker(Thread* pThread, PEXCEPTION_POINTERS pExcep logger.PrintStackTrace(WordAt.GetUnicode()); -#ifdef HOST_ANDROID - PAL_MarkCrashReportAlreadyLogged(); -#endif - #ifdef _DEBUG if (g_LogStackOverflowExit) PrintToStdErrA("@Exiting stack trace printing thread.\n"); @@ -932,15 +928,30 @@ int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR } #ifdef HOST_ANDROID -// Until Android CoreCLR is able to create dumps, provide a way for PROCCreateCrashDumpIfEnabled to log the managed callstack. -extern "C" void LogCallstackForAndroidNativeCrash() +#include + +static Volatile s_callstackForFatalErrorLogged = FALSE; + +// Logs the callstack for a fatal error. Should only be called once. +void EEPolicy::LogCallstackForFatalErrorOnce() { WRAPPER_NO_CONTRACT; + if (InterlockedCompareExchange(&s_callstackForFatalErrorLogged, TRUE, FALSE) != FALSE) + { + return; + } + + minipal_log_write_fatal("Fatal error.\n"); Thread *pThread = GetThreadNULLOk(); if (pThread != NULL) { LogCallstackForLogWorker(pThread, NULL); } } + +void EEPolicy::CallstackForFatalErrorLogged() +{ + s_callstackForFatalErrorLogged = TRUE; +} #endif // HOST_ANDROID diff --git a/src/coreclr/vm/eepolicy.h b/src/coreclr/vm/eepolicy.h index e8f821c065ab6e..0faceddc28ce3e 100644 --- a/src/coreclr/vm/eepolicy.h +++ b/src/coreclr/vm/eepolicy.h @@ -38,6 +38,12 @@ class EEPolicy static void DECLSPEC_NORETURN HandleFatalStackOverflow(EXCEPTION_POINTERS *pException, BOOL fSkipDebugger = FALSE); +#ifdef HOST_ANDROID + static void LogCallstackForFatalErrorOnce(); + + static void CallstackForFatalErrorLogged(); +#endif + private: static void LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource, LPCWSTR argExceptionString=NULL); }; diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 5be8a74835015d..cc6fc9877dc7f2 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -4384,7 +4384,7 @@ DefaultCatchHandlerExceptionMessageWorker(Thread* pThread, PrintToStdErrA("\n"); #ifdef HOST_ANDROID - PAL_MarkCrashReportAlreadyLogged(); + EEPolicy::CallstackForFatalErrorLogged(); #endif #if defined(FEATURE_EVENT_TRACE) && !defined(TARGET_UNIX) From e97e2c7708563f21bb20586149713646fcffdf41 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Thu, 12 Feb 2026 16:42:56 -0500 Subject: [PATCH 10/18] Synchronize callstack logging with LogInfoForFatalError --- src/coreclr/vm/eepolicy.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index 9a59ef673ff6a1..ea7f509b02b08c 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -928,8 +928,6 @@ int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR } #ifdef HOST_ANDROID -#include - static Volatile s_callstackForFatalErrorLogged = FALSE; // Logs the callstack for a fatal error. Should only be called once. @@ -942,12 +940,7 @@ void EEPolicy::LogCallstackForFatalErrorOnce() return; } - minipal_log_write_fatal("Fatal error.\n"); - Thread *pThread = GetThreadNULLOk(); - if (pThread != NULL) - { - LogCallstackForLogWorker(pThread, NULL); - } + LogInfoForFatalError(0, W("Native crash"), nullptr, nullptr, nullptr); } void EEPolicy::CallstackForFatalErrorLogged() From 058139a91018835cde9be682489c24dfc272e5e5 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Thu, 12 Feb 2026 17:11:39 -0500 Subject: [PATCH 11/18] Extract signal name for error message --- src/coreclr/pal/inc/pal.h | 2 +- src/coreclr/pal/src/exception/signal.cpp | 6 +++--- src/coreclr/pal/src/include/pal/process.h | 5 ++++- src/coreclr/pal/src/thread/process.cpp | 24 ++++++++++++++++++++--- src/coreclr/vm/eepolicy.cpp | 4 ++-- src/coreclr/vm/eepolicy.h | 2 +- 6 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 7a06275856d9c6..3d3319267cf567 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -255,7 +255,7 @@ PAL_SetCreateDumpCallback( /// /// Callback invoked when a fatal error occurs. The callback should log the callstack for the fatal error. /// -typedef VOID (*PLOGCALLSTACKFORFATALERROR_CALLBACK)(void); +typedef VOID (*PLOGCALLSTACKFORFATALERROR_CALLBACK)(LPCWSTR errorMessage); PALIMPORT VOID diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp index e1d2e4007d807a..70af4c40f20d37 100644 --- a/src/coreclr/pal/src/exception/signal.cpp +++ b/src/coreclr/pal/src/exception/signal.cpp @@ -433,7 +433,7 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t // Shutdown and create the core dump before we restore the signal to the default handler. PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCLogCallstackForFatalError(); + PROCLogCallstackForFatalError(code); PROCCreateCrashDumpIfEnabled(code, siginfo, context, true); // Restore the original and restart h/w exception. @@ -463,7 +463,7 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCLogCallstackForFatalError(); + PROCLogCallstackForFatalError(code); PROCCreateCrashDumpIfEnabled(code, siginfo, context, true); } @@ -853,7 +853,7 @@ static void sigterm_handler(int code, siginfo_t *siginfo, void *context) DWORD val = 0; if (enableDumpOnSigTerm.IsSet() && enableDumpOnSigTerm.TryAsInteger(10, val) && val == 1) { - PROCLogCallstackForFatalError(); + PROCLogCallstackForFatalError(code); PROCCreateCrashDumpIfEnabled(code, siginfo, context, false); } } diff --git a/src/coreclr/pal/src/include/pal/process.h b/src/coreclr/pal/src/include/pal/process.h index ddbac58fcd342f..1e93e361599a66 100644 --- a/src/coreclr/pal/src/include/pal/process.h +++ b/src/coreclr/pal/src/include/pal/process.h @@ -197,9 +197,12 @@ VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, Invokes the registered callback to log the callstack for a fatal error. Used by Android since CreateDump is not supported there. +Parameters: + signal - POSIX signal number + (no return value) --*/ -VOID PROCLogCallstackForFatalError(); +VOID PROCLogCallstackForFatalError(int signal); #ifdef __cplusplus } diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 7ae787f2d1b7a4..ea757d81610747 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2746,6 +2746,20 @@ static void DoNotOptimize(const void* p) (void)p; } +static LPCWSTR GetSignalName(int signal) +{ + switch (signal) + { + case SIGSEGV: return W("SIGSEGV"); + case SIGBUS: return W("SIGBUS"); + case SIGFPE: return W("SIGFPE"); + case SIGILL: return W("SIGILL"); + case SIGABRT: return W("SIGABRT"); + case SIGTERM: return W("SIGTERM"); + default: return W("Unknown signal"); + } +} + /*++ Function: PROCLogCallstackForFatalError @@ -2753,14 +2767,18 @@ static void DoNotOptimize(const void* p) Invokes the registered callback to log the callstack for a fatal error. Used by Android since CreateDump is not supported there. +Parameters: + signal - POSIX signal number + (no return value) --*/ VOID -PROCLogCallstackForFatalError() +PROCLogCallstackForFatalError(int signal) { if (g_logCallstackForFatalErrorCallback != nullptr) { - g_logCallstackForFatalErrorCallback(); + LPCWSTR errorMessage = GetSignalName(signal); + g_logCallstackForFatalErrorCallback(errorMessage); } } @@ -2891,7 +2909,7 @@ PROCAbort(int signal, siginfo_t* siginfo, void* context) // Do any shutdown cleanup before aborting or creating a core dump PROCNotifyProcessShutdown(); - PROCLogCallstackForFatalError(); + PROCLogCallstackForFatalError(signal); PROCCreateCrashDumpIfEnabled(signal, siginfo, context, true); // Restore all signals; the SIGABORT handler to prevent recursion and diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index ea7f509b02b08c..c242f0595f2c83 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -931,7 +931,7 @@ int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR static Volatile s_callstackForFatalErrorLogged = FALSE; // Logs the callstack for a fatal error. Should only be called once. -void EEPolicy::LogCallstackForFatalErrorOnce() +void EEPolicy::LogCallstackForFatalErrorOnce(LPCWSTR errorMessage) { WRAPPER_NO_CONTRACT; @@ -940,7 +940,7 @@ void EEPolicy::LogCallstackForFatalErrorOnce() return; } - LogInfoForFatalError(0, W("Native crash"), nullptr, nullptr, nullptr); + LogInfoForFatalError(0, errorMessage, nullptr, nullptr, nullptr); } void EEPolicy::CallstackForFatalErrorLogged() diff --git a/src/coreclr/vm/eepolicy.h b/src/coreclr/vm/eepolicy.h index 0faceddc28ce3e..795df735373fe9 100644 --- a/src/coreclr/vm/eepolicy.h +++ b/src/coreclr/vm/eepolicy.h @@ -39,7 +39,7 @@ class EEPolicy static void DECLSPEC_NORETURN HandleFatalStackOverflow(EXCEPTION_POINTERS *pException, BOOL fSkipDebugger = FALSE); #ifdef HOST_ANDROID - static void LogCallstackForFatalErrorOnce(); + static void LogCallstackForFatalErrorOnce(LPCWSTR errorMessage); static void CallstackForFatalErrorLogged(); #endif From caa89cfb2fe32999612193937b0240bfab8f3572 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Fri, 13 Feb 2026 20:46:50 -0500 Subject: [PATCH 12/18] Remove [Native Code] frame markers from crash stack trace The [Native Code] markers cannot be implemented cleanly: - NOTIFY_ON_INITIAL_NATIVE_CONTEXT corrupts the callback-based StackWalkFrames when combined with FUNCTIONSONLY, producing a frame with a corrupt MethodDesc that causes SIGSEGV in TypeString::AppendMethodInternal. - NOTIFY_ON_U2M_TRANSITIONS detects managed-to-native transitions within the stack but cannot detect whether the top of the stack started in native code (needed for the leading marker). - In signal handler context, the stack walker always begins from native code regardless of whether the crash was in native or managed code, so there is no flag-based way to distinguish. The trailing [Native Code] marker below Main was not useful either. Android's native crash reporter already captures native frames; our crash logger adds the managed frames that the native reporter cannot see. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/vm/eepolicy.cpp | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index 6b4d1261f1afc0..f51757426c7cd1 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -183,13 +183,7 @@ class CallStackLogger return SWA_ABORT; } -#ifdef HOST_ANDROID - // Android tracks native frames for its crash report in LogCallstackForFatalErrorCallback. - // Indicate them with nullptr MethodDesc. - *itemPtr = pCF->IsNativeMarker() ? nullptr : pCF->GetFunction(); -#else *itemPtr = pCF->GetFunction(); -#endif return SWA_CONTINUE; } @@ -200,16 +194,6 @@ class CallStackLogger MethodDesc* pMD = m_frames[index]; -#ifdef HOST_ANDROID - if (pMD == nullptr) - { - SString str(pWordAt); - str.Append(W("[Native Code]\n")); - PrintToStdErrW(str.GetUnicode()); - return; - } -#endif - SString str(pWordAt); TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst|TypeString::FormatSignature); str.Append(W("\n")); @@ -262,14 +246,6 @@ class CallStackLogger largestCommonLength = 0; largestCommonRepeat = 0; -#ifdef HOST_ANDROID - // Android includes nullptr frames to print native markers, don't start repetition search on those frames. - if (m_frames[largestCommonStartOffset] == nullptr) - { - continue; - } -#endif - for (int i = largestCommonStartOffset; i < m_frames.Count(); i++) { MethodDesc* pMD = m_frames[i]; @@ -378,15 +354,9 @@ inline void LogCallstackForLogWorker(Thread* pThread, PEXCEPTION_POINTERS pExcep CallStackLogger logger(pExceptionInfo); - unsigned int flags = QUICKUNWIND | FUNCTIONSONLY | ALLOW_ASYNC_STACK_WALK; -#ifdef HOST_ANDROID - flags |= NOTIFY_ON_U2M_TRANSITIONS; -#endif - - pThread->StackWalkFrames(&CallStackLogger::LogCallstackForLogCallback, &logger, flags); + pThread->StackWalkFrames(&CallStackLogger::LogCallstackForLogCallback, &logger, QUICKUNWIND | FUNCTIONSONLY | ALLOW_ASYNC_STACK_WALK); logger.PrintStackTrace(WordAt.GetUnicode()); - #ifdef _DEBUG if (g_LogStackOverflowExit) PrintToStdErrA("@Exiting stack trace printing thread.\n"); From c73b1de0df29d46d24ab3b6d7d7d0b9b5af0c01f Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Fri, 13 Feb 2026 20:47:20 -0500 Subject: [PATCH 13/18] Prevent duplicate callstack logging for FailFast path After LogInfoForFatalError logs the callstack, mark s_callstackForFatalErrorLogged so the subsequent SIGABRT signal handler (LogCallstackForFatalErrorOnce) does not log a duplicate. Add comment explaining why s_callstackForFatalErrorLogged is needed: the unhandled managed exception path (DefaultCatchHandler) logs its own stack trace and never enters LogInfoForFatalError, so s_pCrashingThreadID is never set. Without this separate flag, the signal handler would produce a duplicate stack trace. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/vm/eepolicy.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index f51757426c7cd1..03eda1918a8cd9 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -445,6 +445,14 @@ void LogInfoForFatalError(UINT exitCode, LPCWSTR pszMessage, PEXCEPTION_POINTERS { LogCallstackForLogWorker(pThread, pExceptionInfo); +#ifdef HOST_ANDROID + // Mark that a callstack has been logged so the signal handler + // (LogCallstackForFatalErrorOnce) does not log a duplicate. + // This handles the FailFast path: HandleFatalError -> LogInfoForFatalError + // -> CrashDumpAndTerminateProcess -> PROCAbort -> SIGABRT -> signal handler. + EEPolicy::CallstackForFatalErrorLogged(); +#endif + if (argExceptionString != NULL) { PrintToStdErrW(argExceptionString); } @@ -904,9 +912,15 @@ int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR } #ifdef HOST_ANDROID +// Guards against duplicate callstack logging across different crash paths. +// The unhandled managed exception path (DefaultCatchHandlerExceptionMessageWorker) +// logs its own stack trace and sets this flag, but never enters LogInfoForFatalError +// so s_pCrashingThreadID is never set. Without this flag, the subsequent SIGABRT +// signal handler would produce a duplicate stack trace. +// The FailFast path sets this flag after LogInfoForFatalError logs the callstack. static Volatile s_callstackForFatalErrorLogged = FALSE; -// Logs the callstack for a fatal error. Should only be called once. +// Logs the callstack for a fatal error. void EEPolicy::LogCallstackForFatalErrorOnce(LPCWSTR errorMessage) { WRAPPER_NO_CONTRACT; From eed2e26dd3c60a96285803b380d1d5345131d919 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Fri, 13 Feb 2026 21:16:48 -0500 Subject: [PATCH 14/18] Add SIGTRAP to GetSignalName Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/pal/src/thread/process.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 88a2b956818bd7..c47bb302659e20 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2743,6 +2743,7 @@ static LPCWSTR GetSignalName(int signal) case SIGFPE: return W("SIGFPE"); case SIGILL: return W("SIGILL"); case SIGABRT: return W("SIGABRT"); + case SIGTRAP: return W("SIGTRAP"); case SIGTERM: return W("SIGTERM"); default: return W("Unknown signal"); } From a2c34c848899add3959209a950fe7c996e4a3dfe Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Fri, 13 Feb 2026 21:24:38 -0500 Subject: [PATCH 15/18] Add descriptive native crash message similar to Mono Compose a message like 'Got a SIGSEGV while executing native code. This usually indicates a fatal error in the runtime or one of the native libraries used by your application.' for native crashes, matching the messaging from Mono's mini-exceptions.c. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/pal/src/thread/process.cpp | 4 ++-- src/coreclr/vm/eepolicy.cpp | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index c47bb302659e20..8f347516939000 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2766,8 +2766,8 @@ PROCLogCallstackForFatalError(int signal) { if (g_logCallstackForFatalErrorCallback != nullptr) { - LPCWSTR errorMessage = GetSignalName(signal); - g_logCallstackForFatalErrorCallback(errorMessage); + LPCWSTR signalName = GetSignalName(signal); + g_logCallstackForFatalErrorCallback(signalName); } } diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index 03eda1918a8cd9..93acbb1a277abb 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -930,7 +930,14 @@ void EEPolicy::LogCallstackForFatalErrorOnce(LPCWSTR errorMessage) return; } - LogInfoForFatalError(0, errorMessage, nullptr, nullptr, nullptr); + InlineSString<256> nativeCrashMessage; + nativeCrashMessage.Append(W("Got a ")); + nativeCrashMessage.Append(errorMessage); + nativeCrashMessage.Append(W(" while executing native code. This usually indicates\n") + W("a fatal error in the runtime or one of the native libraries\n") + W("used by your application.")); + + LogInfoForFatalError(0, nativeCrashMessage.GetUnicode(), nullptr, nullptr, nullptr); } void EEPolicy::CallstackForFatalErrorLogged() From 77aacf7cb0dcc20be970a45a3d986844449d1184 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 17 Feb 2026 19:02:41 -0500 Subject: [PATCH 16/18] Remove PROCLogCallstackForFatalError from PROCAbort Some PROCAbort paths have already logged a callstack, like unhandled managed exceptions. Instead of necessitating some deduplication logic, bump PROCLogCallstackForFatalError to callsites in signal.cpp where a callstack would make sense before a PROCAbort. --- src/coreclr/pal/src/exception/signal.cpp | 2 +- src/coreclr/pal/src/thread/process.cpp | 1 - src/coreclr/vm/eepolicy.cpp | 26 ------------------------ src/coreclr/vm/eepolicy.h | 2 -- src/coreclr/vm/excep.cpp | 4 ---- 5 files changed, 1 insertion(+), 34 deletions(-) diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp index d99ee37150b1d3..0e635101bd9d4e 100644 --- a/src/coreclr/pal/src/exception/signal.cpp +++ b/src/coreclr/pal/src/exception/signal.cpp @@ -451,7 +451,6 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t // Shutdown and create the core dump before we restore the signal to the default handler. PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCLogCallstackForFatalError(code); PROCCreateCrashDumpIfEnabled(code, siginfo, context, true); // Restore the original and restart h/w exception. @@ -1032,6 +1031,7 @@ PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread) // Failure to send the signal is fatal. There are only two cases when sending // the signal can fail. First, if the signal ID is invalid and second, // if the thread doesn't exist anymore. + PROCLogCallstackForFatalError(SIGABRT); PROCAbort(); } diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 8f347516939000..268f0e03e620fc 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2898,7 +2898,6 @@ PROCAbort(int signal, siginfo_t* siginfo, void* context) // Do any shutdown cleanup before aborting or creating a core dump PROCNotifyProcessShutdown(); - PROCLogCallstackForFatalError(signal); PROCCreateCrashDumpIfEnabled(signal, siginfo, context, true); // Restore all signals; the SIGABORT handler to prevent recursion and diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index 93acbb1a277abb..c03c381daed511 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -445,14 +445,6 @@ void LogInfoForFatalError(UINT exitCode, LPCWSTR pszMessage, PEXCEPTION_POINTERS { LogCallstackForLogWorker(pThread, pExceptionInfo); -#ifdef HOST_ANDROID - // Mark that a callstack has been logged so the signal handler - // (LogCallstackForFatalErrorOnce) does not log a duplicate. - // This handles the FailFast path: HandleFatalError -> LogInfoForFatalError - // -> CrashDumpAndTerminateProcess -> PROCAbort -> SIGABRT -> signal handler. - EEPolicy::CallstackForFatalErrorLogged(); -#endif - if (argExceptionString != NULL) { PrintToStdErrW(argExceptionString); } @@ -912,24 +904,11 @@ int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR } #ifdef HOST_ANDROID -// Guards against duplicate callstack logging across different crash paths. -// The unhandled managed exception path (DefaultCatchHandlerExceptionMessageWorker) -// logs its own stack trace and sets this flag, but never enters LogInfoForFatalError -// so s_pCrashingThreadID is never set. Without this flag, the subsequent SIGABRT -// signal handler would produce a duplicate stack trace. -// The FailFast path sets this flag after LogInfoForFatalError logs the callstack. -static Volatile s_callstackForFatalErrorLogged = FALSE; - // Logs the callstack for a fatal error. void EEPolicy::LogCallstackForFatalErrorOnce(LPCWSTR errorMessage) { WRAPPER_NO_CONTRACT; - if (InterlockedCompareExchange(&s_callstackForFatalErrorLogged, TRUE, FALSE) != FALSE) - { - return; - } - InlineSString<256> nativeCrashMessage; nativeCrashMessage.Append(W("Got a ")); nativeCrashMessage.Append(errorMessage); @@ -939,9 +918,4 @@ void EEPolicy::LogCallstackForFatalErrorOnce(LPCWSTR errorMessage) LogInfoForFatalError(0, nativeCrashMessage.GetUnicode(), nullptr, nullptr, nullptr); } - -void EEPolicy::CallstackForFatalErrorLogged() -{ - s_callstackForFatalErrorLogged = TRUE; -} #endif // HOST_ANDROID diff --git a/src/coreclr/vm/eepolicy.h b/src/coreclr/vm/eepolicy.h index 795df735373fe9..43d3c565961d68 100644 --- a/src/coreclr/vm/eepolicy.h +++ b/src/coreclr/vm/eepolicy.h @@ -40,8 +40,6 @@ class EEPolicy #ifdef HOST_ANDROID static void LogCallstackForFatalErrorOnce(LPCWSTR errorMessage); - - static void CallstackForFatalErrorLogged(); #endif private: diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 347001f456c7ec..078536db9e10e6 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -4431,10 +4431,6 @@ DefaultCatchHandlerExceptionMessageWorker(Thread* pThread, PrintToStdErrW(message.GetUnicode()); PrintToStdErrA("\n"); -#ifdef HOST_ANDROID - EEPolicy::CallstackForFatalErrorLogged(); -#endif - #if defined(FEATURE_EVENT_TRACE) && !defined(TARGET_UNIX) // Send the log to Windows Event Log if (sendWindowsEventLog && ShouldLogInEventLog()) From a2aebf79969994dd84ccf8527a8227f0e6a80026 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 17 Feb 2026 22:56:17 -0500 Subject: [PATCH 17/18] Rename crash callstack logging APIs from FatalError to ForSignal Rename PAL and VM APIs to better reflect their purpose: - PAL_SetLogCallstackForFatalErrorCallback -> PAL_SetLogManagedCallstackForSignalCallback - PROCLogCallstackForFatalError -> PROCLogManagedCallstackForSignal - EEPolicy::LogCallstackForFatalErrorOnce -> EEPolicy::LogManagedCallstackForSignal - Update callback typedef, global variable, and all callsites Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/pal/inc/pal.h | 9 ++++---- src/coreclr/pal/src/exception/signal.cpp | 8 +++---- src/coreclr/pal/src/include/pal/process.h | 6 +++--- src/coreclr/pal/src/thread/process.cpp | 26 +++++++++++------------ src/coreclr/vm/ceemain.cpp | 2 +- src/coreclr/vm/eepolicy.cpp | 18 ++++++++-------- src/coreclr/vm/eepolicy.h | 2 +- 7 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 2b065c9b0f8c7f..d3db65876a2fa0 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -253,15 +253,16 @@ PAL_SetCreateDumpCallback( IN PCREATEDUMP_CALLBACK callback); /// -/// Callback invoked when a fatal error occurs. The callback should log the callstack for the fatal error. +/// Callback invoked when a signal is received that will terminate the process. +/// The callback should log the managed callstack for the signal. /// -typedef VOID (*PLOGCALLSTACKFORFATALERROR_CALLBACK)(LPCWSTR errorMessage); +typedef VOID (*PLOGMANAGEDCALLSTACKFORSIGNAL_CALLBACK)(LPCWSTR signalName); PALIMPORT VOID PALAPI -PAL_SetLogCallstackForFatalErrorCallback( - IN PLOGCALLSTACKFORFATALERROR_CALLBACK callback); +PAL_SetLogManagedCallstackForSignalCallback( + IN PLOGMANAGEDCALLSTACKFORSIGNAL_CALLBACK callback); PALIMPORT VOID diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp index 0e635101bd9d4e..c27b6e6f92006b 100644 --- a/src/coreclr/pal/src/exception/signal.cpp +++ b/src/coreclr/pal/src/exception/signal.cpp @@ -472,7 +472,7 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t { PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCLogCallstackForFatalError(code); + PROCLogManagedCallstackForSignal(code); PROCCreateCrashDumpIfEnabled(code, siginfo, context, true); } @@ -493,7 +493,7 @@ static void invoke_previous_action(struct sigaction* action, int code, siginfo_t { PROCNotifyProcessShutdown(IsRunningOnAlternateStack(context)); - PROCLogCallstackForFatalError(code); + PROCLogManagedCallstackForSignal(code); PROCCreateCrashDumpIfEnabled(code, siginfo, context, true); } } @@ -884,7 +884,7 @@ static void sigterm_handler(int code, siginfo_t *siginfo, void *context) DWORD val = 0; if (enableDumpOnSigTerm.IsSet() && enableDumpOnSigTerm.TryAsInteger(10, val) && val == 1) { - PROCLogCallstackForFatalError(code); + PROCLogManagedCallstackForSignal(code); PROCCreateCrashDumpIfEnabled(code, siginfo, context, false); } } @@ -1031,7 +1031,7 @@ PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread) // Failure to send the signal is fatal. There are only two cases when sending // the signal can fail. First, if the signal ID is invalid and second, // if the thread doesn't exist anymore. - PROCLogCallstackForFatalError(SIGABRT); + PROCLogManagedCallstackForSignal(SIGABRT); PROCAbort(); } diff --git a/src/coreclr/pal/src/include/pal/process.h b/src/coreclr/pal/src/include/pal/process.h index ea30310794c3bd..e3f26bde875a03 100644 --- a/src/coreclr/pal/src/include/pal/process.h +++ b/src/coreclr/pal/src/include/pal/process.h @@ -160,9 +160,9 @@ VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, /*++ Function: - PROCLogCallstackForFatalError + PROCLogManagedCallstackForSignal - Invokes the registered callback to log the callstack for a fatal error. + Invokes the registered callback to log the managed callstack for a signal. Used by Android since CreateDump is not supported there. Parameters: @@ -170,7 +170,7 @@ VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, (no return value) --*/ -VOID PROCLogCallstackForFatalError(int signal); +VOID PROCLogManagedCallstackForSignal(int signal); #ifdef __cplusplus } diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 268f0e03e620fc..31996bb4c3e78c 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -183,8 +183,8 @@ Volatile g_shutdownCallback = nullptr; // Function to call instead of exec'ing the createdump binary. Used by single-file and native AOT hosts. Volatile g_createdumpCallback = nullptr; -// Function to call to log the callstack for a fatal error. Used by Android since CoreCLR doesn't support CreateDump on Android. -Volatile g_logCallstackForFatalErrorCallback = nullptr; +// Function to call to log the managed callstack for a signal. Used by Android since CoreCLR doesn't support CreateDump on Android. +Volatile g_logManagedCallstackForSignalCallback = nullptr; // Crash dump generating program arguments. Initialized in PROCAbortInitialize(). #define MAX_ARGV_ENTRIES 32 @@ -1351,10 +1351,10 @@ PAL_SetCreateDumpCallback( /*++ Function: - PAL_SetLogCallstackForFatalErrorCallback + PAL_SetLogManagedCallstackForSignalCallback Abstract: - Sets a callback that is executed when a fatal error (crash) occurs to log the callstack. + Sets a callback that is executed when a signal is received to log the managed callstack. Used by Android CoreCLR since CreateDump is not supported on Android. NOTE: Currently only one callback can be set at a time. @@ -1362,11 +1362,11 @@ PAL_SetCreateDumpCallback( PALIMPORT VOID PALAPI -PAL_SetLogCallstackForFatalErrorCallback( - IN PLOGCALLSTACKFORFATALERROR_CALLBACK callback) +PAL_SetLogManagedCallstackForSignalCallback( + IN PLOGMANAGEDCALLSTACKFORSIGNAL_CALLBACK callback) { - _ASSERTE(g_logCallstackForFatalErrorCallback == nullptr); - g_logCallstackForFatalErrorCallback = callback; + _ASSERTE(g_logManagedCallstackForSignalCallback == nullptr); + g_logManagedCallstackForSignalCallback = callback; } // Build the semaphore names using the PID and a value that can be used for distinguishing @@ -2751,9 +2751,9 @@ static LPCWSTR GetSignalName(int signal) /*++ Function: - PROCLogCallstackForFatalError + PROCLogManagedCallstackForSignal - Invokes the registered callback to log the callstack for a fatal error. + Invokes the registered callback to log the managed callstack for a signal. Used by Android since CreateDump is not supported there. Parameters: @@ -2762,12 +2762,12 @@ static LPCWSTR GetSignalName(int signal) (no return value) --*/ VOID -PROCLogCallstackForFatalError(int signal) +PROCLogManagedCallstackForSignal(int signal) { - if (g_logCallstackForFatalErrorCallback != nullptr) + if (g_logManagedCallstackForSignalCallback != nullptr) { LPCWSTR signalName = GetSignalName(signal); - g_logCallstackForFatalErrorCallback(signalName); + g_logManagedCallstackForSignalCallback(signalName); } } diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index ee41817bbf8d2d..79b2bf3f33166b 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -695,7 +695,7 @@ void EEStartupHelper() #endif // TARGET_UNIX #ifdef HOST_ANDROID - PAL_SetLogCallstackForFatalErrorCallback(EEPolicy::LogCallstackForFatalErrorOnce); + PAL_SetLogManagedCallstackForSignalCallback(EEPolicy::LogManagedCallstackForSignal); #endif // HOST_ANDROID #ifdef STRESS_LOG diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index c03c381daed511..60e7aab8ff45ef 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -904,18 +904,18 @@ int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR } #ifdef HOST_ANDROID -// Logs the callstack for a fatal error. -void EEPolicy::LogCallstackForFatalErrorOnce(LPCWSTR errorMessage) +// Logs the managed callstack when a signal is received. +void EEPolicy::LogManagedCallstackForSignal(LPCWSTR signalName) { WRAPPER_NO_CONTRACT; - InlineSString<256> nativeCrashMessage; - nativeCrashMessage.Append(W("Got a ")); - nativeCrashMessage.Append(errorMessage); - nativeCrashMessage.Append(W(" while executing native code. This usually indicates\n") - W("a fatal error in the runtime or one of the native libraries\n") - W("used by your application.")); + InlineSString<256> message; + message.Append(W("Got a ")); + message.Append(signalName); + message.Append(W(" while executing native code. This usually indicates\n") + W("a fatal error in the runtime or one of the native libraries\n") + W("used by your application.")); - LogInfoForFatalError(0, nativeCrashMessage.GetUnicode(), nullptr, nullptr, nullptr); + LogInfoForFatalError(0, message.GetUnicode(), nullptr, nullptr, nullptr); } #endif // HOST_ANDROID diff --git a/src/coreclr/vm/eepolicy.h b/src/coreclr/vm/eepolicy.h index 43d3c565961d68..d9103664195e8b 100644 --- a/src/coreclr/vm/eepolicy.h +++ b/src/coreclr/vm/eepolicy.h @@ -39,7 +39,7 @@ class EEPolicy static void DECLSPEC_NORETURN HandleFatalStackOverflow(EXCEPTION_POINTERS *pException, BOOL fSkipDebugger = FALSE); #ifdef HOST_ANDROID - static void LogCallstackForFatalErrorOnce(LPCWSTR errorMessage); + static void LogManagedCallstackForSignal(LPCWSTR signalName); #endif private: From b4a2953658eccfaec417f8a67e995a2adb80ff14 Mon Sep 17 00:00:00 2001 From: Mitchell Hwang Date: Tue, 17 Feb 2026 23:37:31 -0500 Subject: [PATCH 18/18] Revert artifact of native code marker commit --- src/coreclr/vm/eepolicy.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index 60e7aab8ff45ef..4931f2d990e5e1 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -192,9 +192,9 @@ class CallStackLogger { WRAPPER_NO_CONTRACT; - MethodDesc* pMD = m_frames[index]; - SString str(pWordAt); + + MethodDesc* pMD = m_frames[index]; TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst|TypeString::FormatSignature); str.Append(W("\n"));