core (2)

new machine

数度にわたってOpenBSDのKERNELのcoreをみたくて、いろいろやってるけど成功していない。 dumpエリアが不足って仮定をした。

で、最初から十分なdumpエリアを確保すべく、新規マシンを用意する事にした。とはいえWindows10のHDDも不足ぎみ。で、ばっさりVMWAREで動かしているOpenBSD(64Bit)を削除する事にした。

度重なるsysupgradeで、仮想HDDが肥大化してるんだ。だからこのOSを削除して、一時的に32BitのOpenBSDをいれる事にした。実験が終わる頃7.2が出るはずだから、そうしたら64Bit版を新規に入れればよい。

仮想シリアルの設定。 起動時に接続のチェックボックスをON、 名前パイプのラジオボタンで、名前は、 \\.\pipe\mypipename サーバーです 接続先はアプリですを指定。

ob$ df -h
Filesystem     Size    Used   Avail Capacity  Mounted on
/dev/wd0a      7.8G    3.1G    4.3G    41%    /
mfs:55960      495M    4.0K    470M     0%    /tmp
ob$ swapctl -kl
Device      1K-blocks     Used    Avail Capacity  Priority
/dev/wd0b     4188960        0  4188960     0%    0

雑に言うと12Gの仮想DISKを用意してルートに8G、スワップに4Gと言う構成。 メモリーは1G割当てで、その半分がRAMーDISKと言うスモール構成。それでもi386でMPなマシンだ。

Xは仮想では動かないので入れていない。大事なソースとポーツは一式tarで固めてvirtualBox側からもってきた。後は、 gdb,emacs-no_x11,w3m,bash を入れただけ。

ttys,rc.conf.local,boot.conf,sysctl.conf,fstab,installurlをcpしてから多少編集。 .bashrc,.emacs.d,.profile,.tmux.conf,.w3mもcpしてきた。これで既存の32Bitマシンとそっくりさんが出来上がった。違うのは、パーテションの切りかただけだ。

coredumpの実験

ob# inddb
Stopped at      db_enter+0x4:   popl    %ebp
ddb{0}> call panic
panic: uvm_fault(0xd3633960, 0x0, 0, 1) -> e
Stopped at      db_enter+0x4:   popl    %ebp
    TID    PID    UID     PRFLAGS     PFLAGS  CPU  COMMAND
 *499159  58620      0         0x3          0    0K sysctl
db_enter() at db_enter+0x4
 :
ddb{0}> boot crash

dumping to dev 1, offset 6283071
dump 1021 1020 1019 ... 6 5 4 3 2 1 0 succeeded

rebooting...
>> OpenBSD/i386 BOOT 3.44
boot>
booting hd0a:/bsd: 10431259+2479108+245780+0+1130496 [708146+107+587008+631014]=0xf786c4
entry point at 0x201000

[ using 1926852 bytes of bsd ELF symbol table ]
  :
starting RPC daemons:.
savecore: no core dump
checking quotas: done.
  :

1Gのメモリーを割当てたので、それだけ分をDUMPしたって事だな。ちゃんと成功してる。それにもかかわらず、savecoreで回収していない。

DUMP先は、仮想HDDの後ろの方なんで、VMWAREの分割されたHDDに変化があるかと思ったけど、痕跡すら見付られず。

話半分メモリーも半分

上記でcoreを作ってる(と思われる)案内の所が1021からカウントダウンして、最後は成功で終ってる。前回は成功してなかったので、少しは進歩したな。

で、1021って搭載メモリーを表しているはずなんだけど、本当か? 本来ならgdbで監査したい所なんだけど、それは不可能。なら、搭載メモリーを半分にしてみるか。仮想PCだと簡単にそういう事が出来るので嬉しい。

ddb{0}> boot crash

dumping to dev 1, offset 7331647
dump 509 508 507 506 ... 4 3 2 1 0 succeeded
  :
starting network
starting early daemons: syslogd.
starting RPC daemons:.
savecore: no core dump
checking quotas: done.

やっぱりメモリー依存性が確認出来た。だからスワップエリアは最低でもメモリー容量以上は欲しいという要求なんだな。

/etc/rc

# /var/crash should be a directory or a symbolic link to the crash directory
# if core dumps are to be saved.
if [[ -d /var/crash ]]; then
        savecore $savecore_flags /var/crash
fi

savecoreを起動してる部分。

ob# savecore -v /var/crash
dumpoff = 4022238720 (7855935 * 512)
savecore: /bsd: kvm_dump_mkheader: invalid magic in cpu_hdr
savecore: no core dump

これを見ると保存場所を指定して実行してる。更に詳しく -v も付けてみた。けど、肝心のdumpが無いから、そんなのやり様がないと言う結果だ。

dump on ddb

だったらddb上で直接 dumpsys(void) を呼出たらどうよ。

call dumpsys

なにかVMWAREだとハングするなあ。VMWAREを強制STOPしないとOSが終了しない。で、次回は起動出来無い。メモリーサイズ分のファイルが出来てる。強制的にタスクを殺す。メモリー相当分のファイルが残っていると、再起動出来無い。その憎らしいファイルをWindowsを再起動しないと削除出来無い。踏んだり蹴ったり。

qemu README-main

舞台を再びVirtualBOXに移す。問題はメモリーをどれだけ少く出来るかだ。133M以内だな。

qemuに面白い解説が出てた。

2. Create a virtual disk image:

    $ qemu-img create -f qcow2 virtual.img 10G

3. Install the OS:

    $ qemu-system-i386 -m 32 -monitor stdio -no-fd-bootchk \
        -hda virtual.img -cdrom cd70.iso -boot d

5. Boot normally from the virtual disk:

    $ qemu-system-i386 -m 32 -nographic -no-fd-bootchk -hda virtual.img

32MあればOpenBSDは動くのか。だったら、精一杯頑張って、96Mぐらいのメモリー割当てでいいかな。まあ、やってみるべ。

新しいOpenBSDが降ってきたら、qemuでも入れて、そこでやってみるかな。

coredump in small memory

で、96Mのメモリーを割り付けて起動。無事に動いたので、早速実験。

vbox# inddb
Stopped at      db_enter+0x4:   popl    %ebp
ddb{0}> call panic
panic: uvm_fault(0xd17581e0, 0x0, 0, 1) -> e
Stopped at      db_enter+0x4:   popl    %ebp
    TID    PID    UID     PRFLAGS     PFLAGS  CPU  COMMAND
 * 52383  74121      0         0x3          0    0K sysctl
  :
ddb{0}> boot crash

dumping to dev 1, offset 76560
dump 94 93 92 91 90 ... 3 2 1 0 succeeded
 :
avecore: reboot after panic: uvm_fault(0xd17581e0, 0x0, 0, 1) -> e
savecore: system went down at Wed Sep 28 16:16:23 2022
savecore: writing core to /var/crash/bsd.6.core
savecore: writing kernel to /var/crash/bsd.6
 :

今度はdumpのステータスが成功ってなってる。まあ、成功か失敗かなんてVGAのコンソールじゃ結果が流れてしまって見られない。オイラーみたいにシリアルの設定をしてTeraTermで接続。それのバックログを見るなんて事をしないと無理だろな。こういう工夫が楽しいんだ。

box# ls -l /var/crash
total 528968
-rw-------  1 root  wheel          2 Sep 28 16:17 bounds
-rw-------  1 root  wheel   15093330 Sep 20 16:04 bsd.0
-rw-------  1 root  wheel  139792916 Sep 20 16:04 bsd.0.core
-rw-------  1 root  wheel   15093330 Sep 28 16:17 bsd.6
-rw-------  1 root  wheel  100598292 Sep 28 16:17 bsd.6.core
-rw-r--r--  1 root  wheel          5 May  8  2020 minfree

そして、死体安置所での保存状況。今度はオーバーフローしないで、ちゃんと収まっているな。

そして注目の検死確認。

vbox# fuser -N bsd.6 -M bsd.6.core -f /home/sakae
/home/sakae: 57788c

文句も言われずにちゃんと確認出来た。いやー長い道程だったなあ。

vbox# ps -N bsd.6 -M bsd.6.core -O paddr
  PID    PADDR TT  STAT        TIME COMMAND
50259 d16c8b18 00  Ip       0:00.06 (ksh)
74121 d16c800c 00  RK+U     0:00.00 (sysctl)
99968 d16c865c C0  S+pU     0:00.02 (getty)

検死の指南書 by crash(8)

The -O paddr option prints each process' struct proc address.  This is
 very useful information if you are analyzing process contexts in gdb(1).

メモリーが不足したような場合、何が大判振舞してるか、こんなコマンドで確認しろとな。

vbox# vmstat -N bsd.6 -M bsd.6.core -m
Memory statistics by bucket size
    Size   In Use   Free           Requests  HighWater  Couldfree
      16      456     56               1151    1280          0
      32     1565     99               3027     640          0
      64     2474    534              13863     320        189
      :
Memory usage type by bucket size
    Size  Type(s)
      16  devbuf, pcb, rtable, ifaddr, dirhash, ACPI, proc, in_multi, exec,
          VM swap, UVM amap, UVM aobj, USB, USB device, temp
      32  devbuf, rtable, ifaddr, vnodes, UFS mount, sem, dirhash, ACPI, proc,
          ether_multi, VM swap, UVM amap, USB, USB device, NDP, temp
       :

汎用のgdbを取出してみる。

vbox# gdb
(gdb) file /var/crash/bsd.6
Reading symbols from /var/crash/bsd.6...(no debugging symbols found)...done.
(gdb) target kvm /var/crash/bsd.6.core
#0  0xd03d521e in boot ()
(gdb) bt
#0  0xd03d521e in boot ()
#1  0xd028d899 in reboot ()
#2  0xd036dc86 in db_reboot ()
#3  0xd036d86d in db_boot_crash_cmd ()
#4  0xd036cf23 in db_command ()
#5  0xd036dbbe in db_command_loop ()
#6  0xd091fd1f in db_trap ()
#7  0xd082494f in db_ktrap ()
#8  0xd0489975 in trap ()
#9  0xd03b5fb5 in calltrap ()
#10 0xd0824e84 in db_enter ()

残念ながら、debug情報が落ちているので、詳細を追求はオイラーの腕では無理だ。だって嫌いな石とまともに対面する必要が有りますからね。

カーネルへの live patch

coredumpに関連するソースを見ていると、dumpdevってのが頻出してる。何処で元ネタが定義されるかな? conf/swapgeneric.c

dev_t   rootdev = NODEV;
dev_t   dumpdev = NODEV;

struct  swdevt swdevt[] = {
        { NODEV, 0 },   /* to be filled in */
        { NODEV, 0 }
};

dev_t て最もらしい型名が付いているけど単なる32Bitの整数。で、いきなりこの名前が出て來るって事は、グローバル変数だな。ならば、どのアドレスが所在地か?

vbox# nm bsd.6 | grep dumpdev
d0e8d750 D dumpdev

本当は生きているカーネルから探るのが筋だけど、それをやると怒られてしまうので、おあつらえむきなコピーから採取。

で、どんな風にデバイスを表現してる?

box# ls -l /dev/wd0*
brw-r-----  1 root  operator    0,   0 Apr 25 14:05 /dev/wd0a
brw-r-----  1 root  operator    0,   1 Sep 28 16:17 /dev/wd0b
  :
vbox# ls -l /dev/wd2*
brw-r-----  1 root  operator    0,  32 Apr 25 14:05 /dev/wd2a
brw-r-----  1 root  operator    0,  33 Sep 30 07:35 /dev/wd2b
 :
brw-r-----  1 root  operator    0,  46 Apr 25 14:05 /dev/wd2o
brw-r-----  1 root  operator    0,  47 Apr 25 14:05 /dev/wd2p

デバイスはメジャーとマイナーの組み合わせで表現されてた。で、マイナー番号がパーテションに対応されてるのね。

dump

ddbに落ちて、デバイスを wd0b から wd2b に変更してみる。

db{0}> x dumpdev
dumpdev:                   1
ddb{0}> w dumpdev 0x21
dumpdev                1        =             21
ddb{0}> pp dumpdev
dumpdev:        0x21

こうしておいて、dumpさせる。

ddb{0}> boot crash

dumping to dev 21, offset 8
dump 132 131 130 ... 3 2 1 0 4095 4094 ... 3847 3846 succeeded

rebooting...
 :
savecore: no core dump
checking quotas: done.

どうやら、wd0bはデフォで使われて、足りない分はwd2bにスイッチされてると思われる。 再起動後はデバイス番号が元に戻ってしまうので、savecoreは発動していないっぽい。

強制 savecore

vbox# inddb
Stopped at      db_enter+0x4:   popl    %ebp
ddb{0}> pp dumpdev
dumpdev:        0x1
ddb{0}> w dumpdev 0x21
dumpdev                1        =             21
ddb{0}> c
ddb.trigger: 0 -> 1
vbox# savecore -v /var/crash
dumpoff = 4096 (8 * 512)
savecore: reboot
savecore: system went down at Fri Sep 30 07:21:23 2022
savecore: writing core to /var/crash/bsd.7.core
savecore: writing kernel to /var/crash/bsd.7

ならば再びddbに落ちてデバイスを再設定。それから手動でsavecoreを実施。

一見成功したように見えるかどwd0b相当しかsavecoreしてるぞ。オーバーフロー分(memoryは384Mを割当てている)は無視されてるな。

vbox# ls -l /var/crash
total 831752
-rw-------  1 root  wheel          2 Sep 30 07:35 bounds
-rw-------  1 root  wheel   15093330 Sep 20 16:04 bsd.0
-rw-------  1 root  wheel  139792916 Sep 20 16:04 bsd.0.core
-rw-------  1 root  wheel   15093330 Sep 28 16:17 bsd.6
-rw-------  1 root  wheel  100598292 Sep 28 16:17 bsd.6.core
-rw-------  1 root  wheel   15093330 Sep 30 07:35 bsd.7
-rw-------  1 root  wheel  139792916 Sep 30 07:35 bsd.7.core
-rw-r--r--  1 root  wheel          5 May  8  2020 minfree
vbox# cd /var/crash/
vbox# pstat -N bsd.7 -M bsd.7.core -T
 71/7030 files
   1524 vnodes
0M/5253M swap space
vbox# fstat -N bsd.7 -M bsd.7.core
fstat: can't read process at f53e2c38

試しに、検死してみると、動いたり動かなかったりだ。 ちゃんとしたcoredumpを取得したかったら、最初からそういう環境を作っておけって事ですな。

dumpon

FreeBSDには、ちゃんとそれ用のコマンドが用意されてる。

root@fbox:~ # dumpon -l
ada0s1b

マニュアルをつらつら読むと、ネットワークを経由したdumpをするのが主目的と思われる。 人材が豊富だと、こういう所にも手を出せるのか。羨しいな。

Starting in FreeBSD 13.0, dumpon can configure a series of fallback dump
devices.  For example, an administrator may prefer netdump(4) by default,
but if the netdump(4) service cannot be reached or some other failure
occurs, they might choose a local disk dump as a second choice option.

This year's Index

Home