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

+11
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
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)
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

+9-9
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

+5-5
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

+72-53
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

+18-13
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)