DEV Community

Minsoo Choo
Minsoo Choo

Posted on

"디버깅을 통해 배우는 리눅스 커널의 구조와 원리": 라즈베리 파이 4B 와 최신 리눅스에서의 실습을 위한 변경점

아직 전권을 읽지 않았습니다. 매 챕터 끝날때마다 업데이트 하겠습니다

디버깅을 통해 배우는 리눅스 커널의 구조와 원리는 라즈베리 파이 3과 32비트 리눅스 4.19 버전에서의 코드와 스크립트를 사용합니다. 라즈베리 파이 4B와 최신 64비트 리눅스 버전 6.12 LTS 에서는 몇가지 다른점들이 있는데, 이 포스트에서는 그 차이점들과 원활한 실습을 위한 방법들을 소개합니다.

라즈베리 파이 OS 2024-11-19버전 (데비안 12)에서 리눅스 6.12 커널을 설치했습니다. 테스트된 하드웨어는 라즈베리 파이 4B 8GB 입니다.

2장 라즈베리 파이 설정

라즈베리 파이 4B는 BCM2711 SOC를 사용합니다. 또한 64비트 커널을 사용하기 위해선 $KERNELkernel8로 설정해야 합니다. 커널 이미지와 모듈의 위치도 다른것에 주의하세요!

더 자세한 정보는 라즈베리 파이 문서를 참고하세요.

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
Enter fullscreen mode Exit fullscreen mode

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/
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
        }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

는 최신 커널에서 더이상 트레이싱 불가능(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");
Enter fullscreen mode Exit fullscreen mode

리눅스 6.8 버전에서 <linux/slub_def.h><linux/slab.h>로 이름이 변경되었습니다. <linux/slab.h> 헤더는 이미 include 되었으니

#include <linux/slub_def.h>
Enter fullscreen mode Exit fullscreen mode

부분을 주석처리 해주세요.

drivers/soc/bcm/Makefile 파일이 좀 다를 수도 있지만 마지막 줄에

obj-y +=rpi_debugfs.o
Enter fullscreen mode Exit fullscreen mode

를 입력하는 것은 똑같습니다.

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"
Enter fullscreen mode Exit fullscreen mode

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"   
Enter fullscreen mode Exit fullscreen mode

위와 같은 이유와 더불어 search_binary_handler는 리눅스 6.12 기준으로 트레이싱 되지 않습니다. copy_process는 나눠서 트레이싱 되지 않고 함수 자체만 트레이싱 됩니다.

Top comments (0)