Skip to content
Draft
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
2 changes: 1 addition & 1 deletion src/coreclr/debug/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
45 changes: 40 additions & 5 deletions src/coreclr/debug/crashreport/inproccrashreporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#include <string.h>
#include <ucontext.h>
#include <minipal/thread.h>
#ifdef __APPLE__
#include <sys/sysctl.h>
#endif

// Include the .NET version string instead of linking because it is "static".
#if __has_include("_version.c")
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -629,7 +652,11 @@ WriteRegistersToJson(
if (context != NULL)
{
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
#if defined(__x86_64__)
#if defined(__APPLE__) && defined(__x86_64__)
FormatHexValue(bp, sizeof(bp), static_cast<uint64_t>(ucontext->uc_mcontext->__ss.__rbp));
#elif defined(__APPLE__) && defined(__aarch64__)
FormatHexValue(bp, sizeof(bp), static_cast<uint64_t>(arm_thread_state64_get_fp(ucontext->uc_mcontext->__ss)));
#elif defined(__x86_64__)
FormatHexValue(bp, sizeof(bp), static_cast<uint64_t>(ucontext->uc_mcontext.gregs[REG_RBP]));
#elif defined(__aarch64__)
FormatHexValue(bp, sizeof(bp), static_cast<uint64_t>(ucontext->uc_mcontext.regs[29]));
Expand All @@ -655,7 +682,11 @@ GetInstructionPointer(
}

ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
#if defined(__x86_64__)
#if defined(__APPLE__) && defined(__x86_64__)
return static_cast<uint64_t>(ucontext->uc_mcontext->__ss.__rip);
#elif defined(__APPLE__) && defined(__aarch64__)
return static_cast<uint64_t>(arm_thread_state64_get_pc(ucontext->uc_mcontext->__ss));
#elif defined(__x86_64__)
return static_cast<uint64_t>(ucontext->uc_mcontext.gregs[REG_RIP]);
#elif defined(__aarch64__)
return static_cast<uint64_t>(ucontext->uc_mcontext.pc);
Expand All @@ -676,7 +707,11 @@ GetStackPointer(
}

ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
#if defined(__x86_64__)
#if defined(__APPLE__) && defined(__x86_64__)
return static_cast<uint64_t>(ucontext->uc_mcontext->__ss.__rsp);
#elif defined(__APPLE__) && defined(__aarch64__)
return static_cast<uint64_t>(arm_thread_state64_get_sp(ucontext->uc_mcontext->__ss));
#elif defined(__x86_64__)
return static_cast<uint64_t>(ucontext->uc_mcontext.gregs[REG_RSP]);
#elif defined(__aarch64__)
return static_cast<uint64_t>(ucontext->uc_mcontext.sp);
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/pal/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -228,7 +228,7 @@ add_library(coreclrpal_objects

add_library(coreclrpal STATIC
$<TARGET_OBJECTS:coreclrpal_objects>
$<$<BOOL:${CLR_CMAKE_TARGET_ANDROID}>:$<TARGET_OBJECTS:inproccrashreport>>
$<$<OR:$<BOOL:${CLR_CMAKE_TARGET_LINUX}>,$<BOOL:${CLR_CMAKE_TARGET_APPLE}>>:$<TARGET_OBJECTS:inproccrashreport>>
${LIBUNWIND_OBJECTS}
)

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/pal/src/include/pal/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
30 changes: 18 additions & 12 deletions src/coreclr/pal/src/thread/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d
#include <generatedumpflags.h>
#include <clrconfignocache.h>

#ifdef HOST_ANDROID
#if defined(HOST_UNIX)
#include "debug/crashreport/inproccrashreporter.h"
#endif

Expand Down Expand Up @@ -194,7 +194,7 @@ Volatile<PLOGMANAGEDCALLSTACKFORSIGNAL_CALLBACK> 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<bool> to
// match the signal-path publication of g_logManagedCallstackForSignalCallback.
Expand Down Expand Up @@ -2799,32 +2799,38 @@ PROCLogManagedCallstackForSignal(int signal)
--*/
#ifdef HOST_ANDROID
#include <minipal/log.h>
#endif

#if defined(HOST_UNIX)
void
PROCEnableInProcCrashReport()
{
g_inProcCrashReportEnabled = true;
}
#endif

VOID
PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool serialize)
{
// 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)
{
Expand Down Expand Up @@ -2894,8 +2900,8 @@ PROCCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* context, bool
free(signalErrnoArg);
free(signalAddressArg);
}
#endif // !HOST_ANDROID
}
#endif

/*++
Function:
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/vm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})

Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/vm/ceemain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
35 changes: 32 additions & 3 deletions src/coreclr/vm/crashreportstackwalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include <clrconfignocache.h>
#include <minipal/guid.h>

#ifdef HOST_ANDROID
#if defined(HOST_UNIX)

#include "debug/crashreport/inproccrashreporter.h"
#include "threadsuspend.h"
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -447,4 +476,4 @@ CrashReportRegisterStackWalker()
InProcCrashReportSetThreadEnumerator(CrashReportEnumerateThreads);
}

#endif // HOST_ANDROID
#endif // HOST_UNIX
4 changes: 2 additions & 2 deletions src/coreclr/vm/crashreportstackwalker.h
Original file line number Diff line number Diff line change
Expand Up @@ -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