xref: /linux/tools/tracing/rtla/src/osnoise_top.c (revision 5d9af63e80b5a202e69ce5bcf54af320e46f397a)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
4  */
5 
6 #define _GNU_SOURCE
7 #include <stdlib.h>
8 #include <string.h>
9 #include <signal.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <time.h>
13 
14 #include "osnoise.h"
15 #include "cli.h"
16 
17 struct osnoise_top_cpu {
18 	unsigned long long	sum_runtime;
19 	unsigned long long	sum_noise;
20 	unsigned long long	max_noise;
21 	unsigned long long	max_sample;
22 
23 	unsigned long long	hw_count;
24 	unsigned long long	nmi_count;
25 	unsigned long long	irq_count;
26 	unsigned long long	softirq_count;
27 	unsigned long long	thread_count;
28 
29 	int			sum_cycles;
30 };
31 
32 struct osnoise_top_data {
33 	struct osnoise_top_cpu	*cpu_data;
34 };
35 
36 /*
37  * osnoise_free_top - free runtime data
38  */
39 static void osnoise_free_top(struct osnoise_top_data *data)
40 {
41 	free(data->cpu_data);
42 	free(data);
43 }
44 
45 static void osnoise_free_top_tool(struct osnoise_tool *tool)
46 {
47 	osnoise_free_top(tool->data);
48 }
49 
50 /*
51  * osnoise_alloc_histogram - alloc runtime data
52  */
53 static struct osnoise_top_data *osnoise_alloc_top(void)
54 {
55 	struct osnoise_top_data *data;
56 
57 	data = calloc(1, sizeof(*data));
58 	if (!data)
59 		return NULL;
60 
61 	/* one set of histograms per CPU */
62 	data->cpu_data = calloc(1, sizeof(*data->cpu_data) * nr_cpus);
63 	if (!data->cpu_data)
64 		goto cleanup;
65 
66 	return data;
67 
68 cleanup:
69 	osnoise_free_top(data);
70 	return NULL;
71 }
72 
73 /*
74  * osnoise_top_handler - this is the handler for osnoise tracer events
75  */
76 static int
77 osnoise_top_handler(struct trace_seq *s, struct tep_record *record,
78 		    struct tep_event *event, void *context)
79 {
80 	struct trace_instance *trace = context;
81 	struct osnoise_tool *tool;
82 	unsigned long long val;
83 	struct osnoise_top_cpu *cpu_data;
84 	struct osnoise_top_data *data;
85 	int cpu = record->cpu;
86 
87 	tool = container_of(trace, struct osnoise_tool, trace);
88 
89 	data = tool->data;
90 	cpu_data = &data->cpu_data[cpu];
91 
92 	cpu_data->sum_cycles++;
93 
94 	tep_get_field_val(s, event, "runtime", record, &val, 1);
95 	update_sum(&cpu_data->sum_runtime, &val);
96 
97 	tep_get_field_val(s, event, "noise", record, &val, 1);
98 	update_max(&cpu_data->max_noise, &val);
99 	update_sum(&cpu_data->sum_noise, &val);
100 
101 	tep_get_field_val(s, event, "max_sample", record, &val, 1);
102 	update_max(&cpu_data->max_sample, &val);
103 
104 	tep_get_field_val(s, event, "hw_count", record, &val, 1);
105 	update_sum(&cpu_data->hw_count, &val);
106 
107 	tep_get_field_val(s, event, "nmi_count", record, &val, 1);
108 	update_sum(&cpu_data->nmi_count, &val);
109 
110 	tep_get_field_val(s, event, "irq_count", record, &val, 1);
111 	update_sum(&cpu_data->irq_count, &val);
112 
113 	tep_get_field_val(s, event, "softirq_count", record, &val, 1);
114 	update_sum(&cpu_data->softirq_count, &val);
115 
116 	tep_get_field_val(s, event, "thread_count", record, &val, 1);
117 	update_sum(&cpu_data->thread_count, &val);
118 
119 	return 0;
120 }
121 
122 /*
123  * osnoise_top_header - print the header of the tool output
124  */
125 static void osnoise_top_header(struct osnoise_tool *top)
126 {
127 	struct osnoise_params *params = to_osnoise_params(top->params);
128 	struct trace_seq *s = top->trace.seq;
129 	bool pretty = params->common.pretty_output;
130 	char duration[26];
131 
132 	get_duration(top->start_time, duration, sizeof(duration));
133 
134 	if (pretty)
135 		trace_seq_printf(s, "\033[2;37;40m");
136 
137 	trace_seq_printf(s, "                                          ");
138 
139 	if (params->mode == MODE_OSNOISE) {
140 		trace_seq_printf(s, "Operating System Noise");
141 		trace_seq_printf(s, "                                       ");
142 	} else if (params->mode == MODE_HWNOISE) {
143 		trace_seq_printf(s, "Hardware-related Noise");
144 	}
145 
146 	trace_seq_printf(s, "                                   ");
147 
148 	if (pretty)
149 		trace_seq_printf(s, "\033[0;0;0m");
150 	trace_seq_printf(s, "\n");
151 
152 	trace_seq_printf(s, "duration: %9s | time is in us\n", duration);
153 
154 	if (pretty)
155 		trace_seq_printf(s, "\033[2;30;47m");
156 
157 	trace_seq_printf(s, "CPU Period       Runtime ");
158 	trace_seq_printf(s, "       Noise ");
159 	trace_seq_printf(s, " %% CPU Aval ");
160 	trace_seq_printf(s, "  Max Noise   Max Single ");
161 	trace_seq_printf(s, "         HW          NMI");
162 
163 	if (params->mode == MODE_HWNOISE)
164 		goto eol;
165 
166 	trace_seq_printf(s, "          IRQ      Softirq       Thread");
167 
168 eol:
169 	if (pretty)
170 		trace_seq_printf(s, "\033[0;0;0m");
171 	trace_seq_printf(s, "\n");
172 }
173 
174 /*
175  * clear_terminal - clears the output terminal
176  */
177 static void clear_terminal(struct trace_seq *seq)
178 {
179 	if (!config_debug)
180 		trace_seq_printf(seq, "\033c");
181 }
182 
183 /*
184  * osnoise_top_print - prints the output of a given CPU
185  */
186 static void osnoise_top_print(struct osnoise_tool *tool, int cpu)
187 {
188 	struct osnoise_params *params = to_osnoise_params(tool->params);
189 	struct trace_seq *s = tool->trace.seq;
190 	struct osnoise_top_cpu *cpu_data;
191 	struct osnoise_top_data *data;
192 	int percentage;
193 	int decimal;
194 
195 	data = tool->data;
196 	cpu_data = &data->cpu_data[cpu];
197 
198 	if (!cpu_data->sum_runtime)
199 		return;
200 
201 	percentage = ((cpu_data->sum_runtime - cpu_data->sum_noise) * 10000000)
202 			/ cpu_data->sum_runtime;
203 	decimal = percentage % 100000;
204 	percentage = percentage / 100000;
205 
206 	trace_seq_printf(s, "%3d #%-6d %12llu ", cpu, cpu_data->sum_cycles, cpu_data->sum_runtime);
207 	trace_seq_printf(s, "%12llu ", cpu_data->sum_noise);
208 	trace_seq_printf(s, "  %3d.%05d", percentage, decimal);
209 	trace_seq_printf(s, "%12llu %12llu", cpu_data->max_noise, cpu_data->max_sample);
210 
211 	trace_seq_printf(s, "%12llu ", cpu_data->hw_count);
212 	trace_seq_printf(s, "%12llu ", cpu_data->nmi_count);
213 
214 	if (params->mode == MODE_HWNOISE) {
215 		trace_seq_printf(s, "\n");
216 		return;
217 	}
218 
219 	trace_seq_printf(s, "%12llu ", cpu_data->irq_count);
220 	trace_seq_printf(s, "%12llu ", cpu_data->softirq_count);
221 	trace_seq_printf(s, "%12llu\n", cpu_data->thread_count);
222 }
223 
224 /*
225  * osnoise_print_stats - print data for all cpus
226  */
227 static void
228 osnoise_print_stats(struct osnoise_tool *top)
229 {
230 	struct osnoise_params *params = to_osnoise_params(top->params);
231 	struct trace_instance *trace = &top->trace;
232 	int i;
233 
234 	if (!params->common.quiet)
235 		clear_terminal(trace->seq);
236 
237 	osnoise_top_header(top);
238 
239 	for_each_monitored_cpu(i, &params->common) {
240 		osnoise_top_print(top, i);
241 	}
242 
243 	trace_seq_do_printf(trace->seq);
244 	trace_seq_reset(trace->seq);
245 	osnoise_report_missed_events(top);
246 }
247 
248 /*
249  * osnoise_top_apply_config - apply the top configs to the initialized tool
250  */
251 static int
252 osnoise_top_apply_config(struct osnoise_tool *tool)
253 {
254 	struct osnoise_params *params = to_osnoise_params(tool->params);
255 	int retval;
256 
257 	retval = osnoise_apply_config(tool, params);
258 	if (retval)
259 		goto out_err;
260 
261 	if (params->mode == MODE_HWNOISE) {
262 		retval = osnoise_set_irq_disable(tool->context, 1);
263 		if (retval) {
264 			err_msg("Failed to set OSNOISE_IRQ_DISABLE option\n");
265 			goto out_err;
266 		}
267 	}
268 
269 	if (isatty(STDOUT_FILENO) && !params->common.quiet)
270 		params->common.pretty_output = 1;
271 
272 	return 0;
273 
274 out_err:
275 	return -1;
276 }
277 
278 /*
279  * osnoise_init_top - initialize a osnoise top tool with parameters
280  */
281 struct osnoise_tool *osnoise_init_top(struct common_params *params)
282 {
283 	struct osnoise_tool *tool;
284 
285 	tool = osnoise_init_tool("osnoise_top");
286 	if (!tool)
287 		return NULL;
288 
289 	tool->data = osnoise_alloc_top();
290 	if (!tool->data) {
291 		osnoise_destroy_tool(tool);
292 		return NULL;
293 	}
294 
295 	tep_register_event_handler(tool->trace.tep, -1, "ftrace", "osnoise",
296 				   osnoise_top_handler, NULL);
297 
298 	return tool;
299 }
300 
301 struct tool_ops osnoise_top_ops = {
302 	.tracer = "osnoise",
303 	.comm_prefix = "osnoise/",
304 	.parse_args = osnoise_top_parse_args,
305 	.init_tool = osnoise_init_top,
306 	.apply_config = osnoise_top_apply_config,
307 	.enable = osnoise_enable,
308 	.main = top_main_loop,
309 	.print_stats = osnoise_print_stats,
310 	.free = osnoise_free_top_tool,
311 };
312