VScode で GDB のコマンドを実行する

# はじめに
少し前から`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

## 対象のプログラム
以下のようなありきたりのプログラムを使ってデバッグしています。
```C
#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 の設定
ビルドタスク
```JSON
{
    "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
			}
		}
	]
}
```
デバッグ構成
```JSON
  "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](https://sourceware.org/gdb/onlinedocs/gdb/index.html)を参考にさせていただきました。
[日本語訳](https://web.sfc.wide.ad.jp/~sagawa/gnujdoc/gdb-4.18/gdb-ja_toc.html#SEC_Contents)もあります。

# VScode で GDB のコマンドを直接実行する
いきなり本題です。
まず、適当な行にブレークポイントを置いてデバッグ実行します。
![デバッグ コンソール](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/2ada9593-8c56-1d94-d948-d606152830b8.png)
注目して頂きたいのはデバッグ コンソール[^dbgconsole]に書かれている内容です。

[^dbgconsole]: デバッグ コンソールが表示されていないときは、メニューから **[表示 (V)]** → **[デバッグ コンソール]** で表示できます。

デバッガ コマンドを実行するには "-exec \<command\>" とすると書かれています。
例として "-exec info registers" が示されています。
せっかくなので実行してみます。
![デバッグ -exec info registers](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/79d7d18d-ad6a-ef24-d125-fb4f8ac50dd5.png)
このようにレジスタの内容が表示されます。
これくらいであれば、サイドバーのデバッグの変数に表示されるので必要無いのですが、`-exec`を使用すると`GDB`の全てのコマンドを実行することが可能になります。

## メモリの内容を表示
改めて説明するほどのものではないですが、`C/C++`では配列そのものを関数に渡すことはできません。
配列を実引数に指定すると、配列の先頭要素へのポインタをポインタを渡すことになります。
`calc_sum()`関数の仮引数`a`はポインタですので、デバッガで表示されるのはアドレスで、配列としては表示されないです。
デバッグするときに渡されたデータが先頭の要素しか表示されないので、内容の確認がしづらいです。(16進数のダンプ表示もあるのですが、正直使い勝手がよくないです)

`GDB`には`examine`(省略形は 'x') というコマンドがあり、メモリの内容を直接任意のフォーマットで表示できます。
例えば
```GDB
-exec x/10dw
```
とすると、以下のような結果が得られます。
![デバッグ examine](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/21baf5e7-0b95-2c9a-de3f-6fc434782db6.png)
これは、`a`のアドレスから、10回、10進数で、ワードデータを表示する、という意味になります。
`x`コマンドの一般形式は
```GDB
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`には人工配列[^ArtificialArrays]と言う機能があり、もっと簡単に関数に渡された配列を表示することができます。

[^ArtificialArrays]:仮想配列と言う日本語訳もあるようです。個人的にはこの方がしっくりきます。

```
-exec p *a@10
```
とすると、以下のように表示されます。
![デバッグ 仮想配列](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/cd9eb563-12aa-417b-b361-99ed96527e02.png)
`a`を要素数10の配列と見なして表示します。
## 逆アセンブルリスト
あまり使う機会はないと思いますが、逆アセンブルリストを表示することもできます。
`VScode`で逆アセンブル ビューを表示することもできますが、ソース コードが表示されないので正直あまり見やすいものではないです。
`calc_sum()`関数を逆アセンブルすると、次のように表示されます。
![デバッグ disassemble](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/ea64df3a-2255-2ff0-14a6-7846e6131573.png)
`/s`はソース コードを表示するためのオプションです。
## 変数の内容を変更
`set`コマンドを使用して、変数の内容を変更することができます。
![デバッグ set](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/34dbeb6f-f455-b360-affb-24c828d0c64b.png)
## 実行の順番を変更
ブレーク ポイントで止まっているときに`jump`コマンドを使用すると、任意のアドレスから実行を再開することができます。
現在、`calc_sum()`関数の最初、ソースコードの5行目で止まっていますが、1行ステップ実行してから、`for`ループを飛ばして`return sum;`の行から実行します。なお、そのままでは、プログラムが終了してしまうので、`main()`関数で`calc_sum()`の呼び出しの後にブレークポイントを設置します。
![デバック jump](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/fe765bfd-268f-9812-2863-636191cf654a.png)
`main()`関数で`calc_sum()`から抜けてきたところで止まっていますが、注目して頂きたいのは、変数`sum`の値です。
`calc_sum()`で`for`ループを処理していないので、`sum`は`0`のままです。

実のところ、この`jump`コマンドがこの記事の目玉です。他のコマンドは`VScode`からも殆どが実行できますので、あえて`GDB`のコマンドを直接実行する必要はないのですが、`jump`だけは無理のようです[^jump]。
[^jump]:`VScode`上でマウス右クリックすると **「カーソルにジャンプ」** という項目があるのですが、実行しようとすると「Set next statement is not supported by the current debugger.」というエラーメッセージが表示され実行できないです。

## 関数を呼び出す
あまり使う機会はないかと思いますが、`GDB`から直接関数[^function]を呼び出すこともできます。

[^function]: `printf()`などの標準ライブラリ関数も呼べますが、プログラム内で使われていない関数はダメです。
なお、関数の呼出しはプログラムとは別に行われますが、グローバル変数が変更されたり、ファイルの入出力などが行われますので、実行には十分注意して下さい。

![デバッグ 関数呼出し](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/03f5bcd9-c35a-6af9-c0db-004ad654d4d1.png)
`calc_sum()`関数を直接呼び出して、返却値を`sum`に代入しています。
今度は関数内の処理を全て実行しているので、`sum`は`55`になっています。

# ウォッチ式の有効活用
人工配列はウォッチ式でも有効です。
先ほどと同じように`calc_sum()`関数に入ったところにブレークポイントを設置して、実行を止めています。
ここで、ウォッチ式に
```
*a@size
```
と入力します。すると以下のように配列の内容が表示されます。
![デバッグ ウォッチ式](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/ee891f82-17f5-f478-6467-447f98ef40fb.png)
非常に便利で、関数の引数 (ポインタ) だけでなく、`malloc()`関数などで確保された領域に対しても有効です。

# 条件付きブレークポイントの追加
例えば、画像のように`x==5`という条件でブレークポイントを追加します。
![デバッグ ブレークポイント](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/cbfc6f4b-bcc7-87bc-20ea-25817dd7805a.png)
すると条件が成立した時点で実行が停止します。
![デバッグ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207428/316a9329-a92b-eb90-f62f-51bce1232866.png)
なお、通常のブレークポイントと条件付きブレークポイントではアイコンが違います。

# まとめ
「`VScode`から`GDB`のコマンドを実行することができる」ことを紹介したかったのですが、殆どのことは`VScode`から操作できるので、ちょっと中途半端な内容になってしまいました。
でも、`GDB`のコマンドは豊富で、まだまだ紹介していない機能もあります。
ドキュメントを読んでみて、使えそうなコマンドがあったら是非使ってみて下さい。