März 2016

Band 31, Nummer 3

Visual Studio – Verbesserungen am Debugging in Visual Studio 2015

Von Andrew Hall | März 2016

Obwohl wir alles daran setzen, Code zu schreiben, der gleich beim ersten Mal ordnungsgemäß funktioniert, ist es eine Tatsache, dass Entwickler viel Zeit mit Debuggen verbringen. Visual Studio 2015 hat neue Funktionalität und Verbesserungen gebracht, um Entwicklern zu helfen, Probleme frühzeitiger im Entwicklungszyklus auszumachen und Programmfehler besser zu finden und zu beheben. In diesem Artikel stelle ich die Verbesserungen in Visual Studio 2015 für sowohl .NET- als auch C++-Entwickler vor.

Ich beginne mit zwei Verbesserungen der Nutzbarkeit und den neuen Leistungstools, die während des Debuggings für Microsoft .NET Framework und C++ zum Einsatz kommen. Ich beschäftige mich eingehend mit Verbesserungen speziell für die .NET-Entwicklung und stelle abschließend neue Entwicklungen für C++-Entwickler vor.

Einstellungen für Haltepunkte

Haltepunkte sind eine grundlegende Funktion des Debuggers, und es ist sehr wahrscheinlich, dass Sie sie beim letzten Einsatz des Debuggers verwendet haben. Es gibt Situationen, in denen das Verwenden bedingter Haltepunkte helfen kann, die Ursache eines Bugs wesentlich schneller zu finden. In Visual Studio 2015 ist die Nutzung bedingter Haltepunkte einfacher, da es jetzt ein nicht modales Fenster mit Haltepunkteinstellungen gibt, das im Kontext mit Ihrem Code ist. Es ermöglicht Ihnen ein einfaches Kombinieren unterschiedlicher Einstellungen im selben Fenster.

Zusammenfassend bietet Visual Studio 2015 die folgenden Einstellungen für Haltepunkte:

  • Bedingte Ausdrücke werden nur unterbrochen, wenn angegebene Bedingungen erfüllt sind. Dies ist die logische Entsprechung des Hinzufügens einer IF-Anweisung zum Code und Platzieren des Haltepunkts in der IF-Anweisung, die nur erfüllt wird, wenn die Bedingungen zutreffen. Bedingte Ausdrücke sind nützlich, wenn Sie Code haben, der mehrmals aufgerufen wird, aber der Fehler nur bei einer bestimmten Eingabe auftritt. Es wäre ansonsten langwierig, die Werte manuell zu überprüfen und dann das Debuggen fortzusetzen.
  • Die Trefferanzahl wird nur unterbrochen, nachdem der Haltepunkt mehrere Male erreicht wurde. Dies ist nützlich in Situationen, in denen Code mehrmals aufgerufen wird und Sie entweder genau wissen, wann der Fehler auftritt, oder eine allgemeine Vorstellung haben, dass der Fehler mindestens nach einer bestimmten Anzahl von Iterationen auftritt.
  • Filter werden unterbrochen, wenn der Haltepunkt bei einem bestimmten Thread, Prozess oder Computer erreicht wird. Filterhaltepunkte sind nützlich für das Debuggen von parallel ausgeführtem Code, wenn Sie nur eine Einzelinstanz beenden möchten.
  • Die Option „Meldung im Ausgabefenster protokollieren“ (auch Ablaufverfolgungspunkte genannt) gibt eine Meldung im Ausgabefenster aus und ermöglicht das automatische Fortsetzen der Ausführung. Ablaufverfolgungspunkte sind nützlich für eine vorübergehende Protokollierung, wenn Sie einen Ablauf verfolgen möchten, ohne zu unterbrechen und jeden Wert manuell nachzuverfolgen.

Zum Veranschaulichen des Nutzens bedingter Haltepunkte dient das Beispiel in Abbildung 1, in dem Anwendungen innerhalb einer Schleife nach Typ abgerufen werden. Der Code funktioniert für die meisten Eingabezeichenfolgen und ist nur fehlerhaft, wenn die Eingabe „desktop“ ist. Eine Möglichkeit ist das Festlegen eines Haltepunkts und anschließende Überprüfen von „appType“ bei jedem Erreichen, bis es „desktop“ ist. Dann kann ich die App schrittweise durchlaufen, um festzustellen, was nicht funktioniert. Es wäre jedoch weitaus schneller, einen Haltepunkt mit einem bedingten Ausdruck zu erstellen, der nur anhält, wenn „appType“ gleich „desktop“ ist (siehe Abbildung 1).

Fenster „Haltepunkteinstellungen“ mit einem bedingten Ausdruck
Abbildung 1: Fenster „Haltepunkteinstellungen“ mit einem bedingten Ausdruck

Durch Haltepunkte mit einem bedingten Ausdruck verringern sich die manuellen Ausdrücke, die erforderlich sind, um die Anwendung in den ordnungsgemäßen Zustand für das Debugging zu versetzen.

Ausnahmeeinstellungen

Als Entwickler wissen Sie, dass während der Ausführung Ihrer App Ausnahmen auftreten können. In vielen Fällen müssen Sie die Möglichkeit einkalkulieren, dass etwas schief gehen kann, indem Sie „try/catch“-Anweisungen hinzufügen. Wenn beispielsweise eine Anwendung Informationen über einen Netzwerkaufruf abruft, kann dieser Aufruf eine Ausnahme auslösen, wenn der Benutzer nicht über eine funktionierende Netzwerkverbindung verfügt oder der Server nicht antwortet. In diesem Fall muss die Netzwerkanforderung in einer „try“-Anweisung platziert werden. Wenn dann eine Ausnahme eintritt, muss die Anwendung dem Benutzer eine entsprechende Fehlermeldung anzeigen. Falls die Anforderung misslingt, wenn Sie erwarten, dass sie funktioniert (da beispielsweise die URL im Code falsch formatiert ist), sind Sie ggf. versucht, den Code nach einer Stelle zu durchsuchen, an der ein Haltepunkt gesetzt werden kann. Oder es reizt Sie, die „try/catch“-Anweisung zu entfernen, damit der Debugger bei einer nicht behandelten Ausnahme anhält.

Effizienter ist es jedoch, den Debugger mithilfe des Dialogfelds „Ausnahmeeinstellungen“ so zu konfigurieren, dass er anhält, wenn die Ausnahme ausgelöst wird. Dadurch können Sie den Debugger so festlegen, dass er bei Auslösen aller Ausnahmen oder nur von Ausnahmen eines bestimmten Typs anhält. Das Feedback auf das Dialogfeld „Ausnahmeeinstellungen“ in früheren Visual Studio-Version war, dass es zu langsam geöffnet würde und schlechte Suchfunktionen biete. Deshalb wurde für Visual Studio 2015 das alte Dialogfeld „Ausnahmeeinstellungen“ durch das neue moderne Fenster „Ausnahmeeinstellungen“ ersetzt, das sofort geöffnet wird und die einheitlich schnelle Suchfunktion bietet, die Sie erwarten (siehe Abbildung 2).

Das Visual Studio 2015-Fenster „Ausnahmeeinstellungen“ mit einer Suche nach allen Ausnahmetypen, die „Web“ enthalten
Abbildung 2: Das Visual Studio 2015-Fenster „Ausnahmeeinstellungen“ mit einer Suche nach allen Ausnahmetypen, die „Web“ enthalten

Leistungstools während des Debuggens

Ihre Endbenutzer erwarten zunehmend, dass Software schnell und reaktionsfähig ist, weshalb eine unübersichtliche oder schwerfällige Benutzeroberfläche sich negativ auf die Zufriedenheit und Bindung von Benutzern auswirken kann. Zeit ist kostbar, und wenn Benutzer die Wahl zwischen Anwendungen mit ähnlicher Funktionalität haben, wählen sie diejenige, die ihnen die bessere Benutzererfahrung bietet.

Beim Schreiben von Software stellen Sie allerdings häufig die proaktive Leistungsoptimierung zurück und befolgen stattdessen bewährte Methoden in der Hoffnung, dass die Anwendung schnell genug sein wird. Der Hauptgrund hierfür ist, dass das Messen der Leistung zeitaufwendig und schwierig ist und es noch schwieriger sein kann, Verbesserungsmöglichkeiten zu finden. Um hier Hilfe zu bieten, hat das Visual Studio Diagnostics-Team in Visual Studio 2015 verschiedene Leistungstools in Form von PerfTips und des Fensters „Diagnosetools“ direkt in den Debugger integriert. Mithilfe dieser Tools machen Sie sich mit der Leistung Ihres Codes im Rahmen des tagtäglichen Debuggens vertraut. Sie können Probleme schnell abfangen und fundierte Entwurfsentscheidungen treffen, damit Ihre Anwendung von Anfang an auf Leistung ausgelegt ist.

Eine einfache Möglichkeit, die Leistung Ihrer Anwendung zu verstehen, ist ihr schrittweises Durchlaufen im Debugger, um ein Gefühl zu bekommen, wie lange die Ausführung der einzelnen Codezeilen dauert. Leider ist diese Vorgehensweise nicht sehr wissenschaftlich, da sie von der Fähigkeit abhängt, Unterschiede zu erkennen. Es ist nämlich schwierig, den Unterschied zu benennen, wenn der eine Vorgang 25 ms und ein anderer 75 ms dauert. Alternativ können Sie den Code durch Hinzufügen von Timern ändern, die präzise Informationen erfassen, doch ist dafür eine zusätzliche Codeänderung erforderlich.

PerfTips bietet eine Lösung durch die Anzeige, wie lange die Ausführung des Codes gedauert hat, indem die Zeit in Millisekunden am rechten Ende der aktuellen Zeile gezeigt wird, wenn der Debugger beendet wird (siehe Abbildung 3). PerfTips zeigt die Zeit, die eine Anwendung zwischen zwei Haltezuständen im Debugger für die Ausführung braucht. Dies funktioniert sowohl beim schrittweisen Durchlaufen des Codes als auch bei der Ausführung bis zu Haltepunkten.

PerfTip mit der angezeigten verstrichenen Zeit für die schrittweise Ausführung eines Funktionsaufrufs
Abbildung 3: PerfTip mit der angezeigten verstrichenen Zeit für die schrittweise Ausführung eines Funktionsaufrufs

Sehen wir uns ein einfaches Beispiel an, wie PerfTip Sie wissen lassen kann, wie lange die Ausführung von Code gedauert hat. Sie haben eine Anwendung, die Dateien vom Datenträger lädt, wenn der Benutzer auf eine Schaltfläche klickt, und diese anschließend entsprechend verarbeitet. Die Erwartung ist, dass das Laden der Dateien vom Datenträger nur einige Millisekunden dauert. Doch bei Verwenden von PerfTips erkennen Sie, dass es deutlich länger dauert. Basierend auf diesen Informationen können Sie frühzeitig im Entwicklungszyklus (und bevor die Änderung zu aufwendig wird) den Entwurf der Anwendung so ändern, dass nicht alle Dateien geladen werden, wenn der Benutzer auf die Schaltfläche klickt. Weitere Informationen zu PerfTips finden Sie unter aka.ms/perftips.

Das Fenster „Diagnosetools“ zeigt einen Verlauf der CPU- und Arbeitsspeicherauslastung der Anwendung und alle Unterbrechungsereignisse (Haltepunkte, Schritte, Ausnahmen und „Alle unterbrechen“). Das Fenster hat drei Registerkarten: „Ereignisse“, „Speicherauslastung“ und „CPU-Auslastung“. Die Registerkarte „Ereignisse“ zeigt einen Verlauf aller Unterbrechungsereignisse, was heißt, dass sie eine vollständige Aufzeichnung aller PerfTips-Werte enthält. In der Enterprise-Version von Visual Studio 2015 enthält sie alle IntelliTrace-Ereignisse (die weiter unten in diesem Artikel behandelt werden). Abbildung 4 zeigt die Registerkarte „Ereignisse“ in Visual Studio 2015 Enterprise. Außerdem haben Sie dank der Aktualisierung der Informationen zu CPU und Arbeitsspeicher während Ihrer Debuggingsitzung die Möglichkeit, sich die CPU- und Arbeitsspeichermerkmale bestimmter Codeabschnitte anzusehen. Sie können beispielsweise einen Methodenaufruf durchlaufen und beobachten, wie sich die Graphen beim Messen der Auswirkung der jeweiligen Methode ändern.

Fenster „Diagnosetools“ mit ausgewählter Registerkarte „Ereignisse“
Abbildung 4: Fenster „Diagnosetools“ mit ausgewählter Registerkarte „Ereignisse“

Im Graphen für den Arbeitsspeicher sehen Sie die gesamte Speicherauslastung Ihrer Anwendung und können entsprechende Trends erkennen. Der Graph kann beispielsweise einen stetigen Aufwärtstrend zeigen, was bedeutet, dass die Anwendung Arbeitsspeicher verliert und schlussendlich abstürzen kann. Ich beginne zuerst für .NET Framework und anschließend für C++ mit der Untersuchung der Funktionsweise.

Beim Debugging für .NET Framework zeigt der Graph auftretende Garbage Collections (GCs) sowie den gesamten Arbeitsspeicher. Dadurch lassen sich Situationen erkennen, in denen die gesamte Arbeitsspeichernutzung der Anwendung ein annehmbares Niveau hat, aber ihre Leistung aufgrund häufiger GCs leidet (was üblicherweise durch das Zuordnen zu vieler kurzlebiger Objekte verursacht wird). Die Registerkarte „Speicherauslastung“ ermöglicht über die Schaltfläche „Momentaufnahme erstellen“ das Erstellen von Momentaufnahmen der Objekte im Arbeitsspeicher zu einem bestimmten Zeitpunkt. Sie lässt auch das Vergleichen zweier verschiedener Momentaufnahmen zu, was die einfachste Möglichkeit zum Bestimmen eines Arbeitsspeicherverlusts ist. Sie erstellen eine Momentaufnahme, nutzen die Anwendung für einen Zeitraum auf eine Weise, die Sie für speicherneutral halten, und erstellen dann eine zweite Momentaufnahme. Abbildung 5 zeigt das Arbeitsspeichertool mit zwei .NET-Momentaufnahmen.

Registerkarte „Speicherauslastung“ im Fenster „Diagnosetools“ mit zwei Momentaufnahmen
Abbildung 5: Registerkarte „Speicherauslastung“ im Fenster „Diagnosetools“ mit zwei Momentaufnahmen

Wenn Sie auf eine Momentaufnahme klicken, öffnet sich die Heap-Ansicht, in der Sie die Details zu den Objekten im Arbeitsspeicher sehen, einschließlich Gesamtanzahl und Arbeitsspeicherbelegung. Die untere Hälfte der Heap-Ansicht zeigt, welche Elemente über Verweise auf die Objekte verfügen, was ihre Garbage Collection verhindert (und als „Pfade zum Stamm“ bezeichnet wird). Außerdem sehen Sie auf der Registerkarte „Referenzierte Typen“, auf welche anderen Typen der ausgewählte Typ verweist. Abbildung 6 zeigt die Heap-Ansicht mit den Unterschieden zwischen den beiden Momentaufnahmen.

Die Heap-Ansicht mit den Momentaufnahmen zeigt die Unterschiede zwischen den beiden .NET-Momentaufnahmen
Abbildung 6: Die Heap-Ansicht mit den Momentaufnahmen zeigt die Unterschiede zwischen den beiden .NET-Momentaufnahmen

Das Arbeitsspeichertool für C++ überwacht Speicherbelegungen und -freigaben, um festzustellen, was der Speicher zu einem bestimmten Zeitpunkt enthält. Klicken Sie zum Anzeigen der Daten auf die Schaltfläche „Momentaufnahme erstellen“, um eine Aufzeichnung der Belegungsinformationen zu diesem Zeitpunkt zu erstellen. Momentaufnahmen können auch verglichen werden, um herauszufinden, welcher Speicher sich zwischen zwei Momentaufnahmen geändert hat. Dies erleichtert das Aufspüren von Arbeitsspeicherverlusten in Codepfaden, die im Arbeitsspeicher vollständig freigegeben werden sollen. Wenn Sie eine anzuzeigende Momentaufnahme auswählen, zeigt die Heap-Ansicht eine Liste mit Typen und deren Größen. Beim Vergleich von zwei Momentaufnahmen werden die Unterschiede zwischen diesen Werten gezeigt. Wenn Sie einen Typ sehen, dessen Arbeitsspeichernutzung Sie besser verstehen möchten, lassen Sie sich die Instanzen dieses Typs anzeigen. Die Ansicht „Instanzen“ zeigt die Größe der einzelnen Instanzen, die Dauer ihrer Aktivität im Arbeitsspeicher und die Aufrufliste, die den Speicher belegt hat. Abbildung 7 zeigt die Ansicht „Instanzen“.

Heap-Ansicht von C++-Arbeitsspeicher, in der die Ansicht „Instanzen“ mit der Belegungsaufrufliste gezeigt wird
Abbildung 7: Heap-Ansicht von C++-Arbeitsspeicher, in der die Ansicht „Instanzen“ mit der Belegungsaufrufliste gezeigt wird

Der Graph „CPU“ zeigt die CPU-Belegung der Anwendung als Prozentsatz aller Prozessorkerne des Computers. Dies ist nützlich für das Bestimmen von Vorgängen, die unnötige Spitzen bei der CPU-Belegung verursachen oder die die CPU nicht voll nutzen. Nehmen wir als Beispiel die Verarbeitung einer großen Menge von Daten, bei der jeder Datensatz unabhängig verarbeitet werden kann. Beim Debuggen des Vorgangs stellen Sie fest, dass sich der Graph „CPU“ auf einem Computer mit vier Kernen bei etwas unter 25 % einpendelt. Dies ist ein Hinweis auf eine Möglichkeit, die Datenverarbeitung auf allen Kernen des Computers parallel ausführen zu lassen, um eine schnellere Anwendungsleistung zu erzielen.

In Visual Studio 2015 Update 1 ging das Visual Studio Diagnostics-Team noch einen Schritt weiter, indem auf der Registerkarte „CPU-Auslastung“ ein in den Debugger integrierter CPU-Profiler hinzugefügt wurde, der eine Aufschlüsselung der Funktionen in der Anwendung zeigt, die die CPU nutzen (siehe Abbildung 8). Es gibt z. B. Code, der mithilfe eines regulären Ausdrucks prüft, ob eine vom Benutzer eingegebene E-Mail-Adresse ein gültiges Format hat. Bei Eingabe einer gültigen E-Mail-Adresse wird der Code überaus schnell ausgeführt. Bei einer E-Mail-Adresse in einem falschen Format zeigt der PerfTip, dass es bis zu zwei Sekunden dauern kann, bis die Ungültigkeit der Adresse bestimmt ist. Ein Blick auf das Fenster „Diagnosetools“ ergibt, dass es zu diesem Zeitpunkt eine CPU-Spitze gab. Bei Untersuchung der Aufrufstruktur auf der Registerkarte „CPU-Auslastung“ erkennen Sie, dass die CPU-Nutzung vom Abgleich mit dem regulären Ausdruck herrührt (was auch in Abbildung 8 gezeigt wird). Es stellt sich heraus, dass reguläre C#-Ausdrücke einen Nachteil haben: Wenn sie keine Übereinstimmung mit komplexen Anweisungen finden, kann der Aufwand der Verarbeitung der gesamten Zeichenfolge hoch sein. Mithilfe von PerfTips und dem Tool „CPU-Auslastung“ im Fenster „Diagnosetools“ können Sie rasch feststellen, dass die Leistung der Anwendung bei Verwenden des regulären Ausdrucks nicht in allen Fällen annehmbar sein wird. Sie können Ihren Code dann so ändern, dass stattdessen einige standardmäßige Zeichenfolgenvorgänge erfolgen, die eine wesentlich bessere Leistung liefern, wenn fehlerhafte Daten eingegeben werden. Dies hätte später zu einem Programmfehler mit hohem Korrekturaufwand werden können, insbesondere wenn er es bis in die Produktion geschafft hätte. Zum Glück haben die in den Debugger integrierten Tools eine Änderung des Entwurfs während der Entwicklung ermöglicht, um eine zufriedenstellende Leistung sicherzustellen. Weitere Informationen zum Fenster „Diagnosetools“ finden Sie unter aka.ms/diagtoolswindow.

Registerkarte „CPU-Auslastung“ mit der CPU-Nutzung beim Suchen von Übereinstimmungen mithilfe eines regulären Ausdrucks
Abbildung 8: Registerkarte „CPU-Auslastung“ mit der CPU-Nutzung beim Suchen von Übereinstimmungen mithilfe eines regulären Ausdrucks

Als Nächstes wollen wir uns einige der besonderen Verbesserungen beim .NET-Debugging ansehen.

Lambda-Unterstützung in den Fenstern „Überwachen“ und „Direkt“

Lambda-Ausdrücke (z. B. LINQ) stellen eine überaus leistungsstarke und gängige Möglichkeit dar, schnell mit Datenauflistungen zu arbeiten. Sie lassen das Anwenden komplexer Vorgänge mithilfe einer einzelnen Codezeile zu. Oft möchten Sie lieber Änderungen an Ausdrücken in den Debuggerfenstern testen oder LINQ zum Abfragen einer Auflistung verwenden, anstatt sie im Debugger manuell zu erweitern. Stellen Sie sich beispielsweise eine Situation vor, bei der Ihre Anwendung eine Auflistung von Elementen abfragt und keine Ergebnisse zurückgegeben werden. Sie sind sicher, dass es Elemente gibt, die mit den vorgesehenen Kriterien übereinstimmen, weshalb Sie mit dem Abfragen der Auflistung beginnen, um die unterschiedlichen Elemente in der Liste zu extrahieren. Die Ergebnisse bestätigen, dass es Elemente gibt, die mit den vorgesehenen Kriterien übereinstimmen. Doch anscheinend liegt bei der Schreibung von Zeichenfolgen eine Fehlübereinstimmung vor. Die Schreibung soll beim Abgleich auf Übereinstimmung aber keine Rolle spielen. Ihre Hypothese ist, dass Sie die Abfrage so ändern müssen, dass die Schreibung beim Zeichenfolgenvergleich ignoriert werden soll. Die einfachste Möglichkeit zum Testen dieser Hypothese ist das Eingeben der neuen Abfrage in das Fenster „Überwachen“ oder „Direkt“, um zu prüfen, ob die erwarteten Ergebnisse zurückgegeben werden (siehe Abbildung 9).

Leider führte in Visual Studio-Versionen vor 2015 das Eingeben eines Lambda-Ausdrucks in ein Debuggerfenster zu einer Fehlermeldung. Deshalb wird zum Erfüllen dieser oft genannten Anforderung nun Unterstützung für das Verwenden von Lambda-Ausdrücken in Debuggerfenstern geboten.

Fenster „Direkt“ mit zwei ausgewerteten Lambda-Ausdrücken
Abbildung 9: Fenster „Direkt“ mit zwei ausgewerteten Lambda-Ausdrücken

.NET-Verbesserungen für „Bearbeiten und Fortfahren“

Ein beliebtes Feature zum Steigern der Produktivität beim Debuggen in Visual Studio heißt „Bearbeiten und Fortfahren“. „Bearbeiten und Fortfahren“ ermöglicht das Ändern des im Debugger angehaltenen Codes, das Anwenden der Bearbeitung, ohne das Debugging beenden zu müssen, und das Neukompilieren und Ausführen der Anwendung an derselben Stelle, um zu überprüfen, ob das Problem durch die Änderung behoben wurde. Doch eine der frustrierendsten Erfahrungen bei Verwenden von „Bearbeiten und Fortfahren“ ist das Vornehmen der Bearbeitung und Versuchen der Fortsetzung der Ausführung, bis die Meldung erscheint, dass die vorgenommene Änderung nicht während des Debuggings angewendet werden könne. Dies wurde ein immer häufigeres Problem, da dem Framework weiter neue Sprachfeatures hinzugefügt wurden, die „Bearbeiten und Fortfahren“ nicht unterstützen konnten (wie z. B. Lambda-Ausdrücke und asynchrone Methoden).

Um hier Abhilfe zu schaffen, hat Microsoft Unterstützung für mehrere bislang nicht unterstützte Bearbeitungstypen hinzugefügt, wodurch Bearbeitungen während des Debuggings wesentlich häufiger erfolgreich angewendet werden können. Zu den Verbesserungen zählen die Möglichkeit zum Ändern von Lambda-Ausdrücken, Bearbeiten anonymer und asynchroner Methoden, Arbeiten mit dynamischen Typen und Ändern von C# 6.0-Features (z. B. Interpolation von Zeichenfolgen und Nullbedingungsoperatoren). Eine vollständige Auflistung der unterstützten Bearbeitungen finden Sie unter aka.ms/dotnetenc. Wenn Sie eine Bearbeitung vornehmen und die Fehlermeldung erhalten, dass die Änderung nicht angewendet werden kann, überprüfen Sie die Fehlerliste, der der Compiler zusätzliche Informationen zum Grund hinzufügt, warum die Bearbeitung nicht über „Bearbeiten und Fortfahren“ kompiliert werden konnte.

Weitere Verbesserungen an „Bearbeiten und Fortfahren“ umfassen die Unterstützung für Anwendungen, die die x86- und x64-CoreCLR verwenden. Dadurch kann dieses Feature zum Debuggen von Windows Phone-Apps im Emulator verwendet werden. Zudem wird das Remotedebuggen unterstützt.

Neue IntelliTrace-Umgebung

IntelliTrace bietet Verlaufsinformationen zu Ihrer Anwendung mit dem Ziel, in der Enterprise-Version Vermutungen aus dem Debugging zu verbannen und Sie schneller und in weniger Debuggingsitzungen zu den relevanten Teilen Ihres Codes zu führen. IntelliTrace zeichnet sich durch zahlreiche Verbesserungen aus, die seine Benutzerfreundlichkeit steigern. Dazu zählen eine Zeitachse, die zeigt, zu welchen Zeitpunkten Ereignisse eintreten, die Fähigkeit zum Anzeigen von Ereignissen in Echtzeit, Unterstützung von Ablaufverfolgungspunkten und die Integration in das Fenster „Diagnosetools“.

Anhand der Zeitachse können Sie erkennen, zu welchen Zeitpunkten Ereignisse eintreten, und Gruppen von Ereignissen ausmachen, die ggf. zusammenhängen. Ereignisse werden live auf der Registerkarte „Ereignisse“ angezeigt, wohingegen Sie in früheren Versionen einen Unterbrechungsstatus im Debugger aktivieren mussten, um die von IntelliTrace gesammelten Ereignisse anzuzeigen. Dank der Integration von Ablaufverfolgungspunkten können Sie benutzerdefinierte IntelliTrace-Ereignisse mithilfe standardmäßiger Debuggerfunktionalität erstellen. Schließlich erlaubt die Integration des Fensters „Diagnosetools“, IntelliTrace-Ereignisse in Kontext mit Leistungsdaten zu setzen, wodurch Sie die umfassenden Informationen von IntelliTrace nutzen können, um die Ursache von Leistungs- und Arbeitsspeicherproblemen zu verstehen, indem Sie die Informationen in einer gemeinsamen Zeitachse in Korrelation setzen.

Wenn Sie ein Problem in der Anwendung erkennen, entwickeln Sie üblicherweise eine Hypothese dazu, wo mit der Untersuchung begonnen sollte. Anschließend setzen Sie einen Haltepunkt, und führen das Szenario erneut aus. Wenn sich das Problem nicht an dieser Stelle befindet, müssen Sie eine neue Hypothese aufstellen, um zur richtigen Stelle im Debugger zu gelangen, und den Prozess erneut starten. IntelliTrace zielt auf die Verbesserung dieses Workflows dadurch ab, dass die Notwendigkeit der erneuten Ausführung des Szenarios wegfällt.

Kehren wir noch einmal zum Beispiel in diesem Artikel zurück, bei dem ein Netzwerkaufruf unerwartet misslingt. Ohne IntelliTrace müssen Sie den Fehler beim ersten Mal erkennen, den Debugger aktivieren und ihn anhalten, wenn die Ausnahme ausgelöst wird, und dann das Szenario erneut ausführen. Mit IntelliTrace müssen Sie bei Erkennen des Fehlers nur auf der Registerkarte „Ereignisse“ im Fenster „Diagnosetools“ nachsehen. Die Ausnahme wird als Ereignis angezeigt. Wählen Sie sie aus, und klicken Sie auf „Verlaufsbezogenes Debugging“. Sie werden zurück an die Stelle im Quellcode geführt, an der die Ausnahme aufgetreten ist. Die Fenster „Lokal“ und „Auto“ enthalten Informationen zur Ausnahme. Das Fenster „Aufrufliste“ wird mit der Aufrufliste aufgefüllt, in der die Ausnahme erfolgt ist (siehe Abbildung 10).

Visual Studio im Modus „Verlaufsbezogenes Debugging“ an der Stelle, an der die Ausnahme ausgelöst wurde
Abbildung 10: Visual Studio im Modus „Verlaufsbezogenes Debugging“ an der Stelle, an der die Ausnahme ausgelöst wurde

Zum Schluss wollen wir einen Blick auf die wichtigsten Verbesserungen für das Debuggen von C++ in Visual Studio 2015 werfen.

Bearbeiten und Fortfahren – C++

Wie bereits erwähnt, ist „Bearbeiten und Fortfahren“ ein Produktivitätsfeature, das Ihnen das Ändern Ihres im Debugger angehaltenen Codes und anschließende Anwenden der Änderungen ermöglicht, wenn Sie die Ausführung fortsetzen, ohne dass das Debuggen zum erneuten Kompilieren der geänderten Anwendung beendet werden muss. In bisherigen Versionen galten für „Bearbeiten und Fortfahren“ in C++ zwei erhebliche Einschränkungen. Zum einen wurden nur x86-Anwendungen unterstützt. Zum anderen führte das Aktivieren von „Bearbeiten und Fortfahren“ dazu, dass Visual Studio den Visual Studio 2010-C++-Debugger aktivierte, dem neue Funktionalität wie Unterstützung für Natvis-Datenvisualisierungen (siehe aka.ms/natvis) fehlt. In Visual Studio 2015 gelten diese Einschränkungen nicht mehr. „Bearbeiten und Fortfahren“ ist für C++-Projekte für x86- und x64-Anwendungen standardmäßig aktiviert und funktioniert auch, wenn ein Anfügen an Prozesse und Remotedebuggen erfolgt.

Unterstützung für Android und iOS

Da sich die „Mobil zuerst“-Mentalität immer weiter durchsetzt, sehen sich viele Organisationen der Notwendigkeit gegenüber, mobile Anwendungen zu entwickeln. Bei der Mannigfaltigkeit von Plattformen ist C++ eine der wenigen Technologien, die geräte- und betriebssystemübergreifend verwendet werden können. Viele Großunternehmen verwenden gemeinsam genutzten C++-Code für allgemeine Geschäftslogik, die sie in einem breiten Spektrum von Angeboten wiederverwenden möchten. Um dies zu erleichtern, bietet Visual Studio 2015 Tools, mit denen Entwickler mobiler Apps direkt in Visual Studio für Android und iOS entwickeln können. Dies schließt die vertraute Visual Studio-Debuggingumgebung ein, die Entwickler bei ihrer täglichen Arbeit in C++ unter Windows nutzen.

Zusammenfassung

Das Visual Studio Diagnostics-Team hat der Debuggingumgebung in Visual Studio 2015 eine breite Palette neuer sehr nützlicher Features hinzugefügt. Die in diesem Artikel vorgestellten Features stehen mit Ausnahme von IntelliTrace allesamt in der Community-Edition von Visual Studio 2015 zur Verfügung.

Im Teamblog unter aka.ms/diagnosticsblog können Sie die Fortschritte des Teams und künftige Verbesserungen weiter verfolgen. Probieren Sie die vorgestellten Features aus, und geben Sie dem Team Feedback dazu, wie Visual Studio weiter verbessert werden kann, um Ihre Anforderungen an das Debugging zu erfüllen. Sie können zu den Blogbeiträgen Kommentare und Fragen hinterlassen oder Feedback direkt aus Visual Studio senden, indem Sie rechts oben in der IDE auf das Symbol „Feedback senden“ klicken.


Andrew Hall* ist leitender Programm-Manager im Visual Studio Diagnostics-Team, das die Hauptkomponenten für das Debugging, die Profilerstellung und IntelliTrace sowie den Android-Emulator für Visual Studio entwickelt. In den vergangenen Jahren hat er direkt an den Debugger-, Profiler- und Codeanalysetools in Visual Studio gearbeitet.*

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Angelos Petropoulos, Dan Taylor, Kasey Uhlenhuth und Adam Welch