Skip to content

Commit

Permalink
Add crash report file for VS4Mac Watson (dotnet#54934)
Browse files Browse the repository at this point in the history
Add crash report file for VS4Mac Watson

Add the --crashreport createdump command line option to enable this feature.

Add crash thread (--crashthread) and signal number (--signal) command line options. Use std::vector for g_argCreateDump.

Add the COMPlus_EnableCrashReport environment variable to enable this feature.

Add simple json writer (JsonWriter).

Remote unwinder: special case when encoding == 0 and IP after syscall opcode to popping return address

Remove unwinder: add functionStart return from remote unwind API

Add ModuleInfo struct containing the info about a native or managed module.

Add StackFrame struct containing the info about a native or managed stack frame.

Add signal number to PROCAbort.

Currently the Linux crash report is stubbed out (empty).

Better createdump logging.

Add CrashReportWriter class.
  • Loading branch information
mikem8361 authored Jul 7, 2021
1 parent 0ef56df commit 3d10d70
Show file tree
Hide file tree
Showing 29 changed files with 1,120 additions and 160 deletions.
4 changes: 4 additions & 0 deletions src/coreclr/debug/createdump/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ else(CLR_CMAKE_HOST_WIN32)

add_definitions(-DPAL_STDCPP_COMPAT)

# This is so we can include "version.c"
include_directories(${CMAKE_BINARY_DIR})

set(CREATEDUMP_SOURCES
main.cpp
dumpname.cpp
Expand All @@ -57,6 +60,7 @@ else(CLR_CMAKE_HOST_WIN32)
threadinfo.cpp
datatarget.cpp
dumpwriter.cpp
crashreportwriter.cpp
)

if(CLR_CMAKE_HOST_OSX)
Expand Down
184 changes: 159 additions & 25 deletions src/coreclr/debug/createdump/crashinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
// This is for the PAL_VirtualUnwindOutOfProc read memory adapter.
CrashInfo* g_crashInfo;

CrashInfo::CrashInfo(pid_t pid) :
CrashInfo::CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t signal) :
m_ref(1),
m_pid(pid),
m_ppid(-1)
m_ppid(-1),
m_gatherFrames(gatherFrames),
m_crashThread(crashThread),
m_signal(signal)
{
g_crashInfo = this;
#ifdef __APPLE__
Expand Down Expand Up @@ -307,27 +310,16 @@ CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess)

if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0)
{
ArrayHolder<WCHAR> wszUnicodeName = new (std::nothrow) WCHAR[MAX_LONGPATH + 1];
if (wszUnicodeName == nullptr)
{
fprintf(stderr, "Allocating unicode module name FAILED\n");
result = false;
break;
}
ArrayHolder<WCHAR> wszUnicodeName = new WCHAR[MAX_LONGPATH + 1];
if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName)))
{
ArrayHolder<char> pszName = new (std::nothrow) char[MAX_LONGPATH + 1];
if (pszName == nullptr)
{
fprintf(stderr, "Allocating ascii module name FAILED\n");
result = false;
break;
}
sprintf_s(pszName.GetPtr(), MAX_LONGPATH, "%S", (WCHAR*)wszUnicodeName);
TRACE(" %s\n", pszName.GetPtr());
std::string moduleName = FormatString("%S", wszUnicodeName.GetPtr());

// Change the module mapping name
ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, std::string(pszName.GetPtr()));
ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, moduleName);

// Add managed module info
AddModuleInfo(true, moduleData.LoadedPEAddress, pClrDataModule, moduleName);
}
else {
TRACE("\nModule.GetFileName FAILED %08x\n", hr);
Expand Down Expand Up @@ -369,7 +361,7 @@ CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess)
// Replace an existing module mapping with one with a different name.
//
void
CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const std::string& pszName)
CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const std::string& name)
{
uint64_t start = (uint64_t)baseAddress;
uint64_t end = ((baseAddress + size) + (PAGE_SIZE - 1)) & PAGE_MASK;
Expand All @@ -387,18 +379,18 @@ CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const
if (found == m_moduleMappings.end())
{
// On MacOS the assemblies are always added.
MemoryRegion newRegion(flags, start, end, 0, pszName);
MemoryRegion newRegion(flags, start, end, 0, name);
m_moduleMappings.insert(newRegion);

if (g_diagnostics) {
TRACE("MODULE: ADD ");
newRegion.Trace();
}
}
else if (found->FileName().compare(pszName) != 0)
else if (found->FileName().compare(name) != 0)
{
// Create the new memory region with the managed assembly name.
MemoryRegion newRegion(*found, pszName);
MemoryRegion newRegion(*found, name);

// Remove and cleanup the old one
m_moduleMappings.erase(found);
Expand All @@ -416,9 +408,10 @@ CrashInfo::ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const
//
// Returns the module base address for the IP or 0. Used by the thread unwind code.
//
uint64_t CrashInfo::GetBaseAddress(uint64_t ip)
uint64_t
CrashInfo::GetBaseAddressFromAddress(uint64_t address)
{
MemoryRegion search(0, ip, ip, 0);
MemoryRegion search(0, address, address, 0);
const MemoryRegion* found = SearchMemoryRegions(m_moduleAddresses, search);
if (found == nullptr) {
return 0;
Expand All @@ -427,6 +420,105 @@ uint64_t CrashInfo::GetBaseAddress(uint64_t ip)
return found->Offset();
}

//
// Returns the module base address for the given module name or 0 if not found.
//
uint64_t
CrashInfo::GetBaseAddressFromName(const char* moduleName)
{
for (const ModuleInfo& moduleInfo : m_moduleInfos)
{
std::string name = GetFileName(moduleInfo.ModuleName());
#ifdef __APPLE__
// Module names are case insenstive on MacOS
if (strcasecmp(name.c_str(), moduleName) == 0)
#else
if (name.compare(moduleName) == 0)
#endif
{
return moduleInfo.BaseAddress();
}
}
return 0;
}

//
// Return the module info for the base address
//
const ModuleInfo*
CrashInfo::GetModuleInfoFromBaseAddress(uint64_t baseAddress)
{
ModuleInfo search(baseAddress);
const auto& found = m_moduleInfos.find(search);
if (found != m_moduleInfos.end())
{
return &*found;
}
return nullptr;
}

//
// Adds module address range for IP lookup
//
void
CrashInfo::AddModuleAddressRange(uint64_t startAddress, uint64_t endAddress, uint64_t baseAddress)
{
// Add module segment to base address lookup
MemoryRegion region(0, startAddress, endAddress, baseAddress);
m_moduleAddresses.insert(region);
}

//
// Adds module info (baseAddress, module name, etc)
//
void
CrashInfo::AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule* pClrDataModule, const std::string& moduleName)
{
ModuleInfo moduleInfo(baseAddress);
const auto& found = m_moduleInfos.find(moduleInfo);
if (found == m_moduleInfos.end())
{
uint32_t timeStamp = 0;
uint32_t imageSize = 0;
GUID mvid;
if (isManaged)
{
IMAGE_DOS_HEADER dosHeader;
if (ReadMemory((void*)baseAddress, &dosHeader, sizeof(dosHeader)))
{
WORD magic;
if (ReadMemory((void*)(baseAddress + dosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS, OptionalHeader.Magic)), &magic, sizeof(magic)))
{
if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
IMAGE_NT_HEADERS32 header;
if (ReadMemory((void*)(baseAddress + dosHeader.e_lfanew), &header, sizeof(header)))
{
imageSize = header.OptionalHeader.SizeOfImage;
timeStamp = header.FileHeader.TimeDateStamp;
}
}
else if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
IMAGE_NT_HEADERS64 header;
if (ReadMemory((void*)(baseAddress + dosHeader.e_lfanew), &header, sizeof(header)))
{
imageSize = header.OptionalHeader.SizeOfImage;
timeStamp = header.FileHeader.TimeDateStamp;
}
}
}
}
if (pClrDataModule != nullptr)
{
pClrDataModule->GetVersionId(&mvid);
}
TRACE("MODULE: timestamp %08x size %08x %s %s\n", timeStamp, imageSize, FormatGuid(&mvid).c_str(), moduleName.c_str());
}
m_moduleInfos.insert(ModuleInfo(isManaged, baseAddress, timeStamp, imageSize, &mvid, moduleName));
}
}

//
// ReadMemory from target and add to memory regions list
//
Expand Down Expand Up @@ -644,3 +736,45 @@ CrashInfo::TraceVerbose(const char* format, ...)
va_end(args);
}
}

//
// Returns just the file name portion of a file path
//
const std::string
GetFileName(const std::string& fileName)
{
size_t last = fileName.rfind(DIRECTORY_SEPARATOR_STR_A);
if (last != std::string::npos) {
last++;
}
else {
last = 0;
}
return fileName.substr(last);
}

//
// Formats a std::string with printf syntax. The final formated string is limited
// to MAX_LONGPATH (1024) chars. Returns an empty string on any error.
//
std::string
FormatString(const char* format, ...)
{
ArrayHolder<char> buffer = new char[MAX_LONGPATH + 1];
va_list args;
va_start(args, format);
int result = vsprintf_s(buffer, MAX_LONGPATH, format, args);
va_end(args);
return result > 0 ? std::string(buffer) : std::string();
}

//
// Format a guid
//
std::string
FormatGuid(const GUID* guid)
{
uint8_t* bytes = (uint8_t*)guid;
return FormatString("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
bytes[3], bytes[2], bytes[1], bytes[0], bytes[5], bytes[4], bytes[7], bytes[6], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
}
19 changes: 17 additions & 2 deletions src/coreclr/debug/createdump/crashinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;

#endif

extern const std::string GetFileName(const std::string& fileName);
extern std::string FormatString(const char* format, ...);
extern std::string FormatGuid(const GUID* guid);

class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
#ifdef __APPLE__
public MachOReader
Expand All @@ -42,6 +46,9 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
pid_t m_pid; // pid
pid_t m_ppid; // parent pid
pid_t m_tgid; // process group
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
#ifdef __APPLE__
vm_map_t m_task; // the mach task for the process
Expand All @@ -61,9 +68,10 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
std::set<MemoryRegion> m_otherMappings; // other memory mappings
std::set<MemoryRegion> m_memoryRegions; // memory regions from DAC, etc.
std::set<MemoryRegion> m_moduleAddresses; // memory region to module base address
std::set<ModuleInfo> m_moduleInfos; // module infos (base address and module name)

public:
CrashInfo(pid_t pid);
CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t signal);
virtual ~CrashInfo();

bool Initialize();
Expand All @@ -72,7 +80,11 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
bool GatherCrashInfo(MINIDUMP_TYPE minidumpType);
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 GetBaseAddress(uint64_t ip);
uint64_t GetBaseAddressFromAddress(uint64_t address);
uint64_t GetBaseAddressFromName(const char* moduleName);
const ModuleInfo* GetModuleInfoFromBaseAddress(uint64_t baseAddress);
void AddModuleAddressRange(uint64_t startAddress, uint64_t endAddress, uint64_t baseAddress);
void AddModuleInfo(bool isManaged, uint64_t baseAddress, IXCLRDataModule* pClrDataModule, const std::string& moduleName);
void InsertMemoryRegion(uint64_t address, size_t size);
static const MemoryRegion* SearchMemoryRegions(const std::set<MemoryRegion>& regions, const MemoryRegion& search);

Expand All @@ -82,6 +94,9 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
#ifdef __APPLE__
inline vm_map_t Task() const { return m_task; }
#endif
inline const bool GatherFrames() const { return m_gatherFrames; }
inline const pid_t CrashThread() const { return m_crashThread; }
inline const uint32_t Signal() const { return m_signal; }
inline const std::string& Name() const { return m_name; }

inline const std::vector<ThreadInfo*> Threads() const { return m_threads; }
Expand Down
22 changes: 8 additions & 14 deletions src/coreclr/debug/createdump/crashinfomac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ CrashInfo::EnumerateMemoryRegions()
{
if (region.Contains(*found))
{
MemoryRegion gap(region.Flags(), previousEndAddress, found->StartAddress(), region.Offset(), std::string());
MemoryRegion gap(region.Flags(), previousEndAddress, found->StartAddress(), region.Offset());
if (gap.Size() > 0)
{
TRACE(" Gap: ");
Expand All @@ -182,7 +182,7 @@ CrashInfo::EnumerateMemoryRegions()
}
}

MemoryRegion endgap(region.Flags(), previousEndAddress, region.EndAddress(), region.Offset(), std::string());
MemoryRegion endgap(region.Flags(), previousEndAddress, region.EndAddress(), region.Offset());
if (endgap.Size() > 0)
{
TRACE(" EndGap:");
Expand Down Expand Up @@ -237,17 +237,12 @@ CrashInfo::TryFindDyLinker(mach_vm_address_t address, mach_vm_size_t size, bool*

void CrashInfo::VisitModule(MachOModule& module)
{
AddModuleInfo(false, module.BaseAddress(), nullptr, module.Name());

// Get the process name from the executable module file type
if (m_name.empty() && module.Header().filetype == MH_EXECUTE)
{
size_t last = module.Name().rfind(DIRECTORY_SEPARATOR_STR_A);
if (last != std::string::npos) {
last++;
}
else {
last = 0;
}
m_name = module.Name().substr(last);
m_name = GetFileName(module.Name());
}
// Save the runtime module path
if (m_coreclrPath.empty())
Expand Down Expand Up @@ -291,7 +286,7 @@ void CrashInfo::VisitSegment(MachOModule& module, const segment_command_64& segm
_ASSERTE(end > 0);

// Add module memory region if not already on the list
MemoryRegion moduleRegion(regionFlags, start, end, offset, module.Name());
MemoryRegion moduleRegion(regionFlags, start, end, offset);
const auto& found = m_moduleMappings.find(moduleRegion);
if (found == m_moduleMappings.end())
{
Expand All @@ -303,9 +298,8 @@ void CrashInfo::VisitSegment(MachOModule& module, const segment_command_64& segm
// Add this module segment to the module mappings list
m_moduleMappings.insert(moduleRegion);

// Add module segment ip to base address lookup
MemoryRegion addressRegion(0, start, end, module.BaseAddress());
m_moduleAddresses.insert(addressRegion);
// Add this module segment to the set used by the thread unwinding to lookup the module base address for an ip.
AddModuleAddressRange(start, end, module.BaseAddress());
}
else
{
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/debug/createdump/crashinfounix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ CrashInfo::VisitModule(uint64_t baseAddress, std::string& moduleName)
if (baseAddress == 0 || baseAddress == m_auxvValues[AT_SYSINFO_EHDR]) {
return;
}
AddModuleInfo(false, baseAddress, nullptr, moduleName);
if (m_coreclrPath.empty())
{
size_t last = moduleName.rfind(DIRECTORY_SEPARATOR_STR_A MAKEDLLNAME_A("coreclr"));
Expand Down Expand Up @@ -302,8 +303,7 @@ CrashInfo::VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, Phdr* phd
break;

case PT_LOAD:
MemoryRegion region(0, loadbias + phdr->p_vaddr, loadbias + phdr->p_vaddr + phdr->p_memsz, baseAddress);
m_moduleAddresses.insert(region);
AddModuleAddressRange(loadbias + phdr->p_vaddr, loadbias + phdr->p_vaddr + phdr->p_memsz, baseAddress);
break;
}
}
Expand Down
Loading

0 comments on commit 3d10d70

Please sign in to comment.