日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Linux性能評測工具之一:gprof篇

 昵稱17328427 2014-09-11

簡介

改進應(yīng)用程序的性能是一項非常耗時耗力的工作,但是究竟程序中是哪些函數(shù)消耗掉了大部分執(zhí)行時間,這通常都不是非常明顯的。GNU 編譯器工具包所提供了一種剖析工具 GNU profiler(gprof)。gprof 可以為 Linux平臺上的程序精確分析性能瓶頸。gprof精確地給出函數(shù)被調(diào)用的時間和次數(shù),給出函數(shù)調(diào)用關(guān)系。

gprof 用戶手冊網(wǎng)站 http:///binutils/docs-2.17/gprof/index.html

功能

Gprof 是GNU gnu binutils工具之一,默認情況下linux系統(tǒng)當中都帶有這個工具。

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

2. 可以顯示“Call graph”,包括函數(shù)的調(diào)用關(guān)系,每個函數(shù)調(diào)用花費了多少時間。

3. 可以顯示“注釋的源代碼”--是程序源代碼的一個復本,標記有程序中每行代碼的執(zhí)行次數(shù)。

原理

通過在編譯和鏈接程序的時候(使用 -pg 編譯和鏈接選項),gcc 在你應(yīng)用程序的每個函數(shù)中都加入了一個名為mcount ( or  “_mcount”  , or  “__mcount” , 依賴于編譯器或操作系統(tǒng))的函數(shù),也就是說你的應(yīng)用程序里的每一個函數(shù)都會調(diào)用mcount, 而mcount 會在內(nèi)存中保存一張函數(shù)調(diào)用圖,并通過函數(shù)調(diào)用堆棧的形式查找子函數(shù)和父函數(shù)的地址。這張調(diào)用圖也保存了所有與函數(shù)相關(guān)的調(diào)用時間,調(diào)用次數(shù)等等的所有信息。

使用流程

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

2. 執(zhí)行編譯的二進制程序。執(zhí)行參數(shù)和方式同以前。

3. 在程序運行目錄下 生成 gmon.out 文件。如果原來有g(shù)mon.out 文件,將會被重寫。

4. 結(jié)束進程。這時 gmon.out 會再次被刷新。

5. 用 gprof 工具分析 gmon.out 文件。

參數(shù)說明

-b 不再輸出統(tǒng)計圖表中每個字段的詳細描述。

-p 只輸出函數(shù)的調(diào)用圖(Call graph的那部分信息)。

-q 只輸出函數(shù)的時間消耗列表。

-e Name 不再輸出函數(shù)Name 及其子函數(shù)的調(diào)用圖(除非它們有未被限制的其它父函數(shù))??梢越o定多個 -e 標志。一個 -e 標志只能指定一個函數(shù)。

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

-f Name 輸出函數(shù)Name 及其子函數(shù)的調(diào)用圖??梢灾付ǘ鄠€ -f 標志。一個 -f 標志只能指定一個函數(shù)。

-F Name 輸出函數(shù)Name 及其子函數(shù)的調(diào)用圖,它類似于 -f 標志,但它在總時間和百分比時間計算中僅使用所打印的例程的時間??梢灾付ǘ鄠€ -F 標志。一個 -F 標志只能指定一個函數(shù)。-F 標志覆蓋 -E 標志。

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

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

報告說明

Gprof 產(chǎn)生的信息解釋:

  %time

Cumulative

seconds

Self 

Seconds

Calls

Self

TS/call

Total

TS/call

name

該函數(shù)消耗時間占程序所有時間百分比

程序的累積執(zhí)行時間

(只是包括gprof能夠監(jiān)控到的函數(shù))

該函數(shù)本身執(zhí)行時間

所有被調(diào)用次數(shù)的合共時間

函數(shù)被調(diào)用次數(shù)

函數(shù)平均執(zhí)行時間

(不包括被調(diào)用時間)

函數(shù)的單次執(zhí)行時間

函數(shù)平均執(zhí)行時間

(包括被調(diào)用時間)

函數(shù)的單次執(zhí)行時間

函數(shù)名

Call Graph 的字段含義:

Index

%time

Self

Children

Called

Name

索引值

函數(shù)消耗時間占所有時間百分比

函數(shù)本身執(zhí)行時間

執(zhí)行子函數(shù)所用時間

被調(diào)用次數(shù)

函數(shù)名

注意:

程序的累積執(zhí)行時間只是包括gprof能夠監(jiān)控到的函數(shù)。工作在內(nèi)核態(tài)的函數(shù)和沒有加-pg編譯的第三方庫函數(shù)是無法被gprof能夠監(jiān)控到的,(如sleep()等)

Gprof 的具體參數(shù)可以 通過 man gprof 查詢。

共享庫的支持

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

如果需要分析系統(tǒng)函數(shù)(如libc庫),可以用 lc_p替換-lc。這樣程序會鏈接libc_p.so或libc_p.a。這非常重要,因為只有這樣才能監(jiān)控到底層的c庫函數(shù)的執(zhí)行時間,(例如memcpy(),memset(),sprintf()等)。

gcc example1.c –pg -lc_p -o example1

注意要用ldd ./example | grep libc來查看程序鏈接的是libc.so還是libc_p.so

用戶時間與內(nèi)核時間

gprof 的最大缺陷:它只能分析應(yīng)用程序在運行過程中所消耗掉的用戶時間,無法得到程序內(nèi)核空間的運行時間。通常來說,應(yīng)用程序在運行時既要花費一些時間來運行用戶代碼,也要花費一些時間來運行 “系統(tǒng)代碼”,例如內(nèi)核系統(tǒng)調(diào)用sleep()。

有一個方法可以查看應(yīng)用程序的運行時間組成,在 time 命令下面執(zhí)行程序。這個命令會顯示一個應(yīng)用程序的實際運行時間、用戶空間運行時間、內(nèi)核空間運行時間

如 time ./program

輸出:

real    2m30.295s

user    0m0.000s

sys     0m0.004s

注意事項

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

2. 只能使用靜態(tài)連接libc庫,否則在初始化*.so之前就調(diào)用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. 要監(jiān)控到第三方庫函數(shù)的執(zhí)行時間,第三方庫也必須是添加 pg 選項編譯的。

5. gprof只能分析應(yīng)用程序所消耗掉的用戶時間.

6. 程序不能以demon方式運行。否則采集不到時間。(可采集到調(diào)用次數(shù))

7. 首先使用 time 來運行程序從而判斷 gprof 是否能產(chǎn)生有用信息是個好方法。

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

9. gprof對于代碼大部分是用戶空間的CPU密集型的程序用處明顯。對于大部分時間運行在內(nèi)核空間或者由于外部因素(例如操作系統(tǒng)的 I/O 子系統(tǒng)過載)而運行得非常慢的程序難以進行優(yōu)化。

10. gprof 不支持多線程應(yīng)用,多線程下只能采集主線程性能數(shù)據(jù)。原因是gprof采用ITIMER_PROF信號,在多線程內(nèi)只有主線程才能響應(yīng)該信號。但是有一個簡單的方法可以解決這一問題:http://sam./writings/programming/gprof.html

11. gprof只能在程序正常結(jié)束退出之后才能生成報告(gmon.out)。

a) 原因: gprof通過在atexit()里注冊了一個函數(shù)來產(chǎn)生結(jié)果信息,任何非正常退出都不會執(zhí)行atexit()的動作,所以不會產(chǎn)生gmon.out文件。

b) 程序可從main函數(shù)中正常退出,或者通過系統(tǒng)調(diào)用exit()函數(shù)退出。

10 多線程應(yīng)用

gprof 不支持多線程應(yīng)用,多線程下只能采集主線程性能數(shù)據(jù)。原因是gprof采用ITIMER_PROF信號,在多線程內(nèi)只有主線程才能響應(yīng)該信號。

采用什么方法才能夠分析所有線程呢?關(guān)鍵是能夠讓各個線程都響應(yīng)ITIMER_PROF信號??梢酝ㄟ^樁子函數(shù)來實現(xiàn),重寫pthread_create函數(shù)。

//////////////////// 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;

}

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

然后編譯成動態(tài)庫 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 i = 0;

        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

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多