Flere gode debugging-nyheder i CLR 4

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.

DML i SOS

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.

Leave a Reply