Debugging Linux CMake Projects with gdbserver

Erika Sweet

Erika

Gdbserver is a program that allows you to remotely debug applications running on Linux. It is especially useful in embedded scenarios where your target system may not have the resources to run the full gdb.

Visual Studio 2019 version 16.5 Preview 1 enables remote debugging of CMake projects with gdbserver. In our previous blog post we showed you how to build a CMake application in a Linux docker container. In this post we’re going expand on that set-up to achieve the following workflow:

  1. Cross-compile for ARM in our Linux docker container
  2. Copy the build output back to our local machine
  3. Deploy the program to a separate ARM Linux system (connected over SSH) and debug using gdbserver on the ARM Linux system and a local copy of gdb

This allows you to leverage a specific version of gdb on your local machine and avoid running the full client on your remote system.

Support for this workflow in Visual Studio 2019 version 16.5 Preview 1 is still experimental and requires some manual configuration. Feedback on how you’re using these capabilities and what more you’d like to see is welcome.

Cross-compile a CMake project for ARM

This post assumes you are have already configured Visual Studio 2019 to build a CMake project in a Linux docker container (Ubuntu). Check out our previous post Build C++ Applications in a Linux Docker Container with Visual Studio for more information. However, nothing about this workflow is specific to Docker, so you can follow the same steps to configure any Linux environment (a VM, a remote Linux server, etc.) for build.

The first thing we will do is modify our build to cross-compile for ARM. I’ve created a new Dockerfile based on the image defined in my previous post.

In this Dockerfile I acquire my cross-compilers and copy a CMake toolchain file from my local Windows filesystem to my Linux Docker container. CMake is also a dependency but I will deploy statically linked binaries directly from Visual Studio in a later step.

CMake toolchain files specify information about compiler and utility paths. I used the example provided by CMake to create a toolchain file on Windows with the following content.

Save your toolchain file as ‘arm_toolchain.cmake’ in the directory where your new Dockerfile is saved. Alternatively, you can specify the path to the file relative to the build context as a part of the COPY command.

We can now build an image based on our new Dockerfile and run a container derived from the image:

Lastly, we will interact with our docker container directly to start SSH and create a user account to use with our SSH connection.  Again, note that you can enable root login and start SSH from your Dockerfile if you want to avoid any manual and container-specific configuration. Replace <user-name> with the username you would like to use and run:

You are now ready to build from Visual Studio.

Configure CMake Settings in Visual Studio to cross-compile for ARM

Make sure you have Visual Studio 2019 version 16.5 Preview 1 or later and the Linux development with C++ workload installed. Open Visual Studio and create a new CMake project or open the sample application created in our previous post.

We will then create a new CMake configuration in Visual Studio. Navigate to the CMake Settings Editor and create a new “Linux-Debug” configuration. We will make the following modifications to cross-compile for ARM:

  1. Change the configuration name to arm-Debug (this does not affect build, but will help us reference this specific configuration)
  2. Ensure the remote machine name is set to your Linux docker container
  3. Change the toolset to linux_arm
  4. Specify the full path to your toolchain file on your Linux docker container (/opt/toolchains/arm_toolchain.cmake) as a CMake toolchain file.
  5. Navigate to the underlying CMakeSettings.json file by selecting ‘CMakeSettings.json’ in the description at the top of the editor. In your arm-Debug configuration, set “remoteCopyBuildOutput”: true. This will copy the output of your build back to your local machine for debugging with gdb.

Note that whenever your change your compilers you will need to delete the cache of the modified configuration (Project > CMake Cache (arm-Debug only) > Delete Cache) and reconfigure. If you don’t already have CMake installed, then Visual Studio will prompt you to deploy statically linked binaries directly to your remote machine as a part of the configure step.

Your CMake project is now configured to cross-compile for ARM on your Linux docker container. Once you build the program the executable should be available on both your build system (/home/<user-name>/.vs/…) and your local Windows machine.

Add a second remote connection

Next, I will add a new remote connection to the connection manager. This is the system I will be deploying to and has OS Raspbian (ARM). Make sure ssh is running on this system.

Note: The ability to separate your build system from your deploy system in Visual Studio 2019 version 16.5 Preview 1 does not yet support Visual Studio’s native support for WSL. It also does not support more than one connection to ‘localhost’ in the connection manager. This is due to a bug that will be resolved in the next release of Visual Studio. For this scenario, your docker connection should be the only connection with host name ‘localhost’ and your ARM system should be connected over SSH.

Configure launch.vs.json to debug using gdbserver

Finally, we will configure the debugger. Right-click on the root CMakeLists.txt, click on “Debug and Launch Settings” and select debugger type C/C++ Attach for Linux (gdb). We will manually configure this file (including adding and removing properties) to use gdbserver and a local copy of gdb. My launch file with inline comments is below. Again, this support is new and still requires quite a bit of manual configuration:

Now set a breakpoint and make sure arm-Debug is your active CMake configuration and gdbserver is your active debug configuration.

Make sure arm-Debug is your active CMake configuration and gdbserver is your active debug configuration.

When you press F5 the project will build on the remote system specified in CMakeSettings.json, be deployed to the remote system specified in launch.vs.json, and a local debug session will be launched.

Troubleshooting tips:

  1. If your launch configuration is configured incorrectly then you may be unable to connect to your remote debug machine. Make sure to kill any lingering gdbserver processes on the system you are deploying to before attempting to reconnect.
  2. If you do not change your remote build root in CMake Settings, then the relative path to the program on your remote debug machine is the same as the relative path to the program on your remote build machine from ~/.vs/…
  3. You can enable cross-platform logging (Tools > Options > Cross Platform > Logging) to view the commands executed on your remote systems.

Give us your feedback

Do you have feedback on our Linux tooling or CMake support in Visual Studio? We’d love to hear from you to help us prioritize and build the right features for you. We can be reached via the comments below, Developer Community (you can “Suggest a Feature” to give us new ideas), email (visualcpp@microsoft.com), and Twitter (@VisualC). The best way to suggest new features or file bugs is via Developer Community.

Erika Sweet
Erika Sweet

Follow Erika   

1 comment

  • Avatar
    Sohno Mehar

    The @HostListener decorator is used to set up an event binding on the host element and is applied to a method. The example directive relies on the browser’s DOM API to manipulate its host element, both to add and remove class memberships and to receive the click event. Read More!

Leave a comment