dump_stack()
함수는 리눅스 커널 디버깅의 핵심 도구 중 하나로, 콜 스택을 커널 로그로 볼 수 있게 한다. 커널 개발자들은 이 함수를 활용하여 프로그램의 동작을 추적하고 문제 해결을 위한 힌트를 얻을 수 있다. 이 글에서는 dump_stack() 함수에 대해 알아본다.
dump_stack()
함수란?
dump_stack()
함수는 리눅스 커널 소스 코드에 포함된 함수로, 현재 실행 중인 커널 코드에서 스택 추적 정보를 출력하는 데 사용된다. 이 함수는 주로 커널 모듈을 개발하거나 커널 패닉(kernel panic) 상황에서 디버깅 정보를 수집하는 데 활용된다.
dump_stack()
함수 사용법
dump_stack()
함수를 사용하려면 “linux/kernel.h” 헤더파일을 추가해야 한다.
#include <linux/kernel.h>
다음은 dump_stack()
함수의 선언부이다. 인자와 return 값 모두 void로 커널 소스 어디서든 호출하면 된다.
extern asmlinkage void dump_stack(void) __cold;
아래는 arm 아키텍처에서 ‘divide by zero’ 시 호출되는 함수인 __div0
의 예이다.
asmlinkage void __div0(void)
{
pr_err("Division by zero in kernel.\n");
dump_stack();
}
EXPORT_SYMBOL(__div0);
dump_stack()
사용 예제
module insmod 시에 dump_stack()을 호출하는 예제 코드 이다.
#include <linux/init.h>
#include <linux/module.h>
static int __init my_module_init(void) {
printk(KERN_INFO "My Module: Module loaded.\n");
dump_stack();
return 0;
}
static void __exit my_module_exit(void) {
printk(KERN_INFO "My Module: Module unloaded.\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Sample Linux Kernel Module");
다음은 예제 코드를 빌드한 ko파일을 insmod/rmmod 한 결과이다. my_module_init()
에서 dump_stack()
이 호출한 것을 알 수 있다.
[ 325.343118] My Module: Module loaded.
[ 325.343122] CPU: 1 PID: 2073 Comm: insmod Tainted: G OE 5.15.116-local #2
[ 325.343125] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 325.343126] Call Trace:
[ 325.343127] <TASK>
[ 325.343130] dump_stack_lvl+0x4a/0x63
[ 325.343136] ? 0xffffffffc0c68000
[ 325.343138] dump_stack+0x10/0x16
[ 325.343138] my_module_init+0x1a/0x1000 [div0]
[ 325.343140] do_one_initcall+0x49/0x1e0
[ 325.343144] ? kmem_cache_alloc_trace+0x19e/0x2e0
[ 325.343147] do_init_module+0x52/0x260
[ 325.343151] load_module+0x2b48/0x2d20
[ 325.343153] __do_sys_finit_module+0xbf/0x120
[ 325.343154] ? __do_sys_finit_module+0xbf/0x120
[ 325.343155] __x64_sys_finit_module+0x18/0x20
[ 325.343156] do_syscall_64+0x5c/0xc0
[ 325.343159] ? fput+0x13/0x20
[ 325.343161] ? ksys_mmap_pgoff+0x154/0x2c0
[ 325.343163] ? do_syscall_64+0x69/0xc0
[ 325.343164] ? do_user_addr_fault+0x1e7/0x670
[ 325.343167] ? exit_to_user_mode_prepare+0x35/0x1b0
[ 325.343169] ? syscall_exit_to_user_mode+0x35/0x50
[ 325.343170] ? __x64_sys_mmap+0x33/0x50
[ 325.343172] ? do_syscall_64+0x69/0xc0
[ 325.343172] ? exc_page_fault+0x89/0x170
[ 325.343173] entry_SYSCALL_64_after_hwframe+0x61/0xcb
[ 325.343175] RIP: 0033:0x7f1fe010aa3d
[ 325.343177] Code: 5b 41 5c c3 66 0f 1f 84 00 00 00 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d c3 a3 0f 00 f7 d8 64 89 01 48
[ 325.343177] RSP: 002b:00007ffeeb974448 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[ 325.343179] RAX: ffffffffffffffda RBX: 0000562021004790 RCX: 00007f1fe010aa3d
[ 325.343180] RDX: 0000000000000000 RSI: 00005620200aacd2 RDI: 0000000000000003
[ 325.343181] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
[ 325.343181] R10: 0000000000000003 R11: 0000000000000246 R12: 00005620200aacd2
[ 325.343182] R13: 0000562021004760 R14: 00005620200a9888 R15: 00005620210048a0
[ 325.343183] </TASK>
[ 325.349831] My Module: Module unloaded.
dump_stack()
함수 사용 시 주의사항
호출이 많은 함수 내에서의 dump_stack()
호출은 시스템 응답 속도가 매우 느려지는 성능 문제가 발생할 수 있다. 특히, printk()
보다 더 많은 일들을 하기 때문에 더더욱 주의해야 한다. printk()
함수 사용은 다음글을 참고한다.
dump_stack()
을 사용하려면 printk()
와 마찬가지로 커널 코드에 추가하고 커널 빌드를 해야한다. 이러한 번거로움 없이 디버깅을 하려면 ftrace()
를 사용하면 된다. ftrace()
사용법은 다음 글을 참고한다.