Files
llvm/lldb/source/Breakpoint/BreakpointLocationList.cpp
jimingham 36bce68b97 Add a scripted way to re-present a stop location (#158128)
This patch adds the notion of "Facade" locations which can be reported
from a ScriptedResolver instead of the actual underlying breakpoint
location for the breakpoint. Also add a "was_hit" method to the scripted
resolver that allows the breakpoint to say which of these "Facade"
locations was hit, and "get_location_description" to provide a
description for the facade locations.

I apologize in advance for the size of the patch. Almost all of what's
here was necessary to (a) make the feature testable and (b) not break
any of the current behavior.

The motivation for this feature is given in the "Providing Facade
Locations" section that I added to the python-reference.rst so I won't
repeat it here.

rdar://152112327
2025-10-09 08:37:21 -07:00

327 lines
11 KiB
C++

//===-- BreakpointLocationList.cpp ----------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "lldb/Breakpoint/BreakpointLocationList.h"
#include "lldb/Breakpoint/Breakpoint.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Section.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
using namespace lldb;
using namespace lldb_private;
BreakpointLocationList::BreakpointLocationList(Breakpoint &owner)
: m_owner(owner), m_next_id(0), m_new_location_recorder(nullptr) {}
BreakpointLocationList::~BreakpointLocationList() = default;
BreakpointLocationSP
BreakpointLocationList::Create(const Address &addr,
bool resolve_indirect_symbols) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
// The location ID is just the size of the location list + 1
lldb::break_id_t bp_loc_id = ++m_next_id;
BreakpointLocationSP bp_loc_sp(
new BreakpointLocation(bp_loc_id, m_owner, addr, LLDB_INVALID_THREAD_ID,
resolve_indirect_symbols));
m_locations.push_back(bp_loc_sp);
m_address_to_location[addr] = bp_loc_sp;
return bp_loc_sp;
}
bool BreakpointLocationList::ShouldStop(StoppointCallbackContext *context,
lldb::break_id_t break_id,
lldb::BreakpointLocationSP &bp_loc_sp) {
BreakpointLocationSP bp = FindByID(break_id);
if (bp) {
// Let the BreakpointLocation decide if it should stop here (could not have
// reached it's target hit count yet, or it could have a callback that
// decided it shouldn't stop (shared library loads/unloads).
return bp->ShouldStop(context, bp_loc_sp);
}
// We should stop here since this BreakpointLocation isn't valid anymore or
// it doesn't exist.
return true;
}
lldb::break_id_t BreakpointLocationList::FindIDByAddress(const Address &addr) {
BreakpointLocationSP bp_loc_sp = FindByAddress(addr);
if (bp_loc_sp) {
return bp_loc_sp->GetID();
}
return LLDB_INVALID_BREAK_ID;
}
static bool Compare(BreakpointLocationSP lhs, lldb::break_id_t val) {
return lhs->GetID() < val;
}
BreakpointLocationSP
BreakpointLocationList::FindByID(lldb::break_id_t break_id) const {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
collection::const_iterator end = m_locations.end();
collection::const_iterator pos =
llvm::lower_bound(m_locations, break_id, Compare);
if (pos != end && (*pos)->GetID() == break_id)
return *(pos);
return BreakpointLocationSP();
}
size_t BreakpointLocationList::FindInModule(
Module *module, BreakpointLocationCollection &bp_loc_list) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
const size_t orig_size = bp_loc_list.GetSize();
collection::iterator pos, end = m_locations.end();
for (pos = m_locations.begin(); pos != end; ++pos) {
BreakpointLocationSP break_loc = (*pos);
SectionSP section_sp(break_loc->GetAddress().GetSection());
if (section_sp && section_sp->GetModule().get() == module) {
bp_loc_list.Add(break_loc);
}
}
return bp_loc_list.GetSize() - orig_size;
}
const BreakpointLocationSP
BreakpointLocationList::FindByAddress(const Address &addr) const {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
BreakpointLocationSP bp_loc_sp;
if (!m_locations.empty()) {
Address so_addr;
if (addr.IsSectionOffset()) {
so_addr = addr;
} else {
// Try and resolve as a load address if possible.
m_owner.GetTarget().ResolveLoadAddress(addr.GetOffset(), so_addr);
if (!so_addr.IsValid()) {
// The address didn't resolve, so just set to passed in addr.
so_addr = addr;
}
}
addr_map::const_iterator pos = m_address_to_location.find(so_addr);
if (pos != m_address_to_location.end())
bp_loc_sp = pos->second;
}
return bp_loc_sp;
}
void BreakpointLocationList::Dump(Stream *s) const {
s->Printf("%p: ", static_cast<const void *>(this));
// s->Indent();
std::lock_guard<std::recursive_mutex> guard(m_mutex);
s->Printf("BreakpointLocationList with %" PRIu64 " BreakpointLocations:\n",
(uint64_t)m_locations.size());
s->IndentMore();
collection::const_iterator pos, end = m_locations.end();
for (pos = m_locations.begin(); pos != end; ++pos)
(*pos)->Dump(s);
s->IndentLess();
}
BreakpointLocationSP BreakpointLocationList::GetByIndex(size_t i) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
BreakpointLocationSP bp_loc_sp;
if (i < m_locations.size())
bp_loc_sp = m_locations[i];
return bp_loc_sp;
}
const BreakpointLocationSP BreakpointLocationList::GetByIndex(size_t i) const {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
BreakpointLocationSP bp_loc_sp;
if (i < m_locations.size())
bp_loc_sp = m_locations[i];
return bp_loc_sp;
}
void BreakpointLocationList::ClearAllBreakpointSites() {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
collection::iterator pos, end = m_locations.end();
Log *log = GetLog(LLDBLog::Breakpoints);
for (pos = m_locations.begin(); pos != end; ++pos) {
if (llvm::Error error = (*pos)->ClearBreakpointSite())
LLDB_LOG_ERROR(log, std::move(error), "{0}");
}
}
void BreakpointLocationList::ResolveAllBreakpointSites() {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
collection::iterator pos, end = m_locations.end();
Log *log = GetLog(LLDBLog::Breakpoints);
for (pos = m_locations.begin(); pos != end; ++pos) {
if ((*pos)->IsEnabled()) {
if (llvm::Error error = (*pos)->ResolveBreakpointSite())
LLDB_LOG_ERROR(log, std::move(error), "{0}");
}
}
}
uint32_t BreakpointLocationList::GetHitCount() const {
uint32_t hit_count = 0;
std::lock_guard<std::recursive_mutex> guard(m_mutex);
collection::const_iterator pos, end = m_locations.end();
for (pos = m_locations.begin(); pos != end; ++pos)
hit_count += (*pos)->GetHitCount();
return hit_count;
}
void BreakpointLocationList::ResetHitCount() {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
for (auto &loc : m_locations)
loc->ResetHitCount();
}
size_t BreakpointLocationList::GetNumResolvedLocations() const {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
size_t resolve_count = 0;
collection::const_iterator pos, end = m_locations.end();
for (pos = m_locations.begin(); pos != end; ++pos) {
if ((*pos)->IsResolved())
++resolve_count;
}
return resolve_count;
}
void BreakpointLocationList::GetDescription(Stream *s,
lldb::DescriptionLevel level) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
collection::iterator pos, end = m_locations.end();
for (pos = m_locations.begin(); pos != end; ++pos) {
s->Printf(" ");
(*pos)->GetDescription(s, level);
}
}
BreakpointLocationSP BreakpointLocationList::AddLocation(
const Address &addr, bool resolve_indirect_symbols, bool *new_location) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
if (new_location)
*new_location = false;
BreakpointLocationSP bp_loc_sp(FindByAddress(addr));
if (!bp_loc_sp) {
bp_loc_sp = Create(addr, resolve_indirect_symbols);
if (bp_loc_sp) {
if (llvm::Error error = bp_loc_sp->ResolveBreakpointSite())
LLDB_LOG_ERROR(GetLog(LLDBLog::Breakpoints), std::move(error), "{0}");
if (new_location)
*new_location = true;
if (m_new_location_recorder) {
m_new_location_recorder->Add(bp_loc_sp);
}
}
}
return bp_loc_sp;
}
void BreakpointLocationList::SwapLocation(
BreakpointLocationSP to_location_sp,
BreakpointLocationSP from_location_sp) {
if (!from_location_sp || !to_location_sp)
return;
m_address_to_location.erase(to_location_sp->GetAddress());
to_location_sp->SwapLocation(from_location_sp);
RemoveLocation(from_location_sp);
m_address_to_location[to_location_sp->GetAddress()] = to_location_sp;
llvm::consumeError(to_location_sp->ResolveBreakpointSite());
}
bool BreakpointLocationList::RemoveLocation(
const lldb::BreakpointLocationSP &bp_loc_sp) {
if (bp_loc_sp) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
m_address_to_location.erase(bp_loc_sp->GetAddress());
size_t num_locations = m_locations.size();
for (size_t idx = 0; idx < num_locations; idx++) {
if (m_locations[idx].get() == bp_loc_sp.get()) {
RemoveLocationByIndex(idx);
return true;
}
}
}
return false;
}
void BreakpointLocationList::RemoveLocationByIndex(size_t idx) {
assert (idx < m_locations.size());
m_address_to_location.erase(m_locations[idx]->GetAddress());
m_locations.erase(m_locations.begin() + idx);
}
void BreakpointLocationList::RemoveInvalidLocations(const ArchSpec &arch) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
size_t idx = 0;
// Don't cache m_location.size() as it will change since we might remove
// locations from our vector...
while (idx < m_locations.size()) {
BreakpointLocation *bp_loc = m_locations[idx].get();
if (bp_loc->GetAddress().SectionWasDeleted()) {
// Section was deleted which means this breakpoint comes from a module
// that is no longer valid, so we should remove it.
RemoveLocationByIndex(idx);
continue;
}
if (arch.IsValid()) {
ModuleSP module_sp(bp_loc->GetAddress().GetModule());
if (module_sp) {
if (!arch.IsCompatibleMatch(module_sp->GetArchitecture())) {
// The breakpoint was in a module whose architecture is no longer
// compatible with "arch", so we need to remove it
RemoveLocationByIndex(idx);
continue;
}
}
}
// Only increment the index if we didn't remove the locations at index
// "idx"
++idx;
}
}
void BreakpointLocationList::StartRecordingNewLocations(
BreakpointLocationCollection &new_locations) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
assert(m_new_location_recorder == nullptr);
m_new_location_recorder = &new_locations;
}
void BreakpointLocationList::StopRecordingNewLocations() {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
m_new_location_recorder = nullptr;
}
void BreakpointLocationList::Compact() {
lldb::break_id_t highest_id = 0;
for (BreakpointLocationSP loc_sp : m_locations) {
lldb::break_id_t cur_id = loc_sp->GetID();
if (cur_id > highest_id)
highest_id = cur_id;
}
m_next_id = highest_id;
}