poly: first version

This commit is contained in:
Théophile Bastian 2021-11-22 15:04:41 +01:00
commit de7422c483
12 changed files with 417 additions and 0 deletions

9
poly/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.aux
*.dvi
*.fls
*.fdb*
*.log
*.ps
*.xdv
poly.tex
*.pdf

16
poly/Makefile Normal file
View 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
View 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
View 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
View 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

Binary file not shown.

31
poly/src/exemple.c Normal file
View 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

Binary file not shown.

BIN
poly/src/vgcore.859687 Normal file

Binary file not shown.

BIN
poly/src/vgcore.862386 Normal file

Binary file not shown.

53
poly/syntax/gdb-bt.xml Normal file
View 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="((?:[^ ]|&lt;.+&gt;)+::)?([^ :]+)\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
View 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; -->