diff --git a/lldb/tools/lldb-vscode/CMakeLists.txt b/lldb/tools/lldb-vscode/CMakeLists.txt --- a/lldb/tools/lldb-vscode/CMakeLists.txt +++ b/lldb/tools/lldb-vscode/CMakeLists.txt @@ -32,6 +32,7 @@ IOStream.cpp JSONUtils.cpp LLDBUtils.cpp + ProgressEvent.cpp RunInTerminal.cpp SourceBreakpoint.cpp VSCode.cpp diff --git a/lldb/tools/lldb-vscode/ProgressEvent.h b/lldb/tools/lldb-vscode/ProgressEvent.h new file mode 100644 --- /dev/null +++ b/lldb/tools/lldb-vscode/ProgressEvent.h @@ -0,0 +1,62 @@ +//===-- ProgressEvent.cpp ---------------------------------------*- C++ -*-===// +// +// 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 "VSCodeForward.h" + +#include "llvm/Support/JSON.h" + +namespace lldb_vscode { + +enum ProgressEventType { + progressInvalid, + progressStart, + progressUpdate, + progressEnd +}; + +class ProgressEvent { +public: + ProgressEvent() {} + + ProgressEvent(uint64_t progress_id, const char *message, uint64_t completed, + uint64_t total); + + llvm::json::Value ToJSON() const; + + /// This operator returns \b true if two event messages + /// would result in the same event for the IDE, e.g. + /// same rounded percentage. + bool operator==(const ProgressEvent &other) const; + + const char *GetEventName() const; + + bool IsValid() const; + + uint64_t GetID() const; + +private: + uint64_t m_progress_id; + const char *m_message; + ProgressEventType m_event_type; + llvm::Optional m_percentage; +}; + +/// Class that filters out progress event messages that shouldn't be reported +/// to the IDE, either because they are invalid or because they are too chatty. +class ProgressEventFilterQueue { +public: + ProgressEventFilterQueue(std::function callback); + + void Push(const ProgressEvent &event); + +private: + std::function m_callback; + std::map m_last_events; +}; + +} // namespace lldb_vscode diff --git a/lldb/tools/lldb-vscode/ProgressEvent.cpp b/lldb/tools/lldb-vscode/ProgressEvent.cpp new file mode 100644 --- /dev/null +++ b/lldb/tools/lldb-vscode/ProgressEvent.cpp @@ -0,0 +1,93 @@ +//===-- ProgressEvent.cpp ---------------------------------------*- C++ -*-===// +// +// 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 "ProgressEvent.h" + +#include "JSONUtils.h" + +using namespace lldb_vscode; +using namespace llvm; + +ProgressEvent::ProgressEvent(uint64_t progress_id, const char *message, + uint64_t completed, uint64_t total) + : m_progress_id(progress_id), m_message(message) { + if (completed == total) + m_event_type = progressEnd; + else if (completed == 0) + m_event_type = progressStart; + else if (completed < total) + m_event_type = progressUpdate; + else + m_event_type = progressInvalid; + + if (0 < total && total < UINT64_MAX) + m_percentage = (uint32_t)(((float)completed / (float)total) * 100.0); +} + +bool ProgressEvent::operator==(const ProgressEvent &other) const { + return m_progress_id == other.m_progress_id && + m_event_type == other.m_event_type && + m_percentage == other.m_percentage; +} + +const char *ProgressEvent::GetEventName() const { + if (m_event_type == progressStart) + return "progressStart"; + else if (m_event_type == progressEnd) + return "progressEnd"; + else if (m_event_type == progressUpdate) + return "progressUpdate"; + else + return "progressInvalid"; +} + +bool ProgressEvent::IsValid() const { return m_event_type != progressInvalid; } + +uint64_t ProgressEvent::GetID() const { return m_progress_id; } + +json::Value ProgressEvent::ToJSON() const { + llvm::json::Object event(CreateEventObject(GetEventName())); + llvm::json::Object body; + + std::string progress_id_str; + llvm::raw_string_ostream progress_id_strm(progress_id_str); + progress_id_strm << m_progress_id; + progress_id_strm.flush(); + body.try_emplace("progressId", progress_id_str); + + if (m_event_type == progressStart) { + EmplaceSafeString(body, "title", m_message); + body.try_emplace("cancellable", false); + } + + auto now = std::chrono::duration( + std::chrono::system_clock::now().time_since_epoch()); + std::string timestamp(llvm::formatv("{0:f9}", now.count())); + EmplaceSafeString(body, "timestamp", timestamp); + + if (m_percentage) + body.try_emplace("percentage", *m_percentage); + + event.try_emplace("body", std::move(body)); + return json::Value(std::move(event)); +} + +ProgressEventFilterQueue::ProgressEventFilterQueue( + std::function callback) + : m_callback(callback) {} + +void ProgressEventFilterQueue::Push(const ProgressEvent &event) { + if (!event.IsValid()) + return; + + auto it = m_last_events.find(event.GetID()); + if (it == m_last_events.end() || !(it->second == event)) { + m_last_events[event.GetID()] = event; + m_callback(event); + } +} diff --git a/lldb/tools/lldb-vscode/VSCode.h b/lldb/tools/lldb-vscode/VSCode.h --- a/lldb/tools/lldb-vscode/VSCode.h +++ b/lldb/tools/lldb-vscode/VSCode.h @@ -49,6 +49,7 @@ #include "ExceptionBreakpoint.h" #include "FunctionBreakpoint.h" #include "IOStream.h" +#include "ProgressEvent.h" #include "RunInTerminal.h" #include "SourceBreakpoint.h" #include "SourceReference.h" @@ -113,6 +114,7 @@ uint32_t reverse_request_seq; std::map request_handlers; bool waiting_for_run_in_terminal; + ProgressEventFilterQueue progress_event_queue; // Keep track of the last stop thread index IDs as threads won't go away // unless we send a "thread" event to indicate the thread exited. llvm::DenseSet thread_ids; @@ -136,8 +138,7 @@ void SendOutput(OutputType o, const llvm::StringRef output); - void SendProgressEvent(uint64_t progress_id, const char *message, - uint64_t completed, uint64_t total); + void SendProgressEvent(const ProgressEvent &event); void __attribute__((format(printf, 3, 4))) SendFormattedOutput(OutputType o, const char *format, ...); diff --git a/lldb/tools/lldb-vscode/VSCode.cpp b/lldb/tools/lldb-vscode/VSCode.cpp --- a/lldb/tools/lldb-vscode/VSCode.cpp +++ b/lldb/tools/lldb-vscode/VSCode.cpp @@ -40,8 +40,10 @@ {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift}, {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}), focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false), - stop_at_entry(false), is_attach(false), - reverse_request_seq(0), waiting_for_run_in_terminal(false) { + stop_at_entry(false), is_attach(false), reverse_request_seq(0), + waiting_for_run_in_terminal(false), + progress_event_queue( + [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }) { const char *log_file_path = getenv("LLDBVSCODE_LOG"); #if defined(_WIN32) // Windows opens stdout and stdin in text mode which converts \n to 13,10 @@ -320,51 +322,8 @@ // }; // } -void VSCode::SendProgressEvent(uint64_t progress_id, const char *message, - uint64_t completed, uint64_t total) { - enum ProgressEventType { - progressInvalid, - progressStart, - progressUpdate, - progressEnd - }; - const char *event_name = nullptr; - ProgressEventType event_type = progressInvalid; - if (completed == 0) { - event_type = progressStart; - event_name = "progressStart"; - } else if (completed == total) { - event_type = progressEnd; - event_name = "progressEnd"; - } else if (completed < total) { - event_type = progressUpdate; - event_name = "progressUpdate"; - } - if (event_type == progressInvalid) - return; - - llvm::json::Object event(CreateEventObject(event_name)); - llvm::json::Object body; - std::string progress_id_str; - llvm::raw_string_ostream progress_id_strm(progress_id_str); - progress_id_strm << progress_id; - progress_id_strm.flush(); - body.try_emplace("progressId", progress_id_str); - if (event_type == progressStart) { - EmplaceSafeString(body, "title", message); - body.try_emplace("cancellable", false); - } - auto now = std::chrono::duration( - std::chrono::system_clock::now().time_since_epoch()); - std::string timestamp(llvm::formatv("{0:f9}", now.count())); - EmplaceSafeString(body, "timestamp", timestamp); - - if (0 < total && total < UINT64_MAX) { - uint32_t percentage = (uint32_t)(((float)completed / (float)total) * 100.0); - body.try_emplace("percentage", percentage); - } - event.try_emplace("body", std::move(body)); - SendJSON(llvm::json::Value(std::move(event))); +void VSCode::SendProgressEvent(const ProgressEvent &event) { + progress_event_queue.Push(event); } void __attribute__((format(printf, 3, 4))) diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -374,7 +374,8 @@ const char *message = lldb::SBDebugger::GetProgressFromEvent( event, progress_id, completed, total, is_debugger_specific); if (message) - g_vsc.SendProgressEvent(progress_id, message, completed, total); + g_vsc.SendProgressEvent( + ProgressEvent(progress_id, message, completed, total)); } } }