© saicle/Shutterstock.com
Segmentation Faults erfolgreich debuggen

Der schlimmste aller Fehler ist ...


Auch Softwareentwickler machen Fehler. Wenn sich diese Fehler in einer PHP-Applikation verstecken und auch noch die eigenen sind, hat man beim Beheben quasi einen Heimvorteil. Befinden sich die Fehler in der eigenen Domäne, kann man sie mit Standardwerkzeugen ausbessern. Doch es gibt auch Fehler, die sich weder im eigenen Code noch in der vertrauten Programmiersprache befinden: die Segmentation Faults. Sie verhalten sich nicht wie herkömmliche Bugs und müssen anders behandelt werden. Welche Werkzeuge zur Behebung der Segmentation Faults zur Verfügung stehen, soll folgender Artikel zeigen.

Der Begriff „Segmentation Fault“ ist heute als Bezeichnung für eine Speicherschutzverletzung nicht mehr korrekt, da die meisten Betriebssysteme Paging [1] zur Speicherverwaltung einsetzen. Trotzdem hat sich dieser Begriff erhalten. Eine Speicherschutzverletzung eines Programms liegt dann vor, wenn es versucht, auf eine Speicherregion schreibend zuzugreifen, die entweder schreibgeschützt oder von einem anderen Programm reserviert ist. Weitere häufige Auslöser einer Schutzverletzung sind folgende:

  • Nutzung von uninitialisierten Pointern, die auf keine feste Adresse zeigen

  • Nutzung von Pointern, die auf schon freigegebene Speicherbereiche verweisen

  • ein Hardwaredefekt

Der Kernel löst bei einer Speicherschutzverletzung durch ein Programm ein SIGSEGV-Signal [2] aus und teilt so dem aufrufenden Programm mit, dass es eine Schutzverletzung begangen hat. Das Programm reagiert sofort mit einem Ausführungsstopp und schreibt vorher, falls konfiguriert, noch ein Abbild seines Speicherzustands in einem speziellen Format in ein vorher konfiguriertes Verzeichnis. Dieses Abbild ist der Core Dump [3], der den Speicherzustand des Programms zum Zeitpunkt des Ausführungsstopps beinhaltet.

Bei PHP-Programmen beendet der Interpreter sofort die Programmausführung und zeigt eine Nachricht im konfigurierten Error Log oder an der Kommandozeile an. Vom PHP-Interpreter ausgelöste Segmentation Faults können nicht direkt von einem PHP-Skript stammen. In einer Skriptsprache wie PHP beschreibt das Skript dem Interpreter, welche Funktion er auf der unterliegenden Ebene (bei PHP C-Code) ausführen soll. Die hinter der Beschreibung liegenden Anweisungen werden dann im C-Code auf der Maschine ausgeführt. Ein Segmentation Fault wird daher immer von einer PHP Extension oder dem PHP Core in den unterliegenden C-Funktionen ausgelöst. PHP-Code kann also nicht eigenständig den Segmentation Fault erzeugen. Daher kann ein Fehler auch nicht direkt mit PHP-Werkzeugen gefunden, allerdings auf der PHP-Skript-Ebene unter Nutzung verschiedener Techniken eingegrenzt und gegebenenfalls über Work­arounds vermieden werden. Die Fehlerbehebung ist dann aber nur innerhalb der C-Funktionen möglich. Das bedeutet, dass PHP oder seine Extension neu kompiliert werden muss.

Listing 1: Quelltext von ­„reproduceSegFault.php“

<?php class Test implements Serializable { public $member, $message; function __construct($message) { $this->message = $message; } function serialize() { return serialize(array($this->message, $this->member)); } function unserialize($serialized) { list($this->message, $this->member) = unserialize($serialized); } } $constructed = new Test("original"); $constructed->member = $constructed; //References to self (in this example pointless, but technically legal) var_dump($constructed); $transported = unserialize(serialize($constructed)); // Segmentation fault. 

Wie zeigt sich ein Segmentation Fault?

Der Segmentation Fault führt immer zu einem sofortigen Abbruch der Programmausführung. Wenn PHP nur auf der Kommandozeile ausgeführt wird, ist sehr einfach zu erkennen, dass ein Segmentation Fault vorliegt. Der Interpreter meldet diesen Fehler einfach über stderr an die Konsole, in der der Segmentation Fault geschrieben wird.

Etwas schwieriger ist es, mit PHP-Skripten, die über einen Webserver ausgeführt werden, einen Segmenta­tion Fault zu erkennen. Es muss unterschieden werden, ob PHP direkt als Modul des Webservers (z. B. mod_php) oder als Fast-CGI (php-cgi in Debian Squeeze) ausgeführt wird. Bei Verwendung von Nginx und php-cgi und der Anfrage nach reproduceSegFault.php wird einfach eine HTTP-Fehlermeldung ausgegeben: 502 Bad Gateway (Listing 1, der Quelltext ist bei bugs.php.net zu finden [3] und ermöglicht in der Version PHP 5.3.3 das Produzieren eines Segmentation Faults). Dieser Aufruf zeigt, dass der Interpreter die Verarbeitung gestoppt hat und der Nginx-Webserver nicht mehr mit der php-cgi-Instanz kommunizieren kann.

Der Aufruf des Skripts über das Apache-mod_php-Modul führt zur Ausgabe des Vardumps (Listing 2, ausgeführt mit PHP an der Kommandozeile). Da kein Output Buffering (Ausgabezwischenspeicherung) genutzt wird, wird die Ausgabe noch an den Browser gesendet, bevor das Skript mit dem Segmentation Fault abbricht. Man kann dieses Verhalten einfach beobachten, indem man vor und nach der mit „Segmentation Fault“ kommentierten Zeile eine Ausgabe einfügt:

<?php .... echo "Hallo Welt 1 !"; $transported = unserialize(serialize($constructed)); // Segmentation fault. echo "Hallo Welt 2 !";

Listing 2: Ausgabe von ­„reproduceSegFault.php“

object(Test)#1 (2) { ["member"]=> *RECURSION* ["message"]=> string(8) "original" } Segmentation fault

Bei der Ausführung über mod_php wird Hallo Welt 1 ! noch ausgegeben, Hallo Welt 2 ! aber nicht mehr, da vorher schon die Programmausführung mit einem Segmentation Fault abgebrochen wurde. Bei der Verwendung des Output Bufferings ist die leere oder „weiße“ Seite als Antwort auf...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang