Commit 4d4ca9ec authored by Lucian Grijincu's avatar Lucian Grijincu Committed by facebook-github-bot-0

folly: symbolizer: slow address->{file+line-number} lookup if `.debug_aranges`...

folly: symbolizer: slow address->{file+line-number} lookup if `.debug_aranges` is missing (e.g. --strip-debug-non-line)

Summary:Binaries linked with `gold` and `--strip-debug-non-line` don't have an `.debug_aranges` section

We still want to map `address->{file+line-number}` to get nice stack
traces even though this might be slower (linear search all compilation unit entries in `.debug_info`).

Before:
```
$ # link with gold + --strip-debug-non-line
$ folly/experimental/exception_tracer/exception_tracer_test
E0217 15:02:13.694947 1321814 ExceptionTracer.cpp:179] Exception type: std::runtime_error (9 frames)
    @ 000000000040ad2d __cxa_throw
    @ 0000000000409df3 bar()
    @ 0000000000409eab baz()
    @ 0000000000407c77 main
    @ 00007f00dd9860f5 __libc_start_main
    @ 000000000040991b (unknown)
```

After (similar to the output without `--strip-debug-non-line`):
```
E0217 18:37:37.579596 1583124 ExceptionTracer.cpp:179] Exception type: std::runtime_error (9 frames)
    @ 000000000040ad6d __cxa_throw
                       ./folly/experimental/exception_tracer/ExceptionTracerLib.cpp:57
    @ 0000000000409e33 bar()
                       ./folly/experimental/exception_tracer/ExceptionTracerTest.cpp:24
    @ 0000000000409eeb baz()
                       ./folly/experimental/exception_tracer/ExceptionTracerTest.cpp:51
    @ 0000000000407c87 main
                       ./folly/experimental/exception_tracer/ExceptionTracerTest.cpp:96
    @ 00007f1d16ff80f5 __libc_start_main
    @ 000000000040995b (unknown)
```

Differential Revision: D2947965

fb-gh-sync-id: e517bab324b1dcb70cadc9a5211ce794e35c83a5
shipit-source-id: e517bab324b1dcb70cadc9a5211ce794e35c83a5
parent ad925ffa
......@@ -309,13 +309,16 @@ void Dwarf::init() {
// Make sure that all .debug_* sections exist
if (!getSection(".debug_info", &info_) ||
!getSection(".debug_abbrev", &abbrev_) ||
!getSection(".debug_aranges", &aranges_) ||
!getSection(".debug_line", &line_) ||
!getSection(".debug_str", &strings_)) {
elf_ = nullptr;
return;
}
getSection(".debug_str", &strings_);
// Optional: fast address range lookup. If missing .debug_info can
// be used - but it's much slower (linear scan).
getSection(".debug_aranges", &aranges_);
}
bool Dwarf::readAbbreviation(folly::StringPiece& section,
......@@ -423,23 +426,20 @@ folly::StringPiece Dwarf::getStringFromStringSection(uint64_t offset) const {
return readNullTerminated(sp);
}
bool Dwarf::findAddress(uintptr_t address, LocationInfo& locationInfo) const {
locationInfo = LocationInfo();
if (!elf_) { // no file
return false;
}
// Find address range in .debug_aranges, map to compilation unit
Section arangesSection(aranges_);
/**
* Find @address in .debug_aranges and return the offset in
* .debug_info for compilation unit to which this address belongs.
*/
bool Dwarf::findDebugInfoOffset(uintptr_t address,
StringPiece aranges,
uint64_t& offset) {
Section arangesSection(aranges);
folly::StringPiece chunk;
uint64_t debugInfoOffset;
bool found = false;
while (!found && arangesSection.next(chunk)) {
while (arangesSection.next(chunk)) {
auto version = read<uint16_t>(chunk);
FOLLY_SAFE_CHECK(version == 2, "invalid aranges version");
debugInfoOffset = readOffset(chunk, arangesSection.is64Bit());
offset = readOffset(chunk, arangesSection.is64Bit());
auto addressSize = read<uint8_t>(chunk);
FOLLY_SAFE_CHECK(addressSize == sizeof(uintptr_t), "invalid address size");
auto segmentSize = read<uint8_t>(chunk);
......@@ -448,7 +448,7 @@ bool Dwarf::findAddress(uintptr_t address, LocationInfo& locationInfo) const {
// Padded to a multiple of 2 addresses.
// Strangely enough, this is the only place in the DWARF spec that requires
// padding.
skipPadding(chunk, aranges_.data(), 2 * sizeof(uintptr_t));
skipPadding(chunk, aranges.data(), 2 * sizeof(uintptr_t));
for (;;) {
auto start = read<uintptr_t>(chunk);
auto length = read<uintptr_t>(chunk);
......@@ -459,20 +459,37 @@ bool Dwarf::findAddress(uintptr_t address, LocationInfo& locationInfo) const {
// Is our address in this range?
if (address >= start && address < start + length) {
found = true;
break;
return true;
}
}
}
return false;
}
if (!found) {
return false;
}
// Read compilation unit header from .debug_info
folly::StringPiece sp(info_);
sp.advance(debugInfoOffset);
Section debugInfoSection(sp);
/**
* Find the @locationInfo for @address in the compilation unit represented
* by the @sp .debug_info entry.
* Returns whether the address was found.
* Advances @sp to the next entry in .debug_info.
*/
bool Dwarf::findLocation(uintptr_t address,
StringPiece& infoEntry,
LocationInfo& locationInfo) const {
// For each compilation unit compiled with a DWARF producer, a
// contribution is made to the .debug_info section of the object
// file. Each such contribution consists of a compilation unit
// header (see Section 7.5.1.1) followed by a single
// DW_TAG_compile_unit or DW_TAG_partial_unit debugging information
// entry, together with its children.
// 7.5.1.1 Compilation Unit Header
// 1. unit_length (4B or 12B): read by Section::next
// 2. version (2B)
// 3. debug_abbrev_offset (4B or 8B): offset into the .debug_abbrev section
// 4. address_size (1B)
Section debugInfoSection(infoEntry);
folly::StringPiece chunk;
FOLLY_SAFE_CHECK(debugInfoSection.next(chunk), "invalid debug info");
auto version = read<uint16_t>(chunk);
......@@ -481,14 +498,17 @@ bool Dwarf::findAddress(uintptr_t address, LocationInfo& locationInfo) const {
auto addressSize = read<uint8_t>(chunk);
FOLLY_SAFE_CHECK(addressSize == sizeof(uintptr_t), "invalid address size");
// We survived so far. The first (and only) DIE should be
// DW_TAG_compile_unit
// We survived so far. The first (and only) DIE should be DW_TAG_compile_unit
// NOTE: - binutils <= 2.25 does not issue DW_TAG_partial_unit.
// - dwarf compression tools like `dwz` may generate it.
// TODO(tudorb): Handle DW_TAG_partial_unit?
auto code = readULEB(chunk);
FOLLY_SAFE_CHECK(code != 0, "invalid code");
auto abbr = getAbbreviation(code, abbrevOffset);
FOLLY_SAFE_CHECK(abbr.tag == DW_TAG_compile_unit,
"expecting compile unit entry");
// Skip children entries, advance to the next compilation unit entry.
infoEntry.advance(chunk.end() - infoEntry.begin());
// Read attributes, extracting the few we care about
bool foundLineOffset = false;
......@@ -528,17 +548,49 @@ bool Dwarf::findAddress(uintptr_t address, LocationInfo& locationInfo) const {
locationInfo.mainFile = Path(compilationDirectory, "", mainFileName);
}
if (foundLineOffset) {
folly::StringPiece lineSection(line_);
lineSection.advance(lineOffset);
LineNumberVM lineVM(lineSection, compilationDirectory);
if (!foundLineOffset) {
return false;
}
folly::StringPiece lineSection(line_);
lineSection.advance(lineOffset);
LineNumberVM lineVM(lineSection, compilationDirectory);
// Execute line number VM program to find file and line
locationInfo.hasFileAndLine =
// Execute line number VM program to find file and line
locationInfo.hasFileAndLine =
lineVM.findAddress(address, locationInfo.file, locationInfo.line);
return locationInfo.hasFileAndLine;
}
bool Dwarf::findAddress(uintptr_t address, LocationInfo& locationInfo) const {
locationInfo = LocationInfo();
if (!elf_) { // no file
return false;
}
return true;
if (!aranges_.empty()) {
// Fast path: find the right .debug_info entry by looking up the
// address in .debug_aranges.
uint64_t offset = 0;
if (findDebugInfoOffset(address, aranges_, offset)) {
// Read compilation unit header from .debug_info
folly::StringPiece infoEntry(info_);
infoEntry.advance(offset);
findLocation(address, infoEntry, locationInfo);
return true;
}
}
// Slow path (linear scan): Iterate over all .debug_info entries
// and look for the address in each compilation unit.
// NOTE: clang doesn't generate entries in .debug_aranges for some
// functions, but always generates .debug_info entries.
folly::StringPiece infoEntry(info_);
while (!infoEntry.empty() && !locationInfo.hasFileAndLine) {
findLocation(address, infoEntry, locationInfo);
}
return locationInfo.hasFileAndLine;
}
Dwarf::LineNumberVM::LineNumberVM(folly::StringPiece data,
......
......@@ -117,6 +117,12 @@ class Dwarf {
private:
void init();
static bool findDebugInfoOffset(uintptr_t address,
StringPiece aranges,
uint64_t& offset);
bool findLocation(uintptr_t address,
StringPiece& infoEntry,
LocationInfo& info) const;
const ElfFile* elf_;
......
/*
* Copyright 2016 Facebook, Inc.
*
* 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.
*/
#include <folly/Benchmark.h>
#include <folly/experimental/symbolizer/Dwarf.h>
#include <gflags/gflags.h>
void dummy() {}
BENCHMARK(DwarfFindAddress, n) {
folly::BenchmarkSuspender suspender;
using namespace folly::symbolizer;
// NOTE: Using '/proc/self/exe' only works if the code for @dummy is
// statically linked into the binary.
ElfFile elf("/proc/self/exe");
Dwarf dwarf(&elf);
suspender.dismiss();
for (size_t i = 0; i < n; i++) {
Dwarf::LocationInfo info;
dwarf.findAddress(uintptr_t(&dummy), info);
}
}
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
folly::runBenchmarksOnFlag();
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment