Pull to refresh

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

Reading time4 min
Views3.1K
Многие из нас пользуются отладчиком 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_MACHINE\SOFTWARE\Microsoft\VisualStudio\10.0\NativeDE\StepOver — для х86
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\NativeDE\StepOver — для 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:operator\oper:

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

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

Вывод:

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

Ложка дегтя.


Допустим, мы хотим вообще не попадать внутрь stl и, заодно, boost. Легко! Добавим соответствующие строчки в реестр… Но что делать, если хочется, чтобы при нажатии F11 на вот таком коде:
std::sort( vec.begin(), vec.end(), my_pred() );
мы попадали внутрь my_pred::operator(), если он вызовется?

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

Читайте в следующей серии.
Tags:
Hubs:
Total votes 33: ↑31 and ↓2+29
Comments16

Articles