Для большинства случаев дебаггинга вам должно с головой хватать стандартных точек останова (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:

Рисунок 1: Установка точки останова для перехвата интересующих нас прямоугольников
Рисунок 1: Установка точки останова для перехвата интересующих нас прямоугольников

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

Как вы могли заметить, в Visual Studio 2015 значительно улучшили диалоговое окно для добавления условий (Conditions) и действий (Actions). Например, вы можете открыть его и продолжать редактировать код. Ранее (как, например, в Visual Studio 2013, показанном на рисунке 2) все окна для условий (“Filter”, “Condition” и “When Hit”) были стандартными модальными окнами. Более того, само по себе окно намного чище.

Рисунок 2: Для сравнения аналог окна из Visual Studio 2013 
Рисунок 2: Для сравнения аналог окна из Visual Studio 2013 

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

Expression Evaluator (встроенное средство вычисления выражений) — мы можем писать код внутри редактора условий, но есть некоторые ограничения: вы не можете вызывать код, который имеет какие-либо побочные эффекты, создает новые переменные, содержит new/delete или макросы. К сожалению, это также означает, что мы не можем вызвать пользовательскую функцию с локальными переменными, поскольку она изменяет текущее состояние приложения.

Но у нас в арсенале есть ряд встроенных функций string ('strlen', 'strcmp', 'strst', ...), system ('GetLastError') и некоторые математические функции.

Полный список выражений, которые можно использовать для создания условий, вы найдете в статье MSDN «Выражения в отладчике».

Но это еще не все.

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

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

  • Счетчик срабатываний (Hit Count): точка останова будет срабатывать после заданного количества итераций, например, каждый пятый раз или только в десятый, или после сотой итераций.

  • Фильтр (Filter): позволяет фильтровать точки останова на данной машине, процессе или потоке.

Хотя это и не является условием, в окне установки условной точки останова вы можете указать “действие” (action), которое будет выполняться при попадании в точку останова. Например, мы можем вывести значение переменной или текущее состояние стека. Это очень удобно, поскольку означает, что мы можем динамически вставлять трассировочные операторы без перекомпиляции кода.

Условные точки останова дают нам возможность сосредоточиться на конкретных проблемах и случаях. Это особенно важно в ситуациях, когда нужно оперировать огромным количеством объектов или событий. Грамотное использование условных точек останова может сэкономить вам кучу времени.

Код примера можно найти в моем репозитории на GitHub.

Ссылки:


Статья подготовлена в преддверии старта курса "C++ Developer. Professional".

А уже сегодня вечером состоится открытое занятие «ООП глазами C++». Хоть и модно критиковать ООП-подход к разработке кода, он остаётся самым популярным во многих и многих сферах. Поэтому не знать и не уметь использовать данную парадигму разработки для настоящего профессионала просто не вежливо. На вебинаре поговорим и посмотрим на примерах о том, как термины ООП реализуются в синтаксисе языка C++. Регистрируйтесь по ссылке.

Комментарии (7)


  1. Ingulf
    20.09.2022 17:38
    +7

    что-то подсказывает, что не условные брейкпоинтеры в С++, а в условные брейкпоинтеры в MSVS


    1. TheCalligrapher
      21.09.2022 04:22

      "Брейкпоинтеры" [x]


  1. Ritan
    20.09.2022 19:26
    +1

    Качество материала, конечно, нулевое. Мало того, что от C++ тут только название курса и две строки кода, так ещё и рассказано об одной( и весьма спорной ) IDE.

    В lldb и gdb тоже есть условные брекпоинты.

    И ещё стоило бы упомянуть, что это счастье будет неимоверно тормозить работающее под дебаггером приложение, если бряк стоит в горячем коде. Тормозит настолько, что минут 5-10 пересборки проекта выгоднее если бряк не ловит ничего в первых итерациях


    1. TheCalligrapher
      21.09.2022 04:21

      "Споры об IDE" закончились лет пятнадцать назад. Общепризнанной вершиной Олимпа IDE является Microsoft Visual Studio. Все остальное в этой области - это лишь попытки в условиях ограниченных ресурсов reinvent каждую фичу в VS чуть ли не попиксельно. Poorly, разумеется...

      Что касается использования условных точек останова - это верно, торможение от их использования в общем случае будет существенным. И в условиях разработки "на своей базе" действительно в большинстве случаев лучше просто явно добавить в код соответствующий if и перекомпилировать.

      Но иногда приходится отлаживаться и "на вражеской территории", то есть на клиентском оборудовании и в клиентском окружении, голым gdb без возможности редактирования и/или перекомпиляции кода. В таких случаях помогает стратегическое создание в критических точках кода специальных параметризуемыхif-закладок, призванных отлавливать наиболее часто встречающиеся потребности в условной остановке. То есть что-то вроде

      #ifdef DEBUG
        static unsigneed break_id;
        if (new_object->id == break_id)
          some_sort_of_breakpoint_placeholdfer;
      #endif // DEBUG

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


      1. netch80
        21.09.2022 21:58

        > «Споры об IDE» закончились лет пятнадцать назад. Общепризнанной вершиной Олимпа IDE является Microsoft Visual Studio.

        Тут бы каких-то доказательств, чтобы не получилось «чемпиона мира по версии газеты «Вестник Белогорья»».

        Я лично нахожу VS существенно неудобной в целой пачке деталей.


      1. hard2018
        21.09.2022 22:52

        Такие слепые брекпоинты можно ставить с помощью виндовой DebugBreak. Внутри она вызывает отладочное прерывание процессора(int 3) и вам показывают окно с просьбой найти отладчик.


      1. Ritan
        23.09.2022 09:10

        "Споры об IDE" закончились лет пятнадцать назад.

        Закончились настолько решительно и бесповоротно, что многие не представляют работу без ReSharper и/или VisualAssist

        Poorly, разумеется

        CLion смотрит на ваши слова с недоумением, как на несмешную шутку. Почти все фичи без исключения там сделаны лучше, чем в Visual Studio, включая те, которые в VS появились раньше. И это не отменяет общей кривости CLion