Closed cshung closed 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.
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
@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.
@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
@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.
I am still not done yet. Let me look at the DecodeIL and interleave and get back.
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
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
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
Fix (https://github.com/dotnet/diagnostics/pull/478/commits/e08c33fa1853e66f4560544e24bb245150b7b6c7) applied on the PR.
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 }
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.