Add unwind info to core dumps. (#13547)
authorMike McLaughlin <mikem@microsoft.com>
Thu, 24 Aug 2017 04:13:36 +0000 (21:13 -0700)
committerGitHub <noreply@github.com>
Thu, 24 Aug 2017 04:13:36 +0000 (21:13 -0700)
The createdump utility now enumerates all the native stack frames (with
some help from the managed stack walker) for all the threads adding all
the ELF unwind info needed.

On a different machine and without any of the native modules loaded when
the crashdump was generated all the thread stacks can still be unwound
with lldb/gdb.

Change the PAL_VirtualUnwindOutOfProc read memory adapter in DAC
to add the memory to instances manager.

Some misc. cleanup.

src/debug/createdump/crashinfo.cpp
src/debug/createdump/crashinfo.h
src/debug/createdump/datatarget.cpp
src/debug/createdump/datatarget.h
src/debug/createdump/memoryregion.h
src/debug/createdump/threadinfo.cpp
src/debug/createdump/threadinfo.h
src/debug/daccess/dacfn.cpp
src/debug/daccess/dacimpl.h

index 803e2bd..b8f9294 100644 (file)
@@ -4,6 +4,9 @@
 
 #include "createdump.h"
 
+// This is for the PAL_VirtualUnwindOutOfProc read memory adapter.
+CrashInfo* g_crashInfo;
+
 CrashInfo::CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos) :
     m_ref(1),
     m_pid(pid),
@@ -12,6 +15,7 @@ CrashInfo::CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos) :
     m_sos(sos),
     m_dataTarget(dataTarget)
 {
+    g_crashInfo = this;
     dataTarget->AddRef();
     m_auxvValues.fill(0);
 }
@@ -57,7 +61,7 @@ CrashInfo::QueryInterface(
     }
     else
     {
-        *Interface = NULL;
+        *Interface = nullptr;
         return E_NOINTERFACE;
     }
 }
@@ -172,6 +176,12 @@ CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType)
     {
         return false;
     }
+    for (const MemoryRegion& region : m_moduleAddresses)
+    {
+        region.Trace();
+    }
+
     // If full memory dump, include everything regardless of permissions
     if (minidumpType & MiniDumpWithFullMemory)
     {
@@ -205,15 +215,8 @@ CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType)
         // Add the thread's stack and some code memory to core
         for (ThreadInfo* thread : m_threads)
         {
-            uint64_t start;
-            size_t size;
-
-            // Add the thread's stack and some of the code 
-            thread->GetThreadStack(*this, &start, &size);
-            InsertMemoryRegion(start, size);
-
-            thread->GetThreadCode(&start, &size);
-            InsertMemoryRegion(start, size);
+            // Add the thread's stack
+            thread->GetThreadStack(*this);
         }
         // All the regions added so far has been backed by memory. Now add the rest of 
         // mappings so the debuggers like lldb see that an address is code (PF_X) even
@@ -302,7 +305,7 @@ CrashInfo::EnumerateModuleMappings()
     // 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so
     // 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so
     // 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870  /usr/lib64/libc-2.15.so
-    char* line = NULL;
+    char* line = nullptr;
     size_t lineLen = 0;
     int count = 0;
     ssize_t read;
@@ -313,7 +316,7 @@ CrashInfo::EnumerateModuleMappings()
     assert(chars > 0 && chars <= sizeof(mapPath));
 
     FILE* mapsFile = fopen(mapPath, "r");
-    if (mapsFile == NULL)
+    if (mapsFile == nullptr)
     {
         fprintf(stderr, "fopen(%s) FAILED %s\n", mapPath, strerror(errno));
         return false;
@@ -408,7 +411,8 @@ CrashInfo::GetDSOInfo()
     if (phnum <= 0 || phdrAddr == nullptr) {
         return false;
     }
-    TRACE("DSO: phdr %p phnum %d\n", phdrAddr, phnum);
+    uint64_t baseAddress = (uint64_t)phdrAddr - sizeof(Ehdr);
+    TRACE("DSO: base %" PRIA PRIx64 " phdr %p phnum %d\n", baseAddress, phdrAddr, phnum);
 
     // Search for the program PT_DYNAMIC header 
     ElfW(Dyn)* dynamicAddr = nullptr;
@@ -422,16 +426,23 @@ CrashInfo::GetDSOInfo()
         TRACE("DSO: phdr %p type %d (%x) vaddr %" PRIxA " memsz %" PRIxA " offset %" PRIxA "\n",
             phdrAddr, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_offset);
 
-        if (ph.p_type == PT_DYNAMIC) 
+        switch (ph.p_type)
         {
+        case PT_DYNAMIC:
             dynamicAddr = reinterpret_cast<ElfW(Dyn)*>(ph.p_vaddr);
-        }
-        else if (ph.p_type == PT_NOTE || ph.p_type == PT_GNU_EH_FRAME)
-        {
-            if (ph.p_vaddr != 0 && ph.p_memsz != 0)
-            {
+            break;
+
+        case PT_NOTE:
+        case PT_GNU_EH_FRAME:
+            if (ph.p_vaddr != 0 && ph.p_memsz != 0) {
                 InsertMemoryRegion(ph.p_vaddr, ph.p_memsz);
             }
+            break;
+
+        case PT_LOAD:
+            MemoryRegion region(0, ph.p_vaddr, ph.p_vaddr + ph.p_memsz, baseAddress);
+            m_moduleAddresses.insert(region);
+            break;
         }
     }
 
@@ -542,12 +553,20 @@ CrashInfo::GetELFInfo(uint64_t baseAddress)
             TRACE("ELF: phdr %p type %d (%x) vaddr %" PRIxA " memsz %" PRIxA " paddr %" PRIxA " filesz %" PRIxA " offset %" PRIxA " align %" PRIxA "\n",
                 phdrAddr, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_paddr, ph.p_filesz, ph.p_offset, ph.p_align);
 
-            if (ph.p_type == PT_DYNAMIC || ph.p_type == PT_NOTE || ph.p_type == PT_GNU_EH_FRAME)
+            switch (ph.p_type)
             {
-                if (ph.p_vaddr != 0 && ph.p_memsz != 0)
-                {
+            case PT_DYNAMIC:
+            case PT_NOTE:
+            case PT_GNU_EH_FRAME:
+                if (ph.p_vaddr != 0 && ph.p_memsz != 0) {
                     InsertMemoryRegion(baseAddress + ph.p_vaddr, ph.p_memsz);
                 }
+                break;
+
+            case PT_LOAD:
+                MemoryRegion region(0, baseAddress + ph.p_vaddr, baseAddress + ph.p_vaddr + ph.p_memsz, baseAddress);
+                m_moduleAddresses.insert(region);
+                break;
             }
         }
     }
@@ -562,8 +581,8 @@ bool
 CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE minidumpType)
 {
     PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr;
-    ICLRDataEnumMemoryRegions* clrDataEnumRegions = nullptr;
-    IXCLRDataProcess* clrDataProcess = nullptr;
+    ICLRDataEnumMemoryRegions* pClrDataEnumRegions = nullptr;
+    IXCLRDataProcess* pClrDataProcess = nullptr;
     HMODULE hdac = nullptr;
     HRESULT hr = S_OK;
     bool result = false;
@@ -589,39 +608,43 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE
     }
     if ((minidumpType & MiniDumpWithFullMemory) == 0)
     {
-        hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), m_dataTarget, (void**)&clrDataEnumRegions);
+        hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), m_dataTarget, (void**)&pClrDataEnumRegions);
         if (FAILED(hr))
         {
             fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr);
             goto exit;
         }
         // Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC
-        hr = clrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT);
+        hr = pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT);
         if (FAILED(hr))
         {
             fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr);
             goto exit;
         }
     }
-    hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), m_dataTarget, (void**)&clrDataProcess);
+    hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), m_dataTarget, (void**)&pClrDataProcess);
     if (FAILED(hr))
     {
         fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr);
         goto exit;
     }
-    if (!EnumerateManagedModules(clrDataProcess))
+    if (!EnumerateManagedModules(pClrDataProcess))
+    {
+        goto exit;
+    }
+    if (!UnwindAllThreads(pClrDataProcess))
     {
         goto exit;
     }
     result = true;
 exit:
-    if (clrDataEnumRegions != nullptr)
+    if (pClrDataEnumRegions != nullptr)
     {
-        clrDataEnumRegions->Release();
+        pClrDataEnumRegions->Release();
     }
-    if (clrDataProcess != nullptr)
+    if (pClrDataProcess != nullptr)
     {
-        clrDataProcess->Release();
+        pClrDataProcess->Release();
     }
     if (hdac != nullptr)
     {
@@ -631,24 +654,29 @@ exit:
 }
 
 //
-// Enumerate all the managed modules and replace the module 
-// mapping with the module name found.
+// Enumerate all the managed modules and replace the module mapping with the module name found.
 //
 bool
-CrashInfo::EnumerateManagedModules(IXCLRDataProcess* clrDataProcess)
+CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess)
 {
-    IXCLRDataModule* clrDataModule = nullptr;
     CLRDATA_ENUM enumModules = 0;
+    bool result = true;
     HRESULT hr = S_OK;
 
-    if (FAILED(hr = clrDataProcess->StartEnumModules(&enumModules))) {
+    if (FAILED(hr = pClrDataProcess->StartEnumModules(&enumModules))) {
         fprintf(stderr, "StartEnumModules FAILED %08x\n", hr);
         return false;
     }
-    while ((hr = clrDataProcess->EnumModule(&enumModules, &clrDataModule)) == S_OK)
+
+    while (true)
     {
+        ReleaseHolder<IXCLRDataModule> pClrDataModule;
+        if ((hr = pClrDataProcess->EnumModule(&enumModules, &pClrDataModule)) != S_OK) {
+            break;
+        }
+
         DacpGetModuleData moduleData;
-        if (SUCCEEDED(hr = moduleData.Request(clrDataModule)))
+        if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr())))
         {
             TRACE("MODULE: %" PRIA PRIx64 " dyn %d inmem %d file %d pe %" PRIA PRIx64 " pdb %" PRIA PRIx64, moduleData.LoadedPEAddress, moduleData.IsDynamic, 
                 moduleData.IsInMemory, moduleData.IsFileLayout, moduleData.PEFile, moduleData.InMemoryPdbAddress);
@@ -656,12 +684,13 @@ CrashInfo::EnumerateManagedModules(IXCLRDataProcess* clrDataProcess)
             if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0)
             {
                 ArrayHolder<WCHAR> wszUnicodeName = new WCHAR[MAX_LONGPATH + 1];
-                if (SUCCEEDED(hr = clrDataModule->GetFileName(MAX_LONGPATH, NULL, wszUnicodeName)))
+                if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName)))
                 {
                     char* pszName = (char*)malloc(MAX_LONGPATH + 1);
                     if (pszName == nullptr) {
                         fprintf(stderr, "Allocating module name FAILED\n");
-                        return false;
+                        result = false;
+                        break;
                     }
                     sprintf_s(pszName, MAX_LONGPATH, "%S", (WCHAR*)wszUnicodeName);
                     TRACE(" %s\n", pszName);
@@ -680,12 +709,27 @@ CrashInfo::EnumerateManagedModules(IXCLRDataProcess* clrDataProcess)
         else {
             TRACE("moduleData.Request FAILED %08x\n", hr);
         }
-        if (clrDataModule != nullptr) {
-            clrDataModule->Release();
-        }
     }
+
     if (enumModules != 0) {
-        clrDataProcess->EndEnumModules(enumModules);
+        pClrDataProcess->EndEnumModules(enumModules);
+    }
+
+    return result;
+}
+
+//
+// Unwind all the native threads to ensure that the dwarf unwind info is added to the core dump.
+//
+bool
+CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess)
+{
+    // For each native and managed thread
+    for (ThreadInfo* thread : m_threads)
+    {
+        if (!thread->UnwindThread(*this, pClrDataProcess)) {
+            return false;
+        }
     }
     return true;
 }
@@ -729,6 +773,20 @@ CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, const char* pszName
 }
 
 //
+// Returns the module base address for the IP or 0.
+//
+uint64_t CrashInfo::GetBaseAddress(uint64_t ip)
+{
+    MemoryRegion search(0, ip, ip, 0);
+    const MemoryRegion* found = SearchMemoryRegions(m_moduleAddresses, search);
+    if (found == nullptr) {
+        return 0;
+    }
+    // The memory region Offset() is the base address of the module
+    return found->Offset();
+}
+
+//
 // ReadMemory from target and add to memory regions list
 //
 bool
@@ -828,11 +886,12 @@ CrashInfo::InsertMemoryRegion(const MemoryRegion& region)
 uint32_t 
 CrashInfo::GetMemoryRegionFlags(uint64_t start)
 {
-    const MemoryRegion* region = SearchMemoryRegions(m_moduleMappings, start);
+    MemoryRegion search(0, start, start + PAGE_SIZE);
+    const MemoryRegion* region = SearchMemoryRegions(m_moduleMappings, search);
     if (region != nullptr) {
         return region->Flags();
     }
-    region = SearchMemoryRegions(m_otherMappings, start);
+    region = SearchMemoryRegions(m_otherMappings, search);
     if (region != nullptr) {
         return region->Flags();
     }
@@ -922,12 +981,12 @@ CrashInfo::CombineMemoryRegions()
 // Searches for a memory region given an address.
 //
 const MemoryRegion* 
-CrashInfo::SearchMemoryRegions(const std::set<MemoryRegion>& regions, uint64_t start)
+CrashInfo::SearchMemoryRegions(const std::set<MemoryRegion>& regions, const MemoryRegion& search)
 {
-    std::set<MemoryRegion>::iterator found = regions.find(MemoryRegion(0, start, start + PAGE_SIZE));
+    std::set<MemoryRegion>::iterator found = regions.find(search);
     for (; found != regions.end(); found++)
     {
-        if (start >= found->StartAddress() && start < found->EndAddress())
+        if (search.StartAddress() >= found->StartAddress() && search.StartAddress() < found->EndAddress())
         {
             return &*found;
         }
index 65f1d18..d7c3f03 100644 (file)
@@ -10,7 +10,7 @@ typedef Elf32_auxv_t elf_aux_entry;
 #define PRId PRId32
 #define PRIA "08"
 #define PRIxA PRIA PRIx
-#elif defined(__x86_64) || defined(__aarch64__)
+#elif defined(__x86_64__) || defined(__aarch64__)
 typedef Elf64_auxv_t elf_aux_entry;
 #define PRIx PRIx64
 #define PRIu PRIu64
@@ -40,6 +40,7 @@ private:
     std::set<MemoryRegion> m_moduleMappings;        // module memory mappings
     std::set<MemoryRegion> m_otherMappings;         // other memory mappings
     std::set<MemoryRegion> m_memoryRegions;         // memory regions from DAC, etc.
+    std::set<MemoryRegion> m_moduleAddresses;       // memory region to module base address
 
 public:
     CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos);
@@ -47,21 +48,24 @@ public:
     bool EnumerateAndSuspendThreads();
     bool GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType);
     void ResumeThreads();
+    bool ReadMemory(void* address, void* buffer, size_t size);
+    uint64_t GetBaseAddress(uint64_t ip);
+    void InsertMemoryRegion(uint64_t address, size_t size);
     static bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, char** name);
-    static const MemoryRegion* SearchMemoryRegions(const std::set<MemoryRegion>& regions, uint64_t start);
+    static const MemoryRegion* SearchMemoryRegions(const std::set<MemoryRegion>& regions, const MemoryRegion& search);
 
-    const pid_t Pid() const { return m_pid; }
-    const pid_t Ppid() const { return m_ppid; }
-    const pid_t Tgid() const { return m_tgid; }
-    const char* Name() const { return m_name; }
-    ICLRDataTarget* DataTarget() const { return m_dataTarget; }
+    inline const pid_t Pid() const { return m_pid; }
+    inline const pid_t Ppid() const { return m_ppid; }
+    inline const pid_t Tgid() const { return m_tgid; }
+    inline const char* Name() const { return m_name; }
+    inline ICLRDataTarget* DataTarget() const { return m_dataTarget; }
 
-    const std::vector<ThreadInfo*> Threads() const { return m_threads; }
-    const std::set<MemoryRegion> ModuleMappings() const { return m_moduleMappings; }
-    const std::set<MemoryRegion> OtherMappings() const { return m_otherMappings; }
-    const std::set<MemoryRegion> MemoryRegions() const { return m_memoryRegions; }
-    const std::vector<elf_aux_entry> AuxvEntries() const { return m_auxvEntries; }
-    const size_t GetAuxvSize() const { return m_auxvEntries.size() * sizeof(elf_aux_entry); }
+    inline const std::vector<ThreadInfo*> Threads() const { return m_threads; }
+    inline const std::set<MemoryRegion> ModuleMappings() const { return m_moduleMappings; }
+    inline const std::set<MemoryRegion> OtherMappings() const { return m_otherMappings; }
+    inline const std::set<MemoryRegion> MemoryRegions() const { return m_memoryRegions; }
+    inline const std::vector<elf_aux_entry> AuxvEntries() const { return m_auxvEntries; }
+    inline const size_t GetAuxvSize() const { return m_auxvEntries.size() * sizeof(elf_aux_entry); }
 
     // IUnknown
     STDMETHOD(QueryInterface)(___in REFIID InterfaceId, ___out PVOID* Interface);
@@ -77,11 +81,10 @@ private:
     bool GetDSOInfo();
     bool GetELFInfo(uint64_t baseAddress);
     bool EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE minidumpType);
-    bool EnumerateManagedModules(IXCLRDataProcess* clrDataProcess);
+    bool EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess);
+    bool UnwindAllThreads(IXCLRDataProcess* pClrDataProcess);
     void ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, const char* pszName);
-    bool ReadMemory(void* address, void* buffer, size_t size);
     void InsertMemoryBackedRegion(const MemoryRegion& region);
-    void InsertMemoryRegion(uint64_t address, size_t size);
     void InsertMemoryRegion(const MemoryRegion& region);
     uint32_t GetMemoryRegionFlags(uint64_t start);
     bool ValidRegion(const MemoryRegion& region);
index dec52c7..9bbfcc7 100644 (file)
@@ -251,12 +251,3 @@ DumpDataTarget::Request(
     assert(false);
     return E_NOTIMPL;
 }
-
-HRESULT STDMETHODCALLTYPE 
-DumpDataTarget::VirtualUnwind(
-    /* [in] */ DWORD threadId,
-    /* [in] */ ULONG32 contextSize,
-    /* [in, out, size_is(contextSize)] */ PBYTE context)
-{
-    return E_NOTIMPL;
-}
index 8ff6773..f169818 100644 (file)
@@ -4,7 +4,7 @@
 
 class CrashInfo;
 
-class DumpDataTarget : public ICLRDataTarget, ICorDebugDataTarget4
+class DumpDataTarget : public ICLRDataTarget
 {
 private:
     LONG m_ref;                         // reference count
@@ -79,12 +79,4 @@ public:
         /* [size_is][in] */ BYTE *inBuffer,
         /* [in] */ ULONG32 outBufferSize,
         /* [size_is][out] */ BYTE *outBuffer);
-
-    //
-    // ICorDebugDataTarget4
-    //
-    virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(
-        /* [in] */ DWORD threadId,
-        /* [in] */ ULONG32 contextSize,
-        /* [in, out, size_is(contextSize)] */ PBYTE context);
 };
index e8e49aa..fbdd9d0 100644 (file)
@@ -58,6 +58,18 @@ public:
         assert((end & ~PAGE_MASK) == 0);
     }
 
+    // This is a special constructor for the module base address
+    // set where the start/end are not page aligned and "offset"
+    // is reused as the module base address.
+    MemoryRegion(uint32_t flags, uint64_t start, uint64_t end, uint64_t baseAddress) : 
+        m_flags(flags),
+        m_startAddress(start),
+        m_endAddress(end),
+        m_offset(baseAddress),
+        m_fileName(nullptr)
+    {
+    }
+
     // copy with new file name constructor
     MemoryRegion(const MemoryRegion& region, const char* fileName) : 
         m_flags(region.m_flags),
index 6a91ed7..3e5a5cc 100644 (file)
@@ -5,6 +5,10 @@
 #include "createdump.h"
 #include <asm/ptrace.h>
 
+#ifndef THUMB_CODE
+#define THUMB_CODE 1
+#endif
+
 #ifndef __GLIBC__
 typedef int __ptrace_request;
 #endif
@@ -14,6 +18,8 @@ typedef int __ptrace_request;
 #define FPREG_DataOffset(fpregs) *(DWORD*)&((fpregs).rdp)
 #define FPREG_DataSelector(fpregs) *(((WORD*)&((fpregs).rdp)) + 2)
 
+extern CrashInfo* g_crashInfo;
+
 ThreadInfo::ThreadInfo(pid_t tid) :
     m_tid(tid)
 {
@@ -24,15 +30,15 @@ ThreadInfo::~ThreadInfo()
 }
 
 bool
-ThreadInfo::Initialize(ICLRDataTarget* dataTarget)
+ThreadInfo::Initialize(ICLRDataTarget* pDataTarget)
 {
     if (!CrashInfo::GetStatus(m_tid, &m_ppid, &m_tgid, nullptr)) 
     {
         return false;
     }
-    if (dataTarget != nullptr)
+    if (pDataTarget != nullptr)
     {
-        if (!GetRegistersWithDataTarget(dataTarget))
+        if (!GetRegistersWithDataTarget(pDataTarget))
         {
             return false;
         }
@@ -62,6 +68,108 @@ ThreadInfo::ResumeThread()
     }
 }
 
+// Helper for UnwindNativeFrames
+static void
+GetFrameLocation(CONTEXT* pContext, uint64_t* ip, uint64_t* sp)
+{
+#if defined(__x86_64__)
+    *ip = pContext->Rip;
+    *sp = pContext->Rsp;
+#elif defined(__i386__)
+    *ip = pContext->Eip;
+    *sp = pContext->Esp;
+#elif defined(__arm__)
+    *ip = pContext->Pc & ~THUMB_CODE;
+    *sp = pContext->Sp;
+#endif
+}
+
+// Helper for UnwindNativeFrames
+static BOOL 
+ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size)
+{
+    return g_crashInfo->ReadMemory(address, buffer, size);
+}
+
+void
+ThreadInfo::UnwindNativeFrames(CrashInfo& crashInfo, CONTEXT* pContext)
+{
+    // For each native frame
+    while (true)
+    {
+        uint64_t ip = 0, sp = 0;
+        GetFrameLocation(pContext, &ip, &sp);
+
+        TRACE("Unwind: sp %" PRIA PRIx64 " ip %" PRIA PRIx64 "\n", sp, ip);
+        if (ip == 0) {
+            break;
+        }
+        // Add two pages around the instruction pointer to the core dump
+        crashInfo.InsertMemoryRegion(ip - PAGE_SIZE, PAGE_SIZE * 2);
+
+        // Look up the ip address to get the module base address
+        uint64_t baseAddress = crashInfo.GetBaseAddress(ip);
+        if (baseAddress == 0) {
+            TRACE("Unwind: module base not found ip %" PRIA PRIx64 "\n", ip);
+            break;
+        }
+
+        // Unwind the native frame adding all the memory accessed to the 
+        // core dump via the read memory adapter.
+        if (!PAL_VirtualUnwindOutOfProc(pContext, nullptr, baseAddress, ReadMemoryAdapter)) {
+            TRACE("Unwind: PAL_VirtualUnwindOutOfProc returned false\n");
+            break;
+        }
+    }
+}
+
+bool
+ThreadInfo::UnwindThread(CrashInfo& crashInfo, IXCLRDataProcess* pClrDataProcess)
+{
+    ReleaseHolder<IXCLRDataTask> pTask;
+    ReleaseHolder<IXCLRDataStackWalk> pStackwalk;
+
+    TRACE("Unwind: thread %04x\n", Tid());
+
+    // Get starting native context for the thread
+    CONTEXT context;
+    GetThreadContext(CONTEXT_ALL, &context);
+
+    // Unwind the native frames at the top of the stack
+    UnwindNativeFrames(crashInfo, &context);
+
+    // Get the managed stack walker for this thread
+    if (SUCCEEDED(pClrDataProcess->GetTaskByOSThreadID(Tid(), &pTask)))
+    {
+        pTask->CreateStackWalk(
+            CLRDATA_SIMPFRAME_UNRECOGNIZED |
+            CLRDATA_SIMPFRAME_MANAGED_METHOD |
+            CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
+            CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
+            &pStackwalk);
+    }
+
+    // For each managed frame (if any)
+    if (pStackwalk != nullptr) 
+    {
+        TRACE("Unwind: managed frames\n");
+        do
+        {
+            // Get the managed stack frame context
+            if (pStackwalk->GetContext(CONTEXT_ALL, sizeof(context), nullptr, (BYTE *)&context) != S_OK) {
+                TRACE("Unwind: stack walker GetContext FAILED\n");
+                break;
+            }
+
+            // Unwind all the native frames after the managed frame
+            UnwindNativeFrames(crashInfo, &context);
+
+        } while (pStackwalk->Next() == S_OK);
+    }
+
+    return true;
+}
+
 bool 
 ThreadInfo::GetRegistersWithPTrace()
 {
@@ -97,11 +205,11 @@ ThreadInfo::GetRegistersWithPTrace()
 }
 
 bool 
-ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* dataTarget)
+ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* pDataTarget)
 {
     CONTEXT context;
     context.ContextFlags = CONTEXT_ALL;
-    if (dataTarget->GetThreadContext(m_tid, context.ContextFlags, sizeof(context), reinterpret_cast<PBYTE>(&context)) != S_OK)
+    if (pDataTarget->GetThreadContext(m_tid, context.ContextFlags, sizeof(context), reinterpret_cast<PBYTE>(&context)) != S_OK)
     {
         return false;
     }
@@ -188,39 +296,32 @@ ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* dataTarget)
 }
 
 void
-ThreadInfo::GetThreadStack(const CrashInfo& crashInfo, uint64_t* startAddress, size_t* size) const
+ThreadInfo::GetThreadStack(CrashInfo& crashInfo)
 {
+    uint64_t startAddress;
+    size_t size;
+
 #if defined(__arm__)
-    *startAddress = m_gpRegisters.ARM_sp & PAGE_MASK;
+    startAddress = m_gpRegisters.ARM_sp & PAGE_MASK;
 #else
-    *startAddress = m_gpRegisters.rsp & PAGE_MASK;
+    startAddress = m_gpRegisters.rsp & PAGE_MASK;
 #endif
-    *size = 4 * PAGE_SIZE;
+    size = 4 * PAGE_SIZE;
 
-    const MemoryRegion* region = CrashInfo::SearchMemoryRegions(crashInfo.OtherMappings(), *startAddress);
+    MemoryRegion search(0, startAddress, startAddress + PAGE_SIZE);
+    const MemoryRegion* region = CrashInfo::SearchMemoryRegions(crashInfo.OtherMappings(), search);
     if (region != nullptr) {
 
         // Use the mapping found for the size of the thread's stack
-        *size = region->EndAddress() - *startAddress;
+        size = region->EndAddress() - startAddress;
 
         if (g_diagnostics)
         {
-            TRACE("Thread %04x stack found in other mapping (size %08zx): ", m_tid, *size);
+            TRACE("Thread %04x stack found in other mapping (size %08zx): ", m_tid, size);
             region->Trace();
         }
     }
-    
-}
-
-void
-ThreadInfo::GetThreadCode(uint64_t* startAddress, size_t* size) const
-{
-#if defined(__arm__)
-    *startAddress = m_gpRegisters.ARM_pc & PAGE_MASK;
-#elif defined(__x86_64__)
-    *startAddress = m_gpRegisters.rip & PAGE_MASK;
-#endif
-    *size = PAGE_SIZE;
+    crashInfo.InsertMemoryRegion(startAddress, size);
 }
 
 void 
index 3756669..8851877 100644 (file)
@@ -35,25 +35,26 @@ private:
 public:
     ThreadInfo(pid_t tid);
     ~ThreadInfo();
-    bool Initialize(ICLRDataTarget* dataTarget);
+    bool Initialize(ICLRDataTarget* pDataTarget);
     void ResumeThread();
-    void GetThreadStack(const CrashInfo& crashInfo, uint64_t* startAddress, size_t* size) const;
-    void GetThreadCode(uint64_t* startAddress, size_t* size) const;
+    bool UnwindThread(CrashInfo& crashInfo, IXCLRDataProcess* pClrDataProcess);
+    void GetThreadStack(CrashInfo& crashInfo);
     void GetThreadContext(uint32_t flags, CONTEXT* context) const;
 
-    const pid_t Tid() const { return m_tid; }
-    const pid_t Ppid() const { return m_ppid; }
-    const pid_t Tgid() const { return m_tgid; }
+    inline const pid_t Tid() const { return m_tid; }
+    inline const pid_t Ppid() const { return m_ppid; }
+    inline const pid_t Tgid() const { return m_tgid; }
 
-    const user_regs_struct* GPRegisters() const { return &m_gpRegisters; }
-    const user_fpregs_struct* FPRegisters() const { return &m_fpRegisters; }
+    inline const user_regs_struct* GPRegisters() const { return &m_gpRegisters; }
+    inline const user_fpregs_struct* FPRegisters() const { return &m_fpRegisters; }
 #if defined(__i386__)
-    const user_fpxregs_struct* FPXRegisters() const { return &m_fpxRegisters; }
+    inline const user_fpxregs_struct* FPXRegisters() const { return &m_fpxRegisters; }
 #elif defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__)
-    const user_vfpregs_struct* VFPRegisters() const { return &m_vfpRegisters; }
+    inline const user_vfpregs_struct* VFPRegisters() const { return &m_vfpRegisters; }
 #endif
 
 private:
+    void UnwindNativeFrames(CrashInfo& crashInfo, CONTEXT* pContext);
     bool GetRegistersWithPTrace();
     bool GetRegistersWithDataTarget(ICLRDataTarget* dataTarget);
 };
index 78a7b9f..d6e0a89 100644 (file)
@@ -220,8 +220,29 @@ DacWriteAll(TADDR addr, PVOID buffer, ULONG32 size, bool throwEx)
 
 static BOOL DacReadAllAdapter(PVOID address, PVOID buffer, SIZE_T size)
 {
-    HRESULT hr = DacReadAll((TADDR)address, (PVOID)buffer, size, false);
-    return SUCCEEDED(hr);
+    DAC_INSTANCE* inst = g_dacImpl->m_instances.Find((TADDR)address);
+    if (inst == nullptr || inst->size < size)
+    {
+        inst = g_dacImpl->m_instances.Alloc((TADDR)address, size, DAC_PAL);
+        if (inst == nullptr)
+        {
+            return FALSE;
+        }
+        inst->noReport = 0;
+        HRESULT hr = DacReadAll((TADDR)address, inst + 1, size, false);
+        if (FAILED(hr))
+        {
+            g_dacImpl->m_instances.ReturnAlloc(inst);
+            return FALSE;
+        }
+        if (!g_dacImpl->m_instances.Add(inst))
+        {
+            g_dacImpl->m_instances.ReturnAlloc(inst);
+            return FALSE;
+        }
+    }
+    memcpy(buffer, inst + 1, size);
+    return TRUE;
 }
 
 HRESULT 
index c37face..b858230 100644 (file)
@@ -125,6 +125,7 @@ enum DAC_USAGE_TYPE
     DAC_VPTR,
     DAC_STRA,
     DAC_STRW,
+    DAC_PAL,
 };
 
 // mscordacwks's module handle