使用Kretprobes 观测系统调用 read 字节数 并放到 proc 文件系统

下面使用 Kretprobes 观测系统调用 ksys_read() 的返回字节数, 并把这些数字做成 histogram 的形式放到 /proc/readpattern 去, 然后读这个文件.

源代码

文件名 dumpreadstat.c:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/ktime.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/proc_fs.h>

#define MY_ARRAY_SIZE 10
#define MAX_STRING_LENGTH 16
static uint my_array[MY_ARRAY_SIZE];
static char desc_arr[MY_ARRAY_SIZE][MAX_STRING_LENGTH] = {
    "< 0        ",
    "= 0        ",
    "0 -> 20    ",
    "20 -> 40   ",
    "40 -> 80   ",
    "80 -> 160  ",
    "160 -> 320 ",
    "320 -> 640 ",
    "640 -> 1280",
    " > 1280    "
};

static int flag = 1;
static struct proc_dir_entry *proc_file;

static char func_name[NAME_MAX] = "ksys_read";
module_param_string(func, func_name, NAME_MAX, S_IRUGO);
MODULE_PARM_DESC(func, "Function to kretprobe; this module will report the"
            " function's execution time");

static int open_proc(struct inode *inode, struct file *file)
{
    printk(KERN_ALERT "open proc\n");
    return 0;
}

static int release_proc(struct inode *inode, struct file *file)
{
    printk(KERN_ALERT "release proc\n");
    return 0;
}

static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
{
    int i;
    printk(KERN_ALERT "read proc\n");
    if (flag)
    {
        flag = 0;
    }
    else
    {
        flag = 1;
        return 0;
    }

    char output[1024];
    int offst = 15;
    int remaining = sizeof(output);
    snprintf(output, remaining, "bytes \t\t:count\n");
    remaining -= 15;
    for (i = 0; i < MY_ARRAY_SIZE; i++) {
        int ret;

        ret = snprintf(output + offst, remaining, "%s\t: %u\n", desc_arr[i], my_array[i]);
        if (ret < 0 || ret >= remaining) {
            printk(KERN_ERR "Failed to concatenate my_array values\n");
            return -EINVAL;
        }

        offst += ret;
        remaining -= ret;
    }

    printk(KERN_ALERT "%s", output);

    if (copy_to_user(buffer, output, offst))
    {
        printk(KERN_ERR "Data Send: Err!\n");
        return -EFAULT;
    }
    return strlen(output);
}

static ssize_t write_proc(struct file *filp, const char *buffer, size_t len, loff_t *off)
{
    printk(KERN_ALERT "write proc\n");
    return 0;
}

static const struct proc_ops proc_fops = {
    .proc_open = open_proc,
    .proc_read = read_proc,
    .proc_write = write_proc,
    .proc_release = release_proc,
};

static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    int retval = regs_return_value(regs);
    if (retval < 0) {
        my_array[0]++;
    } else if (0 == retval) {
        my_array[1]++;
    } else if (retval < 20) {
        my_array[2]++;
    } else if (retval < 40) {
        my_array[3]++;
    } else if (retval < 80) {
        my_array[4]++;
    } else if (retval < 160) {
        my_array[5]++;
    } else if (retval < 320) {
        my_array[6]++;
    } else if (retval < 640) {
        my_array[7]++;
    } else if (retval < 1280) {
        my_array[8]++;
    } else {
        my_array[9]++;
    }
    //printk(KERN_INFO "%s returned %d \n", func_name, retval);
    return 0;
}

static struct kretprobe my_kretprobe = {
    .handler        = ret_handler,
    .maxactive        = 3,
};

static int __init kretprobe_init(void)
{
    int ret;

    memset(my_array, 0, sizeof(uint) * MY_ARRAY_SIZE);
    my_kretprobe.kp.symbol_name = func_name;
    ret = register_kretprobe(&my_kretprobe);
    if (ret < 0) {
        printk(KERN_INFO "register_kretprobe failed, returned %d\n",
                ret);
        return -1;
    }
    printk(KERN_INFO "Planted return probe at %s: %p\n",
            my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);

    /* Create proc file under /proc/dumpprocmm */
    proc_file = proc_create("readpattern", 0666, NULL, &proc_fops);
    if (!proc_file) {
        printk(KERN_ERR "Failed to create proc file\n");
        return -ENOMEM;
    }

    return 0;
}

static void __exit kretprobe_exit(void)
{
    int i;
    unregister_kretprobe(&my_kretprobe);
    printk(KERN_INFO "kretprobe at %p unregistered\n",
            my_kretprobe.kp.addr);

    printk(KERN_INFO "my_array values:\n");
    for (i = 0; i < MY_ARRAY_SIZE; i++) {
        printk(KERN_INFO "my_array[%d]: %u\n", i, my_array[i]);
    }

    if (proc_file) {
        proc_remove(proc_file);
        printk(KERN_INFO "Removed /proc/%s file\n", "readpattern");
    }

    /* nmissed > 0 suggests that maxactive was set too low. */
    printk(KERN_INFO "Missed probing %d instances of %s\n",
        my_kretprobe.nmissed, my_kretprobe.kp.symbol_name);
}

module_init(kretprobe_init)
module_exit(kretprobe_exit)
MODULE_LICENSE("GPL");

Makefile

obj-m += dumpreadstat.o

tag ?= `uname -r`
KDIR := /lib/modules/${tag}/build/

all:
    make -C $(KDIR) M=$(PWD) modules

clean:
    make -C $(KDIR) M=$(PWD) clean

执行并观测

$ make all
$ sudo insmod dumpreadstat.ko
$ cat /proc/readpattern
bytes         :count
< 0            : 1
= 0            : 27
0 -> 20        : 65
20 -> 40       : 24
40 -> 80       : 7
80 -> 160      : 8
160 -> 320     : 3
320 -> 640     : 2
640 -> 1280    : 11
 > 1280        : 10

# 卸载模块
$ sudo rmmod dumpreadstat

# 观测系统日志
$ tail -n 100 /var/log/syslog

标签: none

添加新评论