diff options
| -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; |
