Skip to content

[lldb-dap] Use protocol types for modules request and events. #146966

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,8 @@ def _read_packet_thread(self):
finally:
dump_dap_log(self.log_file)

def get_modules(self):
module_list = self.request_modules()["body"]["modules"]
def get_modules(self, startModule: int = 0, moduleCount: int = 0):
module_list = self.request_modules(startModule, moduleCount)["body"]["modules"]
modules = {}
for module in module_list:
modules[module["name"]] = module
Expand Down Expand Up @@ -1143,8 +1143,14 @@ def request_completions(self, text, frameId=None):
}
return self.send_recv(command_dict)

def request_modules(self):
return self.send_recv({"command": "modules", "type": "request"})
def request_modules(self, startModule: int, moduleCount: int):
return self.send_recv(
{
"command": "modules",
"type": "request",
"arguments": {"startModule": startModule, "moduleCount": moduleCount},
}
)

def request_stackTrace(
self, threadId=None, startFrame=None, levels=None, format=None, dump=False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_module_event(self):

# Make sure we got a module event for libother.
event = self.dap_server.wait_for_event("module", 5)
self.assertTrue(event, "didn't get a module event")
self.assertIsNotNone(event, "didn't get a module event")
module_name = event["body"]["module"]["name"]
module_id = event["body"]["module"]["id"]
self.assertEqual(event["body"]["reason"], "new")
Expand All @@ -43,13 +43,20 @@ def test_module_event(self):

# Make sure we got a module event for libother.
event = self.dap_server.wait_for_event("module", 5)
self.assertTrue(event, "didn't get a module event")
self.assertIsNotNone(event, "didn't get a module event")
reason = event["body"]["reason"]
self.assertEqual(event["body"]["reason"], "removed")
self.assertEqual(reason, "removed")
self.assertEqual(event["body"]["module"]["id"], module_id)

# The removed module event should omit everything but the module id.
# Check that there's no module name in the event.
self.assertNotIn("name", event["body"]["module"])
# The removed module event should omit everything but the module id and name
# as they are required fields.
module_data = event["body"]["module"]
required_keys = ["id", "name"]
self.assertListEqual(list(module_data.keys()), required_keys)
self.assertEqual(module_data["name"], "", "expects empty name.")

# Make sure we do not send another event
event = self.dap_server.wait_for_event("module", 3)
self.assertIsNone(event, "expects no events.")

self.continue_to_exit()
2 changes: 1 addition & 1 deletion lldb/test/API/tools/lldb-dap/module-event/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ int main(int argc, char const *argv[]) {
dlclose(handle);
printf("after dlclose\n"); // breakpoint 3

return 0; // breakpoint 1
return 0;
}
32 changes: 26 additions & 6 deletions lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,20 @@ def run_test(self, symbol_basename, expect_debug_info_size):
context="repl",
)

def checkSymbolsLoadedWithSize():
def check_symbols_loaded_with_size():
active_modules = self.dap_server.get_modules()
program_module = active_modules[program_basename]
self.assertIn("symbolFilePath", program_module)
self.assertIn(symbols_path, program_module["symbolFilePath"])
symbol_regex = re.compile(r"[0-9]+(\.[0-9]*)?[KMG]?B")
return symbol_regex.match(program_module["symbolStatus"])
size_regex = re.compile(r"[0-9]+(\.[0-9]*)?[KMG]?B")
return size_regex.match(program_module["debugInfoSize"])

if expect_debug_info_size:
self.waitUntil(checkSymbolsLoadedWithSize)
self.assertTrue(
self.waitUntil(check_symbols_loaded_with_size),
"expect has debug info size",
)

active_modules = self.dap_server.get_modules()
program_module = active_modules[program_basename]
self.assertEqual(program_basename, program_module["name"])
Expand Down Expand Up @@ -84,6 +88,20 @@ def checkSymbolsLoadedWithSize():
self.assertNotEqual(len(module_changed_names), 0)
self.assertIn(program_module["name"], module_changed_names)

# fetch modules from offset
if len(active_modules.keys()) > 2:
resp = self.dap_server.request_modules(startModule=1, moduleCount=1)
self.assertTrue(resp["success"], f"expects a successful response {resp!r}")
resp_total_modules = resp["body"]["totalModules"]
self.assertEqual(resp_total_modules, len(active_modules))
resp_modules = resp["body"]["modules"]
self.assertEqual(len(resp_modules), 1, "expects only one module")

module_name = resp_modules[0]["name"]
self.assertIn(module_name, active_modules.keys())

self.continue_to_exit()

@skipIfWindows
def test_modules(self):
"""
Expand Down Expand Up @@ -119,8 +137,10 @@ def test_compile_units(self):
lines = [breakpoint1_line]
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.continue_to_breakpoints(breakpoint_ids)
moduleId = self.dap_server.get_modules()["a.out"]["id"]
response = self.dap_server.request_compileUnits(moduleId)
module_id = self.dap_server.get_modules()["a.out"]["id"]
response = self.dap_server.request_compileUnits(module_id)
self.assertTrue(response["body"])
cu_paths = [cu["compileUnitPath"] for cu in response["body"]["compileUnits"]]
self.assertIn(main_source_path, cu_paths, "Real path to main.cpp matches")

self.continue_to_exit()
40 changes: 1 addition & 39 deletions lldb/tools/lldb-dap/DAP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1347,45 +1347,7 @@ void DAP::EventThread() {
SendStdOutStdErr(*this, process);
}
} else if (lldb::SBTarget::EventIsTargetEvent(event)) {
if (event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded ||
event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded ||
event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded ||
event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) {
const uint32_t num_modules =
lldb::SBTarget::GetNumModulesFromEvent(event);
std::lock_guard<std::mutex> guard(modules_mutex);
for (uint32_t i = 0; i < num_modules; ++i) {
lldb::SBModule module =
lldb::SBTarget::GetModuleAtIndexFromEvent(i, event);
if (!module.IsValid())
continue;
llvm::StringRef module_id = module.GetUUIDString();
if (module_id.empty())
continue;

llvm::StringRef reason;
bool id_only = false;
if (modules.contains(module_id)) {
if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) {
modules.erase(module_id);
reason = "removed";
id_only = true;
} else {
reason = "changed";
}
} else {
modules.insert(module_id);
reason = "new";
}

llvm::json::Object body;
body.try_emplace("reason", reason);
body.try_emplace("module", CreateModule(target, module, id_only));
llvm::json::Object module_event = CreateEventObject("module");
module_event.try_emplace("body", std::move(body));
SendJSON(llvm::json::Value(std::move(module_event)));
}
}
HandleTargetEvent(*this, event);
Copy link
Member

Choose a reason for hiding this comment

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

This change seems orthogonal to the protocol types change and should probably be its own (small) PR.

} else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
auto event_type =
Expand Down
46 changes: 46 additions & 0 deletions lldb/tools/lldb-dap/EventHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "LLDBUtils.h"
#include "Protocol/ProtocolEvents.h"
#include "Protocol/ProtocolTypes.h"
#include "ProtocolUtils.h"
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBFileSpec.h"
#include "llvm/Support/Error.h"

Expand Down Expand Up @@ -269,4 +271,48 @@ void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) {
dap.SendJSON(llvm::json::Value(std::move(event)));
}

void HandleTargetEvent(DAP &dap, const lldb::SBEvent &event) {
const lldb::SBTarget &target = dap.target;
const uint32_t event_mask = event.GetType();
if (!(event_mask & lldb::SBTarget::eBroadcastBitModulesLoaded) &&
!(event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) &&
!(event_mask & lldb::SBTarget::eBroadcastBitSymbolsLoaded) &&
!(event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged))
return;

const uint32_t num_modules = lldb::SBTarget::GetNumModulesFromEvent(event);
std::lock_guard<std::mutex> guard(dap.modules_mutex);

for (uint32_t i = 0; i < num_modules; ++i) {
lldb::SBModule module = lldb::SBTarget::GetModuleAtIndexFromEvent(i, event);

const bool remove_module =
event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded;
auto p_module = lldb_dap::CreateModule(target, module, remove_module);
if (!p_module)
continue;

const llvm::StringRef module_id = p_module->id;
if (dap.modules.contains(module_id)) {
if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) {
dap.modules.erase(module_id);
dap.Send(protocol::Event{
"module", protocol::ModuleEventBody{
std::move(p_module).value(),
protocol::ModuleEventBody::eReasonRemoved}});
} else {
dap.Send(protocol::Event{
"module", protocol::ModuleEventBody{
std::move(p_module).value(),
protocol::ModuleEventBody::eReasonChanged}});
}
} else if (!remove_module) {
dap.modules.insert(module_id);
dap.Send(protocol::Event{
"module",
protocol::ModuleEventBody{std::move(p_module).value(),
protocol::ModuleEventBody::eReasonNew}});
}
}
}
} // namespace lldb_dap
2 changes: 2 additions & 0 deletions lldb/tools/lldb-dap/EventHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ void SendContinuedEvent(DAP &dap);

void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process);

void HandleTargetEvent(DAP &dap, const lldb::SBEvent &event);

} // namespace lldb_dap

#endif
81 changes: 30 additions & 51 deletions lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,64 +7,43 @@
//===----------------------------------------------------------------------===//

#include "DAP.h"
#include "EventHelper.h"
#include "JSONUtils.h"
#include "ProtocolUtils.h"
#include "RequestHandler.h"

using namespace lldb_dap::protocol;
namespace lldb_dap {

// "modulesRequest": {
// "allOf": [ { "$ref": "#/definitions/Request" }, {
// "type": "object",
// "description": "Modules request; value of command field is
// 'modules'.",
// "properties": {
// "command": {
// "type": "string",
// "enum": [ "modules" ]
// },
// },
// "required": [ "command" ]
// }]
// },
// "modulesResponse": {
// "allOf": [ { "$ref": "#/definitions/Response" }, {
// "type": "object",
// "description": "Response to 'modules' request.",
// "properties": {
// "body": {
// "description": "Response to 'modules' request. Array of
// module objects."
// }
// }
// }]
// }
void ModulesRequestHandler::operator()(
const llvm::json::Object &request) const {
llvm::json::Object response;
FillResponse(request, response);

llvm::json::Array modules;

{
std::lock_guard<std::mutex> guard(dap.modules_mutex);
for (size_t i = 0; i < dap.target.GetNumModules(); i++) {
lldb::SBModule module = dap.target.GetModuleAtIndex(i);
if (!module.IsValid())
continue;

llvm::StringRef module_id = module.GetUUIDString();
if (!module_id.empty())
dap.modules.insert(module_id);

modules.emplace_back(CreateModule(dap.target, module));
/// Modules can be retrieved from the debug adapter with this request which can
/// either return all modules or a range of modules to support paging.
///
/// Clients should only call this request if the corresponding capability
/// `supportsModulesRequest` is true.
Comment on lines +19 to +20
Copy link
Member

Choose a reason for hiding this comment

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

Support for paging is another functional change that's orthogonal to switching over to the protocol types.

llvm::Expected<ModulesResponseBody>
ModulesRequestHandler::Run(const std::optional<ModulesArguments> &args) const {
ModulesResponseBody response;
const auto [start_module, module_count] = args.value_or({});

std::vector<Module> &modules = response.modules;
std::lock_guard<std::mutex> guard(dap.modules_mutex);
const uint32_t total_modules = dap.target.GetNumModules();
const uint32_t max_num_module =
module_count == 0 ? total_modules
: std::min(start_module + module_count, total_modules);
modules.reserve(max_num_module);

response.totalModules = total_modules;

for (uint32_t i = start_module; i < max_num_module; i++) {
lldb::SBModule module = dap.target.GetModuleAtIndex(i);

std::optional<Module> result = CreateModule(dap.target, module);
if (result && !result->id.empty()) {
dap.modules.insert(result->id);
modules.emplace_back(std::move(result).value());
}
}

llvm::json::Object body;
body.try_emplace("modules", std::move(modules));
response.try_emplace("body", std::move(body));
dap.SendJSON(llvm::json::Value(std::move(response)));
return response;
}

} // namespace lldb_dap
9 changes: 6 additions & 3 deletions lldb/tools/lldb-dap/Handler/RequestHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,14 +466,17 @@ class CompileUnitsRequestHandler : public LegacyRequestHandler {
void operator()(const llvm::json::Object &request) const override;
};

class ModulesRequestHandler : public LegacyRequestHandler {
class ModulesRequestHandler final
: public RequestHandler<std::optional<protocol::ModulesArguments>,
llvm::Expected<protocol::ModulesResponseBody>> {
public:
using LegacyRequestHandler::LegacyRequestHandler;
using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "modules"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureModulesRequest};
}
void operator()(const llvm::json::Object &request) const override;
llvm::Expected<protocol::ModulesResponseBody>
Run(const std::optional<protocol::ModulesArguments> &args) const override;
};

class PauseRequestHandler : public LegacyRequestHandler {
Expand Down
Loading
Loading