GNU 에서 만든 GDB 디버거의 사용방법에 대해서 설명하고 있다.
간단한 예제를 통해, 사용방법을 익히도록 하겠다.
설치하기
참고로 여기서 설명하고 있는 환경은 redhat9 이다. 설치시에 개발 패키지를 선택했다면, 자동으로 gdb 가 설치되어 있을 것이다.
버전은 5.3 이다. 물론 이것을 그대로 사용해도 거의 문제가 없다. 하지만 뒤에서 설명할 '멀티프로세스 프로그램 디버깅' 에서는 최소 6.2 버전 이상이 되어야 원활한 디버깅을 할 수 있다.
그래서 여기서는 간단하게 나마, gdb 를 컴파일하고 설치하는 방법에 대해 다루도록 하겠다.
#tar xzf gdb-6.2.tar.gz #cd gdb-6.2 #./configure --prefix=/usr/local/program/gdb #make #make install
에러없이 수행이 되었다면, /usr/local/program/gdb/bin 아래에 실행파일이 복사되었을 것이다.
준비운동 하기
다음의 소스를 test.c 라는 파일로 저장한다.
#include <stdio.h> struct time { int hour; int min; int sec; } gtime = {1,2,3}, gtimes[4]; char *array[4] = {"one","two","three"}; int hello() { int i = 10; while(i--); return 4; } int gethour(short sw) { if (sw) return 2; else return hello(); } int main() { int lval = 2331, i; char *lstr = "I like you."; struct time *pt = >ime; for(i=0; i<100; i++){ printf("array[i] = %s\n", array[i]); gtime.hour += gethour(i % 2); } return 0; }
이제 다음과 같이 컴파일을 한다.
#gcc -g test.c /*-g 옵션을 주는 것은 심볼정보를 오브젝트 파일에 넣기 위함이다. -g 옵션이 없다면, 역 어셈브링해서 디버깅을 해야 한다*/
위의 프로그램은 컴파일은 정상적으로 되지만, 실행을 시켜보면, 'Segmentation fault' 가 발생한다.
이때가 바로 GDB 가 필요할 때다. 여기서는 이미 만들어진 ELF 형태의 오브젝트 파일을 이용해서 디버깅을 하지만, core 파일이나, 현재 실행중인 프로세스의 PID 를 가지고도 디버깅을 할 수 있다.
#gdb a.out GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) Copyright 2003 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux-gnu"... (gdb)
GDB 를 실행시키면, 위와 같은 화면이 나타난다. GDB 프롬프트에서는 명령어 자동완성 기능과 히스토리 기능을 제공하기 때문에, 손쉽게 사용할 수 있다.
GDB 사용법
GDB 를 실행파일과 함께 실행시켰다면, 이제 본격적으로 사용 명령어를 통해 디버깅을 시작해야 한다.
소스보기
명령어 | 사용예 | 설명 |
l(list) | l main | main 함수를 찾아 출력 |
l 10 | 10 행을 기준으로 출력 | |
l - | 출력된 행의 이전 행을 출력 | |
l bug.c:main | bug.c 파일의 main 함수를 출력 | |
l bug.c:10 | bug.c 파일의 10 행을 기준으로 출력 |
한번에 출력되는 행의 수를 20 으로 변경하려면,
(gdb) set listsize 20
입력하면 된다.
BreakPoint
명령어 | 사용예 | 설명 |
b(break) | b func | func 함수의 시작부분에 breakpoint 설정 |
b 10 | 10 행에 breakpoint 설정 | |
b +2 | 현재 행에서 2개 행 이후 지점에 breakpoint 설정 | |
b *0x8049000 | 0x8049000 주소에 breakpoint 설정(어셈블리어로 디버깅시 사용) | |
b 10 if var == 0 | 10 행에 브레이크 포인트를 설정하는데 var 변수 값이 0 일때 작동 |
breakpoint 는 여러개를 지정해 줄 수 있고, 현재 잡은 breakpoint 의 정보를 보고 싶다면,
(gdb) info breakpoints
입력하면 된다. 잡은 breakpoint 를 삭제하는 명령어는 cl(clear) 이다.
명령어 | 사용예 | 설명 |
cl(clear) | cl func | func 함수의 시작 부분에 breakpoint 지움 |
cl 10 | 10 행의 breakpoint 지움 | |
cl bug.c:28 | bug.c 파일의 28 행의 breakpoint 지움 | |
d | 모든 breakpoint 지움 |
마지막으로 현재 설정한 breakpoint 를 임시로 비활성화 시키려면,
(gdb) disable 5
다시 활성화 시키려면,
(gdb) enable 5
한다. 여기서 5 는 breakpoint number 이다. 'info breakpoints' 를 통해 활성화 여부를 볼 수 있다.
프로그램 수행
명령어 | 사용예 | 설명 |
r | r | 프로그램 수행 |
r arg1 arg2 | arg1 과 arg2 를 인자로 프로그램 수행 | |
k | k | 프로그램 수행 종료 |
s | s | 현재 행 수행 후 정지, 함수 호출 시 함수 내부로 들어감 |
s 5 | s를 5번 입력한 것과 동일 | |
n | n | 현재 행 수행 후 정지, 함수 호출 시 함수 수행 후 다음 행으로 감 |
n 5 | n을 5번 입력한 것과 동일 | |
c | c | breakpoint 를 만날 때까지 계속 진행 |
u | u | 현재 루프를 빠져나감 |
finish | finish | 현재 함수를 수행하고 빠져 나감 |
return | return | 현재 함수를 수행하지 않고 빠져 나감 |
return 123 | 리턴값은 123 | |
si | si | 현재의 인스트럭션을 수행, 함수 호출 시 함수 내부로 들어감 |
ni | ni | 현재의 인스트럭션을 수행, 함수 호출 시 함수 내부로 들어가지 않음 |
Watchpoint
특정한 변수값을 보고 싶을 때, watchpoint 를 사용할 수 있다.
(gdb) watch [변수명]
지정한 변수값이 바뀔 때마다, 화면에 표시해준다.
변수 출력
명령어 | 설명 |
info locals | 현재 지역변수들의 값을 출력 |
info variables | 전역변수들의 리스트를 출력 |
p [변수명] | 개별 변수의 값을 출력 |
p [함수명] | 함수의 주소값을 출력 |
p [함수명]::[변수명] | 함수에 있는 변수 값을 출력 |
display [변수명] | 변수 값을 매번 화면에 디스플레이함 |
undisplay [디스플레이 번호] | 디스플레이 설정을 없앰 |
printf 명령어를 사용해서 좀 더 다양한 포맷으로 출력할 수 있다.
(gdb)printf "l is %x\n", sdata.block_state l is fffffffe
변수 sdata.block_state 의 값을 16 진수 형태로 출력한다.
스택 프레임 출력
명령어 | 설명 |
info f 또는 info frame | 현재 스택 프레임 정보 출력 |
bt | 현재 전체 스택 프레임 출력 |
frame [N] | n번 스택 프레임으로 변경 |
up | 상위 스택 프레임으로 이동 |
down | 하위 스택 프레임으로 이동 |
info args | 함수가 호출될 때 인자를 출력 |
info catch | 함수의 예외 핸들러를 출력 |
특정 메모리 영역 입출력
명령어 | 사용예 | 설명 |
x/(범위)(출력형식)(범위의 단위) | x/10i main | main 함수의 시작부터 인스트럭션을 어셈블리로 변환해서 보임 |
set {타입}(주소) = (값) | set {int}0x8048300 = 100 | 0x8048300 번지에 int 타입으로 100 입력 |
함수별 모듈 테스트
명령어 | 사용예 | 설명 |
call (함수명)(인자) | call func(100) | func 함수에 100 인자를 넣어서 실행 |
signal (info signals) | signal SIGKILL | 현재 디버깅 대상의 프로세스에 SIGKILL 시그널을 보냄 |
인자를 추가하여 디버깅 하기
디버깅할 대상 프로그램이 인자값을 필요로 하는 경우가 있다. 이럴 때는 gdb 를 먼저 실행하고, 아래와 같이 실행한다.
(gdb) set args <인자들> (gdb) run