• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tqsheng

            go.....
            隨筆 - 366, 文章 - 18, 評論 - 101, 引用 - 0
            數(shù)據(jù)加載中……

            用 Graphviz 可視化函數(shù)調(diào)用

            用 Graphviz 可視化函數(shù)調(diào)用

            使用開源軟件來簡化復(fù)雜調(diào)用結(jié)構(gòu)

            M. Tim Jones, 資深軟件工程師, Emulex

            簡介: 花一些時間遍歷一下源代碼,可以向您展現(xiàn)所有的函數(shù)調(diào)用過程;但是如果函數(shù)指針非常復(fù)雜,或者代碼太長且晦澀難懂,那么這個過程就可能更加困難了。本文將向您介紹如何使用開源軟件和一些定制的代碼來構(gòu)建一個動態(tài)的圖形函數(shù)調(diào)用生成器。

            本文的標(biāo)簽:  代碼庫圖形化開放源碼調(diào)試

            發(fā)布日期: 2005 年 7 月 11 日 
            級別: 初級 
            訪問情況 : 12381 次瀏覽 
            評論: 1 (查看 | 添加評論 - 登錄)

            平均分 5 星 共 19 個評分 平均分 (19個評分)
            為本文評分

            可以將以圖形形式查看應(yīng)用程序的調(diào)用過程看作是一個學(xué)習(xí)經(jīng)歷。這樣做可以幫助您理解應(yīng)用程序的內(nèi)部行為,并獲得有關(guān)程序優(yōu)化方面的信息。例如,通過對那些經(jīng)常調(diào)用的函數(shù)進(jìn)行優(yōu)化,您就可以用最少的努力來獲得最佳的性能。另外,調(diào)用跟蹤還可以判斷用戶函數(shù)的最大調(diào)用深度,這可以用來對調(diào)用棧使用的內(nèi)存進(jìn)行有效限制(在嵌入式系統(tǒng)中,這是非常重要的一個考慮因素)。

            為了捕獲并顯示調(diào)用圖,您需要 4 個元素:GNU 編譯器工具鏈、Addr2line 工具、定制的中間代碼和一個名為 Graphviz 的代碼。Addr2line 工具可以識別函數(shù)、給定地址的源代碼行數(shù)和可執(zhí)行映像。定制的中間代碼是一個非常簡單的工具,它可以減少對圖形規(guī)范的地址跟蹤。Graphviz 工具可以生成圖形映像。整個過程如圖 1 所示。


            圖 1. 搜集、簡化和可視化跟蹤路徑的過程
            跟蹤過程 

            數(shù)據(jù)搜集:捕獲函數(shù)調(diào)用路徑

            要收集一個函數(shù)調(diào)用的蹤跡,您需要確定每個函數(shù)在應(yīng)用程序中調(diào)用的時間。在過去,都是通過在函數(shù)的入口處和退出處插入一個惟一的符號來手工檢測每個函數(shù)的。這個過程非常繁瑣,而且很容易出錯,通常需要對源代碼進(jìn)行大量的修改。

            幸運的是,GNU 編譯器工具鏈(也稱為 gcc)提供了一種自動檢測應(yīng)用程序中的各個函數(shù)的方法。在執(zhí)行應(yīng)用程序時,就可以收集相關(guān)的分析數(shù)據(jù)。您只需要提供兩個特殊的分析函數(shù)即可。其中一個函數(shù)在每次執(zhí)行想要跟蹤的函數(shù)時都會調(diào)用;而另外一個函數(shù)則在每次退出想要跟蹤的函數(shù)時調(diào)用(參見清單 1)。這兩個函數(shù)都是特別指定的,因此,編譯器可以識別它們。


            清單 1. GNU 的入口和出口配置函數(shù)
            				 void __cyg_profile_func_enter( void *func_address, void *call_site )                                 __attribute__ ((no_instrument_function)); void __cyg_profile_func_exit ( void *func_address, void *call_site )                                 __attribute__ ((no_instrument_function)); 

            避免使用特殊的檢測函數(shù)

            您或許會產(chǎn)生疑惑,如果 gcc 就是我們需要的檢測函數(shù),那么為什么它不檢測__cyg_* 分析函數(shù)呢?gcc 的開發(fā)者曾思考過這個問題,他們提供了一個名為no_instrument_function 的函數(shù)屬性,這個函數(shù)屬性可以應(yīng)用于函數(shù)原型,禁止對它們進(jìn)行檢測。不要將這個函數(shù)屬性應(yīng)用到分析函數(shù)上,這樣會導(dǎo)致無限遞歸分析循環(huán)和大量的無用數(shù)據(jù)。

            在調(diào)用一個檢測函數(shù)時,__cyg_profile_func_enter 同時也會被調(diào)用,并以 func_address 形式傳遞調(diào)用的函數(shù)地址,以及從中調(diào)用該函數(shù)的 call_site 形式的地址。反之,當(dāng)一個函數(shù)退出時,也會調(diào)用__cyg_profile_func_exit 函數(shù),并傳遞 func_address 形式的函數(shù)地址,以及函數(shù)從中退出的真實地址,該地址的表示形式為 call_site

            在這些分析函數(shù)中,您可以記錄下地址對,以供以后再進(jìn)行分析使用。要請求 gcc 所有的檢測函數(shù),每個文件都必須使用 -finstrument-functions 和 -g選項進(jìn)行編譯,這樣可以保留調(diào)試符號。

            因此,現(xiàn)在您就可以為 gcc 提供一些分析函數(shù)了,這些函數(shù)可以透明地插入應(yīng)用程序中的函數(shù)入口點和函數(shù)退出點。但在調(diào)用分析函數(shù)時,又應(yīng)該怎樣處理所提供的地址呢?您有很多選擇,但是為了簡便起見,可以將這個地址簡單地寫入一個文件,要注意哪個地址是函數(shù)的入口地址,哪個地址是函數(shù)的出口地址(參見清單 2)。

            注意:在清單 2 中并沒有使用調(diào)用 Callsite 信息,因為這些信息對于分析程序來說是不必要的。


            清單 2. 分析函數(shù)
            				 void __cyg_profile_func_enter( void *this, void *callsite ) {   /* Function Entry Address */   fprintf(fp, "E%p\n", (int *)this); } void __cyg_profile_func_exit( void *this, void *callsite ) {   /* Function Exit Address */   fprintf(fp, "X%p\n", (int *)this); } 

            現(xiàn)在您可以搜集分析數(shù)據(jù)了,但是您應(yīng)該在什么地方打開或關(guān)閉您的跟蹤輸出文件呢?到現(xiàn)在為止,還不需要為了進(jìn)行分析而對源程序進(jìn)行任何修改。因此,您該如何檢測整個應(yīng)用程序(包括 main 函數(shù))而不用對分析數(shù)據(jù)的輸出結(jié)果進(jìn)行初始化呢?gcc 的開發(fā)者也考慮過這個問題,它們?yōu)?nbsp;main 函數(shù)的 constructor 函數(shù)和 destructor 函數(shù)提供了一些碰巧能夠滿足這個要求一些方法。constructor 函數(shù)是在調(diào)用 main 函數(shù)之前調(diào)用的,而 destructor 函數(shù)則是在應(yīng)用程序退出時調(diào)用的。

            要創(chuàng)建 constructor 和 destructor 函數(shù),則需要聲明兩個函數(shù),然后對這兩個函數(shù)應(yīng)用constructor 和 destructor 函數(shù)屬性。在 constructor 函數(shù)中,會打開一個新的跟蹤文件,分析數(shù)據(jù)的地址跟蹤就是寫入這個文件的;在 destructor 函數(shù)中,會關(guān)閉這個跟蹤文件(參見清單 3)。


            清單 3. 分析 constructor 和 destructor 函數(shù)
            				 /* Constructor and Destructor Prototypes */ void main_constructor( void ) 	__attribute__ ((no_instrument_function, constructor)); void main_destructor( void ) 	__attribute__ ((no_instrument_function, destructor)); /* Output trace file pointer */ static FILE *fp; void main_constructor( void ) {   fp = fopen( "trace.txt", "w" );   if (fp == NULL) exit(-1); } void main_deconstructor( void ) {   fclose( fp ); } 

            如果編譯分析函數(shù)(在 instrument.c)并將它們與目標(biāo)應(yīng)用程序鏈接在一起,然后再執(zhí)行目標(biāo)應(yīng)用程序,結(jié)果會生成一個應(yīng)用程序的調(diào)用追蹤,追蹤記錄被寫入 trace.txt 文件。跟蹤文件與調(diào)用的應(yīng)用程序處于相同的目錄中。最終結(jié)果是,您可能會得到一個其中滿是地址的非常大的文件。為了能夠讓這些數(shù)據(jù)更有意義,您可以使用一個不太出名的叫做 Addr2line 的 GNU 工具。

            回頁首

            使用 Addr2line 將函數(shù)地址解析為函數(shù)名

            Addr2line 工具(它是標(biāo)準(zhǔn)的 GNU Binutils 中的一部分)是一個可以將指令的地址和可執(zhí)行映像轉(zhuǎn)換成文件名、函數(shù)名和源代碼行數(shù)的工具。這種功能對于將跟蹤地址轉(zhuǎn)換成更有意義的內(nèi)容來說簡直是太棒了。

            要了解這個過程是怎樣工作的,我們可以試驗一個簡單的交互式的例子。(我直接從 shell 中進(jìn)行操作,因為這是最簡單地展示這個過程的方法,如清單 4 所示。)這個示例 C 文件(test.c)是通過 cat 一個簡單的應(yīng)用程序?qū)崿F(xiàn)的(也就是說,將標(biāo)準(zhǔn)輸出的文本重定向到一個文件中)。然后使用 gcc 來編譯這個文件,它會傳遞一些特殊的選項。首先,要(使用 -Wl選項)通知鏈接器生成一個映像文件,并(使用 -g 選項)通知編譯器生成調(diào)試符號。最終生成可執(zhí)行文件 test。得到新的可執(zhí)行應(yīng)用程序之后,您就可以使用 grep 工具在映像文件中查找 main 來尋找它的地址了。使用這個地址和 Addr2line 工具,就可以判斷出函數(shù)名(main)、源文件(/home/mtj/test/test.c)以及它在源文件中的行號(4)。

            在調(diào)用 Addr2line 工具時,要使用 -e 選項來指定可執(zhí)行映像是 test。通過使用 -f 選項,可以告訴工具輸出函數(shù)名。


            清單 4. addr2line 的一個交互式例子
            				 $ cat >> test.c #include <stdio.h> int main() {   printf("Hello World\n");   return 0; } <ctld-d> $ gcc -Wl,-Map=test.map -g -o test test.c $ grep main test.map 	0x08048258		__libc_start_main@@GLIBC_2.0 	0x08048258		main $ addr2line 0x08048258 -e test -f main /home/mtj/test/test.c:4 $ 

            Addr2line 和調(diào)試器

            Addr2line 工具提供了基本的符號調(diào)試信息,不過 GNU Debugger (GDB)使用的是其他一些內(nèi)部方法。

            回頁首

            精簡函數(shù)跟蹤數(shù)據(jù)

            現(xiàn)在您有了一個可以搜集函數(shù)函數(shù)地址的追蹤數(shù)據(jù)的方法,還可以使用 Addr2line 工具將地址轉(zhuǎn)換為函數(shù)名。然而,從應(yīng)用程序中產(chǎn)生大量的跟蹤數(shù)據(jù)之后,如何對這些數(shù)據(jù)進(jìn)行精簡,從而使其更有意義呢?這就是使用一些定制的中間代碼在開源工具之間建立聯(lián)系的地方。本文提供了這個工具(Pvtrace)的帶有注釋的完整代碼,包括如何編譯和使用該工具的一些說明。(有關(guān)的更多信息,請參閱 下載 一節(jié)。)

            回想一下圖 1 中的內(nèi)容,在執(zhí)行設(shè)置了檢測函數(shù)的應(yīng)用程序時,會創(chuàng)建一個名為 trace.txt 的文本文件。這個人們可以讀取的文件中包含了一系列地址信息 —— 每行一個地址,每行都有一個前綴字符。如果前綴是 E,那么這個地址就是一個函數(shù)的入口地址(也就是說,您正在調(diào)用這個函數(shù))。如果前綴是一個 X 字符,那么這個地址就是一個出口地址(也就是說,您正在從這個函數(shù)中退出)。

            因此,如果在跟蹤文件中有一個入口地址(A)緊跟著另外一個入口地址(B),那么您就可以推斷是 A 調(diào)用了 B。如果一個入口地址(A)后面跟著一個出口地址(A),那么就說明這個函數(shù)(A)被調(diào)用后就直接返回了。當(dāng)涉及大量的調(diào)用鏈時,就很難分析究竟是誰調(diào)用了誰,因此,一種簡單的解決方案是維護(hù)一個整個地址的堆棧。每次在跟蹤文件中碰到一個入口地址時,就將其壓入堆棧。棧頂?shù)牡刂肪痛碜詈笠淮伪徽{(diào)用的函數(shù)(也就是當(dāng)前的活動函數(shù))。如果后面緊接著是另外一個入口地址,這說明堆棧中的地址調(diào)用了這個剛從跟蹤文件處讀出的地址。在碰到退出函數(shù)時,當(dāng)前的活動函數(shù)就會返回,并釋放棧頂元素。這會將上下文返到回前一個函數(shù),由此,就可以產(chǎn)生正確的調(diào)用鏈過程。

            圖 2 介紹了這個概念,以及精簡數(shù)據(jù)的方法。在分析跟蹤文件中的調(diào)用鏈時,會構(gòu)建一個連通矩陣,用來表示哪個函數(shù)調(diào)用了其他哪些函數(shù)。這個矩陣的行表示調(diào)用函數(shù)的地址,列表示被調(diào)用的地址。對于每個調(diào)用對來說,行與列的交叉點不斷進(jìn)行累加(調(diào)用次數(shù))。當(dāng)處理完整個跟蹤文件時,其結(jié)果是該應(yīng)用程序的整個調(diào)用歷史的一個非常簡單的表示,其中包含了調(diào)用的次數(shù)。


            圖 2. 對跟蹤數(shù)據(jù)進(jìn)行處理和精簡,并生成矩陣格式
            精簡過程 

            編譯并安裝工具

            在下載并解壓 Pvtrace 工具之后,只需在子目錄中輸入 make 命令,就可以編譯 Pvtrace 工具了。也可以使用下面的代碼將這個工具安裝到 /usr/local/bin 目錄中:

            $ unzip pvtrace.zip -d pvtrace

            $ cd pvtrace

            $ make

            $ make install

            現(xiàn)在我們已經(jīng)構(gòu)建了簡化的函數(shù)連通性矩陣,接下來應(yīng)該構(gòu)建圖形的表示了。讓我們深入研究 Graphviz,了便理解如何從連通矩陣生成一個調(diào)用圖。

            回頁首

            使用 Graphviz

            Graphviz 或 Graph Visualization 是由 AT&T 開發(fā)的一個開源的圖形可視化工具。它提供了多種畫圖能力,但是我們重點關(guān)注的是它使用 Dot 語言直連圖的能力。在本文中,我們將簡單介紹如何使用 Dot 來創(chuàng)建一個圖形,并展示如何將分析數(shù)據(jù)轉(zhuǎn)換成 Graphviz 可以使用的規(guī)范。(請參閱 參考資料 一節(jié),以獲得有關(guān)下載這個開源軟件的信息。)

            Dot 使用的圖形規(guī)范

            使用 Dot 語言,您可以指定三種對象:圖、節(jié)點和邊。為了讓您理解這些對象的含義,我們將構(gòu)建一個例子來展示這些元素的用法。

            清單 5 給出了一個簡單的定向圖(directed graph),其中包含 3 個節(jié)點。第一行聲明這個圖為 G,并且聲明了該圖的類型(digraph)。接下來的三行代碼用于創(chuàng)建該圖的節(jié)點,這些節(jié)點分別名為 node1node2 和 node3。節(jié)點是在它們的名字出現(xiàn)在圖規(guī)范中時創(chuàng)建的。邊是在在兩個節(jié)點使用邊操作(->)連接在一起時創(chuàng)建的,如第 6 行到第 8 行所示。我還對邊使用了一個可選的屬性 label,用它來表示邊在圖中的名稱。最后,在第 9 行完成對該圖規(guī)范的定義。


            清單 5. 使用 Dot 符號表示的示例圖(test.dot)
            				 1:  digraph G { 2:    node1; 3:    node2; 4:    node3; 5: 6:    node1 -> node2 [label="edge_1_2"]; 7:    node1 -> node3 [label="edge_1_3"]; 8:    node2 -> node3 [label="edge_2_3"]; 9:  } 

            要將這個 .dot 文件轉(zhuǎn)換成一個圖形映像,則需要使用 Dot 工具,這個工具是在 Graphviz 包中提供的。清單 6 介紹了這種轉(zhuǎn)換。


            清單 6. 使用 Dot 來創(chuàng)建 JPG 映像
            				 $ dot -Tjpg test.dot -o test.jpg $ 

            在這段代碼中,我告訴 Dot 使用 test.dot 圖形規(guī)范,并生成一個 JPG 圖像,將其保存在文件 test.jpg 中。所生成的圖像如圖 3 所示。在此處,我使用了 JPG 格式,但是 Dot 工具也可以支持其他格式,其中包括 GIF、PNG 和 postscript。


            圖 3. Dot 創(chuàng)建的示例圖
            Dot 創(chuàng)建的示例圖 

            Dot 語言還可以支持其他一些選項,包括外形、顏色和很多屬性。但是就我們想要實現(xiàn)的功能而言,這個選項就足夠了。

            回頁首

            綜合

            現(xiàn)在我們已經(jīng)看到了整個過程的各個階段了,下面可以采用一個例子來展示如何將這些階段合并在一起了。現(xiàn)在,您應(yīng)該已經(jīng)展開并安裝了 Pvtrace 工具,然后還需要將 instrument.c 文件復(fù)制到工作源代碼目錄中。

            在這個例子中,我使用了一個源文件 test.c 進(jìn)行檢測。清單 7 給出了整個過程。在第 3 行中,我使用檢測源(instrument.c)來構(gòu)建(編譯并連接)應(yīng)用程序。然后在第 4 行執(zhí)行test,再使用 ls 命令驗證已經(jīng)生成了 trace.txt 文件。在第 8 行,我調(diào)用了 Pvtrace 工具,并提供這個映像文件作為它惟一的參數(shù)。映像名是必需的,這樣 Addr2line(在 Pvtrace 中調(diào)用)就可以訪問這個映像中的調(diào)試信息。在第 9 行中,我又執(zhí)行了一次 ls 命令,以確保 Pvtrace 生成了 graph.dot 文件。最后,在第 12 行,使用 Dot 將這個圖形規(guī)范轉(zhuǎn)換成一個 JPG 圖形映像。


            清單 7. 創(chuàng)建調(diào)用跟蹤圖的整個過程
            				  1:  $ ls  2:  instrument.c    test.c  3:  $ $ gcc -g -finstrument-functions test.c instrument.c -o test  4:  $ ./test  5:  $ ls  6:  instrument.c     test.c  7:  test             trace.txt  8:  $ pvtrace test  9:  $ ls 10:  graph.dot        test           trace.txt 11:  instrument.c     test.c 12:  $ dot -Tjpg graph.dot -o graph.jpg 13:  $ ls 14:  graph.dot        instrument.c   test.c 15:  graph.jpg        test           trace.txt 16:  $ 

            這個過程的示例輸出如圖 4 所示。這個示例圖是從使用 Q 學(xué)習(xí)的一個簡單增強式學(xué)習(xí)應(yīng)用程序中得到的。


            圖 4. 示例應(yīng)用程序的跟蹤結(jié)果
            示例應(yīng)用程序的跟蹤結(jié)果 

            您也可以使用這種方法對更大的應(yīng)用程序進(jìn)行分析。我要展示的最后一個例子是 Gzip 工具。我簡單地將 instrument.c 加入 Gzip 的 Makefile 中,作為其依賴的一個源文件,然后編譯 Gzip,并使用它生成一個跟蹤文件。這個圖形太大了,不太容易進(jìn)行更詳細(xì)的分析,但是下圖表示了 Gzip 對一個小文件進(jìn)行壓縮時的處理過程。


            圖 5. Gzip 跟蹤結(jié)果
            Gzip 跟蹤結(jié)果 

            回頁首

            結(jié)束語

            使用開源軟件和少量的中間代碼,只需要花很少的時間就可以開發(fā)出非常有用的項目。通過使用對應(yīng)用程序進(jìn)行分析的幾個 GNU 編譯器擴展,可以使用 Addr2line 工具進(jìn)行地址轉(zhuǎn)換,并對 Graphviz 應(yīng)用程序進(jìn)行圖形可視化,然后您就可以得到一個程序,該程序可以對應(yīng)用程序進(jìn)行分析,并展示一個說明調(diào)用鏈的定向圖。通過圖形來查看一個應(yīng)用程序的調(diào)用鏈對于理解應(yīng)用程序的內(nèi)部行為來說非常重要。在正確了解調(diào)用鏈及其各自的頻率之后,這些知識可能對調(diào)試和優(yōu)化應(yīng)用程序非常有用。


            回頁首

            下載

            描述名字大小下載方法
            Instrumentation source and Pvtrace sourcepvtrace.zip4 KBHTTP

            posted on 2012-07-15 14:58 tqsheng 閱讀(895) 評論(0)  編輯 收藏 引用 所屬分類: 試試


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久经典免费视频| 久久精品九九亚洲精品| 亚洲精品国精品久久99热| 久久久久久亚洲精品影院| 国产亚洲婷婷香蕉久久精品| 久久精品国产精品亚洲下载| 欧美va久久久噜噜噜久久| 国内精品伊人久久久久影院对白 | 免费观看久久精彩视频| 久久久久九国产精品| 久久精品国产亚洲av麻豆色欲 | 国产精品久久毛片完整版| 欧美粉嫩小泬久久久久久久| 69久久精品无码一区二区| 国产精品中文久久久久久久| 中文字幕亚洲综合久久2| 伊人久久大香线蕉亚洲| 日韩久久久久中文字幕人妻| 久久91精品久久91综合| 久久综合给合久久狠狠狠97色69| 亚洲国产精品无码久久青草| 一本大道加勒比久久综合| 久久精品天天中文字幕人妻| 欧美亚洲国产精品久久高清 | 久久人人爽人人爽人人片AV不| 精品久久久久久久久久久久久久久| 97精品伊人久久大香线蕉app| 精品国产99久久久久久麻豆| 久久天天婷婷五月俺也去| 久久se精品一区二区影院| 国产精品美女久久久久AV福利| 国产精品无码久久综合 | 51久久夜色精品国产| 99久久婷婷国产综合亚洲| 亚洲国产精品久久久天堂| 亚洲国产精品成人久久| 国产偷久久久精品专区| 亚洲熟妇无码另类久久久 | 人人狠狠综合久久亚洲高清| 久久精品国产欧美日韩| 久久久久久久久久久免费精品|