kprobes 를 이용한 커널 디버깅

프로그래밍 2007/06/13 10:35

kprobes 를 사용하면 커널을 디버깅하기 위한 printk 문을 추가하기 위하여 커널을 재컴파일하고 컴파일된 커널을 다시 올려 시스템을 재시작할 필요가 없이 원하는 시점에 원하는 어느 위치에나 probe 를 추가하여 메시지를 출력할 수 있다.

kprobes 에서 지원하는 probe 는 kprobe 와 jprobe 두 가지가 있으며 둘의 차이점은 kprobe 는 커널의 함수 어느 위치에나 추가할 수 있지만, jprobe 는 커널 함수의 entry 에만 추가할 수 있다. 하지만 kprobe 는 함수 파라미터를 검사 할 수 없지만 jprobe 는 그것이 가능하다.

kprobe 를 커널 함수 내의 특정 위치에 추가하려면 함수 내의 offset 을 얻기 위한 약간의 귀찮은 작업이 필요하다.

kprobes 는 모듈로 등록이 되기 때문에 원하는 시점에 probe 를 추가, 제거가 가능하며 현재는 2.6 커널에서 지원하고 있다. kprobes 가 확장된 DProbes 가 있는데 dprobes 를 사용하면 커널 뿐만 아니라 유저 어플리케이션도 비슷한 방법으로 디버깅이 가능하다고 한다.

참고사이트 : Kernel debugging with Kprobes

kprobe 예제

/* kprobebio.c
   This is a simple module to get information about block io operations.
   Will Cohen
*/
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/blkdev.h>

static int count_generic_make_request = 0;

static int inst_generic_make_request(struct kprobe *p, struct pt_regs *regs)
{
    ++count_generic_make_request;
    return 0;
}

/*For each probe you need to allocate a kprobe structure*/
static struct kprobe kp = {
    .pre_handler = inst_generic_make_request,
    .post_handler = NULL,
    .fault_handler = NULL,
    .addr = (kprobe_opcode_t *) generic_make_request,
};

int init_module(void)
{
    register_kprobe(&kp);
    printk("kprobe registered\n");
    return 0;
}

void cleanup_module(void)
{
    unregister_kprobe(&kp);
    printk("kprobe unregistered\n");
    printk("generic_make_request() called %d times.\n",
    count_generic_make_request);
}

MODULE_LICENSE("GPL");


jprobe 예제

/* jprobebio.c
   This is a simple module to get information about block io operations.
   Will Cohen
*/
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/hash.h>

#define LOG_2_BINS 8
#define BINS (1<<LOG_2_BINS)

static int count_generic_make_request = 0;
static unsigned long long sectors_transferred = 0;

static struct block_device *bin_bdev[BINS];
static dev_t bin_bd_dev[BINS];
static int bin_count[BINS];
static unsigned long long bin_sectors[BINS];

static void inst_generic_make_request(struct bio *bio)
{
    unsigned long b=hash_ptr(bio->bi_bdev, LOG_2_BINS);

    ++count_generic_make_request;
    sectors_transferred += bio_sectors(bio);

    if( bin_bdev[b] == NULL || bin_bdev[b] == bio->bi_bdev) {
        bin_bdev[b] = bio->bi_bdev;
        bin_bd_dev[b] = bio->bi_bdev->bd_dev;
        ++bin_count[b];
        bin_sectors[b] += bio_sectors(bio);
    }
    jprobe_return();
}

static struct jprobe my_jprobe = {
    .kp.addr = (kprobe_opcode_t *) generic_make_request,
    .entry = (kprobe_opcode_t *) inst_generic_make_request
};

int init_module(void)
{
    register_jprobe(&my_jprobe);
    printk("plant jprobe at %p, handler addr %p\n",
           my_jprobe.kp.addr, my_jprobe.entry);
    return 0;
}

void cleanup_module(void)
{
    int b;

    unregister_jprobe(&my_jprobe);
    printk("jprobe unregistered\n");

    printk("generic_make_request() called %d times for %lld sectors.\n",
            count_generic_make_request, sectors_transferred);
    for (b=0; b<BINS; ++b)
        if (bin_bdev[b] != NULL)
            printk("bdev 0x%p (%d,%d) %d %lld sectors.\n", bin_bdev[b],
                    MAJOR(bin_bd_dev[b]), MINOR(bin_bd_dev[b]),
                    bin_count[b], bin_sectors[b]);
}

MODULE_LICENSE("GPL");
Trackback 0 : Comment 0
◀ PREV : [1] : ... [37] : [38] : [39] : [40] : [41] : [42] : [43] : NEXT ▶