1 #include "annotate.h"
2 #include "util.h"
3 #include "build-id.h"
4 #include "hist.h"
5 #include "session.h"
6 #include "sort.h"
7 #include <math.h>
8 
9 static bool hists__filter_entry_by_dso(struct hists *hists,
10 				       struct hist_entry *he);
11 static bool hists__filter_entry_by_thread(struct hists *hists,
12 					  struct hist_entry *he);
13 static bool hists__filter_entry_by_symbol(struct hists *hists,
14 					  struct hist_entry *he);
15 
16 enum hist_filter {
17 	HIST_FILTER__DSO,
18 	HIST_FILTER__THREAD,
19 	HIST_FILTER__PARENT,
20 	HIST_FILTER__SYMBOL,
21 };
22 
23 struct callchain_param	callchain_param = {
24 	.mode	= CHAIN_GRAPH_REL,
25 	.min_percent = 0.5,
26 	.order  = ORDER_CALLEE
27 };
28 
hists__col_len(struct hists * hists,enum hist_column col)29 u16 hists__col_len(struct hists *hists, enum hist_column col)
30 {
31 	return hists->col_len[col];
32 }
33 
hists__set_col_len(struct hists * hists,enum hist_column col,u16 len)34 void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len)
35 {
36 	hists->col_len[col] = len;
37 }
38 
hists__new_col_len(struct hists * hists,enum hist_column col,u16 len)39 bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len)
40 {
41 	if (len > hists__col_len(hists, col)) {
42 		hists__set_col_len(hists, col, len);
43 		return true;
44 	}
45 	return false;
46 }
47 
hists__reset_col_len(struct hists * hists)48 static void hists__reset_col_len(struct hists *hists)
49 {
50 	enum hist_column col;
51 
52 	for (col = 0; col < HISTC_NR_COLS; ++col)
53 		hists__set_col_len(hists, col, 0);
54 }
55 
hists__set_unres_dso_col_len(struct hists * hists,int dso)56 static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
57 {
58 	const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
59 
60 	if (hists__col_len(hists, dso) < unresolved_col_width &&
61 	    !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
62 	    !symbol_conf.dso_list)
63 		hists__set_col_len(hists, dso, unresolved_col_width);
64 }
65 
hists__calc_col_len(struct hists * hists,struct hist_entry * h)66 static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
67 {
68 	const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
69 	u16 len;
70 
71 	if (h->ms.sym)
72 		hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
73 	else
74 		hists__set_unres_dso_col_len(hists, HISTC_DSO);
75 
76 	len = thread__comm_len(h->thread);
77 	if (hists__new_col_len(hists, HISTC_COMM, len))
78 		hists__set_col_len(hists, HISTC_THREAD, len + 6);
79 
80 	if (h->ms.map) {
81 		len = dso__name_len(h->ms.map->dso);
82 		hists__new_col_len(hists, HISTC_DSO, len);
83 	}
84 
85 	if (h->branch_info) {
86 		int symlen;
87 		/*
88 		 * +4 accounts for '[x] ' priv level info
89 		 * +2 account of 0x prefix on raw addresses
90 		 */
91 		if (h->branch_info->from.sym) {
92 			symlen = (int)h->branch_info->from.sym->namelen + 4;
93 			hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
94 
95 			symlen = dso__name_len(h->branch_info->from.map->dso);
96 			hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
97 		} else {
98 			symlen = unresolved_col_width + 4 + 2;
99 			hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
100 			hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
101 		}
102 
103 		if (h->branch_info->to.sym) {
104 			symlen = (int)h->branch_info->to.sym->namelen + 4;
105 			hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
106 
107 			symlen = dso__name_len(h->branch_info->to.map->dso);
108 			hists__new_col_len(hists, HISTC_DSO_TO, symlen);
109 		} else {
110 			symlen = unresolved_col_width + 4 + 2;
111 			hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
112 			hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
113 		}
114 	}
115 }
116 
hist_entry__add_cpumode_period(struct hist_entry * he,unsigned int cpumode,u64 period)117 static void hist_entry__add_cpumode_period(struct hist_entry *he,
118 					   unsigned int cpumode, u64 period)
119 {
120 	switch (cpumode) {
121 	case PERF_RECORD_MISC_KERNEL:
122 		he->period_sys += period;
123 		break;
124 	case PERF_RECORD_MISC_USER:
125 		he->period_us += period;
126 		break;
127 	case PERF_RECORD_MISC_GUEST_KERNEL:
128 		he->period_guest_sys += period;
129 		break;
130 	case PERF_RECORD_MISC_GUEST_USER:
131 		he->period_guest_us += period;
132 		break;
133 	default:
134 		break;
135 	}
136 }
137 
hist_entry__decay(struct hist_entry * he)138 static void hist_entry__decay(struct hist_entry *he)
139 {
140 	he->period = (he->period * 7) / 8;
141 	he->nr_events = (he->nr_events * 7) / 8;
142 }
143 
hists__decay_entry(struct hists * hists,struct hist_entry * he)144 static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
145 {
146 	u64 prev_period = he->period;
147 
148 	if (prev_period == 0)
149 		return true;
150 
151 	hist_entry__decay(he);
152 
153 	if (!he->filtered)
154 		hists->stats.total_period -= prev_period - he->period;
155 
156 	return he->period == 0;
157 }
158 
__hists__decay_entries(struct hists * hists,bool zap_user,bool zap_kernel,bool threaded)159 static void __hists__decay_entries(struct hists *hists, bool zap_user,
160 				   bool zap_kernel, bool threaded)
161 {
162 	struct rb_node *next = rb_first(&hists->entries);
163 	struct hist_entry *n;
164 
165 	while (next) {
166 		n = rb_entry(next, struct hist_entry, rb_node);
167 		next = rb_next(&n->rb_node);
168 		/*
169 		 * We may be annotating this, for instance, so keep it here in
170 		 * case some it gets new samples, we'll eventually free it when
171 		 * the user stops browsing and it agains gets fully decayed.
172 		 */
173 		if (((zap_user && n->level == '.') ||
174 		     (zap_kernel && n->level != '.') ||
175 		     hists__decay_entry(hists, n)) &&
176 		    !n->used) {
177 			rb_erase(&n->rb_node, &hists->entries);
178 
179 			if (sort__need_collapse || threaded)
180 				rb_erase(&n->rb_node_in, &hists->entries_collapsed);
181 
182 			hist_entry__free(n);
183 			--hists->nr_entries;
184 		}
185 	}
186 }
187 
hists__decay_entries(struct hists * hists,bool zap_user,bool zap_kernel)188 void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
189 {
190 	return __hists__decay_entries(hists, zap_user, zap_kernel, false);
191 }
192 
hists__decay_entries_threaded(struct hists * hists,bool zap_user,bool zap_kernel)193 void hists__decay_entries_threaded(struct hists *hists,
194 				   bool zap_user, bool zap_kernel)
195 {
196 	return __hists__decay_entries(hists, zap_user, zap_kernel, true);
197 }
198 
199 /*
200  * histogram, sorted on item, collects periods
201  */
202 
hist_entry__new(struct hist_entry * template)203 static struct hist_entry *hist_entry__new(struct hist_entry *template)
204 {
205 	size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
206 	struct hist_entry *he = malloc(sizeof(*he) + callchain_size);
207 
208 	if (he != NULL) {
209 		*he = *template;
210 		he->nr_events = 1;
211 		if (he->ms.map)
212 			he->ms.map->referenced = true;
213 		if (symbol_conf.use_callchain)
214 			callchain_init(he->callchain);
215 	}
216 
217 	return he;
218 }
219 
hists__inc_nr_entries(struct hists * hists,struct hist_entry * h)220 static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
221 {
222 	if (!h->filtered) {
223 		hists__calc_col_len(hists, h);
224 		++hists->nr_entries;
225 		hists->stats.total_period += h->period;
226 	}
227 }
228 
symbol__parent_filter(const struct symbol * parent)229 static u8 symbol__parent_filter(const struct symbol *parent)
230 {
231 	if (symbol_conf.exclude_other && parent == NULL)
232 		return 1 << HIST_FILTER__PARENT;
233 	return 0;
234 }
235 
add_hist_entry(struct hists * hists,struct hist_entry * entry,struct addr_location * al,u64 period)236 static struct hist_entry *add_hist_entry(struct hists *hists,
237 				      struct hist_entry *entry,
238 				      struct addr_location *al,
239 				      u64 period)
240 {
241 	struct rb_node **p;
242 	struct rb_node *parent = NULL;
243 	struct hist_entry *he;
244 	int cmp;
245 
246 	pthread_mutex_lock(&hists->lock);
247 
248 	p = &hists->entries_in->rb_node;
249 
250 	while (*p != NULL) {
251 		parent = *p;
252 		he = rb_entry(parent, struct hist_entry, rb_node_in);
253 
254 		cmp = hist_entry__cmp(entry, he);
255 
256 		if (!cmp) {
257 			he->period += period;
258 			++he->nr_events;
259 
260 			/* If the map of an existing hist_entry has
261 			 * become out-of-date due to an exec() or
262 			 * similar, update it.  Otherwise we will
263 			 * mis-adjust symbol addresses when computing
264 			 * the history counter to increment.
265 			 */
266 			if (he->ms.map != entry->ms.map) {
267 				he->ms.map = entry->ms.map;
268 				if (he->ms.map)
269 					he->ms.map->referenced = true;
270 			}
271 			goto out;
272 		}
273 
274 		if (cmp < 0)
275 			p = &(*p)->rb_left;
276 		else
277 			p = &(*p)->rb_right;
278 	}
279 
280 	he = hist_entry__new(entry);
281 	if (!he)
282 		goto out_unlock;
283 
284 	rb_link_node(&he->rb_node_in, parent, p);
285 	rb_insert_color(&he->rb_node_in, hists->entries_in);
286 out:
287 	hist_entry__add_cpumode_period(he, al->cpumode, period);
288 out_unlock:
289 	pthread_mutex_unlock(&hists->lock);
290 	return he;
291 }
292 
__hists__add_branch_entry(struct hists * self,struct addr_location * al,struct symbol * sym_parent,struct branch_info * bi,u64 period)293 struct hist_entry *__hists__add_branch_entry(struct hists *self,
294 					     struct addr_location *al,
295 					     struct symbol *sym_parent,
296 					     struct branch_info *bi,
297 					     u64 period)
298 {
299 	struct hist_entry entry = {
300 		.thread	= al->thread,
301 		.ms = {
302 			.map	= bi->to.map,
303 			.sym	= bi->to.sym,
304 		},
305 		.cpu	= al->cpu,
306 		.ip	= bi->to.addr,
307 		.level	= al->level,
308 		.period	= period,
309 		.parent = sym_parent,
310 		.filtered = symbol__parent_filter(sym_parent),
311 		.branch_info = bi,
312 	};
313 
314 	return add_hist_entry(self, &entry, al, period);
315 }
316 
__hists__add_entry(struct hists * self,struct addr_location * al,struct symbol * sym_parent,u64 period)317 struct hist_entry *__hists__add_entry(struct hists *self,
318 				      struct addr_location *al,
319 				      struct symbol *sym_parent, u64 period)
320 {
321 	struct hist_entry entry = {
322 		.thread	= al->thread,
323 		.ms = {
324 			.map	= al->map,
325 			.sym	= al->sym,
326 		},
327 		.cpu	= al->cpu,
328 		.ip	= al->addr,
329 		.level	= al->level,
330 		.period	= period,
331 		.parent = sym_parent,
332 		.filtered = symbol__parent_filter(sym_parent),
333 	};
334 
335 	return add_hist_entry(self, &entry, al, period);
336 }
337 
338 int64_t
hist_entry__cmp(struct hist_entry * left,struct hist_entry * right)339 hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
340 {
341 	struct sort_entry *se;
342 	int64_t cmp = 0;
343 
344 	list_for_each_entry(se, &hist_entry__sort_list, list) {
345 		cmp = se->se_cmp(left, right);
346 		if (cmp)
347 			break;
348 	}
349 
350 	return cmp;
351 }
352 
353 int64_t
hist_entry__collapse(struct hist_entry * left,struct hist_entry * right)354 hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
355 {
356 	struct sort_entry *se;
357 	int64_t cmp = 0;
358 
359 	list_for_each_entry(se, &hist_entry__sort_list, list) {
360 		int64_t (*f)(struct hist_entry *, struct hist_entry *);
361 
362 		f = se->se_collapse ?: se->se_cmp;
363 
364 		cmp = f(left, right);
365 		if (cmp)
366 			break;
367 	}
368 
369 	return cmp;
370 }
371 
hist_entry__free(struct hist_entry * he)372 void hist_entry__free(struct hist_entry *he)
373 {
374 	free(he);
375 }
376 
377 /*
378  * collapse the histogram
379  */
380 
hists__collapse_insert_entry(struct hists * hists,struct rb_root * root,struct hist_entry * he)381 static bool hists__collapse_insert_entry(struct hists *hists,
382 					 struct rb_root *root,
383 					 struct hist_entry *he)
384 {
385 	struct rb_node **p = &root->rb_node;
386 	struct rb_node *parent = NULL;
387 	struct hist_entry *iter;
388 	int64_t cmp;
389 
390 	while (*p != NULL) {
391 		parent = *p;
392 		iter = rb_entry(parent, struct hist_entry, rb_node_in);
393 
394 		cmp = hist_entry__collapse(iter, he);
395 
396 		if (!cmp) {
397 			iter->period += he->period;
398 			iter->nr_events += he->nr_events;
399 			if (symbol_conf.use_callchain) {
400 				callchain_cursor_reset(&hists->callchain_cursor);
401 				callchain_merge(&hists->callchain_cursor, iter->callchain,
402 						he->callchain);
403 			}
404 			hist_entry__free(he);
405 			return false;
406 		}
407 
408 		if (cmp < 0)
409 			p = &(*p)->rb_left;
410 		else
411 			p = &(*p)->rb_right;
412 	}
413 
414 	rb_link_node(&he->rb_node_in, parent, p);
415 	rb_insert_color(&he->rb_node_in, root);
416 	return true;
417 }
418 
hists__get_rotate_entries_in(struct hists * hists)419 static struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
420 {
421 	struct rb_root *root;
422 
423 	pthread_mutex_lock(&hists->lock);
424 
425 	root = hists->entries_in;
426 	if (++hists->entries_in > &hists->entries_in_array[1])
427 		hists->entries_in = &hists->entries_in_array[0];
428 
429 	pthread_mutex_unlock(&hists->lock);
430 
431 	return root;
432 }
433 
hists__apply_filters(struct hists * hists,struct hist_entry * he)434 static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
435 {
436 	hists__filter_entry_by_dso(hists, he);
437 	hists__filter_entry_by_thread(hists, he);
438 	hists__filter_entry_by_symbol(hists, he);
439 }
440 
__hists__collapse_resort(struct hists * hists,bool threaded)441 static void __hists__collapse_resort(struct hists *hists, bool threaded)
442 {
443 	struct rb_root *root;
444 	struct rb_node *next;
445 	struct hist_entry *n;
446 
447 	if (!sort__need_collapse && !threaded)
448 		return;
449 
450 	root = hists__get_rotate_entries_in(hists);
451 	next = rb_first(root);
452 
453 	while (next) {
454 		n = rb_entry(next, struct hist_entry, rb_node_in);
455 		next = rb_next(&n->rb_node_in);
456 
457 		rb_erase(&n->rb_node_in, root);
458 		if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) {
459 			/*
460 			 * If it wasn't combined with one of the entries already
461 			 * collapsed, we need to apply the filters that may have
462 			 * been set by, say, the hist_browser.
463 			 */
464 			hists__apply_filters(hists, n);
465 		}
466 	}
467 }
468 
hists__collapse_resort(struct hists * hists)469 void hists__collapse_resort(struct hists *hists)
470 {
471 	return __hists__collapse_resort(hists, false);
472 }
473 
hists__collapse_resort_threaded(struct hists * hists)474 void hists__collapse_resort_threaded(struct hists *hists)
475 {
476 	return __hists__collapse_resort(hists, true);
477 }
478 
479 /*
480  * reverse the map, sort on period.
481  */
482 
__hists__insert_output_entry(struct rb_root * entries,struct hist_entry * he,u64 min_callchain_hits)483 static void __hists__insert_output_entry(struct rb_root *entries,
484 					 struct hist_entry *he,
485 					 u64 min_callchain_hits)
486 {
487 	struct rb_node **p = &entries->rb_node;
488 	struct rb_node *parent = NULL;
489 	struct hist_entry *iter;
490 
491 	if (symbol_conf.use_callchain)
492 		callchain_param.sort(&he->sorted_chain, he->callchain,
493 				      min_callchain_hits, &callchain_param);
494 
495 	while (*p != NULL) {
496 		parent = *p;
497 		iter = rb_entry(parent, struct hist_entry, rb_node);
498 
499 		if (he->period > iter->period)
500 			p = &(*p)->rb_left;
501 		else
502 			p = &(*p)->rb_right;
503 	}
504 
505 	rb_link_node(&he->rb_node, parent, p);
506 	rb_insert_color(&he->rb_node, entries);
507 }
508 
__hists__output_resort(struct hists * hists,bool threaded)509 static void __hists__output_resort(struct hists *hists, bool threaded)
510 {
511 	struct rb_root *root;
512 	struct rb_node *next;
513 	struct hist_entry *n;
514 	u64 min_callchain_hits;
515 
516 	min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100);
517 
518 	if (sort__need_collapse || threaded)
519 		root = &hists->entries_collapsed;
520 	else
521 		root = hists->entries_in;
522 
523 	next = rb_first(root);
524 	hists->entries = RB_ROOT;
525 
526 	hists->nr_entries = 0;
527 	hists->stats.total_period = 0;
528 	hists__reset_col_len(hists);
529 
530 	while (next) {
531 		n = rb_entry(next, struct hist_entry, rb_node_in);
532 		next = rb_next(&n->rb_node_in);
533 
534 		__hists__insert_output_entry(&hists->entries, n, min_callchain_hits);
535 		hists__inc_nr_entries(hists, n);
536 	}
537 }
538 
hists__output_resort(struct hists * hists)539 void hists__output_resort(struct hists *hists)
540 {
541 	return __hists__output_resort(hists, false);
542 }
543 
hists__output_resort_threaded(struct hists * hists)544 void hists__output_resort_threaded(struct hists *hists)
545 {
546 	return __hists__output_resort(hists, true);
547 }
548 
callchain__fprintf_left_margin(FILE * fp,int left_margin)549 static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
550 {
551 	int i;
552 	int ret = fprintf(fp, "            ");
553 
554 	for (i = 0; i < left_margin; i++)
555 		ret += fprintf(fp, " ");
556 
557 	return ret;
558 }
559 
ipchain__fprintf_graph_line(FILE * fp,int depth,int depth_mask,int left_margin)560 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
561 					  int left_margin)
562 {
563 	int i;
564 	size_t ret = callchain__fprintf_left_margin(fp, left_margin);
565 
566 	for (i = 0; i < depth; i++)
567 		if (depth_mask & (1 << i))
568 			ret += fprintf(fp, "|          ");
569 		else
570 			ret += fprintf(fp, "           ");
571 
572 	ret += fprintf(fp, "\n");
573 
574 	return ret;
575 }
576 
ipchain__fprintf_graph(FILE * fp,struct callchain_list * chain,int depth,int depth_mask,int period,u64 total_samples,u64 hits,int left_margin)577 static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
578 				     int depth, int depth_mask, int period,
579 				     u64 total_samples, u64 hits,
580 				     int left_margin)
581 {
582 	int i;
583 	size_t ret = 0;
584 
585 	ret += callchain__fprintf_left_margin(fp, left_margin);
586 	for (i = 0; i < depth; i++) {
587 		if (depth_mask & (1 << i))
588 			ret += fprintf(fp, "|");
589 		else
590 			ret += fprintf(fp, " ");
591 		if (!period && i == depth - 1) {
592 			double percent;
593 
594 			percent = hits * 100.0 / total_samples;
595 			ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
596 		} else
597 			ret += fprintf(fp, "%s", "          ");
598 	}
599 	if (chain->ms.sym)
600 		ret += fprintf(fp, "%s\n", chain->ms.sym->name);
601 	else
602 		ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
603 
604 	return ret;
605 }
606 
607 static struct symbol *rem_sq_bracket;
608 static struct callchain_list rem_hits;
609 
init_rem_hits(void)610 static void init_rem_hits(void)
611 {
612 	rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
613 	if (!rem_sq_bracket) {
614 		fprintf(stderr, "Not enough memory to display remaining hits\n");
615 		return;
616 	}
617 
618 	strcpy(rem_sq_bracket->name, "[...]");
619 	rem_hits.ms.sym = rem_sq_bracket;
620 }
621 
__callchain__fprintf_graph(FILE * fp,struct rb_root * root,u64 total_samples,int depth,int depth_mask,int left_margin)622 static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
623 					 u64 total_samples, int depth,
624 					 int depth_mask, int left_margin)
625 {
626 	struct rb_node *node, *next;
627 	struct callchain_node *child;
628 	struct callchain_list *chain;
629 	int new_depth_mask = depth_mask;
630 	u64 remaining;
631 	size_t ret = 0;
632 	int i;
633 	uint entries_printed = 0;
634 
635 	remaining = total_samples;
636 
637 	node = rb_first(root);
638 	while (node) {
639 		u64 new_total;
640 		u64 cumul;
641 
642 		child = rb_entry(node, struct callchain_node, rb_node);
643 		cumul = callchain_cumul_hits(child);
644 		remaining -= cumul;
645 
646 		/*
647 		 * The depth mask manages the output of pipes that show
648 		 * the depth. We don't want to keep the pipes of the current
649 		 * level for the last child of this depth.
650 		 * Except if we have remaining filtered hits. They will
651 		 * supersede the last child
652 		 */
653 		next = rb_next(node);
654 		if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
655 			new_depth_mask &= ~(1 << (depth - 1));
656 
657 		/*
658 		 * But we keep the older depth mask for the line separator
659 		 * to keep the level link until we reach the last child
660 		 */
661 		ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
662 						   left_margin);
663 		i = 0;
664 		list_for_each_entry(chain, &child->val, list) {
665 			ret += ipchain__fprintf_graph(fp, chain, depth,
666 						      new_depth_mask, i++,
667 						      total_samples,
668 						      cumul,
669 						      left_margin);
670 		}
671 
672 		if (callchain_param.mode == CHAIN_GRAPH_REL)
673 			new_total = child->children_hit;
674 		else
675 			new_total = total_samples;
676 
677 		ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
678 						  depth + 1,
679 						  new_depth_mask | (1 << depth),
680 						  left_margin);
681 		node = next;
682 		if (++entries_printed == callchain_param.print_limit)
683 			break;
684 	}
685 
686 	if (callchain_param.mode == CHAIN_GRAPH_REL &&
687 		remaining && remaining != total_samples) {
688 
689 		if (!rem_sq_bracket)
690 			return ret;
691 
692 		new_depth_mask &= ~(1 << (depth - 1));
693 		ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
694 					      new_depth_mask, 0, total_samples,
695 					      remaining, left_margin);
696 	}
697 
698 	return ret;
699 }
700 
callchain__fprintf_graph(FILE * fp,struct rb_root * root,u64 total_samples,int left_margin)701 static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
702 				       u64 total_samples, int left_margin)
703 {
704 	struct callchain_node *cnode;
705 	struct callchain_list *chain;
706 	u32 entries_printed = 0;
707 	bool printed = false;
708 	struct rb_node *node;
709 	int i = 0;
710 	int ret;
711 
712 	/*
713 	 * If have one single callchain root, don't bother printing
714 	 * its percentage (100 % in fractal mode and the same percentage
715 	 * than the hist in graph mode). This also avoid one level of column.
716 	 */
717 	node = rb_first(root);
718 	if (node && !rb_next(node)) {
719 		cnode = rb_entry(node, struct callchain_node, rb_node);
720 		list_for_each_entry(chain, &cnode->val, list) {
721 			/*
722 			 * If we sort by symbol, the first entry is the same than
723 			 * the symbol. No need to print it otherwise it appears as
724 			 * displayed twice.
725 			 */
726 			if (!i++ && sort__first_dimension == SORT_SYM)
727 				continue;
728 			if (!printed) {
729 				ret += callchain__fprintf_left_margin(fp, left_margin);
730 				ret += fprintf(fp, "|\n");
731 				ret += callchain__fprintf_left_margin(fp, left_margin);
732 				ret += fprintf(fp, "---");
733 				left_margin += 3;
734 				printed = true;
735 			} else
736 				ret += callchain__fprintf_left_margin(fp, left_margin);
737 
738 			if (chain->ms.sym)
739 				ret += fprintf(fp, " %s\n", chain->ms.sym->name);
740 			else
741 				ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
742 
743 			if (++entries_printed == callchain_param.print_limit)
744 				break;
745 		}
746 		root = &cnode->rb_root;
747 	}
748 
749 	return __callchain__fprintf_graph(fp, root, total_samples,
750 					  1, 1, left_margin);
751 }
752 
__callchain__fprintf_flat(FILE * fp,struct callchain_node * self,u64 total_samples)753 static size_t __callchain__fprintf_flat(FILE *fp,
754 					struct callchain_node *self,
755 					u64 total_samples)
756 {
757 	struct callchain_list *chain;
758 	size_t ret = 0;
759 
760 	if (!self)
761 		return 0;
762 
763 	ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
764 
765 
766 	list_for_each_entry(chain, &self->val, list) {
767 		if (chain->ip >= PERF_CONTEXT_MAX)
768 			continue;
769 		if (chain->ms.sym)
770 			ret += fprintf(fp, "                %s\n", chain->ms.sym->name);
771 		else
772 			ret += fprintf(fp, "                %p\n",
773 					(void *)(long)chain->ip);
774 	}
775 
776 	return ret;
777 }
778 
callchain__fprintf_flat(FILE * fp,struct rb_root * self,u64 total_samples)779 static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
780 				      u64 total_samples)
781 {
782 	size_t ret = 0;
783 	u32 entries_printed = 0;
784 	struct rb_node *rb_node;
785 	struct callchain_node *chain;
786 
787 	rb_node = rb_first(self);
788 	while (rb_node) {
789 		double percent;
790 
791 		chain = rb_entry(rb_node, struct callchain_node, rb_node);
792 		percent = chain->hit * 100.0 / total_samples;
793 
794 		ret = percent_color_fprintf(fp, "           %6.2f%%\n", percent);
795 		ret += __callchain__fprintf_flat(fp, chain, total_samples);
796 		ret += fprintf(fp, "\n");
797 		if (++entries_printed == callchain_param.print_limit)
798 			break;
799 
800 		rb_node = rb_next(rb_node);
801 	}
802 
803 	return ret;
804 }
805 
hist_entry_callchain__fprintf(struct hist_entry * he,u64 total_samples,int left_margin,FILE * fp)806 static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
807 					    u64 total_samples, int left_margin,
808 					    FILE *fp)
809 {
810 	switch (callchain_param.mode) {
811 	case CHAIN_GRAPH_REL:
812 		return callchain__fprintf_graph(fp, &he->sorted_chain, he->period,
813 						left_margin);
814 		break;
815 	case CHAIN_GRAPH_ABS:
816 		return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
817 						left_margin);
818 		break;
819 	case CHAIN_FLAT:
820 		return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
821 		break;
822 	case CHAIN_NONE:
823 		break;
824 	default:
825 		pr_err("Bad callchain mode\n");
826 	}
827 
828 	return 0;
829 }
830 
hists__output_recalc_col_len(struct hists * hists,int max_rows)831 void hists__output_recalc_col_len(struct hists *hists, int max_rows)
832 {
833 	struct rb_node *next = rb_first(&hists->entries);
834 	struct hist_entry *n;
835 	int row = 0;
836 
837 	hists__reset_col_len(hists);
838 
839 	while (next && row++ < max_rows) {
840 		n = rb_entry(next, struct hist_entry, rb_node);
841 		if (!n->filtered)
842 			hists__calc_col_len(hists, n);
843 		next = rb_next(&n->rb_node);
844 	}
845 }
846 
hist_entry__pcnt_snprintf(struct hist_entry * he,char * s,size_t size,struct hists * pair_hists,bool show_displacement,long displacement,bool color,u64 total_period)847 static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s,
848 				     size_t size, struct hists *pair_hists,
849 				     bool show_displacement, long displacement,
850 				     bool color, u64 total_period)
851 {
852 	u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
853 	u64 nr_events;
854 	const char *sep = symbol_conf.field_sep;
855 	int ret;
856 
857 	if (symbol_conf.exclude_other && !he->parent)
858 		return 0;
859 
860 	if (pair_hists) {
861 		period = he->pair ? he->pair->period : 0;
862 		nr_events = he->pair ? he->pair->nr_events : 0;
863 		total = pair_hists->stats.total_period;
864 		period_sys = he->pair ? he->pair->period_sys : 0;
865 		period_us = he->pair ? he->pair->period_us : 0;
866 		period_guest_sys = he->pair ? he->pair->period_guest_sys : 0;
867 		period_guest_us = he->pair ? he->pair->period_guest_us : 0;
868 	} else {
869 		period = he->period;
870 		nr_events = he->nr_events;
871 		total = total_period;
872 		period_sys = he->period_sys;
873 		period_us = he->period_us;
874 		period_guest_sys = he->period_guest_sys;
875 		period_guest_us = he->period_guest_us;
876 	}
877 
878 	if (total) {
879 		if (color)
880 			ret = percent_color_snprintf(s, size,
881 						     sep ? "%.2f" : "   %6.2f%%",
882 						     (period * 100.0) / total);
883 		else
884 			ret = scnprintf(s, size, sep ? "%.2f" : "   %6.2f%%",
885 				       (period * 100.0) / total);
886 		if (symbol_conf.show_cpu_utilization) {
887 			ret += percent_color_snprintf(s + ret, size - ret,
888 					sep ? "%.2f" : "   %6.2f%%",
889 					(period_sys * 100.0) / total);
890 			ret += percent_color_snprintf(s + ret, size - ret,
891 					sep ? "%.2f" : "   %6.2f%%",
892 					(period_us * 100.0) / total);
893 			if (perf_guest) {
894 				ret += percent_color_snprintf(s + ret,
895 						size - ret,
896 						sep ? "%.2f" : "   %6.2f%%",
897 						(period_guest_sys * 100.0) /
898 								total);
899 				ret += percent_color_snprintf(s + ret,
900 						size - ret,
901 						sep ? "%.2f" : "   %6.2f%%",
902 						(period_guest_us * 100.0) /
903 								total);
904 			}
905 		}
906 	} else
907 		ret = scnprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
908 
909 	if (symbol_conf.show_nr_samples) {
910 		if (sep)
911 			ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
912 		else
913 			ret += scnprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
914 	}
915 
916 	if (symbol_conf.show_total_period) {
917 		if (sep)
918 			ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
919 		else
920 			ret += scnprintf(s + ret, size - ret, " %12" PRIu64, period);
921 	}
922 
923 	if (pair_hists) {
924 		char bf[32];
925 		double old_percent = 0, new_percent = 0, diff;
926 
927 		if (total > 0)
928 			old_percent = (period * 100.0) / total;
929 		if (total_period > 0)
930 			new_percent = (he->period * 100.0) / total_period;
931 
932 		diff = new_percent - old_percent;
933 
934 		if (fabs(diff) >= 0.01)
935 			scnprintf(bf, sizeof(bf), "%+4.2F%%", diff);
936 		else
937 			scnprintf(bf, sizeof(bf), " ");
938 
939 		if (sep)
940 			ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
941 		else
942 			ret += scnprintf(s + ret, size - ret, "%11.11s", bf);
943 
944 		if (show_displacement) {
945 			if (displacement)
946 				scnprintf(bf, sizeof(bf), "%+4ld", displacement);
947 			else
948 				scnprintf(bf, sizeof(bf), " ");
949 
950 			if (sep)
951 				ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
952 			else
953 				ret += scnprintf(s + ret, size - ret, "%6.6s", bf);
954 		}
955 	}
956 
957 	return ret;
958 }
959 
hist_entry__snprintf(struct hist_entry * he,char * s,size_t size,struct hists * hists)960 int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size,
961 			 struct hists *hists)
962 {
963 	const char *sep = symbol_conf.field_sep;
964 	struct sort_entry *se;
965 	int ret = 0;
966 
967 	list_for_each_entry(se, &hist_entry__sort_list, list) {
968 		if (se->elide)
969 			continue;
970 
971 		ret += scnprintf(s + ret, size - ret, "%s", sep ?: "  ");
972 		ret += se->se_snprintf(he, s + ret, size - ret,
973 				       hists__col_len(hists, se->se_width_idx));
974 	}
975 
976 	return ret;
977 }
978 
hist_entry__fprintf(struct hist_entry * he,size_t size,struct hists * hists,struct hists * pair_hists,bool show_displacement,long displacement,u64 total_period,FILE * fp)979 static int hist_entry__fprintf(struct hist_entry *he, size_t size,
980 			       struct hists *hists, struct hists *pair_hists,
981 			       bool show_displacement, long displacement,
982 			       u64 total_period, FILE *fp)
983 {
984 	char bf[512];
985 	int ret;
986 
987 	if (size == 0 || size > sizeof(bf))
988 		size = sizeof(bf);
989 
990 	ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists,
991 					show_displacement, displacement,
992 					true, total_period);
993 	hist_entry__snprintf(he, bf + ret, size - ret, hists);
994 	return fprintf(fp, "%s\n", bf);
995 }
996 
hist_entry__fprintf_callchain(struct hist_entry * he,struct hists * hists,u64 total_period,FILE * fp)997 static size_t hist_entry__fprintf_callchain(struct hist_entry *he,
998 					    struct hists *hists,
999 					    u64 total_period, FILE *fp)
1000 {
1001 	int left_margin = 0;
1002 
1003 	if (sort__first_dimension == SORT_COMM) {
1004 		struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
1005 							 typeof(*se), list);
1006 		left_margin = hists__col_len(hists, se->se_width_idx);
1007 		left_margin -= thread__comm_len(he->thread);
1008 	}
1009 
1010 	return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
1011 }
1012 
hists__fprintf(struct hists * hists,struct hists * pair,bool show_displacement,bool show_header,int max_rows,int max_cols,FILE * fp)1013 size_t hists__fprintf(struct hists *hists, struct hists *pair,
1014 		      bool show_displacement, bool show_header, int max_rows,
1015 		      int max_cols, FILE *fp)
1016 {
1017 	struct sort_entry *se;
1018 	struct rb_node *nd;
1019 	size_t ret = 0;
1020 	u64 total_period;
1021 	unsigned long position = 1;
1022 	long displacement = 0;
1023 	unsigned int width;
1024 	const char *sep = symbol_conf.field_sep;
1025 	const char *col_width = symbol_conf.col_width_list_str;
1026 	int nr_rows = 0;
1027 
1028 	init_rem_hits();
1029 
1030 	if (!show_header)
1031 		goto print_entries;
1032 
1033 	fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
1034 
1035 	if (symbol_conf.show_cpu_utilization) {
1036 		if (sep) {
1037 			ret += fprintf(fp, "%csys", *sep);
1038 			ret += fprintf(fp, "%cus", *sep);
1039 			if (perf_guest) {
1040 				ret += fprintf(fp, "%cguest sys", *sep);
1041 				ret += fprintf(fp, "%cguest us", *sep);
1042 			}
1043 		} else {
1044 			ret += fprintf(fp, "     sys  ");
1045 			ret += fprintf(fp, "      us  ");
1046 			if (perf_guest) {
1047 				ret += fprintf(fp, "  guest sys  ");
1048 				ret += fprintf(fp, "  guest us  ");
1049 			}
1050 		}
1051 	}
1052 
1053 	if (symbol_conf.show_nr_samples) {
1054 		if (sep)
1055 			fprintf(fp, "%cSamples", *sep);
1056 		else
1057 			fputs("  Samples  ", fp);
1058 	}
1059 
1060 	if (symbol_conf.show_total_period) {
1061 		if (sep)
1062 			ret += fprintf(fp, "%cPeriod", *sep);
1063 		else
1064 			ret += fprintf(fp, "   Period    ");
1065 	}
1066 
1067 	if (pair) {
1068 		if (sep)
1069 			ret += fprintf(fp, "%cDelta", *sep);
1070 		else
1071 			ret += fprintf(fp, "  Delta    ");
1072 
1073 		if (show_displacement) {
1074 			if (sep)
1075 				ret += fprintf(fp, "%cDisplacement", *sep);
1076 			else
1077 				ret += fprintf(fp, " Displ");
1078 		}
1079 	}
1080 
1081 	list_for_each_entry(se, &hist_entry__sort_list, list) {
1082 		if (se->elide)
1083 			continue;
1084 		if (sep) {
1085 			fprintf(fp, "%c%s", *sep, se->se_header);
1086 			continue;
1087 		}
1088 		width = strlen(se->se_header);
1089 		if (symbol_conf.col_width_list_str) {
1090 			if (col_width) {
1091 				hists__set_col_len(hists, se->se_width_idx,
1092 						   atoi(col_width));
1093 				col_width = strchr(col_width, ',');
1094 				if (col_width)
1095 					++col_width;
1096 			}
1097 		}
1098 		if (!hists__new_col_len(hists, se->se_width_idx, width))
1099 			width = hists__col_len(hists, se->se_width_idx);
1100 		fprintf(fp, "  %*s", width, se->se_header);
1101 	}
1102 
1103 	fprintf(fp, "\n");
1104 	if (max_rows && ++nr_rows >= max_rows)
1105 		goto out;
1106 
1107 	if (sep)
1108 		goto print_entries;
1109 
1110 	fprintf(fp, "# ........");
1111 	if (symbol_conf.show_cpu_utilization)
1112 		fprintf(fp, "   .......   .......");
1113 	if (symbol_conf.show_nr_samples)
1114 		fprintf(fp, " ..........");
1115 	if (symbol_conf.show_total_period)
1116 		fprintf(fp, " ............");
1117 	if (pair) {
1118 		fprintf(fp, " ..........");
1119 		if (show_displacement)
1120 			fprintf(fp, " .....");
1121 	}
1122 	list_for_each_entry(se, &hist_entry__sort_list, list) {
1123 		unsigned int i;
1124 
1125 		if (se->elide)
1126 			continue;
1127 
1128 		fprintf(fp, "  ");
1129 		width = hists__col_len(hists, se->se_width_idx);
1130 		if (width == 0)
1131 			width = strlen(se->se_header);
1132 		for (i = 0; i < width; i++)
1133 			fprintf(fp, ".");
1134 	}
1135 
1136 	fprintf(fp, "\n");
1137 	if (max_rows && ++nr_rows >= max_rows)
1138 		goto out;
1139 
1140 	fprintf(fp, "#\n");
1141 	if (max_rows && ++nr_rows >= max_rows)
1142 		goto out;
1143 
1144 print_entries:
1145 	total_period = hists->stats.total_period;
1146 
1147 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
1148 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1149 
1150 		if (h->filtered)
1151 			continue;
1152 
1153 		if (show_displacement) {
1154 			if (h->pair != NULL)
1155 				displacement = ((long)h->pair->position -
1156 					        (long)position);
1157 			else
1158 				displacement = 0;
1159 			++position;
1160 		}
1161 		ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement,
1162 					   displacement, total_period, fp);
1163 
1164 		if (symbol_conf.use_callchain)
1165 			ret += hist_entry__fprintf_callchain(h, hists, total_period, fp);
1166 		if (max_rows && ++nr_rows >= max_rows)
1167 			goto out;
1168 
1169 		if (h->ms.map == NULL && verbose > 1) {
1170 			__map_groups__fprintf_maps(&h->thread->mg,
1171 						   MAP__FUNCTION, verbose, fp);
1172 			fprintf(fp, "%.10s end\n", graph_dotted_line);
1173 		}
1174 	}
1175 out:
1176 	free(rem_sq_bracket);
1177 
1178 	return ret;
1179 }
1180 
1181 /*
1182  * See hists__fprintf to match the column widths
1183  */
hists__sort_list_width(struct hists * hists)1184 unsigned int hists__sort_list_width(struct hists *hists)
1185 {
1186 	struct sort_entry *se;
1187 	int ret = 9; /* total % */
1188 
1189 	if (symbol_conf.show_cpu_utilization) {
1190 		ret += 7; /* count_sys % */
1191 		ret += 6; /* count_us % */
1192 		if (perf_guest) {
1193 			ret += 13; /* count_guest_sys % */
1194 			ret += 12; /* count_guest_us % */
1195 		}
1196 	}
1197 
1198 	if (symbol_conf.show_nr_samples)
1199 		ret += 11;
1200 
1201 	if (symbol_conf.show_total_period)
1202 		ret += 13;
1203 
1204 	list_for_each_entry(se, &hist_entry__sort_list, list)
1205 		if (!se->elide)
1206 			ret += 2 + hists__col_len(hists, se->se_width_idx);
1207 
1208 	if (verbose) /* Addr + origin */
1209 		ret += 3 + BITS_PER_LONG / 4;
1210 
1211 	return ret;
1212 }
1213 
hists__remove_entry_filter(struct hists * hists,struct hist_entry * h,enum hist_filter filter)1214 static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h,
1215 				       enum hist_filter filter)
1216 {
1217 	h->filtered &= ~(1 << filter);
1218 	if (h->filtered)
1219 		return;
1220 
1221 	++hists->nr_entries;
1222 	if (h->ms.unfolded)
1223 		hists->nr_entries += h->nr_rows;
1224 	h->row_offset = 0;
1225 	hists->stats.total_period += h->period;
1226 	hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
1227 
1228 	hists__calc_col_len(hists, h);
1229 }
1230 
1231 
hists__filter_entry_by_dso(struct hists * hists,struct hist_entry * he)1232 static bool hists__filter_entry_by_dso(struct hists *hists,
1233 				       struct hist_entry *he)
1234 {
1235 	if (hists->dso_filter != NULL &&
1236 	    (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) {
1237 		he->filtered |= (1 << HIST_FILTER__DSO);
1238 		return true;
1239 	}
1240 
1241 	return false;
1242 }
1243 
hists__filter_by_dso(struct hists * hists)1244 void hists__filter_by_dso(struct hists *hists)
1245 {
1246 	struct rb_node *nd;
1247 
1248 	hists->nr_entries = hists->stats.total_period = 0;
1249 	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
1250 	hists__reset_col_len(hists);
1251 
1252 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
1253 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1254 
1255 		if (symbol_conf.exclude_other && !h->parent)
1256 			continue;
1257 
1258 		if (hists__filter_entry_by_dso(hists, h))
1259 			continue;
1260 
1261 		hists__remove_entry_filter(hists, h, HIST_FILTER__DSO);
1262 	}
1263 }
1264 
hists__filter_entry_by_thread(struct hists * hists,struct hist_entry * he)1265 static bool hists__filter_entry_by_thread(struct hists *hists,
1266 					  struct hist_entry *he)
1267 {
1268 	if (hists->thread_filter != NULL &&
1269 	    he->thread != hists->thread_filter) {
1270 		he->filtered |= (1 << HIST_FILTER__THREAD);
1271 		return true;
1272 	}
1273 
1274 	return false;
1275 }
1276 
hists__filter_by_thread(struct hists * hists)1277 void hists__filter_by_thread(struct hists *hists)
1278 {
1279 	struct rb_node *nd;
1280 
1281 	hists->nr_entries = hists->stats.total_period = 0;
1282 	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
1283 	hists__reset_col_len(hists);
1284 
1285 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
1286 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1287 
1288 		if (hists__filter_entry_by_thread(hists, h))
1289 			continue;
1290 
1291 		hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD);
1292 	}
1293 }
1294 
hists__filter_entry_by_symbol(struct hists * hists,struct hist_entry * he)1295 static bool hists__filter_entry_by_symbol(struct hists *hists,
1296 					  struct hist_entry *he)
1297 {
1298 	if (hists->symbol_filter_str != NULL &&
1299 	    (!he->ms.sym || strstr(he->ms.sym->name,
1300 				   hists->symbol_filter_str) == NULL)) {
1301 		he->filtered |= (1 << HIST_FILTER__SYMBOL);
1302 		return true;
1303 	}
1304 
1305 	return false;
1306 }
1307 
hists__filter_by_symbol(struct hists * hists)1308 void hists__filter_by_symbol(struct hists *hists)
1309 {
1310 	struct rb_node *nd;
1311 
1312 	hists->nr_entries = hists->stats.total_period = 0;
1313 	hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
1314 	hists__reset_col_len(hists);
1315 
1316 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
1317 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1318 
1319 		if (hists__filter_entry_by_symbol(hists, h))
1320 			continue;
1321 
1322 		hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL);
1323 	}
1324 }
1325 
hist_entry__inc_addr_samples(struct hist_entry * he,int evidx,u64 ip)1326 int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
1327 {
1328 	return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip);
1329 }
1330 
hist_entry__annotate(struct hist_entry * he,size_t privsize)1331 int hist_entry__annotate(struct hist_entry *he, size_t privsize)
1332 {
1333 	return symbol__annotate(he->ms.sym, he->ms.map, privsize);
1334 }
1335 
hists__inc_nr_events(struct hists * hists,u32 type)1336 void hists__inc_nr_events(struct hists *hists, u32 type)
1337 {
1338 	++hists->stats.nr_events[0];
1339 	++hists->stats.nr_events[type];
1340 }
1341 
hists__fprintf_nr_events(struct hists * hists,FILE * fp)1342 size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
1343 {
1344 	int i;
1345 	size_t ret = 0;
1346 
1347 	for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
1348 		const char *name;
1349 
1350 		if (hists->stats.nr_events[i] == 0)
1351 			continue;
1352 
1353 		name = perf_event__name(i);
1354 		if (!strcmp(name, "UNKNOWN"))
1355 			continue;
1356 
1357 		ret += fprintf(fp, "%16s events: %10d\n", name,
1358 			       hists->stats.nr_events[i]);
1359 	}
1360 
1361 	return ret;
1362 }
1363