Linux,  Programming

[Linux] GDB 소개

GDB는 GNU Debugger의 약자로, Rechard Stallman이 개발했다. 많은 UNIX system에서 사용되는 free software다.

사용법

GDB는 gcc build 할 때 -g option을 주고 나온 executable을 가지고 사용 할 수 있다.

$ gdb -g a.c -o a.out

그리고 gdb 수행 할 때 gcc 결과물을 넣어주면 된다. 만약 argument가 있다면 argument를 주고 실행하면 된다. 그 후 run (r)을 치면 수행된다.

# gdb [executable] [arg0] [arg1] ...
$ gdb a.out hello world
...
(gdb) run

Function

GDB를 사용하는데 많은 기능들이 존재한다. 각 기능에 대해서 간략하게 아래 표로 설명할 수 있다.

Command (short key)Description
file파일의 심볼을 읽음
run (r)파일 실행
break (b)Break point 생성
info (i)각종 정보 표시
continue (c)계속 파일 실행
next (n)다음 라인 수행
step (s)함수 안으로 진입
print (p)변수를 출력
set변수 값 변경 가능
examine (x)메모리 내용 확인
list (l)Source code를 출력
kill (k)프로그램 종료
watch (wa)특정 변수를 지속적으로 출력
delete (d)Break point와 watch point 제거
backtrace (bt)프로그램이 중단되었을 때 stack frame 출력
display특정 변수를 매번 출력
u현재 loop을 빠져나감
finish현재 함수를 수행하고 빠져나감
return현재 함수를 수행하지 않고 빠져나감
step instruction (si)현재 instruction 수행하면서 함수로 진입
next instruction (ni)현재 instruction 수행
advance특정 포인트까지 진행
frame (f)Backtrace 후 특정 frame으로 포커스 변경
threadThread switching
attach현재 실행중인 프로그램을 디버깅 할 때 사용
disas특정 주소에 대해 disassembly 할 때 사용
layout각종 정보들 (register, assembly)를 한눈에 볼 수 있게 도움

Run

프로그램을 수행하는 명령어로, argument를 넣어서 수행 할 때도 사용된다.

# 1
$ gdb --args [executable] [arg0] [arg1] ...
# 2
(gdb) r [arg0] [arg1]

Break Point

GDB 수행 중 특정 부분에서 멈추고자 할 때 사용된다. 아래의 코드를 예시를 들어본다.

#include <stdio.h>                                                                                                                                                         
#include <stdlib.h>                                                                  
                                                                                     
int sum(int, int);                                                                   
                                                                                     
int main(int argc, char **argv)                                                      
{                                                                                    
    int i;                                                                           
    int j;                                                                           
    int r;                                                                           
                                                                                     
    i = atoi(argv[1]);                                                               
    j = atoi(argv[2]);                                                               
    printf("Add from %d to %d\n", i, j);                                             
    r = sum(i, j);                                                                   
    printf("Sum = %d\n", r);                                                         
                                                                                     
    return 0;                                                                        
}                                                                                    
                                                                                     
int sum(int from, int to)                                                            
{                                                                                    
    int i;                                                                           
    int total = 0;                                                                   
                                                                                     
    for (i=from; i<to; i++) {                                                        
        total += i;                                                                  
    }                                                                                
    return total;                                                                    
}
(gdb) r 2 3
(gdb) l
1	#include <stdio.h>
2	#include <stdlib.h>
3	
4	int sum(int, int);
5	
6	int main(int argc, char **argv)
7	{
8		int i;
9		int j;
10		int r;
(gdb) l
11	
12		i = atoi(argv[1]);
13		j = atoi(argv[2]);
14		printf("Add from %d to %d\n", i, j);
15		r = sum(i, j);
16		printf("Sum = %d\n", r);
17	
18		return 0;
19	}
20	
(gdb) b 15
Breakpoint 1 at 0x5555555546de: file test.c, line 15.
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005555555546de in main at test.c:15

위 코드에서 15번째 라인에 break point를 넣고 break point를 info (i) command로 확인하는 모습이다. 그리고 수행하면 break point에서 멈추는 것을 확인 할 수 있고, continue (c) command로 끝까지 수행 할 수 있다.

(gdb) r 2 3
Starting program: /home/shumin/workspace/03_System_Programming/00/a.out 2 3
Add from 2 to 3

Breakpoint 1, main (argc=3, argv=0x7fffffffe598) at test.c:15
15		r = sum(i, j);
CommandDescription
b func func 심볼의 시작 부분에 BreakPoint 설정
b 10 10행에 BreakPoint 설정
b file.c:func file.c 파일의 func Symbol에 BreakPoint 설정
b file.c:10 file.c 파일의 10행에 BreakPoint 설정
b +2 현재 행에서 2개 행 이후 지점에 BreakPoint 설정
b -2 현재 행에서 2개 행 이전 지점에 BreakPoint 설정 
b *0x8049000 0x8049000 주소에 BreakPoint 설정(Assembly로 디버깅시 사용)
b 10 if var == 0 10행에 BreakPoint를 설정하는데, var 변수의 값이 0일 때 작동
rb fun* *fun*에 해당하는 모든 Symbol에 BreakPoint 설정
rb ^fun fun으로 시작하는 모든 Symbol에 BreakPoint 설정
rb TestClass:: TestClass에 해당하는 모든 Symbol에 BreakPoint 설정

Step

함수의 안으로 진입하고 싶을 때 사용하는 명령어다. 위 예시 코드를 계속 수행하면 아래와 같다.

(gdb) s
sum (from=2, to=3) at test.c:24
24		int total = 0;

Print

특정 변수 또는 함수의 결과를 출력하고 싶을 때 사용한다.

CommandDescription
p varvar 변수의 값을 출력
p sum(1, 10)sum(1, 10) 함수의 결과를 출력
p struct->var구조체 struct의 var 변수 출력
p (*pointer).var포인터에 접근해서 var 변수 출력
p (*pointer)구조체 전체를 출력
p $rip특정 register의 값 확인

실제로 함수 내부로 진입 후 함수의 argument의 값과, 함수를 호출한 결과를 출력할 수 있는 것을 볼 수 있다.

Next

Line by line으로 수행하고 싶을 때 사용한다. 뒤에 숫자를 넣어 여러 라인을 수행시킬 수 있다.

(gdb) n [number]

Set

특정 변수 또는 메모리의 값을 새로 변경할 때 사용한다.

set count = 10count 변수에 10을 저장
set {int}0x84aa_fa00 = 100x84aa_fa00 주소에 10을 저장

Examine

특정 주소의 메모리 값을 확인하는데 사용하는 명령어다.

x/[repeat count][format][unit size] [address]
OptionDescription
Repeat count입력된 주소를 기준으로 연속된 address에 대해 repeat count만큼 데이터를 출력한다. Default value는 1이다.
Formatx (hexa), d (decimal), u (unsigned decimal), o (octal), t, a, c, f, s (string), i (for machine instructions), m (for displaying memory tags) 다양한 포멧을 제공한다. Default는 h다.
Unit sizeb: Bytes.
h: Halfwords (two bytes).
w: Words (four bytes). This is the initial default.
g: Giant words (eight bytes).
CommandDescription
x 0x5555555547170x555555554717 주소의 data 출력
x/i 0x5555555547170x555555554717 주소의 data를 instruction으로 출력
x/4x 0x5555555547170x555555554717 주소에 있는 data를 4 bytes씩 출력
x $rip$rip에 저장된 주소가 가리키는 내부 값을 출력

아래 코드는 Program Counter (PC)가 가지고 있는 주소를 x command를 통해 알아내는 방법이다.

(gdb) x $pc
0x555555554717 <sum+10>:	0x00fc45c7

참고로 출력하는 형식을 바꿀 수 있는데 다음과 같이 구성된다.

List

Source code를 출력하는데 사용한다.

CommandDescription
 l main 함수를 기점으로 소스 출력 
 l 10 10 행을 기준으로 출력
 l func func 함수의 소스를 출력 
 l – 출력된 행의 이전 행을 출력
 l file.c:func file.c 파일의 func 함수 부분을 출력 
 l file.c:10 file.c 파일의 10행을 기준으로 출력
 set listsize 20 출력 행이 10행에서 20행으로 증가

Info

각종 정보들을 출력한다.

CommandDescription
 i b등록된 BreakPoints와 watchpoints를 출력
 i line현재 디버깅하과 있는 위치 정보를 출력
 i program현재 프로그램의 process 정보를 출력
 i args현재 함수로 넘어온 인자의 정보를 출력
 i locals현재 함수의 지역 변수를 출력
i r레지스터 값 확인
i var모든 변수들 출력
i thread현재 동작하는 thread들의 정보를 출력
(gdb) i r
rax            0xe	14
rbx            0x0	0
rcx            0x5555555546a0	93824992233120
rdx            0x7fffffffe518	140737488348440
rsi            0x7fffffffe508	140737488348424
rdi            0x1	1
rbp            0x7fffffffe420	0x7fffffffe420
rsp            0x7fffffffe400	0x7fffffffe400
r8             0x7ffff7dced80	140737351839104
r9             0x7ffff7dced80	140737351839104
r10            0x2	2
r11            0xffffffff	4294967295
r12            0x555555554540	93824992232768
r13            0x7fffffffe500	140737488348416
r14            0x0	0
r15            0x0	0
rip            0x555555554682	0x555555554682 <main+56>
eflags         0x202	[ IF ]
cs             0x33	51
ss             0x2b	43
ds             0x0	0
es             0x0	0
fs             0x0	0
gs             0x0	0

Watch

Watch point를 정해 특정 변수가 바뀔 때 마다 break가 걸린다. 이때 해당 변수의 scope에서만 설정이 가능하다.

CommandDescription
watch varvar 변수가 써질 때 실행을 중단
rwatch varvar 변수가 읽힐 때 실행을 중단
awatch varvar 변수가 읽고 써질 때 실행을 중단

Delete

Break point와 watch point 제거에 사용된다.

d 11번째 point 삭제
d 2 32, 3번째 point 삭제
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005555555546de in main at test.c:15
	breakpoint already hit 1 time
2       breakpoint     keep y   0x000055555555471e in sum at test.c:26
3       hw watchpoint  keep y                      i
(gdb) d 3
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005555555546de in main at test.c:15
	breakpoint already hit 1 time
2       breakpoint     keep y   0x000055555555471e in sum at test.c:26

Backtrace

어떤 과정을 거쳐 현재의 위치로 와 있는지 확인할 수 있으며, segment fault가 발생 했을 때 유용하다.

Display

특정 변수를 매번 출력할 때 사용한다.

CommandDescription
 display var var 변수 값을 매번 화면에 출력
 display /x var var 변수 값을 16진수로 출력
 undisplay 1 1번 display point를 제거
 disable display 1  1번 display point를 비활성화
 enable display 1 1번 display point를 활성화

Frame

backtrace를 보게되면 function call의 순서가 나열되며 여러 frame이 나오게 된다. 이 때 내가 특정 frame으로 이동해서 해당 frame의 변수를 frame 기능을 통해서 수행 가능하다.

CommandDescription
 f현재 frame 정보 출력
f 8backtrace 출력한 frame number 중 8번 frame으로 이동

Thread

Multi-thread 프로그램을 디버깅 할 때 사용되는 명령어다.

CommandDescription
 thread 22번 thread로 switching
(Thread 정보를 알기 위해서 i thread를 통해 알 수 있음)

Attach

현재 실행중인 프로그램을 디버깅 할 때 사용하는 명령어다.

CommandDescription
 attach 2323PID 2323의 process를 gdb에 물림, 그러나 debug symbol이 없어서 사람 눈으로 보기 해석하기 어려움
(PID 정보를 알기 위해서 ps 명령어를 통해 알 수 있음)

Layout

CommandDescription
 layout reg현재 시점에서 register 정보들을 한눈에 보여줌
layout asm현재 시점에서 assembly code를 한눈에 보여줌

참고로 끄는 방법은 Ctrl+x + a 다.

Core File

프로세스가 수행 중에 치명적인 예외가 발생해 더 이상 수행이 불가능할 때 생성된다. 예를 들면 0으로 나눈다던지, 잘못된 메모리 참조와 같은 상황에 발생한다.

  예외 상황에 처하면 OS는 signal을 해당 프로세스에 보내고 시그널을 받은 프로세스는 kernel에 있는 기본 시그널 핸들러를 수행하는데, 기본 시그널 핸들러에서 core file로 저장하는 부분이 있다면 현재 수행 중인 프로세스의 이미지를 core file로 저장한다. 즉 Core file이란 문제가 발생해 프로세스가 종료되는 순간의 프로세스 이미지 파일로, 여기에는 종료 순간의 CPU context와 프로세스의 code 및 data, stack 등의 정보가 들어있어 debugging 하는데 유용하게 사용할 수 있다.

# gdb [executable file] [core dump]
$ gdb a.out core

Reference

  • https://en.m.wikipedia.org/wiki/GNU_Debugger

Leave a Reply

Your email address will not be published. Required fields are marked *