From 4d043d61ecacae0b9922ecc10a73749e7c8e5987 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Mon, 21 Jun 2021 18:12:54 +0200 Subject: [PATCH] [Tizen] Add method to write coredump of .NET process This method allows to create minimal coredump (as createdump does) of a process that is in dumping state after crush. Original commit: https://github.sec.samsung.net/dotnet/coreclr/commit/66a3b803910b389de38703ce349695a247146f5b --- packaging/coreclr.spec | 2 + src/coreclr/debug/createdump/CMakeLists.txt | 32 +++++ src/coreclr/debug/createdump/crashinfo.cpp | 29 +++- src/coreclr/debug/createdump/crashinfo.h | 5 +- src/coreclr/debug/createdump/crashinfounix.cpp | 25 ++-- src/coreclr/debug/createdump/dnetmemoryenumlib.cpp | 153 +++++++++++++++++++++ src/coreclr/debug/createdump/dnetmemoryenumlib.h | 61 ++++++++ src/coreclr/debug/createdump/threadinfo.cpp | 93 +++++++++++++ src/coreclr/debug/createdump/threadinfo.h | 1 + 9 files changed, 383 insertions(+), 18 deletions(-) create mode 100644 src/coreclr/debug/createdump/dnetmemoryenumlib.cpp create mode 100644 src/coreclr/debug/createdump/dnetmemoryenumlib.h diff --git a/packaging/coreclr.spec b/packaging/coreclr.spec index 9ec55bb..c7e8019 100755 --- a/packaging/coreclr.spec +++ b/packaging/coreclr.spec @@ -445,6 +445,7 @@ cp %{_reldir_clr}/IL/System.Private.CoreLib.pdb %{buildroot}%{_datadir}/%{ne %if 0%{skipmanagedtools} %else cp %{_reldir_clr}/createdump %{buildroot}%{_datadir}/%{netcoreappdir} +cp %{_reldir_clr}/libdnetmemoryenum.so %{buildroot}%{_datadir}/%{netcoreappdir} # Managed crossgen2 mkdir -p %{buildroot}%{_datadir}/%{netcoreappdir}/crossgen2 @@ -598,6 +599,7 @@ cp ./nuget/*.nupkg %{buildroot}/nuget %if 0%{skipmanagedtools} %else %{_datadir}/%{netcoreappdir}/createdump +%{_datadir}/%{netcoreappdir}/libdnetmemoryenum.so %dir %{_datadir}/%{netcoreappdir}/crossgen2 %{_datadir}/%{netcoreappdir}/crossgen2/crossgen2.dll diff --git a/src/coreclr/debug/createdump/CMakeLists.txt b/src/coreclr/debug/createdump/CMakeLists.txt index 462e2fa..12f7822 100644 --- a/src/coreclr/debug/createdump/CMakeLists.txt +++ b/src/coreclr/debug/createdump/CMakeLists.txt @@ -71,6 +71,20 @@ if(CLR_CMAKE_HOST_OSX) ${CREATEDUMP_SOURCES} ) else() + set(DNETMEMORYENUM_SOURCES + dumpname.cpp + crashinfounix.cpp + threadinfounix.cpp + createdumpunix.cpp + crashinfo.cpp + threadinfo.cpp + datatarget.cpp + dumpwriter.cpp + dumpwriterelf.cpp + crashreportwriter.cpp + dnetmemoryenumlib.cpp + ) + add_executable_clr(createdump crashinfounix.cpp threadinfounix.cpp @@ -81,6 +95,24 @@ else() set_property(TARGET createdump PROPERTY COMPILE_FLAGS "-fPIE") add_dependencies(createdump pal_redefines_file) + + add_library_clr(dnetmemoryenum SHARED + ${DNETMEMORYENUM_SOURCES} + ${PAL_REDEFINES_FILE} + ) + + set_property(TARGET dnetmemoryenum PROPERTY COMPILE_FLAGS "-fPIC") + add_dependencies(dnetmemoryenum pal_redefines_file) + + target_link_libraries(dnetmemoryenum + corguids + dbgutil + # share the PAL in the dac module + mscordaccore + ) + add_dependencies(dnetmemoryenum mscordaccore) + install_clr(TARGETS dnetmemoryenum DESTINATIONS . sharedFramework COMPONENT runtime) + endif(CLR_CMAKE_HOST_OSX) target_link_libraries(createdump diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index d939592..dcd5817 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -120,17 +120,36 @@ CrashInfo::EnumMemoryRegion( } // +// Set registers for all threads +// +void +CrashInfo::SetThreadsRegisters(const std::vector &statuses) +{ + for (ThreadInfo* thread : m_threads) { + for (elf_prstatus* status : statuses) { + if (thread->Tid() == status->pr_pid) { + thread->SetRegisters(status); + break; + } + } + } +} + +// // Gather all the necessary crash dump info. // bool -CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) +CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType, bool initialize_threads) { - // Get the info about the threads (registers, etc.) - for (ThreadInfo* thread : m_threads) + if (initialize_threads) { - if (!thread->Initialize()) + // Get the info about the threads (registers, etc.) + for (ThreadInfo* thread : m_threads) { - return false; + if (!thread->Initialize()) + { + return false; + } } } #ifdef __APPLE__ diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index e100b7f..e3bfe2a 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -84,8 +84,9 @@ public: bool Initialize(); void CleanupAndResumeProcess(); - bool EnumerateAndSuspendThreads(); - bool GatherCrashInfo(MINIDUMP_TYPE minidumpType); + bool EnumerateAndSuspendThreads(bool suspend = true); + void SetThreadsRegisters(const std::vector &statuses); + bool GatherCrashInfo(MINIDUMP_TYPE minidumpType, bool initialize_threads = true); bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType); 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 diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index 03712c3..0d075b9 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -56,7 +56,7 @@ CrashInfo::CleanupAndResumeProcess() // Suspends all the threads and creating a list of them. Should be the before gathering any info about the process. // bool -CrashInfo::EnumerateAndSuspendThreads() +CrashInfo::EnumerateAndSuspendThreads(bool suspend) { char taskPath[128]; snprintf(taskPath, sizeof(taskPath), "/proc/%d/task", m_pid); @@ -74,17 +74,20 @@ CrashInfo::EnumerateAndSuspendThreads() pid_t tid = static_cast(strtol(entry->d_name, nullptr, 10)); if (tid != 0) { - // Reference: http://stackoverflow.com/questions/18577956/how-to-use-ptrace-to-get-a-consistent-view-of-multiple-threads - if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) != -1) + if (suspend) { - int waitStatus; - waitpid(tid, &waitStatus, __WALL); - } - else - { - printf_error("ptrace(ATTACH, %d) FAILED %s\n", tid, strerror(errno)); - closedir(taskDir); - return false; + // Reference: http://stackoverflow.com/questions/18577956/how-to-use-ptrace-to-get-a-consistent-view-of-multiple-threads + if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) != -1) + { + int waitStatus; + waitpid(tid, &waitStatus, __WALL); + } + else + { + printf_error("ptrace(ATTACH, %d) FAILED %s\n", tid, strerror(errno)); + closedir(taskDir); + return false; + } } // Add to the list of threads ThreadInfo* thread = new ThreadInfo(*this, tid); diff --git a/src/coreclr/debug/createdump/dnetmemoryenumlib.cpp b/src/coreclr/debug/createdump/dnetmemoryenumlib.cpp new file mode 100644 index 0000000..2295d0e --- /dev/null +++ b/src/coreclr/debug/createdump/dnetmemoryenumlib.cpp @@ -0,0 +1,153 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Samsung Electronics Co., Ltd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "createdump.h" +#include "dnetmemoryenumlib.h" + +const int DEFAULT_SIGNAL = 6; + +bool g_diagnostics = false; +bool g_diagnosticsVerbose = false; + +std::vector sm_regions; + +int +prepare_crash_info(pid_t pid, elf_prstatus **statuses, int statuses_count, + DUMP_TYPE minidump_type, ReleaseHolder &crash_info) +{ + g_diagnostics = true; + + std::vector stats(statuses, statuses + statuses_count); + + MINIDUMP_TYPE minidumpType; + + switch (minidump_type) { + case DT_NORMAL: + default: + minidumpType = MiniDumpNormal; + break; + case DT_WITH_PRIV_AND_SHARED_MEM: + minidumpType = MiniDumpWithPrivateReadWriteMemory; + break; + case DT_FULL: + minidumpType = MiniDumpWithFullMemory; + break; + } + + int exitCode = REGERR_OK; + + if (pid != 0) + { + crash_info = new CrashInfo(pid, true, pid, DEFAULT_SIGNAL); + + if (!crash_info->Initialize()) + { + return REGERR_CRASHINFO_INITIALIZE_ERROR; + } + + if (!crash_info->EnumerateAndSuspendThreads(false)) + { + return REGERR_ENUMERATION_ERROR; + } + crash_info->SetThreadsRegisters(stats); + if (!crash_info->GatherCrashInfo(minidumpType, false)) + { + return REGERR_GATHER_CRASHINFO_ERROR; + } + if (!crash_info->EnumerateMemoryRegionsWithDAC(minidumpType)) + { + return REGERR_ENUMERATIONDAC_ERROR; + } + } + else + { + exitCode = REGERR_WRONG_PID; + } + return exitCode; +} + +extern "C" DLLEXPORT int +DotNetMemoryEnumInit() +{ + int exitCode = PAL_InitializeDLL(); + return exitCode; +} + +extern "C" DLLEXPORT void +DotNetMemoryEnumFinish() +{ + PAL_TerminateEx(0); +} + +extern "C" DLLEXPORT int +DotNetMemoryWriteDump(pid_t pid, elf_prstatus **statuses, int statuses_count, + DUMP_TYPE minidump_type, const char *dump_path) +{ + ReleaseHolder crashInfo; + int exitCode = prepare_crash_info(pid, statuses, statuses_count, minidump_type, crashInfo); + + if (exitCode != REGERR_OK) + return exitCode; + + DumpWriter dumpWriter(*crashInfo); + + if (!dumpWriter.OpenDump(dump_path)) + exitCode = REGERR_OPENDUMP_ERROR; + + if (!dumpWriter.WriteDump()) + exitCode = REGERR_WRITEDUMP_ERROR; + + return exitCode; +} + +void +trace_printf(const char* format, ...) +{ + if (g_diagnostics) + { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + } +} + +void +trace_verbose_printf(const char* format, ...) +{ + if (g_diagnosticsVerbose) + { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + } +} diff --git a/src/coreclr/debug/createdump/dnetmemoryenumlib.h b/src/coreclr/debug/createdump/dnetmemoryenumlib.h new file mode 100644 index 0000000..244f1bc --- /dev/null +++ b/src/coreclr/debug/createdump/dnetmemoryenumlib.h @@ -0,0 +1,61 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Samsung Electronics Co., Ltd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __DNETMEMORYENUMLIB_H__ +#define __DNETMEMORYENUMLIB_H__ + +#include +#include + +struct SimpleMemoryRegion { + uintptr_t m_startAddress; + size_t m_size; +}; + +enum DUMP_TYPE { + DT_NORMAL = 0, + DT_WITH_PRIV_AND_SHARED_MEM, + DT_FULL +}; + +enum REG_ERR { + REGERR_OK = 0, + REGERR_WRONG_PID = -1, + REGERR_INITIALIZATION_ERROR = -2, + REGERR_ENUMERATION_ERROR = -3, + REGERR_OPENDUMP_ERROR = -4, + REGERR_WRITEDUMP_ERROR = -5, + REGERR_ENUMERATIONDAC_ERROR = -6, + REGERR_CRASHINFO_INITIALIZE_ERROR = -7, + REGERR_GATHER_CRASHINFO_ERROR = -8, +}; + +extern "C" DLLEXPORT int DotNetMemoryWriteDump(pid_t pid, elf_prstatus **statuses, int statuses_count, + DUMP_TYPE minidump_type, const char *dump_path); +extern "C" DLLEXPORT int DotNetMemoryEnumInit(); +extern "C" DLLEXPORT void DotNetMemoryEnumFinish(); + +#endif diff --git a/src/coreclr/debug/createdump/threadinfo.cpp b/src/coreclr/debug/createdump/threadinfo.cpp index e64eafc..ef2597c 100644 --- a/src/coreclr/debug/createdump/threadinfo.cpp +++ b/src/coreclr/debug/createdump/threadinfo.cpp @@ -387,3 +387,96 @@ ThreadInfo::GetThreadStack() TRACE("Thread %04x null stack pointer\n", m_tid); } } + +void ThreadInfo::SetRegisters(elf_prstatus *prstatus) +{ +#if defined(__x86_64__) + struct user_regs_struct *u_reg = (struct user_regs_struct *)&prstatus->pr_reg; + m_gpRegisters.rip = u_reg->rip; + m_gpRegisters.rbp = u_reg->rbp; + m_gpRegisters.rsp = u_reg->rsp; + + m_gpRegisters.rax = u_reg->rax; + m_gpRegisters.rbx = u_reg->rbx; + m_gpRegisters.rcx = u_reg->rcx; + m_gpRegisters.rdx = u_reg->rdx; + m_gpRegisters.rsi = u_reg->rsi; + m_gpRegisters.rdi = u_reg->rdi; + + m_gpRegisters.cs = u_reg->cs; + m_gpRegisters.gs = u_reg->gs; + m_gpRegisters.es = u_reg->es; + m_gpRegisters.fs = u_reg->fs; + m_gpRegisters.ds = u_reg->ds; + m_gpRegisters.ss = u_reg->ss; + m_gpRegisters.fs_base = u_reg->fs_base; + m_gpRegisters.gs_base = u_reg->gs_base; + + m_gpRegisters.orig_rax = u_reg->orig_rax; + + m_gpRegisters.r8 = u_reg->r8; + m_gpRegisters.r9 = u_reg->r9; + m_gpRegisters.r10 = u_reg->r10; + m_gpRegisters.r11 = u_reg->r11; + m_gpRegisters.r12 = u_reg->r12; + m_gpRegisters.r13 = u_reg->r13; + m_gpRegisters.r14 = u_reg->r14; + m_gpRegisters.r15 = u_reg->r15; + + m_gpRegisters.eflags = u_reg->eflags; +#elif defined(__i386__) + struct user_regs_struct *u_reg = (struct user_regs_struct *)&prstatus->pr_reg; + m_gpRegisters.ebx = u_reg->ebx; + m_gpRegisters.ecx = u_reg->ecx; + m_gpRegisters.edx = u_reg->edx; + m_gpRegisters.esi = u_reg->esi; + m_gpRegisters.edi = u_reg->edi; + m_gpRegisters.ebp = u_reg->ebp; + m_gpRegisters.eax = u_reg->eax; + m_gpRegisters.xds = u_reg->xds; + m_gpRegisters.xes = u_reg->xes; + m_gpRegisters.xfs = u_reg->xfs; + m_gpRegisters.xgs = u_reg->xgs; + m_gpRegisters.orig_eax = u_reg->orig_eax; + m_gpRegisters.eip = u_reg->eip; + m_gpRegisters.xcs = u_reg->xcs; + m_gpRegisters.eflags = u_reg->eflags; + m_gpRegisters.esp = u_reg->esp; + m_gpRegisters.xss = u_reg->xss; +#elif defined(__arm__) + +#define REGS_REGULAR_NUM 13 +#define REG_FP 11 +#define REG_IP 12 +#define REG_SP 13 +#define REG_LR 14 +#define REG_PC 15 +#define REG_SPSR 16 + struct user_regs *u_reg = (struct user_regs *)&prstatus->pr_reg; + m_gpRegisters.ARM_sp = u_reg->uregs[REG_SP]; + m_gpRegisters.ARM_lr = u_reg->uregs[REG_LR]; + m_gpRegisters.ARM_pc = u_reg->uregs[REG_PC]; + m_gpRegisters.ARM_cpsr = u_reg->uregs[REG_SPSR]; + + m_gpRegisters.ARM_r0 = u_reg->uregs[0]; + m_gpRegisters.ARM_ORIG_r0 = u_reg->uregs[0]; + m_gpRegisters.ARM_r1 = u_reg->uregs[1]; + m_gpRegisters.ARM_r2 = u_reg->uregs[2]; + m_gpRegisters.ARM_r3 = u_reg->uregs[3]; + m_gpRegisters.ARM_r4 = u_reg->uregs[4]; + m_gpRegisters.ARM_r5 = u_reg->uregs[5]; + m_gpRegisters.ARM_r6 = u_reg->uregs[6]; + m_gpRegisters.ARM_r7 = u_reg->uregs[7]; + m_gpRegisters.ARM_r8 = u_reg->uregs[8]; + m_gpRegisters.ARM_r9 = u_reg->uregs[9]; + m_gpRegisters.ARM_r10 = u_reg->uregs[10]; + m_gpRegisters.ARM_fp = u_reg->uregs[REG_FP]; + m_gpRegisters.ARM_ip = u_reg->uregs[REG_IP]; +#elif defined(__aarch64__) + struct user_regs_struct *u_reg = (struct user_regs_struct *)&prstatus->pr_reg; + memcpy(m_gpRegisters.regs, u_reg, sizeof(m_gpRegisters.regs)); + m_gpRegisters.sp = u_reg->sp; + m_gpRegisters.pc = u_reg->pc; + m_gpRegisters.pstate = u_reg->pstate; +#endif +} diff --git a/src/coreclr/debug/createdump/threadinfo.h b/src/coreclr/debug/createdump/threadinfo.h index 4142b55..0cd8e05 100644 --- a/src/coreclr/debug/createdump/threadinfo.h +++ b/src/coreclr/debug/createdump/threadinfo.h @@ -86,6 +86,7 @@ public: bool UnwindThread(IXCLRDataProcess* pClrDataProcess, ISOSDacInterface* pSos); void GetThreadStack(); void GetThreadContext(uint32_t flags, CONTEXT* context) const; + void SetRegisters(elf_prstatus *prstatus); inline pid_t Tid() const { return m_tid; } inline pid_t Ppid() const { return m_ppid; } -- 2.7.4