• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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