1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <[email protected]>
4 */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "event-parse.h"
10 #include "trace-seq.h"
11
12 /*
13 * prev_state is of size long, which is 32 bits on 32 bit architectures.
14 * As it needs to have the same bits for both 32 bit and 64 bit architectures
15 * we can just assume that the flags we care about will all be within
16 * the 32 bits.
17 */
18 #define MAX_STATE_BITS 32
19
convert_sym(struct tep_print_flag_sym * sym)20 static const char *convert_sym(struct tep_print_flag_sym *sym)
21 {
22 static char save_states[MAX_STATE_BITS + 1];
23
24 memset(save_states, 0, sizeof(save_states));
25
26 /* This is the flags for the prev_state_field, now make them into a string */
27 for (; sym; sym = sym->next) {
28 long bitmask = strtoul(sym->value, NULL, 0);
29 int i;
30
31 for (i = 0; !(bitmask & 1); i++)
32 bitmask >>= 1;
33
34 if (i >= MAX_STATE_BITS)
35 continue;
36
37 save_states[i] = sym->str[0];
38 }
39
40 return save_states;
41 }
42
43 static struct tep_print_arg_field *
find_arg_field(struct tep_format_field * prev_state_field,struct tep_print_arg * arg)44 find_arg_field(struct tep_format_field *prev_state_field, struct tep_print_arg *arg)
45 {
46 struct tep_print_arg_field *field;
47
48 if (!arg)
49 return NULL;
50
51 if (arg->type == TEP_PRINT_FIELD)
52 return &arg->field;
53
54 if (arg->type == TEP_PRINT_OP) {
55 field = find_arg_field(prev_state_field, arg->op.left);
56 if (field && field->field == prev_state_field)
57 return field;
58 field = find_arg_field(prev_state_field, arg->op.right);
59 if (field && field->field == prev_state_field)
60 return field;
61 }
62 return NULL;
63 }
64
65 static struct tep_print_flag_sym *
test_flags(struct tep_format_field * prev_state_field,struct tep_print_arg * arg)66 test_flags(struct tep_format_field *prev_state_field, struct tep_print_arg *arg)
67 {
68 struct tep_print_arg_field *field;
69
70 field = find_arg_field(prev_state_field, arg->flags.field);
71 if (!field)
72 return NULL;
73
74 return arg->flags.flags;
75 }
76
77 static struct tep_print_flag_sym *
search_op(struct tep_format_field * prev_state_field,struct tep_print_arg * arg)78 search_op(struct tep_format_field *prev_state_field, struct tep_print_arg *arg)
79 {
80 struct tep_print_flag_sym *sym = NULL;
81
82 if (!arg)
83 return NULL;
84
85 if (arg->type == TEP_PRINT_OP) {
86 sym = search_op(prev_state_field, arg->op.left);
87 if (sym)
88 return sym;
89
90 sym = search_op(prev_state_field, arg->op.right);
91 if (sym)
92 return sym;
93 } else if (arg->type == TEP_PRINT_FLAGS) {
94 sym = test_flags(prev_state_field, arg);
95 }
96
97 return sym;
98 }
99
get_states(struct tep_format_field * prev_state_field)100 static const char *get_states(struct tep_format_field *prev_state_field)
101 {
102 struct tep_print_flag_sym *sym;
103 struct tep_print_arg *arg;
104 struct tep_event *event;
105
106 event = prev_state_field->event;
107
108 /*
109 * Look at the event format fields, and search for where
110 * the prev_state is parsed via the format flags.
111 */
112 for (arg = event->print_fmt.args; arg; arg = arg->next) {
113 /*
114 * Currently, the __print_flags() for the prev_state
115 * is embedded in operations, so they too must be
116 * searched.
117 */
118 sym = search_op(prev_state_field, arg);
119 if (sym)
120 return convert_sym(sym);
121 }
122 return NULL;
123 }
124
write_state(struct trace_seq * s,struct tep_format_field * field,struct tep_record * record)125 static void write_state(struct trace_seq *s, struct tep_format_field *field,
126 struct tep_record *record)
127 {
128 static struct tep_format_field *prev_state_field;
129 static const char *states;
130 unsigned long long val;
131 int found = 0;
132 int len;
133 int i;
134
135 if (!field)
136 return;
137
138 if (!states || field != prev_state_field) {
139 states = get_states(field);
140 if (!states)
141 states = "SDTtXZPI";
142 prev_state_field = field;
143 }
144
145 tep_read_number_field(field, record->data, &val);
146
147 len = strlen(states);
148 for (i = 0; i < len; i++) {
149 if (!(val & (1 << i)))
150 continue;
151
152 if (found)
153 trace_seq_putc(s, '|');
154
155 found = 1;
156 trace_seq_putc(s, states[i]);
157 }
158
159 if (!found)
160 trace_seq_putc(s, 'R');
161 }
162
write_and_save_comm(struct tep_format_field * field,struct tep_record * record,struct trace_seq * s,int pid)163 static void write_and_save_comm(struct tep_format_field *field,
164 struct tep_record *record,
165 struct trace_seq *s, int pid)
166 {
167 const char *comm;
168 int len;
169
170 comm = (char *)(record->data + field->offset);
171 len = s->len;
172 trace_seq_printf(s, "%.*s",
173 field->size, comm);
174
175 /* make sure the comm has a \0 at the end. */
176 trace_seq_terminate(s);
177 comm = &s->buffer[len];
178
179 /* Help out the comm to ids. This will handle dups */
180 tep_register_comm(field->event->tep, comm, pid);
181 }
182
sched_wakeup_handler(struct trace_seq * s,struct tep_record * record,struct tep_event * event,void * context)183 static int sched_wakeup_handler(struct trace_seq *s,
184 struct tep_record *record,
185 struct tep_event *event, void *context)
186 {
187 struct tep_format_field *field;
188 unsigned long long val;
189
190 if (tep_get_field_val(s, event, "pid", record, &val, 1))
191 return trace_seq_putc(s, '!');
192
193 field = tep_find_any_field(event, "comm");
194 if (field) {
195 write_and_save_comm(field, record, s, val);
196 trace_seq_putc(s, ':');
197 }
198 trace_seq_printf(s, "%lld", val);
199
200 if (tep_get_field_val(s, event, "prio", record, &val, 1) == 0)
201 trace_seq_printf(s, " [%lld]", val);
202
203 if (tep_get_field_val(s, event, "success", record, &val, 0) == 0)
204 trace_seq_printf(s, " success=%lld", val);
205
206 if (tep_get_field_val(s, event, "target_cpu", record, &val, 0) == 0)
207 trace_seq_printf(s, " CPU:%03llu", val);
208
209 return 0;
210 }
211
sched_switch_handler(struct trace_seq * s,struct tep_record * record,struct tep_event * event,void * context)212 static int sched_switch_handler(struct trace_seq *s,
213 struct tep_record *record,
214 struct tep_event *event, void *context)
215 {
216 struct tep_format_field *field;
217 unsigned long long val;
218
219 if (tep_get_field_val(s, event, "prev_pid", record, &val, 1))
220 return trace_seq_putc(s, '!');
221
222 field = tep_find_any_field(event, "prev_comm");
223 if (field) {
224 write_and_save_comm(field, record, s, val);
225 trace_seq_putc(s, ':');
226 }
227 trace_seq_printf(s, "%lld ", val);
228
229 if (tep_get_field_val(s, event, "prev_prio", record, &val, 1) == 0)
230 trace_seq_printf(s, "[%d] ", (int) val);
231
232 field = tep_find_any_field(event, "prev_state");
233 write_state(s, field, record);
234
235 trace_seq_puts(s, " ==> ");
236
237 if (tep_get_field_val(s, event, "next_pid", record, &val, 1))
238 return trace_seq_putc(s, '!');
239
240 field = tep_find_any_field(event, "next_comm");
241 if (field) {
242 write_and_save_comm(field, record, s, val);
243 trace_seq_putc(s, ':');
244 }
245 trace_seq_printf(s, "%lld", val);
246
247 if (tep_get_field_val(s, event, "next_prio", record, &val, 1) == 0)
248 trace_seq_printf(s, " [%d]", (int) val);
249
250 return 0;
251 }
252
TEP_PLUGIN_LOADER(struct tep_handle * tep)253 int TEP_PLUGIN_LOADER(struct tep_handle *tep)
254 {
255 tep_register_event_handler(tep, -1, "sched", "sched_switch",
256 sched_switch_handler, NULL);
257
258 tep_register_event_handler(tep, -1, "sched", "sched_wakeup",
259 sched_wakeup_handler, NULL);
260
261 tep_register_event_handler(tep, -1, "sched", "sched_wakeup_new",
262 sched_wakeup_handler, NULL);
263 return 0;
264 }
265
TEP_PLUGIN_UNLOADER(struct tep_handle * tep)266 void TEP_PLUGIN_UNLOADER(struct tep_handle *tep)
267 {
268 tep_unregister_event_handler(tep, -1, "sched", "sched_switch",
269 sched_switch_handler, NULL);
270
271 tep_unregister_event_handler(tep, -1, "sched", "sched_wakeup",
272 sched_wakeup_handler, NULL);
273
274 tep_unregister_event_handler(tep, -1, "sched", "sched_wakeup_new",
275 sched_wakeup_handler, NULL);
276 }
277