[Tizen] Add a library to retrieve memory regions for a coredump
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Wed, 10 Apr 2019 09:40:33 +0000 (11:40 +0200)
committerGleb Balykov <g.balykov@samsung.com>
Wed, 25 Mar 2020 12:29:41 +0000 (15:29 +0300)
This is needed to save a minicoredump of .NET applications to allows
reconstruct managed stack by lldb and SOS plugin.

packaging/coreclr.spec
src/CMakeLists.txt
src/debug/createdump/CMakeLists.txt
src/debug/createdump/crashinfo.cpp
src/debug/createdump/crashinfo.h
src/debug/createdump/dnetmemoryenumlib.cpp [new file with mode: 0644]
src/debug/createdump/dnetmemoryenumlib.h [new file with mode: 0644]
src/debug/createdump/threadinfo.cpp
src/debug/createdump/threadinfo.h

index 5f741b5..04554c7 100755 (executable)
@@ -242,6 +242,7 @@ cp %{_reldir}/libdbgshim.so %{buildroot}%{_datadir}/%{netcoreappdir}
 cp %{_reldir}/libmscordaccore.so  %{buildroot}%{_datadir}/%{netcoreappdir}
 cp %{_reldir}/libmscordbi.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}
index 94214cf..91153a3 100644 (file)
@@ -16,9 +16,9 @@ endif(WIN32 AND FEATURE_EVENT_TRACE)
 
 
 if(CLR_CMAKE_PLATFORM_UNIX)
-  if(CLR_CMAKE_PLATFORM_LINUX AND NOT CLR_CMAKE_PLATFORM_UNIX_X86)
+  if(CLR_CMAKE_PLATFORM_LINUX)
     add_subdirectory(debug/createdump)
-  endif(CLR_CMAKE_PLATFORM_LINUX AND NOT CLR_CMAKE_PLATFORM_UNIX_X86)
+  endif(CLR_CMAKE_PLATFORM_LINUX)
 
   # Include the dummy c++ include files
   include_directories("pal/inc/rt/cpp")
index b44016c..838846a 100644 (file)
@@ -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)
index 7b5c20b..501a85b 100644 (file)
@@ -97,8 +97,8 @@ CrashInfo::EnumMemoryRegion(
 // Suspends all the threads and creating a list of them. Should be the first before
 // gather any info about the process.
 //
-bool
-CrashInfo::EnumerateAndSuspendThreads()
+bool 
+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)
@@ -143,22 +143,42 @@ CrashInfo::EnumerateAndSuspendThreads()
 }
 
 //
+// Set registers for all threads
+//
+bool
+CrashInfo::SetThreadsRegisters(const std::vector<elf_prstatus*> &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
index 096f933..f7ebae9 100644 (file)
@@ -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<elf_prstatus*> &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 (file)
index 0000000..07e48e6
--- /dev/null
@@ -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 <stdarg.h>
+#include <iostream>
+#include <vector>
+#include "createdump.h"
+#include "dnetmemoryenumlib.h"
+
+bool g_diagnostics = false;
+
+std::vector<SimpleMemoryRegion> sm_regions;
+
+static std::vector<SimpleMemoryRegion>
+get_regions(CrashInfo *crashInfo)
+{
+    std::vector<SimpleMemoryRegion> 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<elf_prstatus*> 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<DumpDataTarget> dataTarget = new DumpDataTarget(pid);
+        ReleaseHolder<CrashInfo> 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 (file)
index 0000000..76e65ee
--- /dev/null
@@ -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 <stdint.h>
+#include <stdlib.h>
+
+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
index 02233c1..8af7647 100644 (file)
@@ -65,6 +65,8 @@ ThreadInfo::Initialize(ICLRDataTarget* pDataTarget)
     TRACE("Thread %04x PC %08lx SP %08lx\n", m_tid, (unsigned long)m_gpRegisters.ARM_pc, (unsigned long)m_gpRegisters.ARM_sp);
 #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);
 #else
 #error "Unsupported architecture"
 #endif
@@ -244,6 +246,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)
@@ -344,7 +432,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;
@@ -360,8 +461,10 @@ ThreadInfo::GetThreadStack(CrashInfo& crashInfo)
     startAddress = MCREG_Sp(m_gpRegisters) & PAGE_MASK;
 #elif 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;
 
@@ -495,6 +598,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
index 726e807..1fd696e 100644 (file)
@@ -43,6 +43,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; }