CodingFirst

C言語、Perl、JavaScript、最近はPythonも。出来上がったものより、プログラムを書くことが好き。あと、スイーツ。

スポンサーサイト

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

GCCでC言語の分岐カバレッジを試す。思ったのとちょっと違った。

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2

gcov(gcc)のカバレッジにある "Branchs executed" が
どう計測されるのかを試した。

まず、"Branches executed"について。
分岐網羅の事で、条件文の各条件をどれだけ実行したかで、C1のこと。
分岐網羅の解釈に揺れがあるようで、「条件をただ実行」とか「条件の分岐方向それぞれを実行」などがあるが、
gcovは「条件をただ実行」の意で計測していた。

C言語でソースを作成し、以下のように実行した。

$ gcc -g --coverage gcov-test-branch.c && ./a.out && gcov -bc gcov-test-branch.c && cat gcov-test-branch.c.gcov
P
P
P
P
P
File 'gcov-test-branch.c'
Lines executed:90.48% of 42
Branches executed:100.00% of 10
Taken at least once:60.00% of 10
Calls executed:81.25% of 16
gcov-test-branch.c: 'gcov-test-branch.c.gcov' を作成しています

作成された gcovファイルを見る。冒頭と、main関数付近。

        -:    0:Source:gcov-test-branch.c
        -:    0:Graph:gcov-test-branch.gcno
        -:    0:Data:gcov-test-branch.gcda
        -:    0:Runs:1
        -:    0:Programs:1
        -:    1:#include <stdio.h>
        -:    2:

function main called 1 returned 100% blocks executed 100%
        1:   51:int main(int c,char*v[]){
        1:   52:  branch1_ok(0);
call    0 returned 1
        1:   53:  branch1_ok(1);
call    0 returned 1
        1:   54:  branch2_not(0);
call    0 returned 1
        1:   55:  branch3_not(0);
call    0 returned 1
        1:   56:  branch4_ok(0);
call    0 returned 1
        1:   57:  branch5_ok(0);
call    0 returned 1
        1:   58:  branch6_ok(0);
call    0 returned 1
        1:   59:  branch7_ok(0);
call    0 returned 1
        1:   60:  branch8_ok(0);
call    0 returned 1
        1:   61:  return 0;
        -:   62:}

branch1_ok()から見ていく

function branch1_ok called 2 returned 100% blocks executed 100%
        2:    3:void branch1_ok(int i){
        2:    4:  if(i==0||i>=0){
branch  0 taken 1 (fallthrough)
branch  1 taken 1
branch  2 taken 1 (fallthrough)
branch  3 taken 0
        2:    5:    puts("P");
call    0 returned 2
        -:    6:  }
        2:    7:}

branch の後ろに "never executed" がなく、パス(全網羅)と判定されている。
条件(分岐)は i==0 と、i>=0 で、main関数から i=0, i=1 でコールしてるので、
この判定は true にしかならない。
したがって、条件がtrue/falseによらず実行されていればパス(全網羅)扱いになると言える

function branch2_not called 1 returned 100% blocks executed 100%
        1:    8:void branch2_not(int i){
        -:    9:  if(0){
        -:   10:    puts("P");
        -:   11:  }
        1:   12:}

if(0)は分岐とすらみなさない。

function branch3_not called 1 returned 100% blocks executed 100%
        1:   13:void branch3_not(int i){
        -:   14:  int j;
        1:   15:  j=(i==0)?0:1;
        1:   16:}

3項演算子も同様に、分岐とみなさない。

function branch4_ok called 1 returned 100% blocks executed 67%
        1:   17:void branch4_ok(int i){
        1:   18:  switch(i){
branch  0 taken 1
branch  1 taken 0
        -:   19:    case 0:
        1:   20:      puts("P");
call    0 returned 1
        1:   21:      break;
        -:   22:    default:
    #####:   23:      puts("N");
call    0 never executed
    #####:   24:      break;
        -:   25:  }
        1:   26:}

switchは分岐とみなす。
ただし、if文同様にtrue/falseは関係しない。

function branch5_ok called 1 returned 100% blocks executed 60%
        1:   27:void branch5_ok(int i){
        -:   28:  int j;
        1:   29:  for(j=0;j<i;j++){
branch  0 taken 0
branch  1 taken 1 (fallthrough)
    #####:   30:    puts("N");
call    0 never executed
        -:   31:  }
        1:   32:}

forも同様。分岐にはなる

function branch6_ok called 1 returned 100% blocks executed 75%
        1:   33:void branch6_ok(int i){
        2:   34:  while(i>0){
branch  0 taken 0
branch  1 taken 1 (fallthrough)
    #####:   35:    puts("N");
call    0 never executed
        -:   36:  }
        1:   37:}
function branch7_ok called 1 returned 100% blocks executed 100%

whileも同様。分岐にはなる

        1:   38:void branch7_ok(int i){
        -:   39:  do{
        1:   40:    puts("P");
call    0 returned 1
        1:   41:    break;
        -:   42:  }while(i==0);
        1:   43:}

途中で break した場合、if(0)と同様に whileの行は分岐とみなされない

        -:   44:#define b8(i) if(i==0||i==1){\
        -:   45:  puts("P");\
        -:   46:  }
function branch8_ok called 1 returned 100% blocks executed 100%
        1:   47:void branch8_ok(int i){
        1:   48:  b8(0);
call    0 returned 1
        1:   49:}
        -:   50:

マクロにすると分岐とみなさなくなる。これは意外。

gcovのブランチカバレッジは単純に条件を実行したかだった。
意味はわかりやすかったけど、コードカバレッジとしては使いにくいと感じる。



実例で学ぶGCCの本格的活用法―高機能コンパイラのオプション・コマンドを一つ一つていねいに解説 (TECHI―Embedded Software)実例で学ぶGCCの本格的活用法―高機能コンパイラのオプション・コマンドを一つ一つていねいに解説 (TECHI―Embedded Software)
(2006/07)
岸 哲夫

商品詳細を見る


テスト駆動開発入門テスト駆動開発入門
(2003/09)
ケント ベック

商品詳細を見る

もともとのソースコードは続きに

#include <stdio.h>

void branch1_ok(int i){
  if(i==0||i>=0){
    puts("P");
  }
}
void branch2_not(int i){
  if(0){
    puts("P");
  }
}
void branch3_not(int i){
  int j;
  j=(i==0)?0:1;
}
void branch4_ok(int i){
  switch(i){
    case 0:
      puts("P");
      break;
    default:
      puts("N");
      break;
  }
}
void branch5_ok(int i){
  int j;
  for(j=0;j<i;j++){
    puts("N");
  }
}
void branch6_ok(int i){
  while(i>0){
    puts("N");
  }
}
void branch7_ok(int i){
  do{
    puts("P");
    break;
  }while(i==0);
}
#define b8(i) if(i==0||i==1){\
  puts("P");\
  }
void branch8_ok(int i){
  b8(0);
}

int main(int c,char*v[]){
  branch1_ok(0);
  branch1_ok(1);
  branch2_not(0);
  branch3_not(0);
  branch4_ok(0);
  branch5_ok(0);
  branch6_ok(0);
  branch7_ok(0);
  branch8_ok(0);
  return 0;
}
スポンサーサイト


★☆★コメント★☆★

コメントの投稿

Name
Subject
Mail
URI
Comment
Pass
Secret 管理者にだけ表示を許可する

トラックバック

http://iyukki.blog56.fc2.com/tb.php/157-bd5d2781

 | HOME | 

Search

Recent Entries

Foot Print



Categories

Monthly

Recent Comments

Recent Trackbacks

Profile

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。