天天看點

Linux性能評測工具之一:gprof篇 1 簡介 3 原理 4 使用流程 5 參數說明 6 報告說明 9 注意事項 10 多線程應用

 2 功能

Gprof 是GNU gnu binutils工具之一,預設情況下linux系統當中都帶有這個工具。

1. 可以顯示“flat profile”,包括每個函數的調用次數,每個函數消耗的處理器時間,

2. 可以顯示“Call graph”,包括函數的調用關系,每個函數調用花費了多少時間。

3. 可以顯示“注釋的源代碼”--是程式源代碼的一個複本,标記有程式中每行代碼的執行次數。

1. 在編譯和連結時 加上-pg選項。一般我們可以加在 makefile 中。

2. 執行編譯的二進制程式。執行參數和方式同以前。

3. 在程式運作目錄下 生成 gmon.out 檔案。如果原來有gmon.out 檔案,将會被重寫。

4. 結束程序。這時 gmon.out 會再次被重新整理。

5. 用 gprof 工具分析 gmon.out 檔案。

l -b 不再輸出統計圖表中每個字段的較長的描述。

l -p 隻輸出函數的調用圖(Call graph的那部分資訊)。

l -q 隻輸出函數的時間消耗清單。

l -e Name 不再輸出函數Name 及其子函數的調用圖(除非它們有未被限制的其它父函數)。可以給定多個 -e 标志。一個 -e 标志隻能指定一個函數。

l -E Name 不再輸出函數Name 及其子函數的調用圖,此标志類似于 -e 标志,但它在總時間和百分比時間的計算中排除了由函數Name 及其子函數所用的時間。

l -f Name 輸出函數Name 及其子函數的調用圖。可以指定多個 -f 标志。一個 -f 标志隻能指定一個函數。

l -F Name 輸出函數Name 及其子函數的調用圖,它類似于 -f 标志,但它在總時間和百分比時間計算中僅使用所列印的例程的時間。可以指定多個 -F 标志。一個 -F 标志隻能指定一個函數。-F 标志覆寫 -E 标志。

l -z 顯示使用次數為零的例程(按照調用計數和累積時間計算)。

一般用法: gprof –b 二進制程式 gmon.out >report.txt

Gprof 産生的資訊解釋:

  %time

Cumulative

seconds

Self 

Seconds

Calls

Self

TS/call

Total

name

該函數消耗時間占程式所有時間百分比

程式的累積執行時間

(隻是包括gprof能夠監控到的函數)

該函數本身執行時間

(所有被調用次數的合共時間)

函數被調用次數

函數平均執行時間

(不包括被調用時間)

(函數的單次執行時間)

(包括被調用時間)

函數名

Call Graph 的字段含義:

Index

%time

Children

Called

Name

索引值

函數消耗時間占所有時間百分比

函數本身執行時間

執行子函數所用時間

被調用次數

注意:

程式的累積執行時間隻是包括gprof能夠監控到的函數。工作在核心态的函數和沒有加-pg編譯的第三方庫函數是無法被gprof能夠監控到的,(如sleep()等)

Gprof 的具體參數可以 通過 man gprof 查詢。

 7 共享庫的支援

對于代碼剖析的支援是由編譯器增加的,是以如果希望從共享庫中獲得剖析資訊,就需要使用 -pg 來編譯這些庫。提供已經啟用代碼剖析支援而編譯的 C 庫版本(libc_p.a)。

如果需要分析系統函數(如libc庫),可以用 –lc_p替換-lc。這樣程式會連結libc_p.so或libc_p.a。這非常重要,因為隻有這樣才能監控到底層的c庫函數的執行時間,(例如memcpy(),memset(),sprintf()等)。

gcc example1.c –pg -lc_p -o example1

注意要用ldd ./example | grep libc來檢視程式連結的是libc.so還是libc_p.so

 8 使用者時間與核心時間

gprof 的最大缺陷:它隻能分析應用程式在運作過程中所消耗掉的使用者時間,無法得到程式核心空間的運作時間。通常來說,應用程式在運作時既要花費一些時間來運作使用者代碼,也要花費一些時間來運作 “系統代碼”,例如核心系統調用sleep()。

有一個方法可以檢視應用程式的運作時間組成,在 time 指令下面執行程式。這個指令會顯示一個應用程式的實際運作時間、使用者空間運作時間、核心空間運作時間。

如 time ./program

輸出:

real    2m30.295s

user    0m0.000s

sys     0m0.004s

1. g++在編譯和連結兩個過程,都要使用-pg選項。

2. 隻能使用靜态連接配接libc庫,否則在初始化*.so之前就調用profile代碼會引起“segmentation fault”,解決辦法是編譯時加上-static-libgcc或-static。

3. 如果不用g++而使用ld直接連結程式,要加上連結檔案/lib/gcrt0.o,如ld -o myprog /lib/gcrt0.o myprog.o utils.o -lc_p。也可能是gcrt1.o

4. 要監控到第三方庫函數的執行時間,第三方庫也必須是添加 –pg 選項編譯的。

5. gprof隻能分析應用程式所消耗掉的使用者時間.

6. 程式不能以demon方式運作。否則采集不到時間。(可采集到調用次數)

7. 首先使用 time 來運作程式進而判斷 gprof 是否能産生有用資訊是個好方法。

8. 如果 gprof 不适合您的剖析需要,那麼還有其他一些工具可以克服 gprof 部分缺陷,包括 OProfile 和 Sysprof。

9. gprof對于代碼大部分是使用者空間的CPU密集型的程式用處明顯。對于大部分時間運作在核心空間或者由于外部因素(例如作業系統的 I/O 子系統過載)而運作得非常慢的程式難以進行優化。

11. gprof隻能在程式正常結束退出之後才能生成報告(gmon.out)。

a) 原因: gprof通過在atexit()裡注冊了一個函數來産生結果資訊,任何非正常退出都不會執行atexit()的動作,是以不會産生gmon.out檔案。

b) 程式可從main函數中正常退出,或者通過系統調用exit()函數退出。

gprof 不支援多線程應用,多線程下隻能采集主線程性能資料。原因是gprof采用ITIMER_PROF信号,在多線程内隻有主線程才能響應該信号。

采用什麼方法才能夠分析所有線程呢?關鍵是能夠讓各個線程都響應ITIMER_PROF信号。可以通過樁子函數來實作,重寫pthread_create函數。

//////////////////// gprof-helper.c////////////////////////////

#define _GNU_SOURCE

#include <sys/time.h>

#include <stdio.h>

#include <stdlib.h>

#include <dlfcn.h>

#include <pthread.h>

static void * wrapper_routine(void *);

/* Original pthread function */

static int (*pthread_create_orig)(pthread_t *__restrict,

                                  __const pthread_attr_t *__restrict,

                                  void *(*)(void *),

                                  void *__restrict) = NULL; 

/* Library initialization function */

void wooinit(void) __attribute__((constructor));

void wooinit(void)

{

    pthread_create_orig = dlsym(RTLD_NEXT, "pthread_create");

    fprintf(stderr, "pthreads: using profiling hooks for gprof/n");

    if(pthread_create_orig == NULL)

    {

        char *error = dlerror();

        if(error == NULL)

        {

            error = "pthread_create is NULL";

        }

        fprintf(stderr, "%s/n", error);

        exit(EXIT_FAILURE);

    }

}

/* Our data structure passed to the wrapper */

typedef struct wrapper_s

    void * (*start_routine)(void *);

    void * arg;

    pthread_mutex_t lock;

    pthread_cond_t  wait;

    struct itimerval itimer;

} wrapper_t;

/* The wrapper function in charge for setting the itimer value */

static void * wrapper_routine(void * data)

    /* Put user data in thread-local variables */

    void * (*start_routine)(void *) = ((wrapper_t*)data)->;start_routine;

    void * arg = ((wrapper_t*)data)->;arg;

    /* Set the profile timer value */

    setitimer(ITIMER_PROF, &((wrapper_t*)data)->;itimer, NULL);

    /* Tell the calling thread that we don't need its data anymore */

    pthread_mutex_lock(&((wrapper_t*)data)->;lock);

    pthread_cond_signal(&((wrapper_t*)data)->;wait);

    pthread_mutex_unlock(&((wrapper_t*)data)->;lock);

    /* Call the real function */

    return start_routine(arg);

/* Our wrapper function for the real pthread_create() */

int pthread_create(pthread_t *__restrict thread,

                   __const pthread_attr_t *__restrict attr,

                   void * (*start_routine)(void *),

                   void *__restrict arg)

    wrapper_t wrapper_data;

    int i_return;

    /* Initialize the wrapper structure */

    wrapper_data.start_routine = start_routine;

    wrapper_data.arg = arg;

    getitimer(ITIMER_PROF, &wrapper_data.itimer);

    pthread_cond_init(&wrapper_data.wait, NULL);

    pthread_mutex_init(&wrapper_data.lock, NULL);

    pthread_mutex_lock(&wrapper_data.lock);

    /* The real pthread_create call */

    i_return = pthread_create_orig(thread,

                                   attr,

                                   &wrapper_routine,

                                   &wrapper_data);

    /* If the thread was successfully spawned, wait for the data

     * to be released */

    if(i_return == 0)

        pthread_cond_wait(&wrapper_data.wait, &wrapper_data.lock);

    pthread_mutex_unlock(&wrapper_data.lock);

    pthread_mutex_destroy(&wrapper_data.lock);

    pthread_cond_destroy(&wrapper_data.wait);

    return i_return;

///////////////////

然後編譯成動态庫 gcc -shared -fPIC gprof-helper.c -o gprof-helper.so -lpthread -ldl 

使用例子:

/////////////////////a.c/////////////////////////////

#include <stdio.h>;

#include <stdlib.h>;

#include <unistd.h>;

#include <pthread.h>;

#include <string.h>;

void fun1();

void fun2();

void* fun(void * argv);

int main()

        int i =0;

        int id;

        pthread_t    thread[100];

        for(i =0 ;i< 100; i++)

                id = pthread_create(&thread[i], NULL, fun, NULL);

                printf("thread =%d/n",i);

        printf("dsfsd/n");

        return 0;

void*  fun(void * argv)

        fun1();

        fun2();

        return NULL;

void fun1()

        int i = 0;

        while(i<100)

                i++;        

                printf("fun1/n");

        }        

void fun2()

        int b;

        while(i<50)

                i++;

                printf("fun2/n");

                //b+=i;        

///////////////

gcc -pg a.c  gprof-helper.so

運作程式:

./a.out

分析gmon.out:

gprof -b a.out gmon.out

繼續閱讀