Commit 8f2210a5 authored by Lucian Grijincu's avatar Lucian Grijincu Committed by Facebook GitHub Bot

folly::symbolizer: drop templated FrameArray from test functions and move test...

folly::symbolizer: drop templated FrameArray from test functions and move test functions to separate compilation unit with minimal dependencies

Summary:
Eliminate `#include` dependencies of `SymbolizerTestUtils.cpp` so that the `.o` file with interesting debug info is tiny and easily inspectable.
- Instead of calling test functions that need to be inlined from the `SymbolizerTest.o` use `call_` trampolines so that the inlined debug info is generated in the minimal/tiny `SymbolizerTestUtils.o`.

 ---

Remove the frames template argument so that:
- removed dependencies from the SymbolizerTestUtils to minimize generated debug info (no longer need to include headers about FrameArray).
- all functions used in tests can be made regular functions
- all functions who's debug info is interesting to debug can be easily emitted in a single object file `SymbolizerTestUtils.o`

Reduce some of the copy-pasta that made test noisy by grouping together function name, file and lineno.

 ---

Test for either fullName/shortName (`DW_AT_linkage_name`/`DW_AT_name`)
- fullName: demangled function name with namespace, qualifiers, argument types, etc. and
- shortName: simple function name to support both cases when either both `DW_AT_linkage_name` and `DW_AT_name` or a single one of them is emitted.
- When `DW_AT_linkage_name` (full mangled name) and `DW_AT_name` (just the function name) match, DWARF emitters omit `DW_AT_linkage_name` (to save space). With `-fsplit-dwarf-inlining` only `DW_AT_name` is emitted in `.debug_info` and the `DW_AT_linkage_name` is emitted in the `.debug_info.dwo` sections.

Differential Revision: D25712808

fbshipit-source-id: 88d2ac22564e3d607fb3864d37e49892a5eaf002
parent 731276a4
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/experimental/symbolizer/Dwarf.h> #include <folly/experimental/symbolizer/Dwarf.h>
#include <folly/experimental/symbolizer/SymbolizedFrame.h> #include <folly/experimental/symbolizer/SymbolizedFrame.h>
#include <folly/experimental/symbolizer/Symbolizer.h>
#include <folly/experimental/symbolizer/test/SymbolizerTestUtils.h> #include <folly/experimental/symbolizer/test/SymbolizerTestUtils.h>
#include <folly/portability/GFlags.h> #include <folly/portability/GFlags.h>
...@@ -26,11 +27,10 @@ namespace { ...@@ -26,11 +27,10 @@ namespace {
using namespace folly::symbolizer; using namespace folly::symbolizer;
using namespace folly::symbolizer::test; using namespace folly::symbolizer::test;
template <size_t kNumFrames> FOLLY_NOINLINE void lexicalBlockBar() try {
FOLLY_NOINLINE void lexicalBlockBar(FrameArray<kNumFrames>& frames) try {
size_t unused = 0; size_t unused = 0;
unused++; unused++;
inlineBar(frames); inlineB_inlineA_qsort();
} catch (...) { } catch (...) {
folly::assume_unreachable(); folly::assume_unreachable();
} }
...@@ -39,9 +39,11 @@ void run(LocationInfoMode mode, size_t n) { ...@@ -39,9 +39,11 @@ void run(LocationInfoMode mode, size_t n) {
folly::BenchmarkSuspender suspender; folly::BenchmarkSuspender suspender;
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames; FrameArray<100> frames;
lexicalBlockBar<100>(frames); gComparatorGetStackTraceArg = &frames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
lexicalBlockBar();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
// The address of the line where lexicalBlockBar calls inlineBar. // The address of the line where lexicalBlockBar calls inlineB_inlineA_qsort.
uintptr_t address = frames.frames[7].addr; uintptr_t address = frames.frames[7].addr;
ElfFile elf("/proc/self/exe"); ElfFile elf("/proc/self/exe");
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <array> #include <array>
#include <cstdlib> #include <cstdlib>
#include <boost/filesystem.hpp>
#include <folly/Demangle.h> #include <folly/Demangle.h>
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/String.h> #include <folly/String.h>
...@@ -33,14 +34,14 @@ namespace folly { ...@@ -33,14 +34,14 @@ namespace folly {
namespace symbolizer { namespace symbolizer {
namespace test { namespace test {
namespace {
uintptr_t getBinaryOffset() {
return detail::get_r_debug()->r_map->l_addr;
}
} // namespace
void foo() {} void foo() {}
FOLLY_NOINLINE void bar() {
std::array<int, 2> a = {{1, 2}};
// Use qsort, which is in a different library
qsort(a.data(), 2, sizeof(int), testComparator);
}
TEST(Symbolizer, Single) { TEST(Symbolizer, Single) {
SKIP_IF(!Symbolizer::isAvailable()); SKIP_IF(!Symbolizer::isAvailable());
...@@ -62,15 +63,6 @@ TEST(Symbolizer, Single) { ...@@ -62,15 +63,6 @@ TEST(Symbolizer, Single) {
} }
// Test stack frames... // Test stack frames...
FOLLY_NOINLINE void bar();
void bar(FrameArray<100>& frames) {
framesToFill = &frames;
std::array<int, 2> a = {1, 2};
// Use qsort, which is in a different library
qsort(a.data(), 2, sizeof(int), comparator<100>);
framesToFill = nullptr;
}
class ElfCacheTest : public testing::Test { class ElfCacheTest : public testing::Test {
protected: protected:
...@@ -83,7 +75,10 @@ FrameArray<100> goldenFrames; ...@@ -83,7 +75,10 @@ FrameArray<100> goldenFrames;
void ElfCacheTest::SetUp() { void ElfCacheTest::SetUp() {
SKIP_IF(!Symbolizer::isAvailable()); SKIP_IF(!Symbolizer::isAvailable());
bar(goldenFrames); gComparatorGetStackTraceArg = &goldenFrames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
bar();
Symbolizer symbolizer; Symbolizer symbolizer;
symbolizer.symbolize(goldenFrames); symbolizer.symbolize(goldenFrames);
// At least 3 stack frames from us + getStackTrace() // At least 3 stack frames from us + getStackTrace()
...@@ -124,11 +119,15 @@ TEST(SymbolizerTest, SymbolCache) { ...@@ -124,11 +119,15 @@ TEST(SymbolizerTest, SymbolCache) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL, 100); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL, 100);
FrameArray<100> frames; FrameArray<100> frames;
bar(frames); gComparatorGetStackTraceArg = &frames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
bar();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
FrameArray<100> frames2; FrameArray<100> frames2;
bar(frames2); gComparatorGetStackTraceArg = &frames2;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
bar();
symbolizer.symbolize(frames2); symbolizer.symbolize(frames2);
for (size_t i = 0; i < frames.frameCount; i++) { for (size_t i = 0; i < frames.frameCount; i++) {
EXPECT_STREQ(frames.frames[i].name, frames2.frames[i].name); EXPECT_STREQ(frames.frames[i].name, frames2.frames[i].name);
...@@ -137,43 +136,27 @@ TEST(SymbolizerTest, SymbolCache) { ...@@ -137,43 +136,27 @@ TEST(SymbolizerTest, SymbolCache) {
namespace { namespace {
template <size_t kNumFrames> void expectFrameEq(
FOLLY_NOINLINE void lexicalBlockBar(FrameArray<kNumFrames>& frames) try { SymbolizedFrame frame,
size_t unused = 0; const std::string& shortName,
unused++; const std::string& fullName,
kInlineBarCallByLexicalBarLineNo = __LINE__ + 1; const std::string& file,
inlineBar(frames); size_t lineno) {
} catch (...) { auto normalizePath = [](std::string path) {
folly::assume_unreachable(); path = boost::filesystem::path(path).lexically_normal().native();
} return (path.find("./", 0) != std::string::npos) ? path.substr(2) : path;
};
void verifyStackTrace( auto demangled = folly::demangle(frame.name);
Symbolizer& symbolizer, EXPECT_TRUE(demangled == shortName || demangled == fullName)
const FrameArray<100>& frames, << "Found: demangled=" << demangled
const std::string& barName, << " expecting shortName=" << shortName << " or fullName=" << fullName;
size_t barLine, EXPECT_EQ(normalizePath(frame.location.file.toString()), normalizePath(file))
const std::string& barFile) { << ' ' << fullName;
EXPECT_EQ( EXPECT_EQ(frame.location.line, lineno) << ' ' << fullName;
"void folly::symbolizer::test::inlineFoo<100ul>("
"folly::symbolizer::FrameArray<100ul>&)",
std::string(folly::demangle(frames.frames[5].name)));
EXPECT_EQ(
"./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)));
EXPECT_EQ(barFile, std::string(frames.frames[6].location.file.toString()));
EXPECT_EQ(barLine, frames.frames[6].location.line);
FrameArray<10> singleAddressFrames;
singleAddressFrames.frameCount = 1;
singleAddressFrames.addresses[0] = frames.frames[7].addr + getBinaryOffset();
// Two inline function calls are added into frames.
EXPECT_EQ(3, symbolizer.symbolize(singleAddressFrames));
} }
template <size_t kNumFrames = 100> template <size_t kNumFrames = 100>
void compareFrames( void expectFramesEq(
const FrameArray<kNumFrames>& frames1, const FrameArray<kNumFrames>& frames1,
const FrameArray<kNumFrames>& frames2) { const FrameArray<kNumFrames>& frames2) {
EXPECT_EQ(frames1.frameCount, frames2.frameCount); EXPECT_EQ(frames1.frameCount, frames2.frameCount);
...@@ -184,63 +167,43 @@ void compareFrames( ...@@ -184,63 +167,43 @@ void compareFrames(
} // namespace } // namespace
class ClassWithInlineFunctions {
public:
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<100>& frames) const {
kFooCallByClassBarLineNo = __LINE__ + 1;
inlineFoo(frames);
}
FOLLY_ALWAYS_INLINE static void staticInlineBar(FrameArray<100>& frames) {
kFooCallByClassStaticBarLineNo = __LINE__ + 1;
inlineFoo(frames);
}
// Dummy non-inline function.
size_t dummy() const { return dummy_; }
size_t dummy_ = 0;
};
TEST(SymbolizerTest, InlineFunctionBasic) { TEST(SymbolizerTest, InlineFunctionBasic) {
SKIP_IF(!Symbolizer::isAvailable()); SKIP_IF(!Symbolizer::isAvailable());
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames; FrameArray<100> frames;
inlineBar<100>(frames); gComparatorGetStackTraceArg = &frames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_inlineB_inlineA_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
// clang-format off expectFrameEq(
// Expected full stack trace with @mode/dev. The last frame is missing in opt frames.frames[5],
// mode. "inlineA_qsort",
// Frame: _ZN5folly10symbolizer13getStackTraceEPmm "folly::symbolizer::test::inlineA_qsort()",
// Frame: _ZN5folly10symbolizer13getStackTraceILm100EEEbRNS0_10FrameArrayIXT_EEE "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
// Frame: _ZN5folly10symbolizer4test10comparatorILm100EEEiPKvS4_ kLineno_qsort);
// Frame: msort_with_tmp.part.0
// Frame: __GI___qsort_r expectFrameEq(
// Frame: _ZN5folly10symbolizer4test9inlineFooILm100EEEvRNS0_10FrameArrayIXT_EEE frames.frames[6],
// Frame: _ZN5folly10symbolizer4test12_GLOBAL__N_19inlineBarILm100EEEvRNS0_10FrameArrayIXT_EEE "inlineB_inlineA_qsort",
// Frame: _ZN5folly10symbolizer4test39SymbolizerTest_InlineFunctionBasic_Test8TestBodyEv "folly::symbolizer::test::inlineB_inlineA_qsort()",
// Frame: _ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS_4TestEvEET0_PT_MS4_FS3_vEPKc "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
// Frame: _ZN7testing4Test3RunEv kLineno_inlineA_qsort);
// Frame: _ZN7testing8TestInfo3RunEv
// Frame: _ZN7testing8TestCase3RunEv
// Frame: _ZN7testing8internal12UnitTestImpl11RunAllTestsEv
// clang-format on
verifyStackTrace(
symbolizer,
frames,
"void folly::symbolizer::test::inlineBar<100ul>("
"folly::symbolizer::FrameArray<100ul>&)",
kFooCallByStandaloneBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
FrameArray<100> frames2; FrameArray<100> frames2;
inlineBar<100>(frames2); gComparatorGetStackTraceArg = &frames2;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_inlineB_inlineA_qsort();
symbolizer.symbolize(frames2); symbolizer.symbolize(frames2);
compareFrames(frames, frames2); expectFramesEq(frames, frames2);
}
FOLLY_NOINLINE void call_B_A_qsort() {
kLineno_inlineB_inlineA_qsort = __LINE__ + 1;
inlineB_inlineA_qsort();
} }
TEST(SymbolizerTest, InlineFunctionWithoutEnoughFrames) { TEST(SymbolizerTest, InlineFunctionWithoutEnoughFrames) {
...@@ -249,32 +212,30 @@ TEST(SymbolizerTest, InlineFunctionWithoutEnoughFrames) { ...@@ -249,32 +212,30 @@ TEST(SymbolizerTest, InlineFunctionWithoutEnoughFrames) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames; FrameArray<100> frames;
lexicalBlockBar<100>(frames); gComparatorGetStackTraceArg = &frames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_B_A_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
// The address of the line where lexicalBlockBar calls inlineBar.
uintptr_t address = frames.frames[7].addr + getBinaryOffset();
std::array<SymbolizedFrame, 2> limitedFrames; std::array<SymbolizedFrame, 2> limitedFrames;
// The address of the line where call_B_A_qsort calls inlineB_inlineA_qsort.
symbolizer.symbolize( symbolizer.symbolize(
folly::Range<const uintptr_t*>(&address, 1), folly::range(limitedFrames)); folly::Range<const uintptr_t*>(&frames.addresses[5], 1),
folly::range(limitedFrames));
EXPECT_EQ( expectFrameEq(
"void folly::symbolizer::test::inlineBar<100ul>(" limitedFrames[0],
"folly::symbolizer::FrameArray<100ul>&)", "inlineB_inlineA_qsort",
std::string(folly::demangle(limitedFrames[0].name))); "folly::symbolizer::test::inlineB_inlineA_qsort()",
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h", "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
std::string(limitedFrames[0].location.file.toString())); kLineno_inlineA_qsort);
EXPECT_EQ(kFooCallByStandaloneBarLineNo, limitedFrames[0].location.line); expectFrameEq(
limitedFrames[1],
EXPECT_EQ( "call_B_A_qsort",
"void folly::symbolizer::test::(anonymous namespace)::lexicalBlockBar" "folly::symbolizer::test::call_B_A_qsort()",
"<100ul>(folly::symbolizer::FrameArray<100ul>&)",
std::string(folly::demangle(limitedFrames[1].name)));
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTest.cpp", "folly/experimental/symbolizer/test/SymbolizerTest.cpp",
std::string(limitedFrames[1].location.file.toString())); kLineno_inlineB_inlineA_qsort);
EXPECT_EQ(kInlineBarCallByLexicalBarLineNo, limitedFrames[1].location.line);
} }
TEST(SymbolizerTest, InlineFunctionInLexicalBlock) { TEST(SymbolizerTest, InlineFunctionInLexicalBlock) {
...@@ -283,25 +244,31 @@ TEST(SymbolizerTest, InlineFunctionInLexicalBlock) { ...@@ -283,25 +244,31 @@ TEST(SymbolizerTest, InlineFunctionInLexicalBlock) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames; FrameArray<100> frames;
lexicalBlockBar<100>(frames); gComparatorGetStackTraceArg = &frames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_lexicalBlock_inlineB_inlineA_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
verifyStackTrace( expectFrameEq(
symbolizer, frames.frames[5],
frames, "inlineA_qsort",
"void folly::symbolizer::test::inlineBar<100ul>(" "folly::symbolizer::test::inlineA_qsort()",
"folly::symbolizer::FrameArray<100ul>&)", "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
kFooCallByStandaloneBarLineNo, kLineno_qsort);
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
expectFrameEq(
EXPECT_EQ( frames.frames[6],
"void folly::symbolizer::test::(anonymous namespace)::lexicalBlockBar" "inlineB_inlineA_qsort",
"<100ul>(folly::symbolizer::FrameArray<100ul>&)", "folly::symbolizer::test::inlineB_inlineA_qsort()",
std::string(folly::demangle(frames.frames[7].name))); "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
EXPECT_EQ( kLineno_inlineA_qsort);
"folly/experimental/symbolizer/test/SymbolizerTest.cpp",
std::string(frames.frames[7].location.file.toString())); expectFrameEq(
EXPECT_EQ(kInlineBarCallByLexicalBarLineNo, frames.frames[7].location.line); frames.frames[7],
"lexicalBlock_inlineB_inlineA_qsort",
"folly::symbolizer::test::lexicalBlock_inlineB_inlineA_qsort()",
"folly/experimental/symbolizer/test/SymbolizerTestUtils.cpp",
kLineno_inlineB_inlineA_qsort);
} }
TEST(SymbolizerTest, InlineFunctionInDifferentCompilationUnit) { TEST(SymbolizerTest, InlineFunctionInDifferentCompilationUnit) {
...@@ -310,55 +277,70 @@ TEST(SymbolizerTest, InlineFunctionInDifferentCompilationUnit) { ...@@ -310,55 +277,70 @@ TEST(SymbolizerTest, InlineFunctionInDifferentCompilationUnit) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames; FrameArray<100> frames;
// NOTE: inlineBaz is only inlined with thinlto compilation mode enabled. gComparatorGetStackTraceArg = &frames;
inlineBaz(frames); gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
// NOTE: inlineLTO_inlineA_qsort is only inlined with LTO/ThinLTO.
call_inlineLTO_inlineA_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
EXPECT_EQ( expectFrameEq(
"folly::symbolizer::test::inlineBaz(" frames.frames[6],
"folly::symbolizer::FrameArray<100ul>&)", "inlineLTO_inlineA_qsort",
std::string(folly::demangle(frames.frames[6].name))); "folly::symbolizer::test::inlineLTO_inlineA_qsort()",
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTestUtils.cpp", "folly/experimental/symbolizer/test/SymbolizerTestUtils.cpp",
std::string(frames.frames[6].location.file.toString())); kLineno_inlineA_qsort);
EXPECT_EQ(kFooCallByStandaloneBazLineNo, frames.frames[6].location.line);
} }
TEST(SymbolizerTest, InlineClassMemberFunction) { TEST(SymbolizerTest, InlineClassMemberFunctionSameFile) {
SKIP_IF(!Symbolizer::isAvailable()); SKIP_IF(!Symbolizer::isAvailable());
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames; FrameArray<100> frames;
ClassWithInlineFunctions obj; gComparatorGetStackTraceArg = &frames;
obj.inlineBar(frames); gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_same_file_memberInline_inlineA_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
verifyStackTrace( expectFrameEq(
symbolizer, frames.frames[5],
frames, "inlineA_qsort",
"folly::symbolizer::test::ClassWithInlineFunctions::inlineBar(" "folly::symbolizer::test::inlineA_qsort()",
"folly::symbolizer::FrameArray<100ul>&) const", "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
kFooCallByClassBarLineNo, kLineno_qsort);
"folly/experimental/symbolizer/test/SymbolizerTest.cpp");
expectFrameEq(
frames.frames[6],
"memberInline_inlineA_qsort",
"folly::symbolizer::test::ClassSameFile::memberInline_inlineA_qsort() const",
"folly/experimental/symbolizer/test/SymbolizerTestUtils.cpp",
kLineno_inlineA_qsort);
} }
TEST(SymbolizerTest, StaticInlineClassMemberFunction) { TEST(SymbolizerTest, StaticInlineClassMemberFunctionSameFile) {
SKIP_IF(!Symbolizer::isAvailable()); SKIP_IF(!Symbolizer::isAvailable());
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames; FrameArray<100> frames;
ClassWithInlineFunctions::staticInlineBar(frames); gComparatorGetStackTraceArg = &frames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_same_file_staticMemberInline_inlineA_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
verifyStackTrace( expectFrameEq(
symbolizer, frames.frames[5],
frames, "inlineA_qsort",
"folly::symbolizer::test::ClassWithInlineFunctions::staticInlineBar(" "folly::symbolizer::test::inlineA_qsort()",
"folly::symbolizer::FrameArray<100ul>&)", "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
kFooCallByClassStaticBarLineNo, kLineno_qsort);
"folly/experimental/symbolizer/test/SymbolizerTest.cpp");
expectFrameEq(
frames.frames[6],
"staticMemberInline_inlineA_qsort",
"folly::symbolizer::test::ClassSameFile::staticMemberInline_inlineA_qsort()",
"folly/experimental/symbolizer/test/SymbolizerTestUtils.cpp",
kLineno_inlineA_qsort);
} }
TEST(SymbolizerTest, InlineClassMemberFunctionInDifferentFile) { TEST(SymbolizerTest, InlineClassMemberFunctionInDifferentFile) {
...@@ -367,17 +349,24 @@ TEST(SymbolizerTest, InlineClassMemberFunctionInDifferentFile) { ...@@ -367,17 +349,24 @@ TEST(SymbolizerTest, InlineClassMemberFunctionInDifferentFile) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames; FrameArray<100> frames;
InlineFunctionsWrapper obj; gComparatorGetStackTraceArg = &frames;
obj.inlineBar(frames); gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_different_file_memberInline_inlineA_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
verifyStackTrace( expectFrameEq(
symbolizer, frames.frames[5],
frames, "inlineA_qsort",
"folly::symbolizer::test::InlineFunctionsWrapper::inlineBar(" "folly::symbolizer::test::inlineA_qsort()",
"folly::symbolizer::FrameArray<100ul>&) const", "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
kFooCallByClassInDifferentFileBarLineNo, kLineno_qsort);
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
expectFrameEq(
frames.frames[6],
"memberInline_inlineA_qsort",
"folly::symbolizer::test::ClassDifferentFile::memberInline_inlineA_qsort() const",
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
kLineno_inlineA_qsort);
} }
TEST(SymbolizerTest, StaticInlineClassMemberFunctionInDifferentFile) { TEST(SymbolizerTest, StaticInlineClassMemberFunctionInDifferentFile) {
...@@ -386,16 +375,24 @@ TEST(SymbolizerTest, StaticInlineClassMemberFunctionInDifferentFile) { ...@@ -386,16 +375,24 @@ TEST(SymbolizerTest, StaticInlineClassMemberFunctionInDifferentFile) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames; FrameArray<100> frames;
InlineFunctionsWrapper::staticInlineBar(frames); gComparatorGetStackTraceArg = &frames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_different_file_staticMemberInline_inlineA_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
verifyStackTrace( expectFrameEq(
symbolizer, frames.frames[5],
frames, "inlineA_qsort",
"folly::symbolizer::test::InlineFunctionsWrapper::staticInlineBar(" "folly::symbolizer::test::inlineA_qsort()",
"folly::symbolizer::FrameArray<100ul>&)", "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
kFooCallByClassInDifferentFileStaticBarLineNo, kLineno_qsort);
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
expectFrameEq(
frames.frames[6],
"staticMemberInline_inlineA_qsort",
"folly::symbolizer::test::ClassDifferentFile::staticMemberInline_inlineA_qsort()",
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
kLineno_inlineA_qsort);
} }
// No inline frames should be filled because of no extra frames. // No inline frames should be filled because of no extra frames.
...@@ -403,16 +400,20 @@ TEST(SymbolizerTest, InlineFunctionBasicNoExtraFrames) { ...@@ -403,16 +400,20 @@ TEST(SymbolizerTest, InlineFunctionBasicNoExtraFrames) {
SKIP_IF(!Symbolizer::isAvailable()); SKIP_IF(!Symbolizer::isAvailable());
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 100); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<8> frames; FrameArray<9> frames;
inlineBar<8>(frames); gComparatorGetStackTraceArg = &frames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<9>;
call_inlineB_inlineA_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
Symbolizer symbolizer2(nullptr, LocationInfoMode::FULL, 100); Symbolizer symbolizer2(nullptr, LocationInfoMode::FULL, 100);
FrameArray<8> frames2; FrameArray<9> frames2;
inlineBar<8>(frames2); gComparatorGetStackTraceArg = &frames2;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<9>;
call_inlineB_inlineA_qsort();
symbolizer2.symbolize(frames2); symbolizer2.symbolize(frames2);
compareFrames<8>(frames, frames2); expectFramesEq<9>(frames, frames2);
} }
TEST(SymbolizerTest, InlineFunctionWithCache) { TEST(SymbolizerTest, InlineFunctionWithCache) {
...@@ -421,21 +422,31 @@ TEST(SymbolizerTest, InlineFunctionWithCache) { ...@@ -421,21 +422,31 @@ TEST(SymbolizerTest, InlineFunctionWithCache) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 100); Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 100);
FrameArray<100> frames; FrameArray<100> frames;
inlineBar<100>(frames); gComparatorGetStackTraceArg = &frames;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_inlineB_inlineA_qsort();
symbolizer.symbolize(frames); symbolizer.symbolize(frames);
verifyStackTrace( expectFrameEq(
symbolizer, frames.frames[5],
frames, "inlineA_qsort",
"void folly::symbolizer::test::inlineBar<100ul>(" "folly::symbolizer::test::inlineA_qsort()",
"folly::symbolizer::FrameArray<100ul>&)", "folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
kFooCallByStandaloneBarLineNo, kLineno_qsort);
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
expectFrameEq(
frames.frames[6],
"inlineB_inlineA_qsort",
"folly::symbolizer::test::inlineB_inlineA_qsort()",
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h",
kLineno_inlineA_qsort);
FrameArray<100> frames2; FrameArray<100> frames2;
inlineBar<100>(frames2); gComparatorGetStackTraceArg = &frames2;
gComparatorGetStackTrace = (bool (*)(void*))getStackTrace<100>;
call_inlineB_inlineA_qsort();
symbolizer.symbolize(frames2); symbolizer.symbolize(frames2);
compareFrames(frames, frames2); expectFramesEq(frames, frames2);
} }
int64_t functionWithTwoParameters(size_t a, int32_t b) { int64_t functionWithTwoParameters(size_t a, int32_t b) {
......
...@@ -14,8 +14,21 @@ ...@@ -14,8 +14,21 @@
* limitations under the License. * limitations under the License.
*/ */
// NOTE: To simplify generated DWARF keep #includes to a minimum.
#pragma once
#include <folly/experimental/symbolizer/test/SymbolizerTestUtils.h> #include <folly/experimental/symbolizer/test/SymbolizerTestUtils.h>
extern "C" {
// Fwd declare instead of #include <stdlib.h> to minimize generated DWARF.
void qsort(
void* base,
unsigned long nmemb,
unsigned long size,
int (*compar)(const void*, const void*));
} // "C"
namespace folly { namespace folly {
namespace symbolizer { namespace symbolizer {
namespace test { namespace test {
...@@ -25,32 +38,28 @@ namespace test { ...@@ -25,32 +38,28 @@ namespace test {
* cases that define and declare inline functions in different files. * cases that define and declare inline functions in different files.
*/ */
template <size_t kNumFrames> __attribute__((__always_inline__)) inline void inlineA_qsort() {
FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<kNumFrames>& frames) { int a[2] = {1, 2};
framesToFill = &frames;
std::array<int, 2> a = {1, 2};
// Use qsort, which is in a different library // Use qsort, which is in a different library
kQsortCallLineNo = __LINE__ + 1; kLineno_qsort = __LINE__ + 1;
qsort(a.data(), 2, sizeof(int), comparator<kNumFrames>); qsort(a, 2, sizeof(int), testComparator);
framesToFill = nullptr;
} }
template <size_t kNumFrames> __attribute__((__always_inline__)) inline void inlineB_inlineA_qsort() {
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<kNumFrames>& frames) { kLineno_inlineA_qsort = __LINE__ + 1;
kFooCallByStandaloneBarLineNo = __LINE__ + 1; inlineA_qsort();
inlineFoo(frames);
} }
FOLLY_ALWAYS_INLINE void InlineFunctionsWrapper::inlineBar( __attribute__((__always_inline__)) inline void
FrameArray<100>& frames) const { ClassDifferentFile::memberInline_inlineA_qsort() const {
kFooCallByClassInDifferentFileBarLineNo = __LINE__ + 1; kLineno_inlineA_qsort = __LINE__ + 1;
inlineFoo(frames); inlineA_qsort();
} }
/* static */ FOLLY_ALWAYS_INLINE void InlineFunctionsWrapper::staticInlineBar( /* static */ __attribute__((__always_inline__)) inline void
FrameArray<100>& frames) { ClassDifferentFile::staticMemberInline_inlineA_qsort() {
kFooCallByClassInDifferentFileStaticBarLineNo = __LINE__ + 1; kLineno_inlineA_qsort = __LINE__ + 1;
inlineFoo(frames); inlineA_qsort();
} }
} // namespace test } // namespace test
......
...@@ -16,25 +16,91 @@ ...@@ -16,25 +16,91 @@
#include <folly/experimental/symbolizer/test/SymbolizerTestUtils.h> #include <folly/experimental/symbolizer/test/SymbolizerTestUtils.h>
// NOTE: To simplify generated DWARF keep #includes to a minimum.
namespace folly { namespace folly {
namespace symbolizer { namespace symbolizer {
namespace test { namespace test {
void* framesToFill = {nullptr}; bool (*gComparatorGetStackTrace)(void* arg) = nullptr;
void* gComparatorGetStackTraceArg = nullptr;
size_t kQsortCallLineNo = 0;
size_t kFooCallByStandaloneBarLineNo = 0; int testComparator(const void* ap, const void* bp) {
size_t kFooCallByStandaloneBazLineNo = 0; // This comparator is called for the side effect of capturing the stack trace.
size_t kFooCallByClassBarLineNo = 0; // The function that calls it: qsort usually lives in a separate library -
size_t kFooCallByClassStaticBarLineNo = 0; // which exercises cross-ELF file support.
size_t kFooCallByClassInDifferentFileBarLineNo = 0; (*gComparatorGetStackTrace)(gComparatorGetStackTraceArg);
size_t kFooCallByClassInDifferentFileStaticBarLineNo = 0;
size_t kInlineBarCallByLexicalBarLineNo = 0; int a = *static_cast<const int*>(ap);
size_t kInlineBazCallByLexicalBazLineNo = 0; int b = *static_cast<const int*>(bp);
return a < b ? -1 : a > b ? 1 : 0;
void inlineBaz(FrameArray<100>& frames) { }
kFooCallByStandaloneBazLineNo = __LINE__ + 1;
inlineFoo(frames); int kLineno_qsort = 0;
int kLineno_inlineA_qsort = 0;
int kLineno_inlineB_inlineA_qsort = 0;
// NOTE: inlineLTO_inlineA_qsort is only inlined with LTO/ThinLTO.
void inlineLTO_inlineA_qsort() {
kLineno_inlineA_qsort = __LINE__ + 1;
inlineA_qsort();
}
void call_inlineA_qsort() {
inlineA_qsort();
}
void call_inlineB_inlineA_qsort() {
inlineB_inlineA_qsort();
}
void call_inlineLTO_inlineA_qsort() {
inlineLTO_inlineA_qsort();
}
class ClassSameFile {
public:
__attribute__((__always_inline__)) void memberInline_inlineA_qsort() const {
kLineno_inlineA_qsort = __LINE__ + 1;
inlineA_qsort();
}
__attribute__((__always_inline__)) static void
staticMemberInline_inlineA_qsort() {
kLineno_inlineA_qsort = __LINE__ + 1;
inlineA_qsort();
}
int dummy() const { return dummy_; }
int dummy_ = 0;
};
void call_same_file_memberInline_inlineA_qsort() {
ClassSameFile obj;
obj.memberInline_inlineA_qsort();
}
void call_same_file_staticMemberInline_inlineA_qsort() {
ClassSameFile::staticMemberInline_inlineA_qsort();
}
void call_different_file_memberInline_inlineA_qsort() {
ClassDifferentFile obj;
obj.memberInline_inlineA_qsort();
}
void call_different_file_staticMemberInline_inlineA_qsort() {
ClassDifferentFile::staticMemberInline_inlineA_qsort();
}
void lexicalBlock_inlineB_inlineA_qsort() try {
kLineno_inlineB_inlineA_qsort = __LINE__ + 1;
inlineB_inlineA_qsort();
} catch (...) {
}
void call_lexicalBlock_inlineB_inlineA_qsort() {
lexicalBlock_inlineB_inlineA_qsort();
} }
} // namespace test } // namespace test
......
...@@ -16,51 +16,50 @@ ...@@ -16,51 +16,50 @@
#pragma once #pragma once
#include <folly/experimental/symbolizer/SymbolizedFrame.h> // NOTE: To simplify generated DWARF keep #includes to a minimum.
#include <folly/experimental/symbolizer/Symbolizer.h>
namespace folly { namespace folly {
namespace symbolizer { namespace symbolizer {
namespace test { namespace test {
extern void* framesToFill; // Tests capture a stack trace from @testComparator by calling
// gComparatorGetStackTrace(gComparatorGetStackTraceArg).
template <size_t kNumFrames = 100> extern bool (*gComparatorGetStackTrace)(void* arg);
int comparator(const void* ap, const void* bp) { extern void* gComparatorGetStackTraceArg;
getStackTrace(*static_cast<FrameArray<kNumFrames>*>(framesToFill)); int testComparator(const void* ap, const void* bp);
int a = *static_cast<const int*>(ap); // Set to the line number in the caller function when calling
int b = *static_cast<const int*>(bp); // qsort/inlineA_qsort/inlineB_inlineA_qsort.
return a < b ? -1 : a > b ? 1 : 0; extern int kLineno_qsort;
} extern int kLineno_inlineA_qsort;
extern int kLineno_inlineB_inlineA_qsort;
extern size_t kQsortCallLineNo;
extern size_t kFooCallByStandaloneBarLineNo; // Debug Info for inlined functions is emitted in the functions where they're
extern size_t kFooCallByStandaloneBazLineNo; // inlined in. The SymbolizeTest.o object file has many dependencies and will
extern size_t kFooCallByClassBarLineNo; // have a huge amount of debug info. To simplify debugging call inlined
extern size_t kFooCallByClassStaticBarLineNo; // functions through these trampolines so that all debug info worth inspecting
extern size_t kFooCallByClassInDifferentFileBarLineNo; // is emitted in the tiny SymbolizerTestUtils.o
extern size_t kFooCallByClassInDifferentFileStaticBarLineNo; void call_inlineA_qsort();
extern size_t kInlineBarCallByLexicalBarLineNo; void call_inlineB_inlineA_qsort();
void call_inlineLTO_inlineA_qsort();
template <size_t kNumFrames = 100> void call_same_file_memberInline_inlineA_qsort();
FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<kNumFrames>& frames); void call_same_file_staticMemberInline_inlineA_qsort();
void call_different_file_memberInline_inlineA_qsort();
template <size_t kNumFrames = 100> void call_different_file_staticMemberInline_inlineA_qsort();
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<kNumFrames>& frames); void call_lexicalBlock_inlineB_inlineA_qsort();
extern void inlineBaz(FrameArray<100>& frames); // NOTE: inlineLTO_inlineA_qsort is only inlined with LTO/ThinLTO.
void inlineLTO_inlineA_qsort();
class InlineFunctionsWrapper {
class ClassDifferentFile {
public: public:
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<100>& frames) const; __attribute__((__always_inline__)) inline void memberInline_inlineA_qsort()
const;
FOLLY_ALWAYS_INLINE static void staticInlineBar(FrameArray<100>& frames); __attribute__((__always_inline__)) inline static void
staticMemberInline_inlineA_qsort();
// Dummy non-inline function.
size_t dummy() const { return dummy_; }
size_t dummy_ = 0; int dummy() const { return dummy_; }
int dummy_ = 0;
}; };
} // namespace test } // namespace test
......
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