15

This is a reduced example of the structure of my code:

void increment(int j);

int main()
{
  int i = 0;

  while(1) {
    i = increment(i);
  }

  return 0;
}

int increment(int j)
{
  return j + 1;
}

And here is the corresponding GDB script:

b increment
command 1
finish
print i
continue
end

The problem is that the finish command prevents the commands that come after it (namely print i and continue) to not be called.

Is there a way to tell GDB to print i right after any increment call?

maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
Randomblue
  • 112,777
  • 145
  • 353
  • 547
  • See also: [c++ - How to set a breakpoint in GDB where the function returns? - Stack Overflow](https://stackoverflow.com/questions/3649468/how-to-set-a-breakpoint-in-gdb-where-the-function-returns) – user202729 Mar 11 '20 at 09:42

5 Answers5

15

You can apparently work around this bug by wrapping all the commands in a single python invocation e.g.

(gdb) break doSomething
Breakpoint 1 at 0x400478: file iter.c, line 5.
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>python gdb.execute("print i"); gdb.execute("finish"); gdb.execute("print i");
>end

Breakpoint 1, doSomething () at iter.c:5
5     while (i < 5)
$1 = 0
main (argc=1, argv=0x7fffffffe178) at iter.c:13
13    return 0;
$2 = 5

edit: a 2nd work around that doesn't require python appears to be defining a new gdb command and running that in commands:

define foo
print *i
set $addrOfI = i
finish
print *$addrOfI
end

break doSomething
commands
foo
end
matt
  • 5,364
  • 1
  • 25
  • 25
  • Wow, thanks. I didn't know Python could be use in GDB. However, my version of GDB (for embedded devices) does not support Python scripting. Shame. – Randomblue May 10 '12 at 08:24
  • @Randomblue added another workaround that doesn't require python. – matt May 10 '12 at 10:54
  • FWIW, I haven't tested what happens in the case where gdb hits a breakpoint in between finish and the other stuff... but it sounds nefarious – matt May 10 '12 at 16:05
  • Generally: the trick is that `finish` within a `define`d macro does not exhibit the bug and will execute the subsequent commands, whereas `finish` within a `commands` block doesn't do what you'd expect. – lionello Jan 27 '14 at 12:44
  • Helloooo back there in 2012! It's 2015 and this bug is still not fixed, and the second workaround still works (I didn't check the first one). Note: the bug is still triggered, anything in commands after a 'fin' command will not run (though the whole defined command will fun before stopping). – Wug Nov 20 '15 at 01:49
  • 2
    The non-Python workaround doesn't work in certain cases (nested function calls with breakpoints on both nested and nesting functions). I believe the reason is that the workaround messes up GDB's event loop somehow. I think the only sure way to avoid this is to define a command in Python which adds the commands meant to follow `finish` to GDB's event queue using `gdb.post_event()` with a suitable object. Of course this will cause all kinds of weirdness with nested breakpoints. The bug report in the answer below explains the root cause of all of this: no proper state machine for infrun. Alas. – ack Feb 01 '16 at 17:19
  • This worked for me with `finish`. I also saw similar issues with `next`, which this didn't fix. Adding a `posthook` (see [here](https://stackoverflow.com/questions/14261404/how-to-prevent-gdb-to-stop-after-next-command)) worked great for me and I imagine would work for `finish` as well. – user1266174 Feb 04 '21 at 07:02
4

The problem is that finish seems to stop abort the commands set for the first breakpoint after it.

This is expected behavior: any command that resumes the inferior (being debugged) process (as finish does) also stops the execution of canned command sequence.

Update:

See also this GDB bug report.

Is there a way to tell GDB to print i right after any increment call?

Yes:

  1. Diassemble increment routine using disas command. Find ret instruction at the end of it (there will only be one).
  2. Set a breakpoint on that instruction, using break *0xNNNNN syntax.
  3. Attach a command to that breakpoint:

    command N
     print $rax    # or $eax if you are on 32-bit x86 platform
     continue
    end
    

Voila: you should get values being returned from increment() printed (just before being returned).

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Thanks. This works, but can the whole "`disas`, find `ret`, etc." be done programmatically? – Randomblue May 08 '12 at 16:22
  • @Randomblue No, I don't believe so. – Employed Russian May 08 '12 at 17:25
  • @Randomblue, yes, it can be done programmatically - see my answer: http://stackoverflow.com/a/42607055/427158 – maxschlepzig Mar 05 '17 at 10:12
  • Is there any way to do this for complex return types? `finish` and `continue` doesn't work (the result is printed, but the `continue` is ignored, the program still pauses), even with the workaround in stackoverflow.com/q/10501121 . – user202729 Mar 12 '20 at 03:59
3

Alternatively to @Matt answer, and if you use GDB 7.4, you can use FinishBreakpoints, with something like (untested -- I'm not sure that comments are accepted here):

(gdb) python #first defined the class
class MyFinishBreakpoint (gdb.FinishBreakpoint):
    def stop (self):
        print "%s" % gdb.parse_and_eval("i")
        return False # don't want to stop
end
(gdb) break doSomething
(gdb) commands
# then set the FinishBreakpoint silently
silent
py MyFinishBreakpoint()
continue

(and a link to the documentation)

Kevin
  • 4,618
  • 3
  • 38
  • 61
  • missing ":" at the end of the class line and you need to call python MyFinishBreakpoint() in the commands. – ensonic Aug 28 '15 at 09:16
2

GDB breakpoint command lists are limited in that they ignore any command after the first stepping/continue command (as of March, 2017, GDB 7.12). This is documented in the GDB manual where this motivated with the current implementation not being capable to execute two command lists at the same time (cf. GDB #10852 - command sequences interrupted unexpectedly).

This limitation is only enforced with a stepping/continue command directly present in a command list. Thus, one can hack around this - but the limitation still applies and e.g. the GDB manual warns in the Python API Section: 'You should not alter the execution state of the inferior (i.e., step, next, etc.)'

Thus, when the need arises to execute GDB commands on function entry and function exit, the reliable solution is to use multiple breakpoints and split the command lists. That means that additional breakpoints need to be set for each return instruction of the function under investigation.

This can be done similar to:

(gdb) b my_function
(gdb) commands
silent
printf "my_function: %d -> ", j
end
(gdb) set pagination off
(gdb) set logging file gdb.log
(gdb) set logging overwrite on
(gdb) set logging on
(gdb) disas my_function
(gdb) set logging off
(gdb) shell grep ret gdb.log
0x00007ffff76ad095 <+245>:  retq
(gdb) b *0x00007ffff76ad095
(gdb) commands
silent
printf "%lu\n", $rax
end

What register contains the return value depends on the calling conventions and is architecture dependent. On x86-64 it is in $rax. Other choices are $eax on x86-32, $o0 on SPARC, $r0 on ARM etc.

The creation of the additional breakpoints can be automated in GDB using its scripting support.

Python

Recent GDB versions come with a Python API that is well suited for this automation. GDB packages provided by distributions usually enable Python support, by default.

As a first example, automatically set breakpoints on each ret instruction of a given function:

(gdb) py fn='myfunc'; list(map(lambda l: gdb.execute('b *{}'.format(l[0])), \
    filter(lambda l : l[2].startswith('ret'), map(lambda s : s.split(), \
        gdb.execute('disas '+fn, to_string=True).splitlines()))))

(assumes GDB was compiled with Python3 support, e.g. the Fedora 25 one)

For automating the creation of breakpoints that print the return value (i.e. the value of register $rax) and then continue the gdb.Breakpoint class needs to be subclassed:

py
class RBP(gdb.Breakpoint):
  def stop(self):
    print(gdb.parse_and_eval('$rax'))
    return False

end

Then a breakpoint can be created like this:

py RBP('*0x000055555555894e')

Combining both steps for creating a new custom command:

py
class Pret_Cmd(gdb.Command):
  '''print return value via breakpoint command

  pret FUNCTION
  '''
  def __init__(self):
    super().__init__('pret', gdb.COMMAND_BREAKPOINTS)

  def install(self, fn):
    for l in filter(lambda l : l[2].startswith('ret'),
        map(lambda s : s.split(),
            gdb.execute('disas '+fn, to_string=True).splitlines())):
      RBP('*{}'.format(l[0]))

  def invoke(self, arg, from_tty):
    self.install(arg)

Pret_Cmd()
end

Example of using this new command:

(gdb) help breakpoints
(gdb) help pret
(gdb) pret myfunc

Guile

In case you don't like Python and/or have a GDB that has Python support disabled - but Guile support enabled - one can also automatically set the breakpoints via Guile.

The custom command definition in Guile:

(gdb) gu (use-modules (gdb))
(gdb) gu
(register-command!
  (make-command "pret" #:command-class COMMAND_BREAKPOINTS #:doc
    "print return value via breakpoint command\n\npret FUNCTION"
    #:invoke
    (lambda (fn)
      (map (lambda (x)
             (let ((bp (make-breakpoint (string-append "*" x))))
               (register-breakpoint! bp)
               (set-breakpoint-stop!
                 bp
                 (lambda (x)
                   (display (parse-and-eval "$rax"))
                   (newline)
                   #f))
               bp))
           (map (lambda (x) (list-ref x 0))
                (filter
                  (lambda (x)
                    (and (not (null? x))
                         (string-prefix? "ret" (list-ref x 2))))
                  (map (lambda (x) (string-tokenize x))
                       (string-split
                         (execute
                           (string-append "disas " fn)
                           #:to-string
                           #t)
                         #\newline))))))))

end
maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
1

Have you actually tried to compile this? Your increment() function is declared void, but needs to be int. After changing that, it worked fine for me:

% gdb test
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
[...]
Reading symbols from test...done.
(gdb) b increment 
Breakpoint 1 at 0x4004bb: file test.c, line 5.
(gdb) r
Starting program: test 

Breakpoint 1, increment (j=0) at test.c:5
5               return j+1;
(gdb) fin
Run till exit from #0  increment (j=0) at test.c:5
0x00000000004004dc in main () at test.c:11
11                      i = increment(i);
Value returned is $1 = 1
(gdb) n
12              }
(gdb) p i
$2 = 1
(gdb)
Kevin
  • 53,822
  • 15
  • 101
  • 132
  • 2
    I don't believe you've understood the question OP is asking. He doesn't have a problem with `finish`, he has a problem with a script that tries to `continue` after execution `finish`. – Employed Russian May 08 '12 at 16:12
  • @Kevin: No, I haven't tried compiling it. It was just for illustration. Employed Russian's comment holds. – Randomblue May 08 '12 at 16:16