block-info.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <linux/zalloc.h>
  5. #include "block-info.h"
  6. #include "sort.h"
  7. #include "annotate.h"
  8. #include "symbol.h"
  9. #include "dso.h"
  10. #include "map.h"
  11. #include "srcline.h"
  12. #include "evlist.h"
  13. #include "hist.h"
  14. #include "ui/browsers/hists.h"
  15. static struct block_header_column {
  16. const char *name;
  17. int width;
  18. } block_columns[PERF_HPP_REPORT__BLOCK_MAX_INDEX] = {
  19. [PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT] = {
  20. .name = "Sampled Cycles%",
  21. .width = 15,
  22. },
  23. [PERF_HPP_REPORT__BLOCK_LBR_CYCLES] = {
  24. .name = "Sampled Cycles",
  25. .width = 14,
  26. },
  27. [PERF_HPP_REPORT__BLOCK_CYCLES_PCT] = {
  28. .name = "Avg Cycles%",
  29. .width = 11,
  30. },
  31. [PERF_HPP_REPORT__BLOCK_AVG_CYCLES] = {
  32. .name = "Avg Cycles",
  33. .width = 10,
  34. },
  35. [PERF_HPP_REPORT__BLOCK_RANGE] = {
  36. .name = "[Program Block Range]",
  37. .width = 70,
  38. },
  39. [PERF_HPP_REPORT__BLOCK_DSO] = {
  40. .name = "Shared Object",
  41. .width = 20,
  42. }
  43. };
  44. struct block_info *block_info__get(struct block_info *bi)
  45. {
  46. if (bi)
  47. refcount_inc(&bi->refcnt);
  48. return bi;
  49. }
  50. void block_info__put(struct block_info *bi)
  51. {
  52. if (bi && refcount_dec_and_test(&bi->refcnt))
  53. free(bi);
  54. }
  55. struct block_info *block_info__new(void)
  56. {
  57. struct block_info *bi = zalloc(sizeof(*bi));
  58. if (bi)
  59. refcount_set(&bi->refcnt, 1);
  60. return bi;
  61. }
  62. int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right)
  63. {
  64. struct block_info *bi_l = left->block_info;
  65. struct block_info *bi_r = right->block_info;
  66. int cmp;
  67. if (!bi_l->sym || !bi_r->sym) {
  68. if (!bi_l->sym && !bi_r->sym)
  69. return -1;
  70. else if (!bi_l->sym)
  71. return -1;
  72. else
  73. return 1;
  74. }
  75. cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
  76. if (cmp)
  77. return cmp;
  78. if (bi_l->start != bi_r->start)
  79. return (int64_t)(bi_r->start - bi_l->start);
  80. return (int64_t)(bi_r->end - bi_l->end);
  81. }
  82. int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
  83. struct hist_entry *left, struct hist_entry *right)
  84. {
  85. return __block_info__cmp(left, right);
  86. }
  87. static void init_block_info(struct block_info *bi, struct symbol *sym,
  88. struct cyc_hist *ch, int offset,
  89. u64 total_cycles)
  90. {
  91. bi->sym = sym;
  92. bi->start = ch->start;
  93. bi->end = offset;
  94. bi->cycles = ch->cycles;
  95. bi->cycles_aggr = ch->cycles_aggr;
  96. bi->num = ch->num;
  97. bi->num_aggr = ch->num_aggr;
  98. bi->total_cycles = total_cycles;
  99. memcpy(bi->cycles_spark, ch->cycles_spark,
  100. NUM_SPARKS * sizeof(u64));
  101. }
  102. int block_info__process_sym(struct hist_entry *he, struct block_hist *bh,
  103. u64 *block_cycles_aggr, u64 total_cycles)
  104. {
  105. struct annotation *notes;
  106. struct cyc_hist *ch;
  107. static struct addr_location al;
  108. u64 cycles = 0;
  109. if (!he->ms.map || !he->ms.sym)
  110. return 0;
  111. memset(&al, 0, sizeof(al));
  112. al.map = he->ms.map;
  113. al.sym = he->ms.sym;
  114. notes = symbol__annotation(he->ms.sym);
  115. if (!notes || !notes->src || !notes->src->cycles_hist)
  116. return 0;
  117. ch = notes->src->cycles_hist;
  118. for (unsigned int i = 0; i < symbol__size(he->ms.sym); i++) {
  119. if (ch[i].num_aggr) {
  120. struct block_info *bi;
  121. struct hist_entry *he_block;
  122. bi = block_info__new();
  123. if (!bi)
  124. return -1;
  125. init_block_info(bi, he->ms.sym, &ch[i], i,
  126. total_cycles);
  127. cycles += bi->cycles_aggr / bi->num_aggr;
  128. he_block = hists__add_entry_block(&bh->block_hists,
  129. &al, bi);
  130. if (!he_block) {
  131. block_info__put(bi);
  132. return -1;
  133. }
  134. }
  135. }
  136. if (block_cycles_aggr)
  137. *block_cycles_aggr += cycles;
  138. return 0;
  139. }
  140. static int block_column_header(struct perf_hpp_fmt *fmt,
  141. struct perf_hpp *hpp,
  142. struct hists *hists __maybe_unused,
  143. int line __maybe_unused,
  144. int *span __maybe_unused)
  145. {
  146. struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
  147. return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
  148. block_fmt->header);
  149. }
  150. static int block_column_width(struct perf_hpp_fmt *fmt,
  151. struct perf_hpp *hpp __maybe_unused,
  152. struct hists *hists __maybe_unused)
  153. {
  154. struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
  155. return block_fmt->width;
  156. }
  157. static int color_pct(struct perf_hpp *hpp, int width, double pct)
  158. {
  159. #ifdef HAVE_SLANG_SUPPORT
  160. if (use_browser) {
  161. return __hpp__slsmg_color_printf(hpp, "%*.2f%%",
  162. width - 1, pct);
  163. }
  164. #endif
  165. return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, pct);
  166. }
  167. static int block_total_cycles_pct_entry(struct perf_hpp_fmt *fmt,
  168. struct perf_hpp *hpp,
  169. struct hist_entry *he)
  170. {
  171. struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
  172. struct block_info *bi = he->block_info;
  173. double ratio = 0.0;
  174. if (block_fmt->total_cycles)
  175. ratio = (double)bi->cycles_aggr / (double)block_fmt->total_cycles;
  176. return color_pct(hpp, block_fmt->width, 100.0 * ratio);
  177. }
  178. static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt *fmt,
  179. struct hist_entry *left,
  180. struct hist_entry *right)
  181. {
  182. struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
  183. struct block_info *bi_l = left->block_info;
  184. struct block_info *bi_r = right->block_info;
  185. double l, r;
  186. if (block_fmt->total_cycles) {
  187. l = ((double)bi_l->cycles_aggr /
  188. (double)block_fmt->total_cycles) * 100000.0;
  189. r = ((double)bi_r->cycles_aggr /
  190. (double)block_fmt->total_cycles) * 100000.0;
  191. return (int64_t)l - (int64_t)r;
  192. }
  193. return 0;
  194. }
  195. static void cycles_string(u64 cycles, char *buf, int size)
  196. {
  197. if (cycles >= 1000000)
  198. scnprintf(buf, size, "%.1fM", (double)cycles / 1000000.0);
  199. else if (cycles >= 1000)
  200. scnprintf(buf, size, "%.1fK", (double)cycles / 1000.0);
  201. else
  202. scnprintf(buf, size, "%1d", cycles);
  203. }
  204. static int block_cycles_lbr_entry(struct perf_hpp_fmt *fmt,
  205. struct perf_hpp *hpp, struct hist_entry *he)
  206. {
  207. struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
  208. struct block_info *bi = he->block_info;
  209. char cycles_buf[16];
  210. cycles_string(bi->cycles_aggr, cycles_buf, sizeof(cycles_buf));
  211. return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
  212. cycles_buf);
  213. }
  214. static int block_cycles_pct_entry(struct perf_hpp_fmt *fmt,
  215. struct perf_hpp *hpp, struct hist_entry *he)
  216. {
  217. struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
  218. struct block_info *bi = he->block_info;
  219. double ratio = 0.0;
  220. u64 avg;
  221. if (block_fmt->block_cycles && bi->num_aggr) {
  222. avg = bi->cycles_aggr / bi->num_aggr;
  223. ratio = (double)avg / (double)block_fmt->block_cycles;
  224. }
  225. return color_pct(hpp, block_fmt->width, 100.0 * ratio);
  226. }
  227. static int block_avg_cycles_entry(struct perf_hpp_fmt *fmt,
  228. struct perf_hpp *hpp,
  229. struct hist_entry *he)
  230. {
  231. struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
  232. struct block_info *bi = he->block_info;
  233. char cycles_buf[16];
  234. cycles_string(bi->cycles_aggr / bi->num_aggr, cycles_buf,
  235. sizeof(cycles_buf));
  236. return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
  237. cycles_buf);
  238. }
  239. static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
  240. struct hist_entry *he)
  241. {
  242. struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
  243. struct block_info *bi = he->block_info;
  244. char buf[128];
  245. char *start_line, *end_line;
  246. symbol_conf.disable_add2line_warn = true;
  247. start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
  248. he->ms.sym);
  249. end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
  250. he->ms.sym);
  251. if ((strncmp(start_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0) &&
  252. (strncmp(end_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)) {
  253. scnprintf(buf, sizeof(buf), "[%s -> %s]",
  254. start_line, end_line);
  255. } else {
  256. scnprintf(buf, sizeof(buf), "[%7lx -> %7lx]",
  257. bi->start, bi->end);
  258. }
  259. free_srcline(start_line);
  260. free_srcline(end_line);
  261. return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
  262. }
  263. static int block_dso_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
  264. struct hist_entry *he)
  265. {
  266. struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
  267. struct map *map = he->ms.map;
  268. if (map && map->dso) {
  269. return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
  270. map->dso->short_name);
  271. }
  272. return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
  273. "[unknown]");
  274. }
  275. static void init_block_header(struct block_fmt *block_fmt)
  276. {
  277. struct perf_hpp_fmt *fmt = &block_fmt->fmt;
  278. BUG_ON(block_fmt->idx >= PERF_HPP_REPORT__BLOCK_MAX_INDEX);
  279. block_fmt->header = block_columns[block_fmt->idx].name;
  280. block_fmt->width = block_columns[block_fmt->idx].width;
  281. fmt->header = block_column_header;
  282. fmt->width = block_column_width;
  283. }
  284. static void hpp_register(struct block_fmt *block_fmt, int idx,
  285. struct perf_hpp_list *hpp_list)
  286. {
  287. struct perf_hpp_fmt *fmt = &block_fmt->fmt;
  288. block_fmt->idx = idx;
  289. INIT_LIST_HEAD(&fmt->list);
  290. INIT_LIST_HEAD(&fmt->sort_list);
  291. switch (idx) {
  292. case PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT:
  293. fmt->color = block_total_cycles_pct_entry;
  294. fmt->cmp = block_info__cmp;
  295. fmt->sort = block_total_cycles_pct_sort;
  296. break;
  297. case PERF_HPP_REPORT__BLOCK_LBR_CYCLES:
  298. fmt->entry = block_cycles_lbr_entry;
  299. break;
  300. case PERF_HPP_REPORT__BLOCK_CYCLES_PCT:
  301. fmt->color = block_cycles_pct_entry;
  302. break;
  303. case PERF_HPP_REPORT__BLOCK_AVG_CYCLES:
  304. fmt->entry = block_avg_cycles_entry;
  305. break;
  306. case PERF_HPP_REPORT__BLOCK_RANGE:
  307. fmt->entry = block_range_entry;
  308. break;
  309. case PERF_HPP_REPORT__BLOCK_DSO:
  310. fmt->entry = block_dso_entry;
  311. break;
  312. default:
  313. return;
  314. }
  315. init_block_header(block_fmt);
  316. perf_hpp_list__column_register(hpp_list, fmt);
  317. }
  318. static void register_block_columns(struct perf_hpp_list *hpp_list,
  319. struct block_fmt *block_fmts,
  320. int *block_hpps, int nr_hpps)
  321. {
  322. for (int i = 0; i < nr_hpps; i++)
  323. hpp_register(&block_fmts[i], block_hpps[i], hpp_list);
  324. }
  325. static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts,
  326. int *block_hpps, int nr_hpps)
  327. {
  328. __hists__init(&bh->block_hists, &bh->block_list);
  329. perf_hpp_list__init(&bh->block_list);
  330. bh->block_list.nr_header_lines = 1;
  331. register_block_columns(&bh->block_list, block_fmts,
  332. block_hpps, nr_hpps);
  333. /* Sort by the first fmt */
  334. perf_hpp_list__register_sort_field(&bh->block_list, &block_fmts[0].fmt);
  335. }
  336. static int process_block_report(struct hists *hists,
  337. struct block_report *block_report,
  338. u64 total_cycles, int *block_hpps,
  339. int nr_hpps)
  340. {
  341. struct rb_node *next = rb_first_cached(&hists->entries);
  342. struct block_hist *bh = &block_report->hist;
  343. struct hist_entry *he;
  344. if (nr_hpps > PERF_HPP_REPORT__BLOCK_MAX_INDEX)
  345. return -1;
  346. block_report->nr_fmts = nr_hpps;
  347. init_block_hist(bh, block_report->fmts, block_hpps, nr_hpps);
  348. while (next) {
  349. he = rb_entry(next, struct hist_entry, rb_node);
  350. block_info__process_sym(he, bh, &block_report->cycles,
  351. total_cycles);
  352. next = rb_next(&he->rb_node);
  353. }
  354. for (int i = 0; i < nr_hpps; i++) {
  355. block_report->fmts[i].total_cycles = total_cycles;
  356. block_report->fmts[i].block_cycles = block_report->cycles;
  357. }
  358. hists__output_resort(&bh->block_hists, NULL);
  359. return 0;
  360. }
  361. struct block_report *block_info__create_report(struct evlist *evlist,
  362. u64 total_cycles,
  363. int *block_hpps, int nr_hpps,
  364. int *nr_reps)
  365. {
  366. struct block_report *block_reports;
  367. int nr_hists = evlist->core.nr_entries, i = 0;
  368. struct evsel *pos;
  369. block_reports = calloc(nr_hists, sizeof(struct block_report));
  370. if (!block_reports)
  371. return NULL;
  372. evlist__for_each_entry(evlist, pos) {
  373. struct hists *hists = evsel__hists(pos);
  374. process_block_report(hists, &block_reports[i], total_cycles,
  375. block_hpps, nr_hpps);
  376. i++;
  377. }
  378. *nr_reps = nr_hists;
  379. return block_reports;
  380. }
  381. void block_info__free_report(struct block_report *reps, int nr_reps)
  382. {
  383. for (int i = 0; i < nr_reps; i++)
  384. hists__delete_entries(&reps[i].hist.block_hists);
  385. free(reps);
  386. }
  387. int report__browse_block_hists(struct block_hist *bh, float min_percent,
  388. struct evsel *evsel, struct perf_env *env,
  389. struct annotation_options *annotation_opts)
  390. {
  391. int ret;
  392. switch (use_browser) {
  393. case 0:
  394. symbol_conf.report_individual_block = true;
  395. hists__fprintf(&bh->block_hists, true, 0, 0, min_percent,
  396. stdout, true);
  397. return 0;
  398. case 1:
  399. symbol_conf.report_individual_block = true;
  400. ret = block_hists_tui_browse(bh, evsel, min_percent,
  401. env, annotation_opts);
  402. return ret;
  403. default:
  404. return -1;
  405. }
  406. return 0;
  407. }
  408. float block_info__total_cycles_percent(struct hist_entry *he)
  409. {
  410. struct block_info *bi = he->block_info;
  411. if (bi->total_cycles)
  412. return bi->cycles * 100.0 / bi->total_cycles;
  413. return 0.0;
  414. }