誰動了個人cpu——oprofile使用札記

http://www.cnblogs.com/bangerlee/archive/2012/08/30/2659435.htmlhtml

引言python

cpu無故佔用高?應用程序響應慢?苦於沒有分析的工具?linux

oprofile利用cpu硬件層面提供的性能計數器(performance counter),經過計數採樣,幫助咱們從進程、函數、代碼層面找出佔用cpu的"罪魁禍首"。下面咱們經過實例,瞭解oprofile的具體使用方法。緩存

 

經常使用命令app

使用oprofile進行cpu使用狀況檢測,須要通過初始化、啓動檢測、導出檢測數據、查看檢測結果等步驟,如下爲經常使用的oprofile命令。less

初始化函數

  • opcontrol --no-vmlinux : 指示oprofile啓動檢測後,不記錄內核模塊、內核代碼相關統計數據
  • opcontrol --init : 加載oprofile模塊、oprofile驅動程序

檢測控制工具

  • opcontrol --start : 指示oprofile啓動檢測
  • opcontrol --dump : 指示將oprofile檢測到的數據寫入文件
  • opcontrol --reset : 清空以前檢測的數據記錄
  • opcontrol -h : 關閉oprofile進程

查看檢測結果性能

  • opreport : 以鏡像(image)的角度顯示檢測結果,進程、動態庫、內核模塊屬於鏡像範疇
  • opreport -l : 以函數的角度顯示檢測結果
  • opreport -l test : 以函數的角度,針對test進程顯示檢測結果
  • opannotate -s test : 以代碼的角度,針對test進程顯示檢測結果
  • opannotate -s /lib64/libc-2.4.so : 以代碼的角度,針對libc-2.4.so庫顯示檢測結果

 

opreport輸出解析spa

正如以上命令解析所言,不加參數的opreport命令從鏡像的角度顯示cpu的使用狀況:

複製代碼
linux # opreport
CPU: Core 2, speed 2128.07 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (Unhalted core cycles) count 100000
CPU_CLK_UNHALT.........|
  samples |           %|
------------------------
   31645719     87.6453      no-vmlinux  
    4361113     10.3592      libend.so
       7683      0.1367      libpython2.4.so.1.0
       7046      0.1253      op_test
        ⋯⋯
複製代碼

以上列表按如下形式輸出:

              samples |                            %|
-----------------------------------------------------
     鏡像內發生的採樣次數     採樣次數所佔總採樣次數的百分比      鏡像名稱

 

因咱們在初始化時執行了"opcontrol --no-vmlinux"命令,指示oprofile不對模塊和內核進行檢測,於是在探測結果中,模塊和內核一同顯示成no-vmlinux鏡像。輸出中,libend.so和libpython2.4.so.1.0均爲動態庫,op_test爲進程。以上採樣數據代表,檢測時間內,cpu主要執行內核和模塊代碼,用於執行libend.so庫函數的比重亦較大,達到10%左右。 

 

進一步地,咱們能夠查看到進程、動態庫中的每一個函數在檢測時間內佔用cpu的狀況:

複製代碼
linux # opreport -l
 samples           %        image name        app name         symbol name
31645719     87.4472        no-vmlinux      no-vmlinux         /no-vmlinux
 4361113     10.3605         libend.so       libend.so             endless
    7046      0.1253           op_test         op_test                main
    ⋯⋯
複製代碼

以上輸出顯示消耗cpu的函數爲libend.so庫中的endless函數,以及op_test程序中的main函數。

 

進行oprofile初始化時,若咱們執行opcontrol --vmlinux=vmlinux-`uname -r`,指定oprofile對內核和內核模塊進行探測,在執行opreport查看檢測結果時,內核和內核模塊就再也不顯示爲no-vmlinux,而是內核和各個內核模塊做爲單獨的鏡像,顯示相應cpu佔用狀況。

 

使用opannotate從代碼層看cpu佔用狀況

以上介紹了使用oprofile的opreport命令,分別從進程和函數層面查看cpu使用狀況的方法。看到這裏,有的同窗可能有這樣的疑問:使用opreport,我找到了消耗cpu的進程A,找到了進程A中最消耗cpu的函數B,進一步地,是否有辦法找到函數B中最消耗cpu的那一行代碼呢?

 

oprofile中的opannotate命令能夠幫助咱們完成這個任務,結合具有調試信息的程序、帶有debuginfo的動態庫,opannotate命令可顯示代碼層面佔用cpu的統計信息。下面咱們經過幾個簡單的程序實例,說明opannotate命令的使用方法。

 

首先,咱們須要一個消耗cpu的程序,該程序代碼以下:

複製代碼
//op_test.c
extern void endless();
int main()
{
  int i = 0, j = 0;
  for (; i < 10000000; i++ )
     {
           j++;
     }
  endless();
  return 0;
}
複製代碼

該程序引用了外部函數endless,endless函數定義以下:

複製代碼
//end.c
void endless()
{
  int i = 0;
  while(1)
     {
         i++;
     }
}
複製代碼

endless函數一樣很簡單,下面咱們將定義了endless函數的end.c進行帶調試信息地編譯,並生成libend.so動態庫文件:

linux # gcc -c -g -fPIC end.c
linux # gcc -shared -fPIC -o libend.so end.o
linux # cp libend.so /usr/lib64/libend.so

接着,帶調試信息地編譯op_test.c,生成op_test執行文件:

linux # gcc -g -lend -o op_test op_test.c

 

以後,咱們開啓oprofile進行檢測,並拉起op_test進程:

linux # opcontrol --reset
linux # opcontrol --start
linux # ./op_test &

在程序運行一段時間後,導出檢測數據,使用opannotate進行結果查看:

複製代碼
linux # opcontrol --dump
linux # opannotate -s op_test
/*
 * Total samples for file : "/tmp/lx/op_test.c"
 *
 * 7046  100.00
 */
               : int main()
               :{ /*main total : 7046  100.000 */
              :    int i = 0, j = 0;
6447   91.4987 :    for (; i < 10000000; i++ )
               :    {
 599    8.5013 :          j++;
               :    }
             :    endless();
             :    return 0;
               :}
複製代碼

以上輸出代表,在op_test程序的main函數中,主要消耗cpu的是for循環所在行代碼,因該段代碼不只包含變量i的自增運算,還將i與10000000進行比較。

 

下面顯示對自編動態庫libend.so的檢測結果:

複製代碼
linux # opannotate -s /usr/lib64/libend.so
/*
 * Total samples for file : "/tmp/lx/end.c"
 *
 * 4361113  100.00
 */
                 : void endless()
                 : {
                :     int i = 0;
                :     while(1)
                 :     {
  25661   0.6652 :          i++;
4335452  99.3348 :     }
                 : }
複製代碼

 

查看c庫代碼佔用cpu狀況

以上使用opannotate,分別查看了應用程序代碼、自編動態庫代碼的cpu佔用狀況,對於c庫中的代碼,咱們是否也能查看其消耗cpu的狀況呢?

 

在使用oprofile查看c庫代碼信息前,須要安裝glibc的debuginfo包,安裝debuginfo包以後,咱們便可以經過opannotate查看到c庫代碼,如下展現了malloc底層實現函數_int_malloc的部分代碼:

複製代碼
linux # opannotate -s /lib64/libc-2.4.so
/*
 ----------------malloc---------------------
 */
                :Void_t *
                :_int_malloc( mstate av, size_t bytes )
                :{  /* _int_malloc total: 118396  94.9249 */
                     ⋯⋯
                :       assert((fwd->size & NON_MAIN_ARENA) == 0);
115460  92.5709 :       while((unsigned long)(size) < (unsigned long)(fwd->size)) {
  1161   0.9308 :            fwd = fwd->fd;
                :            assert((fwd->size & NON_MAIN_ARENA) == 0);
                :        }
                :}
複製代碼

 

在進行程序性能調優時,根據oprofile檢測到的c庫代碼佔用cpu的統計信息,能夠判別程序性能瓶頸是否由c庫代碼引發。若oprofile檢測結果顯示cpu被過多地用於執行c庫中的代碼,咱們可進一步地採用修改c庫代碼、升級glibc版本等方法解決c庫引起的應用程序性能問題。

 

小結

本文介紹了使用oprofile工具從進程、函數和代碼層面檢測cpu使用狀況的方法,對於代碼層面,分別介紹了查看程序代碼、自編動態庫代碼以及gblic代碼cpu統計狀況的方法,中間過程使用到opcontrol、opreport、opannotate三個經常使用的oprofile命令。

 

當系統出現cpu使用率異常偏高狀況時,oprofile不但能夠幫助咱們分析出是哪個進程異常使用cpu,還能夠揪出進程中佔用cpu的函數、代碼。在分析應用程序性能瓶頸、進行性能調優時,咱們能夠經過oprofile,得出程序代碼的cpu使用狀況,找到最消耗cpu的那部分代碼進行分析與調優,作到有的放矢。另外,進行程序性能調優時,咱們不該僅僅關注本身編寫的上層代碼,也應考慮底層庫函數,甚至內核對應用程序性能的影響。

 

關於oprofile工具可用於分析的場景,本文僅介紹了cpu使用狀況一種,咱們還能夠經過oprofile查看高速緩存的利用率、錯誤的轉移預測等信息,"opcontrol --list-events"命令顯示了oprofile可檢測到的全部事件,更多的oprofile使用方法,請參看oprofile manual

相關文章
相關標籤/搜索