diff options
author | Len Brown <len.brown@intel.com> | 2016-12-22 23:57:55 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2016-12-24 15:16:10 -0500 |
commit | 388e9c8134be6bbc3751ba7072f5fa9bc8ecbe01 (patch) | |
tree | e43e0ee0818f20e82785fdcc659f2c4d776b02fc /tools | |
parent | 7268d407ad4c49fbd521fb2e2f675a447bfef472 (diff) |
tools/power turbostat: Make extensible via the --add parameter
Create the "--add" parameter. This can be used to teach an existing
turbostat binary about any number of any type of counter.
turbostat(8) details the syntax for --add.
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/power/x86/turbostat/turbostat.8 | 22 | ||||
-rw-r--r-- | tools/power/x86/turbostat/turbostat.c | 396 |
2 files changed, 409 insertions, 9 deletions
diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 index 492e84fbebfa..39f7daeb6fbd 100644 --- a/tools/power/x86/turbostat/turbostat.8 +++ b/tools/power/x86/turbostat/turbostat.8 | |||
@@ -25,6 +25,28 @@ Some information is not available on older processors. | |||
25 | .SS Options | 25 | .SS Options |
26 | Options can be specified with a single or double '-', and only as much of the option | 26 | Options can be specified with a single or double '-', and only as much of the option |
27 | name as necessary to disambiguate it from others is necessary. Note that options are case-sensitive. | 27 | name as necessary to disambiguate it from others is necessary. Note that options are case-sensitive. |
28 | .PP | ||
29 | \fB--add attributes\fP add column with counter having specified 'attributes'. The 'location' attribute is required, all others are optional. | ||
30 | .nf | ||
31 | location: {\fBmsrDDD\fP | \fBmsr0xXXX\fP} | ||
32 | msrDDD is a decimal offset, eg. msr16 | ||
33 | msr0xXXX is a hex offset, eg. msr0x10 | ||
34 | |||
35 | scope: {\fBcpu\fP | \fBcore\fP | \fBpackage\fP} | ||
36 | sample and print the counter for every cpu, core, or package. | ||
37 | default: cpu | ||
38 | |||
39 | size: {\fBu32\fP | \fBu64\fP } | ||
40 | MSRs are read as 64-bits, u32 truncates the displayed value to 32-bits. | ||
41 | default: u64 | ||
42 | |||
43 | format: {\fBraw\fP | \fBdelta\fP | \fBpercent\fP} | ||
44 | 'raw' shows the MSR contents in hex. | ||
45 | 'delta' shows the difference in values during the measurement interval. | ||
46 | 'percent' shows the delta as a percentage of the cycles elapsed. | ||
47 | default: delta | ||
48 | .fi | ||
49 | .PP | ||
28 | \fB--Counter MSR#\fP shows the delta of the specified 64-bit MSR counter. | 50 | \fB--Counter MSR#\fP shows the delta of the specified 64-bit MSR counter. |
29 | .PP | 51 | .PP |
30 | \fB--counter MSR#\fP shows the delta of the specified 32-bit MSR counter. | 52 | \fB--counter MSR#\fP shows the delta of the specified 32-bit MSR counter. |
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 3708386531f2..4490a776bbae 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c | |||
@@ -147,6 +147,12 @@ unsigned int has_hwp_pkg; /* IA32_HWP_REQUEST_PKG */ | |||
147 | 147 | ||
148 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) | 148 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) |
149 | 149 | ||
150 | /* | ||
151 | * buffer size used by sscanf() for added column names | ||
152 | * Usually truncated to 7 characters, but also handles 18 columns for raw 64-bit counters | ||
153 | */ | ||
154 | #define NAME_BYTES 20 | ||
155 | |||
150 | int backwards_count; | 156 | int backwards_count; |
151 | char *progname; | 157 | char *progname; |
152 | 158 | ||
@@ -168,6 +174,7 @@ struct thread_data { | |||
168 | unsigned int flags; | 174 | unsigned int flags; |
169 | #define CPU_IS_FIRST_THREAD_IN_CORE 0x2 | 175 | #define CPU_IS_FIRST_THREAD_IN_CORE 0x2 |
170 | #define CPU_IS_FIRST_CORE_IN_PACKAGE 0x4 | 176 | #define CPU_IS_FIRST_CORE_IN_PACKAGE 0x4 |
177 | unsigned long long counter[1]; | ||
171 | } *thread_even, *thread_odd; | 178 | } *thread_even, *thread_odd; |
172 | 179 | ||
173 | struct core_data { | 180 | struct core_data { |
@@ -176,6 +183,7 @@ struct core_data { | |||
176 | unsigned long long c7; | 183 | unsigned long long c7; |
177 | unsigned int core_temp_c; | 184 | unsigned int core_temp_c; |
178 | unsigned int core_id; | 185 | unsigned int core_id; |
186 | unsigned long long counter[1]; | ||
179 | } *core_even, *core_odd; | 187 | } *core_even, *core_odd; |
180 | 188 | ||
181 | struct pkg_data { | 189 | struct pkg_data { |
@@ -200,7 +208,7 @@ struct pkg_data { | |||
200 | unsigned int rapl_pkg_perf_status; /* MSR_PKG_PERF_STATUS */ | 208 | unsigned int rapl_pkg_perf_status; /* MSR_PKG_PERF_STATUS */ |
201 | unsigned int rapl_dram_perf_status; /* MSR_DRAM_PERF_STATUS */ | 209 | unsigned int rapl_dram_perf_status; /* MSR_DRAM_PERF_STATUS */ |
202 | unsigned int pkg_temp_c; | 210 | unsigned int pkg_temp_c; |
203 | 211 | unsigned long long counter[1]; | |
204 | } *package_even, *package_odd; | 212 | } *package_even, *package_odd; |
205 | 213 | ||
206 | #define ODD_COUNTERS thread_odd, core_odd, package_odd | 214 | #define ODD_COUNTERS thread_odd, core_odd, package_odd |
@@ -214,11 +222,33 @@ struct pkg_data { | |||
214 | (core_base + (pkg_no) * topo.num_cores_per_pkg + (core_no)) | 222 | (core_base + (pkg_no) * topo.num_cores_per_pkg + (core_no)) |
215 | #define GET_PKG(pkg_base, pkg_no) (pkg_base + pkg_no) | 223 | #define GET_PKG(pkg_base, pkg_no) (pkg_base + pkg_no) |
216 | 224 | ||
225 | enum counter_scope {SCOPE_CPU, SCOPE_CORE, SCOPE_PACKAGE}; | ||
226 | enum counter_type {COUNTER_CYCLES, COUNTER_SECONDS}; | ||
227 | enum counter_format {FORMAT_RAW, FORMAT_DELTA, FORMAT_PERCENT}; | ||
228 | |||
229 | struct msr_counter { | ||
230 | unsigned int msr_num; | ||
231 | char name[NAME_BYTES]; | ||
232 | unsigned int width; | ||
233 | enum counter_type type; | ||
234 | enum counter_format format; | ||
235 | struct msr_counter *next; | ||
236 | }; | ||
237 | |||
238 | struct sys_counters { | ||
239 | unsigned int thread_counter_bytes; | ||
240 | unsigned int core_counter_bytes; | ||
241 | unsigned int package_counter_bytes; | ||
242 | struct msr_counter *tp; | ||
243 | struct msr_counter *cp; | ||
244 | struct msr_counter *pp; | ||
245 | } sys; | ||
246 | |||
217 | struct system_summary { | 247 | struct system_summary { |
218 | struct thread_data threads; | 248 | struct thread_data threads; |
219 | struct core_data cores; | 249 | struct core_data cores; |
220 | struct pkg_data packages; | 250 | struct pkg_data packages; |
221 | } sum, average; | 251 | } average; |
222 | 252 | ||
223 | 253 | ||
224 | struct topo_params { | 254 | struct topo_params { |
@@ -320,12 +350,14 @@ int get_msr(int cpu, off_t offset, unsigned long long *msr) | |||
320 | /* | 350 | /* |
321 | * Example Format w/ field column widths: | 351 | * Example Format w/ field column widths: |
322 | * | 352 | * |
323 | * Package Core CPU Avg_MHz Bzy_MHz TSC_MHz IRQ SMI Busy% CPU_%c1 CPU_%c3 CPU_%c6 CPU_%c7 CoreTmp PkgTmp GFXMHz Pkg%pc2 Pkg%pc3 Pkg%pc6 Pkg%pc7 PkgWatt CorWatt GFXWatt | 353 | * Package Core CPU Avg_MHz Bzy_MHz TSC_MHz IRQ SMI Busy% CPU_%c1 CPU_%c3 CPU_%c6 CPU_%c7 ThreadC CoreTmp CoreCnt PkgTmp GFXMHz Pkg%pc2 Pkg%pc3 Pkg%pc6 Pkg%pc7 PkgWatt CorWatt GFXWatt PkgCnt |
324 | * 12345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678 | 354 | * 12345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678 |
325 | */ | 355 | */ |
326 | 356 | ||
327 | void print_header(void) | 357 | void print_header(void) |
328 | { | 358 | { |
359 | struct msr_counter *mp; | ||
360 | |||
329 | if (show_pkg) | 361 | if (show_pkg) |
330 | outp += sprintf(outp, "\tPackage"); | 362 | outp += sprintf(outp, "\tPackage"); |
331 | if (show_core) | 363 | if (show_core) |
@@ -366,8 +398,31 @@ void print_header(void) | |||
366 | if (do_snb_cstates) | 398 | if (do_snb_cstates) |
367 | outp += sprintf(outp, "\tCPU%%c7"); | 399 | outp += sprintf(outp, "\tCPU%%c7"); |
368 | 400 | ||
401 | for (mp = sys.tp; mp; mp = mp->next) { | ||
402 | if (mp->format == FORMAT_RAW) { | ||
403 | if (mp->width == 64) | ||
404 | outp += sprintf(outp, "\t%18.18s", mp->name); | ||
405 | else | ||
406 | outp += sprintf(outp, "\t%10.10s", mp->name); | ||
407 | } else { | ||
408 | outp += sprintf(outp, "\t%-7.7s", mp->name); | ||
409 | } | ||
410 | } | ||
411 | |||
369 | if (do_dts) | 412 | if (do_dts) |
370 | outp += sprintf(outp, "\tCoreTmp"); | 413 | outp += sprintf(outp, "\tCoreTmp"); |
414 | |||
415 | for (mp = sys.cp; mp; mp = mp->next) { | ||
416 | if (mp->format == FORMAT_RAW) { | ||
417 | if (mp->width == 64) | ||
418 | outp += sprintf(outp, "\t%18.18s", mp->name); | ||
419 | else | ||
420 | outp += sprintf(outp, "\t%10.10s", mp->name); | ||
421 | } else { | ||
422 | outp += sprintf(outp, "\t%-7.7s", mp->name); | ||
423 | } | ||
424 | } | ||
425 | |||
371 | if (do_ptm) | 426 | if (do_ptm) |
372 | outp += sprintf(outp, "\tPkgTmp"); | 427 | outp += sprintf(outp, "\tPkgTmp"); |
373 | 428 | ||
@@ -425,13 +480,27 @@ void print_header(void) | |||
425 | if (do_rapl & RAPL_DRAM_PERF_STATUS) | 480 | if (do_rapl & RAPL_DRAM_PERF_STATUS) |
426 | outp += sprintf(outp, "\tRAM_%%"); | 481 | outp += sprintf(outp, "\tRAM_%%"); |
427 | } | 482 | } |
428 | done: | 483 | for (mp = sys.pp; mp; mp = mp->next) { |
484 | if (mp->format == FORMAT_RAW) { | ||
485 | if (mp->width == 64) | ||
486 | outp += sprintf(outp, "\t%18.18s", mp->name); | ||
487 | else | ||
488 | outp += sprintf(outp, "\t%10.10s", mp->name); | ||
489 | } else { | ||
490 | outp += sprintf(outp, "\t%-7.7s", mp->name); | ||
491 | } | ||
492 | } | ||
493 | |||
494 | done: | ||
429 | outp += sprintf(outp, "\n"); | 495 | outp += sprintf(outp, "\n"); |
430 | } | 496 | } |
431 | 497 | ||
432 | int dump_counters(struct thread_data *t, struct core_data *c, | 498 | int dump_counters(struct thread_data *t, struct core_data *c, |
433 | struct pkg_data *p) | 499 | struct pkg_data *p) |
434 | { | 500 | { |
501 | int i; | ||
502 | struct msr_counter *mp; | ||
503 | |||
435 | outp += sprintf(outp, "t %p, c %p, p %p\n", t, c, p); | 504 | outp += sprintf(outp, "t %p, c %p, p %p\n", t, c, p); |
436 | 505 | ||
437 | if (t) { | 506 | if (t) { |
@@ -453,6 +522,11 @@ int dump_counters(struct thread_data *t, struct core_data *c, | |||
453 | outp += sprintf(outp, "IRQ: %08X\n", t->irq_count); | 522 | outp += sprintf(outp, "IRQ: %08X\n", t->irq_count); |
454 | if (do_smi) | 523 | if (do_smi) |
455 | outp += sprintf(outp, "SMI: %08X\n", t->smi_count); | 524 | outp += sprintf(outp, "SMI: %08X\n", t->smi_count); |
525 | |||
526 | for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { | ||
527 | outp += sprintf(outp, "tADDED [%d] msr0x%x: %08llX\n", | ||
528 | i, mp->msr_num, t->counter[i]); | ||
529 | } | ||
456 | } | 530 | } |
457 | 531 | ||
458 | if (c) { | 532 | if (c) { |
@@ -461,6 +535,11 @@ int dump_counters(struct thread_data *t, struct core_data *c, | |||
461 | outp += sprintf(outp, "c6: %016llX\n", c->c6); | 535 | outp += sprintf(outp, "c6: %016llX\n", c->c6); |
462 | outp += sprintf(outp, "c7: %016llX\n", c->c7); | 536 | outp += sprintf(outp, "c7: %016llX\n", c->c7); |
463 | outp += sprintf(outp, "DTS: %dC\n", c->core_temp_c); | 537 | outp += sprintf(outp, "DTS: %dC\n", c->core_temp_c); |
538 | |||
539 | for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { | ||
540 | outp += sprintf(outp, "cADDED [%d] msr0x%x: %08llX\n", | ||
541 | i, mp->msr_num, c->counter[i]); | ||
542 | } | ||
464 | } | 543 | } |
465 | 544 | ||
466 | if (p) { | 545 | if (p) { |
@@ -490,6 +569,11 @@ int dump_counters(struct thread_data *t, struct core_data *c, | |||
490 | outp += sprintf(outp, "Throttle RAM: %0X\n", | 569 | outp += sprintf(outp, "Throttle RAM: %0X\n", |
491 | p->rapl_dram_perf_status); | 570 | p->rapl_dram_perf_status); |
492 | outp += sprintf(outp, "PTM: %dC\n", p->pkg_temp_c); | 571 | outp += sprintf(outp, "PTM: %dC\n", p->pkg_temp_c); |
572 | |||
573 | for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { | ||
574 | outp += sprintf(outp, "pADDED [%d] msr0x%x: %08llX\n", | ||
575 | i, mp->msr_num, p->counter[i]); | ||
576 | } | ||
493 | } | 577 | } |
494 | 578 | ||
495 | outp += sprintf(outp, "\n"); | 579 | outp += sprintf(outp, "\n"); |
@@ -505,6 +589,8 @@ int format_counters(struct thread_data *t, struct core_data *c, | |||
505 | { | 589 | { |
506 | double interval_float; | 590 | double interval_float; |
507 | char *fmt8; | 591 | char *fmt8; |
592 | int i; | ||
593 | struct msr_counter *mp; | ||
508 | 594 | ||
509 | /* if showing only 1st thread in core and this isn't one, bail out */ | 595 | /* if showing only 1st thread in core and this isn't one, bail out */ |
510 | if (show_core_only && !(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) | 596 | if (show_core_only && !(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) |
@@ -602,9 +688,36 @@ int format_counters(struct thread_data *t, struct core_data *c, | |||
602 | if (do_snb_cstates) | 688 | if (do_snb_cstates) |
603 | outp += sprintf(outp, "\t%.2f", 100.0 * c->c7/t->tsc); | 689 | outp += sprintf(outp, "\t%.2f", 100.0 * c->c7/t->tsc); |
604 | 690 | ||
691 | for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { | ||
692 | if (mp->format == FORMAT_RAW) { | ||
693 | if (mp->width == 32) | ||
694 | outp += sprintf(outp, "\t0x%08lx", (unsigned long) t->counter[i]); | ||
695 | else | ||
696 | outp += sprintf(outp, "\t0x%016llx", t->counter[i]); | ||
697 | } else if (mp->format == FORMAT_DELTA) { | ||
698 | outp += sprintf(outp, "\t%8lld", t->counter[i]); | ||
699 | } else if (mp->format == FORMAT_PERCENT) { | ||
700 | outp += sprintf(outp, "\t%.2f", 100.0 * t->counter[i]/t->tsc); | ||
701 | } | ||
702 | } | ||
703 | |||
704 | |||
605 | if (do_dts) | 705 | if (do_dts) |
606 | outp += sprintf(outp, "\t%d", c->core_temp_c); | 706 | outp += sprintf(outp, "\t%d", c->core_temp_c); |
607 | 707 | ||
708 | for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { | ||
709 | if (mp->format == FORMAT_RAW) { | ||
710 | if (mp->width == 32) | ||
711 | outp += sprintf(outp, "\t0x%08lx", (unsigned long) c->counter[i]); | ||
712 | else | ||
713 | outp += sprintf(outp, "\t0x%016llx", c->counter[i]); | ||
714 | } else if (mp->format == FORMAT_DELTA) { | ||
715 | outp += sprintf(outp, "\t%8lld", c->counter[i]); | ||
716 | } else if (mp->format == FORMAT_PERCENT) { | ||
717 | outp += sprintf(outp, "\t%.2f", 100.0 * c->counter[i]/t->tsc); | ||
718 | } | ||
719 | } | ||
720 | |||
608 | /* print per-package data only for 1st core in package */ | 721 | /* print per-package data only for 1st core in package */ |
609 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) | 722 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) |
610 | goto done; | 723 | goto done; |
@@ -689,6 +802,19 @@ int format_counters(struct thread_data *t, struct core_data *c, | |||
689 | if (do_rapl & RAPL_DRAM_PERF_STATUS) | 802 | if (do_rapl & RAPL_DRAM_PERF_STATUS) |
690 | outp += sprintf(outp, fmt8, 100.0 * p->rapl_dram_perf_status * rapl_time_units / interval_float); | 803 | outp += sprintf(outp, fmt8, 100.0 * p->rapl_dram_perf_status * rapl_time_units / interval_float); |
691 | } | 804 | } |
805 | for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { | ||
806 | if (mp->format == FORMAT_RAW) { | ||
807 | if (mp->width == 32) | ||
808 | outp += sprintf(outp, "\t0x%08lx", (unsigned long) p->counter[i]); | ||
809 | else | ||
810 | outp += sprintf(outp, "\t0x%016llx", p->counter[i]); | ||
811 | } else if (mp->format == FORMAT_DELTA) { | ||
812 | outp += sprintf(outp, "\t%8lld", p->counter[i]); | ||
813 | } else if (mp->format == FORMAT_PERCENT) { | ||
814 | outp += sprintf(outp, "\t%.2f", 100.0 * p->counter[i]/t->tsc); | ||
815 | } | ||
816 | } | ||
817 | |||
692 | done: | 818 | done: |
693 | outp += sprintf(outp, "\n"); | 819 | outp += sprintf(outp, "\n"); |
694 | 820 | ||
@@ -744,6 +870,8 @@ void format_all_counters(struct thread_data *t, struct core_data *c, struct pkg_ | |||
744 | int | 870 | int |
745 | delta_package(struct pkg_data *new, struct pkg_data *old) | 871 | delta_package(struct pkg_data *new, struct pkg_data *old) |
746 | { | 872 | { |
873 | int i; | ||
874 | struct msr_counter *mp; | ||
747 | 875 | ||
748 | if (do_skl_residency) { | 876 | if (do_skl_residency) { |
749 | old->pkg_wtd_core_c0 = new->pkg_wtd_core_c0 - old->pkg_wtd_core_c0; | 877 | old->pkg_wtd_core_c0 = new->pkg_wtd_core_c0 - old->pkg_wtd_core_c0; |
@@ -778,16 +906,33 @@ delta_package(struct pkg_data *new, struct pkg_data *old) | |||
778 | DELTA_WRAP32(new->rapl_pkg_perf_status, old->rapl_pkg_perf_status); | 906 | DELTA_WRAP32(new->rapl_pkg_perf_status, old->rapl_pkg_perf_status); |
779 | DELTA_WRAP32(new->rapl_dram_perf_status, old->rapl_dram_perf_status); | 907 | DELTA_WRAP32(new->rapl_dram_perf_status, old->rapl_dram_perf_status); |
780 | 908 | ||
909 | for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { | ||
910 | if (mp->format == FORMAT_RAW) | ||
911 | old->counter[i] = new->counter[i]; | ||
912 | else | ||
913 | old->counter[i] = new->counter[i] - old->counter[i]; | ||
914 | } | ||
915 | |||
781 | return 0; | 916 | return 0; |
782 | } | 917 | } |
783 | 918 | ||
784 | void | 919 | void |
785 | delta_core(struct core_data *new, struct core_data *old) | 920 | delta_core(struct core_data *new, struct core_data *old) |
786 | { | 921 | { |
922 | int i; | ||
923 | struct msr_counter *mp; | ||
924 | |||
787 | old->c3 = new->c3 - old->c3; | 925 | old->c3 = new->c3 - old->c3; |
788 | old->c6 = new->c6 - old->c6; | 926 | old->c6 = new->c6 - old->c6; |
789 | old->c7 = new->c7 - old->c7; | 927 | old->c7 = new->c7 - old->c7; |
790 | old->core_temp_c = new->core_temp_c; | 928 | old->core_temp_c = new->core_temp_c; |
929 | |||
930 | for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { | ||
931 | if (mp->format == FORMAT_RAW) | ||
932 | old->counter[i] = new->counter[i]; | ||
933 | else | ||
934 | old->counter[i] = new->counter[i] - old->counter[i]; | ||
935 | } | ||
791 | } | 936 | } |
792 | 937 | ||
793 | /* | 938 | /* |
@@ -797,6 +942,9 @@ int | |||
797 | delta_thread(struct thread_data *new, struct thread_data *old, | 942 | delta_thread(struct thread_data *new, struct thread_data *old, |
798 | struct core_data *core_delta) | 943 | struct core_data *core_delta) |
799 | { | 944 | { |
945 | int i; | ||
946 | struct msr_counter *mp; | ||
947 | |||
800 | old->tsc = new->tsc - old->tsc; | 948 | old->tsc = new->tsc - old->tsc; |
801 | 949 | ||
802 | /* check for TSC < 1 Mcycles over interval */ | 950 | /* check for TSC < 1 Mcycles over interval */ |
@@ -860,6 +1008,12 @@ delta_thread(struct thread_data *new, struct thread_data *old, | |||
860 | if (do_smi) | 1008 | if (do_smi) |
861 | old->smi_count = new->smi_count - old->smi_count; | 1009 | old->smi_count = new->smi_count - old->smi_count; |
862 | 1010 | ||
1011 | for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { | ||
1012 | if (mp->format == FORMAT_RAW) | ||
1013 | old->counter[i] = new->counter[i]; | ||
1014 | else | ||
1015 | old->counter[i] = new->counter[i] - old->counter[i]; | ||
1016 | } | ||
863 | return 0; | 1017 | return 0; |
864 | } | 1018 | } |
865 | 1019 | ||
@@ -887,6 +1041,9 @@ int delta_cpu(struct thread_data *t, struct core_data *c, | |||
887 | 1041 | ||
888 | void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) | 1042 | void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) |
889 | { | 1043 | { |
1044 | int i; | ||
1045 | struct msr_counter *mp; | ||
1046 | |||
890 | t->tsc = 0; | 1047 | t->tsc = 0; |
891 | t->aperf = 0; | 1048 | t->aperf = 0; |
892 | t->mperf = 0; | 1049 | t->mperf = 0; |
@@ -932,10 +1089,22 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data | |||
932 | 1089 | ||
933 | p->gfx_rc6_ms = 0; | 1090 | p->gfx_rc6_ms = 0; |
934 | p->gfx_mhz = 0; | 1091 | p->gfx_mhz = 0; |
1092 | |||
1093 | for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) | ||
1094 | t->counter[i] = 0; | ||
1095 | |||
1096 | for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) | ||
1097 | c->counter[i] = 0; | ||
1098 | |||
1099 | for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) | ||
1100 | p->counter[i] = 0; | ||
935 | } | 1101 | } |
936 | int sum_counters(struct thread_data *t, struct core_data *c, | 1102 | int sum_counters(struct thread_data *t, struct core_data *c, |
937 | struct pkg_data *p) | 1103 | struct pkg_data *p) |
938 | { | 1104 | { |
1105 | int i; | ||
1106 | struct msr_counter *mp; | ||
1107 | |||
939 | average.threads.tsc += t->tsc; | 1108 | average.threads.tsc += t->tsc; |
940 | average.threads.aperf += t->aperf; | 1109 | average.threads.aperf += t->aperf; |
941 | average.threads.mperf += t->mperf; | 1110 | average.threads.mperf += t->mperf; |
@@ -947,6 +1116,12 @@ int sum_counters(struct thread_data *t, struct core_data *c, | |||
947 | average.threads.irq_count += t->irq_count; | 1116 | average.threads.irq_count += t->irq_count; |
948 | average.threads.smi_count += t->smi_count; | 1117 | average.threads.smi_count += t->smi_count; |
949 | 1118 | ||
1119 | for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { | ||
1120 | if (mp->format == FORMAT_RAW) | ||
1121 | continue; | ||
1122 | average.threads.counter[i] += t->counter[i]; | ||
1123 | } | ||
1124 | |||
950 | /* sum per-core values only for 1st thread in core */ | 1125 | /* sum per-core values only for 1st thread in core */ |
951 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) | 1126 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) |
952 | return 0; | 1127 | return 0; |
@@ -957,6 +1132,12 @@ int sum_counters(struct thread_data *t, struct core_data *c, | |||
957 | 1132 | ||
958 | average.cores.core_temp_c = MAX(average.cores.core_temp_c, c->core_temp_c); | 1133 | average.cores.core_temp_c = MAX(average.cores.core_temp_c, c->core_temp_c); |
959 | 1134 | ||
1135 | for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { | ||
1136 | if (mp->format == FORMAT_RAW) | ||
1137 | continue; | ||
1138 | average.cores.counter[i] += c->counter[i]; | ||
1139 | } | ||
1140 | |||
960 | /* sum per-pkg values only for 1st core in pkg */ | 1141 | /* sum per-pkg values only for 1st core in pkg */ |
961 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) | 1142 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) |
962 | return 0; | 1143 | return 0; |
@@ -991,6 +1172,12 @@ int sum_counters(struct thread_data *t, struct core_data *c, | |||
991 | 1172 | ||
992 | average.packages.rapl_pkg_perf_status += p->rapl_pkg_perf_status; | 1173 | average.packages.rapl_pkg_perf_status += p->rapl_pkg_perf_status; |
993 | average.packages.rapl_dram_perf_status += p->rapl_dram_perf_status; | 1174 | average.packages.rapl_dram_perf_status += p->rapl_dram_perf_status; |
1175 | |||
1176 | for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { | ||
1177 | if (mp->format == FORMAT_RAW) | ||
1178 | continue; | ||
1179 | average.packages.counter[i] += p->counter[i]; | ||
1180 | } | ||
994 | return 0; | 1181 | return 0; |
995 | } | 1182 | } |
996 | /* | 1183 | /* |
@@ -1000,6 +1187,9 @@ int sum_counters(struct thread_data *t, struct core_data *c, | |||
1000 | void compute_average(struct thread_data *t, struct core_data *c, | 1187 | void compute_average(struct thread_data *t, struct core_data *c, |
1001 | struct pkg_data *p) | 1188 | struct pkg_data *p) |
1002 | { | 1189 | { |
1190 | int i; | ||
1191 | struct msr_counter *mp; | ||
1192 | |||
1003 | clear_counters(&average.threads, &average.cores, &average.packages); | 1193 | clear_counters(&average.threads, &average.cores, &average.packages); |
1004 | 1194 | ||
1005 | for_all_cpus(sum_counters, t, c, p); | 1195 | for_all_cpus(sum_counters, t, c, p); |
@@ -1036,6 +1226,22 @@ void compute_average(struct thread_data *t, struct core_data *c, | |||
1036 | average.packages.pc8 /= topo.num_packages; | 1226 | average.packages.pc8 /= topo.num_packages; |
1037 | average.packages.pc9 /= topo.num_packages; | 1227 | average.packages.pc9 /= topo.num_packages; |
1038 | average.packages.pc10 /= topo.num_packages; | 1228 | average.packages.pc10 /= topo.num_packages; |
1229 | |||
1230 | for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { | ||
1231 | if (mp->format == FORMAT_RAW) | ||
1232 | continue; | ||
1233 | average.threads.counter[i] /= topo.num_cpus; | ||
1234 | } | ||
1235 | for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { | ||
1236 | if (mp->format == FORMAT_RAW) | ||
1237 | continue; | ||
1238 | average.cores.counter[i] /= topo.num_cores; | ||
1239 | } | ||
1240 | for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { | ||
1241 | if (mp->format == FORMAT_RAW) | ||
1242 | continue; | ||
1243 | average.packages.counter[i] /= topo.num_packages; | ||
1244 | } | ||
1039 | } | 1245 | } |
1040 | 1246 | ||
1041 | static unsigned long long rdtsc(void) | 1247 | static unsigned long long rdtsc(void) |
@@ -1057,6 +1263,8 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) | |||
1057 | int cpu = t->cpu_id; | 1263 | int cpu = t->cpu_id; |
1058 | unsigned long long msr; | 1264 | unsigned long long msr; |
1059 | int aperf_mperf_retry_count = 0; | 1265 | int aperf_mperf_retry_count = 0; |
1266 | struct msr_counter *mp; | ||
1267 | int i; | ||
1060 | 1268 | ||
1061 | if (cpu_migrate(cpu)) { | 1269 | if (cpu_migrate(cpu)) { |
1062 | fprintf(outf, "Could not migrate to CPU %d\n", cpu); | 1270 | fprintf(outf, "Could not migrate to CPU %d\n", cpu); |
@@ -1154,6 +1362,12 @@ retry: | |||
1154 | return -6; | 1362 | return -6; |
1155 | } | 1363 | } |
1156 | 1364 | ||
1365 | for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { | ||
1366 | if (get_msr(cpu, mp->msr_num, &t->counter[i])) | ||
1367 | return -10; | ||
1368 | } | ||
1369 | |||
1370 | |||
1157 | /* collect core counters only for 1st thread in core */ | 1371 | /* collect core counters only for 1st thread in core */ |
1158 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) | 1372 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) |
1159 | return 0; | 1373 | return 0; |
@@ -1181,6 +1395,10 @@ retry: | |||
1181 | c->core_temp_c = tcc_activation_temp - ((msr >> 16) & 0x7F); | 1395 | c->core_temp_c = tcc_activation_temp - ((msr >> 16) & 0x7F); |
1182 | } | 1396 | } |
1183 | 1397 | ||
1398 | for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { | ||
1399 | if (get_msr(cpu, mp->msr_num, &c->counter[i])) | ||
1400 | return -10; | ||
1401 | } | ||
1184 | 1402 | ||
1185 | /* collect package counters only for 1st core in package */ | 1403 | /* collect package counters only for 1st core in package */ |
1186 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) | 1404 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) |
@@ -1258,6 +1476,11 @@ retry: | |||
1258 | if (do_gfx_mhz) | 1476 | if (do_gfx_mhz) |
1259 | p->gfx_mhz = gfx_cur_mhz; | 1477 | p->gfx_mhz = gfx_cur_mhz; |
1260 | 1478 | ||
1479 | for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { | ||
1480 | if (get_msr(cpu, mp->msr_num, &p->counter[i])) | ||
1481 | return -10; | ||
1482 | } | ||
1483 | |||
1261 | return 0; | 1484 | return 0; |
1262 | } | 1485 | } |
1263 | 1486 | ||
@@ -2740,7 +2963,7 @@ int print_thermal(struct thread_data *t, struct core_data *c, struct pkg_data *p | |||
2740 | cpu = t->cpu_id; | 2963 | cpu = t->cpu_id; |
2741 | 2964 | ||
2742 | /* DTS is per-core, no need to print for each thread */ | 2965 | /* DTS is per-core, no need to print for each thread */ |
2743 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) | 2966 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) |
2744 | return 0; | 2967 | return 0; |
2745 | 2968 | ||
2746 | if (cpu_migrate(cpu)) { | 2969 | if (cpu_migrate(cpu)) { |
@@ -3391,6 +3614,8 @@ void help() | |||
3391 | "when COMMAND completes.\n" | 3614 | "when COMMAND completes.\n" |
3392 | "If no COMMAND is specified, turbostat wakes every 5-seconds\n" | 3615 | "If no COMMAND is specified, turbostat wakes every 5-seconds\n" |
3393 | "to print statistics, until interrupted.\n" | 3616 | "to print statistics, until interrupted.\n" |
3617 | "--add add a counter\n" | ||
3618 | " eg. --add msr0x10,u64,cpu,delta,MY_TSC\n" | ||
3394 | "--debug run in \"debug\" mode\n" | 3619 | "--debug run in \"debug\" mode\n" |
3395 | "--interval sec Override default 5-second measurement interval\n" | 3620 | "--interval sec Override default 5-second measurement interval\n" |
3396 | "--help print this help message\n" | 3621 | "--help print this help message\n" |
@@ -3521,7 +3746,7 @@ allocate_counters(struct thread_data **t, struct core_data **c, struct pkg_data | |||
3521 | int i; | 3746 | int i; |
3522 | 3747 | ||
3523 | *t = calloc(topo.num_threads_per_core * topo.num_cores_per_pkg * | 3748 | *t = calloc(topo.num_threads_per_core * topo.num_cores_per_pkg * |
3524 | topo.num_packages, sizeof(struct thread_data)); | 3749 | topo.num_packages, sizeof(struct thread_data) + sys.thread_counter_bytes); |
3525 | if (*t == NULL) | 3750 | if (*t == NULL) |
3526 | goto error; | 3751 | goto error; |
3527 | 3752 | ||
@@ -3530,14 +3755,14 @@ allocate_counters(struct thread_data **t, struct core_data **c, struct pkg_data | |||
3530 | (*t)[i].cpu_id = -1; | 3755 | (*t)[i].cpu_id = -1; |
3531 | 3756 | ||
3532 | *c = calloc(topo.num_cores_per_pkg * topo.num_packages, | 3757 | *c = calloc(topo.num_cores_per_pkg * topo.num_packages, |
3533 | sizeof(struct core_data)); | 3758 | sizeof(struct core_data) + sys.core_counter_bytes); |
3534 | if (*c == NULL) | 3759 | if (*c == NULL) |
3535 | goto error; | 3760 | goto error; |
3536 | 3761 | ||
3537 | for (i = 0; i < topo.num_cores_per_pkg * topo.num_packages; i++) | 3762 | for (i = 0; i < topo.num_cores_per_pkg * topo.num_packages; i++) |
3538 | (*c)[i].core_id = -1; | 3763 | (*c)[i].core_id = -1; |
3539 | 3764 | ||
3540 | *p = calloc(topo.num_packages, sizeof(struct pkg_data)); | 3765 | *p = calloc(topo.num_packages, sizeof(struct pkg_data) + sys.package_counter_bytes); |
3541 | if (*p == NULL) | 3766 | if (*p == NULL) |
3542 | goto error; | 3767 | goto error; |
3543 | 3768 | ||
@@ -3735,15 +3960,165 @@ int get_and_dump_counters(void) | |||
3735 | } | 3960 | } |
3736 | 3961 | ||
3737 | void print_version() { | 3962 | void print_version() { |
3738 | fprintf(outf, "turbostat version 4.14 22 Apr 2016" | 3963 | fprintf(outf, "turbostat version 4.15 21 Dec 2016" |
3739 | " - Len Brown <lenb@kernel.org>\n"); | 3964 | " - Len Brown <lenb@kernel.org>\n"); |
3740 | } | 3965 | } |
3741 | 3966 | ||
3967 | int add_counter(unsigned int msr_num, char *name, unsigned int width, | ||
3968 | enum counter_scope scope, enum counter_type type, | ||
3969 | enum counter_format format) | ||
3970 | { | ||
3971 | struct msr_counter *msrp; | ||
3972 | |||
3973 | msrp = calloc(1, sizeof(struct msr_counter)); | ||
3974 | if (msrp == NULL) { | ||
3975 | perror("calloc"); | ||
3976 | exit(1); | ||
3977 | } | ||
3978 | |||
3979 | msrp->msr_num = msr_num; | ||
3980 | strncpy(msrp->name, name, NAME_BYTES); | ||
3981 | msrp->width = width; | ||
3982 | msrp->type = type; | ||
3983 | msrp->format = format; | ||
3984 | |||
3985 | switch (scope) { | ||
3986 | |||
3987 | case SCOPE_CPU: | ||
3988 | sys.thread_counter_bytes += 64; | ||
3989 | msrp->next = sys.tp; | ||
3990 | sys.tp = msrp; | ||
3991 | sys.thread_counter_bytes += sizeof(unsigned long long); | ||
3992 | break; | ||
3993 | |||
3994 | case SCOPE_CORE: | ||
3995 | sys.core_counter_bytes += 64; | ||
3996 | msrp->next = sys.cp; | ||
3997 | sys.cp = msrp; | ||
3998 | sys.core_counter_bytes += sizeof(unsigned long long); | ||
3999 | break; | ||
4000 | |||
4001 | case SCOPE_PACKAGE: | ||
4002 | sys.package_counter_bytes += 64; | ||
4003 | msrp->next = sys.pp; | ||
4004 | sys.pp = msrp; | ||
4005 | sys.package_counter_bytes += sizeof(unsigned long long); | ||
4006 | break; | ||
4007 | } | ||
4008 | |||
4009 | return 0; | ||
4010 | } | ||
4011 | |||
4012 | void parse_add_command(char *add_command) | ||
4013 | { | ||
4014 | int msr_num = 0; | ||
4015 | char name_buffer[NAME_BYTES]; | ||
4016 | int width = 64; | ||
4017 | int fail = 0; | ||
4018 | enum counter_scope scope = SCOPE_CPU; | ||
4019 | enum counter_type type = COUNTER_CYCLES; | ||
4020 | enum counter_format format = FORMAT_DELTA; | ||
4021 | |||
4022 | while (add_command) { | ||
4023 | |||
4024 | if (sscanf(add_command, "msr0x%x", &msr_num) == 1) | ||
4025 | goto next; | ||
4026 | |||
4027 | if (sscanf(add_command, "msr%d", &msr_num) == 1) | ||
4028 | goto next; | ||
4029 | |||
4030 | if (sscanf(add_command, "u%d", &width) == 1) { | ||
4031 | if ((width == 32) || (width == 64)) | ||
4032 | goto next; | ||
4033 | width = 64; | ||
4034 | } | ||
4035 | if (!strncmp(add_command, "cpu", strlen("cpu"))) { | ||
4036 | scope = SCOPE_CPU; | ||
4037 | goto next; | ||
4038 | } | ||
4039 | if (!strncmp(add_command, "core", strlen("core"))) { | ||
4040 | scope = SCOPE_CORE; | ||
4041 | goto next; | ||
4042 | } | ||
4043 | if (!strncmp(add_command, "package", strlen("package"))) { | ||
4044 | scope = SCOPE_PACKAGE; | ||
4045 | goto next; | ||
4046 | } | ||
4047 | if (!strncmp(add_command, "cycles", strlen("cycles"))) { | ||
4048 | type = COUNTER_CYCLES; | ||
4049 | goto next; | ||
4050 | } | ||
4051 | if (!strncmp(add_command, "seconds", strlen("seconds"))) { | ||
4052 | type = COUNTER_SECONDS; | ||
4053 | goto next; | ||
4054 | } | ||
4055 | if (!strncmp(add_command, "raw", strlen("raw"))) { | ||
4056 | format = FORMAT_RAW; | ||
4057 | goto next; | ||
4058 | } | ||
4059 | if (!strncmp(add_command, "delta", strlen("delta"))) { | ||
4060 | format = FORMAT_DELTA; | ||
4061 | goto next; | ||
4062 | } | ||
4063 | if (!strncmp(add_command, "percent", strlen("percent"))) { | ||
4064 | format = FORMAT_PERCENT; | ||
4065 | goto next; | ||
4066 | } | ||
4067 | |||
4068 | if (sscanf(add_command, "%18s,%*s", name_buffer) == 1) { /* 18 < NAME_BYTES */ | ||
4069 | char *eos; | ||
4070 | |||
4071 | eos = strchr(name_buffer, ','); | ||
4072 | if (eos) | ||
4073 | *eos = '\0'; | ||
4074 | goto next; | ||
4075 | } | ||
4076 | |||
4077 | next: | ||
4078 | add_command = strchr(add_command, ','); | ||
4079 | if (add_command) | ||
4080 | add_command++; | ||
4081 | |||
4082 | } | ||
4083 | if (msr_num == 0) { | ||
4084 | fprintf(stderr, "--add: (msrDDD | msr0xXXX) required\n"); | ||
4085 | fail++; | ||
4086 | } | ||
4087 | |||
4088 | /* generate default column header */ | ||
4089 | if (*name_buffer == '\0') { | ||
4090 | if (format == FORMAT_RAW) { | ||
4091 | if (width == 32) | ||
4092 | sprintf(name_buffer, "msr%d", msr_num); | ||
4093 | else | ||
4094 | sprintf(name_buffer, "MSR%d", msr_num); | ||
4095 | } else if (format == FORMAT_DELTA) { | ||
4096 | if (width == 32) | ||
4097 | sprintf(name_buffer, "cnt%d", msr_num); | ||
4098 | else | ||
4099 | sprintf(name_buffer, "CNT%d", msr_num); | ||
4100 | } else if (format == FORMAT_PERCENT) { | ||
4101 | if (width == 32) | ||
4102 | sprintf(name_buffer, "msr%d%%", msr_num); | ||
4103 | else | ||
4104 | sprintf(name_buffer, "MSR%d%%", msr_num); | ||
4105 | } | ||
4106 | } | ||
4107 | |||
4108 | if (add_counter(msr_num, name_buffer, width, scope, type, format)) | ||
4109 | fail++; | ||
4110 | |||
4111 | if (fail) { | ||
4112 | help(); | ||
4113 | exit(1); | ||
4114 | } | ||
4115 | } | ||
3742 | void cmdline(int argc, char **argv) | 4116 | void cmdline(int argc, char **argv) |
3743 | { | 4117 | { |
3744 | int opt; | 4118 | int opt; |
3745 | int option_index = 0; | 4119 | int option_index = 0; |
3746 | static struct option long_options[] = { | 4120 | static struct option long_options[] = { |
4121 | {"add", required_argument, 0, 'a'}, | ||
3747 | {"Counter", required_argument, 0, 'C'}, | 4122 | {"Counter", required_argument, 0, 'C'}, |
3748 | {"counter", required_argument, 0, 'c'}, | 4123 | {"counter", required_argument, 0, 'c'}, |
3749 | {"Dump", no_argument, 0, 'D'}, | 4124 | {"Dump", no_argument, 0, 'D'}, |
@@ -3767,6 +4142,9 @@ void cmdline(int argc, char **argv) | |||
3767 | while ((opt = getopt_long_only(argc, argv, "+C:c:Ddhi:JM:m:o:PpST:v", | 4142 | while ((opt = getopt_long_only(argc, argv, "+C:c:Ddhi:JM:m:o:PpST:v", |
3768 | long_options, &option_index)) != -1) { | 4143 | long_options, &option_index)) != -1) { |
3769 | switch (opt) { | 4144 | switch (opt) { |
4145 | case 'a': | ||
4146 | parse_add_command(optarg); | ||
4147 | break; | ||
3770 | case 'C': | 4148 | case 'C': |
3771 | sscanf(optarg, "%x", &extra_delta_offset64); | 4149 | sscanf(optarg, "%x", &extra_delta_offset64); |
3772 | break; | 4150 | break; |