|
@@ -2,7 +2,7 @@
|
|
|
* turbostat -- show CPU frequency and C-state residency
|
|
|
* on modern Intel turbo-capable processors.
|
|
|
*
|
|
|
- * Copyright (c) 2010, Intel Corporation.
|
|
|
+ * Copyright (c) 2012 Intel Corporation.
|
|
|
* Len Brown <len.brown@intel.com>
|
|
|
*
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
@@ -19,6 +19,7 @@
|
|
|
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
*/
|
|
|
|
|
|
+#define _GNU_SOURCE
|
|
|
#include <stdio.h>
|
|
|
#include <unistd.h>
|
|
|
#include <sys/types.h>
|
|
@@ -32,6 +33,7 @@
|
|
|
#include <dirent.h>
|
|
|
#include <string.h>
|
|
|
#include <ctype.h>
|
|
|
+#include <sched.h>
|
|
|
|
|
|
#define MSR_TSC 0x10
|
|
|
#define MSR_NEHALEM_PLATFORM_INFO 0xCE
|
|
@@ -49,6 +51,7 @@
|
|
|
char *proc_stat = "/proc/stat";
|
|
|
unsigned int interval_sec = 5; /* set with -i interval_sec */
|
|
|
unsigned int verbose; /* set with -v */
|
|
|
+unsigned int summary_only; /* set with -s */
|
|
|
unsigned int skip_c0;
|
|
|
unsigned int skip_c1;
|
|
|
unsigned int do_nhm_cstates;
|
|
@@ -68,9 +71,10 @@ unsigned int show_cpu;
|
|
|
int aperf_mperf_unstable;
|
|
|
int backwards_count;
|
|
|
char *progname;
|
|
|
-int need_reinitialize;
|
|
|
|
|
|
int num_cpus;
|
|
|
+cpu_set_t *cpu_mask;
|
|
|
+size_t cpu_mask_size;
|
|
|
|
|
|
struct counters {
|
|
|
unsigned long long tsc; /* per thread */
|
|
@@ -99,44 +103,76 @@ struct timeval tv_even;
|
|
|
struct timeval tv_odd;
|
|
|
struct timeval tv_delta;
|
|
|
|
|
|
-unsigned long long get_msr(int cpu, off_t offset)
|
|
|
+/*
|
|
|
+ * cpu_mask_init(ncpus)
|
|
|
+ *
|
|
|
+ * allocate and clear cpu_mask
|
|
|
+ * set cpu_mask_size
|
|
|
+ */
|
|
|
+void cpu_mask_init(int ncpus)
|
|
|
+{
|
|
|
+ cpu_mask = CPU_ALLOC(ncpus);
|
|
|
+ if (cpu_mask == NULL) {
|
|
|
+ perror("CPU_ALLOC");
|
|
|
+ exit(3);
|
|
|
+ }
|
|
|
+ cpu_mask_size = CPU_ALLOC_SIZE(ncpus);
|
|
|
+ CPU_ZERO_S(cpu_mask_size, cpu_mask);
|
|
|
+}
|
|
|
+
|
|
|
+void cpu_mask_uninit()
|
|
|
+{
|
|
|
+ CPU_FREE(cpu_mask);
|
|
|
+ cpu_mask = NULL;
|
|
|
+ cpu_mask_size = 0;
|
|
|
+}
|
|
|
+
|
|
|
+int cpu_migrate(int cpu)
|
|
|
+{
|
|
|
+ CPU_ZERO_S(cpu_mask_size, cpu_mask);
|
|
|
+ CPU_SET_S(cpu, cpu_mask_size, cpu_mask);
|
|
|
+ if (sched_setaffinity(0, cpu_mask_size, cpu_mask) == -1)
|
|
|
+ return -1;
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int get_msr(int cpu, off_t offset, unsigned long long *msr)
|
|
|
{
|
|
|
ssize_t retval;
|
|
|
- unsigned long long msr;
|
|
|
char pathname[32];
|
|
|
int fd;
|
|
|
|
|
|
sprintf(pathname, "/dev/cpu/%d/msr", cpu);
|
|
|
fd = open(pathname, O_RDONLY);
|
|
|
- if (fd < 0) {
|
|
|
- perror(pathname);
|
|
|
- need_reinitialize = 1;
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- retval = pread(fd, &msr, sizeof msr, offset);
|
|
|
- if (retval != sizeof msr) {
|
|
|
- fprintf(stderr, "cpu%d pread(..., 0x%zx) = %jd\n",
|
|
|
- cpu, offset, retval);
|
|
|
- exit(-2);
|
|
|
- }
|
|
|
+ if (fd < 0)
|
|
|
+ return -1;
|
|
|
|
|
|
+ retval = pread(fd, msr, sizeof *msr, offset);
|
|
|
close(fd);
|
|
|
- return msr;
|
|
|
+
|
|
|
+ if (retval != sizeof *msr)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
void print_header(void)
|
|
|
{
|
|
|
if (show_pkg)
|
|
|
fprintf(stderr, "pk");
|
|
|
+ if (show_pkg)
|
|
|
+ fprintf(stderr, " ");
|
|
|
if (show_core)
|
|
|
- fprintf(stderr, " cr");
|
|
|
+ fprintf(stderr, "cor");
|
|
|
if (show_cpu)
|
|
|
fprintf(stderr, " CPU");
|
|
|
+ if (show_pkg || show_core || show_cpu)
|
|
|
+ fprintf(stderr, " ");
|
|
|
if (do_nhm_cstates)
|
|
|
- fprintf(stderr, " %%c0 ");
|
|
|
+ fprintf(stderr, " %%c0");
|
|
|
if (has_aperf)
|
|
|
- fprintf(stderr, " GHz");
|
|
|
+ fprintf(stderr, " GHz");
|
|
|
fprintf(stderr, " TSC");
|
|
|
if (do_nhm_cstates)
|
|
|
fprintf(stderr, " %%c1");
|
|
@@ -147,13 +183,13 @@ void print_header(void)
|
|
|
if (do_snb_cstates)
|
|
|
fprintf(stderr, " %%c7");
|
|
|
if (do_snb_cstates)
|
|
|
- fprintf(stderr, " %%pc2");
|
|
|
+ fprintf(stderr, " %%pc2");
|
|
|
if (do_nhm_cstates)
|
|
|
- fprintf(stderr, " %%pc3");
|
|
|
+ fprintf(stderr, " %%pc3");
|
|
|
if (do_nhm_cstates)
|
|
|
- fprintf(stderr, " %%pc6");
|
|
|
+ fprintf(stderr, " %%pc6");
|
|
|
if (do_snb_cstates)
|
|
|
- fprintf(stderr, " %%pc7");
|
|
|
+ fprintf(stderr, " %%pc7");
|
|
|
if (extra_msr_offset)
|
|
|
fprintf(stderr, " MSR 0x%x ", extra_msr_offset);
|
|
|
|
|
@@ -187,6 +223,15 @@ void dump_list(struct counters *cnt)
|
|
|
dump_cnt(cnt);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * column formatting convention & formats
|
|
|
+ * package: "pk" 2 columns %2d
|
|
|
+ * core: "cor" 3 columns %3d
|
|
|
+ * CPU: "CPU" 3 columns %3d
|
|
|
+ * GHz: "GHz" 3 columns %3.2
|
|
|
+ * TSC: "TSC" 3 columns %3.2
|
|
|
+ * percentage " %pc3" %6.2
|
|
|
+ */
|
|
|
void print_cnt(struct counters *p)
|
|
|
{
|
|
|
double interval_float;
|
|
@@ -196,39 +241,45 @@ void print_cnt(struct counters *p)
|
|
|
/* topology columns, print blanks on 1st (average) line */
|
|
|
if (p == cnt_average) {
|
|
|
if (show_pkg)
|
|
|
+ fprintf(stderr, " ");
|
|
|
+ if (show_pkg && show_core)
|
|
|
fprintf(stderr, " ");
|
|
|
if (show_core)
|
|
|
- fprintf(stderr, " ");
|
|
|
+ fprintf(stderr, " ");
|
|
|
if (show_cpu)
|
|
|
- fprintf(stderr, " ");
|
|
|
+ fprintf(stderr, " " " ");
|
|
|
} else {
|
|
|
if (show_pkg)
|
|
|
- fprintf(stderr, "%d", p->pkg);
|
|
|
+ fprintf(stderr, "%2d", p->pkg);
|
|
|
+ if (show_pkg && show_core)
|
|
|
+ fprintf(stderr, " ");
|
|
|
if (show_core)
|
|
|
- fprintf(stderr, "%4d", p->core);
|
|
|
+ fprintf(stderr, "%3d", p->core);
|
|
|
if (show_cpu)
|
|
|
- fprintf(stderr, "%4d", p->cpu);
|
|
|
+ fprintf(stderr, " %3d", p->cpu);
|
|
|
}
|
|
|
|
|
|
/* %c0 */
|
|
|
if (do_nhm_cstates) {
|
|
|
+ if (show_pkg || show_core || show_cpu)
|
|
|
+ fprintf(stderr, " ");
|
|
|
if (!skip_c0)
|
|
|
- fprintf(stderr, "%7.2f", 100.0 * p->mperf/p->tsc);
|
|
|
+ fprintf(stderr, "%6.2f", 100.0 * p->mperf/p->tsc);
|
|
|
else
|
|
|
- fprintf(stderr, " ****");
|
|
|
+ fprintf(stderr, " ****");
|
|
|
}
|
|
|
|
|
|
/* GHz */
|
|
|
if (has_aperf) {
|
|
|
if (!aperf_mperf_unstable) {
|
|
|
- fprintf(stderr, "%5.2f",
|
|
|
+ fprintf(stderr, " %3.2f",
|
|
|
1.0 * p->tsc / units * p->aperf /
|
|
|
p->mperf / interval_float);
|
|
|
} else {
|
|
|
if (p->aperf > p->tsc || p->mperf > p->tsc) {
|
|
|
- fprintf(stderr, " ****");
|
|
|
+ fprintf(stderr, " ***");
|
|
|
} else {
|
|
|
- fprintf(stderr, "%4.1f*",
|
|
|
+ fprintf(stderr, "%3.1f*",
|
|
|
1.0 * p->tsc /
|
|
|
units * p->aperf /
|
|
|
p->mperf / interval_float);
|
|
@@ -241,7 +292,7 @@ void print_cnt(struct counters *p)
|
|
|
|
|
|
if (do_nhm_cstates) {
|
|
|
if (!skip_c1)
|
|
|
- fprintf(stderr, "%7.2f", 100.0 * p->c1/p->tsc);
|
|
|
+ fprintf(stderr, " %6.2f", 100.0 * p->c1/p->tsc);
|
|
|
else
|
|
|
fprintf(stderr, " ****");
|
|
|
}
|
|
@@ -252,13 +303,13 @@ void print_cnt(struct counters *p)
|
|
|
if (do_snb_cstates)
|
|
|
fprintf(stderr, " %6.2f", 100.0 * p->c7/p->tsc);
|
|
|
if (do_snb_cstates)
|
|
|
- fprintf(stderr, " %5.2f", 100.0 * p->pc2/p->tsc);
|
|
|
+ fprintf(stderr, " %6.2f", 100.0 * p->pc2/p->tsc);
|
|
|
if (do_nhm_cstates)
|
|
|
- fprintf(stderr, " %5.2f", 100.0 * p->pc3/p->tsc);
|
|
|
+ fprintf(stderr, " %6.2f", 100.0 * p->pc3/p->tsc);
|
|
|
if (do_nhm_cstates)
|
|
|
- fprintf(stderr, " %5.2f", 100.0 * p->pc6/p->tsc);
|
|
|
+ fprintf(stderr, " %6.2f", 100.0 * p->pc6/p->tsc);
|
|
|
if (do_snb_cstates)
|
|
|
- fprintf(stderr, " %5.2f", 100.0 * p->pc7/p->tsc);
|
|
|
+ fprintf(stderr, " %6.2f", 100.0 * p->pc7/p->tsc);
|
|
|
if (extra_msr_offset)
|
|
|
fprintf(stderr, " 0x%016llx", p->extra_msr);
|
|
|
putc('\n', stderr);
|
|
@@ -267,12 +318,20 @@ void print_cnt(struct counters *p)
|
|
|
void print_counters(struct counters *counters)
|
|
|
{
|
|
|
struct counters *cnt;
|
|
|
+ static int printed;
|
|
|
+
|
|
|
|
|
|
- print_header();
|
|
|
+ if (!printed || !summary_only)
|
|
|
+ print_header();
|
|
|
|
|
|
if (num_cpus > 1)
|
|
|
print_cnt(cnt_average);
|
|
|
|
|
|
+ printed = 1;
|
|
|
+
|
|
|
+ if (summary_only)
|
|
|
+ return;
|
|
|
+
|
|
|
for (cnt = counters; cnt != NULL; cnt = cnt->next)
|
|
|
print_cnt(cnt);
|
|
|
|
|
@@ -440,31 +499,51 @@ void compute_average(struct counters *delta, struct counters *avg)
|
|
|
free(sum);
|
|
|
}
|
|
|
|
|
|
-void get_counters(struct counters *cnt)
|
|
|
+int get_counters(struct counters *cnt)
|
|
|
{
|
|
|
for ( ; cnt; cnt = cnt->next) {
|
|
|
- cnt->tsc = get_msr(cnt->cpu, MSR_TSC);
|
|
|
- if (do_nhm_cstates)
|
|
|
- cnt->c3 = get_msr(cnt->cpu, MSR_CORE_C3_RESIDENCY);
|
|
|
- if (do_nhm_cstates)
|
|
|
- cnt->c6 = get_msr(cnt->cpu, MSR_CORE_C6_RESIDENCY);
|
|
|
- if (do_snb_cstates)
|
|
|
- cnt->c7 = get_msr(cnt->cpu, MSR_CORE_C7_RESIDENCY);
|
|
|
- if (has_aperf)
|
|
|
- cnt->aperf = get_msr(cnt->cpu, MSR_APERF);
|
|
|
- if (has_aperf)
|
|
|
- cnt->mperf = get_msr(cnt->cpu, MSR_MPERF);
|
|
|
- if (do_snb_cstates)
|
|
|
- cnt->pc2 = get_msr(cnt->cpu, MSR_PKG_C2_RESIDENCY);
|
|
|
- if (do_nhm_cstates)
|
|
|
- cnt->pc3 = get_msr(cnt->cpu, MSR_PKG_C3_RESIDENCY);
|
|
|
- if (do_nhm_cstates)
|
|
|
- cnt->pc6 = get_msr(cnt->cpu, MSR_PKG_C6_RESIDENCY);
|
|
|
+
|
|
|
+ if (cpu_migrate(cnt->cpu))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (get_msr(cnt->cpu, MSR_TSC, &cnt->tsc))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (has_aperf) {
|
|
|
+ if (get_msr(cnt->cpu, MSR_APERF, &cnt->aperf))
|
|
|
+ return -1;
|
|
|
+ if (get_msr(cnt->cpu, MSR_MPERF, &cnt->mperf))
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (do_nhm_cstates) {
|
|
|
+ if (get_msr(cnt->cpu, MSR_CORE_C3_RESIDENCY, &cnt->c3))
|
|
|
+ return -1;
|
|
|
+ if (get_msr(cnt->cpu, MSR_CORE_C6_RESIDENCY, &cnt->c6))
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
if (do_snb_cstates)
|
|
|
- cnt->pc7 = get_msr(cnt->cpu, MSR_PKG_C7_RESIDENCY);
|
|
|
+ if (get_msr(cnt->cpu, MSR_CORE_C7_RESIDENCY, &cnt->c7))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ if (do_nhm_cstates) {
|
|
|
+ if (get_msr(cnt->cpu, MSR_PKG_C3_RESIDENCY, &cnt->pc3))
|
|
|
+ return -1;
|
|
|
+ if (get_msr(cnt->cpu, MSR_PKG_C6_RESIDENCY, &cnt->pc6))
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (do_snb_cstates) {
|
|
|
+ if (get_msr(cnt->cpu, MSR_PKG_C2_RESIDENCY, &cnt->pc2))
|
|
|
+ return -1;
|
|
|
+ if (get_msr(cnt->cpu, MSR_PKG_C7_RESIDENCY, &cnt->pc7))
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
if (extra_msr_offset)
|
|
|
- cnt->extra_msr = get_msr(cnt->cpu, extra_msr_offset);
|
|
|
+ if (get_msr(cnt->cpu, extra_msr_offset, &cnt->extra_msr))
|
|
|
+ return -1;
|
|
|
}
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
void print_nehalem_info(void)
|
|
@@ -475,7 +554,7 @@ void print_nehalem_info(void)
|
|
|
if (!do_nehalem_platform_info)
|
|
|
return;
|
|
|
|
|
|
- msr = get_msr(0, MSR_NEHALEM_PLATFORM_INFO);
|
|
|
+ get_msr(0, MSR_NEHALEM_PLATFORM_INFO, &msr);
|
|
|
|
|
|
ratio = (msr >> 40) & 0xFF;
|
|
|
fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n",
|
|
@@ -491,7 +570,7 @@ void print_nehalem_info(void)
|
|
|
if (!do_nehalem_turbo_ratio_limit)
|
|
|
return;
|
|
|
|
|
|
- msr = get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT);
|
|
|
+ get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT, &msr);
|
|
|
|
|
|
ratio = (msr >> 24) & 0xFF;
|
|
|
if (ratio)
|
|
@@ -557,7 +636,8 @@ void insert_counters(struct counters **list,
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- show_cpu = 1; /* there is more than one CPU */
|
|
|
+ if (!summary_only)
|
|
|
+ show_cpu = 1; /* there is more than one CPU */
|
|
|
|
|
|
/*
|
|
|
* insert on front of list.
|
|
@@ -575,13 +655,15 @@ void insert_counters(struct counters **list,
|
|
|
|
|
|
while (prev->next && (prev->next->pkg < new->pkg)) {
|
|
|
prev = prev->next;
|
|
|
- show_pkg = 1; /* there is more than 1 package */
|
|
|
+ if (!summary_only)
|
|
|
+ show_pkg = 1; /* there is more than 1 package */
|
|
|
}
|
|
|
|
|
|
while (prev->next && (prev->next->pkg == new->pkg)
|
|
|
&& (prev->next->core < new->core)) {
|
|
|
prev = prev->next;
|
|
|
- show_core = 1; /* there is more than 1 core */
|
|
|
+ if (!summary_only)
|
|
|
+ show_core = 1; /* there is more than 1 core */
|
|
|
}
|
|
|
|
|
|
while (prev->next && (prev->next->pkg == new->pkg)
|
|
@@ -681,7 +763,7 @@ int get_core_id(int cpu)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * run func(index, cpu) on every cpu in /proc/stat
|
|
|
+ * run func(pkg, core, cpu) on every cpu in /proc/stat
|
|
|
*/
|
|
|
|
|
|
int for_all_cpus(void (func)(int, int, int))
|
|
@@ -717,18 +799,18 @@ int for_all_cpus(void (func)(int, int, int))
|
|
|
|
|
|
void re_initialize(void)
|
|
|
{
|
|
|
- printf("turbostat: topology changed, re-initializing.\n");
|
|
|
free_all_counters();
|
|
|
num_cpus = for_all_cpus(alloc_new_counters);
|
|
|
- need_reinitialize = 0;
|
|
|
- printf("num_cpus is now %d\n", num_cpus);
|
|
|
+ cpu_mask_uninit();
|
|
|
+ cpu_mask_init(num_cpus);
|
|
|
+ printf("turbostat: re-initialized with num_cpus %d\n", num_cpus);
|
|
|
}
|
|
|
|
|
|
void dummy(int pkg, int core, int cpu) { return; }
|
|
|
/*
|
|
|
* check to see if a cpu came on-line
|
|
|
*/
|
|
|
-void verify_num_cpus(void)
|
|
|
+int verify_num_cpus(void)
|
|
|
{
|
|
|
int new_num_cpus;
|
|
|
|
|
@@ -738,8 +820,9 @@ void verify_num_cpus(void)
|
|
|
if (verbose)
|
|
|
printf("num_cpus was %d, is now %d\n",
|
|
|
num_cpus, new_num_cpus);
|
|
|
- need_reinitialize = 1;
|
|
|
+ return -1;
|
|
|
}
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
void turbostat_loop()
|
|
@@ -749,25 +832,25 @@ restart:
|
|
|
gettimeofday(&tv_even, (struct timezone *)NULL);
|
|
|
|
|
|
while (1) {
|
|
|
- verify_num_cpus();
|
|
|
- if (need_reinitialize) {
|
|
|
+ if (verify_num_cpus()) {
|
|
|
re_initialize();
|
|
|
goto restart;
|
|
|
}
|
|
|
sleep(interval_sec);
|
|
|
- get_counters(cnt_odd);
|
|
|
+ if (get_counters(cnt_odd)) {
|
|
|
+ re_initialize();
|
|
|
+ goto restart;
|
|
|
+ }
|
|
|
gettimeofday(&tv_odd, (struct timezone *)NULL);
|
|
|
-
|
|
|
compute_delta(cnt_odd, cnt_even, cnt_delta);
|
|
|
timersub(&tv_odd, &tv_even, &tv_delta);
|
|
|
compute_average(cnt_delta, cnt_average);
|
|
|
print_counters(cnt_delta);
|
|
|
- if (need_reinitialize) {
|
|
|
+ sleep(interval_sec);
|
|
|
+ if (get_counters(cnt_even)) {
|
|
|
re_initialize();
|
|
|
goto restart;
|
|
|
}
|
|
|
- sleep(interval_sec);
|
|
|
- get_counters(cnt_even);
|
|
|
gettimeofday(&tv_even, (struct timezone *)NULL);
|
|
|
compute_delta(cnt_even, cnt_odd, cnt_delta);
|
|
|
timersub(&tv_even, &tv_odd, &tv_delta);
|
|
@@ -953,6 +1036,7 @@ void turbostat_init()
|
|
|
check_super_user();
|
|
|
|
|
|
num_cpus = for_all_cpus(alloc_new_counters);
|
|
|
+ cpu_mask_init(num_cpus);
|
|
|
|
|
|
if (verbose)
|
|
|
print_nehalem_info();
|
|
@@ -1005,8 +1089,11 @@ void cmdline(int argc, char **argv)
|
|
|
|
|
|
progname = argv[0];
|
|
|
|
|
|
- while ((opt = getopt(argc, argv, "+vi:M:")) != -1) {
|
|
|
+ while ((opt = getopt(argc, argv, "+svi:M:")) != -1) {
|
|
|
switch (opt) {
|
|
|
+ case 's':
|
|
|
+ summary_only++;
|
|
|
+ break;
|
|
|
case 'v':
|
|
|
verbose++;
|
|
|
break;
|