How to have gdb exit if program succeeds, break if program crashes?
Asked Answered
B

7

26

I seem to have some kind of multithreading bug in my code that makes it crash once every 30 runs of its test suite. The test suite is non-interactive. I want to run my test suite in gdb, and have gdb exit normally if the program exits normally, or break (and show a debugging prompt) if it crashes. This way I can let the test suite run repeatedly, go grab a cup of coffee, come back, and be presented with a nice debugging prompt. How can I do this with gdb?

Binford answered 28/12, 2011 at 15:17 Comment(0)
M
40

This is a little hacky but you could do:

gdb -ex='set confirm on' -ex=run -ex=quit --args ./a.out

If a.out terminates normally, it will just drop you out of GDB. But if you crash, the program will still be active, so GDB will typically prompt if you really want to quit with an active inferior:

Program received signal SIGABRT, Aborted.
0x00007ffff72dad05 in raise (sig=...) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64  ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
    in ../nptl/sysdeps/unix/sysv/linux/raise.c
A debugging session is active.

    Inferior 1 [process 15126] will be killed.

Quit anyway? (y or n) 

Like I said, not pretty, but it works, as long as you haven't toggled off the prompt to quit with an active process. There is probably a way to use gdb's quit command too: it takes a numeric argument which is the exit code for the debugging session. So maybe you can use --eval-command="quit stuff", where stuff is some GDB expression that reflects whether the inferior is running or not.

This program can be used to test it out:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    if (time(NULL) % 2) {
        raise(SIGINT);
    }
    puts("no crash");
    return EXIT_SUCCESS;
}
Mayolamayon answered 28/12, 2011 at 15:34 Comment(4)
I'm on OS X Snow Leopard and gdb here doesn't support --eval-command, but I'll pick your answer anyway.Binford
I think that this won't work if you have a set confirm off in your .gdbinit file, GDB will always return ...Semite
gdb --eval-command=run --eval-command="set confirm off" --eval-command=bt --eval-command=quit --args ./a.outWholesale
As an alternative, consider using gdb --return-child-result -ex r --command /dev/fd/3 3<<<$'if ! $_isvoid ($_exitcode)\nquit\nend' --args ... ; see also https://mcmap.net/q/535918/-make-gdb-quit-automatically-on-successful-terminationPeg
S
9

The easiest way is to use the Python API offered by gdb:

 def exit_handler(event):
     gdb.execute("quit")
 
 gdb.events.exited.connect(exit_handler)

You can even do it with one line:

(gdb) python gdb.events.exited.connect(lambda x : gdb.execute("quit"))

You can also examine the return code to ensure it's the "normal" code you expected with event.exit_code.

You can use it in conjuction with --eval-command or --command as mentioned by @acm to register the event handler from the command line, or with a .gdbinit file.

Semite answered 1/1, 2012 at 17:46 Comment(2)
Sadly this seems to make GDB dump core: sourceware.org/bugzilla/show_bug.cgi?id=27682#c1Detail
Note that it seems that exit_handler needs to be in a file with .py extension to be interpreted correctly.Dilemma
C
9

You can also trigger a backtrace when the program crashes and let gdb exit with the return code of the child process:

gdb -return-child-result -ex run -ex "thread apply all bt" -ex "quit" --args myProgram -myProgramArg
Cordate answered 23/10, 2012 at 8:36 Comment(1)
This is not the best answer to this particular question but it helped me alot with my question! Thank you, did not know about -return-child-resultLaicize
D
3

Create a file named .gdbinit and it will be used when gdb is launched.

run
quit

Run with no options:

gdb --args prog arg1...

You are telling gdb to run and quit, but it should stop processing the file if an error occurs.

Defluxion answered 16/7, 2013 at 21:22 Comment(0)
M
2

Make it dump core when it crashes. If you're on linux, read the man core man page and also the ulimit builtin if you're running bash.

This way when it crashes you'll find a nice corefile that you can feed to gdb:

$ ulimit -c unlimited
$ ... run program ..., gopher coffee (or reddit ;)
$ gdb progname corefile
Mascara answered 28/12, 2011 at 15:34 Comment(0)
T
2

If you put the following lines in your ~/.gdbinit file, gdb will exit when your program exits with a status code of 0.

python

def exit_handler ( event ):
  if event .exit_code == 0:
    gdb .execute ( "quit" )

gdb .events .exited .connect ( exit_handler )

end

The above is a refinement of Kevin's answer.

Territoriality answered 16/9, 2017 at 4:30 Comment(0)
P
-2

Are you not getting a core file when it crashes? Start gdb like this 'gdb -c core' and do a stack traceback.

More likely you will want to be using Valgrind.

Perceptive answered 28/12, 2011 at 15:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.