mirror of
https://github.com/intel/llvm.git
synced 2026-01-17 06:40:01 +08:00
[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`
This commit is contained in:
@@ -658,6 +658,14 @@ public:
|
||||
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,
|
||||
|
||||
@@ -45,9 +45,9 @@ public:
|
||||
/// 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.
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
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.
|
||||
@@ -220,6 +221,8 @@ private:
|
||||
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;
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
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[],
|
||||
|
||||
@@ -291,6 +291,8 @@ public:
|
||||
|
||||
size_t GetSize() const;
|
||||
|
||||
size_t GetTotalByteSize() const;
|
||||
|
||||
uint32_t GetMaxOpcocdeByteSize() const;
|
||||
|
||||
lldb::InstructionSP GetInstructionAtIndex(size_t idx) const;
|
||||
|
||||
@@ -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"
|
||||
@@ -723,7 +724,7 @@ public:
|
||||
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,
|
||||
@@ -752,8 +753,8 @@ public:
|
||||
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,
|
||||
@@ -1334,6 +1335,10 @@ public:
|
||||
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:
|
||||
|
||||
@@ -107,17 +107,23 @@ 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")
|
||||
|
||||
@@ -125,6 +131,9 @@ class Source(object):
|
||||
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
|
||||
@@ -135,6 +144,19 @@ class Source(object):
|
||||
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."""
|
||||
|
||||
@@ -170,7 +192,7 @@ class DebugCommunication(object):
|
||||
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:
|
||||
@@ -326,8 +348,8 @@ class DebugCommunication(object):
|
||||
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):
|
||||
@@ -484,7 +506,14 @@ class DebugCommunication(object):
|
||||
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)
|
||||
|
||||
@@ -59,24 +59,22 @@ class DAPTestCaseBase(TestBase):
|
||||
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,
|
||||
)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,13 @@
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt,
|
||||
const char *name_cstr, FunctionNameType name_type_mask,
|
||||
LanguageType language, Breakpoint::MatchType type, lldb::addr_t offset,
|
||||
BreakpointResolverName::BreakpointResolverName(
|
||||
const BreakpointSP &bkpt, const char *name_cstr,
|
||||
FunctionNameType name_type_mask, LanguageType language,
|
||||
Breakpoint::MatchType type, lldb::addr_t offset, bool offset_is_insn_count,
|
||||
bool skip_prologue)
|
||||
: BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
|
||||
: BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset,
|
||||
offset_is_insn_count),
|
||||
m_match_type(type), m_language(language), m_skip_prologue(skip_prologue) {
|
||||
if (m_match_type == Breakpoint::Regexp) {
|
||||
m_regex = RegularExpression(name_cstr);
|
||||
@@ -81,7 +83,7 @@ BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt,
|
||||
BreakpointResolverName::BreakpointResolverName(
|
||||
const BreakpointResolverName &rhs)
|
||||
: BreakpointResolver(rhs.GetBreakpoint(), BreakpointResolver::NameResolver,
|
||||
rhs.GetOffset()),
|
||||
rhs.GetOffset(), rhs.GetOffsetIsInsnCount()),
|
||||
m_lookups(rhs.m_lookups), m_class_name(rhs.m_class_name),
|
||||
m_regex(rhs.m_regex), m_match_type(rhs.m_match_type),
|
||||
m_language(rhs.m_language), m_skip_prologue(rhs.m_skip_prologue) {}
|
||||
@@ -177,7 +179,8 @@ BreakpointResolverSP BreakpointResolverName::CreateFromStructuredData(
|
||||
std::shared_ptr<BreakpointResolverName> resolver_sp =
|
||||
std::make_shared<BreakpointResolverName>(
|
||||
nullptr, names[0].c_str(), name_masks[0], language,
|
||||
Breakpoint::MatchType::Exact, offset, skip_prologue);
|
||||
Breakpoint::MatchType::Exact, offset,
|
||||
/*offset_is_insn_count = */ false, skip_prologue);
|
||||
for (size_t i = 1; i < num_elem; i++) {
|
||||
resolver_sp->AddNameLookup(ConstString(names[i]), name_masks[i]);
|
||||
}
|
||||
|
||||
@@ -1016,6 +1016,16 @@ uint32_t InstructionList::GetMaxOpcocdeByteSize() const {
|
||||
return max_inst_size;
|
||||
}
|
||||
|
||||
size_t InstructionList::GetTotalByteSize() const {
|
||||
size_t total_byte_size = 0;
|
||||
collection::const_iterator pos, end;
|
||||
for (pos = m_instructions.begin(), end = m_instructions.end(); pos != end;
|
||||
++pos) {
|
||||
total_byte_size += (*pos)->GetOpcode().GetByteSize();
|
||||
}
|
||||
return total_byte_size;
|
||||
}
|
||||
|
||||
InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const {
|
||||
InstructionSP inst_sp;
|
||||
if (idx < m_instructions.size())
|
||||
|
||||
@@ -1561,7 +1561,8 @@ void DynamicLoaderDarwinKernel::SetNotificationBreakpointIfNeeded() {
|
||||
.CreateBreakpoint(&module_spec_list, nullptr,
|
||||
"OSKextLoadedKextSummariesUpdated",
|
||||
eFunctionNameTypeFull, eLanguageTypeUnknown, 0,
|
||||
skip_prologue, internal_bp, hardware)
|
||||
/*offset_is_insn_count = */ false, skip_prologue,
|
||||
internal_bp, hardware)
|
||||
.get();
|
||||
|
||||
bp->SetCallback(DynamicLoaderDarwinKernel::BreakpointHitCallback, this,
|
||||
|
||||
@@ -530,7 +530,7 @@ bool DynamicLoaderMacOS::SetNotificationBreakpoint() {
|
||||
m_process->GetTarget()
|
||||
.CreateBreakpoint(&dyld_filelist, source_files,
|
||||
"lldb_image_notifier", eFunctionNameTypeFull,
|
||||
eLanguageTypeUnknown, 0, skip_prologue,
|
||||
eLanguageTypeUnknown, 0, false, skip_prologue,
|
||||
internal, hardware)
|
||||
.get();
|
||||
breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
|
||||
@@ -546,8 +546,9 @@ bool DynamicLoaderMacOS::SetNotificationBreakpoint() {
|
||||
m_process->GetTarget()
|
||||
.CreateBreakpoint(&dyld_filelist, source_files,
|
||||
"gdb_image_notifier", eFunctionNameTypeFull,
|
||||
eLanguageTypeUnknown, 0, skip_prologue,
|
||||
internal, hardware)
|
||||
eLanguageTypeUnknown, 0,
|
||||
/*offset_is_insn_count = */ false,
|
||||
skip_prologue, internal, hardware)
|
||||
.get();
|
||||
breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
|
||||
true);
|
||||
|
||||
@@ -102,7 +102,7 @@ AppleObjCRuntimeV1::CreateExceptionResolver(const BreakpointSP &bkpt,
|
||||
resolver_sp = std::make_shared<BreakpointResolverName>(
|
||||
bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
|
||||
eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
|
||||
eLazyBoolNo);
|
||||
/*offset_is_insn_count = */ false, eLazyBoolNo);
|
||||
// FIXME: don't do catch yet.
|
||||
return resolver_sp;
|
||||
}
|
||||
|
||||
@@ -1163,7 +1163,7 @@ AppleObjCRuntimeV2::CreateExceptionResolver(const BreakpointSP &bkpt,
|
||||
resolver_sp = std::make_shared<BreakpointResolverName>(
|
||||
bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
|
||||
eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
|
||||
eLazyBoolNo);
|
||||
/*offset_is_insn_count = */ false, eLazyBoolNo);
|
||||
// FIXME: We don't do catch breakpoints for ObjC yet.
|
||||
// Should there be some way for the runtime to specify what it can do in this
|
||||
// regard?
|
||||
|
||||
@@ -169,7 +169,8 @@ GNUstepObjCRuntime::CreateExceptionResolver(const BreakpointSP &bkpt,
|
||||
if (throw_bp)
|
||||
resolver_sp = std::make_shared<BreakpointResolverName>(
|
||||
bkpt, "objc_exception_throw", eFunctionNameTypeBase,
|
||||
eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo);
|
||||
eLanguageTypeUnknown, Breakpoint::Exact, 0,
|
||||
/*offset_is_insn_count = */ false, eLazyBoolNo);
|
||||
|
||||
return resolver_sp;
|
||||
}
|
||||
|
||||
@@ -1601,6 +1601,7 @@ void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) {
|
||||
|
||||
const char *func_name = "_libtrace_init";
|
||||
const lldb::addr_t offset = 0;
|
||||
const bool offset_is_insn_count = false;
|
||||
const LazyBool skip_prologue = eLazyBoolCalculate;
|
||||
// This is an internal breakpoint - the user shouldn't see it.
|
||||
const bool internal = true;
|
||||
@@ -1608,7 +1609,8 @@ void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) {
|
||||
|
||||
auto breakpoint_sp = target.CreateBreakpoint(
|
||||
&module_spec_list, source_spec_list, func_name, eFunctionNameTypeFull,
|
||||
eLanguageTypeC, offset, skip_prologue, internal, hardware);
|
||||
eLanguageTypeC, offset, offset_is_insn_count, skip_prologue, internal,
|
||||
hardware);
|
||||
if (!breakpoint_sp) {
|
||||
// Huh? Bail here.
|
||||
LLDB_LOGF(log,
|
||||
|
||||
@@ -558,10 +558,11 @@ BreakpointSP Target::CreateBreakpoint(lldb::addr_t addr, bool internal,
|
||||
|
||||
BreakpointSP Target::CreateBreakpoint(const Address &addr, bool internal,
|
||||
bool hardware) {
|
||||
SearchFilterSP filter_sp(
|
||||
new SearchFilterForUnconstrainedSearches(shared_from_this()));
|
||||
BreakpointResolverSP resolver_sp(
|
||||
new BreakpointResolverAddress(nullptr, addr));
|
||||
SearchFilterSP filter_sp =
|
||||
std::make_shared<SearchFilterForUnconstrainedSearches>(
|
||||
shared_from_this());
|
||||
BreakpointResolverSP resolver_sp =
|
||||
std::make_shared<BreakpointResolverAddress>(nullptr, addr);
|
||||
return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, false);
|
||||
}
|
||||
|
||||
@@ -569,10 +570,12 @@ lldb::BreakpointSP
|
||||
Target::CreateAddressInModuleBreakpoint(lldb::addr_t file_addr, bool internal,
|
||||
const FileSpec &file_spec,
|
||||
bool request_hardware) {
|
||||
SearchFilterSP filter_sp(
|
||||
new SearchFilterForUnconstrainedSearches(shared_from_this()));
|
||||
BreakpointResolverSP resolver_sp(new BreakpointResolverAddress(
|
||||
nullptr, file_addr, file_spec));
|
||||
SearchFilterSP filter_sp =
|
||||
std::make_shared<SearchFilterForUnconstrainedSearches>(
|
||||
shared_from_this());
|
||||
BreakpointResolverSP resolver_sp =
|
||||
std::make_shared<BreakpointResolverAddress>(nullptr, file_addr,
|
||||
file_spec);
|
||||
return CreateBreakpoint(filter_sp, resolver_sp, internal, request_hardware,
|
||||
false);
|
||||
}
|
||||
@@ -581,7 +584,8 @@ BreakpointSP Target::CreateBreakpoint(
|
||||
const FileSpecList *containingModules,
|
||||
const FileSpecList *containingSourceFiles, const char *func_name,
|
||||
FunctionNameType func_name_type_mask, LanguageType language,
|
||||
lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) {
|
||||
lldb::addr_t offset, bool offset_is_insn_count, LazyBool skip_prologue,
|
||||
bool internal, bool hardware) {
|
||||
BreakpointSP bp_sp;
|
||||
if (func_name) {
|
||||
SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList(
|
||||
@@ -594,7 +598,7 @@ BreakpointSP Target::CreateBreakpoint(
|
||||
|
||||
BreakpointResolverSP resolver_sp(new BreakpointResolverName(
|
||||
nullptr, func_name, func_name_type_mask, language, Breakpoint::Exact,
|
||||
offset, skip_prologue));
|
||||
offset, offset_is_insn_count, skip_prologue));
|
||||
bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true);
|
||||
}
|
||||
return bp_sp;
|
||||
@@ -2996,6 +3000,38 @@ lldb::addr_t Target::GetBreakableLoadAddress(lldb::addr_t addr) {
|
||||
return arch_plugin ? arch_plugin->GetBreakableLoadAddress(addr, *this) : addr;
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::DisassemblerSP>
|
||||
Target::ReadInstructions(const Address &start_addr, uint32_t count,
|
||||
const char *flavor_string) {
|
||||
DataBufferHeap data(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 =
|
||||
ReadMemory(start_addr, data.GetBytes(), data.GetByteSize(), error,
|
||||
force_live_memory, &load_addr);
|
||||
|
||||
if (error.Fail())
|
||||
return llvm::createStringError(
|
||||
error.AsCString("Target::ReadInstructions failed to read memory at %s"),
|
||||
start_addr.GetLoadAddress(this));
|
||||
|
||||
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 = GetArchitecture().GetTriple().getArch();
|
||||
if (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64)
|
||||
flavor_string = GetDisassemblyFlavor();
|
||||
}
|
||||
|
||||
return Disassembler::DisassembleBytes(
|
||||
GetArchitecture(), nullptr, flavor_string, GetDisassemblyCPU(),
|
||||
GetDisassemblyFeatures(), start_addr, data.GetBytes(), bytes_read, count,
|
||||
data_from_file);
|
||||
}
|
||||
|
||||
SourceManager &Target::GetSourceManager() {
|
||||
if (!m_source_manager_up)
|
||||
m_source_manager_up = std::make_unique<SourceManager>(shared_from_this());
|
||||
|
||||
@@ -83,3 +83,79 @@ class TestDAP_setBreakpointsAssembly(lldbdap_testcase.DAPTestCaseBase):
|
||||
break_point["message"],
|
||||
"Invalid sourceReference.",
|
||||
)
|
||||
|
||||
@skipIfWindows
|
||||
def test_persistent_assembly_breakpoint(self):
|
||||
"""Tests that assembly breakpoints are working persistently across sessions"""
|
||||
self.build()
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.create_debug_adapter()
|
||||
|
||||
# Run the first session and set a persistent assembly breakpoint
|
||||
try:
|
||||
self.dap_server.request_initialize()
|
||||
self.dap_server.request_launch(program)
|
||||
|
||||
assmebly_func_breakpoints = self.set_function_breakpoints(["assembly_func"])
|
||||
self.continue_to_breakpoints(assmebly_func_breakpoints)
|
||||
|
||||
assembly_func_frame = self.get_stackFrames()[0]
|
||||
source_reference = assembly_func_frame["source"]["sourceReference"]
|
||||
|
||||
# Set an assembly breakpoint in the middle of the assembly function
|
||||
persistent_breakpoint_line = 4
|
||||
persistent_breakpoint_ids = self.set_source_breakpoints_assembly(
|
||||
source_reference, [persistent_breakpoint_line]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
len(persistent_breakpoint_ids),
|
||||
1,
|
||||
"Expected one assembly breakpoint to be set",
|
||||
)
|
||||
|
||||
persistent_breakpoint_source = self.dap_server.resolved_breakpoints[
|
||||
persistent_breakpoint_ids[0]
|
||||
].source()
|
||||
self.assertIn(
|
||||
"adapterData",
|
||||
persistent_breakpoint_source,
|
||||
"Expected assembly breakpoint to have persistent information",
|
||||
)
|
||||
self.assertIn(
|
||||
"persistenceData",
|
||||
persistent_breakpoint_source["adapterData"],
|
||||
"Expected assembly breakpoint to have persistent information",
|
||||
)
|
||||
|
||||
self.continue_to_breakpoints(persistent_breakpoint_ids)
|
||||
finally:
|
||||
self.dap_server.request_disconnect(terminateDebuggee=True)
|
||||
self.dap_server.terminate()
|
||||
|
||||
# Restart the session and verify the breakpoint is still there
|
||||
self.create_debug_adapter()
|
||||
try:
|
||||
self.dap_server.request_initialize()
|
||||
self.dap_server.request_launch(program)
|
||||
new_session_breakpoints_ids = self.set_source_breakpoints_from_source(
|
||||
Source(raw_dict=persistent_breakpoint_source),
|
||||
[persistent_breakpoint_line],
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
len(new_session_breakpoints_ids),
|
||||
1,
|
||||
"Expected one breakpoint to be set in the new session",
|
||||
)
|
||||
|
||||
self.continue_to_breakpoints(new_session_breakpoints_ids)
|
||||
current_line = self.get_stackFrames()[0]["line"]
|
||||
self.assertEqual(
|
||||
current_line,
|
||||
persistent_breakpoint_line,
|
||||
"Expected to hit the persistent assembly breakpoint at the same line",
|
||||
)
|
||||
finally:
|
||||
self.dap_server.request_disconnect(terminateDebuggee=True)
|
||||
self.dap_server.terminate()
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
|
||||
#include "Breakpoint.h"
|
||||
#include "DAP.h"
|
||||
#include "LLDBUtils.h"
|
||||
#include "Protocol/DAPTypes.h"
|
||||
#include "ProtocolUtils.h"
|
||||
#include "lldb/API/SBAddress.h"
|
||||
#include "lldb/API/SBBreakpointLocation.h"
|
||||
#include "lldb/API/SBFileSpec.h"
|
||||
#include "lldb/API/SBLineEntry.h"
|
||||
#include "lldb/API/SBModule.h"
|
||||
#include "lldb/API/SBMutex.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include <cstddef>
|
||||
@@ -21,6 +25,22 @@
|
||||
|
||||
using namespace lldb_dap;
|
||||
|
||||
static std::optional<protocol::PersistenceData>
|
||||
GetPersistenceDataForSymbol(lldb::SBSymbol &symbol) {
|
||||
protocol::PersistenceData persistence_data;
|
||||
lldb::SBModule module = symbol.GetStartAddress().GetModule();
|
||||
if (!module.IsValid())
|
||||
return std::nullopt;
|
||||
|
||||
lldb::SBFileSpec file_spec = module.GetFileSpec();
|
||||
if (!file_spec.IsValid())
|
||||
return std::nullopt;
|
||||
|
||||
persistence_data.module_path = GetSBFileSpecPath(file_spec);
|
||||
persistence_data.symbol_name = symbol.GetName();
|
||||
return persistence_data;
|
||||
}
|
||||
|
||||
void Breakpoint::SetCondition() { m_bp.SetCondition(m_condition.c_str()); }
|
||||
|
||||
void Breakpoint::SetHitCondition() {
|
||||
@@ -73,7 +93,7 @@ protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() {
|
||||
const auto column = line_entry.GetColumn();
|
||||
if (column != LLDB_INVALID_COLUMN_NUMBER)
|
||||
breakpoint.column = column;
|
||||
} else {
|
||||
} else if (source) {
|
||||
// Assembly breakpoint.
|
||||
auto symbol = bp_addr.GetSymbol();
|
||||
if (symbol.IsValid()) {
|
||||
@@ -82,6 +102,15 @@ protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() {
|
||||
.ReadInstructions(symbol.GetStartAddress(), bp_addr, nullptr)
|
||||
.GetSize() +
|
||||
1;
|
||||
|
||||
// Add persistent data so that the breakpoint can be resolved
|
||||
// in future sessions.
|
||||
std::optional<protocol::PersistenceData> persistence_data =
|
||||
GetPersistenceDataForSymbol(symbol);
|
||||
if (persistence_data) {
|
||||
source->adapterData =
|
||||
protocol::SourceLLDBData{std::move(persistence_data)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ add_lldb_library(lldbDAP
|
||||
Handler/ThreadsRequestHandler.cpp
|
||||
Handler/VariablesRequestHandler.cpp
|
||||
Handler/WriteMemoryRequestHandler.cpp
|
||||
|
||||
|
||||
Protocol/DAPTypes.cpp
|
||||
Protocol/ProtocolBase.cpp
|
||||
Protocol/ProtocolEvents.cpp
|
||||
Protocol/ProtocolTypes.cpp
|
||||
|
||||
@@ -1406,11 +1406,15 @@ void DAP::EventThread() {
|
||||
// avoids sending paths that should be source mapped. Note that
|
||||
// CreateBreakpoint doesn't apply source mapping and certain
|
||||
// implementation ignore the source part of this event anyway.
|
||||
llvm::json::Value source_bp = bp.ToProtocolBreakpoint();
|
||||
source_bp.getAsObject()->erase("source");
|
||||
protocol::Breakpoint protocol_bp = bp.ToProtocolBreakpoint();
|
||||
|
||||
// "source" is not needed here, unless we add adapter data to be
|
||||
// saved by the client.
|
||||
if (protocol_bp.source && !protocol_bp.source->adapterData)
|
||||
protocol_bp.source = std::nullopt;
|
||||
|
||||
llvm::json::Object body;
|
||||
body.try_emplace("breakpoint", source_bp);
|
||||
body.try_emplace("breakpoint", protocol_bp);
|
||||
body.try_emplace("reason", "changed");
|
||||
|
||||
llvm::json::Object bp_event = CreateEventObject("breakpoint");
|
||||
@@ -1491,8 +1495,9 @@ std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints(
|
||||
|
||||
protocol::Breakpoint response_breakpoint =
|
||||
iv->second.ToProtocolBreakpoint();
|
||||
response_breakpoint.source = source;
|
||||
|
||||
if (!response_breakpoint.source)
|
||||
response_breakpoint.source = source;
|
||||
if (!response_breakpoint.line &&
|
||||
src_bp.GetLine() != LLDB_INVALID_LINE_NUMBER)
|
||||
response_breakpoint.line = src_bp.GetLine();
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "DAP.h"
|
||||
#include "Protocol/ProtocolRequests.h"
|
||||
#include "RequestHandler.h"
|
||||
#include <vector>
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
|
||||
36
lldb/tools/lldb-dap/Protocol/DAPTypes.cpp
Normal file
36
lldb/tools/lldb-dap/Protocol/DAPTypes.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "Protocol/DAPTypes.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace lldb_dap::protocol {
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, PersistenceData &PD,
|
||||
llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.mapOptional("module_path", PD.module_path) &&
|
||||
O.mapOptional("symbol_name", PD.symbol_name);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const PersistenceData &PD) {
|
||||
json::Object result{
|
||||
{"module_path", PD.module_path},
|
||||
{"symbol_name", PD.symbol_name},
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fromJSON(const llvm::json::Value &Params, SourceLLDBData &SLD,
|
||||
llvm::json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.mapOptional("persistenceData", SLD.persistenceData);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(const SourceLLDBData &SLD) {
|
||||
json::Object result;
|
||||
if (SLD.persistenceData)
|
||||
result.insert({"persistenceData", SLD.persistenceData});
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace lldb_dap::protocol
|
||||
53
lldb/tools/lldb-dap/Protocol/DAPTypes.h
Normal file
53
lldb/tools/lldb-dap/Protocol/DAPTypes.h
Normal file
@@ -0,0 +1,53 @@
|
||||
//===-- ProtocolTypes.h ---------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains private DAP types used in the protocol.
|
||||
//
|
||||
// Each struct has a toJSON and fromJSON function, that converts between
|
||||
// the struct and a JSON representation. (See JSON.h)
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_DAP_TYPES_H
|
||||
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_DAP_TYPES_H
|
||||
|
||||
#include "lldb/lldb-types.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace lldb_dap::protocol {
|
||||
|
||||
/// Data used to help lldb-dap resolve breakpoints persistently across different
|
||||
/// sessions. This information is especially useful for assembly breakpoints,
|
||||
/// because `sourceReference` can change across sessions. For regular source
|
||||
/// breakpoints the path and line are the same For each session.
|
||||
struct PersistenceData {
|
||||
/// The source module path.
|
||||
std::string module_path;
|
||||
|
||||
/// The symbol name of the Source.
|
||||
std::string symbol_name;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, PersistenceData &, llvm::json::Path);
|
||||
llvm::json::Value toJSON(const PersistenceData &);
|
||||
|
||||
/// Custom source data used by lldb-dap.
|
||||
/// This data should help lldb-dap identify sources correctly across different
|
||||
/// sessions.
|
||||
struct SourceLLDBData {
|
||||
/// Data that helps lldb resolve this source persistently across different
|
||||
/// sessions.
|
||||
std::optional<PersistenceData> persistenceData;
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, SourceLLDBData &, llvm::json::Path);
|
||||
llvm::json::Value toJSON(const SourceLLDBData &);
|
||||
|
||||
} // namespace lldb_dap::protocol
|
||||
|
||||
#endif
|
||||
@@ -46,7 +46,8 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
|
||||
json::ObjectMapper O(Params, P);
|
||||
return O && O.map("name", S.name) && O.map("path", S.path) &&
|
||||
O.map("presentationHint", S.presentationHint) &&
|
||||
O.map("sourceReference", S.sourceReference);
|
||||
O.map("sourceReference", S.sourceReference) &&
|
||||
O.map("adapterData", S.adapterData);
|
||||
}
|
||||
|
||||
llvm::json::Value toJSON(Source::PresentationHint hint) {
|
||||
@@ -71,6 +72,8 @@ llvm::json::Value toJSON(const Source &S) {
|
||||
result.insert({"sourceReference", *S.sourceReference});
|
||||
if (S.presentationHint)
|
||||
result.insert({"presentationHint", *S.presentationHint});
|
||||
if (S.adapterData)
|
||||
result.insert({"adapterData", *S.adapterData});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
|
||||
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
|
||||
|
||||
#include "Protocol/DAPTypes.h"
|
||||
#include "lldb/lldb-defines.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
@@ -336,7 +337,12 @@ struct Source {
|
||||
/// skipped on stepping.
|
||||
std::optional<PresentationHint> presentationHint;
|
||||
|
||||
// unsupported keys: origin, sources, adapterData, checksums
|
||||
/// Additional data that a debug adapter might want to loop through the
|
||||
/// client. The client should leave the data intact and persist it across
|
||||
/// sessions. The client should not interpret the data.
|
||||
std::optional<SourceLLDBData> adapterData;
|
||||
|
||||
// unsupported keys: origin, sources, checksums
|
||||
};
|
||||
bool fromJSON(const llvm::json::Value &, Source::PresentationHint &,
|
||||
llvm::json::Path);
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#include "BreakpointBase.h"
|
||||
#include "DAP.h"
|
||||
#include "JSONUtils.h"
|
||||
#include "ProtocolUtils.h"
|
||||
#include "lldb/API/SBBreakpoint.h"
|
||||
#include "lldb/API/SBFileSpec.h"
|
||||
#include "lldb/API/SBFileSpecList.h"
|
||||
#include "lldb/API/SBFrame.h"
|
||||
#include "lldb/API/SBInstruction.h"
|
||||
@@ -46,41 +48,20 @@ llvm::Error SourceBreakpoint::SetBreakpoint(const protocol::Source &source) {
|
||||
|
||||
if (source.sourceReference) {
|
||||
// Breakpoint set by assembly source.
|
||||
std::optional<lldb::addr_t> raw_addr =
|
||||
m_dap.GetSourceReferenceAddress(*source.sourceReference);
|
||||
if (!raw_addr)
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"Invalid sourceReference.");
|
||||
|
||||
lldb::SBAddress source_address(*raw_addr, m_dap.target);
|
||||
if (!source_address.IsValid())
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"Invalid sourceReference.");
|
||||
|
||||
lldb::SBSymbol symbol = source_address.GetSymbol();
|
||||
if (!symbol.IsValid()) {
|
||||
// FIXME: Support assembly breakpoints without a valid symbol.
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"Breakpoints in assembly without a valid "
|
||||
"symbol are not supported yet.");
|
||||
if (source.adapterData && source.adapterData->persistenceData) {
|
||||
// Prefer use the adapter persitence data, because this could be a
|
||||
// breakpoint from a previous session where the `sourceReference` is not
|
||||
// valid anymore.
|
||||
if (llvm::Error error = CreateAssemblyBreakpointWithPersistenceData(
|
||||
*source.adapterData->persistenceData))
|
||||
return error;
|
||||
} else {
|
||||
if (llvm::Error error = CreateAssemblyBreakpointWithSourceReference(
|
||||
*source.sourceReference))
|
||||
return error;
|
||||
}
|
||||
|
||||
lldb::SBInstructionList inst_list =
|
||||
m_dap.target.ReadInstructions(symbol.GetStartAddress(), m_line);
|
||||
if (inst_list.GetSize() < m_line)
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"Invalid instruction list size.");
|
||||
|
||||
lldb::SBAddress address =
|
||||
inst_list.GetInstructionAtIndex(m_line - 1).GetAddress();
|
||||
|
||||
m_bp = m_dap.target.BreakpointCreateBySBAddress(address);
|
||||
} else {
|
||||
// Breakpoint set by a regular source file.
|
||||
const auto source_path = source.path.value_or("");
|
||||
lldb::SBFileSpecList module_list;
|
||||
m_bp = m_dap.target.BreakpointCreateByLocation(source_path.c_str(), m_line,
|
||||
m_column, 0, module_list);
|
||||
CreatePathBreakpoint(source);
|
||||
}
|
||||
|
||||
if (!m_log_message.empty())
|
||||
@@ -97,6 +78,60 @@ void SourceBreakpoint::UpdateBreakpoint(const SourceBreakpoint &request_bp) {
|
||||
BreakpointBase::UpdateBreakpoint(request_bp);
|
||||
}
|
||||
|
||||
void SourceBreakpoint::CreatePathBreakpoint(const protocol::Source &source) {
|
||||
const auto source_path = source.path.value_or("");
|
||||
lldb::SBFileSpecList module_list;
|
||||
m_bp = m_dap.target.BreakpointCreateByLocation(source_path.c_str(), m_line,
|
||||
m_column, 0, module_list);
|
||||
}
|
||||
|
||||
llvm::Error SourceBreakpoint::CreateAssemblyBreakpointWithSourceReference(
|
||||
int64_t source_reference) {
|
||||
std::optional<lldb::addr_t> raw_addr =
|
||||
m_dap.GetSourceReferenceAddress(source_reference);
|
||||
if (!raw_addr)
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"Invalid sourceReference.");
|
||||
|
||||
lldb::SBAddress source_address(*raw_addr, m_dap.target);
|
||||
if (!source_address.IsValid())
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"Invalid sourceReference.");
|
||||
|
||||
lldb::SBSymbol symbol = source_address.GetSymbol();
|
||||
if (!symbol.IsValid()) {
|
||||
// FIXME: Support assembly breakpoints without a valid symbol.
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"Breakpoints in assembly without a valid "
|
||||
"symbol are not supported yet.");
|
||||
}
|
||||
|
||||
lldb::SBInstructionList inst_list =
|
||||
m_dap.target.ReadInstructions(symbol.GetStartAddress(), m_line);
|
||||
if (inst_list.GetSize() < m_line)
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"Invalid instruction list size.");
|
||||
|
||||
lldb::SBAddress address =
|
||||
inst_list.GetInstructionAtIndex(m_line - 1).GetAddress();
|
||||
|
||||
m_bp = m_dap.target.BreakpointCreateBySBAddress(address);
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
llvm::Error SourceBreakpoint::CreateAssemblyBreakpointWithPersistenceData(
|
||||
const protocol::PersistenceData &persistence_data) {
|
||||
lldb::SBFileSpec file_spec(persistence_data.module_path.c_str());
|
||||
lldb::SBFileSpecList comp_unit_list;
|
||||
lldb::SBFileSpecList file_spec_list;
|
||||
file_spec_list.Append(file_spec);
|
||||
m_bp = m_dap.target.BreakpointCreateByName(
|
||||
persistence_data.symbol_name.c_str(), lldb::eFunctionNameTypeFull,
|
||||
lldb::eLanguageTypeUnknown, m_line - 1, true, file_spec_list,
|
||||
comp_unit_list);
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
lldb::SBError SourceBreakpoint::AppendLogMessagePart(llvm::StringRef part,
|
||||
bool is_expr) {
|
||||
if (is_expr) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "Breakpoint.h"
|
||||
#include "DAPForward.h"
|
||||
#include "Protocol/DAPTypes.h"
|
||||
#include "Protocol/ProtocolTypes.h"
|
||||
#include "lldb/API/SBError.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
@@ -50,6 +51,12 @@ public:
|
||||
uint32_t GetColumn() const { return m_column; }
|
||||
|
||||
protected:
|
||||
void CreatePathBreakpoint(const protocol::Source &source);
|
||||
llvm::Error
|
||||
CreateAssemblyBreakpointWithSourceReference(int64_t source_reference);
|
||||
llvm::Error CreateAssemblyBreakpointWithPersistenceData(
|
||||
const protocol::PersistenceData &persistence_data);
|
||||
|
||||
// logMessage part can be either a raw text or an expression.
|
||||
struct LogMessagePart {
|
||||
LogMessagePart(llvm::StringRef text, bool is_expr)
|
||||
|
||||
Reference in New Issue
Block a user