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

132
#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) 			\
133
static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused,	\
134 135
			      struct perf_hpp *hpp __maybe_unused,	\
			      struct perf_evsel *evsel)			\
136 137 138
{									\
	int len = _min_width;						\
									\
139
	if (symbol_conf.event_group)					\
140
		len = max(len, evsel->nr_members * _unit_width);	\
141
									\
142
	return len;							\
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
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;
}

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

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

195 196 197 198 199 200 201 202
#define __HPP_SORT_FN(_type, _field)						\
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)	\
{										\
	s64 __a = he_get_##_field(a);						\
	s64 __b = he_get_##_field(b);						\
	return __a - __b;							\
}

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

217 218 219 220 221 222 223 224 225
#define __HPP_SORT_RAW_FN(_type, _field)					\
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b)	\
{										\
	s64 __a = he_get_raw_##_field(a);					\
	s64 __b = he_get_raw_##_field(b);					\
	return __a - __b;							\
}


226 227 228 229
#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)					\
230 231
__HPP_ENTRY_PERCENT_FN(_type, _field)					\
__HPP_SORT_FN(_type, _field)
232

233 234 235
#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)				\
236 237
__HPP_ENTRY_RAW_FN(_type, _field)					\
__HPP_SORT_RAW_FN(_type, _field)
238

239

240 241 242 243 244
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)
245

246 247
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
HPP_RAW_FNS(period, "Period", period, 12, 12)
248

249 250 251 252 253 254
static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused,
			    struct hist_entry *b __maybe_unused)
{
	return 0;
}

255 256 257 258 259
#define HPP__COLOR_PRINT_FNS(_name)			\
	{						\
		.header	= hpp__header_ ## _name,	\
		.width	= hpp__width_ ## _name,		\
		.color	= hpp__color_ ## _name,		\
260 261 262 263
		.entry	= hpp__entry_ ## _name,		\
		.cmp	= hpp__nop_cmp,			\
		.collapse = hpp__nop_cmp,		\
		.sort	= hpp__sort_ ## _name,		\
264
	}
265

266 267 268 269
#define HPP__PRINT_FNS(_name)				\
	{						\
		.header	= hpp__header_ ## _name,	\
		.width	= hpp__width_ ## _name,		\
270 271 272 273
		.entry	= hpp__entry_ ## _name,		\
		.cmp	= hpp__nop_cmp,			\
		.collapse = hpp__nop_cmp,		\
		.sort	= hpp__sort_ ## _name,		\
274
	}
275 276

struct perf_hpp_fmt perf_hpp__format[] = {
277 278 279 280 281 282
	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),
283
	HPP__PRINT_FNS(period)
284 285
};

286 287
LIST_HEAD(perf_hpp__list);

288

289 290 291
#undef HPP__COLOR_PRINT_FNS
#undef HPP__PRINT_FNS

292 293 294 295 296 297 298 299 300 301
#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


302
void perf_hpp__init(void)
303
{
304 305
	perf_hpp__column_enable(PERF_HPP__OVERHEAD);

306
	if (symbol_conf.show_cpu_utilization) {
307 308
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS);
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);
309 310

		if (perf_guest) {
311 312
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS);
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US);
313 314 315 316
		}
	}

	if (symbol_conf.show_nr_samples)
317
		perf_hpp__column_enable(PERF_HPP__SAMPLES);
318 319

	if (symbol_conf.show_total_period)
320
		perf_hpp__column_enable(PERF_HPP__PERIOD);
321
}
322

323 324 325 326 327 328
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)
329 330
{
	BUG_ON(col >= PERF_HPP__MAX_INDEX);
331
	perf_hpp__column_register(&perf_hpp__format[col]);
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
}

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;
}
352 353 354 355 356 357

/*
 * See hists__fprintf to match the column widths
 */
unsigned int hists__sort_list_width(struct hists *hists)
{
358
	struct perf_hpp_fmt *fmt;
359
	struct sort_entry *se;
360
	int i = 0, ret = 0;
361
	struct perf_hpp dummy_hpp;
362

363
	perf_hpp__for_each_format(fmt) {
364 365 366
		if (i)
			ret += 2;

367
		ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
368 369 370 371 372 373 374 375 376 377 378
	}

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