Open Source im professionellen Einsatz
Linux-Magazin 04/2005

Fehlersuche mit dem grafischen Hilfsmittel Data Display Debugger

Jagdstimmung

Bei der Jagd nach Bugs im eigenen Programmcode holen sich Entwickler gern Hilfe. Etwa beim DDD: Er stöbert das virtuelle Ungeziefer auf und gibt den Blick frei auf alle Daten, die das Programm produziert.

874

Wenn ein selbst geschriebenes Programm nicht ordnungsgemäß funktioniert oder gar abstürzt, beginnt der ungeliebte Teil der Programmierarbeit: die Fehlersuche, auch Debuggen genannt. Wichtig sind dann Hilfsprogramme, die diese Arbeit erleichtern und beschleunigen. Ein weiterer Artikel in dieser Ausgabe stellt das Standardtool hierfür vor, den GNU-Debugger GDB[2].

Der GDB ist allerdings so umfangreich, dass die wenigsten Programmierer alle Optionen und Befehle kennen und daher nur einen geringen Teil seiner Funktionen nutzen. Zudem sind die Bedienung per Kommandozeile und die reine Textausgabe nicht sehr komfortabel. Das ändert der DDD (Data Display Debugger,[1]), der wie GDB ebenfalls zur GNU-Familie gehört. DDD ist nicht nur eine grafische Oberfläche für Konsolen-Debugger, er erweitert die Debugging-Möglichkeiten erheblich. Seine Stärke ist die Darstellung von Daten und komplexen Datenstrukturen.

Der DDD-Benutzer kann sogar weitere externe Programme einbinden und eigene Buttons einrichten, um seine eigene kleine IDE zusammenzubauen. Damit steuert er den kompletten Edit-Compile-Run-Zyklus aus dem DDD. Die Software unterstützt aber nicht nur den GDB, sie hilft auch beim Debuggen von Python-, PHP- oder Perl-Skripten (über Letztere berichtet der Perl-Snapshot in dieser Ausgabe).

Fehler verfolgen

Der Code in Listing 1 zeigt ein fehlerhaftes Programm. Es erhält eine Reihe von Zeichenketten als Eingabeparameter und sollte sie sortiert ausgeben, allerdings funktioniert das nicht wie gewünscht. Beim Aufspüren der Fehlerstellen hilft der Data Display Debugger.

Listing 1:
Fehlerhaftes Programm

01 #include <stdio.h>
02 #include <string.h>
03 #include <stdlib.h>
04 
05 void slow_sort(char *arrIn[], int nrElements) {
06   int ready = 0,j;
07 
08   while(!ready){
09     ready=1;
10     for (j=0; j< nrElements; j++) {
11       if (strcmp(arrIn[j], arrIn[j+1]) > 0) {
12         char *tmp;
13         ready = 0;
14         tmp = arrIn[j];
15         arrIn[j] = arrIn[j+1];
16         arrIn[j+1] = tmp;
17       }
18     }
19   }
20 }
21 
22 int main(int argc, char *argv[]) {
23   int i, nrElems;
24   char **args;
25 
26   nrElems = argc-1;
27   args = (char **) malloc(nrElems*sizeof (char *));
28   for (i=0; i< nrElems; i++)
29     args[i] = strdup(argv[i+1]);
30 
31   slow_sort(args, nrElems);
32 
33   for(i=0; i<nrElems; i++)
34     printf("%d: %sn", i, args[i]);
35 
36   free(args);
37   return 0;
38 }

Damit der Debugger später nicht nur Maschinensprache anzeigt, sondern auch den Code, aus dem das Programm übersetzt wurde, muss der Compiler beim Übersetzen Debugging-Informationen einbinden. Beim GCC ist dafür die Option »-g« zuständig, das vollständige Kommando lautet »gcc -g slow_sort.c -o slow_sort«. Als erster Test dient folgender Aufruf:

./slow_sort Otto Willi Anna Xaver Zeppelin Gustav


Statt einer sortierten Liste spuckt der Rechner nur die Fehlermeldung »Speicherzugriffsfehler« aus (oder bei englischer Locale: »Segmentation fault«). Zur Ursachenforschung ist der Debugger mit dem Namen des Programms aufzurufen: »ddd ./slow_sort«. Abbildung 1 zeigt die drei Bereiche des DDD-Hauptfensters: Oben das Datenfenster (eingeschaltet per »View | Data Window«), in der Mitte das Fenster mit dem Quelltext und darunter die GDB-Konsole. Diese zeigt die Befehle, mit denen DDD den GDB steuert. Das Data-Window bleibt zunächst leer, hier zeigt DDD auf Wunsch die Werte von Variablen und Datenstrukturen an.

Um wie in Abbildung 1 die Werkzeug-Buttons ins Hauptfenster einzubetten statt sie als eigenständiges Fenster zu benutzen, wählt man im Menü »Edit | Preferences | Source« hinter »Tool Buttons Location« die Option »Source Window«. Hier versteckt sich auch die Option, die Zeilennummern im Quelltext einblendet: »Display Source Line Numbers«. Wer beim Start keinen Splash Screen und keinen Tip of the Day mag, schaltet beides unter »Startup« aus. Nach einem »Edit | Save Options« merkt sich DDD diese Einstellungen dauerhaft.

Abbildung 1: Das DDD-Hauptfenster teilt sich in Datenbereich (oben, noch leer), Quellcode (Mitte) und GDB-Konsole (unten). Beim Aufruf mit einer Namensliste bricht das Programm mit einem Segmentation Fault ab. Der Backtrace zeigt, wo im Quelltext der Fehler aufgetreten ist.

Programmstart

DDD hat das Programm noch nicht gestartet. Das erledigt entweder ein »run Parameter« in der GDB-Konsole oder der Menüpunkt »Program | Run...«. Bei Letzterem öffnet DDD ein Fenster, das die Aufrufparameter entgegennimmt (unter »Run with Arguments«). Beim Start mit einer Namensliste stürzt die Sortiersoftware dank eines Segmentation Fault prompt ab. Der GDB (siehe GDB-Konsole in Abbildung 1) informiert zusätzlich darüber, in welcher Funktion an welcher Adresse der Fehler aufgetreten ist.

Der Programmablauf ist innerhalb der Systemroutine »strcmp()« gescheitert. Diese Funktion ist Teil der Libc und vermutlich fehlerfrei - wahrscheinlich hat das Programm die Funktion falsch benutzt. Um das näher einzugrenzen, hilft die Aufrufreihenfolge, der so genannte Backtrace. Er ist über »Status | Backtrace« zu erreichen (Abbildung 1).

Das Ergebnis: Die »strcmp()«-Funktion wurde laut Eintrag »#1« im Backtrace vom Code in Zeile 11 (Listing 1) in der Sortierfunktion »slow_sort()« aufgerufen. Nach einem Klick auf den Eintrag wechselt die Darstellung im Quellcode-Fenster an die entsprechende Stelle.

Der grüne Pfeil vor dem »strcmp()«-Aufruf bestätigt, dass das Programm gerade an dieser Zeile war, bevor es abstürzte - der Pfeil markiert die gerade ausgeführte Funktion. Die beiden Tool-Buttons »Up« und »Down« bewegen den Cursor zur aufrufenden oder aufgerufenen Funktion, also nach oben oder unten im Call Stack.

Den nächsten Hinweis bei der Spurensuche geben die Parameter der »strcmp()«-Funktion selbst: Den ersten Parameter »arrIn[j]« im Quelltext-Fenster markieren und auf den Toolbar-Button »Print« klicken, dann wertet GDB den Ausdruck aus und präsentiert das Ergebnis in seiner Konsole. Es erscheint der unverdächtige Wert »$1 = 0x8049878 "Zeppelin"«. Beim zweiten »strcmp()«-Parameter »arrIn[j+1]« gibt GDB allerdings »$2 = 0x0« aus, der Inhalt dieses Array-Elements ist ein Null-Zeiger.

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Humpeln zur Diagnose

    Viele verdammen Debugger als Teufelszeug. Oft erweisen sie sich aber als letzte Rettung. Perl hat einen sogar eingebaut. Mit ihm beschäftigt sich dieser Snapshot - passend zum Schwerpunkt des Hefts.

  • Auf der Pirsch

    Wer Programme schreibt, baut ahnungslos oft auch gut versteckte Bugs ein. Die richtigen Tools und etwas Geduld vereinfachen das Debugging. Dieser Workshop erklärt die Grundlagen der Fehlersuche und vermittelt den Umgang mit dem prominentesten Vertreter der Gattung, dem GNU-Debugger GDB.

  • AMD stellt Code XL 2.0 unter Open-Source-Lizenz

    Drastische Änderungen am Entwicklungsmodell kündigt AMD für die neue Version 2.0 von Code XL an, einem GPU-Debugger und -Profiler.

  • Mozilla stellt HTML-Debugger vor

    Mit dem Tool Debugger.html will Mozilla den bisher in den Firefox Developer Tools zu findenden Debugger ablösen. Der neue Debugger ist als Webanwendung mit einem React-basierten Interface und dem Javascript-Container Redux realisiert.

  • Tooltipps

    Themen: Python interaktiv skripten, SSH-Sitzungen parallelisieren, Bash-Debugging leicht gemacht, Die nächste Pine-Generation, Fetchmail-Alternative für Soho-Netze, Terminal-Multiplexer

comments powered by Disqus

Ausgabe 01/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.