Coverage Report

Created: 2023-12-14 13:59

/src/bloaty/src/dwarf.cc
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