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 #include <cstdint> 22 #include <iterator> 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 trimming all overlapping 278 // intervals, deleting them if the overlap is fully contained in the new 279 // range, and splitting into two entries pointing to the same value if a 280 // single entry fully contains the new range. Returns true on success, fails 281 // if the new range is empty (as there is effectively no key to map from). 282 template <typename... Args> TrimOverlapsAndEmplace(AddressRange range,Args &&...args)283 bool TrimOverlapsAndEmplace(AddressRange range, Args&&... args) { 284 if (range.empty()) { 285 return false; 286 } 287 auto it = ranges_.upper_bound(range.start()); 288 PERFETTO_DCHECK(it == ranges_.end() || range.start() < it->first.end()); 289 290 // First check if we need to trim the first overlapping range, if any. 291 if (it != ranges_.end() && it->first.start() < range.start()) { 292 // Range starts after `it->first` starts, but before `it->first` ends, and 293 // so overlaps it: 294 // it->first: |-----------? 295 // range: |------? 296 PERFETTO_DCHECK(it->first.Overlaps(range)); 297 298 // Cache it->first since we'll be mutating it in TrimEntryRange. 299 AddressRange existing_range = it->first; 300 301 // Trim the first overlap to end at the start of the range. 302 // it->first: |----| 303 // range: |------? 304 it = TrimEntryRange(it, 305 AddressRange(existing_range.start(), range.start())); 306 307 if (range.end() < existing_range.end()) { 308 // Range also ends before existing_range, thus strictly containing it. 309 // existing_range: |-----------| (previously it->first) 310 // range: |----| 311 PERFETTO_DCHECK(existing_range.Contains(range)); 312 313 // In this special case, we need to split existing_range into two 314 // ranges, with the same value, and insert the new range between them: 315 // it->first: |----| 316 // range: |----| 317 // tail: |-| 318 // We've already trimmed existing_range (as it->first), so just add 319 // the tail now. 320 321 AddressRange tail(range.end(), existing_range.end()); 322 ranges_.emplace_hint(std::next(it, 1), tail, Value(it->second)); 323 } 324 325 // After trimming, the current iterated range is now before the new 326 // range. This means it no longer ends after the new range starts, and we 327 // need to advance the iterator to the new upper_bound. 328 ++it; 329 PERFETTO_DCHECK(it == ranges_.upper_bound(range.start())); 330 } 331 332 // Now, check for any ranges which are _fully_ contained inside 333 // the existing range. 334 while (it != ranges_.end() && it->first.end() <= range.end()) { 335 // Range fully contains `it->first`: 336 // it->first: |----| 337 // range: |-----------| 338 // 339 // We're testing for whether it ends after it->first, and we know it 340 // starts before it->first (because we've already handled the first 341 // overlap), so this existing range is fully contained inside the new 342 // range 343 PERFETTO_DCHECK(range.Contains(it->first)); 344 it = ranges_.erase(it); 345 } 346 347 // Finally, check if we need to trim the last range. We know that it 348 // ends after the new range, but it might also start after the new 349 // range, or we might have reached the end, so this is really a check for 350 // overlap. 351 if (it != ranges_.end() && it->first.start() < range.end()) { 352 // Range overlaps with it->first, and ends before `it->first`: 353 // it->first: |----------| 354 // range: |-----| 355 PERFETTO_DCHECK(range.Overlaps(it->first)); 356 357 // Trim this overlap to end after the end of the range, and insert it 358 // after where the range will be inserted. 359 // range: |-----| 360 // it->first: |-----| 361 it = TrimEntryRange(it, AddressRange(range.end(), it->first.end())); 362 363 // `it` now points to the newly trimmed range, which is _after_ where 364 // we want to insert the new range. This is what we want as an insertion 365 // hint, so keep it as is. 366 } 367 368 ranges_.emplace_hint(it, std::piecewise_construct, 369 std::forward_as_tuple(range), 370 std::forward_as_tuple(std::forward<Args>(args)...)); 371 372 return true; 373 } 374 375 // Emplaces a new value into the map by first deleting all overlapping 376 // intervals. It takes an optional (set to nullptr to ignore) callback `cb` 377 // that will be called for each deleted map entry. 378 // Returns true on success, fails if the new range is empty (as there is 379 // effectively no key to map from). 380 template <typename Callback, typename... Args> DeleteOverlapsAndEmplace(Callback cb,AddressRange range,Args &&...args)381 bool DeleteOverlapsAndEmplace(Callback cb, 382 AddressRange range, 383 Args&&... args) { 384 if (range.empty()) { 385 return false; 386 } 387 auto it = ranges_.upper_bound(range.start()); 388 PERFETTO_DCHECK(it == ranges_.end() || range.start() < it->first.end()); 389 390 while (it != ranges_.end() && range.end() > it->first.start()) { 391 cb(*it); 392 it = ranges_.erase(it); 393 } 394 395 ranges_.emplace_hint(it, std::piecewise_construct, 396 std::forward_as_tuple(range), 397 std::forward_as_tuple(std::forward<Args>(args)...)); 398 return true; 399 } 400 401 // Calls `cb` for each entry in the map that overlaps the given `range`. That 402 // is, there is a point so for which `AddressRange::Contains` returns true for 403 // both the entry and the given `range' 404 template <typename Callback> ForOverlaps(AddressRange range,Callback cb)405 void ForOverlaps(AddressRange range, Callback cb) { 406 if (range.empty()) { 407 return; 408 } 409 for (auto it = ranges_.upper_bound(range.start()); 410 it != ranges_.end() && range.end() > it->first.start(); ++it) { 411 cb(*it); 412 } 413 } 414 415 private: 416 // Trim an entry's address range to a new value. The new value must be fully 417 // contained inside the existing range's value, to guarantee that the ranges 418 // stay in the same order. Returns a new iterator to the trimmed entry, since 419 // the trimming process invalidates the iterator. TrimEntryRange(typename Impl::iterator it,AddressRange new_range)420 typename Impl::iterator TrimEntryRange(typename Impl::iterator it, 421 AddressRange new_range) { 422 PERFETTO_DCHECK(it->first.Contains(new_range)); 423 PERFETTO_DCHECK(!new_range.empty()); 424 425 // Advance the iterator so that it stays valid -- it now also conveniently 426 // points to the entry after the current entry, which is exactly the hint we 427 // want for re-inserting in the same place. 428 auto extracted = ranges_.extract(it++); 429 extracted.key() = new_range; 430 // Reinsert in the same place, using the advanced iterator as a hint. 431 return ranges_.insert(it, std::move(extracted)); 432 } 433 434 // Invariants: 435 // * There are no overlapping ranges. 436 // * There are no empty ranges. 437 // * Ranges are sorted by end 438 // Thus lookups are O(log N) and point lookups are trivial using upper_bound() 439 Impl ranges_; 440 }; 441 442 } // namespace trace_processor 443 } // namespace perfetto 444 445 #endif // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ADDRESS_RANGE_H_ 446