[Lldb-commits] [lldb] [lldb-dap] Use protocol types for modules request and events. (PR #146966)
Ebuka Ezike via lldb-commits
lldb-commits at lists.llvm.org
Thu Jul 3 15:19:35 PDT 2025
https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/146966
>From 8ece8558ed3caebf6a58772d9aa81d7f82a5b564 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Sun, 22 Jun 2025 08:28:54 +0100
Subject: [PATCH 1/5] [lldb-dap] Use protocol-types for modules request
---
.../Handler/ModulesRequestHandler.cpp | 81 +++++---------
lldb/tools/lldb-dap/Handler/RequestHandler.h | 9 +-
lldb/tools/lldb-dap/JSONUtils.cpp | 95 ----------------
lldb/tools/lldb-dap/JSONUtils.h | 18 ---
.../lldb-dap/Protocol/ProtocolRequests.cpp | 14 +++
.../lldb-dap/Protocol/ProtocolRequests.h | 21 ++++
.../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 25 +++++
lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 42 +++++++
lldb/tools/lldb-dap/ProtocolUtils.cpp | 103 ++++++++++++++++++
lldb/tools/lldb-dap/ProtocolUtils.h | 20 ++++
10 files changed, 261 insertions(+), 167 deletions(-)
diff --git a/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp
index d37f302b06958..e8440f591d2b2 100644
--- a/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp
@@ -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.
+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
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index e35b9830ab60f..3b910abe81992 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -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 {
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index e72d93ee34571..553c52605c998 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -341,101 +341,6 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
return llvm::json::Value(std::move(object));
}
-static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
- uint64_t debug_info_size = 0;
- llvm::StringRef section_name(section.GetName());
- if (section_name.starts_with(".debug") ||
- section_name.starts_with("__debug") ||
- section_name.starts_with(".apple") || section_name.starts_with("__apple"))
- debug_info_size += section.GetFileByteSize();
- size_t num_sub_sections = section.GetNumSubSections();
- for (size_t i = 0; i < num_sub_sections; i++) {
- debug_info_size +=
- GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
- }
- return debug_info_size;
-}
-
-static uint64_t GetDebugInfoSize(lldb::SBModule module) {
- uint64_t debug_info_size = 0;
- size_t num_sections = module.GetNumSections();
- for (size_t i = 0; i < num_sections; i++) {
- debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
- }
- return debug_info_size;
-}
-
-static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
- std::ostringstream oss;
- oss << std::fixed << std::setprecision(1);
- if (debug_info < 1024) {
- oss << debug_info << "B";
- } else if (debug_info < 1024 * 1024) {
- double kb = double(debug_info) / 1024.0;
- oss << kb << "KB";
- } else if (debug_info < 1024 * 1024 * 1024) {
- double mb = double(debug_info) / (1024.0 * 1024.0);
- oss << mb << "MB";
- } else {
- double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
- oss << gb << "GB";
- }
- return oss.str();
-}
-
-llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module,
- bool id_only) {
- llvm::json::Object object;
- if (!target.IsValid() || !module.IsValid())
- return llvm::json::Value(std::move(object));
-
- const char *uuid = module.GetUUIDString();
- object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
-
- if (id_only)
- return llvm::json::Value(std::move(object));
-
- object.try_emplace("name", std::string(module.GetFileSpec().GetFilename()));
- char module_path_arr[PATH_MAX];
- module.GetFileSpec().GetPath(module_path_arr, sizeof(module_path_arr));
- std::string module_path(module_path_arr);
- object.try_emplace("path", module_path);
- if (module.GetNumCompileUnits() > 0) {
- std::string symbol_str = "Symbols loaded.";
- std::string debug_info_size;
- uint64_t debug_info = GetDebugInfoSize(module);
- if (debug_info > 0) {
- debug_info_size = ConvertDebugInfoSizeToString(debug_info);
- }
- object.try_emplace("symbolStatus", symbol_str);
- object.try_emplace("debugInfoSize", debug_info_size);
- char symbol_path_arr[PATH_MAX];
- module.GetSymbolFileSpec().GetPath(symbol_path_arr,
- sizeof(symbol_path_arr));
- std::string symbol_path(symbol_path_arr);
- object.try_emplace("symbolFilePath", symbol_path);
- } else {
- object.try_emplace("symbolStatus", "Symbols not found.");
- }
- std::string load_address =
- llvm::formatv("{0:x}",
- module.GetObjectFileHeaderAddress().GetLoadAddress(target))
- .str();
- object.try_emplace("addressRange", load_address);
- std::string version_str;
- uint32_t version_nums[3];
- uint32_t num_versions =
- module.GetVersion(version_nums, sizeof(version_nums) / sizeof(uint32_t));
- for (uint32_t i = 0; i < num_versions; ++i) {
- if (!version_str.empty())
- version_str += ".";
- version_str += std::to_string(version_nums[i]);
- }
- if (!version_str.empty())
- object.try_emplace("version", version_str);
- return llvm::json::Value(std::move(object));
-}
-
// "Event": {
// "allOf": [ { "$ref": "#/definitions/ProtocolMessage" }, {
// "type": "object",
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index fd9a06931ebff..0424438ad5b72 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -196,24 +196,6 @@ GetStringMap(const llvm::json::Object &obj, llvm::StringRef key);
void FillResponse(const llvm::json::Object &request,
llvm::json::Object &response);
-/// Converts a LLDB module to a VS Code DAP module for use in "modules" events.
-///
-/// \param[in] target
-/// A LLDB target object to convert into a JSON value.
-///
-/// \param[in] module
-/// A LLDB module object to convert into a JSON value
-///
-/// \param[in] id_only
-/// Only include the module ID in the JSON value. This is used when sending
-/// a "removed" module event.
-///
-/// \return
-/// A "Module" JSON object with that follows the formal JSON
-/// definition outlined by Microsoft.
-llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module,
- bool id_only = false);
-
/// Create a "Event" JSON object using \a event_name as the event name
///
/// \param[in] event_name
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 9bd84a6c898f9..124dbace97dd6 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -517,4 +517,18 @@ json::Value toJSON(const ReadMemoryResponseBody &RMR) {
return result;
}
+bool fromJSON(const json::Value &Params, ModulesArguments &MA, json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.mapOptional("startModule", MA.startModule) &&
+ O.mapOptional("moduleCount", MA.moduleCount);
+}
+
+json::Value toJSON(const ModulesResponseBody &MR) {
+ json::Object result{{"modules", MR.modules}};
+ if (MR.totalModules != 0)
+ result.insert({"totalModules", MR.totalModules});
+
+ return result;
+}
+
} // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index d4b816c72679b..26eb3cb396ad1 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -875,6 +875,27 @@ struct ReadMemoryResponseBody {
};
llvm::json::Value toJSON(const ReadMemoryResponseBody &);
+/// Arguments for `modules` request.
+struct ModulesArguments {
+ /// The index of the first module to return; if omitted modules start at 0.
+ uint32_t startModule = 0;
+
+ /// The number of modules to return. If `moduleCount` is not specified or 0,
+ /// all modules are returned.
+ uint32_t moduleCount = 0;
+};
+bool fromJSON(const llvm::json::Value &, ModulesArguments &, llvm::json::Path);
+
+/// Response to `modules` request.
+struct ModulesResponseBody {
+ /// All modules or range of modules.
+ std::vector<Module> modules;
+
+ /// The total number of modules available.
+ uint32_t totalModules = 0;
+};
+llvm::json::Value toJSON(const ModulesResponseBody &);
+
} // namespace lldb_dap::protocol
#endif
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index f3635202175a7..ee30c9bbddf4a 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -926,4 +926,29 @@ llvm::json::Value toJSON(const DisassembledInstruction &DI) {
return result;
}
+json::Value toJSON(const Module &M) {
+ json::Object result{{"id", M.id}, {"name", M.name}};
+
+ if (!M.path.empty())
+ result.insert({"path", M.path});
+ if (M.isOptimized)
+ result.insert({"isOptimized", *M.isOptimized});
+ if (M.isUserCode)
+ result.insert({"isUserCode", *M.isUserCode});
+ if (!M.version.empty())
+ result.insert({"version", M.version});
+ if (!M.symbolStatus.empty())
+ result.insert({"symbolStatus", M.symbolStatus});
+ if (!M.symbolFilePath.empty())
+ result.insert({"symbolFilePath", M.symbolFilePath});
+ if (!M.dateTimeStamp.empty())
+ result.insert({"dateTimeStamp", M.dateTimeStamp});
+ if (!M.addressRange.empty())
+ result.insert({"addressRange", M.addressRange});
+ if (!M.debugInfoSize.empty())
+ result.insert({"debugInfoSize", M.debugInfoSize});
+
+ return result;
+}
+
} // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 6adfe3b7211b1..c653d9468a358 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -743,6 +743,48 @@ bool fromJSON(const llvm::json::Value &, DisassembledInstruction &,
llvm::json::Path);
llvm::json::Value toJSON(const DisassembledInstruction &);
+struct Module {
+ /// Unique identifier for the module.
+ std::string id;
+
+ /// A name of the module.
+ std::string name;
+
+ /// Logical full path to the module. The exact definition is implementation
+ /// defined, but usually this would be a full path to the on-disk file for the
+ /// module.
+ std::string path;
+
+ /// True if the module is optimized.
+ std::optional<bool> isOptimized;
+
+ /// True if the module is considered 'user code' by a debugger that supports
+ /// 'Just My Code'.
+ std::optional<bool> isUserCode;
+
+ /// Version of Module.
+ std::string version;
+
+ /// User-understandable description of if symbols were found for the module
+ /// (ex: 'Symbols Loaded', 'Symbols not found', etc.)
+ std::string symbolStatus;
+
+ /// Logical full path to the symbol file. The exact definition is
+ /// implementation defined.
+ std::string symbolFilePath;
+
+ /// Module created or modified, encoded as an RFC 3339 timestamp.
+ std::string dateTimeStamp;
+
+ /// Address range covered by this module.
+ std::string addressRange;
+
+ /* Custom fields */
+ /// Size of the debug_info sections in the module.
+ std::string debugInfoSize;
+};
+llvm::json::Value toJSON(const Module &);
+
} // namespace lldb_dap::protocol
#endif
diff --git a/lldb/tools/lldb-dap/ProtocolUtils.cpp b/lldb/tools/lldb-dap/ProtocolUtils.cpp
index 724d851107928..d4ba3d1b92424 100644
--- a/lldb/tools/lldb-dap/ProtocolUtils.cpp
+++ b/lldb/tools/lldb-dap/ProtocolUtils.cpp
@@ -16,6 +16,8 @@
#include "lldb/API/SBTarget.h"
#include "lldb/API/SBThread.h"
#include "lldb/Host/PosixApi.h" // Adds PATH_MAX for windows
+
+#include <iomanip>
#include <optional>
using namespace lldb_dap::protocol;
@@ -46,6 +48,107 @@ static bool ShouldDisplayAssemblySource(
return false;
}
+static uint64_t GetDebugInfoSizeInSection(lldb::SBSection section) {
+ uint64_t debug_info_size = 0;
+ const llvm::StringRef section_name(section.GetName());
+ if (section_name.starts_with(".debug") ||
+ section_name.starts_with("__debug") ||
+ section_name.starts_with(".apple") || section_name.starts_with("__apple"))
+ debug_info_size += section.GetFileByteSize();
+
+ const size_t num_sub_sections = section.GetNumSubSections();
+ for (size_t i = 0; i < num_sub_sections; i++) {
+ debug_info_size +=
+ GetDebugInfoSizeInSection(section.GetSubSectionAtIndex(i));
+ }
+ return debug_info_size;
+}
+
+static uint64_t GetDebugInfoSize(lldb::SBModule module) {
+ uint64_t debug_info_size = 0;
+ const size_t num_sections = module.GetNumSections();
+ for (size_t i = 0; i < num_sections; i++) {
+ debug_info_size += GetDebugInfoSizeInSection(module.GetSectionAtIndex(i));
+ }
+ return debug_info_size;
+}
+
+static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
+ std::ostringstream oss;
+ oss << std::fixed << std::setprecision(1);
+ if (debug_info < 1024) {
+ oss << debug_info << "B";
+ } else if (debug_info < static_cast<uint64_t>(1024 * 1024)) {
+ double kb = double(debug_info) / 1024.0;
+ oss << kb << "KB";
+ } else if (debug_info < 1024 * 1024 * 1024) {
+ double mb = double(debug_info) / (1024.0 * 1024.0);
+ oss << mb << "MB";
+ } else {
+ double gb = double(debug_info) / (1024.0 * 1024.0 * 1024.0);
+ oss << gb << "GB";
+ }
+ return oss.str();
+}
+
+std::optional<protocol::Module> CreateModule(const lldb::SBTarget &target,
+ lldb::SBModule &module,
+ bool id_only) {
+ if (!target.IsValid() || !module.IsValid())
+ return std::nullopt;
+
+ const llvm::StringRef uuid = module.GetUUIDString();
+ if (uuid.empty())
+ return std::nullopt;
+
+ protocol::Module p_module;
+ p_module.id = uuid;
+
+ if (id_only)
+ return p_module;
+
+ std::array<char, PATH_MAX> path_buffer{};
+ if (const lldb::SBFileSpec file_spec = module.GetFileSpec()) {
+ p_module.name = file_spec.GetFilename();
+
+ const uint32_t path_size =
+ file_spec.GetPath(path_buffer.data(), path_buffer.size());
+ p_module.path = std::string(path_buffer.data(), path_size);
+ }
+
+ if (const uint32_t num_compile_units = module.GetNumCompileUnits();
+ num_compile_units > 0) {
+ p_module.symbolStatus = "Symbols loaded.";
+
+ const uint64_t debug_info = GetDebugInfoSize(module);
+ p_module.debugInfoSize = ConvertDebugInfoSizeToString(debug_info);
+
+ if (const lldb::SBFileSpec symbol_fspec = module.GetSymbolFileSpec()) {
+ const uint32_t path_size =
+ symbol_fspec.GetPath(path_buffer.data(), path_buffer.size());
+ p_module.symbolFilePath = std::string(path_buffer.data(), path_size);
+ }
+ } else {
+ p_module.symbolStatus = "Symbols not found.";
+ }
+
+ const auto load_address = module.GetObjectFileHeaderAddress();
+ if (const lldb::addr_t raw_address = load_address.GetLoadAddress(target);
+ raw_address != LLDB_INVALID_ADDRESS)
+ p_module.addressRange = llvm::formatv("{0:x}", raw_address);
+
+ std::array<uint32_t, 3> version_nums{};
+ const uint32_t num_versions =
+ module.GetVersion(version_nums.data(), version_nums.size());
+ if (num_versions > 0) {
+ p_module.version = llvm::formatv(
+ "{:$[.]}", llvm::make_range(version_nums.begin(),
+ version_nums.begin() + num_versions));
+ }
+
+ return p_module;
+}
+
std::optional<protocol::Source> CreateSource(const lldb::SBFileSpec &file) {
if (!file.IsValid())
return std::nullopt;
diff --git a/lldb/tools/lldb-dap/ProtocolUtils.h b/lldb/tools/lldb-dap/ProtocolUtils.h
index f36bf0fb60a87..e9a104a8f7b4f 100644
--- a/lldb/tools/lldb-dap/ProtocolUtils.h
+++ b/lldb/tools/lldb-dap/ProtocolUtils.h
@@ -20,6 +20,26 @@
namespace lldb_dap {
+/// Converts a LLDB module to a DAP protocol module for use in `module events or
+/// request.
+///
+/// \param[in] target
+/// The target that has the module
+///
+/// \param[in] module
+/// A LLDB module object to convert into a protocol module
+///
+/// \param[in] id_only
+/// Only initialize the module ID in the return type. This is used when
+/// sending a "removed" module event.
+///
+/// \return
+/// A `protocol::Module` that follows the formal Module
+/// definition outlined by the DAP protocol.
+std::optional<protocol::Module> CreateModule(const lldb::SBTarget &target,
+ lldb::SBModule &module,
+ bool id_only = false);
+
/// Create a "Source" JSON object as described in the debug adapter definition.
///
/// \param[in] file
>From 6f030c171d711647a5f021b32bec6b52156be8a7 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Sun, 22 Jun 2025 13:29:34 +0100
Subject: [PATCH 2/5] [lldb-dap] update events
---
.../test/tools/lldb-dap/dap_server.py | 14 +++++--
.../module-event/TestDAP_module_event.py | 19 +++++++---
.../API/tools/lldb-dap/module-event/main.cpp | 2 +-
.../tools/lldb-dap/module/TestDAP_module.py | 31 ++++++++++++---
lldb/tools/lldb-dap/DAP.cpp | 38 ++++++++++---------
.../lldb-dap/Protocol/ProtocolEvents.cpp | 16 ++++++++
lldb/tools/lldb-dap/Protocol/ProtocolEvents.h | 15 ++++++++
7 files changed, 100 insertions(+), 35 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 0fe36cd4bc71f..d227a66a703c1 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -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
@@ -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
diff --git a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py
index 1ef2f2a8235a4..64ed4154b035d 100644
--- a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py
+++ b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py
@@ -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")
@@ -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()
diff --git a/lldb/test/API/tools/lldb-dap/module-event/main.cpp b/lldb/test/API/tools/lldb-dap/module-event/main.cpp
index 711471b9fadd0..283b2f8214b4a 100644
--- a/lldb/test/API/tools/lldb-dap/module-event/main.cpp
+++ b/lldb/test/API/tools/lldb-dap/module-event/main.cpp
@@ -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;
}
diff --git a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
index 4fc221668a8ee..041270daf7d48 100644
--- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
+++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
@@ -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"])
@@ -83,6 +87,19 @@ def checkSymbolsLoadedWithSize():
# symbols got added.
self.assertNotEqual(len(module_changed_names), 0)
self.assertIn(program_module["name"], module_changed_names)
+ self.continue_to_exit()
+
+ # 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())
@skipIfWindows
def test_modules(self):
@@ -119,8 +136,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()
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index a26beed491e98..a607df731366d 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -44,6 +44,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
+#include <Protocol/ProtocolEvents.h>
#include <algorithm>
#include <cassert>
#include <chrono>
@@ -1357,33 +1358,34 @@ void DAP::EventThread() {
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())
+
+ const bool remove_module =
+ event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded;
+ auto p_module = CreateModule(target, module, remove_module);
+ if (!p_module)
continue;
- llvm::StringRef reason;
- bool id_only = false;
+ llvm::StringRef module_id = p_module->id;
+
if (modules.contains(module_id)) {
if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) {
modules.erase(module_id);
- reason = "removed";
- id_only = true;
+ Send(protocol::Event{
+ "module",
+ ModuleEventBody{std::move(p_module).value(),
+ ModuleEventBody::eReasonRemoved}});
} else {
- reason = "changed";
+ Send(protocol::Event{
+ "module",
+ ModuleEventBody{std::move(p_module).value(),
+ ModuleEventBody::eReasonChanged}});
}
- } else {
+ } else if (!remove_module) {
modules.insert(module_id);
- reason = "new";
+ Send(protocol::Event{
+ "module", ModuleEventBody{std::move(p_module).value(),
+ ModuleEventBody::eReasonNew}});
}
-
- 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)));
}
}
} else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp
index ad6e305d09fed..2d789dfd739b8 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.cpp
@@ -17,4 +17,20 @@ json::Value toJSON(const CapabilitiesEventBody &CEB) {
return json::Object{{"capabilities", CEB.capabilities}};
}
+json::Value toJSON(ModuleEventBody::Reason MEBR) {
+ switch (MEBR) {
+ case ModuleEventBody::eReasonNew:
+ return "new";
+ case ModuleEventBody::eReasonChanged:
+ return "changed";
+ case ModuleEventBody::eReasonRemoved:
+ return "removed";
+ }
+ llvm_unreachable("unhandled module event reason!.");
+}
+
+json::Value toJSON(const ModuleEventBody &MEB) {
+ return json::Object{{"reason", MEB.reason}, {"module", MEB.module}};
+}
+
} // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h
index 512106222362c..ba67eeb0c472b 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolEvents.h
@@ -41,6 +41,21 @@ struct CapabilitiesEventBody {
};
llvm::json::Value toJSON(const CapabilitiesEventBody &);
+/// The event indicates that some information about a module has changed.
+struct ModuleEventBody {
+ enum Reason : unsigned { eReasonNew, eReasonChanged, eReasonRemoved };
+
+ /// The new, changed, or removed module. In case of `removed` only the module
+ /// id is used.
+ Module module;
+
+ /// The reason for the event.
+ /// Values: 'new', 'changed', 'removed'
+ Reason reason;
+};
+llvm::json::Value toJSON(ModuleEventBody::Reason);
+llvm::json::Value toJSON(const ModuleEventBody &);
+
} // end namespace lldb_dap::protocol
#endif
>From e2c1810ab874c49821542b28b578fbc8453579f2 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Sun, 22 Jun 2025 13:29:50 +0100
Subject: [PATCH 3/5] [lldb-dap] update vscode extension
---
.../lldb-dap/src-ts/debug-session-tracker.ts | 8 ++++++--
.../lldb-dap/src-ts/ui/modules-data-provider.ts | 15 +++++++++------
2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
index 50db1e1c3a7b0..804c0d6ea5b03 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
@@ -25,6 +25,10 @@ function isEvent(
);
}
+export interface LLDBModule extends DebugProtocol.Module {
+ debugInfoSize?: string;
+}
+
/** Tracks lldb-dap sessions for data visualizers. */
export class DebugSessionTracker
implements vscode.DebugAdapterTrackerFactory, vscode.Disposable
@@ -34,7 +38,7 @@ export class DebugSessionTracker
*
* The modules are kept in an array to maintain the load order of the modules.
*/
- private modules = new Map<vscode.DebugSession, DebugProtocol.Module[]>();
+ private modules = new Map<vscode.DebugSession, LLDBModule[]>();
private modulesChanged = new vscode.EventEmitter<
vscode.DebugSession | undefined
>();
@@ -73,7 +77,7 @@ export class DebugSessionTracker
*
* Modules are returned in load order.
*/
- debugSessionModules(session: vscode.DebugSession): DebugProtocol.Module[] {
+ debugSessionModules(session: vscode.DebugSession): LLDBModule[] {
return this.modules.get(session) ?? [];
}
diff --git a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts
index d0fb9270c734f..4f599cca7467c 100644
--- a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts
+++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts
@@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { DebugProtocol } from "@vscode/debugprotocol";
-import { DebugSessionTracker } from "../debug-session-tracker";
+import { DebugSessionTracker, LLDBModule } from "../debug-session-tracker";
export interface ModuleProperty {
key: string;
@@ -9,19 +9,19 @@ export interface ModuleProperty {
/** Type to represent both Module and ModuleProperty since TreeDataProvider
* expects one concrete type */
-type TreeData = DebugProtocol.Module | ModuleProperty;
+type TreeData = LLDBModule | ModuleProperty;
-function isModule(type: TreeData): type is DebugProtocol.Module {
- return (type as DebugProtocol.Module).id !== undefined;
+function isModule(type: TreeData): type is LLDBModule {
+ return (type as LLDBModule).id !== undefined;
}
class ModuleItem extends vscode.TreeItem {
- constructor(module: DebugProtocol.Module) {
+ constructor(module: LLDBModule) {
super(module.name, vscode.TreeItemCollapsibleState.Collapsed);
this.description = module.symbolStatus;
}
- static getProperties(module: DebugProtocol.Module): ModuleProperty[] {
+ static getProperties(module: LLDBModule): ModuleProperty[] {
// does not include the name and symbol status as it is show in the parent.
let children: ModuleProperty[] = [];
children.push({ key: "id:", value: module.id.toString() });
@@ -41,6 +41,9 @@ class ModuleItem extends vscode.TreeItem {
if (module.symbolFilePath) {
children.push({ key: "symbol filepath:", value: module.symbolFilePath });
}
+ if (module.debugInfoSize) {
+ children.push({ key: "debuginfo size: ", value: module.debugInfoSize });
+ }
return children;
}
}
>From 475a615c70ecbc3ebad93c83ae1e5595ef69997b Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 26 Jun 2025 15:21:40 +0100
Subject: [PATCH 4/5] [lldb-dap] add unit tests
---
lldb/unittests/DAP/CMakeLists.txt | 1 +
lldb/unittests/DAP/JSONUtilsTest.cpp | 11 ---
lldb/unittests/DAP/ProtocolTypesTest.cpp | 91 ++++++++++++++++++++++++
lldb/unittests/DAP/ProtocolUtilsTest.cpp | 24 +++++++
4 files changed, 116 insertions(+), 11 deletions(-)
create mode 100644 lldb/unittests/DAP/ProtocolUtilsTest.cpp
diff --git a/lldb/unittests/DAP/CMakeLists.txt b/lldb/unittests/DAP/CMakeLists.txt
index d5824f4b38a5e..156cd625546bd 100644
--- a/lldb/unittests/DAP/CMakeLists.txt
+++ b/lldb/unittests/DAP/CMakeLists.txt
@@ -7,6 +7,7 @@ add_lldb_unittest(DAPTests
JSONUtilsTest.cpp
LLDBUtilsTest.cpp
ProtocolTypesTest.cpp
+ ProtocolUtilsTest.cpp
TestBase.cpp
VariablesTest.cpp
diff --git a/lldb/unittests/DAP/JSONUtilsTest.cpp b/lldb/unittests/DAP/JSONUtilsTest.cpp
index ce4be085965d5..876980eb4bf4a 100644
--- a/lldb/unittests/DAP/JSONUtilsTest.cpp
+++ b/lldb/unittests/DAP/JSONUtilsTest.cpp
@@ -139,17 +139,6 @@ TEST(JSONUtilsTest, GetInteger_DifferentTypes) {
EXPECT_EQ(result.value(), static_cast<int16_t>(789));
}
-TEST(JSONUtilsTest, CreateModule) {
- SBTarget target;
- SBModule module;
-
- json::Value value = CreateModule(target, module);
- json::Object *object = value.getAsObject();
-
- ASSERT_NE(object, nullptr);
- EXPECT_EQ(object->size(), 0UL);
-}
-
TEST(JSONUtilsTest, GetStrings_EmptyArray) {
llvm::json::Object obj;
obj.try_emplace("key", llvm::json::Array());
diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp
index 085348ffc519d..7d679c981716f 100644
--- a/lldb/unittests/DAP/ProtocolTypesTest.cpp
+++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp
@@ -792,3 +792,94 @@ TEST(ProtocolTypesTest, ReadMemoryResponseBody) {
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(response));
}
+
+TEST(ProtocolTypesTest, Modules) {
+ Module module;
+ module.id = "AC805E8E-B6A4-CD92-4B05-5CFA7CE24AE8-8926C776";
+ module.name = "libm.so.6";
+ module.path = "/some/path/to/libm.so.6";
+ module.isOptimized = false;
+ module.isUserCode = true;
+ module.version = "0.0.1";
+ module.symbolStatus = "Symbol not found.";
+ module.symbolFilePath = "/some/file/path/to/the/symbol/module";
+ module.dateTimeStamp = "2020-12-09T16:09:53+00:00";
+ module.addressRange = "0xcafeface";
+ module.debugInfoSize = "1.2MB";
+
+ Expected<json::Value> expected = json::parse(
+ R"({
+ "id" : "AC805E8E-B6A4-CD92-4B05-5CFA7CE24AE8-8926C776",
+ "name": "libm.so.6",
+ "path": "/some/path/to/libm.so.6",
+ "isOptimized": false,
+ "isUserCode": true,
+ "version": "0.0.1",
+ "symbolStatus": "Symbol not found.",
+ "symbolFilePath": "/some/file/path/to/the/symbol/module",
+ "dateTimeStamp": "2020-12-09T16:09:53+00:00",
+ "addressRange": "0xcafeface",
+ "debugInfoSize": "1.2MB" })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(pp(*expected), pp(module));
+
+ // Test without optional values.
+ module.path.clear();
+ module.isOptimized.reset();
+ module.isUserCode.reset();
+ module.version.clear();
+ module.symbolStatus.clear();
+ module.symbolFilePath.clear();
+ module.dateTimeStamp.clear();
+ module.addressRange.clear();
+ module.debugInfoSize.clear();
+ EXPECT_NE(pp(*expected), pp(module));
+
+ Expected<json::Value> expected_no_opt = json::parse(
+ R"({
+ "id" : "AC805E8E-B6A4-CD92-4B05-5CFA7CE24AE8-8926C776",
+ "name": "libm.so.6"})");
+ ASSERT_THAT_EXPECTED(expected_no_opt, llvm::Succeeded());
+ EXPECT_EQ(pp(*expected_no_opt), pp(module));
+}
+
+TEST(ProtocolTypesTest, ModulesArguments) {
+ ModulesArguments args;
+
+ llvm::Expected<ModulesArguments> expected = parse<ModulesArguments>(R"({})");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(args.startModule, expected->startModule);
+ EXPECT_EQ(args.moduleCount, expected->moduleCount);
+
+ // Non Default values.
+ args.startModule = 1;
+ args.moduleCount = 2;
+ llvm::Expected<ModulesArguments> expected_no_default =
+ parse<ModulesArguments>(R"({ "startModule": 1, "moduleCount": 2})");
+ ASSERT_THAT_EXPECTED(expected_no_default, llvm::Succeeded());
+ EXPECT_EQ(args.startModule, expected_no_default->startModule);
+ EXPECT_EQ(args.moduleCount, expected_no_default->moduleCount);
+}
+
+TEST(ProtocolTypesTest, ModulesResponseBody) {
+ ModulesResponseBody response;
+ Module module1;
+ module1.id = "first id";
+ module1.name = "first name";
+
+ Module module2;
+ module2.id = "second id";
+ module2.name = "second name";
+ response.modules = {std::move(module1), std::move(module2)};
+ response.totalModules = 2;
+
+ Expected<json::Value> expected = json::parse(
+ R"({
+ "modules": [
+ { "id": "first id", "name": "first name"},
+ { "id": "second id", "name": "second name"}
+ ],
+ "totalModules": 2 })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(pp(*expected), pp(response));
+}
diff --git a/lldb/unittests/DAP/ProtocolUtilsTest.cpp b/lldb/unittests/DAP/ProtocolUtilsTest.cpp
new file mode 100644
index 0000000000000..62bd885af594e
--- /dev/null
+++ b/lldb/unittests/DAP/ProtocolUtilsTest.cpp
@@ -0,0 +1,24 @@
+//===-- ProtocolUtilsTest.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProtocolUtils.h"
+#include "JSONUtils.h"
+#include "lldb/API/LLDB.h"
+#include "gtest/gtest.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_dap;
+
+TEST(ProtocolUtilsTest, CreateModule) {
+ SBTarget target;
+ SBModule module;
+
+ std::optional<protocol::Module> module_opt = CreateModule(target, module);
+ EXPECT_EQ(module_opt, std::nullopt);
+}
>From 4a9dd25afadb03b51fd5795d83006a8779117a41 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 3 Jul 2025 22:24:00 +0100
Subject: [PATCH 5/5] [lldb-dap] Create a function for target events.
---
.../tools/lldb-dap/module/TestDAP_module.py | 3 +-
lldb/tools/lldb-dap/DAP.cpp | 42 +----------------
lldb/tools/lldb-dap/EventHelper.cpp | 46 +++++++++++++++++++
lldb/tools/lldb-dap/EventHelper.h | 2 +
4 files changed, 51 insertions(+), 42 deletions(-)
diff --git a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
index 041270daf7d48..d8d9cf24c5e41 100644
--- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
+++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
@@ -87,7 +87,6 @@ def check_symbols_loaded_with_size():
# symbols got added.
self.assertNotEqual(len(module_changed_names), 0)
self.assertIn(program_module["name"], module_changed_names)
- self.continue_to_exit()
# fetch modules from offset
if len(active_modules.keys()) > 2:
@@ -101,6 +100,8 @@ def check_symbols_loaded_with_size():
module_name = resp_modules[0]["name"]
self.assertIn(module_name, active_modules.keys())
+ self.continue_to_exit()
+
@skipIfWindows
def test_modules(self):
"""
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index a607df731366d..94e27bb7e52d7 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -44,7 +44,6 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
-#include <Protocol/ProtocolEvents.h>
#include <algorithm>
#include <cassert>
#include <chrono>
@@ -1348,46 +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);
-
- const bool remove_module =
- event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded;
- auto p_module = CreateModule(target, module, remove_module);
- if (!p_module)
- continue;
-
- llvm::StringRef module_id = p_module->id;
-
- if (modules.contains(module_id)) {
- if (event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded) {
- modules.erase(module_id);
- Send(protocol::Event{
- "module",
- ModuleEventBody{std::move(p_module).value(),
- ModuleEventBody::eReasonRemoved}});
- } else {
- Send(protocol::Event{
- "module",
- ModuleEventBody{std::move(p_module).value(),
- ModuleEventBody::eReasonChanged}});
- }
- } else if (!remove_module) {
- modules.insert(module_id);
- Send(protocol::Event{
- "module", ModuleEventBody{std::move(p_module).value(),
- ModuleEventBody::eReasonNew}});
- }
- }
- }
+ HandleTargetEvent(*this, event);
} else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
auto event_type =
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp
index 364cc7ab4ef8c..5f7739da1580d 100644
--- a/lldb/tools/lldb-dap/EventHelper.cpp
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -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"
@@ -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
diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h
index 72ad5308a2b0c..ba82c89a31119 100644
--- a/lldb/tools/lldb-dap/EventHelper.h
+++ b/lldb/tools/lldb-dap/EventHelper.h
@@ -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
More information about the lldb-commits
mailing list