© pixelquelle.de
Mit dem Gnu-Debugger auf Inspektion in Perls Mechanik
Getriebeschaden
von Michael Schilli
Erschienen im Linux-Magazin
2007/01
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.
Wer in Perl statt in C oder C++ programmiert, nimmt es oft als selbstverständlich hin, dass ihm viel unnütze Arbeit erspart bleibt: Speicher reservieren und freigeben, Referenzen zählen, auf wild gewordene Pointer aufpassen - derlei Sisyphusarbeit hält Perls virtuelle Maschine vom Programmierer fern, damit der sich auf das Implementieren konzentrieren kann.
Doch auch tief unten im Maschinenraum können Fehler auftreten. Es kommt zwar sehr selten vor, dass ein Bug in einer Perl-Release die in C implementierte virtuelle Maschine zum Absturz bringt. Häufiger aber können handgeschriebene Perl-Erweiterungen eines unachtsamen C/C++-Entwicklers dafür eine mögliche Ursache sein.
Nur in Ausnahmefällen stürzt der Perl-Interpreter »perl« so richtig ab, wenn es aber doch passiert, hilft auch der Perl-Debugger [2] nicht mehr weiter. Das Skript in Listing 1 führt zum Beispiel mit Hilfe einer C-Erweiterung bewusst einen Absturz des Interpreters mit einem Segmentation Fault herbei.
01 #!/usr/bin/perl -w
02 use strict;
03 use Inline "C";
04 use Inline Config =>
05 CLEAN_AFTER_BUILD => 0;
06
07 c_crash(43);
08
09 __END__
10 __C__
11 int c_crash( int num ) {
12 char *cp = 0xcba00000;
13 strcpy(cp, "Ouch!");
14 }
|
Linux zieht den Teppich weg
Das Beispiel bedient sich hierzu des CPAN-Moduls Inline, das angehängten C-Code kompiliert und dynamisch in das Skript einbindet. Der C-Code nach der »__END__«-Markierung setzt einen Pointer auf die Adresse »0xcba00000« und lässt dann die C-Funktion »strcpy« rücksichtslos an dieser, zumindest in der 32-Bit-x86-Architektur geschützten Kerneladresse schreiben. Der Prozessor merkt das, löst einen Interrupt aus und der Linux-Kernel zieht daraufhin dem schuldigen Programm den Teppich unter den Füßen weg.
Abbildung 1 zeigt, wie das Perl-Skript für die Reproduktion des Fehlers im Gnu-Debugger »gdb« aufzurufen ist. Das ausführende Binärprogramm ist der Perl-Interpreter, also startet der Debugger mit »gdb perl«. Um dann das Perl-Skript »crash« vom Interpreter abarbeiten zu lassen, ruft man im Debugger das Kommando »run crash« auf. Nach dem Absturz liefert »gdb« nicht nur den C-Code jener Zeile, die den Crash auslöste. Das Debugger-Kommando »bt« (für Backtrace, alternativ funktioniert auch »where«) zeigt zusätzlich die aufrufende C-Funktionshierarchie im so genannten Stacktrace an.

|
Abbildung 1: Eine »gdb«-Session analysiert den Stacktrace des Perl-Skripts.
|
Damit der Debugger ausgeführte Funktionen den Zeilennummern im C-Sourcecode zuordnen kann, muss der Fehlersucher sein Perl zuvor mit dem Compiler-Flag »-g« kompiliert haben. Auf die Frage des Konfigurationsskripts: »What optimizer/debugger flag should be used?« sollte er dazu »-g« antworten oder das Skript gleich mit »./Configure -D optimize=-g -d« aufrufen.
Versäumt er dies, ist die Analyse mangels Referenzen zum C-Sourcecode schwieriger, und wenn das Executable auch noch gestrippt ist, sieht es ganz düster aus, denn disassemblierten Assemblercode verstehen bleibt langbärtigen Gurus vorbehalten. Aber auch aus einem normal kompilierten »perl« lassen sich Informationen herausholen. Die Autopsie gestaltet sich dann zwar schwieriger, doch ein Trick, dessen Erklärung weiter unten folgt, hilft dabei, exzessives Jonglieren mit Hex-Zahlen zu vermeiden.
Obduktion der Skriptleiche
Kommt es in einem laufenden Programm zum Crash, erzeugt der Linux-Kernel normalerweise eine Core-Datei. Ist dies nicht der Fall, unterdrückt die Bash wahrscheinlich die Core-Produktion mit der Standardeinstellung »ulimit -c 0«. Lautet die Einstellung hingegen »ulimit -c unlimited«, dann entsteht eine Core-Datei (»core« oder auch »core.PID« mit angehängter Prozess-ID):
$ ./crash
Segmentation fault (core dumped)
$ ls -l core.*
-rw------- 1 mschilli mschilli 1658880 Nov 3 21:30 core.1234
Der so genannte Core-Dump liegt normalerweise in dem aufrufenden Verzeichnis, es sei denn, in »/proc/sys/kernel/core_pattern« ist etwas anderes definiert. Wer herausfinden will, was den Crash ausgelöst hat, ruft den Debugger post mortem mit dem ausführenden Programm und dem Core-File auf (beispielsweise »gdb perl core.1234«). Er erhält eine ähnliche Debugger-Session, wie sie in Abbildung 1 zu sehen ist. Aus ihr lässt sich ebenfalls der Stacktrace kurz vor dem Absturz ermitteln. Zu starten ist ein solcher Speicher-Schnappschuss allerdings nicht mehr.
| Whitepaper |
|
The Role of Open Source in Data Integration
Obwohl in den letzten Jahren viele technische Fortschritte erzielt werden konnten, verfügen die meisten Datenintegrationsprozesse nach wie vor nur über eine sehr begrenzte Automatisierung. Das vorliegende White Paper von dem Industry Analyst Mark Madson wird zunächst ein grundlegendes Verständnis von Daten Integration vermitteln, die Vorzüge von Open Source Lösungen für Daten Integration erläutern und Ihnen professionelle Empfehlungen geben, damit Sie Ihre Integrationsjobs noch einfacher und produktiver gestalten können.
Download PDF (Registrierung erforderlich)
|
|
Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele (Folge 2)
Der zweite Teil des Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele White Papers beleuchtet anhand weiterer ausgewählter Case Studies die Implementierung von Open Source Datenintegration in der Praxis und benennt die daraus resultierenden Vorteile.
Download PDF (Registrierung erforderlich)
|
Dieser Online-Artikel kann Links enthalten, die auf nicht mehr vorhandene Seiten verweisen. Wir ändern solche "broken links"
nur in wenigen Ausnahmefällen. Der Online-Artikel soll möglichst unverändert der gedrucken Fassung entsprechen.
|