[PATCH v11 17/19] perf env: Add helper to lazily compute the os_release

From: Ian Rogers

Date: Tue Jun 02 2026 - 02:32:58 EST


In live mode the os_release isn't being initialized, make a lazy
initialization helper that assumes when the os_release isn't
initialized this is live mode.

Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/util/data-convert-bt.c | 2 +-
tools/perf/util/data-convert-json.c | 6 +++--
tools/perf/util/env.c | 38 +++++++++++++++++++++++++++++
tools/perf/util/env.h | 1 +
tools/perf/util/header.c | 16 ++++++++----
tools/perf/util/symbol.c | 4 +--
6 files changed, 57 insertions(+), 10 deletions(-)

diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index b3f745cff2a7..cc51b8677c8e 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -1414,7 +1414,7 @@ do { \

ADD("host", env->hostname);
ADD("sysname", "Linux");
- ADD("release", env->os_release);
+ ADD("release", perf_env__os_release(env));
ADD("version", env->version);
ADD("machine", env->arch);
ADD("domain", "kernel");
diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c
index a7da93a7ff0e..c71dfb77c697 100644
--- a/tools/perf/util/data-convert-json.c
+++ b/tools/perf/util/data-convert-json.c
@@ -16,6 +16,7 @@
#include "linux/err.h"
#include "util/auxtrace.h"
#include "util/debug.h"
+#include "util/env.h"
#include "util/dso.h"
#include "util/event.h"
#include "util/evsel.h"
@@ -272,7 +273,7 @@ static void output_headers(struct perf_session *session, struct convert_json *c)
{
struct stat st;
const struct perf_header *header = &session->header;
- const struct perf_env *env = perf_session__env(session);
+ struct perf_env *env = perf_session__env(session);
int ret;
int fd = perf_data__fd(session->data);
int i;
@@ -296,7 +297,8 @@ static void output_headers(struct perf_session *session, struct convert_json *c)
output_json_key_format(out, true, 2, "feat-offset", "%" PRIu64, header->feat_offset);

output_json_key_string(out, true, 2, "hostname", env->hostname);
- output_json_key_string(out, true, 2, "os-release", env->os_release);
+ output_json_key_string(out, true, 2, "os-release",
+ perf_env__os_release(env));
output_json_key_string(out, true, 2, "arch", env->arch);

if (env->cpu_desc)
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 4de07cc7ef5d..61b30f08493a 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -361,6 +361,44 @@ bool perf_arch_is_big_endian(const char *arch)
return false;
}

+const char *perf_env__os_release(struct perf_env *env)
+{
+ struct utsname uts;
+ int ret;
+ const char *release;
+
+ if (!env)
+ return perf_version_string;
+
+ mutex_lock(&env->lock);
+ if (env->os_release) {
+ release = env->os_release;
+ goto out;
+ }
+
+ /*
+ * If env->arch is set, this is an offline target environment.
+ * If the os_release is not populated in the file, we do not want
+ * to poison it with the host's release which would break guest checks.
+ */
+ if (env->arch) {
+ release = NULL;
+ goto out;
+ }
+
+ /*
+ * The os_release is being accessed but wasn't initialized from a data
+ * file, assume this is 'live' mode and use the release from uname. If
+ * uname or strdup fails then use the current perf tool version.
+ */
+ ret = uname(&uts);
+ env->os_release = strdup(ret < 0 ? perf_version_string : uts.release);
+ release = env->os_release ?: perf_version_string;
+out:
+ mutex_unlock(&env->lock);
+ return release;
+}
+
int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
{
int i;
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 6aaf80c640bd..7621d1f73b83 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -176,6 +176,7 @@ void perf_env__exit(struct perf_env *env);

int perf_env__kernel_is_64_bit(struct perf_env *env);
bool perf_arch_is_big_endian(const char *arch);
+const char *perf_env__os_release(struct perf_env *env);

int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]);

diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index ecdac427d9c4..d7f41db7322c 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -432,13 +432,19 @@ static int write_osrelease(struct feat_fd *ff,
struct evlist *evlist __maybe_unused)
{
struct utsname uts;
- int ret;
+ const char *release = NULL;

- ret = uname(&uts);
- if (ret < 0)
- return -1;
+ if (evlist->session)
+ release = perf_env__os_release(perf_session__env(evlist->session));

- return do_write_string(ff, uts.release);
+ if (!release) {
+ int ret = uname(&uts);
+
+ if (ret < 0)
+ return -1;
+ release = uts.release;
+ }
+ return do_write_string(ff, release);
}

static int write_arch(struct feat_fd *ff, struct evlist *evlist)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 2ce512f08a1d..077d19af5240 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2225,7 +2225,7 @@ static int vmlinux_path__init(struct perf_env *env)
{
struct utsname uts;
char bf[PATH_MAX];
- char *kernel_version;
+ const char *kernel_version;
unsigned int i;

vmlinux_path = malloc(sizeof(char *) * (ARRAY_SIZE(vmlinux_paths) +
@@ -2242,7 +2242,7 @@ static int vmlinux_path__init(struct perf_env *env)
return 0;

if (env) {
- kernel_version = env->os_release;
+ kernel_version = perf_env__os_release(env);
} else {
if (uname(&uts) < 0)
goto out_fail;
--
2.54.0.929.g9b7fa37559-goog