性能分析工具显示的函数名是被混淆过的

13

我想尝试使用perf来对一些程序进行性能分析,这是我在CppCon 2015上看到this演讲后想尝试的。我下载了与该演讲中使用的Google基准库相同的版本,使用适当的开关编译我的程序,并将其链接到该库,然后使用perf记录运行情况。报告选项给出了以下内容:

enter image description here

如您所见,函数名并不是很易读。我猜测这可能与C++的名称重载有关。有趣的是,在发表演讲的人的视频中,所有函数名都显示正确,但在我的电脑上却没有。我认为这不是完全缺失符号信息的情况,因为在那种情况下,我只会看到内存地址。由于某种原因,perf无法为我“撤消”C++名称重载,这让我感到沮丧。
我使用gcc(g++)版本5.2.1,perf版本4.2.6,并在编译时使用以下开关:-I<my own include path> -L<path to the benchmark library> -O3 -std=c++14 -gdwarf-2 -fno-rtti -Wall -pedantic -lbenchmark -pthread 我不使用-fno-omit-frame-pointer的原因是我使用-gdwarf-2选项,它将调试信息留在dwarf可执行文件中,这是在此情况下保留帧指针的替代方法。这也意味着我将--call-graph "dwarf"传递给perf record。无论如何,我也尝试了帧指针方法,结果相同,所以这并不重要。
那么为什么perf在这种情况下不会“取消”C++名称修饰?这是否与使用GCC有关,这当然意味着我正在使用libstdc++?

1
我正在使用Arch Linux,perf report 显示正确的符号解助。perf的手册也显示了一个默认启用的 --demangle 选项。既然我没有看到与您相同的行为,那么我就没有答案,但是您所看到的不是预期的默认行为。 - Gabriel Southern
我也尝试手动添加那个开关,但是它没有改变任何东西。 - notadam
@GabrielSouthern 你也在使用gcc吗? - notadam
gcc --version gcc(GCC)5.2.0 - Gabriel Southern
4
看起来您正在使用Ubuntu操作系统。我认为这可能是您遇到问题的原因,同时也提供了解决方案:https://dev59.com/3VwY5IYBdhLWcg3wEEN2#34061874 - billyw
我目前没有任何项目可以使用perf,但是一旦我需要再次使用它,我会尝试这个方法。看起来似乎可以行得通。谢谢! - notadam
1个回答

13
当使用perf report命令时,如果出现像_Z*_ZN*_ZL*等混淆的名称,这意味着您的perf工具在编译时没有访问解构函数或被禁用。有代码可以检测Makefile中的解构器:

http://elixir.free-electrons.com/linux/v4.2.6/source/tools/perf/Makefile.perf

# Define NO_DEMANGLE if you do not want C++ symbol demangling.
# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds)

http://elixir.free-electrons.com/linux/v4.2.6/source/tools/perf/config/Makefile

ifdef NO_LIBELF
...
  NO_DEMANGLE := 1
...
else
  ifeq ($(feature-libelf), 0)
    ifeq ($(feature-glibc), 1)
      LIBC_SUPPORT := 1
    endif
    ...
    ifeq ($(LIBC_SUPPORT),1)
      ...
      NO_DEMANGLE := 1
    ...

这些测试位于 tools/build/feature 目录下: http://elixir.free-electrons.com/linux/v4.2.6/source/tools/build/feature,当测试程序使用 elf_begin 函数的 libelf 特性 (<libelf.h> header of elfutils package,并链接上 -lelf) 可用时,则启用 libelf 特性(并返回结果?是否需要运行测试?在交叉编译过程中,内核构建机器无法直接运行目标机器的 elf 二进制文件,必须使用 ssh 连接到真实机器或一些用户/系统 qemu)。

在性能实现中处理名称重整的代码(如果定义了HAVE_CPLUS_DEMANGLE_SUPPORT,则使用cplus_demangle,如果在Makefiles之后设置NO_DEMANGLE,则不使用重整,使用bfd.h和bfd_demangle函数文档-2.3.1.24 bfd_demangle ): http://elixir.free-electrons.com/linux/v4.2.6/source/tools/perf/util/symbol-elf.c#L19

#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT
extern char *cplus_demangle(const char *, int);

static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i)
{
    return cplus_demangle(c, i);
}
#else
#ifdef NO_DEMANGLE
static inline char *bfd_demangle(void __maybe_unused *v,
                 const char __maybe_unused *c,
                 int __maybe_unused i)
{
    return NULL;
}
#else
#define PACKAGE 'perf'
#include <bfd.h>
#endif

这有点奇怪(在C++11后期的Linux世界中仍没有标准的C++ demangle函数?)。而且您的perf被编译或配置错误 - 这就是它无法解码名称的原因。billyw 链接的答案Michal Fapso表示,这是ubuntu的bug 1396654 - https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1396654
您可以使用c++filt程序对perf的输出进行过滤,但这会阻止您使用perf的交互式默认TUI界面(添加less或将其写入文本文件以查看非常长的列表并使用正常的pageDown/pageUp):
perf report | c++filt | less
perf annotate function_name | c++filt | less
# or: perf annotate -s function_name | c++filt | less

或者你可以按照billyw他的评论中建议的更新/重新编译你的perf。

4^ 看起来你正在使用Ubuntu。我怀疑这是你的问题和解决方案:https://dev59.com/3VwY5IYBdhLWcg3wEEN2#34061874 – billyw 3月3日 17:31


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接