(original) (raw)
diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp index c099c28a620ec..afc095ddbb2f9 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -417,14 +417,9 @@ std::vector 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+MinidumpParser::GetExceptionStreams() { + return GetMinidumpFile().getExceptionStreams(); } std::optionaldiff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/lldb/source/Plugins/Process/minidump/MinidumpParser.h index 222c0ef47fb85..f0b6e6027c52f 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 GetFilteredModuleList(); - const llvm::minidump::ExceptionStream *GetExceptionStream(); + llvm::iterator_range GetExceptionStreams(); std::optional FindMemoryRange(lldb::addr_t addr); diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp index ac1ecbfc0e2e7..42cc9f02518b3 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 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; } @@ -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 context; if (!m_is_wow64) diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h index e39ae3913e878..f2ea0a2b61d14 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 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 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 2de3e36b50734..5a0b6e790a424 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 0000000000000..f6fcfdbf5c0eb --- /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 632a7fd4e4f8f..c7547ba261c7f 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,</uint32_t,>