Существует ряд инструментов, которые помогают отлаживать ошибки сегментации, и я хотел бы добавить свой любимый инструмент в список: Обеззараживатели адресов (часто сокращенно ASAN) .
Современные¹ компиляторы поставляются с удобным флагом -fsanitize=address
, добавляющим некоторое время компиляции и время выполнения, что делает больше проверки ошибок.
Согласно документации эти проверки по умолчанию включают обнаружение ошибок сегментации. Преимущество здесь в том, что вы получаете трассировку стека, аналогичную выводу GDB, но без запуска программы внутри отладчика. Пример:
int main() {
volatile int *ptr = (int*)0;
*ptr = 0;
}
$ gcc -g -fsanitize=address main.c
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==4848==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5654348db1a0 bp 0x7ffc05e39240 sp 0x7ffc05e39230 T0)
==4848==The signal is caused by a WRITE memory access.
==4848==Hint: address points to the zero page.
#0 0x5654348db19f in main /tmp/tmp.s3gwjqb8zT/main.c:3
#1 0x7f0e5a052b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
#2 0x5654348db099 in _start (/tmp/tmp.s3gwjqb8zT/a.out+0x1099)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /tmp/tmp.s3gwjqb8zT/main.c:3 in main
==4848==ABORTING
Вывод немного сложнее, чем выводит GDB, но есть и плюсы:
Нет необходимости воспроизводить проблему для получения трассировки стека. Достаточно просто включить флаг во время разработки.
ASAN обнаруживают намного больше, чем просто ошибки сегментации. Многие выходы за границы будут обнаружены, даже если эта область памяти была доступна для процесса.
¹ То есть Clang 3.1 + и GCC 4.8 + .