Re: [PATCH v4] tools: create power/x86/turbostat

From: Thiago Farina
Date: Tue Dec 07 2010 - 09:29:22 EST


On Tue, Dec 7, 2010 at 4:00 AM, Len Brown <lenb@xxxxxxxxxx> wrote:
> From: Len Brown <len.brown@xxxxxxxxx>
>
> turbostat is a tool to help verify proper operation
> of processor power management states on modern x86.
>
> Signed-off-by: Len Brown <len.brown@xxxxxxxxx>
> ---
> v4: fix open file descriptor leak, reported by David C. Wong
>
> Âtools/power/x86/turbostat/Makefile  Â|  Â8 +
> Âtools/power/x86/turbostat/turbostat.8 | Â172 ++++++
> Âtools/power/x86/turbostat/turbostat.c | 1048 +++++++++++++++++++++++++++++++++
> Â3 files changed, 1228 insertions(+), 0 deletions(-)
>
> diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
> new file mode 100644
> index 0000000..4c6983d
> --- /dev/null
> +++ b/tools/power/x86/turbostat/turbostat.c
> @@ -0,0 +1,1048 @@
> +/*
> + * turbostat -- show CPU frequency and C-state residency
> + * on modern Intel turbo-capable processors.
> + *
> + * Copyright (c) 2010, Intel Corporation.
> + * Len Brown <len.brown@xxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. ÂSee the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
> +#include <sys/stat.h>
> +#include <sys/resource.h>
> +#include <fcntl.h>
> +#include <signal.h>
> +#include <sys/time.h>
> +#include <stdlib.h>
> +#include <dirent.h>
> +#include <string.h>
> +#include <ctype.h>
> +
> +#define MSR_TSC Â Â Â Â0x10
> +#define MSR_NEHALEM_PLATFORM_INFO Â Â Â0xCE
> +#define MSR_NEHALEM_TURBO_RATIO_LIMIT Â0x1AD
> +#define MSR_APERF Â Â Â0xE8
> +#define MSR_MPERF Â Â Â0xE7
> +#define MSR_PKG_C2_RESIDENCY Â 0x60D Â /* SNB only */
> +#define MSR_PKG_C3_RESIDENCY Â 0x3F8
> +#define MSR_PKG_C6_RESIDENCY Â 0x3F9
> +#define MSR_PKG_C7_RESIDENCY Â 0x3FA Â /* SNB only */
> +#define MSR_CORE_C3_RESIDENCY Â0x3FC
> +#define MSR_CORE_C6_RESIDENCY Â0x3FD
> +#define MSR_CORE_C7_RESIDENCY Â0x3FE Â /* SNB only */
> +
> +char *proc_stat = "/proc/stat";
> +unsigned int interval_sec = 5; /* set with -i interval_sec */
> +unsigned int verbose; Â Â Â Â Â/* set with -v */
> +unsigned int skip_c0;
> +unsigned int skip_c1;
> +unsigned int do_nhm_cstates;
> +unsigned int do_snb_cstates;
> +unsigned int has_aperf;
> +unsigned int units = 1000000000; Â Â Â /* Ghz etc */
> +unsigned int genuine_intel;
> +unsigned int has_invariant_tsc;
> +unsigned int do_nehalem_platform_info;
> +unsigned int do_nehalem_turbo_ratio_limit;
> +unsigned int extra_msr_offset;
> +double bclk;
> +unsigned int show_pkg;
> +unsigned int show_core;
> +unsigned int show_cpu;
> +
> +int aperf_mperf_unstable;
> +int backwards_count;
> +char *progname;
> +int need_reinitialize;
> +
> +int num_cpus;
> +
> +typedef struct per_cpu_counters {
> + Â Â Â unsigned long long tsc; Â Â Â Â /* per thread */
> + Â Â Â unsigned long long aperf; Â Â Â /* per thread */
> + Â Â Â unsigned long long mperf; Â Â Â /* per thread */
> + Â Â Â unsigned long long c1; Â/* per thread (calculated) */
> + Â Â Â unsigned long long c3; Â/* per core */
> + Â Â Â unsigned long long c6; Â/* per core */
> + Â Â Â unsigned long long c7; Â/* per core */
> + Â Â Â unsigned long long pc2; /* per package */
> + Â Â Â unsigned long long pc3; /* per package */
> + Â Â Â unsigned long long pc6; /* per package */
> + Â Â Â unsigned long long pc7; /* per package */
> + Â Â Â unsigned long long extra_msr; Â /* per thread */
> + Â Â Â int pkg;
> + Â Â Â int core;
> + Â Â Â int cpu;
> + Â Â Â struct per_cpu_counters *next;
> +} PCC;
> +

Why the typedef? Isn't preferable to use struct per_cpu_counters all
around (yeah I know it requires more typing, but is less obfuscated).

> +PCC *pcc_even;
> +PCC *pcc_odd;
> +PCC *pcc_delta;
> +PCC *pcc_average;
> +struct timeval tv_even;
> +struct timeval tv_odd;
> +struct timeval tv_delta;
> +
> +unsigned long long get_msr(int cpu, off_t offset)
> +{
> + Â Â Â 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);
> + Â Â Â }
> +
> + Â Â Â close(fd);
> + Â Â Â return msr;
> +}
> +
> +void print_header()
use void in the paramenter? Also make this static?

> +
> +#define SUBTRACT_COUNTER(after, before, delta) (delta = (after - before), (before > after))
> +
> +
Remove this extra blank line?

> +
> +void get_counters(PCC *pcc)
> +{
> + Â Â Â for ( ; pcc; pcc = pcc->next) {
> + Â Â Â Â Â Â Â pcc->tsc = get_msr(pcc->cpu, MSR_TSC);
> + Â Â Â Â Â Â Â if (do_nhm_cstates)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->c3 = get_msr(pcc->cpu, MSR_CORE_C3_RESIDENCY);
> + Â Â Â Â Â Â Â if (do_nhm_cstates)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->c6 = get_msr(pcc->cpu, MSR_CORE_C6_RESIDENCY);
> + Â Â Â Â Â Â Â if (do_snb_cstates)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->c7 = get_msr(pcc->cpu, MSR_CORE_C7_RESIDENCY);
> + Â Â Â Â Â Â Â if (has_aperf)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->aperf = get_msr(pcc->cpu, MSR_APERF);
> + Â Â Â Â Â Â Â if (has_aperf)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->mperf = get_msr(pcc->cpu, MSR_MPERF);
> + Â Â Â Â Â Â Â if (do_snb_cstates)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->pc2 = get_msr(pcc->cpu, MSR_PKG_C2_RESIDENCY);
> + Â Â Â Â Â Â Â if (do_nhm_cstates)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->pc3 = get_msr(pcc->cpu, MSR_PKG_C3_RESIDENCY);
> + Â Â Â Â Â Â Â if (do_nhm_cstates)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->pc6 = get_msr(pcc->cpu, MSR_PKG_C6_RESIDENCY);
> + Â Â Â Â Â Â Â if (do_snb_cstates)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->pc7 = get_msr(pcc->cpu, MSR_PKG_C7_RESIDENCY);
> + Â Â Â Â Â Â Â if (extra_msr_offset)
> + Â Â Â Â Â Â Â Â Â Â Â pcc->extra_msr = get_msr(pcc->cpu, extra_msr_offset);
> + Â Â Â }
> +}
> +
> +
Remove this blank line too?

> +void print_nehalem_info()
> +{
Add void into the parameter?

> + Â Â Â unsigned long long msr;
> + Â Â Â unsigned int ratio;
> +
> + Â Â Â if (!do_nehalem_platform_info)
> + Â Â Â Â Â Â Â return;
> +
> + Â Â Â msr = get_msr(0, MSR_NEHALEM_PLATFORM_INFO);
> +
> + Â Â Â ratio = (msr >> 40) & 0xFF;
> + Â Â Â fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n",
> + Â Â Â Â Â Â Â ratio, bclk, ratio * bclk);
> +
> + Â Â Â ratio = (msr >> 8) & 0xFF;
> + Â Â Â fprintf(stderr, "%d * %.0f = %.0f MHz TSC frequency\n",
> + Â Â Â Â Â Â Â ratio, bclk, ratio * bclk);
> +
> + Â Â Â if (verbose > 1)
> + Â Â Â Â Â Â Â fprintf(stderr, "MSR_NEHALEM_PLATFORM_INFO: 0x%llx\n", msr);
> +
> + Â Â Â if (!do_nehalem_turbo_ratio_limit)
> + Â Â Â Â Â Â Â return;
> +
> + Â Â Â msr = get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT);
> +
> + Â Â Â ratio = (msr >> 24) & 0xFF;
> + Â Â Â if (ratio)
> + Â Â Â Â Â Â Â fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 4 active cores\n",
> + Â Â Â Â Â Â Â Â Â Â Â ratio, bclk, ratio * bclk);
> +
> + Â Â Â ratio = (msr >> 16) & 0xFF;
> + Â Â Â if (ratio)
> + Â Â Â Â Â Â Â fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 3 active cores\n",
> + Â Â Â Â Â Â Â Â Â Â Â ratio, bclk, ratio * bclk);
> +
> + Â Â Â ratio = (msr >> 8) & 0xFF;
> + Â Â Â if (ratio)
> + Â Â Â Â Â Â Â fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 2 active cores\n",
> + Â Â Â Â Â Â Â Â Â Â Â ratio, bclk, ratio * bclk);
> +
> + Â Â Â ratio = (msr >> 0) & 0xFF;
> + Â Â Â if (ratio)
> + Â Â Â Â Â Â Â fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 1 active cores\n",
> + Â Â Â Â Â Â Â Â Â Â Â ratio, bclk, ratio * bclk);
> +
> +}
> +
> +void free_counter_list(PCC *list)
> +{
> + Â Â Â PCC *p;
> +
> + Â Â Â for (p = list; p; ) {
> + Â Â Â Â Â Â Â PCC *free_me;
> +
> + Â Â Â Â Â Â Â free_me = p;
> + Â Â Â Â Â Â Â p = p->next;
> + Â Â Â Â Â Â Â free(free_me);
> + Â Â Â }
> + Â Â Â return;
Why the return here? Isn't redundant?

> +
> +void insert_cpu_counters(PCC **list, PCC *new)
> +{
> + Â Â Â PCC *prev;
> +
> + Â Â Â /*
> + Â Â Â Â* list was empty
> + Â Â Â Â*/
> + Â Â Â if (*list == NULL) {
> + Â Â Â Â Â Â Â new->next = *list;
> + Â Â Â Â Â Â Â *list = new;
> + Â Â Â Â Â Â Â return;
> + Â Â Â }
> +
> + Â Â Â show_cpu = 1; Â /* there is more than one CPU */
> +
> + Â Â Â /*
> + Â Â Â Â* insert on front of list.
> + Â Â Â Â* It is sorted by ascending package#, core#, cpu#
> + Â Â Â Â*/
> + Â Â Â if (((*list)->pkg > new->pkg) ||
> + Â Â Â Â Â (((*list)->pkg == new->pkg) && ((*list)->core > new->core)) ||
> + Â Â Â Â Â (((*list)->pkg == new->pkg) && ((*list)->core == new->core) && ((*list)->cpu > new->cpu))) {
> + Â Â Â Â Â Â Â new->next = *list;
> + Â Â Â Â Â Â Â *list = new;
> + Â Â Â Â Â Â Â return;
> + Â Â Â }
> +
> + Â Â Â prev = *list;
> +
> + Â Â Â while (prev->next && (prev->next->pkg < new->pkg)) {
> + Â Â Â Â Â Â Â prev = prev->next;
> + Â Â Â Â Â Â Â 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 */
> + Â Â Â }
> +
> + Â Â Â while (prev->next && (prev->next->pkg == new->pkg)
> + Â Â Â Â Â Â Â && (prev->next->core == new->core)
> + Â Â Â Â Â Â Â && (prev->next->cpu < new->cpu)) {
> + Â Â Â Â Â Â Â prev = prev->next;
> + Â Â Â }
> +
> + Â Â Â /*
> + Â Â Â Â* insert after "prev"
> + Â Â Â Â*/
> + Â Â Â new->next = prev->next;
> + Â Â Â prev->next = new;
> +
> + Â Â Â return;
Same thing here about the return.

> +
> +int get_physical_package_id(int cpu)
> +{
> + Â Â Â char path[64];
> + Â Â Â FILE *filep;
> + Â Â Â int pkg;
> +
> + Â Â Â sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/physical_package_id", cpu);
> + Â Â Â filep = fopen(path, "r");
> + Â Â Â if (filep == NULL) {
> + Â Â Â Â Â Â Â perror(path);
> + Â Â Â Â Â Â Â exit(1);
> + Â Â Â }
> + Â Â Â fscanf(filep, "%d", &pkg);
> + Â Â Â fclose(filep);
> + Â Â Â return pkg;
> +}
> +
> +int get_core_id(int cpu)
> +{
> + Â Â Â char path[64];
> + Â Â Â FILE *filep;
> + Â Â Â int core;
> +
> + Â Â Â sprintf(path, "/sys/devices/system/cpu/cpu%d/topology/core_id", cpu);
> + Â Â Â filep = fopen(path, "r");
> + Â Â Â if (filep == NULL) {
> + Â Â Â Â Â Â Â perror(path);
> + Â Â Â Â Â Â Â exit(1);
> + Â Â Â }
> + Â Â Â fscanf(filep, "%d", &core);
> + Â Â Â fclose(filep);
> + Â Â Â return core;
> +}
> +
> +/*
> + * run func(index, cpu) on every cpu in /proc/stat
> + */
> +
> +int for_all_cpus(void (func)(int, int, int))
> +{
> + Â Â Â FILE *fp;
> + Â Â Â int cpu_count;
> + Â Â Â int retval;
> +
> + Â Â Â fp = fopen(proc_stat, "r");
> + Â Â Â if (fp == NULL) {
> + Â Â Â Â Â Â Â perror(proc_stat);
> + Â Â Â Â Â Â Â exit(1);
> + Â Â Â }
> +
> + Â Â Â retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n");
> + Â Â Â if (retval != 0) {
> + Â Â Â Â Â Â Â perror("/proc/stat format");
> + Â Â Â Â Â Â Â exit(1);
> + Â Â Â }
> +
> + Â Â Â for (cpu_count = 0; ; cpu_count++) {
> + Â Â Â Â Â Â Â int cpu;
> +
> + Â Â Â Â Â Â Â retval = fscanf(fp, "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", &cpu);
> + Â Â Â Â Â Â Â if (retval != 1)
> + Â Â Â Â Â Â Â Â Â Â Â break;
> +
> + Â Â Â Â Â Â Â func(get_physical_package_id(cpu), get_core_id(cpu), cpu);
> + Â Â Â }
> + Â Â Â fclose(fp);
> + Â Â Â return cpu_count;
> +}
> +
> +void re_initialize(void)
> +{
> + Â Â Â printf("turbostat: topology changed, re-initializing.\n");
> + Â Â Â free_all_counters();
> + Â Â Â num_cpus = for_all_cpus(alloc_new_cpu_counters);
> + Â Â Â need_reinitialize = 0;
> + Â Â Â printf("num_cpus is now %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()
Use void on parameter?

> +{
> + Â Â Â int new_num_cpus;
> +
> + Â Â Â new_num_cpus = for_all_cpus(dummy);
> +
> + Â Â Â if (new_num_cpus != num_cpus) {
> + Â Â Â Â Â Â Â if (verbose)
> + Â Â Â Â Â Â Â Â Â Â Â printf("num_cpus was %d, is now Â%d\n",
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â num_cpus, new_num_cpus);
> + Â Â Â Â Â Â Â need_reinitialize = 1;
> + Â Â Â }
> +
> + Â Â Â return;
> +}
No need of this return, right?
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/