Comment obtenir un Backtrace utile
Cette page tente d'expliquer comment obtenir le backtrace d'un programme qui plante de manière reproductible. Pour cet exemple, nous installerons le paquet "hello" ou, s'il n'est pas disponible, nous reconstruirons et nous installerons le paquet "hello" pour avoir les informations de déboguage.
Pour rappel, un "#" en début de ligne signifie que ce qui suit est une commande à exécuter en tant que root (ou avec sudo). Un "$" en début de ligne signifie que la commande doit être exécutée par un utilisateur normal. Un "(gdb)" en début de ligne signifie que ce qui suit doit être saisi dans l'invite de gdb (GNU Debugger).
Installer les symboles de déboguage
Assurez-vous d'avoir l'archive AutomaticDebugPackages dans votre ?SourceList (indiquez celle correspondant à votre release) : (pick the one for your release):
deb http://debug.mirrors.debian.org/debian-debug/ stretch-debug main deb http://debug.mirrors.debian.org/debian-debug/ testing-debug main deb http://debug.mirrors.debian.org/debian-debug/ unstable-debug main deb http://debug.mirrors.debian.org/debian-debug/ experimental-debug main
Pour savoir si le paquet que vous essayez de déboguer dispose d'un paquet -dbgsym ou -dbg (par exemple, si hello est le paquet source, si le paquet de déboguage hello-dbg ou hello-dbgsym existe), cherchez-le dans le gestionnaire de paquet. En ligne de commande, ceci donne :
$ apt-cache search hello | grep dbg p hello-dbgsym - Debug symbols for hello
Si rien n'est retourné, il faut reconstruire le paquet comme expliqué dans la section suivante. En revanche, si un paquet existe, installez-le, par exemple avec :
# apt-get install hello-dbgsym
et sautez alors la section suivante pour passer directement à l'exécution de gdb.
Reconstruire le paquet que vous déboguez
Rebuilding the package you’re debugging
Vous pouvez passer cette section si vous avez pu installer le(s) paquet(s) -dbg ou -dbgsym nécessaires dans la section précédente.
Installez les paquets de base de développement et les dépendances de construction du paquet qu'on veut reconstruire. Notez que vous pouvez ne pas reconstruire le paquet et aller directement à l'exécution de gdb, mais vous n'aurez probablement alors pas de trace utile. (Si la ligne build-dep échoue, vérifiez que vous avez les lignes deb-src dans /etc/apt/sources.list.)
# apt-get install build-essential fakeroot gdb # apt-get build-dep hello
Téléchargez et reconstruisez notre paquet à la source, en conservant les symboles de déboguage
$ DEB_BUILD_OPTIONS="nostrip noopt" fakeroot apt-get -b source hello
Installez notre/nos paquet(s) nouvellement construit(s). Il se peut que plusieurs paquets .deb soient générés, assurez-vous donc de n'installer que ceux que vous voulez. Dans cet exemple, le .deb généré s'appelle hello_2.1.1-4_i386.deb.
# dpkg -i hello_2.1.1-4_i386.deb
Vous pouvez vous assurer que les binaires installés à partir de votre .deb contient bien les symboles de déboguage via la commande 'file' command, ou avec gdb lui-même (voir ci-dessous).
$ file /usr/bin/hello # output should contain "not stripped"
Exécuter gdb
- À présent, lancez votre programme comme suit, en remplaçant "[--args]" avec n'importe quel argument que vous souhaitez :
$ gdb hello ... gdb loads ... (gdb) set pagination 0 (gdb) run [--args] ... hello loads...
- Essayez de reproduire le crash du programme. Si vous êtes chanceux, le crash se produira et vous retournerez à l'invite de gdb.
- Si vous n'êtes pas aussi chanceux et que le programme freeze, vous pouvez tout de même récupérer l'invite de gdb en tapant CTRL+C dans le terminal dans lequel tourne gdb.
- À cette étape, vous pouvez exécuter :
(gdb) bt
- Vous allez obtenir des messages volumineux que vous pouvez copier/coller pour transmettre dans un rapport de bogue, via mél ou reportbug.
- Lorsque c'est fait, vous pouvez quitter gdb en tapant simplement :
(gdb) quit
Si le problème semble être dans une librairie majeure telle que libc6, xlibs, ou libgtk2.0-0, If the problem seems to be in a major library such as libc6, xlibs, or libgtk2.0-0, you’ll want to install the appropriate -dbg package (e.g. libc6-dbg in the case of libc6) and then run the problematic program again under gdb.
Often, you will see a backtrace where one or more of the top lines is in malloc() or g_malloc(). When this happens, chances are your backtrace isn’t very useful. The easiest way to find some useful information is to set the environment variable MALLOC_CHECK_ to a value of 2. You can do this while running gdb by doing this:
$ MALLOC_CHECK_=2 gdb hello
Commandes gdb avancées
If the program you’re backtracing is multi-threaded, you might want to get a backtrace for all threads:
(gdb) thread apply all bt
Another thing which is quite helpful to report is what variables were set locally at each point in the stack:
(gdb) bt full
You might want to report the output of the combination of the preceding options:
(gdb) thread apply all bt full
And if this is too much irrelevant output, you might want to keep only a few calls, such as the top 10:
(gdb) thread apply all bt full 10
If you have a large backtrace, you can log gdb output to a file (the default is gdb.txt):
(gdb) set logging on
To check you have debugging symbols in your binary:
$ gdb (gdb) symbol-file /usr/bin/hello # you should see something like this: Reading symbols from /usr/bin/hello ... done Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) # NB you should _not_ see Reading symbols from /usr/bin/hello...(no debugging symbols found)...done
Déboguer des erreurs X
If a GTK program has received an X error; i.e. you see a message of the form:
The program 'preview1' received an X Window System error.
then you can try running the program with --sync, and break on the gdk_x_error function in order to obtain a backtrace, thus:
(gdb) break gdk_x_error (gdb) run --sync