MinidumpParsing: pid, modules, exceptions, strings
Summary: Added parsing of the MiscInfo data stream. The main member of it that we care about is the process_id On Linux generated Minidump (from breakpad) we don't have the MiscInfo, we have the /proc/$pid/status from where we can get the pid. Also parsing the module list - the list of all of the loaded modules/shared libraries. Parsing the exception stream. Parsing MinidumpStrings. I have unit tests for all of that. Also added some tests using a Minidump generated from Windows tools (not from breakpad) Reviewers: labath, zturner Subscribers: beanz, lldb-commits Differential Revision: https://reviews.llvm.org/D24385 llvm-svn: 281348
This commit is contained in:
parent
45b467523b
commit
1d2859ef6d
|
@ -1,5 +1,4 @@
|
|||
//===-- MinidumpParser.cpp ---------------------------------------*- C++
|
||||
//-*-===//
|
||||
//===-- MinidumpParser.cpp ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
|
@ -56,13 +55,12 @@ MinidumpParser::Create(const lldb::DataBufferSP &data_buf_sp) {
|
|||
directory->location;
|
||||
}
|
||||
|
||||
MinidumpParser parser(data_buf_sp, header, directory_map);
|
||||
return llvm::Optional<MinidumpParser>(parser);
|
||||
return MinidumpParser(data_buf_sp, header, std::move(directory_map));
|
||||
}
|
||||
|
||||
MinidumpParser::MinidumpParser(
|
||||
const lldb::DataBufferSP &data_buf_sp, const MinidumpHeader *header,
|
||||
const llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &directory_map)
|
||||
llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &&directory_map)
|
||||
: m_data_sp(data_buf_sp), m_header(header), m_directory_map(directory_map) {
|
||||
}
|
||||
|
||||
|
@ -70,50 +68,48 @@ lldb::offset_t MinidumpParser::GetByteSize() {
|
|||
return m_data_sp->GetByteSize();
|
||||
}
|
||||
|
||||
llvm::Optional<llvm::ArrayRef<uint8_t>>
|
||||
llvm::ArrayRef<uint8_t>
|
||||
MinidumpParser::GetStream(MinidumpStreamType stream_type) {
|
||||
auto iter = m_directory_map.find(static_cast<uint32_t>(stream_type));
|
||||
if (iter == m_directory_map.end())
|
||||
return llvm::None;
|
||||
return {};
|
||||
|
||||
// check if there is enough data
|
||||
if (iter->second.rva + iter->second.data_size > m_data_sp->GetByteSize())
|
||||
return llvm::None;
|
||||
return {};
|
||||
|
||||
llvm::ArrayRef<uint8_t> arr_ref(m_data_sp->GetBytes() + iter->second.rva,
|
||||
iter->second.data_size);
|
||||
return llvm::Optional<llvm::ArrayRef<uint8_t>>(arr_ref);
|
||||
return llvm::ArrayRef<uint8_t>(m_data_sp->GetBytes() + iter->second.rva,
|
||||
iter->second.data_size);
|
||||
}
|
||||
|
||||
llvm::Optional<std::vector<const MinidumpThread *>>
|
||||
MinidumpParser::GetThreads() {
|
||||
llvm::Optional<llvm::ArrayRef<uint8_t>> data =
|
||||
GetStream(MinidumpStreamType::ThreadList);
|
||||
llvm::Optional<std::string> MinidumpParser::GetMinidumpString(uint32_t rva) {
|
||||
auto arr_ref = m_data_sp->GetData();
|
||||
if (rva > arr_ref.size())
|
||||
return llvm::None;
|
||||
arr_ref = arr_ref.drop_front(rva);
|
||||
return parseMinidumpString(arr_ref);
|
||||
}
|
||||
|
||||
if (!data)
|
||||
llvm::ArrayRef<MinidumpThread> MinidumpParser::GetThreads() {
|
||||
llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ThreadList);
|
||||
|
||||
if (data.size() == 0)
|
||||
return llvm::None;
|
||||
|
||||
return MinidumpThread::ParseThreadList(data.getValue());
|
||||
return MinidumpThread::ParseThreadList(data);
|
||||
}
|
||||
|
||||
const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() {
|
||||
llvm::Optional<llvm::ArrayRef<uint8_t>> data =
|
||||
GetStream(MinidumpStreamType::SystemInfo);
|
||||
llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::SystemInfo);
|
||||
|
||||
if (!data)
|
||||
if (data.size() == 0)
|
||||
return nullptr;
|
||||
|
||||
return MinidumpSystemInfo::Parse(data.getValue());
|
||||
return MinidumpSystemInfo::Parse(data);
|
||||
}
|
||||
|
||||
ArchSpec MinidumpParser::GetArchitecture() {
|
||||
ArchSpec arch_spec;
|
||||
arch_spec.GetTriple().setOS(llvm::Triple::OSType::UnknownOS);
|
||||
arch_spec.GetTriple().setVendor(llvm::Triple::VendorType::UnknownVendor);
|
||||
arch_spec.GetTriple().setArch(llvm::Triple::ArchType::UnknownArch);
|
||||
|
||||
// TODO should we add the OS type here, or somewhere else ?
|
||||
|
||||
const MinidumpSystemInfo *system_info = GetSystemInfo();
|
||||
|
||||
if (!system_info)
|
||||
|
@ -122,35 +118,110 @@ ArchSpec MinidumpParser::GetArchitecture() {
|
|||
// TODO what to do about big endiand flavors of arm ?
|
||||
// TODO set the arm subarch stuff if the minidump has info about it
|
||||
|
||||
llvm::Triple triple;
|
||||
triple.setVendor(llvm::Triple::VendorType::UnknownVendor);
|
||||
|
||||
const MinidumpCPUArchitecture arch =
|
||||
static_cast<const MinidumpCPUArchitecture>(
|
||||
static_cast<const uint32_t>(system_info->processor_arch));
|
||||
|
||||
switch (arch) {
|
||||
case MinidumpCPUArchitecture::X86:
|
||||
arch_spec.GetTriple().setArch(llvm::Triple::ArchType::x86);
|
||||
triple.setArch(llvm::Triple::ArchType::x86);
|
||||
break;
|
||||
case MinidumpCPUArchitecture::AMD64:
|
||||
arch_spec.GetTriple().setArch(llvm::Triple::ArchType::x86_64);
|
||||
triple.setArch(llvm::Triple::ArchType::x86_64);
|
||||
break;
|
||||
case MinidumpCPUArchitecture::ARM:
|
||||
arch_spec.GetTriple().setArch(llvm::Triple::ArchType::arm);
|
||||
triple.setArch(llvm::Triple::ArchType::arm);
|
||||
break;
|
||||
case MinidumpCPUArchitecture::ARM64:
|
||||
arch_spec.GetTriple().setArch(llvm::Triple::ArchType::aarch64);
|
||||
triple.setArch(llvm::Triple::ArchType::aarch64);
|
||||
break;
|
||||
default:
|
||||
triple.setArch(llvm::Triple::ArchType::UnknownArch);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const MinidumpOSPlatform os = static_cast<const MinidumpOSPlatform>(
|
||||
static_cast<const uint32_t>(system_info->platform_id));
|
||||
|
||||
// TODO add all of the OSes that Minidump/breakpad distinguishes?
|
||||
switch (os) {
|
||||
case MinidumpOSPlatform::Win32S:
|
||||
case MinidumpOSPlatform::Win32Windows:
|
||||
case MinidumpOSPlatform::Win32NT:
|
||||
case MinidumpOSPlatform::Win32CE:
|
||||
triple.setOS(llvm::Triple::OSType::Win32);
|
||||
break;
|
||||
case MinidumpOSPlatform::Linux:
|
||||
triple.setOS(llvm::Triple::OSType::Linux);
|
||||
break;
|
||||
case MinidumpOSPlatform::MacOSX:
|
||||
triple.setOS(llvm::Triple::OSType::MacOSX);
|
||||
break;
|
||||
case MinidumpOSPlatform::Android:
|
||||
triple.setOS(llvm::Triple::OSType::Linux);
|
||||
triple.setEnvironment(llvm::Triple::EnvironmentType::Android);
|
||||
break;
|
||||
default:
|
||||
triple.setOS(llvm::Triple::OSType::UnknownOS);
|
||||
break;
|
||||
}
|
||||
|
||||
arch_spec.SetTriple(triple);
|
||||
|
||||
return arch_spec;
|
||||
}
|
||||
|
||||
const MinidumpMiscInfo *MinidumpParser::GetMiscInfo() {
|
||||
llvm::Optional<llvm::ArrayRef<uint8_t>> data =
|
||||
GetStream(MinidumpStreamType::MiscInfo);
|
||||
llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MiscInfo);
|
||||
|
||||
if (!data)
|
||||
if (data.size() == 0)
|
||||
return nullptr;
|
||||
|
||||
return MinidumpMiscInfo::Parse(data.getValue());
|
||||
return MinidumpMiscInfo::Parse(data);
|
||||
}
|
||||
|
||||
llvm::Optional<LinuxProcStatus> MinidumpParser::GetLinuxProcStatus() {
|
||||
llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::LinuxProcStatus);
|
||||
|
||||
if (data.size() == 0)
|
||||
return llvm::None;
|
||||
|
||||
return LinuxProcStatus::Parse(data);
|
||||
}
|
||||
|
||||
llvm::Optional<lldb::pid_t> MinidumpParser::GetPid() {
|
||||
const MinidumpMiscInfo *misc_info = GetMiscInfo();
|
||||
if (misc_info != nullptr) {
|
||||
return misc_info->GetPid();
|
||||
}
|
||||
|
||||
llvm::Optional<LinuxProcStatus> proc_status = GetLinuxProcStatus();
|
||||
if (proc_status.hasValue()) {
|
||||
return proc_status->GetPid();
|
||||
}
|
||||
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
llvm::ArrayRef<MinidumpModule> MinidumpParser::GetModuleList() {
|
||||
llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::ModuleList);
|
||||
|
||||
if (data.size() == 0)
|
||||
return {};
|
||||
|
||||
return MinidumpModule::ParseModuleList(data);
|
||||
}
|
||||
|
||||
const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
|
||||
llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::Exception);
|
||||
|
||||
if (data.size() == 0)
|
||||
return nullptr;
|
||||
|
||||
return MinidumpExceptionStream::Parse(data);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
// C includes
|
||||
|
||||
|
@ -40,10 +41,11 @@ public:
|
|||
|
||||
lldb::offset_t GetByteSize();
|
||||
|
||||
llvm::Optional<llvm::ArrayRef<uint8_t>>
|
||||
GetStream(MinidumpStreamType stream_type);
|
||||
llvm::ArrayRef<uint8_t> GetStream(MinidumpStreamType stream_type);
|
||||
|
||||
llvm::Optional<std::vector<const MinidumpThread *>> GetThreads();
|
||||
llvm::Optional<std::string> GetMinidumpString(uint32_t rva);
|
||||
|
||||
llvm::ArrayRef<MinidumpThread> GetThreads();
|
||||
|
||||
const MinidumpSystemInfo *GetSystemInfo();
|
||||
|
||||
|
@ -51,15 +53,22 @@ public:
|
|||
|
||||
const MinidumpMiscInfo *GetMiscInfo();
|
||||
|
||||
llvm::Optional<LinuxProcStatus> GetLinuxProcStatus();
|
||||
|
||||
llvm::Optional<lldb::pid_t> GetPid();
|
||||
|
||||
llvm::ArrayRef<MinidumpModule> GetModuleList();
|
||||
|
||||
const MinidumpExceptionStream *GetExceptionStream();
|
||||
|
||||
private:
|
||||
lldb::DataBufferSP m_data_sp;
|
||||
const MinidumpHeader *m_header;
|
||||
llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> m_directory_map;
|
||||
|
||||
MinidumpParser(const lldb::DataBufferSP &data_buf_sp,
|
||||
const MinidumpHeader *header,
|
||||
const llvm::DenseMap<uint32_t, MinidumpLocationDescriptor>
|
||||
&directory_map);
|
||||
MinidumpParser(
|
||||
const lldb::DataBufferSP &data_buf_sp, const MinidumpHeader *header,
|
||||
llvm::DenseMap<uint32_t, MinidumpLocationDescriptor> &&directory_map);
|
||||
};
|
||||
|
||||
} // namespace minidump
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
// Project includes
|
||||
#include "MinidumpTypes.h"
|
||||
#include "MinidumpParser.h"
|
||||
|
||||
// Other libraries and framework includes
|
||||
// C includes
|
||||
|
@ -40,6 +39,35 @@ const MinidumpHeader *MinidumpHeader::Parse(llvm::ArrayRef<uint8_t> &data) {
|
|||
return header;
|
||||
}
|
||||
|
||||
// Minidump string
|
||||
llvm::Optional<std::string>
|
||||
lldb_private::minidump::parseMinidumpString(llvm::ArrayRef<uint8_t> &data) {
|
||||
std::string result;
|
||||
|
||||
const uint32_t *source_length;
|
||||
Error error = consumeObject(data, source_length);
|
||||
if (error.Fail() || *source_length > data.size() || *source_length % 2 != 0)
|
||||
return llvm::None;
|
||||
|
||||
auto source_start = reinterpret_cast<const UTF16 *>(data.data());
|
||||
// source_length is the length of the string in bytes
|
||||
// we need the length of the string in UTF-16 characters/code points (16 bits
|
||||
// per char)
|
||||
// that's why it's divided by 2
|
||||
const auto source_end = source_start + (*source_length) / 2;
|
||||
// resize to worst case length
|
||||
result.resize(UNI_MAX_UTF8_BYTES_PER_CODE_POINT * (*source_length) / 2);
|
||||
auto result_start = reinterpret_cast<UTF8 *>(&result[0]);
|
||||
const auto result_end = result_start + result.size();
|
||||
ConvertUTF16toUTF8(&source_start, source_end, &result_start, result_end,
|
||||
strictConversion);
|
||||
const auto result_size =
|
||||
std::distance(reinterpret_cast<UTF8 *>(&result[0]), result_start);
|
||||
result.resize(result_size); // shrink to actual length
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// MinidumpThread
|
||||
const MinidumpThread *MinidumpThread::Parse(llvm::ArrayRef<uint8_t> &data) {
|
||||
const MinidumpThread *thread = nullptr;
|
||||
|
@ -50,24 +78,15 @@ const MinidumpThread *MinidumpThread::Parse(llvm::ArrayRef<uint8_t> &data) {
|
|||
return thread;
|
||||
}
|
||||
|
||||
llvm::Optional<std::vector<const MinidumpThread *>>
|
||||
llvm::ArrayRef<MinidumpThread>
|
||||
MinidumpThread::ParseThreadList(llvm::ArrayRef<uint8_t> &data) {
|
||||
std::vector<const MinidumpThread *> thread_list;
|
||||
|
||||
const llvm::support::ulittle32_t *thread_count;
|
||||
Error error = consumeObject(data, thread_count);
|
||||
if (error.Fail())
|
||||
return llvm::None;
|
||||
if (error.Fail() || *thread_count * sizeof(MinidumpThread) > data.size())
|
||||
return {};
|
||||
|
||||
const MinidumpThread *thread;
|
||||
for (uint32_t i = 0; i < *thread_count; ++i) {
|
||||
thread = MinidumpThread::Parse(data);
|
||||
if (thread == nullptr)
|
||||
return llvm::None;
|
||||
thread_list.push_back(thread);
|
||||
}
|
||||
|
||||
return llvm::Optional<std::vector<const MinidumpThread *>>(thread_list);
|
||||
return llvm::ArrayRef<MinidumpThread>(
|
||||
reinterpret_cast<const MinidumpThread *>(data.data()), *thread_count);
|
||||
}
|
||||
|
||||
// MinidumpSystemInfo
|
||||
|
@ -90,3 +109,70 @@ const MinidumpMiscInfo *MinidumpMiscInfo::Parse(llvm::ArrayRef<uint8_t> &data) {
|
|||
|
||||
return misc_info;
|
||||
}
|
||||
|
||||
llvm::Optional<lldb::pid_t> MinidumpMiscInfo::GetPid() const {
|
||||
uint32_t pid_flag =
|
||||
static_cast<const uint32_t>(MinidumpMiscInfoFlags::ProcessID);
|
||||
if (flags1 & pid_flag)
|
||||
return llvm::Optional<lldb::pid_t>(process_id);
|
||||
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
// Linux Proc Status
|
||||
// it's stored as an ascii string in the file
|
||||
llvm::Optional<LinuxProcStatus>
|
||||
LinuxProcStatus::Parse(llvm::ArrayRef<uint8_t> &data) {
|
||||
LinuxProcStatus result;
|
||||
result.proc_status =
|
||||
llvm::StringRef(reinterpret_cast<const char *>(data.data()), data.size());
|
||||
data = data.drop_front(data.size());
|
||||
|
||||
llvm::SmallVector<llvm::StringRef, 0> lines;
|
||||
result.proc_status.split(lines, '\n', 42);
|
||||
// /proc/$pid/status has 41 lines, but why not use 42?
|
||||
for (auto line : lines) {
|
||||
if (line.consume_front("Pid:")) {
|
||||
line = line.trim();
|
||||
if (!line.getAsInteger(10, result.pid))
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
lldb::pid_t LinuxProcStatus::GetPid() const { return pid; }
|
||||
|
||||
// Module stuff
|
||||
const MinidumpModule *MinidumpModule::Parse(llvm::ArrayRef<uint8_t> &data) {
|
||||
const MinidumpModule *module = nullptr;
|
||||
Error error = consumeObject(data, module);
|
||||
if (error.Fail())
|
||||
return nullptr;
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
llvm::ArrayRef<MinidumpModule>
|
||||
MinidumpModule::ParseModuleList(llvm::ArrayRef<uint8_t> &data) {
|
||||
|
||||
const llvm::support::ulittle32_t *modules_count;
|
||||
Error error = consumeObject(data, modules_count);
|
||||
if (error.Fail() || *modules_count * sizeof(MinidumpModule) > data.size())
|
||||
return {};
|
||||
|
||||
return llvm::ArrayRef<MinidumpModule>(
|
||||
reinterpret_cast<const MinidumpModule *>(data.data()), *modules_count);
|
||||
}
|
||||
|
||||
// Exception stuff
|
||||
const MinidumpExceptionStream *
|
||||
MinidumpExceptionStream::Parse(llvm::ArrayRef<uint8_t> &data) {
|
||||
const MinidumpExceptionStream *exception_stream = nullptr;
|
||||
Error error = consumeObject(data, exception_stream);
|
||||
if (error.Fail())
|
||||
return nullptr;
|
||||
|
||||
return exception_stream;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/BitmaskEnum.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/ConvertUTF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
// C includes
|
||||
|
@ -148,6 +151,12 @@ enum class MinidumpPCPUInformationARMElfHwCaps : uint32_t {
|
|||
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ IDIVT)
|
||||
};
|
||||
|
||||
enum class MinidumpMiscInfoFlags : uint32_t {
|
||||
ProcessID = (1 << 0),
|
||||
ProcessTimes = (1 << 1),
|
||||
LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ ProcessTimes)
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Error consumeObject(llvm::ArrayRef<uint8_t> &Buffer, const T *&Object) {
|
||||
Error error;
|
||||
|
@ -161,6 +170,11 @@ Error consumeObject(llvm::ArrayRef<uint8_t> &Buffer, const T *&Object) {
|
|||
return error;
|
||||
}
|
||||
|
||||
// parse a MinidumpString which is with UTF-16
|
||||
// Reference:
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680395(v=vs.85).aspx
|
||||
llvm::Optional<std::string> parseMinidumpString(llvm::ArrayRef<uint8_t> &data);
|
||||
|
||||
// Reference:
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680378(v=vs.85).aspx
|
||||
struct MinidumpHeader {
|
||||
|
@ -219,7 +233,7 @@ struct MinidumpThread {
|
|||
|
||||
static const MinidumpThread *Parse(llvm::ArrayRef<uint8_t> &data);
|
||||
|
||||
static llvm::Optional<std::vector<const MinidumpThread *>>
|
||||
static llvm::ArrayRef<MinidumpThread>
|
||||
ParseThreadList(llvm::ArrayRef<uint8_t> &data);
|
||||
};
|
||||
static_assert(sizeof(MinidumpThread) == 48,
|
||||
|
@ -272,12 +286,12 @@ struct MinidumpSystemInfo {
|
|||
static_assert(sizeof(MinidumpSystemInfo) == 56,
|
||||
"sizeof MinidumpSystemInfo is not correct!");
|
||||
|
||||
// TODO check flags to see what's valid
|
||||
// TODO misc2, misc3 ?
|
||||
// Reference:
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680389(v=vs.85).aspx
|
||||
struct MinidumpMiscInfo {
|
||||
llvm::support::ulittle32_t size;
|
||||
// flags1 represents what info in the struct is valid
|
||||
llvm::support::ulittle32_t flags1;
|
||||
llvm::support::ulittle32_t process_id;
|
||||
llvm::support::ulittle32_t process_create_time;
|
||||
|
@ -285,10 +299,94 @@ struct MinidumpMiscInfo {
|
|||
llvm::support::ulittle32_t process_kernel_time;
|
||||
|
||||
static const MinidumpMiscInfo *Parse(llvm::ArrayRef<uint8_t> &data);
|
||||
|
||||
llvm::Optional<lldb::pid_t> GetPid() const;
|
||||
};
|
||||
static_assert(sizeof(MinidumpMiscInfo) == 24,
|
||||
"sizeof MinidumpMiscInfo is not correct!");
|
||||
|
||||
// The /proc/pid/status is saved as an ascii string in the file
|
||||
class LinuxProcStatus {
|
||||
public:
|
||||
llvm::StringRef proc_status;
|
||||
lldb::pid_t pid;
|
||||
|
||||
static llvm::Optional<LinuxProcStatus> Parse(llvm::ArrayRef<uint8_t> &data);
|
||||
|
||||
lldb::pid_t GetPid() const;
|
||||
|
||||
private:
|
||||
LinuxProcStatus() = default;
|
||||
};
|
||||
|
||||
// MinidumpModule stuff
|
||||
struct MinidumpVSFixedFileInfo {
|
||||
llvm::support::ulittle32_t signature;
|
||||
llvm::support::ulittle32_t struct_version;
|
||||
llvm::support::ulittle32_t file_version_hi;
|
||||
llvm::support::ulittle32_t file_version_lo;
|
||||
llvm::support::ulittle32_t product_version_hi;
|
||||
llvm::support::ulittle32_t product_version_lo;
|
||||
// file_flags_mask - identifies valid bits in fileFlags
|
||||
llvm::support::ulittle32_t file_flags_mask;
|
||||
llvm::support::ulittle32_t file_flags;
|
||||
llvm::support::ulittle32_t file_os;
|
||||
llvm::support::ulittle32_t file_type;
|
||||
llvm::support::ulittle32_t file_subtype;
|
||||
llvm::support::ulittle32_t file_date_hi;
|
||||
llvm::support::ulittle32_t file_date_lo;
|
||||
};
|
||||
static_assert(sizeof(MinidumpVSFixedFileInfo) == 52,
|
||||
"sizeof MinidumpVSFixedFileInfo is not correct!");
|
||||
|
||||
struct MinidumpModule {
|
||||
llvm::support::ulittle64_t base_of_image;
|
||||
llvm::support::ulittle32_t size_of_image;
|
||||
llvm::support::ulittle32_t checksum;
|
||||
llvm::support::ulittle32_t time_date_stamp;
|
||||
llvm::support::ulittle32_t module_name_rva;
|
||||
MinidumpVSFixedFileInfo version_info;
|
||||
MinidumpLocationDescriptor CV_record;
|
||||
MinidumpLocationDescriptor misc_record;
|
||||
llvm::support::ulittle32_t reserved0[2];
|
||||
llvm::support::ulittle32_t reserved1[2];
|
||||
|
||||
static const MinidumpModule *Parse(llvm::ArrayRef<uint8_t> &data);
|
||||
|
||||
static llvm::ArrayRef<MinidumpModule>
|
||||
ParseModuleList(llvm::ArrayRef<uint8_t> &data);
|
||||
};
|
||||
static_assert(sizeof(MinidumpModule) == 108,
|
||||
"sizeof MinidumpVSFixedFileInfo is not correct!");
|
||||
|
||||
// Exception stuff
|
||||
struct MinidumpException {
|
||||
enum {
|
||||
MaxParams = 15,
|
||||
};
|
||||
|
||||
llvm::support::ulittle32_t exception_code;
|
||||
llvm::support::ulittle32_t exception_flags;
|
||||
llvm::support::ulittle64_t exception_record;
|
||||
llvm::support::ulittle64_t exception_address;
|
||||
llvm::support::ulittle32_t number_parameters;
|
||||
llvm::support::ulittle32_t unused_alignment;
|
||||
llvm::support::ulittle64_t exception_information[MaxParams];
|
||||
};
|
||||
static_assert(sizeof(MinidumpException) == 152,
|
||||
"sizeof MinidumpException is not correct!");
|
||||
|
||||
struct MinidumpExceptionStream {
|
||||
llvm::support::ulittle32_t thread_id;
|
||||
llvm::support::ulittle32_t alignment;
|
||||
MinidumpException exception_record;
|
||||
MinidumpLocationDescriptor thread_context;
|
||||
|
||||
static const MinidumpExceptionStream *Parse(llvm::ArrayRef<uint8_t> &data);
|
||||
};
|
||||
static_assert(sizeof(MinidumpExceptionStream) == 168,
|
||||
"sizeof MinidumpExceptionStream is not correct!");
|
||||
|
||||
} // namespace minidump
|
||||
} // namespace lldb_private
|
||||
#endif // liblldb_MinidumpTypes_h_
|
||||
|
|
|
@ -3,6 +3,7 @@ add_lldb_unittest(LLDBMinidumpTests
|
|||
)
|
||||
|
||||
set(test_inputs
|
||||
linux-x86_64.dmp)
|
||||
linux-x86_64.dmp
|
||||
fizzbuzz_no_heap.dmp)
|
||||
|
||||
add_unittest_inputs(LLDBMinidumpTests "${test_inputs}")
|
||||
|
|
Binary file not shown.
|
@ -59,33 +59,111 @@ public:
|
|||
|
||||
TEST_F(MinidumpParserTest, GetThreads) {
|
||||
SetUpData("linux-x86_64.dmp");
|
||||
llvm::Optional<std::vector<const MinidumpThread *>> thread_list;
|
||||
llvm::ArrayRef<MinidumpThread> thread_list;
|
||||
|
||||
thread_list = parser->GetThreads();
|
||||
ASSERT_TRUE(thread_list.hasValue());
|
||||
ASSERT_EQ(1UL, thread_list->size());
|
||||
ASSERT_EQ(1UL, thread_list.size());
|
||||
|
||||
const MinidumpThread *thread = thread_list.getValue()[0];
|
||||
ASSERT_EQ(16001UL, thread->thread_id);
|
||||
const MinidumpThread thread = thread_list[0];
|
||||
ASSERT_EQ(16001UL, thread.thread_id);
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetThreadsTruncatedFile) {
|
||||
SetUpData("linux-x86_64.dmp", 200);
|
||||
llvm::Optional<std::vector<const MinidumpThread *>> thread_list;
|
||||
llvm::ArrayRef<MinidumpThread> thread_list;
|
||||
|
||||
thread_list = parser->GetThreads();
|
||||
ASSERT_FALSE(thread_list.hasValue());
|
||||
ASSERT_EQ(0UL, thread_list.size());
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetArchitecture) {
|
||||
SetUpData("linux-x86_64.dmp");
|
||||
ASSERT_EQ(llvm::Triple::ArchType::x86_64,
|
||||
parser->GetArchitecture().GetTriple().getArch());
|
||||
parser->GetArchitecture().GetMachine());
|
||||
ASSERT_EQ(llvm::Triple::OSType::Linux,
|
||||
parser->GetArchitecture().GetTriple().getOS());
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetMiscInfo) {
|
||||
SetUpData("linux-x86_64.dmp");
|
||||
const MinidumpMiscInfo *misc_info = parser->GetMiscInfo();
|
||||
ASSERT_EQ(nullptr, misc_info);
|
||||
// linux breakpad generated minidump files don't have misc info stream
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetLinuxProcStatus) {
|
||||
SetUpData("linux-x86_64.dmp");
|
||||
llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus();
|
||||
ASSERT_TRUE(proc_status.hasValue());
|
||||
lldb::pid_t pid = proc_status->GetPid();
|
||||
ASSERT_EQ(16001UL, pid);
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetPid) {
|
||||
SetUpData("linux-x86_64.dmp");
|
||||
llvm::Optional<lldb::pid_t> pid = parser->GetPid();
|
||||
ASSERT_TRUE(pid.hasValue());
|
||||
ASSERT_EQ(16001UL, pid.getValue());
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetModuleList) {
|
||||
SetUpData("linux-x86_64.dmp");
|
||||
llvm::ArrayRef<MinidumpModule> modules = parser->GetModuleList();
|
||||
ASSERT_EQ(8UL, modules.size());
|
||||
std::string module_names[8] = {
|
||||
"/usr/local/google/home/dvlahovski/projects/test_breakpad/a.out",
|
||||
"/lib/x86_64-linux-gnu/libm-2.19.so",
|
||||
"/lib/x86_64-linux-gnu/libc-2.19.so",
|
||||
"/lib/x86_64-linux-gnu/libgcc_s.so.1",
|
||||
"/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19",
|
||||
"/lib/x86_64-linux-gnu/libpthread-2.19.so",
|
||||
"/lib/x86_64-linux-gnu/ld-2.19.so",
|
||||
"linux-gate.so",
|
||||
};
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
llvm::Optional<std::string> name =
|
||||
parser->GetMinidumpString(modules[i].module_name_rva);
|
||||
ASSERT_TRUE(name.hasValue());
|
||||
ASSERT_EQ(module_names[i], name.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetExceptionStream) {
|
||||
SetUpData("linux-x86_64.dmp");
|
||||
const MinidumpExceptionStream *exception_stream =
|
||||
parser->GetExceptionStream();
|
||||
ASSERT_NE(nullptr, exception_stream);
|
||||
ASSERT_EQ(11UL, exception_stream->exception_record.exception_code);
|
||||
}
|
||||
|
||||
// Windows Minidump tests
|
||||
// fizzbuzz_no_heap.dmp is copied from the WinMiniDump tests
|
||||
TEST_F(MinidumpParserTest, GetArchitectureWindows) {
|
||||
SetUpData("fizzbuzz_no_heap.dmp");
|
||||
ASSERT_EQ(llvm::Triple::ArchType::x86,
|
||||
parser->GetArchitecture().GetMachine());
|
||||
ASSERT_EQ(llvm::Triple::OSType::Win32,
|
||||
parser->GetArchitecture().GetTriple().getOS());
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetLinuxProcStatusWindows) {
|
||||
SetUpData("fizzbuzz_no_heap.dmp");
|
||||
llvm::Optional<LinuxProcStatus> proc_status = parser->GetLinuxProcStatus();
|
||||
ASSERT_FALSE(proc_status.hasValue());
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetMiscInfoWindows) {
|
||||
SetUpData("fizzbuzz_no_heap.dmp");
|
||||
const MinidumpMiscInfo *misc_info = parser->GetMiscInfo();
|
||||
ASSERT_NE(nullptr, misc_info);
|
||||
llvm::Optional<lldb::pid_t> pid = misc_info->GetPid();
|
||||
ASSERT_TRUE(pid.hasValue());
|
||||
ASSERT_EQ(4440UL, pid.getValue());
|
||||
}
|
||||
|
||||
TEST_F(MinidumpParserTest, GetPidWindows) {
|
||||
SetUpData("fizzbuzz_no_heap.dmp");
|
||||
llvm::Optional<lldb::pid_t> pid = parser->GetPid();
|
||||
ASSERT_TRUE(pid.hasValue());
|
||||
ASSERT_EQ(4440UL, pid.getValue());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue