hist.c 8.7 KB
Newer Older
1
#include <math.h>
2
#include <linux/compiler.h>
3 4 5 6

#include "../util/hist.h"
#include "../util/util.h"
#include "../util/sort.h"
7
#include "../util/evsel.h"
8 9 10

/* hist period print (hpp) functions */

11 12 13 14 15 16 17
#define hpp__call_print_fn(hpp, fn, fmt, ...)			\
({								\
	int __ret = fn(hpp, fmt, ##__VA_ARGS__);		\
	advance_hpp(hpp, __ret);				\
	__ret;							\
})

18
int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
19
	       hpp_field_fn get_field, hpp_callback_fn callback,
20
	       const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent)
21
{
22
	int ret = 0;
23
	struct hists *hists = he->hists;
24
	struct perf_evsel *evsel = hists_to_evsel(hists);
25 26
	char *buf = hpp->buf;
	size_t size = hpp->size;
27

28 29 30 31 32
	if (callback) {
		ret = callback(hpp, true);
		advance_hpp(hpp, ret);
	}

33 34
	if (fmt_percent) {
		double percent = 0.0;
35

36 37 38 39
		if (hists->stats.total_period)
			percent = 100.0 * get_field(he) /
				  hists->stats.total_period;

40
		ret += hpp__call_print_fn(hpp, print_fn, fmt, percent);
41
	} else
42
		ret += hpp__call_print_fn(hpp, print_fn, fmt, get_field(he));
43

44
	if (perf_evsel__is_group_event(evsel)) {
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
		int prev_idx, idx_delta;
		struct hist_entry *pair;
		int nr_members = evsel->nr_members;

		prev_idx = perf_evsel__group_idx(evsel);

		list_for_each_entry(pair, &he->pairs.head, pairs.node) {
			u64 period = get_field(pair);
			u64 total = pair->hists->stats.total_period;

			if (!total)
				continue;

			evsel = hists_to_evsel(pair->hists);
			idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;

			while (idx_delta--) {
				/*
				 * zero-fill group members in the middle which
				 * have no sample
				 */
66
				if (fmt_percent) {
67 68
					ret += hpp__call_print_fn(hpp, print_fn,
								  fmt, 0.0);
69
				} else {
70 71
					ret += hpp__call_print_fn(hpp, print_fn,
								  fmt, 0ULL);
72
				}
73 74
			}

75 76 77 78 79 80 81
			if (fmt_percent) {
				ret += hpp__call_print_fn(hpp, print_fn, fmt,
							  100.0 * period / total);
			} else {
				ret += hpp__call_print_fn(hpp, print_fn, fmt,
							  period);
			}
82 83 84 85 86 87 88 89 90 91

			prev_idx = perf_evsel__group_idx(evsel);
		}

		idx_delta = nr_members - prev_idx - 1;

		while (idx_delta--) {
			/*
			 * zero-fill group members at last which have no sample
			 */
92
			if (fmt_percent) {
93 94
				ret += hpp__call_print_fn(hpp, print_fn,
							  fmt, 0.0);
95
			} else {
96 97
				ret += hpp__call_print_fn(hpp, print_fn,
							  fmt, 0ULL);
98
			}
99 100
		}
	}
101

102 103 104 105 106 107 108
	if (callback) {
		int __ret = callback(hpp, false);

		advance_hpp(hpp, __ret);
		ret += __ret;
	}

109 110 111 112 113 114 115
	/*
	 * Restore original buf and size as it's where caller expects
	 * the result will be saved.
	 */
	hpp->buf = buf;
	hpp->size = size;

116
	return ret;
117 118
}

119
#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) 		\
120 121
static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused,	\
			       struct perf_hpp *hpp)			\
122 123 124
{									\
	int len = _min_width;						\
									\
125 126 127 128 129
	if (symbol_conf.event_group) {					\
		struct perf_evsel *evsel = hpp->ptr;			\
									\
		len = max(len, evsel->nr_members * _unit_width);	\
	}								\
130
	return scnprintf(hpp->buf, hpp->size, "%*s", len, _str);	\
131 132
}

133
#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) 			\
134 135
static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused,	\
			      struct perf_hpp *hpp __maybe_unused)	\
136 137 138
{									\
	int len = _min_width;						\
									\
139 140 141 142 143
	if (symbol_conf.event_group) {					\
		struct perf_evsel *evsel = hpp->ptr;			\
									\
		len = max(len, evsel->nr_members * _unit_width);	\
	}								\
144
	return len;							\
145 146
}

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
{
	va_list args;
	ssize_t ssize = hpp->size;
	double percent;
	int ret;

	va_start(args, fmt);
	percent = va_arg(args, double);
	ret = value_color_snprintf(hpp->buf, hpp->size, fmt, percent);
	va_end(args);

	return (ret >= ssize) ? (ssize - 1) : ret;
}

static int hpp_entry_scnprintf(struct perf_hpp *hpp, const char *fmt, ...)
{
	va_list args;
	ssize_t ssize = hpp->size;
	int ret;

	va_start(args, fmt);
	ret = vsnprintf(hpp->buf, hpp->size, fmt, args);
	va_end(args);

	return (ret >= ssize) ? (ssize - 1) : ret;
}

175 176 177 178 179 180
#define __HPP_COLOR_PERCENT_FN(_type, _field)					\
static u64 he_get_##_field(struct hist_entry *he)				\
{										\
	return he->stat._field;							\
}										\
										\
181 182
static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,		\
			      struct perf_hpp *hpp, struct hist_entry *he) 	\
183
{										\
184
	return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%",		\
185
			  hpp_color_scnprintf, true);				\
186 187
}

188
#define __HPP_ENTRY_PERCENT_FN(_type, _field)					\
189 190
static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused,		\
			      struct perf_hpp *hpp, struct hist_entry *he) 	\
191 192
{										\
	const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%";		\
193
	return __hpp__fmt(hpp, he, he_get_##_field, NULL, fmt,			\
194
			  hpp_entry_scnprintf, true);				\
195 196
}

197 198 199 200 201 202
#define __HPP_ENTRY_RAW_FN(_type, _field)					\
static u64 he_get_raw_##_field(struct hist_entry *he)				\
{										\
	return he->stat._field;							\
}										\
										\
203 204
static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused,		\
			      struct perf_hpp *hpp, struct hist_entry *he) 	\
205 206
{										\
	const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64;	\
207
	return __hpp__fmt(hpp, he, he_get_raw_##_field, NULL, fmt,		\
208
			  hpp_entry_scnprintf, false);				\
209 210
}

211 212 213 214 215
#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width)	\
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width)			\
__HPP_WIDTH_FN(_type, _min_width, _unit_width)				\
__HPP_COLOR_PERCENT_FN(_type, _field)					\
__HPP_ENTRY_PERCENT_FN(_type, _field)
216

217 218 219 220
#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width)	\
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width)			\
__HPP_WIDTH_FN(_type, _min_width, _unit_width)				\
__HPP_ENTRY_RAW_FN(_type, _field)
221

222

223 224 225 226 227
HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
228

229 230
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
HPP_RAW_FNS(period, "Period", period, 12, 12)
231

232 233 234 235 236 237 238
#define HPP__COLOR_PRINT_FNS(_name)			\
	{						\
		.header	= hpp__header_ ## _name,	\
		.width	= hpp__width_ ## _name,		\
		.color	= hpp__color_ ## _name,		\
		.entry	= hpp__entry_ ## _name		\
	}
239

240 241 242 243 244 245
#define HPP__PRINT_FNS(_name)				\
	{						\
		.header	= hpp__header_ ## _name,	\
		.width	= hpp__width_ ## _name,		\
		.entry	= hpp__entry_ ## _name		\
	}
246 247

struct perf_hpp_fmt perf_hpp__format[] = {
248 249 250 251 252 253
	HPP__COLOR_PRINT_FNS(overhead),
	HPP__COLOR_PRINT_FNS(overhead_sys),
	HPP__COLOR_PRINT_FNS(overhead_us),
	HPP__COLOR_PRINT_FNS(overhead_guest_sys),
	HPP__COLOR_PRINT_FNS(overhead_guest_us),
	HPP__PRINT_FNS(samples),
254
	HPP__PRINT_FNS(period)
255 256
};

257 258
LIST_HEAD(perf_hpp__list);

259

260 261 262
#undef HPP__COLOR_PRINT_FNS
#undef HPP__PRINT_FNS

263 264 265 266 267 268 269 270 271 272
#undef HPP_PERCENT_FNS
#undef HPP_RAW_FNS

#undef __HPP_HEADER_FN
#undef __HPP_WIDTH_FN
#undef __HPP_COLOR_PERCENT_FN
#undef __HPP_ENTRY_PERCENT_FN
#undef __HPP_ENTRY_RAW_FN


273
void perf_hpp__init(void)
274
{
275 276
	perf_hpp__column_enable(PERF_HPP__OVERHEAD);

277
	if (symbol_conf.show_cpu_utilization) {
278 279
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS);
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);
280 281

		if (perf_guest) {
282 283
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS);
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US);
284 285 286 287
		}
	}

	if (symbol_conf.show_nr_samples)
288
		perf_hpp__column_enable(PERF_HPP__SAMPLES);
289 290

	if (symbol_conf.show_total_period)
291
		perf_hpp__column_enable(PERF_HPP__PERIOD);
292
}
293

294 295 296 297 298 299
void perf_hpp__column_register(struct perf_hpp_fmt *format)
{
	list_add_tail(&format->list, &perf_hpp__list);
}

void perf_hpp__column_enable(unsigned col)
300 301
{
	BUG_ON(col >= PERF_HPP__MAX_INDEX);
302
	perf_hpp__column_register(&perf_hpp__format[col]);
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
}

int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,
			      struct hists *hists)
{
	const char *sep = symbol_conf.field_sep;
	struct sort_entry *se;
	int ret = 0;

	list_for_each_entry(se, &hist_entry__sort_list, list) {
		if (se->elide)
			continue;

		ret += scnprintf(s + ret, size - ret, "%s", sep ?: "  ");
		ret += se->se_snprintf(he, s + ret, size - ret,
				       hists__col_len(hists, se->se_width_idx));
	}

	return ret;
}
323 324 325 326 327 328

/*
 * See hists__fprintf to match the column widths
 */
unsigned int hists__sort_list_width(struct hists *hists)
{
329
	struct perf_hpp_fmt *fmt;
330
	struct sort_entry *se;
331
	int i = 0, ret = 0;
332 333 334
	struct perf_hpp dummy_hpp = {
		.ptr	= hists_to_evsel(hists),
	};
335

336
	perf_hpp__for_each_format(fmt) {
337 338 339
		if (i)
			ret += 2;

340
		ret += fmt->width(fmt, &dummy_hpp);
341 342 343 344 345 346 347 348 349 350 351
	}

	list_for_each_entry(se, &hist_entry__sort_list, list)
		if (!se->elide)
			ret += 2 + hists__col_len(hists, se->se_width_idx);

	if (verbose) /* Addr + origin */
		ret += 3 + BITS_PER_LONG / 4;

	return ret;
}