1103a8feaSLen Brown /* 2103a8feaSLen Brown * turbostat -- show CPU frequency and C-state residency 3103a8feaSLen Brown * on modern Intel turbo-capable processors. 4103a8feaSLen Brown * 5e23da037SLen Brown * Copyright (c) 2012 Intel Corporation. 6103a8feaSLen Brown * Len Brown <len.brown@intel.com> 7103a8feaSLen Brown * 8103a8feaSLen Brown * This program is free software; you can redistribute it and/or modify it 9103a8feaSLen Brown * under the terms and conditions of the GNU General Public License, 10103a8feaSLen Brown * version 2, as published by the Free Software Foundation. 11103a8feaSLen Brown * 12103a8feaSLen Brown * This program is distributed in the hope it will be useful, but WITHOUT 13103a8feaSLen Brown * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14103a8feaSLen Brown * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15103a8feaSLen Brown * more details. 16103a8feaSLen Brown * 17103a8feaSLen Brown * You should have received a copy of the GNU General Public License along with 18103a8feaSLen Brown * this program; if not, write to the Free Software Foundation, Inc., 19103a8feaSLen Brown * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 20103a8feaSLen Brown */ 21103a8feaSLen Brown 2288c3281fSLen Brown #define _GNU_SOURCE 23103a8feaSLen Brown #include <stdio.h> 24103a8feaSLen Brown #include <unistd.h> 25103a8feaSLen Brown #include <sys/types.h> 26103a8feaSLen Brown #include <sys/wait.h> 27103a8feaSLen Brown #include <sys/stat.h> 28103a8feaSLen Brown #include <sys/resource.h> 29103a8feaSLen Brown #include <fcntl.h> 30103a8feaSLen Brown #include <signal.h> 31103a8feaSLen Brown #include <sys/time.h> 32103a8feaSLen Brown #include <stdlib.h> 33103a8feaSLen Brown #include <dirent.h> 34103a8feaSLen Brown #include <string.h> 35103a8feaSLen Brown #include <ctype.h> 3688c3281fSLen Brown #include <sched.h> 37103a8feaSLen Brown 38103a8feaSLen Brown #define MSR_NEHALEM_PLATFORM_INFO 0xCE 39103a8feaSLen Brown #define MSR_NEHALEM_TURBO_RATIO_LIMIT 0x1AD 406574a5d5SLen Brown #define MSR_IVT_TURBO_RATIO_LIMIT 0x1AE 41103a8feaSLen Brown #define MSR_APERF 0xE8 42103a8feaSLen Brown #define MSR_MPERF 0xE7 43103a8feaSLen Brown #define MSR_PKG_C2_RESIDENCY 0x60D /* SNB only */ 44103a8feaSLen Brown #define MSR_PKG_C3_RESIDENCY 0x3F8 45103a8feaSLen Brown #define MSR_PKG_C6_RESIDENCY 0x3F9 46103a8feaSLen Brown #define MSR_PKG_C7_RESIDENCY 0x3FA /* SNB only */ 47103a8feaSLen Brown #define MSR_CORE_C3_RESIDENCY 0x3FC 48103a8feaSLen Brown #define MSR_CORE_C6_RESIDENCY 0x3FD 49103a8feaSLen Brown #define MSR_CORE_C7_RESIDENCY 0x3FE /* SNB only */ 50103a8feaSLen Brown 51103a8feaSLen Brown char *proc_stat = "/proc/stat"; 52103a8feaSLen Brown unsigned int interval_sec = 5; /* set with -i interval_sec */ 53103a8feaSLen Brown unsigned int verbose; /* set with -v */ 54e23da037SLen Brown unsigned int summary_only; /* set with -s */ 55103a8feaSLen Brown unsigned int skip_c0; 56103a8feaSLen Brown unsigned int skip_c1; 57103a8feaSLen Brown unsigned int do_nhm_cstates; 58103a8feaSLen Brown unsigned int do_snb_cstates; 59103a8feaSLen Brown unsigned int has_aperf; 60103a8feaSLen Brown unsigned int units = 1000000000; /* Ghz etc */ 61103a8feaSLen Brown unsigned int genuine_intel; 62103a8feaSLen Brown unsigned int has_invariant_tsc; 63103a8feaSLen Brown unsigned int do_nehalem_platform_info; 64103a8feaSLen Brown unsigned int do_nehalem_turbo_ratio_limit; 656574a5d5SLen Brown unsigned int do_ivt_turbo_ratio_limit; 66*2f32edf1SLen Brown unsigned int extra_msr_offset32; 67*2f32edf1SLen Brown unsigned int extra_msr_offset64; 68103a8feaSLen Brown double bclk; 69103a8feaSLen Brown unsigned int show_pkg; 70103a8feaSLen Brown unsigned int show_core; 71103a8feaSLen Brown unsigned int show_cpu; 72c98d5d94SLen Brown unsigned int show_pkg_only; 73c98d5d94SLen Brown unsigned int show_core_only; 74c98d5d94SLen Brown char *output_buffer, *outp; 75103a8feaSLen Brown 76103a8feaSLen Brown int aperf_mperf_unstable; 77103a8feaSLen Brown int backwards_count; 78103a8feaSLen Brown char *progname; 79103a8feaSLen Brown 80c98d5d94SLen Brown cpu_set_t *cpu_present_set, *cpu_affinity_set; 81c98d5d94SLen Brown size_t cpu_present_setsize, cpu_affinity_setsize; 82c98d5d94SLen Brown 83c98d5d94SLen Brown struct thread_data { 84c98d5d94SLen Brown unsigned long long tsc; 85c98d5d94SLen Brown unsigned long long aperf; 86c98d5d94SLen Brown unsigned long long mperf; 87c98d5d94SLen Brown unsigned long long c1; /* derived */ 88*2f32edf1SLen Brown unsigned long long extra_msr64; 89*2f32edf1SLen Brown unsigned int extra_msr32; 90c98d5d94SLen Brown unsigned int cpu_id; 91c98d5d94SLen Brown unsigned int flags; 92c98d5d94SLen Brown #define CPU_IS_FIRST_THREAD_IN_CORE 0x2 93c98d5d94SLen Brown #define CPU_IS_FIRST_CORE_IN_PACKAGE 0x4 94c98d5d94SLen Brown } *thread_even, *thread_odd; 95c98d5d94SLen Brown 96c98d5d94SLen Brown struct core_data { 97c98d5d94SLen Brown unsigned long long c3; 98c98d5d94SLen Brown unsigned long long c6; 99c98d5d94SLen Brown unsigned long long c7; 100c98d5d94SLen Brown unsigned int core_id; 101c98d5d94SLen Brown } *core_even, *core_odd; 102c98d5d94SLen Brown 103c98d5d94SLen Brown struct pkg_data { 104c98d5d94SLen Brown unsigned long long pc2; 105c98d5d94SLen Brown unsigned long long pc3; 106c98d5d94SLen Brown unsigned long long pc6; 107c98d5d94SLen Brown unsigned long long pc7; 108c98d5d94SLen Brown unsigned int package_id; 109c98d5d94SLen Brown } *package_even, *package_odd; 110c98d5d94SLen Brown 111c98d5d94SLen Brown #define ODD_COUNTERS thread_odd, core_odd, package_odd 112c98d5d94SLen Brown #define EVEN_COUNTERS thread_even, core_even, package_even 113c98d5d94SLen Brown 114c98d5d94SLen Brown #define GET_THREAD(thread_base, thread_no, core_no, pkg_no) \ 115c98d5d94SLen Brown (thread_base + (pkg_no) * topo.num_cores_per_pkg * \ 116c98d5d94SLen Brown topo.num_threads_per_core + \ 117c98d5d94SLen Brown (core_no) * topo.num_threads_per_core + (thread_no)) 118c98d5d94SLen Brown #define GET_CORE(core_base, core_no, pkg_no) \ 119c98d5d94SLen Brown (core_base + (pkg_no) * topo.num_cores_per_pkg + (core_no)) 120c98d5d94SLen Brown #define GET_PKG(pkg_base, pkg_no) (pkg_base + pkg_no) 121c98d5d94SLen Brown 122c98d5d94SLen Brown struct system_summary { 123c98d5d94SLen Brown struct thread_data threads; 124c98d5d94SLen Brown struct core_data cores; 125c98d5d94SLen Brown struct pkg_data packages; 126c98d5d94SLen Brown } sum, average; 127c98d5d94SLen Brown 128c98d5d94SLen Brown 129c98d5d94SLen Brown struct topo_params { 130c98d5d94SLen Brown int num_packages; 131103a8feaSLen Brown int num_cpus; 132c98d5d94SLen Brown int num_cores; 133c98d5d94SLen Brown int max_cpu_num; 134c98d5d94SLen Brown int num_cores_per_pkg; 135c98d5d94SLen Brown int num_threads_per_core; 136c98d5d94SLen Brown } topo; 137103a8feaSLen Brown 138c98d5d94SLen Brown struct timeval tv_even, tv_odd, tv_delta; 139103a8feaSLen Brown 140c98d5d94SLen Brown void setup_all_buffers(void); 141103a8feaSLen Brown 142c98d5d94SLen Brown int cpu_is_not_present(int cpu) 143d15cf7c1SLen Brown { 144c98d5d94SLen Brown return !CPU_ISSET_S(cpu, cpu_present_setsize, cpu_present_set); 145c98d5d94SLen Brown } 146c98d5d94SLen Brown /* 147c98d5d94SLen Brown * run func(thread, core, package) in topology order 148c98d5d94SLen Brown * skip non-present cpus 149c98d5d94SLen Brown */ 150c98d5d94SLen Brown 151c98d5d94SLen Brown int for_all_cpus(int (func)(struct thread_data *, struct core_data *, struct pkg_data *), 152c98d5d94SLen Brown struct thread_data *thread_base, struct core_data *core_base, struct pkg_data *pkg_base) 153c98d5d94SLen Brown { 154c98d5d94SLen Brown int retval, pkg_no, core_no, thread_no; 155c98d5d94SLen Brown 156c98d5d94SLen Brown for (pkg_no = 0; pkg_no < topo.num_packages; ++pkg_no) { 157c98d5d94SLen Brown for (core_no = 0; core_no < topo.num_cores_per_pkg; ++core_no) { 158c98d5d94SLen Brown for (thread_no = 0; thread_no < 159c98d5d94SLen Brown topo.num_threads_per_core; ++thread_no) { 160c98d5d94SLen Brown struct thread_data *t; 161c98d5d94SLen Brown struct core_data *c; 162c98d5d94SLen Brown struct pkg_data *p; 163c98d5d94SLen Brown 164c98d5d94SLen Brown t = GET_THREAD(thread_base, thread_no, core_no, pkg_no); 165c98d5d94SLen Brown 166c98d5d94SLen Brown if (cpu_is_not_present(t->cpu_id)) 167c98d5d94SLen Brown continue; 168c98d5d94SLen Brown 169c98d5d94SLen Brown c = GET_CORE(core_base, core_no, pkg_no); 170c98d5d94SLen Brown p = GET_PKG(pkg_base, pkg_no); 171c98d5d94SLen Brown 172c98d5d94SLen Brown retval = func(t, c, p); 173c98d5d94SLen Brown if (retval) 174c98d5d94SLen Brown return retval; 175c98d5d94SLen Brown } 176c98d5d94SLen Brown } 177c98d5d94SLen Brown } 178d15cf7c1SLen Brown return 0; 179d15cf7c1SLen Brown } 180d15cf7c1SLen Brown 18188c3281fSLen Brown int cpu_migrate(int cpu) 18288c3281fSLen Brown { 183c98d5d94SLen Brown CPU_ZERO_S(cpu_affinity_setsize, cpu_affinity_set); 184c98d5d94SLen Brown CPU_SET_S(cpu, cpu_affinity_setsize, cpu_affinity_set); 185c98d5d94SLen Brown if (sched_setaffinity(0, cpu_affinity_setsize, cpu_affinity_set) == -1) 18688c3281fSLen Brown return -1; 18788c3281fSLen Brown else 18888c3281fSLen Brown return 0; 18988c3281fSLen Brown } 19088c3281fSLen Brown 19115aaa346SLen Brown int get_msr(int cpu, off_t offset, unsigned long long *msr) 192103a8feaSLen Brown { 193103a8feaSLen Brown ssize_t retval; 194103a8feaSLen Brown char pathname[32]; 195103a8feaSLen Brown int fd; 196103a8feaSLen Brown 197103a8feaSLen Brown sprintf(pathname, "/dev/cpu/%d/msr", cpu); 198103a8feaSLen Brown fd = open(pathname, O_RDONLY); 19915aaa346SLen Brown if (fd < 0) 20015aaa346SLen Brown return -1; 201103a8feaSLen Brown 20215aaa346SLen Brown retval = pread(fd, msr, sizeof *msr, offset); 203103a8feaSLen Brown close(fd); 20415aaa346SLen Brown 20515aaa346SLen Brown if (retval != sizeof *msr) 20615aaa346SLen Brown return -1; 20715aaa346SLen Brown 20815aaa346SLen Brown return 0; 209103a8feaSLen Brown } 210103a8feaSLen Brown 211*2f32edf1SLen Brown /* 212*2f32edf1SLen Brown * Truncate the 8 bytes we read from /dev/cpu/.../msr 213*2f32edf1SLen Brown * to the 4 bytes requested 214*2f32edf1SLen Brown */ 215*2f32edf1SLen Brown 216*2f32edf1SLen Brown int get_msr32(int cpu, off_t offset, unsigned int *msr) 217*2f32edf1SLen Brown { 218*2f32edf1SLen Brown int retval; 219*2f32edf1SLen Brown 220*2f32edf1SLen Brown unsigned long long msr64; 221*2f32edf1SLen Brown 222*2f32edf1SLen Brown retval = get_msr(cpu, offset, &msr64); 223*2f32edf1SLen Brown *msr = (unsigned int) msr64; 224*2f32edf1SLen Brown 225*2f32edf1SLen Brown return retval; 226*2f32edf1SLen Brown } 227*2f32edf1SLen Brown 228*2f32edf1SLen Brown 229a829eb4dSLen Brown void print_header(void) 230103a8feaSLen Brown { 231103a8feaSLen Brown if (show_pkg) 232c98d5d94SLen Brown outp += sprintf(outp, "pk"); 233e23da037SLen Brown if (show_pkg) 234c98d5d94SLen Brown outp += sprintf(outp, " "); 235103a8feaSLen Brown if (show_core) 236c98d5d94SLen Brown outp += sprintf(outp, "cor"); 237103a8feaSLen Brown if (show_cpu) 238c98d5d94SLen Brown outp += sprintf(outp, " CPU"); 239e23da037SLen Brown if (show_pkg || show_core || show_cpu) 240c98d5d94SLen Brown outp += sprintf(outp, " "); 241103a8feaSLen Brown if (do_nhm_cstates) 242c98d5d94SLen Brown outp += sprintf(outp, " %%c0"); 243103a8feaSLen Brown if (has_aperf) 244c98d5d94SLen Brown outp += sprintf(outp, " GHz"); 245c98d5d94SLen Brown outp += sprintf(outp, " TSC"); 246*2f32edf1SLen Brown if (extra_msr_offset32) 247*2f32edf1SLen Brown outp += sprintf(outp, " MSR 0x%04X", extra_msr_offset32); 248*2f32edf1SLen Brown if (extra_msr_offset64) 249*2f32edf1SLen Brown outp += sprintf(outp, " MSR 0x%04X", extra_msr_offset64); 250103a8feaSLen Brown if (do_nhm_cstates) 251c98d5d94SLen Brown outp += sprintf(outp, " %%c1"); 252103a8feaSLen Brown if (do_nhm_cstates) 253c98d5d94SLen Brown outp += sprintf(outp, " %%c3"); 254103a8feaSLen Brown if (do_nhm_cstates) 255c98d5d94SLen Brown outp += sprintf(outp, " %%c6"); 256103a8feaSLen Brown if (do_snb_cstates) 257c98d5d94SLen Brown outp += sprintf(outp, " %%c7"); 258103a8feaSLen Brown if (do_snb_cstates) 259c98d5d94SLen Brown outp += sprintf(outp, " %%pc2"); 260103a8feaSLen Brown if (do_nhm_cstates) 261c98d5d94SLen Brown outp += sprintf(outp, " %%pc3"); 262103a8feaSLen Brown if (do_nhm_cstates) 263c98d5d94SLen Brown outp += sprintf(outp, " %%pc6"); 264103a8feaSLen Brown if (do_snb_cstates) 265c98d5d94SLen Brown outp += sprintf(outp, " %%pc7"); 266103a8feaSLen Brown 267c98d5d94SLen Brown outp += sprintf(outp, "\n"); 268103a8feaSLen Brown } 269103a8feaSLen Brown 270c98d5d94SLen Brown int dump_counters(struct thread_data *t, struct core_data *c, 271c98d5d94SLen Brown struct pkg_data *p) 272103a8feaSLen Brown { 273c98d5d94SLen Brown fprintf(stderr, "t %p, c %p, p %p\n", t, c, p); 274c98d5d94SLen Brown 275c98d5d94SLen Brown if (t) { 276c98d5d94SLen Brown fprintf(stderr, "CPU: %d flags 0x%x\n", t->cpu_id, t->flags); 277c98d5d94SLen Brown fprintf(stderr, "TSC: %016llX\n", t->tsc); 278c98d5d94SLen Brown fprintf(stderr, "aperf: %016llX\n", t->aperf); 279c98d5d94SLen Brown fprintf(stderr, "mperf: %016llX\n", t->mperf); 280c98d5d94SLen Brown fprintf(stderr, "c1: %016llX\n", t->c1); 281*2f32edf1SLen Brown fprintf(stderr, "msr0x%x: %08X\n", 282*2f32edf1SLen Brown extra_msr_offset32, t->extra_msr32); 283c98d5d94SLen Brown fprintf(stderr, "msr0x%x: %016llX\n", 284*2f32edf1SLen Brown extra_msr_offset64, t->extra_msr64); 285103a8feaSLen Brown } 286103a8feaSLen Brown 287c98d5d94SLen Brown if (c) { 288c98d5d94SLen Brown fprintf(stderr, "core: %d\n", c->core_id); 289c98d5d94SLen Brown fprintf(stderr, "c3: %016llX\n", c->c3); 290c98d5d94SLen Brown fprintf(stderr, "c6: %016llX\n", c->c6); 291c98d5d94SLen Brown fprintf(stderr, "c7: %016llX\n", c->c7); 292c98d5d94SLen Brown } 293103a8feaSLen Brown 294c98d5d94SLen Brown if (p) { 295c98d5d94SLen Brown fprintf(stderr, "package: %d\n", p->package_id); 296c98d5d94SLen Brown fprintf(stderr, "pc2: %016llX\n", p->pc2); 297c98d5d94SLen Brown fprintf(stderr, "pc3: %016llX\n", p->pc3); 298c98d5d94SLen Brown fprintf(stderr, "pc6: %016llX\n", p->pc6); 299c98d5d94SLen Brown fprintf(stderr, "pc7: %016llX\n", p->pc7); 300c98d5d94SLen Brown } 301c98d5d94SLen Brown return 0; 302103a8feaSLen Brown } 303103a8feaSLen Brown 304e23da037SLen Brown /* 305e23da037SLen Brown * column formatting convention & formats 306e23da037SLen Brown * package: "pk" 2 columns %2d 307e23da037SLen Brown * core: "cor" 3 columns %3d 308e23da037SLen Brown * CPU: "CPU" 3 columns %3d 309e23da037SLen Brown * GHz: "GHz" 3 columns %3.2 310e23da037SLen Brown * TSC: "TSC" 3 columns %3.2 311e23da037SLen Brown * percentage " %pc3" %6.2 312e23da037SLen Brown */ 313c98d5d94SLen Brown int format_counters(struct thread_data *t, struct core_data *c, 314c98d5d94SLen Brown struct pkg_data *p) 315103a8feaSLen Brown { 316103a8feaSLen Brown double interval_float; 317103a8feaSLen Brown 318c98d5d94SLen Brown /* if showing only 1st thread in core and this isn't one, bail out */ 319c98d5d94SLen Brown if (show_core_only && !(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) 320c98d5d94SLen Brown return 0; 321c98d5d94SLen Brown 322c98d5d94SLen Brown /* if showing only 1st thread in pkg and this isn't one, bail out */ 323c98d5d94SLen Brown if (show_pkg_only && !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) 324c98d5d94SLen Brown return 0; 325c98d5d94SLen Brown 326103a8feaSLen Brown interval_float = tv_delta.tv_sec + tv_delta.tv_usec/1000000.0; 327103a8feaSLen Brown 328c98d5d94SLen Brown /* topo columns, print blanks on 1st (average) line */ 329c98d5d94SLen Brown if (t == &average.threads) { 330103a8feaSLen Brown if (show_pkg) 331c98d5d94SLen Brown outp += sprintf(outp, " "); 332e23da037SLen Brown if (show_pkg && show_core) 333c98d5d94SLen Brown outp += sprintf(outp, " "); 334103a8feaSLen Brown if (show_core) 335c98d5d94SLen Brown outp += sprintf(outp, " "); 336103a8feaSLen Brown if (show_cpu) 337c98d5d94SLen Brown outp += sprintf(outp, " " " "); 338103a8feaSLen Brown } else { 339c98d5d94SLen Brown if (show_pkg) { 340c98d5d94SLen Brown if (p) 341c98d5d94SLen Brown outp += sprintf(outp, "%2d", p->package_id); 342c98d5d94SLen Brown else 343c98d5d94SLen Brown outp += sprintf(outp, " "); 344c98d5d94SLen Brown } 345e23da037SLen Brown if (show_pkg && show_core) 346c98d5d94SLen Brown outp += sprintf(outp, " "); 347c98d5d94SLen Brown if (show_core) { 348c98d5d94SLen Brown if (c) 349c98d5d94SLen Brown outp += sprintf(outp, "%3d", c->core_id); 350c98d5d94SLen Brown else 351c98d5d94SLen Brown outp += sprintf(outp, " "); 352c98d5d94SLen Brown } 353103a8feaSLen Brown if (show_cpu) 354c98d5d94SLen Brown outp += sprintf(outp, " %3d", t->cpu_id); 355103a8feaSLen Brown } 356103a8feaSLen Brown 357103a8feaSLen Brown /* %c0 */ 358103a8feaSLen Brown if (do_nhm_cstates) { 359e23da037SLen Brown if (show_pkg || show_core || show_cpu) 360c98d5d94SLen Brown outp += sprintf(outp, " "); 361103a8feaSLen Brown if (!skip_c0) 362c98d5d94SLen Brown outp += sprintf(outp, "%6.2f", 100.0 * t->mperf/t->tsc); 363103a8feaSLen Brown else 364c98d5d94SLen Brown outp += sprintf(outp, " ****"); 365103a8feaSLen Brown } 366103a8feaSLen Brown 367103a8feaSLen Brown /* GHz */ 368103a8feaSLen Brown if (has_aperf) { 369103a8feaSLen Brown if (!aperf_mperf_unstable) { 370c98d5d94SLen Brown outp += sprintf(outp, " %3.2f", 371c98d5d94SLen Brown 1.0 * t->tsc / units * t->aperf / 372c98d5d94SLen Brown t->mperf / interval_float); 373103a8feaSLen Brown } else { 374c98d5d94SLen Brown if (t->aperf > t->tsc || t->mperf > t->tsc) { 375c98d5d94SLen Brown outp += sprintf(outp, " ***"); 376103a8feaSLen Brown } else { 377c98d5d94SLen Brown outp += sprintf(outp, "%3.1f*", 378c98d5d94SLen Brown 1.0 * t->tsc / 379c98d5d94SLen Brown units * t->aperf / 380c98d5d94SLen Brown t->mperf / interval_float); 381103a8feaSLen Brown } 382103a8feaSLen Brown } 383103a8feaSLen Brown } 384103a8feaSLen Brown 385103a8feaSLen Brown /* TSC */ 386c98d5d94SLen Brown outp += sprintf(outp, "%5.2f", 1.0 * t->tsc/units/interval_float); 387103a8feaSLen Brown 388*2f32edf1SLen Brown /* msr */ 389*2f32edf1SLen Brown if (extra_msr_offset32) 390*2f32edf1SLen Brown outp += sprintf(outp, " 0x%08x", t->extra_msr32); 391*2f32edf1SLen Brown 392130ff304SLen Brown /* MSR */ 393*2f32edf1SLen Brown if (extra_msr_offset64) 394*2f32edf1SLen Brown outp += sprintf(outp, " 0x%016llx", t->extra_msr64); 395130ff304SLen Brown 396103a8feaSLen Brown if (do_nhm_cstates) { 397103a8feaSLen Brown if (!skip_c1) 398c98d5d94SLen Brown outp += sprintf(outp, " %6.2f", 100.0 * t->c1/t->tsc); 399103a8feaSLen Brown else 400c98d5d94SLen Brown outp += sprintf(outp, " ****"); 401103a8feaSLen Brown } 402c98d5d94SLen Brown 403c98d5d94SLen Brown /* print per-core data only for 1st thread in core */ 404c98d5d94SLen Brown if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) 405c98d5d94SLen Brown goto done; 406c98d5d94SLen Brown 407103a8feaSLen Brown if (do_nhm_cstates) 408c98d5d94SLen Brown outp += sprintf(outp, " %6.2f", 100.0 * c->c3/t->tsc); 409103a8feaSLen Brown if (do_nhm_cstates) 410c98d5d94SLen Brown outp += sprintf(outp, " %6.2f", 100.0 * c->c6/t->tsc); 411103a8feaSLen Brown if (do_snb_cstates) 412c98d5d94SLen Brown outp += sprintf(outp, " %6.2f", 100.0 * c->c7/t->tsc); 413c98d5d94SLen Brown 414c98d5d94SLen Brown /* print per-package data only for 1st core in package */ 415c98d5d94SLen Brown if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) 416c98d5d94SLen Brown goto done; 417c98d5d94SLen Brown 418103a8feaSLen Brown if (do_snb_cstates) 419c98d5d94SLen Brown outp += sprintf(outp, " %6.2f", 100.0 * p->pc2/t->tsc); 420103a8feaSLen Brown if (do_nhm_cstates) 421c98d5d94SLen Brown outp += sprintf(outp, " %6.2f", 100.0 * p->pc3/t->tsc); 422103a8feaSLen Brown if (do_nhm_cstates) 423c98d5d94SLen Brown outp += sprintf(outp, " %6.2f", 100.0 * p->pc6/t->tsc); 424103a8feaSLen Brown if (do_snb_cstates) 425c98d5d94SLen Brown outp += sprintf(outp, " %6.2f", 100.0 * p->pc7/t->tsc); 426c98d5d94SLen Brown done: 427c98d5d94SLen Brown outp += sprintf(outp, "\n"); 428c98d5d94SLen Brown 429c98d5d94SLen Brown return 0; 430103a8feaSLen Brown } 431103a8feaSLen Brown 432c98d5d94SLen Brown void flush_stdout() 433103a8feaSLen Brown { 434c98d5d94SLen Brown fputs(output_buffer, stdout); 435c98d5d94SLen Brown outp = output_buffer; 436c98d5d94SLen Brown } 437c98d5d94SLen Brown void flush_stderr() 438c98d5d94SLen Brown { 439c98d5d94SLen Brown fputs(output_buffer, stderr); 440c98d5d94SLen Brown outp = output_buffer; 441c98d5d94SLen Brown } 442c98d5d94SLen Brown void format_all_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) 443c98d5d94SLen Brown { 444e23da037SLen Brown static int printed; 445103a8feaSLen Brown 446e23da037SLen Brown if (!printed || !summary_only) 447103a8feaSLen Brown print_header(); 448103a8feaSLen Brown 449c98d5d94SLen Brown if (topo.num_cpus > 1) 450c98d5d94SLen Brown format_counters(&average.threads, &average.cores, 451c98d5d94SLen Brown &average.packages); 452103a8feaSLen Brown 453e23da037SLen Brown printed = 1; 454e23da037SLen Brown 455e23da037SLen Brown if (summary_only) 456e23da037SLen Brown return; 457e23da037SLen Brown 458c98d5d94SLen Brown for_all_cpus(format_counters, t, c, p); 459103a8feaSLen Brown } 460103a8feaSLen Brown 461c98d5d94SLen Brown void 462c98d5d94SLen Brown delta_package(struct pkg_data *new, struct pkg_data *old) 463103a8feaSLen Brown { 464c98d5d94SLen Brown old->pc2 = new->pc2 - old->pc2; 465c98d5d94SLen Brown old->pc3 = new->pc3 - old->pc3; 466c98d5d94SLen Brown old->pc6 = new->pc6 - old->pc6; 467c98d5d94SLen Brown old->pc7 = new->pc7 - old->pc7; 468103a8feaSLen Brown } 469103a8feaSLen Brown 470c98d5d94SLen Brown void 471c98d5d94SLen Brown delta_core(struct core_data *new, struct core_data *old) 472c98d5d94SLen Brown { 473c98d5d94SLen Brown old->c3 = new->c3 - old->c3; 474c98d5d94SLen Brown old->c6 = new->c6 - old->c6; 475c98d5d94SLen Brown old->c7 = new->c7 - old->c7; 476103a8feaSLen Brown } 477c98d5d94SLen Brown 478c3ae331dSLen Brown /* 479c3ae331dSLen Brown * old = new - old 480c3ae331dSLen Brown */ 481c98d5d94SLen Brown void 482c98d5d94SLen Brown delta_thread(struct thread_data *new, struct thread_data *old, 483c98d5d94SLen Brown struct core_data *core_delta) 484c98d5d94SLen Brown { 485c98d5d94SLen Brown old->tsc = new->tsc - old->tsc; 486c98d5d94SLen Brown 487103a8feaSLen Brown /* check for TSC < 1 Mcycles over interval */ 488c98d5d94SLen Brown if (old->tsc < (1000 * 1000)) { 489c98d5d94SLen Brown fprintf(stderr, "Insanely slow TSC rate, TSC stops in idle?\n"); 490c98d5d94SLen Brown fprintf(stderr, "You can disable all c-states by booting with \"idle=poll\"\n"); 491c98d5d94SLen Brown fprintf(stderr, "or just the deep ones with \"processor.max_cstate=1\"\n"); 492103a8feaSLen Brown exit(-3); 493103a8feaSLen Brown } 494103a8feaSLen Brown 495c98d5d94SLen Brown old->c1 = new->c1 - old->c1; 496c98d5d94SLen Brown 497c98d5d94SLen Brown if ((new->aperf > old->aperf) && (new->mperf > old->mperf)) { 498c98d5d94SLen Brown old->aperf = new->aperf - old->aperf; 499c98d5d94SLen Brown old->mperf = new->mperf - old->mperf; 500c98d5d94SLen Brown } else { 501c98d5d94SLen Brown 502103a8feaSLen Brown if (!aperf_mperf_unstable) { 503103a8feaSLen Brown fprintf(stderr, "%s: APERF or MPERF went backwards *\n", progname); 504103a8feaSLen Brown fprintf(stderr, "* Frequency results do not cover entire interval *\n"); 505103a8feaSLen Brown fprintf(stderr, "* fix this by running Linux-2.6.30 or later *\n"); 506103a8feaSLen Brown 507103a8feaSLen Brown aperf_mperf_unstable = 1; 508103a8feaSLen Brown } 509103a8feaSLen Brown /* 510103a8feaSLen Brown * mperf delta is likely a huge "positive" number 511103a8feaSLen Brown * can not use it for calculating c0 time 512103a8feaSLen Brown */ 513103a8feaSLen Brown skip_c0 = 1; 514103a8feaSLen Brown skip_c1 = 1; 515103a8feaSLen Brown } 516103a8feaSLen Brown 517c98d5d94SLen Brown 518103a8feaSLen Brown /* 519c3ae331dSLen Brown * As counter collection is not atomic, 520c3ae331dSLen Brown * it is possible for mperf's non-halted cycles + idle states 521103a8feaSLen Brown * to exceed TSC's all cycles: show c1 = 0% in that case. 522103a8feaSLen Brown */ 523c3ae331dSLen Brown if ((old->mperf + core_delta->c3 + core_delta->c6 + core_delta->c7) > old->tsc) 524c98d5d94SLen Brown old->c1 = 0; 525c98d5d94SLen Brown else { 526c98d5d94SLen Brown /* normal case, derive c1 */ 527c98d5d94SLen Brown old->c1 = old->tsc - old->mperf - core_delta->c3 528c98d5d94SLen Brown - core_delta->c6 - core_delta->c7; 529c98d5d94SLen Brown } 530c3ae331dSLen Brown 531c98d5d94SLen Brown if (old->mperf == 0) { 532c3ae331dSLen Brown if (verbose > 1) fprintf(stderr, "cpu%d MPERF 0!\n", old->cpu_id); 533c98d5d94SLen Brown old->mperf = 1; /* divide by 0 protection */ 534c98d5d94SLen Brown } 535103a8feaSLen Brown 536103a8feaSLen Brown /* 537*2f32edf1SLen Brown * Extra MSR is a snapshot, simply copy latest w/o subtracting 538103a8feaSLen Brown */ 539*2f32edf1SLen Brown old->extra_msr32 = new->extra_msr32; 540*2f32edf1SLen Brown old->extra_msr64 = new->extra_msr64; 541103a8feaSLen Brown } 542c98d5d94SLen Brown 543c98d5d94SLen Brown int delta_cpu(struct thread_data *t, struct core_data *c, 544c98d5d94SLen Brown struct pkg_data *p, struct thread_data *t2, 545c98d5d94SLen Brown struct core_data *c2, struct pkg_data *p2) 546c98d5d94SLen Brown { 547c98d5d94SLen Brown /* calculate core delta only for 1st thread in core */ 548c98d5d94SLen Brown if (t->flags & CPU_IS_FIRST_THREAD_IN_CORE) 549c98d5d94SLen Brown delta_core(c, c2); 550c98d5d94SLen Brown 551c98d5d94SLen Brown /* always calculate thread delta */ 552c98d5d94SLen Brown delta_thread(t, t2, c2); /* c2 is core delta */ 553c98d5d94SLen Brown 554c98d5d94SLen Brown /* calculate package delta only for 1st core in package */ 555c98d5d94SLen Brown if (t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE) 556c98d5d94SLen Brown delta_package(p, p2); 557c98d5d94SLen Brown 558103a8feaSLen Brown return 0; 559103a8feaSLen Brown } 560103a8feaSLen Brown 561c98d5d94SLen Brown void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) 562103a8feaSLen Brown { 563c98d5d94SLen Brown t->tsc = 0; 564c98d5d94SLen Brown t->aperf = 0; 565c98d5d94SLen Brown t->mperf = 0; 566c98d5d94SLen Brown t->c1 = 0; 567103a8feaSLen Brown 568c98d5d94SLen Brown /* tells format_counters to dump all fields from this set */ 569c98d5d94SLen Brown t->flags = CPU_IS_FIRST_THREAD_IN_CORE | CPU_IS_FIRST_CORE_IN_PACKAGE; 570c98d5d94SLen Brown 571c98d5d94SLen Brown c->c3 = 0; 572c98d5d94SLen Brown c->c6 = 0; 573c98d5d94SLen Brown c->c7 = 0; 574c98d5d94SLen Brown 575c98d5d94SLen Brown p->pc2 = 0; 576c98d5d94SLen Brown p->pc3 = 0; 577c98d5d94SLen Brown p->pc6 = 0; 578c98d5d94SLen Brown p->pc7 = 0; 579103a8feaSLen Brown } 580c98d5d94SLen Brown int sum_counters(struct thread_data *t, struct core_data *c, 581c98d5d94SLen Brown struct pkg_data *p) 582103a8feaSLen Brown { 583c98d5d94SLen Brown average.threads.tsc += t->tsc; 584c98d5d94SLen Brown average.threads.aperf += t->aperf; 585c98d5d94SLen Brown average.threads.mperf += t->mperf; 586c98d5d94SLen Brown average.threads.c1 += t->c1; 58715aaa346SLen Brown 588c98d5d94SLen Brown /* sum per-core values only for 1st thread in core */ 589c98d5d94SLen Brown if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) 590c98d5d94SLen Brown return 0; 591c98d5d94SLen Brown 592c98d5d94SLen Brown average.cores.c3 += c->c3; 593c98d5d94SLen Brown average.cores.c6 += c->c6; 594c98d5d94SLen Brown average.cores.c7 += c->c7; 595c98d5d94SLen Brown 596c98d5d94SLen Brown /* sum per-pkg values only for 1st core in pkg */ 597c98d5d94SLen Brown if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) 598c98d5d94SLen Brown return 0; 599c98d5d94SLen Brown 600c98d5d94SLen Brown average.packages.pc2 += p->pc2; 601c98d5d94SLen Brown average.packages.pc3 += p->pc3; 602c98d5d94SLen Brown average.packages.pc6 += p->pc6; 603c98d5d94SLen Brown average.packages.pc7 += p->pc7; 604c98d5d94SLen Brown 605c98d5d94SLen Brown return 0; 606c98d5d94SLen Brown } 607c98d5d94SLen Brown /* 608c98d5d94SLen Brown * sum the counters for all cpus in the system 609c98d5d94SLen Brown * compute the weighted average 610c98d5d94SLen Brown */ 611c98d5d94SLen Brown void compute_average(struct thread_data *t, struct core_data *c, 612c98d5d94SLen Brown struct pkg_data *p) 613c98d5d94SLen Brown { 614c98d5d94SLen Brown clear_counters(&average.threads, &average.cores, &average.packages); 615c98d5d94SLen Brown 616c98d5d94SLen Brown for_all_cpus(sum_counters, t, c, p); 617c98d5d94SLen Brown 618c98d5d94SLen Brown average.threads.tsc /= topo.num_cpus; 619c98d5d94SLen Brown average.threads.aperf /= topo.num_cpus; 620c98d5d94SLen Brown average.threads.mperf /= topo.num_cpus; 621c98d5d94SLen Brown average.threads.c1 /= topo.num_cpus; 622c98d5d94SLen Brown 623c98d5d94SLen Brown average.cores.c3 /= topo.num_cores; 624c98d5d94SLen Brown average.cores.c6 /= topo.num_cores; 625c98d5d94SLen Brown average.cores.c7 /= topo.num_cores; 626c98d5d94SLen Brown 627c98d5d94SLen Brown average.packages.pc2 /= topo.num_packages; 628c98d5d94SLen Brown average.packages.pc3 /= topo.num_packages; 629c98d5d94SLen Brown average.packages.pc6 /= topo.num_packages; 630c98d5d94SLen Brown average.packages.pc7 /= topo.num_packages; 631c98d5d94SLen Brown } 632c98d5d94SLen Brown 633c98d5d94SLen Brown static unsigned long long rdtsc(void) 634c98d5d94SLen Brown { 635c98d5d94SLen Brown unsigned int low, high; 636c98d5d94SLen Brown 637c98d5d94SLen Brown asm volatile("rdtsc" : "=a" (low), "=d" (high)); 638c98d5d94SLen Brown 639c98d5d94SLen Brown return low | ((unsigned long long)high) << 32; 640c98d5d94SLen Brown } 641c98d5d94SLen Brown 642c98d5d94SLen Brown 643c98d5d94SLen Brown /* 644c98d5d94SLen Brown * get_counters(...) 645c98d5d94SLen Brown * migrate to cpu 646c98d5d94SLen Brown * acquire and record local counters for that cpu 647c98d5d94SLen Brown */ 648c98d5d94SLen Brown int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) 649c98d5d94SLen Brown { 650c98d5d94SLen Brown int cpu = t->cpu_id; 651c98d5d94SLen Brown 652c98d5d94SLen Brown if (cpu_migrate(cpu)) 65315aaa346SLen Brown return -1; 65415aaa346SLen Brown 655c98d5d94SLen Brown t->tsc = rdtsc(); /* we are running on local CPU of interest */ 65615aaa346SLen Brown 65715aaa346SLen Brown if (has_aperf) { 658c98d5d94SLen Brown if (get_msr(cpu, MSR_APERF, &t->aperf)) 659c98d5d94SLen Brown return -3; 660c98d5d94SLen Brown if (get_msr(cpu, MSR_MPERF, &t->mperf)) 661c98d5d94SLen Brown return -4; 66288c3281fSLen Brown } 66388c3281fSLen Brown 664*2f32edf1SLen Brown if (extra_msr_offset32) 665*2f32edf1SLen Brown if (get_msr32(cpu, extra_msr_offset32, &t->extra_msr32)) 666*2f32edf1SLen Brown return -5; 667*2f32edf1SLen Brown 668*2f32edf1SLen Brown if (extra_msr_offset64) 669*2f32edf1SLen Brown if (get_msr(cpu, extra_msr_offset64, &t->extra_msr64)) 670c98d5d94SLen Brown return -5; 671c98d5d94SLen Brown 672c98d5d94SLen Brown /* collect core counters only for 1st thread in core */ 673c98d5d94SLen Brown if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) 674c98d5d94SLen Brown return 0; 675c98d5d94SLen Brown 67615aaa346SLen Brown if (do_nhm_cstates) { 677c98d5d94SLen Brown if (get_msr(cpu, MSR_CORE_C3_RESIDENCY, &c->c3)) 678c98d5d94SLen Brown return -6; 679c98d5d94SLen Brown if (get_msr(cpu, MSR_CORE_C6_RESIDENCY, &c->c6)) 680c98d5d94SLen Brown return -7; 681103a8feaSLen Brown } 68215aaa346SLen Brown 68315aaa346SLen Brown if (do_snb_cstates) 684c98d5d94SLen Brown if (get_msr(cpu, MSR_CORE_C7_RESIDENCY, &c->c7)) 685c98d5d94SLen Brown return -8; 686c98d5d94SLen Brown 687c98d5d94SLen Brown /* collect package counters only for 1st core in package */ 688c98d5d94SLen Brown if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) 689c98d5d94SLen Brown return 0; 69015aaa346SLen Brown 69115aaa346SLen Brown if (do_nhm_cstates) { 692c98d5d94SLen Brown if (get_msr(cpu, MSR_PKG_C3_RESIDENCY, &p->pc3)) 693c98d5d94SLen Brown return -9; 694c98d5d94SLen Brown if (get_msr(cpu, MSR_PKG_C6_RESIDENCY, &p->pc6)) 695c98d5d94SLen Brown return -10; 69615aaa346SLen Brown } 69715aaa346SLen Brown if (do_snb_cstates) { 698c98d5d94SLen Brown if (get_msr(cpu, MSR_PKG_C2_RESIDENCY, &p->pc2)) 699c98d5d94SLen Brown return -11; 700c98d5d94SLen Brown if (get_msr(cpu, MSR_PKG_C7_RESIDENCY, &p->pc7)) 701c98d5d94SLen Brown return -12; 70215aaa346SLen Brown } 70315aaa346SLen Brown return 0; 704103a8feaSLen Brown } 705103a8feaSLen Brown 706c98d5d94SLen Brown void print_verbose_header(void) 707103a8feaSLen Brown { 708103a8feaSLen Brown unsigned long long msr; 709103a8feaSLen Brown unsigned int ratio; 710103a8feaSLen Brown 711103a8feaSLen Brown if (!do_nehalem_platform_info) 712103a8feaSLen Brown return; 713103a8feaSLen Brown 71415aaa346SLen Brown get_msr(0, MSR_NEHALEM_PLATFORM_INFO, &msr); 715103a8feaSLen Brown 7166574a5d5SLen Brown if (verbose > 1) 7176574a5d5SLen Brown fprintf(stderr, "MSR_NEHALEM_PLATFORM_INFO: 0x%llx\n", msr); 7186574a5d5SLen Brown 719103a8feaSLen Brown ratio = (msr >> 40) & 0xFF; 720103a8feaSLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n", 721103a8feaSLen Brown ratio, bclk, ratio * bclk); 722103a8feaSLen Brown 723103a8feaSLen Brown ratio = (msr >> 8) & 0xFF; 724103a8feaSLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz TSC frequency\n", 725103a8feaSLen Brown ratio, bclk, ratio * bclk); 726103a8feaSLen Brown 7276574a5d5SLen Brown if (!do_ivt_turbo_ratio_limit) 7286574a5d5SLen Brown goto print_nhm_turbo_ratio_limits; 7296574a5d5SLen Brown 7306574a5d5SLen Brown get_msr(0, MSR_IVT_TURBO_RATIO_LIMIT, &msr); 7316574a5d5SLen Brown 732103a8feaSLen Brown if (verbose > 1) 7336574a5d5SLen Brown fprintf(stderr, "MSR_IVT_TURBO_RATIO_LIMIT: 0x%llx\n", msr); 7346574a5d5SLen Brown 7356574a5d5SLen Brown ratio = (msr >> 56) & 0xFF; 7366574a5d5SLen Brown if (ratio) 7376574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 16 active cores\n", 7386574a5d5SLen Brown ratio, bclk, ratio * bclk); 7396574a5d5SLen Brown 7406574a5d5SLen Brown ratio = (msr >> 48) & 0xFF; 7416574a5d5SLen Brown if (ratio) 7426574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 15 active cores\n", 7436574a5d5SLen Brown ratio, bclk, ratio * bclk); 7446574a5d5SLen Brown 7456574a5d5SLen Brown ratio = (msr >> 40) & 0xFF; 7466574a5d5SLen Brown if (ratio) 7476574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 14 active cores\n", 7486574a5d5SLen Brown ratio, bclk, ratio * bclk); 7496574a5d5SLen Brown 7506574a5d5SLen Brown ratio = (msr >> 32) & 0xFF; 7516574a5d5SLen Brown if (ratio) 7526574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 13 active cores\n", 7536574a5d5SLen Brown ratio, bclk, ratio * bclk); 7546574a5d5SLen Brown 7556574a5d5SLen Brown ratio = (msr >> 24) & 0xFF; 7566574a5d5SLen Brown if (ratio) 7576574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 12 active cores\n", 7586574a5d5SLen Brown ratio, bclk, ratio * bclk); 7596574a5d5SLen Brown 7606574a5d5SLen Brown ratio = (msr >> 16) & 0xFF; 7616574a5d5SLen Brown if (ratio) 7626574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 11 active cores\n", 7636574a5d5SLen Brown ratio, bclk, ratio * bclk); 7646574a5d5SLen Brown 7656574a5d5SLen Brown ratio = (msr >> 8) & 0xFF; 7666574a5d5SLen Brown if (ratio) 7676574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 10 active cores\n", 7686574a5d5SLen Brown ratio, bclk, ratio * bclk); 7696574a5d5SLen Brown 7706574a5d5SLen Brown ratio = (msr >> 0) & 0xFF; 7716574a5d5SLen Brown if (ratio) 7726574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 9 active cores\n", 7736574a5d5SLen Brown ratio, bclk, ratio * bclk); 7746574a5d5SLen Brown 7756574a5d5SLen Brown print_nhm_turbo_ratio_limits: 776103a8feaSLen Brown 777103a8feaSLen Brown if (!do_nehalem_turbo_ratio_limit) 778103a8feaSLen Brown return; 779103a8feaSLen Brown 78015aaa346SLen Brown get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT, &msr); 781103a8feaSLen Brown 7826574a5d5SLen Brown if (verbose > 1) 7836574a5d5SLen Brown fprintf(stderr, "MSR_NEHALEM_TURBO_RATIO_LIMIT: 0x%llx\n", msr); 7846574a5d5SLen Brown 7856574a5d5SLen Brown ratio = (msr >> 56) & 0xFF; 7866574a5d5SLen Brown if (ratio) 7876574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 8 active cores\n", 7886574a5d5SLen Brown ratio, bclk, ratio * bclk); 7896574a5d5SLen Brown 7906574a5d5SLen Brown ratio = (msr >> 48) & 0xFF; 7916574a5d5SLen Brown if (ratio) 7926574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 7 active cores\n", 7936574a5d5SLen Brown ratio, bclk, ratio * bclk); 7946574a5d5SLen Brown 7956574a5d5SLen Brown ratio = (msr >> 40) & 0xFF; 7966574a5d5SLen Brown if (ratio) 7976574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 6 active cores\n", 7986574a5d5SLen Brown ratio, bclk, ratio * bclk); 7996574a5d5SLen Brown 8006574a5d5SLen Brown ratio = (msr >> 32) & 0xFF; 8016574a5d5SLen Brown if (ratio) 8026574a5d5SLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 5 active cores\n", 8036574a5d5SLen Brown ratio, bclk, ratio * bclk); 8046574a5d5SLen Brown 805103a8feaSLen Brown ratio = (msr >> 24) & 0xFF; 806103a8feaSLen Brown if (ratio) 807103a8feaSLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 4 active cores\n", 808103a8feaSLen Brown ratio, bclk, ratio * bclk); 809103a8feaSLen Brown 810103a8feaSLen Brown ratio = (msr >> 16) & 0xFF; 811103a8feaSLen Brown if (ratio) 812103a8feaSLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 3 active cores\n", 813103a8feaSLen Brown ratio, bclk, ratio * bclk); 814103a8feaSLen Brown 815103a8feaSLen Brown ratio = (msr >> 8) & 0xFF; 816103a8feaSLen Brown if (ratio) 817103a8feaSLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 2 active cores\n", 818103a8feaSLen Brown ratio, bclk, ratio * bclk); 819103a8feaSLen Brown 820103a8feaSLen Brown ratio = (msr >> 0) & 0xFF; 821103a8feaSLen Brown if (ratio) 822103a8feaSLen Brown fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 1 active cores\n", 823103a8feaSLen Brown ratio, bclk, ratio * bclk); 824103a8feaSLen Brown } 825103a8feaSLen Brown 826c98d5d94SLen Brown void free_all_buffers(void) 827103a8feaSLen Brown { 828c98d5d94SLen Brown CPU_FREE(cpu_present_set); 829c98d5d94SLen Brown cpu_present_set = NULL; 830c98d5d94SLen Brown cpu_present_set = 0; 831103a8feaSLen Brown 832c98d5d94SLen Brown CPU_FREE(cpu_affinity_set); 833c98d5d94SLen Brown cpu_affinity_set = NULL; 834c98d5d94SLen Brown cpu_affinity_setsize = 0; 835103a8feaSLen Brown 836c98d5d94SLen Brown free(thread_even); 837c98d5d94SLen Brown free(core_even); 838c98d5d94SLen Brown free(package_even); 839103a8feaSLen Brown 840c98d5d94SLen Brown thread_even = NULL; 841c98d5d94SLen Brown core_even = NULL; 842c98d5d94SLen Brown package_even = NULL; 843103a8feaSLen Brown 844c98d5d94SLen Brown free(thread_odd); 845c98d5d94SLen Brown free(core_odd); 846c98d5d94SLen Brown free(package_odd); 847103a8feaSLen Brown 848c98d5d94SLen Brown thread_odd = NULL; 849c98d5d94SLen Brown core_odd = NULL; 850c98d5d94SLen Brown package_odd = NULL; 851103a8feaSLen Brown 852c98d5d94SLen Brown free(output_buffer); 853c98d5d94SLen Brown output_buffer = NULL; 854c98d5d94SLen Brown outp = NULL; 855103a8feaSLen Brown } 856103a8feaSLen Brown 857103a8feaSLen Brown /* 858c98d5d94SLen Brown * cpu_is_first_sibling_in_core(cpu) 859c98d5d94SLen Brown * return 1 if given CPU is 1st HT sibling in the core 860103a8feaSLen Brown */ 861c98d5d94SLen Brown int cpu_is_first_sibling_in_core(int cpu) 862103a8feaSLen Brown { 863c98d5d94SLen Brown char path[64]; 864c98d5d94SLen Brown FILE *filep; 865c98d5d94SLen Brown int first_cpu; 866103a8feaSLen Brown 867c98d5d94SLen Brown sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", cpu); 868c98d5d94SLen Brown filep = fopen(path, "r"); 869c98d5d94SLen Brown if (filep == NULL) { 870c98d5d94SLen Brown perror(path); 871103a8feaSLen Brown exit(1); 872103a8feaSLen Brown } 873c98d5d94SLen Brown fscanf(filep, "%d", &first_cpu); 874c98d5d94SLen Brown fclose(filep); 875c98d5d94SLen Brown return (cpu == first_cpu); 876c98d5d94SLen Brown } 877103a8feaSLen Brown 878c98d5d94SLen Brown /* 879c98d5d94SLen Brown * cpu_is_first_core_in_package(cpu) 880c98d5d94SLen Brown * return 1 if given CPU is 1st core in package 881c98d5d94SLen Brown */ 882c98d5d94SLen Brown int cpu_is_first_core_in_package(int cpu) 883c98d5d94SLen Brown { 884c98d5d94SLen Brown char path[64]; 885c98d5d94SLen Brown FILE *filep; 886c98d5d94SLen Brown int first_cpu; 887c98d5d94SLen Brown 888c98d5d94SLen Brown sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list", cpu); 889c98d5d94SLen Brown filep = fopen(path, "r"); 890c98d5d94SLen Brown if (filep == NULL) { 891c98d5d94SLen Brown perror(path); 892103a8feaSLen Brown exit(1); 893103a8feaSLen Brown } 894c98d5d94SLen Brown fscanf(filep, "%d", &first_cpu); 895c98d5d94SLen Brown fclose(filep); 896c98d5d94SLen Brown return (cpu == first_cpu); 897103a8feaSLen Brown } 898103a8feaSLen Brown 899103a8feaSLen Brown int get_physical_package_id(int cpu) 900103a8feaSLen Brown { 901c98d5d94SLen Brown char path[80]; 902103a8feaSLen Brown FILE *filep; 903103a8feaSLen Brown int pkg; 904103a8feaSLen Brown 905103a8feaSLen Brown sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/physical_package_id", cpu); 906103a8feaSLen Brown filep = fopen(path, "r"); 907103a8feaSLen Brown if (filep == NULL) { 908103a8feaSLen Brown perror(path); 909103a8feaSLen Brown exit(1); 910103a8feaSLen Brown } 911103a8feaSLen Brown fscanf(filep, "%d", &pkg); 912103a8feaSLen Brown fclose(filep); 913103a8feaSLen Brown return pkg; 914103a8feaSLen Brown } 915103a8feaSLen Brown 916103a8feaSLen Brown int get_core_id(int cpu) 917103a8feaSLen Brown { 918c98d5d94SLen Brown char path[80]; 919103a8feaSLen Brown FILE *filep; 920103a8feaSLen Brown int core; 921103a8feaSLen Brown 922103a8feaSLen Brown sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/core_id", cpu); 923103a8feaSLen Brown filep = fopen(path, "r"); 924103a8feaSLen Brown if (filep == NULL) { 925103a8feaSLen Brown perror(path); 926103a8feaSLen Brown exit(1); 927103a8feaSLen Brown } 928103a8feaSLen Brown fscanf(filep, "%d", &core); 929103a8feaSLen Brown fclose(filep); 930103a8feaSLen Brown return core; 931103a8feaSLen Brown } 932103a8feaSLen Brown 933c98d5d94SLen Brown int get_num_ht_siblings(int cpu) 934c98d5d94SLen Brown { 935c98d5d94SLen Brown char path[80]; 936c98d5d94SLen Brown FILE *filep; 937c98d5d94SLen Brown int sib1, sib2; 938c98d5d94SLen Brown int matches; 939c98d5d94SLen Brown char character; 940c98d5d94SLen Brown 941c98d5d94SLen Brown sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", cpu); 942c98d5d94SLen Brown filep = fopen(path, "r"); 943c98d5d94SLen Brown if (filep == NULL) { 944c98d5d94SLen Brown perror(path); 945c98d5d94SLen Brown exit(1); 946c98d5d94SLen Brown } 947103a8feaSLen Brown /* 948c98d5d94SLen Brown * file format: 949c98d5d94SLen Brown * if a pair of number with a character between: 2 siblings (eg. 1-2, or 1,4) 950c98d5d94SLen Brown * otherwinse 1 sibling (self). 951c98d5d94SLen Brown */ 952c98d5d94SLen Brown matches = fscanf(filep, "%d%c%d\n", &sib1, &character, &sib2); 953c98d5d94SLen Brown 954c98d5d94SLen Brown fclose(filep); 955c98d5d94SLen Brown 956c98d5d94SLen Brown if (matches == 3) 957c98d5d94SLen Brown return 2; 958c98d5d94SLen Brown else 959c98d5d94SLen Brown return 1; 960c98d5d94SLen Brown } 961c98d5d94SLen Brown 962c98d5d94SLen Brown /* 963c98d5d94SLen Brown * run func(thread, core, package) in topology order 964c98d5d94SLen Brown * skip non-present cpus 965103a8feaSLen Brown */ 966103a8feaSLen Brown 967c98d5d94SLen Brown int for_all_cpus_2(int (func)(struct thread_data *, struct core_data *, 968c98d5d94SLen Brown struct pkg_data *, struct thread_data *, struct core_data *, 969c98d5d94SLen Brown struct pkg_data *), struct thread_data *thread_base, 970c98d5d94SLen Brown struct core_data *core_base, struct pkg_data *pkg_base, 971c98d5d94SLen Brown struct thread_data *thread_base2, struct core_data *core_base2, 972c98d5d94SLen Brown struct pkg_data *pkg_base2) 973c98d5d94SLen Brown { 974c98d5d94SLen Brown int retval, pkg_no, core_no, thread_no; 975c98d5d94SLen Brown 976c98d5d94SLen Brown for (pkg_no = 0; pkg_no < topo.num_packages; ++pkg_no) { 977c98d5d94SLen Brown for (core_no = 0; core_no < topo.num_cores_per_pkg; ++core_no) { 978c98d5d94SLen Brown for (thread_no = 0; thread_no < 979c98d5d94SLen Brown topo.num_threads_per_core; ++thread_no) { 980c98d5d94SLen Brown struct thread_data *t, *t2; 981c98d5d94SLen Brown struct core_data *c, *c2; 982c98d5d94SLen Brown struct pkg_data *p, *p2; 983c98d5d94SLen Brown 984c98d5d94SLen Brown t = GET_THREAD(thread_base, thread_no, core_no, pkg_no); 985c98d5d94SLen Brown 986c98d5d94SLen Brown if (cpu_is_not_present(t->cpu_id)) 987c98d5d94SLen Brown continue; 988c98d5d94SLen Brown 989c98d5d94SLen Brown t2 = GET_THREAD(thread_base2, thread_no, core_no, pkg_no); 990c98d5d94SLen Brown 991c98d5d94SLen Brown c = GET_CORE(core_base, core_no, pkg_no); 992c98d5d94SLen Brown c2 = GET_CORE(core_base2, core_no, pkg_no); 993c98d5d94SLen Brown 994c98d5d94SLen Brown p = GET_PKG(pkg_base, pkg_no); 995c98d5d94SLen Brown p2 = GET_PKG(pkg_base2, pkg_no); 996c98d5d94SLen Brown 997c98d5d94SLen Brown retval = func(t, c, p, t2, c2, p2); 998c98d5d94SLen Brown if (retval) 999c98d5d94SLen Brown return retval; 1000c98d5d94SLen Brown } 1001c98d5d94SLen Brown } 1002c98d5d94SLen Brown } 1003c98d5d94SLen Brown return 0; 1004c98d5d94SLen Brown } 1005c98d5d94SLen Brown 1006c98d5d94SLen Brown /* 1007c98d5d94SLen Brown * run func(cpu) on every cpu in /proc/stat 1008c98d5d94SLen Brown * return max_cpu number 1009c98d5d94SLen Brown */ 1010c98d5d94SLen Brown int for_all_proc_cpus(int (func)(int)) 1011103a8feaSLen Brown { 1012103a8feaSLen Brown FILE *fp; 1013c98d5d94SLen Brown int cpu_num; 1014103a8feaSLen Brown int retval; 1015103a8feaSLen Brown 1016103a8feaSLen Brown fp = fopen(proc_stat, "r"); 1017103a8feaSLen Brown if (fp == NULL) { 1018103a8feaSLen Brown perror(proc_stat); 1019103a8feaSLen Brown exit(1); 1020103a8feaSLen Brown } 1021103a8feaSLen Brown 1022103a8feaSLen Brown retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n"); 1023103a8feaSLen Brown if (retval != 0) { 1024103a8feaSLen Brown perror("/proc/stat format"); 1025103a8feaSLen Brown exit(1); 1026103a8feaSLen Brown } 1027103a8feaSLen Brown 1028c98d5d94SLen Brown while (1) { 1029c98d5d94SLen Brown retval = fscanf(fp, "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", &cpu_num); 1030103a8feaSLen Brown if (retval != 1) 1031103a8feaSLen Brown break; 1032103a8feaSLen Brown 1033c98d5d94SLen Brown retval = func(cpu_num); 1034c98d5d94SLen Brown if (retval) { 1035c98d5d94SLen Brown fclose(fp); 1036c98d5d94SLen Brown return(retval); 1037c98d5d94SLen Brown } 1038103a8feaSLen Brown } 1039103a8feaSLen Brown fclose(fp); 1040c98d5d94SLen Brown return 0; 1041103a8feaSLen Brown } 1042103a8feaSLen Brown 1043103a8feaSLen Brown void re_initialize(void) 1044103a8feaSLen Brown { 1045c98d5d94SLen Brown free_all_buffers(); 1046c98d5d94SLen Brown setup_all_buffers(); 1047c98d5d94SLen Brown printf("turbostat: re-initialized with num_cpus %d\n", topo.num_cpus); 1048103a8feaSLen Brown } 1049103a8feaSLen Brown 1050c98d5d94SLen Brown 1051103a8feaSLen Brown /* 1052c98d5d94SLen Brown * count_cpus() 1053c98d5d94SLen Brown * remember the last one seen, it will be the max 1054103a8feaSLen Brown */ 1055c98d5d94SLen Brown int count_cpus(int cpu) 1056103a8feaSLen Brown { 1057c98d5d94SLen Brown if (topo.max_cpu_num < cpu) 1058c98d5d94SLen Brown topo.max_cpu_num = cpu; 1059103a8feaSLen Brown 1060c98d5d94SLen Brown topo.num_cpus += 1; 1061c98d5d94SLen Brown return 0; 1062103a8feaSLen Brown } 1063c98d5d94SLen Brown int mark_cpu_present(int cpu) 1064c98d5d94SLen Brown { 1065c98d5d94SLen Brown CPU_SET_S(cpu, cpu_present_setsize, cpu_present_set); 106615aaa346SLen Brown return 0; 1067103a8feaSLen Brown } 1068103a8feaSLen Brown 1069103a8feaSLen Brown void turbostat_loop() 1070103a8feaSLen Brown { 1071c98d5d94SLen Brown int retval; 1072c98d5d94SLen Brown 1073103a8feaSLen Brown restart: 1074c98d5d94SLen Brown retval = for_all_cpus(get_counters, EVEN_COUNTERS); 1075c98d5d94SLen Brown if (retval) { 1076c98d5d94SLen Brown re_initialize(); 1077c98d5d94SLen Brown goto restart; 1078c98d5d94SLen Brown } 1079103a8feaSLen Brown gettimeofday(&tv_even, (struct timezone *)NULL); 1080103a8feaSLen Brown 1081103a8feaSLen Brown while (1) { 1082c98d5d94SLen Brown if (for_all_proc_cpus(cpu_is_not_present)) { 1083103a8feaSLen Brown re_initialize(); 1084103a8feaSLen Brown goto restart; 1085103a8feaSLen Brown } 1086103a8feaSLen Brown sleep(interval_sec); 1087c98d5d94SLen Brown retval = for_all_cpus(get_counters, ODD_COUNTERS); 1088c98d5d94SLen Brown if (retval) { 108915aaa346SLen Brown re_initialize(); 109015aaa346SLen Brown goto restart; 109115aaa346SLen Brown } 1092103a8feaSLen Brown gettimeofday(&tv_odd, (struct timezone *)NULL); 1093103a8feaSLen Brown timersub(&tv_odd, &tv_even, &tv_delta); 1094c98d5d94SLen Brown for_all_cpus_2(delta_cpu, ODD_COUNTERS, EVEN_COUNTERS); 1095c98d5d94SLen Brown compute_average(EVEN_COUNTERS); 1096c98d5d94SLen Brown format_all_counters(EVEN_COUNTERS); 1097c98d5d94SLen Brown flush_stdout(); 109815aaa346SLen Brown sleep(interval_sec); 1099c98d5d94SLen Brown retval = for_all_cpus(get_counters, EVEN_COUNTERS); 1100c98d5d94SLen Brown if (retval) { 1101103a8feaSLen Brown re_initialize(); 1102103a8feaSLen Brown goto restart; 1103103a8feaSLen Brown } 1104103a8feaSLen Brown gettimeofday(&tv_even, (struct timezone *)NULL); 1105103a8feaSLen Brown timersub(&tv_even, &tv_odd, &tv_delta); 1106c98d5d94SLen Brown for_all_cpus_2(delta_cpu, EVEN_COUNTERS, ODD_COUNTERS); 1107c98d5d94SLen Brown compute_average(ODD_COUNTERS); 1108c98d5d94SLen Brown format_all_counters(ODD_COUNTERS); 1109c98d5d94SLen Brown flush_stdout(); 1110103a8feaSLen Brown } 1111103a8feaSLen Brown } 1112103a8feaSLen Brown 1113103a8feaSLen Brown void check_dev_msr() 1114103a8feaSLen Brown { 1115103a8feaSLen Brown struct stat sb; 1116103a8feaSLen Brown 1117103a8feaSLen Brown if (stat("/dev/cpu/0/msr", &sb)) { 1118103a8feaSLen Brown fprintf(stderr, "no /dev/cpu/0/msr\n"); 1119103a8feaSLen Brown fprintf(stderr, "Try \"# modprobe msr\"\n"); 1120103a8feaSLen Brown exit(-5); 1121103a8feaSLen Brown } 1122103a8feaSLen Brown } 1123103a8feaSLen Brown 1124103a8feaSLen Brown void check_super_user() 1125103a8feaSLen Brown { 1126103a8feaSLen Brown if (getuid() != 0) { 1127103a8feaSLen Brown fprintf(stderr, "must be root\n"); 1128103a8feaSLen Brown exit(-6); 1129103a8feaSLen Brown } 1130103a8feaSLen Brown } 1131103a8feaSLen Brown 1132103a8feaSLen Brown int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model) 1133103a8feaSLen Brown { 1134103a8feaSLen Brown if (!genuine_intel) 1135103a8feaSLen Brown return 0; 1136103a8feaSLen Brown 1137103a8feaSLen Brown if (family != 6) 1138103a8feaSLen Brown return 0; 1139103a8feaSLen Brown 1140103a8feaSLen Brown switch (model) { 1141103a8feaSLen Brown case 0x1A: /* Core i7, Xeon 5500 series - Bloomfield, Gainstown NHM-EP */ 1142103a8feaSLen Brown case 0x1E: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper Forest */ 1143103a8feaSLen Brown case 0x1F: /* Core i7 and i5 Processor - Nehalem */ 1144103a8feaSLen Brown case 0x25: /* Westmere Client - Clarkdale, Arrandale */ 1145103a8feaSLen Brown case 0x2C: /* Westmere EP - Gulftown */ 1146103a8feaSLen Brown case 0x2A: /* SNB */ 1147103a8feaSLen Brown case 0x2D: /* SNB Xeon */ 1148553575f1SLen Brown case 0x3A: /* IVB */ 11491300651bSLen Brown case 0x3E: /* IVB Xeon */ 1150103a8feaSLen Brown return 1; 1151103a8feaSLen Brown case 0x2E: /* Nehalem-EX Xeon - Beckton */ 1152103a8feaSLen Brown case 0x2F: /* Westmere-EX Xeon - Eagleton */ 1153103a8feaSLen Brown default: 1154103a8feaSLen Brown return 0; 1155103a8feaSLen Brown } 1156103a8feaSLen Brown } 11576574a5d5SLen Brown int has_ivt_turbo_ratio_limit(unsigned int family, unsigned int model) 11586574a5d5SLen Brown { 11596574a5d5SLen Brown if (!genuine_intel) 11606574a5d5SLen Brown return 0; 11616574a5d5SLen Brown 11626574a5d5SLen Brown if (family != 6) 11636574a5d5SLen Brown return 0; 11646574a5d5SLen Brown 11656574a5d5SLen Brown switch (model) { 11666574a5d5SLen Brown case 0x3E: /* IVB Xeon */ 11676574a5d5SLen Brown return 1; 11686574a5d5SLen Brown default: 11696574a5d5SLen Brown return 0; 11706574a5d5SLen Brown } 11716574a5d5SLen Brown } 11726574a5d5SLen Brown 1173103a8feaSLen Brown 1174103a8feaSLen Brown int is_snb(unsigned int family, unsigned int model) 1175103a8feaSLen Brown { 1176103a8feaSLen Brown if (!genuine_intel) 1177103a8feaSLen Brown return 0; 1178103a8feaSLen Brown 1179103a8feaSLen Brown switch (model) { 1180103a8feaSLen Brown case 0x2A: 1181103a8feaSLen Brown case 0x2D: 1182650a37f3SLen Brown case 0x3A: /* IVB */ 11831300651bSLen Brown case 0x3E: /* IVB Xeon */ 1184103a8feaSLen Brown return 1; 1185103a8feaSLen Brown } 1186103a8feaSLen Brown return 0; 1187103a8feaSLen Brown } 1188103a8feaSLen Brown 1189103a8feaSLen Brown double discover_bclk(unsigned int family, unsigned int model) 1190103a8feaSLen Brown { 1191103a8feaSLen Brown if (is_snb(family, model)) 1192103a8feaSLen Brown return 100.00; 1193103a8feaSLen Brown else 1194103a8feaSLen Brown return 133.33; 1195103a8feaSLen Brown } 1196103a8feaSLen Brown 1197103a8feaSLen Brown void check_cpuid() 1198103a8feaSLen Brown { 1199103a8feaSLen Brown unsigned int eax, ebx, ecx, edx, max_level; 1200103a8feaSLen Brown unsigned int fms, family, model, stepping; 1201103a8feaSLen Brown 1202103a8feaSLen Brown eax = ebx = ecx = edx = 0; 1203103a8feaSLen Brown 1204103a8feaSLen Brown asm("cpuid" : "=a" (max_level), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0)); 1205103a8feaSLen Brown 1206103a8feaSLen Brown if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e) 1207103a8feaSLen Brown genuine_intel = 1; 1208103a8feaSLen Brown 1209103a8feaSLen Brown if (verbose) 1210103a8feaSLen Brown fprintf(stderr, "%.4s%.4s%.4s ", 1211103a8feaSLen Brown (char *)&ebx, (char *)&edx, (char *)&ecx); 1212103a8feaSLen Brown 1213103a8feaSLen Brown asm("cpuid" : "=a" (fms), "=c" (ecx), "=d" (edx) : "a" (1) : "ebx"); 1214103a8feaSLen Brown family = (fms >> 8) & 0xf; 1215103a8feaSLen Brown model = (fms >> 4) & 0xf; 1216103a8feaSLen Brown stepping = fms & 0xf; 1217103a8feaSLen Brown if (family == 6 || family == 0xf) 1218103a8feaSLen Brown model += ((fms >> 16) & 0xf) << 4; 1219103a8feaSLen Brown 1220103a8feaSLen Brown if (verbose) 1221103a8feaSLen Brown fprintf(stderr, "%d CPUID levels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n", 1222103a8feaSLen Brown max_level, family, model, stepping, family, model, stepping); 1223103a8feaSLen Brown 1224103a8feaSLen Brown if (!(edx & (1 << 5))) { 1225103a8feaSLen Brown fprintf(stderr, "CPUID: no MSR\n"); 1226103a8feaSLen Brown exit(1); 1227103a8feaSLen Brown } 1228103a8feaSLen Brown 1229103a8feaSLen Brown /* 1230103a8feaSLen Brown * check max extended function levels of CPUID. 1231103a8feaSLen Brown * This is needed to check for invariant TSC. 1232103a8feaSLen Brown * This check is valid for both Intel and AMD. 1233103a8feaSLen Brown */ 1234103a8feaSLen Brown ebx = ecx = edx = 0; 1235103a8feaSLen Brown asm("cpuid" : "=a" (max_level), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x80000000)); 1236103a8feaSLen Brown 1237103a8feaSLen Brown if (max_level < 0x80000007) { 1238103a8feaSLen Brown fprintf(stderr, "CPUID: no invariant TSC (max_level 0x%x)\n", max_level); 1239103a8feaSLen Brown exit(1); 1240103a8feaSLen Brown } 1241103a8feaSLen Brown 1242103a8feaSLen Brown /* 1243103a8feaSLen Brown * Non-Stop TSC is advertised by CPUID.EAX=0x80000007: EDX.bit8 1244103a8feaSLen Brown * this check is valid for both Intel and AMD 1245103a8feaSLen Brown */ 1246103a8feaSLen Brown asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x80000007)); 12478209e054SThomas Renninger has_invariant_tsc = edx & (1 << 8); 1248103a8feaSLen Brown 1249103a8feaSLen Brown if (!has_invariant_tsc) { 1250103a8feaSLen Brown fprintf(stderr, "No invariant TSC\n"); 1251103a8feaSLen Brown exit(1); 1252103a8feaSLen Brown } 1253103a8feaSLen Brown 1254103a8feaSLen Brown /* 1255103a8feaSLen Brown * APERF/MPERF is advertised by CPUID.EAX=0x6: ECX.bit0 1256103a8feaSLen Brown * this check is valid for both Intel and AMD 1257103a8feaSLen Brown */ 1258103a8feaSLen Brown 1259103a8feaSLen Brown asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x6)); 12608209e054SThomas Renninger has_aperf = ecx & (1 << 0); 1261103a8feaSLen Brown if (!has_aperf) { 1262103a8feaSLen Brown fprintf(stderr, "No APERF MSR\n"); 1263103a8feaSLen Brown exit(1); 1264103a8feaSLen Brown } 1265103a8feaSLen Brown 1266103a8feaSLen Brown do_nehalem_platform_info = genuine_intel && has_invariant_tsc; 1267103a8feaSLen Brown do_nhm_cstates = genuine_intel; /* all Intel w/ non-stop TSC have NHM counters */ 1268103a8feaSLen Brown do_snb_cstates = is_snb(family, model); 1269103a8feaSLen Brown bclk = discover_bclk(family, model); 1270103a8feaSLen Brown 1271103a8feaSLen Brown do_nehalem_turbo_ratio_limit = has_nehalem_turbo_ratio_limit(family, model); 12726574a5d5SLen Brown do_ivt_turbo_ratio_limit = has_ivt_turbo_ratio_limit(family, model); 1273103a8feaSLen Brown } 1274103a8feaSLen Brown 1275103a8feaSLen Brown 1276103a8feaSLen Brown void usage() 1277103a8feaSLen Brown { 1278*2f32edf1SLen Brown fprintf(stderr, "%s: [-v] [-m msr#] [-M MSR#] [-i interval_sec | command ...]\n", 1279103a8feaSLen Brown progname); 1280103a8feaSLen Brown exit(1); 1281103a8feaSLen Brown } 1282103a8feaSLen Brown 1283103a8feaSLen Brown 1284103a8feaSLen Brown /* 1285103a8feaSLen Brown * in /dev/cpu/ return success for names that are numbers 1286103a8feaSLen Brown * ie. filter out ".", "..", "microcode". 1287103a8feaSLen Brown */ 1288103a8feaSLen Brown int dir_filter(const struct dirent *dirp) 1289103a8feaSLen Brown { 1290103a8feaSLen Brown if (isdigit(dirp->d_name[0])) 1291103a8feaSLen Brown return 1; 1292103a8feaSLen Brown else 1293103a8feaSLen Brown return 0; 1294103a8feaSLen Brown } 1295103a8feaSLen Brown 1296103a8feaSLen Brown int open_dev_cpu_msr(int dummy1) 1297103a8feaSLen Brown { 1298103a8feaSLen Brown return 0; 1299103a8feaSLen Brown } 1300103a8feaSLen Brown 1301c98d5d94SLen Brown void topology_probe() 1302c98d5d94SLen Brown { 1303c98d5d94SLen Brown int i; 1304c98d5d94SLen Brown int max_core_id = 0; 1305c98d5d94SLen Brown int max_package_id = 0; 1306c98d5d94SLen Brown int max_siblings = 0; 1307c98d5d94SLen Brown struct cpu_topology { 1308c98d5d94SLen Brown int core_id; 1309c98d5d94SLen Brown int physical_package_id; 1310c98d5d94SLen Brown } *cpus; 1311c98d5d94SLen Brown 1312c98d5d94SLen Brown /* Initialize num_cpus, max_cpu_num */ 1313c98d5d94SLen Brown topo.num_cpus = 0; 1314c98d5d94SLen Brown topo.max_cpu_num = 0; 1315c98d5d94SLen Brown for_all_proc_cpus(count_cpus); 1316c98d5d94SLen Brown if (!summary_only && topo.num_cpus > 1) 1317c98d5d94SLen Brown show_cpu = 1; 1318c98d5d94SLen Brown 1319c98d5d94SLen Brown if (verbose > 1) 1320c98d5d94SLen Brown fprintf(stderr, "num_cpus %d max_cpu_num %d\n", topo.num_cpus, topo.max_cpu_num); 1321c98d5d94SLen Brown 1322c98d5d94SLen Brown cpus = calloc(1, (topo.max_cpu_num + 1) * sizeof(struct cpu_topology)); 1323c98d5d94SLen Brown if (cpus == NULL) { 1324c98d5d94SLen Brown perror("calloc cpus"); 1325c98d5d94SLen Brown exit(1); 1326c98d5d94SLen Brown } 1327c98d5d94SLen Brown 1328c98d5d94SLen Brown /* 1329c98d5d94SLen Brown * Allocate and initialize cpu_present_set 1330c98d5d94SLen Brown */ 1331c98d5d94SLen Brown cpu_present_set = CPU_ALLOC((topo.max_cpu_num + 1)); 1332c98d5d94SLen Brown if (cpu_present_set == NULL) { 1333c98d5d94SLen Brown perror("CPU_ALLOC"); 1334c98d5d94SLen Brown exit(3); 1335c98d5d94SLen Brown } 1336c98d5d94SLen Brown cpu_present_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1)); 1337c98d5d94SLen Brown CPU_ZERO_S(cpu_present_setsize, cpu_present_set); 1338c98d5d94SLen Brown for_all_proc_cpus(mark_cpu_present); 1339c98d5d94SLen Brown 1340c98d5d94SLen Brown /* 1341c98d5d94SLen Brown * Allocate and initialize cpu_affinity_set 1342c98d5d94SLen Brown */ 1343c98d5d94SLen Brown cpu_affinity_set = CPU_ALLOC((topo.max_cpu_num + 1)); 1344c98d5d94SLen Brown if (cpu_affinity_set == NULL) { 1345c98d5d94SLen Brown perror("CPU_ALLOC"); 1346c98d5d94SLen Brown exit(3); 1347c98d5d94SLen Brown } 1348c98d5d94SLen Brown cpu_affinity_setsize = CPU_ALLOC_SIZE((topo.max_cpu_num + 1)); 1349c98d5d94SLen Brown CPU_ZERO_S(cpu_affinity_setsize, cpu_affinity_set); 1350c98d5d94SLen Brown 1351c98d5d94SLen Brown 1352c98d5d94SLen Brown /* 1353c98d5d94SLen Brown * For online cpus 1354c98d5d94SLen Brown * find max_core_id, max_package_id 1355c98d5d94SLen Brown */ 1356c98d5d94SLen Brown for (i = 0; i <= topo.max_cpu_num; ++i) { 1357c98d5d94SLen Brown int siblings; 1358c98d5d94SLen Brown 1359c98d5d94SLen Brown if (cpu_is_not_present(i)) { 1360c98d5d94SLen Brown if (verbose > 1) 1361c98d5d94SLen Brown fprintf(stderr, "cpu%d NOT PRESENT\n", i); 1362c98d5d94SLen Brown continue; 1363c98d5d94SLen Brown } 1364c98d5d94SLen Brown cpus[i].core_id = get_core_id(i); 1365c98d5d94SLen Brown if (cpus[i].core_id > max_core_id) 1366c98d5d94SLen Brown max_core_id = cpus[i].core_id; 1367c98d5d94SLen Brown 1368c98d5d94SLen Brown cpus[i].physical_package_id = get_physical_package_id(i); 1369c98d5d94SLen Brown if (cpus[i].physical_package_id > max_package_id) 1370c98d5d94SLen Brown max_package_id = cpus[i].physical_package_id; 1371c98d5d94SLen Brown 1372c98d5d94SLen Brown siblings = get_num_ht_siblings(i); 1373c98d5d94SLen Brown if (siblings > max_siblings) 1374c98d5d94SLen Brown max_siblings = siblings; 1375c98d5d94SLen Brown if (verbose > 1) 1376c98d5d94SLen Brown fprintf(stderr, "cpu %d pkg %d core %d\n", 1377c98d5d94SLen Brown i, cpus[i].physical_package_id, cpus[i].core_id); 1378c98d5d94SLen Brown } 1379c98d5d94SLen Brown topo.num_cores_per_pkg = max_core_id + 1; 1380c98d5d94SLen Brown if (verbose > 1) 1381c98d5d94SLen Brown fprintf(stderr, "max_core_id %d, sizing for %d cores per package\n", 1382c98d5d94SLen Brown max_core_id, topo.num_cores_per_pkg); 1383c98d5d94SLen Brown if (!summary_only && topo.num_cores_per_pkg > 1) 1384c98d5d94SLen Brown show_core = 1; 1385c98d5d94SLen Brown 1386c98d5d94SLen Brown topo.num_packages = max_package_id + 1; 1387c98d5d94SLen Brown if (verbose > 1) 1388c98d5d94SLen Brown fprintf(stderr, "max_package_id %d, sizing for %d packages\n", 1389c98d5d94SLen Brown max_package_id, topo.num_packages); 1390c98d5d94SLen Brown if (!summary_only && topo.num_packages > 1) 1391c98d5d94SLen Brown show_pkg = 1; 1392c98d5d94SLen Brown 1393c98d5d94SLen Brown topo.num_threads_per_core = max_siblings; 1394c98d5d94SLen Brown if (verbose > 1) 1395c98d5d94SLen Brown fprintf(stderr, "max_siblings %d\n", max_siblings); 1396c98d5d94SLen Brown 1397c98d5d94SLen Brown free(cpus); 1398c98d5d94SLen Brown } 1399c98d5d94SLen Brown 1400c98d5d94SLen Brown void 1401c98d5d94SLen Brown allocate_counters(struct thread_data **t, struct core_data **c, struct pkg_data **p) 1402c98d5d94SLen Brown { 1403c98d5d94SLen Brown int i; 1404c98d5d94SLen Brown 1405c98d5d94SLen Brown *t = calloc(topo.num_threads_per_core * topo.num_cores_per_pkg * 1406c98d5d94SLen Brown topo.num_packages, sizeof(struct thread_data)); 1407c98d5d94SLen Brown if (*t == NULL) 1408c98d5d94SLen Brown goto error; 1409c98d5d94SLen Brown 1410c98d5d94SLen Brown for (i = 0; i < topo.num_threads_per_core * 1411c98d5d94SLen Brown topo.num_cores_per_pkg * topo.num_packages; i++) 1412c98d5d94SLen Brown (*t)[i].cpu_id = -1; 1413c98d5d94SLen Brown 1414c98d5d94SLen Brown *c = calloc(topo.num_cores_per_pkg * topo.num_packages, 1415c98d5d94SLen Brown sizeof(struct core_data)); 1416c98d5d94SLen Brown if (*c == NULL) 1417c98d5d94SLen Brown goto error; 1418c98d5d94SLen Brown 1419c98d5d94SLen Brown for (i = 0; i < topo.num_cores_per_pkg * topo.num_packages; i++) 1420c98d5d94SLen Brown (*c)[i].core_id = -1; 1421c98d5d94SLen Brown 1422c98d5d94SLen Brown *p = calloc(topo.num_packages, sizeof(struct pkg_data)); 1423c98d5d94SLen Brown if (*p == NULL) 1424c98d5d94SLen Brown goto error; 1425c98d5d94SLen Brown 1426c98d5d94SLen Brown for (i = 0; i < topo.num_packages; i++) 1427c98d5d94SLen Brown (*p)[i].package_id = i; 1428c98d5d94SLen Brown 1429c98d5d94SLen Brown return; 1430c98d5d94SLen Brown error: 1431c98d5d94SLen Brown perror("calloc counters"); 1432c98d5d94SLen Brown exit(1); 1433c98d5d94SLen Brown } 1434c98d5d94SLen Brown /* 1435c98d5d94SLen Brown * init_counter() 1436c98d5d94SLen Brown * 1437c98d5d94SLen Brown * set cpu_id, core_num, pkg_num 1438c98d5d94SLen Brown * set FIRST_THREAD_IN_CORE and FIRST_CORE_IN_PACKAGE 1439c98d5d94SLen Brown * 1440c98d5d94SLen Brown * increment topo.num_cores when 1st core in pkg seen 1441c98d5d94SLen Brown */ 1442c98d5d94SLen Brown void init_counter(struct thread_data *thread_base, struct core_data *core_base, 1443c98d5d94SLen Brown struct pkg_data *pkg_base, int thread_num, int core_num, 1444c98d5d94SLen Brown int pkg_num, int cpu_id) 1445c98d5d94SLen Brown { 1446c98d5d94SLen Brown struct thread_data *t; 1447c98d5d94SLen Brown struct core_data *c; 1448c98d5d94SLen Brown struct pkg_data *p; 1449c98d5d94SLen Brown 1450c98d5d94SLen Brown t = GET_THREAD(thread_base, thread_num, core_num, pkg_num); 1451c98d5d94SLen Brown c = GET_CORE(core_base, core_num, pkg_num); 1452c98d5d94SLen Brown p = GET_PKG(pkg_base, pkg_num); 1453c98d5d94SLen Brown 1454c98d5d94SLen Brown t->cpu_id = cpu_id; 1455c98d5d94SLen Brown if (thread_num == 0) { 1456c98d5d94SLen Brown t->flags |= CPU_IS_FIRST_THREAD_IN_CORE; 1457c98d5d94SLen Brown if (cpu_is_first_core_in_package(cpu_id)) 1458c98d5d94SLen Brown t->flags |= CPU_IS_FIRST_CORE_IN_PACKAGE; 1459c98d5d94SLen Brown } 1460c98d5d94SLen Brown 1461c98d5d94SLen Brown c->core_id = core_num; 1462c98d5d94SLen Brown p->package_id = pkg_num; 1463c98d5d94SLen Brown } 1464c98d5d94SLen Brown 1465c98d5d94SLen Brown 1466c98d5d94SLen Brown int initialize_counters(int cpu_id) 1467c98d5d94SLen Brown { 1468c98d5d94SLen Brown int my_thread_id, my_core_id, my_package_id; 1469c98d5d94SLen Brown 1470c98d5d94SLen Brown my_package_id = get_physical_package_id(cpu_id); 1471c98d5d94SLen Brown my_core_id = get_core_id(cpu_id); 1472c98d5d94SLen Brown 1473c98d5d94SLen Brown if (cpu_is_first_sibling_in_core(cpu_id)) { 1474c98d5d94SLen Brown my_thread_id = 0; 1475c98d5d94SLen Brown topo.num_cores++; 1476c98d5d94SLen Brown } else { 1477c98d5d94SLen Brown my_thread_id = 1; 1478c98d5d94SLen Brown } 1479c98d5d94SLen Brown 1480c98d5d94SLen Brown init_counter(EVEN_COUNTERS, my_thread_id, my_core_id, my_package_id, cpu_id); 1481c98d5d94SLen Brown init_counter(ODD_COUNTERS, my_thread_id, my_core_id, my_package_id, cpu_id); 1482c98d5d94SLen Brown return 0; 1483c98d5d94SLen Brown } 1484c98d5d94SLen Brown 1485c98d5d94SLen Brown void allocate_output_buffer() 1486c98d5d94SLen Brown { 1487c98d5d94SLen Brown output_buffer = calloc(1, (1 + topo.num_cpus) * 128); 1488c98d5d94SLen Brown outp = output_buffer; 1489c98d5d94SLen Brown if (outp == NULL) { 1490c98d5d94SLen Brown perror("calloc"); 1491c98d5d94SLen Brown exit(-1); 1492c98d5d94SLen Brown } 1493c98d5d94SLen Brown } 1494c98d5d94SLen Brown 1495c98d5d94SLen Brown void setup_all_buffers(void) 1496c98d5d94SLen Brown { 1497c98d5d94SLen Brown topology_probe(); 1498c98d5d94SLen Brown allocate_counters(&thread_even, &core_even, &package_even); 1499c98d5d94SLen Brown allocate_counters(&thread_odd, &core_odd, &package_odd); 1500c98d5d94SLen Brown allocate_output_buffer(); 1501c98d5d94SLen Brown for_all_proc_cpus(initialize_counters); 1502c98d5d94SLen Brown } 1503103a8feaSLen Brown void turbostat_init() 1504103a8feaSLen Brown { 1505103a8feaSLen Brown check_cpuid(); 1506103a8feaSLen Brown 1507103a8feaSLen Brown check_dev_msr(); 1508103a8feaSLen Brown check_super_user(); 1509103a8feaSLen Brown 1510c98d5d94SLen Brown setup_all_buffers(); 1511103a8feaSLen Brown 1512103a8feaSLen Brown if (verbose) 1513c98d5d94SLen Brown print_verbose_header(); 1514103a8feaSLen Brown } 1515103a8feaSLen Brown 1516103a8feaSLen Brown int fork_it(char **argv) 1517103a8feaSLen Brown { 1518103a8feaSLen Brown pid_t child_pid; 1519d15cf7c1SLen Brown 1520c98d5d94SLen Brown for_all_cpus(get_counters, EVEN_COUNTERS); 1521d15cf7c1SLen Brown /* clear affinity side-effect of get_counters() */ 1522d15cf7c1SLen Brown sched_setaffinity(0, cpu_present_setsize, cpu_present_set); 1523103a8feaSLen Brown gettimeofday(&tv_even, (struct timezone *)NULL); 1524103a8feaSLen Brown 1525103a8feaSLen Brown child_pid = fork(); 1526103a8feaSLen Brown if (!child_pid) { 1527103a8feaSLen Brown /* child */ 1528103a8feaSLen Brown execvp(argv[0], argv); 1529103a8feaSLen Brown } else { 1530103a8feaSLen Brown int status; 1531103a8feaSLen Brown 1532103a8feaSLen Brown /* parent */ 1533103a8feaSLen Brown if (child_pid == -1) { 1534103a8feaSLen Brown perror("fork"); 1535103a8feaSLen Brown exit(1); 1536103a8feaSLen Brown } 1537103a8feaSLen Brown 1538103a8feaSLen Brown signal(SIGINT, SIG_IGN); 1539103a8feaSLen Brown signal(SIGQUIT, SIG_IGN); 1540103a8feaSLen Brown if (waitpid(child_pid, &status, 0) == -1) { 1541103a8feaSLen Brown perror("wait"); 1542103a8feaSLen Brown exit(1); 1543103a8feaSLen Brown } 1544103a8feaSLen Brown } 1545c98d5d94SLen Brown /* 1546c98d5d94SLen Brown * n.b. fork_it() does not check for errors from for_all_cpus() 1547c98d5d94SLen Brown * because re-starting is problematic when forking 1548c98d5d94SLen Brown */ 1549c98d5d94SLen Brown for_all_cpus(get_counters, ODD_COUNTERS); 1550103a8feaSLen Brown gettimeofday(&tv_odd, (struct timezone *)NULL); 1551103a8feaSLen Brown timersub(&tv_odd, &tv_even, &tv_delta); 1552c98d5d94SLen Brown for_all_cpus_2(delta_cpu, ODD_COUNTERS, EVEN_COUNTERS); 1553c98d5d94SLen Brown compute_average(EVEN_COUNTERS); 1554c98d5d94SLen Brown format_all_counters(EVEN_COUNTERS); 1555c98d5d94SLen Brown flush_stderr(); 1556103a8feaSLen Brown 15576eab04a8SJustin P. Mattock fprintf(stderr, "%.6f sec\n", tv_delta.tv_sec + tv_delta.tv_usec/1000000.0); 1558103a8feaSLen Brown 1559103a8feaSLen Brown return 0; 1560103a8feaSLen Brown } 1561103a8feaSLen Brown 1562103a8feaSLen Brown void cmdline(int argc, char **argv) 1563103a8feaSLen Brown { 1564103a8feaSLen Brown int opt; 1565103a8feaSLen Brown 1566103a8feaSLen Brown progname = argv[0]; 1567103a8feaSLen Brown 1568*2f32edf1SLen Brown while ((opt = getopt(argc, argv, "+cpsvi:m:M:")) != -1) { 1569103a8feaSLen Brown switch (opt) { 1570c98d5d94SLen Brown case 'c': 1571c98d5d94SLen Brown show_core_only++; 1572c98d5d94SLen Brown break; 1573c98d5d94SLen Brown case 'p': 1574c98d5d94SLen Brown show_pkg_only++; 1575c98d5d94SLen Brown break; 1576e23da037SLen Brown case 's': 1577e23da037SLen Brown summary_only++; 1578e23da037SLen Brown break; 1579103a8feaSLen Brown case 'v': 1580103a8feaSLen Brown verbose++; 1581103a8feaSLen Brown break; 1582103a8feaSLen Brown case 'i': 1583103a8feaSLen Brown interval_sec = atoi(optarg); 1584103a8feaSLen Brown break; 1585*2f32edf1SLen Brown case 'm': 1586*2f32edf1SLen Brown sscanf(optarg, "%x", &extra_msr_offset32); 1587103a8feaSLen Brown if (verbose > 1) 1588*2f32edf1SLen Brown fprintf(stderr, "msr 0x%X\n", extra_msr_offset32); 1589*2f32edf1SLen Brown break; 1590*2f32edf1SLen Brown case 'M': 1591*2f32edf1SLen Brown sscanf(optarg, "%x", &extra_msr_offset64); 1592*2f32edf1SLen Brown if (verbose > 1) 1593*2f32edf1SLen Brown fprintf(stderr, "MSR 0x%X\n", extra_msr_offset64); 1594103a8feaSLen Brown break; 1595103a8feaSLen Brown default: 1596103a8feaSLen Brown usage(); 1597103a8feaSLen Brown } 1598103a8feaSLen Brown } 1599103a8feaSLen Brown } 1600103a8feaSLen Brown 1601103a8feaSLen Brown int main(int argc, char **argv) 1602103a8feaSLen Brown { 1603103a8feaSLen Brown cmdline(argc, argv); 1604103a8feaSLen Brown 1605103a8feaSLen Brown if (verbose > 1) 1606c98d5d94SLen Brown fprintf(stderr, "turbostat v2.0 May 16, 2012" 1607103a8feaSLen Brown " - Len Brown <lenb@kernel.org>\n"); 1608103a8feaSLen Brown 1609103a8feaSLen Brown turbostat_init(); 1610103a8feaSLen Brown 1611103a8feaSLen Brown /* 1612103a8feaSLen Brown * if any params left, it must be a command to fork 1613103a8feaSLen Brown */ 1614103a8feaSLen Brown if (argc - optind) 1615103a8feaSLen Brown return fork_it(argv + optind); 1616103a8feaSLen Brown else 1617103a8feaSLen Brown turbostat_loop(); 1618103a8feaSLen Brown 1619103a8feaSLen Brown return 0; 1620103a8feaSLen Brown } 1621