インフラエンジニアがSegmentation fault をなんとか治してみる

普段Webサーバを運用していて、めんどくさいトラブルのひとつに「Segmentation fault」があります。
あれー?なんか500エラーがでるなーなんて思ってログを見るとSegmentation faultになってるときは死にたくなります。


そもそもSegmentation faultはメモリ上にあるデータに対して不正が行われたときに起こるもので、
インフラエンジニアにとってはなかなか手がだせないところでもあります。


それでもなんとかして治さないといけないわけなので
せめてどのプログラムが悪さしてるかどうかぐらいは調べ上げてみます。

apacheでのログ

apache + mod_perl での環境です。
こんな感じでエラーがでます。

#tail error_log 
[notice] child pid 26028 exit signal Segmentation fault (11)
[notice] child pid 26029 exit signal Segmentation fault (11)
[notice] child pid 26030 exit signal Segmentation fault (11)
[notice] child pid 26031 exit signal Segmentation fault (11)

完全に死んでます。ほんとうにありがとうございました。

coreをはかせる

Segmentation faultがでてもこれ以上の情報がないのでcoreファイルを吐かせてデバッグしていきます。

ulimit設定

ulimit -aを打ってでる結果で、core file sizeの値が0の場合、 coreファイルの容量が制限されてます。

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 16249
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) unlimited
virtual memory          (kbytes, -v) unlimited
file locks

なので、ulimitコマンドでこの値を変えてやる必要があるわけですが、ulimitはプロセス毎(pid毎)の値なのでログオフすると元に戻ってしまいます。
値を変えるには/etc/profileを書き換えると簡単なのでいつもこっちで変えてしまいます。

$ diff /etc/profile.b /etc/profile
30c30
< # ulimit -S -c 0 > /dev/null 2>&1
---
> ulimit -c unlimited > /dev/null 2>&1
$

「-c unlimited」で無制限に
変更したら一度ログアウト→ログインします。

$ ulimit -a
core file size          (blocks, -c) unlimited
(略)
apacheの設定

apacheは一行加えて終了

CoreDumpDirectory /tmp

Coreファイルを吐く場所は適宜。再起動を忘れずに

Coreファイルを解析してみる

わざとエラーを出し、Segmentation faultがでていれば、/tmp配下にcoreファイルができているはずです。
Coreファイルの解析はgdbコマンドを使用します。


gdbコマンドは

gdb <commnd> -c <(core)file>

で使用します。
まぁ実際に打ってみましょう。


今回はapacheでエラーがでたので上のにはapacheのコマンドがはいります。

#gdb /usr/local/apache2/bin/httpd -c /tmp/core.30864

こんな感じ。
実際打つと

#gdb /usr/local/apache2/bin/httpd -c /tmp/core.30864
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-23.el5_5.2)
Copyright (C) 2009 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 "i386-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/local/apache2/bin/httpd ...(no debugging symbols found)...done.
Reading symbols from /lib/libm.so.6...(no debugging symbols found)...done.
.........
......
...
<略>

こんな感じでトレースしていって。。

<略>
...
......
.........
Reading symbols from /lib/libselinux.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib/libselinux.so.1
Reading symbols from /lib/libsepol.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib/libsepol.so.1
Core was generated by `/usr/local/apache2/bin/httpd '.
Program terminated with signal 11, Segmentation fault.
#0  0x0061faa4 in SSL_CTX_ctrl () from /lib/libssl.so.6
(gdb)

こんな感じでSegmentation faultのエラーが発生して、
(gdb)というプロンプトが返ります。


gdbコマンドは高度なデバッガでプログラムができる人はいろいろデバッグできるのですが
とりあえず
(gdb)where
と打ってみましょう。落ちてるところがわかります。


こんな感じ。

(gdb) where
#0  0x0061faa4 in SSL_CTX_ctrl () from /lib/libssl.so.6
#1  0xb721f6f7 in XS_Crypt__SSLeay__CTX_new (cv=0xb92a828) at SSLeay.xs:133
#2  0x0810ed51 in Perl_pp_entersub ()
#3  0x08108ede in Perl_runops_standard ()
#4  0x080bdfcb in Perl_call_sv ()
#5  0x08069414 in perl_call_handler ()
#6  0x08069b29 in perl_run_stacked_handlers ()
#7  0x0806b071 in perl_handler ()
#8  0x0808b7cb in ap_invoke_handler ()
#9  0x080a0cee in ?? ()
#10 0x0ae30fc4 in ?? ()
#11 0x08179e99 in ?? ()
#12 0xbff5f558 in ?? ()
#13 0x080add23 in ap_ctx_new ()
#14 0x080a0e4b in ap_process_request ()
#15 0x080963ce in ?? ()
#16 0x0ae30fc4 in ?? ()
#17 0x00000004 in ?? ()
#18 0x0ae30fc4 in ?? ()
#19 0xbff5f864 in ?? ()
#20 0x00000004 in ?? ()
#21 0x4cb7f3d2 in ?? ()
#22 0x00000000 in ?? ()
(gdb) 

でまぁここからちょっとわかりくいんですが、#0と#1みると

#0  0x0061faa4 in SSL_CTX_ctrl () from /lib/libssl.so.6
#1  0xb721f6f7 in XS_Crypt__SSLeay__CTX_new (cv=0xb92a828) at SSLeay.xs:133

となっていて、なんかSSLeay.xsで落ちてるっぽいってわかります。
xsってのはPerlがCを使うためのモジュールなんで、なんかこいつが犯人っぽい。

てな具合でなんとか悪さしてるプログラムは見つけることができました。

あとは

ほんとはここまできたら、スーパーデバッグタイムなんだけど、
正直プログラマーじゃないと厳しいし、まずは犯人プログラムをアップデートしたり、
リコンパイルしてみるといいと思う。


ちなみに↑で紹介したエラーはcpanから最新のCrypt::SSLeayを持ってきて入れなおしたらあっさり直りました。


例外として、すごい調べたけどまったく原因がわからなくて
とりあえずサーバを再起動したらエラーが解消したってこともありました。
まぁそういう日もあるよね。