C LANGUAGE TECHNOLOGY

【C言語】GDBでプログラムを効率的にデバッグ

悩んでいる人

GDBでC言語のプログラムを効率的にデバッグする方法を教えて!

こういった悩みにお答えします.

本記事の信頼性

  • 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校コンピュータサイエンス学部2020年の世界大学学術ランキング17位)で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
  • プログラミング歴15年以上,習得している言語: C/C++Solidity,Java,Python,Ruby,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
  • 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」GitHubにオープンソースとして公開
  • 2020年1月~現在はアメリカのスタートアップ「Guarantee Happiness LLC」CTOとしてECサイト開発やWeb/SNSマーケティングの業務.(コロナの影響のため,現在は日本からアメリカの仕事をリモートワーク.)
  • 2020年からC言語で業務委託のエンジニアとして3件,技術顧問として1件,講師として1件の案件を請け負った実績.

こういった私が解説していきます.

GNU Debugger(GDB)

GNU Debugger(GDB)は,GNUプログラムの実行の変更や追跡する機能を提供するデバッガです.

また,プログラムの変数の値を修正したり,監視したりすることもできます.

これまでにC言語で開発したプログラムを実行する時に,「セグメンテーション違反(segmentation fault)」と出力されて強制終了したことがあると思います.

その際,printfデバッグ(printf関数でセグメンテーション違反した場所を探すこと)は面倒ですよね.

そのセグメンテーション違反した場所を簡単に特定できるのがGDBなのです.

※マルチスレッドプログラミングでは,printfデバッグが便利なことがあります.

UbuntuでGDBをインストールする場合は以下のコマンドを実行して下さい.

GDBの簡単な利用例

GDBの簡単な利用例を紹介します.

GDBで実行するC言語のサンプルコードは以下になります.

このコードでは,NULL番地のアドレスに123を代入します.

もちろん実行するとセグメンテーション違反になります.

gdb.cファイルをGDBのオプション「-g -ggdb」を付けてビルドします.

GDBなしで実行

GDBなしで実行します.

セグメンテーション違反(segmentation fault)が発生しました...

GDBありで実行

GDBありで実行します.

18行目でrunコマンドを入力して実行すると,21行目にセグメンテーション違反が発生し,22~23行目に発生した場所がgdb.cファイルの13行目であることがわかります.

24行目でquitコマンドを入力して終了します.

このようにGDBを利用するとセグメンテーション違反した場所が簡単に特定できます.

このコードは短いのでセグメンテーション違反が発生する場所を簡単に見つけられます.

しかし,数万行以上の大規模なソフトウェアの場合,全部読んでセグメンテーション違反を発生する場所を見つけるのはとても大変ですよね.

GDBは大規模なソフトウェアで効果を発揮します!

GDBによるステップ実行

GDBによるステップ実行を解説します.

ステップ実行とは,プログラムを1行ずつ(または1命令ずつ)実行することで変数等の値がどのように変化するのかを分析することができます.

ステップ実行を利用することで,効率的にデバッグすることができます.

ステップ実行するコードは以下になります.

以下のようにビルドします.

GDBなしで実行

まず,GDBなしで実行します.

引数は3を設定すると以下の結果になりました.

GDBありで実行

次に,GDBありで実行します.

19行目のrunコマンドの引数に3を設定するとプログラムの引数に3が設定されます.

GDBありでステップ実行

GDBでステップ実行するためにGDBを起動した直後に,以下のようにmain関数にブレークポイントを設定してします.

ブレークポイントを設定すると,プログラムの実行が,そのブレークポイントを設定した場所で中断します.

以下のようにコマンドラインから3を与えて実行してみましょう.

するとブレークポイントが設定されるので,プログラムの実行が始まり,main関数で実行が中断します.

ブレークポイントは以下の場所で設定できます.

  • 関数名
  • 絶対行数
  • 相対行数
  • アドレス

上記の例では,関数名で指定しています.

設定されているbreak pointは以下のようにすれば確認できます.

また,現在実行中のプログラムリストを表示するためには,以下のようにlistコマンドを実行します.

listはデフォルトでは10行分(現在実行中の行の±5行分)の表示を行います.

引数として行数を指定すれば,その行から10行表示します.

また,以下のようにカンマで区切って表示行の最初と最後を指定できます.

次にnextコマンドを利用してステップ実行します.

nextコマンドを利用すると,このように1行だけ実行できます.

argcが2より大きいかどうかのif文の判定式になりました.

引数に3と入力したのでargcの個数は2(実行ファイル名と引数1個)になり,このif文は偽になるはずです.

実際のargcの値をprintコマンドで表示してみましょう.

以下のようにargcは2に正しく設定されていることがわかります.

以下のように実行してみましょう.

maxnumに値が代入する前(実行される前)は不定値(この場合は0,$2=0から確認)が入っています.

28行目の実行後には正しくmaxnumに3が代入されていることが確認できます.

次に,以下のように34行目(y = func_a(x)の行)にブレークポイントを設定し,continueコマンドで継続実行してみます.

以下のように34行目に設定されたブレークポイントまで継続実行します.

さらに,以下のように実行してみましょう.

このように,ステップ実行をするコマンドには,これまで利用してきたnextコマンドの他にstepコマンドがあります.

nextコマンドとstepコマンドの違いは関数を呼ぶ際にあります.

nextコマンドは,その関数の内部に入ることなく,その関数を全て実行します(ステップアウト実行).

これに対して,stepコマンドは,その関数の内部に入って実行します(ステップイン実行).

以下の例では,func_a関数を実行する際に,nextコマンドとstepコマンドのそれぞれを利用します.

ブレークポイントの設定

上記の例でも紹介しましたが,ブレークポイントは「info breakpoints」で表示できます.

ブレークポイントは,設定した順に番号(Num)が付くようになっています.

例えば,「delete 2」とすると2番目の(34行目に設定した)ブレークポイントを削除できます.

ブレークポイントを削除するのではなく,一時的に無効化するには以下のようにします.

無効にした場合,「info breakpoints」と入力すると以下のようにEnb(Enable)がnになっている(disableになっている)ことがわかります.

もし有効にしたい場合は以下のコマンドを実行して下さい.

再度「info breakpoints」を入力するとEnb(Enable)がyになっていることがわかります.

ループの途中等でブレークポイントを設定すると必ずそこで中断してしまいます.

ブレークポイントには条件を設定できます.

例えば,以下のように設定した場合,xが2の場合にのみ34行目で中断するようになります.

GDBの短縮コマンド

GDBのコマンドには短縮コマンドがありますので,使いこなせるようにしましょう(下表).

コマンド短縮コマンド説明
breakbブレークポイントの追加
continuecプログラムの再開
deletedブレークポイントの削除
listlコードを表示
nextnステップアウト実行
printp式を評価して結果を表示
quitqGDBの終了
runrプログラムの実行
stepsステップイン実行

まとめ

GDBでC言語のプログラムを効率的にデバッグする方法を紹介しました.

GDBは初見では使いこなすのが難しい開発ツールですが,使いこなせばC言語のプログラミングがとても楽になりますので,是非身につけましょう!

GDBを深く理解したいあなたは,以下のサイトが参考になります.

C言語を独学で習得することは難しいです.

私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.

友だち追加

独学が難しいあなたは,C言語を学べるおすすめのオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!

-C LANGUAGE, TECHNOLOGY
-, , , , , , ,