Retrieve JIT output

后端 未结 5 2048
Happy的楠姐
Happy的楠姐 2020-12-03 15:00

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?

相关标签:
5条回答
  • 2020-12-03 15:24

    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).

    0 讨论(0)
  • 2020-12-03 15:28

    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.

    0 讨论(0)
  • 2020-12-03 15:31

    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.

    0 讨论(0)
  • 2020-12-03 15:35

    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 
    
    0 讨论(0)
  • 2020-12-03 15:37

    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.

    0 讨论(0)
提交回复
热议问题