[patch/testsuite/c++] test script for PR c++/186

Michael Elizabeth Chastain
2003-11-23T22:05:13Z

This is a new test script. It's for PR gdb/186:

http://sources.redhat.com/gdb/bugs/186
gdb have problems with C++ casting

Here is a summary of the code:

class A;
class B : public A;

int main (void)
{
B beta, *beta_p;
beta_p = &beta;
...
} // breakpoint here

And here is a summary of the *.exp file:

(gdb) break "breakpoint here"
(gdb) print beta
... okay ...
(gdb) print * beta_p
... gdb prints bogus data ...

The issue is that the user is accessing a destroyed object through a
pointer. Because the object has been destroyed, its vptr has been
degraded to its most primitive base class. gdb gets confused and prints
incorrect data, which is always wrong.

Testing: I tested this on native i686-pc-linux-gnu with:

gcc 2.95.3 -gdwarf-2
gcc 2.95.3 -gstabs+
gcc 3.3.2 -gdwarf-2
gcc 3.3.2 -gstabs+

The results are all PASS and KFAIL, with no FAIL.

I'll put this up for a few days for comment, especially from David C.
Then I'll commit it.

Michael C

===

2003-11-23 Michael Chastain <mec.gnu@mindspring.com>

New test case for PR c++/186.
* gdb.cp/gdb186.cc: New file.
* gdb.cp/gdb186.exp: New file.

===

/* This testcase is part of GDB, the GNU debugger.

Copyright 2003 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

Please email any bugs, comments, and/or additions to this file to:
bug-gdb@prep.ai.mit.edu */

struct A
{
virtual ~A ();
int a1;
};

A::~A()
{
;
}

struct B : public A
{
virtual ~B ();
int b1;
int b2;
};

B::~B()
{
;
}

void marker1 (A *)
{
;
}

int main (void)
{
A *alpha_p;
B beta, *beta_p;
beta.a1 = 100;
beta.b1 = 200;
beta.b2 = 201;
alpha_p = &beta;
beta_p = (B *) alpha_p;
marker1 (alpha_p);
marker1 (beta_p);
return 0; // marker return 0
} // marker close brace

===

# Copyright 2003 Free Software Foundation, Inc.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# Test for PR gdb/186.
#
# This is a problem with C++ casting on a dead object. The object's
# type (its vptr) has been degraded by its destructors.

if $tracelevel then {
strace $tracelevel
}

if { [skip_cplus_tests] } { continue }

set prms_id 0
set bug_id 0

set testfile "gdb186"
set srcfile ${testfile}.cc
set binfile ${objdir}/${subdir}/${testfile}

if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail."
}

gdb_exit
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}

if ![runto_main] then {
perror "couldn't run to main"
continue
}

gdb_test "break [gdb_get_line_number "marker return 0"]" \
"Breakpoint.*at.* file .*" ""

gdb_test "continue" "Breakpoint .* at .*" ""

# This part should be simple and good.

gdb_test "print beta" \
"= {.*a1 = 100.*b1 = 200.*b2 = 201}" \
"print beta at marker return 0"

gdb_test "print * beta_p" \
"= {.*a1 = 100.*b1 = 200.*b2 = 201}" \
"print * beta_p at marker return 0"

gdb_test "print * (B *) alpha_p" \
"= {.*a1 = 100.*b1 = 200.*b2 = 201}" \
"print * (B *) alpha_p at marker return 0"

gdb_test "print * alpha_p" \
"= {.*a1 = 100.*}" \
"print * alpha_p at marker return 0"

# All that again, at the close brace.
#
# This is where the real problem happens. "beta" has been destroyed!
# The gcc code for B::~B changes the type (the vptr) of "beta" from
# "B" to "A" so that B::~B can call A::~A. Every C++ compiler has to
# do something like this, in case A::~A calls a virtual function.
#
# It would also be legal for a C++ compiler to put some kind of sentinel
# value in beta.vptr so that it's not even an "A" any more. But I am
# not seeing that with gcc.
#
# So, the user is trying to print a structure of type "B", but there is
# only an "A" in memory, and the user might not even realize that.

gdb_test "break [gdb_get_line_number "marker close brace"]" \
"Breakpoint.*at.* file .*" ""

gdb_test "continue" "Breakpoint .* at .*" ""

gdb_test_multiple "print beta" "print beta at marker close brace" {
-re "= {.*a1 = 100.*b1 = 200.*b2 = 201}\r\n$gdb_prompt " {
# the data fields should still be okay even though beta is dead
pass "print beta at marker close brace"
}
}

# "* beta_p" has static type "B", but there is probably only an "A"
# in memory after B::~B does its thing. The user may not realize
# this so gdb should be careful to be informative here.
#
# TODO: add another PASS case for this depending on what David C
# thinks the correct output should be.

gdb_test_multiple "print * beta_p" "print * beta_p at marker close brace" {
-re "= {.*a1 = 100.*b1 = 200.*b2 = 201}\r\n$gdb_prompt $" {
# This is not happening, but it would be okay.
pass "print * beta_p at marker close brace"
}
-re "= {.*a1 = 100}\r\n$gdb_prompt $" {
# This is not happening, but it would be okay,
# although a little surprising.
pass "print * beta_p at marker close brace"
}
-re "= {.*a1 = 100.*b1 = .*b2 = .*}\r\n$gdb_prompt $" {
# gcc 2.95.3 -gstabs+
# gcc 3.3.2 -gdwarf-2
# gcc 3.3.2 -gstabs+
# Someone might argue that gdb is not buggy here but I think it is.
kfail "gdb/186" "print * beta_p at marker close brace #1"
}
-re "= {.*a1 = .*b1 = .*b2 = .*}\r\n$gdb_prompt $" {
# gcc 2.95.3 -gdwarf-2
# gdb does not even get the value of a1 right!
kfail "gdb/186" "print * beta_p at marker close brace #2"
}
}

# Another way to pose the same question.

gdb_test_multiple "print * (B *) alpha_p " "print * (B *) alpha_p at marker close brace" {
-re "= {.*a1 = 100.*b1 = 200.*b2 = 201}\r\n$gdb_prompt $" {
# This is not happening, but it would be okay.
pass "print * (B *) alpha_p at marker close brace"
}
-re "= {.*a1 = 100}\r\n$gdb_prompt $" {
# This is not happening, but it would be okay,
# although a little surprising.
pass "print * (B *) alpha_p at marker close brace"
}
-re "= {.*a1 = 100.*b1 = .*b2 = .*}\r\n$gdb_prompt $" {
# gcc 2.95.3 -gstabs+
# gcc 3.3.2 -gdwarf-2
# gcc 3.3.2 -gstabs+
# Someone might argue that gdb is not buggy here but I think it is.
kfail "gdb/186" "print * (B *) alpha_p at marker close brace #1"
}
-re "= {.*a1 = .*b1 = .*b2 = .*}\r\n$gdb_prompt $" {
# gcc 2.95.3 -gdwarf-2
# gdb does not even get the value of a1 right!
kfail "gdb/186" "print * (B *) alpha_p at marker close brace #2"
}
}

# This should work better, as long as beta is still an "A" at least.

gdb_test_multiple "print * alpha_p" "print * alpha_p at marker close brace" {
-re "= {.*a1 = 100.*b1 = 200.*b2 = 201}\r\n$gdb_prompt $" {
# this should not happen but if it did, it would be okay
pass "print * alpha_p at marker close brace"
}
-re "= {.*a1 = .*b1 = .*b2 = .*}\r\n$gdb_prompt $" {
# if gdb prints any values and they are WRONG, that is not okay
kfail "gdb/186" "print * alpha_p at marker close brace #1"
}
-re "= {.*a1 = 100.*}\r\n$gdb_prompt $" {
# gcc 2.95.3 -gstabs+
# gcc 3.3.2 -gdwarf-2
# gcc 3.3.2 -gstabs+
# this is okay
pass "print * alpha_p at marker close brace"
}
-re "= {.*a1 = .*}\r\n$gdb_prompt $" {
# gcc 2.95.3 -gdwarf-2
kfail "gdb/186" "print * alpha_p at marker close brace #2"
}
}