Skip to content

Commit a740c69

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 tried to publish the declaration locations as value locations. However, it seems that VS-Code still has issues with correctly resolving file paths, as reported in microsoft/vscode#225546 (comment)
1 parent 82ee31f commit a740c69

File tree

9 files changed

+285
-102
lines changed

9 files changed

+285
-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
@@ -1079,6 +1079,17 @@ def request_setVariable(self, containingVarRef, name, value, id=None):
10791079
}
10801080
return self.send_recv(command_dict)
10811081

1082+
def request_locations(self, locationReference):
1083+
args_dict = {
1084+
"locationReference": locationReference,
1085+
}
1086+
command_dict = {
1087+
"command": "locations",
1088+
"type": "request",
1089+
"arguments": args_dict,
1090+
}
1091+
return self.send_recv(command_dict)
1092+
10821093
def request_testGetTargetBreakpoints(self):
10831094
"""A request packet used in the LLDB test suite to get all currently
10841095
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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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(locals["var1"]["declarationLocationReference"])
36+
self.assertTrue(loc_var1["success"])
37+
self.assertTrue(loc_var1["body"]["source"]["path"].endswith("main.c"))
38+
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: 9 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,24 @@ 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,
843+
bool is_permanent) {
844844
int64_t var_ref = GetNewVariableReference(is_permanent);
845845
if (is_permanent)
846-
expandable_permanent_variables.insert(std::make_pair(var_ref, variable));
846+
referenced_permanent_variables.insert(std::make_pair(var_ref, variable));
847847
else
848-
expandable_variables.insert(std::make_pair(var_ref, variable));
848+
referenced_variables.insert(std::make_pair(var_ref, variable));
849849
return var_ref;
850850
}
851851

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);
@@ -1143,65 +1146,75 @@ std::string VariableDescription::GetResult(llvm::StringRef context) {
11431146
// "description": "The number of indexed child variables. The client
11441147
// can use this optional information to present the
11451148
// children in a paged UI and fetch them in chunks."
1146-
// }
1147-
//
1149+
// },
1150+
// "declarationLocationReference": {
1151+
// "type": "integer",
1152+
// "description": "A reference that allows the client to request the
1153+
// location where the variable is declared. This should be
1154+
// present only if the adapter is likely to be able to
1155+
// resolve the location.\n\nThis reference shares the same
1156+
// lifetime as the `variablesReference`. See 'Lifetime of
1157+
// Object References' in the Overview section for
1158+
// details."
1159+
// },
11481160
//
11491161
// "$__lldb_extensions": {
11501162
// "description": "Unofficial extensions to the protocol",
11511163
// "properties": {
11521164
// "declaration": {
1153-
// "type": "object",
1154-
// "description": "The source location where the variable was declared.
1155-
// This value won't be present if no declaration is
1156-
// available.",
1157-
// "properties": {
1158-
// "path": {
1159-
// "type": "string",
1160-
// "description": "The source file path where the variable was
1161-
// declared."
1162-
// },
1163-
// "line": {
1164-
// "type": "number",
1165-
// "description": "The 1-indexed source line where the variable was
1166-
// declared."
1167-
// },
1168-
// "column": {
1169-
// "type": "number",
1170-
// "description": "The 1-indexed source column where the variable
1171-
// was declared."
1165+
// "type": "object",
1166+
// "description": "The source location where the variable was declared.
1167+
// This value won't be present if no declaration is
1168+
// available.
1169+
// Superseded by `declarationLocationReference`",
1170+
// "properties": {
1171+
// "path": {
1172+
// "type": "string",
1173+
// "description": "The source file path where the variable was
1174+
// declared."
1175+
// },
1176+
// "line": {
1177+
// "type": "number",
1178+
// "description": "The 1-indexed source line where the variable
1179+
// was declared."
1180+
// },
1181+
// "column": {
1182+
// "type": "number",
1183+
// "description": "The 1-indexed source column where the variable
1184+
// was declared."
1185+
// }
11721186
// }
1187+
// },
1188+
// "value": {
1189+
// "type": "string",
1190+
// "description": "The internal value of the variable as returned by
1191+
// This is effectively SBValue.GetValue(). The other
1192+
// `value` entry in the top-level variable response
1193+
// is, on the other hand, just a display string for
1194+
// the variable."
1195+
// },
1196+
// "summary": {
1197+
// "type": "string",
1198+
// "description": "The summary string of the variable. This is
1199+
// effectively SBValue.GetSummary()."
1200+
// },
1201+
// "autoSummary": {
1202+
// "type": "string",
1203+
// "description": "The auto generated summary if using
1204+
// `enableAutoVariableSummaries`."
1205+
// },
1206+
// "error": {
1207+
// "type": "string",
1208+
// "description": "An error message generated if LLDB couldn't inspect
1209+
// the variable."
11731210
// }
1174-
// },
1175-
// "value":
1176-
// "type": "string",
1177-
// "description": "The internal value of the variable as returned by
1178-
// This is effectively SBValue.GetValue(). The other
1179-
// `value` entry in the top-level variable response is,
1180-
// on the other hand, just a display string for the
1181-
// variable."
1182-
// },
1183-
// "summary":
1184-
// "type": "string",
1185-
// "description": "The summary string of the variable. This is
1186-
// effectively SBValue.GetSummary()."
1187-
// },
1188-
// "autoSummary":
1189-
// "type": "string",
1190-
// "description": "The auto generated summary if using
1191-
// `enableAutoVariableSummaries`."
1192-
// },
1193-
// "error":
1194-
// "type": "string",
1195-
// "description": "An error message generated if LLDB couldn't inspect
1196-
// the variable."
11971211
// }
11981212
// }
11991213
// },
12001214
// "required": [ "name", "value", "variablesReference" ]
12011215
// }
1202-
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
1203-
int64_t varID, bool format_hex,
1204-
bool is_name_duplicated,
1216+
llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
1217+
bool format_hex, bool is_name_duplicated,
12051218
std::optional<std::string> custom_name) {
12061219
VariableDescription desc(v, format_hex, is_name_duplicated, custom_name);
12071220
llvm::json::Object object;
@@ -1246,12 +1259,18 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
12461259
}
12471260
}
12481261
EmplaceSafeString(object, "type", desc.display_type_name);
1249-
if (varID != INT64_MAX)
1250-
object.try_emplace("id", varID);
1262+
1263+
// A unique variable identifier to help in properly identifying variables with
1264+
// the same name. This is an extension to the VS protocol.
1265+
object.try_emplace("id", var_ref);
1266+
12511267
if (v.MightHaveChildren())
1252-
object.try_emplace("variablesReference", variablesReference);
1268+
object.try_emplace("variablesReference", var_ref);
12531269
else
1254-
object.try_emplace("variablesReference", (int64_t)0);
1270+
object.try_emplace("variablesReference", 0);
1271+
1272+
if (v.GetDeclaration().IsValid())
1273+
object.try_emplace("declarationLocationReference", var_ref << 1);
12551274

12561275
object.try_emplace("$__lldb_extensions", desc.GetVariableExtensionsJSON());
12571276
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)