Skip to content

Commit 4d3feae

Browse files
authored
[lldb-dap] persistent assembly breakpoints (#148061)
Resolves #141955 - Adds data to breakpoints `Source` object, in order for assembly breakpoints, which rely on a temporary `sourceReference` value, to be able to resolve in future sessions like normal path+line breakpoints - Adds optional `instructions_offset` parameter to `BreakpointResolver`
1 parent ff616b4 commit 4d3feae

File tree

30 files changed

+498
-121
lines changed

30 files changed

+498
-121
lines changed

lldb/include/lldb/API/SBTarget.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,14 @@ class LLDB_API SBTarget {
658658
lldb::LanguageType symbol_language,
659659
const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list);
660660

661+
lldb::SBBreakpoint BreakpointCreateByName(
662+
const char *symbol_name,
663+
uint32_t
664+
name_type_mask, // Logical OR one or more FunctionNameType enum bits
665+
lldb::LanguageType symbol_language, lldb::addr_t offset,
666+
bool offset_is_insn_count, const SBFileSpecList &module_list,
667+
const SBFileSpecList &comp_unit_list);
668+
661669
#ifdef SWIG
662670
lldb::SBBreakpoint BreakpointCreateByNames(
663671
const char **symbol_name, uint32_t num_names,

lldb/include/lldb/Breakpoint/BreakpointResolver.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ class BreakpointResolver : public Searcher {
4545
/// The breakpoint that owns this resolver.
4646
/// \param[in] resolverType
4747
/// The concrete breakpoint resolver type for this breakpoint.
48-
BreakpointResolver(const lldb::BreakpointSP &bkpt,
49-
unsigned char resolverType,
50-
lldb::addr_t offset = 0);
48+
BreakpointResolver(const lldb::BreakpointSP &bkpt, unsigned char resolverType,
49+
lldb::addr_t offset = 0,
50+
bool offset_is_insn_count = false);
5151

5252
/// The Destructor is virtual, all significant breakpoint resolvers derive
5353
/// from this class.
@@ -76,6 +76,7 @@ class BreakpointResolver : public Searcher {
7676
void SetOffset(lldb::addr_t offset);
7777

7878
lldb::addr_t GetOffset() const { return m_offset; }
79+
lldb::addr_t GetOffsetIsInsnCount() const { return m_offset_is_insn_count; }
7980

8081
/// In response to this method the resolver scans all the modules in the
8182
/// breakpoint's target, and adds any new locations it finds.
@@ -220,6 +221,8 @@ class BreakpointResolver : public Searcher {
220221
lldb::BreakpointWP m_breakpoint; // This is the breakpoint we add locations to.
221222
lldb::addr_t m_offset; // A random offset the user asked us to add to any
222223
// breakpoints we set.
224+
bool m_offset_is_insn_count; // Use the offset as an instruction count
225+
// instead of an address offset.
223226

224227
// Subclass identifier (for llvm isa/dyn_cast)
225228
const unsigned char SubclassID;

lldb/include/lldb/Breakpoint/BreakpointResolverName.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class BreakpointResolverName : public BreakpointResolver {
2727
lldb::FunctionNameType name_type_mask,
2828
lldb::LanguageType language,
2929
Breakpoint::MatchType type, lldb::addr_t offset,
30-
bool skip_prologue);
30+
bool offset_is_insn_count, bool skip_prologue);
3131

3232
// This one takes an array of names. It is always MatchType = Exact.
3333
BreakpointResolverName(const lldb::BreakpointSP &bkpt, const char *names[],

lldb/include/lldb/Core/Disassembler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ class InstructionList {
291291

292292
size_t GetSize() const;
293293

294+
size_t GetTotalByteSize() const;
295+
294296
uint32_t GetMaxOpcocdeByteSize() const;
295297

296298
lldb::InstructionSP GetInstructionAtIndex(size_t idx) const;

lldb/include/lldb/Target/Target.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "lldb/Breakpoint/BreakpointList.h"
1919
#include "lldb/Breakpoint/BreakpointName.h"
2020
#include "lldb/Breakpoint/WatchpointList.h"
21+
#include "lldb/Core/Address.h"
2122
#include "lldb/Core/Architecture.h"
2223
#include "lldb/Core/Disassembler.h"
2324
#include "lldb/Core/ModuleList.h"
@@ -723,7 +724,7 @@ class Target : public std::enable_shared_from_this<Target>,
723724
lldb::BreakpointSP CreateBreakpoint(lldb::addr_t load_addr, bool internal,
724725
bool request_hardware);
725726

726-
// Use this to create a breakpoint from a load address and a module file spec
727+
// Use this to create a breakpoint from a file address and a module file spec
727728
lldb::BreakpointSP CreateAddressInModuleBreakpoint(lldb::addr_t file_addr,
728729
bool internal,
729730
const FileSpec &file_spec,
@@ -752,8 +753,8 @@ class Target : public std::enable_shared_from_this<Target>,
752753
const FileSpecList *containingModules,
753754
const FileSpecList *containingSourceFiles, const char *func_name,
754755
lldb::FunctionNameType func_name_type_mask, lldb::LanguageType language,
755-
lldb::addr_t offset, LazyBool skip_prologue, bool internal,
756-
bool request_hardware);
756+
lldb::addr_t offset, bool offset_is_insn_count, LazyBool skip_prologue,
757+
bool internal, bool request_hardware);
757758

758759
lldb::BreakpointSP
759760
CreateExceptionBreakpoint(enum lldb::LanguageType language, bool catch_bp,
@@ -1334,6 +1335,10 @@ class Target : public std::enable_shared_from_this<Target>,
13341335
const lldb_private::RegisterFlags &flags,
13351336
uint32_t byte_size);
13361337

1338+
llvm::Expected<lldb::DisassemblerSP>
1339+
ReadInstructions(const Address &start_addr, uint32_t count,
1340+
const char *flavor_string = nullptr);
1341+
13371342
// Target Stop Hooks
13381343
class StopHook : public UserID {
13391344
public:

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

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,24 +107,33 @@ def dump_dap_log(log_file):
107107

108108
class Source(object):
109109
def __init__(
110-
self, path: Optional[str] = None, source_reference: Optional[int] = None
110+
self,
111+
path: Optional[str] = None,
112+
source_reference: Optional[int] = None,
113+
raw_dict: Optional[dict[str, Any]] = None,
111114
):
112115
self._name = None
113116
self._path = None
114117
self._source_reference = None
118+
self._raw_dict = None
115119

116120
if path is not None:
117121
self._name = os.path.basename(path)
118122
self._path = path
119123
elif source_reference is not None:
120124
self._source_reference = source_reference
125+
elif raw_dict is not None:
126+
self._raw_dict = raw_dict
121127
else:
122128
raise ValueError("Either path or source_reference must be provided")
123129

124130
def __str__(self):
125131
return f"Source(name={self.name}, path={self.path}), source_reference={self.source_reference})"
126132

127133
def as_dict(self):
134+
if self._raw_dict is not None:
135+
return self._raw_dict
136+
128137
source_dict = {}
129138
if self._name is not None:
130139
source_dict["name"] = self._name
@@ -135,6 +144,19 @@ def as_dict(self):
135144
return source_dict
136145

137146

147+
class Breakpoint(object):
148+
def __init__(self, obj):
149+
self._breakpoint = obj
150+
151+
def is_verified(self):
152+
"""Check if the breakpoint is verified."""
153+
return self._breakpoint.get("verified", False)
154+
155+
def source(self):
156+
"""Get the source of the breakpoint."""
157+
return self._breakpoint.get("source", {})
158+
159+
138160
class NotSupportedError(KeyError):
139161
"""Raised if a feature is not supported due to its capabilities."""
140162

@@ -170,7 +192,7 @@ def __init__(
170192
self.initialized = False
171193
self.frame_scopes = {}
172194
self.init_commands = init_commands
173-
self.resolved_breakpoints = {}
195+
self.resolved_breakpoints: dict[str, Breakpoint] = {}
174196

175197
@classmethod
176198
def encode_content(cls, s: str) -> bytes:
@@ -326,8 +348,8 @@ def _process_continued(self, all_threads_continued: bool):
326348
def _update_verified_breakpoints(self, breakpoints: list[Event]):
327349
for breakpoint in breakpoints:
328350
if "id" in breakpoint:
329-
self.resolved_breakpoints[str(breakpoint["id"])] = breakpoint.get(
330-
"verified", False
351+
self.resolved_breakpoints[str(breakpoint["id"])] = Breakpoint(
352+
breakpoint
331353
)
332354

333355
def send_packet(self, command_dict: Request, set_sequence=True):
@@ -484,7 +506,14 @@ def wait_for_breakpoints_to_be_verified(
484506
if breakpoint_event is None:
485507
break
486508

487-
return [id for id in breakpoint_ids if id not in self.resolved_breakpoints]
509+
return [
510+
id
511+
for id in breakpoint_ids
512+
if (
513+
id not in self.resolved_breakpoints
514+
or not self.resolved_breakpoints[id].is_verified()
515+
)
516+
]
488517

489518
def wait_for_exited(self, timeout: Optional[float] = None):
490519
event_dict = self.wait_for_event("exited", timeout=timeout)

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

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,24 +59,22 @@ def set_source_breakpoints(
5959
Each object in data is 1:1 mapping with the entry in lines.
6060
It contains optional location/hitCondition/logMessage parameters.
6161
"""
62-
response = self.dap_server.request_setBreakpoints(
63-
Source(source_path), lines, data
62+
return self.set_source_breakpoints_from_source(
63+
Source(path=source_path), lines, data, wait_for_resolve
6464
)
65-
if response is None or not response["success"]:
66-
return []
67-
breakpoints = response["body"]["breakpoints"]
68-
breakpoint_ids = []
69-
for breakpoint in breakpoints:
70-
breakpoint_ids.append("%i" % (breakpoint["id"]))
71-
if wait_for_resolve:
72-
self.wait_for_breakpoints_to_resolve(breakpoint_ids)
73-
return breakpoint_ids
7465

7566
def set_source_breakpoints_assembly(
7667
self, source_reference, lines, data=None, wait_for_resolve=True
68+
):
69+
return self.set_source_breakpoints_from_source(
70+
Source(source_reference=source_reference), lines, data, wait_for_resolve
71+
)
72+
73+
def set_source_breakpoints_from_source(
74+
self, source: Source, lines, data=None, wait_for_resolve=True
7775
):
7876
response = self.dap_server.request_setBreakpoints(
79-
Source(source_reference=source_reference),
77+
source,
8078
lines,
8179
data,
8280
)

lldb/source/API/SBTarget.cpp

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -766,16 +766,19 @@ SBBreakpoint SBTarget::BreakpointCreateByName(const char *symbol_name,
766766
const bool hardware = false;
767767
const LazyBool skip_prologue = eLazyBoolCalculate;
768768
const lldb::addr_t offset = 0;
769+
const bool offset_is_insn_count = false;
769770
if (module_name && module_name[0]) {
770771
FileSpecList module_spec_list;
771772
module_spec_list.Append(FileSpec(module_name));
772773
sb_bp = target_sp->CreateBreakpoint(
773774
&module_spec_list, nullptr, symbol_name, eFunctionNameTypeAuto,
774-
eLanguageTypeUnknown, offset, skip_prologue, internal, hardware);
775+
eLanguageTypeUnknown, offset, offset_is_insn_count, skip_prologue,
776+
internal, hardware);
775777
} else {
776778
sb_bp = target_sp->CreateBreakpoint(
777779
nullptr, nullptr, symbol_name, eFunctionNameTypeAuto,
778-
eLanguageTypeUnknown, offset, skip_prologue, internal, hardware);
780+
eLanguageTypeUnknown, offset, offset_is_insn_count, skip_prologue,
781+
internal, hardware);
779782
}
780783
}
781784

@@ -811,6 +814,17 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
811814
const SBFileSpecList &comp_unit_list) {
812815
LLDB_INSTRUMENT_VA(this, symbol_name, name_type_mask, symbol_language,
813816
module_list, comp_unit_list);
817+
return BreakpointCreateByName(symbol_name, name_type_mask, symbol_language, 0,
818+
false, module_list, comp_unit_list);
819+
}
820+
821+
lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
822+
const char *symbol_name, uint32_t name_type_mask,
823+
LanguageType symbol_language, lldb::addr_t offset,
824+
bool offset_is_insn_count, const SBFileSpecList &module_list,
825+
const SBFileSpecList &comp_unit_list) {
826+
LLDB_INSTRUMENT_VA(this, symbol_name, name_type_mask, symbol_language, offset,
827+
offset_is_insn_count, module_list, comp_unit_list);
814828

815829
SBBreakpoint sb_bp;
816830
if (TargetSP target_sp = GetSP();
@@ -821,7 +835,8 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
821835
std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
822836
FunctionNameType mask = static_cast<FunctionNameType>(name_type_mask);
823837
sb_bp = target_sp->CreateBreakpoint(module_list.get(), comp_unit_list.get(),
824-
symbol_name, mask, symbol_language, 0,
838+
symbol_name, mask, symbol_language,
839+
offset, offset_is_insn_count,
825840
skip_prologue, internal, hardware);
826841
}
827842

@@ -1955,29 +1970,10 @@ lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr,
19551970

19561971
if (TargetSP target_sp = GetSP()) {
19571972
if (Address *addr_ptr = base_addr.get()) {
1958-
DataBufferHeap data(
1959-
target_sp->GetArchitecture().GetMaximumOpcodeByteSize() * count, 0);
1960-
bool force_live_memory = true;
1961-
lldb_private::Status error;
1962-
lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
1963-
const size_t bytes_read =
1964-
target_sp->ReadMemory(*addr_ptr, data.GetBytes(), data.GetByteSize(),
1965-
error, force_live_memory, &load_addr);
1966-
1967-
const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
1968-
if (!flavor_string || flavor_string[0] == '\0') {
1969-
// FIXME - we don't have the mechanism in place to do per-architecture
1970-
// settings. But since we know that for now we only support flavors on
1971-
// x86 & x86_64,
1972-
const llvm::Triple::ArchType arch =
1973-
target_sp->GetArchitecture().GetTriple().getArch();
1974-
if (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64)
1975-
flavor_string = target_sp->GetDisassemblyFlavor();
1973+
if (llvm::Expected<DisassemblerSP> disassembler =
1974+
target_sp->ReadInstructions(*addr_ptr, count, flavor_string)) {
1975+
sb_instructions.SetDisassembler(*disassembler);
19761976
}
1977-
sb_instructions.SetDisassembler(Disassembler::DisassembleBytes(
1978-
target_sp->GetArchitecture(), nullptr, flavor_string,
1979-
target_sp->GetDisassemblyCPU(), target_sp->GetDisassemblyFeatures(),
1980-
*addr_ptr, data.GetBytes(), bytes_read, count, data_from_file));
19811977
}
19821978
}
19831979

lldb/source/Breakpoint/BreakpointResolver.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address",
4242

4343
const char *BreakpointResolver::g_option_names[static_cast<uint32_t>(
4444
BreakpointResolver::OptionNames::LastOptionName)] = {
45-
"AddressOffset", "Exact", "FileName", "Inlines", "Language",
46-
"LineNumber", "Column", "ModuleName", "NameMask", "Offset",
47-
"PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
45+
"AddressOffset", "Exact", "FileName", "Inlines", "Language",
46+
"LineNumber", "Column", "ModuleName", "NameMask", "Offset",
47+
"PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
4848
"SkipPrologue", "SymbolNames"};
4949

5050
const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type) {
@@ -65,8 +65,10 @@ BreakpointResolver::NameToResolverTy(llvm::StringRef name) {
6565

6666
BreakpointResolver::BreakpointResolver(const BreakpointSP &bkpt,
6767
const unsigned char resolverTy,
68-
lldb::addr_t offset)
69-
: m_breakpoint(bkpt), m_offset(offset), SubclassID(resolverTy) {}
68+
lldb::addr_t offset,
69+
bool offset_is_insn_count)
70+
: m_breakpoint(bkpt), m_offset(offset),
71+
m_offset_is_insn_count(offset_is_insn_count), SubclassID(resolverTy) {}
7072

7173
BreakpointResolver::~BreakpointResolver() = default;
7274

@@ -364,7 +366,32 @@ void BreakpointResolver::AddLocation(SearchFilter &filter,
364366

365367
BreakpointLocationSP BreakpointResolver::AddLocation(Address loc_addr,
366368
bool *new_location) {
367-
loc_addr.Slide(m_offset);
369+
if (m_offset_is_insn_count) {
370+
Target &target = GetBreakpoint()->GetTarget();
371+
llvm::Expected<DisassemblerSP> expected_instructions =
372+
target.ReadInstructions(loc_addr, m_offset);
373+
if (!expected_instructions) {
374+
LLDB_LOG_ERROR(GetLog(LLDBLog::Breakpoints),
375+
expected_instructions.takeError(),
376+
"error: Unable to read instructions at address 0x{0:x}",
377+
loc_addr.GetLoadAddress(&target));
378+
return BreakpointLocationSP();
379+
}
380+
381+
const DisassemblerSP instructions = *expected_instructions;
382+
if (!instructions ||
383+
instructions->GetInstructionList().GetSize() != m_offset) {
384+
LLDB_LOG(GetLog(LLDBLog::Breakpoints),
385+
"error: Unable to read {0} instructions at address 0x{1:x}",
386+
m_offset, loc_addr.GetLoadAddress(&target));
387+
return BreakpointLocationSP();
388+
}
389+
390+
loc_addr.Slide(instructions->GetInstructionList().GetTotalByteSize());
391+
} else {
392+
loc_addr.Slide(m_offset);
393+
}
394+
368395
return GetBreakpoint()->AddLocation(loc_addr, new_location);
369396
}
370397

lldb/source/Breakpoint/BreakpointResolverAddress.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ Searcher::CallbackReturn BreakpointResolverAddress::SearchCallback(
133133
Address tmp_address;
134134
if (module_sp->ResolveFileAddress(m_addr.GetOffset(), tmp_address))
135135
m_addr = tmp_address;
136+
else
137+
return Searcher::eCallbackReturnStop;
138+
} else {
139+
// If we didn't find the module, then we can't resolve the address.
140+
return Searcher::eCallbackReturnStop;
136141
}
137142
}
138143

0 commit comments

Comments
 (0)