Skip to content

Commit e8d1f3a

Browse files
committed
Add support to read multiple exception streams in minidumps
1 parent 57555c6 commit e8d1f3a

File tree

10 files changed

+162
-78
lines changed

10 files changed

+162
-78
lines changed

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
408408
continue;
409409
}
410410
// This module has been seen. Modules are sometimes mentioned multiple
411-
// times when they are mapped discontiguously, so find the module with
411+
// times when they are mapped discontiguously, so find the module with
412412
// the lowest "base_of_image" and use that as the filtered module.
413413
if (module.BaseOfImage < dup_module->BaseOfImage)
414414
filtered_modules[iter->second] = &module;
@@ -417,14 +417,15 @@ std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
417417
return filtered_modules;
418418
}
419419

420-
const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() {
421-
auto ExpectedStream = GetMinidumpFile().getExceptionStream();
420+
const std::vector<minidump::ExceptionStream> MinidumpParser::GetExceptionStreams() {
421+
auto ExpectedStream = GetMinidumpFile().getExceptionStreams();
422422
if (ExpectedStream)
423-
return &*ExpectedStream;
423+
return ExpectedStream.get();
424424

425425
LLDB_LOG_ERROR(GetLog(LLDBLog::Process), ExpectedStream.takeError(),
426426
"Failed to read minidump exception stream: {0}");
427-
return nullptr;
427+
// return empty on failure.
428+
return std::vector<minidump::ExceptionStream>();
428429
}
429430

430431
std::optional<minidump::Range>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class MinidumpParser {
8282
// have the same name, it keeps the copy with the lowest load address.
8383
std::vector<const minidump::Module *> GetFilteredModuleList();
8484

85-
const llvm::minidump::ExceptionStream *GetExceptionStream();
85+
const std::vector<llvm::minidump::ExceptionStream> GetExceptionStreams();
8686

8787
std::optional<Range> FindMemoryRange(lldb::addr_t addr);
8888

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

Lines changed: 68 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
#include <memory>
4141
#include <optional>
42+
#include <iostream>
4243

4344
using namespace lldb;
4445
using namespace lldb_private;
@@ -157,7 +158,7 @@ ProcessMinidump::ProcessMinidump(lldb::TargetSP target_sp,
157158
const FileSpec &core_file,
158159
DataBufferSP core_data)
159160
: PostMortemProcess(target_sp, listener_sp, core_file),
160-
m_core_data(std::move(core_data)), m_active_exception(nullptr),
161+
m_core_data(std::move(core_data)),
161162
m_is_wow64(false) {}
162163

163164
ProcessMinidump::~ProcessMinidump() {
@@ -209,7 +210,19 @@ Status ProcessMinidump::DoLoadCore() {
209210
GetTarget().SetArchitecture(arch, true /*set_platform*/);
210211

211212
m_thread_list = m_minidump_parser->GetThreads();
212-
m_active_exception = m_minidump_parser->GetExceptionStream();
213+
std::vector<minidump::ExceptionStream> exception_streams = m_minidump_parser->GetExceptionStreams();
214+
for (const auto &exception_stream : exception_streams) {
215+
if (m_exceptions_by_tid.count(exception_stream.ThreadId) > 0) {
216+
// We only cast to avoid the warning around converting little endian in printf.
217+
error.SetErrorStringWithFormat("duplicate exception stream for tid %" PRIu32, (uint32_t)exception_stream.ThreadId);
218+
return error;
219+
} else
220+
m_exceptions_by_tid[exception_stream.ThreadId] = exception_stream;
221+
222+
223+
std::cout << "Adding Exception Stream # " << (uint32_t)exception_stream.ThreadId << std::endl;
224+
std::cout << "Added index " << (uint32_t)m_exceptions_by_tid[exception_stream.ThreadId].ExceptionRecord.ExceptionCode << std::endl;
225+
}
213226

214227
SetUnixSignals(UnixSignals::Create(GetArchitecture()));
215228

@@ -232,60 +245,59 @@ Status ProcessMinidump::DoDestroy() { return Status(); }
232245

233246
void ProcessMinidump::RefreshStateAfterStop() {
234247

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

253-
lldb::StopInfoSP stop_info;
254-
lldb::ThreadSP stop_thread;
264+
lldb::StopInfoSP stop_info;
265+
lldb::ThreadSP stop_thread;
255266

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

260-
if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
261-
uint32_t signo = m_active_exception->ExceptionRecord.ExceptionCode;
271+
if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
272+
uint32_t signo = exception_stream.ExceptionRecord.ExceptionCode;
273+
std::cout << "Thread Id : " << exception_stream.ThreadId << " has signal " << signo << std::endl;
274+
if (signo == 0) {
275+
// No stop.
276+
return;
277+
}
262278

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

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);
299+
stop_thread->SetStopInfo(stop_info);
300+
}
289301
}
290302

291303
bool ProcessMinidump::IsAlive() { return true; }
@@ -386,19 +398,21 @@ bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list,
386398
for (const minidump::Thread &thread : m_thread_list) {
387399
LocationDescriptor context_location = thread.Context;
388400

401+
std::optional<minidump::Exception> exception;
389402
// 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;
403+
if (m_exceptions_by_tid.count(thread.ThreadId) > 0) {
404+
context_location = m_exceptions_by_tid[thread.ThreadId].ThreadContext;
405+
exception = m_exceptions_by_tid[thread.ThreadId].ExceptionRecord;
393406
}
394407

408+
395409
llvm::ArrayRef<uint8_t> context;
396410
if (!m_is_wow64)
397411
context = m_minidump_parser->GetThreadContext(context_location);
398412
else
399413
context = m_minidump_parser->GetThreadContextWow64(thread);
400414

401-
lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context));
415+
lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context, exception));
402416
new_thread_list.AddThread(thread_sp);
403417
}
404418
return new_thread_list.GetSize(false) > 0;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class ProcessMinidump : public PostMortemProcess {
109109
private:
110110
lldb::DataBufferSP m_core_data;
111111
llvm::ArrayRef<minidump::Thread> m_thread_list;
112-
const minidump::ExceptionStream *m_active_exception;
112+
std::unordered_map<uint32_t, minidump::ExceptionStream> m_exceptions_by_tid;
113113
lldb::CommandObjectSP m_command_sp;
114114
bool m_is_wow64;
115115
std::optional<MemoryRegionInfos> m_memory_regions;

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ using namespace lldb_private;
3434
using namespace minidump;
3535

3636
ThreadMinidump::ThreadMinidump(Process &process, const minidump::Thread &td,
37-
llvm::ArrayRef<uint8_t> gpregset_data)
37+
llvm::ArrayRef<uint8_t> gpregset_data, std::optional<minidump::Exception> exception)
3838
: Thread(process, td.ThreadId), m_thread_reg_ctx_sp(),
39-
m_gpregset_data(gpregset_data) {}
39+
m_gpregset_data(gpregset_data), m_exception(exception) {}
4040

4141
ThreadMinidump::~ThreadMinidump() = default;
4242

@@ -115,4 +115,12 @@ ThreadMinidump::CreateRegisterContextForFrame(StackFrame *frame) {
115115
return reg_ctx_sp;
116116
}
117117

118-
bool ThreadMinidump::CalculateStopInfo() { return false; }
118+
bool ThreadMinidump::CalculateStopInfo() {
119+
if (!m_exception)
120+
return false;
121+
122+
minidump::Exception thread_exception = m_exception.value();
123+
SetStopInfo(StopInfo::CreateStopReasonWithSignal(
124+
*this, thread_exception.ExceptionCode, /*description=*/nullptr, thread_exception.ExceptionCode));
125+
return true;
126+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace minidump {
2121
class ThreadMinidump : public Thread {
2222
public:
2323
ThreadMinidump(Process &process, const minidump::Thread &td,
24-
llvm::ArrayRef<uint8_t> gpregset_data);
24+
llvm::ArrayRef<uint8_t> gpregset_data, std::optional<minidump::Exception> exception);
2525

2626
~ThreadMinidump() override;
2727

@@ -35,6 +35,7 @@ class ThreadMinidump : public Thread {
3535
protected:
3636
lldb::RegisterContextSP m_thread_reg_ctx_sp;
3737
llvm::ArrayRef<uint8_t> m_gpregset_data;
38+
std::optional<minidump::Exception> m_exception;
3839

3940
bool CalculateStopInfo() override;
4041
};

lldb/unittests/Process/minidump/MinidumpParserTest.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,13 @@ 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+
llvm::Expected<std::vector<ExceptionStream>> exception_stream =
255+
parser->GetExceptionStreams();
256+
// LLVM::Expected has an explicit bool operator that determines if
257+
// the expected value is an error or not.
258+
ASSERT_TRUE((bool)exception_stream);
259+
ASSERT_EQ(1UL, exception_stream->size());
260+
ASSERT_EQ(11UL, exception_stream.get()[0].ExceptionRecord.ExceptionCode);
258261
}
259262

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

llvm/include/llvm/Object/Minidump.h

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,22 @@ class MinidumpFile : public Binary {
8282
return getListStream<minidump::Thread>(minidump::StreamType::ThreadList);
8383
}
8484

85-
/// Returns the contents of the Exception stream. An error is returned if the
86-
/// file does not contain this stream, or the stream is smaller than the size
87-
/// of the ExceptionStream structure. The internal consistency of the stream
88-
/// is not checked in any way.
89-
Expected<const minidump::ExceptionStream &> getExceptionStream() const {
90-
return getStream<minidump::ExceptionStream>(
91-
minidump::StreamType::Exception);
85+
/// Returns the contents of the Exception stream. An error is returned if the
86+
/// associated stream is smaller than the size of the ExceptionStream structure.
87+
/// Or the directory supplied is not of kind exception stream.
88+
Expected<minidump::ExceptionStream> getExceptionStream(minidump::Directory Directory) const {
89+
if (Directory.Type != minidump::StreamType::Exception) {
90+
return createError("Not an exception stream");
91+
}
92+
93+
return getStreamFromDirectory<minidump::ExceptionStream>(Directory);
9294
}
9395

96+
/// Returns the contents of the Exception streams. An error is returned if
97+
/// any of the streams are smaller than the size of the ExceptionStream structure.
98+
/// The internal consistency of the stream is not checked in any way.
99+
Expected<std::vector<minidump::ExceptionStream>> getExceptionStreams() const;
100+
94101
/// Returns the list of descriptors embedded in the MemoryList stream. The
95102
/// descriptors provide the content of interesting regions of memory at the
96103
/// time the minidump was taken. An error is returned if the file does not
@@ -172,6 +179,11 @@ class MinidumpFile : public Binary {
172179
return arrayRefFromStringRef(Data.getBuffer());
173180
}
174181

182+
/// Return the stream of the given type, cast to the appropriate type. Checks
183+
/// that the stream is large enough to hold an object of this type.
184+
template <typename T>
185+
Expected<const T &> getStreamFromDirectory(minidump::Directory Directory) const;
186+
175187
/// Return the stream of the given type, cast to the appropriate type. Checks
176188
/// that the stream is large enough to hold an object of this type.
177189
template <typename T>
@@ -187,6 +199,14 @@ class MinidumpFile : public Binary {
187199
DenseMap<minidump::StreamType, std::size_t> StreamMap;
188200
};
189201

202+
template <typename T>
203+
Expected<const T &> MinidumpFile::getStreamFromDirectory(minidump::Directory Directory) const {
204+
ArrayRef<uint8_t> Stream = getRawStream(Directory);
205+
if (Stream.size() >= sizeof(T))
206+
return *reinterpret_cast<const T *>(Stream.data());
207+
return createEOFError();
208+
}
209+
190210
template <typename T>
191211
Expected<const T &> MinidumpFile::getStream(minidump::StreamType Type) const {
192212
if (std::optional<ArrayRef<uint8_t>> Stream = getRawStream(Type)) {

0 commit comments

Comments
 (0)