cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hist.c (22099B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <stdio.h>
      3#include <stdlib.h>
      4#include <linux/string.h>
      5
      6#include "../../util/callchain.h"
      7#include "../../util/debug.h"
      8#include "../../util/event.h"
      9#include "../../util/hist.h"
     10#include "../../util/map.h"
     11#include "../../util/maps.h"
     12#include "../../util/symbol.h"
     13#include "../../util/sort.h"
     14#include "../../util/evsel.h"
     15#include "../../util/srcline.h"
     16#include "../../util/string2.h"
     17#include "../../util/thread.h"
     18#include "../../util/block-info.h"
     19#include <linux/ctype.h>
     20#include <linux/zalloc.h>
     21
     22static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
     23{
     24	int i;
     25	int ret = fprintf(fp, "            ");
     26
     27	for (i = 0; i < left_margin; i++)
     28		ret += fprintf(fp, " ");
     29
     30	return ret;
     31}
     32
     33static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
     34					  int left_margin)
     35{
     36	int i;
     37	size_t ret = callchain__fprintf_left_margin(fp, left_margin);
     38
     39	for (i = 0; i < depth; i++)
     40		if (depth_mask & (1 << i))
     41			ret += fprintf(fp, "|          ");
     42		else
     43			ret += fprintf(fp, "           ");
     44
     45	ret += fprintf(fp, "\n");
     46
     47	return ret;
     48}
     49
     50static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
     51				     struct callchain_list *chain,
     52				     int depth, int depth_mask, int period,
     53				     u64 total_samples, int left_margin)
     54{
     55	int i;
     56	size_t ret = 0;
     57	char bf[1024], *alloc_str = NULL;
     58	char buf[64];
     59	const char *str;
     60
     61	ret += callchain__fprintf_left_margin(fp, left_margin);
     62	for (i = 0; i < depth; i++) {
     63		if (depth_mask & (1 << i))
     64			ret += fprintf(fp, "|");
     65		else
     66			ret += fprintf(fp, " ");
     67		if (!period && i == depth - 1) {
     68			ret += fprintf(fp, "--");
     69			ret += callchain_node__fprintf_value(node, fp, total_samples);
     70			ret += fprintf(fp, "--");
     71		} else
     72			ret += fprintf(fp, "%s", "          ");
     73	}
     74
     75	str = callchain_list__sym_name(chain, bf, sizeof(bf), false);
     76
     77	if (symbol_conf.show_branchflag_count) {
     78		callchain_list_counts__printf_value(chain, NULL,
     79						    buf, sizeof(buf));
     80
     81		if (asprintf(&alloc_str, "%s%s", str, buf) < 0)
     82			str = "Not enough memory!";
     83		else
     84			str = alloc_str;
     85	}
     86
     87	fputs(str, fp);
     88	fputc('\n', fp);
     89	free(alloc_str);
     90
     91	return ret;
     92}
     93
     94static struct symbol *rem_sq_bracket;
     95static struct callchain_list rem_hits;
     96
     97static void init_rem_hits(void)
     98{
     99	rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
    100	if (!rem_sq_bracket) {
    101		fprintf(stderr, "Not enough memory to display remaining hits\n");
    102		return;
    103	}
    104
    105	strcpy(rem_sq_bracket->name, "[...]");
    106	rem_hits.ms.sym = rem_sq_bracket;
    107}
    108
    109static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
    110					 u64 total_samples, int depth,
    111					 int depth_mask, int left_margin)
    112{
    113	struct rb_node *node, *next;
    114	struct callchain_node *child = NULL;
    115	struct callchain_list *chain;
    116	int new_depth_mask = depth_mask;
    117	u64 remaining;
    118	size_t ret = 0;
    119	int i;
    120	uint entries_printed = 0;
    121	int cumul_count = 0;
    122
    123	remaining = total_samples;
    124
    125	node = rb_first(root);
    126	while (node) {
    127		u64 new_total;
    128		u64 cumul;
    129
    130		child = rb_entry(node, struct callchain_node, rb_node);
    131		cumul = callchain_cumul_hits(child);
    132		remaining -= cumul;
    133		cumul_count += callchain_cumul_counts(child);
    134
    135		/*
    136		 * The depth mask manages the output of pipes that show
    137		 * the depth. We don't want to keep the pipes of the current
    138		 * level for the last child of this depth.
    139		 * Except if we have remaining filtered hits. They will
    140		 * supersede the last child
    141		 */
    142		next = rb_next(node);
    143		if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
    144			new_depth_mask &= ~(1 << (depth - 1));
    145
    146		/*
    147		 * But we keep the older depth mask for the line separator
    148		 * to keep the level link until we reach the last child
    149		 */
    150		ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
    151						   left_margin);
    152		i = 0;
    153		list_for_each_entry(chain, &child->val, list) {
    154			ret += ipchain__fprintf_graph(fp, child, chain, depth,
    155						      new_depth_mask, i++,
    156						      total_samples,
    157						      left_margin);
    158		}
    159
    160		if (callchain_param.mode == CHAIN_GRAPH_REL)
    161			new_total = child->children_hit;
    162		else
    163			new_total = total_samples;
    164
    165		ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
    166						  depth + 1,
    167						  new_depth_mask | (1 << depth),
    168						  left_margin);
    169		node = next;
    170		if (++entries_printed == callchain_param.print_limit)
    171			break;
    172	}
    173
    174	if (callchain_param.mode == CHAIN_GRAPH_REL &&
    175		remaining && remaining != total_samples) {
    176		struct callchain_node rem_node = {
    177			.hit = remaining,
    178		};
    179
    180		if (!rem_sq_bracket)
    181			return ret;
    182
    183		if (callchain_param.value == CCVAL_COUNT && child && child->parent) {
    184			rem_node.count = child->parent->children_count - cumul_count;
    185			if (rem_node.count <= 0)
    186				return ret;
    187		}
    188
    189		new_depth_mask &= ~(1 << (depth - 1));
    190		ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth,
    191					      new_depth_mask, 0, total_samples,
    192					      left_margin);
    193	}
    194
    195	return ret;
    196}
    197
    198/*
    199 * If have one single callchain root, don't bother printing
    200 * its percentage (100 % in fractal mode and the same percentage
    201 * than the hist in graph mode). This also avoid one level of column.
    202 *
    203 * However when percent-limit applied, it's possible that single callchain
    204 * node have different (non-100% in fractal mode) percentage.
    205 */
    206static bool need_percent_display(struct rb_node *node, u64 parent_samples)
    207{
    208	struct callchain_node *cnode;
    209
    210	if (rb_next(node))
    211		return true;
    212
    213	cnode = rb_entry(node, struct callchain_node, rb_node);
    214	return callchain_cumul_hits(cnode) != parent_samples;
    215}
    216
    217static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
    218				       u64 total_samples, u64 parent_samples,
    219				       int left_margin)
    220{
    221	struct callchain_node *cnode;
    222	struct callchain_list *chain;
    223	u32 entries_printed = 0;
    224	bool printed = false;
    225	struct rb_node *node;
    226	int i = 0;
    227	int ret = 0;
    228	char bf[1024];
    229
    230	node = rb_first(root);
    231	if (node && !need_percent_display(node, parent_samples)) {
    232		cnode = rb_entry(node, struct callchain_node, rb_node);
    233		list_for_each_entry(chain, &cnode->val, list) {
    234			/*
    235			 * If we sort by symbol, the first entry is the same than
    236			 * the symbol. No need to print it otherwise it appears as
    237			 * displayed twice.
    238			 */
    239			if (!i++ && field_order == NULL &&
    240			    sort_order && strstarts(sort_order, "sym"))
    241				continue;
    242
    243			if (!printed) {
    244				ret += callchain__fprintf_left_margin(fp, left_margin);
    245				ret += fprintf(fp, "|\n");
    246				ret += callchain__fprintf_left_margin(fp, left_margin);
    247				ret += fprintf(fp, "---");
    248				left_margin += 3;
    249				printed = true;
    250			} else
    251				ret += callchain__fprintf_left_margin(fp, left_margin);
    252
    253			ret += fprintf(fp, "%s",
    254				       callchain_list__sym_name(chain, bf,
    255								sizeof(bf),
    256								false));
    257
    258			if (symbol_conf.show_branchflag_count)
    259				ret += callchain_list_counts__printf_value(
    260						chain, fp, NULL, 0);
    261			ret += fprintf(fp, "\n");
    262
    263			if (++entries_printed == callchain_param.print_limit)
    264				break;
    265		}
    266		root = &cnode->rb_root;
    267	}
    268
    269	if (callchain_param.mode == CHAIN_GRAPH_REL)
    270		total_samples = parent_samples;
    271
    272	ret += __callchain__fprintf_graph(fp, root, total_samples,
    273					  1, 1, left_margin);
    274	if (ret) {
    275		/* do not add a blank line if it printed nothing */
    276		ret += fprintf(fp, "\n");
    277	}
    278
    279	return ret;
    280}
    281
    282static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
    283					u64 total_samples)
    284{
    285	struct callchain_list *chain;
    286	size_t ret = 0;
    287	char bf[1024];
    288
    289	if (!node)
    290		return 0;
    291
    292	ret += __callchain__fprintf_flat(fp, node->parent, total_samples);
    293
    294
    295	list_for_each_entry(chain, &node->val, list) {
    296		if (chain->ip >= PERF_CONTEXT_MAX)
    297			continue;
    298		ret += fprintf(fp, "                %s\n", callchain_list__sym_name(chain,
    299					bf, sizeof(bf), false));
    300	}
    301
    302	return ret;
    303}
    304
    305static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,
    306				      u64 total_samples)
    307{
    308	size_t ret = 0;
    309	u32 entries_printed = 0;
    310	struct callchain_node *chain;
    311	struct rb_node *rb_node = rb_first(tree);
    312
    313	while (rb_node) {
    314		chain = rb_entry(rb_node, struct callchain_node, rb_node);
    315
    316		ret += fprintf(fp, "           ");
    317		ret += callchain_node__fprintf_value(chain, fp, total_samples);
    318		ret += fprintf(fp, "\n");
    319		ret += __callchain__fprintf_flat(fp, chain, total_samples);
    320		ret += fprintf(fp, "\n");
    321		if (++entries_printed == callchain_param.print_limit)
    322			break;
    323
    324		rb_node = rb_next(rb_node);
    325	}
    326
    327	return ret;
    328}
    329
    330static size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
    331{
    332	const char *sep = symbol_conf.field_sep ?: ";";
    333	struct callchain_list *chain;
    334	size_t ret = 0;
    335	char bf[1024];
    336	bool first;
    337
    338	if (!node)
    339		return 0;
    340
    341	ret += __callchain__fprintf_folded(fp, node->parent);
    342
    343	first = (ret == 0);
    344	list_for_each_entry(chain, &node->val, list) {
    345		if (chain->ip >= PERF_CONTEXT_MAX)
    346			continue;
    347		ret += fprintf(fp, "%s%s", first ? "" : sep,
    348			       callchain_list__sym_name(chain,
    349						bf, sizeof(bf), false));
    350		first = false;
    351	}
    352
    353	return ret;
    354}
    355
    356static size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree,
    357					u64 total_samples)
    358{
    359	size_t ret = 0;
    360	u32 entries_printed = 0;
    361	struct callchain_node *chain;
    362	struct rb_node *rb_node = rb_first(tree);
    363
    364	while (rb_node) {
    365
    366		chain = rb_entry(rb_node, struct callchain_node, rb_node);
    367
    368		ret += callchain_node__fprintf_value(chain, fp, total_samples);
    369		ret += fprintf(fp, " ");
    370		ret += __callchain__fprintf_folded(fp, chain);
    371		ret += fprintf(fp, "\n");
    372		if (++entries_printed == callchain_param.print_limit)
    373			break;
    374
    375		rb_node = rb_next(rb_node);
    376	}
    377
    378	return ret;
    379}
    380
    381static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
    382					    u64 total_samples, int left_margin,
    383					    FILE *fp)
    384{
    385	u64 parent_samples = he->stat.period;
    386
    387	if (symbol_conf.cumulate_callchain)
    388		parent_samples = he->stat_acc->period;
    389
    390	switch (callchain_param.mode) {
    391	case CHAIN_GRAPH_REL:
    392		return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
    393						parent_samples, left_margin);
    394		break;
    395	case CHAIN_GRAPH_ABS:
    396		return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
    397						parent_samples, left_margin);
    398		break;
    399	case CHAIN_FLAT:
    400		return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
    401		break;
    402	case CHAIN_FOLDED:
    403		return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples);
    404		break;
    405	case CHAIN_NONE:
    406		break;
    407	default:
    408		pr_err("Bad callchain mode\n");
    409	}
    410
    411	return 0;
    412}
    413
    414int __hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp,
    415			   struct perf_hpp_list *hpp_list)
    416{
    417	const char *sep = symbol_conf.field_sep;
    418	struct perf_hpp_fmt *fmt;
    419	char *start = hpp->buf;
    420	int ret;
    421	bool first = true;
    422
    423	if (symbol_conf.exclude_other && !he->parent)
    424		return 0;
    425
    426	perf_hpp_list__for_each_format(hpp_list, fmt) {
    427		if (perf_hpp__should_skip(fmt, he->hists))
    428			continue;
    429
    430		/*
    431		 * If there's no field_sep, we still need
    432		 * to display initial '  '.
    433		 */
    434		if (!sep || !first) {
    435			ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
    436			advance_hpp(hpp, ret);
    437		} else
    438			first = false;
    439
    440		if (perf_hpp__use_color() && fmt->color)
    441			ret = fmt->color(fmt, hpp, he);
    442		else
    443			ret = fmt->entry(fmt, hpp, he);
    444
    445		ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
    446		advance_hpp(hpp, ret);
    447	}
    448
    449	return hpp->buf - start;
    450}
    451
    452static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
    453{
    454	return __hist_entry__snprintf(he, hpp, he->hists->hpp_list);
    455}
    456
    457static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
    458					 struct perf_hpp *hpp,
    459					 struct hists *hists,
    460					 FILE *fp)
    461{
    462	const char *sep = symbol_conf.field_sep;
    463	struct perf_hpp_fmt *fmt;
    464	struct perf_hpp_list_node *fmt_node;
    465	char *buf = hpp->buf;
    466	size_t size = hpp->size;
    467	int ret, printed = 0;
    468	bool first = true;
    469
    470	if (symbol_conf.exclude_other && !he->parent)
    471		return 0;
    472
    473	ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, "");
    474	advance_hpp(hpp, ret);
    475
    476	/* the first hpp_list_node is for overhead columns */
    477	fmt_node = list_first_entry(&hists->hpp_formats,
    478				    struct perf_hpp_list_node, list);
    479	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
    480		/*
    481		 * If there's no field_sep, we still need
    482		 * to display initial '  '.
    483		 */
    484		if (!sep || !first) {
    485			ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
    486			advance_hpp(hpp, ret);
    487		} else
    488			first = false;
    489
    490		if (perf_hpp__use_color() && fmt->color)
    491			ret = fmt->color(fmt, hpp, he);
    492		else
    493			ret = fmt->entry(fmt, hpp, he);
    494
    495		ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
    496		advance_hpp(hpp, ret);
    497	}
    498
    499	if (!sep)
    500		ret = scnprintf(hpp->buf, hpp->size, "%*s",
    501				(hists->nr_hpp_node - 2) * HIERARCHY_INDENT, "");
    502	advance_hpp(hpp, ret);
    503
    504	printed += fprintf(fp, "%s", buf);
    505
    506	perf_hpp_list__for_each_format(he->hpp_list, fmt) {
    507		hpp->buf  = buf;
    508		hpp->size = size;
    509
    510		/*
    511		 * No need to call hist_entry__snprintf_alignment() since this
    512		 * fmt is always the last column in the hierarchy mode.
    513		 */
    514		if (perf_hpp__use_color() && fmt->color)
    515			fmt->color(fmt, hpp, he);
    516		else
    517			fmt->entry(fmt, hpp, he);
    518
    519		/*
    520		 * dynamic entries are right-aligned but we want left-aligned
    521		 * in the hierarchy mode
    522		 */
    523		printed += fprintf(fp, "%s%s", sep ?: "  ", skip_spaces(buf));
    524	}
    525	printed += putc('\n', fp);
    526
    527	if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
    528		u64 total = hists__total_period(hists);
    529
    530		printed += hist_entry_callchain__fprintf(he, total, 0, fp);
    531		goto out;
    532	}
    533
    534out:
    535	return printed;
    536}
    537
    538static int hist_entry__block_fprintf(struct hist_entry *he,
    539				     char *bf, size_t size,
    540				     FILE *fp)
    541{
    542	struct block_hist *bh = container_of(he, struct block_hist, he);
    543	int ret = 0;
    544
    545	for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) {
    546		struct perf_hpp hpp = {
    547			.buf		= bf,
    548			.size		= size,
    549			.skip		= false,
    550		};
    551
    552		bh->block_idx = i;
    553		hist_entry__snprintf(he, &hpp);
    554
    555		if (!hpp.skip)
    556			ret += fprintf(fp, "%s\n", bf);
    557	}
    558
    559	return ret;
    560}
    561
    562static int hist_entry__individual_block_fprintf(struct hist_entry *he,
    563						char *bf, size_t size,
    564						FILE *fp)
    565{
    566	int ret = 0;
    567
    568	struct perf_hpp hpp = {
    569		.buf		= bf,
    570		.size		= size,
    571		.skip		= false,
    572	};
    573
    574	hist_entry__snprintf(he, &hpp);
    575	if (!hpp.skip)
    576		ret += fprintf(fp, "%s\n", bf);
    577
    578	return ret;
    579}
    580
    581static int hist_entry__fprintf(struct hist_entry *he, size_t size,
    582			       char *bf, size_t bfsz, FILE *fp,
    583			       bool ignore_callchains)
    584{
    585	int ret;
    586	int callchain_ret = 0;
    587	struct perf_hpp hpp = {
    588		.buf		= bf,
    589		.size		= size,
    590	};
    591	struct hists *hists = he->hists;
    592	u64 total_period = hists->stats.total_period;
    593
    594	if (size == 0 || size > bfsz)
    595		size = hpp.size = bfsz;
    596
    597	if (symbol_conf.report_hierarchy)
    598		return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
    599
    600	if (symbol_conf.report_block)
    601		return hist_entry__block_fprintf(he, bf, size, fp);
    602
    603	if (symbol_conf.report_individual_block)
    604		return hist_entry__individual_block_fprintf(he, bf, size, fp);
    605
    606	hist_entry__snprintf(he, &hpp);
    607
    608	ret = fprintf(fp, "%s\n", bf);
    609
    610	if (hist_entry__has_callchains(he) && !ignore_callchains)
    611		callchain_ret = hist_entry_callchain__fprintf(he, total_period,
    612							      0, fp);
    613
    614	ret += callchain_ret;
    615
    616	return ret;
    617}
    618
    619static int print_hierarchy_indent(const char *sep, int indent,
    620				  const char *line, FILE *fp)
    621{
    622	int width;
    623
    624	if (sep != NULL || indent < 2)
    625		return 0;
    626
    627	width = (indent - 2) * HIERARCHY_INDENT;
    628
    629	return fprintf(fp, "%-*.*s", width, width, line);
    630}
    631
    632static int hists__fprintf_hierarchy_headers(struct hists *hists,
    633					    struct perf_hpp *hpp, FILE *fp)
    634{
    635	bool first_node, first_col;
    636	int indent;
    637	int depth;
    638	unsigned width = 0;
    639	unsigned header_width = 0;
    640	struct perf_hpp_fmt *fmt;
    641	struct perf_hpp_list_node *fmt_node;
    642	const char *sep = symbol_conf.field_sep;
    643
    644	indent = hists->nr_hpp_node;
    645
    646	/* preserve max indent depth for column headers */
    647	print_hierarchy_indent(sep, indent, " ", fp);
    648
    649	/* the first hpp_list_node is for overhead columns */
    650	fmt_node = list_first_entry(&hists->hpp_formats,
    651				    struct perf_hpp_list_node, list);
    652
    653	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
    654		fmt->header(fmt, hpp, hists, 0, NULL);
    655		fprintf(fp, "%s%s", hpp->buf, sep ?: "  ");
    656	}
    657
    658	/* combine sort headers with ' / ' */
    659	first_node = true;
    660	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
    661		if (!first_node)
    662			header_width += fprintf(fp, " / ");
    663		first_node = false;
    664
    665		first_col = true;
    666		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
    667			if (perf_hpp__should_skip(fmt, hists))
    668				continue;
    669
    670			if (!first_col)
    671				header_width += fprintf(fp, "+");
    672			first_col = false;
    673
    674			fmt->header(fmt, hpp, hists, 0, NULL);
    675
    676			header_width += fprintf(fp, "%s", strim(hpp->buf));
    677		}
    678	}
    679
    680	fprintf(fp, "\n# ");
    681
    682	/* preserve max indent depth for initial dots */
    683	print_hierarchy_indent(sep, indent, dots, fp);
    684
    685	/* the first hpp_list_node is for overhead columns */
    686	fmt_node = list_first_entry(&hists->hpp_formats,
    687				    struct perf_hpp_list_node, list);
    688
    689	first_col = true;
    690	perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
    691		if (!first_col)
    692			fprintf(fp, "%s", sep ?: "..");
    693		first_col = false;
    694
    695		width = fmt->width(fmt, hpp, hists);
    696		fprintf(fp, "%.*s", width, dots);
    697	}
    698
    699	depth = 0;
    700	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
    701		first_col = true;
    702		width = depth * HIERARCHY_INDENT;
    703
    704		perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
    705			if (perf_hpp__should_skip(fmt, hists))
    706				continue;
    707
    708			if (!first_col)
    709				width++;  /* for '+' sign between column header */
    710			first_col = false;
    711
    712			width += fmt->width(fmt, hpp, hists);
    713		}
    714
    715		if (width > header_width)
    716			header_width = width;
    717
    718		depth++;
    719	}
    720
    721	fprintf(fp, "%s%-.*s", sep ?: "  ", header_width, dots);
    722
    723	fprintf(fp, "\n#\n");
    724
    725	return 2;
    726}
    727
    728static void fprintf_line(struct hists *hists, struct perf_hpp *hpp,
    729			 int line, FILE *fp)
    730{
    731	struct perf_hpp_fmt *fmt;
    732	const char *sep = symbol_conf.field_sep;
    733	bool first = true;
    734	int span = 0;
    735
    736	hists__for_each_format(hists, fmt) {
    737		if (perf_hpp__should_skip(fmt, hists))
    738			continue;
    739
    740		if (!first && !span)
    741			fprintf(fp, "%s", sep ?: "  ");
    742		else
    743			first = false;
    744
    745		fmt->header(fmt, hpp, hists, line, &span);
    746
    747		if (!span)
    748			fprintf(fp, "%s", hpp->buf);
    749	}
    750}
    751
    752static int
    753hists__fprintf_standard_headers(struct hists *hists,
    754				struct perf_hpp *hpp,
    755				FILE *fp)
    756{
    757	struct perf_hpp_list *hpp_list = hists->hpp_list;
    758	struct perf_hpp_fmt *fmt;
    759	unsigned int width;
    760	const char *sep = symbol_conf.field_sep;
    761	bool first = true;
    762	int line;
    763
    764	for (line = 0; line < hpp_list->nr_header_lines; line++) {
    765		/* first # is displayed one level up */
    766		if (line)
    767			fprintf(fp, "# ");
    768		fprintf_line(hists, hpp, line, fp);
    769		fprintf(fp, "\n");
    770	}
    771
    772	if (sep)
    773		return hpp_list->nr_header_lines;
    774
    775	first = true;
    776
    777	fprintf(fp, "# ");
    778
    779	hists__for_each_format(hists, fmt) {
    780		unsigned int i;
    781
    782		if (perf_hpp__should_skip(fmt, hists))
    783			continue;
    784
    785		if (!first)
    786			fprintf(fp, "%s", sep ?: "  ");
    787		else
    788			first = false;
    789
    790		width = fmt->width(fmt, hpp, hists);
    791		for (i = 0; i < width; i++)
    792			fprintf(fp, ".");
    793	}
    794
    795	fprintf(fp, "\n");
    796	fprintf(fp, "#\n");
    797	return hpp_list->nr_header_lines + 2;
    798}
    799
    800int hists__fprintf_headers(struct hists *hists, FILE *fp)
    801{
    802	char bf[1024];
    803	struct perf_hpp dummy_hpp = {
    804		.buf	= bf,
    805		.size	= sizeof(bf),
    806	};
    807
    808	fprintf(fp, "# ");
    809
    810	if (symbol_conf.report_hierarchy)
    811		return hists__fprintf_hierarchy_headers(hists, &dummy_hpp, fp);
    812	else
    813		return hists__fprintf_standard_headers(hists, &dummy_hpp, fp);
    814
    815}
    816
    817size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
    818		      int max_cols, float min_pcnt, FILE *fp,
    819		      bool ignore_callchains)
    820{
    821	struct rb_node *nd;
    822	size_t ret = 0;
    823	const char *sep = symbol_conf.field_sep;
    824	int nr_rows = 0;
    825	size_t linesz;
    826	char *line = NULL;
    827	unsigned indent;
    828
    829	init_rem_hits();
    830
    831	hists__reset_column_width(hists);
    832
    833	if (symbol_conf.col_width_list_str)
    834		perf_hpp__set_user_width(symbol_conf.col_width_list_str);
    835
    836	if (show_header)
    837		nr_rows += hists__fprintf_headers(hists, fp);
    838
    839	if (max_rows && nr_rows >= max_rows)
    840		goto out;
    841
    842	linesz = hists__sort_list_width(hists) + 3 + 1;
    843	linesz += perf_hpp__color_overhead();
    844	line = malloc(linesz);
    845	if (line == NULL) {
    846		ret = -1;
    847		goto out;
    848	}
    849
    850	indent = hists__overhead_width(hists) + 4;
    851
    852	for (nd = rb_first_cached(&hists->entries); nd;
    853	     nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) {
    854		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
    855		float percent;
    856
    857		if (h->filtered)
    858			continue;
    859
    860		if (symbol_conf.report_individual_block)
    861			percent = block_info__total_cycles_percent(h);
    862		else
    863			percent = hist_entry__get_percent_limit(h);
    864
    865		if (percent < min_pcnt)
    866			continue;
    867
    868		ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, ignore_callchains);
    869
    870		if (max_rows && ++nr_rows >= max_rows)
    871			break;
    872
    873		/*
    874		 * If all children are filtered out or percent-limited,
    875		 * display "no entry >= x.xx%" message.
    876		 */
    877		if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
    878			int depth = hists->nr_hpp_node + h->depth + 1;
    879
    880			print_hierarchy_indent(sep, depth, " ", fp);
    881			fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
    882
    883			if (max_rows && ++nr_rows >= max_rows)
    884				break;
    885		}
    886
    887		if (h->ms.map == NULL && verbose > 1) {
    888			maps__fprintf(h->thread->maps, fp);
    889			fprintf(fp, "%.10s end\n", graph_dotted_line);
    890		}
    891	}
    892
    893	free(line);
    894out:
    895	zfree(&rem_sq_bracket);
    896
    897	return ret;
    898}
    899
    900size_t events_stats__fprintf(struct events_stats *stats, FILE *fp,
    901			     bool skip_empty)
    902{
    903	int i;
    904	size_t ret = 0;
    905	u32 total = stats->nr_events[0];
    906
    907	for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
    908		const char *name;
    909
    910		name = perf_event__name(i);
    911		if (!strcmp(name, "UNKNOWN"))
    912			continue;
    913		if (skip_empty && !stats->nr_events[i])
    914			continue;
    915
    916		if (i && total) {
    917			ret += fprintf(fp, "%16s events: %10d  (%4.1f%%)\n",
    918				       name, stats->nr_events[i],
    919				       100.0 * stats->nr_events[i] / total);
    920		} else {
    921			ret += fprintf(fp, "%16s events: %10d\n",
    922				       name, stats->nr_events[i]);
    923		}
    924	}
    925
    926	return ret;
    927}