1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ADDRESS_RANGE_H_ 18 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ADDRESS_RANGE_H_ 19 20 #include <algorithm> 21 22 #include <cstdint> 23 #include <map> 24 #include <set> 25 #include <tuple> 26 #include <utility> 27 28 #include "perfetto/base/logging.h" 29 30 namespace perfetto { 31 namespace trace_processor { 32 33 // A range in the form [start, end), i.e. start is inclusive and end is 34 // exclusive. 35 // Note: This means that you can not have a range containing int64_max 36 class AddressRange { 37 public: 38 struct CompareByEnd { 39 // Allow heterogeneous lookups (https://abseil.io/tips/144) 40 using is_transparent = void; 41 // Keeps ranges sorted by end address operatorCompareByEnd42 bool operator()(const AddressRange& lhs, const AddressRange& rhs) const { 43 return lhs.end() < rhs.end(); 44 } 45 46 // Overload to implement PC lookup via upper_bound. operatorCompareByEnd47 bool operator()(const AddressRange& lhs, uint64_t pc) const { 48 return lhs.end() < pc; 49 } 50 51 // Overload to implement PC lookup via upper_bound. operatorCompareByEnd52 bool operator()(uint64_t pc, const AddressRange& rhs) const { 53 return pc < rhs.end(); 54 } 55 }; 56 FromStartAndSize(uint64_t start,uint64_t size)57 static constexpr AddressRange FromStartAndSize(uint64_t start, 58 uint64_t size) { 59 return AddressRange(start, start + size); 60 } AddressRange()61 constexpr AddressRange() : AddressRange(0, 0) {} 62 AddressRange(uint64_t start,uint64_t end)63 constexpr AddressRange(uint64_t start, uint64_t end) 64 : start_(start), end_(end) { 65 PERFETTO_CHECK(start <= end); 66 } 67 68 // Checks whether the given `addr` lies withing this range. Contains(uint64_t addr)69 constexpr bool Contains(uint64_t addr) const { 70 return start_ <= addr && addr < end_; 71 } 72 73 // Checks whether the given `other` range is fully contained in this range. Contains(const AddressRange & other)74 constexpr bool Contains(const AddressRange& other) const { 75 return start_ <= other.start_ && other.end_ <= end_; 76 } 77 78 // Computes the intersection of the two ranges, that is, it returns a range 79 // with all the points in common between the two. IntersectWith(const AddressRange & other)80 constexpr AddressRange IntersectWith(const AddressRange& other) const { 81 auto start = std::max(start_, other.start_); 82 auto end = std::min(end_, other.end_); 83 return start < end ? AddressRange(start, end) : AddressRange(); 84 } 85 86 // Checks whether there is any overlap between the two ranges, that it, if 87 // there exists a point such that Contains(point) would return true for both 88 // ranges. Overlaps(const AddressRange & other)89 constexpr bool Overlaps(const AddressRange& other) const { 90 return !empty() && !other.empty() && start_ < other.end_ && 91 other.start_ < end_; 92 } 93 94 // Two ranges are the same is their respective limits are the same, that is A 95 // contains A and B contains A 96 constexpr bool operator==(const AddressRange& other) const { 97 return start_ == other.start_ && end_ == other.end_; 98 } 99 constexpr bool operator!=(const AddressRange& other) const { 100 return !(*this == other); 101 } 102 103 // Start of range, inclusive start()104 constexpr uint64_t start() const { return start_; } 105 // Start of range, exclusive end()106 constexpr uint64_t end() const { return end_; } 107 length()108 constexpr uint64_t length() const { return end_ - start_; } size()109 constexpr uint64_t size() const { return end_ - start_; } 110 111 // Check whether the length is zero, that is no point will is contained by 112 // this range. empty()113 constexpr bool empty() const { return length() == 0; } 114 115 private: 116 uint64_t start_; 117 uint64_t end_; 118 }; 119 120 // Contains unique collection of addresses. These addresses are kept as 121 // sorted collection of non contiguous and non overlapping AddressRange 122 // instances. As addresses are added or removed these AddressRange might be 123 // merged or spliced as needed to keep the ranges non contiguous and non 124 // overlapping. 125 class AddressSet { 126 public: 127 // TODO(carlscab): Consider using base::FlatSet. As of now this class is used 128 // so little that it does not really matter. 129 using Impl = std::set<AddressRange, AddressRange::CompareByEnd>; 130 131 using value_type = typename Impl::value_type; 132 using const_iterator = typename Impl::const_iterator; 133 using size_type = typename Impl::size_type; 134 begin()135 const_iterator begin() const { return ranges_.begin(); } end()136 const_iterator end() const { return ranges_.end(); } 137 138 // Adds all the addresses in the given range to the set. Add(AddressRange range)139 void Add(AddressRange range) { 140 if (range.empty()) { 141 return; 142 } 143 uint64_t start = range.start(); 144 uint64_t end = range.end(); 145 // Note lower_bound here as we might need to merge with the range just 146 // before. 147 auto it = ranges_.lower_bound(start); 148 149 PERFETTO_DCHECK(it == ranges_.end() || range.start() <= it->end()); 150 151 while (it != ranges_.end() && range.end() >= it->start()) { 152 start = std::min(start, it->start()); 153 end = std::max(end, it->end()); 154 it = ranges_.erase(it); 155 } 156 ranges_.emplace_hint(it, AddressRange(start, end)); 157 } 158 159 // Removes all the addresses in the given range from the set. Remove(AddressRange range)160 void Remove(AddressRange range) { 161 if (range.empty()) { 162 return; 163 } 164 auto it = ranges_.upper_bound(range.start()); 165 PERFETTO_DCHECK(it == ranges_.end() || range.start() < it->end()); 166 167 while (it != ranges_.end() && range.end() > it->start()) { 168 if (range.start() > it->start()) { 169 // range.start() is contained in *it. Split *it at range.start() into 170 // two ranges. Continue the loop at the second of them. 171 PERFETTO_DCHECK(it->Contains(range.start())); 172 auto old = *it; 173 it = ranges_.erase(it); 174 ranges_.emplace_hint(it, old.start(), range.start()); 175 it = ranges_.emplace_hint(it, range.start(), old.end()); 176 } else if (range.end() < it->end()) { 177 // range.end() is contained in *it. Split *it at range.end() into two 178 // ranges. The first of them needs to be deleted. 179 PERFETTO_DCHECK(it->Contains(range.end())); 180 auto old_end = it->end(); 181 it = ranges_.erase(it); 182 ranges_.emplace_hint(it, range.end(), old_end); 183 } else { 184 // range fully contains *it, so it can be removed 185 PERFETTO_DCHECK(range.Contains(*it)); 186 it = ranges_.erase(it); 187 } 188 } 189 } 190 191 bool operator==(const AddressSet& o) const { return ranges_ == o.ranges_; } 192 bool operator!=(const AddressSet& o) const { return ranges_ != o.ranges_; } 193 194 private: 195 // Invariants: 196 // * There are no overlapping ranges. 197 // * There are no empty ranges. 198 // * There are no two ranges a, b where a.end == b.start, that is there are 199 // no contiguous mappings. 200 // * Ranges are sorted by end 201 // Thus lookups are O(log N) and point lookups are trivial using upper_bound() 202 Impl ranges_; 203 }; 204 205 // Maps AddressRange instances to a given value. These AddressRange instances 206 // (basically the keys of the map) will never overlap, as insertions of 207 // overlapping ranges will always fail. 208 template <typename Value> 209 class AddressRangeMap { 210 public: 211 using Impl = std::map<AddressRange, Value, AddressRange::CompareByEnd>; 212 213 using value_type = typename Impl::value_type; 214 using iterator = typename Impl::iterator; 215 using const_iterator = typename Impl::const_iterator; 216 using size_type = typename Impl::size_type; 217 218 // Fails if the new range overlaps with any existing one or when inserting an 219 // empty range (as there is effectively no key to map from). 220 template <typename... Args> Emplace(AddressRange range,Args &&...args)221 bool Emplace(AddressRange range, Args&&... args) { 222 if (range.empty()) { 223 return false; 224 } 225 auto it = ranges_.upper_bound(range.start()); 226 if (it != ranges_.end() && range.end() > it->first.start()) { 227 return false; 228 } 229 ranges_.emplace_hint(it, std::piecewise_construct, 230 std::forward_as_tuple(range), 231 std::forward_as_tuple(std::forward<Args>(args)...)); 232 return true; 233 } 234 235 // Finds the map entry that fully contains the given `range` or `end()` if not 236 // such entry can be found. 237 // ATTENTION: `range` can not be empty. Strictly speaking any range contains 238 // the empty range but that would mean we need to return all the ranges here. 239 // So we chose to just ban that case. FindRangeThatContains(AddressRange range)240 iterator FindRangeThatContains(AddressRange range) { 241 PERFETTO_CHECK(!range.empty()); 242 auto it = Find(range.start()); 243 if (it != end() && it->first.end() >= range.end()) { 244 return it; 245 } 246 return end(); 247 } 248 249 // Finds the range that contains a given address. Find(uint64_t address)250 iterator Find(uint64_t address) { 251 auto it = ranges_.upper_bound(address); 252 if (it != ranges_.end() && address >= it->first.start()) { 253 return it; 254 } 255 return end(); 256 } 257 258 // Finds the range that contains a given address. Find(uint64_t address)259 const_iterator Find(uint64_t address) const { 260 auto it = ranges_.upper_bound(address); 261 if (it != ranges_.end() && address >= it->first.start()) { 262 return it; 263 } 264 return end(); 265 } 266 267 // std::map like methods 268 empty()269 bool empty() const { return ranges_.empty(); } size()270 bool size() const { return ranges_.size(); } begin()271 iterator begin() { return ranges_.begin(); } begin()272 const_iterator begin() const { return ranges_.begin(); } end()273 iterator end() { return ranges_.end(); } end()274 const_iterator end() const { return ranges_.end(); } erase(const_iterator pos)275 iterator erase(const_iterator pos) { return ranges_.erase(pos); } 276 277 // Emplaces a new value into the map by first deleting all overlapping 278 // intervals. It takes an optional (set to nullptr to ignore) callback `cb` 279 // that will be called for each deleted map entry. 280 // Returns true on success, fails if the new range is empty (as there is 281 // effectively no key to map from). 282 template <typename Callback, typename... Args> DeleteOverlapsAndEmplace(Callback cb,AddressRange range,Args &&...args)283 bool DeleteOverlapsAndEmplace(Callback cb, 284 AddressRange range, 285 Args&&... args) { 286 if (range.empty()) { 287 return false; 288 } 289 auto it = ranges_.upper_bound(range.start()); 290 PERFETTO_DCHECK(it == ranges_.end() || range.start() < it->first.end()); 291 292 while (it != ranges_.end() && range.end() > it->first.start()) { 293 cb(*it); 294 it = ranges_.erase(it); 295 } 296 297 ranges_.emplace_hint(it, std::piecewise_construct, 298 std::forward_as_tuple(range), 299 std::forward_as_tuple(std::forward<Args>(args)...)); 300 return true; 301 } 302 303 // Same as above but without a callback. 304 template <typename... Args> DeleteOverlapsAndEmplace(AddressRange range,Args &&...args)305 bool DeleteOverlapsAndEmplace(AddressRange range, Args&&... args) { 306 struct NoOp { 307 void operator()(std::pair<const AddressRange, Value>&) {} 308 }; 309 return DeleteOverlapsAndEmplace(NoOp(), range, std::forward<Args>(args)...); 310 } 311 312 // Calls `cb` for each entry in the map that overlaps the given `range`. That 313 // is, there is a point so for which `AddressRange::Contains` returns true for 314 // both the entry and the given `range' 315 template <typename Callback> ForOverlaps(AddressRange range,Callback cb)316 void ForOverlaps(AddressRange range, Callback cb) { 317 if (range.empty()) { 318 return; 319 } 320 for (auto it = ranges_.upper_bound(range.start()); 321 it != ranges_.end() && range.end() > it->first.start(); ++it) { 322 cb(*it); 323 } 324 } 325 326 private: 327 // Invariants: 328 // * There are no overlapping ranges. 329 // * There are no empty ranges. 330 // * Ranges are sorted by end 331 // Thus lookups are O(log N) and point lookups are trivial using upper_bound() 332 Impl ranges_; 333 }; 334 335 } // namespace trace_processor 336 } // namespace perfetto 337 338 #endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ADDRESS_RANGE_H_ 339