Index: include/lldb/lldb-private-types.h =================================================================== --- include/lldb/lldb-private-types.h +++ include/lldb/lldb-private-types.h @@ -14,6 +14,8 @@ #include "lldb/lldb-private.h" +#include "llvm/ADT/ArrayRef.h" + namespace llvm { namespace sys { class DynamicLibrary; @@ -61,6 +63,15 @@ // the byte size of this register. size_t dynamic_size_dwarf_len; // The length of the DWARF expression in bytes // in the dynamic_size_dwarf_expr_bytes member. + + llvm::ArrayRef data(const uint8_t *context_base) const { + return llvm::ArrayRef(context_base + byte_offset, byte_size); + } + + llvm::MutableArrayRef mutable_data(uint8_t *context_base) const { + return llvm::MutableArrayRef(context_base + byte_offset, + byte_size); + } }; //---------------------------------------------------------------------- Index: source/Plugins/Process/minidump/CMakeLists.txt =================================================================== --- source/Plugins/Process/minidump/CMakeLists.txt +++ source/Plugins/Process/minidump/CMakeLists.txt @@ -3,4 +3,5 @@ add_lldb_library(lldbPluginProcessMinidump MinidumpTypes.cpp MinidumpParser.cpp + RegisterContextMinidump_x86_64.cpp ) Index: source/Plugins/Process/minidump/MinidumpParser.h =================================================================== --- source/Plugins/Process/minidump/MinidumpParser.h +++ source/Plugins/Process/minidump/MinidumpParser.h @@ -39,7 +39,7 @@ static llvm::Optional Create(const lldb::DataBufferSP &data_buf_sp); - lldb::offset_t GetByteSize(); + llvm::ArrayRef GetData(); llvm::ArrayRef GetStream(MinidumpStreamType stream_type); @@ -71,6 +71,6 @@ llvm::DenseMap &&directory_map); }; -} // namespace minidump -} // namespace lldb_private +} // end namespace minidump +} // end namespace lldb_private #endif // liblldb_MinidumpParser_h_ Index: source/Plugins/Process/minidump/MinidumpParser.cpp =================================================================== --- source/Plugins/Process/minidump/MinidumpParser.cpp +++ source/Plugins/Process/minidump/MinidumpParser.cpp @@ -64,8 +64,9 @@ : m_data_sp(data_buf_sp), m_header(header), m_directory_map(directory_map) { } -lldb::offset_t MinidumpParser::GetByteSize() { - return m_data_sp->GetByteSize(); +llvm::ArrayRef MinidumpParser::GetData() { + return llvm::ArrayRef(m_data_sp->GetBytes(), + m_data_sp->GetByteSize()); } llvm::ArrayRef Index: source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h =================================================================== --- /dev/null +++ source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.h @@ -0,0 +1,119 @@ +//===-- RegisterContextMinidump_x86_64.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextMinidump_h_ +#define liblldb_RegisterContextMinidump_h_ + +// Project includes +#include "MinidumpTypes.h" + +// Other libraries and framework includes +#include "Plugins/Process/Utility/RegisterInfoInterface.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "lldb/Target/RegisterContext.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" + +// C includes +// C++ includes + +namespace lldb_private { + +namespace minidump { + +// The content of the Minidump register context is as follows: +// (for reference see breakpad's source or WinNT.h) +// Register parameter home addresses: (p1_home .. p6_home) +// - uint64_t p1_home +// - uint64_t p2_home +// - uint64_t p3_home +// - uint64_t p4_home +// - uint64_t p5_home +// - uint64_t p6_home +// +// - uint32_t context_flags - field that determines the layout of the structure +// and which parts of it are populated +// - uint32_t mx_csr +// +// - uint16_t cs - included if MinidumpContext_x86_64_Flags::Control +// +// - uint16_t ds - included if MinidumpContext_x86_64_Flags::Segments +// - uint16_t es - included if MinidumpContext_x86_64_Flags::Segments +// - uint16_t fs - included if MinidumpContext_x86_64_Flags::Segments +// - uint16_t gs - included if MinidumpContext_x86_64_Flags::Segments +// +// - uint16_t ss - included if MinidumpContext_x86_64_Flags::Control +// - uint32_t rflags - included if MinidumpContext_x86_64_Flags::Control +// +// Debug registers: (included if MinidumpContext_x86_64_Flags::DebugRegisters) +// - uint64_t dr0 +// - uint64_t dr1 +// - uint64_t dr2 +// - uint64_t dr3 +// - uint64_t dr6 +// - uint64_t dr7 +// +// The next 4 registers are included if MinidumpContext_x86_64_Flags::Integer +// - uint64_t rax +// - uint64_t rcx +// - uint64_t rdx +// - uint64_t rbx +// +// - uint64_t rsp - included if MinidumpContext_x86_64_Flags::Control +// +// The next 11 registers are included if MinidumpContext_x86_64_Flags::Integer +// - uint64_t rbp +// - uint64_t rsi +// - uint64_t rdi +// - uint64_t r8 +// - uint64_t r9 +// - uint64_t r10 +// - uint64_t r11 +// - uint64_t r12 +// - uint64_t r13 +// - uint64_t r14 +// - uint64_t r15 +// +// - uint64_t rip - included if MinidumpContext_x86_64_Flags::Control +// +// TODO: add floating point registers here + +// This function receives an ArrayRef pointing to the bytes of the Minidump +// register context and returns a DataBuffer that's ordered by the offsets +// specified in the RegisterInfoInterface argument +// This way we can reuse the already existing register contexts +lldb::DataBufferSP +ConvertMinidumpContextToRegIface(llvm::ArrayRef source_data, + RegisterInfoInterface *target_reg_interface); + +// For context_flags. These values indicate the type of +// context stored in the structure. The high 24 bits identify the CPU, the +// low 8 bits identify the type of context saved. +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +enum class MinidumpContext_x86_64_Flags : uint32_t { + x86_64_Flag = 0x00100000, + Control = x86_64_Flag | 0x00000001, + Integer = x86_64_Flag | 0x00000002, + Segments = x86_64_Flag | 0x00000004, + FloatingPoint = x86_64_Flag | 0x00000008, + DebugRegisters = x86_64_Flag | 0x00000010, + XState = x86_64_Flag | 0x00000040, + + Full = Control | Integer | FloatingPoint, + All = Full | Segments | DebugRegisters, + + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ All) +}; + +} // end namespace minidump +} // end namespace lldb_private +#endif // liblldb_RegisterContextMinidump_h_ Index: source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/minidump/RegisterContextMinidump_x86_64.cpp @@ -0,0 +1,158 @@ +//===-- RegisterContextMinidump_x86_64.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Project includes +#include "RegisterContextMinidump_x86_64.h" + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" + +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +void writeRegister(llvm::ArrayRef ®_src, + llvm::MutableArrayRef reg_dest) { + memcpy(reg_dest.data(), reg_src.data(), reg_dest.size()); + reg_src = reg_src.drop_front(reg_dest.size()); +} + +llvm::MutableArrayRef getDestRegister(uint8_t *context, + uint32_t lldb_reg_num, + const RegisterInfo ®) { + auto bytes = reg.mutable_data(context); + + switch (lldb_reg_num) { + case lldb_cs_x86_64: + case lldb_ds_x86_64: + case lldb_es_x86_64: + case lldb_fs_x86_64: + case lldb_gs_x86_64: + case lldb_ss_x86_64: + return bytes.take_front(2); + break; + case lldb_rflags_x86_64: + return bytes.take_front(4); + break; + default: + return bytes.take_front(8); + break; + } +} + +lldb::DataBufferSP lldb_private::minidump::ConvertMinidumpContextToRegIface( + llvm::ArrayRef source_data, + RegisterInfoInterface *target_reg_interface) { + + const RegisterInfo *reg_info = target_reg_interface->GetRegisterInfo(); + + lldb::DataBufferSP result_context_buf( + new DataBufferHeap(target_reg_interface->GetGPRSize(), 0)); + uint8_t *result_base = result_context_buf->GetBytes(); + + source_data = source_data.drop_front(6 * 8); // p[1-6] home registers + const uint32_t *context_flags; + consumeObject(source_data, context_flags); + const uint32_t x86_64_Flag = + static_cast(MinidumpContext_x86_64_Flags::x86_64_Flag); + const uint32_t ControlFlag = + static_cast(MinidumpContext_x86_64_Flags::Control); + const uint32_t IntegerFlag = + static_cast(MinidumpContext_x86_64_Flags::Integer); + const uint32_t SegmentsFlag = + static_cast(MinidumpContext_x86_64_Flags::Segments); + const uint32_t DebugRegistersFlag = + static_cast(MinidumpContext_x86_64_Flags::DebugRegisters); + + if (!(*context_flags & x86_64_Flag)) { + return result_context_buf; // error + } + + source_data = source_data.drop_front(4); // mx_csr + + if (*context_flags & ControlFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_cs_x86_64, + reg_info[lldb_cs_x86_64])); + } + + if (*context_flags & SegmentsFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_ds_x86_64, + reg_info[lldb_ds_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_es_x86_64, + reg_info[lldb_es_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_fs_x86_64, + reg_info[lldb_fs_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_gs_x86_64, + reg_info[lldb_gs_x86_64])); + } + + if (*context_flags & ControlFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_ss_x86_64, + reg_info[lldb_ss_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rflags_x86_64, + reg_info[lldb_rflags_x86_64])); + } + + if (*context_flags & DebugRegistersFlag) { + source_data = + source_data.drop_front(6 * 8); // 6 debug registers 64 bit each + } + + if (*context_flags & IntegerFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_rax_x86_64, + reg_info[lldb_rax_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rcx_x86_64, + reg_info[lldb_rcx_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rdx_x86_64, + reg_info[lldb_rdx_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rbx_x86_64, + reg_info[lldb_rbx_x86_64])); + } + + if (*context_flags & ControlFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_rsp_x86_64, + reg_info[lldb_rsp_x86_64])); + } + + if (*context_flags & IntegerFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_rbp_x86_64, + reg_info[lldb_rbp_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rsi_x86_64, + reg_info[lldb_rsi_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_rdi_x86_64, + reg_info[lldb_rdi_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r8_x86_64, + reg_info[lldb_r8_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r9_x86_64, + reg_info[lldb_r9_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r10_x86_64, + reg_info[lldb_r10_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r11_x86_64, + reg_info[lldb_r11_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r12_x86_64, + reg_info[lldb_r12_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r13_x86_64, + reg_info[lldb_r13_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r14_x86_64, + reg_info[lldb_r14_x86_64])); + writeRegister(source_data, getDestRegister(result_base, lldb_r15_x86_64, + reg_info[lldb_r15_x86_64])); + } + + if (*context_flags & ControlFlag) { + writeRegister(source_data, getDestRegister(result_base, lldb_rip_x86_64, + reg_info[lldb_rip_x86_64])); + } + + // TODO parse the floating point registers + + return result_context_buf; +} Index: unittests/Process/minidump/MinidumpParserTest.cpp =================================================================== --- unittests/Process/minidump/MinidumpParserTest.cpp +++ unittests/Process/minidump/MinidumpParserTest.cpp @@ -8,8 +8,10 @@ //===----------------------------------------------------------------------===// // Project includes +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" #include "Plugins/Process/minidump/MinidumpParser.h" #include "Plugins/Process/minidump/MinidumpTypes.h" +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" // Other libraries and framework includes #include "gtest/gtest.h" @@ -18,6 +20,7 @@ #include "lldb/Core/DataExtractor.h" #include "lldb/Host/FileSpec.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -50,7 +53,7 @@ MinidumpParser::Create(data_sp); ASSERT_TRUE(optional_parser.hasValue()); parser.reset(new MinidumpParser(optional_parser.getValue())); - ASSERT_GT(parser->GetByteSize(), 0UL); + ASSERT_GT(parser->GetData().size(), 0UL); } llvm::SmallString<128> inputs_folder; @@ -167,3 +170,61 @@ ASSERT_TRUE(pid.hasValue()); ASSERT_EQ(4440UL, pid.getValue()); } + +// Register stuff +// TODO probably split register stuff tests into different file? +#define REG_VAL(x) *(reinterpret_cast(x)) + +TEST_F(MinidumpParserTest, ConvertRegisterContext) { + SetUpData("linux-x86_64.dmp"); + llvm::ArrayRef thread_list = parser->GetThreads(); + const MinidumpThread thread = thread_list[0]; + llvm::ArrayRef registers(parser->GetData().data() + + thread.thread_context.rva, + thread.thread_context.data_size); + + ArchSpec arch = parser->GetArchitecture(); + RegisterInfoInterface *reg_interface = new RegisterContextLinux_x86_64(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContextToRegIface(registers, reg_interface); + ASSERT_EQ(reg_interface->GetGPRSize(), buf->GetByteSize()); + + const RegisterInfo *reg_info = reg_interface->GetRegisterInfo(); + + std::map reg_values; + + // clang-format off + reg_values[lldb_rax_x86_64] = 0x0000000000000000; + reg_values[lldb_rbx_x86_64] = 0x0000000000000000; + reg_values[lldb_rcx_x86_64] = 0x0000000000000010; + reg_values[lldb_rdx_x86_64] = 0x0000000000000000; + reg_values[lldb_rdi_x86_64] = 0x00007ffceb349cf0; + reg_values[lldb_rsi_x86_64] = 0x0000000000000000; + reg_values[lldb_rbp_x86_64] = 0x00007ffceb34a210; + reg_values[lldb_rsp_x86_64] = 0x00007ffceb34a210; + reg_values[lldb_r8_x86_64] = 0x00007fe9bc1aa9c0; + reg_values[lldb_r9_x86_64] = 0x0000000000000000; + reg_values[lldb_r10_x86_64] = 0x00007fe9bc3f16a0; + reg_values[lldb_r11_x86_64] = 0x0000000000000246; + reg_values[lldb_r12_x86_64] = 0x0000000000401c92; + reg_values[lldb_r13_x86_64] = 0x00007ffceb34a430; + reg_values[lldb_r14_x86_64] = 0x0000000000000000; + reg_values[lldb_r15_x86_64] = 0x0000000000000000; + reg_values[lldb_rip_x86_64] = 0x0000000000401dc6; + reg_values[lldb_rflags_x86_64] = 0x0000000000010206; + reg_values[lldb_cs_x86_64] = 0x0000000000000033; + reg_values[lldb_fs_x86_64] = 0x0000000000000000; + reg_values[lldb_gs_x86_64] = 0x0000000000000000; + reg_values[lldb_ss_x86_64] = 0x0000000000000000; + reg_values[lldb_ds_x86_64] = 0x0000000000000000; + reg_values[lldb_es_x86_64] = 0x0000000000000000; + // clang-format on + + for (uint32_t reg_index = 0; reg_index < reg_interface->GetRegisterCount(); + ++reg_index) { + if (reg_values.find(reg_index) != reg_values.end()) { + EXPECT_EQ(reg_values[reg_index], + REG_VAL(buf->GetBytes() + reg_info[reg_index].byte_offset)); + } + } +}