Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2016 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <assert.h> |
16 | | #include <stdio.h> |
17 | | |
18 | | #include <algorithm> |
19 | | #include <initializer_list> |
20 | | #include <iostream> |
21 | | #include <limits> |
22 | | #include <memory> |
23 | | #include <stack> |
24 | | #include <unordered_map> |
25 | | #include <unordered_set> |
26 | | #include <vector> |
27 | | |
28 | | #include "absl/base/attributes.h" |
29 | | #include "absl/base/macros.h" |
30 | | #include "absl/strings/string_view.h" |
31 | | #include "absl/strings/substitute.h" |
32 | | #include "absl/types/optional.h" |
33 | | #include "bloaty.h" |
34 | | #include "bloaty.pb.h" |
35 | | #include "dwarf_constants.h" |
36 | | #include "util.h" |
37 | | #include "dwarf/attr.h" |
38 | | #include "dwarf/dwarf_util.h" |
39 | | #include "dwarf/line_info.h" |
40 | | |
41 | | using namespace dwarf2reader; |
42 | | using absl::string_view; |
43 | | |
44 | | namespace bloaty { |
45 | | |
46 | | extern int verbose_level; |
47 | | |
48 | | namespace dwarf { |
49 | | |
50 | | // AddressRanges /////////////////////////////////////////////////////////////// |
51 | | |
52 | | // Code for reading address ranges out of .debug_aranges. |
53 | | |
54 | | class AddressRanges { |
55 | | public: |
56 | 36.2k | AddressRanges(string_view data) : section_(data), next_unit_(data) {} |
57 | | |
58 | | // Offset into .debug_info for the current compilation unit. |
59 | 97.1k | uint64_t debug_info_offset() { return debug_info_offset_; } |
60 | | |
61 | | // Address and length for this range. |
62 | 291k | uint64_t address() { return address_; } |
63 | 104k | uint64_t length() { return length_; } |
64 | | // The range of the file where this data occurs. |
65 | 187k | string_view data() { return data_; } |
66 | | |
67 | | // Advance to the next range. The values will be available in address() and |
68 | | // length(). Returns false when the end of this compilation unit is hit. |
69 | | // Must call this once before reading the first range. |
70 | | bool NextRange(); |
71 | | |
72 | | // Advance to the next compilation unit. The unit offset will be available in |
73 | | // debug_info_offset(). Must call this once before reading the first unit. |
74 | | bool NextUnit(); |
75 | | |
76 | 187k | uint8_t address_size() const { return sizes_.address_size(); } |
77 | | |
78 | | private: |
79 | | CompilationUnitSizes sizes_; |
80 | | string_view data_; |
81 | | string_view section_; |
82 | | string_view unit_remaining_; |
83 | | string_view next_unit_; |
84 | | uint64_t debug_info_offset_; |
85 | | uint64_t address_; |
86 | | uint64_t length_; |
87 | | }; |
88 | | |
89 | 274k | bool AddressRanges::NextRange() { |
90 | 274k | if (unit_remaining_.empty()) { |
91 | 86.3k | return false; |
92 | 86.3k | } |
93 | | |
94 | 187k | const char* start = unit_remaining_.data(); |
95 | 187k | address_ = sizes_.ReadAddress(&unit_remaining_); |
96 | 187k | length_ = sizes_.ReadAddress(&unit_remaining_); |
97 | 187k | data_ = string_view(start, unit_remaining_.data() - start); |
98 | 187k | return true; |
99 | 274k | } |
100 | | |
101 | 122k | bool AddressRanges::NextUnit() { |
102 | 122k | if (next_unit_.empty()) { |
103 | 16.9k | return false; |
104 | 16.9k | } |
105 | | |
106 | 105k | unit_remaining_ = sizes_.ReadInitialLength(&next_unit_); |
107 | 105k | sizes_.ReadDWARFVersion(&unit_remaining_); |
108 | | |
109 | 105k | if (sizes_.dwarf_version() > 4) { |
110 | 122 | THROW("DWARF data is too new for us"); |
111 | 122 | } |
112 | | |
113 | 105k | debug_info_offset_ = sizes_.ReadDWARFOffset(&unit_remaining_); |
114 | | |
115 | 105k | uint8_t segment_size; |
116 | | |
117 | 105k | sizes_.SetAddressSize(ReadFixed<uint8_t>(&unit_remaining_)); |
118 | 105k | segment_size = ReadFixed<uint8_t>(&unit_remaining_); |
119 | | |
120 | 105k | if (segment_size) { |
121 | 80 | THROW("we don't know how to handle segmented addresses."); |
122 | 80 | } |
123 | | |
124 | 105k | size_t ofs = unit_remaining_.data() - section_.data(); |
125 | 105k | size_t aligned_ofs = AlignUp(ofs, sizes_.address_size() * 2); |
126 | 105k | SkipBytes(aligned_ofs - ofs, &unit_remaining_); |
127 | 105k | return true; |
128 | 105k | } |
129 | | |
130 | | |
131 | | // LocationList //////////////////////////////////////////////////////////////// |
132 | | |
133 | | // Code for reading entries out of a location list. |
134 | | // For the moment we only care about finding the bounds of a list given its |
135 | | // offset, so we don't actually vend any of the data. |
136 | | |
137 | | class LocationList { |
138 | | public: |
139 | | LocationList(CompilationUnitSizes sizes, string_view data) |
140 | 22.3k | : sizes_(sizes), remaining_(data) {} |
141 | | |
142 | 21.5k | const char* read_offset() const { return remaining_.data(); } |
143 | | bool NextEntry(); |
144 | | |
145 | | private: |
146 | | CompilationUnitSizes sizes_; |
147 | | string_view remaining_; |
148 | | }; |
149 | | |
150 | 142k | bool LocationList::NextEntry() { |
151 | 142k | uint64_t start, end; |
152 | 142k | start = sizes_.ReadAddress(&remaining_); |
153 | 142k | end = sizes_.ReadAddress(&remaining_); |
154 | 142k | if (start == 0 && end == 0) { |
155 | 21.5k | return false; |
156 | 120k | } else if (start == UINT64_MAX || |
157 | 120k | (start == UINT32_MAX && sizes_.address_size() == 4)) { |
158 | | // Base address selection, nothing more to do. |
159 | 120k | } else { |
160 | | // Need to skip the location description. |
161 | 120k | uint16_t length = ReadFixed<uint16_t>(&remaining_); |
162 | 120k | SkipBytes(length, &remaining_); |
163 | 120k | } |
164 | 120k | return true; |
165 | 142k | } |
166 | | |
167 | | string_view GetLocationListRange(CompilationUnitSizes sizes, |
168 | 22.3k | string_view available) { |
169 | 22.3k | LocationList list(sizes, available); |
170 | 142k | while (list.NextEntry()) {} |
171 | 22.3k | return available.substr(0, list.read_offset() - available.data()); |
172 | 22.3k | } |
173 | | |
174 | | // RangeList /////////////////////////////////////////////////////////////////// |
175 | | |
176 | | void ReadRangeList(const CU& cu, uint64_t low_pc, string_view name, |
177 | 19.0k | RangeSink* sink, string_view* data) { |
178 | 19.0k | std::string name_str(name); |
179 | 19.0k | uint64_t max_address = cu.unit_sizes().MaxAddress(); |
180 | 65.6k | while (true) { |
181 | 64.7k | uint64_t start, end; |
182 | 64.7k | start = cu.unit_sizes().ReadAddress(data); |
183 | 64.7k | end = cu.unit_sizes().ReadAddress(data); |
184 | 64.7k | if (start == 0 && end == 0) { |
185 | 18.1k | return; |
186 | 46.6k | } else if (start == max_address) { |
187 | 11.9k | low_pc = end; |
188 | 34.6k | } else { |
189 | 34.6k | uint64_t size = end - start; |
190 | 34.6k | sink->AddVMRangeIgnoreDuplicate("dwarf_rangelist", low_pc + start, size, |
191 | 34.6k | name_str); |
192 | 34.6k | } |
193 | 64.7k | } |
194 | 19.0k | } |
195 | | |
196 | 5.04M | string_view* File::GetFieldByName(string_view name) { |
197 | 5.04M | if (name == "aranges") { |
198 | 91.7k | return &debug_aranges; |
199 | 4.94M | } else if (name == "addr") { |
200 | 183 | return &debug_addr; |
201 | 4.94M | } else if (name == "str") { |
202 | 118k | return &debug_str; |
203 | 4.83M | } else if (name == "str_offsets") { |
204 | 4.63k | return &debug_str_offsets; |
205 | 4.82M | } else if (name == "info") { |
206 | 138k | return &debug_info; |
207 | 4.68M | } else if (name == "types") { |
208 | 36.5k | return &debug_types; |
209 | 4.65M | } else if (name == "abbrev") { |
210 | 99.6k | return &debug_abbrev; |
211 | 4.55M | } else if (name == "line") { |
212 | 113k | return &debug_line; |
213 | 4.43M | } else if (name == "loc") { |
214 | 61.7k | return &debug_loc; |
215 | 4.37M | } else if (name == "pubnames") { |
216 | 4.99k | return &debug_pubnames; |
217 | 4.37M | } else if (name == "pubtypes") { |
218 | 3.37k | return &debug_pubtypes; |
219 | 4.36M | } else if (name == "ranges") { |
220 | 64.2k | return &debug_ranges; |
221 | 4.30M | } else if (name == "rnglists") { |
222 | 702 | return &debug_rnglists; |
223 | 4.30M | } else { |
224 | 4.30M | return nullptr; |
225 | 4.30M | } |
226 | 5.04M | } |
227 | | |
228 | | } // namespace dwarf |
229 | | |
230 | | // Bloaty DWARF Data Sources /////////////////////////////////////////////////// |
231 | | |
232 | | // The DWARF .debug_aranges section should, in theory, give us exactly the |
233 | | // information we need to map file ranges in linked binaries to compilation |
234 | | // units from where that code came. However, .debug_aranges is often incomplete |
235 | | // or missing completely, so we use it as just one of several data sources for |
236 | | // the "compileunits" data source. |
237 | 36.2k | static bool ReadDWARFAddressRanges(const dwarf::File& file, RangeSink* sink) { |
238 | | // Maps compilation unit offset -> source filename |
239 | | // Lazily initialized. |
240 | 36.2k | class FilenameMap { |
241 | 36.2k | public: |
242 | 36.2k | FilenameMap(const dwarf::File& file) |
243 | 36.2k | : info_reader_(file), |
244 | 36.2k | missing_("[DWARF is missing filename]") {} |
245 | | |
246 | 97.1k | std::string GetFilename(uint64_t compilation_unit_offset) { |
247 | 97.1k | auto& name = map_[compilation_unit_offset]; |
248 | 97.1k | if (name.empty()) { |
249 | 97.0k | name = LookupFilename(compilation_unit_offset); |
250 | 97.0k | } |
251 | 97.1k | return name; |
252 | 97.1k | } |
253 | | |
254 | 36.2k | private: |
255 | 97.0k | bool ReadName(std::string* name, uint64_t offset) { |
256 | 97.0k | auto sec = dwarf::InfoReader::Section::kDebugInfo; |
257 | 97.0k | dwarf::CUIter iter = info_reader_.GetCUIter(sec, offset); |
258 | 97.0k | dwarf::CU cu; |
259 | 97.0k | if (!iter.NextCU(info_reader_, &cu)) { |
260 | 3 | return false; |
261 | 3 | } |
262 | 97.0k | *name = cu.unit_name(); |
263 | 97.0k | return true; |
264 | 97.0k | } |
265 | | |
266 | 97.0k | std::string LookupFilename(uint64_t compilation_unit_offset) { |
267 | 97.0k | std::string name; |
268 | 97.0k | if (ReadName(&name, compilation_unit_offset)) { |
269 | 86.5k | return name; |
270 | 86.5k | } else { |
271 | 10.5k | return missing_; |
272 | 10.5k | } |
273 | 97.0k | } |
274 | | |
275 | 36.2k | dwarf::InfoReader info_reader_; |
276 | 36.2k | std::unordered_map<uint64_t, std::string> map_; |
277 | 36.2k | std::string missing_; |
278 | 36.2k | } map(file); |
279 | | |
280 | 36.2k | dwarf::AddressRanges ranges(file.debug_aranges); |
281 | | |
282 | 133k | while (ranges.NextUnit()) { |
283 | 97.1k | std::string filename = map.GetFilename(ranges.debug_info_offset()); |
284 | | |
285 | 284k | while (ranges.NextRange()) { |
286 | 187k | if (dwarf::IsValidDwarfAddress(ranges.address(), ranges.address_size())) { |
287 | 104k | sink->AddVMRangeIgnoreDuplicate("dwarf_aranges", ranges.address(), |
288 | 104k | ranges.length(), filename); |
289 | 104k | } |
290 | 187k | sink->AddFileRange("dwarf_aranges_data", filename, ranges.data()); |
291 | 187k | } |
292 | 97.1k | } |
293 | | |
294 | 36.2k | return true; |
295 | 36.2k | } |
296 | | |
297 | | struct GeneralDIE { |
298 | | absl::optional<string_view> name; |
299 | | absl::optional<string_view> location_string; |
300 | | absl::optional<uint64_t> location_uint64; |
301 | | absl::optional<uint64_t> low_pc; |
302 | | absl::optional<uint64_t> high_pc_addr; |
303 | | absl::optional<uint64_t> high_pc_size; |
304 | | absl::optional<uint64_t> stmt_list; |
305 | | absl::optional<uint64_t> rnglistx; |
306 | | absl::optional<uint64_t> ranges; |
307 | | absl::optional<uint64_t> start_scope; |
308 | | bool declaration = false; |
309 | | }; |
310 | | |
311 | | void ReadGeneralDIEAttr(uint16_t tag, dwarf::AttrValue val, const dwarf::CU& cu, |
312 | 2.27M | GeneralDIE* die) { |
313 | 2.27M | switch (tag) { |
314 | 359k | case DW_AT_name: |
315 | 359k | if (val.IsString()) { |
316 | 357k | die->name = val.GetString(cu); |
317 | 357k | } |
318 | 359k | break; |
319 | 46.2k | case DW_AT_declaration: |
320 | 46.2k | if (auto uint = val.ToUint(cu)) { |
321 | 46.2k | die->declaration = *uint; |
322 | 46.2k | } |
323 | 46.2k | break; |
324 | 155k | case DW_AT_location: |
325 | 155k | if (val.IsString()) { |
326 | 129k | die->location_string = val.GetString(cu); |
327 | 129k | } else if (val.form() == DW_FORM_sec_offset) { |
328 | 24.8k | die->location_uint64 = val.GetUint(cu); |
329 | 24.8k | } |
330 | 155k | break; |
331 | 123k | case DW_AT_low_pc: |
332 | 123k | if (auto uint = val.ToUint(cu)) { |
333 | 122k | die->low_pc = *uint; |
334 | 122k | } |
335 | 123k | break; |
336 | 72.0k | case DW_AT_high_pc: |
337 | 72.0k | switch (val.form()) { |
338 | 12.9k | case DW_FORM_addr: |
339 | 13.0k | case DW_FORM_addrx: |
340 | 13.0k | case DW_FORM_addrx1: |
341 | 13.0k | case DW_FORM_addrx2: |
342 | 13.0k | case DW_FORM_addrx3: |
343 | 13.1k | case DW_FORM_addrx4: |
344 | | // high_pc is absolute. |
345 | 13.1k | die->high_pc_addr = val.GetUint(cu); |
346 | 13.1k | break; |
347 | 299 | case DW_FORM_data1: |
348 | 342 | case DW_FORM_data2: |
349 | 23.9k | case DW_FORM_data4: |
350 | 57.8k | case DW_FORM_data8: |
351 | | // high_pc is a size. |
352 | 57.8k | die->high_pc_size = val.ToUint(cu); |
353 | 57.8k | break; |
354 | 1.04k | default: |
355 | 1.04k | if (verbose_level > 0) { |
356 | 0 | fprintf(stderr, "Unexpected form for high_pc: %d\n", val.form()); |
357 | 0 | } |
358 | 1.04k | break; |
359 | 72.0k | } |
360 | 71.8k | break; |
361 | 77.2k | case DW_AT_stmt_list: |
362 | 77.2k | if (auto uint = val.ToUint(cu)) { |
363 | 76.9k | die->stmt_list = *uint; |
364 | 76.9k | } |
365 | 77.2k | break; |
366 | 28.0k | case DW_AT_ranges: |
367 | 28.0k | if (auto uint = val.ToUint(cu)) { |
368 | 27.8k | if (val.form() == DW_FORM_rnglistx) { |
369 | 14 | die->rnglistx = *uint; |
370 | 27.8k | } else { |
371 | 27.8k | die->ranges = *uint; |
372 | 27.8k | } |
373 | 27.8k | } |
374 | 28.0k | break; |
375 | 1.08k | case DW_AT_start_scope: |
376 | 1.08k | if (auto uint = val.ToUint(cu)) { |
377 | 716 | die->start_scope = *uint; |
378 | 716 | } |
379 | 1.08k | break; |
380 | 2.27M | } |
381 | 2.27M | } |
382 | | |
383 | | uint64_t TryReadPcPair(const dwarf::CU& cu, const GeneralDIE& die, |
384 | 576k | RangeSink* sink) { |
385 | 576k | uint64_t addr; |
386 | 576k | uint64_t size; |
387 | | |
388 | 576k | if (!die.low_pc) return 0; |
389 | 105k | addr = *die.low_pc; |
390 | | |
391 | 105k | if (die.high_pc_addr) { |
392 | 11.9k | size = *die.high_pc_addr - addr; |
393 | 93.5k | } else if (die.high_pc_size) { |
394 | 42.0k | size = *die.high_pc_size; |
395 | 51.5k | } else{ |
396 | 51.5k | return 0; |
397 | 51.5k | } |
398 | | |
399 | 53.9k | sink->AddVMRangeIgnoreDuplicate("dwarf_pcpair", addr, size, cu.unit_name()); |
400 | 53.9k | return addr; |
401 | 105k | } |
402 | | |
403 | | // To view DIEs for a given file, try: |
404 | | // readelf --debug-dump=info foo.bin |
405 | | void AddDIE(const dwarf::CU& cu, const GeneralDIE& die, |
406 | 576k | const DualMap& symbol_map, RangeSink* sink) { |
407 | 576k | uint64_t low_pc = TryReadPcPair(cu, die, sink); |
408 | | |
409 | | // Sometimes the DIE has a "location", which gives the location as an address. |
410 | | // This parses a very small subset of the overall DWARF expression grammar. |
411 | 576k | if (die.location_string) { |
412 | 129k | string_view location = *die.location_string; |
413 | 129k | if (location.size() == cu.unit_sizes().address_size() + 1 && |
414 | 129k | location[0] == DW_OP_addr) { |
415 | 25.7k | location.remove_prefix(1); |
416 | 25.7k | uint64_t addr; |
417 | | // TODO(haberman): endian? |
418 | 25.7k | if (cu.unit_sizes().address_size() == 4) { |
419 | 15.4k | addr = ReadFixed<uint32_t>(&location); |
420 | 15.4k | } else if (cu.unit_sizes().address_size() == 8) { |
421 | 10.3k | addr = ReadFixed<uint64_t>(&location); |
422 | 10.3k | } else { |
423 | 0 | BLOATY_UNREACHABLE(); |
424 | 0 | } |
425 | | |
426 | | // Unfortunately the location doesn't include a size, so we look that part |
427 | | // up in the symbol map. |
428 | 25.7k | uint64_t size; |
429 | 25.7k | if (symbol_map.vm_map.TryGetSize(addr, &size)) { |
430 | 10.8k | sink->AddVMRangeIgnoreDuplicate("dwarf_location", addr, size, |
431 | 10.8k | cu.unit_name()); |
432 | 14.9k | } else { |
433 | 14.9k | if (verbose_level > 0) { |
434 | 0 | fprintf(stderr, |
435 | 0 | "bloaty: warning: couldn't find DWARF location in symbol " |
436 | 0 | "table, address: %" PRIx64 ", name: %s\n", |
437 | 0 | addr, cu.unit_name().c_str()); |
438 | 0 | } |
439 | 14.9k | } |
440 | 25.7k | } |
441 | 129k | } |
442 | | |
443 | | // Sometimes a location is given as an offset into debug_loc. |
444 | 576k | if (die.location_uint64) { |
445 | 24.8k | uint64_t location = *die.location_uint64;; |
446 | 24.8k | if (location < cu.dwarf().debug_loc.size()) { |
447 | 22.3k | absl::string_view loc_range = cu.dwarf().debug_loc.substr(location); |
448 | 22.3k | loc_range = GetLocationListRange(cu.unit_sizes(), loc_range); |
449 | 22.3k | sink->AddFileRange("dwarf_locrange", cu.unit_name(), loc_range); |
450 | 22.3k | } else if (verbose_level > 0) { |
451 | 0 | fprintf(stderr, |
452 | 0 | "bloaty: warning: DWARF location out of range, location=%" PRIx64 |
453 | 0 | "\n", |
454 | 0 | location); |
455 | 0 | } |
456 | 24.8k | } |
457 | | |
458 | | // DWARF 5 range list is the same information as "ranges" but in a different |
459 | | // format. |
460 | 576k | if (die.rnglistx) { |
461 | 5 | uint64_t range_list = *die.rnglistx; |
462 | 5 | size_t offset_size = cu.unit_sizes().dwarf64() ? 8 : 4; |
463 | 5 | string_view offset_data = |
464 | 5 | StrictSubstr(cu.dwarf().debug_rnglists, |
465 | 5 | cu.range_lists_base() + (range_list * offset_size)); |
466 | 5 | uint64_t offset = cu.unit_sizes().ReadDWARFOffset(&offset_data); |
467 | 5 | string_view data = StrictSubstr( |
468 | 5 | cu.dwarf().debug_rnglists, cu.range_lists_base() + offset); |
469 | 5 | const char* start = data.data(); |
470 | 5 | bool done = false; |
471 | 5 | uint64_t base_address = cu.addr_base(); |
472 | 5 | while (!done) { |
473 | 0 | switch (ReadFixed<uint8_t>(&data)) { |
474 | 0 | case DW_RLE_end_of_list: |
475 | 0 | done = true; |
476 | 0 | break; |
477 | 0 | case DW_RLE_base_addressx: |
478 | 0 | base_address = |
479 | 0 | ReadIndirectAddress(cu, dwarf::ReadLEB128<uint64_t>(&data)); |
480 | 0 | break; |
481 | 0 | case DW_RLE_startx_endx: { |
482 | 0 | uint64_t start = |
483 | 0 | ReadIndirectAddress(cu, dwarf::ReadLEB128<uint64_t>(&data)); |
484 | 0 | uint64_t end = |
485 | 0 | ReadIndirectAddress(cu, dwarf::ReadLEB128<uint64_t>(&data)); |
486 | 0 | sink->AddVMRangeIgnoreDuplicate("dwarf_rangelst", start, end - start, |
487 | 0 | cu.unit_name()); |
488 | 0 | break; |
489 | 0 | } |
490 | 0 | case DW_RLE_startx_length: { |
491 | 0 | uint64_t start = |
492 | 0 | ReadIndirectAddress(cu, dwarf::ReadLEB128<uint64_t>(&data)); |
493 | 0 | uint64_t length = dwarf::ReadLEB128<uint64_t>(&data); |
494 | 0 | sink->AddVMRangeIgnoreDuplicate("dwarf_rangelst", start, length, |
495 | 0 | cu.unit_name()); |
496 | 0 | break; |
497 | 0 | } |
498 | 0 | case DW_RLE_offset_pair: { |
499 | 0 | uint64_t start = dwarf::ReadLEB128<uint64_t>(&data) + base_address; |
500 | 0 | uint64_t end = dwarf::ReadLEB128<uint64_t>(&data) + base_address; |
501 | 0 | sink->AddVMRangeIgnoreDuplicate("dwarf_rangelst", start, end - start, |
502 | 0 | cu.unit_name()); |
503 | 0 | break; |
504 | 0 | } |
505 | 0 | case DW_RLE_base_address: |
506 | 0 | case DW_RLE_start_end: |
507 | 0 | case DW_RLE_start_length: |
508 | 0 | THROW("NYI"); |
509 | 0 | break; |
510 | 0 | } |
511 | 0 | } |
512 | 5 | string_view all(start, data.data() - start); |
513 | 5 | sink->AddFileRange("dwarf_rangelst_addrs", cu.unit_name(), all); |
514 | 576k | } else { |
515 | 576k | uint64_t ranges_offset = UINT64_MAX; |
516 | | |
517 | | // There are two different attributes that sometimes contain an offset into |
518 | | // debug_ranges. |
519 | 576k | if (die.ranges) { |
520 | 27.4k | ranges_offset = *die.ranges; |
521 | 549k | } else if (die.start_scope) { |
522 | 648 | ranges_offset = *die.start_scope; |
523 | 648 | } |
524 | | |
525 | 576k | if (ranges_offset != UINT64_MAX) { |
526 | 28.0k | if (ranges_offset < cu.dwarf().debug_ranges.size()) { |
527 | 19.0k | absl::string_view data = cu.dwarf().debug_ranges.substr(ranges_offset); |
528 | 19.0k | const char* start = data.data(); |
529 | 19.0k | ReadRangeList(cu, low_pc, cu.unit_name(), sink, &data); |
530 | 19.0k | string_view all(start, data.data() - start); |
531 | 19.0k | sink->AddFileRange("dwarf_debugrange", cu.unit_name(), all); |
532 | 19.0k | } else if (verbose_level > 0) { |
533 | 0 | fprintf(stderr, |
534 | 0 | "bloaty: warning: DWARF debug range out of range, " |
535 | 0 | "ranges_offset=%" PRIx64 "\n", |
536 | 0 | ranges_offset); |
537 | 0 | } |
538 | 28.0k | } |
539 | 576k | } |
540 | 576k | } |
541 | | |
542 | | static void ReadDWARFPubNames(dwarf::InfoReader& reader, string_view section, |
543 | 21.3k | RangeSink* sink) { |
544 | 21.3k | string_view remaining = section; |
545 | | |
546 | 21.6k | while (remaining.size() > 0) { |
547 | 317 | dwarf::CompilationUnitSizes sizes; |
548 | 317 | string_view full_unit = remaining; |
549 | 317 | string_view unit = sizes.ReadInitialLength(&remaining); |
550 | 317 | full_unit = |
551 | 317 | full_unit.substr(0, unit.size() + (unit.data() - full_unit.data())); |
552 | 317 | sizes.ReadDWARFVersion(&unit); |
553 | 317 | uint64_t debug_info_offset = sizes.ReadDWARFOffset(&unit); |
554 | | |
555 | 317 | dwarf::CUIter iter = reader.GetCUIter( |
556 | 317 | dwarf::InfoReader::Section::kDebugInfo, debug_info_offset); |
557 | 317 | dwarf::CU cu; |
558 | 317 | if (iter.NextCU(reader, &cu) && !cu.unit_name().empty()) { |
559 | 46 | sink->AddFileRange("dwarf_pubnames", cu.unit_name(), full_unit); |
560 | 46 | } |
561 | 317 | } |
562 | 21.3k | } |
563 | | |
564 | | static void ReadDWARFStmtListRange(const dwarf::CU& cu, uint64_t offset, |
565 | 57.7k | RangeSink* sink) { |
566 | 57.7k | string_view data = cu.dwarf().debug_line; |
567 | 57.7k | SkipBytes(offset, &data); |
568 | 57.7k | string_view data_with_length = data; |
569 | 57.7k | dwarf::CompilationUnitSizes sizes; |
570 | 57.7k | data = sizes.ReadInitialLength(&data); |
571 | 57.7k | data = data_with_length.substr( |
572 | 57.7k | 0, data.size() + (data.data() - data_with_length.data())); |
573 | 57.7k | sink->AddFileRange("dwarf_stmtlistrange", cu.unit_name(), data); |
574 | 57.7k | } |
575 | | |
576 | | struct DwoFilePointer { |
577 | | std::string comp_dir; |
578 | | std::string dwo_name; |
579 | | }; |
580 | | |
581 | | // The DWARF debug info can help us get compileunits info. DIEs for compilation |
582 | | // units, functions, and global variables often have attributes that will |
583 | | // resolve to addresses. |
584 | | static void ReadDWARFDebugInfo(dwarf::InfoReader& reader, |
585 | | dwarf::InfoReader::Section section, |
586 | 35.2k | const DualMap& symbol_map, RangeSink* sink) { |
587 | 35.2k | dwarf::CUIter iter = reader.GetCUIter(section); |
588 | 35.2k | dwarf::CU cu; |
589 | 512k | cu.SetIndirectStringCallback([sink, &cu](string_view str) { |
590 | 512k | sink->AddFileRange("dwarf_strp", cu.unit_name(), str); |
591 | 512k | }); |
592 | | |
593 | 113k | while (iter.NextCU(reader, &cu)) { |
594 | 78.0k | dwarf::DIEReader die_reader = cu.GetDIEReader(); |
595 | 78.0k | GeneralDIE compileunit_die; |
596 | 78.0k | DwoFilePointer dwo_info; |
597 | 78.0k | auto* abbrev = die_reader.ReadCode(cu); |
598 | 78.0k | die_reader.ReadAttributes( |
599 | 78.0k | cu, abbrev, |
600 | 489k | [&](uint16_t tag, dwarf::AttrValue value) { |
601 | 489k | ReadGeneralDIEAttr(tag, value, cu, &compileunit_die); |
602 | 489k | switch (tag) { |
603 | 76.8k | case DW_AT_comp_dir: |
604 | 76.8k | if (value.IsString()) { |
605 | 76.5k | dwo_info.comp_dir = value.GetString(cu); |
606 | 76.5k | } |
607 | 76.8k | break; |
608 | 0 | case DW_AT_GNU_dwo_name: |
609 | 0 | if (value.IsString()) { |
610 | 0 | dwo_info.dwo_name = value.GetString(cu); |
611 | 0 | } |
612 | 0 | break; |
613 | 489k | } |
614 | 489k | }); |
615 | | |
616 | 78.0k | if (!dwo_info.comp_dir.empty() && !dwo_info.dwo_name.empty()) { |
617 | 0 | auto file = MmapInputFileFactory().OpenFile(dwo_info.comp_dir + "/" + dwo_info.dwo_name); |
618 | 0 | dwarf::File dwo_dwarf; |
619 | 0 | cu.dwarf().open(*file, &dwo_dwarf, sink); |
620 | 0 | ReadDWARFCompileUnits(dwo_dwarf, symbol_map, &cu, sink); |
621 | 0 | } |
622 | | |
623 | 78.0k | if (cu.unit_name().empty()) { |
624 | 17.9k | continue; |
625 | 17.9k | } |
626 | | |
627 | 60.0k | sink->AddFileRange("dwarf_debuginfo", cu.unit_name(), cu.entire_unit()); |
628 | 60.0k | AddDIE(cu, compileunit_die, symbol_map, sink); |
629 | | |
630 | 60.0k | if (compileunit_die.stmt_list) { |
631 | 57.7k | ReadDWARFStmtListRange(cu, *compileunit_die.stmt_list, sink); |
632 | 57.7k | } |
633 | | |
634 | 60.0k | sink->AddFileRange("dwarf_abbrev", cu.unit_name(), cu.unit_abbrev().abbrev_data()); |
635 | | |
636 | 629k | while (auto abbrev = die_reader.ReadCode(cu)) { |
637 | 569k | GeneralDIE die; |
638 | 569k | die_reader.ReadAttributes( |
639 | 1.78M | cu, abbrev, [&cu, &die](uint16_t tag, dwarf::AttrValue value) { |
640 | 1.78M | ReadGeneralDIEAttr(tag, value, cu, &die); |
641 | 1.78M | }); |
642 | | |
643 | | // low_pc == 0 is a signal that this routine was stripped out of the |
644 | | // final binary. Also any declaration should be skipped. |
645 | 569k | if ((die.low_pc && !cu.IsValidDwarfAddress(*die.low_pc)) || |
646 | 569k | die.declaration) { |
647 | 48.1k | die_reader.SkipChildren(cu, abbrev); |
648 | 520k | } else { |
649 | 520k | AddDIE(cu, die, symbol_map, sink); |
650 | 520k | } |
651 | 569k | } |
652 | 60.0k | } |
653 | 35.2k | } |
654 | | |
655 | | void ReadDWARFCompileUnits(const dwarf::File& file, const DualMap& symbol_map, |
656 | 58.9k | const dwarf::CU* skeleton, RangeSink* sink) { |
657 | 58.9k | if (!file.debug_info.size()) { |
658 | 15.0k | THROW("missing debug info"); |
659 | 15.0k | } |
660 | | |
661 | 43.8k | if (file.debug_aranges.size()) { |
662 | 36.2k | ReadDWARFAddressRanges(file, sink); |
663 | 36.2k | } |
664 | | |
665 | | // Share a reader to avoid re-parsing debug abbreviations. |
666 | 43.8k | dwarf::InfoReader reader(file, skeleton); |
667 | | |
668 | 43.8k | ReadDWARFDebugInfo(reader, dwarf::InfoReader::Section::kDebugInfo, symbol_map, |
669 | 43.8k | sink); |
670 | 43.8k | ReadDWARFDebugInfo(reader, dwarf::InfoReader::Section::kDebugTypes, |
671 | 43.8k | symbol_map, sink); |
672 | 43.8k | ReadDWARFPubNames(reader, file.debug_pubnames, sink); |
673 | 43.8k | ReadDWARFPubNames(reader, file.debug_pubtypes, sink); |
674 | 43.8k | } |
675 | | |
676 | | static std::string LineInfoKey(const std::string& file, uint32_t line, |
677 | 0 | bool include_line) { |
678 | 0 | if (include_line) { |
679 | 0 | return file + ":" + std::to_string(line); |
680 | 0 | } else { |
681 | 0 | return file; |
682 | 0 | } |
683 | 0 | } |
684 | | |
685 | | static void ReadDWARFStmtList(bool include_line, |
686 | | dwarf::LineInfoReader* line_info_reader, |
687 | 0 | RangeSink* sink) { |
688 | 0 | uint64_t span_startaddr = 0; |
689 | 0 | std::string last_source; |
690 | |
|
691 | 0 | while (line_info_reader->ReadLineInfo()) { |
692 | 0 | const auto& line_info = line_info_reader->lineinfo(); |
693 | 0 | auto addr = line_info.address; |
694 | 0 | auto number = line_info.line; |
695 | 0 | auto name = |
696 | 0 | line_info.end_sequence |
697 | 0 | ? last_source |
698 | 0 | : LineInfoKey(line_info_reader->GetExpandedFilename(line_info.file), |
699 | 0 | number, include_line); |
700 | 0 | if (!span_startaddr) { |
701 | 0 | span_startaddr = addr; |
702 | 0 | } else if (line_info.end_sequence || |
703 | 0 | (!last_source.empty() && name != last_source)) { |
704 | 0 | sink->AddVMRange("dwarf_stmtlist", span_startaddr, addr - span_startaddr, |
705 | 0 | last_source); |
706 | 0 | if (line_info.end_sequence) { |
707 | 0 | span_startaddr = 0; |
708 | 0 | } else { |
709 | 0 | span_startaddr = addr; |
710 | 0 | } |
711 | 0 | } |
712 | 0 | last_source = name; |
713 | 0 | } |
714 | 0 | } |
715 | | |
716 | | void ReadDWARFInlines(const dwarf::File& file, RangeSink* sink, |
717 | 60.8k | bool include_line) { |
718 | 60.8k | if (!file.debug_info.size() || !file.debug_line.size()) { |
719 | 15.5k | THROW("no debug info"); |
720 | 15.5k | } |
721 | | |
722 | 45.3k | dwarf::InfoReader reader(file); |
723 | 45.3k | dwarf::CUIter iter = reader.GetCUIter(dwarf::InfoReader::Section::kDebugInfo); |
724 | 45.3k | dwarf::CU cu; |
725 | 45.3k | dwarf::DIEReader die_reader = cu.GetDIEReader(); |
726 | 45.3k | dwarf::LineInfoReader line_info_reader(file); |
727 | | |
728 | 45.3k | if (!iter.NextCU(reader, &cu)) { |
729 | 0 | THROW("debug info is present, but empty"); |
730 | 0 | } |
731 | | |
732 | 45.3k | while (auto abbrev = die_reader.ReadCode(cu)) { |
733 | 0 | absl::optional<uint64_t> stmt_list; |
734 | 0 | die_reader.ReadAttributes( |
735 | 0 | cu, abbrev, [&stmt_list, &cu](uint16_t tag, dwarf::AttrValue val) { |
736 | 0 | if (tag == DW_AT_stmt_list) { |
737 | 0 | stmt_list = val.ToUint(cu); |
738 | 0 | } |
739 | 0 | }); |
740 | |
|
741 | 0 | if (stmt_list) { |
742 | 0 | line_info_reader.SeekToOffset(*stmt_list, cu.unit_sizes().address_size()); |
743 | 0 | ReadDWARFStmtList(include_line, &line_info_reader, sink); |
744 | 0 | } |
745 | 0 | } |
746 | 45.3k | } |
747 | | |
748 | | } // namespace bloaty |