2020-10-26 23:06:17 +01:00
|
|
|
|
\chapter{Debug}\index{Debug}
|
|
|
|
|
\label{chap:debug}
|
|
|
|
|
|
2020-11-16 19:45:21 +01:00
|
|
|
|
Quand plus rien ne marche, reste-il encore un espoir ?
|
|
|
|
|
Il existe bien entendu des outils \textsl{mainstream}
|
|
|
|
|
tels que le classique \texttt{gdb}\index{gdb}, mais il en existe
|
|
|
|
|
une foultitude d'autres, injustement méconnus.
|
2020-11-25 11:29:26 +01:00
|
|
|
|
Nous allons en voir quelques-uns en essayant de nous baser
|
2022-04-03 11:56:49 +02:00
|
|
|
|
sur des cas quasiment réels.
|
|
|
|
|
|
|
|
|
|
Et non, je ne vais pas vous parler du vénérable \texttt{ddt}
|
|
|
|
|
des antiques systèmes CP/M, ni même du \texttt{debug} des
|
|
|
|
|
anciens DOS, bien que tous deux méritent votre attention.
|
2020-10-26 23:06:17 +01:00
|
|
|
|
|
2020-11-22 21:07:05 +01:00
|
|
|
|
% ==============================================================
|
|
|
|
|
|
|
|
|
|
\section{Gdb}\index{gdb}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GDB is a source-level debugger, capable of breaking programs at
|
|
|
|
|
any specific line, displaying variable values, and determining
|
|
|
|
|
where errors occurred. Currently, gdb supports C, C++, D,
|
|
|
|
|
Objective-C, Fortran, Java, OpenCL C, Pascal, assembly, Modula-2,
|
|
|
|
|
Go, and Ada. \textsc{A must-have for any serious programmer}.
|
|
|
|
|
|
2020-11-25 11:29:26 +01:00
|
|
|
|
\subsection{Clickaconvi}
|
2020-11-22 21:07:05 +01:00
|
|
|
|
|
2020-11-25 11:29:26 +01:00
|
|
|
|
\textbf{DDD} is a graphical front-end for GDB and other command-line debuggers.
|
2020-11-22 21:07:05 +01:00
|
|
|
|
Using DDD, you can see what is going on “inside” another program while
|
|
|
|
|
it executes—or what another program was doing at the moment it crashed.
|
|
|
|
|
|
2020-11-25 11:29:26 +01:00
|
|
|
|
\textbf{xxgdb} is a simple but powerful graphical interface to the GNU
|
2020-11-22 21:07:05 +01:00
|
|
|
|
debugger gdb. A more powerful (but slower and much bigger) interface
|
|
|
|
|
is available in the ddd package.
|
2020-10-26 23:06:17 +01:00
|
|
|
|
|
2020-11-25 11:29:26 +01:00
|
|
|
|
% --------------------------------------------------
|
|
|
|
|
% novembre 2020, panique dans le kernel de la Fedora
|
|
|
|
|
%
|
|
|
|
|
\subsection{Un cas réel}
|
|
|
|
|
|
|
|
|
|
J'ai un programme, écrit en C, qui est assez agressif sur
|
|
|
|
|
le calcul flottant et les accès disque,
|
|
|
|
|
et qui se fait tuer en cours de route,
|
|
|
|
|
avec un message inquiétant du kernel
|
|
|
|
|
\texttt{[95335.731943] fonderie: Corrupted page table at address
|
|
|
|
|
7fffe82d6000} qui me laisse perplexe.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
[tth@laserbox Sauvageonnes]$ gdb ~/Devel/FloatImg/Fonderie/fonderie
|
|
|
|
|
GNU gdb (GDB) Fedora 10.1-2.fc33
|
|
|
|
|
This GDB was configured as "x86_64-redhat-linux-gnu".
|
|
|
|
|
Reading symbols from /home/tth/Devel/FloatImg/Fonderie/fonderie...
|
|
|
|
|
(gdb) run -I 'G/?????.fimg' -O 'Png' -w 0 -x 0 -T 80
|
|
|
|
|
Starting program:
|
|
|
|
|
/home/tth/Devel/FloatImg/Fonderie/fonderie
|
|
|
|
|
-I 'G/?????.fimg' -O 'Png' -w 0 -x 0 -T 80
|
|
|
|
|
Missing separate debuginfos, use:
|
|
|
|
|
dnf debuginfo-install glibc-2.32-2.fc33.x86_64
|
|
|
|
|
*** /home/tth/Devel/FloatImg/Fonderie/fonderie :
|
|
|
|
|
compiled by tTh, Nov 25 2020 10:19:10
|
|
|
|
|
pid 1949
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Là, on attend vingt minutes que le logiciel tripote plein
|
|
|
|
|
d'images en virgule flottante. C'est long.
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
54 / 1784
|
|
|
|
|
Program terminated with signal SIGKILL, Killed.
|
|
|
|
|
The program no longer exists.
|
|
|
|
|
Missing separate debuginfos, use:
|
|
|
|
|
dnf debuginfo-install pnglite-0.1.17-1.fc33.21.x86_64
|
|
|
|
|
zlib-1.2.11-22.fc33.x86_64
|
|
|
|
|
(gdb)
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
On est donc bien avancé, \texttt{SIGKILL}, dans ce cas-là,
|
|
|
|
|
c'est le noyau qui flingue\index{bfg9000} le processus.
|
|
|
|
|
Mais pour quelle raison, et à quel endroit dans le programme ?
|
|
|
|
|
Avec un peu de chance, gdb a conservé une trace des dernières
|
|
|
|
|
microscondes de la fonderie.
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
(gdb) backtrace
|
|
|
|
|
No stack.
|
|
|
|
|
(gdb)
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Bah non, il nous avait bien prévenu :
|
|
|
|
|
\textit{The program no longer exists}, point suivant.
|
|
|
|
|
|
2020-11-16 19:45:21 +01:00
|
|
|
|
% ==============================================================
|
|
|
|
|
|
2020-11-25 14:24:19 +01:00
|
|
|
|
\section{Valgrind}
|
|
|
|
|
|
|
|
|
|
Reprenons le premier exemple précédemment traité avec Gdb, et
|
|
|
|
|
tentons de résoudre l'énigme avec Valgrind.
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
(gdb) quit
|
|
|
|
|
[tth@laserbox]$ man valgrind
|
|
|
|
|
[tth@laserbox]$ valgrind fonderie -I 'G/?????.fimg' -O 'Png' \
|
|
|
|
|
-w 0 -x 0 -T 80
|
|
|
|
|
==2388== Memcheck, a memory error detector
|
|
|
|
|
==2388== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
|
|
|
|
|
==2388== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
|
|
|
|
|
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Là, on attend vingt heures que le logiciel tripote plein
|
|
|
|
|
d'images en virgule flottante. C'est très long. Je me
|
|
|
|
|
demande même si ce n'est pas un roblock total.
|
|
|
|
|
|
|
|
|
|
% ==============================================================
|
|
|
|
|
|
2020-11-16 19:45:21 +01:00
|
|
|
|
\section{Strace}\index{strace}
|
|
|
|
|
|
|
|
|
|
Strace permet de tracer les appels systèmes d'un processus.
|
|
|
|
|
Comme vous le savez tous, un appel système
|
|
|
|
|
(aka syscall\index{syscall})
|
|
|
|
|
est \textbf{le} moyen de communication qu'utilise un process
|
|
|
|
|
utilisateur pôur demander un service au noyau.
|
|
|
|
|
|
2020-11-22 21:07:05 +01:00
|
|
|
|
\lstinputlisting[language=C]{code/hello.c}
|
2020-11-20 04:06:44 +01:00
|
|
|
|
|
|
|
|
|
Un exemple canonique, n'est-il pas ? Ce bout de code affichant
|
|
|
|
|
quelque chose à l'écran, il doit bien y avoir un appel au noyau
|
2020-11-22 21:07:05 +01:00
|
|
|
|
qui traine par là pour écrire vers la sortie standard.
|
|
|
|
|
Nous allons donc le chercher%
|
2020-11-20 04:06:44 +01:00
|
|
|
|
\footnote{En trichant un peu, je l'avoue, je connais son nom.}
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
$ gcc hello.c
|
|
|
|
|
$ strace -o foo ./a.out
|
|
|
|
|
$ grep write foo
|
|
|
|
|
write(1, "Hello world.\n", 13) = 13
|
|
|
|
|
$
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
On peut réaliser la même opération en utilisant le filtrage interne
|
|
|
|
|
de strace, ce qui évite le passage par un
|
|
|
|
|
fichier intermédiaire, mais le résultat est moins lisible,
|
|
|
|
|
puisque a.out écrit sur stdout, et strace sur stderr~:
|
|
|
|
|
|
2020-11-16 19:45:21 +01:00
|
|
|
|
\begin{verbatim}
|
2020-11-20 04:06:44 +01:00
|
|
|
|
$ strace -e write ./a.out
|
|
|
|
|
write(1, "Hello world.\n", 13Hello world.
|
|
|
|
|
) = 13
|
|
|
|
|
+++ exited with 0 +++
|
|
|
|
|
$
|
2020-11-16 19:45:21 +01:00
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
2020-11-20 04:06:44 +01:00
|
|
|
|
|
2020-11-16 19:45:21 +01:00
|
|
|
|
% ==============================================================
|
2020-10-26 23:06:17 +01:00
|
|
|
|
|
|
|
|
|
\section{LD\_PRELOAD}\index{LD\_PRELOAD}
|
|
|
|
|
|
2020-11-16 19:45:21 +01:00
|
|
|
|
D'accord, aves \texttt{strace} nous pouvons voir passer les
|
|
|
|
|
appels systèmes, mais dans la vie d'un process, certaines
|
|
|
|
|
opérations ne sortent pas de la \texttt{libc}\index{libc}.
|
|
|
|
|
L'une d'entre elles, \texttt{getenv(3)}, va nous servir d'exemple.
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
NAME
|
|
|
|
|
getenv - get an environment variable
|
|
|
|
|
SYNOPSIS
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
char *getenv(const char *name);
|
|
|
|
|
DESCRIPTION
|
|
|
|
|
The getenv() function searches the environment list to find the envi‐
|
|
|
|
|
ronment variable name, and returns a pointer to the corresponding value
|
|
|
|
|
string.
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
|
|
|
|
Cette fonction est utilisée par un logiciel pour avoir accès à
|
|
|
|
|
son contexte extérieur, son environnement, dans lequel on
|
|
|
|
|
peut trouver (entre autres) la variable \texttt{\$LOGNAME} qui,
|
|
|
|
|
oh surprise, contient votre nom de login.
|
|
|
|
|
|
|
|
|
|
Et justement, vous avez un programme sous la main que vous suspecter
|
|
|
|
|
d'avoir un problème relationnel avec cette variable.
|
2023-11-29 14:12:03 +01:00
|
|
|
|
Il nous faut donc remplacer le getenv de la libc par notre propre
|
2020-11-16 19:45:21 +01:00
|
|
|
|
version qui va écouter et exfiltrer l'utilisation de cette
|
|
|
|
|
fonction.
|
2020-10-26 23:06:17 +01:00
|
|
|
|
|
2022-01-29 22:46:41 +01:00
|
|
|
|
\lstinputlisting[language=C]{code/debug/spy_getenv.c}
|
2020-10-26 23:06:17 +01:00
|
|
|
|
|
2023-11-29 14:12:03 +01:00
|
|
|
|
Et à l'utilsation~:
|
|
|
|
|
|
|
|
|
|
\begin{verbatim}
|
|
|
|
|
tth@redlady:~$ LD_PRELOAD=~/Devel/TetaTricks/code/debug/spy_getenv date
|
|
|
|
|
--getenv--> TZ --> (nil)
|
|
|
|
|
--getenv--> TZ --> (nil)
|
|
|
|
|
Tue Oct 17 08:55:01 CEST 2023
|
|
|
|
|
tth@redlady:~$
|
|
|
|
|
\end{verbatim}
|
|
|
|
|
|
2020-11-16 19:45:21 +01:00
|
|
|
|
Simple et efficace.
|
|
|
|
|
|
|
|
|
|
% ==============================================================
|
|
|
|
|
|
2022-02-16 00:53:49 +01:00
|
|
|
|
\section{Cflow et Slint}
|
|
|
|
|
|
|
|
|
|
Deux outils pour savoir quoi nettoyer dans du code C un peu relou.
|
|
|
|
|
|
2020-10-26 23:06:17 +01:00
|
|
|
|
|
2020-11-16 19:45:21 +01:00
|
|
|
|
% ==============================================================
|