コアダンプとクラッシュ解析の仕組み
再現しない本番クラッシュも落ちた瞬間のメモリ像から死因を特定できる。コアダンプ生成とELFコアの中身、kdumpのカーネル解析を原理で押さえる。
- 1.特定のシグナル(SIGSEGV/SIGABRT/SIGBUS等)はデフォルト動作がコア生成で、カーネルがプロセスのメモリ像をELFコアファイルに書き出す。出力先と書式はcore_patternで決まり、パイプ先のハンドラへ直接渡すこともできる。
- 2.ELFコアはタイプET_COREのELFで、各メモリ領域をPT_LOADセグメントに、レジスタや実行情報をPT_NOTEに格納する。gdbはこれと元バイナリのシンボルを突き合わせてスタックを復元する。
- 3.ユーザー空間が落ちてもカーネルは生きるが、カーネル自身がpanicするとkexecで予約メモリ上のクラッシュカーネルへ切り替え、旧カーネルの物理メモリをvmcoreとして退避してcrashツールで解析する。
コアダンプとは何の写しか
コアダンプは、プロセスが異常終了した瞬間の アドレス空間とCPU状態のスナップショット をファイルに固めたものです。デバッガでステップ実行できない本番障害でも、この一枚の死体写真があれば、どの命令で・どんなレジスタ値で・どんなコールスタックで死んだかを事後に剖検できます。再現しないクラッシュほどコアダンプの価値が高い、というのが実務の前提です。
「コア」という語は磁気コアメモリ時代の名残で、要するに メモリの内容そのもの を指します。何が写されるかは正確に決まっています。書き込み可能なデータ領域・ヒープ・スタック、つまり「失われると死因が分からなくなる可変状態」です。逆に、テキスト(コード)セグメントや読み込み専用のマップは原則ダンプに含めません。元の実行ファイルや共有ライブラリを読めば同じ内容が手に入るため、写す意味がないからです。
どのVMA(仮想メモリ領域)を書き出すかは /proc/<pid>/coredump_filter のビットマスクで領域種別ごとに選べます。無名プライベート、ファイルバックト、共有、Huge Page などを個別にオン・オフでき、巨大なファイルマップを除いてコアを小さく保つ、といった調整ができます。既定では無名ページ中心で、コードや読み取り専用マップは省かれます。
どのシグナルがコアを生むのか
コアダンプはシグナルのデフォルト動作の一種です。すべてのシグナルがコアを生むわけではなく、「プログラムの不変条件が壊れた」種類のシグナル だけがデフォルト動作 Core(終了+コア生成)を持ちます。
| シグナル | 典型的な原因 | デフォルト動作 |
|---|---|---|
| SIGSEGV | 不正なアドレスへのアクセス(ヌルポインタ等) | Core |
| SIGABRT | abort()・assert失敗・glibcのヒープ破壊検出 | Core |
| SIGBUS | アライメント違反・mmap範囲外アクセス | Core |
| SIGILL | 不正命令の実行 | Core |
| SIGFPE | ゼロ除算等の算術例外 | Core |
| SIGTERM / SIGINT | 通常の終了要求 | Term(コアなし) |
ここで重要なのは、これらが 同期シグナル、すなわちCPUの例外(フォルト/トラップ)から生まれる点です。不正アクセスをハードウェアが検知してページフォルト例外を起こし、カーネルが「これは正規のページではない」と判断して当該プロセスへ SIGSEGV を送る——この配送経路は シグナル配送の内部とシグナルセーフティ の通りです。コア生成は、ハンドラが登録されておらずデフォルト動作が選ばれたときに、配送処理の中でカーネルが直接行います。
本番でコアが落ちない原因はほぼこの三つです。(1)ulimit -c(RLIMIT_CORE)が0でサイズ上限により抑止されている。(2)プロセスがsetuid等で特権を持ち、情報漏洩防止のため既定でダンプ抑止される(fs.suid_dumpable と prctl(PR_SET_DUMPABLE) の管轄)。(3)当該シグナルにハンドラが登録され、デフォルト動作が上書きされている。コア解析の前に、まずこの三点を確認します。
ELFコアファイルの構造
生成されるコアファイルは、実行ファイルと同じ ELF形式 です。ただしタイプは ET_EXEC や ET_DYN ではなく ET_CORE で、セクションヘッダを持たず プログラムヘッダ(セグメント)だけ で構成されます。ELFの基本構造は ELFバイナリのロードとリンカ・ローダの内部 と共通で、コアはその「ロードの逆向き」——メモリ像をELFに固める操作だと捉えると理解が早いです。
コアに現れるセグメントは大きく二種類です。
ET_CORE の構成:
PT_NOTE ……… 1個。プロセスのメタ状態を可変長ノートの並びで保持
NT_PRSTATUS 各スレッドの汎用レジスタ・停止シグナル番号
NT_PRPSINFO プロセス名・PID・UID等の概要
NT_FPREGSET 浮動小数点レジスタ
NT_X86_XSTATE 拡張レジスタ状態(SSE/AVX等)
NT_FILE マップされたファイルとアドレス・オフセット対応表
PT_LOAD ……… 複数個。ダンプ対象の各メモリ領域の中身そのもの
p_vaddr …… その領域が本来あった仮想アドレス
p_offset …… コアファイル内でのその領域の位置
p_memsz …… 領域サイズ
p_flags …… R/W/X 権限
PT_NOTE が「状態」、PT_LOAD が「中身」だと押さえれば十分です。デバッガは死亡時のレジスタを NT_PRSTATUS から取り出してプログラムカウンタとスタックポインタを復元し、PT_LOAD 群を仮想アドレス空間として再構成して、その上でスタックフレームを巻き戻します。マルチスレッドプロセスでは PT_NOTE 内に スレッド数だけ NT_PRSTATUS が並ぶ ため、全スレッドのバックトレースを取れます。
コアにはコードもシンボル表も含まれません。関数名や行番号を得るには、コアを生んだ実行ファイルと共有ライブラリ(できればデバッグ情報付き) を gdb に同時に渡す必要があります。NT_FILE ノートがどのアドレスにどのファイルがマップされていたかを記録しているので、gdb はこれを手がかりに正しいバイナリを突き合わせます。本番バイナリをstripしている場合は、ビルドIDで対応する .debug ファイルを別途用意します。
core_pattern:出力先とパイプハンドラ
コアの出力先と命名は kernel.core_pattern(/proc/sys/kernel/core_pattern)で決まります。%p(PID)、%e(実行ファイル名)、%s(シグナル番号)、%t(時刻)などのテンプレートでファイル名を組み立てます。素朴な設定はこうです。
/var/cores/core.%e.%p.%t
より強力なのが、先頭を |(パイプ)で始める書式です。この場合カーネルはファイルに書く代わりに、指定したプログラムを起動し、コアの中身をそのプロセスの標準入力へ直接ストリーム します。
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h
パイプ書式の利点は二つあります。第一に、巨大なコアを一旦ディスクに全量書き出す前にハンドラ側で 圧縮・選別・遠隔転送 でき、I/Oとストレージを節約できます。第二に、ハンドラはカーネルが起こす独立プロセスなので、クラッシュしたプロセス自身のメモリ状態に依存せず 安全に解析を始められます。systemd-coredump や Ubuntu の apport がこの仕組みで動き、メタデータと圧縮済みコアを所定の場所へ格納します。コンテナでは、ホストとコンテナで core_pattern が共有される(名前空間化されない)点に注意が必要です。設定はホスト側カーネルのもの一つだけです。
クラッシュしたプロセス自身をデバッガで掴んで解析したい場合は、コアに頼らず生きたプロセスにアタッチする手もあります。その土台が ptraceとデバッガ・トレーサの仕組み で、gcore のように動作中プロセスへ ptrace でアタッチしてオンデマンドでコアを吐かせるツールもこの上に立っています。
ユーザー空間のクラッシュとカーネルパニックは別物
ここまではユーザー空間プロセスのコアダンプでした。プロセスが SIGSEGV で死んでも、カーネル自身は何も壊れていません。カーネルは例外を捕まえて該当プロセスにシグナルを送り、コアを書き、プロセスを回収するだけで、システム全体は動き続けます。
問題は カーネル自身が壊れたとき です。カーネル内のヌルポインタ参照やデータ構造の不整合(oops)が致命的だと判断されると panic() が呼ばれます。このときコアダンプの仕組みは使えません。死んだ当人であるカーネルが、自分のメモリを自分で安全に書き出すことは原理的にできないからです。信頼できる解析を行うには、壊れていない別のカーネル が必要になります。これを実現するのが kdump です。
kexec/kdump:カーネルクラッシュの解析
kdump の核心は kexec——通常のブートチェーン(ファームウェア・ブートローダ)を経由せず、動作中のカーネルから別のカーネルへ直接ジャンプ するブート機構です。BIOS/UEFIの再初期化を飛ばすため高速で、これが「壊れたカーネルから即座に解析カーネルへ切り替える」鍵になります。通常起動の文脈は ブートチェーンの内部(UEFI・ブートローダ・initramfs) を参照してください。
仕組みは「メモリの一部をあらかじめ囲っておく」点に尽きます。
平常時(システム起動直後):
crashkernel=512M で物理メモリの一区画を予約し、
本番カーネルからは見えない領域として隔離しておく。
その予約領域にクラッシュカーネル(解析用の第二カーネル)を
ロード済みの状態で待機させる。
panic 発生時:
1. 本番カーネルの panic 経路が kexec を呼ぶ
2. 予約領域のクラッシュカーネルへCPUを直接ジャンプ
→ 壊れた本番カーネルのコードは一切使わない
3. クラッシュカーネルは予約領域内だけで起動する
→ 本番カーネルが使っていた物理メモリは温存される
4. 旧カーネルの物理メモリ全体を /proc/vmcore として読み出し、
ファイル(vmcore)に退避してから通常再起動
肝は、クラッシュカーネルが 本番カーネルのメモリに一切手を付けず、隔離された予約領域だけで立ち上がることです。だからこそ、/proc/vmcore を通して 死亡時の本番カーネルの物理メモリ像をそのまま 取り出せます。vmcore も本質はELFコアの一種で、物理メモリの各区画が PT_LOAD として並びます。
kdump は予約領域があって初めて成立します。crashkernel= を指定し忘れる、あるいはメモリ不足で予約に失敗すると、panic時に切り替える先が無く、vmcoreは一切残りません。さらに予約サイズが小さすぎると、クラッシュカーネル自身がメモリ不足でvmcoreを退避できずに二次クラッシュする恐れがあります。本番投入前に 実際にpanicを起こして(echo c > /proc/sysrq-trigger)vmcoreが取れること を必ず検証します。設定したつもりで取れていない、が最も多い失敗です。
退避された vmcore は crash ユーティリティ で解析します。crash は vmcore と、対応するカーネルのデバッグ情報(vmlinux +シンボル)を突き合わせ、bt(バックトレース)、ps、log(カーネルログバッファ=dmesgの最後の様子)、スラブやページ構造の検査を提供します。ユーザー空間のコアを gdb で読むのと構図は同じで、「メモリ像」+「シンボル」=復元 という原理は両者で共通です。
(1)コアを生むのは Core 動作を持つ同期シグナル(SIGSEGV/SIGABRT/SIGBUS/SIGILL/SIGFPE)で、SIGTERM等は生まない。(2)コアは ET_CORE のELFで、PT_NOTE(レジスタ等の状態)+PT_LOAD(メモリの中身)で構成され、シンボルは含まない。(3)出力は core_pattern で制御し、| 先頭でハンドラへ直接ストリームできる。(4)ユーザー空間クラッシュはカーネル無傷、カーネルpanicはコア機構が使えない。(5)kdumpはkexecで予約領域の第二カーネルへ切り替え、旧カーネル物理メモリを /proc/vmcore として退避し crash で解析する。この5点が頻出です。
まとめ
- コアダンプ は異常終了時のアドレス空間とCPU状態のスナップショット。
Coreをデフォルト動作に持つ同期シグナルが、ハンドラ未登録時にカーネルへコア生成を行わせる。ulimit -c・suid_dumpable・ハンドラ登録の三点がコア有無を左右する。 - コアは
ET_COREのELFで、PT_NOTE(レジスタ・プロセス情報)とPT_LOAD(メモリ領域の実体) から成る。コード・シンボルは含まないため、解析には元バイナリとデバッグ情報を併せて gdb に渡す。 - 出力先と書式は
core_patternが決め、|先頭でコアをハンドラの標準入力へ直接ストリームして圧縮・転送・選別できる。 - ユーザー空間のクラッシュではカーネルは無傷だが、カーネルpanic ではコア機構が使えない。kexec/kdump が予約した
crashkernel領域の第二カーネルへ切り替え、旧カーネルの物理メモリを/proc/vmcoreとして退避し、crash ツールで解析する。
死因の入口である例外とシグナルは シグナル配送の内部とシグナルセーフティ、コアと対をなすバイナリ側の知識は ELFバイナリのロードとリンカ・ローダの内部 を合わせて読むと、生成から解析までが一本の線で繋がります。
OS Article
コアダンプとクラッシュ解析の仕組みを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
コアダンプ
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 6
導入後に効く点
ELFコアはタイプET_COREのELFで、各メモリ領域をPT_LOADセグメントに、レジスタや実行情報をPT_NOTEに格納する。gdbはこれと元バイナリのシンボルを突き合わせてスタックを復元する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 6
判断チェックリスト
- 自社の用途が「コアダンプ / クラッシュ解析」に近いか確認する。
- 強みである「特定のシグナル(SIGSEGV/SIGABRT/SIGBUS等)はデフォルト動作がコア生成で、カーネルがプロセスのメモリ像をELFコアファイルに書き出す。出力先と書式はcore_patternで決まり、パイプ先のハンドラへ直接渡すこともできる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。