有没有办法避免Google Performance Tools将文件列为" ??:?",也就是说,无法找到包含其报告的功能的文件?我如何确定哪个库包含被调用的函数?
$ 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 _"被定义为?如果有多个加载的库包含具有相同名称的函数,是否有任何方法可以判断调用哪个版本?
答案 0 :(得分:1)
链接器只关注未解析的符号(因此应该存在阻止多个库同时调用它们各自内部定义的具有重合名称的函数的注意事项)。如果文件调用声明但未定义的函数,并且多个可用库实现该符号,则链接器可以自由选择(例如,通过搜索路径中的优先级)替换哪个版本。(顺便说一下,这是相同的机制哪些分析器(如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库)提供"回溯" call(用于从调用堆栈中获取指针列表)和" backtrace_symbols"要求自动将每个指针转换为描述性字符串,例如:
"/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7fc429929ec5]"
Gperftools可以(根据github镜像上的查询判断)调用泛型" backtrace",而不是" backtrace_symbols"它"分配到pprof进行实际的符号化" 。这是一个相当史诗般的perl脚本,看起来很可能是" ??"来自。
至关重要的是,google-pprof正在尝试报告定义函数的源文件(和行号),而不是包含机器代码的二进制文件(通常在堆栈跟踪中引用)。它调用" nm"效用。在我的系统上(通过运行" nm -l -D"),与libc和python二进制文件不同,libblas已经被剥离了这样的调试符号(大概是为了优化),解释了结果。
回答最初的问题:调用堆栈样本应明确并明确指定要调用的版本。这些可以使用几个月前在google-pprof中添加的选项转储,或者(对于时间密集型函数)可以通过使用gdb手动重新采样来粗略确定。 (甚至可以想象,可以调整g-pprof以在其输出摘要中明确地识别二进制路径。)或者可以运行" nm" (和grep)候选二进制文件/库(其中可以通过运行"字符串"在探查器的原始输出上获得短列表,以及其他方法)。如果源是可访问的(grep)或库是流行的(在Web上),那么当然(和Mike Dunlavey)可能最容易查询函数名称。在理论上," ??:?"可以通过仔细重新编译有问题的对象来解决。
答案 1 :(得分:0)
只是谷歌的违规功能名称。您在上面显示的那些是在LAPACK中定义的。 dtrsm
用于求解矩阵方程。 dgemm
用于乘以矩阵。
您需要知道的是1)为什么要调用它们,以及2)矩阵有多大。
为了找出他们被调用的原因,我所做的只是检查单个堆栈样本,as here。
矩阵大小很重要的原因是它们很小,这些LAPACK例程实际上可以花费相当大的时间来分类它们的输入,例如通过调用函数LSAME。