Coverage Report

Created: 2023-09-30 09:22

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