In diesem Blogpost beschreibe ich jetzt mal, wie man für den Debugger von Visual Studio verhindern kann, dass bestimmte Symbole geladen werden. Mein Problem ist, dass ich immer wieder vergesse, wie der Registry Key heisst, den man da anlegen/bearbeiten muss, und welche DLLs ich da am Besten eintrage, und dieses Problem wiederholt sich bei mir bei jeder Entwicklungsbox, die ich aufsetze, insofern bin ich mit diesem Blogpost mein eigenes und bestes Zielpublikum.
Das Problem
Wenn man einen Pfad zum Laden von Symbolen gesetzt hat, dann ist das normalerweise eine schöne Sache. Der Debugger versucht, von jeder DLL im Prozessraum des Debuggees die Symbole zu laden, und man erhält zumindest schöne Callstacks beim Debuggen. Bei einem Symbol Store für die eigenen Binaries sieht man auch lokale Variablen und ihre Werte sowie den dazugehörigen Code, notfalls automatisch ausgecheckt aus dem Versionskontrollsystem (wie man sich sowas aufbaut, habe ich vor langer Zeit hier beschrieben). Damit ist die Welt schön, denn beispielsweise von den DLLs des Betriebssystems bekommt man die Symbole automatisiert von Microsofts Symbol Server runtergeladen.
Blöd wird das Ganze aber, wenn im Prozessraum eine DLL auftaucht, für die man partout keine Symbole bekommt, denn der zum Scheitern verurteilte Versuch, für solche DLLs die Symbole zu finden und zu laden, braucht sehr viel Zeit, und das Leben ist bekanntlich ja schon kurz genug. Und beim Debuggen warten zu müssen ist eine PITA. Historisch zählten dazu schon ab und zu mal DLLs von Microsoft, aber notorisch bekannt dafür sind eigentlich die Hersteller von Grafikkarten, die es fertig bringen, in jeden Prozessraum ihre verschissenen DLLs zu injizieren, oder aber irgendwelche third-party Shell-Extensions. Da ich zuhause bekannterweise einen Subversion-Server einsetze und auf meinen Entwicklungsboxen daher das wunderbare Tortoise-SVN zum Einsatz kommt, ist Tortoise-SVN ein wunderbares Beispiel für dieses Problem. Am Besten manifestiert sich das Problem, wenn man beim Debuggen eine File-Open Box öffnet. Dann wird alles an Shell-Extensions geladen, was nicht bei drei auf den Bäumen ist, unter anderem auch Tortoise-SVN. Auf meiner guten alten SAAVIK, einer x64-Box von 2005, dauert das dann 22s im Debugger, bis die File-Open Box dargestellt und bedienbar ist. Der Debugger schaut für jede zu ladenden DLL in das Binary rein und versucht, das Symbol, also die pdb-Datei, zu ermitteln und dann zu laden. Wenn er sie nicht über einen absoluten Pfad findet, klappert er der Reihe nach die konfigurierten Symbol Stores ab und das dauert dann üblicherweise jedesmal ein paar Sekunden, bis er bei der Nachfrage beim Microsoft Symbol Store (oder dem eigenen Symbol Store) die Antwort bekommt: "Nein die Datei TortoiseSVN.pdb ist nicht von uns".
Die Lösung
Die Lösung besteht darin, diejenigen DLLs zu identifizieren, für die man ohnehin keine Symbole bekommt, und die dann von vornherein vom Laden auszuschliessen. Wenn Symbole lokal gefunden werden, dann sieht man sowas im Debugger-Output:
'abcd.exe': Loaded 'C:\foobar\bin\Debug\xyz.dll', Symbols loaded.
oder aber, wenn die Symbole beispielsweise vom MS Symbol Store kommen (und daher die Debuginformationen 'stripped' sind, also nur Funktionsnamen beinhalten):
'
abcd
.exe': Loaded 'C:\Windows\SysWOW64\ntdll.dll', Symbols loaded (source information stripped).
Wenn aber die Symbole nicht gefunden werden, dann sieht das lediglich so aus (also ohne irgendwas mit "Symbols loaded"):
'abcd.exe': Loaded 'C:\Program Files (x86)\TortoiseSVN\bin\TortoiseSVN.dll'
Es gilt also, diese DLLs im Debugger-Output zu identifizieren und deren Symboldateien zu ermitteln. In der Regel heissen die Symboldateien so wie die DLLs, nur mit der Extension .pdb. Um aber auf Nummer Sicher zu gehen, schaut man sich jede DLL in einem Hexeditor an und sucht nach dem String ".pdb". Üblicherweise erhält man nur einen Treffer und das ist dann der Name der Symboldatei, nach der der Debugger sucht. Hat man den Namen der Datei, dann legt man den Key HKEY_CURRENT_USER\Software\Microsoft\Symbol Server\Exclusions an (sofern der noch nicht existiert) und ergänzt dort einen REG_SZ-Value mit dem Namen der Symboldatei. Bei mir schaut das dann beispielsweise so aus:
[HKEY_CURRENT_USER\Software\Microsoft\Symbol Server\Exclusions]
"TortoiseOverlays.pdb"=""
"TortoiseStub.pdb"=""
"TortoiseSVN.pdb"=""
"libapr_tsvn.pdb"=""
"libaprutil_tsvn.pdb"=""
"intl3_tsvn.pdb"=""
"slc.pdb"=""
"WMVCORE.pdb"=""
"SPYHK55.pdb"=""
"TortoiseShell.pdb"=""
"libsvn_tsvn32.pdb"=""
"intl3_tsvn32.pdb"=""
"libsasl.pdb"=""
Wenn man damit dann fertig ist, beendet man Visual Studio und startet es neu, und ab jetzt werden die angegebenen Symbole ignoriert und man kann pfeilschnell debuggen. Auf SAAVIK dauert damit das Öffnen einer File-Open Box dann unter 5s.
Als Referenz ist ein Regfile mit meinen Einstellungen von oben hier zu finden. Einfach die Datei in die lokale Registry reinmergen und dann Rock'n Roll. Debuggt man dann unter einem anderen User muss man das dann für diesen User auch nochmal tun.
Happy debugging and symbol loading, you know, I am.
Update (03/14/2013): Registry-Skript und dessen Listing angepasst für die aktuelle Version des Tortoise-SVN-Clients