Commit b1fc83ca authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo
Browse files

perf hist: Implement output fields for mem stats



This is a preparation for later changes to support mem_stat output.  The
new fields will need two lines for the header - the first line will show
type of mem stat and the second line will show the name of each item
which is returned by mem_stat_name().

Each element in the mem_stat array will be printed in percentage for the
hist_entry and their sum would be 100%.

Add new output field dimension only for SORT_MODE__MEM using mem_stat.

To handle possible name conflict with existing sort keys, move the order
of checking output field dimensions after the sort dimensions when it
looks for sort keys.

Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Link: https://lore.kernel.org/r/20250430205548.789750-7-namhyung@kernel.org


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 9fcb43e2
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -1266,6 +1266,16 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
			_fmttype);					\
}

#define __HPP_COLOR_MEM_STAT_FN(_name, _type)				\
static int								\
hist_browser__hpp_color_mem_stat_##_name(struct perf_hpp_fmt *fmt,	\
					 struct perf_hpp *hpp,		\
					 struct hist_entry *he)		\
{									\
	return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type,	\
				 " %5.1f%%", __hpp__slsmg_color_printf);\
}

__HPP_COLOR_PERCENT_FN(overhead, period, PERF_HPP_FMT_TYPE__PERCENT)
__HPP_COLOR_PERCENT_FN(latency, latency, PERF_HPP_FMT_TYPE__LATENCY)
__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT)
@@ -1277,6 +1287,7 @@ __HPP_COLOR_ACC_PERCENT_FN(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY)

#undef __HPP_COLOR_PERCENT_FN
#undef __HPP_COLOR_ACC_PERCENT_FN
#undef __HPP_COLOR_MEM_STAT_FN

void hist_browser__init_hpp(void)
{
+155 −3
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include "../util/evsel.h"
#include "../util/evlist.h"
#include "../util/mem-events.h"
#include "../util/string2.h"
#include "../util/thread.h"
#include "../util/util.h"

@@ -151,6 +152,45 @@ int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
	return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmtype);
}

int hpp__fmt_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
		      struct hist_entry *he, enum mem_stat_type mst,
		      const char *fmtstr, hpp_snprint_fn print_fn)
{
	struct hists *hists = he->hists;
	int mem_stat_idx = -1;
	char *buf = hpp->buf;
	size_t size = hpp->size;
	u64 total = 0;
	int ret = 0;

	for (int i = 0; i < hists->nr_mem_stats; i++) {
		if (hists->mem_stat_types[i] == mst) {
			mem_stat_idx = i;
			break;
		}
	}
	assert(mem_stat_idx != -1);

	for (int i = 0; i < MEM_STAT_LEN; i++)
		total += he->mem_stat[mem_stat_idx].entries[i];
	assert(total != 0);

	for (int i = 0; i < MEM_STAT_LEN; i++) {
		u64 val = he->mem_stat[mem_stat_idx].entries[i];

		ret += hpp__call_print_fn(hpp, print_fn, fmtstr, 100.0 * val / total);
	}

	/*
	 * Restore original buf and size as it's where caller expects
	 * the result will be saved.
	 */
	hpp->buf = buf;
	hpp->size = size;

	return ret;
}

static int field_cmp(u64 field_a, u64 field_b)
{
	if (field_a > field_b)
@@ -295,6 +335,23 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b,
	return ret;
}

static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt);

static enum mem_stat_type hpp__mem_stat_type(struct perf_hpp_fmt *fmt)
{
	if (!perf_hpp__is_mem_stat_entry(fmt))
		return -1;

	pr_debug("Should not reach here\n");
	return -1;
}

static int64_t hpp__sort_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused,
				  struct hist_entry *a, struct hist_entry *b)
{
	return a->stat.period - b->stat.period;
}

static int hpp__width_fn(struct perf_hpp_fmt *fmt,
			 struct perf_hpp *hpp __maybe_unused,
			 struct hists *hists)
@@ -334,6 +391,45 @@ static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
	return scnprintf(hpp->buf, hpp->size, "%*s", len, hdr);
}

static int hpp__header_mem_stat_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
				   struct hists *hists, int line,
				   int *span __maybe_unused)
{
	char *buf = hpp->buf;
	int ret = 0;
	int len;
	enum mem_stat_type mst = hpp__mem_stat_type(fmt);

	(void)hists;
	if (line == 0) {
		int left, right;

		len = fmt->len;
		left = (len - strlen(fmt->name)) / 2 - 1;
		right = len - left - strlen(fmt->name) - 2;

		if (left < 0)
			left = 0;
		if (right < 0)
			right = 0;

		return scnprintf(hpp->buf, hpp->size, "%.*s %s %.*s",
				 left, graph_dotted_line, fmt->name, right, graph_dotted_line);
	}

	len = hpp->size;
	for (int i = 0; i < MEM_STAT_LEN; i++) {
		int printed;

		printed = scnprintf(buf, len, "%*s", MEM_STAT_PRINT_LEN,
				    mem_stat_name(mst, i));
		ret += printed;
		buf += printed;
		len -= printed;
	}
	return ret;
}

int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
{
	va_list args;
@@ -459,6 +555,23 @@ static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
	return __hpp__sort(a, b, he_get_##_field);				\
}

#define __HPP_COLOR_MEM_STAT_FN(_name, _type)					\
static int hpp__color_mem_stat_##_name(struct perf_hpp_fmt *fmt,		\
				       struct perf_hpp *hpp,			\
				       struct hist_entry *he)			\
{										\
	return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type,		\
				 " %5.1f%%", hpp_color_scnprintf);		\
}

#define __HPP_ENTRY_MEM_STAT_FN(_name, _type)					\
static int hpp__entry_mem_stat_##_name(struct perf_hpp_fmt *fmt, 		\
				       struct perf_hpp *hpp,			\
				       struct hist_entry *he)			\
{										\
	return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type,		\
				 " %5.1f%%", hpp_entry_scnprintf);		\
}

#define HPP_PERCENT_FNS(_type, _field, _fmttype)			\
__HPP_COLOR_PERCENT_FN(_type, _field, _fmttype)				\
@@ -478,6 +591,10 @@ __HPP_SORT_RAW_FN(_type, _field)
__HPP_ENTRY_AVERAGE_FN(_type, _field)					\
__HPP_SORT_AVERAGE_FN(_type, _field)

#define HPP_MEM_STAT_FNS(_name, _type)					\
__HPP_COLOR_MEM_STAT_FN(_name, _type)					\
__HPP_ENTRY_MEM_STAT_FN(_name, _type)

HPP_PERCENT_FNS(overhead, period, PERF_HPP_FMT_TYPE__PERCENT)
HPP_PERCENT_FNS(latency, latency, PERF_HPP_FMT_TYPE__LATENCY)
HPP_PERCENT_FNS(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT)
@@ -494,6 +611,8 @@ HPP_AVERAGE_FNS(weight1, weight1)
HPP_AVERAGE_FNS(weight2, weight2)
HPP_AVERAGE_FNS(weight3, weight3)

HPP_MEM_STAT_FNS(unknown, UNKNOWN)  /* placeholder */

static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
			    struct hist_entry *a __maybe_unused,
			    struct hist_entry *b __maybe_unused)
@@ -503,8 +622,7 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,

static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt)
{
	(void)fmt;
	return false;
	return fmt->sort == hpp__sort_mem_stat;
}

static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a)
@@ -520,6 +638,14 @@ static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
	return a->idx == b->idx;
}

static bool hpp__equal_mem_stat(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
{
	if (!perf_hpp__is_mem_stat_entry(a) || !perf_hpp__is_mem_stat_entry(b))
		return false;

	return a->entry == b->entry;
}

#define HPP__COLOR_PRINT_FNS(_name, _fn, _idx)		\
	{						\
		.name   = _name,			\
@@ -561,6 +687,20 @@ static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
		.equal	= hpp__equal,			\
	}

#define HPP__MEM_STAT_PRINT_FNS(_name, _fn, _type)	\
	{						\
		.name   = _name,			\
		.header	= hpp__header_mem_stat_fn,	\
		.width	= hpp__width_fn,		\
		.color	= hpp__color_mem_stat_ ## _fn,	\
		.entry	= hpp__entry_mem_stat_ ## _fn,	\
		.cmp	= hpp__nop_cmp,			\
		.collapse = hpp__nop_cmp,		\
		.sort	= hpp__sort_mem_stat,		\
		.idx	= PERF_HPP__MEM_STAT_ ## _type,	\
		.equal	= hpp__equal_mem_stat,		\
	}

struct perf_hpp_fmt perf_hpp__format[] = {
	HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD),
	HPP__COLOR_PRINT_FNS("Latency", latency, LATENCY),
@@ -575,6 +715,7 @@ struct perf_hpp_fmt perf_hpp__format[] = {
	HPP__PRINT_FNS("Weight1", weight1, WEIGHT1),
	HPP__PRINT_FNS("Weight2", weight2, WEIGHT2),
	HPP__PRINT_FNS("Weight3", weight3, WEIGHT3),
	HPP__MEM_STAT_PRINT_FNS("Unknown", unknown, UNKNOWN),  /* placeholder */
};

struct perf_hpp_list perf_hpp_list = {
@@ -586,11 +727,13 @@ struct perf_hpp_list perf_hpp_list = {
#undef HPP__COLOR_PRINT_FNS
#undef HPP__COLOR_ACC_PRINT_FNS
#undef HPP__PRINT_FNS
#undef HPP__MEM_STAT_PRINT_FNS

#undef HPP_PERCENT_FNS
#undef HPP_PERCENT_ACC_FNS
#undef HPP_RAW_FNS
#undef HPP_AVERAGE_FNS
#undef HPP_MEM_STAT_FNS

#undef __HPP_HEADER_FN
#undef __HPP_WIDTH_FN
@@ -600,6 +743,9 @@ struct perf_hpp_list perf_hpp_list = {
#undef __HPP_ENTRY_ACC_PERCENT_FN
#undef __HPP_ENTRY_RAW_FN
#undef __HPP_ENTRY_AVERAGE_FN
#undef __HPP_COLOR_MEM_STAT_FN
#undef __HPP_ENTRY_MEM_STAT_FN

#undef __HPP_SORT_FN
#undef __HPP_SORT_ACC_FN
#undef __HPP_SORT_RAW_FN
@@ -924,6 +1070,10 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists)
		fmt->len = 8;
		break;

	case PERF_HPP__MEM_STAT_UNKNOWN:  /* placeholder */
		fmt->len = MEM_STAT_LEN * MEM_STAT_PRINT_LEN;
		break;

	default:
		break;
	}
@@ -1042,12 +1192,14 @@ int perf_hpp__alloc_mem_stats(struct perf_hpp_list *list, struct evlist *evlist)
			continue;

		assert(nr_mem_stats < ARRAY_SIZE(mst));
		mst[nr_mem_stats++] = PERF_MEM_STAT_UNKNOWN;
		mst[nr_mem_stats++] = hpp__mem_stat_type(fmt);
	}

	if (nr_mem_stats == 0)
		return 0;

	list->nr_header_lines = 2;

	evlist__for_each_entry(evlist, evsel) {
		struct hists *hists = evsel__hists(evsel);

+4 −0
Original line number Diff line number Diff line
@@ -587,6 +587,7 @@ enum {
	PERF_HPP__WEIGHT1,
	PERF_HPP__WEIGHT2,
	PERF_HPP__WEIGHT3,
	PERF_HPP__MEM_STAT_UNKNOWN,  /* placeholder */

	PERF_HPP__MAX_INDEX
};
@@ -656,6 +657,9 @@ int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
		 struct hist_entry *he, hpp_field_fn get_field,
		 const char *fmtstr, hpp_snprint_fn print_fn,
		 enum perf_hpp_fmt_type fmtype);
int hpp__fmt_mem_stat(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
		      struct hist_entry *he, enum mem_stat_type mst,
		      const char *fmtstr, hpp_snprint_fn print_fn);

static inline void advance_hpp(struct perf_hpp *hpp, int inc)
{
+12 −0
Original line number Diff line number Diff line
@@ -817,3 +817,15 @@ int mem_stat_index(const enum mem_stat_type mst, const u64 val)
	(void)val;
	return -1;
}

/* To align output, returned string should be shorter than MEM_STAT_PRINT_LEN */
const char *mem_stat_name(const enum mem_stat_type mst, const int idx)
{
	switch (mst) {
	case PERF_MEM_STAT_UNKNOWN:
	default:
		break;
	}
	(void)idx;
	return "N/A";
}
+3 −0
Original line number Diff line number Diff line
@@ -93,6 +93,9 @@ enum mem_stat_type {
	PERF_MEM_STAT_UNKNOWN,  /* placeholder */
};

#define MEM_STAT_PRINT_LEN  7  /* 1 space + 5 digits + 1 percent sign */

int mem_stat_index(const enum mem_stat_type mst, const u64 data_src);
const char *mem_stat_name(const enum mem_stat_type mst, const int idx);

#endif /* __PERF_MEM_EVENTS_H */
Loading