はじめに
少し前からVScode (Visual Studio Code)
をよく使っています。
以前からMinGW-W64
をインストールして gcc
は使っていたのですが、プログラムの編集はテキスト エディタでコンパイルはコマンドラインで行っていました。
デバッグするときには別途Visual Studio
を起動して、そちらで行っていました。
GDB
は強力なデバッガですがコマンド入力が面倒で、あまり使っていなかったのですがVScode
を使うようになってからはVScode
から簡単にGDB
が使えるので、最近は専らVScode
+gcc
+GDB
が主要な開発環境になっています。
前提
MinGW-W64
のインストールやVScode
のインストール/設定に関する記事はたくさんありますので、それらは省略させていただきます。
VScode
でデバッグする情報も最近はかなり見かけますので、他ではあまり紹介されていない機能を中心に解説しようと思います。
開発環境
以下の環境で実行しています。
- Windows 10 Home
- gcc (i686-posix-dwarf-rev0, Built by MinGW-W64 project) 8.1.0
- VScode バージョン: 1.66.2
対象のプログラム
以下のようなありきたりのプログラムを使ってデバッグしています。
#include <stdio.h>
int calc_sum(int* a, int size)
{
int sum = 0;
for (int i = 0; i < size; ++i) {
sum += a[i];
}
return sum;
}
int main(void)
{
int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = calc_sum(data, sizeof data / sizeof *data);
printf("%d\n", sum);
return 0;
}
VScode の設定
ビルドタスク
{
"version": "2.0.0",
"tasks": [
{
"label": "C Language",
"type": "shell",
"command": "gcc",
"args": [
"-g",
"-Wall",
"-pedantic",
"-fexec-charset=cp932",
"-std=c11",
"-D__USE_MINGW_ANSI_STDIO=1",
"-o",
"${workspaceFolder}\\${fileBasenameNoExtension}",
"${file}",
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
デバッグ構成
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/${fileBasenameNoExtension}.exe",
"args": [ ],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"miDebuggerPath": "C:/MinGW/bin/gdb.exe",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
GDB
のコマンドはDebugging with GDBを参考にさせていただきました。
日本語訳もあります。
VScode で GDB のコマンドを直接実行する
いきなり本題です。
まず、適当な行にブレークポイントを置いてデバッグ実行します。
注目して頂きたいのはデバッグ コンソール1に書かれている内容です。
デバッガ コマンドを実行するには "-exec <command>" とすると書かれています。
例として "-exec info registers" が示されています。
せっかくなので実行してみます。
このようにレジスタの内容が表示されます。
これくらいであれば、サイドバーのデバッグの変数に表示されるので必要無いのですが、-exec
を使用するとGDB
の全てのコマンドを実行することが可能になります。
メモリの内容を表示
改めて説明するほどのものではないですが、C/C++
では配列そのものを関数に渡すことはできません。
配列を実引数に指定すると、配列の先頭要素へのポインタをポインタを渡すことになります。
calc_sum()
関数の仮引数a
はポインタですので、デバッガで表示されるのはアドレスで、配列としては表示されないです。
デバッグするときに渡されたデータが先頭の要素しか表示されないので、内容の確認がしづらいです。(16進数のダンプ表示もあるのですが、正直使い勝手がよくないです)
GDB
にはexamine
(省略形は 'x') というコマンドがあり、メモリの内容を直接任意のフォーマットで表示できます。
例えば
-exec x/10dw
とすると、以下のような結果が得られます。
これは、a
のアドレスから、10回、10進数で、ワードデータを表示する、という意味になります。
x
コマンドの一般形式は
x/nfu addr
で、n
は繰り返し回数、f
は表示フォーマット、u
は表示データサイズです。
フォーマット | 意味 |
---|---|
x | 符号なし16進整数値 |
d | 符号付き10進整数値 |
u | 符号なし10進整数値 |
o | 符号なし8進整数値 |
a | アドレス |
c | 文字 |
f | 浮動小数点数 |
s | 文字列 |
i | インストラクション |
サイズ | バイト数 |
---|---|
b | 1 |
h | 2 |
w | 4 |
g | 8 |
人工配列 (Artificial Arrays)
実はGDB
には人工配列2と言う機能があり、もっと簡単に関数に渡された配列を表示することができます。
-exec p *a@10
とすると、以下のように表示されます。
a
を要素数10の配列と見なして表示します。
逆アセンブルリスト
あまり使う機会はないと思いますが、逆アセンブルリストを表示することもできます。
VScode
で逆アセンブル ビューを表示することもできますが、ソース コードが表示されないので正直あまり見やすいものではないです。
calc_sum()
関数を逆アセンブルすると、次のように表示されます。
/s
はソース コードを表示するためのオプションです。
変数の内容を変更
set
コマンドを使用して、変数の内容を変更することができます。
実行の順番を変更
ブレーク ポイントで止まっているときにjump
コマンドを使用すると、任意のアドレスから実行を再開することができます。
現在、calc_sum()
関数の最初、ソースコードの5行目で止まっていますが、1行ステップ実行してから、for
ループを飛ばしてreturn sum;
の行から実行します。なお、そのままでは、プログラムが終了してしまうので、main()
関数でcalc_sum()
の呼び出しの後にブレークポイントを設置します。
main()
関数でcalc_sum()
から抜けてきたところで止まっていますが、注目して頂きたいのは、変数sum
の値です。
calc_sum()
でfor
ループを処理していないので、sum
は0
のままです。
実のところ、このjump
コマンドがこの記事の目玉です。他のコマンドはVScode
からも殆どが実行できますので、あえてGDB
のコマンドを直接実行する必要はないのですが、jump
だけは無理のようです3。
関数を呼び出す
あまり使う機会はないかと思いますが、GDB
から直接関数4を呼び出すこともできます。
calc_sum()
関数を直接呼び出して、返却値をsum
に代入しています。
今度は関数内の処理を全て実行しているので、sum
は55
になっています。
ウォッチ式の有効活用
人工配列はウォッチ式でも有効です。
先ほどと同じようにcalc_sum()
関数に入ったところにブレークポイントを設置して、実行を止めています。
ここで、ウォッチ式に
*a@size
と入力します。すると以下のように配列の内容が表示されます。
非常に便利で、関数の引数 (ポインタ) だけでなく、malloc()
関数などで確保された領域に対しても有効です。
条件付きブレークポイントの追加
例えば、画像のようにx==5
という条件でブレークポイントを追加します。
すると条件が成立した時点で実行が停止します。
なお、通常のブレークポイントと条件付きブレークポイントではアイコンが違います。
まとめ
「VScode
からGDB
のコマンドを実行することができる」ことを紹介したかったのですが、殆どのことはVScode
から操作できるので、ちょっと中途半端な内容になってしまいました。
でも、GDB
のコマンドは豊富で、まだまだ紹介していない機能もあります。
ドキュメントを読んでみて、使えそうなコマンドがあったら是非使ってみて下さい。
-
デバッグ コンソールが表示されていないときは、メニューから [表示 (V)] → [デバッグ コンソール] で表示できます。 ↩
-
仮想配列と言う日本語訳もあるようです。個人的にはこの方がしっくりきます。 ↩
-
VScode
上でマウス右クリックすると 「カーソルにジャンプ」 という項目があるのですが、実行しようとすると「Set next statement is not supported by the current debugger.」というエラーメッセージが表示され実行できないです。 ↩ -
printf()
などの標準ライブラリ関数も呼べますが、プログラム内で使われていない関数はダメです。
なお、関数の呼出しはプログラムとは別に行われますが、グローバル変数が変更されたり、ファイルの入出力などが行われますので、実行には十分注意して下さい。 ↩