From: Mateusz Moscicki/System (PLT) /SRPOL/Engineer/Samsung Electronics Date: Fri, 12 Jan 2024 07:46:43 +0000 (+0100) Subject: [Tizen] Add method to write coredump of .NET process (#384) X-Git-Tag: accepted/tizen/unified/riscv/20240115.053917^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fheads%2Faccepted%2Ftizen_unified_riscv;p=platform%2Fupstream%2Fdotnet%2Fruntime.git [Tizen] Add method to write coredump of .NET process (#384) This method allows to create minimal coredump (as createdump does) of a process that is in dumping state after crush. This is a modified version of https://github.sec.samsung.net/dotnet/coreclr/commit/4d043d61ecacae0b9922ecc10a73749e7c8e5987 commit, adapted to .NET 8.0 Co-authored-by: Mateusz Moscicki --- diff --git a/packaging/coreclr.spec b/packaging/coreclr.spec index 6340b2f..2686211 100644 --- a/packaging/coreclr.spec +++ b/packaging/coreclr.spec @@ -470,6 +470,7 @@ cp %{_reldir_clr}/libjitinterface_%{_tarch}.so %{buildroot}%{_datadir}/ cp %{_reldir_clr}/ilasm %{buildroot}%{_datadir}/%{netcoreappdir} cp %{_reldir_clr}/ildasm %{buildroot}%{_datadir}/%{netcoreappdir} cp %{_reldir_clr}/createdump %{buildroot}%{_datadir}/%{netcoreappdir} +cp %{_reldir_clr}/libdnetmemoryenum.so %{buildroot}%{_datadir}/%{netcoreappdir} # CoreFX native cp %{_reldir_fx_native}/libSystem.Globalization.Native.so %{buildroot}%{_datadir}/%{netcoreappdir} @@ -718,7 +719,7 @@ cp ./nuget/*.nupkg %{buildroot}/nuget %{_datadir}/%{netcoreappdir}/ilasm %{_datadir}/%{netcoreappdir}/ildasm %{_datadir}/%{netcoreappdir}/createdump - +%{_datadir}/%{netcoreappdir}/libdnetmemoryenum.so %if 0%{skipmanaged} %else diff --git a/src/coreclr/debug/createdump/CMakeLists.txt b/src/coreclr/debug/createdump/CMakeLists.txt index 666bd68..cbf131b 100644 --- a/src/coreclr/debug/createdump/CMakeLists.txt +++ b/src/coreclr/debug/createdump/CMakeLists.txt @@ -107,6 +107,35 @@ endif(CLR_CMAKE_HOST_OSX) ) set_target_properties(createdump PROPERTIES LINK_FLAGS -pie) + set(DNETMEMORYENUM_SOURCES + dumpname.cpp + crashinfounix.cpp + threadinfounix.cpp + createdumpunix.cpp + crashinfo.cpp + threadinfo.cpp + datatarget.cpp + dumpwriter.cpp + dumpwriterelf.cpp + crashreportwriter.cpp + dnetmemoryenumlib.cpp + createdumppal.cpp + ${CLR_SRC_NATIVE_DIR}/minipal/utf8.c + ) + + add_library_clr(dnetmemoryenum SHARED + ${DNETMEMORYENUM_SOURCES} + ) + + set_property(TARGET dnetmemoryenum PROPERTY COMPILE_FLAGS "-fPIC") + + target_link_libraries(dnetmemoryenum + corguids + dbgutil + ) + + install_clr(TARGETS dnetmemoryenum DESTINATIONS . sharedFramework COMPONENT runtime) + endif(CLR_CMAKE_HOST_WIN32) install_clr(TARGETS createdump DESTINATIONS . sharedFramework COMPONENT runtime) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 8af6ec4..351e6a0 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -147,17 +147,36 @@ CrashInfo::LogMessage( } // +// Set registers for all threads +// +void +CrashInfo::SetThreadsRegisters(const std::vector &statuses) +{ + for (ThreadInfo* thread : m_threads) { + for (elf_prstatus* status : statuses) { + if (thread->Tid() == status->pr_pid) { + thread->SetRegisters(status); + break; + } + } + } +} + +// // Gather all the necessary crash dump info. // bool -CrashInfo::GatherCrashInfo(DumpType dumpType) +CrashInfo::GatherCrashInfo(DumpType dumpType, bool initialize_threads) { - // Get the info about the threads (registers, etc.) - for (ThreadInfo* thread : m_threads) + if (initialize_threads) { - if (!thread->Initialize()) + // Get the info about the threads (registers, etc.) + for (ThreadInfo* thread : m_threads) { - return false; + if (!thread->Initialize()) + { + return false; + } } } #ifdef __APPLE__ diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index 6673e2d..0253d2a 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -93,8 +93,9 @@ public: bool Initialize(); void CleanupAndResumeProcess(); - bool EnumerateAndSuspendThreads(); - bool GatherCrashInfo(DumpType dumpType); + bool EnumerateAndSuspendThreads(bool suspend = true); + void SetThreadsRegisters(const std::vector &statuses); + bool GatherCrashInfo(DumpType minidumpType, bool initialize_threads = true); void CombineMemoryRegions(); bool EnumerateMemoryRegionsWithDAC(DumpType dumpType); bool ReadMemory(void* address, void* buffer, size_t size); // read memory and add to dump diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index 70b2ddb..d373127 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -102,7 +102,7 @@ CrashInfo::CleanupAndResumeProcess() // Suspends all the threads and creating a list of them. Should be the before gathering any info about the process. // bool -CrashInfo::EnumerateAndSuspendThreads() +CrashInfo::EnumerateAndSuspendThreads(bool suspend) { char taskPath[128]; int chars = snprintf(taskPath, sizeof(taskPath), "/proc/%u/task", m_pid); @@ -125,17 +125,20 @@ CrashInfo::EnumerateAndSuspendThreads() pid_t tid = static_cast(strtol(entry->d_name, nullptr, 10)); if (tid != 0) { - // Reference: http://stackoverflow.com/questions/18577956/how-to-use-ptrace-to-get-a-consistent-view-of-multiple-threads - if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) != -1) + if (suspend) { - int waitStatus; - waitpid(tid, &waitStatus, __WALL); - } - else - { - printf_error("Problem suspending threads: ptrace(ATTACH, %d) FAILED %s (%d)\n", tid, strerror(errno), errno); - closedir(taskDir); - return false; + // Reference: http://stackoverflow.com/questions/18577956/how-to-use-ptrace-to-get-a-consistent-view-of-multiple-threads + if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) != -1) + { + int waitStatus; + waitpid(tid, &waitStatus, __WALL); + } + else + { + printf_error("Problem suspending threads: ptrace(ATTACH, %d) FAILED %s (%d)\n", tid, strerror(errno), errno); + closedir(taskDir); + return false; + } } // Add to the list of threads ThreadInfo* thread = new ThreadInfo(*this, tid); diff --git a/src/coreclr/debug/createdump/dnetmemoryenumlib.cpp b/src/coreclr/debug/createdump/dnetmemoryenumlib.cpp new file mode 100644 index 0000000..0148e5d --- /dev/null +++ b/src/coreclr/debug/createdump/dnetmemoryenumlib.cpp @@ -0,0 +1,287 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2024 Samsung Electronics Co., Ltd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "createdump.h" +#include "dnetmemoryenumlib.h" + +const int DEFAULT_SIGNAL = 6; + +bool g_diagnostics = false; +bool g_diagnosticsVerbose = false; + +#if defined(HOST_ARM64) +// Flag to check if atomics feature is available on +// the machine +bool g_arm64_atomics_present = false; +#endif + +std::vector sm_regions; + +int +prepare_crash_info(pid_t pid, elf_prstatus **statuses, int statuses_count, + DUMP_TYPE minidump_type, ReleaseHolder &crash_info) +{ + g_diagnostics = true; + + CreateDumpOptions options; + options.DumpType = DumpType::Heap; + options.DumpPathTemplate = nullptr; + options.AppModel = AppModelType::Normal; + options.CrashReport = false; + options.CreateDump = true; + options.Signal = DEFAULT_SIGNAL; + options.CrashThread = pid; + options.Pid = pid; + options.SignalCode = 0; + options.SignalErrno = 0; + options.SignalAddress = 0; + options.ExceptionRecord = 0; + + std::vector stats(statuses, statuses + statuses_count); + + switch (minidump_type) { + case DT_NORMAL: + default: + options.DumpType = DumpType::Mini; + break; + case DT_WITH_PRIV_AND_SHARED_MEM: + options.DumpType = DumpType::Triage; + break; + case DT_FULL: + options.DumpType = DumpType::Full; + break; + } + + int exitCode = REGERR_OK; + + if (pid != 0) + { + crash_info = new CrashInfo(options); + + // Initialize PAGE_SIZE +#if defined(__arm__) || defined(__aarch64__) || defined(__loongarch64) || defined(__riscv) + g_pageSize = sysconf(_SC_PAGESIZE); +#endif + + if (!crash_info->Initialize()) + { + return REGERR_CRASHINFO_INITIALIZE_ERROR; + } + + if (!crash_info->EnumerateAndSuspendThreads(false)) + { + return REGERR_ENUMERATION_ERROR; + } + crash_info->SetThreadsRegisters(stats); + if (!crash_info->GatherCrashInfo(options.DumpType, false)) + { + return REGERR_GATHER_CRASHINFO_ERROR; + } + if (!crash_info->EnumerateMemoryRegionsWithDAC(options.DumpType)) + { + return REGERR_ENUMERATIONDAC_ERROR; + } + crash_info->CombineMemoryRegions(); + } + else + { + exitCode = REGERR_WRONG_PID; + } + return exitCode; +} + +extern "C" DLLEXPORT int +DotNetMemoryEnumInit() +{ + return 0; +} + +extern "C" DLLEXPORT void +DotNetMemoryEnumFinish() +{ +} + +extern "C" DLLEXPORT int +DotNetMemoryWriteDump(pid_t pid, elf_prstatus **statuses, int statuses_count, + DUMP_TYPE minidump_type, const char *dump_path) +{ + ReleaseHolder crashInfo; + int exitCode = prepare_crash_info(pid, statuses, statuses_count, minidump_type, crashInfo); + + if (exitCode != REGERR_OK) + return exitCode; + + DumpWriter dumpWriter(*crashInfo); + + if (!dumpWriter.OpenDump(dump_path)) + exitCode = REGERR_OPENDUMP_ERROR; + + if (!dumpWriter.WriteDump()) + exitCode = REGERR_WRITEDUMP_ERROR; + + return exitCode; +} + +void +printf_status(const char* format, ...) +{ + va_list args; + va_start(args, format); + fprintf(stdout, "[createdump] "); + vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); +} + +void +printf_error(const char* format, ...) +{ + va_list args; + va_start(args, format); + fprintf(stderr, "[createdump] "); + vfprintf(stderr, format, args); + fflush(stderr); + va_end(args); +} + +void +trace_printf(const char* format, ...) +{ + if (g_diagnostics) + { + va_list args; + va_start(args, format); + fprintf(stdout, "[dnetmemoryenumlib] "); + vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + } +} + +void +trace_verbose_printf(const char* format, ...) +{ + if (g_diagnosticsVerbose) + { + va_list args; + va_start(args, format); + fprintf(stdout, "[dnetmemoryenumlib] "); + vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + } +} + +#ifdef HOST_UNIX +void +CrashInfo::Trace(const char* format, ...) +{ + if (g_diagnostics) + { + va_list args; + va_start(args, format); + fprintf(stdout, "[dnetmemoryenumlib] "); + vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + } +} + +void +CrashInfo::TraceVerbose(const char* format, ...) +{ + if (g_diagnosticsVerbose) + { + va_list args; + va_start(args, format); + fprintf(stdout, "[dnetmemoryenumlib] "); + vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + } +} +#endif + +const char* +GetDumpTypeString(DumpType dumpType) +{ + switch (dumpType) + { + case DumpType::Mini: + return "minidump"; + case DumpType::Heap: + return "minidump with heap"; + case DumpType::Triage: + return "triage minidump"; + case DumpType::Full: + return "full dump"; + default: + return "unknown"; + } +} + +MINIDUMP_TYPE +GetMiniDumpType(DumpType dumpType) +{ + switch (dumpType) + { + case DumpType::Mini: + return (MINIDUMP_TYPE)(MiniDumpNormal | + MiniDumpWithDataSegs | + MiniDumpWithHandleData | + MiniDumpWithThreadInfo); + case DumpType::Heap: + return (MINIDUMP_TYPE)(MiniDumpWithPrivateReadWriteMemory | + MiniDumpWithDataSegs | + MiniDumpWithHandleData | + MiniDumpWithUnloadedModules | + MiniDumpWithFullMemoryInfo | + MiniDumpWithThreadInfo | + MiniDumpWithTokenInformation); + case DumpType::Triage: + return (MINIDUMP_TYPE)(MiniDumpFilterTriage | + MiniDumpIgnoreInaccessibleMemory | + MiniDumpWithoutOptionalData | + MiniDumpWithProcessThreadData | + MiniDumpFilterModulePaths | + MiniDumpWithUnloadedModules | + MiniDumpFilterMemory | + MiniDumpWithHandleData); + case DumpType::Full: + default: + return (MINIDUMP_TYPE)(MiniDumpWithFullMemory | + MiniDumpWithDataSegs | + MiniDumpWithHandleData | + MiniDumpWithUnloadedModules | + MiniDumpWithFullMemoryInfo | + MiniDumpWithThreadInfo | + MiniDumpWithTokenInformation); + } +} diff --git a/src/coreclr/debug/createdump/dnetmemoryenumlib.h b/src/coreclr/debug/createdump/dnetmemoryenumlib.h new file mode 100644 index 0000000..0823d97 --- /dev/null +++ b/src/coreclr/debug/createdump/dnetmemoryenumlib.h @@ -0,0 +1,61 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019-2024 Samsung Electronics Co., Ltd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __DNETMEMORYENUMLIB_H__ +#define __DNETMEMORYENUMLIB_H__ + +#include +#include + +struct SimpleMemoryRegion { + uintptr_t m_startAddress; + size_t m_size; +}; + +enum DUMP_TYPE { + DT_NORMAL = 0, + DT_WITH_PRIV_AND_SHARED_MEM, + DT_FULL +}; + +enum REG_ERR { + REGERR_OK = 0, + REGERR_WRONG_PID = -1, + REGERR_INITIALIZATION_ERROR = -2, + REGERR_ENUMERATION_ERROR = -3, + REGERR_OPENDUMP_ERROR = -4, + REGERR_WRITEDUMP_ERROR = -5, + REGERR_ENUMERATIONDAC_ERROR = -6, + REGERR_CRASHINFO_INITIALIZE_ERROR = -7, + REGERR_GATHER_CRASHINFO_ERROR = -8, +}; + +extern "C" DLLEXPORT int DotNetMemoryWriteDump(pid_t pid, elf_prstatus **statuses, int statuses_count, + DUMP_TYPE minidump_type, const char *dump_path); +extern "C" DLLEXPORT int DotNetMemoryEnumInit(); +extern "C" DLLEXPORT void DotNetMemoryEnumFinish(); + +#endif diff --git a/src/coreclr/debug/createdump/threadinfo.cpp b/src/coreclr/debug/createdump/threadinfo.cpp index 9d4e837..9c908ad 100644 --- a/src/coreclr/debug/createdump/threadinfo.cpp +++ b/src/coreclr/debug/createdump/threadinfo.cpp @@ -396,3 +396,99 @@ ThreadInfo::IsCrashThread() const { return m_tid == m_crashInfo.CrashThread(); } + +void ThreadInfo::SetRegisters(elf_prstatus *prstatus) +{ +#if defined(__x86_64__) + struct user_regs_struct *u_reg = (struct user_regs_struct *)&prstatus->pr_reg; + m_gpRegisters.rip = u_reg->rip; + m_gpRegisters.rbp = u_reg->rbp; + m_gpRegisters.rsp = u_reg->rsp; + + m_gpRegisters.rax = u_reg->rax; + m_gpRegisters.rbx = u_reg->rbx; + m_gpRegisters.rcx = u_reg->rcx; + m_gpRegisters.rdx = u_reg->rdx; + m_gpRegisters.rsi = u_reg->rsi; + m_gpRegisters.rdi = u_reg->rdi; + + m_gpRegisters.cs = u_reg->cs; + m_gpRegisters.gs = u_reg->gs; + m_gpRegisters.es = u_reg->es; + m_gpRegisters.fs = u_reg->fs; + m_gpRegisters.ds = u_reg->ds; + m_gpRegisters.ss = u_reg->ss; + m_gpRegisters.fs_base = u_reg->fs_base; + m_gpRegisters.gs_base = u_reg->gs_base; + + m_gpRegisters.orig_rax = u_reg->orig_rax; + + m_gpRegisters.r8 = u_reg->r8; + m_gpRegisters.r9 = u_reg->r9; + m_gpRegisters.r10 = u_reg->r10; + m_gpRegisters.r11 = u_reg->r11; + m_gpRegisters.r12 = u_reg->r12; + m_gpRegisters.r13 = u_reg->r13; + m_gpRegisters.r14 = u_reg->r14; + m_gpRegisters.r15 = u_reg->r15; + + m_gpRegisters.eflags = u_reg->eflags; +#elif defined(__i386__) + struct user_regs_struct *u_reg = (struct user_regs_struct *)&prstatus->pr_reg; + m_gpRegisters.ebx = u_reg->ebx; + m_gpRegisters.ecx = u_reg->ecx; + m_gpRegisters.edx = u_reg->edx; + m_gpRegisters.esi = u_reg->esi; + m_gpRegisters.edi = u_reg->edi; + m_gpRegisters.ebp = u_reg->ebp; + m_gpRegisters.eax = u_reg->eax; + m_gpRegisters.xds = u_reg->xds; + m_gpRegisters.xes = u_reg->xes; + m_gpRegisters.xfs = u_reg->xfs; + m_gpRegisters.xgs = u_reg->xgs; + m_gpRegisters.orig_eax = u_reg->orig_eax; + m_gpRegisters.eip = u_reg->eip; + m_gpRegisters.xcs = u_reg->xcs; + m_gpRegisters.eflags = u_reg->eflags; + m_gpRegisters.esp = u_reg->esp; + m_gpRegisters.xss = u_reg->xss; +#elif defined(__arm__) + +#define REGS_REGULAR_NUM 13 +#define REG_FP 11 +#define REG_IP 12 +#define REG_SP 13 +#define REG_LR 14 +#define REG_PC 15 +#define REG_SPSR 16 + struct user_regs *u_reg = (struct user_regs *)&prstatus->pr_reg; + m_gpRegisters.ARM_sp = u_reg->uregs[REG_SP]; + m_gpRegisters.ARM_lr = u_reg->uregs[REG_LR]; + m_gpRegisters.ARM_pc = u_reg->uregs[REG_PC]; + m_gpRegisters.ARM_cpsr = u_reg->uregs[REG_SPSR]; + + m_gpRegisters.ARM_r0 = u_reg->uregs[0]; + m_gpRegisters.ARM_ORIG_r0 = u_reg->uregs[0]; + m_gpRegisters.ARM_r1 = u_reg->uregs[1]; + m_gpRegisters.ARM_r2 = u_reg->uregs[2]; + m_gpRegisters.ARM_r3 = u_reg->uregs[3]; + m_gpRegisters.ARM_r4 = u_reg->uregs[4]; + m_gpRegisters.ARM_r5 = u_reg->uregs[5]; + m_gpRegisters.ARM_r6 = u_reg->uregs[6]; + m_gpRegisters.ARM_r7 = u_reg->uregs[7]; + m_gpRegisters.ARM_r8 = u_reg->uregs[8]; + m_gpRegisters.ARM_r9 = u_reg->uregs[9]; + m_gpRegisters.ARM_r10 = u_reg->uregs[10]; + m_gpRegisters.ARM_fp = u_reg->uregs[REG_FP]; + m_gpRegisters.ARM_ip = u_reg->uregs[REG_IP]; +#elif defined(__aarch64__) + struct user_regs_struct *u_reg = (struct user_regs_struct *)&prstatus->pr_reg; + memcpy(m_gpRegisters.regs, u_reg, sizeof(m_gpRegisters.regs)); + m_gpRegisters.sp = u_reg->sp; + m_gpRegisters.pc = u_reg->pc; + m_gpRegisters.pstate = u_reg->pstate; +#elif defined(__riscv) + struct user_regs_struct *u_reg = (struct user_regs_struct *)&prstatus->pr_reg; + memcpy(&m_gpRegisters, u_reg, sizeof(m_gpRegisters)); +#endif +} diff --git a/src/coreclr/debug/createdump/threadinfo.h b/src/coreclr/debug/createdump/threadinfo.h index d9f38a7..8d93fbe 100644 --- a/src/coreclr/debug/createdump/threadinfo.h +++ b/src/coreclr/debug/createdump/threadinfo.h @@ -112,6 +112,7 @@ public: bool UnwindThread(IXCLRDataProcess* pClrDataProcess, ISOSDacInterface* pSos); void GetThreadStack(); void GetThreadContext(uint32_t flags, CONTEXT* context) const; + void SetRegisters(elf_prstatus *prstatus); inline pid_t Tid() const { return m_tid; } inline pid_t Ppid() const { return m_ppid; }