답변:
GCC는 그렇게 할 수 없지만 GDB ( 디버거 )는 가능합니다. 다음 -g
과 같이 스위치를 사용하여 프로그래밍 하십시오.
gcc program.c -g
그런 다음 gdb를 사용하십시오.
$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>
다음 은 GDB를 시작하는 데 유용한 자습서입니다.
segfault가 발생하는 위치는 일반적으로 코드에서 "발생하는 실수"의 위치에 대한 단서 일뿐입니다. 주어진 위치가 반드시 문제가있는 곳은 아닙니다.
bt
의 속기로 사용하십시오 backtrace
.
또한 valgrind
시도해 볼 수 있습니다 : 설치 valgrind
하고 실행하면
valgrind --leak-check=full <program>
그런 다음 프로그램을 실행하고 유효하지 않은 메모리 읽기 또는 쓰기 및 메모리 누수뿐만 아니라 모든 segfault에 대한 스택 추적을 표시합니다. 정말 유용합니다.
--leak-check=full
segfaults를 디버깅하는 데 도움이되지 않습니다. 메모리 누수 디버깅에만 유용합니다.
코어 덤프를 사용한 다음 gdb로 검사 할 수도 있습니다. 유용한 정보를 얻으려면 -g
플래그 로 컴파일해야합니다 .
메시지가 나타날 때마다 :
Segmentation fault (core dumped)
코어 파일이 현재 디렉토리에 작성됩니다. 그리고 당신은 명령으로 그것을 확인할 수 있습니다
gdb your_program core_file
파일에는 프로그램 충돌시 메모리 상태가 포함됩니다. 코어 덤프는 소프트웨어 배포 중에 유용 할 수 있습니다.
시스템이 코어 덤프 파일 크기를 0으로 설정하지 않았는지 확인하십시오. 다음을 사용하여 무제한으로 설정할 수 있습니다.
ulimit -c unlimited
조심해! 코어 덤프가 커질 수 있습니다.
세그멘테이션 결함 디버깅에 도움이되는 여러 가지 도구가 있으며, 내가 좋아하는 도구를 목록에 추가하고 싶습니다 : Address Sanitizers (대개 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은 단순한 세그먼트 오류보다 더 많은 것을 포착합니다. 해당 메모리 영역이 프로세스에 액세스 가능하더라도 많은 범위를 벗어난 액세스가 포착됩니다.
코어 덤프에 대한 Lucas의 답변이 좋습니다. 내 .cshrc에는 다음이 있습니다.
alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'
'core'를 입력하여 역 추적을 표시합니다. 그리고 날짜 스탬프는 올바른 파일을보고 있는지 확인합니다.
추가 : 스택 손상 버그 가 있으면 코어 덤프에 적용된 역 추적은 종종 쓰레기입니다. 이 경우 gdb 내에서 프로그램을 실행하면 허용되는 답변에 따라 더 나은 결과를 얻을 수 있습니다 (오류가 쉽게 재현 가능하다고 가정). 또한 코어를 동시에 덤프하는 여러 프로세스에주의하십시오. 일부 OS는 PID를 코어 파일 이름에 추가합니다.
ulimit -c unlimited
우선 코어 덤프를 활성화 하는 것을 잊지 마십시오 .
위의 모든 답변은 정확하고 권장됩니다. 이 답변은 위에서 언급 한 접근 방식을 사용할 수없는 경우 최후의 수단으로 만 사용됩니다.
다른 모든 방법이 실패하면 fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);
코드의 관련 부분이라고 생각되는 부분에 다양한 임시 디버그 인쇄 문 (예 :)을 사용하여 프로그램을 항상 다시 컴파일 할 수 있습니다 . 그런 다음 프로그램을 실행하고 충돌이 발생하기 직전에 마지막으로 인쇄 된 디버그 인쇄가 무엇인지 관찰하십시오. 프로그램이 그렇게 멀어지면 그 시점 이후에 충돌이 발생했을 것입니다. 한 줄의 코드로 좁힐 때까지 디버그 인쇄를 추가 또는 제거하고 다시 컴파일 한 후 테스트를 다시 실행하십시오. 이 시점에서 버그를 수정하고 모든 임시 디버그 인쇄를 제거 할 수 있습니다.
매우 지루하지만 어느 곳에서나 작업 할 수 있다는 이점이 있습니다. 어떤 이유로 stdout 또는 stderr에 액세스 할 수 없거나 수정하려는 버그가 레이스 인 경우가 아닐 수도 있습니다 -프로그램의 타이밍이 변경 될 때 동작이 변경되는 조건 (디버그 인쇄가 프로그램을 느리게하고 타이밍을 변경하기 때문에)