Получить вывод JIT

Я заинтересован в просмотре фактического вывода сборки x86 программой C# (не инструкциями байт-кода CLR). Есть ли хороший способ сделать это?

5 ответов

Решение

Вы должны использовать WinDbg с SOS/SOSEX, убедиться, что метод, для которого вы хотите увидеть код x86, JITted в таблицах методов, а затем увидеть фактическую разборку с u команда. Таким образом, вы увидите реальный код.

Как уже упоминалось здесь, с помощью ngen вы можете увидеть код, который не совсем соответствует фактическому результату JIT-компиляции. С Visual Studio это также возможно, потому что компиляция JIT сильно зависит от того, присутствует ли отладчик или нет.

UPD: некоторые уточнения. WinDbg также является отладчиком, но он встроенный.

Здесь вы можете прочитать о технике в деталях.

Во время отладки приложения в Visual Studio вы можете щелкнуть правой кнопкой мыши код, который вы остановили (используя точку останова), и нажать "Перейти к разборке". Вы можете отлаживать с помощью родных инструкций.

Что касается этого с файлами *.exe на диске, возможно, вы могли бы использовать NGen для генерации собственного вывода, а затем разобрать его (хотя я никогда не пробовал этого, поэтому я не могу гарантировать, что он будет работать).

Вот несколько примеров кодов операций из простой арифметической операции, написанной на 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] 
добавить eax,dword ptr [ebp-44h] 
MOV Dword PTR [EBP-48H], Eax 

Как ответил @IvanDanilov, вы можете использовать WinDbg и SOS. Я отвечаю отдельно, чтобы обеспечить проход.

В этом примере я хочу посмотреть дизассемблирование метода AreEqual() из:

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'.");
            }
        }
    }
}

шаги:

  1. Откройте WinDbg. В меню "Файл" выберите "Открыть исполняемый файл...". Перейдите к расположению EXE (в моем случае, C:\Users\Daniel\Documents\Visual Studio 2013\Projects\TestArrayCompare\TestArrayCompare\bin\Release\TestArrayCompare.exe).
  2. Добавьте каталог, содержащий файл PDB, к пути символа. Например:

    .sympath "C: \ Users \ Daniel \ Documents \ Visual Studio 2013 \ Projects \ TestArrayCompare \ TestArrayCompare \ bin \ Release"
    
  3. В окне команд WinDbg установите точку останова, когда clr.dll загружается через:

    sxe ld: clr
    
  4. Продолжите, выполнив команду "Перейти": g

  5. На clr.dll ModLoad, загрузить SOS: .loadby sos clr
  6. Запустите BPMD, чтобы отключить метод, для которого вы хотите увидеть разборку. Например:

    0: 000>! BPMD TestArrayCompare.exe TestArrayCompare.Program.AreEqual
    Добавление ожидающих точек останова...
    
  7. Продолжите снова, введя команду "Go": g

  8. Запустите Name2EE, чтобы увидеть дескриптор метода. Например:

    0: 000>! Name2EE TestArrayCompare.exe TestArrayCompare.Program.AreEqual
    Модуль:      00a62edc
    Сборка:    TestArrayCompare.exe
    Токен:       06000001
    MethodDesc:  00a637a4
    Имя:        TestArrayCompare.Program.AreEqual(Byte[], Byte[])
    Еще не встряхнуло. Используйте! Bpmd -md 00a637a4, чтобы разбить на бегу.
    
  9. Запустите команду BPMD в строке "Пока не выполнено". Например:

    0: 000>! Bpmd -md 00a637a4
    MethodDesc = 00a637a4
    Добавление ожидающих точек останова...
    
  10. Продолжить снова: g

  11. Вы должны увидеть "JITTED ..." в окне команд. Повторите команду Name2EE, чтобы увидеть адрес кода JIT. Например:

    0: 000>! Name2EE TestArrayCompare.exe TestArrayCompare.Program.AreEqual
    Модуль:      00a62edc
    Сборка:    TestArrayCompare.exe
    Токен:       06000001
    MethodDesc:  00a637a4
    Имя:        TestArrayCompare.Program.AreEqual(Byte[], Byte[])
    JITTED кодовый адрес: 00b500c8
    
  12. Использовать u Команда на разборку, начиная с указанного кода адреса. Например:

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

(Для вышеупомянутого я использовал WinDbg 6.3.9600.17200 X86 из Windows 8.1 SDK.)

Одной из полезных ссылок является справочная страница SOS.dll (SOS Debugging Extension) на MSDN.

Вы можете использовать отладчик Visual Studio, установив точку останова, а затем просмотрев окно "Разборка" (Alt + Ctrl + D), или попробуйте инструмент Native Image Generator Tool (ngen.exe).

Вы можете сделать дамп памяти. Однако обратите внимание, что код в памяти не обязательно содержит каждый метод.

ngen выполняет генерацию кода AOT или Ahead-of-time, которая может отличаться от кода JIT.

Другие вопросы по тегам