Skip to content

[LLDB-DAP] Send Progress update message over DAP #123837

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lldb/test/API/tools/lldb-dap/progress/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
84 changes: 84 additions & 0 deletions lldb/test/API/tools/lldb-dap/progress/Progress_emitter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import inspect
import optparse
import shlex
import sys
import time

import lldb


class ProgressTesterCommand:
program = "test-progress"

@classmethod
def register_lldb_command(cls, debugger, module_name):
parser = cls.create_options()
cls.__doc__ = parser.format_help()
# Add any commands contained in this module to LLDB
command = "command script add -c %s.%s %s" % (
module_name,
cls.__name__,
cls.program,
)
debugger.HandleCommand(command)
print(
'The "{0}" command has been installed, type "help {0}" or "{0} '
'--help" for detailed help.'.format(cls.program)
)

@classmethod
def create_options(cls):
usage = "usage: %prog [options]"
description = "SBProgress testing tool"
# Opt parse is deprecated, but leaving this the way it is because it allows help formating
# Additionally all our commands use optparse right now, ideally we migrate them all in one go.
parser = optparse.OptionParser(
description=description, prog=cls.program, usage=usage
)

parser.add_option(
"--total", dest="total", help="Total to count up.", type="int"
)

parser.add_option(
"--seconds",
dest="seconds",
help="Total number of seconds to wait between increments",
type="int",
)

return parser

def get_short_help(self):
return "Progress Tester"

def get_long_help(self):
return self.help_string

def __init__(self, debugger, unused):
self.parser = self.create_options()
self.help_string = self.parser.format_help()

def __call__(self, debugger, command, exe_ctx, result):
command_args = shlex.split(command)
try:
(cmd_options, args) = self.parser.parse_args(command_args)
except:
result.SetError("option parsing failed")
return

total = cmd_options.total
progress = lldb.SBProgress("Progress tester", "Detail", total, debugger)

for i in range(1, total):
progress.Increment(1, f"Step {i}")
time.sleep(cmd_options.seconds)


def __lldb_init_module(debugger, dict):
# Register all classes that have a register_lldb_command method
for _name, cls in inspect.getmembers(sys.modules[__name__]):
if inspect.isclass(cls) and callable(
getattr(cls, "register_lldb_command", None)
):
cls.register_lldb_command(debugger, __name__)
49 changes: 49 additions & 0 deletions lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Test lldb-dap output events
"""

from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
import os
import time

import lldbdap_testcase


class TestDAP_progress(lldbdap_testcase.DAPTestCaseBase):
@skipIfWindows
def test_output(self):
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
progress_emitter = os.path.join(os.getcwd(), "Progress_emitter.py")
print(f"Progress emitter path: {progress_emitter}")
source = "main.cpp"
# Set breakpoint in the thread function so we can step the threads
breakpoint_ids = self.set_source_breakpoints(
source, [line_number(source, "// break here")]
)
self.continue_to_breakpoints(breakpoint_ids)
self.dap_server.request_evaluate(
f"`command script import {progress_emitter}", context="repl"
)
self.dap_server.request_evaluate(
"`test-progress --total 3 --seconds 1", context="repl"
)

self.dap_server.wait_for_event("progressEnd", 15)
# Expect at least a start, an update, and end event
# However because the underlying Progress instance is an RAII object and we can't guaruntee
# it's deterministic destruction in the python API, we verify just start and update
# otherwise this test could be flakey.
self.assertTrue(len(self.dap_server.progress_events) > 0)
start_found = False
update_found = False
for event in self.dap_server.progress_events:
event_type = event["event"]
if "progressStart" in event_type:
start_found = True
if "progressUpdate" in event_type:
update_found = True

self.assertTrue(start_found)
self.assertTrue(update_found)
5 changes: 5 additions & 0 deletions lldb/test/API/tools/lldb-dap/progress/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
int main() {
char *ptr = "unused";
// break here
return 0;
}
11 changes: 7 additions & 4 deletions lldb/tools/lldb-dap/ProgressEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ json::Value ProgressEvent::ToJSON() const {
body.try_emplace("cancellable", false);
}

if (m_event_type == progressUpdate)
EmplaceSafeString(body, "message", m_message);

std::string timestamp(llvm::formatv("{0:f9}", m_creation_time.count()));
EmplaceSafeString(body, "timestamp", timestamp);

Expand Down Expand Up @@ -164,10 +167,10 @@ const ProgressEvent &ProgressEventManager::GetMostRecentEvent() const {
return m_last_update_event ? *m_last_update_event : m_start_event;
}

void ProgressEventManager::Update(uint64_t progress_id, uint64_t completed,
uint64_t total) {
void ProgressEventManager::Update(uint64_t progress_id, llvm::StringRef message,
uint64_t completed, uint64_t total) {
if (std::optional<ProgressEvent> event = ProgressEvent::Create(
progress_id, std::nullopt, completed, total, &GetMostRecentEvent())) {
progress_id, message, completed, total, &GetMostRecentEvent())) {
if (event->GetEventType() == progressEnd)
m_finished = true;

Expand Down Expand Up @@ -227,7 +230,7 @@ void ProgressEventReporter::Push(uint64_t progress_id, const char *message,
m_unreported_start_events.push(event_manager);
}
} else {
it->second->Update(progress_id, completed, total);
it->second->Update(progress_id, StringRef(message), completed, total);
if (it->second->Finished())
m_event_managers.erase(it);
}
Expand Down
3 changes: 2 additions & 1 deletion lldb/tools/lldb-dap/ProgressEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ class ProgressEventManager {

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

/// \return
/// \b true if a \a progressEnd event has been notified. There's no
Expand Down
Loading