annotate-data.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <inttypes.h>
  3. #include <string.h>
  4. #include <linux/zalloc.h>
  5. #include <sys/ttydefaults.h>
  6. #include "ui/browser.h"
  7. #include "ui/helpline.h"
  8. #include "ui/keysyms.h"
  9. #include "ui/ui.h"
  10. #include "util/annotate.h"
  11. #include "util/annotate-data.h"
  12. #include "util/evsel.h"
  13. #include "util/evlist.h"
  14. #include "util/sort.h"
  15. #define FOLDED_SIGN '+'
  16. #define UNFOLD_SIGN '-'
  17. #define NOCHLD_SIGN ' '
  18. struct browser_entry {
  19. struct list_head node;
  20. struct annotated_member *data;
  21. struct type_hist_entry *hists;
  22. struct browser_entry *parent;
  23. struct list_head children;
  24. int indent; /*indentation level, starts from 0 */
  25. int nr_entries; /* # of visible entries: self + descendents */
  26. bool folded; /* only can be false when it has children */
  27. };
  28. struct annotated_data_browser {
  29. struct ui_browser b;
  30. struct list_head entries;
  31. struct browser_entry *curr;
  32. int nr_events;
  33. };
  34. static struct annotated_data_browser *get_browser(struct ui_browser *uib)
  35. {
  36. return container_of(uib, struct annotated_data_browser, b);
  37. }
  38. static void update_hist_entry(struct type_hist_entry *dst,
  39. struct type_hist_entry *src)
  40. {
  41. dst->nr_samples += src->nr_samples;
  42. dst->period += src->period;
  43. }
  44. static int get_member_overhead(struct annotated_data_type *adt,
  45. struct browser_entry *entry,
  46. struct evsel *leader)
  47. {
  48. struct annotated_member *member = entry->data;
  49. int i, k;
  50. for (i = 0; i < member->size; i++) {
  51. struct type_hist *h;
  52. struct evsel *evsel;
  53. int offset = member->offset + i;
  54. k = 0;
  55. for_each_group_evsel(evsel, leader) {
  56. if (symbol_conf.skip_empty &&
  57. evsel__hists(evsel)->stats.nr_samples == 0)
  58. continue;
  59. h = adt->histograms[evsel->core.idx];
  60. update_hist_entry(&entry->hists[k++], &h->addr[offset]);
  61. }
  62. }
  63. return 0;
  64. }
  65. static int add_child_entries(struct annotated_data_browser *browser,
  66. struct browser_entry *parent,
  67. struct annotated_data_type *adt,
  68. struct annotated_member *member,
  69. struct evsel *evsel, int indent)
  70. {
  71. struct annotated_member *pos;
  72. struct browser_entry *entry;
  73. struct list_head *parent_list;
  74. entry = zalloc(sizeof(*entry));
  75. if (entry == NULL)
  76. return -1;
  77. entry->hists = calloc(browser->nr_events, sizeof(*entry->hists));
  78. if (entry->hists == NULL) {
  79. free(entry);
  80. return -1;
  81. }
  82. entry->data = member;
  83. entry->parent = parent;
  84. entry->indent = indent;
  85. if (get_member_overhead(adt, entry, evsel) < 0) {
  86. free(entry);
  87. return -1;
  88. }
  89. INIT_LIST_HEAD(&entry->children);
  90. if (parent)
  91. parent_list = &parent->children;
  92. else
  93. parent_list = &browser->entries;
  94. list_add_tail(&entry->node, parent_list);
  95. list_for_each_entry(pos, &member->children, node) {
  96. int nr = add_child_entries(browser, entry, adt, pos, evsel,
  97. indent + 1);
  98. if (nr < 0)
  99. return nr;
  100. }
  101. /* add an entry for the closing bracket ("}") */
  102. if (!list_empty(&member->children)) {
  103. struct browser_entry *bracket;
  104. bracket = zalloc(sizeof(*bracket));
  105. if (bracket == NULL)
  106. return -1;
  107. bracket->indent = indent;
  108. bracket->parent = entry;
  109. bracket->folded = true;
  110. bracket->nr_entries = 1;
  111. INIT_LIST_HEAD(&bracket->children);
  112. list_add_tail(&bracket->node, &entry->children);
  113. }
  114. /* fold child entries by default */
  115. entry->folded = true;
  116. entry->nr_entries = 1;
  117. return 0;
  118. }
  119. static u32 count_visible_entries(struct annotated_data_browser *browser)
  120. {
  121. int nr = 0;
  122. struct browser_entry *entry;
  123. list_for_each_entry(entry, &browser->entries, node)
  124. nr += entry->nr_entries;
  125. return nr;
  126. }
  127. static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser)
  128. {
  129. struct hist_entry *he = browser->b.priv;
  130. struct annotated_data_type *adt = he->mem_type;
  131. struct evsel *evsel = hists_to_evsel(he->hists);
  132. INIT_LIST_HEAD(&browser->entries);
  133. add_child_entries(browser, /*parent=*/NULL, adt, &adt->self, evsel,
  134. /*indent=*/0);
  135. browser->b.entries = &browser->entries;
  136. browser->b.nr_entries = count_visible_entries(browser);
  137. return 0;
  138. }
  139. static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser)
  140. {
  141. struct browser_entry *pos, *tmp;
  142. list_for_each_entry_safe(pos, tmp, &browser->entries, node) {
  143. list_del_init(&pos->node);
  144. zfree(&pos->hists);
  145. free(pos);
  146. }
  147. }
  148. static struct browser_entry *get_first_child(struct browser_entry *entry)
  149. {
  150. if (list_empty(&entry->children))
  151. return NULL;
  152. return list_first_entry(&entry->children, struct browser_entry, node);
  153. }
  154. static struct browser_entry *get_last_child(struct browser_entry *entry)
  155. {
  156. if (list_empty(&entry->children))
  157. return NULL;
  158. return list_last_entry(&entry->children, struct browser_entry, node);
  159. }
  160. static bool is_first_child(struct browser_entry *entry)
  161. {
  162. /* This will be checked in a different way */
  163. if (entry->parent == NULL)
  164. return false;
  165. return get_first_child(entry->parent) == entry;
  166. }
  167. static bool is_last_child(struct browser_entry *entry)
  168. {
  169. /* This will be checked in a different way */
  170. if (entry->parent == NULL)
  171. return false;
  172. return get_last_child(entry->parent) == entry;
  173. }
  174. static struct browser_entry *browser__prev_entry(struct ui_browser *uib,
  175. struct browser_entry *entry)
  176. {
  177. struct annotated_data_browser *browser = get_browser(uib);
  178. struct browser_entry *first;
  179. first = list_first_entry(&browser->entries, struct browser_entry, node);
  180. while (entry != first) {
  181. if (is_first_child(entry))
  182. entry = entry->parent;
  183. else {
  184. entry = list_prev_entry(entry, node);
  185. while (!entry->folded)
  186. entry = get_last_child(entry);
  187. }
  188. if (!uib->filter || !uib->filter(uib, &entry->node))
  189. return entry;
  190. }
  191. return first;
  192. }
  193. static struct browser_entry *browser__next_entry(struct ui_browser *uib,
  194. struct browser_entry *entry)
  195. {
  196. struct annotated_data_browser *browser = get_browser(uib);
  197. struct browser_entry *last;
  198. last = list_last_entry(&browser->entries, struct browser_entry, node);
  199. while (!last->folded)
  200. last = get_last_child(last);
  201. while (entry != last) {
  202. if (!entry->folded)
  203. entry = get_first_child(entry);
  204. else {
  205. while (is_last_child(entry))
  206. entry = entry->parent;
  207. entry = list_next_entry(entry, node);
  208. }
  209. if (!uib->filter || !uib->filter(uib, &entry->node))
  210. return entry;
  211. }
  212. return last;
  213. }
  214. static void browser__seek(struct ui_browser *uib, off_t offset, int whence)
  215. {
  216. struct annotated_data_browser *browser = get_browser(uib);
  217. struct browser_entry *entry;
  218. if (uib->nr_entries == 0)
  219. return;
  220. switch (whence) {
  221. case SEEK_SET:
  222. entry = list_first_entry(&browser->entries, typeof(*entry), node);
  223. if (uib->filter && uib->filter(uib, &entry->node))
  224. entry = browser__next_entry(uib, entry);
  225. break;
  226. case SEEK_CUR:
  227. entry = list_entry(uib->top, typeof(*entry), node);
  228. break;
  229. case SEEK_END:
  230. entry = list_last_entry(&browser->entries, typeof(*entry), node);
  231. while (!entry->folded)
  232. entry = get_last_child(entry);
  233. if (uib->filter && uib->filter(uib, &entry->node))
  234. entry = browser__prev_entry(uib, entry);
  235. break;
  236. default:
  237. return;
  238. }
  239. assert(entry != NULL);
  240. if (offset > 0) {
  241. while (offset-- != 0)
  242. entry = browser__next_entry(uib, entry);
  243. } else {
  244. while (offset++ != 0)
  245. entry = browser__prev_entry(uib, entry);
  246. }
  247. uib->top = &entry->node;
  248. }
  249. static unsigned int browser__refresh(struct ui_browser *uib)
  250. {
  251. struct annotated_data_browser *browser = get_browser(uib);
  252. struct browser_entry *entry, *next;
  253. int row = 0;
  254. if (uib->top == NULL || uib->top == uib->entries)
  255. browser__seek(uib, SEEK_SET, 0);
  256. entry = list_entry(uib->top, typeof(*entry), node);
  257. while (true) {
  258. if (!uib->filter || !uib->filter(uib, &entry->node)) {
  259. ui_browser__gotorc(uib, row, 0);
  260. uib->write(uib, entry, row);
  261. if (uib->top_idx + row == uib->index)
  262. browser->curr = entry;
  263. if (++row == uib->rows)
  264. break;
  265. }
  266. next = browser__next_entry(uib, entry);
  267. if (next == entry)
  268. break;
  269. entry = next;
  270. }
  271. return row;
  272. }
  273. static int browser__show(struct ui_browser *uib)
  274. {
  275. struct hist_entry *he = uib->priv;
  276. struct annotated_data_type *adt = he->mem_type;
  277. struct annotated_data_browser *browser = get_browser(uib);
  278. const char *help = "Press 'h' for help on key bindings";
  279. char title[256];
  280. snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)",
  281. adt->self.type_name, he->stat.nr_events);
  282. if (ui_browser__show(uib, title, help) < 0)
  283. return -1;
  284. /* second line header */
  285. ui_browser__gotorc_title(uib, 0, 0);
  286. ui_browser__set_color(uib, HE_COLORSET_ROOT);
  287. if (symbol_conf.show_total_period)
  288. strcpy(title, "Period");
  289. else if (symbol_conf.show_nr_samples)
  290. strcpy(title, "Samples");
  291. else
  292. strcpy(title, "Percent");
  293. ui_browser__printf(uib, "%*s %10s %10s %10s %s",
  294. 2 + 11 * (browser->nr_events - 1), "",
  295. title, "Offset", "Size", "Field");
  296. ui_browser__write_nstring(uib, "", uib->width);
  297. return 0;
  298. }
  299. static void browser__write_overhead(struct ui_browser *uib,
  300. struct type_hist *total,
  301. struct type_hist_entry *hist, int row)
  302. {
  303. u64 period = hist->period;
  304. double percent = total->period ? (100.0 * period / total->period) : 0;
  305. bool current = ui_browser__is_current_entry(uib, row);
  306. int nr_samples = 0;
  307. ui_browser__set_percent_color(uib, percent, current);
  308. if (symbol_conf.show_total_period)
  309. ui_browser__printf(uib, " %10" PRIu64, period);
  310. else if (symbol_conf.show_nr_samples)
  311. ui_browser__printf(uib, " %10d", nr_samples);
  312. else
  313. ui_browser__printf(uib, " %10.2f", percent);
  314. ui_browser__set_percent_color(uib, 0, current);
  315. }
  316. static void browser__write(struct ui_browser *uib, void *entry, int row)
  317. {
  318. struct annotated_data_browser *browser = get_browser(uib);
  319. struct browser_entry *be = entry;
  320. struct annotated_member *member = be->data;
  321. struct hist_entry *he = uib->priv;
  322. struct annotated_data_type *adt = he->mem_type;
  323. struct evsel *leader = hists_to_evsel(he->hists);
  324. struct evsel *evsel;
  325. int idx = 0;
  326. bool current = ui_browser__is_current_entry(uib, row);
  327. if (member == NULL) {
  328. /* print the closing bracket */
  329. ui_browser__set_percent_color(uib, 0, current);
  330. ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
  331. ui_browser__write_nstring(uib, "", 11 * browser->nr_events);
  332. ui_browser__printf(uib, " %10s %10s %*s};",
  333. "", "", be->indent * 4, "");
  334. ui_browser__write_nstring(uib, "", uib->width);
  335. return;
  336. }
  337. ui_browser__set_percent_color(uib, 0, current);
  338. if (!list_empty(&be->children))
  339. ui_browser__printf(uib, "%c ", be->folded ? FOLDED_SIGN : UNFOLD_SIGN);
  340. else
  341. ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
  342. /* print the number */
  343. for_each_group_evsel(evsel, leader) {
  344. struct type_hist *h = adt->histograms[evsel->core.idx];
  345. if (symbol_conf.skip_empty &&
  346. evsel__hists(evsel)->stats.nr_samples == 0)
  347. continue;
  348. browser__write_overhead(uib, h, &be->hists[idx++], row);
  349. }
  350. /* print type info */
  351. if (be->indent == 0 && !member->var_name) {
  352. ui_browser__printf(uib, " %#10x %#10x %s%s",
  353. member->offset, member->size,
  354. member->type_name,
  355. list_empty(&member->children) || be->folded? ";" : " {");
  356. } else {
  357. ui_browser__printf(uib, " %#10x %#10x %*s%s\t%s%s",
  358. member->offset, member->size,
  359. be->indent * 4, "", member->type_name,
  360. member->var_name ?: "",
  361. list_empty(&member->children) || be->folded ? ";" : " {");
  362. }
  363. /* fill the rest */
  364. ui_browser__write_nstring(uib, "", uib->width);
  365. }
  366. static void annotated_data_browser__fold(struct annotated_data_browser *browser,
  367. struct browser_entry *entry,
  368. bool recursive)
  369. {
  370. struct browser_entry *child;
  371. if (list_empty(&entry->children))
  372. return;
  373. if (entry->folded && !recursive)
  374. return;
  375. if (recursive) {
  376. list_for_each_entry(child, &entry->children, node)
  377. annotated_data_browser__fold(browser, child, true);
  378. }
  379. entry->nr_entries = 1;
  380. entry->folded = true;
  381. }
  382. static void annotated_data_browser__unfold(struct annotated_data_browser *browser,
  383. struct browser_entry *entry,
  384. bool recursive)
  385. {
  386. struct browser_entry *child;
  387. int nr_entries;
  388. if (list_empty(&entry->children))
  389. return;
  390. if (!entry->folded && !recursive)
  391. return;
  392. nr_entries = 1; /* for self */
  393. list_for_each_entry(child, &entry->children, node) {
  394. if (recursive)
  395. annotated_data_browser__unfold(browser, child, true);
  396. nr_entries += child->nr_entries;
  397. }
  398. entry->nr_entries = nr_entries;
  399. entry->folded = false;
  400. }
  401. static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser,
  402. bool recursive)
  403. {
  404. struct browser_entry *curr = browser->curr;
  405. struct browser_entry *parent;
  406. parent = curr->parent;
  407. while (parent) {
  408. parent->nr_entries -= curr->nr_entries;
  409. parent = parent->parent;
  410. }
  411. browser->b.nr_entries -= curr->nr_entries;
  412. if (curr->folded)
  413. annotated_data_browser__unfold(browser, curr, recursive);
  414. else
  415. annotated_data_browser__fold(browser, curr, recursive);
  416. parent = curr->parent;
  417. while (parent) {
  418. parent->nr_entries += curr->nr_entries;
  419. parent = parent->parent;
  420. }
  421. browser->b.nr_entries += curr->nr_entries;
  422. assert(browser->b.nr_entries == count_visible_entries(browser));
  423. }
  424. static int annotated_data_browser__run(struct annotated_data_browser *browser,
  425. struct evsel *evsel __maybe_unused,
  426. struct hist_browser_timer *hbt)
  427. {
  428. int delay_secs = hbt ? hbt->refresh : 0;
  429. int key;
  430. if (browser__show(&browser->b) < 0)
  431. return -1;
  432. while (1) {
  433. key = ui_browser__run(&browser->b, delay_secs);
  434. switch (key) {
  435. case K_TIMER:
  436. if (hbt)
  437. hbt->timer(hbt->arg);
  438. continue;
  439. case K_F1:
  440. case 'h':
  441. ui_browser__help_window(&browser->b,
  442. "UP/DOWN/PGUP\n"
  443. "PGDN/SPACE Navigate\n"
  444. "</> Move to prev/next symbol\n"
  445. "e Expand/Collapse current entry\n"
  446. "E Expand/Collapse all children of the current\n"
  447. "q/ESC/CTRL+C Exit\n\n");
  448. continue;
  449. case 'e':
  450. annotated_data_browser__toggle_fold(browser,
  451. /*recursive=*/false);
  452. break;
  453. case 'E':
  454. annotated_data_browser__toggle_fold(browser,
  455. /*recursive=*/true);
  456. break;
  457. case K_LEFT:
  458. case '<':
  459. case '>':
  460. case K_ESC:
  461. case 'q':
  462. case CTRL('c'):
  463. goto out;
  464. default:
  465. continue;
  466. }
  467. }
  468. out:
  469. ui_browser__hide(&browser->b);
  470. return key;
  471. }
  472. int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel,
  473. struct hist_browser_timer *hbt)
  474. {
  475. struct annotated_data_browser browser = {
  476. .b = {
  477. .refresh = browser__refresh,
  478. .seek = browser__seek,
  479. .write = browser__write,
  480. .priv = he,
  481. .extra_title_lines = 1,
  482. },
  483. .nr_events = 1,
  484. };
  485. int ret;
  486. ui_helpline__push("Press ESC to exit");
  487. if (evsel__is_group_event(evsel)) {
  488. struct evsel *pos;
  489. int nr = 0;
  490. for_each_group_evsel(pos, evsel) {
  491. if (!symbol_conf.skip_empty ||
  492. evsel__hists(pos)->stats.nr_samples)
  493. nr++;
  494. }
  495. browser.nr_events = nr;
  496. }
  497. ret = annotated_data_browser__collect_entries(&browser);
  498. if (ret < 0)
  499. goto out;
  500. /* To get the top and current entry */
  501. browser__refresh(&browser.b);
  502. /* Show the first-level child entries by default */
  503. annotated_data_browser__toggle_fold(&browser, /*recursive=*/false);
  504. ret = annotated_data_browser__run(&browser, evsel, hbt);
  505. out:
  506. annotated_data_browser__delete_entries(&browser);
  507. return ret;
  508. }