hist.c 10.8 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
	perf_hpp__column_enable(PERF_HPP__OVERHEAD);

359
	if (symbol_conf.show_cpu_utilization) {
360 361
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS);
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);
362 363

		if (perf_guest) {
364 365
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS);
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US);
366 367 368 369
		}
	}

	if (symbol_conf.show_nr_samples)
370
		perf_hpp__column_enable(PERF_HPP__SAMPLES);
371 372

	if (symbol_conf.show_total_period)
373
		perf_hpp__column_enable(PERF_HPP__PERIOD);
374
}
375

376 377 378 379 380
void perf_hpp__column_register(struct perf_hpp_fmt *format)
{
	list_add_tail(&format->list, &perf_hpp__list);
}

381 382 383 384 385
void perf_hpp__register_sort_field(struct perf_hpp_fmt *format)
{
	list_add_tail(&format->sort_list, &perf_hpp__sort_list);
}

386
void perf_hpp__column_enable(unsigned col)
387 388
{
	BUG_ON(col >= PERF_HPP__MAX_INDEX);
389
	perf_hpp__column_register(&perf_hpp__format[col]);
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
}

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;
}
410 411 412 413 414 415

/*
 * See hists__fprintf to match the column widths
 */
unsigned int hists__sort_list_width(struct hists *hists)
{
416
	struct perf_hpp_fmt *fmt;
417
	struct sort_entry *se;
418
	int i = 0, ret = 0;
419
	struct perf_hpp dummy_hpp;
420

421
	perf_hpp__for_each_format(fmt) {
422 423 424
		if (i)
			ret += 2;

425
		ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
426 427 428 429 430 431 432 433 434 435 436
	}

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