2015-07-11 200 views
0

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

$ 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代码中,有许多编译的C扩展模块Python包。在上面的玩具示例中,我将如何跟踪定义“dtrsm_”的位置?如果有多个加载的库包含具有相同名称的函数,是否有任何方法可以告诉哪个版本被调用?

回答

1

如果相同的预处理源文件(例如扩展了#include)包含同一符号的重复定义,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_symbols”呼叫对每个这些指针的自动转换成描述字符串如:

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

Gperftools可以(从上github上反射镜的查询判断)调用通用的“回溯”,但不是“backtrace_symbols”它“叉出来pprof做实际的符号表示”。这是一部相当史诗般的Perl脚本,看起来很可能在“??”来自。

重要的是,google-pprof试图报告定义函数的源文件(和行号),而不是包含机器代码的二进制文件(通常在堆栈跟踪中引用)。它调用“nm”实用程序。在我的系统上,它出现(通过运行“nm -l -D”)libblas与libc和python二进制文件不同,它已经被剥夺了这种调试符号(大概是为了优化),解释了结果。

要回答原来的问题:调用堆栈样品应明确,并明确指定被调用哪个版本。这些可能会使用几个月前在google-pprof中添加的选项进行转储,或者(对于时间密集型功能)可以通过使用gdb进行手动重新取样来大致确定。 (甚至可以想象,可以调整g-pprof以在其输出摘要中明确标识二进制路径。)或者,可以在候选二进制文件/库(其中可以获得短列表)上运行“nm”(和grep)通过在探查器的原始输出上运行“字符串”以及其他方法)。如果源代码可以访问(grep)或者库很受欢迎(在网络上),那么当然(根据Mike Dunlavey),查询函数名称可能是最简单的。理论上“??:?”可以通过仔细重新编译违规对象来解决。

+0

+1赞成一般原则,但分析本身很少是目的。通常目标是节省时间。由于您大概只能编辑您编写的代码,因此您需要知道的是在代码中查找加速的位置。例如,如果您调用'dgemm'超过您的需要,您可以尝试减少调用它。如果你使用的矩阵很小,那么一个专门的例程可能会节省时间。这就是为什么我在这个问题上有这么一个害虫,[* as here *](http://scicomp.stackexchange.com/a/2719/1262)。如果你能避免它,那么在lib例程中XYZ才是重要的。 –

0

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

你需要知道的是1)为什么要被调用,2)矩阵有多大。

要找出他们被调用的原因,我所做的只是检查单个堆栈样本,as here

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

+0

如果我正在使用包含具有相同名称的函数的多个库,该怎么办?有没有办法确定哪个版本正在执行? – benjimin

+0

@benjimin:那么链接器有同样的问题 - 它必须在多个定义之间进行选择。在任何情况下,如果您查看包含该函数的堆栈样本,则可以看到调用发生的行,并且可能有助于消除它的歧义。 –