Skip to content

Instantly share code, notes, and snippets.

@pwq1989
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pwq1989/fb1e89835ecafaa50568 to your computer and use it in GitHub Desktop.
Save pwq1989/fb1e89835ecafaa50568 to your computer and use it in GitHub Desktop.
a kernel module sample process's wall-time
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/pid.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/smp.h>
#include <linux/cpumask.h>
#include <linux/kthread.h>
#include <linux/string.h>
#include <asm/processor.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#define NOT_USED( x ) (void*)(x)
#define THREAD_NAME "walltime_thread"
#define TEST_PID_FILE "/home/gin/code/kernel-sample/pid.file"
#define SLEEP_TIME 4000
#define BUF_SIZE 32
// status
enum {
IS_IN_SAMPLING = 0,
IS_NOT_IN_SAMPLING = 1
};
struct walltime_struct {
struct perf_event **events;
unsigned int *flags;
unsigned int cpu_number;
atomic_t count_down;
unsigned int sample_count;
atomic_t is_sampling;
};
static struct walltime_struct _wts;
void wts_init(struct walltime_struct *wts) {
wts->events = NULL;
wts->flags = __alloc_percpu(sizeof(unsigned int), 8);
wts->cpu_number = num_online_cpus();
atomic_set(&wts->count_down, wts->cpu_number);
atomic_set(&wts->is_sampling, IS_NOT_IN_SAMPLING);
wts->sample_count = 0;
}
void wts_reset(struct walltime_struct *wts) {
int cpu;
wts->events = NULL;
wts->cpu_number = num_online_cpus();
atomic_set(&wts->count_down, wts->cpu_number);
atomic_set(&wts->is_sampling, IS_NOT_IN_SAMPLING);
wts->sample_count++;
for_each_possible_cpu(cpu) {
unsigned int *flag = per_cpu_ptr(wts->flags, cpu);
if (*flag) {
*flag = 0;
}
}
}
void wts_set_start(struct walltime_struct *wts,
struct perf_event** events) {
wts->events = events;
atomic_set(&wts->is_sampling, IS_IN_SAMPLING);
}
static struct task_struct *ts;
static volatile bool stop; // tag for task finish
// file read buf, just for example
static char buf[BUF_SIZE];
// just an example
// read pid information from a file for test
static int read_update_pid(void) {
char filename[] = TEST_PID_FILE;
struct file *filp;
struct inode *inode;
off_t fsize;
mm_segment_t fs;
long pid;
printk(KERN_INFO "read file start.. \n");
filp = filp_open(filename, O_RDONLY, 0);
inode=filp->f_dentry->d_inode;
fsize = inode->i_size;
BUG_ON(fsize > BUF_SIZE); // overflow
fs = get_fs();
set_fs(KERNEL_DS);
filp->f_op->read(filp, buf, fsize, &(filp->f_pos));
set_fs(fs);
buf[fsize] = '\0';
filp_close(filp, NULL);
if (strict_strtoul(buf, 0, &pid) == 0) { // success
printk(KERN_INFO "target pid is %d\n ", (int)pid);
return (int)pid;
} else {
return -1;
}
}
static struct perf_event_attr attr;
static atomic_t count;
static void del_perf_event(struct walltime_struct*);
void walltime_perf_callback(struct perf_event *event,
struct perf_sample_data *data,
struct pt_regs *regs) {
int cpu;
int *flag;
cpu = get_cpu();
flag = per_cpu_ptr(_wts.flags, cpu);
if (*flag == 0) {
*flag = 1;
atomic_dec(&_wts.count_down);
}
put_cpu();
atomic_inc(&count);
}
// perf event example
static int register_perf_event(struct walltime_struct *wts) {
int cpu;
struct perf_event **events;
printk(KERN_INFO "before create event .\n");
memset(&attr, 0, sizeof(attr));
attr.size = sizeof(struct perf_event_attr);
attr.config = 0ULL;
attr.type = 1ULL;
attr.sample_period = 10000ULL;
events = __alloc_percpu(sizeof(struct walltime_struct*), 8);
if (events == NULL) return -1;
for_each_possible_cpu(cpu) {
struct perf_event **event = per_cpu_ptr(events, cpu);
if (cpu_is_offline(cpu)) {
*event = NULL;
continue;
}
*event = perf_event_create_kernel_counter(&attr, cpu, NULL, walltime_perf_callback, NULL);
if (IS_ERR(*event)) {
printk (KERN_INFO "create perf event failure\n");
return -1;
}
}
wts_set_start(wts, events);
return 0;
}
static void del_perf_event(struct walltime_struct *wts) {
int cpu;
for_each_possible_cpu(cpu) {
struct perf_event **event = per_cpu_ptr(wts->events, cpu);
if (*event) {
perf_event_release_kernel(*event);
}
}
free_percpu(wts->events);
wts_reset(wts);
}
static int sample_task_func(void* param) {
struct pid *pid_c;
struct task_struct *ts_c;
struct task_struct *p;
int pid_num;
NOT_USED(param);
printk(KERN_INFO "sample start.. \n");
while (!stop || !kthread_should_stop()) {
msleep(SLEEP_TIME);
//printk(KERN_INFO "schedule is called. \n");
pid_num = read_update_pid();
if (pid_num == -1) {
continue;
}
rcu_read_lock();
pid_c = find_vpid(pid_num);
if (pid_c == NULL) {
continue;
}
ts_c = pid_task(pid_c, PIDTYPE_PID);
rcu_read_unlock();
register_perf_event(&_wts);
printk(KERN_INFO "finish register!!! \n");
while (atomic_read(&_wts.count_down) > 0) {
msleep(1); // wait for finish
}
del_perf_event(&_wts);
rcu_read_lock();
// print main thread
printk(KERN_INFO "target pid is %d, rip is %#lx, rbp is %#lx \n ",
ts_c->pid, task_pt_regs(ts_c)->ip, task_pt_regs(ts_c)->bp);
// print other thread
for (p = next_thread(ts_c);
p != ts_c; p = next_thread(p)) {
printk(KERN_INFO "target pid is %d, rip is %#lx, rbp is %#lx \n ",
p->pid, task_pt_regs(p)->ip, task_pt_regs(p)->bp);
}
rcu_read_unlock();
printk(KERN_INFO "end!!! \n");
}
return 0;
}
/* Init function called on module entry */
int walltime_module_init(void) {
char thread_name[] = THREAD_NAME;
stop = false;
wts_init(&_wts);
ts = kthread_create(sample_task_func, NULL, thread_name);
wake_up_process(ts);
printk(KERN_INFO "walltime_module_init called. Module is now loaded.\n");
return 0;
}
/* Cleanup function called on module exit */
void walltime_module_cleanup(void) {
int ret;
// set stop
stop = true;
if (ts != NULL) {
ret = kthread_stop(ts); // for test
}
//printk(KERN_INFO "count is %d\n", atomic_read(&count));
printk(KERN_INFO "walltime_module_cleanup called. Module is now unloaded.\n");
return;
}
/* Declare entry and exit functions */
module_init(walltime_module_init);
module_exit(walltime_module_cleanup);
/* Defines the license for this linux kernel module */
MODULE_LICENSE("GPL");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment