[Lldb-commits] [lldb] 6257a98 - [lldb-dap] Implement `StepGranularity` for "next" and "step-in" (#105464)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 21 11:30:13 PDT 2024
Author: Adrian Vogelsgesang
Date: 2024-08-21T20:30:10+02:00
New Revision: 6257a98b258a3f17b78af31bf43009a559c5dd1d
URL: https://github.com/llvm/llvm-project/commit/6257a98b258a3f17b78af31bf43009a559c5dd1d
DIFF: https://github.com/llvm/llvm-project/commit/6257a98b258a3f17b78af31bf43009a559c5dd1d.diff
LOG: [lldb-dap] Implement `StepGranularity` for "next" and "step-in" (#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.
Added:
Modified:
lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
lldb/tools/lldb-dap/lldb-dap.cpp
Removed:
################################################################################
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index a324af57b61df3..874383a13e2bb6 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -816,17 +816,21 @@ def request_launch(
self.wait_for_event(filter=["process", "initialized"])
return response
- def request_next(self, threadId):
+ def request_next(self, threadId, granularity="statement"):
if self.exit_status is not None:
raise ValueError("request_continue called after process exited")
- args_dict = {"threadId": threadId}
+ args_dict = {"threadId": threadId, "granularity": granularity}
command_dict = {"command": "next", "type": "request", "arguments": args_dict}
return self.send_recv(command_dict)
- def request_stepIn(self, threadId, targetId):
+ def request_stepIn(self, threadId, targetId, granularity="statement"):
if self.exit_status is not None:
raise ValueError("request_stepIn called after process exited")
- args_dict = {"threadId": threadId, "targetId": targetId}
+ args_dict = {
+ "threadId": threadId,
+ "targetId": targetId,
+ "granularity": granularity,
+ }
command_dict = {"command": "stepIn", "type": "request", "arguments": args_dict}
return self.send_recv(command_dict)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index a312a88ebd7e58..27545816f20707 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -222,14 +222,18 @@ def set_global(self, name, value, id=None):
"""Set a top level global variable only."""
return self.dap_server.request_setVariable(2, name, str(value), id=id)
- def stepIn(self, threadId=None, targetId=None, waitForStop=True):
- self.dap_server.request_stepIn(threadId=threadId, targetId=targetId)
+ def stepIn(
+ self, threadId=None, targetId=None, waitForStop=True, granularity="statement"
+ ):
+ self.dap_server.request_stepIn(
+ threadId=threadId, targetId=targetId, granularity=granularity
+ )
if waitForStop:
return self.dap_server.wait_for_stopped()
return None
- def stepOver(self, threadId=None, waitForStop=True):
- self.dap_server.request_next(threadId=threadId)
+ def stepOver(self, threadId=None, waitForStop=True, granularity="statement"):
+ self.dap_server.request_next(threadId=threadId, granularity=granularity)
if waitForStop:
return self.dap_server.wait_for_stopped()
return None
diff --git a/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py b/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
index 8a1bb76340be73..42a39e3c8c080b 100644
--- a/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
+++ b/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
@@ -68,5 +68,18 @@ def test_step(self):
self.assertEqual(x4, x3, "verify step over variable")
self.assertGreater(line4, line3, "verify step over line")
self.assertEqual(src1, src4, "verify step over source")
+
+ # Step a single assembly instruction.
+ # Unfortunately, there is no portable way to verify the correct
+ # stepping behavior here, because the generated assembly code
+ # depends highly on the compiler, its version, the operating
+ # system, and many more factors.
+ self.stepOver(
+ threadId=tid, waitForStop=True, granularity="instruction"
+ )
+ self.stepIn(
+ threadId=tid, waitForStop=True, granularity="instruction"
+ )
+
# only step one thread that is at the breakpoint and stop
break
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index f50a6c17310739..b534a48660a5f8 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -1677,6 +1677,9 @@ void request_initialize(const llvm::json::Object &request) {
body.try_emplace("supportsCompletionsRequest", true);
// The debug adapter supports the disassembly request.
body.try_emplace("supportsDisassembleRequest", true);
+ // The debug adapter supports stepping granularities (argument `granularity`)
+ // for the stepping requests.
+ body.try_emplace("supportsSteppingGranularity", true);
llvm::json::Array completion_characters;
completion_characters.emplace_back(".");
@@ -1985,6 +1988,14 @@ void request_launch(const llvm::json::Object &request) {
g_dap.SendJSON(CreateEventObject("initialized"));
}
+// Check if the step-granularity is `instruction`
+static bool hasInstructionGranularity(const llvm::json::Object &requestArgs) {
+ if (std::optional<llvm::StringRef> value =
+ requestArgs.getString("granularity"))
+ return value == "instruction";
+ return false;
+}
+
// "NextRequest": {
// "allOf": [ { "$ref": "#/definitions/Request" }, {
// "type": "object",
@@ -2012,6 +2023,11 @@ void request_launch(const llvm::json::Object &request) {
// "threadId": {
// "type": "integer",
// "description": "Execute 'next' for this thread."
+// },
+// "granularity": {
+// "$ref": "#/definitions/SteppingGranularity",
+// "description": "Stepping granularity. If no granularity is specified, a
+// granularity of `statement` is assumed."
// }
// },
// "required": [ "threadId" ]
@@ -2032,7 +2048,11 @@ void request_next(const llvm::json::Object &request) {
// Remember the thread ID that caused the resume so we can set the
// "threadCausedFocus" boolean value in the "stopped" events.
g_dap.focus_tid = thread.GetThreadID();
- thread.StepOver();
+ if (hasInstructionGranularity(*arguments)) {
+ thread.StepInstruction(/*step_over=*/true);
+ } else {
+ thread.StepOver();
+ }
} else {
response["success"] = llvm::json::Value(false);
}
@@ -3193,6 +3213,11 @@ void request_stackTrace(const llvm::json::Object &request) {
// "targetId": {
// "type": "integer",
// "description": "Optional id of the target to step into."
+// },
+// "granularity": {
+// "$ref": "#/definitions/SteppingGranularity",
+// "description": "Stepping granularity. If no granularity is specified, a
+// granularity of `statement` is assumed."
// }
// },
// "required": [ "threadId" ]
@@ -3223,7 +3248,11 @@ void request_stepIn(const llvm::json::Object &request) {
// Remember the thread ID that caused the resume so we can set the
// "threadCausedFocus" boolean value in the "stopped" events.
g_dap.focus_tid = thread.GetThreadID();
- thread.StepInto(step_in_target.c_str(), run_mode);
+ if (hasInstructionGranularity(*arguments)) {
+ thread.StepInstruction(/*step_over=*/false);
+ } else {
+ thread.StepInto(step_in_target.c_str(), run_mode);
+ }
} else {
response["success"] = llvm::json::Value(false);
}
More information about the lldb-commits
mailing list