Skip to content

Commit 4926835

Browse files
authored
[LLDB][Minidump] Support minidumps where there are multiple exception streams (llvm#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.~~
1 parent 094e6b8 commit 4926835

File tree

7 files changed

+137
-73
lines changed

7 files changed

+137
-73
lines changed

lldb/source/Plugins/Process/minidump/MinidumpParser.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -417,14 +417,9 @@ std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
417417
return filtered_modules;
418418
}
419419

420-
const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() {
421-
auto ExpectedStream = GetMinidumpFile().getExceptionStream();
422-
if (ExpectedStream)
423-
return &*ExpectedStream;
424-
425-
LLDB_LOG_ERROR(GetLog(LLDBLog::Process), ExpectedStream.takeError(),
426-
"Failed to read minidump exception stream: {0}");
427-
return nullptr;
420+
llvm::iterator_range<ExceptionStreamsIterator>
421+
MinidumpParser::GetExceptionStreams() {
422+
return GetMinidumpFile().getExceptionStreams();
428423
}
429424

430425
std::optional<minidump::Range>

lldb/source/Plugins/Process/minidump/MinidumpParser.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ struct Range {
4848
};
4949

5050
using FallibleMemory64Iterator = llvm::object::MinidumpFile::FallibleMemory64Iterator;
51+
using ExceptionStreamsIterator =
52+
llvm::object::MinidumpFile::ExceptionStreamsIterator;
5153

5254
class MinidumpParser {
5355
public:
@@ -84,7 +86,7 @@ class MinidumpParser {
8486
// have the same name, it keeps the copy with the lowest load address.
8587
std::vector<const minidump::Module *> GetFilteredModuleList();
8688

87-
const llvm::minidump::ExceptionStream *GetExceptionStream();
89+
llvm::iterator_range<ExceptionStreamsIterator> GetExceptionStreams();
8890

8991
std::optional<Range> FindMemoryRange(lldb::addr_t addr);
9092

lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp

Lines changed: 63 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,7 @@ ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,
157157
const FileSpec &core_file,
158158
DataBufferSP core_data)
159159
: PostMortemProcess(target_sp, listener_sp, core_file),
160-
m_core_data(std::move(core_data)), m_active_exception(nullptr),
161-
m_is_wow64(false) {}
160+
m_core_data(std::move(core_data)), m_is_wow64(false) {}
162161

163162
ProcessMinidump::~ProcessMinidump() {
164163
Clear();
@@ -209,7 +208,20 @@ Status ProcessMinidump::DoLoadCore() {
209208
GetTarget().SetArchitecture(arch, true /*set_platform*/);
210209

211210
m_thread_list = m_minidump_parser->GetThreads();
212-
m_active_exception = m_minidump_parser->GetExceptionStream();
211+
auto exception_stream_it = m_minidump_parser->GetExceptionStreams();
212+
for (auto exception_stream : exception_stream_it) {
213+
// If we can't read an exception stream skip it
214+
// We should probably serve a warning
215+
if (!exception_stream)
216+
continue;
217+
218+
if (!m_exceptions_by_tid
219+
.try_emplace(exception_stream->ThreadId, exception_stream.get())
220+
.second) {
221+
return Status::FromErrorStringWithFormatv(
222+
"Duplicate exception stream for tid {0}", exception_stream->ThreadId);
223+
}
224+
}
213225

214226
SetUnixSignals(UnixSignals::Create(GetArchitecture()));
215227

@@ -232,60 +244,57 @@ Status ProcessMinidump::DoDestroy() { return Status(); }
232244

233245
void ProcessMinidump::RefreshStateAfterStop() {
234246

235-
if (!m_active_exception)
236-
return;
237-
238-
constexpr uint32_t BreakpadDumpRequested = 0xFFFFFFFF;
239-
if (m_active_exception->ExceptionRecord.ExceptionCode ==
240-
BreakpadDumpRequested) {
241-
// This "ExceptionCode" value is a sentinel that is sometimes used
242-
// when generating a dump for a process that hasn't crashed.
243-
244-
// TODO: The definition and use of this "dump requested" constant
245-
// in Breakpad are actually Linux-specific, and for similar use
246-
// cases on Mac/Windows it defines different constants, referring
247-
// to them as "simulated" exceptions; consider moving this check
248-
// down to the OS-specific paths and checking each OS for its own
249-
// constant.
250-
return;
251-
}
247+
for (const auto &[_, exception_stream] : m_exceptions_by_tid) {
248+
constexpr uint32_t BreakpadDumpRequested = 0xFFFFFFFF;
249+
if (exception_stream.ExceptionRecord.ExceptionCode ==
250+
BreakpadDumpRequested) {
251+
// This "ExceptionCode" value is a sentinel that is sometimes used
252+
// when generating a dump for a process that hasn't crashed.
253+
254+
// TODO: The definition and use of this "dump requested" constant
255+
// in Breakpad are actually Linux-specific, and for similar use
256+
// cases on Mac/Windows it defines different constants, referring
257+
// to them as "simulated" exceptions; consider moving this check
258+
// down to the OS-specific paths and checking each OS for its own
259+
// constant.
260+
return;
261+
}
252262

253-
lldb::StopInfoSP stop_info;
254-
lldb::ThreadSP stop_thread;
263+
lldb::StopInfoSP stop_info;
264+
lldb::ThreadSP stop_thread;
255265

256-
Process::m_thread_list.SetSelectedThreadByID(m_active_exception->ThreadId);
257-
stop_thread = Process::m_thread_list.GetSelectedThread();
258-
ArchSpec arch = GetArchitecture();
266+
Process::m_thread_list.SetSelectedThreadByID(exception_stream.ThreadId);
267+
stop_thread = Process::m_thread_list.GetSelectedThread();
268+
ArchSpec arch = GetArchitecture();
259269

260-
if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
261-
uint32_t signo = m_active_exception->ExceptionRecord.ExceptionCode;
270+
if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
271+
uint32_t signo = exception_stream.ExceptionRecord.ExceptionCode;
272+
if (signo == 0) {
273+
// No stop.
274+
return;
275+
}
262276

263-
if (signo == 0) {
264-
// No stop.
265-
return;
277+
stop_info = StopInfo::CreateStopReasonWithSignal(*stop_thread, signo);
278+
} else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {
279+
stop_info = StopInfoMachException::CreateStopReasonWithMachException(
280+
*stop_thread, exception_stream.ExceptionRecord.ExceptionCode, 2,
281+
exception_stream.ExceptionRecord.ExceptionFlags,
282+
exception_stream.ExceptionRecord.ExceptionAddress, 0);
283+
} else {
284+
std::string desc;
285+
llvm::raw_string_ostream desc_stream(desc);
286+
desc_stream << "Exception "
287+
<< llvm::format_hex(
288+
exception_stream.ExceptionRecord.ExceptionCode, 8)
289+
<< " encountered at address "
290+
<< llvm::format_hex(
291+
exception_stream.ExceptionRecord.ExceptionAddress, 8);
292+
stop_info = StopInfo::CreateStopReasonWithException(
293+
*stop_thread, desc_stream.str().c_str());
266294
}
267295

268-
stop_info = StopInfo::CreateStopReasonWithSignal(
269-
*stop_thread, signo);
270-
} else if (arch.GetTriple().getVendor() == llvm::Triple::Apple) {
271-
stop_info = StopInfoMachException::CreateStopReasonWithMachException(
272-
*stop_thread, m_active_exception->ExceptionRecord.ExceptionCode, 2,
273-
m_active_exception->ExceptionRecord.ExceptionFlags,
274-
m_active_exception->ExceptionRecord.ExceptionAddress, 0);
275-
} else {
276-
std::string desc;
277-
llvm::raw_string_ostream desc_stream(desc);
278-
desc_stream << "Exception "
279-
<< llvm::format_hex(
280-
m_active_exception->ExceptionRecord.ExceptionCode, 8)
281-
<< " encountered at address "
282-
<< llvm::format_hex(
283-
m_active_exception->ExceptionRecord.ExceptionAddress, 8);
284-
stop_info = StopInfo::CreateStopReasonWithException(
285-
*stop_thread, desc_stream.str().c_str());
286-
}
287-
288-
stop_thread->SetStopInfo(stop_info);
296+
stop_thread->SetStopInfo(stop_info);
297+
}
289298
}
290299

291300
bool ProcessMinidump::IsAlive() { return true; }
@@ -387,10 +396,9 @@ bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list,
387396
LocationDescriptor context_location = thread.Context;
388397

389398
// If the minidump contains an exception context, use it
390-
if (m_active_exception != nullptr &&
391-
m_active_exception->ThreadId == thread.ThreadId) {
392-
context_location = m_active_exception->ThreadContext;
393-
}
399+
if (auto it = m_exceptions_by_tid.find(thread.ThreadId);
400+
it != m_exceptions_by_tid.end())
401+
context_location = it->second.ThreadContext;
394402

395403
llvm::ArrayRef<uint8_t> context;
396404
if (!m_is_wow64)

lldb/source/Plugins/Process/minidump/ProcessMinidump.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ class ProcessMinidump : public PostMortemProcess {
107107
private:
108108
lldb::DataBufferSP m_core_data;
109109
llvm::ArrayRef<minidump::Thread> m_thread_list;
110-
const minidump::ExceptionStream *m_active_exception;
110+
std::unordered_map<uint32_t, const minidump::ExceptionStream>
111+
m_exceptions_by_tid;
111112
lldb::CommandObjectSP m_command_sp;
112113
bool m_is_wow64;
113114
std::optional<MemoryRegionInfos> m_memory_regions;

lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -505,8 +505,22 @@ def test_minidump_memory64list(self):
505505
self.assertEqual(region.GetRegionBase(), 0x7FFF12A84030)
506506
self.assertTrue(region.GetRegionEnd(), 0x7FFF12A84030 + 0x2FD0)
507507
self.assertTrue(region_info_list.GetMemoryRegionAtIndex(1, region))
508-
self.assertEqual(region.GetRegionBase(), 0x00007fff12a87000)
509-
self.assertTrue(region.GetRegionEnd(), 0x00007fff12a87000 + 0x00000018)
508+
self.assertEqual(region.GetRegionBase(), 0x00007FFF12A87000)
509+
self.assertTrue(region.GetRegionEnd(), 0x00007FFF12A87000 + 0x00000018)
510510
self.assertTrue(region_info_list.GetMemoryRegionAtIndex(2, region))
511-
self.assertEqual(region.GetRegionBase(), 0x00007fff12a87018)
512-
self.assertTrue(region.GetRegionEnd(), 0x00007fff12a87018 + 0x00000400)
511+
self.assertEqual(region.GetRegionBase(), 0x00007FFF12A87018)
512+
self.assertTrue(region.GetRegionEnd(), 0x00007FFF12A87018 + 0x00000400)
513+
514+
def test_multiple_exceptions_or_signals(self):
515+
"""Test that lldb can read the exception information from the Minidump."""
516+
print("Starting to read multiple-sigsev.yaml")
517+
self.process_from_yaml("multiple-sigsev.yaml")
518+
print("Done reading multiple-sigsev.yaml")
519+
self.check_state()
520+
# This process crashed due to a segmentation fault in both it's threads.
521+
self.assertEqual(self.process.GetNumThreads(), 2)
522+
for i in range(2):
523+
thread = self.process.GetThreadAtIndex(i)
524+
self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonSignal)
525+
stop_description = thread.GetStopDescription(256)
526+
self.assertIn("SIGSEGV", stop_description)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--- !minidump
2+
Streams:
3+
- Type: ThreadList
4+
Threads:
5+
- Thread Id: 0x1B4F23
6+
Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0010000000000033000000000000000000000006020100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000010A234EBFC7F000010A234EBFC7F00000000000000000000F09C34EBFC7F0000C0A91ABCE97F00000000000000000000A0163FBCE97F00004602000000000000921C40000000000030A434EBFC7F000000000000000000000000000000000000C61D4000000000007F0300000000000000000000000000000000000000000000801F0000FFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF25252525252525252525252525252525000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7+
Stack:
8+
Start of Memory Range: 0x7FFFFFFFD348
9+
Content: ''
10+
- Thread Id: 0x1B6D22
11+
Context: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B0010000000000033000000000000000000000006020100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000010A234EBFC7F000010A234EBFC7F00000000000000000000F09C34EBFC7F0000C0A91ABCE97F00000000000000000000A0163FBCE97F00004602000000000000921C40000000000030A434EBFC7F000000000000000000000000000000000000C61D4000000000007F0300000000000000000000000000000000000000000000801F0000FFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF25252525252525252525252525252525000000000000000000000000000000000000000000000000000000000000000000FFFF00FFFFFFFFFFFFFF00FFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
12+
Stack:
13+
Start of Memory Range: 0x7FFFF75FDE28
14+
Content: ''
15+
- Type: ModuleList
16+
Modules:
17+
- Base of Image: 0x0000000000400000
18+
Size of Image: 0x00017000
19+
Module Name: 'a.out'
20+
CodeView Record: ''
21+
- Type: SystemInfo
22+
Processor Arch: AMD64
23+
Platform ID: Linux
24+
CSD Version: 'Linux 3.13'
25+
CPU:
26+
Vendor ID: GenuineIntel
27+
Version Info: 0x00000000
28+
Feature Info: 0x00000000
29+
- Type: Exception
30+
Thread ID: 0x1B4F23
31+
Exception Record:
32+
Exception Code: 0xB
33+
Thread Context: 00000000
34+
- Type: Exception
35+
Thread ID: 0x1B6D22
36+
Exception Record:
37+
Exception Code: 0xB
38+
Thread Context: 00000000
39+
...

lldb/unittests/Process/minidump/MinidumpParserTest.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,15 @@ TEST_F(MinidumpParserTest, GetFilteredModuleList) {
251251

252252
TEST_F(MinidumpParserTest, GetExceptionStream) {
253253
SetUpData("linux-x86_64.dmp");
254-
const llvm::minidump::ExceptionStream *exception_stream =
255-
parser->GetExceptionStream();
256-
ASSERT_NE(nullptr, exception_stream);
257-
ASSERT_EQ(11UL, exception_stream->ExceptionRecord.ExceptionCode);
254+
auto exception_streams = parser->GetExceptionStreams();
255+
size_t count = 0;
256+
for (auto exception_stream : exception_streams) {
257+
ASSERT_THAT_EXPECTED(exception_stream, llvm::Succeeded());
258+
ASSERT_EQ(16001UL, exception_stream->ThreadId);
259+
count++;
260+
}
261+
262+
ASSERT_THAT(1UL, count);
258263
}
259264

260265
void check_mem_range_exists(MinidumpParser &parser, const uint64_t range_start,

0 commit comments

Comments
 (0)