Linux Debugging FileSystem

2 minute read

해당 포스트에서는 커스텀 디버깅 파일시스템을 통하여 커널 디버깅하는 방법을 설명합니다.

  • 리눅스 소스코드의 fs 디렉토리 아래 다음 소스코드 작성
/* km_debugfs.c */
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/memblock.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <asm/setup.h>
#include <linux/input.h>
#include <linux/debugfs.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/memory.h>

uint32_t km_debug_state = 0x1000;
static struct dentry *km_kernel_debugfs_root;

static int km_kernel_debug_stat_get(void *data, u64 *val)
{
	printk("[%s][L:%d][val:%d]\n", __func__, __LINE__, km_debug_state);
	*val = km_debug_state;
	return 0;
}

static int km_kernel_debug_stat_set(void *data, u64 val)
{
	km_debug_state = (uint32_t)val;
	printk("[km] [%s][L:%d], [km_debug_state:%lu], [value:%lu]\n",
			__func__, __LINE__, (long unsigned int)km_debug_state, (long unsigned int)val);
	return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(km_kernel_debug_stat_fops, km_kernel_debug_stat_get,
		km_kernel_debug_stat_set, "%llu\n");

static int km_kernel_debugfs_driver_probe(struct platform_device *pdev)
{
	printk("[%s][L:%d]\n", __func__, __LINE__);
	return 0;
}

static struct platform_driver km_kernel_debugfs_driver = {
	.probe = km_kernel_debugfs_driver_probe,
	.driver = {
		.owner = THIS_MODULE,
		.name = "km_debug",
	},
};

static int __init km_kernel_debugfs_init(void)
{
	printk("[%s][L:%d]\n", __func__, __LINE__);
	
	km_kernel_debugfs_root = debugfs_create_dir("km_debug", NULL);
	debugfs_create_file("val", S_IRUGO, km_kernel_debugfs_root, NULL, &km_kernel_debug_stat_fops);
	return platform_driver_register(&km_kernel_debugfs_driver);
}

late_initcall(km_kernel_debugfs_init);

MODULE_DESCRIPTION("debugfs driver");
MODULE_LICENSE("GPL");
  • Makefile의 obj-y 수정
obj-y :=	open.o read_write.o file_table.o super.o \
		char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
		ioctl.o readdir.o select.o dcache.o inode.o \
		attr.o bad_inode.o file.o filesystems.o namespace.o \
		seq_file.o xattr.o libfs.o fs-writeback.o \
		pnode.o splice.o sync.o utimes.o d_path.o \
		stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
		fs_types.o fs_context.o fs_parser.o fsopen.o init.o km_debugfs.o
# 위 소스파일 이름이 km_debugfs.c라서 km_debugfs.o를 추가
# my_debugfs.c라고 작성했다면 my_debugfs.o를 추가
  • 원하는 소스 코드 수정
// 리눅스 커널 소스코드 중 net/ipv4/ip_input.c 파일의 528 라인(ip_rcv_core 함수 종료)에
// 다음과 같이 print_skb 함수와 km_debug_state 선언 추가
static void print_skb(struct sk_buff *skb)
{
	int i = 0, j = 0;

	printk("%7s", "offset ");

	for (i = 0; i < 16; i++) {
		printk(KERN_CONT "%02x ", i);

		if (!(i % 16 - 7))
			printk(KERN_CONT "- ");
	}
	printk(KERN_CONT "\n");

	for (i = 0; i < skb->len; i++) {
		if (!(i % 16))
			printk(KERN_CONT "0x%04x ", i);
		printk(KERN_CONT "%02x ", skb->data[i]);

		if (!(i % 16 - 7))
			printk(KERN_CONT "- ");

		if (!(i % 16 - 15)) {
			printk(KERN_CONT "\t");
			for (j = i - 15; j <= i; j++) {
				printk(KERN_CONT "%c", skb->data[j] >= 0x20 && skb->data[j] < 0x80 ? skb->data[j] : '.');
			}

			printk(KERN_CONT "\n");
		}
	}

	printk("\n");
}

extern uint32_t km_debug_state;

/*
 * IP receive entry point
 */
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
	   struct net_device *orig_dev)
{
	struct net *net = dev_net(dev);

	skb = ip_rcv_core(skb, net);
	if (skb == NULL)
		return NET_RX_DROP;
	/***** 추가된 소스 코드 *****/
	if (km_debug_state == 16)
		print_skb(skb);
	/***************************/
	return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
		       net, NULL, skb, dev, NULL,
		       ip_rcv_finish);
}
  • 커널 재빌드 및 설치
  • /sys/kernel/debug 디렉토리 아래 새로 생성된 디렉토리 확인 위 소스코드 그대로면 km_debug 디렉토리 생성
  • 콘솔에서 dmesg -w 명령으로 커널 메시지 확인
  • echo 16 > /sys/kernel/debug/km_debug/val 명령으로 디버깅 루틴 시작
  • 네트워크 사용 시 다음과 같은 결과 확인 debug_fs