Using Semihosting for Debug Output with VisualGDB

This tutorial shows how to use semihosting to send printf()-style messages from the debugged program to VisualGDB. Semihosting is a mechanism of passing data from the debugged program to the debugger via a syscall-style interface. In this tutorial we will create a basic project that sends some debug via semihosting and explain how it works.

You will need VisualGDB 4.3 or later.

  1. Begin with creating a new project with VisualGDB Embedded Project Wizard:
  2. Select “Create a new project -> Embedded binary -> MSBuild”:
  3. Select your device. In this example we will use the STM32F4Discovery board. Note that the semihosting interface does not require any special hardware, so it will work with many other devices as well:
  4. Select the default sample. You can specify the LED settings that match your board, or proceed with the default ones, as LEDs are not vital for this example:
  5. Specify the debug interface you are using. In this example we will use OpenOCD with ST-Link:
  6. Include the <stdio.h> file and replace the for() loop inside the main() function with the following code:
    for (int i = 0;;i++)
    {
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
        printf("LED ON (%d)\n", i);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
        printf("LED OFF (%d)\n", i);
    }

    Make sure your printf() strings end with a “\n” as the C library buffers incomplete lines to improve performance. You can override this behavior by calling setvbuf() or fflush() on stdout.
    Ensure that you are using our latest ARM toolchain. Build your project by pressing Ctrl-Shift-B:

  7. The default implementation of printf()-related syscalls will simply discard the output. In order to change this, open VisualGDB Project Properties and change the syscall implementation to “Support Semihosting”:
  8. Start debugging by pressing F5. You will see the output from the printf() function in Debug->Windows->VisualGDB Output->ARM Semihosting Console:If you are using VisualGDB 5.4 or earlier, the ARM Semihosting Console will be shown in a separate tool window instead of the VisualGDB Output window.
  9. The regular semihosting mechanism is relatively slow as it stops the CPU in order to send new data to the debugger. In order to improve the performance, VisualGDB provides its own fast semihosting framework that places the data in a memory buffer and uses background memory reads to retrieve it. Reference the fast semihosting framework via VisualGDB Project Properties -> Embedded Frameworks:Unless you are planning to use sampling profiler, we recommend checking the “exclude sampling profiler code” checkbox in order to improve build time and remove platform-specific profiler code that could require MCU-specific adjustments.
  10. If you try building the project now, it will fail with the “multiple definition of _isatty” error:
  11. This happens because both regular and fast semihosting provide their own implementation of basic syscalls used by printf() and other functions. In order to solve this, disable the regular semihosting via VisualGDB Project Properties:
  12. Now you can build and run the updated project. The semihosting will work much faster as the CPU will not need to be stopped after each line:
  13. VisualGDB’s semihosting console supports ANSI escape codes for moving the cursor, setting text colors, etc. E.g. you can set different colors for the “LED ON” and “LED OFF” lines using the code below:
    enum class ConsoleColor
    {
        Black,
        Red,
        Green,
        Yellow,
        Blue,
        Magenta,
        Cyan,
        White
    };
     
    void SetForegroundColor(ConsoleColor color)
    {
        printf("\x1b[%dm", 30 + (int)color);
    }
     
    int main()
    {
        //...
        for (int i = 0;;i++)
        {
        	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET);
            SetForegroundColor(ConsoleColor::Green);
            printf("LED ON (%d)\n", i);
            HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_RESET);
            SetForegroundColor(ConsoleColor::Red);
            printf("LED OFF (%d)\n", i);
        }
    }

You can also route the output of printf() to another interface (e.g UART) by redefining low-level IO functions. See the UART tutorial for details.