Skip to content

Commit b9a7212

Browse files
vogelsgesangJDevlieghere
authored andcommitted
[lldb-dap] Provide declarationLocation for variables (llvm#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`. (cherry picked from commit 0cc2cd7)
1 parent 093bb34 commit b9a7212

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
@@ -824,7 +824,7 @@ void Variables::Clear() {
824824
locals.Clear();
825825
globals.Clear();
826826
registers.Clear();
827-
expandable_variables.clear();
827+
referenced_variables.clear();
828828
}
829829

830830
int64_t Variables::GetNewVariableReference(bool is_permanent) {
@@ -839,24 +839,23 @@ bool Variables::IsPermanentVariableReference(int64_t var_ref) {
839839

840840
lldb::SBValue Variables::GetVariable(int64_t var_ref) const {
841841
if (IsPermanentVariableReference(var_ref)) {
842-
auto pos = expandable_permanent_variables.find(var_ref);
843-
if (pos != expandable_permanent_variables.end())
842+
auto pos = referenced_permanent_variables.find(var_ref);
843+
if (pos != referenced_permanent_variables.end())
844844
return pos->second;
845845
} else {
846-
auto pos = expandable_variables.find(var_ref);
847-
if (pos != expandable_variables.end())
846+
auto pos = referenced_variables.find(var_ref);
847+
if (pos != referenced_variables.end())
848848
return pos->second;
849849
}
850850
return lldb::SBValue();
851851
}
852852

853-
int64_t Variables::InsertExpandableVariable(lldb::SBValue variable,
854-
bool is_permanent) {
853+
int64_t Variables::InsertVariable(lldb::SBValue variable, bool is_permanent) {
855854
int64_t var_ref = GetNewVariableReference(is_permanent);
856855
if (is_permanent)
857-
expandable_permanent_variables.insert(std::make_pair(var_ref, variable));
856+
referenced_permanent_variables.insert(std::make_pair(var_ref, variable));
858857
else
859-
expandable_variables.insert(std::make_pair(var_ref, variable));
858+
referenced_variables.insert(std::make_pair(var_ref, variable));
860859
return var_ref;
861860
}
862861

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);
@@ -1250,7 +1253,7 @@ std::string VariableDescription::GetResult(llvm::StringRef context) {
12501253
// "description": "The number of indexed child variables. The client
12511254
// can use this optional information to present the
12521255
// children in a paged UI and fetch them in chunks."
1253-
// }
1256+
// },
12541257
// "memoryReference": {
12551258
// "type": "string",
12561259
// "description": "A memory reference associated with this variable.
@@ -1260,63 +1263,75 @@ std::string VariableDescription::GetResult(llvm::StringRef context) {
12601263
// be used in a `disassemble` request. This attribute may
12611264
// be returned by a debug adapter if corresponding
12621265
// capability `supportsMemoryReferences` is true."
1263-
// },
1266+
// },
1267+
// "declarationLocationReference": {
1268+
// "type": "integer",
1269+
// "description": "A reference that allows the client to request the
1270+
// location where the variable is declared. This should be
1271+
// present only if the adapter is likely to be able to
1272+
// resolve the location.\n\nThis reference shares the same
1273+
// lifetime as the `variablesReference`. See 'Lifetime of
1274+
// Object References' in the Overview section for
1275+
// details."
1276+
// },
1277+
//
12641278
// "$__lldb_extensions": {
12651279
// "description": "Unofficial extensions to the protocol",
12661280
// "properties": {
12671281
// "declaration": {
1268-
// "type": "object",
1269-
// "description": "The source location where the variable was declared.
1270-
// This value won't be present if no declaration is
1271-
// available.",
1272-
// "properties": {
1273-
// "path": {
1274-
// "type": "string",
1275-
// "description": "The source file path where the variable was
1276-
// declared."
1277-
// },
1278-
// "line": {
1279-
// "type": "number",
1280-
// "description": "The 1-indexed source line where the variable was
1281-
// declared."
1282-
// },
1283-
// "column": {
1284-
// "type": "number",
1285-
// "description": "The 1-indexed source column where the variable
1286-
// was declared."
1282+
// "type": "object",
1283+
// "description": "The source location where the variable was
1284+
// declared. This value won't be present if no
1285+
// declaration is available.
1286+
// Superseded by `declarationLocationReference`",
1287+
// "properties": {
1288+
// "path": {
1289+
// "type": "string",
1290+
// "description": "The source file path where the variable was
1291+
// declared."
1292+
// },
1293+
// "line": {
1294+
// "type": "number",
1295+
// "description": "The 1-indexed source line where the variable
1296+
// was declared."
1297+
// },
1298+
// "column": {
1299+
// "type": "number",
1300+
// "description": "The 1-indexed source column where the variable
1301+
// was declared."
1302+
// }
12871303
// }
1304+
// },
1305+
// "value": {
1306+
// "type": "string",
1307+
// "description": "The internal value of the variable as returned by
1308+
// This is effectively SBValue.GetValue(). The other
1309+
// `value` entry in the top-level variable response
1310+
// is, on the other hand, just a display string for
1311+
// the variable."
1312+
// },
1313+
// "summary": {
1314+
// "type": "string",
1315+
// "description": "The summary string of the variable. This is
1316+
// effectively SBValue.GetSummary()."
1317+
// },
1318+
// "autoSummary": {
1319+
// "type": "string",
1320+
// "description": "The auto generated summary if using
1321+
// `enableAutoVariableSummaries`."
1322+
// },
1323+
// "error": {
1324+
// "type": "string",
1325+
// "description": "An error message generated if LLDB couldn't inspect
1326+
// the variable."
12881327
// }
1289-
// },
1290-
// "value":
1291-
// "type": "string",
1292-
// "description": "The internal value of the variable as returned by
1293-
// This is effectively SBValue.GetValue(). The other
1294-
// `value` entry in the top-level variable response is,
1295-
// on the other hand, just a display string for the
1296-
// variable."
1297-
// },
1298-
// "summary":
1299-
// "type": "string",
1300-
// "description": "The summary string of the variable. This is
1301-
// effectively SBValue.GetSummary()."
1302-
// },
1303-
// "autoSummary":
1304-
// "type": "string",
1305-
// "description": "The auto generated summary if using
1306-
// `enableAutoVariableSummaries`."
1307-
// },
1308-
// "error":
1309-
// "type": "string",
1310-
// "description": "An error message generated if LLDB couldn't inspect
1311-
// the variable."
13121328
// }
13131329
// }
13141330
// },
13151331
// "required": [ "name", "value", "variablesReference" ]
13161332
// }
1317-
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1318-
int64_t varID, bool format_hex,
1319-
bool is_name_duplicated,
1333+
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
1334+
bool format_hex, bool is_name_duplicated,
13201335
std::optional<std::string> custom_name) {
13211336
VariableDescription desc(v, format_hex, is_name_duplicated, custom_name);
13221337
llvm::json::Object object;
@@ -1361,12 +1376,18 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
13611376
}
13621377
}
13631378
EmplaceSafeString(object, "type", desc.display_type_name);
1364-
if (varID != INT64_MAX)
1365-
object.try_emplace("id", varID);
1379+
1380+
// A unique variable identifier to help in properly identifying variables with
1381+
// the same name. This is an extension to the VS protocol.
1382+
object.try_emplace("id", var_ref);
1383+
13661384
if (v.MightHaveChildren())
1367-
object.try_emplace("variablesReference", variablesReference);
1385+
object.try_emplace("variablesReference", var_ref);
13681386
else
1369-
object.try_emplace("variablesReference", (int64_t)0);
1387+
object.try_emplace("variablesReference", 0);
1388+
1389+
if (v.GetDeclaration().IsValid())
1390+
object.try_emplace("declarationLocationReference", var_ref);
13701391

13711392
if (lldb::addr_t addr = v.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS)
13721393
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)