Перейти к содержимому

Как делать отладочный пример

  • автор:

Как отлаживать маленькие программы

Пусть у вас есть небольшая программа, которая… не работает. Причем не просто как-то не работает, а у вас есть конкретный тест, конкретный пример, на котором она не работает. (Если у вас такого примера нет, то у меня есть отдельный текст про то, что делать в таком случае.) Как понять, что в программе не так, и как это исправить?

На самом деле, на эту тему есть знаменитый текст «How to debug small programs» и его русский перевод, но на мой взгляд рекомендации, приведенные там, — это излишнее усложнение, не нужное на самом деле в 90% действительно простых программ.

Итак, у вас есть тест, но вы не понимаете, почему программа на нем выдает не тот результат, который нужен. Ну, во-первых, по возможности уменьшите тест. Если в вашей задаче вводится какой-то массив или т.п., то не надо разбираться с программой на массиве длины 10. Попробуйте найти тест длины 3-4, на котором программа тоже не будет работать. Если вводится одно число, но дальше будет цикл до этого числа, то подберите число поменьше. И т.п.

Дальше есть несколько подходов.

Представьте себе, что вы компьютер

Основной, самый главный подход, когда программа действительно небольшая, строк 10-20 максимум, состоит в том, чтобы представить себя на месте компьютера и в уме выполнить программу. Возьмите листочек бумажки (или откройте «Блокнот»), выпишите на нем список переменных, которые есть в вашей программе, оставив у каждой переменной место, куда вы будете записывать их значения. Это будет оперативная память вашего компьютера. (В дальнейшем, когда вы освоитесь, бумажка вам не будет нужна, вы будете держать все нужные значения в уме, и все это выполнение будет происходить весьма быстро.)

Далее выполняйте программу пошагово, с самого начала (ну можете пропустить ввод данных, если вы в нем на 200% уверены). При каждом изменении значения каждой переменной выписывая измененное значение на бумажке. Самое важное тут — это подробно и тщательно делать именно то, что написано в программе. Забудьте (точнее лучше задвиньте на задний план) вашу задачу, забудьте, зачем вы писали этот код. Вы работаете за компьютер, компьютер ничего не знает про то, какая у вас задача, он просто тупо выполняет написанный код. Полезно тщательно проговаривать каждую выполняемую операцию. Не забывайте, что операции — это не только присваивания, это еще и все управляющие конструкции (if’ы, циклы и т.д.); не забывайте, что в циклах на каждой итерации выполняются действия, относящиеся собственно к циклу (проверка условия в while, увеличение индекса цикла в for). Все изменения переменных отражайте на бумажке, каждый раз, когда вам нужно значение какой-то переменной, сверяйтесь с бумажкой.

При этом надо где-то глубоко в уме все-таки помнить, какую задачу вы решаете, и какой код зачем написан, чтобы, как только реальное выполнение кода отойдет от того, что вы имели в виду, сразу это и заметить.

Пример. Задача «Переставить элементы в обратном порядке». Типичный код, который тут многие пишут, примерно такой:

# a - массив, который вы считали for i in range(len(a)): t = a[i] a[i] = a[len(a) - i] a[len(a) - i] = t 

Вы запускаете программу на тесте 1 2 3 , и она падает. Хорошо, давайте представим, что мы выполняем эту программу за компьютер. У нас есть массив a , в котором записано 1 2 3 (и это записано у нас на бумажке), и переменные i и t . Поехали.

Начинается цикл for , переменная i становится равна 0 (на бумажке рядом с именем переменной i пишем 0 ).

Команда t = a[i] . Чему у нас равно i ? Смотрим на бумажку, i равно 0 . Чему равно a[i] ? Смотрим на бумажку, a[i] это a[0] это 1 . Это значение записывается в t . Записываем на бумажке рядом с t единицу.

Команда a[i] = a[len(a)-i] . Чему у нас равно i ? Нулю. Чему равно len(a) ? Трем. Чему равно len(a)-i ? Трем. Чему равно a[3] ? Ой, выход за пределы массива (не забываем, что элементы в массиве нумеруются с нуля; полезно на бумажке под значениями элементов массива подписать их индексы).

Вот собственно мы нашли первую ошибку. Обратите внимание, что мы специально тщательно и подробно все проговаривали; если бы вы действовали поверхностно, то вы могли бы сразу сказать: « a[len(a)-i] — это последний элемент массива (ведь я именно для этого писал этот код), поэтому это просто 3 ». И вы не заметили бы ошибку. Именно поэтому и надо по максимуму забыть, что обозначает этот код, а вместо этого просто четко и подробно выполнять то, что написано, постоянно сверяясь с бумажкой.

Хорошо, давайте исправим ошибку, теперь код такой:

# a - массив, который вы считали for i in range(len(a)): t = a[i] a[i] = a[len(a) - i - 1] a[len(a) - i - 1] = t 

Запускаем программу — и она выдает 1 2 3 , т.е. как будто массив не изменился. Это все равно неправильно, поэтому поехали еще раз.

Начинается цикл for , переменная i становится равна 0 (на бумажке рядом с именем переменной i пишем 0 ).

Команда t = a[i] . Чему у нас равно i ? Смотрим на бумажку, i равно 0 . Чему равно a[i] ? Смотрим на бумажку, a[i] это a[0] это 1 . Это значение записывается в t . Записываем на бумажке рядом с t единицу.

Команда a[i] = a[len(a)-i-1] . Чему у нас равно i ? Нулю. Чему равно len(a) ? Трем. Чему равно len(a)-i-1 ? Двум. Чему равно a[2] ? Трем. Это значение записывается в a[i] . Что такое a[i] ? У нас i равно 0 , поэтому это нулевой элемент массива. Зачеркиваем единичку, которая записана на бумажке на нулевом месте в массиве, записываем туда 3.

Команда a[len(a) — i — 1] = t . Чему у нас равно t ? Единице. Что такое a[len(a) — i — 1] ? Выражение в квадратных скобках мы только что считали (но надо как минимум внимательно проверить, что выражение тут написано то же, а лучше пересчитать), поэтому это a[2] . Значит, в a[2] записываем 1. Зачеркиваем число 3, которое раньше было написано в a[2] , записываем туда 1.

Продолжаем. Итерация цикла закончилась, начинается новая итерация цикла. i становится равно 1 .

Продолжаем все делать так же тщательно и подробно. Я не буду дальше все это расписывать, но (особенно если вы еще не видите ошибки в коде выше) можете продолжить и все-таки найти ошибку.

Добавьте отладочный вывод

Второй полезный подход — добавить в программу вывод на экран промежуточных значений переменных в ключевых местах программы. Тут надо суметь понять, что такое «ключевые места» и какие переменные выводить. Обычно, например, если у вас в программе есть какие-то циклы, то полезно добавить вывод как минимум в конце каждой итерации, и выводить те переменные, которые меняются в цикле, в том числе для циклов for — переменную цикла. Если у вас сложная конструкция из if’ов, то добавить вывод внутрь каждого if’а, чтобы видеть, в какой именно if зашла программа. Если у вас несколько функций или тем более рекурсия, то добавить отладочный вывод типа «вошли в такую-то функцию» (зачастую с указанием параметров функции) и «вышли из такой-то функции». Если у вас просто сложные вычисления, длинная формула или несколько формул — то добавить вывод промежуточных результатов вычислений; возможно, для этого придется длинную формулу разбить на части (и это заодно сделает ее понятнее).

После этого запускаете программу на том тесте, на котором она не работает, смотрите на отладочный вывод, проверяете все значения и находите первое значение, которое неверно. Таким образом вы довольно хорошо локализуете проблему, вы будете знать, что ошибка, например, в вычислении той или иной переменной. Дальше уже действуйте по ситуации: если программа не очень сложная, то эта переменная меняется в одном-двух местах, и вы сразу можете посмотреть, как она считается и почему значение получается неправильным. Тем более что вы, наверное, вывели на экран не только значение этой переменной, но и значения других переменных, от которых она зависит, поэтому сразу можете подставить эти значения в формулу и проверить.

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

Может быть и другая причина проблемы — не неправильное значение переменной, а, например, заход не в тот if или слишком раннее или слишком позднее окончание цикла, и т.д. Но в любом случае, как только вы добавили отладочный вывод, вы стали намного лучше понимать, что происходит в программе, и вам будет намного легче найти ошибку.

Рассмотрим в качестве примера тот же код, который мы разбирали выше. Добавим в него отладочный вывод примерно такой:

# a — массив, который вы считали for i in range(len(a)): t = a[i] a[i] = a[len(a) — i — 1] a[len(a) — i — 1] = t print(«i=», i, «a выполните-программу-пошагово-в-среде-разработки»>Выполните программу пошагово в среде разработки

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

Поговорите с уточкой

Еще одна стандартная рекомендация — это взять резиновую уточку (ну на самом деле любую игрушку, или даже можно и без игрушки), и подробно по порядку объяснить ей, что делает ваша программа, что делает в ней каждая строчка и почему всё написано так, а не иначе.

Вот прямо так и говорите: мне надо переставить элементы массива в обратном порядке. Для этого мне надо взять первый элемент массива и поменять местами с последним, потом второй с предпоследним и так далее. Как я это пишу в программе? Мне надо менять много пар элементов, поэтому я делаю цикл for . В цикле for переменная i обозначает номер элемента, который я буду менять местами с симметричным. Она должна меняться от 0 до… и вот тут вы понимаете, где ошибка в программе.

Это весьма полезный прием, если вы сумеете его освоить. Ключевой момент, что тут надо делать — надо подробно и про каждый элемент кода объяснить, зачем вы это делаете и почему код написан именно так. В частности, в примере выше фраза «она должна меняться от 0 до…» возникла потому, что вы видите, что в коде написано range(len(a)) , значит, надо объяснить уточке, почему написано именно так.

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

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

Мой курс по алгоритмическому программированию (и подготовке к олимпиадам) для школьников, студентов и всех желающих — algoprog.ru

Знакомство с отладчиком Visual Studio

Область применения:yesVisual Studio Visual Studio для Mac noVisual Studio Code no

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

Описанные здесь функции применяются к C#, C++, Visual Basic, JavaScript и другим языкам, поддерживаемым Visual Studio (если не указано иное).

Установка точки останова и запуск отладчика

Точки останова полезны, если вам известны строка или раздел кода, которые вы хотите подробно изучить в среде выполнения. Дополнительные сведения о различных типах точек останова, которые можно задать, например об условных точках останова и точках останова в функциях, см. в разделе Использование точек останова.

Для отладки нужно запустить приложение с отладчиком, подключенным к процессу приложения. Для этого:

  • Нажмите клавишу F5 (Отладка > Начать отладку), которая является наиболее распространенным методом.

Однако сейчас у вас, возможно, не задано ни одной точки останова для проверки кода приложения, поэтому мы сначала зададим их, а затем начнем отладку. Точки останова — это самая основная и важная функция надежной отладки. Точка останова указывает, где Visual Studio следует приостановить выполнение кода, чтобы вы могли проверить значения переменных или поведение памяти либо выполнение ветви кода.

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

Set a Breakpoint

Start Debugging

Нажмите клавишу F5 (Отладка > запуска отладки) или кнопку «Начать отладку» на панели инструментов отладки, а отладчик запускается в первую точку останова, с которой она сталкивается. Если приложение еще не запущено, при нажатии клавиши F5 запускается отладчик и выполняется остановка в первой точке останова.

Переход по коду в отладчике с помощью пошаговых команд

Мы указываем сочетания клавиш для большинства команд, так как они ускоряют навигацию по коду вашего приложения. (Аналогичные команды, такие как команды меню, отображаются в круглых скобках.) Дополнительные сведения об использовании команд пошагового выполнения см. в разделе Навигация по коду в отладчике.

Для запуска приложения с подключенным отладчиком нажмите клавишу F11 (Отладка > Шаг с заходом). F11 — это команда Шаг с заходом, которая выполняет приложение с переходом к следующему оператору. При запуске приложения с помощью клавиши F11 отладчик останавливается на первом выполняемом операторе.

F11 Step Into

F11 Step Into

Желтая стрелка представляет оператор, на котором приостановлен отладчик. В этой же точке приостанавливается выполнение приложения (этот оператор пока не выполнен).

Клавишу F11 удобно использовать для более детальной проверки потока выполнения. (Чтобы ускорить переход по коду, мы также показываем другие варианты.) По умолчанию отладчик пропускает код, отличный от пользователя (если требуется дополнительные сведения, см . только мой код).

В управляемом коде вы увидите диалоговое окно с запросом о том, хотите ли вы получать уведомления при автоматическом обходе свойств и операторов (поведение по умолчанию). Если вы хотите изменить этот параметр позже, отключите параметр Шаг с обходом свойств и операторов в меню Инструменты > Параметры в разделе Отладка.

Шаг с обходом по коду для пропуска функций

Когда вы находитесь в строке кода, представляющей собой вызов функции или метода, можно нажать клавишу F10 (Отладка > Шаг с обходом) вместо F11.

Клавиша F10 продолжает выполнение отладчика без захода в функции или методы в коде приложения (код продолжает выполняться). Нажав клавишу F10, вы можете обойти код, который вас не интересует. Так можно быстро перейти к важному для вас коду. Дополнительные сведения об использовании команд пошагового выполнения см. в разделе Навигация по коду в отладчике.

Быстрое выполнение до точки в коде с помощью мыши

Использование кнопки Выполнение до щелкнутого аналогично установке временной точки останова. Кроме того, эта команда удобна для быстрой работы в видимой области кода приложения. Выполнение до щелкнутого можно использовать в любом открытом файле. Дополнительные сведения об этой функции и аналогичных функциях навигации см. в разделе Выполнение до определенного места в коде.

Screenshot of the Run to Click button from the Visual Studio Debugger. The button indicates that execution should run to the line where the button is placed.

Пока в отладчике наведите указатель мыши на строку кода, пока не появится кнопка «Запустить» (выполнить выполнение здесь).

Screenshot of the Visual Studio Debugger showing the Run to Click button appearing just to the left of a call to a function.

Screenshot of the Visual Studio Debugger showing the Run to Click button appearing just to the left of a call to a function.

Кнопка Выполнить о щелчка (Выполнить до этого места) доступна начиная с Visual Studio 2017.

Нажмите кнопку выполнения до щелкнутого (Выполнить до этого места). Отладчик продолжает выполнение до строки кода, которую вы щелкнули.

Вывод отладчика из текущей функции

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

Нажмите сочетание клавиш SHIFT + F11 (или выберите Отладка > Шаг с выходом).

Эта команда возобновляет выполнение приложения (и перемещает отладчик) до возврата текущей функции.

Выполнить до текущей позиции

Если вы находитесь в режиме редактирования кода (то есть работа отладчика не приостановлена), щелкните правой кнопкой мыши строку кода в приложении и выберите команду Выполнить до текущей позиции (или нажмите клавиши CTRL+F10). Эта команда запускает отладку и задает временную точку останова на текущей строке кода. Дополнительные сведения об этой функции и аналогичных функциях навигации см. в разделе Выполнение до определенного места в коде.

Run to Cursor

Run to Cursor

Если имеются заданные точки останова, отладчик приостанавливается в первой достигнутой точке останова.

Нажимайте клавишу F5, пока не достигнете строки кода, для которой выбрали Выполнить до текущей позиции.

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

Вы можете использовать функцию Выполнить до текущей позиции в окне Стек вызовов во время отладки.

Быстрый перезапуск приложения

Restart App

Нажмите кнопку «Перезапустить» на панели инструментов отладки (или нажмите клавиши CTRL+SHIFT+F5).

Кнопка Перезапустить позволяет сэкономить время, затрачиваемое на остановку приложения и перезапуск отладчика. Отладчик приостанавливается в первой точке останова, достигнутой при выполнении кода.

Stop Debugging

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

Редактирование кода в реальном времени

Visual Studio 2022 поддерживает динамическое редактирование кода в процессе отладки. Дополнительные сведения см. в следующих разделах.

  • Создание и отладка выполняющегося кода
  • Создание и отладка выполняющегося кода XAML с помощью Горячей перезагрузки XAML
  • Изменить и продолжить

Изменение кода и продолжение отладки (C#, VB, C++, XAML)

В большинстве языков, поддерживаемых Visual Studio, можно изменять код во время сеанса отладки, а затем продолжать отладку. Для использования этой функции щелкните код, чтобы установить в нем курсор, когда отладчик приостановлен, внесите изменения и нажмите клавишу F5, F10 или F11, чтобы продолжить отладку. Дополнительные сведения об этой функции и ее ограничениях см. в статье Изменить и продолжить.

Сведения об изменении кода XAML во время сеанса отладки см. в статье Создание и отладка выполняющегося кода XAML с помощью горячей перезагрузки XAML.

Проверка переменных с помощью подсказок по данным

Теперь, когда вы немного освоились, у вас есть хорошая возможность проверить состояние приложения (переменные) с помощью отладчика. Функции, позволяющие проверять переменные, являются одними из самых полезных в отладчике. Реализовывать эту задачу можно разными способами. Часто при попытке выполнить отладку проблемы пользователь старается выяснить, хранятся ли в переменных значения, которые требуются в определенном состоянии приложения. Подробные сведения об использовании подсказок по данным см. в разделе Просмотр значений данных в подсказках по данным.

В режиме приостановки в отладчике наведите указатель мыши на объект, чтобы увидеть его текущее значение или значение по умолчанию.

View a Data Tip

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

Часто при отладке бывает необходимо быстро проверить значения свойств для объектов. Лучше всего для этого подходят подсказки по данным.

Проверка переменных с помощью окон «Видимые» и «Локальные»

В окне Видимые отображаются переменные вместе с текущим значением и типом. Окно Видимые показывает все переменные, используемые в текущей или предыдущей строке (в C++ это окно показывает переменные в трех предыдущих строках кода; сведения о поведении для конкретного языка см. в документации). Дополнительные сведения об использовании этих окон см. в статье Проверка переменных в окнах «Видимые» и «Локальные».

Во время отладки взгляните на окно Видимые в нижней части редактора кода.

Autos Window

Autos Window

В JavaScript окно Локальные поддерживается, а окно Видимые — нет.

Взгляните в окно Локальные. В окне Локальные показаны переменные, которые находятся в текущей области.

Locals Window

Locals Window

В этом примере объекты this и f находятся в области действия. Дополнительные сведения см. в статье Проверка переменных в окнах «Видимые» и «Локальные».

Установка контрольного значения

В окне Контрольное значение можно указать переменную (или выражение), которую необходимо отслеживать. Дополнительные сведения см. в статье Установка контрольных значений с помощью окон «Контрольное значение» и «Быстрая проверка».

Во время отладки щелкните правой объект кнопкой мыши и выберите пункт Добавить контрольное значение.

Watch Window

Watch Window

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

Изучение стека вызовов

В окне Стек вызовов показан порядок вызова методов и функций. В верхней строке показана текущая функция. Во второй строке показана функция или свойство, из которого она вызывалась, и т. д. Стек вызовов хорошо подходит для изучения и анализа потока выполнения приложения. Дополнительные сведения см. в статье о просмотре стека вызовов.

Окно Стек вызовов аналогично перспективе «Отладка» в некоторых интегрированных средах разработки, например Eclipse.

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

Examine the Call Stack

Examine the Call Stack

Дважды щелкните строку кода, чтобы просмотреть исходный код. При этом также изменится текущая область, проверяемая отладчиком. Это не перемещает отладчик.

Для выполнения других задач можно воспользоваться контекстными меню из окна Стек вызовов. Например, можно вставлять точки останова в указанные функции, перезапускать приложение с помощью функции Выполнение до текущей позиции и изучать исходный код.

Проверка исключения

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

Exception Helper

В этом примере Помощник по исправлению ошибок отображает исключение System.NullReferenceException и ошибку с сообщением о том, что для экземпляра объекта не задана ссылка на объект. Также он сообщает, что строковое значение имело значение NULL при попытке вызвать метод Trim .

Exception Helper

В этом примере помощник по исправлению ошибок показывает исключение System.Argument и сообщение об ошибке, где сказано, что путь имеет недопустимую форму. Таким образом, мы знаем, что ошибка произошла в аргументе метода или функции.

В этом примере вызов DirectoryInfo выдал ошибку на пустой строке, хранящейся в переменной value .

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

Разверните узел Параметры исключений, чтобы просмотреть дополнительные параметры для обработки исключения этого типа, однако в рамках этого тура ничего менять не требуется.

Настройка отладки

Вы можете настроить проект для сборки, как описано в статье о конфигурации отладки или выпуска, настроить свойства проекта для отладки или настроить общие параметры для отладки. Кроме того, можно настроить отладчик для вывода пользовательских сведений с помощью таких компонентов, как атрибута DebuggerDisplay либо платформы NatVis для C/C++.

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

Начиная с Visual Studio 2022, вкладка Отладка для проектов .NET содержит ссылку на пользовательский интерфейс профилей запуска отладки, где можно задать свойства, связанные с отладкой.

Project properties

Project properties

Отладка интерактивных приложений ASP.NET в Службе приложений Azure

Сведения об отладке в службе приложение Azure см. в статье «Отладка приложений Azure».

Для Visual Studio Enterprise (только) отладчик моментальных снимков принимает моментальный снимок ваших рабочих приложений при выполнении кода. Чтобы указать отладчику на необходимость создать моментальный снимок, следует установить точки прикрепления и точки ведения в коде. Отладчик позволяет увидеть источник ошибки, не затрагивая трафик рабочего приложения. Средство Snapshot Debugger позволяет значительно сократить затраты времени на устранение проблем, возникающих в рабочих средах.

Launch the snapshot debugger

Коллекция моментальных снимков доступна для приложений ASP.NET, выполняющихся в Службе приложений Azure. Приложения ASP.NET должны выполняться на платформе .NET Framework 4.6.1 или более поздней версии, а приложения ASP.NET Core должны выполняться на платформе .NET Core 2.0 или более поздней версии в Windows.

Просмотр моментальных снимков с помощью возврата на шаг назад в IntelliTrace (Visual Studio Enterprise)

Функция возврата на шаг назад в IntelliTrace автоматически создает моментальный снимок вашего приложения для каждого события точки останова и шага отладчика. Используя записанные моментальные снимки, вы можете возвращаться к этим точкам останова и шагам, просматривая предыдущее состояние приложения. Возможность возврата на шаг назад в IntelliTrace позволяет сэкономить время в тех случаях, когда вам нужно просмотреть предыдущее состояние приложения, но не требуется перезапускать отладку или воссоздавать необходимое состояние приложения.

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

Step Backward and Forward Buttons

Отладка проблем производительности

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

Следующие шаги

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

Use breakpoints in the Visual Studio debugger (Использование точек останова в отладчике Visual Studio)

Отладочные символы

RSS

Что же послужило отправной точкой к написанию этого, надо заметить, достаточно узконаправленного материала? В основном желание разобраться в пока еще не до конца мною понятом процессе отладки кода, к изучению особенностей которого я в своё время приступил. Весь круг вопросов по этой теме пока еще даже в полной мере не сформировался, просто на определенном этапе стало очевидно, что не до конца осознана роль отладочных символов. До момента углубленного изучения данной темы, у меня сложилось довольно сумбурное мнение на предмет того, что основное предназначение отладочных символов — это процесс отладки, иными словами поиск проблем в программном обеспечении. Со временем, благодаря пусть даже поверхностной работе с отладочными средствами, удалось понять, что существует некая, достаточно важная составляющая отладки, именуемая отладочными символами или символами отладки, без которой вывод того же стека вызовов может превратиться в набор малопонятных адресов памяти. Чтобы подробнее разобраться в данном вопросе, давайте сделаем экскурс в прошлое. Дело в том, что когда то процесс исследования кода и вовсе не подразумевал использование каких бы то ни было сторонних файлов, облегчающих восприятие получаемого исходного кода. Исследователь видел перед собой всего-лишь «голые» адреса памяти, используемые вызовами, переменными, смещениями различных операций.

При изучении абсолютного большинства [стороннего] программного обеспечения, написанного для операционной системы Windows, никакой вспомогательной информацией, кроме самого исследуемого бинарного исполняемого файла, специалист не располагает. Конечно же, это создает определенные, надо отметить — порой довольно ощутимые, неудобства. Но это типичная, я бы даже сказал нормальная ситуация для стороннего программного обеспечения, автором которого являются независимые разработчики, ведь нигде не регламентировано предоставление конечному пользователю дополнительной информации помимо бинарных файлов пакета. Совсем другое дело, когда вопрос касается кода самой операционной системы Windows, где процесс исследования может помочь обнаружить ошибки в коде критичных системных модулей, или, хотя бы, получить ответ на вопрос, почему возникла та или иная ошибка. Ведь, как все мы знаем, исходный код Windows сокрыт от посторонних глаз, и по аналогии с миром Linux Вы не сможете так вот запросто получить листинг того или иного системного модуля. В свете всего этого, вопрос о предоставлении какой бы то ни было дополнительной отладочной информации, позволяющей облегчить изучение кода, является неким консенсусом между закрытостью, желанием сделать код более стабильным и пойти навстречу своим пользователям.

Так вот, до определенного времени никакие дополнительные файлы, облегчающие процесс отладки, исследователям кода не предоставлялись.

В дополнение ко всему прочему, изрядные проблемы при освоении логики продолжает создавать оптимизация кода, которую выполняют компилятор и компоновщик, окончательно запутывая и раздувая код. Единственное, что могли делать в свое время реверсивные инженеры, это создавать в дизассемблированном листинге собственные комментарии, пытаясь этот самый листинг документировать, тем самым хоть как-то «оживив» исходный код. Вероятно, на каком-то из этапов решено было упростить столь нелегкий процесс отладки программного обеспечения и некоторые вендоры операционных систем начали предоставлять специальные файлы, называемые отладочными символами, для системных файлов своих операционных систем и различных продуктов. Не стала исключением и корпорация Microsoft, поэтому данное нововведение заметно упростило процесс отладки закрытого кода Windows.

Отладочные символы (файлы идентификаторов, символы отладки, debug-символы или symbol files) — это определенным образом сконструированный блок данных, генерируемый компилятором языка программирования на основе исходного кода программы на этапе сборки (линковки), содержащий информацию о том, какие элементы (имена переменных, функций) и где располагаются в соответствующем бинарном исполняемом модуле.

или, иными словами:

Отладочные символы используются средствами отладки для сопоставления адресов (смещений) в исполняемых модулях (.EXE или .DLL) с именами/вызовами функций в этих файлах.

Отладочные символы можно рассматривать в качестве метаданных, связанных с исполняемыми образами/библиотеками, значительно улучшающих (облегчающих) процесс отладки.

Символы абсолютно не применимы в ходе штатного выполнения программы, но крайне полезны в процессе её отладки. Люди, которые не по наслышке знакомы с программированием, могут подтвердить, что компилятор, конвертируя исходный код программы в машинный код, выбрасывает имена функций. Другими словами, в получившемся исполняемом модуле Вы не увидите никаких имен. Единственный вариант получить связь между адресами и именами, это сгенерировать файл отладочных символов. Однако отладочные символы, в большинстве случаев, по умолчанию не генерируются, а могут быть созданы лишь при указании дополнительных опций компиляции.
Если пояснить данное понятие простым языком, то отладочные символы требуются (что очевидно по названию), для удобства отладки кода, а именно с целью понять что есть что в структуре изучаемого приложения. В общем случае, в файле отладочных символов хранятся:

  • Имена и адреса точек входа для функций;
  • Местоположение и имена локальных переменных;
  • Адреса и имена глобальных переменных;
  • Информация по типам для переменных, структур и прч.;
  • Объявление классов;
  • Информация о нумерации строк в исходном файле и файле символов. На основании этого машинные коды могут быть сопоставлены с исходным текстом, в случае его наличия.
  • соответствие между именами функций и соответствующими им адресами памяти

При наличии этой информации отладчик по адресу памяти легко находит соответствующую функцию по ближайшему предыдущему адресу. Да и исследователю эта информация позволяет видеть «символические» данные об исследуемом двоичном файле, что в свою очередь в значительной мере упрощает исследование алгоритма приложения в процессе статического дизассемблирования либо интерактивной отладки. Исследователю кода гораздо удобнее просматривать значения переменных, а не безликих ячеек памяти. Отладочные символы часто используются в процессе поиска ошибок в исходном коде, отладке программы и разного рода отказах. По методу размещения отладочная информация может:

  • быть представлена блоком данных, встроенным в бинарный исполняемый файл, то есть находиться внутри исполняемого файла. Не лучший способ хранения отладочных символов, поскольку иногда размер исполняемого файла может существенно увеличиваться.
  • быть размещена в виде отдельного внешнего файла, находящегося либо в директории с бинарным файлом, либо в специализированной независимой директории. Подобный способ размещение символов наиболее универсален.
  • вовсе отсутствовать. То есть не создаваться на этапе компиляции и/или сборки. В большинстве случаев так и происходит 🙂

Для конкретной версии исполняемого кода компилятор создает свой уникальный файл символов.

Это имеет под собой достаточно простую подоплеку, ведь отладочные символы жестко привязаны к конкретному исходному коду, и если в исходном коде что-либо меняется (строка, процедура, переменная), то старое соответствие перестает быть актуальным и требуется сгенерировать новый файл символов, который в точности соответствует уже новому, измененному исходному коду. С точки зрения разработчика стоит учесть и тот факт, что потенциально любое изменение проекта вызывает полную несовместимость между файлов символов и компонентом, поэтому файлы символов и бинарные файлы нужно строить одновременно. С точки зрения исследователя необходимо помнить, что файлы отладочных символов меняются при каждой новой сборке (build), поэтому процесс обновления системы требует также и обновления файлов отладочных символов, относящихся к системным модулям. Вот почему достаточно удобно использовать автоматизированный процесс запроса символов, дабы не отвлекаться на ручную синхронизацию.

Нужны ли символы

Отладчик WinDbg корпорации Microsoft можно сконфигурировать на автоматический запрос отладочной информации, если в ней есть необходимость. Вот как раз основной вопрос, на который я хотел сам себе ответить, нужны ли эти самые отладочные символы или нет? Я провел простейший эксперимент, который состоял в том, что я нашел завалявшийся у меня на тестовой станции дамп падения в синий экран (BSOD) и подключил его сначала в WinDbg с доступными символами, а затем в том же WinDbg без символов, и вот результат:

Вывод отладчика с отладочными символами и без

Честно говоря, результат меня удивил. На момент ошибки мы имеем довольно скудную информацию о сбое, такую как адрес возникновения ошибки и стек вызовов момента падения. Отладчик пытается выполнить автоматический разбор стека, и что же получается, мало того, что отладчик при отсутствии отладочных символов не может сопоставить имена и параметры функций, так он еще и не может правильно выполнить трассировку стека вызовов момента падения. Выходит, что без символов становится практически невозможно (в некоторых случаях) проследить последовательность вызовов функций, и в дополнение осложняется понимание того, какой именно модуль вызвал ошибку. Именно так! Всё только что сказанное актуально для ситуации с 32-битным кодом, поскольку:

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

Конечно иногда можно вручную изучить значения в стеке, и выяснить, какие из них являются адресами возврата, однако их можно легко спутать со значениями функций или данными, то есть весь процесс становится сложным и неудобным, требующим большого времени и определенного уровня знаний. Если же Вы отлаживаете 64-битные исполняемые файлы, необходимость в символьных файлах для получения правильной трассировки стека исчезает, поскольку 64-битным операционным системам и компиляторам для этого символы не требуются. Но даже и в этом случае, символы требуются для получения имен функций, параметров вызовов и локальных переменных.
Вот еще один пример, на этот раз использован отладчик OllyDbg . Слева вывод без применения отладочных символов, справа с применением:

OllyDbg с отладочными символами и без

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

Файл символов — это [не более чем] вспомогательный источник данных, который снабжает код приложения дополнительной информацией, которая может быть полезна во время отладки.

Распространение

Понятное дело, что если возникло желание символы официально предоставлять, то их необходимо каким-то образом распространять, для того, что бы конечные потребители программного продукта имели возможность (при остром на то желании конечно же) этот самый продукт отлаживать, то есть в случае возникновения ошибок иметь возможность быстро находить вероятную причину возникновения. Каким же образом отладочные символы предоставляют своей целевой аудитории? До некоторых пор разработчики, перед которыми стояла задача предоставления отладочных символов своим клиентам, передавали подобную информацию для своих продуктов на оптических носителях, дисках CD/DVD. Исключением не стала и операционная система Windows, которая может поставляться с отладочными символами, традиционно размещаемыми на дополнительном диске дистрибутива, либо в составе Driver Development Kit ( DDK ). Однако, с определенного времени популярным стал метод распространения символов через сеть Интернет. В сети для этой цели Microsoft размещает собственные сервера символов, которые и предоставляют отладочные символы, однако, чтобы работать с сервером символов, необходимо использовать специализированный протокол обмена.

Публичные и приватные (частные) символы

Как Вы уже поняли, информация в файлах символов может отличаться по степени полноты. По умолчанию, символьные файлы, генерируемые C/C++ компоновщиком, содержат довольно много информации об исполняемом файле, обычно больше, чем большинство разработчиков программного обеспечения готовы предоставлять своим клиентам. В связи с этим, после создания удаляют информацию по приватным символам из PDB -файлов и получают на выходе то, что называют публичными символами (public symbols) или обрезанными символами (stripped symbols). Эти «обрезанные» публичные символы не могут быть использованы для отладки в режиме исходного текста, потому что исходного текста в них попросту нет, как нет и информации по номерам строк в исходном файле.

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

Публичные символы, так же, не включают информацию для помощи по отображению параметров большинства функций, информацию по локальным переменным, типам локальных переменных. Тем не менее, они содержат достаточно информации для ключевых отладочных сценариев, что зачастую является достаточным. Таким образом, можно сделать утешительный вывод:

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

Символьные файлы, распространяемые корпорацией Microsoft (посредством серверов символов), включают в себя исключительно публичные функции, глобальные переменные и их типы данных, то есть являются публичными. В противоположность этому, некоторые разработчики программного обеспечения (Mozilla) распространяют полную отладочную информацию (публичные символы и приватные символы). Приватные (полные, закрытые) символы (private symbols) содержат значительно больше информации, включающей в себя путь и номера строк исходного кода, имена и типы параметров функций и переменных, и если сравнивать приватные и публичные символы с точки зрения процесса отладки, то последние делают его слегка сложнее, тем не менее достаточны для большинства сценариев отладки.

Сервер символов Microsoft

Многие компоненты, разрабатываемые корпорацией Microsoft, такие как файлы, входящие в состав операционных систем, офисных приложений и прочих продуктов, компилируются вместе с символами, которые затем распространяются через сервер символов Microsoft (Microsoft Symbol Server). Сервер символов представляет собой онлайновый репозиторий публичных символов для продуктов Microsoft, который доступен для запроса посредством протокола HTTP по визуально-неотображаемому пути (URL):

Это доступное в сети Интернет хранилище всех символов для всех публичных продуктов, выпущенных Microsoft за последние несколько лет. Огромный плюс предоставления отладочных символов онлайн заключается в том, что все отладчики Microsoft (независимые как WinDbg , KD и поставляемые в составе продуктов, как MS Visual Studio Debugger), а так же ряд сторонних отладочных средств, теперь имеют возможность автоматически загружать символы непосредственно с сервера в зависимости от версии отлаживаемого двоичного кода. Представляется, сколько времени можно сэкономить по сравнению с ситуацией, когда Вы в ручном режиме вынуждены подцеплять символы именно той версии модуля, которую вы отлаживаете в данный момент.

Стоит отметить, что на сервере символов Microsoft предоставляют только публичные символы, то есть «урезанную» информацию по структуре бинарного кода. Исключение составляют некоторые типы в файле ntdll.dll и модули ядра.

Формат PDB

Со времени того, как отладочные символы стали массово востребованы сообществом специалистов, многие разработчики программного обеспечения озаботились созданием разнообразных методов хранения информации об исходных файлах в модулях символов. В связи с чем, в природе появились всевозможные форматы записи символов. Нас же, в контексте данной статьи, интересуют форматы хранения символов для продуктов Microsoft.
В общем случае, символы могут быть предоставлены в следующих форматах:

  • Файлы .COFF , содержащие данные в формате Common Object File Format (COFF);
  • Файлы .CV , содержащие данные в формате CodeView. Формат хранения символов от Microsoft. Устаревший;
  • Файлы .SYM (Symbols). Устаревший формат;
  • Файлы .DBG (Debug), содержащие данные в формате COFF (Common Object File Format). Довольно распространенный формат, совместимый с большим количеством старых отладчиков. Однако, не может содержать информацию по строкам исходного кода;
  • Файлы .PDB (Program Database), содержащие данные в формате MSF (Multi-Stream Files). Современный продвинутый формат, разработанный Microsoft. Может содержать намного больше информации, чем .dbg.

Однако мы будем рассматривать в данной статье лишь формат PDB, который является самым современным, соответственно и предпочтительным форматом для различных средств разработки от Microsoft.
Все данные, которые PDB файл может содержать:

  • Публичные символы: все функции, статические и глобальные переменные;
  • Список объектных файлов которые отвечают за секции кода в исполняемом файле;
  • Информация о оптимизации указателя фрейма стека (FPO);
  • Имена локальных переменных;
  • Объявление классов;
  • Определения перечислений;
  • Тип локальных переменных. Благодаря этому отладчик или дизассемблер могут не только считывать из памяти значения переменных, но и выводить эти значения на экран в определенном виде (в зависимости от типа переменной);
  • Имена структур данных;
  • Тип структур данных;
  • Приватные символы: исходный текст программы;
  • Приватные символы: информация о номерах строк в исходном тексте;

PDB файл служит для:

  • Сопоставления идентификаторов, созданных в исходных файлах для классов, методов и другого кода, с идентификаторами, которые используются в скомпилированных исполняемых файлах;
  • Сопоставления операторов в исходном коде с машинными инструкциями в исполняемом файле;
  • Определения адреса памяти любой переменной, функции или строки кода.
  • Определения по имени функции, переменной и номера строки кода, адреса памяти.

Для проверки совместимости между бинарным файлом и файлом символов PDB компоновщик вносит в них GUID (глобальный уникальный идентификатор), которые должны быть идентичны в обоих файлах. Если они различаются, отладчики довольно часто отказываются подключать символы. Файл отладочных символов также содержит оригинальный путь к исходным файлам и, при необходимости, адрес сервера системы управления версиями, откуда можно эти исходные файлы получить.
В настоящее время у Microsoft наиболее распространен формат PDB версии 2 (MS Visual Studio 6.0) и PDB 7.0 (MS Visual Studio 7.0+). Данные форматы, в силу своей архитектуры, позволяют получать расширенную информацию об исполняемых модулях, в том числе возможность разбора стека, получения локальных переменных и так далее. PDB-файлы содержат данные в формате MSF (Multi-Stream Files) и в них по первым 32 байтам можно обнаружить сигнатуру Microsoft C/C++ MSF 7.00\r\n\032DS\0\0 . В формате MSF вся информация хранится в потоках (аналогия с файлами в файловой системе), при этом каждый поток имеет собственный идентификатор, данные, массив номеров страниц, количество занимаемых страниц. Различные потоки хранят информацию о различных структурах символов, таких как типы переменных, функции и прочее.
Другое преимущество PDB заключается в том, что становится возможна пошаговая отладка по исходному тексту, при условии конечно же, доступности этого самого исходного текста.

На примере отладчика WinDbg можно сказать, что он «умеет» автоматически загружать файлы отладочных символов в формате PDB, с сервера посредством подбора различных критериев (метка времени, контрольная сумма (CRC), количество ядер и прч.) с помощью SymSrv ( SymSrv.dll ). Стандартный протокол поиска символов работает с сервером символов Microsoft.

Выводы

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

Похожие записи:

  • Отключение проверки целостности bootmgr и winload
  • Останов на точке входа приложения в WinDbg
  • Последовательность вызова функций
  • Отключение проверки целостности ядра ntoskrnl.exe
  • IDA интерфейс не поддерживается

Методы и средства отладки, помогающие писать более качественный код

Область применения:yesVisual Studio Visual Studio для Mac noVisual Studio Code no

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

В этой статье вы узнаете, как работать с интегрированной среду разработки, чтобы сделать сеансы программирования более продуктивными. Мы затронем несколько задач, таких как:

  • Подготовка кода к отладке с помощью анализатора кода интегрированной среды разработки
  • Исправление исключений (ошибки времени выполнения)
  • Минимизация ошибок путем написания кода с учетом его предназначения (с помощью оператора assert)
  • Аспекты использования отладчика

Чтобы продемонстрировать эти задачи, мы показываем несколько наиболее распространенных типов ошибок и ошибок, которые могут возникнуть при попытке отладки приложений. Хотя пример кода — C#, концептуальные сведения обычно применимы к C++, Visual Basic, JavaScript и другим языкам, поддерживаемым Visual Studio (за исключением отмеченных). На снимках экрана представлены примеры на C#.

Создание примера приложения с некоторыми ошибками

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

Чтобы создать приложение, необходимо установить Visual Studio и рабочую нагрузку разработки для классических приложений .NET.

  • Установите Visual Studio бесплатно со страницы скачиваемых материалов Visual Studio, если еще не сделали этого.
  • Если необходимо установить рабочую нагрузку, но у вас уже есть Visual Studio, выберите «Сервис >получения инструментов» и «Компоненты». Запускается Visual Studio Installer. Выберите рабочую нагрузку Разработка классических приложений .NET и нажмите Изменить.

Выполните следующие действия, чтобы создать приложение:

  1. Откройте Visual Studio. В окне запуска выберите Создание нового проекта.
  2. В поле поиска введите консоль , а затем один из вариантов консольного приложения для .NET.
  3. Выберите Далее.
  4. Введите имя проекта, например Console_Parse_JSON, и нажмите кнопку «Далее» или «Создать«, как применимо. Выберите рекомендуемую целевую платформу или .NET 8, а затем нажмите кнопку «Создать«. Если вы не видите шаблон проекта консольного приложения для .NET, перейдите к разделу «Сервис >получения инструментов и компонентов«, который открывает Установщик Visual Studio. Выберите рабочую нагрузку Разработка классических приложений .NET и нажмите Изменить. Visual Studio создаст консольный проект и откроет его в Обозревателе решений в области справа.

Когда проект готов, замените код по умолчанию в файле Program.cs проекта следующим примером кода:

using System; using System.Collections.Generic; using System.Runtime.Serialization.Json; using System.Runtime.Serialization; using System.IO; namespace Console_Parse_JSON < class Program < static void Main(string[] args) < var localDB = LoadRecords(); string data = GetJsonData(); User[] users = ReadToObject(data); UpdateRecords(localDB, users); for (int i = 0; i < users.Length; i++) < Listresult = localDB.FindAll(delegate (User u) < return u.lastname == users[i].lastname; >); foreach (var item in result) < Console.WriteLine($"Matching Record, got name=, lastname=, age="); > > Console.ReadKey(); > // Deserialize a JSON stream to a User object. public static User[] ReadToObject(string json) < User deserializedUser = new User(); User[] users = < >; MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)); DataContractJsonSerializer ser = new DataContractJsonSerializer(users.GetType()); users = ser.ReadObject(ms) as User[]; ms.Close(); return users; > // Simulated operation that returns JSON data. public static string GetJsonData() < string str = "[< \"points\":4o,\"firstname\":\"Fred\",\"lastname\":\"Smith\">,]"; return str; > public static List LoadRecords() < var db = new List< >; User user1 = new User(); user1.firstname = "Joe"; user1.lastname = "Smith"; user1.totalpoints = 41; db.Add(user1); User user2 = new User(); user2.firstname = "Pete"; user2.lastname = "Peterson"; user2.totalpoints = 30; db.Add(user2); return db; > public static void UpdateRecords(List db, User[] users) < bool existingUser = false; for (int i = 0; i < users.Length; i++) < foreach (var item in db) < if (item.lastname == users[i].lastname && item.firstname == users[i].firstname) < existingUser = true; item.totalpoints += users[i].points; >> if (existingUser == false) < User user = new User(); user.firstname = users[i].firstname; user.lastname = users[i].lastname; user.totalpoints = users[i].points; db.Add(user); >> > > [DataContract] internal class User < [DataMember] internal string firstname; [DataMember] internal string lastname; [DataMember] // internal double points; internal string points; [DataMember] internal int totalpoints; >> 

Ищите красные и зеленые волнистые линии!

Прежде чем пытаться запустить пример приложения и отладчик, проверьте код в редакторе кода на наличие красных и зеленых волнистых линий. Они представляют ошибки и предупреждения, определенные анализатором кода интегрированной среды разработки. Красные волнистые линии обозначают ошибки времени компиляции, которые необходимо исправить, прежде чем можно будет запустить код. Зеленые волнистые линии обозначают предупреждения. Хотя часто приложение можно запустить без исправления предупреждений, они могут быть источником ошибок, а их устранение во многих случаях помогает сэкономить время и силы. Эти предупреждения и ошибки также можно просмотреть в окне Список ошибок, если вы предпочитаете представление списка.

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

Error showing as a red squiggle

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

Проверяйте лампочку!

Первая красная волнистая линия представляет ошибку времени компиляции. Наведите на нее указатель мыши, и появится сообщение The name `Encoding` does not exist in the current context .

Обратите внимание, что в нижнем левом углу от этой ошибки отображается значок лампочки. Наряду с значком отвертки значок screwdriver icon light bulb iconлампочки представляет быстрые действия, которые помогут вам исправить или рефакторинг кода. Лампочка представляет проблемы, которые необходимо исправить. Отвертка указывает на проблемы, которые при желании можно исправить. Используйте первое предлагаемое исправление для устранения этой ошибки, щелкнув элемент using System.Text слева.

Use the light bulb to fix code

При выборе этого элемента Visual Studio добавляет using System.Text инструкцию в верхней части файла Program.cs , а красная волнистая полоса исчезает. (Если вы не уверены в изменениях, примененных предлагаемым исправлением, выберите Предварительная версия ссылки на изменения справа перед применением исправления.)

Описанная выше ошибка является распространенной и обычно устраняется путем добавления в код нового оператора using . Существует несколько распространенных ошибок, таких как The type or namespace «Name» cannot be found. эти типы ошибок, могут указывать на отсутствующую ссылку на сборку (щелкните проект правой кнопкой мыши, выберите «Добавить> ссылку«), имя с ошибкой или недостающую библиотеку, которую необходимо добавить (для C#, щелкните проект правой кнопкой мыши и выберите «Управление пакетами NuGet»).

Исправление оставшихся ошибок и предупреждений

В этом коде есть еще несколько волнистых линий. Здесь присутствует типичная ошибка преобразования типа. При наведении указателя мыши на волнистую линию вы увидите, что код пытается преобразовать строку в int, которая не поддерживается, если только не добавить явный код для преобразования.

Type conversion error

Так как анализатор кода не может угадать ваше намерение, в данном случае лампочки, которые могут помочь вам, отсутствуют. Чтобы устранить эту ошибку, необходимо знать предназначение кода. В этом примере не слишком трудно увидеть, что points должно быть числовым (целочисленным) значением, так как вы пытаетесь добавить points в totalpoints .

Чтобы устранить эту ошибку, измените член points класса User с такого:

[DataMember] internal string points; 
[DataMember] internal int points; 

Красные волнистые линии в редакторе кода исчезают.

Затем наведите указатель мыши на зеленую волнистую линию в объявлении элемента данных points . Анализатор кода сообщает, что переменной никогда не присваивается значение.

Warning message for unassigned variable

Как правило, это указывает на проблему, которую необходимо исправить. Однако в этом примере приложения вы фактически сохраняете данные в переменной points в процессе десериализации, а затем добавляете это значение в элемент данных totalpoints . В этом примере вы знаете предназначение кода и можете спокойно проигнорировать предупреждение. Однако если вы хотите устранить это предупреждение, можно заменить этот код:

item.totalpoints = users[i].points; 
item.points = users[i].points; item.totalpoints += users[i].points; 

Зеленая волнистая линия исчезает.

Исправление исключения

Когда вы исправили все красные волнистые линии и разрешенные или по крайней мере расследованные- все зеленые волнистые волнистые линии, вы готовы запустить отладчик и запустить приложение.

Start Debugging

Нажмите клавишу F5 (Отладка > запуска отладки) или кнопку «Начать отладку» на панели инструментов отладки.

На этом этапе пример приложения выдает исключение SerializationException (ошибка времени выполнения). То есть приложение перехватывает данные, которые он пытается сериализовать. Так как приложение запущено в режиме отладки (подключен отладчик), помощник по исправлению ошибок отладчика переносит вас прямо к коду, выдавшему исключение, и выдает полезное сообщение об ошибке.

A SerializationException occurs

Сообщение об ошибке указывает, что значение 4o не может быть проанализировано как целое число. Итак, в этом примере вы знаете, что данные указаны неправильно: 4o вместо 40 . Однако если вы не управляете данными в реальном сценарии (предположим, что вы получаете его из веб-службы), что вы делаете с этим? Как это исправить?

При возникновении исключения необходимо задать себе пару вопросов:

  • Является ли это исключение простой ошибкой, которую можно исправить? или
  • Может ли это исключение возникнуть у пользователей?

В первом случае исправьте ошибку. (В примере приложения необходимо исправить плохие данные.) Если это последний вариант, может потребоваться обработать исключение в коде с помощью try/catch блока (мы рассмотрим другие возможные стратегии в следующем разделе). В примере приложения замените приведенный ниже код:

users = ser.ReadObject(ms) as User[]; 
try < users = ser.ReadObject(ms) as User[]; >catch (SerializationException) < Console.WriteLine("Give user some info or instructions, if necessary"); // Take appropriate action for your app >

Блок try/catch подразумевает некоторое снижение производительности, поэтому их следует использовать только тогда, когда это действительно необходимо, то есть, когда (а) они могут возникнуть в окончательной версии приложения и (б) документация по методу указывает, что необходимо проверить наличие исключения (с предположением, что документация завершена). Во многих случаях можно соответствующим образом обработать исключение, чтобы пользователь никогда с ним не столкнулся.

Ниже приведено несколько важных советов по обработке исключений:

  • Избегайте использования пустого блока перехвата, например catch (Exception) <> , который не принимает соответствующее действие для предоставления или обработки ошибки. Пустой или неинформативный блок перехвата может скрыть исключения и сделать код более сложным для отладки, а не проще.
  • Используйте блок try/catch вокруг конкретной функции, которая вызывает исключение ( ReadObject в примере приложения). Если использовать его вокруг большого фрагмента кода, вы в итоге скроете расположение ошибки. Например, не используйте блок try/catch вокруг вызова родительской функции ReadToObject , как показано здесь, или вы не сможете точно определить, где возникло исключение.

// Don't do this try < User[] users = ReadToObject(data); >catch (SerializationException)

Для данного примера приложения исправьте SerializationException в методе GetJsonData , изменив 4o на 40 .

Уточнение предназначения кода с помощью оператора assert

Restart App

Нажмите кнопку «Перезапустить» на панели инструментов отладки (CTRL + SHIFT + F5). Это приведет к перезапуску приложения за меньшее количество шагов. В окне консоли вы увидите приведенные ниже выходные данные.

Null value in output

Вы можете увидеть что-то в этом выходных данных неправильно. Значения имени и фамилии для третьей записи пусты!

Это подходящий момент, чтобы рассмотреть полезный и часто недооцененный подход к написанию кода, который заключается в использовании операторов assert в функциях. Добавив следующий код, вы включаете среду выполнения проверка, чтобы убедиться, что firstname и lastname нет null . Замените приведенный ниже код в методе UpdateRecords :

if (existingUser == false) < User user = new User(); user.firstname = users[i].firstname; user.lastname = users[i].lastname; 
// Also, add a using statement for System.Diagnostics at the start of the file. Debug.Assert(users[i].firstname != null); Debug.Assert(users[i].lastname != null); if (existingUser == false) < User user = new User(); user.firstname = users[i].firstname; user.lastname = users[i].lastname; 

Добавив такие операторы assert в функции в процессе разработки, можно указать предназначение кода. В предыдущем примере мы укажем следующие элементы:

  • Для имени требуется допустимая строка.
  • Для фамилии требуется допустимая строка.

Указав таким образом намерение, вы применяете необходимые требования. Это простой и удобный способ, который можно использовать для изучения ошибок во время разработки. (Операторы assert также используются в качестве главного элемента в модульных тестах.)

Restart App

Нажмите кнопку "Перезапустить" на панели инструментов отладки (CTRL + SHIFT + F5).

Код assert активен только в отладочной сборке.

При перезапуске отладчик приостанавливает выполнение на операторе assert , так как выражение users[i].firstname != null вычисляется как false вместо true .

Assert resolves to false

Ошибка assert указывает на проблему, которую необходимо изучить. assert может охватывать множество сценариев, в которых исключение отображается не всегда. В этом примере пользователь не видит исключения, а null значение добавляется как firstname в списке записей. Это условие может привести к проблемам позже (например, в выходных данных консоли) и может быть труднее выполнить отладку.

В сценариях, где вы вызываете метод для значения null , возникает NullReferenceException . Обычно следует избегать использования блока try/catch для общего исключения, то есть исключения, не привязанного к определенной функции библиотеки. Любой объект может вызывать NullReferenceException . Если вы не уверены, изучите документацию по функции библиотеки.

Во время процесса отладки рекомендуется использовать определенный оператор assert , пока не станет понятно, что его нужно заменить реальным исправлением кода. Допустим, вы решили, что пользователь может столкнуться с исключением в сборке выпуска приложения. В этом случае необходимо выполнить рефакторинг кода, чтобы убедиться, что приложение не выдает неустранимое исключение или не приводит к возникновению какой-либо другой ошибки. Итак, чтобы исправить этот код, замените приведенный ниже код:

if (existingUser == false) < User user = new User(); 
if (existingUser == false && users[i].firstname != null && users[i].lastname != null) < User user = new User(); 

Используя этот код, вы выполняете требования к коду и убедитесь, что запись с firstname или lastname значением null не добавляется в данные.

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

public static void UpdateRecords(List db, User[] users) < Debug.Assert(db != null); Debug.Assert(users != null); 

Для предыдущих операторов ваше намерение заключается в том, чтобы загрузить существующие данные ( db ) и извлечь новые данные ( users ) перед обновлением.

Оператор assert можно использовать с любым видом выражения, которое разрешается в true или false . Например, можно добавить оператор assert следующего вида.

Debug.Assert(users[0].points > 0); 

Приведенный выше код полезен, если необходимо указать следующее намерение: для обновления записи пользователя требуется новое значение точки больше нуля (0).

Проверка кода в отладчике

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

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

  • Вы пытаетесь изолировать ошибку среды выполнения в коде, но не можете сделать это с помощью методов и инструментов, которые ранее обсуждались.
  • Вы хотите проверить код, то есть смотреть его во время его выполнения, чтобы убедиться, что он ведет себя так, как вы ожидаете и делаете то, что вы хотите. Это поучительно смотреть код во время его выполнения. Так вы можете получить дополнительные сведения о коде и выявить ошибки до того, как проявляются их очевидные симптомы.

Дополнительные сведения об использовании функций отладчика см. в статье Отладка для начинающих.

исправлять проблемы производительности;

Ошибки другого типа включают неэффективный код, из-за которого приложение выполняется медленнее или использует слишком много памяти. Как правило, оптимизация производительности осуществляется на более позднем этапе разработки приложения. Однако вы можете столкнуться с проблемами с производительностью раньше (например, вы видите, что некоторые части приложения работают медленно), и вам может потребоваться протестировать приложение с помощью средств профилирования на ранних этапах. Дополнительные сведения о средствах профилирования, таких как средство загрузки ЦП и анализатор памяти, см. в разделе Знакомство со средствами профилирования.

Связанный контент

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *