hist.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. #include <stdio.h>
  2. #include "../../util/util.h"
  3. #include "../../util/hist.h"
  4. #include "../../util/sort.h"
  5. #include "../../util/evsel.h"
  6. static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
  7. {
  8. int i;
  9. int ret = fprintf(fp, " ");
  10. for (i = 0; i < left_margin; i++)
  11. ret += fprintf(fp, " ");
  12. return ret;
  13. }
  14. static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
  15. int left_margin)
  16. {
  17. int i;
  18. size_t ret = callchain__fprintf_left_margin(fp, left_margin);
  19. for (i = 0; i < depth; i++)
  20. if (depth_mask & (1 << i))
  21. ret += fprintf(fp, "| ");
  22. else
  23. ret += fprintf(fp, " ");
  24. ret += fprintf(fp, "\n");
  25. return ret;
  26. }
  27. static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
  28. struct callchain_list *chain,
  29. int depth, int depth_mask, int period,
  30. u64 total_samples, int left_margin)
  31. {
  32. int i;
  33. size_t ret = 0;
  34. char bf[1024];
  35. ret += callchain__fprintf_left_margin(fp, left_margin);
  36. for (i = 0; i < depth; i++) {
  37. if (depth_mask & (1 << i))
  38. ret += fprintf(fp, "|");
  39. else
  40. ret += fprintf(fp, " ");
  41. if (!period && i == depth - 1) {
  42. ret += fprintf(fp, "--");
  43. ret += callchain_node__fprintf_value(node, fp, total_samples);
  44. ret += fprintf(fp, "--");
  45. } else
  46. ret += fprintf(fp, "%s", " ");
  47. }
  48. fputs(callchain_list__sym_name(chain, bf, sizeof(bf), false), fp);
  49. fputc('\n', fp);
  50. return ret;
  51. }
  52. static struct symbol *rem_sq_bracket;
  53. static struct callchain_list rem_hits;
  54. static void init_rem_hits(void)
  55. {
  56. rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
  57. if (!rem_sq_bracket) {
  58. fprintf(stderr, "Not enough memory to display remaining hits\n");
  59. return;
  60. }
  61. strcpy(rem_sq_bracket->name, "[...]");
  62. rem_hits.ms.sym = rem_sq_bracket;
  63. }
  64. static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
  65. u64 total_samples, int depth,
  66. int depth_mask, int left_margin)
  67. {
  68. struct rb_node *node, *next;
  69. struct callchain_node *child = NULL;
  70. struct callchain_list *chain;
  71. int new_depth_mask = depth_mask;
  72. u64 remaining;
  73. size_t ret = 0;
  74. int i;
  75. uint entries_printed = 0;
  76. int cumul_count = 0;
  77. remaining = total_samples;
  78. node = rb_first(root);
  79. while (node) {
  80. u64 new_total;
  81. u64 cumul;
  82. child = rb_entry(node, struct callchain_node, rb_node);
  83. cumul = callchain_cumul_hits(child);
  84. remaining -= cumul;
  85. cumul_count += callchain_cumul_counts(child);
  86. /*
  87. * The depth mask manages the output of pipes that show
  88. * the depth. We don't want to keep the pipes of the current
  89. * level for the last child of this depth.
  90. * Except if we have remaining filtered hits. They will
  91. * supersede the last child
  92. */
  93. next = rb_next(node);
  94. if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
  95. new_depth_mask &= ~(1 << (depth - 1));
  96. /*
  97. * But we keep the older depth mask for the line separator
  98. * to keep the level link until we reach the last child
  99. */
  100. ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
  101. left_margin);
  102. i = 0;
  103. list_for_each_entry(chain, &child->val, list) {
  104. ret += ipchain__fprintf_graph(fp, child, chain, depth,
  105. new_depth_mask, i++,
  106. total_samples,
  107. left_margin);
  108. }
  109. if (callchain_param.mode == CHAIN_GRAPH_REL)
  110. new_total = child->children_hit;
  111. else
  112. new_total = total_samples;
  113. ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
  114. depth + 1,
  115. new_depth_mask | (1 << depth),
  116. left_margin);
  117. node = next;
  118. if (++entries_printed == callchain_param.print_limit)
  119. break;
  120. }
  121. if (callchain_param.mode == CHAIN_GRAPH_REL &&
  122. remaining && remaining != total_samples) {
  123. struct callchain_node rem_node = {
  124. .hit = remaining,
  125. };
  126. if (!rem_sq_bracket)
  127. return ret;
  128. if (callchain_param.value == CCVAL_COUNT && child && child->parent) {
  129. rem_node.count = child->parent->children_count - cumul_count;
  130. if (rem_node.count <= 0)
  131. return ret;
  132. }
  133. new_depth_mask &= ~(1 << (depth - 1));
  134. ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth,
  135. new_depth_mask, 0, total_samples,
  136. left_margin);
  137. }
  138. return ret;
  139. }
  140. /*
  141. * If have one single callchain root, don't bother printing
  142. * its percentage (100 % in fractal mode and the same percentage
  143. * than the hist in graph mode). This also avoid one level of column.
  144. *
  145. * However when percent-limit applied, it's possible that single callchain
  146. * node have different (non-100% in fractal mode) percentage.
  147. */
  148. static bool need_percent_display(struct rb_node *node, u64 parent_samples)
  149. {
  150. struct callchain_node *cnode;
  151. if (rb_next(node))
  152. return true;
  153. cnode = rb_entry(node, struct callchain_node, rb_node);
  154. return callchain_cumul_hits(cnode) != parent_samples;
  155. }
  156. static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
  157. u64 total_samples, u64 parent_samples,
  158. int left_margin)
  159. {
  160. struct callchain_node *cnode;
  161. struct callchain_list *chain;
  162. u32 entries_printed = 0;
  163. bool printed = false;
  164. struct rb_node *node;
  165. int i = 0;
  166. int ret = 0;
  167. char bf[1024];
  168. node = rb_first(root);
  169. if (node && !need_percent_display(node, parent_samples)) {
  170. cnode = rb_entry(node, struct callchain_node, rb_node);
  171. list_for_each_entry(chain, &cnode->val, list) {
  172. /*
  173. * If we sort by symbol, the first entry is the same than
  174. * the symbol. No need to print it otherwise it appears as
  175. * displayed twice.
  176. */
  177. if (!i++ && field_order == NULL &&
  178. sort_order && !prefixcmp(sort_order, "sym"))
  179. continue;
  180. if (!printed) {
  181. ret += callchain__fprintf_left_margin(fp, left_margin);
  182. ret += fprintf(fp, "|\n");
  183. ret += callchain__fprintf_left_margin(fp, left_margin);
  184. ret += fprintf(fp, "---");
  185. left_margin += 3;
  186. printed = true;
  187. } else
  188. ret += callchain__fprintf_left_margin(fp, left_margin);
  189. ret += fprintf(fp, "%s\n", callchain_list__sym_name(chain, bf, sizeof(bf),
  190. false));
  191. if (++entries_printed == callchain_param.print_limit)
  192. break;
  193. }
  194. root = &cnode->rb_root;
  195. }
  196. if (callchain_param.mode == CHAIN_GRAPH_REL)
  197. total_samples = parent_samples;
  198. ret += __callchain__fprintf_graph(fp, root, total_samples,
  199. 1, 1, left_margin);
  200. if (ret) {
  201. /* do not add a blank line if it printed nothing */
  202. ret += fprintf(fp, "\n");
  203. }
  204. return ret;
  205. }
  206. static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
  207. u64 total_samples)
  208. {
  209. struct callchain_list *chain;
  210. size_t ret = 0;
  211. char bf[1024];
  212. if (!node)
  213. return 0;
  214. ret += __callchain__fprintf_flat(fp, node->parent, total_samples);
  215. list_for_each_entry(chain, &node->val, list) {
  216. if (chain->ip >= PERF_CONTEXT_MAX)
  217. continue;
  218. ret += fprintf(fp, " %s\n", callchain_list__sym_name(chain,
  219. bf, sizeof(bf), false));
  220. }
  221. return ret;
  222. }
  223. static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,
  224. u64 total_samples)
  225. {
  226. size_t ret = 0;
  227. u32 entries_printed = 0;
  228. struct callchain_node *chain;
  229. struct rb_node *rb_node = rb_first(tree);
  230. while (rb_node) {
  231. chain = rb_entry(rb_node, struct callchain_node, rb_node);
  232. ret += fprintf(fp, " ");
  233. ret += callchain_node__fprintf_value(chain, fp, total_samples);
  234. ret += fprintf(fp, "\n");
  235. ret += __callchain__fprintf_flat(fp, chain, total_samples);
  236. ret += fprintf(fp, "\n");
  237. if (++entries_printed == callchain_param.print_limit)
  238. break;
  239. rb_node = rb_next(rb_node);
  240. }
  241. return ret;
  242. }
  243. static size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
  244. {
  245. const char *sep = symbol_conf.field_sep ?: ";";
  246. struct callchain_list *chain;
  247. size_t ret = 0;
  248. char bf[1024];
  249. bool first;
  250. if (!node)
  251. return 0;
  252. ret += __callchain__fprintf_folded(fp, node->parent);
  253. first = (ret == 0);
  254. list_for_each_entry(chain, &node->val, list) {
  255. if (chain->ip >= PERF_CONTEXT_MAX)
  256. continue;
  257. ret += fprintf(fp, "%s%s", first ? "" : sep,
  258. callchain_list__sym_name(chain,
  259. bf, sizeof(bf), false));
  260. first = false;
  261. }
  262. return ret;
  263. }
  264. static size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree,
  265. u64 total_samples)
  266. {
  267. size_t ret = 0;
  268. u32 entries_printed = 0;
  269. struct callchain_node *chain;
  270. struct rb_node *rb_node = rb_first(tree);
  271. while (rb_node) {
  272. chain = rb_entry(rb_node, struct callchain_node, rb_node);
  273. ret += callchain_node__fprintf_value(chain, fp, total_samples);
  274. ret += fprintf(fp, " ");
  275. ret += __callchain__fprintf_folded(fp, chain);
  276. ret += fprintf(fp, "\n");
  277. if (++entries_printed == callchain_param.print_limit)
  278. break;
  279. rb_node = rb_next(rb_node);
  280. }
  281. return ret;
  282. }
  283. static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
  284. u64 total_samples, int left_margin,
  285. FILE *fp)
  286. {
  287. u64 parent_samples = he->stat.period;
  288. if (symbol_conf.cumulate_callchain)
  289. parent_samples = he->stat_acc->period;
  290. switch (callchain_param.mode) {
  291. case CHAIN_GRAPH_REL:
  292. return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
  293. parent_samples, left_margin);
  294. break;
  295. case CHAIN_GRAPH_ABS:
  296. return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
  297. parent_samples, left_margin);
  298. break;
  299. case CHAIN_FLAT:
  300. return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
  301. break;
  302. case CHAIN_FOLDED:
  303. return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples);
  304. break;
  305. case CHAIN_NONE:
  306. break;
  307. default:
  308. pr_err("Bad callchain mode\n");
  309. }
  310. return 0;
  311. }
  312. int __hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp,
  313. struct perf_hpp_list *hpp_list)
  314. {
  315. const char *sep = symbol_conf.field_sep;
  316. struct perf_hpp_fmt *fmt;
  317. char *start = hpp->buf;
  318. int ret;
  319. bool first = true;
  320. if (symbol_conf.exclude_other && !he->parent)
  321. return 0;
  322. perf_hpp_list__for_each_format(hpp_list, fmt) {
  323. if (perf_hpp__should_skip(fmt, he->hists))
  324. continue;
  325. /*
  326. * If there's no field_sep, we still need
  327. * to display initial ' '.
  328. */
  329. if (!sep || !first) {
  330. ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
  331. advance_hpp(hpp, ret);
  332. } else
  333. first = false;
  334. if (perf_hpp__use_color() && fmt->color)
  335. ret = fmt->color(fmt, hpp, he);
  336. else
  337. ret = fmt->entry(fmt, hpp, he);
  338. ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
  339. advance_hpp(hpp, ret);
  340. }
  341. return hpp->buf - start;
  342. }
  343. static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
  344. {
  345. return __hist_entry__snprintf(he, hpp, he->hists->hpp_list);
  346. }
  347. static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
  348. struct perf_hpp *hpp,
  349. struct hists *hists,
  350. FILE *fp)
  351. {
  352. const char *sep = symbol_conf.field_sep;
  353. struct perf_hpp_fmt *fmt;
  354. struct perf_hpp_list_node *fmt_node;
  355. char *buf = hpp->buf;
  356. size_t size = hpp->size;
  357. int ret, printed = 0;
  358. bool first = true;
  359. if (symbol_conf.exclude_other && !he->parent)
  360. return 0;
  361. ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, "");
  362. advance_hpp(hpp, ret);
  363. /* the first hpp_list_node is for overhead columns */
  364. fmt_node = list_first_entry(&hists->hpp_formats,
  365. struct perf_hpp_list_node, list);
  366. perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
  367. /*
  368. * If there's no field_sep, we still need
  369. * to display initial ' '.
  370. */
  371. if (!sep || !first) {
  372. ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
  373. advance_hpp(hpp, ret);
  374. } else
  375. first = false;
  376. if (perf_hpp__use_color() && fmt->color)
  377. ret = fmt->color(fmt, hpp, he);
  378. else
  379. ret = fmt->entry(fmt, hpp, he);
  380. ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret);
  381. advance_hpp(hpp, ret);
  382. }
  383. if (!sep)
  384. ret = scnprintf(hpp->buf, hpp->size, "%*s",
  385. (hists->nr_hpp_node - 2) * HIERARCHY_INDENT, "");
  386. advance_hpp(hpp, ret);
  387. printed += fprintf(fp, "%s", buf);
  388. perf_hpp_list__for_each_format(he->hpp_list, fmt) {
  389. hpp->buf = buf;
  390. hpp->size = size;
  391. /*
  392. * No need to call hist_entry__snprintf_alignment() since this
  393. * fmt is always the last column in the hierarchy mode.
  394. */
  395. if (perf_hpp__use_color() && fmt->color)
  396. fmt->color(fmt, hpp, he);
  397. else
  398. fmt->entry(fmt, hpp, he);
  399. /*
  400. * dynamic entries are right-aligned but we want left-aligned
  401. * in the hierarchy mode
  402. */
  403. printed += fprintf(fp, "%s%s", sep ?: " ", ltrim(buf));
  404. }
  405. printed += putc('\n', fp);
  406. if (symbol_conf.use_callchain && he->leaf) {
  407. u64 total = hists__total_period(hists);
  408. printed += hist_entry_callchain__fprintf(he, total, 0, fp);
  409. goto out;
  410. }
  411. out:
  412. return printed;
  413. }
  414. static int hist_entry__fprintf(struct hist_entry *he, size_t size,
  415. char *bf, size_t bfsz, FILE *fp,
  416. bool use_callchain)
  417. {
  418. int ret;
  419. struct perf_hpp hpp = {
  420. .buf = bf,
  421. .size = size,
  422. };
  423. struct hists *hists = he->hists;
  424. u64 total_period = hists->stats.total_period;
  425. if (size == 0 || size > bfsz)
  426. size = hpp.size = bfsz;
  427. if (symbol_conf.report_hierarchy)
  428. return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp);
  429. hist_entry__snprintf(he, &hpp);
  430. ret = fprintf(fp, "%s\n", bf);
  431. if (use_callchain)
  432. ret += hist_entry_callchain__fprintf(he, total_period, 0, fp);
  433. return ret;
  434. }
  435. static int print_hierarchy_indent(const char *sep, int indent,
  436. const char *line, FILE *fp)
  437. {
  438. if (sep != NULL || indent < 2)
  439. return 0;
  440. return fprintf(fp, "%-.*s", (indent - 2) * HIERARCHY_INDENT, line);
  441. }
  442. static int hists__fprintf_hierarchy_headers(struct hists *hists,
  443. struct perf_hpp *hpp, FILE *fp)
  444. {
  445. bool first_node, first_col;
  446. int indent;
  447. int depth;
  448. unsigned width = 0;
  449. unsigned header_width = 0;
  450. struct perf_hpp_fmt *fmt;
  451. struct perf_hpp_list_node *fmt_node;
  452. const char *sep = symbol_conf.field_sep;
  453. indent = hists->nr_hpp_node;
  454. /* preserve max indent depth for column headers */
  455. print_hierarchy_indent(sep, indent, spaces, fp);
  456. /* the first hpp_list_node is for overhead columns */
  457. fmt_node = list_first_entry(&hists->hpp_formats,
  458. struct perf_hpp_list_node, list);
  459. perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
  460. fmt->header(fmt, hpp, hists, 0, NULL);
  461. fprintf(fp, "%s%s", hpp->buf, sep ?: " ");
  462. }
  463. /* combine sort headers with ' / ' */
  464. first_node = true;
  465. list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
  466. if (!first_node)
  467. header_width += fprintf(fp, " / ");
  468. first_node = false;
  469. first_col = true;
  470. perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
  471. if (perf_hpp__should_skip(fmt, hists))
  472. continue;
  473. if (!first_col)
  474. header_width += fprintf(fp, "+");
  475. first_col = false;
  476. fmt->header(fmt, hpp, hists, 0, NULL);
  477. header_width += fprintf(fp, "%s", trim(hpp->buf));
  478. }
  479. }
  480. fprintf(fp, "\n# ");
  481. /* preserve max indent depth for initial dots */
  482. print_hierarchy_indent(sep, indent, dots, fp);
  483. /* the first hpp_list_node is for overhead columns */
  484. fmt_node = list_first_entry(&hists->hpp_formats,
  485. struct perf_hpp_list_node, list);
  486. first_col = true;
  487. perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
  488. if (!first_col)
  489. fprintf(fp, "%s", sep ?: "..");
  490. first_col = false;
  491. width = fmt->width(fmt, hpp, hists);
  492. fprintf(fp, "%.*s", width, dots);
  493. }
  494. depth = 0;
  495. list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
  496. first_col = true;
  497. width = depth * HIERARCHY_INDENT;
  498. perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
  499. if (perf_hpp__should_skip(fmt, hists))
  500. continue;
  501. if (!first_col)
  502. width++; /* for '+' sign between column header */
  503. first_col = false;
  504. width += fmt->width(fmt, hpp, hists);
  505. }
  506. if (width > header_width)
  507. header_width = width;
  508. depth++;
  509. }
  510. fprintf(fp, "%s%-.*s", sep ?: " ", header_width, dots);
  511. fprintf(fp, "\n#\n");
  512. return 2;
  513. }
  514. static void fprintf_line(struct hists *hists, struct perf_hpp *hpp,
  515. int line, FILE *fp)
  516. {
  517. struct perf_hpp_fmt *fmt;
  518. const char *sep = symbol_conf.field_sep;
  519. bool first = true;
  520. int span = 0;
  521. hists__for_each_format(hists, fmt) {
  522. if (perf_hpp__should_skip(fmt, hists))
  523. continue;
  524. if (!first && !span)
  525. fprintf(fp, "%s", sep ?: " ");
  526. else
  527. first = false;
  528. fmt->header(fmt, hpp, hists, line, &span);
  529. if (!span)
  530. fprintf(fp, "%s", hpp->buf);
  531. }
  532. }
  533. static int
  534. hists__fprintf_standard_headers(struct hists *hists,
  535. struct perf_hpp *hpp,
  536. FILE *fp)
  537. {
  538. struct perf_hpp_list *hpp_list = hists->hpp_list;
  539. struct perf_hpp_fmt *fmt;
  540. unsigned int width;
  541. const char *sep = symbol_conf.field_sep;
  542. bool first = true;
  543. int line;
  544. for (line = 0; line < hpp_list->nr_header_lines; line++) {
  545. /* first # is displayed one level up */
  546. if (line)
  547. fprintf(fp, "# ");
  548. fprintf_line(hists, hpp, line, fp);
  549. fprintf(fp, "\n");
  550. }
  551. if (sep)
  552. return hpp_list->nr_header_lines;
  553. first = true;
  554. fprintf(fp, "# ");
  555. hists__for_each_format(hists, fmt) {
  556. unsigned int i;
  557. if (perf_hpp__should_skip(fmt, hists))
  558. continue;
  559. if (!first)
  560. fprintf(fp, "%s", sep ?: " ");
  561. else
  562. first = false;
  563. width = fmt->width(fmt, hpp, hists);
  564. for (i = 0; i < width; i++)
  565. fprintf(fp, ".");
  566. }
  567. fprintf(fp, "\n");
  568. fprintf(fp, "#\n");
  569. return hpp_list->nr_header_lines + 2;
  570. }
  571. int hists__fprintf_headers(struct hists *hists, FILE *fp)
  572. {
  573. char bf[1024];
  574. struct perf_hpp dummy_hpp = {
  575. .buf = bf,
  576. .size = sizeof(bf),
  577. };
  578. fprintf(fp, "# ");
  579. if (symbol_conf.report_hierarchy)
  580. return hists__fprintf_hierarchy_headers(hists, &dummy_hpp, fp);
  581. else
  582. return hists__fprintf_standard_headers(hists, &dummy_hpp, fp);
  583. }
  584. size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
  585. int max_cols, float min_pcnt, FILE *fp,
  586. bool use_callchain)
  587. {
  588. struct rb_node *nd;
  589. size_t ret = 0;
  590. const char *sep = symbol_conf.field_sep;
  591. int nr_rows = 0;
  592. size_t linesz;
  593. char *line = NULL;
  594. unsigned indent;
  595. init_rem_hits();
  596. hists__reset_column_width(hists);
  597. if (symbol_conf.col_width_list_str)
  598. perf_hpp__set_user_width(symbol_conf.col_width_list_str);
  599. if (show_header)
  600. nr_rows += hists__fprintf_headers(hists, fp);
  601. if (max_rows && nr_rows >= max_rows)
  602. goto out;
  603. linesz = hists__sort_list_width(hists) + 3 + 1;
  604. linesz += perf_hpp__color_overhead();
  605. line = malloc(linesz);
  606. if (line == NULL) {
  607. ret = -1;
  608. goto out;
  609. }
  610. indent = hists__overhead_width(hists) + 4;
  611. for (nd = rb_first(&hists->entries); nd; nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) {
  612. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  613. float percent;
  614. if (h->filtered)
  615. continue;
  616. percent = hist_entry__get_percent_limit(h);
  617. if (percent < min_pcnt)
  618. continue;
  619. ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, use_callchain);
  620. if (max_rows && ++nr_rows >= max_rows)
  621. break;
  622. /*
  623. * If all children are filtered out or percent-limited,
  624. * display "no entry >= x.xx%" message.
  625. */
  626. if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) {
  627. int depth = hists->nr_hpp_node + h->depth + 1;
  628. print_hierarchy_indent(sep, depth, spaces, fp);
  629. fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt);
  630. if (max_rows && ++nr_rows >= max_rows)
  631. break;
  632. }
  633. if (h->ms.map == NULL && verbose > 1) {
  634. __map_groups__fprintf_maps(h->thread->mg,
  635. MAP__FUNCTION, fp);
  636. fprintf(fp, "%.10s end\n", graph_dotted_line);
  637. }
  638. }
  639. free(line);
  640. out:
  641. zfree(&rem_sq_bracket);
  642. return ret;
  643. }
  644. size_t events_stats__fprintf(struct events_stats *stats, FILE *fp)
  645. {
  646. int i;
  647. size_t ret = 0;
  648. for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
  649. const char *name;
  650. if (stats->nr_events[i] == 0)
  651. continue;
  652. name = perf_event__name(i);
  653. if (!strcmp(name, "UNKNOWN"))
  654. continue;
  655. ret += fprintf(fp, "%16s events: %10d\n", name,
  656. stats->nr_events[i]);
  657. }
  658. return ret;
  659. }