Archiv für die Kategorie ‘Hacks & stuff’

h1

Optimator: I’ll be back!

1. März 2009

Ich dachte mir, dass ich gerade meine C-Tracer lib optimiere, wäre nicht interessant genug, um darüber was zu schreiben. Dann dachte ich mir, dass ich schon länger nichts geschrieben hab. Und dann dachte ich, dass ich ja schreiben kann, dass ich erwägte, etwas zu schreiben.

Ja… so war das.

Wenn ich fertig optimiert habe (Haha, auch so’n Informatiker-Witz :D ) melde ich mich nochmal :) Dauert wahrscheinlich etwas, muss da noch diverse Größenordnungen an Performance rausholen…!

-Tim

h1

Victim und Watchdog Thread III

2. Januar 2009

Letztendlich funktioniert das System wie es soll *puh*!

Die Lösung mit eigenem Opfer-Prozess musste erst wieder einer reinen Thread-Lösung weichen (Watchdog-Thread startet als Opfer einfach einen weiteren pthread). Dies funktioniert wider meiner ersten Versuche doch, wenn man es mit geeigneten Signal-Handlern kombiniert.

Ich teste das System gerade eingehend, aber es sieht sehr vielversprechend aus!

Beweisfoto:

JEM Logviewer mit Daten aus dem ctracer

JEM Logviewer mit Daten aus dem ctracer

Durch den ctracer mit Victim/Watchdog-System ist es z.B. möglich, Membervariablen von Strukturen aufzulösen (Links unten im Bild zu sehen).

- Tim

h1

Victim und Watchdog Thread II

12. Dezember 2008

Sieht so aus, als würde ich doch noch ein Bißchen mehr “Spaß” mit meiner Victim/Watchdog-Lösung haben. Auf bestimmte, komplexere Applikationen angewendet, funktioniert das Ganze nämlich überhaupt nicht mehr so reibungslos. Und Debuggen kann ich’s auch nicht vernünftig, weil der gdb bei Multithreaded-Anwendungen gerne ein wenig rumzickt. Seufz. Ich poste wieder, wenn ich weitergekommen bin :)

h1

Victim und Watchdog Thread

5. Dezember 2008

Bei der Entwicklung der C/C++-trace Bibliothek, an der ich gerade arbeite, hatte ich ein kleines aber fieses Problem. Und zwar möchte ich nicht nur Funktionsaufrufe und -returns tracen, sondern auch die Argumente der Funktionen, lokale Variablen im caller- und callee-scope, usw. Und zwar inklusive Typ und Wert. Das funktioniert schon recht passabel. Ich kann Strukturen aufdröseln, Pointern folgen, die visibility und den const-Status der Symbole angeben, …

Stop.

Der geneigte Leser mag in diesem Augenblick bereits erfasst haben, was das fiese Problem an der Sache ist. Die Aussage “Ich kann Pointern folgen” sollte Alarmglocken jedes C-Programmierers schrillen lassen. Zur Erinnerung: Ich trace user-code! Was passiert, wenn ich versuche, uninitialisierten oder ungültigen Pointern zu folgen? …genau: Rumms.

Und das noch-schlimmere daran ist, dass dieses “Rumms” dann nicht nur meine trace-library killt, sondern sie auch noch die Applikation mitreißt, in die sie dynamisch gelinkt wurde – sprich, den User Algorithmus. Das ist… doof.

Also überlegte ich, wie ich das Problem lösen könnte. Es gibt ja leider kein “try / catch” für Segfaults. Nach einem Segfault ist das Programm in einem undefinierten Zustand (Auch, wenn der segfault nur lesend war. Hmpf!). Könnte man das Lesen des Wertes in einen eigenen Thread verlagern? Nein, wenn in einem Thread ein Segfault passiert, reißt es den Eltern-Thread mit sich. Bliebe ein eigener Prozess. Allerdings wäre es ein viel zu hoher Overhead, jedesmal einen Prozess zu starten, wenn man versuchen möchte, einem Pointer zu folgen. Aber man muss ihn ja nicht jedesmal dann starten…!

Meine derzeitige Lösung sieht wie folgt aus:

Zu Beginn, also wenn die Trace-Bibliothek geladen wird, wird in ein neuer Thread gestartet. Dieser forkt sofort einen Kindprozess, den “Victim” (Opfer). Heißt so, weil der arme Kerl höchstwahrscheinlich sehr bald sterben wird. Der Elternprozess (also der Thread der ctrace-library, im Folgenden “Watchdog”), der Victim sowie der ctrace-Hauptthread mappen dann ein Stück Shared Memory. Hier findet, von Mutexen geschützt, die Interprozesskommunikation statt. Der Victim fängt in einer Endlosschleife an, auf Arbeit zu warten, und der Watchdog fängt an, den Victim zu überwachen.

Komme ich in der libctrace an eine Stelle, wo ich einem (User-)Pointer folgen muss, übergebe ich die Adresse und Länge an den Victim. Dieser versucht, den Speicherinhalt in das Shared Memory zu kopieren. Klappt dies, setzt er ein Flag, der Hauptthread liest den Wert aus, und der Victim wartet auf den nächsten Job. Crasht der Victim allerdings beim Kopieren, startet der Watchdog einen neuen Victim-Prozess und meldet den Fehler an den Hauptthread.

Auf diese Weise tritt ein größerer Overhead nur genau dann auf, wenn ein Lesevorgang scheitert, da dann ein neuer Prozess geforked werden muss.

Vielleicht geht das Ganze noch eleganter, aber die Lösung funktioniert, und das auch sehr gut!

Bis zum nächsten Hack…

- Tim

Follow

Bekomme jeden neuen Artikel in deinen Posteingang.