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