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

209 210 211 212 213
#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)
214

215 216 217 218
#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)
219

220

221 222 223 224 225
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)
226

227 228
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
HPP_RAW_FNS(period, "Period", period, 12, 12)
229

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

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

struct perf_hpp_fmt perf_hpp__format[] = {
246 247 248 249 250 251
	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),
252
	HPP__PRINT_FNS(period)
253 254
};

255 256
LIST_HEAD(perf_hpp__list);

257

258 259 260
#undef HPP__COLOR_PRINT_FNS
#undef HPP__PRINT_FNS

261 262 263 264 265 266 267 268 269 270
#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


271
void perf_hpp__init(void)
272
{
273 274
	perf_hpp__column_enable(PERF_HPP__OVERHEAD);

275
	if (symbol_conf.show_cpu_utilization) {
276 277
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS);
		perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);
278 279

		if (perf_guest) {
280 281
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS);
			perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US);
282 283 284 285
		}
	}

	if (symbol_conf.show_nr_samples)
286
		perf_hpp__column_enable(PERF_HPP__SAMPLES);
287 288

	if (symbol_conf.show_total_period)
289
		perf_hpp__column_enable(PERF_HPP__PERIOD);
290
}
291

292 293 294 295 296 297
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)
298 299
{
	BUG_ON(col >= PERF_HPP__MAX_INDEX);
300
	perf_hpp__column_register(&perf_hpp__format[col]);
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
}

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;
}
321 322 323 324 325 326

/*
 * See hists__fprintf to match the column widths
 */
unsigned int hists__sort_list_width(struct hists *hists)
{
327
	struct perf_hpp_fmt *fmt;
328
	struct sort_entry *se;
329
	int i = 0, ret = 0;
330
	struct perf_hpp dummy_hpp;
331

332
	perf_hpp__for_each_format(fmt) {
333 334 335
		if (i)
			ret += 2;

336
		ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
337 338 339 340 341 342 343 344 345 346 347
	}

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