-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb][lldb-dap] Implement jump to cursor #130503
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
da-viper
wants to merge
26
commits into
llvm:main
Choose a base branch
from
da-viper:implement-jump-to-cursor
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
aa8ce4d
[lldb-dap] implement jump to cursor.
da-viper d7342ea
[lldb][lldb-dap] add jump to cursor tests
da-viper d468264
[lldb-dap] Rever removing libXML version
da-viper bdfa47c
[lldb][lldb-dap] fix code format
da-viper 03f6598
Update lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
da-viper 5bec4a9
Update lldb/tools/lldb-dap/DAP.h
da-viper 31df5a3
Update lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
da-viper f02a341
[lldb][lldb-dap] add review commits
da-viper 988b9ce
[lldb][lldb-dap] add review changes
da-viper ca875e4
[lldb][lldb-dap] Update jump to cursor test
da-viper 6373d10
[lldb][lldb-dap] add new test for goto execute again.
da-viper 64a2b9c
[lldb][lldb-dap] add helper function `SendThreadGotoEvent`
da-viper ae46dce
[lldb][lldb-dap] Rename `gotoTarget` to `gotoTargets` and improve val…
da-viper 05a3ebb
Update lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
da-viper e8697c7
Update lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
da-viper 37d3487
Update lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
da-viper e038671
Update lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
da-viper 2d9b7a8
[lldb][lldb-dap] use breakpoints to verify goto location.
da-viper b36ca14
Add Release notes
da-viper 3977645
[lldb-dap] Update with review changes
da-viper cdaf3b4
[lldb-dap] update gototargets to use the new protocol
da-viper 9173d8c
[lldb-dap] Fix code format
da-viper 17e6c85
[lldb][lldb-dap] enable gotoTargets capability
da-viper 066f2e2
[lldb][lldb-dap] use std::vector instead of llvm::DenseMap.
da-viper 3ff3281
[lldb][lldb-dap] use llvm::smallVector as gotargets are usually few
da-viper 5c6140c
[lldb][lldb-dap] Update with review changes
da-viper File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
C_SOURCES := main.c | ||
|
||
include Makefile.rules |
145 changes: 145 additions & 0 deletions
145
lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
""" | ||
Test lldb-dap gotoTarget request | ||
""" | ||
|
||
from typing import Dict, Any | ||
from unittest import SkipTest | ||
|
||
from lldbsuite.test.lldbtest import line_number | ||
import lldbdap_testcase | ||
import os | ||
|
||
|
||
class TestDAP_gotoTargets(lldbdap_testcase.DAPTestCaseBase): | ||
def verify_variable( | ||
self, actual_dict: Dict[str, Any], expected_dict: Dict[str, Any] | ||
): | ||
for key, value in expected_dict.items(): | ||
actual_value = actual_dict[key] | ||
self.assertEqual( | ||
actual_value, | ||
value, | ||
f"values does not match for key: `{key}` expected_value: `{value}`, actual_value: `{actual_value}`", | ||
) | ||
|
||
def test_default(self): | ||
""" | ||
Tests the jump to cursor of a simple program. No arguments, | ||
environment, or anything else is specified. | ||
This does not run any statement between the current breakpoint | ||
and the jump line location. | ||
""" | ||
program = self.getBuildArtifact("a.out") | ||
self.build_and_launch(program) | ||
|
||
source_file = "main.c" | ||
self.source_path = os.path.join(os.getcwd(), source_file) | ||
self.set_source_breakpoints( | ||
source_file, [line_number(source_file, "// breakpoint 1")] | ||
) | ||
self.continue_to_next_stop() | ||
|
||
first_var_1_object = self.dap_server.get_local_variable("var_1") | ||
self.assertEqual(first_var_1_object["value"], "10") | ||
|
||
goto_line = line_number(source_file, "// goto 1") | ||
goto_column = 1 | ||
response = self.dap_server.request_gotoTargets( | ||
source_file, self.source_path, goto_line, goto_column | ||
) | ||
|
||
self.assertEqual( | ||
response["success"], True, "request for gotoTargets should be successful" | ||
) | ||
target = response["body"]["targets"][0] | ||
self.assertGreaterEqual( | ||
target["id"], 0, "targetId should be greater than or equal to zero" | ||
) | ||
|
||
target_id = target["id"] | ||
thread_id = self.dap_server.get_thread_id() | ||
self.assertIsNotNone(thread_id, "threadId should not be none") | ||
|
||
response = self.dap_server.request_goto(thread_id, target_id) | ||
self.assertEqual( | ||
response["success"], True, "goto request with targetId should be successful" | ||
) | ||
|
||
stopped_events = self.dap_server.wait_for_stopped(timeout=0.200) | ||
is_goto = lambda event: event["body"]["reason"] == "goto" | ||
has_goto_event = any(map(is_goto, stopped_events)) | ||
self.assertEqual( | ||
has_goto_event, True, "expected a stopped event with reason `goto`" | ||
) | ||
|
||
self.dap_server.request_next(thread_id) | ||
self.continue_to_next_stop() | ||
|
||
# Verify that `var_1=10` and `var_2=40`. This combination is only possible by | ||
# skipping execution of a line from the original program. Observing this combination | ||
# hence proves that our `goto` request actually skipped execution of the code line. | ||
var1_variable = self.dap_server.get_local_variable("var_1") | ||
var_1_expected = { | ||
"name": "var_1", | ||
"type": "int", | ||
"value": "10", | ||
"variablesReference": 0, | ||
} | ||
self.verify_variable(var1_variable, var_1_expected) | ||
|
||
var2_variable = self.dap_server.get_local_variable("var_2") | ||
var_2_expected = { | ||
"name": "var_2", | ||
"type": "int", | ||
"value": "40", | ||
"variablesReference": 0, | ||
} | ||
self.verify_variable(var2_variable, var_2_expected) | ||
|
||
self.continue_to_exit() | ||
|
||
def test_execute_again(self): | ||
program = self.getBuildArtifact("a.out") | ||
self.build_and_launch(program) | ||
|
||
source_file = "main.c" | ||
self.source_path = os.path.join(os.getcwd(), source_file) | ||
self.set_source_breakpoints( | ||
source_file, [line_number(source_file, "// breakpoint 2")] | ||
) | ||
self.continue_to_next_stop() | ||
|
||
end_var_3_value = self.dap_server.get_local_variable_value("var_3") | ||
self.assertEqual(end_var_3_value, "99") | ||
|
||
goto_line = line_number(source_file, "// goto 2") | ||
goto_column = 1 | ||
response = self.dap_server.request_gotoTargets( | ||
source_file, self.source_path, goto_line, goto_column | ||
) | ||
|
||
target = response["body"]["targets"][0] | ||
self.assertGreaterEqual( | ||
target["id"], 0, "targetId should be greater than or equal to zero" | ||
) | ||
|
||
target_id = target["id"] | ||
thread_id = self.dap_server.get_thread_id() | ||
self.assertIsNotNone(thread_id, "threadId should not be none") | ||
|
||
response = self.dap_server.request_goto(thread_id, target_id) | ||
self.assertEqual(response["success"], True, "expects success to go to targetId") | ||
|
||
stopped_events = self.dap_server.wait_for_stopped(timeout=0.200) # 200ms | ||
is_goto = lambda event: event["body"]["reason"] == "goto" | ||
has_goto_event = any(map(is_goto, stopped_events)) | ||
self.assertEqual(has_goto_event, True, "expects stopped event with reason goto") | ||
|
||
self.dap_server.request_next(thread_id) | ||
self.continue_to_next_stop() | ||
|
||
goto_var_3_value = self.dap_server.get_local_variable_value("var_3") | ||
self.assertEqual(goto_var_3_value, "10") | ||
|
||
self.continue_to_next_stop() | ||
self.continue_to_exit() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
|
||
int test_execute_again() { | ||
int var_3 = 10; // goto 2 | ||
|
||
var_3 = 99; | ||
|
||
return var_3; // breakpoint 2 | ||
} | ||
|
||
int main() { | ||
|
||
int var_1 = 10; | ||
|
||
var_1 = 20; // breakpoint 1 | ||
|
||
int var_2 = 40; // goto 1 | ||
|
||
int result = test_execute_again(); | ||
return 0; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
//===-- GoToRequestHandler.cpp --------------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "DAP.h" | ||
#include "EventHelper.h" | ||
#include "JSONUtils.h" | ||
#include "Protocol/ProtocolRequests.h" | ||
#include "RequestHandler.h" | ||
|
||
namespace lldb_dap { | ||
|
||
/// Creates an \p StoppedEvent with the reason \a goto. | ||
static void SendThreadGotoEvent(DAP &dap, lldb::tid_t thread_id) { | ||
llvm::json::Object event(CreateEventObject("stopped")); | ||
llvm::json::Object body; | ||
body.try_emplace("reason", "goto"); | ||
body.try_emplace("description", "Paused on Jump To Cursor"); | ||
body.try_emplace("threadId", thread_id); | ||
body.try_emplace("preserveFocusHint", false); | ||
body.try_emplace("allThreadsStopped", true); | ||
|
||
event.try_emplace("body", std::move(body)); | ||
dap.SendJSON(llvm::json::Value(std::move(event))); | ||
} | ||
|
||
llvm::Expected<protocol::GotoResponseBody> | ||
GotoRequestHandler::Run(const protocol::GotoArguments &args) const { | ||
const lldb::tid_t thread_id = args.threadId; | ||
lldb::SBThread current_thread = | ||
dap.target.GetProcess().GetThreadByID(thread_id); | ||
|
||
if (!current_thread.IsValid()) { | ||
return llvm::createStringError(llvm::formatv("Thread id `{0}` is not valid", | ||
current_thread.GetThreadID())); | ||
} | ||
|
||
const uint64_t target_id = args.targetId; | ||
const std::optional<lldb::SBLineEntry> line_entry = | ||
dap.gotos.GetLineEntry(target_id); | ||
if (!line_entry) { | ||
return llvm::createStringError( | ||
llvm::formatv("Target id `{0}` is not valid", thread_id)); | ||
} | ||
|
||
lldb::SBFileSpec file_spec = line_entry->GetFileSpec(); | ||
const lldb::SBError error = | ||
current_thread.JumpToLine(file_spec, line_entry->GetLine()); | ||
|
||
if (error.Fail()) { | ||
return llvm::make_error<DAPError>(error.GetCString()); | ||
} | ||
|
||
SendThreadGotoEvent(dap, thread_id); | ||
return protocol::GotoResponseBody(); | ||
} | ||
|
||
} // namespace lldb_dap |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.