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

Get the correct file name for inline functions.

Summary:
Here is an example for class inline member function declared and defined in the same file:

```
Definition:
0x00004c0a:           DW_TAG_subprogram [55] *
                        DW_AT_linkage_name [DW_FORM_strp] ( .debug_str[0x00177124] = "_ZNK5folly10symbolizer4test24ClassWithInlineFunctions9inlineBarERNS0_10FrameArrayILm100EEE")
                        DW_AT_name [DW_FORM_strp] ( .debug_str[0x00089a73] = "inlineBar")
                        DW_AT_decl_file [DW_FORM_data1] (0x1a)
                        DW_AT_decl_line [DW_FORM_data1] (0xa5)
                        DW_AT_declaration [DW_FORM_flag_present]  (true)
                        DW_AT_external [DW_FORM_flag_present] (true)
                        DW_AT_accessibility [DW_FORM_data1] (0x01)
…

Declaration:   -- no DW_AT_decl_file
0x000164c1:   DW_TAG_subprogram [228] *
                DW_AT_specification [DW_FORM_ref4]  (cu + 0x48ea => {0x00004c0a})
                DW_AT_inline [DW_FORM_data1]  (0x01)
                DW_AT_object_pointer [DW_FORM_ref4] (cu + 0x161ac => {0x000164cc})

…

Inline function call
0x0001657b:     DW_TAG_inlined_subroutine [140] *
                  DW_AT_abstract_origin [DW_FORM_ref4]  (cu + 0x161a1 => {0x000164c1})
                  DW_AT_low_pc [DW_FORM_addr] (0x000000000040fa6e)
                  DW_AT_high_pc [DW_FORM_data4] (0x00000055)
                  DW_AT_call_file [DW_FORM_data1] (0x1a)
                  DW_AT_call_line [DW_FORM_data1] (0xe2)
```

Here is an example for class inline member function declared and defined in different files:

```
Definition:  DW_AT_decl_file: 0x01  --> SymbolizerTestUtils.h
0x00004c3d:           DW_TAG_subprogram [55] *
                        DW_AT_linkage_name [DW_FORM_strp] ( .debug_str[0x0017717f] = "_ZNK5folly10symbolizer4test22InlineFunctionsWrapper9inlineBarERNS0_10FrameArrayILm100EEE")
                        DW_AT_name [DW_FORM_strp] ( .debug_str[0x00089a73] = "inlineBar")
                        DW_AT_decl_file [DW_FORM_data1] (0x01)
                        DW_AT_decl_line [DW_FORM_data1] (0x30)
                        DW_AT_declaration [DW_FORM_flag_present]  (true)
                        DW_AT_external [DW_FORM_flag_present] (true)
                        DW_AT_accessibility [DW_FORM_data1] (0x01)

…

Declaration:   DW_AT_decl_file: 0xbf  --> SymbolizerTestUtils-inl.h
0x00017ee8:   DW_TAG_subprogram [231] *
                DW_AT_decl_file [DW_FORM_data1] (0xbf)
                DW_AT_decl_line [DW_FORM_data1] (0x21)
                DW_AT_specification [DW_FORM_ref4]  (cu + 0x491d => {0x00004c3d})
                DW_AT_inline [DW_FORM_data1]  (0x01)
                DW_AT_object_pointer [DW_FORM_ref4] (cu + 0x17bd5 => {0x00017ef5})

…

Inline function call
0x00017fa5:     DW_TAG_inlined_subroutine [158] *
                  DW_AT_abstract_origin [DW_FORM_ref4]  (cu + 0x17bc8 => {0x00017ee8})
                  DW_AT_low_pc [DW_FORM_addr] (0x00000000004104ee)
                  DW_AT_high_pc [DW_FORM_data4] (0x00000055)
                  DW_AT_call_file [DW_FORM_data1] (0x1a)
                  DW_AT_call_line [DW_FORM_data2] (0x0101)
```

Reviewed By: luciang

Differential Revision: D19444082

fbshipit-source-id: 5a552404609073af04b0cc0b86b5e19d5149a1e1
parent 8feeb7e4
......@@ -78,7 +78,8 @@ struct Attribute {
struct CodeLocation {
Die die;
uint64_t line;
folly::Optional<uint64_t> file;
folly::Optional<uint64_t> line;
};
} // namespace detail
......@@ -537,10 +538,13 @@ bool Dwarf::findLocation(
inlineLocIter != inlineLocsRange.end();
inlineLocIter++, inlineFrameIter++) {
inlineFrameIter->location.line = callerLine;
callerLine = inlineLocIter->line;
if (!inlineLocIter->line) {
break;
}
callerLine = inlineLocIter->line.value();
folly::Optional<folly::StringPiece> linkageName;
folly::Optional<folly::StringPiece> name;
folly::Optional<uint64_t> file;
forEachAttribute(
cu, inlineLocIter->die, [&](const detail::Attribute& attr) {
switch (attr.spec.name) {
......@@ -551,12 +555,18 @@ bool Dwarf::findLocation(
name = boost::get<folly::StringPiece>(attr.attrValue);
break;
case DW_AT_decl_file:
file = boost::get<uint64_t>(attr.attrValue);
// 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 !file || !name || !linkageName;
return !inlineLocIter->file || !name || !linkageName;
});
if (!(name || linkageName) || !file) {
if (!(name || linkageName) || !inlineLocIter->file) {
break;
}
......@@ -567,7 +577,8 @@ bool Dwarf::findLocation(
inlineFrameIter->location.hasFileAndLine = true;
inlineFrameIter->location.name =
linkageName ? linkageName.value() : name.value();
inlineFrameIter->location.file = lineVM.getFullFileName(file.value());
inlineFrameIter->location.file =
lineVM.getFullFileName(inlineLocIter->file.value());
}
locationInfo.line = callerLine;
}
......@@ -650,17 +661,9 @@ detail::Die Dwarf::findDefinitionDie(
const detail::CompilationUnit& cu,
const detail::Die& die) const {
// Find the real definition instead of declaration.
folly::Optional<uint64_t> offset;
forEachAttribute(cu, die, [&](const detail::Attribute& attr) {
// Incomplete, non-defining, or separate declaration corresponding to
// a declaration
if (attr.spec.name == DW_AT_specification) {
offset = boost::get<uint64_t>(attr.attrValue);
return false;
}
return true;
});
// DW_AT_specification: Incomplete, non-defining, or separate declaration
// corresponding to a declaration
auto offset = getAttribute<uint64_t>(cu, die, DW_AT_specification);
if (!offset) {
return die;
}
......@@ -715,6 +718,22 @@ size_t Dwarf::forEachAttribute(
return values.data() - debugInfo_.data();
}
template <class T>
folly::Optional<T> Dwarf::getAttribute(
const detail::CompilationUnit& cu,
const detail::Die& die,
uint64_t attrName) const {
folly::Optional<T> result;
forEachAttribute(cu, die, [&](const detail::Attribute& attr) {
if (attr.spec.name == attrName) {
result = boost::get<T>(attr.attrValue);
return false;
}
return true;
});
return result;
}
void Dwarf::findSubProgramDieForAddress(
const detail::CompilationUnit& cu,
const detail::Die& die,
......@@ -774,7 +793,7 @@ void Dwarf::findInlinedSubroutineDieForAddress(
bool isHighPcAddr = false;
uint64_t origin = 0;
uint64_t originRefType = 0;
uint64_t callLine = 0;
folly::Optional<uint64_t> callLine = 0;
forEachAttribute(cu, childDie, [&](const detail::Attribute& attr) {
switch (attr.spec.name) {
// Here DW_AT_ranges is not supported since it requires looking up
......@@ -803,22 +822,30 @@ void Dwarf::findInlinedSubroutineDieForAddress(
// Address doesn't match. Keep searching other children.
return true;
}
isrLoc[0].line = callLine;
if (originRefType == DW_FORM_ref1 || originRefType == DW_FORM_ref2 ||
originRefType == DW_FORM_ref4 || originRefType == DW_FORM_ref8 ||
originRefType == DW_FORM_ref_udata) {
auto setLocationInfo = [&](uint64_t dieOffset) {
auto declDie = getDieAtOffset(cu, 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>(cu, declDie, DW_AT_decl_file);
// Jump to the actual function definition instead of declaration for name
// and line info.
isrLoc[0].die =
findDefinitionDie(cu, getDieAtOffset(cu, cu.offset + origin));
isrLoc[0].die = findDefinitionDie(cu, declDie);
isrLoc.advance(1);
};
if (originRefType == DW_FORM_ref1 || originRefType == DW_FORM_ref2 ||
originRefType == DW_FORM_ref4 || originRefType == DW_FORM_ref8 ||
originRefType == DW_FORM_ref_udata) {
setLocationInfo(cu.offset + origin);
findInlinedSubroutineDieForAddress(cu, childDie, address, isrLoc);
} else if (originRefType == DW_FORM_ref_addr) {
setLocationInfo(origin);
auto srcu = findCompilationUnit(debugInfo_, origin);
isrLoc[0].die = findDefinitionDie(cu, getDieAtOffset(cu, origin));
isrLoc.advance(1);
findInlinedSubroutineDieForAddress(srcu, childDie, address, isrLoc);
}
return false;
});
}
......
......@@ -183,6 +183,12 @@ class Dwarf {
const detail::Die& die,
folly::FunctionRef<bool(const detail::Attribute& die)> f) const;
template <class T>
folly::Optional<T> getAttribute(
const detail::CompilationUnit& cu,
const detail::Die& die,
uint64_t attrName) const;
const ElfFile* elf_;
const folly::StringPiece debugInfo_; // .debug_info
const folly::StringPiece debugAbbrev_; // .debug_abbrev
......
......@@ -23,6 +23,7 @@
#include <folly/Range.h>
#include <folly/String.h>
#include <folly/experimental/symbolizer/SymbolizedFrame.h>
#include <folly/experimental/symbolizer/test/SymbolizerTestUtils.h>
#include <folly/portability/GTest.h>
namespace folly {
......@@ -49,17 +50,6 @@ TEST(Symbolizer, Single) {
EXPECT_EQ("SymbolizerTest.cpp", basename.str());
}
void* framesToFill{nullptr};
template <size_t kNumFrames = 100>
int comparator(const void* ap, const void* bp) {
getStackTrace(*static_cast<FrameArray<kNumFrames>*>(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();
......@@ -133,21 +123,6 @@ TEST(SymbolizerTest, SymbolCache) {
namespace {
size_t kQsortCallLineNo = 0;
size_t kFooCallByStandaloneBarLineNo = 0;
size_t kFooCallByClassBarLineNo = 0;
size_t kFooCallByClassStaticBarLineNo = 0;
template <size_t kNumFrames = 100>
FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<kNumFrames>& frames) {
framesToFill = &frames;
std::array<int, 2> a = {1, 2};
// Use qsort, which is in a different library
kQsortCallLineNo = __LINE__ + 1;
qsort(a.data(), 2, sizeof(int), comparator<kNumFrames>);
framesToFill = nullptr;
}
template <size_t kNumFrames = 100>
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<kNumFrames>& frames) {
kFooCallByStandaloneBarLineNo = __LINE__ + 1;
......@@ -157,20 +132,19 @@ FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<kNumFrames>& frames) {
void verifyStackTrace(
const FrameArray<100>& frames,
const std::string& barName,
size_t barLine) {
size_t barLine,
const std::string& barFile) {
EXPECT_TRUE(frames.frameCount == 14 || frames.frameCount == 15);
EXPECT_EQ(
"void folly::symbolizer::test::(anonymous namespace)::inlineFoo<100ul>("
"void folly::symbolizer::test::inlineFoo<100ul>("
"folly::symbolizer::FrameArray<100ul>&)",
std::string(folly::demangle(frames.frames[5].name)));
EXPECT_EQ(
"folly/experimental/symbolizer/test/SymbolizerTest.cpp",
"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(
"folly/experimental/symbolizer/test/SymbolizerTest.cpp",
std::string(frames.frames[6].location.file.toString()));
EXPECT_EQ(barFile, std::string(frames.frames[6].location.file.toString()));
EXPECT_EQ(barLine, frames.frames[6].location.line);
}
......@@ -216,11 +190,12 @@ TEST(SymbolizerTest, InlineFunctionBasic) {
// clang-format off
// Expected full stack trace with @mode/dev. The last frame is missing in opt
// mode.
// Frame: _ZN5folly10symbolizer13getStackTraceEPmm
// Frame: _ZN5folly10symbolizer13getStackTraceILm100EEEbRNS0_10FrameArrayIXT_EEE
// Frame: _ZN5folly10symbolizer4test10comparatorILm100EEEiPKvS4_
// Frame: msort_with_tmp.part.0
// Frame: __GI___qsort_r
// Frame: _ZN5folly10symbolizer4test12_GLOBAL__N_19inlineFooILm100EEEvRNS0_10FrameArrayIXT_EEE
// Frame: _ZN5folly10symbolizer4test9inlineFooILm100EEEvRNS0_10FrameArrayIXT_EEE
// Frame: _ZN5folly10symbolizer4test12_GLOBAL__N_19inlineBarILm100EEEvRNS0_10FrameArrayIXT_EEE
// Frame: _ZN5folly10symbolizer4test39SymbolizerTest_InlineFunctionBasic_Test8TestBodyEv
// Frame: _ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS_4TestEvEET0_PT_MS4_FS3_vEPKc
......@@ -228,14 +203,13 @@ TEST(SymbolizerTest, InlineFunctionBasic) {
// Frame: _ZN7testing8TestInfo3RunEv
// Frame: _ZN7testing8TestCase3RunEv
// Frame: _ZN7testing8internal12UnitTestImpl11RunAllTestsEv
// Frame: _ZN7testing8UnitTest3RunEv
// Frame: _Z13RUN_ALL_TESTSv
// clang-format on
verifyStackTrace(
frames,
"void folly::symbolizer::test::(anonymous namespace)::inlineBar<100ul>("
"folly::symbolizer::FrameArray<100ul>&)",
kFooCallByStandaloneBarLineNo);
kFooCallByStandaloneBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTest.cpp");
FrameArray<100> frames2;
inlineBar<100>(frames2);
......@@ -256,7 +230,8 @@ TEST(SymbolizerTest, InlineClassMemberFunction) {
frames,
"folly::symbolizer::test::ClassWithInlineFunctions::inlineBar("
"folly::symbolizer::FrameArray<100ul>&) const",
kFooCallByClassBarLineNo);
kFooCallByClassBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTest.cpp");
}
TEST(SymbolizerTest, StaticInlineClassMemberFunction) {
......@@ -270,7 +245,39 @@ TEST(SymbolizerTest, StaticInlineClassMemberFunction) {
frames,
"folly::symbolizer::test::ClassWithInlineFunctions::staticInlineBar("
"folly::symbolizer::FrameArray<100ul>&)",
kFooCallByClassStaticBarLineNo);
kFooCallByClassStaticBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTest.cpp");
}
TEST(SymbolizerTest, InlineClassMemberFunctionInDifferentFile) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames;
InlineFunctionsWrapper obj;
obj.inlineBar(frames);
symbolizer.symbolize(frames);
verifyStackTrace(
frames,
"folly::symbolizer::test::InlineFunctionsWrapper::inlineBar("
"folly::symbolizer::FrameArray<100ul>&) const",
kFooCallByClassInDifferentFileBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
}
TEST(SymbolizerTest, StaticInlineClassMemberFunctionInDifferentFile) {
Symbolizer symbolizer(nullptr, LocationInfoMode::FULL_WITH_INLINE, 0);
FrameArray<100> frames;
InlineFunctionsWrapper::staticInlineBar(frames);
symbolizer.symbolize(frames);
verifyStackTrace(
frames,
"folly::symbolizer::test::InlineFunctionsWrapper::staticInlineBar("
"folly::symbolizer::FrameArray<100ul>&)",
kFooCallByClassInDifferentFileStaticBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h");
}
// No inline frames should be filled because of no extra frames.
......@@ -299,7 +306,8 @@ TEST(SymbolizerTest, InlineFunctionWithCache) {
frames,
"void folly::symbolizer::test::(anonymous namespace)::inlineBar<100ul>("
"folly::symbolizer::FrameArray<100ul>&)",
kFooCallByStandaloneBarLineNo);
kFooCallByStandaloneBarLineNo,
"folly/experimental/symbolizer/test/SymbolizerTest.cpp");
FrameArray<100> frames2;
inlineBar<100>(frames2);
......
/*
* 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 {
/*
* Put the inline functions definition in a separate -inl.h file to cover test
* cases that define and declare inline functions in different files.
*/
template <size_t kNumFrames>
FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<kNumFrames>& frames) {
framesToFill = &frames;
std::array<int, 2> a = {1, 2};
// Use qsort, which is in a different library
kQsortCallLineNo = __LINE__ + 1;
qsort(a.data(), 2, sizeof(int), comparator<kNumFrames>);
framesToFill = nullptr;
}
FOLLY_ALWAYS_INLINE void InlineFunctionsWrapper::inlineBar(
FrameArray<100>& frames) const {
kFooCallByClassInDifferentFileBarLineNo = __LINE__ + 1;
inlineFoo(frames);
}
/* static */ FOLLY_ALWAYS_INLINE void InlineFunctionsWrapper::staticInlineBar(
FrameArray<100>& frames) {
kFooCallByClassInDifferentFileStaticBarLineNo = __LINE__ + 1;
inlineFoo(frames);
}
} // namespace test
} // namespace symbolizer
} // namespace folly
/*
* 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.
*/
#pragma once
#include <folly/experimental/symbolizer/SymbolizedFrame.h>
namespace folly {
namespace symbolizer {
namespace test {
void* framesToFill{nullptr};
template <size_t kNumFrames = 100>
int comparator(const void* ap, const void* bp) {
getStackTrace(*static_cast<FrameArray<kNumFrames>*>(framesToFill));
int a = *static_cast<const int*>(ap);
int b = *static_cast<const int*>(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;
template <size_t kNumFrames = 100>
FOLLY_ALWAYS_INLINE void inlineFoo(FrameArray<kNumFrames>& frames);
class InlineFunctionsWrapper {
public:
FOLLY_ALWAYS_INLINE void inlineBar(FrameArray<100>& frames) const;
FOLLY_ALWAYS_INLINE static void staticInlineBar(FrameArray<100>& frames);
// Dummy non-inline function.
size_t dummy() const {
return dummy_;
}
size_t dummy_ = 0;
};
} // namespace test
} // namespace symbolizer
} // namespace folly
#include <folly/experimental/symbolizer/test/SymbolizerTestUtils-inl.h>
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