세그먼테이션 오류를 일으키는 코드 줄을 결정 하시겠습니까?


151

세그먼테이션 오류 를 일으키는 코드에서 실수가 어디에 있는지 어떻게 알 수 있습니까?

컴파일러 ( gcc)가 프로그램에서 오류 위치를 표시 할 수 있습니까 ?


5
gcc / gdb는 할 수 없습니다. segfault가 발생한 위치 를 찾을 수 있지만 실제 오류는 완전히 다른 위치에있을 수 있습니다.

답변:


218

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가 발생하는 위치는 일반적으로 코드에서 "발생하는 실수"의 위치에 대한 단서 일뿐입니다. 주어진 위치가 반드시 문제가있는 곳은 아닙니다.


28
segfault가 발생하는 위치는 일반적으로 코드에서 "발생하는 실수"의 위치에 대한 단서 일뿐입니다. 중요한 단서이지만 반드시 문제가있는 곳은 아닙니다.
mpez0

9
(bt full)을 사용하여 자세한 내용을 얻을 수도 있습니다.
ant2009


2
bt의 속기로 사용하십시오 backtrace.
rustyx

43

또한 valgrind시도해 볼 수 있습니다 : 설치 valgrind하고 실행하면

valgrind --leak-check=full <program>

그런 다음 프로그램을 실행하고 유효하지 않은 메모리 읽기 또는 쓰기 및 메모리 누수뿐만 아니라 모든 segfault에 대한 스택 추적을 표시합니다. 정말 유용합니다.


2
+1, Valgrind는 메모리 오류를 발견하는 데 훨씬 더 빠르고 사용하기 쉽습니다. 디버깅 기호가있는 최적화되지 않은 빌드 에서는 segfault가 발생한 위치와 이유 를 정확하게 알려줍니다 .
Tim Post

1
슬프게도 -g -O0으로 컴파일하고 valgrind와 결합하면 내 segfault가 사라집니다.
JohnMudd

2
--leak-check=fullsegfaults를 디버깅하는 데 도움이되지 않습니다. 메모리 누수 디버깅에만 유용합니다.
ks1322

@JohnMudd 나는 segfault가 테스트 된 입력 파일의 약 1 % 만 나타납니다. 실패한 입력을 반복해도 실패하지 않습니다. 내 문제는 멀티 스레딩으로 인해 발생했습니다. 지금 까지이 문제를 일으키는 코드 줄을 찾지 못했습니다. 지금은이 문제를 해결하기 위해 재 시도를 사용하고 있습니다. -g 옵션을 사용하면 오류가 사라집니다!
Kemin Zhou

18

코어 덤프를 사용한 다음 gdb로 검사 할 수도 있습니다. 유용한 정보를 얻으려면 -g플래그 로 컴파일해야합니다 .

메시지가 나타날 때마다 :

 Segmentation fault (core dumped)

코어 파일이 현재 디렉토리에 작성됩니다. 그리고 당신은 명령으로 그것을 확인할 수 있습니다

 gdb your_program core_file

파일에는 프로그램 충돌시 메모리 상태가 포함됩니다. 코어 덤프는 소프트웨어 배포 중에 유용 할 수 있습니다.

시스템이 코어 덤프 파일 크기를 0으로 설정하지 않았는지 확인하십시오. 다음을 사용하여 무제한으로 설정할 수 있습니다.

ulimit -c unlimited

조심해! 코어 덤프가 커질 수 있습니다.


최근에 아치 리눅스로 전환했습니다. 현재 디렉토리에 코어 덤프 파일이 없습니다. 어떻게 생성 할 수 있습니까?
Abhinav

당신은 그것을 생성하지 않습니다; 리눅스는 그렇습니다. 코어 덤프는 다른 Linuces-Google의 다른 위치에 저장됩니다. Arch Linux의 경우이 wiki.archlinux.org/index.php/Core_dump를
Mawg는 Monica를 복원하는 Monica

7

세그멘테이션 결함 디버깅에 도움이되는 여러 가지 도구가 있으며, 내가 좋아하는 도구를 목록에 추가하고 싶습니다 : 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은 단순한 세그먼트 오류보다 더 많은 것을 포착합니다. 해당 메모리 영역이 프로세스에 액세스 가능하더라도 많은 범위를 벗어난 액세스가 포착됩니다.


¹ Clang 3.1+GCC 4.8+ 입니다.


이것은 나에게 가장 도움이됩니다. 빈도가 약 1 % 인 무작위로 발생하는 매우 미묘한 버그가 있습니다. 나는 (16 개의 주요 단계; 각기 다른 C 또는 C ++ 이진에 의해 수행되는) 많은 입력 파일을 처리합니다. 이후 단계는 멀티 스레딩으로 인해 세그먼트 오류를 ​​무작위로만 트리거합니다. 디버깅하기가 어렵습니다. 이 옵션은 최소한 디버그 정보 출력을 트리거하여 버그의 위치를 ​​찾기위한 코드 검토의 시작점을 제공했습니다.
Kemin Zhou

2

코어 덤프에 대한 Lucas의 답변이 좋습니다. 내 .cshrc에는 다음이 있습니다.

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

'core'를 입력하여 역 추적을 표시합니다. 그리고 날짜 스탬프는 올바른 파일을보고 있는지 확인합니다.

추가 : 스택 손상 버그 가 있으면 코어 덤프에 적용된 역 추적은 종종 쓰레기입니다. 이 경우 gdb 내에서 프로그램을 실행하면 허용되는 답변에 따라 더 나은 결과를 얻을 수 있습니다 (오류가 쉽게 재현 가능하다고 가정). 또한 코어를 동시에 덤프하는 여러 프로세스에주의하십시오. 일부 OS는 PID를 코어 파일 이름에 추가합니다.


4
ulimit -c unlimited우선 코어 덤프를 활성화 하는 것을 잊지 마십시오 .
James Morris

@ 제임스 : 맞습니다. 루카스는 이미 이것을 언급했다. 그리고 여전히 csh에 갇혀있는 사람들에게는 'limit'를 사용하십시오. 그리고 CYGWIN 스택 덤프를 읽을 수 없었습니다 (그러나 2 ~ 3 년 동안 시도하지 않았습니다).
Joseph Quinsey

2

위의 모든 답변은 정확하고 권장됩니다. 이 답변은 위에서 언급 한 접근 방식을 사용할 수없는 경우 최후의 수단으로 만 사용됩니다.

다른 모든 방법이 실패하면 fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);코드의 관련 부분이라고 생각되는 부분에 다양한 임시 디버그 인쇄 문 (예 :)을 사용하여 프로그램을 항상 다시 컴파일 할 수 있습니다 . 그런 다음 프로그램을 실행하고 충돌이 발생하기 직전에 마지막으로 인쇄 된 디버그 인쇄가 무엇인지 관찰하십시오. 프로그램이 그렇게 멀어지면 그 시점 이후에 충돌이 발생했을 것입니다. 한 줄의 코드로 좁힐 때까지 디버그 인쇄를 추가 또는 제거하고 다시 컴파일 한 후 테스트를 다시 실행하십시오. 이 시점에서 버그를 수정하고 모든 임시 디버그 인쇄를 제거 할 수 있습니다.

매우 지루하지만 어느 곳에서나 작업 할 수 있다는 이점이 있습니다. 어떤 이유로 stdout 또는 stderr에 액세스 할 수 없거나 수정하려는 버그가 레이스 인 경우가 아닐 수도 있습니다 -프로그램의 타이밍이 변경 될 때 동작이 변경되는 조건 (디버그 인쇄가 프로그램을 느리게하고 타이밍을 변경하기 때문에)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.