- Published on
커널을 관측하고 디버깅하기 — printk, ftrace, kprobes, 그리고 QEMU와 GDB
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며 — 멈춰 세울 수 없는 대상을 디버깅하기
- 1. printk와 dmesg — 모두가 실제로 하는 것
- 2. oops와 panic 읽기
- 3. /proc과 /sys — 커널 자신의 인터페이스
- 4. ftrace — 커널에 내장된 추적기
- 5. kprobes — 임의 함수에 동적으로 붙기
- 6. perf — 전체 그림 잡기
- 7. eBPF가 놓이는 자리
- 8. QEMU와 GDB — 진짜 중단점이 필요할 때
- 흔한 함정
- 마치며
- 직접 해보기
- 이전 편과 다음 편
- 참고 자료
들어가며 — 멈춰 세울 수 없는 대상을 디버깅하기
5편에서 모듈을 만들고 dmesg로 확인했습니다. 그게 사실상 첫 번째 디버깅이었습니다. 이 글은 그 위에 나머지 도구를 얹습니다.
커널 디버깅이 사용자 공간과 다른 지점은 하나입니다. 디버거로 멈춰 세우면 시스템 전체가 멈춥니다. 응용 프로그램이면 중단점에서 멈춰 놓고 천천히 들여다보면 되지만, 커널을 멈추면 디스크도 네트워크도 타이머도 멈춥니다. 원격 서버라면 접속도 끊깁니다. 그래서 커널 관측 도구는 멈추지 않고 들여다보는 방향으로 발달했습니다.
이 블로그에는 관측 도구를 다루는 글이 이미 여럿 있습니다. eBPF 기초, bpftrace 실전, 리눅스 성능 도구 가이드가 그것입니다. 그 글들은 운영 중인 시스템의 상태를 파악하는 것이 목적입니다. 이 글의 목적은 다릅니다. 커널 코드를 이해하고 내가 쓴 커널 코드를 고치는 것이 목적입니다. 같은 도구를 쓰지만 묻는 질문이 다릅니다. 앞의 글들이 "이 서버가 왜 느린가"를 묻는다면, 이 글은 "내가 읽은 그 함수가 정말 그렇게 실행되는가"를 묻습니다.
그래서 eBPF는 이 글에서 자리만 잡아 주고 넘어가겠습니다. 이미 세 편이 다루고 있으니 다시 가르치지 않습니다.
1. printk와 dmesg — 모두가 실제로 하는 것
먼저 정직하게 말하겠습니다. 이 글에 나오는 모든 도구를 다 배워도, 실제로 가장 많이 쓰게 되는 것은 로그 출력입니다. 제 관찰이자 판단이지만, 커널 코드를 고치는 사람들이 함수 안에 로그를 하나 넣고 다시 빌드해 보는 것은 아주 흔한 방식입니다.
부끄러워할 일이 아닙니다. 로그 출력이 좋은 이유가 실제로 있습니다.
- 어떤 환경에서든 됩니다. 디버거 연결도 특별한 설정도 필요 없습니다.
- 타이밍에 영향을 덜 줍니다. 중단점처럼 실행을 멈추지 않습니다.
- 결과가 남습니다. 재현이 어려운 문제에서 특히 중요합니다.
5편 4절에서 다룬 pr_ 계열 매크로가 그 도구입니다. 여기서는 실전 요령만 덧붙이겠습니다.
# 실시간으로 따라가기 — 실습 중에는 이 창을 띄워 둡니다
sudo dmesg -wH
# 링 버퍼 비우기 — 실험 전에 깨끗하게
sudo dmesg -C
# 콘솔로 모든 레벨 내보내기
sudo dmesg -n 8
printk의 한계도 알아 두어야 합니다. 커널 문서의 디버깅 안내는 코드 실행 타이밍이 결과를 좌우하는 상황에서는 디버깅 목적의 단순한 printk 호출이 동작하지 않을 수 있다고 지적하고, 대안으로 trace_printk를 제시합니다. 이 함수는 메시지를 커널 로그가 아니라 추적 버퍼에 남기기 때문에 훨씬 가볍습니다. 인터럽트 처리기처럼 매우 자주 실행되는 경로에 로그를 넣어야 한다면 이쪽을 쓰세요. 다만 trace_printk가 들어간 코드는 그대로 패치로 보내면 안 됩니다. 디버깅용이지 제품용이 아닙니다.
2. oops와 panic 읽기
커널이 죽으면 화면이나 dmesg에 진단 덤프가 남습니다. 처음 보면 암호 같지만 읽는 순서가 정해져 있습니다.
- 첫 줄의 오류 종류. 널 포인터 역참조인지, 잘못된 주소 접근인지,
BUG_ON에 걸린 것인지. RIP줄. 문제가 난 명령의 위치입니다. 함수 이름과 오프셋이 함께 나옵니다.Tainted:표시. 5편 7절에서 다룬 오염 플래그입니다. 여기에O가 있으면 트리 밖 모듈이 올라와 있다는 뜻이고, 그 모듈이 용의자입니다.Call Trace:아래의 스택. 위에서부터 최근 호출입니다.Modules linked in:— 로드된 모듈 목록입니다.
주소만 나오고 함수 이름이 안 나온다면 커널 트리 안의 스크립트가 해결해 줍니다.
# 스택 트레이스를 소스 파일과 줄 번호로 변환
./scripts/decode_stacktrace.sh vmlinux < oops.txt
# 함수 이름과 오프셋을 소스 위치로 변환
./scripts/faddr2line vmlinux vfs_read+0x42
두 스크립트 모두 vmlinux가 필요합니다. 3편 1절에서 만들어진 그 파일입니다. 디버그 심볼을 끄고 빌드했다면 결과가 부실하니, 디버깅 목적의 커널은 심볼을 켜 두는 편이 낫습니다.
BUG_ON과 WARN_ON의 차이도 알아 두면 좋습니다. 전자는 조건이 참이면 커널을 죽이고, 후자는 경고 덤프만 남기고 계속 진행합니다. 자기 코드에 검사를 넣을 때는 대부분 WARN_ON 쪽이 맞습니다. 시스템을 죽여야 할 만큼 심각한 상황은 생각보다 드뭅니다.
3. /proc과 /sys — 커널 자신의 인터페이스
별도 도구 없이 커널에게 직접 물어볼 수 있는 것이 꽤 많습니다.
| 경로 | 무엇을 알려 주는가 |
|---|---|
/proc/kallsyms | 커널 심볼과 주소 전체 목록 |
/proc/modules | 로드된 모듈 (lsmod가 읽는 원본) |
/proc/sys/ | sysctl로 조정 가능한 항목 전부 |
/proc/interrupts | 인터럽트별 CPU 처리 횟수 |
/proc/slabinfo | 슬랩 할당기 상태 |
/proc/PID/stack | 그 프로세스의 커널 스택 |
/proc/PID/status | 프로세스 상태 요약 |
/sys/kernel/debug/ | debugfs. 각종 디버그 인터페이스 |
/sys/kernel/tracing/ | tracefs. 다음 절의 주제 |
/proc/kallsyms는 특히 유용합니다. 어떤 함수가 이 커널에 실제로 존재하는지, 모듈이 심볼을 제대로 등록했는지 확인할 수 있습니다.
grep ' vfs_read$' /proc/kallsyms
grep hello /proc/kallsyms # 내가 올린 모듈의 심볼
주소가 전부 0으로 나온다면 권한 문제이거나 커널 주소 노출 제한 때문입니다. 커널 문서의 sysctl 안내가 설명하듯 kptr_restrict가 이 동작을 통제합니다. 기본값 0에서는 주소가 해시되어 출력되고, 1로 두면 특정 포맷으로 출력되는 커널 포인터가 권한 없는 사용자에게는 0으로 대체됩니다. 실습 중에 실제 주소가 필요하면 루트로 실행하고 이 값을 조정해야 합니다. 물론 프로덕션 서버에서 함부로 건드릴 값은 아닙니다.
/proc/PID/stack도 기억해 둘 만합니다. 어떤 프로세스가 D 상태로 멈춰 있을 때, 커널의 어느 함수에서 기다리고 있는지 바로 보여 줍니다. 로드 애버리지 글에서 다룬 D 상태 태스크의 정체를 확인하는 가장 직접적인 방법입니다.
4. ftrace — 커널에 내장된 추적기
ftrace는 커널 안에 들어 있는 함수 추적기입니다. 추가 설치가 필요 없고, 파일에 문자열을 쓰는 것만으로 제어됩니다.
어디에 있는가
커널 문서 Documentation/trace/ftrace.rst가 표준 위치를 /sys/kernel/tracing으로 명시합니다. 마운트되어 있지 않다면 이렇게 합니다.
sudo mount -t tracefs nodev /sys/kernel/tracing
버전 차이를 하나 짚어야 합니다. 같은 문서에 따르면 4.1 이전에는 이 제어 파일들이 debugfs 안에 있었고 위치가 /sys/kernel/debug/tracing이었습니다. 지금도 debugfs가 마운트되면 호환을 위해 그 경로에도 자동으로 붙습니다. 인터넷 자료마다 경로가 다른 이유가 이것입니다. 둘 다 같은 것을 가리킵니다.
핵심 파일들
문서가 설명하는 주요 파일을 정리하면 이렇습니다.
| 파일 | 역할 |
|---|---|
available_tracers | 이 커널에 컴파일된 추적기 목록 |
current_tracer | 지금 설정된 추적기. 여기에 이름을 써서 바꿉니다 |
tracing_on | 링 버퍼 기록 켜기와 끄기 |
trace | 사람이 읽을 수 있는 형태의 추적 결과 |
trace_pipe | 실시간 스트리밍용. 새 데이터가 올 때까지 블록됩니다 |
set_ftrace_filter | 특정 함수만 추적하도록 제한 |
available_filter_functions | 필터에 넣을 수 있는 함수 목록 |
set_graph_function | 호출 그래프 추적을 시작할 함수 |
추적기 이름 중 자주 쓰는 것은 셋입니다. function은 모든 커널 함수 호출을 기록하고, function_graph는 함수의 진입과 반환을 함께 기록해 호출 그래프를 그려 주며, nop은 아무것도 추적하지 않는 상태로 되돌립니다.
실전 — 함수 하나의 호출 그래프 보기
4편에서 읽은 경로를 실제로 확인해 봅시다.
cd /sys/kernel/tracing
# 깨끗하게 시작
echo nop > current_tracer
echo > trace
# vfs_read에서 시작하는 호출 그래프만
echo function_graph > current_tracer
echo vfs_read > set_graph_function
echo 1 > tracing_on
cat /etc/hostname > /dev/null
echo 0 > tracing_on
head -50 trace
출력에는 vfs_read 아래로 어떤 함수들이 호출됐는지가 들여쓰기와 소요 시간과 함께 나옵니다. 4편 5절에서 본 함수 포인터 분기가 실제로 어느 구현으로 갔는지가 여기서 드러납니다.
정리를 잊지 마세요. 추적을 켜 두면 오버헤드가 계속 발생합니다.
echo nop > current_tracer
echo > set_graph_function
echo > trace
필터로 범위 좁히기
아무 필터 없이 function 추적기를 켜면 초당 수십만 건이 쏟아져 버퍼가 금세 덮어써집니다. 필터가 사실상 필수입니다.
# 이름 패턴으로 제한
echo 'vfs_*' > set_ftrace_filter
# 여러 개 추가
echo 'ksys_read' >> set_ftrace_filter
# 필터 확인과 해제
cat set_ftrace_filter
echo > set_ftrace_filter
available_filter_functions에 없는 이름은 필터에 넣어도 동작하지 않습니다. 인라인되었거나 추적 대상에서 빠진 함수입니다. 무언가 안 잡힐 때 이 파일을 먼저 확인하세요.
trace-cmd
파일을 직접 다루는 것이 번거로우면 trace-cmd가 같은 일을 명령줄로 감싸 줍니다.
sudo trace-cmd record -p function_graph -g vfs_read -- cat /etc/hostname
sudo trace-cmd report | head -50
결과가 파일로 저장되므로 나중에 분석하거나 다른 장비로 옮기기 좋습니다.
5. kprobes — 임의 함수에 동적으로 붙기
ftrace는 추적 지점이 미리 준비된 함수만 볼 수 있습니다. kprobes는 그 제약이 없습니다. 커널 문서 Documentation/trace/kprobes.rst의 표현대로, 거의 모든 커널 루틴에 끼어들어 디버깅과 성능 정보를 방해 없이 수집할 수 있습니다.
필요한 설정
문서가 요구하는 항목들입니다.
| 설정 | 필요한 이유 |
|---|---|
CONFIG_KPROBES | 기능 자체 |
CONFIG_KALLSYMS | 이름으로 주소를 찾기 위해 |
CONFIG_MODULES, CONFIG_MODULE_UNLOAD | 프로브 모듈을 올리고 내리기 위해 |
CONFIG_KPROBE_EVENTS | 아래의 파일 인터페이스용 |
CONFIG_OPTPROBES | 점프 최적화. x86에서는 자동으로 켜집니다 |
파일 인터페이스로 쓰기
모듈을 만들지 않고도 쓸 수 있습니다. 커널 문서 Documentation/trace/kprobetrace.rst가 설명하는 방식입니다.
cd /sys/kernel/tracing
# vfs_read에 프로브 하나 심기
echo 'p:myprobe vfs_read' > kprobe_events
# 켜기
echo 1 > events/kprobes/myprobe/enable
echo 1 > tracing_on
cat /etc/hostname > /dev/null
# 결과 보기
head -20 trace
# 정리
echo 0 > events/kprobes/myprobe/enable
echo > kprobe_events
문법에서 알아야 할 것은 앞 글자입니다. p는 함수 진입에 심는 일반 프로브, r은 반환 시점에 동작하는 반환 프로브입니다. 인자도 뽑아낼 수 있는데, 레지스터는 % 접두사로, 메모리는 @ 접두사로 지정합니다.
모듈로 쓰기
정교한 처리가 필요하면 5편에서 배운 모듈로 만듭니다. 문서가 설명하는 API의 핵심은 이렇습니다.
- 등록은
register_kprobe, 해제는unregister_kprobe - 반환 프로브는
register_kretprobe와unregister_kretprobe - 구조체의
pre_handler는 프로브 지점에 도달했을 때,post_handler는 해당 명령을 단일 실행한 뒤에 호출됩니다
samples/kprobes/ 아래에 동작하는 예제가 들어 있습니다. 처음부터 쓰지 말고 이걸 고쳐 쓰는 편이 빠릅니다.
프로브를 심을 수 없는 함수들
문서가 설명하듯 kprobes는 블랙리스트를 유지합니다. 프로브를 심으면 재귀적 트랩 같은 문제를 일으킬 수 있는 함수들이 여기 등록됩니다. 자기 코드에 이 표시를 붙이려면 NOKPROBE_SYMBOL() 매크로를 씁니다.
지금 걸려 있는 프로브 목록과 전역 스위치는 debugfs에 있습니다.
sudo cat /sys/kernel/debug/kprobes/list
sudo cat /sys/kernel/debug/kprobes/enabled
목록에는 [OPTIMIZED], [DISABLED], [GONE] 같은 상태 표시가 함께 나옵니다.
6. perf — 전체 그림 잡기
ftrace와 kprobes가 "이 함수를 보겠다"는 도구라면, perf는 "무엇이 CPU를 쓰는지 모르겠다"에 답하는 도구입니다. 샘플링 기반이라 어디를 봐야 할지 모를 때 출발점이 됩니다.
# 지금 CPU를 쓰는 커널 함수 실시간 순위
sudo perf top
# 특정 명령 실행 동안의 프로파일 수집
sudo perf record -a -g -- sleep 5
sudo perf report
# 시스템 콜 추적 — strace보다 훨씬 가볍습니다
sudo perf trace -- cat /etc/hostname
# 커널 함수에 동적 추적점 만들기 (내부적으로 kprobes를 씁니다)
sudo perf probe --add vfs_read
sudo perf record -e probe:vfs_read -a -- sleep 5
sudo perf probe --del vfs_read
권한 문제가 자주 걸립니다. 커널 문서의 sysctl 안내에 따르면 perf_event_paranoid의 기본값은 2이고, 값에 따라 권한 없는 사용자에게 허용되는 범위가 달라집니다. -1이면 거의 모든 이벤트를 허용하고, 0 이상이면 ftrace 함수 추적점과 원시 추적점 접근이 제한되며, 1 이상이면 CPU 이벤트 접근이, 2 이상이면 커널 프로파일링이 제한됩니다.
실습 환경에서는 이렇게 열어 둘 수 있습니다.
sudo sysctl kernel.perf_event_paranoid=-1
프로덕션에서는 하면 안 되는 설정입니다. 자세한 활용은 리눅스 성능 도구 가이드를 참고하세요.
7. eBPF가 놓이는 자리
eBPF는 이 지형에서 가장 강력한 도구지만, 이 시리즈의 관점에서는 읽고 있는 코드를 확인하는 용도보다 운영 중인 시스템을 관찰하는 용도에 훨씬 잘 맞습니다.
정리하면 이렇습니다.
| 도구 | 잘하는 일 | 이 시리즈에서의 쓰임 |
|---|---|---|
printk | 어디서든, 확실하게 | 내가 쓴 코드가 실행되는지 확인 |
| ftrace | 호출 그래프, 저비용 | 읽은 경로가 맞는지 검증 |
| kprobes | 임의 함수, 인자 추출 | 특정 함수의 인자값 확인 |
| perf | 샘플링, 전체 그림 | 어디를 볼지 모를 때 출발점 |
| eBPF | 집계와 가공, 안전성 | 운영 시스템 관찰 |
| GDB | 진짜 중단점, 변수 조사 | 논리 오류를 단계 실행으로 추적 |
eBPF의 원리와 실전 활용은 이미 세 편이 다루고 있습니다. 기초, 관측성, 런타임 보안 순으로 읽으면 됩니다.
한 가지만 덧붙이자면, eBPF는 검증기를 통과해야 하는 대신 안전하고, 모듈은 검증기가 없는 대신 제약이 없습니다. 커널 코드를 배우는 입장에서는 두 쪽을 다 해 보는 것이 이해에 도움이 됩니다.
8. QEMU와 GDB — 진짜 중단점이 필요할 때
지금까지의 도구는 전부 "멈추지 않고 보는" 것이었습니다. 논리 오류를 잡으려면 결국 멈춰 세우고 변수를 들여다봐야 할 때가 옵니다. 3편에서 QEMU 환경을 만들어 둔 이유가 여기 있습니다.
커널 설정
커널 문서 Documentation/process/debugging/gdb-kernel-debugging.rst가 요구하는 항목들입니다.
| 설정 | 상태 |
|---|---|
CONFIG_GDB_SCRIPTS | 켜기 |
CONFIG_DEBUG_INFO_REDUCED | 끄기 |
CONFIG_FRAME_POINTER | 지원되면 켜기 |
CONFIG_RANDOMIZE_BASE | KASLR이 있으면 끄기 |
CONFIG_RANDOMIZE_BASE를 끄는 이유는 커널 주소가 부팅마다 달라지면 디버거가 심볼을 맞추기 어렵기 때문입니다.
빌드 후에 스크립트를 준비합니다. 문서에 따르면 5.1 이상에서는 이 명령이 필요합니다.
make scripts_gdb
이러면 트리 최상위에 vmlinux-gdb.py가 생기고, GDB가 vmlinux를 열 때 자동으로 읽습니다. 자동 로드가 거부되면 홈 디렉터리의 gdb 설정에 안전 경로를 추가해야 한다고 문서가 안내합니다.
연결
문서가 설명하는 두 가지 방법이 있습니다. QEMU 명령줄에 -s를 붙이거나, 실행 중에 QEMU 모니터에서 gdbserver를 시작하는 것입니다. 앞쪽이 간단합니다.
# 터미널 1 — 부팅과 동시에 멈춰 세우려면 -S도 함께
qemu-system-x86_64 \
-kernel arch/x86/boot/bzImage \
-initrd initramfs.cpio.gz \
-append "console=ttyS0 nokaslr" \
-m 2G -nographic -s
# 터미널 2
gdb vmlinux
(gdb) target remote :1234
(gdb) lx-symbols
(gdb) break vfs_read
(gdb) continue
nokaslr은 커널 명령줄에서 주소 무작위화를 끄는 파라미터입니다. 설정에서 껐다면 없어도 되지만 넣어 두면 확실합니다.
커널이 제공하는 GDB 명령들
vmlinux-gdb.py가 커널 자료구조를 이해하는 명령을 추가해 줍니다. 문서가 나열하는 것들입니다.
| 명령 | 하는 일 |
|---|---|
lx-symbols | 커널과 모듈의 심볼을 읽어 들입니다 |
lx-dmesg | 커널 로그 버퍼를 보여 줍니다 |
lx-lsmod | 로드된 모듈 목록 |
apropos lx | 사용할 수 있는 lx 명령 전체 검색 |
편의 함수도 있습니다.
| 함수 | 하는 일 |
|---|---|
$lx_current() | 현재 태스크 |
$lx_per_cpu() | per-CPU 변수 접근 |
$lx_task_by_pid() | PID로 태스크 찾기 |
$container_of() | 커널 자료구조 탐색 보조 |
lx-dmesg가 특히 유용합니다. 커널이 멈춰 버려 콘솔 출력이 안 나오는 상황에서도 로그 버퍼를 읽어 낼 수 있기 때문입니다. 시스템이 죽은 직후의 마지막 메시지를 확인하는 가장 확실한 방법입니다.
lx-symbols를 잊지 마세요. 이걸 실행하지 않으면 모듈 안의 심볼이 보이지 않아서, 5편에서 만든 모듈에 중단점을 걸 수 없습니다.
물리 장비라면 KGDB
가상 머신이 아니라 실제 하드웨어를 디버깅해야 한다면 커널 자체에 들어 있는 KGDB를 씁니다. 문서 Documentation/process/debugging/kgdb.rst가 요구하는 설정입니다.
| 설정 | 역할 |
|---|---|
CONFIG_KGDB | KGDB 커널 디버거 |
CONFIG_KGDB_SERIAL_CONSOLE | 시리얼 콘솔 지원 |
CONFIG_KGDB_KDB | 커널 내장 셸 프론트엔드 |
CONFIG_MAGIC_SYSRQ | 수동으로 디버거에 진입하기 위해 |
CONFIG_DEBUG_INFO | 심볼 정보 |
CONFIG_FRAME_POINTER | 스택 되감기 |
부팅 파라미터로 연결 포트를 지정합니다.
kgdboc=ttyS0,115200 kgdbwait
kgdbwait는 부팅 초기에 디버거 연결을 기다리게 만듭니다. 부팅 중 문제를 잡을 때 필요합니다. 실행 중에 바꾸려면 sysfs를 씁니다.
echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
디버거 쪽 연결은 이렇습니다.
% gdb ./vmlinux
(gdb) set serial baud 115200
(gdb) target remote /dev/ttyS0
KGDB에는 장비가 두 대 필요합니다. 디버깅 대상과 디버거를 돌릴 장비가 따로 있어야 하고, 둘을 시리얼로 연결해야 합니다. 그래서 학습 단계에서는 QEMU 쪽이 훨씬 현실적입니다.
흔한 함정
tracefs 경로를 못 찾습니다. 4절에서 다룬 대로 커널 버전에 따라 두 경로가 있습니다. 마운트되어 있는지부터 확인하세요.
추적을 켜 두고 잊습니다. 특히 필터 없는 function 추적기는 오버헤드가 상당합니다. 실험이 끝나면 nop으로 되돌리는 것을 습관으로 만드세요.
추적 결과가 비어 있습니다. tracing_on이 0이거나, 필터에 넣은 이름이 available_filter_functions에 없거나, 버퍼가 이미 덮어써진 경우입니다. 세 가지를 순서대로 확인하면 됩니다.
GDB에서 변수를 봤더니 최적화되어 사라졌다고 나옵니다. 커널은 최적화를 켜고 빌드되므로 정상입니다. 어떤 지역 변수는 볼 수 없습니다. 이때는 그 변수를 로그로 찍는 편이 빠릅니다.
lx-symbols를 실행하지 않고 모듈에 중단점을 겁니다. 심볼을 모르니 걸리지 않습니다.
프로덕션 서버에서 kptr_restrict나 perf_event_paranoid를 열어 둡니다. 둘 다 커널 주소와 프로파일 정보 노출에 관한 보안 설정입니다. 실습 뒤에 되돌리세요.
오염된 커널로 재현한 버그를 그대로 보고합니다. 5편에서 다룬 오염 플래그 이야기입니다. 자기 모듈을 뺀 상태에서 재현되는지 먼저 확인하는 것이 예의이자 효율입니다.
마치며
커널 관측 도구를 다 나열하면 압도적으로 보이지만, 실제로 쓰는 순서는 단순합니다.
먼저 dmesg를 봅니다. 아무것도 없으면 printk를 넣어 봅니다. 어디를 봐야 할지 모르겠으면 perf로 전체 그림을 잡습니다. 볼 함수가 정해지면 ftrace로 호출 그래프를 뜹니다. 인자값이 필요하면 kprobes를 붙입니다. 그래도 안 되면 그때 GDB를 꺼냅니다.
GDB가 마지막인 데는 이유가 있습니다. 설정이 가장 번거롭고, 실행을 멈추기 때문에 타이밍에 의존하는 문제는 오히려 사라져 버립니다. 커널 디버깅에서 중단점은 최후의 수단이지 첫 수단이 아닙니다.
그리고 반복하지만, 이 모든 도구를 익힌 뒤에도 printk를 가장 많이 쓰게 될 것입니다. 그건 도구를 제대로 못 배워서가 아니라 그게 실제로 효율적이기 때문입니다.
다음 편은 이 시리즈에서 가장 밀도가 높은 편입니다. 커널 C가 응용 C와 어떻게 다른지, 메모리 할당과 동시성에서 어떤 규칙이 추가되는지를 다룹니다. 5편에서 만든 모듈에 코드를 조금만 더 넣기 시작하면 바로 부딪히게 되는 규칙들입니다.
이 글의 파일 경로, 설정 이름, 명령은 2026년 8월 19일에 공식 문서와 메인라인 트리에서 확인했습니다.
직접 해보기
- eBPF 플레이그라운드 — 7절에서 자리를 잡아 준 도구를 직접 만져 보기
- 리눅스 터미널 — tracefs 조작 명령을 손에 익히기
- 커널 파라미터 탐색기 —
kptr_restrict와perf_event_paranoid같은 설정 둘러보기
이전 편과 다음 편
- 이전 글: 커널 모듈 작성
- 다음 글: 커널 C는 응용 C가 아니다 — 메모리 할당과 동시성의 규칙
참고 자료
모든 링크는 2026년 8월 19일에 확인했습니다.
- ftrace - Function Tracer — tracefs 경로, 제어 파일, 추적기 목록
- Kprobes — 설정 항목, API, 블랙리스트
- Kprobe-based Event Tracing —
kprobe_events문법 - Debugging kernel and modules via gdb — 설정, QEMU 연결, lx 명령
- Using kgdb, kdb and the kernel debugger internals
- General debugging advice —
trace_printk대안 - Documentation for /proc/sys/kernel/ —
kptr_restrict,perf_event_paranoid