[Lldb-commits] [lldb] 4584a64 - [lldb][AArch64][NFC] Make watchpoint adjustment more clear (#209483)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 15 06:13:14 PDT 2026
Author: David Spickett
Date: 2026-07-15T13:13:09Z
New Revision: 4584a6489341b4fe7762991e65f98467ffe4c699
URL: https://github.com/llvm/llvm-project/commit/4584a6489341b4fe7762991e65f98467ffe4c699
DIFF: https://github.com/llvm/llvm-project/commit/4584a6489341b4fe7762991e65f98467ffe4c699.diff
LOG: [lldb][AArch64][NFC] Make watchpoint adjustment more clear (#209483)
NativeRegisterContextDBReg_arm64::AdjustWatchpoint has a way of
adjusting the range of a watchpoint but it took me a while to figure out
how it worked. I've rewritten it in a way I think is easier to follow,
with more explanatory comments.
It turns out that this adjustment results in us only using a subset of
what the hardware allows.
* You don't have to select a continuous range of bytes (1, 2, 3). You
can select (1, 4) for example.
* The address can be 4 byte aligned, in which case only 4 bits of the
byte select apply (though you can set up an equivalent watch with 8 byte
alignment and a different 8 bit BAS value).
Added:
Modified:
lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
Removed:
################################################################################
diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
index f2f772ad615c9..91c440f4c0453 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp
@@ -12,6 +12,8 @@
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
+#include "llvm/Support/MathExtras.h"
+
using namespace lldb_private;
uint32_t
@@ -36,30 +38,56 @@ NativeRegisterContextDBReg_arm64::GetWatchpointSize(uint32_t wp_index) {
std::optional<NativeRegisterContextDBReg::WatchpointDetails>
NativeRegisterContextDBReg_arm64::AdjustWatchpoint(
const WatchpointDetails &details) {
+ // For the way we are using BAS watchpoints, their size must be a power of 2
+ // that is less than 8. Note that this is a subset of what hardware allows.
+ const size_t max_size = 8;
size_t size = details.size;
- lldb::addr_t addr = details.addr;
- // Check if size has a valid hardware watchpoint length.
- if (size != 1 && size != 2 && size != 4 && size != 8)
+ if (!llvm::isPowerOf2_64(size) || size > max_size)
return std::nullopt;
- // Check 8-byte alignment for hardware watchpoint target address. Below is a
- // hack to recalculate address and size in order to make sure we can watch
- // non 8-byte aligned addresses as well.
- if (addr & 0x07) {
- uint8_t watch_mask = (addr & 0x07) + size;
-
- if (watch_mask > 0x08)
- return std::nullopt;
+ // The start address must be aligned to 8 bytes.
+ lldb::addr_t addr = details.addr;
+ const size_t misalignment = addr & (max_size - 1);
+ if (misalignment == 0)
+ return details;
+
+ // The start address is not aligned, but we might be able to expand the
+ // watched range backwards to the previous 8 byte aligned address while
+ // keeping the size <= 8 bytes.
+ //
+ // Aligned Address
+ // | /--------- Start Address
+ // [ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
+ // | |<-------- Size ------->|
+ // |<----- Aligned Size -------->|
+ //
+ size_t aligned_size = misalignment + size;
+ if (aligned_size > max_size) {
+ // The range does not fit within a single BAS watchpoint, reject it.
+ return std::nullopt;
+ }
- if (watch_mask <= 0x02)
- size = 2;
- else if (watch_mask <= 0x04)
- size = 4;
- else
- size = 8;
+ // Size must still be a power of 2. After correcting this, sometimes we will
+ // watch bytes before and after the intended range. Stops in these extra bytes
+ // will be filtered out.
+ //
+ // For example, A is an aligned address and S is the start address. You want
+ // to watch the 1 byte at address S.
+ // [A][ ][S][ ]
+ // { }
+ // This is misaligned by 2, so our aligned size is 3.
+ // [A][ ][S][ ]
+ // { }
+ // We cannot watch 3 bytes but we can watch 4.
+ // [A][ ][S][ ]
+ // { }
+ // The watched range is expanded before and after S.
+
+ // Round up to a power of 2 size.
+ size = llvm::PowerOf2Ceil(aligned_size);
+ // Align the address down.
+ addr -= misalignment;
- addr = addr & (~0x07);
- }
return WatchpointDetails{size, addr};
}
@@ -67,14 +95,15 @@ uint32_t NativeRegisterContextDBReg_arm64::MakeBreakControlValue(size_t size) {
// PAC (bits 2:1): 0b10
const uint32_t pac_bits = 2 << 1;
- // BAS (bits 12:5) hold a bit-mask of addresses to watch
+ // BAS (bits 12:5) hold a bit-mask of addresses to watch.
+ // Hardware does allow gaps but we only support unbroken ranges at this time
+ //
// e.g. 0b00000001 means 1 byte at address
// 0b00000011 means 2 bytes (addr..addr+1)
// ...
// 0b11111111 means 8 bytes (addr..addr+7)
size_t encoded_size = ((1 << size) - 1) << 5;
- // Return encoded hardware breakpoint control value.
return m_hw_dbg_enable_bit | pac_bits | encoded_size;
}
@@ -84,13 +113,14 @@ NativeRegisterContextDBReg_arm64::MakeWatchControlValue(size_t size,
// PAC (bits 2:1): 0b10
const uint32_t pac_bits = 2 << 1;
- // BAS (bits 12:5) hold a bit-mask of addresses to watch
+ // BAS (bits 12:5) hold a bit-mask of addresses to watch.
+ // Hardware does allow gaps but we only support unbroken ranges at this time.
+ //
// e.g. 0b00000001 means 1 byte at address
// 0b00000011 means 2 bytes (addr..addr+1)
// ...
// 0b11111111 means 8 bytes (addr..addr+7)
size_t encoded_size = ((1 << size) - 1) << 5;
- // Return encoded hardware watchpoint control value.
return m_hw_dbg_enable_bit | pac_bits | encoded_size | (watch_flags << 3);
}
More information about the lldb-commits
mailing list