Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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.