エラー時にexit(1)しちゃうようなプログラムにコアダンプさせる

かなり久々の記事。

昨日、一昨日とmixiが障害発生のために閲覧できなくなっていた。
DDoSアタックでもないらしいし、なにが原因かなーと思ってたら、
mixiのCTO @nealsato氏がtwitterで下記のツイートをしてた。

二日とも複数台のmemcachedが連続して落ちました。コアは吐かずにストンと落ちるので、原因追及に時間がかかりましたが、memcachedへの接続数が異常に多いと落ちる事は再現できました。
#mixi

http://twitter.com/nealsato/status/20903258605

コア吐かないってことはプログラム側でexitしてるんだろうなーと思いつつ、
またまたtwitterで@kazuho氏による下記ツイートを発見。

「正常終了してるなら exit を LD_PRELOAD して SEGV させてコアダンプ採取すればいいじゃない」ってじっちゃんが言ってた

http://twitter.com/kazuho/status/21021589792

おーなるほどと思い、実際やってみた。

まずコアダンプしてくれるか設定確認。

# 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) 4096
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                      (-x) unlimited

core file sizeが0だとコアダンプしてくれないので、
下記コマンドを実行。

# ulimit -c 1048576

これでコアファイルのサイズは1MBになり、
コアダンプしてくれるようになる。

んで、テストプログラム。

/////
// test.c

#include <stdio.h>
#include <stdlib.h>

int main() {
    puts("hello, world!");
    exit(0);
}


/////

コンパイルして実行。

# gcc -o test test.c
# ./test
hello, world!

次はexitのラッパー関数。

/////
// sigsegv.c

#include <signal.h>

void exit(int status) {
    raise(SIGSEGV);
}

/////

コンパイル

# gcc -fPIC -shared -o sigsegv.o sigsegv.c

んでexitを置き換えて実行。

# LD_PRELOAD=./sigsegv.o ./test
hello, world!
セグメンテーション違反です (core dumped)

成功!

これは色々使えそう。