Skip to content

Commit d416798

Browse files
JlalondJDevlieghere
authored andcommitted
[LLDB-DAP] Send Progress update message over DAP (llvm#123837)
When testing my SBProgress DAP PR (llvm#123826), I noticed Progress update messages aren't sent over DAP. This patch adds the lldb progress event's message to the body when sent over DAP. Before ![image](https://github.com/user-attachments/assets/404adaa8-b784-4f23-895f-cd3625fdafad) Now ![image](https://github.com/user-attachments/assets/eb1c3235-0936-4e36-96e5-0a0ee60dabb8) Tested with my [progress tester command](https://gist.github.com/Jlalond/48d85e75a91f7a137e3142e6a13d0947), testing 10 events 5 seconds apart 1-10 (cherry picked from commit a939a9f)
1 parent 62f7492 commit d416798

File tree

6 files changed

+150
-5
lines changed

6 files changed

+150
-5
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import inspect
2+
import optparse
3+
import shlex
4+
import sys
5+
import time
6+
7+
import lldb
8+
9+
10+
class ProgressTesterCommand:
11+
program = "test-progress"
12+
13+
@classmethod
14+
def register_lldb_command(cls, debugger, module_name):
15+
parser = cls.create_options()
16+
cls.__doc__ = parser.format_help()
17+
# Add any commands contained in this module to LLDB
18+
command = "command script add -c %s.%s %s" % (
19+
module_name,
20+
cls.__name__,
21+
cls.program,
22+
)
23+
debugger.HandleCommand(command)
24+
print(
25+
'The "{0}" command has been installed, type "help {0}" or "{0} '
26+
'--help" for detailed help.'.format(cls.program)
27+
)
28+
29+
@classmethod
30+
def create_options(cls):
31+
usage = "usage: %prog [options]"
32+
description = "SBProgress testing tool"
33+
# Opt parse is deprecated, but leaving this the way it is because it allows help formating
34+
# Additionally all our commands use optparse right now, ideally we migrate them all in one go.
35+
parser = optparse.OptionParser(
36+
description=description, prog=cls.program, usage=usage
37+
)
38+
39+
parser.add_option(
40+
"--total", dest="total", help="Total to count up.", type="int"
41+
)
42+
43+
parser.add_option(
44+
"--seconds",
45+
dest="seconds",
46+
help="Total number of seconds to wait between increments",
47+
type="int",
48+
)
49+
50+
return parser
51+
52+
def get_short_help(self):
53+
return "Progress Tester"
54+
55+
def get_long_help(self):
56+
return self.help_string
57+
58+
def __init__(self, debugger, unused):
59+
self.parser = self.create_options()
60+
self.help_string = self.parser.format_help()
61+
62+
def __call__(self, debugger, command, exe_ctx, result):
63+
command_args = shlex.split(command)
64+
try:
65+
(cmd_options, args) = self.parser.parse_args(command_args)
66+
except:
67+
result.SetError("option parsing failed")
68+
return
69+
70+
total = cmd_options.total
71+
progress = lldb.SBProgress("Progress tester", "Detail", total, debugger)
72+
73+
for i in range(1, total):
74+
progress.Increment(1, f"Step {i}")
75+
time.sleep(cmd_options.seconds)
76+
77+
78+
def __lldb_init_module(debugger, dict):
79+
# Register all classes that have a register_lldb_command method
80+
for _name, cls in inspect.getmembers(sys.modules[__name__]):
81+
if inspect.isclass(cls) and callable(
82+
getattr(cls, "register_lldb_command", None)
83+
):
84+
cls.register_lldb_command(debugger, __name__)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""
2+
Test lldb-dap output events
3+
"""
4+
5+
from lldbsuite.test.decorators import *
6+
from lldbsuite.test.lldbtest import *
7+
import os
8+
import time
9+
10+
import lldbdap_testcase
11+
12+
13+
class TestDAP_progress(lldbdap_testcase.DAPTestCaseBase):
14+
@skipIfWindows
15+
def test_output(self):
16+
program = self.getBuildArtifact("a.out")
17+
self.build_and_launch(program)
18+
progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py")
19+
print(f"Progress emitter path: {progress_emitter}")
20+
source = "main.cpp"
21+
# Set breakpoint in the thread function so we can step the threads
22+
breakpoint_ids = self.set_source_breakpoints(
23+
source, [line_number(source, "// break here")]
24+
)
25+
self.continue_to_breakpoints(breakpoint_ids)
26+
self.dap_server.request_evaluate(
27+
f"`command script import {progress_emitter}", context="repl"
28+
)
29+
self.dap_server.request_evaluate(
30+
"`test-progress --total 3 --seconds 1", context="repl"
31+
)
32+
33+
self.dap_server.wait_for_event("progressEnd", 15)
34+
# Expect at least a start, an update, and end event
35+
# However because the underlying Progress instance is an RAII object and we can't guaruntee
36+
# it's deterministic destruction in the python API, we verify just start and update
37+
# otherwise this test could be flakey.
38+
self.assertTrue(len(self.dap_server.progress_events) > 0)
39+
start_found = False
40+
update_found = False
41+
for event in self.dap_server.progress_events:
42+
event_type = event["event"]
43+
if "progressStart" in event_type:
44+
start_found = True
45+
if "progressUpdate" in event_type:
46+
update_found = True
47+
48+
self.assertTrue(start_found)
49+
self.assertTrue(update_found)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
int main() {
2+
char *ptr = "unused";
3+
// break here
4+
return 0;
5+
}

lldb/tools/lldb-dap/ProgressEvent.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ json::Value ProgressEvent::ToJSON() const {
118118
body.try_emplace("cancellable", false);
119119
}
120120

121+
if (m_event_type == progressUpdate)
122+
EmplaceSafeString(body, "message", m_message);
123+
121124
std::string timestamp(llvm::formatv("{0:f9}", m_creation_time.count()));
122125
EmplaceSafeString(body, "timestamp", timestamp);
123126

@@ -165,10 +168,10 @@ const ProgressEvent &ProgressEventManager::GetMostRecentEvent() const {
165168
return m_last_update_event ? *m_last_update_event : m_start_event;
166169
}
167170

168-
void ProgressEventManager::Update(uint64_t progress_id, uint64_t completed,
169-
uint64_t total) {
171+
void ProgressEventManager::Update(uint64_t progress_id, llvm::StringRef message,
172+
uint64_t completed, uint64_t total) {
170173
if (std::optional<ProgressEvent> event = ProgressEvent::Create(
171-
progress_id, std::nullopt, completed, total, &GetMostRecentEvent())) {
174+
progress_id, message, completed, total, &GetMostRecentEvent())) {
172175
if (event->GetEventType() == progressEnd)
173176
m_finished = true;
174177

@@ -228,7 +231,7 @@ void ProgressEventReporter::Push(uint64_t progress_id, const char *message,
228231
m_unreported_start_events.push(event_manager);
229232
}
230233
} else {
231-
it->second->Update(progress_id, completed, total);
234+
it->second->Update(progress_id, StringRef(message), completed, total);
232235
if (it->second->Finished())
233236
m_event_managers.erase(it);
234237
}

lldb/tools/lldb-dap/ProgressEvent.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ class ProgressEventManager {
9898

9999
/// Receive a new progress event for the start event and try to report it if
100100
/// appropriate.
101-
void Update(uint64_t progress_id, uint64_t completed, uint64_t total);
101+
void Update(uint64_t progress_id, llvm::StringRef message, uint64_t completed,
102+
uint64_t total);
102103

103104
/// \return
104105
/// \b true if a \a progressEnd event has been notified. There's no

0 commit comments

Comments
 (0)