-
Notifications
You must be signed in to change notification settings - Fork 350
/
Copy pathrange_map.h
392 lines (337 loc) · 13.6 KB
/
range_map.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// RagneMap maps
//
// [uint64_t, uint64_t) -> std::string, [optional other range base]
//
// where ranges must be non-overlapping.
//
// This is used to map the address space (either pointer offsets or file
// offsets).
//
// The other range base allows us to use this RangeMap to translate addresses
// from this domain to another one (like vm_addr -> file_addr or vice versa).
//
// This type is only exposed in the .h file for unit testing purposes.
#ifndef BLOATY_RANGE_MAP_H_
#define BLOATY_RANGE_MAP_H_
#include <assert.h>
#include <stdint.h>
#include <exception>
#include <map>
#include <stdexcept>
#include <vector>
#include "absl/strings/str_cat.h"
namespace bloaty {
class RangeMapTest;
class RangeMap {
public:
RangeMap() = default;
RangeMap(RangeMap&& other) = default;
RangeMap& operator=(RangeMap&& other) = default;
RangeMap(RangeMap& other) = delete;
RangeMap& operator=(RangeMap& other) = delete;
// Adds a range to this map.
void AddRange(uint64_t addr, uint64_t size, const std::string& val);
// Adds a range to this map (in domain D1) that also corresponds to a
// different range in a different map (in domain D2). The correspondance will
// be noted to allow us to translate into the other domain later.
void AddDualRange(uint64_t addr, uint64_t size, uint64_t otheraddr,
const std::string& val);
// Adds a range to this map (in domain D1), and also adds corresponding ranges
// to |other| (in domain D2), using |translator| (in domain D1) to translate
// D1->D2. The translation is performed using information from previous
// AddDualRange() calls on |translator|.
//
// Returns true if the entire range [addr, size] was present in the
// |translator| map. (This does not necessarily mean that every part of the
// range was actually translated). If the return value is false, then the
// contents of |this| and |other| are undefined (Bloaty will bail in this
// case).
bool AddRangeWithTranslation(uint64_t addr, uint64_t size,
const std::string& val,
const RangeMap& translator, bool verbose,
RangeMap* other);
// Collapses adjacent ranges with the same label. This reduces memory usage
// and removes redundant noise from the output when dumping a full memory map
// (in normal Bloaty output it makes no difference, because all labels with
// the same name are added together).
//
// TODO(haberman): see if we can do this at insertion time instead, so it
// doesn't require a second pass.
void Compress();
// Returns whether this RangeMap fully covers the given range.
bool CoversRange(uint64_t addr, uint64_t size) const;
// Returns the maximum address contained in this map.
uint64_t GetMaxAddress() const;
// Translates |addr| into the other domain, returning |true| if this was
// successful.
bool Translate(uint64_t addr, uint64_t *translated) const;
// Looks for a range within this map that contains |addr|. If found, returns
// true and sets |label| to the corresponding label, and |offset| to the
// offset from the beginning of this range.
bool TryGetLabel(uint64_t addr, std::string* label) const;
bool TryGetLabelForRange(uint64_t addr, uint64_t size,
std::string* label) const;
// Looks for a range that starts exactly on |addr|. If it exists, returns
// true and sets |size| to its size.
bool TryGetSize(uint64_t addr, uint64_t* size) const;
std::string DebugString() const;
static std::string EntryDebugString(uint64_t addr, uint64_t size,
uint64_t other_start,
const std::string& label) {
std::string end =
size == kUnknownSize ? "?" : absl::StrCat(absl::Hex(addr + size));
std::string ret = absl::StrCat("[", absl::Hex(addr), ", ", end,
"] (size=", absl::Hex(size), "): ", label);
if (other_start != UINT64_MAX) {
absl::StrAppend(&ret, ", other_start=", absl::Hex(other_start));
}
return ret;
}
template <class T>
std::string EntryDebugString(T it) const {
if (it == mappings_.end()) {
return "[end]";
} else {
return EntryDebugString(it->first, it->second.size,
it->second.other_start, it->second.label);
}
}
template <class Func>
static void ComputeRollup(const std::vector<const RangeMap*>& range_maps,
Func func);
template <class Func>
void ForEachRange(Func func) const {
for (auto iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
func(iter->first, RangeEnd(iter) - iter->first);
}
}
template <class Func>
void ForEachRangeWithStart(uint64_t start, Func func) const {
for (auto iter = FindContaining(start); iter != mappings_.end(); ++iter) {
if (!func(iter->second.label, iter->first,
RangeEnd(iter) - iter->first)) {
return;
}
}
}
static constexpr uint64_t kUnknownSize = UINT64_MAX;
private:
friend class RangeMapTest;
static const uint64_t kNoTranslation = UINT64_MAX;
struct Entry {
Entry(const std::string& label_, uint64_t size_, uint64_t other_)
: label(label_), size(size_), other_start(other_) {}
std::string label;
uint64_t size;
uint64_t other_start; // kNoTranslation if there is no mapping.
bool HasTranslation() const { return other_start != kNoTranslation; }
bool HasFallbackLabel() const { return !label.empty() && label[0] == '['; }
// We assume that short regions that were unattributed (have fallback
// labels) are actually padding. We could probably make this heuristic
// a bit more robust.
bool IsShortFallback() const { return size <= 16 && HasFallbackLabel(); }
};
typedef std::map<uint64_t, Entry> Map;
Map mappings_;
template <class T>
void CheckConsistency(T iter) const {
assert(iter->first + iter->second.size > iter->first);
assert(iter == mappings_.begin() ||
RangeEnd(std::prev(iter)) <= iter->first);
assert(std::next(iter) == mappings_.end() ||
RangeEnd(iter) <= std::next(iter)->first);
}
template <class T>
bool EntryContains(T iter, uint64_t addr) const {
return addr >= iter->first && addr < RangeEnd(iter);
}
template <class T>
bool EntryContainsStrict(T iter, uint64_t addr) const {
if (iter->second.size == kUnknownSize) {
return iter->first == addr;
} else {
return addr >= iter->first && addr < RangeEnd(iter);
}
}
template <class T>
void MaybeSetLabel(T iter, const std::string& label, uint64_t addr,
uint64_t end);
// When the size is unknown return |unknown| for the end.
uint64_t RangeEndUnknownLimit(Map::const_iterator iter,
uint64_t unknown) const {
if (iter->second.size == kUnknownSize) {
Map::const_iterator next = std::next(iter);
if (IterIsEnd(next) || next->first > unknown) {
return unknown;
} else {
return next->first;
}
} else {
uint64_t ret = iter->first + iter->second.size;
assert(ret > iter->first);
return ret;
}
}
uint64_t RangeEnd(Map::const_iterator iter) const {
return RangeEndUnknownLimit(iter, UINT64_MAX);
}
bool IterIsEnd(Map::const_iterator iter) const {
return iter == mappings_.end();
}
template <class T>
uint64_t TranslateWithEntry(T iter, uint64_t addr) const;
template <class T>
bool TranslateAndTrimRangeWithEntry(T iter, uint64_t addr, uint64_t size,
uint64_t* trimmed_addr,
uint64_t* translated_addr,
uint64_t* trimmed_size) const;
// Finds the entry that contains |addr|. If no such mapping exists, returns
// mappings_.end().
Map::const_iterator FindContaining(uint64_t addr) const;
// Finds the entry that contains |addr|, or the very next entry (which may be
// mappings_.end()).
Map::iterator FindContainingOrAfter(uint64_t addr);
Map::const_iterator FindContainingOrAfter(uint64_t addr) const;
};
template <class Func>
void RangeMap::ComputeRollup(const std::vector<const RangeMap*>& range_maps,
Func func) {
assert(range_maps.size() > 0);
std::vector<Map::const_iterator> iters;
if (range_maps[0]->mappings_.empty()) {
for (int i = 0; i < range_maps.size(); i++) {
const RangeMap* range_map = range_maps[i];
if (!range_map->mappings_.empty()) {
printf(
"Error, range (%s) exists at index %d, but base map is empty\n",
range_map->EntryDebugString(range_map->mappings_.begin()).c_str(),
i);
assert(false);
throw std::runtime_error("Range extends beyond base map.");
}
}
return;
}
iters.reserve(range_maps.size());
for (auto range_map : range_maps) {
iters.push_back(range_map->mappings_.begin());
}
// Iterate over all ranges in parallel to perform this transformation:
//
// ----- ----- ----- ---------------
// | | 1 A,X,1
// | X ----- ---------------
// | | | A,X,2
// A ----- | ---------------
// | | | |
// | | 2 -----> |
// | Y | A,Y,2
// | | | |
// ----- | | ---------------
// B | | B,Y,2
// ----- ----- ----- ---------------
//
//
// ----- ----- ----- ---------------
// C Z 3 C,Z,3
// ----- ----- ----- ---------------
//
// All input maps must cover exactly the same domain.
// Outer loop: once per continuous (gapless) region.
while (true) {
std::vector<std::string> keys;
uint64_t current = 0;
if (range_maps[0]->IterIsEnd(iters[0])) {
// Termination condition: all iterators must be at end.
for (int i = 0; i < range_maps.size(); i++) {
if (!range_maps[i]->IterIsEnd(iters[i])) {
printf(
"Error, range (%s) extends beyond final base map range "
"(%s)\n",
range_maps[i]->EntryDebugString(iters[i]).c_str(),
range_maps[0]->EntryDebugString(std::prev(iters[0])).c_str());
assert(false);
throw std::runtime_error("Range extends beyond base map.");
}
}
return;
} else {
// Starting a new continuous range: all iterators must start at the same
// place.
current = iters[0]->first;
for (int i = 0; i < range_maps.size(); i++) {
if (range_maps[i]->IterIsEnd(iters[i])) {
printf(
"Error, no more ranges for index %d but we need one "
"to match (%s)\n",
i, range_maps[0]->EntryDebugString(iters[0]).c_str());
assert(false);
throw std::runtime_error("No more ranges.");
} else if (iters[i]->first != current) {
printf(
"Error, range (%s) doesn't match the beginning of base range "
"(%s)\n",
range_maps[i]->EntryDebugString(iters[i]).c_str(),
range_maps[0]->EntryDebugString(iters[0]).c_str());
assert(false);
throw std::runtime_error("No more ranges.");
}
keys.push_back(iters[i]->second.label);
}
}
bool continuous = true;
// Inner loop: once per range within the continuous region.
while (continuous) {
uint64_t next_break = UINT64_MAX;
for (int i = 0; i < iters.size(); i++) {
next_break = std::min(next_break, range_maps[i]->RangeEnd(iters[i]));
}
func(keys, current, next_break);
// Advance all iterators with ranges ending at next_break.
for (int i = 0; i < iters.size(); i++) {
const RangeMap& map = *range_maps[i];
Map::const_iterator& iter = iters[i];
uint64_t end = continuous ? map.RangeEnd(iter)
: map.RangeEndUnknownLimit(iter, next_break);
if (end != next_break) {
continue;
}
++iter;
// Test for discontinuity.
if (map.IterIsEnd(iter) || iter->first != next_break) {
if (i > 0 && continuous) {
printf(
"Error, gap between ranges (%s) and (%s) fails to cover base "
"range (%s)\n",
map.EntryDebugString(std::prev(iter)).c_str(),
map.EntryDebugString(iter).c_str(),
range_maps[0]->EntryDebugString(iters[0]).c_str());
assert(false);
throw std::runtime_error("Entry range extends beyond base range");
}
assert(i == 0 || !continuous);
continuous = false;
} else {
assert(continuous);
keys[i] = iter->second.label;
}
}
current = next_break;
}
}
}
} // namespace bloaty
#endif // BLOATY_RANGE_MAP_H_