From nobody Fri Jul 24 21:52:58 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7CB443F4831; Fri, 24 Jul 2026 09:52:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886727; cv=none; b=IGlBFjiWdc6iaI3Cwru53bf27innevIOu9n5TQyEj3LbboUNQEXidoxtFpgifu/sm3RnpU0k7Ai2j1XGc4zz2eLrWZn9AFER6BAPahAsOpdEen7wrmFTcEw4zz1CP3vhzUPHFcAe4a81nBBkvvc12M4A+FsMxdZrzPudK8IZTLc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886727; c=relaxed/simple; bh=ATGHRjit6QlTuDdIZ41LbqZvG2hh+QDuLrI/HLK7JlA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gW/nWQVuexyfVgCzWyUG8uAvJepwG0TMts+f0JwMG11MQAn5Sh8pA9dBFcrlDZ60ntyaVMG3dl1y2YUluFUfLyxFApFRsaT/B0KY/xIF6OItt+RBrARS5/1r1gwppI8KeFB7T1optAHsGZZJkqOYeO4uxMEzNVFnTWhdm+bglQ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=L5cY4ywl; arc=none smtp.client-ip=198.175.65.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="L5cY4ywl" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1784886726; x=1816422726; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ATGHRjit6QlTuDdIZ41LbqZvG2hh+QDuLrI/HLK7JlA=; b=L5cY4ywl5fzTV7Of/nKgUyP81Z4cusoqKmCs79WuJgz9NRvJYHO7DaVQ wOJTnkjQo1ID1W093GnDB3y88xKuvv5n4YgEZGzoFd0UpA4yAHJBm+WL1 ZHK6FJB/zcN8oULiOEztgaVfaAIKrq6Et4HibMR29Yo0awe7uXO5I530g PpiudybTNIt4o67mSJFFUdXKtRNM1famAqieGeDF2uIDG3UHVqwqsyh4A Lpon1vmfIwmx1cRqh+yTyEpFS+vT+Gd8jpC15UpZLizYBrF+Ij7/fEYNx buUIxzUcmWrTDOlsZnOy1z1TJvO/16DCJaNfiFI8OG+TZaTXsTwe6TE7o A==; X-CSE-ConnectionGUID: 4A4x62CwRYazdc1CI4U/5g== X-CSE-MsgGUID: c/U+Q+38Qgaecc56htkIjg== X-IronPort-AV: E=McAfee;i="6800,10657,11854"; a="95907566" X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="95907566" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Jul 2026 02:52:06 -0700 X-CSE-ConnectionGUID: 1k4sLbvZQUWvr8kilsf6OQ== X-CSE-MsgGUID: X4zwbx/1RVqsmrP7O3H+LQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="256018429" Received: from linux-pnp-gnr-1.sh.intel.com ([10.239.83.186]) by fmviesa008.fm.intel.com with ESMTP; 24 Jul 2026 02:52:00 -0700 From: Jiebin Sun To: Namhyung Kim , acme@kernel.org, mingo@redhat.com, peterz@infradead.org Cc: adrian.hunter@intel.com, alexander.shishkin@linux.intel.com, irogers@google.com, james.clark@linaro.org, jolsa@kernel.org, mark.rutland@arm.com, dapeng1.mi@linux.intel.com, thomas.falcon@intel.com, tianyou.li@intel.com, wangyang.guo@intel.com, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Jiebin Sun Subject: [PATCH v4 1/9] perf c2c: extract shared data structures into c2c.h Date: Fri, 24 Jul 2026 17:58:34 +0800 Message-ID: <20260724095842.995920-2-jiebin.sun@intel.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260724095842.995920-1-jiebin.sun@intel.com> References: <20260717020530.1645123-1-jiebin.sun@intel.com> <20260724095842.995920-1-jiebin.sun@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move c2c_hists, compute_stats, c2c_hist_entry, and perf_c2c structure definitions from builtin-c2c.c into a new shared header c2c.h. This allows the upcoming function view browser (c2c-function.c) to reuse these types. Make the global perf_c2c instance 'c2c' non-static and export perf_c2c__browse_cacheline() so they can be accessed from the new function view module. No functional change. Signed-off-by: Jiebin Sun Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Reviewed-by: Tianyou Li Reviewed-by: Wangyang Guo --- tools/perf/builtin-c2c.c | 128 ++------------------------------ tools/perf/c2c.h | 153 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 120 deletions(-) create mode 100644 tools/perf/c2c.h diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index c9584dbedf77..f1dd9d8699cb 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -38,6 +38,7 @@ #include "mem-events.h" #include "mem-info.h" #include "mem2node.h" +#include "c2c.h" #include "pmus.h" #include "session.h" #include "sort.h" @@ -52,75 +53,9 @@ #include "util/symbol.h" #include "util/util.h" =20 -struct c2c_hists { - struct hists hists; - struct perf_hpp_list list; - struct c2c_stats stats; -}; - -struct compute_stats { - struct stats lcl_hitm; - struct stats rmt_hitm; - struct stats lcl_peer; - struct stats rmt_peer; - struct stats load; -}; - -struct c2c_hist_entry { - struct c2c_hists *hists; - struct evsel *evsel; - struct c2c_stats stats; - unsigned long *cpuset; - unsigned long *nodeset; - struct c2c_stats *node_stats; - unsigned int cacheline_idx; - - struct compute_stats cstats; - - unsigned long paddr; - unsigned long paddr_cnt; - bool paddr_zero; - char *nodestr; - - /* - * must be at the end, - * because of its callchain dynamic entry - */ - struct hist_entry he; -}; =20 static char const *coalesce_default =3D "iaddr"; =20 -struct perf_c2c { - struct perf_tool tool; - struct c2c_hists hists; - struct mem2node mem2node; - - unsigned long **nodes; - int nodes_cnt; - int cpus_cnt; - int *cpu2node; - int node_info; - - bool show_src; - bool show_all; - bool use_stdio; - bool stats_only; - bool symbol_full; - bool stitch_lbr; - - /* Shared cache line stats */ - struct c2c_stats shared_clines_stats; - int shared_clines; - - int display; - - const char *coalesce; - char *cl_sort; - char *cl_resort; - char *cl_output; -}; - enum { DISPLAY_LCL_HITM, DISPLAY_RMT_HITM, @@ -141,9 +76,9 @@ static const struct option c2c_options[] =3D { OPT_END() }; =20 -static struct perf_c2c c2c; +struct perf_c2c c2c; =20 -static void *c2c_he_zalloc(size_t size) +void *c2c_he_zalloc(size_t size) { struct c2c_hist_entry *c2c_he; =20 @@ -458,36 +393,6 @@ static const char * const __usage_report[] =3D { =20 static const char * const *report_c2c_usage =3D __usage_report; =20 -#define C2C_HEADER_MAX 2 - -struct c2c_header { - struct { - const char *text; - int span; - } line[C2C_HEADER_MAX]; -}; - -struct c2c_dimension { - struct c2c_header header; - const char *name; - int width; - struct sort_entry *se; - - int64_t (*cmp)(struct perf_hpp_fmt *fmt, - struct hist_entry *, struct hist_entry *); - int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, - struct hist_entry *he); - int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, - struct hist_entry *he); -}; - -struct c2c_fmt { - struct perf_hpp_fmt fmt; - struct c2c_dimension *dim; -}; - -#define SYMBOL_WIDTH 30 - static struct c2c_dimension dim_symbol; static struct c2c_dimension dim_srcline; =20 @@ -1389,23 +1294,6 @@ cl_idx_empty_entry(struct perf_hpp_fmt *fmt, struct = perf_hpp *hpp, return scnprintf(hpp->buf, hpp->size, "%*s", width, ""); } =20 -#define HEADER_LOW(__h) \ - { \ - .line[1] =3D { \ - .text =3D __h, \ - }, \ - } - -#define HEADER_BOTH(__h0, __h1) \ - { \ - .line[0] =3D { \ - .text =3D __h0, \ - }, \ - .line[1] =3D { \ - .text =3D __h1, \ - }, \ - } - #define HEADER_SPAN(__h0, __h1, __s) \ { \ .line[0] =3D { \ @@ -1928,7 +1816,7 @@ static struct c2c_dimension *dimensions[] =3D { NULL, }; =20 -static void fmt_free(struct perf_hpp_fmt *fmt) +void c2c_fmt_free(struct perf_hpp_fmt *fmt) { struct c2c_fmt *c2c_fmt; =20 @@ -1936,7 +1824,7 @@ static void fmt_free(struct perf_hpp_fmt *fmt) free(c2c_fmt); } =20 -static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) +bool c2c_fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) { struct c2c_fmt *c2c_a =3D container_of(a, struct c2c_fmt, fmt); struct c2c_fmt *c2c_b =3D container_of(b, struct c2c_fmt, fmt); @@ -2021,8 +1909,8 @@ static struct c2c_fmt *get_format(const char *name) fmt->header =3D c2c_header; fmt->width =3D c2c_width; fmt->collapse =3D dim->se ? c2c_se_collapse : dim->cmp; - fmt->equal =3D fmt_equal; - fmt->free =3D fmt_free; + fmt->equal =3D c2c_fmt_equal; + fmt->free =3D c2c_fmt_free; =20 return c2c_fmt; } @@ -2710,7 +2598,7 @@ c2c_cacheline_browser__new(struct hists *hists, struc= t hist_entry *he) return browser; } =20 -static int perf_c2c__browse_cacheline(struct hist_entry *he) +int perf_c2c__browse_cacheline(struct hist_entry *he) { struct c2c_hist_entry *c2c_he; struct c2c_hists *c2c_hists; diff --git a/tools/perf/c2c.h b/tools/perf/c2c.h new file mode 100644 index 000000000000..432d5f0b28f1 --- /dev/null +++ b/tools/perf/c2c.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PERF_C2C_H_ +#define _PERF_C2C_H_ 1 + +#include +#include +#include +#include "util/stat.h" +#include "util/hist.h" +#include "util/mem-events.h" +#include "util/mem2node.h" +#include "util/tool.h" + +struct sort_entry; + +struct compute_stats { + struct stats lcl_hitm; + struct stats rmt_hitm; + struct stats lcl_peer; + struct stats rmt_peer; + struct stats load; +}; + +struct c2c_hists { + struct hists hists; + struct perf_hpp_list list; + struct c2c_stats stats; +}; + +struct c2c_hist_entry { + struct c2c_hists *hists; + struct evsel *evsel; + struct c2c_stats stats; + unsigned long *cpuset; + unsigned long *nodeset; + struct c2c_stats *node_stats; + unsigned int cacheline_idx; + + struct compute_stats cstats; + + unsigned long paddr; + unsigned long paddr_cnt; + bool paddr_zero; + char *nodestr; + + /* + * Function view only: cached sum of the level-2 children's store + * counts, accumulated while the hierarchy is built. Lets the + * total-stores column and its sort comparator avoid an O(children) + * walk on every access. Unused by the cacheline view. + */ + u64 child_stores; + + /* + * must be at the end, + * because of its callchain dynamic entry + */ + struct hist_entry he; +}; + +struct perf_c2c { + struct perf_tool tool; + struct c2c_hists hists; + struct mem2node mem2node; + + unsigned long **nodes; + int nodes_cnt; + int cpus_cnt; + int *cpu2node; + int node_info; + + bool show_src; + bool show_all; + bool use_stdio; + bool stats_only; + bool symbol_full; + bool stitch_lbr; + + /* Shared cache line stats */ + struct c2c_stats shared_clines_stats; + int shared_clines; + + int display; + + const char *coalesce; + char *cl_sort; + char *cl_resort; + char *cl_output; +}; + +extern struct perf_c2c c2c; + +#define C2C_HEADER_MAX 2 +#define SYMBOL_WIDTH 30 + +#define HEADER_LOW(__h) \ + { \ + .line[1] =3D { \ + .text =3D __h, \ + }, \ + } + +#define HEADER_BOTH(__h0, __h1) \ + { \ + .line[0] =3D { \ + .text =3D __h0, \ + }, \ + .line[1] =3D { \ + .text =3D __h1, \ + }, \ + } + +struct c2c_header { + struct { + const char *text; + int span; + } line[C2C_HEADER_MAX]; +}; + +struct c2c_dimension { + struct c2c_header header; + const char *name; + int width; + struct sort_entry *se; + + int64_t (*cmp)(struct perf_hpp_fmt *fmt, + struct hist_entry *left, struct hist_entry *right); + int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hist_entry *he); + int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hist_entry *he); +}; + +struct c2c_fmt { + struct perf_hpp_fmt fmt; + struct c2c_dimension *dim; +}; + +void *c2c_he_zalloc(size_t size); +void c2c_fmt_free(struct perf_hpp_fmt *fmt); +bool c2c_fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); + +/* + * The TUI browsers are only built with SLANG support. Their sole callers + * live inside HAVE_SLANG_SUPPORT-guarded code in builtin-c2c.c (stdio mode + * forces use_browser=3D0), so no #else stub is needed -- matching the res= t of + * perf's TUI entry points. + */ +#ifdef HAVE_SLANG_SUPPORT +int perf_c2c__browse_cacheline(struct hist_entry *he); +#endif + +#endif /* _PERF_C2C_H_ */ --=20 2.52.0 From nobody Fri Jul 24 21:52:58 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5C54841D20C; Fri, 24 Jul 2026 09:52:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886731; cv=none; b=OkjEbLleXc14pXqqPX3OIm0M+X4Ur6F4GHdduFkTGPbFrUCL4QYPqjpXwrvNynwa5d9LgT3id0Les6TD31mpJeruB6M37eQUXffGS8OFx1WGcll9hAQfkpTe7NVA0tM1363q0mcOCZ1QX9MKrYAlm/Jd0/yHosweqC2+2+EIZLQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886731; c=relaxed/simple; bh=7TdEbx12b9EFVwThnbv1y9rx3xTIzUxbxgMzrl+zafQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=d+DklAp+NmbkyeUcEi4F1uIU0DPRdB9Ad8ZjGJJflmFrQbZIbEHNJ/oKgZrJZAPk0aCmo06mmj2nm8+JZT9eupOAVveTvSzOVpOz3rvTqCuszOUtwSNmxXXKEaIqluR45na8vGc/Fnf3RCVf7JdQDAjzE8LcIkCd1A5ynyRNQC8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=WAM5IRNi; arc=none smtp.client-ip=198.175.65.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="WAM5IRNi" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1784886730; x=1816422730; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=7TdEbx12b9EFVwThnbv1y9rx3xTIzUxbxgMzrl+zafQ=; b=WAM5IRNinwHQT5hydnOYfHWODsEOERDmKH8rb4FxL9ULrWzjGdZFrN3W iN/6DpZ0aEFIzDKCgPo9HEXRgoRSUwLu1IIRCyb0ykEJuTEtC0KcHwMux YuHm5H7O4mo9jutZXEtTgaLlekofu0UaIIBrP4We358UZiASQFEH42P82 f3BGzaq/hHNPUZtMS2WbJKVpbKX15o32YJjXM0xpU5AOH4fW5fza9Zaz6 6myAPTrgYaWboKbDDpwD4xo7sDEOE9gIBbkFnEOiPa47vvhUwifO5E8BM FSxu0xRmKGmeyKZ2SguvOPciVTffW1mTIqBE/vacnvoQuq7hOQ+hNGDvI w==; X-CSE-ConnectionGUID: d9Dqg5wESMWcd0z1owYf2g== X-CSE-MsgGUID: /vJAEXXgQmKZ4yZFUBPD9Q== X-IronPort-AV: E=McAfee;i="6800,10657,11854"; a="95907578" X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="95907578" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Jul 2026 02:52:09 -0700 X-CSE-ConnectionGUID: FFaS0CWqQoi8fJReA0+eJA== X-CSE-MsgGUID: AkBiTo5pQv299M00XiO9mw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="256018437" Received: from linux-pnp-gnr-1.sh.intel.com ([10.239.83.186]) by fmviesa008.fm.intel.com with ESMTP; 24 Jul 2026 02:52:04 -0700 From: Jiebin Sun To: Namhyung Kim , acme@kernel.org, mingo@redhat.com, peterz@infradead.org Cc: adrian.hunter@intel.com, alexander.shishkin@linux.intel.com, irogers@google.com, james.clark@linaro.org, jolsa@kernel.org, mark.rutland@arm.com, dapeng1.mi@linux.intel.com, thomas.falcon@intel.com, tianyou.li@intel.com, wangyang.guo@intel.com, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Jiebin Sun Subject: [PATCH v4 2/9] perf c2c: add function view browser skeleton Date: Fri, 24 Jul 2026 17:58:35 +0800 Message-ID: <20260724095842.995920-3-jiebin.sun@intel.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260724095842.995920-1-jiebin.sun@intel.com> References: <20260717020530.1645123-1-jiebin.sun@intel.com> <20260724095842.995920-1-jiebin.sun@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the skeleton of the c2c function view: a new TUI browser in tools/perf/ui/browsers/c2c-function.c reached by pressing TAB in the cacheline view. This commit wires up the entry point (a stub that will be filled in by later patches), registers the file in the build, declares perf_c2c__browse_function_view() in c2c.h, and adds the TAB key handler and help text to the cacheline browser. Signed-off-by: Jiebin Sun Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Reviewed-by: Tianyou Li Reviewed-by: Wangyang Guo --- tools/perf/builtin-c2c.c | 4 ++ tools/perf/c2c.h | 1 + tools/perf/ui/browsers/Build | 1 + tools/perf/ui/browsers/c2c-function.c | 79 +++++++++++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 tools/perf/ui/browsers/c2c-function.c diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index f1dd9d8699cb..23c58eba415a 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -2693,6 +2693,7 @@ static int perf_c2c__hists_browse(struct hists *hists) static const char help[] =3D " d Display cacheline details \n" " ENTER Toggle callchains (if present) \n" + " TAB Switch to function view\n" " q Quit \n"; =20 browser =3D perf_c2c_browser__new(hists); @@ -2714,6 +2715,9 @@ static int perf_c2c__hists_browse(struct hists *hists) case 'd': perf_c2c__browse_cacheline(browser->he_selection); break; + case '\t': + perf_c2c__browse_function_view(hists); + break; case '?': ui_browser__help_window(&browser->b, help); break; diff --git a/tools/perf/c2c.h b/tools/perf/c2c.h index 432d5f0b28f1..8d0871d9556b 100644 --- a/tools/perf/c2c.h +++ b/tools/perf/c2c.h @@ -148,6 +148,7 @@ bool c2c_fmt_equal(struct perf_hpp_fmt *a, struct perf_= hpp_fmt *b); */ #ifdef HAVE_SLANG_SUPPORT int perf_c2c__browse_cacheline(struct hist_entry *he); +int perf_c2c__browse_function_view(struct hists *hists); #endif =20 #endif /* _PERF_C2C_H_ */ diff --git a/tools/perf/ui/browsers/Build b/tools/perf/ui/browsers/Build index a07489e44765..ae67a2161f7d 100644 --- a/tools/perf/ui/browsers/Build +++ b/tools/perf/ui/browsers/Build @@ -5,3 +5,4 @@ perf-ui-y +=3D map.o perf-ui-y +=3D scripts.o perf-ui-y +=3D header.o perf-ui-y +=3D res_sample.o +perf-ui-y +=3D c2c-function.o diff --git a/tools/perf/ui/browsers/c2c-function.c b/tools/perf/ui/browsers= /c2c-function.c new file mode 100644 index 000000000000..3f6822f61600 --- /dev/null +++ b/tools/perf/ui/browsers/c2c-function.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * C2C Function Browser - function-level cacheline sharing analysis + * + * Displays a 3-level hierarchy showing which functions share cachelines: + * Level 1: Read-side functions sorted by Cycles % (estimated load cycle= s) + * Level 2: Functions that write the shared lines, causing the level-1 H= ITMs + * Level 3: The specific cachelines where the two functions contend + * + * Builds the hierarchy from the existing cacheline histograms + * (c2c_hist_entry->hists), reusing the shared c2c data structures. + */ + +#include +#include +#include +#include /* reallocarray */ +#include +#include +#include +#include +#include + +#include "../browser.h" +#include "../helpline.h" +#include "../keysyms.h" +#include "../libslang.h" +#include "../ui.h" +#include "../../util/addr_location.h" +#include "../../util/cacheline.h" +#include "../../util/debug.h" +#include "../../util/hist.h" +#include "../../util/map.h" +#include "../../util/mem-events.h" +#include "../../util/mem-info.h" +#include "../../util/sort.h" +#include "../../util/symbol.h" +#include "../../util/thread.h" +#include "../../c2c.h" +#include "hists.h" + + +struct perf_c2c_ext { + struct c2c_hists function_hists; + /* Cached across all level-1 entries; 0 means "not yet computed". */ + u64 total_cycles; +}; + +static struct perf_c2c_ext c2c_ext __maybe_unused; + +struct c2c_function_browser { + struct hist_browser hb; +}; + +static inline __maybe_unused u64 c2c_hitm_count(const struct c2c_stats *st= ats) +{ + return stats->tot_hitm; +} + +static inline __maybe_unused bool symbol_name_equal(struct symbol *a, stru= ct symbol *b) +{ + /* Two unknown symbols compare equal, matching cmp_null() in util/sort.c.= */ + if (!a || !b) + return a =3D=3D b; + return arch__compare_symbol_names(a->name, b->name) =3D=3D 0; +} + +static inline __maybe_unused u64 hist_entry__iaddr(struct hist_entry *he) +{ + if (he->mem_info) + return mem_info__iaddr(he->mem_info)->addr; + return he->ip; +} + +int perf_c2c__browse_function_view(struct hists *hists __maybe_unused) +{ + ui__warning("C2C function view is not implemented yet.\n"); + return 0; +} --=20 2.52.0 From nobody Fri Jul 24 21:52:58 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7FB213FADE0; Fri, 24 Jul 2026 09:52:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886735; cv=none; b=gewhA0cMWuLSTlKGdA+1nQjS5ok6uhspkXjRmjQea7Nu+0zZ9vArBG9XhtG6DawHeY1b3485XkZnW6pXJvk/Ku7Jgz+YODGGt3LLaVnjXaVBwVnU5KFusrk0UnBgcOI7i7S8n6OWd+4iuuwhaYEuRFVXDCfyBHpTFD5QdhQI9fQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886735; c=relaxed/simple; bh=OCf1/DcdK+MXBkgOVMm78D6bheZ7CJt5ApGl4fAmQN4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PCOSo5HIM2dNVgW+JpQxoIqzGY9nhONTRrhuEH8MKTTzaoUB/7HCM7yEfEzL2auhI86geZYN1qmTzH47+1A3S6UlhcL5kxhzdmkoGK+TXIYyaQixwb2AwSUArsf9M2OyMZj2VO0KaX950rVxTkMfORNMFWO3Z4rqS9+vaYxZtQ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=SKjA0ala; arc=none smtp.client-ip=198.175.65.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="SKjA0ala" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1784886734; x=1816422734; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=OCf1/DcdK+MXBkgOVMm78D6bheZ7CJt5ApGl4fAmQN4=; b=SKjA0alaK7WX4Pr8n887y8wX5A82aVFF9VwSZBdCItFmCUH8G/xRCD38 BI50lNM127M3Z0U8BqZyo+weBwxkwS/Mk0JCyyW1H7lO6Cop9epnxa/Dr vNG8n5Nbw/68pXUCoxSnio5LwAIPylFIh3APZz7q3mpYioYbIBpel9Lur 7TQQbLEYBGqN9CyniDvEP5qUJXh9sWtj6NqmaPMjW4oAiLAQkkIHN0TY6 kmLjMvcJOGDQKtpVxU+vAYqskzgQaj87uE7oKaAbOYuativ1UgOZGM5W5 qpq2KQ3u9aQejlVkLEm4AgL1ZlCJJ6xOZPMQhjLd3WwnMn3xUFtPvxm4s g==; X-CSE-ConnectionGUID: c5XpFpJBSNe9rik3V84Lng== X-CSE-MsgGUID: N8oquUFrT7+CDKsr/sst/w== X-IronPort-AV: E=McAfee;i="6800,10657,11854"; a="95907596" X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="95907596" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Jul 2026 02:52:14 -0700 X-CSE-ConnectionGUID: UqBZDpdyRH2dqjs7CZ3MLA== X-CSE-MsgGUID: a7YGhuBvSJCi4pPGdEjgOw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="256018444" Received: from linux-pnp-gnr-1.sh.intel.com ([10.239.83.186]) by fmviesa008.fm.intel.com with ESMTP; 24 Jul 2026 02:52:08 -0700 From: Jiebin Sun To: Namhyung Kim , acme@kernel.org, mingo@redhat.com, peterz@infradead.org Cc: adrian.hunter@intel.com, alexander.shishkin@linux.intel.com, irogers@google.com, james.clark@linaro.org, jolsa@kernel.org, mark.rutland@arm.com, dapeng1.mi@linux.intel.com, thomas.falcon@intel.com, tianyou.li@intel.com, wangyang.guo@intel.com, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Jiebin Sun Subject: [PATCH v4 3/9] perf c2c: add column rendering for function view Date: Fri, 24 Jul 2026 17:58:36 +0800 Message-ID: <20260724095842.995920-4-jiebin.sun@intel.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260724095842.995920-1-jiebin.sun@intel.com> References: <20260717020530.1645123-1-jiebin.sun@intel.com> <20260724095842.995920-1-jiebin.sun@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the function view's column renderers: the Cycles %, Store count and the single indented identity column (function / cacheline) with per-level indentation, the width/header helpers, the per-function estimated-cycles computation, and the dimension table tying each column to its renderer and comparator. The HPP list parsing that turns a column string into these dimensions is added by the next patch; symbols consumed only later are __maybe_unused for now. Signed-off-by: Jiebin Sun Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Reviewed-by: Tianyou Li Reviewed-by: Wangyang Guo --- tools/perf/ui/browsers/c2c-function.c | 335 ++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) diff --git a/tools/perf/ui/browsers/c2c-function.c b/tools/perf/ui/browsers= /c2c-function.c index 3f6822f61600..4eda97894d1d 100644 --- a/tools/perf/ui/browsers/c2c-function.c +++ b/tools/perf/ui/browsers/c2c-function.c @@ -72,6 +72,341 @@ static inline __maybe_unused u64 hist_entry__iaddr(stru= ct hist_entry *he) return he->ip; } =20 +static inline bool hist_entry__is_cacheline(struct hist_entry *he) +{ + return he->parent_he && he->parent_he->parent_he; /* level 3: cacheline */ +} + +/* Spaces of indent per hierarchy level, like the normal report view. */ +#define C2C_FUNC_INDENT 2 + +/* Width of the folded-sign prefix ("%c ") each identity cell emits. */ +#define C2C_FUNC_FOLD_WIDTH 2 + +/* + * Write he->depth levels of leading indentation into @buf, so lower-level + * entries are visually nested under their parent. Returns bytes written. + */ +static int hist_entry__indent(struct hist_entry *he, char *buf, size_t siz= e) +{ + int indent =3D he->depth * C2C_FUNC_INDENT; + + if (indent <=3D 0 || (size_t)indent >=3D size) + return 0; + + return scnprintf(buf, size, "%*s", indent, ""); +} + +static int symbol_width(struct hists *hists, struct sort_entry *se) +{ + int width =3D hists__col_len(hists, se->se_width_idx); + + /* + * Cap long symbol names as the cacheline view does, but never below + * what a level-3 cacheline address needs (deepest indent + folded sign + * + "0x" + 16 hex digits), so the address is not truncated when the + * level-1 function names happen to be short. + */ + if (!c2c.symbol_full && width > SYMBOL_WIDTH) { + int cl_min =3D 2 * C2C_FUNC_INDENT + C2C_FUNC_FOLD_WIDTH + 2 + 16; + + width =3D max(SYMBOL_WIDTH, cl_min); + } + + return width; +} + +static struct c2c_dimension dim_symbol_view; + +/* + * c2c_width - Calculate width for a C2C column in function view + */ +static int c2c_width(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp __maybe_unused, + struct hists *hists) +{ + struct c2c_fmt *c2c_fmt; + struct c2c_dimension *dim; + + c2c_fmt =3D container_of(fmt, struct c2c_fmt, fmt); + dim =3D c2c_fmt->dim; + + if (dim =3D=3D &dim_symbol_view) + return symbol_width(hists, dim->se); + + return dim->se ? hists__col_len(hists, dim->se->se_width_idx) : + dim->width; +} + +static int __maybe_unused c2c_header(struct perf_hpp_fmt *fmt, struct perf= _hpp *hpp, + struct hists *hists, int line, int *span) +{ + struct c2c_fmt *c2c_fmt; + struct c2c_dimension *dim; + const char *text =3D NULL; + int width =3D c2c_width(fmt, hpp, hists); + + c2c_fmt =3D container_of(fmt, struct c2c_fmt, fmt); + dim =3D c2c_fmt->dim; + + if (dim->se) { + text =3D dim->header.line[line].text; + /* Use the last line from sort_entry if not defined. */ + if (!text && line =3D=3D hists->hpp_list->nr_header_lines - 1) + text =3D dim->se->se_header; + } else { + text =3D dim->header.line[line].text; + + if (span) { + if (*span) { + (*span)--; + return 0; + } + + *span =3D dim->header.line[line].span; + } + } + + if (text =3D=3D NULL) + text =3D ""; + + return scnprintf(hpp->buf, hpp->size, "%*s", width, text); +} + +/* + * Return the estimated total cycles for a c2c_hist_entry + * (rmt_hitm + lcl_hitm + rmt_peer + lcl_peer + other loads). + */ +static u64 c2c_hist_entry__cycles(struct c2c_hist_entry *c2c_he) +{ + struct compute_stats *cs =3D &c2c_he->cstats; + double cycles =3D 0; + + /* + * compute_stats() in builtin-c2c.c routes each load sample into exactly + * one cstats bucket (rmt_hitm, lcl_hitm, rmt_peer, lcl_peer or plain + * load), so each bucket's cycle total is its mean times its own sample + * count. Summing the per-bucket totals avoids both dropping peer-snoop + * cycles and double counting a sample that carries several data-source + * flags (e.g. Arm SPE sets HITM and PEER on the same load), which would + * happen if the mean were multiplied by the non-exclusive stats counts. + */ + cycles +=3D avg_stats(&cs->rmt_hitm) * cs->rmt_hitm.n; + cycles +=3D avg_stats(&cs->lcl_hitm) * cs->lcl_hitm.n; + cycles +=3D avg_stats(&cs->rmt_peer) * cs->rmt_peer.n; + cycles +=3D avg_stats(&cs->lcl_peer) * cs->lcl_peer.n; + cycles +=3D avg_stats(&cs->load) * cs->load.n; + + return (u64)cycles; +} + +/* Sum c2c_hist_entry__cycles() across all level-1 entries. */ +static u64 __maybe_unused c2c_ext__total_cycles(void) +{ + struct rb_node *nd; + u64 total =3D 0; + + for (nd =3D rb_first_cached(&c2c_ext.function_hists.hists.entries); nd; + nd =3D rb_next(nd)) { + struct hist_entry *he =3D rb_entry(nd, struct hist_entry, rb_node); + struct c2c_hist_entry *c2c_he =3D container_of(he, struct c2c_hist_entry= , he); + + total +=3D c2c_hist_entry__cycles(c2c_he); + } + return total; +} + +/* + * Sum of the writer store counts under a level-1 function or level-2 writ= ing + * function. Read from the cache populated by the hierarchy builder, so th= is is + * O(1) and safe to call from the sort comparator. + */ +static u64 hist_entry__child_stores(struct hist_entry *he) +{ + struct c2c_hist_entry *c2c_he =3D container_of(he, struct c2c_hist_entry,= he); + + return c2c_he->child_stores; +} + +/* + * Store count shown in the column: the level-3 cacheline leaves show the = store + * count on that line; the level-1 function and level-2 writing function s= how + * the sum of their writer descendants. + */ +static u64 hist_entry__displayed_stores(struct hist_entry *he) +{ + struct c2c_hist_entry *c2c_he =3D container_of(he, struct c2c_hist_entry,= he); + + return hist_entry__is_cacheline(he) ? (u64)c2c_he->stats.store : + hist_entry__child_stores(he); +} + +static int +total_stores_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hist_entry *he) +{ + int width =3D c2c_width(fmt, hpp, he->hists); + u64 total =3D hist_entry__displayed_stores(he); + + return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, total); +} + +/* + * symbol_view_entry - Render the unified, indented identity column. + * + * All three levels share this single column so the hierarchy reads top-do= wn + * with progressive indentation, like the normal report hierarchy view. It= is + * a pure function view -- no code addresses: + * L1 read-side function: "- [k] cpupri_set" + * L2 writing function: " - [k] pull_rt_task" + * L3 shared cacheline: " 0xff2d0082809da080" + */ +static int +symbol_view_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hist_entry *he) +{ + int width =3D c2c_width(fmt, hpp, he->hists); + int text_width; + int ret; + char folded_sign; + + ret =3D hist_entry__indent(he, hpp->buf, hpp->size); + + folded_sign =3D he->has_children ? (he->unfolded ? '-' : '+') : ' '; + ret +=3D scnprintf(hpp->buf + ret, hpp->size - ret, "%c ", folded_sign); + + text_width =3D width - ret; + if (text_width <=3D 0) + return ret; + + if (hist_entry__is_cacheline(he)) { + /* Level 3: the shared cacheline address. */ + u64 addr =3D he->mem_info ? + cl_address(mem_info__daddr(he->mem_info)->addr, chk_double_cl) : 0; + char symbuf[32]; + + scnprintf(symbuf, sizeof(symbuf), "0x%" PRIx64, addr); + ret +=3D scnprintf(hpp->buf + ret, hpp->size - ret, "%-*.*s", + text_width, text_width, symbuf); + } else { + /* Level 1 and level 2 are both functions. */ + size_t cell_size =3D min_t(size_t, hpp->size - ret, text_width + 1); + int len; + + len =3D sort_sym.se_snprintf(he, hpp->buf + ret, cell_size, + text_width); + ret +=3D len; + if (len < text_width) + ret +=3D scnprintf(hpp->buf + ret, hpp->size - ret, "%*s", + text_width - len, ""); + } + + return ret; +} + +/* + * cycles_percent_entry - Render cycles percentage column + */ +static int +cycles_percent_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hist_entry *he) +{ + struct c2c_hist_entry *c2c_he; + int width =3D c2c_width(fmt, hpp, he->hists); + u64 fn_cycles, total_cycles; + char folded_sign; + double pct; + int ret, pct_width; + + /* Hide Cycles Percent for child functions and cachelines. */ + if (he->parent_he) + return scnprintf(hpp->buf, hpp->size, "%*s", width, ""); + + c2c_he =3D container_of(he, struct c2c_hist_entry, he); + fn_cycles =3D c2c_hist_entry__cycles(c2c_he); + /* Populated by build_function_view_hierarchy() once the L1 tree is built= . */ + total_cycles =3D c2c_ext.total_cycles; + pct =3D total_cycles > 0 ? (double)fn_cycles / total_cycles * 100.0 : 0.0; + + /* Add folded sign only for level-1 entries */ + folded_sign =3D he->has_children ? (he->unfolded ? '-' : '+') : ' '; + ret =3D scnprintf(hpp->buf, hpp->size, "%c ", folded_sign); + + pct_width =3D width - ret; + if (pct_width <=3D 0) + return ret; + ret +=3D scnprintf(hpp->buf + ret, hpp->size - ret, "%*.2f%%", pct_width = - 1, pct); + return ret; +} + +/* + * cycles_percent_cmp - Comparison function for cycles percentage sorting + */ +static int64_t +cycles_percent_cmp(struct perf_hpp_fmt *fmt __maybe_unused, + struct hist_entry *left, struct hist_entry *right) +{ + struct c2c_hist_entry *c2c_left =3D container_of(left, struct c2c_hist_en= try, he); + struct c2c_hist_entry *c2c_right =3D container_of(right, struct c2c_hist_= entry, he); + u64 cycles_left, cycles_right; + + /* Cycles Percent is only shown for level-1 entries; others compare equal= . */ + if (left->parent_he || right->parent_he) + return 0; + + cycles_left =3D c2c_hist_entry__cycles(c2c_left); + cycles_right =3D c2c_hist_entry__cycles(c2c_right); + + return (cycles_left > cycles_right) - (cycles_left < cycles_right); +} + +/* + * total_stores_cmp - Comparison function for total stores sorting + */ +static int64_t +total_stores_cmp(struct perf_hpp_fmt *fmt __maybe_unused, + struct hist_entry *left, struct hist_entry *right) +{ + u64 left_store =3D hist_entry__displayed_stores(left); + u64 right_store =3D hist_entry__displayed_stores(right); + + return (left_store > right_store) - (left_store < right_store); +} + +/* + * Function view dimensions + */ +static struct c2c_dimension dim_cycles_percent =3D { + .header =3D HEADER_BOTH("Cycles", "%"), + .name =3D "cycles_percent", + .cmp =3D cycles_percent_cmp, + .entry =3D cycles_percent_entry, + .width =3D 9, +}; + +static struct c2c_dimension dim_total_stores =3D { + .header =3D HEADER_BOTH("Store", "count"), + .name =3D "total_stores", + .cmp =3D total_stores_cmp, + .entry =3D total_stores_entry, + .width =3D 7, +}; + +static struct c2c_dimension dim_symbol_view =3D { + .header =3D HEADER_LOW("Function / Contending function / Cacheline"), + .name =3D "symbol_view", + .se =3D &sort_sym, + .entry =3D symbol_view_entry, + .width =3D SYMBOL_WIDTH, +}; + +static struct c2c_dimension *function_view_dimensions[] __maybe_unused =3D= { + &dim_cycles_percent, + &dim_total_stores, + &dim_symbol_view, + NULL, +}; + int perf_c2c__browse_function_view(struct hists *hists __maybe_unused) { ui__warning("C2C function view is not implemented yet.\n"); --=20 2.52.0 From nobody Fri Jul 24 21:52:58 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9DE253BED4F; Fri, 24 Jul 2026 09:52:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886743; cv=none; b=rTOoa4cQBYDd6ntpreH5dkJ9M3PK2XAIXkmvB7IlbmSrvIZTCZHijFJo1MLT28ACJbYoNxhnLNL57B9M4EZg+bySv91Ek6aWmeSX0263v74IyTb+hMAlKyWqhA3QvoyajLy3UJD6G4MYJ4yW7mXxUejaUw+s73/WVULt40KCQT4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886743; c=relaxed/simple; bh=f6utw0rPQNMTVIeWHt/WQOIxePqcSQ70euVppHcb5WQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Pbs6JovUJBR2AQl/l27Lj5C4wZDpeZjF4O2T+SddXDlreWT4VnnZFkm2ruTDg96pvuZSGZvtqw/YQ2isqT21CdM57jpPZA4VkbyIZfue1z7W2mjFdpxjokJ6bbd+w6yQI/IyR5bmTwlpO1pNAH0GDIQYC+N4DKesJMqJvABzBYA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=nkgy2+HH; arc=none smtp.client-ip=198.175.65.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="nkgy2+HH" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1784886741; x=1816422741; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=f6utw0rPQNMTVIeWHt/WQOIxePqcSQ70euVppHcb5WQ=; b=nkgy2+HHlBtMigSKVKYR473eH1iZFi9bzEvwBQwqnQs6oye49gzYSgzq TTGTVgvJlhaMnEE8vzpogntIOVFbVajUi5vWHNwk4MmmIdAgWLe0xvVn5 H41JTEVFXHodEXttxCqt3Q0HGqa3/8eXSprNqs4lcY6qw+x6pSptEhPxB 0KnMFgVg7yB3hpmD2XoVqzPHGqF0v9LNAaHv9WGh1rFLyMoAkqxW/O3RJ KC2VBNlmvty6T4vZJezLUPIqBgHHUsosNbo1WwHp2ojyKGAwzZ4vrRze8 zKiKSwt1SfXPdhvEwLZeH5b9zUWrZBRr+ZsosuCVneVGzvH2LxeOCbkDL w==; X-CSE-ConnectionGUID: gXMZ7GTDRiaB5sDchj1uZQ== X-CSE-MsgGUID: aJ0EcCNXR8W3q5+0+gfiVg== X-IronPort-AV: E=McAfee;i="6800,10657,11854"; a="95907611" X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="95907611" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Jul 2026 02:52:21 -0700 X-CSE-ConnectionGUID: 9nKrn4+xQqagViz96s990g== X-CSE-MsgGUID: hl3vLYB4St6Y5BgFTUB5dg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="256018461" Received: from linux-pnp-gnr-1.sh.intel.com ([10.239.83.186]) by fmviesa008.fm.intel.com with ESMTP; 24 Jul 2026 02:52:12 -0700 From: Jiebin Sun To: Namhyung Kim , acme@kernel.org, mingo@redhat.com, peterz@infradead.org Cc: adrian.hunter@intel.com, alexander.shishkin@linux.intel.com, irogers@google.com, james.clark@linaro.org, jolsa@kernel.org, mark.rutland@arm.com, dapeng1.mi@linux.intel.com, thomas.falcon@intel.com, tianyou.li@intel.com, wangyang.guo@intel.com, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Jiebin Sun Subject: [PATCH v4 4/9] perf c2c: add HPP list parsing for function view columns Date: Fri, 24 Jul 2026 17:58:37 +0800 Message-ID: <20260724095842.995920-5-jiebin.sun@intel.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260724095842.995920-1-jiebin.sun@intel.com> References: <20260717020530.1645123-1-jiebin.sun@intel.com> <20260724095842.995920-1-jiebin.sun@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the parsing that turns an output/sort column string into the function view's hpp_list: the dimension lookup, the sort-entry comparator wrappers, the c2c_fmt allocation, and the init/reinit entry points used by the hierarchy builder. Make perf_hpp__setup_output_field() register appended sort keys on the hpp_list passed by its caller. It previously used the global-list wrapper, which left a local list without output fields and linked its formats into the global perf_hpp_list instead. Signed-off-by: Jiebin Sun Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Reviewed-by: Tianyou Li Reviewed-by: Wangyang Guo --- tools/perf/ui/browsers/c2c-function.c | 268 +++++++++++++++++++++++++- 1 file changed, 266 insertions(+), 2 deletions(-) diff --git a/tools/perf/ui/browsers/c2c-function.c b/tools/perf/ui/browsers= /c2c-function.c index 4eda97894d1d..21e156bbbef3 100644 --- a/tools/perf/ui/browsers/c2c-function.c +++ b/tools/perf/ui/browsers/c2c-function.c @@ -138,7 +138,7 @@ static int c2c_width(struct perf_hpp_fmt *fmt, dim->width; } =20 -static int __maybe_unused c2c_header(struct perf_hpp_fmt *fmt, struct perf= _hpp *hpp, +static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hists *hists, int line, int *span) { struct c2c_fmt *c2c_fmt; @@ -400,13 +400,277 @@ static struct c2c_dimension dim_symbol_view =3D { .width =3D SYMBOL_WIDTH, }; =20 -static struct c2c_dimension *function_view_dimensions[] __maybe_unused =3D= { +static struct c2c_dimension *function_view_dimensions[] =3D { &dim_cycles_percent, &dim_total_stores, &dim_symbol_view, NULL, }; =20 +static struct c2c_dimension *get_function_dimension(const char *name) +{ + unsigned int i; + + for (i =3D 0; function_view_dimensions[i]; i++) { + struct c2c_dimension *dim =3D function_view_dimensions[i]; + + if (!strcmp(dim->name, name)) + return dim; + } + + return NULL; +} + +/* Wrappers so sort_entry-backed dimensions sort/collapse via their se. */ +static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt, + struct hist_entry *a, struct hist_entry *b) +{ + struct c2c_fmt *c2c_fmt =3D container_of(fmt, struct c2c_fmt, fmt); + struct c2c_dimension *dim =3D c2c_fmt->dim; + + return dim->se->se_cmp(a, b); +} + +static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt, + struct hist_entry *a, struct hist_entry *b) +{ + struct c2c_fmt *c2c_fmt =3D container_of(fmt, struct c2c_fmt, fmt); + struct c2c_dimension *dim =3D c2c_fmt->dim; + int64_t (*collapse_fn)(struct hist_entry *a, struct hist_entry *b); + + collapse_fn =3D dim->se->se_collapse ?: dim->se->se_cmp; + return collapse_fn(a, b); +} + +static int64_t c2c_se_sort(struct perf_hpp_fmt *fmt, + struct hist_entry *a, struct hist_entry *b) +{ + struct c2c_fmt *c2c_fmt =3D container_of(fmt, struct c2c_fmt, fmt); + struct c2c_dimension *dim =3D c2c_fmt->dim; + int64_t (*sort_fn)(struct hist_entry *a, struct hist_entry *b); + + sort_fn =3D dim->se->se_sort ?: dim->se->se_cmp; + return sort_fn(a, b); +} + +/* + * Build the c2c_fmt for @name. Returns: + * 0 and *fmtp set on success; + * -ENOENT and *fmtp =3D NULL if @name is not a function-view dimensi= on + * (caller should fall back to the generic f= ield); + * -ENOMEM if allocation failed (distinct from -ENOE= NT so + * the caller does not misreport it as an + * "invalid field"). + */ +static int get_function_format(const char *name, struct c2c_fmt **fmtp) +{ + struct c2c_dimension *dim =3D get_function_dimension(name); + struct c2c_fmt *c2c_fmt; + struct perf_hpp_fmt *fmt; + + *fmtp =3D NULL; + + if (!dim) + return -ENOENT; + + c2c_fmt =3D zalloc(sizeof(*c2c_fmt)); + if (!c2c_fmt) + return -ENOMEM; + + fmt =3D &c2c_fmt->fmt; + + c2c_fmt->dim =3D dim; + INIT_LIST_HEAD(&fmt->list); + INIT_LIST_HEAD(&fmt->sort_list); + + fmt->cmp =3D dim->se ? c2c_se_cmp : dim->cmp; + fmt->sort =3D dim->se ? c2c_se_sort : dim->cmp; + fmt->color =3D dim->color; + fmt->entry =3D dim->entry; + fmt->header =3D c2c_header; + fmt->width =3D c2c_width; + fmt->collapse =3D dim->se ? c2c_se_collapse : dim->cmp; + fmt->equal =3D c2c_fmt_equal; + fmt->free =3D c2c_fmt_free; + + *fmtp =3D c2c_fmt; + return 0; +} + +static int +c2c_function_hists__init_output(struct perf_hpp_list *hpp_list, char *name, + struct perf_env *env __maybe_unused) +{ + struct c2c_fmt *c2c_fmt; + int ret; + + ret =3D get_function_format(name, &c2c_fmt); + if (ret =3D=3D -ENOMEM) + return ret; + /* The function view only accepts its own dimensions. */ + if (ret =3D=3D -ENOENT) + return -EINVAL; + + /* + * Mark symbol-backed columns so hists__has(hists, sym) is correct. + * Only dim_symbol_view carries a sort_entry (.se); the function + * view's field strings are fixed and always include symbol_view, so + * this single check is sufficient (unlike the user-configurable + * cacheline view, which must also test dim_iaddr). + */ + if (c2c_fmt->dim->se =3D=3D &sort_sym) + hpp_list->sym =3D 1; + + perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt); + return 0; +} + +static int +c2c_function_hists__init_sort(struct perf_hpp_list *hpp_list, char *name, + struct perf_env *env __maybe_unused) +{ + struct c2c_fmt *c2c_fmt; + int ret; + + ret =3D get_function_format(name, &c2c_fmt); + if (ret =3D=3D -ENOMEM) + return ret; + /* The function view only accepts its own dimensions. */ + if (ret =3D=3D -ENOENT) + return -EINVAL; + + /* Mark symbol-backed sort keys so hists__has(hists, sym) is correct. */ + if (c2c_fmt->dim->se =3D=3D &sort_sym) + hpp_list->sym =3D 1; + + perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt); + return 0; +} + +typedef int (*hpp_list_add_fn)(struct perf_hpp_list *hpp_list, char *name, + struct perf_env *env); + +static int function_hpp_list__add_tokens(struct perf_hpp_list *hpp_list, c= har *list, + struct perf_env *env, hpp_list_add_fn add) +{ + char *tok, *tmp; + int ret; + + if (!list) + return 0; + + for (tok =3D strtok_r(list, ", ", &tmp); tok; tok =3D strtok_r(NULL, ", "= , &tmp)) { + ret =3D add(hpp_list, tok, env); + if (ret) { + if (ret =3D=3D -EINVAL || ret =3D=3D -ESRCH) + pr_err("Invalid c2c function-view field: %s\n", tok); + return ret; + } + } + return 0; +} + +/* + * Append the function view's sort keys to its own output fields, mirroring + * perf_hpp__setup_output_field() but on the local @list. The shared helper + * registers onto the global perf_hpp_list, which would leave this local l= ist + * without output columns, so the function view keeps its own copy here. + */ +static void c2c_function_hists__setup_output_field(struct perf_hpp_list *l= ist) +{ + struct perf_hpp_fmt *fmt; + + perf_hpp_list__for_each_sort_list(list, fmt) { + struct perf_hpp_fmt *pos; + + if (!fmt->entry && !fmt->color) + continue; + + perf_hpp_list__for_each_format(list, pos) { + if (c2c_fmt_equal(fmt, pos)) + goto next; + } + + perf_hpp_list__column_register(list, fmt); +next: + continue; + } +} + +static int +function_hpp_list__parse(struct perf_hpp_list *hpp_list, + const char *output_str, + const char *sort_str, + struct perf_env *env) +{ + char *output =3D output_str ? strdup(output_str) : NULL; + char *sort =3D sort_str ? strdup(sort_str) : NULL; + int ret =3D 0; + + if ((output_str && !output) || (sort_str && !sort)) { + ret =3D -ENOMEM; + goto out; + } + + ret =3D function_hpp_list__add_tokens(hpp_list, output, env, + c2c_function_hists__init_output); + if (ret) + goto out; + + ret =3D function_hpp_list__add_tokens(hpp_list, sort, env, + c2c_function_hists__init_sort); + if (ret) + goto out; + + c2c_function_hists__setup_output_field(hpp_list); +out: + if (ret) + perf_hpp__reset_output_field(hpp_list); + free(output); + free(sort); + return ret; +} + +static int __maybe_unused +c2c_function_hists__init(struct c2c_hists *hists, + const char *sort, + int nr_header_lines, + struct perf_env *env) +{ + __hists__init(&hists->hists, &hists->list); + + perf_hpp_list__init(&hists->list); + + hists->list.nr_header_lines =3D nr_header_lines; + + return function_hpp_list__parse(&hists->list, /*output=3D*/NULL, sort, en= v); +} + +static int __maybe_unused +c2c_function_hists__reinit(struct c2c_hists *c2c_hists, + const char *output, + const char *sort, + struct perf_env *env) +{ + int nr_header_lines =3D c2c_hists->list.nr_header_lines; + + perf_hpp__reset_output_field(&c2c_hists->list); + INIT_LIST_HEAD(&c2c_hists->list.sorts); + + /* Clear stale state flags so a different output/sort set starts fresh. */ + c2c_hists->list.need_collapse =3D 0; + c2c_hists->list.parent =3D 0; + c2c_hists->list.sym =3D 0; + c2c_hists->list.dso =3D 0; + c2c_hists->list.socket =3D 0; + c2c_hists->list.thread =3D 0; + c2c_hists->list.comm =3D 0; + c2c_hists->list.comm_nodigit =3D 0; + c2c_hists->list.nr_header_lines =3D nr_header_lines; + + return function_hpp_list__parse(&c2c_hists->list, output, sort, env); +} + int perf_c2c__browse_function_view(struct hists *hists __maybe_unused) { ui__warning("C2C function view is not implemented yet.\n"); --=20 2.52.0 From nobody Fri Jul 24 21:52:58 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 309BA3FADE0; Fri, 24 Jul 2026 09:52:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886744; cv=none; b=U1HKHalEPDnFz+I3ldrWSLdIQgRDlj3EgAagVA+GzlSlTivGslwssxQKRGWQLyGky5FBEEkO5AfQhxS2CGjouebmMWqnuWSYlmc+Wa6gVwSbVfJvXbdeHvVmzO2IQWrk/inbLr3sGDwNG3yDGkFjCSY7QtoldiHe7vciad97dvQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886744; c=relaxed/simple; bh=CIDMAFHYlr59s82yhXhANsAdtCPzJ8b3nMa11+26oFo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jReUsbwMcfgUGlQ1PGxHah0L/g5DthD8Qu3nwWXIDLP3wQqsvatqAtwvm0d2Z2NWTc2eubUuyGnqJAsPNsvcU4sEzyBFF9DH16JUlfVwBTObCqSGJz7aus68wjvXFbUAewnF5wArR/2aN3ma3QtoXuiGjW9hx19TM39LqsWqOZg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=j5E1fET4; arc=none smtp.client-ip=198.175.65.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="j5E1fET4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1784886744; x=1816422744; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=CIDMAFHYlr59s82yhXhANsAdtCPzJ8b3nMa11+26oFo=; b=j5E1fET4C/E4mGwrmwEU/5kHYWarDu0tsfDL+9ZedfvyKKPAV6VZ15ck aXZL0fg9x8THxp+Un0HN65Yz5Q4uxY/h0HwmSZa3CkO2XBjMyqvaO/mwd KtNKo0K/nQNV+A1H3xYBw/DwKT6x+RxUs3CCLuGK6KlJZp2JcMMzCWwBl KHgzChQNCkElMTGMyPz6kF7henHX95ah6Ewl/FrZf7li2uQ4MuzPQRj2y bK9jPrbEWwJ5hXvPIgkrUc4xuX2KrbOnG44SH485WonVN0UGFKUmwQH7I wNcW4HRg2ARhoX0ZgjMN2uGWTNFhxALgAUp3kOv3CVxpdkwIcUTrpvTDa A==; X-CSE-ConnectionGUID: BU+PJpNES8C05ezlve+GWQ== X-CSE-MsgGUID: guXU3l0vS9qs8btTT4hUug== X-IronPort-AV: E=McAfee;i="6800,10657,11854"; a="95907617" X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="95907617" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Jul 2026 02:52:23 -0700 X-CSE-ConnectionGUID: 8dXMjCEuQYmJcM02jtVTog== X-CSE-MsgGUID: v+rIlqF7T9ir9Gbif1LmLg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="256018475" Received: from linux-pnp-gnr-1.sh.intel.com ([10.239.83.186]) by fmviesa008.fm.intel.com with ESMTP; 24 Jul 2026 02:52:18 -0700 From: Jiebin Sun To: Namhyung Kim , acme@kernel.org, mingo@redhat.com, peterz@infradead.org Cc: adrian.hunter@intel.com, alexander.shishkin@linux.intel.com, irogers@google.com, james.clark@linaro.org, jolsa@kernel.org, mark.rutland@arm.com, dapeng1.mi@linux.intel.com, thomas.falcon@intel.com, tianyou.li@intel.com, wangyang.guo@intel.com, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Jiebin Sun Subject: [PATCH v4 5/9] perf c2c: add function view stats merge and memory management Date: Fri, 24 Jul 2026 17:58:38 +0800 Message-ID: <20260724095842.995920-6-jiebin.sun@intel.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260724095842.995920-1-jiebin.sun@intel.com> References: <20260717020530.1645123-1-jiebin.sun@intel.com> <20260724095842.995920-1-jiebin.sun@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the per-entry stats/cstats aggregation helpers, the borrowed-reference teardown for hierarchy children (which frees only the owned resources and never the borrowed thread/ms), and the pruning of cachelines/functions with no contention. These are used by the entry-creation and builder patches that follow and are __maybe_unused until then. Signed-off-by: Jiebin Sun Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Reviewed-by: Tianyou Li Reviewed-by: Wangyang Guo --- tools/perf/ui/browsers/c2c-function.c | 196 ++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/tools/perf/ui/browsers/c2c-function.c b/tools/perf/ui/browsers= /c2c-function.c index 21e156bbbef3..1bddc6bdc073 100644 --- a/tools/perf/ui/browsers/c2c-function.c +++ b/tools/perf/ui/browsers/c2c-function.c @@ -671,6 +671,202 @@ c2c_function_hists__reinit(struct c2c_hists *c2c_hist= s, return function_hpp_list__parse(&c2c_hists->list, output, sort, env); } =20 +static void c2c_stats_merge(struct stats *dest, const struct stats *src) +{ + double delta; + + if (src->n =3D=3D 0) + return; + + if (dest->n =3D=3D 0) { + *dest =3D *src; + return; + } + + delta =3D src->mean - dest->mean; + dest->M2 +=3D src->M2 + delta * delta * dest->n * src->n / (dest->n + src= ->n); + dest->mean =3D (dest->mean * dest->n + src->mean * src->n) / (dest->n + s= rc->n); + dest->n +=3D src->n; + + /* Update min/max */ + if (src->max > dest->max) + dest->max =3D src->max; + if (src->min < dest->min) + dest->min =3D src->min; +} + +/* Merge compute_stats during function aggregation. */ +static void __maybe_unused c2c_add_cstats(struct compute_stats *dest, + const struct compute_stats *src) +{ + c2c_stats_merge(&dest->rmt_hitm, &src->rmt_hitm); + c2c_stats_merge(&dest->lcl_hitm, &src->lcl_hitm); + c2c_stats_merge(&dest->rmt_peer, &src->rmt_peer); + c2c_stats_merge(&dest->lcl_peer, &src->lcl_peer); + c2c_stats_merge(&dest->load, &src->load); +} + +static bool __maybe_unused hist_entry__add_c2c_stats(struct hist_entry *he, + const struct c2c_stats *stats) +{ + u64 nr_events =3D c2c_hitm_count(stats) + stats->rmt_peer + stats->lcl_pe= er; + u64 weight1 =3D c2c_hitm_count(stats); + + he->stat.nr_events +=3D nr_events; + he->stat.period +=3D nr_events; + he->stat.weight1 +=3D weight1; + + if (!symbol_conf.cumulate_callchain) + return true; + + if (!he->stat_acc) { + he->stat_acc =3D calloc(1, sizeof(struct he_stat)); + if (!he->stat_acc) + return false; + } + + he->stat_acc->nr_events +=3D nr_events; + he->stat_acc->period +=3D nr_events; + he->stat_acc->weight1 +=3D weight1; + + return true; +} + +static void c2c_he__free_hierarchy(struct hist_entry *he); + +/* + * Free a function-view histogram entry (hist_entry_ops::free). + */ +static void __maybe_unused c2c_function_he_free(void *ptr) +{ + struct hist_entry *he =3D ptr; + struct c2c_hist_entry *c2c_he; + + c2c_he =3D container_of(he, struct c2c_hist_entry, he); + + if (c2c_he->hists) { + perf_hpp__reset_output_field(&c2c_he->hists->list); + hists__delete_all_entries(&c2c_he->hists->hists); + zfree(&c2c_he->hists); + } + + c2c_he__free_hierarchy(he); + + zfree(&c2c_he->nodeset); + zfree(&c2c_he->cpuset); + zfree(&c2c_he->nodestr); + zfree(&c2c_he->node_stats); + + free(c2c_he); +} + +/* + * Free all child entries under @he, recursively (hroot_out sub-tree). + * + * Children are built by c2c_child_entry__alloc(), which BORROWS thread and + * ms (plain copy, no thread__get()/map__get()) and OWNS only mem_info (a + * clone), stat_acc and the c2c-specific fields (hists, cpuset, nodeset, + * nodestr, node_stats). We therefore must NOT call hist_entry__delete() + * here: it would thread__zput()/map_symbol__exit() the borrowed refs and + * underflow their refcounts. Free exactly the owned resources instead. + */ +/* + * Free one child entry @child_he and everything below it. Frees exactly t= he + * resources c2c_child_entry__alloc() owns (mem_info clone, stat_acc, hist= s and + * the c2c-specific arrays); the borrowed thread/ms are left untouched. + */ +static void c2c_child_he__free(struct hist_entry *child_he) +{ + struct c2c_hist_entry *child_c2c =3D container_of(child_he, struct c2c_hi= st_entry, he); + + if (child_he->stat_acc) + zfree(&child_he->stat_acc); + + if (child_he->mem_info) + mem_info__put(child_he->mem_info); + + if (child_c2c->hists) { + perf_hpp__reset_output_field(&child_c2c->hists->list); + hists__delete_all_entries(&child_c2c->hists->hists); + zfree(&child_c2c->hists); + } + + zfree(&child_c2c->cpuset); + zfree(&child_c2c->nodeset); + zfree(&child_c2c->nodestr); + zfree(&child_c2c->node_stats); + + c2c_he__free_hierarchy(child_he); + + free(child_c2c); +} + +static void c2c_he__free_hierarchy(struct hist_entry *he) +{ + struct rb_node *nd; + struct hist_entry *child_he; + + /* + * Leaf entries alias hroot_out with sorted_chain (callchains) in a + * union, so they have no child hierarchy to free here. + */ + if (he->leaf) + return; + + if (RB_EMPTY_ROOT(&he->hroot_out.rb_root)) + return; + + nd =3D rb_first_cached(&he->hroot_out); + while (nd) { + struct rb_node *next =3D rb_next(nd); + + child_he =3D rb_entry(nd, struct hist_entry, rb_node); + rb_erase_cached(&child_he->rb_node, &he->hroot_out); + c2c_child_he__free(child_he); + + nd =3D next; + } + + /* All children erased; clear the tree (and its cached leftmost). */ + he->hroot_out =3D RB_ROOT_CACHED; +} + +/* + * Drop level-2 writing functions that carry no stores (child_stores =3D= =3D 0) or + * no cacheline children. Writers are only added when they store into a sh= ared + * line, so this is mainly a safety net. Returns the number of surviving + * writers. + */ +static int __maybe_unused c2c_he__prune_empty_writers(struct hist_entry *l= 1_he) +{ + struct rb_node *nd; + int surviving =3D 0; + + if (!l1_he->has_children) + return 0; + + nd =3D rb_first_cached(&l1_he->hroot_out); + while (nd) { + struct rb_node *next =3D rb_next(nd); + struct hist_entry *l2_he =3D rb_entry(nd, struct hist_entry, rb_node); + + if (l2_he->has_children && hist_entry__child_stores(l2_he) > 0) + surviving++; + else { + rb_erase_cached(&l2_he->rb_node, &l1_he->hroot_out); + c2c_child_he__free(l2_he); + } + nd =3D next; + } + + if (!surviving) { + l1_he->hroot_out =3D RB_ROOT_CACHED; + l1_he->has_children =3D false; + l1_he->unfolded =3D false; + } + return surviving; +} + int perf_c2c__browse_function_view(struct hists *hists __maybe_unused) { ui__warning("C2C function view is not implemented yet.\n"); --=20 2.52.0 From nobody Fri Jul 24 21:52:58 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 709FE41F7ED; Fri, 24 Jul 2026 09:52:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886749; cv=none; b=oKk4/VlMzYhTIlXsAXZlZ1NfQEaGxa7krAJe/F/Aj1jdtAymyRr/kzEldjdg6OK4XXPpF7CZKUPVdQKi49e5eose8g06bYyYeHaJgPzBSN/35Cs7ue4Vj2ulX9hWKTgL+gFNSB3BtacYMRWpG+3wcDJ7Gc7PDOMq3CfadtRaqyk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886749; c=relaxed/simple; bh=zbl8tJiqEWlT7aAjeU6dAGQUmDe2DYiQqmNxcsTqKHs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=V4EjZDBPdQNfajKOY/mUMaL0XfyLLJm1x3jPT59xbwgdrVwQZlR8Y13H41Jb7+frxB52CP7ooqf5LJZvhNGBgFocz4di5YohgG01iUIp0LiJVQnbnqlgVRaNkhDEk092n8nO7qK/+B2mLevnqPbIL9mNFM8icuUvXiCt9b5Y8ik= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=l1NJc6IQ; arc=none smtp.client-ip=198.175.65.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="l1NJc6IQ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1784886748; x=1816422748; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=zbl8tJiqEWlT7aAjeU6dAGQUmDe2DYiQqmNxcsTqKHs=; b=l1NJc6IQIK9BbqXTP7aEKC/nK6ff+QziscDxOsRq8mVw45S3AFBLdeZb FD4O+5xL5Vbfs+LzxZgUXIIob6V0aknawZDE79/dWLqk7XAXR0B04hgOd X496cOMqiOgxLIMQ2tTNz/qIUDC9FxHdWZ7zMur+Li8rr/u4lKJU4qS6U XBxi0jznLDbW+UQ26cFRIl36l+outxRcj/G71wFnLniEbzGhEFT9BHWis QlsWAkY9Xbt3DtzdCZS9Uqckxuf1A3UOrx8Pw+21Iw/HWhn0e3RBo3Hsn +yub6H6jKrJctLaBtMDIqU0NCcXo/wWRZumdd9JTy91VhCbHWwHRHGZ8E Q==; X-CSE-ConnectionGUID: XISEiz+kSgilL5XH6dDkkQ== X-CSE-MsgGUID: Obu2RZNWT5GIGJf5hAWAqg== X-IronPort-AV: E=McAfee;i="6800,10657,11854"; a="95907628" X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="95907628" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Jul 2026 02:52:27 -0700 X-CSE-ConnectionGUID: mLbydUx1Tt2MjfgDkfOrSw== X-CSE-MsgGUID: TV5jFBiMSdKoufhJaKJFEw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="256018480" Received: from linux-pnp-gnr-1.sh.intel.com ([10.239.83.186]) by fmviesa008.fm.intel.com with ESMTP; 24 Jul 2026 02:52:22 -0700 From: Jiebin Sun To: Namhyung Kim , acme@kernel.org, mingo@redhat.com, peterz@infradead.org Cc: adrian.hunter@intel.com, alexander.shishkin@linux.intel.com, irogers@google.com, james.clark@linaro.org, jolsa@kernel.org, mark.rutland@arm.com, dapeng1.mi@linux.intel.com, thomas.falcon@intel.com, tianyou.li@intel.com, wangyang.guo@intel.com, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Jiebin Sun Subject: [PATCH v4 6/9] perf c2c: add function view hierarchy entry creation Date: Fri, 24 Jul 2026 17:58:39 +0800 Message-ID: <20260724095842.995920-7-jiebin.sun@intel.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260724095842.995920-1-jiebin.sun@intel.com> References: <20260717020530.1645123-1-jiebin.sun@intel.com> <20260724095842.995920-1-jiebin.sun@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the entry-creation layer: the borrowed-reference child allocation and insertion, and the level-1/2/3 lookup-or-create functions keyed by function symbol (level 1 read-side, level 2 writer) and cacheline address (level 3). These are driven by the hierarchy builder in the next patch and are __maybe_unused until then. Signed-off-by: Jiebin Sun Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Reviewed-by: Tianyou Li Reviewed-by: Wangyang Guo --- tools/perf/ui/browsers/c2c-function.c | 277 +++++++++++++++++++++++++- 1 file changed, 271 insertions(+), 6 deletions(-) diff --git a/tools/perf/ui/browsers/c2c-function.c b/tools/perf/ui/browsers= /c2c-function.c index 1bddc6bdc073..9a7b1353e11c 100644 --- a/tools/perf/ui/browsers/c2c-function.c +++ b/tools/perf/ui/browsers/c2c-function.c @@ -29,6 +29,7 @@ #include "../../util/addr_location.h" #include "../../util/cacheline.h" #include "../../util/debug.h" +#include "../../util/dso.h" #include "../../util/hist.h" #include "../../util/map.h" #include "../../util/mem-events.h" @@ -57,12 +58,34 @@ static inline __maybe_unused u64 c2c_hitm_count(const s= truct c2c_stats *stats) return stats->tot_hitm; } =20 -static inline __maybe_unused bool symbol_name_equal(struct symbol *a, stru= ct symbol *b) +static int64_t c2c_function_cmp(const struct map_symbol *left, + const struct map_symbol *right) { - /* Two unknown symbols compare equal, matching cmp_null() in util/sort.c.= */ - if (!a || !b) - return a =3D=3D b; - return arch__compare_symbol_names(a->name, b->name) =3D=3D 0; + const struct dso *left_dso =3D left->map ? map__dso(left->map) : NULL; + const struct dso *right_dso =3D right->map ? map__dso(right->map) : NULL; + int ret; + + if (!left_dso || !right_dso) { + if (left_dso !=3D right_dso) + return left_dso ? 1 : -1; + } else { + /* + * Use the same DSO name as _sort__dso_cmp() (short name unless + * verbose), so this matches the DSO comparison the level-1 + * entries are deduplicated by; otherwise same-basename DSOs + * could be split or merged inconsistently across levels. + */ + const char *left_name =3D verbose > 0 ? + dso__long_name(left_dso) : dso__short_name(left_dso); + const char *right_name =3D verbose > 0 ? + dso__long_name(right_dso) : dso__short_name(right_dso); + + ret =3D strcmp(left_name, right_name); + if (ret) + return ret; + } + + return _sort__sym_cmp(left->sym, right->sym); } =20 static inline __maybe_unused u64 hist_entry__iaddr(struct hist_entry *he) @@ -737,7 +760,7 @@ static void c2c_he__free_hierarchy(struct hist_entry *h= e); /* * Free a function-view histogram entry (hist_entry_ops::free). */ -static void __maybe_unused c2c_function_he_free(void *ptr) +static void c2c_function_he_free(void *ptr) { struct hist_entry *he =3D ptr; struct c2c_hist_entry *c2c_he; @@ -867,6 +890,248 @@ static int __maybe_unused c2c_he__prune_empty_writers= (struct hist_entry *l1_he) return surviving; } =20 +/* Entry operations for function view */ +static struct hist_entry_ops c2c_function_entry_ops =3D { + .new =3D c2c_he_zalloc, + .free =3D c2c_function_he_free, +}; + +static struct c2c_hist_entry * +c2c_child_entry__alloc(struct hist_entry *parent_he, struct hist_entry *sr= c_he, + int depth, u64 ip) +{ + struct c2c_hist_entry *child_c2c; + struct hist_entry *child_he; + + /* Function-view children never own or display callchains. */ + child_he =3D c2c_he_zalloc(0); + if (!child_he) + return NULL; + + child_c2c =3D container_of(child_he, struct c2c_hist_entry, he); + memcpy(&child_he->ms, &src_he->ms, sizeof(struct map_symbol)); + + if (src_he->mem_info) { + child_he->mem_info =3D mem_info__clone(src_he->mem_info); + if (!child_he->mem_info) + goto out_free; + } + + child_he->thread =3D src_he->thread; + child_he->cpumode =3D src_he->cpumode; + child_he->cpu =3D src_he->cpu; + child_he->socket =3D src_he->socket; + child_he->level =3D src_he->level; + child_he->ip =3D ip; + + child_he->parent_he =3D parent_he; + child_he->depth =3D depth; + child_he->leaf =3D (depth >=3D 2); + child_he->hists =3D &c2c_ext.function_hists.hists; + child_he->filtered =3D false; + child_he->unfolded =3D false; + child_he->has_children =3D false; + child_he->has_no_entry =3D false; + child_he->nr_rows =3D 0; + child_he->row_offset =3D 0; + + memset(&child_he->stat, 0, sizeof(child_he->stat)); + child_he->hroot_in =3D RB_ROOT_CACHED; + child_he->hroot_out =3D RB_ROOT_CACHED; + INIT_LIST_HEAD(&child_he->pairs.node); + child_he->hpp_list =3D &c2c_ext.function_hists.list; + if (symbol_conf.cumulate_callchain) { + child_he->stat_acc =3D calloc(1, sizeof(struct he_stat)); + if (!child_he->stat_acc) + goto out_free; + } + + return child_c2c; + +out_free: + if (child_he->mem_info) + mem_info__put(child_he->mem_info); + zfree(&child_c2c->cpuset); + zfree(&child_c2c->nodeset); + zfree(&child_c2c->node_stats); + free(child_c2c); + return NULL; +} + +static void +c2c_child_entry__insert(struct hist_entry *parent_he, struct hist_entry *c= hild_he, + struct rb_node **p, struct rb_node *rb_parent, bool leftmost) +{ + rb_link_node(&child_he->rb_node, rb_parent, p); + rb_insert_color_cached(&child_he->rb_node, &parent_he->hroot_out, leftmos= t); + + parent_he->has_children =3D true; + parent_he->leaf =3D false; +} + +static __maybe_unused struct hist_entry * +c2c_function_hists__level1_entry(struct symbol *sym, + struct hist_entry *detail_he, + struct thread *synthetic_thread) +{ + struct addr_location al; + struct perf_sample sample =3D {}; + struct mem_info *mi; + struct hist_entry *he; + /* + * Key the level-1 entry by the function, not by a specific code + * address: use the symbol start so every instruction address inside + * the same function collapses into one entry. This makes level 1 a + * true "function view" rather than a per-code-address view. + */ + u64 sym_start =3D sym ? map__unmap_ip(detail_he->ms.map, sym->start) : de= tail_he->ip; + + mi =3D mem_info__new(); + if (mi) { + mem_info__iaddr(mi)->addr =3D sym_start; + /* mem_info__put() will map_symbol__exit() these, so take refs. */ + mem_info__iaddr(mi)->ms.thread =3D thread__get(detail_he->ms.thread); + mem_info__iaddr(mi)->ms.map =3D map__get(detail_he->ms.map); + mem_info__iaddr(mi)->ms.sym =3D sym; + mem_info__daddr(mi)->addr =3D 0; + } + + addr_location__init(&al); + al.thread =3D thread__get(synthetic_thread); + al.map =3D map__get(detail_he->ms.map); + al.sym =3D sym; + al.addr =3D sym_start; + al.level =3D detail_he->level; + al.cpumode =3D detail_he->cpumode; + al.cpu =3D 0; + al.socket =3D 0; + al.filtered =3D 0; + al.latency =3D 0; + + /* + * Synthetic sample: period/weight are placeholders only. The real + * c2c counters live in c2c_hist_entry::stats and are added via + * hist_entry__add_c2c_stats(); no function-view column or sort key + * reads he->stat.period/nr_events, so the +1 that __hists__add_entry() + * accrues on each dedup hit has no effect on what is displayed. + */ + sample.period =3D 1; + sample.weight =3D 1; + sample.ip =3D sym_start; + sample.pid =3D thread__pid(synthetic_thread); + sample.tid =3D thread__tid(synthetic_thread); + sample.cpu =3D 0; + + /* Add entry - histogram handles dedup */ + he =3D hists__add_entry_ops(&c2c_ext.function_hists.hists, + &c2c_function_entry_ops, + &al, NULL, NULL, mi, + NULL, &sample, true); + + addr_location__exit(&al); + if (mi) + mem_info__put(mi); + + if (he) + he->hpp_list =3D &c2c_ext.function_hists.list; + + return he; +} + +/* + * Level 2: another function that writes a cacheline the level-1 function + * reads, keyed by DSO and symbol so all code addresses and cachelines for= the + * same writer function aggregate into one row without merging identically + * named functions from different DSOs. + */ +static __maybe_unused struct c2c_hist_entry * +c2c_function_hists__level2_entry(struct c2c_hist_entry *level1_c2c, + struct symbol *sym, struct hist_entry *detail_he) +{ + struct hist_entry *level1_he =3D &level1_c2c->he; + struct rb_node **p =3D &level1_he->hroot_out.rb_root.rb_node; + struct rb_node *parent =3D NULL; + struct c2c_hist_entry *level2_c2c; + bool leftmost =3D true; + + while (*p) { + struct hist_entry *iter =3D rb_entry(*p, struct hist_entry, rb_node); + struct map_symbol key =3D detail_he->ms; + int64_t cmp; + + key.sym =3D sym; + parent =3D *p; + cmp =3D c2c_function_cmp(&key, &iter->ms); + + if (cmp < 0) { + p =3D &parent->rb_left; + } else if (cmp > 0) { + p =3D &parent->rb_right; + leftmost =3D false; + } else { + return container_of(iter, struct c2c_hist_entry, he); + } + } + + /* Key by the function symbol start so all code addresses collapse. */ + level2_c2c =3D c2c_child_entry__alloc(level1_he, detail_he, 1, + sym ? map__unmap_ip(detail_he->ms.map, sym->start) : + hist_entry__iaddr(detail_he)); + if (!level2_c2c) + return NULL; + + /* Key this level by the looked-up symbol, not detail_he's. */ + level2_c2c->he.ms.sym =3D sym; + if (level2_c2c->he.mem_info) + mem_info__iaddr(level2_c2c->he.mem_info)->ms.sym =3D sym; + + c2c_child_entry__insert(level1_he, &level2_c2c->he, p, parent, leftmost); + + return level2_c2c; +} + +/* Level 3: a specific cacheline where the L1/L2 functions contend (keyed = by cl_addr). */ +static __maybe_unused struct c2c_hist_entry * +c2c_function_hists__level3_entry(struct c2c_hist_entry *level2_c2c, u64 cl= _addr, + struct c2c_hist_entry *cacheline_src_he) +{ + struct hist_entry *level2_he =3D &level2_c2c->he; + struct rb_node **p =3D &level2_he->hroot_out.rb_root.rb_node; + struct rb_node *parent =3D NULL; + struct c2c_hist_entry *level3_c2c; + bool leftmost =3D true; + + while (*p) { + struct hist_entry *iter =3D rb_entry(*p, struct hist_entry, rb_node); + u64 iter_addr =3D 0; + + if (iter->mem_info) { + u64 daddr =3D mem_info__daddr(iter->mem_info)->addr; + + iter_addr =3D cl_address(daddr, chk_double_cl); + } + + parent =3D *p; + if (cl_addr < iter_addr) { + p =3D &parent->rb_left; + } else if (cl_addr > iter_addr) { + p =3D &parent->rb_right; + leftmost =3D false; + } else { + return container_of(iter, struct c2c_hist_entry, he); + } + } + + level3_c2c =3D c2c_child_entry__alloc(level2_he, &cacheline_src_he->he, 2, + hist_entry__iaddr(&cacheline_src_he->he)); + if (!level3_c2c) + return NULL; + + c2c_child_entry__insert(level2_he, &level3_c2c->he, p, parent, leftmost); + + return level3_c2c; +} + int perf_c2c__browse_function_view(struct hists *hists __maybe_unused) { ui__warning("C2C function view is not implemented yet.\n"); --=20 2.52.0 From nobody Fri Jul 24 21:52:58 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4407E41F7EE; Fri, 24 Jul 2026 09:52:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886753; cv=none; b=e+ZLh7gTsEjfgSOPLEXNX6F5Ptgu6j74NKnVGH7uCjniLsoLf439VcW+EqqSEYlo6Ah9JG9nXZpZwV5QfeYUQzV4vb8ZjrFQYjj3L220FF7BhE+xZ+m+gT6WFHqNv/3urPhs1uS5k6k1FwEfw0QN349KVQXx6IHc+8EqKhda1wM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886753; c=relaxed/simple; bh=/4qWagf48+7Q8af5cvBaiK/qrm5KNmMh4ywBJuimSoI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IQDETjxgBTHCvkV57uNblypNF4jJWpgR2SNJE9MbABgDxYruHnSrGZNLJ4xuDiMYZnquNcp6M/g9oX7fSO2szQyoaC1aC92YWX+/rIfVLOeelmIktAWfyWeldTcmQcQJXzNywr6wx+KR6QrSTQaMo0M8AfncBtOHhkTFH2QmR0Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=ZDQRMT5Q; arc=none smtp.client-ip=198.175.65.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="ZDQRMT5Q" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1784886752; x=1816422752; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=/4qWagf48+7Q8af5cvBaiK/qrm5KNmMh4ywBJuimSoI=; b=ZDQRMT5QEDXE/hmWZcPhDIcvnR3a5sqdScdmeWAQvlUWnLatbUi//5CR s7UW03gysUrP5XxFF414lmK53bwfOcpyY7PR38CvlEDR/MIVYbif3oV5P skoxR05OUvd45BRolcJAVZDnUpX1htGIsCQAYwjwWd4rhZLNalNwyglyz NgEIcYx1z/Ote4ROj8lLj8WlDtPBIpsH7mN0B8fb7mjekFT/+o+cvoQr8 l4jTyRlQg46sFI078UoaycV9G52MuECLeL2KSyEHwpJjzNIz5nyaZwZ51 mAmIn3o5l3R1+0iemfSt5zWRemWKldmL9e3VqcT13rGt3bahg2dD6Ko80 Q==; X-CSE-ConnectionGUID: JlEW1fKdQ3mi9kks47Lqew== X-CSE-MsgGUID: s0kc7+H3TFSB/Z3St44TAA== X-IronPort-AV: E=McAfee;i="6800,10657,11854"; a="95907643" X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="95907643" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Jul 2026 02:52:31 -0700 X-CSE-ConnectionGUID: vREecuUvTKK33t2LfJ22jg== X-CSE-MsgGUID: wWe658w9Q3qKmLihFmXspg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="256018487" Received: from linux-pnp-gnr-1.sh.intel.com ([10.239.83.186]) by fmviesa008.fm.intel.com with ESMTP; 24 Jul 2026 02:52:26 -0700 From: Jiebin Sun To: Namhyung Kim , acme@kernel.org, mingo@redhat.com, peterz@infradead.org Cc: adrian.hunter@intel.com, alexander.shishkin@linux.intel.com, irogers@google.com, james.clark@linaro.org, jolsa@kernel.org, mark.rutland@arm.com, dapeng1.mi@linux.intel.com, thomas.falcon@intel.com, tianyou.li@intel.com, wangyang.guo@intel.com, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Jiebin Sun Subject: [PATCH v4 7/9] perf c2c: build and finalize the function view hierarchy Date: Fri, 24 Jul 2026 17:58:40 +0800 Message-ID: <20260724095842.995920-8-jiebin.sun@intel.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260724095842.995920-1-jiebin.sun@intel.com> References: <20260717020530.1645123-1-jiebin.sun@intel.com> <20260724095842.995920-1-jiebin.sun@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the builder that walks the cacheline histograms once and, for each read-side function, records the functions that write the lines it reads (level 2) and the specific cachelines they contend over (level 3), aggregating the write traffic per contending function. Add the store-count resort, the pruning of writers/functions with no contention, and the finalize pass that computes the per-function cycle totals. build_function_view_hierarchy() is wired into the browser entry point by the next patch and is marked __maybe_unused until then. Signed-off-by: Jiebin Sun Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Reviewed-by: Tianyou Li Reviewed-by: Wangyang Guo --- tools/perf/ui/browsers/c2c-function.c | 471 +++++++++++++++++++++++++- 1 file changed, 459 insertions(+), 12 deletions(-) diff --git a/tools/perf/ui/browsers/c2c-function.c b/tools/perf/ui/browsers= /c2c-function.c index 9a7b1353e11c..bb7f967ab904 100644 --- a/tools/perf/ui/browsers/c2c-function.c +++ b/tools/perf/ui/browsers/c2c-function.c @@ -47,13 +47,13 @@ struct perf_c2c_ext { u64 total_cycles; }; =20 -static struct perf_c2c_ext c2c_ext __maybe_unused; +static struct perf_c2c_ext c2c_ext; =20 struct c2c_function_browser { struct hist_browser hb; }; =20 -static inline __maybe_unused u64 c2c_hitm_count(const struct c2c_stats *st= ats) +static inline u64 c2c_hitm_count(const struct c2c_stats *stats) { return stats->tot_hitm; } @@ -88,7 +88,7 @@ static int64_t c2c_function_cmp(const struct map_symbol *= left, return _sort__sym_cmp(left->sym, right->sym); } =20 -static inline __maybe_unused u64 hist_entry__iaddr(struct hist_entry *he) +static inline u64 hist_entry__iaddr(struct hist_entry *he) { if (he->mem_info) return mem_info__iaddr(he->mem_info)->addr; @@ -224,7 +224,7 @@ static u64 c2c_hist_entry__cycles(struct c2c_hist_entry= *c2c_he) } =20 /* Sum c2c_hist_entry__cycles() across all level-1 entries. */ -static u64 __maybe_unused c2c_ext__total_cycles(void) +static u64 c2c_ext__total_cycles(void) { struct rb_node *nd; u64 total =3D 0; @@ -654,7 +654,7 @@ function_hpp_list__parse(struct perf_hpp_list *hpp_list, return ret; } =20 -static int __maybe_unused +static int c2c_function_hists__init(struct c2c_hists *hists, const char *sort, int nr_header_lines, @@ -669,7 +669,7 @@ c2c_function_hists__init(struct c2c_hists *hists, return function_hpp_list__parse(&hists->list, /*output=3D*/NULL, sort, en= v); } =20 -static int __maybe_unused +static int c2c_function_hists__reinit(struct c2c_hists *c2c_hists, const char *output, const char *sort, @@ -719,7 +719,7 @@ static void c2c_stats_merge(struct stats *dest, const s= truct stats *src) } =20 /* Merge compute_stats during function aggregation. */ -static void __maybe_unused c2c_add_cstats(struct compute_stats *dest, +static void c2c_add_cstats(struct compute_stats *dest, const struct compute_stats *src) { c2c_stats_merge(&dest->rmt_hitm, &src->rmt_hitm); @@ -729,7 +729,7 @@ static void __maybe_unused c2c_add_cstats(struct comput= e_stats *dest, c2c_stats_merge(&dest->load, &src->load); } =20 -static bool __maybe_unused hist_entry__add_c2c_stats(struct hist_entry *he, +static bool hist_entry__add_c2c_stats(struct hist_entry *he, const struct c2c_stats *stats) { u64 nr_events =3D c2c_hitm_count(stats) + stats->rmt_peer + stats->lcl_pe= er; @@ -860,7 +860,7 @@ static void c2c_he__free_hierarchy(struct hist_entry *h= e) * line, so this is mainly a safety net. Returns the number of surviving * writers. */ -static int __maybe_unused c2c_he__prune_empty_writers(struct hist_entry *l= 1_he) +static int c2c_he__prune_empty_writers(struct hist_entry *l1_he) { struct rb_node *nd; int surviving =3D 0; @@ -969,7 +969,7 @@ c2c_child_entry__insert(struct hist_entry *parent_he, s= truct hist_entry *child_h parent_he->leaf =3D false; } =20 -static __maybe_unused struct hist_entry * +static struct hist_entry * c2c_function_hists__level1_entry(struct symbol *sym, struct hist_entry *detail_he, struct thread *synthetic_thread) @@ -1044,7 +1044,7 @@ c2c_function_hists__level1_entry(struct symbol *sym, * same writer function aggregate into one row without merging identically * named functions from different DSOs. */ -static __maybe_unused struct c2c_hist_entry * +static struct c2c_hist_entry * c2c_function_hists__level2_entry(struct c2c_hist_entry *level1_c2c, struct symbol *sym, struct hist_entry *detail_he) { @@ -1091,7 +1091,7 @@ c2c_function_hists__level2_entry(struct c2c_hist_entr= y *level1_c2c, } =20 /* Level 3: a specific cacheline where the L1/L2 functions contend (keyed = by cl_addr). */ -static __maybe_unused struct c2c_hist_entry * +static struct c2c_hist_entry * c2c_function_hists__level3_entry(struct c2c_hist_entry *level2_c2c, u64 cl= _addr, struct c2c_hist_entry *cacheline_src_he) { @@ -1132,6 +1132,453 @@ c2c_function_hists__level3_entry(struct c2c_hist_en= try *level2_c2c, u64 cl_addr, return level3_c2c; } =20 +/* + * Re-sort child entries of @parent_he by total store count, descending. + */ +static void c2c_he__resort_by_stores(struct hist_entry *parent_he) +{ + struct rb_root_cached new_root =3D RB_ROOT_CACHED; + struct rb_node *nd; + + if (!parent_he->has_children) + return; + + /* Extract all nodes and re-insert sorted by displayed store count */ + while ((nd =3D rb_first_cached(&parent_he->hroot_out))) { + struct hist_entry *he =3D rb_entry(nd, struct hist_entry, rb_node); + u64 he_store =3D hist_entry__displayed_stores(he); + struct rb_node **p =3D &new_root.rb_root.rb_node; + struct rb_node *parent =3D NULL; + bool leftmost =3D true; + int cmp; + + /* Remove from current tree */ + rb_erase_cached(&he->rb_node, &parent_he->hroot_out); + + /* + * Insert sorted by store count, descending. Use the displayed + * store count so a level-1 function and level-2 writer (whose own + * stats.store is 0 / partial) sort by the aggregated write traffic + * beneath them, not by their own store field. + */ + while (*p) { + struct hist_entry *iter =3D rb_entry(*p, struct hist_entry, rb_node); + u64 iter_store =3D hist_entry__displayed_stores(iter); + + parent =3D *p; + if (he_store !=3D iter_store) { + cmp =3D he_store > iter_store ? -1 : 1; + } else { + /* Stable tie-break: instruction address, then name. */ + u64 a =3D hist_entry__iaddr(he), b =3D hist_entry__iaddr(iter); + + if (a !=3D b) + cmp =3D a < b ? -1 : 1; + else if (he->ms.sym && iter->ms.sym) + cmp =3D arch__compare_symbol_names(he->ms.sym->name, + iter->ms.sym->name); + else + cmp =3D (iter->ms.sym ? 1 : 0) - (he->ms.sym ? 1 : 0); + } + + if (cmp < 0) { + p =3D &parent->rb_left; + } else { + p =3D &parent->rb_right; + leftmost =3D false; + } + } + + rb_link_node(&he->rb_node, parent, p); + rb_insert_color_cached(&he->rb_node, &new_root, leftmost); + } + + parent_he->hroot_out =3D new_root; +} + +/* Initial per-cacheline capacity for the seen[] set; grown on demand. */ +#define DEFAULT_SYMBOLS_PER_CL 64 + +struct function_seen { + struct map_symbol ms; +}; + +static bool function_seen__find(const struct function_seen *seen, int nr, + const struct map_symbol *ms) +{ + int i; + + for (i =3D 0; i < nr; i++) { + if (!c2c_function_cmp(&seen[i].ms, ms)) + return true; + } + return false; +} + +/* Aggregate stats from the cacheline-side entry @c2c_b into level 2/3 @ds= t. */ +static bool c2c_he__add_sharing(struct c2c_hist_entry *dst, struct c2c_his= t_entry *src) +{ + /* Do the fallible update first so a failure leaves dst unmodified. */ + if (!hist_entry__add_c2c_stats(&dst->he, &src->stats)) + return false; + + c2c_add_stats(&dst->stats, &src->stats); + c2c_add_cstats(&dst->cstats, &src->cstats); + return true; +} + +/* + * Process one cacheline: for every function reading it, create/update its + * level-1 function entry, then for each other function that writes the li= ne + * add it as a level-2 writer and add this cacheline as a level-3 child. + */ +static int c2c_function__process_cl(struct c2c_hist_entry *cacheline_he, u= 64 cl_addr, + struct thread *synthetic_thread) +{ + struct rb_node *nd_a, *nd_b; + struct function_seen *seen =3D NULL; + int nr_seen =3D 0, nr_alloc =3D 0; + int ret =3D 0; + + for (nd_a =3D rb_first_cached(&cacheline_he->hists->hists.entries); nd_a; + nd_a =3D rb_next(nd_a)) { + struct hist_entry *he_a =3D rb_entry(nd_a, struct hist_entry, rb_node); + struct c2c_hist_entry *c2c_a; + struct hist_entry *level1_he; + struct c2c_hist_entry *level1_c2c; + + if (!he_a->ms.sym || he_a->filtered) + continue; + + c2c_a =3D container_of(he_a, struct c2c_hist_entry, he); + if (c2c_a->stats.load =3D=3D 0) + continue; + + + level1_he =3D c2c_function_hists__level1_entry(he_a->ms.sym, + he_a, synthetic_thread); + if (!level1_he) { + ret =3D -ENOMEM; + goto out; + } + + level1_c2c =3D container_of(level1_he, struct c2c_hist_entry, he); + + /* + * Aggregate every source entry into its level-1 function parent. + * level1_he is keyed by symbol, so all code addresses inside the + * same function collapse into one parent. When the cacheline view + * splits a function into siblings (different code addresses, or + * -d pid/tid/dso), each sibling holds a DISJOINT slice of the + * traffic, so summing them here is correct accumulation, not + * double counting. The seen[] set below therefore guards only the + * subtree build (to avoid building a function's level-2/3 subtree + * twice for the same cacheline), never this L1 update. Update + * he->stat first; on failure leave the aggregates untouched. + */ + if (!hist_entry__add_c2c_stats(level1_he, &c2c_a->stats)) { + ret =3D -ENOMEM; + goto out; + } + c2c_add_stats(&level1_c2c->stats, &c2c_a->stats); + c2c_add_cstats(&level1_c2c->cstats, &c2c_a->cstats); + c2c_add_stats(&c2c_ext.function_hists.stats, &c2c_a->stats); + + /* Build this function's subtree for this cacheline only once. */ + if (function_seen__find(seen, nr_seen, &he_a->ms)) + continue; + + if (nr_seen =3D=3D nr_alloc) { + struct function_seen *tmp; + int new_alloc =3D nr_alloc ? nr_alloc * 2 : DEFAULT_SYMBOLS_PER_CL; + + tmp =3D reallocarray(seen, new_alloc, sizeof(*seen)); + if (!tmp) { + ret =3D -ENOMEM; + goto out; + } + seen =3D tmp; + nr_alloc =3D new_alloc; + } + seen[nr_seen].ms =3D he_a->ms; + nr_seen++; + + for (nd_b =3D rb_first_cached(&cacheline_he->hists->hists.entries); nd_b; + nd_b =3D rb_next(nd_b)) { + struct hist_entry *he_b =3D rb_entry(nd_b, struct hist_entry, rb_node); + struct c2c_hist_entry *c2c_b, *level2_c2c, *level3_c2c; + + if (!he_b->ms.sym || he_b->filtered) + continue; + + c2c_b =3D container_of(he_b, struct c2c_hist_entry, he); + + /* Skip the level-1 function itself. */ + if (!c2c_function_cmp(&he_a->ms, &he_b->ms)) + continue; + + /* + * The level-1 function pays a read-side HITM/peer penalty + * (its Cycles %) because someone else writes this cacheline. + * Only writers are contending functions, so keep the ones + * that actually store into the line. + */ + if (c2c_b->stats.store =3D=3D 0) + continue; + + /* Level 2: the writing function (aggregated across cachelines). */ + level2_c2c =3D c2c_function_hists__level2_entry(level1_c2c, he_b->ms.sy= m, + he_b); + if (!level2_c2c || !c2c_he__add_sharing(level2_c2c, c2c_b)) { + ret =3D -ENOMEM; + goto out; + } + + /* Level 3: the specific cacheline they contend over. */ + level3_c2c =3D c2c_function_hists__level3_entry(level2_c2c, cl_addr, + cacheline_he); + if (!level3_c2c || !c2c_he__add_sharing(level3_c2c, c2c_b)) { + ret =3D -ENOMEM; + goto out; + } + + /* + * Cache the running sum of writer store counts on the + * level-2 writer and level-1 function parents, so + * hist_entry__child_stores() stays O(1). + */ + level2_c2c->child_stores +=3D c2c_b->stats.store; + level1_c2c->child_stores +=3D c2c_b->stats.store; + } + } + +out: + free(seen); + return ret; +} + +/* + * Remove a level-1 function that has no contended cachelines left. It is a + * normal (owned) hist_entry in function_hists, so mirror hists__delete_en= try() + * for the no-collapse case: unlink from both trees, fix the counters, then + * delete. report_hierarchy is never set and its hroot_out is already empt= y, so + * hist_entry__delete() will not recurse into borrowed children. + */ +static void c2c_function__drop_level1(struct hist_entry *he) +{ + struct hists *hists =3D &c2c_ext.function_hists.hists; + + rb_erase_cached(&he->rb_node_in, hists->entries_in); + rb_erase_cached(&he->rb_node, &hists->entries); + + --hists->nr_entries; + if (!he->filtered) + --hists->nr_non_filtered_entries; + + hist_entry__delete(he); +} + +/* Length of the identity text (symbol name or cacheline address) at @he. = */ +static int c2c_function__ident_len(struct hist_entry *he) +{ + char buf[512]; + char *symbuf; + size_t size; + int len; + + if (hist_entry__is_cacheline(he)) { + u64 addr =3D he->mem_info ? + cl_address(mem_info__daddr(he->mem_info)->addr, chk_double_cl) : 0; + + return scnprintf(buf, sizeof(buf), "0x%" PRIx64, addr); + } + + if (!he->ms.sym) + return 0; + + /* + * Match symbol_view_entry(): sort_sym adds the cpumode prefix and, in + * verbose mode, the address and DSO origin before the symbol name. + */ + size =3D strlen(he->ms.sym->name) + 64; + symbuf =3D malloc(size); + if (!symbuf) + return size - 1; + + len =3D sort_sym.se_snprintf(he, symbuf, size, size - 1); + free(symbuf); + return len; +} + +/* + * Grow the symbol column so the deepest, longest identity cell fits. The + * generic hists__calc_col_len() only measures the top-level (L1) entries;= the + * hand-linked L2 writers and L3 cacheline addresses live in hroot_out and= are + * never measured, so with a short L1 name the indented L2/L3 text would be + * truncated. Account for the per-level indent and the folded-sign prefix. + */ +static void c2c_function__update_symbol_width(struct hist_entry *he) +{ + struct hists *hists =3D &c2c_ext.function_hists.hists; + int need =3D he->depth * C2C_FUNC_INDENT + C2C_FUNC_FOLD_WIDTH + + c2c_function__ident_len(he); + + if (need > hists__col_len(hists, HISTC_SYMBOL)) + hists__set_col_len(hists, HISTC_SYMBOL, need); +} + +/* + * Prune writers with no stores, drop functions left with no contending + * writer, sort the survivors by store count, then compute the global tota= l. + */ +static void c2c_function__finalize(void) +{ + struct rb_node *nd_l1; + + nd_l1 =3D rb_first_cached(&c2c_ext.function_hists.hists.entries); + while (nd_l1) { + struct hist_entry *he_l1 =3D rb_entry(nd_l1, struct hist_entry, rb_node); + struct rb_node *next_l1 =3D rb_next(nd_l1); + struct rb_node *nd_l2; + + /* Drop writers with no stores before sorting. */ + if (!he_l1->has_children || !c2c_he__prune_empty_writers(he_l1)) { + /* No contending writer: this function is not shared. */ + c2c_function__drop_level1(he_l1); + nd_l1 =3D next_l1; + continue; + } + + c2c_he__resort_by_stores(he_l1); + c2c_function__update_symbol_width(he_l1); + + for (nd_l2 =3D rb_first_cached(&he_l1->hroot_out); nd_l2; + nd_l2 =3D rb_next(nd_l2)) { + struct hist_entry *he_l2 =3D rb_entry(nd_l2, struct hist_entry, rb_node= ); + struct rb_node *nd_l3; + + c2c_function__update_symbol_width(he_l2); + + if (he_l2->has_children) + c2c_he__resort_by_stores(he_l2); + + for (nd_l3 =3D rb_first_cached(&he_l2->hroot_out); nd_l3; + nd_l3 =3D rb_next(nd_l3)) { + struct hist_entry *he_l3 =3D rb_entry(nd_l3, struct hist_entry, + rb_node); + + c2c_function__update_symbol_width(he_l3); + } + } + + nd_l1 =3D next_l1; + } + + c2c_ext.total_cycles =3D c2c_ext__total_cycles(); +} + +/* + * Release all per-visit function-view state. Keep the hists object itself + * initialized so its mutex is initialized exactly once across TAB re-entr= y. + */ +static void c2c_function_hists__reset(void) +{ + hists__delete_all_entries(&c2c_ext.function_hists.hists); + if (c2c_ext.function_hists.list.fields.next) + perf_hpp__reset_output_field(&c2c_ext.function_hists.list); + + memset(&c2c_ext.function_hists.stats, 0, + sizeof(c2c_ext.function_hists.stats)); + c2c_ext.total_cycles =3D 0; +} + +/* + * Build the three-level function view in a single pass over the cacheline + * entries: + * L1: read-side functions (aggregated across all their code addresses) + * L2: writing functions contending with each level-1 function + * L3: shared cachelines for each function pair + */ +static int __maybe_unused build_function_view_hierarchy(void) +{ + static const char output_fields[] =3D + "cycles_percent,total_stores,symbol_view"; + static bool hists_initialized; + struct rb_node *nd_cl; + int ret; + + c2c_function_hists__reset(); + + /* + * __hists__init() (called by c2c_function_hists__init()) also + * mutex_init()s the hists lock, so only run it once for this static + * hists; on re-entry just re-parse the columns via reinit(). + */ + if (!hists_initialized) { + ret =3D c2c_function_hists__init(&c2c_ext.function_hists, + "symbol_view", 2, NULL); + hists_initialized =3D true; + } else { + ret =3D c2c_function_hists__reinit(&c2c_ext.function_hists, + /*output=3D*/NULL, "symbol_view", NULL); + } + if (ret) + return ret; + + nd_cl =3D rb_first_cached(&c2c.hists.hists.entries); + + /* An empty C2C report yields an empty (but valid) function view. */ + for (; nd_cl; nd_cl =3D rb_next(nd_cl)) { + struct hist_entry *he_cl =3D rb_entry(nd_cl, struct hist_entry, rb_node); + struct c2c_hist_entry *cacheline_he =3D container_of(he_cl, + struct c2c_hist_entry, he); + struct thread *synthetic_thread =3D he_cl->thread; + u64 cl_addr; + + /* + * Include any cacheline with sharing activity (HITM, peer, + * stores or loads), not just HITM, so totals/sorting reflect + * all aggregated traffic surfaced by the function view. + */ + if ((c2c_hitm_count(&cacheline_he->stats) =3D=3D 0 && + cacheline_he->stats.tot_peer =3D=3D 0 && + cacheline_he->stats.store =3D=3D 0 && + cacheline_he->stats.load =3D=3D 0) || + !cacheline_he->hists || + RB_EMPTY_ROOT(&cacheline_he->hists->hists.entries.rb_root) || + !he_cl->mem_info || !synthetic_thread) + continue; + + cl_addr =3D cl_address(mem_info__daddr(he_cl->mem_info)->addr, chk_doubl= e_cl); + ret =3D c2c_function__process_cl(cacheline_he, cl_addr, synthetic_thread= ); + if (ret) + goto out_err; + } + + ret =3D c2c_function_hists__reinit(&c2c_ext.function_hists, output_fields, + "cycles_percent", NULL); + if (ret) + goto out_err; + + hists__collapse_resort(&c2c_ext.function_hists.hists, NULL); + hists__output_resort(&c2c_ext.function_hists.hists, NULL); + + c2c_function__finalize(); + + return 0; + +out_err: + /* + * On error, migrate any entries still in entries_in to entries and + * delete them, so a later rebuild does not strand them (the top-level + * __hists__init() memset would otherwise lose the pointers). + */ + hists__collapse_resort(&c2c_ext.function_hists.hists, NULL); + hists__output_resort(&c2c_ext.function_hists.hists, NULL); + c2c_function_hists__reset(); + return ret; +} + int perf_c2c__browse_function_view(struct hists *hists __maybe_unused) { ui__warning("C2C function view is not implemented yet.\n"); --=20 2.52.0 From nobody Fri Jul 24 21:52:58 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 380B63F8248; Fri, 24 Jul 2026 09:52:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886756; cv=none; b=N/3jGSeKZ0Q5zJ6EZ1QXSeyuqjVnzwQ6JXHjB/SeVS0kITL2AyQDx7yTD0AbiFnJ8AdCERnAmIQxB/1I/ozPDN0HRYSo9cZODC/BcpJCJmT9lm/VeSzKQWDEn/7Xmm+ifkXJ5A2xE7odpD2FjR0CZWA8KEKOSrsoG+LXpvkz8eI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886756; c=relaxed/simple; bh=aVr1jbZLBlxYPBAEW0MZeDEQwl7T+0VvTdqCh30dOEY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LLjxOLgJBbMa5RE8YcFhkA0FD3winBuKapE9wLcl8D8P2M30xO/bCChSnHLTdv/Xs9nOFUrCGfP796j1rgsyK/OupawAKvXuVu2CnWIZSzS+jND6nHChWaz92U1yVj78xBwQhzHmo/uwpQateFXcLNYQEIW9Mb1WSgiOm3K9Y4g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=g11S+5Bo; arc=none smtp.client-ip=198.175.65.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="g11S+5Bo" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1784886756; x=1816422756; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=aVr1jbZLBlxYPBAEW0MZeDEQwl7T+0VvTdqCh30dOEY=; b=g11S+5BoUb3rJal1jVUVlFzZnPELzS+/Zl8HaylVRvU8l5UEe6uPqnVa 438DiZq8i4CDhbAUZs7+WvHGopojEyjrhq8boFuu9pEIggvJ2E4WAVnon U1QJRV2Ei/Fz7pC+pMVI7vcOil7ruLCaZY8muI92qXRnwy0pGLkOGIZy+ KJag/KNr92TXzm0TeQV+WoqkCmWj9J8Y5yCu3gSxJ4NnNJzyoY28rBIZP XyPCQzpNrLgaprQ6uUeHn1vl5U1LwAsiN58iXHOSvkpbuXtu56eOjLA0G tLDR6Ekmz1Oba7GTn036iioPcOv2Ht9xTYmUxtjtDe4sklkRdYNZk8sVA A==; X-CSE-ConnectionGUID: 6mCG/hwVRjKaiB5Kc8VPbQ== X-CSE-MsgGUID: LREmuT8/Qdegd3RhN18O6w== X-IronPort-AV: E=McAfee;i="6800,10657,11854"; a="95907656" X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="95907656" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Jul 2026 02:52:35 -0700 X-CSE-ConnectionGUID: 878LD6W1TImov2QnN4ISnQ== X-CSE-MsgGUID: Skf7mjZMS5Wlsh8PWy7i+A== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="256018496" Received: from linux-pnp-gnr-1.sh.intel.com ([10.239.83.186]) by fmviesa008.fm.intel.com with ESMTP; 24 Jul 2026 02:52:30 -0700 From: Jiebin Sun To: Namhyung Kim , acme@kernel.org, mingo@redhat.com, peterz@infradead.org Cc: adrian.hunter@intel.com, alexander.shishkin@linux.intel.com, irogers@google.com, james.clark@linaro.org, jolsa@kernel.org, mark.rutland@arm.com, dapeng1.mi@linux.intel.com, thomas.falcon@intel.com, tianyou.li@intel.com, wangyang.guo@intel.com, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Jiebin Sun Subject: [PATCH v4 8/9] perf c2c: add function view browser UI and cacheline detail Date: Fri, 24 Jul 2026 17:58:41 +0800 Message-ID: <20260724095842.995920-9-jiebin.sun@intel.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260724095842.995920-1-jiebin.sun@intel.com> References: <20260717020530.1645123-1-jiebin.sun@intel.com> <20260724095842.995920-1-jiebin.sun@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the browser front end: create/run/delete the hist_browser, the title, and the 'd' shortcut that opens the existing per-cacheline detail view for the selected entry. Wire the builder into perf_c2c__browse_function_view() and refuse to run under report_hierarchy, whose hist_entry__delete() recursion would put the children's borrowed references. Signed-off-by: Jiebin Sun Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Reviewed-by: Tianyou Li Reviewed-by: Wangyang Guo --- tools/perf/ui/browsers/c2c-function.c | 211 +++++++++++++++++++++++++- 1 file changed, 207 insertions(+), 4 deletions(-) diff --git a/tools/perf/ui/browsers/c2c-function.c b/tools/perf/ui/browsers= /c2c-function.c index bb7f967ab904..46b3ff854833 100644 --- a/tools/perf/ui/browsers/c2c-function.c +++ b/tools/perf/ui/browsers/c2c-function.c @@ -40,7 +40,6 @@ #include "../../c2c.h" #include "hists.h" =20 - struct perf_c2c_ext { struct c2c_hists function_hists; /* Cached across all level-1 entries; 0 means "not yet computed". */ @@ -95,6 +94,10 @@ static inline u64 hist_entry__iaddr(struct hist_entry *h= e) return he->ip; } =20 +/* + * Hierarchy levels (by depth): L1 =3D read-side function, L2 =3D the writ= ing + * function it contends with, L3 =3D the specific shared cacheline. + */ static inline bool hist_entry__is_cacheline(struct hist_entry *he) { return he->parent_he && he->parent_he->parent_he; /* level 3: cacheline */ @@ -694,6 +697,7 @@ c2c_function_hists__reinit(struct c2c_hists *c2c_hists, return function_hpp_list__parse(&c2c_hists->list, output, sort, env); } =20 +/* Welford online merge of two "stats" (from util/stat.h) accumulators. */ static void c2c_stats_merge(struct stats *dest, const struct stats *src) { double delta; @@ -1427,6 +1431,37 @@ static void c2c_function__update_symbol_width(struct= hist_entry *he) hists__set_col_len(hists, HISTC_SYMBOL, need); } =20 +/* + * Count visible entries in @root, descending only through visible, unfold= ed + * parents. Match hists__filter_entries(), which drives generic browser + * navigation, so the count cannot include rows the browser skips. + */ +static u64 +c2c_function__nr_visible_rows(struct rb_root_cached *root, float min_pcnt) +{ + struct rb_node *nd; + u64 rows =3D 0; + + for (nd =3D rb_first_cached(root); nd; nd =3D rb_next(nd)) { + struct hist_entry *he =3D rb_entry(nd, struct hist_entry, rb_node); + + /* + * The generic refresh folds filtered parents and therefore hides + * their subtree. A percentage-rejected parent is merely skipped; + * if it is unfolded, qualifying descendants are still rendered. + */ + if (he->filtered) + continue; + + if (hist_entry__get_percent_limit(he) >=3D min_pcnt) + rows++; + if (he->has_children && he->unfolded) + rows +=3D c2c_function__nr_visible_rows(&he->hroot_out, + min_pcnt); + } + return rows; +} + /* * Prune writers with no stores, drop functions left with no contending * writer, sort the survivors by store count, then compute the global tota= l. @@ -1499,7 +1534,7 @@ static void c2c_function_hists__reset(void) * L2: writing functions contending with each level-1 function * L3: shared cachelines for each function pair */ -static int __maybe_unused build_function_view_hierarchy(void) +static int build_function_view_hierarchy(void) { static const char output_fields[] =3D "cycles_percent,total_stores,symbol_view"; @@ -1579,8 +1614,176 @@ static int __maybe_unused build_function_view_hiera= rchy(void) return ret; } =20 -int perf_c2c__browse_function_view(struct hists *hists __maybe_unused) +static int c2c_function_browser__title(struct hist_browser *browser, + char *bf, size_t size) { - ui__warning("C2C function view is not implemented yet.\n"); + scnprintf(bf, size, + "Shared Data Functions Table (%" PRIu64 " entries, sorted on Cycle= s %%)", + browser->hists->nr_non_filtered_entries); return 0; } + +static struct c2c_function_browser *c2c_function_browser__new(struct hists= *hists) +{ + struct c2c_function_browser *browser; + + if (!hists) + return NULL; + + browser =3D zalloc(sizeof(*browser)); + if (!browser) + return NULL; + + hist_browser__init(&browser->hb, hists); + + browser->hb.title =3D c2c_function_browser__title; + browser->hb.c2c_filter =3D true; + browser->hb.show_headers =3D true; + /* Keep title line count consistent with forcing headers on. */ + browser->hb.b.extra_title_lines =3D hists->hpp_list->nr_header_lines; + browser->hb.min_pcnt =3D 0.0; + + /* + * Note: symbol_conf.report_hierarchy is deliberately left unset. + * The generic browser still descends into hroot_out children via + * rb_hierarchy_next()/can_goto_child(), which key off he->unfolded, + * so 'e'/'+' expands L1 -> L2 -> L3 correctly. Setting the flag would + * additionally make hist_entry__delete() recurse hroot_out and free + * each child, but our children borrow thread/ms (see + * c2c_child_entry__alloc()), so that would underflow their refcounts. + * Teardown is handled by c2c_he__free_hierarchy() instead. + */ + return browser; +} + +/* + * c2c_function_browser__delete - Free function browser + */ +static void c2c_function_browser__delete(struct c2c_function_browser *brow= ser) +{ + free(browser); +} + +static int c2c_function_browser__browse_cacheline_detail(struct hist_entry= *he_selection, + struct hists *hists) +{ + struct rb_node *nd; + u64 cl_addr; + + if (!he_selection || !he_selection->parent_he || + !he_selection->parent_he->parent_he || !he_selection->mem_info) + return -1; + + cl_addr =3D cl_address(mem_info__daddr(he_selection->mem_info)->addr, chk= _double_cl); + + for (nd =3D rb_first_cached(&hists->entries); nd; nd =3D rb_next(nd)) { + struct hist_entry *he_cl =3D rb_entry(nd, struct hist_entry, rb_node); + u64 this_cl; + + if (!he_cl->mem_info) + continue; + + this_cl =3D cl_address(mem_info__daddr(he_cl->mem_info)->addr, chk_doubl= e_cl); + if (this_cl =3D=3D cl_addr) + return perf_c2c__browse_cacheline(he_cl); + } + + return -1; +} + +/* + * perf_c2c__browse_function_view - Browse function view with TAB key supp= ort + * @hists: Main cacheline histograms + * + * Returns: 0 on success, negative error code on failure + */ +int perf_c2c__browse_function_view(struct hists *hists) +{ + struct c2c_function_browser *sym_browser; + bool saved_use_callchain =3D symbol_conf.use_callchain; + int key, ret; + static const char help[] =3D + " d Display cacheline details for the selected entry\n" + " e/+ Expand/collapse the selected entry\n" + " TAB/ESC/q Return to the cacheline view\n"; + + if (!hists) + return -EINVAL; + + /* + * The level-2/3 children borrow thread/ms (see c2c_child_entry__alloc()) + * and are torn down by c2c_he__free_hierarchy(). report_hierarchy would + * make hist_entry__delete() recurse into hroot_out and put those borrowed + * refs (and call a NULL ops->free), so refuse to run if it is ever set. + */ + if (WARN_ON_ONCE(symbol_conf.report_hierarchy)) + return -EINVAL; + + /* Disable callchain before building so no callchain structs are allocate= d. */ + symbol_conf.use_callchain =3D false; + + ret =3D build_function_view_hierarchy(); + if (ret) { + ui__error("Failed to build function view hierarchy (ret=3D%d)\n", ret); + goto out; + } + + + sym_browser =3D c2c_function_browser__new(&c2c_ext.function_hists.hists); + if (!sym_browser) { + ret =3D -ENOMEM; + goto out_reset; + } + + /* Reset abort key so we can receive Ctrl-C as a key. */ + SLang_reset_tty(); + SLang_init_tty(0, 0, 0); + SLtty_set_suspend_state(true); + + while (1) { + /* + * hist_browser__run() resets b.nr_entries from + * nr_non_filtered_entries on entry, so refresh the visible-row + * count (which includes expanded L2/L3 children) here, before + * each run, or the cursor cannot move onto the expanded rows + * after returning from the 'd'/'?' windows. + */ + sym_browser->hb.nr_non_filtered_entries =3D + c2c_function__nr_visible_rows( + &c2c_ext.function_hists.hists.entries, + sym_browser->hb.min_pcnt); + + key =3D hist_browser__run(&sym_browser->hb, "? - help", true, 0); + + switch (key) { + case 'q': + case K_TAB: + case K_ESC: + goto browser_done; + case 'd': + /* Cacheline detail honors the user's callchain setting. */ + symbol_conf.use_callchain =3D saved_use_callchain; + c2c_function_browser__browse_cacheline_detail(sym_browser->hb.he_select= ion, + hists); + /* Preserve any toggle made in the detail view, then + * re-disable callchain for the function view. + */ + saved_use_callchain =3D symbol_conf.use_callchain; + symbol_conf.use_callchain =3D false; + break; + case '?': + ui_browser__help_window(&sym_browser->hb.b, help); + break; + default: + break; + } + } + +browser_done: + c2c_function_browser__delete(sym_browser); +out_reset: + c2c_function_hists__reset(); +out: + symbol_conf.use_callchain =3D saved_use_callchain; + return ret; +} --=20 2.52.0 From nobody Fri Jul 24 21:52:58 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7267841E6A9; Fri, 24 Jul 2026 09:52:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886762; cv=none; b=NkC1yynGwjJ5TM56fCHCafqDS7zD4bRh33YIxtjleMsmzhLhzcAIRFLT4b9MDCeWlAJTRAlSROyAlNrL8tlgmbw1Le2aT6eT7DnYkwYoQQGntmvbZQN6UAD4EzC40+rLsm/5/gfjKuhm2SCWEbdlruGDY3r8PvoWzLxELIvdYNM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784886762; c=relaxed/simple; bh=JhRvw4trRB/hsRjHlEe9l1qdTLitpOQUUwyltcnKRrI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jOJmwD5lcp4PsbEZYLacgj3hM8VGM3eSvMyoh/iXAijJgKHD18bauBuj8cDS1Yh4WqoP6XC/IETG11jtknKCZYsWu1deeFDkGRaXGxteHVBTh0q9ZsYdOyxfSYNhAute56qRm5qVBJrxzQTOvM5Q4EXUeQw/wxImFuEHzeez5Js= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=H4cQQ6CD; arc=none smtp.client-ip=198.175.65.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="H4cQQ6CD" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1784886761; x=1816422761; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=JhRvw4trRB/hsRjHlEe9l1qdTLitpOQUUwyltcnKRrI=; b=H4cQQ6CD01Jk5A2AlOQ/o9yDzMosTgmTVhJv/w2JsiR718zre13MDJ5z Z5gmzbfm3ui9T2Q1OxqlQPyPHrWneHfDLJuvUaCTyhCWC+S8onRStwa9f M+ON37hAT/nWSBdVoTgA3aXxSAXEtFMgQAPvUSu5/TKgTBYsBrUGnw+4r 1TiKoSqIl/tqpAJTv3FY/jPtazaVed0riNZfZNc5DCcAZSWCnzkH5xcCz YPTQE9dUV2IX+LaKEUHltMp4pT332QIUickmyJXwZlCkk8t2mPOyzS5ez xSv2gYpL/2Aau6Sizykf4YTcbvau8pV26h1GTljpFI+QXU8nvp/MJwirB g==; X-CSE-ConnectionGUID: 7PNqdPydROOa6oSl+dQlGA== X-CSE-MsgGUID: z1740FZFS6O11w9c+q6JUw== X-IronPort-AV: E=McAfee;i="6800,10657,11854"; a="95907671" X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="95907671" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by orvoesa103.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Jul 2026 02:52:39 -0700 X-CSE-ConnectionGUID: ht0HKdNWReiHZh7ZHEuDZA== X-CSE-MsgGUID: NdEfaWWQR6eX53ifIy2nEA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,182,1779174000"; d="scan'208";a="256018502" Received: from linux-pnp-gnr-1.sh.intel.com ([10.239.83.186]) by fmviesa008.fm.intel.com with ESMTP; 24 Jul 2026 02:52:34 -0700 From: Jiebin Sun To: Namhyung Kim , acme@kernel.org, mingo@redhat.com, peterz@infradead.org Cc: adrian.hunter@intel.com, alexander.shishkin@linux.intel.com, irogers@google.com, james.clark@linaro.org, jolsa@kernel.org, mark.rutland@arm.com, dapeng1.mi@linux.intel.com, thomas.falcon@intel.com, tianyou.li@intel.com, wangyang.guo@intel.com, linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org, Jiebin Sun Subject: [PATCH v4 9/9] perf c2c: document function view in perf-c2c man page Date: Fri, 24 Jul 2026 17:58:42 +0800 Message-ID: <20260724095842.995920-10-jiebin.sun@intel.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260724095842.995920-1-jiebin.sun@intel.com> References: <20260717020530.1645123-1-jiebin.sun@intel.com> <20260724095842.995920-1-jiebin.sun@intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Describe the function view hierarchy (read-side function -> contending writer function -> shared cachelines), the per-level indentation, and the keys, with a worked example. Signed-off-by: Jiebin Sun Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Dapeng Mi Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Falcon Reviewed-by: Tianyou Li Reviewed-by: Wangyang Guo --- tools/perf/Documentation/perf-c2c.txt | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentati= on/perf-c2c.txt index e57a122b8719..ff05eb20a800 100644 --- a/tools/perf/Documentation/perf-c2c.txt +++ b/tools/perf/Documentation/perf-c2c.txt @@ -365,6 +365,52 @@ TUI OUTPUT The TUI output provides interactive interface to navigate through cachelines list and to display offset details. =20 +Pressing the 'TAB' key in the cacheline view switches to the function +view. The function view shows a three-level hierarchy of the same data, +organized around functions rather than cachelines. Every level shows +functions and cachelines only -- code addresses are not shown here; they +remain available in the per-cacheline detail view ('d'). + + Level 1: the read-side function, sorted by Cycles % (estimated load + cycles: HITM, peer-snoop and other-load cycles -- on systems + whose default display mode is peer, such as Arm64, the + peer-snoop component dominates) + Level 2: the functions that write the shared lines, causing the + level-1 function's HITMs, sorted by store count + Level 3: the specific cachelines where the two functions contend + +Each function aggregates all of its code addresses into a single entry, +and a level-2 writer aggregates all of the cachelines it contends over, +so a contending pair is a single row with its total shown -- there is no +need to sum a writer's traffic across cachelines by hand. + +In the function view the 'd' key opens the detail view of the selected +cacheline, 'e'/'+' expands or collapses the current entry, and 'TAB', +'ESC' or 'q' returns to the cacheline view. + +For example, expanding a level-1 function reveals the functions that write +the lines it reads, and expanding one of those reveals the specific shared +cachelines: + + Shared Data Functions Table (18 entries, sorted on Cycles %) + Cycles Store + % count Function / Contending function / Cacheline + ---------------------------------------------------------------------- + - 16.54% 419 - [k] dequeue_pushable_task + 145 - [k] pull_rt_task + 145 0xff2d0082809da080 + 139 - [k] enqueue_pushable_task + 70 0xff2d00a2071f9640 + 69 0xff2d0082809da000 + +Here dequeue_pushable_task pays the read-side HITM penalty (16.54% of the +estimated load cycles). It is contended by pull_rt_task and +enqueue_pushable_task, which write the shared lines; enqueue_pushable_task +contends over two cachelines but stays a single row with its total (139) +shown, and the individual cachelines are underneath. This is the +false-sharing chain that the cacheline view otherwise leaves to be +reconstructed by hand. + For details please refer to the help window by pressing '?' key. =20 CREDITS --=20 2.52.0