Skip to content

Commit 5bdcb82

Browse files
committed
[lldb-dap] Implement declaration locations
This commit implements support for the "declaration location" recently added by microsoft/debug-adapter-protocol#494 to the debug adapter protocol. For the `declarationLocationReference` we need a variable ID similar to the the `variablesReference`. I decided to simply reuse the `variablesReference` here and renamed `Variables::expandable_variables` and friends accordingly. Given that almost all variables have a declaration location, we now assign those variable ids to all variables. While `declarationLocationReference` effectively supersedes `$__lldb_extensions.declaration`, I did not remove this extension, yet, since I assume that there are some closed-source extensions which rely on it. I tested this against VS-Code Insiders. However, VS-Code Insiders currently only supports `valueLoctionReference` and not `declarationLocationReference`, yet. Locally, I hence published the declaration locations as value locations, and VS Code Insiders navigated to the expected places. Looking forward to proper VS Code support for `declarationLocationReference`.
1 parent b765fdd commit 5bdcb82

File tree

9 files changed

+286
-102
lines changed

9 files changed

+286
-102
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,17 @@ def request_setVariable(self, containingVarRef, name, value, id=None):
10831083
}
10841084
return self.send_recv(command_dict)
10851085

1086+
def request_locations(self, locationReference):
1087+
args_dict = {
1088+
"locationReference": locationReference,
1089+
}
1090+
command_dict = {
1091+
"command": "locations",
1092+
"type": "request",
1093+
"arguments": args_dict,
1094+
}
1095+
return self.send_recv(command_dict)
1096+
10861097
def request_testGetTargetBreakpoints(self):
10871098
"""A request packet used in the LLDB test suite to get all currently
10881099
set breakpoint infos for all breakpoints currently set in the
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""
2+
Test lldb-dap locations request
3+
"""
4+
5+
6+
import dap_server
7+
from lldbsuite.test.decorators import *
8+
from lldbsuite.test.lldbtest import *
9+
from lldbsuite.test import lldbutil
10+
import lldbdap_testcase
11+
import os
12+
13+
14+
class TestDAP_locations(lldbdap_testcase.DAPTestCaseBase):
15+
@skipIfWindows
16+
def test_locations(self):
17+
"""
18+
Tests the 'locations' request.
19+
"""
20+
program = self.getBuildArtifact("a.out")
21+
self.build_and_launch(program)
22+
source = "main.c"
23+
self.source_path = os.path.join(os.getcwd(), source)
24+
self.set_source_breakpoints(
25+
source,
26+
[line_number(source, "// BREAK HERE")],
27+
)
28+
self.continue_to_next_stop()
29+
30+
locals = {l["name"]: l for l in self.dap_server.get_local_variables()}
31+
32+
# var1 has a declarationLocation but no valueLocation
33+
self.assertIn("declarationLocationReference", locals["var1"].keys())
34+
self.assertNotIn("valueLocationReference", locals["var1"].keys())
35+
loc_var1 = self.dap_server.request_locations(
36+
locals["var1"]["declarationLocationReference"]
37+
)
38+
self.assertTrue(loc_var1["success"])
39+
self.assertTrue(loc_var1["body"]["source"]["path"].endswith("main.c"))
40+
self.assertEqual(loc_var1["body"]["line"], 2)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
int main(void) {
2+
int var1 = 1;
3+
// BREAK HERE
4+
return 0;
5+
}

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ void Variables::Clear() {
813813
locals.Clear();
814814
globals.Clear();
815815
registers.Clear();
816-
expandable_variables.clear();
816+
referenced_variables.clear();
817817
}
818818

819819
int64_t Variables::GetNewVariableReference(bool is_permanent) {
@@ -828,24 +828,23 @@ bool Variables::IsPermanentVariableReference(int64_t var_ref) {
828828

829829
lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
830830
if (IsPermanentVariableReference(var_ref)) {
831-
auto pos = expandable_permanent_variables.find(var_ref);
832-
if (pos != expandable_permanent_variables.end())
831+
auto pos = referenced_permanent_variables.find(var_ref);
832+
if (pos != referenced_permanent_variables.end())
833833
return pos->second;
834834
} else {
835-
auto pos = expandable_variables.find(var_ref);
836-
if (pos != expandable_variables.end())
835+
auto pos = referenced_variables.find(var_ref);
836+
if (pos != referenced_variables.end())
837837
return pos->second;
838838
}
839839
return lldb::SBValue();
840840
}
841841

842-
int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
843-
bool is_permanent) {
842+
int64_t Variables::InsertVariable(lldb::SBValue variable, bool is_permanent) {
844843
int64_t var_ref = GetNewVariableReference(is_permanent);
845844
if (is_permanent)
846-
expandable_permanent_variables.insert(std::make_pair(var_ref, variable));
845+
referenced_permanent_variables.insert(std::make_pair(var_ref, variable));
847846
else
848-
expandable_variables.insert(std::make_pair(var_ref, variable));
847+
referenced_variables.insert(std::make_pair(var_ref, variable));
849848
return var_ref;
850849
}
851850

lldb/tools/lldb-dap/DAP.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,12 @@ struct Variables {
104104
int64_t next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
105105
int64_t next_permanent_var_ref{PermanentVariableStartIndex};
106106

107-
/// Expandable variables that are alive in this stop state.
107+
/// Variables that are alive in this stop state.
108108
/// Will be cleared when debuggee resumes.
109-
llvm::DenseMap<int64_t, lldb::SBValue> expandable_variables;
110-
/// Expandable variables that persist across entire debug session.
109+
llvm::DenseMap<int64_t, lldb::SBValue> referenced_variables;
110+
/// Variables that persist across entire debug session.
111111
/// These are the variables evaluated from debug console REPL.
112-
llvm::DenseMap<int64_t, lldb::SBValue> expandable_permanent_variables;
112+
llvm::DenseMap<int64_t, lldb::SBValue> referenced_permanent_variables;
113113

114114
/// Check if \p var_ref points to a variable that should persist for the
115115
/// entire duration of the debug session, e.g. repl expandable variables
@@ -127,7 +127,7 @@ struct Variables {
127127

128128
/// Insert a new \p variable.
129129
/// \return variableReference assigned to this expandable variable.
130-
int64_t InsertExpandableVariable(lldb::SBValue variable, bool is_permanent);
130+
int64_t InsertVariable(lldb::SBValue variable, bool is_permanent);
131131

132132
/// Clear all scope variables and non-permanent expandable variables.
133133
void Clear();

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 72 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -614,9 +614,8 @@ CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
614614
// }
615615
// }
616616
// }
617-
llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
617+
llvm::json::Value CreateSource(const lldb::SBFileSpec &file) {
618618
llvm::json::Object object;
619-
lldb::SBFileSpec file = line_entry.GetFileSpec();
620619
if (file.IsValid()) {
621620
const char *name = file.GetFilename();
622621
if (name)
@@ -630,6 +629,10 @@ llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
630629
return llvm::json::Value(std::move(object));
631630
}
632631

632+
llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry) {
633+
return CreateSource(line_entry.GetFileSpec());
634+
}
635+
633636
llvm::json::Value CreateSource(llvm::StringRef source_path) {
634637
llvm::json::Object source;
635638
llvm::StringRef name = llvm::sys::path::filename(source_path);
@@ -1146,65 +1149,75 @@ std::string VariableDescription::GetResult(llvm::StringRef context) {
11461149
// "description": "The number of indexed child variables. The client
11471150
// can use this optional information to present the
11481151
// children in a paged UI and fetch them in chunks."
1149-
// }
1150-
//
1152+
// },
1153+
// "declarationLocationReference": {
1154+
// "type": "integer",
1155+
// "description": "A reference that allows the client to request the
1156+
// location where the variable is declared. This should be
1157+
// present only if the adapter is likely to be able to
1158+
// resolve the location.\n\nThis reference shares the same
1159+
// lifetime as the `variablesReference`. See 'Lifetime of
1160+
// Object References' in the Overview section for
1161+
// details."
1162+
// },
11511163
//
11521164
// "$__lldb_extensions": {
11531165
// "description": "Unofficial extensions to the protocol",
11541166
// "properties": {
11551167
// "declaration": {
1156-
// "type": "object",
1157-
// "description": "The source location where the variable was declared.
1158-
// This value won't be present if no declaration is
1159-
// available.",
1160-
// "properties": {
1161-
// "path": {
1162-
// "type": "string",
1163-
// "description": "The source file path where the variable was
1164-
// declared."
1165-
// },
1166-
// "line": {
1167-
// "type": "number",
1168-
// "description": "The 1-indexed source line where the variable was
1169-
// declared."
1170-
// },
1171-
// "column": {
1172-
// "type": "number",
1173-
// "description": "The 1-indexed source column where the variable
1174-
// was declared."
1168+
// "type": "object",
1169+
// "description": "The source location where the variable was
1170+
// declared. This value won't be present if no
1171+
// declaration is available.
1172+
// Superseded by `declarationLocationReference`",
1173+
// "properties": {
1174+
// "path": {
1175+
// "type": "string",
1176+
// "description": "The source file path where the variable was
1177+
// declared."
1178+
// },
1179+
// "line": {
1180+
// "type": "number",
1181+
// "description": "The 1-indexed source line where the variable
1182+
// was declared."
1183+
// },
1184+
// "column": {
1185+
// "type": "number",
1186+
// "description": "The 1-indexed source column where the variable
1187+
// was declared."
1188+
// }
11751189
// }
1190+
// },
1191+
// "value": {
1192+
// "type": "string",
1193+
// "description": "The internal value of the variable as returned by
1194+
// This is effectively SBValue.GetValue(). The other
1195+
// `value` entry in the top-level variable response
1196+
// is, on the other hand, just a display string for
1197+
// the variable."
1198+
// },
1199+
// "summary": {
1200+
// "type": "string",
1201+
// "description": "The summary string of the variable. This is
1202+
// effectively SBValue.GetSummary()."
1203+
// },
1204+
// "autoSummary": {
1205+
// "type": "string",
1206+
// "description": "The auto generated summary if using
1207+
// `enableAutoVariableSummaries`."
1208+
// },
1209+
// "error": {
1210+
// "type": "string",
1211+
// "description": "An error message generated if LLDB couldn't inspect
1212+
// the variable."
11761213
// }
1177-
// },
1178-
// "value":
1179-
// "type": "string",
1180-
// "description": "The internal value of the variable as returned by
1181-
// This is effectively SBValue.GetValue(). The other
1182-
// `value` entry in the top-level variable response is,
1183-
// on the other hand, just a display string for the
1184-
// variable."
1185-
// },
1186-
// "summary":
1187-
// "type": "string",
1188-
// "description": "The summary string of the variable. This is
1189-
// effectively SBValue.GetSummary()."
1190-
// },
1191-
// "autoSummary":
1192-
// "type": "string",
1193-
// "description": "The auto generated summary if using
1194-
// `enableAutoVariableSummaries`."
1195-
// },
1196-
// "error":
1197-
// "type": "string",
1198-
// "description": "An error message generated if LLDB couldn't inspect
1199-
// the variable."
12001214
// }
12011215
// }
12021216
// },
12031217
// "required": [ "name", "value", "variablesReference" ]
12041218
// }
1205-
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1206-
int64_t varID, bool format_hex,
1207-
bool is_name_duplicated,
1219+
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
1220+
bool format_hex, bool is_name_duplicated,
12081221
std::optional<std::string> custom_name) {
12091222
VariableDescription desc(v, format_hex, is_name_duplicated, custom_name);
12101223
llvm::json::Object object;
@@ -1249,12 +1262,18 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
12491262
}
12501263
}
12511264
EmplaceSafeString(object, "type", desc.display_type_name);
1252-
if (varID != INT64_MAX)
1253-
object.try_emplace("id", varID);
1265+
1266+
// A unique variable identifier to help in properly identifying variables with
1267+
// the same name. This is an extension to the VS protocol.
1268+
object.try_emplace("id", var_ref);
1269+
12541270
if (v.MightHaveChildren())
1255-
object.try_emplace("variablesReference", variablesReference);
1271+
object.try_emplace("variablesReference", var_ref);
12561272
else
1257-
object.try_emplace("variablesReference", (int64_t)0);
1273+
object.try_emplace("variablesReference", 0);
1274+
1275+
if (v.GetDeclaration().IsValid())
1276+
object.try_emplace("declarationLocationReference", var_ref);
12581277

12591278
object.try_emplace("$__lldb_extensions", desc.GetVariableExtensionsJSON());
12601279
return llvm::json::Value(std::move(object));

lldb/tools/lldb-dap/JSONUtils.h

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -282,16 +282,26 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
282282
int64_t variablesReference,
283283
int64_t namedVariables, bool expensive);
284284

285+
/// Create a "Source" JSON object as described in the debug adaptor definition.
286+
///
287+
/// \param[in] file
288+
/// The SBFileSpec to use when populating out the "Source" object
289+
///
290+
/// \return
291+
/// A "Source" JSON object that follows the formal JSON
292+
/// definition outlined by Microsoft.
293+
llvm::json::Value CreateSource(const lldb::SBFileSpec &file);
294+
285295
/// Create a "Source" JSON object as described in the debug adaptor definition.
286296
///
287297
/// \param[in] line_entry
288298
/// The LLDB line table to use when populating out the "Source"
289299
/// object
290300
///
291301
/// \return
292-
/// A "Source" JSON object with that follows the formal JSON
302+
/// A "Source" JSON object that follows the formal JSON
293303
/// definition outlined by Microsoft.
294-
llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry);
304+
llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry);
295305

296306
/// Create a "Source" object for a given source path.
297307
///
@@ -433,15 +443,10 @@ struct VariableDescription {
433443
/// The LLDB value to use when populating out the "Variable"
434444
/// object.
435445
///
436-
/// \param[in] variablesReference
437-
/// The variable reference. Zero if this value isn't structured
438-
/// and has no children, non-zero if it does have children and
439-
/// might be asked to expand itself.
440-
///
441-
/// \param[in] varID
442-
/// A unique variable identifier to help in properly identifying
443-
/// variables with the same name. This is an extension to the
444-
/// VS protocol.
446+
/// \param[in] var_ref
447+
/// The variable reference. Used to identify the value, e.g.
448+
/// in the `variablesReference` or `declarationLocationReference`
449+
/// properties.
445450
///
446451
/// \param[in] format_hex
447452
/// It set to true the variable will be formatted as hex in
@@ -462,8 +467,8 @@ struct VariableDescription {
462467
/// \return
463468
/// A "Variable" JSON object with that follows the formal JSON
464469
/// definition outlined by Microsoft.
465-
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
466-
int64_t varID, bool format_hex,
470+
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
471+
bool format_hex,
467472
bool is_name_duplicated = false,
468473
std::optional<std::string> custom_name = {});
469474

0 commit comments

Comments
 (0)