From c53fa592ba54c0f346856d680cc30348850eba83 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Mon, 24 Apr 2017 17:12:36 -0700 Subject: [PATCH] Add "createdump" command to SOS. (#11161) Uses the createdump utility code as a library. Changed the "-m, --micro" option to "-t, --triage". Lots of cleanup. --- Documentation/botr/xplat-minidump-generation.md | 2 +- src/ToolBox/SOS/Strike/CMakeLists.txt | 6 ++ src/ToolBox/SOS/Strike/sos_unixexports.src | 1 + src/ToolBox/SOS/Strike/sosdocsunix.txt | 15 ++- src/ToolBox/SOS/Strike/strike.cpp | 74 ++++++++++++++ src/ToolBox/SOS/lldbplugin/soscommand.cpp | 1 + src/debug/createdump/CMakeLists.txt | 7 +- src/debug/createdump/crashinfo.cpp | 71 +++++++------- src/debug/createdump/crashinfo.h | 6 +- src/debug/createdump/createdump.cpp | 124 ++++++------------------ src/debug/createdump/createdump.h | 3 +- src/debug/createdump/datatarget.cpp | 38 ++++---- src/debug/createdump/datatarget.h | 8 +- src/debug/createdump/dumpwriter.cpp | 11 +-- src/debug/createdump/dumpwriter.h | 5 +- src/debug/createdump/main.cpp | 99 +++++++++++++++++++ src/debug/createdump/threadinfo.cpp | 99 +++++++++++++++++-- src/debug/createdump/threadinfo.h | 5 +- src/inc/releaseholder.h | 77 +++++++++++++++ src/pal/src/thread/process.cpp | 4 +- 20 files changed, 471 insertions(+), 185 deletions(-) create mode 100644 src/debug/createdump/main.cpp create mode 100644 src/inc/releaseholder.h diff --git a/Documentation/botr/xplat-minidump-generation.md b/Documentation/botr/xplat-minidump-generation.md index 495ecff..78660b6 100644 --- a/Documentation/botr/xplat-minidump-generation.md +++ b/Documentation/botr/xplat-minidump-generation.md @@ -61,7 +61,7 @@ Environment variables supported: -f, --name - dump path and file name. The pid can be placed in the name with %d. The default is "/tmp/coredump.%d" -n, --normal - create minidump (default). -h, --withheap - create minidump with heap. - -m, --micro - create triage minidump. + -t, --triage - create triage minidump. -d, --diag - enable diagnostic messages. # Testing # diff --git a/src/ToolBox/SOS/Strike/CMakeLists.txt b/src/ToolBox/SOS/Strike/CMakeLists.txt index 8ba0ade..ff5f864 100644 --- a/src/ToolBox/SOS/Strike/CMakeLists.txt +++ b/src/ToolBox/SOS/Strike/CMakeLists.txt @@ -145,6 +145,12 @@ else(WIN32) endif(WIN32) if(CLR_CMAKE_PLATFORM_ARCH_AMD64) + if(CLR_CMAKE_PLATFORM_LINUX) + list(APPEND + SOS_LIBRARY + createdump_lib + ) + endif(CLR_CMAKE_PLATFORM_LINUX) set(SOS_SOURCES_ARCH disasmX86.cpp ) diff --git a/src/ToolBox/SOS/Strike/sos_unixexports.src b/src/ToolBox/SOS/Strike/sos_unixexports.src index ed811b6..a8cc712 100644 --- a/src/ToolBox/SOS/Strike/sos_unixexports.src +++ b/src/ToolBox/SOS/Strike/sos_unixexports.src @@ -4,6 +4,7 @@ bpmd ClrStack +CreateDump DumpArray DumpAssembly DumpClass diff --git a/src/ToolBox/SOS/Strike/sosdocsunix.txt b/src/ToolBox/SOS/Strike/sosdocsunix.txt index 5ab2b31..517227c 100644 --- a/src/ToolBox/SOS/Strike/sosdocsunix.txt +++ b/src/ToolBox/SOS/Strike/sosdocsunix.txt @@ -53,8 +53,8 @@ DumpSigElem Examining the GC history Other ----------------------------- ----------------------------- HistInit (histinit) FAQ -HistRoot (histroot) Help (soshelp) -HistObj (histobj) +HistRoot (histroot) CreateDump (createdump) +HistObj (histobj) Help (soshelp) HistObjFind (histobjfind) HistClear (histclear) \\ @@ -618,6 +618,17 @@ should only need to execute "clrstack -i", and from there, click on the DML hyperlinks to inspect the different managed stack frames and managed variables. \\ +COMMAND: createdump. +createdump [options] [dumpFileName] +-n - create minidump. +-h - create minidump with heap (default). +-t - create triage minidump. +-d - enable diagnostic messages. + +Creates a platform (ELF core on Linux, etc.) minidump. The pid can be placed in the dump +file name with %d. The default is '/tmp/coredump.%d'. +\\ + COMMAND: ip2md. IP2MD diff --git a/src/ToolBox/SOS/Strike/strike.cpp b/src/ToolBox/SOS/Strike/strike.cpp index 2b54b4f..0d5d8c3 100644 --- a/src/ToolBox/SOS/Strike/strike.cpp +++ b/src/ToolBox/SOS/Strike/strike.cpp @@ -14368,6 +14368,80 @@ _EFN_GetManagedObjectFieldInfo( return S_OK; } +#ifdef FEATURE_PAL + +#ifdef __linux__ +#include +#include "datatarget.h" +extern bool CreateDumpForSOS(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType, ICLRDataTarget* dataTarget); +extern bool g_diagnostics; +#endif // __linux__ + +DECLARE_API(CreateDump) +{ + INIT_API(); +#ifdef __linux__ + StringHolder sFileName; + BOOL normal = FALSE; + BOOL withHeap = FALSE; + BOOL triage = FALSE; + BOOL diag = FALSE; + + size_t nArg = 0; + CMDOption option[] = + { // name, vptr, type, hasValue + {"-n", &normal, COBOOL, FALSE}, + {"-h", &withHeap, COBOOL, FALSE}, + {"-t", &triage, COBOOL, FALSE}, + {"-d", &diag, COBOOL, FALSE}, + }; + CMDValue arg[] = + { // vptr, type + {&sFileName.data, COSTRING} + }; + if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) + { + return E_FAIL; + } + MINIDUMP_TYPE minidumpType = MiniDumpWithPrivateReadWriteMemory; + ULONG pid = 0; + g_ExtSystem->GetCurrentProcessId(&pid); + + if (withHeap) + { + minidumpType = MiniDumpWithPrivateReadWriteMemory; + } + else if (triage) + { + minidumpType = MiniDumpFilterTriage; + } + else if (normal) + { + minidumpType = MiniDumpNormal; + } + g_diagnostics = diag; + + const char* programPath = g_ExtServices->GetCoreClrDirectory(); + const char* dumpPathTemplate = "/tmp/coredump.%d"; + ToRelease dataTarget = new DataTarget(); + dataTarget->AddRef(); + + if (sFileName.data != nullptr) + { + dumpPathTemplate = sFileName.data; + } + if (!CreateDumpForSOS(programPath, dumpPathTemplate, pid, minidumpType, dataTarget)) + { + Status = E_FAIL; + } +#else // __linux__ + ExtErr("CreateDump not supported on this platform\n"); +#endif // __linux__ + return Status; +} + +#endif // FEATURE_PAL + void PrintHelp (__in_z LPCSTR pszCmdName) { static LPSTR pText = NULL; diff --git a/src/ToolBox/SOS/lldbplugin/soscommand.cpp b/src/ToolBox/SOS/lldbplugin/soscommand.cpp index 4ca7ce3..64d198c 100644 --- a/src/ToolBox/SOS/lldbplugin/soscommand.cpp +++ b/src/ToolBox/SOS/lldbplugin/soscommand.cpp @@ -125,6 +125,7 @@ sosCommandInitialize(lldb::SBDebugger debugger) interpreter.AddCommand("bpmd", new sosCommand("bpmd"), "Creates a breakpoint at the specified managed method in the specified module."); interpreter.AddCommand("clrstack", new sosCommand("ClrStack"), "Provides a stack trace of managed code only."); interpreter.AddCommand("clrthreads", new sosCommand("Threads"), "List the managed threads running."); + interpreter.AddCommand("createdump", new sosCommand("CreateDump"), "Create a xplat minidump."); interpreter.AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method."); interpreter.AddCommand("dumpclass", new sosCommand("DumpClass"), "Displays information about a EE class structure at the specified address."); interpreter.AddCommand("dumpheap", new sosCommand("DumpHeap"), "Displays info about the garbage-collected heap and collection statistics about objects."); diff --git a/src/debug/createdump/CMakeLists.txt b/src/debug/createdump/CMakeLists.txt index 081a308..5b5ec0a 100644 --- a/src/debug/createdump/CMakeLists.txt +++ b/src/debug/createdump/CMakeLists.txt @@ -19,11 +19,16 @@ set(CREATEDUMP_SOURCES dumpwriter.cpp ) -_add_executable(createdump +_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 ) diff --git a/src/debug/createdump/crashinfo.cpp b/src/debug/createdump/crashinfo.cpp index f92630b..cae8857 100644 --- a/src/debug/createdump/crashinfo.cpp +++ b/src/debug/createdump/crashinfo.cpp @@ -4,14 +4,15 @@ #include "createdump.h" -CrashInfo::CrashInfo(pid_t pid, DataTarget& dataTarget) : +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(); + dataTarget->AddRef(); m_auxvValues.fill(0); } @@ -39,7 +40,7 @@ CrashInfo::~CrashInfo() const_cast(region).Cleanup(); } m_otherMappings.clear(); - m_dataTarget.Release(); + m_dataTarget->Release(); } STDMETHODIMP @@ -107,22 +108,25 @@ 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) + // Don't suspend the threads if running under sos + if (!m_sos) { - int waitStatus; - waitpid(tid, &waitStatus, __WALL); - - // Add to the list of (suspended) threads - ThreadInfo* thread = new ThreadInfo(tid); - m_threads.push_back(thread); - } - else - { - fprintf(stderr, "ptrace(ATTACH, %d) FAILED %s\n", tid, strerror(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 + { + 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); } } @@ -141,7 +145,7 @@ CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType) // Get the info about the threads (registers, etc.) for (ThreadInfo* thread : m_threads) { - if (!thread->Initialize()) + if (!thread->Initialize(m_sos ? m_dataTarget : nullptr)) { return false; } @@ -187,9 +191,12 @@ CrashInfo::GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType) void CrashInfo::ResumeThreads() { - for (ThreadInfo* thread : m_threads) + if (!m_sos) { - thread->ResumeThread(); + for (ThreadInfo* thread : m_threads) + { + thread->ResumeThread(); + } } } @@ -337,24 +344,16 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE bool result = false; // We assume that the DAC is in the same location as this createdump exe - ArrayHolder dacPath = new char[MAX_LONGPATH]; - strcpy_s(dacPath, MAX_LONGPATH, programPath); - char *last = strrchr(dacPath, '/'); - if (last != nullptr) - { - *(last + 1) = '\0'; - } - else - { - dacPath[0] = '\0'; - } - strcat_s(dacPath, MAX_LONGPATH, MAKEDLLNAME_A("mscordaccore")); + std::string dacPath; + dacPath.append(programPath); + dacPath.append("/"); + dacPath.append(MAKEDLLNAME_A("mscordaccore")); // Load and initialize the DAC - hdac = LoadLibraryA(dacPath); + hdac = LoadLibraryA(dacPath.c_str()); if (hdac == nullptr) { - fprintf(stderr, "LoadLibraryA(%s) FAILED %d\n", (char*)dacPath, GetLastError()); + fprintf(stderr, "LoadLibraryA(%s) FAILED %d\n", dacPath.c_str(), GetLastError()); goto exit; } pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(hdac, "CLRDataCreateInstance"); @@ -363,7 +362,7 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(const char* programPath, MINIDUMP_TYPE fprintf(stderr, "GetProcAddress(CLRDataCreateInstance) FAILED %d\n", GetLastError()); goto exit; } - hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), &m_dataTarget, (void**)&clrDataEnumRegions); + hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), m_dataTarget, (void**)&clrDataEnumRegions); if (FAILED(hr)) { fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr); @@ -479,7 +478,7 @@ bool CrashInfo::ReadMemory(void* address, void* buffer, size_t size) { uint32_t read = 0; - if (FAILED(m_dataTarget.ReadVirtual(reinterpret_cast(address), reinterpret_cast(buffer), size, &read))) + if (FAILED(m_dataTarget->ReadVirtual(reinterpret_cast(address), reinterpret_cast(buffer), size, &read))) { return false; } diff --git a/src/debug/createdump/crashinfo.h b/src/debug/createdump/crashinfo.h index bdd19cf..40d7f5d 100644 --- a/src/debug/createdump/crashinfo.h +++ b/src/debug/createdump/crashinfo.h @@ -22,7 +22,8 @@ private: pid_t m_ppid; // parent pid pid_t m_tgid; // process group char* m_name; // exe name - DataTarget& m_dataTarget; // read process memory + bool m_sos; // true if running under sos + ICLRDataTarget* m_dataTarget; // read process memory, etc. std::array m_auxvValues; // auxv values std::vector m_auxvEntries; // full auxv entries std::vector m_threads; // threads found and suspended @@ -31,7 +32,7 @@ private: std::set m_memoryRegions; // memory regions from DAC, etc. public: - CrashInfo(pid_t pid, DataTarget& dataTarget); + CrashInfo(pid_t pid, ICLRDataTarget* dataTarget, bool sos); virtual ~CrashInfo(); bool EnumerateAndSuspendThreads(); bool GatherCrashInfo(const char* programPath, MINIDUMP_TYPE minidumpType); @@ -42,6 +43,7 @@ public: 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 Threads() const { return m_threads; } const std::set ModuleMappings() const { return m_moduleMappings; } diff --git a/src/debug/createdump/createdump.cpp b/src/debug/createdump/createdump.cpp index 9e6af4b..0a95e53 100644 --- a/src/debug/createdump/createdump.cpp +++ b/src/debug/createdump/createdump.cpp @@ -4,134 +4,66 @@ #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 (default).\n" -"-h, --withheap - create minidump with heap.\n" -"-m, --micro - create triage minidump.\n" -"-d, --diag - enable diagnostic messages.\n"; - bool g_diagnostics = false; // -// Create a minidump using the DAC's enum memory regions interface +// The common create dump code // -static bool -CreateDump(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType) +bool +CreateDumpCommon(const char* programPath, const char* dumpPathTemplate, MINIDUMP_TYPE minidumpType, CrashInfo* crashInfo) { - DataTarget* dataTarget = new DataTarget(pid); - CrashInfo* crashInfo = new CrashInfo(pid, *dataTarget); - DumpWriter* dumpWriter = new DumpWriter(*dataTarget, *crashInfo); + ReleaseHolder dumpWriter = new DumpWriter(*crashInfo); + bool result = false; + ArrayHolder dumpPath = new char[MAX_LONGPATH]; + snprintf(dumpPath, MAX_LONGPATH, dumpPathTemplate, crashInfo->Pid()); + const char* dumpType = "minidump"; - bool result = false; + 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; } - // The initialize the data target's ReadVirtual support (opens /proc/$pid/mem) - if (!dataTarget->Initialize(crashInfo)) - { - goto exit; - } // Gather all the info about the process, threads (registers, etc.) and memory regions if (!crashInfo->GatherCrashInfo(programPath, minidumpType)) { goto exit; } - snprintf(dumpPath, MAX_LONGPATH, dumpPathTemplate, pid); if (!dumpWriter->OpenDump(dumpPath)) { goto exit; } - 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); if (!dumpWriter->WriteDump()) { goto exit; } result = true; exit: - dumpWriter->Release(); crashInfo->ResumeThreads(); - crashInfo->Release(); - dataTarget->Release(); return result; } // -// main entry point +// Entry point for SOS createdump command // -int __cdecl main(const int argc, const char* argv[]) +bool +CreateDumpForSOS(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType, ICLRDataTarget* dataTarget) { - MINIDUMP_TYPE minidumpType = MiniDumpWithPrivateReadWriteMemory; - const char* dumpPathTemplate = "/tmp/coredump.%d"; - const char* programPath = nullptr; - pid_t pid = 0; - - int exitCode = PAL_InitializeDLL(); - if (exitCode != 0) - { - fprintf(stderr, "PAL initialization FAILED %d\n", exitCode); - return exitCode; - } - programPath = *argv; - argv++; - - 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, "-m") == 0) || (strcmp(*argv, "--micro") == 0)) - { - minidumpType = MiniDumpFilterTriage; - } - else if ((strcmp(*argv, "-d") == 0) || (strcmp(*argv, "--diag") == 0)) - { - g_diagnostics = true; - } - else { - pid = atoll(*argv); - } - argv++; - } - } - // if no pid or invalid command line option - if (pid == 0) - { - fprintf(stderr, "%s", g_help); - exitCode = -1; - } - else if (!CreateDump(programPath, dumpPathTemplate, pid, minidumpType)) - { - exitCode = -1; - } - PAL_TerminateEx(exitCode); - return exitCode; -} + ReleaseHolder 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 index 5a8d52d..38c3525 100644 --- a/src/debug/createdump/createdump.h +++ b/src/debug/createdump/createdump.h @@ -34,6 +34,7 @@ extern bool g_diagnostics; #include #include #include +#include #include #include #include @@ -53,4 +54,4 @@ extern bool g_diagnostics; #include "threadinfo.h" #include "memoryregion.h" #include "crashinfo.h" -#include "dumpwriter.h" +#include "dumpwriter.h" \ No newline at end of file diff --git a/src/debug/createdump/datatarget.cpp b/src/debug/createdump/datatarget.cpp index b8c09e0..38505e2 100644 --- a/src/debug/createdump/datatarget.cpp +++ b/src/debug/createdump/datatarget.cpp @@ -6,7 +6,7 @@ #define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8) -DataTarget::DataTarget(pid_t pid) : +DumpDataTarget::DumpDataTarget(pid_t pid) : m_ref(1), m_pid(pid), m_fd(-1), @@ -14,7 +14,7 @@ DataTarget::DataTarget(pid_t pid) : { } -DataTarget::~DataTarget() +DumpDataTarget::~DumpDataTarget() { if (m_fd != -1) { @@ -24,7 +24,7 @@ DataTarget::~DataTarget() } bool -DataTarget::Initialize(CrashInfo * crashInfo) +DumpDataTarget::Initialize(CrashInfo * crashInfo) { char memPath[128]; _snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%lu/mem", m_pid); @@ -40,7 +40,7 @@ DataTarget::Initialize(CrashInfo * crashInfo) } STDMETHODIMP -DataTarget::QueryInterface( +DumpDataTarget::QueryInterface( ___in REFIID InterfaceId, ___out PVOID* Interface ) @@ -66,14 +66,14 @@ DataTarget::QueryInterface( } STDMETHODIMP_(ULONG) -DataTarget::AddRef() +DumpDataTarget::AddRef() { LONG ref = InterlockedIncrement(&m_ref); return ref; } STDMETHODIMP_(ULONG) -DataTarget::Release() +DumpDataTarget::Release() { LONG ref = InterlockedDecrement(&m_ref); if (ref == 0) @@ -84,7 +84,7 @@ DataTarget::Release() } HRESULT STDMETHODCALLTYPE -DataTarget::GetMachineType( +DumpDataTarget::GetMachineType( /* [out] */ ULONG32 *machine) { #ifdef _AMD64_ @@ -102,7 +102,7 @@ DataTarget::GetMachineType( } HRESULT STDMETHODCALLTYPE -DataTarget::GetPointerSize( +DumpDataTarget::GetPointerSize( /* [out] */ ULONG32 *size) { #if defined(_AMD64_) || defined(_ARM64_) @@ -116,7 +116,7 @@ DataTarget::GetPointerSize( } HRESULT STDMETHODCALLTYPE -DataTarget::GetImageBase( +DumpDataTarget::GetImageBase( /* [string][in] */ LPCWSTR moduleName, /* [out] */ CLRDATA_ADDRESS *baseAddress) { @@ -149,7 +149,7 @@ DataTarget::GetImageBase( } HRESULT STDMETHODCALLTYPE -DataTarget::ReadVirtual( +DumpDataTarget::ReadVirtual( /* [in] */ CLRDATA_ADDRESS address, /* [length_is][size_is][out] */ PBYTE buffer, /* [in] */ ULONG32 size, @@ -168,7 +168,7 @@ DataTarget::ReadVirtual( } HRESULT STDMETHODCALLTYPE -DataTarget::WriteVirtual( +DumpDataTarget::WriteVirtual( /* [in] */ CLRDATA_ADDRESS address, /* [size_is][in] */ PBYTE buffer, /* [in] */ ULONG32 size, @@ -179,7 +179,7 @@ DataTarget::WriteVirtual( } HRESULT STDMETHODCALLTYPE -DataTarget::GetTLSValue( +DumpDataTarget::GetTLSValue( /* [in] */ ULONG32 threadID, /* [in] */ ULONG32 index, /* [out] */ CLRDATA_ADDRESS* value) @@ -189,7 +189,7 @@ DataTarget::GetTLSValue( } HRESULT STDMETHODCALLTYPE -DataTarget::SetTLSValue( +DumpDataTarget::SetTLSValue( /* [in] */ ULONG32 threadID, /* [in] */ ULONG32 index, /* [in] */ CLRDATA_ADDRESS value) @@ -199,7 +199,7 @@ DataTarget::SetTLSValue( } HRESULT STDMETHODCALLTYPE -DataTarget::GetCurrentThreadID( +DumpDataTarget::GetCurrentThreadID( /* [out] */ ULONG32* threadID) { assert(false); @@ -207,7 +207,7 @@ DataTarget::GetCurrentThreadID( } HRESULT STDMETHODCALLTYPE -DataTarget::GetThreadContext( +DumpDataTarget::GetThreadContext( /* [in] */ ULONG32 threadID, /* [in] */ ULONG32 contextFlags, /* [in] */ ULONG32 contextSize, @@ -232,7 +232,7 @@ DataTarget::GetThreadContext( } HRESULT STDMETHODCALLTYPE -DataTarget::SetThreadContext( +DumpDataTarget::SetThreadContext( /* [in] */ ULONG32 threadID, /* [in] */ ULONG32 contextSize, /* [out, size_is(contextSize)] */ PBYTE context) @@ -242,7 +242,7 @@ DataTarget::SetThreadContext( } HRESULT STDMETHODCALLTYPE -DataTarget::Request( +DumpDataTarget::Request( /* [in] */ ULONG32 reqCode, /* [in] */ ULONG32 inBufferSize, /* [size_is][in] */ BYTE *inBuffer, @@ -254,10 +254,10 @@ DataTarget::Request( } HRESULT STDMETHODCALLTYPE -DataTarget::VirtualUnwind( +DumpDataTarget::VirtualUnwind( /* [in] */ DWORD threadId, /* [in] */ ULONG32 contextSize, /* [in, out, size_is(contextSize)] */ PBYTE context) { return E_NOTIMPL; -} \ No newline at end of file +} diff --git a/src/debug/createdump/datatarget.h b/src/debug/createdump/datatarget.h index 802c9d6..8ff6773 100644 --- a/src/debug/createdump/datatarget.h +++ b/src/debug/createdump/datatarget.h @@ -4,7 +4,7 @@ class CrashInfo; -class DataTarget : public ICLRDataTarget, ICorDebugDataTarget4 +class DumpDataTarget : public ICLRDataTarget, ICorDebugDataTarget4 { private: LONG m_ref; // reference count @@ -13,8 +13,8 @@ private: CrashInfo* m_crashInfo; public: - DataTarget(pid_t pid); - virtual ~DataTarget(); + DumpDataTarget(pid_t pid); + virtual ~DumpDataTarget(); bool Initialize(CrashInfo* crashInfo); // @@ -87,4 +87,4 @@ public: /* [in] */ DWORD threadId, /* [in] */ ULONG32 contextSize, /* [in, out, size_is(contextSize)] */ PBYTE context); -}; \ No newline at end of file +}; diff --git a/src/debug/createdump/dumpwriter.cpp b/src/debug/createdump/dumpwriter.cpp index cec19e8..9057d18 100644 --- a/src/debug/createdump/dumpwriter.cpp +++ b/src/debug/createdump/dumpwriter.cpp @@ -4,13 +4,11 @@ #include "createdump.h" -DumpWriter::DumpWriter(DataTarget& dataTarget, CrashInfo& crashInfo) : +DumpWriter::DumpWriter(CrashInfo& crashInfo) : m_ref(1), m_fd(-1), - m_dataTarget(dataTarget), m_crashInfo(crashInfo) { - m_dataTarget.AddRef(); m_crashInfo.AddRef(); } @@ -21,7 +19,6 @@ DumpWriter::~DumpWriter() close(m_fd); m_fd = -1; } - m_dataTarget.Release(); m_crashInfo.Release(); } @@ -62,7 +59,7 @@ DumpWriter::Release() } bool -DumpWriter::OpenDump(char* dumpFileName) +DumpWriter::OpenDump(const char* dumpFileName) { m_fd = open(dumpFileName, O_WRONLY|O_CREAT|O_TRUNC, 0664); if (m_fd == -1) @@ -242,7 +239,7 @@ DumpWriter::WriteDump() uint32_t bytesToRead = std::min(size, (uint32_t)sizeof(m_tempBuffer)); uint32_t read = 0; - if (FAILED(m_dataTarget.ReadVirtual(address, m_tempBuffer, bytesToRead, &read))) { + if (FAILED(m_crashInfo.DataTarget()->ReadVirtual(address, m_tempBuffer, bytesToRead, &read))) { fprintf(stderr, "ReadVirtual(%016lx, %08x) FAILED\n", address, bytesToRead); return false; } @@ -486,4 +483,4 @@ DumpWriter::WriteData(const void* buffer, size_t length) done += written; } return true; -} \ No newline at end of file +} diff --git a/src/debug/createdump/dumpwriter.h b/src/debug/createdump/dumpwriter.h index 272a2eb..0b4f88c 100644 --- a/src/debug/createdump/dumpwriter.h +++ b/src/debug/createdump/dumpwriter.h @@ -33,14 +33,13 @@ class DumpWriter : IUnknown private: LONG m_ref; // reference count int m_fd; - DataTarget& m_dataTarget; CrashInfo& m_crashInfo; BYTE m_tempBuffer[0x4000]; public: - DumpWriter(DataTarget& dataTarget, CrashInfo& crashInfo); + DumpWriter(CrashInfo& crashInfo); virtual ~DumpWriter(); - bool OpenDump(char* dumpFileName); + bool OpenDump(const char* dumpFileName); bool WriteDump(); // IUnknown diff --git a/src/debug/createdump/main.cpp b/src/debug/createdump/main.cpp new file mode 100644 index 0000000..0338277 --- /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 dataTarget = new DumpDataTarget(pid); + ReleaseHolder 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/threadinfo.cpp b/src/debug/createdump/threadinfo.cpp index 52af060..8e73fcf 100644 --- a/src/debug/createdump/threadinfo.cpp +++ b/src/debug/createdump/threadinfo.cpp @@ -4,6 +4,11 @@ #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) { @@ -14,15 +19,24 @@ ThreadInfo::~ThreadInfo() } bool -ThreadInfo::Initialize() +ThreadInfo::Initialize(ICLRDataTarget* dataTarget) { if (!CrashInfo::GetStatus(m_tid, &m_ppid, &m_tgid, nullptr)) { return false; } - if (!GetRegisters()) + if (dataTarget != nullptr) { - return false; + 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; @@ -39,7 +53,7 @@ ThreadInfo::ResumeThread() } bool -ThreadInfo::GetRegisters() +ThreadInfo::GetRegistersWithPTrace() { if (ptrace((__ptrace_request)PTRACE_GETREGS, m_tid, nullptr, &m_gpRegisters) == -1) { @@ -61,6 +75,70 @@ ThreadInfo::GetRegisters() return true; } +bool +ThreadInfo::GetRegistersWithDataTarget(ICLRDataTarget* dataTarget) +{ + CONTEXT context; + context.ContextFlags = CONTEXT_ALL; + if (dataTarget->GetThreadContext(m_tid, context.ContextFlags, sizeof(context), reinterpret_cast(&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 { @@ -95,6 +173,7 @@ 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; @@ -135,10 +214,10 @@ ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const context->FltSave.TagWord = m_fpRegisters.ftw; context->FltSave.ErrorOpcode = m_fpRegisters.fop; - context->FltSave.ErrorOffset = (DWORD)m_fpRegisters.rip; - context->FltSave.ErrorSelector = *(((WORD *)&m_fpRegisters.rip) + 2); - context->FltSave.DataOffset = (DWORD)m_fpRegisters.rdp; - context->FltSave.DataSelector = *(((WORD *)&m_fpRegisters.rdp) + 2); + 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; @@ -150,5 +229,7 @@ ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const memcpy(context->FltSave.XmmRegisters, m_fpRegisters.xmm_space, sizeof(context->FltSave.XmmRegisters)); } // TODO: debug registers? - // TODO: x86 registers +#else +#error Platform not supported +#endif } diff --git a/src/debug/createdump/threadinfo.h b/src/debug/createdump/threadinfo.h index a2aa3ce..8620219 100644 --- a/src/debug/createdump/threadinfo.h +++ b/src/debug/createdump/threadinfo.h @@ -19,7 +19,7 @@ private: public: ThreadInfo(pid_t tid); ~ThreadInfo(); - bool Initialize(); + 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; @@ -36,6 +36,7 @@ public: #endif private: - bool GetRegisters(); + bool GetRegistersWithPTrace(); + bool GetRegistersWithDataTarget(ICLRDataTarget* dataTarget); }; diff --git a/src/inc/releaseholder.h b/src/inc/releaseholder.h new file mode 100644 index 0000000..b2c42e3 --- /dev/null +++ b/src/inc/releaseholder.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. + +// This class acts a smart pointer which calls the Release method on any object +// you place in it when the ReleaseHolder class falls out of scope. You may use it +// just like you would a standard pointer to a COM object (including if (foo), +// if (!foo), if (foo == 0), etc) except for two caveats: +// 1. This class never calls AddRef and it always calls Release when it +// goes out of scope. +// 2. You should never use & to try to get a pointer to a pointer unless +// you call Release first, or you will leak whatever this object contains +// prior to updating its internal pointer. +template +class ReleaseHolder +{ +public: + ReleaseHolder() + : m_ptr(NULL) + {} + + ReleaseHolder(T* ptr) + : m_ptr(ptr) + {} + + ~ReleaseHolder() + { + Release(); + } + + void operator=(T *ptr) + { + Release(); + + m_ptr = ptr; + } + + T* operator->() + { + return m_ptr; + } + + operator T*() + { + return m_ptr; + } + + T** operator&() + { + return &m_ptr; + } + + T* GetPtr() const + { + return m_ptr; + } + + T* Detach() + { + T* pT = m_ptr; + m_ptr = NULL; + return pT; + } + + void Release() + { + if (m_ptr != NULL) + { + m_ptr->Release(); + m_ptr = NULL; + } + } + +private: + T* m_ptr; +}; + diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index e7a2d2f..2a93d3c 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -2894,7 +2894,7 @@ PROCAbortInitialize() return FALSE; } const char* DumpGeneratorName = "createdump"; - int programLen = strlen(g_szCoreCLRPath) + strlen(DumpGeneratorName); + int programLen = strlen(g_szCoreCLRPath) + strlen(DumpGeneratorName) + 1; char* program = (char*)InternalMalloc(programLen); if (program == nullptr) { @@ -2949,7 +2949,7 @@ PROCAbortInitialize() } else if (strcmp(envvar, "3") == 0) { - *argv++ = "--micro"; + *argv++ = "--triage"; } } -- 2.7.4