Commit 88e03cf4 authored by Matt Ma's avatar Matt Ma Committed by Facebook Github Bot

Back out "Support inline functions in folly stack trace symbolizer"

Summary: broken zinc/test and tao/coordinator/test: in dev/nosan mode.

Reviewed By: luciang

Differential Revision: D17310046

fbshipit-source-id: 0a98d7cea24438a42932466394e63db4f64591cf
parent 32701064
This diff is collapsed.
......@@ -20,7 +20,6 @@
#include <boost/variant.hpp>
#include <folly/Optional.h>
#include <folly/Range.h>
#include <folly/experimental/symbolizer/Elf.h>
......@@ -116,21 +115,9 @@ class Dwarf {
FAST,
// Scan all CU in .debug_info (slow!) on .debug_aranges lookup failure.
FULL,
// Scan .debug_info (super slower, use with caution) for inline functions in
// addition to FULL.
FULL_WITH_INLINE,
};
// More than one location info may exist if current frame is an inline
// function call.
static const uint32_t kMaxLocationInfoPerFrame = 5;
struct LocationInfo {
// Whether the location info is filled.
bool empty = true;
// Function name in call stack.
folly::StringPiece name;
bool hasMainFile = false;
Path mainFile;
......@@ -139,19 +126,21 @@ class Dwarf {
uint64_t line = 0;
};
// Find the file and line number information corresponding to address.
bool findAddress(
uintptr_t address,
LocationInfoMode mode,
LocationInfo& info,
LocationInfo inlineInfo[],
uint32_t maxNumInline = kMaxLocationInfoPerFrame) const;
/**
* Find the file and line number information corresponding to address.
*/
bool findAddress(uintptr_t address, LocationInfo& info, LocationInfoMode mode)
const;
private:
static bool
findDebugInfoOffset(uintptr_t address, StringPiece aranges, uint64_t& offset);
void init();
bool findLocation(
uintptr_t address,
StringPiece& infoEntry,
LocationInfo& info) const;
const ElfFile* elf_;
......@@ -180,78 +169,18 @@ class Dwarf {
folly::StringPiece data_;
};
struct AttributeSpec {
uint64_t name = 0;
uint64_t form = 0;
explicit operator bool() const {
return name != 0 || form != 0;
}
};
// Abbreviation for a Debugging Information Entry.
struct DIEAbbreviation {
uint64_t code;
uint64_t tag;
bool hasChildren = false;
folly::StringPiece attributes;
};
// Maximum number of DIEAbbreviation to cache in a compilation unit. Used to
// speed up inline function lookup.
static const uint32_t kMaxAbbreviationEntries = 1000;
// A top level chunk in the .debug_info that contains a compilation unit.
struct CompilationUnit {
// Offset in .debug_info of this compilation unit.
uint64_t offset;
uint64_t size;
bool is64Bit;
uint8_t version;
uint8_t addrSize;
uint64_t abbrevOffset;
// Offset in .debug_info for the first DIE in this compilation unit.
uint64_t firstDie;
// Only the CompilationUnit that contains the caller functions needs this
// cache.
// Indexed by (abbr.code - 1) if (abbr.code - 1) < abbrCache.size();
folly::Range<DIEAbbreviation*> abbrCache;
};
bool hasChildren;
// Debugging information entry to define a low-level representation of a
// source program. Each debugging information entry consists of an identifying
// tag and a series of attributes. An entry, or group of entries together,
// provide a description of a corresponding entity in the source program.
struct Die {
const CompilationUnit* cu;
// Offset within debug info.
uint64_t offset;
bool is64Bit;
uint64_t code;
// Offset from start to first attribute
uint8_t attrOffset;
// If we know where the next sibling is (eg via DW_AT_sibling), and
// it fits in uint32_t, the delta we need to add to offset to get
// there; otherwise zero.
uint32_t siblingDelta;
// If we know where the next die is, and it fits in uint32_t, this
// is the delta we need to add to offset to get there; otherwise zero.
// if there are no children, this will be the same as sibling.
uint32_t nextDieDelta;
DIEAbbreviation abbr;
};
struct Attribute {
AttributeSpec spec;
Die* die;
boost::variant<uint64_t, folly::StringPiece> attrValue;
};
struct Attribute {
uint64_t name;
uint64_t form;
};
struct CodeLocation {
CompilationUnit cu;
Die die;
uint64_t line;
folly::StringPiece attributes;
};
// Interpreter for the line number bytecode VM
......@@ -263,12 +192,6 @@ class Dwarf {
bool findAddress(uintptr_t address, Path& file, uint64_t& line);
// Gets full file name at given index including directory.
Path getFullFileName(uint64_t index) const {
auto fn = getFileName(index);
return Path({}, getIncludeDirectory(fn.directoryIndex), fn.relativeName);
}
private:
void init();
void reset();
......@@ -338,82 +261,27 @@ class Dwarf {
uint64_t discriminator_;
};
using AttributeValue = boost::variant<uint64_t, folly::StringPiece>;
// Finds the Compilation Unit starting at offset.
void getCompilationUnit(
Dwarf::CompilationUnit& cu,
uint64_t offset,
Dwarf::DIEAbbreviation* abbrs) const;
// Finds the Compilation Unit that contains offset.
CompilationUnit findCompilationUnit(uint64_t offset) const;
// cu must exist during the life cycle of created Die.
Die getDieAtOffset(const CompilationUnit& cu, uint64_t offset) const;
Die getNextSibling(Die& die) const;
// Reads an abbreviation from a StringPiece, return true if at end; advance sp
// Read an abbreviation from a StringPiece, return true if at end; advance sp
static bool readAbbreviation(folly::StringPiece& sp, DIEAbbreviation& abbr);
// Gets abbreviation corresponding to a code, in the chunk starting at
// Get abbreviation corresponding to a code, in the chunk starting at
// offset in the .debug_abbrev section
DIEAbbreviation getAbbreviation(uint64_t code, uint64_t offset) const;
// Reads attribute name and form from sp.
static AttributeSpec readAttributeSpec(folly::StringPiece& sp);
// Reads attribute value of given DIE
Attribute readAttribute(Die& die, AttributeSpec& spec, folly::StringPiece& sp)
const;
// Read one attribute <name, form> pair, advance sp; returns <0, 0> at end.
static DIEAbbreviation::Attribute readAttribute(folly::StringPiece& sp);
// Finds a subprogram debugging info entry that contains a given address among
// children of given die. Depth first search.
void findSubProgramDieForAddress(
Dwarf::Die& die,
uint64_t address,
Dwarf::Die& subprogram) const;
// Finds inlined subroutine DIEs and their caller lines that contains a given
// address among children of given die. Depth first search.
void findInlinedSubroutineDieForAddress(
const CompilationUnit& cu,
Dwarf::Die& die,
uint64_t address,
CodeLocation* isrLoc,
uint32_t& numFound) const;
// Iterates over all children of a debugging info entry, calling the given
// callable for each. Iteration is stopped early if any of the calls return
// false.
template <typename F>
void forEachChild(Die& die, F&& f) const;
// Iterates over all attributes of a debugging info entry, calling the given
// callable for each. Iteration is stopped early if any of the calls return
// false.
template <typename F>
void forEachAttribute(Die& die, F&& f) const;
// Iterate over all attributes of the given DIE, and move to its first child
// if any or its next sibling.
void moveToNextDie(Dwarf::Die& die) const;
// Read one attribute value, advance sp
typedef boost::variant<uint64_t, folly::StringPiece> AttributeValue;
AttributeValue
readAttributeValue(folly::StringPiece& sp, uint64_t form, bool is64Bit) const;
// Get an ELF section by name, return true if found
bool getSection(const char* name, folly::StringPiece* section) const;
// Gets a string from the .debug_str section
// Get a string from the .debug_str section
folly::StringPiece getStringFromStringSection(uint64_t offset) const;
// Finds location info (file and line) for a given address in the given
// compilation unit.
bool findLocation(
uintptr_t address,
const LocationInfoMode mode,
const CompilationUnit& unit,
LocationInfo& info,
LocationInfo inlineInfo[],
uint32_t maxNumInline = kMaxLocationInfoPerFrame) const;
folly::StringPiece info_; // .debug_info
folly::StringPiece abbrev_; // .debug_abbrev
folly::StringPiece aranges_; // .debug_aranges
......
......@@ -76,15 +76,8 @@ void SymbolizedFrame::set(
file_ = file;
name = file->getSymbolName(sym);
location.name = name;
Dwarf(file.get())
.findAddress(
address,
mode,
location,
inlineLocations,
Dwarf::kMaxLocationInfoPerFrame);
Dwarf(file.get()).findAddress(address, location, mode);
}
Symbolizer::Symbolizer(
......@@ -222,19 +215,52 @@ void SymbolizePrinter::print(uintptr_t address, const SymbolizedFrame& frame) {
doPrint(formatter.format(address));
}
const char padBuf[] = " ";
folly::StringPiece pad(
padBuf, sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
color(kFunctionColor);
if (!frame.found) {
doPrint(" (not found)");
return;
}
printLocationInfo(frame.location, false);
for (const Dwarf::LocationInfo& location : frame.inlineLocations) {
if (location.empty) {
break;
if (!frame.name || frame.name[0] == '\0') {
doPrint(" (unknown)");
} else {
char demangledBuf[2048];
demangle(frame.name, demangledBuf, sizeof(demangledBuf));
doPrint(" ");
doPrint(demangledBuf[0] == '\0' ? frame.name : demangledBuf);
}
if (!(options_ & NO_FILE_AND_LINE)) {
color(kFileColor);
char fileBuf[PATH_MAX];
fileBuf[0] = '\0';
if (frame.location.hasFileAndLine) {
frame.location.file.toBuffer(fileBuf, sizeof(fileBuf));
doPrint("\n");
doPrint(pad);
doPrint(fileBuf);
char buf[22];
uint32_t n = uint64ToBufferUnsafe(frame.location.line, buf);
doPrint(":");
doPrint(StringPiece(buf, n));
}
if (frame.location.hasMainFile) {
char mainFileBuf[PATH_MAX];
mainFileBuf[0] = '\0';
frame.location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
if (!frame.location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
doPrint("\n");
doPrint(pad);
doPrint("-> ");
doPrint(mainFileBuf);
}
}
doPrint("\n");
printLocationInfo(location, true);
}
}
......@@ -286,60 +312,6 @@ void SymbolizePrinter::println(
}
}
void SymbolizePrinter::printLocationInfo(
const Dwarf::LocationInfo& location,
bool isInline) {
const char padBuf[] = " ";
folly::StringPiece pad(
padBuf, sizeof(padBuf) - 1 - (16 - 2 * sizeof(uintptr_t)));
// Inline function has no address in stack trace.
if (isInline) {
doPrint(pad);
}
color(kFunctionColor);
if (location.name.empty()) {
doPrint(" (unknown)");
} else if (isInline) {
doPrint(location.name);
} else {
char demangledBuf[2048];
demangle(location.name.data(), demangledBuf, sizeof(demangledBuf));
doPrint(" ");
doPrint(demangledBuf[0] == '\0' ? location.name : demangledBuf);
}
if (!(options_ & NO_FILE_AND_LINE)) {
color(kFileColor);
char fileBuf[PATH_MAX];
fileBuf[0] = '\0';
if (location.hasFileAndLine) {
location.file.toBuffer(fileBuf, sizeof(fileBuf));
doPrint("\n");
doPrint(pad);
doPrint(fileBuf);
char buf[22];
uint32_t n = uint64ToBufferUnsafe(location.line, buf);
doPrint(":");
doPrint(StringPiece(buf, n));
}
if (location.hasMainFile) {
char mainFileBuf[PATH_MAX];
mainFileBuf[0] = '\0';
location.mainFile.toBuffer(mainFileBuf, sizeof(mainFileBuf));
if (!location.hasFileAndLine || strcmp(fileBuf, mainFileBuf)) {
doPrint("\n");
doPrint(pad);
doPrint("-> ");
doPrint(mainFileBuf);
}
}
}
}
namespace {
int getFD(const std::ios& stream) {
......
......@@ -56,12 +56,6 @@ struct SymbolizedFrame {
bool found = false;
const char* name = nullptr;
Dwarf::LocationInfo location;
// If stack trace happens on an inline function call, more than one locations
// need to be printed. First element is always the location of caller
// function, and the subsequent locations are inline function locations if
// applicable. std::vector is not used here in favor of signal-safe and memory
// constrained situations like std::bad_alloc.
Dwarf::LocationInfo inlineLocations[Dwarf::kMaxLocationInfoPerFrame];
/**
* Demangle the name and return it. Not async-signal-safe; allocates memory.
......@@ -259,9 +253,6 @@ class SymbolizePrinter {
private:
void printTerse(uintptr_t address, const SymbolizedFrame& frame);
void printLocationInfo(
const Dwarf::LocationInfo& locationInfo,
bool isInline);
virtual void doPrint(StringPiece sp) = 0;
static constexpr std::array<const char*, Color::NUM> kColorMap = {{
......
......@@ -33,9 +33,7 @@ void run(Dwarf::LocationInfoMode mode, size_t n) {
suspender.dismiss();
for (size_t i = 0; i < n; i++) {
Dwarf::LocationInfo info;
auto inlineInfo =
std::array<Dwarf::LocationInfo, Dwarf::kMaxLocationInfoPerFrame>();
dwarf.findAddress(uintptr_t(&dummy), mode, info, inlineInfo.data());
dwarf.findAddress(uintptr_t(&dummy), info, mode);
}
}
......@@ -49,10 +47,6 @@ BENCHMARK(DwarfFindAddressFull, n) {
run(folly::symbolizer::Dwarf::LocationInfoMode::FULL, n);
}
BENCHMARK(DwarfFindAddressFullWithInline, n) {
run(folly::symbolizer::Dwarf::LocationInfoMode::FULL_WITH_INLINE, n);
}
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
......
......@@ -14,7 +14,6 @@
* limitations under the License.
*/
#include <folly/experimental/symbolizer/test/SymbolizerTest.h>
#include <folly/experimental/symbolizer/Symbolizer.h>
#include <cstdlib>
......@@ -47,6 +46,16 @@ TEST(Symbolizer, Single) {
EXPECT_EQ("SymbolizerTest.cpp", basename.str());
}
FrameArray<100>* framesToFill{nullptr};
int comparator(const void* ap, const void* bp) {
getStackTrace(*framesToFill);
int a = *static_cast<const int*>(ap);
int b = *static_cast<const int*>(bp);
return a < b ? -1 : a > b ? 1 : 0;
}
// Test stack frames...
FOLLY_NOINLINE void bar();
......@@ -118,78 +127,6 @@ TEST(SymbolizerTest, SymbolCache) {
}
}
namespace {
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<100>& frames) {
inlineFoo(frames);
}
} // namespace
TEST(SymbolizerTest, InlineFunctionBasic) {
Symbolizer symbolizer(
nullptr, Dwarf::LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<100> frames;
inlineBar(frames);
symbolizer.symbolize(frames);
StringSymbolizePrinter printer;
// Stack trace for SymbolizerTest_InlineFunctionBasic_Test function call.
printer.print(4, frames.frames[4]);
EXPECT_STREQ(
printer.str().c_str(),
R"( @ 0000000000000004 folly::symbolizer::test::SymbolizerTest_InlineFunctionBasic_Test::TestBody()
./folly/experimental/symbolizer/test/SymbolizerTest.h:134
-> ./folly/experimental/symbolizer/test/SymbolizerTest.cpp
inlineBar
folly/experimental/symbolizer/test/SymbolizerTest.cpp:124
inlineFoo
folly/experimental/symbolizer/test/SymbolizerTest.h:39)");
}
TEST(SymbolizerTest, InlineFunctionInSeparateFile) {
Symbolizer symbolizer(
nullptr, Dwarf::LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<100> frames;
inlineFunctionInSeparateFile(frames);
symbolizer.symbolize(frames);
StringSymbolizePrinter printer;
// Stack trace for SymbolizerTest_InlineFunctionInSeparateFile_Test function
// call.
printer.print(4, frames.frames[4]);
EXPECT_STREQ(
printer.str().c_str(),
R"( @ 0000000000000004 folly::symbolizer::test::SymbolizerTest_InlineFunctionInSeparateFile_Test::TestBody()
./folly/experimental/symbolizer/test/SymbolizerTest.h:155
-> ./folly/experimental/symbolizer/test/SymbolizerTest.cpp
inlineFunctionInSeparateFile
folly/experimental/symbolizer/test/SymbolizerTest.h:44
inlineFoo
folly/experimental/symbolizer/test/SymbolizerTest.h:39)");
}
// Not supported yet.
TEST(SymbolizerTest, InlineFunctionInClass) {
Symbolizer symbolizer(
nullptr, Dwarf::LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<100> frames;
ClassWithInlineFunction cif;
cif.inlineFunctionInClass(frames);
symbolizer.symbolize(frames);
StringSymbolizePrinter printer;
// Stack trace for SymbolizerTest_InlineFunctionInClass_Test function call.
printer.print(4, frames.frames[4]);
EXPECT_STREQ(
printer.str().c_str(),
R"( @ 0000000000000004 folly::symbolizer::test::SymbolizerTest_InlineFunctionInClass_Test::TestBody()
./folly/experimental/symbolizer/test/SymbolizerTest.h:39
-> ./folly/experimental/symbolizer/test/SymbolizerTest.cpp)");
}
} // namespace test
} // namespace symbolizer
} // namespace folly
......
/*
* Copyright 2019-present 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.
*/
#pragma once
#include <folly/container/Array.h>
#include <folly/experimental/symbolizer/Symbolizer.h>
namespace folly {
namespace symbolizer {
namespace test {
FrameArray<100>* framesToFill{nullptr};
int comparator(const void* ap, const void* bp) {
getStackTrace(*framesToFill);
int a = *static_cast<const int*>(ap);
int b = *static_cast<const int*>(bp);
return a < b ? -1 : a > b ? 1 : 0;
}
FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<100>& frames) {
framesToFill = &frames;
int a[2] = {1, 2};
// Use qsort, which is in a different library
qsort(a, 2, sizeof(int), comparator);
framesToFill = nullptr;
}
FOLLY_ALWAYS_INLINE void inlineFunctionInSeparateFile(FrameArray<100>& frames) {
inlineFoo(frames);
}
class ClassWithInlineFunction {
public:
FOLLY_ALWAYS_INLINE void inlineFunctionInClass(FrameArray<100>& frames) {
inlineFoo(frames);
}
};
} // namespace test
} // namespace symbolizer
} // namespace folly
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