Skip to content

Commit 03a2723

Browse files
authored
[lldb-dap] Use protocol types for modules request and events. (#146966)
Update tests to fix silently failing test and handle when a module is removed.
1 parent d0a4af7 commit 03a2723

File tree

20 files changed

+476
-215
lines changed

20 files changed

+476
-215
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ def _read_packet_thread(self):
199199
finally:
200200
dump_dap_log(self.log_file)
201201

202-
def get_modules(self):
203-
module_list = self.request_modules()["body"]["modules"]
202+
def get_modules(self, startModule: int = 0, moduleCount: int = 0):
203+
module_list = self.request_modules(startModule, moduleCount)["body"]["modules"]
204204
modules = {}
205205
for module in module_list:
206206
modules[module["name"]] = module
@@ -1143,8 +1143,14 @@ def request_completions(self, text, frameId=None):
11431143
}
11441144
return self.send_recv(command_dict)
11451145

1146-
def request_modules(self):
1147-
return self.send_recv({"command": "modules", "type": "request"})
1146+
def request_modules(self, startModule: int, moduleCount: int):
1147+
return self.send_recv(
1148+
{
1149+
"command": "modules",
1150+
"type": "request",
1151+
"arguments": {"startModule": startModule, "moduleCount": moduleCount},
1152+
}
1153+
)
11481154

11491155
def request_stackTrace(
11501156
self, threadId=None, startFrame=None, levels=None, format=None, dump=False

lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def test_module_event(self):
3232

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

4444
# Make sure we got a module event for libother.
4545
event = self.dap_server.wait_for_event("module", 5)
46-
self.assertTrue(event, "didn't get a module event")
46+
self.assertIsNotNone(event, "didn't get a module event")
4747
reason = event["body"]["reason"]
48-
self.assertEqual(event["body"]["reason"], "removed")
48+
self.assertEqual(reason, "removed")
4949
self.assertEqual(event["body"]["module"]["id"], module_id)
5050

51-
# The removed module event should omit everything but the module id.
52-
# Check that there's no module name in the event.
53-
self.assertNotIn("name", event["body"]["module"])
51+
# The removed module event should omit everything but the module id and name
52+
# as they are required fields.
53+
module_data = event["body"]["module"]
54+
required_keys = ["id", "name"]
55+
self.assertListEqual(list(module_data.keys()), required_keys)
56+
self.assertEqual(module_data["name"], "", "expects empty name.")
57+
58+
# Make sure we do not send another event
59+
event = self.dap_server.wait_for_event("module", 3)
60+
self.assertIsNone(event, "expects no events.")
5461

5562
self.continue_to_exit()

lldb/test/API/tools/lldb-dap/module/TestDAP_module.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,20 @@ def run_test(self, symbol_basename, expect_debug_info_size):
4545
context="repl",
4646
)
4747

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

5656
if expect_debug_info_size:
57-
self.waitUntil(checkSymbolsLoadedWithSize)
57+
self.assertTrue(
58+
self.waitUntil(check_symbols_loaded_with_size),
59+
"expect has debug info size",
60+
)
61+
5862
active_modules = self.dap_server.get_modules()
5963
program_module = active_modules[program_basename]
6064
self.assertEqual(program_basename, program_module["name"])
@@ -83,6 +87,7 @@ def checkSymbolsLoadedWithSize():
8387
# symbols got added.
8488
self.assertNotEqual(len(module_changed_names), 0)
8589
self.assertIn(program_module["name"], module_changed_names)
90+
self.continue_to_exit()
8691

8792
@skipIfWindows
8893
def test_modules(self):
@@ -124,3 +129,5 @@ def test_compile_units(self):
124129
self.assertTrue(response["body"])
125130
cu_paths = [cu["compileUnitPath"] for cu in response["body"]["compileUnits"]]
126131
self.assertIn(main_source_path, cu_paths, "Real path to main.cpp matches")
132+
133+
self.continue_to_exit()

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "LLDBUtils.h"
1717
#include "OutputRedirector.h"
1818
#include "Protocol/ProtocolBase.h"
19+
#include "Protocol/ProtocolEvents.h"
1920
#include "Protocol/ProtocolRequests.h"
2021
#include "Protocol/ProtocolTypes.h"
2122
#include "ProtocolUtils.h"
@@ -1353,37 +1354,37 @@ void DAP::EventThread() {
13531354
event_mask & lldb::SBTarget::eBroadcastBitSymbolsChanged) {
13541355
const uint32_t num_modules =
13551356
lldb::SBTarget::GetNumModulesFromEvent(event);
1357+
const bool remove_module =
1358+
event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded;
1359+
13561360
std::lock_guard<std::mutex> guard(modules_mutex);
13571361
for (uint32_t i = 0; i < num_modules; ++i) {
13581362
lldb::SBModule module =
13591363
lldb::SBTarget::GetModuleAtIndexFromEvent(i, event);
1360-
if (!module.IsValid())
1361-
continue;
1362-
llvm::StringRef module_id = module.GetUUIDString();
1363-
if (module_id.empty())
1364+
1365+
std::optional<protocol::Module> p_module =
1366+
CreateModule(target, module, remove_module);
1367+
if (!p_module)
13641368
continue;
13651369

1366-
llvm::StringRef reason;
1367-
bool id_only = false;
1368-
if (modules.contains(module_id)) {
1369-
if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) {
1370-
modules.erase(module_id);
1371-
reason = "removed";
1372-
id_only = true;
1373-
} else {
1374-
reason = "changed";
1375-
}
1376-
} else {
1370+
llvm::StringRef module_id = p_module->id;
1371+
1372+
const bool module_exists = modules.contains(module_id);
1373+
if (remove_module && module_exists) {
1374+
modules.erase(module_id);
1375+
Send(protocol::Event{
1376+
"module", ModuleEventBody{std::move(p_module).value(),
1377+
ModuleEventBody::eReasonRemoved}});
1378+
} else if (module_exists) {
1379+
Send(protocol::Event{
1380+
"module", ModuleEventBody{std::move(p_module).value(),
1381+
ModuleEventBody::eReasonChanged}});
1382+
} else if (!remove_module) {
13771383
modules.insert(module_id);
1378-
reason = "new";
1384+
Send(protocol::Event{
1385+
"module", ModuleEventBody{std::move(p_module).value(),
1386+
ModuleEventBody::eReasonNew}});
13791387
}
1380-
1381-
llvm::json::Object body;
1382-
body.try_emplace("reason", reason);
1383-
body.try_emplace("module", CreateModule(target, module, id_only));
1384-
llvm::json::Object module_event = CreateEventObject("module");
1385-
module_event.try_emplace("body", std::move(body));
1386-
SendJSON(llvm::json::Value(std::move(module_event)));
13871388
}
13881389
}
13891390
} else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {

lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp

Lines changed: 25 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,64 +7,38 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "DAP.h"
10-
#include "EventHelper.h"
11-
#include "JSONUtils.h"
10+
#include "ProtocolUtils.h"
1211
#include "RequestHandler.h"
1312

13+
using namespace lldb_dap::protocol;
1414
namespace lldb_dap {
1515

16-
// "modulesRequest": {
17-
// "allOf": [ { "$ref": "#/definitions/Request" }, {
18-
// "type": "object",
19-
// "description": "Modules request; value of command field is
20-
// 'modules'.",
21-
// "properties": {
22-
// "command": {
23-
// "type": "string",
24-
// "enum": [ "modules" ]
25-
// },
26-
// },
27-
// "required": [ "command" ]
28-
// }]
29-
// },
30-
// "modulesResponse": {
31-
// "allOf": [ { "$ref": "#/definitions/Response" }, {
32-
// "type": "object",
33-
// "description": "Response to 'modules' request.",
34-
// "properties": {
35-
// "body": {
36-
// "description": "Response to 'modules' request. Array of
37-
// module objects."
38-
// }
39-
// }
40-
// }]
41-
// }
42-
void ModulesRequestHandler::operator()(
43-
const llvm::json::Object &request) const {
44-
llvm::json::Object response;
45-
FillResponse(request, response);
46-
47-
llvm::json::Array modules;
48-
49-
{
50-
std::lock_guard<std::mutex> guard(dap.modules_mutex);
51-
for (size_t i = 0; i < dap.target.GetNumModules(); i++) {
52-
lldb::SBModule module = dap.target.GetModuleAtIndex(i);
53-
if (!module.IsValid())
54-
continue;
55-
56-
llvm::StringRef module_id = module.GetUUIDString();
57-
if (!module_id.empty())
58-
dap.modules.insert(module_id);
59-
60-
modules.emplace_back(CreateModule(dap.target, module));
16+
/// Modules can be retrieved from the debug adapter with this request which can
17+
/// either return all modules or a range of modules to support paging.
18+
///
19+
/// Clients should only call this request if the corresponding capability
20+
/// `supportsModulesRequest` is true.
21+
llvm::Expected<ModulesResponseBody>
22+
ModulesRequestHandler::Run(const std::optional<ModulesArguments> &args) const {
23+
ModulesResponseBody response;
24+
25+
std::vector<Module> &modules = response.modules;
26+
std::lock_guard<std::mutex> guard(dap.modules_mutex);
27+
const uint32_t total_modules = dap.target.GetNumModules();
28+
response.totalModules = total_modules;
29+
30+
modules.reserve(total_modules);
31+
for (uint32_t i = 0; i < total_modules; i++) {
32+
lldb::SBModule module = dap.target.GetModuleAtIndex(i);
33+
34+
std::optional<Module> result = CreateModule(dap.target, module);
35+
if (result && !result->id.empty()) {
36+
dap.modules.insert(result->id);
37+
modules.emplace_back(std::move(result).value());
6138
}
6239
}
6340

64-
llvm::json::Object body;
65-
body.try_emplace("modules", std::move(modules));
66-
response.try_emplace("body", std::move(body));
67-
dap.SendJSON(llvm::json::Value(std::move(response)));
41+
return response;
6842
}
6943

7044
} // namespace lldb_dap

lldb/tools/lldb-dap/Handler/RequestHandler.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,14 +466,17 @@ class CompileUnitsRequestHandler : public LegacyRequestHandler {
466466
void operator()(const llvm::json::Object &request) const override;
467467
};
468468

469-
class ModulesRequestHandler : public LegacyRequestHandler {
469+
class ModulesRequestHandler final
470+
: public RequestHandler<std::optional<protocol::ModulesArguments>,
471+
llvm::Expected<protocol::ModulesResponseBody>> {
470472
public:
471-
using LegacyRequestHandler::LegacyRequestHandler;
473+
using RequestHandler::RequestHandler;
472474
static llvm::StringLiteral GetCommand() { return "modules"; }
473475
FeatureSet GetSupportedFeatures() const override {
474476
return {protocol::eAdapterFeatureModulesRequest};
475477
}
476-
void operator()(const llvm::json::Object &request) const override;
478+
llvm::Expected<protocol::ModulesResponseBody>
479+
Run(const std::optional<protocol::ModulesArguments> &args) const override;
477480
};
478481

479482
class PauseRequestHandler : public LegacyRequestHandler {

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 0 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -341,101 +341,6 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
341341
return llvm::json::Value(std::move(object));
342342
}
343343

344-
static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
345-
uint64_t debug_info_size = 0;
346-
llvm::StringRef section_name(section.GetName());
347-
if (section_name.starts_with(".debug") ||
348-
section_name.starts_with("__debug") ||
349-
section_name.starts_with(".apple") || section_name.starts_with("__apple"))
350-
debug_info_size += section.GetFileByteSize();
351-
size_t num_sub_sections = section.GetNumSubSections();
352-
for (size_t i = 0; i < num_sub_sections; i++) {
353-
debug_info_size +=
354-
GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
355-
}
356-
return debug_info_size;
357-
}
358-
359-
static uint64_t GetDebugInfoSize(lldb::SBModule module) {
360-
uint64_t debug_info_size = 0;
361-
size_t num_sections = module.GetNumSections();
362-
for (size_t i = 0; i < num_sections; i++) {
363-
debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
364-
}
365-
return debug_info_size;
366-
}
367-
368-
static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
369-
std::ostringstream oss;
370-
oss << std::fixed << std::setprecision(1);
371-
if (debug_info < 1024) {
372-
oss << debug_info << "B";
373-
} else if (debug_info < 1024 * 1024) {
374-
double kb = double(debug_info) / 1024.0;
375-
oss << kb << "KB";
376-
} else if (debug_info < 1024 * 1024 * 1024) {
377-
double mb = double(debug_info) / (1024.0 * 1024.0);
378-
oss << mb << "MB";
379-
} else {
380-
double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
381-
oss << gb << "GB";
382-
}
383-
return oss.str();
384-
}
385-
386-
llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module,
387-
bool id_only) {
388-
llvm::json::Object object;
389-
if (!target.IsValid() || !module.IsValid())
390-
return llvm::json::Value(std::move(object));
391-
392-
const char *uuid = module.GetUUIDString();
393-
object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
394-
395-
if (id_only)
396-
return llvm::json::Value(std::move(object));
397-
398-
object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
399-
char module_path_arr[PATH_MAX];
400-
module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
401-
std::string module_path(module_path_arr);
402-
object.try_emplace("path", module_path);
403-
if (module.GetNumCompileUnits() > 0) {
404-
std::string symbol_str = "Symbols loaded.";
405-
std::string debug_info_size;
406-
uint64_t debug_info = GetDebugInfoSize(module);
407-
if (debug_info > 0) {
408-
debug_info_size = ConvertDebugInfoSizeToString(debug_info);
409-
}
410-
object.try_emplace("symbolStatus", symbol_str);
411-
object.try_emplace("debugInfoSize", debug_info_size);
412-
char symbol_path_arr[PATH_MAX];
413-
module.GetSymbolFileSpec().GetPath(symbol_path_arr,
414-
sizeof(symbol_path_arr));
415-
std::string symbol_path(symbol_path_arr);
416-
object.try_emplace("symbolFilePath", symbol_path);
417-
} else {
418-
object.try_emplace("symbolStatus", "Symbols not found.");
419-
}
420-
std::string load_address =
421-
llvm::formatv("{0:x}",
422-
module.GetObjectFileHeaderAddress().GetLoadAddress(target))
423-
.str();
424-
object.try_emplace("addressRange", load_address);
425-
std::string version_str;
426-
uint32_t version_nums[3];
427-
uint32_t num_versions =
428-
module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
429-
for (uint32_t i = 0; i < num_versions; ++i) {
430-
if (!version_str.empty())
431-
version_str += ".";
432-
version_str += std::to_string(version_nums[i]);
433-
}
434-
if (!version_str.empty())
435-
object.try_emplace("version", version_str);
436-
return llvm::json::Value(std::move(object));
437-
}
438-
439344
// "Event": {
440345
// "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
441346
// "type": "object",

0 commit comments

Comments
 (0)