Received: by 2002:a05:6a10:a852:0:0:0:0 with SMTP id d18csp92591pxy; Fri, 30 Apr 2021 00:48:19 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxA+kvyHk2IZDykjC76uO98nNtiQxFVUKbiMP6wy1CvqXloGDAAka4TPyR90tLVPDTNYy+U X-Received: by 2002:a17:90a:4593:: with SMTP id v19mr13754587pjg.207.1619768899765; Fri, 30 Apr 2021 00:48:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1619768899; cv=none; d=google.com; s=arc-20160816; b=DLmii77BFnHjeAJ1PNi4MC9WlJHD39TwP/wgYKC5U64weP702UU+6a7KPFfCH86TcZ zQ2t9DJGJLHWKGzdSd9kb7yFd+iXReXerJh64spK507UAb/Vij0JW0sfnvD3Kku8BFnw JOhLlyxrjy9d2drkz1ezG2NCiT/hK1V1nJNbTy9GsQe7gx6sIcDTcfYImgs6pgklg68k 2t2cMm/yDzB3dQzIUJFpom5EI/LEAlAdbJR103lui3OSJGzTFHPTIfcBJ47v1z0ZHaQL Cp0WBpf8McV6Mt6Ng3mFXqQYgHpf9pIGBMqOlLNUPgbHrCpKdMAmNhqmV5YCiZCi3qQI C1Yw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:references:in-reply-to:message-id:date:subject :cc:to:from:ironport-sdr:ironport-sdr; bh=j+/JdnH/2B9HX+Q+pw7m/Xc+gYDduDVAJLDtzbIbrRY=; b=djGj51kgkt9LiFrD76TR1xIsmaL40Fzz6RcluMuTXDf4iRY5KOvvJbYf0zpKp6r2Pj HuWdkdMUGF7Pxcmt2JHaD7W6CjO6Peiaww1EKn8cPrF5zrcjCa5AEX7v0puw4odWEbpd JjPmhkuZa6oN+flTZUoeip3bsdXGAFX7m3by/NVUwDPCpxwohiQHYLsb/GZPQU5vodlc eG0uz43g4aOybyvUd+BntH1oMd2/l7FJu6MTfn1f5UY8QSFIVk6z/Ex300C2VvWERmNs k3kXdBSlAMaistp/K2ts5F+rnJ9BvGBsOGJ464Gk1RldolQ9uzhlRYgaCDIjnUO9LM7p DZSQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id p17si2672962plq.340.2021.04.30.00.48.07; Fri, 30 Apr 2021 00:48:19 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231886AbhD3Hs1 (ORCPT + 99 others); Fri, 30 Apr 2021 03:48:27 -0400 Received: from mga03.intel.com ([134.134.136.65]:45970 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231665AbhD3Hrq (ORCPT ); Fri, 30 Apr 2021 03:47:46 -0400 IronPort-SDR: XejBNCppOhNjyarXQikmX7ndVgNQoGdUB7pelBwaewYY/mOV5UuMzp7zdM1MkAw6/erkASb2H1 WevB9psCzmLQ== X-IronPort-AV: E=McAfee;i="6200,9189,9969"; a="197268483" X-IronPort-AV: E=Sophos;i="5.82,260,1613462400"; d="scan'208";a="197268483" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 30 Apr 2021 00:46:58 -0700 IronPort-SDR: G02kBr5rgPsErJbkZ9igIVF2RynunDknxg/kZfD6zSV6vQmMChjBOgq2Ze4Snd4IMd16d9w/np fjEBS7SYP5Lg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.82,260,1613462400"; d="scan'208";a="527549183" Received: from kbl-ppc.sh.intel.com ([10.239.159.163]) by fmsmga001.fm.intel.com with ESMTP; 30 Apr 2021 00:46:52 -0700 From: Jin Yao To: acme@kernel.org, jolsa@kernel.org, peterz@infradead.org, mingo@redhat.com, alexander.shishkin@linux.intel.com Cc: Linux-kernel@vger.kernel.org, ak@linux.intel.com, kan.liang@intel.com, yao.jin@intel.com, Jin Yao Subject: [PATCH v1 2/2] perf header: Support hybrid CPU_PMU_CAPS Date: Fri, 30 Apr 2021 15:46:02 +0800 Message-Id: <20210430074602.3028-2-yao.jin@linux.intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210430074602.3028-1-yao.jin@linux.intel.com> References: <20210430074602.3028-1-yao.jin@linux.intel.com> Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On hybrid platform, it may have several cpu pmus, such as, "cpu_core" and "cpu_atom". The CPU_PMU_CAPS feature in perf header needs to be improved to support multiple cpu pmus. The new layout in header is defined as: It's considered to be compatible with old perf.data (the perf.data generated by old perf tool). With this patch, some examples, New perf tool with new perf.data (new perf.data is generated on hybrid platform): root@otcpl-adl-s-2:~# perf report --header-only -I ... # cpu_core pmu capabilities: branches=32, max_precise=3, pmu_name=alderlake_hybrid # cpu_atom pmu capabilities: branches=32, max_precise=3, pmu_name=alderlake_hybrid New perf tool with new perf.data (new perf.data is generated on non-hybrid platform): root@kbl-ppc:~# perf report --header-only -I ... # cpu pmu capabilities: branches=32, max_precise=3, pmu_name=skylake New perf tool with old perf.data (old perf.data is generated by old perf tool on non-hybrid platform): root@kbl-ppc:~# perf report --header-only -I ... # cpu pmu capabilities: branches=32, max_precise=3, pmu_name=skylake Note that: this patch is on tmp.perf/core. Signed-off-by: Jin Yao --- tools/perf/util/env.c | 6 ++ tools/perf/util/env.h | 11 ++- tools/perf/util/header.c | 175 ++++++++++++++++++++++++++++++++++----- 3 files changed, 168 insertions(+), 24 deletions(-) diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 9e05eca324a1..8ef24aad2152 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -208,6 +208,12 @@ void perf_env__exit(struct perf_env *env) zfree(&env->hybrid_nodes[i].pmu_name); } zfree(&env->hybrid_nodes); + + for (i = 0; i < env->nr_cpu_pmu_caps_nodes; i++) { + zfree(&env->cpu_pmu_caps_nodes[i].cpu_pmu_caps); + zfree(&env->cpu_pmu_caps_nodes[i].pmu_name); + } + zfree(&env->cpu_pmu_caps_nodes); } void perf_env__init(struct perf_env *env __maybe_unused) diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 9ca7633787e1..5552c98a6a76 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -42,6 +42,13 @@ struct hybrid_node { struct perf_cpu_map *map; }; +struct cpu_pmu_caps_node { + int nr_cpu_pmu_caps; + unsigned int max_branches; + char *cpu_pmu_caps; + char *pmu_name; +}; + struct perf_env { char *hostname; char *os_release; @@ -63,15 +70,14 @@ struct perf_env { int nr_memory_nodes; int nr_pmu_mappings; int nr_groups; - int nr_cpu_pmu_caps; int nr_hybrid_nodes; + int nr_cpu_pmu_caps_nodes; char *cmdline; const char **cmdline_argv; char *sibling_cores; char *sibling_dies; char *sibling_threads; char *pmu_mappings; - char *cpu_pmu_caps; struct cpu_topology_map *cpu; struct cpu_cache_level *caches; int caches_cnt; @@ -84,6 +90,7 @@ struct perf_env { struct memory_node *memory_nodes; unsigned long long memory_bsize; struct hybrid_node *hybrid_nodes; + struct cpu_pmu_caps_node *cpu_pmu_caps_nodes; #ifdef HAVE_LIBBPF_SUPPORT /* * bpf_info_lock protects bpf rbtrees. This is needed because the diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index dff89c0be79c..6989c57b57e6 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -49,6 +49,7 @@ #include "cputopo.h" #include "bpf-event.h" #include "clockid.h" +#include "pmu-hybrid.h" #include #include @@ -1459,18 +1460,22 @@ static int write_compressed(struct feat_fd *ff __maybe_unused, return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len)); } -static int write_cpu_pmu_caps(struct feat_fd *ff, - struct evlist *evlist __maybe_unused) +static int write_per_cpu_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu, + int nr) { - struct perf_pmu *cpu_pmu = perf_pmu__find("cpu"); struct perf_pmu_caps *caps = NULL; int nr_caps; int ret; - if (!cpu_pmu) - return -ENOENT; - - nr_caps = perf_pmu__caps_parse(cpu_pmu); + /* + * The layout is: + * + * + * + * + * + */ + nr_caps = perf_pmu__caps_parse(pmu); if (nr_caps < 0) return nr_caps; @@ -1478,7 +1483,7 @@ static int write_cpu_pmu_caps(struct feat_fd *ff, if (ret < 0) return ret; - list_for_each_entry(caps, &cpu_pmu->caps, list) { + list_for_each_entry(caps, &pmu->caps, list) { ret = do_write_string(ff, caps->name); if (ret < 0) return ret; @@ -1488,9 +1493,49 @@ static int write_cpu_pmu_caps(struct feat_fd *ff, return ret; } + ret = do_write_string(ff, pmu->name); + if (ret < 0) + return ret; + + ret = do_write(ff, &nr, sizeof(nr)); + if (ret < 0) + return ret; + return ret; } +static int write_cpu_pmu_caps(struct feat_fd *ff, + struct evlist *evlist __maybe_unused) +{ + struct perf_pmu *pmu = perf_pmu__find("cpu"); + u32 nr; + int ret; + + if (pmu) + nr = 1; + else { + nr = perf_pmu__hybrid_pmu_num(); + pmu = NULL; + } + + if (nr == 0) + return -1; + + if (pmu) { + ret = write_per_cpu_pmu_caps(ff, pmu, 0); + if (ret < 0) + return ret; + } else { + perf_pmu__for_each_hybrid_pmu(pmu) { + ret = write_per_cpu_pmu_caps(ff, pmu, --nr); + if (ret < 0) + return ret; + } + } + + return 0; +} + static void print_hostname(struct feat_fd *ff, FILE *fp) { fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname); @@ -1963,18 +2008,28 @@ static void print_compressed(struct feat_fd *ff, FILE *fp) ff->ph->env.comp_level, ff->ph->env.comp_ratio); } -static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) +static void print_per_cpu_pmu_caps(FILE *fp, struct cpu_pmu_caps_node *n) { - const char *delimiter = "# cpu pmu capabilities: "; - u32 nr_caps = ff->ph->env.nr_cpu_pmu_caps; - char *str; + const char *delimiter; + u32 nr_caps = n->nr_cpu_pmu_caps; + char *str, buf[128]; if (!nr_caps) { - fprintf(fp, "# cpu pmu capabilities: not available\n"); + if (!n->pmu_name) + fprintf(fp, "# cpu pmu capabilities: not available\n"); + else + fprintf(fp, "# %s pmu capabilities: not available\n", n->pmu_name); return; } - str = ff->ph->env.cpu_pmu_caps; + if (!n->pmu_name) + scnprintf(buf, sizeof(buf), "# cpu pmu capabilities: "); + else + scnprintf(buf, sizeof(buf), "# %s pmu capabilities: ", n->pmu_name); + + delimiter = buf; + + str = n->cpu_pmu_caps; while (nr_caps--) { fprintf(fp, "%s%s", delimiter, str); delimiter = ", "; @@ -1984,6 +2039,17 @@ static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) fprintf(fp, "\n"); } +static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp) +{ + struct cpu_pmu_caps_node *n; + int i; + + for (i = 0; i < ff->ph->env.nr_cpu_pmu_caps_nodes; i++) { + n = &ff->ph->env.cpu_pmu_caps_nodes[i]; + print_per_cpu_pmu_caps(fp, n); + } +} + static void print_pmu_mappings(struct feat_fd *ff, FILE *fp) { const char *delimiter = "# pmu mappings: "; @@ -3093,13 +3159,14 @@ static int process_compressed(struct feat_fd *ff, return 0; } -static int process_cpu_pmu_caps(struct feat_fd *ff, - void *data __maybe_unused) +static int process_cpu_pmu_caps_node(struct feat_fd *ff, + struct cpu_pmu_caps_node *n, bool *end) { - char *name, *value; + char *name, *value, *pmu_name; struct strbuf sb; - u32 nr_caps; + u32 nr_caps, nr; + *end = false; if (do_read_u32(ff, &nr_caps)) return -1; @@ -3108,7 +3175,7 @@ static int process_cpu_pmu_caps(struct feat_fd *ff, return 0; } - ff->ph->env.nr_cpu_pmu_caps = nr_caps; + n->nr_cpu_pmu_caps = nr_caps; if (strbuf_init(&sb, 128) < 0) return -1; @@ -3129,13 +3196,33 @@ static int process_cpu_pmu_caps(struct feat_fd *ff, if (strbuf_add(&sb, "", 1) < 0) goto free_value; - if (!strcmp(name, "branches")) - ff->ph->env.max_branches = atoi(value); + if (!strcmp(name, "branches")) { + n->max_branches = atoi(value); + if (n->max_branches > ff->ph->env.max_branches) + ff->ph->env.max_branches = n->max_branches; + } free(value); free(name); } - ff->ph->env.cpu_pmu_caps = strbuf_detach(&sb, NULL); + + /* + * Old perf.data may not have pmu_name, + */ + pmu_name = do_read_string(ff); + if (!pmu_name || strncmp(pmu_name, "cpu_", 4)) { + *end = true; + goto out; + } + + if (do_read_u32(ff, &nr)) + return -1; + + if (nr == 0) + *end = true; +out: + n->cpu_pmu_caps = strbuf_detach(&sb, NULL); + n->pmu_name = pmu_name; return 0; free_value: @@ -3147,6 +3234,50 @@ static int process_cpu_pmu_caps(struct feat_fd *ff, return -1; } +static int process_cpu_pmu_caps(struct feat_fd *ff, + void *data __maybe_unused) +{ + struct cpu_pmu_caps_node *nodes = NULL, *tmp; + int ret, i, nr_alloc = 1, nr_used = 0; + bool end; + + while (1) { + if (nr_used == nr_alloc || !nodes) { + nr_alloc *= 2; + tmp = realloc(nodes, sizeof(*nodes) * nr_alloc); + if (!tmp) + return -ENOMEM; + memset(tmp + nr_used, 0, + sizeof(*nodes) * (nr_alloc - nr_used)); + nodes = tmp; + } + + ret = process_cpu_pmu_caps_node(ff, &nodes[nr_used], &end); + if (ret) { + if (nr_used) + break; + goto err; + } + + nr_used++; + if (end) + break; + } + + ff->ph->env.nr_cpu_pmu_caps_nodes = (u32)nr_used; + ff->ph->env.cpu_pmu_caps_nodes = nodes; + return 0; + +err: + for (i = 0; i < nr_used; i++) { + free(nodes[i].cpu_pmu_caps); + free(nodes[i].pmu_name); + } + + free(nodes); + return ret; +} + #define FEAT_OPR(n, func, __full_only) \ [HEADER_##n] = { \ .name = __stringify(n), \ -- 2.17.1