『리눅스 학당-리눅스 강좌 / 연재 (go LINUX)』 332번 제 목:gdb 사용법<1> 올린이:생체졸병(유수행 ) 96/09/29 23:03 읽음:4305 관련자료 없음 ----------------------------------------------------------------------------- [서두 : 프로그램 디버거란???] 디버거란 프로그램 개발 도구로써, 프로그램을 개발하다가 에러가 발생하면 발생 위치 및 발생이유를 쉽게 찾을 수 있도록 도와 준다. [gdb] 명령 요약 프로그램 실행과 트레이스(trace)에 관련된 명령들 --------------------------------------------------------- run 현재의 인수를 사용하여 프로그램을 실행 run 새로운 <인수>를 가지고 프로그램을 실행 continue 현재 위치에서 프로그램을 계속 실행시킨다. (약자) c, cont, (dbx)return next 한 줄씩 실행 시킨다. 이 때 함수를 포함하고 있으면 함수를 수행시킨다. (약자) n next 줄을 실행시킨다. step 한 줄씩 실행 시킨다. 이 때 함수를 포함하고 있으면 함수 내부로 들어가서 한 줄씩 실행한다. (약자) s step 줄을 실행시킨다. " " break 라인 번호에서 프로그램 실행을 멈추게 한다. (dbx) stop at (약자) b break <함수 명> 함수 내부의 첫번째 라인에서 프로그램의 실행을 멈추게한다. (dbx) stop in <함수명> quit gdb를 종료 시킨다. ------------------------------------------------------------ 데이타에 관련된 명령들 ----------------------------------------------------------- whatis 지정한 <변수>에 관련된 정보를 보여준다. print 에 지정된 식의 값을 보여준다. (약자) p display 현재 지정된 display 명령의 목록을 보여준다. (dbx) history list 현재 위치에서 소스 파일의 내용을 10줄 보여준다. list , <시작줄>과 <끝줄>사이의 소스파일 내용을 보여준다. ----------------------------------------------------------- gdb 사용법을 알기 위해서 우선 bug가 있는 프로그램을 작성하자. ~#vi test.c ---------------< test.c 내용>-------------- 1 #include 2 3 main() 4 { 5 int i; 6 double 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 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 for( 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 9 for( 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해야 할까? 다음에 설명하겠다. 기대해 주세요. 『리눅스 학당-리눅스 강좌 / 연재 (go LINUX)』 337번 제 목:gdb 강좌<2> 올린이:생체졸병(유수행 ) 96/10/06 20:05 읽음:2993 관련자료 없음 ----------------------------------------------------------------------------- 서론 ******* 이번 강좌주제는 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에서 -------------------- # 번호 이름 국어 영어 수학 " 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 2 #include 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 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) ---<설명>------------------------------------------------------------- 위의 모습은 지난번 강좌로 눈에 익숙해 졌을 것이다. ---------------------------------------------------------------------- (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_base = 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 에서 파져 나오자. 『리눅스 학당-리눅스 강좌 / 연재 (go LINUX)』 338번 제 목:gdb 강좌<3> 올린이:생체졸병(유수행 ) 96/10/06 20:06 읽음:2831 관련자료 없음 ----------------------------------------------------------------------------- 휴 식 끝 && 계속 디버깅하기 ****************************** 방금 전까지 read 함수 앞까지 디버깅하고 휴식했다. 다시 작업을 하는데 앞에서 했던 일을 반복하지 않고, read함수에 breakpoint를 잡고 싶다. 그러면 다음과 같이 해 보자. ------------------------------------------------------------- hacker:~/temp/src# gdb score 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 of commands: 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 info subcommand name for full documentation. Command name abbreviations are allowed if unambiguous. (gdb) ------------------------------------------------- 위에 보이는 것처럼 info breakpoints는 사용자가 만든 breakpoints의 status 보이는 명령어라는 것을 알 수 있다. 위 방법처럼 앞으로 사용해 주길 바란다. 이번 강좌를 통해서 많은 것을 보여 줄려고 했지만, 솔직히 나도 초보자 라 많은 것을 보이지 못 하고 두서 없이 서술한것 같다. [출처] [펌] GDB 간단 레퍼런스 & 복잡|작성자 스핑크스