fastapple's blog

時系列関係なく、情報を追記・分割・統合などします。ブログに記載の内容のうち、引用ではなく、私自身が記載している文章・コードなどについては、自由にご利用ください。

vscodeでC++のソースをgdbでデバッグする


日本語でまとまった情報がなかったため、色々調べながらやった。放置していたが、急速に記憶がなくなってきているので、メモとして残しておく。
コンパイラgccがインストールされていることとする。gccMinGWをインストールしてgccを選べば利用可能になる。MinGWGCCのインストールは以下が参考になる。
<2018年版>VSCode+MinGWでC/C++のプログラミング環境を構築 - newbie hhyuga

gdbデバッグするときのコンパイラオプションは以下が参考になる。
gcc+gdbによるプログラムのデバッグ 第1回 ステップ実行、変数の操作、ブレークポイント
上述のプログラムを最適化なしでコンパイルするコマンドは以下になる。

g++ -O0 test1.cpp

vscodeの設定は、基本的にjsonファイルを書き換える形になる。vscodeのsettings.jsonなどのようにvscodeからGUIjsonファイルを操作できるものもある。下記のyoutubeの動画は非常に参考になる。
setup C++ for visual studio code ( compile and debug) : 100% working - YouTube

上述のブログや動画を参考に、細かいことは忘れたがc_cpp_properties.jsonを以下のように用意した。MinGWのインストールパスによって、適宜書き換える。

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "${workspaceFolder}/**"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE"
            ],
            "intelliSenseMode": "msvc-x64"
        },

        {
            "_comment_": "https://hhyuga.hatenablog.com/entry/2018/06/25/091359",
            "name": "MinGW",
            "intelliSenseMode": "gcc-x64",
            "compilerPath": "D:/software/installed/MinGW/bin/gcc.exe",
            "includePath": [
                "${workspaceFolder}",
                "D:/software/installed/MinGW/include"
            ],
            "defines": [],
            "browse": {
                "path": [
                    "${workspaceFolder}",
                    "D:/software/installed/MinGW/include/*"
                ],
                "limitSymbolsToIncludedHeaders": true,
                "databaseFilename": ""
            },
            "cStandard": "c11",
            "cppStandard": "c++17"
        }
    ],
    "version": 4
}
Launch.jsonの編集

Launch.jsonデバッグモードを実行するとき(▶←こういうボタンを押して実行する)に呼ばれる設定ファイル。デバッグウインドウの左上から歯車ボタンを選んで、Launch.jsonを編集する。
f:id:fastapple:20181110130358p:plain

大体以下のような感じ。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/a.exe",
            "args": [],
            //ファイルを読ませたい場合
            //最初で止めたいならtrueにする  
            "stopAtEntry": true,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": true,
            "MIMode": "gdb",
            "miDebuggerPath": "D:/software/installed/MinGW/bin/gdb.exe",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            //先に実行させたいタスクを記述する
            "preLaunchTask": "debug",
        }
    ]
}

肝は、"stopAtEntry": true,の部分で、これをやっておくとmain()の行にブレークポイントがなくても実行時に止まってくれる。つまり.gdbinitにbreak mainと書いておくのと同じような効果が得られる。後述するが、vscodeだと標準入力から入力をもらうのが至難の業なので、最初に標準入力の付け替えをやりたいときにstopAtEntryしとくと便利。ちなみにvscodeデバッグするときに.gdbinitを読ませる方法はわかっていないので、.gdbinitは書かない前提で話を進める。

上記で、preLaunchTask に debugというのを指定しているが、これは予め定義しておく必要がある。taskの記述はtasks.jsonで行う。ctrl+shift+Pを押しておもむろにコマンドパレットを立ち上げ、Configure Taskをえらぶ。
f:id:fastapple:20181110131416p:plain

以下の"label": "debug",の部分でコンパイルを走らせていることがわかるだろう。同じように定義すればいい。"label": "Compile and run for debug",の部分はあまり覚えてない。

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    // ref. https://www.youtube.com/watch?v=P5bgrG6g-zQ
    "version": "2.0.0",
    "tasks": [
        {
            "label": "debug",
            "type": "shell",
            "command": "",
            "args": [
                "g++",
                "-g",
                "-O0",
                "${relativeFile}",
                "-o",
                "a.exe"
            ]
        },
        
        {
            "label": "Compile and run for debug",
            "type": "shell",
            "command": "",
            "args": [
                "g++",
                "-g",
                "-O0",
                "${relativeFile}",
                "-o",
                "${fileBasenameNoExtension}.exe",
                "&&",
                "${fileBasenameNoExtension}.exe"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": {
                "owner": "cpp",
                "fileLocation": [
                    "relative",
                    "${workspaceRoot}"
                ],
                "pattern": {
                    "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                }
            }
        }
    ]
}

これでデバッグモードで実行する土壌が整った。

標準入力の取り方

ファイルを標準入力からリダイレクトで渡そうとすると、うまくいかないということが起こる。vscodeMinGW の組み合わせでは以下のようなワークアラウンドで対応する。
まず、int main()の前にbreak pointを置いておく(または、先ほどのようにLaunch.json"stopAtEntry": trueが設定されていればそれでいい。)デバッグ実行されたら、DEBUG CONSOLEから、以下のようにする。

-exec call dup2(open("input.txt", 0), 0)

これで、標準入力がinput.txtにつけ変わる。vscodeのDEBUG CONSOLEでは、-exec のあとにgdbのコマンドを書かないといけないことに注意したい。

さて、これで心置きなくdebug実行ができるが、結局printfデバッグ(coutデバッグ)のほうが利用機会が多かったりする。ところで、std::vector vecのi番目をみたい場合のWATCH式は、vec._M_impl._M_start[i]となる。

あとで有用な関連Tipsは書き足すかも。では。