Hey, wo ist meine Exception hin?

Ich hatte kürzlich den Fall eines Programms, welches munter mit einem uninitialisierten Pointer hantierte und trotzdem keine Exception auslöste oder von Windows gestoppt wurde. Die tl;dr-Version des Resultats meiner darauf folgenden Abklärung: Wenn 32bit-Programme unter einem 64bit-Windows laufen, werden unter gewissen Umständen Fehler wie ungültige Zugriffe auf Speicher einfach verschluckt. Der Stack wird abgebaut und das Programm läuft weiter, unklar an welcher Stelle, in einem wahrscheinlich inkonsistenten Zustand. Es sieht so aus, als könne man im Moment nichts dagegen unternehmen.

Die lange Version meiner Geschichte:

In unseren „native“ d.h. C++-Programmen haben wir bei Windows mit Hilfe des WIN32-API-Aufrufs SetUnhandledExceptionFilter eine Prozedur installiert, welche sämtliche Laufzeitfehler melden und in einem Log auf Disk vermerken soll.

Beim eingangs erwähnten Fehler mit einem ungültigen Pointer wurde diese Prozedur allerdings nicht aufgerufen, und es passierte mit und ohne Debugger gleichermassen folgendes: Der Fehler wurde verschluckt und das Programm fuhr fort, jedoch an einer unklaren Stelle (Step Into und Step Over im Debugger versagten beide, so dass man nicht sah, wo), in einem ziemlich „wackeligen“ internen Zustand.

Nach einigem Googlen fand ich dann den folgenden Blog-Eintrag: The case of the disappearing OnLoad exception.

Dieser bestätigt: Wenn während der Ausführung eines 32bit-Programms auf einem 64bit-Windows die Prozeduraufrufe so geschachtelt sind, dass es „zwischendurch“ Prozeduren von Windows selbst hat, z.B. wenn man sich in einer eigenen WindowProc befindet, und dann ein Fehler passiert, wird dieser verschluckt.

Als Lösung wird beschrieben, man solle sein Programm per Manifest explizit als unterstützt Windows 7 markieren. Wie genau sich das Programm verhalten soll, wenn man das macht, wurde mir anhand der Ausführungen im Blog-Eintrag nicht ganz klar, aber auf jeden Fall werden Fehler offenbar nicht mehr einfach ignoriert.

Ok, hab‘ ich ausprobiert, eine solche Markierung ist ja nicht schwierig, wie Microsoft hier beschreibt. Resultat: Keine Änderung.

Ich vermute, der Grund dafür ist diese Geschichte: Application Manifest may be ignored in Windows 7 x64: Man kann zwar sagen, man unterstütze Windows 7, aber ein 64bit Windows 7 lässt das 32bit-Programm unter Umständen trotzdem nur als Windows Vista laufen, und da werden Fehler offenbar noch ignoriert.

Es gab zwar einmal einen Hotfix, um dieses Problem zu beheben, aber der will auf meinem Windows 7 SP1 nicht installieren, der Hotfix aus dem Jahr 2010 ist wohl nur für Windows 7 „pur“.

Mir scheint, hier verheddert man sich prächtig in einem Gestrüpp von Windows-Fehlern und -Unzulänglichkeiten; auf jeden Fall habe ich bis jetzt keine Verbesserung hingekriegt.

Advertisements
Veröffentlicht in Keine Kategorie. Schlagwörter: , , . Leave a Comment »

Ein Lob für das Visual-Studio-Debugger-Team

Vor einiger Zeit hatte ich eine Beta von Visual Studio 2012 ausprobiert und versucht, damit unsere C++-basierten Applikationen zu debuggen. Mit VS2005 bis VS2010 hatte das jahrelang prima geklappt. Mit VS2012 hingegen ging es hochkannt schief: Der Versuch, die Applikation zu starten, führte zu einem Total-Crash von VS2012 selbst, noch bevor die erste Zeile unseres Programm-Codes überhaupt zur Ausführung kam. Mehr oder weniger das selbe Phänomen bei einem Attach to process: Unser EXE war toxisch, bei der ersten Berührung damit fiel VS2012 tot um.

Als sich das auch mit der RTM-Version von VS2012 nicht geändert hatte, und weit und breit keine gute Idee zu sehen war, was der Grund sein könnte für das Problem, wurde ich langsam nervös und rang mich dazu durch, bei Microsoft Hilfe zu suchen, obwohl ich recht skeptisch war: Würde sich ein Weltkonzern wie Microsoft interessieren für ein Problem einer kleinen IT-Bude in der Schweiz?

Ich machte also ein Posting im Forum des Visual Studio Developer Center. Das Resultat war mittelprächtig: Ein Microsoft-Mitarbeiter, der da wohl so etwas wie „Dienst tut“, schaute sich die Sache an, war aber bald mit seinem Latein am Ende, und machte schliesslich den Vorschlag, ich solle direkt einen Bug-Report erstellen, auf connect.microsoft.com.

Das tat ich auch – den Bug Report find man hier – und wartete gespannt, wie es weitergehen würde.

Das Resultat übertraf meine Erwartungen deutlich: Nach nur etwa 2 Wochen hatte jemand im Debugger-Team den Grund für die Crashes gefunden, die Sache für die nächste Version von Visual Studio korrigiert, und erst noch eine Umgehungs-Lösung geliefert, wie es auch mit dem bestehenden VS2012 klappt.

Allein diese Umgehungslösung ist sehr interessant: Offenbar hat Microsoft, ohne das an die grosse Glocke zu hängen, wesentliche Teile der „debugging engine“ für die 2012er-Version von Visual Studio überarbeitet, und es war dieser neue Code, welcher an einer Seltsamkeit in den VERSIONINFO-Daten in unserem EXE gar keine Freude hatte. Es gibt einen Trick, wie man die „legacy debug engine“ aktivieren kann, den alten Code, welcher damit klarkommt und der noch da ist: Man schaltet die Option Enable Edit and Continue in den Debugging-Options ein:

Legacy Debugger

Legacy Debugger aktivieren

Das ganze war für mich ein voller Erfolg. Auch wenn es natürlich keinerlei Garantien auf Erfolg gibt, möchte ich doch jeden ermuntern, der ein ernstes Problem hat mit Visual Studio, den Weg über einen Bug-Report auch zu versuchen. Es kann sein, dass man erhört wird.

Veröffentlicht in Keine Kategorie. Schlagwörter: , . Leave a Comment »

Debugging von ASP.NET-MVC2-Applikationen

Irgendwie ging immer alles schief, wenn ich Fehler aufspüren wollte in der Applikation, die ich gerade mit ASP.NET MVC2 entwickle: Z.B. bei der Verwendung eines Objekts, das fehlerhafterweise null war, stoppte der Debugger nicht am Ort des Fehlers, sondern irgendwo im MVC2-Code, dort nämlich, wo ein „hoch oben“ angesiedelter Handler per catch die resultierende Exception abfing.

Um noch eins draufzusetzen, hatte Visual Studio zudem etwas am entsprechenden MVC2-Source-File mit dem catch drin zu meckern, das es vom Microsoft-Source-Code-Server heruntergeladen hatte, irgendetwas mit Some bytes have been replaced with the Unicode substitution character while loading file, so wie bei StackOverflow hier beschrieben, notabene ohne Tipps zur Abhilfe.

Es gibt in Visual Studio eine Einstellung, mit der der Debugger gleich stoppt, sobald irgendeine Exception auftritt, unabhängig davon, ob ein catch dafür zuständig ist oder nicht, so wie hier beschrieben, aber dieser Mechanismus hat auch so seine Probleme:

Mit Debugging-Option Enable Just My Code nicht gesetzt gibt’s viel zu viele Exceptions, weil in MVC2 vieles über Exceptions abgehandelt wird, die man dann alle erst „wegklicken“ muss, bis schliesslich die eigene Exception drankommt, um die es eigentlich geht. Und mit Option gesetzt klappt’s mit Exceptions innerhalb von MVC2 nicht, die auftreten, weil man z.B. einer Methode einen Parameter als null übergeben hat, der definiert sein muss.

Die Rettung war schliesslich folgendes Vorgehen:

Man kann dankenswerterweise den Source Code von ASP.NET MVC2 bei Microsoft CodePlex hier herunterladen, in einer Form, über die Visual Studio nichts zu meckern hat. Und es ist überraschend einfach, diesen Code quasi als „eigenen“ Code in sein Projekt einzubinden, so wie hier beschrieben. (Die Beschreibung ist zwar noch für MVC1, es klappt aber auch mit MVC2.)

Für die Zeit der Entwicklung meiner Applikation habe ich nun an zwei Orten in diesem MVC2-Source-Code catch-Anweisungen auskommentiert, so dass der Debugger die meisten auftretenden Exceptions als unhandled taxiert und gleich stoppt. (So komme ich jetzt ohne die erwähnte untaugliche Option aus, die den Debugger bei jeder Exception stoppen lässt.)

Auch sonst ist es natürlich ganz interessant, ein paar Blicke auf diesen Source-Code zu werfen, und man findet im Netz etliche Kommentare von Leuten, die schwierige Probleme in ihren Web-Applikationen nur dadurch lösen konnten, indem sie anhand dieser Sourcen im Debugger verfolgten, was innerhalb ASP.NET MVC2 genau passierte.

Veröffentlicht in Allgemein. Schlagwörter: , , . Leave a Comment »

Der Historical Debugger in VS2010

Schon vor einiger Zeit hat Microsoft angekündigt, dass Visual Studio 2010 einen neuen, speziellen Debugger namens Historical Debugger enthalten wird. Man findet z.B. hier eine Ankündigung aus dem Oktober 2008:

Are you tired of constantly setting breakpoints to hone in on a pesky bug? How would you like to be able to step „back in time“ through your debugger? The Historical Debugger in Visual Studio Team System 2010 promises to revolutionize your debugging experience.

Ich sagte mir, na das ist doch mal was Interessantes: Irgendwie rückwärts durch ein Programm debuggen? Weil ich für Revolutionen immer zu haben bin und als „Systemprogrammierer“ einfach wissen muss, wie man einen Debugger implementiert, der so etwas leistet, bin ich der Sache heute auf den Grund gegangen und kann nun berichten, worum es geht.

Zunächst musste ich eine Hürde aus dem Weg räumen: Der Historical Debugger ist tatsächlich nur im Visual Studio Team System 2010 verfügbar, das „normale“ Visual Studio ohne TS, das ich mir zuerst testweise installiert hatte, enthält ihn nicht. Man findet die VS2010-TS-Preview als ISO-Download hier.

Der Witz an der Sache ist der folgende: Der Historical Debugger schreibt selbständig zu bestimmten Zeitpunkten während der Programmausführung ein Log mit Informationen über den Zustand des Programms zum jeweiligen Zeitpunkt. Der angepriesene „Schritt zurück in der Zeit“ besteht darin, nach einem Stopp des Programms aus der Debug History, d.h. der Liste der Zeitpunkte, einen Eintrag auszuwählen und sich im Debugger den Zustand des Programms anzuschauen, wie er damals war.

Das kann natürlich – im Prinzip – ganz nützlich sein: Hat man z.B. einen Breakpoint erreicht und stellt fest, dass eine Variable einen falschen Wert aufweist, muss man nun nicht mehr mit dem Debugging von vorne beginnen und sich vorsichtig an den Zeitpunkt heranpirschen, wo der falsche Wert zum ersten Mal auftaucht, sondern kann bequem und entspannt die Debug History verwenden, um den Zeitpunkt zu finden oder mindestens stark einzugrenzen, wo die Sache aus dem Ruder läuft.

Man kann in 2 Stufen wählen, wie die Zeitpunkte bestimmt werden sollen: Log schreiben entweder bei „wichtigen Systemereignissen“ oder bei solchen Ereignissen plus bei jedem Methoden-Aufruf. Man kann hier erahnen, dass die Sache Potential hat auszuufern, denn Methoden-Aufrufe können ja sehr zahlreich sein.

Ins Log geschrieben werden der Call Stack zum Zeitpunkt und Variablen-Werte zum Zeitpunkt, wobei man bei Objekten nur noch das Resultat von ToString sieht, aber keinen Zugriff mehr bekommt auf die einzelnen Komponenten.

Ein paar Screenshots hierzu findet man im Blog Dariusz quatscht hier.

Meiner Meinung nach ist das ganz hübsch, aber ob es für eine Revolution reicht, ist fraglich. Insbesondere die doch sehr eingeschränkte historische Sicht auf Variablen-Werte enttäuscht. Mir ist schon klar, dass es schlicht unmöglich ist, zu Hunderten von Zeitpunkten den gesamten Heap ins Log zu schreiben, damit ich auch nachträglich noch Objekte mit all ihren Details anschauen kann, aber hey, es hätte ja sein können, dass jemand mit Hilfe einer genialen Idee ein Log implementiert hat, das viele Details mit praktikablem Aufwand speichern kann, z.B. mit einer Heap-Differenzen-Methode…

Was haben andere so gemacht in Sachen Debugger mit Historie? Hier ist von einem experimentellen Java-Debugger die Rede, der vor ein paar Jahren implementiert wurde und der offenbar alle Variablen-Zuweisungen während eines Programmlaufs protokolliert, indem er sich in die Pseudocode-Ausführung der JVM einklinkt. Er nennt sich denn auch treffend Omniscient Debugger („allwissender Debugger“) und erlaubt offenbar die volle Sicht zu einem beliebigen Zeitpunkt.

In einer PDF-Datei, welche den Debugger genauer beschreibt, findet sich die Information, dass Apache Ant etwa 24 Millionen Variablen-Zuweisungen macht, während das Tool seine eigene Kompilation steuert. Das sind eine ganze Menge, aber nicht so viele, dass sie den Ansatz an sich gleich zu Fall bringen.

VMWare hat etwas im Angebot, das die Firma Replay Debugging nennt, beschrieben auf der Website hier. Unterstützt wird Debugging für C/C++, wie man im Artikel hier nachlesen kann. Das ganze funktioniert mit Hilfe der Virtual Machine, wo man natürlich wie bei der JVM eine zentrale Stelle hat, wo man sich einklinken kann, um zu protokollieren, was läuft. Das Debugging, sprich der Blick auf das Log findet statt in einem Visual Studio, das um ein VMWare-Plugin ergänzt wurde.

Anhand der Beschreibung vermute ich, dass komplett protokolliert wird und man so in einzelnen Schritten vorwärts und sogar rückwärts durch das Programm gehen kann. Die Suche nach dem Punkt im Programm, wo eine Datenstruktur „zerstört“ wird, finde man so: Zeitpunkt finden, wo sie falsch ist, dann Programm „rückwärts laufen lassen“ mit Daten-Breakpoint auf der Struktur und einfach auf den Break durch Veränderung warten.

Hier meldet sich natürlich wieder meine Neugier, die mich veranlasst hat, überhaupt erst den Historical Debugger von Microsoft anzuschauen und die sich nun fragt: Wie macht VMWare das? Ich vermute, dass die komplette Abfolge aller CPU-Befehle, welche das Programm ausführte, der zentrale Bestandteil des Logs ist, und dass das „Debugging“ darin besteht, mit Hilfe der Virtual Machine das Programm irgendwie entlang dieser Abfolge zu führen, vielleicht gestützt auf Komplett-Speicherabzüge in gewissen Zeitabständen.

Und ich vermute ebenfalls, dass die Sache derart auf die Ausführung direkter CPU-Anweisungen zentriert ist, dass die Ausdehnung des Prinzips auf C# mit seinem JIT-Compiler eine schwierige bis unmögliche Sache werden dürfte.

Veröffentlicht in Keine Kategorie. Schlagwörter: , . 1 Comment »

DebuggerDisplay

Es ist ja eine schöne Sache, wie einfach man die Anzeige von Objekten im Debugger verbessern kann. Man muss nur wissen, dass der Debugger für diese Anzeige die ToString-Methoden der jeweiligen Objekte aufruft, und dann eben diese Methoden entsprechend programmieren.

Umso überraschter war ich deshalb, als dieses Vorgehen einfach nicht klappen wollte, als ich eine Ableitung der Klasse TextBox programmierte und mir im Debugger nicht den momentanen textuellen Inhalt des Controls wünschte, sondern seinen Namen, um besser verfolgen zu können, was wann mit welchem Control aus einer ganzen Gruppe von Controls passierte: Ein Überschreiben von ToString hatte keinerlei Effekt im Debugger.

Ich habe nicht herausgefunden, warum genau das so ist, aber jedenfalls kann man mit Hilfe des DebuggerDisplay-Attributs erreichen, dass auch bei einer TextBox der Debugger die Methode ToString verwendet:

[DebuggerDisplay("{ToString()}")]
public class MyTextBox : TextBox {
  ...
    public override string ToString() {
      return Name;
    }
}
Veröffentlicht in Keine Kategorie. Schlagwörter: , . Leave a Comment »