Многие из нас пользуются отладчиком 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