[Lldb-commits] [lldb] 4926835 - [LLDB][Minidump] Support minidumps where there are multiple exception streams (#97470)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Sep 9 21:07:15 PDT 2024
Author: Jacob Lalonde
Date: 2024-09-09T21:07:12-07:00
New Revision: 492683527eda7ef055660842d9a6b7e2575ae208
URL: https://github.com/llvm/llvm-project/commit/492683527eda7ef055660842d9a6b7e2575ae208
DIFF: https://github.com/llvm/llvm-project/commit/492683527eda7ef055660842d9a6b7e2575ae208.diff
LOG: [LLDB][Minidump] Support minidumps where there are multiple exception streams (#97470)
Currently, LLDB assumes all minidumps will have unique sections. This is
intuitive because almost all of the minidump sections are themselves
lists. Exceptions including Signals are unique in that they are all
individual sections with their own directory.
This means LLDB fails to load minidumps with multiple exceptions due to
them not being unique. This behavior is erroneous and this PR introduces
support for an arbitrary number of exception streams. Additionally, stop
info was calculated only for a single thread before, and now we properly
support mapping exceptions to threads.
~~This PR is starting in DRAFT because implementing testing is still
required.~~
Added:
lldb/test/API/functionalities/postmortem/minidump-new/multiple-sigsev.yaml
Modified:
lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
lldb/source/Plugins/Process/minidump/MinidumpParser.h
lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
lldb/source/Plugins/Process/minidump/ProcessMinidump.h
lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
lldb/unittests/Process/minidump/MinidumpParserTest.cpp
Removed:
################################################################################
diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
index c099c28a620ecf..afc095ddbb2f91 100644
--- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
+++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -417,14 +417,9 @@ std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
return filtered_modules;
}
-const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() {
- auto ExpectedStream = GetMinidumpFile().getExceptionStream();
- if (ExpectedStream)
- return &*ExpectedStream;
-
- LLDB_LOG_ERROR(GetLog(LLDBLog::Process), ExpectedStream.takeError(),
- "Failed to read minidump exception stream: {0}");
- return nullptr;
+llvm::iterator_range<ExceptionStreamsIterator>
+MinidumpParser::GetExceptionStreams() {
+ return GetMinidumpFile().getExceptionStreams();
}
std::optional<minidump::Range>
diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/lldb/source/Plugins/Process/minidump/MinidumpParser.h
index 222c0ef47fb853..f0b6e6027c52f0 100644
--- a/lldb/source/Plugins/Process/minidump/MinidumpParser.h
+++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.h
@@ -48,6 +48,8 @@ struct Range {
};
using FallibleMemory64Iterator = llvm::object::MinidumpFile::FallibleMemory64Iterator;
+using ExceptionStreamsIterator =
+ llvm::object::MinidumpFile::ExceptionStreamsIterator;
class MinidumpParser {
public:
@@ -84,7 +86,7 @@ class MinidumpParser {
// have the same name, it keeps the copy with the lowest load address.
std::vector<const minidump::Module *> GetFilteredModuleList();
- const llvm::minidump::ExceptionStream *GetExceptionStream();
+ llvm::iterator_range<ExceptionStreamsIterator> GetExceptionStreams();
std::optional<Range> FindMemoryRange(lldb::addr_t addr);
diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
index ac1ecbfc0e2e70..42cc9f02518b32 100644
--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
+++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -157,8 +157,7 @@ ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,
const FileSpec &core_file,
DataBufferSP core_data)
: PostMortemProcess(target_sp, listener_sp, core_file),
- m_core_data(std::move(core_data)), m_active_exception(nullptr),
- m_is_wow64(false) {}
+ m_core_data(std::move(core_data)), m_is_wow64(false) {}
ProcessMinidump::~ProcessMinidump() {
Clear();
@@ -209,7 +208,20 @@ Status ProcessMinidump::DoLoadCore() {
GetTarget().SetArchitecture(arch, true /*set_platform*/);
m_thread_list = m_minidump_parser->GetThreads();
- m_active_exception = m_minidump_parser->GetExceptionStream();
+ auto exception_stream_it = m_minidump_parser->GetExceptionStreams();
+ for (auto exception_stream : exception_stream_it) {
+ // If we can't read an exception stream skip it
+ // We should probably serve a warning
+ if (!exception_stream)
+ continue;
+
+ if (!m_exceptions_by_tid
+ .try_emplace(exception_stream->ThreadId, exception_stream.get())
+ .second) {
+ return Status::FromErrorStringWithFormatv(
+ "Duplicate exception stream for tid {0}", exception_stream->ThreadId);
+ }
+ }
SetUnixSignals(UnixSignals::Create(GetArchitecture()));
@@ -232,60 +244,57 @@ Status ProcessMinidump::DoDestroy() { return Status(); }
void ProcessMinidump::RefreshStateAfterStop() {
- if (!m_active_exception)
- return;
-
- constexpr uint32_t BreakpadDumpRequested = 0xFFFFFFFF;
- if (m_active_exception->ExceptionRecord.ExceptionCode ==
- BreakpadDumpRequested) {
- // This "ExceptionCode" value is a sentinel that is sometimes used
- // when generating a dump for a process that hasn't crashed.
-
- // TODO: The definition and use of this "dump requested" constant
- // in Breakpad are actually Linux-specific, and for similar use
- // cases on Mac/Windows it defines
diff erent constants, referring
- // to them as "simulated" exceptions; consider moving this check
- // down to the OS-specific paths and checking each OS for its own
- // constant.
- return;
- }
+ for (const auto &[_, exception_stream] : m_exceptions_by_tid) {
+ constexpr uint32_t BreakpadDumpRequested = 0xFFFFFFFF;
+ if (exception_stream.ExceptionRecord.ExceptionCode ==
+ BreakpadDumpRequested) {
+ // This "ExceptionCode" value is a sentinel that is sometimes used
+ // when generating a dump for a process that hasn't crashed.
+
+ // TODO: The definition and use of this "dump requested" constant
+ // in Breakpad are actually Linux-specific, and for similar use
+ // cases on Mac/Windows it defines
diff erent constants, referring
+ // to them as "simulated" exceptions; consider moving this check
+ // down to the OS-specific paths and checking each OS for its own
+ // constant.
+ return;
+ }
- lldb::StopInfoSP stop_info;
- lldb::ThreadSP stop_thread;
+ lldb::StopInfoSP stop_info;
+ lldb::ThreadSP stop_thread;
- Process::m_thread_list.SetSelectedThreadByID(m_active_exception->ThreadId);
- stop_thread = Process::m_thread_list.GetSelectedThread();
- ArchSpec arch = GetArchitecture();
+ Process::m_thread_list.SetSelectedThreadByID(exception_stream.ThreadId);
+ stop_thread = Process::m_thread_list.GetSelectedThread();
+ ArchSpec arch = GetArchitecture();
- if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
- uint32_t signo = m_active_exception->ExceptionRecord.ExceptionCode;
+ if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
+ uint32_t signo = exception_stream.ExceptionRecord.ExceptionCode;
+ if (signo == 0) {
+ // No stop.
+ return;
+ }
- if (signo == 0) {
- // No stop.
- return;
+ stop_info = StopInfo::CreateStopReasonWithSignal(*stop_thread, signo);
+ } else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {
+ stop_info = StopInfoMachException::CreateStopReasonWithMachException(
+ *stop_thread, exception_stream.ExceptionRecord.ExceptionCode, 2,
+ exception_stream.ExceptionRecord.ExceptionFlags,
+ exception_stream.ExceptionRecord.ExceptionAddress, 0);
+ } else {
+ std::string desc;
+ llvm::raw_string_ostream desc_stream(desc);
+ desc_stream << "Exception "
+ << llvm::format_hex(
+ exception_stream.ExceptionRecord.ExceptionCode, 8)
+ << " encountered at address "
+ << llvm::format_hex(
+ exception_stream.ExceptionRecord.ExceptionAddress, 8);
+ stop_info = StopInfo::CreateStopReasonWithException(
+ *stop_thread, desc_stream.str().c_str());
}
- stop_info = StopInfo::CreateStopReasonWithSignal(
- *stop_thread, signo);
- } else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {
- stop_info = StopInfoMachException::CreateStopReasonWithMachException(
- *stop_thread, m_active_exception->ExceptionRecord.ExceptionCode, 2,
- m_active_exception->ExceptionRecord.ExceptionFlags,
- m_active_exception->ExceptionRecord.ExceptionAddress, 0);
- } else {
- std::string desc;
- llvm::raw_string_ostream desc_stream(desc);
- desc_stream << "Exception "
- << llvm::format_hex(
- m_active_exception->ExceptionRecord.ExceptionCode, 8)
- << " encountered at address "
- << llvm::format_hex(
- m_active_exception->ExceptionRecord.ExceptionAddress, 8);
- stop_info = StopInfo::CreateStopReasonWithException(
- *stop_thread, desc_stream.str().c_str());
- }
-
- stop_thread->SetStopInfo(stop_info);
+ stop_thread->SetStopInfo(stop_info);
+ }
}
bool ProcessMinidump::IsAlive() { return true; }
@@ -387,10 +396,9 @@ bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list,
LocationDescriptor context_location = thread.Context;
// If the minidump contains an exception context, use it
- if (m_active_exception != nullptr &&
- m_active_exception->ThreadId == thread.ThreadId) {
- context_location = m_active_exception->ThreadContext;
- }
+ if (auto it = m_exceptions_by_tid.find(thread.ThreadId);
+ it != m_exceptions_by_tid.end())
+ context_location = it->second.ThreadContext;
llvm::ArrayRef<uint8_t> context;
if (!m_is_wow64)
diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
index e39ae3913e8782..f2ea0a2b61d14e 100644
--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
+++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
@@ -107,7 +107,8 @@ class ProcessMinidump : public PostMortemProcess {
private:
lldb::DataBufferSP m_core_data;
llvm::ArrayRef<minidump::Thread> m_thread_list;
- const minidump::ExceptionStream *m_active_exception;
+ std::unordered_map<uint32_t, const minidump::ExceptionStream>
+ m_exceptions_by_tid;
lldb::CommandObjectSP m_command_sp;
bool m_is_wow64;
std::optional<MemoryRegionInfos> m_memory_regions;
diff --git a/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py b/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
index 2de3e36b507341..5a0b6e790a424c 100644
--- a/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
+++ b/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
@@ -505,8 +505,22 @@ def test_minidump_memory64list(self):
self.assertEqual(region.GetRegionBase(), 0x7FFF12A84030)
self.assertTrue(region.GetRegionEnd(), 0x7FFF12A84030 + 0x2FD0)
self.assertTrue(region_info_list.GetMemoryRegionAtIndex(1, region))
- self.assertEqual(region.GetRegionBase(), 0x00007fff12a87000)
- self.assertTrue(region.GetRegionEnd(), 0x00007fff12a87000 + 0x00000018)
+ self.assertEqual(region.GetRegionBase(), 0x00007FFF12A87000)
+ self.assertTrue(region.GetRegionEnd(), 0x00007FFF12A87000 + 0x00000018)
self.assertTrue(region_info_list.GetMemoryRegionAtIndex(2, region))
- self.assertEqual(region.GetRegionBase(), 0x00007fff12a87018)
- self.assertTrue(region.GetRegionEnd(), 0x00007fff12a87018 + 0x00000400)
+ self.assertEqual(region.GetRegionBase(), 0x00007FFF12A87018)
+ self.assertTrue(region.GetRegionEnd(), 0x00007FFF12A87018 + 0x00000400)
+
+ def test_multiple_exceptions_or_signals(self):
+ """Test that lldb can read the exception information from the Minidump."""
+ print("Starting to read multiple-sigsev.yaml")
+ self.process_from_yaml("multiple-sigsev.yaml")
+ print("Done reading multiple-sigsev.yaml")
+ self.check_state()
+ # This process crashed due to a segmentation fault in both it's threads.
+ self.assertEqual(self.process.GetNumThreads(), 2)
+ for i in range(2):
+ thread = self.process.GetThreadAtIndex(i)
+ self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonSignal)
+ stop_description = thread.GetStopDescription(256)
+ self.assertIn("SIGSEGV", stop_description)
diff --git a/lldb/test/API/functionalities/postmortem/minidump-new/multiple-sigsev.yaml b/lldb/test/API/functionalities/postmortem/minidump-new/multiple-sigsev.yaml
new file mode 100644
index 00000000000000..f6fcfdbf5c0eb0
--- /dev/null
+++ b/lldb/test/API/functionalities/postmortem/minidump-new/multiple-sigsev.yaml
@@ -0,0 +1,39 @@
+--- !minidump
+Streams:
+ - Type: ThreadList
+ Threads:
+ - Thread Id: 0x1B4F23
+ Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0010000000000033000000000000000000000006020100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000010A234EBFC7F000010A234EBFC7F00000000000000000000F09C34EBFC7F0000C0A91ABCE97F00000000000000000000A0163FBCE97F00004602000000000000921C40000000000030A434EBFC7F000000000000000000000000000000000000C61D4000000000007F0300000000000000000000000000000000000000000000801F0000FFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF25252525252525252525252525252525000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ Stack:
+ Start of Memory Range: 0x7FFFFFFFD348
+ Content: ''
+ - Thread Id: 0x1B6D22
+ Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0010000000000033000000000000000000000006020100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000010A234EBFC7F000010A234EBFC7F00000000000000000000F09C34EBFC7F0000C0A91ABCE97F00000000000000000000A0163FBCE97F00004602000000000000921C40000000000030A434EBFC7F000000000000000000000000000000000000C61D4000000000007F0300000000000000000000000000000000000000000000801F0000FFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF25252525252525252525252525252525000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ Stack:
+ Start of Memory Range: 0x7FFFF75FDE28
+ Content: ''
+ - Type: ModuleList
+ Modules:
+ - Base of Image: 0x0000000000400000
+ Size of Image: 0x00017000
+ Module Name: 'a.out'
+ CodeView Record: ''
+ - Type: SystemInfo
+ Processor Arch: AMD64
+ Platform ID: Linux
+ CSD Version: 'Linux 3.13'
+ CPU:
+ Vendor ID: GenuineIntel
+ Version Info: 0x00000000
+ Feature Info: 0x00000000
+ - Type: Exception
+ Thread ID: 0x1B4F23
+ Exception Record:
+ Exception Code: 0xB
+ Thread Context: 00000000
+ - Type: Exception
+ Thread ID: 0x1B6D22
+ Exception Record:
+ Exception Code: 0xB
+ Thread Context: 00000000
+...
diff --git a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp
index 632a7fd4e4f8fa..c7547ba261c7f7 100644
--- a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp
+++ b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp
@@ -251,10 +251,15 @@ TEST_F(MinidumpParserTest, GetFilteredModuleList) {
TEST_F(MinidumpParserTest, GetExceptionStream) {
SetUpData("linux-x86_64.dmp");
- const llvm::minidump::ExceptionStream *exception_stream =
- parser->GetExceptionStream();
- ASSERT_NE(nullptr, exception_stream);
- ASSERT_EQ(11UL, exception_stream->ExceptionRecord.ExceptionCode);
+ auto exception_streams = parser->GetExceptionStreams();
+ size_t count = 0;
+ for (auto exception_stream : exception_streams) {
+ ASSERT_THAT_EXPECTED(exception_stream, llvm::Succeeded());
+ ASSERT_EQ(16001UL, exception_stream->ThreadId);
+ count++;
+ }
+
+ ASSERT_THAT(1UL, count);
}
void check_mem_range_exists(MinidumpParser &parser, const uint64_t range_start,
More information about the lldb-commits
mailing list