アプリケーションのクラッシュが旅の終わりである必要はありません。強力な GNU デバッガーである GDB の使用の基本を学び、Linux でコア ダンプをデバッグする方法を学びます。エンド ユーザーにも、デバッグ初心者にも同様に最適です。
GDB とは何ですか?
GDB ツールは、Linux GNU ツールセットの古くからある非常に評判の高いデバッグ ユーティリティです。独自のコマンド ライン、幅広いコマンドと関数、ステップバイステップのプログラム (コンピューター コード) の実行、さらには変更機能も提供します。
GDB の開発は 1986 年から 1988 年にかけて始まり、1988 年にこのツールは Free Software Foundation の一部になりました。これは完全に無料で、すべての主要な Linux ディストリビューションに簡単にインストールできます。
Windows に興味がある場合は、 「Windows メモリ ダンプ: 正確に何のためにあるの?」 を読んでみてください。その代わり!
Linux ユーザーにとって、コンピューターのバグやエラーを考慮する際に、プロセス フローのどこに GDB が適合するかを理解することが重要です。考えられるシナリオは 3 つあります。まず、アプリケーションのクラッシュに遭遇したエンド ユーザーが、 何が起こったのか をもう少し詳しく知り、そのバグがコミュニティなどですでに知られているかどうかを知りたいと考えている可能性があります。
これは一般的な状況であり、ほとんどの上級ユーザーは、ある時点でアプリケーションのクラッシュをデバッグしていることに気づくでしょう。 GDB を知っていると、このタスクに非常に役立ちます。これについては以下で詳しく説明します。
2 番目のシナリオは、専門家 (IT コンサルタントやテスト エンジニアなど) が、サポートまたは保守も行っているアプリケーションでクラッシュが発生する場合です。このような場合、エンジニアは、特にテスト エンジニアの場合、クラッシュ時に実行されていた関数などのバックトレース (概要) を取得するために、発生したクラッシュをデバッグしたいと思うでしょう。これは、次のような場合に役立ちます。バグをより適切に定義し、テスト ケースを絞り込むのに役立つ可能性があります。
3 番目のシナリオは、より専門的なレベルで GDB を使用して、たとえばブレークポイントの設定、変数監視のデバッグ、コア ダンプ分析などを行いたい開発者のシナリオです。この記事の範囲はそのような専門家にとっては少し軽いものですが、より詳細な GDB 記事が後ほど続きます。また、開発者で GDB を使用したことがない場合は、読み続けてください。
コアダンプと は何ですか?
スタートレックを見て、ピカード船長 (またはジェインウェイ!) が「ワープ コアをダンプする」という指示を聞いたことがあるなら、コア ダンプがどのようなものかをかなりよくイメージできるでしょう。これは基本的に、何らかの障害や問題が認識された結果、コア コンポーネント (ワープ コア) が排出されたものでした。おそらく Linux のコアダンピングをもじったものでしょう。
面白いことはさておき、コア ダンプは、アプリケーションがクラッシュしたときの (完全または部分的な) 状態 (およびメモリ) で生成されたファイルにすぎません。
コア ダンプはバイナリ ファイルであり、デバッガのみが読み取ることができます。 GDB はそのようなデバッガーであり、最高のデバッガーの 1 つです。コア ダンプは、クラッシュしたアプリケーション自体によって書き込まれることもあります (一般的には使用されませんが、可能であり、一部のより大規模なサーバー規模のソフトウェアではこれが使用される場合があります) が、オペレーティング システム自体によって書き込まれることがよくあります。
一部のオペレーティング システムでは、 コア ダンプが デフォルトで無効になっているか、コア ダンプをミニ ダンプに最小化します。これはアプリケーションのデバッグに役立つ可能性がありますが、完全なコア ダンプよりもはるかに制限される可能性があります。ただし、完全なコア ダンプには問題が生じる可能性があります。たとえば、128 GB のメモリを備えたシステムがあり、アプリケーションがそのメモリの大部分を使用している場合、コア ダンプ (ディスク上のファイル) のサイズはほぼ同じになる可能性があります。
特定の OS でのコア ダンプの構成についてはこの記事の範囲外ですが、この情報はオンラインで比較的簡単に見つけることができます。お気に入りの検索エンジンで「Linux Mint でコア ダンプを構成する」などの語句を検索し、「Mint」を Linux オペレーティング システム名に置き換えます。
オペレーティング システムと現在のセットアップによっては、構成ファイルの少しの調整と編集、数回の再起動、および場合によっては問題の簡単な解決が必要になる場合がありますが、一度設定すると、コア ダンプに対して GDB を使用できるようになります。アプリケーションがクラッシュするたびに書き込まれます。
オペレーティング システムでのコア ダンプの書き込みを有効にするのは、常に既存 (または新しい) 構成ファイルの構成設定の変更に限定されることに注意してください。アプリケーションがクラッシュするたびにコア ダンプを有効にして書き込むために、他のアプリケーションをインストールする必要はありません。ただし、ツール GDB をインストールする必要がありますが、デフォルトで主要な Linux ディストリビューションのアプリケーション リポジトリで利用可能になります。
GDBの インストール
Debian/Apt ベースの Linux ディストリビューション (Ubuntu や Mint など) に GDB をインストールするには、ターミナルで次のコマンドを実行します。
sudo apt install gdb
RedHat/Yum ベースの Linux ディストリビューション (RHEL、Centos、Fedora など) に GDB をインストールするには、ターミナルで次のコマンドを実行します。
sudo yum install gdb
コアダンプの場所と問題の再現性
コア ダンプを有効にし、GDB をインストールしたら、GDB を使用してコア ダンプ (アプリケーションがクラッシュしたときにオペレーティング システムによって生成されるファイル) を検索して読み取ります。
アプリケーションがクラッシュした後にシステムをコア ダンプ用に構成した場合は、前回のクラッシュで使用できるコア ダンプが存在しない可能性が高くなります。アプリケーションで同じ手順を実行して、クラッシュ/問題を再現してみてください。
それが起こったら、Linux ディストリビューション上のコア ダンプのデフォルトの場所を確認してください (例:
/ var /crash
または
/ var /lib/systemd/coredump/
Ubuntu/Mint および Centos または
/ var /spool/abrt
RedHat 上)。定期的に、また設定によっては、コア ダンプがバイナリ (アプリケーション) が存在するディレクトリ (可能性が高い)、またはそのメインの作業ディレクトリ (可能性はやや低い) にも書き込まれることがあります。
言葉のあいまいさから、コア ダンプを追跡するのはややとらえどころのないような印象を受けるかもしれません。それは正確な評価だろう。 GDB ツール自体は非常に安定しており、回復力があり、成熟していますが、コア ダンプの作成ははるかに無計画な作業です。これにはいくつかの理由がありますが、主な理由は、ほとんどの主要な Linux ディストリビューションでコア ダンピング動作の実装が異なり、それに合わせてさまざまな構成設定が行われていることです。
コア ダンプの作成は、かなり重要なことにセキュリティにも触れています。コンピュータのメイン メモリの全部または一部がダンプに書き込まれた後、GDB ユーザーは潜在的な機密情報を読み取ることができます。さらに、前に説明したように、コア ダンプの書き込みがシステム リソースによって制限される場合があります。
最後に、不明な状態でクラッシュするアプリケーションを扱っていますが、そのような状態をディスクに書き込むことが常に可能であるとは限りません。特定のシステム上でコア ダンピングを確実かつ永続的に機能させるには、ある程度の時間がかかることが予想されると言っても過言ではありません。次に、問題の再現性というトピックに移ります。
音楽ファイルの再生時にコンピューターが常にクラッシュするなど、常に再現可能な問題がある場合は、コア ダンプを構成し、GDB を使用して同じデバッグを行うことは非常に意味があります。私はかつてこの方法でオーディオ ドライバーのバグを発見しました。あなたがテスト エンジニアで、特定のプログラムを繰り返しテストしている場合、コア ダンプを構成して GDB を使用することは、さらに理にかなっています。
ただし、アプリケーションがクラッシュするインスタンスが 1 つだけあり、その問題をすぐに再現できない場合は、選択する必要があります。アプリケーションの次回のクラッシュに備えたい場合、および/またはアプリケーションが自分にとって非常に重要である場合 (ビジネス継続性など)、次回アプリケーションがクラッシュしたときに、少なくともコア ダンプを構成する必要があります。コアダンプが生成されます。 GDB は、コア ダンプが生成された後でもインストールできます。
GDB を使用したコア ダンプの読み取り
これで、特定のクラッシュしたアプリケーションのコア ダンプが利用可能になり、GDB がインストールされたので、GDB を簡単に呼び出すことができます。
gdb ./myapp ./core
ここには、起動するとすぐにクラッシュする
myapp
というアプリケーションがあります。この Ubuntu システムでは、root として
ulimit -c
より高い数値に設定し、アプリケーションを起動するだけで、コア ダンプが有効になりました。その結果、コア ダンプが
./core
として生成されます。
2 つのオプションを指定して gdb を呼び出します。最初のオプションは、クラッシュを生成したアプリケーション/バイナリ/実行可能ファイルです。 2 つ目は、アプリケーションのクラッシュの結果としてオペレーティング システムによって生成されるコア ダンプです。
コアダンプの初期分析
上の画像の GDB 出力に見られるように、GDB が起動すると、大量の情報が提供されます。これを注意深く確認すると、アプリケーションのクラッシュを引き起こしたシステムで発生した問題に関する多くの情報が得られます。
たとえば、算術例外である
SIGFPE
エラーによりプログラムが終了したことがすぐに分かります。また、GDB が
Core was generated by `./myapp'
ことを正しく識別していることも確認します。
また、
No debugging symbols found in ./myapp
興味深い行/概念も表示され、アプリケーション バイナリからデバッグ シンボルを読み取れなかったことを示しています。デバッグ シンボルは、状況がすぐに曖昧になる可能性がある場所です。最適化/リリース レベルのバイナリ (日常的に実行しているアプリケーションのほとんど) では、スペースを節約し、アプリケーションの実行時間/作業効率を向上させるために、結果として得られるバイナリからデバッグ シンボル情報が削除されます。
結果として得られる
optimized
バイナリからどれだけ削除されたかによっては、単純な関数名であっても使用できない場合があります。この場合、
do_the_maths()
関数名のリファレンスからわかるように、関数名はまだ表示されています。ただし、変数は表示されません。これは、GDB が
No debugging symbols found in ./myapp
メモで参照していたことです。関数名が使用できない場合、関数名の参照は
??
として表示されます。関数名の代わりに。
ただし、クラッシュしているフレーム/関数の名前は
#0 do_the_maths()
であることがわかります。
フレーム
とは何なのか疑問に思われるかもしれません。フレームを説明し考える最良の方法は、コンピューター プログラム内の関数
do_the_maths()
など) について考えることです。 1 つのフレームは 1 つの機能です。
したがって、プログラムがさまざまな関数を段階的に実行する場合、たとえば、C または C++ プログラムの
main()
関数が、最後に
do_the_maths()
を呼び出す
math_function()
() という関数を呼び出す可能性がある場合、フレーム
#0
で 3 つのフレームが生成されます。 (最終的に生成されクラッシュするフレーム) は
do_the_maths()
関数、フレーム
#1
は
math_function()
、フレーム #2 (最初に呼び出されたフレーム、最も大きい番号) は
main()
関数です。
一部のコンピューター プログラムで 10 ~ 20 のフレーム
stack
が見られることは珍しいことではありません。また、データベース ソフトウェアなどでは、時折 40 または 50 のフレーム
stack
発生する可能性が十分にあります。フレームの順序が逆であることに注意してください。最初にフレーム番号
#0
のフレームがクラッシュし、そこから最初のフレームに戻ります。これは、デバッガー/コア ダンプの観点から考えると理にかなっています。クラッシュした時点から開始され、フレームから
main()
関数に至るまでずっと動作しました。
フレーム スタック
という用語は、よりわかりやすくなりました。最も具体的なもの (つまり
do_the_maths
) から最も具体的なもの (つまり
main()
) までのフレームのスタック。何が起こったのかを評価する際に役立ちます。それでは、現在の問題について、GDB でこのスタック/フレーム スタックを確認できますか?確かにそれは可能です。
バックトレースタイム!
gdb プロンプトに到達したら、backtrace
bt
コマンドを発行できます。このコマンドは、現在のスレッド (ほとんどの場合、クラッシュしたスレッドです。GDB はクラッシュしたスレッドを自動検出し、そのスレッドに自動的に配置しますが、必ずしも正確であるとは限りません) に対してのみ、フレームのバックトレースをダンプします。スタック、これについては上で説明しました。言い換えれば、プログラムがクラッシュする瞬間までに実行された関数の流れを確認できるようになります。
ここでは
bt
コマンドを実行しました。これにより、優れたコール スタック、フレーム スタック、スタック、バックトレースが得られます。どのような言葉を選んでも、それらはすべてまったく同じ意味です。
main()
が
math_function
を呼び出し、それが
do_the_maths()
関数を呼び出していることがわかります。
ご覧のとおり、この特定の出力には変数名が表示されず、GDB からデバッグ シンボルが使用できないことが通知されました。すべてのフレーム/関数名が正しく表示され、
??
としてレンダリングされたフレームがないため、結果として得られたバイナリには利用可能なある程度のデバッグ情報がまだ含まれていました。 。
そこで、
gcc
に
-ggdb
オプションを使用して (
gcc -ggdb ...
のように) ソースからアプリケーションを再コンパイルし、より多くの情報が利用できることに注目しました。
今度は、GDB がシンボルを読み取っていることがはっきりとわかります (
Reading symbols from ./myapp...
」で示されているように)、
x
や
y
などの変数名が確認できます。ソース コードが利用可能であれば、問題が発生した正確なソース コード行を確認することもできます (
3 o=x/y;
で示されているように)。すべてのフレームのソース コード行も確認できます。
出力を少し調べてみると、何が間違っているのかがすぐにわかります。変数
o
は、変数
x
を
y
で割った値に設定されていました。ただし、関数の入力 (
(x=1, y=0)
で示される) をもう少し詳しく見ると、アプリケーションが数値をゼロで除算しようとしたことがわかります。これは数学的に不可能であり、
SIGFPE
(算術例外) が発生しています。 )信号。
したがって、私たちの問題は、電卓で
1
0
で割った値を入力するのと何ら変わりません。それもクラッシュはしませんが、文句を言います;)
このような場合、アプリケーションに別の入力を提供することで回避策が考案される可能性があります (特定のクラッシュを回避しようとしており、ソース コードが利用できない場合)。たとえば、0 の代わりに 0.000000001 を入力してみてください。小さな丸め調整を受け入れるか、ソースが利用可能であり、開発者としてソース コードを改善している場合には、プログラムにコードを追加して、
y
変数への誤った入力に対応することができます。
ご覧のとおり、GDB は、さまざまな役割や状況に応じて非常に多用途なツールです。さらに、私たちはこのクールなツールで何ができるのかのほんの表面をなぞっただけです。また、各インスタンスで GDB をどこまで進めることができるかは、状況と周囲の情報 (コア ダンプが利用可能か、デバッグ シンボルがあるかなど) に正確に依存します。しかし、一つだけ明らかなことがある。コア ダンプと GDB をいつ、どのように使用すれば、特定の状況をより完全にデバッグできる可能性が高くなります。
まとめ
この記事では、主要な Linux ディストリビューションに簡単にインストールして使用できる GNU デバッガーである GDB を紹介しました。最初にターゲット システム上でコア ダンプを構成する必要性と、その複雑さについて説明しました。 GDB をインストールして、生成されたコア ダンプを読み取り、処理できるようにする方法を確認しました。
次に、コア ダンプとユーザーまたは開発者との間の基本的なインターフェイスを確認し、
bt
backtrace コマンドを見て実際の分析の実用的な例を示しました。また、最適化されたアプリケーション ビルドとデバッグ [シンボル] インストルメントされたアプリケーション ビルドについて、およびこれが GDB 内の情報可視性のレベルにどのような影響を与えるかについても説明しました。
今後の記事では、GDB についてさらに詳しく説明し、より高度な GDB の使用方法を探っていきます。
デバッグを楽しんでください!





