206 lines
5.6 KiB
C
206 lines
5.6 KiB
C
#include "monitor_kernel.h"
|
|
|
|
#include <linux/cdev.h> // for cdev
|
|
#include <linux/device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h> // for kmalloc
|
|
|
|
#define DEVICE_NAME "variable_monitor"
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
|
|
// for character device
|
|
static dev_t dev_num;
|
|
static struct cdev *watch_cdev;
|
|
static struct class *watch_class;
|
|
|
|
struct my_device_data {
|
|
pid_t pid;
|
|
};
|
|
|
|
static int device_open(struct inode *inode, struct file *file) {
|
|
struct my_device_data *data;
|
|
printk(KERN_INFO "variable_monitor open device with pid %d\n", current->pid);
|
|
// save pid
|
|
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
data->pid = current->pid;
|
|
file->private_data = data;
|
|
return 0;
|
|
}
|
|
|
|
static int device_release(struct inode *inode, struct file *file) {
|
|
// load pid
|
|
struct my_device_data *data = file->private_data;
|
|
printk(KERN_INFO "variable_monitor release device with pid %d\n", data->pid);
|
|
// clear watch with pid
|
|
clear_watch(data->pid);
|
|
kfree(data); // free data memory
|
|
return 0;
|
|
}
|
|
|
|
typedef struct {
|
|
int id;
|
|
} ioctl_id;
|
|
|
|
// dump log from buffer
|
|
static int dump_log(ioctl_dump_param *dump_param, unsigned long ioctl_param,
|
|
struct diag_variant_buffer *buffer) {
|
|
int ret = 0;
|
|
ret = copy_from_user(dump_param, (ioctl_dump_param *)ioctl_param,
|
|
sizeof(ioctl_dump_param));
|
|
printk(KERN_INFO "dump_param: %p %lx %p\n", dump_param->user_ptr_len,
|
|
dump_param->user_buf_len, dump_param->user_buf);
|
|
if (!ret) {
|
|
ret = copy_to_user_variant_buffer(buffer, dump_param->user_ptr_len,
|
|
dump_param->user_buf,
|
|
dump_param->user_buf_len);
|
|
printk(KERN_INFO "ret %d, %lx\n", ret, dump_param->user_buf_len);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static long device_ioctl(struct file *file, unsigned int ioctl_num,
|
|
unsigned long ioctl_param) {
|
|
int ret = 0;
|
|
watch_arg warg;
|
|
ioctl_dump_param dump_param;
|
|
ioctl_id wid;
|
|
|
|
printk(KERN_INFO "variable_monitor fun: %s with ioctl_num %d\n", __FUNCTION__,
|
|
ioctl_num);
|
|
switch (ioctl_num) {
|
|
case IOCTL_WATCH_VARIABLE:
|
|
// copy watch_arg
|
|
if (copy_from_user(&warg, (watch_arg *)ioctl_param, sizeof(warg))) {
|
|
return -EACCES;
|
|
}
|
|
printk(KERN_INFO "Watch_arg: task_id=%d, name=%s, ptr=%p, length_byte=%d, "
|
|
"time_ns=%ld, threshold=%lld\n",
|
|
warg.task_id, warg.name, warg.ptr, warg.length_byte, warg.time_ns,
|
|
warg.threshold);
|
|
warg.time_ns =
|
|
warg.time_ns == 0 ? def_interval_ns : warg.time_ns; // default 10us
|
|
// start watch variable
|
|
ret = start_watch_variable(warg);
|
|
if (ret) {
|
|
printk(KERN_INFO "start_watch_variable failed\n");
|
|
}
|
|
break;
|
|
case IOCTL_DUMP_LOG:
|
|
printk(KERN_INFO "variable_monitor IOCTL_DUMP_LOG\n");
|
|
ret = dump_log(&dump_param, ioctl_param, &load_monitor_variant_buffer);
|
|
if (ret) {
|
|
printk(KERN_INFO "dump_log failed\n");
|
|
}
|
|
break;
|
|
case IOCTL_PID:
|
|
printk(KERN_INFO "variable_monitor PID\n");
|
|
ret = copy_from_user(&wid, (ioctl_id *)ioctl_param, sizeof(ioctl_id));
|
|
if (ret) {
|
|
printk(KERN_INFO "copy_from_user failed\n");
|
|
}
|
|
ret = diag_pid(wid.id);
|
|
if (ret) {
|
|
printk(KERN_INFO "diag_pid failed\n");
|
|
}
|
|
break;
|
|
case IOCTL_TGID:
|
|
printk(KERN_INFO "variable_monitor TGID\n");
|
|
ret = copy_from_user(&wid, (ioctl_id *)ioctl_param, sizeof(ioctl_id));
|
|
diag_tgid(wid.id);
|
|
break;
|
|
case IOCTL_DUMP_LOG_SA:
|
|
printk(KERN_INFO "variable_monitor IOCTL_DUMP_LOG_SA\n");
|
|
ret = dump_log(&dump_param, ioctl_param, &stand_alone_buffer);
|
|
if (ret) {
|
|
printk(KERN_INFO "dump_log failed\n");
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct file_operations fops = {
|
|
.open = device_open,
|
|
.release = device_release,
|
|
.unlocked_ioctl = device_ioctl,
|
|
};
|
|
|
|
int init_module(void) {
|
|
printk(KERN_INFO "variable_monitor fun: %s\n", __FUNCTION__);
|
|
if (alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME) < 0) {
|
|
printk(KERN_ALERT "Failed to register device number\n");
|
|
return -1;
|
|
}
|
|
|
|
if ((watch_class = class_create(THIS_MODULE, DEVICE_NAME)) == NULL) {
|
|
printk(KERN_ALERT "Failed to create class\n");
|
|
cdev_del(watch_cdev);
|
|
unregister_chrdev_region(dev_num, 1);
|
|
return -1;
|
|
}
|
|
|
|
if (device_create(watch_class, NULL, dev_num, NULL, DEVICE_NAME) == NULL) {
|
|
printk(KERN_ALERT "Failed to create device\n");
|
|
class_destroy(watch_class);
|
|
cdev_del(watch_cdev);
|
|
unregister_chrdev_region(dev_num, 1);
|
|
return -1;
|
|
}
|
|
|
|
if ((watch_cdev = cdev_alloc()) == NULL) {
|
|
printk(KERN_ALERT "Failed to allocate cdev structure\n");
|
|
unregister_chrdev_region(dev_num, 1);
|
|
return -1;
|
|
}
|
|
|
|
cdev_init(watch_cdev, &fops);
|
|
if (cdev_add(watch_cdev, dev_num, 1) == -1) {
|
|
printk(KERN_ALERT "Failed to add cdev structure\n");
|
|
device_destroy(watch_class, dev_num);
|
|
class_destroy(watch_class);
|
|
unregister_chrdev_region(dev_num, 1);
|
|
return -1;
|
|
}
|
|
|
|
printk(KERN_INFO "dev number: %d\n", dev_num);
|
|
printk(KERN_INFO "path: /dev/%s %d\n", DEVICE_NAME, dev_num);
|
|
|
|
// proc
|
|
monitor_proc_init();
|
|
|
|
// orig_X | buffer
|
|
monitor_init();
|
|
|
|
// orig_open_softirq(MY_SOFTIRQ, my_softirq_handler);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cleanup_module(void) {
|
|
printk(KERN_INFO "variable_monitor fun: %s\n", __FUNCTION__);
|
|
// clear all watch | free buffer
|
|
monitor_exit();
|
|
|
|
// proc
|
|
monitor_proc_exit();
|
|
|
|
// unmount
|
|
device_destroy(watch_class, dev_num);
|
|
class_destroy(watch_class);
|
|
cdev_del(watch_cdev);
|
|
unregister_chrdev_region(dev_num, 1);
|
|
|
|
// cleanup_perf_event(); // for perf
|
|
}
|
|
|
|
MODULE_LICENSE("GPL");
|