Skip to content

[lldb-dap] persistent assembly breakpoints #148061

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

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
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
8 changes: 8 additions & 0 deletions lldb/include/lldb/API/SBTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,14 @@ class LLDB_API SBTarget {
lldb::LanguageType symbol_language,
const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list);

lldb::SBBreakpoint BreakpointCreateByName(
const char *symbol_name,
uint32_t
name_type_mask, // Logical OR one or more FunctionNameType enum bits
lldb::LanguageType symbol_language, lldb::addr_t offset,
bool offset_is_insn_count, const SBFileSpecList &module_list,
const SBFileSpecList &comp_unit_list);

#ifdef SWIG
lldb::SBBreakpoint BreakpointCreateByNames(
const char **symbol_name, uint32_t num_names,
Expand Down
9 changes: 6 additions & 3 deletions lldb/include/lldb/Breakpoint/BreakpointResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ class BreakpointResolver : public Searcher {
/// The breakpoint that owns this resolver.
/// \param[in] resolverType
/// The concrete breakpoint resolver type for this breakpoint.
BreakpointResolver(const lldb::BreakpointSP &bkpt,
unsigned char resolverType,
lldb::addr_t offset = 0);
BreakpointResolver(const lldb::BreakpointSP &bkpt, unsigned char resolverType,
lldb::addr_t offset = 0,
bool offset_is_insn_count = false);

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

lldb::addr_t GetOffset() const { return m_offset; }
lldb::addr_t GetOffsetIsInsnCount() const { return m_offset_is_insn_count; }

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

// Subclass identifier (for llvm isa/dyn_cast)
const unsigned char SubclassID;
Expand Down
2 changes: 1 addition & 1 deletion lldb/include/lldb/Breakpoint/BreakpointResolverName.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class BreakpointResolverName : public BreakpointResolver {
lldb::FunctionNameType name_type_mask,
lldb::LanguageType language,
Breakpoint::MatchType type, lldb::addr_t offset,
bool skip_prologue);
bool offset_is_insn_count, bool skip_prologue);

// This one takes an array of names. It is always MatchType = Exact.
BreakpointResolverName(const lldb::BreakpointSP &bkpt, const char *names[],
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Core/Disassembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ class InstructionList {

size_t GetSize() const;

size_t GetTotalByteSize() const;

uint32_t GetMaxOpcocdeByteSize() const;

lldb::InstructionSP GetInstructionAtIndex(size_t idx) const;
Expand Down
11 changes: 8 additions & 3 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "lldb/Breakpoint/BreakpointList.h"
#include "lldb/Breakpoint/BreakpointName.h"
#include "lldb/Breakpoint/WatchpointList.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/Architecture.h"
#include "lldb/Core/Disassembler.h"
#include "lldb/Core/ModuleList.h"
Expand Down Expand Up @@ -723,7 +724,7 @@ class Target : public std::enable_shared_from_this<Target>,
lldb::BreakpointSP CreateBreakpoint(lldb::addr_t load_addr, bool internal,
bool request_hardware);

// Use this to create a breakpoint from a load address and a module file spec
// Use this to create a breakpoint from a file address and a module file spec
lldb::BreakpointSP CreateAddressInModuleBreakpoint(lldb::addr_t file_addr,
bool internal,
const FileSpec &file_spec,
Expand Down Expand Up @@ -752,8 +753,8 @@ class Target : public std::enable_shared_from_this<Target>,
const FileSpecList *containingModules,
const FileSpecList *containingSourceFiles, const char *func_name,
lldb::FunctionNameType func_name_type_mask, lldb::LanguageType language,
lldb::addr_t offset, LazyBool skip_prologue, bool internal,
bool request_hardware);
lldb::addr_t offset, bool offset_is_insn_count, LazyBool skip_prologue,
bool internal, bool request_hardware);

lldb::BreakpointSP
CreateExceptionBreakpoint(enum lldb::LanguageType language, bool catch_bp,
Expand Down Expand Up @@ -1328,6 +1329,10 @@ class Target : public std::enable_shared_from_this<Target>,
const lldb_private::RegisterFlags &flags,
uint32_t byte_size);

llvm::Expected<lldb::DisassemblerSP>
ReadInstructions(const Address &start_addr, uint32_t count,
const char *flavor_string = nullptr);

// Target Stop Hooks
class StopHook : public UserID {
public:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,33 @@ def dump_dap_log(log_file):

class Source(object):
def __init__(
self, path: Optional[str] = None, source_reference: Optional[int] = None
self,
path: Optional[str] = None,
source_reference: Optional[int] = None,
raw_dict: Optional[dict[str, Any]] = None,
):
self._name = None
self._path = None
self._source_reference = None
self._raw_dict = None

if path is not None:
self._name = os.path.basename(path)
self._path = path
elif source_reference is not None:
self._source_reference = source_reference
elif raw_dict is not None:
self._raw_dict = raw_dict
else:
raise ValueError("Either path or source_reference must be provided")

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

def as_dict(self):
if self._raw_dict is not None:
return self._raw_dict

source_dict = {}
if self._name is not None:
source_dict["name"] = self._name
Expand All @@ -135,6 +144,19 @@ def as_dict(self):
return source_dict


class Breakpoint(object):
def __init__(self, obj):
self._breakpoint = obj

def is_verified(self):
"""Check if the breakpoint is verified."""
return self._breakpoint.get("verified", False)

def source(self):
"""Get the source of the breakpoint."""
return self._breakpoint.get("source", {})


class NotSupportedError(KeyError):
"""Raised if a feature is not supported due to its capabilities."""

Expand Down Expand Up @@ -170,7 +192,7 @@ def __init__(
self.initialized = False
self.frame_scopes = {}
self.init_commands = init_commands
self.resolved_breakpoints = {}
self.resolved_breakpoints: dict[str, Breakpoint] = {}

@classmethod
def encode_content(cls, s: str) -> bytes:
Expand Down Expand Up @@ -326,8 +348,8 @@ def _process_continued(self, all_threads_continued: bool):
def _update_verified_breakpoints(self, breakpoints: list[Event]):
for breakpoint in breakpoints:
if "id" in breakpoint:
self.resolved_breakpoints[str(breakpoint["id"])] = breakpoint.get(
"verified", False
self.resolved_breakpoints[str(breakpoint["id"])] = Breakpoint(
breakpoint
)

def send_packet(self, command_dict: Request, set_sequence=True):
Expand Down Expand Up @@ -484,7 +506,14 @@ def wait_for_breakpoints_to_be_verified(
if breakpoint_event is None:
break

return [id for id in breakpoint_ids if id not in self.resolved_breakpoints]
return [
id
for id in breakpoint_ids
if (
id not in self.resolved_breakpoints
or not self.resolved_breakpoints[id].is_verified()
)
]

def wait_for_exited(self, timeout: Optional[float] = None):
event_dict = self.wait_for_event("exited", timeout=timeout)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,22 @@ def set_source_breakpoints(
Each object in data is 1:1 mapping with the entry in lines.
It contains optional location/hitCondition/logMessage parameters.
"""
response = self.dap_server.request_setBreakpoints(
Source(source_path), lines, data
return self.set_source_breakpoints_from_source(
Source(path=source_path), lines, data, wait_for_resolve
)
if response is None or not response["success"]:
return []
breakpoints = response["body"]["breakpoints"]
breakpoint_ids = []
for breakpoint in breakpoints:
breakpoint_ids.append("%i" % (breakpoint["id"]))
if wait_for_resolve:
self.wait_for_breakpoints_to_resolve(breakpoint_ids)
return breakpoint_ids

def set_source_breakpoints_assembly(
self, source_reference, lines, data=None, wait_for_resolve=True
):
return self.set_source_breakpoints_from_source(
Source(source_reference=source_reference), lines, data, wait_for_resolve
)

def set_source_breakpoints_from_source(
self, source: Source, lines, data=None, wait_for_resolve=True
):
response = self.dap_server.request_setBreakpoints(
Source(source_reference=source_reference),
source,
lines,
data,
)
Expand Down
46 changes: 21 additions & 25 deletions lldb/source/API/SBTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,16 +766,19 @@ SBBreakpoint SBTarget::BreakpointCreateByName(const char *symbol_name,
const bool hardware = false;
const LazyBool skip_prologue = eLazyBoolCalculate;
const lldb::addr_t offset = 0;
const bool offset_is_insn_count = false;
if (module_name && module_name[0]) {
FileSpecList module_spec_list;
module_spec_list.Append(FileSpec(module_name));
sb_bp = target_sp->CreateBreakpoint(
&module_spec_list, nullptr, symbol_name, eFunctionNameTypeAuto,
eLanguageTypeUnknown, offset, skip_prologue, internal, hardware);
eLanguageTypeUnknown, offset, offset_is_insn_count, skip_prologue,
internal, hardware);
} else {
sb_bp = target_sp->CreateBreakpoint(
nullptr, nullptr, symbol_name, eFunctionNameTypeAuto,
eLanguageTypeUnknown, offset, skip_prologue, internal, hardware);
eLanguageTypeUnknown, offset, offset_is_insn_count, skip_prologue,
internal, hardware);
}
}

Expand Down Expand Up @@ -811,6 +814,17 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
const SBFileSpecList &comp_unit_list) {
LLDB_INSTRUMENT_VA(this, symbol_name, name_type_mask, symbol_language,
module_list, comp_unit_list);
return BreakpointCreateByName(symbol_name, name_type_mask, symbol_language, 0,
false, module_list, comp_unit_list);
}

lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
const char *symbol_name, uint32_t name_type_mask,
LanguageType symbol_language, lldb::addr_t offset,
bool offset_is_insn_count, const SBFileSpecList &module_list,
const SBFileSpecList &comp_unit_list) {
LLDB_INSTRUMENT_VA(this, symbol_name, name_type_mask, symbol_language, offset,
offset_is_insn_count, module_list, comp_unit_list);

SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP();
Expand All @@ -821,7 +835,8 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
FunctionNameType mask = static_cast<FunctionNameType>(name_type_mask);
sb_bp = target_sp->CreateBreakpoint(module_list.get(), comp_unit_list.get(),
symbol_name, mask, symbol_language, 0,
symbol_name, mask, symbol_language,
offset, offset_is_insn_count,
skip_prologue, internal, hardware);
}

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

if (TargetSP target_sp = GetSP()) {
if (Address *addr_ptr = base_addr.get()) {
DataBufferHeap data(
target_sp->GetArchitecture().GetMaximumOpcodeByteSize() * count, 0);
bool force_live_memory = true;
lldb_private::Status error;
lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
const size_t bytes_read =
target_sp->ReadMemory(*addr_ptr, data.GetBytes(), data.GetByteSize(),
error, force_live_memory, &load_addr);

const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
if (!flavor_string || flavor_string[0] == '\0') {
// FIXME - we don't have the mechanism in place to do per-architecture
// settings. But since we know that for now we only support flavors on
// x86 & x86_64,
const llvm::Triple::ArchType arch =
target_sp->GetArchitecture().GetTriple().getArch();
if (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64)
flavor_string = target_sp->GetDisassemblyFlavor();
if (llvm::Expected<DisassemblerSP> disassembler =
target_sp->ReadInstructions(*addr_ptr, count, flavor_string)) {
sb_instructions.SetDisassembler(*disassembler);
}
sb_instructions.SetDisassembler(Disassembler::DisassembleBytes(
target_sp->GetArchitecture(), nullptr, flavor_string,
target_sp->GetDisassemblyCPU(), target_sp->GetDisassemblyFeatures(),
*addr_ptr, data.GetBytes(), bytes_read, count, data_from_file));
}
}

Expand Down
39 changes: 33 additions & 6 deletions lldb/source/Breakpoint/BreakpointResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address",

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

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

BreakpointResolver::BreakpointResolver(const BreakpointSP &bkpt,
const unsigned char resolverTy,
lldb::addr_t offset)
: m_breakpoint(bkpt), m_offset(offset), SubclassID(resolverTy) {}
lldb::addr_t offset,
bool offset_is_insn_count)
: m_breakpoint(bkpt), m_offset(offset),
m_offset_is_insn_count(offset_is_insn_count), SubclassID(resolverTy) {}

BreakpointResolver::~BreakpointResolver() = default;

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

BreakpointLocationSP BreakpointResolver::AddLocation(Address loc_addr,
bool *new_location) {
loc_addr.Slide(m_offset);
if (m_offset_is_insn_count) {
Target &target = GetBreakpoint()->GetTarget();
llvm::Expected<DisassemblerSP> expected_instructions =
target.ReadInstructions(loc_addr, m_offset);
if (!expected_instructions) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Breakpoints),
expected_instructions.takeError(),
"error: Unable to read instructions at address 0x{0:x}",
loc_addr.GetLoadAddress(&target));
return BreakpointLocationSP();
}

const DisassemblerSP instructions = *expected_instructions;
if (!instructions ||
instructions->GetInstructionList().GetSize() != m_offset) {
LLDB_LOG(GetLog(LLDBLog::Breakpoints),
"error: Unable to read {0} instructions at address 0x{1:x}",
m_offset, loc_addr.GetLoadAddress(&target));
return BreakpointLocationSP();
}

loc_addr.Slide(instructions->GetInstructionList().GetTotalByteSize());
} else {
loc_addr.Slide(m_offset);
}

return GetBreakpoint()->AddLocation(loc_addr, new_location);
}

Expand Down
5 changes: 5 additions & 0 deletions lldb/source/Breakpoint/BreakpointResolverAddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ Searcher::CallbackReturn BreakpointResolverAddress::SearchCallback(
Address tmp_address;
if (module_sp->ResolveFileAddress(m_addr.GetOffset(), tmp_address))
m_addr = tmp_address;
else
return Searcher::eCallbackReturnStop;
} else {
// If we didn't find the module, then we can't resolve the address.
return Searcher::eCallbackReturnStop;
}
}

Expand Down
Loading
Loading