xref: /llvm-project/lldb/tools/lldb-dap/ProgressEvent.cpp (revision a939a9fd53d98f33b94f9121646d5906a2b9f598)
101263c6cSJonas Devlieghere //===-- ProgressEvent.cpp ---------------------------------------*- C++ -*-===//
201263c6cSJonas Devlieghere //
301263c6cSJonas Devlieghere // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
401263c6cSJonas Devlieghere // See https://llvm.org/LICENSE.txt for license information.
501263c6cSJonas Devlieghere // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
601263c6cSJonas Devlieghere //
701263c6cSJonas Devlieghere //===----------------------------------------------------------------------===//
801263c6cSJonas Devlieghere 
901263c6cSJonas Devlieghere #include "ProgressEvent.h"
1001263c6cSJonas Devlieghere 
1101263c6cSJonas Devlieghere #include "JSONUtils.h"
1201263c6cSJonas Devlieghere #include "llvm/Support/ErrorHandling.h"
1301263c6cSJonas Devlieghere #include <optional>
1401263c6cSJonas Devlieghere 
1501263c6cSJonas Devlieghere using namespace lldb_dap;
1601263c6cSJonas Devlieghere using namespace llvm;
1701263c6cSJonas Devlieghere 
1801263c6cSJonas Devlieghere // The minimum duration of an event for it to be reported
1901263c6cSJonas Devlieghere const std::chrono::duration<double> kStartProgressEventReportDelay =
2001263c6cSJonas Devlieghere     std::chrono::seconds(1);
2101263c6cSJonas Devlieghere // The minimum time interval between update events for reporting. If multiple
2201263c6cSJonas Devlieghere // updates fall within the same time interval, only the latest is reported.
2301263c6cSJonas Devlieghere const std::chrono::duration<double> kUpdateProgressEventReportDelay =
2401263c6cSJonas Devlieghere     std::chrono::milliseconds(250);
2501263c6cSJonas Devlieghere 
2601263c6cSJonas Devlieghere ProgressEvent::ProgressEvent(uint64_t progress_id,
2701263c6cSJonas Devlieghere                              std::optional<StringRef> message,
2801263c6cSJonas Devlieghere                              uint64_t completed, uint64_t total,
2901263c6cSJonas Devlieghere                              const ProgressEvent *prev_event)
3001263c6cSJonas Devlieghere     : m_progress_id(progress_id) {
3101263c6cSJonas Devlieghere   if (message)
3201263c6cSJonas Devlieghere     m_message = message->str();
3301263c6cSJonas Devlieghere 
3401263c6cSJonas Devlieghere   const bool calculate_percentage = total != UINT64_MAX;
3501263c6cSJonas Devlieghere   if (completed == 0) {
3601263c6cSJonas Devlieghere     // Start event
3701263c6cSJonas Devlieghere     m_event_type = progressStart;
3801263c6cSJonas Devlieghere     // Wait a bit before reporting the start event in case in completes really
3901263c6cSJonas Devlieghere     // quickly.
4001263c6cSJonas Devlieghere     m_minimum_allowed_report_time =
4101263c6cSJonas Devlieghere         m_creation_time + kStartProgressEventReportDelay;
4201263c6cSJonas Devlieghere     if (calculate_percentage)
4301263c6cSJonas Devlieghere       m_percentage = 0;
4401263c6cSJonas Devlieghere   } else if (completed == total) {
4501263c6cSJonas Devlieghere     // End event
4601263c6cSJonas Devlieghere     m_event_type = progressEnd;
4701263c6cSJonas Devlieghere     // We should report the end event right away.
4801263c6cSJonas Devlieghere     m_minimum_allowed_report_time = std::chrono::seconds::zero();
4901263c6cSJonas Devlieghere     if (calculate_percentage)
5001263c6cSJonas Devlieghere       m_percentage = 100;
5101263c6cSJonas Devlieghere   } else {
5201263c6cSJonas Devlieghere     // Update event
5301263c6cSJonas Devlieghere     m_event_type = progressUpdate;
5401263c6cSJonas Devlieghere     m_percentage = std::min(
5501263c6cSJonas Devlieghere         (uint32_t)((double)completed / (double)total * 100.0), (uint32_t)99);
5601263c6cSJonas Devlieghere     if (prev_event->Reported()) {
5701263c6cSJonas Devlieghere       // Add a small delay between reports
5801263c6cSJonas Devlieghere       m_minimum_allowed_report_time =
5901263c6cSJonas Devlieghere           prev_event->m_minimum_allowed_report_time +
6001263c6cSJonas Devlieghere           kUpdateProgressEventReportDelay;
6101263c6cSJonas Devlieghere     } else {
6201263c6cSJonas Devlieghere       // We should use the previous timestamp, as it's still pending
6301263c6cSJonas Devlieghere       m_minimum_allowed_report_time = prev_event->m_minimum_allowed_report_time;
6401263c6cSJonas Devlieghere     }
6501263c6cSJonas Devlieghere   }
6601263c6cSJonas Devlieghere }
6701263c6cSJonas Devlieghere 
6801263c6cSJonas Devlieghere std::optional<ProgressEvent>
6901263c6cSJonas Devlieghere ProgressEvent::Create(uint64_t progress_id, std::optional<StringRef> message,
7001263c6cSJonas Devlieghere                       uint64_t completed, uint64_t total,
7101263c6cSJonas Devlieghere                       const ProgressEvent *prev_event) {
7201263c6cSJonas Devlieghere   // If it's an update without a previous event, we abort
7301263c6cSJonas Devlieghere   if (completed > 0 && completed < total && !prev_event)
7401263c6cSJonas Devlieghere     return std::nullopt;
7501263c6cSJonas Devlieghere   ProgressEvent event(progress_id, message, completed, total, prev_event);
7601263c6cSJonas Devlieghere   // We shouldn't show unnamed start events in the IDE
7701263c6cSJonas Devlieghere   if (event.GetEventType() == progressStart && event.GetEventName().empty())
7801263c6cSJonas Devlieghere     return std::nullopt;
7901263c6cSJonas Devlieghere 
8001263c6cSJonas Devlieghere   if (prev_event && prev_event->EqualsForIDE(event))
8101263c6cSJonas Devlieghere     return std::nullopt;
8201263c6cSJonas Devlieghere 
8301263c6cSJonas Devlieghere   return event;
8401263c6cSJonas Devlieghere }
8501263c6cSJonas Devlieghere 
8601263c6cSJonas Devlieghere bool ProgressEvent::EqualsForIDE(const ProgressEvent &other) const {
8701263c6cSJonas Devlieghere   return m_progress_id == other.m_progress_id &&
8801263c6cSJonas Devlieghere          m_event_type == other.m_event_type &&
8901263c6cSJonas Devlieghere          m_percentage == other.m_percentage;
9001263c6cSJonas Devlieghere }
9101263c6cSJonas Devlieghere 
9201263c6cSJonas Devlieghere ProgressEventType ProgressEvent::GetEventType() const { return m_event_type; }
9301263c6cSJonas Devlieghere 
9401263c6cSJonas Devlieghere StringRef ProgressEvent::GetEventName() const {
9501263c6cSJonas Devlieghere   switch (m_event_type) {
9601263c6cSJonas Devlieghere   case progressStart:
9701263c6cSJonas Devlieghere     return "progressStart";
9801263c6cSJonas Devlieghere   case progressUpdate:
9901263c6cSJonas Devlieghere     return "progressUpdate";
10001263c6cSJonas Devlieghere   case progressEnd:
10101263c6cSJonas Devlieghere     return "progressEnd";
10201263c6cSJonas Devlieghere   }
10301263c6cSJonas Devlieghere   llvm_unreachable("All cases handled above!");
10401263c6cSJonas Devlieghere }
10501263c6cSJonas Devlieghere 
10601263c6cSJonas Devlieghere json::Value ProgressEvent::ToJSON() const {
10701263c6cSJonas Devlieghere   llvm::json::Object event(CreateEventObject(GetEventName()));
10801263c6cSJonas Devlieghere   llvm::json::Object body;
10901263c6cSJonas Devlieghere 
11001263c6cSJonas Devlieghere   std::string progress_id_str;
11101263c6cSJonas Devlieghere   llvm::raw_string_ostream progress_id_strm(progress_id_str);
11201263c6cSJonas Devlieghere   progress_id_strm << m_progress_id;
11301263c6cSJonas Devlieghere   body.try_emplace("progressId", progress_id_str);
11401263c6cSJonas Devlieghere 
11501263c6cSJonas Devlieghere   if (m_event_type == progressStart) {
11601263c6cSJonas Devlieghere     EmplaceSafeString(body, "title", m_message);
11701263c6cSJonas Devlieghere     body.try_emplace("cancellable", false);
11801263c6cSJonas Devlieghere   }
11901263c6cSJonas Devlieghere 
120*a939a9fdSJacob Lalonde   if (m_event_type == progressUpdate)
121*a939a9fdSJacob Lalonde     EmplaceSafeString(body, "message", m_message);
122*a939a9fdSJacob Lalonde 
12301263c6cSJonas Devlieghere   std::string timestamp(llvm::formatv("{0:f9}", m_creation_time.count()));
12401263c6cSJonas Devlieghere   EmplaceSafeString(body, "timestamp", timestamp);
12501263c6cSJonas Devlieghere 
12601263c6cSJonas Devlieghere   if (m_percentage)
12701263c6cSJonas Devlieghere     body.try_emplace("percentage", *m_percentage);
12801263c6cSJonas Devlieghere 
12901263c6cSJonas Devlieghere   event.try_emplace("body", std::move(body));
13001263c6cSJonas Devlieghere   return json::Value(std::move(event));
13101263c6cSJonas Devlieghere }
13201263c6cSJonas Devlieghere 
13301263c6cSJonas Devlieghere bool ProgressEvent::Report(ProgressEventReportCallback callback) {
13401263c6cSJonas Devlieghere   if (Reported())
13501263c6cSJonas Devlieghere     return true;
13601263c6cSJonas Devlieghere   if (std::chrono::system_clock::now().time_since_epoch() <
13701263c6cSJonas Devlieghere       m_minimum_allowed_report_time)
13801263c6cSJonas Devlieghere     return false;
13901263c6cSJonas Devlieghere 
14001263c6cSJonas Devlieghere   m_reported = true;
14101263c6cSJonas Devlieghere   callback(*this);
14201263c6cSJonas Devlieghere   return true;
14301263c6cSJonas Devlieghere }
14401263c6cSJonas Devlieghere 
14501263c6cSJonas Devlieghere bool ProgressEvent::Reported() const { return m_reported; }
14601263c6cSJonas Devlieghere 
14701263c6cSJonas Devlieghere ProgressEventManager::ProgressEventManager(
14801263c6cSJonas Devlieghere     const ProgressEvent &start_event,
14901263c6cSJonas Devlieghere     ProgressEventReportCallback report_callback)
15001263c6cSJonas Devlieghere     : m_start_event(start_event), m_finished(false),
15101263c6cSJonas Devlieghere       m_report_callback(report_callback) {}
15201263c6cSJonas Devlieghere 
15301263c6cSJonas Devlieghere bool ProgressEventManager::ReportIfNeeded() {
15401263c6cSJonas Devlieghere   // The event finished before we were able to report it.
15501263c6cSJonas Devlieghere   if (!m_start_event.Reported() && Finished())
15601263c6cSJonas Devlieghere     return true;
15701263c6cSJonas Devlieghere 
15801263c6cSJonas Devlieghere   if (!m_start_event.Report(m_report_callback))
15901263c6cSJonas Devlieghere     return false;
16001263c6cSJonas Devlieghere 
16101263c6cSJonas Devlieghere   if (m_last_update_event)
16201263c6cSJonas Devlieghere     m_last_update_event->Report(m_report_callback);
16301263c6cSJonas Devlieghere   return true;
16401263c6cSJonas Devlieghere }
16501263c6cSJonas Devlieghere 
16601263c6cSJonas Devlieghere const ProgressEvent &ProgressEventManager::GetMostRecentEvent() const {
16701263c6cSJonas Devlieghere   return m_last_update_event ? *m_last_update_event : m_start_event;
16801263c6cSJonas Devlieghere }
16901263c6cSJonas Devlieghere 
170*a939a9fdSJacob Lalonde void ProgressEventManager::Update(uint64_t progress_id, llvm::StringRef message,
171*a939a9fdSJacob Lalonde                                   uint64_t completed, uint64_t total) {
17201263c6cSJonas Devlieghere   if (std::optional<ProgressEvent> event = ProgressEvent::Create(
173*a939a9fdSJacob Lalonde           progress_id, message, completed, total, &GetMostRecentEvent())) {
17401263c6cSJonas Devlieghere     if (event->GetEventType() == progressEnd)
17501263c6cSJonas Devlieghere       m_finished = true;
17601263c6cSJonas Devlieghere 
17701263c6cSJonas Devlieghere     m_last_update_event = *event;
17801263c6cSJonas Devlieghere     ReportIfNeeded();
17901263c6cSJonas Devlieghere   }
18001263c6cSJonas Devlieghere }
18101263c6cSJonas Devlieghere 
18201263c6cSJonas Devlieghere bool ProgressEventManager::Finished() const { return m_finished; }
18301263c6cSJonas Devlieghere 
18401263c6cSJonas Devlieghere ProgressEventReporter::ProgressEventReporter(
18501263c6cSJonas Devlieghere     ProgressEventReportCallback report_callback)
18601263c6cSJonas Devlieghere     : m_report_callback(report_callback) {
18701263c6cSJonas Devlieghere   m_thread_should_exit = false;
18801263c6cSJonas Devlieghere   m_thread = std::thread([&] {
18901263c6cSJonas Devlieghere     while (!m_thread_should_exit) {
19001263c6cSJonas Devlieghere       std::this_thread::sleep_for(kUpdateProgressEventReportDelay);
19101263c6cSJonas Devlieghere       ReportStartEvents();
19201263c6cSJonas Devlieghere     }
19301263c6cSJonas Devlieghere   });
19401263c6cSJonas Devlieghere }
19501263c6cSJonas Devlieghere 
19601263c6cSJonas Devlieghere ProgressEventReporter::~ProgressEventReporter() {
19701263c6cSJonas Devlieghere   m_thread_should_exit = true;
19801263c6cSJonas Devlieghere   m_thread.join();
19901263c6cSJonas Devlieghere }
20001263c6cSJonas Devlieghere 
20101263c6cSJonas Devlieghere void ProgressEventReporter::ReportStartEvents() {
20201263c6cSJonas Devlieghere   std::lock_guard<std::mutex> locker(m_mutex);
20301263c6cSJonas Devlieghere 
20401263c6cSJonas Devlieghere   while (!m_unreported_start_events.empty()) {
20501263c6cSJonas Devlieghere     ProgressEventManagerSP event_manager = m_unreported_start_events.front();
20601263c6cSJonas Devlieghere     if (event_manager->Finished())
20701263c6cSJonas Devlieghere       m_unreported_start_events.pop();
20801263c6cSJonas Devlieghere     else if (event_manager->ReportIfNeeded())
20901263c6cSJonas Devlieghere       m_unreported_start_events
21001263c6cSJonas Devlieghere           .pop(); // we remove it from the queue as it started reporting
21101263c6cSJonas Devlieghere                   // already, the Push method will be able to continue its
21201263c6cSJonas Devlieghere                   // reports.
21301263c6cSJonas Devlieghere     else
21401263c6cSJonas Devlieghere       break; // If we couldn't report it, then the next event in the queue won't
21501263c6cSJonas Devlieghere              // be able as well, as it came later.
21601263c6cSJonas Devlieghere   }
21701263c6cSJonas Devlieghere }
21801263c6cSJonas Devlieghere 
21901263c6cSJonas Devlieghere void ProgressEventReporter::Push(uint64_t progress_id, const char *message,
22001263c6cSJonas Devlieghere                                  uint64_t completed, uint64_t total) {
22101263c6cSJonas Devlieghere   std::lock_guard<std::mutex> locker(m_mutex);
22201263c6cSJonas Devlieghere 
22301263c6cSJonas Devlieghere   auto it = m_event_managers.find(progress_id);
22401263c6cSJonas Devlieghere   if (it == m_event_managers.end()) {
22501263c6cSJonas Devlieghere     if (std::optional<ProgressEvent> event = ProgressEvent::Create(
22601263c6cSJonas Devlieghere             progress_id, StringRef(message), completed, total)) {
22701263c6cSJonas Devlieghere       ProgressEventManagerSP event_manager =
22801263c6cSJonas Devlieghere           std::make_shared<ProgressEventManager>(*event, m_report_callback);
22901263c6cSJonas Devlieghere       m_event_managers.insert({progress_id, event_manager});
23001263c6cSJonas Devlieghere       m_unreported_start_events.push(event_manager);
23101263c6cSJonas Devlieghere     }
23201263c6cSJonas Devlieghere   } else {
233*a939a9fdSJacob Lalonde     it->second->Update(progress_id, StringRef(message), completed, total);
23401263c6cSJonas Devlieghere     if (it->second->Finished())
23501263c6cSJonas Devlieghere       m_event_managers.erase(it);
23601263c6cSJonas Devlieghere   }
23701263c6cSJonas Devlieghere }
238