下面使用 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