Open Source im professionellen Einsatz
Linux-Magazin 04/2005

Der Perl-Debugger kommt versteckten Fehlern auf die Schliche

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.

939

Linus Torvalds mag keine Debugger und auch keine Programmierer, die sie verwenden. Zu leichtfertig lässt sich nach seiner Ansicht ein Stück Code zusammenschustern und mit einem Debugger geradebiegen. Allerdings macht sich kaum ein Chaosprogrammierer Design-Gedanken. Das rächt sich später oft, weil die Software schwer wart- oder erweiterbar ist. Gewissenhaft eingebettetes Logging macht aber Debugger in vielen Fällen überflüssig. Wie[3] erläutert, hilft Log::Log4perl dabei, einen maßgeschneiderten Debugger in die Applikation einzubauen und per Fernsteuerung zu aktivieren.

Probelauf

Manchmal nützt aber alles nichts. Was tun, wenn ein Programmstück unerwartet reagiert, die Dokumentation darüber nichts enthält und der (natürlich von anderen geschriebene) Code zu kompliziert ist, um seinen Ablauf durch Studieren der Listings zu verstehen? Perl enthält einen Debugger, der Fehler recht schnell mit Breakpoints, Actions und Watchpoints einkreist. Listing 1 zeigt den praktischen Fünfzeiler »wsrv«, der anzeigt, was für ein Webserver hinter einer URL steckt. Der Aufruf »wsrv http://sun.com« etwa gibt preis, dass Sun auf eigene Technik setzt »Sun Java System Web Server 6.1«.

Listing 1:
»wsrv«

01 #!/usr/bin/perl -w
02 ###########################################
03 # wsrv - Display a URL's web server
04 ###########################################
05 use LWP::Simple;
06 my $url = shift or die "usage $0 url";
07 my (@fields) = head($url) or
08      die "Fetch failed";
09 print "Server: $fields[4]n";

Soll das Skript stattdessen im Debugger laufen, setzt man einfach ein »perl -d« vor den vollständigen Skriptpfad und alle Kommandozeilenargumente, also »perl -d wsrv http://microsoft.com«. Das führt zu folgender Ausgabe:

Loading DB routines from perl5db.pl version 1.27
Editor support available.
Enter h or `h h' for help or `man perldebug' for more help.
main::(wsrv:7): my $url = shift or die "usage $0 url";


Das Debugger-Kommando »n« (Next) führt die erste Zeile des Skripts aus - sie ist auch am Ende der obigen Ausgabe zu sehen. Diese Zeile extrahiert die URL aus dem Argumenten-Array »@ARGV«:

DB<1> n
main::(wsrv:8): my (@fields) = head($url)or
main::(wsrv:9): die "Fetch failed";


Perl hat die erste Zeile des Programms ausgeführt, die auf der Kommandozeile angegebene URL extrahiert und sie in der Variablen »$url« abgelegt. Die nächste ausführbare Anweisung besteht aus den Zeilen 8 und 9 von »wsrv«. Statt sie mit »n« vollständig auszuführen, geht das Kommando »s« (Step) in Einzelschritten vor. Prompt steigt der Debugger in die in Zeile 9 aufgerufene Funktion »head« hinab:<c>@15 Li:

LWP::Simple::head(.../LWP/Simple.pm:70):

70: my($url) = @_;

Das Kommando »l« (List) verschafft einen Überblick über die nächsten Zeilen:

70==> my($url) = @_;
71:   _init_ua() unless $ua;
72
73:   my $request = HTTP::Request->new(HEAD => $url);
74:   my $response = $ua->request($request);
[...]


Um im Code weiter nach unten zu fahren, ohne ihn auszuführen, genügt ein weiteres »l«-Kommando. Alternativ auch »l 70+20« (20 Zeilen ab Zeile 70) oder »l 70-100« (Zeilen 70 bis 100). Die nächste ausführbare Zeile zeigt »==>« an.

Zurück zum Anfang

Durch die Eingabe eines Punkts kehrt die Listinganzeige wieder zum Ausgangspunkt zurück. Die Eingabe von »r« (Return) weist den Debugger dazu an, die aktuelle Funktion bis zum Ende auszuführen und anschließend sofort im Hauptprogramm anzuhalten.

list context return from LWP::Simple::head:
0  'text/html'
1  16144
2  1107018115
3  1107028115
4  'Microsoft-IIS/6.0'
main::(wsrv:9): print "Server: $fields[4]n";


Freundlicherweise zeigt der Debugger sogar die Rückgabewerte der Funktion »head()« an, unmittelbar vor der nächsten ausführbaren Zeile, der »print()«-Funktion im Hauptprogramm. Interessiert der Wert des Array-Elements »$fields[4]«, fördert ihn der Befehl »p« (Print) des Debuggers zutage, noch bevor die »print()«-Zeile des Hauptprogramms ihn preisgibt. Auf »p $fields[4]« folgt »Microsoft-IIS/6.0«

Um den Inhalt des Arrays »@fields« auszugeben, eignet sich »p @fields«, allerdings wäre die Ausgabe nicht gerade augenfreundlich. Für kompliziertere Datenstrukturen bietet der Debugger daher die Funktion »x«:

DB<2> x @fields
0  'text/html'
1  16144
2  1107021419
3  1107031419
4  'Microsoft-IIS/6.0'


Ähnliches gilt für Hashes, die man sogar direkt im Debugger definieren kann:

DB<3> %h = (donald => 'duck',
            klaas  => 'klever')
DB<4> x %h
0  'donald'
1  'duck'
2  'klaas'
3  'klever'


Wer statt der Array-artigen Anzeige lieber Key-Value-Paare möchte, übergibt stattdessen eine Hash-Referenz an »x«:

DB<5> x %h
0  HASH(0x837a5f8)
 'donald' => 'duck'
 'klass'  => 'klever'


Inzwischen hat sich die Nummer im Prompt erhöht. Sie taucht auch in der History-Liste auf, die »H« ausgibt:

DB<6> H
5: x %h
4: x %h
3: %h=(donald=>'duck',klaas=>'klever')
2: x @fields
1: p $fields[4]


Um zum Beispiel das »$field[4]«-Element nochmals auszugeben, genügt ein Ausrufezeichen gefolgt von der Nummer des History-Eintrags »!1«. Mit diesem kleinen Rüstsatz an Befehlen lassen sich nun schon kompliziertere Aufgaben anpacken.

Tabelle
1: Dynamische Navigation

 

Kommando

Bedeutung

Programmausführung steuern

 

n

Nächste Zeile ausführen, danach anhalten

s

Nächste Zeile starten, in Unterfunktion anhalten

r

Aktuelle Funktion fertig durchlaufen, dann stopp

R

Zurück zum Start und noch einmal ausführen

Variablen anzeigen

 

p

Wert ausgeben

x

Dump (x %hash)

Source-Navigation

 

l

Vorwärts blättern

-

Rückwärts blättern

v

Code um aktuelle Zeile herum zeigen

.

Zurück zur aktuellen Zeile

f

In eine andere Source-Datei wechseln

Erweiterte dynamische Navigation

 

c Zeile

Code bis zu dieser Zeile ausführen, dann stoppen

c Funktion

Code bis zur Funktion ausführen, in ihr anhalten

b Zeile

Breakpoint in Zeile setzen

b Funktion

Breakpoint in Funktion setzen

b Zei/Fu

Breakpoint mit Bedingung Bedingung

 

a Zei/Fu Aktion

Actionpoint in Zeile/Funktion

w Zei/Fu

Watchpoint in Zeile/Funktion Variable

 

< Command

Pre-Prompt setzen

L

Breakpoints, Watchpoints, Actions anzeigen

B/A/W

Breakpoints, Watchpoints, Actions löschen

Linux-Magazin kaufen

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

Deutschland

Ähnliche Artikel

  • Getriebeschaden

    Dreht sich das Perl-Rad nur noch knirschend, sind vielleicht Fehler im Perl-Interpreter oder in Erweiterungsmodulen schuld. Der gute alte GDB inspiziert das Getriebe und kommt Problemstellen auf die Schliche.

  • 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.

  • Neues Perl Major Release

    Nach einem Jahr Entwicklungszeit ist jetzt das nächste Major Release von Perl 5 erscheinen. Perl 5.16 enthält im Verleich zum Vorgänger 5.14 rund 590.000 Änderungen in 2500 Files, die 139 Autoren beisteuerten.

  • 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.

  • Epic

    Weil Perl-Kundige eher Kommandozeilen-Spartaner sind, kann man integrierte Entwicklungsumgebungen für diese Sprache an einer Hand abzählen. Eine davon, Epic, bringt Licht in Eclipse, die Finsternis.

comments powered by Disqus

Ausgabe 07/2017

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

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