Skip to content

Commit 2901b39

Browse files
vogelsgesangcjdb
authored andcommitted
[lldb-dap] Implement StepGranularity for "next" and "step-in" (llvm#105464)
VS Code requests the `instruction` stepping granularity if the assembly view is currently focused. By implementing `StepGranularity`, we can hence properly single-step through assembly code.
1 parent 0b9bc5b commit 2901b39

File tree

4 files changed

+60
-10
lines changed

4 files changed

+60
-10
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -816,17 +816,21 @@ def request_launch(
816816
self.wait_for_event(filter=["process", "initialized"])
817817
return response
818818

819-
def request_next(self, threadId):
819+
def request_next(self, threadId, granularity="statement"):
820820
if self.exit_status is not None:
821821
raise ValueError("request_continue called after process exited")
822-
args_dict = {"threadId": threadId}
822+
args_dict = {"threadId": threadId, "granularity": granularity}
823823
command_dict = {"command": "next", "type": "request", "arguments": args_dict}
824824
return self.send_recv(command_dict)
825825

826-
def request_stepIn(self, threadId, targetId):
826+
def request_stepIn(self, threadId, targetId, granularity="statement"):
827827
if self.exit_status is not None:
828828
raise ValueError("request_stepIn called after process exited")
829-
args_dict = {"threadId": threadId, "targetId": targetId}
829+
args_dict = {
830+
"threadId": threadId,
831+
"targetId": targetId,
832+
"granularity": granularity,
833+
}
830834
command_dict = {"command": "stepIn", "type": "request", "arguments": args_dict}
831835
return self.send_recv(command_dict)
832836

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,14 +222,18 @@ def set_global(self, name, value, id=None):
222222
"""Set a top level global variable only."""
223223
return self.dap_server.request_setVariable(2, name, str(value), id=id)
224224

225-
def stepIn(self, threadId=None, targetId=None, waitForStop=True):
226-
self.dap_server.request_stepIn(threadId=threadId, targetId=targetId)
225+
def stepIn(
226+
self, threadId=None, targetId=None, waitForStop=True, granularity="statement"
227+
):
228+
self.dap_server.request_stepIn(
229+
threadId=threadId, targetId=targetId, granularity=granularity
230+
)
227231
if waitForStop:
228232
return self.dap_server.wait_for_stopped()
229233
return None
230234

231-
def stepOver(self, threadId=None, waitForStop=True):
232-
self.dap_server.request_next(threadId=threadId)
235+
def stepOver(self, threadId=None, waitForStop=True, granularity="statement"):
236+
self.dap_server.request_next(threadId=threadId, granularity=granularity)
233237
if waitForStop:
234238
return self.dap_server.wait_for_stopped()
235239
return None

lldb/test/API/tools/lldb-dap/step/TestDAP_step.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,18 @@ def test_step(self):
6868
self.assertEqual(x4, x3, "verify step over variable")
6969
self.assertGreater(line4, line3, "verify step over line")
7070
self.assertEqual(src1, src4, "verify step over source")
71+
72+
# Step a single assembly instruction.
73+
# Unfortunately, there is no portable way to verify the correct
74+
# stepping behavior here, because the generated assembly code
75+
# depends highly on the compiler, its version, the operating
76+
# system, and many more factors.
77+
self.stepOver(
78+
threadId=tid, waitForStop=True, granularity="instruction"
79+
)
80+
self.stepIn(
81+
threadId=tid, waitForStop=True, granularity="instruction"
82+
)
83+
7184
# only step one thread that is at the breakpoint and stop
7285
break

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

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,6 +1677,9 @@ void request_initialize(const llvm::json::Object &request) {
16771677
body.try_emplace("supportsCompletionsRequest", true);
16781678
// The debug adapter supports the disassembly request.
16791679
body.try_emplace("supportsDisassembleRequest", true);
1680+
// The debug adapter supports stepping granularities (argument `granularity`)
1681+
// for the stepping requests.
1682+
body.try_emplace("supportsSteppingGranularity", true);
16801683

16811684
llvm::json::Array completion_characters;
16821685
completion_characters.emplace_back(".");
@@ -1985,6 +1988,14 @@ void request_launch(const llvm::json::Object &request) {
19851988
g_dap.SendJSON(CreateEventObject("initialized"));
19861989
}
19871990

1991+
// Check if the step-granularity is `instruction`
1992+
static bool hasInstructionGranularity(const llvm::json::Object &requestArgs) {
1993+
if (std::optional<llvm::StringRef> value =
1994+
requestArgs.getString("granularity"))
1995+
return value == "instruction";
1996+
return false;
1997+
}
1998+
19881999
// "NextRequest": {
19892000
// "allOf": [ { "$ref": "#/definitions/Request" }, {
19902001
// "type": "object",
@@ -2012,6 +2023,11 @@ void request_launch(const llvm::json::Object &request) {
20122023
// "threadId": {
20132024
// "type": "integer",
20142025
// "description": "Execute 'next' for this thread."
2026+
// },
2027+
// "granularity": {
2028+
// "$ref": "#/definitions/SteppingGranularity",
2029+
// "description": "Stepping granularity. If no granularity is specified, a
2030+
// granularity of `statement` is assumed."
20152031
// }
20162032
// },
20172033
// "required": [ "threadId" ]
@@ -2032,7 +2048,11 @@ void request_next(const llvm::json::Object &request) {
20322048
// Remember the thread ID that caused the resume so we can set the
20332049
// "threadCausedFocus" boolean value in the "stopped" events.
20342050
g_dap.focus_tid = thread.GetThreadID();
2035-
thread.StepOver();
2051+
if (hasInstructionGranularity(*arguments)) {
2052+
thread.StepInstruction(/*step_over=*/true);
2053+
} else {
2054+
thread.StepOver();
2055+
}
20362056
} else {
20372057
response["success"] = llvm::json::Value(false);
20382058
}
@@ -3193,6 +3213,11 @@ void request_stackTrace(const llvm::json::Object &request) {
31933213
// "targetId": {
31943214
// "type": "integer",
31953215
// "description": "Optional id of the target to step into."
3216+
// },
3217+
// "granularity": {
3218+
// "$ref": "#/definitions/SteppingGranularity",
3219+
// "description": "Stepping granularity. If no granularity is specified, a
3220+
// granularity of `statement` is assumed."
31963221
// }
31973222
// },
31983223
// "required": [ "threadId" ]
@@ -3223,7 +3248,11 @@ void request_stepIn(const llvm::json::Object &request) {
32233248
// Remember the thread ID that caused the resume so we can set the
32243249
// "threadCausedFocus" boolean value in the "stopped" events.
32253250
g_dap.focus_tid = thread.GetThreadID();
3226-
thread.StepInto(step_in_target.c_str(), run_mode);
3251+
if (hasInstructionGranularity(*arguments)) {
3252+
thread.StepInstruction(/*step_over=*/false);
3253+
} else {
3254+
thread.StepInto(step_in_target.c_str(), run_mode);
3255+
}
32273256
} else {
32283257
response["success"] = llvm::json::Value(false);
32293258
}

0 commit comments

Comments
 (0)