Commit 2d2c15fc authored by Matt Ma's avatar Matt Ma Committed by Facebook GitHub Bot

Fix and refactor inline functions stack trace.

Summary:
Fix the issue that there may be other tags like DW_TAG_lexical_block between DW_TAG_subprogram and DW_TAG_inlined_subroutine.

Also increase the maximum inline location info per frame since previous value 3 is too small.

Refactors the code to set file and line for inline functions.

Reviewed By: luciang

Differential Revision: D21250080

fbshipit-source-id: f52dc74ee5c144aa26723546b48486f8a5250f19
parent 8228e607
......@@ -19,6 +19,7 @@
#include <array>
#include <dwarf.h>
#include <folly/Optional.h>
#include <type_traits>
namespace folly {
......@@ -76,10 +77,11 @@ struct Attribute {
boost::variant<uint64_t, folly::StringPiece> attrValue;
};
struct CodeLocation {
Die die;
folly::Optional<uint64_t> file;
folly::Optional<uint64_t> line;
// Indicates inline funtion `name` is called at `line@file`.
struct CallLocation {
Path file = {};
uint64_t line;
folly::StringPiece name;
};
} // namespace detail
......@@ -468,18 +470,15 @@ bool Dwarf::findLocation(
// Offset in .debug_line for the line number VM program for this
// compilation unit
bool foundLineOffset = false;
uint64_t lineOffset = 0;
folly::Optional<uint64_t> lineOffset;
folly::StringPiece compilationDirectory;
folly::StringPiece mainFileName;
folly::Optional<folly::StringPiece> mainFileName;
forEachAttribute(cu, die, [&](const detail::Attribute& attr) {
switch (attr.spec.name) {
case DW_AT_stmt_list:
// Offset in .debug_line for the line number VM program for this
// compilation unit
lineOffset = boost::get<uint64_t>(attr.attrValue);
foundLineOffset = true;
break;
case DW_AT_comp_dir:
// Compilation directory
......@@ -494,17 +493,17 @@ bool Dwarf::findLocation(
return true;
});
if (!mainFileName.empty()) {
if (mainFileName) {
locationInfo.hasMainFile = true;
locationInfo.mainFile = Path(compilationDirectory, "", mainFileName);
locationInfo.mainFile = Path(compilationDirectory, "", *mainFileName);
}
if (!foundLineOffset) {
if (!lineOffset) {
return false;
}
folly::StringPiece lineSection(debugLine_);
lineSection.advance(lineOffset);
lineSection.advance(*lineOffset);
LineNumberVM lineVM(lineSection, compilationDirectory);
// Execute line number VM program to find file and line
......@@ -518,75 +517,85 @@ bool Dwarf::findLocation(
std::array<detail::DIEAbbreviation, kMaxAbbreviationEntries> abbrs;
cu.abbrCache = folly::range(abbrs);
readCompilationUnitAbbrs(debugAbbrev_, cu);
// Find the subprogram that matches the given address.
detail::Die subprogram;
findSubProgramDieForAddress(cu, die, address, subprogram);
// Subprogram is the DIE of caller function.
if (subprogram.abbr.hasChildren) {
size_t size = std::min<size_t>(
Dwarf::kMaxInlineLocationInfoPerFrame, inlineFrames.size());
detail::CodeLocation inlineLocs[size];
auto inlineLocsRange =
folly::Range<detail::CodeLocation*>(inlineLocs, size);
// Use an extra location and get its call file and call line, so that
// they can be used for the second last location when we don't have
// enough inline frames for all inline functions call stack.
size_t size =
std::min<size_t>(
Dwarf::kMaxInlineLocationInfoPerFrame, inlineFrames.size()) +
1;
detail::CallLocation callLocations[size];
size_t numFound = 0;
findInlinedSubroutineDieForAddress(
cu, subprogram, address, inlineLocsRange);
size_t numFound = inlineLocsRange.data() - inlineLocs;
// The line in locationInfo is the line in the deepest inline functions,
// and the lines in inlineLocationInfo are the line of callers in call
// sequence. A shuffle is needed to match lines with their function name.
uint64_t callerLine = locationInfo.line;
inlineLocsRange =
folly::Range<detail::CodeLocation*>(inlineLocs, numFound);
// Put inline frames from inside to outside (callee is before caller).
std::reverse(inlineLocsRange.begin(), inlineLocsRange.end());
auto inlineFrameIter = inlineFrames.begin();
for (auto inlineLocIter = inlineLocsRange.begin();
inlineLocIter != inlineLocsRange.end();
inlineLocIter++, inlineFrameIter++) {
inlineFrameIter->location.line = callerLine;
if (!inlineLocIter->line) {
break;
cu,
subprogram,
lineVM,
address,
folly::Range<detail::CallLocation*>(callLocations, size),
numFound);
if (numFound > 0) {
folly::Range<detail::CallLocation*> inlineLocations(
callLocations, numFound);
const auto innerMostFile = locationInfo.file;
const auto innerMostLine = locationInfo.line;
// Earlier we filled in locationInfo:
// - mainFile: the path to the CU -- the file where the non-inlined
// call is made from.
// - file + line: the location of the inner-most inlined call.
// Here we already find inlined info so mainFile would be redundant.
locationInfo.hasMainFile = false;
locationInfo.mainFile = Path{};
// @findInlinedSubroutineDieForAddress fills inlineLocations[0] with the
// file+line of the non-inlined outer function making the call.
// locationInfo.name is already set by the caller by looking up the
// non-inlined function @address belongs to.
locationInfo.hasFileAndLine = true;
locationInfo.file = inlineLocations[0].file;
locationInfo.line = inlineLocations[0].line;
// The next inlined subroutine's call file and call line is the current
// caller's location.
for (size_t i = 0; i < numFound - 1; i++) {
inlineLocations[i].file = inlineLocations[i + 1].file;
inlineLocations[i].line = inlineLocations[i + 1].line;
}
callerLine = inlineLocIter->line.value();
folly::Optional<folly::StringPiece> linkageName;
folly::Optional<folly::StringPiece> name;
forEachAttribute(
cu, inlineLocIter->die, [&](const detail::Attribute& attr) {
switch (attr.spec.name) {
case DW_AT_linkage_name:
linkageName = boost::get<folly::StringPiece>(attr.attrValue);
break;
case DW_AT_name:
name = boost::get<folly::StringPiece>(attr.attrValue);
break;
case DW_AT_decl_file:
// If the inline function is declared and defined in the same
// file, the declaration subprogram may not have attribute
// DW_AT_decl_file, then we need to find decl_file in the
// definition subprogram.
if (!inlineLocIter->file) {
inlineLocIter->file = boost::get<uint64_t>(attr.attrValue);
}
break;
}
return !inlineLocIter->file || !name || !linkageName;
});
if (!(name || linkageName) || !inlineLocIter->file) {
break;
// CallLocation for the inner-most inlined function:
// - will be computed if enough space was available in the passed
// buffer.
// - will have a .name, but no !.file && !.line
// - its corresponding file+line is the one returned by LineVM based
// on @address.
// Use the inner-most inlined file+line info we got from the LineVM.
inlineLocations[numFound - 1].file = innerMostFile;
inlineLocations[numFound - 1].line = innerMostLine;
// Skip the extra location when actual inline function calls are more
// than provided frames.
inlineLocations = inlineLocations.subpiece(
0, std::min(numFound, inlineFrames.size()));
// Fill in inline frames in reverse order (as
// expected by the caller).
std::reverse(inlineLocations.begin(), inlineLocations.end());
for (size_t i = 0; i < inlineLocations.size(); i++) {
inlineFrames[i].found = true;
inlineFrames[i].addr = address;
inlineFrames[i].name = inlineLocations[i].name.data();
inlineFrames[i].location.hasFileAndLine = true;
inlineFrames[i].location.file = inlineLocations[i].file;
inlineFrames[i].location.line = inlineLocations[i].line;
}
inlineFrameIter->found = true;
inlineFrameIter->addr = address;
inlineFrameIter->name =
linkageName ? linkageName.value().data() : name.value().data();
inlineFrameIter->location.hasFileAndLine = true;
inlineFrameIter->location.name =
linkageName ? linkageName.value() : name.value();
inlineFrameIter->location.file =
lineVM.getFullFileName(inlineLocIter->file.value());
}
locationInfo.line = callerLine;
}
}
......@@ -820,23 +829,34 @@ void Dwarf::findSubProgramDieForAddress(
});
}
/**
* Find DW_TAG_inlined_subroutine child DIEs that contain @address and
* then extract:
* - Where was it called from (DW_AT_call_file & DW_AT_call_line):
* the statement or expression that caused the inline expansion.
* - The inlined function's name. As a function may be inlined multiple
* times, common attributes like DW_AT_linkage_name or DW_AT_name
* are only stored in its "concrete out-of-line instance" (a
* DW_TAG_subprogram) which we find using DW_AT_abstract_origin.
*/
void Dwarf::findInlinedSubroutineDieForAddress(
const detail::CompilationUnit& cu,
const detail::Die& die,
const LineNumberVM& lineVM,
uint64_t address,
folly::Range<detail::CodeLocation*>& isrLoc) const {
if (isrLoc.empty()) {
folly::Range<detail::CallLocation*> locations,
size_t& numFound) const {
if (numFound >= locations.size()) {
return;
}
forEachChild(cu, die, [&](const detail::Die& childDie) {
if (childDie.abbr.tag != DW_TAG_inlined_subroutine) {
return true; // Skip DIE that is not inlined_subroutine.
}
folly::Optional<uint64_t> lowPc;
folly::Optional<uint64_t> highPc;
folly::Optional<bool> isHighPcAddr;
folly::Optional<uint64_t> origin;
folly::Optional<uint64_t> originRefType;
folly::Optional<uint64_t> abstractOrigin;
folly::Optional<uint64_t> abstractOriginRefType;
folly::Optional<uint64_t> callFile;
folly::Optional<uint64_t> callLine;
folly::Optional<uint64_t> rangeOffset;
forEachAttribute(cu, childDie, [&](const detail::Attribute& attr) {
......@@ -854,59 +874,115 @@ void Dwarf::findInlinedSubroutineDieForAddress(
highPc = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_abstract_origin:
originRefType = attr.spec.form;
origin = boost::get<uint64_t>(attr.attrValue);
abstractOriginRefType = attr.spec.form;
abstractOrigin = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_call_line:
callLine = boost::get<uint64_t>(attr.attrValue);
break;
case DW_AT_call_file:
callFile = boost::get<uint64_t>(attr.attrValue);
break;
}
// Iterate through all until find all above attributes.
return true;
});
if (!origin || !originRefType || !callLine) {
// Missing required fields. Keep searching other children.
return true;
}
if ((!lowPc || !highPc || !isHighPcAddr || address < *lowPc ||
address >= (*isHighPcAddr ? *highPc : *lowPc + *highPc)) &&
(!rangeOffset ||
!isAddrInRangeList(address, rangeOffset.value(), cu.addrSize))) {
// 2.17 Code Addresses and Ranges
// Any debugging information entry describing an entity that has a
// machine code address or range of machine code addresses,
// which includes compilation units, module initialization, subroutines,
// ordinary blocks, try/catch blocks, labels and the like, may have
// - A DW_AT_low_pc attribute for a single address,
// - A DW_AT_low_pc and DW_AT_high_pc pair of attributes for a
// single contiguous range of addresses, or
// - A DW_AT_ranges attribute for a non-contiguous range of addresses.
// TODO: Support DW_TAG_entry_point and DW_TAG_common_block that don't
// have DW_AT_low_pc/DW_AT_high_pc pairs and DW_AT_ranges.
bool pcMatch = lowPc && highPc && isHighPcAddr && address >= *lowPc &&
(address < (*isHighPcAddr ? *highPc : *lowPc + *highPc));
bool rangeMatch = rangeOffset &&
isAddrInRangeList(address, rangeOffset.value(), cu.addrSize);
if (!pcMatch && !rangeMatch) {
// Address doesn't match. Keep searching other children.
return true;
}
isrLoc[0].line = *callLine;
auto setLocationInfo = [&](const detail::CompilationUnit& srcu,
// Between a DW_TAG_subprogram and and DW_TAG_inlined_subroutine we might
// have arbitrary intermediary "nodes", including DW_TAG_common_block,
// DW_TAG_lexical_block, DW_TAG_try_block, DW_TAG_catch_block and
// DW_TAG_with_stmt, etc.
if (childDie.abbr.tag == DW_TAG_try_block ||
childDie.abbr.tag == DW_TAG_catch_block ||
childDie.abbr.tag == DW_TAG_entry_point ||
childDie.abbr.tag == DW_TAG_common_block ||
childDie.abbr.tag == DW_TAG_lexical_block) {
findInlinedSubroutineDieForAddress(
cu, childDie, lineVM, address, locations, numFound);
// We expect a single sibling DIE to match on addr. Stop searching for
// other DIEs.
return false;
}
if (!abstractOrigin || !abstractOriginRefType || !callLine || !callFile) {
// We expect a single sibling DIE to match on addr, but it's missing
// required fields. Stop searching for other DIEs.
return false;
}
locations[numFound].file = lineVM.getFullFileName(*callFile);
locations[numFound].line = *callLine;
auto getFunctionName = [&](const detail::CompilationUnit& srcu,
uint64_t dieOffset) {
auto declDie = getDieAtOffset(srcu, dieOffset);
// If the inline function is declared and defined in different file, then
// DW_AT_decl_file attribute of the declaration may contain the definition
// file name.
isrLoc[0].file = getAttribute<uint64_t>(srcu, declDie, DW_AT_decl_file);
// Jump to the actual function definition instead of declaration for name
// and line info.
isrLoc[0].die = findDefinitionDie(srcu, declDie);
isrLoc.advance(1);
auto defDie = findDefinitionDie(srcu, declDie);
folly::StringPiece name;
// The file and line will be set in the next inline subroutine based on
// its DW_AT_call_file and DW_AT_call_line.
forEachAttribute(srcu, defDie, [&](const detail::Attribute& attr) {
switch (attr.spec.name) {
case DW_AT_linkage_name:
name = boost::get<folly::StringPiece>(attr.attrValue);
break;
case DW_AT_name:
// NOTE: when DW_AT_linkage_name and DW_AT_name match, dwarf
// emitters omit DW_AT_linkage_name (to save space). If present
// DW_AT_linkage_name should always be preferred (mangled C++ name
// vs just the function name).
if (name.empty()) {
name = boost::get<folly::StringPiece>(attr.attrValue);
}
break;
}
return true;
});
return name;
};
if (*originRefType == DW_FORM_ref1 || *originRefType == DW_FORM_ref2 ||
*originRefType == DW_FORM_ref4 || *originRefType == DW_FORM_ref8 ||
*originRefType == DW_FORM_ref_udata) {
// These reference types identify any debugging information entry within
// the containing unit. This type of reference is an offset from the first
// byte of the compilation header for the compilation unit containing the
// reference.
setLocationInfo(cu, cu.offset + *origin);
findInlinedSubroutineDieForAddress(cu, childDie, address, isrLoc);
} else if (*originRefType == DW_FORM_ref_addr) {
// DW_FORM_ref_addr identifies any debugging information entry
// within a .debug_info section; in particular, it may refer to an entry
// in a different compilation unit from the unit containing the reference,
// and may refer to an entry in a different shared object.
auto srcu = findCompilationUnit(debugInfo_, *origin);
setLocationInfo(srcu, *origin);
findInlinedSubroutineDieForAddress(srcu, childDie, address, isrLoc);
}
// DW_AT_abstract_origin is a reference. There a 3 types of references:
// - the reference can identify any debugging information entry within the
// compilation unit (DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4,
// DW_FORM_ref8, DW_FORM_ref_udata). This type of reference is an offset
// from the first byte of the compilation header for the compilation unit
// containing the reference.
// - the reference can identify any debugging information entry within a
// .debug_info section; in particular, it may refer to an entry in a
// different compilation unit (DW_FORM_ref_addr)
// - the reference can identify any debugging information type entry that
// has been placed in its own type unit.
// Not applicable for DW_AT_abstract_origin.
locations[numFound].name = (*abstractOriginRefType != DW_FORM_ref_addr)
? getFunctionName(cu, cu.offset + *abstractOrigin)
: getFunctionName(
findCompilationUnit(debugInfo_, *abstractOrigin),
*abstractOrigin);
findInlinedSubroutineDieForAddress(
cu, childDie, lineVM, address, locations, ++numFound);
return false;
});
......
......@@ -44,7 +44,7 @@ struct DIEAbbreviation;
struct AttributeSpec;
struct Attribute;
struct CodeLocation;
struct CallLocation;
} // namespace detail
......@@ -85,7 +85,7 @@ class Dwarf {
* More than one location info may exist if current frame is an inline
* function call.
*/
static const uint32_t kMaxInlineLocationInfoPerFrame = 3;
static const uint32_t kMaxInlineLocationInfoPerFrame = 10;
/** Find the file and line number information corresponding to address. */
bool findAddress(
......@@ -136,8 +136,10 @@ class Dwarf {
void findInlinedSubroutineDieForAddress(
const detail::CompilationUnit& cu,
const detail::Die& die,
const LineNumberVM& lineVM,
uint64_t address,
folly::Range<detail::CodeLocation*>& isrLoc) const;
folly::Range<detail::CallLocation*> locations,
size_t& numFound) const;
static bool
findDebugInfoOffset(uintptr_t address, StringPiece aranges, uint64_t& offset);
......
......@@ -18,27 +18,40 @@
#include <folly/Range.h>
#include <folly/experimental/symbolizer/Dwarf.h>
#include <folly/experimental/symbolizer/SymbolizedFrame.h>
#include <folly/experimental/symbolizer/test/SymbolizerTestUtils.h>
#include <folly/portability/GFlags.h>
void dummy() {}
namespace {
using namespace folly::symbolizer;
using namespace folly::symbolizer::test;
template <size_t kNumFrames>
FOLLY_NOINLINE void lexicalBlockBar(FrameArray<kNumFrames>& frames) try {
size_t unused = 0;
unused++;
inlineBar(frames);
} catch (...) {
folly::assume_unreachable();
}
void run(LocationInfoMode mode, size_t n) {
folly::BenchmarkSuspender suspender;
// NOTE: Using '/proc/self/exe' only works if the code for @dummy is
// statically linked into the binary.
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames;
lexicalBlockBar<100>(frames);
symbolizer.symbolize(frames);
// The address of the line where lexicalBlockBar calls inlineBar.
uintptr_t address = frames.frames[7].addr;
ElfFile elf("/proc/self/exe");
Dwarf dwarf(&elf);
auto inlineFrames = std::array<SymbolizedFrame, 10>();
suspender.dismiss();
for (size_t i = 0; i < n; i++) {
LocationInfo info;
auto inlineFrames =
std::array<SymbolizedFrame, Dwarf::kMaxInlineLocationInfoPerFrame>();
dwarf.findAddress(
uintptr_t(&dummy), mode, info, folly::range(inlineFrames));
dwarf.findAddress(address, mode, info, folly::range(inlineFrames));
}
}
......
......@@ -123,10 +123,14 @@ TEST(SymbolizerTest, SymbolCache) {
namespace {
template <size_t kNumFrames = 100>
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<kNumFrames>& frames) {
kFooCallByStandaloneBarLineNo = __LINE__ + 1;
inlineFoo(frames);
template <size_t kNumFrames>
FOLLY_NOINLINE void lexicalBlockBar(FrameArray<kNumFrames>& frames) try {
size_t unused = 0;
unused++;
kInlineBarCallByLexicalBarLineNo = __LINE__ + 1;
inlineBar(frames);
} catch (...) {
folly::assume_unreachable();
}
void verifyStackTrace(
......@@ -139,7 +143,7 @@ void verifyStackTrace(
"folly::symbolizer::FrameArray<100ul>&)",
std::string(folly::demangle(frames.frames[5].name)));
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
"./folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
std::string(frames.frames[5].location.file.toString()));
EXPECT_EQ(kQsortCallLineNo, frames.frames[5].location.line);
EXPECT_EQ(barName, std::string(folly::demangle(frames.frames[6].name)));
......@@ -205,10 +209,10 @@ TEST(SymbolizerTest, InlineFunctionBasic) {
// clang-format on
verifyStackTrace(
frames,
"void folly::symbolizer::test::(anonymous namespace)::inlineBar<100ul>("
"void folly::symbolizer::test::inlineBar<100ul>("
"folly::symbolizer::FrameArray<100ul>&)",
kFooCallByStandaloneBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTest.cpp");
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
FrameArray<100> frames2;
inlineBar<100>(frames2);
......@@ -217,6 +221,80 @@ TEST(SymbolizerTest, InlineFunctionBasic) {
compareFrames(frames, frames2);
}
TEST(SymbolizerTest, InlineFunctionWithoutEnoughFrames) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames;
lexicalBlockBar<100>(frames);
symbolizer.symbolize(frames);
// The address of the line where lexicalBlockBar calls inlineBar.
uintptr_t address = frames.frames[7].addr;
std::array<SymbolizedFrame, 2> limitedFrames;
symbolizer.symbolize(
folly::Range<const uintptr_t*>(&address, 1), folly::range(limitedFrames));
EXPECT_EQ(
"void folly::symbolizer::test::inlineBar<100ul>("
"folly::symbolizer::FrameArray<100ul>&)",
std::string(folly::demangle(limitedFrames[0].name)));
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
std::string(limitedFrames[0].location.file.toString()));
EXPECT_EQ(kFooCallByStandaloneBarLineNo, limitedFrames[0].location.line);
EXPECT_EQ(
"void folly::symbolizer::test::(anonymous namespace)::lexicalBlockBar"
"<100ul>(folly::symbolizer::FrameArray<100ul>&)",
std::string(folly::demangle(limitedFrames[1].name)));
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTest.cpp",
std::string(limitedFrames[1].location.file.toString()));
EXPECT_EQ(kInlineBarCallByLexicalBarLineNo, limitedFrames[1].location.line);
}
TEST(SymbolizerTest, InlineFunctionInLexicalBlock) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames;
lexicalBlockBar<100>(frames);
symbolizer.symbolize(frames);
verifyStackTrace(
frames,
"void folly::symbolizer::test::inlineBar<100ul>("
"folly::symbolizer::FrameArray<100ul>&)",
kFooCallByStandaloneBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
EXPECT_EQ(
"void folly::symbolizer::test::(anonymous namespace)::lexicalBlockBar"
"<100ul>(folly::symbolizer::FrameArray<100ul>&)",
std::string(folly::demangle(frames.frames[7].name)));
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTest.cpp",
std::string(frames.frames[7].location.file.toString()));
EXPECT_EQ(kInlineBarCallByLexicalBarLineNo, frames.frames[7].location.line);
}
TEST(SymbolizerTest, InlineFunctionInDifferentCompilationUnit) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames;
// NOTE: inlineBaz is only inlined with thinlto compilation mode enabled.
inlineBaz(frames);
symbolizer.symbolize(frames);
EXPECT_EQ(
"folly::symbolizer::test::inlineBaz("
"folly::symbolizer::FrameArray<100ul>&)",
std::string(folly::demangle(frames.frames[6].name)));
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTestUtils.cpp",
std::string(frames.frames[6].location.file.toString()));
EXPECT_EQ(kFooCallByStandaloneBazLineNo, frames.frames[6].location.line);
}
TEST(SymbolizerTest, InlineClassMemberFunction) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
......@@ -303,10 +381,10 @@ TEST(SymbolizerTest, InlineFunctionWithCache) {
verifyStackTrace(
frames,
"void folly::symbolizer::test::(anonymous namespace)::inlineBar<100ul>("
"void folly::symbolizer::test::inlineBar<100ul>("
"folly::symbolizer::FrameArray<100ul>&)",
kFooCallByStandaloneBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTest.cpp");
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
FrameArray<100> frames2;
inlineBar<100>(frames2);
......
......@@ -35,6 +35,12 @@ FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<kNumFrames>& frames) {
framesToFill = nullptr;
}
template <size_t kNumFrames>
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<kNumFrames>& frames) {
kFooCallByStandaloneBarLineNo = __LINE__ + 1;
inlineFoo(frames);
}
FOLLY_ALWAYS_INLINE void InlineFunctionsWrapper::inlineBar(
FrameArray<100>& frames) const {
kFooCallByClassInDifferentFileBarLineNo = __LINE__ + 1;
......
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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/experimental/symbolizer/test/SymbolizerTestUtils.h>
namespace folly {
namespace symbolizer {
namespace test {
void* framesToFill = {nullptr};
size_t kQsortCallLineNo = 0;
size_t kFooCallByStandaloneBarLineNo = 0;
size_t kFooCallByStandaloneBazLineNo = 0;
size_t kFooCallByClassBarLineNo = 0;
size_t kFooCallByClassStaticBarLineNo = 0;
size_t kFooCallByClassInDifferentFileBarLineNo = 0;
size_t kFooCallByClassInDifferentFileStaticBarLineNo = 0;
size_t kInlineBarCallByLexicalBarLineNo = 0;
size_t kInlineBazCallByLexicalBazLineNo = 0;
void inlineBaz(FrameArray<100>& frames) {
kFooCallByStandaloneBazLineNo = __LINE__ + 1;
inlineFoo(frames);
}
} // namespace test
} // namespace symbolizer
} // namespace folly
......@@ -23,7 +23,7 @@ namespace folly {
namespace symbolizer {
namespace test {
void* framesToFill{nullptr};
extern void* framesToFill;
template <size_t kNumFrames = 100>
int comparator(const void* ap, const void* bp) {
......@@ -34,16 +34,23 @@ int comparator(const void* ap, const void* bp) {
return a < b ? -1 : a > b ? 1 : 0;
}
size_t kQsortCallLineNo = 0;
size_t kFooCallByStandaloneBarLineNo = 0;
size_t kFooCallByClassBarLineNo = 0;
size_t kFooCallByClassStaticBarLineNo = 0;
size_t kFooCallByClassInDifferentFileBarLineNo = 0;
size_t kFooCallByClassInDifferentFileStaticBarLineNo = 0;
extern size_t kQsortCallLineNo;
extern size_t kFooCallByStandaloneBarLineNo;
extern size_t kFooCallByStandaloneBazLineNo;
extern size_t kFooCallByClassBarLineNo;
extern size_t kFooCallByClassStaticBarLineNo;
extern size_t kFooCallByClassInDifferentFileBarLineNo;
extern size_t kFooCallByClassInDifferentFileStaticBarLineNo;
extern size_t kInlineBarCallByLexicalBarLineNo;
template <size_t kNumFrames = 100>
FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<kNumFrames>& frames);
template <size_t kNumFrames = 100>
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<kNumFrames>& frames);
extern void inlineBaz(FrameArray<100>& frames);
class InlineFunctionsWrapper {
public:
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<100>& frames) const;
......
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