Howto debug Ceedling Unit-Tests

Jürgen Welzenbach

11/05/2022

Hintergrund

Ceedling ist ein (zu Recht) weit verbreitetes und in Ruby entwickeltes Build-System für C-Projekte.   Zusammen mit Unity und CMock wird es in der Embedded Software-Entwicklung gerne eingesetzt. Nach einer gewissen Einarbeitungszeit sind Unit-Tests normalerweise recht zügig erstellt. Idealerweise erstellt man die Tests zusammen mit der eigentlichen Implementierung. (Auf Test-Driven-Development will ich hier allerdings nicht näher eingehen.)

Es passiert allerdings immer wieder mal, dass ein Test „abraucht“ – sprich: sich sang- und klanglos wegen einer Exception verabschiedet.

Hier ein Beispiel einer solchen wenig hilfreichen Fehlermeldung:

Linking test_adcCtrl.out…
Running test_adcCtrl.out…

ERROR: Test executable „test_adcCtrl.out“ failed.
> Produced no output to $stdout.
> And exited with status: [0] (count of failed tests).
> This is often a symptom of a bad memory access in source or test code.

rake aborted!

C:/testprj/tools/vendor/ceedling/lib/ceedling/generator_helper.rb:36:in `test_results_error_handler‘
C:/testprj/tools/vendor/ceedling/lib/ceedling/generator.rb:173:in `generate_test_results‘
C:/testprj/tools/vendor/ceedling/lib/ceedling/rules_tests.rake:55:in `block in <top (required)>‘
C:/testprj/tools/vendor/ceedling/lib/ceedling/task_invoker.rb:107:in `invoke_test_results‘
C:/testprj/tools/vendor/ceedling/lib/ceedling/test_invoker.rb:124:in `block in setup_and_invoke‘
C:/testprj/tools/vendor/ceedling/lib/ceedling/test_invoker.rb:51:in `each‘
C:/testprj/tools/vendor/ceedling/lib/ceedling/test_invoker.rb:51:in `setup_and_invoke‘
C:/testprj/tools/vendor/ceedling/lib/ceedling/rules_tests.rake:70:in `block (2 levels) in <top (required)>‘
../../tools/vendor/ceedling/bin/ceedling:346:in `block in <main>‘
../../tools/vendor/ceedling/bin/ceedling:333:in `<main>‘
Tasks: TOP => build/test/results/test_adcCtrl.pass
(See full trace by running task with –trace)

Ursache sind z.B. Aufrufe von SDK-Funktionen, die auf Hardware-Adressen zugreifen und die es in der Simulation natürlich nicht gibt. In solchen Fällen die Fehlerursache zu finden, kann ohne weitere Analysemöglichkeiten auf längliches printf-Debugging hinauslaufen. Schön ist anders!

Hinweis: Im Folgenden gehe ich davon aus, dass bereits Unittests (unter Windows) mit Ceedling laufen; für das Einrichten von Ceedling gibt es gute Anleitungen ;-)

Wir verwenden in verschiedenen Projekten Ruby mit DevTools (Ruby-Installation https://rubyinstaller.org/downloads/, z.B. rubyinstaller-devkit-2.7.5-1-x86.exe). Der Compiler gcc kommt mit den MSYS2-Tools (https://www.msys2.org/) und ist da schon inklusive.

Back to the roots: gdb

Es ist schon seeehr lange her, dass ich den GNU Debugger gdb direkt verwendet habe. Zum Applikations-Debugging verwendet man normalerweise eine IDE (Segger/CubeIDE) oder ein anderes graphisches Debugger-Frontend (xgdb, DDD, KDbg,…) um sich das Leben nicht unnötig zu erschweren.

Wer Cygwin verwendet, hat den gdb normalerweise schon installiert. Wer Ruby mit MSYS2 verwendet, muss den gdb als optionales Packet installieren. Tipps dazu finden Sie am Ende („MSYS2 gdb-Installation“).

Die old-school-Version, um die Absturzursache eines Tests zu finden ist

  • eine Konsole (cmd) öffnen und den gdb mit dem absoluten Pfad zum Test-Executable starten:

    C:\testprj\scripts>gdb C:\testprj\Unittest\build\test\out\test_adcCtrl.out

  • gdb lädt das Executable und liest die darin enthaltene Symbolinformation:

    Reading symbols from C:\testprj\Unittest\build\test\out\test_adcCtrl.out…
    (gdb)

  • Mit “run” (oder kurz “r“) starten man das Executable:

    (gdb) rStarting program: C:\tesprj\Unittest\build\test\out\test_adcCtrl.out
    [New Thread 29368.0x700c]

    Thread 1 received signal SIGSEGV, Segmentation fault.
    test_adcCtrl_daqComplete () at test/test_adcCtrl.c:68
    68          adc_handler.Instance->CR = 0xFF;(gdb)

  • Falls man’s noch etwas genauer haben möchten – den Stacktrace erhält man mittels „where“:

    (gdb) where
    #0 test_adcCtrl_daqComplete () at test/test_adcCtrl.c:68
    #1  0x00f816f0 in run_test (func=0xf818b4 <test_adcCtrl_daqComplete>, name=0xfc8053 „test_adcCtrl_daqComplete“, line_num=57) at build/test/runners/test_adcCtrl_runner.c:93
    #2  0x00f81755 in main () at build/test/runners/test_adcCtrl_runner.c:109(gdb)

Mit diesen Informationen kommt man zumeist schon sehr viel weiter. (In diesem konkreten Fall greift der Applikationscode auf einen Pointer in der Datenstruktur ADC_HandleTypeDef  zu – und der ist NULL).

Etwas mehr Komfort bitte

Hardcore-gdb-ing ist ok – aber im Alter mag man’s gern auch etwas bequemer. Als gelegentlich VS Code-Nutzer lag die Suche nach einem Plug-In nahe, welches den Ceedling Unit-Tests Workflow unterstützt. Dabei bin auf über mehrere Blog-Beiträge etc. gestoßen. Am Ende des Artikels finden Sie eine Liste der verwendeten Quellen. Die Quintessenz ist in diesem Artikel zusammengefasst.

Jetzt geht’s also um die Zutaten die’s braucht, um einen Testcase zu Debuggen – und das möglichst komfortabel.

VS Code – mehr als ein Editor

VS Code ist ein Open-Source-Projekt (MIT-Lizenz) und wird manchmal auch nur als Editor verwendet. Damit bleibt man aber weit unter seinen Möglichkeiten. Mit den zahlreichen Erweiterungen wird er schnell zur eierlegenden Wollmilchsau – und dem diesem Fall zur „Ceedling-IDE“ :-)

Installation

Folgende Tools sind erforderlich

  1. VS Code (https://code.visualstudio.com/download – ich verwende die 64bit Version)
  2. Die VS Code-Erweiterungen
    • Ceedling Test Explorer: Startet Ceedling und die generierten Test-Runner im Hintergrund, parst deren Ausgaben, listet die Testcases, …
    • C/C++ : Notwendig zum Debuggen (setzen von Breakpoints, Single stepping, etc.)
      Am Ende des Blogs finden Sie eine Liste der installierten Plug-Ins.
  3. gdb – der GNU-Debugger. Wie anfangs erwähnt, ist er in einer Cygwin-Toolchain üblicherweise enthalten. Verwendet man MSYS, wird’s unter Umständen knifflig. Dazu finden Sie Am Ende noch zwei Links, mit Hilfe dere ich den gdb installieren konnte.

Die Installation sollte keine Schwierigkeit darstellen. Falls doch – bitte melden.

Bringing everything together

  1. Zunächst öffnet man in VC Code das Verzeichnis, in dem sich das project.yaml befindet: File -> Open Folder oder in der „Explorer View“
  2. Für diesen „Workspace“ muss zunächst eine „launch“-Konfiguration erstellt werden.
    Dazu wechseln Sie in die „Run View„: Auf der „Activity Bar“ (am linken Fensterrand) klicken Sie auf das Run-Icon  (Dreieck mit Käfer). Nun klicken Sie auf „create a launch.json file„.

    Jetzt erscheint eine Auswahlliste mit verschiedenen Konfigurationsmöglichkeiten. Sie können irgendeine Option auswählen. Es spielt keine Rolle, denn den Inhalt der im Verzeichnis .vscode erzeugten Datei ersetzen wir mit dem folgenden.
  3. Den folgenden Text – die sogenannte Launch-Konfiguration – kopieren Sie in die geöffnete launch.json-Datei:
    
    {
        "version": "0.2.0",
        "configurations": [
     
            {
                "name": "ceedlingExplorer",
                "type": "cppdbg",
                "request": "launch",
                "program": "${workspaceFolder}/build/test/out/${command:ceedlingExplorer.debugTestExecutable}",
                "args": [],
                "stopAtEntry": false,
                "cwd": "${workspaceFolder}",
                "environment": [],
                "externalConsole": false,
                "MIMode": "gdb",
                "miDebuggerPath": "gdb.exe",
                "setupCommands": [
                    {
                        "description": "Enable pretty-printing for gdb",
                        "text": "-enable-pretty-printing",
                        "ignoreFailures": true
                    }
                ],
                "debugConfiguration": {
                    "description": "Debug configuration to run during test debug.",
                    "type": "string",
                    "scope": "resource"
                  },
                "problemMatching": {
                    "mode": "gcc"
                }
            }
        ]
    }

    Mit der Konfiguration teilt man dem Ceedling Test Explorer mit

      • wo die Test-Sourcen liegen.
        Den Pfad „build/test/out“ im Eintrag „program“ müssen Sie ggf. an Ihre Ceedling-Konfiguration (siehe project.yml) angleichen.
      • welcher Debugger verwendet werden soll.
        Hiefür müssen Sie den Parameter für miDebuggerPath an Ihre Buildumgebung anpassen. Ist gdb.exe nicht im Suchpfad, können Sie hier auch einen absoluten Pfad angeben, z.B.:
        „miDebuggerPath“: „C:/Ruby27/msys32/mingw32/bin/gdb.exe„,

    Die weiteren Parameter muss man normalerweise nicht anfassen.

  4. In den VS Code-Settings (via File->Preferences->Settings) unter „Extensions > Ceedling Test Explorer configuration“ muss der Name der Konfiguration, wie im Launch.json angegeben, eingetragen werden:
  5. Damit der Ceedling-Test-Explorer die Testausgaben parsen kann, muss in der Projekt-Datei project.yml noch das Plug-In „xml_test_report“ ergänzt werden::plugins:
    :load_paths:
      - ../../tools/vendor/ceedling/plugins
    :enabled:
      - stdout_pretty_tests_report
      - xml_tests_report
  1. Nun noch in die „Test“-Ansicht wechseln, auf „Refresh“ klicken (zweites Icon neben „TESTING“) – und voila :-)

Danach werden alle Testcases aufgelistet.

Nun können Sie wie in jeder anderen IDE auch den Test editieren, Tests starten, Breakpoints setzen und debuggen. Was will man mehr!

Eine Einschränkung gibt es derzeit noch: Man kann einzelne Test-Cases eines Unit-Tests (noch?) nicht separat ausführen oder debuggen. Wollen Sie also einen Test-Case debuggen, müssen Sie dort zuerst einen Breakpoint setzen und dann den Bug-Button  klicken („Debug this test“).

Sollten Sie Fragen oder Anregungen haben, schreiben Sie einen Kommentar. Ich helfe ihnen gerne weiter.

Referenzen

MSYS2 gdb-Installation:

  • GDB installieren: Eine ältere Ruby/MSYS2-Installation hatte sich energisch geweigert, ein Paket zu installieren. Mit Ruby27+MSYS2 war das kein Problem. Einfach die MSYS32-Console starten (in meinem Fall msys2.exe im Verzeichnis C:\Ruby27\msys32) und mittels pacman -S gdb wird gdb installiert.
    Hier eine weitere Anleitung: https://gist.github.com/bd2357/b2d69ab18849c1e2f70959eef426ff09
    Hinweis: Sie können problemlos die 64Bit-Version von gdb installieren (mingw64/mingw-w64-x86_64-gdb), wie in der Anleitung beschrieben, auch wenn Sie die 32-Bit-Version von Ruby/MSYS2 verwenden.
  • Probleme mit Signaturen: https://github.com/msys2/MSYS2-packages/issues/2343

Links:

VS Code-Plugins


Geschrieben von Jürgen Welzenbach

Jürgen hat nach seinem Elektrotechnikstudium in Erlangen seine Diplomarbeit in Kooperation mit einem Hersteller von ophthalmologischen Geräten und der Universitätsaugenklinik durchgeführt.

In zwei Erlanger Unternehmen fand er zur Embedded Software und hat vor allem HMIs für Baumaschinen und Laboranalysegeräte entwickelt.


Weitere Beiträge

  • 25/09/2025
  • Allgemein, Elektrodermale Aktivität, Hardware, Produktion

Darf ich vorstellen? Das ist EDA – unsere Eule für elektrodermale AktivitätDas ist EDA, unsere kleine Eule mit einem besonderen Talent. EDA kann elektrodermale Aktivität (kurz: EDA) messen – ...

Weiterlesen
  • 09/09/2025
  • Allgemein, Software

In vorangegangenen Blogbeiträgen habe ich zwei wesentliche Komponenten einer einfachen und universell einsetzbaren Software-Architektur vorgestellt: Events mit Dispatcher, Listeners und Datapool. Damit lassen sich bereits sehr viele einfache Use-Cases ...

Weiterlesen
  • 19/03/2025
  • Allgemein, Unternehmen, Veranstaltungen

Wir freuen uns, euch zu einem exklusiven VDI-Event des Netzwerk Systems Engineering einzuladen, das bei uns im Büro stattfindet!Am Freitag, den 28. März 2025, wird sich alles um die ...

Weiterlesen
Cookie-Übersicht

Die Internetseiten der MEDtech Ingenieur GmbH verwenden Cookies. Cookies sind Textdateien, welche über einen Internetbrowser auf einem Computersystem abgelegt und gespeichert werden.

Zahlreiche Internetseiten und Server verwenden Cookies. Viele Cookies enthalten eine sogenannte Cookie-ID. Eine Cookie-ID ist eine eindeutige Kennung des Cookies. Sie besteht aus einer Zeichenfolge, durch welche Internetseiten und Server dem konkreten Internetbrowser zugeordnet werden können, in dem das Cookie gespeichert wurde. Dies ermöglicht es den besuchten Internetseiten und Servern, den individuellen Browser der betroffenen Person von anderen Internetbrowsern, die andere Cookies enthalten, zu unterscheiden. Ein bestimmter Internetbrowser kann über die eindeutige Cookie-ID wiedererkannt und identifiziert werden.

Durch den Einsatz von Cookies kann die MEDtech Ingenieur GmbH den Nutzern dieser Internetseite nutzerfreundlichere Services bereitstellen, die ohne die Cookie-Setzung nicht möglich wären.

Mittels eines Cookies können die Informationen und Angebote auf unserer Internetseite im Sinne des Benutzers optimiert werden. Cookies ermöglichen uns, wie bereits erwähnt, die Benutzer unserer Internetseite wiederzuerkennen. Zweck dieser Wiedererkennung ist es, den Nutzern die Verwendung unserer Internetseite zu erleichtern. Der Benutzer einer Internetseite, die Cookies verwendet, muss beispielsweise nicht bei jedem Besuch der Internetseite erneut seine Zugangsdaten eingeben, weil dies von der Internetseite und dem auf dem Computersystem des Benutzers abgelegten Cookie übernommen wird.

Die betroffene Person kann die Setzung von Cookies durch unsere Internetseite jederzeit mittels einer entsprechenden Einstellung des genutzten Internetbrowsers verhindern und damit der Setzung von Cookies dauerhaft widersprechen. Ferner können bereits gesetzte Cookies jederzeit über einen Internetbrowser oder andere Softwareprogramme gelöscht werden. Dies ist in allen gängigen Internetbrowsern möglich. Deaktiviert die betroffene Person die Setzung von Cookies in dem genutzten Internetbrowser, sind unter Umständen nicht alle Funktionen unserer Internetseite vollumfänglich nutzbar.

Weitere Informationen erhalten Sie in unserer Datenschutzerklärung.

Unbedingt notwendige Cookies

Dieses Cookie wird benötigt, um Ihre Cookie-Einstellungen zu merken und weitere Hauptfunktionen zur Verfügung zu stellen

Um Ihnen eine Auskunft über Ihre gespeicherten personenbezogenen Daten hier (https://medtech-ingenieur.de/gespeicherte-daten-2/) geben zu können, benötigen wir einen Cookie, um Sie bei der Datenabfrage identifizieren zu können. Dieser Cookie muss aus Sicherheitsgründen deshalb aktiviert sein. Ein weiterer Cookie wird gesetzt, um diesen Banner nicht erneut anzeigen zu müssen.

Cookie-Name Beschreibung
PHPSESSID Name: PHP session
Anbieter:
Eigentümer der Webseite (MEDtech Ingenieur)
Zweck:
Wir benötigt, um Sie bei der Anfrage von personenbezogenen Daten identifizieren zu können. Das Cookie wird nur gesetzt, wenn Sie eine Anfrage hier (https://medtech-ingenieur.de/gespeicherte-daten-2/) stellen.
Laufzeit: Sitzungsende
Kategorie: Unbedingt notwendige Cookies
moove_gdpr_popup Name: Cookie-Box Einstellungen
Anbieter:
Eigentümer der Webseite (MEDtech Ingenieur)
Zweck:
Wird benötigt, um Ihre Cookie-Einstellungen zu speichern, um den Cookie-Banner nicht erneut anzeigen zu müssen.
Laufzeit: 1 Jahr
Kategorie: Unbedingt notwendige Cookies
comment_author_9c90e388e3e1be4a6c594fa6ac8a3eec
comment_author_email_9c90e388e3e1be4a6c594fa6ac8a3eec
comment_author_url_9c90e388e3e1be4a6c594fa6ac8a3eec
Name: Kommentar Einstellungen
Anbieter:
Eigentümer der Webseite (MEDtech Ingenieur)
Zweck:
Cookie wird angelegt, wenn Sie ein Kommentar auf MEDtech Ingenieur veröffentlichen wollen, um Sie als Autor identifizieren und den aktuellen Status Ihres Kommentars anzeigen zu können. Das Cookie enthält den angegebenen Namen. Das Cookie wird erst gesetzt, wenn Sie der Speicherung Ihrer personenbezogenen Daten zustimmen.
Laufzeit: 1 Jahr
Kategorie: Unbedingt notwendige Cookies
rmp-rate Name: RMP Rate
Anbieter: Eigentümer der Webseite (MEDtech Ingenieur)
Zweck: Cookie wird angelegt, wenn Sie eine Bewertung eines Blogbeitrags mithilfe des Sternebewertungssystems abgeben. Ihnen wird eine anonymisierte ID zugewiesen, um zu erkennen, ob Sie einen Artikel bereits bewertet haben oder nicht. Das Cookie wird nur verwendet, um zu verhindern, dass mehrfache Bewertung abgegeben werden und erst gesetzt, wenn Sie auf einen Stern klicken.
Laufzeit: 1 Jahr
Kategorie: Unbedingt notwendige Cookies
medtech-download-page Name: Download Page
Anbieter: Eigentümer der Webseite (MEDtech Ingenieur)
Zweck: Cookie wird angelegt, wenn Sie den Landing-Page Prozess erfolgreich durchlaufen haben. Dies geschieht nur, wenn Sie einen Content-Download von unserer Website anstreben.
Laufzeit: 1/2 Jahr
Kategorie: Unbedingt notwendige Cookies