Coverage Report

Created: 2022-01-15 10:30

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/tools/lldb-vscode/ProgressEvent.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- ProgressEvent.cpp ---------------------------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "ProgressEvent.h"
10
11
#include "JSONUtils.h"
12
13
using namespace lldb_vscode;
14
using namespace llvm;
15
16
// The minimum duration of an event for it to be reported
17
const std::chrono::duration<double> kStartProgressEventReportDelay =
18
    std::chrono::seconds(1);
19
// The minimum time interval between update events for reporting. If multiple
20
// updates fall within the same time interval, only the latest is reported.
21
const std::chrono::duration<double> kUpdateProgressEventReportDelay =
22
    std::chrono::milliseconds(250);
23
24
ProgressEvent::ProgressEvent(uint64_t progress_id, Optional<StringRef> message,
25
                             uint64_t completed, uint64_t total,
26
                             const ProgressEvent *prev_event)
27
0
    : m_progress_id(progress_id) {
28
0
  if (message)
29
0
    m_message = message->str();
30
31
0
  const bool calculate_percentage = total != UINT64_MAX;
32
0
  if (completed == 0) {
33
    // Start event
34
0
    m_event_type = progressStart;
35
    // Wait a bit before reporting the start event in case in completes really
36
    // quickly.
37
0
    m_minimum_allowed_report_time =
38
0
        m_creation_time + kStartProgressEventReportDelay;
39
0
    if (calculate_percentage)
40
0
      m_percentage = 0;
41
0
  } else if (completed == total) {
42
    // End event
43
0
    m_event_type = progressEnd;
44
    // We should report the end event right away.
45
0
    m_minimum_allowed_report_time = std::chrono::seconds::zero();
46
0
    if (calculate_percentage)
47
0
      m_percentage = 100;
48
0
  } else {
49
    // Update event
50
0
    m_event_type = progressUpdate;
51
0
    m_percentage = std::min(
52
0
        (uint32_t)((double)completed / (double)total * 100.0), (uint32_t)99);
53
0
    if (prev_event->Reported()) {
54
      // Add a small delay between reports
55
0
      m_minimum_allowed_report_time =
56
0
          prev_event->m_minimum_allowed_report_time +
57
0
          kUpdateProgressEventReportDelay;
58
0
    } else {
59
      // We should use the previous timestamp, as it's still pending
60
0
      m_minimum_allowed_report_time = prev_event->m_minimum_allowed_report_time;
61
0
    }
62
0
  }
63
0
}
64
65
Optional<ProgressEvent> ProgressEvent::Create(uint64_t progress_id,
66
                                              Optional<StringRef> message,
67
                                              uint64_t completed,
68
                                              uint64_t total,
69
0
                                              const ProgressEvent *prev_event) {
70
  // If it's an update without a previous event, we abort
71
0
  if (completed > 0 && completed < total && !prev_event)
72
0
    return None;
73
0
  ProgressEvent event(progress_id, message, completed, total, prev_event);
74
  // We shouldn't show unnamed start events in the IDE
75
0
  if (event.GetEventType() == progressStart && event.GetEventName().empty())
76
0
    return None;
77
78
0
  if (prev_event && prev_event->EqualsForIDE(event))
79
0
    return None;
80
81
0
  return event;
82
0
}
83
84
0
bool ProgressEvent::EqualsForIDE(const ProgressEvent &other) const {
85
0
  return m_progress_id == other.m_progress_id &&
86
0
         m_event_type == other.m_event_type &&
87
0
         m_percentage == other.m_percentage;
88
0
}
89
90
0
ProgressEventType ProgressEvent::GetEventType() const { return m_event_type; }
91
92
0
StringRef ProgressEvent::GetEventName() const {
93
0
  if (m_event_type == progressStart)
94
0
    return "progressStart";
95
0
  else if (m_event_type == progressEnd)
96
0
    return "progressEnd";
97
0
  else
98
0
    return "progressUpdate";
99
0
}
100
101
0
json::Value ProgressEvent::ToJSON() const {
102
0
  llvm::json::Object event(CreateEventObject(GetEventName()));
103
0
  llvm::json::Object body;
104
105
0
  std::string progress_id_str;
106
0
  llvm::raw_string_ostream progress_id_strm(progress_id_str);
107
0
  progress_id_strm << m_progress_id;
108
0
  progress_id_strm.flush();
109
0
  body.try_emplace("progressId", progress_id_str);
110
111
0
  if (m_event_type == progressStart) {
112
0
    EmplaceSafeString(body, "title", m_message);
113
0
    body.try_emplace("cancellable", false);
114
0
  }
115
116
0
  std::string timestamp(llvm::formatv("{0:f9}", m_creation_time.count()));
117
0
  EmplaceSafeString(body, "timestamp", timestamp);
118
119
0
  if (m_percentage)
120
0
    body.try_emplace("percentage", *m_percentage);
121
122
0
  event.try_emplace("body", std::move(body));
123
0
  return json::Value(std::move(event));
124
0
}
125
126
0
bool ProgressEvent::Report(ProgressEventReportCallback callback) {
127
0
  if (Reported())
128
0
    return true;
129
0
  if (std::chrono::system_clock::now().time_since_epoch() <
130
0
      m_minimum_allowed_report_time)
131
0
    return false;
132
133
0
  m_reported = true;
134
0
  callback(*this);
135
0
  return true;
136
0
}
137
138
0
bool ProgressEvent::Reported() const { return m_reported; }
139
140
ProgressEventManager::ProgressEventManager(
141
    const ProgressEvent &start_event,
142
    ProgressEventReportCallback report_callback)
143
    : m_start_event(start_event), m_finished(false),
144
0
      m_report_callback(report_callback) {}
145
146
0
bool ProgressEventManager::ReportIfNeeded() {
147
  // The event finished before we were able to report it.
148
0
  if (!m_start_event.Reported() && Finished())
149
0
    return true;
150
151
0
  if (!m_start_event.Report(m_report_callback))
152
0
    return false;
153
154
0
  if (m_last_update_event)
155
0
    m_last_update_event->Report(m_report_callback);
156
0
  return true;
157
0
}
158
159
0
const ProgressEvent &ProgressEventManager::GetMostRecentEvent() const {
160
0
  return m_last_update_event ? *m_last_update_event : m_start_event;
161
0
}
162
163
void ProgressEventManager::Update(uint64_t progress_id, uint64_t completed,
164
0
                                  uint64_t total) {
165
0
  if (Optional<ProgressEvent> event = ProgressEvent::Create(
166
0
          progress_id, None, completed, total, &GetMostRecentEvent())) {
167
0
    if (event->GetEventType() == progressEnd)
168
0
      m_finished = true;
169
170
0
    m_last_update_event = *event;
171
0
    ReportIfNeeded();
172
0
  }
173
0
}
174
175
0
bool ProgressEventManager::Finished() const { return m_finished; }
176
177
ProgressEventReporter::ProgressEventReporter(
178
    ProgressEventReportCallback report_callback)
179
2
    : m_report_callback(report_callback) {
180
2
  m_thread_should_exit = false;
181
2
  m_thread = std::thread([&] {
182
4
    while (!m_thread_should_exit) {
183
2
      std::this_thread::sleep_for(kUpdateProgressEventReportDelay);
184
2
      ReportStartEvents();
185
2
    }
186
2
  });
187
2
}
188
189
0
ProgressEventReporter::~ProgressEventReporter() {
190
0
  m_thread_should_exit = true;
191
0
  m_thread.join();
192
0
}
193
194
0
void ProgressEventReporter::ReportStartEvents() {
195
0
  std::lock_guard<std::mutex> locker(m_mutex);
196
197
0
  while (!m_unreported_start_events.empty()) {
198
0
    ProgressEventManagerSP event_manager = m_unreported_start_events.front();
199
0
    if (event_manager->Finished())
200
0
      m_unreported_start_events.pop();
201
0
    else if (event_manager->ReportIfNeeded())
202
0
      m_unreported_start_events
203
0
          .pop(); // we remove it from the queue as it started reporting
204
                  // already, the Push method will be able to continue its
205
                  // reports.
206
0
    else
207
0
      break; // If we couldn't report it, then the next event in the queue won't
208
             // be able as well, as it came later.
209
0
  }
210
0
}
211
212
void ProgressEventReporter::Push(uint64_t progress_id, const char *message,
213
0
                                 uint64_t completed, uint64_t total) {
214
0
  std::lock_guard<std::mutex> locker(m_mutex);
215
216
0
  auto it = m_event_managers.find(progress_id);
217
0
  if (it == m_event_managers.end()) {
218
0
    if (Optional<ProgressEvent> event =
219
0
            ProgressEvent::Create(progress_id, StringRef(message), completed, total)) {
220
0
      ProgressEventManagerSP event_manager =
221
0
          std::make_shared<ProgressEventManager>(*event, m_report_callback);
222
0
      m_event_managers.insert({progress_id, event_manager});
223
0
      m_unreported_start_events.push(event_manager);
224
0
    }
225
0
  } else {
226
0
    it->second->Update(progress_id, completed, total);
227
0
    if (it->second->Finished())
228
0
      m_event_managers.erase(it);
229
0
  }
230
0
}