아직 전권을 읽지 않았습니다. 매 챕터 끝날때마다 업데이트 하겠습니다
디버깅을 통해 배우는 리눅스 커널의 구조와 원리는 라즈베리 파이 3과 32비트 리눅스 4.19 버전에서의 코드와 스크립트를 사용합니다. 라즈베리 파이 4B와 최신 64비트 리눅스 버전 6.12 LTS 에서는 몇가지 다른점들이 있는데, 이 포스트에서는 그 차이점들과 원활한 실습을 위한 방법들을 소개합니다.
라즈베리 파이 OS 2024-11-19버전 (데비안 12)에서 리눅스 6.12 커널을 설치했습니다. 테스트된 하드웨어는 라즈베리 파이 4B 8GB 입니다.
2장 라즈베리 파이 설정
라즈베리 파이 4B는 BCM2711 SOC를 사용합니다. 또한 64비트 커널을 사용하기 위해선 $KERNEL
을 kernel8
로 설정해야 합니다. 커널 이미지와 모듈의 위치도 다른것에 주의하세요!
더 자세한 정보는 라즈베리 파이 문서를 참고하세요.
2.3.3 라즈비안 리눅스 커널 빌드
build_rpi_kernel.sh
#!/bin/bash
echo "configure build output path"
KERNEL_TOP_PATH="$( cd "$(dirname "$0")" ; pwd -P )"
OUTPUT="$KERNEL_TOP_PATH/out"
echo "$OUTPUT"
KERNEL=kernel8
BUILD_LOG="$KERNEL_TOP_PATH/rpi_build_log.txt"
echo "move to kernel source"
cd linux
echo "make defconfig"
make O=$OUTPUT bcm2711_defconfig
echo "kernel build"
make O=$OUTPUT Image.gz modules dtbs -j6 2>&1 | tee $BUILD_LOG
2.3.4 라즈비안 리눅스 커널 설치
install_rpi_kernel_img.sh
#!/bin/bash
KERNEL_TOP_PATH="$( cd "$(dirname "$0")" ; pwd -P )"
OUTPUT="$KERNEL_TOP_PATH/out"
echo "$OUTPUT"
KERNEL=kernel8
cd linux
make O=$OUTPUT modules_install
sudo cp /boot/firmware/$KERNEL.img /boot/firmware/$KERNEL-backup.img
sudo cp $OUTPUT/arch/arm64/boot/Image.gz /boot/firmware/$KERNEL.img
sudo cp $OUTPUT/arch/arm64/boot/dts/broadcom/*.dtb /boot/firmware/
sudo cp $OUTPUT/arch/arm64/boot/dts/overlays/*.dtb* /boot/firmware/overlays/
sudo cp $OUTPUT/arch/arm64/boot/dts/overlays/README /boot/firmware/overlays/
2.3.5 전처리 코드 생성
build_preprocess_rpi_kernel.sh
#!/bin/bash
echo "configure build output path"
KERNEL_TOP_PATH="$( cd "$(dirname "$0")" ; pwd -P )"
OUTPUT="$KERNEL_TOP_PATH/out"
echo "$OUTPUT"
KERNEL=kernel8
BUILD_LOG="$KERNEL_TOP_PATH/rpi_preprocess_build_log.txt"
PREPROCESS_FILE=$1
echo "build preprocessed file: $PREPROCESS_FILE""
echo "move to kernel source"
cd linux
echo "make defconfig"
make O=$OUTPUT bcm2711_defconfig
echo "kernel build"
make $PREPROCESS_FILE O=$OUTPUT Image.gz modules dtbs -j6 2>&1 | tee $BUILD_LOG
3장 커널 디버깅과 코드 학습
3.1.2 디버깅과 코드 학습 능력
kernel/irq/proc.c
noinline void rpi_get_interrupt_info(struct irqaction *action_p)
{
unsigned int irq_num = action_p->irq;
void *irq_handler = NULL;
if (action_p->handler) {
irq_handler = (void*)action_p->handler;
}
if (irq_handler) {
trace_printk("[%s] %d: %s, irq_handler: %pS \n",
current->comm, irq_num, action_p->name, irq_handler);
}
}
noinline
이 없으면 컴파일러가 최적화를 위해 자동으로 함수를 인라이닝(inlining) 하게 됩니다. 그렇게 되면 함수 자체는 /sys/kernel/debug/tracing/available_filter_function
파일에서 트레이싱 가능한 함수로 나오지만 실제로는 트레시징 되지 않게 됩니다.
또한 컴파일 도중에 -Wmissing-prototypes
경고가 뜨게 되는데 무시해도 상관없습니다.
또한 irq_trace_ftrace.sh
에서 11번째 줄
echo secondary_start_kernel > /sys/kernel/debug/tracing/set_ftrace_filter
는 최신 커널에서 더이상 트레이싱 불가능(notrace
)하니 주석처리 해주세요.
3.6 커널 디버깅용 Debugfs 드라이버 코드
drivers/soc/bcm/rpi_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/slub_def.h>
#include <linux/uaccess.h>
#include <asm/memory.h>
uint32_t raspbian_debug_state = 0x1000;
static struct dentry *rpi_kernel_debug_debugfs_root;
static int rpi_kernel_debug_stat_get(void *data, u64 *val)
{
printk("===[%s][L:%d][val:%d]===\n", __func__, __LINE__, raspbian_debug_state);
*val = raspbian_debug_state;
return 0;
}
static int rpi_kernel_debug_stat_set(void *data, u64 val)
{
raspbian_debug_state = (uint32_t)val;
printk("[raspbian] [%s][L:%d], raspbian_debug_state[%lu],value[%lu]===\n",
__func__, __LINE__, (long unsigned int)raspbian_debug_state, (long unsigned int)val);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(rpi_kernel_debug_stat_fops, rpi_kernel_debug_stat_get, rpi_kernel_debug_stat_set, "%llu\n");
static int rpi_kernel_debug_debugfs_driver_probe(struct platform_device *pdev)
{
printk("===[%s][L:%d]===\n", __func__, __LINE__);
return 0;
}
static struct platform_driver rpi_kernel_debug_debugfs_driver = {
.probe = rpi_kernel_debug_debugfs_driver_probe,
.driver = {
.owner = THIS_MODULE,
.name = "rpi_debug",
},
};
static int __init rpi_kernel_debug_debugfs_init(void)
{
printk("===[%s][L:%d]===\n", __func__, __LINE__);
rpi_kernel_debug_debugfs_root = debugfs_create_dir("rpi_debug", NULL);
debugfs_create_file("val", S_IRUGO, rpi_kernel_debug_debugfs_root, NULL, &rpi_kernel_debug_stat_fops);
return platform_driver_register(&rpi_kernel_debug_debugfs_driver);
}
late_initcall(rpi_kernel_debug_debugfs_init);
MODULE_DESCRIPTION("raspberrypi debug interface driver");
MODULE_AUTHOR("Austin Kim <austindh.kim@gmail.com>");
MODULE_LICENSE("GPL");
리눅스 6.8 버전에서 <linux/slub_def.h>
는 <linux/slab.h>
로 이름이 변경되었습니다. <linux/slab.h>
헤더는 이미 include 되었으니
#include <linux/slub_def.h>
부분을 주석처리 해주세요.
drivers/soc/bcm/Makefile
파일이 좀 다를 수도 있지만 마지막 줄에
obj-y +=rpi_debugfs.o
를 입력하는 것은 똑같습니다.
4장 프로세스
4.4.1 기본 유저 레벨 프로세스 실행 실습 및 ftrace 로그 분석
clone_process_debug.sh
#!/bin/bash
echo 0 > /sys/kernel/debug/tracing/tracing_on
sleep 1
echo "tracing_off"
echo 0 > /sys/kernel/debug/tracing/events/enable
sleep 1
echo "events disabled"
# echo secondary_start_kernel > /sys/kernel/debug/tracing/set_ftrace_filter
# sleep 1
# echo "set_ftrace_filter init"
echo function > /sys/kernel/debug/tracing/current_tracer
sleep 1
echo "function tracer enabled"
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_switch/enable
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_process_fork/enable
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_process_exit/enable
echo 1 > /sys/kernel/debug/tracing/events/signal/enable
echo __arm64_sys_clone do_exit > /sys/kernel/debug/tracing/set_ftrace_filter
echo kernel_clone copy_process >> /sys/kernel/debug/tracing/set_ftrace_filter
sleep 1
echo "event enabled"
sleep 1
echo "set_ftrace_filter enabled"
echo 1 > /sys/kernel/debug/tracing/options/func_stack_trace
echo 1 > /sys/kernel/debug/tracing/options/sym-offset
echo "function stack trace enabled"
echo 1 > /sys/kernel/debug/tracing/tracing_on
echo "tracing_on"
64비트 arm에서 sys_clone
을 트레이싱 하려면 __arm64_sys_clone
으로 설정해야 합니다. 리눅스 5.10에서 _do_fork
함수는 kernel_clone
함수로 대체되었습니다.
4.4.2 exit() 함수로 프로세스가 종료되는 과정 및 ftrace 로그 분석
clone_process_debug.sh
#!/bin/bash
echo 0 > /sys/kernel/debug/tracing/tracing_on
sleep 1
echo "tracing_off"
echo 0 > /sys/kernel/debug/tracing/events/enable
sleep 1
echo "events disabled"
# echo secondary_start_kernel > /sys/kernel/debug/tracing/set_ftrace_filter
# sleep 1
# echo "set_ftrace_filter init"
echo function > /sys/kernel/debug/tracing/current_tracer
sleep 1
echo "function tracer enabled"
echo do_exit copy_process > /sys/kernel/debug/tracing/set_ftrace_filter
echo __arm64_sys_clone kernel_clone >> /sys/kernel/debug/tracing/set_ftrace_filter
sleep 1
echo "set_ftrace_filter enabled"
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_process_exit/enable
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_process_fork/enable
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_process_free/enable
sleep 1
echo "event enabled"
echo 1 > /sys/kernel/debug/tracing/options/func_stack_trace
echo 1 > /sys/kernel/debug/tracing/options/sym-offset
echo "function stack trace enabled"
echo 1 > /sys/kernel/debug/tracing/tracing_on
echo "tracing_on"
위와 같은 이유와 더불어 search_binary_handler
는 리눅스 6.12 기준으로 트레이싱 되지 않습니다. copy_process
는 나눠서 트레이싱 되지 않고 함수 자체만 트레이싱 됩니다.
Top comments (0)