自動 core dump 分析とは、クラッシュしたプロセスの core ファイルをツールに渡し、クラッシュした瞬間にプロセスが何をしていたのかをツールに再構築させることです——GDB で手作業で掘り下げる代わりに。OpenResty XRay はクラッシュした OpenResty/Nginx アプリケーションに対してこれを行います。core ファイルを指定するだけで、C と Lua のバックトレース、クラッシュした正確なソース行、Lua コルーチン分析、Lua GC オブジェクト参照グラフ、libc アロケータ分析、そしてクラッシュ時に処理中だった並行 HTTP リクエストまで、完全なレポートを返してくれます——手動の GDB は不要です。詳しい手順の解説は、本記事冒頭の動画をご覧ください。

OpenResty/Nginx の core dump ファイルの場所を特定する

OpenResty または Nginx アプリケーションの worker プロセスがクラッシュすると、Linux カーネルが core dump ファイルを書き出します。今回のクラッシュしたサーバーでは、worker プロセスがユーザーの home ディレクトリに core.175276 ファイルを残しました——ls で見つかり、readlink -f で絶対パスが得られます。そのパスをコピーしてください。OpenResty XRay はファイルをその場で分析するため、移動したりライブラリを手作業で合わせたりする必要はありません。

クラッシュした OpenResty サーバーの home ディレクトリに一覧表示された core dump ファイル core.175276

OpenResty XRay でガイド付き core dump 分析を開始する

ブラウザで OpenResty XRay の Web コンソールを開き、対象マシン(ここでは biz-app-server)を選択して、Guided Analysis(ガイド付き分析)ページを開きます。問題の種類として Core dumps or process crashes(core dump またはプロセスのクラッシュ)を選択します。

OpenResty XRay のガイド付き分析の問題リストで Core dumps or process crashes が選択されている様子

core ファイルのパスを貼り付けます。XRay は core dump を読み込み、実行可能ファイルのパス(/usr/local/openresty/nginx/sbin/nginx)とアプリケーションの種類(OpenResty)を自動的に抽出し、分析を開始します。バイナリが strip されデバッグシンボルが欠落している場合でも、XRay はまず欠落したデバッグシンボルを自動的に再構築するため(nginx-dbg パッケージのインストールも再コンパイルも不要)、以下のクラッシュレポートは実際の関数名とソース行まで解決されます。

core ファイルのパスと自動検出された nginx 実行可能ファイルのメタデータを示すガイド付き分析のステップ

クラッシュレポートを読む:シグナル、レジスタ、C バックトレース

レポートはまず実行コンテキストを表示します。この worker プロセスは SEGV シグナルによって中止されました——SEGV は「Segmentation Violation(セグメンテーション違反)」の略で、通常は不正なメモリアクセスを示します。

core dump を引き起こした SEGV シグナルを特定した OpenResty XRay のレポート

機械語レベルでは、XRay が問題を引き起こした命令——mov ecx, DWORD PTR [rsi+rdx*1-0x4]——を赤い矢印で示します。

セグメンテーション違反を引き起こした問題の命令周辺の逆アセンブリコード

XRay はクラッシュした瞬間のすべての CPU レジスタも取得します。rsirdx はどちらも 0x4 を保持しているため、命令のソースアドレス [rsi+rdx*1-0x4]0x4 になります——ほぼヌルに等しいアドレスであり、これはヌルポインタ参照の典型的な特徴です。

core dump の CPU レジスタ値。rsi と rdx がどちらも 0x4 を保持しているため、問題の読み取りはアドレス 0x4 に着地する

C バックトレースは、実行がどのようにしてその命令に到達したのかを説明します。Nginx の ngx_http_core_content_phasengx_http_lua_run_thread を介して Lua コンテンツハンドラを実行し、続いて LuaJITcdata 型に対する FFI__index メタメソッドをディスパッチし、それが LuaJIT の C 型変換を経て glibc の memcpy に入り——そこでセグメンテーション違反が発生しました。

core dump の C バックトレース。glibc memcpy から LuaJIT FFI メタメソッド、Nginx Lua モジュールまで

Lua バックトレースからクラッシュした正確な行へ

XRay は core dump から直接 Lua レベルのバックトレースを再構築し、それを Lua CPU フレームグラフから推測します。クラッシュの実行経路は content_by_luagoapi.lua)→ handlerouter/order.lua)→ process_orderorder/core.lua:78)→ decode_order_dataorder/processor.lua:79)です。

クラッシュした decode_order_data のコードパスが赤色でハイライトされた Lua レベルの CPU フレームグラフ

さらに、各関数フレームの引数とローカル変数を含む完全な Lua コールスタックも提供されるため、何も再現することなくクラッシュ時の正確な状態を確認できます。

core dump から取得したローカル変数と引数を含む完全な Lua コールスタック

processor.lua の 79 行目を開くと、問題の文 order.uid = order_cdata.user_id が現れます。order_cdata の値自体は有効なポインタ型 cdata オブジェクトですが、それが保持する C ポインタは NULL です。order_cdata.user_id を読み取ると、その NULL ベースからオフセット 4 を参照することになり、これがまさに問題のアドレスが 0x4 である理由です。修正方法は、フィールドにアクセスする前に、order_cdata == nil によって order_cdata 内部のポインタが NULL かどうかを確認することです。

processor.lua の 79 行目にあるクラッシュした Lua ソース行 order.uid = order_cdata.user_id

今回のクラッシュはヌルポインタ参照でした。別の種類の障害——記録と再生(record-and-replay)で突き止めた use-after-free(解放後使用)のクラッシュ——については、クラッシュから根本原因まで:OpenResty XRay が Nginx メモリ破壊問題を明確に分析する方法をご覧ください。

クラッシュ時の Lua コルーチン、並行 HTTP リクエストと Lua GC メモリ

XRay が分析するのは、クラッシュしたコルーチンだけではありません。core dump 内で生存しているすべての Lua コルーチンを分析し、その中で最も一般的な Lua バックトレースを提示します——この例では、sleep 呼び出しで停止しているコードパスでした。同じ process_order のビジネスコードが、ngx.sleepngx_http_lua_ngx_sleep)の中で待機しています。

content_by_lua から process_order を経て ngx.sleep に至る、生存 Lua コルーチンの中で最も一般的な Lua バックトレース

レポート内でハイライトされた、最も一般的なコルーチンバックトレース先頭の sleep フレーム

レポートは処理中のすべての HTTP リクエストも取得します。クラッシュした worker が処理していたリクエストは POST https://e.woniu.com:1443/order/api(クライアント 127.0.0.1curl/7.76.1)で、3 つの並行する GET /order/api?request_type=search_orders リクエストと同時に実行されていました。

クラッシュ中に処理されていた POST /order/api リクエストの詳細情報

メモリについては、XRay が最もホットな Lua GC オブジェクト参照パスをランク付けします。ここでの第 1 位のパスは registry_LOADEDengines.sre.sre_librun_rules で、40.12 MB の生存 Lua GC オブジェクトを占めています——これは Lua GC オブジェクトメモリ分布フレームグラフから自動的に導出されたものです。

core dump のメモリ分析における第 1 位の最もホットな Lua GC オブジェクト参照パス

自動 core dump 分析とレポート

ガイド付き分析を手動で実行する必要はありません。OpenResty XRay はオンラインアプリケーションで新しい core dump を監視し、自動的に分析して、その結果を Insights ページに日次および週次のレポートとして公開します。

自動分析された問題を一覧表示する OpenResty XRay の Insights 日次レポート

このため、ガイド付き分析は主にアプリケーションの開発やデモンストレーションに役立ちます。本番環境では、自動レポートが新しいクラッシュをあなたのために浮かび上がらせてくれます。

よくある質問

core dump から Lua バックトレースを取得するにはどうすればよいですか?

OpenResty XRay は core ファイルから直接 Lua レベルのバックトレースを再構築し、それを Lua CPU フレームグラフから自動的に推測します。各 Lua コルーチンの完全なコールスタックを、引数とローカル変数の値を含むすべての関数フレームとともに表示します——これは本来 GDB で手作業で掘り出さなければならないコンテキストと同じものです。

LuaJIT はなぜ FFI cdata でセグメンテーション違反を起こすのですか?

基盤となる C ポインタが NULL であるポインタ型 cdata のフィールドにアクセスすると、セグメンテーション違反が発生します。この core dump では、order_cdata は有効な cdata オブジェクトですが、それがラップするポインタは NULL です。decode_order_data がその user_id フィールドを読み取り、NULL ベースからオフセット 4 を参照した(問題のアドレス 0x4)ため、SEGV が発生し worker プロセスがクラッシュしました。修正方法は、フィールドを参照する前に、order_cdata == nil によってラップされたポインタが NULL かどうかを確認することです。

OpenResty や Nginx の core dump はどのシグナルで発生しますか?

今回のクラッシュでは、core dump は SEGV シグナルによって発生しました。SEGV は「Segmentation Violation(セグメンテーション違反)」の略で、通常は不正なメモリアクセスを示します——プロセスが許可されていないメモリアドレスに触れたということです。OpenResty XRay は、正確なシグナル、問題の命令、そしてクラッシュした瞬間の CPU レジスタ値を報告します。

クラッシュした正確な Lua の行を見つけるにはどうすればよいですか?

レポートは、クラッシュした Lua 関数フレームをそのソースにリンクします。関数のボックスにマウスを合わせるとソースファイルとその完全なパスが表示され、レポートは正確なソース行番号を示します。ここでは decode_order_data の 79 行目、すなわち cdata 内部にラップされた NULL ポインタを参照している行を指しています。

GDB を手動で実行せずに core dump を分析できますか?

はい。OpenResty XRay はオンラインアプリケーションで新しい core dump を監視し、Insights ページに日次および週次の自動分析レポートを生成するため、ガイド付き分析を手動で実行する必要はありません。内部では Y 言語を使用し、コンテキストに応じて Stap+、eBPF+、GDB、ODB などのランタイム上で動作します。

OpenResty XRay について

OpenResty XRay動的トレーシング製品であり、実行中のアプリケーションを自動的に分析して、パフォーマンスの問題、動作の問題、セキュリティの脆弱性を解決し、実行可能な提案を提供いたします。基盤となる実装において、OpenResty XRay は弊社の Y 言語によって駆動され、Stap+、eBPF+、GDB、ODB など、様々な環境下で複数の異なるランタイムをサポートしております。

著者について

章亦春(Zhang Yichun)は、オープンソースの OpenResty® プロジェクトの創始者であり、OpenResty Inc. の CEO および創業者です。

章亦春(GitHub ID: agentzh)は中国江蘇省生まれで、現在は米国ベイエリアに在住しております。彼は中国における初期のオープンソース技術と文化の提唱者およびリーダーの一人であり、Cloudflare、Yahoo!、Alibaba など、国際的に有名なハイテク企業に勤務した経験があります。「エッジコンピューティング」、「動的トレーシング」、「機械プログラミング」 の先駆者であり、22 年以上のプログラミング経験と 16 年以上のオープンソース経験を持っております。世界中で 4000 万以上のドメイン名を持つユーザーを抱えるオープンソースプロジェクトのリーダーとして、彼は OpenResty® オープンソースプロジェクトをベースに、米国シリコンバレーの中心部にハイテク企業 OpenResty Inc. を設立いたしました。同社の主力製品である OpenResty XRay動的トレーシング技術を利用した非侵襲的な障害分析および排除ツール)と OpenResty Edge(マイクロサービスおよび分散トラフィックに最適化された多機能ゲートウェイソフトウェア)は、世界中の多くの上場企業および大企業から高い評価を得ております。OpenResty 以外にも、章亦春は Linux カーネル、Nginx、LuaJITGDBSystemTapLLVM、Perl など、複数のオープンソースプロジェクトに累計 100 万行以上のコードを寄与し、60 以上のオープンソースソフトウェアライブラリを執筆しております。

翻訳

英語版の原文と日本語訳版(本文)をご用意しております。読者の皆様による他の言語への翻訳版も歓迎いたします。全文翻訳で省略がなければ、採用を検討させていただきます。心より感謝申し上げます!