Commit | Line | Data |
---|---|---|
4f19048f | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7fe2f639 DB |
2 | /* |
3 | * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | |
4 | * | |
7fe2f639 | 5 | * Output format inspired by Len Brown's <lenb@kernel.org> turbostat tool. |
7fe2f639 DB |
6 | */ |
7 | ||
8 | ||
9 | #include <stdio.h> | |
10 | #include <unistd.h> | |
11 | #include <stdlib.h> | |
12 | #include <string.h> | |
13 | #include <time.h> | |
14 | #include <signal.h> | |
15 | #include <sys/types.h> | |
16 | #include <sys/wait.h> | |
17 | #include <libgen.h> | |
18 | ||
19 | #include "idle_monitor/cpupower-monitor.h" | |
20 | #include "idle_monitor/idle_monitors.h" | |
21 | #include "helpers/helpers.h" | |
22 | ||
23 | /* Define pointers to all monitors. */ | |
24 | #define DEF(x) & x ## _monitor , | |
b510b541 | 25 | struct cpuidle_monitor *all_monitors[] = { |
7fe2f639 DB |
26 | #include "idle_monitors.def" |
27 | 0 | |
28 | }; | |
29 | ||
2de7fb60 MG |
30 | int cpu_count; |
31 | ||
7fe2f639 DB |
32 | static struct cpuidle_monitor *monitors[MONITORS_MAX]; |
33 | static unsigned int avail_monitors; | |
34 | ||
35 | static char *progname; | |
36 | ||
37 | enum operation_mode_e { list = 1, show, show_all }; | |
38 | static int mode; | |
39 | static int interval = 1; | |
40 | static char *show_monitors_param; | |
41 | static struct cpupower_topology cpu_top; | |
c8cfc3c6 | 42 | static unsigned int wake_cpus; |
7fe2f639 DB |
43 | |
44 | /* ToDo: Document this in the manpage */ | |
45 | static char range_abbr[RANGE_MAX] = { 'T', 'C', 'P', 'M', }; | |
46 | ||
498ca793 DB |
47 | static void print_wrong_arg_exit(void) |
48 | { | |
49 | printf(_("invalid or unknown argument\n")); | |
50 | exit(EXIT_FAILURE); | |
51 | } | |
52 | ||
7fe2f639 DB |
53 | long long timespec_diff_us(struct timespec start, struct timespec end) |
54 | { | |
55 | struct timespec temp; | |
56 | if ((end.tv_nsec - start.tv_nsec) < 0) { | |
57 | temp.tv_sec = end.tv_sec - start.tv_sec - 1; | |
58 | temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; | |
59 | } else { | |
60 | temp.tv_sec = end.tv_sec - start.tv_sec; | |
61 | temp.tv_nsec = end.tv_nsec - start.tv_nsec; | |
62 | } | |
63 | return (temp.tv_sec * 1000000) + (temp.tv_nsec / 1000); | |
64 | } | |
65 | ||
7fe2f639 DB |
66 | void print_n_spaces(int n) |
67 | { | |
68 | int x; | |
69 | for (x = 0; x < n; x++) | |
70 | printf(" "); | |
b510b541 | 71 | } |
7fe2f639 | 72 | |
f9652d5c AG |
73 | /*s is filled with left and right spaces |
74 | *to make its length atleast n+1 | |
75 | */ | |
7fe2f639 DB |
76 | int fill_string_with_spaces(char *s, int n) |
77 | { | |
f9652d5c | 78 | char *temp; |
b510b541 | 79 | int len = strlen(s); |
f9652d5c AG |
80 | |
81 | if (len >= n) | |
7fe2f639 | 82 | return -1; |
f9652d5c AG |
83 | |
84 | temp = malloc(sizeof(char) * (n+1)); | |
b510b541 DB |
85 | for (; len < n; len++) |
86 | s[len] = ' '; | |
87 | s[len] = '\0'; | |
f9652d5c AG |
88 | snprintf(temp, n+1, " %s", s); |
89 | strcpy(s, temp); | |
90 | free(temp); | |
7fe2f639 | 91 | return 0; |
b510b541 | 92 | } |
7fe2f639 | 93 | |
f9652d5c | 94 | #define MAX_COL_WIDTH 6 |
7fe2f639 DB |
95 | void print_header(int topology_depth) |
96 | { | |
97 | int unsigned mon; | |
fb8eaeb7 | 98 | int state, need_len; |
7fe2f639 DB |
99 | cstate_t s; |
100 | char buf[128] = ""; | |
7fe2f639 DB |
101 | |
102 | fill_string_with_spaces(buf, topology_depth * 5 - 1); | |
103 | printf("%s|", buf); | |
104 | ||
105 | for (mon = 0; mon < avail_monitors; mon++) { | |
f9652d5c | 106 | need_len = monitors[mon]->hw_states_num * (MAX_COL_WIDTH + 1) |
7fe2f639 | 107 | - 1; |
f9652d5c AG |
108 | if (mon != 0) |
109 | printf("||"); | |
7fe2f639 DB |
110 | sprintf(buf, "%s", monitors[mon]->name); |
111 | fill_string_with_spaces(buf, need_len); | |
112 | printf("%s", buf); | |
113 | } | |
114 | printf("\n"); | |
115 | ||
116 | if (topology_depth > 2) | |
f9652d5c | 117 | printf(" PKG|"); |
7fe2f639 DB |
118 | if (topology_depth > 1) |
119 | printf("CORE|"); | |
120 | if (topology_depth > 0) | |
f9652d5c | 121 | printf(" CPU|"); |
7fe2f639 DB |
122 | |
123 | for (mon = 0; mon < avail_monitors; mon++) { | |
124 | if (mon != 0) | |
f9652d5c | 125 | printf("||"); |
7fe2f639 DB |
126 | for (state = 0; state < monitors[mon]->hw_states_num; state++) { |
127 | if (state != 0) | |
f9652d5c | 128 | printf("|"); |
7fe2f639 DB |
129 | s = monitors[mon]->hw_states[state]; |
130 | sprintf(buf, "%s", s.name); | |
f9652d5c | 131 | fill_string_with_spaces(buf, MAX_COL_WIDTH); |
7fe2f639 DB |
132 | printf("%s", buf); |
133 | } | |
134 | printf(" "); | |
135 | } | |
136 | printf("\n"); | |
137 | } | |
138 | ||
139 | ||
140 | void print_results(int topology_depth, int cpu) | |
141 | { | |
142 | unsigned int mon; | |
143 | int state, ret; | |
144 | double percent; | |
145 | unsigned long long result; | |
146 | cstate_t s; | |
147 | ||
9ee31f61 TR |
148 | /* Be careful CPUs may got resorted for pkg value do not just use cpu */ |
149 | if (!bitmask_isbitset(cpus_chosen, cpu_top.core_info[cpu].cpu)) | |
150 | return; | |
20102ac5 JT |
151 | if (!cpu_top.core_info[cpu].is_online && |
152 | cpu_top.core_info[cpu].pkg == -1) | |
153 | return; | |
9ee31f61 | 154 | |
7fe2f639 DB |
155 | if (topology_depth > 2) |
156 | printf("%4d|", cpu_top.core_info[cpu].pkg); | |
157 | if (topology_depth > 1) | |
158 | printf("%4d|", cpu_top.core_info[cpu].core); | |
159 | if (topology_depth > 0) | |
160 | printf("%4d|", cpu_top.core_info[cpu].cpu); | |
161 | ||
162 | for (mon = 0; mon < avail_monitors; mon++) { | |
163 | if (mon != 0) | |
164 | printf("||"); | |
165 | ||
166 | for (state = 0; state < monitors[mon]->hw_states_num; state++) { | |
167 | if (state != 0) | |
168 | printf("|"); | |
169 | ||
170 | s = monitors[mon]->hw_states[state]; | |
171 | ||
172 | if (s.get_count_percent) { | |
173 | ret = s.get_count_percent(s.id, &percent, | |
174 | cpu_top.core_info[cpu].cpu); | |
b510b541 | 175 | if (ret) |
7fe2f639 | 176 | printf("******"); |
b510b541 | 177 | else if (percent >= 100.0) |
7fe2f639 DB |
178 | printf("%6.1f", percent); |
179 | else | |
180 | printf("%6.2f", percent); | |
b510b541 | 181 | } else if (s.get_count) { |
7fe2f639 DB |
182 | ret = s.get_count(s.id, &result, |
183 | cpu_top.core_info[cpu].cpu); | |
b510b541 | 184 | if (ret) |
7fe2f639 | 185 | printf("******"); |
b510b541 | 186 | else |
7fe2f639 | 187 | printf("%6llu", result); |
b510b541 | 188 | } else { |
7fe2f639 DB |
189 | printf(_("Monitor %s, Counter %s has no count " |
190 | "function. Implementation error\n"), | |
191 | monitors[mon]->name, s.name); | |
b510b541 | 192 | exit(EXIT_FAILURE); |
7fe2f639 DB |
193 | } |
194 | } | |
195 | } | |
7c74d2bc TR |
196 | /* |
197 | * The monitor could still provide useful data, for example | |
198 | * AMD HW counters partly sit in PCI config space. | |
199 | * It's up to the monitor plug-in to check .is_online, this one | |
200 | * is just for additional info. | |
201 | */ | |
20102ac5 JT |
202 | if (!cpu_top.core_info[cpu].is_online && |
203 | cpu_top.core_info[cpu].pkg != -1) { | |
7fe2f639 DB |
204 | printf(_(" *is offline\n")); |
205 | return; | |
206 | } else | |
207 | printf("\n"); | |
208 | } | |
209 | ||
210 | ||
211 | /* param: string passed by -m param (The list of monitors to show) | |
212 | * | |
213 | * Monitors must have been registered already, matching monitors | |
214 | * are picked out and available monitors array is overridden | |
215 | * with matching ones | |
216 | * | |
217 | * Monitors get sorted in the same order the user passes them | |
218 | */ | |
219 | ||
b510b541 | 220 | static void parse_monitor_param(char *param) |
7fe2f639 DB |
221 | { |
222 | unsigned int num; | |
223 | int mon, hits = 0; | |
224 | char *tmp = param, *token; | |
225 | struct cpuidle_monitor *tmp_mons[MONITORS_MAX]; | |
226 | ||
227 | ||
b510b541 | 228 | for (mon = 0; mon < MONITORS_MAX; mon++, tmp = NULL) { |
7fe2f639 DB |
229 | token = strtok(tmp, ","); |
230 | if (token == NULL) | |
231 | break; | |
232 | if (strlen(token) >= MONITOR_NAME_LEN) { | |
233 | printf(_("%s: max monitor name length" | |
234 | " (%d) exceeded\n"), token, MONITOR_NAME_LEN); | |
235 | continue; | |
236 | } | |
237 | ||
238 | for (num = 0; num < avail_monitors; num++) { | |
239 | if (!strcmp(monitors[num]->name, token)) { | |
240 | dprint("Found requested monitor: %s\n", token); | |
241 | tmp_mons[hits] = monitors[num]; | |
242 | hits++; | |
243 | } | |
b510b541 | 244 | } |
7fe2f639 DB |
245 | } |
246 | if (hits == 0) { | |
247 | printf(_("No matching monitor found in %s, " | |
248 | "try -l option\n"), param); | |
7fe2f639 DB |
249 | exit(EXIT_FAILURE); |
250 | } | |
251 | /* Override detected/registerd monitors array with requested one */ | |
b510b541 DB |
252 | memcpy(monitors, tmp_mons, |
253 | sizeof(struct cpuidle_monitor *) * MONITORS_MAX); | |
7fe2f639 DB |
254 | avail_monitors = hits; |
255 | } | |
256 | ||
b510b541 DB |
257 | void list_monitors(void) |
258 | { | |
7fe2f639 DB |
259 | unsigned int mon; |
260 | int state; | |
261 | cstate_t s; | |
262 | ||
263 | for (mon = 0; mon < avail_monitors; mon++) { | |
264 | printf(_("Monitor \"%s\" (%d states) - Might overflow after %u " | |
b510b541 DB |
265 | "s\n"), |
266 | monitors[mon]->name, monitors[mon]->hw_states_num, | |
267 | monitors[mon]->overflow_s); | |
268 | ||
7fe2f639 DB |
269 | for (state = 0; state < monitors[mon]->hw_states_num; state++) { |
270 | s = monitors[mon]->hw_states[state]; | |
271 | /* | |
272 | * ToDo show more state capabilities: | |
273 | * percent, time (granlarity) | |
274 | */ | |
275 | printf("%s\t[%c] -> %s\n", s.name, range_abbr[s.range], | |
276 | gettext(s.desc)); | |
277 | } | |
278 | } | |
279 | } | |
280 | ||
281 | int fork_it(char **argv) | |
282 | { | |
283 | int status; | |
284 | unsigned int num; | |
285 | unsigned long long timediff; | |
286 | pid_t child_pid; | |
287 | struct timespec start, end; | |
288 | ||
289 | child_pid = fork(); | |
290 | clock_gettime(CLOCK_REALTIME, &start); | |
291 | ||
292 | for (num = 0; num < avail_monitors; num++) | |
293 | monitors[num]->start(); | |
294 | ||
295 | if (!child_pid) { | |
296 | /* child */ | |
297 | execvp(argv[0], argv); | |
298 | } else { | |
299 | /* parent */ | |
300 | if (child_pid == -1) { | |
301 | perror("fork"); | |
302 | exit(1); | |
303 | } | |
304 | ||
305 | signal(SIGINT, SIG_IGN); | |
306 | signal(SIGQUIT, SIG_IGN); | |
307 | if (waitpid(child_pid, &status, 0) == -1) { | |
308 | perror("wait"); | |
309 | exit(1); | |
310 | } | |
311 | } | |
312 | clock_gettime(CLOCK_REALTIME, &end); | |
313 | for (num = 0; num < avail_monitors; num++) | |
314 | monitors[num]->stop(); | |
315 | ||
316 | timediff = timespec_diff_us(start, end); | |
317 | if (WIFEXITED(status)) | |
318 | printf(_("%s took %.5f seconds and exited with status %d\n"), | |
b510b541 DB |
319 | argv[0], timediff / (1000.0 * 1000), |
320 | WEXITSTATUS(status)); | |
7fe2f639 DB |
321 | return 0; |
322 | } | |
323 | ||
324 | int do_interval_measure(int i) | |
325 | { | |
326 | unsigned int num; | |
c8cfc3c6 TR |
327 | int cpu; |
328 | ||
329 | if (wake_cpus) | |
330 | for (cpu = 0; cpu < cpu_count; cpu++) | |
331 | bind_cpu(cpu); | |
7fe2f639 DB |
332 | |
333 | for (num = 0; num < avail_monitors; num++) { | |
334 | dprint("HW C-state residency monitor: %s - States: %d\n", | |
335 | monitors[num]->name, monitors[num]->hw_states_num); | |
336 | monitors[num]->start(); | |
337 | } | |
c8cfc3c6 | 338 | |
7fe2f639 | 339 | sleep(i); |
c8cfc3c6 TR |
340 | |
341 | if (wake_cpus) | |
342 | for (cpu = 0; cpu < cpu_count; cpu++) | |
343 | bind_cpu(cpu); | |
344 | ||
b510b541 | 345 | for (num = 0; num < avail_monitors; num++) |
7fe2f639 | 346 | monitors[num]->stop(); |
b510b541 | 347 | |
c8cfc3c6 | 348 | |
7fe2f639 DB |
349 | return 0; |
350 | } | |
351 | ||
352 | static void cmdline(int argc, char *argv[]) | |
353 | { | |
354 | int opt; | |
355 | progname = basename(argv[0]); | |
356 | ||
c8cfc3c6 | 357 | while ((opt = getopt(argc, argv, "+lci:m:")) != -1) { |
7fe2f639 | 358 | switch (opt) { |
7fe2f639 | 359 | case 'l': |
498ca793 DB |
360 | if (mode) |
361 | print_wrong_arg_exit(); | |
7fe2f639 DB |
362 | mode = list; |
363 | break; | |
364 | case 'i': | |
365 | /* only allow -i with -m or no option */ | |
498ca793 DB |
366 | if (mode && mode != show) |
367 | print_wrong_arg_exit(); | |
7fe2f639 DB |
368 | interval = atoi(optarg); |
369 | break; | |
370 | case 'm': | |
498ca793 DB |
371 | if (mode) |
372 | print_wrong_arg_exit(); | |
7fe2f639 DB |
373 | mode = show; |
374 | show_monitors_param = optarg; | |
375 | break; | |
c8cfc3c6 TR |
376 | case 'c': |
377 | wake_cpus = 1; | |
378 | break; | |
7fe2f639 | 379 | default: |
498ca793 | 380 | print_wrong_arg_exit(); |
7fe2f639 DB |
381 | } |
382 | } | |
383 | if (!mode) | |
384 | mode = show_all; | |
385 | } | |
386 | ||
387 | int cmd_monitor(int argc, char **argv) | |
388 | { | |
389 | unsigned int num; | |
390 | struct cpuidle_monitor *test_mon; | |
391 | int cpu; | |
392 | ||
393 | cmdline(argc, argv); | |
394 | cpu_count = get_cpu_topology(&cpu_top); | |
395 | if (cpu_count < 0) { | |
396 | printf(_("Cannot read number of available processors\n")); | |
397 | return EXIT_FAILURE; | |
398 | } | |
399 | ||
20102ac5 JT |
400 | if (!cpu_top.core_info[0].is_online) |
401 | printf("WARNING: at least one cpu is offline\n"); | |
402 | ||
9ee31f61 TR |
403 | /* Default is: monitor all CPUs */ |
404 | if (bitmask_isallclear(cpus_chosen)) | |
405 | bitmask_setall(cpus_chosen); | |
406 | ||
7fe2f639 | 407 | dprint("System has up to %d CPU cores\n", cpu_count); |
b510b541 | 408 | |
7fe2f639 DB |
409 | for (num = 0; all_monitors[num]; num++) { |
410 | dprint("Try to register: %s\n", all_monitors[num]->name); | |
411 | test_mon = all_monitors[num]->do_register(); | |
412 | if (test_mon) { | |
d3f5d2a1 | 413 | if (test_mon->flags.needs_root && !run_as_root) { |
7fe2f639 DB |
414 | fprintf(stderr, _("Available monitor %s needs " |
415 | "root access\n"), test_mon->name); | |
416 | continue; | |
417 | } | |
418 | monitors[avail_monitors] = test_mon; | |
419 | dprint("%s registered\n", all_monitors[num]->name); | |
420 | avail_monitors++; | |
421 | } | |
422 | } | |
423 | ||
424 | if (avail_monitors == 0) { | |
425 | printf(_("No HW Cstate monitors found\n")); | |
426 | return 1; | |
427 | } | |
428 | ||
429 | if (mode == list) { | |
430 | list_monitors(); | |
431 | exit(EXIT_SUCCESS); | |
432 | } | |
433 | ||
434 | if (mode == show) | |
435 | parse_monitor_param(show_monitors_param); | |
436 | ||
437 | dprint("Packages: %d - Cores: %d - CPUs: %d\n", | |
438 | cpu_top.pkgs, cpu_top.cores, cpu_count); | |
439 | ||
440 | /* | |
441 | * if any params left, it must be a command to fork | |
442 | */ | |
443 | if (argc - optind) | |
444 | fork_it(argv + optind); | |
445 | else | |
446 | do_interval_measure(interval); | |
447 | ||
448 | /* ToDo: Topology parsing needs fixing first to do | |
449 | this more generically */ | |
450 | if (cpu_top.pkgs > 1) | |
451 | print_header(3); | |
452 | else | |
453 | print_header(1); | |
454 | ||
455 | for (cpu = 0; cpu < cpu_count; cpu++) { | |
456 | if (cpu_top.pkgs > 1) | |
457 | print_results(3, cpu); | |
458 | else | |
459 | print_results(1, cpu); | |
460 | } | |
461 | ||
b510b541 | 462 | for (num = 0; num < avail_monitors; num++) |
7fe2f639 | 463 | monitors[num]->unregister(); |
b510b541 | 464 | |
7fe2f639 DB |
465 | cpu_topology_release(cpu_top); |
466 | return 0; | |
467 | } |