Каков наилучший способ присоединить отладчик к процессу в 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 только частично останавливает ваше приложение. Поскольку вы показываете пользовательский интерфейс, насос сообщений все еще работает по крайней мере в одном потоке в вашей программе. Так что, если ваш код выполняет любое из следующих действий.

  1. Общается через сообщения Windows
  2. Имеет нетривиальный интерфейс
  3. Многопоточный

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

Недавно мы внесли изменения в нашу кодовую базу, чтобы никогда не показывать 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'. На этом этапе я мог бы выйти из этой функции и / или установить точки останова, а затем возобновить выполнение некоторых или всех потоков.

Другие вопросы по тегам