Uses the createdump utility code as a library.
Changed the "-m, --micro" option to "-t, --triage".
Lots of cleanup.
-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 #
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
)
bpmd
ClrStack
+CreateDump
DumpArray
DumpAssembly
DumpClass
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)
\\
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>
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;
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.");
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
)
#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);
}
const_cast<MemoryRegion&>(region).Cleanup();
}
m_otherMappings.clear();
- m_dataTarget.Release();
+ m_dataTarget->Release();
}
STDMETHODIMP
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);
}
}
// 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;
}
void
CrashInfo::ResumeThreads()
{
- for (ThreadInfo* thread : m_threads)
+ if (!m_sos)
{
- thread->ResumeThread();
+ for (ThreadInfo* thread : m_threads)
+ {
+ thread->ResumeThread();
+ }
}
}
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");
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);
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;
}
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
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);
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; }
#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
#include <mscoree.h>
#include <dumpcommon.h>
#include <arrayholder.h>
+#include <releaseholder.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include "threadinfo.h"
#include "memoryregion.h"
#include "crashinfo.h"
-#include "dumpwriter.h"
+#include "dumpwriter.h"
\ No newline at end of file
#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),
{
}
-DataTarget::~DataTarget()
+DumpDataTarget::~DumpDataTarget()
{
if (m_fd != -1)
{
}
bool
-DataTarget::Initialize(CrashInfo * crashInfo)
+DumpDataTarget::Initialize(CrashInfo * crashInfo)
{
char memPath[128];
_snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%lu/mem", m_pid);
}
STDMETHODIMP
-DataTarget::QueryInterface(
+DumpDataTarget::QueryInterface(
___in REFIID InterfaceId,
___out PVOID* Interface
)
}
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)
}
HRESULT STDMETHODCALLTYPE
-DataTarget::GetMachineType(
+DumpDataTarget::GetMachineType(
/* [out] */ ULONG32 *machine)
{
#ifdef _AMD64_
}
HRESULT STDMETHODCALLTYPE
-DataTarget::GetPointerSize(
+DumpDataTarget::GetPointerSize(
/* [out] */ ULONG32 *size)
{
#if defined(_AMD64_) || defined(_ARM64_)
}
HRESULT STDMETHODCALLTYPE
-DataTarget::GetImageBase(
+DumpDataTarget::GetImageBase(
/* [string][in] */ LPCWSTR moduleName,
/* [out] */ CLRDATA_ADDRESS *baseAddress)
{
}
HRESULT STDMETHODCALLTYPE
-DataTarget::ReadVirtual(
+DumpDataTarget::ReadVirtual(
/* [in] */ CLRDATA_ADDRESS address,
/* [length_is][size_is][out] */ PBYTE buffer,
/* [in] */ ULONG32 size,
}
HRESULT STDMETHODCALLTYPE
-DataTarget::WriteVirtual(
+DumpDataTarget::WriteVirtual(
/* [in] */ CLRDATA_ADDRESS address,
/* [size_is][in] */ PBYTE buffer,
/* [in] */ ULONG32 size,
}
HRESULT STDMETHODCALLTYPE
-DataTarget::GetTLSValue(
+DumpDataTarget::GetTLSValue(
/* [in] */ ULONG32 threadID,
/* [in] */ ULONG32 index,
/* [out] */ CLRDATA_ADDRESS* value)
}
HRESULT STDMETHODCALLTYPE
-DataTarget::SetTLSValue(
+DumpDataTarget::SetTLSValue(
/* [in] */ ULONG32 threadID,
/* [in] */ ULONG32 index,
/* [in] */ CLRDATA_ADDRESS value)
}
HRESULT STDMETHODCALLTYPE
-DataTarget::GetCurrentThreadID(
+DumpDataTarget::GetCurrentThreadID(
/* [out] */ ULONG32* threadID)
{
assert(false);
}
HRESULT STDMETHODCALLTYPE
-DataTarget::GetThreadContext(
+DumpDataTarget::GetThreadContext(
/* [in] */ ULONG32 threadID,
/* [in] */ ULONG32 contextFlags,
/* [in] */ ULONG32 contextSize,
}
HRESULT STDMETHODCALLTYPE
-DataTarget::SetThreadContext(
+DumpDataTarget::SetThreadContext(
/* [in] */ ULONG32 threadID,
/* [in] */ ULONG32 contextSize,
/* [out, size_is(contextSize)] */ PBYTE context)
}
HRESULT STDMETHODCALLTYPE
-DataTarget::Request(
+DumpDataTarget::Request(
/* [in] */ ULONG32 reqCode,
/* [in] */ ULONG32 inBufferSize,
/* [size_is][in] */ BYTE *inBuffer,
}
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
+}
class CrashInfo;
-class DataTarget : public ICLRDataTarget, ICorDebugDataTarget4
+class DumpDataTarget : public ICLRDataTarget, ICorDebugDataTarget4
{
private:
LONG m_ref; // reference count
CrashInfo* m_crashInfo;
public:
- DataTarget(pid_t pid);
- virtual ~DataTarget();
+ DumpDataTarget(pid_t pid);
+ virtual ~DumpDataTarget();
bool Initialize(CrashInfo* crashInfo);
//
/* [in] */ DWORD threadId,
/* [in] */ ULONG32 contextSize,
/* [in, out, size_is(contextSize)] */ PBYTE context);
-};
\ No newline at end of file
+};
#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();
}
close(m_fd);
m_fd = -1;
}
- m_dataTarget.Release();
m_crashInfo.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)
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;
}
done += written;
}
return true;
-}
\ No newline at end of file
+}
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
--- /dev/null
+// 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;
+}
#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)
{
}
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;
}
bool
-ThreadInfo::GetRegisters()
+ThreadInfo::GetRegistersWithPTrace()
{
if (ptrace((__ptrace_request)PTRACE_GETREGS, m_tid, nullptr, &m_gpRegisters) == -1)
{
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
{
ThreadInfo::GetThreadContext(uint32_t flags, CONTEXT* context) const
{
context->ContextFlags = flags;
+#if defined(__x86_64__)
if ((flags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
{
context->Rbp = m_gpRegisters.rbp;
context->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;
memcpy(context->FltSave.XmmRegisters, m_fpRegisters.xmm_space, sizeof(context->FltSave.XmmRegisters));
}
// TODO: debug registers?
- // TODO: x86 registers
+#else
+#error Platform not supported
+#endif
}
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;
#endif
private:
- bool GetRegisters();
+ bool GetRegistersWithPTrace();
+ bool GetRegistersWithDataTarget(ICLRDataTarget* dataTarget);
};
--- /dev/null
+// 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;
+};
+
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)
{
}
else if (strcmp(envvar, "3") == 0)
{
- *argv++ = "--micro";
+ *argv++ = "--triage";
}
}