다음 이전 차례

5. 디버깅과 Profiling

5.1 예방적인 관리(lint)

문제가 발생하고 나서 해결하는 것보다는 문제를 미연에 방지하는 것이 중요하지 않을까? 리눅스에 널리 쓰이는 lint는 없다. 아마도 대부분의 사람들이 gcc가 내놓는 자세한 경고 메세지에 만족하고 있기 때문인 것 같다. 아마도 가장 유용하쓰이는 것은 -Wall 스위치일 것이다. 이것이 의미하는 바는 "Warnings, all"로서 모든 경고 메세지를 발생시키라는 말이다. 또한 아주 자세하게 나온다.

Public Domain lint는 ftp://larch.lcs.mit.edu/pub/Larch/lclint에서 얻을 수 있다. 하지만 얼마나 괜찮은지 본인은 모른다.

5.2 디버깅

어떻게 하면 프로그램의 디버깅 정보를 알아낼 수 있는가?

그러기 위해서는 -g 옵션을 주고 컴파일/링크해야 한다. 그리고 -fomit-frame-pointer 스위치는 빼주어야 한다. 사실 모든 부분을 다시 컴파일할 필요는 없고, 여러분이 관심 갖고 있는 부분만을 그렇게 해주면 된다.

a.out에 있어서 공유라이브러리가 만약 -fomit-frame-pointer 스위치를 가지고 컴파일되었다면 gdb를 사용할 수 없을 것이다. -g 옵션을 주는 이유는 바로 정적 링크를 행하라는 말을 함축하게 된다.

만약 링커가 libg.a를 찾을 수 없다고 하면서 실패하게 된다면, 여러분이 /usr/lib/libg.a을 갖고 있지 않기 때문일 것이다. 그 화일은 특별한 라이브러리로서 디버깅 가능 C 라이브러리이다. libc 패키지에 포함되어 있거나 또는 libc 소스 코드를 받아서 컴파일하면 생긴다. 실제로 그렇게 필요한 것은 아니고 대충 /usr/lib/libc.a/usr/lib/libg.a로 링크시켜버려도 대부분 상관없을 것이다.

디버깅 정보를 어떻게 하면 다시 꺼낼 수 있는가?

아주 많은 GNU 소프트웨어들은 -g 옵션을 가지고 컴파일되어 있으므로 화일 크기가 매우 크다. (종종 정적 링크되어 있음) 그렇게 괜찮은 생각인 것 같지는 않다.

만약 프로그램이 autoconf에 의해 만들어진 configure를 가지고 있다면, 보통의 경우 Makefile을 건드림으로써 디버깅 정보를 넣지 않게 할 수 있다. 물론 ELF를 사용하고 있다면, 프로그램은 -g 세팅과는 상관없이 동적 링크되며, 그냥 쉽게 strip(디버깅 정보를 실행화일에서 빼버리는 행위)시킬 수 있다.

관련 소프트웨어

대부분의 사람들은 gdb를 사용하고 있다. gdb는 GNU archive sites에서 소스의 형태로, 아니면 tsx-11이나 선사이트에서 바이너리의 형태로 구할 수 있다. xxgdb는 gdb에 기초한 X 윈도우 디버거이다. 즉, 우선적으로 gdb를 이미 설치했어야 한다는 뜻이다. 그 소스는 ftp://ftp.x.org/contrib/xxgdb-1.08.tar.gz에서 찾을 수 있다.

또한 UPS 디버거가 Rick Sladkey씨에 의해 포팅되었다. X 윈도우에서도 잘 돌아간다. 하지만 xxgdb와 같이 텍스트 디버거인 gdb같은 것에 의존하는 형태는 아니다. 아주 훌륭한 기능들을 많이 가지고 있다. 따라서 여러분이 디버깅에 많은 시간을 할애하고 있다면, 우선적으로 UPS 디버거를 권한다. 리눅스용으로 컴파일된 바이너리나 소스 패치화일은 ftp://sunsite.unc.edu/pub/Linux/devel/debuggers/에서 구할 수 있고 오리지널 소스는 ftp://ftp.x.org/contrib/ups-2.45.2.tar.Z에서 찾을 수 있다.

디버깅에 쓰이는 또 다른 툴 하나를 들자면 strace를 들 수 있다. strace는 프로그램이 만들어내는 시스템 호출을 화면에 표시해준다. 이것 말고도 다방면으로 사용가능한데, 예를 들어 어떠한 패스명이 소스코드를 갖고 있지 않은 바이너리 화일 안에 컴파일되어 들어가있는지, 분명히 바이너리 안에 들어있는 조건들을 발견하고자 할 때, 일반적으로 일반적으로 어떻게 작동하고 있는지를 알아내고자 할 때 사용한다. 최신 strace 버전(현재 3.0.8)은 ftp://ftp.std.com/pub/jrs/에서 구할 수 있다.

백그라운드 (데몬) 프로그램

데몬 프로그램들은 전형적으로 fork()를 먼저 하고 나서, 부모 프로세스를 종료시켜 버린다. 이는 디버깅 세션에 대하여 공격적인 요소임이 분명하다.

이럴 때 가장 간단한 방법은 fork에 대하여 정지점(breakpoint)을 지정해주는 것이고 프로그램이 멈추면 다시금 그것을 0 으로 만들어주는 것이다.

(gdb) list 
1       #include <stdio.h>
2
3       main()
4       {
5         if(fork()==0) printf("child\n");
6         else printf("parent\n");
7       }
(gdb) break fork
Breakpoint 1 at 0x80003b8
(gdb) run
Starting program: /home/dan/src/hello/./fork 
Breakpoint 1 at 0x400177c4

Breakpoint 1, 0x400177c4 in fork ()
(gdb) return 0
Make selected stack frame return now? (y or n) y
#0  0x80004a8 in main ()
    at fork.c:5
5         if(fork()==0) printf("child\n");
(gdb) next
Single stepping until exit from function fork, 
which has no line number information.
child
7       }

코어 화일(Core file)

보통 리눅스 부팅시에 코어 화일을 만들지 않도록 세팅되어 있다. 하지만 코어화일 생성을 가능케 하려고 한다면 그것을 다시 가능케 하는 셸의 내장 명령을 사용한다.

셸 호환 셸(예. tcsh)을 쓰고 있다면 다음과 같이 명령을 내린다.

% limit core unlimited

만약 본셸류(sh, bash, zsh, pdksh)를 사용하고 있다면,

$ ulimit -c unlimited

만약 코어 화일의 이름에 대하여 융통성을 가지고 싶다면, 커널 소스를 약간만 변경해주면 된다. 자, fs/binfmt_aout.cfs/binfmt_elf.c와 같은 화일을 찾아보자.

        memcpy(corefile,"core.",5);
#if 0
        memcpy(corefile+5,current->comm,sizeof(current->comm));
#else
        corefile[4] = '\0';
#endif

grep 같은 것을 가지고 이런 부분을 모두 찾은 후에 0이라고 되어 있는 것을 1이라고 모두 고쳐준다.

5.3 Profiling

Profiling이라고 하는 것은 프로그램의 어떤 부분이 제일 자주 호출되고 있는지 또는 많은 시간을 소요하고 있는지를 조사하는 것이다. 코드를 최적화시키고 시간이 가장 많이 소비되는 곳을 고쳐주는 좋은 방법이다. 이렇게 하기 위해서는 -p 옵션을 주어서 시간 정보를 오브젝트 화일들이 가질 수 있도록 다시 컴파일해주어야 한다. 또한 binutil 패키지에 있는 gprof 라는 것을 필요로 한다. 자세한 사항은 gprof 맨페이지를 참고하기 바란다.


다음 이전 차례