From a47b48e04a50c9b41313e1a5fbc0d871379bdee0 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 17 Jul 2023 17:53:37 -0700 Subject: [PATCH 01/15] NativeAOT createdump fork/exec for crash dump generation Port the .NET Core createdump fork/exec code to NativeAOT. Add src/native/inc/generatedumpflags.h. Remove dup definitions of this enum. Move and port clrconfignocache.h from src/coreclr/inc to src/native/inc/. --- src/coreclr/CMakeLists.txt | 1 + src/coreclr/nativeaot/Runtime/CMakeLists.txt | 1 + .../nativeaot/Runtime/RuntimeInstance.cpp | 8 + .../nativeaot/Runtime/eventpipe/ds-rt-aot.cpp | 19 + .../nativeaot/Runtime/eventpipe/ds-rt-aot.h | 20 +- .../nativeaot/Runtime/unix/PalCreateDump.cpp | 470 ++++++++++++++++++ .../nativeaot/Runtime/unix/PalRedhawkUnix.cpp | 13 +- .../nativeaot/Runtime/unix/config.h.in | 2 + .../nativeaot/Runtime/unix/configure.cmake | 13 + .../src/System/Runtime/RuntimeImports.cs | 6 + .../src/System/RuntimeExceptionHelpers.cs | 1 + src/coreclr/pal/inc/pal.h | 10 +- src/coreclr/vm/excep.h | 12 +- .../inc/clrconfignocache.h | 22 +- src/native/inc/generatedumpflags.h | 20 + 15 files changed, 584 insertions(+), 34 deletions(-) create mode 100644 src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp rename src/{coreclr => native}/inc/clrconfignocache.h (76%) create mode 100644 src/native/inc/generatedumpflags.h diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 925fc6d447652..8d564b6283cfb 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -12,6 +12,7 @@ include(../../eng/native/configurepaths.cmake) include(${CLR_ENG_NATIVE_DIR}/configurecompiler.cmake) include_directories("${CLR_SRC_NATIVE_DIR}") +include_directories("${CLR_SRC_NATIVE_DIR}/inc") if(MSVC) set(CMAKE_CXX_STANDARD_LIBRARIES "") # do not link against standard win32 libs i.e. kernel32, uuid, user32, etc. diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt index d6e45dc776260..ec7b6d0e3679d 100644 --- a/src/coreclr/nativeaot/Runtime/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt @@ -142,6 +142,7 @@ else() list(APPEND COMMON_RUNTIME_SOURCES unix/PalRedhawkUnix.cpp + unix/PalCreateDump.cpp ${GC_DIR}/unix/gcenv.unix.cpp ${GC_DIR}/unix/numasupport.cpp ${GC_DIR}/unix/events.cpp diff --git a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp index 9f82dd84b8926..8fc758aa311ee 100644 --- a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp +++ b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp @@ -50,6 +50,14 @@ COOP_PINVOKE_HELPER(uint8_t *, RhGetCrashInfoBuffer, (int32_t* pcbMaxSize)) return g_CrashInfoBuffer; } +#if TARGET_UNIX +extern void PalCreateCrashDumpIfEnabled(); +COOP_PINVOKE_HELPER(void, RhCreateCrashDumpIfEnabled, ()) +{ + PalCreateCrashDumpIfEnabled(); +} +#endif + COOP_PINVOKE_HELPER(uint8_t *, RhGetRuntimeVersion, (int32_t* pcbLength)) { *pcbLength = sizeof(CLR_PRODUCT_VERSION) - 1; // don't include the terminating null diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp index f6346b3c04666..e085957ac8503 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp @@ -248,4 +248,23 @@ ds_rt_aot_set_environment_variable (const ep_char16_t *name, const ep_char16_t * return SetEnvironmentVariableW(reinterpret_cast(name), reinterpret_cast(value)) ? S_OK : HRESULT_FROM_WIN32(GetLastError()); #endif } + +bool +ds_rt_aot_generate_core_dump ( + const ep_char16_t* dumpName, + int32_t dumpType, + uint32_t flags, + ep_char8_t *errorMessageBuffer, + int32_t cbErrorMessageBuffer) +{ +#ifdef TARGET_UNIX + ep_char8_t *dumpNameUtf8 = ep_rt_utf16le_to_utf8_string (dumpName, ep_rt_utf16_string_len (dumpName)); + extern bool PalGenerateCoreDump(const char* dumpName, int dumpType, uint32_t flags, char* errorMessageBuffer, int cbErrorMessageBuffer); + return PalGenerateCoreDump(dumpNameUtf8, dumpType, flags, errorMessageBuffer, cbErrorMessageBuffer); +#else + return false; +#endif +} + + #endif /* ENABLE_PERFTRACING */ diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h index 18c8753475148..f36edf3ed6332 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h @@ -6,6 +6,7 @@ #define DIAGNOSTICS_RT_AOT_H #include +#include #ifdef ENABLE_PERFTRACING #include "ep-rt-aot.h" @@ -181,8 +182,23 @@ ds_rt_generate_core_dump ( { STATIC_CONTRACT_NOTHROW; - // Eventpipe driven core_dump is not currently supported in NativeAOT - return DS_IPC_E_NOTSUPPORTED; + ds_ipc_result_t result = DS_IPC_E_FAIL; + uint32_t flags = ds_generate_core_dump_command_payload_get_flags(payload); + if (commandId == DS_DUMP_COMMANDID_GENERATE_CORE_DUMP) + { + // For the old commmand, this payload field is a bool of whether to enable logging + flags = flags != 0 ? GenerateDumpFlagsLoggingEnabled : 0; + } + const ep_char16_t *dumpName = ds_generate_core_dump_command_payload_get_dump_name (payload); + int32_t dumpType = static_cast(ds_generate_core_dump_command_payload_get_dump_type (payload)); + + extern bool ds_rt_aot_generate_core_dump (const ep_char16_t* dumpName, int32_t dumpType, uint32_t flags, ep_char8_t *errorMessageBuffer, int32_t cbErrorMessageBuffer); + if (ds_rt_aot_generate_core_dump(dumpName, dumpType, flags, errorMessageBuffer, cbErrorMessageBuffer)) + { + result = DS_IPC_S_OK; + } + + return 0; } /* diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp new file mode 100644 index 0000000000000..b7ee0a4fdb327 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -0,0 +1,470 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include +#include +#include +#include +#include +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_PRCTL_H +#include +#include +#endif +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include +#include +#endif + +#ifdef __NetBSD__ +#include +#include +#include +#include +#endif + +#ifdef __FreeBSD__ +#include +#include +#endif + +#define _ASSERTE(x) + +#include +#include + +// Crash dump generating program arguments +#define MAX_ARGV_ENTRIES 16 +const char* g_argvCreateDump[MAX_ARGV_ENTRIES]; +char* g_szCreateDumpPath = nullptr; +char* g_ppidarg = nullptr; + +/*++ +Function: + FormatInt + + Helper function to format an uint32 as a string. + +--*/ +static +char* +FormatInt(uint32_t value) +{ + char* buffer = (char*)malloc(128); + if (buffer != nullptr) + { + if (snprintf(buffer, 128, "%d", value) < 0) + { + free(buffer); + buffer = nullptr; + } + } + return buffer; +} + +/*++ +Function: + FormatInt64 + + Helper function to format an ULONG64 as a string. + +--*/ +static +char* +FormatInt64(uint64_t value) +{ + char* buffer = (char*)malloc(128); + if (buffer != nullptr) + { + if (snprintf(buffer, 128, "%lu", value) < 0) + { + free(buffer); + buffer = nullptr; + } + } + return buffer; +} + +static const int UndefinedDumpType = 0; + +/*++ +Function + BuildCreateDumpCommandLine + +Abstract + Builds the createdump command line from the arguments. + +Return + true - succeeds, false - fails + +--*/ +static +bool +BuildCreateDumpCommandLine( + const char** argv, + const char* dumpName, + const char* logFileName, + int dumpType, + uint32_t flags) +{ + if (g_szCreateDumpPath == nullptr || g_ppidarg == nullptr) + { + return false; + } + + int argc = 0; + argv[argc++] = g_szCreateDumpPath; + + if (dumpName != nullptr) + { + argv[argc++] = "--name"; + argv[argc++] = dumpName; + } + + switch (dumpType) + { + case 1: argv[argc++] = "--normal"; + break; + case 2: argv[argc++] = "--withheap"; + break; + case 3: argv[argc++] = "--triage"; + break; + case 4: argv[argc++] = "--full"; + break; + default: + break; + } + + if (flags & GenerateDumpFlagsLoggingEnabled) + { + argv[argc++] = "--diag"; + } + + if (flags & GenerateDumpFlagsVerboseLoggingEnabled) + { + argv[argc++] = "--verbose"; + } + + if (flags & GenerateDumpFlagsCrashReportEnabled) + { + argv[argc++] = "--crashreport"; + } + + if (flags & GenerateDumpFlagsCrashReportOnlyEnabled) + { + argv[argc++] = "--crashreportonly"; + } + + if (logFileName != nullptr) + { + argv[argc++] = "--logtofile"; + argv[argc++] = logFileName; + } + + argv[argc++] = "--nativeaot"; + argv[argc++] = g_ppidarg; + argv[argc++] = nullptr; + _ASSERTE(argc < MAX_ARGV_ENTRIES); + + return true; +} + +/*++ +Function: + CreateCrashDump + + Creates crash dump of the process. Can be called from the + unhandled native exception handler. + +(no return value) +--*/ +static bool +CreateCrashDump( + const char* argv[], + char* errorMessageBuffer, + int cbErrorMessageBuffer) +{ + int pipe_descs[2]; + if (pipe(pipe_descs) == -1) + { + if (errorMessageBuffer != nullptr) + { + snprintf(errorMessageBuffer, cbErrorMessageBuffer, "Problem launching createdump: pipe() FAILED %s (%d)\n", strerror(errno), errno); + } + return false; + } + // [0] is read end, [1] is write end + int parent_pipe = pipe_descs[0]; + int child_pipe = pipe_descs[1]; + + // Fork the core dump child process. + pid_t childpid = fork(); + + // If error, write an error to trace log and abort + if (childpid == -1) + { + if (errorMessageBuffer != nullptr) + { + snprintf(errorMessageBuffer, cbErrorMessageBuffer, "Problem launching createdump: fork() FAILED %s (%d)\n", strerror(errno), errno); + } + close(pipe_descs[0]); + close(pipe_descs[1]); + return false; + } + else if (childpid == 0) + { + // Close the read end of the pipe, the child doesn't need it + close(parent_pipe); + + // Only dup the child's stderr if there is error buffer + if (errorMessageBuffer != nullptr) + { + dup2(child_pipe, STDERR_FILENO); + } + // Execute the createdump program + if (execve(argv[0], (char* const *)argv, environ) == -1) + { + fprintf(stderr, "Problem launching createdump (may not have execute permissions): execve(%s) FAILED %s (%d)\n", argv[0], strerror(errno), errno); + exit(-1); + } + } + else + { +#if HAVE_PRCTL_H && HAVE_PR_SET_PTRACER + // Gives the child process permission to use /proc//mem and ptrace + if (prctl(PR_SET_PTRACER, childpid, 0, 0, 0) == -1) + { + // Ignore any error because on some CentOS and OpenSUSE distros, it isn't + // supported but createdump works just fine. + fprintf(stderr, "CreateCrashDump: prctl() FAILED %s (%d)\n", strerror(errno), errno); + } +#endif // HAVE_PRCTL_H && HAVE_PR_SET_PTRACER + close(child_pipe); + + // Read createdump's stderr messages (if any) + if (errorMessageBuffer != nullptr) + { + // Read createdump's stderr + int bytesRead = 0; + int count = 0; + while ((count = read(parent_pipe, errorMessageBuffer + bytesRead, cbErrorMessageBuffer - bytesRead)) > 0) + { + bytesRead += count; + } + errorMessageBuffer[bytesRead] = 0; + if (bytesRead > 0) + { + fputs(errorMessageBuffer, stderr); + } + } + close(parent_pipe); + + // Parent waits until the child process is done + int wstatus = 0; + int result = waitpid(childpid, &wstatus, 0); + if (result != childpid) + { + fprintf(stderr, "Problem waiting for createdump: waitpid() FAILED result %d wstatus %08x errno %s (%d)\n", + result, wstatus, strerror(errno), errno); + return false; + } + else + { +#ifdef _DEBUG + fprintf(stderr, "waitpid() returned successfully (wstatus %08x) WEXITSTATUS %x WTERMSIG %x\n", wstatus, WEXITSTATUS(wstatus), WTERMSIG(wstatus)); +#endif + return !WIFEXITED(wstatus) || WEXITSTATUS(wstatus) == 0; + } + } + return true; +} + +/*++ +Function: + PalCreateCrashDumpIfEnabled + + Creates crash dump of the process (if enabled). Can be called from the unhandled native exception handler. + +Parameters: + none + +(no return value) +--*/ +void +PalCreateCrashDumpIfEnabled() +{ + // If enabled, launch the create minidump utility and wait until it completes + if (g_argvCreateDump != nullptr) + { + CreateCrashDump(g_argvCreateDump, nullptr, 0); + } +} + +/*++ +Function: + PalGenerateCoreDump + +Abstract: + Public entry point to create a crash dump of the process. + +Parameters: + dumpName + dumpType: + Normal = 1, + WithHeap = 2, + Triage = 3, + Full = 4 + flags + See enum + +Return: + true success + false failed +--*/ +bool +PalGenerateCoreDump( + const char* dumpName, + int dumpType, + uint32_t flags, + char* errorMessageBuffer, + int cbErrorMessageBuffer) +{ + const char* argvCreateDump[MAX_ARGV_ENTRIES]; + if (dumpType < 1 || dumpType > 4) + { + return false; + } + if (dumpName != nullptr && dumpName[0] == '\0') + { + dumpName = nullptr; + } + bool result = BuildCreateDumpCommandLine(argvCreateDump, dumpName, nullptr, dumpType, flags); + if (result) + { + result = CreateCrashDump(argvCreateDump, errorMessageBuffer, cbErrorMessageBuffer); + } + return result; +} + +/*++ +Function + PalCreateDumpInitialize() + +Abstract + Initialize the process abort crash dump program file path and + name. Doing all of this ahead of time so nothing is allocated + or copied in abort/signal handler. + +Return + true - succeeds, false - fails + +--*/ +bool +PalCreateDumpInitialize() +{ + CLRConfigNoCache enabledCfg = CLRConfigNoCache::Get("DbgEnableMiniDump", /*noprefix*/ false, &getenv); + + uint32_t enabled = 0; + if (enabledCfg.IsSet() && enabledCfg.TryAsInteger(10, enabled) && enabled) + { + CLRConfigNoCache dmpNameCfg = CLRConfigNoCache::Get("DbgMiniDumpName", /*noprefix*/ false, &getenv); + const char* dumpName = dmpNameCfg.IsSet() ? dmpNameCfg.AsString() : nullptr; + + CLRConfigNoCache dmpLogToFileCfg = CLRConfigNoCache::Get("CreateDumpLogToFile", /*noprefix*/ false, &getenv); + const char* logFilePath = dmpLogToFileCfg.IsSet() ? dmpLogToFileCfg.AsString() : nullptr; + + CLRConfigNoCache dmpTypeCfg = CLRConfigNoCache::Get("DbgMiniDumpType", /*noprefix*/ false, &getenv); + uint32_t dumpType = UndefinedDumpType; + if (dmpTypeCfg.IsSet()) + { + (void)dmpTypeCfg.TryAsInteger(10, dumpType); + if (dumpType < 1 || dumpType > 4) + { + dumpType = UndefinedDumpType; + } + } + + uint32_t flags = GenerateDumpFlagsNone; + CLRConfigNoCache createDumpDiag = CLRConfigNoCache::Get("CreateDumpDiagnostics", /*noprefix*/ false, &getenv); + uint32_t val = 0; + if (createDumpDiag.IsSet() && createDumpDiag.TryAsInteger(10, val) && val == 1) + { + flags |= GenerateDumpFlagsLoggingEnabled; + } + CLRConfigNoCache createDumpVerboseDiag = CLRConfigNoCache::Get("CreateDumpVerboseDiagnostics", /*noprefix*/ false, &getenv); + val = 0; + if (createDumpVerboseDiag.IsSet() && createDumpVerboseDiag.TryAsInteger(10, val) && val == 1) + { + flags |= GenerateDumpFlagsVerboseLoggingEnabled; + } + CLRConfigNoCache enabledReportCfg = CLRConfigNoCache::Get("EnableCrashReport", /*noprefix*/ false, &getenv); + val = 0; + if (enabledReportCfg.IsSet() && enabledReportCfg.TryAsInteger(10, val) && val == 1) + { + flags |= GenerateDumpFlagsCrashReportEnabled; + } + CLRConfigNoCache enabledReportOnlyCfg = CLRConfigNoCache::Get("EnableCrashReportOnly", /*noprefix*/ false, &getenv); + val = 0; + if (enabledReportOnlyCfg.IsSet() && enabledReportOnlyCfg.TryAsInteger(10, val) && val == 1) + { + flags |= GenerateDumpFlagsCrashReportOnlyEnabled; + } + + // Build the createdump program path for the command line + Dl_info info; + if (dladdr((void*)&PalCreateDumpInitialize, &info) == 0) + { + return false; + } + const char* DumpGeneratorName = "createdump"; + int programLen = strlen(info.dli_fname) + strlen(DumpGeneratorName) + 1; + char* program = (char*)malloc(programLen); + if (program == nullptr) + { + return false; + } + strncpy(program, info.dli_fname, programLen); + char *last = strrchr(program, '/'); + if (last != nullptr) + { + *(last + 1) = '\0'; + } + else + { + program[0] = '\0'; + } + strncat(program, DumpGeneratorName, programLen); + g_szCreateDumpPath = program; + + // Format the app pid for the createdump command line + g_ppidarg = FormatInt(getpid()); + if (g_ppidarg == nullptr) + { + return false; + } + + if (!BuildCreateDumpCommandLine(g_argvCreateDump, dumpName, logFilePath, dumpType, flags)) + { + return false; + } + } + return true; +} + diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index fba1cb0e8c8af..f1e7550e56b00 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -85,9 +85,15 @@ static const int tccMilliSecondsToMicroSeconds = 1000; static const int tccMilliSecondsToNanoSeconds = 1000000; static const int tccMicroSecondsToNanoSeconds = 1000; +extern bool PalCreateDumpInitialize(); +extern void PalCreateCrashDumpIfEnabled(); + extern "C" void RaiseFailFastException(PEXCEPTION_RECORD arg1, PCONTEXT arg2, uint32_t arg3) { - // Abort aborts the process and causes creation of a crash dump + // Causes creation of a crash dump if enabled + PalCreateCrashDumpIfEnabled(); + + // Aborts the process abort(); } @@ -419,6 +425,11 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() ConfigureSignals(); + if (!PalCreateDumpInitialize()) + { + return false; + } + GCConfig::Initialize(); if (!GCToOSInterface::Initialize()) diff --git a/src/coreclr/nativeaot/Runtime/unix/config.h.in b/src/coreclr/nativeaot/Runtime/unix/config.h.in index c4e6dcdbda92e..75cb486352766 100644 --- a/src/coreclr/nativeaot/Runtime/unix/config.h.in +++ b/src/coreclr/nativeaot/Runtime/unix/config.h.in @@ -3,6 +3,8 @@ #cmakedefine01 HAVE_AUXV_HWCAP_H +#cmakedefine01 HAVE_PRCTL_H +#cmakedefine01 HAVE_PR_SET_PTRACER #cmakedefine01 HAVE_PTHREAD_ATTR_GET_NP #cmakedefine01 HAVE_PTHREAD_GETATTR_NP #cmakedefine01 HAVE_PTHREAD_CONDATTR_SETCLOCK diff --git a/src/coreclr/nativeaot/Runtime/unix/configure.cmake b/src/coreclr/nativeaot/Runtime/unix/configure.cmake index 798f6a178ee49..570a771869550 100644 --- a/src/coreclr/nativeaot/Runtime/unix/configure.cmake +++ b/src/coreclr/nativeaot/Runtime/unix/configure.cmake @@ -15,6 +15,10 @@ endif() list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_FILE_OFFSET_BITS=64) +check_include_files("sys/prctl.h" HAVE_PRCTL_H) +check_include_files("sys/ptrace.h" HAVE_SYS_PTRACE_H) +check_include_files("sys/auxv.h;asm/hwcap.h" HAVE_AUXV_HWCAP_H) + check_library_exists(pthread pthread_create "" HAVE_LIBPTHREAD) check_library_exists(c pthread_create "" HAVE_PTHREAD_IN_LIBC) @@ -75,6 +79,15 @@ int main() exit(ret); }" HAVE_CLOCK_MONOTONIC_COARSE) +check_cxx_source_compiles(" +#include + +int main(int argc, char **argv) +{ + int flag = (int)PR_SET_PTRACER; + return 0; +}" HAVE_PR_SET_PTRACER) + check_symbol_exists( clock_gettime_nsec_np time.h diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 6e903753f1a47..23e9cfdb529ac 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -30,6 +30,12 @@ public static partial class RuntimeImports [RuntimeImport(RuntimeLibrary, "RhGetCrashInfoBuffer")] internal static extern unsafe byte* RhGetCrashInfoBuffer(out int cbMaxSize); +#if TARGET_UNIX + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhCreateCrashDumpIfEnabled")] + internal static extern void RhCreateCrashDumpIfEnabled(); +#endif + [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetRuntimeVersion")] internal static extern unsafe byte* RhGetRuntimeVersion(out int cbLength); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs index 5c24bf0a8db78..a9e26cf0f6e5b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -255,6 +255,7 @@ internal static unsafe void FailFast(string? message, Exception? exception, RhFa #if TARGET_WINDOWS Interop.Kernel32.RaiseFailFastException(errorCode, pExAddress, pExContext, triageBufferAddress, triageBufferSize); #else + RuntimeImports.RhCreateCrashDumpIfEnabled(); Interop.Sys.Abort(); #endif } diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 494e5625ff966..aa775471f2a22 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -407,15 +407,7 @@ PALAPI PAL_SetCreateDumpCallback( IN PCREATEDUMP_CALLBACK callback); -// Must be the same as the copy in excep.h and the WriteDumpFlags enum in the diagnostics repo -enum -{ - GenerateDumpFlagsNone = 0x00, - GenerateDumpFlagsLoggingEnabled = 0x01, - GenerateDumpFlagsVerboseLoggingEnabled = 0x02, - GenerateDumpFlagsCrashReportEnabled = 0x04, - GenerateDumpFlagsCrashReportOnlyEnabled = 0x08 -}; +#include PALIMPORT BOOL diff --git a/src/coreclr/vm/excep.h b/src/coreclr/vm/excep.h index 75da016d6f569..9030331474c73 100644 --- a/src/coreclr/vm/excep.h +++ b/src/coreclr/vm/excep.h @@ -194,17 +194,7 @@ enum UnhandledExceptionLocation }; #ifdef HOST_WINDOWS - -// Must be the same as the copy in pal.h and the WriteDumpFlags enum in the diagnostics repo -enum -{ - GenerateDumpFlagsNone = 0x00, - GenerateDumpFlagsLoggingEnabled = 0x01, - GenerateDumpFlagsVerboseLoggingEnabled = 0x02, - GenerateDumpFlagsCrashReportEnabled = 0x04, - GenerateDumpFlagsCrashReportOnlyEnabled = 0x08 -}; - +#include void InitializeCrashDump(); void CreateCrashDumpIfEnabled(bool stackoverflow = false); #endif diff --git a/src/coreclr/inc/clrconfignocache.h b/src/native/inc/clrconfignocache.h similarity index 76% rename from src/coreclr/inc/clrconfignocache.h rename to src/native/inc/clrconfignocache.h index d15a566e9c989..5693992f70957 100644 --- a/src/coreclr/inc/clrconfignocache.h +++ b/src/native/inc/clrconfignocache.h @@ -7,7 +7,7 @@ // Logic for resolving configuration names. // -#include +#include // Config prefixes #define COMPLUS_PREFIX_A "COMPlus_" @@ -23,30 +23,30 @@ class CLRConfigNoCache const char* _value; CLRConfigNoCache() = default; - CLRConfigNoCache(LPCSTR cfg) : _value { cfg } + CLRConfigNoCache(const char* cfg) : _value { cfg } { } public: bool IsSet() const { return _value != NULL; } - LPCSTR AsString() const + const char* AsString() const { _ASSERTE(IsSet()); return _value; } - bool TryAsInteger(int radix, DWORD& result) const + bool TryAsInteger(int radix, uint32_t& result) const { _ASSERTE(IsSet()); errno = 0; - LPSTR endPtr; + char* endPtr; result = strtoul(_value, &endPtr, radix); bool fSuccess = (errno != ERANGE) && (endPtr != _value); return fSuccess; } - static CLRConfigNoCache Get(LPCSTR cfg, bool noPrefix = false, char*(*getEnvFptr)(const char*) = nullptr) + static CLRConfigNoCache Get(const char* cfg, bool noPrefix = false, char*(*getEnvFptr)(const char*) = nullptr) { char nameBuffer[64]; const char* fallbackPrefix = NULL; @@ -73,17 +73,17 @@ class CLRConfigNoCache } // Priority order is DOTNET_ and then COMPlus_. - strcpy_s(nameBuffer, ARRAY_SIZE(nameBuffer), DOTNET_PREFIX_A); + strcpy(nameBuffer, DOTNET_PREFIX_A); fallbackPrefix = COMPLUS_PREFIX_A; } - strcat_s(nameBuffer, ARRAY_SIZE(nameBuffer), cfg); + strcat(nameBuffer, cfg); - LPCSTR val = getEnvFptr != NULL ? getEnvFptr(nameBuffer) : getenv(nameBuffer); + const char* val = getEnvFptr != NULL ? getEnvFptr(nameBuffer) : getenv(nameBuffer); if (val == NULL && fallbackPrefix != NULL) { - strcpy_s(nameBuffer, ARRAY_SIZE(nameBuffer), fallbackPrefix); - strcat_s(nameBuffer, ARRAY_SIZE(nameBuffer), cfg); + strcpy(nameBuffer, fallbackPrefix); + strcat(nameBuffer, cfg); val = getEnvFptr != NULL ? getEnvFptr(nameBuffer) : getenv(nameBuffer); } diff --git a/src/native/inc/generatedumpflags.h b/src/native/inc/generatedumpflags.h new file mode 100644 index 0000000000000..e0da616d0ffbd --- /dev/null +++ b/src/native/inc/generatedumpflags.h @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// -------------------------------------------------------------------------------------------------- +// generatedumpflags.h +// +// Dump generation flags +// +// Must be the same as the WriteDumpFlags enum in the diagnostics repo + +#pragma once + +enum GenerateDumpFlags +{ + GenerateDumpFlagsNone = 0x00, + GenerateDumpFlagsLoggingEnabled = 0x01, + GenerateDumpFlagsVerboseLoggingEnabled = 0x02, + GenerateDumpFlagsCrashReportEnabled = 0x04, + GenerateDumpFlagsCrashReportOnlyEnabled = 0x08 +}; From f6888dc8a2231086b30865bf17ddde9f4f3993fb Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Wed, 19 Jul 2023 17:19:57 -0700 Subject: [PATCH 02/15] Add building more command line arguments for signal number, etc. Fix some build problems. --- .../nativeaot/Runtime/eventpipe/ds-rt-aot.cpp | 18 --- .../nativeaot/Runtime/eventpipe/ds-rt-aot.h | 11 +- .../nativeaot/Runtime/unix/PalCreateDump.cpp | 122 ++++++++++++++++-- src/coreclr/pal/inc/pal.h | 2 - src/coreclr/pal/src/thread/process.cpp | 1 + .../vm/eventing/eventpipe/ds-rt-coreclr.h | 1 + src/coreclr/vm/gcenv.ee.cpp | 1 + 7 files changed, 121 insertions(+), 35 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp index e085957ac8503..a411cf71a84d9 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp @@ -249,22 +249,4 @@ ds_rt_aot_set_environment_variable (const ep_char16_t *name, const ep_char16_t * #endif } -bool -ds_rt_aot_generate_core_dump ( - const ep_char16_t* dumpName, - int32_t dumpType, - uint32_t flags, - ep_char8_t *errorMessageBuffer, - int32_t cbErrorMessageBuffer) -{ -#ifdef TARGET_UNIX - ep_char8_t *dumpNameUtf8 = ep_rt_utf16le_to_utf8_string (dumpName, ep_rt_utf16_string_len (dumpName)); - extern bool PalGenerateCoreDump(const char* dumpName, int dumpType, uint32_t flags, char* errorMessageBuffer, int cbErrorMessageBuffer); - return PalGenerateCoreDump(dumpNameUtf8, dumpType, flags, errorMessageBuffer, cbErrorMessageBuffer); -#else - return false; -#endif -} - - #endif /* ENABLE_PERFTRACING */ diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h index f36edf3ed6332..7d19d7031cc66 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h @@ -191,14 +191,15 @@ ds_rt_generate_core_dump ( } const ep_char16_t *dumpName = ds_generate_core_dump_command_payload_get_dump_name (payload); int32_t dumpType = static_cast(ds_generate_core_dump_command_payload_get_dump_type (payload)); - - extern bool ds_rt_aot_generate_core_dump (const ep_char16_t* dumpName, int32_t dumpType, uint32_t flags, ep_char8_t *errorMessageBuffer, int32_t cbErrorMessageBuffer); - if (ds_rt_aot_generate_core_dump(dumpName, dumpType, flags, errorMessageBuffer, cbErrorMessageBuffer)) +#ifdef TARGET_UNIX + ep_char8_t *dumpNameUtf8 = ep_rt_utf16le_to_utf8_string (dumpName, ep_rt_utf16_string_len (dumpName)); + extern bool PalGenerateCoreDump(const char* dumpName, int dumpType, uint32_t flags, char* errorMessageBuffer, int cbErrorMessageBuffer); + if (PalGenerateCoreDump(dumpNameUtf8, dumpType, flags, errorMessageBuffer, cbErrorMessageBuffer)) { result = DS_IPC_S_OK; } - - return 0; +#endif + return result; } /* diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index b7ee0a4fdb327..83bccdb1b0d43 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -13,6 +13,8 @@ #include #include #include +#define __STDC_FORMAT_MACROS +#include #include #include #include @@ -50,12 +52,38 @@ #include #include -// Crash dump generating program arguments -#define MAX_ARGV_ENTRIES 16 -const char* g_argvCreateDump[MAX_ARGV_ENTRIES]; +// Crash dump generating program arguments. MAX_ARGV_ENTRIES is the max number +// of entries if every createdump option/argument is passed. +#define MAX_ARGV_ENTRIES 32 +const char* g_argvCreateDump[MAX_ARGV_ENTRIES] = { nullptr }; char* g_szCreateDumpPath = nullptr; char* g_ppidarg = nullptr; +/*++ +Function: + PlatformGetCurrentThreadId + + Returns the current thread id +*/ + +#if defined(__linux__) +#define PlatformGetCurrentThreadId() (uint32_t)syscall(SYS_gettid) +#elif defined(__APPLE__) +inline uint32_t PlatformGetCurrentThreadId() { + uint64_t tid; + pthread_threadid_np(pthread_self(), &tid); + return (SIZE_T)tid; +} +#elif defined(__FreeBSD__) +#include +#define PlatformGetCurrentThreadId() (uint32_t)pthread_getthreadid_np() +#elif defined(__NetBSD__) +#include +#define PlatformGetCurrentThreadId() (uint32_t)_lwp_self() +#else +#define PlatformGetCurrentThreadId() (uint32_t)pthread_self() +#endif + /*++ Function: FormatInt @@ -83,7 +111,7 @@ FormatInt(uint32_t value) Function: FormatInt64 - Helper function to format an ULONG64 as a string. + Helper function to format an uint64 as a string. --*/ static @@ -93,7 +121,7 @@ FormatInt64(uint64_t value) char* buffer = (char*)malloc(128); if (buffer != nullptr) { - if (snprintf(buffer, 128, "%lu", value) < 0) + if (snprintf(buffer, 128, "%" PRIu64, value) < 0) { free(buffer); buffer = nullptr; @@ -303,20 +331,95 @@ CreateCrashDump( Creates crash dump of the process (if enabled). Can be called from the unhandled native exception handler. Parameters: - none + signal - POSIX signal number or 0 + siginfo - signal info or nullptr (no return value) --*/ void -PalCreateCrashDumpIfEnabled() +PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo) { // If enabled, launch the create minidump utility and wait until it completes - if (g_argvCreateDump != nullptr) + if (g_argvCreateDump[0] != nullptr) { - CreateCrashDump(g_argvCreateDump, nullptr, 0); + const char* argv[MAX_ARGV_ENTRIES]; + char* signalArg = nullptr; + char* crashThreadArg = nullptr; + char* signalCodeArg = nullptr; + char* signalErrnoArg = nullptr; + char* signalAddressArg = nullptr; + + // Copy the createdump argv + int argc = 0; + for (; argc < MAX_ARGV_ENTRIES; argc++) + { + argv[argc] = g_argvCreateDump[argc]; + if (g_argvCreateDump[argc] == nullptr) + { + break; + } + } + + if (signal != 0) + { + // Add the signal number to the command line + signalArg = FormatInt(signal); + if (signalArg != nullptr) + { + argv[argc++] = "--signal"; + argv[argc++] = signalArg; + } + + // Add the current thread id to the command line. This function is always called on the crashing thread. + crashThreadArg = FormatInt(PlatformGetCurrentThreadId()); + if (crashThreadArg != nullptr) + { + argv[argc++] = "--crashthread"; + argv[argc++] = crashThreadArg; + } + + if (siginfo != nullptr) + { + signalCodeArg = FormatInt(siginfo->si_code); + if (signalCodeArg != nullptr) + { + argv[argc++] = "--code"; + argv[argc++] = signalCodeArg; + } + signalErrnoArg = FormatInt(siginfo->si_errno); + if (signalErrnoArg != nullptr) + { + argv[argc++] = "--errno"; + argv[argc++] = signalErrnoArg; + } + signalAddressArg = FormatInt64((uint64_t)siginfo->si_addr); + if (signalAddressArg != nullptr) + { + argv[argc++] = "--address"; + argv[argc++] = signalAddressArg; + } + } + + argv[argc++] = nullptr; + _ASSERTE(argc < MAX_ARGV_ENTRIES); + } + + CreateCrashDump(argv, nullptr, 0); + + free(signalArg); + free(crashThreadArg); + free(signalCodeArg); + free(signalErrnoArg); + free(signalAddressArg); } } +void +PalCreateCrashDumpIfEnabled() +{ + PalCreateCrashDumpIfEnabled(SIGABRT, nullptr); +} + /*++ Function: PalGenerateCoreDump @@ -467,4 +570,3 @@ PalCreateDumpInitialize() } return true; } - diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index aa775471f2a22..39c289c553a80 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -407,8 +407,6 @@ PALAPI PAL_SetCreateDumpCallback( IN PCREATEDUMP_CALLBACK callback); -#include - PALIMPORT BOOL PALAPI diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index a3cb53270b1fc..8fb8335307642 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -33,6 +33,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d #include "pal/stackstring.hpp" #include "pal/signal.hpp" +#include #include #include diff --git a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h index 94c6d97216c8b..b239592683640 100644 --- a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h @@ -10,6 +10,7 @@ #ifdef ENABLE_PERFTRACING #include "ep-rt-coreclr.h" #include +#include #include #include #include diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index ad508dbfb7c3a..dc28e791de0cf 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -10,6 +10,7 @@ * */ +#include #include "gcrefmap.h" void GCToEEInterface::SuspendEE(SUSPEND_REASON reason) From 813d29fd18f1bd07dde7d7f737218eac56127d00 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Wed, 19 Jul 2023 22:19:02 -0700 Subject: [PATCH 03/15] Code review feedback - use RhConfig instead of clrconfignocache.h --- .../inc/clrconfignocache.h | 22 +++--- .../nativeaot/Runtime/unix/PalCreateDump.cpp | 78 +++++++++---------- 2 files changed, 49 insertions(+), 51 deletions(-) rename src/{native => coreclr}/inc/clrconfignocache.h (76%) diff --git a/src/native/inc/clrconfignocache.h b/src/coreclr/inc/clrconfignocache.h similarity index 76% rename from src/native/inc/clrconfignocache.h rename to src/coreclr/inc/clrconfignocache.h index 5693992f70957..d15a566e9c989 100644 --- a/src/native/inc/clrconfignocache.h +++ b/src/coreclr/inc/clrconfignocache.h @@ -7,7 +7,7 @@ // Logic for resolving configuration names. // -#include +#include // Config prefixes #define COMPLUS_PREFIX_A "COMPlus_" @@ -23,30 +23,30 @@ class CLRConfigNoCache const char* _value; CLRConfigNoCache() = default; - CLRConfigNoCache(const char* cfg) : _value { cfg } + CLRConfigNoCache(LPCSTR cfg) : _value { cfg } { } public: bool IsSet() const { return _value != NULL; } - const char* AsString() const + LPCSTR AsString() const { _ASSERTE(IsSet()); return _value; } - bool TryAsInteger(int radix, uint32_t& result) const + bool TryAsInteger(int radix, DWORD& result) const { _ASSERTE(IsSet()); errno = 0; - char* endPtr; + LPSTR endPtr; result = strtoul(_value, &endPtr, radix); bool fSuccess = (errno != ERANGE) && (endPtr != _value); return fSuccess; } - static CLRConfigNoCache Get(const char* cfg, bool noPrefix = false, char*(*getEnvFptr)(const char*) = nullptr) + static CLRConfigNoCache Get(LPCSTR cfg, bool noPrefix = false, char*(*getEnvFptr)(const char*) = nullptr) { char nameBuffer[64]; const char* fallbackPrefix = NULL; @@ -73,17 +73,17 @@ class CLRConfigNoCache } // Priority order is DOTNET_ and then COMPlus_. - strcpy(nameBuffer, DOTNET_PREFIX_A); + strcpy_s(nameBuffer, ARRAY_SIZE(nameBuffer), DOTNET_PREFIX_A); fallbackPrefix = COMPLUS_PREFIX_A; } - strcat(nameBuffer, cfg); + strcat_s(nameBuffer, ARRAY_SIZE(nameBuffer), cfg); - const char* val = getEnvFptr != NULL ? getEnvFptr(nameBuffer) : getenv(nameBuffer); + LPCSTR val = getEnvFptr != NULL ? getEnvFptr(nameBuffer) : getenv(nameBuffer); if (val == NULL && fallbackPrefix != NULL) { - strcpy(nameBuffer, fallbackPrefix); - strcat(nameBuffer, cfg); + strcpy_s(nameBuffer, ARRAY_SIZE(nameBuffer), fallbackPrefix); + strcat_s(nameBuffer, ARRAY_SIZE(nameBuffer), cfg); val = getEnvFptr != NULL ? getEnvFptr(nameBuffer) : getenv(nameBuffer); } diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index 83bccdb1b0d43..b8212a126486d 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -5,13 +5,10 @@ #include #include #include -#include #include #include "config.h" -#include #include -#include -#include +#include #include #define __STDC_FORMAT_MACROS #include @@ -47,9 +44,9 @@ #include #endif -#define _ASSERTE(x) +#define _T(s) s +#include "RhConfig.h" -#include #include // Crash dump generating program arguments. MAX_ARGV_ENTRIES is the max number @@ -72,7 +69,7 @@ char* g_ppidarg = nullptr; inline uint32_t PlatformGetCurrentThreadId() { uint64_t tid; pthread_threadid_np(pthread_self(), &tid); - return (SIZE_T)tid; + return (uint32_t)tid; } #elif defined(__FreeBSD__) #include @@ -209,7 +206,7 @@ BuildCreateDumpCommandLine( argv[argc++] = "--nativeaot"; argv[argc++] = g_ppidarg; argv[argc++] = nullptr; - _ASSERTE(argc < MAX_ARGV_ENTRIES); + assert(argc < MAX_ARGV_ENTRIES); return true; } @@ -267,9 +264,9 @@ CreateCrashDump( dup2(child_pipe, STDERR_FILENO); } // Execute the createdump program - if (execve(argv[0], (char* const *)argv, environ) == -1) + if (execv(argv[0], (char* const *)argv) == -1) { - fprintf(stderr, "Problem launching createdump (may not have execute permissions): execve(%s) FAILED %s (%d)\n", argv[0], strerror(errno), errno); + fprintf(stderr, "Problem launching createdump (may not have execute permissions): execv(%s) FAILED %s (%d)\n", argv[0], strerror(errno), errno); exit(-1); } } @@ -401,7 +398,7 @@ PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo) } argv[argc++] = nullptr; - _ASSERTE(argc < MAX_ARGV_ENTRIES); + assert(argc < MAX_ARGV_ENTRIES); } CreateCrashDump(argv, nullptr, 0); @@ -482,52 +479,53 @@ Return bool PalCreateDumpInitialize() { - CLRConfigNoCache enabledCfg = CLRConfigNoCache::Get("DbgEnableMiniDump", /*noprefix*/ false, &getenv); - - uint32_t enabled = 0; - if (enabledCfg.IsSet() && enabledCfg.TryAsInteger(10, enabled) && enabled) + bool enabled = false; + RhConfig::Environment::TryGetBooleanValue("DbgEnableMiniDump", &enabled); + if (enabled) { - CLRConfigNoCache dmpNameCfg = CLRConfigNoCache::Get("DbgMiniDumpName", /*noprefix*/ false, &getenv); - const char* dumpName = dmpNameCfg.IsSet() ? dmpNameCfg.AsString() : nullptr; + char* dumpName = nullptr; + RhConfig::Environment::TryGetStringValue("DbgMiniDumpName", &dumpName); - CLRConfigNoCache dmpLogToFileCfg = CLRConfigNoCache::Get("CreateDumpLogToFile", /*noprefix*/ false, &getenv); - const char* logFilePath = dmpLogToFileCfg.IsSet() ? dmpLogToFileCfg.AsString() : nullptr; + char* logFilePath = nullptr; + RhConfig::Environment::TryGetStringValue("CreateDumpLogToFile", &logFilePath); - CLRConfigNoCache dmpTypeCfg = CLRConfigNoCache::Get("DbgMiniDumpType", /*noprefix*/ false, &getenv); - uint32_t dumpType = UndefinedDumpType; - if (dmpTypeCfg.IsSet()) + uint64_t dumpType = UndefinedDumpType; + if (RhConfig::Environment::TryGetIntegerValue("DbgMiniDumpType", &dumpType, true)) { - (void)dmpTypeCfg.TryAsInteger(10, dumpType); if (dumpType < 1 || dumpType > 4) { dumpType = UndefinedDumpType; } } - uint32_t flags = GenerateDumpFlagsNone; - CLRConfigNoCache createDumpDiag = CLRConfigNoCache::Get("CreateDumpDiagnostics", /*noprefix*/ false, &getenv); - uint32_t val = 0; - if (createDumpDiag.IsSet() && createDumpDiag.TryAsInteger(10, val) && val == 1) + bool value = false; + if (RhConfig::Environment::TryGetBooleanValue("CreateDumpDiagnostics", &value)) { - flags |= GenerateDumpFlagsLoggingEnabled; + if (value) + { + flags |= GenerateDumpFlagsLoggingEnabled; + } } - CLRConfigNoCache createDumpVerboseDiag = CLRConfigNoCache::Get("CreateDumpVerboseDiagnostics", /*noprefix*/ false, &getenv); - val = 0; - if (createDumpVerboseDiag.IsSet() && createDumpVerboseDiag.TryAsInteger(10, val) && val == 1) + if (RhConfig::Environment::TryGetBooleanValue("CreateDumpVerboseDiagnostics", &value)) { - flags |= GenerateDumpFlagsVerboseLoggingEnabled; + if (value) + { + flags |= GenerateDumpFlagsVerboseLoggingEnabled; + } } - CLRConfigNoCache enabledReportCfg = CLRConfigNoCache::Get("EnableCrashReport", /*noprefix*/ false, &getenv); - val = 0; - if (enabledReportCfg.IsSet() && enabledReportCfg.TryAsInteger(10, val) && val == 1) + if (RhConfig::Environment::TryGetBooleanValue("EnableCrashReport", &value)) { - flags |= GenerateDumpFlagsCrashReportEnabled; + if (value) + { + flags |= GenerateDumpFlagsCrashReportEnabled; + } } - CLRConfigNoCache enabledReportOnlyCfg = CLRConfigNoCache::Get("EnableCrashReportOnly", /*noprefix*/ false, &getenv); - val = 0; - if (enabledReportOnlyCfg.IsSet() && enabledReportOnlyCfg.TryAsInteger(10, val) && val == 1) + if (RhConfig::Environment::TryGetBooleanValue("EnableCrashReportOnly", &value)) { - flags |= GenerateDumpFlagsCrashReportOnlyEnabled; + if (value) + { + flags |= GenerateDumpFlagsCrashReportOnlyEnabled; + } } // Build the createdump program path for the command line From 4b4b7326990659069b5c92b3d571eceb194f97e7 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Wed, 19 Jul 2023 23:50:04 -0700 Subject: [PATCH 04/15] Fix OSX builds --- src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index b8212a126486d..a9b969d5aaeeb 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -7,6 +7,7 @@ #include #include #include "config.h" +#include #include #include #include From 0446824169155fab8ab9a6906b92f09f3aa7f5b2 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Thu, 20 Jul 2023 11:21:19 -0700 Subject: [PATCH 05/15] Code review feedback --- .../nativeaot/Runtime/unix/PalCreateDump.cpp | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index a9b969d5aaeeb..79b9599d55f75 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #ifdef __APPLE__ @@ -48,6 +49,7 @@ #define _T(s) s #include "RhConfig.h" +#include #include // Crash dump generating program arguments. MAX_ARGV_ENTRIES is the max number @@ -82,6 +84,9 @@ inline uint32_t PlatformGetCurrentThreadId() { #define PlatformGetCurrentThreadId() (uint32_t)pthread_self() #endif +const size_t MaxUnsigned32BitDecString = ARRAY_SIZE("4294967295") - 1; +const size_t MaxUnsigned64BitDecString = ARRAY_SIZE("18446744073709551615") - 1; + /*++ Function: FormatInt @@ -93,10 +98,10 @@ static char* FormatInt(uint32_t value) { - char* buffer = (char*)malloc(128); + char* buffer = (char*)malloc(MaxUnsigned32BitDecString); if (buffer != nullptr) { - if (snprintf(buffer, 128, "%d", value) < 0) + if (snprintf(buffer, MaxUnsigned32BitDecString, "%" PRIu32, value) < 0) { free(buffer); buffer = nullptr; @@ -116,10 +121,10 @@ static char* FormatInt64(uint64_t value) { - char* buffer = (char*)malloc(128); + char* buffer = (char*)malloc(MaxUnsigned64BitDecString); if (buffer != nullptr) { - if (snprintf(buffer, 128, "%" PRIu64, value) < 0) + if (snprintf(buffer, MaxUnsigned64BitDecString, "%" PRIu64, value) < 0) { free(buffer); buffer = nullptr; @@ -553,6 +558,13 @@ PalCreateDumpInitialize() program[0] = '\0'; } strncat(program, DumpGeneratorName, programLen); + + struct stat fileData; + if (stat(program, &fileData) == -1 || !S_ISREG(fileData.st_mode)) + { + fprintf(stderr, "DOTNET_DbgEnableMiniDump is set and the createdump binary does not exist: %s\n", program); + return false; + } g_szCreateDumpPath = program; // Format the app pid for the createdump command line From 59fd6e4e009bf5a454b1f01843c9cb4c21dd48df Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Thu, 20 Jul 2023 17:04:58 -0700 Subject: [PATCH 06/15] Fix string buffer length --- src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index 79b9599d55f75..bd951a0c30509 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -84,8 +84,8 @@ inline uint32_t PlatformGetCurrentThreadId() { #define PlatformGetCurrentThreadId() (uint32_t)pthread_self() #endif -const size_t MaxUnsigned32BitDecString = ARRAY_SIZE("4294967295") - 1; -const size_t MaxUnsigned64BitDecString = ARRAY_SIZE("18446744073709551615") - 1; +const size_t MaxUnsigned32BitDecString = STRING_LENGTH("4294967295"); +const size_t MaxUnsigned64BitDecString = STRING_LENGTH("18446744073709551615"); /*++ Function: @@ -98,7 +98,7 @@ static char* FormatInt(uint32_t value) { - char* buffer = (char*)malloc(MaxUnsigned32BitDecString); + char* buffer = (char*)malloc(MaxUnsigned32BitDecString + 1); if (buffer != nullptr) { if (snprintf(buffer, MaxUnsigned32BitDecString, "%" PRIu32, value) < 0) @@ -121,7 +121,7 @@ static char* FormatInt64(uint64_t value) { - char* buffer = (char*)malloc(MaxUnsigned64BitDecString); + char* buffer = (char*)malloc(MaxUnsigned64BitDecString + 1); if (buffer != nullptr) { if (snprintf(buffer, MaxUnsigned64BitDecString, "%" PRIu64, value) < 0) From 31b37464b68b01883e43c4f0d750ef3b43285ca9 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Thu, 20 Jul 2023 18:22:20 -0700 Subject: [PATCH 07/15] More code review feedback --- .../nativeaot/Runtime/eventpipe/ds-rt-aot.h | 2 +- .../nativeaot/Runtime/unix/PalCreateDump.cpp | 29 +++++++++++-------- src/coreclr/pal/src/thread/process.cpp | 23 ++++++++------- src/native/inc/generatedumpflags.h | 13 ++++++++- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h index 7d19d7031cc66..98f065968e6fc 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.h @@ -183,6 +183,7 @@ ds_rt_generate_core_dump ( STATIC_CONTRACT_NOTHROW; ds_ipc_result_t result = DS_IPC_E_FAIL; +#ifdef TARGET_UNIX uint32_t flags = ds_generate_core_dump_command_payload_get_flags(payload); if (commandId == DS_DUMP_COMMANDID_GENERATE_CORE_DUMP) { @@ -191,7 +192,6 @@ ds_rt_generate_core_dump ( } const ep_char16_t *dumpName = ds_generate_core_dump_command_payload_get_dump_name (payload); int32_t dumpType = static_cast(ds_generate_core_dump_command_payload_get_dump_type (payload)); -#ifdef TARGET_UNIX ep_char8_t *dumpNameUtf8 = ep_rt_utf16le_to_utf8_string (dumpName, ep_rt_utf16_string_len (dumpName)); extern bool PalGenerateCoreDump(const char* dumpName, int dumpType, uint32_t flags, char* errorMessageBuffer, int cbErrorMessageBuffer); if (PalGenerateCoreDump(dumpNameUtf8, dumpType, flags, errorMessageBuffer, cbErrorMessageBuffer)) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index bd951a0c30509..8fc9d7b1b21f8 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -133,8 +133,6 @@ FormatInt64(uint64_t value) return buffer; } -static const int UndefinedDumpType = 0; - /*++ Function BuildCreateDumpCommandLine @@ -171,13 +169,17 @@ BuildCreateDumpCommandLine( switch (dumpType) { - case 1: argv[argc++] = "--normal"; + case DumpTypeNormal: + argv[argc++] = "--normal"; break; - case 2: argv[argc++] = "--withheap"; + case DumpTypeWithHeap: + argv[argc++] = "--withheap"; break; - case 3: argv[argc++] = "--triage"; + case DumpTypeTriage: + argv[argc++] = "--triage"; break; - case 4: argv[argc++] = "--full"; + case DumpTypeFull: + argv[argc++] = "--full"; break; default: break; @@ -212,8 +214,11 @@ BuildCreateDumpCommandLine( argv[argc++] = "--nativeaot"; argv[argc++] = g_ppidarg; argv[argc++] = nullptr; - assert(argc < MAX_ARGV_ENTRIES); + if (argc >= MAX_ARGV_ENTRIES) + { + return false; + } return true; } @@ -363,7 +368,7 @@ PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo) } } - if (signal != 0) + if (signal != 0 && argc < MAX_ARGV_ENTRIES) { // Add the signal number to the command line signalArg = FormatInt(signal); @@ -453,7 +458,7 @@ PalGenerateCoreDump( int cbErrorMessageBuffer) { const char* argvCreateDump[MAX_ARGV_ENTRIES]; - if (dumpType < 1 || dumpType > 4) + if (dumpType <= DumpTypeUnknown || dumpType > DumpTypeMax) { return false; } @@ -495,12 +500,12 @@ PalCreateDumpInitialize() char* logFilePath = nullptr; RhConfig::Environment::TryGetStringValue("CreateDumpLogToFile", &logFilePath); - uint64_t dumpType = UndefinedDumpType; + uint64_t dumpType = DumpTypeUnknown; if (RhConfig::Environment::TryGetIntegerValue("DbgMiniDumpType", &dumpType, true)) { - if (dumpType < 1 || dumpType > 4) + if (dumpType <= DumpTypeUnknown || dumpType > DumpTypeMax) { - dumpType = UndefinedDumpType; + dumpType = DumpTypeUnknown; } } uint32_t flags = GenerateDumpFlagsNone; diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 8fb8335307642..dcfc4c936f514 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2054,8 +2054,6 @@ PROCFormatInt64(ULONG64 value) return buffer; } -static const INT UndefinedDumpType = 0; - /*++ Function PROCBuildCreateDumpCommandLine @@ -2120,15 +2118,18 @@ PROCBuildCreateDumpCommandLine( switch (dumpType) { - case 1: argv.push_back("--normal"); + case DumpTypeNormal: + argv.push_back("--normal"); break; - case 2: argv.push_back("--withheap"); + case DumpTypeWithHeap: + argv.push_back("--withheap"); break; - case 3: argv.push_back("--triage"); + case DumpTypeTriage: + argv.push_back("--triage"); break; - case 4: argv.push_back("--full"); + case DumpTypeFull: + argv.push_back("--full"); break; - case UndefinedDumpType: default: break; } @@ -2322,13 +2323,13 @@ PROCAbortInitialize() const char* logFilePath = dmpLogToFileCfg.IsSet() ? dmpLogToFileCfg.AsString() : nullptr; CLRConfigNoCache dmpTypeCfg = CLRConfigNoCache::Get("DbgMiniDumpType", /*noprefix*/ false, &getenv); - DWORD dumpType = UndefinedDumpType; + DWORD dumpType = DumpTypeUnknown; if (dmpTypeCfg.IsSet()) { (void)dmpTypeCfg.TryAsInteger(10, dumpType); - if (dumpType < 1 || dumpType > 4) + if (dumpType <= DumpTypeUnknown || dumpType > DumpTypeMax) { - dumpType = UndefinedDumpType; + dumpType = DumpTypeUnknown; } } @@ -2399,7 +2400,7 @@ PAL_GenerateCoreDump( { std::vector argvCreateDump; - if (dumpType < 1 || dumpType > 4) + if (dumpType <= DumpTypeUnknown || dumpType > DumpTypeMax) { return FALSE; } diff --git a/src/native/inc/generatedumpflags.h b/src/native/inc/generatedumpflags.h index e0da616d0ffbd..34926376e969a 100644 --- a/src/native/inc/generatedumpflags.h +++ b/src/native/inc/generatedumpflags.h @@ -6,10 +6,10 @@ // // Dump generation flags // -// Must be the same as the WriteDumpFlags enum in the diagnostics repo #pragma once +// Must be the same as the WriteDumpFlags enum in the diagnostics repo enum GenerateDumpFlags { GenerateDumpFlagsNone = 0x00, @@ -18,3 +18,14 @@ enum GenerateDumpFlags GenerateDumpFlagsCrashReportEnabled = 0x04, GenerateDumpFlagsCrashReportOnlyEnabled = 0x08 }; + +// Must be the same as the DumpType enum in the diagnostics repo +enum DumpType +{ + DumpTypeUnknown = 0, + DumpTypeNormal = 1, + DumpTypeWithHeap = 2, + DumpTypeTriage = 3, + DumpTypeFull = 4, + DumpTypeMax = 4 +}; From d803aa352c194b676a20b1e9558c64949845a45c Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Fri, 21 Jul 2023 17:54:22 -0700 Subject: [PATCH 08/15] Fix nativeaot test failures. Added crash buffer address to debug header contract --- src/coreclr/nativeaot/Runtime/DebugHeader.cpp | 13 +++++++++++++ .../nativeaot/Runtime/unix/PalCreateDump.cpp | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/Runtime/DebugHeader.cpp b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp index 24c5bc58bb8f4..c0ce4ca7babda 100644 --- a/src/coreclr/nativeaot/Runtime/DebugHeader.cpp +++ b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp @@ -17,6 +17,7 @@ #include "thread.h" #include "threadstore.h" +extern uint8_t* g_CrashInfoBuffer; GPTR_DECL(MethodTable, g_pFreeObjectEEType); struct DebugTypeEntry @@ -195,6 +196,11 @@ extern "C" void PopulateDebugHeaders() MAKE_DEBUG_FIELD_ENTRY(ThreadBuffer, m_rgbAllocContextBuffer); MAKE_DEBUG_FIELD_ENTRY(ThreadBuffer, m_threadId); MAKE_DEBUG_FIELD_ENTRY(ThreadBuffer, m_pThreadStressLog); + MAKE_DEBUG_FIELD_ENTRY(ThreadBuffer, m_pExInfoStackHead); + + MAKE_SIZE_ENTRY(ExInfo); + MAKE_DEBUG_FIELD_ENTRY(ExInfo, m_pPrevExInfo); + MAKE_DEBUG_FIELD_ENTRY(ExInfo, m_exception); MAKE_SIZE_ENTRY(MethodTable); MAKE_DEBUG_FIELD_ENTRY(MethodTable, m_uBaseSize); @@ -244,6 +250,8 @@ extern "C" void PopulateDebugHeaders() MAKE_SIZE_ENTRY(RuntimeInstance); MAKE_DEBUG_FIELD_ENTRY(RuntimeInstance, m_pThreadStore); + MAKE_GLOBAL_ENTRY(g_CrashInfoBuffer); + RuntimeInstance *g_pTheRuntimeInstance = GetRuntimeInstance(); MAKE_GLOBAL_ENTRY(g_pTheRuntimeInstance); @@ -269,4 +277,9 @@ extern "C" void PopulateDebugHeaders() static_assert(MethodTable::Flags::IsGenericFlag == 0x02000000, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); static_assert(MethodTable::Flags::ElementTypeMask == 0x7C000000, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); static_assert(MethodTable::Flags::ElementTypeShift == 26, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Flags::HasComponentSizeFlag == 0x80000000, "The debugging data contract has a hard coded dependency on this value of MethodTable::Flags. If you change this value you must bump major_version_number."); + + static_assert(MethodTable::Kinds::CanonicalEEType == 0x00000000, "The debugging data contract has a hard coded dependency on this value of MethodTable::Kinds. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Kinds::ParameterizedEEType == 0x00020000, "The debugging data contract has a hard coded dependency on this value of MethodTable::Kinds. If you change this value you must bump major_version_number."); + static_assert(MethodTable::Kinds::GenericTypeDefEEType == 0x00030000, "The debugging data contract has a hard coded dependency on this value of MethodTable::Kinds. If you change this value you must bump major_version_number."); } diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index 8fc9d7b1b21f8..c168e05b85364 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -289,7 +289,9 @@ CreateCrashDump( { // Ignore any error because on some CentOS and OpenSUSE distros, it isn't // supported but createdump works just fine. +#ifdef _DEBUG fprintf(stderr, "CreateCrashDump: prctl() FAILED %s (%d)\n", strerror(errno), errno); +#endif } #endif // HAVE_PRCTL_H && HAVE_PR_SET_PTRACER close(child_pipe); @@ -568,7 +570,7 @@ PalCreateDumpInitialize() if (stat(program, &fileData) == -1 || !S_ISREG(fileData.st_mode)) { fprintf(stderr, "DOTNET_DbgEnableMiniDump is set and the createdump binary does not exist: %s\n", program); - return false; + return true; } g_szCreateDumpPath = program; From 08f53dff835b40ff8bd7397bed01e7aff1f1dc7b Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Fri, 21 Jul 2023 19:16:51 -0700 Subject: [PATCH 09/15] Fix build breaks --- src/coreclr/nativeaot/Runtime/DebugHeader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/Runtime/DebugHeader.cpp b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp index c0ce4ca7babda..121fdec1019e0 100644 --- a/src/coreclr/nativeaot/Runtime/DebugHeader.cpp +++ b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp @@ -17,7 +17,7 @@ #include "thread.h" #include "threadstore.h" -extern uint8_t* g_CrashInfoBuffer; +extern uint8_t g_CrashInfoBuffer[]; GPTR_DECL(MethodTable, g_pFreeObjectEEType); struct DebugTypeEntry From b7141ca56fd747badeedf60e774c20fab60978d2 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 24 Jul 2023 13:58:03 -0700 Subject: [PATCH 10/15] Generate a core dump on unhandled SIGSEGV/SIGFPE Add PalCreateDump.h with all the public functions. --- src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp | 6 +++--- .../nativeaot/Runtime/unix/HardwareExceptions.cpp | 5 +++++ src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp | 6 ++++++ src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h | 11 +++++++++++ src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp | 4 +--- .../src/System/Runtime/RuntimeImports.cs | 2 +- .../src/System/RuntimeExceptionHelpers.cs | 2 +- 7 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h diff --git a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp index 8fc758aa311ee..9bb7d11a5aef8 100644 --- a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp +++ b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp @@ -51,10 +51,10 @@ COOP_PINVOKE_HELPER(uint8_t *, RhGetCrashInfoBuffer, (int32_t* pcbMaxSize)) } #if TARGET_UNIX -extern void PalCreateCrashDumpIfEnabled(); -COOP_PINVOKE_HELPER(void, RhCreateCrashDumpIfEnabled, ()) +#include "PalCreateDump.h" +COOP_PINVOKE_HELPER(void, RhCreateCrashDumpIfEnabled, (void* pExAddress, void* pExContext, void* triageBuffer, int triageBufferSize)) { - PalCreateCrashDumpIfEnabled(); + PalCreateCrashDumpIfEnabled(pExAddress, pExContext, triageBuffer, triageBufferSize); } #endif diff --git a/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp b/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp index 5aecfc0c33211..03ee5d514c3dc 100644 --- a/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp @@ -10,6 +10,7 @@ #include "UnixContext.h" #include "HardwareExceptions.h" #include "UnixSignals.h" +#include "PalCreateDump.h" #if defined(HOST_APPLE) #include @@ -559,6 +560,8 @@ void SIGSEGVHandler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception RestoreSignalHandler(code, &g_previousSIGSEGV); } + + PalCreateCrashDumpIfEnabled(code, siginfo); } // Handler for the SIGFPE signal @@ -579,6 +582,8 @@ void SIGFPEHandler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception RestoreSignalHandler(code, &g_previousSIGFPE); } + + PalCreateCrashDumpIfEnabled(code, siginfo); } // Initialize hardware exception handling diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index c168e05b85364..514105f904306 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -430,6 +430,12 @@ PalCreateCrashDumpIfEnabled() PalCreateCrashDumpIfEnabled(SIGABRT, nullptr); } +void +PalCreateCrashDumpIfEnabled(void* pExAddress, void* pExContext, void* triageBuffer, int triageBufferSize) +{ + PalCreateCrashDumpIfEnabled(SIGABRT, nullptr); +} + /*++ Function: PalGenerateCoreDump diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h new file mode 100644 index 0000000000000..8a855c1e1c949 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include + +extern bool PalCreateDumpInitialize(); +extern void PalCreateCrashDumpIfEnabled(); +extern void PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo); +extern void PalCreateCrashDumpIfEnabled(void* pExAddress, void* pExContext, void* triageBuffer, int triageBufferSize); diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index f1e7550e56b00..ebea2db6968d9 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -19,6 +19,7 @@ #include "UnixSignals.h" #include "UnixContext.h" #include "HardwareExceptions.h" +#include "PalCreateDump.h" #include "cgroupcpu.h" #include "threadstore.h" #include "thread.h" @@ -85,9 +86,6 @@ static const int tccMilliSecondsToMicroSeconds = 1000; static const int tccMilliSecondsToNanoSeconds = 1000000; static const int tccMicroSecondsToNanoSeconds = 1000; -extern bool PalCreateDumpInitialize(); -extern void PalCreateCrashDumpIfEnabled(); - extern "C" void RaiseFailFastException(PEXCEPTION_RECORD arg1, PCONTEXT arg2, uint32_t arg3) { // Causes creation of a crash dump if enabled diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 23e9cfdb529ac..e706ddff4741e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -33,7 +33,7 @@ public static partial class RuntimeImports #if TARGET_UNIX [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhCreateCrashDumpIfEnabled")] - internal static extern void RhCreateCrashDumpIfEnabled(); + internal static extern void RhCreateCrashDumpIfEnabled(IntPtr pExAddress, IntPtr pExContext, IntPtr triageBufferAddress, int triageBufferSize); #endif [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs index a9e26cf0f6e5b..23d36a40fdcb8 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -255,7 +255,7 @@ internal static unsafe void FailFast(string? message, Exception? exception, RhFa #if TARGET_WINDOWS Interop.Kernel32.RaiseFailFastException(errorCode, pExAddress, pExContext, triageBufferAddress, triageBufferSize); #else - RuntimeImports.RhCreateCrashDumpIfEnabled(); + RuntimeImports.RhCreateCrashDumpIfEnabled(pExAddress, pExContext, triageBufferAddress, triageBufferSize); Interop.Sys.Abort(); #endif } From 2eb2437cafe9c33eea536c9432ab5ae2de5bb89d Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 24 Jul 2023 18:21:41 -0700 Subject: [PATCH 11/15] Build and pass an EXCEPTION_RECORD for Linux like it was done for Windows. The next step is to pass the address of it to createdump. --- .../nativeaot/Runtime/RuntimeInstance.cpp | 4 +- .../nativeaot/Runtime/unix/PalCreateDump.cpp | 2 +- .../nativeaot/Runtime/unix/PalCreateDump.h | 2 +- .../nativeaot/Runtime/unix/PalRedhawkUnix.cpp | 3 + .../src/System/Runtime/RuntimeImports.cs | 2 +- .../src/System/RuntimeExceptionHelpers.cs | 41 +++++++++++++- .../Interop.RaiseFailFastException.cs | 56 +------------------ 7 files changed, 48 insertions(+), 62 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp index 9bb7d11a5aef8..c3a36d7cebac1 100644 --- a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp +++ b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp @@ -52,9 +52,9 @@ COOP_PINVOKE_HELPER(uint8_t *, RhGetCrashInfoBuffer, (int32_t* pcbMaxSize)) #if TARGET_UNIX #include "PalCreateDump.h" -COOP_PINVOKE_HELPER(void, RhCreateCrashDumpIfEnabled, (void* pExAddress, void* pExContext, void* triageBuffer, int triageBufferSize)) +COOP_PINVOKE_HELPER(void, RhCreateCrashDumpIfEnabled, (PEXCEPTION_RECORD pExceptionRecord, PCONTEXT pExContext)) { - PalCreateCrashDumpIfEnabled(pExAddress, pExContext, triageBuffer, triageBufferSize); + PalCreateCrashDumpIfEnabled(pExceptionRecord, pExContext); } #endif diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index 514105f904306..e56948337c817 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -431,7 +431,7 @@ PalCreateCrashDumpIfEnabled() } void -PalCreateCrashDumpIfEnabled(void* pExAddress, void* pExContext, void* triageBuffer, int triageBufferSize) +PalCreateCrashDumpIfEnabled(void* pExceptionRecord, void* pExContext) { PalCreateCrashDumpIfEnabled(SIGABRT, nullptr); } diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h index 8a855c1e1c949..8fa15c41af75a 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h @@ -8,4 +8,4 @@ extern bool PalCreateDumpInitialize(); extern void PalCreateCrashDumpIfEnabled(); extern void PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo); -extern void PalCreateCrashDumpIfEnabled(void* pExAddress, void* pExContext, void* triageBuffer, int triageBufferSize); +extern void PalCreateCrashDumpIfEnabled(void* pExceptionRecord, void* pExContext); diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index ebea2db6968d9..5b184882830a8 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -1114,6 +1114,9 @@ REDHAWK_PALEXPORT void REDHAWK_PALAPI PalHijack(HANDLE hThread, _In_opt_ void* p if (status != 0) { + // Causes creation of a crash dump if enabled + PalCreateCrashDumpIfEnabled(); + // 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. diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index e706ddff4741e..35f45960045ed 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -33,7 +33,7 @@ public static partial class RuntimeImports #if TARGET_UNIX [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhCreateCrashDumpIfEnabled")] - internal static extern void RhCreateCrashDumpIfEnabled(IntPtr pExAddress, IntPtr pExContext, IntPtr triageBufferAddress, int triageBufferSize); + internal static extern void RhCreateCrashDumpIfEnabled(IntPtr pExceptionRecord, IntPtr pContextRecord); #endif [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs index 23d36a40fdcb8..d156c034bdb24 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -175,6 +175,27 @@ public static void RuntimeFailFast(RhFailFastReason reason, Exception? exception } } + internal const uint EXCEPTION_NONCONTINUABLE = 0x1; + internal const uint FAIL_FAST_GENERATE_EXCEPTION_ADDRESS = 0x1; + internal const uint STATUS_STACK_BUFFER_OVERRUN = 0xC0000409; + internal const uint FAST_FAIL_EXCEPTION_DOTNET_AOT = 0x48; + +#pragma warning disable 649 + internal unsafe struct EXCEPTION_RECORD + { + internal uint ExceptionCode; + internal uint ExceptionFlags; + internal IntPtr ExceptionRecord; + internal IntPtr ExceptionAddress; + internal uint NumberParameters; +#if TARGET_64BIT + internal fixed ulong ExceptionInformation[15]; +#else + internal fixed uint ExceptionInformation[15]; +#endif + } +#pragma warning restore 649 + private static ulong s_crashingThreadId; [DoesNotReturn] @@ -252,10 +273,26 @@ internal static unsafe void FailFast(string? message, Exception? exception, RhFa } } + EXCEPTION_RECORD exceptionRecord; + // STATUS_STACK_BUFFER_OVERRUN is a "transport" exception code required by Watson to trigger the proper analyzer/provider for bucketing + exceptionRecord.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + exceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + exceptionRecord.ExceptionRecord = IntPtr.Zero; + exceptionRecord.ExceptionAddress = pExAddress; + exceptionRecord.NumberParameters = 4; + exceptionRecord.ExceptionInformation[0] = FAST_FAIL_EXCEPTION_DOTNET_AOT; + exceptionRecord.ExceptionInformation[1] = (uint)errorCode; +#if TARGET_64BIT + exceptionRecord.ExceptionInformation[2] = (ulong)triageBufferAddress; +#else + exceptionRecord.ExceptionInformation[2] = (uint)triageBufferAddress; +#endif + exceptionRecord.ExceptionInformation[3] = (uint)triageBufferSize; + #if TARGET_WINDOWS - Interop.Kernel32.RaiseFailFastException(errorCode, pExAddress, pExContext, triageBufferAddress, triageBufferSize); + Interop.Kernel32.RaiseFailFastException(new IntPtr(&exceptionRecord), pExContext, pExAddress == IntPtr.Zero ? FAIL_FAST_GENERATE_EXCEPTION_ADDRESS : 0); #else - RuntimeImports.RhCreateCrashDumpIfEnabled(pExAddress, pExContext, triageBufferAddress, triageBufferSize); + RuntimeImports.RhCreateCrashDumpIfEnabled(new IntPtr(&exceptionRecord), pExContext); Interop.Sys.Abort(); #endif } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RaiseFailFastException.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RaiseFailFastException.cs index 517c8ff418d95..49e07bc01cc7e 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RaiseFailFastException.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RaiseFailFastException.cs @@ -3,68 +3,14 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; internal partial class Interop { -#pragma warning disable 649 - internal unsafe struct EXCEPTION_RECORD - { - internal uint ExceptionCode; - internal uint ExceptionFlags; - internal IntPtr ExceptionRecord; - internal IntPtr ExceptionAddress; - internal uint NumberParameters; -#if TARGET_64BIT - internal fixed ulong ExceptionInformation[15]; -#else - internal fixed uint ExceptionInformation[15]; -#endif - } -#pragma warning restore 649 - internal partial class Kernel32 { - private const uint EXCEPTION_NONCONTINUABLE = 0x1; - private const uint FAIL_FAST_GENERATE_EXCEPTION_ADDRESS = 0x1; - private const uint STATUS_STACK_BUFFER_OVERRUN = 0xC0000409; - private const uint FAST_FAIL_EXCEPTION_DOTNET_AOT = 0x48; - - // - // NativeAOT wrapper for calling RaiseFailFastException - // - - [DoesNotReturn] - internal static unsafe void RaiseFailFastException(int errorCode, IntPtr pExAddress, IntPtr pExContext, IntPtr pTriageBuffer, int cbTriageBuffer) - { - EXCEPTION_RECORD exceptionRecord; - // STATUS_STACK_BUFFER_OVERRUN is a "transport" exception code required by Watson to trigger the proper analyzer/provider for bucketing - exceptionRecord.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; - exceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; - exceptionRecord.ExceptionRecord = IntPtr.Zero; - exceptionRecord.ExceptionAddress = pExAddress; - exceptionRecord.NumberParameters = 4; - exceptionRecord.ExceptionInformation[0] = FAST_FAIL_EXCEPTION_DOTNET_AOT; - exceptionRecord.ExceptionInformation[1] = (uint)errorCode; -#if TARGET_64BIT - exceptionRecord.ExceptionInformation[2] = (ulong)pTriageBuffer; -#else - exceptionRecord.ExceptionInformation[2] = (uint)pTriageBuffer; -#endif - exceptionRecord.ExceptionInformation[3] = (uint)cbTriageBuffer; - - RaiseFailFastException( - &exceptionRecord, - pExContext, - pExAddress == IntPtr.Zero ? FAIL_FAST_GENERATE_EXCEPTION_ADDRESS : 0); - } - [LibraryImport(Libraries.Kernel32, EntryPoint = "RaiseFailFastException")] [DoesNotReturn] - private static unsafe partial void RaiseFailFastException( - EXCEPTION_RECORD* pExceptionRecord, - IntPtr pContextRecord, - uint dwFlags); + public static unsafe partial void RaiseFailFastException(IntPtr pExceptionRecord, IntPtr pContextRecord, uint dwFlags); } } From 9885d95107f86507bede0ef512857b7230f6be4b Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 24 Jul 2023 19:32:44 -0700 Subject: [PATCH 12/15] Pass exception record address to createdump --- src/coreclr/debug/createdump/crashinfo.cpp | 3 ++- src/coreclr/debug/createdump/crashinfo.h | 2 ++ src/coreclr/debug/createdump/createdump.h | 3 ++- .../debug/createdump/createdumpmain.cpp | 11 ++++++---- .../nativeaot/Runtime/RuntimeInstance.cpp | 2 +- .../nativeaot/Runtime/unix/PalCreateDump.cpp | 21 +++++++++++++++---- .../nativeaot/Runtime/unix/PalCreateDump.h | 2 +- 7 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index c9bf2238385b3..37cc748573f48 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -22,6 +22,7 @@ CrashInfo::CrashInfo(const CreateDumpOptions& options) : m_gatherFrames(options.CrashReport), m_crashThread(options.CrashThread), m_signal(options.Signal), + m_exceptionRecord(options.ExceptionRecord), m_moduleInfos(&ModuleInfoCompare), m_mainModule(nullptr), m_cbModuleMappings(0), @@ -39,7 +40,7 @@ CrashInfo::CrashInfo(const CreateDumpOptions& options) : m_siginfo.si_signo = options.Signal; m_siginfo.si_code = options.SignalCode; m_siginfo.si_errno = options.SignalErrno; - m_siginfo.si_addr = options.SignalAddress; + m_siginfo.si_addr = (void*)options.SignalAddress; } CrashInfo::~CrashInfo() diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index 0e3459b671b68..6673e2dec7d3e 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -55,6 +55,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi bool m_gatherFrames; // if true, add the native and managed stack frames to the thread info pid_t m_crashThread; // crashing thread id or 0 if none uint32_t m_signal; // crash signal code or 0 if none + uint64_t m_exceptionRecord; // exception record address or 0 if none std::string m_name; // exe name siginfo_t m_siginfo; // signal info (if any) std::string m_coreclrPath; // the path of the coreclr module or empty if none @@ -115,6 +116,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi inline const bool GatherFrames() const { return m_gatherFrames; } inline const pid_t CrashThread() const { return m_crashThread; } inline const uint32_t Signal() const { return m_signal; } + inline const uint64_t ExceptionRecord () const { return m_exceptionRecord; } inline const std::string& Name() const { return m_name; } inline const ModuleInfo* MainModule() const { return m_mainModule; } inline const uint64_t RuntimeBaseAddress() const { return m_runtimeBaseAddress; } diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h index e24fa94a2df20..fb7f967bec197 100644 --- a/src/coreclr/debug/createdump/createdump.h +++ b/src/coreclr/debug/createdump/createdump.h @@ -119,7 +119,8 @@ typedef struct int Signal; int SignalCode; int SignalErrno; - void* SignalAddress; + uint64_t SignalAddress; + uint64_t ExceptionRecord; } CreateDumpOptions; #ifdef HOST_UNIX diff --git a/src/coreclr/debug/createdump/createdumpmain.cpp b/src/coreclr/debug/createdump/createdumpmain.cpp index 10468394956ef..e39538c4cb1c3 100644 --- a/src/coreclr/debug/createdump/createdumpmain.cpp +++ b/src/coreclr/debug/createdump/createdumpmain.cpp @@ -71,11 +71,10 @@ int createdump_main(const int argc, const char* argv[]) options.Signal = 0; options.CrashThread = 0; options.Pid = 0; -#if defined(HOST_UNIX) && !defined(HOST_OSX) options.SignalCode = 0; options.SignalErrno = 0; - options.SignalAddress = nullptr; -#endif + options.SignalAddress = 0; + options.ExceptionRecord = 0; bool help = false; int exitCode = 0; @@ -141,7 +140,11 @@ int createdump_main(const int argc, const char* argv[]) } else if (strcmp(*argv, "--address") == 0) { - options.SignalAddress = (void*)atoll(*++argv); + options.SignalAddress = atoll(*++argv); + } + else if (strcmp(*argv, "--exception-record") == 0) + { + options.ExceptionRecord = atoll(*++argv); } #endif else if ((strcmp(*argv, "-d") == 0) || (strcmp(*argv, "--diag") == 0)) diff --git a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp index c3a36d7cebac1..680876949415a 100644 --- a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp +++ b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp @@ -37,7 +37,7 @@ bool ShouldHijackForGcStress(uintptr_t CallsiteIP, HijackType ht); #include "shash.inl" #define MAX_CRASHINFOBUFFER_SIZE 8192 -uint8_t g_CrashInfoBuffer[MAX_CRASHINFOBUFFER_SIZE]; +uint8_t g_CrashInfoBuffer[MAX_CRASHINFOBUFFER_SIZE] = { 0 }; ThreadStore * RuntimeInstance::GetThreadStore() { diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp index e56948337c817..a189ef73573da 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -343,11 +343,12 @@ CreateCrashDump( Parameters: signal - POSIX signal number or 0 siginfo - signal info or nullptr + exceptionRecord - address of exception record or nullptr (no return value) --*/ void -PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo) +PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo, void* exceptionRecord) { // If enabled, launch the create minidump utility and wait until it completes if (g_argvCreateDump[0] != nullptr) @@ -358,6 +359,7 @@ PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo) char* signalCodeArg = nullptr; char* signalErrnoArg = nullptr; char* signalAddressArg = nullptr; + char* exceptionRecordArg = nullptr; // Copy the createdump argv int argc = 0; @@ -388,7 +390,7 @@ PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo) argv[argc++] = crashThreadArg; } - if (siginfo != nullptr) + if (siginfo != nullptr && argc < MAX_ARGV_ENTRIES) { signalCodeArg = FormatInt(siginfo->si_code); if (signalCodeArg != nullptr) @@ -410,6 +412,16 @@ PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo) } } + if (exceptionRecord != nullptr && argc < MAX_ARGV_ENTRIES) + { + exceptionRecordArg = FormatInt64((uint64_t)exceptionRecord); + if (exceptionRecordArg != nullptr) + { + argv[argc++] = "--exception-record"; + argv[argc++] = exceptionRecordArg; + } + } + argv[argc++] = nullptr; assert(argc < MAX_ARGV_ENTRIES); } @@ -421,19 +433,20 @@ PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo) free(signalCodeArg); free(signalErrnoArg); free(signalAddressArg); + free(exceptionRecordArg); } } void PalCreateCrashDumpIfEnabled() { - PalCreateCrashDumpIfEnabled(SIGABRT, nullptr); + PalCreateCrashDumpIfEnabled(SIGABRT, nullptr, nullptr); } void PalCreateCrashDumpIfEnabled(void* pExceptionRecord, void* pExContext) { - PalCreateCrashDumpIfEnabled(SIGABRT, nullptr); + PalCreateCrashDumpIfEnabled(SIGABRT, nullptr, pExceptionRecord); } /*++ diff --git a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h index 8fa15c41af75a..560ba3527aa38 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h @@ -7,5 +7,5 @@ extern bool PalCreateDumpInitialize(); extern void PalCreateCrashDumpIfEnabled(); -extern void PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo); +extern void PalCreateCrashDumpIfEnabled(int signal, siginfo_t* siginfo = nullptr, void* exceptionRecord = nullptr); extern void PalCreateCrashDumpIfEnabled(void* pExceptionRecord, void* pExContext); From 9c887870efb45c16886b831de1a9d54e3b0f89ab Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Tue, 25 Jul 2023 16:48:39 -0700 Subject: [PATCH 13/15] Add special diagnostic info memory region Contains the exception record address for Native AOT crashes. --- src/coreclr/debug/createdump/crashinfo.cpp | 3 ++ src/coreclr/debug/createdump/createdump.h | 1 + src/coreclr/debug/createdump/dumpwriter.cpp | 21 +++++++++ .../debug/createdump/dumpwriterelf.cpp | 45 +++++++++++-------- src/coreclr/debug/createdump/dumpwriterelf.h | 1 + .../debug/createdump/dumpwritermacho.cpp | 8 +++- .../debug/createdump/dumpwritermacho.h | 1 + .../debug/createdump/specialdiaginfo.h | 32 +++++++++++++ 8 files changed, 93 insertions(+), 19 deletions(-) create mode 100644 src/coreclr/debug/createdump/specialdiaginfo.h diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 37cc748573f48..ef903767ba027 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -194,6 +194,9 @@ CrashInfo::GatherCrashInfo(DumpType dumpType) { return false; } + // Add the special (fake) memory region for the special diagnostics info + MemoryRegion special(PF_R, SpecialDiagInfoAddress, SpecialDiagInfoAddress + PAGE_SIZE); + m_memoryRegions.insert(special); #ifdef __APPLE__ InitializeOtherMappings(); #endif diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h index fb7f967bec197..50cf53dccfc7a 100644 --- a/src/coreclr/debug/createdump/createdump.h +++ b/src/coreclr/debug/createdump/createdump.h @@ -137,6 +137,7 @@ typedef struct #include "crashreportwriter.h" #include "dumpwriter.h" #include "runtimeinfo.h" +#include "specialdiaginfo.h" #endif #ifndef MAX_LONGPATH diff --git a/src/coreclr/debug/createdump/dumpwriter.cpp b/src/coreclr/debug/createdump/dumpwriter.cpp index ec9101ec8ec68..ed21420bac97c 100644 --- a/src/coreclr/debug/createdump/dumpwriter.cpp +++ b/src/coreclr/debug/createdump/dumpwriter.cpp @@ -32,6 +32,27 @@ DumpWriter::OpenDump(const char* dumpFileName) return true; } +bool +DumpWriter::WriteDiagInfo(int size) +{ + // Write the diagnostics info header + SpecialDiagInfoHeader header = { + {SPECIAL_DIAGINFO_SIGNATURE}, + SPECIAL_DIAGINFO_VERSION, + m_crashInfo.ExceptionRecord() + }; + if (!WriteData(&header, sizeof(header))) { + return false; + } + int alignment = size - sizeof(header); + assert(alignment < sizeof(m_tempBuffer)); + memset(m_tempBuffer, 0, alignment); + if (!WriteData(m_tempBuffer, alignment)) { + return false; + } + return true; +} + // Write all of the given buffer, handling short writes and EINTR. Return true iff successful. bool DumpWriter::WriteData(int fd, const void* buffer, size_t length) diff --git a/src/coreclr/debug/createdump/dumpwriterelf.cpp b/src/coreclr/debug/createdump/dumpwriterelf.cpp index be3072d347bbd..675113b59bd99 100644 --- a/src/coreclr/debug/createdump/dumpwriterelf.cpp +++ b/src/coreclr/debug/createdump/dumpwriterelf.cpp @@ -176,28 +176,37 @@ DumpWriter::WriteDump() size_t size = memoryRegion.Size(); total += size; - while (size > 0) + if (address == SpecialDiagInfoAddress) { - size_t bytesToRead = std::min(size, sizeof(m_tempBuffer)); - size_t read = 0; - - if (!m_crashInfo.ReadProcessMemory((void*)address, m_tempBuffer, bytesToRead, &read)) { - printf_error("Error reading memory at %" PRIA PRIx64 " size %08zx FAILED %s (%d)\n", address, bytesToRead, strerror(g_readProcessMemoryErrno), g_readProcessMemoryErrno); + if (!WriteDiagInfo(size)) { return false; } - - // This can happen if the target process dies before createdump is finished - if (read == 0) { - printf_error("Error reading memory at %" PRIA PRIx64 " size %08zx returned 0 bytes read: %s (%d)\n", address, bytesToRead, strerror(g_readProcessMemoryErrno), g_readProcessMemoryErrno); - return false; - } - - if (!WriteData(m_tempBuffer, read)) { - return false; + } + else + { + while (size > 0) + { + size_t bytesToRead = std::min(size, sizeof(m_tempBuffer)); + size_t read = 0; + + if (!m_crashInfo.ReadProcessMemory((void*)address, m_tempBuffer, bytesToRead, &read)) { + printf_error("Error reading memory at %" PRIA PRIx64 " size %08zx FAILED %s (%d)\n", address, bytesToRead, strerror(g_readProcessMemoryErrno), g_readProcessMemoryErrno); + return false; + } + + // This can happen if the target process dies before createdump is finished + if (read == 0) { + printf_error("Error reading memory at %" PRIA PRIx64 " size %08zx returned 0 bytes read: %s (%d)\n", address, bytesToRead, strerror(g_readProcessMemoryErrno), g_readProcessMemoryErrno); + return false; + } + + if (!WriteData(m_tempBuffer, read)) { + return false; + } + + address += read; + size -= read; } - - address += read; - size -= read; } } diff --git a/src/coreclr/debug/createdump/dumpwriterelf.h b/src/coreclr/debug/createdump/dumpwriterelf.h index bfc5ae15c147a..43b5e5c62f42c 100644 --- a/src/coreclr/debug/createdump/dumpwriterelf.h +++ b/src/coreclr/debug/createdump/dumpwriterelf.h @@ -56,6 +56,7 @@ class DumpWriter static bool WriteData(int fd, const void* buffer, size_t length); private: + bool WriteDiagInfo(int size); bool WriteProcessInfo(); bool WriteAuxv(); size_t GetNTFileInfoSize(size_t* alignmentBytes = nullptr); diff --git a/src/coreclr/debug/createdump/dumpwritermacho.cpp b/src/coreclr/debug/createdump/dumpwritermacho.cpp index 313ed5d5f1e94..79b71132f3a3a 100644 --- a/src/coreclr/debug/createdump/dumpwritermacho.cpp +++ b/src/coreclr/debug/createdump/dumpwritermacho.cpp @@ -229,7 +229,13 @@ DumpWriter::WriteSegments() (segment.initprot & VM_PROT_EXECUTE) ? 'x' : '-', segment.initprot); - if (address == SpecialThreadInfoAddress) + if (address == SpecialDiagInfoAddress) + { + if (!WriteDiagInfo(size)) { + return false; + } + } + else if (address == SpecialThreadInfoAddress) { // Write the header SpecialThreadInfoHeader header = { diff --git a/src/coreclr/debug/createdump/dumpwritermacho.h b/src/coreclr/debug/createdump/dumpwritermacho.h index 704ea08488214..d166332aef44c 100644 --- a/src/coreclr/debug/createdump/dumpwritermacho.h +++ b/src/coreclr/debug/createdump/dumpwritermacho.h @@ -42,6 +42,7 @@ class DumpWriter static bool WriteData(int fd, const void* buffer, size_t length); private: + bool WriteDiagInfo(int size); void BuildSegmentLoadCommands(); void BuildThreadLoadCommands(); bool WriteHeader(uint64_t* pFileOffset); diff --git a/src/coreclr/debug/createdump/specialdiaginfo.h b/src/coreclr/debug/createdump/specialdiaginfo.h new file mode 100644 index 0000000000000..e1b0bae7727cd --- /dev/null +++ b/src/coreclr/debug/createdump/specialdiaginfo.h @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// ****************************************************************************** +// WARNING!!!: This code is also used by SOS in the diagnostics repo. Should be +// updated in a backwards and forwards compatible way. +// See: https://github.com/dotnet/diagnostics/blob/main/src/SOS/inc/specialdiaginfo.h +// ****************************************************************************** + +// This is a special memory region added to ELF and MachO dumps that contains extra diagnostics +// information like the exception record for a crash for a NativeAOT app. The exception record +// contains the pointer to the JSON formatted crash info. + +#define SPECIAL_DIAGINFO_SIGNATURE "DIAGINFOHEADER" +#define SPECIAL_DIAGINFO_VERSION 1 + +#ifdef __APPLE__ +const uint64_t SpecialDiagInfoAddress = 0x7fffffff10000000; +#else +#if TARGET_64BIT +const uint64_t SpecialDiagInfoAddress = 0x00007ffffff10000; +#else +const uint64_t SpecialDiagInfoAddress = 0x7fff1000; +#endif +#endif + +struct SpecialDiagInfoHeader +{ + char Signature[16]; + int Version; + uint64_t ExceptionRecordAddress; +}; From b59d243b7731c59ccc2633e0641fd7839ef7505c Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Wed, 26 Jul 2023 15:11:08 -0700 Subject: [PATCH 14/15] Fix GCC build errors --- src/coreclr/debug/createdump/dumpwriter.cpp | 4 ++-- src/coreclr/debug/createdump/dumpwriterelf.h | 2 +- src/coreclr/debug/createdump/dumpwritermacho.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/debug/createdump/dumpwriter.cpp b/src/coreclr/debug/createdump/dumpwriter.cpp index ed21420bac97c..5153ba790154f 100644 --- a/src/coreclr/debug/createdump/dumpwriter.cpp +++ b/src/coreclr/debug/createdump/dumpwriter.cpp @@ -33,7 +33,7 @@ DumpWriter::OpenDump(const char* dumpFileName) } bool -DumpWriter::WriteDiagInfo(int size) +DumpWriter::WriteDiagInfo(size_t size) { // Write the diagnostics info header SpecialDiagInfoHeader header = { @@ -44,7 +44,7 @@ DumpWriter::WriteDiagInfo(int size) if (!WriteData(&header, sizeof(header))) { return false; } - int alignment = size - sizeof(header); + size_t alignment = size - sizeof(header); assert(alignment < sizeof(m_tempBuffer)); memset(m_tempBuffer, 0, alignment); if (!WriteData(m_tempBuffer, alignment)) { diff --git a/src/coreclr/debug/createdump/dumpwriterelf.h b/src/coreclr/debug/createdump/dumpwriterelf.h index 43b5e5c62f42c..912befddf03b9 100644 --- a/src/coreclr/debug/createdump/dumpwriterelf.h +++ b/src/coreclr/debug/createdump/dumpwriterelf.h @@ -56,7 +56,7 @@ class DumpWriter static bool WriteData(int fd, const void* buffer, size_t length); private: - bool WriteDiagInfo(int size); + bool WriteDiagInfo(size_t size); bool WriteProcessInfo(); bool WriteAuxv(); size_t GetNTFileInfoSize(size_t* alignmentBytes = nullptr); diff --git a/src/coreclr/debug/createdump/dumpwritermacho.h b/src/coreclr/debug/createdump/dumpwritermacho.h index d166332aef44c..c1788a3b4bded 100644 --- a/src/coreclr/debug/createdump/dumpwritermacho.h +++ b/src/coreclr/debug/createdump/dumpwritermacho.h @@ -42,7 +42,7 @@ class DumpWriter static bool WriteData(int fd, const void* buffer, size_t length); private: - bool WriteDiagInfo(int size); + bool WriteDiagInfo(size_t size); void BuildSegmentLoadCommands(); void BuildThreadLoadCommands(); bool WriteHeader(uint64_t* pFileOffset); From b4873772bf5548c6b7542d45285156b3ed9c560c Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Thu, 27 Jul 2023 15:57:26 -0700 Subject: [PATCH 15/15] Code review feedback --- src/coreclr/debug/createdump/specialdiaginfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/debug/createdump/specialdiaginfo.h b/src/coreclr/debug/createdump/specialdiaginfo.h index e1b0bae7727cd..3a04a9f551e6d 100644 --- a/src/coreclr/debug/createdump/specialdiaginfo.h +++ b/src/coreclr/debug/createdump/specialdiaginfo.h @@ -27,6 +27,6 @@ const uint64_t SpecialDiagInfoAddress = 0x7fff1000; struct SpecialDiagInfoHeader { char Signature[16]; - int Version; + int32_t Version; uint64_t ExceptionRecordAddress; };