Skip to content

Commit 7d9e788

Browse files
vogelsgesangJDevlieghere
authored andcommitted
[lldb-dap] Implement value locations for function pointers (llvm#104589)
This commit adds `valueLocationReference` to function pointers and function references. Thereby, users can navigate directly to the pointed-to function from within the "variables" pane. In general, it would be useful to also a add similar location references also to member function pointers, `std::source_location`, `std::function`, and many more. Doing so would require extending the formatters to provide such a source code location. There were two RFCs about this a while ago: https://discourse.llvm.org/t/rfc-extending-formatters-with-a-source-code-reference/68375 https://discourse.llvm.org/t/rfc-sbvalue-metadata-provider/68377/26 However, both RFCs ended without a conclusion. As such, this commit now implements the lowest-hanging fruit, i.e. function pointers. If people find it useful, I will revive the RFC afterwards. (cherry picked from commit 9f8ae78)
1 parent 2e26e9a commit 7d9e788

File tree

7 files changed

+192
-40
lines changed

7 files changed

+192
-40
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
C_SOURCES := main.c
1+
CXX_SOURCES := main.cpp
22

33
include Makefile.rules

lldb/test/API/tools/lldb-dap/locations/TestDAP_locations.py

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ def test_locations(self):
1919
"""
2020
program = self.getBuildArtifact("a.out")
2121
self.build_and_launch(program)
22-
source = "main.c"
22+
source = "main.cpp"
2323
self.source_path = os.path.join(os.getcwd(), source)
2424
self.set_source_breakpoints(
2525
source,
26-
[line_number(source, "// BREAK HERE")],
26+
[line_number(source, "break here")],
2727
)
2828
self.continue_to_next_stop()
2929

@@ -36,5 +36,46 @@ def test_locations(self):
3636
locals["var1"]["declarationLocationReference"]
3737
)
3838
self.assertTrue(loc_var1["success"])
39-
self.assertTrue(loc_var1["body"]["source"]["path"].endswith("main.c"))
40-
self.assertEqual(loc_var1["body"]["line"], 2)
39+
self.assertTrue(loc_var1["body"]["source"]["path"].endswith("main.cpp"))
40+
self.assertEqual(loc_var1["body"]["line"], 6)
41+
42+
# func_ptr has both a declaration and a valueLocation
43+
self.assertIn("declarationLocationReference", locals["func_ptr"].keys())
44+
self.assertIn("valueLocationReference", locals["func_ptr"].keys())
45+
decl_loc_func_ptr = self.dap_server.request_locations(
46+
locals["func_ptr"]["declarationLocationReference"]
47+
)
48+
self.assertTrue(decl_loc_func_ptr["success"])
49+
self.assertTrue(
50+
decl_loc_func_ptr["body"]["source"]["path"].endswith("main.cpp")
51+
)
52+
self.assertEqual(decl_loc_func_ptr["body"]["line"], 7)
53+
val_loc_func_ptr = self.dap_server.request_locations(
54+
locals["func_ptr"]["valueLocationReference"]
55+
)
56+
self.assertTrue(val_loc_func_ptr["success"])
57+
self.assertTrue(val_loc_func_ptr["body"]["source"]["path"].endswith("main.cpp"))
58+
self.assertEqual(val_loc_func_ptr["body"]["line"], 3)
59+
60+
# func_ref has both a declaration and a valueLocation
61+
self.assertIn("declarationLocationReference", locals["func_ref"].keys())
62+
self.assertIn("valueLocationReference", locals["func_ref"].keys())
63+
decl_loc_func_ref = self.dap_server.request_locations(
64+
locals["func_ref"]["declarationLocationReference"]
65+
)
66+
self.assertTrue(decl_loc_func_ref["success"])
67+
self.assertTrue(
68+
decl_loc_func_ref["body"]["source"]["path"].endswith("main.cpp")
69+
)
70+
self.assertEqual(decl_loc_func_ref["body"]["line"], 8)
71+
val_loc_func_ref = self.dap_server.request_locations(
72+
locals["func_ref"]["valueLocationReference"]
73+
)
74+
self.assertTrue(val_loc_func_ref["success"])
75+
self.assertTrue(val_loc_func_ref["body"]["source"]["path"].endswith("main.cpp"))
76+
self.assertEqual(val_loc_func_ref["body"]["line"], 3)
77+
78+
# `evaluate` responses for function pointers also have locations associated
79+
eval_res = self.dap_server.request_evaluate("greet")
80+
self.assertTrue(eval_res["success"])
81+
self.assertIn("valueLocationReference", eval_res["body"].keys())

lldb/test/API/tools/lldb-dap/locations/main.c

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <cstdio>
2+
3+
void greet() { printf("Hello"); }
4+
5+
int main(void) {
6+
int var1 = 1;
7+
void (*func_ptr)() = &greet;
8+
void (&func_ref)() = greet;
9+
return 0; // break here
10+
}

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,25 @@ std::string VariableDescription::GetResult(llvm::StringRef context) {
11951195
return description.trim().str();
11961196
}
11971197

1198+
bool ValuePointsToCode(lldb::SBValue v) {
1199+
if (!v.GetType().GetPointeeType().IsFunctionType())
1200+
return false;
1201+
1202+
lldb::addr_t addr = v.GetValueAsAddress();
1203+
lldb::SBLineEntry line_entry =
1204+
g_dap.target.ResolveLoadAddress(addr).GetLineEntry();
1205+
1206+
return line_entry.IsValid();
1207+
}
1208+
1209+
int64_t PackLocation(int64_t var_ref, bool is_value_location) {
1210+
return var_ref << 1 | is_value_location;
1211+
}
1212+
1213+
std::pair<int64_t, bool> UnpackLocation(int64_t location_id) {
1214+
return std::pair{location_id >> 1, location_id & 1};
1215+
}
1216+
11981217
// "Variable": {
11991218
// "type": "object",
12001219
// "description": "A Variable is a name/value pair. Optionally a variable
@@ -1274,6 +1293,18 @@ std::string VariableDescription::GetResult(llvm::StringRef context) {
12741293
// Object References' in the Overview section for
12751294
// details."
12761295
// },
1296+
// "valueLocationReference": {
1297+
// "type": "integer",
1298+
// "description": "A reference that allows the client to request the
1299+
// location where the variable's value is declared. For
1300+
// example, if the variable contains a function pointer,
1301+
// the adapter may be able to look up the function's
1302+
// location. This should be present only if the adapter
1303+
// is likely to be able to resolve the location.\n\nThis
1304+
// reference shares the same lifetime as the
1305+
// `variablesReference`. See 'Lifetime of Object
1306+
// References' in the Overview section for details."
1307+
// },
12771308
//
12781309
// "$__lldb_extensions": {
12791310
// "description": "Unofficial extensions to the protocol",
@@ -1387,7 +1418,11 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
13871418
object.try_emplace("variablesReference", 0);
13881419

13891420
if (v.GetDeclaration().IsValid())
1390-
object.try_emplace("declarationLocationReference", var_ref);
1421+
object.try_emplace("declarationLocationReference",
1422+
PackLocation(var_ref, false));
1423+
1424+
if (ValuePointsToCode(v))
1425+
object.try_emplace("valueLocationReference", PackLocation(var_ref, true));
13911426

13921427
if (lldb::addr_t addr = v.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS)
13931428
object.try_emplace("memoryReference", EncodeMemoryReference(addr));
@@ -1413,8 +1448,8 @@ CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
14131448
llvm::StringRef comm_file,
14141449
lldb::pid_t debugger_pid) {
14151450
llvm::json::Object run_in_terminal_args;
1416-
// This indicates the IDE to open an embedded terminal, instead of opening the
1417-
// terminal in a new window.
1451+
// This indicates the IDE to open an embedded terminal, instead of opening
1452+
// the terminal in a new window.
14181453
run_in_terminal_args.try_emplace("kind", "integrated");
14191454

14201455
auto launch_request_arguments = launch_request.getObject("arguments");

lldb/tools/lldb-dap/JSONUtils.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,16 @@ struct VariableDescription {
458458
std::string GetResult(llvm::StringRef context);
459459
};
460460

461+
/// Does the given variable have an associated value location?
462+
bool ValuePointsToCode(lldb::SBValue v);
463+
464+
/// Pack a location into a single integer which we can send via
465+
/// the debug adapter protocol.
466+
int64_t PackLocation(int64_t var_ref, bool is_value_location);
467+
468+
/// Reverse of `PackLocation`
469+
std::pair<int64_t, bool> UnpackLocation(int64_t location_id);
470+
461471
/// Create a "Variable" object for a LLDB thread object.
462472
///
463473
/// This function will fill in the following keys in the returned

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 88 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,19 @@ void request_completions(const llvm::json::Object &request) {
15541554
// client can use this optional information to
15551555
// present the variables in a paged UI and fetch
15561556
// them in chunks."
1557+
// },
1558+
// "valueLocationReference": {
1559+
// "type": "integer",
1560+
// "description": "A reference that allows the client to request
1561+
// the location where the returned value is
1562+
// declared. For example, if a function pointer is
1563+
// returned, the adapter may be able to look up the
1564+
// function's location. This should be present only
1565+
// if the adapter is likely to be able to resolve
1566+
// the location.\n\nThis reference shares the same
1567+
// lifetime as the `variablesReference`. See
1568+
// 'Lifetime of Object References' in the
1569+
// Overview section for details."
15571570
// }
15581571
// "memoryReference": {
15591572
// "type": "string",
@@ -1640,16 +1653,19 @@ void request_evaluate(const llvm::json::Object &request) {
16401653
VariableDescription desc(value);
16411654
EmplaceSafeString(body, "result", desc.GetResult(context));
16421655
EmplaceSafeString(body, "type", desc.display_type_name);
1643-
if (value.MightHaveChildren()) {
1644-
auto variableReference = g_dap.variables.InsertVariable(
1656+
int64_t var_ref = 0;
1657+
if (value.MightHaveChildren() || ValuePointsToCode(value))
1658+
var_ref = g_dap.variables.InsertVariable(
16451659
value, /*is_permanent=*/context == "repl");
1646-
body.try_emplace("variablesReference", variableReference);
1647-
} else {
1660+
if (value.MightHaveChildren())
1661+
body.try_emplace("variablesReference", var_ref);
1662+
else
16481663
body.try_emplace("variablesReference", (int64_t)0);
1649-
}
16501664
if (lldb::addr_t addr = value.GetLoadAddress();
16511665
addr != LLDB_INVALID_ADDRESS)
16521666
body.try_emplace("memoryReference", EncodeMemoryReference(addr));
1667+
if (ValuePointsToCode(value))
1668+
body.try_emplace("valueLocationReference", var_ref);
16531669
}
16541670
}
16551671
response.try_emplace("body", std::move(body));
@@ -3764,6 +3780,17 @@ void request_threads(const llvm::json::Object &request) {
37643780
// "description": "The number of indexed child variables. The client
37653781
// can use this optional information to present the variables in a
37663782
// paged UI and fetch them in chunks."
3783+
// },
3784+
// "valueLocationReference": {
3785+
// "type": "integer",
3786+
// "description": "A reference that allows the client to request the
3787+
// location where the new value is declared. For example, if the new
3788+
// value is function pointer, the adapter may be able to look up the
3789+
// function's location. This should be present only if the adapter
3790+
// is likely to be able to resolve the location.\n\nThis reference
3791+
// shares the same lifetime as the `variablesReference`. See
3792+
// 'Lifetime of Object References' in the Overview section for
3793+
// details."
37673794
// }
37683795
// },
37693796
// "required": [ "value" ]
@@ -3788,7 +3815,6 @@ void request_setVariable(const llvm::json::Object &request) {
37883815
response.try_emplace("success", false);
37893816

37903817
lldb::SBValue variable;
3791-
int64_t newVariablesReference = 0;
37923818

37933819
// The "id" is the unique integer ID that is unique within the enclosing
37943820
// variablesReference. It is optionally added to any "interface Variable"
@@ -3818,14 +3844,17 @@ void request_setVariable(const llvm::json::Object &request) {
38183844
// so always insert a new one to get its variablesReference.
38193845
// is_permanent is false because debug console does not support
38203846
// setVariable request.
3847+
int64_t new_var_ref =
3848+
g_dap.variables.InsertVariable(variable, /*is_permanent=*/false);
38213849
if (variable.MightHaveChildren())
3822-
newVariablesReference =
3823-
g_dap.variables.InsertVariable(variable, /*is_permanent=*/false);
3824-
body.try_emplace("variablesReference", newVariablesReference);
3825-
3850+
body.try_emplace("variablesReference", new_var_ref);
3851+
else
3852+
body.try_emplace("variablesReference", 0);
38263853
if (lldb::addr_t addr = variable.GetLoadAddress();
38273854
addr != LLDB_INVALID_ADDRESS)
38283855
body.try_emplace("memoryReference", EncodeMemoryReference(addr));
3856+
if (ValuePointsToCode(variable))
3857+
body.try_emplace("valueLocationReference", new_var_ref);
38293858
} else {
38303859
EmplaceSafeString(body, "message", std::string(error.GetCString()));
38313860
}
@@ -4116,32 +4145,64 @@ void request_variables(const llvm::json::Object &request) {
41164145
void request_locations(const llvm::json::Object &request) {
41174146
llvm::json::Object response;
41184147
FillResponse(request, response);
4119-
auto arguments = request.getObject("arguments");
4148+
auto *arguments = request.getObject("arguments");
41204149

4121-
uint64_t reference_id = GetUnsigned(arguments, "locationReference", 0);
4122-
lldb::SBValue variable = g_dap.variables.GetVariable(reference_id);
4150+
uint64_t location_id = GetUnsigned(arguments, "locationReference", 0);
4151+
// We use the lowest bit to distinguish between value location and declaration
4152+
// location
4153+
auto [var_ref, is_value_location] = UnpackLocation(location_id);
4154+
lldb::SBValue variable = g_dap.variables.GetVariable(var_ref);
41234155
if (!variable.IsValid()) {
41244156
response["success"] = false;
41254157
response["message"] = "Invalid variable reference";
41264158
g_dap.SendJSON(llvm::json::Value(std::move(response)));
41274159
return;
41284160
}
41294161

4130-
// Get the declaration location
4131-
lldb::SBDeclaration decl = variable.GetDeclaration();
4132-
if (!decl.IsValid()) {
4133-
response["success"] = false;
4134-
response["message"] = "No declaration location available";
4135-
g_dap.SendJSON(llvm::json::Value(std::move(response)));
4136-
return;
4137-
}
4138-
41394162
llvm::json::Object body;
4140-
body.try_emplace("source", CreateSource(decl.GetFileSpec()));
4141-
if (int line = decl.GetLine())
4142-
body.try_emplace("line", line);
4143-
if (int column = decl.GetColumn())
4144-
body.try_emplace("column", column);
4163+
if (is_value_location) {
4164+
// Get the value location
4165+
if (!variable.GetType().IsPointerType() &&
4166+
!variable.GetType().IsReferenceType()) {
4167+
response["success"] = false;
4168+
response["message"] =
4169+
"Value locations are only available for pointers and references";
4170+
g_dap.SendJSON(llvm::json::Value(std::move(response)));
4171+
return;
4172+
}
4173+
4174+
lldb::addr_t addr = variable.GetValueAsAddress();
4175+
lldb::SBLineEntry line_entry =
4176+
g_dap.target.ResolveLoadAddress(addr).GetLineEntry();
4177+
4178+
if (!line_entry.IsValid()) {
4179+
response["success"] = false;
4180+
response["message"] = "Failed to resolve line entry for location";
4181+
g_dap.SendJSON(llvm::json::Value(std::move(response)));
4182+
return;
4183+
}
4184+
4185+
body.try_emplace("source", CreateSource(line_entry.GetFileSpec()));
4186+
if (int line = line_entry.GetLine())
4187+
body.try_emplace("line", line);
4188+
if (int column = line_entry.GetColumn())
4189+
body.try_emplace("column", column);
4190+
} else {
4191+
// Get the declaration location
4192+
lldb::SBDeclaration decl = variable.GetDeclaration();
4193+
if (!decl.IsValid()) {
4194+
response["success"] = false;
4195+
response["message"] = "No declaration location available";
4196+
g_dap.SendJSON(llvm::json::Value(std::move(response)));
4197+
return;
4198+
}
4199+
4200+
body.try_emplace("source", CreateSource(decl.GetFileSpec()));
4201+
if (int line = decl.GetLine())
4202+
body.try_emplace("line", line);
4203+
if (int column = decl.GetColumn())
4204+
body.try_emplace("column", column);
4205+
}
41454206

41464207
response.try_emplace("body", std::move(body));
41474208
g_dap.SendJSON(llvm::json::Value(std::move(response)));

0 commit comments

Comments
 (0)