How to determine if an object is an instance of certain derived C++ class from a pointer to a base class in GDB?
Asked Answered
R

5

62

I'm debugging a C++ program with GDB.

I have a pointer to an object of certain class. The pointer is declared to be of some super class which is extended by several sub-classes.

There is no fields in the object to specify the precise class type of this object but some virtual functions (e.g. bool is_xxx()) are defined to tell the class type at runtime.

Is there some way to tell the precise class type of an object in GDB without calling these virtual functions. Calling such functions in GDB may generate confusing result when the program is multi-threaded.

Recreate answered 16/12, 2011 at 1:59 Comment(2)
I'm no gdb wiz, but you might be able to traverse the v-table pointer (which is what your base class pointer is literally pointing to) and resolve the name of the functions.Moot
@Moot consulting the v-table is exactly what ptype does under the hood if {set print object on} is active, as Beta pointed out in his/her answerAntispasmodic
F
69

Use ptype. If you use it by itself, you get the declared type of the pointer:

(gdb) ptype ptr
type = class SuperClass {
  // various members
} *

To get the actual type of the object pointed to, set the "print object" variable:

(gdb) set print object on
(gdb) ptype ptr
type = /* real type = DerivedClass * */
class SuperClass {
  // various members
} *
Faizabad answered 16/12, 2011 at 2:28 Comment(4)
ptype didn't work for me (gdb 7.2). After set print object on the class was printed by p *ptr however.Maureen
@PerJohansson It didn't work for me neither. Did you find a better solution?Chook
@PerJohansson ptype works for me on GDB 7.11 after doing set print object on: https://mcmap.net/q/226283/-how-to-determine-if-an-object-is-an-instance-of-certain-derived-c-class-from-a-pointer-to-a-base-class-in-gdbZoa
See here for how to always have set print object onAsuncionasunder
D
23

On my system ptype or whatis also only show the obvious.

(gdb) whatis pObject
type = QObject *

But printing the first entry of the vtable helped me:

(gdb) p /a (*(void ***)pObject)[0]
$4 = 0xb4b4cdf4 <QMessageBox::metaObject() const>

Here the pObject pointed to a QMessageBox which is derived from QObject. This only works if vtable-entry points to a method that is overridden by the derived class.

See also: Print C++ vtables using GDB

Edit: Printing only the pointer to the vtable works more reliable (though the output uses the mangled name and is not so readable):

(gdb) p /a (*(void ***)pObject)
$5 = 0xb4af33a0 <_ZTV11QMessageBox+8>
Destalinization answered 16/12, 2014 at 15:59 Comment(1)
The second entry in the vtable points at the type_info. This is always overriden.Narda
Z
8

GDB 7.11

As of GDB 7.11, GCC 5.3.1, Ubuntu 16.04, doing just:

p *myBase

on something compiled with:

gcc -O0 -ggdb3

may be enough as it already shows:

$1 = {_vptr.MyBase = 0x400c00 <vtable for MyDerived1+16>}

where MyDerived1 is the current derived class we are looking for.

But if you do in addition:

set print object on

the output is even clearer and looks like:

$1 = (MyDerived1) {<MyBase> = {_vptr.MyBase = 0x400c00 <vtable for MyDerived1+16>}, <No data fields>}

This also affects other commands like:

ptype myBase

which shows:

type = /* real type = MyDerived1 * */
class MyBase {
  public:
    virtual int myMethod(void);
} *

instead of:

type = class MyBase {
  public:
    virtual int myMethod(void);
} *

In this case, there was no indication of the derived type without set print object on.

whatis is similarly affected:

(gdb) whatis myBase
type = MyBase *
(gdb) set print object on
(gdb) whatis myBase
type = /* real type = MyDerived1 * */
MyBase *

Test program:

#include <iostream>

class MyBase {
    public:
        virtual int myMethod() = 0;
};

class MyDerived1 : public MyBase {
    public:
        virtual int myMethod() { return 1; }
};

class MyDerived2 : public MyBase {
    public:
        virtual int myMethod() { return 2; }
};

int main() {
    MyBase *myBase;
    MyDerived1 myDerived1;
    MyDerived2 myDerived2;
    myBase = &myDerived1;
    std::cout << myBase->myMethod() << std::endl;
    myBase = &myDerived2;
    std::cout << myBase->myMethod() << std::endl;
}
Zoa answered 5/5, 2016 at 15:14 Comment(0)
R
2

For those who can't get the desired result out of ptype or whatis, you may try to use C++ operator typeid in conjunction with GDB's print.

Here's an example of how to get this working in GDB 9.2:

(gdb) n
19      Base* pb = new Derived{};
(gdb) p typeid(*pb)
$1 = {_vptr.type_info = 0x7ffff7f9bc98 <vtable for __cxxabiv1::__si_class_type_info+16>, __name = 0x555555556008 <typeinfo name for Derived> "7Derived"}
Ruble answered 12/5, 2022 at 18:5 Comment(0)
V
1

You do not need to call the virtual functions, you can just see the address of the virtual function or vtable. Another way is to use RTTI

Vinery answered 16/12, 2011 at 2:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.