tags:

views:

546

answers:

5

When I'm debugging, I sometimes find it useful to "replay" the last few statements of code. For example:

void foo (int & i) {
  i = 0;
  ++i;
  i++;
}

While running this through the debugger, you can add a breakpoint at the top of the function body, and then from any statement inside of foo if you type: "jump file.cc:2" the debugger will return back to i = 0. I appreciate that this isn't always perfect, but sometimes it can be enough to find the bug you're searching for.

I'm currently investigating a problem that results in an exception being thrown. The exception is being thrown at the bottom of a called fucntion, so something like:

void bar ()
{
  throw int ();
}

void foo (int & i)
{
  i = 0;
  ++i;

  bar ();

  i++;
}

int main ()
{
  try
  {
    int i;
    foo (i);
  }
  catch (...)
  {
  }
}

What I want to be able to do, is to put a breakpoint before throw int (), then to jump over just that statement, finish the function bar - so that I can then jump back up to the i = 0 line in foo.

Is there a way I can jump past throw int (), or finish out of bar without executing the throw statement?

The problem appears to be that there's no statement after the throw so I have nowhere to put the breakpoint I want to jump to.

UPDATE:

To highlight what happens in my simple example above:

This GDB was configured as "i486-slackware-linux"...
(gdb) break bar
Breakpoint 1 at 0x804856a: file t.cc, line 3.
(gdb) run
Starting program: ..../t 

Breakpoint 1, bar () at t.cc:3
(gdb) break t.cc:4
Breakpoint 2 at 0x8048592: file t.cc, line 4.
(gdb) jump t.cc:4
Line 4 is not in `bar()'.  Jump anyway? (y or n) y
Continuing at 0x8048592.

Breakpoint 2, foo (i=@0xb80155eb) at t.cc:6

The close curly for 'bar' is on line 4 of 't.cc', however gdb considers this as a breakpoint for foo.

+2  A: 

Yes, you can. You need to set the instruction pointer to the value you want.

  (gdb) set $eip = 0xValue
Justin
+2  A: 

In many cases, the compiler will eliminate the end of the function as it is unreachable. You might want to make a flag you can set to avoid this:

void bar() {
  if (!debugFlag)
    throw int();
}

Be sure the flag is global (not static) so the compiler can't prove it will never be written to.

To skip over the throw,

(gdb) set debugFlag = 1

And make sure to set it back later.

bdonlan
I was hoping to avoid to avoid editing the code if possible. Good point about the function return being unreachable (+1).
Richard Corden
+2  A: 

Expanding on @Justin's answer - while in the bar() function type disassemble, note the address of the ret instruction, set the eip to that address.

Nikolai N Fetissov
+1: I was just about to ask him for more detail....you got the rep instead!
Richard Corden
Surely that won't clean up the stack frame?
bdonlan
(or restore callee-save registers, etc)
bdonlan
+1 Kudos for expanding on a previous answer
ojblass
@bdonlan - right, the eip has to point to stack cleanup code, not directly at the ret. Thanks for the correction.
Nikolai N Fetissov
A: 

%eip is platform-specific and requires some work. It's easier to just jump to the line number containing the end curly. You need to combine this with something like bdonlan is suggesting in case the compiler is optimizing the return-from-function away as unreachable.

$ cat >x.cpp
#include <stdio.h>

static volatile int debug = 0;

void f() {
  if (!debug)
    throw 1;
}

int main() {
  try {
    f();
    puts("f didn't throw");
  } catch(...) {
    puts("f threw");
  }
  return 0;
}

$ g++ -g x.cpp -o x
$ gdb x
[...]
(gdb) run
Starting program: [...]/x 
Reading symbols for shared libraries . done
f threw

Program exited normally.
(gdb) break f
Breakpoint 1 at 0x1e30: file x.cpp, line 6.
(gdb) run
Starting program: [...]/x 

Breakpoint 1, f () at x.c:6
6         if (!debug)
(gdb) jump 8
Continuing at 0x1e73.
f didn't throw

Program exited normally.
laalto
(-1). I think you've missed the problem. In my example the end curly is not considered part of the function bar. I'll update the question to show what happens when I do that.
Richard Corden
You can also place a breakpoint on the end-curly line. I updated the example to demonstrate this.
laalto
Your example doesn't end with a throw. It's possible that this is the crucial difference between our two cases. I've added the output generated during my gdb session to my question.
Richard Corden
Yes - I added a note that this needs to be combined with something like bdonlan is suggesting in case the compiler is optimizing away unreachable code. The main point remains: jump is easier than set %eip and jumping to end-curlies is easier than jumping to function cleanup code.
laalto
... also updated the example to have a throwing function where the end-curly code is not optimized away.
laalto
+3  A: 

My bad spelling has actually provided me with the answer!

My variation of "disassemble" was not working, so while looking for the correct spelling I eventually stumbled onto "help stack":

Examining the stack. The stack is made up of stack frames. Gdb assigns numbers to stack frames counting from zero for the innermost (currently executing) frame.

At any time gdb identifies one frame as the "selected" frame. Variable lookups are done with respect to the selected frame. When the program being debugged stops, gdb selects the innermost frame. The commands below can be used to select other frames by number or address.

List of commands:

backtrace -- Print backtrace of all stack frames

bt -- Print backtrace of all stack frames

down -- Select and print stack frame called by this one

frame -- Select and print a stack frame

return -- Make selected stack frame return to its caller

select-frame -- Select a stack frame without printing anything

up -- Select and print stack frame that called this one

The return command from the above list does exactly what I want in this case.

Thanks to you all for your help.

Richard Corden
Nice find - be sure to accept your answer in 48 hours so the next person with this question sees it first off :)
bdonlan