LCOV:函数结尾的分支覆盖率

13

这个函数末尾的分支是什么?我该如何处理它们?

3个回答

16
您正在观察gcc生成的静态存储期(全局)变量销毁代码。
您的覆盖率显示函数foo已被调用了三次,但作用域末尾的计数器显示该代码已执行了八次,包括您查询的分支。
现在您必须考虑编译器将头文件放入翻译单元中,而gcov并不真正按照原样查看您的代码,而是将其作为带有分支的汇编指令控制流图来处理。
因此,在lcov html输出中,“foo作用域结束”实际上并不是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

1
有趣..那么有没有一种方法可以通过单元测试有意识地进入这些分支呢? - jsj
5
我认为你不想人为地进入它们,最好的方法是从lcov覆盖范围中排除它们-在最后一个括号上放置一个LCOV_EXCL_LINE:} // LCOV_EXCL_LINE - mockinterface
哇,这个 LCOV 关键字真是太好用了!谢谢。 - Luv2code
@mockinterface 对于 delete 语句也是同样的情况吗?我在这些语句上得到了一个错过的分支,例如:136 [ + - ]: 45 : delete seq; - Luv2code

0

我遇到了在无返回值函数中未被覆盖的结束括号相同的问题;

我找到了两个解决方法:

  • 首先,在最后一个函数调用行添加结束括号,这样它们就不会作为单独的行显示出来。

  • 第二个解决方法,更好的方法是:在函数末尾添加随机"return;"以强制执行代码。


-3
作为一个超级简单的答案,分支表示IF / ELSE分支。所以对于每个if / else,都有两个新分支(应该覆盖)。如果嵌套,它们会呈指数增长。
function twoNewBranches() {
  if () {
    // code
  } else {
    // code
  }
}

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

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

• 第一个函数twoNewBranches创建了两个新分支,需要进行覆盖。

• 第二个函数twoNewBranchesNotAparent也创建了两个新分支,因为您仍然需要覆盖不满足if语句的测试。

• 第三个函数fourNewBranches创建了四个(2^2=4)新分支进行覆盖。两个嵌套,嵌套的父级和隐藏的else。

总体而言,记住覆盖分支是关于覆盖条件语句。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接