Add "createdump" command to SOS. (#11161)
authorMike McLaughlin <mikem@microsoft.com>
Tue, 25 Apr 2017 00:12:36 +0000 (17:12 -0700)
committerGitHub <noreply@github.com>
Tue, 25 Apr 2017 00:12:36 +0000 (17:12 -0700)
Uses the createdump utility code as a library.

Changed the "-m, --micro" option to "-t, --triage".

Lots of cleanup.

20 files changed:
Documentation/botr/xplat-minidump-generation.md
src/ToolBox/SOS/Strike/CMakeLists.txt
src/ToolBox/SOS/Strike/sos_unixexports.src
src/ToolBox/SOS/Strike/sosdocsunix.txt
src/ToolBox/SOS/Strike/strike.cpp
src/ToolBox/SOS/lldbplugin/soscommand.cpp
src/debug/createdump/CMakeLists.txt
src/debug/createdump/crashinfo.cpp
src/debug/createdump/crashinfo.h
src/debug/createdump/createdump.cpp
src/debug/createdump/createdump.h
src/debug/createdump/datatarget.cpp
src/debug/createdump/datatarget.h
src/debug/createdump/dumpwriter.cpp
src/debug/createdump/dumpwriter.h
src/debug/createdump/main.cpp [new file with mode: 0644]
src/debug/createdump/threadinfo.cpp
src/debug/createdump/threadinfo.h
src/inc/releaseholder.h [new file with mode: 0644]
src/pal/src/thread/process.cpp

index 495ecff..78660b6 100644 (file)
@@ -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 #
index 8ba0ade..ff5f864 100644 (file)
@@ -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
   )
index ed811b6..a8cc712 100644 (file)
@@ -4,6 +4,7 @@
 
 bpmd
 ClrStack
+CreateDump
 DumpArray
 DumpAssembly
 DumpClass
index 5ab2b31..517227c 100644 (file)
@@ -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 <Code address>
 
index 2b54b4f..0d5d8c3 100644 (file)
@@ -14368,6 +14368,80 @@ _EFN_GetManagedObjectFieldInfo(
     return S_OK;
 }
 
+#ifdef FEATURE_PAL
+
+#ifdef __linux__
+#include <dumpcommon.h>
+#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<ICLRDataTarget> 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;
index 4ca7ce3..64d198c 100644 (file)
@@ -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.");
index 081a308..5b5ec0a 100644 (file)
@@ -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
 )
index f92630b..cae8857 100644 (file)
@@ -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<MemoryRegion&>(region).Cleanup();
     }
     m_otherMappings.clear();
-    m_dataTarget.Release();
+    m_dataTarget->Release();
 }
 
 STDMETHODIMP
@@ -107,22 +108,25 @@ CrashInfo::EnumerateAndSuspendThreads()
         pid_t tid = static_cast<pid_t>(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<char> 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<CLRDATA_ADDRESS>(address), reinterpret_cast<PBYTE>(buffer), size, &read)))
+    if (FAILED(m_dataTarget->ReadVirtual(reinterpret_cast<CLRDATA_ADDRESS>(address), reinterpret_cast<PBYTE>(buffer), size, &read)))
     {
         return false;
     }
index bdd19cf..40d7f5d 100644 (file)
@@ -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<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
@@ -31,7 +32,7 @@ private:
     std::set<MemoryRegion> 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<ThreadInfo*> Threads() const { return m_threads; }
     const std::set<MemoryRegion> ModuleMappings()  const { return m_moduleMappings; }
index 9e6af4b..0a95e53 100644 (file)
 
 #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> 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";
-    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> crashInfo = new CrashInfo(pid, dataTarget, true);
+    return CreateDumpCommon(programPath, dumpPathTemplate, minidumpType, crashInfo);
+}
\ No newline at end of file
index 5a8d52d..38c3525 100644 (file)
@@ -34,6 +34,7 @@ extern bool g_diagnostics;
 #include <mscoree.h>
 #include <dumpcommon.h>
 #include <arrayholder.h>
+#include <releaseholder.h>
 #include <unistd.h>
 #include <signal.h>
 #include <sys/types.h>
@@ -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
index b8c09e0..38505e2 100644 (file)
@@ -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
+}
index 802c9d6..8ff6773 100644 (file)
@@ -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
+};
index cec19e8..9057d18 100644 (file)
@@ -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
+}
index 272a2eb..0b4f88c 100644 (file)
@@ -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 (file)
index 0000000..0338277
--- /dev/null
@@ -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;
+}
index 52af060..8e73fcf 100644 (file)
@@ -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<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
 {
@@ -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
 }
index a2aa3ce..8620219 100644 (file)
@@ -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 (file)
index 0000000..b2c42e3
--- /dev/null
@@ -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 T>
+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;    
+};
+
index e7a2d2f..2a93d3c 100644 (file)
@@ -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";
             }
         }