Зробити свій крок мови .NET правильно у відладчику


135

По-перше, прошу вибачення за тривалість цього питання.

Я автор IronScheme . Останнім часом я наполегливо працюю над тим, щоб надсилати гідну інформацію про налагодження, щоб я міг використовувати "рідний" .NET налагоджувач.

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

Перша проблема пов'язана з кроком.

Через те, що схема є мовою вираження, все має тенденцію до загортання в дужках, на відміну від основних .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. Викличте baz з null arg, він працює правильно. (null? x) з наступним x.
  2. Викличте baz за допомогою аргументу аргументу, він працює правильно. (null? x) тоді (пара? x) тоді (автомобіль x).
  3. Викликати baz з іншими аргументами, це не вдається. (null? x) тоді (пара? x), тоді (автомобіль x), тоді (твердження-порушення ...).

При застосуванні IgnoreSymbolStoreSequencePoints(як рекомендовано):

  1. Викличте baz з null arg, він працює правильно. (null? x) з наступним x.
  2. Зателефонуйте до baz з конс-аргом, це не вдається. (null? x) тоді (пара? x).
  3. Викликати baz з іншими аргументами, це не вдається. (null? x) тоді (пара? x), тоді (автомобіль x), тоді (твердження-порушення ...).

Я також знаходжу в цьому режимі, що деякі рядки (не показані тут) неправильно виділені, вони відключені на 1.

Ось кілька ідей, які можуть бути причинами:

  • Хвостові дзвінки плутають налагоджувач
  • Місця, що перекриваються (тут не показано) плутають налагоджувач (це робить дуже добре при встановленні точки перерви)
  • ????

Друга, але також серйозна проблема - це те, що в деяких випадках налагоджувач не в змозі зламати / вдарити точки прориву.

Єдине місце, де я можу змусити налагоджувач правильно (і стійко) пробиватися, - це точка введення методу.

Ситуація стає трохи кращою, коли IgnoreSymbolStoreSequencePointsїї не застосовують.

Висновок

Можливо, налагоджувач VS - це просто баггі :(

Список літератури:

  1. Здійснення налагодження мови CLR / .NET

Оновлення 1:

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

Оновлення 2:

Я створив додаток C # і спробував розрізати інформацію про рядок.

Мої висновки:

  • Після будь-якої brXXXінструкції потрібно мати точку послідовності (якщо вона не дійсна, так само "#line hidden", виберіть a nop).
  • Перед будь-якою brXXXінструкцією виправте "#line hidden" та "a" nop.

Застосовуючи це, однак не вирішує проблеми (поодинці?).

Але додаючи наступне, дає бажаний результат :)

  • Після retцього виведіть "#line hidden" та a 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:

Проблема з вищезазначеним "напівсправлення". Перифікуйте помилки у звітах щодо всіх методів із-за nopпісля ret. Я не розумію проблему насправді. Як можна nopперевірити перерву після a 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 ідеї, спробуйте mdbg і напишіть той самий код у C # та порівняйте.
леппі

6
У чому питання?
Джордж Стокер

9
Я думаю, що його запитання відповідає принципам "чому моя мова не переходить правильно?"
Маккей

1
Якщо ви не проходите певерифікацію, ви не зможете запускатись у ситуаціях часткового довіри, як-от із зібраного накопичувача чи в багатьох налаштуваннях хостингу плагінів. Як ви генеруєте свій ІЛ, роздум. Випромінюйте або за допомогою тексту + ілазми? В будь-якому випадку ви завжди можете поставити .line 16707566,16707566: 0,0 '' так, що вам не доведеться турбуватися про те, щоб вони повернули ноп після повернення.
Hippiehunter

@Hippiehunter: Дякую На жаль, без цього nopкроку не вдасться (я ще раз переконаюсь у цьому). Думаю, що це - жертва. Це не так, як VS може працювати навіть без прав адміністратора :) Btw за допомогою Reflection.Emit через DLR (дуже зламаний ранній розгалужений).
леппі

Відповіді:


26

Я інженер з команди налагодження Visual Studio.

Виправте мене, якщо я помиляюся, але це звучить як єдине питання, що залишилося - це те, що при переході з PDB на формат символу динамічного компіляції .NET 4 деякі пропускні точки пропускаються.

Напевно, нам знадобиться запит, щоб точно діагностувати цю проблему, однак ось кілька зауважень, які можуть допомогти.

  1. VS (2008+) може запускатися як не-адміністратор
  2. Чи завантажуються якісь символи взагалі вдруге? Ви можете перевірити, ввівши (через виняток або зателефонувавши на System.Diagnostic.Debugger.Break ())
  3. Якщо припустити, що символи завантажуються, чи є докір, який ви можете нам надіслати?
  4. Ймовірна відмінність полягає в тому, що формат символів для динамічно складеного коду на 100% відрізняється між .NET 2 (потік PDB) і .NET 4 (IL DB Я думаю, що вони його назвали?)
  5. Звук "нопа" справа. Нижче див. Правила для генерації неявних послідовних точок.
  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:

Leppie перевірив виправлення для роботи з ним у бета-версії Dev11, доступній за адресою http://www.microsoft.com/visualstudio/11/en-us/downloads, як зазначено в помилці підключення https://connect.microsoft. com / VisualStudio / відгуки / деталі / 684089 / .

Дякую,

Лука


Дуже дякую. Я спробую зробити репро, якщо потрібно.
леппі

Що стосується підтвердження, так, ви праві, але я все-таки вважаю за краще вирішити крокові питання, не порушуючи перевіреності. Але, як було сказано, я готовий пожертвувати, що якщо я можу довести, що це ніколи не кине виконання VerificationException.
леппі

Що стосується пункту 6, я просто зробив це "лінійним" для цього питання. Налагоджувач дійсно правильно вводить / вимикає / над виразами, як я маю намір це працювати :) Єдиною проблемою з підходом є встановлення точок прориву на "внутрішні" вирази, якщо "зовнішній" вираз охоплює його; відладчик у VS, як правило, хоче зробити максимально зовнішній вираз як точку розриву.
леппі

Я думаю, що я виділив питання перелому. Під час запуску під CLR2 точки зламки, як видається, переоцінюються при повторному запуску коду, хоч і нового коду. У CLR4 точки прориву лише "прилипають" до початкового коду. Я зробив невеличку скріншот поведінки CLR4 @ screencast.com/t/eiSilNzL5Nr . Зауважте, що назва типу змінюється між викликами.
леппі

Я подав помилку підключення. Запрошення досить легко :) connect.microsoft.com/VisualStudio/feedback/details/684089
leppie

3

Я інженер з команди налагодження SharpDevelop :-)

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

Ви намагалися налагодити його в SharpDevelop? Якщо в .NET є помилка, мені цікаво, чи потрібно реалізувати якесь вирішення. Я не знаю цього питання.

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

Швидкі примітки до вихідного коду IL:

  • .line 19,19: 6,15 '' трапляється двічі?
  • .line 20,20: 7,14 '' не починається з неявної точки послідовності (стек не порожній). я боюся
  • .line 20,20: 7,14 '' включає код для "автомобіля x" (хороший), а також "#f nooo x" (погано?)
  • щодо nop після ret. Що про stloc, ldloc, ret? Я думаю, що C # використовує цей трюк, щоб зробити різну точку послідовності.

Девід


+1 Дякую за відгук, як перший момент, невдалий побічний ефект від генератора коду, але здається нешкідливим. Другий момент, це саме те, що мені потрібно, але я бачу, ваш погляд, буде розглядати його. Не впевнений, що ви маєте на увазі під останнім пунктом.
леппі

Третім пунктом було те, що .line 22,22: 7,40 '' має бути перед IL_0020. Або перед IL_0020 має бути щось, інакше код все ще вважається .line 20,20: 7,14 ''. Четвертий пункт "ret, nop" може бути замінений на "sloc, .line, ldloc, ret". Я вже бачив цю картину, можливо, вона була зайвою, але, можливо, у неї була причина.
dsrbecky

Я не можу робити stloc, ldloc перед тим, як відступити, тому що я втрачу хвостовий дзвінок. Ах, я розумію, ви мали на увазі оригінальний вихід?
леппі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.