Skip to content

Commit 0cc2cd7

Browse files
authored
[lldb-dap] Provide declarationLocation for variables (#102928)
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 `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 d904542 commit 0cc2cd7

File tree

9 files changed

+288
-102
lines changed

9 files changed

+288
-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
@@ -1109,6 +1109,17 @@ def request_setVariable(self, containingVarRef, name, value, id=None):
11091109
}
11101110
return self.send_recv(command_dict)
11111111

1112+
def request_locations(self, locationReference):
1113+
args_dict = {
1114+
"locationReference": locationReference,
1115+
}
1116+
command_dict = {
1117+
"command": "locations",
1118+
"type": "request",
1119+
"arguments": args_dict,
1120+
}
1121+
return self.send_recv(command_dict)
1122+
11121123
def request_testGetTargetBreakpoints(self):
11131124
"""A request packet used in the LLDB test suite to get all currently
11141125
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
@@ -822,7 +822,7 @@ void Variables::Clear() {
822822
locals.Clear();
823823
globals.Clear();
824824
registers.Clear();
825-
expandable_variables.clear();
825+
referenced_variables.clear();
826826
}
827827

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

838838
lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
839839
if (IsPermanentVariableReference(var_ref)) {
840-
auto pos = expandable_permanent_variables.find(var_ref);
841-
if (pos != expandable_permanent_variables.end())
840+
auto pos = referenced_permanent_variables.find(var_ref);
841+
if (pos != referenced_permanent_variables.end())
842842
return pos->second;
843843
} else {
844-
auto pos = expandable_variables.find(var_ref);
845-
if (pos != expandable_variables.end())
844+
auto pos = referenced_variables.find(var_ref);
845+
if (pos != referenced_variables.end())
846846
return pos->second;
847847
}
848848
return lldb::SBValue();
849849
}
850850

851-
int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
852-
bool is_permanent) {
851+
int64_t Variables::InsertVariable(lldb::SBValue variable, bool is_permanent) {
853852
int64_t var_ref = GetNewVariableReference(is_permanent);
854853
if (is_permanent)
855-
expandable_permanent_variables.insert(std::make_pair(var_ref, variable));
854+
referenced_permanent_variables.insert(std::make_pair(var_ref, variable));
856855
else
857-
expandable_variables.insert(std::make_pair(var_ref, variable));
856+
referenced_variables.insert(std::make_pair(var_ref, variable));
858857
return var_ref;
859858
}
860859

lldb/tools/lldb-dap/DAP.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,12 @@ struct Variables {
111111
int64_t next_temporary_var_ref{VARREF_FIRST_VAR_IDX};
112112
int64_t next_permanent_var_ref{PermanentVariableStartIndex};
113113

114-
/// Expandable variables that are alive in this stop state.
114+
/// Variables that are alive in this stop state.
115115
/// Will be cleared when debuggee resumes.
116-
llvm::DenseMap<int64_t, lldb::SBValue> expandable_variables;
117-
/// Expandable variables that persist across entire debug session.
116+
llvm::DenseMap<int64_t, lldb::SBValue> referenced_variables;
117+
/// Variables that persist across entire debug session.
118118
/// These are the variables evaluated from debug console REPL.
119-
llvm::DenseMap<int64_t, lldb::SBValue> expandable_permanent_variables;
119+
llvm::DenseMap<int64_t, lldb::SBValue> referenced_permanent_variables;
120120

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

135135
/// Insert a new \p variable.
136136
/// \return variableReference assigned to this expandable variable.
137-
int64_t InsertExpandableVariable(lldb::SBValue variable, bool is_permanent);
137+
int64_t InsertVariable(lldb::SBValue variable, bool is_permanent);
138138

139139
/// Clear all scope variables and non-permanent expandable variables.
140140
void Clear();

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 74 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -630,9 +630,8 @@ CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
630630
// }
631631
// }
632632
// }
633-
llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
633+
llvm::json::Value CreateSource(const lldb::SBFileSpec &file) {
634634
llvm::json::Object object;
635-
lldb::SBFileSpec file = line_entry.GetFileSpec();
636635
if (file.IsValid()) {
637636
const char *name = file.GetFilename();
638637
if (name)
@@ -646,6 +645,10 @@ llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
646645
return llvm::json::Value(std::move(object));
647646
}
648647

648+
llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry) {
649+
return CreateSource(line_entry.GetFileSpec());
650+
}
651+
649652
llvm::json::Value CreateSource(llvm::StringRef source_path) {
650653
llvm::json::Object source;
651654
llvm::StringRef name = llvm::sys::path::filename(source_path);
@@ -1253,7 +1256,7 @@ std::string VariableDescription::GetResult(llvm::StringRef context) {
12531256
// "description": "The number of indexed child variables. The client
12541257
// can use this optional information to present the
12551258
// children in a paged UI and fetch them in chunks."
1256-
// }
1259+
// },
12571260
// "memoryReference": {
12581261
// "type": "string",
12591262
// "description": "A memory reference associated with this variable.
@@ -1263,63 +1266,75 @@ std::string VariableDescription::GetResult(llvm::StringRef context) {
12631266
// be used in a `disassemble` request. This attribute may
12641267
// be returned by a debug adapter if corresponding
12651268
// capability `supportsMemoryReferences` is true."
1266-
// },
1269+
// },
1270+
// "declarationLocationReference": {
1271+
// "type": "integer",
1272+
// "description": "A reference that allows the client to request the
1273+
// location where the variable is declared. This should be
1274+
// present only if the adapter is likely to be able to
1275+
// resolve the location.\n\nThis reference shares the same
1276+
// lifetime as the `variablesReference`. See 'Lifetime of
1277+
// Object References' in the Overview section for
1278+
// details."
1279+
// },
1280+
//
12671281
// "$__lldb_extensions": {
12681282
// "description": "Unofficial extensions to the protocol",
12691283
// "properties": {
12701284
// "declaration": {
1271-
// "type": "object",
1272-
// "description": "The source location where the variable was declared.
1273-
// This value won't be present if no declaration is
1274-
// available.",
1275-
// "properties": {
1276-
// "path": {
1277-
// "type": "string",
1278-
// "description": "The source file path where the variable was
1279-
// declared."
1280-
// },
1281-
// "line": {
1282-
// "type": "number",
1283-
// "description": "The 1-indexed source line where the variable was
1284-
// declared."
1285-
// },
1286-
// "column": {
1287-
// "type": "number",
1288-
// "description": "The 1-indexed source column where the variable
1289-
// was declared."
1285+
// "type": "object",
1286+
// "description": "The source location where the variable was
1287+
// declared. This value won't be present if no
1288+
// declaration is available.
1289+
// Superseded by `declarationLocationReference`",
1290+
// "properties": {
1291+
// "path": {
1292+
// "type": "string",
1293+
// "description": "The source file path where the variable was
1294+
// declared."
1295+
// },
1296+
// "line": {
1297+
// "type": "number",
1298+
// "description": "The 1-indexed source line where the variable
1299+
// was declared."
1300+
// },
1301+
// "column": {
1302+
// "type": "number",
1303+
// "description": "The 1-indexed source column where the variable
1304+
// was declared."
1305+
// }
12901306
// }
1307+
// },
1308+
// "value": {
1309+
// "type": "string",
1310+
// "description": "The internal value of the variable as returned by
1311+
// This is effectively SBValue.GetValue(). The other
1312+
// `value` entry in the top-level variable response
1313+
// is, on the other hand, just a display string for
1314+
// the variable."
1315+
// },
1316+
// "summary": {
1317+
// "type": "string",
1318+
// "description": "The summary string of the variable. This is
1319+
// effectively SBValue.GetSummary()."
1320+
// },
1321+
// "autoSummary": {
1322+
// "type": "string",
1323+
// "description": "The auto generated summary if using
1324+
// `enableAutoVariableSummaries`."
1325+
// },
1326+
// "error": {
1327+
// "type": "string",
1328+
// "description": "An error message generated if LLDB couldn't inspect
1329+
// the variable."
12911330
// }
1292-
// },
1293-
// "value":
1294-
// "type": "string",
1295-
// "description": "The internal value of the variable as returned by
1296-
// This is effectively SBValue.GetValue(). The other
1297-
// `value` entry in the top-level variable response is,
1298-
// on the other hand, just a display string for the
1299-
// variable."
1300-
// },
1301-
// "summary":
1302-
// "type": "string",
1303-
// "description": "The summary string of the variable. This is
1304-
// effectively SBValue.GetSummary()."
1305-
// },
1306-
// "autoSummary":
1307-
// "type": "string",
1308-
// "description": "The auto generated summary if using
1309-
// `enableAutoVariableSummaries`."
1310-
// },
1311-
// "error":
1312-
// "type": "string",
1313-
// "description": "An error message generated if LLDB couldn't inspect
1314-
// the variable."
13151331
// }
13161332
// }
13171333
// },
13181334
// "required": [ "name", "value", "variablesReference" ]
13191335
// }
1320-
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1321-
int64_t varID, bool format_hex,
1322-
bool is_name_duplicated,
1336+
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
1337+
bool format_hex, bool is_name_duplicated,
13231338
std::optional<std::string> custom_name) {
13241339
VariableDescription desc(v, format_hex, is_name_duplicated, custom_name);
13251340
llvm::json::Object object;
@@ -1364,12 +1379,18 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
13641379
}
13651380
}
13661381
EmplaceSafeString(object, "type", desc.display_type_name);
1367-
if (varID != INT64_MAX)
1368-
object.try_emplace("id", varID);
1382+
1383+
// A unique variable identifier to help in properly identifying variables with
1384+
// the same name. This is an extension to the VS protocol.
1385+
object.try_emplace("id", var_ref);
1386+
13691387
if (v.MightHaveChildren())
1370-
object.try_emplace("variablesReference", variablesReference);
1388+
object.try_emplace("variablesReference", var_ref);
13711389
else
1372-
object.try_emplace("variablesReference", (int64_t)0);
1390+
object.try_emplace("variablesReference", 0);
1391+
1392+
if (v.GetDeclaration().IsValid())
1393+
object.try_emplace("declarationLocationReference", var_ref);
13731394

13741395
if (lldb::addr_t addr = v.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS)
13751396
object.try_emplace("memoryReference", EncodeMemoryReference(addr));

lldb/tools/lldb-dap/JSONUtils.h

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

292+
/// Create a "Source" JSON object as described in the debug adaptor definition.
293+
///
294+
/// \param[in] file
295+
/// The SBFileSpec to use when populating out the "Source" object
296+
///
297+
/// \return
298+
/// A "Source" JSON object that follows the formal JSON
299+
/// definition outlined by Microsoft.
300+
llvm::json::Value CreateSource(const lldb::SBFileSpec &file);
301+
292302
/// Create a "Source" JSON object as described in the debug adaptor definition.
293303
///
294304
/// \param[in] line_entry
295305
/// The LLDB line table to use when populating out the "Source"
296306
/// object
297307
///
298308
/// \return
299-
/// A "Source" JSON object with that follows the formal JSON
309+
/// A "Source" JSON object that follows the formal JSON
300310
/// definition outlined by Microsoft.
301-
llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry);
311+
llvm::json::Value CreateSource(const lldb::SBLineEntry &line_entry);
302312

303313
/// Create a "Source" object for a given source path.
304314
///
@@ -470,15 +480,10 @@ struct VariableDescription {
470480
/// The LLDB value to use when populating out the "Variable"
471481
/// object.
472482
///
473-
/// \param[in] variablesReference
474-
/// The variable reference. Zero if this value isn't structured
475-
/// and has no children, non-zero if it does have children and
476-
/// might be asked to expand itself.
477-
///
478-
/// \param[in] varID
479-
/// A unique variable identifier to help in properly identifying
480-
/// variables with the same name. This is an extension to the
481-
/// VS protocol.
483+
/// \param[in] var_ref
484+
/// The variable reference. Used to identify the value, e.g.
485+
/// in the `variablesReference` or `declarationLocationReference`
486+
/// properties.
482487
///
483488
/// \param[in] format_hex
484489
/// It set to true the variable will be formatted as hex in
@@ -499,8 +504,8 @@ struct VariableDescription {
499504
/// \return
500505
/// A "Variable" JSON object with that follows the formal JSON
501506
/// definition outlined by Microsoft.
502-
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
503-
int64_t varID, bool format_hex,
507+
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
508+
bool format_hex,
504509
bool is_name_duplicated = false,
505510
std::optional<std::string> custom_name = {});
506511

0 commit comments

Comments
 (0)