hists.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. #define _GNU_SOURCE
  2. #include <stdio.h>
  3. #undef _GNU_SOURCE
  4. #include "../libslang.h"
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <newt.h>
  8. #include <linux/rbtree.h>
  9. #include "../../hist.h"
  10. #include "../../pstack.h"
  11. #include "../../sort.h"
  12. #include "../../util.h"
  13. #include "../browser.h"
  14. #include "../helpline.h"
  15. #include "../util.h"
  16. #include "map.h"
  17. struct hist_browser {
  18. struct ui_browser b;
  19. struct hists *hists;
  20. struct hist_entry *he_selection;
  21. struct map_symbol *selection;
  22. };
  23. static void hist_browser__refresh_dimensions(struct hist_browser *self)
  24. {
  25. /* 3 == +/- toggle symbol before actual hist_entry rendering */
  26. self->b.width = 3 + (hists__sort_list_width(self->hists) +
  27. sizeof("[k]"));
  28. }
  29. static void hist_browser__reset(struct hist_browser *self)
  30. {
  31. self->b.nr_entries = self->hists->nr_entries;
  32. hist_browser__refresh_dimensions(self);
  33. ui_browser__reset_index(&self->b);
  34. }
  35. static char tree__folded_sign(bool unfolded)
  36. {
  37. return unfolded ? '-' : '+';
  38. }
  39. static char map_symbol__folded(const struct map_symbol *self)
  40. {
  41. return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
  42. }
  43. static char hist_entry__folded(const struct hist_entry *self)
  44. {
  45. return map_symbol__folded(&self->ms);
  46. }
  47. static char callchain_list__folded(const struct callchain_list *self)
  48. {
  49. return map_symbol__folded(&self->ms);
  50. }
  51. static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
  52. {
  53. int n = 0;
  54. struct rb_node *nd;
  55. for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
  56. struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
  57. struct callchain_list *chain;
  58. char folded_sign = ' '; /* No children */
  59. list_for_each_entry(chain, &child->val, list) {
  60. ++n;
  61. /* We need this because we may not have children */
  62. folded_sign = callchain_list__folded(chain);
  63. if (folded_sign == '+')
  64. break;
  65. }
  66. if (folded_sign == '-') /* Have children and they're unfolded */
  67. n += callchain_node__count_rows_rb_tree(child);
  68. }
  69. return n;
  70. }
  71. static int callchain_node__count_rows(struct callchain_node *node)
  72. {
  73. struct callchain_list *chain;
  74. bool unfolded = false;
  75. int n = 0;
  76. list_for_each_entry(chain, &node->val, list) {
  77. ++n;
  78. unfolded = chain->ms.unfolded;
  79. }
  80. if (unfolded)
  81. n += callchain_node__count_rows_rb_tree(node);
  82. return n;
  83. }
  84. static int callchain__count_rows(struct rb_root *chain)
  85. {
  86. struct rb_node *nd;
  87. int n = 0;
  88. for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
  89. struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
  90. n += callchain_node__count_rows(node);
  91. }
  92. return n;
  93. }
  94. static bool map_symbol__toggle_fold(struct map_symbol *self)
  95. {
  96. if (!self->has_children)
  97. return false;
  98. self->unfolded = !self->unfolded;
  99. return true;
  100. }
  101. static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
  102. {
  103. struct rb_node *nd = rb_first(&self->rb_root);
  104. for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
  105. struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
  106. struct callchain_list *chain;
  107. int first = true;
  108. list_for_each_entry(chain, &child->val, list) {
  109. if (first) {
  110. first = false;
  111. chain->ms.has_children = chain->list.next != &child->val ||
  112. rb_first(&child->rb_root) != NULL;
  113. } else
  114. chain->ms.has_children = chain->list.next == &child->val &&
  115. rb_first(&child->rb_root) != NULL;
  116. }
  117. callchain_node__init_have_children_rb_tree(child);
  118. }
  119. }
  120. static void callchain_node__init_have_children(struct callchain_node *self)
  121. {
  122. struct callchain_list *chain;
  123. list_for_each_entry(chain, &self->val, list)
  124. chain->ms.has_children = rb_first(&self->rb_root) != NULL;
  125. callchain_node__init_have_children_rb_tree(self);
  126. }
  127. static void callchain__init_have_children(struct rb_root *self)
  128. {
  129. struct rb_node *nd;
  130. for (nd = rb_first(self); nd; nd = rb_next(nd)) {
  131. struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
  132. callchain_node__init_have_children(node);
  133. }
  134. }
  135. static void hist_entry__init_have_children(struct hist_entry *self)
  136. {
  137. if (!self->init_have_children) {
  138. callchain__init_have_children(&self->sorted_chain);
  139. self->init_have_children = true;
  140. }
  141. }
  142. static bool hist_browser__toggle_fold(struct hist_browser *self)
  143. {
  144. if (map_symbol__toggle_fold(self->selection)) {
  145. struct hist_entry *he = self->he_selection;
  146. hist_entry__init_have_children(he);
  147. self->hists->nr_entries -= he->nr_rows;
  148. if (he->ms.unfolded)
  149. he->nr_rows = callchain__count_rows(&he->sorted_chain);
  150. else
  151. he->nr_rows = 0;
  152. self->hists->nr_entries += he->nr_rows;
  153. self->b.nr_entries = self->hists->nr_entries;
  154. return true;
  155. }
  156. /* If it doesn't have children, no toggling performed */
  157. return false;
  158. }
  159. static int hist_browser__run(struct hist_browser *self, const char *title,
  160. struct newtExitStruct *es)
  161. {
  162. char str[256], unit;
  163. unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
  164. self->b.entries = &self->hists->entries;
  165. self->b.nr_entries = self->hists->nr_entries;
  166. hist_browser__refresh_dimensions(self);
  167. nr_events = convert_unit(nr_events, &unit);
  168. snprintf(str, sizeof(str), "Events: %lu%c ",
  169. nr_events, unit);
  170. newtDrawRootText(0, 0, str);
  171. if (ui_browser__show(&self->b, title) < 0)
  172. return -1;
  173. newtFormAddHotKey(self->b.form, 'A');
  174. newtFormAddHotKey(self->b.form, 'a');
  175. newtFormAddHotKey(self->b.form, '?');
  176. newtFormAddHotKey(self->b.form, 'h');
  177. newtFormAddHotKey(self->b.form, 'H');
  178. newtFormAddHotKey(self->b.form, 'd');
  179. newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
  180. newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
  181. newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
  182. while (1) {
  183. ui_browser__run(&self->b, es);
  184. if (es->reason != NEWT_EXIT_HOTKEY)
  185. break;
  186. switch (es->u.key) {
  187. case 'd': { /* Debug */
  188. static int seq;
  189. struct hist_entry *h = rb_entry(self->b.top,
  190. struct hist_entry, rb_node);
  191. ui_helpline__pop();
  192. ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
  193. seq++, self->b.nr_entries,
  194. self->hists->nr_entries,
  195. self->b.height,
  196. self->b.index,
  197. self->b.top_idx,
  198. h->row_offset, h->nr_rows);
  199. }
  200. continue;
  201. case NEWT_KEY_ENTER:
  202. if (hist_browser__toggle_fold(self))
  203. break;
  204. /* fall thru */
  205. default:
  206. return 0;
  207. }
  208. }
  209. return 0;
  210. }
  211. static char *callchain_list__sym_name(struct callchain_list *self,
  212. char *bf, size_t bfsize)
  213. {
  214. if (self->ms.sym)
  215. return self->ms.sym->name;
  216. snprintf(bf, bfsize, "%#Lx", self->ip);
  217. return bf;
  218. }
  219. #define LEVEL_OFFSET_STEP 3
  220. static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
  221. struct callchain_node *chain_node,
  222. u64 total, int level,
  223. unsigned short row,
  224. off_t *row_offset,
  225. bool *is_current_entry)
  226. {
  227. struct rb_node *node;
  228. int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
  229. u64 new_total, remaining;
  230. if (callchain_param.mode == CHAIN_GRAPH_REL)
  231. new_total = chain_node->children_hit;
  232. else
  233. new_total = total;
  234. remaining = new_total;
  235. node = rb_first(&chain_node->rb_root);
  236. while (node) {
  237. struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
  238. struct rb_node *next = rb_next(node);
  239. u64 cumul = cumul_hits(child);
  240. struct callchain_list *chain;
  241. char folded_sign = ' ';
  242. int first = true;
  243. int extra_offset = 0;
  244. remaining -= cumul;
  245. list_for_each_entry(chain, &child->val, list) {
  246. char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
  247. const char *str;
  248. int color;
  249. bool was_first = first;
  250. if (first) {
  251. first = false;
  252. chain->ms.has_children = chain->list.next != &child->val ||
  253. rb_first(&child->rb_root) != NULL;
  254. } else {
  255. extra_offset = LEVEL_OFFSET_STEP;
  256. chain->ms.has_children = chain->list.next == &child->val &&
  257. rb_first(&child->rb_root) != NULL;
  258. }
  259. folded_sign = callchain_list__folded(chain);
  260. if (*row_offset != 0) {
  261. --*row_offset;
  262. goto do_next;
  263. }
  264. alloc_str = NULL;
  265. str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
  266. if (was_first) {
  267. double percent = cumul * 100.0 / new_total;
  268. if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
  269. str = "Not enough memory!";
  270. else
  271. str = alloc_str;
  272. }
  273. color = HE_COLORSET_NORMAL;
  274. width = self->b.width - (offset + extra_offset + 2);
  275. if (ui_browser__is_current_entry(&self->b, row)) {
  276. self->selection = &chain->ms;
  277. color = HE_COLORSET_SELECTED;
  278. *is_current_entry = true;
  279. }
  280. SLsmg_set_color(color);
  281. SLsmg_gotorc(self->b.y + row, self->b.x);
  282. slsmg_write_nstring(" ", offset + extra_offset);
  283. slsmg_printf("%c ", folded_sign);
  284. slsmg_write_nstring(str, width);
  285. free(alloc_str);
  286. if (++row == self->b.height)
  287. goto out;
  288. do_next:
  289. if (folded_sign == '+')
  290. break;
  291. }
  292. if (folded_sign == '-') {
  293. const int new_level = level + (extra_offset ? 2 : 1);
  294. row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
  295. new_level, row, row_offset,
  296. is_current_entry);
  297. }
  298. if (row == self->b.height)
  299. goto out;
  300. node = next;
  301. }
  302. out:
  303. return row - first_row;
  304. }
  305. static int hist_browser__show_callchain_node(struct hist_browser *self,
  306. struct callchain_node *node,
  307. int level, unsigned short row,
  308. off_t *row_offset,
  309. bool *is_current_entry)
  310. {
  311. struct callchain_list *chain;
  312. int first_row = row,
  313. offset = level * LEVEL_OFFSET_STEP,
  314. width = self->b.width - offset;
  315. char folded_sign = ' ';
  316. list_for_each_entry(chain, &node->val, list) {
  317. char ipstr[BITS_PER_LONG / 4 + 1], *s;
  318. int color;
  319. /*
  320. * FIXME: This should be moved to somewhere else,
  321. * probably when the callchain is created, so as not to
  322. * traverse it all over again
  323. */
  324. chain->ms.has_children = rb_first(&node->rb_root) != NULL;
  325. folded_sign = callchain_list__folded(chain);
  326. if (*row_offset != 0) {
  327. --*row_offset;
  328. continue;
  329. }
  330. color = HE_COLORSET_NORMAL;
  331. if (ui_browser__is_current_entry(&self->b, row)) {
  332. self->selection = &chain->ms;
  333. color = HE_COLORSET_SELECTED;
  334. *is_current_entry = true;
  335. }
  336. s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
  337. SLsmg_gotorc(self->b.y + row, self->b.x);
  338. SLsmg_set_color(color);
  339. slsmg_write_nstring(" ", offset);
  340. slsmg_printf("%c ", folded_sign);
  341. slsmg_write_nstring(s, width - 2);
  342. if (++row == self->b.height)
  343. goto out;
  344. }
  345. if (folded_sign == '-')
  346. row += hist_browser__show_callchain_node_rb_tree(self, node,
  347. self->hists->stats.total_period,
  348. level + 1, row,
  349. row_offset,
  350. is_current_entry);
  351. out:
  352. return row - first_row;
  353. }
  354. static int hist_browser__show_callchain(struct hist_browser *self,
  355. struct rb_root *chain,
  356. int level, unsigned short row,
  357. off_t *row_offset,
  358. bool *is_current_entry)
  359. {
  360. struct rb_node *nd;
  361. int first_row = row;
  362. for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
  363. struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
  364. row += hist_browser__show_callchain_node(self, node, level,
  365. row, row_offset,
  366. is_current_entry);
  367. if (row == self->b.height)
  368. break;
  369. }
  370. return row - first_row;
  371. }
  372. static int hist_browser__show_entry(struct hist_browser *self,
  373. struct hist_entry *entry,
  374. unsigned short row)
  375. {
  376. char s[256];
  377. double percent;
  378. int printed = 0;
  379. int color, width = self->b.width;
  380. char folded_sign = ' ';
  381. bool current_entry = ui_browser__is_current_entry(&self->b, row);
  382. off_t row_offset = entry->row_offset;
  383. if (current_entry) {
  384. self->he_selection = entry;
  385. self->selection = &entry->ms;
  386. }
  387. if (symbol_conf.use_callchain) {
  388. entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
  389. folded_sign = hist_entry__folded(entry);
  390. }
  391. if (row_offset == 0) {
  392. hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
  393. 0, false, self->hists->stats.total_period);
  394. percent = (entry->period * 100.0) / self->hists->stats.total_period;
  395. color = HE_COLORSET_SELECTED;
  396. if (!current_entry) {
  397. if (percent >= MIN_RED)
  398. color = HE_COLORSET_TOP;
  399. else if (percent >= MIN_GREEN)
  400. color = HE_COLORSET_MEDIUM;
  401. else
  402. color = HE_COLORSET_NORMAL;
  403. }
  404. SLsmg_set_color(color);
  405. SLsmg_gotorc(self->b.y + row, self->b.x);
  406. if (symbol_conf.use_callchain) {
  407. slsmg_printf("%c ", folded_sign);
  408. width -= 2;
  409. }
  410. slsmg_write_nstring(s, width);
  411. ++row;
  412. ++printed;
  413. } else
  414. --row_offset;
  415. if (folded_sign == '-' && row != self->b.height) {
  416. printed += hist_browser__show_callchain(self, &entry->sorted_chain,
  417. 1, row, &row_offset,
  418. &current_entry);
  419. if (current_entry)
  420. self->he_selection = entry;
  421. }
  422. return printed;
  423. }
  424. static unsigned int hist_browser__refresh(struct ui_browser *self)
  425. {
  426. unsigned row = 0;
  427. struct rb_node *nd;
  428. struct hist_browser *hb = container_of(self, struct hist_browser, b);
  429. if (self->top == NULL)
  430. self->top = rb_first(&hb->hists->entries);
  431. for (nd = self->top; nd; nd = rb_next(nd)) {
  432. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  433. if (h->filtered)
  434. continue;
  435. row += hist_browser__show_entry(hb, h, row);
  436. if (row == self->height)
  437. break;
  438. }
  439. return row;
  440. }
  441. static struct rb_node *hists__filter_entries(struct rb_node *nd)
  442. {
  443. while (nd != NULL) {
  444. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  445. if (!h->filtered)
  446. return nd;
  447. nd = rb_next(nd);
  448. }
  449. return NULL;
  450. }
  451. static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
  452. {
  453. while (nd != NULL) {
  454. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  455. if (!h->filtered)
  456. return nd;
  457. nd = rb_prev(nd);
  458. }
  459. return NULL;
  460. }
  461. static void ui_browser__hists_seek(struct ui_browser *self,
  462. off_t offset, int whence)
  463. {
  464. struct hist_entry *h;
  465. struct rb_node *nd;
  466. bool first = true;
  467. switch (whence) {
  468. case SEEK_SET:
  469. nd = hists__filter_entries(rb_first(self->entries));
  470. break;
  471. case SEEK_CUR:
  472. nd = self->top;
  473. goto do_offset;
  474. case SEEK_END:
  475. nd = hists__filter_prev_entries(rb_last(self->entries));
  476. first = false;
  477. break;
  478. default:
  479. return;
  480. }
  481. /*
  482. * Moves not relative to the first visible entry invalidates its
  483. * row_offset:
  484. */
  485. h = rb_entry(self->top, struct hist_entry, rb_node);
  486. h->row_offset = 0;
  487. /*
  488. * Here we have to check if nd is expanded (+), if it is we can't go
  489. * the next top level hist_entry, instead we must compute an offset of
  490. * what _not_ to show and not change the first visible entry.
  491. *
  492. * This offset increments when we are going from top to bottom and
  493. * decreases when we're going from bottom to top.
  494. *
  495. * As we don't have backpointers to the top level in the callchains
  496. * structure, we need to always print the whole hist_entry callchain,
  497. * skipping the first ones that are before the first visible entry
  498. * and stop when we printed enough lines to fill the screen.
  499. */
  500. do_offset:
  501. if (offset > 0) {
  502. do {
  503. h = rb_entry(nd, struct hist_entry, rb_node);
  504. if (h->ms.unfolded) {
  505. u16 remaining = h->nr_rows - h->row_offset;
  506. if (offset > remaining) {
  507. offset -= remaining;
  508. h->row_offset = 0;
  509. } else {
  510. h->row_offset += offset;
  511. offset = 0;
  512. self->top = nd;
  513. break;
  514. }
  515. }
  516. nd = hists__filter_entries(rb_next(nd));
  517. if (nd == NULL)
  518. break;
  519. --offset;
  520. self->top = nd;
  521. } while (offset != 0);
  522. } else if (offset < 0) {
  523. while (1) {
  524. h = rb_entry(nd, struct hist_entry, rb_node);
  525. if (h->ms.unfolded) {
  526. if (first) {
  527. if (-offset > h->row_offset) {
  528. offset += h->row_offset;
  529. h->row_offset = 0;
  530. } else {
  531. h->row_offset += offset;
  532. offset = 0;
  533. self->top = nd;
  534. break;
  535. }
  536. } else {
  537. if (-offset > h->nr_rows) {
  538. offset += h->nr_rows;
  539. h->row_offset = 0;
  540. } else {
  541. h->row_offset = h->nr_rows + offset;
  542. offset = 0;
  543. self->top = nd;
  544. break;
  545. }
  546. }
  547. }
  548. nd = hists__filter_prev_entries(rb_prev(nd));
  549. if (nd == NULL)
  550. break;
  551. ++offset;
  552. self->top = nd;
  553. if (offset == 0) {
  554. /*
  555. * Last unfiltered hist_entry, check if it is
  556. * unfolded, if it is then we should have
  557. * row_offset at its last entry.
  558. */
  559. h = rb_entry(nd, struct hist_entry, rb_node);
  560. if (h->ms.unfolded)
  561. h->row_offset = h->nr_rows;
  562. break;
  563. }
  564. first = false;
  565. }
  566. } else {
  567. self->top = nd;
  568. h = rb_entry(nd, struct hist_entry, rb_node);
  569. h->row_offset = 0;
  570. }
  571. }
  572. static struct hist_browser *hist_browser__new(struct hists *hists)
  573. {
  574. struct hist_browser *self = zalloc(sizeof(*self));
  575. if (self) {
  576. self->hists = hists;
  577. self->b.refresh = hist_browser__refresh;
  578. self->b.seek = ui_browser__hists_seek;
  579. }
  580. return self;
  581. }
  582. static void hist_browser__delete(struct hist_browser *self)
  583. {
  584. newtFormDestroy(self->b.form);
  585. newtPopWindow();
  586. free(self);
  587. }
  588. static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
  589. {
  590. return self->he_selection;
  591. }
  592. static struct thread *hist_browser__selected_thread(struct hist_browser *self)
  593. {
  594. return self->he_selection->thread;
  595. }
  596. static int hist_browser__title(char *bf, size_t size, const char *ev_name,
  597. const struct dso *dso, const struct thread *thread)
  598. {
  599. int printed = 0;
  600. if (thread)
  601. printed += snprintf(bf + printed, size - printed,
  602. "Thread: %s(%d)",
  603. (thread->comm_set ? thread->comm : ""),
  604. thread->pid);
  605. if (dso)
  606. printed += snprintf(bf + printed, size - printed,
  607. "%sDSO: %s", thread ? " " : "",
  608. dso->short_name);
  609. return printed ?: snprintf(bf, size, "Event: %s", ev_name);
  610. }
  611. int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
  612. {
  613. struct hist_browser *browser = hist_browser__new(self);
  614. struct pstack *fstack;
  615. const struct thread *thread_filter = NULL;
  616. const struct dso *dso_filter = NULL;
  617. struct newtExitStruct es;
  618. char msg[160];
  619. int key = -1;
  620. if (browser == NULL)
  621. return -1;
  622. fstack = pstack__new(2);
  623. if (fstack == NULL)
  624. goto out;
  625. ui_helpline__push(helpline);
  626. hist_browser__title(msg, sizeof(msg), ev_name,
  627. dso_filter, thread_filter);
  628. while (1) {
  629. const struct thread *thread;
  630. const struct dso *dso;
  631. char *options[16];
  632. int nr_options = 0, choice = 0, i,
  633. annotate = -2, zoom_dso = -2, zoom_thread = -2,
  634. browse_map = -2;
  635. if (hist_browser__run(browser, msg, &es))
  636. break;
  637. thread = hist_browser__selected_thread(browser);
  638. dso = browser->selection->map ? browser->selection->map->dso : NULL;
  639. if (es.reason == NEWT_EXIT_HOTKEY) {
  640. key = es.u.key;
  641. switch (key) {
  642. case NEWT_KEY_F1:
  643. goto do_help;
  644. case NEWT_KEY_TAB:
  645. case NEWT_KEY_UNTAB:
  646. /*
  647. * Exit the browser, let hists__browser_tree
  648. * go to the next or previous
  649. */
  650. goto out_free_stack;
  651. default:;
  652. }
  653. key = toupper(key);
  654. switch (key) {
  655. case 'A':
  656. if (browser->selection->map == NULL &&
  657. browser->selection->map->dso->annotate_warned)
  658. continue;
  659. goto do_annotate;
  660. case 'D':
  661. goto zoom_dso;
  662. case 'T':
  663. goto zoom_thread;
  664. case 'H':
  665. case '?':
  666. do_help:
  667. ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
  668. "<- Zoom out\n"
  669. "a Annotate current symbol\n"
  670. "h/?/F1 Show this window\n"
  671. "d Zoom into current DSO\n"
  672. "t Zoom into current Thread\n"
  673. "q/CTRL+C Exit browser");
  674. continue;
  675. default:;
  676. }
  677. if (is_exit_key(key)) {
  678. if (key == NEWT_KEY_ESCAPE &&
  679. !ui__dialog_yesno("Do you really want to exit?"))
  680. continue;
  681. break;
  682. }
  683. if (es.u.key == NEWT_KEY_LEFT) {
  684. const void *top;
  685. if (pstack__empty(fstack))
  686. continue;
  687. top = pstack__pop(fstack);
  688. if (top == &dso_filter)
  689. goto zoom_out_dso;
  690. if (top == &thread_filter)
  691. goto zoom_out_thread;
  692. continue;
  693. }
  694. }
  695. if (browser->selection->sym != NULL &&
  696. !browser->selection->map->dso->annotate_warned &&
  697. asprintf(&options[nr_options], "Annotate %s",
  698. browser->selection->sym->name) > 0)
  699. annotate = nr_options++;
  700. if (thread != NULL &&
  701. asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
  702. (thread_filter ? "out of" : "into"),
  703. (thread->comm_set ? thread->comm : ""),
  704. thread->pid) > 0)
  705. zoom_thread = nr_options++;
  706. if (dso != NULL &&
  707. asprintf(&options[nr_options], "Zoom %s %s DSO",
  708. (dso_filter ? "out of" : "into"),
  709. (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
  710. zoom_dso = nr_options++;
  711. if (browser->selection->map != NULL &&
  712. asprintf(&options[nr_options], "Browse map details") > 0)
  713. browse_map = nr_options++;
  714. options[nr_options++] = (char *)"Exit";
  715. choice = ui__popup_menu(nr_options, options);
  716. for (i = 0; i < nr_options - 1; ++i)
  717. free(options[i]);
  718. if (choice == nr_options - 1)
  719. break;
  720. if (choice == -1)
  721. continue;
  722. if (choice == annotate) {
  723. struct hist_entry *he;
  724. do_annotate:
  725. if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
  726. browser->selection->map->dso->annotate_warned = 1;
  727. ui_helpline__puts("No vmlinux file found, can't "
  728. "annotate with just a "
  729. "kallsyms file");
  730. continue;
  731. }
  732. he = hist_browser__selected_entry(browser);
  733. if (he == NULL)
  734. continue;
  735. hist_entry__tui_annotate(he);
  736. } else if (choice == browse_map)
  737. map__browse(browser->selection->map);
  738. else if (choice == zoom_dso) {
  739. zoom_dso:
  740. if (dso_filter) {
  741. pstack__remove(fstack, &dso_filter);
  742. zoom_out_dso:
  743. ui_helpline__pop();
  744. dso_filter = NULL;
  745. } else {
  746. if (dso == NULL)
  747. continue;
  748. ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
  749. dso->kernel ? "the Kernel" : dso->short_name);
  750. dso_filter = dso;
  751. pstack__push(fstack, &dso_filter);
  752. }
  753. hists__filter_by_dso(self, dso_filter);
  754. hist_browser__title(msg, sizeof(msg), ev_name,
  755. dso_filter, thread_filter);
  756. hist_browser__reset(browser);
  757. } else if (choice == zoom_thread) {
  758. zoom_thread:
  759. if (thread_filter) {
  760. pstack__remove(fstack, &thread_filter);
  761. zoom_out_thread:
  762. ui_helpline__pop();
  763. thread_filter = NULL;
  764. } else {
  765. ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
  766. thread->comm_set ? thread->comm : "",
  767. thread->pid);
  768. thread_filter = thread;
  769. pstack__push(fstack, &thread_filter);
  770. }
  771. hists__filter_by_thread(self, thread_filter);
  772. hist_browser__title(msg, sizeof(msg), ev_name,
  773. dso_filter, thread_filter);
  774. hist_browser__reset(browser);
  775. }
  776. }
  777. out_free_stack:
  778. pstack__delete(fstack);
  779. out:
  780. hist_browser__delete(browser);
  781. return key;
  782. }
  783. int hists__tui_browse_tree(struct rb_root *self, const char *help)
  784. {
  785. struct rb_node *first = rb_first(self), *nd = first, *next;
  786. int key = 0;
  787. while (nd) {
  788. struct hists *hists = rb_entry(nd, struct hists, rb_node);
  789. const char *ev_name = __event_name(hists->type, hists->config);
  790. key = hists__browse(hists, help, ev_name);
  791. if (is_exit_key(key))
  792. break;
  793. switch (key) {
  794. case NEWT_KEY_TAB:
  795. next = rb_next(nd);
  796. if (next)
  797. nd = next;
  798. break;
  799. case NEWT_KEY_UNTAB:
  800. if (nd == first)
  801. continue;
  802. nd = rb_prev(nd);
  803. default:
  804. break;
  805. }
  806. }
  807. return key;
  808. }