Сделайте ваш шаг .NET правильно в отладчике - PullRequest
134 голосов
/ 04 августа 2011

Во-первых, я прошу прощения за длину этого вопроса.

Я автор IronScheme . В последнее время я усердно работал над выдачей приличной отладочной информации, чтобы я мог использовать «нативный» отладчик .NET.

Хотя это было частично успешно, у меня возникают некоторые проблемы с прорезыванием зубов.

Первая проблема связана со степпингом.

Из-за того, что Scheme является языком выражений, все имеет тенденцию заключаться в круглые скобки, в отличие от основных языков .NET, которые, кажется, основаны на выражениях (или строках).

Исходный код (схема) выглядит так:

(define (baz x)
  (cond
    [(null? x) 
      x]
    [(pair? x) 
      (car x)]
    [else
      (assertion-violation #f "nooo" x)]))

Я специально выложил каждое выражение на новую строку.

Излучаемый код преобразуется в C # (через ILSpy) выглядит следующим образом:

public static object ::baz(object x)
{
  if (x == null)
  {
    return x;
  }
  if (x is Cons)
  {
    return Builtins.Car(x);
  }
  return #.ironscheme.exceptions::assertion-violation+(
     RuntimeHelpers.False, "nooo", Builtins.List(x));
}

Как видите, довольно просто.

Примечание: если код был преобразован в условное выражение (? :) в C #, все это было бы просто одним шагом отладки, имейте это в виду.

Вот вывод IL с номерами источника и строки:

  .method public static object  '::baz'(object x) cil managed
  {
    // Code size       56 (0x38)
    .maxstack  6
    .line 15,15 : 1,2 ''
//000014: 
//000015: (define (baz x)
    IL_0000:  nop
    .line 17,17 : 6,15 ''
//000016:   (cond
//000017:     [(null? x) 
    IL_0001:  ldarg.0
    IL_0002:  brtrue     IL_0009

    .line 18,18 : 7,8 ''
//000018:       x]
    IL_0007:  ldarg.0
    IL_0008:  ret

    .line 19,19 : 6,15 ''
//000019:     [(pair? x) 
    .line 19,19 : 6,15 ''
    IL_0009:  ldarg.0
    IL_000a:  isinst [IronScheme]IronScheme.Runtime.Cons
    IL_000f:  ldnull
    IL_0010:  cgt.un
    IL_0012:  brfalse    IL_0020

    IL_0017:  ldarg.0
    .line 20,20 : 7,14 ''
//000020:       (car x)]
    IL_0018:  tail.
    IL_001a:  call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
    IL_001f:  ret

    IL_0020:  ldsfld object 
         [Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
    IL_0025:  ldstr      "nooo"
    IL_002a:  ldarg.0
    IL_002b:  call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
    .line 22,22 : 7,40 ''
//000021:     [else
//000022:       (assertion-violation #f "nooo" x)]))
    IL_0030:  tail.
    IL_0032:  call object [ironscheme.boot]#::
       'ironscheme.exceptions::assertion-violation+'(object,object,object)
    IL_0037:  ret
  } // end of method 'eval-core(033)'::'::baz'

Примечание: Чтобы не дать отладчику просто выделить весь метод, я делаю точку входа в метод шириной всего в 1 столбец.

Как видите, каждое выражение корректно отображается на строку.

Теперь проблема со степпингом (протестирована на VS2010, но такая же / похожая проблема на VS2008):

Они с IgnoreSymbolStoreSequencePoints не применяются.

  1. Вызовите базу с нулевым аргументом, он работает правильно. (ноль? x), за которым следует x.
  2. Позвоните в базу с Cons arg, она работает правильно. (null? x), затем (pair? x), затем (car x).
  3. Вызовите базу с другим аргументом, это не удастся. (null? x) затем (pair? x) затем (car x) затем (нарушение-утверждение ...).

При применении IgnoreSymbolStoreSequencePoints (в соответствии с рекомендациями):

  1. Вызовите базу с нулевым аргументом, он работает правильно. (ноль? x), за которым следует x.
  2. Позвоните в базу с Cons arg, это не удалось. (ноль? x) затем (пара? x).
  3. Вызовите базу с другим аргументом, это не удастся. (null? x) затем (pair? x) затем (car x) затем (нарушение-утверждение ...).

Я также нахожу в этом режиме, что некоторые строки (здесь не показаны) неправильно выделены, они выключены на 1.

Вот несколько идей, которые могут быть причинами:

  • Tailcalls сбивает с толку отладчик
  • Перекрывающиеся местоположения (здесь не показаны) сбивают с толку отладчик (это очень хорошо работает при установке точки останова)
  • ????

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

Единственное место, где я могу заставить отладчик работать корректно (и последовательно), это точка входа метода.

Ситуация становится немного лучше, когда IgnoreSymbolStoreSequencePoints не применяется.

Заключение

Возможно, отладчик VS просто глючит: (

Ссылка:

  1. Отладка языка CLR / .NET

Обновление 1:

Mdbg не работает для 64-битных сборок. Так что это вышло. У меня больше нет 32-битных машин для тестирования. Обновление: Я уверен, что это не большая проблема, у кого-нибудь есть исправление? Редактировать: Да, глупый я, просто запустите mdbg из командной строки x64:)

Обновление 2:

Я создал приложение на C # и попытался проанализировать информацию о строке.

Мои выводы:

  • После любой инструкции brXXX необходимо указать точку последовательности (если она недействительна, то есть «#line hidden», выдать nop).
  • Перед любой brXXX инструкцией выведите «#line hidden» и nop.

Применение этого, однако, не устраняет проблемы (в одиночку?).

Но добавление следующего дает желаемый результат:)

  • После ret испустить '#line hidden' и nop.

Это режим, в котором IgnoreSymbolStoreSequencePoints не применяется. При применении некоторые шаги все еще пропускаются: (

Вот выход IL, когда было применено выше:

  .method public static object  '::baz'(object x) cil managed
  {
    // Code size       63 (0x3f)
    .maxstack  6
    .line 15,15 : 1,2 ''
    IL_0000:  nop
    .line 17,17 : 6,15 ''
    IL_0001:  ldarg.0
    .line 16707566,16707566 : 0,0 ''
    IL_0002:  nop
    IL_0003:  brtrue     IL_000c

    .line 16707566,16707566 : 0,0 ''
    IL_0008:  nop
    .line 18,18 : 7,8 ''
    IL_0009:  ldarg.0
    IL_000a:  ret

    .line 16707566,16707566 : 0,0 ''
    IL_000b:  nop
    .line 19,19 : 6,15 ''
    .line 19,19 : 6,15 ''
    IL_000c:  ldarg.0
    IL_000d:  isinst     [IronScheme]IronScheme.Runtime.Cons
    IL_0012:  ldnull
    IL_0013:  cgt.un
    .line 16707566,16707566 : 0,0 ''
    IL_0015:  nop
    IL_0016:  brfalse    IL_0026

    .line 16707566,16707566 : 0,0 ''
    IL_001b:  nop
    IL_001c:  ldarg.0
    .line 20,20 : 7,14 ''
    IL_001d:  tail.
    IL_001f:  call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
    IL_0024:  ret

    .line 16707566,16707566 : 0,0 ''
    IL_0025:  nop
    IL_0026:  ldsfld object 
      [Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
    IL_002b:  ldstr      "nooo"
    IL_0030:  ldarg.0
    IL_0031:  call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
    .line 22,22 : 7,40 ''
    IL_0036:  tail.
    IL_0038:  call object [ironscheme.boot]#::
      'ironscheme.exceptions::assertion-violation+'(object,object,object)
    IL_003d:  ret

    .line 16707566,16707566 : 0,0 ''
    IL_003e:  nop
  } // end of method 'eval-core(033)'::'::baz'

Обновление 3:

Проблема с вышеприведенным 'полувлажом'. Peverify сообщает об ошибках на всех методах из-за nop после ret. Я не понимаю проблему на самом деле. Как может nop прервать проверку после ret. Это как мертвый код (за исключением того, что это даже не код) ... О, хорошо, эксперименты продолжаются.

Обновление 4:

Вернувшись домой, удалил «непроверяемый» код, работающий на VS2008, и все стало намного хуже. Возможно, запуск непроверяемого кода для правильной отладки может быть ответом. В режиме «релиз» все выходные данные будут проверяемыми.

Обновление 5:

Теперь я решил, что моя идея, представленная выше, является пока единственно возможной. Хотя сгенерированный код не поддается проверке, я еще не нашел ни одного VerificationException. Я не знаю, как этот сценарий повлияет на конечного пользователя.

В качестве бонуса, моя вторая проблема также была решена. :)

Вот небольшой скринкаст того, что я закончил. Он поражает точки останова, делает правильный шаг (вход / выход / овер) и т. Д. В общем, желаемый эффект.

Я, однако, все еще не принимаю это как способ сделать это. Мне это кажется слишком хакерским. Было бы неплохо получить подтверждение о реальной проблеме.

Обновление 6:

Только что было внесено изменение для проверки кода на VS2010, похоже, есть некоторые проблемы:

  1. Первый звонок теперь не выполняется правильно. (нарушение-утверждение ...) ударил. В других случаях работает нормально. Какой-то старый код выдавал ненужные позиции. Убрал код, работает как положено. :)
  2. А если серьезно, то точки прерывания завершаются неудачно при втором вызове программы (при компиляции в памяти выгрузка сборки в файл, кажется, снова делает точки останова счастливыми).

Оба эти случая корректно работают под VS2008. Основное отличие состоит в том, что в VS2010 все приложение компилируется для .NET 4, а в VS2008 компилируется в .NET 2. Оба работают под управлением 64-разрядных систем.

Обновление 7:

Как уже упоминалось, я получил mdbg под 64-битным. К сожалению, у него также есть проблема с точкой останова, из-за которой он не может сломаться, если я перезапущу программу (это означает, что она перекомпилируется, поэтому не использует ту же сборку, но все еще использует тот же исходный код).

Обновление 8:

У меня есть ошибка на сайте MS Connect, связанная с проблемой точки останова.

Обновление: исправлено

Обновление 9:

После долгих раздумий кажется, что единственный способ сделать отладчик счастливым - это использовать SSA, поэтому каждый шаг может быть изолированным и последовательным. Мне еще предстоит доказать это понятие, хотя. Но это кажется логичным. Очевидно, что очистка временных файлов из SSA приведет к поломке отладки, но это легко переключить, и оставить их без особых накладных расходов.

Ответы [ 2 ]

26 голосов
/ 13 августа 2011

Я инженер в команде отладчика Visual Studio.

Поправьте меня, если я ошибаюсь, но, похоже, осталась только одна проблема - при переключении с PDB на формат символов динамической компиляции .NET 4некоторые точки останова пропускаются.

Нам, вероятно, потребуется точная копия для точной диагностики проблемы, однако вот некоторые примечания, которые могут помочь.

  1. VS (2008+) can-toзапускать от имени не-администратора
  2. Загружаются ли символы вообще во второй раз?Вы можете проверить, взломав (через исключение или вызвав System.Diagnostic.Debugger.Break ())
  3. Если предположить, что символы загружаются, есть ли репродукция, которую вы можете отправить нам?
  4. ВероятныйРазница в том, что формат символов для динамически скомпилированного кода на 100% отличается между .NET 2 (поток PDB) и .NET 4 (IL DB, я думаю, они так называли?)
  5. Звук 'nop о праве.См. Правила генерации неявных точек последовательности ниже.
  6. На самом деле вам не нужно испускать вещи в разных строках.По умолчанию VS шагает по «операторам символов», где, когда вы пишете компилятор, вы определяете, что означает «оператор символов».Поэтому, если вы хотите, чтобы каждое выражение было отдельной вещью в файле символов, это будет прекрасно работать.

JIT создает неявную точку последовательности на основе следующих правил: 1. Инструкции IL nop 2. IL ставит пустые точки 3. Инструкция IL, следующая сразу за инструкцией вызова

Если выясняется, что для решения вашей проблемы нам нужно репро, вы можете сообщить об ошибке подключения и безопасно загрузить файлы через этот носитель.

Обновление:

Мы призываем других пользователей, столкнувшихся с этой проблемой, попробовать предварительный просмотр Dev11 для разработчиков из http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=27543 и комментировать любые отзывы.(Требуется цель 4.5)

Обновление 2:

Леппи проверил исправление, чтобы работать для него на бета-версии Dev11, доступной по http://www.microsoft.com/visualstudio/11/en-us/downloads какотметил в соединении ошибка https://connect.microsoft.com/VisualStudio/feedback/details/684089/.

Спасибо,

Люк

3 голосов
/ 12 сентября 2011

Я инженер команды отладчика SharpDevelop: -)

Вы решили проблему?

Вы пытались отладить ее в SharpDevelop?Если есть ошибка в .NET, мне интересно, нужно ли нам реализовать какой-то обходной путь.Я не знаю об этой проблеме.

Вы пытались отладить его в ILSpy?Особенно без отладочных символов.Это отладило бы код C #, но сообщило бы, хорошо ли отлаживаются инструкции IL.(Имейте в виду, что отладчик ILSpy является бета-версией)

Быстрые заметки об оригинальном коде IL:

  • .line 19,19: 6,15 '' происходит дважды?
  • .line 20,20: 7,14 '' не начинается с неявной точки последовательности (стек не пуст).Я беспокоюсь, что
  • .line 20,20: 7,14 '' включает в себя код "car x" (хорошо), а также "#f nooo x" (плохо?)
  • относительно NOP после рет.Что насчет stloc, ldloc, ret?Я думаю, что C # использует этот трюк, чтобы сделать ret отличной точкой последовательности.

David

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...