linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/2] Add arch TSC frequency information
@ 2022-07-15 22:35 Ian Rogers
  2022-07-15 22:35 ` [PATCH v3 1/2] perf metrics: Add literal for system TSC frequency Ian Rogers
  2022-07-15 22:35 ` [PATCH v3 2/2] perf tsc: Add arch TSC frequency information Ian Rogers
  0 siblings, 2 replies; 6+ messages in thread
From: Ian Rogers @ 2022-07-15 22:35 UTC (permalink / raw)
  To: perry.taylor, caleb.biggers, kshipra.bopardikar, Kan Liang,
	Zhengjun Xing, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Namhyung Kim, Maxime Coquelin, Alexandre Torgue,
	Andi Kleen, James Clark, John Garry, linux-kernel,
	linux-perf-users
  Cc: Stephane Eranian, Ian Rogers

The first patch adds support for deriving from /proc/cpuinfo and adds
tests. The second patch from Kan Liang derives it from CPUID available
on newer Intel processors as discussed here:
https://lore.kernel.org/lkml/20220527040407.4193232-1-irogers@google.com/#t

v2. Adds warnings to make clear if things have changed/broken on future
    Intel platforms. It also adds caching and an Intel specific that a
    value is computed.

Ian Rogers (1):
  perf metrics: Add literal for system TSC frequency

Kan Liang (1):
  perf tsc: Add arch TSC frequency information

 tools/perf/arch/x86/util/cpuid.h  | 34 +++++++++++++++++
 tools/perf/arch/x86/util/header.c | 27 ++++++--------
 tools/perf/arch/x86/util/tsc.c    | 33 ++++++++++++++++
 tools/perf/tests/expr.c           | 15 ++++++++
 tools/perf/util/expr.c            | 62 +++++++++++++++++++++++++++++++
 tools/perf/util/tsc.h             |  1 +
 6 files changed, 156 insertions(+), 16 deletions(-)
 create mode 100644 tools/perf/arch/x86/util/cpuid.h

-- 
2.37.0.170.g444d1eabd0-goog


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v3 1/2] perf metrics: Add literal for system TSC frequency
  2022-07-15 22:35 [PATCH v3 0/2] Add arch TSC frequency information Ian Rogers
@ 2022-07-15 22:35 ` Ian Rogers
  2022-07-18 12:14   ` Liang, Kan
  2022-07-15 22:35 ` [PATCH v3 2/2] perf tsc: Add arch TSC frequency information Ian Rogers
  1 sibling, 1 reply; 6+ messages in thread
From: Ian Rogers @ 2022-07-15 22:35 UTC (permalink / raw)
  To: perry.taylor, caleb.biggers, kshipra.bopardikar, Kan Liang,
	Zhengjun Xing, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Namhyung Kim, Maxime Coquelin, Alexandre Torgue,
	Andi Kleen, James Clark, John Garry, linux-kernel,
	linux-perf-users
  Cc: Stephane Eranian, Ian Rogers

Such a literal is useful to calculate things like the average frequency
[1]. The TSC frequency isn't exposed by sysfs although some experimental
drivers look to add it [2]. This change computes the value using the
frequency in /proc/cpuinfo which is accruate at least on Intel
processors.

[1] https://github.com/intel/perfmon-metrics/blob/5ad9ef7056f31075e8178b9f1fb732af183b2c8d/SKX/metrics/perf/skx_metric_perf.json#L11
[2] https://github.com/trailofbits/tsc_freq_khz

Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/tests/expr.c | 15 +++++++++++++
 tools/perf/util/expr.c  | 49 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 5c0032fe93ae..45afe4f24859 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -1,8 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "util/debug.h"
 #include "util/expr.h"
+#include "util/header.h"
 #include "util/smt.h"
 #include "tests.h"
+#include <math.h>
 #include <stdlib.h>
 #include <string.h>
 #include <linux/zalloc.h>
@@ -69,6 +71,11 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u
 	double val, num_cpus, num_cores, num_dies, num_packages;
 	int ret;
 	struct expr_parse_ctx *ctx;
+	bool is_intel = false;
+	char buf[128];
+
+	if (!get_cpuid(buf, sizeof(buf)))
+		is_intel = strstr(buf, "Intel") != NULL;
 
 	TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0);
 
@@ -175,6 +182,14 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u
 	if (num_dies) // Some platforms do not have CPU die support, for example s390
 		TEST_ASSERT_VAL("#num_dies >= #num_packages", num_dies >= num_packages);
 
+	if (is_intel) {
+		double system_tsc_freq;
+
+		TEST_ASSERT_VAL("#system_tsc_freq", expr__parse(&system_tsc_freq, ctx,
+								"#system_tsc_freq") == 0);
+		TEST_ASSERT_VAL("!isnan(#system_tsc_freq)", !isnan(system_tsc_freq));
+	}
+
 	/*
 	 * Source count returns the number of events aggregating in a leader
 	 * event including the leader. Check parsing yields an id.
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 675f318ce7c1..4c81533e4b43 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -402,6 +402,50 @@ double expr_id_data__source_count(const struct expr_id_data *data)
 	return data->val.source_count;
 }
 
+/*
+ * Derive the TSC frequency in Hz from the /proc/cpuinfo, for example:
+ * ...
+ * model name      : Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz
+ * ...
+ * will return 3000000000.
+ */
+static double system_tsc_freq(void)
+{
+	static double result;
+	static bool computed;
+	FILE *cpuinfo;
+	char *line = NULL;
+	size_t len = 0;
+
+	if (computed)
+		return result;
+
+	computed = true;
+	result = NAN;
+	cpuinfo = fopen("/proc/cpuinfo", "r");
+	if (!cpuinfo) {
+		pr_err("Failed to read /proc/cpuinfo for TSC frequency");
+		return NAN;
+	}
+	while (getline(&line, &len, cpuinfo) > 0) {
+		if (!strncmp(line, "model name", 10)) {
+			char *pos = strstr(line + 11, " @ ");
+
+			if (pos && sscanf(pos, " @ %lfGHz", &result) == 1) {
+				result *= 1000000000;
+				goto out;
+			}
+		}
+	}
+out:
+	if (isnan(result))
+		pr_err("Failed to find TSC frequency in /proc/cpuinfo");
+
+	free(line);
+	fclose(cpuinfo);
+	return result;
+}
+
 double expr__get_literal(const char *literal)
 {
 	static struct cpu_topology *topology;
@@ -417,6 +461,11 @@ double expr__get_literal(const char *literal)
 		goto out;
 	}
 
+	if (!strcasecmp("#system_tsc_freq", literal)) {
+		result = system_tsc_freq();
+		goto out;
+	}
+
 	/*
 	 * Assume that topology strings are consistent, such as CPUs "0-1"
 	 * wouldn't be listed as "0,1", and so after deduplication the number of
-- 
2.37.0.170.g444d1eabd0-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v3 2/2] perf tsc: Add arch TSC frequency information
  2022-07-15 22:35 [PATCH v3 0/2] Add arch TSC frequency information Ian Rogers
  2022-07-15 22:35 ` [PATCH v3 1/2] perf metrics: Add literal for system TSC frequency Ian Rogers
@ 2022-07-15 22:35 ` Ian Rogers
  2022-07-18 12:49   ` Liang, Kan
  1 sibling, 1 reply; 6+ messages in thread
From: Ian Rogers @ 2022-07-15 22:35 UTC (permalink / raw)
  To: perry.taylor, caleb.biggers, kshipra.bopardikar, Kan Liang,
	Zhengjun Xing, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Namhyung Kim, Maxime Coquelin, Alexandre Torgue,
	Andi Kleen, James Clark, John Garry, linux-kernel,
	linux-perf-users
  Cc: Stephane Eranian, Ian Rogers

From: Kan Liang <kan.liang@linux.intel.com>

The TSC frequency information is required for the event metrics with
the literal, system_tsc_freq. For the newer Intel platform, the TSC
frequency information can be retrieved from the CPUID leaf 0x15.
If the TSC frequency information isn't present the /proc/cpuinfo
approach is used.

Refactor cpuid for this use. Note, the previous stack pushing/popping
approach was broken on x86-64 that has stack red zones that would be
clobbered.

Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Ian Rogers <irogers@google.com>
---
 tools/perf/arch/x86/util/cpuid.h  | 34 +++++++++++++++++++++++++++++++
 tools/perf/arch/x86/util/header.c | 27 ++++++++++--------------
 tools/perf/arch/x86/util/tsc.c    | 33 ++++++++++++++++++++++++++++++
 tools/perf/util/expr.c            | 15 +++++++++++++-
 tools/perf/util/tsc.h             |  1 +
 5 files changed, 93 insertions(+), 17 deletions(-)
 create mode 100644 tools/perf/arch/x86/util/cpuid.h

diff --git a/tools/perf/arch/x86/util/cpuid.h b/tools/perf/arch/x86/util/cpuid.h
new file mode 100644
index 000000000000..0a3ae0ace7e9
--- /dev/null
+++ b/tools/perf/arch/x86/util/cpuid.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef PERF_CPUID_H
+#define PERF_CPUID_H 1
+
+
+static inline void
+cpuid(unsigned int op, unsigned int op2, unsigned int *a, unsigned int *b,
+	unsigned int *c, unsigned int *d)
+{
+	/*
+	 * Preserve %ebx/%rbx register by either placing it in %rdi or saving it
+	 * on the stack - x86-64 needs to avoid the stack red zone. In PIC
+	 * compilations %ebx contains the address of the global offset
+	 * table. %rbx is occasionally used to address stack variables in
+	 * presence of dynamic allocas.
+	 */
+	asm(
+#if defined(__x86_64__)
+		"mov %%rbx, %%rdi\n"
+		"cpuid\n"
+		"xchg %%rdi, %%rbx\n"
+#else
+		"pushl %%ebx\n"
+		"cpuid\n"
+		"movl %%ebx, %%edi\n"
+		"popl %%ebx\n"
+#endif
+		: "=a"(*a), "=D"(*b), "=c"(*c), "=d"(*d)
+		: "a"(op), "2"(op2));
+}
+
+void get_cpuid_0(char *vendor, unsigned int *lvl);
+
+#endif
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c
index 578c8c568ffd..a51444a77a5f 100644
--- a/tools/perf/arch/x86/util/header.c
+++ b/tools/perf/arch/x86/util/header.c
@@ -9,18 +9,17 @@
 
 #include "../../../util/debug.h"
 #include "../../../util/header.h"
+#include "cpuid.h"
 
-static inline void
-cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
-      unsigned int *d)
+void get_cpuid_0(char *vendor, unsigned int *lvl)
 {
-	__asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t"
-			      "movl %%ebx, %%esi\n\t.byte 0x5b"
-			: "=a" (*a),
-			"=S" (*b),
-			"=c" (*c),
-			"=d" (*d)
-			: "a" (op));
+	unsigned int b, c, d;
+
+	cpuid(0, 0, lvl, &b, &c, &d);
+	strncpy(&vendor[0], (char *)(&b), 4);
+	strncpy(&vendor[4], (char *)(&d), 4);
+	strncpy(&vendor[8], (char *)(&c), 4);
+	vendor[12] = '\0';
 }
 
 static int
@@ -31,14 +30,10 @@ __get_cpuid(char *buffer, size_t sz, const char *fmt)
 	int nb;
 	char vendor[16];
 
-	cpuid(0, &lvl, &b, &c, &d);
-	strncpy(&vendor[0], (char *)(&b), 4);
-	strncpy(&vendor[4], (char *)(&d), 4);
-	strncpy(&vendor[8], (char *)(&c), 4);
-	vendor[12] = '\0';
+	get_cpuid_0(vendor, &lvl);
 
 	if (lvl >= 1) {
-		cpuid(1, &a, &b, &c, &d);
+		cpuid(1, 0, &a, &b, &c, &d);
 
 		family = (a >> 8) & 0xf;  /* bits 11 - 8 */
 		model  = (a >> 4) & 0xf;  /* Bits  7 - 4 */
diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c
index 559365f8fe52..b69144f22489 100644
--- a/tools/perf/arch/x86/util/tsc.c
+++ b/tools/perf/arch/x86/util/tsc.c
@@ -1,7 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/types.h>
+#include <string.h>
 
 #include "../../../util/tsc.h"
+#include "cpuid.h"
 
 u64 rdtsc(void)
 {
@@ -11,3 +13,34 @@ u64 rdtsc(void)
 
 	return low | ((u64)high) << 32;
 }
+
+double arch_get_tsc_freq(void)
+{
+	unsigned int a, b, c, d, lvl;
+	static bool cached;
+	static double tsc;
+	char vendor[16];
+
+	if (cached)
+		return tsc;
+
+	cached = true;
+	get_cpuid_0(vendor, &lvl);
+	if (!strstr(vendor, "Intel"))
+		return 0;
+
+	/*
+	 * Don't support Time Stamp Counter and
+	 * Nominal Core Crystal Clock Information Leaf.
+	 */
+	if (lvl < 0x15)
+		return 0;
+
+	cpuid(0x15, 0, &a, &b, &c, &d);
+	/* TSC frequency is not enumerated */
+	if (!a || !b || !c)
+		return 0;
+
+	tsc = (double)c * (double)b / (double)a;
+	return tsc;
+}
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 4c81533e4b43..16f10e6d5ca5 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -12,6 +12,7 @@
 #include "expr-bison.h"
 #include "expr-flex.h"
 #include "smt.h"
+#include "tsc.h"
 #include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/zalloc.h>
@@ -443,9 +444,19 @@ static double system_tsc_freq(void)
 
 	free(line);
 	fclose(cpuinfo);
+	if (isnan(result))
+		pr_err("Error reading system_tsc_freq");
+
 	return result;
 }
 
+#if !defined(__i386__) && !defined(__x86_64__)
+double arch_get_tsc_freq(void)
+{
+	return 0.0;
+}
+#endif
+
 double expr__get_literal(const char *literal)
 {
 	static struct cpu_topology *topology;
@@ -462,7 +473,9 @@ double expr__get_literal(const char *literal)
 	}
 
 	if (!strcasecmp("#system_tsc_freq", literal)) {
-		result = system_tsc_freq();
+		result = arch_get_tsc_freq();
+		if (fpclassify(result) == FP_ZERO)
+			result = system_tsc_freq();
 		goto out;
 	}
 
diff --git a/tools/perf/util/tsc.h b/tools/perf/util/tsc.h
index 7d83a31732a7..88fd1c4c1cb8 100644
--- a/tools/perf/util/tsc.h
+++ b/tools/perf/util/tsc.h
@@ -25,6 +25,7 @@ int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
 u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc);
 u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);
 u64 rdtsc(void);
+double arch_get_tsc_freq(void);
 
 size_t perf_event__fprintf_time_conv(union perf_event *event, FILE *fp);
 
-- 
2.37.0.170.g444d1eabd0-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 1/2] perf metrics: Add literal for system TSC frequency
  2022-07-15 22:35 ` [PATCH v3 1/2] perf metrics: Add literal for system TSC frequency Ian Rogers
@ 2022-07-18 12:14   ` Liang, Kan
  0 siblings, 0 replies; 6+ messages in thread
From: Liang, Kan @ 2022-07-18 12:14 UTC (permalink / raw)
  To: Ian Rogers, perry.taylor, caleb.biggers, kshipra.bopardikar,
	Zhengjun Xing, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Namhyung Kim, Maxime Coquelin, Alexandre Torgue,
	Andi Kleen, James Clark, John Garry, linux-kernel,
	linux-perf-users
  Cc: Stephane Eranian


Thanks Ian for working on the issue and coordinate the patches.

On 2022-07-15 6:35 p.m., Ian Rogers wrote:
> Such a literal is useful to calculate things like the average frequency
> [1]. The TSC frequency isn't exposed by sysfs although some experimental
> drivers look to add it [2]. This change computes the value using the
> frequency in /proc/cpuinfo which is accruate 

%s/accruate/accurate/

> at least on Intel processors.

I googled the cpuinfo of other Archs, e.g., arm and s390. It looks like
only Intel display the TSC frequency in the "model name".

So it may be better to move it to X86 specific code, arch/x86/util/tsc.c

> 
> [1] https://github.com/intel/perfmon-metrics/blob/5ad9ef7056f31075e8178b9f1fb732af183b2c8d/SKX/metrics/perf/skx_metric_perf.json#L11
> [2] https://github.com/trailofbits/tsc_freq_khz
> 
> Signed-off-by: Ian Rogers <irogers@google.com>
> ---
>  tools/perf/tests/expr.c | 15 +++++++++++++
>  tools/perf/util/expr.c  | 49 +++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 64 insertions(+)
> 
> diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
> index 5c0032fe93ae..45afe4f24859 100644
> --- a/tools/perf/tests/expr.c
> +++ b/tools/perf/tests/expr.c
> @@ -1,8 +1,10 @@
>  // SPDX-License-Identifier: GPL-2.0
>  #include "util/debug.h"
>  #include "util/expr.h"
> +#include "util/header.h"
>  #include "util/smt.h"
>  #include "tests.h"
> +#include <math.h>
>  #include <stdlib.h>
>  #include <string.h>
>  #include <linux/zalloc.h>
> @@ -69,6 +71,11 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u
>  	double val, num_cpus, num_cores, num_dies, num_packages;
>  	int ret;
>  	struct expr_parse_ctx *ctx;
> +	bool is_intel = false;
> +	char buf[128];
> +
> +	if (!get_cpuid(buf, sizeof(buf)))
> +		is_intel = strstr(buf, "Intel") != NULL;
>  
>  	TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0);
>  
> @@ -175,6 +182,14 @@ static int test__expr(struct test_suite *t __maybe_unused, int subtest __maybe_u
>  	if (num_dies) // Some platforms do not have CPU die support, for example s390
>  		TEST_ASSERT_VAL("#num_dies >= #num_packages", num_dies >= num_packages);
>  
> +	if (is_intel) {
> +		double system_tsc_freq;
> +
> +		TEST_ASSERT_VAL("#system_tsc_freq", expr__parse(&system_tsc_freq, ctx,
> +								"#system_tsc_freq") == 0);


I think we should use arch_get_tsc_freq() to replace here.
This belong to a separate patch.


Thanks,
Kan
> +		TEST_ASSERT_VAL("!isnan(#system_tsc_freq)", !isnan(system_tsc_freq));
> +	}
> +
>  	/*
>  	 * Source count returns the number of events aggregating in a leader
>  	 * event including the leader. Check parsing yields an id.
> diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
> index 675f318ce7c1..4c81533e4b43 100644
> --- a/tools/perf/util/expr.c
> +++ b/tools/perf/util/expr.c
> @@ -402,6 +402,50 @@ double expr_id_data__source_count(const struct expr_id_data *data)
>  	return data->val.source_count;
>  }
>  
> +/*
> + * Derive the TSC frequency in Hz from the /proc/cpuinfo, for example:
> + * ...
> + * model name      : Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz
> + * ...
> + * will return 3000000000.
> + */
> +static double system_tsc_freq(void)
> +{
> +	static double result;
> +	static bool computed;
> +	FILE *cpuinfo;
> +	char *line = NULL;
> +	size_t len = 0;
> +
> +	if (computed)
> +		return result;
> +
> +	computed = true;
> +	result = NAN;
> +	cpuinfo = fopen("/proc/cpuinfo", "r");
> +	if (!cpuinfo) {
> +		pr_err("Failed to read /proc/cpuinfo for TSC frequency");
> +		return NAN;
> +	}
> +	while (getline(&line, &len, cpuinfo) > 0) {
> +		if (!strncmp(line, "model name", 10)) {
> +			char *pos = strstr(line + 11, " @ ");
> +
> +			if (pos && sscanf(pos, " @ %lfGHz", &result) == 1) {
> +				result *= 1000000000;
> +				goto out;
> +			}
> +		}
> +	}
> +out:
> +	if (isnan(result))
> +		pr_err("Failed to find TSC frequency in /proc/cpuinfo");
> +
> +	free(line);
> +	fclose(cpuinfo);
> +	return result;
> +}
> +
>  double expr__get_literal(const char *literal)
>  {
>  	static struct cpu_topology *topology;
> @@ -417,6 +461,11 @@ double expr__get_literal(const char *literal)
>  		goto out;
>  	}
>  
> +	if (!strcasecmp("#system_tsc_freq", literal)) {
> +		result = system_tsc_freq();
> +		goto out;
> +	}
> +
>  	/*
>  	 * Assume that topology strings are consistent, such as CPUs "0-1"
>  	 * wouldn't be listed as "0,1", and so after deduplication the number of

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 2/2] perf tsc: Add arch TSC frequency information
  2022-07-15 22:35 ` [PATCH v3 2/2] perf tsc: Add arch TSC frequency information Ian Rogers
@ 2022-07-18 12:49   ` Liang, Kan
  2022-07-18 14:47     ` Ian Rogers
  0 siblings, 1 reply; 6+ messages in thread
From: Liang, Kan @ 2022-07-18 12:49 UTC (permalink / raw)
  To: Ian Rogers, perry.taylor, caleb.biggers, kshipra.bopardikar,
	Zhengjun Xing, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Mark Rutland, Alexander Shishkin,
	Jiri Olsa, Namhyung Kim, Maxime Coquelin, Alexandre Torgue,
	Andi Kleen, James Clark, John Garry, linux-kernel,
	linux-perf-users
  Cc: Stephane Eranian



On 2022-07-15 6:35 p.m., Ian Rogers wrote:
> From: Kan Liang <kan.liang@linux.intel.com>
> 
> The TSC frequency information is required for the event metrics with
> the literal, system_tsc_freq. For the newer Intel platform, the TSC
> frequency information can be retrieved from the CPUID leaf 0x15.
> If the TSC frequency information isn't present the /proc/cpuinfo
> approach is used.
> 
> Refactor cpuid for this use. Note, the previous stack pushing/popping
> approach was broken on x86-64 that has stack red zones that would be
> clobbered.
> 
> Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
> Signed-off-by: Ian Rogers <irogers@google.com>
> ---
>  tools/perf/arch/x86/util/cpuid.h  | 34 +++++++++++++++++++++++++++++++
>  tools/perf/arch/x86/util/header.c | 27 ++++++++++--------------
>  tools/perf/arch/x86/util/tsc.c    | 33 ++++++++++++++++++++++++++++++
>  tools/perf/util/expr.c            | 15 +++++++++++++-
>  tools/perf/util/tsc.h             |  1 +
>  5 files changed, 93 insertions(+), 17 deletions(-)
>  create mode 100644 tools/perf/arch/x86/util/cpuid.h
> 
> diff --git a/tools/perf/arch/x86/util/cpuid.h b/tools/perf/arch/x86/util/cpuid.h
> new file mode 100644
> index 000000000000..0a3ae0ace7e9
> --- /dev/null
> +++ b/tools/perf/arch/x86/util/cpuid.h
> @@ -0,0 +1,34 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef PERF_CPUID_H
> +#define PERF_CPUID_H 1
> +
> +
> +static inline void
> +cpuid(unsigned int op, unsigned int op2, unsigned int *a, unsigned int *b,
> +	unsigned int *c, unsigned int *d)
> +{
> +	/*
> +	 * Preserve %ebx/%rbx register by either placing it in %rdi or saving it
> +	 * on the stack - x86-64 needs to avoid the stack red zone. In PIC
> +	 * compilations %ebx contains the address of the global offset
> +	 * table. %rbx is occasionally used to address stack variables in
> +	 * presence of dynamic allocas.
> +	 */
> +	asm(
> +#if defined(__x86_64__)
> +		"mov %%rbx, %%rdi\n"
> +		"cpuid\n"
> +		"xchg %%rdi, %%rbx\n"
> +#else
> +		"pushl %%ebx\n"
> +		"cpuid\n"
> +		"movl %%ebx, %%edi\n"
> +		"popl %%ebx\n"
> +#endif
> +		: "=a"(*a), "=D"(*b), "=c"(*c), "=d"(*d)
> +		: "a"(op), "2"(op2));
> +}
> +
> +void get_cpuid_0(char *vendor, unsigned int *lvl);
> +
> +#endif
> diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c
> index 578c8c568ffd..a51444a77a5f 100644
> --- a/tools/perf/arch/x86/util/header.c
> +++ b/tools/perf/arch/x86/util/header.c
> @@ -9,18 +9,17 @@
>  
>  #include "../../../util/debug.h"
>  #include "../../../util/header.h"
> +#include "cpuid.h"
>  
> -static inline void
> -cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
> -      unsigned int *d)
> +void get_cpuid_0(char *vendor, unsigned int *lvl)
>  {
> -	__asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t"
> -			      "movl %%ebx, %%esi\n\t.byte 0x5b"
> -			: "=a" (*a),
> -			"=S" (*b),
> -			"=c" (*c),
> -			"=d" (*d)
> -			: "a" (op));
> +	unsigned int b, c, d;
> +
> +	cpuid(0, 0, lvl, &b, &c, &d);
> +	strncpy(&vendor[0], (char *)(&b), 4);
> +	strncpy(&vendor[4], (char *)(&d), 4);
> +	strncpy(&vendor[8], (char *)(&c), 4);
> +	vendor[12] = '\0';
>  }
>  
>  static int
> @@ -31,14 +30,10 @@ __get_cpuid(char *buffer, size_t sz, const char *fmt)
>  	int nb;
>  	char vendor[16];
>  
> -	cpuid(0, &lvl, &b, &c, &d);
> -	strncpy(&vendor[0], (char *)(&b), 4);
> -	strncpy(&vendor[4], (char *)(&d), 4);
> -	strncpy(&vendor[8], (char *)(&c), 4);
> -	vendor[12] = '\0';
> +	get_cpuid_0(vendor, &lvl);
>  
>  	if (lvl >= 1) {
> -		cpuid(1, &a, &b, &c, &d);
> +		cpuid(1, 0, &a, &b, &c, &d);
>  
>  		family = (a >> 8) & 0xf;  /* bits 11 - 8 */
>  		model  = (a >> 4) & 0xf;  /* Bits  7 - 4 */
> diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c
> index 559365f8fe52..b69144f22489 100644
> --- a/tools/perf/arch/x86/util/tsc.c
> +++ b/tools/perf/arch/x86/util/tsc.c
> @@ -1,7 +1,9 @@
>  // SPDX-License-Identifier: GPL-2.0
>  #include <linux/types.h>
> +#include <string.h>
>  
>  #include "../../../util/tsc.h"
> +#include "cpuid.h"
>  
>  u64 rdtsc(void)
>  {
> @@ -11,3 +13,34 @@ u64 rdtsc(void)
>  
>  	return low | ((u64)high) << 32;
>  }
> +
> +double arch_get_tsc_freq(void)
> +{
> +	unsigned int a, b, c, d, lvl;
> +	static bool cached;
> +	static double tsc;
> +	char vendor[16];
> +
> +	if (cached)
> +		return tsc;
> +
> +	cached = true;
> +	get_cpuid_0(vendor, &lvl);
> +	if (!strstr(vendor, "Intel"))
> +		return 0;
> +
> +	/*
> +	 * Don't support Time Stamp Counter and
> +	 * Nominal Core Crystal Clock Information Leaf.
> +	 */
> +	if (lvl < 0x15)
> +		return 0;
> +
> +	cpuid(0x15, 0, &a, &b, &c, &d);
> +	/* TSC frequency is not enumerated */
> +	if (!a || !b || !c)
> +		return 0;
> +
> +	tsc = (double)c * (double)b / (double)a;
> +	return tsc;
> +}
> diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
> index 4c81533e4b43..16f10e6d5ca5 100644
> --- a/tools/perf/util/expr.c
> +++ b/tools/perf/util/expr.c
> @@ -12,6 +12,7 @@
>  #include "expr-bison.h"
>  #include "expr-flex.h"
>  #include "smt.h"
> +#include "tsc.h"
>  #include <linux/err.h>
>  #include <linux/kernel.h>
>  #include <linux/zalloc.h>
> @@ -443,9 +444,19 @@ static double system_tsc_freq(void)
>  
>  	free(line);
>  	fclose(cpuinfo);
> +	if (isnan(result))
> +		pr_err("Error reading system_tsc_freq");
> +
>  	return result;
>  }
>  
> +#if !defined(__i386__) && !defined(__x86_64__)
> +double arch_get_tsc_freq(void)

Other arch_* functions are __weak functions. I think it's better to keep
it consistent. It also avoid to add a new #if defined when adding a new
arch. Is there a problem to use __weak here?

Thanks,
Kan

> +{
> +	return 0.0;
> +}
> +#endif
> +
>  double expr__get_literal(const char *literal)
>  {
>  	static struct cpu_topology *topology;
> @@ -462,7 +473,9 @@ double expr__get_literal(const char *literal)
>  	}
>  
>  	if (!strcasecmp("#system_tsc_freq", literal)) {
> -		result = system_tsc_freq();
> +		result = arch_get_tsc_freq();
> +		if (fpclassify(result) == FP_ZERO)
> +			result = system_tsc_freq();
>  		goto out;
>  	}
>  
> diff --git a/tools/perf/util/tsc.h b/tools/perf/util/tsc.h
> index 7d83a31732a7..88fd1c4c1cb8 100644
> --- a/tools/perf/util/tsc.h
> +++ b/tools/perf/util/tsc.h
> @@ -25,6 +25,7 @@ int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
>  u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc);
>  u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);
>  u64 rdtsc(void);
> +double arch_get_tsc_freq(void);
>  
>  size_t perf_event__fprintf_time_conv(union perf_event *event, FILE *fp);
>  

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v3 2/2] perf tsc: Add arch TSC frequency information
  2022-07-18 12:49   ` Liang, Kan
@ 2022-07-18 14:47     ` Ian Rogers
  0 siblings, 0 replies; 6+ messages in thread
From: Ian Rogers @ 2022-07-18 14:47 UTC (permalink / raw)
  To: Liang, Kan
  Cc: perry.taylor, caleb.biggers, kshipra.bopardikar, Zhengjun Xing,
	Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
	Maxime Coquelin, Alexandre Torgue, Andi Kleen, James Clark,
	John Garry, linux-kernel, linux-perf-users, Stephane Eranian

On Mon, Jul 18, 2022 at 5:49 AM Liang, Kan <kan.liang@linux.intel.com> wrote:
>
> On 2022-07-15 6:35 p.m., Ian Rogers wrote:
> > From: Kan Liang <kan.liang@linux.intel.com>
> >
> > The TSC frequency information is required for the event metrics with
> > the literal, system_tsc_freq. For the newer Intel platform, the TSC
> > frequency information can be retrieved from the CPUID leaf 0x15.
> > If the TSC frequency information isn't present the /proc/cpuinfo
> > approach is used.
> >
> > Refactor cpuid for this use. Note, the previous stack pushing/popping
> > approach was broken on x86-64 that has stack red zones that would be
> > clobbered.
> >
> > Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
> > Signed-off-by: Ian Rogers <irogers@google.com>
> > ---
> >  tools/perf/arch/x86/util/cpuid.h  | 34 +++++++++++++++++++++++++++++++
> >  tools/perf/arch/x86/util/header.c | 27 ++++++++++--------------
> >  tools/perf/arch/x86/util/tsc.c    | 33 ++++++++++++++++++++++++++++++
> >  tools/perf/util/expr.c            | 15 +++++++++++++-
> >  tools/perf/util/tsc.h             |  1 +
> >  5 files changed, 93 insertions(+), 17 deletions(-)
> >  create mode 100644 tools/perf/arch/x86/util/cpuid.h
> >
> > diff --git a/tools/perf/arch/x86/util/cpuid.h b/tools/perf/arch/x86/util/cpuid.h
> > new file mode 100644
> > index 000000000000..0a3ae0ace7e9
> > --- /dev/null
> > +++ b/tools/perf/arch/x86/util/cpuid.h
> > @@ -0,0 +1,34 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef PERF_CPUID_H
> > +#define PERF_CPUID_H 1
> > +
> > +
> > +static inline void
> > +cpuid(unsigned int op, unsigned int op2, unsigned int *a, unsigned int *b,
> > +     unsigned int *c, unsigned int *d)
> > +{
> > +     /*
> > +      * Preserve %ebx/%rbx register by either placing it in %rdi or saving it
> > +      * on the stack - x86-64 needs to avoid the stack red zone. In PIC
> > +      * compilations %ebx contains the address of the global offset
> > +      * table. %rbx is occasionally used to address stack variables in
> > +      * presence of dynamic allocas.
> > +      */
> > +     asm(
> > +#if defined(__x86_64__)
> > +             "mov %%rbx, %%rdi\n"
> > +             "cpuid\n"
> > +             "xchg %%rdi, %%rbx\n"
> > +#else
> > +             "pushl %%ebx\n"
> > +             "cpuid\n"
> > +             "movl %%ebx, %%edi\n"
> > +             "popl %%ebx\n"
> > +#endif
> > +             : "=a"(*a), "=D"(*b), "=c"(*c), "=d"(*d)
> > +             : "a"(op), "2"(op2));
> > +}
> > +
> > +void get_cpuid_0(char *vendor, unsigned int *lvl);
> > +
> > +#endif
> > diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c
> > index 578c8c568ffd..a51444a77a5f 100644
> > --- a/tools/perf/arch/x86/util/header.c
> > +++ b/tools/perf/arch/x86/util/header.c
> > @@ -9,18 +9,17 @@
> >
> >  #include "../../../util/debug.h"
> >  #include "../../../util/header.h"
> > +#include "cpuid.h"
> >
> > -static inline void
> > -cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c,
> > -      unsigned int *d)
> > +void get_cpuid_0(char *vendor, unsigned int *lvl)
> >  {
> > -     __asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t"
> > -                           "movl %%ebx, %%esi\n\t.byte 0x5b"
> > -                     : "=a" (*a),
> > -                     "=S" (*b),
> > -                     "=c" (*c),
> > -                     "=d" (*d)
> > -                     : "a" (op));
> > +     unsigned int b, c, d;
> > +
> > +     cpuid(0, 0, lvl, &b, &c, &d);
> > +     strncpy(&vendor[0], (char *)(&b), 4);
> > +     strncpy(&vendor[4], (char *)(&d), 4);
> > +     strncpy(&vendor[8], (char *)(&c), 4);
> > +     vendor[12] = '\0';
> >  }
> >
> >  static int
> > @@ -31,14 +30,10 @@ __get_cpuid(char *buffer, size_t sz, const char *fmt)
> >       int nb;
> >       char vendor[16];
> >
> > -     cpuid(0, &lvl, &b, &c, &d);
> > -     strncpy(&vendor[0], (char *)(&b), 4);
> > -     strncpy(&vendor[4], (char *)(&d), 4);
> > -     strncpy(&vendor[8], (char *)(&c), 4);
> > -     vendor[12] = '\0';
> > +     get_cpuid_0(vendor, &lvl);
> >
> >       if (lvl >= 1) {
> > -             cpuid(1, &a, &b, &c, &d);
> > +             cpuid(1, 0, &a, &b, &c, &d);
> >
> >               family = (a >> 8) & 0xf;  /* bits 11 - 8 */
> >               model  = (a >> 4) & 0xf;  /* Bits  7 - 4 */
> > diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c
> > index 559365f8fe52..b69144f22489 100644
> > --- a/tools/perf/arch/x86/util/tsc.c
> > +++ b/tools/perf/arch/x86/util/tsc.c
> > @@ -1,7 +1,9 @@
> >  // SPDX-License-Identifier: GPL-2.0
> >  #include <linux/types.h>
> > +#include <string.h>
> >
> >  #include "../../../util/tsc.h"
> > +#include "cpuid.h"
> >
> >  u64 rdtsc(void)
> >  {
> > @@ -11,3 +13,34 @@ u64 rdtsc(void)
> >
> >       return low | ((u64)high) << 32;
> >  }
> > +
> > +double arch_get_tsc_freq(void)
> > +{
> > +     unsigned int a, b, c, d, lvl;
> > +     static bool cached;
> > +     static double tsc;
> > +     char vendor[16];
> > +
> > +     if (cached)
> > +             return tsc;
> > +
> > +     cached = true;
> > +     get_cpuid_0(vendor, &lvl);
> > +     if (!strstr(vendor, "Intel"))
> > +             return 0;
> > +
> > +     /*
> > +      * Don't support Time Stamp Counter and
> > +      * Nominal Core Crystal Clock Information Leaf.
> > +      */
> > +     if (lvl < 0x15)
> > +             return 0;
> > +
> > +     cpuid(0x15, 0, &a, &b, &c, &d);
> > +     /* TSC frequency is not enumerated */
> > +     if (!a || !b || !c)
> > +             return 0;
> > +
> > +     tsc = (double)c * (double)b / (double)a;
> > +     return tsc;
> > +}
> > diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
> > index 4c81533e4b43..16f10e6d5ca5 100644
> > --- a/tools/perf/util/expr.c
> > +++ b/tools/perf/util/expr.c
> > @@ -12,6 +12,7 @@
> >  #include "expr-bison.h"
> >  #include "expr-flex.h"
> >  #include "smt.h"
> > +#include "tsc.h"
> >  #include <linux/err.h>
> >  #include <linux/kernel.h>
> >  #include <linux/zalloc.h>
> > @@ -443,9 +444,19 @@ static double system_tsc_freq(void)
> >
> >       free(line);
> >       fclose(cpuinfo);
> > +     if (isnan(result))
> > +             pr_err("Error reading system_tsc_freq");
> > +
> >       return result;
> >  }
> >
> > +#if !defined(__i386__) && !defined(__x86_64__)
> > +double arch_get_tsc_freq(void)
>
> Other arch_* functions are __weak functions. I think it's better to keep
> it consistent. It also avoid to add a new #if defined when adding a new
> arch. Is there a problem to use __weak here?

There are problems with weak in general and link time optimizations -
weak is implemented as a C compiler extension. There have been
patches/threads in the past about avoiding it. In this case it'd be
easy to get two arch_get_tsc_freq defined or none. Without weak these
are both linker errors. With weak your mileage varies.

Thanks,
Ian

> Thanks,
> Kan
>
> > +{
> > +     return 0.0;
> > +}
> > +#endif
> > +
> >  double expr__get_literal(const char *literal)
> >  {
> >       static struct cpu_topology *topology;
> > @@ -462,7 +473,9 @@ double expr__get_literal(const char *literal)
> >       }
> >
> >       if (!strcasecmp("#system_tsc_freq", literal)) {
> > -             result = system_tsc_freq();
> > +             result = arch_get_tsc_freq();
> > +             if (fpclassify(result) == FP_ZERO)
> > +                     result = system_tsc_freq();
> >               goto out;
> >       }
> >
> > diff --git a/tools/perf/util/tsc.h b/tools/perf/util/tsc.h
> > index 7d83a31732a7..88fd1c4c1cb8 100644
> > --- a/tools/perf/util/tsc.h
> > +++ b/tools/perf/util/tsc.h
> > @@ -25,6 +25,7 @@ int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
> >  u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc);
> >  u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);
> >  u64 rdtsc(void);
> > +double arch_get_tsc_freq(void);
> >
> >  size_t perf_event__fprintf_time_conv(union perf_event *event, FILE *fp);
> >

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-07-18 14:47 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-15 22:35 [PATCH v3 0/2] Add arch TSC frequency information Ian Rogers
2022-07-15 22:35 ` [PATCH v3 1/2] perf metrics: Add literal for system TSC frequency Ian Rogers
2022-07-18 12:14   ` Liang, Kan
2022-07-15 22:35 ` [PATCH v3 2/2] perf tsc: Add arch TSC frequency information Ian Rogers
2022-07-18 12:49   ` Liang, Kan
2022-07-18 14:47     ` Ian Rogers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).