디버깅에 대해서 #1 : http://forum.falinux.com/zbxe/index.php?document_srl=865340


이번 글 부터는 간단한 예제를 통해서 여러가지 디버깅 기법을 살펴보도록 하겠습니다.


예제에 사용되는 소스는 간단하게 만들어서 첨부로 포함했습니다. 코드를 살펴보면 아래와 같습니다.


#include <stdio.h>

void raise_seg(char * ptr, int offset) {
	ptr[0] = 1;
}

void init(char * str) {
    printf("%s\n", str);
    raise_seg(0, 100);
}

int main() {
	init("tests");
	return 0;
}


이 코드를 컴파일 해서 실행하면 당연하겠지만 아래와 같이 에러가 발생합니다.


$ ./seg
tests
Segmentation fault (core dumped)


제가 첫번째 썼던 글에 따라서 분석을 시작해보면,


우선 재현 경로는 간단합니다. 실행하면 문제가 발생합니다.

재현 빈도는? 항상 발생합니다. 100% 입니다.

그룹 구분은 일단 이번 예제에서는 안되는 소스는 존재하지는 않습니다. 무조건 안됩니다.


정리하면 아래와 같습니다.


재현경로 : 실행하면 발생
재현빈도 : 100%
대조그룹 : 없음




우선 디버깅의 아주 기초가 되는 거의 모든 환경에서 사용이 가능한 printf를 활용하는 방법이 있습니다.

printf를 사용할 때는 가장 중요한 것이 빌드환경과 높은 재현빈도의 경로입니다. 


왜냐하면 이 방법을 사용하게 되면 빌드 및 테스트를 많이 해야하기때문입니다.



지금 예제처럼 매우 간단한 소스는 몇번 하지 않아도 printf를 군데군데 넣어서 확인할 수 있겠지만 거대한 규모의 프로젝트의 경우 주요부분에 printf를 넣고 범위를 점차 좁혀가는 식으로 작업을 해야합니다.


예제 소스를 아래와 같이 수정해보겠습니다.


#include <stdio.h>

void raise_seg(char * ptr, int offset) {
    printf("entering [%s]--\n", __func__);
	ptr[0] = 1;
    printf("leaving [%s]--\n", __func__);
}

void init(char * str) {
    printf("entering [%s]--\n", __func__);
    printf("%s\n", str);
    raise_seg(0, 100);
    printf("leaving [%s]--\n", __func__);
}

int main() {
	init("tests");
	return 0;
}


컴파일하고 실행해보면 결과가 아래와 같습니다.


$ ./seg
entering [init]--
tests
entering [raise_seg]--
Segmentation fault (core dumped)


화면에 출력된 것을 분석해보면 init함수에 들어가고, raise_seg 함수에 들어갔으나 나오지 못하고 오류가 발생했습니다.


즉, raise_seg 함수안에 버그가 있는 것이죠.


이제 소스를 아래와 같이 수정합니다.


#include <stdio.h>

void raise_seg(char * ptr, int offset) {
    printf("entering [%s]--\n", __func__);
    printf("ptr: %p, offset: %d\n", ptr, offset);
	ptr[0] = 1;
    printf("leaving [%s]--\n", __func__);
}

void init(char * str) {
    printf("entering [%s]--\n", __func__);
    printf("%s\n", str);
    raise_seg(0, 100);
    printf("leaving [%s]--\n", __func__);
}

int main() {
	init("tests");
	return 0;
}


결과는 아래와 같습니다.


$ ./seg
entering [init]--
tests
entering [raise_seg]--
ptr: (nil), offset: 100
Segmentation fault (core dumped)


아~! 이제야 범인을 찾았습니다. ptr이 null입니다. 즉, null인 pointer에 값을 쓰려고 해서 오류가 발생했습니다.




printf를 활용해서 이런식으로 범위를 좁혀가고 값들을 출력해가면서 계속 빌드와 테스트를 반복해서 버그에 접근하여 수정하는 방법을 살펴봤습니다.


이런 방식으로 디버깅을 할 때 가장 주의해야할 점은 멀티쓰레드로 동작하거나, 이벤트 방식의 프로그램에서는 예상과 다른 결과를 얻을 수 있습니다.  예를들어 위의 경우는 단일 쓰레드라서 당연하다시피 raise_seg에서 빠져나오지 못한채로 오류가 발생한걸로 분석했습니다만, 멀티쓰레드라면 다른 쓰레드에서 오류가 발생했을 수도 있습니다.


다음 글에서는 gdb를 활용하는 방법에 대해서 알아보겠습니다.