Skip to content

[LLDB][Minidump] Support minidumps where there are multiple exception streams #97470

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
4 changes: 3 additions & 1 deletion lldb/source/Plugins/Process/minidump/MinidumpParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ struct Range {
};

using FallibleMemory64Iterator = llvm::object::MinidumpFile::FallibleMemory64Iterator;
using ExceptionStreamsIterator =
llvm::object::MinidumpFile::ExceptionStreamsIterator;

class MinidumpParser {
public:
Expand Down Expand Up @@ -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);

Expand Down
118 changes: 63 additions & 55 deletions lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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()));

Expand All @@ -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 different 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 different 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; }
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Plugins/Process/minidump/ProcessMinidump.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
@@ -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
...
13 changes: 9 additions & 4 deletions lldb/unittests/Process/minidump/MinidumpParserTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading