MSVS Debugger.UNDOCUMENTED, ч.1 — Управление пошаговым выполнением

в 12:16, , рубрики: c++, debugging, lifehack, step-by-step, Visual Studio, метки: , , ,

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

В этой статье я расскажу, как избавиться от постоянного попадания внутрь библиотечного кода при пошаговом Step-In выполнении. Это для затравки. Позже я опишу механизм управления представлением типов в отладочных окнах типа locals и watch (все видели, как красиво там отображаются вектора/карты и т.п.?).

Пожалуйста учтите, что основной упор будет сделан на отладку Native кода.

И так, приступим.

Если Вы уже перешли на С++11, то Вам должны быть знакомы строчки кода наподобие следующих:

do_something( std::move( some_obj ) );

Ну, или таких:

do_something( std::forward< T1 >( rr1 ), std::forward< T2 >( rr2 ), std::forward< T3 >( rr3 ) );

По крайней мере я подобные вещи вижу при отладке достаточно часто. И вот что бесит: стоишь на таком вызове, хочется нажать F11 и попасть внутрь do_something, ан нет! сначала мы попадаем внутрь всех этих std::move и std::forward и только потом куда хотели.

Возьмем, хотя бы второй пример (с тремя вызовами std::forward).
Чтобы попасть внутрь do_something надо нажать F10, F10, F10, F11. Это самый быстрый способ. Только им никто не пользуется: легко ошибиться с количеством F10 и «прошагать» нужный вызов. Поэтому жмем Step In, Step Out до тех пор, пока не попадем куда надо. Это получается F11, Shift-F11, F11, Shift-F11, F11, Shift-F11, F11 — ужас какой-то! И даже при этом лично я регулярно промахиваюсь мимо нужного вызова.

И вот однажды мне это надоело. Как было бы хорошо, если бы Step In игнорировал некоторые функции, хотя бы move и forward (чего я в них не видел?), — подумал я. И начал искать.

Быстро выяснилась, что такая опция есть для .NET — «Just my code», «Step over properties and operators», «Enable .NET framework source stepping», и ее даже можно руками настраивать через атрибуты. Для native кода подобных параметров нету… или есть?

Оказалось, что все-таки есть. В реестре, неофициальные.

Пошаговое выполнение: недокументированные функции отладчика native кода.

Нас будет интересовать ветка:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftVisualStudio10.0NativeDEStepOver — для х86
HKEY_LOCAL_MACHINESOFTWAREWow6432NodeMicrosoftVisualStudio10.0NativeDEStepOver — для x64

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

Синтаксис значения следующий:

<регулярное выражение, соответствующее названию функции>[=NoStepInto]

"=NoStepInto" означает, что отладчик никогда не должен заходить внутрь указанной функции. Вы можете смело опускать "=NoStepInto", т.к. это поведение по умолчанию, а другие «указания» отладчику мне не известны.

Синтаксис регулярных выражений следующий — тот же, что и в поиске, плюс следующие команды:

"cid:"         - Имя C/C++ идентификатора
"funct:"      - Имя С/С++ функции (кажется, это тоже самое, что и cid:)
"scope:"    - Набор идентификаторов класса/пространства
                    имен для функции (например, ATL::CFoo::CBar::)
                    "scope:" не может соответсовать пустой строке
"anything:" - Любая строка, в т.ч. и без кавычек
"oper:"       - С++ оператор (например, "*")

Примеры:

Не входить в std::forward:

std::forward<.*>

Не входить в std::move:

std::move<.*>

Не входить в функции пространтва имен «test»:

test::funct:

Не входить в перегруженные операторы:

scope:operatoroper:

Дополнительные замечания:

  • Visual Studio читает настройки при запуске, так что для того, чтобы они вступили в силу студию придется перезапустить.
  • Если очень хочется попасть внутрь функции, которая уже внесена «в реестр», это можно сделать либо поставив внутри нее точку останова, либо, сделав Step In в окне disassembly.

Вывод:

Аллилуйя! Подредактировав реестр мы больше никогда не будем попадать в std::forward! Теперь при нажатии F11 мы попадаем сразу туда, куда хотели: внутрь нашего кода.

Ложка дегтя.

Допустим, мы хотим вообще не попадать внутрь stl и, заодно, boost. Легко! Добавим соответствующие строчки в реестр… Но что делать, если хочется, чтобы при нажатии F11 на вот таком коде:

std::sort( vec.begin(), vec.end(), my_pred() );

мы попадали внутрь my_pred::operator(), если он вызовется?

Как сделать так, чтобы можно было контролировать, хотим ли мы попадать при отладке внутрь функции на уровне исходного кода (в C# это делается через атрибуты)?

Читайте в следующей серии.

Автор: Wyrd

* - обязательные к заполнению поля