diff --git a/src/coreclr/debug/CMakeLists.txt b/src/coreclr/debug/CMakeLists.txt index c8fb2407550d90..bd2bba5cdb8bfa 100644 --- a/src/coreclr/debug/CMakeLists.txt +++ b/src/coreclr/debug/CMakeLists.txt @@ -7,7 +7,7 @@ include_directories(${RUNTIME_DIR}) add_subdirectory(daccess) add_subdirectory(ee) add_subdirectory(di) -if(CLR_CMAKE_TARGET_ANDROID) +if(CLR_CMAKE_TARGET_LINUX OR CLR_CMAKE_TARGET_APPLE) add_subdirectory(crashreport) endif() if(CLR_CMAKE_HOST_WIN32) diff --git a/src/coreclr/debug/crashreport/inproccrashreporter.cpp b/src/coreclr/debug/crashreport/inproccrashreporter.cpp index e7d35f1dd68234..b9676cb73d2a17 100644 --- a/src/coreclr/debug/crashreport/inproccrashreporter.cpp +++ b/src/coreclr/debug/crashreport/inproccrashreporter.cpp @@ -15,6 +15,9 @@ #include #include #include +#ifdef __APPLE__ +#include +#endif // Include the .NET version string instead of linking because it is "static". #if __has_include("_version.c") @@ -33,6 +36,26 @@ static volatile InProcCrashReportGetExceptionCallback g_getExceptionCallback = N static volatile InProcCrashReportEnumerateThreadsCallback g_enumerateThreadsCallback = NULL; static char g_reportPath[256]; +#ifdef __APPLE__ +// Query a sysctl by name into a caller-supplied stack buffer and write it to the JSON writer. +// sysctlbyname is async-signal-safe and avoids heap allocation, matching the createdump +// behavior (see src/coreclr/debug/createdump/crashreportwriter.cpp WriteSysctl). +static void WriteSysctlString(CrashJsonWriter* writer, const char* sysctlName, const char* valueName) +{ + char buffer[256]; + size_t size = sizeof(buffer); + if (sysctlbyname(sysctlName, buffer, &size, NULL, 0) == 0 && size > 0) + { + buffer[sizeof(buffer) - 1] = '\0'; + CrashJsonWriteString(writer, valueName, buffer); + } + else + { + CrashJsonWriteString(writer, valueName, ""); + } +} +#endif // __APPLE__ + struct MultiThreadJsonContext { CrashJsonWriter* writer; @@ -336,8 +359,8 @@ InProcCrashReportGenerate( (void)snprintf(signalBuf, sizeof(signalBuf), "%d", signal); CrashJsonWriteString(&s_jsonWriter, "signal", signalBuf); #ifdef __APPLE__ - CrashJsonWriteString(&s_jsonWriter, "OSVersion", ""); - CrashJsonWriteString(&s_jsonWriter, "SystemModel", ""); + WriteSysctlString(&s_jsonWriter, "kern.osproductversion", "OSVersion"); + WriteSysctlString(&s_jsonWriter, "hw.model", "SystemModel"); CrashJsonWriteString(&s_jsonWriter, "SystemManufacturer", "apple"); #endif CrashJsonCloseObject(&s_jsonWriter); @@ -629,7 +652,11 @@ WriteRegistersToJson( if (context != NULL) { ucontext_t* ucontext = reinterpret_cast(context); -#if defined(__x86_64__) +#if defined(__APPLE__) && defined(__x86_64__) + FormatHexValue(bp, sizeof(bp), static_cast(ucontext->uc_mcontext->__ss.__rbp)); +#elif defined(__APPLE__) && defined(__aarch64__) + FormatHexValue(bp, sizeof(bp), static_cast(arm_thread_state64_get_fp(ucontext->uc_mcontext->__ss))); +#elif defined(__x86_64__) FormatHexValue(bp, sizeof(bp), static_cast(ucontext->uc_mcontext.gregs[REG_RBP])); #elif defined(__aarch64__) FormatHexValue(bp, sizeof(bp), static_cast(ucontext->uc_mcontext.regs[29])); @@ -655,7 +682,11 @@ GetInstructionPointer( } ucontext_t* ucontext = reinterpret_cast(context); -#if defined(__x86_64__) +#if defined(__APPLE__) && defined(__x86_64__) + return static_cast(ucontext->uc_mcontext->__ss.__rip); +#elif defined(__APPLE__) && defined(__aarch64__) + return static_cast(arm_thread_state64_get_pc(ucontext->uc_mcontext->__ss)); +#elif defined(__x86_64__) return static_cast(ucontext->uc_mcontext.gregs[REG_RIP]); #elif defined(__aarch64__) return static_cast(ucontext->uc_mcontext.pc); @@ -676,7 +707,11 @@ GetStackPointer( } ucontext_t* ucontext = reinterpret_cast(context); -#if defined(__x86_64__) +#if defined(__APPLE__) && defined(__x86_64__) + return static_cast(ucontext->uc_mcontext->__ss.__rsp); +#elif defined(__APPLE__) && defined(__aarch64__) + return static_cast(arm_thread_state64_get_sp(ucontext->uc_mcontext->__ss)); +#elif defined(__x86_64__) return static_cast(ucontext->uc_mcontext.gregs[REG_RSP]); #elif defined(__aarch64__) return static_cast(ucontext->uc_mcontext.sp); diff --git a/src/coreclr/pal/src/CMakeLists.txt b/src/coreclr/pal/src/CMakeLists.txt index f5f83c9dbe4206..8137c59125426c 100644 --- a/src/coreclr/pal/src/CMakeLists.txt +++ b/src/coreclr/pal/src/CMakeLists.txt @@ -37,9 +37,9 @@ endif(CORECLR_SET_RPATH) # Include directories include_directories(include) -if(CLR_CMAKE_TARGET_ANDROID) +if(CLR_CMAKE_TARGET_LINUX OR CLR_CMAKE_TARGET_APPLE) include_directories(${CLR_DIR}) -endif(CLR_CMAKE_TARGET_ANDROID) +endif(CLR_CMAKE_TARGET_LINUX OR CLR_CMAKE_TARGET_APPLE) # Compile options @@ -228,7 +228,7 @@ add_library(coreclrpal_objects add_library(coreclrpal STATIC $ - $<$:$> + $<$,$>:$> ${LIBUNWIND_OBJECTS} ) diff --git a/src/coreclr/pal/src/include/pal/process.h b/src/coreclr/pal/src/include/pal/process.h index 6c2f3c1077cc04..4fd1f2094f4d7f 100644 --- a/src/coreclr/pal/src/include/pal/process.h +++ b/src/coreclr/pal/src/include/pal/process.h @@ -172,7 +172,7 @@ VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, --*/ VOID PROCLogManagedCallstackForSignal(int signal); -#ifdef HOST_ANDROID +#if defined(HOST_UNIX) void PROCEnableInProcCrashReport(); #endif diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index af6269fe0bdbd1..373ffd1146ac95 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -35,7 +35,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d #include #include -#ifdef HOST_ANDROID +#if defined(HOST_UNIX) #include "debug/crashreport/inproccrashreporter.h" #endif @@ -194,7 +194,7 @@ Volatile g_logManagedCallstackForSignalC #define MAX_ARGV_ENTRIES 32 const char* g_argvCreateDump[MAX_ARGV_ENTRIES] = { nullptr }; -#ifdef HOST_ANDROID +#if defined(HOST_UNIX) // Read from the fatal-signal path (PROCCreateCrashDumpIfEnabled) and written // once during startup (PROCEnableInProcCrashReport); use Volatile to // match the signal-path publication of g_logManagedCallstackForSignalCallback. @@ -2799,11 +2799,15 @@ PROCLogManagedCallstackForSignal(int signal) --*/ #ifdef HOST_ANDROID #include +#endif + +#if defined(HOST_UNIX) void PROCEnableInProcCrashReport() { g_inProcCrashReportEnabled = true; } +#endif VOID PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize) @@ -2811,20 +2815,22 @@ PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool // Preserve context pointer to prevent optimization DoNotOptimize(&context); - // TODO: Dump stress log into logcat and/or file when enabled? +#if defined(HOST_UNIX) + // If in-proc crash reporting is enabled, generate the report and return. + // The in-proc reporter is an alternative to createdump (not a supplement), + // so skip the createdump launch path below when it is active. if (g_inProcCrashReportEnabled) { + // TODO: Dump stress log into logcat and/or file when enabled? InProcCrashReportGenerate(signal, siginfo, context); +#if defined(HOST_ANDROID) + minipal_log_write_fatal("Aborting process.\n"); +#endif + return; } - minipal_log_write_fatal("Aborting process.\n"); -} -#else -VOID -PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize) -{ - // Preserve context pointer to prevent optimization - DoNotOptimize(&context); +#endif // HOST_UNIX +#if !defined(HOST_ANDROID) // If enabled, launch the create minidump utility and wait until it completes if (g_argvCreateDump[0] != nullptr) { @@ -2894,8 +2900,8 @@ PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool free(signalErrnoArg); free(signalAddressArg); } +#endif // !HOST_ANDROID } -#endif /*++ Function: diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 9662b1f5a8df3f..b1ff3836fbc7ea 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -4,9 +4,9 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${ARCH_SOURCES_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../interop/inc) -if(CLR_CMAKE_TARGET_ANDROID) +if(CLR_CMAKE_TARGET_LINUX OR CLR_CMAKE_TARGET_APPLE) include_directories(${CLR_DIR}) -endif(CLR_CMAKE_TARGET_ANDROID) +endif(CLR_CMAKE_TARGET_LINUX OR CLR_CMAKE_TARGET_APPLE) include_directories(${CLR_SRC_NATIVE_DIR}) include_directories(${RUNTIME_DIR}) diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 6b36a675474f91..9a3ecd590fd19f 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -697,9 +697,12 @@ void EEStartupHelper() #ifdef HOST_ANDROID PAL_SetLogManagedCallstackForSignalCallback(EEPolicy::LogManagedCallstackForSignal); - CrashReportRegisterStackWalker(); #endif // HOST_ANDROID +#if defined(HOST_UNIX) + CrashReportRegisterStackWalker(); +#endif // HOST_UNIX + #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/crashreportstackwalker.cpp b/src/coreclr/vm/crashreportstackwalker.cpp index 98a7614538c044..dd92f4aef9f9ff 100644 --- a/src/coreclr/vm/crashreportstackwalker.cpp +++ b/src/coreclr/vm/crashreportstackwalker.cpp @@ -11,7 +11,7 @@ #include #include -#ifdef HOST_ANDROID +#if defined(HOST_UNIX) #include "debug/crashreport/inproccrashreporter.h" #include "threadsuspend.h" @@ -396,6 +396,21 @@ CrashReportRegisterStackWalker() // Read crash report configuration here rather than in PROCAbortInitialize // because on Android the DOTNET_* environment variables are set via JNI // after PAL_Initialize has already run. + // + // Mobile platforms (Android, iOS, tvOS, MacCatalyst) have no createdump + // utility, so DOTNET_EnableCrashReport / DOTNET_EnableCrashReportOnly -- + // the knobs that would toggle createdump's JSON report on platforms where + // createdump exists -- instead opt in to the in-proc reporter here. + // DOTNET_EnableInProcessCrashReport is accepted on mobile as well so a + // uniform opt-in can be used across all platforms that support this + // reporter. + // + // On desktop POSIX platforms (Linux, macOS), createdump is the incumbent + // implementation for DOTNET_EnableCrashReport. To avoid silently + // replacing that path, the in-proc reporter is opted in explicitly via + // DOTNET_EnableInProcessCrashReport only and takes precedence over + // createdump when it is set. +#if defined(HOST_ANDROID) || defined(HOST_IOS) || defined(HOST_TVOS) || defined(HOST_MACCATALYST) CLRConfigNoCache enabledReportCfg = CLRConfigNoCache::Get("EnableCrashReport", /*noprefix*/ false, &getenv); DWORD reportEnabled = 0; bool enableCrashReport = enabledReportCfg.IsSet() && enabledReportCfg.TryAsInteger(10, reportEnabled) && reportEnabled == 1; @@ -404,10 +419,24 @@ CrashReportRegisterStackWalker() DWORD reportOnlyEnabled = 0; bool enableCrashReportOnly = enabledReportOnlyCfg.IsSet() && enabledReportOnlyCfg.TryAsInteger(10, reportOnlyEnabled) && reportOnlyEnabled == 1; - if (!enableCrashReport && !enableCrashReportOnly) + CLRConfigNoCache enabledInProcCfg = CLRConfigNoCache::Get("EnableInProcessCrashReport", /*noprefix*/ false, &getenv); + DWORD inProcEnabled = 0; + bool enableInProcCrashReport = enabledInProcCfg.IsSet() && enabledInProcCfg.TryAsInteger(10, inProcEnabled) && inProcEnabled == 1; + + if (!enableCrashReport && !enableCrashReportOnly && !enableInProcCrashReport) + { + return; + } +#else + CLRConfigNoCache enabledInProcCfg = CLRConfigNoCache::Get("EnableInProcessCrashReport", /*noprefix*/ false, &getenv); + DWORD inProcEnabled = 0; + bool enableInProcCrashReport = enabledInProcCfg.IsSet() && enabledInProcCfg.TryAsInteger(10, inProcEnabled) && inProcEnabled == 1; + + if (!enableInProcCrashReport) { return; } +#endif CLRConfigNoCache dmpNameCfg = CLRConfigNoCache::Get("DbgMiniDumpName", /*noprefix*/ false, &getenv); const char* dumpName = dmpNameCfg.IsSet() ? dmpNameCfg.AsString() : nullptr; @@ -447,4 +476,4 @@ CrashReportRegisterStackWalker() InProcCrashReportSetThreadEnumerator(CrashReportEnumerateThreads); } -#endif // HOST_ANDROID +#endif // HOST_UNIX diff --git a/src/coreclr/vm/crashreportstackwalker.h b/src/coreclr/vm/crashreportstackwalker.h index 0beaae6077e92c..d55cd1413f8e7c 100644 --- a/src/coreclr/vm/crashreportstackwalker.h +++ b/src/coreclr/vm/crashreportstackwalker.h @@ -4,10 +4,10 @@ #ifndef CRASHREPORTSTACKWALKER_H #define CRASHREPORTSTACKWALKER_H -#ifdef HOST_ANDROID +#if defined(HOST_UNIX) void CrashReportRegisterStackWalker(); -#endif // HOST_ANDROID +#endif // HOST_UNIX #endif // CRASHREPORTSTACKWALKER_H