public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* [NEED HELP][GCOV] uncoverage virtual destructor
@ 2023-03-02 12:42 Nguyen Duc Sy
  2023-03-02 16:26 ` Jonathan Wakely
  0 siblings, 1 reply; 7+ messages in thread
From: Nguyen Duc Sy @ 2023-03-02 12:42 UTC (permalink / raw)
  To: gcc-help

[-- Attachment #1: Type: text/plain, Size: 4191 bytes --]

Hello GCC-Help
I'm trying to get code coverage with a sample code:
#include <iostream>
using namespace std;
class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    virtual ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }

};

int main()
{
    Derived1 *b = new Derived1();
    delete b;
}

I tried to build and get code coverage with the following steps:
>> g++ -std=c++11 -fprofile-arcs -ftest-coverage  -g test.cpp -o test
>> ./test
>> gcov -b -f test.cpp

And I got the test.cpp.gcov file,

        -:    0:Source:test.cpp
        -:    0:Graph:test.gcno
        -:    0:Data:test.gcda
        -:    0:Runs:1
        -:    1:#include <iostream>
        -:    2:using namespace std;
        -:    3:
        -:    4:class Base
        -:    5:{
        -:    6:public:
function _ZN4BaseC2Ev called 1 returned 100% blocks executed 100%
        1:    7:    Base(){
        1:    8:        cout << "Base Constructor Called\n";
call    0 returned 100%
        1:    9:    }
        -:   10:  
       2*:   11:    virtual ~Base(){
        1:   12:        cout << "Base Destructor called\n";
       1*:   13:    }
------------------
_ZN4BaseD0Ev:
function _ZN4BaseD0Ev called 0 returned 0% blocks executed 0%
    #####:   11:    virtual ~Base(){
        -:   12:        cout << "Base Destructor called\n";
    #####:   13:    }
call    0 never executed
call    1 never executed
------------------
_ZN4BaseD2Ev:
function _ZN4BaseD2Ev called 1 returned 100% blocks executed 100%
        2:   11:    virtual ~Base(){
        1:   12:        cout << "Base Destructor called\n";
call    0 returned 100%
        1:   13:    }
------------------
        -:   14:};
        -:   15:
        -:   16:class Derived1: public Base
        -:   17:{
        -:   18:public:
function _ZN8Derived1C2Ev called 1 returned 100% blocks executed 80%
        1:   19:    Derived1(){
call    0 returned 100%
call    1 never executed
        1:   20:        cout << "Derived constructor called\n";
call    0 returned 100%
branch  1 taken 100% (fallthrough)
branch  2 taken 0% (throw)
        1:   21:    }
        2:   22:    ~Derived1(){
        1:   23:        cout << "Derived destructor called\n";
        2:   24:    }
------------------
_ZN8Derived1D0Ev:
function _ZN8Derived1D0Ev called 1 returned 100% blocks executed 100%
        1:   22:    ~Derived1(){
        -:   23:        cout << "Derived destructor called\n";
        1:   24:    }
call    0 returned 100%
call    1 returned 100%
------------------
_ZN8Derived1D2Ev:
function _ZN8Derived1D2Ev called 1 returned 100% blocks executed 100%
        1:   22:    ~Derived1(){
call    0 returned 100%
        1:   23:        cout << "Derived destructor called\n";
call    0 returned 100%
        1:   24:    }
------------------
        -:   25:
        -:   26:};
        -:   27:
function main called 1 returned 100% blocks executed 88%
        1:   28:int main()
        -:   29:{
        1:   30:    Derived1 *b = new Derived1();
call    0 returned 100%
call    1 returned 100%
branch  2 taken 100% (fallthrough)
branch  3 taken 0% (throw)
call    4 never executed
        1:   31:    delete b;
branch  0 taken 100% (fallthrough)
branch  1 taken 0%
call    2 returned 100%
        1:   32:}

but I don't understand why this line is uncovered:
function _ZN4BaseD0Ev called 0 returned 0% blocks executed 0%
    #####:   11:    virtual ~Base(){
        -:   12:        cout << "Base Destructor called\n";
    #####:   13:    }

Could this be a bug in G++/Gcov?
Version
g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Please help me confirm the isssue ?
Thank you

Best regards,
Sy Nguyen

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [NEED HELP][GCOV] uncoverage virtual destructor
  2023-03-02 12:42 [NEED HELP][GCOV] uncoverage virtual destructor Nguyen Duc Sy
@ 2023-03-02 16:26 ` Jonathan Wakely
  2023-03-03  1:48   ` Nguyen Duc Sy
  0 siblings, 1 reply; 7+ messages in thread
From: Jonathan Wakely @ 2023-03-02 16:26 UTC (permalink / raw)
  To: Nguyen Duc Sy; +Cc: gcc-help

On Thu, 2 Mar 2023 at 12:43, Nguyen Duc Sy wrote:
> but I don't understand why this line is uncovered:
> function _ZN4BaseD0Ev called 0 returned 0% blocks executed 0%
>     #####:   11:    virtual ~Base(){
>         -:   12:        cout << "Base Destructor called\n";
>     #####:   13:    }
>
> Could this be a bug in G++/Gcov?

No, your program never uses the D0 destructor variant. Try:

Base* b = new Base();
delete b;

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [NEED HELP][GCOV] uncoverage virtual destructor
  2023-03-02 16:26 ` Jonathan Wakely
@ 2023-03-03  1:48   ` Nguyen Duc Sy
  2023-03-03  8:08     ` Jonathan Wakely
  0 siblings, 1 reply; 7+ messages in thread
From: Nguyen Duc Sy @ 2023-03-03  1:48 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-help

[-- Attachment #1: Type: text/plain, Size: 1126 bytes --]

Hello Mr. Jonathan Wakely

Basically, my program only creates a Base class to be inherited, and only uses the Derived1 class in the main program. What is the purpose of the D0 destructor variant that G++ generates?
It is clear that the content of the ~Base() function, which is
        cout << "Base Destructor called\n";
 , is still executed.

Thank you for your support
Best regards,
Sy Nguyen
________________________________
From: Jonathan Wakely <jwakely.gcc@gmail.com>
Sent: Thursday, March 2, 2023 11:26 PM
To: Nguyen Duc Sy <sy.nguyen-duc@banvien.com.vn>
Cc: gcc-help@gcc.gnu.org <gcc-help@gcc.gnu.org>
Subject: Re: [NEED HELP][GCOV] uncoverage virtual destructor

On Thu, 2 Mar 2023 at 12:43, Nguyen Duc Sy wrote:
> but I don't understand why this line is uncovered:
> function _ZN4BaseD0Ev called 0 returned 0% blocks executed 0%
>     #####:   11:    virtual ~Base(){
>         -:   12:        cout << "Base Destructor called\n";
>     #####:   13:    }
>
> Could this be a bug in G++/Gcov?

No, your program never uses the D0 destructor variant. Try:

Base* b = new Base();
delete b;

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [NEED HELP][GCOV] uncoverage virtual destructor
  2023-03-03  1:48   ` Nguyen Duc Sy
@ 2023-03-03  8:08     ` Jonathan Wakely
  2023-03-03 12:39       ` Nguyen Duc Sy
  0 siblings, 1 reply; 7+ messages in thread
From: Jonathan Wakely @ 2023-03-03  8:08 UTC (permalink / raw)
  To: Nguyen Duc Sy; +Cc: gcc-help

[-- Attachment #1: Type: text/plain, Size: 1337 bytes --]

On Fri, 3 Mar 2023, 01:48 Nguyen Duc Sy, <sy.nguyen-duc@banvien.com.vn>
wrote:

> Hello Mr. Jonathan Wakely
>
> Basically, my program only creates a Base class to be inherited, and only
> uses the Derived1 class in the main program.
>

And the coverage checks are telling you there is unused code, which is
correct. You have defined a type that can be used on its own or as a base
class, but then you don't use it on its own. The code for using it on its
own is never executed.

Make it an abstract class (with a pure virtual function) if it should only
be usable as a base class.

What is the purpose of the D0 destructor variant that G++ generates?
>

It destroys the object and then calls operator delete(this, sizeof(Base)).
That is only used when Base is a complete object, not a base class. To
destroy a base class the D2 variant is used.


It is clear that the content of the ~Base() function, which is
>         cout << "Base Destructor called\n";
>  , is still executed.
>


There are two versions of the destructor code, and the message is only
printed once, so obviously only one of them executes.

Either test destroying a non-base-class object of type Base to use the D0
destructor, or tell the compiler that Base can only be a base class by
making it abstract, or accept that the D0 destructor will not have coverage
data.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [NEED HELP][GCOV] uncoverage virtual destructor
  2023-03-03  8:08     ` Jonathan Wakely
@ 2023-03-03 12:39       ` Nguyen Duc Sy
  2023-03-03 16:12         ` Xi Ruoyao
  0 siblings, 1 reply; 7+ messages in thread
From: Nguyen Duc Sy @ 2023-03-03 12:39 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-help

[-- Attachment #1: Type: text/plain, Size: 2176 bytes --]

Hello Mr. Jonathan Wakely

Thank you so much for your help.
As per your explanation, I tried to understand. Whether, in all cases, if I use a base class to be inherited, does the destructor always generate two variants (D0 and D2)?
But when I change the code as below:
    virtual ~Base(){
        cout << "Base Destructor called\n";
    }
to
    ~Base(){
        cout << "Base Destructor called\n";
    }
the D0 destructor variant doesn't generate.
Could you please explain more?

Thank you!
Best regards
________________________________
From: Jonathan Wakely <jwakely.gcc@gmail.com>
Sent: Friday, March 3, 2023 3:08 PM
To: Nguyen Duc Sy <sy.nguyen-duc@banvien.com.vn>
Cc: gcc-help <gcc-help@gcc.gnu.org>
Subject: Re: [NEED HELP][GCOV] uncoverage virtual destructor



On Fri, 3 Mar 2023, 01:48 Nguyen Duc Sy, <sy.nguyen-duc@banvien.com.vn<mailto:sy.nguyen-duc@banvien.com.vn>> wrote:
Hello Mr. Jonathan Wakely

Basically, my program only creates a Base class to be inherited, and only uses the Derived1 class in the main program.

And the coverage checks are telling you there is unused code, which is correct. You have defined a type that can be used on its own or as a base class, but then you don't use it on its own. The code for using it on its own is never executed.

Make it an abstract class (with a pure virtual function) if it should only be usable as a base class.

What is the purpose of the D0 destructor variant that G++ generates?

It destroys the object and then calls operator delete(this, sizeof(Base)). That is only used when Base is a complete object, not a base class. To destroy a base class the D2 variant is used.


It is clear that the content of the ~Base() function, which is
        cout << "Base Destructor called\n";
 , is still executed.


There are two versions of the destructor code, and the message is only printed once, so obviously only one of them executes.

Either test destroying a non-base-class object of type Base to use the D0 destructor, or tell the compiler that Base can only be a base class by making it abstract, or accept that the D0 destructor will not have coverage data.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [NEED HELP][GCOV] uncoverage virtual destructor
  2023-03-03 12:39       ` Nguyen Duc Sy
@ 2023-03-03 16:12         ` Xi Ruoyao
       [not found]           ` <TYCPR01MB94668FBD9D4BBD05065631CAA5B79@TYCPR01MB9466.jpnprd01.prod.outlook.com>
  0 siblings, 1 reply; 7+ messages in thread
From: Xi Ruoyao @ 2023-03-03 16:12 UTC (permalink / raw)
  To: Nguyen Duc Sy, Jonathan Wakely; +Cc: gcc-help

On Fri, 2023-03-03 at 12:39 +0000, Nguyen Duc Sy wrote:

> As per your explanation, I tried to understand. Whether, in all cases,
> if I use a base class to be inherited, does the destructor always
> generate two variants (D0 and D2)?

Maybe 3 variants.  See
	http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.

D0 is the deleting destructor, D1 is the complete object destructor, D2
is the base object destructor.

The base object destructor of a class T T runs the destructors for non-
static data members of T and non-virtual direct base classes of T.

The complete object destructor of a class T in addition to the actions
required of a base object destructor, runs the destructors for the
virtual base classes of T.

The deleting destructor of a class T in addition to the actions required
of a complete object destructor, calls the appropriate deallocation
function (i.e,. operator delete) for T.

> But when I change the code as below:
>     virtual ~Base(){
>         cout << "Base Destructor called\n";
>     }
> to
>     ~Base(){
>         cout << "Base Destructor called\n";
>     }
> the D0 destructor variant doesn't generate.

"A function defined within a class definition is an inline function." 
And, "an inline function or variable shall be defined in every
translation unit in which it is odr-used and shall have exactly the same
definition in every case" [dcl.inline].  So the compiler can avoid
generating the destructor as it's not used in the current TU.  If it's
used in another TU, a valid definition will be in that TU anyway.

But when you make it virtual, the compiler need to fill the address of
the destructor into the vtable:

_ZTV4Base:
    .quad   0
    .quad   _ZTI4Base
    .quad   _ZN4BaseD1Ev
    .quad   _ZN4BaseD0Ev

So the compiler generates the code for the destructor in all TUs as it
*might* be used through vtable from another TU.

However, as Base itself does not have a base class, I can't figure out a
valid way to invoke it through the vtable without including the
definition of the destructor in the TU.  So it seems valid to avoid
generating the code for _ZN4BaseD0Ev unless it's used in the TU.  Note
that it's a weak symbol so we can rely on the linker to fill 0 into
vtable if it's not use at all.  But maybe I'm missing something here
(I'm not a language lawyer).

Anyway, AFAIK the compilers tend to ignore "inline" in "inline virtual"
functions.

-- 
Xi Ruoyao <xry111@xry111.site>
School of Aerospace Science and Technology, Xidian University

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [NEED HELP][GCOV] uncoverage virtual destructor
       [not found]           ` <TYCPR01MB94668FBD9D4BBD05065631CAA5B79@TYCPR01MB9466.jpnprd01.prod.outlook.com>
@ 2023-03-07  5:20             ` Xi Ruoyao
  0 siblings, 0 replies; 7+ messages in thread
From: Xi Ruoyao @ 2023-03-07  5:20 UTC (permalink / raw)
  To: Nhu Vo, Jonathan Wakely
  Cc: gcc-help, Chan Le, Vu Phan, Viet Ngo, Duc La, Duy Pham,
	Dinh Thanh Truc, Sy Nguyen Duc

On Tue, 2023-03-07 at 05:18 +0000, Nhu Vo wrote:
> According to your explanation about D0, D1, D2, we have some questions
> as follows:
>  
> 1. We understood that D0 will call D1, D1 will call D2, is it correct?
> (as image below)

It's implementation defined, D0 can either call D1 or inline D1 (i. e.
perform the action of D1 itself).

-- 
Xi Ruoyao <xry111@xry111.site>
School of Aerospace Science and Technology, Xidian University

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-03-07  5:20 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-02 12:42 [NEED HELP][GCOV] uncoverage virtual destructor Nguyen Duc Sy
2023-03-02 16:26 ` Jonathan Wakely
2023-03-03  1:48   ` Nguyen Duc Sy
2023-03-03  8:08     ` Jonathan Wakely
2023-03-03 12:39       ` Nguyen Duc Sy
2023-03-03 16:12         ` Xi Ruoyao
     [not found]           ` <TYCPR01MB94668FBD9D4BBD05065631CAA5B79@TYCPR01MB9466.jpnprd01.prod.outlook.com>
2023-03-07  5:20             ` Xi Ruoyao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).