Search code examples
c++debugginggdb

How Do I get correct "this" ptr information for GDB function breakpoint usable for conditional breakpoint?


I want to setup breakpoints on a C++ constructor, destructor or method and see the correct "this" pointer for the object this is invoked for.

Unfortunately when the breakpoint hits, the "this" information stated by GDB is not the expected one. If the constructor, destructor or method contains at least one line, and single-step is used once after the breakpoint has been hit, then the "this" information appears to change and is then showing the expected value.

However

  • constructor, destructor or method may also be empty (or set to default in the first 2 cases) so that single-stepping might not to be possible.
  • It is also not possible out of the box to configure commands to be executed on hitting the breakpoint that execute the single-step and then continue normal execution since a "single-step" or "next" command breaks further execution of provided command sequence, preventing a "continue" statement from getting executed
  • Also I believe since the "this" value initially is wrong it is most likely not possible to set up a conditional breakpoint and stop only in case the call is made for an object which address is known (= should be in "this" pointer).

Simple example code MyClass.cpp:

#include <iostream>

class MyClass
{
public:
    MyClass() {
        std::cout << "MyClass() constructor, this = " << this << std::endl;
    }

    void someMethod() {
        std::cout << "someMethod(), this = " << this << std::endl;
    }

    ~MyClass() {
        std::cout << "MyClass() destructor, this = " << this << std::endl;
    }
};

int
main(int argc, char **argv)
{
    MyClass *myClass = new MyClass();
    std::cout << "MyClass located at = " << myClass << std::endl;
    myClass->someMethod();
    delete myClass;
}

Compilation done with

g++ -std=c++14 -g  -o MyClass MyClass.cpp

Execution with GDB:

$ gdb ./MyClass
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
...
Reading symbols from ./MyClass...
(gdb) b MyClass::MyClass
Breakpoint 1 at 0x1352: file MyClass.cpp, line 6.
(gdb) b MyClass::~MyClass
Breakpoint 2 at 0x13ee: file MyClass.cpp, line 14.
(gdb) b MyClass::someMethod
Breakpoint 3 at 0x13a0: file MyClass.cpp, line 10.
(gdb) b 11
Breakpoint 4 at 0x13b0: file MyClass.cpp, line 11.
(gdb) r
Starting program: /home/ostkamp/src/cpp_examples/MyClass 

Breakpoint 1, MyClass::MyClass (this=0x7ffff7e7bb39 <operator new(unsigned long)+25>) at MyClass.cpp:6
6       MyClass() {
(gdb) c
Continuing.
MyClass() constructor, this = 0x55555556aeb0
MyClass located at = 0x55555556aeb0

Breakpoint 3, MyClass::someMethod (this=0x7ffff7f037a3 <std::ostream::flush()+35>) at MyClass.cpp:10
10      void someMethod() {
(gdb) c
Continuing.

Breakpoint 4, MyClass::someMethod (this=0x55555556aeb0) at MyClass.cpp:11
11          std::cout << "someMethod(), this = " << this << std::endl;
(gdb) c
Continuing.
someMethod(), this = 0x55555556aeb0

Breakpoint 2, MyClass::~MyClass (this=0x55555556aeb0, __in_chrg=<optimized out>) at MyClass.cpp:14
14      ~MyClass() {
(gdb) c
Continuing.
MyClass() destructor, this = 0x55555556aeb0
[Inferior 1 (process 9105) exited normally]
(gdb) 

As can be seen the breakpoints for constructor (line 6), someMethod (line 10) show invalid "this" information. The breakpoint inside someMethod (line 11) shows correct "this". And sometimes this also works for destructor (line 14).

Is there a way to get correct "this" information?

Is there a way to setup a conditional breakpoint e.g. for someMethod() call that hits only if execution is done for the object with known address e.g. here 0x55555556aeb0 without using a line inside the method?


Solution

  • Unfortunately when the breakpoint hits, the "this" information stated by GDB is not the expected one.

    You have a buggy GCC or GDB.

    Here is what I see:

    (gdb) b MyClass::MyClass
    Breakpoint 1 at 0x12e8: file foo.cc, line 7.
    (gdb) b MyClass::someMethod
    Breakpoint 2 at 0x1334: file foo.cc, line 11.
    (gdb) b MyClass::~MyClass
    Breakpoint 3 at 0x1380: file foo.cc, line 15.
    (gdb) run
    Starting program: /tmp/a.out
    
    Breakpoint 1, MyClass::MyClass (this=0x55555556aeb0) at foo.cc:7
    7               std::cout << "MyClass() constructor, this = " << this << std::endl;
    (gdb) c
    Continuing.
    MyClass() constructor, this = 0x55555556aeb0
    MyClass located at = 0x55555556aeb0
    
    Breakpoint 2, MyClass::someMethod (this=0x55555556aeb0) at foo.cc:11
    11              std::cout << "someMethod(), this = " << this << std::endl;
    (gdb) c
    Continuing.
    someMethod(), this = 0x55555556aeb0
    
    Breakpoint 3, MyClass::~MyClass (this=0x55555556aeb0, __in_chrg=<optimized out>) at foo.cc:15
    15              std::cout << "MyClass() destructor, this = " << this << std::endl;
    (gdb) c
    Continuing.
    MyClass() destructor, this = 0x55555556aeb0
    [Inferior 1 (process 434310) exited normally]
    

    This is using gcc (Debian 12.2.0-14) 12.2.0 and corresponding GDB.


    Probably this GDB bug is what's causing the issue. See also this answer.

    If your x/4i 0x13ee starts with endbr64, that almost certainly is the issue.

    The good news is that building and using more recent GDB is pretty easy: just grab a new release, and run ./configure && make && make install.