チャチャチャおもちゃの抹茶っちゃ

ゲームのこととかプログラミングのこととか。気が向いたら書く。ブログタイトルは友人が考えました。

【VSCode】デバッグ機能を使ってC/C++のデバッグをする

はじめに

この記事はCCS Advent Calendar 2018 12/8の記事です。みてね!

adventar.org

@MeerIioka 我々の購買情報はもっと価値があるのかもしれない - FragileSky - 別館 - ←前 | 後→@inaenomaki VsVimのすすめ

VSCodeVim拡張の設定とかの話でもよかったけどいなえさんが布教しそうだったので別のお話。

例によって今回の記事も1万字程度ある。

VSCode使ってますか???

Visual Studo Code。Microsoftによって開発された高機能テキストエディタ。2015年初版なので結構新しい。

Windows、MaxOS X、Linux全てに対応!とても嬉しい!設定共有できたりもする。

gitにも標準対応です!!!やったー!!

このテキストエディタ、思いの外気に入ってしまったので最近はよく使っている。Visual Studioには結構文句を言っているけどVSCodeについては「めっちゃ便利!」とか言ってる。これほんとに同じMicrosoft製か???

2018.stateofjs.com

これツイッターで回ってきたやつだけど、VSCode圧倒的パワーを誇っていてさすがやなってなっていた (まあこれ、個人的にはすぐに使い始めることができないVimがかなり利用されてるのが一番意味不明だとはおもっている、恐るべしVim

VSCodeC/C++のプログラムをデバッグしたい

VSCodeにはデバッグ機能が標準で搭載されている。何もしなくてもはじめから搭載されているデバッグ機能はJavaScriptアプリ(Node.jsランタイム)のデバッグ機能で、ブレークポイントとかウォッチ式とかデバッグ機能らしい機能はだいたい使える。

本記事では、標準未対応のC/C++デバッグVSCodeですることを目指す。

ちなみに、本記事の内容をそのままやって動作を確認したのは、

VSCodeは2018年12月8日の最新版のはず。Macはもっていないのでわかりません、すみません。でもLinuxのほうと近そう?

始める前に確認

VSCodeデバッグしたりコンパイルするとき、結局裏で動いているのはコンパイラGCCとデバッガーGDB。これらが皆さんの環境にインストールされてないと何も始まりません!使えるか確認しましょう!

この記事で使えないと困るコマンドは、

$ g++ -g -O0 sample.cpp
$ gdb

の2つ。コマンドラインC++コンパイルできる環境が整っている人は問題ないはず。端末やコマンドプロンプトで打ってみて実行できればOK。Visual StudioをインストールしてついてくるVisual C++コンパイラを使う場合はPATH通してないかもしれないので通しておこう。

一応軽く振れておくと、g++コンパイラgdbはデバッガーである。g++を使ったことがある人は多くても、もしかするとgdbを使ったことがある人は少ないかもしれない。実はこのgdbを用いることでコマンドライン上でデバッグを行うことができる。やってみたい人は少しいじってみるといいかもしれない(「gdb 使い方」などで検索するとわかる。ちゃんとブレークポイントなども設定できることが確認できる)。結果的には、テキストエディタが裏でデバッガーを動かしてくれている、ということに過ぎなかったりする。本当はコマンドラインでやるところをビジュアル化して、自動化してくれていることになる。

f:id:mattyan1053:20181208143945p:plain
コマンドラインからgdbを用いてデバッグしたときの例(ブレークポイントは9行目に設定)

本記事ではWindowsならMinGWLinuxではsudo apt install build-essentialでインストールできたFree Softwere Foundation, Inc.のg++ 7.3.0とGNU gdb 8.1.0で動作確認。どちらもフリー。

VSCodeのインストール

まずはVSCodeがインストールされてないとはじまりませんね!!!!みなさんこの機会にインストールしましょう!!!!

日本語化までの手順はコチラ↓

mattyan1053.hatenablog.com

その他の設定とかはとりあえず今は割愛。でもテキストエディタを自分好みにカスタマイズするのはとても楽しいので時間のあるときにいろいろいじくり回してほしい。そのうちおすすめ拡張機能とかの記事書きたいね。

とりあえずVim拡張機能はあります。いつか設定方法まとめるつもり。これでVimmerも安心!

C/C++拡張機能のインストール

C/C++をコーディングしてデバッグするのでとりあえずC/C++VSCodeが対応できるようにしなければいけない。デフォルトで対応してないものもだいたい拡張機能で解決できる。日本語化のときと同様に拡張機能のとこをクリッククリック!検索窓にC/C++って書いて一番上に出てくるやつをインストールすればOK。

f:id:mattyan1053:20181208135148p:plain
C/C++拡張機能

インストールできたら再起動して拡張機能をアクティブ(有効)にしよう。

サンプルコード

次のようなコードをデバッグする。ファイル名は「sample.cpp」。

#include<iostream>
#include<vector>

int main(){

    int a, b;
    std::vector<int> v;
    a = 10;
    std::cin >> b;
    v.emplace_back(a);
    v.emplace_back(b);
    std::cout << "a=" << a << " b=" << b << std::endl;
    for (int i = 0; i < 2; i++) {
        std::cout << v[i] << std::endl;
    }

    return 0;
}

変数とインスタンスとforループあればいいかなって。まあこんなのデバッグすることはないけど使い方の記事なので許して。

このファイルを適当な場所につくった作業ディレクトリの中にいれる。(ディレクトリのパスに日本語が混ざっているとうまくいかないことがあるみたいなので注意!Windowsのユーザー名とかどうしようもないとこあるのでやっぱりWindowsはクソ)

タスクからコンパイルする

[Ctrl+Shift+B]を押してみてほしい。すると次のように表示されると思う。
f:id:mattyan1053:20181208141334p:plain
まだビルドタスクの設定をしていないので当然。[Enter]を押してすすめていく。
f:id:mattyan1053:20181208141651p:plain
選択肢は一つしかない。[Enter]。
f:id:mattyan1053:20181208141716p:plain
今回は「Others」を選択。すると、次のような「tasks.json」ファイルが、「sample.cpp」と同じディレクトリに生成された「.vscode」フォルダの中に作られると思う。

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "echo",
            "type": "shell",
            "command": "echo Hello"
        }
    ]
}

これは、次からこのタスクを実行すると勝手にVSCodeのほうでコマンド入力をやってくれるというものだ。ここにそのやってほしいコマンド入力について設定ファイルを書いて登録しておく。この状態だと、単純にシェルで「Hello」と表示するだけのものだ。これをコンパイル用に書き換えていく。次のように書き換えてほしい。

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "g++ compile",
            "type": "shell",
            "command": "g++",
            "args": [
                "-g",
                "-O0",
                "sample.cpp"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}

「label」はタスクの名前なので、ぶっちゃけなんでもよい。自分のわかりやすい名前をつければOK。"tasks":[の下にある{ }が一つのタスクを示しているので、下に追加して同じようなことを書いていくと複数のタスクを登録できたりもする。

"command"はみてわかるとおり実行するコマンドだ。その下の"args":[]コマンドライン引数になっている。今回は引数として

を設定している。出力ファイルの名前をつけたければ、"-o", "output"として`"args":[]"の[ ]の中に書き足して上げれば良い。
すべて書いて保存したら、もう一度[Ctrl+Shift+B]を押してタスクを実行する。するとうまくコンパイルされて、同じディレクトリに実行ファイルが生成されるはずだ。

f:id:mattyan1053:20181208142733p:plain
tasks.jsonが生成され、タスクを実行したあとの状態

デバッグを行う

「sample.cpp」を開き、[F5]を押して見る。少しすると、「構成の選択」というのが表示されるので、「C++(GDB/LLDB)」を選択する。
f:id:mattyan1053:20181208144307p:plain
すると、「tasks.json」と同じ場所に今度は「launch.json」が作られ、中身は次のようになっている。

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "enter program name, for example ${workspaceFolder}/a.out",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": true,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

ある程度はデバッガーの設定を書いてくれているので、これからここを設定していく。次のように書き換える。

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/a.out",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": true,
            "MIMode": "gdb",
            "windows": {
                "miDebuggerPath": "C:\\\\MinGW\\bin\\gdb.exe"
            },
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

"program":の項目は、実行ファイルのある場所を示す。このときの${workspaceFolder}は現在の作業ディレクトリをさす。"windows"のところはWindows使っている人以外はいらない。勝手にgdb見つけてきてくれる。というかPath通ってるはずなのにWindowsはこれ書かなきゃいけないのよくわからない。"externalConsole"は追加でコンソール画面開くかって設定なのだけど適宜オンオフ切り替えよう。

実行ファイルに対するコマンドライン引数は"args"に記述する。タスクを作るときと同様にすれば良い。たとえば、

"args":[
    "testfile.txt"
]

などとすることでファイル名を渡すことができる。他にもリダイレクトなどもできるので、

"args":[
    "<",
    "input.txt",
    "<",
    "output.txt"
]

とすれば入力と出力をファイルに出せる。このときはコンソールを追加で開かなくていいので"externalConsole"falseで良さそう。競プロなんかはこれが便利。

これがかけて保存できたら、もう一度「sample.cpp」に戻って[F5]を押してみるとデバッグをしてくれる。今回のコードだと、入力待ちがあるので、そこで一旦止まるはず。裏で起動している端末やコマンドプロンプトを開き、好きな数字を入力しよう。

f:id:mattyan1053:20181208145825p:plain
デバッグ実行して入力待ちの状態

デバッグ画面の使い方

これでデバッグをするまでの設定は完了した。おつかれ! 一度設定すればあとは

  • コードを書く
  • タスクからビルド
  • F5でデバッグ

の繰り返しですぐにデバッグできる。

ブレークポイントの設定

プログラムの実行を指定の場所で一旦止めたい、そんなときに使えるのがブレークポイント。行番号の左隣をクリックすることで赤い丸が表示される。これをつけた状態でデバッグを実行すると、その点で一旦実行を停止してくれる。

f:id:mattyan1053:20181208150403p:plain
ブレークポイントの設定

左下にブレークポイントの一覧が出る。ここのチェックを外すとブレークポイントを一時的に無効にできる。また、行番号の左の赤丸を再度クリックすることで、ブレークポイントを削除することができる。

変数の中身を見る

ブレークポイントを設定したら[F5]でデバッグを実行してみる。すると、一旦停止したとき次のような画面になる。
f:id:mattyan1053:20181208150755p:plain
デバッグ中は下のバーがオレンジ色になる(表示される文字はいれている拡張機能によっても変わるのであまり器にしなくて良い。ぼくの場合はVim拡張機能が入っているのでノーマルモードの表示がある。他にもリポジトリ化してればブランチが表示されたりする)。
左側がデバッグの画面になり、「変数」というところには停止した時点におけるローカル変数の値を表示することができる。1つめのブレークポイントでは初期化をまだどれもしていないので、意味不明な値が入っていることが格納できる。
普通の変数のみでなく、インスタンス変数の中身や配列の中身なども表示することができるので、一つ一つどんな値が入っているのか確認することができる。
画面上のほうに表示されている再生ボタンや停止ボタンのようなものがあるやつを用いて、実行を次のブレークポイントまで進めたり、デバッグを終了したりできる。

ウォッチ式

変数以外に、ウォッチ式というので値を見ることができる。こちらは式なので、a+bなどの値も見ることができる。
f:id:mattyan1053:20181208151239p:plain
「+」マークをクリックしたあと任意の式を入力することで中身を表示できる。dp[i][j]なんかを表示したりすることも可能。

コールスタック

呼ばれた関数がここに表示され、その関数が終了すると表示されなくなる。スタック領域の様子を見ることができるような感じ。再帰処理とか書くとここがすごいことになったりする。

f:id:mattyan1053:20181208154101p:plain
再帰処理をしているときのコールスタック

最後に

以上がVSCodeデバッグを行う手順だった。デバッグを行うまでの設定がVisual StudioなどのIDEと違って手動なので少しむずかしい部分があったかもしれない。とはいえVSCodeはかなり軽量でストレスフリーなので、学校の課題など短めのプログラムを書いたりするときは便利だと思う。ライブラリなどを使う大きなプログラムを書くときはおとなしくIDEを使うのが固いのはそれはそう。うん。

ちなみに、更に便利なものとしてCMakeとかもあるので調べてみると面白い。

デバッグ画面、個人的にはこの画面構成のシンプルさが好き。Simple is the best!!!

VSCodeはとにかく拡張機能が豊富で、JavaとかPythonとか他の言語も使えるようにしたり、TeXを書いたりできる。普段ぼくはVSCode使ってTeXでレポート書いたりしてる。

みんなVisual Studio Codeを使おう!!!!!