- dump_stack() 함수 개념
- dump_stack() 함수 사용법 및 동작
- dump_stack.c 원형 코드
- printk와 dump_stack 함수 차이점
1. dump_stack() 함수 개념
dump_stack은 리눅스 커널에서 현재 호출 스택을 출력하는 데 사용되는 디버깅 함수다. 이 함수는 커널의 다양한 상황에서 유용하게 사용될 수 있으며, 특히 커널 패닉이나 오류가 발생했을 때 호출 스택을 출력하여 문제를 진단하는 데 도움이 된다.
2. dump_stack() 함수 사용법 및 동작
dump_stack 함수의 동작
- 목적: 커널에서 현재 호출 스택을 출력하여 디버깅 정보를 제공한다.
- 사용 위치: 주로 커널의 디버깅 코드, 오류 처리 루틴, 또는 커널 패닉 처리 루틴에서 사용된다.
- 출력 내용: 현재 실행 중인 함수와 해당 함수가 호출된 위치를 포함한 전체 호출 스택 정보를 출력한다.
dump_stack 함수 사용법
1. 함수 선언
dump_stack 함수는 아래와 같이 선언되어 있다. 이 함수는 매개변수를 받지 않으며, 단순히 호출 스택을 출력한다.
void dump_stack(void);
2. 사용 예시
dump_stack 함수는 코드 내에서 아래와 같이 사용할 수 있다. 아래 코드는 모듈 초기화 시 dump_stack을 통해 호출 스택을 출력하는 예제이다.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
static int __init my_module_init(void)
{
printk(KERN_INFO "Initializing my module\n");
dump_stack();
return 0;
}
static void __exit my_module_exit(void)
{
printk(KERN_INFO "Exiting my module\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Example module that uses dump_stack");
MODULE_AUTHOR("Your Name");
3. 실행 결과
dump_stack을 호출하면 다음과 같은 형식의 출력이 커널 로그에 기록된다.
[ 1234.567890] Call Trace:
[ 1234.567890] [<ffffffff81234567>] dump_stack+0x15/0x17
[ 1234.567890] [<ffffffff81234567>] my_module_init+0x20/0x30
[ 1234.567890] [<ffffffff81012345>] do_one_initcall+0x56/0x200
[ 1234.567890] ...
4. 출력 내용 해석
- Call Trace: 호출 스택의 시작을 알리는 문구
- 주소: 각 함수 호출의 주소
- 함수명: 호출된 함수의 이름
- 오프셋: 해당 함수 내에서의 오프셋
- 함수 호출 위치: 함수가 호출된 위치
3. dump_stack.c 함수 원형 코드
dump_stack 함수 원형을 보기 위해 아래 경로로 들어가 확인해 보았다. 현재 내가 사용하고 있는 리눅스 커널 버전은 5.4.210이라 아래의 경로에 있었고, 리눅스 커널을 다운로드한 후 dump_stack.c를 찾고 싶다면 아래 명령어를 입력하면 된다.
find . -name "dump_stack.c"
./linux-5.4.210/lib/dump_stack.c
// SPDX-License-Identifier: GPL-2.0
/*
* Provide a default dump_stack() function for architectures
* which don't implement their own.
*/
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/smp.h>
#include <linux/atomic.h>
#include <linux/kexec.h>
#include <linux/utsname.h>
static char dump_stack_arch_desc_str[128];
/**
* dump_stack_set_arch_desc - set arch-specific str to show with task dumps
* @fmt: printf-style format string
* @...: arguments for the format string
*
* The configured string will be printed right after utsname during task
* dumps. Usually used to add arch-specific system identifiers. If an
* arch wants to make use of such an ID string, it should initialize this
* as soon as possible during boot.
*/
void __init dump_stack_set_arch_desc(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vsnprintf(dump_stack_arch_desc_str, sizeof(dump_stack_arch_desc_str),
fmt, args);
va_end(args);
}
/**
* dump_stack_print_info - print generic debug info for dump_stack()
* @log_lvl: log level
*
* Arch-specific dump_stack() implementations can use this function to
* print out the same debug information as the generic dump_stack().
*/
void dump_stack_print_info(const char *log_lvl)
{
printk("%sCPU: %d PID: %d Comm: %.20s %s%s %s %.*s\n",
log_lvl, raw_smp_processor_id(), current->pid, current->comm,
kexec_crash_loaded() ? "Kdump: loaded " : "",
print_tainted(),
init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version);
if (dump_stack_arch_desc_str[0] != '\0')
printk("%sHardware name: %s\n",
log_lvl, dump_stack_arch_desc_str);
print_worker_info(log_lvl, current);
}
/**
* show_regs_print_info - print generic debug info for show_regs()
* @log_lvl: log level
*
* show_regs() implementations can use this function to print out generic
* debug information.
*/
void show_regs_print_info(const char *log_lvl)
{
dump_stack_print_info(log_lvl);
}
static void __dump_stack(void)
{
dump_stack_print_info(KERN_DEFAULT);
show_stack(NULL, NULL);
}
/**
* dump_stack - dump the current task information and its stack trace
*
* Architectures can override this implementation by implementing its own.
*/
#ifdef CONFIG_SMP
static atomic_t dump_lock = ATOMIC_INIT(-1);
asmlinkage __visible void dump_stack(void)
{
unsigned long flags;
int was_locked;
int old;
int cpu;
/*
* Permit this cpu to perform nested stack dumps while serialising
* against other CPUs
*/
retry:
local_irq_save(flags);
cpu = smp_processor_id();
old = atomic_cmpxchg(&dump_lock, -1, cpu);
if (old == -1) {
was_locked = 0;
} else if (old == cpu) {
was_locked = 1;
} else {
local_irq_restore(flags);
/*
* Wait for the lock to release before jumping to
* atomic_cmpxchg() in order to mitigate the thundering herd
* problem.
*/
do { cpu_relax(); } while (atomic_read(&dump_lock) != -1);
goto retry;
}
__dump_stack();
if (!was_locked)
atomic_set(&dump_lock, -1);
local_irq_restore(flags);
}
#else
asmlinkage __visible void dump_stack(void)
{
__dump_stack();
}
#endif
EXPORT_SYMBOL(dump_stack);
4. printk와 dump_stack 차이점
- 목적
- dump_stack: 호출 스택을 출력하여 현재 함수가 호출된 경로를 추적
- printk: 커널 로그 메시지를 출력하여 시스템 상태나 오류, 경고 등을 기록
- 사용 방식
- dump_stack: 매개변수 없이 호출하여 호출 스택을 출력
- printk: 형식 문자열과 가변 인수를 받아 로그 메시지를 출력
- 출력 내용
- dump_stack: 호출된 함수와 호출 위치를 포함한 호출 스택
- printk: 사용자 지정 형식의 로그 메시지
dump_stack 함수는 내부적으로 printk를 사용하여 호출 스택을 출력한다. 즉, dump_stack가 호출되면, 현재의 호출 스택 정보가 printk를 통해 커널 로그에 기록된다. printk는 유용한 디버깅 도구이지만, 과도하게 사용하면 성능 저하, 메모리 문제, 동시성 문제 등을 초래할 수 있다. 따라서, printk나 dump_stack을 사용하는 디버깅 코드 작성 시에는 주의가 필요하며, 로그 출력의 양을 적절히 조절하는 것이 중요하다.
'Opensource > Linux' 카테고리의 다른 글
Ubuntu TACACS+ 서버 구축 및 systemd 서비스 생성하기 (0) | 2024.09.25 |
---|---|
리눅스 커널 메모리 할당과 GFP Flags (kmalloc, GFP_KERNEL, GFP_ATOMIC) (3) | 2024.09.20 |
소켓(Socket) 통신 개념과 c언어 예제 (4) | 2024.08.28 |
리눅스 디바이스 드라이버(Linux Device Driver) 개념 구조 예제 (1) | 2024.08.07 |
[Linux] GDB를 이용해 C 디버깅하기 (Debugging 예제) (0) | 2024.08.06 |