Каков наилучший способ присоединить отладчик к процессу в VC++ в нужный момент времени?
При отладке иногда требуется присоединить уже запущенный процесс, а не просто запустить приложение в отладчике.
Я обычно использую вызов Sleep() или MessageBox, чтобы было проще подключить отладчик. Я беспокоюсь о том, что некоторые из них могут в конечном итоге перейти к контролю над источниками.
Что лучше всего сделать, чтобы избежать этой ситуации, при этом откладывая достаточно времени, чтобы вы могли подключить отладчик к работающему процессу?
Охрана сна или окно сообщения с #ifdef _DEBUG
Это один из способов, но мне интересно, есть ли лучший способ.
Во время сна у вас также есть проблема, которую вы не можете вовремя приложить. С MessageBox у вас проблема в том, что вы можете удаленно отлаживать или отлаживать процесс, у которого нет видимого графического интерфейса (например, в качестве службы в Vista)
7 ответов
Вы можете использовать DebugBreak, проверьте эти ссылки:
http://www.epsilon-delta.net/articles/vc6_debug.html
http://blogs.msdn.com/calvin_hsia/archive/2006/08/25/724572.aspx
Другой вариант, который я иногда использую,
while( !::IsDebuggerPresent() )
::Sleep( 100 ); // to avoid 100% CPU load
он должен просто молча подождать, пока вы не подключите отладчик к процессу.
Чтобы прикрепить отладчик к определенной точке, у вас есть несколько вариантов:
Самое простое просто позвонить DebugBreak
, что в значительной степени эквивалентно __asm int 3
, но также работает на других архитектурах (MSVC для x64 не позволяет встроенную сборку, если я правильно помню). Это откроет окно отладчика точно в срок, и вы сможете выбрать из зарегистрированных отладчиков (например, Visual Studio) для подключения к процессу.
Кроме того, вы можете ввести вызов Sleep
, давая вам возможность подключить отладчик. Вы должны использовать #ifdef _DEBUG
вокруг этого, чтобы гарантировать, что вы на самом деле не отправляете с этим кодом.
Один вопрос: почему вы не можете запустить код из IDE? Это служба или DLL-библиотека, загруженная IIS, или аналогичная?
В этом случае вы можете проверить ImageFileExecutionOptions
ключ реестра, который позволяет подключить отладчик в момент запуска процесса.
Если вы используете для этого cdb, вы можете настроить его в качестве сервера или клиента для экземпляра WinDbg и выполнить отладку таким образом. Я делал это в прошлом, используя WinDbg в качестве отладчика ядра и используя ImageFileExecutionOptions для запуска ntsd -d
с названным процессом. Это заставляет WinDbg переходить в режим пользователя. Это иногда полезная техника.
Фредди и Реоа имеют правильные решения. Но я хотел добавить причину, почему бы не использовать MessageBox.
Отображение MessageBox только частично останавливает ваше приложение. Поскольку вы показываете пользовательский интерфейс, насос сообщений все еще работает по крайней мере в одном потоке в вашей программе. Так что, если ваш код выполняет любое из следующих действий.
- Общается через сообщения Windows
- Имеет нетривиальный интерфейс
- Многопоточный
По сути, вы будете запрашивать отладчик в одном состоянии, но подключаться к своей программе в совершенно другом состоянии. Это может привести к недоразумениям и ошибкам.
Недавно мы внесли изменения в нашу кодовую базу, чтобы никогда не показывать MessageBox, чтобы облегчить разрыв по этой самой причине. Это приводит к очень плохому поведению для нетривиального приложения.
Необходимость присоединения в "правильной точке" - это боль... один из вариантов - это просто включить в код операторы DebugBreak(), чтобы вызвать проблему, и защитить их с помощью #ifdef _DEBUG
было бы хорошей идеей. Мы используем макрос ASSERT, который может вызывать DebugBreak(), поэтому вы можете просто написать ASSERT(false)
Другой вариант, который стоит рассмотреть, - это использовать "опции исполнения файла изображения" для автоматического запуска отладчика. Смотрите этот блог и документацию MSDN.
__asm int 3
Эта жесткая точка останова вызовет диалог отладки, который позволит вам присоединиться к процессу. Оберните это в #ifdef _DEBUG, и вы попадете только в отладочные сборки.
Уважать:
DebugBreak, __debugbreak и друзья
или же
static void timeToChase() { __asm { int 3; }; }
Если __debugbreak() или DebugBreak() сработают для вас, я думаю, что это, вероятно, лучший подход.
Однако я столкнулся с некоторыми обстоятельствами, когда по неизвестным причинам __debugbreak() просто немедленно завершал программу, вместо того, чтобы ждать подключения отладчика. Я пробовал различные способы решения этой проблемы (взлом реестра и т. Д.), Но ничего не помогало. (Может быть важно, что в этом случае процесс, который я пытался отлаживать, не запускался непосредственно из командной строки, а вместо этого был запущен программой Java.)
В любом случае, в этом случае я использовал этот подход, который, похоже, работал хорошо:
void waitForDebuggerToConnect() {
#ifdef _DEBUG
static volatile bool spin = true;
while (spin)
{}
#endif
}
После того как я вызвал эту функцию, моя программа зависала на неопределенное время в цикле while. Затем я мог бы вызвать отладчик VC++ и подключиться к процессу. Затем я мог бы использовать "сломать все", чтобы остановить все потоки. Затем я мог найти стек вызовов застрявшего потока и мог использовать отладчик для проверки верхнего кадра и мог проверить значение 'spin', а затем установить для него значение 'false'. На этом этапе я мог бы выйти из этой функции и / или установить точки останова, а затем возобновить выполнение некоторых или всех потоков.