Kdump est une fonctionnalité du noyau Linux permettant de prendre un dump (une empreinte mémoire) lors d'un crash du système d'exploitation. Cette fonctionnalité permet d'analyser après coup ce qui s'est passé sur le serveur au moment du crash et quel a été le processus engendrant ce crash.
Après le crash d'un serveur, l'administrateur système que vous êtes espère de tout son être qu'un crashdump soit disponible dans le répertoire /var/crash/ (si tel est la destination choisie)
1. Kdump - Analyse - Gaëtan Trellu
Analyse d'un kernel (crash, core) dump
Analyse d'un kernel (crash, core) dump
Introduction
Pré-requis
Analyse
Premiers pas
État de la mémoire
La backtrace, un indispensable de crash
Désassembler une fonction
Sources
Qui à fait quoi ?
Conclusion
Sources
Introduction
Après le crash d'un serveur, l'administrateur système que vous êtes espère de tout son être qu'un crashdump soit disponible dans le répertoire /
var/crash/ (si tel est la destination choisie). Dans cet exemple, la cause du crash du serveur sera liée au chargement d'un module noyau. Ce
dernier appellera dès son chargement la fonction du noyau Linux : panic()
Configuration de Kdump
Si Kdump n'est pas encore configuré sur le serveur alors je vous invite à parcourir la procédure suivante : Debian - Kdump
Pré-requis
La lecture d'un crashdump Linux se fait à l'aide de l'outil crash, ce dernier permet d'interpréter de manière lisible pour l'homme les données
contenues dans le fichier binaire.
# aptitude install crash gdb binutils
Le paquet binutils est nécessaire car il contient entre autre la commande strings qui est utilisée par crash. Sans cette commande l'erreur
suivante s'affichera au lancement de la commande crash :
sh: 1: /usr/bin/strings: not found
Analyse
À lire
Cette analyse est effectuée depuis une distribution Debian GNU/Linux. Le crashdump analysé provient d'un serveur exécutant une
distribution Debian GNUX/Linux.
Les différences avec les autres distributions sont minimes.
Dans le répertoire /var/crash/ doit se trouver un répertoire nommé de la date du crash sous la forme suivante (YYYYMMDDHHMM). Ce
répertoire contient deux choses (sous Debian GNU/Linux) :
lrwxrwxrwx 1 root root
55 Jul 22 19:36 kernel_link ->
/usr/lib/debug/lib/modules/2.6.32-amd64/vmlinux
-r-------- 1 root root 2050769968 Jul 22 19:47 vmcore.201307221946
2. kernel_link est lien symbolique, il est créé à partir de la valeur DEBUG_KERNEL définie dans le fichier de configuration /etc/default/kdum
p-tools.
Premiers pas
En fonction de la taille du crashdump il est possible que crash soit un peu long rendre la main, c'est normal.
# crash kernel_link vmcore.201307221946
Résultat :
3. crash 6.0.6
Copyright (C) 2002-2012 Red Hat, Inc.
Copyright (C) 2004, 2005, 2006 IBM Corporation
Copyright (C) 1999-2006 Hewlett-Packard Co
Copyright (C) 2005, 2006 Fujitsu Limited
Copyright (C) 2006, 2007 VA Linux Systems Japan K.K.
Copyright (C) 2005 NEC Corporation
Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions. Enter "help copying" to see the conditions.
This program has absolutely no warranty. Enter "help warranty" for
details.
GNU gdb (GDB) 7.3.1
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu"...
KERNEL: kernel_link
DUMPFILE: vmcore.201307221946
CPUS: 1
DATE: Mon Jul 22 19:45:42 2013
UPTIME: 00:09:40
LOAD AVERAGE: 0.01, 0.02, 0.00
TASKS: 64
NODENAME: kernel-debug
RELEASE: 2.6.32-amd64
VERSION: #1 SMP Mon Jul 8 15:25:50 EDT 2013
MACHINE: x86_64 (1995 Mhz)
MEMORY: 2 GB
PANIC: "[ 580.890926] Kernel panic - not syncing: Panic function
called, don't be worried the system will be down !"
PID: 2655
COMMAND: "insmod"
TASK: ffff88007b0b8cc0 [THREAD_INFO: ffff88007a0c8000]
CPU: 0
STATE: TASK_RUNNING (PANIC)
Par défaut crash décide d'afficher les "méta-données" du crashdump ainsi que des copyrights, pour passer outre toutes ces informations il
existe le paramètre -s à positionner juste avant kernel_link.
Les premières informations fournies sont les suivantes :
Le nombre de CPU
La mémoire vive totale
La date exacte du crash
La charge du serveur au moment du crash
4. Le nombre de processus au moment du crash
La version du noyau utilisé
etc...
La première interprétation des informations serait la suivante (Cluedo style
):
Homicide
Le décès eu lieu le 22 juillet 2013 à 19h45, la charge présente sur Mr kernel_debug était moindre. Seuls 64 processus étaient en
cours d'exécution au moment du drame.
Le sujet est un serveur de type 1 processeur 64Bits cadencé à 2Ghz, doté de 2Go de mémoire, ses origines remontent à l'an 2.6.
32.
Le coupable, un jeune processus nommé insmod connu de nos services sous le PID 2655. Il causa le décès du sujet par Kernel
panic et le coup mortel fut exécuté dans le CPU 0.
Le mobile du crime : "Panic function called, don't be worried the system will be down !"
Les informations indiquées permettent déjà d'avoir une idée de ce qui s'est passé sur le serveur, un kernel panic.
État de la mémoire
Il est intéressant de connaître l'état de la mémoire au moment du crash, savoir par exemple si l' OOM killer est passé faire le ménage...
crash> kmem -i
Résultat :
PAGES
TOTAL MEM
FREE
USED
SHARED
BUFFERS
CACHED
SLAB
TOTAL SWAP
SWAP USED
SWAP FREE
TOTAL
474877
437169
37708
6007
4375
17116
9631
0
0
0
PERCENTAGE
1.8 GB
1.7 GB
92%
147.3 MB
7%
23.5 MB
1%
17.1 MB
0%
66.9 MB
3%
37.6 MB
2%
0
0 100%
0
0%
---TOTAL
TOTAL
TOTAL
TOTAL
TOTAL
TOTAL
---of TOTAL
of TOTAL
of
of
of
of
of
of
MEM
MEM
MEM
MEM
MEM
MEM
SWAP
SWAP
Dans le cas présent la consommation mémoire du serveur au moment du crash n'est pas la raison du kernel panic, il reste 92% de la
mémoire disponible. Il est donc peut probable que la piste d'un Out Of Memory soit à suivre.
La backtrace, un indispensable de crash
La commande bt (backtrace) de l'outil crash est un must dans l'analyse de crashdump. Indispensable mais pas obligatoire (non ce n'est pas
une contradiction
), il est possible de passer par les commandes log et runq pour obtenir à peu de chose près les mêmes informations.
bt permet d'obtenir de plus amples informations sur le processus responsable du crash du serveur.
crash> bt
Résultat :
5. PID: 2655
TASK: ffff88007b0b8cc0 CPU: 0
COMMAND: "insmod"
#0 [ffff88007a0c9d60] machine_kexec at ffffffff810363e4
#1 [ffff88007a0c9dd0] crash_kexec at ffffffff810e68e2
#2 [ffff88007a0c9ea0] panic at ffffffff8154d0f3
#3 [ffff88007a0c9f10] init_module at ffffffffa018b025 [panic]
#4 [ffff88007a0c9f20] do_one_initcall at ffffffff8100204c
#5 [ffff88007a0c9f50] sys_init_module at ffffffff810db4c6
#6 [ffff88007a0c9f80] system_call_fastpath at ffffffff8100b182
RIP: 00007f31d21fa14a RSP: 00007fffb9a1b1e8 RFLAGS: 00010206
RAX: 00000000000000af RBX: ffffffff8100b182 RCX: 00007f31d21f648a
RDX: 00007f31d24b9f88 RSI: 000000000004b6ce RDI: 00007f31d288a000
RBP: 00007f31d2fbc2a0
R8: 0000000000000004
R9: 0000000000000000
R10: 00007f31d21f648a R11: 0000000000000206 R12: 0000000000000000
R13: 00007f31d2fbb090 R14: 00007f31d24b9f88 R15: 00007f31d2fbc1a0
ORIG_RAX: 00000000000000af CS: 0033 SS: 002b
On récupère certaines informations déjà obtenues plus haut, comme par exemple :
Le PID (2655)
L'ID du CPU qui exécutait le processus (0)
La commande exécutée (insmod)
Mais le plus intéressant est que la fonction causant l'exception ainsi que le RIP sont indiqués !
#6 [ffff88007a0c9f80] system_call_fastpath at ffffffff8100b182
RIP: 00007f31d21fa14a
RIP est un registre du processeur. Il contient l'adresse mémoire de l'instruction du programme en cours d'exécution. Exemple :
Le programme panicdump.c appelle plusieurs fonctions (c'est un exemple farfelu) :
open()
read()
close()
Si le programme panicdump.c est la cause du crash alors l'une des fonctions citées ci-dessus sera certainement dans le registre RIP
RIP / EIP
En fonction de l'architecture CPU les registres peuvent se nommer différemment et leur nombre peut varier.
x86 = Les registres se nomment E(XX) et non R(XX), exemple : EIP et non RIP
x86_64 = Tous les registres se nomment R(XX), il y en a 8 de plus qu'en 32Bits
Lien à consulter pour de plus amples informations : http://docs.oracle.com/cd/E19205-01/820-4220/
Au final ce que la backtrace nous apprend c'est que la fonction machine_kexec() a été appelée par la fonction crash_kernel() qui fut
appelée par la fonction panic() elle même appelée par la fonction init_module() appelée par la fonction do_one_initcall() qui à son
tour fut appelée par sys_init_module() finalement appelée par system_call_fastpath() qui est la fonction qui a causé le crash du
serveur. Toujours avec nous ?
La valeur ffffffff8100b182 située à côté de la fonction system_call_fastpath() est un offset, cet offset indique l'endroit dans la fonction
qui a posé problème.
Désassembler une fonction
Connaître la fonction qui a causé le problème c'est utile certes mais qu'a t-elle fait pour causer un appel de la fonction panic() ? La commande
dis permet de désassembler une fonction, en l'occurrence la fonction system_call_fastpath() ainsi que son offset.
La fonction system_call_fastpath() peut aussi être appelée symbole.
6. Il est possible de traduire l'adresse virtuelle ffffffff8100b182 par system_call_fastpath+22, pour faire cela la commande sym sera
utilisée.
crash> sym ffffffff8100b182
Résultat :
ffffffff8100b182 (t) system_call_fastpath+22
/usr/src/linux-2.6.32/arch/x86/kernel/entry_64.S: 505
L'option -r de dis permet de reverser une fonction, l'option -l permet d'obtenir plus de lisibilité. Exemple :
crash> dis -lr ffffffff8100b182
Ou :
crash> dis -lr
system_call_fastpath+22
Résultat :
/usr/src/linux-2.6.32/arch/x86/kernel/entry_64.S: 501
0xffffffff8100b16c <system_call_fastpath>:
cmp
/usr/src/linux-2.6.32/arch/x86/kernel/entry_64.S: 502
0xffffffff8100b172 <system_call_fastpath+6>:
ja
/usr/src/linux-2.6.32/arch/x86/kernel/entry_64.S: 503
0xffffffff8100b178 <system_call_fastpath+12>:
mov
/usr/src/linux-2.6.32/arch/x86/kernel/entry_64.S: 504
0xffffffff8100b17b <system_call_fastpath+15>:
callq
*-0x7ea96ba0(,%rax,8)
/usr/src/linux-2.6.32/arch/x86/kernel/entry_64.S: 505
0xffffffff8100b182 <system_call_fastpath+22>:
mov
$0x1fe,%rax
0xffffffff8100b262
%r10,%rcx
%rax,0x20(%rsp)
Le désassemblage nous indique que la fonction system_call_fastpath()+22 a essayé de copier le contenu du registre RSP dans le registre
RAX, c'est cette instruction qui a causé une exception.
Sources
Le code source de cette fonction se trouve dans le fichier /usr/src/linux-2.6.32/arch/x86/kernel/entry_64.S
arch/x86/kernel/entry_64.S
system_call_fastpath:
cmpq $__NR_syscall_max,%rax
ja badsys
movq %r10,%rcx
call *sys_call_table(,%rax,8)
movq %rax,RAX-ARGOFFSET(%rsp)
# XXX:
rip relative
7. Cette fonction regroupe des instructions d'assembleur pour des processeurs de type x86_64.
La lettre q présente dans les instructions cmpq et movq sont typiques des processeurs x86_64.
Qui à fait quoi ?
Il peut être parfois intéressant de savoir comment le processus a été exécuté, la commande ps propose deux options qui permettent de connaître
l'arborescence père et l’arborescence fils (si cette dernière existe).
1. -p : affiche l'arborescence père
2. -c : affiche l'arborescence fils
Dans le cas suivant, le processus 2655 sera passé au peigne fin.
crash> ps -p 2655
Résultat :
PID: 0
TASK: ffffffff81875020 CPU: 0
COMMAND: "swapper"
PID: 1
TASK: ffff88007eff4040 CPU: 0
COMMAND: "init"
PID: 1968
TASK: ffff88007a87d1c0 CPU: 0
COMMAND: "sshd"
PID: 1998
TASK: ffff88007d66c140 CPU: 0
COMMAND: "sshd"
PID: 2000
TASK: ffff88007ab7c640 CPU: 0
COMMAND: "bash"
PID: 2655
TASK: ffff88007b0b8cc0 CPU: 0
COMMAND: "insmod"
La commande insmod a été exécutée depuis un terminal bash par un utilisateur connecté en SSH.
Le processus swapper est en fait l’ordonnanceur (scheduler), il est présent autant de fois qu'il y a de cores sur le serveur.
En tant qu'administrateur système vous savez certainement que la commande insmod permet de charger un module noyau or actuellement nous
n'avons aucune information sur le module qui a été chargé par cette commande. Encore une fois la commande ps permet d'avoir de plus amples
informations sur l'environnement qui a exécuté la commande.
crash> ps -a 2655
Résultat :
8. PID: 2655
TASK: ffff88007b0b8cc0 CPU: 0
COMMAND: "insmod"
ARG: insmod panic.ko
ENV: TERM=xterm
SHELL=/bin/bash
SSH_CLIENT=88.143.117.83 48720 22
SSH_TTY=/dev/pts/0
USER=root
SSH_AUTH_SOCK=/tmp/ssh-Dk4T8LiOcC/agent.1998
MAIL=/var/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/root/panic_module
LANG=en_US.UTF-8
SHLVL=1
HOME=/root
LS_OPTIONS=--color=auto
LOGNAME=root
SSH_CONNECTION=88.143.117.83 48720 10.13.45.7 22
_=/sbin/insmod
OLDPWD=/root
Les informations ci-dessous permettent de savoir que le module panic.ko est le module qui a causé le kernel panic du serveur. Le
coupable est physique car il s'est connecté à un terminal depuis SSH :
SSH_CLIENT=88.143.117.83
SSH_TTY=/dev/pts/0
USER=root
PWD=/root/panic_module
ARG: insmod panic.ko
En théorie le module panic devrait être chargé, pour le vérifier la commande mod sera utilisée.
crash> mod
Résultat :
9. MODULE
NAME
ffffffffa0005500
ffffffffa000ba40
ffffffffa00113c0
ffffffffa0017940
ffffffffa001c4a0
ffffffffa0020a60
ffffffffa0027780
ffffffffa002e620
ffffffffa0045fc0
ffffffffa0057240
ffffffffa008b280
ffffffffa00af300
ffffffffa00bab60
ffffffffa00c0240
ffffffffa00d2000
ffffffffa00e3c60
ffffffffa00ec420
ffffffffa00f4760
ffffffffa010b180
ffffffffa011c640
ffffffffa0168140
ffffffffa018b100
SIZE OBJECT FILE
ata_piix
24437 (not loaded)
virtio
5094 (not loaded)
virtio_ring
8858 (not loaded)
pata_acpi
3717 (not loaded)
virtio_pci
7141 (not loaded)
ata_generic
3885 (not loaded)
virtio_net
17578 (not loaded)
virtio_blk
7501 (not loaded)
jbd
83562 (not loaded)
mbcache
7945 (not loaded)
ext3
244318 (not loaded)
i2c_core
31840 (not loaded)
i2c_piix4
13136 (not loaded)
soundcore
8201 (not loaded)
snd
76223 (not loaded)
virtio_balloon
4778 (not loaded)
snd_timer
22985 (not loaded)
snd_page_alloc
8710 (not loaded)
snd_pcm
93468 (not loaded)
snd_pcsp
8842 (not loaded)
ipv6
340294 (not loaded)
panic
1004 (not loaded)
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
[CONFIG_KALLSYMS]
Conclusion
Dans la plupart des cas si le crashdump est lisible alors la source du crash du serveur pourra être identifiée.
Debugger un crashdump n'est pas une chose facile, plusieurs paramètres peuvent entrer en cause. Si par exemple les sources d'un
programme ne sont pas disponibles alors la difficulté sera accentuée.
La commande crash permet de faire énormément de choses avec un crashdump, dans cette procédure je n'ai utilisé que quelques commandes
mais sachez que la commande crash regorge de commandes.
ps
log
bt -f
runq
foreach bt -f
task
Bref, la commande help listera toutes les commandes disponibles.
Sources
Le module panic.ko utilisé pour cette procédure
http://www.dedoimedo.com/computers/crash.html
http://magazine.redhat.com/2007/08/15/a-quick-overview-of-linux-kernel-crash-dump-analysis/
http://people.redhat.com/anderson/crash_whitepaper/