[Lldb-commits] [lldb] [llvm] [LLDB][Minidump] Support minidumps where there are multiple exception streams (PR #97470)

Jacob Lalonde via lldb-commits lldb-commits at lists.llvm.org
Tue Jul 2 15:00:14 PDT 2024


https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/97470

>From e8d1f3a7f978bd3c5767f0b0cacea60146a257f7 Mon Sep 17 00:00:00 2001
From: Jacob Lalonde <jalalonde at fb.com>
Date: Tue, 2 Jul 2024 12:41:02 -0700
Subject: [PATCH 1/4] Add support to read multiple exception streams in
 minidumps

---
 .../Process/minidump/MinidumpParser.cpp       |  11 +-
 .../Plugins/Process/minidump/MinidumpParser.h |   2 +-
 .../Process/minidump/ProcessMinidump.cpp      | 122 ++++++++++--------
 .../Process/minidump/ProcessMinidump.h        |   2 +-
 .../Process/minidump/ThreadMinidump.cpp       |  14 +-
 .../Plugins/Process/minidump/ThreadMinidump.h |   3 +-
 .../Process/minidump/MinidumpParserTest.cpp   |  11 +-
 llvm/include/llvm/Object/Minidump.h           |  34 ++++-
 llvm/lib/Object/Minidump.cpp                  |  37 ++++++
 llvm/lib/ObjectYAML/MinidumpYAML.cpp          |   4 +-
 10 files changed, 162 insertions(+), 78 deletions(-)

diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
index be9fae938e227..ac487a5ed0c0a 100644
--- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
+++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -408,7 +408,7 @@ std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
         continue;
       }
       // This module has been seen. Modules are sometimes mentioned multiple
-      // times when they are mapped discontiguously, so find the module with
+      // times when they are mapped discontiguously, so find the module with 
       // the lowest "base_of_image" and use that as the filtered module.
       if (module.BaseOfImage < dup_module->BaseOfImage)
         filtered_modules[iter->second] = &module;
@@ -417,14 +417,15 @@ std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
   return filtered_modules;
 }
 
-const minidump::ExceptionStream *MinidumpParser::GetExceptionStream() {
-  auto ExpectedStream = GetMinidumpFile().getExceptionStream();
+const std::vector<minidump::ExceptionStream> MinidumpParser::GetExceptionStreams() {
+  auto ExpectedStream = GetMinidumpFile().getExceptionStreams();
   if (ExpectedStream)
-    return &*ExpectedStream;
+    return ExpectedStream.get();
 
   LLDB_LOG_ERROR(GetLog(LLDBLog::Process), ExpectedStream.takeError(),
                  "Failed to read minidump exception stream: {0}");
-  return nullptr;
+  // return empty on failure.
+  return std::vector<minidump::ExceptionStream>();
 }
 
 std::optional<minidump::Range>
diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/lldb/source/Plugins/Process/minidump/MinidumpParser.h
index 050ba086f46f5..e552c7210e330 100644
--- a/lldb/source/Plugins/Process/minidump/MinidumpParser.h
+++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.h
@@ -82,7 +82,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();
+  const std::vector<llvm::minidump::ExceptionStream> 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 13599f4a1553f..9f707c0d8a7a7 100644
--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
+++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -39,6 +39,7 @@
 
 #include <memory>
 #include <optional>
+#include <iostream>
 
 using namespace lldb;
 using namespace lldb_private;
@@ -157,7 +158,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_core_data(std::move(core_data)),
       m_is_wow64(false) {}
 
 ProcessMinidump::~ProcessMinidump() {
@@ -209,7 +210,19 @@ Status ProcessMinidump::DoLoadCore() {
   GetTarget().SetArchitecture(arch, true /*set_platform*/);
 
   m_thread_list = m_minidump_parser->GetThreads();
-  m_active_exception = m_minidump_parser->GetExceptionStream();
+  std::vector<minidump::ExceptionStream> exception_streams = m_minidump_parser->GetExceptionStreams();
+  for (const auto &exception_stream : exception_streams) {
+    if (m_exceptions_by_tid.count(exception_stream.ThreadId) > 0) {
+      // We only cast to avoid the warning around converting little endian in printf.
+      error.SetErrorStringWithFormat("duplicate exception stream for tid %" PRIu32, (uint32_t)exception_stream.ThreadId);
+      return error;
+    } else 
+      m_exceptions_by_tid[exception_stream.ThreadId] = exception_stream;
+
+
+    std::cout << "Adding Exception Stream # " << (uint32_t)exception_stream.ThreadId << std::endl;
+    std::cout << "Added index " << (uint32_t)m_exceptions_by_tid[exception_stream.ThreadId].ExceptionRecord.ExceptionCode << std::endl;
+  }
 
   SetUnixSignals(UnixSignals::Create(GetArchitecture()));
 
@@ -232,60 +245,59 @@ 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 (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;
+      std::cout << "Thread Id : " << exception_stream.ThreadId << " has signal " << signo << std::endl;
+      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; }
@@ -386,19 +398,21 @@ bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list,
   for (const minidump::Thread &thread : m_thread_list) {
     LocationDescriptor context_location = thread.Context;
 
+    std::optional<minidump::Exception> exception;
     // 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 (m_exceptions_by_tid.count(thread.ThreadId) > 0) {
+      context_location = m_exceptions_by_tid[thread.ThreadId].ThreadContext;
+      exception = m_exceptions_by_tid[thread.ThreadId].ExceptionRecord;
     }
 
+
     llvm::ArrayRef<uint8_t> context;
     if (!m_is_wow64)
       context = m_minidump_parser->GetThreadContext(context_location);
     else
       context = m_minidump_parser->GetThreadContextWow64(thread);
 
-    lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context));
+    lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context, exception));
     new_thread_list.AddThread(thread_sp);
   }
   return new_thread_list.GetSize(false) > 0;
diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
index 3f3123a0a8b5d..0379de7bee5e6 100644
--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
+++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
@@ -109,7 +109,7 @@ 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, 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/source/Plugins/Process/minidump/ThreadMinidump.cpp b/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
index 1fbc52815238b..3cc154f1eac47 100644
--- a/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
+++ b/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
@@ -34,9 +34,9 @@ using namespace lldb_private;
 using namespace minidump;
 
 ThreadMinidump::ThreadMinidump(Process &process, const minidump::Thread &td,
-                               llvm::ArrayRef<uint8_t> gpregset_data)
+                               llvm::ArrayRef<uint8_t> gpregset_data, std::optional<minidump::Exception> exception)
     : Thread(process, td.ThreadId), m_thread_reg_ctx_sp(),
-      m_gpregset_data(gpregset_data) {}
+      m_gpregset_data(gpregset_data), m_exception(exception) {}
 
 ThreadMinidump::~ThreadMinidump() = default;
 
@@ -115,4 +115,12 @@ ThreadMinidump::CreateRegisterContextForFrame(StackFrame *frame) {
   return reg_ctx_sp;
 }
 
-bool ThreadMinidump::CalculateStopInfo() { return false; }
+bool ThreadMinidump::CalculateStopInfo() {
+  if (!m_exception)
+    return false;
+
+  minidump::Exception thread_exception = m_exception.value();
+  SetStopInfo(StopInfo::CreateStopReasonWithSignal(
+      *this, thread_exception.ExceptionCode, /*description=*/nullptr, thread_exception.ExceptionCode));
+  return true;
+}
diff --git a/lldb/source/Plugins/Process/minidump/ThreadMinidump.h b/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
index aed7cfbc1b161..cc336ad2d7b3d 100644
--- a/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
+++ b/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
@@ -21,7 +21,7 @@ namespace minidump {
 class ThreadMinidump : public Thread {
 public:
   ThreadMinidump(Process &process, const minidump::Thread &td,
-                 llvm::ArrayRef<uint8_t> gpregset_data);
+                 llvm::ArrayRef<uint8_t> gpregset_data, std::optional<minidump::Exception> exception);
 
   ~ThreadMinidump() override;
 
@@ -35,6 +35,7 @@ class ThreadMinidump : public Thread {
 protected:
   lldb::RegisterContextSP m_thread_reg_ctx_sp;
   llvm::ArrayRef<uint8_t> m_gpregset_data;
+  std::optional<minidump::Exception> m_exception;
 
   bool CalculateStopInfo() override;
 };
diff --git a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp
index 632a7fd4e4f8f..e5355e07e41fc 100644
--- a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp
+++ b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp
@@ -251,10 +251,13 @@ 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);
+  llvm::Expected<std::vector<ExceptionStream>> exception_stream =
+      parser->GetExceptionStreams();
+  // LLVM::Expected has an explicit bool operator that determines if 
+  // the expected value is an error or not.
+  ASSERT_TRUE((bool)exception_stream);
+  ASSERT_EQ(1UL, exception_stream->size());
+  ASSERT_EQ(11UL, exception_stream.get()[0].ExceptionRecord.ExceptionCode);
 }
 
 void check_mem_range_exists(MinidumpParser &parser, const uint64_t range_start,
diff --git a/llvm/include/llvm/Object/Minidump.h b/llvm/include/llvm/Object/Minidump.h
index e45d4de0090de..87fdc555fb1ac 100644
--- a/llvm/include/llvm/Object/Minidump.h
+++ b/llvm/include/llvm/Object/Minidump.h
@@ -82,15 +82,22 @@ class MinidumpFile : public Binary {
     return getListStream<minidump::Thread>(minidump::StreamType::ThreadList);
   }
 
-  /// Returns the contents of the Exception stream.  An error is returned if the
-  /// file does not contain this stream, or the stream is smaller than the size
-  /// of the ExceptionStream structure.  The internal consistency of the stream
-  /// is not checked in any way.
-  Expected<const minidump::ExceptionStream &> getExceptionStream() const {
-    return getStream<minidump::ExceptionStream>(
-        minidump::StreamType::Exception);
+  /// Returns the contents of the Exception stream. An error is returned if the 
+  /// associated stream is smaller than the size of the ExceptionStream structure.  
+  /// Or the directory supplied is not of kind exception stream.
+  Expected<minidump::ExceptionStream> getExceptionStream(minidump::Directory Directory) const {
+    if (Directory.Type != minidump::StreamType::Exception) {
+      return createError("Not an exception stream");
+    }
+
+    return getStreamFromDirectory<minidump::ExceptionStream>(Directory);
   }
 
+  /// Returns the contents of the Exception streams.  An error is returned if
+  /// any of the streams are smaller than the size of the ExceptionStream structure.  
+  /// The internal consistency of the stream is not checked in any way.
+  Expected<std::vector<minidump::ExceptionStream>> getExceptionStreams() const;
+
   /// Returns the list of descriptors embedded in the MemoryList stream. The
   /// descriptors provide the content of interesting regions of memory at the
   /// time the minidump was taken. An error is returned if the file does not
@@ -172,6 +179,11 @@ class MinidumpFile : public Binary {
     return arrayRefFromStringRef(Data.getBuffer());
   }
 
+  /// Return the stream of the given type, cast to the appropriate type. Checks
+  /// that the stream is large enough to hold an object of this type.
+  template <typename T>
+  Expected<const T &> getStreamFromDirectory(minidump::Directory Directory) const;
+
   /// Return the stream of the given type, cast to the appropriate type. Checks
   /// that the stream is large enough to hold an object of this type.
   template <typename T>
@@ -187,6 +199,14 @@ class MinidumpFile : public Binary {
   DenseMap<minidump::StreamType, std::size_t> StreamMap;
 };
 
+template <typename T>
+Expected<const T &> MinidumpFile::getStreamFromDirectory(minidump::Directory Directory) const {
+  ArrayRef<uint8_t> Stream = getRawStream(Directory);
+  if (Stream.size() >= sizeof(T))
+    return *reinterpret_cast<const T *>(Stream.data());
+  return createEOFError();
+}
+
 template <typename T>
 Expected<const T &> MinidumpFile::getStream(minidump::StreamType Type) const {
   if (std::optional<ArrayRef<uint8_t>> Stream = getRawStream(Type)) {
diff --git a/llvm/lib/Object/Minidump.cpp b/llvm/lib/Object/Minidump.cpp
index 6febff89ac519..d366b4d3d579e 100644
--- a/llvm/lib/Object/Minidump.cpp
+++ b/llvm/lib/Object/Minidump.cpp
@@ -9,6 +9,7 @@
 #include "llvm/Object/Minidump.h"
 #include "llvm/Object/Error.h"
 #include "llvm/Support/ConvertUTF.h"
+#include <iostream>
 
 using namespace llvm;
 using namespace llvm::object;
@@ -53,6 +54,33 @@ Expected<std::string> MinidumpFile::getString(size_t Offset) const {
   return Result;
 }
 
+Expected<std::vector<minidump::ExceptionStream>> MinidumpFile::getExceptionStreams() const {
+  // Scan the directories for exceptions first
+  std::vector<Directory> exceptionStreams;
+  for (const auto &directory : Streams) {
+    if (directory.Type == StreamType::Exception)
+      exceptionStreams.push_back(directory);
+  }
+
+  if (exceptionStreams.empty())
+    return createError("No exception streams found");
+
+  std::vector<minidump::ExceptionStream> exceptionStreamList;
+  for (const auto &exceptionStream : exceptionStreams) {
+    llvm::Expected<minidump::ExceptionStream> ExpectedStream = getStreamFromDirectory<minidump::ExceptionStream>(exceptionStream);
+    if (!ExpectedStream)
+      return ExpectedStream.takeError();
+
+    std::cout << "Adding Exception Stream # " << exceptionStreamList.size() << std::endl;
+    std::cout << "Thread Id : " << ExpectedStream->ThreadId << std::endl;
+    std::cout << "Exception Code : " << ExpectedStream.get().ExceptionRecord.ExceptionCode << std::endl;
+    exceptionStreamList.push_back(ExpectedStream.get());
+    assert(exceptionStreamList.back().ThreadId == ExpectedStream->ThreadId);
+  }
+
+  return exceptionStreamList;
+}
+
 Expected<iterator_range<MinidumpFile::MemoryInfoIterator>>
 MinidumpFile::getMemoryInfoList() const {
   std::optional<ArrayRef<uint8_t>> Stream =
@@ -127,6 +155,7 @@ MinidumpFile::create(MemoryBufferRef Source) {
     return ExpectedStreams.takeError();
 
   DenseMap<StreamType, std::size_t> StreamMap;
+  std::vector<std::size_t> ExceptionStreams;
   for (const auto &StreamDescriptor : llvm::enumerate(*ExpectedStreams)) {
     StreamType Type = StreamDescriptor.value().Type;
     const LocationDescriptor &Loc = StreamDescriptor.value().Location;
@@ -142,6 +171,14 @@ MinidumpFile::create(MemoryBufferRef Source) {
       continue;
     }
 
+    // We treat exceptions differently here because the LLDB minidump
+    // makes some assumptions about uniqueness, all the streams other than exceptions
+    // are lists. But exceptions are not a list, they are single streams that point back to their thread
+    // So we will omit them here, and will find them when needed in the MinidumpFile.
+    if (Type == StreamType::Exception) {
+      continue;
+    }
+
     if (Type == DenseMapInfo<StreamType>::getEmptyKey() ||
         Type == DenseMapInfo<StreamType>::getTombstoneKey())
       return createError("Cannot handle one of the minidump streams");
diff --git a/llvm/lib/ObjectYAML/MinidumpYAML.cpp b/llvm/lib/ObjectYAML/MinidumpYAML.cpp
index fdbd2d8e6dcbc..e00feceea57af 100644
--- a/llvm/lib/ObjectYAML/MinidumpYAML.cpp
+++ b/llvm/lib/ObjectYAML/MinidumpYAML.cpp
@@ -464,8 +464,8 @@ Stream::create(const Directory &StreamDesc, const object::MinidumpFile &File) {
   StreamKind Kind = getKind(StreamDesc.Type);
   switch (Kind) {
   case StreamKind::Exception: {
-    Expected<const minidump::ExceptionStream &> ExpectedExceptionStream =
-        File.getExceptionStream();
+    Expected<minidump::ExceptionStream> ExpectedExceptionStream =
+        File.getExceptionStream(StreamDesc);
     if (!ExpectedExceptionStream)
       return ExpectedExceptionStream.takeError();
     Expected<ArrayRef<uint8_t>> ExpectedThreadContext =

>From a710aa1993a03dedf4e0af6a10d8b78091ef3f74 Mon Sep 17 00:00:00 2001
From: Jacob Lalonde <jalalonde at fb.com>
Date: Tue, 2 Jul 2024 12:44:59 -0700
Subject: [PATCH 2/4] Remove debug std::cout code. Run git-clang-format

---
 .../Process/minidump/MinidumpParser.cpp       |  5 ++-
 .../Process/minidump/ProcessMinidump.cpp      | 37 +++++++++----------
 .../Process/minidump/ThreadMinidump.cpp       |  8 +++-
 .../Plugins/Process/minidump/ThreadMinidump.h |  3 +-
 .../Process/minidump/MinidumpParserTest.cpp   |  2 +-
 llvm/include/llvm/Object/Minidump.h           | 20 ++++++----
 llvm/lib/Object/Minidump.cpp                  | 17 ++++-----
 7 files changed, 49 insertions(+), 43 deletions(-)

diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
index ac487a5ed0c0a..c51fed8d91b07 100644
--- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
+++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -408,7 +408,7 @@ std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
         continue;
       }
       // This module has been seen. Modules are sometimes mentioned multiple
-      // times when they are mapped discontiguously, so find the module with 
+      // times when they are mapped discontiguously, so find the module with
       // the lowest "base_of_image" and use that as the filtered module.
       if (module.BaseOfImage < dup_module->BaseOfImage)
         filtered_modules[iter->second] = &module;
@@ -417,7 +417,8 @@ std::vector<const minidump::Module *> MinidumpParser::GetFilteredModuleList() {
   return filtered_modules;
 }
 
-const std::vector<minidump::ExceptionStream> MinidumpParser::GetExceptionStreams() {
+const std::vector<minidump::ExceptionStream>
+MinidumpParser::GetExceptionStreams() {
   auto ExpectedStream = GetMinidumpFile().getExceptionStreams();
   if (ExpectedStream)
     return ExpectedStream.get();
diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
index 9f707c0d8a7a7..9a7e481b92796 100644
--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
+++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -39,7 +39,6 @@
 
 #include <memory>
 #include <optional>
-#include <iostream>
 
 using namespace lldb;
 using namespace lldb_private;
@@ -158,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_is_wow64(false) {}
+      m_core_data(std::move(core_data)), m_is_wow64(false) {}
 
 ProcessMinidump::~ProcessMinidump() {
   Clear();
@@ -210,18 +208,19 @@ Status ProcessMinidump::DoLoadCore() {
   GetTarget().SetArchitecture(arch, true /*set_platform*/);
 
   m_thread_list = m_minidump_parser->GetThreads();
-  std::vector<minidump::ExceptionStream> exception_streams = m_minidump_parser->GetExceptionStreams();
+  std::vector<minidump::ExceptionStream> exception_streams =
+      m_minidump_parser->GetExceptionStreams();
   for (const auto &exception_stream : exception_streams) {
-    if (m_exceptions_by_tid.count(exception_stream.ThreadId) > 0) {
-      // We only cast to avoid the warning around converting little endian in printf.
-      error.SetErrorStringWithFormat("duplicate exception stream for tid %" PRIu32, (uint32_t)exception_stream.ThreadId);
+    if (!m_exceptions_by_tid
+             .try_emplace(exception_stream.ThreadId, exception_stream)
+             .second) {
+      // We only cast to avoid the warning around converting little endian in
+      // printf.
+      error.SetErrorStringWithFormat(
+          "duplicate exception stream for tid %" PRIu32,
+          (uint32_t)exception_stream.ThreadId);
       return error;
-    } else 
-      m_exceptions_by_tid[exception_stream.ThreadId] = exception_stream;
-
-
-    std::cout << "Adding Exception Stream # " << (uint32_t)exception_stream.ThreadId << std::endl;
-    std::cout << "Added index " << (uint32_t)m_exceptions_by_tid[exception_stream.ThreadId].ExceptionRecord.ExceptionCode << std::endl;
+    }
   }
 
   SetUnixSignals(UnixSignals::Create(GetArchitecture()));
@@ -270,14 +269,12 @@ void ProcessMinidump::RefreshStateAfterStop() {
 
     if (arch.GetTriple().getOS() == llvm::Triple::Linux) {
       uint32_t signo = exception_stream.ExceptionRecord.ExceptionCode;
-      std::cout << "Thread Id : " << exception_stream.ThreadId << " has signal " << signo << std::endl;
       if (signo == 0) {
         // No stop.
         return;
       }
 
-      stop_info = StopInfo::CreateStopReasonWithSignal(
-          *stop_thread, signo);
+      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,
@@ -288,10 +285,10 @@ void ProcessMinidump::RefreshStateAfterStop() {
       llvm::raw_string_ostream desc_stream(desc);
       desc_stream << "Exception "
                   << llvm::format_hex(
-                        exception_stream.ExceptionRecord.ExceptionCode, 8)
+                         exception_stream.ExceptionRecord.ExceptionCode, 8)
                   << " encountered at address "
                   << llvm::format_hex(
-                        exception_stream.ExceptionRecord.ExceptionAddress, 8);
+                         exception_stream.ExceptionRecord.ExceptionAddress, 8);
       stop_info = StopInfo::CreateStopReasonWithException(
           *stop_thread, desc_stream.str().c_str());
     }
@@ -405,14 +402,14 @@ bool ProcessMinidump::DoUpdateThreadList(ThreadList &old_thread_list,
       exception = m_exceptions_by_tid[thread.ThreadId].ExceptionRecord;
     }
 
-
     llvm::ArrayRef<uint8_t> context;
     if (!m_is_wow64)
       context = m_minidump_parser->GetThreadContext(context_location);
     else
       context = m_minidump_parser->GetThreadContextWow64(thread);
 
-    lldb::ThreadSP thread_sp(new ThreadMinidump(*this, thread, context, exception));
+    lldb::ThreadSP thread_sp(
+        new ThreadMinidump(*this, thread, context, exception));
     new_thread_list.AddThread(thread_sp);
   }
   return new_thread_list.GetSize(false) > 0;
diff --git a/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp b/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
index 3cc154f1eac47..a1d7da376b02b 100644
--- a/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
+++ b/lldb/source/Plugins/Process/minidump/ThreadMinidump.cpp
@@ -34,7 +34,8 @@ using namespace lldb_private;
 using namespace minidump;
 
 ThreadMinidump::ThreadMinidump(Process &process, const minidump::Thread &td,
-                               llvm::ArrayRef<uint8_t> gpregset_data, std::optional<minidump::Exception> exception)
+                               llvm::ArrayRef<uint8_t> gpregset_data,
+                               std::optional<minidump::Exception> exception)
     : Thread(process, td.ThreadId), m_thread_reg_ctx_sp(),
       m_gpregset_data(gpregset_data), m_exception(exception) {}
 
@@ -115,12 +116,15 @@ ThreadMinidump::CreateRegisterContextForFrame(StackFrame *frame) {
   return reg_ctx_sp;
 }
 
+// This method doesn't end up getting called for minidump
+// because the stopinfo is set in `ProcessMinidump::RefreshStateAfterStop`
 bool ThreadMinidump::CalculateStopInfo() {
   if (!m_exception)
     return false;
 
   minidump::Exception thread_exception = m_exception.value();
   SetStopInfo(StopInfo::CreateStopReasonWithSignal(
-      *this, thread_exception.ExceptionCode, /*description=*/nullptr, thread_exception.ExceptionCode));
+      *this, thread_exception.ExceptionCode, /*description=*/nullptr,
+      thread_exception.ExceptionCode));
   return true;
 }
diff --git a/lldb/source/Plugins/Process/minidump/ThreadMinidump.h b/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
index cc336ad2d7b3d..abde13ccb3f2a 100644
--- a/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
+++ b/lldb/source/Plugins/Process/minidump/ThreadMinidump.h
@@ -21,7 +21,8 @@ namespace minidump {
 class ThreadMinidump : public Thread {
 public:
   ThreadMinidump(Process &process, const minidump::Thread &td,
-                 llvm::ArrayRef<uint8_t> gpregset_data, std::optional<minidump::Exception> exception);
+                 llvm::ArrayRef<uint8_t> gpregset_data,
+                 std::optional<minidump::Exception> exception);
 
   ~ThreadMinidump() override;
 
diff --git a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp
index e5355e07e41fc..aa74e690d45a5 100644
--- a/lldb/unittests/Process/minidump/MinidumpParserTest.cpp
+++ b/lldb/unittests/Process/minidump/MinidumpParserTest.cpp
@@ -253,7 +253,7 @@ TEST_F(MinidumpParserTest, GetExceptionStream) {
   SetUpData("linux-x86_64.dmp");
   llvm::Expected<std::vector<ExceptionStream>> exception_stream =
       parser->GetExceptionStreams();
-  // LLVM::Expected has an explicit bool operator that determines if 
+  // LLVM::Expected has an explicit bool operator that determines if
   // the expected value is an error or not.
   ASSERT_TRUE((bool)exception_stream);
   ASSERT_EQ(1UL, exception_stream->size());
diff --git a/llvm/include/llvm/Object/Minidump.h b/llvm/include/llvm/Object/Minidump.h
index 87fdc555fb1ac..14fad92d1d2b6 100644
--- a/llvm/include/llvm/Object/Minidump.h
+++ b/llvm/include/llvm/Object/Minidump.h
@@ -82,10 +82,11 @@ class MinidumpFile : public Binary {
     return getListStream<minidump::Thread>(minidump::StreamType::ThreadList);
   }
 
-  /// Returns the contents of the Exception stream. An error is returned if the 
-  /// associated stream is smaller than the size of the ExceptionStream structure.  
-  /// Or the directory supplied is not of kind exception stream.
-  Expected<minidump::ExceptionStream> getExceptionStream(minidump::Directory Directory) const {
+  /// Returns the contents of the Exception stream. An error is returned if the
+  /// associated stream is smaller than the size of the ExceptionStream
+  /// structure. Or the directory supplied is not of kind exception stream.
+  Expected<minidump::ExceptionStream>
+  getExceptionStream(minidump::Directory Directory) const {
     if (Directory.Type != minidump::StreamType::Exception) {
       return createError("Not an exception stream");
     }
@@ -94,8 +95,9 @@ class MinidumpFile : public Binary {
   }
 
   /// Returns the contents of the Exception streams.  An error is returned if
-  /// any of the streams are smaller than the size of the ExceptionStream structure.  
-  /// The internal consistency of the stream is not checked in any way.
+  /// any of the streams are smaller than the size of the ExceptionStream
+  /// structure. The internal consistency of the stream is not checked in any
+  /// way.
   Expected<std::vector<minidump::ExceptionStream>> getExceptionStreams() const;
 
   /// Returns the list of descriptors embedded in the MemoryList stream. The
@@ -182,7 +184,8 @@ class MinidumpFile : public Binary {
   /// Return the stream of the given type, cast to the appropriate type. Checks
   /// that the stream is large enough to hold an object of this type.
   template <typename T>
-  Expected<const T &> getStreamFromDirectory(minidump::Directory Directory) const;
+  Expected<const T &>
+  getStreamFromDirectory(minidump::Directory Directory) const;
 
   /// Return the stream of the given type, cast to the appropriate type. Checks
   /// that the stream is large enough to hold an object of this type.
@@ -200,7 +203,8 @@ class MinidumpFile : public Binary {
 };
 
 template <typename T>
-Expected<const T &> MinidumpFile::getStreamFromDirectory(minidump::Directory Directory) const {
+Expected<const T &>
+MinidumpFile::getStreamFromDirectory(minidump::Directory Directory) const {
   ArrayRef<uint8_t> Stream = getRawStream(Directory);
   if (Stream.size() >= sizeof(T))
     return *reinterpret_cast<const T *>(Stream.data());
diff --git a/llvm/lib/Object/Minidump.cpp b/llvm/lib/Object/Minidump.cpp
index d366b4d3d579e..43c2a4a949214 100644
--- a/llvm/lib/Object/Minidump.cpp
+++ b/llvm/lib/Object/Minidump.cpp
@@ -54,7 +54,8 @@ Expected<std::string> MinidumpFile::getString(size_t Offset) const {
   return Result;
 }
 
-Expected<std::vector<minidump::ExceptionStream>> MinidumpFile::getExceptionStreams() const {
+Expected<std::vector<minidump::ExceptionStream>>
+MinidumpFile::getExceptionStreams() const {
   // Scan the directories for exceptions first
   std::vector<Directory> exceptionStreams;
   for (const auto &directory : Streams) {
@@ -67,15 +68,12 @@ Expected<std::vector<minidump::ExceptionStream>> MinidumpFile::getExceptionStrea
 
   std::vector<minidump::ExceptionStream> exceptionStreamList;
   for (const auto &exceptionStream : exceptionStreams) {
-    llvm::Expected<minidump::ExceptionStream> ExpectedStream = getStreamFromDirectory<minidump::ExceptionStream>(exceptionStream);
+    llvm::Expected<minidump::ExceptionStream> ExpectedStream =
+        getStreamFromDirectory<minidump::ExceptionStream>(exceptionStream);
     if (!ExpectedStream)
       return ExpectedStream.takeError();
 
-    std::cout << "Adding Exception Stream # " << exceptionStreamList.size() << std::endl;
-    std::cout << "Thread Id : " << ExpectedStream->ThreadId << std::endl;
-    std::cout << "Exception Code : " << ExpectedStream.get().ExceptionRecord.ExceptionCode << std::endl;
     exceptionStreamList.push_back(ExpectedStream.get());
-    assert(exceptionStreamList.back().ThreadId == ExpectedStream->ThreadId);
   }
 
   return exceptionStreamList;
@@ -172,9 +170,10 @@ MinidumpFile::create(MemoryBufferRef Source) {
     }
 
     // We treat exceptions differently here because the LLDB minidump
-    // makes some assumptions about uniqueness, all the streams other than exceptions
-    // are lists. But exceptions are not a list, they are single streams that point back to their thread
-    // So we will omit them here, and will find them when needed in the MinidumpFile.
+    // makes some assumptions about uniqueness, all the streams other than
+    // exceptions are lists. But exceptions are not a list, they are single
+    // streams that point back to their thread So we will omit them here, and
+    // will find them when needed in the MinidumpFile.
     if (Type == StreamType::Exception) {
       continue;
     }

>From 47a94dafabcdd1a99fcefee016c843d5ea30f951 Mon Sep 17 00:00:00 2001
From: Jacob Lalonde <jalalonde at fb.com>
Date: Tue, 2 Jul 2024 14:54:03 -0700
Subject: [PATCH 3/4] Add test case and obj yaml

---
 .../minidump-new/TestMiniDumpNew.py           | 14 +++++++
 .../minidump-new/multiple-sigsev.yaml         | 39 +++++++++++++++++++
 2 files changed, 53 insertions(+)
 create mode 100644 lldb/test/API/functionalities/postmortem/minidump-new/multiple-sigsev.yaml

diff --git a/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py b/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
index 91fd2439492b5..038ca819e6a0b 100644
--- a/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
+++ b/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
@@ -491,3 +491,17 @@ def test_minidump_sysroot(self):
         spec_dir_norm = os.path.normcase(module.GetFileSpec().GetDirectory())
         exe_dir_norm = os.path.normcase(exe_dir)
         self.assertEqual(spec_dir_norm, exe_dir_norm)
+
+    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
+...

>From f91ff4b28e24565f4909a914c0d65052e7c95f9a Mon Sep 17 00:00:00 2001
From: Jacob Lalonde <jalalonde at fb.com>
Date: Tue, 2 Jul 2024 15:00:00 -0700
Subject: [PATCH 4/4] Rebase and run python formatter

---
 .../postmortem/minidump-new/TestMiniDumpNew.py                | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py b/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
index 038ca819e6a0b..ba93eff6f1f82 100644
--- a/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
+++ b/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
@@ -494,9 +494,9 @@ def test_minidump_sysroot(self):
 
     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")
+        print("Starting to read multiple-sigsev.yaml")
         self.process_from_yaml("multiple-sigsev.yaml")
-        print ("Done reading 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)



More information about the lldb-commits mailing list