Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a createdump static library for single-file dump generation #84864

Merged
merged 7 commits into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/coreclr/debug/createdump/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ if(CLR_CMAKE_HOST_WIN32)

set(CREATEDUMP_SOURCES
main.cpp
createdumpmain.cpp
dumpname.cpp
createdumpwindows.cpp
createdump.rc
Expand Down Expand Up @@ -55,7 +56,7 @@ else(CLR_CMAKE_HOST_WIN32)
include_directories(${CMAKE_BINARY_DIR})

set(CREATEDUMP_SOURCES
main.cpp
createdumpmain.cpp
dumpname.cpp
createdumpunix.cpp
crashinfo.cpp
Expand All @@ -66,25 +67,34 @@ else(CLR_CMAKE_HOST_WIN32)
)

if(CLR_CMAKE_HOST_OSX)
add_executable_clr(createdump
add_library_clr(createdump_static
STATIC
crashinfomac.cpp
threadinfomac.cpp
dumpwritermacho.cpp
${CREATEDUMP_SOURCES}
)
else()
add_executable_clr(createdump
main.cpp
)
else()
add_library_clr(createdump_static
STATIC
crashinfounix.cpp
threadinfounix.cpp
dumpwriterelf.cpp
${CREATEDUMP_SOURCES}
)
add_executable_clr(createdump
main.cpp
${PAL_REDEFINES_FILE}
)
add_dependencies(createdump pal_redefines_file)
endif(CLR_CMAKE_HOST_OSX)

target_link_libraries(createdump
PRIVATE
createdump_static
corguids
dbgutil
# share the PAL in the dac module
Expand Down
133 changes: 81 additions & 52 deletions src/coreclr/debug/createdump/crashinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#include "createdump.h"

typedef BOOL (PALAPI_NOEXPORT *PFN_DLLMAIN)(HINSTANCE, DWORD, LPVOID); /* entry point of module */
typedef HINSTANCE (PALAPI_NOEXPORT *PFN_REGISTER_MODULE)(LPCSTR); /* used to create the HINSTANCE for above DLLMain entry point */

// This is for the PAL_VirtualUnwindOutOfProc read memory adapter.
CrashInfo* g_crashInfo;

Expand All @@ -12,9 +15,10 @@ CrashInfo::CrashInfo(const CreateDumpOptions& options) :
m_ref(1),
m_pid(options.Pid),
m_ppid(-1),
m_hdac(nullptr),
m_dacModule(nullptr),
m_pClrDataEnumRegions(nullptr),
m_pClrDataProcess(nullptr),
m_appModel(options.AppModel),
m_gatherFrames(options.CrashReport),
m_crashThread(options.CrashThread),
m_signal(options.Signal),
Expand All @@ -30,12 +34,12 @@ CrashInfo::CrashInfo(const CreateDumpOptions& options) :
#else
m_auxvValues.fill(0);
m_fdMem = -1;
#endif
memset(&m_siginfo, 0, sizeof(m_siginfo));
m_siginfo.si_signo = options.Signal;
m_siginfo.si_code = options.SignalCode;
m_siginfo.si_errno = options.SignalErrno;
m_siginfo.si_addr = options.SignalAddress;
#endif
}

CrashInfo::~CrashInfo()
Expand Down Expand Up @@ -64,10 +68,10 @@ CrashInfo::~CrashInfo()
m_pClrDataProcess->Release();
}
// Unload DAC module
if (m_hdac != nullptr)
if (m_dacModule != nullptr)
{
FreeLibrary(m_hdac);
m_hdac = nullptr;
dlclose(m_dacModule);
m_dacModule = nullptr;
}
#ifdef __APPLE__
if (m_task != 0)
Expand Down Expand Up @@ -145,7 +149,7 @@ CrashInfo::LogMessage(
// Gather all the necessary crash dump info.
//
bool
CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType)
CrashInfo::GatherCrashInfo(DumpType dumpType)
{
// Get the info about the threads (registers, etc.)
for (ThreadInfo* thread : m_threads)
Expand Down Expand Up @@ -178,7 +182,7 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType)
}
#endif
// Load and initialize DAC interfaces
if (!InitializeDAC())
if (!InitializeDAC(dumpType))
mikem8361 marked this conversation as resolved.
Show resolved Hide resolved
{
return false;
}
Expand All @@ -205,7 +209,7 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType)
}
}
// If full memory dump, include everything regardless of permissions
if (minidumpType & MiniDumpWithFullMemory)
if (dumpType == DumpType::Full)
{
for (const MemoryRegion& region : m_moduleMappings)
{
Expand All @@ -224,7 +228,7 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType)
{
// Add all the heap read/write memory regions (m_otherMappings contains the heaps). On Alpine
// the heap regions are marked RWX instead of just RW.
if (minidumpType & MiniDumpWithPrivateReadWriteMemory)
if (dumpType == DumpType::Heap)
{
for (const MemoryRegion& region : m_otherMappings)
{
Expand Down Expand Up @@ -254,18 +258,18 @@ GetHResultString(HRESULT hr)
{
switch (hr)
{
case E_FAIL:
return "The operation has failed";
case E_INVALIDARG:
return "Invalid argument";
case E_OUTOFMEMORY:
return "Out of memory";
case CORDBG_E_INCOMPATIBLE_PLATFORMS:
return "The operation failed because debuggee and debugger are on incompatible platforms";
case CORDBG_E_MISSING_DEBUGGER_EXPORTS:
return "The debuggee memory space does not have the expected debugging export table";
case CORDBG_E_UNSUPPORTED:
return "The specified action is unsupported by this version of the runtime";
case E_FAIL:
return "The operation has failed";
case E_INVALIDARG:
return "Invalid argument";
case E_OUTOFMEMORY:
return "Out of memory";
case CORDBG_E_INCOMPATIBLE_PLATFORMS:
return "The operation failed because debuggee and debugger are on incompatible platforms";
case CORDBG_E_MISSING_DEBUGGER_EXPORTS:
return "The debuggee memory space does not have the expected debugging export table";
case CORDBG_E_UNSUPPORTED:
return "The specified action is unsupported by this version of the runtime";
}
return "";
}
Expand All @@ -274,49 +278,73 @@ GetHResultString(HRESULT hr)
// Enumerate all the memory regions using the DAC memory region support given a minidump type
//
bool
CrashInfo::InitializeDAC()
CrashInfo::InitializeDAC(DumpType dumpType)
{
// Don't attempt to load the DAC if the app model doesn't support it by default. The default for single-file is a
// full dump, but if the dump type requested is a mini, triage or heap and the DAC is side-by-side to the single-file
// application the core dump will be generated.
if (dumpType == DumpType::Full && (m_appModel == AppModelType::SingleFile || m_appModel == AppModelType::NativeAOT))
{
return true;
}
// Can't load the DAC if the runtime wasn't found
if (m_coreclrPath.empty())
{
printf_error("InitializeDAC: coreclr not found; not using DAC\n");
return true;
}
ReleaseHolder<DumpDataTarget> dataTarget = new DumpDataTarget(*this);
PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr;
PFN_DLLMAIN pfnDllMain = nullptr;
bool result = false;
HRESULT hr = S_OK;

if (!m_coreclrPath.empty())
{
// We assume that the DAC is in the same location as the libcoreclr.so module
std::string dacPath;
dacPath.append(m_coreclrPath);
dacPath.append(MAKEDLLNAME_A("mscordaccore"));
// We assume that the DAC is in the same location as the libcoreclr.so module
std::string dacPath;
dacPath.append(m_coreclrPath);
dacPath.append(MAKEDLLNAME_A("mscordaccore"));

// Load and initialize the DAC
m_hdac = LoadLibraryA(dacPath.c_str());
if (m_hdac == nullptr)
{
printf_error("InitializeDAC: LoadLibraryA(%s) FAILED %s\n", dacPath.c_str(), GetLastErrorString().c_str());
goto exit;
}
pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(m_hdac, "CLRDataCreateInstance");
if (pfnCLRDataCreateInstance == nullptr)
{
printf_error("InitializeDAC: GetProcAddress(CLRDataCreateInstance) FAILED %s\n", GetLastErrorString().c_str());
goto exit;
}
hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&m_pClrDataEnumRegions);
if (FAILED(hr))
// Load and initialize the DAC. We don't use the LoadLibraryA here because the PAL may not be
// initialized properly in the forked process for the statically linked single-file scenario.
m_dacModule = dlopen(dacPath.c_str(), RTLD_LAZY);
mikem8361 marked this conversation as resolved.
Show resolved Hide resolved
if (m_dacModule == nullptr)
{
printf_error("InitializeDAC: dlopen(%s) FAILED %s\n", dacPath.c_str(), dlerror());
goto exit;
}
pfnDllMain = (PFN_DLLMAIN)dlsym(m_dacModule, "DllMain");
if (pfnDllMain != nullptr)
{
PFN_REGISTER_MODULE registerModule = (PFN_REGISTER_MODULE)dlsym(m_dacModule, "PAL_RegisterModule");
if (registerModule == nullptr)
{
printf_error("InitializeDAC: CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %s (%08x)\n", GetHResultString(hr), hr);
printf_error("InitializeDAC: PAL_RegisterModule FAILED\n");
goto exit;
}
hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&m_pClrDataProcess);
if (FAILED(hr))
HINSTANCE hModule = registerModule(dacPath.c_str());
if (!pfnDllMain(hModule, DLL_PROCESS_ATTACH, nullptr))
{
printf_error("InitializeDAC: CLRDataCreateInstance(IXCLRDataProcess) FAILED %s (%08x)\n", GetHResultString(hr), hr);
printf_error("InitializeDAC: DllMain(DLL_PROCESS_ATTACH) FAILED\n");
goto exit;
}
}
else
pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)dlsym(m_dacModule, "CLRDataCreateInstance");
if (pfnCLRDataCreateInstance == nullptr)
{
printf_error("InitializeDAC: coreclr not found; not using DAC\n");
printf_error("InitializeDAC: GetProcAddress(CLRDataCreateInstance) FAILED %s\n", dlerror());
goto exit;
}
hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&m_pClrDataEnumRegions);
if (FAILED(hr))
{
printf_error("InitializeDAC: CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %s (%08x)\n", GetHResultString(hr), hr);
goto exit;
}
hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&m_pClrDataProcess);
if (FAILED(hr))
{
printf_error("InitializeDAC: CLRDataCreateInstance(IXCLRDataProcess) FAILED %s (%08x)\n", GetHResultString(hr), hr);
goto exit;
}
result = true;
exit:
Expand All @@ -327,17 +355,18 @@ CrashInfo::InitializeDAC()
// Enumerate all the memory regions using the DAC memory region support given a minidump type
//
bool
CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType)
CrashInfo::EnumerateMemoryRegionsWithDAC(DumpType dumpType)
{
if (m_pClrDataEnumRegions != nullptr && (minidumpType & MiniDumpWithFullMemory) == 0)
if (m_pClrDataEnumRegions != nullptr && dumpType != DumpType::Full)
{
TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration STARTED (%d %d)\n", m_enumMemoryPagesAdded, m_dataTargetPagesAdded);

// CLRDATA_ENUM_MEM_HEAP2 skips the expensive (in both time and memory usage) enumeration of the
// low level data structures and adds all the loader allocator heaps instead. The older 'DbgEnableFastHeapDumps'
// env var didn't generate a complete enough heap dump on Linux and this new path does.
CLRDataEnumMemoryFlags flags = CLRDATA_ENUM_MEM_HEAP2;
if (minidumpType & MiniDumpWithPrivateReadWriteMemory)
MINIDUMP_TYPE minidumpType = GetMiniDumpType(dumpType);
if (dumpType == DumpType::Heap)
{
// This is the old fast heap env var for backwards compatibility for VS4Mac.
CLRConfigNoCache fastHeapDumps = CLRConfigNoCache::Get("DbgEnableFastHeapDumps", /*noprefix*/ false, &getenv);
Expand Down
22 changes: 10 additions & 12 deletions src/coreclr/debug/createdump/crashinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,24 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi
pid_t m_pid; // pid
pid_t m_ppid; // parent pid
pid_t m_tgid; // process group
HMODULE m_hdac; // dac module handle when loaded
void* m_dacModule; // dac module pointer when loaded
ICLRDataEnumMemoryRegions* m_pClrDataEnumRegions; // dac enumerate memory interface instance
IXCLRDataProcess* m_pClrDataProcess; // dac process interface instance
AppModelType m_appModel; // Normal, single-file or native AOT app.
bool m_gatherFrames; // if true, add the native and managed stack frames to the thread info
pid_t m_crashThread; // crashing thread id or 0 if none
uint32_t m_signal; // crash signal code or 0 if none
std::string m_name; // exe name
siginfo_t m_siginfo; // signal info (if any)
std::string m_coreclrPath; // the path of the coreclr module or empty if none
uint64_t m_runtimeBaseAddress; // base address of the runtime module
#ifdef __APPLE__
vm_map_t m_task; // the mach task for the process
std::set<MemoryRegion> m_allMemoryRegions; // all memory regions on MacOS
#else
siginfo_t m_siginfo; // signal info (if any)
bool m_canUseProcVmReadSyscall;
int m_fdMem; // /proc/<pid>/mem handle
int m_fdPagemap; // /proc/<pid>/pagemap handle
#endif
std::string m_coreclrPath; // the path of the coreclr module or empty if none
uint64_t m_runtimeBaseAddress;
#ifdef __APPLE__
std::set<MemoryRegion> m_allMemoryRegions; // all memory regions on MacOS
#else
std::array<elf_aux_val_t, AT_MAX> m_auxvValues; // auxv values
std::vector<elf_aux_entry> m_auxvEntries; // full auxv entries
#endif
Expand Down Expand Up @@ -95,9 +93,9 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi
bool Initialize();
void CleanupAndResumeProcess();
bool EnumerateAndSuspendThreads();
bool GatherCrashInfo(MINIDUMP_TYPE minidumpType);
bool GatherCrashInfo(DumpType dumpType);
void CombineMemoryRegions();
bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType);
bool EnumerateMemoryRegionsWithDAC(DumpType dumpType);
bool ReadMemory(void* address, void* buffer, size_t size); // read memory and add to dump
bool ReadProcessMemory(void* address, void* buffer, size_t size, size_t* read); // read raw memory
uint64_t GetBaseAddressFromAddress(uint64_t address);
Expand Down Expand Up @@ -125,10 +123,10 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi
inline const std::set<MemoryRegion>& ModuleMappings() const { return m_moduleMappings; }
inline const std::set<MemoryRegion>& OtherMappings() const { return m_otherMappings; }
inline const std::set<MemoryRegion>& MemoryRegions() const { return m_memoryRegions; }
inline const siginfo_t* SigInfo() const { return &m_siginfo; }
#ifndef __APPLE__
inline const std::vector<elf_aux_entry>& AuxvEntries() const { return m_auxvEntries; }
inline size_t GetAuxvSize() const { return m_auxvEntries.size() * sizeof(elf_aux_entry); }
inline const siginfo_t* SigInfo() const { return &m_siginfo; }
#endif

// IUnknown
Expand Down Expand Up @@ -156,7 +154,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, public ICLRDataLoggi
void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr);
bool EnumerateMemoryRegions();
#endif
bool InitializeDAC();
bool InitializeDAC(DumpType dumpType);
bool EnumerateManagedModules();
bool UnwindAllThreads();
void AddOrReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const std::string& pszName);
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/debug/createdump/crashinfomac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ void CrashInfo::VisitModule(MachOModule& module)
TRACE("TryLookupSymbol(" DACCESS_TABLE_SYMBOL ") FAILED\n");
}
}
else if (g_checkForSingleFile)
else if (m_appModel == AppModelType::SingleFile)
{
uint64_t symbolOffset;
if (module.TryLookupSymbol("DotNetRuntimeInfo", &symbolOffset))
Expand Down
Loading