Привет, Друзья!
Сегодня мы отвлечемся от Silverlight'a и пройдемся по самому популярному инструменту .NET разработчика: по отладчику Visual Studio, в контексте языка C#. Прежде чем вы подумаете, что я окончательно спятил, если собираюсь рассказывать очевидные вещи, позвольте возразить. Мы, разработчики ПО, склонны останавливаться на выученном, после того, как думаем, что знаний достаточно для выполнения работы. В то же время, копнув глубже, можно найти более эффективные способоы решения проблемы. Думаю, это одно из отличительных свойств настоящих Специалистов - желание всегда копать глубже, чем большинство, не останавливаясь на достигнутом.
Итак, приятного вам "копания". Да не испугает вас размер скроллбара - тут картинок больше, чем текста :).
Сегодня мы отвлечемся от Silverlight'a и пройдемся по самому популярному инструменту .NET разработчика: по отладчику Visual Studio, в контексте языка C#. Прежде чем вы подумаете, что я окончательно спятил, если собираюсь рассказывать очевидные вещи, позвольте возразить. Мы, разработчики ПО, склонны останавливаться на выученном, после того, как думаем, что знаний достаточно для выполнения работы. В то же время, копнув глубже, можно найти более эффективные способоы решения проблемы. Думаю, это одно из отличительных свойств настоящих Специалистов - желание всегда копать глубже, чем большинство, не останавливаясь на достигнутом.
Итак, приятного вам "копания". Да не испугает вас размер скроллбара - тут картинок больше, чем текста :).
Содержание
Для вашего удобства, вот список всех полезностей, рассмотренных в этом посте:
- Наблюдение за экземплярами классов или Make Object ID
- Улучшаем читабельность в Watch window.
- Поймать все исключения? - Легко!
- Исключение, ты кто? - $exception.
- Debugger.Break(), или Стоп машина!
- Поток, тебя как зовут?
- Позвольте к вам прикрепиться: Attach to Process.
- Отладка без отладки: Design Time Debugging.
- Хочу остановиться здесь, если только...
- Вывод информации, без вывода информации: Trace Points.
- Когда очень плохо: Отладка .NET framework'a.
- Отладка запуска сервисов/приложений.
- Отладка Silverlight приложений.
- Знайте свой инструмент, или блог Сары Форд.
Наблюдение за экземплярами классов или Make Object ID
Когда вы работаете с несколькими экземплярами класса, как можно отличить один от
другого? Если это неуправляемый код, можно посмотреть на адрес объекта. Если вы
в управляемом коде, то можно попросить GetHashCode() у объекта, и надеятся, что
его реализация не подкачает. В отладчике есть более простой способ: правый клик
на объекте в окне Watch/Locals/Autos -> Make Object ID.
После этого объекту присваивается уникальная метка {1#}, {2#}, и т.д. Метка дается объекту на все время жизни, и именно так объект будет появляться во всех окошках, в том числе, в Immediate Window, и даже в тултипах.
1. Делаем Object ID:
2. Объект получает идентификатор:
3. Теперь всюду, где встречается "меченный" объект, студия даст нам знать об этом:
Что еще более интересно, так это возможность использовать метку для ссылки на объект. Т.е. можно написать в окне Watch 1# и получить мгновенный доступ к содержимому меченного объекта. Также метку можно использовать в условных (conditional) точках останова. Например, условие this == 1# остановит выполнение программы только если это наш меченный объект.
После этого объекту присваивается уникальная метка {1#}, {2#}, и т.д. Метка дается объекту на все время жизни, и именно так объект будет появляться во всех окошках, в том числе, в Immediate Window, и даже в тултипах.
1. Делаем Object ID:
2. Объект получает идентификатор:
3. Теперь всюду, где встречается "меченный" объект, студия даст нам знать об этом:
Что еще более интересно, так это возможность использовать метку для ссылки на объект. Т.е. можно написать в окне Watch 1# и получить мгновенный доступ к содержимому меченного объекта. Также метку можно использовать в условных (conditional) точках останова. Например, условие this == 1# остановит выполнение программы только если это наш меченный объект.
Улучшаем читабельность в Watch window
По умолчанию, значение объекта в окне Watch - имя его типа. Хочешь больше -
раскрывай дерево. Но ведь для списков мы видим число элементов в списке. Значит,
должен быть способ повысить читабельность нашего класса Customer?
ToString() - подумаете вы, и будете практически правы. Можно перекрыть метод ToString() и выводить отладочную информацию, которая будет показана в Watch. Но ToString() не всегда является лучшим выбором. Например, WPF использует его для вывода элементов без определенного DataTemplate на UI. Более удобным методом будет использование атрибута DebuggerDisplay.
Перекомпилируем приложение, и под отладчиком получаем:
Когда определены и атрибут DebuggerDisplay и ToString(), отладчик использует атрибут. Однако будьте аккуратны, в некоторых случаях вы можете получить интересное насекомое, зарегистрированное под именем Гейзенбаг.
ToString() - подумаете вы, и будете практически правы. Можно перекрыть метод ToString() и выводить отладочную информацию, которая будет показана в Watch. Но ToString() не всегда является лучшим выбором. Например, WPF использует его для вывода элементов без определенного DataTemplate на UI. Более удобным методом будет использование атрибута DebuggerDisplay.
[System.Diagnostics.DebuggerDisplay("Customer: {Name}")] public class Customer { public string Name { get; set; } // ... }
Перекомпилируем приложение, и под отладчиком получаем:
Когда определены и атрибут DebuggerDisplay и ToString(), отладчик использует атрибут. Однако будьте аккуратны, в некоторых случаях вы можете получить интересное насекомое, зарегистрированное под именем Гейзенбаг.
Поймать все исключения? - Легко!
Отладчик может останавливаться при возникновении любого исключения. Даже на обработанных исключениях. И на исключениях в недрах библиотек. И даже
на обработанных исключениях в недрах библиотек. Особенно полезным это может
быть, когда причина бага, который вы пытаетесь поймать, кроется в "придушенном"
исключении:
try { DoSomethingBuggy(); } catch { // Sorry. }
Все, что нужно для остановки, это зайти в меню Debug -> Exceptions... и
выбрать на чем именно вам интересно было бы остановиться:
Теперь при возникновении любого из выбранных исключений, во время отладки, отладчик любезно остановит выполнение программы и перенесет вас к проблемному месту.
Теперь при возникновении любого из выбранных исключений, во время отладки, отладчик любезно остановит выполнение программы и перенесет вас к проблемному месту.
Исключение, ты кто? - $exception.
В предыдущем примере, когда мы останавливались на всех исключениях, вы могли
встретить код следующего типа:
try { // Do something } catch { // Catch }
Как теперь быть, если вы хотите посмотреть на исключение в окне Watch? Экземпляр исключения не указан в блоке catch,
что написать? Конечно, можно добавить
переменную исключения, перекомпилировать код, и посмотреть на красавца. Но ни в
коем случае не делайте так. Просто введите в окно Watch строку $exception:
PS: Какие еще псеводпеременные поддерживает отладчик? Для C# мне известна только $user, которая показывает текущего пользователя. Для неуправляемого кода их чуть-чуть больше.
PS: Какие еще псеводпеременные поддерживает отладчик? Для C# мне известна только $user, которая показывает текущего пользователя. Для неуправляемого кода их чуть-чуть больше.
Debugger.Break(), или Стоп машина!
Чтобы мгновенно провалиться в отладчик, даже когда программа запущена не в
режиме отладке, и даже когда она запущена в Release версии, достаточно вызвать
Debugger.Break().
Поток, тебя как зовут?
Когда создаете новый поток, не поленитесь дать ему имя (Thread.Name).
Так будет легче отличать потоки друг от друга во время отладки: на скриншоте
ниже Happy thread получил имя.
Позвольте к вам прикрепиться: Attach to Process.
Расставили все брякпоинты, прошли первые десять шагов багорепа, и вдруг
вспомнили, что программа запущена не под отладчиком? Ничего страшного. Выбираете
в меню Debug -> Attach to Proces или просто Alt + D, P.
После этого, выбираем процесс, который хотим отладить, и жмем Attach. Если все загруженные символы соответсвуют прикрепленному процессу, вы попадаете в мир полноценной отладки.
После этого, выбираем процесс, который хотим отладить, и жмем Attach. Если все загруженные символы соответсвуют прикрепленному процессу, вы попадаете в мир полноценной отладки.
Отладка без отладки: Design Time Debugging.
Immediate Window (Ctrl + Alt + I, или Debug -> Windows -> Immediate) позволяет
выполнить метод даже если программа не запущена. А если еще поставить точку
останова в методе, то мы попадем в отладчик:
Для того, чтобы отладить функцию Sum() достаточно ввести в Immediate Window'e выражение ?Sum(x, y), и нажать Enter. Эта фича называется "Отладка в режиме дизайна", и работает во всех редакциях студии, кроме Express'a.
Для того, чтобы отладить функцию Sum() достаточно ввести в Immediate Window'e выражение ?Sum(x, y), и нажать Enter. Эта фича называется "Отладка в режиме дизайна", и работает во всех редакциях студии, кроме Express'a.
Хочу остановиться здесь, если только...
Не можете понять как эта переменная становится null'ом? Или как этот счетчик
выходит за пределы? Установите условный breakpoint. Для этого нужно
добавить точку останова и тыкунть правой кнопкой мыши по ней. В контекстном меню
выбираем "Condition...":
И указываем при каких условиях должна сработать точка останова:
В следующий раз, когда вы войдете в функцию, и x будет меньше сработает точка останова.
Совместно с метками объектов условные точки останова представляют отличное подспорье, когда вы пытаетесь отладить конкретный экземпляр коллекции. Если вы хотите, чтобы точка останова срабатывала только для данного элемента коллекции достаточно пометить элемент коллекции и использовать его метку в условии. Например условие 1# == this остановит выполнение только в случае если текущей объект обладает меткой 1#.
И указываем при каких условиях должна сработать точка останова:
В следующий раз, когда вы войдете в функцию, и x будет меньше сработает точка останова.
Совместно с метками объектов условные точки останова представляют отличное подспорье, когда вы пытаетесь отладить конкретный экземпляр коллекции. Если вы хотите, чтобы точка останова срабатывала только для данного элемента коллекции достаточно пометить элемент коллекции и использовать его метку в условии. Например условие 1# == this остановит выполнение только в случае если текущей объект обладает меткой 1#.
Вывод информации, без вывода информации: Trace Points.
Приходилось ли вам когда-нибудь писать следующий код для упрощения отладки?
private static int Sum(int x, int y) int y) { Trace.WriteLine(string.Format("Enetring function Sum(). X = {0}; Y = {1}; Time = {2}", x, y, DateTime.Now)); // ... }
Да? - Поздравляю, вы делали ненужную работу. Я тоже так делал :).
Ведь это очень полезно, особенно когда ищешь баги, связанные с фокусом ввода
- переключение в отладчик влечет за собой потерю фокуса, и усложняет отладку. А
так - написал и смотришь в Output Window, что там куда приходит. Но есть способ
сделать это легче.
Устанавливаем breakpoint, кликаем по нему правой кнопкой, выбираем "When Hit..." и в появившемся окошке пишем, что бы мы хотели увидеть:
Когда отладчик войдет в эту функцию, в окошке Output появится следующее сообщение:
Устанавливаем breakpoint, кликаем по нему правой кнопкой, выбираем "When Hit..." и в появившемся окошке пишем, что бы мы хотели увидеть:
Когда отладчик войдет в эту функцию, в окошке Output появится следующее сообщение:
Entering ConsoleApplication1.Program.Sum(int, int); X == 0; Y = 1; Time = {19.02.2010 8:30:34}
Как видите, можно выводить не только значения переменных, но и значения вызовов
функций или выражений. Безусловный плюс - не нужно перекомпилировать приложение,
если хотите изменить что-либо.
PS: Это не значит, что Trace.WriteLine() становится вовсе бесполезным. Он все так же бесценен для сопровождения приложений.
PS: Это не значит, что Trace.WriteLine() становится вовсе бесполезным. Он все так же бесценен для сопровождения приложений.
Когда очень плохо: Отладка .NET framework'a.
Я люблю копаться в framework'e. Эта страсть к археологии позволяет не только
лучше понять работу Библиотеки но и понять почему вызов этого метода не привел к
ожидаемым результатам. Для отладки .NET Framework вам нужны символы, флажок и
желание поискать на форумах, почему имея первые два пункта вы до сих пор не
можете отлаживать исходники .NET'a.
В самом простом случае, достаточно установить флажок в настройках студии Enable .NET Framework source stepping (Tools -> Options -> Debugging -> General):
В самом простом случае, достаточно установить флажок в настройках студии Enable .NET Framework source stepping (Tools -> Options -> Debugging -> General):
Я также вЫключил флаг Requre source files to exactly match the original version. Это увеличивает шансы на успешный вход в исходники .NET, в случае если загруженные символы по какой-то прчине отличаются от загруженной библиотеки.
Далее соглашайтесь на все предложения студии. Запускайте программу в дебаге, и попробуйте войти внутрь вызова системного метода. Если в Output окне вы видите сообщение "Step into: Stepping over method without symbols '...'":
Значит нужно проверить, что символы загружены. Идем в Call Stack и смотрим на методы без символов (серые):
Тыкаем правой кнопкой по любому из них и выбираем Load Symbols From -> Microsoft symbols server. После этого студия начинает скачивать символы:
Загрузить и проверить статус дополнительных символов можно так же из окна Modules (Debug -> Windows -> Modules). Итак, символы загружены, пробуем войти в функцию еще раз:
Что самое приятное - можно прочитать комментарии разработчиков библиотеки, которые нельзя найти ни в одном Reflector'e и MSDN'e.
Напоследок хочу предупредить, что не для всех сборок .NET Framework Microsoft позволяет отлаживать исходники. Смотрите в Output Window.
Отладка запуска сервисов/приложений.
Иногда нужно отладить код запуска сервисов или приложений, которые
стартует другой процесс (например, installer). Нажать F5 для запуска сервиса вы
не можете, а прикрепиться к процессу - уже слишком поздно. Как быть?
А быть надо с редактором реестра. Идем в раздел "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" и добавляем новый подраздел, с названием процесса, который будем отлаживать (e.g. explorer.exe). Затем, в этом разделе создаем строковый параметр с ключом debugger и значением - указывающм на отладчик:
На картинке выше я решил отладить сервис HP Health Check (не потому, что у моей хапешки проблемы со здоровьем, тьфу-тьфу-тьфу, просто ради примера). Имя сервиса hphc_service.exe - именно так я назвал подраздел в Image File Execution Options. VSJitDebugger - это процесс запускающий отладчик Visual Studio. Теперь, если кто-то запустит процесс мы получим окошко предлагающее отладку:
Кстати 1. Этот подход помогает не только отладить приложения, но и выследить какой засранец (извините) запускает исполняемые файлы вирусов...
Кстати 2. Если вы когда-нибудь устанавливали Process Explorer от SysInternals, и заменяли им стандартный диспетчер задач, то теперь вы знаете как он подменяет запуск taskmgr.exe :).
А быть надо с редактором реестра. Идем в раздел "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" и добавляем новый подраздел, с названием процесса, который будем отлаживать (e.g. explorer.exe). Затем, в этом разделе создаем строковый параметр с ключом debugger и значением - указывающм на отладчик:
На картинке выше я решил отладить сервис HP Health Check (не потому, что у моей хапешки проблемы со здоровьем, тьфу-тьфу-тьфу, просто ради примера). Имя сервиса hphc_service.exe - именно так я назвал подраздел в Image File Execution Options. VSJitDebugger - это процесс запускающий отладчик Visual Studio. Теперь, если кто-то запустит процесс мы получим окошко предлагающее отладку:
Кстати 1. Этот подход помогает не только отладить приложения, но и выследить какой засранец (извините) запускает исполняемые файлы вирусов...
Кстати 2. Если вы когда-нибудь устанавливали Process Explorer от SysInternals, и заменяли им стандартный диспетчер задач, то теперь вы знаете как он подменяет запуск taskmgr.exe :).
Отладка Silverlight приложений.
Мой основной браузер - Google Chrome, но разработку я веду под Firefox. Как же
отлаживать Silverlight в таком случае? Один способ - это в настройках веб
проекта указать каким браузером вы хотите запустить сайт, но я предпочитаю
просто прикрепляться к процессу Firefox.
В четвертом Silverlight появилась удобная возможность отлаживать Out of Browser приложения. Достаточно указать в настройках Silverlight проекта какое именно приложение нужно отладить:
В четвертом Silverlight появилась удобная возможность отлаживать Out of Browser приложения. Достаточно указать в настройках Silverlight проекта какое именно приложение нужно отладить:
Знайте свой инструмент, или блог Сары Форд.
Sara Ford - Program Manager CodePlex, автор книги
MS Visual Studio Tips,
очень жизнерадостная и веселая женщина. Она ведет потрясающий
блог, огромная часть советов из
которого и составила книгу. Если вы думаете, что отлично знаете Visual Studio -
посетите ее блог, чтобы понять, насколько глубоко заблуждаетесь :).
Спасибо!
Друзья,
спасибо вам, что прочитали или просто пролистали до этого места. Надеюсь, вы
нашли что-нибудь новое для себя. Буду очень рад, если вы поделитесь своими
открытиями, которые не вошли в пост. Напоследок, хотелось бы упомянуть слова мудреца: If debugging is the process of
removing bugs, then programming must be the process of putting them in...
Отличного настроения и программирования :)!
Супер! очень полезная инфа.
ОтветитьУдалитьТрассировка через брейкоинты, $exception, отладка в дизайн-тайм - незаменмые вещи.
Отличная статья! Большое спасибо, давно не встречал такой высокой концентрации полезной информации в одной статье.
ОтветитьУдалитьНебольшой комментарий по поводу: "Эта фича называется "Отладка в режиме дизайна", и работает во всех редакциях студии, кроме Express'a." - в экспрессе эта функция тоже работает. Правда вызов ?Sum(x, y) у меня не выполнился, пришлось писать Program.Sum(x, y).
Спасибо, Виталек, k0Zer, за добрые отзывы :). Рад, что оказалось полезным :)!
ОтветитьУдалитьв 64 процессе framework source debugging не работает.
ОтветитьУдалить