12

ここに画像の説明を入力

この関数の最後にある分岐は何ですか。どうすればそれらをカバーできますか?

4

3 に答える 3

15

静的ストレージ期間 (グローバル) 変数を破棄するために gcc によって生成されたコードを観察しています。

あなたのカバレッジは、関数fooが 3 回入力されたことを示していますが、スコープの終わり近くのカウンターは、コードが 8 回実行されたことを示しています。

ここで、コンパイラがヘッダー ファイルを変換単位に配置し、gcov がコードをそのままではなく、グラフのエッジとして分岐するアセンブリ命令の制御フロー グラフとして認識することを考慮する必要があります。

したがって、foolcov html 出力の「スコープの終わり」は、実際にはfooメソッド スコープの終わりではなくfoo、ヘッダー ファイルで宣言されたグローバル変数の破棄を含む、翻訳単位全体の後に含まれるすべてのものです。

ヘッダー自体は質問に含まれていませんが、gcc が生成する最も基本的な__static_initialization_and_destructionアセンブリでさえ、多数のブランチが含まれています。

グローバル変数が含まれている場合と含まれていない場合があることに注意してください。gcc は、すべての翻訳単位に対してこのコードを生成する可能性があります。


gcov の基本的な出力を見てください。

function _Z3fooi called 1 returned 100% blocks executed 50%
        1:    4:int foo(int x) {
        1:    5:    if (x==1) {
branch  0 taken 0% (fallthrough)
branch  1 taken 100%
    #####:    6:        std::cout << "foo" << std::endl;
call    0 never executed
call    1 never executed
    #####:    7:        return 0;
        -:    8:    }
        1:    9:    return 1;
function _GLOBAL__sub_D__Z3fooi called 1 returned 100% blocks executed 100%
function _GLOBAL__sub_I__Z3fooi called 1 returned 100% blocks executed 100%
function _Z41__static_initialization_and_destruction_0ii called 2 returned 100% blocks executed 100%
        6:   10:}
call    0 returned 100%
call    1 returned 100%
branch  2 taken 50% (fallthrough)
branch  3 taken 50%
branch  4 taken 100% (fallthrough)
branch  5 taken 0%
        -:   11:

そして、要点を明確にするためにトリミングされた、生成されたアセンブリを見てください。

        ...
        ret
        .seh_endproc
        .def    _Z41__static_initialization_and_destruction_0ii;        .scl    3;      .type   32;     .endef
        .seh_proc       _Z41__static_initialization_and_destruction_0ii
_Z41__static_initialization_and_destruction_0ii:
.LFB978:
        ...
        mov     QWORD PTR __gcov0._Z41__static_initialization_and_destruction_0ii[rip], rax
        cmp     DWORD PTR 16[rbp], 1
        jne     .L5                                 <-- BRANCH
        mov     rax, QWORD PTR __gcov0._Z41__static_initialization_and_destruction_0ii[rip+8]
        add     rax, 1
        mov     QWORD PTR __gcov0._Z41__static_initialization_and_destruction_0ii[rip+8], rax
        cmp     DWORD PTR 24[rbp], 65535
        jne     .L5                                 <-- BRANCH
        ...
.L5:
        cmp     DWORD PTR 16[rbp], 0
        je      .L6                                 <-- BRANCH
于 2014-01-01T12:51:07.680 に答える
0

void関数でカバーされていないエンドブラケットでも同じ問題がありました。

私は2つの回避策を見つけました:

  • 最初に最後の関数呼び出し行にエンドブラケットを追加して、個々の行として表示されないようにします

  • 2 番目以降: ランダムな "return;" を追加します。関数の最後でコードを強制的に実行する

于 2015-11-10T15:28:56.270 に答える
-3

非常に単純な答えとして、ブランチはIF/ELSEブランチを意味します。そのため、すべての if/else には 2 つの新しいブランチがあります (カバーする必要があります)。ネストされている場合、指数関数的に成長します。

function twoNewBranches() {
  if () {
    // code
  } else {
    // code
  }
}

function twoNewBranchesNotAparent() {
  if () {
    // code
  }
}

function fourNewBranches() {
  if () {
    if () {
      // code
    } else {
      // code
    }
  }
}

• 最初の関数twoNewBranchesは、カバーする必要がある 2 つの新しいブランチを作成します

• if ステートメントを満たさないテストをカバーする必要があるため、2番目の関数twoNewBranchesNotAparentも 2 つの新しい分岐を作成します。

• 3 番目の関数fourNewBranchesは、カバーする 4 つの (2^2=4) 新しい分岐を作成します。ネストされた 2 つ、ネストされたものの親、および非表示の Else。

ブランチをカバーするということは、条件ステートメントをカバーすることです。

于 2016-12-15T15:23:38.387 に答える