[Lldb-commits] [lldb] 2fc38b2 - [lldb] Report debugger diagnostics as events

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Wed Mar 16 08:33:07 PDT 2022


Author: Jonas Devlieghere
Date: 2022-03-16T08:33:01-07:00
New Revision: 2fc38b2b7bf9896806749655124ea5f13cc6d383

URL: https://github.com/llvm/llvm-project/commit/2fc38b2b7bf9896806749655124ea5f13cc6d383
DIFF: https://github.com/llvm/llvm-project/commit/2fc38b2b7bf9896806749655124ea5f13cc6d383.diff

LOG: [lldb] Report debugger diagnostics as events

Report warnings and errors through events instead of printing directly
the to the debugger's error stream. By using events, IDEs such as Xcode
can report these issues in the UI instead of having them show up in the
debugger console.

The new diagnostic events are handled by the default event loop. If a
diagnostic is reported while nobody is listening for the new event
types, it is printed directly to the debugger's error stream.

Differential revision: https://reviews.llvm.org/D121511

Added: 
    lldb/unittests/Core/DiagnosticEventTest.cpp

Modified: 
    lldb/include/lldb/Core/Debugger.h
    lldb/include/lldb/Core/DebuggerEvents.h
    lldb/source/Core/Debugger.cpp
    lldb/source/Core/DebuggerEvents.cpp
    lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
    lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp
    lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
    lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
    lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
    lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
    lldb/source/Target/StopInfo.cpp
    lldb/unittests/Core/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 8fd22413d8181..6b6ad22d23511 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -14,6 +14,7 @@
 #include <memory>
 #include <vector>
 
+#include "lldb/Core/DebuggerEvents.h"
 #include "lldb/Core/FormatEntity.h"
 #include "lldb/Core/IOHandler.h"
 #include "lldb/Core/SourceManager.h"
@@ -57,7 +58,6 @@ class Process;
 class Stream;
 class SymbolContext;
 class Target;
-class ProgressEventData;
 
 namespace repro {
 class DataRecorder;
@@ -77,6 +77,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
   /// Broadcaster event bits definitions.
   enum {
     eBroadcastBitProgress = (1 << 0),
+    eBroadcastBitWarning = (1 << 1),
+    eBroadcastBitError = (1 << 2),
   };
 
   static ConstString GetStaticBroadcasterClass();
@@ -375,6 +377,50 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
     return m_broadcaster_manager_sp;
   }
 
+  /// Report warning events.
+  ///
+  /// Progress events will be delivered to any debuggers that have listeners
+  /// for the eBroadcastBitError.
+  ///
+  /// \param[in] message
+  ///   The warning message to be reported.
+  ///
+  /// \param [in] debugger_id
+  ///   If this optional parameter has a value, it indicates the unique
+  ///   debugger identifier that this progress should be delivered to. If this
+  ///   optional parameter does not have a value, the progress will be
+  ///   delivered to all debuggers.
+  ///
+  /// \param [in] once
+  ///   If a pointer is passed to a std::once_flag, then it will be used to
+  ///   ensure the given warning is only broadcast once.
+  static void
+  ReportWarning(std::string messsage,
+                llvm::Optional<lldb::user_id_t> debugger_id = llvm::None,
+                std::once_flag *once = nullptr);
+
+  /// Report error events.
+  ///
+  /// Progress events will be delivered to any debuggers that have listeners
+  /// for the eBroadcastBitError.
+  ///
+  /// \param[in] message
+  ///   The error message to be reported.
+  ///
+  /// \param [in] debugger_id
+  ///   If this optional parameter has a value, it indicates the unique
+  ///   debugger identifier that this progress should be delivered to. If this
+  ///   optional parameter does not have a value, the progress will be
+  ///   delivered to all debuggers.
+  ///
+  /// \param [in] once
+  ///   If a pointer is passed to a std::once_flag, then it will be used to
+  ///   ensure the given error is only broadcast once.
+  static void
+  ReportError(std::string messsage,
+              llvm::Optional<lldb::user_id_t> debugger_id = llvm::None,
+              std::once_flag *once = nullptr);
+
 protected:
   friend class CommandInterpreter;
   friend class REPL;
@@ -413,6 +459,11 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
                              uint64_t completed, uint64_t total,
                              llvm::Optional<lldb::user_id_t> debugger_id);
 
+  static void ReportDiagnosticImpl(DiagnosticEventData::Type type,
+                                   std::string message,
+                                   llvm::Optional<lldb::user_id_t> debugger_id,
+                                   std::once_flag *once);
+
   void PrintProgress(const ProgressEventData &data);
 
   bool StartEventHandlerThread();
@@ -444,6 +495,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   void HandleProgressEvent(const lldb::EventSP &event_sp);
 
+  void HandleDiagnosticEvent(const lldb::EventSP &event_sp);
+
   // Ensures two threads don't attempt to flush process output in parallel.
   std::mutex m_output_flush_mutex;
   void FlushProcessOutput(Process &process, bool flush_stdout,

diff  --git a/lldb/include/lldb/Core/DebuggerEvents.h b/lldb/include/lldb/Core/DebuggerEvents.h
index 749bcc86f3720..e4394da2ffdac 100644
--- a/lldb/include/lldb/Core/DebuggerEvents.h
+++ b/lldb/include/lldb/Core/DebuggerEvents.h
@@ -46,6 +46,40 @@ class ProgressEventData : public EventData {
   ProgressEventData(const ProgressEventData &) = delete;
   const ProgressEventData &operator=(const ProgressEventData &) = delete;
 };
+
+class DiagnosticEventData : public EventData {
+public:
+  enum class Type {
+    Warning,
+    Error,
+  };
+  DiagnosticEventData(Type type, std::string message, bool debugger_specific)
+      : m_message(std::move(message)), m_type(type),
+        m_debugger_specific(debugger_specific) {}
+  ~DiagnosticEventData() {}
+
+  const std::string &GetMessage() const { return m_message; }
+  Type GetType() const { return m_type; }
+
+  llvm::StringRef GetPrefix() const;
+
+  void Dump(Stream *s) const override;
+
+  static ConstString GetFlavorString();
+  ConstString GetFlavor() const override;
+
+  static const DiagnosticEventData *
+  GetEventDataFromEvent(const Event *event_ptr);
+
+protected:
+  std::string m_message;
+  Type m_type;
+  const bool m_debugger_specific;
+
+  DiagnosticEventData(const DiagnosticEventData &) = delete;
+  const DiagnosticEventData &operator=(const DiagnosticEventData &) = delete;
+};
+
 } // namespace lldb_private
 
 #endif // LLDB_CORE_DEBUGGER_EVENTS_H

diff  --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 44e74e9f292ec..cac5ae4dacd75 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1326,6 +1326,79 @@ void Debugger::ReportProgress(uint64_t progress_id, const std::string &message,
   }
 }
 
+static void PrivateReportDiagnostic(Debugger &debugger,
+                                    DiagnosticEventData::Type type,
+                                    std::string message,
+                                    bool debugger_specific) {
+  uint32_t event_type = 0;
+  switch (type) {
+  case DiagnosticEventData::Type::Warning:
+    event_type = Debugger::eBroadcastBitWarning;
+    break;
+  case DiagnosticEventData::Type::Error:
+    event_type = Debugger::eBroadcastBitError;
+    break;
+  }
+
+  Broadcaster &broadcaster = debugger.GetBroadcaster();
+  if (!broadcaster.EventTypeHasListeners(event_type)) {
+    // Diagnostics are too important to drop. If nobody is listening, print the
+    // diagnostic directly to the debugger's error stream.
+    DiagnosticEventData event_data(type, std::move(message), debugger_specific);
+    StreamSP stream = debugger.GetAsyncErrorStream();
+    event_data.Dump(stream.get());
+    return;
+  }
+  EventSP event_sp = std::make_shared<Event>(
+      event_type,
+      new DiagnosticEventData(type, std::move(message), debugger_specific));
+  broadcaster.BroadcastEvent(event_sp);
+}
+
+void Debugger::ReportDiagnosticImpl(DiagnosticEventData::Type type,
+                                    std::string message,
+                                    llvm::Optional<lldb::user_id_t> debugger_id,
+                                    std::once_flag *once) {
+  auto ReportDiagnosticLambda = [&]() {
+    // Check if this progress is for a specific debugger.
+    if (debugger_id) {
+      // It is debugger specific, grab it and deliver the event if the debugger
+      // still exists.
+      DebuggerSP debugger_sp = FindDebuggerWithID(*debugger_id);
+      if (debugger_sp)
+        PrivateReportDiagnostic(*debugger_sp, type, std::move(message), true);
+      return;
+    }
+    // The progress event is not debugger specific, iterate over all debuggers
+    // and deliver a progress event to each one.
+    if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
+      std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
+      for (const auto &debugger : *g_debugger_list_ptr)
+        PrivateReportDiagnostic(*debugger, type, message, false);
+    }
+  };
+
+  if (once)
+    std::call_once(*once, ReportDiagnosticLambda);
+  else
+    ReportDiagnosticLambda();
+}
+
+void Debugger::ReportWarning(std::string message,
+                             llvm::Optional<lldb::user_id_t> debugger_id,
+                             std::once_flag *once) {
+  ReportDiagnosticImpl(DiagnosticEventData::Type::Warning, std::move(message),
+                       debugger_id, once);
+}
+
+void Debugger::ReportError(std::string message,
+                           llvm::Optional<lldb::user_id_t> debugger_id,
+                           std::once_flag *once) {
+
+  ReportDiagnosticImpl(DiagnosticEventData::Type::Error, std::move(message),
+                       debugger_id, once);
+}
+
 bool Debugger::EnableLog(llvm::StringRef channel,
                          llvm::ArrayRef<const char *> categories,
                          llvm::StringRef log_file, uint32_t log_options,
@@ -1605,8 +1678,9 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
           CommandInterpreter::eBroadcastBitAsynchronousOutputData |
           CommandInterpreter::eBroadcastBitAsynchronousErrorData);
 
-  listener_sp->StartListeningForEvents(&m_broadcaster,
-                                       Debugger::eBroadcastBitProgress);
+  listener_sp->StartListeningForEvents(
+      &m_broadcaster,
+      eBroadcastBitProgress | eBroadcastBitWarning | eBroadcastBitError);
 
   // Let the thread that spawned us know that we have started up and that we
   // are now listening to all required events so no events get missed
@@ -1660,6 +1734,10 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
           } else if (broadcaster == &m_broadcaster) {
             if (event_type & Debugger::eBroadcastBitProgress)
               HandleProgressEvent(event_sp);
+            else if (event_type & Debugger::eBroadcastBitWarning)
+              HandleDiagnosticEvent(event_sp);
+            else if (event_type & Debugger::eBroadcastBitError)
+              HandleDiagnosticEvent(event_sp);
           }
         }
 
@@ -1793,6 +1871,15 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) {
   output->Flush();
 }
 
+void Debugger::HandleDiagnosticEvent(const lldb::EventSP &event_sp) {
+  auto *data = DiagnosticEventData::GetEventDataFromEvent(event_sp.get());
+  if (!data)
+    return;
+
+  StreamSP stream = GetAsyncErrorStream();
+  data->Dump(stream.get());
+}
+
 bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); }
 
 bool Debugger::StartIOHandlerThread() {

diff  --git a/lldb/source/Core/DebuggerEvents.cpp b/lldb/source/Core/DebuggerEvents.cpp
index e3af32e0eb175..a433ec31738f4 100644
--- a/lldb/source/Core/DebuggerEvents.cpp
+++ b/lldb/source/Core/DebuggerEvents.cpp
@@ -10,6 +10,15 @@
 
 using namespace lldb_private;
 
+template <typename T>
+static const T *GetEventDataFromEventImpl(const Event *event_ptr) {
+  if (event_ptr)
+    if (const EventData *event_data = event_ptr->GetData())
+      if (event_data->GetFlavor() == T::GetFlavorString())
+        return static_cast<const T *>(event_ptr->GetData());
+  return nullptr;
+}
+
 ConstString ProgressEventData::GetFlavorString() {
   static ConstString g_flavor("ProgressEventData");
   return g_flavor;
@@ -33,9 +42,33 @@ void ProgressEventData::Dump(Stream *s) const {
 
 const ProgressEventData *
 ProgressEventData::GetEventDataFromEvent(const Event *event_ptr) {
-  if (event_ptr)
-    if (const EventData *event_data = event_ptr->GetData())
-      if (event_data->GetFlavor() == ProgressEventData::GetFlavorString())
-        return static_cast<const ProgressEventData *>(event_ptr->GetData());
-  return nullptr;
+  return GetEventDataFromEventImpl<ProgressEventData>(event_ptr);
+}
+
+llvm::StringRef DiagnosticEventData::GetPrefix() const {
+  switch (m_type) {
+  case Type::Warning:
+    return "warning";
+  case Type::Error:
+    return "error";
+  }
+}
+
+void DiagnosticEventData::Dump(Stream *s) const {
+  *s << GetPrefix() << ": " << GetMessage() << '\n';
+  s->Flush();
+}
+
+ConstString DiagnosticEventData::GetFlavorString() {
+  static ConstString g_flavor("DiagnosticEventData");
+  return g_flavor;
+}
+
+ConstString DiagnosticEventData::GetFlavor() const {
+  return DiagnosticEventData::GetFlavorString();
+}
+
+const DiagnosticEventData *
+DiagnosticEventData::GetEventDataFromEvent(const Event *event_ptr) {
+  return GetEventDataFromEventImpl<DiagnosticEventData>(event_ptr);
 }

diff  --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
index 2ca40d71ec156..e6bcc4c00feab 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
@@ -299,10 +299,12 @@ bool DynamicLoaderMacOS::NotifyBreakpointHit(void *baton,
       }
     }
   } else {
-    process->GetTarget().GetDebugger().GetAsyncErrorStream()->Printf(
-        "No ABI plugin located for triple %s -- shared libraries will not be "
-        "registered!\n",
-        process->GetTarget().GetArchitecture().GetTriple().getTriple().c_str());
+    Target &target = process->GetTarget();
+    Debugger::ReportWarning(
+        "no ABI plugin located for triple " +
+            target.GetArchitecture().GetTriple().getTriple() +
+            ": shared libraries will not be registered",
+        target.GetDebugger().GetID());
   }
 
   // Return true to stop the target, false to just let the target run

diff  --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp
index 15083e8a95b19..438cec75cabbf 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp
@@ -400,10 +400,12 @@ bool DynamicLoaderMacOSXDYLD::NotifyBreakpointHit(
       }
     }
   } else {
-    process->GetTarget().GetDebugger().GetAsyncErrorStream()->Printf(
-        "No ABI plugin located for triple %s -- shared libraries will not be "
-        "registered!\n",
-        process->GetTarget().GetArchitecture().GetTriple().getTriple().c_str());
+    Target &target = process->GetTarget();
+    Debugger::ReportWarning(
+        "no ABI plugin located for triple " +
+            target.GetArchitecture().GetTriple().getTriple() +
+            ": shared libraries will not be registered",
+        target.GetDebugger().GetID());
   }
 
   // Return true to stop the target, false to just let the target run

diff  --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
index a3c3e0b4900e9..43dcb640d80a7 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -21,6 +21,7 @@
 
 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
 #include "lldb/Core/Debugger.h"
+#include "lldb/Core/DebuggerEvents.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Section.h"
@@ -665,8 +666,8 @@ AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process,
       m_loaded_objc_opt(false), m_non_pointer_isa_cache_up(),
       m_tagged_pointer_vendor_up(
           TaggedPointerVendorV2::CreateInstance(*this, objc_module_sp)),
-      m_encoding_to_type_sp(), m_noclasses_warning_emitted(false),
-      m_CFBoolean_values(), m_realized_class_generation_count(0) {
+      m_encoding_to_type_sp(), m_CFBoolean_values(),
+      m_realized_class_generation_count(0) {
   static const ConstString g_gdb_object_getClass("gdb_object_getClass");
   m_has_object_getClass = HasSymbol(g_gdb_object_getClass);
   static const ConstString g_objc_copyRealizedClassList(
@@ -2330,32 +2331,27 @@ static bool DoesProcessHaveSharedCache(Process &process) {
 
 void AppleObjCRuntimeV2::WarnIfNoClassesCached(
     SharedCacheWarningReason reason) {
-  if (m_noclasses_warning_emitted)
-    return;
-
   if (GetProcess() && !DoesProcessHaveSharedCache(*GetProcess())) {
     // Simulators do not have the objc_opt_ro class table so don't actually
     // complain to the user
-    m_noclasses_warning_emitted = true;
     return;
   }
 
   Debugger &debugger(GetProcess()->GetTarget().GetDebugger());
-  if (auto stream = debugger.GetAsyncOutputStream()) {
-    switch (reason) {
-    case SharedCacheWarningReason::eNotEnoughClassesRead:
-      stream->PutCString("warning: could not find Objective-C class data in "
-                         "the process. This may reduce the quality of type "
-                         "information available.\n");
-      m_noclasses_warning_emitted = true;
-      break;
-    case SharedCacheWarningReason::eExpressionExecutionFailure:
-      stream->PutCString("warning: could not execute support code to read "
-                         "Objective-C class data in the process. This may "
-                         "reduce the quality of type information available.\n");
-      m_noclasses_warning_emitted = true;
-      break;
-    }
+  switch (reason) {
+  case SharedCacheWarningReason::eNotEnoughClassesRead:
+    Debugger::ReportWarning("warning: could not find Objective-C class data in "
+                            "the process. This may reduce the quality of type "
+                            "information available.\n",
+                            debugger.GetID(), &m_no_classes_cached_warning);
+    break;
+  case SharedCacheWarningReason::eExpressionExecutionFailure:
+    Debugger::ReportWarning(
+        "warning: could not execute support code to read "
+        "Objective-C class data in the process. This may "
+        "reduce the quality of type information available.\n",
+        debugger.GetID(), &m_no_classes_cached_warning);
+    break;
   }
 }
 
@@ -2372,17 +2368,25 @@ void AppleObjCRuntimeV2::WarnIfNoExpandedSharedCache() {
 
   Target &target = GetProcess()->GetTarget();
   Debugger &debugger = target.GetDebugger();
-  if (auto stream = debugger.GetAsyncOutputStream()) {
-    const char *msg = "read from the shared cache";
-    if (PlatformSP platform_sp = target.GetPlatform())
-      msg = platform_sp->IsHost()
-                ? "read from the host's in-memory shared cache"
-                : "find the on-disk shared cache for this device";
-    stream->Printf("warning: libobjc.A.dylib is being read from process "
-                   "memory. This indicates that LLDB could not %s. This will "
-                   "likely reduce debugging performance.\n",
-                   msg);
+
+  std::string buffer;
+  llvm::raw_string_ostream os(buffer);
+
+  os << "warning: libobjc.A.dylib is being read from process memory. This "
+        "indicates that LLDB could not ";
+  if (PlatformSP platform_sp = target.GetPlatform()) {
+    if (platform_sp->IsHost()) {
+      os << "read from the host's in-memory shared cache";
+    } else {
+      os << "find the on-disk shared cache for this device";
+    }
+  } else {
+    os << "read from the shared cache";
   }
+  os << ". This will likely reduce debugging performance.\n";
+
+  Debugger::ReportWarning(os.str(), debugger.GetID(),
+                          &m_no_expanded_cache_warning);
 }
 
 DeclVendor *AppleObjCRuntimeV2::GetDeclVendor() {

diff  --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
index e1a6b7cde48a7..2ed2d8784698f 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
@@ -433,7 +433,8 @@ class AppleObjCRuntimeV2 : public AppleObjCRuntime {
   std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_up;
   std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_up;
   EncodingToTypeSP m_encoding_to_type_sp;
-  bool m_noclasses_warning_emitted;
+  std::once_flag m_no_classes_cached_warning;
+  std::once_flag m_no_expanded_cache_warning;
   llvm::Optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
   uint64_t m_realized_class_generation_count;
 };

diff  --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
index 1b4406b88bb8d..60b8466b74a5c 100644
--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
+++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -292,9 +292,9 @@ Status ProcessMinidump::DoLoadCore() {
 
   llvm::Optional<lldb::pid_t> pid = m_minidump_parser->GetPid();
   if (!pid) {
-    GetTarget().GetDebugger().GetAsyncErrorStream()->PutCString(
-        "Unable to retrieve process ID from minidump file, setting process ID "
-        "to 1.\n");
+    Debugger::ReportWarning("unable to retrieve process ID from minidump file, "
+                            "setting process ID to 1",
+                            GetTarget().GetDebugger().GetID());
     pid = 1;
   }
   SetID(pid.getValue());

diff  --git a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
index 9816b07885dca..85366d3f9f4b0 100644
--- a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
+++ b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
@@ -1818,13 +1818,8 @@ void StructuredDataDarwinLog::EnableNow() {
                   "enable command failed (process uid %u)",
                   __FUNCTION__, process_sp->GetUniqueID());
     }
-    // Report failures to the debugger error stream.
-    auto error_stream_sp = debugger_sp->GetAsyncErrorStream();
-    if (error_stream_sp) {
-      error_stream_sp->Printf("failed to configure DarwinLog "
-                              "support\n");
-      error_stream_sp->Flush();
-    }
+    Debugger::ReportError("failed to configure DarwinLog support",
+                          debugger_sp->GetID());
     return;
   }
 
@@ -1852,13 +1847,8 @@ void StructuredDataDarwinLog::EnableNow() {
               "ConfigureStructuredData() call failed "
               "(process uid %u): %s",
               __FUNCTION__, process_sp->GetUniqueID(), error.AsCString());
-    auto error_stream_sp = debugger_sp->GetAsyncErrorStream();
-    if (error_stream_sp) {
-      error_stream_sp->Printf("failed to configure DarwinLog "
-                              "support: %s\n",
-                              error.AsCString());
-      error_stream_sp->Flush();
-    }
+    Debugger::ReportError("failed to configure DarwinLog support",
+                          debugger_sp->GetID());
     m_is_enabled = false;
   } else {
     m_is_enabled = true;

diff  --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 180c129a49a81..f6cf3e55ce233 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -376,9 +376,10 @@ class StopInfoBreakpoint : public StopInfo {
                       "StopInfoBreakpoint::PerformAction - in expression, "
                       "continuing: %s.",
                       m_should_stop ? "true" : "false");
-            process->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf(
-                "Warning: hit breakpoint while running function, skipping "
-                "commands and conditions to prevent recursion.\n");
+            Debugger::ReportWarning(
+                "hit breakpoint while running function, skipping commands and "
+                "conditions to prevent recursion",
+                process->GetTarget().GetDebugger().GetID());
             return;
           }
 
@@ -450,21 +451,21 @@ class StopInfoBreakpoint : public StopInfo {
                   bp_loc_sp->ConditionSaysStop(exe_ctx, condition_error);
 
               if (!condition_error.Success()) {
-                Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
-                StreamSP error_sp = debugger.GetAsyncErrorStream();
-                error_sp->Printf("Stopped due to an error evaluating condition "
-                                 "of breakpoint ");
-                bp_loc_sp->GetDescription(error_sp.get(),
-                                          eDescriptionLevelBrief);
-                error_sp->Printf(": \"%s\"", bp_loc_sp->GetConditionText());
-                error_sp->EOL();
                 const char *err_str =
                     condition_error.AsCString("<Unknown Error>");
                 LLDB_LOGF(log, "Error evaluating condition: \"%s\"\n", err_str);
 
-                error_sp->PutCString(err_str);
-                error_sp->EOL();
-                error_sp->Flush();
+                std::string error_message;
+                llvm::raw_string_ostream os(error_message);
+                os << "stopped due to an error evaluating condition of "
+                      "breakpoint "
+                   << bp_loc_sp->GetConditionText() << '\n';
+                os << err_str;
+                os.flush();
+
+                Debugger::ReportError(
+                    std::move(error_message),
+                    exe_ctx.GetTargetRef().GetDebugger().GetID());
               } else {
                 LLDB_LOGF(log,
                           "Condition evaluated for breakpoint %s on thread "

diff  --git a/lldb/unittests/Core/CMakeLists.txt b/lldb/unittests/Core/CMakeLists.txt
index dfe017b3c9c6e..bd4acef40f77a 100644
--- a/lldb/unittests/Core/CMakeLists.txt
+++ b/lldb/unittests/Core/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_lldb_unittest(LLDBCoreTests
   CommunicationTest.cpp
+  DiagnosticEventTest.cpp
   DumpDataExtractorTest.cpp
   FormatEntityTest.cpp
   MangledTest.cpp
@@ -13,11 +14,12 @@ add_lldb_unittest(LLDBCoreTests
   LINK_LIBS
     lldbCore
     lldbHost
-    lldbSymbol
     lldbPluginObjectFileELF
     lldbPluginObjectFileMachO
     lldbPluginObjectFilePECOFF
+    lldbPluginPlatformMacOSX
     lldbPluginSymbolFileSymtab
+    lldbSymbol
     lldbUtilityHelpers
     LLVMTestingSupport
   LINK_COMPONENTS

diff  --git a/lldb/unittests/Core/DiagnosticEventTest.cpp b/lldb/unittests/Core/DiagnosticEventTest.cpp
new file mode 100644
index 0000000000000..55d16ce25e61b
--- /dev/null
+++ b/lldb/unittests/Core/DiagnosticEventTest.cpp
@@ -0,0 +1,176 @@
+//===-- DiagnosticEventTest.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+#include "Plugins/Platform/MacOSX/PlatformMacOSX.h"
+#include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/DebuggerEvents.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Utility/Broadcaster.h"
+#include "lldb/Utility/Event.h"
+#include "lldb/Utility/Listener.h"
+#include "lldb/Utility/Reproducer.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::repro;
+
+static const constexpr std::chrono::seconds TIMEOUT(0);
+static const constexpr size_t DEBUGGERS = 3;
+
+static std::once_flag debugger_initialize_flag;
+
+namespace {
+class DiagnosticEventTest : public ::testing::Test {
+public:
+  void SetUp() override {
+    llvm::cantFail(Reproducer::Initialize(ReproducerMode::Off, llvm::None));
+    FileSystem::Initialize();
+    HostInfo::Initialize();
+    PlatformMacOSX::Initialize();
+    std::call_once(debugger_initialize_flag,
+                   []() { Debugger::Initialize(nullptr); });
+    ArchSpec arch("x86_64-apple-macosx-");
+    Platform::SetHostPlatform(
+        PlatformRemoteMacOSX::CreateInstance(true, &arch));
+  }
+  void TearDown() override {
+    PlatformMacOSX::Terminate();
+    HostInfo::Terminate();
+    FileSystem::Terminate();
+    Reproducer::Terminate();
+  }
+};
+} // namespace
+
+TEST_F(DiagnosticEventTest, Warning) {
+  DebuggerSP debugger_sp = Debugger::CreateInstance();
+
+  Broadcaster &broadcaster = debugger_sp->GetBroadcaster();
+  ListenerSP listener_sp = Listener::MakeListener("test-listener");
+
+  listener_sp->StartListeningForEvents(&broadcaster,
+                                       Debugger::eBroadcastBitWarning);
+  EXPECT_TRUE(
+      broadcaster.EventTypeHasListeners(Debugger::eBroadcastBitWarning));
+
+  Debugger::ReportWarning("foo", debugger_sp->GetID());
+
+  EventSP event_sp;
+  EXPECT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
+  ASSERT_TRUE(event_sp);
+
+  const DiagnosticEventData *data =
+      DiagnosticEventData::GetEventDataFromEvent(event_sp.get());
+  ASSERT_NE(data, nullptr);
+  EXPECT_EQ(data->GetPrefix(), "warning");
+  EXPECT_EQ(data->GetMessage(), "foo");
+
+  Debugger::Destroy(debugger_sp);
+}
+
+TEST_F(DiagnosticEventTest, Error) {
+  DebuggerSP debugger_sp = Debugger::CreateInstance();
+
+  Broadcaster &broadcaster = debugger_sp->GetBroadcaster();
+  ListenerSP listener_sp = Listener::MakeListener("test-listener");
+
+  listener_sp->StartListeningForEvents(&broadcaster,
+                                       Debugger::eBroadcastBitError);
+  EXPECT_TRUE(broadcaster.EventTypeHasListeners(Debugger::eBroadcastBitError));
+
+  Debugger::ReportError("bar", debugger_sp->GetID());
+
+  EventSP event_sp;
+  EXPECT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
+  ASSERT_TRUE(event_sp);
+
+  const DiagnosticEventData *data =
+      DiagnosticEventData::GetEventDataFromEvent(event_sp.get());
+  ASSERT_NE(data, nullptr);
+  EXPECT_EQ(data->GetPrefix(), "error");
+  EXPECT_EQ(data->GetMessage(), "bar");
+
+  Debugger::Destroy(debugger_sp);
+}
+
+TEST_F(DiagnosticEventTest, MultipleDebuggers) {
+  std::vector<DebuggerSP> debuggers;
+  std::vector<ListenerSP> listeners;
+
+  for (size_t i = 0; i < DEBUGGERS; ++i) {
+    DebuggerSP debugger = Debugger::CreateInstance();
+    ListenerSP listener = Listener::MakeListener("listener");
+
+    debuggers.push_back(debugger);
+    listeners.push_back(listener);
+
+    listener->StartListeningForEvents(&debugger->GetBroadcaster(),
+                                      Debugger::eBroadcastBitError);
+  }
+
+  Debugger::ReportError("baz");
+
+  for (size_t i = 0; i < DEBUGGERS; ++i) {
+    EventSP event_sp;
+    EXPECT_TRUE(listeners[i]->GetEvent(event_sp, TIMEOUT));
+    ASSERT_TRUE(event_sp);
+
+    const DiagnosticEventData *data =
+        DiagnosticEventData::GetEventDataFromEvent(event_sp.get());
+    ASSERT_NE(data, nullptr);
+    EXPECT_EQ(data->GetPrefix(), "error");
+    EXPECT_EQ(data->GetMessage(), "baz");
+  }
+
+  for (size_t i = 0; i < DEBUGGERS; ++i) {
+    Debugger::Destroy(debuggers[i]);
+  }
+}
+
+TEST_F(DiagnosticEventTest, WarningOnce) {
+  DebuggerSP debugger_sp = Debugger::CreateInstance();
+
+  Broadcaster &broadcaster = debugger_sp->GetBroadcaster();
+  ListenerSP listener_sp = Listener::MakeListener("test-listener");
+
+  listener_sp->StartListeningForEvents(&broadcaster,
+                                       Debugger::eBroadcastBitWarning);
+  EXPECT_TRUE(
+      broadcaster.EventTypeHasListeners(Debugger::eBroadcastBitWarning));
+
+  std::once_flag once;
+  Debugger::ReportWarning("foo", debugger_sp->GetID(), &once);
+
+  {
+    EventSP event_sp;
+    EXPECT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
+    ASSERT_TRUE(event_sp);
+
+    const DiagnosticEventData *data =
+        DiagnosticEventData::GetEventDataFromEvent(event_sp.get());
+    ASSERT_NE(data, nullptr);
+    EXPECT_EQ(data->GetPrefix(), "warning");
+    EXPECT_EQ(data->GetMessage(), "foo");
+  }
+
+  EventSP second_event_sp;
+  EXPECT_FALSE(listener_sp->GetEvent(second_event_sp, TIMEOUT));
+
+  Debugger::ReportWarning("foo", debugger_sp->GetID(), &once);
+  EXPECT_FALSE(listener_sp->GetEvent(second_event_sp, TIMEOUT));
+
+  Debugger::ReportWarning("foo", debugger_sp->GetID());
+  EXPECT_TRUE(listener_sp->GetEvent(second_event_sp, TIMEOUT));
+
+  Debugger::Destroy(debugger_sp);
+}


        


More information about the lldb-commits mailing list