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