簡介 在這章里,我們將會探討gcov實用程序,并且了解一下如何使用gcov來幫助測試與支持軟件配置與優化。我們將會了解如何使用gcov來構建軟件,并且理解他所提供的各種數據類型。最后,我們將探討當執行保險測試時要避免的事情。
gcov是什么? 我們從gcov可以為我們做什么開始。gcov是一個保險測試工具。當構建一個程序時,gcov會監視一個程序的執行,并且會標識出執行了哪一行源碼,哪 一行沒有執行。更進一步,gcov可以標識出某一行源執行的次數,這對于執行配置很有用(程序在哪里花費了大多數的時間)。因為gcov可以分辨出哪一行 沒有執行,這對于保險測試工具是很有用的。 讓我們來看一下如何為gcov的使用準備鏡像。我們將會在接下來的部分提供更為詳細的gcov的選項,所以這里只是作為一個介紹。我們將將會使用下面的所列的bubblesort的源碼: 1: #include <stdio.h> 2: 3: void bubbleSort( int list[], int size ) 4: { 5: int i, j, temp, swap = 1; 6: 7: while (swap) { 8: 9: swap = 0; 10: 11: for ( i = (size-1) ; i >= 0 ; i-- ) { 12: 13: for ( j = 1 ; j <= i ; j++ ) { 14: 15: if ( list[j-1] > list[j] ) { 16: 17: temp = list[j-1]; 18: list[j-1] = list[j]; 19: list[j] = temp; 20: swap = 1; 21: 22: } 23: 24: } 25: 26: } 27: 28: } 29: 30: } 31: 32: int main() 33: { 34: int theList[10]={10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; 35: int i; 36: 37: /* Invoke the bubble sort algorithm */ 38: bubbleSort( theList, 10 ); 39: 40: /* Print out the final list */ 41: for (i = 0 ; i < 10 ; i++) { 42: printf("%d\n", theList[i]); 43: } 44: 45: } gcov程序將會與編譯器工具鏈一起使用。這就意味著我們將要在其上進行保險測試的鏡像必須用一個特殊的選項集合進行編譯。下面是我們用來演示編譯bubbleSort.c的命令: gcc bubblesort.c -o bubblesort -ftest-coverage -fprofile-arcs 當我們執行生成的程序時會生成一些包含關于程序的相關數據的文件。gcov程序將會使用這些文件來報告數據并且向開發者提供相應的信息。當指定 -ftest-coverage選項時會為每一個源碼生成兩個文件。這些文件會使用.bb與.bbg作為擴展名,并且用這些文件來重組每一個可執行程序的 程序流圖。對于-fprofile-arcs,將會生成一個包含每一個指令分支的執行計數的以.da為擴展名的文件。這些文件會在執行以后與源碼文件一起 使用,來標識源碼的執行行為。
使用gcov程序 現在我們準備好了我們的程序鏡像了,讓我們繼續我們其余的部分。運行我們的程序就會生成我們在前面所討論的數據集文件。然后我們使用我們希望進行檢測的源碼運行gcov程序。如下面所示: $ ./bubblesort ... $ gcov bubblesort.c 100.00% of 17 source lines executed in file bubblesort.c Creating bubblesort.c.gcov. 這告訴我們在我們的例子程序中所有的源碼行至少都執行了一次。我們可以通過查看所生成的bubblesort.c.gcov文件來了解每一源碼行所實際運行的次數。如下面所示: -: 0:Source:bubblesort.c -: 0:Graph:bubblesort.gcno -: 0:Data:bubblesort.gcda -: 0:Runs:1 -: 0:Programs:1 -: 1:#include <stdio.h> -: 2:void bubbleSort(int list[],int size) 1: 3:{ 1: 4: int i,j,temp,swap=1; 4: 5: while(swap) -: 6: { 2: 7: swap=0; 22: 8: for(i=(size-1);i>=0;i--) -: 9: { 110: 10: for(j=1;j<=i;j++) -: 11: { 90: 12: if(list[j-1]>list[j]) -: 13: { 45: 14: temp=list[j-1]; 45: 15: list[j-1]=list[j]; 45: 16: list[j]=temp; 45: 17: swap=1; -: 18: } -: 19: } -: 20: } -: 21: } 1: 22:} -: 23:int main() 1: 24:{ 1: 25: int theList[10]={10,9,8,7,6,5,4,3,2,1}; -: 26: int i; -: 27: /*Invoke the buble sort algorithm*/ 1: 28: bubbleSort(theList,10); -: 29: -: 30: /*print out the final list*/ 11: 31: for(i=0;i<10;i++) -: 32: { 10: 33: printf("%d\n",theList[i]); -: 34: } 1: 35: return 0; -: 36:} 現在讓我們來看一下其中的一些關鍵點,看一下他所提供的內容。第一列顯示了源碼中每一行源碼所執行的次數。在一些情況下,執行次數并沒有提供。這些只是并不會影響代碼的簡單C源碼元素。 這些計數可以提供一些關于程序執行的信息。例如,測試的第12行執行了90次,而14-17行的代碼只是執行了45次。這告訴我們當這個函數調用了90次,真正成功的僅是45次。換句話說,大部分的測試時間浪費在兩個元素的交換上。這是由于測試數據的順序所造成的。 從這里我們可以看到代碼段中最常執行的部分就是排序算法的內循環部分。這是因為由于退出測試第10行要比第12行執行的次數多一些。 查看分支概率 我們也可以使用-b選項來查看程序的分支數據。這個選項會輸出程序中每一個分支的頻度與相應的摘要。例如,我們使用-b選項來執行gcov命令: $ gcov -b bubblesort.c 100.00% of 17 source lines executed in file bubblesort.c 100.00% of 12 branches executed in file bubblesort.c 100.00% of 12 branches taken at least once in file bubblesort.c 100.00% of 2 calls executed in file bubblesort.c Creating bubblesort.c.gcov. 所生成的bubblesort.c.gcov文件如下所示。 -: 0:Source:bubblesort.c -: 0:Graph:bubblesort.gcno -: 0:Data:bubblesort.gcda -: 0:Runs:1 -: 0:Programs:1 -: 1:#include <stdio.h> -: 2:void bubbleSort(int list[],int size) function bubbleSort called 1 returned 100% blocks executed 100% 1: 3:{ 1: 4: int i,j,temp,swap=1; 4: 5: while(swap) branch 0 taken 67% branch 1 taken 33% (fallthrough) -: 6: { 2: 7: swap=0; 22: 8: for(i=(size-1);i>=0;i--) branch 0 taken 91% branch 1 taken 9% (fallthrough) -: 9: { 110: 10: for(j=1;j<=i;j++) branch 0 taken 82% branch 1 taken 18% (fallthrough) -: 11: { 90: 12: if(list[j-1]>list[j]) branch 0 taken 50% (fallthrough) branch 1 taken 50% -: 13: { 45: 14: temp=list[j-1]; 45: 15: list[j-1]=list[j]; 45: 16: list[j]=temp; 45: 17: swap=1; -: 18: } -: 19: } -: 20: } -: 21: } 1: 22:} -: 23:int main() function main called 1 returned 100% blocks executed 100% 1: 24:{ 1: 25: int theList[10]={10,9,8,7,6,5,4,3,2,1}; -: 26: int i; -: 27: /*Invoke the buble sort algorithm*/ 1: 28: bubbleSort(theList,10); call 0 returned 100% -: 29: -: 30: /*print out the final list*/ 11: 31: for(i=0;i<10;i++) branch 0 taken 91% branch 1 taken 9% (fallthrough) -: 32: { 10: 33: printf("%d\n",theList[i]); call 0 returned 100% -: 34: } 1: 35: return 0; -: 36:} 從這里我們可看到這與上面的文件相類似,但是這一次每一個分支點都用他們的頻度進行了標示。 分支點依賴于目標結構建指令集。第12行是一個簡單的if語句,所以有一個分支點。在這里我們可以注意到這是50%,這通過我們前面觀察程序的執行次數可 以看出。其他的分支點有一些難于分析。例如,第7行是一個while語句,有兩個分支點。在X86匯編中,這一行分編譯成我們下面所看到的樣子: 1: cmpl $0, -20(%ebp) 2: jne .L4 3: jmp .L1 從這里我們可看出,swap變量與0進行比較。如果他不等于0,就會跳轉到第2行,.L4。否則要跳轉到第3行,.L1。第2行所示的分支概率為67%。 這是因為這一行執行3次,但是jne只執行了兩次。當第2行的jne并沒有執行時,我們直勢頭跳轉到第3行。這只執行一次,但是一旦執行,程序就結束了。 所以分支1要花費100%的時間。 所以分支概率在理解程序流時是要相當有用的,但是要參考匯編需要理解分支點在哪里。 不完整程序測試 當gcov計數一個測試并不是100%的程序時,并沒有執行的行是標記為####,而不是執行次數。下面顯示的是一個由gcov創建的文件來顯示少于100%的測試。 1: #include <stdio.h> 2: 3: int main() 4: 1 { 5: 1 int a=1, b=2; 6: 7: 1 if (a == 1) { 8: 1 printf("a = 1\n"); 9: } else { 10: ###### printf("a != 1\n"); 11: } 12: 13: 1 if (b == 1) { 14: ###### printf("b = 1\n"); 15: } else { 16: 1 printf("b != 1\n"); 17: } 18: 19: 1 return 0; 20: } 當這個程序運行時,gcov實用程序也會向標準輸出輸出相應的信息。他會顯示可能執行的源碼行的行數以及實際運行的百分比。 $ gcov incomptest.c 77.78% of 9 source lines executed in file incomptest.c Creating incomptest.c.gcov. $ 如果我們的例子程序有多個函數,我們可以通過使用-f選項來查看每一個函數的執行情況。如下面的我們以bubbleSort程序所進行的演示: $ gcov -f bubblesort.c 100.00% of 11 source lines executed in function bubbleSort 100.00% of 6 source lines executed in function main 100.00% of 17 source lines executed in file bubblesort.c Creating bubblesort.c.gcov. $ gcov可用的選項 gcov程序調用的格式為: gcov [options] sourcefile 其可用的選項如下: 選項 目的 -v,-version 打印版本信息 -h,-help 打印幫助信息 -b,-branch-probabilities向輸出文件輸出分支頻度 -c,-branch-counts 打印分支計數而不是分支頻度 -n,-no-output 不創建gcov輸出文件 -l,-long-file-names 創建長文件名 -f,-function-summaries 打印每一個函數的概要 -o,-object-directory .bb,.bbg,.da文件存放的目錄 從上面這個表中,我們可以看到一個單個字符選項,以及一個長選項。當從命令行中使用gcov命令時短選項是比較有用的,但是當gcov是Makefile的一個部分時,應使用長選項,因為這更易于理解。 當了解gcov程序的版本信息時,可以使用-v選項。因為gcov是與一個指定的編譯器工具鏈聯系在一起的(實際上是由gcc工具鏈而構建的),gcc版本與gcov的版本是相同的。 gcov程序的簡介以及選項幫助可以用-h選項來進行顯示。
|
|