gpt4 book ai didi

c++ - GCC/GCOV 为使用 throw()/noexcept 的函数生成的不同分支覆盖

转载 作者:太空宇宙 更新时间:2023-11-04 13:03:37 24 4
gpt4 key购买 nike

想了解一下GCC编译器在使用noexcept时生成的分支或 throw()用于标记非抛出函数。我知道 noexcept 之间的区别和 throw() , 但无法弄清楚使用 throw() 时定义的附加分支是什么而不是 noexcept以及如何提高覆盖率以达到 100% 的分支机构覆盖率?

测试示例类的代码片段:

class MyClass
{
public:
MyClass() noexcept :
iThrowFlag(false)
{}

void non_throwing_method() noexcept
{
try
{
throwing_method();
}
catch (...)
{
}
}

void throwing_method()
{
if (iThrowFlag)
{
throw std::exception();
}
}

public:
bool iThrowFlag;
};

定义了 2 个测试用例,都通过了:

  1. 验证是否non_throwing_method底层时完成 throwing_method不扔,
  2. 验证是否non_throwing_method底层时完成 throwing_method正在抛出任何异常。

下面是 GCC/GCOV 为上述代码生成的覆盖率报告,所有的行和分支都被覆盖了(覆盖了 4/4 个分支):

        -:    0:Source:myclass.hh
-: 0:Graph:main.gcno
-: 0:Data:main.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <iostream>
-: 2:#include <string>
-: 3:
-: 4:using namespace std;
-: 5:
-: 6:class MyClass
-: 7:{
-: 8:public:
function _ZN7MyClassC2Ev called 2 returned 100% blocks executed 100%
2: 9: MyClass() noexcept :
2: 10: iThrowFlag(false)
2: 11: {}
-: 12:
function _ZN7MyClass19non_throwing_methodEv called 2 returned 100% blocks executed 100%
2: 13: void non_throwing_method() noexcept
-: 14: {
-: 15: try
-: 16: {
2: 17: throwing_method();
call 0 returned 100%
branch 1 taken 50% (fallthrough)
branch 2 taken 50% (throw)
-: 18: }
1: 19: catch (...)
call 0 returned 100%
-: 20: {
-: 21: }
2: 22: }
-: 23:
function _ZN7MyClass15throwing_methodEv called 2 returned 50% blocks executed 100%
2: 24: void throwing_method()
-: 25: {
2: 26: if (iThrowFlag)
branch 0 taken 50% (fallthrough)
branch 1 taken 50%
-: 27: {
1: 28: throw std::exception();
call 0 returned 100%
call 1 returned 100%
call 2 returned 0%
-: 29: }
1: 30: }
-: 31:
-: 32:public:
-: 33: bool iThrowFlag;
-: 34:};

由于目标平台不支持 C++11 和 noexcept , 它被交换为 throw()说明符如下:

class MyClass
{
public:
MyClass() throw() :
iThrowFlag(false)
{}

void non_throwing_method() throw()
{
try
{
throwing_method();
}
catch (...)
{
}
}

void throwing_method()
{
if (iThrowFlag)
{
throw std::exception();
}
}

public:
bool iThrowFlag;
};

在带有已检查分支的 GCOV 输出下方 (5/8)。用 throw() 编码生成额外的 4 个分支,这些分支很难理解,也很难通过测试覆盖:

        -:    0:Source:myclass.hh
-: 0:Graph:main.gcno
-: 0:Data:main.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <iostream>
-: 2:#include <string>
-: 3:
-: 4:using namespace std;
-: 5:
-: 6:class MyClass
-: 7:{
-: 8:public:
function _ZN7MyClassC2Ev called 2 returned 100% blocks executed 100%
2: 9: MyClass() throw() :
2: 10: iThrowFlag(false)
2: 11: {}
-: 12:
function _ZN7MyClass19non_throwing_methodEv called 2 returned 100% blocks executed 75%
2: 13: void non_throwing_method() throw()
-: 14: {
-: 15: try
-: 16: {
2: 17: throwing_method();
call 0 returned 100%
branch 1 taken 50% (fallthrough)
branch 2 taken 50% (throw)
-: 18: }
1: 19: catch (...)
call 0 returned 100%
call 1 returned 100%
branch 2 taken 100% (fallthrough)
branch 3 taken 0% (throw)
branch 4 never executed
branch 5 never executed
call 6 never executed
-: 20: {
-: 21: }
2: 22: }
-: 23:
function _ZN7MyClass15throwing_methodEv called 2 returned 50% blocks executed 100%
2: 24: void throwing_method()
-: 25: {
2: 26: if (iThrowFlag)
branch 0 taken 50% (fallthrough)
branch 1 taken 50%
-: 27: {
1: 28: throw std::exception();
call 0 returned 100%
call 1 returned 100%
call 2 returned 0%
-: 29: }
1: 30: }
-: 31:
-: 32:public:
-: 33: bool iThrowFlag;
-: 34:};

测试用例如下(以获取有关该用例的完整详细信息):

void test1()
{
MyClass lObj;
lObj.non_throwing_method();
}

void test2()
{
MyClass lObj;
lObj.iThrowFlag = true;
lObj.non_throwing_method();
lObj.iThrowFlag = false;
}

如果有人对所描述的行为有答案/解释,我将不胜感激。

编辑:另附汇编代码(仅函数代码:`non_throwing_method1 进行比较)。

没有异常(exception):

_ZN7MyClass19non_throwing_methodEv:
.LFB1253:
.loc 2 13 0
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
.cfi_lsda 0x3,.LLSDA1253
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq __gcov0._ZN7MyClass19non_throwing_methodEv(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv(%rip)
.loc 2 17 0
movq -8(%rbp), %rax
movq %rax, %rdi
.LEHB0:
call _ZN7MyClass15throwing_methodEv
.LEHE0:
movq __gcov0._ZN7MyClass19non_throwing_methodEv+8(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv+8(%rip)
.loc 2 22 0
jmp .L7
.L6:
movq %rax, %rdx
movq __gcov0._ZN7MyClass19non_throwing_methodEv+16(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv+16(%rip)
.loc 2 19 0
movq %rdx, %rax
movq %rax, %rdi
call __cxa_begin_catch
movq __gcov0._ZN7MyClass19non_throwing_methodEv+24(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv+24(%rip)
call __cxa_end_catch
movq __gcov0._ZN7MyClass19non_throwing_methodEv+32(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv+32(%rip)
.L7:
.loc 2 22 0
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1253:
.globl __gxx_personality_v0
.section .gcc_except_table._ZN7MyClass19non_throwing_methodEv,"aG",@progbits,_ZN7MyClass19non_throwing_methodEv,comdat
.align 4
.LLSDA1253:
.byte 0xff
.byte 0x3
.uleb128 .LLSDATT1253-.LLSDATTD1253
.LLSDATTD1253:
.byte 0x1
.uleb128 .LLSDACSE1253-.LLSDACSB1253
.LLSDACSB1253:
.uleb128 .LEHB0-.LFB1253
.uleb128 .LEHE0-.LEHB0
.uleb128 .L6-.LFB1253
.uleb128 0x1
.LLSDACSE1253:
.byte 0x1
.byte 0
.align 4
.long 0

.LLSDATT1253:
.section .text._ZN7MyClass19non_throwing_methodEv,"axG",@progbits,_ZN7MyClass19non_throwing_methodEv,comdat
.size _ZN7MyClass19non_throwing_methodEv, .-_ZN7MyClass19non_throwing_methodEv
.section .text._ZN7MyClass15throwing_methodEv,"axG",@progbits,_ZN7MyClass15throwing_methodEv,comdat
.align 2
.weak _ZN7MyClass15throwing_methodEv
.type _ZN7MyClass15throwing_methodEv, @function

使用 throw():

_ZN7MyClass19non_throwing_methodEv:
.LFB1253:
.loc 2 13 0
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
.cfi_lsda 0x3,.LLSDA1253
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq __gcov0._ZN7MyClass19non_throwing_methodEv(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv(%rip)
.loc 2 17 0
movq -8(%rbp), %rax
movq %rax, %rdi
.LEHB0:
call _ZN7MyClass15throwing_methodEv
.LEHE0:
movq __gcov0._ZN7MyClass19non_throwing_methodEv+8(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv+8(%rip)
.loc 2 22 0
jmp .L3
.L8:
movq %rax, %rdx
movq __gcov0._ZN7MyClass19non_throwing_methodEv+16(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv+16(%rip)
.loc 2 19 0
movq %rdx, %rax
movq %rax, %rdi
call __cxa_begin_catch
movq __gcov0._ZN7MyClass19non_throwing_methodEv+24(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv+24(%rip)
.LEHB1:
call __cxa_end_catch
.LEHE1:
movq __gcov0._ZN7MyClass19non_throwing_methodEv+32(%rip), %rax
addq $1, %rax
movq %rax, __gcov0._ZN7MyClass19non_throwing_methodEv+32(%rip)
.loc 2 22 0
jmp .L3
.L9:
cmpq $-1, %rdx
je .L7
movq __gcov0._ZN7MyClass19non_throwing_methodEv+48(%rip), %rdx
addq $1, %rdx
movq %rdx, __gcov0._ZN7MyClass19non_throwing_methodEv+48(%rip)
movq %rax, %rdi
.LEHB2:
call _Unwind_Resume
.L7:
movq __gcov0._ZN7MyClass19non_throwing_methodEv+40(%rip), %rdx
addq $1, %rdx
movq %rdx, __gcov0._ZN7MyClass19non_throwing_methodEv+40(%rip)
.loc 2 13 0
movq %rax, %rdi
call __cxa_call_unexpected
.LEHE2:
.L3:
.loc 2 22 0
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1253:
.globl __gxx_personality_v0
.section .gcc_except_table._ZN7MyClass19non_throwing_methodEv,"aG",@progbits,_ZN7MyClass19non_throwing_methodEv,comdat
.align 4
.LLSDA1253:
.byte 0xff
.byte 0x3
.uleb128 .LLSDATT1253-.LLSDATTD1253
.LLSDATTD1253:
.byte 0x1
.uleb128 .LLSDACSE1253-.LLSDACSB1253
.LLSDACSB1253:
.uleb128 .LEHB0-.LFB1253
.uleb128 .LEHE0-.LEHB0
.uleb128 .L8-.LFB1253
.uleb128 0x1
.uleb128 .LEHB1-.LFB1253
.uleb128 .LEHE1-.LEHB1
.uleb128 .L9-.LFB1253
.uleb128 0x3
.uleb128 .LEHB2-.LFB1253
.uleb128 .LEHE2-.LEHB2
.uleb128 0
.uleb128 0
.LLSDACSE1253:
.byte 0x1
.byte 0
.byte 0x7f
.byte 0
.align 4
.long 0

.LLSDATT1253:
.byte 0
.section .text._ZN7MyClass19non_throwing_methodEv,"axG",@progbits,_ZN7MyClass19non_throwing_methodEv,comdat
.size _ZN7MyClass19non_throwing_methodEv, .-_ZN7MyClass19non_throwing_methodEv
.section .text._ZN7MyClass15throwing_methodEv,"axG",@progbits,_ZN7MyClass15throwing_methodEv,comdat
.align 2
.weak _ZN7MyClass15throwing_methodEv
.type _ZN7MyClass15throwing_methodEv, @function

最佳答案

看来我要过一会儿再回答我自己的问题。

throw()noexcept 之间的区别(从 C++11 到 C++17)非常大。

在运行时,如果异常将函数标记为throw() 调用堆栈展开到函数的调用者,然后调用 std::unexpected 来执行意外处理程序,最后(默认行为)程序由 std::terminate.

对于 C++11,noexcept 运行时行为略有不同:调用堆栈只是可能在程序执行终止之前展开。没有调用 std::unexpected。通常编译器只调用 std::terminate 而不会展开。

就是这样,这就是为什么 throw()noexcept 函数生成不同的汇编代码以及代码覆盖工具测量不同结果的原因。

幸运的是,从 C++17 开始,就不会再有惊喜了,因为动态异常规范已被弃用并从标准中删除,而 throw() 规范仍然有效,但它与 noexcept(true).

关于c++ - GCC/GCOV 为使用 throw()/noexcept 的函数生成的不同分支覆盖,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43278010/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com