Skip to content

Commit 89c27d6

Browse files
santhoshe447Santhosh Kumar EllendulaSanthosh Kumar Ellendula
authored
[lldb-dap] Enabling instruction breakpoint support to lldb-dap. (#105278)
Added support for "supportsInstructionBreakpoints" capability and now it this command is triggered when we set instruction breakpoint. We need this support as part of enabling disassembly view debugging. Following features should work as part of this feature enablement: 1. Settings breakpoints in disassembly view: Unsetting the breakpoint is not happening from the disassembly view. Currently we need to unset breakpoint manually from the breakpoint List. Multiple breakpoints are getting set for the same $ 2. Step over, step into, continue in the disassembly view The format for DisassembleRequest and DisassembleResponse at https://raw.githubusercontent.com/microsoft/vscode/master/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts . Ref Images: Set instruction breakpoint in disassembly view: ![image](https://github.com/user-attachments/assets/833bfb34-86f4-40e2-8c20-14b638a612a2) After issuing continue: ![image](https://github.com/user-attachments/assets/884572a3-915e-422b-b8dd-d132e5c00de6) --------- Co-authored-by: Santhosh Kumar Ellendula <[email protected]> Co-authored-by: Santhosh Kumar Ellendula <[email protected]>
1 parent e1d2251 commit 89c27d6

File tree

14 files changed

+577
-3
lines changed

14 files changed

+577
-3
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,20 @@ def terminate(self):
10991099
self.send.close()
11001100
# self.recv.close()
11011101

1102+
def request_setInstructionBreakpoints(self, memory_reference=[]):
1103+
breakpoints = []
1104+
for i in memory_reference:
1105+
args_dict = {
1106+
"instructionReference": i,
1107+
}
1108+
breakpoints.append(args_dict)
1109+
args_dict = {"breakpoints": breakpoints}
1110+
command_dict = {
1111+
"command": "setInstructionBreakpoints",
1112+
"type": "request",
1113+
"arguments": args_dict,
1114+
}
1115+
return self.send_recv(command_dict)
11021116

11031117
class DebugAdaptorServer(DebugCommunication):
11041118
def __init__(

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ def verify_breakpoint_hit(self, breakpoint_ids):
8181
body = stopped_event["body"]
8282
if "reason" not in body:
8383
continue
84-
if body["reason"] != "breakpoint":
84+
if (
85+
body["reason"] != "breakpoint"
86+
and body["reason"] != "instruction breakpoint"
87+
):
8588
continue
8689
if "description" not in body:
8790
continue
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CXX_SOURCES := main-copy.cpp
2+
CXXFLAGS_EXTRAS := -O0 -g
3+
include Makefile.rules
4+
5+
main-copy.cpp: main.cpp
6+
cp -f $< $@
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import dap_server
2+
import shutil
3+
from lldbsuite.test.decorators import *
4+
from lldbsuite.test.lldbtest import *
5+
from lldbsuite.test import lldbutil
6+
import lldbdap_testcase
7+
import os
8+
import lldb
9+
10+
11+
class TestDAP_InstructionBreakpointTestCase(lldbdap_testcase.DAPTestCaseBase):
12+
NO_DEBUG_INFO_TESTCASE = True
13+
14+
def setUp(self):
15+
lldbdap_testcase.DAPTestCaseBase.setUp(self)
16+
17+
self.main_basename = "main-copy.cpp"
18+
self.main_path = os.path.realpath(self.getBuildArtifact(self.main_basename))
19+
20+
def test_instruction_breakpoint(self):
21+
self.build()
22+
self.instruction_breakpoint_test()
23+
24+
def instruction_breakpoint_test(self):
25+
"""Sample test to ensure SBFrame::Disassemble produces SOME output"""
26+
# Create a target by the debugger.
27+
target = self.createTestTarget()
28+
29+
main_line = line_number("main.cpp", "breakpoint 1")
30+
31+
program = self.getBuildArtifact("a.out")
32+
self.build_and_launch(program)
33+
34+
# Set source breakpoint 1
35+
response = self.dap_server.request_setBreakpoints(self.main_path, [main_line])
36+
breakpoints = response["body"]["breakpoints"]
37+
self.assertEquals(len(breakpoints), 1)
38+
breakpoint = breakpoints[0]
39+
self.assertEqual(
40+
breakpoint["line"], main_line, "incorrect breakpoint source line"
41+
)
42+
self.assertTrue(breakpoint["verified"], "breakpoint is not verified")
43+
self.assertEqual(
44+
self.main_basename, breakpoint["source"]["name"], "incorrect source name"
45+
)
46+
self.assertEqual(
47+
self.main_path, breakpoint["source"]["path"], "incorrect source file path"
48+
)
49+
other_breakpoint_id = breakpoint["id"]
50+
51+
# Continue and then verifiy the breakpoint
52+
self.dap_server.request_continue()
53+
self.verify_breakpoint_hit([other_breakpoint_id])
54+
55+
# now we check the stack trace making sure that we got mapped source paths
56+
frames = self.dap_server.request_stackTrace()["body"]["stackFrames"]
57+
intstructionPointerReference = []
58+
setIntstructionBreakpoints = []
59+
intstructionPointerReference.append(frames[0]["instructionPointerReference"])
60+
self.assertEqual(
61+
frames[0]["source"]["name"], self.main_basename, "incorrect source name"
62+
)
63+
self.assertEqual(
64+
frames[0]["source"]["path"], self.main_path, "incorrect source file path"
65+
)
66+
67+
# Check disassembly view
68+
instruction = self.disassemble(frameIndex=0)
69+
self.assertEqual(
70+
instruction["address"],
71+
intstructionPointerReference[0],
72+
"current breakpoint reference is not in the disaasembly view",
73+
)
74+
75+
# Get next instruction address to set instruction breakpoint
76+
disassembled_instruction_list = self.dap_server.disassembled_instructions
77+
instruction_addr_list = list(disassembled_instruction_list.keys())
78+
index = instruction_addr_list.index(intstructionPointerReference[0])
79+
if len(instruction_addr_list) >= (index + 1):
80+
next_inst_addr = instruction_addr_list[index + 1]
81+
if len(next_inst_addr) > 2:
82+
setIntstructionBreakpoints.append(next_inst_addr)
83+
instruction_breakpoint_response = (
84+
self.dap_server.request_setInstructionBreakpoints(
85+
setIntstructionBreakpoints
86+
)
87+
)
88+
inst_breakpoints = instruction_breakpoint_response["body"][
89+
"breakpoints"
90+
]
91+
self.assertEqual(
92+
inst_breakpoints[0]["instructionReference"],
93+
next_inst_addr,
94+
"Instruction breakpoint has not been resolved or failed to relocate the instruction breakpoint",
95+
)
96+
self.dap_server.request_continue()
97+
self.verify_breakpoint_hit([inst_breakpoints[0]["id"]])
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <stdio.h>
2+
#include <unistd.h>
3+
4+
int function(int x) {
5+
6+
if (x == 0) // breakpoint 1
7+
return x;
8+
9+
if ((x % 2) != 0)
10+
return x;
11+
else
12+
return function(x - 1) + x;
13+
}
14+
15+
int main(int argc, char const *argv[]) {
16+
int n = function(2);
17+
return n;
18+
}

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ add_lldb_tool(lldb-dap
3838
SourceBreakpoint.cpp
3939
DAP.cpp
4040
Watchpoint.cpp
41+
InstructionBreakpoint.cpp
4142

4243
LINK_LIBS
4344
liblldb

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ static std::string capitalize(llvm::StringRef str) {
6868

6969
void DAP::PopulateExceptionBreakpoints() {
7070
llvm::call_once(init_exception_breakpoints_flag, [this]() {
71-
exception_breakpoints = std::vector<ExceptionBreakpoint> {};
71+
exception_breakpoints = std::vector<ExceptionBreakpoint>{};
7272

7373
if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {
7474
exception_breakpoints->emplace_back("cpp_catch", "C++ Catch",
@@ -996,4 +996,32 @@ void DAP::SetThreadFormat(llvm::StringRef format) {
996996
}
997997
}
998998

999+
InstructionBreakpoint *
1000+
DAP::GetInstructionBreakpoint(const lldb::break_id_t bp_id) {
1001+
for (auto &bp : instruction_breakpoints) {
1002+
if (bp.second.id == bp_id)
1003+
return &bp.second;
1004+
}
1005+
return nullptr;
1006+
}
1007+
1008+
InstructionBreakpoint *
1009+
DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {
1010+
const auto num = thread.GetStopReasonDataCount();
1011+
InstructionBreakpoint *inst_bp = nullptr;
1012+
for (size_t i = 0; i < num; i += 2) {
1013+
// thread.GetStopReasonDataAtIndex(i) will return the bp ID and
1014+
// thread.GetStopReasonDataAtIndex(i+1) will return the location
1015+
// within that breakpoint. We only care about the bp ID so we can
1016+
// see if this is an instruction breakpoint that is getting hit.
1017+
lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
1018+
inst_bp = GetInstructionBreakpoint(bp_id);
1019+
// If any breakpoint is not an instruction breakpoint, then stop and
1020+
// report this as a normal breakpoint
1021+
if (inst_bp == nullptr)
1022+
return nullptr;
1023+
}
1024+
return inst_bp;
1025+
}
1026+
9991027
} // namespace lldb_dap

lldb/tools/lldb-dap/DAP.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include "ExceptionBreakpoint.h"
5555
#include "FunctionBreakpoint.h"
5656
#include "IOStream.h"
57+
#include "InstructionBreakpoint.h"
5758
#include "ProgressEvent.h"
5859
#include "RunInTerminal.h"
5960
#include "SourceBreakpoint.h"
@@ -68,6 +69,8 @@ namespace lldb_dap {
6869

6970
typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap;
7071
typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
72+
typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint>
73+
InstructionBreakpointMap;
7174

7275
enum class OutputType { Console, Stdout, Stderr, Telemetry };
7376

@@ -160,6 +163,7 @@ struct DAP {
160163
std::unique_ptr<std::ofstream> log;
161164
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
162165
FunctionBreakpointMap function_breakpoints;
166+
InstructionBreakpointMap instruction_breakpoints;
163167
std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints;
164168
llvm::once_flag init_exception_breakpoints_flag;
165169
std::vector<std::string> pre_init_commands;
@@ -334,6 +338,10 @@ struct DAP {
334338

335339
void SetThreadFormat(llvm::StringRef format);
336340

341+
InstructionBreakpoint *GetInstructionBreakpoint(const lldb::break_id_t bp_id);
342+
343+
InstructionBreakpoint *GetInstructionBPFromStopReason(lldb::SBThread &thread);
344+
337345
private:
338346
// Send the JSON in "json_str" to the "out" stream. Correctly send the
339347
// "Content-Length:" field followed by the length, followed by the raw

lldb/tools/lldb-dap/DAPForward.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct ExceptionBreakpoint;
1515
struct FunctionBreakpoint;
1616
struct SourceBreakpoint;
1717
struct Watchpoint;
18+
struct InstructionBreakpoint;
1819
} // namespace lldb_dap
1920

2021
namespace lldb {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===-- InstructionBreakpoint.cpp ------------------------------------*- C++
2+
//-*-===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "InstructionBreakpoint.h"
11+
#include "DAP.h"
12+
13+
namespace lldb_dap {
14+
15+
// Instruction Breakpoint
16+
InstructionBreakpoint::InstructionBreakpoint(const llvm::json::Object &obj)
17+
: Breakpoint(obj), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0),
18+
offset(GetSigned(obj, "offset", 0)) {
19+
GetString(obj, "instructionReference")
20+
.getAsInteger(0, instructionAddressReference);
21+
instructionAddressReference += offset;
22+
}
23+
24+
void InstructionBreakpoint::SetInstructionBreakpoint() {
25+
bp = g_dap.target.BreakpointCreateByAddress(instructionAddressReference);
26+
id = bp.GetID();
27+
}
28+
} // namespace lldb_dap
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===-- InstructionBreakpoint.h --------------------------------------*- C++
2+
//-*-===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef LLDB_TOOLS_LLDB_DAP_INSTRUCTIONBREAKPOINT_H
11+
#define LLDB_TOOLS_LLDB_DAP_INSTRUCTIONBREAKPOINT_H
12+
13+
#include "Breakpoint.h"
14+
#include "llvm/ADT/StringRef.h"
15+
16+
namespace lldb_dap {
17+
18+
// Instruction Breakpoint
19+
struct InstructionBreakpoint : public Breakpoint {
20+
21+
lldb::addr_t instructionAddressReference;
22+
int32_t id;
23+
int32_t offset;
24+
25+
InstructionBreakpoint()
26+
: Breakpoint(), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0),
27+
offset(0) {}
28+
InstructionBreakpoint(const llvm::json::Object &obj);
29+
30+
// Set instruction breakpoint in LLDB as a new breakpoint
31+
void SetInstructionBreakpoint();
32+
};
33+
34+
} // namespace lldb_dap
35+
36+
#endif

0 commit comments

Comments
 (0)