[Lldb-commits] [lldb] WIP: Stop using replicated variable ids (PR #124232)
Anthony Eid via lldb-commits
lldb-commits at lists.llvm.org
Sun Feb 16 10:24:19 PST 2025
https://github.com/Anthony-Eid updated https://github.com/llvm/llvm-project/pull/124232
>From e741fc75ccb6e2a725b3b26dd4c503cdea6c5fbb Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 24 Jan 2025 00:43:39 -0500
Subject: [PATCH 1/3] Stop using replicated variable ids
---
lldb/tools/lldb-dap/DAP.cpp | 28 ++++++++++++---
lldb/tools/lldb-dap/DAP.h | 15 ++++++--
lldb/tools/lldb-dap/JSONUtils.cpp | 6 ++--
lldb/tools/lldb-dap/JSONUtils.h | 3 +-
lldb/tools/lldb-dap/ProgressEvent.h | 5 +++
lldb/tools/lldb-dap/lldb-dap.cpp | 54 ++++++++++++++++++-----------
6 files changed, 80 insertions(+), 31 deletions(-)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 35250d9eef608..0836fbf9f32dd 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -8,6 +8,7 @@
#include <chrono>
#include <cstdarg>
+#include <cstdint>
#include <fstream>
#include <mutex>
@@ -137,6 +138,15 @@ void DAP::PopulateExceptionBreakpoints() {
});
}
+std::optional<ScopeKind> DAP::ScopeKind(const int64_t variablesReference) {
+ auto iter = scope_kinds.find(variablesReference);
+ if (iter != scope_kinds.end()) {
+ return iter->second;
+ }
+
+ return std::nullopt;
+}
+
ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const std::string &filter) {
// PopulateExceptionBreakpoints() is called after g_dap.debugger is created
// in a request-initialize.
@@ -476,12 +486,22 @@ lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
llvm::json::Value DAP::CreateTopLevelScopes() {
llvm::json::Array scopes;
- scopes.emplace_back(
- CreateScope("Locals", VARREF_LOCALS, variables.locals.GetSize(), false));
- scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
+
+ scopes.emplace_back(CreateScope("Locals", variables.next_temporary_var_ref,
+ ScopeKind::Locals, variables.locals.GetSize(),
+ false));
+ scope_kinds[variables.next_temporary_var_ref++] = ScopeKind::Locals;
+
+ scopes.emplace_back(CreateScope("Globals", variables.next_temporary_var_ref,
+ ScopeKind::Globals,
variables.globals.GetSize(), false));
- scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
+ scope_kinds[variables.next_temporary_var_ref++] = ScopeKind::Globals;
+
+ scopes.emplace_back(CreateScope("Registers", variables.next_temporary_var_ref,
+ ScopeKind::Registers,
variables.registers.GetSize(), false));
+ scope_kinds[variables.next_temporary_var_ref++] = ScopeKind::Registers;
+
return llvm::json::Value(std::move(scopes));
}
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index ae496236f1336..68399e2e0410c 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -13,6 +13,7 @@
#include <iosfwd>
#include <map>
#include <optional>
+#include <set>
#include <thread>
#include "llvm/ADT/DenseMap.h"
@@ -39,6 +40,7 @@
#include "InstructionBreakpoint.h"
#include "ProgressEvent.h"
#include "SourceBreakpoint.h"
+#include "lldb/API/SBValueList.h"
#define VARREF_LOCALS (int64_t)1
#define VARREF_GLOBALS (int64_t)2
@@ -86,6 +88,8 @@ struct Variables {
int64_t next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
int64_t next_permanent_var_ref{PermanentVariableStartIndex};
+ std::set<uint32_t> added_frames;
+
/// Variables that are alive in this stop state.
/// Will be cleared when debuggee resumes.
llvm::DenseMap<int64_t, lldb::SBValue> referenced_variables;
@@ -117,25 +121,27 @@ struct Variables {
struct StartDebuggingRequestHandler : public lldb::SBCommandPluginInterface {
DAP &dap;
- explicit StartDebuggingRequestHandler(DAP &d) : dap(d) {};
+ explicit StartDebuggingRequestHandler(DAP &d) : dap(d){};
bool DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject &result) override;
};
struct ReplModeRequestHandler : public lldb::SBCommandPluginInterface {
DAP &dap;
- explicit ReplModeRequestHandler(DAP &d) : dap(d) {};
+ explicit ReplModeRequestHandler(DAP &d) : dap(d){};
bool DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject &result) override;
};
struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
DAP &dap;
- explicit SendEventRequestHandler(DAP &d) : dap(d) {};
+ explicit SendEventRequestHandler(DAP &d) : dap(d){};
bool DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject &result) override;
};
+enum ScopeKind { Locals, Globals, Registers };
+
struct DAP {
llvm::StringRef debug_adaptor_path;
InputStream input;
@@ -159,6 +165,7 @@ struct DAP {
std::vector<std::string> exit_commands;
std::vector<std::string> stop_commands;
std::vector<std::string> terminate_commands;
+ std::map<int, ScopeKind> scope_kinds;
// Map step in target id to list of function targets that user can choose.
llvm::DenseMap<lldb::addr_t, std::string> step_in_targets;
// A copy of the last LaunchRequest or AttachRequest so we can reuse its
@@ -231,6 +238,8 @@ struct DAP {
void PopulateExceptionBreakpoints();
+ std::optional<ScopeKind> ScopeKind(const int64_t variablesReference);
+
/// Attempt to determine if an expression is a variable expression or
/// lldb command using a heuristic based on the first term of the
/// expression.
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 6ca4dfb4711a1..238343de05596 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -352,16 +352,16 @@ void FillResponse(const llvm::json::Object &request,
// "required": [ "name", "variablesReference", "expensive" ]
// }
llvm::json::Value CreateScope(const llvm::StringRef name,
- int64_t variablesReference,
+ int64_t variablesReference, ScopeKind kind,
int64_t namedVariables, bool expensive) {
llvm::json::Object object;
EmplaceSafeString(object, "name", name.str());
// TODO: Support "arguments" scope. At the moment lldb-dap includes the
// arguments into the "locals" scope.
- if (variablesReference == VARREF_LOCALS) {
+ if (kind == ScopeKind::Locals) {
object.try_emplace("presentationHint", "locals");
- } else if (variablesReference == VARREF_REGS) {
+ } else if (kind == ScopeKind::Registers) {
object.try_emplace("presentationHint", "registers");
}
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index db56d98777347..f117ad9fff792 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -9,6 +9,7 @@
#ifndef LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
#define LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
+#include "DAP.h"
#include "DAPForward.h"
#include "lldb/API/SBCompileUnit.h"
#include "lldb/API/SBFileSpec.h"
@@ -319,7 +320,7 @@ CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp);
/// A "Scope" JSON object with that follows the formal JSON
/// definition outlined by Microsoft.
llvm::json::Value CreateScope(const llvm::StringRef name,
- int64_t variablesReference,
+ int64_t variablesReference, ScopeKind kind,
int64_t namedVariables, bool expensive);
/// Create a "Source" JSON object as described in the debug adaptor definition.
diff --git a/lldb/tools/lldb-dap/ProgressEvent.h b/lldb/tools/lldb-dap/ProgressEvent.h
index 72317b879c803..e029831f8f330 100644
--- a/lldb/tools/lldb-dap/ProgressEvent.h
+++ b/lldb/tools/lldb-dap/ProgressEvent.h
@@ -6,6 +6,9 @@
//
//===----------------------------------------------------------------------===//
+#ifndef PROGRESS_EVENT_H
+#define PROGRESS_EVENT_H
+
#include <atomic>
#include <chrono>
#include <mutex>
@@ -155,3 +158,5 @@ class ProgressEventReporter {
};
} // namespace lldb_dap
+
+#endif // PROGRESS_EVENT_H
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 6b12569d90a83..52a31477d7092 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -128,16 +128,19 @@ static void PrintWelcomeMessage(DAP &dap) {
}
lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) {
- switch (variablesReference) {
- case VARREF_LOCALS:
- return &dap.variables.locals;
- case VARREF_GLOBALS:
- return &dap.variables.globals;
- case VARREF_REGS:
- return &dap.variables.registers;
- default:
- return nullptr;
+ std::optional<ScopeKind> scope_kind = dap.ScopeKind(variablesReference);
+
+ if (scope_kind.has_value()) {
+ switch (scope_kind.value()) {
+ case lldb_dap::ScopeKind::Locals:
+ return &dap.variables.locals;
+ case lldb_dap::ScopeKind::Globals:
+ return &dap.variables.globals;
+ case lldb_dap::ScopeKind::Registers:
+ return &dap.variables.registers;
+ }
}
+ return nullptr;
}
SOCKET AcceptConnection(DAP &dap, int portno) {
@@ -2559,15 +2562,25 @@ void request_scopes(DAP &dap, const llvm::json::Object &request) {
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
}
- dap.variables.locals = frame.GetVariables(/*arguments=*/true,
- /*locals=*/true,
- /*statics=*/false,
- /*in_scope_only=*/true);
- dap.variables.globals = frame.GetVariables(/*arguments=*/false,
- /*locals=*/false,
- /*statics=*/true,
- /*in_scope_only=*/true);
- dap.variables.registers = frame.GetRegisters();
+ uint32_t frame_id = frame.GetFrameID();
+
+ if (dap.variables.added_frames.find(frame_id) ==
+ dap.variables.added_frames.end()) {
+ dap.variables.added_frames.insert(frame_id);
+
+ dap.variables.locals.Append(frame.GetVariables(/*arguments=*/true,
+ /*locals=*/true,
+ /*statics=*/false,
+ /*in_scope_only=*/true));
+
+ dap.variables.globals.Append(frame.GetVariables(/*arguments=*/false,
+ /*locals=*/false,
+ /*statics=*/true,
+ /*in_scope_only=*/true));
+
+ dap.variables.registers.Append(frame.GetRegisters());
+ }
+
body.try_emplace("scopes", dap.CreateTopLevelScopes());
response.try_emplace("body", std::move(body));
dap.SendJSON(llvm::json::Value(std::move(response)));
@@ -3945,7 +3958,7 @@ void request_variables(DAP &dap, const llvm::json::Object &request) {
int64_t start_idx = 0;
int64_t num_children = 0;
- if (variablesReference == VARREF_REGS) {
+ if (dap.ScopeKind(variablesReference) == ScopeKind::Registers) {
// Change the default format of any pointer sized registers in the first
// register set to be the lldb::eFormatAddressInfo so we show the pointer
// and resolve what the pointer resolves to. Only change the format if the
@@ -3965,7 +3978,8 @@ void request_variables(DAP &dap, const llvm::json::Object &request) {
}
num_children = top_scope->GetSize();
- if (num_children == 0 && variablesReference == VARREF_LOCALS) {
+ if (num_children == 0 &&
+ dap.ScopeKind(variablesReference) == ScopeKind::Locals) {
// Check for an error in the SBValueList that might explain why we don't
// have locals. If we have an error display it as the sole value in the
// the locals.
>From 0b7aaed3da01adb6c1e1fa0d7836ca36bc1842da Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Fri, 24 Jan 2025 03:09:58 -0500
Subject: [PATCH 2/3] Fix bug where some scope requests would be invalid
---
lldb/tools/lldb-dap/DAP.cpp | 30 +++++++++++++---
lldb/tools/lldb-dap/DAP.h | 12 +++++--
lldb/tools/lldb-dap/lldb-dap.cpp | 62 +++++++++++++++++++-------------
3 files changed, 72 insertions(+), 32 deletions(-)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 0836fbf9f32dd..0f36c0a578b3b 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -141,7 +141,7 @@ void DAP::PopulateExceptionBreakpoints() {
std::optional<ScopeKind> DAP::ScopeKind(const int64_t variablesReference) {
auto iter = scope_kinds.find(variablesReference);
if (iter != scope_kinds.end()) {
- return iter->second;
+ return iter->second.first;
}
return std::nullopt;
@@ -484,23 +484,26 @@ lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
return thread.GetFrameAtIndex(GetLLDBFrameID(frame_id));
}
-llvm::json::Value DAP::CreateTopLevelScopes() {
+llvm::json::Value DAP::CreateTopLevelScopes(uint32_t frame_id) {
llvm::json::Array scopes;
scopes.emplace_back(CreateScope("Locals", variables.next_temporary_var_ref,
ScopeKind::Locals, variables.locals.GetSize(),
false));
- scope_kinds[variables.next_temporary_var_ref++] = ScopeKind::Locals;
+ scope_kinds[variables.next_temporary_var_ref++] =
+ std::make_pair(ScopeKind::Locals, frame_id);
scopes.emplace_back(CreateScope("Globals", variables.next_temporary_var_ref,
ScopeKind::Globals,
variables.globals.GetSize(), false));
- scope_kinds[variables.next_temporary_var_ref++] = ScopeKind::Globals;
+ scope_kinds[variables.next_temporary_var_ref++] =
+ std::make_pair(ScopeKind::Globals, frame_id);
scopes.emplace_back(CreateScope("Registers", variables.next_temporary_var_ref,
ScopeKind::Registers,
variables.registers.GetSize(), false));
- scope_kinds[variables.next_temporary_var_ref++] = ScopeKind::Registers;
+ scope_kinds[variables.next_temporary_var_ref++] =
+ std::make_pair(ScopeKind::Registers, frame_id);
return llvm::json::Value(std::move(scopes));
}
@@ -846,11 +849,28 @@ lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
return error;
}
+bool Variables::SwitchFrame(uint32_t frame_id) {
+ auto iter = frames.find(frame_id);
+
+ if (iter == frames.end()) {
+ return false;
+ }
+
+ auto [frame_locals, frame_globals, frame_registers] = iter->second;
+
+ locals = frame_locals;
+ globals = frame_globals;
+ registers = frame_registers;
+
+ return true;
+}
+
void Variables::Clear() {
locals.Clear();
globals.Clear();
registers.Clear();
referenced_variables.clear();
+ frames.clear();
}
int64_t Variables::GetNewVariableReference(bool is_permanent) {
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 68399e2e0410c..a77e3c0389f5a 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -15,6 +15,8 @@
#include <optional>
#include <set>
#include <thread>
+#include <tuple>
+#include <utility>
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
@@ -88,7 +90,9 @@ struct Variables {
int64_t next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
int64_t next_permanent_var_ref{PermanentVariableStartIndex};
- std::set<uint32_t> added_frames;
+ std::map<uint32_t,
+ std::tuple<lldb::SBValueList, lldb::SBValueList, lldb::SBValueList>>
+ frames;
/// Variables that are alive in this stop state.
/// Will be cleared when debuggee resumes.
@@ -115,6 +119,8 @@ struct Variables {
/// \return variableReference assigned to this expandable variable.
int64_t InsertVariable(lldb::SBValue variable, bool is_permanent);
+ bool SwitchFrame(uint32_t frame_id);
+
/// Clear all scope variables and non-permanent expandable variables.
void Clear();
};
@@ -165,7 +171,7 @@ struct DAP {
std::vector<std::string> exit_commands;
std::vector<std::string> stop_commands;
std::vector<std::string> terminate_commands;
- std::map<int, ScopeKind> scope_kinds;
+ std::map<int, std::pair<ScopeKind, uint32_t>> scope_kinds;
// Map step in target id to list of function targets that user can choose.
llvm::DenseMap<lldb::addr_t, std::string> step_in_targets;
// A copy of the last LaunchRequest or AttachRequest so we can reuse its
@@ -234,7 +240,7 @@ struct DAP {
lldb::SBFrame GetLLDBFrame(const llvm::json::Object &arguments);
- llvm::json::Value CreateTopLevelScopes();
+ llvm::json::Value CreateTopLevelScopes(uint32_t frame_id);
void PopulateExceptionBreakpoints();
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 52a31477d7092..f61b667a70220 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -51,6 +51,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <thread>
+#include <tuple>
#include <vector>
#if defined(_WIN32)
@@ -128,17 +129,25 @@ static void PrintWelcomeMessage(DAP &dap) {
}
lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) {
- std::optional<ScopeKind> scope_kind = dap.ScopeKind(variablesReference);
-
- if (scope_kind.has_value()) {
- switch (scope_kind.value()) {
- case lldb_dap::ScopeKind::Locals:
- return &dap.variables.locals;
- case lldb_dap::ScopeKind::Globals:
- return &dap.variables.globals;
- case lldb_dap::ScopeKind::Registers:
- return &dap.variables.registers;
- }
+ auto iter = dap.scope_kinds.find(variablesReference);
+ if (iter == dap.scope_kinds.end()) {
+ return nullptr;
+ }
+
+ ScopeKind scope_kind = iter->second.first;
+ uint32_t frame_id = iter->second.second;
+
+ if (!dap.variables.SwitchFrame(frame_id)) {
+ return nullptr;
+ }
+
+ switch (scope_kind) {
+ case lldb_dap::ScopeKind::Locals:
+ return &dap.variables.locals;
+ case lldb_dap::ScopeKind::Globals:
+ return &dap.variables.globals;
+ case lldb_dap::ScopeKind::Registers:
+ return &dap.variables.registers;
}
return nullptr;
}
@@ -2564,24 +2573,29 @@ void request_scopes(DAP &dap, const llvm::json::Object &request) {
uint32_t frame_id = frame.GetFrameID();
- if (dap.variables.added_frames.find(frame_id) ==
- dap.variables.added_frames.end()) {
- dap.variables.added_frames.insert(frame_id);
+ if (dap.variables.frames.find(frame_id) == dap.variables.frames.end()) {
+
+ auto locals = frame.GetVariables(/*arguments=*/true,
+ /*locals=*/true,
+ /*statics=*/false,
+ /*in_scope_only=*/true);
+
+ auto globals = frame.GetVariables(/*arguments=*/false,
+ /*locals=*/false,
+ /*statics=*/true,
+ /*in_scope_only=*/true);
- dap.variables.locals.Append(frame.GetVariables(/*arguments=*/true,
- /*locals=*/true,
- /*statics=*/false,
- /*in_scope_only=*/true));
+ auto registers = frame.GetRegisters();
- dap.variables.globals.Append(frame.GetVariables(/*arguments=*/false,
- /*locals=*/false,
- /*statics=*/true,
- /*in_scope_only=*/true));
+ dap.variables.frames.insert(
+ std::make_pair(frame_id, std::make_tuple(locals, globals, registers)));
- dap.variables.registers.Append(frame.GetRegisters());
+ dap.variables.locals = locals;
+ dap.variables.globals = globals;
+ dap.variables.registers = registers;
}
- body.try_emplace("scopes", dap.CreateTopLevelScopes());
+ body.try_emplace("scopes", dap.CreateTopLevelScopes(frame_id));
response.try_emplace("body", std::move(body));
dap.SendJSON(llvm::json::Value(std::move(response)));
}
>From fa7ed034e3c84ea9b432dbdf44d34302d67838cb Mon Sep 17 00:00:00 2001
From: Anthony Eid <hello at anthonyeid.me>
Date: Sun, 16 Feb 2025 13:23:55 -0500
Subject: [PATCH 3/3] Clean up frame switching
---
lldb/tools/lldb-dap/DAP.h | 5 ++++-
lldb/tools/lldb-dap/lldb-dap.cpp | 6 ++----
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index a77e3c0389f5a..6a25eeb37d390 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -330,7 +330,10 @@ struct DAP {
void RegisterRequestCallback(std::string request, RequestCallback callback);
/// Debuggee will continue from stopped state.
- void WillContinue() { variables.Clear(); }
+ void WillContinue() {
+ variables.Clear();
+ scope_kinds.clear();
+ }
/// Poll the process to wait for it to reach the eStateStopped state.
///
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index f61b667a70220..43ca4fe08bfeb 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -2589,12 +2589,10 @@ void request_scopes(DAP &dap, const llvm::json::Object &request) {
dap.variables.frames.insert(
std::make_pair(frame_id, std::make_tuple(locals, globals, registers)));
-
- dap.variables.locals = locals;
- dap.variables.globals = globals;
- dap.variables.registers = registers;
}
+ dap.variables.SwitchFrame(frame_id);
+
body.try_emplace("scopes", dap.CreateTopLevelScopes(frame_id));
response.try_emplace("body", std::move(body));
dap.SendJSON(llvm::json::Value(std::move(response)));
More information about the lldb-commits
mailing list