Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/samples/ftrace/ftrace-ops.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
3
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4
5
#include <linux/ftrace.h>
6
#include <linux/ktime.h>
7
#include <linux/module.h>
8
9
#include <asm/barrier.h>
10
11
/*
12
* Arbitrary large value chosen to be sufficiently large to minimize noise but
13
* sufficiently small to complete quickly.
14
*/
15
static unsigned int nr_function_calls = 100000;
16
module_param(nr_function_calls, uint, 0);
17
MODULE_PARM_DESC(nr_function_calls, "How many times to call the relevant tracee");
18
19
/*
20
* The number of ops associated with a call site affects whether a tracer can
21
* be called directly or whether it's necessary to go via the list func, which
22
* can be significantly more expensive.
23
*/
24
static unsigned int nr_ops_relevant = 1;
25
module_param(nr_ops_relevant, uint, 0);
26
MODULE_PARM_DESC(nr_ops_relevant, "How many ftrace_ops to associate with the relevant tracee");
27
28
/*
29
* On architectures where all call sites share the same trampoline, having
30
* tracers enabled for distinct functions can force the use of the list func
31
* and incur overhead for all call sites.
32
*/
33
static unsigned int nr_ops_irrelevant;
34
module_param(nr_ops_irrelevant, uint, 0);
35
MODULE_PARM_DESC(nr_ops_irrelevant, "How many ftrace_ops to associate with the irrelevant tracee");
36
37
/*
38
* On architectures with DYNAMIC_FTRACE_WITH_REGS, saving the full pt_regs can
39
* be more expensive than only saving the minimal necessary regs.
40
*/
41
static bool save_regs;
42
module_param(save_regs, bool, 0);
43
MODULE_PARM_DESC(save_regs, "Register ops with FTRACE_OPS_FL_SAVE_REGS (save all registers in the trampoline)");
44
45
static bool assist_recursion;
46
module_param(assist_recursion, bool, 0);
47
MODULE_PARM_DESC(assist_reursion, "Register ops with FTRACE_OPS_FL_RECURSION");
48
49
static bool assist_rcu;
50
module_param(assist_rcu, bool, 0);
51
MODULE_PARM_DESC(assist_reursion, "Register ops with FTRACE_OPS_FL_RCU");
52
53
/*
54
* By default, a trivial tracer is used which immediately returns to mimimize
55
* overhead. Sometimes a consistency check using a more expensive tracer is
56
* desireable.
57
*/
58
static bool check_count;
59
module_param(check_count, bool, 0);
60
MODULE_PARM_DESC(check_count, "Check that tracers are called the expected number of times\n");
61
62
/*
63
* Usually it's not interesting to leave the ops registered after the test
64
* runs, but sometimes it can be useful to leave them registered so that they
65
* can be inspected through the tracefs 'enabled_functions' file.
66
*/
67
static bool persist;
68
module_param(persist, bool, 0);
69
MODULE_PARM_DESC(persist, "Successfully load module and leave ftrace ops registered after test completes\n");
70
71
/*
72
* Marked as noinline to ensure that an out-of-line traceable copy is
73
* generated by the compiler.
74
*
75
* The barrier() ensures the compiler won't elide calls by determining there
76
* are no side-effects.
77
*/
78
static noinline void tracee_relevant(void)
79
{
80
barrier();
81
}
82
83
/*
84
* Marked as noinline to ensure that an out-of-line traceable copy is
85
* generated by the compiler.
86
*
87
* The barrier() ensures the compiler won't elide calls by determining there
88
* are no side-effects.
89
*/
90
static noinline void tracee_irrelevant(void)
91
{
92
barrier();
93
}
94
95
struct sample_ops {
96
struct ftrace_ops ops;
97
unsigned int count;
98
};
99
100
static void ops_func_nop(unsigned long ip, unsigned long parent_ip,
101
struct ftrace_ops *op,
102
struct ftrace_regs *fregs)
103
{
104
/* do nothing */
105
}
106
107
static void ops_func_count(unsigned long ip, unsigned long parent_ip,
108
struct ftrace_ops *op,
109
struct ftrace_regs *fregs)
110
{
111
struct sample_ops *self;
112
113
self = container_of(op, struct sample_ops, ops);
114
self->count++;
115
}
116
117
static struct sample_ops *ops_relevant;
118
static struct sample_ops *ops_irrelevant;
119
120
static struct sample_ops *ops_alloc_init(void *tracee, ftrace_func_t func,
121
unsigned long flags, int nr)
122
{
123
struct sample_ops *ops;
124
125
ops = kcalloc(nr, sizeof(*ops), GFP_KERNEL);
126
if (WARN_ON_ONCE(!ops))
127
return NULL;
128
129
for (unsigned int i = 0; i < nr; i++) {
130
ops[i].ops.func = func;
131
ops[i].ops.flags = flags;
132
WARN_ON_ONCE(ftrace_set_filter_ip(&ops[i].ops, (unsigned long)tracee, 0, 0));
133
WARN_ON_ONCE(register_ftrace_function(&ops[i].ops));
134
}
135
136
return ops;
137
}
138
139
static void ops_destroy(struct sample_ops *ops, int nr)
140
{
141
if (!ops)
142
return;
143
144
for (unsigned int i = 0; i < nr; i++) {
145
WARN_ON_ONCE(unregister_ftrace_function(&ops[i].ops));
146
ftrace_free_filter(&ops[i].ops);
147
}
148
149
kfree(ops);
150
}
151
152
static void ops_check(struct sample_ops *ops, int nr,
153
unsigned int expected_count)
154
{
155
if (!ops || !check_count)
156
return;
157
158
for (unsigned int i = 0; i < nr; i++) {
159
if (ops->count == expected_count)
160
continue;
161
pr_warn("Counter called %u times (expected %u)\n",
162
ops->count, expected_count);
163
}
164
}
165
166
static ftrace_func_t tracer_relevant = ops_func_nop;
167
static ftrace_func_t tracer_irrelevant = ops_func_nop;
168
169
static int __init ftrace_ops_sample_init(void)
170
{
171
unsigned long flags = 0;
172
ktime_t start, end;
173
u64 period;
174
175
if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS) && save_regs) {
176
pr_info("this kernel does not support saving registers\n");
177
save_regs = false;
178
} else if (save_regs) {
179
flags |= FTRACE_OPS_FL_SAVE_REGS;
180
}
181
182
if (assist_recursion)
183
flags |= FTRACE_OPS_FL_RECURSION;
184
185
if (assist_rcu)
186
flags |= FTRACE_OPS_FL_RCU;
187
188
if (check_count) {
189
tracer_relevant = ops_func_count;
190
tracer_irrelevant = ops_func_count;
191
}
192
193
pr_info("registering:\n"
194
" relevant ops: %u\n"
195
" tracee: %ps\n"
196
" tracer: %ps\n"
197
" irrelevant ops: %u\n"
198
" tracee: %ps\n"
199
" tracer: %ps\n"
200
" saving registers: %s\n"
201
" assist recursion: %s\n"
202
" assist RCU: %s\n",
203
nr_ops_relevant, tracee_relevant, tracer_relevant,
204
nr_ops_irrelevant, tracee_irrelevant, tracer_irrelevant,
205
save_regs ? "YES" : "NO",
206
assist_recursion ? "YES" : "NO",
207
assist_rcu ? "YES" : "NO");
208
209
ops_relevant = ops_alloc_init(tracee_relevant, tracer_relevant,
210
flags, nr_ops_relevant);
211
ops_irrelevant = ops_alloc_init(tracee_irrelevant, tracer_irrelevant,
212
flags, nr_ops_irrelevant);
213
214
start = ktime_get();
215
for (unsigned int i = 0; i < nr_function_calls; i++)
216
tracee_relevant();
217
end = ktime_get();
218
219
ops_check(ops_relevant, nr_ops_relevant, nr_function_calls);
220
ops_check(ops_irrelevant, nr_ops_irrelevant, 0);
221
222
period = ktime_to_ns(ktime_sub(end, start));
223
224
pr_info("Attempted %u calls to %ps in %lluns (%lluns / call)\n",
225
nr_function_calls, tracee_relevant,
226
period, div_u64(period, nr_function_calls));
227
228
if (persist)
229
return 0;
230
231
ops_destroy(ops_relevant, nr_ops_relevant);
232
ops_destroy(ops_irrelevant, nr_ops_irrelevant);
233
234
/*
235
* The benchmark completed sucessfully, but there's no reason to keep
236
* the module around. Return an error do the user doesn't have to
237
* manually unload the module.
238
*/
239
return -EINVAL;
240
}
241
module_init(ftrace_ops_sample_init);
242
243
static void __exit ftrace_ops_sample_exit(void)
244
{
245
ops_destroy(ops_relevant, nr_ops_relevant);
246
ops_destroy(ops_irrelevant, nr_ops_irrelevant);
247
}
248
module_exit(ftrace_ops_sample_exit);
249
250
MODULE_AUTHOR("Mark Rutland");
251
MODULE_DESCRIPTION("Example of using custom ftrace_ops");
252
MODULE_LICENSE("GPL");
253
254