Существует ли переносимый эквивалент DebugBreak()/__b debugbreak?
В MSVC, DebugBreak () или __debugbreak вызывают сбой отладчика. На x86 это эквивалентно написанию "_asm int 3", на x64 это что-то другое. При компиляции с помощью gcc (или любого другого стандартного компилятора) я также хочу сделать разрыв в отладчике. Есть ли независимая от платформы функция или встроенная? Я видел вопрос XCode об этом, но он не выглядит достаточно портативным.
Sidenote: Я в основном хочу реализовать ASSERT с этим, и я понимаю, что могу использовать assert() для этого, но я также хочу написать DEBUG_BREAK или что-то еще в коде.
11 ответов
Как насчет определения условного макроса на основе #ifdef, который расширяется до различных конструкций на основе текущей архитектуры или платформы.
Что-то вроде:
#ifdef _MSC_VER
#define DEBUG_BREAK __debugbreak()
#else
...
#endif
Это будет расширено препроцессором до правильной инструкции прерывания отладчика на основе платформы, на которой компилируется код. Таким образом, вы всегда используете DEBUG_BREAK
в вашем коде.
Метод, который переносим для большинства систем POSIX:
raise(SIGTRAP);
Я просто добавил модуль в portable-snippets (набор фрагментов общедоступного домена переносимого кода), чтобы сделать это. Это не на 100% портативно, но должно быть довольно надежно:
__builtin_debugtrap
для некоторых версий clang__has_builtin(__builtin_debugtrap)
)- На MSVC и компиляторе Intel C/C++:
__debugbreak
- Для компилятора ARM C/C++:
__breakpoint(42)
- Для x86/x86_64 сборка:
int $03
- Для большого пальца руки, сборка:
.inst 0xde01
- Для ARM AArch64, сборка:
.inst 0xd4200000
- Для других ARM, сборка:
.inst 0xe7f001f0
- Для Альфы, сборка:
bpt
- Для не размещенного C с GCC (или что-то, что маскируется под него),
__builtin_trap
- В противном случае, включить
signal.h
а также- Если
defined(SIGTRAP)
(то есть POSIX),raise(SIGTRAP)
- Иначе,
raise(SIGABRT)
- Если
В будущем модуль в переносимых фрагментах может расширяться, чтобы включать другую логику, и я, вероятно, забуду обновить этот ответ, так что вы должны искать там обновления. Это общественное достояние (CC0), поэтому не стесняйтесь воровать код.
GCC имеет встроенную функцию с именем __builtin_trap
что вы можете увидеть здесь, однако предполагается, что выполнение кода останавливается, как только это будет достигнуто.
Вы должны убедиться, что __builtin_trap()
Вызов условный, иначе код после него не будет выдан.
этот пост подпитывается всеми 5 минутами тестирования YMMV.
Это похоже на соответствующую библиотеку compat https://github.com/scottt/debugbreak
Кажется, это очень хорошее портативное решение этого вопроса:https://github.com/scottt/debugbreak
Заголовок, представленный в указанном репозитории (debugbreak.h), инкапсулирует файлы MSVC
__debugbreak,
а также
__asm__ volatile("int $0x03");
на i386 и x86_64, а на ARM реализует
__asm__ volatile(".inst 0xe7f001f0");
а также документировать некоторые обходные пути для проблем, отмеченных в заголовке для пошагового мимо точки останова в GDB плюс сценарий Python для расширения GDB на тех платформах, где СТЕПЬ или продолжение застревает. Сценарий добавляет в GDB шаг debugbreak-step и debugbreak-continue.
Если вы считаете assert(x) достаточно переносимым, assert(false) кажется очевидным переносимым решением вашей проблемы.
Если вы пытаетесь отладить состояние, связанное с падением, старый добрый abort() даст вам стек вызовов на большинстве платформ. Недостатком является то, что вы не можете продолжить с текущего ПК, что вы, вероятно, не хотите делать в любом случае.
FWIW, ни одно из этих решений не работало на nRF9160 с использованием NRF Connect SDK. Это среда SEGGER Embedded Studio for ARM (Nordic Edition), использующая
arm-none-eabi-gcc
компилятор.
В
debug-trap.h
,
debugbreak.h
а также
__builtin_trap()
упомянутые в других ответах, все привели к «неопределенному коду операции» и жесткой ошибке (или ошибке монитора отладки, но результат тот же), и нет полезного программного счетчика, кадра стека или другой отлаживаемой информации.
В конце концов, эта альтернатива сработала. Я извлек его из другой загадочной скандинавской библиотеки, где он упоминается как
NRF_BREAKPOINT
:
#if defined(__GNUC__)
__asm__("BKPT 0");
#else
__BKPT(0)
#endif
Во время сборки это
__GNUC__
путь, который включается, поэтому
__asm__("BKPT 0")
это все, что требуется.
#define __debugbreak() \
do \
{ static bool b; \
while (!b) \
sleep(1); \
b = false; \
} while (false)
Когда процесс находится в спящем режиме, вы можете присоединить к нему отладчик, изменить переменную b, чтобы разорвать цикл и сделать свое дело. Этот код может не работать в оптимизированной сборке!
Вместо использования "нормальных" разрывов отладки, почему бы не использовать одно из следующего, например деление на ноль:
int iCrash = 13 / 0;
или разыменуйте нулевой указатель:
BYTE bCrash = *(BYTE *)(NULL);
По крайней мере, это переносимо во многих платформах / архитектурах.
Во многих отладчиках вы можете указать, какое действие вы хотите выполнить с какими исключениями, чтобы вы могли действовать соответственно, когда выполняется одно из вышеперечисленных действий (например, выполнение паузы, ala инструкция "int 3") и генерируется исключение.