JDevlieghere created this revision.
JDevlieghere added reviewers: labath, clayborg, mib, aprantl, kastiglione.
Herald added a project: All.
JDevlieghere requested review of this revision.

This patch adds support for showing progress events when using lldb on the 
command line. It spawns a separate thread that listens for progress events and 
prints them to the debugger's output stream.

It's nothing fancy (yet), for now it just prints the progress message. If we 
know the total number of items being processed, we prefix the message with 
something like  [1/100], similar to ninja's output. It doesn't use any fancy 
terminal manipulation: it uses a simple carriage return (`\r`) to bring the 
cursor to the front of the line. If we support ANSI escape codes, we use a 
vt100 escape code to clear the current line when a progress event is complete. 
Otherwise we just overwrite it with as many spaces as the current terminal is 
wide.

Here's a recording of what this looks like in practice: 
https://asciinema.org/a/HBMDelpb9XDKccXZ09mgfuJ8R


https://reviews.llvm.org/D120972

Files:
  lldb/include/lldb/API/SBCommandInterpreterRunOptions.h
  lldb/include/lldb/Core/Debugger.h
  lldb/include/lldb/Interpreter/CommandInterpreter.h
  lldb/source/API/SBCommandInterpreterRunOptions.cpp
  lldb/source/Core/Debugger.cpp
  lldb/source/Interpreter/CommandInterpreter.cpp
  lldb/source/Symbol/LocateSymbolFile.cpp
  lldb/tools/driver/Driver.cpp

Index: lldb/tools/driver/Driver.cpp
===================================================================
--- lldb/tools/driver/Driver.cpp
+++ lldb/tools/driver/Driver.cpp
@@ -565,6 +565,7 @@
     m_debugger.SetAsync(false);
 
     SBCommandInterpreterRunOptions options;
+    options.SetShowProgress(true);
     options.SetAutoHandleEvents(true);
     options.SetSpawnThread(false);
     options.SetStopOnError(true);
Index: lldb/source/Symbol/LocateSymbolFile.cpp
===================================================================
--- lldb/source/Symbol/LocateSymbolFile.cpp
+++ lldb/source/Symbol/LocateSymbolFile.cpp
@@ -10,6 +10,7 @@
 
 #include "lldb/Core/ModuleList.h"
 #include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Progress.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Utility/ArchSpec.h"
@@ -257,6 +258,10 @@
 FileSpec
 Symbols::LocateExecutableSymbolFile(const ModuleSpec &module_spec,
                                     const FileSpecList &default_search_paths) {
+
+  Progress progress(llvm::formatv(
+      "Locating external symbol file for {0}",
+      module_spec.GetFileSpec().GetFilename().AsCString("<Unknown>")));
   FileSpec symbol_file_spec = module_spec.GetSymbolFileSpec();
   if (symbol_file_spec.IsAbsolute() &&
       FileSystem::Instance().Exists(symbol_file_spec))
Index: lldb/source/Interpreter/CommandInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/CommandInterpreter.cpp
+++ lldb/source/Interpreter/CommandInterpreter.cpp
@@ -3283,6 +3283,9 @@
 
 CommandInterpreterRunResult CommandInterpreter::RunCommandInterpreter(
     CommandInterpreterRunOptions &options) {
+  if (options.GetShowProgress())
+    m_debugger.StartProgressHandlerThread();
+
   // Always re-create the command interpreter when we run it in case any file
   // handles have changed.
   bool force_create = true;
Index: lldb/source/Core/Debugger.cpp
===================================================================
--- lldb/source/Core/Debugger.cpp
+++ lldb/source/Core/Debugger.cpp
@@ -1719,6 +1719,68 @@
   return {};
 }
 
+lldb::thread_result_t Debugger::ProgressHandlerThread() {
+  // Only report progress for real terminals.
+  if (!GetOutputFile().GetIsRealTerminal())
+    return {};
+
+  // Only report progress is nobody else is listening for it.
+  if (GetBroadcaster().EventTypeHasListeners(Debugger::eBroadcastBitProgress))
+    return {};
+
+  ListenerSP listener_sp = Listener::MakeListener("lldb.progress.listener");
+  listener_sp->StartListeningForEvents(&m_broadcaster,
+                                       Debugger::eBroadcastBitProgress);
+
+  // Only handle one kind of event at the same time. Ignore events that come in
+  // in while the current event hasn't completed
+  llvm::Optional<uint64_t> CurrentEventID;
+
+  // Assume that the if the terminal supports colors (ANSI escape codes) it
+  // also supports vt100 escape codes.
+  const bool use_vt100_escapes = GetOutputFile().GetIsTerminalWithColors();
+
+  while (true) {
+    lldb::EventSP event_sp;
+    if (listener_sp->GetEvent(event_sp, llvm::None)) {
+      if (!event_sp)
+        continue;
+      if (auto *data = Debugger::ProgressEventData::GetEventDataFromEvent(
+              event_sp.get())) {
+
+        uint64_t id = data->GetID();
+        if (CurrentEventID) {
+          if (id != *CurrentEventID)
+            continue;
+        } else {
+          CurrentEventID = id;
+        }
+
+        std::string message = data->GetMessage();
+        if (data->GetTotal() != UINT64_MAX) {
+          GetOutputFile().Printf("[%llu/%llu] %s\r", data->GetCompleted(),
+                                 data->GetTotal(), message.c_str());
+        } else {
+          GetOutputFile().Printf("%s\r", message.c_str());
+        }
+
+        if (data->GetCompleted()) {
+          CurrentEventID.reset();
+          // Clear the current line. Use an escape character if supported,
+          // otherwise overwrite the line with a sequence of spaces.
+          if (use_vt100_escapes) {
+            GetOutputFile().Printf("\33[2K\r");
+          } else {
+            std::string clear(GetTerminalWidth(), ' ');
+            GetOutputFile().Printf("%s\r", clear.c_str());
+          }
+        }
+      }
+    }
+  }
+  return {};
+}
+
 bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); }
 
 bool Debugger::StartIOHandlerThread() {
@@ -1751,6 +1813,30 @@
   }
 }
 
+bool Debugger::StartProgressHandlerThread() {
+  if (!m_progress_handler_thread.IsJoinable()) {
+    llvm::Expected<HostThread> progress_handler_thread =
+        ThreadLauncher::LaunchThread(
+            "lldb.debugger.progress-handler",
+            [this] { return ProgressHandlerThread(); },
+            8 * 1024 * 1024); // Use larger 8MB stack for this thread
+    if (progress_handler_thread) {
+      m_progress_handler_thread = *progress_handler_thread;
+    } else {
+      LLDB_LOG(GetLog(LLDBLog::Host), "failed to launch host thread: {}",
+               llvm::toString(progress_handler_thread.takeError()));
+    }
+  }
+
+  return m_io_handler_thread.IsJoinable();
+}
+
+void Debugger::StopProgressHandlerThread() {
+  if (m_progress_handler_thread.IsJoinable()) {
+    m_progress_handler_thread.Join(nullptr);
+  }
+}
+
 Target &Debugger::GetSelectedOrDummyTarget(bool prefer_dummy) {
   if (!prefer_dummy) {
     if (TargetSP target = m_target_list.GetSelectedTarget())
Index: lldb/source/API/SBCommandInterpreterRunOptions.cpp
===================================================================
--- lldb/source/API/SBCommandInterpreterRunOptions.cpp
+++ lldb/source/API/SBCommandInterpreterRunOptions.cpp
@@ -139,6 +139,18 @@
   m_opaque_up->SetAddToHistory(add_to_history);
 }
 
+bool SBCommandInterpreterRunOptions::GetShowProgress() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  return m_opaque_up->GetShowProgress();
+}
+
+void SBCommandInterpreterRunOptions::SetShowProgress(bool spawn_thread) {
+  LLDB_INSTRUMENT_VA(this, spawn_thread);
+
+  m_opaque_up->SetShowProgress(spawn_thread);
+}
+
 bool SBCommandInterpreterRunOptions::GetAutoHandleEvents() const {
   LLDB_INSTRUMENT_VA(this);
 
Index: lldb/include/lldb/Interpreter/CommandInterpreter.h
===================================================================
--- lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -166,10 +166,14 @@
     m_add_to_history = add_to_history ? eLazyBoolYes : eLazyBoolNo;
   }
 
-  bool GetAutoHandleEvents() const {
-    return DefaultToYes(m_auto_handle_events);
+  bool GetShowProgress() const { return DefaultToYes(m_show_progress); }
+
+  void SetShowProgress(bool show_progress) {
+    m_show_progress = show_progress ? eLazyBoolYes : eLazyBoolNo;
   }
 
+  bool GetAutoHandleEvents() const { return DefaultToNo(m_auto_handle_events); }
+
   void SetAutoHandleEvents(bool auto_handle_events) {
     m_auto_handle_events = auto_handle_events ? eLazyBoolYes : eLazyBoolNo;
   }
@@ -188,6 +192,7 @@
   LazyBool m_print_results = eLazyBoolCalculate;
   LazyBool m_print_errors = eLazyBoolCalculate;
   LazyBool m_add_to_history = eLazyBoolCalculate;
+  LazyBool m_show_progress;
   LazyBool m_auto_handle_events;
   LazyBool m_spawn_thread;
 
@@ -225,17 +230,18 @@
 
   enum ChildrenTruncatedWarningStatus // tristate boolean to manage children
                                       // truncation warning
-  { eNoTruncation = 0,                // never truncated
-    eUnwarnedTruncation = 1,          // truncated but did not notify
-    eWarnedTruncation = 2             // truncated and notified
+  {
+    eNoTruncation = 0,       // never truncated
+    eUnwarnedTruncation = 1, // truncated but did not notify
+    eWarnedTruncation = 2    // truncated and notified
   };
 
   enum CommandTypes {
     eCommandTypesBuiltin = 0x0001, //< native commands such as "frame"
     eCommandTypesUserDef = 0x0002, //< scripted commands
-    eCommandTypesUserMW  = 0x0004, //< multiword commands (command containers)
+    eCommandTypesUserMW = 0x0004,  //< multiword commands (command containers)
     eCommandTypesAliases = 0x0008, //< aliases such as "po"
-    eCommandTypesHidden  = 0x0010, //< commands prefixed with an underscore
+    eCommandTypesHidden = 0x0010,  //< commands prefixed with an underscore
     eCommandTypesAllThem = 0xFFFF  //< all commands
   };
 
@@ -307,9 +313,8 @@
   ///         dummy "contains everything MWC, so we return null here, but
   ///         in this case error.Success is true.
 
-  CommandObjectMultiword *VerifyUserMultiwordCmdPath(Args &path,
-                                                     bool leaf_is_command,
-                                                     Status &result);
+  CommandObjectMultiword *
+  VerifyUserMultiwordCmdPath(Args &path, bool leaf_is_command, Status &result);
 
   CommandAlias *AddAlias(llvm::StringRef alias_name,
                          lldb::CommandObjectSP &command_obj_sp,
Index: lldb/include/lldb/Core/Debugger.h
===================================================================
--- lldb/include/lldb/Core/Debugger.h
+++ lldb/include/lldb/Core/Debugger.h
@@ -452,10 +452,15 @@
 
   void JoinIOHandlerThread();
 
+  bool StartProgressHandlerThread();
+  void StopProgressHandlerThread();
+
   lldb::thread_result_t IOHandlerThread();
 
   lldb::thread_result_t DefaultEventHandler();
 
+  lldb::thread_result_t ProgressHandlerThread();
+
   void HandleBreakpointEvent(const lldb::EventSP &event_sp);
 
   void HandleProcessEvent(const lldb::EventSP &event_sp);
@@ -518,6 +523,7 @@
   LoadedPluginsList m_loaded_plugins;
   HostThread m_event_handler_thread;
   HostThread m_io_handler_thread;
+  HostThread m_progress_handler_thread;
   Broadcaster m_sync_broadcaster; ///< Private debugger synchronization.
   Broadcaster m_broadcaster;      ///< Public Debugger event broadcaster.
   lldb::ListenerSP m_forward_listener_sp;
Index: lldb/include/lldb/API/SBCommandInterpreterRunOptions.h
===================================================================
--- lldb/include/lldb/API/SBCommandInterpreterRunOptions.h
+++ lldb/include/lldb/API/SBCommandInterpreterRunOptions.h
@@ -64,6 +64,10 @@
 
   void SetAddToHistory(bool);
 
+  bool GetShowProgress() const;
+
+  void SetShowProgress(bool);
+
   bool GetAutoHandleEvents() const;
 
   void SetAutoHandleEvents(bool);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to