Debugging in GDB from Visual Studio

Posted by user on 01 Dec 2015

I wanted to share some tips and tricks for those of you who use a recently released Visual Studio GDB Debugger extension. You may want to read the announcing blog post as well.

Add a new project

After installing the extension. You can create a new project type. Use:

    File -> New -> Project... -> Templates/Visual C++/Cross Platform -> Makefile Project (GDB)

Note: If your solution have other configurations than Debug and Release, you may want to create additional project configurations for this project. This was my case when using CMake's RelWithDebInfo.



First of all, we have to have a remote Unix-based machine (or even a local MinGW distribution) with gdb and other developer tools installed. To start rapidly, I recommend you using vagrant. In the following that our machine has SSH listening on port 2200, user name is vagrant and it is on the localhost (

Private key

If you use vagrant, the private key is in the .vagrant/machines/default/virtual_box/private_key file. Unfortunately, it's not in a format needed by the VS GDB extension. No problem though, we can use Puttygen tool to import the private key (in OpenSSH format) and save it into a .ppk file.

Project properties

In the project properties, you should have something like this (see the Visual C++ Team blog post for some details).

Debugging page

  • Remote Host Name:
  • Remote User Name: vagrant
  • Private Key: C:workspacevmkey.ppk
  • Secure Shell Executable: C:ProgramDatachocolateybinPLINK.exe
  • Remote Working Directory: /home/vagrant/my_source/bin64/$(Configuration)
  • Remote Executable: as you wish
  • Remote Executable Arguments: as you wish
  • Remote Debugger Executable: gdb

Note: If your SSH daemon on the Linux listens on a non-standard port, e.g. 2200, add -P 2200 to the end of Private Key (sic!). This will spare you a few problems.

NMake page

  • Build Command Line:
$(SecureShellExecutable) $(RemoteUserName)@$(RemoteHostName) -i $(PrivateKey) "LowerCaseConfiguration="%24(tr '[:upper:]' '[:lower:]' ^<^<^< $(Configuration) )"; cd $(RemoteWorkingDirectory)/../../project/$LowerCaseConfiguration; ninja $(RemoteExecutable)"
  • Rebuild All Command Line:
pscp.exe -r -i $(PrivateKey) "C:workspacemy_source*"  $(RemoteUserName)@$(RemoteHostName):$(RemoteWorkingDirectory)/../../qdb

The rebuild command will just overwrite all the files. -r option will copy directories recursively. As you remember, I told you to add the port argument -P 2200 to the private key. It's useful here, we (nor extensions' debug command) do not escape private key here so the additional argument will be used as we expect it. If we had chosen to add the port to the remote host name, this command would fail, because $(RemoteHostName) would be expanded to -P 2200 and so pscp wouldn't work.

Some hacks

The build command will, through SSH, go to the project directory and invoke ninja there.
I use the $(Configuration) macro to make my life easy.
I have lower-cased the configuration name (using tr) to go to the correct directory on the remote host.

I had to escape some characters as well.
First, we cannot write a dollar symbol $ followed by an open bracket (, because VS treats this in a special manner. To remedy this problem, we can use hexadecimal ASCII codes preceded by a percent sign %, so we write %24( instead of $(.
Next, we have to escape the windows command line special characters <<. So we write ^< instead of <.

If you don't need such dirty hacks, just write:

$(SecureShellExecutable) $(RemoteUserName)@$(RemoteHostName) -i $(PrivateKey); cd $(RemoteWorkingDirectory)/../../project/$(Configuration); ninja $(RemoteExecutable)"

or even simpler

$(SecureShellExecutable) $(RemoteUserName)@$(RemoteHostName) -i $(PrivateKey); cd $(RemoteWorkingDirectory)/<project directory>; ninja <target>"

Instead of ninja, you can write cmake --build $(RemoteWorkingDirectory)/<project directory> --target <target> as well.

Synchronizing changes

One could just use pscp as proposed in the VS blog post, but it will copy all files, even if there weren't modified since the last copy. Use WinSCP instead. Have a look at keepuptodate task and script option.

# invoke with: "C:Program Files (x86)" /script=name-of-this-file

open vagrant@ # connect
option batch continue # resume on error

# exclude some directories
keepuptodate -filemask="|*/.git/;*/bin/*;*/project/*;*/thirdparty/" c:workspacemy_source /home/vagrant/my_source

Note: WinSCP should accept masks like */bin* that will omit bin directory as well as bin32 or bin64.

Do not forget to open the connection with WinSCP before, validate and accept the server fingerprint to avoid ugly warnings. In the script above, the name vagrant@ is the name of a connection created in WinSCP/PuTTy.

Alternatives: Some people use the rsync from Cygwin. Others prefer their favourite editor's plugin, e.g. Sublime Text has RSync plugin.

Get to work!

  1. Add breakpoints in existing projects of your solution.
  2. Debug!


There are some enhancements that would be welcome in this extension. Hopefully, the folks behind this extension will hear it... The list is minimalist:

  • Add SSH port configuration: this will avoid our ugly hacks with passing port -P parameter in the private key.
  • Synchronize automatically the files in the solution: ideally, no thirdparty software would be needed to synchronized modified files.
  • Allow running without debugging (CTRL-F5): maybe that's a problem in my configuration, but that just doesn't work and it would make the life a bit easier.

That's all for today. Don't hesitate to share your thoughts and comments!

Topics: debugging, gdb, Uncategorized, visual studio