バグの調べ方
Cybozu Tech Conrefence 2016
12/13
サイボウズ・ラボ 光成滋生
• 自己紹介
• 自分が開発してるもの
• サイボウズ・ラボユース紹介
• 不具合調査
• サイボウズOfficeでの不具合
• Garoonでの不具合
• WalBでの不具合
• まとめ
概要
2 / 29
自己紹介
• サイボウズの研究開発部門
• 次世代の製品・サービスの基盤となる技術を
中長期視点で研究開発する
• 私
• 主にセキュリティ関係に関わる
• https://github.com/herumi/
• サブでcybozu.comのバックアップレプリケーションシステム
• + たまにやってくる不具合調査
サイボウズ・ラボ
4 / 29
• https://github.com/herumi/mcl
• 汎用楕円曲線・ペアリング暗号ライブラリ
• https://github.com/herumi/ate-pairing
• 世界最速実装の一つ(IEEE Trans. on Computers, 2015)
• 何に使うの?
• 『クラウドを支えるこれからの暗号技術』
をご覧ください
• 例:完全匿名可能な分散型暗号通貨
Zcashのゼロ知識証明プロトコル
ペアリング暗号の実装
5 / 29
• https://github.com/herumi/xbyak
• 暗号ライブラリなどのために開発
• 来年やその後に出る予定のCPUのAVX-512に対応
• gcc-6, clang-4, NASM, VC2015, Intel SDEなどのツールは
AVX512_4FMAPS, AVX512_4VNNIWに未対応(2016/11)
• Intelの機械学習ライブラリCaffeやMKL-DNNで利用される
C++用JITアセンブラXbyak
実装したけど正しく動いてるかツールが無いので分からん
未公開のIntelソフトウェアXEDで確認したよ
6 / 29
• http://labs.cybozu.co.jp/youth/requirements.html
• 学生が作りたいものをサポートする
• サイボウズ・ラボ社員がメンター
• 比較的長期(最大1年)
• 開発物はオープンソース
• 給料がもらえる
• 私がメンター担当したもの(一部)
• 正規表現JITエンジン開発
• JavaScriptエンジン開発
• ペアリング暗号ライブラリ開発
• Intel DPDKを用いたネットワークライブラリ開発
サイボウズ・ラボユース
7 / 29
サイボウズOfficeでの不具合調査
• サイボウズOffice:中小企業向けグループウェア
• ファイルキャッシュの不具合(2012年の話)
• 1000秒おきにファイルキャッシュがクリア
突然サイボウズOfficeが遅くなる
Office
ext3
mdadm
LVM
kernel/driver
disk
ファイルシステム
ソフトウェアRAID
パーティション管理
メモリにキャッシュ
誰かにクリア されて遅くなる
9 / 29
• 手元の環境で再現させる
• 結構つらい
• 仕掛ける
• 普通ユーザランドでキャッシュを明示的に破棄しない
• 犯人はだれだ
• ページキャッシュに関係するkernelの関数をwatch
• flush_disk, invalidate_disk, invalidate_partition, ...
• ftrace
• 低負荷で手軽に使えるkernelトレーサ
再現させる&仕掛ける
10 / 29
• invalidate_partitionが呼ばれた
• 呼び出したのはmdadm
• 犯人はこいつか
• gdbでattach
ヒット
mdadm-900 [001] .... 75491.384221: __invalidate_device <-invalidate_partition => ftrace_call => invalidate_partition => rescan_partitions => __blkdev_get => blkdev_get ... => do_sys_open => sys_open
gdb -p 9000 gdb) bt #0 0x00007f85b1ebf103 in __select_nocancel () at ../sysdeps/unix/syscall-template.S:82 #1 0x000000000040de66 in mdstat_wait (seconds=1000) at mdstat.c:317 #2 0x000000000042dea1 in Monitor (devlist=0x0, mailaddr=0x1a7b020 "root",
waitしてる時間が1000秒
11 / 29
• mdのモニタツールがinvalid_partitionを呼んでいる
• 呼ぶとはとても思えないが...
• /dev/mdをオープンするだけでクリアされると判明
• mdのドライバが犯人か?
位置関係(1/3)
mdデーモン
md driver
disk driver
disk
invalid_partition()
キャッシュクリア
モニタツール
?
fd = open("dev/md/test..", O_RDONLY);
kernel
user
12 / 29
• md_open()がinvalid_partitionを呼ぶとは思えない...
• 調べたがmd_openは悪くはなかった
• スタックトレースの途中にあったblkdev_get()を見てみる
位置関係(2/3)
mdデーモン
md driver
disk driver
disk
invalid_partition()
キャッシュクリア
モニタツールopen()
md_open()
?
kernel
user
13 / 29
• bd_invalidatedが怪しい
blkdev_get
__blkdev_get(...) { ... ret = disk->fops->open(bdev, mode); ... if (bdev->bd_invalidated) { if (!ret) rescan_partitions(disk, bdev); else if (ret == -ENOMEDIUM) invalidate_partitions(disk, bdev);
md_openが呼ばれる
__invalidate_partitionが呼ばれる
14 / 29
• mdは悪くなかった
• kernel/fsのバグ
• check_disk_size_change()内でbd_invalidatedをクリアし忘れ
• kernel patchを送る
位置関係(3/3)
mdデーモン
md driver
disk driver
disk
bd_invalidatedがtrueなら invalid_partition()
モニタツールopen()
blk_dev() kernel
user
md_open() // 引っかけ
キャッシュクリア
15 / 29
Garoonでの不具合調査
• Garoon:中堅・大規模組織向けグループウェア
• リリース予定の少し前に不具合が発覚(2015年の話)
• PHPのheapが壊れて死んだ
• 本社チームがさまざまな設定で検証
• OPcacheを無効にすると発生しない
• OPcache : PHPのコンパイル結果をキャッシュして高速化
• パフォーマンス劣化は許容範囲外
• opcache.max_accelerated_filesを増やすと発生しない
• Garoonのファイルは約7000個
• ファイルキャッシュを越えると不具合か?
突然Garoonが白くなる
[.10:59:22] WARNING: [pool www] child 19055 said into stderr: "zend_mm_heap corrupted" [.10:59:22] WARNING: [pool www] child 19055 exited with code 1 after 946.944662 seconds from start
17 / 29
• Address sanitizerで調査
• エラーで落ちるがそのメモリがどう壊れてるのか、
どこで確保されたのかはわからない
• 自前ツールで調査
• 全てのmalloc/freeを記録して整合性をチェック
• 共有ライブラリを通してかなり複雑なやりとりをしている
• ひとまず解決
• このバージョンのPHPではopcache.fast_shutdown=0だと
エラーになるパスを通らないことを確認
• パフォーマンス劣化も許容範囲内
• 同時に軽微なメモリ破壊の修正patch
メモリチェックツール
18 / 29
WalBでの不具合調査その1
• WalB
• cybozu.comの次期バックアップレプリケーションシステム
• ブロックデバイスの全IOを記録する
• デーモン・制御ツールはC++/Python
• ラボ:星野(メイン) + 私(サブ)
• 現在、星野とSRE(インフラ)チームで本運用に向け最終開発
WalBでおかしな実行バイナリ
WalB
storage
アプリケーション
Diff
backup
storage
replicated
storage
20 / 29
• gdbでデバッグしようとしたらgdbが落ちる
• 起動も何もしていない
• gdbを使わなければプログラム自体は普通に動く
• 1行だけデータをコメントアウトしたらgdbでも動いた
ある日のデバッグ
gdb --args binsrc/archive-server -vg vg1 -p 10201 -b /home/shigeo/Program/walb-tools/stest/tmp/a1 -l /home/shigeo/Program/walb-tools/stest/tmp/a1.log -id a1 -debug GNU gdb (Ubuntu 7.7-0ubuntu3.1) 7.7 ...(snip) Type "apropos word" to search for commands related to "word"... Reading symbols from binsrc/archive-server...Segmentation fault (core dumped)
const protocol::Str2ServerHandler archiveHandlerMap = { ... // { dirtyFullSyncPN, s2aDirtyFullSyncServer }, /* 動く・動かないの分岐点 */ };
21 / 29
• 3万行のコードからひたすらコードを削る
• 少し変更するだけで動いたり、動かなかったりで難しい
• 最終的に30行ほどで落ちるコード
• 結局gdbのシンボルパーサにバグ
• template+継承+ラムダ式のコンボによる複雑なシンボル
• 当時のcurrent版で修正済みだった
おもしろい
22 / 29
WalBでの不具合調査その2
• 試験運用時書き込みデータ(logpack)に不正を検出
• しばらくretryしてシステムは継続
• 一度しか発生せず致命的ではなかったため様子見
• しかし20日後再現
• 対応作業
• コードレビュー
• チェックコード
• 3週間に一度しか起こらないのでよくわからない
WalBで不正なlogを検出
24 / 29
• エラーの再現頻度を上げるチェッカーツールの作成
• 最短で10分に一度エラーが発生!
• 条件によって数時間走らせても出ないことも
• WalB driverのflushまわりの制御バグを発見
• ツールを1日走らせてもエラーが出なくなった
• 問題解決か?
• 残念
• ツールを使っても3日に一度エラーが発生
原因究明作業(主に星野担当)
25 / 29
• diskの読み書きは非同期
• データは分割・統合されうる
• 実際にはflush処理もある
原因追求が難しい理由
write()
user空間 WalB driver/kernel空間
logの書き込みsubmit
disk
書き込み
complete 完了
dataのコピー・統合・分割
dataの書き込みsubmit 書き込み
complete
26 / 29
• いろいろ調べても悪そうには見えない
• 環境によって出るのと出ないのと
• 同じマシンでも出やすかったりそうでなかったり
• 何が違うのかなかなか思い当たらない
• つらい期間
• WalBのせいじゃないのではないかという仮定の元に
複数書き込みを模倣するユーザランドツールを作成
• 同じエラーが発生
• WalB driverのせいではなかった!
• では犯人はdisk driver or kernel?
WalB driverのせいなのか?
27 / 29
• disk IOを詳細に取得できるツール
• completedでdiskの書き込みは完了しているはずなのに
3296001番目のセクタの中身はは0のままだった
• RAIDカードのドライバかファームウェアの不具合
blktrace
252,7 1 12079 228.808092957 4494 Q W 3296001 + 1 [kworker/u66:21] 8,2 1 162836 228.808094689 4494 A W 622367489 + 1 <- (252,7) 3296001 8,0 1 162837 228.808095153 4494 A W 622867201 + 1 <- (8,2) 622367489 8,0 1 162838 228.808095648 4494 Q W 622867201 + 1 [kworker/u66:21] 8,0 1 162839 228.808099628 4494 G W 622867201 + 1 [kworker/u66:21] 252,7 1 12080 228.808101183 4494 Q W 3296002 + 256 [kworker/u66:21] 8,2 1 162840 228.808101724 4494 A W 622367490 + 256 <- (252,7) 3296002 ... (snip) ... 8,0 1 162854 228.808198793 4494 Q W 622867459 + 256 [kworker/u66:21] 8,0 1 162855 228.808199268 4494 G W 622867459 + 256 [kworker/u66:21] 8,0 7 187897 228.808255052 4367 C W 622867202 + 256 [0] 252,7 7 19389 228.808256993 4367 C W 3296002 + 256 [0] 8,0 7 187898 228.808260877 4367 C W 622867201 + 1 [0] 252,7 7 19390 228.808261198 4367 C W 3296001 + 1 [0]
heder submitted
logpack submitted
logpack completed
header completed
28 / 29
• まずは自分たちのコードを疑う
• ほとんどは自分のミス
• 論理的に、ときにはひらめきで追いかける
• ありそうにないものが犯人のときもある
• つらいときでも耐える
• 気分転換でもして
バグの調べ方のまとめ
29 / 29