Home | History | Annotate | Download | only in stdio
      1 #include <stdio.h>
      2 
      3 #include "../../util/util.h"
      4 #include "../../util/hist.h"
      5 #include "../../util/sort.h"
      6 #include "../../util/evsel.h"
      7 
      8 
      9 static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
     10 {
     11 	int i;
     12 	int ret = fprintf(fp, "            ");
     13 
     14 	for (i = 0; i < left_margin; i++)
     15 		ret += fprintf(fp, " ");
     16 
     17 	return ret;
     18 }
     19 
     20 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
     21 					  int left_margin)
     22 {
     23 	int i;
     24 	size_t ret = callchain__fprintf_left_margin(fp, left_margin);
     25 
     26 	for (i = 0; i < depth; i++)
     27 		if (depth_mask & (1 << i))
     28 			ret += fprintf(fp, "|          ");
     29 		else
     30 			ret += fprintf(fp, "           ");
     31 
     32 	ret += fprintf(fp, "\n");
     33 
     34 	return ret;
     35 }
     36 
     37 static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
     38 				     int depth, int depth_mask, int period,
     39 				     u64 total_samples, u64 hits,
     40 				     int left_margin)
     41 {
     42 	int i;
     43 	size_t ret = 0;
     44 
     45 	ret += callchain__fprintf_left_margin(fp, left_margin);
     46 	for (i = 0; i < depth; i++) {
     47 		if (depth_mask & (1 << i))
     48 			ret += fprintf(fp, "|");
     49 		else
     50 			ret += fprintf(fp, " ");
     51 		if (!period && i == depth - 1) {
     52 			double percent;
     53 
     54 			percent = hits * 100.0 / total_samples;
     55 			ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
     56 		} else
     57 			ret += fprintf(fp, "%s", "          ");
     58 	}
     59 	if (chain->ms.sym)
     60 		ret += fprintf(fp, "%s\n", chain->ms.sym->name);
     61 	else
     62 		ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
     63 
     64 	return ret;
     65 }
     66 
     67 static struct symbol *rem_sq_bracket;
     68 static struct callchain_list rem_hits;
     69 
     70 static void init_rem_hits(void)
     71 {
     72 	rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
     73 	if (!rem_sq_bracket) {
     74 		fprintf(stderr, "Not enough memory to display remaining hits\n");
     75 		return;
     76 	}
     77 
     78 	strcpy(rem_sq_bracket->name, "[...]");
     79 	rem_hits.ms.sym = rem_sq_bracket;
     80 }
     81 
     82 static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
     83 					 u64 total_samples, int depth,
     84 					 int depth_mask, int left_margin)
     85 {
     86 	struct rb_node *node, *next;
     87 	struct callchain_node *child;
     88 	struct callchain_list *chain;
     89 	int new_depth_mask = depth_mask;
     90 	u64 remaining;
     91 	size_t ret = 0;
     92 	int i;
     93 	uint entries_printed = 0;
     94 
     95 	remaining = total_samples;
     96 
     97 	node = rb_first(root);
     98 	while (node) {
     99 		u64 new_total;
    100 		u64 cumul;
    101 
    102 		child = rb_entry(node, struct callchain_node, rb_node);
    103 		cumul = callchain_cumul_hits(child);
    104 		remaining -= cumul;
    105 
    106 		/*
    107 		 * The depth mask manages the output of pipes that show
    108 		 * the depth. We don't want to keep the pipes of the current
    109 		 * level for the last child of this depth.
    110 		 * Except if we have remaining filtered hits. They will
    111 		 * supersede the last child
    112 		 */
    113 		next = rb_next(node);
    114 		if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
    115 			new_depth_mask &= ~(1 << (depth - 1));
    116 
    117 		/*
    118 		 * But we keep the older depth mask for the line separator
    119 		 * to keep the level link until we reach the last child
    120 		 */
    121 		ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
    122 						   left_margin);
    123 		i = 0;
    124 		list_for_each_entry(chain, &child->val, list) {
    125 			ret += ipchain__fprintf_graph(fp, chain, depth,
    126 						      new_depth_mask, i++,
    127 						      total_samples,
    128 						      cumul,
    129 						      left_margin);
    130 		}
    131 
    132 		if (callchain_param.mode == CHAIN_GRAPH_REL)
    133 			new_total = child->children_hit;
    134 		else
    135 			new_total = total_samples;
    136 
    137 		ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
    138 						  depth + 1,
    139 						  new_depth_mask | (1 << depth),
    140 						  left_margin);
    141 		node = next;
    142 		if (++entries_printed == callchain_param.print_limit)
    143 			break;
    144 	}
    145 
    146 	if (callchain_param.mode == CHAIN_GRAPH_REL &&
    147 		remaining && remaining != total_samples) {
    148 
    149 		if (!rem_sq_bracket)
    150 			return ret;
    151 
    152 		new_depth_mask &= ~(1 << (depth - 1));
    153 		ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
    154 					      new_depth_mask, 0, total_samples,
    155 					      remaining, left_margin);
    156 	}
    157 
    158 	return ret;
    159 }
    160 
    161 static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
    162 				       u64 total_samples, int left_margin)
    163 {
    164 	struct callchain_node *cnode;
    165 	struct callchain_list *chain;
    166 	u32 entries_printed = 0;
    167 	bool printed = false;
    168 	struct rb_node *node;
    169 	int i = 0;
    170 	int ret = 0;
    171 
    172 	/*
    173 	 * If have one single callchain root, don't bother printing
    174 	 * its percentage (100 % in fractal mode and the same percentage
    175 	 * than the hist in graph mode). This also avoid one level of column.
    176 	 */
    177 	node = rb_first(root);
    178 	if (node && !rb_next(node)) {
    179 		cnode = rb_entry(node, struct callchain_node, rb_node);
    180 		list_for_each_entry(chain, &cnode->val, list) {
    181 			/*
    182 			 * If we sort by symbol, the first entry is the same than
    183 			 * the symbol. No need to print it otherwise it appears as
    184 			 * displayed twice.
    185 			 */
    186 			if (!i++ && sort__first_dimension == SORT_SYM)
    187 				continue;
    188 			if (!printed) {
    189 				ret += callchain__fprintf_left_margin(fp, left_margin);
    190 				ret += fprintf(fp, "|\n");
    191 				ret += callchain__fprintf_left_margin(fp, left_margin);
    192 				ret += fprintf(fp, "---");
    193 				left_margin += 3;
    194 				printed = true;
    195 			} else
    196 				ret += callchain__fprintf_left_margin(fp, left_margin);
    197 
    198 			if (chain->ms.sym)
    199 				ret += fprintf(fp, " %s\n", chain->ms.sym->name);
    200 			else
    201 				ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
    202 
    203 			if (++entries_printed == callchain_param.print_limit)
    204 				break;
    205 		}
    206 		root = &cnode->rb_root;
    207 	}
    208 
    209 	ret += __callchain__fprintf_graph(fp, root, total_samples,
    210 					  1, 1, left_margin);
    211 	ret += fprintf(fp, "\n");
    212 
    213 	return ret;
    214 }
    215 
    216 static size_t __callchain__fprintf_flat(FILE *fp,
    217 					struct callchain_node *self,
    218 					u64 total_samples)
    219 {
    220 	struct callchain_list *chain;
    221 	size_t ret = 0;
    222 
    223 	if (!self)
    224 		return 0;
    225 
    226 	ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
    227 
    228 
    229 	list_for_each_entry(chain, &self->val, list) {
    230 		if (chain->ip >= PERF_CONTEXT_MAX)
    231 			continue;
    232 		if (chain->ms.sym)
    233 			ret += fprintf(fp, "                %s\n", chain->ms.sym->name);
    234 		else
    235 			ret += fprintf(fp, "                %p\n",
    236 					(void *)(long)chain->ip);
    237 	}
    238 
    239 	return ret;
    240 }
    241 
    242 static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
    243 				      u64 total_samples)
    244 {
    245 	size_t ret = 0;
    246 	u32 entries_printed = 0;
    247 	struct rb_node *rb_node;
    248 	struct callchain_node *chain;
    249 
    250 	rb_node = rb_first(self);
    251 	while (rb_node) {
    252 		double percent;
    253 
    254 		chain = rb_entry(rb_node, struct callchain_node, rb_node);
    255 		percent = chain->hit * 100.0 / total_samples;
    256 
    257 		ret = percent_color_fprintf(fp, "           %6.2f%%\n", percent);
    258 		ret += __callchain__fprintf_flat(fp, chain, total_samples);
    259 		ret += fprintf(fp, "\n");
    260 		if (++entries_printed == callchain_param.print_limit)
    261 			break;
    262 
    263 		rb_node = rb_next(rb_node);
    264 	}
    265 
    266 	return ret;
    267 }
    268 
    269 static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
    270 					    u64 total_samples, int left_margin,
    271 					    FILE *fp)
    272 {
    273 	switch (callchain_param.mode) {
    274 	case CHAIN_GRAPH_REL:
    275 		return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period,
    276 						left_margin);
    277 		break;
    278 	case CHAIN_GRAPH_ABS:
    279 		return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
    280 						left_margin);
    281 		break;
    282 	case CHAIN_FLAT:
    283 		return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
    284 		break;
    285 	case CHAIN_NONE:
    286 		break;
    287 	default:
    288 		pr_err("Bad callchain mode\n");
    289 	}
    290 
    291 	return 0;
    292 }
    293 
    294 static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
    295 					    struct hists *hists,
    296 					    FILE *fp)
    297 {
    298 	int left_margin = 0;
    299 	u64 total_period = hists->stats.total_period;
    300 
    301 	if (sort__first_dimension == SORT_COMM) {
    302 		struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
    303 							 typeof(*se), list);
    304 		left_margin = hists__col_len(hists, se->se_width_idx);
    305 		left_margin -= thread__comm_len(he->thread);
    306 	}
    307 
    308 	return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
    309 }
    310 
    311 static inline void advance_hpp(struct perf_hpp *hpp, int inc)
    312 {
    313 	hpp->buf  += inc;
    314 	hpp->size -= inc;
    315 }
    316 
    317 static int hist_entry__period_snprintf(struct perf_hpp *hpp,
    318 				       struct hist_entry *he)
    319 {
    320 	const char *sep = symbol_conf.field_sep;
    321 	struct perf_hpp_fmt *fmt;
    322 	char *start = hpp->buf;
    323 	int ret;
    324 	bool first = true;
    325 
    326 	if (symbol_conf.exclude_other && !he->parent)
    327 		return 0;
    328 
    329 	perf_hpp__for_each_format(fmt) {
    330 		/*
    331 		 * If there's no field_sep, we still need
    332 		 * to display initial '  '.
    333 		 */
    334 		if (!sep || !first) {
    335 			ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: "  ");
    336 			advance_hpp(hpp, ret);
    337 		} else
    338 			first = false;
    339 
    340 		if (perf_hpp__use_color() && fmt->color)
    341 			ret = fmt->color(fmt, hpp, he);
    342 		else
    343 			ret = fmt->entry(fmt, hpp, he);
    344 
    345 		advance_hpp(hpp, ret);
    346 	}
    347 
    348 	return hpp->buf - start;
    349 }
    350 
    351 static int hist_entry__fprintf(struct hist_entry *he, size_t size,
    352 			       struct hists *hists,
    353 			       char *bf, size_t bfsz, FILE *fp)
    354 {
    355 	int ret;
    356 	struct perf_hpp hpp = {
    357 		.buf		= bf,
    358 		.size		= size,
    359 	};
    360 
    361 	if (size == 0 || size > bfsz)
    362 		size = hpp.size = bfsz;
    363 
    364 	ret = hist_entry__period_snprintf(&hpp, he);
    365 	hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
    366 
    367 	ret = fprintf(fp, "%s\n", bf);
    368 
    369 	if (symbol_conf.use_callchain)
    370 		ret += hist_entry__callchain_fprintf(he, hists, fp);
    371 
    372 	return ret;
    373 }
    374 
    375 size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
    376 		      int max_cols, float min_pcnt, FILE *fp)
    377 {
    378 	struct perf_hpp_fmt *fmt;
    379 	struct sort_entry *se;
    380 	struct rb_node *nd;
    381 	size_t ret = 0;
    382 	unsigned int width;
    383 	const char *sep = symbol_conf.field_sep;
    384 	const char *col_width = symbol_conf.col_width_list_str;
    385 	int nr_rows = 0;
    386 	char bf[96];
    387 	struct perf_hpp dummy_hpp = {
    388 		.buf	= bf,
    389 		.size	= sizeof(bf),
    390 		.ptr	= hists_to_evsel(hists),
    391 	};
    392 	bool first = true;
    393 	size_t linesz;
    394 	char *line = NULL;
    395 
    396 	init_rem_hits();
    397 
    398 	if (!show_header)
    399 		goto print_entries;
    400 
    401 	fprintf(fp, "# ");
    402 
    403 	perf_hpp__for_each_format(fmt) {
    404 		if (!first)
    405 			fprintf(fp, "%s", sep ?: "  ");
    406 		else
    407 			first = false;
    408 
    409 		fmt->header(fmt, &dummy_hpp);
    410 		fprintf(fp, "%s", bf);
    411 	}
    412 
    413 	list_for_each_entry(se, &hist_entry__sort_list, list) {
    414 		if (se->elide)
    415 			continue;
    416 		if (sep) {
    417 			fprintf(fp, "%c%s", *sep, se->se_header);
    418 			continue;
    419 		}
    420 		width = strlen(se->se_header);
    421 		if (symbol_conf.col_width_list_str) {
    422 			if (col_width) {
    423 				hists__set_col_len(hists, se->se_width_idx,
    424 						   atoi(col_width));
    425 				col_width = strchr(col_width, ',');
    426 				if (col_width)
    427 					++col_width;
    428 			}
    429 		}
    430 		if (!hists__new_col_len(hists, se->se_width_idx, width))
    431 			width = hists__col_len(hists, se->se_width_idx);
    432 		fprintf(fp, "  %*s", width, se->se_header);
    433 	}
    434 
    435 	fprintf(fp, "\n");
    436 	if (max_rows && ++nr_rows >= max_rows)
    437 		goto out;
    438 
    439 	if (sep)
    440 		goto print_entries;
    441 
    442 	first = true;
    443 
    444 	fprintf(fp, "# ");
    445 
    446 	perf_hpp__for_each_format(fmt) {
    447 		unsigned int i;
    448 
    449 		if (!first)
    450 			fprintf(fp, "%s", sep ?: "  ");
    451 		else
    452 			first = false;
    453 
    454 		width = fmt->width(fmt, &dummy_hpp);
    455 		for (i = 0; i < width; i++)
    456 			fprintf(fp, ".");
    457 	}
    458 
    459 	list_for_each_entry(se, &hist_entry__sort_list, list) {
    460 		unsigned int i;
    461 
    462 		if (se->elide)
    463 			continue;
    464 
    465 		fprintf(fp, "  ");
    466 		width = hists__col_len(hists, se->se_width_idx);
    467 		if (width == 0)
    468 			width = strlen(se->se_header);
    469 		for (i = 0; i < width; i++)
    470 			fprintf(fp, ".");
    471 	}
    472 
    473 	fprintf(fp, "\n");
    474 	if (max_rows && ++nr_rows >= max_rows)
    475 		goto out;
    476 
    477 	fprintf(fp, "#\n");
    478 	if (max_rows && ++nr_rows >= max_rows)
    479 		goto out;
    480 
    481 print_entries:
    482 	linesz = hists__sort_list_width(hists) + 3 + 1;
    483 	linesz += perf_hpp__color_overhead();
    484 	line = malloc(linesz);
    485 	if (line == NULL) {
    486 		ret = -1;
    487 		goto out;
    488 	}
    489 
    490 	for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
    491 		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
    492 		float percent = h->stat.period * 100.0 /
    493 					hists->stats.total_period;
    494 
    495 		if (h->filtered)
    496 			continue;
    497 
    498 		if (percent < min_pcnt)
    499 			continue;
    500 
    501 		ret += hist_entry__fprintf(h, max_cols, hists, line, linesz, fp);
    502 
    503 		if (max_rows && ++nr_rows >= max_rows)
    504 			break;
    505 
    506 		if (h->ms.map == NULL && verbose > 1) {
    507 			__map_groups__fprintf_maps(&h->thread->mg,
    508 						   MAP__FUNCTION, verbose, fp);
    509 			fprintf(fp, "%.10s end\n", graph_dotted_line);
    510 		}
    511 	}
    512 
    513 	free(line);
    514 out:
    515 	free(rem_sq_bracket);
    516 
    517 	return ret;
    518 }
    519 
    520 size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
    521 {
    522 	int i;
    523 	size_t ret = 0;
    524 
    525 	for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
    526 		const char *name;
    527 
    528 		if (stats->nr_events[i] == 0)
    529 			continue;
    530 
    531 		name = perf_event__name(i);
    532 		if (!strcmp(name, "UNKNOWN"))
    533 			continue;
    534 
    535 		ret += fprintf(fp, "%16s events: %10d\n", name,
    536 			       stats->nr_events[i]);
    537 	}
    538 
    539 	return ret;
    540 }
    541