Tag Archives: CMake

Remote debugging with KDevelop and ptxdist

At work we use ptxdist for building firmware images for embedded devices. Our own software is built with CMake and my personal Linux desktop of choice is KDE with KDevelop as Integrated Development Environment. This is nothing new and I wrote about different aspects of that in the past. Today it’s about remote debugging.

Introduction

Writing C/C++ software and building it with CMake is well integrated in KDevelop. Running and debugging things on the same host (your personal computer or laptop) from within KDevelop with gdb is smooth. Things get interesting if you do cross compiling, and the software runs on some embedded target which has not enough space for debugger plus debug symbols plus source fragments. You could use NFS, but every now and then we find ourselves in the position where we need to debug something on devices deployed in the field. What you usually do in this case is installing gdbserver on the target and connect to it from the gdb on your host computer.

The command line way

Remote debugging on the command line is easy from within your ptxdist based Board Support Package (BSP) if you are comfortable enough with gdb or cgdb on the shell. The process is basically as described in HowTo: Debug core dumps with ptxdist 2017.07.0 already, you call ptxdist with the argument gdb. For remote debugging you would do this:

  1. Build gdbserver for your target by selecting it from the menu (like with your other target packages)
  2. Start gdbserver on the target e.g. like this:
    gdbserver 192.168.10.184:12345 mytool --my-arguments
  3. Start gdb on the host from your BSP workspace:
    ptxdist gdb
  4. In the gdb command line connect to gdbserver like this:
    target remote 192.168.10.184:12345
  5. Use gdb as usual (set breakpoints, run, inspect, etc.)

The magic of choosing the correct gdb suitable for the target and setting the necessary options for finding debug symbols and getting paths right is done by ptxdist in step 3. Maybe all this could go to ptxdist’s documentation, but it’s more or less straightforward for experienced developers.

Using KDevelop to edit CMake based projects in your ptxdist BSP

Before we come to debugging, we start with importing a CMake project from the BSP into KDevelop. Please make sure everything builds fine first, a ptxdist go should be completed without errors to have everything in place.

For demonstration I’ll import mosquitto, because it’s a CMake project. I first enabled PTXCONF_MOSQUITTO and let ptxdist built it once.

Then in KDevelop I choose Open / Import Project … and a dialog pops up. The project file I select is /path/to/my/BSP/platform-xyz/built-target/mosquitto-2.0.5/CMakeLists.txt and I usually just select the main CMakeLists.txt of a project here.

CMake projects are usually built out-of-tree, so the source file folders are not clobbered with build artefacts and ptxdist does OOT builds too. In the next dialog KDevelop asks for a build directory, but the suggested one is not the one ptxdist uses. We should select the one from ptxdist however, which in this case is /path/to/my/BSP/platform-xyz/built-target/mosquitto-2.0.5-build and it’s always directly next to the source dir in ptxdist just with -build appended. In that dialog KDevelop tells us now: „Using an already created build directory.“ and that’s exactly what we want here, because ptxdist already set this up with the necessary cross-compiling options.

You could use that to actually code in that source tree now, and even let KDevelop build it locally. That’s a whole other can of worms however and out of scope of what we actually wanna show in this blog post (remote debugging).

Remote debugging with KDevelop

If you never used gdb from within KDevelop before, you should read Running programs in KDevelop and Debugging programs in KDevelop from the KDevelop documentation first.

So, create a Launch Configuration, name it as you like it, instead of using some project target as executable, select „Executable“ and use the binary from the BSP root folder, e.g. this in case of mosquitto_pub (we just use this as example now): /path/to/my/BSP/platform-xyz/root/usr/bin/mosquitto_pub

Then go to the Debug dialog of that Launch Configuration which should look like this somehow:

Screenshot of KDevelop Launch Configurations dialog

As Debugger executable we select the one from the toolchain. Easiest is to click through to your BSP folder, then further to platform-xyz/selected_toolchain and from there choose the gdb. In my case I have
/path/to/my/BSP/platform-xyz/selected_toolchain/arm-v7a-linux-gnueabihf-gdb
here, which actually is in
/opt/OSELAS.Toolchain-2020.08.0/arm-v7a-linux-gnueabihf/gcc-10.2.1-clang-10.0.1-glibc-2.32-b
inutils-2.35-kernel-5.8-sanitized/bin

but it’s a lot easier to use that selected_toolchain symlink here.

Up to here that was the „easy“ part. The main thing I struggled with a lot comes with those three scripts for Remote Debugging seen in the lower half of the dialog. You put those in three different files and those can reside anywhere you like. Let’s dive in.

Gdb config script

The Gdb config script is used to setup gdb and you can think of it like things done before gdb is started. The KDevelop help if you hover over it says: „This script is sourced by gdb when the debugging starts.“ We will put the same in here ptxdist uses when calling gdb, but have to add some more. This is essential. If you don’t get this right, things will not work as expected.

set debug-file-directory /path/to/my/BSP/platform-xyz/root/usr/lib/debug
add-auto-load-safe-path /path/to/my/BSP/platform-xyz/root
set sysroot /path/to/my/BSP/platform-xyz/root
set substitute-path platform-xyz /path/to/my/BSP/platform-xyz
directory /path/to/my/BSP

The first four lines do the same what ptxdist does when calling it with the gdb argument. The last line is exceptionally important, because KDevelop is not started from the BSP workspace directory as it’s the case when calling ptxdist gdb. This is essential however, because since ptxdist-2018.10.0 you only have paths relative to your BSP in the debug symbols. If you don’t add that extra directory gdb started by KDevelop won’t find the source files and breakpoints won’t work. (You can set the breakpoints in KDevelop, but gdb does not find the source files as communicated by KDevelop. Result is: breakpoints stay pending and execution is not interrupted. If you set the breakpoints manually through the GDB console, execution is interrupted, but KDevelop does not jump to the correct source lines. Both not satisfying.)

(Debugging without that directory line probably works when calling ptxdist gdb from the BSP workspace folder, because gdb has some paths added automatically. See Specifying Source Directories from the gdb documentation and especially the section about two special entries ‘$cdir’ and ‘$cwd’.)

Run shell script

The Run shell script can be used to let KDevelop start the application on the remote target. This works with ssh for example, but is only smooth if that connection can be made without typing a password. One example to put into such a script:

ssh root@192.168.10.185 'gdbserver 192.168.10.185:12345' /usr/bin/mosquitto_pub -h 192.168.10.74 -t 'hello' -m 'world'

Consider this optional. You might as well start gdbserver on the target manually with whatever options you need. (This is different for example if you want to attach to an already running process.)

Run gdb script

This is executed by KDevelop on your behalf, usually to connect to the remote gdbserver. You could as well type it into the GDB console in KDevelop. This is what you could put in such a script:

shell sleep 3
target remote 192.168.10.185:12345

Aftermath

The whole thing caused me some headaches, especially because things changed when ptxdist introduced layers back in late 2018. However it’s a lot more comfortable to debug in the same environment as you code and especially inspection of frame stacks and variables is much more comfortable in a graphical environment than on the console. I think it’s worth the effort and maybe this HowTo helps someone else to use a similar setup.

You can of course create those three files manually. What we did at work was writing a shell script creating those from some template, so we can recreate them again with different settings for hosts, binary paths, etc. This is not complicated however, you can do that by yourself.

Getting CDash to work on Debian 9 (stretch) with lighttpd and MariaDB

After reading It’s Time To Do CMake Right and The Ultimate Guide to Modern CMake I stumbled about the slides of Effective CMake by Daniel Pfeifer. (I did not watch the related video C++Now 2017: Daniel Pfeifer “Effective CMake” though.)

What attracted my attention where the commands ctest_coverage() and ctest_memcheck() from the slide about CTest which comes with CMake and which I already use. In libcgi and some non free projects I create additional tests to be run with valgrind if that tool is found on the build host, but when using CTest/CDash I don’t need to do that and also get coverage tests on top, so I set up a local CDash server on my workstation, which was painful in multiple ways.

After extracting the CDash archive and configuring lighttpd to server its PHP files the install.php came back with the following error message:

Specified key was too long; max key length is 767 bytes

It was not easy to find the cause. The web says this is fixed in MariaDB 10.2.x while my Debian stable still has 10.1.x … and I found only some workarounds on that problem for other projects than CDash. I could “solve” that by changing the database collation from utf8mb4_unicode_ci to utf8_unicode_ci in phpMyAdmin on the still empty database cdash before running install.php.

The more challenging problem was to actually submit results to the CDash server when calling CTest. The webserver always responded with HTTP Status Code 417. That was only partly fault of CDash, which seems to call curl with some strange (?) headers for submission. That turned out to trigger some (from my side) unexpected behavior in lighttpd, for which several tickets exist, I found #1017 eventually, which led me to the lighttpd 1.4.21 release info giving the hint I needed. I added this somewhere in my lighttpd config files:

server.reject-expect-100-with-417 = "disable" 

In the end I have a local CDash instance now and could already submit some helpful coverage and memcheck results. Best thing: This way I can remove the error prone additional tests from my CMakeLists.txt and still run the tests with valgrind, even more flexible than ever.

Microsoft Visual C++ 6 und CMake

Warum man eine steinalte Entwicklungsumgebung mit einem modernen Build-System zusammen benutzen will, lässt sich nicht nur mit »Weil es geht!« begründen, es gibt sogar handfeste Argumente dafür, doch der Reihe nach.

Aus Gründen hab ich hier einige MFC-Projekte vorliegen, die in Visual Studio 6 entwickelt wurden, für das hier auch eine gültige Enterprise-Lizenz existiert. Bekommen habe ich Quellcode eines älteren Projekts ohne die Projektdateien. Neu zu entwickeln ist ein vergleichbares Programm und da Entwicklungsumgebung und KnowHow verfügbar sind, wird das neue Projekt eben auch in Visual Studio 6 entwickelt, die Programme laufen ja trotzdem.

Mit CMake füge ich dem Quellcode jetzt einfach die passenden Dateien mit dem Namen “CMakeList.txt” hinzu und lasse mir von CMake ein Projekt für VS6 erzeugen. Vorteil: ich brauche keine Projektdateien im Versionsverwaltungssystem ablegen, kann mir das auschecken wohin ich will und mein Quellcodebaum enthält nur das nötigste. De facto war ich so sogar in der Lage, das alte Projekt, wo mir die Projektdateien fehlten, mit CMake zu bauen und dem auf die Finger zu gucken. Soviel zum »warum«, es folgt jetzt das »wie« …

Zunächst mal sei gesagt, wenn schon Visual Studio 6, dann auch das letzte Service Pack installieren. Ist ein FAQ1 und das Service Pack gibt’s bei Microsoft.

Da ich hier eine MFC-Anwendung entwickeln will, also ein Tool mit GUI, reicht mir der reine Code nicht aus, sondern ist sind noch ressource files nötig. Bevor ich also später die Projektdateien mit CMake generieren lasse, erzeuge ich mit dem Assistenten von Visual Studio ein neues MFC-Projekt, in meinem Fall »Dialogfeldbasierend«:

MFC-Anwendungs-Assistent Dialogfeldbasierend

Den Rest des Assistenten klickt man nach eigenen Vorstellungen durch und dann kann man Visual Studio erstmal wieder schließen. Man hat jetzt einen Ordner vorliegen, in dem folgende Dateien liegen:

von VS6 erzeugte Dateien für ein neues MFC-Projekt

Davon kopieren wir jetzt den Ordner res und folgende Dateien an einen neuen Ort:

  • foo.aps
  • foo.clw
  • foo.cpp
  • foo.h
  • foo.rc
  • fooDlg.cpp
  • fooDlg.h
  • Resource.h
  • StdAfx.cpp
  • StdAfx.h

Das ist jetzt unser neuer Source-Ordner und dort wird nun eine Datei namens CMakeLists.txt angelegt. Fortgeschrittene CMake-Nutzer können das auch auf getrennte Unterordner für Programmcode, Header und externe Ressourcen aufteilen, spar ich mir hier mal und zeige nur wie das mit einer einzigen CMakeLists.txt aussehen kann:

project(foo)
cmake_minimum_required(VERSION 2.8)

find_package(MFC)

set(FOO-H
    foo.h
    fooDlg.h
    Resource.h
    StdAfx.h
)

set(FOO-RC
    foo.rc
)

set(FOO-SRC
    foo.cpp
    fooDlg.cpp
    StdAfx.cpp
)

add_definitions(-D_AFXDLL)
set(CMAKE_MFC_FLAG 2)
add_executable(${PROJECT_NAME} WIN32
    ${FOO-H}
    ${FOO-RC}
    ${FOO-SRC}
)

install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX})

Zu set(CMAKE_MFC_FLAG 2) bitte nochmal selbständig die Doku lesen und nicht verwirren lassen, die dort gezeigten Code-Abschnitte sind aus älteren Versionen vom Installer von CMake selbst kopiert. Wie eingangs angedeutet, machen wir jetzt einen Build außerhalb des Source-Verzeichnisses und rufen dazu das CMake-GUI auf:

CMake GUI für unser Mini-Projekt

Nach einem Klick auf Configure wird man nach dem Generator gefragt. Dort wählt man Visual Studio 6 aus der Liste und bestätigt. Ein weiterer Klick auf Generate erzeugt dann die Projektdateien im zuvor eingestellten Build-Ordner, alles oben im Screenshot zu sehen. Mit einem Doppelklick auf die Datei foo.dsw öffnet sich dann Visual Studio und man kann sein Projekt bauen. Schick auch, dass es gleich ein Unterprojekt gibt, was einem die Anwendung installiert. Theoretisch gibt’s auch noch CPack, womit man sich dann noch gleich einen Installer backen kann, aber das würde jetzt hier zu weit führen. ;-)

das von CMake erzeugte VC6-Projekt

Wenn das alles soweit schön kompiliert, kann man den Source-Ordner so nehmen wie er ist und in das Versionsverwaltungssystem seiner Wahl packen. Projektdateien für Visual Studio generiert sich dann jeder Entwickler selbst mit CMake. Bisschen umständlich ist das später beim Hinzufügen von neuen Dateien ins Projekt, weil man die CMakeLists.txt parallel pflegen muss, aber das ist es meiner Meinung nach wert.

  1. Visual Studio 6 Compiler : Freezing CMake Configuration []