Blame view

samples/kprobes/kretprobe_example.c 2.93 KB
804defea1   Ananth N Mavinakayanahalli   Kprobes: move kpr...
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
  /*
   * kretprobe_example.c
   *
   * Here's a sample kernel module showing the use of return probes to
   * report the return value and total time taken for probed function
   * to run.
   *
   * usage: insmod kretprobe_example.ko func=<func_name>
   *
   * If no func_name is specified, do_fork is instrumented
   *
   * For more information on theory of operation of kretprobes, see
   * Documentation/kprobes.txt
   *
   * Build and insert the kernel module as done in the kprobe example.
   * You will see the trace data in /var/log/messages and on the console
   * whenever the probed function returns. (Some messages may be suppressed
   * if syslogd is configured to eliminate duplicate messages.)
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/kprobes.h>
  #include <linux/ktime.h>
  #include <linux/limits.h>
8abf91960   Alexey Dobriyan   sparc64: cheaper ...
26
  #include <linux/sched.h>
804defea1   Ananth N Mavinakayanahalli   Kprobes: move kpr...
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
  
  static char func_name[NAME_MAX] = "do_fork";
  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");
  
  /* per-instance private data */
  struct my_data {
  	ktime_t entry_stamp;
  };
  
  /* Here we use the entry_hanlder to timestamp function entry */
  static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
  {
  	struct my_data *data;
  
  	if (!current->mm)
  		return 1;	/* Skip kernel threads */
  
  	data = (struct my_data *)ri->data;
  	data->entry_stamp = ktime_get();
  	return 0;
  }
  
  /*
   * Return-probe handler: Log the return value and duration. Duration may turn
   * out to be zero consistently, depending upon the granularity of time
   * accounting on the platform.
   */
  static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
  {
  	int retval = regs_return_value(regs);
  	struct my_data *data = (struct my_data *)ri->data;
  	s64 delta;
  	ktime_t now;
  
  	now = ktime_get();
  	delta = ktime_to_ns(ktime_sub(now, data->entry_stamp));
  	printk(KERN_INFO "%s returned %d and took %lld ns to execute
  ",
  			func_name, retval, (long long)delta);
  	return 0;
  }
  
  static struct kretprobe my_kretprobe = {
  	.handler		= ret_handler,
  	.entry_handler		= entry_handler,
  	.data_size		= sizeof(struct my_data),
  	/* Probe up to 20 instances concurrently. */
  	.maxactive		= 20,
  };
  
  static int __init kretprobe_init(void)
  {
  	int ret;
  
  	my_kretprobe.kp.symbol_name = func_name;
  	ret = register_kretprobe(&my_kretprobe);
  	if (ret < 0) {
  		printk(KERN_INFO "register_kretprobe failed, returned %d
  ",
  				ret);
  		return -1;
  	}
  	printk(KERN_INFO "Planted return probe at %s: %p
  ",
  			my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);
  	return 0;
  }
  
  static void __exit kretprobe_exit(void)
  {
  	unregister_kretprobe(&my_kretprobe);
  	printk(KERN_INFO "kretprobe at %p unregistered
  ",
  			my_kretprobe.kp.addr);
  
  	/* nmissed > 0 suggests that maxactive was set too low. */
  	printk(KERN_INFO "Missed probing %d instances of %s
  ",
  		my_kretprobe.nmissed, my_kretprobe.kp.symbol_name);
  }
  
  module_init(kretprobe_init)
  module_exit(kretprobe_exit)
  MODULE_LICENSE("GPL");