panic

panic

どうした事か、FreeBSDを使っていたらpanicで落る事が3回あった。4度目の正直にならないように防止策を検討しないとな。

3度目の時、コンソール画面に目をこらしていたら(正確にはpanicした後の再起動時)、/var/crashの中を覗いてみろってご神託に気が付いた。

[sakae@fb ~]$ ls -ltr /var/crash/
total 733464
-rw-r--r--  1 root  wheel          5 Apr  9  2021 minfree
-rw-------  1 root  wheel        401 Sep 11 15:06 info.0
-rw-------  1 root  wheel  311848960 Sep 11 15:06 vmcore.0
-rw-r--r--  1 root  wheel     128501 Sep 11 15:06 core.txt.0
-rw-------  1 root  wheel        362 Sep 11 16:48 info.1
-rw-------  1 root  wheel  252727296 Sep 11 16:48 vmcore.1
-rw-r--r--  1 root  wheel     129010 Sep 11 16:48 core.txt.1
-rw-r--r--  1 root  wheel          2 Sep 15 07:38 bounds
-rw-------  1 root  wheel        401 Sep 15 07:38 info.2
-rw-------  1 root  wheel  253448192 Sep 15 07:38 vmcore.2
lrwxr-xr-x  1 root  wheel          6 Sep 15 07:38 info.last@ -> info.2
lrwxr-xr-x  1 root  wheel          8 Sep 15 07:38 vmcore.last@ -> vmcore.2
-rw-r--r--  1 root  wheel     134464 Sep 15 07:38 core.txt.2

infoを見ればいいんだな。

root@fb:/var/crash # cat info.2
Dump header from device: /dev/ada0p3
  Architecture: i386
  Architecture Version: 2
  Dump Length: 253448192
  Blocksize: 512
  Compression: none
  Dumptime: 2022-09-15 06:14:50 +0900
  Hostname: fb
  Magic: FreeBSD Kernel Dump
  Version String: FreeBSD 13.1-RELEASE-p2 GENERIC
  Panic String: vm_page_free_prep: page 0x3253f94 has references
  Dump Parity: 1363963683
  Bounds: 2
  Dump Status: good

これだけじゃ、餓鬼の使い、もとえM$並みの全く役にたたないメッセージだ。他にテキストなやつって事で、開いてみる。

root@fb:/var/crash # lv core.txt.2
fb dumped core - see /var/crash/vmcore.2
 :
panic: vm_page_free_prep: page 0x3253f94 has references

GNU gdb (GDB) 12.1 [GDB v12.1 for FreeBSD]
 :
Unread portion of the kernel message buffer:
WARNING !drm_modeset_is_locked(&crtc->mutex) failed at /wrkdirs/usr/ports/graphi
cs/drm-fbsd13-kmod/work/drm-kmod-drm_v5.4.191_1/drivers/gpu/drm/drm_atomic_helpe
r.c:621
#0 0x2679f329 at linux_dump_stack+0x19
#1 0x26e36eb2 at drm_atomic_helper_check_modeset+0x92
 :
ps -axlww

 UID  PID PPID  C PRI NI    VSZ RSS MWCHAN   STAT TT     TIME COMMAND
   0    0    0  0 -16  0      0   0 swapin   DLs   -  0:00.02 [kernel]
   :
------------------------------------------------------------------------
vmstat -s

   673894 cpu context switches
   322699 device interrupts
   :
------------------------------------------------------------------------
pstat -T

164/124987 files
0M/0M swap space
------------------------------------------------------------------------
netstat -s

tcp:
        2765 packets sent
        :
------------------------------------------------------------------------
fstat

USER     CMD          PID   FD MOUNT      INUM MODE         SZ|DV R/W
sakae    vi          1259 root /             2 drwxr-xr-x    1024  r
         :
------------------------------------------------------------------------
dmesg
   :
drmn0: <drmn> on vgapci0
vgapci0: child drmn0 requested pci_enable_io
vgapci0: child drmn0 requested pci_enable_io
[drm] Unable to create a private tmpfs mount, hugepage support will be disabled(-19).
 :
------------------------------------------------------------------------
kernel config

options CONFIG_AUTOGENERATED
ident   GENERIC
 :

3000行近い、その時の状況レポートだった。cscope経由でviを使っていたよ。それから、drmなんて語句も出てる。

思い起すと、Xがvesaしか使っていなかったんで、インテル入ってるってドライバーを追加したんだった。ハードの対応に嬉しくなってLinux由来のDRMサポートが動き出したんだな。そいつが、不安定なものだから、カーネルを巻き込んで落ちたって推測出来る。

ならば、インテル・ドライバーを外して様子をみよう。

where panic

所で、panicってどうなってるの? panic(9)なんてのがあるけど、余り役に立たないぞ。 こういう場合な、ソースに当るのが正解。

C symbol: panic

  File                     Function                  Line
0 fuse_vfsops.c            fuse_vfsop_unmount          485 panic("no private
                                                           data for mount
                                                           point?");
1 fuse_vnops.c             fuse_vnop_reclaim          1976 panic("FUSE: no vnode
                                                           data during
                                                           recycling");
2 msdosfs_denode.c         deextend                    515 panic("deextend: file
                                                           too large");
3 msdosfs_fat.c            extendfile                 1090 panic("extendfile:
                                                           pcbmap");
4 msdosfs_vfsops.c         msdosfs_sync               1011 panic("msdosfs_sync:
                                                           rofs mod");
5 msdosfs_vnops.c          msdosfs_create              166 panic("msdosfs_create
                                                           : no name");

 * Lines 1044-1050 of 2643, 1594 more - press the space bar to display more *

シンボル検索したら、2643ヶも語句が見付かった。それだけ厳しく監視してますって事だな。 Teoさんが言ってた、カーネルは気難しい愛人って感想が実感できますよ。

Global definition: panic

  File            Line
0 zfssubr.c         39 #define panic(...) do { \
1 zfs_context.h    158 extern void panic(const char *, ...) __NORETURN;
2 kernel.c         624 panic(const char *fmt, ...)
3 lstate.h         143 lua_CFunction panic;
4 bxe.h           1758 int panic;
5 kern_shutdown.c  839 panic(const char *fmt, ...)
6 subr_stats.c     196 #define panic(fmt, ...) do { \
7 aeabi_unwind.c    37 #define panic(x) (void )0
8 radix.c           52 #define panic(x) fprintf(stderr, "PANIC: %s", x), exit(1)
9 key_debug.c       83 #define panic(fmt, ...) { printf(fmt, ## __VA_ARGS__);
                       exit(-1); }
a dn_heap.c         56 #define panic(x...) fprintf(stderr, ## x), exit(1)
b test_dn_heap.c    44 #define panic(x...) fprintf(stderr, ## x), exit(1)
c kassert.h         75 void panic(const char *, ...) __dead2 __printflike(1, 2);

で、定義場所を探すと、あちこちに出て来る。No.5が本物で、後はエイリアスっぽいな。

/*
 * Panic is called on unresolvable fatal errors.  It prints "panic: mesg",
 * and then reboots.  If we are called twice, then we avoid trying to sync
 * the disks as this often leads to recursive panics.
 */
void
panic(const char *fmt, ...)
{
        va_list ap;

        va_start(ap, fmt);
        vpanic(fmt, ap);
}

本当の処理は、vpanicの中で行われている。SMPなシステムなら、余分なCPUを停止して、変な事がおきないように処置。それからスケジューラーを停止、それから、 kern_reboot を呼び出すとな。

この kern_reboot を見ていくと、doadump(TRUE)なんてのも行われている。ソースを眺めるのもいいけど、 kern_reboot(9) も折角用意したんだから見てね、なんてソースに書いてあった。

1.   If this is the first invocation of kern_reboot() and the
     RB_NOSYNC flag is not set in howto, syncs and unmounts the
     system disks by calling vfs_unmountall(9).

2.   Disables interrupts.

3.   If rebooting after a crash (i.e., if RB_DUMP is set in howto,
     but RB_HALT is not), saves a system crash dump.

4.   Runs any shutdown hooks previously registered.

5.   Prints a message indicating that the system is about to be
     halted or rebooted.

6.   If RB_HALT is set in howto, halts the system.  Otherwise,
     reboots the system.

要になるカーネルの関数は、マニュアルにも記述してあるのね。

dumpの本体は、 kern_dump.c に用意されてる。

crashinfo

上で、色々なpanic時の状況を報告するやつ、解析スクリプトになってた。勿論シェルスクリプトだ。perlとかpythonとかじゃないよ。

libexec/rc/rc.d/savecore から、呼ばれているっぽい。

NAME
     crashinfo – analyze a core dump of the operating system

DESCRIPTION
     The crashinfo utility analyzes a core dump saved by savecore(8).  It
     generates a text file containing the analysis in the same directory as
     the core dump.  For a given core dump file named vmcore.XX the generated
     text file will be named core.txt.XX.

どんな事をやってるか気楽に眺めてみる。/usr/src/usr.sbin/crashinfo/

file=`mktemp /tmp/crashinfo.XXXXXX`
if [ $? -eq 0 ]; then
        echo "bt" >> $file
        echo "quit" >> $file
        ${GDB%gdb}kgdb $KERNEL $VMCORE < $file
        rm -f $file
        echo
fi

こんな風にgdbを操作して捜査を走査するんだねぇ。同音異義が並んだ。

echo "------------------------------------------------------------------------"
echo "vmstat -s"
echo
vmstat -M $VMCORE -N $KERNEL -s

その他、これはと思われる状況を調べてる。ps,netstat,iostat …

いずれのコマンドも共通で、-M, -N をサポートしてる。何者かは想像付くけど、一応当っておく。

-M      Extract values associated with the name list from the specified
        core.

-N      If -M is also specified, extract the name list from the specified
        system instead of the default, which is the kernel image the
        system has booted from.

coreした中から、特定の変数なりテーブルを取出したい。例えばプロセステーブル。これが参照出来れば、その時に動いていたプロセス即ちアプリが判明する。

その為には、カーネルエリアの何処にそのエリアがあるかの情報が必要。それを正常なカーネルから貰ってくるって事だ。gdbでcoreを調べる時も、同じ事をやるね。

-M, -N

iostatの例を調べてみる。

        case 'M':
                memf = optarg;
                break;
        case 'N':
                nlistf = optarg;
                break;

if (memf != NULL) {
        kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);

        if ((ret = kvm_nlist(kd, nl)) != 0) {

kvm_openfiles は、Kernel Data Access Libraryを使う時のお決まり。 kvm_nlist は、下記のように欲しいテーブルをnlに入れて呼ぶ。これで、構造にアクセス出来る。

DESCRIPTION
     The kvm_nlist() function retrieves the symbol table entries indicated by
     the name list argument nl.  This argument points to an array of nlist
     structures, terminated by an entry whose n_name field is NULL (see

実際に使う時は下記のようにもう一段下のアクセスが必要

if (kvm_read(kd, nl[NL_TTY_LIST].n_value, &tl, sizeof tl) != sizeof tl)
        errx(1, "kvm_read(): %s", kvm_geterr(kd));
tp = TAILQ_FIRST(&tl);
while (tp != NULL) {
        if (kvm_read(kd, (u_long)tp, &tty, sizeof tty) != sizeof tty)
                errx(1, "kvm_read(): %s", kvm_geterr(kd));
[sakae@fb ~]$ pstat -t
      LINE   INQ  CAN  LIN  LOW  OUTQ  USE  LOW   COL  SESS  PGID STATE
  sysmouse     0    0    0    0     0    0    0     0     0     0 -
     ttyv0  1920    0    0  192  2016    0  202    21  1126  1127 Oi
     ttyv1  1920    0    0  192  2016    0  202     7   988   988 Oil
      :
     pts/0  7680    0    0  768  7812    0  782 99999  1115  1116 Oil
     pts/1  1920    0    0  192  2016    0  202   179  1119 16267 Oi
     pts/2  1920    0    0  192  2016   73  202     0  1121 16342 Oi

pstatのSEE ALSOには、 K. Thompson, UNIX Implementation.が名を連ねているなあ。

config

crashinfoで使われていた、カーネルのconfig状況確認プログラムで後追い。DEBUGオプションがついていたんですねぇ。で、これを眺めると、ムラムラと自分KERNELを作りたくなるんだけど、それをやっちゃうと後々面倒な事がおきるので自粛。

[sakae@fb /usr/src]$ config -x /boot/kernel/kernel
options CONFIG_AUTOGENERATED
ident   GENERIC
machine i386
cpu     I686_CPU
cpu     I586_CPU
cpu     I486_CPU
makeoptions     WITH_CTF=1
makeoptions     DEBUG=-g
options IICHID_SAMPLING
options VESA
device  sc
device  vt
device  vt_vga
device  vt_vbefb
device  agp
 :

by hand

crashinfoで使ってるgdbを手動操作してみたい。肝はそれぞれをフルパスで指定する事(特にkernelのpath)。手抜きをすると、そんなファイル正式じゃ無いと意地悪される。プチ、セキュリティーに配慮した対策になってる。なんたって、coreファイルには大事なデータが格納されてますからねぇ。取扱注意品ですよ。

root@fb:/tmp # /usr/local/bin/kgdb /boot/kernel/kernel /var/crash/vmcore.2
GNU gdb (GDB) 12.1 [GDB v12.1 for FreeBSD]
 :
Reading symbols from /boot/kernel/kernel...
Reading symbols from /usr/lib/debug//boot/kernel/kernel.debug...

Unread portion of the kernel message buffer:
WARNING !drm_modeset_is_locked(&crtc->mutex) failed at /wrkdirs/usr/ports/graphi
cs/drm-fbsd13-kmod/work/drm-kmod-drm_v5.4.191_1/drivers/gpu/drm/drm_atomic_helpe
r.c:621
#0 0x2679f329 at linux_dump_stack+0x19
  :
<4>WARN_ON(!mutex_is_locked(&dev->struct_mutex))

<4>WARN_ON(!mutex_is_locked(&fbc->lock))WARN_ON(!mutex_is_locked(&fbc->lock))
Physical memory: 3905 MB
Dumping 241 MB:..7%..14%..27%..34%..47%..53%..67%..73%..87%..93%

0x0102d895 in doadump (textdump=<optimized out>)
    at /usr/src/sys/kern/kern_shutdown.c:398
398             savectx(&dumppcb);

ここまでがgdbを起動した時の前振りなんだ。カーネル自身が記憶してる秘密の日記を閲覧してるんだな。だからそれを晒してしまおうと言うあこぎな事をやってしまうとな。後は普通のgdb操作になるのね。と言う事はkgdbの特別仕様を使ってる事になる。

root@fb:/tmp # /usr/local/bin/gdb -q /boot/kernel/kernel /var/crash/vmcore.2
Reading symbols from /boot/kernel/kernel...
Reading symbols from /usr/lib/debug//boot/kernel/kernel.debug...
"/var/crash/vmcore.2" is not a core dump: file format not recognized

どもうもcoreの前に余計なデータが付いている。これを理解出来無いから、フォーマット違いというエラーになるんだな。

(kgdb) bt 3
#0  0x0102d895 in doadump (textdump=<optimized out>)
    at /usr/src/sys/kern/kern_shutdown.c:398
#1  0x0102d6d6 in kern_reboot (howto=260)
    at /usr/src/sys/kern/kern_shutdown.c:487
#2  0x0102daab in vpanic (
    fmt=0x14c1a2a "vm_page_free_prep: page %p has references",
    ap=0x263a71ec "\224?%\003\320\003")
    at /usr/src/sys/kern/kern_shutdown.c:920
(More stack frames follow...)
(kgdb) info locals
error = 0
coredump = <optimized out>

savecore

swap-dirに保存されたcoreファイルを/var/crashにcpして、後の解析に役立てる。そのための収集プログラムだ。

DESCRIPTION
     The savecore utility copies a core dump into directory, or the current
     working directory if no directory argument is given, and enters a reboot
     message and information about the core dump into the system log.

debug how to

今日からあなたもFreeBSD(98)の検死官

kgdbによるカーネルのクラッシュダンプのデバッグ

カーネルパニックを最大限に利用する

ちょっと古い投稿だけど、言わんとしてる事は今でも健在。

ddbに入って、panicさせたいんだけど、どうやったら入れる?


This year's Index

Home