gdb 를 활용한 segmentation fault 분석

Published:

gdb backtrace 의 필요성

실행 이미지에서 segmentation fault 가 날 경우, 어느 라인에서 에러가 생겼는지 추적하기가 쉽지 않다. 의심가는 라인 전, 후로 print를 하는 방법이 있겠지만 프로그램 전반적으로 호출되는 함수에서는 print를 너무 많이해야해서 비효율적이다. 이 때, gdb bt 를 사용할 경우, 추적이 수월하다!

gdb란?

GNU 에서 나온 디버거 프로그램

예제

예제로 두 인자를 받는 함수를 이용한다

#include <fstream>
#include <iostream>
#include <string>
#include <vector>
void func4(int a, int b)
{ 
	if (b>1000)
	{
		std::vector<double> a;
		printf("%lf", a[101]);
	}

	printf("sum : %d\n", a + b);
}
void func3(int a, int b) { return func4(a, b); }
void func2(int a, int b) { return func3(a, b); }
void func1(int a, int b) { return func2(a, b); }

int main(int argc, char *argv[])
{
    int a = atoi(argv[1]);
    int b = atoi(argv[2]);
 
    func1(a, b);
 
    return 0;
}


gdb_exercise 라는 실행 파일 이름을 가지도록 CMakeList 를 구성한뒤에 정상 실행하면 아래와 같다.

project/build$ ./gdb_exercise 3 5 
sum : 8

하지만 만약 두번째 인자가 1000 보다 클 때는 segmentation fault 가 날것이다. ( vector a 에는 101 번째 인자가 없기 때문에)

project/build$ ./gdb_exercise 3 3000
Segmentation fault (core dumped)

위와 같은 상황에서 gdb를 이용하여 Debugging 을 해본다.

먼저 argument가 있는 상황에서 gdb 실행은 아래 명령어와 같다.

gdb --args executablename arg1 arg2 arg3

위의 예제에 이를 적용하면 아래와 같이 적용할 수 있다

gdb --args ./gdb_exercise 3 3000

여기까지 했으면 gdb 실행 환경에 집입하게 된다 여기서 실행을 위해 run 명령어를 치면 마찬가지로 Segmentation fault 가 날 것이다.

(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x000000000040093a in func4(int, int) ()

보면 func4 에서 에러가 났음을 알수 있다.

함수 호출 순서를 보고 싶으면 bt 명령어를 치면 된다. bt 는 back trace 의 줄임말이다

(gdb) bt
#0  0x000000000040093a in func4(int, int) ()
#1  0x00000000004009cf in func3(int, int) ()
#2  0x00000000004009ee in func2(int, int) ()
#3  0x0000000000400a0d in func1(int, int) ()
#4  0x0000000000400a59 in main ()

이를 보면 스택에 쌓인 순서대로 함수의 호출 과정을 볼 수 있다. 따라서 이를 보면, main 에서 func1 -> func2 -> func3 -> func4 순서대로 불린 func4 에서 Segmentation fault 가 일어났음을 알 수 있다. (bt full 을 이용할 경우, Local 변수도 볼 수 있다)

정리

gdb --args executablename arg1 arg2 arg3

bt full