Определить строку кода, которая вызывает ошибку сегментации? - PullRequest
125 голосов
/ 20 мая 2010

Как определить, где в нашем коде ошибка, которая вызывает ошибку сегментации ?

После написания некоторого кода, чтобы определить, где у меня ошибка сегментации, может ли мой компилятор (gcc) показать мне местоположение ошибки в моей программе?

Ответы [ 5 ]

175 голосов
/ 20 мая 2010

GCC не может этого сделать, но GDB точно может. Скомпилируйте свою программу, используя переключатель -g, например:

gcc program.c -g

Тогда используйте gdb:

$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>

Здесь - хорошее руководство, чтобы начать работу с GDB.

36 голосов
/ 20 мая 2010

Кроме того, вы можете попробовать Valgrind: если вы установите Valgrind и запустите valgrind --leak-check = full, тогда он запустит вашу программу и отобразит трассировки стека для любых ошибок, а также любые недопустимые операции чтения или записи в память и утечки памяти. Это действительно очень полезно.

15 голосов
/ 20 мая 2010

Вы также можете использовать дамп ядра, а затем проверить его с помощью gdb. Для получения полезной информации вам также необходимо скомпилировать с флагом -g.

Всякий раз, когда вы получаете сообщение:

 Segmentation fault (core dumped)

файл ядра записан в ваш текущий каталог. И вы можете проверить это с помощью команды

 gdb your_program core_file

Файл содержит состояние памяти при сбое программы. Дамп ядра может быть полезен при развертывании вашего программного обеспечения.

Убедитесь, что ваша система не устанавливает нулевой размер файла дампа ядра. Вы можете установить его неограниченным с помощью:

ulimit -c unlimited

Осторожнее, хотя! что основные дампы могут стать огромными.

2 голосов
/ 11 мая 2019

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

2 голосов
/ 20 мая 2010

Ответ Лукаса о дампах ядра хорош. В моем .cshrc у меня есть:

alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'

, чтобы отобразить обратную трассировку, введя 'core'. И штамп с датой, чтобы убедиться, что я смотрю на нужный файл: (.

Добавлено : Если есть ошибка повреждения stack , то обратная трассировка, применяемая к дампу ядра, часто является мусором. В этом случае запуск программы в пределах gdb может дать лучшие результаты в соответствии с принятым ответом (при условии, что ошибка легко воспроизводима). А также остерегайтесь нескольких процессов, сбрасывающих ядро ​​одновременно; некоторые ОС добавляют PID к имени основного файла.

...