我喜欢fp
收集调用堆栈的方法,perf record
因为它比dwarf
. 但是,当我查看程序使用 C++ 标准库时得到的调用堆栈/火焰图时,它们是不正确的。
这是一个测试程序:
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
int __attribute__((noinline)) stupid_factorial(int x) {
std::vector<std::string> xs;
// Need to convert numbers to strings or it will all get inlined
for (int i = 0; i < x; ++i) {
std::stringstream ss;
ss << std::setw(4) << std::setfill('0') << i;
xs.push_back(ss.str());
}
int res = 1;
while(std::next_permutation(xs.begin(), xs.end())) {
res += 1;
};
return res;
}
int main() {
std::cout << stupid_factorial(11) << "\n";
}
这是火焰图:
它是在 Docker 容器中的 Ubuntu 20.04 上通过以下步骤生成的:
g++ -Wall -O3 -g -fno-omit-frame-pointer program.cpp -o 6_stl.bin
# Make sure you have libc6-prof and libstdc++6-9-dbg installed
env LD_LIBRARY_PATH=/lib/libc6-prof/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu/debug:${LD_LIBRARY_PATH} perf record -F 1000 --call-graph fp -- ./6_stl.bin
# Make sure you have https://github.com/jonhoo/inferno installed
perf script | inferno-collapse-perf | inferno-flamegraph > flamegraph.svg
这样做的主要问题是,并非所有函数都是 的子函数stupid_factorial
,例如__memcmp_avx2_movbe
. ,dwarf
他们是。在更复杂的程序中,我什至看到像这样的函数在外部main
。__dynamic_cast
例如,它通常没有父母。
在gdb
中,我总是看到正确的回溯,包括此处未正确显示的函数。是否有可能在不自己编译的情况下获得正确的fp
调用堆栈libstdc++
(这似乎需要做很多工作)?
还有其他一些奇怪的东西,虽然我无法在 Ubuntu 18.04(在 Docker 容器之外)中重现它们:
- 中有一个未解决的函数
libstdc++.so.6.28
。 - 我自己的二进制文件中有一个未解析的函数
6_stl.bin
,位于最左边。情况也是如此dwarf
。