poly: first version
This commit is contained in:
commit
de7422c483
12 changed files with 417 additions and 0 deletions
9
poly/.gitignore
vendored
Normal file
9
poly/.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
*.aux
|
||||
*.dvi
|
||||
*.fls
|
||||
*.fdb*
|
||||
*.log
|
||||
*.ps
|
||||
*.xdv
|
||||
poly.tex
|
||||
*.pdf
|
16
poly/Makefile
Normal file
16
poly/Makefile
Normal file
|
@ -0,0 +1,16 @@
|
|||
TARGET=poly
|
||||
PANDOC_OPTS=\
|
||||
--include-in-header=header.tex \
|
||||
--highlight-style tango \
|
||||
--syntax-definition=syntax/gdb-bt.xml \
|
||||
--syntax-definition=syntax/gdb.xml
|
||||
PDFDEPS=
|
||||
MDDEPS=meta.yml
|
||||
|
||||
all: $(TARGET).pdf
|
||||
|
||||
%.pdf: %.tex $(PDFDEPS) $(MDDEPS)
|
||||
latexmk -xelatex $<
|
||||
|
||||
%.tex: %.md $(MDDEPS)
|
||||
pandoc -s $(PANDOC_OPTS) -o "$@" "$<" meta.yml
|
16
poly/header.tex
Normal file
16
poly/header.tex
Normal file
|
@ -0,0 +1,16 @@
|
|||
\usepackage[most]{tcolorbox}
|
||||
\renewenvironment{Shaded}%
|
||||
{\begin{tcolorbox}}%
|
||||
{\end{tcolorbox}}
|
||||
|
||||
% Thanks https://github.com/jgm/pandoc/issues/2453#issuecomment-219233316
|
||||
\newcommand{\hideFromPandoc}[1]{#1}
|
||||
\hideFromPandoc{
|
||||
\let\Begin\begin
|
||||
\let\End\end
|
||||
}
|
||||
|
||||
\definecolor{codeboxbg}{RGB}{255,255,230}
|
||||
\tcbset{colback=codeboxbg}
|
||||
\tcbset{boxrule=1pt}
|
||||
\tcbset{size=title}
|
13
poly/meta.yml
Normal file
13
poly/meta.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
title: "INF301 -- Debug de segfaults"
|
||||
author: "Théophile Bastian \\texttt{<theophile.bastian@inria.fr>}"
|
||||
date: "\\vspace{-1em}"
|
||||
lang: fr
|
||||
geometry:
|
||||
- top=2cm
|
||||
- bottom=2cm
|
||||
- left=2cm
|
||||
- right=2cm
|
||||
pagestyle: empty
|
||||
papersize: a4
|
||||
---
|
182
poly/poly.md
Normal file
182
poly/poly.md
Normal file
|
@ -0,0 +1,182 @@
|
|||
\thispagestyle{empty}
|
||||
|
||||
Une *segfault* -- *segmentation fault*, ou *erreur de segmentation* -- apparaît
|
||||
quand un programme essaye d'accéder à une zone mémoire qui ne lui est pas
|
||||
allouée. C'est une erreur fréquente dès lors qu'on manipule des pointeurs, et
|
||||
elle est plus difficile à corriger qu'un bug classique -- en tout cas quand on
|
||||
n'a pas les bons réflexes !
|
||||
|
||||
Elle arrive par exemple dans les cas suivants :
|
||||
|
||||
\Begin{minipage}{0.3\textwidth}
|
||||
```c
|
||||
int *test = NULL;
|
||||
*test = 42;
|
||||
```
|
||||
\End{minipage} \hfill \Begin{minipage}{0.3\textwidth}
|
||||
```c
|
||||
int *test = NULL;
|
||||
printf("%d\n", *test);
|
||||
```
|
||||
\End{minipage} \hfill \Begin{minipage}{0.3\textwidth}
|
||||
```c
|
||||
int *test = 215342565;
|
||||
*test = 42;
|
||||
```
|
||||
\End{minipage}
|
||||
|
||||
Dans le premier cas, on essaye d'assigner une valeur à l'adresse mémoire `NULL`
|
||||
-- c'est-à-dire, l'adresse 0, ce qui est une erreur. De même, dans le
|
||||
second cas, on essaye de lire cette valeur -- c'est tout autant interdit. Dans
|
||||
le troisième cas, on fait de même, mais à une adresse au hasard -- les chances
|
||||
que cette adresse corresponde à de la mémoire qui nous est réservée sont
|
||||
infimes !
|
||||
|
||||
Prenons l'exemple suivant :
|
||||
|
||||
\Begin{minipage}{0.39\textwidth}
|
||||
```c
|
||||
struct liste {
|
||||
struct liste* suivant;
|
||||
int valeur;
|
||||
};
|
||||
typedef struct liste liste_t;
|
||||
|
||||
// Alloue une nouvelle cellule de
|
||||
// valeur `val`
|
||||
liste_t* alloc_cell(int val) {
|
||||
liste_t* cell =
|
||||
malloc(sizeof(liste_t));
|
||||
cell->valeur = val;
|
||||
cell->suivant = NULL;
|
||||
return cell;
|
||||
}
|
||||
```
|
||||
\End{minipage}\hfill\Begin{minipage}{0.58\textwidth}
|
||||
```c
|
||||
// Affiche les `taille` 1ères valeurs de `liste`
|
||||
void afficher_liste(liste_t* liste, int taille) {
|
||||
liste_t* cur = liste;
|
||||
for(int pos=0; pos < taille; ++pos) {
|
||||
printf("%d\n", cur->valeur);
|
||||
cur = cur->suivant;
|
||||
}
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
int n = atoi(argv[1]); // premier argument
|
||||
liste_t* tete = alloc_cell(1);
|
||||
tete->suivant = alloc_cell(2);
|
||||
afficher_liste(tete, n);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
\End{minipage}
|
||||
|
||||
Ce programme n'est pas correct si on lui passe `n > 2` : on tente d'afficher
|
||||
plus de valeurs de la liste qu'il n'y en a. Et en effet :
|
||||
|
||||
```GDB
|
||||
$ ./test 3
|
||||
1
|
||||
2
|
||||
Segmentation fault (core dumped)
|
||||
```
|
||||
|
||||
# Premier réflexe : `valgrind`
|
||||
|
||||
[Valgrind](https://fr.wikipedia.org/wiki/Valgrind) est un logiciel analysant
|
||||
les accès mémoire d'un programme. Il peut servir de débuggeur rudimentaire,
|
||||
mais analyse également les *fuites mémoire*. Il s'utilise très facilement : il
|
||||
suffit de rajouter `valgrind` en tête de sa ligne de commande.
|
||||
|
||||
```GDB
|
||||
$ valgrind ./test 3
|
||||
[...]
|
||||
==862386== Process terminating with default action of signal 11 (SIGSEGV): dumping core
|
||||
==862386== Access not within mapped region at address 0x8
|
||||
==862386== at 0x1091CE: afficher_liste (exemple.c:20)
|
||||
==862386== by 0x109253: main (exemple.c:29)
|
||||
[...]
|
||||
```
|
||||
|
||||
Valgrind commence par nous rappeler qu'il s'agit d'une *segfault* : `SIGSEGV`
|
||||
est encore un de ses autres noms\footnote{C'est en réalité plutôt le nom de la
|
||||
réaction de Linux face à une segfault -- \textit{cf.} les \textit{signaux
|
||||
UNIX} si vous êtes curieux·se.}.
|
||||
|
||||
Dans les lignes *avant* ce message (éludées ici), valgrind liste les erreurs
|
||||
non-critiques rencontrées. Bien souvent, ces erreurs correspondent se
|
||||
traduisent par des problèmes incompréhensibles plus tard, et mieux vaut les
|
||||
inspecter de plus près !
|
||||
|
||||
Pour une *segfault*, la partie la plus importante est celle recopiée ici. Elle
|
||||
nous indique, de bas en haut, que :
|
||||
|
||||
* dans la fonction `main`, fichier `exemple.c`, ligne 29, …
|
||||
* on appelle la fonction `afficher_liste`, fichier `exemple.c`, et ligne 20…
|
||||
* on crash (segfault)
|
||||
|
||||
La première ligne nous indique la position du bug, les lignes suivantes nous
|
||||
indiquent d'où on vient dans le programme.
|
||||
|
||||
C'est certes peu d'informations (la ligne de l'erreur seulement -- pas la
|
||||
variable concernée, etc), mais c'est déjà mieux que juste `Segmentation fault
|
||||
(core dumped)`, et souvent suffisant.
|
||||
|
||||
# Si c'est plus compliqué : gdb
|
||||
|
||||
Le debugger `gdb` est plus compliqué à utiliser, mais plus puissant. Lorsqu'on
|
||||
l'appelle
|
||||
|
||||
```GDB
|
||||
$ gdb ./test
|
||||
[...]
|
||||
(gdb)
|
||||
```
|
||||
|
||||
on entre en *mode interactif* : gdb attend des commandes. Notez qu'on ne
|
||||
**passe pas nos arguments à gdb** : `gdb ./test` et non `gdb ./test 3`.
|
||||
|
||||
Il faut tout d'abord dire à gdb de lancer le programme (c'est ici qu'on passe le 3 !)
|
||||
|
||||
```GDB
|
||||
(gdb) run 3
|
||||
[...]
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x00005555555551ce in afficher_liste (tete=0x5555555592a0, taille=3) at exemple.c:20
|
||||
20 printf("%d\n", cur->valeur);
|
||||
(gdb)
|
||||
```
|
||||
|
||||
`gdb` nous dit qu'on a une *segfault* dans `afficher_liste`, ligne 20 de
|
||||
`exemple.c` -- rien de nouveau. Il nous donne les valeurs des arguments de
|
||||
cette fonction (`taille=3`, et une adresse pour `tete`). Mais surtout, *on
|
||||
reste en mode interactif* ! On peut lui demander la même chose qu'à `valgrind`,
|
||||
savoir d'où on vient :
|
||||
|
||||
```GDB
|
||||
(gdb) backtrace
|
||||
#0 0x00005555555551ce in afficher_liste (tete=0x5555555592a0, taille=3) at exemple.c:20
|
||||
#1 0x0000555555555254 in main (argc=2, argv=0x7fffffffe4b8) at exemple.c:29
|
||||
```
|
||||
|
||||
On peut lui demander d'afficher des valeurs *au moment du crash* :
|
||||
```GDB
|
||||
(gdb) print pos
|
||||
2
|
||||
(gdb) print cur
|
||||
(liste_t *) 0x0
|
||||
```
|
||||
|
||||
...ou la même chose, mais dans `main`, au moment de l'appel de fonction :
|
||||
```GDB
|
||||
(gdb) frame 1 # le même 1 qu'en début de ligne de `backtrace`
|
||||
(gdb) print n
|
||||
3
|
||||
(gdb) frame 0 # et de retour dans `afficher_liste`
|
||||
```
|
||||
|
||||
Le debugger `gdb` est *beaucoup* plus puissant que ça, avec des dizaines de
|
||||
fonctionnalités : renseignez-vous sur les *breakpoints* par exemple. Il est
|
||||
également très pratique pour débugger des erreurs classiques, et pas seulement
|
||||
des *segfaults*.
|
BIN
poly/src/a.out
Executable file
BIN
poly/src/a.out
Executable file
Binary file not shown.
31
poly/src/exemple.c
Normal file
31
poly/src/exemple.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct liste {
|
||||
struct liste* suivant;
|
||||
int valeur;
|
||||
};
|
||||
typedef struct liste liste_t;
|
||||
|
||||
liste_t* alloc_cell(int val) {
|
||||
liste_t* cell = malloc(sizeof(liste_t));
|
||||
cell->valeur = val;
|
||||
cell->suivant = NULL;
|
||||
return cell;
|
||||
}
|
||||
|
||||
void afficher_liste(liste_t* tete, int taille) {
|
||||
liste_t* cur = tete;
|
||||
for(int pos=0; pos < taille; ++pos) {
|
||||
printf("%d\n", cur->valeur);
|
||||
cur = cur->suivant;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int n = atoi(argv[1]);
|
||||
liste_t* tete = alloc_cell(1);
|
||||
tete->suivant = alloc_cell(2);
|
||||
afficher_liste(tete, n);
|
||||
return 0;
|
||||
}
|
BIN
poly/src/test
Executable file
BIN
poly/src/test
Executable file
Binary file not shown.
BIN
poly/src/vgcore.859687
Normal file
BIN
poly/src/vgcore.859687
Normal file
Binary file not shown.
BIN
poly/src/vgcore.862386
Normal file
BIN
poly/src/vgcore.862386
Normal file
Binary file not shown.
53
poly/syntax/gdb-bt.xml
Normal file
53
poly/syntax/gdb-bt.xml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "language.dtd">
|
||||
|
||||
<language name="GDB Backtrace" section="Other"
|
||||
version="3" kateversion="3.4"
|
||||
extensions="*.kcrash;*.crash;*.bt"
|
||||
mimetype=""
|
||||
author="Milian Wolff (mail@milianw.de)" license="LGPL">
|
||||
|
||||
<highlighting>
|
||||
|
||||
<contexts>
|
||||
<context name="apache" attribute="Normal" lineEndContext="#stay">
|
||||
<DetectChar char="#" context="stackframe" firstNonSpace="true" />
|
||||
<IncludeRules context="oneliners" />
|
||||
</context>
|
||||
<context name="oneliners" attribute="Normal" lineEndContext="#pop">
|
||||
<StringDetect attribute="Crash" context="#stay" String="[KCrash Handler]" />
|
||||
<StringDetect String="Thread" firstNonSpace="true" context="thread" />
|
||||
<StringDetect String="[Current thread" firstNonSpace="true" context="thread" />
|
||||
</context>
|
||||
<context name="stackframe" attribute="Normal" lineEndContext="#stay">
|
||||
<RegExpr attribute="Normal" context="identifier" String="((?:[^ ]|<.+>)+::)?([^ :]+)\s*\(" lookAhead="true" />
|
||||
<Detect2Chars attribute="Normal" char="a" char1="t" context="file" />
|
||||
<StringDetect attribute="Normal" String="from" context="file" />
|
||||
<IncludeRules context="oneliners" />
|
||||
</context>
|
||||
<context name="identifier" attribute="Normal" lineEndContext="#stay" dynamic="true">
|
||||
<StringDetect attribute="QualifiedIdentifier" String="%2" dynamic="true" />
|
||||
<StringDetect attribute="Function" String="%2" dynamic="true" />
|
||||
<RegExpr attribute="Crash" String="\b0x0\b" />
|
||||
<DetectChar char=")" context="#pop" />
|
||||
</context>
|
||||
<context name="file" attribute="File" lineEndContext="#pop#pop">
|
||||
<DetectChar char=":" context="#pop#pop" attribute="Normal" />
|
||||
</context>
|
||||
<context name="thread" attribute="Thread" lineEndContext="#pop">
|
||||
</context>
|
||||
</contexts>
|
||||
|
||||
<itemDatas>
|
||||
<itemData name="Normal" defStyleNum="dsNormal" spellChecking="false" />
|
||||
<itemData name="Crash" defStyleNum="dsError" spellChecking="false" underline="false" bold="true" />
|
||||
<itemData name="QualifiedIdentifier" defStyleNum="dsOthers" spellChecking="false" />
|
||||
<itemData name="Function" defStyleNum="dsFunction" spellChecking="false" bold="true" />
|
||||
<itemData name="File" defStyleNum="dsDataType" spellChecking="false" />
|
||||
<itemData name="Thread" defStyleNum="dsBaseN" spellChecking="false" />
|
||||
</itemDatas>
|
||||
</highlighting>
|
||||
|
||||
</language>
|
||||
|
||||
<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->
|
97
poly/syntax/gdb.xml
Normal file
97
poly/syntax/gdb.xml
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "language.dtd">
|
||||
|
||||
<language name="GDB" section="Other"
|
||||
version="6" kateversion="5.0"
|
||||
extensions="*.gdb"
|
||||
mimetype=""
|
||||
author="Milian Wolff (mail@milianw.de)" license="LGPL">
|
||||
|
||||
<highlighting>
|
||||
<list name="commands">
|
||||
<item>break</item>
|
||||
<item>rbreak</item>
|
||||
<item>run</item>
|
||||
<item>continue</item>
|
||||
<item>backtrace</item>
|
||||
<item>detach</item>
|
||||
<item>quit</item>
|
||||
<item>up</item>
|
||||
<item>down</item>
|
||||
<item>frame</item>
|
||||
<item>where</item>
|
||||
<item>info</item>
|
||||
<item>ptype</item>
|
||||
<item>print</item>
|
||||
<item>call</item>
|
||||
<item>catch</item>
|
||||
<item>condition</item>
|
||||
<item>command</item>
|
||||
<item>set</item>
|
||||
<item>watch</item>
|
||||
<item>awatch</item>
|
||||
<item>thread</item>
|
||||
<item>list</item>
|
||||
<item>dprintf</item>
|
||||
<item>target</item>
|
||||
<item>end</item>
|
||||
<item>source</item>
|
||||
<item>next</item>
|
||||
<item>nexti</item>
|
||||
<item>step</item>
|
||||
<item>stepi</item>
|
||||
<item>finish</item>
|
||||
<item>start</item>
|
||||
<item>reverse-continue</item>
|
||||
<item>reverse-next</item>
|
||||
<item>reverse-step</item>
|
||||
<item>reverse-nexti</item>
|
||||
<item>reverse-stepi</item>
|
||||
<item>reverse-finish</item>
|
||||
<item>checkpoint</item>
|
||||
<item>restart</item>
|
||||
</list>
|
||||
<contexts>
|
||||
<context name="default" attribute="Normal" lineEndContext="#stay">
|
||||
<Detect2Chars char="#" char1=" " context="comment" />
|
||||
<!-- highlight GDB command listings -->
|
||||
<StringDetect String="(gdb)" context="gdb" firstNonSpace="true" />
|
||||
<StringDetect String="(rr)" context="gdb" firstNonSpace="true" />
|
||||
<!-- continuation of commands -->
|
||||
<DetectChar char=">" context="gdb" firstNonSpace="true" />
|
||||
<!-- output of backtrace-generating commands -->
|
||||
<IncludeRules context="##GDB Backtrace" />
|
||||
</context>
|
||||
<context name="gdb" attribute="CommandLine" lineEndContext="#pop">
|
||||
<keyword attribute="Command" String="commands" context="args" />
|
||||
<WordDetect String="python" context="python" attribute="Command" />
|
||||
<DetectChar char="#" context="comment" />
|
||||
</context>
|
||||
<context name="args" attribute="CommandLine" lineEndContext="#pop">
|
||||
<DetectChar char="#" context="comment" />
|
||||
</context>
|
||||
<context name="comment" attribute="Comment" lineEndContext="#pop">
|
||||
<DetectSpaces />
|
||||
<IncludeRules context="##Comments" />
|
||||
</context>
|
||||
<context name="python" attribute="Normal" lineEndContext="#stay">
|
||||
<WordDetect String="end" context="#pop" attribute="Command" />
|
||||
<IncludeRules context="##Python" />
|
||||
</context>
|
||||
</contexts>
|
||||
<itemDatas>
|
||||
<itemData name="Normal" defStyleNum="dsNormal" spellChecking="false" />
|
||||
<itemData name="CommandLine" defStyleNum="dsFunction" spellChecking="false" />
|
||||
<itemData name="Command" defStyleNum="dsKeyword" spellChecking="false" />
|
||||
<itemData name="Comment" defStyleNum="dsComment" spellChecking="false" />
|
||||
</itemDatas>
|
||||
</highlighting>
|
||||
<general>
|
||||
<keywords weakDeliminator="-" />
|
||||
<comments>
|
||||
<comment name="singleLine" start="#" />
|
||||
</comments>
|
||||
</general>
|
||||
</language>
|
||||
|
||||
<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->
|
Loading…
Reference in a new issue