From 8cabfb764d6b0390038f2d0af6f29781b4b2294b Mon Sep 17 00:00:00 2001 From: Dimitar Vlahovski Date: Tue, 4 Oct 2016 21:02:13 +0000 Subject: [PATCH] Adding a new Minidump post-mortem debugging plugin Summary: This plugin resembles the already existing Windows-only Minidump plugin. The WinMinidumpPlugin uses the Windows API for parsing Minidumps while this plugin is cross-platform because it includes a Minidump parser (which is already commited) It is able to produce a backtrace, to read the general puprose regiters, inspect local variables, show image list, do memory reads, etc. For now the only arch that this supports is x86 64 bit This is because I have only written a register context for that arch. Others will come in next CLs. I copied the WinMinidump tests and adapted them a little bit for them to work with the new plugin (and they pass) I will add more tests, aiming for better code coverage. There is still functionality to be added, see TODOs in code. Reviewers: labath, zturner Subscribers: beanz, mgorny, amccarth, lldb-commits, modocache Differential Revision: https://reviews.llvm.org/D25196 llvm-svn: 283259 --- .../postmortem/minidump-new/Makefile | 6 + .../postmortem/minidump-new/TestMiniDumpNew.py | 100 ++++++++ .../postmortem/minidump-new/linux-x86_64.cpp | 28 +++ .../postmortem/minidump-new/linux-x86_64.dmp | Bin 0 -> 38320 bytes .../minidump-new/linux-x86_64_not_crashed.cpp | 36 +++ .../minidump-new/linux-x86_64_not_crashed.dmp | Bin 0 -> 42408 bytes lldb/source/API/SystemInitializerFull.cpp | 4 + .../source/Plugins/Process/minidump/CMakeLists.txt | 2 + .../Plugins/Process/minidump/MinidumpParser.cpp | 52 +++- .../Plugins/Process/minidump/MinidumpParser.h | 22 +- .../Plugins/Process/minidump/MinidumpTypes.cpp | 17 +- .../Plugins/Process/minidump/MinidumpTypes.h | 8 +- .../Plugins/Process/minidump/ProcessMinidump.cpp | 263 +++++++++++++++++++++ .../Plugins/Process/minidump/ProcessMinidump.h | 103 ++++++++ .../Plugins/Process/minidump/ThreadMinidump.cpp | 102 ++++++++ .../Plugins/Process/minidump/ThreadMinidump.h | 52 ++++ .../Process/minidump/MinidumpParserTest.cpp | 26 +- 17 files changed, 800 insertions(+), 21 deletions(-) create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/Makefile create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpNew.py create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64.cpp create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64.dmp create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64_not_crashed.cpp create mode 100644 lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64_not_crashed.dmp create mode 100644 lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp create mode 100644 lldb/source/Plugins/Process/minidump/ProcessMinidump.h create mode 100644 lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp create mode 100644 lldb/source/Plugins/Process/minidump/ThreadMinidump.h diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/Makefile new file mode 100644 index 0000000..b3034c1 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpNew.py b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpNew.py new file mode 100644 index 0000000..2064fd3 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpNew.py @@ -0,0 +1,100 @@ +""" +Test basics of Minidump debugging. +""" + +from __future__ import print_function +from six import iteritems + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class MiniDumpNewTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + NO_DEBUG_INFO_TESTCASE = True + + def test_process_info_in_mini_dump(self): + """Test that lldb can read the process information from the Minidump.""" + # target create -c linux-x86_64.dmp + self.dbg.CreateTarget("") + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-x86_64.dmp") + self.assertTrue(self.process, PROCESS_IS_VALID) + self.assertEqual(self.process.GetNumThreads(), 1) + self.assertEqual(self.process.GetProcessID(), 16001) + + def test_thread_info_in_mini_dump(self): + """Test that lldb can read the thread information from the Minidump.""" + # target create -c linux-x86_64.dmp + self.dbg.CreateTarget("") + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-x86_64.dmp") + # This process crashed due to a segmentation fault in its + # one and only thread. + self.assertEqual(self.process.GetNumThreads(), 1) + thread = self.process.GetThreadAtIndex(0) + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonSignal) + stop_description = thread.GetStopDescription(256) + self.assertTrue("SIGSEGV" in stop_description) + + def test_stack_info_in_mini_dump(self): + """Test that we can see a trivial stack in a breakpad-generated Minidump.""" + # target create -c linux-x86_64.dmp + self.dbg.CreateTarget("") + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-x86_64.dmp") + self.assertEqual(self.process.GetNumThreads(), 1) + thread = self.process.GetThreadAtIndex(0) + # frame #0: a.out`crash() + # frame #1: a.out`main() + # frame #2: libc-2.19.so`__libc_start_main() + # frame #3: a.out`_start + self.assertEqual(thread.GetNumFrames(), 4) + frame = thread.GetFrameAtIndex(0) + self.assertTrue(frame.IsValid()) + pc = frame.GetPC() + eip = frame.FindRegister("pc") + self.assertTrue(eip.IsValid()) + self.assertEqual(pc, eip.GetValueAsUnsigned()) + + def test_snapshot_minidump(self): + """Test that if we load a snapshot minidump file (meaning the process did not crash) there is no stop reason.""" + # target create -c linux-x86_64_not_crashed.dmp + self.dbg.CreateTarget("") + self.target = self.dbg.GetSelectedTarget() + self.process = self.target.LoadCore("linux-x86_64_not_crashed.dmp") + self.assertEqual(self.process.GetNumThreads(), 1) + thread = self.process.GetThreadAtIndex(0) + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonNone) + stop_description = thread.GetStopDescription(256) + self.assertEqual(stop_description, None) + + def test_deeper_stack_in_mini_dump(self): + """Test that we can examine a more interesting stack in a Minidump.""" + # Launch with the Minidump, and inspect the stack. + target = self.dbg.CreateTarget(None) + process = target.LoadCore("linux-x86_64_not_crashed.dmp") + thread = process.GetThreadAtIndex(0) + + expected_stack = {1: 'bar', 2: 'foo', 3: 'main'} + self.assertGreaterEqual(thread.GetNumFrames(), len(expected_stack)) + for index, name in iteritems(expected_stack): + frame = thread.GetFrameAtIndex(index) + self.assertTrue(frame.IsValid()) + function_name = frame.GetFunctionName() + self.assertTrue(name in function_name) + + def test_local_variables_in_mini_dump(self): + """Test that we can examine local variables in a Minidump.""" + # Launch with the Minidump, and inspect a local variable. + target = self.dbg.CreateTarget(None) + process = target.LoadCore("linux-x86_64_not_crashed.dmp") + thread = process.GetThreadAtIndex(0) + frame = thread.GetFrameAtIndex(1) + value = frame.EvaluateExpression('x') + self.assertEqual(value.GetValueAsSigned(), 3) diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64.cpp new file mode 100644 index 0000000..827fe67 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64.cpp @@ -0,0 +1,28 @@ +// Example source from breakpad's linux tutorial +// https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/linux_starter_guide.md + +#include +#include +#include + +#include "client/linux/handler/exception_handler.h" + +static bool dumpCallback(const google_breakpad::MinidumpDescriptor &descriptor, + void *context, bool succeeded) { + printf("Dump path: %s\n", descriptor.path()); + return succeeded; +} + +void crash() { + volatile int *a = (int *)(NULL); + *a = 1; +} + +int main(int argc, char *argv[]) { + google_breakpad::MinidumpDescriptor descriptor("/tmp"); + google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, + true, -1); + printf("pid: %d\n", getpid()); + crash(); + return 0; +} diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64.dmp new file mode 100644 index 0000000000000000000000000000000000000000..29a12d6a2ebca007c0360a49ab7a46fb7f34ed80 GIT binary patch literal 38320 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 +#include +#include + +#include "client/linux/handler/exception_handler.h" + +static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, +void* context, bool succeeded) { + printf("Dump path: %s\n", descriptor.path()); + return succeeded; +} + +int global = 42; + +int +bar(int x, google_breakpad::ExceptionHandler &eh) +{ + eh.WriteMinidump(); + int y = 4*x + global; + return y; +} + +int +foo(int x, google_breakpad::ExceptionHandler &eh) +{ + int y = 2*bar(3*x, eh); + return y; +} + + +int main(int argc, char* argv[]) { + google_breakpad::MinidumpDescriptor descriptor("/tmp"); + google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1); + foo(1, eh); + return 0; +} diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64_not_crashed.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/linux-x86_64_not_crashed.dmp new file mode 100644 index 0000000000000000000000000000000000000000..e175b0d7264c3ad18407ce083cdf1dc6245d4844 GIT binary patch literal 42408 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 MinidumpParser::GetData() { - return llvm::ArrayRef(m_data_sp->GetBytes(), - m_data_sp->GetByteSize()); + return m_data_sp->GetData(); } llvm::ArrayRef @@ -76,15 +75,14 @@ MinidumpParser::GetStream(MinidumpStreamType stream_type) { return {}; // check if there is enough data - if (iter->second.rva + iter->second.data_size > m_data_sp->GetByteSize()) + if (iter->second.rva + iter->second.data_size > GetData().size()) return {}; - return llvm::ArrayRef(m_data_sp->GetBytes() + iter->second.rva, - iter->second.data_size); + return GetData().slice(iter->second.rva, iter->second.data_size); } llvm::Optional MinidumpParser::GetMinidumpString(uint32_t rva) { - auto arr_ref = m_data_sp->GetData(); + auto arr_ref = GetData(); if (rva > arr_ref.size()) return llvm::None; arr_ref = arr_ref.drop_front(rva); @@ -100,6 +98,14 @@ llvm::ArrayRef MinidumpParser::GetThreads() { return MinidumpThread::ParseThreadList(data); } +llvm::ArrayRef +MinidumpParser::GetThreadContext(const MinidumpThread &td) { + if (td.thread_context.rva + td.thread_context.data_size > GetData().size()) + return llvm::None; + + return GetData().slice(td.thread_context.rva, td.thread_context.data_size); +} + const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() { llvm::ArrayRef data = GetStream(MinidumpStreamType::SystemInfo); @@ -224,3 +230,33 @@ const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() { return MinidumpExceptionStream::Parse(data); } + +llvm::Optional MinidumpParser::FindMemoryRange(lldb::addr_t addr) { + llvm::ArrayRef data = GetStream(MinidumpStreamType::MemoryList); + + if (data.size() == 0) + return llvm::None; + + llvm::ArrayRef memory_list = + MinidumpMemoryDescriptor::ParseMemoryList(data); + + if (memory_list.size() == 0) + return llvm::None; + + for (auto memory_desc : memory_list) { + const MinidumpLocationDescriptor &loc_desc = memory_desc.memory; + const lldb::addr_t range_start = memory_desc.start_of_memory_range; + const size_t range_size = loc_desc.data_size; + + if (loc_desc.rva + loc_desc.data_size > GetData().size()) + return llvm::None; + + if (range_start <= addr && addr < range_start + range_size) { + return Range(range_start, GetData().slice(loc_desc.rva, range_size)); + } + } + + // TODO parse Memory64List which is present in full-memory Minidumps + + return llvm::None; +} diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/lldb/source/Plugins/Process/minidump/MinidumpParser.h index 76a8ece..686d0f3 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.h +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.h @@ -1,12 +1,11 @@ -//===-- MinidumpParser.h -----------------------------------------*- C++ -//-*-===// +//===-- MinidumpParser.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_MinidumpParser_h_ #define liblldb_MinidumpParser_h_ @@ -25,15 +24,22 @@ #include "llvm/ADT/StringRef.h" // C includes - // C++ includes -#include -#include namespace lldb_private { namespace minidump { +// Describes a range of memory captured in the Minidump +struct Range { + lldb::addr_t start; // virtual address of the beginning of the range + // range_ref - absolute pointer to the first byte of the range and size + llvm::ArrayRef range_ref; + + Range(lldb::addr_t start, llvm::ArrayRef range_ref) + : start(start), range_ref(range_ref) {} +}; + class MinidumpParser { public: static llvm::Optional @@ -47,6 +53,8 @@ public: llvm::ArrayRef GetThreads(); + llvm::ArrayRef GetThreadContext(const MinidumpThread &td); + const MinidumpSystemInfo *GetSystemInfo(); ArchSpec GetArchitecture(); @@ -61,6 +69,8 @@ public: const MinidumpExceptionStream *GetExceptionStream(); + llvm::Optional FindMemoryRange(lldb::addr_t addr); + private: lldb::DataBufferSP m_data_sp; const MinidumpHeader *m_header; diff --git a/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp b/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp index 5f8aa59..55f8598 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpTypes.cpp @@ -85,7 +85,7 @@ MinidumpThread::ParseThreadList(llvm::ArrayRef &data) { if (error.Fail() || *thread_count * sizeof(MinidumpThread) > data.size()) return {}; - return llvm::ArrayRef( + return llvm::makeArrayRef( reinterpret_cast(data.data()), *thread_count); } @@ -162,7 +162,7 @@ MinidumpModule::ParseModuleList(llvm::ArrayRef &data) { if (error.Fail() || *modules_count * sizeof(MinidumpModule) > data.size()) return {}; - return llvm::ArrayRef( + return llvm::makeArrayRef( reinterpret_cast(data.data()), *modules_count); } @@ -176,3 +176,16 @@ MinidumpExceptionStream::Parse(llvm::ArrayRef &data) { return exception_stream; } + +llvm::ArrayRef +MinidumpMemoryDescriptor::ParseMemoryList(llvm::ArrayRef &data) { + const llvm::support::ulittle32_t *mem_ranges_count; + Error error = consumeObject(data, mem_ranges_count); + if (error.Fail() || + *mem_ranges_count * sizeof(MinidumpMemoryDescriptor) > data.size()) + return {}; + + return llvm::makeArrayRef( + reinterpret_cast(data.data()), + *mem_ranges_count); +} diff --git a/lldb/source/Plugins/Process/minidump/MinidumpTypes.h b/lldb/source/Plugins/Process/minidump/MinidumpTypes.h index bc3d846..b452c27 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpTypes.h +++ b/lldb/source/Plugins/Process/minidump/MinidumpTypes.h @@ -207,6 +207,9 @@ static_assert(sizeof(MinidumpLocationDescriptor) == 8, struct MinidumpMemoryDescriptor { llvm::support::ulittle64_t start_of_memory_range; MinidumpLocationDescriptor memory; + + static llvm::ArrayRef + ParseMemoryList(llvm::ArrayRef &data); }; static_assert(sizeof(MinidumpMemoryDescriptor) == 16, "sizeof MinidumpMemoryDescriptor is not correct!"); @@ -362,7 +365,8 @@ static_assert(sizeof(MinidumpModule) == 108, // Exception stuff struct MinidumpException { enum { - MaxParams = 15, + ExceptonInfoMaxParams = 15, + DumpRequested = 0xFFFFFFFF, }; llvm::support::ulittle32_t exception_code; @@ -371,7 +375,7 @@ struct MinidumpException { 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]; + llvm::support::ulittle64_t exception_information[ExceptonInfoMaxParams]; }; static_assert(sizeof(MinidumpException) == 152, "sizeof MinidumpException is not correct!"); diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp new file mode 100644 index 0000000..12f83fe --- /dev/null +++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -0,0 +1,263 @@ +//===-- ProcessMinidump.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 "ProcessMinidump.h" +#include "ThreadMinidump.h" + +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/LLDBAssert.h" + +// C includes +// C++ includes + +using namespace lldb_private; +using namespace minidump; + +ConstString ProcessMinidump::GetPluginNameStatic() { + static ConstString g_name("minidump"); + return g_name; +} + +const char *ProcessMinidump::GetPluginDescriptionStatic() { + return "Minidump plug-in."; +} + +lldb::ProcessSP ProcessMinidump::CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file) { + if (!crash_file) + return nullptr; + + lldb::ProcessSP process_sp; + // Read enough data for the Minidump header + const size_t header_size = sizeof(MinidumpHeader); + lldb::DataBufferSP data_sp(crash_file->MemoryMapFileContents(0, header_size)); + // The memory map can fail + if (!data_sp) + return nullptr; + + // first, only try to parse the header, beacuse we need to be fast + llvm::ArrayRef header_data(data_sp->GetBytes(), header_size); + const MinidumpHeader *header = MinidumpHeader::Parse(header_data); + + if (data_sp->GetByteSize() != header_size || header == nullptr) + return nullptr; + + lldb::DataBufferSP all_data_sp(crash_file->MemoryMapFileContents()); + auto minidump_parser = MinidumpParser::Create(all_data_sp); + // check if the parser object is valid + // skip if the Minidump file is Windows generated, because we are still + // work-in-progress + if (!minidump_parser || + minidump_parser->GetArchitecture().GetTriple().getOS() == + llvm::Triple::OSType::Win32) + return nullptr; + + return lldb::ProcessSP(new ProcessMinidump( + target_sp, listener_sp, *crash_file, minidump_parser.getValue())); +} + +// TODO leave it to be only "return true" ? +bool ProcessMinidump::CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) { + return true; +} + +ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec &core_file, + MinidumpParser minidump_parser) + : Process(target_sp, listener_sp), m_minidump_parser(minidump_parser), + m_core_file(core_file) {} + +ProcessMinidump::~ProcessMinidump() { + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +void ProcessMinidump::Initialize() { + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, []() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + ProcessMinidump::CreateInstance); + }); +} + +void ProcessMinidump::Terminate() { + PluginManager::UnregisterPlugin(ProcessMinidump::CreateInstance); +} + +Error ProcessMinidump::DoLoadCore() { + Error error; + + m_thread_list = m_minidump_parser.GetThreads(); + m_active_exception = m_minidump_parser.GetExceptionStream(); + GetTarget().SetArchitecture(GetArchitecture()); + ReadModuleList(); + + llvm::Optional pid = m_minidump_parser.GetPid(); + if (!pid) { + error.SetErrorString("failed to parse PID"); + return error; + } + SetID(pid.getValue()); + + return error; +} + +DynamicLoader *ProcessMinidump::GetDynamicLoader() { + if (m_dyld_ap.get() == nullptr) + m_dyld_ap.reset(DynamicLoader::FindPlugin(this, nullptr)); + return m_dyld_ap.get(); +} + +ConstString ProcessMinidump::GetPluginName() { return GetPluginNameStatic(); } + +uint32_t ProcessMinidump::GetPluginVersion() { return 1; } + +Error ProcessMinidump::DoDestroy() { return Error(); } + +void ProcessMinidump::RefreshStateAfterStop() { + if (!m_active_exception) + return; + + if (m_active_exception->exception_record.exception_code == + MinidumpException::DumpRequested) { + return; + } + + lldb::StopInfoSP stop_info; + lldb::ThreadSP stop_thread; + + Process::m_thread_list.SetSelectedThreadByID(m_active_exception->thread_id); + stop_thread = Process::m_thread_list.GetSelectedThread(); + ArchSpec arch = GetArchitecture(); + + if (arch.GetTriple().getOS() == llvm::Triple::Linux) { + stop_info = StopInfo::CreateStopReasonWithSignal( + *stop_thread, m_active_exception->exception_record.exception_code); + } else { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " + << llvm::format_hex( + m_active_exception->exception_record.exception_code, 8) + << " encountered at address " + << llvm::format_hex( + m_active_exception->exception_record.exception_address, + 8); + stop_info = StopInfo::CreateStopReasonWithException( + *stop_thread, desc_stream.str().c_str()); + } + + stop_thread->SetStopInfo(stop_info); +} + +bool ProcessMinidump::IsAlive() { return true; } + +bool ProcessMinidump::WarnBeforeDetach() const { return false; } + +size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Error &error) { + // Don't allow the caching that lldb_private::Process::ReadMemory does + // since we have it all cached in our dump file anyway. + return DoReadMemory(addr, buf, size, error); +} + +size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Error &error) { + // I don't have a sense of how frequently this is called or how many memory + // ranges a Minidump typically has, so I'm not sure if searching for the + // appropriate range linearly each time is stupid. Perhaps we should build + // an index for faster lookups. + llvm::Optional range = m_minidump_parser.FindMemoryRange(addr); + if (!range) + return 0; + + // There's at least some overlap between the beginning of the desired range + // (addr) and the current range. Figure out where the overlap begins and + // how much overlap there is, then copy it to the destination buffer. + lldbassert(range->start <= addr); + const size_t offset = addr - range->start; + lldbassert(offset < range->range_ref.size()); + const size_t overlap = std::min(size, range->range_ref.size() - offset); + std::memcpy(buf, range->range_ref.data() + offset, overlap); + return overlap; +} + +ArchSpec ProcessMinidump::GetArchitecture() { + return m_minidump_parser.GetArchitecture(); +} + +// TODO - parse the MemoryInfoListStream and implement this method +Error ProcessMinidump::GetMemoryRegionInfo( + lldb::addr_t load_addr, lldb_private::MemoryRegionInfo &range_info) { + return {}; +} + +void ProcessMinidump::Clear() { Process::m_thread_list.Clear(); } + +bool ProcessMinidump::UpdateThreadList( + lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) { + uint32_t num_threads = 0; + if (m_thread_list.size() > 0) + num_threads = m_thread_list.size(); + + for (lldb::tid_t tid = 0; tid < num_threads; ++tid) { + lldb::ThreadSP thread_sp(new ThreadMinidump( + *this, m_thread_list[tid], + m_minidump_parser.GetThreadContext(m_thread_list[tid]))); + new_thread_list.AddThread(thread_sp); + } + return new_thread_list.GetSize(false) > 0; +} + +void ProcessMinidump::ReadModuleList() { + llvm::ArrayRef modules = m_minidump_parser.GetModuleList(); + + for (auto module : modules) { + llvm::Optional name = + m_minidump_parser.GetMinidumpString(module.module_name_rva); + + if (!name) + continue; + + const auto file_spec = FileSpec(name.getValue(), true); + ModuleSpec module_spec = file_spec; + Error error; + lldb::ModuleSP module_sp = + this->GetTarget().GetSharedModule(module_spec, &error); + if (!module_sp || error.Fail()) { + continue; + } + + bool load_addr_changed = false; + module_sp->SetLoadAddress(this->GetTarget(), module.base_of_image, false, + load_addr_changed); + } +} diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h new file mode 100644 index 0000000..00c45d6 --- /dev/null +++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h @@ -0,0 +1,103 @@ +//===-- ProcessMinidump.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_ProcessMinidump_h_ +#define liblldb_ProcessMinidump_h_ + +// Project includes +#include "MinidumpParser.h" +#include "MinidumpTypes.h" + +// Other libraries and framework includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +// C Includes +// C++ Includes + +namespace lldb_private { + +namespace minidump { + +class ProcessMinidump : public Process { +public: + static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, + lldb::ListenerSP listener_sp, + const FileSpec *crash_file_path); + + static void Initialize(); + + static void Terminate(); + + static ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + ProcessMinidump(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, + const lldb_private::FileSpec &core_file, + MinidumpParser minidump_parser); + + ~ProcessMinidump() override; + + bool CanDebug(lldb::TargetSP target_sp, + bool plugin_specified_by_name) override; + + Error DoLoadCore() override; + + DynamicLoader *GetDynamicLoader() override; + + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override; + + Error DoDestroy() override; + + void RefreshStateAfterStop() override; + + bool IsAlive() override; + + bool WarnBeforeDetach() const override; + + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, + Error &error) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Error &error) override; + + ArchSpec GetArchitecture(); + + Error GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + MinidumpParser m_minidump_parser; + +protected: + void Clear(); + + bool UpdateThreadList(ThreadList &old_thread_list, + ThreadList &new_thread_list) override; + + void ReadModuleList(); + +private: + FileSpec m_core_file; + llvm::ArrayRef m_thread_list; + const MinidumpExceptionStream *m_active_exception; +}; + +} // namespace minidump +} // namespace lldb_private + +#endif // liblldb_ProcessMinidump_h_ diff --git a/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp b/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp new file mode 100644 index 0000000..22641da --- /dev/null +++ b/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp @@ -0,0 +1,102 @@ +//===-- ThreadMinidump.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 "ThreadMinidump.h" +#include "ProcessMinidump.h" + +#include "RegisterContextMinidump_x86_64.h" + +// Other libraries and framework includes +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" + +#include "Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.h" + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" + +// C Includes +// C++ Includes + +using namespace lldb; +using namespace lldb_private; +using namespace minidump; + +ThreadMinidump::ThreadMinidump(Process &process, const MinidumpThread &td, + llvm::ArrayRef gpregset_data) + : Thread(process, td.thread_id), m_thread_reg_ctx_sp(), + m_gpregset_data(gpregset_data) {} + +ThreadMinidump::~ThreadMinidump() {} + +void ThreadMinidump::RefreshStateAfterStop() {} + +void ThreadMinidump::ClearStackFrames() {} + +RegisterContextSP ThreadMinidump::GetRegisterContext() { + if (!m_reg_context_sp) { + m_reg_context_sp = CreateRegisterContextForFrame(nullptr); + } + return m_reg_context_sp; +} + +RegisterContextSP +ThreadMinidump::CreateRegisterContextForFrame(StackFrame *frame) { + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) { + if (m_thread_reg_ctx_sp) + return m_thread_reg_ctx_sp; + + ProcessMinidump *process = + static_cast(GetProcess().get()); + ArchSpec arch = process->GetArchitecture(); + RegisterInfoInterface *reg_interface = nullptr; + + // TODO write other register contexts and add them here + switch (arch.GetMachine()) { + case llvm::Triple::x86_64: { + reg_interface = new RegisterContextLinux_x86_64(arch); + lldb::DataBufferSP buf = + ConvertMinidumpContextToRegIface(m_gpregset_data, reg_interface); + DataExtractor gpregs(buf, lldb::eByteOrderLittle, 8); + DataExtractor fpregs; + m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_x86_64( + *this, reg_interface, gpregs, fpregs)); + break; + } + default: + break; + } + + if (!reg_interface) { + if (log) + log->Printf("elf-core::%s:: Architecture(%d) not supported", + __FUNCTION__, arch.GetMachine()); + assert(false && "Architecture not supported"); + } + + reg_ctx_sp = m_thread_reg_ctx_sp; + } else if (m_unwinder_ap) { + reg_ctx_sp = m_unwinder_ap->CreateRegisterContextForFrame(frame); + } + + return reg_ctx_sp; +} + +bool ThreadMinidump::CalculateStopInfo() { return false; } diff --git a/lldb/source/Plugins/Process/minidump/ThreadMinidump.h b/lldb/source/Plugins/Process/minidump/ThreadMinidump.h new file mode 100644 index 0000000..97db452 --- /dev/null +++ b/lldb/source/Plugins/Process/minidump/ThreadMinidump.h @@ -0,0 +1,52 @@ +//===-- ThreadMinidump.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_ThreadMinidump_h_ +#define liblldb_ThreadMinidump_h_ + +// Project includes +#include "MinidumpTypes.h" + +// Other libraries and framework includes +#include "lldb/Target/Thread.h" + +// C Includes +// C++ Includes + +namespace lldb_private { + +namespace minidump { + +class ThreadMinidump : public Thread { +public: + ThreadMinidump(Process &process, const MinidumpThread &td, + llvm::ArrayRef gpregset_data); + + ~ThreadMinidump() override; + + void RefreshStateAfterStop() override; + + lldb::RegisterContextSP GetRegisterContext() override; + + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + + void ClearStackFrames() override; + +protected: + lldb::RegisterContextSP m_thread_reg_ctx_sp; + llvm::ArrayRef m_gpregset_data; + + bool CalculateStopInfo() override; +}; + +} // namespace minidump +} // namespace lldb_private + +#endif // liblldb_ThreadMinidump_h_ diff --git a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp index 7597fab..cbb8f7f 100644 --- a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp +++ b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp @@ -60,7 +60,7 @@ public: std::unique_ptr parser; }; -TEST_F(MinidumpParserTest, GetThreads) { +TEST_F(MinidumpParserTest, GetThreadsAndGetThreadContext) { SetUpData("linux-x86_64.dmp"); llvm::ArrayRef thread_list; @@ -68,7 +68,10 @@ TEST_F(MinidumpParserTest, GetThreads) { ASSERT_EQ(1UL, thread_list.size()); const MinidumpThread thread = thread_list[0]; - ASSERT_EQ(16001UL, thread.thread_id); + EXPECT_EQ(16001UL, thread.thread_id); + + llvm::ArrayRef context = parser->GetThreadContext(thread); + EXPECT_EQ(1232, context.size()); } TEST_F(MinidumpParserTest, GetThreadsTruncatedFile) { @@ -139,6 +142,24 @@ TEST_F(MinidumpParserTest, GetExceptionStream) { ASSERT_EQ(11UL, exception_stream->exception_record.exception_code); } +TEST_F(MinidumpParserTest, GetMemoryRange) { + SetUpData("linux-x86_64.dmp"); + // There are two memory ranges in the file (size is in bytes, decimal): + // 1) 0x7ffceb34a000 12288 + // 2) 0x401d46 256 + EXPECT_TRUE(parser->FindMemoryRange(0x7ffceb34a000).hasValue()); + EXPECT_TRUE(parser->FindMemoryRange(0x7ffceb34a000 + 12288 / 2).hasValue()); + EXPECT_TRUE(parser->FindMemoryRange(0x7ffceb34a000 + 12288 - 1).hasValue()); + EXPECT_FALSE(parser->FindMemoryRange(0x7ffceb34a000 + 12288).hasValue()); + + EXPECT_TRUE(parser->FindMemoryRange(0x401d46).hasValue()); + EXPECT_TRUE(parser->FindMemoryRange(0x401d46 + 256 / 2).hasValue()); + EXPECT_TRUE(parser->FindMemoryRange(0x401d46 + 256 - 1).hasValue()); + EXPECT_FALSE(parser->FindMemoryRange(0x401d46 + 256).hasValue()); + + EXPECT_FALSE(parser->FindMemoryRange(0x2a).hasValue()); +} + // Windows Minidump tests // fizzbuzz_no_heap.dmp is copied from the WinMiniDump tests TEST_F(MinidumpParserTest, GetArchitectureWindows) { @@ -172,7 +193,6 @@ TEST_F(MinidumpParserTest, GetPidWindows) { } // Register stuff -// TODO probably split register stuff tests into different file? #define REG_VAL(x) *(reinterpret_cast(x)) TEST_F(MinidumpParserTest, ConvertRegisterContext) { -- 2.7.4