Re: [PATCH v2] perf metrics: Add literal for system TSC frequency

Peter Zijlstra
2022-05-27T09:50:31Z

On Thu, May 26, 2022 at 09:04:07PM -0700, 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 accurate at least on Intel
> processors.
>
> 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.
>
> [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

This all seems bonghits inspired... and perf actually does expose the
tsc frequency. What do you think is in perf_event_mmap_page::time_* ?

>
> Signed-off-by: Ian Rogers <irogers@google.com>
> ---
> tools/perf/tests/expr.c | 15 +++++++++++++
> tools/perf/util/expr.c | 50 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 65 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..f33aeb1e6faa 100644
> --- a/tools/perf/util/expr.c
> +++ b/tools/perf/util/expr.c
> @@ -402,6 +402,51 @@ 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 +462,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.36.1.124.g0e6072fb45-goog
>