Для большинства случаев дебаггинга вам должно с головой хватать стандартных точек останова (breakpoints или брейкпоинтов). Но иногда кода для проверки, объектов или случаев попросту слишком много. Что делать, если мы хотим отфильтровать код, генерирующий точку останова? Прошу вас поприветствовать условные точки останова!
В подавляющем большинстве случаев мы ограничиваемся стандартными точками останова функций. Мы просто устанавливаем точку останова в определенной функции и дожидаемся ее вызова. Но если код, который нам необходимо отлаживать, сложен, мы можем воспользоваться дополнительной фичей брейкпоинтов: условиями.
Давайте рассмотрим простой пример: что, если нам нужно отлаживать цикл, который перебирает 100 прямоугольников, и у нас есть подозрение, что прямоугольники большого размера каким-то образом повреждаются в результате обработки в рамках этого цикла? Вы же не будете отлаживать каждую итерацию этого цикла? Вас может еще хватить на первые десять объектов, но перебирать вручную сто или даже тысячу - это безумие! Возможно, мы могли бы установить условную точку останова и работать только с большими прямоугольниками, с которыми у нас возникает проблема.
Вот наш код:
// Делаем несколько прямоугольников большими
// left, top, right, bottom
rects[7] = { 10, 100, 200, 0 };
rects[17] = { 10, 100, 200, 0 };
rects[37] = { 10, 100, 200, 0 };
ScanForInvalidRects(rects);
for (auto &r : rects)
{
ScanRectangle(r);
}
ScanForInvalidRects(rects);
Похоже, что наш код делает что-то с прямоугольниками, ширина которых больше 100. Поэтому, чтобы отловить эти прямоугольники, мы установим точку останова, подобную той, что показана на рисунке 1:
Когда мы будем отлаживать этот код, дебаггер остановится, только если будет удовлетворено указанное нами условие. Мы можем сосредоточиться на проблемных случаях, сэкономив кучу времени.
Как вы могли заметить, в Visual Studio 2015 значительно улучшили диалоговое окно для добавления условий (Conditions) и действий (Actions). Например, вы можете открыть его и продолжать редактировать код. Ранее (как, например, в Visual Studio 2013, показанном на рисунке 2) все окна для условий (“Filter”, “Condition” и “When Hit”) были стандартными модальными окнами. Более того, само по себе окно намного чище.
Теперь, когда мы знаем, как создавать простые условия, давайте углубимся в более сложные возможности.
Expression Evaluator (встроенное средство вычисления выражений) — мы можем писать код внутри редактора условий, но есть некоторые ограничения: вы не можете вызывать код, который имеет какие-либо побочные эффекты, создает новые переменные, содержит new/delete или макросы. К сожалению, это также означает, что мы не можем вызвать пользовательскую функцию с локальными переменными, поскольку она изменяет текущее состояние приложения.
Но у нас в арсенале есть ряд встроенных функций string
('strlen
', 'strcmp
', 'strst
', ...), system
('GetLastError
') и некоторые математические функции.
Полный список выражений, которые можно использовать для создания условий, вы найдете в статье MSDN «Выражения в отладчике».
Но это еще не все.
В крайнем случае, если нам нужно создать более сложное условие, которое невозможно реализовать с помощью встроенных условных брейкпоинтов, мы можем задействовать для этого собственный код. Мы можем написать условие в нашем коде и, когда это условие выполняется, вызывать функцию DebugBreak. Конечно, наш код нужно перекомпилировать каждый раз, когда мы будем вносить изменения.
По умолчанию окна условий для точек останова позволяют нам накладывать ряд предопределенных условий для ваших локальных переменных, но на них наши возможности не ограничиваются:
* Счетчик срабатываний (Hit Count): точка останова будет срабатывать после заданного количества итераций, например, каждый пятый раз или только в десятый, или после сотой итераций.
* Фильтр (Filter): позволяет фильтровать точки останова на данной машине, процессе или потоке.
Хотя это и не является условием, в окне установки условной точки останова вы можете указать “действие” (action), которое будет выполняться при попадании в точку останова. Например, мы можем вывести значение переменной или текущее состояние стека. Это очень удобно, поскольку означает, что мы можем динамически вставлять трассировочные операторы без перекомпиляции кода.
Условные точки останова дают нам возможность сосредоточиться на конкретных проблемах и случаях. Это особенно важно в ситуациях, когда нужно оперировать огромным количеством объектов или событий. Грамотное использование условных точек останова может сэкономить вам кучу времени.
Код примера можно найти в моем репозитории на GitHub.
Ссылки:
MSDN: Expressions in the Debugger
StackOverflow: Conditional breakpoint: This expression has side effects and will not be evaluated
MSDN: New Breakpoint Configuration Experience in Visual Studio 2015
MSDN: Conditional Breakpoints
Статья подготовлена в преддверии старта курса "C++ Developer. Professional".
А уже сегодня вечером состоится открытое занятие «ООП глазами C++». Хоть и модно критиковать ООП-подход к разработке кода, он остаётся самым популярным во многих и многих сферах. Поэтому не знать и не уметь использовать данную парадигму разработки для настоящего профессионала просто не вежливо. На вебинаре поговорим и посмотрим на примерах о том, как термины ООП реализуются в синтаксисе языка C++. Регистрируйтесь по ссылке.