xref: /linux/samples/trace_events/trace_custom_sched.c (revision 953c2f052112a857c00058a641dc0c58ec7551d4)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * event tracer
4  *
5  * Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org>
6  */
7 
8 #define pr_fmt(fmt) fmt
9 
10 #include <linux/trace_events.h>
11 #include <linux/version.h>
12 #include <linux/module.h>
13 #include <linux/sched.h>
14 #include <trace/events/sched.h>
15 
16 #define THIS_SYSTEM "custom_sched"
17 
18 #define SCHED_PRINT_FMT							\
19 	C("prev_prio=%d next_pid=%d next_prio=%d", REC->prev_prio, REC->next_pid, \
20 	  REC->next_prio)
21 
22 #define SCHED_WAKING_FMT				\
23 	C("pid=%d prio=%d", REC->pid, REC->prio)
24 
25 #undef C
26 #define C(a, b...) a, b
27 
28 static struct trace_event_fields sched_switch_fields[] = {
29 	{
30 		.type = "unsigned short",
31 		.name = "prev_prio",
32 		.size = sizeof(short),
33 		.align = __alignof__(short),
34 		.is_signed = 0,
35 		.filter_type = FILTER_OTHER,
36 	},
37 	{
38 		.type = "unsigned short",
39 		.name = "next_prio",
40 		.size = sizeof(short),
41 		.align = __alignof__(short),
42 		.is_signed = 0,
43 		.filter_type = FILTER_OTHER,
44 	},
45 	{
46 		.type = "unsigned int",
47 		.name = "next_prio",
48 		.size = sizeof(int),
49 		.align = __alignof__(int),
50 		.is_signed = 0,
51 		.filter_type = FILTER_OTHER,
52 	},
53 	{}
54 };
55 
56 struct sched_event {
57 	struct trace_entry	ent;
58 	unsigned short		prev_prio;
59 	unsigned short		next_prio;
60 	unsigned int		next_pid;
61 };
62 
63 static struct trace_event_fields sched_waking_fields[] = {
64 	{
65 		.type = "unsigned int",
66 		.name = "pid",
67 		.size = sizeof(int),
68 		.align = __alignof__(int),
69 		.is_signed = 0,
70 		.filter_type = FILTER_OTHER,
71 	},
72 	{
73 		.type = "unsigned short",
74 		.name = "prio",
75 		.size = sizeof(short),
76 		.align = __alignof__(short),
77 		.is_signed = 0,
78 		.filter_type = FILTER_OTHER,
79 	},
80 	{}
81 };
82 
83 struct wake_event {
84 	struct trace_entry	ent;
85 	unsigned int		pid;
86 	unsigned short		prio;
87 };
88 
89 static void sched_switch_probe(void *data, bool preempt, struct task_struct *prev,
90 			       struct task_struct *next)
91 {
92 	struct trace_event_file *trace_file = data;
93 	struct trace_event_buffer fbuffer;
94 	struct sched_event *entry;
95 
96 	if (trace_trigger_soft_disabled(trace_file))
97 		return;
98 
99 	entry = trace_event_buffer_reserve(&fbuffer, trace_file,
100 					   sizeof(*entry));
101 
102 	if (!entry)
103 		return;
104 
105 	entry->prev_prio = prev->prio;
106 	entry->next_prio = next->prio;
107 	entry->next_pid = next->pid;
108 
109 	trace_event_buffer_commit(&fbuffer);
110 }
111 
112 static struct trace_event_class sched_switch_class = {
113 	.system			= THIS_SYSTEM,
114 	.reg			= trace_event_reg,
115 	.fields_array		= sched_switch_fields,
116 	.fields			= LIST_HEAD_INIT(sched_switch_class.fields),
117 	.probe			= sched_switch_probe,
118 };
119 
120 static void sched_waking_probe(void *data, struct task_struct *t)
121 {
122 	struct trace_event_file *trace_file = data;
123 	struct trace_event_buffer fbuffer;
124 	struct wake_event *entry;
125 
126 	if (trace_trigger_soft_disabled(trace_file))
127 		return;
128 
129 	entry = trace_event_buffer_reserve(&fbuffer, trace_file,
130 					   sizeof(*entry));
131 
132 	if (!entry)
133 		return;
134 
135 	entry->prio = t->prio;
136 	entry->pid = t->pid;
137 
138 	trace_event_buffer_commit(&fbuffer);
139 }
140 
141 static struct trace_event_class sched_waking_class = {
142 	.system			= THIS_SYSTEM,
143 	.reg			= trace_event_reg,
144 	.fields_array		= sched_waking_fields,
145 	.fields			= LIST_HEAD_INIT(sched_waking_class.fields),
146 	.probe			= sched_waking_probe,
147 };
148 
149 static enum print_line_t sched_switch_output(struct trace_iterator *iter, int flags,
150 					     struct trace_event *trace_event)
151 {
152 	struct trace_seq *s = &iter->seq;
153 	struct sched_event *REC = (struct sched_event *)iter->ent;
154 	int ret;
155 
156 	ret = trace_raw_output_prep(iter, trace_event);
157 	if (ret != TRACE_TYPE_HANDLED)
158 		return ret;
159 
160 	trace_seq_printf(s, SCHED_PRINT_FMT);
161 	trace_seq_putc(s, '\n');
162 
163 	return trace_handle_return(s);
164 }
165 
166 static struct trace_event_functions sched_switch_funcs = {
167 	.trace			= sched_switch_output,
168 };
169 
170 static enum print_line_t sched_waking_output(struct trace_iterator *iter, int flags,
171 					     struct trace_event *trace_event)
172 {
173 	struct trace_seq *s = &iter->seq;
174 	struct wake_event *REC = (struct wake_event *)iter->ent;
175 	int ret;
176 
177 	ret = trace_raw_output_prep(iter, trace_event);
178 	if (ret != TRACE_TYPE_HANDLED)
179 		return ret;
180 
181 	trace_seq_printf(s, SCHED_WAKING_FMT);
182 	trace_seq_putc(s, '\n');
183 
184 	return trace_handle_return(s);
185 }
186 
187 static struct trace_event_functions sched_waking_funcs = {
188 	.trace			= sched_waking_output,
189 };
190 
191 #undef C
192 #define C(a, b...) #a "," __stringify(b)
193 
194 static struct trace_event_call sched_switch_call = {
195 	.class			= &sched_switch_class,
196 	.event			= {
197 		.funcs			= &sched_switch_funcs,
198 	},
199 	.print_fmt		= SCHED_PRINT_FMT,
200 	.module			= THIS_MODULE,
201 	.flags			= TRACE_EVENT_FL_TRACEPOINT,
202 };
203 
204 static struct trace_event_call sched_waking_call = {
205 	.class			= &sched_waking_class,
206 	.event			= {
207 		.funcs			= &sched_waking_funcs,
208 	},
209 	.print_fmt		= SCHED_WAKING_FMT,
210 	.module			= THIS_MODULE,
211 	.flags			= TRACE_EVENT_FL_TRACEPOINT,
212 };
213 
214 static void fct(struct tracepoint *tp, void *priv)
215 {
216 	if (tp->name && strcmp(tp->name, "sched_switch") == 0)
217 		sched_switch_call.tp = tp;
218 	else if (tp->name && strcmp(tp->name, "sched_waking") == 0)
219 		sched_waking_call.tp = tp;
220 }
221 
222 static int add_event(struct trace_event_call *call)
223 {
224 	int ret;
225 
226 	ret = register_trace_event(&call->event);
227 	if (WARN_ON(!ret))
228 		return -ENODEV;
229 
230 	ret = trace_add_event_call(call);
231 	if (WARN_ON(ret))
232 		unregister_trace_event(&call->event);
233 
234 	return ret;
235 }
236 
237 static int __init trace_sched_init(void)
238 {
239 	int ret;
240 
241 	check_trace_callback_type_sched_switch(sched_switch_probe);
242 	check_trace_callback_type_sched_waking(sched_waking_probe);
243 
244 	for_each_kernel_tracepoint(fct, NULL);
245 
246 	ret = add_event(&sched_switch_call);
247 	if (ret)
248 		return ret;
249 
250 	ret = add_event(&sched_waking_call);
251 	if (ret)
252 		trace_remove_event_call(&sched_switch_call);
253 
254 	return ret;
255 }
256 
257 static void __exit trace_sched_exit(void)
258 {
259 	trace_set_clr_event(THIS_SYSTEM, "sched_switch", 0);
260 	trace_set_clr_event(THIS_SYSTEM, "sched_waking", 0);
261 
262 	trace_remove_event_call(&sched_switch_call);
263 	trace_remove_event_call(&sched_waking_call);
264 }
265 
266 module_init(trace_sched_init);
267 module_exit(trace_sched_exit);
268 
269 MODULE_AUTHOR("Steven Rostedt");
270 MODULE_DESCRIPTION("Custom scheduling events");
271 MODULE_LICENSE("GPL");
272