Announcing the VS GDB Debugger extension

Earlier this year I wrote a post on how you could debug C++ code on Linux from Visual Studio. It was a bit cumbersome, but it was doable. Today we are releasing the Visual Studio GDB Debugger extension preview. This will enable debugging remote Linux targets including IoT devices.

To use this extension, you need to have Visual Studio 2015 Community or higher with the Visual C++ tools for Android installed. Those tools use the open source MIEngine that provides the support for the Visual Studio debugger to use Machine Interface that GDB and LLDB use.  The GDB Debugger extension introduces a new project type that surfaces additional project properties that allow you to connect the Visual Studio debugger to GDB either locally or remotely, including on remote Linux targets.

To get started go download the GDB Debugger extension from the VS Gallery and install it. This post is going to walk through how to use the extension in detail, from project creation to F5 including how to configure your Windows and Linux environments for SSH. I’ll have a post next week on how to use this with a Raspberry Pi.

Project creation

After you’ve installed the extension, create a new project and you’ll find a new template for Makefile Project GDB) under Visual C++, Cross Platform.

When we select that and create a new project we get a new empty solution with some supporting files and a readme. Much of what is covered in the readme is covered below, but with more pictures. 🙂

If we take a quick look at the project properties, you’ll see under the debugging tab we’ve added options for a Remote and Local GDB debugger. We’ll cover these options in more detail below.

 

Once configured you can hit any breakpoints you set and the normal VS debugger experience you are accustomed to will be there; peek, watch, setting and removing breakpoints, etc.

Configuring SSH

Local SSH Clients

First you will need to get a local SSH client. You may use the SSH client of your choice, for example the Win32 Port of OpenSSH, PuTTY, MinGW, or Cygwin. The SSH client will be used to securely tunnel Machine Interface commands to a remote GDB executable.

For PuTTY you need to use plink.exe as the SSH Executable. You will probably also want the full putty.exe client and pscp.exe for copying files to your Linux machine. You will need puttygen.exe for creating your private key for SSH access. The tools in the Win32 OpenSSH port and from Putty can be used in the NMake build step in VS. Note that MinGW/Cygwin based SSH is fine for the SSH Executable but cannot be used from the build step in VS due to how we launch processes there.

Creating your keys on Windows

At this time we cannot support certificates that have a pass phrase.

To generate private/public keys for Win 32 OpenSSH use the ssh-keygen utility as follows
ssh-keygen.exe -t rsa -f yourkey

Your private key will be in the file specified above as yourkey and the public key will be in a file yourkey.pub.

For PuTTY you need to create a certificate usable for SSH authentication by running puttygen and clicking generate.

Save the private and public keys to use in subsequent steps.

Adding your public keys on the Linux machine

On your Linux machine you need to add the public key to the ~/.ssh/authorized_keys file. If it doesn’t exist look below for how to create it. Each public key should be on one line. You can do this as follows once the file already exists.
nano ~/.ssh/authorized_keys

In the editor this invokes right click on a new line to insert the copied public key text. The screenshot above shows my file that has two keys added to it. Hit ctrl-x to exit which prompts you to save.
 
If the file does not already exist do the following first:
mkdir ~/.ssh
touch ~/.ssh/authorized_keys
chmod 0700 ~/.ssh
chmod 0600 ~/.ssh/authorized_keys

Connecting with SSH

To connect from an SSH client:
ssh.exe -i privatekey user@host
 
To connect from PuTTY, load your saved session and then go to Connection, Data and set your user name. Now under Connection expand SSH and under the Auth node add the private key you saved. Go back to the session page and save it. Now you can open a session using keys without a password. Simply double click the session name to open it.

On your first connection you will be prompted to save the host as a trusted connection. You must do this via your SSH client before the other tools can connect via cert auth from within Visual Studio.

GDB debugging on an Azure Linux VM

While we’re describing this with an Azure VM the principles are the same for any machine running Linux be it a hosted/local VM, server or device. The only real requirement we have on the remote Linux target is that you can securely connect over SSH using certificates and that GDB is present. Go here if you need help getting started with Azure Linux VMs.

For your source you probably already have something. If you do add it via add existing item by right clicking on the VS project. You can also add a new item if you are just trying this out. For this example I added a new item main.cpp which I added the following to.
#include <iostream>
using namespace std;

int main()
{
 int loops = 10;
 for (int i = 0; i < loops; i++) {
  cout << “loop number ” << i << endl;
 }
 cout << “All done” << endl;
 return 0;
}

The source obviously isn’t interesting, it’s just here to exercise the steps below.

You will need to get the remote machines address, e.g. myServer.cloudapp.net. You can get that in the Azure Portal by selecting your VM and copying the DNS address on the main page.

I’m going to use Putty tools for connecting in this example. No matter which tool you use you’ll want to connect to the remote target at least once so you can accept the prompt to add it to your trusted hosts before scripting connections in VS.

Go to the properties on the project from and on the Debugger tab set the “Debugger to launch” to “Remote GDB”.

Here I need to set my host name, user name, my private key and the secure shell I want to use. When using PuTTY tools I need to use plink as that is the tool used for piping commands as opposed to the interactive PuTTY terminal. Now I can set the remote working directory and remote executable. I have no arguments for the executable and gdb is fine as my remote debugger executable. Apply those changes so we can see these values as we create our build command.

We will use pscp from PuTTY for this in combination with our parameters from the build tab for the private key, user, host and remote working directory. When you are copying files this way to a Linux host make sure the remote directories you are using already exist. Next we’ll specify the build command line with the remote executable name as commands passed via plink. Note here if there are spaces in your parameter values you should escape them with quotes. We specify these on the build command line of the Nmake tab of the project properties.

 

If you open that line for edit you will be able to see parameter names evaluated.
c:\tools\pscp.exe -i $(PrivateKey) “C:\Users\mgoodner\Documents\Visual Studio 2015\Projects\Project1\Project1\main.cpp”  $(RemoteUserName)@$(RemoteHostName):$(RemoteWorkingDirectory)/main.cpp
$(SecureShellExecutable) $(RemoteUserName)@$(RemoteHostName) -i $(PrivateKey) “cd $(RemoteWorkingDirectory);g++ -g main.cpp -o $(RemoteExecutable)”

Now we see the debugger is set to Remote GDB, set a breakpoint in the loop and you can F5.

Let it build and we see we hit our break point. You’ll get the normal functionality you’d expect, e.g. setting and removing breakpoints, adding watches, etc. If you connect to the machine with your ssh client you will see the files are present in the remote destination and you can run the executable there directly as well.

If you hit an error and your output window has a message that “More than one remote source not supported”, check if there is a space at the end of your host name, if so delete it.

Local GDB debugging

In addition to Linux you can use the GDB Debugger extension on Windows, for example using MinGW. Note that you will want your MinGW bin folder, e.g. C:\MinGW\bin, on your path. That provides us with the GNU compilers, gcc, g++ and the gdb debugger that we’ll describe here. Let’s modify the project properties from the example above to run locally. Change the Debugger to Launch drop down from Remote GDB to Local GDB.

 

For the options we will specify the full path to where our source is, do not let the selection dialog use the “.” convention. We can specify our executable name and gdb is fine as it is on our path.

Now tell VS how to build this. On the NMake edit the build command line. First hit “Apply” so we can see the values from what we set on the debugging tab get evaluated. We’ll use g++ to build this which works as it is part of my global path already, and we’ll pass it the source in our working directory, tell it to emit the debugging symbols and output the executable name we specified in our working directory.

 

We can now see that the debugger is set to Local GDB, now you can F5.

 

We’ll let it build and we see we hit our breakpoint again.

More on scripting your build

The project properties from the debug tab are all available for use in the NMake build command line. Note that you can set values for both Remote and Local GDB for use in your build command line even though you are only launching one or the other. The properties available are:
Host Name: $(RemoteHostName)
User Name: $(RemoteUserName)
Private Key: $(PrivateKey)
Secure Shell Executable: $(SecureShellExecutable)
Remote Working Directory: $(RemoteWorkingDirectory)
Remote Executable Name: $(RemoteExecutable)
Remote Executable Arguments: $(RemoteExecutableArguments)
Remote Debugger Executable: $(RemoteDebuggerExecutable)
Local Working Directory: $(LocalWorkingDirectory)
Local Executable: $(LocalExecutable)
Local Executable Arguments: $(LocalExecutableArguments)
Local Debugger Executable: $(LocalDebuggerExecutable)
Local Debugger Server Address: $(LocalDebuggerServerAddress)

For targeting Linux your source must be built on a remote Linux machine. By invoking pscp from the build command line you can copy your source(s) to the remote Linux machine. Using plink you can invoke the compiler on the source on the remote Linux machine. The plink command is capable of executing any command supported by the remote target.

Here is an example remote file copy and build command line using PuTTY Tools:
pscp.exe -i $(PrivateKey) source.cpp  $(RemoteUserName)@$(RemoteHostName):$(RemoteWorkingDirectory)/source.cpp
plink.exe $(RemoteUserName)@$(RemoteHostName) -i $(PrivateKey) “cd $(RemoteWorkingDirectory);g++ -g source.cpp -o $(RemoteExecutable)”

Note that we do not support MinGW/Cygwin SSH tools invoked from the build command line due to how we launch that process. You can use the PowerShell tools port of OpenSSH here including their implementation of sftp.

Here is an example of building on the local Windows machine (presuming g++ is on the global path).
g++ $(LocalWorkingDirectory)\source.cpp -g -o  $(LocalWorkingDirectory)\$(LocalExecutable)

Conclusion

This is our first preview release and we need your feedback on what is working for you and what isn’t. We hope to hear from those of you building cross platform apps, services running on big Linux servers and of course everyone with devices that I hope you are connecting to Azure IoT. We hope to release regularly and are working on enabling this with local GDB servers that interface with On Chip Debuggers for even smaller device targets. If that is of interest to you please get in touch with us via the blog or you can find me on Twitter @robotdad.

–Marc Goodner