summaryrefslogtreecommitdiff
path: root/monitor_kernel.c
blob: 098bb80d9a5abaf0e6469cf4d813229c6e1e2d80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>

#include "monitor_kernel_lib.c"

#define DEVICE_NAME "variable_monitor"

// 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)
{
    // printk(KERN_INFO "%s\n", __FUNCTION__);
    struct my_device_data *data;
    // 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)
{
    // printk(KERN_INFO "%s\n", __FUNCTION__);
    // load pid
    struct my_device_data *data = file->private_data;
    // clear watch with pid
    clear_watch(data->pid);
    return 0;
}

static long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
    watch_arg warg;
    void *kptr;
    kernel_watch_timer *timer = NULL;
    kernel_watch_arg k_watch_arg;
    // 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);
    // user space address to kernel space address
    kptr = convert_user_space_ptr(warg.task_id, (unsigned long)warg.ptr);
    if (kptr == NULL)
    {
        printk(KERN_ERR "Cannot access user space\n");
        return -EACCES;
    }
    // check length
    if (warg.length_byte != 1 && warg.length_byte != 2 && warg.length_byte != 4 && warg.length_byte != 8)
    {
        printk(KERN_ERR "Invalid length %d\n", warg.length_byte);
        return -EINVAL;
    }
    // k_watch_arg init
    w_arg2k_w_arg(kptr, warg, &k_watch_arg);
    timer = get_timer(warg.time_ns); // get a valuable timer

    printk(KERN_INFO "ptr transform kptr: %p\n", kptr);
    printk(KERN_INFO "timer: %p\n", timer);
    printk(KERN_INFO "timer->sentinel: %d, timer->time_ns: %lld\n", timer->sentinel, timer->time_ns);
    printk(KERN_INFO "timer->hr_timer: %p\n", &timer->hr_timer);

    TIMER_CANCEL(timer); // just in case
    timer_add_watch(timer, k_watch_arg);
    TIMER_START(timer);

    printk(KERN_INFO "Start watching var: %s\n", warg.name);
    return 0;
}

static struct file_operations fops = {
    .open = device_open,
    .release = device_release,
    .unlocked_ioctl = device_ioctl,
};

int init_module(void)
{
    printk(KERN_INFO "%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_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;
    }

    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;
    }

    printk(KERN_INFO "dev number: %d\n", dev_num);
    printk(KERN_INFO "path: /dev/%s %d\n", DEVICE_NAME, dev_num);

    fn_kallsyms_lookup_name_init();    // init kallsyms_lookup_name
    LOOKUP_SYMS(stack_trace_save_tsk); // stack_trace_save_tsk
    LOOKUP_SYMS(show_stack);           // show_stack

    return 0;
}

void cleanup_module(void)
{
    printk(KERN_INFO "%s\n", __FUNCTION__);
    // clear all timer and page list
    clear_all_watch();
    // unmount
    device_destroy(watch_class, dev_num);
    class_destroy(watch_class);
    cdev_del(watch_cdev);
    unregister_chrdev_region(dev_num, 1);
}

MODULE_LICENSE("GPL");