LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   GDB showing garbage for C++ member variables on 2 Linux distros, not on OS X/FreeBSD (https://www.linuxquestions.org/questions/programming-9/gdb-showing-garbage-for-c-member-variables-on-2-linux-distros-not-on-os-x-freebsd-772063/)

exscape 11-28-2009 03:53 AM

GDB showing garbage for C++ member variables on 2 Linux distros, not on OS X/FreeBSD
 
This is mostly a repost from Stack Overflow, with some edits to bring it more up to date.

I ran in to a (in my eyes) very strange issue with gdb today, while trying to write a more C++-ish version of the reverse() method of my string class (all for learning, of course). Despite having a proper default constructor that initializes all member variables to 0, they start out as gibberish inside the member reverse() - according to gdb (everything except debugging actually works perfectly) - but not in a bare-bones program that just creates an empty string. Could this have something to do with the fact that reverse() is a member function that creates an instance of its own class? If not, why would it not happen in a bare-bones program?

BTW, before I mention any code... I compile thusly:

Code:

g++ -c -o tests.o tests.cpp -Wall -Werror -DDEBUG=0 -O0 -ggdb3
g++ -c -o string.o string.cpp -Wall -Werror -DDEBUG=0 -O0 -ggdb3
g++ -o tests tests.o string.o -Wall -Werror -DDEBUG=0 -O0 -ggdb3

Code:

Code:

string::string() : buf(NULL), _length(0), _size(0) {
    init();
}

/* This is redundant in this case (right?), but the function is used elsewhere,
  and I added the above initialization list while debugging this. */
void string::init() {
    this->buf = NULL;
    this->_length = 0;
    this->_size = 0;
}

string string::reverse(void) const {
    string rev;
    rev.alloc(this->_length + 1);

    for (size_t i=0; i<this->_length; i++) {
    ...

Here's what I get from running the above through gdb 7.0 (on Linux), with "FEDCBA" as the input string:

Code:

Breakpoint 1, exscape::string::reverse (this=0x7fffffffd580) at string.cpp:368
368                    string rev;
(gdb) next
378                    rev.alloc(this->_length + 1); <<<< not yet executed when we print below!
(gdb) p rev
$1 = {buf = 0x7fffffffd560 "", _length = 140737488344448, _size = 140737488344128}
(gdb) n
380                    for (size_t i=0; i<this->_length; i++) {
(gdb)
381                            rev.buf[this->_length-i-1] = this->buf[i];
380                    for (size_t i=0; i<this->_length; i++) {
(gdb) p rev
$2 = {buf = 0x7fffffffd560 "P\321`", _length = 140737488344448, _size = 140737488344128}
(gdb) n
381                            rev.buf[this->_length-i-1] = this->buf[i];
(gdb)
380                    for (size_t i=0; i<this->_length; i++) {
...
384                    rev._length = this->_length;
(gdb)
386            }
(gdb) p rev
$3 = {buf = 0x7fffffffd560 "P\321`", _length = 140737488344448, _size = 140737488344128}
(gdb) next
main () at tests.cpp:72
(gdb) p r2 <<<< r2 is the name of the variable returned by reverse()
$4 = {buf = 0x60d150 "ABCDEF", _length = 6, _size = 7}

As you can see, the GDB output is just gibberish, but the method works just as expected. I also tried GDB 6.8, 6.6 and 6.3 on Linux, none of which worked.
Note that the supposed values of the variables all all very close the value of this. In the above run, _length = 0x7fffffffd580 = this.
"GNU gdb 6.3.50-20050815 (Apple version gdb-1344)" on OS X Snow Leopard works perfectly, as does gdb 6.1.1 on FreeBSD/amd64, although I doubt that the GDB version is the problem.

Here's sample output from the Mac:
Code:

Breakpoint 1, exscape::string::reverse (this=0x7fff5fbff800) at string.cpp:368
368            string rev;
(gdb) n
378            rev.alloc(this->_length + 1);
(gdb) p rev
$1 = (exscape::string &) @0x7fff5fbff7c0: {
  buf = 0x0,
  _length = 0,
  _size = 0
}

Any advice on what might cause this?

exscape 11-28-2009 04:59 AM

Update: I did a clean Ubuntu Server 9.10 install just to test on another Linux system. Also amd64, same problem.

Code:

serenity@ubuntu:~/test$ gdb --version
GNU gdb (GDB) 7.0-ubuntu
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
serenity@ubuntu:~/test$ gcc --version
gcc (Ubuntu 4.4.1-4ubuntu8) 4.4.1
Copyright (C) 2009 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.

serenity@ubuntu:~/test$ uname -a
Linux ubuntu 2.6.31-14-server #48-Ubuntu SMP Fri Oct 16 15:07:34 UTC 2009 x86_64 GNU/Linux

Code:

Breakpoint 1, exscape::string::reverse (this=0x7fffffffe050) at string.cpp:368
368                        string rev;
(gdb) n
378                        rev.alloc(this->_length + 1);
(gdb) p rev
$1 = {buf = 0x7ffff762e780 "\204*\255", <incomplete sequence \373>, _length = 140737488347088, _size = 140737488346688}


graemef 11-28-2009 08:50 AM

I believe that you need to explicitly call the constructor for the object in you method.

exscape 11-28-2009 09:12 AM

Quote:

Originally Posted by graemef (Post 3772395)
I believe that you need to explicitly call the constructor for the object in you method.

Didn't help, unfortunately:

Code:

    /* Initializes a string to an empty state */
    void string::init(void) {
        std::cerr << "In init() for string " << this << std::endl;
        this->buf = NULL;
        this->_length = 0;
        this->_size = 0;
    }

    /* Copy constructor from const char * */
    string::string(const char *in = NULL) : buf(NULL), _length(0), _size(0) {
        std::cerr << "In const char* copy/default constructor for string " << this << std::endl;
        this->init();       
        this->append(in);
    }
   
    // Copy constructor from another string instance
    string::string(const string &in) : buf(NULL), _length(0), _size(0) {
        std::cerr << "In const string & copy instructor" << std::endl;
        this->init();
        this->append(in.c_str());
    }

string string::reverse(void) const {
    string rev(0);
    ...

... still causes nonsense variables.
I stumbled upon something VERY strange though, that I haven't been able to wrap my head around yet... If I remove the "const string &in" constructor, IT WORKS. It is however, according to the cerr statement, NEVER CALLED. :confused:
No matter how broken the code inside it may be, HOW can it possibly affect the program when it is not called?!

Edit: Here's the calling code, should it matter:
Code:

int main() {
        exscape::string s = "ABCDEF";
        std::cout << "Reversed: " << s.reverse() << std::endl;

I've cut the code down from 1151 lines in 3 files to 153 lines in 1 file to remove things that cannot affect this bug, and during the cutting down I noticed that the const string& constructor was the culprit, somehow...

Edit: What the hell? Removing the const string& constructor in the "real class", i.e. the whole deal, did NOT work.
I'm very confused right now...

graemef 11-28-2009 09:22 AM

To explicitly call the default constructor I'd do:
Code:

string rev = string::string();
When you exit the method you will call the copy constructor to make a copy of your local object to the return object.

exscape 11-28-2009 09:38 AM

Quote:

Originally Posted by graemef (Post 3772433)
To explicitly call the default constructor I'd do:
Code:

string rev = string::string();
When you exit the method you will call the copy constructor to make a copy of your local object to the return object.

Didn't help. :(
Also, the copy constructor doesn't seem to be called, as the cerr statement in it is never executed, while it is for the other constructors.

Code:

$ ./a.out
Reversed: FEDCBA

With cerr statements in all the constructors and init, I get this from the same calling code:

Code:

$ ./a.out
In const char* copy constructor for string 0x7fff19b60840
In init() for string 0x7fff19b60840
In default constructor for string 0x7fff19b60860
In init() for string 0x7fff19b60860
Reversed: FEDCBA


smeezekitty 11-28-2009 06:20 PM

how about full code?

exscape 11-29-2009 01:47 AM

Quote:

Originally Posted by smeezekitty (Post 3772768)
how about full code?

The full version is >1100 lines. The cut-down version is probably useless since the workaround that fixes it doesn't fix the full version.
Anyway, the full sources for the current commit are here:
http://github.com/exscape/CPlusPlus/...9a15b1d/string
I know very well of several things I could do better BTW (a bit embarassed about some parts of the code) - that's the second class I wrote, and my code quality is improving quite rapidly. :)

smeezekitty 11-29-2009 11:03 AM

actually it looks pretty clean (compared to mine).
one thing is why are you using calloc instead of new ?

exscape 11-29-2009 11:04 AM

Quote:

Originally Posted by smeezekitty (Post 3773385)
one thing is why are you using calloc instead of new ?

Because I need realloc.

smeezekitty 11-29-2009 11:32 AM

compiled an ran on windows with mingw and it seems to be ok.
the only things i can think of at this point is a bug in the linux kernel or a bug in gcc for linux (btw what versions?):
Code:

(gdb) next
375    for (size_t i=0; i<this->_length; i++) {
(gdb) prev
Undefined command: "prev".  Try "help".
(gdb) p rev
$3 = (string *) 0x22fe00
(gdb) p rev
$4 = (string *) 0x22fe00
(gdb) p *rev
$5 = {buf = 0x8d2bf0 "", _length = 0, _size = 7}
(gdb) next
376    rev.buf[this->_length-i-1] = this->buf[i];
(gdb) p *rev
$6 = {buf = 0x8d2bf0 "", _length = 0, _size = 7}
(gdb) next
375    for (size_t i=0; i<this->_length; i++) {
(gdb) p *rev
$7 = {buf = 0x8d2bf0 "", _length = 0, _size = 7}
(gdb) next
376    rev.buf[this->_length-i-1] = this->buf[i];
(gdb) next
375    for (size_t i=0; i<this->_length; i++) {
(gdb) p *rev
$8 = {buf = 0x8d2bf0 "", _length = 0, _size = 7}
(gdb)


exscape 11-29-2009 11:35 AM

Thanks.
Yeah, it seems to be related to either the Linux kernel or something in the building toolchain (which I guess is more likely).

The Ubuntu gcc/gdb versions are posted above, the ones I use on my main box are:
Quote:

$ gcc --version
gcc (Gentoo 4.3.4 p1.0, pie-10.1.5) 4.3.4
Copyright (C) 2008 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.

$ gdb --version

warning: Can not parse XML syscalls information; XML support was disabled at compile time.
GNU gdb (Gentoo 7.0 p1) 7.0
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.

smeezekitty 11-29-2009 11:38 AM

Heres what I used:
Code:


C:\MinGW\bin>gcc --version
gcc (GCC) 3.4.5 (mingw-vista special r3)
Copyright (C) 2004 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.


C:\MinGW\bin>gdb --version
GNU gdb 5.2.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32".

C:\MinGW\bin>

Also what is your expected output? i am not trying to be a pain in your behind, i am just trying to track down the problem.

exscape 11-29-2009 11:50 AM

Quote:

Originally Posted by smeezekitty (Post 3773418)
[/code]
Also what is your expected output? i am not trying to be a pain in your behind, i am just trying to track down the problem.

The program itself runs as it should, it's the GDB output in "print rev" that is way off. It should show buf=0x0, _length=0 and _size=0 since they have just been initialized, and no code has run since then.
Debugging the functions is impossible on my Linux boxes due to this.

ntubski 11-29-2009 02:43 PM

It looks like gdb is using the wrong address for rev. This gdb script show the problem:
Code:

break exscape::string::reverse
run
advance exscape::string::alloc
set $real_rev=this
finish
advance 379
printf "===============================\n"
printf "WHAT GDB THINKS IS REV:\n"
print &rev
print rev
printf "THE REAL REV:\n"
print $real_rev
print *$real_rev

You can run it like this:
Code:

~/src/CPlusPlus/string$ gdb '--eval-command=source show-rev.gdb' ./tests
...
===============================
WHAT GDB THINKS IS REV:
$1 = (exscape::string *) 0xbffff2a0
$2 = {buf = 0xbffff33c "¨0\005\b", _length = 3221222216, _size = 3221222772}
---Type <return> to continue, or q <return> to quit---
THE REAL REV:
$3 = (exscape::string * const) 0xbffff33c
$4 = {buf = 0x80530a8 "ABCDEF", _length = 0, _size = 7}



All times are GMT -5. The time now is 12:47 AM.