summaryrefslogtreecommitdiff
path: root/src/debug
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug')
-rw-r--r--src/debug/CMakeLists.txt2
-rw-r--r--src/debug/createdump/.gitmirrorall1
-rw-r--r--src/debug/createdump/CMakeLists.txt38
-rw-r--r--src/debug/createdump/crashinfo.cpp628
-rw-r--r--src/debug/createdump/crashinfo.h73
-rw-r--r--src/debug/createdump/createdump.cpp69
-rw-r--r--src/debug/createdump/createdump.h57
-rw-r--r--src/debug/createdump/datatarget.cpp263
-rw-r--r--src/debug/createdump/datatarget.h90
-rw-r--r--src/debug/createdump/dumpwriter.cpp486
-rw-r--r--src/debug/createdump/dumpwriter.h77
-rw-r--r--src/debug/createdump/main.cpp99
-rw-r--r--src/debug/createdump/memoryregion.h97
-rw-r--r--src/debug/createdump/threadinfo.cpp235
-rw-r--r--src/debug/createdump/threadinfo.h42
-rw-r--r--src/debug/daccess/dacfn.cpp2
-rw-r--r--src/debug/daccess/enummem.cpp103
-rw-r--r--src/debug/daccess/request_svr.cpp16
-rw-r--r--src/debug/di/dbgtransportmanager.cpp4
-rw-r--r--src/debug/inc/dump/dumpcommon.h29
20 files changed, 2351 insertions, 60 deletions
diff --git a/src/debug/CMakeLists.txt b/src/debug/CMakeLists.txt
index 1940aa9c79..bcfc257b9d 100644
--- a/src/debug/CMakeLists.txt
+++ b/src/debug/CMakeLists.txt
@@ -3,4 +3,4 @@ add_subdirectory(dbgutil)
add_subdirectory(ildbsymlib)
add_subdirectory(ee)
add_subdirectory(di)
-add_subdirectory(shim)
+add_subdirectory(shim) \ No newline at end of file
diff --git a/src/debug/createdump/.gitmirrorall b/src/debug/createdump/.gitmirrorall
new file mode 100644
index 0000000000..9ee5c57b99
--- /dev/null
+++ b/src/debug/createdump/.gitmirrorall
@@ -0,0 +1 @@
+This folder will be mirrored by the Git-TFS Mirror recursively. \ No newline at end of file
diff --git a/src/debug/createdump/CMakeLists.txt b/src/debug/createdump/CMakeLists.txt
new file mode 100644
index 0000000000..5b5ec0a5c6
--- /dev/null
+++ b/src/debug/createdump/CMakeLists.txt
@@ -0,0 +1,38 @@
+project(createdump)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+remove_definitions(-DUNICODE)
+remove_definitions(-D_UNICODE)
+
+include_directories(BEFORE ${VM_DIR})
+
+add_definitions(-DPAL_STDCPP_COMPAT=1)
+
+add_compile_options(-fPIC)
+
+set(CREATEDUMP_SOURCES
+ createdump.cpp
+ crashinfo.cpp
+ threadinfo.cpp
+ datatarget.cpp
+ dumpwriter.cpp
+)
+
+_add_library(createdump_lib
+ ${CREATEDUMP_SOURCES}
+)
+
+_add_executable(createdump
+ main.cpp
+)
+
+target_link_libraries(createdump
+ createdump_lib
+ # share the PAL in the dac module
+ mscordaccore
+)
+
+add_dependencies(createdump mscordaccore)
+
+install_clr(createdump)
diff --git a/src/debug/createdump/crashinfo.cpp b/src/debug/createdump/crashinfo.cpp
new file mode 100644
index 0000000000..cae8857d65
--- /dev/null
+++ b/src/debug/createdump/crashinfo.cpp
@@ -0,0 +1,628 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "createdump.h"
+
+CrashInfo::CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos) :
+ m_ref(1),
+ m_pid(pid),
+ m_ppid(-1),
+ m_name(nullptr),
+ m_sos(sos),
+ m_dataTarget(dataTarget)
+{
+ dataTarget->AddRef();
+ m_auxvValues.fill(0);
+}
+
+CrashInfo::~CrashInfo()
+{
+ if (m_name != nullptr)
+ {
+ free(m_name);
+ }
+ // Clean up the threads
+ for (ThreadInfo* thread : m_threads)
+ {
+ delete thread;
+ }
+ m_threads.clear();
+
+ // Module and other mappings have a file name to clean up.
+ for (const MemoryRegion& region : m_moduleMappings)
+ {
+ const_cast<MemoryRegion&>(region).Cleanup();
+ }
+ m_moduleMappings.clear();
+ for (const MemoryRegion& region : m_otherMappings)
+ {
+ const_cast<MemoryRegion&>(region).Cleanup();
+ }
+ m_otherMappings.clear();
+ m_dataTarget->Release();
+}
+
+STDMETHODIMP
+CrashInfo::QueryInterface(
+ ___in REFIID InterfaceId,
+ ___out PVOID* Interface)
+{
+ if (InterfaceId == IID_IUnknown ||
+ InterfaceId == IID_ICLRDataEnumMemoryRegionsCallback)
+ {
+ *Interface = (ICLRDataEnumMemoryRegionsCallback*)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+CrashInfo::AddRef()
+{
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+}
+
+STDMETHODIMP_(ULONG)
+CrashInfo::Release()
+{
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+}
+
+HRESULT STDMETHODCALLTYPE
+CrashInfo::EnumMemoryRegion(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [in] */ ULONG32 size)
+{
+ InsertMemoryRegion(address, size);
+ return S_OK;
+}
+
+bool
+CrashInfo::EnumerateAndSuspendThreads()
+{
+ char taskPath[128];
+ snprintf(taskPath, sizeof(taskPath), "/proc/%d/task", m_pid);
+
+ DIR* taskDir = opendir(taskPath);
+ if (taskDir == nullptr)
+ {
+ fprintf(stderr, "opendir(%s) FAILED %s\n", taskPath, strerror(errno));
+ return false;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(taskDir)) != nullptr)
+ {
+ pid_t tid = static_cast<pid_t>(strtol(entry->d_name, nullptr, 10));
+ if (tid != 0)
+ {
+ // Don't suspend the threads if running under sos
+ if (!m_sos)
+ {
+ // 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
+ {
+ fprintf(stderr, "ptrace(ATTACH, %d) FAILED %s\n", tid, strerror(errno));
+ closedir(taskDir);
+ return false;
+ }
+ }
+ // Add to the list of threads
+ ThreadInfo* thread = new ThreadInfo(tid);
+ m_threads.push_back(thread);
+ }
+ }
+
+ closedir(taskDir);
+ return true;
+}
+
+bool
+CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType)
+{
+ // 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))
+ {
+ return false;
+ }
+ }
+ // Get the auxv data
+ if (!GetAuxvEntries())
+ {
+ return false;
+ }
+ // Get shared module debug info
+ if (!GetDSOInfo())
+ {
+ return false;
+ }
+ // Gather all the module memory mappings (from /dev/$pid/maps)
+ if (!EnumerateModuleMappings())
+ {
+ return false;
+ }
+ // Gather all the useful memory regions from the DAC
+ if (!EnumerateMemoryRegionsWithDAC(programPath, minidumpType))
+ {
+ return false;
+ }
+ // 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);
+ }
+ // Join all adjacent memory regions
+ CombineMemoryRegions();
+ return true;
+}
+
+void
+CrashInfo::ResumeThreads()
+{
+ if (!m_sos)
+ {
+ for (ThreadInfo* thread : m_threads)
+ {
+ thread->ResumeThread();
+ }
+ }
+}
+
+bool
+CrashInfo::GetAuxvEntries()
+{
+ char auxvPath[128];
+ snprintf(auxvPath, sizeof(auxvPath), "/proc/%d/auxv", m_pid);
+
+ int fd = open(auxvPath, O_RDONLY, 0);
+ if (fd == -1)
+ {
+ fprintf(stderr, "open(%s) FAILED %s\n", auxvPath, strerror(errno));
+ return false;
+ }
+ bool result = false;
+ elf_aux_entry auxvEntry;
+
+ while (read(fd, &auxvEntry, sizeof(elf_aux_entry)) == sizeof(elf_aux_entry))
+ {
+ m_auxvEntries.push_back(auxvEntry);
+ if (auxvEntry.a_type == AT_NULL)
+ {
+ break;
+ }
+ if (auxvEntry.a_type < AT_MAX)
+ {
+ m_auxvValues[auxvEntry.a_type] = auxvEntry.a_un.a_val;
+ TRACE("AUXV: %lu = %016lx\n", auxvEntry.a_type, auxvEntry.a_un.a_val);
+ result = true;
+ }
+ }
+
+ close(fd);
+ return result;
+}
+
+bool
+CrashInfo::EnumerateModuleMappings()
+{
+ // Here we read /proc/<pid>/maps file in order to parse it and figure out what it says
+ // about a library we are looking for. This file looks something like this:
+ //
+ // [address] [perms] [offset] [dev] [inode] [pathname] - HEADER is not preset in an actual file
+ //
+ // 35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so
+ // 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so
+ // 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so
+ // 35b1a21000-35b1a22000 rw-p 00000000 00:00 0 [heap]
+ // 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so
+ // 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;
+ size_t lineLen = 0;
+ int count = 0;
+ ssize_t read;
+
+ // Making something like: /proc/123/maps
+ char mapPath[128];
+ int chars = snprintf(mapPath, sizeof(mapPath), "/proc/%d/maps", m_pid);
+ assert(chars > 0 && chars <= sizeof(mapPath));
+
+ FILE* mapsFile = fopen(mapPath, "r");
+ if (mapsFile == NULL)
+ {
+ fprintf(stderr, "fopen(%s) FAILED %s\n", mapPath, strerror(errno));
+ return false;
+ }
+ // linuxGateAddress is the beginning of the kernel's mapping of
+ // linux-gate.so in the process. It doesn't actually show up in the
+ // maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
+ // aux vector entry, which gives the information necessary to special
+ // case its entry when creating the list of mappings.
+ // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
+ // information.
+ const void* linuxGateAddress = (const void*)m_auxvValues[AT_SYSINFO_EHDR];
+
+ // Reading maps file line by line
+ while ((read = getline(&line, &lineLen, mapsFile)) != -1)
+ {
+ uint64_t start, end, offset;
+ char* permissions = nullptr;
+ char* moduleName = nullptr;
+
+ int c = 0;
+ if ((c = sscanf(line, "%lx-%lx %m[-rwxsp] %lx %*[:0-9a-f] %*d %ms\n", &start, &end, &permissions, &offset, &moduleName)) == 5)
+ {
+ if (linuxGateAddress != nullptr && reinterpret_cast<void*>(start) == linuxGateAddress)
+ {
+ InsertMemoryRegion(start, end - start);
+ free(moduleName);
+ }
+ else {
+ uint32_t permissionFlags = 0;
+ if (strchr(permissions, 'r')) {
+ permissionFlags |= PF_R;
+ }
+ if (strchr(permissions, 'w')) {
+ permissionFlags |= PF_W;
+ }
+ if (strchr(permissions, 'x')) {
+ permissionFlags |= PF_X;
+ }
+ MemoryRegion memoryRegion(permissionFlags, start, end, offset, moduleName);
+
+ if (moduleName != nullptr && *moduleName == '/') {
+ m_moduleMappings.insert(memoryRegion);
+ }
+ else {
+ m_otherMappings.insert(memoryRegion);
+ }
+ }
+ free(permissions);
+ }
+ }
+
+ if (g_diagnostics)
+ {
+ TRACE("Module mappings:\n");
+ for (const MemoryRegion& region : m_moduleMappings)
+ {
+ region.Print();
+ }
+ TRACE("Other mappings:\n");
+ for (const MemoryRegion& region : m_otherMappings)
+ {
+ region.Print();
+ }
+ }
+
+ free(line); // We didn't allocate line, but as per contract of getline we should free it
+ fclose(mapsFile);
+
+ return true;
+}
+
+bool
+CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE minidumpType)
+{
+ PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr;
+ ICLRDataEnumMemoryRegions *clrDataEnumRegions = nullptr;
+ HMODULE hdac = nullptr;
+ HRESULT hr = S_OK;
+ bool result = false;
+
+ // We assume that the DAC is in the same location as this createdump exe
+ std::string dacPath;
+ dacPath.append(programPath);
+ dacPath.append("/");
+ dacPath.append(MAKEDLLNAME_A("mscordaccore"));
+
+ // Load and initialize the DAC
+ hdac = LoadLibraryA(dacPath.c_str());
+ if (hdac == nullptr)
+ {
+ fprintf(stderr, "LoadLibraryA(%s) FAILED %d\n", dacPath.c_str(), GetLastError());
+ goto exit;
+ }
+ pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance");
+ if (pfnCLRDataCreateInstance == nullptr)
+ {
+ fprintf(stderr, "GetProcAddress(CLRDataCreateInstance) FAILED %d\n", GetLastError());
+ goto exit;
+ }
+ hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), m_dataTarget, (void**)&clrDataEnumRegions);
+ 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);
+ if (FAILED(hr))
+ {
+ fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr);
+ goto exit;
+ }
+ result = true;
+exit:
+ if (clrDataEnumRegions != nullptr)
+ {
+ clrDataEnumRegions->Release();
+ }
+ if (hdac != nullptr)
+ {
+ FreeLibrary(hdac);
+ }
+ return result;
+}
+
+bool
+CrashInfo::GetDSOInfo()
+{
+ Phdr* phdrAddr = reinterpret_cast<Phdr*>(m_auxvValues[AT_PHDR]);
+ int phnum = m_auxvValues[AT_PHNUM];
+ assert(m_auxvValues[AT_PHENT] == sizeof(Phdr));
+
+ if (phnum <= 0 || phdrAddr == nullptr) {
+ return false;
+ }
+ TRACE("DSO: phdr %p phnum %d\n", phdrAddr, phnum);
+
+ // Search for the program PT_DYNAMIC header
+ ElfW(Dyn)* dynamicAddr = nullptr;
+ for (int i = 0; i < phnum; i++, phdrAddr++)
+ {
+ Phdr ph;
+ if (!ReadMemory(phdrAddr, &ph, sizeof(ph))) {
+ return false;
+ }
+ TRACE("DSO: phdr %p type %d (%x) vaddr %016lx memsz %016lx offset %016lx\n",
+ phdrAddr, ph.p_type, ph.p_type, ph.p_vaddr, ph.p_memsz, ph.p_offset);
+
+ if (ph.p_type == PT_DYNAMIC)
+ {
+ dynamicAddr = reinterpret_cast<ElfW(Dyn)*>(ph.p_vaddr);
+ }
+ else if (ph.p_type == PT_GNU_EH_FRAME)
+ {
+ if (ph.p_vaddr != 0 && ph.p_memsz != 0)
+ {
+ InsertMemoryRegion(ph.p_vaddr, ph.p_memsz);
+ }
+ }
+ }
+
+ if (dynamicAddr == nullptr) {
+ return false;
+ }
+
+ // Search for dynamic debug (DT_DEBUG) entry
+ struct r_debug* rdebugAddr = nullptr;
+ for (;;) {
+ ElfW(Dyn) dyn;
+ if (!ReadMemory(dynamicAddr, &dyn, sizeof(dyn))) {
+ return false;
+ }
+ TRACE("DSO: dyn %p tag %ld (%lx) d_ptr %016lx\n", dynamicAddr, dyn.d_tag, dyn.d_tag, dyn.d_un.d_ptr);
+ if (dyn.d_tag == DT_DEBUG) {
+ rdebugAddr = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr);
+ }
+ else if (dyn.d_tag == DT_NULL) {
+ break;
+ }
+ dynamicAddr++;
+ }
+
+ // Add the DSO r_debug entry
+ TRACE("DSO: rdebugAddr %p\n", rdebugAddr);
+ struct r_debug debugEntry;
+ if (!ReadMemory(rdebugAddr, &debugEntry, sizeof(debugEntry))) {
+ return false;
+ }
+
+ // Add the DSO link_map entries
+ for (struct link_map* linkMapAddr = debugEntry.r_map; linkMapAddr != nullptr;) {
+ struct link_map map;
+ if (!ReadMemory(linkMapAddr, &map, sizeof(map))) {
+ return false;
+ }
+ char moduleName[257] = { 0 };
+ if (map.l_name != nullptr) {
+ if (!ReadMemory(map.l_name, &moduleName, sizeof(moduleName) - 1)) {
+ return false;
+ }
+ }
+ TRACE("DSO: link_map entry %p l_ld %p l_addr %lx %s\n", linkMapAddr, map.l_ld, map.l_addr, moduleName);
+ linkMapAddr = map.l_next;
+ }
+
+ return true;
+}
+
+//
+// ReadMemory from target and add to memory regions list
+//
+bool
+CrashInfo::ReadMemory(void* address, void* buffer, size_t size)
+{
+ uint32_t read = 0;
+ if (FAILED(m_dataTarget->ReadVirtual(reinterpret_cast<CLRDATA_ADDRESS>(address), reinterpret_cast<PBYTE>(buffer), size, &read)))
+ {
+ return false;
+ }
+ InsertMemoryRegion(reinterpret_cast<uint64_t>(address), size);
+ return true;
+}
+
+//
+// Add this memory chunk to the list of regions to be
+// written to the core dump.
+//
+void
+CrashInfo::InsertMemoryRegion(uint64_t address, size_t size)
+{
+ // Round to page boundary
+ uint64_t start = address & PAGE_MASK;
+ assert(start > 0);
+
+ // Round up to page boundary
+ uint64_t end = ((address + size) + (PAGE_SIZE - 1)) & PAGE_MASK;
+ assert(end > 0);
+
+ MemoryRegion memoryRegionFull(start, end);
+
+ // First check if the full memory region can be added without conflicts
+ const auto& found = m_memoryRegions.find(memoryRegionFull);
+ if (found == m_memoryRegions.end())
+ {
+ // Add full memory region
+ m_memoryRegions.insert(memoryRegionFull);
+ }
+ else
+ {
+ // The memory region is not wholely contained in region found
+ if (!found->Contains(memoryRegionFull))
+ {
+ // The region overlaps/conflicts with one already in the set so
+ // add one page at a time to avoid the overlapping pages.
+ uint64_t numberPages = (end - start) >> PAGE_SHIFT;
+
+ for (int p = 0; p < numberPages; p++, start += PAGE_SIZE)
+ {
+ MemoryRegion memoryRegion(start, start + PAGE_SIZE);
+
+ const auto& found = m_memoryRegions.find(memoryRegion);
+ if (found == m_memoryRegions.end())
+ {
+ m_memoryRegions.insert(memoryRegion);
+ }
+ }
+ }
+ }
+}
+
+//
+// Combine any adjacent memory regions into one
+//
+void
+CrashInfo::CombineMemoryRegions()
+{
+ assert(!m_memoryRegions.empty());
+
+ std::set<MemoryRegion> memoryRegionsNew;
+
+ uint64_t start = m_memoryRegions.begin()->StartAddress();
+ uint64_t end = start;
+
+ for (const MemoryRegion& region : m_memoryRegions)
+ {
+ if (end == region.StartAddress())
+ {
+ end = region.EndAddress();
+ }
+ else
+ {
+ MemoryRegion memoryRegion(start, end);
+ assert(memoryRegionsNew.find(memoryRegion) == memoryRegionsNew.end());
+ memoryRegionsNew.insert(memoryRegion);
+
+ start = region.StartAddress();
+ end = region.EndAddress();
+ }
+ }
+
+ assert(start != end);
+ MemoryRegion memoryRegion(start, end);
+ assert(memoryRegionsNew.find(memoryRegion) == memoryRegionsNew.end());
+ memoryRegionsNew.insert(memoryRegion);
+
+ m_memoryRegions = memoryRegionsNew;
+
+ if (g_diagnostics)
+ {
+ TRACE("Memory Regions:\n");
+ for (const MemoryRegion& region : m_memoryRegions)
+ {
+ region.Print();
+ }
+ }
+}
+
+bool
+CrashInfo::GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, char** name)
+{
+ char statusPath[128];
+ snprintf(statusPath, sizeof(statusPath), "/proc/%d/status", pid);
+
+ FILE *statusFile = fopen(statusPath, "r");
+ if (statusFile == nullptr)
+ {
+ fprintf(stderr, "GetStatus fopen(%s) FAILED\n", statusPath);
+ return false;
+ }
+
+ *ppid = -1;
+
+ char *line = nullptr;
+ size_t lineLen = 0;
+ ssize_t read;
+ while ((read = getline(&line, &lineLen, statusFile)) != -1)
+ {
+ if (strncmp("PPid:\t", line, 6) == 0)
+ {
+ *ppid = _atoi64(line + 6);
+ }
+ else if (strncmp("Tgid:\t", line, 6) == 0)
+ {
+ *tgid = _atoi64(line + 6);
+ }
+ else if (strncmp("Name:\t", line, 6) == 0)
+ {
+ if (name != nullptr)
+ {
+ char* n = strchr(line + 6, '\n');
+ if (n != nullptr)
+ {
+ *n = '\0';
+ }
+ *name = strdup(line + 6);
+ }
+ }
+ }
+
+ free(line);
+ fclose(statusFile);
+ return true;
+}
diff --git a/src/debug/createdump/crashinfo.h b/src/debug/createdump/crashinfo.h
new file mode 100644
index 0000000000..40d7f5da45
--- /dev/null
+++ b/src/debug/createdump/crashinfo.h
@@ -0,0 +1,73 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// typedef for our parsing of the auxv variables in /proc/pid/auxv.
+#if defined(__i386) || defined(__ARM_EABI__)
+typedef Elf32_auxv_t elf_aux_entry;
+#elif defined(__x86_64) || defined(__aarch64__)
+typedef Elf64_auxv_t elf_aux_entry;
+#endif
+
+typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
+
+// All interesting auvx entry types are AT_SYSINFO_EHDR and below
+#define AT_MAX (AT_SYSINFO_EHDR + 1)
+
+class CrashInfo : public ICLRDataEnumMemoryRegionsCallback
+{
+private:
+ LONG m_ref; // reference count
+ pid_t m_pid; // pid
+ pid_t m_ppid; // parent pid
+ pid_t m_tgid; // process group
+ char* m_name; // exe name
+ bool m_sos; // true if running under sos
+ ICLRDataTarget* m_dataTarget; // read process memory, etc.
+ std::array<elf_aux_val_t, AT_MAX> m_auxvValues; // auxv values
+ std::vector<elf_aux_entry> m_auxvEntries; // full auxv entries
+ std::vector<ThreadInfo*> m_threads; // threads found and suspended
+ 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.
+
+public:
+ CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos);
+ virtual ~CrashInfo();
+ bool EnumerateAndSuspendThreads();
+ bool GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType);
+ void ResumeThreads();
+ static bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, char** name);
+
+ 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; }
+
+ 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); }
+
+ // IUnknown
+ STDMETHOD(QueryInterface)(___in REFIID InterfaceId, ___out PVOID* Interface);
+ STDMETHOD_(ULONG, AddRef)();
+ STDMETHOD_(ULONG, Release)();
+
+ // ICLRDataEnumMemoryRegionsCallback
+ virtual HRESULT STDMETHODCALLTYPE EnumMemoryRegion(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [in] */ ULONG32 size);
+
+private:
+ bool GetAuxvEntries();
+ bool EnumerateModuleMappings();
+ bool EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE minidumpType);
+ bool GetDSOInfo();
+ bool ReadMemory(void* address, void* buffer, size_t size);
+ void InsertMemoryRegion(uint64_t address, size_t size);
+ void CombineMemoryRegions();
+};
diff --git a/src/debug/createdump/createdump.cpp b/src/debug/createdump/createdump.cpp
new file mode 100644
index 0000000000..0a95e535aa
--- /dev/null
+++ b/src/debug/createdump/createdump.cpp
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "createdump.h"
+
+bool g_diagnostics = false;
+
+//
+// The common create dump code
+//
+bool
+CreateDumpCommon(const char* programPath, const char* dumpPathTemplate, MINIDUMP_TYPE minidumpType, CrashInfo* crashInfo)
+{
+ ReleaseHolder<DumpWriter> dumpWriter = new DumpWriter(*crashInfo);
+ bool result = false;
+
+ ArrayHolder<char> dumpPath = new char[MAX_LONGPATH];
+ snprintf(dumpPath, MAX_LONGPATH, dumpPathTemplate, crashInfo->Pid());
+
+ const char* dumpType = "minidump";
+ switch (minidumpType)
+ {
+ case MiniDumpWithPrivateReadWriteMemory:
+ dumpType = "minidump with heap";
+ break;
+
+ case MiniDumpFilterTriage:
+ dumpType = "triage minidump";
+ break;
+
+ default:
+ break;
+ }
+ printf("Writing %s to file %s\n", dumpType, (char*)dumpPath);
+
+ // Suspend all the threads in the target process and build the list of threads
+ if (!crashInfo->EnumerateAndSuspendThreads())
+ {
+ goto exit;
+ }
+ // Gather all the info about the process, threads (registers, etc.) and memory regions
+ if (!crashInfo->GatherCrashInfo(programPath, minidumpType))
+ {
+ goto exit;
+ }
+ if (!dumpWriter->OpenDump(dumpPath))
+ {
+ goto exit;
+ }
+ if (!dumpWriter->WriteDump())
+ {
+ goto exit;
+ }
+ result = true;
+exit:
+ crashInfo->ResumeThreads();
+ return result;
+}
+
+//
+// Entry point for SOS createdump command
+//
+bool
+CreateDumpForSOS(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType, ICLRDataTarget* dataTarget)
+{
+ ReleaseHolder<CrashInfo> crashInfo = new CrashInfo(pid, dataTarget, true);
+ return CreateDumpCommon(programPath, dumpPathTemplate, minidumpType, crashInfo);
+} \ No newline at end of file
diff --git a/src/debug/createdump/createdump.h b/src/debug/createdump/createdump.h
new file mode 100644
index 0000000000..38c3525f34
--- /dev/null
+++ b/src/debug/createdump/createdump.h
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#define ___in _SAL1_Source_(__in, (), _In_)
+#define ___out _SAL1_Source_(__out, (), _Out_)
+
+#ifndef _countof
+#define _countof(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+extern bool g_diagnostics;
+
+#define TRACE(args...) \
+ if (g_diagnostics) { \
+ printf(args); \
+ }
+
+#include <winternl.h>
+#include <winver.h>
+#include <windows.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <corhdr.h>
+#include <cor.h>
+#include <corsym.h>
+#include <clrdata.h>
+#include <xclrdata.h>
+#include <corerror.h>
+#include <cordebug.h>
+#include <xcordebug.h>
+#include <mscoree.h>
+#include <dumpcommon.h>
+#include <arrayholder.h>
+#include <releaseholder.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <sys/procfs.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <elf.h>
+#include <link.h>
+#include <map>
+#include <set>
+#include <vector>
+#include "datatarget.h"
+#include "threadinfo.h"
+#include "memoryregion.h"
+#include "crashinfo.h"
+#include "dumpwriter.h" \ No newline at end of file
diff --git a/src/debug/createdump/datatarget.cpp b/src/debug/createdump/datatarget.cpp
new file mode 100644
index 0000000000..38505e2d45
--- /dev/null
+++ b/src/debug/createdump/datatarget.cpp
@@ -0,0 +1,263 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "createdump.h"
+
+#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
+
+DumpDataTarget::DumpDataTarget(pid_t pid) :
+ m_ref(1),
+ m_pid(pid),
+ m_fd(-1),
+ m_crashInfo(nullptr)
+{
+}
+
+DumpDataTarget::~DumpDataTarget()
+{
+ if (m_fd != -1)
+ {
+ close(m_fd);
+ m_fd = -1;
+ }
+}
+
+bool
+DumpDataTarget::Initialize(CrashInfo * crashInfo)
+{
+ char memPath[128];
+ _snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%lu/mem", m_pid);
+
+ m_fd = open(memPath, O_RDONLY);
+ if (m_fd == -1)
+ {
+ fprintf(stderr, "open(%s) FAILED %d (%s)\n", memPath, errno, strerror(errno));
+ return false;
+ }
+ m_crashInfo = crashInfo;
+ return true;
+}
+
+STDMETHODIMP
+DumpDataTarget::QueryInterface(
+ ___in REFIID InterfaceId,
+ ___out PVOID* Interface
+ )
+{
+ if (InterfaceId == IID_IUnknown ||
+ InterfaceId == IID_ICLRDataTarget)
+ {
+ *Interface = (ICLRDataTarget*)this;
+ AddRef();
+ return S_OK;
+ }
+ else if (InterfaceId == IID_ICorDebugDataTarget4)
+ {
+ *Interface = (ICorDebugDataTarget4*)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+DumpDataTarget::AddRef()
+{
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+}
+
+STDMETHODIMP_(ULONG)
+DumpDataTarget::Release()
+{
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::GetMachineType(
+ /* [out] */ ULONG32 *machine)
+{
+#ifdef _AMD64_
+ *machine = IMAGE_FILE_MACHINE_AMD64;
+#elif _ARM_
+ *machine = IMAGE_FILE_MACHINE_ARMNT;
+#elif _ARM64_
+ *machine = IMAGE_FILE_MACHINE_ARM64;
+#elif _X86_
+ *machine = IMAGE_FILE_MACHINE_I386;
+#else
+#error Unsupported architecture
+#endif
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::GetPointerSize(
+ /* [out] */ ULONG32 *size)
+{
+#if defined(_AMD64_) || defined(_ARM64_)
+ *size = 8;
+#elif defined(_ARM_) || defined(_X86_)
+ *size = 4;
+#else
+#error Unsupported architecture
+#endif
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::GetImageBase(
+ /* [string][in] */ LPCWSTR moduleName,
+ /* [out] */ CLRDATA_ADDRESS *baseAddress)
+{
+ assert(m_crashInfo != nullptr);
+ *baseAddress = 0;
+
+ char tempModuleName[MAX_PATH];
+ int length = WideCharToMultiByte(CP_ACP, 0, moduleName, -1, tempModuleName, sizeof(tempModuleName), NULL, NULL);
+ if (length > 0)
+ {
+ for (const MemoryRegion& image : m_crashInfo->ModuleMappings())
+ {
+ const char *name = strrchr(image.FileName(), '/');
+ if (name != nullptr)
+ {
+ name++;
+ }
+ else
+ {
+ name = image.FileName();
+ }
+ if (strcmp(name, tempModuleName) == 0)
+ {
+ *baseAddress = image.StartAddress();
+ return S_OK;
+ }
+ }
+ }
+ return E_FAIL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::ReadVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [length_is][size_is][out] */ PBYTE buffer,
+ /* [in] */ ULONG32 size,
+ /* [optional][out] */ ULONG32 *done)
+{
+ assert(m_fd != -1);
+ size_t read = pread64(m_fd, buffer, size, (off64_t)address);
+ if (read == -1)
+ {
+ fprintf(stderr, "ReadVirtual FAILED %016lx %08x\n", address, size);
+ *done = 0;
+ return E_FAIL;
+ }
+ *done = read;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::WriteVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [size_is][in] */ PBYTE buffer,
+ /* [in] */ ULONG32 size,
+ /* [optional][out] */ ULONG32 *done)
+{
+ assert(false);
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::GetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [out] */ CLRDATA_ADDRESS* value)
+{
+ assert(false);
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::SetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [in] */ CLRDATA_ADDRESS value)
+{
+ assert(false);
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::GetCurrentThreadID(
+ /* [out] */ ULONG32* threadID)
+{
+ assert(false);
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::GetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ assert(m_crashInfo != nullptr);
+ if (contextSize < sizeof(CONTEXT))
+ {
+ assert(false);
+ return E_INVALIDARG;
+ }
+ memset(context, 0, contextSize);
+ for (const ThreadInfo* thread : m_crashInfo->Threads())
+ {
+ if (thread->Tid() == threadID)
+ {
+ thread->GetThreadContext(contextFlags, reinterpret_cast<CONTEXT*>(context));
+ return S_OK;
+ }
+ }
+ return E_FAIL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::SetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ assert(false);
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+DumpDataTarget::Request(
+ /* [in] */ ULONG32 reqCode,
+ /* [in] */ ULONG32 inBufferSize,
+ /* [size_is][in] */ BYTE *inBuffer,
+ /* [in] */ ULONG32 outBufferSize,
+ /* [size_is][out] */ BYTE *outBuffer)
+{
+ 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;
+}
diff --git a/src/debug/createdump/datatarget.h b/src/debug/createdump/datatarget.h
new file mode 100644
index 0000000000..8ff6773fa3
--- /dev/null
+++ b/src/debug/createdump/datatarget.h
@@ -0,0 +1,90 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+class CrashInfo;
+
+class DumpDataTarget : public ICLRDataTarget, ICorDebugDataTarget4
+{
+private:
+ LONG m_ref; // reference count
+ pid_t m_pid; // process id
+ int m_fd; // /proc/<pid>/mem handle
+ CrashInfo* m_crashInfo;
+
+public:
+ DumpDataTarget(pid_t pid);
+ virtual ~DumpDataTarget();
+ bool Initialize(CrashInfo* crashInfo);
+
+ //
+ // IUnknown
+ //
+ STDMETHOD(QueryInterface)(___in REFIID InterfaceId, ___out PVOID* Interface);
+ STDMETHOD_(ULONG, AddRef)();
+ STDMETHOD_(ULONG, Release)();
+
+ //
+ // ICLRDataTarget
+ //
+ virtual HRESULT STDMETHODCALLTYPE GetMachineType(
+ /* [out] */ ULONG32 *machine);
+
+ virtual HRESULT STDMETHODCALLTYPE GetPointerSize(
+ /* [out] */ ULONG32 *size);
+
+ virtual HRESULT STDMETHODCALLTYPE GetImageBase(
+ /* [string][in] */ LPCWSTR moduleName,
+ /* [out] */ CLRDATA_ADDRESS *baseAddress);
+
+ virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [length_is][size_is][out] */ PBYTE buffer,
+ /* [in] */ ULONG32 size,
+ /* [optional][out] */ ULONG32 *done);
+
+ virtual HRESULT STDMETHODCALLTYPE WriteVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [size_is][in] */ PBYTE buffer,
+ /* [in] */ ULONG32 size,
+ /* [optional][out] */ ULONG32 *done);
+
+ virtual HRESULT STDMETHODCALLTYPE GetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [out] */ CLRDATA_ADDRESS* value);
+
+ virtual HRESULT STDMETHODCALLTYPE SetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [in] */ CLRDATA_ADDRESS value);
+
+ virtual HRESULT STDMETHODCALLTYPE GetCurrentThreadID(
+ /* [out] */ ULONG32* threadID);
+
+ virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context);
+
+ virtual HRESULT STDMETHODCALLTYPE SetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [in, size_is(contextSize)] */ PBYTE context);
+
+ virtual HRESULT STDMETHODCALLTYPE Request(
+ /* [in] */ ULONG32 reqCode,
+ /* [in] */ ULONG32 inBufferSize,
+ /* [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);
+};
diff --git a/src/debug/createdump/dumpwriter.cpp b/src/debug/createdump/dumpwriter.cpp
new file mode 100644
index 0000000000..9057d180e1
--- /dev/null
+++ b/src/debug/createdump/dumpwriter.cpp
@@ -0,0 +1,486 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "createdump.h"
+
+DumpWriter::DumpWriter(CrashInfo& crashInfo) :
+ m_ref(1),
+ m_fd(-1),
+ m_crashInfo(crashInfo)
+{
+ m_crashInfo.AddRef();
+}
+
+DumpWriter::~DumpWriter()
+{
+ if (m_fd != -1)
+ {
+ close(m_fd);
+ m_fd = -1;
+ }
+ m_crashInfo.Release();
+}
+
+STDMETHODIMP
+DumpWriter::QueryInterface(
+ ___in REFIID InterfaceId,
+ ___out PVOID* Interface)
+{
+ if (InterfaceId == IID_IUnknown)
+ {
+ *Interface = (IUnknown*)this;
+ AddRef();
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+DumpWriter::AddRef()
+{
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+}
+
+STDMETHODIMP_(ULONG)
+DumpWriter::Release()
+{
+ LONG ref = InterlockedDecrement(&m_ref);
+ if (ref == 0)
+ {
+ delete this;
+ }
+ return ref;
+}
+
+bool
+DumpWriter::OpenDump(const char* dumpFileName)
+{
+ m_fd = open(dumpFileName, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+ if (m_fd == -1)
+ {
+ fprintf(stderr, "Could not open output %s: %s\n", dumpFileName, strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+// Write the core dump file:
+// ELF header
+// Single section header (Shdr) for 64 bit program header count
+// Phdr for the PT_NOTE
+// PT_LOAD
+// PT_NOTEs
+// process info (prpsinfo_t)
+// NT_FILE entries
+// threads
+// alignment
+// memory blocks
+bool
+DumpWriter::WriteDump()
+{
+ // Write the ELF header
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(Ehdr));
+ ehdr.e_ident[0] = ELFMAG0;
+ ehdr.e_ident[1] = ELFMAG1;
+ ehdr.e_ident[2] = ELFMAG2;
+ ehdr.e_ident[3] = ELFMAG3;
+ ehdr.e_ident[4] = ELF_CLASS;
+
+ // Note: The sex is the current system running minidump-2-core
+ // Big or Little endian. This means you have to create
+ // the core (minidump-2-core) on the system that matches
+ // your intent to debug properly.
+ ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB;
+ ehdr.e_ident[6] = EV_CURRENT;
+ ehdr.e_ident[EI_OSABI] = ELFOSABI_LINUX;
+
+ ehdr.e_type = ET_CORE;
+ ehdr.e_machine = ELF_ARCH;
+ ehdr.e_version = EV_CURRENT;
+ ehdr.e_shoff = sizeof(Ehdr);
+ ehdr.e_phoff = sizeof(Ehdr) + sizeof(Shdr);
+
+ ehdr.e_ehsize = sizeof(Ehdr);
+ ehdr.e_phentsize = sizeof(Phdr);
+ ehdr.e_shentsize = sizeof(Shdr);
+
+ // The ELF header only allows UINT16 for the number of program
+ // headers. In a core dump this equates to PT_NODE and PT_LOAD.
+ //
+ // When more program headers than 65534 the first section entry
+ // is used to store the actual program header count.
+
+ // PT_NOTE + number of memory regions
+ uint64_t phnum = 1 + m_crashInfo.MemoryRegions().size();
+
+ if (phnum < PH_HDR_CANARY) {
+ ehdr.e_phnum = phnum;
+ }
+ else {
+ ehdr.e_phnum = PH_HDR_CANARY;
+ }
+
+ if (!WriteData(&ehdr, sizeof(Ehdr))) {
+ return false;
+ }
+
+ size_t offset = sizeof(Ehdr) + sizeof(Shdr) + (phnum * sizeof(Phdr));
+ size_t filesz = GetProcessInfoSize() + GetAuxvInfoSize() + GetThreadInfoSize() + GetNTFileInfoSize();
+
+ // Add single section containing the actual count
+ // of the program headers to be written.
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_info = phnum;
+ // When section header offset is present but ehdr section num = 0
+ // then is is expected that the sh_size indicates the size of the
+ // section array or 1 in our case.
+ shdr.sh_size = 1;
+ if (!WriteData(&shdr, sizeof(shdr))) {
+ return false;
+ }
+
+ // PT_NOTE header
+ Phdr phdr;
+ memset(&phdr, 0, sizeof(Phdr));
+ phdr.p_type = PT_NOTE;
+ phdr.p_offset = offset;
+ phdr.p_filesz = filesz;
+
+ if (!WriteData(&phdr, sizeof(phdr))) {
+ return false;
+ }
+
+ // PT_NOTE sections must end on 4 byte boundary
+ // We output the NT_FILE, AUX and Thread entries
+ // AUX is aligned, NT_FILE is aligned and then we
+ // check to pad end of the thread list
+ phdr.p_type = PT_LOAD;
+ phdr.p_align = 4096;
+
+ size_t finalNoteAlignment = phdr.p_align - ((offset + filesz) % phdr.p_align);
+ if (finalNoteAlignment == phdr.p_align) {
+ finalNoteAlignment = 0;
+ }
+ offset += finalNoteAlignment;
+
+ TRACE("Writing memory region headers to core file\n");
+
+ // Write memory region note headers
+ for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions())
+ {
+ phdr.p_flags = memoryRegion.Permissions();
+ phdr.p_vaddr = memoryRegion.StartAddress();
+ phdr.p_memsz = memoryRegion.Size();
+
+ offset += filesz;
+ phdr.p_filesz = filesz = memoryRegion.Size();
+ phdr.p_offset = offset;
+
+ if (!WriteData(&phdr, sizeof(phdr))) {
+ return false;
+ }
+ }
+
+ // Write process info data to core file
+ if (!WriteProcessInfo()) {
+ return false;
+ }
+
+ // Write auxv data to core file
+ if (!WriteAuxv()) {
+ return false;
+ }
+
+ // Write NT_FILE entries to the core file
+ if (!WriteNTFileInfo()) {
+ return false;
+ }
+
+ TRACE("Writing %ld thread entries to core file\n", m_crashInfo.Threads().size());
+
+ // Write all the thread's state and registers
+ for (const ThreadInfo* thread : m_crashInfo.Threads())
+ {
+ if (!WriteThread(*thread, 0)) {
+ return false;
+ }
+ }
+
+ // Zero out the end of the PT_NOTE section to the boundary
+ // and then laydown the memory blocks
+ if (finalNoteAlignment > 0) {
+ assert(finalNoteAlignment < sizeof(m_tempBuffer));
+ memset(m_tempBuffer, 0, finalNoteAlignment);
+ if (!WriteData(m_tempBuffer, finalNoteAlignment)) {
+ return false;
+ }
+ }
+
+ TRACE("Writing %ld memory regions to core file\n", m_crashInfo.MemoryRegions().size());
+
+ // Read from target process and write memory regions to core
+ uint64_t total = 0;
+ for (const MemoryRegion& memoryRegion : m_crashInfo.MemoryRegions())
+ {
+ uint32_t size = memoryRegion.Size();
+ uint64_t address = memoryRegion.StartAddress();
+ total += size;
+
+ while (size > 0)
+ {
+ uint32_t bytesToRead = std::min(size, (uint32_t)sizeof(m_tempBuffer));
+ uint32_t read = 0;
+
+ if (FAILED(m_crashInfo.DataTarget()->ReadVirtual(address, m_tempBuffer, bytesToRead, &read))) {
+ fprintf(stderr, "ReadVirtual(%016lx, %08x) FAILED\n", address, bytesToRead);
+ return false;
+ }
+
+ if (!WriteData(m_tempBuffer, read)) {
+ return false;
+ }
+
+ address += read;
+ size -= read;
+ }
+ }
+
+ printf("Written %ld bytes (%ld pages) to core file\n", total, total >> PAGE_SHIFT);
+
+ return true;
+}
+
+bool
+DumpWriter::WriteProcessInfo()
+{
+ prpsinfo_t processInfo;
+ memset(&processInfo, 0, sizeof(processInfo));
+ processInfo.pr_sname = 'R';
+ processInfo.pr_pid = m_crashInfo.Pid();
+ processInfo.pr_ppid = m_crashInfo.Ppid();
+ processInfo.pr_pgrp = m_crashInfo.Tgid();
+ strcpy_s(processInfo.pr_fname, sizeof(processInfo.pr_fname), m_crashInfo.Name());
+
+ Nhdr nhdr;
+ memset(&nhdr, 0, sizeof(nhdr));
+ nhdr.n_namesz = 5;
+ nhdr.n_descsz = sizeof(prpsinfo_t);
+ nhdr.n_type = NT_PRPSINFO;
+
+ TRACE("Writing process information to core file\n");
+
+ // Write process info data to core file
+ if (!WriteData(&nhdr, sizeof(nhdr)) ||
+ !WriteData("CORE\0PRP", 8) ||
+ !WriteData(&processInfo, sizeof(prpsinfo_t))) {
+ return false;
+ }
+ return true;
+}
+
+bool
+DumpWriter::WriteAuxv()
+{
+ Nhdr nhdr;
+ memset(&nhdr, 0, sizeof(nhdr));
+ nhdr.n_namesz = 5;
+ nhdr.n_descsz = m_crashInfo.GetAuxvSize();
+ nhdr.n_type = NT_AUXV;
+
+ TRACE("Writing %ld auxv entries to core file\n", m_crashInfo.AuxvEntries().size());
+
+ if (!WriteData(&nhdr, sizeof(nhdr)) ||
+ !WriteData("CORE\0AUX", 8)) {
+ return false;
+ }
+ for (const auto& auxvEntry : m_crashInfo.AuxvEntries())
+ {
+ if (!WriteData(&auxvEntry, sizeof(auxvEntry))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+struct NTFileEntry
+{
+ uint64_t StartAddress;
+ uint64_t EndAddress;
+ uint64_t Offset;
+};
+
+// Calculate the NT_FILE entries total size
+size_t
+DumpWriter::GetNTFileInfoSize(size_t* alignmentBytes)
+{
+ size_t count = m_crashInfo.ModuleMappings().size();
+ size_t size = 0;
+
+ // Header, CORE, entry count, page size
+ size = sizeof(Nhdr) + sizeof(NTFileEntry);
+
+ // start_address, end_address, offset
+ size += count * sizeof(NTFileEntry);
+
+ // \0 terminator for each filename
+ size += count;
+
+ // File name storage needed
+ for (const MemoryRegion& image : m_crashInfo.ModuleMappings()) {
+ size += strlen(image.FileName());
+ }
+ // Notes must end on 4 byte alignment
+ size_t alignmentBytesNeeded = 4 - (size % 4);
+ size += alignmentBytesNeeded;
+
+ if (alignmentBytes != nullptr) {
+ *alignmentBytes = alignmentBytesNeeded;
+ }
+ return size;
+}
+
+// Write NT_FILE entries to the PT_NODE section
+//
+// Nhdr (NT_FILE)
+// Total entries
+// Page size
+// [0] start_address end_address offset
+// [1] start_address end_address offset
+// [file name]\0[file name]\0...
+bool
+DumpWriter::WriteNTFileInfo()
+{
+ Nhdr nhdr;
+ memset(&nhdr, 0, sizeof(nhdr));
+
+ // CORE + \0 and we align on 4 byte boundary
+ // so we can use CORE\0FIL for easier hex debugging
+ nhdr.n_namesz = 5;
+ nhdr.n_type = NT_FILE; // "FILE"
+
+ // Size of payload for NT_FILE after CORE tag written
+ size_t alignmentBytesNeeded = 0;
+ nhdr.n_descsz = GetNTFileInfoSize(&alignmentBytesNeeded) - sizeof(nhdr) - 8;
+
+ size_t count = m_crashInfo.ModuleMappings().size();
+ size_t pageSize = PAGE_SIZE;
+
+ TRACE("Writing %ld NT_FILE entries to core file\n", m_crashInfo.ModuleMappings().size());
+
+ if (!WriteData(&nhdr, sizeof(nhdr)) ||
+ !WriteData("CORE\0FIL", 8) ||
+ !WriteData(&count, 8) ||
+ !WriteData(&pageSize, 8)) {
+ return false;
+ }
+
+ for (const MemoryRegion& image : m_crashInfo.ModuleMappings())
+ {
+ struct NTFileEntry entry { image.StartAddress(), image.EndAddress(), image.Offset() };
+ if (!WriteData(&entry, sizeof(entry))) {
+ return false;
+ }
+ }
+
+ for (const MemoryRegion& image : m_crashInfo.ModuleMappings())
+ {
+ if (!WriteData(image.FileName(), strlen(image.FileName())) ||
+ !WriteData("\0", 1)) {
+ return false;
+ }
+ }
+
+ // Has to end on a 4 byte boundary. Debugger, readelf and such
+ // will automatically align on next 4 bytes and look for a PT_NOTE
+ // header.
+ if (alignmentBytesNeeded) {
+ if (!WriteData("\0\0\0\0", alignmentBytesNeeded)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+DumpWriter::WriteThread(const ThreadInfo& thread, int fatal_signal)
+{
+ prstatus_t pr;
+ memset(&pr, 0, sizeof(pr));
+
+ pr.pr_info.si_signo = fatal_signal;
+ pr.pr_cursig = fatal_signal;
+ pr.pr_pid = thread.Tid();
+ pr.pr_ppid = thread.Ppid();
+ pr.pr_pgrp = thread.Tgid();
+ memcpy(&pr.pr_reg, thread.GPRegisters(), sizeof(user_regs_struct));
+
+ Nhdr nhdr;
+ memset(&nhdr, 0, sizeof(nhdr));
+
+ // Name size is CORE plus the NULL terminator
+ // The format requires 4 byte alignment so the
+ // value written in 8 bytes. Stuff the last 3
+ // bytes with the type of NT_PRSTATUS so it is
+ // easier to debug in a hex editor.
+ nhdr.n_namesz = 5;
+ nhdr.n_descsz = sizeof(prstatus_t);
+ nhdr.n_type = NT_PRSTATUS;
+ if (!WriteData(&nhdr, sizeof(nhdr)) ||
+ !WriteData("CORE\0THR", 8) ||
+ !WriteData(&pr, sizeof(prstatus_t))) {
+ return false;
+ }
+
+#if defined(__i386__) || defined(__x86_64__)
+ nhdr.n_descsz = sizeof(user_fpregs_struct);
+ nhdr.n_type = NT_FPREGSET;
+ if (!WriteData(&nhdr, sizeof(nhdr)) ||
+ !WriteData("CORE\0FLT", 8) ||
+ !WriteData(thread.FPRegisters(), sizeof(user_fpregs_struct))) {
+ return false;
+ }
+#endif
+
+#if defined(__i386__)
+ nhdr.n_descsz = sizeof(user_fpxregs_struct);
+ nhdr.n_type = NT_PRXFPREG;
+ if (!WriteData(&nhdr, sizeof(nhdr)) ||
+ !WriteData("LINUX\0\0\0", 8) ||
+ !WriteData(&thread.FPXRegisters(), sizeof(user_fpxregs_struct))) {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+// Write all of the given buffer, handling short writes and EINTR. Return true iff successful.
+bool
+DumpWriter::WriteData(const void* buffer, size_t length)
+{
+ const uint8_t* data = (const uint8_t*)buffer;
+
+ size_t done = 0;
+ while (done < length) {
+ ssize_t written;
+ do {
+ written = write(m_fd, data + done, length - done);
+ } while (written == -1 && errno == EINTR);
+
+ if (written < 1) {
+ fprintf(stderr, "WriteData FAILED %s\n", strerror(errno));
+ return false;
+ }
+ done += written;
+ }
+ return true;
+}
diff --git a/src/debug/createdump/dumpwriter.h b/src/debug/createdump/dumpwriter.h
new file mode 100644
index 0000000000..0b4f88cbd6
--- /dev/null
+++ b/src/debug/createdump/dumpwriter.h
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifdef BIT64
+#define ELF_CLASS ELFCLASS64
+#else
+#define ELF_CLASS ELFCLASS32
+#endif
+
+#define Ehdr ElfW(Ehdr)
+#define Phdr ElfW(Phdr)
+#define Shdr ElfW(Shdr)
+#define Nhdr ElfW(Nhdr)
+#define auxv_t ElfW(auxv_t)
+
+#if defined(__x86_64__)
+#define ELF_ARCH EM_X86_64
+#elif defined(__i386__)
+#define ELF_ARCH EM_386
+#elif defined(__arm__)
+#define ELF_ARCH EM_ARM
+#endif
+
+#define PH_HDR_CANARY 0xFFFF
+
+#ifndef NT_FILE
+#define NT_FILE 0x46494c45
+#endif
+
+class DumpWriter : IUnknown
+{
+private:
+ LONG m_ref; // reference count
+ int m_fd;
+ CrashInfo& m_crashInfo;
+ BYTE m_tempBuffer[0x4000];
+
+public:
+ DumpWriter(CrashInfo& crashInfo);
+ virtual ~DumpWriter();
+ bool OpenDump(const char* dumpFileName);
+ bool WriteDump();
+
+ // IUnknown
+ STDMETHOD(QueryInterface)(___in REFIID InterfaceId, ___out PVOID* Interface);
+ STDMETHOD_(ULONG, AddRef)();
+ STDMETHOD_(ULONG, Release)();
+
+private:
+ bool WriteProcessInfo();
+ bool WriteAuxv();
+ size_t GetNTFileInfoSize(size_t* alignmentBytes = nullptr);
+ bool WriteNTFileInfo();
+ bool WriteThread(const ThreadInfo& thread, int fatal_signal);
+ bool WriteData(const void* buffer, size_t length);
+
+ const size_t GetProcessInfoSize() const { return sizeof(Nhdr) + 8 + sizeof(prpsinfo_t); }
+ const size_t GetAuxvInfoSize() const { return sizeof(Nhdr) + 8 + m_crashInfo.GetAuxvSize(); }
+ const size_t GetThreadInfoSize() const
+ {
+ return m_crashInfo.Threads().size() * ((sizeof(Nhdr) + 8 + sizeof(prstatus_t))
+#if defined(__i386__) || defined(__x86_64__)
+ + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct)
+#endif
+#if defined(__i386__)
+ + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)
+#endif
+ );
+ }
+};
+
+static inline int sex()
+{
+ int probe = 1;
+ return !*(char *)&probe;
+}
diff --git a/src/debug/createdump/main.cpp b/src/debug/createdump/main.cpp
new file mode 100644
index 0000000000..03382779a5
--- /dev/null
+++ b/src/debug/createdump/main.cpp
@@ -0,0 +1,99 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "createdump.h"
+
+const char* g_help = "createdump [options] pid\n"
+"-f, --name - dump path and file name. The pid can be placed in the name with %d. The default is '/tmp/coredump.%d'\n"
+"-n, --normal - create minidump.\n"
+"-h, --withheap - create minidump with heap (default).\n"
+"-t, --triage - create triage minidump.\n"
+"-d, --diag - enable diagnostic messages.\n";
+
+bool CreateDumpCommon(const char* programPath, const char* dumpPathTemplate, MINIDUMP_TYPE minidumpType, CrashInfo* crashInfo);
+
+//
+// Main entry point
+//
+int __cdecl main(const int argc, const char* argv[])
+{
+ MINIDUMP_TYPE minidumpType = MiniDumpWithPrivateReadWriteMemory;
+#ifdef ANDROID
+ const char* dumpPathTemplate = "/data/local/tmp/coredump.%d";
+#else
+ const char* dumpPathTemplate = "/tmp/coredump.%d";
+#endif
+ pid_t pid = 0;
+
+ int exitCode = PAL_InitializeDLL();
+ if (exitCode != 0)
+ {
+ fprintf(stderr, "PAL initialization FAILED %d\n", exitCode);
+ return exitCode;
+ }
+
+ // Parse off the program name leaving just the path. Used to locate/load the DAC module.
+ std::string programPath;
+ programPath.append(*argv++);
+ size_t last = programPath.find_last_of('/');
+ programPath = programPath.substr(0, last);
+
+ // Parse the command line options and target pid
+ for (int i = 1; i < argc; i++)
+ {
+ if (*argv != nullptr)
+ {
+ if ((strcmp(*argv, "-f") == 0) || (strcmp(*argv, "--name") == 0))
+ {
+ dumpPathTemplate = *++argv;
+ }
+ else if ((strcmp(*argv, "-n") == 0) || (strcmp(*argv, "--normal") == 0))
+ {
+ minidumpType = MiniDumpNormal;
+ }
+ else if ((strcmp(*argv, "-h") == 0) || (strcmp(*argv, "--withheap") == 0))
+ {
+ minidumpType = MiniDumpWithPrivateReadWriteMemory;
+ }
+ else if ((strcmp(*argv, "-t") == 0) || (strcmp(*argv, "--triage") == 0))
+ {
+ minidumpType = MiniDumpFilterTriage;
+ }
+ else if ((strcmp(*argv, "-d") == 0) || (strcmp(*argv, "--diag") == 0))
+ {
+ g_diagnostics = true;
+ }
+ else {
+ pid = atoll(*argv);
+ }
+ argv++;
+ }
+ }
+ if (pid != 0)
+ {
+ ReleaseHolder<DumpDataTarget> dataTarget = new DumpDataTarget(pid);
+ ReleaseHolder<CrashInfo> crashInfo = new CrashInfo(pid, dataTarget, false);
+
+ // The initialize the data target's ReadVirtual support (opens /proc/$pid/mem)
+ if (dataTarget->Initialize(crashInfo))
+ {
+ if (!CreateDumpCommon(programPath.c_str(), dumpPathTemplate, minidumpType, crashInfo))
+ {
+ exitCode = -1;
+ }
+ }
+ else
+ {
+ exitCode = -1;
+ }
+ }
+ else
+ {
+ // if no pid or invalid command line option
+ fprintf(stderr, "%s", g_help);
+ exitCode = -1;
+ }
+ PAL_TerminateEx(exitCode);
+ return exitCode;
+}
diff --git a/src/debug/createdump/memoryregion.h b/src/debug/createdump/memoryregion.h
new file mode 100644
index 0000000000..16c4d1c693
--- /dev/null
+++ b/src/debug/createdump/memoryregion.h
@@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+struct MemoryRegion
+{
+private:
+ uint32_t m_permissions;
+ uint64_t m_startAddress;
+ uint64_t m_endAddress;
+ uint64_t m_offset;
+
+ // The name used for NT_FILE output
+ char* m_fileName;
+
+public:
+ MemoryRegion(uint64_t start, uint64_t end) :
+ m_permissions(PF_R | PF_W | PF_X),
+ m_startAddress(start),
+ m_endAddress(end),
+ m_offset(0),
+ m_fileName(nullptr)
+ {
+ assert((start & ~PAGE_MASK) == 0);
+ assert((end & ~PAGE_MASK) == 0);
+ }
+
+ MemoryRegion(uint32_t permissions, uint64_t start, uint64_t end, uint64_t offset, char* filename) :
+ m_permissions(permissions),
+ m_startAddress(start),
+ m_endAddress(end),
+ m_offset(offset),
+ m_fileName(filename)
+ {
+ assert((start & ~PAGE_MASK) == 0);
+ assert((end & ~PAGE_MASK) == 0);
+ }
+
+ const uint32_t Permissions() const
+ {
+ return m_permissions;
+ }
+
+ const uint64_t StartAddress() const
+ {
+ return m_startAddress;
+ }
+
+ const uint64_t EndAddress() const
+ {
+ return m_endAddress;
+ }
+
+ const uint64_t Size() const
+ {
+ return m_endAddress - m_startAddress;
+ }
+
+ const uint64_t Offset() const
+ {
+ return m_offset;
+ }
+
+ const char* FileName() const
+ {
+ return m_fileName;
+ }
+
+ bool operator<(const MemoryRegion& rhs) const
+ {
+ return (m_startAddress < rhs.m_startAddress) && (m_endAddress <= rhs.m_startAddress);
+ }
+
+ bool Contains(const MemoryRegion& rhs) const
+ {
+ return (m_startAddress <= rhs.m_startAddress) && (m_endAddress >= rhs.m_endAddress);
+ }
+
+ void Cleanup()
+ {
+ if (m_fileName != nullptr)
+ {
+ free(m_fileName);
+ m_fileName = nullptr;
+ }
+ }
+
+ void Print() const
+ {
+ if (m_fileName != nullptr) {
+ TRACE("%016lx - %016lx (%04ld) %016lx %x %s\n", m_startAddress, m_endAddress, (Size() >> PAGE_SHIFT), m_offset, m_permissions, m_fileName);
+ }
+ else {
+ TRACE("%016lx - %016lx (%04ld) %02x\n", m_startAddress, m_endAddress, (Size() >> PAGE_SHIFT), m_permissions);
+ }
+ }
+};
diff --git a/src/debug/createdump/threadinfo.cpp b/src/debug/createdump/threadinfo.cpp
new file mode 100644
index 0000000000..8e73fcf2cb
--- /dev/null
+++ b/src/debug/createdump/threadinfo.cpp
@@ -0,0 +1,235 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "createdump.h"
+
+#define FPREG_ErrorOffset(fpregs) *(DWORD*)&((fpregs).rip)
+#define FPREG_ErrorSelector(fpregs) *(((WORD*)&((fpregs).rip)) + 2)
+#define FPREG_DataOffset(fpregs) *(DWORD*)&((fpregs).rdp)
+#define FPREG_DataSelector(fpregs) *(((WORD*)&((fpregs).rdp)) + 2)
+
+ThreadInfo::ThreadInfo(pid_t tid) :
+ m_tid(tid)
+{
+}
+
+ThreadInfo::~ThreadInfo()
+{
+}
+
+bool
+ThreadInfo::Initialize(ICLRDataTarget* dataTarget)
+{
+ if (!CrashInfo::GetStatus(m_tid, &m_ppid, &m_tgid, nullptr))
+ {
+ return false;
+ }
+ if (dataTarget != nullptr)
+ {
+ if (!GetRegistersWithDataTarget(dataTarget))
+ {
+ return false;
+ }
+ }
+ else {
+ if (!GetRegistersWithPTrace())
+ {
+ return false;
+ }
+ }
+ TRACE("Thread %04x RIP %016llx RSP %016llx\n", m_tid, m_gpRegisters.rip, m_gpRegisters.rsp);
+ return true;
+}
+
+void
+ThreadInfo::ResumeThread()
+{
+ if (ptrace(PTRACE_DETACH, m_tid, nullptr, nullptr) != -1)
+ {
+ int waitStatus;
+ waitpid(m_tid, &waitStatus, __WALL);
+ }
+}
+
+bool
+ThreadInfo::GetRegistersWithPTrace()
+{
+ if (ptrace((__ptrace_request)PTRACE_GETREGS, m_tid, nullptr, &m_gpRegisters) == -1)
+ {
+ fprintf(stderr, "ptrace(GETREGS, %d) FAILED %d (%s)\n", m_tid, errno, strerror(errno));
+ return false;
+ }
+ if (ptrace((__ptrace_request)PTRACE_GETFPREGS, m_tid, nullptr, &m_fpRegisters) == -1)
+ {
+ fprintf(stderr, "ptrace(GETFPREGS, %d) FAILED %d (%s)\n", m_tid, errno, strerror(errno));
+ return false;
+ }
+#if defined(__i386__)
+ if (ptrace((__ptrace_request)PTRACE_GETFPXREGS, m_tid, nullptr, &m_fpxRegisters) == -1)
+ {
+ fprintf(stderr, "ptrace(GETFPXREGS, %d) FAILED %d (%s)\n", m_tid, errno, strerror(errno));
+ return false;
+ }
+#endif
+ return true;
+}
+
+bool
+ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* dataTarget)
+{
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_ALL;
+ if (dataTarget->GetThreadContext(m_tid, context.ContextFlags, sizeof(context), reinterpret_cast<PBYTE>(&context)) != S_OK)
+ {
+ return false;
+ }
+#if defined(__x86_64__)
+ m_gpRegisters.rbp = context.Rbp;
+ m_gpRegisters.rip = context.Rip;
+ m_gpRegisters.cs = context.SegCs;
+ m_gpRegisters.eflags = context.EFlags;
+ m_gpRegisters.ss = context.SegSs;
+ m_gpRegisters.rsp = context.Rsp;
+ m_gpRegisters.rdi = context.Rdi;
+
+ m_gpRegisters.rsi = context.Rsi;
+ m_gpRegisters.rbx = context.Rbx;
+ m_gpRegisters.rdx = context.Rdx;
+ m_gpRegisters.rcx = context.Rcx;
+ m_gpRegisters.rax = context.Rax;
+ m_gpRegisters.orig_rax = context.Rax;
+ m_gpRegisters.r8 = context.R8;
+ m_gpRegisters.r9 = context.R9;
+ m_gpRegisters.r10 = context.R10;
+ m_gpRegisters.r11 = context.R11;
+ m_gpRegisters.r12 = context.R12;
+ m_gpRegisters.r13 = context.R13;
+ m_gpRegisters.r14 = context.R14;
+ m_gpRegisters.r15 = context.R15;
+
+ m_gpRegisters.ds = context.SegDs;
+ m_gpRegisters.es = context.SegEs;
+ m_gpRegisters.fs = context.SegFs;
+ m_gpRegisters.gs = context.SegGs;
+ m_gpRegisters.fs_base = 0;
+ m_gpRegisters.gs_base = 0;
+
+ m_fpRegisters.cwd = context.FltSave.ControlWord;
+ m_fpRegisters.swd = context.FltSave.StatusWord;
+ m_fpRegisters.ftw = context.FltSave.TagWord;
+ m_fpRegisters.fop = context.FltSave.ErrorOpcode;
+
+ FPREG_ErrorOffset(m_fpRegisters) = context.FltSave.ErrorOffset;
+ FPREG_ErrorSelector(m_fpRegisters) = context.FltSave.ErrorSelector;
+ FPREG_DataOffset(m_fpRegisters) = context.FltSave.DataOffset;
+ FPREG_DataSelector(m_fpRegisters) = context.FltSave.DataSelector;
+
+ m_fpRegisters.mxcsr = context.FltSave.MxCsr;
+ m_fpRegisters.mxcr_mask = context.FltSave.MxCsr_Mask;
+
+ assert(sizeof(context.FltSave.FloatRegisters) == sizeof(m_fpRegisters.st_space));
+ memcpy(m_fpRegisters.st_space, context.FltSave.FloatRegisters, sizeof(m_fpRegisters.st_space));
+
+ assert(sizeof(context.FltSave.XmmRegisters) == sizeof(m_fpRegisters.xmm_space));
+ memcpy(m_fpRegisters.xmm_space, context.FltSave.XmmRegisters, sizeof(m_fpRegisters.xmm_space));
+#else
+#error Platform not supported
+#endif
+ return true;
+}
+
+void
+ThreadInfo::GetThreadStack(const CrashInfo& crashInfo, uint64_t* startAddress, size_t* size) const
+{
+ *startAddress = m_gpRegisters.rsp & PAGE_MASK;
+ *size = 4 * PAGE_SIZE;
+
+ for (const MemoryRegion& mapping : crashInfo.OtherMappings())
+ {
+ if (*startAddress >= mapping.StartAddress() && *startAddress < mapping.EndAddress())
+ {
+ // Use the mapping found for the size of the thread's stack
+ *size = mapping.EndAddress() - *startAddress;
+
+ if (g_diagnostics)
+ {
+ TRACE("Thread %04x stack found in other mapping (size %08lx): ", m_tid, *size);
+ mapping.Print();
+ }
+ break;
+ }
+ }
+}
+
+void
+ThreadInfo::GetThreadCode(uint64_t* startAddress, size_t* size) const
+{
+ *startAddress = m_gpRegisters.rip & PAGE_MASK;
+ *size = PAGE_SIZE;
+}
+
+void
+ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const
+{
+ context->ContextFlags = flags;
+#if defined(__x86_64__)
+ if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
+ {
+ context->Rbp = m_gpRegisters.rbp;
+ context->Rip = m_gpRegisters.rip;
+ context->SegCs = m_gpRegisters.cs;
+ context->EFlags = m_gpRegisters.eflags;
+ context->SegSs = m_gpRegisters.ss;
+ context->Rsp = m_gpRegisters.rsp;
+ }
+ if ((flags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
+ {
+ context->Rdi = m_gpRegisters.rdi;
+ context->Rsi = m_gpRegisters.rsi;
+ context->Rbx = m_gpRegisters.rbx;
+ context->Rdx = m_gpRegisters.rdx;
+ context->Rcx = m_gpRegisters.rcx;
+ context->Rax = m_gpRegisters.rax;
+ context->R8 = m_gpRegisters.r8;
+ context->R9 = m_gpRegisters.r9;
+ context->R10 = m_gpRegisters.r10;
+ context->R11 = m_gpRegisters.r11;
+ context->R12 = m_gpRegisters.r12;
+ context->R13 = m_gpRegisters.r13;
+ context->R14 = m_gpRegisters.r14;
+ context->R15 = m_gpRegisters.r15;
+ }
+ if ((flags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS)
+ {
+ context->SegDs = m_gpRegisters.ds;
+ context->SegEs = m_gpRegisters.es;
+ context->SegFs = m_gpRegisters.fs;
+ context->SegGs = m_gpRegisters.gs;
+ }
+ if ((flags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT)
+ {
+ context->FltSave.ControlWord = m_fpRegisters.cwd;
+ context->FltSave.StatusWord = m_fpRegisters.swd;
+ context->FltSave.TagWord = m_fpRegisters.ftw;
+ context->FltSave.ErrorOpcode = m_fpRegisters.fop;
+
+ context->FltSave.ErrorOffset = FPREG_ErrorOffset(m_fpRegisters);
+ context->FltSave.ErrorSelector = FPREG_ErrorSelector(m_fpRegisters);
+ context->FltSave.DataOffset = FPREG_DataOffset(m_fpRegisters);
+ context->FltSave.DataSelector = FPREG_DataSelector(m_fpRegisters);
+
+ context->FltSave.MxCsr = m_fpRegisters.mxcsr;
+ context->FltSave.MxCsr_Mask = m_fpRegisters.mxcr_mask;
+
+ assert(sizeof(context->FltSave.FloatRegisters) == sizeof(m_fpRegisters.st_space));
+ memcpy(context->FltSave.FloatRegisters, m_fpRegisters.st_space, sizeof(context->FltSave.FloatRegisters));
+
+ assert(sizeof(context->FltSave.XmmRegisters) == sizeof(m_fpRegisters.xmm_space));
+ memcpy(context->FltSave.XmmRegisters, m_fpRegisters.xmm_space, sizeof(context->FltSave.XmmRegisters));
+ }
+ // TODO: debug registers?
+#else
+#error Platform not supported
+#endif
+}
diff --git a/src/debug/createdump/threadinfo.h b/src/debug/createdump/threadinfo.h
new file mode 100644
index 0000000000..8620219747
--- /dev/null
+++ b/src/debug/createdump/threadinfo.h
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+class CrashInfo;
+
+class ThreadInfo
+{
+private:
+ pid_t m_tid; // thread id
+ pid_t m_ppid; // parent process
+ pid_t m_tgid; // thread group
+ struct user_regs_struct m_gpRegisters; // general purpose registers
+ struct user_fpregs_struct m_fpRegisters; // floating point registers
+#if defined(__i386__)
+ struct user_fpxregs_struct m_fpxRegisters; // x86 floating point registers
+#endif
+
+public:
+ ThreadInfo(pid_t tid);
+ ~ThreadInfo();
+ bool Initialize(ICLRDataTarget* dataTarget);
+ void ResumeThread();
+ void GetThreadStack(const CrashInfo& crashInfo, uint64_t* startAddress, size_t* size) const;
+ void GetThreadCode(uint64_t* startAddress, size_t* size) const;
+ 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; }
+
+ const user_regs_struct* GPRegisters() const { return &m_gpRegisters; }
+ const user_fpregs_struct* FPRegisters() const { return &m_fpRegisters; }
+#if defined(__i386__)
+ const user_fpxregs_struct* FPXRegisters() const { return &m_fpxRegisters; }
+#endif
+
+private:
+ bool GetRegistersWithPTrace();
+ bool GetRegistersWithDataTarget(ICLRDataTarget* dataTarget);
+};
+
diff --git a/src/debug/daccess/dacfn.cpp b/src/debug/daccess/dacfn.cpp
index 0a167418a1..2f7a98de1a 100644
--- a/src/debug/daccess/dacfn.cpp
+++ b/src/debug/daccess/dacfn.cpp
@@ -1386,6 +1386,8 @@ bool DacTargetConsistencyAssertsEnabled()
//
void DacEnumCodeForStackwalk(TADDR taCallEnd)
{
+ if (taCallEnd == 0)
+ return;
//
// x86 stack walkers often end up having to guess
// about what's a return address on the stack.
diff --git a/src/debug/daccess/enummem.cpp b/src/debug/daccess/enummem.cpp
index 027fe59543..9305bba488 100644
--- a/src/debug/daccess/enummem.cpp
+++ b/src/debug/daccess/enummem.cpp
@@ -22,6 +22,10 @@
#include "binder.h"
#include "win32threadpool.h"
+#ifdef FEATURE_PAL
+#include <dactablerva.h>
+#endif
+
#ifdef FEATURE_APPX
#include "appxutil.h"
#endif // FEATURE_APPX
@@ -220,6 +224,11 @@ HRESULT ClrDataAccess::EnumMemCLRStatic(IN CLRDataEnumMemoryFlags flags)
#define DEFINE_DACVAR_SVR(id_type, size_type, id, var) \
ReportMem(m_globalBase + g_dacGlobals.id, sizeof(size_type));
+#ifdef FEATURE_PAL
+ // Add the dac table memory in coreclr
+ CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED ( ReportMem(m_globalBase + DAC_TABLE_RVA, sizeof(g_dacGlobals)); )
+#endif
+
// Cannot use CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED
// around conditional preprocessor directives in a sane fashion.
EX_TRY
@@ -233,39 +242,33 @@ HRESULT ClrDataAccess::EnumMemCLRStatic(IN CLRDataEnumMemoryFlags flags)
}
EX_END_CATCH(RethrowCancelExceptions)
- CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED
- (
- // StressLog is not defined on Rotor build for DAC
- ReportMem(m_globalBase + g_dacGlobals.dac__g_pStressLog, sizeof(StressLog *));
- );
+ // StressLog is not defined on Rotor build for DAC
+ CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED ( ReportMem(m_globalBase + g_dacGlobals.dac__g_pStressLog, sizeof(StressLog *)); )
EX_TRY
{
// These two static pointers are pointed to static data of byte[]
// then run constructor in place
//
- ReportMem(m_globalBase + g_dacGlobals.SystemDomain__m_pSystemDomain,
- sizeof(SystemDomain));
- ReportMem(m_globalBase + g_dacGlobals.SharedDomain__m_pSharedDomain,
- sizeof(SharedDomain));
+ ReportMem(m_globalBase + g_dacGlobals.SystemDomain__m_pSystemDomain, sizeof(SystemDomain));
+ ReportMem(m_globalBase + g_dacGlobals.SharedDomain__m_pSharedDomain, sizeof(SharedDomain));
// We need IGCHeap pointer to make EEVersion work
- ReportMem(m_globalBase + g_dacGlobals.dac__g_pGCHeap,
- sizeof(IGCHeap *));
+ ReportMem(m_globalBase + g_dacGlobals.dac__g_pGCHeap, sizeof(IGCHeap *));
// see synblk.cpp, the pointer is pointed to a static byte[]
SyncBlockCache::s_pSyncBlockCache.EnumMem();
#ifndef FEATURE_IMPLICIT_TLS
- ReportMem(m_globalBase + g_dacGlobals.dac__gThreadTLSIndex,
- sizeof(DWORD));
- ReportMem(m_globalBase + g_dacGlobals.dac__gAppDomainTLSIndex,
- sizeof(DWORD));
+ ReportMem(m_globalBase + g_dacGlobals.dac__gThreadTLSIndex, sizeof(DWORD));
+ ReportMem(m_globalBase + g_dacGlobals.dac__gAppDomainTLSIndex, sizeof(DWORD));
#endif
- ReportMem( m_globalBase + g_dacGlobals.dac__g_FCDynamicallyAssignedImplementations,
+ ReportMem(m_globalBase + g_dacGlobals.dac__g_FCDynamicallyAssignedImplementations,
sizeof(TADDR)*ECall::NUM_DYNAMICALLY_ASSIGNED_FCALL_IMPLEMENTATIONS);
+ ReportMem(g_gcDacGlobals.GetAddr(), sizeof(GcDacVars));
+
// We need all of the dac variables referenced by the GC DAC global struct.
// This struct contains pointers to pointers, so we first dereference the pointers
// to obtain the location of the variable that's reported.
@@ -316,11 +319,8 @@ HRESULT ClrDataAccess::EnumMemCLRStatic(IN CLRDataEnumMemoryFlags flags)
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( StubManager::EnumMemoryRegions(flags); )
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pFinalizerThread.EnumMem(); )
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pSuspensionThread.EnumMem(); )
-
- CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED
- (
- g_heap_type.EnumMem();
- );
+
+ CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_heap_type.EnumMem(); )
m_dumpStats.m_cbClrStatics = m_cbMemoryReported - cbMemoryReported;
@@ -345,8 +345,6 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWorkerHeap(IN CLRDataEnumMemoryFlags fla
HRESULT status = S_OK;
- m_instances.ClearEnumMemMarker();
-
// clear all of the previous cached memory
Flush();
@@ -365,7 +363,6 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWorkerHeap(IN CLRDataEnumMemoryFlags fla
// would be true, but I don't think we have that guarantee here.
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(flags); );
-#ifdef FEATURE_LAZY_COW_PAGES
// Iterating to all threads' stacks, as we have to collect data stored inside (core)clr.dll
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(flags); )
@@ -377,11 +374,11 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWorkerHeap(IN CLRDataEnumMemoryFlags fla
// now dump the memory get dragged in by using DAC API implicitly.
m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb);
-#endif // FEATURE_LAZY_COW_PAGES
-
- // end of code
status = m_memStatus;
+ // Do not let any remaining implicitly enumerated memory leak out.
+ Flush();
+
return S_OK;
} // EnumMemoryRegionsWorkerHeap
@@ -976,16 +973,19 @@ HRESULT ClrDataAccess::EnumMemWalkStackHelper(CLRDataEnumMemoryFlags flags,
{
EECodeInfo codeInfo(addr);
- // We want IsFilterFunclet to work for anything on the stack
- codeInfo.GetJitManager()->IsFilterFunclet(&codeInfo);
-
- // The stackwalker needs GC info to find the parent 'stack pointer' or PSP
- GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
- PTR_BYTE pGCInfo = dac_cast<PTR_BYTE>(gcInfoToken.Info);
- if (pGCInfo != NULL)
+ if (codeInfo.IsValid())
{
- GcInfoDecoder gcDecoder(gcInfoToken, DECODE_PSP_SYM, 0);
- DacEnumMemoryRegion(dac_cast<TADDR>(pGCInfo), gcDecoder.GetNumBytesRead(), true);
+ // We want IsFilterFunclet to work for anything on the stack
+ codeInfo.GetJitManager()->IsFilterFunclet(&codeInfo);
+
+ // The stackwalker needs GC info to find the parent 'stack pointer' or PSP
+ GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
+ PTR_BYTE pGCInfo = dac_cast<PTR_BYTE>(gcInfoToken.Info);
+ if (pGCInfo != NULL)
+ {
+ GcInfoDecoder gcDecoder(gcInfoToken, DECODE_PSP_SYM, 0);
+ DacEnumMemoryRegion(dac_cast<TADDR>(pGCInfo), gcDecoder.GetNumBytesRead(), true);
+ }
}
}
#endif // WIN64EXCEPTIONS && USE_GC_INFO_DECODER
@@ -1603,10 +1603,6 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWorkerSkinny(IN CLRDataEnumMemoryFlags f
// collect CLR static
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); )
- // now dump the memory get dragged in by using DAC API implicitly.
- m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb);
- status = m_memStatus;
-
// Dump AppDomain-specific info needed for MiniDumpNormal.
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); )
@@ -1618,6 +1614,10 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWorkerSkinny(IN CLRDataEnumMemoryFlags f
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumStreams(flags); )
#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
+ // now dump the memory get dragged in by using DAC API implicitly.
+ m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb);
+ status = m_memStatus;
+
// Do not let any remaining implicitly enumerated memory leak out.
Flush();
@@ -1654,10 +1654,6 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWorkerMicroTriage(IN CLRDataEnumMemoryFl
// collect CLR static
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); )
- // now dump the memory get dragged in by using DAC API implicitly.
- m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb);
- status = m_memStatus;
-
// Dump AppDomain-specific info needed for triage dumps methods enumeration (k command).
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); )
@@ -1669,6 +1665,10 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWorkerMicroTriage(IN CLRDataEnumMemoryFl
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumStreams(flags); )
#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
+ // now dump the memory get dragged in by using DAC API implicitly.
+ m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb);
+ status = m_memStatus;
+
// Do not let any remaining implicitly enumerated memory leak out.
Flush();
@@ -1847,17 +1847,17 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWrapper(IN CLRDataEnumMemoryFlags flags)
// The various EnumMemoryRegions() implementations should understand
// CLRDATA_ENUM_MEM_MINI to mean that the bare minimimum memory
// to make a MiniDumpNormal work should be included.
- if ( flags == CLRDATA_ENUM_MEM_MINI)
+ if (flags == CLRDATA_ENUM_MEM_MINI)
{
// skinny mini-dump
status = EnumMemoryRegionsWorkerSkinny(flags);
}
- else if ( flags == CLRDATA_ENUM_MEM_TRIAGE)
+ else if (flags == CLRDATA_ENUM_MEM_TRIAGE)
{
// triage micro-dump
status = EnumMemoryRegionsWorkerMicroTriage(flags);
}
- else if ( flags == CLRDATA_ENUM_MEM_HEAP)
+ else if (flags == CLRDATA_ENUM_MEM_HEAP)
{
status = EnumMemoryRegionsWorkerHeap(flags);
}
@@ -1874,12 +1874,6 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWrapper(IN CLRDataEnumMemoryFlags flags)
return status;
}
-#define MiniDumpWithPrivateReadWriteMemory 0x00000200
-#define MiniDumpWithFullAuxiliaryState 0x00008000
-#define MiniDumpFilterTriage 0x00100000
-
-
-
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Entry function for generating CLR aware dump. This function is called
@@ -1972,6 +1966,7 @@ ClrDataAccess::EnumMemoryRegions(IN ICLRDataEnumMemoryRegionsCallback* callback,
status = EnumMemoryRegionsWrapper(CLRDATA_ENUM_MEM_MINI);
}
+#ifndef FEATURE_PAL
// For all dump types, we need to capture the chain to the IMAGE_DIRECTORY_ENTRY_DEBUG
// contents, so that DAC can validate against the TimeDateStamp even if the
// debugger can't find the main CLR module on disk.
@@ -1986,7 +1981,7 @@ ClrDataAccess::EnumMemoryRegions(IN ICLRDataEnumMemoryRegionsCallback* callback,
m_instances.DumpAllInstances(m_enumMemCb);
}
}
-
+#endif
Flush();
}
EX_CATCH
diff --git a/src/debug/daccess/request_svr.cpp b/src/debug/daccess/request_svr.cpp
index 6a1de35ff9..40e3600f9f 100644
--- a/src/debug/daccess/request_svr.cpp
+++ b/src/debug/daccess/request_svr.cpp
@@ -22,6 +22,8 @@
int GCHeapCount()
{
+ if (g_gcDacGlobals->n_heaps == nullptr)
+ return 0;
return *g_gcDacGlobals->n_heaps;
}
@@ -206,14 +208,19 @@ void
ClrDataAccess::EnumSvrGlobalMemoryRegions(CLRDataEnumMemoryFlags flags)
{
SUPPORTS_DAC;
+
+ if (g_gcDacGlobals->n_heaps == nullptr || g_gcDacGlobals->g_heaps == nullptr)
+ return;
+
g_gcDacGlobals->n_heaps.EnumMem();
- DacEnumMemoryRegion(g_gcDacGlobals->g_heaps.GetAddr(),
- sizeof(TADDR) * *g_gcDacGlobals->n_heaps);
+
+ int heaps = *g_gcDacGlobals->n_heaps;
+ DacEnumMemoryRegion(g_gcDacGlobals->g_heaps.GetAddr(), sizeof(TADDR) * heaps);
g_gcDacGlobals->gc_structures_invalid_cnt.EnumMem();
g_gcDacGlobals->g_heaps.EnumMem();
- for (int i=0; i < *g_gcDacGlobals->n_heaps; i++)
+ for (int i = 0; i < heaps; i++)
{
DPTR(dac_gc_heap) pHeap = HeapTableIndex(g_gcDacGlobals->g_heaps, i);
@@ -249,6 +256,9 @@ DWORD DacGetNumHeaps()
HRESULT DacHeapWalker::InitHeapDataSvr(HeapData *&pHeaps, size_t &pCount)
{
+ if (g_gcDacGlobals->n_heaps == nullptr || g_gcDacGlobals->g_heaps == nullptr)
+ return S_OK;
+
// Scrape basic heap details
int heaps = *g_gcDacGlobals->n_heaps;
pCount = heaps;
diff --git a/src/debug/di/dbgtransportmanager.cpp b/src/debug/di/dbgtransportmanager.cpp
index 77a3548ea5..8c1079dc33 100644
--- a/src/debug/di/dbgtransportmanager.cpp
+++ b/src/debug/di/dbgtransportmanager.cpp
@@ -102,7 +102,7 @@ HRESULT DbgTransportTarget::GetTransportForProcess(DWORD dwPID
entry->m_cProcessRef++;
_ASSERTE(entry->m_cProcessRef > 0);
_ASSERTE(entry->m_transport != NULL);
- _ASSERTE(entry->m_hProcess > 0);
+ _ASSERTE((intptr_t)entry->m_hProcess > 0);
*ppTransport = entry->m_transport;
if (!DuplicateHandle(GetCurrentProcess(),
@@ -139,7 +139,7 @@ void DbgTransportTarget::ReleaseTransport(DbgTransportSession *pTransport)
_ASSERTE(entry->m_cProcessRef > 0);
_ASSERTE(entry->m_transport != NULL);
- _ASSERTE(entry->m_hProcess > 0);
+ _ASSERTE((intptr_t)entry->m_hProcess > 0);
if (entry->m_transport == pTransport)
{
diff --git a/src/debug/inc/dump/dumpcommon.h b/src/debug/inc/dump/dumpcommon.h
index 3e197ce29b..e57b4b3a12 100644
--- a/src/debug/inc/dump/dumpcommon.h
+++ b/src/debug/inc/dump/dumpcommon.h
@@ -5,6 +5,35 @@
#ifndef DEBUGGER_DUMPCOMMON_H
#define DEBUGGER_DUMPCOMMON_H
+#ifdef FEATURE_PAL
+typedef enum _MINIDUMP_TYPE {
+ MiniDumpNormal = 0x00000000,
+ MiniDumpWithDataSegs = 0x00000001,
+ MiniDumpWithFullMemory = 0x00000002,
+ MiniDumpWithHandleData = 0x00000004,
+ MiniDumpFilterMemory = 0x00000008,
+ MiniDumpScanMemory = 0x00000010,
+ MiniDumpWithUnloadedModules = 0x00000020,
+ MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
+ MiniDumpFilterModulePaths = 0x00000080,
+ MiniDumpWithProcessThreadData = 0x00000100,
+ MiniDumpWithPrivateReadWriteMemory = 0x00000200,
+ MiniDumpWithoutOptionalData = 0x00000400,
+ MiniDumpWithFullMemoryInfo = 0x00000800,
+ MiniDumpWithThreadInfo = 0x00001000,
+ MiniDumpWithCodeSegs = 0x00002000,
+ MiniDumpWithoutAuxiliaryState = 0x00004000,
+ MiniDumpWithFullAuxiliaryState = 0x00008000,
+ MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
+ MiniDumpIgnoreInaccessibleMemory = 0x00020000,
+ MiniDumpWithTokenInformation = 0x00040000,
+ MiniDumpWithModuleHeaders = 0x00080000,
+ MiniDumpFilterTriage = 0x00100000,
+ MiniDumpWithAvxXStateContext = 0x00200000,
+ MiniDumpValidTypeFlags = 0x003fffff,
+} MINIDUMP_TYPE;
+#endif // FEATURE_PAL
+
#if defined(DACCESS_COMPILE) || defined(RIGHT_SIDE_COMPILE)
// When debugging against minidumps, we frequently need to ignore errors