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, ¶ms->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