2015-07-11 245 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:那麼鏈接器有同樣的問題 - 它必須在多個定義之間進行選擇。在任何情況下,如果您查看包含該函數的堆棧樣本,則可以看到調用發生的行,並且可能有助於消除它的歧義。 –