1

有没有办法避免 Google 性能工具将文件列为“??:?”,即无法找到包含它报告的功能的文件?如何确定哪个库包含被调用的函数?

$ env LD_PRELOAD="/usr/lib/libprofiler.so.0" \
   CPUPROFILE=output.prof python script.py
$ google-pprof --text --files /usr/bin/python output.prof 
Using local file /usr/bin/python.
Using local file output.prof.
Removing _L_unlock_13 from all stack traces.
Total: 433 samples
 362  83.6%  83.6%      362  83.6% dtrsm_ ??:?
  58  13.4%  97.0%       58  13.4% dgemm_ ??:?
   1   0.2%  97.2%        1   0.2% PyDict_GetItem /.../Objects/dictobject.c
   1   0.2%  97.5%        1   0.2% PyParser_AddToken /.../Parser/parser.c
...

我的目标是能够在具有许多已编译 C 扩展模块的 python 包中分析 C 代码。在上面的玩具示例中,我将如何追踪“dtrsm_”的定义位置?如果有多个加载的库包含具有相同名称的函数,有没有办法判断正在调用哪个版本?

4

2 回答 2

1

如果相同的预处理源文件(例如扩展#includes)包含相同符号的重复定义,C/C++ 将不会编译。(请注意,在 C++ 的情况下,符号会根据编译器特定的方案进行修改,以合并参数签名,以便于重载函数,否则无法区分。)

链接器只关心未解析的符号(所以应该没有什么可以阻止多个库同时调用它们各自的内部定义的具有重合名称的函数)。如果一个文件调用了一个已声明但未定义的函数,并且多个可用库实现了该符号,则链接器可以自由选择(比如在搜索路径中的优先级)哪个版本被替换。(顺便说一下,这是相同的机制哪些分析器(例如 gperftools 或 hpctoolkit)能够自我注入并改变另一个应用程序的正常行为。)

由于不同的库被映射到不同的内存页面,应该可以(从内存地址)识别哪个库包含函数的执行版本。事实上,GNU 调试器可以识别包含代码的库,即使它无法命名函数。

$      gdb python
(gdb)  run -c "from numpy import *; linalg.inv(random.random((1000,1000)))"
CTRL-C
(gdb)  backtrace
#0 0x00007ffff5ba9df8 in dtrsm_ () from /usr/lib/libblas.so.3
...
#3 0x00007ffff420df83 in ?? () from /.../numpy/linalg/_umath_linalg.so

Linux(或者更确切地说是 GNU C 库)提供“backtrace”调用(用于从调用堆栈中获取指针列表),以及“backtrace_symbols”调用用于自动将每个指针转换为描述性字符串,例如:

"/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7fc429929ec5]"

Gperftools 可以(从 gi​​thub 镜像上的查询判断)调用通用的“backtrace”,但不是“backtrace_symbols”,而是“派生给 pprof 进行实际的符号化”。这是一个相当史诗般的 perl 脚本,看起来很可能是“??” 来自。

至关重要的是,google-pprof 试图报告定义函数的源文件(和行号),而不是包含机器代码的二进制文件(通常在堆栈跟踪中引用)。它调用“nm”实用程序。在我的系统上,似乎(通过运行“nm -l -D”)libblas 与 libc 和 python 二进制文件不同,已被删除了此类调试符号(可能是为了优化),从而解释了结果。

要回答最初的问题:调用堆栈示例应该明确明确地指定正在调用的版本。这些可能可以使用几个月前在 google-pprof 中添加的选项转储,或者(对于时间密集型功能)可以通过使用 gdb 手动重新采样来粗略确定。(甚至可以想象,可以调整 g-pprof 以在其输出摘要中明确识别二进制文件路径。)或者,可以在候选二进制文件/库上运行“nm”(和 grep)(可以获得一个候选清单)通过在探查器的原始输出上运行“字符串”,以及其他方法)。如果源可访问(对 grep)或库很流行(在网络上),那么当然(根据 Mike Dunlavey 的说法),只查询函数名称可能是最简单的。理论上“??:

于 2015-09-07T17:24:50.783 回答
0

只需谷歌有问题的函数名称。您在上面显示的那些是在 LAPACK 中定义的。dtrsm用于求解矩阵方程。dgemm用于矩阵相乘。

您需要知道的是 1) 为什么要调用它们,以及 2) 矩阵有多大。

为了找出它们被调用的原因,我所做的只是检查单个堆栈样本,如下所示

矩阵大小很重要的原因是,如果它们很小,这些 LAPACK 例程实际上可以花费相对大部分时间来对输入进行分类,例如通过调用函数 LSAME。

于 2015-08-31T21:26:46.983 回答