GDB·LLDB TUI와 프런트엔드 — gdb-dashboard·gef·pwndbg·VS Code
GDB는 기본적으로 명령줄입니다. 강력하지만 한 화면에 지금 어디서 멈췄는지, 변수가 얼마인지, 콜스택, 레지스터를 동시에 보여 주지 못합니다. 이 장은 TUI(터미널 UI)와 외부 프런트엔드로 그 한계를 메우는 방법입니다.
내장 TUI에서 출발해 cgdb·gdb-dashboard·gef/pwndbg 같은 터미널 확장, VSCode·nvim-dap·Emacs 같은 IDE 통합, 마지막으로 모든 IDE가 공유하는 DAP(Debug Adapter Protocol)의 정체까지 살펴봅니다.
#GDB 내장 TUI
(gdb) tui enable(gdb) layout src # 소스 + 명령(gdb) layout asm # 어셈블리 + 명령(gdb) layout regs # 레지스터 패널 추가(gdb) layout split # 소스 + 어셈블리 + 명령토글 단축키 Ctrl-x a. 한 번 켜면 화면이 분할되어 위쪽엔 소스가, 아래쪽엔 명령창이 뜹니다. 화살표/PgUp으로 소스 스크롤, Ctrl-x o로 패널 포커스 전환, Ctrl-l로 다시 그리기.
#동작 원리
TUI는 ncurses 위에 구현됐습니다. 정지·재개 이벤트마다 GDB가 현재 PC·소스·레지스터를 다시 받아 화면을 그립니다.
레이아웃은 고정된 윈도 셋의 조합.
| 윈도 | 단축 | 내용 |
|---|---|---|
src | s | 소스 코드 |
asm | a | 어셈블리 |
regs | r | 레지스터 |
cmd | c | 명령창 (항상 표시) |
#자주 쓰는 단축키
| 단축키 | 동작 |
|---|---|
Ctrl-x a | TUI on/off 토글 |
Ctrl-x 1 | 한 윈도 |
Ctrl-x 2 | 두 윈도 |
Ctrl-x o | 다음 패널로 포커스 |
Ctrl-l | 다시 그리기 |
Ctrl-x s | TUI SingleKey 모드(s=step, n=next 등) |
+ / - | 활성 윈도 크기 조절 |
< / > | 소스 스크롤 좌우 |
| PgUp / PgDn | 소스 스크롤 |
#제약
- 윈도 깨짐이 자주 일어남(
Ctrl-l자주 누르게 됨). 이유는 GDB가 ncurses 화면 위에 그대로print출력을 던지면서 화면이 망가지기 때문입니다. Python으로 직접 ncurses와 통합한 cgdb·dashboard가 더 안정적인 이유. - 마우스 안 됨.
- 일부 환경에서 color 안 나옴.
- 변수·watch 패널 없음.
가벼운 디버깅에는 충분. 큰 코드베이스에선 곧 한계를 느낍니다.
#cgdb — 분할 화면 GDB
cgdb는 위에 vi 키 바인딩이 적용된 소스 뷰어, 아래에 GDB 명령창이 있는 진짜 TUI.
$ cgdb ./my_programEsc로 소스 패널,i로 명령 패널.- 소스 패널에서 vi 키(
j/k//)로 이동. - 마우스 휠 스크롤 가능.
- 색상·구문 강조가 깔끔.
#동작 원리
cgdb는 GDB를 서브프로세스로 띄우고 stdin/stdout을 가로챕니다. MI(머신 인터페이스) 명령을 보내 현재 PC, 소스 파일, 콜스택을 구조적으로 받아 자체 ncurses UI를 그립니다. GDB 내장 TUI보다 ncurses와 충돌이 적은 이유.
GDB 내장 TUI보다 훨씬 안정적입니다. 패키지 매니저로 바로 설치되므로 첫 선택지로 추천.
~/.cgdb/cgdbrc # 설정set winminheight=3set winsplit=top_bigset syntax=onset color=onmap <C-n> :gdb next<CR>#gdb-dashboard — 깔끔한 단일 파이썬 스크립트
cyrus-and/gdb-dashboard — 한 파이썬 스크립트(~/.gdbinit에 source)로 콘솔에 멀티 패널을 그립니다.
$ wget -O ~/.gdbinit https://raw.githubusercontent.com/cyrus-and/gdb-dashboard/master/.gdbinit$ gdb ./my_program기본 패널: source / assembly / stack / registers / variables / breakpoints / expressions / threads / memory / history.
>>> dashboard -layout source assembly stack registers variables>>> dashboard memory watch 0x7fff0000 64>>> dashboard expressions watch "i + j">>> dashboard source -style compact>>> dashboard -style syntax_highlighting 'monokai'#어떻게 그리는가
gdb.events.stop에 훅을 걸어 정지마다 각 패널의 콘텐츠를 Python으로 수집해 print. ncurses를 쓰지 않으므로 화면 깨짐 없음. 매 정지마다 위에서 아래로 다시 그려지는 식이라 스크롤백을 잃지 않습니다.
장점 — Python 한 파일이라 설정/이식 쉬움. TUI 모드처럼 화면이 깨지지 않고, 매 정지마다 위쪽에 패널이 다시 그려집니다.
커스텀 모듈을 직접 추가할 수 있어 팀별 디버깅 환경을 표준화하기 좋습니다.
class MyPanel(Dashboard.Module): def label(self): return "my-counters" def lines(self, term_width, term_height, style_changed): cnt = int(gdb.parse_and_eval("g_counter")) return [f"counter = {cnt}"]#gef / pwndbg / peda — 보안·exploit 디버깅
세 가지 모두 보안 분석가의 작업을 GDB 위에서 빠르게 하기 위한 Python 확장입니다. 설치하면 정지할 때마다 레지스터·스택·디스어셈블·코드 컨텍스트가 자동으로 한 화면에 뜹니다.
# gef$ wget -O ~/.gdbinit-gef.py https://gef.blah.cat/py$ echo "source ~/.gdbinit-gef.py" >> ~/.gdbinit
# pwndbg$ git clone https://github.com/pwndbg/pwndbg$ cd pwndbg && ./setup.sh이 장의 관심사는 TUI이므로 여기서는 셋의 성격만 구분해 둡니다. gef는 한 파일짜리라 가볍고 ARM·MIPS·PPC까지 따라오며, pwndbg는 힙 분석이 가장 깊어 CTF·exploit에서 사실상 표준입니다. peda는 x86 중심의 오래된 도구라 지금은 옛 자료를 따라갈 때 씁니다.
셋의 상세 비교와 context·vmmap·heap·checksec 같은 공통 명령표는 GDB Extension and IDE Ch 6: GDB 프런트엔드 비교에 정리돼 있습니다.
주의할 점은 하나입니다. 이 확장들은 매 정지마다 큰 컨텍스트를 쏟아내기 때문에, 보안 분석이 아닌 일반 애플리케이션 디버깅에서는 화면이 오히려 시끄러워집니다. 그럴 때는 앞의 gdb-dashboard나 cgdb 쪽이 맞습니다.
#VSCode + cppdbg / CodeLLDB
터미널을 벗어나면 VSCode가 가장 많이 쓰는 프런트엔드입니다. GDB 사용자 입장에서 알아 둘 것은 두 익스텐션이 GDB와 대화하는 방식이 다르다는 점입니다.
cppdbg는 Microsoft가 만든 C/C++ 익스텐션으로, GDB와 MI 프로토콜로 통신합니다. GDB의 명령이 그대로 전달되므로 .gdbinit의 pretty-printer 설정이 살아 있고, setupCommands로 GDB 명령을 직접 밀어 넣을 수도 있습니다. CodeLLDB는 LLDB의 DAP 서버를 직접 띄우는 쪽이라 macOS·Rust·Swift에서 강합니다.
MI를 한 겹 거치기 때문에 생기는 어긋남도 있습니다. 조건부 breakpoint의 표현식이 GDB에서 직접 칠 때와 미묘하게 다르게 평가되는 경우가 있고, fork 추적은 GDB에서 set follow-fork-mode로 다루던 만큼 정교하지 않습니다. 콘솔 입력이 필요한 프로그램은 "externalConsole": true가 필요하고, core dump는 coreDumpPath를 따로 줘야 합니다.
launch.json 전체 예시(launch·attach·gdbserver 원격), 단축키표, Logpoint와 Watch 패널 사용법은 GDB Extension and IDE Ch 6에 정리돼 있습니다.
#Neovim — nvim-dap
nvim-dap이 Debug Adapter Protocol(DAP) 클라이언트, nvim-dap-ui가 UI. VSCode와 같은 디버그 어댑터(cppdbg, CodeLLDB)를 그대로 씁니다.
local dap = require('dap')
dap.adapters.lldb = { type = 'executable', command = '/usr/bin/lldb-vscode', name = 'lldb',}
dap.configurations.cpp = { { name = 'Launch', type = 'lldb', request = 'launch', program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file') end, cwd = '${workspaceFolder}', stopOnEntry = false, args = {}, }, { name = 'Attach to PID', type = 'lldb', request = 'attach', pid = require('dap.utils').pick_process, args = {}, },}
dap.configurations.c = dap.configurations.cppdap.configurations.rust = dap.configurations.cppvim.keymap.set('n', '<F5>', dap.continue)vim.keymap.set('n', '<F10>', dap.step_over)vim.keymap.set('n', '<F11>', dap.step_into)vim.keymap.set('n', '<S-F11>', dap.step_out)vim.keymap.set('n', '<F9>', dap.toggle_breakpoint)vim.keymap.set('n', '<leader>B', function() dap.set_breakpoint(vim.fn.input('Condition: '))end)vim.keymap.set('n', '<leader>dl', dap.run_last)vim.keymap.set('n', '<leader>dt', dap.terminate)dap-ui가 VSCode와 거의 같은 패널 레이아웃을 그립니다.
local dapui = require('dapui')dapui.setup()dap.listeners.after.event_initialized['dapui_config'] = dapui.opendap.listeners.before.event_terminated['dapui_config'] = dapui.closeNeovim 안에서 디버그 패널 띄우고, REPL로 표현식 평가. Vim 키를 유지하면서 IDE 수준의 경험을 가져갑니다.
#DAP — 표준 프로토콜의 의미
VSCode·Neovim·Emacs(dap-mode)·Sublime·Helix가 모두 같은 DAP를 씁니다. 따라서 한 어댑터(cppdbg)를 설치하면 어느 에디터든 동일한 디버깅 경험을 얻을 수 있습니다. RSP가 디버거 ↔ 타깃 표준이라면 DAP는 IDE ↔ 디버거 표준.
[에디터] ← DAP → [adapter (예: cppdbg)] ← MI → [GDB] ↑ RSP [gdbserver / OpenOCD]#DAP 메시지
JSON-RPC over TCP/stdio. 모든 메시지가 request / response / event 셋.
// 요청{"seq": 1, "type": "request", "command": "initialize", "arguments": {"clientID": "vscode", "linesStartAt1": true}}
// 응답{"seq": 2, "type": "response", "request_seq": 1, "success": true, "command": "initialize", "body": {"supportsConfigurationDoneRequest": true, ...}}
// 이벤트{"seq": 3, "type": "event", "event": "stopped", "body": {"reason": "breakpoint", "threadId": 1}}주요 명령.
| Command | 동작 |
|---|---|
initialize | 핸드셰이크 |
launch / attach | 시작 / 부착 |
setBreakpoints | 파일별 BP 일괄 설정 |
setFunctionBreakpoints | 함수명 BP |
setDataBreakpoints | watchpoint |
threads | 스레드 목록 |
stackTrace | 콜스택 |
scopes | 한 프레임의 스코프 (Local/Arg/Reg) |
variables | 한 스코프의 변수들 |
continue / next / stepIn / stepOut | 진행 |
evaluate | 표현식 평가 |
VSCode의 변수 트리는 재귀적인 variables 호출입니다. 사용자가 트리 노드를 펼칠 때마다 그 노드의 variablesReference를 인자로 다시 호출. 결과적으로 깊은 객체도 지연 로드되어 IDE가 빠릅니다.
#Emacs — dap-mode / gud
전통적으로 Emacs는 M-x gdb로 GUD(GDB UI mode)를 썼습니다. 요즘은 dap-mode로 같은 DAP 어댑터를 공유.
(use-package dap-mode :after lsp-mode :config (dap-auto-configure-mode))
(use-package dap-cpptools) ; cppdbg(use-package dap-lldb)(dap-register-debug-template "C++ Launch" (list :type "cppdbg" :request "launch" :name "C++ Launch" :MIMode "gdb" :program "${workspaceFolder}/build/my_program" :cwd "${workspaceFolder}"))자세한 설정은 dap-mode 문서. Doom Emacs는 lsp +debugger 모듈로 한 번에.
#데이터·메모리 시각화
- VSCode의 Hex Editor + memoryview — 메모리 영역을 hex 그리드로.
- gef의
memoryview— 메모리 영역을 색칠해 표시. - gdb의
dashboard memory— 직접 watch 영역 지정. - GDB 자체 —
x/100bx 0x...또는 Python으로 binary blob 시각화.
큰 버퍼·이미지 디버깅에서는 hex 뷰가 콜스택보다 더 자주 쓰입니다.
#NumPy/이미지 자체 시각화
GDB Python으로 NumPy 배열을 PNG로 떨어뜨려 외부 뷰어로 보기.
class ViewImage(gdb.Command): def __init__(self): super().__init__("view_image", gdb.COMMAND_USER) def invoke(self, arg, from_tty): v = gdb.parse_and_eval(arg) w = int(v['width']); h = int(v['height']) data = v['data'] # 메모리를 numpy로 import numpy as np buf = gdb.selected_inferior().read_memory(int(data), w*h*3) arr = np.frombuffer(buf, dtype=np.uint8).reshape((h, w, 3)) from PIL import Image Image.fromarray(arr).save('/tmp/view.png') import subprocess subprocess.Popen(['xdg-open', '/tmp/view.png'])
ViewImage()컴퓨터 비전·비디오 코덱·머신러닝 디버깅에서 가시화는 콜스택을 100번 들여다보는 것보다 빠릅니다.
#Cortex-Debug — Cortex-M을 붙일 때
Cortex-M 펌웨어라면 Cortex-Debug가 IDE 쪽 표준입니다. OpenOCD·J-Link·ST-Link·Black Magic Probe를 자동으로 띄우고 GDB를 붙여 주므로, Ch 8에서 손으로 하던 gdbserver 연결이 설정 한 덩어리로 줄어듭니다.
이 시리즈의 관심사는 GDB 쪽이므로 여기서는 GDB 사용자가 얻는 것만 짚습니다. SVD 파일을 읽어 peripheral 레지스터를 비트 단위로 펼쳐 주고, Segger RTT 출력을 콘솔에 얹어 주며, J-Link에서는 CPU를 세우지 않고도 변수를 갱신합니다.
launch.json 전체 예시와 servertype별 차이는 GDB Extension and IDE Ch 6에 있습니다.
#어느 걸 골라야 하나
| 상황 | 추천 |
|---|---|
| 가벼운 한 번 디버깅 | GDB TUI(Ctrl-x a) |
| 일상 — 터미널만 | cgdb 또는 gdb-dashboard |
| 일상 — IDE 같이 | VSCode + cppdbg/CodeLLDB |
| Vim 사용자 | nvim-dap + dapui |
| CTF / exploit | pwndbg 또는 gef |
| 임베디드 GDB (Cortex-M) | VSCode + Cortex-Debug |
| 자동화 / 헤드리스 | gdb -batch + Python |
| 원격 + 콘솔만 | cgdb over SSH |
#정리
- 내장 TUI(
Ctrl-x a)는 빠른 시각화, 화면 깨짐만 감수하면 충분. - cgdb·gdb-dashboard가 터미널에서 가장 안정적.
- gef/pwndbg는 보안·CTF 용으로 강력하지만 일반 작업엔 시끄러움.
- IDE 통합은 VSCode + cppdbg/CodeLLDB 또는 nvim-dap.
- DAP가 IDE↔디버거 표준 — request/response/event 셋의 JSON-RPC.
- 변수 트리는
variables재귀 호출로 지연 로드. - Cortex-M은 VSCode Cortex-Debug 익스텐션이 자동으로 OpenOCD/J-Link까지 묶어 줌. SVD/RTT 통합.
#다음 장 예고
Ch 11(시리즈 마지막) — 실전 팁. STL pretty-printer, -O2로 빌드된 코드 디버깅, .gdbinit 추천 설정, time-travel(rr)으로 마무리.
#관련 항목
- Ch 8: 원격 디버깅 — Cortex-Debug 익스텐션이 OpenOCD를 띄움
- Ch 9: Python 스크립팅 — dashboard도 Python 확장
- DAP 명세
- cgdb 공식
- gdb-dashboard
- pwndbg
- Cortex-Debug
- nvim-dap
GDB and LLDB · 10 of 12
- 1 GDB vs LLDB 분석 — 두 디버거의 설치·차이·선택 기준
- 2 GDB·LLDB 기본 명령 — break·step·next·print 동작 비교
- 3 디버거로 상태 들여다보기 — 변수·메모리·레지스터·STL 추적
- 4 GDB·LLDB Backtrace와 프레임 이동 — Call Stack 분석
- 5 Breakpoint와 Watchpoint 분석 — Conditional·Hardware·Catchpoint
- 6 멀티스레드·멀티프로세스 디버깅 — Non-Stop·Scheduler-Locking·Fork
- 7 Core Dump 분석 기법 — gcore·coredumpctl·디버거 활용
- 8 GDB 원격 디버깅 — gdbserver·OpenOCD·J-Link 통합
- 9 GDB·LLDB Python 스크립팅 — Pretty-Printer·Custom Command
- 10 GDB·LLDB TUI와 프런트엔드 — gdb-dashboard·gef·pwndbg·VS Code
- 11 GDB·LLDB 실전 팁 — STL·최적화 코드·시간 역행 디버깅
- 12 DWARF 디버그 정보 — 디버거가 변수와 라인을 찾는 방식
관련 글
DWARF 디버그 정보 — 디버거가 변수와 라인을 찾는 방식
DWARF 표준, DIE / abbrev / line / location, expression VM, CFI, split-DWARF.
같은 시리즈에서 이어 읽기
GDB·LLDB 실전 팁 — STL·최적화 코드·시간 역행 디버깅
STL pretty-printers, -O2 디버깅, .gdbinit 추천, rr time-travel. 시리즈 마무리.
같은 시리즈에서 이어 읽기
GDB·LLDB Python 스크립팅 — Pretty-Printer·Custom Command
GDB / LLDB Python API. pretty-printer 작성, 커스텀 명령, 자동화, MI.
같은 시리즈에서 이어 읽기