hist.c 11.3 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
		u64 total = hists__total_period(hists);
36

37 38
		if (total)
			percent = 100.0 * get_field(he) / total;
39

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
		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);
53
			u64 total = hists__total_period(pair->hists);
54 55 56 57 58 59 60 61 62 63 64 65

			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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 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 field_cmp(u64 field_a, u64 field_b)
{
	if (field_a > field_b)
		return 1;
	if (field_a < field_b)
		return -1;
	return 0;
}

static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
		       hpp_field_fn get_field)
{
	s64 ret;
	int i, nr_members;
	struct perf_evsel *evsel;
	struct hist_entry *pair;
	u64 *fields_a, *fields_b;

	ret = field_cmp(get_field(a), get_field(b));
	if (ret || !symbol_conf.event_group)
		return ret;

	evsel = hists_to_evsel(a->hists);
	if (!perf_evsel__is_group_event(evsel))
		return ret;

	nr_members = evsel->nr_members;
	fields_a = calloc(sizeof(*fields_a), nr_members);
	fields_b = calloc(sizeof(*fields_b), nr_members);

	if (!fields_a || !fields_b)
		goto out;

	list_for_each_entry(pair, &a->pairs.head, pairs.node) {
		evsel = hists_to_evsel(pair->hists);
		fields_a[perf_evsel__group_idx(evsel)] = get_field(pair);
	}

	list_for_each_entry(pair, &b->pairs.head, pairs.node) {
		evsel = hists_to_evsel(pair->hists);
		fields_b[perf_evsel__group_idx(evsel)] = get_field(pair);
	}

	for (i = 1; i < nr_members; i++) {
		ret = field_cmp(fields_a[i], fields_b[i]);
		if (ret)
			break;
	}

out:
	free(fields_a);
	free(fields_b);

	return ret;
}

175
#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) 		\
176
static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused,	\
177 178
			       struct perf_hpp *hpp,			\
			       struct perf_evsel *evsel)		\
179 180 181
{									\
	int len = _min_width;						\
									\
182
	if (symbol_conf.event_group)					\
183
		len = max(len, evsel->nr_members * _unit_width);	\
184
									\
185
	return scnprintf(hpp->buf, hpp->size, "%*s", len, _str);	\
186 187
}

188
#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) 			\
189
static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused,	\
190 191
			      struct perf_hpp *hpp __maybe_unused,	\
			      struct perf_evsel *evsel)			\
192 193 194
{									\
	int len = _min_width;						\
									\
195
	if (symbol_conf.event_group)					\
196
		len = max(len, evsel->nr_members * _unit_width);	\
197
									\
198
	return len;							\
199 200
}

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
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;
}

229 230 231 232 233 234
#define __HPP_COLOR_PERCENT_FN(_type, _field)					\
static u64 he_get_##_field(struct hist_entry *he)				\
{										\
	return he->stat._field;							\
}										\
										\
235 236
static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,		\
			      struct perf_hpp *hpp, struct hist_entry *he) 	\
237
{										\
238
	return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%",		\
239
			  hpp_color_scnprintf, true);				\
240 241
}

242
#define __HPP_ENTRY_PERCENT_FN(_type, _field)					\
243 244
static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused,		\
			      struct perf_hpp *hpp, struct hist_entry *he) 	\
245 246
{										\
	const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%";		\
247
	return __hpp__fmt(hpp, he, he_get_##_field, NULL, fmt,			\
248
			  hpp_entry_scnprintf, true);				\
249 250
}

251 252 253
#define __HPP_SORT_FN(_type, _field)						\
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)	\
{										\
254
	return __hpp__sort(a, b, he_get_##_field);				\
255 256
}

257 258 259 260 261 262
#define __HPP_ENTRY_RAW_FN(_type, _field)					\
static u64 he_get_raw_##_field(struct hist_entry *he)				\
{										\
	return he->stat._field;							\
}										\
										\
263 264
static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused,		\
			      struct perf_hpp *hpp, struct hist_entry *he) 	\
265 266
{										\
	const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64;	\
267
	return __hpp__fmt(hpp, he, he_get_raw_##_field, NULL, fmt,		\
268
			  hpp_entry_scnprintf, false);				\
269 270
}

271 272 273
#define __HPP_SORT_RAW_FN(_type, _field)					\
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)	\
{										\
274
	return __hpp__sort(a, b, he_get_raw_##_field);				\
275 276 277
}


278 279 280 281
#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)					\
282 283
__HPP_ENTRY_PERCENT_FN(_type, _field)					\
__HPP_SORT_FN(_type, _field)
284

285 286 287
#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)				\
288 289
__HPP_ENTRY_RAW_FN(_type, _field)					\
__HPP_SORT_RAW_FN(_type, _field)
290

291

292 293 294 295 296
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)
297

298 299
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
HPP_RAW_FNS(period, "Period", period, 12, 12)
300

301 302 303 304 305 306
static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
			    struct hist_entry *b __maybe_unused)
{
	return 0;
}

307 308 309 310 311
#define HPP__COLOR_PRINT_FNS(_name)			\
	{						\
		.header	= hpp__header_ ## _name,	\
		.width	= hpp__width_ ## _name,		\
		.color	= hpp__color_ ## _name,		\
312 313 314 315
		.entry	= hpp__entry_ ## _name,		\
		.cmp	= hpp__nop_cmp,			\
		.collapse = hpp__nop_cmp,		\
		.sort	= hpp__sort_ ## _name,		\
316
	}
317

318 319 320 321
#define HPP__PRINT_FNS(_name)				\
	{						\
		.header	= hpp__header_ ## _name,	\
		.width	= hpp__width_ ## _name,		\
322 323 324 325
		.entry	= hpp__entry_ ## _name,		\
		.cmp	= hpp__nop_cmp,			\
		.collapse = hpp__nop_cmp,		\
		.sort	= hpp__sort_ ## _name,		\
326
	}
327 328

struct perf_hpp_fmt perf_hpp__format[] = {
329 330 331 332 333 334
	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),
335
	HPP__PRINT_FNS(period)
336 337
};

338
LIST_HEAD(perf_hpp__list);
339
LIST_HEAD(perf_hpp__sort_list);
340

341

342 343 344
#undef HPP__COLOR_PRINT_FNS
#undef HPP__PRINT_FNS

345 346 347 348 349 350 351 352 353 354
#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


355
void perf_hpp__init(void)
356
{
357 358 359 360 361 362 363 364
	struct list_head *list;
	int i;

	for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
		INIT_LIST_HEAD(&perf_hpp__format[i].list);
		INIT_LIST_HEAD(&perf_hpp__format[i].sort_list);
	}

365 366
	perf_hpp__column_enable(PERF_HPP__OVERHEAD);

367
	if (symbol_conf.show_cpu_utilization) {
368 369
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS);
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);
370 371

		if (perf_guest) {
372 373
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS);
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US);
374 375 376 377
		}
	}

	if (symbol_conf.show_nr_samples)
378
		perf_hpp__column_enable(PERF_HPP__SAMPLES);
379 380

	if (symbol_conf.show_total_period)
381
		perf_hpp__column_enable(PERF_HPP__PERIOD);
382 383 384 385 386 387 388

	/* prepend overhead field for backward compatiblity.  */
	list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
	if (list_empty(list))
		list_add(list, &perf_hpp__sort_list);

	perf_hpp__setup_output_field();
389
}
390

391 392 393 394 395
void perf_hpp__column_register(struct perf_hpp_fmt *format)
{
	list_add_tail(&format->list, &perf_hpp__list);
}

396 397 398 399 400
void perf_hpp__register_sort_field(struct perf_hpp_fmt *format)
{
	list_add_tail(&format->sort_list, &perf_hpp__sort_list);
}

401
void perf_hpp__column_enable(unsigned col)
402 403
{
	BUG_ON(col >= PERF_HPP__MAX_INDEX);
404
	perf_hpp__column_register(&perf_hpp__format[col]);
405 406
}

407 408 409 410 411 412 413 414 415 416 417
void perf_hpp__setup_output_field(void)
{
	struct perf_hpp_fmt *fmt;

	/* append sort keys to output field */
	perf_hpp__for_each_sort_list(fmt) {
		if (list_empty(&fmt->list))
			perf_hpp__column_register(fmt);
	}
}

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
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;
}
436 437 438 439 440 441

/*
 * See hists__fprintf to match the column widths
 */
unsigned int hists__sort_list_width(struct hists *hists)
{
442
	struct perf_hpp_fmt *fmt;
443
	struct sort_entry *se;
444
	int i = 0, ret = 0;
445
	struct perf_hpp dummy_hpp;
446

447
	perf_hpp__for_each_format(fmt) {
448 449 450
		if (i)
			ret += 2;

451
		ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
452 453 454 455 456 457 458 459 460 461 462
	}

	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;
}