kprobes 를 이용한 커널 디버깅
프로그래밍 2007/06/13 10:35kprobes 를 사용하면 커널을 디버깅하기 위한 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"); 