1ac9a064cSDimitry Andric //===-- WatchpointAlgorithms.cpp ------------------------------------------===//
2ac9a064cSDimitry Andric //
3ac9a064cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ac9a064cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5ac9a064cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ac9a064cSDimitry Andric //
7ac9a064cSDimitry Andric //===----------------------------------------------------------------------===//
8ac9a064cSDimitry Andric
9ac9a064cSDimitry Andric #include "lldb/Breakpoint/WatchpointAlgorithms.h"
10ac9a064cSDimitry Andric #include "lldb/Breakpoint/WatchpointResource.h"
11ac9a064cSDimitry Andric #include "lldb/Target/Process.h"
12ac9a064cSDimitry Andric #include "lldb/Utility/ArchSpec.h"
13ac9a064cSDimitry Andric #include "lldb/Utility/LLDBLog.h"
14ac9a064cSDimitry Andric #include "lldb/Utility/Log.h"
15ac9a064cSDimitry Andric
16ac9a064cSDimitry Andric #include <algorithm>
17ac9a064cSDimitry Andric #include <utility>
18ac9a064cSDimitry Andric #include <vector>
19ac9a064cSDimitry Andric
20ac9a064cSDimitry Andric using namespace lldb;
21ac9a064cSDimitry Andric using namespace lldb_private;
22ac9a064cSDimitry Andric
23ac9a064cSDimitry Andric std::vector<WatchpointResourceSP>
AtomizeWatchpointRequest(addr_t addr,size_t size,bool read,bool write,WatchpointHardwareFeature supported_features,ArchSpec & arch)24ac9a064cSDimitry Andric WatchpointAlgorithms::AtomizeWatchpointRequest(
25ac9a064cSDimitry Andric addr_t addr, size_t size, bool read, bool write,
26ac9a064cSDimitry Andric WatchpointHardwareFeature supported_features, ArchSpec &arch) {
27ac9a064cSDimitry Andric
28ac9a064cSDimitry Andric std::vector<Region> entries;
29ac9a064cSDimitry Andric
30ac9a064cSDimitry Andric if (supported_features & eWatchpointHardwareArmMASK) {
31ac9a064cSDimitry Andric entries =
32ac9a064cSDimitry Andric PowerOf2Watchpoints(addr, size,
33ac9a064cSDimitry Andric /*min_byte_size*/ 1,
34ac9a064cSDimitry Andric /*max_byte_size*/ INT32_MAX,
35ac9a064cSDimitry Andric /*address_byte_size*/ arch.GetAddressByteSize());
36ac9a064cSDimitry Andric } else {
37ac9a064cSDimitry Andric // As a fallback, assume we can watch any power-of-2
38ac9a064cSDimitry Andric // number of bytes up through the size of an address in the target.
39ac9a064cSDimitry Andric entries =
40ac9a064cSDimitry Andric PowerOf2Watchpoints(addr, size,
41ac9a064cSDimitry Andric /*min_byte_size*/ 1,
42ac9a064cSDimitry Andric /*max_byte_size*/ arch.GetAddressByteSize(),
43ac9a064cSDimitry Andric /*address_byte_size*/ arch.GetAddressByteSize());
44ac9a064cSDimitry Andric }
45ac9a064cSDimitry Andric
46ac9a064cSDimitry Andric Log *log = GetLog(LLDBLog::Watchpoints);
47ac9a064cSDimitry Andric LLDB_LOGV(log, "AtomizeWatchpointRequest user request addr {0:x} size {1}",
48ac9a064cSDimitry Andric addr, size);
49ac9a064cSDimitry Andric std::vector<WatchpointResourceSP> resources;
50ac9a064cSDimitry Andric for (Region &ent : entries) {
51ac9a064cSDimitry Andric LLDB_LOGV(log, "AtomizeWatchpointRequest creating resource {0:x} size {1}",
52ac9a064cSDimitry Andric ent.addr, ent.size);
53ac9a064cSDimitry Andric WatchpointResourceSP wp_res_sp =
54ac9a064cSDimitry Andric std::make_shared<WatchpointResource>(ent.addr, ent.size, read, write);
55ac9a064cSDimitry Andric resources.push_back(wp_res_sp);
56ac9a064cSDimitry Andric }
57ac9a064cSDimitry Andric
58ac9a064cSDimitry Andric return resources;
59ac9a064cSDimitry Andric }
60ac9a064cSDimitry Andric
61ac9a064cSDimitry Andric // This should be `std::bit_ceil(aligned_size)` but
62ac9a064cSDimitry Andric // that requires C++20.
63ac9a064cSDimitry Andric // Calculates the smallest integral power of two that is not smaller than x.
bit_ceil(uint64_t input)64ac9a064cSDimitry Andric static uint64_t bit_ceil(uint64_t input) {
65ac9a064cSDimitry Andric if (input <= 1 || llvm::popcount(input) == 1)
66ac9a064cSDimitry Andric return input;
67ac9a064cSDimitry Andric
68ac9a064cSDimitry Andric return 1ULL << (64 - llvm::countl_zero(input));
69ac9a064cSDimitry Andric }
70ac9a064cSDimitry Andric
71ac9a064cSDimitry Andric /// Convert a user's watchpoint request (\a user_addr and \a user_size)
72ac9a064cSDimitry Andric /// into hardware watchpoints, for a target that can watch a power-of-2
73ac9a064cSDimitry Andric /// region of memory (1, 2, 4, 8, etc), aligned to that same power-of-2
74ac9a064cSDimitry Andric /// memory address.
75ac9a064cSDimitry Andric ///
76ac9a064cSDimitry Andric /// If a user asks to watch 4 bytes at address 0x1002 (0x1002-0x1005
77ac9a064cSDimitry Andric /// inclusive) we can implement this with two 2-byte watchpoints
78ac9a064cSDimitry Andric /// (0x1002 and 0x1004) or with an 8-byte watchpoint at 0x1000.
79ac9a064cSDimitry Andric /// A 4-byte watchpoint at 0x1002 would not be properly 4 byte aligned.
80ac9a064cSDimitry Andric ///
81ac9a064cSDimitry Andric /// If a user asks to watch 16 bytes at 0x1000, and this target supports
82ac9a064cSDimitry Andric /// 8-byte watchpoints, we can implement this with two 8-byte watchpoints
83ac9a064cSDimitry Andric /// at 0x1000 and 0x1008.
84ac9a064cSDimitry Andric std::vector<WatchpointAlgorithms::Region>
PowerOf2Watchpoints(addr_t user_addr,size_t user_size,size_t min_byte_size,size_t max_byte_size,uint32_t address_byte_size)85ac9a064cSDimitry Andric WatchpointAlgorithms::PowerOf2Watchpoints(addr_t user_addr, size_t user_size,
86ac9a064cSDimitry Andric size_t min_byte_size,
87ac9a064cSDimitry Andric size_t max_byte_size,
88ac9a064cSDimitry Andric uint32_t address_byte_size) {
89ac9a064cSDimitry Andric
90ac9a064cSDimitry Andric Log *log = GetLog(LLDBLog::Watchpoints);
91ac9a064cSDimitry Andric LLDB_LOGV(log,
92ac9a064cSDimitry Andric "AtomizeWatchpointRequest user request addr {0:x} size {1} "
93ac9a064cSDimitry Andric "min_byte_size {2}, max_byte_size {3}, address_byte_size {4}",
94ac9a064cSDimitry Andric user_addr, user_size, min_byte_size, max_byte_size,
95ac9a064cSDimitry Andric address_byte_size);
96ac9a064cSDimitry Andric
97ac9a064cSDimitry Andric // Can't watch zero bytes.
98ac9a064cSDimitry Andric if (user_size == 0)
99ac9a064cSDimitry Andric return {};
100ac9a064cSDimitry Andric
101ac9a064cSDimitry Andric size_t aligned_size = std::max(user_size, min_byte_size);
102ac9a064cSDimitry Andric /// Round up \a user_size to the next power-of-2 size
103ac9a064cSDimitry Andric /// user_size == 8 -> aligned_size == 8
104ac9a064cSDimitry Andric /// user_size == 9 -> aligned_size == 16
105ac9a064cSDimitry Andric aligned_size = bit_ceil(aligned_size);
106ac9a064cSDimitry Andric
107ac9a064cSDimitry Andric addr_t aligned_start = user_addr & ~(aligned_size - 1);
108ac9a064cSDimitry Andric
109ac9a064cSDimitry Andric // Does this power-of-2 memory range, aligned to power-of-2 that the
110ac9a064cSDimitry Andric // hardware can watch, completely cover the requested region.
111ac9a064cSDimitry Andric if (aligned_size <= max_byte_size &&
112ac9a064cSDimitry Andric aligned_start + aligned_size >= user_addr + user_size)
113ac9a064cSDimitry Andric return {{aligned_start, aligned_size}};
114ac9a064cSDimitry Andric
115ac9a064cSDimitry Andric // If the maximum region we can watch is larger than the aligned
116ac9a064cSDimitry Andric // size, try increasing the region size by one power of 2 and see
117ac9a064cSDimitry Andric // if aligning to that amount can cover the requested region.
118ac9a064cSDimitry Andric //
119ac9a064cSDimitry Andric // Increasing the aligned_size repeatedly instead of splitting the
120ac9a064cSDimitry Andric // watchpoint can result in us watching large regions of memory
121ac9a064cSDimitry Andric // unintentionally when we could use small two watchpoints. e.g.
122ac9a064cSDimitry Andric // user_addr 0x3ff8 user_size 32
123ac9a064cSDimitry Andric // can be watched with four 8-byte watchpoints or if it's done with one
124ac9a064cSDimitry Andric // MASK watchpoint, it would need to be a 32KB watchpoint (a 16KB
125ac9a064cSDimitry Andric // watchpoint at 0x0 only covers 0x0000-0x4000). A user request
126ac9a064cSDimitry Andric // at the end of a power-of-2 region can lead to these undesirably
127ac9a064cSDimitry Andric // large watchpoints and many false positive hits to ignore.
128ac9a064cSDimitry Andric if (max_byte_size >= (aligned_size << 1)) {
129ac9a064cSDimitry Andric aligned_size <<= 1;
130ac9a064cSDimitry Andric aligned_start = user_addr & ~(aligned_size - 1);
131ac9a064cSDimitry Andric if (aligned_size <= max_byte_size &&
132ac9a064cSDimitry Andric aligned_start + aligned_size >= user_addr + user_size)
133ac9a064cSDimitry Andric return {{aligned_start, aligned_size}};
134ac9a064cSDimitry Andric
135ac9a064cSDimitry Andric // Go back to our original aligned size, to try the multiple
136ac9a064cSDimitry Andric // watchpoint approach.
137ac9a064cSDimitry Andric aligned_size >>= 1;
138ac9a064cSDimitry Andric }
139ac9a064cSDimitry Andric
140ac9a064cSDimitry Andric // We need to split the user's watchpoint into two or more watchpoints
141ac9a064cSDimitry Andric // that can be monitored by hardware, because of alignment and/or size
142ac9a064cSDimitry Andric // reasons.
143ac9a064cSDimitry Andric aligned_size = std::min(aligned_size, max_byte_size);
144ac9a064cSDimitry Andric aligned_start = user_addr & ~(aligned_size - 1);
145ac9a064cSDimitry Andric
146ac9a064cSDimitry Andric std::vector<Region> result;
147ac9a064cSDimitry Andric addr_t current_address = aligned_start;
148ac9a064cSDimitry Andric const addr_t user_end_address = user_addr + user_size;
149ac9a064cSDimitry Andric while (current_address + aligned_size < user_end_address) {
150ac9a064cSDimitry Andric result.push_back({current_address, aligned_size});
151ac9a064cSDimitry Andric current_address += aligned_size;
152ac9a064cSDimitry Andric }
153ac9a064cSDimitry Andric
154ac9a064cSDimitry Andric if (current_address < user_end_address)
155ac9a064cSDimitry Andric result.push_back({current_address, aligned_size});
156ac9a064cSDimitry Andric
157ac9a064cSDimitry Andric return result;
158ac9a064cSDimitry Andric }
159