Setting breakpoint in GDB where the function returns

0 votes
asked Sep 6, 2010 by avd

I have a C++ function which has many return statements at various places. How to set a breakpoint at the return statement where the function actually returns ?

And what does "break" command without argument means?

5 Answers

0 votes
answered Sep 6, 2010 by potatoswatter

Break without argument sets a breakpoint at the current line.

There is no way for a single breakpoint to catch all return paths. Either set a breakpoint at the caller immediately after it returns, or break at all return statements.

Since this is C++, I suppose you could create a local sentry object, and break on its destructor, though.

0 votes
answered Sep 6, 2010 by jim-brissom

break without arguments stops execution at the next instruction in the currently selected stack frame. You select strack frames via the frame or up and down commands. If you want to debug the point where you are actually leaving the current function, select the next outer frame and break there.

0 votes
answered Sep 6, 2010 by ks1322

You can use reverse debugging to find out where function actually returns. Finish executing current frame, do reverse-step and then you should stop at just returned statement.

(gdb) record
(gdb) fin
(gdb) reverse-step
0 votes
answered Sep 7, 2010 by employed-russian

Contrary to answers so far, most compilers will create a single return assembly instruction, regardless of how many return statements are in the function (it is convenient for the compiler to do that, so there is only a single place to perform all the stack frame cleanup).

If you wanted to stop on that instruction, all you have to do is disas and look for retq (or whatever the return instruction for your processor is), and set a breakpoint on it. For example:

int foo(int x)
{
  switch(x) {
   case 1: return 2;
   case 2: return 3;
   default: return 42;
  }
}

int main()
{
  return foo(0);
}


(gdb) disas foo
Dump of assembler code for function foo:
   0x0000000000400448 <+0>: push   %rbp
   0x0000000000400449 <+1>: mov    %rsp,%rbp
   0x000000000040044c <+4>: mov    %edi,-0x4(%rbp)
   0x000000000040044f <+7>: mov    -0x4(%rbp),%eax
   0x0000000000400452 <+10>:    mov    %eax,-0xc(%rbp)
   0x0000000000400455 <+13>:    cmpl   $0x1,-0xc(%rbp)
   0x0000000000400459 <+17>:    je     0x400463 <foo+27>
   0x000000000040045b <+19>:    cmpl   $0x2,-0xc(%rbp)
   0x000000000040045f <+23>:    je     0x40046c <foo+36>
   0x0000000000400461 <+25>:    jmp    0x400475 <foo+45>
   0x0000000000400463 <+27>:    movl   $0x2,-0x8(%rbp)
   0x000000000040046a <+34>:    jmp    0x40047c <foo+52>
   0x000000000040046c <+36>:    movl   $0x3,-0x8(%rbp)
   0x0000000000400473 <+43>:    jmp    0x40047c <foo+52>
   0x0000000000400475 <+45>:    movl   $0x2a,-0x8(%rbp)
   0x000000000040047c <+52>:    mov    -0x8(%rbp),%eax
   0x000000000040047f <+55>:    leaveq 
   0x0000000000400480 <+56>:    retq   
End of assembler dump.
(gdb) b *0x0000000000400480
Breakpoint 1 at 0x400480
(gdb) r

Breakpoint 1, 0x0000000000400480 in foo ()
(gdb) p $rax
$1 = 42
0 votes
answered Sep 7, 2015 by urzeit

If you can change the source code, you might use some dirty trick with the preprocessor:

void on_return() {

}

#define return return on_return(), /* If the function has a return value != void */
#define return return on_return()  /* If the function has a return value == void */

/* <<<-- Insert your function here -->>> */

#undef return

Then set a breakpoint to on_return and go one frame up.

Attention: This will not work, if a function does not return via a return statement. So ensure, that it's last line is a return.

Example (shamelessly copied from C code, but will work also in C++):

#include <stdio.h>

/* Dummy function to place the breakpoint */
void on_return(void) {

}

#define return return on_return()
void myfun1(int a) {
    if (a > 10) return;
    printf("<10\n");
    return;   
}
#undef return

#define return return on_return(),
int myfun2(int a) {
    if (a < 0) return -1;
    if (a > 0) return 1;
    return 0;
}
#undef return


int main(void)
{
    myfun1(1);
    myfun2(2);
}

The first macro will change

return;

to

return on_return();

Which is valid, since on_return also returns void.

The second macro will change

return -1;

to

return on_return(), -1;

Which will call on_return() and then return -1 (thanks to the ,-operator).

This is a very dirty trick, but despite using backwards-stepping, it will work in multi-threaded environments and inlined functions, too.

Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...