From ac72c989f4bd51d2f6172bf6d8bb406ffe8aa653 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Wed, 10 Apr 2019 11:40:33 +0200 Subject: [Tizen] Add a library to retrieve memory regions for a coredump This is needed to save a minicoredump of .NET applications to allows reconstruct managed stack by lldb and SOS plugin. --- packaging/coreclr.spec | 1 + src/CMakeLists.txt | 2 +- src/debug/createdump/CMakeLists.txt | 22 +++++ src/debug/createdump/crashinfo.cpp | 36 ++++++-- src/debug/createdump/crashinfo.h | 5 +- src/debug/createdump/dnetmemoryenumlib.cpp | 108 +++++++++++++++++++++++ src/debug/createdump/dnetmemoryenumlib.h | 46 ++++++++++ src/debug/createdump/threadinfo.cpp | 132 ++++++++++++++++++++++++++++- src/debug/createdump/threadinfo.h | 1 + 9 files changed, 339 insertions(+), 14 deletions(-) create mode 100644 src/debug/createdump/dnetmemoryenumlib.cpp create mode 100644 src/debug/createdump/dnetmemoryenumlib.h diff --git a/packaging/coreclr.spec b/packaging/coreclr.spec index ad54d76acf..9bd45f55f6 100755 --- a/packaging/coreclr.spec +++ b/packaging/coreclr.spec @@ -232,6 +232,7 @@ cp %{_reldir}/libmscordbi.so %{buildroot}%{_datadir}/%{netcoreappdir} cp %{_reldir}/libsos.so %{buildroot}%{_datadir}/%{netcoreappdir} cp %{_reldir}/libsosplugin.so %{buildroot}%{_datadir}/%{netcoreappdir} cp %{_reldir}/libcoreclrtraceptprovider.so %{buildroot}%{_datadir}/%{netcoreappdir} +cp %{_reldir}/libdnetmemoryenum.so %{buildroot}%{_datadir}/%{netcoreappdir} cp %{_reldir}/System.Globalization.Native.so %{buildroot}%{_datadir}/%{netcoreappdir} cp %{_reldir}/coreconsole %{buildroot}%{_datadir}/%{netcoreappdir} cp %{_reldir}/crossgen %{buildroot}%{_datadir}/%{netcoreappdir} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51ce71e598..4658228d8d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,7 +17,7 @@ endif(WIN32 AND FEATURE_EVENT_TRACE) if(CLR_CMAKE_PLATFORM_UNIX) if(CLR_CMAKE_PLATFORM_LINUX) - if(CLR_CMAKE_PLATFORM_UNIX_AMD64 OR CLR_CMAKE_PLATFORM_UNIX_ARM) + if(CLR_CMAKE_PLATFORM_UNIX_AMD64 OR CLR_CMAKE_PLATFORM_UNIX_ARM OR CLR_CMAKE_PLATFORM_UNIX_X86) add_subdirectory(debug/createdump) endif() endif(CLR_CMAKE_PLATFORM_LINUX) diff --git a/src/debug/createdump/CMakeLists.txt b/src/debug/createdump/CMakeLists.txt index 4e5b7a15f1..8c0a0440e3 100644 --- a/src/debug/createdump/CMakeLists.txt +++ b/src/debug/createdump/CMakeLists.txt @@ -30,16 +30,31 @@ set(CREATEDUMP_SOURCES dumpwriter.cpp ) +set(DNETMEMORYENUM_SOURCES + dnetmemoryenumlib.cpp + crashinfo.cpp + threadinfo.cpp + datatarget.cpp +) + _add_library(createdump_lib ${CREATEDUMP_SOURCES} ) +_add_library(dnetmemoryenum SHARED + ${DNETMEMORYENUM_SOURCES} + ${PAL_REDEFINES_FILE} +) + +set_property(TARGET dnetmemoryenum PROPERTY POSITION_INDEPENDENT_CODE ON) + _add_executable(createdump main.cpp ${PAL_REDEFINES_FILE} ) add_dependencies(createdump pal_redefines_file) +add_dependencies(dnetmemoryenum pal_redefines_file) target_link_libraries(createdump createdump_lib @@ -47,6 +62,13 @@ target_link_libraries(createdump mscordaccore ) +target_link_libraries(dnetmemoryenum + # share the PAL in the dac module + mscordaccore +) + add_dependencies(createdump mscordaccore) +add_dependencies(dnetmemoryenum mscordaccore) install_clr(createdump) +install_clr(dnetmemoryenum) diff --git a/src/debug/createdump/crashinfo.cpp b/src/debug/createdump/crashinfo.cpp index 4db2b00eeb..a720e5ca36 100644 --- a/src/debug/createdump/crashinfo.cpp +++ b/src/debug/createdump/crashinfo.cpp @@ -98,7 +98,7 @@ CrashInfo::EnumMemoryRegion( // gather any info about the process. // bool -CrashInfo::EnumerateAndSuspendThreads() +CrashInfo::EnumerateAndSuspendThreads(bool suspend) { char taskPath[128]; snprintf(taskPath, sizeof(taskPath), "/proc/%d/task", m_pid); @@ -117,7 +117,7 @@ CrashInfo::EnumerateAndSuspendThreads() if (tid != 0) { // Don't suspend the threads if running under sos - if (!m_sos) + if (!m_sos && suspend) { // 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) @@ -142,23 +142,43 @@ CrashInfo::EnumerateAndSuspendThreads() return true; } +// +// Set registers for all threads +// +bool +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; + } + } + } + + return true; +} + // // Gather all the necessary crash dump info. // bool -CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) +CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType, bool initialize_threads) { // Get the process info if (!GetStatus(m_pid, &m_ppid, &m_tgid, &m_name)) { return false; } - // Get the info about the threads (registers, etc.) - for (ThreadInfo* thread : m_threads) - { - if (!thread->Initialize(m_sos ? m_dataTarget : nullptr)) + if (initialize_threads) { + // Get the info about the threads (registers, etc.) + for (ThreadInfo* thread : m_threads) { - return false; + if (!thread->Initialize(m_sos ? m_dataTarget : nullptr)) + { + return false; + } } } // Get the auxv data diff --git a/src/debug/createdump/crashinfo.h b/src/debug/createdump/crashinfo.h index 096f933af0..f7ebae9f26 100644 --- a/src/debug/createdump/crashinfo.h +++ b/src/debug/createdump/crashinfo.h @@ -46,8 +46,9 @@ private: public: CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos); virtual ~CrashInfo(); - bool EnumerateAndSuspendThreads(); - bool GatherCrashInfo(MINIDUMP_TYPE minidumpType); + bool EnumerateAndSuspendThreads(bool suspend = true); + bool SetThreadsRegisters(const std::vector &statuses); + bool GatherCrashInfo(MINIDUMP_TYPE minidumpType, bool initialize_threads = true); void ResumeThreads(); bool ReadMemory(void* address, void* buffer, size_t size); uint64_t GetBaseAddress(uint64_t ip); diff --git a/src/debug/createdump/dnetmemoryenumlib.cpp b/src/debug/createdump/dnetmemoryenumlib.cpp new file mode 100644 index 0000000000..07e48e6787 --- /dev/null +++ b/src/debug/createdump/dnetmemoryenumlib.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "createdump.h" +#include "dnetmemoryenumlib.h" + +bool g_diagnostics = false; + +std::vector sm_regions; + +static std::vector +get_regions(CrashInfo *crashInfo) +{ + std::vector reg_vec; + for (const MemoryRegion& memoryRegion : crashInfo->MemoryRegions()) { + if (memoryRegion.IsBackedByMemory()) { + reg_vec.push_back({memoryRegion.StartAddress(), memoryRegion.Size()}); + } + } + return reg_vec; +} + + +extern "C" int +DotNetMemoryEnumInit() +{ + int exitCode = PAL_InitializeDLL(); + return exitCode; +} + +extern "C" void +DotNetMemoryEnumFinish() +{ + PAL_TerminateEx(0); +} + +extern "C" int +DotNetMemoryEnumRegions(pid_t pid, elf_prstatus **statuses, int statuses_count, + DUMP_TYPE minidump_type, SimpleMemoryRegion **regions) +{ + g_diagnostics = true; + + std::vector stats; + for (int i = 0; i < statuses_count; i++) { + stats.push_back(statuses[i]); + } + + 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) + { + ReleaseHolder dataTarget = new DumpDataTarget(pid); + ReleaseHolder crashInfo = new CrashInfo(pid, dataTarget, true); + + if (dataTarget->Initialize(crashInfo)) + { + if (!crashInfo->EnumerateAndSuspendThreads(false)) + { + return REGERR_ENUMERATION_ERROR; + } + crashInfo->SetThreadsRegisters(stats); + crashInfo->GatherCrashInfo(minidumpType, false); + sm_regions = get_regions(crashInfo); + *regions = sm_regions.data(); + exitCode = sm_regions.size(); + } + else + { + exitCode = REGERR_INITIALIZATION_ERROR; + } + } + else + { + exitCode = REGERR_WRONG_PID; + } + return exitCode; +} diff --git a/src/debug/createdump/dnetmemoryenumlib.h b/src/debug/createdump/dnetmemoryenumlib.h new file mode 100644 index 0000000000..76e65eea4b --- /dev/null +++ b/src/debug/createdump/dnetmemoryenumlib.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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 +}; + +extern "C" int DotNetMemoryEnumRegions(pid_t pid, elf_prstatus **statuses, int statuses_count, + DUMP_TYPE minidump_type, SimpleMemoryRegion **regions); +extern "C" int DotNetMemoryEnumInit(); +extern "C" void DotNetMemoryEnumFinish(); + +#endif diff --git a/src/debug/createdump/threadinfo.cpp b/src/debug/createdump/threadinfo.cpp index 07d8193fd3..4cc838d04e 100644 --- a/src/debug/createdump/threadinfo.cpp +++ b/src/debug/createdump/threadinfo.cpp @@ -52,8 +52,10 @@ ThreadInfo::Initialize(ICLRDataTarget* pDataTarget) #if defined(__arm__) TRACE("Thread %04x PC %08lx SP %08lx\n", m_tid, (unsigned long)m_gpRegisters.ARM_pc, (unsigned long)m_gpRegisters.ARM_sp); -#else +#elif defined(__x86_64__) TRACE("Thread %04x RIP %016llx RSP %016llx\n", m_tid, (unsigned long long)m_gpRegisters.rip, (unsigned long long)m_gpRegisters.rsp); +#elif defined(__i386__) + TRACE("Thread %04x EIP %016lx ESP %016lx\n", m_tid, (unsigned long)m_gpRegisters.eip, (unsigned long)m_gpRegisters.esp); #endif return true; } @@ -206,6 +208,92 @@ ThreadInfo::GetRegistersWithPTrace() #endif return true; } +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]; +#endif +} bool ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* pDataTarget) @@ -292,7 +380,20 @@ ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* pDataTarget) assert(sizeof(context.D) == sizeof(m_vfpRegisters.fpregs)); memcpy(m_vfpRegisters.fpregs, context.D, sizeof(context.D)); #endif -#else +#elif defined(__i386__) + m_gpRegisters.ebp = context.Ebp; + m_gpRegisters.eip = context.Eip; + m_gpRegisters.eflags = context.EFlags; + m_gpRegisters.esp = context.Esp; + m_gpRegisters.edi = context.Edi; + + m_gpRegisters.esi = context.Esi; + m_gpRegisters.ebx = context.Ebx; + m_gpRegisters.edx = context.Edx; + m_gpRegisters.ecx = context.Ecx; + m_gpRegisters.eax = context.Eax; + m_gpRegisters.orig_eax = context.Eax; +#else #error Platform not supported #endif return true; @@ -306,8 +407,10 @@ ThreadInfo::GetThreadStack(CrashInfo& crashInfo) #if defined(__arm__) startAddress = m_gpRegisters.ARM_sp & PAGE_MASK; -#else +#elif defined(__x86_64__) startAddress = m_gpRegisters.rsp & PAGE_MASK; +#elif defined(__i386__) + startAddress = m_gpRegisters.esp & PAGE_MASK; #endif size = 4 * PAGE_SIZE; @@ -421,6 +524,29 @@ ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const memcpy(context->D, m_vfpRegisters.fpregs, sizeof(context->D)); #endif } +#elif defined(__i386__) + if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL) + { + context->Ebp = m_gpRegisters.ebp; + context->Eip = m_gpRegisters.eip; + context->EFlags = m_gpRegisters.eflags; + context->Esp = m_gpRegisters.esp; + context->SegCs = m_gpRegisters.xcs; + context->SegSs = m_gpRegisters.xss; + context->SegGs_PAL_Undefined = m_gpRegisters.xgs; + context->SegFs_PAL_Undefined = m_gpRegisters.xfs; + context->SegEs_PAL_Undefined = m_gpRegisters.xes; + context->SegDs_PAL_Undefined = m_gpRegisters.xds; + } + if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) + { + context->Edi = m_gpRegisters.edi; + context->Esi = m_gpRegisters.esi; + context->Ebx = m_gpRegisters.ebx; + context->Edx = m_gpRegisters.edx; + context->Ecx = m_gpRegisters.ecx; + context->Eax = m_gpRegisters.eax; + } #else #error Platform not supported #endif diff --git a/src/debug/createdump/threadinfo.h b/src/debug/createdump/threadinfo.h index 5055b22674..09684e10ec 100644 --- a/src/debug/createdump/threadinfo.h +++ b/src/debug/createdump/threadinfo.h @@ -40,6 +40,7 @@ public: bool UnwindThread(CrashInfo& crashInfo, IXCLRDataProcess* pClrDataProcess); void GetThreadStack(CrashInfo& crashInfo); 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; } -- cgit v1.2.3