Retrieve JIT output

2019-01-12 06:24发布

问题:

I'm interested in viewing the actual x86 assembly output by a C# program (not the CLR bytecode instructions). Is there a good way to do this?

回答1:

You should use WinDbg with SOS/SOSEX, ensure that method you want to see x86 code for is JITted in method tables and then see actual unassembly with u command. Thus you would see actual code.

As others mentioned here, with ngen you could see code that is not exactly matches actual JIT compilation result. With Visual Studio it is also possible because JIT's compilation depends heavily on the fact if debugger is present or not.

UPD: Some clarification. WinDbg is a debugger also, but it is native one.

Here you can read about the technique in detail.



回答2:

While debugging your application in Visual Studio, you can right-click on a code where you have stopped (using breakpoint) and click "Go to Disassembly". You can debug through native instructions.

As for doing that with *.exe files on disk, maybe you could use NGen to generate native output and then disassemble it (although I never tried that, so I can't guarantee that it will work).

Here are some sample opcodes from simple arithmetic operation that was written in c#:

            int x = 5;
mov         dword ptr [ebp-40h],5 
            int y = 6;
mov         dword ptr [ebp-44h],6 
            int z = x + y;
mov         eax,dword ptr [ebp-40h] 
add         eax,dword ptr [ebp-44h] 
mov         dword ptr [ebp-48h],eax 


回答3:

As @IvanDanilov answered, you can use WinDbg and SOS. I am answering separately to provide a walk-through.

In this example, I want to view the disassembly of the AreEqual() method from:

using System;

namespace TestArrayCompare
{
    class Program
    {
        static bool AreEqual(byte[] a1, byte[] a2)
        {
            bool result = true;
            for (int i = 0; i < a1.Length; ++i)
            {
                if (a1[i] != a2[i])
                    result = false;
            }
            return result;
        }

        static void Main(string[] args)
        {
            byte[] a1 = new byte[100];
            byte[] a2 = new byte[100];
            if (AreEqual(a1, a2))
            {
                Console.WriteLine("`a1' equals `a2'.");
            }
            else
            {
                Console.WriteLine("`a1' does not equal `a2'.");
            }
        }
    }
}

Steps:

  1. Open WinDbg. From the File menu, select "Open Executable...". Browse to the location of the EXE (in my case, C:\Users\Daniel\Documents\Visual Studio 2013\Projects\TestArrayCompare\TestArrayCompare\bin\Release\TestArrayCompare.exe).
  2. Add the directory containing the PDB file to the symbol path. For example:

    .sympath "C:\Users\Daniel\Documents\Visual Studio 2013\Projects\TestArrayCompare\TestArrayCompare\bin\Release"
    
  3. In WinDbg's Command window, set a breakpoint when clr.dll is loaded via:

    sxe ld:clr
    
  4. Continue by running the 'Go' command: g

  5. At the clr.dll ModLoad, load SOS: .loadby sos clr
  6. Run BPMD to break on the method for which you wish to see the disassembly. For example:

    0:000> !BPMD TestArrayCompare.exe TestArrayCompare.Program.AreEqual
    Adding pending breakpoints...
    
  7. Continue again by running the 'Go' command: g

  8. Run Name2EE to see the method descriptor. For example:

    0:000> !Name2EE TestArrayCompare.exe TestArrayCompare.Program.AreEqual
    Module:      00a62edc
    Assembly:    TestArrayCompare.exe
    Token:       06000001
    MethodDesc:  00a637a4
    Name:        TestArrayCompare.Program.AreEqual(Byte[], Byte[])
    Not JITTED yet. Use !bpmd -md 00a637a4 to break on run.
    
  9. Run the BPMD command in the "Not JITTED yet" line. For example:

    0:000> !bpmd -md 00a637a4
    MethodDesc = 00a637a4
    Adding pending breakpoints...
    
  10. Continue again: g

  11. You should see "JITTED ..." in the Command window. Re-run the Name2EE command to see the address of the JIT code. For example:

    0:000> !Name2EE TestArrayCompare.exe TestArrayCompare.Program.AreEqual
    Module:      00a62edc
    Assembly:    TestArrayCompare.exe
    Token:       06000001
    MethodDesc:  00a637a4
    Name:        TestArrayCompare.Program.AreEqual(Byte[], Byte[])
    JITTED Code Address: 00b500c8
    
  12. Use the u command to disassemble, starting at the listed code address. For example:

    0:000> u 00b500c8 L20
    00b500c8 55              push    ebp
    00b500c9 8bec            mov     ebp,esp
    00b500cb 57              push    edi
    00b500cc 56              push    esi
    ...
    

(For the above, I was using WinDbg 6.3.9600.17200 X86 from the Windows 8.1 SDK.)

One handy reference is the SOS.dll (SOS Debugging Extension) reference page on MSDN.



回答4:

You could use Visual Studio Debugger by placing a breakpoint and then viewing the Dissassembly window (Alt+Ctrl+D) or try the Native Image Generator Tool (ngen.exe).



回答5:

You can do a memory dump. However, note that the in-memory code does not necessarily contain every method.

ngen does AOT, or Ahead-of-time code generation, which can be different from JIT code.