dump_stack() 함수

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() 사용법은 다음 글을 참고한다.

참고

답글 남기기