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