Skip to content

Initial step in targets DAP support #86623

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

Merged
merged 10 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lldb/include/lldb/API/SBLineEntry.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class LLDB_API SBLineEntry {

lldb::SBAddress GetEndAddress() const;

lldb::SBAddress
GetSameLineContiguousAddressRangeEnd(bool include_inlined_functions) const;

Comment on lines +32 to +34
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this API right? We spoke about using existing APIs for this. I believe we can find the index the SBLineEntry in the line table using:

uint32_t SBCompileUnit::FindLineEntryIndex(lldb::SBLineEntry &line_entry, bool exact = false) const;

And then you can use:

lldb::SBLineEntry SBCompileUnit::GetLineEntryAtIndex(uint32_t idx) const;

With the next index until the line doesn't match.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clayborg, right, I know I can do that but I did not use it for two reasons:

  1. That seems to be what GetSameLineContiguousAddressRangeEnd API is doing internally so I prefer to reuse the API than implement myself.
  2. This seems to depend on the dwarf line table encoding which may not work for other platforms like PDB? Hiding behind GetSameLineContiguousAddressRangeEnd makes the code more resilient?

Anyway, if you still prefer us calling "GetLineEntryAtIndex" repeat, I will change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I take a second look at GetSameLineContiguousAddressRangeEnd implementation, it seems to take care of compiler generate code and inline functions which I find it is tedious to duplicate the implementation in lldb-dap:

if (*original_file_sp == *next_line_sc.line_entry.original_file_sp &&
(next_line_sc.line_entry.line == 0 ||
line == next_line_sc.line_entry.line)) {
// Include any line 0 entries - they indicate that this is compiler-
// generated code that does not correspond to user source code.
// next_line_sc is the same file & line as this LineEntry, so extend
// our AddressRange by its size and continue to see if there are more
// LineEntries that we can combine. However, if there was nothing to
// extend we're done.
if (!complete_line_range.Extend(next_line_sc.line_entry.range))
break;
continue;
}
if (include_inlined_functions && next_line_sc.block &&
next_line_sc.block->GetContainingInlinedBlock() != nullptr) {
// The next_line_sc might be in a different file if it's an inlined
// function. If this is the case then we still want to expand our line
// range to include them if the inlined function is at the same call site
// as this line entry. The current block could represent a nested inline
// function call so we need to need to check up the block tree to see if
// we find one.
auto inlined_parent_block =
next_line_sc.block->GetContainingInlinedBlockWithCallSite(
start_call_site);
if (!inlined_parent_block)
// We didn't find any parent inlined block with a call site at this line
// entry so this inlined function is probably at another line.
break;
// Extend our AddressRange by the size of the inlined block, but if there
// was nothing to add then we're done.
if (!complete_line_range.Extend(next_line_sc.line_entry.range))

So I think it makes more sense to reuse it. Please take a further review. Thanks.

explicit operator bool() const;

bool IsValid() const;
Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/API/SBSymbolContextList.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class LLDB_API SBSymbolContextList {
protected:
friend class SBModule;
friend class SBTarget;
friend class SBCompileUnit;

lldb_private::SymbolContextList *operator->() const;

Expand Down
4 changes: 4 additions & 0 deletions lldb/include/lldb/API/SBTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,10 @@ class LLDB_API SBTarget {
uint32_t count,
const char *flavor_string);

lldb::SBInstructionList ReadInstructions(lldb::SBAddress start_addr,
lldb::SBAddress end_addr,
const char *flavor_string);

lldb::SBInstructionList GetInstructions(lldb::SBAddress base_addr,
const void *buf, size_t size);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -811,23 +811,34 @@ def request_next(self, threadId):
command_dict = {"command": "next", "type": "request", "arguments": args_dict}
return self.send_recv(command_dict)

def request_stepIn(self, threadId):
def request_stepIn(self, threadId, targetId):
if self.exit_status is not None:
raise ValueError("request_continue called after process exited")
args_dict = {"threadId": threadId}
raise ValueError("request_stepIn called after process exited")
args_dict = {"threadId": threadId, "targetId": targetId}
command_dict = {"command": "stepIn", "type": "request", "arguments": args_dict}
return self.send_recv(command_dict)

def request_stepInTargets(self, frameId):
if self.exit_status is not None:
raise ValueError("request_stepInTargets called after process exited")
args_dict = {"frameId": frameId}
command_dict = {
"command": "stepInTargets",
"type": "request",
"arguments": args_dict,
}
return self.send_recv(command_dict)

def request_stepOut(self, threadId):
if self.exit_status is not None:
raise ValueError("request_continue called after process exited")
raise ValueError("request_stepOut called after process exited")
args_dict = {"threadId": threadId}
command_dict = {"command": "stepOut", "type": "request", "arguments": args_dict}
return self.send_recv(command_dict)

def request_pause(self, threadId=None):
if self.exit_status is not None:
raise ValueError("request_continue called after process exited")
raise ValueError("request_pause called after process exited")
if threadId is None:
threadId = self.get_thread_id()
args_dict = {"threadId": threadId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@ 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, waitForStop=True):
self.dap_server.request_stepIn(threadId=threadId)
def stepIn(self, threadId=None, targetId=None, waitForStop=True):
self.dap_server.request_stepIn(threadId=threadId, targetId=targetId)
if waitForStop:
return self.dap_server.wait_for_stopped()
return None
Expand Down
15 changes: 15 additions & 0 deletions lldb/source/API/SBLineEntry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ SBAddress SBLineEntry::GetEndAddress() const {
return sb_address;
}

SBAddress SBLineEntry::GetSameLineContiguousAddressRangeEnd(
bool include_inlined_functions) const {
LLDB_INSTRUMENT_VA(this);

SBAddress sb_address;
if (m_opaque_up) {
AddressRange line_range = m_opaque_up->GetSameLineContiguousAddressRange(
include_inlined_functions);

sb_address.SetAddress(line_range.GetBaseAddress());
sb_address.OffsetAddress(line_range.GetByteSize());
}
return sb_address;
}

Comment on lines +70 to +84
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this API and use existing ones I spoke about in the inline comment in the header file?

bool SBLineEntry::IsValid() const {
LLDB_INSTRUMENT_VA(this);
return this->operator bool();
Expand Down
24 changes: 24 additions & 0 deletions lldb/source/API/SBTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2011,6 +2011,30 @@ lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr,
return sb_instructions;
}

lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress start_addr,
lldb::SBAddress end_addr,
const char *flavor_string) {
LLDB_INSTRUMENT_VA(this, start_addr, end_addr, flavor_string);

SBInstructionList sb_instructions;

TargetSP target_sp(GetSP());
if (target_sp) {
lldb::addr_t start_load_addr = start_addr.GetLoadAddress(*this);
lldb::addr_t end_load_addr = end_addr.GetLoadAddress(*this);
if (end_load_addr > start_load_addr) {
lldb::addr_t size = end_load_addr - start_load_addr;

AddressRange range(start_load_addr, size);
const bool force_live_memory = true;
sb_instructions.SetDisassembler(Disassembler::DisassembleRange(
target_sp->GetArchitecture(), nullptr, flavor_string, *target_sp,
range, force_live_memory));
}
}
return sb_instructions;
}

lldb::SBInstructionList SBTarget::GetInstructions(lldb::SBAddress base_addr,
const void *buf,
size_t size) {
Expand Down
6 changes: 6 additions & 0 deletions lldb/test/API/tools/lldb-dap/stepInTargets/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

ENABLE_THREADS := YES

CXX_SOURCES := main.cpp

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""
Test lldb-dap stepInTargets request
"""

import dap_server
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
import lldbdap_testcase
from lldbsuite.test import lldbutil


class TestDAP_stepInTargets(lldbdap_testcase.DAPTestCaseBase):
@skipIf(
archs=no_match(["x86_64"])
) # InstructionControlFlowKind for ARM is not supported yet.
def test_basic(self):
"""
Tests the basic stepping in targets with directly calls.
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = "main.cpp"

breakpoint_line = line_number(source, "// set breakpoint here")
lines = [breakpoint_line]
# Set breakpoint in the thread function so we can step the threads
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEqual(
len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
)
self.continue_to_breakpoints(breakpoint_ids)

threads = self.dap_server.get_threads()
self.assertEqual(len(threads), 1, "expect one thread")
tid = threads[0]["id"]

leaf_frame = self.dap_server.get_stackFrame()
self.assertIsNotNone(leaf_frame, "expect a leaf frame")

# Request all step in targets list and verify the response.
step_in_targets_response = self.dap_server.request_stepInTargets(
leaf_frame["id"]
)
self.assertEqual(step_in_targets_response["success"], True, "expect success")
self.assertIn(
"body", step_in_targets_response, "expect body field in response body"
)
self.assertIn(
"targets",
step_in_targets_response["body"],
"expect targets field in response body",
)

step_in_targets = step_in_targets_response["body"]["targets"]
self.assertEqual(len(step_in_targets), 3, "expect 3 step in targets")

# Verify the target names are correct.
self.assertEqual(step_in_targets[0]["label"], "bar()", "expect bar()")
self.assertEqual(step_in_targets[1]["label"], "bar2()", "expect bar2()")
self.assertEqual(
step_in_targets[2]["label"], "foo(int, int)", "expect foo(int, int)"
)

# Choose to step into second target and verify that we are in bar2()
self.stepIn(threadId=tid, targetId=step_in_targets[1]["id"], waitForStop=True)
leaf_frame = self.dap_server.get_stackFrame()
self.assertIsNotNone(leaf_frame, "expect a leaf frame")
self.assertEqual(leaf_frame["name"], "bar2()")
11 changes: 11 additions & 0 deletions lldb/test/API/tools/lldb-dap/stepInTargets/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

int foo(int val, int extra) { return val + extra; }

int bar() { return 22; }

int bar2() { return 54; }

int main(int argc, char const *argv[]) {
foo(bar(), bar2()); // set breakpoint here
return 0;
}
2 changes: 2 additions & 0 deletions lldb/tools/lldb-dap/DAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ struct DAP {
std::vector<std::string> exit_commands;
std::vector<std::string> stop_commands;
std::vector<std::string> terminate_commands;
// Map step in target id to list of function targets that user can choose.
llvm::DenseMap<lldb::addr_t, std::string> step_in_targets;
// A copy of the last LaunchRequest or AttachRequest so we can reuse its
// arguments if we get a RestartRequest.
std::optional<llvm::json::Object> last_launch_or_attach_request;
Expand Down
Loading