Egloos | Log-in
F/OSS study
F/OSS study
[Linux] 커널 디버거 사용법 (1) - kgdb
Linux: 2.6.38-rc6
arch: x86_64
gdb: 7.1
qemu: 0.12.3


현재 리눅스 커널에는 두 종류의 커널 디버거가 포함되어 있다.
(아직 모든 아키텍처에 포팅된 것은 아니므로 실제 이용하기 전에 확인이 필요할 것이다.)
하나는 2.6.26 버전에서 포함된 kgdb로, gdb를 통해 원격으로 디버깅이 가능하며
gdb의 강력한 기능을 마음껏 이용할 수 있지만 디버깅을 위한 별도의 머신이 필요하다.

또 하나는 2.6.35 버전에서 포함된 kdb로, 자체적인 shell 기능을 구현하고 있어
별도의 머신이 필요는 없지만 소스 레벨 디버깅이 불가능하다는 제한 사항이 있다.

먼저 kgdb를 사용하는 방법을 간단히 살펴보도록 하자.
여기서는 임베디드 장치/보드를 디버깅하는 상황을 가정하고 설명할 것이지만
실제 장치가 아닌 qemu를 통해 우리가 디버깅하고자 하는 장치를 emulate하고
데스크탑에서 gdb를 실행하여 장치 (qemu)에 연결할 것이다.

설명을 단순하게 하기 위해 타겟 장치도 x86_64 머신인 경우를 다루기로 한다.
qemu는 별도의 디스크 이미지가 필요없이 직접 커널 이미지를 통해 부팅할 수 있는 옵션을 제공하므로
이를 이용하기로 할 것이다. (물론 제대로 동작하려면 root filesystem image가 있어야 한다.)
kgdb를 이용하기 위해서는 커널 설정 시 다음과 같은 옵션을 선택해야 한다.
  • CONFIG_DEBUG_INFO=y
  • # CONFIG_DEBUG_RODATA is not set
  • CONFIG_FRAME_POINTER=y
  • CONFIG_KGDB=y
  • CONFIG_KGDB_SERIAL_CONSOLE=y
이렇게 빌드한 커널 이미지의 이름이 vmlinuz-2.6.38-rc6라고 가정하자.
(이는 x86 아키텍처의 경우 arch/x86/boot/bzImage 파일과 동일한 파일이다.)
여기서는 장치/보드 상에 console 장치가 별도로 존재하는 상황을 가정할 것이다.
만일 그렇지 못한 상황이라면 - 개발 중인 시스템에 serial port가 하나 만 존재하고
그것이 console로 쓰이고 있다면 - 뒤에서 설명할 kdb 및 kgdb/kdb 연동에 대한 내용을 살펴보기 바란다.

console과 kgdb가 같은 serial terminal에 사용되는 경우 gdb가 터미널에 대한 제어를 가지기 때문에
console을 이용할 수가 없게 된다. 이 경우에도 부팅 시 커널 옵션에 kgdbcon을 추가하면
printk 메시지를 볼 수는 있는 것 같지만, 권장하는 방법은 아니다.

따라서 디버깅하려는 장치/보드 (여기서는 qemu)와 실제로 gdb가 동작할 머신 (데스크탑)을 연결하기 위해
보드 상에 별도의 serial terminal 장치가 필요하며 qemu에서는 다음과 같이 이용할 수 있다.

$ qemu-system-x86_64 -kernel vmlinuz-2.6.38-rc6 -append "kgdboc=ttyS0,115200 kgdbwait" -serial pty

간단히 qemu의 옵션에 대해서 설명하면 -kernel은 (당연히도) 실행하고자 하는 커널 이미지를 나타내며
-append는 커널에 넘겨주는 부트 옵션을 지정할 수 있다. -serial은 qemu에서 사용하는 serial 장치를
지정하는 옵션인데 리눅스의 경우 pty라는 값을 설정하여 가상 터미널 장치를 이용할 수 있다.
위와 같이 실행한 경우 다음과 같은 출력을 통해 /dev/pts/3이라는 가상 터미널 장치가 생성되었음을 알 수 있다.

char device redirected to /dev/pts/3

-append로 지정된 커널 옵션의 경우 kgdb 실행에 직접 관련된 것인데
kgdboc는 kgdb over console의 약자로 serial terminal 장치를 통해 kgdb를 사용할 것임을 말하고
해당 장치의 이름과 baud rate을 지정한 것이다. kgdbwait는 kgdb 설정이 완료된 즉시 커널을 멈추고
원격 머신에서 gdb가 연결될 때까지 기다리라는 의미이다.

실제로 이렇게 최초 breakpoint가 실행되는 시점은 커널 초기화가 거의 끝나는 시점
즉, start_kernel() 함수의 제일 마지막에서 rest_init() 함수를 호출하고
kernel_init 스레드를 만들어서 초기화를 마친 후 사용자 모드의 init 프로세스로 exec 하기 직전이다.
따라서 부트 단계에서의 문제를 디버깅하기 위해서는 kgdb를 이용할 수 없을 것이다.

아무튼 이렇게 커널이 kgdb를 기다리고 있는 상태가 되면 gdb를 통해 연결할 수 있다.
gdb가 소스 레벨에서 디버깅을 하려면 심볼 정보가 필요한데
이는 커널 빌드 시 생성되는 vmlinux (마지막이 'x' 임에 주의하자!) 파일에서 찾을 수 있다.

$ gdb -q vmlinux
Reading symbols from /home/namhyung/build/linux/vmlinux...done.
(gdb) set remotebaud 115200
(gdb) target remote /dev/pts/3
Remote debugging using /dev/pts/3
kgdb_breakpoint (new_dbg_io_ops=0xffffffff8181c000)
    at /home/namhyung/project/linux/kernel/debug/debug_core.c:960
960        wmb(); /- Sync point after breakpoint *-
(gdb)

이렇게 remotebaud를 설정한 뒤 qemu가 생성한 가상 터미널 장치를 remote target으로 지정하면
연결이 이루어진다. (위에서 (gdb) 라고 표시된 줄의 내용 만을 입력하면 된다.)
이 후에는 일반적으로 gdb를 이용하듯이 breakpoint를 설정하는 등의 다른 작업을 할 수 있다.

이제 gdb에서 c (continue) 명령을 수행하면 커널이 실행을 계속한다.
위와 같이 실행한 경우라면 root filesystem을 mount하지 못해 panic이 발생하고
다시 gdb로 제어가 넘어오는 것을 볼 수 있다.

Program received signal SIGSEGV, Segmentation fault.
native_stop_other_cpus (wait=0) at /home/namhyung/project/linux/arch/x86/kernel/smp.c:194
194    }
(gdb) bt
#0  native_stop_other_cpus (wait=0) at /home/namhyung/project/linux/arch/x86/kernel/smp.c:194
#1  0xffffffff814f502e in smp_send_stop (
    fmt=0xffffffff816e7203 "VFS: Unable to mount root fs on %s")
    at /home/namhyung/project/linux/arch/x86/include/asm/smp.h:73
#2  panic (fmt=0xffffffff816e7203 "VFS: Unable to mount root fs on %s")
    at /home/namhyung/project/linux/kernel/panic.c:98
#3  0xffffffff81a9a1b2 in mount_block_root (name=0xffffffff816e711c "/dev/root", flags=32769)
    at /home/namhyung/project/linux/init/do_mounts.c:356
#4  0xffffffff81a9a21f in mount_root () at /home/namhyung/project/linux/init/do_mounts.c:429
#5  0xffffffff81a9a393 in prepare_namespace ()
    at /home/namhyung/project/linux/init/do_mounts.c:487
#6  0xffffffff81a9972b in kernel_init (unused=<value optimized out>)
    at /home/namhyung/project/linux/init/main.c:916
#7  0xffffffff81003914 in ?? () at /home/namhyung/project/linux/arch/x86/kernel/entry_64.S:1156
#8  0x0000000000000000 in ?? ()
(gdb)

정상적으로 실행하려면 root filesystem이 필요한데, busybox 등을 이용해 직접 만들어도 상관없지만
http://fs.devloop.org.uk/ 에 가보면 미리 만들어진 (x86 용) 이미지를 다운로드 받을 수 있다.
여기서 사용할 root filesystem image의 이름은 rootfs.img라고 할 것이다.
root filesystem을 포함한 정상적인 qemu 명령은 다음과 같을 것이다.

$ qemu-system-x86_64 -kernel vmlinuz-2.6.38-rc6 -hda rootfs.img -serial pty \
-append "kgdboc=ttyS0,115200 kgdbwait root=/dev/sda"

부팅 후 gdb를 연결하고 c 명령을 실행하거나 커널 부트 옵션에서 kgdbwait를 제거한 경우
정상적으로 시스템이 실행되므로 더이상 kgdb에서 시스템을 제어할 수 없게 된다.
다시 강제로 kgdb에게 제어를 넘기려면 magic sysrq + g 키를 누르거나 다음과 같이 할 수 있다.
(이 기능을 사용하려면 CONFIG_MAGIC_SYSRQ 옵션이 선택되어 있어야 한다.
물론 breakpoint에 도달하거나 커널 패닉이 발생하면 자동으로 kgdb에게 제어가 넘어온다.)

qemu# echo g > /proc/sysrq-trigger

(qemu# 이라는 프롬프트는 이 명령을 디버깅하려는 장치/보드 (qemu)의 콘솔 상에서
root 권한으로 실행해야 함을 뜻한다.)

혹은 이와 같은 과정이 번거롭다면 자주 사용되지 않는 시스템 콜 서비스 루틴에 breakpoint를 걸어두고
해당 시스템 콜을 이용하는 프로세스를 실행하여 kgdb에게 제어를 넘겨줄 수도 있다.
이를 위해 사용할 만한 것이 바로 (모든 dirty-page를 디스크에 쓰도록 하는) sync() 시스템 콜이다.

(gdb) break sys_sync
Breakpoint 1 at 0xffffffff8112a892: file /home/namhyung/project/linux/fs/sync.c, line 100.
(gdb)
(gdb) cont
Continuing.

이제 콘솔에서 원하는 작업을 수행하다가 디버깅이 필요한 경우 sync 프로그램을 실행하면
sync() 시스템 콜을 호출하여 kgdb로 제어가 넘어가게 된다.

qemu# sync

gdb 상에서는 다음과 같이 출력된 후 다시 프롬프트가 표시되는 것을 볼 수 있다.

[New Thread 56]
[Switching to Thread 56]

Breakpoint 1, sys_sync () at /home/namhyung/project/linux/fs/sync.c:100
100        wakeup_flusher_threads(0);
(gdb)


=== 참조 문헌 ===
by namhyung | 2011/03/01 22:33 | Kernel | 트랙백(1) | 덧글(5)
트랙백 주소 : http://studyfoss.egloos.com/tb/5490783
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from F/OSS study at 2011/03/02 13:37

제목 : [Linux] 커널 디버거 사용법 (2) - kdb
Linux: 2.6.38-rc6 arch: x86_64 qemu: 0.12.3 minicom: 2.4 이전 글 보기: [Linux] 커널 디버거 사용법 (1) - kgdb 이제 kdb에 대해서도 살펴보기로 하자. kdb는 내부적으로 kgdb와 동일한 루틴들을 이용하므로 상호연동이 가능하며 kdb를 이용하기 위해서는 커널 설정 시 다음과 같은 옵션을 더 추가해야 한다. * CONFIG_KGDB_KDB=y 기본적으로......more

Commented by namhyung at 2011/03/02 16:57
커널 설정 옵션 중에 CONFIG_DEBUG_INFO 옵션이 빠져 있어서 추가하였습니다.
Commented by kgdb at 2012/01/13 11:28
질문이 있습니다.
qemu로 가상화 돌리기에
gdb ./vmlinux 하는 서버는 같은 서버에서 하는것 같은데..
target remote /dev/pts/3 하게 되면
Ignoring packet error, continuing... 반복하다가 끝나버리네요..
이유를 모르겠네요..ㅜㅜ
Commented by namhyung at 2012/02/03 09:51
안녕하세요.. 답변이 늦었습니다만..
gdb는 호스트 머신에서 실행하는 것이 맞구요.. qemu 실행 시
"char device redirected to /dev/pts/3" 라는 메시지를 확인하셨다면
커널 옵션 설정이나 부트 옵션 등을 다시 확인해 보시기 바랍니다.
Commented by song0319 at 2012/05/16 14:52
잘보고 갑니다 우분투랑 안드로이드 KGDB 붙이는데 고생했내요 이 자료 없었으면 더 고생했을듯
Commented by namhyung at 2012/05/25 10:19
도움이 되셨다니 다행입니다.

:         :

:

비공개 덧글

◀ 이전 페이지 다음 페이지 ▶

카테고리
General
Application
System
Kernel
Book
Tips
태그
SMP linux documentation computer-architecture script scm kernel CARM gcc sed C elf compiler binutils block-layer emacs bash algorithm glibc perf git patch memory CAaQA3 blktrace x86 awk synchronization vcs build
전체보기
이글루 파인더

최근 등록된 덧글
1번은 call-stack 금방 확인이 ..
by 혁 at 11/13
해당 문법에 대한 자세한 가이드 같..
by ㅇㄷㅎ at 11/07
perf에 대한 설명 감사드립니다! ..
by flavono123 at 10/24
최근 등록된 트랙백
Tod's Ferrari Homme
by Tods Pas Cher,Kodak did ..
Mocassin Femme
by Mocassins Homme, I got so..
natural garcinia cambogia
by
rss

skin by jiinny


X