Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
85 changes: 85 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,85 @@
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 = "Jacob Lalonde's 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)

# This actually should start at 1 but it's 6:30 on a Friday...
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 progress is an RAII object and we can't guaruntee
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean "progress is not a RAII object"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did, but I believe because we'll free that memory when the python object is destroyed we can't depend on the deterministic behavior of the RAII object.

# 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