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/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index c9bf2238385b3..ef903767ba027 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() @@ -193,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/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..50cf53dccfc7a 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 @@ -136,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/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/debug/createdump/dumpwriter.cpp b/src/coreclr/debug/createdump/dumpwriter.cpp index ec9101ec8ec68..5153ba790154f 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(size_t size) +{ + // Write the diagnostics info header + SpecialDiagInfoHeader header = { + {SPECIAL_DIAGINFO_SIGNATURE}, + SPECIAL_DIAGINFO_VERSION, + m_crashInfo.ExceptionRecord() + }; + if (!WriteData(&header, sizeof(header))) { + return false; + } + size_t 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..912befddf03b9 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(size_t 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..c1788a3b4bded 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(size_t 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..3a04a9f551e6d --- /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]; + int32_t Version; + uint64_t ExceptionRecordAddress; +}; 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/DebugHeader.cpp b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp index 24c5bc58bb8f4..121fdec1019e0 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/RuntimeInstance.cpp b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp index 9f82dd84b8926..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() { @@ -50,6 +50,14 @@ COOP_PINVOKE_HELPER(uint8_t *, RhGetCrashInfoBuffer, (int32_t* pcbMaxSize)) return g_CrashInfoBuffer; } +#if TARGET_UNIX +#include "PalCreateDump.h" +COOP_PINVOKE_HELPER(void, RhCreateCrashDumpIfEnabled, (PEXCEPTION_RECORD pExceptionRecord, PCONTEXT pExContext)) +{ + PalCreateCrashDumpIfEnabled(pExceptionRecord, pExContext); +} +#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..a411cf71a84d9 100644 --- a/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp +++ b/src/coreclr/nativeaot/Runtime/eventpipe/ds-rt-aot.cpp @@ -248,4 +248,5 @@ 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 } + #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..98f065968e6fc 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,24 @@ 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; +#ifdef TARGET_UNIX + 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)); + 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; + } +#endif + return result; } /* 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 new file mode 100644 index 0000000000000..a189ef73573da --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.cpp @@ -0,0 +1,609 @@ +// 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 "config.h" +#include +#include +#include +#include +#define __STDC_FORMAT_MACROS +#include +#include +#include +#include +#include +#if HAVE_PRCTL_H +#include +#include +#endif +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include +#include +#endif + +#ifdef __NetBSD__ +#include +#include +#include +#include +#endif + +#ifdef __FreeBSD__ +#include +#include +#endif + +#define _T(s) s +#include "RhConfig.h" + +#include +#include + +// 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 (uint32_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 + +const size_t MaxUnsigned32BitDecString = STRING_LENGTH("4294967295"); +const size_t MaxUnsigned64BitDecString = STRING_LENGTH("18446744073709551615"); + +/*++ +Function: + FormatInt + + Helper function to format an uint32 as a string. + +--*/ +static +char* +FormatInt(uint32_t value) +{ + char* buffer = (char*)malloc(MaxUnsigned32BitDecString + 1); + if (buffer != nullptr) + { + if (snprintf(buffer, MaxUnsigned32BitDecString, "%" PRIu32, value) < 0) + { + free(buffer); + buffer = nullptr; + } + } + return buffer; +} + +/*++ +Function: + FormatInt64 + + Helper function to format an uint64 as a string. + +--*/ +static +char* +FormatInt64(uint64_t value) +{ + char* buffer = (char*)malloc(MaxUnsigned64BitDecString + 1); + if (buffer != nullptr) + { + if (snprintf(buffer, MaxUnsigned64BitDecString, "%" PRIu64, value) < 0) + { + free(buffer); + buffer = nullptr; + } + } + return buffer; +} + +/*++ +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 DumpTypeNormal: + argv[argc++] = "--normal"; + break; + case DumpTypeWithHeap: + argv[argc++] = "--withheap"; + break; + case DumpTypeTriage: + argv[argc++] = "--triage"; + break; + case DumpTypeFull: + 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; + + if (argc >= MAX_ARGV_ENTRIES) + { + return false; + } + 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 (execv(argv[0], (char* const *)argv) == -1) + { + fprintf(stderr, "Problem launching createdump (may not have execute permissions): execv(%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. +#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); + + // 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: + 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, void* exceptionRecord) +{ + // If enabled, launch the create minidump utility and wait until it completes + if (g_argvCreateDump[0] != nullptr) + { + const char* argv[MAX_ARGV_ENTRIES]; + char* signalArg = nullptr; + char* crashThreadArg = nullptr; + char* signalCodeArg = nullptr; + char* signalErrnoArg = nullptr; + char* signalAddressArg = nullptr; + char* exceptionRecordArg = 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 && argc < MAX_ARGV_ENTRIES) + { + // 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 && argc < MAX_ARGV_ENTRIES) + { + 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; + } + } + + 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); + } + + CreateCrashDump(argv, nullptr, 0); + + free(signalArg); + free(crashThreadArg); + free(signalCodeArg); + free(signalErrnoArg); + free(signalAddressArg); + free(exceptionRecordArg); + } +} + +void +PalCreateCrashDumpIfEnabled() +{ + PalCreateCrashDumpIfEnabled(SIGABRT, nullptr, nullptr); +} + +void +PalCreateCrashDumpIfEnabled(void* pExceptionRecord, void* pExContext) +{ + PalCreateCrashDumpIfEnabled(SIGABRT, nullptr, pExceptionRecord); +} + +/*++ +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 <= DumpTypeUnknown || dumpType > DumpTypeMax) + { + 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() +{ + bool enabled = false; + RhConfig::Environment::TryGetBooleanValue("DbgEnableMiniDump", &enabled); + if (enabled) + { + char* dumpName = nullptr; + RhConfig::Environment::TryGetStringValue("DbgMiniDumpName", &dumpName); + + char* logFilePath = nullptr; + RhConfig::Environment::TryGetStringValue("CreateDumpLogToFile", &logFilePath); + + uint64_t dumpType = DumpTypeUnknown; + if (RhConfig::Environment::TryGetIntegerValue("DbgMiniDumpType", &dumpType, true)) + { + if (dumpType <= DumpTypeUnknown || dumpType > DumpTypeMax) + { + dumpType = DumpTypeUnknown; + } + } + uint32_t flags = GenerateDumpFlagsNone; + bool value = false; + if (RhConfig::Environment::TryGetBooleanValue("CreateDumpDiagnostics", &value)) + { + if (value) + { + flags |= GenerateDumpFlagsLoggingEnabled; + } + } + if (RhConfig::Environment::TryGetBooleanValue("CreateDumpVerboseDiagnostics", &value)) + { + if (value) + { + flags |= GenerateDumpFlagsVerboseLoggingEnabled; + } + } + if (RhConfig::Environment::TryGetBooleanValue("EnableCrashReport", &value)) + { + if (value) + { + flags |= GenerateDumpFlagsCrashReportEnabled; + } + } + if (RhConfig::Environment::TryGetBooleanValue("EnableCrashReportOnly", &value)) + { + if (value) + { + 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); + + 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 true; + } + 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/PalCreateDump.h b/src/coreclr/nativeaot/Runtime/unix/PalCreateDump.h new file mode 100644 index 0000000000000..560ba3527aa38 --- /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 = nullptr, void* exceptionRecord = nullptr); +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 fba1cb0e8c8af..5b184882830a8 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" @@ -87,7 +88,10 @@ static const int tccMicroSecondsToNanoSeconds = 1000; 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 +423,11 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() ConfigureSignals(); + if (!PalCreateDumpInitialize()) + { + return false; + } + GCConfig::Initialize(); if (!GCToOSInterface::Initialize()) @@ -1105,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/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..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 @@ -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(IntPtr pExceptionRecord, IntPtr pContextRecord); +#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..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,9 +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(new IntPtr(&exceptionRecord), pExContext); Interop.Sys.Abort(); #endif } diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 494e5625ff966..39c289c553a80 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -407,16 +407,6 @@ 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 -}; - PALIMPORT BOOL PALAPI diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index a3cb53270b1fc..dcfc4c936f514 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 @@ -2053,8 +2054,6 @@ PROCFormatInt64(ULONG64 value) return buffer; } -static const INT UndefinedDumpType = 0; - /*++ Function PROCBuildCreateDumpCommandLine @@ -2119,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; } @@ -2321,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; } } @@ -2398,7 +2400,7 @@ PAL_GenerateCoreDump( { std::vector argvCreateDump; - if (dumpType < 1 || dumpType > 4) + if (dumpType <= DumpTypeUnknown || dumpType > DumpTypeMax) { return FALSE; } 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/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/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) 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); } } diff --git a/src/native/inc/generatedumpflags.h b/src/native/inc/generatedumpflags.h new file mode 100644 index 0000000000000..34926376e969a --- /dev/null +++ b/src/native/inc/generatedumpflags.h @@ -0,0 +1,31 @@ +// 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 +// + +#pragma once + +// Must be the same as the WriteDumpFlags enum in the diagnostics repo +enum GenerateDumpFlags +{ + GenerateDumpFlagsNone = 0x00, + GenerateDumpFlagsLoggingEnabled = 0x01, + GenerateDumpFlagsVerboseLoggingEnabled = 0x02, + 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 +};