diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -329,6 +329,8 @@ bool SetUseColor(bool use_color); + bool GetShowProgress() const; + bool GetUseAutosuggestion() const; llvm::StringRef GetAutosuggestionAnsiPrefix() const; @@ -439,6 +441,8 @@ uint64_t completed, uint64_t total, llvm::Optional debugger_id); + void PrintProgress(const Debugger::ProgressEventData &data); + bool StartEventHandlerThread(); void StopEventHandlerThread(); @@ -466,6 +470,8 @@ void HandleThreadEvent(const lldb::EventSP &event_sp); + void HandleProgressEvent(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, @@ -514,6 +520,8 @@ IOHandlerStack m_io_handler_stack; std::recursive_mutex m_io_handler_synchronous_mutex; + llvm::Optional m_current_event_id; + llvm::StringMap> m_log_streams; std::shared_ptr m_log_callback_stream_sp; ConstString m_instance_name; diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -131,6 +131,10 @@ Global, DefaultTrue, Desc<"Whether to use Ansi color codes or not.">; + def ShowProgress: Property<"show-progress", "Boolean">, + Global, + DefaultTrue, + Desc<"Whether to show progress or not if the debugger's output is an interactive color-enabled terminal.">; def UseSourceCache: Property<"use-source-cache", "Boolean">, Global, DefaultTrue, diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -378,6 +378,12 @@ return ret; } +bool Debugger::GetShowProgress() const { + const uint32_t idx = ePropertyShowProgress; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_debugger_properties[idx].default_uint_value != 0); +} + bool Debugger::GetUseAutosuggestion() const { const uint32_t idx = ePropertyShowAutosuggestion; return m_collection_sp->GetPropertyAtIndexAsBoolean( @@ -1615,6 +1621,11 @@ CommandInterpreter::eBroadcastBitAsynchronousOutputData | CommandInterpreter::eBroadcastBitAsynchronousErrorData); + if (!m_broadcaster.EventTypeHasListeners(Debugger::eBroadcastBitProgress)) { + listener_sp->StartListeningForEvents(&m_broadcaster, + Debugger::eBroadcastBitProgress); + } + // 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 m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening); @@ -1664,6 +1675,9 @@ } } } + } else if (broadcaster == &m_broadcaster) { + if (event_type & Debugger::eBroadcastBitProgress) + HandleProgressEvent(event_sp); } } @@ -1729,6 +1743,61 @@ return {}; } +void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) { + auto *data = + Debugger::ProgressEventData::GetEventDataFromEvent(event_sp.get()); + if (!data) + return; + + // Do some bookkeeping for the current event, regardless of whether we're + // going to show the progress. + const uint64_t id = data->GetID(); + if (m_current_event_id) { + if (id != *m_current_event_id) + return; + if (data->GetCompleted()) + m_current_event_id.reset(); + } else { + m_current_event_id = id; + } + + // Decide whether we actually are going to show the progress. This decision + // can change between iterations so check it inside the loop. + if (!GetShowProgress()) + return; + + // Determine whether the current output file is an interactive terminal with + // color support. We assume that if we support ANSI escape codes we support + // vt100 escape codes. + File &output = GetOutputFile(); + if (!output.GetIsInteractive() || !output.GetIsTerminalWithColors()) + return; + + if (data->GetCompleted()) { + // Clear the current line. + output.Printf("\33[2K\r"); + return; + } + + // Print over previous line, if any. + output.Printf("\r"); + + // Print the progress message. + std::string message = data->GetMessage(); + if (data->GetTotal() != UINT64_MAX) { + output.Printf("[%llu/%llu] %s...", data->GetCompleted(), data->GetTotal(), + message.c_str()); + } else { + output.Printf("%s...", message.c_str()); + } + + // Clear until the end of the line. + output.Printf("\x1B[K"); + + // Flush the output. + output.Flush(); +} + bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); } bool Debugger::StartIOHandlerThread() { diff --git a/lldb/source/Symbol/LocateSymbolFile.cpp b/lldb/source/Symbol/LocateSymbolFile.cpp --- a/lldb/source/Symbol/LocateSymbolFile.cpp +++ b/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" @@ -262,6 +263,10 @@ FileSystem::Instance().Exists(symbol_file_spec)) return symbol_file_spec; + Progress progress(llvm::formatv( + "Locating external symbol file for {0}", + module_spec.GetFileSpec().GetFilename().AsCString(""))); + FileSpecList debug_file_search_paths = default_search_paths; // Add module directory.