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(addr, 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(addr, size, 41 /*min_byte_size*/ 1, 42 /*max_byte_size*/ arch.GetAddressByteSize(), 43 /*address_byte_size*/ arch.GetAddressByteSize()); 44 } 45 46 Log *log = GetLog(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>(ent.addr, ent.size, read, write); 55 resources.push_back(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(input) == 1) 66 return input; 67 68 return 1ULL << (64 - llvm::countl_zero(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(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(user_size, 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(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 {{aligned_start, 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 {{aligned_start, 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(aligned_size, 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({current_address, aligned_size}); 151 current_address += aligned_size; 152 } 153 154 if (current_address < user_end_address) 155 result.push_back({current_address, aligned_size}); 156 157 return result; 158 } 159