본문 바로가기

UNIX_LINUX_C_C++

GDB 명령요약

출처 : 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
보이는 명령어라는 것을 알 수 있다.