Freigeben über


Debuggen von API-Funktionen

Aktualisiert: November 2007

Dieses Thema bietet eine Übersicht über die Funktionalität, die die Common Language Runtime (CLR)-Debugdienste bereitstellen. Es enthält die folgenden Unterabschnitte:

Anfügen an ein Programm oder Starten eines Programms

Steuern der Programmausführung

Untersuchen des Programmzustands

Ändern des Programmzustands

Verwenden von Bearbeiten und Fortfahren

Auswerten von Funktionen

Dynamisches Einfügen von Code

Anfügen an ein Programm oder Starten eines Programms

Die CLR ermöglicht Ihnen, den Debugger an ein laufendes Programm anzufügen oder einen Prozess zu starten. Die CLR-Debugdienste unterstützen ein Just-In-Time (JIT)-Debuggen, indem sie Ihnen das Anfügen des Debuggers an ein Programm ermöglichen, das eine nicht behandelte Ausnahme auslöst. Für ein Programm, das nicht im debugfähigen Modus ausgeführt wird, sind jedoch möglicherweise weniger Debuginformationen verfügbar. Ein Programm kann sich immer selbst im debugfähigen Modus ausführen, um dieses Problem zu vermeiden. Weitere Informationen über den debugfähigen Modus finden Sie in folgenden Themen:

Steuern der Programmausführung

Die CLR-Debugdienste bieten mehrere Möglichkeiten, die Ausführung eines Programms zu steuern. Dazu gehören Haltepunkte, Einzelschrittausführung, Ausnahmebenachrichtigung, Funktionsauswertung und andere Ereignisse in Verbindung mit dem Starten und Beenden eines Programms.

Die CLR-Debug-API stellt nur für verwalteten Code eine Ablaufsteuerung bereit. Wenn Sie die Ablaufsteuerung für nicht verwalteten Code ausführen möchten, müssen Sie diese Funktion gesondert im Debugger implementieren.

Haltepunkte

Sie können Haltepunkte erstellen, indem Sie den Code und die Microsoft Intermediate Language (MSIL) oder den systemeigenen Offset der Stelle angeben, an der die Unterbrechung stattfinden soll. Der Debugger wird dann benachrichtigt, wenn der Haltepunkt erreicht wird. Die Debug-API bietet keine direkte Unterstützung für bedingte Haltepunkte. Ein Debugger kann solche Haltepunkte implementieren, indem er einen Ausdruck als Reaktion auf einen Haltepunkt auswertet und entscheidet, ob der Benutzer über die Unterbrechung informiert werden muss.

Schrittweises Ausführen

Die CLR-Debugdienste stellen eine Vielzahl von Möglichkeiten zur schrittweisen Ausführung bereit. Ein Programm kann einen Code Anweisung für Anweisung (Einzelschrittausführung) oder Anweisungsbereich für Anweisungsbereich (Bereichsschrittausführung) durchlaufen. Das Programm kann eine Funktion überspringen, schrittweise ausführen oder die schrittweise Ausführung der Funktion beenden. Die CLR-Debugdienste können auch den Debugger benachrichtigen, wenn eine Ausnahme eintritt, die den Prozess der schrittweisen Ausführung unterbricht.

Auch wenn die Debugdienste die schrittweise Ausführung von nicht verwaltetem Code nicht direkt unterstützen, liefern sie Rückrufe, wenn ein Schrittvorgang auf nicht verwalteten Code trifft, um die Steuerung an den Debugger zu übergeben. Sie stellen auch Funktionen bereit, mit denen der Debugger ermitteln kann, wann von nicht verwaltetem Code aus in verwalteten Code übergegangen wird.

Die CLR stellt nicht direkt ein schrittweises Ausführen auf Quellcodeebene bereit. Ein Debugger kann diese Funktionalität bereitstellen, indem er die Bereichsschrittausführung zusammen mit seinen eigenen Quellzuordnungsinformationen verwendet. Sie können die Symbolspeicherschnittstellen verwenden, um Informationen auf Quellcodeebene zu erhalten. Weitere Informationen zu diesen Schnittstellen finden Sie unter Diagnosesymbolspeicher (Referenz zur nicht verwalteten API).

Ausnahmen

Die CLR-Debugdienste ermöglichen einem Debugger, über Ausnahmen der ersten Chance und der zweiten Chance in verwaltetem Code informiert zu werden. Das ausgelöste Objekt ist für die Überprüfung an jedem Punkt verfügbar.

Die CLR behandelt keine systemeigenen Ausnahmen von nicht verwaltetem Code, außer wenn sie aufwärts zu verwaltetem Code weitergeleitet werden. Sie können jedoch weiterhin die Win32-Debugdienste verwenden, die gemeinsam mit den CRL-Debugdiensten verwendet werden, um nicht verwaltete Ausnahmen zu behandeln.

Programmereignisse

Die CLR-Debugdienste benachrichtigen einen Debugger, wenn viele Programmereignisse auftreten. Zu diesen Ereignissen gehören Prozesserstellung und Prozessende, Threaderstellung und Threadende, Anwendungsdomänenerstellung und -beendung, Laden und Entladen von Assemblys, Laden und Entladen von Modulen sowie Laden und Entladen von Klassen. Um eine gute Leistung zu garantieren, können Sie das Laden von Klassen und das Entladen von Ereignissen für ein Modul deaktivieren. Standardmäßig sind die Ereignisse für das Laden und Entladen von Klassen deaktiviert.

Threadsteuerung

Die CLR-Debugdienste stellen Schnittstellen für das Unterbrechen und Fortsetzen von einzelnen (verwalteten) Threads bereit.

Untersuchen des Programmzustands

Die CLR-Debugdienste stellen eine detaillierte Möglichkeit zum Untersuchen der Teile eines Prozesses bereit, die in verwaltetem Code ausgeführt werden, wenn der Prozess im angehaltenen Zustand ist. Ein Prozess kann überprüft werden, um eine Liste physischer Threads zu erhalten.

Ein Thread kann untersucht werden, um seine Aufrufliste zu überprüfen. Die Aufrufliste eines Threads kann auf zwei Ebenen aufgelöst werden: auf Kettenebene und auf Stapelrahmenebene. Die Aufrufliste wird zunächst in Ketten untergliedert. Eine Kette ist ein fortlaufendes logisches Aufruflistensegment, das vollkommen verwaltete oder nicht verwaltete Stapelrahmen enthält. Außerdem nutzen alle verwalteten Aufrufframes in einer einzelnen Kettenfreigabe denselben CLR-Kontext. Eine Kette kann entweder verwaltet oder nicht verwaltet sein.

Jede verwaltete Kette kann darüber hinaus in einzelne Stapelrahmen untergliedert sein. Jeder Stapelrahmen stellt einen Methodenaufruf dar. Sie können einen Stapelrahmen abfragen, um den Code zu erhalten, den er ausführt, oder um seine Argumente, lokalen Variablen und systemeigenen Register zu erhalten.

Eine nicht verwaltete Kette enthält keine Stapelrahmen. Stattdessen enthält sie den Bereich von Stapeladressen, die nicht verwaltetem Code zugeteilt sind. Ein Debugger für nicht verwalteten Code muss eingesetzt werden, um den nicht verwalteten Teil des Stapels zu decodieren und eine Stapelüberwachung zu ermöglichen.

Tipp

Die CLR-Debugdienste unterstützen das Konzept der lokalen Variablen nicht, da sie im Quellcode vorhanden sind. Der Debugger ist zuständig dafür, lokale Variablen ihren Reservierungen zuzuordnen.

Die CLR-Debugdienste stellen auch einen Zugriff auf globale, klassenstatische und lokale Threadvariablen bereit.

Ändern des Programmzustands

Die CLR-Debugdienste ermöglichen einem Debugger, die physische Position des Anweisungszeigers während der Ausführung zu ändern, auch wenn ein solches Vorgehen nicht ohne Risiko ist. Der Anweisungszeiger kann erfolgreich geändert werden, wenn die folgenden Bedingungen den Wert true haben:

  • Der aktuelle Anweisungszeiger und der Zielanweisungszeiger sind beide an Sequenzpunkten. Sequenzpunkte stellen näherungsweise Anweisungsbegrenzungen dar.

  • Der Zielanweisungszeiger befindet sich nicht in einem Ausnahmefilter, einem catch-Block oder einem finally-Block.

  • Wenn sich der Zielanweisungszeiger in einem catch-Block befindet, darf er nicht außerhalb des catch liegen.

  • Der Zielanweisungszeiger ist im gleichen Frame wie der aktuelle Anweisungszeiger.

Wenn sich die physische Position des Anweisungszeigers ändert, werden die Variablen an der aktuellen Anweisungszeigerposition den Variablen der Zielposition des Anweisungszeigers zugeordnet. Garbage Collection-Verweise auf die Position des Zielanweisungszeigers werden ordnungsgemäß initialisiert.

Nachdem der Anweisungszeiger geändert wurde, markieren die CLR-Debugdienste alle zwischengespeicherten Stapelinformationen als ungültig und aktualisieren die Informationen, sobald diese das nächste Mal benötigt werden. Debugger, die Zeiger auf Stapelinformationen, z. B. Frames und Ketten, zwischenspeichern, sollten diese Informationen nach dem Ändern des Anweisungszeigers aktualisieren.

Der Debugger kann auch die Daten eines Programms ändern, wenn das Programm beendet wird. Der Debugger kann die lokalen Variablen und Argumente einer Funktion ändern, wenn die Funktion ausgeführt wird, ähnlich wie bei einer Überprüfung. Der Debugger kann auch Felder von Arrays und Objekten aktualisieren, ebenso wie statische Felder und globale Variablen.

Verwenden von Bearbeiten und Fortfahren

Bearbeiten und Fortfahren ist ein Feature, das es ermöglicht, während einer Debugsitzung Quellcode zu bearbeiten, den geänderten Quellcode neu zu kompilieren und die Debugsitzung fortzusetzen, ohne die ausführbare Datei von Anfang an erneut auszuführen. Aus funktionaler Perspektive ermöglicht Bearbeiten und Fortsetzen, Code während der Ausführung im Debugger zu ändern und dabei den Rest des Laufzeitzustands der ausführbaren Datei beizubehalten, die gedebuggt wird.

Auswerten von Funktionen

Um Benutzerausdrücke und dynamische Eigenschaften von Objekten auszuwerten, benötigt ein Debugger eine Möglichkeit, den Code des Prozesses auszuführen, der gedebuggt wird. Die CLR-Debugdienste ermöglichen dem Debugger, einen Funktions- oder Methodenaufruf zu machen und diesen im Prozess auszuführen, der gedebuggt werden soll.

Die CLR sorgt dafür, dass der Debugger einen solchen Vorgang abbricht, wenn er gefährlich sein kann (z. B. wenn dadurch ein Deadlock mit vorhandenem Code ausgelöst werden kann). Nach erfolgreichem Abbrechen der Auswertung wird der Thread so behandelt, als hätte keinerlei Auswertung stattgefunden, mit Ausnahme möglicher Nebeneffekte auf lokale Variablen durch die teilweise Auswertung. Wenn die Funktion nicht verwalteten Code aufruft oder in irgendeiner Weise blockiert, kann das Beenden der Auswertung unmöglich werden.

Nachdem die Funktionsauswertung abgeschlossen ist, verwendet die CLR einen Rückruf, um den Debugger zu benachrichtigen, ob die Auswertung normal abgeschlossen wurde oder die Funktion eine Ausnahme ausgelöst hat. Sie können die ICorDebugValue-Methode und die ICorDebugValue2-Methode verwenden, um die Ergebnisse einer Auswertung zu überprüfen.

Der Thread, für den die Funktionsauswertung ausgeführt werden soll, muss im verwalteten Code beendet werden, und zwar an einem Punkt, der für die Garbage Collection sicher ist. (Die Funktionsauswertung ist auch für nicht behandelte Ausnahmen zulässig.) In nicht optimiertem Code sind diese sicheren Punkte sehr häufig vorhanden. Die meisten schrittweisen Vorgänge auf Haltepunkt- oder MSIL-Ebene werden bei einem dieser Punkte abgeschlossen. Diese Punkte können jedoch in optimiertem Code selten sein. Manchmal verfügt eine ganze Funktion möglicherweise über keine sicheren Punkte. Die Häufigkeit von Punkten, die für die Garbage Collection sicher sind, ändert sich von Funktion zu Funktion. Sogar in nicht optimiertem Code kann es vorkommen, dass kein sicherer Punkt für das Beenden gefunden wird. In optimiertem oder nicht optimiertem Code kommt die ICorDebugController::Stop-Methode selten bei einem sicheren Punkt an.

Die CLR-Debugdienste richten eine neue Kette an dem Thread ein, um eine Funktionsauswertung zu starten und die angeforderte Funktion aufzurufen. Sobald die Auswertung gestartet wird, sind alle Aspekte der Debug-API verfügbar: Ablaufsteuerung, Funktionsauswertung und so weiter. Geschachtelte Auswertungen werden unterstützt, und Haltepunkte werden wie üblich behandelt.

Dynamisches Einfügen von Code

Einige Debugger ermöglichen einem Benutzer, beliebige Anweisungen in das Direktfenster einzugeben, und führen die Anweisungen aus. Die CLR-Debugdienste unterstützen dieses Szenario. Innerhalb bestimmter Grenzen gibt es keine Beschränkungen dafür, welchen Code Sie dynamisch einfügen können. (Zum Beispiel sind nicht lokale goto-Anweisungen nicht zugelassen.)

Dynamische Codeeinfügung wird implementiert, indem eine Kombination aus Bearbeiten und Fortfahren-Operationen und der Funktionsauswertung verwendet wird. Der einzufügende Code wird in einer Funktion umbrochen und wird durch Verwenden von Bearbeiten und Fortfahren eingefügt. Die eingefügte Funktion wird dann ausgewertet. Wenn Sie wünschen, können Sie die Umbruchfunktion mit Argumenten bereitstellen, die als ByRef deklariert sind, damit die Nebeneffekte unmittelbar und dauerhaft sind.

Siehe auch

Weitere Ressourcen

Debuggen in .NET Framework

Übersicht über das Debugging in der CLR

Debuggen (Referenz zur nicht verwalteten API)