kmsg_dump 사용법

Linux 커널은 다양한 방식으로 시스템의 상태와 이벤트를 기록한다. 가장 대표적인 로그 시스템은 **printk()**를 통해 출력되는 커널 메시지이며, 이 메시지들은 ring buffer 형태로 저장되어 /dev/kmsg 또는 dmesg 명령어로 확인할 수 있다.

하지만 시스템이 패닉(panic) 상태에 빠지거나 크래시가 발생했을 때, printk 로그가 손실될 수 있다. 이러한 상황에서도 로그를 확보하고, 이를 디버깅에 활용할 수 있도록 해주는 커널 기능이 바로 kmsg_dump이다.

이 글에서는 kmsg_dump의 개념, 사용법, 예제 코드, 동작 방식, 주의사항 등에 대해 알아본다.


kmsg_dump란?

kmsg_dump커널 패닉, 재부팅, 또는 크래시 발생 시점에 printk 로그 버퍼의 내용을 안전한 저장 매체(예: 메모리, 시리얼 포트, 플래시 메모리 등)에 덤프(dump) 할 수 있도록 지원하는 기능이다.

특징:

  • panic context에서도 안전하게 사용 가능
  • 비블로킹(non-blocking) 출력 권장
  • 수집한 로그를 UART, Flash, RAM에 저장 가능

이 기능은 커널 크래시 디버깅원인 분석에 매우 유용하며, 특히 임베디드 시스템이나 서버 환경에서 중요하게 사용된다.


kmsg_dump 주요 인터페이스

구조체: struct kmsg_dumper

/**
 * struct kmsg_dumper - kernel crash message dumper structure
 * @list:       Entry in the dumper list (private)
 * @dump:       Call into dumping code which will retrieve the data with
 *              through the record iterator
 * @max_reason: filter for highest reason number that should be dumped
 * @registered: Flag that specifies if this is already registered
 */
struct kmsg_dumper {
        struct list_head list;
        void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason);
        enum kmsg_dump_reason max_reason;
        bool registered;
};

주요 함수

함수설명
kmsg_dump_register()dumper 등록
kmsg_dump_unregister()dumper 등록 해제
kmsg_dump_rewind()읽기 위치 초기화
kmsg_dump_get_line()한 줄 읽어오기
kmsg_dump_get_buffer()버퍼 단위로 읽기

kmsg_dump 동작 흐름


커널 모듈 예제: kmsg_dump 로그 수집

커널 패닉 시 printk 버퍼 내용을 수집하여 /tmp/kmsg.log 또는 메모리 상의 임시 버퍼에 저장하는 예제를 작성한다.

1. 모듈 코드: kmsg_dump_example.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kmsg_dump.h>
#include <linux/reboot.h>
#include <linux/fs.h>

static void my_kmsg_dump(struct kmsg_dumper *dumper,
                             struct kmsg_dump_detail *detail)
{
    struct kmsg_dump_iter iter;
    static char line[1024];
    size_t len;
    enum kmsg_dump_reason reason = detail->reason;

    if (reason != KMSG_DUMP_PANIC && reason != KMSG_DUMP_OOPS) {
        pr_info("kmsg_dump: skip reason=%d\n", reason);
        return;
    }

    pr_info("kmsg_dump: start dumping lines (reason=%d)\n", reason);

    kmsg_dump_rewind(&iter);

    while (kmsg_dump_get_line(&iter, true, line, sizeof(line), &len)) {
            line[len] = '\0';
            printk("%s", line);
    }

    pr_info("kmsg_dump: completed\n");
}

static struct kmsg_dumper my_kmsg_dumper = {
    .dump = my_kmsg_dump,
};

static int __init my_module_init(void)
{
    kmsg_dump_register(&my_kmsg_dumper);
    printk(KERN_INFO "kmsg_dumper registered.\n");

    return 0;
}

static void __exit my_module_exit(void)
{
    kmsg_dump_unregister(&my_kmsg_dumper);
    printk(KERN_INFO "kmsg_dumper unregistered.\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("kmsg_dump Example");

2. Makefile

obj-m += kmsg_dump_example.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

3. 커널 모듈 빌드 및 로딩

make
sudo insmod kmsg_dump_example.ko

Klog:

[ 1856.569326] kmsg_dumper registered.

4. 패닉 유발 테스트 (주의!)

echo c | sudo tee /proc/sysrq-trigger

예상 로그 (dmesg 또는 UART 출력)

--- Sending BREAK (SysRq) ---
[ 51.442359] sysrq: Trigger a crash
[ 51.445764] Kernel panic - not syncing: sysrq triggered crash
[ 51.451501] CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Tainted: P O 6.12.0-332 #1
[ 51.459842] Tainted: [P]=PROPRIETARY_MODULE, [O]=OOT_MODULE
[ 51.465401] Hardware name: Raspberry Pi 3 Model B Rev 1.2 (DT)
[ 51.472090] Call trace:
[ 51.474524] dump_backtrace+0x94/0x114
[ 51.478267] show_stack+0x18/0x24
[ 51.481570] dump_stack_lvl+0x38/0x90
[ 51.485225] dump_stack+0x18/0x24
[ 51.488528] panic+0x38c/0x3e4
...

[ 52.930101] kmsg_dump: start dumping lines (reason=1)
...
[ 62.453802] kmsg_dump: completed


참고 사이트

답글 남기기