はじめに
crash
コマンドを使った Linux kernel の vmcore の解析方法についてまとめて見ました。
検証に使った OS は、CentOS 6 です。
crash
コマンドとは
vmcore の解析に使うデバッグ用のツールです。gdb
のコマンドが(そのままだいたい)使えます。
詳しくはググってください。
vmcore
の取得方法
kdump
がセットアップしてあれば、kernel がクラッシュしたときには自動で vmcore を保存できます。保存先は、ローカルディスクや NFS、SSH でのリモートホストなどいろいろ設定できます。
# chkconfig kdump on
# service kdump restart
vmcore 取得のためには、OS をクラッシュさせる必要があります。OS がクラッシュするトリガーは色々設定できますが、ここでは以下の状況でクラッシュさせるように設定します。
- /etc/sysctl.conf
(以下の設定を追加すると、hung_task が発生した際に、OS をクラッシュさせます。)
kernel.hung_task_panic = 1
(以下の設定を追加すると、OOM が発生した際に、OS をクラッシュさせます。)
vm.panic_on_oom = 1
(以下の設定を追加すると、softlockup が発生した際に、OS をクラッシュさせます。)
kernel.softlockup_panic = 1
以下のコマンドで設定を反映させます。
# sysctl -p
インストール
開発系のツール一式は入れておきます。
# yum groupinstall "Development Tools"
以下でテスト用にデバイスドライバなどを作成するのでそれに必要なパッケージもインストールしておきます。
# yum install yum-utils kernel-devel kernel-headers
crash
コマンドをインストールします。
# yum install crash
crash コマンドでコア解析するには、kernel のデバッグパッケージも必要なのでインストールしておきます。
# debuginfo-install kernel
デバイスドライバの作成
テスト用にわざとハングするデバイスドライバを作成します。
- devone.c
/*
* devone.c
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
MODULE_LICENSE("Dual BSD/GPL");
static int devone_devs = 1; /* device count */
static int devone_major = 0; /* dynamic allocation */
static int devone_minor = 0;
static struct cdev devone_cdev;
ssize_t devone_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int i;
unsigned char val = 0xff;
int retval;
msleep(180000); /* わざと 180秒スリープする */
for (i = 0 ; i < count ; i++) {
if (copy_to_user(&buf[i], &val, 1)) {
retval = -EFAULT;
goto out;
}
}
retval = count;
out:
return (retval);
}
struct file_operations devone_fops = {
.read = devone_read,
};
static int devone_init(void)
{
dev_t dev = MKDEV(devone_major, 0);
int alloc_ret = 0;
int major;
int cdev_err = 0;
alloc_ret = alloc_chrdev_region(&dev, 0, devone_devs, "devone");
if (alloc_ret)
goto error;
devone_major = major = MAJOR(dev);
cdev_init(&devone_cdev, &devone_fops);
devone_cdev.owner = THIS_MODULE;
devone_cdev.ops = &devone_fops;
cdev_err = cdev_add(&devone_cdev, MKDEV(devone_major, devone_minor), 1);
if (cdev_err)
goto error;
printk(KERN_ALERT "devone driver(major %d) installed.\n", major);
return 0;
error:
if (cdev_err == 0)
cdev_del(&devone_cdev);
if (alloc_ret == 0)
unregister_chrdev_region(dev, devone_devs);
return -1;
}
static void devone_exit(void)
{
dev_t dev = MKDEV(devone_major, 0);
cdev_del(&devone_cdev);
unregister_chrdev_region(dev, devone_devs);
printk(KERN_ALERT "devone driver removed.\n");
}
module_init(devone_init);
module_exit(devone_exit);
- Makefile
obj-m := devone.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
コンパイルしてデバイスを作成します。
# make
(devone.ko ができあがります。)
# insmod devone.ko
(できあがった kernel モジュールをインストールします。)
# lsmod | grep devone
# grep devone /proc/devices
(devone のメジャー番号を確認します。例 247 )
# mknod /dev/devone c 247 0
(デバイスファイルを作成します。)
# ls -l /dev/devone
これで、/dev/devone ファイルを読み込もうとすると 180秒間ハングします。
OS をクラッシュさせる
プロセスをハングさせる
わざと、/dev/devone を読み込んで hung_task を発生させます。
# dd if=/dev/devone of=/dev/null count=1 bs=1
hung_task が発生して、OS がクラッシュして、vmcore ができあがりました。デフォルトの設定のままですので、「/var/crash」以下にできあがります。
# ls -l /var/crash/
OOM を発生させる
適当な perl スクリプトなどでメモリを徐々にかつ大量に消費させます。
- test.pl
#!/usr/bin/perl
use strict;
my $i;
while(1){
$i .= "a"x10000000;
}
OOM が発生して、OS がクラッシュして、vmcore ができあがりました。
# ls -l /var/crash/
vmcore 解析
hung_task の場合
「crash バイナリ vmcore」を実行して、crash を起動する。
# cd /var/crash/127.0.0.1-2015-12-20-17:24:43/
# crash /usr/lib/debug/lib/modules/2.6.32-573.12.1.el6.x86_64/vmlinux vmcore
sys
コマンドで、システム関連の情報を表示。panic のメッセージも見れる。
crash> sys
KERNEL: /usr/lib/debug/lib/modules/2.6.32-573.12.1.el6.x86_64/vmlinux
DUMPFILE: vmcore [PARTIAL DUMP]
CPUS: 3
DATE: Sun Dec 20 17:24:40 2015
UPTIME: 00:03:12
LOAD AVERAGE: 0.62, 0.19, 0.06
TASKS: 120
NODENAME: centos6a.kinoshiy
RELEASE: 2.6.32-573.12.1.el6.x86_64
VERSION: #1 SMP Tue Dec 15 21:19:08 UTC 2015
MACHINE: x86_64 (997 Mhz)
MEMORY: 2 GB
PANIC: "Kernel panic - not syncing: hung_task: blocked tasks"
hung_task
が原因だとわかる。
log
コマンドで、kernel のメッセージバッファーの内容を表示する。
crash> log
... snip ...
INFO: task dd:1521 blocked for more than 120 seconds.
Not tainted 2.6.32-573.12.1.el6.x86_64 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
dd D 0000000000000002 0 1521 1449 0x00000080
ffff880078e3fdd8 0000000000000082 0000000000000000 ffff88007e560108
ffff880078e3fd88 ffffffff8118ecf7 000000208c45c069 ffff880037434880
ffff880037b696c0 00000000fffd8e34 ffff880037783ad8 ffff880078e3ffd8
Call Trace:
[<ffffffff8118ecf7>] ? __dentry_open+0x257/0x380
[<ffffffff815397b2>] schedule_timeout+0x192/0x2e0
[<ffffffff81089be0>] ? process_timeout+0x0/0x10
[<ffffffff8153991e>] schedule_timeout_uninterruptible+0x1e/0x20
[<ffffffff8108bd40>] msleep+0x20/0x30
[<ffffffffa0312148>] devone_read+0x28/0x90 [devone]
[<ffffffff81191abd>] ? rw_verify_area+0x5d/0xc0
[<ffffffff81192315>] vfs_read+0xb5/0x1a0
[<ffffffff811930c6>] ? fget_light_pos+0x16/0x50
[<ffffffff81192661>] sys_read+0x51/0xb0
[<ffffffff8100b0d2>] system_call_fastpath+0x16/0x1b
Kernel panic - not syncing: hung_task: blocked tasks
Pid: 58, comm: khungtaskd Not tainted 2.6.32-573.12.1.el6.x86_64 #1
Call Trace:
[<ffffffff81538231>] ? panic+0xa7/0x16f
[<ffffffff810ecd34>] ? watchdog+0x254/0x290
[<ffffffff810ecae0>] ? watchdog+0x0/0x290
[<ffffffff810a0fce>] ? kthread+0x9e/0xc0
[<ffffffff8100c28a>] ? child_rip+0xa/0x20
[<ffffffff810a0f30>] ? kthread+0x0/0xc0
[<ffffffff8100c280>] ? child_rip+0x0/0x20
PID 1521 の dd
コマンドがハングしていたのがわかる。
devone
モジュール内でハングしているのがわかる。
ps
コマンドでプロセスの状況を確認。 「UN」(uninterruptible)の状態だったとわかる。
crash> ps 1521
PID PPID CPU TASK ST %MEM VSZ RSS COMM
1521 1449 2 ffff880037783520 UN 0.0 105176 720 dd
set
コマンドで、カレントコンテクストを任意のプロセスに切り替え、files
コマンドでそのプロセスがオープンしているファイルを確認します。
crash> set 1521
PID: 1521
COMMAND: "dd"
TASK: ffff880037783520 [THREAD_INFO: ffff880078e3c000]
CPU: 2
STATE: TASK_UNINTERRUPTIBLE
crash> files
PID: 1521 TASK: ffff880037783520 CPU: 2 COMMAND: "dd"
ROOT: / CWD: /root/git/samples/kernel/devone-cdev_add
FD FILE DENTRY INODE TYPE PATH
0 ffff8800377805c0 ffff880075d1ef00 ffff880037e8c418 CHR /dev/devone
1 ffff8800377809c0 ffff88007eb435c0 ffff88007e560108 CHR /dev/null
2 ffff880037493280 ffff880075ccbd80 ffff88007eb64c98 CHR /dev/pts/0
ハングしていたプロセスは、/dev/devone
をアクセスしていたとわかる。
/dev/devone
が怪しい。
OOM の場合
「crash バイナリ vmcore」を実行して、crash を起動する。
# cd /var/crash/127.0.0.1-2015-12-20-18:23:16
# crash /usr/lib/debug/lib/modules/2.6.32-573.12.1.el6.x86_64/vmlinux vmcore
crash> sys
... snip ...
PANIC: "Kernel panic - not syncing: Out of memory: system-wide panic_on_oom is enabled"
OOM が原因でクラッシュしたとわかる。
crash> ps
PID PPID CPU TASK ST %MEM VSZ RSS COMM
... snip ...
1581 1578 2 ffff880037720040 IN 0.0 108304 244 bash
> 1632 1552 2 ffff8800379baab0 RU 77.8 3715784 1631992 test.pl
1633 1581 1 ffff88003798a040 IN 0.0 15024 328 top
test.pl が、77.8% のメモリを消費している。 test.pl が怪しい。
参考文献
- http://people.redhat.com/anderson/crash_whitepaper/
- Linuxデバイスドライバプログラミング http://www.amazon.co.jp/dp/4797346426