From 6df9b48e5fd8bc3f4762544e62023fafbdbb7a01 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Thu, 9 Mar 2023 11:41:44 -0800 Subject: [PATCH 1/7] Create a createdump static library for single-file and native AOT dump generation A "createdump_static" lib is now built that is linked into the single-file host and in the future can be linked into a Native AOT host/app going forward. For single-file apps most of the same unhandled exception or crash dump generation code is the same (same env vars, most of all the PAL code is the same) except where createdump executable was execve, a callback invokes (set by the host) the static createdump lib entry point. File full dumps are generated (which currently are very large under MacOS) but the user can put the DAC side-by-side with their app and the smaller heap or mini dumps can be generated. The SDK could give an option to publish the DAC SXS for a single-file app. --- src/coreclr/debug/createdump/CMakeLists.txt | 16 +- src/coreclr/debug/createdump/crashinfo.cpp | 133 ++++-- src/coreclr/debug/createdump/crashinfo.h | 21 +- src/coreclr/debug/createdump/crashinfomac.cpp | 2 +- .../debug/createdump/crashinfounix.cpp | 23 +- .../debug/createdump/crashreportwriter.cpp | 4 +- src/coreclr/debug/createdump/createdump.h | 20 +- .../debug/createdump/createdumpmain.cpp | 420 ++++++++++++++++++ .../debug/createdump/createdumpunix.cpp | 11 +- .../debug/createdump/createdumpwindows.cpp | 38 +- src/coreclr/debug/createdump/main.cpp | 408 +---------------- src/coreclr/pal/inc/pal.h | 17 +- src/coreclr/pal/src/thread/process.cpp | 45 +- .../corehost/apphost/static/CMakeLists.txt | 5 +- src/native/corehost/corehost.cpp | 2 + src/native/corehost/hostmisc/pal.h | 2 + src/native/corehost/hostmisc/pal.unix.cpp | 11 + src/native/corehost/hostmisc/pal.windows.cpp | 4 + 18 files changed, 691 insertions(+), 491 deletions(-) create mode 100644 src/coreclr/debug/createdump/createdumpmain.cpp diff --git a/src/coreclr/debug/createdump/CMakeLists.txt b/src/coreclr/debug/createdump/CMakeLists.txt index 2489b8d135e1c..282412f354122 100644 --- a/src/coreclr/debug/createdump/CMakeLists.txt +++ b/src/coreclr/debug/createdump/CMakeLists.txt @@ -14,6 +14,7 @@ if(CLR_CMAKE_HOST_WIN32) set(CREATEDUMP_SOURCES main.cpp + createdumpmain.cpp dumpname.cpp createdumpwindows.cpp createdump.rc @@ -55,7 +56,7 @@ else(CLR_CMAKE_HOST_WIN32) include_directories(${CMAKE_BINARY_DIR}) set(CREATEDUMP_SOURCES - main.cpp + createdumpmain.cpp dumpname.cpp createdumpunix.cpp crashinfo.cpp @@ -66,18 +67,26 @@ else(CLR_CMAKE_HOST_WIN32) ) if(CLR_CMAKE_HOST_OSX) - add_executable_clr(createdump + add_library_clr(createdump_static + STATIC crashinfomac.cpp threadinfomac.cpp dumpwritermacho.cpp ${CREATEDUMP_SOURCES} ) -else() add_executable_clr(createdump + main.cpp + ) +else() + add_library_clr(createdump_static + STATIC crashinfounix.cpp threadinfounix.cpp dumpwriterelf.cpp ${CREATEDUMP_SOURCES} + ) + add_executable_clr(createdump + main.cpp ${PAL_REDEFINES_FILE} ) add_dependencies(createdump pal_redefines_file) @@ -85,6 +94,7 @@ endif(CLR_CMAKE_HOST_OSX) target_link_libraries(createdump PRIVATE + createdump_static corguids dbgutil # share the PAL in the dac module diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 5addb7903ccc9..9b7120cffb5f4 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -3,6 +3,9 @@ #include "createdump.h" +typedef BOOL (PALAPI_NOEXPORT *PFN_DLLMAIN)(HINSTANCE, DWORD, LPVOID); /* entry point of module */ +typedef HINSTANCE (PALAPI_NOEXPORT *PFN_REGISTER_MODULE)(LPCSTR); /* used to create the HINSTANCE for above DLLMain entry point */ + // This is for the PAL_VirtualUnwindOutOfProc read memory adapter. CrashInfo* g_crashInfo; @@ -12,9 +15,11 @@ CrashInfo::CrashInfo(const CreateDumpOptions& options) : m_ref(1), m_pid(options.Pid), m_ppid(-1), - m_hdac(nullptr), + m_dacModule(nullptr), m_pClrDataEnumRegions(nullptr), m_pClrDataProcess(nullptr), + m_singleFile(options.SingleFile), + m_nativeAOT(options.NativeAOT), m_gatherFrames(options.CrashReport), m_crashThread(options.CrashThread), m_signal(options.Signal), @@ -30,12 +35,12 @@ CrashInfo::CrashInfo(const CreateDumpOptions& options) : #else m_auxvValues.fill(0); m_fdMem = -1; +#endif memset(&m_siginfo, 0, sizeof(m_siginfo)); m_siginfo.si_signo = options.Signal; m_siginfo.si_code = options.SignalCode; m_siginfo.si_errno = options.SignalErrno; m_siginfo.si_addr = options.SignalAddress; -#endif } CrashInfo::~CrashInfo() @@ -64,10 +69,10 @@ CrashInfo::~CrashInfo() m_pClrDataProcess->Release(); } // Unload DAC module - if (m_hdac != nullptr) + if (m_dacModule != nullptr) { - FreeLibrary(m_hdac); - m_hdac = nullptr; + dlclose(m_dacModule); + m_dacModule = nullptr; } #ifdef __APPLE__ if (m_task != 0) @@ -145,7 +150,7 @@ CrashInfo::LogMessage( // Gather all the necessary crash dump info. // bool -CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) +CrashInfo::GatherCrashInfo(DumpType* dumpType) { // Get the info about the threads (registers, etc.) for (ThreadInfo* thread : m_threads) @@ -204,8 +209,13 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) region.Trace(); } } + // If the DAC module present side-by-side (the default for single-file and native AOT apps), fallback to full dump. + if (m_pClrDataProcess == nullptr) + { + *dumpType = DumpType::Full; + } // If full memory dump, include everything regardless of permissions - if (minidumpType & MiniDumpWithFullMemory) + if (*dumpType == DumpType::Full) { for (const MemoryRegion& region : m_moduleMappings) { @@ -224,7 +234,7 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) { // Add all the heap read/write memory regions (m_otherMappings contains the heaps). On Alpine // the heap regions are marked RWX instead of just RW. - if (minidumpType & MiniDumpWithPrivateReadWriteMemory) + if (*dumpType == DumpType::Heap) { for (const MemoryRegion& region : m_otherMappings) { @@ -254,18 +264,18 @@ GetHResultString(HRESULT hr) { switch (hr) { - case E_FAIL: - return "The operation has failed"; - case E_INVALIDARG: - return "Invalid argument"; - case E_OUTOFMEMORY: - return "Out of memory"; - case CORDBG_E_INCOMPATIBLE_PLATFORMS: - return "The operation failed because debuggee and debugger are on incompatible platforms"; - case CORDBG_E_MISSING_DEBUGGER_EXPORTS: - return "The debuggee memory space does not have the expected debugging export table"; - case CORDBG_E_UNSUPPORTED: - return "The specified action is unsupported by this version of the runtime"; + case E_FAIL: + return "The operation has failed"; + case E_INVALIDARG: + return "Invalid argument"; + case E_OUTOFMEMORY: + return "Out of memory"; + case CORDBG_E_INCOMPATIBLE_PLATFORMS: + return "The operation failed because debuggee and debugger are on incompatible platforms"; + case CORDBG_E_MISSING_DEBUGGER_EXPORTS: + return "The debuggee memory space does not have the expected debugging export table"; + case CORDBG_E_UNSUPPORTED: + return "The specified action is unsupported by this version of the runtime"; } return ""; } @@ -276,47 +286,77 @@ GetHResultString(HRESULT hr) bool CrashInfo::InitializeDAC() { + // Don't attempt to load the DAC if createdump is statically linked into the runtime + //if (m_singleFile || m_nativeAOT) + if (m_nativeAOT) + { + return true; + } + // Can't load the DAC if the runtime wasn't found + if (m_coreclrPath.empty()) + { + printf_error("InitializeDAC: coreclr not found; not using DAC\n"); + return true; + } ReleaseHolder dataTarget = new DumpDataTarget(*this); PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr; + PFN_DLLMAIN pfnDllMain = nullptr; bool result = false; HRESULT hr = S_OK; - if (!m_coreclrPath.empty()) - { - // We assume that the DAC is in the same location as the libcoreclr.so module - std::string dacPath; - dacPath.append(m_coreclrPath); - dacPath.append(MAKEDLLNAME_A("mscordaccore")); + // We assume that the DAC is in the same location as the libcoreclr.so module + std::string dacPath; + dacPath.append(m_coreclrPath); + dacPath.append(MAKEDLLNAME_A("mscordaccore")); - // Load and initialize the DAC - m_hdac = LoadLibraryA(dacPath.c_str()); - if (m_hdac == nullptr) + // Load and initialize the DAC + m_dacModule = dlopen(dacPath.c_str(), RTLD_LAZY); + if (m_dacModule == nullptr) + { + // Don't fail for single-file apps when the DAC can't be found. Will fall back to full dump. + if (m_singleFile) { - printf_error("InitializeDAC: LoadLibraryA(%s) FAILED %s\n", dacPath.c_str(), GetLastErrorString().c_str()); - goto exit; + result = true; } - pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(m_hdac, "CLRDataCreateInstance"); - if (pfnCLRDataCreateInstance == nullptr) + else { - printf_error("InitializeDAC: GetProcAddress(CLRDataCreateInstance) FAILED %s\n", GetLastErrorString().c_str()); - goto exit; + printf_error("InitializeDAC: dlopen(%s) FAILED %s\n", dacPath.c_str(), dlerror()); } - hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&m_pClrDataEnumRegions); - if (FAILED(hr)) + goto exit; + } + pfnDllMain = (PFN_DLLMAIN)dlsym(m_dacModule, "DllMain"); + if (pfnDllMain != nullptr) + { + PFN_REGISTER_MODULE registerModule = (PFN_REGISTER_MODULE)dlsym(m_dacModule, "PAL_RegisterModule"); + if (registerModule == nullptr) { - printf_error("InitializeDAC: CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %s (%08x)\n", GetHResultString(hr), hr); + printf_error("InitializeDAC: PAL_RegisterModule FAILED\n"); goto exit; } - hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&m_pClrDataProcess); - if (FAILED(hr)) + HINSTANCE hModule = registerModule(dacPath.c_str()); + if (!pfnDllMain(hModule, DLL_PROCESS_ATTACH, nullptr)) { - printf_error("InitializeDAC: CLRDataCreateInstance(IXCLRDataProcess) FAILED %s (%08x)\n", GetHResultString(hr), hr); + printf_error("InitializeDAC: DllMain(DLL_PROCESS_ATTACH) FAILED\n"); goto exit; } } - else + pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)dlsym(m_dacModule, "CLRDataCreateInstance"); + if (pfnCLRDataCreateInstance == nullptr) { - printf_error("InitializeDAC: coreclr not found; not using DAC\n"); + printf_error("InitializeDAC: GetProcAddress(CLRDataCreateInstance) FAILED %s\n", dlerror()); + goto exit; + } + hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&m_pClrDataEnumRegions); + if (FAILED(hr)) + { + printf_error("InitializeDAC: CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %s (%08x)\n", GetHResultString(hr), hr); + goto exit; + } + hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&m_pClrDataProcess); + if (FAILED(hr)) + { + printf_error("InitializeDAC: CLRDataCreateInstance(IXCLRDataProcess) FAILED %s (%08x)\n", GetHResultString(hr), hr); + goto exit; } result = true; exit: @@ -327,9 +367,9 @@ CrashInfo::InitializeDAC() // Enumerate all the memory regions using the DAC memory region support given a minidump type // bool -CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) +CrashInfo::EnumerateMemoryRegionsWithDAC(DumpType dumpType) { - if (m_pClrDataEnumRegions != nullptr && (minidumpType & MiniDumpWithFullMemory) == 0) + if (m_pClrDataEnumRegions != nullptr && dumpType != DumpType::Full) { TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration STARTED (%d %d)\n", m_enumMemoryPagesAdded, m_dataTargetPagesAdded); @@ -337,7 +377,8 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) // low level data structures and adds all the loader allocator heaps instead. The older 'DbgEnableFastHeapDumps' // env var didn't generate a complete enough heap dump on Linux and this new path does. CLRDataEnumMemoryFlags flags = CLRDATA_ENUM_MEM_HEAP2; - if (minidumpType & MiniDumpWithPrivateReadWriteMemory) + MINIDUMP_TYPE minidumpType = GetMiniDumpType(dumpType); + if (dumpType == DumpType::Heap) { // This is the old fast heap env var for backwards compatibility for VS4Mac. CLRConfigNoCache fastHeapDumps = CLRConfigNoCache::Get("DbgEnableFastHeapDumps", /*noprefix*/ false, &getenv); diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index de00dee4bbac3..99249f43039f2 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -48,26 +48,25 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi pid_t m_pid; // pid pid_t m_ppid; // parent pid pid_t m_tgid; // process group - HMODULE m_hdac; // dac module handle when loaded + void* m_dacModule; // dac module pointer when loaded ICLRDataEnumMemoryRegions* m_pClrDataEnumRegions; // dac enumerate memory interface instance IXCLRDataProcess* m_pClrDataProcess; // dac process interface instance + bool m_singleFile; // if true, single-file app. Checks for the single-file export in every module. + bool m_nativeAOT; // if true, native AOT app. 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 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 + uint64_t m_runtimeBaseAddress; // base address of the runtime module #ifdef __APPLE__ vm_map_t m_task; // the mach task for the process + std::set m_allMemoryRegions; // all memory regions on MacOS #else - siginfo_t m_siginfo; // signal info (if any) bool m_canUseProcVmReadSyscall; int m_fdMem; // /proc//mem handle int m_fdPagemap; // /proc//pagemap handle -#endif - std::string m_coreclrPath; // the path of the coreclr module or empty if none - uint64_t m_runtimeBaseAddress; -#ifdef __APPLE__ - std::set m_allMemoryRegions; // all memory regions on MacOS -#else std::array m_auxvValues; // auxv values std::vector m_auxvEntries; // full auxv entries #endif @@ -95,9 +94,9 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi bool Initialize(); void CleanupAndResumeProcess(); bool EnumerateAndSuspendThreads(); - bool GatherCrashInfo(MINIDUMP_TYPE minidumpType); + bool GatherCrashInfo(DumpType* dumpType); void CombineMemoryRegions(); - bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType); + bool EnumerateMemoryRegionsWithDAC(DumpType dumpType); bool ReadMemory(void* address, void* buffer, size_t size); // read memory and add to dump bool ReadProcessMemory(void* address, void* buffer, size_t size, size_t* read); // read raw memory uint64_t GetBaseAddressFromAddress(uint64_t address); @@ -125,10 +124,10 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi inline const std::set& ModuleMappings() const { return m_moduleMappings; } inline const std::set& OtherMappings() const { return m_otherMappings; } inline const std::set& MemoryRegions() const { return m_memoryRegions; } + inline const siginfo_t* SigInfo() const { return &m_siginfo; } #ifndef __APPLE__ inline const std::vector& AuxvEntries() const { return m_auxvEntries; } inline size_t GetAuxvSize() const { return m_auxvEntries.size() * sizeof(elf_aux_entry); } - inline const siginfo_t* SigInfo() const { return &m_siginfo; } #endif // IUnknown diff --git a/src/coreclr/debug/createdump/crashinfomac.cpp b/src/coreclr/debug/createdump/crashinfomac.cpp index 21ec72ff23417..8ae9ac5290c55 100644 --- a/src/coreclr/debug/createdump/crashinfomac.cpp +++ b/src/coreclr/debug/createdump/crashinfomac.cpp @@ -245,7 +245,7 @@ void CrashInfo::VisitModule(MachOModule& module) TRACE("TryLookupSymbol(" DACCESS_TABLE_SYMBOL ") FAILED\n"); } } - else if (g_checkForSingleFile) + else if (m_singleFile) { uint64_t symbolOffset; if (module.TryLookupSymbol("DotNetRuntimeInfo", &symbolOffset)) diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index 20b2494f329db..fd9443e9abf84 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -17,7 +17,7 @@ bool CrashInfo::Initialize() { char memPath[128]; - _snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%lu/mem", m_pid); + _snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%d/mem", m_pid); m_fdMem = open(memPath, O_RDONLY); if (m_fdMem == -1) @@ -42,7 +42,7 @@ CrashInfo::Initialize() { TRACE("DbgDisablePagemapUse detected - pagemap file checking is enabled\n"); char pagemapPath[128]; - _snprintf_s(pagemapPath, sizeof(pagemapPath), sizeof(pagemapPath), "/proc/%lu/pagemap", m_pid); + _snprintf_s(pagemapPath, sizeof(pagemapPath), sizeof(pagemapPath), "/proc/%d/pagemap", m_pid); m_fdPagemap = open(pagemapPath, O_RDONLY); if (m_fdPagemap == -1) { @@ -95,7 +95,7 @@ bool CrashInfo::EnumerateAndSuspendThreads() { char taskPath[128]; - snprintf(taskPath, sizeof(taskPath), "/proc/%d/task", m_pid); + _snprintf_s(taskPath, sizeof(taskPath), sizeof(taskPath), "/proc/%d/task", m_pid); DIR* taskDir = opendir(taskPath); if (taskDir == nullptr) @@ -139,7 +139,7 @@ bool CrashInfo::GetAuxvEntries() { char auxvPath[128]; - snprintf(auxvPath, sizeof(auxvPath), "/proc/%d/auxv", m_pid); + _snprintf_s(auxvPath, sizeof(auxvPath), sizeof(auxvPath), "/proc/%d/auxv", m_pid); int fd = open(auxvPath, O_RDONLY, 0); if (fd == -1) @@ -195,7 +195,7 @@ CrashInfo::EnumerateMemoryRegions() // Making something like: /proc/123/maps char mapPath[128]; - int chars = snprintf(mapPath, sizeof(mapPath), "/proc/%d/maps", m_pid); + int chars = _snprintf_s(mapPath, sizeof(mapPath), sizeof(mapPath), "/proc/%d/maps", m_pid); assert(chars > 0 && (size_t)chars <= sizeof(mapPath)); FILE* mapsFile = fopen(mapPath, "r"); @@ -339,7 +339,7 @@ CrashInfo::VisitModule(uint64_t baseAddress, std::string& moduleName) } } } - else if (g_checkForSingleFile) + else if (m_singleFile) { if (PopulateForSymbolLookup(baseAddress)) { @@ -489,7 +489,7 @@ bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name) { char statusPath[128]; - snprintf(statusPath, sizeof(statusPath), "/proc/%d/status", pid); + _snprintf_s(statusPath, sizeof(statusPath), sizeof(statusPath), "/proc/%d/status", pid); FILE *statusFile = fopen(statusPath, "r"); if (statusFile == nullptr) @@ -538,6 +538,13 @@ ModuleInfo::LoadModule() if (m_module == nullptr) { m_module = dlopen(m_moduleName.c_str(), RTLD_LAZY); - m_localBaseAddress = ((struct link_map*)m_module)->l_addr; + if (m_module != nullptr) + { + m_localBaseAddress = ((struct link_map*)m_module)->l_addr; + } + else + { + TRACE("LoadModule: dlopen(%s) FAILED %s\n", m_moduleName.c_str(), dlerror()); + } } } diff --git a/src/coreclr/debug/createdump/crashreportwriter.cpp b/src/coreclr/debug/createdump/crashreportwriter.cpp index a092e93b42428..77081fad39236 100644 --- a/src/coreclr/debug/createdump/crashreportwriter.cpp +++ b/src/coreclr/debug/createdump/crashreportwriter.cpp @@ -376,7 +376,7 @@ void CrashReportWriter::WriteValue32(const char* key, uint32_t value) { char buffer[16]; - snprintf(buffer, sizeof(buffer), "0x%x", value); + _snprintf_s(buffer, sizeof(buffer), sizeof(buffer), "0x%x", value); WriteValue(key, buffer); } @@ -384,7 +384,7 @@ void CrashReportWriter::WriteValue64(const char* key, uint64_t value) { char buffer[32]; - snprintf(buffer, sizeof(buffer), "0x%" PRIx64, value); + _snprintf_s(buffer, sizeof(buffer), sizeof(buffer), "0x%" PRIx64, value); WriteValue(key, buffer); } diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h index 0bc6ea73bcfa6..3f0e122353930 100644 --- a/src/coreclr/debug/createdump/createdump.h +++ b/src/coreclr/debug/createdump/createdump.h @@ -10,7 +10,6 @@ extern bool g_diagnostics; extern bool g_diagnosticsVerbose; #ifdef HOST_UNIX -extern bool g_checkForSingleFile; extern void trace_printf(const char* format, ...); extern void trace_verbose_printf(const char* format, ...); #define TRACE(args...) trace_printf(args) @@ -91,21 +90,28 @@ typedef int T_CONTEXT; #include #include +enum DumpType +{ + Mini, + Heap, + Triage, + Full +}; + typedef struct { const char* DumpPathTemplate; - const char* DumpType; - MINIDUMP_TYPE MinidumpType; + enum DumpType DumpType; + bool SingleFile; + bool NativeAOT; bool CreateDump; bool CrashReport; int Pid; int CrashThread; int Signal; -#if defined(HOST_UNIX) int SignalCode; int SignalErrno; void* SignalAddress; -#endif } CreateDumpOptions; #ifdef HOST_UNIX @@ -130,7 +136,11 @@ typedef struct extern bool CreateDump(const CreateDumpOptions& options); extern bool FormatDumpName(std::string& name, const char* pattern, const char* exename, int pid); +extern const char* GetDumpTypeString(DumpType dumpType); +extern MINIDUMP_TYPE GetMiniDumpType(DumpType dumpType); +#ifdef HOST_WINDOWS extern std::string GetLastErrorString(); +#endif extern void printf_status(const char* format, ...); extern void printf_error(const char* format, ...); diff --git a/src/coreclr/debug/createdump/createdumpmain.cpp b/src/coreclr/debug/createdump/createdumpmain.cpp new file mode 100644 index 0000000000000..a56f42f3ef1c2 --- /dev/null +++ b/src/coreclr/debug/createdump/createdumpmain.cpp @@ -0,0 +1,420 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "createdump.h" + +#ifdef HOST_WINDOWS +#define DEFAULT_DUMP_PATH "%TEMP%\\" +#define DEFAULT_DUMP_TEMPLATE "dump.%p.dmp" +#else +#define DEFAULT_DUMP_PATH "/tmp/" +#define DEFAULT_DUMP_TEMPLATE "coredump.%p" +#endif + +#ifdef HOST_UNIX +const char* g_help = "createdump [options] pid\n" +#else +const char* g_help = "createdump [options]\n" +#endif +"-f, --name - dump path and file name. The default is '" DEFAULT_DUMP_PATH DEFAULT_DUMP_TEMPLATE "'. These specifiers are substituted with following values:\n" +" %p PID of dumped process.\n" +" %e The process executable filename.\n" +" %h Hostname return by gethostname().\n" +" %t Time of dump, expressed as seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).\n" +"-n, --normal - create minidump.\n" +"-h, --withheap - create minidump with heap (default).\n" +"-t, --triage - create triage minidump.\n" +"-u, --full - create full core dump.\n" +"-d, --diag - enable diagnostic messages.\n" +"-v, --verbose - enable verbose diagnostic messages.\n" +"-l, --logtofile - file path and name to log diagnostic messages.\n" +#ifdef HOST_UNIX +"--crashreport - write crash report file (dump file path + .crashreport.json).\n" +"--crashreportonly - write crash report file only (no dump).\n" +"--crashthread - the thread id of the crashing thread.\n" +"--signal - the signal code of the crash.\n" +"--singlefile - enable single-file app check.\n" +#endif +; + + +FILE *g_logfile = nullptr; +FILE *g_stdout = stdout; +bool g_diagnostics = false; +bool g_diagnosticsVerbose = false; +uint64_t g_ticksPerMS = 0; +uint64_t g_startTime = 0; +uint64_t GetTickFrequency(); +uint64_t GetTimeStamp(); + +// +// Common entry point +// +int createdump_main(const int argc, const char* argv[]) +{ +#ifdef HOST_UNIX + CLRConfigNoCache waitForAttach = CLRConfigNoCache::Get("CreateDumpWaitForAttach", /*noprefix*/ false, &getenv); + DWORD value = 0; + if (waitForAttach.IsSet() && waitForAttach.TryAsInteger(10, value) && value == 1) + { + fprintf(stderr, "[createdump] waiting for attach %u: ", getpid()); + fgetc(stdin); + } +#endif + CreateDumpOptions options; + options.DumpType = DumpType::Heap; + options.DumpPathTemplate = nullptr; + options.SingleFile = false; + options.NativeAOT = false; + options.CrashReport = false; + options.CreateDump = true; + 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 + bool help = false; + int exitCode = 0; + + // Parse the command line options and target pid + argv++; + for (int i = 1; i < argc; i++) + { + if (*argv != nullptr) + { + if ((strcmp(*argv, "-f") == 0) || (strcmp(*argv, "--name") == 0)) + { + options.DumpPathTemplate = *++argv; + } + else if ((strcmp(*argv, "-n") == 0) || (strcmp(*argv, "--normal") == 0)) + { + options.DumpType = DumpType::Mini; + } + else if ((strcmp(*argv, "-h") == 0) || (strcmp(*argv, "--withheap") == 0)) + { + options.DumpType = DumpType::Heap; + } + else if ((strcmp(*argv, "-t") == 0) || (strcmp(*argv, "--triage") == 0)) + { + options.DumpType = DumpType::Triage; + } + else if ((strcmp(*argv, "-u") == 0) || (strcmp(*argv, "--full") == 0)) + { + options.DumpType = DumpType::Full; + } +#ifdef HOST_UNIX + else if (strcmp(*argv, "--crashreport") == 0) + { + options.CrashReport = true; + } + else if (strcmp(*argv, "--crashreportonly") == 0) + { + options.CrashReport = true; + options.CreateDump = false; + } + else if (strcmp(*argv, "--crashthread") == 0) + { + options.CrashThread = atoi(*++argv); + } + else if (strcmp(*argv, "--signal") == 0) + { + options.Signal = atoi(*++argv); + } + else if (strcmp(*argv, "--singlefile") == 0) + { + options.SingleFile = true; + } + else if (strcmp(*argv, "--code") == 0) + { + options.SignalCode = atoi(*++argv); + } + else if (strcmp(*argv, "--errno") == 0) + { + options.SignalErrno = atoi(*++argv); + } + else if (strcmp(*argv, "--address") == 0) + { + options.SignalAddress = (void*)atoll(*++argv); + } +#endif + else if ((strcmp(*argv, "-d") == 0) || (strcmp(*argv, "--diag") == 0)) + { + g_diagnostics = true; + } + else if ((strcmp(*argv, "-v") == 0) || (strcmp(*argv, "--verbose") == 0)) + { + g_diagnostics = true; + g_diagnosticsVerbose = true; + } + else if ((strcmp(*argv, "-l") == 0) || (strcmp(*argv, "--logtofile") == 0)) + { + const char* logFilePath = *++argv; + g_logfile = fopen(logFilePath, "w"); + if (g_logfile == nullptr) + { + printf_error("Can not create log file '%s': %s (%d)\n", logFilePath, strerror(errno), errno); + return errno; + } + g_stdout = g_logfile; + } + else if ((strcmp(*argv, "-?") == 0) || (strcmp(*argv, "--help") == 0)) + { + help = true; + } + else + { +#ifdef HOST_UNIX + options.Pid = atoi(*argv); +#else + printf_error("The pid argument is no longer supported\n"); + return -1; +#endif + } + argv++; + } + } + +#ifdef HOST_UNIX + if (options.Pid == 0) + { + help = true; + } +#endif + + if (help) + { + // if no pid or invalid command line option + printf_error("%s", g_help); + return -1; + } + + g_ticksPerMS = GetTickFrequency() / 1000UL; + g_startTime = GetTimeStamp(); + TRACE("TickFrequency: %d ticks per ms\n", g_ticksPerMS); + + ArrayHolder tmpPath = new char[MAX_LONGPATH]; + if (options.DumpPathTemplate == nullptr) + { + if (::GetTempPathA(MAX_LONGPATH, tmpPath) == 0) + { + //printf_error("GetTempPath failed %s", GetLastErrorString().c_str()); + printf_error("GetTempPath failed\n"); + return ::GetLastError(); + } + exitCode = strcat_s(tmpPath, MAX_LONGPATH, DEFAULT_DUMP_TEMPLATE); + if (exitCode != 0) + { + printf_error("strcat_s failed (%d)", exitCode); + return exitCode; + } + options.DumpPathTemplate = tmpPath; + } + + if (CreateDump(options)) + { + printf_status("Dump successfully written in %llums\n", GetTimeStamp() - g_startTime); + } + else + { + printf_error("Failure took %llums\n", GetTimeStamp() - g_startTime); + exitCode = -1; + } + + fflush(g_stdout); + + if (g_logfile != nullptr) + { + fflush(g_logfile); + fclose(g_logfile); + } + return exitCode; +} + +const char* +GetDumpTypeString(DumpType dumpType) +{ + switch (dumpType) + { + case DumpType::Mini: + return "minidump"; + case DumpType::Heap: + return "minidump with heap"; + case DumpType::Triage: + return "triage minidump"; + case DumpType::Full: + return "full dump"; + default: + return "unknown"; + } +} + +MINIDUMP_TYPE +GetMiniDumpType(DumpType dumpType) +{ + switch (dumpType) + { + case DumpType::Mini: + return (MINIDUMP_TYPE)(MiniDumpNormal | + MiniDumpWithDataSegs | + MiniDumpWithHandleData | + MiniDumpWithThreadInfo); + case DumpType::Heap: + return (MINIDUMP_TYPE)(MiniDumpWithPrivateReadWriteMemory | + MiniDumpWithDataSegs | + MiniDumpWithHandleData | + MiniDumpWithUnloadedModules | + MiniDumpWithFullMemoryInfo | + MiniDumpWithThreadInfo | + MiniDumpWithTokenInformation); + case DumpType::Triage: + return (MINIDUMP_TYPE)(MiniDumpFilterTriage | + MiniDumpIgnoreInaccessibleMemory | + MiniDumpWithoutOptionalData | + MiniDumpWithProcessThreadData | + MiniDumpFilterModulePaths | + MiniDumpWithUnloadedModules | + MiniDumpFilterMemory | + MiniDumpWithHandleData); + case DumpType::Full: + default: + return (MINIDUMP_TYPE)(MiniDumpWithFullMemory | + MiniDumpWithDataSegs | + MiniDumpWithHandleData | + MiniDumpWithUnloadedModules | + MiniDumpWithFullMemoryInfo | + MiniDumpWithThreadInfo | + MiniDumpWithTokenInformation); + } +} + +void +printf_status(const char* format, ...) +{ + va_list args; + va_start(args, format); + if (g_logfile == nullptr) + { + fprintf(g_stdout, "[createdump] "); + } + vfprintf(g_stdout, format, args); + fflush(g_stdout); + va_end(args); +} + +void +printf_error(const char* format, ...) +{ + va_list args; + va_start(args, format); + + // Log error message to file + if (g_logfile != nullptr) + { + va_list args2; + va_copy(args2, args); + vfprintf(g_logfile, format, args2); + fflush(g_logfile); + } + // Always print errors on stderr + fprintf(stderr, "[createdump] "); + vfprintf(stderr, format, args); + fflush(stderr); + va_end(args); +} + +uint64_t +GetTickFrequency() +{ + LARGE_INTEGER ret; + ZeroMemory(&ret, sizeof(LARGE_INTEGER)); + QueryPerformanceFrequency(&ret); + return ret.QuadPart; +} + +uint64_t +GetTimeStamp() +{ + LARGE_INTEGER ret; + ZeroMemory(&ret, sizeof(LARGE_INTEGER)); + QueryPerformanceCounter(&ret); + return ret.QuadPart / g_ticksPerMS; +} + +#ifdef HOST_UNIX + +static void +trace_prefix() +{ + // Only add this prefix if logging to the console + if (g_logfile == nullptr) + { + fprintf(g_stdout, "[createdump] "); + } + fprintf(g_stdout, "%08" PRIx64 " ", GetTimeStamp()); +} + +void +trace_printf(const char* format, ...) +{ + if (g_diagnostics) + { + va_list args; + va_start(args, format); + trace_prefix(); + vfprintf(g_stdout, format, args); + fflush(g_stdout); + va_end(args); + } +} + +void +trace_verbose_printf(const char* format, ...) +{ + if (g_diagnosticsVerbose) + { + va_list args; + va_start(args, format); + trace_prefix(); + vfprintf(g_stdout, format, args); + fflush(g_stdout); + va_end(args); + } +} + +void +CrashInfo::Trace(const char* format, ...) +{ + if (g_diagnostics) + { + va_list args; + va_start(args, format); + trace_prefix(); + vfprintf(g_stdout, format, args); + fflush(g_stdout); + va_end(args); + } +} + +void +CrashInfo::TraceVerbose(const char* format, ...) +{ + if (g_diagnosticsVerbose) + { + va_list args; + va_start(args, format); + trace_prefix(); + vfprintf(g_stdout, format, args); + fflush(g_stdout); + va_end(args); + } +} + +void initialize_static_createdump() +{ + PAL_SetCreateDumpCallback(createdump_main); +} + +#endif // HOST_UNIX diff --git a/src/coreclr/debug/createdump/createdumpunix.cpp b/src/coreclr/debug/createdump/createdumpunix.cpp index eec103af880f1..cc119943a76b8 100644 --- a/src/coreclr/debug/createdump/createdumpunix.cpp +++ b/src/coreclr/debug/createdump/createdumpunix.cpp @@ -14,6 +14,7 @@ bool CreateDump(const CreateDumpOptions& options) { ReleaseHolder crashInfo = new CrashInfo(options); + DumpType dumpType = options.DumpType; DumpWriter dumpWriter(*crashInfo); std::string dumpPath; bool result = false; @@ -42,7 +43,7 @@ CreateDump(const CreateDumpOptions& options) goto exit; } // Gather all the info about the process, threads (registers, etc.) and memory regions - if (!crashInfo->GatherCrashInfo(options.MinidumpType)) + if (!crashInfo->GatherCrashInfo(&dumpType)) { goto exit; } @@ -51,8 +52,8 @@ CreateDump(const CreateDumpOptions& options) { goto exit; } - // Write the crash report json file if enabled - if (options.CrashReport) + // Write the crash report json file if enabled and createdump isn't statically linked into the runtime + if (options.CrashReport && !options.SingleFile && !options.NativeAOT) { CrashReportWriter crashReportWriter(*crashInfo); crashReportWriter.WriteCrashReport(dumpPath); @@ -60,14 +61,14 @@ CreateDump(const CreateDumpOptions& options) if (options.CreateDump) { // Gather all the useful memory regions from the DAC - if (!crashInfo->EnumerateMemoryRegionsWithDAC(options.MinidumpType)) + if (!crashInfo->EnumerateMemoryRegionsWithDAC(dumpType)) { goto exit; } // Join all adjacent memory regions crashInfo->CombineMemoryRegions(); - printf_status("Writing %s to file %s\n", options.DumpType, dumpPath.c_str()); + printf_status("Writing %s to file %s\n", GetDumpTypeString(dumpType), dumpPath.c_str()); // Write the actual dump file if (!dumpWriter.OpenDump(dumpPath.c_str())) diff --git a/src/coreclr/debug/createdump/createdumpwindows.cpp b/src/coreclr/debug/createdump/createdumpwindows.cpp index 4e8429255a343..759ec3c52f678 100644 --- a/src/coreclr/debug/createdump/createdumpwindows.cpp +++ b/src/coreclr/debug/createdump/createdumpwindows.cpp @@ -55,7 +55,7 @@ CreateDump(const CreateDumpOptions& options) { goto exit; } - printf_status("Writing %s for process %d to file %s\n", options.DumpType, pid, dumpPath.c_str()); + printf_status("Writing %s for process %d to file %s\n", GetDumpTypeString(options.DumpType), pid, dumpPath.c_str()); hFile = CreateFileA(dumpPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) @@ -67,7 +67,7 @@ CreateDump(const CreateDumpOptions& options) // Retry the write dump on ERROR_PARTIAL_COPY for (int i = 0; i < 5; i++) { - if (MiniDumpWriteDump(hProcess, pid, hFile, options.MinidumpType, NULL, NULL, NULL)) + if (MiniDumpWriteDump(hProcess, pid, hFile, GetMiniDumpType(options.DumpType), NULL, NULL, NULL)) { result = true; break; @@ -96,3 +96,37 @@ CreateDump(const CreateDumpOptions& options) return result; } + +std::string +GetLastErrorString() +{ + DWORD error = GetLastError(); + std::string result; + LPSTR messageBuffer; + DWORD length = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&messageBuffer, + 0, + NULL); + if (length > 0) + { + result.append(messageBuffer, length); + LocalFree(messageBuffer); + + // Remove the \r\n at the end of the system message. Assumes that the \r is first. + size_t found = result.find_last_of('\r'); + if (found != std::string::npos) + { + result.erase(found); + } + result.append(" "); + } + char buffer[64]; + _snprintf_s(buffer, sizeof(buffer), sizeof(buffer), "(%d)", error); + result.append(buffer); + return result; +} + diff --git a/src/coreclr/debug/createdump/main.cpp b/src/coreclr/debug/createdump/main.cpp index 75a616dc2e47b..822947271aff1 100644 --- a/src/coreclr/debug/createdump/main.cpp +++ b/src/coreclr/debug/createdump/main.cpp @@ -3,51 +3,12 @@ #include "createdump.h" -#ifdef HOST_WINDOWS -#define DEFAULT_DUMP_PATH "%TEMP%\\" -#define DEFAULT_DUMP_TEMPLATE "dump.%p.dmp" -#else -#define DEFAULT_DUMP_PATH "/tmp/" -#define DEFAULT_DUMP_TEMPLATE "coredump.%p" -#endif +extern int createdump_main(const int argc, const char* argv[]); -#ifdef HOST_UNIX -const char* g_help = "createdump [options] pid\n" -#else -const char* g_help = "createdump [options]\n" -#endif -"-f, --name - dump path and file name. The default is '" DEFAULT_DUMP_PATH DEFAULT_DUMP_TEMPLATE "'. These specifiers are substituted with following values:\n" -" %p PID of dumped process.\n" -" %e The process executable filename.\n" -" %h Hostname return by gethostname().\n" -" %t Time of dump, expressed as seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).\n" -"-n, --normal - create minidump.\n" -"-h, --withheap - create minidump with heap (default).\n" -"-t, --triage - create triage minidump.\n" -"-u, --full - create full core dump.\n" -"-d, --diag - enable diagnostic messages.\n" -"-v, --verbose - enable verbose diagnostic messages.\n" -"-l, --logtofile - file path and name to log diagnostic messages.\n" -#ifdef HOST_UNIX -"--crashreport - write crash report file (dump file path + .crashreport.json).\n" -"--crashreportonly - write crash report file only (no dump).\n" -"--crashthread - the thread id of the crashing thread.\n" -"--signal - the signal code of the crash.\n" -"--singlefile - enable single-file app check.\n" -#endif -; - -FILE *g_logfile = nullptr; -FILE *g_stdout = stdout; -bool g_diagnostics = false; -bool g_diagnosticsVerbose = false; -uint64_t g_ticksPerMS = 0; -uint64_t g_startTime = 0; -uint64_t GetTickFrequency(); -uint64_t GetTimeStamp(); - -#ifdef HOST_UNIX -bool g_checkForSingleFile = false; +#if defined(HOST_ARM64) +// Flag to check if atomics feature is available on +// the machine +bool g_arm64_atomics_present = false; #endif // @@ -55,29 +16,7 @@ bool g_checkForSingleFile = false; // int __cdecl main(const int argc, const char* argv[]) { - CreateDumpOptions options; - options.MinidumpType = (MINIDUMP_TYPE)(MiniDumpWithPrivateReadWriteMemory | - MiniDumpWithDataSegs | - MiniDumpWithHandleData | - MiniDumpWithUnloadedModules | - MiniDumpWithFullMemoryInfo | - MiniDumpWithThreadInfo | - MiniDumpWithTokenInformation); - options.DumpType = "minidump with heap"; - options.DumpPathTemplate = nullptr; - options.CrashReport = false; - options.CreateDump = true; - options.Signal = 0; - options.CrashThread = 0; - options.Pid = 0; -#if defined(HOST_UNIX) - options.SignalCode = 0; - options.SignalErrno = 0; - options.SignalAddress = nullptr; -#endif - bool help = false; int exitCode = 0; - #ifdef HOST_UNIX exitCode = PAL_InitializeDLL(); if (exitCode != 0) @@ -86,344 +25,21 @@ int __cdecl main(const int argc, const char* argv[]) return exitCode; } #endif - - // Parse the command line options and target pid - argv++; - for (int i = 1; i < argc; i++) - { - if (*argv != nullptr) - { - if ((strcmp(*argv, "-f") == 0) || (strcmp(*argv, "--name") == 0)) - { - options.DumpPathTemplate = *++argv; - } - else if ((strcmp(*argv, "-n") == 0) || (strcmp(*argv, "--normal") == 0)) - { - options.DumpType = "minidump"; - options.MinidumpType = (MINIDUMP_TYPE)(MiniDumpNormal | - MiniDumpWithDataSegs | - MiniDumpWithHandleData | - MiniDumpWithThreadInfo); - } - else if ((strcmp(*argv, "-h") == 0) || (strcmp(*argv, "--withheap") == 0)) - { - options.DumpType = "minidump with heap"; - options.MinidumpType = (MINIDUMP_TYPE)(MiniDumpWithPrivateReadWriteMemory | - MiniDumpWithDataSegs | - MiniDumpWithHandleData | - MiniDumpWithUnloadedModules | - MiniDumpWithFullMemoryInfo | - MiniDumpWithThreadInfo | - MiniDumpWithTokenInformation); - } - else if ((strcmp(*argv, "-t") == 0) || (strcmp(*argv, "--triage") == 0)) - { - options.DumpType = "triage minidump"; - options.MinidumpType = (MINIDUMP_TYPE)(MiniDumpFilterTriage | - MiniDumpIgnoreInaccessibleMemory | - MiniDumpWithoutOptionalData | - MiniDumpWithProcessThreadData | - MiniDumpFilterModulePaths | - MiniDumpWithUnloadedModules | - MiniDumpFilterMemory | - MiniDumpWithHandleData); - } - else if ((strcmp(*argv, "-u") == 0) || (strcmp(*argv, "--full") == 0)) - { - options.DumpType = "full dump"; - options.MinidumpType = (MINIDUMP_TYPE)(MiniDumpWithFullMemory | - MiniDumpWithDataSegs | - MiniDumpWithHandleData | - MiniDumpWithUnloadedModules | - MiniDumpWithFullMemoryInfo | - MiniDumpWithThreadInfo | - MiniDumpWithTokenInformation); - } -#ifdef HOST_UNIX - else if (strcmp(*argv, "--crashreport") == 0) - { - options.CrashReport = true; - } - else if (strcmp(*argv, "--crashreportonly") == 0) - { - options.CrashReport = true; - options.CreateDump = false; - } - else if (strcmp(*argv, "--crashthread") == 0) - { - options.CrashThread = atoi(*++argv); - } - else if (strcmp(*argv, "--signal") == 0) - { - options.Signal = atoi(*++argv); - } - else if (strcmp(*argv, "--singlefile") == 0) - { - g_checkForSingleFile = true; - } - else if (strcmp(*argv, "--code") == 0) - { - options.SignalCode = atoi(*++argv); - } - else if (strcmp(*argv, "--errno") == 0) - { - options.SignalErrno = atoi(*++argv); - } - else if (strcmp(*argv, "--address") == 0) - { - options.SignalAddress = (void*)atoll(*++argv); - } -#endif - else if ((strcmp(*argv, "-d") == 0) || (strcmp(*argv, "--diag") == 0)) - { - g_diagnostics = true; - } - else if ((strcmp(*argv, "-v") == 0) || (strcmp(*argv, "--verbose") == 0)) - { - g_diagnostics = true; - g_diagnosticsVerbose = true; - } - else if ((strcmp(*argv, "-l") == 0) || (strcmp(*argv, "--logtofile") == 0)) - { - const char* logFilePath = *++argv; - g_logfile = fopen(logFilePath, "w"); - if (g_logfile == nullptr) - { - printf_error("Can not create log file '%s': %s (%d)\n", logFilePath, strerror(errno), errno); - return errno; - } - g_stdout = g_logfile; - } - else if ((strcmp(*argv, "-?") == 0) || (strcmp(*argv, "--help") == 0)) - { - help = true; - } - else - { -#ifdef HOST_UNIX - options.Pid = atoi(*argv); -#else - printf_error("The pid argument is no longer supported\n"); - return -1; -#endif - } - argv++; - } - } - -#ifdef HOST_UNIX - if (options.Pid == 0) - { - help = true; - } -#endif - - if (help) - { - // if no pid or invalid command line option - printf_error("%s", g_help); - return -1; - } - - g_ticksPerMS = GetTickFrequency() / 1000UL; - g_startTime = GetTimeStamp(); - TRACE("TickFrequency: %d ticks per ms\n", g_ticksPerMS); - - ArrayHolder tmpPath = new char[MAX_LONGPATH]; - if (options.DumpPathTemplate == nullptr) - { - if (::GetTempPathA(MAX_LONGPATH, tmpPath) == 0) - { - printf_error("GetTempPath failed %s", GetLastErrorString().c_str()); - return ::GetLastError(); - } - exitCode = strcat_s(tmpPath, MAX_LONGPATH, DEFAULT_DUMP_TEMPLATE); - if (exitCode != 0) - { - printf_error("strcat_s failed (%d)", exitCode); - return exitCode; - } - options.DumpPathTemplate = tmpPath; - } - - if (CreateDump(options)) - { - printf_status("Dump successfully written in %llums\n", GetTimeStamp() - g_startTime); - } - else - { - printf_error("Failure took %llums\n", GetTimeStamp() - g_startTime); - exitCode = -1; - } - - fflush(g_stdout); - - if (g_logfile != nullptr) - { - fflush(g_logfile); - fclose(g_logfile); - } + exitCode = createdump_main(argc, argv); #ifdef HOST_UNIX PAL_TerminateEx(exitCode); #endif return exitCode; } -std::string -GetLastErrorString() -{ - DWORD error = GetLastError(); - std::string result; -#ifdef HOST_WINDOWS - LPSTR messageBuffer; - DWORD length = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&messageBuffer, - 0, - NULL); - if (length > 0) - { - result.append(messageBuffer, length); - LocalFree(messageBuffer); - - // Remove the \r\n at the end of the system message. Assumes that the \r is first. - size_t found = result.find_last_of('\r'); - if (found != std::string::npos) - { - result.erase(found); - } - result.append(" "); - } -#endif - char buffer[64]; - snprintf(buffer, sizeof(buffer), "(%d)", error); - result.append(buffer); - return result; -} - -void -printf_status(const char* format, ...) -{ - va_list args; - va_start(args, format); - if (g_logfile == nullptr) - { - fprintf(g_stdout, "[createdump] "); - } - vfprintf(g_stdout, format, args); - fflush(g_stdout); - va_end(args); -} - -void -printf_error(const char* format, ...) -{ - va_list args; - va_start(args, format); - - // Log error message to file - if (g_logfile != nullptr) - { - va_list args2; - va_copy(args2, args); - vfprintf(g_logfile, format, args2); - fflush(g_logfile); - } - // Always print errors on stderr - fprintf(stderr, "[createdump] "); - vfprintf(stderr, format, args); - fflush(stderr); - va_end(args); -} - -uint64_t -GetTickFrequency() -{ - LARGE_INTEGER ret; - ZeroMemory(&ret, sizeof(LARGE_INTEGER)); - QueryPerformanceFrequency(&ret); - return ret.QuadPart; -} - -uint64_t -GetTimeStamp() -{ - LARGE_INTEGER ret; - ZeroMemory(&ret, sizeof(LARGE_INTEGER)); - QueryPerformanceCounter(&ret); - return ret.QuadPart / g_ticksPerMS; -} - #ifdef HOST_UNIX -static void -trace_prefix() -{ - // Only add this prefix if logging to the console - if (g_logfile == nullptr) - { - fprintf(g_stdout, "[createdump] "); - } - fprintf(g_stdout, "%08" PRIx64 " ", GetTimeStamp()); -} - -void -trace_printf(const char* format, ...) +PALIMPORT +VOID +PALAPI +PAL_SetCreateDumpCallback( + IN PCREATEDUMP_CALLBACK callback) { - if (g_diagnostics) - { - va_list args; - va_start(args, format); - trace_prefix(); - vfprintf(g_stdout, format, args); - fflush(g_stdout); - va_end(args); - } } -void -trace_verbose_printf(const char* format, ...) -{ - if (g_diagnosticsVerbose) - { - va_list args; - va_start(args, format); - trace_prefix(); - vfprintf(g_stdout, format, args); - fflush(g_stdout); - va_end(args); - } -} - -void -CrashInfo::Trace(const char* format, ...) -{ - if (g_diagnostics) - { - va_list args; - va_start(args, format); - trace_prefix(); - vfprintf(g_stdout, format, args); - fflush(g_stdout); - va_end(args); - } -} - -void -CrashInfo::TraceVerbose(const char* format, ...) -{ - if (g_diagnosticsVerbose) - { - va_list args; - va_start(args, format); - trace_prefix(); - vfprintf(g_stdout, format, args); - fflush(g_stdout); - va_end(args); - } -} - -#endif // HOST_UNIX +#endif diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 8e085e3cc15ab..866f76a6a3060 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -343,12 +343,6 @@ PAL_Initialize( int argc, char * const argv[]); -PALIMPORT -void -PALAPI -PAL_InitializeWithFlags( - DWORD flags); - PALIMPORT int PALAPI @@ -402,6 +396,17 @@ PALAPI PAL_SetShutdownCallback( IN PSHUTDOWN_CALLBACK callback); +/// +/// Used by the single-file and native AOT hosts to connect the linked in version of createdump +/// +typedef int (*PCREATEDUMP_CALLBACK)(const int argc, const char* argv[]); + +PALIMPORT +VOID +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 { diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 32da7beef37e6..0c65fa5deeacf 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -236,6 +236,9 @@ static_assert_no_msg(CLR_SEM_MAX_NAMELEN <= MAX_PATH); // Function to call during PAL/process shutdown/abort Volatile g_shutdownCallback = nullptr; +// Function to call instead of exec'ing the createdump binary. Used by single-file and native AOT hosts. +Volatile g_createdumpCallback = nullptr; + // Crash dump generating program arguments. Initialized in PROCAbortInitialize(). std::vector g_argvCreateDump; @@ -1372,6 +1375,25 @@ PAL_SetShutdownCallback( g_shutdownCallback = callback; } +/*++ +Function: + PAL_SetCreateDumpCallback + +Abstract: + Sets a callback that is executed when create dump is launched to create a crash dump. + + NOTE: Currently only one callback can be set at a time. +--*/ +PALIMPORT +VOID +PALAPI +PAL_SetCreateDumpCallback( + IN PCREATEDUMP_CALLBACK callback) +{ + _ASSERTE(g_createdumpCallback == nullptr); + g_createdumpCallback = callback; +} + // Build the semaphore names using the PID and a value that can be used for distinguishing // between processes with the same PID (which ran at different times). This is to avoid // cases where a prior process with the same PID exited abnormally without having a chance @@ -2202,11 +2224,24 @@ PROCCreateCrashDump( { dup2(child_pipe, STDERR_FILENO); } - // Execute the createdump program - if (execve(argv[0], (char**)argv.data(), palEnvironment) == -1) + if (g_createdumpCallback != nullptr) + { + // Remove the signal handlers inherited from the runtime process + SEHCleanupSignals(); + + // Call the statically linked createdump code + fprintf(stderr, "[createdump] entering callback %d\n", gPID); + g_createdumpCallback(argv.size(), argv.data()); + fprintf(stderr, "[createdump] returned from callback\n"); + } + else { - fprintf(stderr, "Problem launching createdump (may not have execute permissions): execve(%s) FAILED %s (%d)\n", argv[0], strerror(errno), errno); - exit(-1); + // Execute the createdump program + if (execve(argv[0], (char**)argv.data(), palEnvironment) == -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 @@ -2252,7 +2287,7 @@ PROCCreateCrashDump( else { #ifdef _DEBUG - fprintf(stderr, "[createdump] waitpid() returned successfully (wstatus %08x)\n", wstatus); + fprintf(stderr, "waitpid() returned successfully (wstatus %08x) WEXITSTATUS %x WTERMSIG %x\n", wstatus, WEXITSTATUS(wstatus), WTERMSIG(wstatus)); #endif return !WIFEXITED(wstatus) || WEXITSTATUS(wstatus) == 0; } diff --git a/src/native/corehost/apphost/static/CMakeLists.txt b/src/native/corehost/apphost/static/CMakeLists.txt index db5d751a3fc6f..d94acbbe30668 100644 --- a/src/native/corehost/apphost/static/CMakeLists.txt +++ b/src/native/corehost/apphost/static/CMakeLists.txt @@ -134,6 +134,7 @@ if(CLR_CMAKE_TARGET_WIN32) else() set(NATIVE_LIBS coreclr_static + createdump_static System.Globalization.Native-Static System.IO.Compression.Native-Static @@ -142,7 +143,9 @@ else() System.Security.Cryptography.Native.OpenSsl-Static palrt - coreclrpal + coreclrpal_dac + corguids + dbgutil eventprovider nativeresourcestring ) diff --git a/src/native/corehost/corehost.cpp b/src/native/corehost/corehost.cpp index be7b76a519964..7a85b6acbb216 100644 --- a/src/native/corehost/corehost.cpp +++ b/src/native/corehost/corehost.cpp @@ -101,6 +101,8 @@ void need_newer_framework_error(const pal::string_t& dotnet_root, const pal::str int exe_start(const int argc, const pal::char_t* argv[]) { + pal::initialize_createdump(); + pal::string_t host_path; if (!pal::get_own_executable_path(&host_path) || !pal::realpath(&host_path)) { diff --git a/src/native/corehost/hostmisc/pal.h b/src/native/corehost/hostmisc/pal.h index 72aa756a93399..f7a07c6a01eca 100644 --- a/src/native/corehost/hostmisc/pal.h +++ b/src/native/corehost/hostmisc/pal.h @@ -335,6 +335,8 @@ namespace pal bool is_emulating_x64(); bool are_paths_equal_with_normalized_casing(const string_t& path1, const string_t& path2); + + void initialize_createdump(); } #endif // PAL_H diff --git a/src/native/corehost/hostmisc/pal.unix.cpp b/src/native/corehost/hostmisc/pal.unix.cpp index fafcb90d679f0..6f1e9641ffe35 100644 --- a/src/native/corehost/hostmisc/pal.unix.cpp +++ b/src/native/corehost/hostmisc/pal.unix.cpp @@ -1106,3 +1106,14 @@ bool pal::are_paths_equal_with_normalized_casing(const string_t& path1, const st return path1 == path2; #endif } + +#if defined(FEATURE_STATIC_HOST) +extern void initialize_static_createdump(); +#endif + +void pal::initialize_createdump() +{ +#if defined(FEATURE_STATIC_HOST) + initialize_static_createdump(); +#endif +} diff --git a/src/native/corehost/hostmisc/pal.windows.cpp b/src/native/corehost/hostmisc/pal.windows.cpp index 6afca1fe41de6..98e4efbed72f0 100644 --- a/src/native/corehost/hostmisc/pal.windows.cpp +++ b/src/native/corehost/hostmisc/pal.windows.cpp @@ -942,3 +942,7 @@ void pal::mutex_t::unlock() { ::LeaveCriticalSection(&_impl); } + +void pal::initialize_createdump() +{ +} From 9a464a62467cdae921955cecd85954f026031582 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Fri, 14 Apr 2023 17:37:34 -0700 Subject: [PATCH 2/7] Fix arm64 builds --- src/coreclr/debug/createdump/datatarget.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/coreclr/debug/createdump/datatarget.cpp b/src/coreclr/debug/createdump/datatarget.cpp index 6f6a68ef70eed..38675532aeb78 100644 --- a/src/coreclr/debug/createdump/datatarget.cpp +++ b/src/coreclr/debug/createdump/datatarget.cpp @@ -4,12 +4,6 @@ #include "createdump.h" #include -#if defined(HOST_ARM64) -// Flag to check if atomics feature is available on -// the machine -bool g_arm64_atomics_present = false; -#endif - DumpDataTarget::DumpDataTarget(CrashInfo& crashInfo) : m_ref(1), m_crashInfo(crashInfo) From b11ef58726ead33a70a850291ee1f34bfd79369f Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Fri, 14 Apr 2023 19:18:11 -0700 Subject: [PATCH 3/7] Fix build again --- src/native/corehost/apphost/static/CMakeLists.txt | 5 ++++- src/native/corehost/hostmisc/pal.unix.cpp | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/native/corehost/apphost/static/CMakeLists.txt b/src/native/corehost/apphost/static/CMakeLists.txt index d94acbbe30668..c07ed8a398d2a 100644 --- a/src/native/corehost/apphost/static/CMakeLists.txt +++ b/src/native/corehost/apphost/static/CMakeLists.txt @@ -134,7 +134,6 @@ if(CLR_CMAKE_TARGET_WIN32) else() set(NATIVE_LIBS coreclr_static - createdump_static System.Globalization.Native-Static System.IO.Compression.Native-Static @@ -150,6 +149,10 @@ else() nativeresourcestring ) + if(CLR_CMAKE_HOST_OSX OR (CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_UNIX_X86 AND NOT CLR_CMAKE_HOST_ANDROID)) + LIST(APPEND NATIVE_LIBS createdump_static) + endif() + # additional requirements for System.IO.Compression.Native include(${CLR_SRC_NATIVE_DIR}/libs/System.IO.Compression.Native/extra_libs.cmake) append_extra_compression_libs(NATIVE_LIBS) diff --git a/src/native/corehost/hostmisc/pal.unix.cpp b/src/native/corehost/hostmisc/pal.unix.cpp index 6f1e9641ffe35..46ffaf951adfb 100644 --- a/src/native/corehost/hostmisc/pal.unix.cpp +++ b/src/native/corehost/hostmisc/pal.unix.cpp @@ -1107,13 +1107,13 @@ bool pal::are_paths_equal_with_normalized_casing(const string_t& path1, const st #endif } -#if defined(FEATURE_STATIC_HOST) +#if defined(FEATURE_STATIC_HOST) && (defined(TARGET_OSX) || defined(TARGET_LINUX)) && !defined(TARGET_X86) extern void initialize_static_createdump(); #endif void pal::initialize_createdump() { -#if defined(FEATURE_STATIC_HOST) +#if defined(FEATURE_STATIC_HOST) && (defined(TARGET_OSX) || defined(TARGET_LINUX)) && !defined(TARGET_X86) initialize_static_createdump(); #endif } From ae4649a1e0175ca7eed091cbcecdb21fa6eddb8f Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 17 Apr 2023 15:54:08 -0700 Subject: [PATCH 4/7] Fix gcc linux x64 build --- src/native/corehost/apphost/static/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/native/corehost/apphost/static/CMakeLists.txt b/src/native/corehost/apphost/static/CMakeLists.txt index c07ed8a398d2a..adc5dfeaefac5 100644 --- a/src/native/corehost/apphost/static/CMakeLists.txt +++ b/src/native/corehost/apphost/static/CMakeLists.txt @@ -132,7 +132,11 @@ if(CLR_CMAKE_TARGET_WIN32) set(RUNTIMEINFO_LIB runtimeinfo) else() - set(NATIVE_LIBS + if(CLR_CMAKE_HOST_OSX OR (CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_UNIX_X86 AND NOT CLR_CMAKE_HOST_ANDROID)) + LIST(APPEND NATIVE_LIBS createdump_static) + endif() + + LIST(APPEND NATIVE_LIBS coreclr_static System.Globalization.Native-Static @@ -149,10 +153,6 @@ else() nativeresourcestring ) - if(CLR_CMAKE_HOST_OSX OR (CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_UNIX_X86 AND NOT CLR_CMAKE_HOST_ANDROID)) - LIST(APPEND NATIVE_LIBS createdump_static) - endif() - # additional requirements for System.IO.Compression.Native include(${CLR_SRC_NATIVE_DIR}/libs/System.IO.Compression.Native/extra_libs.cmake) append_extra_compression_libs(NATIVE_LIBS) From b1f2d13a06841d917d1769eaf6f3da83de58da52 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Fri, 5 May 2023 11:01:02 -0700 Subject: [PATCH 5/7] Code review feedback changes --- src/coreclr/debug/createdump/crashinfo.cpp | 11 +++++------ src/coreclr/debug/createdump/crashinfo.h | 3 +-- src/coreclr/debug/createdump/crashinfomac.cpp | 2 +- src/coreclr/debug/createdump/crashinfounix.cpp | 12 ++++++------ src/coreclr/debug/createdump/createdump.h | 10 ++++++++-- src/coreclr/debug/createdump/createdumpmain.cpp | 5 ++--- src/coreclr/debug/createdump/createdumpunix.cpp | 2 +- 7 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 9b7120cffb5f4..a58372087794b 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -18,8 +18,7 @@ CrashInfo::CrashInfo(const CreateDumpOptions& options) : m_dacModule(nullptr), m_pClrDataEnumRegions(nullptr), m_pClrDataProcess(nullptr), - m_singleFile(options.SingleFile), - m_nativeAOT(options.NativeAOT), + m_appModel(options.AppModel), m_gatherFrames(options.CrashReport), m_crashThread(options.CrashThread), m_signal(options.Signal), @@ -287,8 +286,7 @@ bool CrashInfo::InitializeDAC() { // Don't attempt to load the DAC if createdump is statically linked into the runtime - //if (m_singleFile || m_nativeAOT) - if (m_nativeAOT) + if (m_appModel == AppModelType::NativeAOT) { return true; } @@ -309,12 +307,13 @@ CrashInfo::InitializeDAC() dacPath.append(m_coreclrPath); dacPath.append(MAKEDLLNAME_A("mscordaccore")); - // Load and initialize the DAC + // Load and initialize the DAC. We don't use the LoadLibraryA here because the PAL may not be + // initialized properly in the forked process for the statically linked single-file scenario. m_dacModule = dlopen(dacPath.c_str(), RTLD_LAZY); if (m_dacModule == nullptr) { // Don't fail for single-file apps when the DAC can't be found. Will fall back to full dump. - if (m_singleFile) + if (m_appModel == AppModelType::SingleFile) { result = true; } diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index 99249f43039f2..43d88107f4c9b 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -51,8 +51,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi void* m_dacModule; // dac module pointer when loaded ICLRDataEnumMemoryRegions* m_pClrDataEnumRegions; // dac enumerate memory interface instance IXCLRDataProcess* m_pClrDataProcess; // dac process interface instance - bool m_singleFile; // if true, single-file app. Checks for the single-file export in every module. - bool m_nativeAOT; // if true, native AOT app. + AppModelType m_appModel; // Normal, single-file or native AOT app. 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 diff --git a/src/coreclr/debug/createdump/crashinfomac.cpp b/src/coreclr/debug/createdump/crashinfomac.cpp index 8ae9ac5290c55..97477f2918b63 100644 --- a/src/coreclr/debug/createdump/crashinfomac.cpp +++ b/src/coreclr/debug/createdump/crashinfomac.cpp @@ -245,7 +245,7 @@ void CrashInfo::VisitModule(MachOModule& module) TRACE("TryLookupSymbol(" DACCESS_TABLE_SYMBOL ") FAILED\n"); } } - else if (m_singleFile) + else if (m_appModel == AppModelType::SingleFile) { uint64_t symbolOffset; if (module.TryLookupSymbol("DotNetRuntimeInfo", &symbolOffset)) diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index fd9443e9abf84..d4b003a20ae8b 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -17,7 +17,7 @@ bool CrashInfo::Initialize() { char memPath[128]; - _snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%d/mem", m_pid); + _snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%u/mem", m_pid); m_fdMem = open(memPath, O_RDONLY); if (m_fdMem == -1) @@ -42,7 +42,7 @@ CrashInfo::Initialize() { TRACE("DbgDisablePagemapUse detected - pagemap file checking is enabled\n"); char pagemapPath[128]; - _snprintf_s(pagemapPath, sizeof(pagemapPath), sizeof(pagemapPath), "/proc/%d/pagemap", m_pid); + _snprintf_s(pagemapPath, sizeof(pagemapPath), sizeof(pagemapPath), "/proc/%u/pagemap", m_pid); m_fdPagemap = open(pagemapPath, O_RDONLY); if (m_fdPagemap == -1) { @@ -95,7 +95,7 @@ bool CrashInfo::EnumerateAndSuspendThreads() { char taskPath[128]; - _snprintf_s(taskPath, sizeof(taskPath), sizeof(taskPath), "/proc/%d/task", m_pid); + _snprintf_s(taskPath, sizeof(taskPath), sizeof(taskPath), "/proc/%u/task", m_pid); DIR* taskDir = opendir(taskPath); if (taskDir == nullptr) @@ -139,7 +139,7 @@ bool CrashInfo::GetAuxvEntries() { char auxvPath[128]; - _snprintf_s(auxvPath, sizeof(auxvPath), sizeof(auxvPath), "/proc/%d/auxv", m_pid); + _snprintf_s(auxvPath, sizeof(auxvPath), sizeof(auxvPath), "/proc/%u/auxv", m_pid); int fd = open(auxvPath, O_RDONLY, 0); if (fd == -1) @@ -195,7 +195,7 @@ CrashInfo::EnumerateMemoryRegions() // Making something like: /proc/123/maps char mapPath[128]; - int chars = _snprintf_s(mapPath, sizeof(mapPath), sizeof(mapPath), "/proc/%d/maps", m_pid); + int chars = _snprintf_s(mapPath, sizeof(mapPath), sizeof(mapPath), "/proc/%u/maps", m_pid); assert(chars > 0 && (size_t)chars <= sizeof(mapPath)); FILE* mapsFile = fopen(mapPath, "r"); @@ -339,7 +339,7 @@ CrashInfo::VisitModule(uint64_t baseAddress, std::string& moduleName) } } } - else if (m_singleFile) + else if (m_appModel == AppModelType::SingleFile) { if (PopulateForSymbolLookup(baseAddress)) { diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h index 3f0e122353930..d3097e2ee4399 100644 --- a/src/coreclr/debug/createdump/createdump.h +++ b/src/coreclr/debug/createdump/createdump.h @@ -98,12 +98,18 @@ enum DumpType Full }; +enum AppModelType +{ + Normal, + SingleFile, + NativeAOT +}; + typedef struct { const char* DumpPathTemplate; enum DumpType DumpType; - bool SingleFile; - bool NativeAOT; + enum AppModelType AppModel; bool CreateDump; bool CrashReport; int Pid; diff --git a/src/coreclr/debug/createdump/createdumpmain.cpp b/src/coreclr/debug/createdump/createdumpmain.cpp index a56f42f3ef1c2..503bfecc4ce07 100644 --- a/src/coreclr/debug/createdump/createdumpmain.cpp +++ b/src/coreclr/debug/createdump/createdumpmain.cpp @@ -64,8 +64,7 @@ int createdump_main(const int argc, const char* argv[]) CreateDumpOptions options; options.DumpType = DumpType::Heap; options.DumpPathTemplate = nullptr; - options.SingleFile = false; - options.NativeAOT = false; + options.AppModel = AppModelType::Normal; options.CrashReport = false; options.CreateDump = true; options.Signal = 0; @@ -125,7 +124,7 @@ int createdump_main(const int argc, const char* argv[]) } else if (strcmp(*argv, "--singlefile") == 0) { - options.SingleFile = true; + options.AppModel = AppModelType::SingleFile; } else if (strcmp(*argv, "--code") == 0) { diff --git a/src/coreclr/debug/createdump/createdumpunix.cpp b/src/coreclr/debug/createdump/createdumpunix.cpp index cc119943a76b8..613d138d680d1 100644 --- a/src/coreclr/debug/createdump/createdumpunix.cpp +++ b/src/coreclr/debug/createdump/createdumpunix.cpp @@ -53,7 +53,7 @@ CreateDump(const CreateDumpOptions& options) goto exit; } // Write the crash report json file if enabled and createdump isn't statically linked into the runtime - if (options.CrashReport && !options.SingleFile && !options.NativeAOT) + if (options.CrashReport && options.AppModel != AppModelType::SingleFile && options.AppModel != AppModelType::NativeAOT) { CrashReportWriter crashReportWriter(*crashInfo); crashReportWriter.WriteCrashReport(dumpPath); From 4a89e2346158e4121dfe7d1c89d7e0316a46eac5 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 8 May 2023 11:36:22 -0700 Subject: [PATCH 6/7] More code review feedback chantges --- src/coreclr/debug/createdump/crashinfo.cpp | 27 +++++-------------- src/coreclr/debug/createdump/crashinfo.h | 4 +-- src/coreclr/debug/createdump/createdump.h | 4 +-- .../debug/createdump/createdumpunix.cpp | 7 +++-- 4 files changed, 14 insertions(+), 28 deletions(-) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index a58372087794b..d30909dced194 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -149,7 +149,7 @@ CrashInfo::LogMessage( // Gather all the necessary crash dump info. // bool -CrashInfo::GatherCrashInfo(DumpType* dumpType) +CrashInfo::GatherCrashInfo(DumpType dumpType) { // Get the info about the threads (registers, etc.) for (ThreadInfo* thread : m_threads) @@ -182,7 +182,7 @@ CrashInfo::GatherCrashInfo(DumpType* dumpType) } #endif // Load and initialize DAC interfaces - if (!InitializeDAC()) + if (!InitializeDAC(dumpType)) { return false; } @@ -208,13 +208,8 @@ CrashInfo::GatherCrashInfo(DumpType* dumpType) region.Trace(); } } - // If the DAC module present side-by-side (the default for single-file and native AOT apps), fallback to full dump. - if (m_pClrDataProcess == nullptr) - { - *dumpType = DumpType::Full; - } // If full memory dump, include everything regardless of permissions - if (*dumpType == DumpType::Full) + if (dumpType == DumpType::Full) { for (const MemoryRegion& region : m_moduleMappings) { @@ -233,7 +228,7 @@ CrashInfo::GatherCrashInfo(DumpType* dumpType) { // Add all the heap read/write memory regions (m_otherMappings contains the heaps). On Alpine // the heap regions are marked RWX instead of just RW. - if (*dumpType == DumpType::Heap) + if (dumpType == DumpType::Heap) { for (const MemoryRegion& region : m_otherMappings) { @@ -283,9 +278,9 @@ GetHResultString(HRESULT hr) // Enumerate all the memory regions using the DAC memory region support given a minidump type // bool -CrashInfo::InitializeDAC() +CrashInfo::InitializeDAC(DumpType dumpType) { - // Don't attempt to load the DAC if createdump is statically linked into the runtime + // Don't attempt to load the DAC if native AOT app (there is no DAC) if (m_appModel == AppModelType::NativeAOT) { return true; @@ -312,15 +307,7 @@ CrashInfo::InitializeDAC() m_dacModule = dlopen(dacPath.c_str(), RTLD_LAZY); if (m_dacModule == nullptr) { - // Don't fail for single-file apps when the DAC can't be found. Will fall back to full dump. - if (m_appModel == AppModelType::SingleFile) - { - result = true; - } - else - { - printf_error("InitializeDAC: dlopen(%s) FAILED %s\n", dacPath.c_str(), dlerror()); - } + printf_error("InitializeDAC: dlopen(%s) FAILED %s\n", dacPath.c_str(), dlerror()); goto exit; } pfnDllMain = (PFN_DLLMAIN)dlsym(m_dacModule, "DllMain"); diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index 43d88107f4c9b..0e3459b671b68 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -93,7 +93,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi bool Initialize(); void CleanupAndResumeProcess(); bool EnumerateAndSuspendThreads(); - bool GatherCrashInfo(DumpType* dumpType); + bool GatherCrashInfo(DumpType dumpType); void CombineMemoryRegions(); bool EnumerateMemoryRegionsWithDAC(DumpType dumpType); bool ReadMemory(void* address, void* buffer, size_t size); // read memory and add to dump @@ -154,7 +154,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr); bool EnumerateMemoryRegions(); #endif - bool InitializeDAC(); + bool InitializeDAC(DumpType dumpType); bool EnumerateManagedModules(); bool UnwindAllThreads(); void AddOrReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const std::string& pszName); diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h index d3097e2ee4399..3d796d5446ad1 100644 --- a/src/coreclr/debug/createdump/createdump.h +++ b/src/coreclr/debug/createdump/createdump.h @@ -90,7 +90,7 @@ typedef int T_CONTEXT; #include #include -enum DumpType +enum class DumpType { Mini, Heap, @@ -98,7 +98,7 @@ enum DumpType Full }; -enum AppModelType +enum class AppModelType { Normal, SingleFile, diff --git a/src/coreclr/debug/createdump/createdumpunix.cpp b/src/coreclr/debug/createdump/createdumpunix.cpp index 613d138d680d1..db8313d9fc469 100644 --- a/src/coreclr/debug/createdump/createdumpunix.cpp +++ b/src/coreclr/debug/createdump/createdumpunix.cpp @@ -14,7 +14,6 @@ bool CreateDump(const CreateDumpOptions& options) { ReleaseHolder crashInfo = new CrashInfo(options); - DumpType dumpType = options.DumpType; DumpWriter dumpWriter(*crashInfo); std::string dumpPath; bool result = false; @@ -43,7 +42,7 @@ CreateDump(const CreateDumpOptions& options) goto exit; } // Gather all the info about the process, threads (registers, etc.) and memory regions - if (!crashInfo->GatherCrashInfo(&dumpType)) + if (!crashInfo->GatherCrashInfo(options.DumpType)) { goto exit; } @@ -61,14 +60,14 @@ CreateDump(const CreateDumpOptions& options) if (options.CreateDump) { // Gather all the useful memory regions from the DAC - if (!crashInfo->EnumerateMemoryRegionsWithDAC(dumpType)) + if (!crashInfo->EnumerateMemoryRegionsWithDAC(options.DumpType)) { goto exit; } // Join all adjacent memory regions crashInfo->CombineMemoryRegions(); - printf_status("Writing %s to file %s\n", GetDumpTypeString(dumpType), dumpPath.c_str()); + printf_status("Writing %s to file %s\n", GetDumpTypeString(options.DumpType), dumpPath.c_str()); // Write the actual dump file if (!dumpWriter.OpenDump(dumpPath.c_str())) From 45ec22b0c894e93c5fd003045103ae95d7dcce49 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Tue, 9 May 2023 11:31:45 -0700 Subject: [PATCH 7/7] Code review feedback --- src/coreclr/debug/createdump/crashinfo.cpp | 6 ++++-- src/coreclr/debug/createdump/createdumpunix.cpp | 10 ++++++++-- src/coreclr/pal/src/thread/process.cpp | 2 -- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index d30909dced194..44294dc439bda 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -280,8 +280,10 @@ GetHResultString(HRESULT hr) bool CrashInfo::InitializeDAC(DumpType dumpType) { - // Don't attempt to load the DAC if native AOT app (there is no DAC) - if (m_appModel == AppModelType::NativeAOT) + // Don't attempt to load the DAC if the app model doesn't support it by default. The default for single-file is a + // full dump, but if the dump type requested is a mini, triage or heap and the DAC is side-by-side to the single-file + // application the core dump will be generated. + if (dumpType == DumpType::Full && (m_appModel == AppModelType::SingleFile || m_appModel == AppModelType::NativeAOT)) { return true; } diff --git a/src/coreclr/debug/createdump/createdumpunix.cpp b/src/coreclr/debug/createdump/createdumpunix.cpp index db8313d9fc469..f39ab9be4b932 100644 --- a/src/coreclr/debug/createdump/createdumpunix.cpp +++ b/src/coreclr/debug/createdump/createdumpunix.cpp @@ -24,6 +24,12 @@ CreateDump(const CreateDumpOptions& options) #endif TRACE("PAGE_SIZE %d\n", PAGE_SIZE); + if (options.CrashReport && (options.AppModel == AppModelType::SingleFile || options.AppModel != AppModelType::NativeAOT)) + { + printf_error("The app model does not support crash report generation\n"); + goto exit; + } + // Initialize the crash info if (!crashInfo->Initialize()) { @@ -51,8 +57,8 @@ CreateDump(const CreateDumpOptions& options) { goto exit; } - // Write the crash report json file if enabled and createdump isn't statically linked into the runtime - if (options.CrashReport && options.AppModel != AppModelType::SingleFile && options.AppModel != AppModelType::NativeAOT) + // Write the crash report json file if enabled + if (options.CrashReport) { CrashReportWriter crashReportWriter(*crashInfo); crashReportWriter.WriteCrashReport(dumpPath); diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 0c65fa5deeacf..a3cb53270b1fc 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -2230,9 +2230,7 @@ PROCCreateCrashDump( SEHCleanupSignals(); // Call the statically linked createdump code - fprintf(stderr, "[createdump] entering callback %d\n", gPID); g_createdumpCallback(argv.size(), argv.data()); - fprintf(stderr, "[createdump] returned from callback\n"); } else {