hist.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. #include "hist.h"
  2. #include "session.h"
  3. #include "sort.h"
  4. struct callchain_param callchain_param = {
  5. .mode = CHAIN_GRAPH_REL,
  6. .min_percent = 0.5
  7. };
  8. /*
  9. * histogram, sorted on item, collects counts
  10. */
  11. struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self,
  12. struct addr_location *al,
  13. struct symbol *sym_parent,
  14. u64 count, bool *hit)
  15. {
  16. struct rb_node **p = &self->hists.rb_node;
  17. struct rb_node *parent = NULL;
  18. struct hist_entry *he;
  19. struct hist_entry entry = {
  20. .thread = al->thread,
  21. .map = al->map,
  22. .sym = al->sym,
  23. .ip = al->addr,
  24. .level = al->level,
  25. .count = count,
  26. .parent = sym_parent,
  27. };
  28. int cmp;
  29. while (*p != NULL) {
  30. parent = *p;
  31. he = rb_entry(parent, struct hist_entry, rb_node);
  32. cmp = hist_entry__cmp(&entry, he);
  33. if (!cmp) {
  34. *hit = true;
  35. return he;
  36. }
  37. if (cmp < 0)
  38. p = &(*p)->rb_left;
  39. else
  40. p = &(*p)->rb_right;
  41. }
  42. he = malloc(sizeof(*he));
  43. if (!he)
  44. return NULL;
  45. *he = entry;
  46. rb_link_node(&he->rb_node, parent, p);
  47. rb_insert_color(&he->rb_node, &self->hists);
  48. *hit = false;
  49. return he;
  50. }
  51. int64_t
  52. hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
  53. {
  54. struct sort_entry *se;
  55. int64_t cmp = 0;
  56. list_for_each_entry(se, &hist_entry__sort_list, list) {
  57. cmp = se->cmp(left, right);
  58. if (cmp)
  59. break;
  60. }
  61. return cmp;
  62. }
  63. int64_t
  64. hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
  65. {
  66. struct sort_entry *se;
  67. int64_t cmp = 0;
  68. list_for_each_entry(se, &hist_entry__sort_list, list) {
  69. int64_t (*f)(struct hist_entry *, struct hist_entry *);
  70. f = se->collapse ?: se->cmp;
  71. cmp = f(left, right);
  72. if (cmp)
  73. break;
  74. }
  75. return cmp;
  76. }
  77. void hist_entry__free(struct hist_entry *he)
  78. {
  79. free(he);
  80. }
  81. /*
  82. * collapse the histogram
  83. */
  84. static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
  85. {
  86. struct rb_node **p = &root->rb_node;
  87. struct rb_node *parent = NULL;
  88. struct hist_entry *iter;
  89. int64_t cmp;
  90. while (*p != NULL) {
  91. parent = *p;
  92. iter = rb_entry(parent, struct hist_entry, rb_node);
  93. cmp = hist_entry__collapse(iter, he);
  94. if (!cmp) {
  95. iter->count += he->count;
  96. hist_entry__free(he);
  97. return;
  98. }
  99. if (cmp < 0)
  100. p = &(*p)->rb_left;
  101. else
  102. p = &(*p)->rb_right;
  103. }
  104. rb_link_node(&he->rb_node, parent, p);
  105. rb_insert_color(&he->rb_node, root);
  106. }
  107. void perf_session__collapse_resort(struct perf_session *self)
  108. {
  109. struct rb_root tmp;
  110. struct rb_node *next;
  111. struct hist_entry *n;
  112. if (!sort__need_collapse)
  113. return;
  114. tmp = RB_ROOT;
  115. next = rb_first(&self->hists);
  116. while (next) {
  117. n = rb_entry(next, struct hist_entry, rb_node);
  118. next = rb_next(&n->rb_node);
  119. rb_erase(&n->rb_node, &self->hists);
  120. collapse__insert_entry(&tmp, n);
  121. }
  122. self->hists = tmp;
  123. }
  124. /*
  125. * reverse the map, sort on count.
  126. */
  127. static void perf_session__insert_output_hist_entry(struct rb_root *root,
  128. struct hist_entry *he,
  129. u64 min_callchain_hits)
  130. {
  131. struct rb_node **p = &root->rb_node;
  132. struct rb_node *parent = NULL;
  133. struct hist_entry *iter;
  134. if (symbol_conf.use_callchain)
  135. callchain_param.sort(&he->sorted_chain, &he->callchain,
  136. min_callchain_hits, &callchain_param);
  137. while (*p != NULL) {
  138. parent = *p;
  139. iter = rb_entry(parent, struct hist_entry, rb_node);
  140. if (he->count > iter->count)
  141. p = &(*p)->rb_left;
  142. else
  143. p = &(*p)->rb_right;
  144. }
  145. rb_link_node(&he->rb_node, parent, p);
  146. rb_insert_color(&he->rb_node, root);
  147. }
  148. void perf_session__output_resort(struct perf_session *self, u64 total_samples)
  149. {
  150. struct rb_root tmp;
  151. struct rb_node *next;
  152. struct hist_entry *n;
  153. u64 min_callchain_hits;
  154. min_callchain_hits =
  155. total_samples * (callchain_param.min_percent / 100);
  156. tmp = RB_ROOT;
  157. next = rb_first(&self->hists);
  158. while (next) {
  159. n = rb_entry(next, struct hist_entry, rb_node);
  160. next = rb_next(&n->rb_node);
  161. rb_erase(&n->rb_node, &self->hists);
  162. perf_session__insert_output_hist_entry(&tmp, n,
  163. min_callchain_hits);
  164. }
  165. self->hists = tmp;
  166. }
  167. static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
  168. {
  169. int i;
  170. int ret = fprintf(fp, " ");
  171. for (i = 0; i < left_margin; i++)
  172. ret += fprintf(fp, " ");
  173. return ret;
  174. }
  175. static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
  176. int left_margin)
  177. {
  178. int i;
  179. size_t ret = callchain__fprintf_left_margin(fp, left_margin);
  180. for (i = 0; i < depth; i++)
  181. if (depth_mask & (1 << i))
  182. ret += fprintf(fp, "| ");
  183. else
  184. ret += fprintf(fp, " ");
  185. ret += fprintf(fp, "\n");
  186. return ret;
  187. }
  188. static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
  189. int depth, int depth_mask, int count,
  190. u64 total_samples, int hits,
  191. int left_margin)
  192. {
  193. int i;
  194. size_t ret = 0;
  195. ret += callchain__fprintf_left_margin(fp, left_margin);
  196. for (i = 0; i < depth; i++) {
  197. if (depth_mask & (1 << i))
  198. ret += fprintf(fp, "|");
  199. else
  200. ret += fprintf(fp, " ");
  201. if (!count && i == depth - 1) {
  202. double percent;
  203. percent = hits * 100.0 / total_samples;
  204. ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
  205. } else
  206. ret += fprintf(fp, "%s", " ");
  207. }
  208. if (chain->sym)
  209. ret += fprintf(fp, "%s\n", chain->sym->name);
  210. else
  211. ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
  212. return ret;
  213. }
  214. static struct symbol *rem_sq_bracket;
  215. static struct callchain_list rem_hits;
  216. static void init_rem_hits(void)
  217. {
  218. rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
  219. if (!rem_sq_bracket) {
  220. fprintf(stderr, "Not enough memory to display remaining hits\n");
  221. return;
  222. }
  223. strcpy(rem_sq_bracket->name, "[...]");
  224. rem_hits.sym = rem_sq_bracket;
  225. }
  226. static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
  227. u64 total_samples, int depth,
  228. int depth_mask, int left_margin)
  229. {
  230. struct rb_node *node, *next;
  231. struct callchain_node *child;
  232. struct callchain_list *chain;
  233. int new_depth_mask = depth_mask;
  234. u64 new_total;
  235. u64 remaining;
  236. size_t ret = 0;
  237. int i;
  238. if (callchain_param.mode == CHAIN_GRAPH_REL)
  239. new_total = self->children_hit;
  240. else
  241. new_total = total_samples;
  242. remaining = new_total;
  243. node = rb_first(&self->rb_root);
  244. while (node) {
  245. u64 cumul;
  246. child = rb_entry(node, struct callchain_node, rb_node);
  247. cumul = cumul_hits(child);
  248. remaining -= cumul;
  249. /*
  250. * The depth mask manages the output of pipes that show
  251. * the depth. We don't want to keep the pipes of the current
  252. * level for the last child of this depth.
  253. * Except if we have remaining filtered hits. They will
  254. * supersede the last child
  255. */
  256. next = rb_next(node);
  257. if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
  258. new_depth_mask &= ~(1 << (depth - 1));
  259. /*
  260. * But we keep the older depth mask for the line seperator
  261. * to keep the level link until we reach the last child
  262. */
  263. ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
  264. left_margin);
  265. i = 0;
  266. list_for_each_entry(chain, &child->val, list) {
  267. if (chain->ip >= PERF_CONTEXT_MAX)
  268. continue;
  269. ret += ipchain__fprintf_graph(fp, chain, depth,
  270. new_depth_mask, i++,
  271. new_total,
  272. cumul,
  273. left_margin);
  274. }
  275. ret += __callchain__fprintf_graph(fp, child, new_total,
  276. depth + 1,
  277. new_depth_mask | (1 << depth),
  278. left_margin);
  279. node = next;
  280. }
  281. if (callchain_param.mode == CHAIN_GRAPH_REL &&
  282. remaining && remaining != new_total) {
  283. if (!rem_sq_bracket)
  284. return ret;
  285. new_depth_mask &= ~(1 << (depth - 1));
  286. ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
  287. new_depth_mask, 0, new_total,
  288. remaining, left_margin);
  289. }
  290. return ret;
  291. }
  292. static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
  293. u64 total_samples, int left_margin)
  294. {
  295. struct callchain_list *chain;
  296. bool printed = false;
  297. int i = 0;
  298. int ret = 0;
  299. list_for_each_entry(chain, &self->val, list) {
  300. if (chain->ip >= PERF_CONTEXT_MAX)
  301. continue;
  302. if (!i++ && sort__first_dimension == SORT_SYM)
  303. continue;
  304. if (!printed) {
  305. ret += callchain__fprintf_left_margin(fp, left_margin);
  306. ret += fprintf(fp, "|\n");
  307. ret += callchain__fprintf_left_margin(fp, left_margin);
  308. ret += fprintf(fp, "---");
  309. left_margin += 3;
  310. printed = true;
  311. } else
  312. ret += callchain__fprintf_left_margin(fp, left_margin);
  313. if (chain->sym)
  314. ret += fprintf(fp, " %s\n", chain->sym->name);
  315. else
  316. ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
  317. }
  318. ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin);
  319. return ret;
  320. }
  321. static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
  322. u64 total_samples)
  323. {
  324. struct callchain_list *chain;
  325. size_t ret = 0;
  326. if (!self)
  327. return 0;
  328. ret += callchain__fprintf_flat(fp, self->parent, total_samples);
  329. list_for_each_entry(chain, &self->val, list) {
  330. if (chain->ip >= PERF_CONTEXT_MAX)
  331. continue;
  332. if (chain->sym)
  333. ret += fprintf(fp, " %s\n", chain->sym->name);
  334. else
  335. ret += fprintf(fp, " %p\n",
  336. (void *)(long)chain->ip);
  337. }
  338. return ret;
  339. }
  340. static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
  341. u64 total_samples, int left_margin)
  342. {
  343. struct rb_node *rb_node;
  344. struct callchain_node *chain;
  345. size_t ret = 0;
  346. rb_node = rb_first(&self->sorted_chain);
  347. while (rb_node) {
  348. double percent;
  349. chain = rb_entry(rb_node, struct callchain_node, rb_node);
  350. percent = chain->hit * 100.0 / total_samples;
  351. switch (callchain_param.mode) {
  352. case CHAIN_FLAT:
  353. ret += percent_color_fprintf(fp, " %6.2f%%\n",
  354. percent);
  355. ret += callchain__fprintf_flat(fp, chain, total_samples);
  356. break;
  357. case CHAIN_GRAPH_ABS: /* Falldown */
  358. case CHAIN_GRAPH_REL:
  359. ret += callchain__fprintf_graph(fp, chain, total_samples,
  360. left_margin);
  361. case CHAIN_NONE:
  362. default:
  363. break;
  364. }
  365. ret += fprintf(fp, "\n");
  366. rb_node = rb_next(rb_node);
  367. }
  368. return ret;
  369. }
  370. static size_t hist_entry__fprintf(FILE *fp, struct hist_entry *self,
  371. struct perf_session *session)
  372. {
  373. struct sort_entry *se;
  374. size_t ret;
  375. if (symbol_conf.exclude_other && !self->parent)
  376. return 0;
  377. if (session->events_stats.total)
  378. ret = percent_color_fprintf(fp,
  379. symbol_conf.field_sep ? "%.2f" : " %6.2f%%",
  380. (self->count * 100.0) / session->events_stats.total);
  381. else
  382. ret = fprintf(fp, symbol_conf.field_sep ? "%lld" : "%12lld ", self->count);
  383. if (symbol_conf.show_nr_samples) {
  384. if (symbol_conf.field_sep)
  385. fprintf(fp, "%c%lld", *symbol_conf.field_sep, self->count);
  386. else
  387. fprintf(fp, "%11lld", self->count);
  388. }
  389. list_for_each_entry(se, &hist_entry__sort_list, list) {
  390. if (se->elide)
  391. continue;
  392. fprintf(fp, "%s", symbol_conf.field_sep ?: " ");
  393. ret += se->print(fp, self, se->width ? *se->width : 0);
  394. }
  395. ret += fprintf(fp, "\n");
  396. if (symbol_conf.use_callchain) {
  397. int left_margin = 0;
  398. if (sort__first_dimension == SORT_COMM) {
  399. se = list_first_entry(&hist_entry__sort_list, typeof(*se),
  400. list);
  401. left_margin = se->width ? *se->width : 0;
  402. left_margin -= thread__comm_len(self->thread);
  403. }
  404. hist_entry_callchain__fprintf(fp, self, session->events_stats.total,
  405. left_margin);
  406. }
  407. return ret;
  408. }
  409. size_t perf_session__fprintf_hists(struct perf_session *self, FILE *fp)
  410. {
  411. struct hist_entry *pos;
  412. struct sort_entry *se;
  413. struct rb_node *nd;
  414. size_t ret = 0;
  415. unsigned int width;
  416. char *col_width = symbol_conf.col_width_list_str;
  417. init_rem_hits();
  418. fprintf(fp, "# Samples: %ld\n", self->events_stats.total);
  419. fprintf(fp, "#\n");
  420. fprintf(fp, "# Overhead");
  421. if (symbol_conf.show_nr_samples) {
  422. if (symbol_conf.field_sep)
  423. fprintf(fp, "%cSamples", *symbol_conf.field_sep);
  424. else
  425. fputs(" Samples ", fp);
  426. }
  427. list_for_each_entry(se, &hist_entry__sort_list, list) {
  428. if (se->elide)
  429. continue;
  430. if (symbol_conf.field_sep) {
  431. fprintf(fp, "%c%s", *symbol_conf.field_sep, se->header);
  432. continue;
  433. }
  434. width = strlen(se->header);
  435. if (se->width) {
  436. if (symbol_conf.col_width_list_str) {
  437. if (col_width) {
  438. *se->width = atoi(col_width);
  439. col_width = strchr(col_width, ',');
  440. if (col_width)
  441. ++col_width;
  442. }
  443. }
  444. width = *se->width = max(*se->width, width);
  445. }
  446. fprintf(fp, " %*s", width, se->header);
  447. }
  448. fprintf(fp, "\n");
  449. if (symbol_conf.field_sep)
  450. goto print_entries;
  451. fprintf(fp, "# ........");
  452. if (symbol_conf.show_nr_samples)
  453. fprintf(fp, " ..........");
  454. list_for_each_entry(se, &hist_entry__sort_list, list) {
  455. unsigned int i;
  456. if (se->elide)
  457. continue;
  458. fprintf(fp, " ");
  459. if (se->width)
  460. width = *se->width;
  461. else
  462. width = strlen(se->header);
  463. for (i = 0; i < width; i++)
  464. fprintf(fp, ".");
  465. }
  466. fprintf(fp, "\n");
  467. fprintf(fp, "#\n");
  468. print_entries:
  469. for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) {
  470. pos = rb_entry(nd, struct hist_entry, rb_node);
  471. ret += hist_entry__fprintf(fp, pos, self);
  472. }
  473. if (sort_order == default_sort_order &&
  474. parent_pattern == default_parent_pattern) {
  475. fprintf(fp, "#\n");
  476. fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");
  477. fprintf(fp, "#\n");
  478. }
  479. fprintf(fp, "\n");
  480. free(rem_sq_bracket);
  481. return ret;
  482. }