newt.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267
  1. #define _GNU_SOURCE
  2. #include <stdio.h>
  3. #undef _GNU_SOURCE
  4. #include "ui/libslang.h"
  5. #include <signal.h>
  6. #include <stdlib.h>
  7. #include <elf.h>
  8. #include <newt.h>
  9. #include <sys/ttydefaults.h>
  10. #include "cache.h"
  11. #include "hist.h"
  12. #include "pstack.h"
  13. #include "session.h"
  14. #include "sort.h"
  15. #include "symbol.h"
  16. #include "ui/browser.h"
  17. #include "ui/helpline.h"
  18. newtComponent newt_form__new(void);
  19. static int ui_entry__read(const char *title, char *bf, size_t size, int width)
  20. {
  21. struct newtExitStruct es;
  22. newtComponent form, entry;
  23. const char *result;
  24. int err = -1;
  25. newtCenteredWindow(width, 1, title);
  26. form = newtForm(NULL, NULL, 0);
  27. if (form == NULL)
  28. return -1;
  29. entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
  30. if (entry == NULL)
  31. goto out_free_form;
  32. newtFormAddComponent(form, entry);
  33. newtFormAddHotKey(form, NEWT_KEY_ENTER);
  34. newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
  35. newtFormAddHotKey(form, NEWT_KEY_LEFT);
  36. newtFormAddHotKey(form, CTRL('c'));
  37. newtFormRun(form, &es);
  38. if (result != NULL) {
  39. strncpy(bf, result, size);
  40. err = 0;
  41. }
  42. out_free_form:
  43. newtPopWindow();
  44. newtFormDestroy(form);
  45. return 0;
  46. }
  47. char browser__last_msg[1024];
  48. int browser__show_help(const char *format, va_list ap)
  49. {
  50. int ret;
  51. static int backlog;
  52. ret = vsnprintf(browser__last_msg + backlog,
  53. sizeof(browser__last_msg) - backlog, format, ap);
  54. backlog += ret;
  55. if (browser__last_msg[backlog - 1] == '\n') {
  56. ui_helpline__puts(browser__last_msg);
  57. newtRefresh();
  58. backlog = 0;
  59. }
  60. return ret;
  61. }
  62. static void newt_form__set_exit_keys(newtComponent self)
  63. {
  64. newtFormAddHotKey(self, NEWT_KEY_LEFT);
  65. newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
  66. newtFormAddHotKey(self, 'Q');
  67. newtFormAddHotKey(self, 'q');
  68. newtFormAddHotKey(self, CTRL('c'));
  69. }
  70. newtComponent newt_form__new(void)
  71. {
  72. newtComponent self = newtForm(NULL, NULL, 0);
  73. if (self)
  74. newt_form__set_exit_keys(self);
  75. return self;
  76. }
  77. static int popup_menu(int argc, char * const argv[])
  78. {
  79. struct newtExitStruct es;
  80. int i, rc = -1, max_len = 5;
  81. newtComponent listbox, form = newt_form__new();
  82. if (form == NULL)
  83. return -1;
  84. listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
  85. if (listbox == NULL)
  86. goto out_destroy_form;
  87. newtFormAddComponent(form, listbox);
  88. for (i = 0; i < argc; ++i) {
  89. int len = strlen(argv[i]);
  90. if (len > max_len)
  91. max_len = len;
  92. if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
  93. goto out_destroy_form;
  94. }
  95. newtCenteredWindow(max_len, argc, NULL);
  96. newtFormRun(form, &es);
  97. rc = newtListboxGetCurrent(listbox) - NULL;
  98. if (es.reason == NEWT_EXIT_HOTKEY)
  99. rc = -1;
  100. newtPopWindow();
  101. out_destroy_form:
  102. newtFormDestroy(form);
  103. return rc;
  104. }
  105. static int ui__help_window(const char *text)
  106. {
  107. struct newtExitStruct es;
  108. newtComponent tb, form = newt_form__new();
  109. int rc = -1;
  110. int max_len = 0, nr_lines = 0;
  111. const char *t;
  112. if (form == NULL)
  113. return -1;
  114. t = text;
  115. while (1) {
  116. const char *sep = strchr(t, '\n');
  117. int len;
  118. if (sep == NULL)
  119. sep = strchr(t, '\0');
  120. len = sep - t;
  121. if (max_len < len)
  122. max_len = len;
  123. ++nr_lines;
  124. if (*sep == '\0')
  125. break;
  126. t = sep + 1;
  127. }
  128. tb = newtTextbox(0, 0, max_len, nr_lines, 0);
  129. if (tb == NULL)
  130. goto out_destroy_form;
  131. newtTextboxSetText(tb, text);
  132. newtFormAddComponent(form, tb);
  133. newtCenteredWindow(max_len, nr_lines, NULL);
  134. newtFormRun(form, &es);
  135. newtPopWindow();
  136. rc = 0;
  137. out_destroy_form:
  138. newtFormDestroy(form);
  139. return rc;
  140. }
  141. static bool dialog_yesno(const char *msg)
  142. {
  143. /* newtWinChoice should really be accepting const char pointers... */
  144. char yes[] = "Yes", no[] = "No";
  145. return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
  146. }
  147. static char *callchain_list__sym_name(struct callchain_list *self,
  148. char *bf, size_t bfsize)
  149. {
  150. if (self->ms.sym)
  151. return self->ms.sym->name;
  152. snprintf(bf, bfsize, "%#Lx", self->ip);
  153. return bf;
  154. }
  155. /* -------------------------------------------------------------------- */
  156. struct map_browser {
  157. struct ui_browser b;
  158. struct map *map;
  159. u16 namelen;
  160. u8 addrlen;
  161. };
  162. static void map_browser__write(struct ui_browser *self, void *nd, int row)
  163. {
  164. struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
  165. struct map_browser *mb = container_of(self, struct map_browser, b);
  166. bool current_entry = ui_browser__is_current_entry(self, row);
  167. int color = ui_browser__percent_color(0, current_entry);
  168. SLsmg_set_color(color);
  169. slsmg_printf("%*llx %*llx %c ",
  170. mb->addrlen, sym->start, mb->addrlen, sym->end,
  171. sym->binding == STB_GLOBAL ? 'g' :
  172. sym->binding == STB_LOCAL ? 'l' : 'w');
  173. slsmg_write_nstring(sym->name, mb->namelen);
  174. }
  175. /* FIXME uber-kludgy, see comment on cmd_report... */
  176. static u32 *symbol__browser_index(struct symbol *self)
  177. {
  178. return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
  179. }
  180. static int map_browser__search(struct map_browser *self)
  181. {
  182. char target[512];
  183. struct symbol *sym;
  184. int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
  185. if (err)
  186. return err;
  187. if (target[0] == '0' && tolower(target[1]) == 'x') {
  188. u64 addr = strtoull(target, NULL, 16);
  189. sym = map__find_symbol(self->map, addr, NULL);
  190. } else
  191. sym = map__find_symbol_by_name(self->map, target, NULL);
  192. if (sym != NULL) {
  193. u32 *idx = symbol__browser_index(sym);
  194. self->b.top = &sym->rb_node;
  195. self->b.index = self->b.top_idx = *idx;
  196. } else
  197. ui_helpline__fpush("%s not found!", target);
  198. return 0;
  199. }
  200. static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
  201. {
  202. if (ui_browser__show(&self->b, self->map->dso->long_name) < 0)
  203. return -1;
  204. ui_helpline__fpush("Press <- or ESC to exit, %s / to search",
  205. verbose ? "" : "restart with -v to use");
  206. newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
  207. newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
  208. if (verbose)
  209. newtFormAddHotKey(self->b.form, '/');
  210. while (1) {
  211. ui_browser__run(&self->b, es);
  212. if (es->reason != NEWT_EXIT_HOTKEY)
  213. break;
  214. if (verbose && es->u.key == '/')
  215. map_browser__search(self);
  216. else
  217. break;
  218. }
  219. newtFormDestroy(self->b.form);
  220. newtPopWindow();
  221. ui_helpline__pop();
  222. return 0;
  223. }
  224. static int map__browse(struct map *self)
  225. {
  226. struct map_browser mb = {
  227. .b = {
  228. .entries = &self->dso->symbols[self->type],
  229. .refresh = ui_browser__rb_tree_refresh,
  230. .seek = ui_browser__rb_tree_seek,
  231. .write = map_browser__write,
  232. },
  233. .map = self,
  234. };
  235. struct newtExitStruct es;
  236. struct rb_node *nd;
  237. char tmp[BITS_PER_LONG / 4];
  238. u64 maxaddr = 0;
  239. for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
  240. struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
  241. if (mb.namelen < pos->namelen)
  242. mb.namelen = pos->namelen;
  243. if (maxaddr < pos->end)
  244. maxaddr = pos->end;
  245. if (verbose) {
  246. u32 *idx = symbol__browser_index(pos);
  247. *idx = mb.b.nr_entries;
  248. }
  249. ++mb.b.nr_entries;
  250. }
  251. mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
  252. mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
  253. return map_browser__run(&mb, &es);
  254. }
  255. /* -------------------------------------------------------------------- */
  256. struct hist_browser {
  257. struct ui_browser b;
  258. struct hists *hists;
  259. struct hist_entry *he_selection;
  260. struct map_symbol *selection;
  261. };
  262. static void hist_browser__reset(struct hist_browser *self);
  263. static int hist_browser__run(struct hist_browser *self, const char *title,
  264. struct newtExitStruct *es);
  265. static unsigned int hist_browser__refresh(struct ui_browser *self);
  266. static void ui_browser__hists_seek(struct ui_browser *self,
  267. off_t offset, int whence);
  268. static struct hist_browser *hist_browser__new(struct hists *hists)
  269. {
  270. struct hist_browser *self = zalloc(sizeof(*self));
  271. if (self) {
  272. self->hists = hists;
  273. self->b.refresh = hist_browser__refresh;
  274. self->b.seek = ui_browser__hists_seek;
  275. }
  276. return self;
  277. }
  278. static void hist_browser__delete(struct hist_browser *self)
  279. {
  280. newtFormDestroy(self->b.form);
  281. newtPopWindow();
  282. free(self);
  283. }
  284. static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
  285. {
  286. return self->he_selection;
  287. }
  288. static struct thread *hist_browser__selected_thread(struct hist_browser *self)
  289. {
  290. return self->he_selection->thread;
  291. }
  292. static int hist_browser__title(char *bf, size_t size, const char *ev_name,
  293. const struct dso *dso, const struct thread *thread)
  294. {
  295. int printed = 0;
  296. if (thread)
  297. printed += snprintf(bf + printed, size - printed,
  298. "Thread: %s(%d)",
  299. (thread->comm_set ? thread->comm : ""),
  300. thread->pid);
  301. if (dso)
  302. printed += snprintf(bf + printed, size - printed,
  303. "%sDSO: %s", thread ? " " : "",
  304. dso->short_name);
  305. return printed ?: snprintf(bf, size, "Event: %s", ev_name);
  306. }
  307. int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
  308. {
  309. struct hist_browser *browser = hist_browser__new(self);
  310. struct pstack *fstack;
  311. const struct thread *thread_filter = NULL;
  312. const struct dso *dso_filter = NULL;
  313. struct newtExitStruct es;
  314. char msg[160];
  315. int key = -1;
  316. if (browser == NULL)
  317. return -1;
  318. fstack = pstack__new(2);
  319. if (fstack == NULL)
  320. goto out;
  321. ui_helpline__push(helpline);
  322. hist_browser__title(msg, sizeof(msg), ev_name,
  323. dso_filter, thread_filter);
  324. while (1) {
  325. const struct thread *thread;
  326. const struct dso *dso;
  327. char *options[16];
  328. int nr_options = 0, choice = 0, i,
  329. annotate = -2, zoom_dso = -2, zoom_thread = -2,
  330. browse_map = -2;
  331. if (hist_browser__run(browser, msg, &es))
  332. break;
  333. thread = hist_browser__selected_thread(browser);
  334. dso = browser->selection->map ? browser->selection->map->dso : NULL;
  335. if (es.reason == NEWT_EXIT_HOTKEY) {
  336. key = es.u.key;
  337. switch (key) {
  338. case NEWT_KEY_F1:
  339. goto do_help;
  340. case NEWT_KEY_TAB:
  341. case NEWT_KEY_UNTAB:
  342. /*
  343. * Exit the browser, let hists__browser_tree
  344. * go to the next or previous
  345. */
  346. goto out_free_stack;
  347. default:;
  348. }
  349. key = toupper(key);
  350. switch (key) {
  351. case 'A':
  352. if (browser->selection->map == NULL &&
  353. browser->selection->map->dso->annotate_warned)
  354. continue;
  355. goto do_annotate;
  356. case 'D':
  357. goto zoom_dso;
  358. case 'T':
  359. goto zoom_thread;
  360. case 'H':
  361. case '?':
  362. do_help:
  363. ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
  364. "<- Zoom out\n"
  365. "a Annotate current symbol\n"
  366. "h/?/F1 Show this window\n"
  367. "d Zoom into current DSO\n"
  368. "t Zoom into current Thread\n"
  369. "q/CTRL+C Exit browser");
  370. continue;
  371. default:;
  372. }
  373. if (is_exit_key(key)) {
  374. if (key == NEWT_KEY_ESCAPE) {
  375. if (dialog_yesno("Do you really want to exit?"))
  376. break;
  377. else
  378. continue;
  379. } else
  380. break;
  381. }
  382. if (es.u.key == NEWT_KEY_LEFT) {
  383. const void *top;
  384. if (pstack__empty(fstack))
  385. continue;
  386. top = pstack__pop(fstack);
  387. if (top == &dso_filter)
  388. goto zoom_out_dso;
  389. if (top == &thread_filter)
  390. goto zoom_out_thread;
  391. continue;
  392. }
  393. }
  394. if (browser->selection->sym != NULL &&
  395. !browser->selection->map->dso->annotate_warned &&
  396. asprintf(&options[nr_options], "Annotate %s",
  397. browser->selection->sym->name) > 0)
  398. annotate = nr_options++;
  399. if (thread != NULL &&
  400. asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
  401. (thread_filter ? "out of" : "into"),
  402. (thread->comm_set ? thread->comm : ""),
  403. thread->pid) > 0)
  404. zoom_thread = nr_options++;
  405. if (dso != NULL &&
  406. asprintf(&options[nr_options], "Zoom %s %s DSO",
  407. (dso_filter ? "out of" : "into"),
  408. (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
  409. zoom_dso = nr_options++;
  410. if (browser->selection->map != NULL &&
  411. asprintf(&options[nr_options], "Browse map details") > 0)
  412. browse_map = nr_options++;
  413. options[nr_options++] = (char *)"Exit";
  414. choice = popup_menu(nr_options, options);
  415. for (i = 0; i < nr_options - 1; ++i)
  416. free(options[i]);
  417. if (choice == nr_options - 1)
  418. break;
  419. if (choice == -1)
  420. continue;
  421. if (choice == annotate) {
  422. struct hist_entry *he;
  423. do_annotate:
  424. if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
  425. browser->selection->map->dso->annotate_warned = 1;
  426. ui_helpline__puts("No vmlinux file found, can't "
  427. "annotate with just a "
  428. "kallsyms file");
  429. continue;
  430. }
  431. he = hist_browser__selected_entry(browser);
  432. if (he == NULL)
  433. continue;
  434. hist_entry__tui_annotate(he);
  435. } else if (choice == browse_map)
  436. map__browse(browser->selection->map);
  437. else if (choice == zoom_dso) {
  438. zoom_dso:
  439. if (dso_filter) {
  440. pstack__remove(fstack, &dso_filter);
  441. zoom_out_dso:
  442. ui_helpline__pop();
  443. dso_filter = NULL;
  444. } else {
  445. if (dso == NULL)
  446. continue;
  447. ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
  448. dso->kernel ? "the Kernel" : dso->short_name);
  449. dso_filter = dso;
  450. pstack__push(fstack, &dso_filter);
  451. }
  452. hists__filter_by_dso(self, dso_filter);
  453. hist_browser__title(msg, sizeof(msg), ev_name,
  454. dso_filter, thread_filter);
  455. hist_browser__reset(browser);
  456. } else if (choice == zoom_thread) {
  457. zoom_thread:
  458. if (thread_filter) {
  459. pstack__remove(fstack, &thread_filter);
  460. zoom_out_thread:
  461. ui_helpline__pop();
  462. thread_filter = NULL;
  463. } else {
  464. ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
  465. thread->comm_set ? thread->comm : "",
  466. thread->pid);
  467. thread_filter = thread;
  468. pstack__push(fstack, &thread_filter);
  469. }
  470. hists__filter_by_thread(self, thread_filter);
  471. hist_browser__title(msg, sizeof(msg), ev_name,
  472. dso_filter, thread_filter);
  473. hist_browser__reset(browser);
  474. }
  475. }
  476. out_free_stack:
  477. pstack__delete(fstack);
  478. out:
  479. hist_browser__delete(browser);
  480. return key;
  481. }
  482. int hists__tui_browse_tree(struct rb_root *self, const char *help)
  483. {
  484. struct rb_node *first = rb_first(self), *nd = first, *next;
  485. int key = 0;
  486. while (nd) {
  487. struct hists *hists = rb_entry(nd, struct hists, rb_node);
  488. const char *ev_name = __event_name(hists->type, hists->config);
  489. key = hists__browse(hists, help, ev_name);
  490. if (is_exit_key(key))
  491. break;
  492. switch (key) {
  493. case NEWT_KEY_TAB:
  494. next = rb_next(nd);
  495. if (next)
  496. nd = next;
  497. break;
  498. case NEWT_KEY_UNTAB:
  499. if (nd == first)
  500. continue;
  501. nd = rb_prev(nd);
  502. default:
  503. break;
  504. }
  505. }
  506. return key;
  507. }
  508. static void newt_suspend(void *d __used)
  509. {
  510. newtSuspend();
  511. raise(SIGTSTP);
  512. newtResume();
  513. }
  514. void setup_browser(void)
  515. {
  516. if (!isatty(1) || !use_browser || dump_trace) {
  517. use_browser = 0;
  518. setup_pager();
  519. return;
  520. }
  521. use_browser = 1;
  522. newtInit();
  523. newtCls();
  524. newtSetSuspendCallback(newt_suspend, NULL);
  525. ui_helpline__puts(" ");
  526. ui_browser__init();
  527. }
  528. void exit_browser(bool wait_for_ok)
  529. {
  530. if (use_browser > 0) {
  531. if (wait_for_ok) {
  532. char title[] = "Fatal Error", ok[] = "Ok";
  533. newtWinMessage(title, ok, browser__last_msg);
  534. }
  535. newtFinished();
  536. }
  537. }
  538. static void hist_browser__refresh_dimensions(struct hist_browser *self)
  539. {
  540. /* 3 == +/- toggle symbol before actual hist_entry rendering */
  541. self->b.width = 3 + (hists__sort_list_width(self->hists) +
  542. sizeof("[k]"));
  543. }
  544. static void hist_browser__reset(struct hist_browser *self)
  545. {
  546. self->b.nr_entries = self->hists->nr_entries;
  547. hist_browser__refresh_dimensions(self);
  548. ui_browser__reset_index(&self->b);
  549. }
  550. static char tree__folded_sign(bool unfolded)
  551. {
  552. return unfolded ? '-' : '+';
  553. }
  554. static char map_symbol__folded(const struct map_symbol *self)
  555. {
  556. return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
  557. }
  558. static char hist_entry__folded(const struct hist_entry *self)
  559. {
  560. return map_symbol__folded(&self->ms);
  561. }
  562. static char callchain_list__folded(const struct callchain_list *self)
  563. {
  564. return map_symbol__folded(&self->ms);
  565. }
  566. static bool map_symbol__toggle_fold(struct map_symbol *self)
  567. {
  568. if (!self->has_children)
  569. return false;
  570. self->unfolded = !self->unfolded;
  571. return true;
  572. }
  573. #define LEVEL_OFFSET_STEP 3
  574. static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
  575. struct callchain_node *chain_node,
  576. u64 total, int level,
  577. unsigned short row,
  578. off_t *row_offset,
  579. bool *is_current_entry)
  580. {
  581. struct rb_node *node;
  582. int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
  583. u64 new_total, remaining;
  584. if (callchain_param.mode == CHAIN_GRAPH_REL)
  585. new_total = chain_node->children_hit;
  586. else
  587. new_total = total;
  588. remaining = new_total;
  589. node = rb_first(&chain_node->rb_root);
  590. while (node) {
  591. struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
  592. struct rb_node *next = rb_next(node);
  593. u64 cumul = cumul_hits(child);
  594. struct callchain_list *chain;
  595. char folded_sign = ' ';
  596. int first = true;
  597. int extra_offset = 0;
  598. remaining -= cumul;
  599. list_for_each_entry(chain, &child->val, list) {
  600. char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
  601. const char *str;
  602. int color;
  603. bool was_first = first;
  604. if (first) {
  605. first = false;
  606. chain->ms.has_children = chain->list.next != &child->val ||
  607. rb_first(&child->rb_root) != NULL;
  608. } else {
  609. extra_offset = LEVEL_OFFSET_STEP;
  610. chain->ms.has_children = chain->list.next == &child->val &&
  611. rb_first(&child->rb_root) != NULL;
  612. }
  613. folded_sign = callchain_list__folded(chain);
  614. if (*row_offset != 0) {
  615. --*row_offset;
  616. goto do_next;
  617. }
  618. alloc_str = NULL;
  619. str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
  620. if (was_first) {
  621. double percent = cumul * 100.0 / new_total;
  622. if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
  623. str = "Not enough memory!";
  624. else
  625. str = alloc_str;
  626. }
  627. color = HE_COLORSET_NORMAL;
  628. width = self->b.width - (offset + extra_offset + 2);
  629. if (ui_browser__is_current_entry(&self->b, row)) {
  630. self->selection = &chain->ms;
  631. color = HE_COLORSET_SELECTED;
  632. *is_current_entry = true;
  633. }
  634. SLsmg_set_color(color);
  635. SLsmg_gotorc(self->b.y + row, self->b.x);
  636. slsmg_write_nstring(" ", offset + extra_offset);
  637. slsmg_printf("%c ", folded_sign);
  638. slsmg_write_nstring(str, width);
  639. free(alloc_str);
  640. if (++row == self->b.height)
  641. goto out;
  642. do_next:
  643. if (folded_sign == '+')
  644. break;
  645. }
  646. if (folded_sign == '-') {
  647. const int new_level = level + (extra_offset ? 2 : 1);
  648. row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
  649. new_level, row, row_offset,
  650. is_current_entry);
  651. }
  652. if (row == self->b.height)
  653. goto out;
  654. node = next;
  655. }
  656. out:
  657. return row - first_row;
  658. }
  659. static int hist_browser__show_callchain_node(struct hist_browser *self,
  660. struct callchain_node *node,
  661. int level, unsigned short row,
  662. off_t *row_offset,
  663. bool *is_current_entry)
  664. {
  665. struct callchain_list *chain;
  666. int first_row = row,
  667. offset = level * LEVEL_OFFSET_STEP,
  668. width = self->b.width - offset;
  669. char folded_sign = ' ';
  670. list_for_each_entry(chain, &node->val, list) {
  671. char ipstr[BITS_PER_LONG / 4 + 1], *s;
  672. int color;
  673. /*
  674. * FIXME: This should be moved to somewhere else,
  675. * probably when the callchain is created, so as not to
  676. * traverse it all over again
  677. */
  678. chain->ms.has_children = rb_first(&node->rb_root) != NULL;
  679. folded_sign = callchain_list__folded(chain);
  680. if (*row_offset != 0) {
  681. --*row_offset;
  682. continue;
  683. }
  684. color = HE_COLORSET_NORMAL;
  685. if (ui_browser__is_current_entry(&self->b, row)) {
  686. self->selection = &chain->ms;
  687. color = HE_COLORSET_SELECTED;
  688. *is_current_entry = true;
  689. }
  690. s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
  691. SLsmg_gotorc(self->b.y + row, self->b.x);
  692. SLsmg_set_color(color);
  693. slsmg_write_nstring(" ", offset);
  694. slsmg_printf("%c ", folded_sign);
  695. slsmg_write_nstring(s, width - 2);
  696. if (++row == self->b.height)
  697. goto out;
  698. }
  699. if (folded_sign == '-')
  700. row += hist_browser__show_callchain_node_rb_tree(self, node,
  701. self->hists->stats.total_period,
  702. level + 1, row,
  703. row_offset,
  704. is_current_entry);
  705. out:
  706. return row - first_row;
  707. }
  708. static int hist_browser__show_callchain(struct hist_browser *self,
  709. struct rb_root *chain,
  710. int level, unsigned short row,
  711. off_t *row_offset,
  712. bool *is_current_entry)
  713. {
  714. struct rb_node *nd;
  715. int first_row = row;
  716. for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
  717. struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
  718. row += hist_browser__show_callchain_node(self, node, level,
  719. row, row_offset,
  720. is_current_entry);
  721. if (row == self->b.height)
  722. break;
  723. }
  724. return row - first_row;
  725. }
  726. static int hist_browser__show_entry(struct hist_browser *self,
  727. struct hist_entry *entry,
  728. unsigned short row)
  729. {
  730. char s[256];
  731. double percent;
  732. int printed = 0;
  733. int color, width = self->b.width;
  734. char folded_sign = ' ';
  735. bool current_entry = ui_browser__is_current_entry(&self->b, row);
  736. off_t row_offset = entry->row_offset;
  737. if (current_entry) {
  738. self->he_selection = entry;
  739. self->selection = &entry->ms;
  740. }
  741. if (symbol_conf.use_callchain) {
  742. entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
  743. folded_sign = hist_entry__folded(entry);
  744. }
  745. if (row_offset == 0) {
  746. hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
  747. 0, false, self->hists->stats.total_period);
  748. percent = (entry->period * 100.0) / self->hists->stats.total_period;
  749. color = HE_COLORSET_SELECTED;
  750. if (!current_entry) {
  751. if (percent >= MIN_RED)
  752. color = HE_COLORSET_TOP;
  753. else if (percent >= MIN_GREEN)
  754. color = HE_COLORSET_MEDIUM;
  755. else
  756. color = HE_COLORSET_NORMAL;
  757. }
  758. SLsmg_set_color(color);
  759. SLsmg_gotorc(self->b.y + row, self->b.x);
  760. if (symbol_conf.use_callchain) {
  761. slsmg_printf("%c ", folded_sign);
  762. width -= 2;
  763. }
  764. slsmg_write_nstring(s, width);
  765. ++row;
  766. ++printed;
  767. } else
  768. --row_offset;
  769. if (folded_sign == '-' && row != self->b.height) {
  770. printed += hist_browser__show_callchain(self, &entry->sorted_chain,
  771. 1, row, &row_offset,
  772. &current_entry);
  773. if (current_entry)
  774. self->he_selection = entry;
  775. }
  776. return printed;
  777. }
  778. static unsigned int hist_browser__refresh(struct ui_browser *self)
  779. {
  780. unsigned row = 0;
  781. struct rb_node *nd;
  782. struct hist_browser *hb = container_of(self, struct hist_browser, b);
  783. if (self->top == NULL)
  784. self->top = rb_first(&hb->hists->entries);
  785. for (nd = self->top; nd; nd = rb_next(nd)) {
  786. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  787. if (h->filtered)
  788. continue;
  789. row += hist_browser__show_entry(hb, h, row);
  790. if (row == self->height)
  791. break;
  792. }
  793. return row;
  794. }
  795. static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
  796. {
  797. struct rb_node *nd = rb_first(&self->rb_root);
  798. for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
  799. struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
  800. struct callchain_list *chain;
  801. int first = true;
  802. list_for_each_entry(chain, &child->val, list) {
  803. if (first) {
  804. first = false;
  805. chain->ms.has_children = chain->list.next != &child->val ||
  806. rb_first(&child->rb_root) != NULL;
  807. } else
  808. chain->ms.has_children = chain->list.next == &child->val &&
  809. rb_first(&child->rb_root) != NULL;
  810. }
  811. callchain_node__init_have_children_rb_tree(child);
  812. }
  813. }
  814. static void callchain_node__init_have_children(struct callchain_node *self)
  815. {
  816. struct callchain_list *chain;
  817. list_for_each_entry(chain, &self->val, list)
  818. chain->ms.has_children = rb_first(&self->rb_root) != NULL;
  819. callchain_node__init_have_children_rb_tree(self);
  820. }
  821. static void callchain__init_have_children(struct rb_root *self)
  822. {
  823. struct rb_node *nd;
  824. for (nd = rb_first(self); nd; nd = rb_next(nd)) {
  825. struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
  826. callchain_node__init_have_children(node);
  827. }
  828. }
  829. static void hist_entry__init_have_children(struct hist_entry *self)
  830. {
  831. if (!self->init_have_children) {
  832. callchain__init_have_children(&self->sorted_chain);
  833. self->init_have_children = true;
  834. }
  835. }
  836. static struct rb_node *hists__filter_entries(struct rb_node *nd)
  837. {
  838. while (nd != NULL) {
  839. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  840. if (!h->filtered)
  841. return nd;
  842. nd = rb_next(nd);
  843. }
  844. return NULL;
  845. }
  846. static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
  847. {
  848. while (nd != NULL) {
  849. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  850. if (!h->filtered)
  851. return nd;
  852. nd = rb_prev(nd);
  853. }
  854. return NULL;
  855. }
  856. static void ui_browser__hists_seek(struct ui_browser *self,
  857. off_t offset, int whence)
  858. {
  859. struct hist_entry *h;
  860. struct rb_node *nd;
  861. bool first = true;
  862. switch (whence) {
  863. case SEEK_SET:
  864. nd = hists__filter_entries(rb_first(self->entries));
  865. break;
  866. case SEEK_CUR:
  867. nd = self->top;
  868. goto do_offset;
  869. case SEEK_END:
  870. nd = hists__filter_prev_entries(rb_last(self->entries));
  871. first = false;
  872. break;
  873. default:
  874. return;
  875. }
  876. /*
  877. * Moves not relative to the first visible entry invalidates its
  878. * row_offset:
  879. */
  880. h = rb_entry(self->top, struct hist_entry, rb_node);
  881. h->row_offset = 0;
  882. /*
  883. * Here we have to check if nd is expanded (+), if it is we can't go
  884. * the next top level hist_entry, instead we must compute an offset of
  885. * what _not_ to show and not change the first visible entry.
  886. *
  887. * This offset increments when we are going from top to bottom and
  888. * decreases when we're going from bottom to top.
  889. *
  890. * As we don't have backpointers to the top level in the callchains
  891. * structure, we need to always print the whole hist_entry callchain,
  892. * skipping the first ones that are before the first visible entry
  893. * and stop when we printed enough lines to fill the screen.
  894. */
  895. do_offset:
  896. if (offset > 0) {
  897. do {
  898. h = rb_entry(nd, struct hist_entry, rb_node);
  899. if (h->ms.unfolded) {
  900. u16 remaining = h->nr_rows - h->row_offset;
  901. if (offset > remaining) {
  902. offset -= remaining;
  903. h->row_offset = 0;
  904. } else {
  905. h->row_offset += offset;
  906. offset = 0;
  907. self->top = nd;
  908. break;
  909. }
  910. }
  911. nd = hists__filter_entries(rb_next(nd));
  912. if (nd == NULL)
  913. break;
  914. --offset;
  915. self->top = nd;
  916. } while (offset != 0);
  917. } else if (offset < 0) {
  918. while (1) {
  919. h = rb_entry(nd, struct hist_entry, rb_node);
  920. if (h->ms.unfolded) {
  921. if (first) {
  922. if (-offset > h->row_offset) {
  923. offset += h->row_offset;
  924. h->row_offset = 0;
  925. } else {
  926. h->row_offset += offset;
  927. offset = 0;
  928. self->top = nd;
  929. break;
  930. }
  931. } else {
  932. if (-offset > h->nr_rows) {
  933. offset += h->nr_rows;
  934. h->row_offset = 0;
  935. } else {
  936. h->row_offset = h->nr_rows + offset;
  937. offset = 0;
  938. self->top = nd;
  939. break;
  940. }
  941. }
  942. }
  943. nd = hists__filter_prev_entries(rb_prev(nd));
  944. if (nd == NULL)
  945. break;
  946. ++offset;
  947. self->top = nd;
  948. if (offset == 0) {
  949. /*
  950. * Last unfiltered hist_entry, check if it is
  951. * unfolded, if it is then we should have
  952. * row_offset at its last entry.
  953. */
  954. h = rb_entry(nd, struct hist_entry, rb_node);
  955. if (h->ms.unfolded)
  956. h->row_offset = h->nr_rows;
  957. break;
  958. }
  959. first = false;
  960. }
  961. } else {
  962. self->top = nd;
  963. h = rb_entry(nd, struct hist_entry, rb_node);
  964. h->row_offset = 0;
  965. }
  966. }
  967. static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
  968. {
  969. int n = 0;
  970. struct rb_node *nd;
  971. for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
  972. struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
  973. struct callchain_list *chain;
  974. char folded_sign = ' '; /* No children */
  975. list_for_each_entry(chain, &child->val, list) {
  976. ++n;
  977. /* We need this because we may not have children */
  978. folded_sign = callchain_list__folded(chain);
  979. if (folded_sign == '+')
  980. break;
  981. }
  982. if (folded_sign == '-') /* Have children and they're unfolded */
  983. n += callchain_node__count_rows_rb_tree(child);
  984. }
  985. return n;
  986. }
  987. static int callchain_node__count_rows(struct callchain_node *node)
  988. {
  989. struct callchain_list *chain;
  990. bool unfolded = false;
  991. int n = 0;
  992. list_for_each_entry(chain, &node->val, list) {
  993. ++n;
  994. unfolded = chain->ms.unfolded;
  995. }
  996. if (unfolded)
  997. n += callchain_node__count_rows_rb_tree(node);
  998. return n;
  999. }
  1000. static int callchain__count_rows(struct rb_root *chain)
  1001. {
  1002. struct rb_node *nd;
  1003. int n = 0;
  1004. for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
  1005. struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
  1006. n += callchain_node__count_rows(node);
  1007. }
  1008. return n;
  1009. }
  1010. static bool hist_browser__toggle_fold(struct hist_browser *self)
  1011. {
  1012. if (map_symbol__toggle_fold(self->selection)) {
  1013. struct hist_entry *he = self->he_selection;
  1014. hist_entry__init_have_children(he);
  1015. self->hists->nr_entries -= he->nr_rows;
  1016. if (he->ms.unfolded)
  1017. he->nr_rows = callchain__count_rows(&he->sorted_chain);
  1018. else
  1019. he->nr_rows = 0;
  1020. self->hists->nr_entries += he->nr_rows;
  1021. self->b.nr_entries = self->hists->nr_entries;
  1022. return true;
  1023. }
  1024. /* If it doesn't have children, no toggling performed */
  1025. return false;
  1026. }
  1027. static int hist_browser__run(struct hist_browser *self, const char *title,
  1028. struct newtExitStruct *es)
  1029. {
  1030. char str[256], unit;
  1031. unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
  1032. self->b.entries = &self->hists->entries;
  1033. self->b.nr_entries = self->hists->nr_entries;
  1034. hist_browser__refresh_dimensions(self);
  1035. nr_events = convert_unit(nr_events, &unit);
  1036. snprintf(str, sizeof(str), "Events: %lu%c ",
  1037. nr_events, unit);
  1038. newtDrawRootText(0, 0, str);
  1039. if (ui_browser__show(&self->b, title) < 0)
  1040. return -1;
  1041. newtFormAddHotKey(self->b.form, 'A');
  1042. newtFormAddHotKey(self->b.form, 'a');
  1043. newtFormAddHotKey(self->b.form, '?');
  1044. newtFormAddHotKey(self->b.form, 'h');
  1045. newtFormAddHotKey(self->b.form, 'H');
  1046. newtFormAddHotKey(self->b.form, 'd');
  1047. newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
  1048. newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
  1049. newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
  1050. while (1) {
  1051. ui_browser__run(&self->b, es);
  1052. if (es->reason != NEWT_EXIT_HOTKEY)
  1053. break;
  1054. switch (es->u.key) {
  1055. case 'd': { /* Debug */
  1056. static int seq;
  1057. struct hist_entry *h = rb_entry(self->b.top,
  1058. struct hist_entry, rb_node);
  1059. ui_helpline__pop();
  1060. ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
  1061. seq++, self->b.nr_entries,
  1062. self->hists->nr_entries,
  1063. self->b.height,
  1064. self->b.index,
  1065. self->b.top_idx,
  1066. h->row_offset, h->nr_rows);
  1067. }
  1068. continue;
  1069. case NEWT_KEY_ENTER:
  1070. if (hist_browser__toggle_fold(self))
  1071. break;
  1072. /* fall thru */
  1073. default:
  1074. return 0;
  1075. }
  1076. }
  1077. return 0;
  1078. }