dotnet / diagnostics

This repository contains the source code for various .NET Core runtime diagnostic tools and documents.
MIT License
1.18k stars 354 forks source link

Complete the !u -il SOS command #452

Closed cshung closed 5 years ago

cshung commented 5 years ago

I have bootstrapped the work to allow us to view IL interleaved with disassembly. My initial PR should have addressed the data source problem. The rest is simply code refactoring by following an existing implementation.

cshung commented 5 years ago

This comment is meant to explain the work better for @neeraj9, which is also generally applicable for readers who wanted to contribute do not know how to get started.

To get started, it is helpful to explain what this work is for. This work is an enhancement of the dotnet-sos tool used for debugging .NET Core applications. Here is a brief instruction on how to get started.

Since we are working on the debugger (technically, just a debugger extension), we do not need to change .NET Core itself, which means we can simply install one online. Here is a link for downloading .NET Core.

We will need a build for this repo, to build this repo, simply use the Build.cmd command.

This description assumes the work is done on Windows.

Once .NET Core is installed, we can create a new HelloWorld project as follow:

mkdir c:\HelloWorld
cd c:\HelloWorld
dotnet new console

This creates a HelloWorld console application. To make it easier to work with, we modify the code as follow:

using System;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("What is your name? ");
            string name = Console.ReadLine();
            Console.WriteLine("Hello " + name);
        }
    }
}

There is nothing special with this code, it is just that the program will pause at the beginning, giving us opportunity to use the debugger.

It should be great forward to try it out:

dotnet publish --self-contained -r win-x64

And it should show: (make sure you use a full path instead of the ...)

...\HelloWorld.exe
What is your name? Andrew
Hello Andrew

Next, we wanted to debug it. To do that, we install the Debugger Tools for Windows.

After installation, we can debug the process as follow:

windbg ...\HeloWorld.exe

The debugger windows should pop up with a command window, just hit g (stands for go) to continue executing the process, the process would launch and then stop at the prompt.

Now pause the process using the pause button on the menu bar, the process should stop. Note that by default the process stops on the remote break-in thread, we need to switch it back to the main thread:

0:007> ~0s

The ~ denotes thread, 0 is the id of the main thread, s stands for switch. The 007 at the prompt is meant to indicate the current thread is thread number 7.

So far, these are all capabilities of the Debugger Tools for Windows. Next, we want to exercise our own code. The component we are using here is named Son of Strike, or sos for short. It is an extension of the Debugger Tools for Windows. By default, the installation comes with a version of it, and we want to use our, therefore, we will unload the one come with the installation first by typing this into the command window:

0:000> .unload sos

Next, we want to load our own, so we type this into the command window:

0:000> .load C:\Dev\diagnostics\artifacts\bin\Windows_NT.x64.Debug\sos.dll

Once we are using our own version, we can inspect the stack using !clrstack, we should see something like this:


0:000> !clrstack
OS Thread Id: 0x1a0c (0)
        Child SP               IP Call Site
000000B22397E638 00007ffae675c124 [InlinedCallFrame: 000000b22397e638] Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)
000000B22397E638 00007ffa45411d28 [InlinedCallFrame: 000000b22397e638] Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)
000000B22397E600 00007ffa45411d28 ILStubClass.IL_STUB_PInvoke(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)
000000B22397E6D0 00007ffadfa68d76 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, Byte[], Int32, Int32, Boolean, Int32 ByRef, Boolean)
000000B22397E730 00007ffadfa68cc3 System.ConsolePal+WindowsConsoleStream.Read(Byte[], Int32, Int32)
000000B22397E7A0 00007ffa9fb04e14 System.IO.StreamReader.ReadBuffer() [/_/src/System.Private.CoreLib/shared/System/IO/StreamReader.cs @ 613]
000000B22397E7F0 00007ffa9fb1c3ce System.IO.StreamReader.ReadLine() [/_/src/System.Private.CoreLib/shared/System/IO/StreamReader.cs @ 784]
000000B22397E840 00007ffadfa6d173 System.IO.SyncTextReader.ReadLine()
000000B22397E890 00007ffadfa6533d System.Console.ReadLine()
000000B22397E8C0 00007ffa45410663 HelloWorld.Program.Main(System.String[]) [C:\HelloWorld\Program.cs @ 10]
000000B22397EAF8 00007ffaa4ef9ac3 [GCFrame: 000000b22397eaf8] 
000000B22397F0A0 00007ffaa4ef9ac3 [GCFrame: 000000b22397f0a0] 

As we are curious, we wanted to look at the disassembly our own Main method, we can do that by clicking on the link next to the Main method, as it should show this:

0:000> !U /d 00007ffa45410663
Normal JIT generated code
HelloWorld.Program.Main(System.String[])
Begin 00007FFA45410620, size 7a

C:\HelloWorld\Program.cs @ 8:
00007ffa`45410620 55              push    rbp
00007ffa`45410621 4883ec40        sub     rsp,40h
00007ffa`45410625 488d6c2440      lea     rbp,[rsp+40h]
00007ffa`4541062a 33c0            xor     eax,eax
00007ffa`4541062c 488945f8        mov     qword ptr [rbp-8],rax
00007ffa`45410630 488945f0        mov     qword ptr [rbp-10h],rax
00007ffa`45410634 488945e8        mov     qword ptr [rbp-18h],rax
00007ffa`45410638 48894d10        mov     qword ptr [rbp+10h],rcx
00007ffa`4541063c 833de562090000  cmp     dword ptr [00007ffa`454a6928],0
00007ffa`45410643 7405            je      HelloWorld!HelloWorld.Program.Main(System.String[])+0x2a (00007ffa`4541064a)
00007ffa`45410645 e8f6f2c15f      call    coreclr!JIT_DbgIsJustMyCode (00007ffa`a502f940)
00007ffa`4541064a 90              nop

C:\HelloWorld\Program.cs @ 9:
00007ffa`4541064b 48b9c030c16072020000 mov rcx,27260C130C0h
00007ffa`45410655 488b09          mov     rcx,qword ptr [rcx]
00007ffa`45410658 e89bffffff      call    CLRStub[MethodDescPrestub]@7ffa454105f8 (00007ffa`454105f8) (System.Console.Write(System.String), mdToken: 0000000006000096)
00007ffa`4541065d 90              nop

C:\HelloWorld\Program.cs @ 10:
00007ffa`4541065e e87dfeffff      call    CLRStub[MethodDescPrestub]@7ffa454104e0 (00007ffa`454104e0) (System.Console.ReadLine(), mdToken: 0000000006000073)
>>> 00007ffa`45410663 488945f0        mov     qword ptr [rbp-10h],rax
00007ffa`45410667 488b4df0        mov     rcx,qword ptr [rbp-10h]
00007ffa`4541066b 48894df8        mov     qword ptr [rbp-8],rcx

C:\HelloWorld\Program.cs @ 11:
00007ffa`4541066f 48b9c830c16072020000 mov rcx,27260C130C8h
00007ffa`45410679 488b09          mov     rcx,qword ptr [rcx]
00007ffa`4541067c 488b55f8        mov     rdx,qword ptr [rbp-8]
00007ffa`45410680 e8533affff      call    CLRStub[MethodDescPrestub]@7ffa454040d8 (00007ffa`454040d8) (System.String.Concat(System.String, System.String), mdToken: 0000000006000703)
00007ffa`45410685 488945e8        mov     qword ptr [rbp-18h],rax
00007ffa`45410689 488b4de8        mov     rcx,qword ptr [rbp-18h]
00007ffa`4541068d e8befeffff      call    CLRStub[MethodDescPrestub]@7ffa45410550 (00007ffa`45410550) (System.Console.WriteLine(System.String), mdToken: 0000000006000081)
00007ffa`45410692 90              nop

C:\HelloWorld\Program.cs @ 12:
00007ffa`45410693 90              nop
00007ffa`45410694 488d6500        lea     rsp,[rbp]
00007ffa`45410698 5d              pop     rbp
00007ffa`45410699 c3              ret

Note that the disassembly is interleaved with the source code line number, which is good.

The debugger extension also support viewing the MSIL of the method, this is a little more involved. To do that, we want to go from an IP address to a MethodDesc.

0:000> !ip2md 00007ffa45410663
MethodDesc:   00007ffa454a82b0
Method Name:          HelloWorld.Program.Main(System.String[])
Class:                00007ffa454b6e60
MethodTable:          00007ffa454a82c8
mdToken:              0000000006000001
Module:               00007ffa454a6688
IsJitted:             yes
Current CodeAddr:     00007ffa45410620
Version History:
  NativeCodeVersion:  0000000000000000
    ReJIT ID:           0
    CodeAddr:           00007ffa45410620  (MinOptJitted)
    IL Addr:            0000000000000000
    ILCodeVersion:      0000000000000000
Source file:  C:\HelloWorld\Program.cs @ 10

Once we get to the MethodDesc, then we can display the MSIL

0:000> !DumpIL 00007ffa454a82b0
ilAddr = 000002724F0F2048
IL_0000: nop 
IL_0001: ldstr "What is your name? "
IL_0006: call void System.Console::Write(string)
IL_000b: nop 
IL_000c: call string System.Console::ReadLine()
IL_0011: stloc.0 
IL_0012: ldstr "Hello "
IL_0017: ldloc.0 
IL_0018: call string System.String::Concat(string,string)
IL_001d: call void System.Console::WriteLine(string)
IL_0022: nop 
IL_0023: ret 

Now we have two views of the same code. Ideally, we would like to see them interleaved. This is particularly helpful for the compiler team to inspect if the generated code is correct.

Given just two streams of text, we don't know how to interleave them, this is where my last PR comes in handy. If we specify -il in the !U command invoked earlier, I outputted a map between the two:

0:000> !U -il 00007ffa45410663
Normal JIT generated code
HelloWorld.Program.Main(System.String[])
fffffffe 00007FFA45410620 00007FFA4541063C
ffffffff 00007FFA4541063C 00007FFA4541064A
0000 00007FFA4541064A 00007FFA4541064B
0001 00007FFA4541064B 00007FFA45410658
0006 00007FFA45410658 00007FFA4541065D
000b 00007FFA4541065D 00007FFA4541065E
000c 00007FFA4541065E 00007FFA4541065E
000c 00007FFA4541065E 00007FFA45410667
0011 00007FFA45410667 00007FFA4541066F
0012 00007FFA4541066F 00007FFA45410680
0018 00007FFA45410680 00007FFA45410689
001d 00007FFA45410689 00007FFA4541068D
001d 00007FFA4541068D 00007FFA45410692
0022 00007FFA45410692 00007FFA45410693
0023 00007FFA45410693 00007FFA45410694
fffffffd 00007FFA45410694 00007FFA45410620
Begin 00007FFA45410620, size 7a

C:\HelloWorld\Program.cs @ 8:
00007ffa`45410620 55              push    rbp
00007ffa`45410621 4883ec40        sub     rsp,40h
00007ffa`45410625 488d6c2440      lea     rbp,[rsp+40h]
00007ffa`4541062a 33c0            xor     eax,eax
00007ffa`4541062c 488945f8        mov     qword ptr [rbp-8],rax
00007ffa`45410630 488945f0        mov     qword ptr [rbp-10h],rax
00007ffa`45410634 488945e8        mov     qword ptr [rbp-18h],rax
00007ffa`45410638 48894d10        mov     qword ptr [rbp+10h],rcx
00007ffa`4541063c 833de562090000  cmp     dword ptr [00007ffa`454a6928],0
00007ffa`45410643 7405            je      HelloWorld!HelloWorld.Program.Main(System.String[])+0x2a (00007ffa`4541064a)
00007ffa`45410645 e8f6f2c15f      call    coreclr!JIT_DbgIsJustMyCode (00007ffa`a502f940)
00007ffa`4541064a 90              nop

C:\HelloWorld\Program.cs @ 9:
00007ffa`4541064b 48b9c030c16072020000 mov rcx,27260C130C0h
00007ffa`45410655 488b09          mov     rcx,qword ptr [rcx]
00007ffa`45410658 e89bffffff      call    CLRStub[MethodDescPrestub]@7ffa454105f8 (00007ffa`454105f8) (System.Console.Write(System.String), mdToken: 0000000006000096)
00007ffa`4541065d 90              nop

C:\HelloWorld\Program.cs @ 10:
00007ffa`4541065e e87dfeffff      call    CLRStub[MethodDescPrestub]@7ffa454104e0 (00007ffa`454104e0) (System.Console.ReadLine(), mdToken: 0000000006000073)
>>> 00007ffa`45410663 488945f0        mov     qword ptr [rbp-10h],rax
00007ffa`45410667 488b4df0        mov     rcx,qword ptr [rbp-10h]
00007ffa`4541066b 48894df8        mov     qword ptr [rbp-8],rcx

C:\HelloWorld\Program.cs @ 11:
00007ffa`4541066f 48b9c830c16072020000 mov rcx,27260C130C8h
00007ffa`45410679 488b09          mov     rcx,qword ptr [rcx]
00007ffa`4541067c 488b55f8        mov     rdx,qword ptr [rbp-8]
00007ffa`45410680 e8533affff      call    CLRStub[MethodDescPrestub]@7ffa454040d8 (00007ffa`454040d8) (System.String.Concat(System.String, System.String), mdToken: 0000000006000703)
00007ffa`45410685 488945e8        mov     qword ptr [rbp-18h],rax
00007ffa`45410689 488b4de8        mov     rcx,qword ptr [rbp-18h]
00007ffa`4541068d e8befeffff      call    CLRStub[MethodDescPrestub]@7ffa45410550 (00007ffa`45410550) (System.Console.WriteLine(System.String), mdToken: 0000000006000081)
00007ffa`45410692 90              nop

C:\HelloWorld\Program.cs @ 12:
00007ffa`45410693 90              nop
00007ffa`45410694 488d6500        lea     rsp,[rbp]
00007ffa`45410698 5d              pop     rbp
00007ffa`45410699 c3              ret

Ignoring the first two entries, the first number corresponds to the MSIL offset (i.e. the IL_000x) numbers, the second and third number is an [inclusive, exclusive) IP addresses that implements the MSIL instruction. With some effort to refactor the code, we should be able to fit these 3 pieces together to produce an interleaved view.

Down to the code, the !u command is implemented https://github.com/dotnet/diagnostics/blob/3ca946b4f177178942648119e2a270735eb3dca3/src/SOS/Strike/strike.cpp#L9184. In general, all these ! commands are simply implemented in the corresponding DECLARE_API() block, so it should be easy to find them.

Last but not least, interleaving information with the disassembly is already attempted before, and we have a precursor to follow, check out the fWithGCInfo code path to see how that is done. I expect a relatively similar code is possible for interleaving the IL.

This is a long description and a lot of things could go wrong, please do not hesitate to let me know if you need help.

neeraj9 commented 5 years ago

Thanks @cshung for the detailed write-up. This was very helpful in getting things up and running quickly. I missed the build step for diagnostics project but that was an oversight (commonsense) anyways since I was shamelessly following your steps :) Although, I reproduce all of your steps it is not entirely clear as to how the commands should interleave? Are you expecting that the command "!U -il {address}" should interleave the Intermediate Language (MSIL) while displaying the assembly for the function something like this?

...
C:\HelloWorld\Program.cs @ 9:
IL_0001: ldstr "What is your name? "
00007ffa`4541064b 48b9c030c16072020000 mov rcx,27260C130C0h
00007ffa`45410655 488b09          mov     rcx,qword ptr [rcx]
IL_0006: call void System.Console::Write(string)
00007ffa`45410658 e89bffffff      call    CLRStub[MethodDescPrestub]@7ffa454105f8 (00007ffa`454105f8) (System.Console.Write(System.String), mdToken: 0000000006000096)
IL_000b: nop
00007ffa`4541065d 90              nop
...

Additionally, are you alright with refactoring something like the following? The function is awfully long for me to fit into a single page.

https://gist.github.com/neeraj9/054d90f6ebb91ac636f2a70d37a3795d

cshung commented 5 years ago

@neeraj9 I like the sample output - perhaps with a few blank lines to make it easier to read.

To achieve that, you need the strings like IL_0001: ldstr "What is your name? " to be available when you wanted to output it. In order to obtain them, you need to get it from DecodeIL here. The code is currently doing a lot of printf, which goes directly to the console. We would like to capture them so that it will be shown at the right time. That is the refactoring I was referring to.

neeraj9 commented 5 years ago

@cshung before I proceed with any more refactoring, take a look at some of my refactoring at https://github.com/neeraj9/diagnostics/commits/dev_il_with_disassem

cshung commented 5 years ago

@neeraj9 I read the code, it seems fine to me. Please open a PR to us so that we can have the system to test the change. Thanks.

neeraj9 commented 5 years ago

I am still not done yet. Let me look at the DecodeIL and interleave and get back.

neeraj9 commented 5 years ago

My changes work finally although I have hacked my way around so the code changes may not be very clean and definitely need to understand some of the details. @cshung let me know if you like the output.

0:009> ~0s
ntdll!NtReadFile+0x14:
00007ffd`e25aaa64 c3              ret

0:000> .load C:/Users/neerajs/source/repos/diagnostics/artifacts/bin/Windows_NT.x64.Debug/./sos.dll

0:000> !clrstack
OS Thread Id: 0x802c (0)
        Child SP               IP Call Site
000000458477E608 00007ffde25aaa64 [InlinedCallFrame: 000000458477e608] Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)
000000458477E608 00007ffd07562888 [InlinedCallFrame: 000000458477e608] Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)
000000458477E5D0 00007ffd07562888 ILStubClass.IL_STUB_PInvoke(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr)
000000458477E6A0 00007ffdd3ae8d76 System.ConsolePal+WindowsConsoleStream.ReadFileNative(IntPtr, Byte[], Int32, Int32, Boolean, Int32 ByRef, Boolean)
000000458477E700 00007ffdd3ae8cc3 System.ConsolePal+WindowsConsoleStream.Read(Byte[], Int32, Int32)
000000458477E770 00007ffd65694e14 System.IO.StreamReader.ReadBuffer() [/_/src/System.Private.CoreLib/shared/System/IO/StreamReader.cs @ 613]
000000458477E7C0 00007ffd656ac3ce System.IO.StreamReader.ReadLine() [/_/src/System.Private.CoreLib/shared/System/IO/StreamReader.cs @ 784]
000000458477E810 00007ffdd3aed173 System.IO.SyncTextReader.ReadLine()
000000458477E860 00007ffdd3ae533d System.Console.ReadLine()
000000458477E890 00007ffd07560f93 hello.Program.Main(System.String[]) [C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 10]
000000458477EAC8 00007ffd67059ac3 [GCFrame: 000000458477eac8] 
000000458477F070 00007ffd67059ac3 [GCFrame: 000000458477f070] 
0:000> !U /d 00007ffd07560f93
Normal JIT generated code
hello.Program.Main(System.String[])
ilAddr = 000001FA02B62048
Begin 00007FFD07560F50, size 7a

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 8:
00007ffd`07560f50 55              push    rbp
00007ffd`07560f51 4883ec40        sub     rsp,40h
00007ffd`07560f55 488d6c2440      lea     rbp,[rsp+40h]
00007ffd`07560f5a 33c0            xor     eax,eax
00007ffd`07560f5c 488945f8        mov     qword ptr [rbp-8],rax
00007ffd`07560f60 488945f0        mov     qword ptr [rbp-10h],rax
00007ffd`07560f64 488945e8        mov     qword ptr [rbp-18h],rax
00007ffd`07560f68 48894d10        mov     qword ptr [rbp+10h],rcx
00007ffd`07560f6c 833d9dea090000  cmp     dword ptr [00007ffd`075ffa10],0
00007ffd`07560f73 7405            je      00007ffd`07560f7a
00007ffd`07560f75 e8c6e9c25f      call    coreclr!JIT_DbgIsJustMyCode (00007ffd`6718f940)
00007ffd`07560f7a 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 9:
00007ffd`07560f7b 48b9c0306f14fa010000 mov rcx,1FA146F30C0h
00007ffd`07560f85 488b09          mov     rcx,qword ptr [rcx]
00007ffd`07560f88 e8cbf5ffff      call    00007ffd`07560558 (System.Console.WriteLine(System.String), mdToken: 0000000006000081)
00007ffd`07560f8d 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 10:
00007ffd`07560f8e e835ffffff      call    00007ffd`07560ec8 (System.Console.ReadLine(), mdToken: 0000000006000073)
>>> 00007ffd`07560f93 488945f0        mov     qword ptr [rbp-10h],rax
00007ffd`07560f97 488b4df0        mov     rcx,qword ptr [rbp-10h]
00007ffd`07560f9b 48894df8        mov     qword ptr [rbp-8],rcx

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 11:
00007ffd`07560f9f 48b9c8306f14fa010000 mov rcx,1FA146F30C8h
00007ffd`07560fa9 488b09          mov     rcx,qword ptr [rcx]
00007ffd`07560fac 488b55f8        mov     rdx,qword ptr [rbp-8]
00007ffd`07560fb0 e8eb35ffff      call    00007ffd`075545a0 (System.String.Concat(System.String, System.String), mdToken: 0000000006000703)
00007ffd`07560fb5 488945e8        mov     qword ptr [rbp-18h],rax
00007ffd`07560fb9 488b4de8        mov     rcx,qword ptr [rbp-18h]
00007ffd`07560fbd e896f5ffff      call    00007ffd`07560558 (System.Console.WriteLine(System.String), mdToken: 0000000006000081)
00007ffd`07560fc2 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 12:
00007ffd`07560fc3 90              nop
00007ffd`07560fc4 488d6500        lea     rsp,[rbp]
00007ffd`07560fc8 5d              pop     rbp
00007ffd`07560fc9 c3              ret

0:000> !u -il 00007ffd07560f93
Normal JIT generated code
hello.Program.Main(System.String[])
fffffffe 00007FFD07560F50 00007FFD07560F6C
ffffffff 00007FFD07560F6C 00007FFD07560F7A
0000 00007FFD07560F7A 00007FFD07560F7B
0001 00007FFD07560F7B 00007FFD07560F88
0006 00007FFD07560F88 00007FFD07560F8D
000b 00007FFD07560F8D 00007FFD07560F8E
000c 00007FFD07560F8E 00007FFD07560F8E
000c 00007FFD07560F8E 00007FFD07560F97
0011 00007FFD07560F97 00007FFD07560F9F
0012 00007FFD07560F9F 00007FFD07560FB0
0018 00007FFD07560FB0 00007FFD07560FB9
001d 00007FFD07560FB9 00007FFD07560FBD
001d 00007FFD07560FBD 00007FFD07560FC2
0022 00007FFD07560FC2 00007FFD07560FC3
0023 00007FFD07560FC3 00007FFD07560FC4
fffffffd 00007FFD07560FC4 00007FFD07560F50
ilAddr = 000001FA02B62048
Begin 00007FFD07560F50, size 7a

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 8:
00007ffd`07560f50 55              push    rbp
00007ffd`07560f51 4883ec40        sub     rsp,40h
00007ffd`07560f55 488d6c2440      lea     rbp,[rsp+40h]
00007ffd`07560f5a 33c0            xor     eax,eax
00007ffd`07560f5c 488945f8        mov     qword ptr [rbp-8],rax
00007ffd`07560f60 488945f0        mov     qword ptr [rbp-10h],rax
00007ffd`07560f64 488945e8        mov     qword ptr [rbp-18h],rax
00007ffd`07560f68 48894d10        mov     qword ptr [rbp+10h],rcx
00007ffd`07560f6c 833d9dea090000  cmp     dword ptr [00007ffd`075ffa10],0
00007ffd`07560f73 7405            je      00007ffd`07560f7a
00007ffd`07560f75 e8c6e9c25f      call    coreclr!JIT_DbgIsJustMyCode (00007ffd`6718f940)

IL_0000: nop 
00007ffd`07560f7a 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 9:

IL_0001: ldstr "Hello there! What is your name? "
00007ffd`07560f7b 48b9c0306f14fa010000 mov rcx,1FA146F30C0h
00007ffd`07560f85 488b09          mov     rcx,qword ptr [rcx]

IL_0006: call void System.Console::WriteLine(string)
00007ffd`07560f88 e8cbf5ffff      call    00007ffd`07560558 (System.Console.WriteLine(System.String), mdToken: 0000000006000081)

IL_000b: nop 
00007ffd`07560f8d 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 10:

IL_000c: call string System.Console::ReadLine()
00007ffd`07560f8e e835ffffff      call    00007ffd`07560ec8 (System.Console.ReadLine(), mdToken: 0000000006000073)
>>> 00007ffd`07560f93 488945f0        mov     qword ptr [rbp-10h],rax

IL_0011: stloc.0 
00007ffd`07560f97 488b4df0        mov     rcx,qword ptr [rbp-10h]
00007ffd`07560f9b 48894df8        mov     qword ptr [rbp-8],rcx

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 11:

IL_0012: ldstr "Hello "
00007ffd`07560f9f 48b9c8306f14fa010000 mov rcx,1FA146F30C8h
00007ffd`07560fa9 488b09          mov     rcx,qword ptr [rcx]
00007ffd`07560fac 488b55f8        mov     rdx,qword ptr [rbp-8]

IL_0018: call string System.String::Concat(string,string)
00007ffd`07560fb0 e8eb35ffff      call    00007ffd`075545a0 (System.String.Concat(System.String, System.String), mdToken: 0000000006000703)
00007ffd`07560fb5 488945e8        mov     qword ptr [rbp-18h],rax

IL_001d: call void System.Console::WriteLine(string)
00007ffd`07560fb9 488b4de8        mov     rcx,qword ptr [rbp-18h]

IL_001d: call void System.Console::WriteLine(string)
00007ffd`07560fbd e896f5ffff      call    00007ffd`07560558 (System.Console.WriteLine(System.String), mdToken: 0000000006000081)

IL_0022: nop 
00007ffd`07560fc2 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 12:

IL_0023: ret 
00007ffd`07560fc3 90              nop
00007ffd`07560fc4 488d6500        lea     rsp,[rbp]
00007ffd`07560fc8 5d              pop     rbp
00007ffd`07560fc9 c3              ret
neeraj9 commented 5 years ago

The previous version does not take care of reordered instructions and additionally any optimisations done by compiler. Note that not all IL instructions can be mapped to debug info so even that needs to taken into account. Thanks @cshung for working this with me. The following is the updated output after some fixes.

0:000> !u -il 00007ffd03bc0f9e
Normal JIT generated code
hello.Program.Main(System.String[])
fffffffe 00007FFD03BC0F50 00007FFD03BC0F77
ffffffff 00007FFD03BC0F77 00007FFD03BC0F85
0000 00007FFD03BC0F85 00007FFD03BC0F86
0001 00007FFD03BC0F86 00007FFD03BC0F93
0006 00007FFD03BC0F93 00007FFD03BC0F98
000b 00007FFD03BC0F98 00007FFD03BC0F99
000c 00007FFD03BC0F99 00007FFD03BC0F99
000c 00007FFD03BC0F99 00007FFD03BC0FA2
0011 00007FFD03BC0FA2 00007FFD03BC0FAA
0012 00007FFD03BC0FAA 00007FFD03BC0FBB
0018 00007FFD03BC0FBB 00007FFD03BC0FC4
001d 00007FFD03BC0FC4 00007FFD03BC0FC8
001d 00007FFD03BC0FC8 00007FFD03BC0FCD
0022 00007FFD03BC0FCD 00007FFD03BC0FCE
0023 00007FFD03BC0FCE 00007FFD03BC0FD4
0025 00007FFD03BC0FD4 00007FFD03BC0FD5
0026 00007FFD03BC0FD5 00007FFD03BC0FED
002d 00007FFD03BC0FED 00007FFD03BC0FEE
002e 00007FFD03BC0FEE 00007FFD03BC0FF1
0041 00007FFD03BC0FF1 00007FFD03BC0FF2
fffffffd 00007FFD03BC0FF2 00007FFD03BC0FFA
fffffffe 00007FFD03BC0FFA 00007FFD03BC100E
0030 00007FFD03BC100E 00007FFD03BC101A
0031 00007FFD03BC101A 00007FFD03BC101B
0032 00007FFD03BC101B 00007FFD03BC102C
0038 00007FFD03BC102C 00007FFD03BC1031
003d 00007FFD03BC1031 00007FFD03BC1032
003e 00007FFD03BC1032 00007FFD03BC1033
003f 00007FFD03BC1033 00007FFD03BC103B
fffffffd 00007FFD03BC103B 00007FFD03BC0F50
ilAddr is 00000238C5982048 pImport is 00000214A1B31E50
IL_0000: nop 
IL_0001: ldstr "Hello there! What is your name? "
IL_0006: call void System.Console::WriteLine(string)
IL_000b: nop 
IL_000c: call string System.Console::ReadLine()
IL_0011: stloc.0 
IL_0012: ldstr "Hello "
IL_0017: ldloc.0 
IL_0018: call string System.String::Concat(string,string)
IL_001d: call void System.Console::WriteLine(string)
IL_0022: nop 
IL_0023: ldnull 
IL_0024: stloc.1 
.try
{
  IL_0025: nop 
  IL_0026: ldloc.1 
  IL_0027: unbox.any System.Int32
  IL_002c: stloc.2 
  IL_002d: nop 
  IL_002e: leave.s IL_0041
} // end .try
.catch
{
  IL_0030: stloc.3 
  IL_0031: nop 
  IL_0032: ldstr "I just caught my first exception."
  IL_0037: ldloc.3 
  IL_0038: call void System.Console::WriteLine(string,object)
  IL_003d: nop 
  IL_003e: nop 
  IL_003f: leave.s IL_0041
} // end .catch
IL_0041: ret 
Begin 00007FFD03BC0F50, size f3

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 8:
00007ffd`03bc0f50 55              push    rbp
00007ffd`03bc0f51 57              push    rdi
00007ffd`03bc0f52 56              push    rsi
00007ffd`03bc0f53 4883ec60        sub     rsp,60h
00007ffd`03bc0f57 488d6c2470      lea     rbp,[rsp+70h]
00007ffd`03bc0f5c 488bf1          mov     rsi,rcx
00007ffd`03bc0f5f 488d7db8        lea     rdi,[rbp-48h]
00007ffd`03bc0f63 b90e000000      mov     ecx,0Eh
00007ffd`03bc0f68 33c0            xor     eax,eax
00007ffd`03bc0f6a f3ab            rep stos dword ptr [rdi]
00007ffd`03bc0f6c 488bce          mov     rcx,rsi
00007ffd`03bc0f6f 488965b0        mov     qword ptr [rbp-50h],rsp
00007ffd`03bc0f73 48894d10        mov     qword ptr [rbp+10h],rcx
00007ffd`03bc0f77 833d92ea090000  cmp     dword ptr [00007ffd`03c5fa10],0
00007ffd`03bc0f7e 7405            je      00007ffd`03bc0f85
00007ffd`03bc0f80 e8bbe9c45f      call    coreclr!JIT_DbgIsJustMyCode (00007ffd`6380f940)
IL_0000: nop 
00007ffd`03bc0f85 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 9:
IL_0001: ldstr "Hello there! What is your name? "
00007ffd`03bc0f86 48b9c0303ed738020000 mov rcx,238D73E30C0h
00007ffd`03bc0f90 488b09          mov     rcx,qword ptr [rcx]
IL_0006: call void System.Console::WriteLine(string)
00007ffd`03bc0f93 e8f0f5ffff      call    00007ffd`03bc0588 (System.Console.WriteLine(System.String), mdToken: 0000000006000081)
IL_000b: nop 
00007ffd`03bc0f98 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 10:
00007ffd`03bc0f99 e82affffff      call    00007ffd`03bc0ec8 (System.Console.ReadLine(), mdToken: 0000000006000073)
>>> 00007ffd`03bc0f9e 488945c8        mov     qword ptr [rbp-38h],rax
IL_0011: stloc.0 
00007ffd`03bc0fa2 488b4dc8        mov     rcx,qword ptr [rbp-38h]
00007ffd`03bc0fa6 48894de8        mov     qword ptr [rbp-18h],rcx

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 11:
IL_0012: ldstr "Hello "
IL_0017: ldloc.0 
00007ffd`03bc0faa 48b9c8303ed738020000 mov rcx,238D73E30C8h
00007ffd`03bc0fb4 488b09          mov     rcx,qword ptr [rcx]
00007ffd`03bc0fb7 488b55e8        mov     rdx,qword ptr [rbp-18h]
IL_0018: call string System.String::Concat(string,string)
00007ffd`03bc0fbb e8e035ffff      call    00007ffd`03bb45a0 (System.String.Concat(System.String, System.String), mdToken: 0000000006000703)
00007ffd`03bc0fc0 488945c0        mov     qword ptr [rbp-40h],rax
IL_001d: call void System.Console::WriteLine(string)
00007ffd`03bc0fc4 488b4dc0        mov     rcx,qword ptr [rbp-40h]
00007ffd`03bc0fc8 e8bbf5ffff      call    00007ffd`03bc0588 (System.Console.WriteLine(System.String), mdToken: 0000000006000081)
IL_0022: nop 
00007ffd`03bc0fcd 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 14:
IL_0023: ldnull 
IL_0024: stloc.1 
00007ffd`03bc0fce 33d2            xor     edx,edx
00007ffd`03bc0fd0 488955e0        mov     qword ptr [rbp-20h],rdx

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 16:
.try
{
  IL_0025: nop 
00007ffd`03bc0fd4 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 17:
  IL_0026: ldloc.1 
  IL_0027: unbox.any System.Int32
  IL_002c: stloc.2 
00007ffd`03bc0fd5 488b55e0        mov     rdx,qword ptr [rbp-20h]
00007ffd`03bc0fd9 48b9e8b1b803fd7f0000 mov rcx,7FFD03B8B1E8h (MT: System.Int32)
00007ffd`03bc0fe3 e818119e5f      call    coreclr!JIT_Unbox (00007ffd`635a2100)
00007ffd`03bc0fe8 8b10            mov     edx,dword ptr [rax]
00007ffd`03bc0fea 8955dc          mov     dword ptr [rbp-24h],edx

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 18:
  IL_002d: nop 
00007ffd`03bc0fed 90              nop
  IL_002e: leave.s IL_0041
00007ffd`03bc0fee 90              nop
00007ffd`03bc0fef eb00            jmp     00007ffd`03bc0ff1

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 23:
} // end .catch
IL_0041: ret 
00007ffd`03bc0ff1 90              nop
00007ffd`03bc0ff2 488d65f0        lea     rsp,[rbp-10h]
00007ffd`03bc0ff6 5e              pop     rsi
00007ffd`03bc0ff7 5f              pop     rdi
00007ffd`03bc0ff8 5d              pop     rbp
00007ffd`03bc0ff9 c3              ret

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 8:
00007ffd`03bc0ffa 55              push    rbp
00007ffd`03bc0ffb 57              push    rdi
00007ffd`03bc0ffc 56              push    rsi
00007ffd`03bc0ffd 4883ec30        sub     rsp,30h
00007ffd`03bc1001 488b6920        mov     rbp,qword ptr [rcx+20h]
00007ffd`03bc1005 48896c2420      mov     qword ptr [rsp+20h],rbp
00007ffd`03bc100a 488d6d70        lea     rbp,[rbp+70h]

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 19:
  } // end .try
  .catch
  {
IL_0030: stloc.3 
00007ffd`03bc100e 488955b8        mov     qword ptr [rbp-48h],rdx
00007ffd`03bc1012 488b4db8        mov     rcx,qword ptr [rbp-48h]
00007ffd`03bc1016 48894dd0        mov     qword ptr [rbp-30h],rcx

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 20:
IL_0031: nop 
00007ffd`03bc101a 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 21:
IL_0032: ldstr "I just caught my first exception."
IL_0037: ldloc.3 
00007ffd`03bc101b 48b9d0303ed738020000 mov rcx,238D73E30D0h
00007ffd`03bc1025 488b09          mov     rcx,qword ptr [rcx]
00007ffd`03bc1028 488b55d0        mov     rdx,qword ptr [rbp-30h]
IL_0038: call void System.Console::WriteLine(string,object)
00007ffd`03bc102c e85ff5ffff      call    00007ffd`03bc0590 (System.Console.WriteLine(System.String, System.Object), mdToken: 0000000006000082)
IL_003d: nop 
00007ffd`03bc1031 90              nop

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 22:
IL_003e: nop 
00007ffd`03bc1032 90              nop
IL_003f: leave.s IL_0041
00007ffd`03bc1033 90              nop
00007ffd`03bc1034 488d05b6ffffff  lea     rax,[00007ffd`03bc0ff1]

C:\Users\neerajs\source\repos\LearnWinDebugging\hello\Program.cs @ 23:
00007ffd`03bc103b 4883c430        add     rsp,30h
00007ffd`03bc103f 5e              pop     rsi
00007ffd`03bc1040 5f              pop     rdi
00007ffd`03bc1041 5d              pop     rbp
00007ffd`03bc1042 c3              ret
neeraj9 commented 5 years ago

Looks like the following IL is missing and needs to be debugged, which is strange because it should be a simpler case to deal with.

IL_000c: call string System.Console::ReadLine()

[update]

The following is the source of issue which either must be fixed in the map generated by debugger or any workaround towards this issue.

000c 00007FFD03BC0F99 00007FFD03BC0F99
000c 00007FFD03BC0F99 00007FFD03BC0FA2
neeraj9 commented 5 years ago

Fix (https://github.com/dotnet/diagnostics/pull/478/commits/e08c33fa1853e66f4560544e24bb245150b7b6c7) applied on the PR.

neeraj9 commented 5 years ago

Capturing the program used to debug the above for reference.

  1 using System;
  2
  3 namespace hello
  4 {
  5     class Program
  6     {
  7         static void Main(string[] args)
  8         {
  9             Console.WriteLine("Hello there! What is your name? ");
 10             string name = Console.ReadLine();
 11             Console.WriteLine("Hello " + name);
 12
 13             // testing try / catch
 14             object nullobj = null;
 15             try
 16             {
 17                 int thisIsWrong = (int)nullobj;  // should throw
 18             }
 19             catch (InvalidCastException e)
 20             {
 21                 Console.WriteLine("I just caught my first exception.", e);
 22             }
 23         }
 24     }
 25 }