출처 : http://blog.naver.com/endfirst?Redirect=Log&logNo=20007573126
디버거란 프로그램 개발 도구로써, 프로그램을 개발하다가 에러가
발생하면 발생 위치 및 발생이유를 쉽게 찾을 수 있도록 도와 준다.
[gdb] 명령 요약
프로그램 실행과 트레이스(trace)에 관련된 명령들
---------------------------------------------------------
run 현재의 인수를 사용하여 프로그램을 실행
run <args> 새로운 <인수>를 가지고 프로그램을 실행
continue 현재 위치에서 프로그램을 계속 실행시킨다.
(약자) c, cont, (dbx)return
next 한 줄씩 실행 시킨다. 이 때 함수를 포함하고 있으면
함수를 수행시킨다. (약자) n
next <n> <n>줄을 실행시킨다.
step 한 줄씩 실행 시킨다. 이 때 함수를 포함하고 있으면
함수 내부로 들어가서 한 줄씩 실행한다. (약자) s
step <n> <n>줄을 실행시킨다. " "
break <line number> 라인 번호에서 프로그램 실행을 멈추게 한다.
(dbx) stop at <line number> (약자) b
break <함수 명> 함수 내부의 첫번째 라인에서 프로그램의 실행을
멈추게한다.
(dbx) stop in <함수명>
quit gdb를 종료 시킨다.
------------------------------------------------------------
데이타에 관련된 명령들
-----------------------------------------------------------
whatis <name> 지정한 <변수>에 관련된 정보를 봇㈐娩?
print <eval> <eval>에 지정된 식의 값을 보여준다.
(약자) p
display 현재 지정된 display 명령의 목록을 보여준다.
(dbx) history
list 현재 위치에서 소스 파일의 내용을 10줄 보여준다.
list <first>,<last> <시작줄>과 <끝줄>사이의 소스파일 내용을 보여준다.
-----------------------------------------------------------
gdb 사용법을 알기 위해서 우선 bug가 있는 프로그램을 작성하자.
~#vi test.c
---------------< test.c 내용>--------------
1 #include <stdio.h>
2
3 main()
4 {
5 int i;
6double j;
7 char *bug = NULL;
8
/* 다음은 i/2 + i 의 값을 출력 시키는 문이다.
i 가 1 이면, j 는 1.5 가 되도록 짠 것이다.
그러나 실제로 그렇지 않다. */
9 for( i = 0; i < 5; i++) {
10 j = i/2 + i;
97
10 j = i/2 + i;
11 printf(" j is %lf \n", j );
12 }
/* 다음은 bug 변수에 hi를 copy하려는 것이다.
변수명 bug에서 느끼겠지만, 일부려 bug를 만들었다.
무엇일까 ? */
13 strcpy(bug,"hi");
14 printf("bug is %s \n", bug);
15
16 return;
17 }
---------------------------------------------
위의 내용을 저장하고 나서,
yoo:~# cc -g test.c -o test
<설명>------------------
위 명령어에서 cc 는 c language를 compile 한 후 실행화일 형성할 때
사용하는 명령이며, gnu c에서는 gcc로 대치될 수 있다.
-g option 은 형성된 실행화일을 가지고 debug될 수 있게 compile 해
달라는 일종의 부탁하는 option이다.
-o option은 -o 뒤의 화일 이름을 가진 실행화일을 만들어 달라라는 것이다.
위의 test.c를 compile하면 error 메세지는 없다.
------------------------------------------------------------
yoo:~# test
yoo:~# mv test a.out
yoo:~# a.out
j is 0.000000
j is 1.000000
j is 3.000000
j is 4.000000
j is 6.000000
Segmentation fault (core dumped)
yoo:~# rm core
<설명>-----------------------------------------------------
test 실행화일을 실행시켰더니 아무런 반응이 없어서 깜짝놀랐다.
필자는 보통 SGI에서 프로그램을 짜서, 이런 현상은 처음이다.
아뭏든, a.out로 바꾸고 보니 core가 형성되었다. 그리고, j 값이 이상하다.
-----------------------------------------------------------
yoo:~# gdb a.out
GDB is free software and you are welcome to 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.
GDB 4.15.1 (i486-slackware-linux),
Copyright 1995 Free Software Foundation, Inc...
(gdb)
--<설명>------------------------------------------------------
이제부터 test.c 를 디버그(debug)한다. 프로그램 짜는 것보다 훨씬
재미있다. (아실랑가 모르겠네....)
방금전 실행화일 (test)을 a.out로 바꾼사실을 기억하죠.
그래서 gdb (실행화일) 즉, gdb a.out로 치면 된다.
BSD 계열에선 dbx (실행화일) 즉, dbx a.out로 치면된다.
-----------------------------------------------------------------
(gdb) list
1 #include <stdio.h>
2
3 main()
4 {
5 int i;
6 double j;
7 char *bug = NULL;
8
9 for( i = 0; i < 5; i++) {
10 j = i/2 + i;
--< 설명 > ----------------------------------------------------
list는 소스 내용을 보여줍니다.
--------------------------------------------------------------
(gdb) list 4, 13
4 {
5 int i;
6 double j;
7 char *bug = NULL;
8
9
or( i = 0; i < 5; i++) {
10 j = i/2 + i;
11 printf(" j is %lf \n", j );
12 }
13
--<설명> --------------------------------------------------------
list < 첫번째 줄>, < 끝줄> 치면 위처럼 보입니다.
---------------------------------------------------------------
(gdb) break 9
Breakpoint 1 at 0x80484b1: file test.c, line 9.
(gdb) run
Starting program: /root/a.out
Breakpoint 1, main () at test.c:9
9 for( i = 0; i < 5; i++) {
(gdb)
--<설명>-----------------------------------------------------
debug하려면 우선 bug가 있을만한 라인 앞을 break point로 잡습니다.
저는 for 문에 이상이 있다고 판단하고
9 for( i = 0; i < 5; i++) {
for문의 line number 가 9 이므로, break 9로 했읍니다.
다른 예, 함수 내부에 breakpoint를 잡는 법은 나중에 ...
그리구, run 했읍니다.
---------------------------------------------------------------
9 for( i = 0; i < 5; i++) {
(gdb) step
10 j = i/2 + i;
(gdb) step
11 printf(" j is %lf \n", j );
(gdb) step
j is 0.000000
9for( i = 0; i < 5; i++) {
(gdb) step
10 j = i/2 + i;
(gdb) p i
$1 = 1
(gdb) p j
$2 = 0
(gdb) s
11 printf(" j is %lf \n", j );
(gdb) p j
$3 = 1
(gdb)
----<설명>----------------------------------------
위의 경우는 step 의 설명입니다. s로 쳐도 됩니다.
step은 한줄씩 실행 시킵니다.
i = 1 일 때 j = 1 입니다. 예상 밖이죠.
즉 10 line에서 잘못된 것입니다.
무엇이 잘못된 것인지는 여러분이 깨달으시고,
j = (double)i/2. + i;하면 제대로 될 것입니다.
--------------------------------------------------
(gdb) b 14
Breakpoint 2 at 0x80484fc: file test.c, line 14.
(gdb) continue
Continuing.
j is 1.000000
j is 3.000000
j is 4.000000
j is 6.000000
Breakpoint 2, main () at test.c:14
14 strcpy(bug,"hi");
(gdb)
---<설명>---------------------------------------
b 14는 14번째 라인에서 break point를 잡았다. 만약 dbx 사용자는
stop at 14로 하면 된다. continue는 프로그램 실행을 다음 breakpoint까지
계속 실행 시키는 것이다. dbx 사용자는 return을 치면 된다.
-----------------------------------------------------------
(gdb) p bug
$1 = 0x0
(gdb) s
Program received signal SIGSEGV, Segmentation fault.
0x400602cf in __fpu_control ()
(gdb)
---<설명>-------------------------------------------------
드디어 core 가 발생하는 위치로 왔다. 즉 14번 라인을 실행시켰더니
error message가 보였다. 즉 14번째 라인에서 버그가 있는 것이다.
무엇일까?
우선 p bug(변수 bug의 내용을 보여라)의 결과가 0x0이다 .
gdb에선 0x0 은 null 이다는 것이다. 즉 번지(address)가 없다.
그래서, 14번 라인을 실행시키전에 변수 bug에 번지를 할당 해야 한다.
즉 bug = (char *) calloc(1, sizeof(char)); 를 먼저 실행시키면된다.
--------------------------------------------------------
(gdb) quit
The program is running. Quit anyway (and kill it)? (y or n) y
yoo:~#
----< 설명>-------------------------------------------------
core 위치와 왜 잘못되었는지를 찾았으면, source를 고쳐야 한다.
그리고, 실행중인 gdb에서 빠져나와야한다. 소기의 목적을 달성했으니까..
그리고, 위에서 언급한데로 수정하고 다시 gdb를 실행해 보아라.
그러면 bug가 사라진 기쁨을 느낄 것이다.
우희희 (웃음 소리)..
------------------------------------------------------------
지금까진 초급수준에서 debugging 했다.
만약 source가 여러개 있다면, 즉 ***.c 화일이 여러개 있다면, 어떻게
debugging해야 할까?
다음에 설명하겠다. 기대해 주세요.
제 목 : [갈무리/강좌] GDB 사용법 (2)
서론
*******
이번 강좌주제는 c 화일이 여러개 있을 때 어떻게 디버그하나, 그리고,
pointer 변수를 어떻게 보냐 에 관심을 주겠다. 그리고, Makefile의 작성법도
포함 시킬려고 했지만, 마침 리눅스 강좌란에 automake, autoconf강좌가 있다.
그래서 보통과 달리 automake, autoconf강좌를 따라서 Makefile을 만들었다.
여러분도 Makefile을 만들어 보라 .기쁨을 느낄 것이다.
그리고,계시판 자
실인가? 여기에 source file과 Makefile을 압축한
score.tgz 화일이 있다. 이것을 down 받아서 사용하길 바란다.
본론
*******
구성된 화일은 score.c read.c write.c sample.h sample.inp이다.
실행화일이 하는 일은 sample.inp에 저장된 data에서
-------<sample.inp>-------------
# 번호 이름 국어 영어 수학
"
2 최인걸 100 90 50
"
--------------------------------
국어 영어 수학 점수를 읽어서, output file에는 총점과 평균값을 출력시킨다.
run은 다음과 같이 한다.
-----------------------------------
score sample.inp sample.out
-----<설명>------------------------
[명령어] [input file name] [output file name]
-----------------------------------
헤더화일에 포함된 struct는 다음과 같다.
---------------------------------
typedef struct score_def SCORE;
struct score_def {
char name[10];
int num, korea, math, engl, sum;
float aver;
SCORE * next;
};
-----------------------------------
그러면, 본격적으로 작성된 c화일을 디버깅해보자.
다음은 score.c 화일이며, main {}이 포함되어있다.
------------------------------------------------
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include "sample.h"
4
5 void main(int argc, char * argv [])
6 {
7 FILE * output , * input;
8 SCORE * score ;
9
10 if(argc < 3) {
11 perror( " TYPE [COMMAND] [INPUT FILE] [OUTPUT FILE] \n
")
12 exit(1);
13 }
14
15 /* 각 사람의 점수가 저장된 input 화일을 열어서,
16 input화일에 있는 data을 read함수를 이용해서 읽어 들인다. *
/
17 input = fopen(argv[1],"rt");
18 if (input == NULL) {
19 printf("There is an error in read input file . \n");
20 exit(1);
21 }
22
23 score = (SCORE *) calloc(1, sizeof(SCORE));
24 read(input, score);
25 fclose(input);
26
27 /* output 화일을 열고, write함수를 이용하여 output화일에 출력시킨다. *
/
28 output = fopen(argv[2],"wt");
29 write(output, score);
30 fclose(output);
31
32 return;
33 }
---<설명 >----------------------------------------------------------------
5번 line을 보면 간단한 프로그램과 모양이 다르게 나타났다. 즉
void main() 이나 int main()이 아닌, void main(int argc, char *argv[])로
되어 있다. 이런 모양이 무슨 역할을 할까? 물론, libray를 찾거나, 문법책을
보면서 배우면 되겠지만, 나는 gdb로 이용해서 생각해 보자.
우선 다음과 같이 해 보자.
[추천] vi 사용자는 term(예, 한텀)을 2개 열어놓고, 한 term은 vi editor로
다른 term은 gdb로 이용해서 사용하면 좋다.(일종의 통합환경...)
----------------------------------------------------------------------
hacker:~/score-0.0/src$ gdb score
GDB is free software and you are welcome to distribute copies ofit
under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.15.1 (i486-slackware-linux),
Copyright 1995 Free Software Foundation, Inc...
(gdb)
---<설명>-------------------------------------------------------------
위의 모습은 지난번 강좌로 눈에 익숙해 졌을 것이다.
----------------------------------------------------------------------
(gdb) b 10
Breakpoint 1 at 0x804864d: file score.c, line 10.
(gdb) run sample.inp sample.out
Starting program: /usr/home/ysh/score-0.0/src/score sample.inp sample.out
Breakpoint 1, main (argc=3, argv=0xbffff724) at score.c:10
10 if(argc < 3) {
(gdb)
---<설명>-----------------------------------------------------------
score.c 화일 내에서 10번째 라인에서 breakpoint를 잡도록 하고,
실행을 시키자. 실행명령은 run sample.inp[ input file 이름이다] sample.out[
output file 이름이다] 처럼친다.
그러면, 실행이 10번째 라인에서 멈춘다.
-----------------------------------------------------------------
(gdb) p argc
$1 = 3
(gdb) p *argv
$2 = 0xbffff80e "/usr/home/ysh/score-0.0/src/score"
(gdb) p argv[0]
$3 = 0xbffff80e "/usr/home/ysh/score-0.0/src/score"
(gdb) p argv[1]
$4 = 0xbffff830 "sample.inp"
(gdb) p argv[2]
$5 = 0xbffff83b "sample.out"
(gdb) p argv[3]
$6 = 0x0
(gdb)
---<설명>--------------------------------------------------------
지금 보여준 내용은 main(int argc, char *argv[])에 있는 argc와 argv가
어떤 값을 가지는 지 보여준다. argc는 argument의 갯수를 말하,
argv는 각 argument의 변수 명이다.
실행 시킨 명령어가 run[score] sample.inp sample.out이므로, argument
갯수가 3개 이고, argv[0]은 score이다. 보시다시피, directory 명과 함께
저장된다. argv[1]은 sample.inp이고, ....
argv[3]은 0x0즉 NULL 즉 pointer, 주소가 없다.
------------------------------------------------------------------
(gdb) b 24
Breakpoint 2 at 0x8048697: file score.c, line 24.
(gdb) c
Continuing.
Breakpoint 2, main (argc=3, argv=0xbffff724) at score.c:24
24 read(input, score);
(gdb) p input
$7 = (_IO_FILE *) 0x80499a8
(gdb) p *input
$8 = {_flags = -72539000, _IO_read_ptr = 0x0, _IO_read_end = 0x0,
_IO_read_base = 0x0, _IO_write_base = 0x0, _IO_write_ptr = 0x0,
_IO_write_end = 0x0, _IO_buf_base = 0x0, _IO_buf_end = 0x0,
_IO_save_base = 0x0, _IO_backup_ba
e = 0x0, _IO_save_end = 0x0,
_markers = 0x0, _chain = 0x40085ec8, _fileno = 5, _blksize = 0,
_offset = -1, _cur_column = 0, _unused = 0 '\000', _shortbuf = "",
_IO_lock = {ptr = 0x0, field1 = 0, field2 = 0}}
(gdb)
---< 설명 >--------------------------------------------------------
breakpoint를 24번째 라인 read(input, score);으로 정했다.
c 는 continue의 약자로 다음 breakpoint까지 ㅤ실행하라는 것이다.
p input 했더니, 주소가 나온다. 그리고, struct type로 보여준다.
적어도 input이 pointer인 것은 알 수있다. 실제로 input을 선언했을 때,
FILE * input로 했다. 그러면, pointer 변수의 내용은 어떻게 보는가?
p *( 변수명)처럼하면 된다.
----------------------------------------------------------------
(gdb) p score
$9 = (SCORE *) 0x8049a00
(gdb) p *score
$10 = {name = "\000\000\000\000\000\000\000\000\000", num = 0, korea = 0,
math = 0, engl = 0, sum = 0, aver = 0, next = 0x0}
(gdb)
---<설명 >-----------------------------------------------------
score도 pointer로 선언되었다. SCORE *score;
실제로 p score 치면, 번지와 struct type name이 나왔다.
내용을 볼 때는 어떻게 하지?
--------------------------------------------------------------
휴 식
*********
일단 gdb 에서 파져 나오자.
휴 식 끝 && 계속 디버깅하기
******************************
방금 전까지 read 함수 앞까지 디버깅하고 휴식했다.
다시 작업을 하는데 앞에서 했던 일을 반복하지 않고, read함수에 breakpoint를
잡고 싶다. 그러면 다음과 같이 해 보자.
-------------------------------------------------------------
hacker:~/temp/src# gdbscore
GDB is free software and you are welcome to 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.
GDB 4.15.1 (i486-slackware-linux),
Copyright 1995 Free Software Foundation, Inc...
(gdb) b read
Breakpoint 1 at 0x80486dc: file read.c, line 10.
(gdb) run sample.inp sample.out
Starting program: /root/temp/src/score sample.inp sample.out
Breakpoint 1, read (infile=0x80499a8,score=0x8049a00) at read.c:10
10 SCORE *iscore = NULL;
(gdb) s
13 while(fgets(line,80,infile) != NULL) {
(gdb) s
14 if( strncmp(line,"#",1) == 0)
(gdb) p line
$1 = "# 이 화일은 유 수행씨가 연습용으로 만든 것입니다.\n
\000\036;\000@\000\232\004\b$阪\004阪\225\
003@\000\232\004\b\000\000\000"
(gdb)
----<설명>---------------------------------------------------
(gdb)b read 에서 볼 수 있듯이 특정 함수를 디버깅하거나, breakpoint를
특정함수에 잡고싶으면, b(reak) [function name]처럼 치면 된다.
지금은 read 함수내로 들어왔다. 지금부터 input file에서 data를 읽어
들이는 과정이다.
read (infile=0x80499a8, score=0x8049a00)에서 보면, infile의 주소와
score의 주소가 나온다. 왜 그럴까?
read(FILE *infile, SCORE * score)에서 보면 알 수 있듯이 pointer론
선언을 해서, 주소로 전달된다. 주소로 전달될때의 장점과 단점은 ..?
fgets는 (f : file, get, s : string)로 이해 하자. 즉 화일에서 한 줄
string을 얻는다라는 쪽으로...
p line으로 확인 해보니까, 정말로 첫번째 line의 내용이 보인다.
-------------------------------------------------------------
(gdb) b 25
Breakpoint 2 at 0x8048739: file read.c, line 25.
(gdb) c
Continuing.
Breakpoint 2, read (infile=0x80499a8, score=0x8049a00) at read.c:25
25 sscanf(line,"%d %s %d %d %d",
(gdb) p line
$2 = " 1 유수행 100 100 100\n\000\n\000 만든 것입니다.\n
\000\036;\000@\000\232\004\b$阪\004阪\225\003@
\000\232\004\b\000\000\000"
(gdb) s
28 }
(gdb) p iscore
$3 = (SCORE *) 0x8049a00
(gdb) p *iscore
$4 = {name = "유수행\000\000\000", num = 1, korea = 100, math = 100,
engl = 100, sum = 0, aver = 0, next = 0x0}
(gdb) p score
$5 = (SCORE *) 0x8049a00
(gdb) p *score
$6 = {name = "유수행\000\000\000", num = 1, korea = 100, math = 100,
engl = 100, sum = 0, aver = 0, next = 0x0}
(gdb)
----<설명 >---------------------------------------------
breakpoint를 25번째 라인으로 설정했다. 그리고, c 로 실행시키면,
25번째 라인에서 실행이 멈췄다. 이 때, line에 저장된 내용을 보려고,
p line을 쳤고, step을 통해서 sscanf를 실행 시켰다.
sscanf는 s :string, scanf로 이해하자. string에서 scanf한다는것이다.
실제로 sscanf(line, " *******", ******); 처럼 string인 line이 함수내에
있다.
iscore와 score의 주소가 같다. 고로 저장된 data 내용도 같다.
pointer의 기능을 살작 엿볼 수 있는 기회다.
-------------------------------------------------------
(gdb) b write
Breakpoint 3 at 0x8048773: file write.c, line 12.
(gdb) c
Continuing.
Breakpoint 2, read (infile=0x80499a8, score=0x8049a00) at read.c:25
25 sscanf(line,"%d %s %d %d %d",
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x080486dc in read at read.c:10
breakpoint already hit 1 time
2 breakpoint keep y 0x08048739 in read at read.c:25
breakpoint already hit 2 times
3 breakpoint keep y 0x08048773 in write at write.c:12
(gdb) d 2
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x080486dc in read at read.c:10
breakpoint already hit 1 time
3 breakpoint keep y 0x08048773 in write at write.c:12
(gdb)
-----<설명>--------------------------------------
이번은 설정된 breakpoint가 왠지 부담스러울 때가 있을 때 처리하는 방법이다.
위에서 b write 처럼 write 함수에 breakpoint를 잡고 c로 실행시켰는데
전에 설정했던 breakpoint에서 실행이 멈췄다. 이 breakpoint를 없애기 위해선
info breakpoints를 치면된다.( 약자로 info b) 그러면 breakpoint에 할당된
번호가 있고 주소와 어디에서 무엇을 break했는지 메세지가 나온다.
설정을 취소하고 싶은 곳은 2번 breakpoint 이므로, delete 2 (약자로 d 2)로
친다.
정말로 설정이 해제되었는지 확인 해보자. info b ....
-----------------------------------------------
이것으로 디버깅 강좌를 마치고자 한다.
다만 부탁할 것은 생각한 어떤것을 실행시키고자 하나 명령어를 모를 때,
즉, 위에서처럼 설정된 breakpoint 를 제거하고 싶다는 생각이 들면,
help( 약자로 h)를 치면,
------------
-----------------------
(gdb) h
List of classes ofcommands:
running -- Running the program
stack -- Examining the stack
data -- Examining data
breakpoints -- Making program stop at certain points
files -- Specifying and examining files
status -- Status inquiries
support -- Support facilities
user-defined -- User-defined commands
aliases -- Aliases of other commands
obscure -- Obscure features
internals -- Maintenance commands
Type "help" followed by a class name for a list of commands in that class.
Type "help" followed by command name for full documentation.
Command name abbreviations are allowed if unambiguous.
(gdb)
----------------------------------------------
과 같이 나온다. status가 궁금하므로,
----------------------------------------------
(gdb) h status
Status inquiries.
List of commands:
show -- Generic command for showing things about the debugger
info -- Generic command for showing things about the program being debugged
Type "help" followed by command name for full documentation.
Command name abbreviations are allowed if unambiguous.
(gdb)
-------------------------------------------------
또 새로운 설명문이 나온다. 이번에 h info로 치자. 여러분은 h show나 다른
것의 명령어를 눈여겨 보아라. 필요할때 사용될 수 있는 명령어를 찾을 수
있다.
--------------------------------------------------
"
info breakpoints -- Status of user-settable breakpoints
info files -- Names of targets and files being debugged
info target -- Names of targets and files being debugged
Type "help info" followed by infosubcommand name for full documentation.
Command name abbreviations are allowed if unambiguous.
(gdb)
-------------------------------------------------
위에 보이는 것처럼 info breakpoints는 사용자가 만든 breakpoints의 status
보이는 명령어라는 것을 알 수 있다.
'UNIX_LINUX_C_C++' 카테고리의 다른 글
GDB를 이용한 디버깅 (0) | 2011.10.16 |
---|---|
gcc와 make 강좌 (0) | 2011.10.16 |
공유메모리 큐 구성을 통한 효과적인 데이터 처리 프로세스 구현 (0) | 2011.10.16 |
IPC 설비-메시지 큐 (0) | 2011.10.16 |
polling 개념잡기 - Polling Made Efficient (select vs poll vs /dev/poll) (0) | 2011.10.16 |