Skip to content

Commit 5326a10

Browse files
committed
[lldb-dap] Prevent using an implicit step-in.
When there is a function that is inlined at the current program counter. If you get the current `line_entry` using the program counter's address it will point to the location of the inline function that may be in another file. (this is in implicit step-in and should not happen what step over is called). Use the current frame to get the correct `line_entry`
1 parent 4e706ad commit 5326a10

File tree

7 files changed

+151
-7
lines changed

7 files changed

+151
-7
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,12 @@ def stepOver(
344344
granularity="statement",
345345
timeout=DEFAULT_TIMEOUT,
346346
):
347-
self.dap_server.request_next(threadId=threadId, granularity=granularity)
347+
response = self.dap_server.request_next(
348+
threadId=threadId, granularity=granularity
349+
)
350+
self.assertTrue(
351+
response["success"], f"next request failed: response {response}"
352+
)
348353
if waitForStop:
349354
return self.dap_server.wait_for_stopped(timeout)
350355
return None
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#ifndef NUMBER_H
2+
#define NUMBER_H
3+
4+
#include <cstddef>
5+
6+
template <std::size_t Size> class Numbers {
7+
public:
8+
using ValueType = int;
9+
using PointerType = ValueType *;
10+
using ReferenceType = ValueType &;
11+
12+
class Iterator {
13+
// all the operators needs to be inlined so that there is no call
14+
// instruction.
15+
public:
16+
__attribute__((always_inline)) explicit Iterator(PointerType ptr)
17+
: m_ptr(ptr) {}
18+
19+
__attribute__((always_inline)) Iterator &operator++() noexcept {
20+
++m_ptr;
21+
return *this;
22+
};
23+
24+
__attribute__((always_inline)) Iterator operator++(int) noexcept {
25+
Iterator iter = *this;
26+
m_ptr++;
27+
return iter;
28+
}
29+
30+
__attribute__((always_inline)) ReferenceType operator*() const noexcept {
31+
return *m_ptr;
32+
}
33+
__attribute__((always_inline)) bool
34+
operator==(const Iterator &iter) noexcept {
35+
return m_ptr == iter.m_ptr;
36+
}
37+
__attribute__((always_inline)) bool
38+
operator!=(const Iterator &iter) noexcept {
39+
return !(*this == iter);
40+
}
41+
42+
private:
43+
PointerType m_ptr;
44+
};
45+
46+
PointerType data() { return static_cast<PointerType>(m_buffer); }
47+
48+
Iterator begin() { return Iterator(data()); }
49+
Iterator cbegin() { return Iterator(data()); }
50+
Iterator end() { return Iterator(data() + Size); }
51+
Iterator cend() { return Iterator(data() + Size); }
52+
53+
private:
54+
int m_buffer[Size]{};
55+
};
56+
57+
#endif // NUMBER_H

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,44 @@ def test_step(self):
8383

8484
# only step one thread that is at the breakpoint and stop
8585
break
86+
87+
def test_step_over(self):
88+
"""
89+
Test stepping over when the program counter is in another file.
90+
"""
91+
# when in a for range loop the program counter will in the iterator header.
92+
# make sure the frame source file is correct.
93+
program = self.getBuildArtifact("a.out")
94+
self.build_and_launch(program)
95+
source = "main.cpp"
96+
breakpoint1_line = line_number(source, "// breakpoint 2")
97+
step_over_pos = line_number(source, "// position_after_step_over")
98+
lines = [breakpoint1_line]
99+
breakpoint_ids = self.set_source_breakpoints(source, lines)
100+
self.assertEqual(
101+
len(breakpoint_ids), len(lines), "expect correct number of breakpoints."
102+
)
103+
self.continue_to_breakpoints(breakpoint_ids)
104+
105+
thread_id = self.dap_server.get_thread_id()
106+
self.stepOver(thread_id)
107+
levels = 1
108+
frames = self.get_stackFrames(thread_id, 0, levels)
109+
self.assertEqual(len(frames), levels, "expect current number of frame levels.")
110+
top_frame = frames[0]
111+
self.assertEqual(
112+
top_frame["source"]["name"], source, "expect we are in the same file."
113+
)
114+
self.assertTrue(
115+
top_frame["source"]["path"].endswith(source),
116+
f"expect path ending with '{source}'.",
117+
)
118+
self.assertEqual(
119+
top_frame["line"],
120+
step_over_pos,
121+
f"expect step_over on line {step_over_pos}",
122+
)
123+
124+
# clear breakpoints
125+
self.set_source_breakpoints(source, [])
126+
self.continue_to_exit(exitCode=13)
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
1+
#include "Number.h"
2+
13
int function(int x) {
24
if ((x % 2) == 0)
35
return function(x - 1) + x; // breakpoint 1
46
else
57
return x;
68
}
79

8-
int main(int argc, char const *argv[]) { return function(2); }
10+
int function2() {
11+
Numbers<10> list;
12+
13+
int result = 0;
14+
for (const int val : list) // position_after_step_over
15+
result++; // breakpoint 2
16+
17+
return result;
18+
}
19+
20+
int main(int argc, char const *argv[]) {
21+
int func_result = function2();
22+
return function(2) + func_result; // returns 13
23+
}

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -581,19 +581,19 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
581581

582582
EmplaceSafeString(object, "name", frame_name);
583583

584-
auto target = frame.GetThread().GetProcess().GetTarget();
585-
auto source = CreateSource(frame.GetPCAddress(), target);
584+
lldb::SBTarget target = frame.GetThread().GetProcess().GetTarget();
585+
protocol::Source source = CreateSource(frame);
586+
586587
if (!IsAssemblySource(source)) {
587588
// This is a normal source with a valid line entry.
588-
auto line_entry = frame.GetLineEntry();
589+
lldb::SBLineEntry line_entry = frame.GetLineEntry();
589590
object.try_emplace("line", line_entry.GetLine());
590-
auto column = line_entry.GetColumn();
591+
uint32_t column = line_entry.GetColumn();
591592
object.try_emplace("column", column);
592593
} else if (frame.GetSymbol().IsValid()) {
593594
// This is a source where the disassembly is used, but there is a valid
594595
// symbol. Calculate the line of the current PC from the start of the
595596
// current symbol.
596-
lldb::SBTarget target = frame.GetThread().GetProcess().GetTarget();
597597
lldb::SBInstructionList inst_list = target.ReadInstructions(
598598
frame.GetSymbol().GetStartAddress(), frame.GetPCAddress(), nullptr);
599599
size_t inst_line = inst_list.GetSize();

lldb/tools/lldb-dap/ProtocolUtils.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,21 @@ protocol::Source CreateSource(lldb::SBAddress address, lldb::SBTarget &target) {
105105
return CreateSource(line_entry.GetFileSpec());
106106
}
107107

108+
protocol::Source CreateSource(lldb::SBFrame frame) {
109+
if (!frame.IsValid())
110+
return {};
111+
112+
const lldb::SBTarget target = frame.GetThread().GetProcess().GetTarget();
113+
lldb::SBDebugger debugger = target.GetDebugger();
114+
lldb::StopDisassemblyType stop_disassembly_display =
115+
GetStopDisassemblyDisplay(debugger);
116+
const lldb::SBAddress frame_pc = frame.GetPCAddress();
117+
if (ShouldDisplayAssemblySource(frame_pc, stop_disassembly_display))
118+
return CreateAssemblySource(target, frame_pc);
119+
120+
return CreateSource(frame.GetLineEntry().GetFileSpec());
121+
}
122+
108123
bool IsAssemblySource(const protocol::Source &source) {
109124
// According to the specification, a source must have either `path` or
110125
// `sourceReference` specified. We use `path` for sources with known source

lldb/tools/lldb-dap/ProtocolUtils.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ protocol::Source CreateSource(const lldb::SBFileSpec &file);
4242
/// definition outlined by Microsoft.
4343
protocol::Source CreateSource(lldb::SBAddress address, lldb::SBTarget &target);
4444

45+
/// Create a `protocol::Source` object as described in the debug adapter
46+
/// definition.
47+
///
48+
/// \param[in] frame
49+
/// The frame to use when populating the "Source" object.
50+
///
51+
/// \return
52+
/// A `protcol::Source` object that follows the formal JSON
53+
/// definition outlined by Microsoft.
54+
protocol::Source CreateSource(lldb::SBFrame frame);
55+
4556
/// Checks if the given source is for assembly code.
4657
bool IsAssemblySource(const protocol::Source &source);
4758

0 commit comments

Comments
 (0)