- GDB가 줄, 변수 및 함수를 읽기 쉬운 방식으로 표시하도록 하려면 항상 `-go -ggdb` 옵션을 사용하여 컴파일하십시오.
- 실행을 제어하려면 중단점(단순 중단점 및 조건부 중단점)과 함께 실행, 다음, 단계별 실행 및 계속 명령을 사용하십시오.
- 이 도구는 출력, 목록, 역추적 및 감시점을 결합하여 변수, 메모리 및 호출 스택을 검사합니다.
- 키보드 단축키와 정보 명령어를 활용하여 복잡한 C 및 C++ 프로그램을 더 빠르게 디버깅하세요.

C나 C++로 프로그래밍을 하다 보면 조만간 단순히 코드로 잡아낼 수 없는 버그를 만나게 될 겁니다... printf와 시행착오바로 그때 GDB는 더 이상 "이상한 명령어"가 아니라 프로그램이 실제로 한 줄 한 줄 무슨 일을 하는지 이해하는 데 필수적인 도구가 됩니다.
이 종합 가이드에서는 디버그 심볼을 포함하여 코드를 컴파일하는 방법부터 디버그 파일을 배치하는 방법까지 GDB를 쉽게 사용하는 방법을 살펴보겠습니다. 중단점 설정, 변수 검사, 스택 탐색 키보드 단축키를 활용해 보세요. 실용적인 예제와 명확한 설명으로 터미널에서 모든 작업을 수행할 수 있으므로, 오류를 찾아 헤매는 데 시간을 낭비하지 않고 제대로 디버깅할 수 있습니다.
GDB란 무엇이며, 왜 사용할 가치가 있을까요?
GDB(GNU Debugger)는 GNU 생태계의 표준 디버거입니다. 주로 C와 C++에서 사용되지만 다른 언어도 지원합니다. 미리 컴파일된 실행 파일에서 작동하며, 프로그램 실행 중에 내부를 "살펴보고", 충돌 시 발생하는 상황을 확인하고, 실행 과정을 조작할 수 있습니다.
화면 메시지를 이용한 디버깅과 달리 GDB는 다음과 같은 기능을 제공합니다. 프로그램을 언제든지 중단하고 메모리, 변수, 스택을 검사할 수 있습니다.값을 즉시 변경하고 실행을 재개할 수 있습니다. 이 모든 것은 매우 오래되었지만 놀라울 정도로 강력한 명령줄 인터페이스를 통해 이루어집니다.
GDB는 유닉스 초창기부터 존재해 왔으며, 다른 고전적인 도구들과 마찬가지로, gcc 또는 g ++원래는 그래픽 인터페이스 없이 터미널에서 직접 작동하도록 설계되었습니다. 오늘날에는 창과 버튼이 있는 프런트엔드가 있지만, 기본 명령어를 익히고 나면 대부분의 개발자는 GDB를 "기본적으로" 사용하는 것이 더 빠르다는 것을 알게 됩니다.
GDB가 진가를 발휘하는 대표적인 시나리오는 프로그램이 끔찍한 오류와 함께 종료될 때입니다. 세그멘테이션 오류(SIGSEGV)디버거가 없으면 유효하지 않은 메모리에 접근했다는 사실만 알 수 있지만, GDB를 사용하면 충돌이 발생한 줄, 실행 중인 함수, 그리고 그 순간 각 주요 변수에 어떤 값이 저장되어 있었는지까지 확인할 수 있습니다.
GDB를 사용하여 디버깅용 프로그램을 컴파일합니다.

GDB가 제대로 작동하려면 실행 파일에 다음 내용이 포함되어야 합니다. 디버깅 정보변수 이름, 함수, 소스 코드 줄 수 등을 지정할 수 있습니다. 이는 가이드에서 설명하는 것처럼 컴파일러에 특정 옵션을 추가하여 구현할 수 있습니다. gcc 명령어와 주요 옵션에 대한 완벽한 튜토리얼.
최소 요구 사항은 해당 옵션을 사용하여 컴파일하는 것입니다. -g예를 들어, 파일이 하나 있다면 crash.c 버그가 있으면 다음과 같은 바이너리 파일을 생성할 수 있습니다. crash 과 :
gcc -g -o crash crash.c
선택 -g 프로그램의 논리는 변경되지 않고, GDB가 소스 코드, 정확한 줄 번호, 스택 프레임의 실제 변수 이름을 표시할 수 있도록 메타데이터만 추가됩니다. 실행 파일 크기는 약간 커지지만, 그만한 가치가 있습니다.
C99 표준을 사용하면서 디버깅을 편리하게 계속하려는 프로젝트에서는 다음과 같은 옵션들을 조합할 수 있습니다.
gcc -std=c99 -g -o test test.c
C++를 사용하거나 GDB와의 더욱 긴밀한 통합을 원하는 경우, 많은 가이드에서 다음과 같은 방법을 권장합니다. -ggdb이 디버거에 대한 특정 디버깅 정보를 생성합니다.
g++ -ggdb -o programa main.cpp otras.cpp
요약하자면, GDB로 바이너리를 디버깅할 때는 항상 해당 바이너리가 다음 옵션으로 컴파일되었는지 확인해야 합니다. -go -ggdb그렇지 않으면 읽을 수 있는 함수와 코드 줄 대신 메모리 주소와 알 수 없는 기호만 표시됩니다.
첫 접촉: GDB를 시작하고 프로그램을 실행하세요.
심볼 정보가 포함된 실행 파일을 얻었다면 GDB를 시작하는 방법은 두 가지입니다. 하나는 "빈" 디버거를 실행하는 것이고, 다른 하나는 분석할 바이너리를 지정하는 것입니다. 두 번째 방법이 더 일반적입니다. 실행 파일을 시작하고 디버깅하는 작업을 단 한 단계로 완료할 수 있습니다..
예를 들어, 바이너리 파일을 로드하고 디버깅하려면 다음과 같이 하면 됩니다. crash:
gdb crash
GDB 버전, 저작권 및 라이선스 고지 사항이 포함된 헤더와 일반적인 프롬프트가 표시됩니다.
(gdb)
해당 프롬프트에서 명령어를 사용하여 프로그램을 실행할 수 있습니다. 운영 (또는 단순히 r):
(gdb) run
Starting program: ./crash
프로그램이 표준 입력을 기대하는 경우 GDB는 이를 그대로 전달합니다. 시작 시 실행 파일을 지정하는 대신 프로그램을 실행했다면, gdb나중에 바이너리 파일을 명령어와 연결할 수 있습니다. 파일:
(gdb) file memsim
에 GDB 종료 그냥 써 quit 또는 그 약어 q프로그램이 실행 중인 경우 GDB는 디버깅 종료 여부를 묻는 메시지를 표시합니다.
실제 사례: 세그멘테이션 오류 찾기
워크플로를 이해하기 위해 고전적인 예를 살펴보겠습니다. 숫자를 입력받아 1과 임의의 숫자의 합을 계산하고 결과를 출력하는 C 프로그램이 있는데, 메모리 오류가 발생했다고 가정해 보겠습니다.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *buf;
int sum_to_n(int num) {
int i, sum = 0;
for (i = 1; i <= num; i++)
sum += i;
return sum;
}
void printSum() {
char line;
printf("enter a number:\n");
fgets(line, 10, stdin);
if (line != NULL)
strtok(line, "\n");
sprintf(buf, "sum=%d", sum_to_n(atoi(line)));
printf("%s\n", buf);
}
int main() {
printSum();
return 0;
}
뭔가 분명히 수상하지만, 그대로 컴파일하고 실행하면 바로 문제가 드러날 것입니다.
gcc -g -o crash crash.c
./crash
enter a number:
5
Segmentation fault
시스템은 한 가지 경우에만 경고합니다. 잘못된 메모리 접근이게 전부입니다. 이제 GDB에서 프로그램을 실행하면 훨씬 더 자세한 내용을 볼 수 있습니다. 바이너리 파일을 로드하고 실행해 보세요.
gdb crash
(gdb) run
Starting program: ./crash
enter a number:
10
오류가 발생하면 GDB는 해당 신호를 통해 사용자에게 알립니다.
Program received signal SIGSEGV, Segmentation fault.
0x0017fa24 in _IO_str_overflow_internal () from /lib/tls/libc.so.6
당신이 그곳에 가게 된 이유를 알아보려면 다음 명령어를 사용하세요. 역 추적 (o bt):
(gdb) backtrace
#0 0x0017fa24 in _IO_str_overflow_internal() from /lib/tls/libc.so.6
#1 0x0017e4a8 in _IO_default_xsputn_internal() from /lib/tls/libc.so.6
#2 0x001554e7 in vfprintf() from /lib/tls/libc.so.6
#3 0x001733dc in vsprintf() from /lib/tls/libc.so.6
#4 0x0015e03d in sprintf() from /lib/tls/libc.so.6
#5 0x08048487 in printSum() at crash.c:22
#6 0x080484b7 in main() at crash.c:28
추적 결과는 다음과 같습니다. main 함수가 printSum을 호출했고, printSum은 다시 sprintf를 호출했습니다.그 후, libc 내부 함수들의 연쇄적인 작동으로 인해 충돌이 발생했습니다. 우리는 해당 구현부를 건드릴 수 없습니다. sprintf하지만 22번째 줄에서 어떤 매개변수를 전달했는지 확인할 수 있습니다. crash.c.
중단점, 단계별 실행 및 기본 명령
디버거의 가장 큰 장점은 필요할 때 실행을 멈출 수 있다는 것입니다. 바로 그것이 디버거의 존재 이유입니다. 브레이크포인트이는 프로그램이 한 줄을 실행하기 직전이나 함수에 진입할 때 프로그램을 중지시키는 기능입니다.
이전 예시에서는 호출 직전에 무슨 일이 일어나는지 살펴보고 싶었습니다. sprintf22번째 줄에 중단점을 설정할 수 있습니다. crash.c GDB에서 직접 가져온 내용:
(gdb) break crash.c:22
Breakpoint 1 at 0x804845b: file crash.c, line 22.
프로그램을 다시 실행하면 운영GDB는 이미 실행 중임을 알리고 처음부터 다시 시작할지 묻습니다. '예'를 선택하고 한계점에 도달하세요.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: ./crash
enter a number:
10
Breakpoint 1, printSum() at crash.c:22
22 sprintf(buf, "sum=%d", sum_to_n(atoi(line)));
프로그램이 거기서 멈춘 상태이므로, 다음 명령어를 사용하여 변수를 확인할 수 있습니다. 인쇄 (o p):
(gdb) print line
$1 = "10\000\000\000\000\000\000…"
읽어온 문자열은 합리적으로 보입니다. 이제 포인터를 살펴보겠습니다. buf:
(gdb) print buf
$2 = 0x0
문제는 바로 이것입니다: 우리는 다음으로 이동하고 있습니다 sprintf는 널 포인터입니다 목적지로 잘못된 주소에 쓰려고 시도하는 것입니다. 해결책은 메모리를 예약하는 것입니다. buf 또는 초기화되지 않은 포인터 대신 로컬 배열을 사용하십시오.
GDB는 또한 허용합니다 한 줄씩 실행하세요 명령 덕분에 다음 것 (N), 단계 (s) 및 까지 (또는):
- 다음 (n): 다음 줄을 실행하지만, 함수 호출이 있는 경우에는 해당 줄로 들어가지 않고 즉시 실행합니다.
- 단계(들): 해당 줄을 실행하고, 호출된 함수가 있으면 해당 함수의 첫 번째 명령어를 입력합니다. 이는 사용자 정의 함수를 실행하는 데 이상적입니다.
- (u)까지반복문 내에서 매우 유용하며, 해당 반복문을 벗어나 지정된 줄이나 반복문 외부의 다음 줄에 도달할 때까지 실행을 계속합니다.
예를 들어, 루프 라인에 중단점을 설정하면 sum_to_n 그런 다음 사용하면 됩니다. n o u모든 턴을 수동으로 반복하지 않고도 누적값이 어떻게 변하는지 확인할 수 있습니다.
(gdb) break crash.c:10
(gdb) run
Breakpoint 1, sum_to_n (num=50) at crash.c:10
10 for (i = 1; i <= num; i++)
(gdb) n
11 sum += i;
(gdb) n
10 for (i = 1; i <= num; i++)
(gdb) until 12
12 return sum;
프로그램이 다음 중단점까지 또는 종료될 때까지 계속 실행되도록 하려면 다음을 사용합니다. 계속 (o c):
(gdb) continue
Continuing.
조건부 중단점 및 중단점 관리
반복문이 많거나 복잡한 프로그램에서는 항상 "같은 지점"에서 멈추고 싶지 않을 수 있습니다. 바로 이럴 때 반복문이 유용합니다. 조건부 중단점이는 논리 표현식이 충족될 경우에만 활성화됩니다.
만약 당신이 안에 멈추고 싶다고 가정해 보세요. sum_to_n 매개변수가 다음과 같을 때만 num 값을 50으로 설정해 보겠습니다. 먼저 원하는 줄에 중단점을 만든 다음 조건을 추가합니다.
(gdb) break crash.c:10
Breakpoint 1 at 0x8048441: file crash.c, line 10.
(gdb) condition 1 num == 50
프로그램을 다시 실행한 후, GDB는 다음 경우에만 중지됩니다. 숫자 값 50 해당 선에 도달하면:
(gdb) run
Starting program: ./crash
enter a number:
50
Breakpoint 1, sum_to_n (num=50) at crash.c:10
10 for (i = 1; i <= num; i++)
활성화된 중단점과 그 개수를 확인하려면 다음을 사용할 수 있습니다. 정보 중단점중단점은 다양한 명령어를 사용하여 조작할 수 있습니다.
- 삭제: 인자 없이 사용하면 모든 중단점을 삭제하고, 숫자와 함께 사용하면 지정된 중단점만 삭제합니다.
delete 1). - 선명한함수 또는 줄과 연결된 중단점을 제거합니다.
clear main,clear 42...). - 비활성화/활성화일시적으로 중단점을 비활성화했다가 나중에 다시 활성화할 수 있습니다.
기존의 분기점 외에도 다음과 같은 것들이 있습니다. 관전 포인트특정 노선에서 멈추지 않고, 수정하거나 읽습니다 변수의 내용. 이것들은 다음과 같이 생성됩니다. watch variable (변경 사항의 경우) 또는 rwatch variable (읽기용) 목록은 다음과 같습니다. info watch복잡한 구조물에 적힌 예상치 못한 글을 찾아내는 데 매우 유용합니다.
코드, 변수 및 메모리를 살펴보세요
프로그램이 정지된 상태에서는 주로 코드를 탐색하고 값을 살펴보게 됩니다. GDB는 이를 위한 상당히 편리한 명령어 모음을 제공하며, 그 시작은 다음과 같습니다. 명부이는 원본의 일부를 화면에 표시하는 기능입니다.
가장 일반적인 용도 list 줄 번호나 함수 주변에 몇 줄의 코드를 요청하는 것입니다.
list 20: 20번째 줄을 중심으로 몇 개의 줄이 표시됩니다.list main: 목록의 처음부터 시작합니다main.list간단히 말해서, 마지막으로 표시된 줄부터 목록이 계속 이어집니다.
값을 검사하기 위해 우리는 이미 다음과 같은 방법을 살펴보았습니다. 인쇄 이것은 별표 명령어이지만, 단순 변수에만 국한되지 않습니다. 평가도 가능합니다. 완전한 표현형변환을 수행하거나, 구조체 필드에 접근하거나, 배열 요소를 볼 수 있습니다.
(gdb) print intermedio
$1 = 134513691
(gdb) print primero
$2 = 2654196
(gdb) print lista@25
초기화되지 않은 변수를 사용하는 프로그램의 예에서 GDB를 사용하면 다음을 빠르게 확인할 수 있습니다. 지역 변수에 초기값이 할당되지 않았습니다. 그리고 그 내용이 쓸모없는 데이터라는 뜻입니다. 이것이 바로 실행할 때마다 다른 숫자가 출력되는 이유이며, 초기화되지 않은 자동 메모리에 의존할 때 흔히 발생하는 현상입니다.
메모리의 원시 내용을 한 방향으로 봐야 하는 경우, 여러 방법을 조합할 수 있습니다. 프린트 &변수 주소를 얻은 다음 명령어를 사용합니다. x (조사하다):
(gdb) print &num
$1 = (int *) 0xbffff580
(gdb) x 0xbffff580
0xbffff580: 0x00000064
또한 피타입 GDB에게 변수나 표현식의 유형을 알려달라고 요청할 수 있는데, 이는 C++에서 복잡한 구조체나 템플릿을 다룰 때 유형이 헷갈리는 경우에 유용합니다.
(gdb) ptype saldo_ptr
또 다른 강력한 기능은 다음과 같은 기능입니다. 변수를 실시간으로 수정합니다 명령으로 set var예를 들어, 포인터의 이름을 지정하고 싶다면 saldo_ptr 제가 가리키겠습니다 saldo 정화 과정 중:
(gdb) set var saldo_ptr = &saldo
그 시점부터 실행은 해당 새 값으로 계속되므로 매번 프로그램을 다시 컴파일하지 않고도 다양한 시나리오를 테스트할 수 있습니다.
실행 및 호출 스택에 대한 정밀한 제어
규모가 큰 프로그램에 참여하게 되면 단순히 한 줄씩 차근차근 진행하는 것만으로는 충분하지 않습니다. 알아야 할 것이 많습니다. 어떻게 여기까지 오게 된 거죠? GDB는 스택을 관리하고 다양한 프레임을 탐색하기 위한 여러 명령어를 제공합니다.
명령 어디에 (o 역 추적/bt```는 현재 함수부터 진입점까지의 호출 스택을 표시합니다. 각 프레임에는 함수, 매개변수, 파일/줄 번호가 포함됩니다. ``를 사용하여 이러한 프레임을 위아래로 이동할 수 있습니다. up y 아래 (down) 다양한 수준에서 로컬 변수를 검사하려면:
(gdb) where
#0 funcion_profunda() at modulo.c:120
#1 calcula() at core.c:45
#2 main() at main.c:30
에 함수에서 빠르게 나가기 단계별로 끝까지 설명하지 않고 바로 명령어를 알려드리겠습니다. 마무리이는 현재 함수의 본문 나머지 부분을 실행하고 호출 지점에서 실행을 중지하여 이전 프레임으로 돌아갑니다.
디버깅을 바로 시작하려면 main libc 시작 코드부터 실행할 필요 없이 매우 유용한 지름길이 있습니다. 메인 시작이 명령은 프로그램을 시작하고 함수의 시작 부분에 임시 중단점을 설정합니다. main.
마지막으로 입력한 명령을 반복하려면 다음을 수행하십시오. 엔터 키를 누르세요 빈 줄에 입력하세요. 여러 항목을 연결할 때 매우 편리합니다. next o step 따라 갔다.
GDB에서의 키보드 단축키 및 히스토리 관리
GDB는 터미널에서 사용되므로 GNU Readline의 줄 편집 기능을 활용합니다. 즉, 다양한 편집 기능을 사용할 수 있다는 뜻입니다. 키보드 단축키 수동으로 모든 것을 입력하지 않고도 이동하고, 명령을 편집하고, 이전 명령을 재사용할 수 있습니다.
가장 유용한 단축키는 다음과 같습니다.
- CTRL+P / CTRL+N명령 기록에서 이전 또는 앞으로 이동합니다.
- CTRL+A / CTRL+E현재 줄의 시작 또는 끝으로 이동합니다.
- CTRL+B / CTRL+F왼쪽 또는 오른쪽으로 한 글자 이동합니다.
- ALT+F / ALT+B단어 단위로 앞뒤로 이동합니다.
- CTRL + K현재 커서 위치부터 줄 끝까지 잘라내어 버퍼에 저장합니다.
- Ctrl + Y마지막으로 잘라낸 항목을 커서 위치에 붙여넣으세요.
- CTRL+_ o CTRL+X CTRL+U해당 줄의 마지막 편집 내용을 되돌립니다(이 작업을 여러 번 반복할 수 있습니다).
- CTRL + L출력 화면이 엉망이 되었다면 화면을 새로고침하세요.
- CTRL + C디버깅 중인 프로그램의 실행을 중단합니다.
- CTRL+XA터미널에서 소스 코드 분할 보기 기능을 활성화 또는 비활성화합니다.
- CTRL+X 2: 두 번째 "창"(예: 어셈블러 창)을 열고 보기 방식을 전환합니다.
쉽게 생기는 습관 중 하나는 과거의 경험에 지나치게 의존하는 것입니다. CTRL+P를 누르고 ENTER를 누르세요. 명령어를 반복할 때 다음과 같은 경우 run, next o print, 그리고 사용 CTRL + R (Readline에서 상속받은 역방향 검색)을 사용하여 오래전에 실행한 명령을 찾습니다.
일상적으로 유용한 기타 GDB 명령어
앞서 언급한 내용 외에도, 매 순간 사용하지는 않더라도 기억해 두면 유용한 몇 가지 명령어가 있습니다. 그들은 매우 특정한 상황을 해결합니다. 우아한 방식으로.
- 도움: 내장 도움말을 표시합니다.
help일반적인 주제만 가르칩니다.help comando각 주문의 세부 정보를 확인하세요. - 정보 하위 명령 포함: 예를 들면,
info functions프로그램 기능 목록을 작성하려면,info locals현재 프레임의 로컬 변수를 보려면, 또는info breakpoints모든 한계점을 점검하기 위해. - 운영 인수를 사용할 경우: 일반 콘솔에서와 마찬가지로 매개변수와 리디렉션을 전달할 수 있습니다.
run 2048 24 4orun >salida.txt. - 선명한특정 함수 또는 줄에 설정된 중단점을 내부 번호를 기억할 필요 없이 제거합니다.
- 계속: 다음 중단점 또는 프로그램 끝까지 실행을 재개합니다.
- 종료GDB 세션이 종료됩니다. 프로그램이 아직 실행 중인 경우 확인을 요청합니다.
이 명령어 및 단축키 모음을 사용하면 심볼을 포함한 컴파일, GDB 시작 등 C 또는 C++에서 거의 모든 일반적인 디버깅 작업을 처리할 수 있습니다. 원하는 지점에서 멈추고, 프로그램의 상태를 확인하고, 변수를 변경합니다. 필요한 경우, 문제의 원인을 찾을 때까지 계속 진행하십시오.
GDB를 사용하여 실행 과정을 단계별로 추적하고, SIGSEGV 오류 발생 후 스택을 확인하고, 감시점을 설정하여 변수를 모니터링하고, 전체 루프를 건너뛰는 데 익숙해지면, until디버깅은 더 이상 추측과 테스트의 과정이 아니라 훨씬 더 정확하고 체계적인 조사로 바뀌며, 이를 통해 코드 자체에 대한 이해도도 향상됩니다.
바이트와 기술 전반에 관한 세계에 대한 열정적인 작가입니다. 나는 글쓰기를 통해 내 지식을 공유하는 것을 좋아하며 이것이 바로 이 블로그에서 할 일이며 가젯, 소프트웨어, 하드웨어, 기술 동향 등에 관한 가장 흥미로운 모든 것을 보여 드리겠습니다. 제 목표는 여러분이 간단하고 재미있는 방식으로 디지털 세계를 탐색할 수 있도록 돕는 것입니다.