Læser man hjælpen til den nye version af SOS, er det første man lægger mærke til nok de mange nye kommandoer, men der er faktisk også meget interessante opdateringer til den eksisterende funktionalitet.
Hjælpeteksten til !u-kommandoen er således blevet opdateret med følgende lille kommentar:
If the debugger has the option SYMOPT_LOAD_LINES specified (either by the.lines or .symopt commands), and if symbols are available for the managed module containing the method being examined, the output of the command will include the source file name and line number corresponding to the disassembly.
Vi har med andre ord fået en smule af funktionaliteten fra den legendariske version 6.7.5 tilbage. Det er desværre ikke en komplet løsning, men det er i hvert fald et seriøst skridt i den rigtige retning.
Heldigvis er det ikke kun !u, der er blevet opdateret. !clrstack viser nu også linjenumre, hvis ovenstående betingelser er på plads. Vi har således fået meget bedre muligheder, for at illustrere sammenhængen den kørende applikation og kildeteksten. Lad os se på et eksempel.
static void SomeMethod(int i) {
SomeSubMethod(i + 1);
}
static void SomeSubMethod(int i) {
var text = string.Format("The answer is {0}", i);
Console.WriteLine(text);
Console.ReadLine();
}
static void Main() {
SomeMethod(41);
}
Sætter vi WinDbg på ved Console.ReadLine(), og udskriver stakken for den relevante tråd, får vi følgende:
0:000> !clrstack OS Thread Id: 0x1b00 (0) Child SP IP Call Site 000000000019e648 00000000772c00da [NDirectMethodFrameStandalone: 000000000019e648] System.IO.__ConsoleStream.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr) 000000000019e5f0 000007feeb1d6311 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30128_64\mscorlib\efe2adda88bca3a9f9bf9cc89514729b\mscorlib.ni.dll 000000000019e710 000007feeb980e7a System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Int32, Int32 ByRef) 000000000019e780 000007feeb980ce2 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) 000000000019e7e0 000007feeb19601c System.IO.StreamReader.ReadBuffer() 000000000019e830 000007feeb194c40 System.IO.StreamReader.ReadLine() 000000000019e890 000007feeb988d90 System.IO.TextReader+SyncTextReader.ReadLine() 000000000019e8f0 000007ff0015023e TestApp.Program.SomeSubMethod(Int32) [C:\dev2010\TestApp\TestApp\Program.cs @ 18] 000000000019e950 000007ff00150199 TestApp.Program.SomeMethod(Int32) [C:\dev2010\TestApp\TestApp\Program.cs @ 12] 000000000019e980 000007ff00150144 TestApp.Program.Main() [C:\dev2010\TestApp\TestApp\Program.cs @ 22] 000000000019ed70 000007feec05cc74 [GCFrame: 000000000019ed70]
Læg mærke til fil- og linjeangivelserne for Main(), SomeMethod() og SomeSubMethod().
Via instruktionspointeren, kan vi vise den oversatte kode for metoderne. Lad os se nærmere på SomeSubMethod().
0:000> !U 000007ff0015023e Normal JIT generated code TestApp.Program.SomeSubMethod(Int32) Begin 000007ff001501c0, size 8b C:\dev2010\TestApp\TestApp\Program.cs @ 15: 000007ff`001501c0 894c2408 mov dword ptr [rsp+8],ecx 000007ff`001501c4 4883ec58 sub rsp,58h 000007ff`001501c8 48c744242000000000 mov qword ptr [rsp+20h],0 000007ff`001501d1 48b8d8340300ff070000 mov rax,7FF000334D8h 000007ff`001501db 8b00 mov eax,dword ptr [rax] 000007ff`001501dd 85c0 test eax,eax 000007ff`001501df 7405 je 000007ff`001501e6 000007ff`001501e1 e88ada33ec call clr!JIT_DbgIsJustMyCode (000007fe`ec48dc70) 000007ff`001501e6 90 nop C:\dev2010\TestApp\TestApp\Program.cs @ 16: 000007ff`001501e7 48b85030591200000000 mov rax,12593050h 000007ff`001501f1 488b00 mov rax,qword ptr [rax] 000007ff`001501f4 4889442430 mov qword ptr [rsp+30h],rax 000007ff`001501f9 8b442460 mov eax,dword ptr [rsp+60h] 000007ff`001501fd 89442428 mov dword ptr [rsp+28h],eax 000007ff`00150201 488d0d888a15eb lea rcx,[mscorlib_ni+0x4f8c90 (000007fe`eb2a8c90)] 000007ff`00150208 488d542428 lea rdx,[rsp+28h] 000007ff`0015020d e89e29edeb call clr!JIT_BoxFastMP_InlineGetThread (000007fe`ec022bb0) 000007ff`00150212 488bd0 mov rdx,rax 000007ff`00150215 488b4c2430 mov rcx,qword ptr [rsp+30h] 000007ff`0015021a e8116a01eb call mscorlib_ni+0x3b6c30 (000007fe`eb166c30) (System.String.Format(System.String, System.Object), mdToken: 0000000001C9C090) 000007ff`0015021f 4889442438 mov qword ptr [rsp+38h],rax 000007ff`00150224 488b442438 mov rax,qword ptr [rsp+38h] 000007ff`00150229 4889442420 mov qword ptr [rsp+20h],rax C:\dev2010\TestApp\TestApp\Program.cs @ 17: 000007ff`0015022e 488b4c2420 mov rcx,qword ptr [rsp+20h] 000007ff`00150233 e8f80903eb call mscorlib_ni+0x3d0c30 (000007fe`eb180c30) (System.Console.WriteLine(System.String), mdToken: 0000000001C9C090) 000007ff`00150238 90 nop C:\dev2010\TestApp\TestApp\Program.cs @ 18: 000007ff`00150239 e862886beb call mscorlib_ni+0xa58aa0 (000007fe`eb808aa0) (System.Console.ReadLine(), mdToken: 0000000001C9C090) >>> 000007ff`0015023e 4889442440 mov qword ptr [rsp+40h],rax 000007ff`00150243 90 nop C:\dev2010\TestApp\TestApp\Program.cs @ 19: 000007ff`00150244 eb00 jmp 000007ff`00150246 000007ff`00150246 4883c458 add rsp,58h 000007ff`0015024a c3 ret
Som sædvanlig får vi den JIT-oversatte kode tilsat CLR-kommentarer, men læg mærke til, at de enkelte kodeblokke nu indledes med en reference til den relevante linje i kildeteksten. Det gør det meget lettere, at skabe sammenhæng mellem den JIT-oversatte maskinekode og den oprindelige kildetekst.
Men vent! Der er mere!
Yderligere granskning af hjælpeteksten opsporer følgende lille, interessante notits i slutningen af FAQ-sektionen.
Does SOS support DML?
Yes. SOS respects the .prefer_dml option in the debugger. If this setting is turned on, then SOS will output DML by default. Alternatively, you may leave it off and add /D to the beginning of a command to get DML based output for it. Not all SOS commands support DML output.
Så SOS understøtter nu DML. Fint, men hvad er pokker DML?
DML står for debugger markup language. Det er ikke en ny feature, den har været der siden version 6.6.07, men det er en forholdsvis velbevaret hemmelighed. Således er der kun en enkelt reference til DML i hjælpeteksten og ingen uddybning. Den eneste information, jeg har fundet, er et dokument ved navn dml.doc i WinDbg installationsbibliotek.
Nyheden er altså ikke DML i sig selv, men at SOS nu understøtter DML.
Heldigvis kræver DML ikke den store forklaring. I en nøddeskal er DML et simpelt markup-sprog, der kan bruges til at tilføre yderligere information til resultatet af debugger-kommandoer. Som FAQen nævner, er flere af SOS-kommandoerne blevet udstyret med DML, så hvis vi slår DML-visning til, får vi altså yderligere funktionalitet i form af links til relevante kommandoer i forhold til det aktuelle output.
Kører vi eksempelvis !dumpobj, får vi, som vist nedenfor, links til flere relaterede opslag. Det gør en hel del arbejdsgange meget lettere.

Effekten af de enkelte links kan aflures nederst til venstre i WinDbgs statusbar.
Udokumenterede kommandoer og forkortelser
Udover den umiddelbare reduktion af tastearbejdet giver DML også indblik i yderligere features. Jeg opdagede således .extmatch-kommandoen, da jeg kørte .chain med DML slået til.
.extmatch viser alle eksporterede kommandoer fra en given extension. Kører vi den på SOS, dukker der adskillige udokumenterede kommandoer op som f.eks. HandleCLRN, SOSFlush, VerifyStackTrace og WatsonBuckets. Uden dokumentation er det svært, at få noget fornuftig ud af disse. SOSFlush er godt nok beskrevet på MSDN, men det er ikke fordi, jeg blev meget klogere af det.
Derudover fandt jeg også en del udokumenterede forkortelser, jeg ikke kendte. De er som følger:
!da for !dumparray
!hof for !histobjfind
!t for !threads
!tp for !threadpool
!vh for !verifyheap
!vo for !verifyobj
Det eneste problem, jeg har opdaget med DML indtil videre, er, at mdTokens af en eller anden grund linker til !u med et ugyldigt -md-parameter. Jeg håber, at det bliver rettet inden release.