VB6 hat keinen Profiler? Dann bau ich mir eben einen

Kurz gesagt: Mein Profiler arbeitet mit einer temporären Kopie des VB6-Projekts, fügt dort automatisch Messpunkte ein und zeigt dir live, wo Laufzeit und Windows-Ressourcen hängen bleiben. Dein Originalcode bleibt dabei unangetastet.

Auf einen Blick

n

Das Problem

Visual Basic 6 hat bis heute keinen eingebauten Profiler. Wenn du in einer Anwendung den Zeitfresser suchst, bleibt ohne eigenes Werkzeug oft nur Handarbeit: Debug.Print Timer vor und nach verdächtigen Stellen. Für eine einzelne Methode reicht das, in einem großen Projekt wird es schnell mühsam.

Ich wollte deshalb ein externes Werkzeug, das ein bestehendes VB6-Projekt analysiert und Messpunkte einfügt, ohne die Originaldateien anzutasten. Der Profiler arbeitet dafür mit einer temporären Projektkopie.

Die Idee: Automatisch instrumentieren, ohne das Original anzufassen

Der VB6 Profiler ist eine eigenständige VB.NET-Anwendung, die ein bestehendes VB6-Projekt (oder eine ganze Projektgruppe) einliest, versteht, welche Methoden und Zeilen es enthält — und bei Bedarf eine temporäre Kopie davon anlegt, in die automatisch Zeitmess-Code eingefügt wird. Das Original-Projekt wird zu keinem Zeitpunkt verändert; alle Eingriffe passieren ausschließlich in einer Wegwerf-Kopie im Temp-Verzeichnis. Man wählt im Konfigurator einfach aus, was überwacht werden soll — ein ganzes Projekt, eine einzelne Datei, eine einzelne Methode oder sogar einzelne Zeilen darin — und der Profiler kümmert sich um den Rest: Kopie anlegen, Profiler-Code einfügen, Zeilen instrumentieren, starten.

Was der Profiler kann

Ganze Projekte und Projektgruppen einlesen

.vbp– und .vbg-Dateien werden vollständig geparst — inklusive aller Module, Formulare und Klassen, deren Methoden (Sub/Function/Property Get/Set/Let) und sogar der einzelnen Quellcodezeilen darin. Der Projektbaum gruppiert nach Dateityp (Formulare, Module, Klassenmodule, UserControls), damit man in großen Projekten nicht in einer flachen Liste versinkt. Gesucht wird über eine UND-verknüpfte Mehrzeilen-Suche mit Spaltenfiltern: jede Zeile ein Kriterium (Projekt, Datei, Methode oder direkt Quellcode-Inhalt), der Suchbereich wächst automatisch mit, und auf Wunsch werden nur bereits überwachte Elemente angezeigt.

Feingranulare Überwachungskonfiguration

Überwachung lässt sich exakt dosieren: ein komplettes Projekt, eine einzelne Datei, eine einzelne Methode — oder, wenn’s genau sein muss, zeilenbasiert bis auf einzelne Codezeilen herunter. Jeder Knoten im Baum hat eine eigene Checkbox. Wer eine ganze Verdächtigen-Liste hat (zum Beispiel aus einer vorherigen Analyse), fügt sie einfach als Text ins Kontextmenü ein — alle genannten Methoden werden auf einen Schlag angehakt, nicht gefundene Namen werden gemeldet.

Zusätzlich wählt man vor dem Start, welche Kennzahlen überhaupt erfasst werden: Die Dauer ist immer dabei, GDI-Handles, USER-Objekte und deren Peak-Werte sind einzeln zuschaltbar. Eine abgewählte Kennzahl wird beim Generieren des Messcodes gar nicht erst eingebaut und verursacht dann auch keinen API-Aufruf.

Die komplette Konfiguration wird pro Projekt in schlichten JSON-Dateien gespeichert — diffbar, von Hand reparierbar, beim nächsten Start ist alles wieder genau so eingestellt wie zuletzt.

Automatische Instrumentierung im Detail

Beim Start des Profilers passiert Folgendes automatisch, ohne manuellen Eingriff: Das komplette Projekt (bzw. die Projektgruppe) wird in einen Temp-Ordner kopiert, ein fertiges Profiler-Modul und eine Profiler-Klasse werden in jedes betroffene Projekt eingefügt, und in jeder überwachten Methode werden Start-/Ende-Aufrufe eingebaut — bei zeilenbasierter Überwachung vor jeder einzelnen markierten Zeile. Anschließend übernimmt VB6 selbst das Kompilieren/Ausführen dieser Kopie ganz normal, nur dass sie jetzt „mitschreibt“.

Wenn der Profiler lügen würde: die Select-Case-Falle

Die wichtigste Eigenschaft eines Instrumentierers ist nicht Geschwindigkeit, sondern Unsichtbarkeit: Das vermessene Programm muss sich exakt so verhalten wie ohne Messung. Wie schnell das kippen kann, zeigt eine Falle, in die die zeilenbasierte Instrumentierung anfangs getappt ist. Case-Zeilen wurden nach diesem Muster instrumentiert:

' vorher:
Case 5
' instrumentiert:
Case Not Profiler.StartZeile(4711, True), 5

Sieht harmlos aus: Not True ergibt False, und False matcht ja nichts … außer, der Select-Ausdruck ist selbst 0, False oder ein Leerstring. Dann matcht plötzlich der eingeschleuste Vergleichswert — und die Ausführung landet im falschen Zweig. Bewiesen habe ich das mit einem Mini-Testprojekt, das die Instrumentierung von Hand nachstellt (Select Case 0, Select Case False, Select Case "" — alle drei kippen). Die Konsequenz: Case-Zeilen mit Werteliste werden bewusst nicht instrumentiert — die erste Zeile im Rumpf des Zweigs übernimmt die Messung. Der Informationsverlust ist minimal, die Semantik bleibt garantiert unangetastet.

Präzise Zeitmessung mit Windows-Bordmitteln

Gemessen wird mit voller Genauigkeit und erst für die Anzeige gerundet. Der Overhead des Profilers wird automatisch herausgerechnet; rekursive Aufrufe zählen in Methodensummen nicht doppelt. Schleifen lassen sich jetzt genauer lesen: Neben der aufsummierten Zeilenzeit zeigt die Aufrufliste auch die Dauer eines einzelnen Durchlaufs. Bei Bedarf kann der Profiler GDI- und USER-Handles weiterhin je Zeile erfassen; standardmäßig geschieht das an Methodengrenzen, damit die Messpunkte schneller bleiben.

Live mitschauen, während das Programm läuft

Sobald der Profiler gestartet ist, laufen die instrumentierten Methodenaufrufe in Echtzeit ins Tool — mit Dauer, Aufrufzahl und einer klaren Unterscheidung zwischen „läuft noch“ und „abgeschlossen“. Über eine Schnellleiste blendet man ganze Spaltengruppen ein und aus (Dauer, Gruppierung, Aufrufe, Analyse, GDI, USER) und holt sich genau die Kennzahlen ins Bild, die gerade interessieren: prozentualer Anteil, Eigenzeit (ohne Kinder), Spannweite, Kinderzahl, Aufruftiefe, Durchschnittswerte, Peaks. Die zweizeiligen, nach Gruppen pastellig eingefärbten Spaltenköpfe halten das Ganze trotz Spaltenmenge lesbar.

Auch bei extremen Datenmengen bleibt das UI bedienbar: Große Aufruflisten werden asynchron geladen, und gerät die Auswertung bei sehr vielen Logs in Rückstand, zeigt die Statusleiste die aktuelle Auswerterate in Logs/s — man sieht, dass das Tool arbeitet, statt zu rätseln, ob es hängt.

Hotspots: Wo geht die Zeit hin?

Die wichtigste Frage beim Profiling ist selten „was ist alles passiert?“, sondern fast immer „wo steckt die Zeit?“. Die Hotspot-Ansicht beantwortet sie direkt: Gleichartige Aufrufe werden zusammengefasst und als Treemap (Fläche = Zeit) oder Balkenliste dargestellt, mit Summendauer, prozentualem Anteil und einstellbarer Gruppenanzahl von 5 bis 100. Statt sich durch einen Aufrufbaum zu hangeln, sieht man auf einen Blick, welche drei Methoden 90 % des Laufs ausmachen.

Session-Übersicht: Auswerten auch nach dem Stopp

Die Session-Übersicht (Taste F6) zeigt kumulierte Kennzahlen je Methode oder Zeile über die gesamte Profiling-Session — darunter Eigenzeit, Gesamtzeit, Aufrufe, Durchschnitt, längster Einzelaufruf und GDI-/USER-Werte. Suche, Dateifilter und Mindestanteil helfen beim Eingrenzen; die Live-Aktualisierung arbeitet nur bei Änderungen, alternativ aktualisiert F5. Nach dem Stoppen lassen sich die Ergebnisse weiter auswerten. Neben CSV kann die Session jetzt auch als KI-Datenpaket exportiert werden.

Das KI-Datenpaket: Messwerte und Quellcode gemeinsam auswerten

Das KI-Datenpaket bündelt ein Markdown-Briefing und eine SQLite-Datenbank mit den Profiling-Ergebnissen. Die Datenbank enthält jede einzelne Ausführung mit Aufrufbeziehungen, Laufzeiten und erfassten Handles sowie den vollständigen Original-Quelltext der beteiligten Dateien. Fertige Ansichten erschließen Zeilen- und Methoden-Hotspots, Schleifen, den Aufrufgraphen, annotierten Quellcode und weitere Zusammenhänge. Exportiert werden kann eine ganze Session, eine Aufrufliste oder eine Auswahl von Aufrufen samt Unteraufrufen. Ein Fortschrittsdialog begleitet den Export und zeigt die einzelnen Arbeitsschritte; bei Abbruch werden unvollständige Dateien entfernt. Das Briefing erklärt das Messmodell und Datenbankschema und enthält passende SQL-Abfragen für den direkten Einstieg in die Analyse.

Eingebetteter Quellcode-Viewer

Wählt man einen Aufruf aus, zeigt der Quellcode-Viewer sofort die passende Stelle im Code — wahlweise eingebettet im Hauptfenster, in einem eigenen, immer sichtbaren Extra-Fenster, oder ganz ausgeblendet, wenn gerade nur die reinen Zahlen interessieren. So lässt sich eine auffällig lange Dauer sofort mit dem tatsächlichen Code dahinter abgleichen, ohne zwischen VB6-IDE und Profiler hin- und herzuwechseln.

Ressourcen im Blick, nicht nur Zeit

Zusätzlich zur Laufzeit erfasst der Profiler auf Wunsch GDI-Handles, USER-Objekte und ihre Spitzenwerte. Standardmäßig werden die Werte an Methodenanfang und -ende abgefragt; mit der Option „Handles je Zeile“ geht die Messung bis auf Quellzeilenebene. So bleibt die schnelle Standardmessung schlank, während sich ein vermuteter Ressourcenanstieg gezielt genauer eingrenzen lässt. Nicht aktivierte Kennzahlen werden gar nicht erst in den erzeugten Messcode aufgenommen.

Fehler nicht verpassen

Unbehandelte Exceptions der profilierten Anwendung landen automatisch in einer eigenen Fehlerliste — inzwischen inklusive ThreadException und UnobservedTaskException, mit farbig dargestellter, vollständiger Exception-Kette samt Stacktrace. Die Liste überlebt Projektwechsel und Neustart, und alles wird zusätzlich als NDJSON-Log auf Platte gesichert. Praktisch, wenn ein Absturz genau während einer Profiling-Sitzung passiert und man sonst Gefahr liefe, ihn im Log-Rauschen zu übersehen.

Persistenz & schneller Workflow

Konfiguration in JSON, Fenster- und Splitterpositionen (auch verschachtelt) werden gespeichert und wiederhergestellt — beim nächsten Start ist alles wieder genau so wie zuletzt. Dazu ein auf Tastatur-Bedienung ausgelegter Workflow für den täglichen Einsatz:

TasteAktion
Strg+FFokus ins Suchfeld (Projekt/Datei)
Strg+OVB6-Projekt/Konfiguration auswählen
F3Projekt im Konfigurator in VB6 öffnen
F4Profiler starten/neu starten
F5Instrumentierte Projektkopie in VB6 öffnen
F6Session-Übersicht öffnen
EscProfiler stoppen
EntfMethode von der Überwachung ausschließen

Qualitätssicherung: der Hindernislauf

Die Select-Case-Falle hat eine Konsequenz hinterlassen: Der Profiler hat ein eigenes Folter-Testprojekt bekommen. „Hindernislauf“ ist ein absichtlich fieses VB6-Programm, das so ziemlich alles enthält, woran eine automatische Instrumentierung scheitern kann:

  • GoTo vorwärts und rückwärts über tote Codeblöcke, GoSub/Return, On x GoSub-Verteiler
  • nummerierte Zeilen mit Erl-Auswertung und allen drei Resume-Varianten (Resume, Resume Next, Resume <Ziel>)
  • Einzeiler-Ifs mit Doppelpunkt und Else, IIf mit Seiteneffekten, Fortsetzungszeilen-Monster, nicht übersetzte #If-Zweige
  • die Select-Case-Semantik-Fallen (Case 0, Case False, Case "") als Dauer-Regression
  • enge Schleifen mit 100.000 Durchläufen (Overhead-Messung), Sleep, Busy-Wait und DoEvents-Warten — inklusive eines zuschaltbaren „Störfeuer-Timers“, der alle 250 ms mitten in laufende Messungen hineinfunkt
  • exponentielle und wechselseitige Rekursion (Stress für Aufruftiefe und Flush-Drosselung)
  • Objektketten mit eigenem Class_Initialize/Class_Terminate (kollidiert bewusst mit der Terminate-basierten Ende-Erkennung des Profilers) und CallByName-Aufrufe am Parser vorbei

Jeder Testlauf protokolliert sein Ergebnis; die erwarteten Werte stehen in der Testdokumentation. Stimmen Ergebnis und Soll überein, bleibt das Verhalten des VB6-Programms trotz Instrumentierung erhalten.

Genauer messen und große Aufruflisten flüssig auswerten

Der Profiler zieht seinen Overhead automatisch ab. Zeitwerte bleiben bis zur Anzeige ungerundet, rekursive Aufrufe zählen in Methodensummen nicht doppelt und Schleifen zeigen neben der Gesamtdauer auch die Dauer je Durchlauf. GDI- und USER-Handles werden standardmäßig an Methodengrenzen erfasst; eine Option schaltet die Abfrage je Zeile ein.

Auch große Aufruflisten mit Hunderttausenden Zeilen bleiben bedienbar: Der Profiler baut sie im Hintergrund auf und berechnet Quellcode- und Diagrammanzeigen nicht bei jeder Auswahl komplett neu. Ignorierte Aufrufe kannst du aus der Detailansicht ausblenden; die Session-Übersicht zählt sie weiterhin mit.

Die Instrumentierung erhält nummerierte ElseIf– und Do While-Zeilen, damit Erl und Resume <Nummer> weiter funktionieren. Kommentare mit einem Unterstrich am Ende ziehen keine nächste Codezeile mehr ein. Auch Prozedur-Attribute und Projektgruppen mit gleichnamigen Dateien werden korrekt behandelt.

Zehn VB6-Testprojekte prüfen feste Sollzeiten und fachliche Ergebnisse: Schleifen, Rekursion und Fehlerbehandlung gehören ebenso dazu wie Formulare, Klassen, Projektgruppen, 100.000 Aufrufe und ein Dauerlauf.

Unter der Haube

Für den laufenden Betrieb puffert der Profiler bis zu 10.000 Logeinträge und schreibt nur den belegten Teil auf die Platte. Am Ende der äußersten profilierten Methode wird sofort geschrieben; verschachtelte Methodenenden werden gesammelt, damit nicht für jeden Unteraufruf eine eigene Logdatei entsteht.

Die Verarbeitung der eingehenden Log-Daten läuft in getrennten Arbeitsschritten für Einlesen, Zuordnung und Auswertung. Zusätzlich werden umfangreiche Aufruflisten und ihre Filteransichten im Hintergrund vorbereitet, sodass die Oberfläche auch bei sehr großen Datenmengen bedienbar bleibt.

Für die Oberfläche kommen ObjectListView für die performanten Baum-/Listenansichten und FastColoredTextBox für die Quellcode-Anzeige mit Syntax-Highlighting zum Einsatz. Ein eigenes VB6-Datenmodell (Projekt → Datei → Methode → Zeile) bildet die komplette Struktur eines VB6-Projekts inklusive der historischen Windows-1252-Zeichenkodierung sauber ab — Umlaute in Bezeichnern wie „Rückgabe“ bleiben dabei korrekt erhalten. Die Konfiguration liegt in schlanken JSON-Dateien, ganz ohne Datenbank-Unterbau.

Fazit

Mit dem VB6 Profiler kannst du Methoden und einzelne Zeilen untersuchen, Ergebnisse über eine Session vergleichen und Messwerte samt Quellcode als KI-Datenpaket exportieren. Die zehn Testprojekte prüfen, dass die Instrumentierung das fachliche Verhalten deiner Anwendung erhält.