Tc14
Tc14

Reputation: 109

g++ Show warnings for uninitialized variables without optimizing out variables

I have the following example program that I want to debug:

#include <iostream>
#include <assert.h>
using namespace std;

int f(int x) {

    assert(false); // something bad happens

    return 2 * x;
}

int main() {

    int a;

    for (int i = 0; i < 5; i++) {
        a++; // a is uninitialized
        cout << a << endl;
    }

    cout << f(1) << endl; 

}

This code has two problems:

  1. the variable a within the loop is uninitialized
  2. the function f causes the program to crash

To detect these problems, I compile with g++ -Wall -Og -g source.cpp and I get the following warning:

source.cpp: In function ‘int main()’:
source.cpp:17:10: warning: ‘a’ may be used uninitialized in this function [-Wmaybe-uninitialized]
   17 |         a++; // a is uninitialized
      |

As this question shows, the flag -Og (or any other optimization flag) is necessary for getting this warning.

When I debug the resulting executable with gdb, it crashes (because of the assert statement) and the backtrace looks something like this:

[...]
#4  0x000055555555528f in f (x=<optimized out>) at source.cpp:7
#5  0x0000555555555322 in main () at source.cpp:21

As you can see, the variable x has been optimized out. This happened because of the -Og flag, as discussed in this question.

Obviously, I don't want this for debugging purposes. But when I remove the -Og flag, the previously mentioned warning won't show up anymore. I now want to find a way to get this warning without having optimized out variables. Is this possible with g++?

I am using g++ version 10.2.0 and gdb version 9.2 on Ubuntu 20.10.

Upvotes: 1

Views: 733

Answers (1)

Employed Russian
Employed Russian

Reputation: 213907

Is this possible with g++?

No. Unfortunately, GCC performs the "is value used uninitialized?" analysis in one of its optimization passes, and disabling optimization also disables that pass.

Contrast this with Clang/LLVM, which had an explicit goal of not making warnings dependent on optimization.

clang++ -Wall -Wextra -c t.cc  # no optimization

t.cc:17:9: warning: variable 'a' is uninitialized when used here [-Wuninitialized]
        a++; // a is uninitialized
        ^
t.cc:14:10: note: initialize the variable 'a' to silence this warning
    int a;
         ^
          = 0
1 warning generated.

As you can see, the variable x has been optimized out.

That is a deficiency in either the g++ (if it didn't emit location info for the parameter), or in gdb (if it didn't decode the info which g++ emitted).

In theory -Og should not degrade your debugging experience, though it obviously did here.

Looking at the readelf -wi for g++-compiled code, I see:

<1><285d>: Abbrev Number: 114 (DW_TAG_subprogram)
    <285e>   DW_AT_external    : 1
    <285e>   DW_AT_name        : f
    <2860>   DW_AT_decl_file   : 1
    <2861>   DW_AT_decl_line   : 5
    <2862>   DW_AT_decl_column : 5
    <2863>   DW_AT_linkage_name: (indirect string, offset: 0x169d): _Z1fi
    <2867>   DW_AT_type        : <0xe4f>
    <286b>   DW_AT_low_pc      : 0x3d
    <2873>   DW_AT_high_pc     : 0x23
    <287b>   DW_AT_frame_base  : 1 byte block: 9c       (DW_OP_call_frame_cfa)
    <287d>   DW_AT_GNU_all_call_sites: 1
    <287d>   DW_AT_sibling     : <0x28e1>
 <2><2881>: Abbrev Number: 115 (DW_TAG_formal_parameter)
    <2882>   DW_AT_name        : x
    <2884>   DW_AT_decl_file   : 1
    <2885>   DW_AT_decl_line   : 5
    <2886>   DW_AT_decl_column : 11
    <2887>   DW_AT_type        : <0xe4f>
    <288b>   DW_AT_location    : 0x2dc (location list)
    <288f>   DW_AT_GNU_locviews: 0x2d8
...

I don't see any definition of 0x2dc or 0x2d8, so it seems to me that this is a problem on the g++ side, but I am not entirely sure how to read location lists. It's possible that g++ does in fact emit required info, and GDB doesn't interpret it as it should.

P.S. lldb shows the same output as GDB:

a.out: t.cc:7: int f(int): Assertion `false' failed.
Process 974666 stopped
* thread #1, name = 'a.out', stop reason = hit program assert
    frame #4: 0x0000555555555205 a.out`f(x=<unavailable>) at t.cc:7:5
   4
   5    int f(int x) {
   6
-> 7        assert(false); // something bad happens
   8
   9        return 2 * x;
   10   }

That is an extra indication that the problem is probably on the g++ side.

Upvotes: 1

Related Questions