aboutsummaryrefslogtreecommitdiff
path: root/lldb/source
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source')
-rw-r--r--lldb/source/Breakpoint/CMakeLists.txt1
-rw-r--r--lldb/source/Breakpoint/Watchpoint.cpp28
-rw-r--r--lldb/source/Breakpoint/WatchpointAlgorithms.cpp159
-rw-r--r--lldb/source/Breakpoint/WatchpointResource.cpp4
-rw-r--r--lldb/source/Commands/CommandObjectWatchpoint.cpp15
-rw-r--r--lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp13
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp34
-rw-r--r--lldb/source/Target/StopInfo.cpp4
8 files changed, 227 insertions, 31 deletions
diff --git a/lldb/source/Breakpoint/CMakeLists.txt b/lldb/source/Breakpoint/CMakeLists.txt
index 3b39189..2fa659f 100644
--- a/lldb/source/Breakpoint/CMakeLists.txt
+++ b/lldb/source/Breakpoint/CMakeLists.txt
@@ -21,6 +21,7 @@ add_lldb_library(lldbBreakpoint NO_PLUGIN_DEPENDENCIES
StoppointSite.cpp
StopPointSiteList.cpp
Watchpoint.cpp
+ WatchpointAlgorithms.cpp
WatchpointList.cpp
WatchpointOptions.cpp
WatchpointResource.cpp
diff --git a/lldb/source/Breakpoint/Watchpoint.cpp b/lldb/source/Breakpoint/Watchpoint.cpp
index 79f5741..a128ced5 100644
--- a/lldb/source/Breakpoint/Watchpoint.cpp
+++ b/lldb/source/Breakpoint/Watchpoint.cpp
@@ -44,10 +44,16 @@ Watchpoint::Watchpoint(Target &target, lldb::addr_t addr, uint32_t size,
LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
"Failed to set type: {0}");
} else {
- if (auto ts = *type_system_or_err)
- m_type =
- ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8 * size);
- else
+ if (auto ts = *type_system_or_err) {
+ if (size <= target.GetArchitecture().GetAddressByteSize()) {
+ m_type =
+ ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8 * size);
+ } else {
+ CompilerType clang_uint8_type =
+ ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);
+ m_type = clang_uint8_type.GetArrayType(size);
+ }
+ } else
LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
"Failed to set type: Typesystem is no longer live: {0}");
}
@@ -350,6 +356,20 @@ void Watchpoint::DumpWithLevel(Stream *s,
s->Printf("\n declare @ '%s'", m_decl_str.c_str());
if (!m_watch_spec_str.empty())
s->Printf("\n watchpoint spec = '%s'", m_watch_spec_str.c_str());
+ if (IsEnabled()) {
+ if (ProcessSP process_sp = m_target.GetProcessSP()) {
+ auto &resourcelist = process_sp->GetWatchpointResourceList();
+ size_t idx = 0;
+ s->Printf("\n watchpoint resources:");
+ for (WatchpointResourceSP &wpres : resourcelist.Sites()) {
+ if (wpres->ConstituentsContains(this)) {
+ s->Printf("\n #%zu: ", idx);
+ wpres->Dump(s);
+ }
+ idx++;
+ }
+ }
+ }
// Dump the snapshots we have taken.
DumpSnapshots(s, " ");
diff --git a/lldb/source/Breakpoint/WatchpointAlgorithms.cpp b/lldb/source/Breakpoint/WatchpointAlgorithms.cpp
new file mode 100644
index 0000000..94f1dff
--- /dev/null
+++ b/lldb/source/Breakpoint/WatchpointAlgorithms.cpp
@@ -0,0 +1,159 @@
+//===-- WatchpointAlgorithms.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/WatchpointAlgorithms.h"
+#include "lldb/Breakpoint/WatchpointResource.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+std::vector<WatchpointResourceSP>
+WatchpointAlgorithms::AtomizeWatchpointRequest(
+ addr_t addr, size_t size, bool read, bool write,
+ WatchpointHardwareFeature supported_features, ArchSpec &arch) {
+
+ std::vector<Region> entries;
+
+ if (supported_features &
+ WatchpointHardwareFeature::eWatchpointHardwareArmMASK) {
+ entries =
+ PowerOf2Watchpoints(addr, size,
+ /*min_byte_size*/ 1,
+ /*max_byte_size*/ INT32_MAX,
+ /*address_byte_size*/ arch.GetAddressByteSize());
+ } else {
+ // As a fallback, assume we can watch any power-of-2
+ // number of bytes up through the size of an address in the target.
+ entries =
+ PowerOf2Watchpoints(addr, size,
+ /*min_byte_size*/ 1,
+ /*max_byte_size*/ arch.GetAddressByteSize(),
+ /*address_byte_size*/ arch.GetAddressByteSize());
+ }
+
+ Log *log = GetLog(LLDBLog::Watchpoints);
+ LLDB_LOGV(log, "AtomizeWatchpointRequest user request addr {0:x} size {1}",
+ addr, size);
+ std::vector<WatchpointResourceSP> resources;
+ for (Region &ent : entries) {
+ LLDB_LOGV(log, "AtomizeWatchpointRequest creating resource {0:x} size {1}",
+ ent.addr, ent.size);
+ WatchpointResourceSP wp_res_sp =
+ std::make_shared<WatchpointResource>(ent.addr, ent.size, read, write);
+ resources.push_back(wp_res_sp);
+ }
+
+ return resources;
+}
+
+// This should be `std::bit_ceil(aligned_size)` but
+// that requires C++20.
+// Calculates the smallest integral power of two that is not smaller than x.
+static uint64_t bit_ceil(uint64_t input) {
+ if (input <= 1 || llvm::popcount(input) == 1)
+ return input;
+
+ return 1ULL << (64 - llvm::countl_zero(input));
+}
+
+/// Convert a user's watchpoint request (\a user_addr and \a user_size)
+/// into hardware watchpoints, for a target that can watch a power-of-2
+/// region of memory (1, 2, 4, 8, etc), aligned to that same power-of-2
+/// memory address.
+///
+/// If a user asks to watch 4 bytes at address 0x1002 (0x1002-0x1005
+/// inclusive) we can implement this with two 2-byte watchpoints
+/// (0x1002 and 0x1004) or with an 8-byte watchpoint at 0x1000.
+/// A 4-byte watchpoint at 0x1002 would not be properly 4 byte aligned.
+///
+/// If a user asks to watch 16 bytes at 0x1000, and this target supports
+/// 8-byte watchpoints, we can implement this with two 8-byte watchpoints
+/// at 0x1000 and 0x1008.
+std::vector<WatchpointAlgorithms::Region>
+WatchpointAlgorithms::PowerOf2Watchpoints(addr_t user_addr, size_t user_size,
+ size_t min_byte_size,
+ size_t max_byte_size,
+ uint32_t address_byte_size) {
+
+ Log *log = GetLog(LLDBLog::Watchpoints);
+ LLDB_LOGV(log,
+ "AtomizeWatchpointRequest user request addr {0:x} size {1} "
+ "min_byte_size {2}, max_byte_size {3}, address_byte_size {4}",
+ user_addr, user_size, min_byte_size, max_byte_size,
+ address_byte_size);
+
+ // Can't watch zero bytes.
+ if (user_size == 0)
+ return {};
+
+ size_t aligned_size = std::max(user_size, min_byte_size);
+ /// Round up \a user_size to the next power-of-2 size
+ /// user_size == 8 -> aligned_size == 8
+ /// user_size == 9 -> aligned_size == 16
+ aligned_size = bit_ceil(aligned_size);
+
+ addr_t aligned_start = user_addr & ~(aligned_size - 1);
+
+ // Does this power-of-2 memory range, aligned to power-of-2 that the
+ // hardware can watch, completely cover the requested region.
+ if (aligned_size <= max_byte_size &&
+ aligned_start + aligned_size >= user_addr + user_size)
+ return {{aligned_start, aligned_size}};
+
+ // If the maximum region we can watch is larger than the aligned
+ // size, try increasing the region size by one power of 2 and see
+ // if aligning to that amount can cover the requested region.
+ //
+ // Increasing the aligned_size repeatedly instead of splitting the
+ // watchpoint can result in us watching large regions of memory
+ // unintentionally when we could use small two watchpoints. e.g.
+ // user_addr 0x3ff8 user_size 32
+ // can be watched with four 8-byte watchpoints or if it's done with one
+ // MASK watchpoint, it would need to be a 32KB watchpoint (a 16KB
+ // watchpoint at 0x0 only covers 0x0000-0x4000). A user request
+ // at the end of a power-of-2 region can lead to these undesirably
+ // large watchpoints and many false positive hits to ignore.
+ if (max_byte_size >= (aligned_size << 1)) {
+ aligned_size <<= 1;
+ aligned_start = user_addr & ~(aligned_size - 1);
+ if (aligned_size <= max_byte_size &&
+ aligned_start + aligned_size >= user_addr + user_size)
+ return {{aligned_start, aligned_size}};
+
+ // Go back to our original aligned size, to try the multiple
+ // watchpoint approach.
+ aligned_size >>= 1;
+ }
+
+ // We need to split the user's watchpoint into two or more watchpoints
+ // that can be monitored by hardware, because of alignment and/or size
+ // reasons.
+ aligned_size = std::min(aligned_size, max_byte_size);
+ aligned_start = user_addr & ~(aligned_size - 1);
+
+ std::vector<Region> result;
+ addr_t current_address = aligned_start;
+ const addr_t user_end_address = user_addr + user_size;
+ while (current_address + aligned_size < user_end_address) {
+ result.push_back({current_address, aligned_size});
+ current_address += aligned_size;
+ }
+
+ if (current_address < user_end_address)
+ result.push_back({current_address, aligned_size});
+
+ return result;
+}
diff --git a/lldb/source/Breakpoint/WatchpointResource.cpp b/lldb/source/Breakpoint/WatchpointResource.cpp
index c1eb50c..8f15fc7 100644
--- a/lldb/source/Breakpoint/WatchpointResource.cpp
+++ b/lldb/source/Breakpoint/WatchpointResource.cpp
@@ -9,6 +9,7 @@
#include <assert.h>
#include "lldb/Breakpoint/WatchpointResource.h"
+#include "lldb/Utility/Stream.h"
#include <algorithm>
@@ -113,7 +114,8 @@ bool WatchpointResource::ShouldStop(StoppointCallbackContext *context) {
}
void WatchpointResource::Dump(Stream *s) const {
- return; // LWP_TODO
+ s->Printf("addr = 0x%8.8" PRIx64 " size = %zu", m_addr, m_size);
+ return;
}
wp_resource_id_t WatchpointResource::GetNextID() {
diff --git a/lldb/source/Commands/CommandObjectWatchpoint.cpp b/lldb/source/Commands/CommandObjectWatchpoint.cpp
index c80868d..438a16c 100644
--- a/lldb/source/Commands/CommandObjectWatchpoint.cpp
+++ b/lldb/source/Commands/CommandObjectWatchpoint.cpp
@@ -1139,9 +1139,22 @@ protected:
// Fetch the type from the value object, the type of the watched object is
// the pointee type
- /// of the expression, so convert to that if we found a valid type.
+ /// of the expression, so convert to that if we found a valid type.
CompilerType compiler_type(valobj_sp->GetCompilerType());
+ std::optional<uint64_t> valobj_size = valobj_sp->GetByteSize();
+ // Set the type as a uint8_t array if the size being watched is
+ // larger than the ValueObject's size (which is probably the size
+ // of a pointer).
+ if (valobj_size && size > *valobj_size) {
+ auto type_system = compiler_type.GetTypeSystem();
+ if (type_system) {
+ CompilerType clang_uint8_type =
+ type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);
+ compiler_type = clang_uint8_type.GetArrayType(size);
+ }
+ }
+
Status error;
WatchpointSP watch_sp =
target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error);
diff --git a/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp b/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
index 565941d..d756354 100644
--- a/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
+++ b/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp
@@ -491,14 +491,13 @@ static StopInfoSP GetStopInfoForHardwareBP(Thread &thread, Target *target,
uint64_t exc_sub_sub_code) {
// Try hardware watchpoint.
if (target) {
- // LWP_TODO: We need to find the WatchpointResource that matches
- // the address, and evaluate its Watchpoints.
-
// The exc_sub_code indicates the data break address.
- lldb::WatchpointSP wp_sp =
- target->GetWatchpointList().FindByAddress((lldb::addr_t)exc_sub_code);
- if (wp_sp && wp_sp->IsEnabled()) {
- return StopInfo::CreateStopReasonWithWatchpointID(thread, wp_sp->GetID());
+ WatchpointResourceSP wp_rsrc_sp =
+ target->GetProcessSP()->GetWatchpointResourceList().FindByAddress(
+ (addr_t)exc_sub_code);
+ if (wp_rsrc_sp && wp_rsrc_sp->GetNumberOfConstituents() > 0) {
+ return StopInfo::CreateStopReasonWithWatchpointID(
+ thread, wp_rsrc_sp->GetConstituentAtIndex(0)->GetID());
}
}
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index fd724350b..4e3447e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -24,6 +24,7 @@
#include <sys/types.h>
#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Breakpoint/WatchpointAlgorithms.h"
#include "lldb/Breakpoint/WatchpointResource.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
@@ -3153,23 +3154,22 @@ Status ProcessGDBRemote::EnableWatchpoint(WatchpointSP wp_sp, bool notify) {
bool write = wp_sp->WatchpointWrite() || wp_sp->WatchpointModify();
size_t size = wp_sp->GetByteSize();
- // New WatchpointResources needed to implement this Watchpoint.
- std::vector<WatchpointResourceSP> resources;
-
- // LWP_TODO: Break up the user's request into pieces that can be watched
- // given the capabilities of the target cpu / stub software.
- // As a default, breaking the watched region up into target-pointer-sized,
- // aligned, groups.
- //
- // Beyond the default, a stub can / should inform us of its capabilities,
- // e.g. a stub that can do AArch64 power-of-2 MASK watchpoints.
- //
- // And the cpu may have unique capabilities. AArch64 BAS watchpoints
- // can watch any sequential bytes in a doubleword, but Intel watchpoints
- // can only watch 1, 2, 4, 8 bytes within a doubleword.
- WatchpointResourceSP wp_res_sp =
- std::make_shared<WatchpointResource>(addr, size, read, write);
- resources.push_back(wp_res_sp);
+ ArchSpec target_arch = GetTarget().GetArchitecture();
+ WatchpointHardwareFeature supported_features =
+ eWatchpointHardwareFeatureUnknown;
+
+ // LWP_TODO: enable MASK watchpoint for arm64 debugserver
+ // when it reports that it supports them.
+ if (target_arch.GetTriple().getOS() == llvm::Triple::MacOSX &&
+ target_arch.GetTriple().getArch() == llvm::Triple::aarch64) {
+#if 0
+ supported_features |= eWatchpointHardwareArmMASK;
+#endif
+ }
+
+ std::vector<WatchpointResourceSP> resources =
+ WatchpointAlgorithms::AtomizeWatchpointRequest(
+ addr, size, read, write, supported_features, target_arch);
// LWP_TODO: Now that we know the WP Resources needed to implement this
// Watchpoint, we need to look at currently allocated Resources in the
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 3b65d66..95f7805 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -987,8 +987,10 @@ protected:
// Don't stop if the watched region value is unmodified, and
// this is a Modify-type watchpoint.
- if (m_should_stop && !wp_sp->WatchedValueReportable(exe_ctx))
+ if (m_should_stop && !wp_sp->WatchedValueReportable(exe_ctx)) {
+ wp_sp->UndoHitCount();
m_should_stop = false;
+ }
// Finally, if we are going to stop, print out the new & old values:
if (m_should_stop) {