newt.c 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  1. #define _GNU_SOURCE
  2. #include <stdio.h>
  3. #undef _GNU_SOURCE
  4. /*
  5. * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
  6. * the build if it isn't defined. Use the equivalent one that glibc
  7. * has on features.h.
  8. */
  9. #include <features.h>
  10. #ifndef HAVE_LONG_LONG
  11. #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
  12. #endif
  13. #include <slang.h>
  14. #include <stdlib.h>
  15. #include <newt.h>
  16. #include <sys/ttydefaults.h>
  17. #include "cache.h"
  18. #include "hist.h"
  19. #include "pstack.h"
  20. #include "session.h"
  21. #include "sort.h"
  22. #include "symbol.h"
  23. #if SLANG_VERSION < 20104
  24. #define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
  25. #define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
  26. #define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
  27. (char *)fg, (char *)bg)
  28. #else
  29. #define slsmg_printf SLsmg_printf
  30. #define slsmg_write_nstring SLsmg_write_nstring
  31. #define sltt_set_color SLtt_set_color
  32. #endif
  33. struct ui_progress {
  34. newtComponent form, scale;
  35. };
  36. struct ui_progress *ui_progress__new(const char *title, u64 total)
  37. {
  38. struct ui_progress *self = malloc(sizeof(*self));
  39. if (self != NULL) {
  40. int cols;
  41. newtGetScreenSize(&cols, NULL);
  42. cols -= 4;
  43. newtCenteredWindow(cols, 1, title);
  44. self->form = newtForm(NULL, NULL, 0);
  45. if (self->form == NULL)
  46. goto out_free_self;
  47. self->scale = newtScale(0, 0, cols, total);
  48. if (self->scale == NULL)
  49. goto out_free_form;
  50. newtFormAddComponent(self->form, self->scale);
  51. newtRefresh();
  52. }
  53. return self;
  54. out_free_form:
  55. newtFormDestroy(self->form);
  56. out_free_self:
  57. free(self);
  58. return NULL;
  59. }
  60. void ui_progress__update(struct ui_progress *self, u64 curr)
  61. {
  62. newtScaleSet(self->scale, curr);
  63. newtRefresh();
  64. }
  65. void ui_progress__delete(struct ui_progress *self)
  66. {
  67. newtFormDestroy(self->form);
  68. newtPopWindow();
  69. free(self);
  70. }
  71. static void ui_helpline__pop(void)
  72. {
  73. newtPopHelpLine();
  74. }
  75. static void ui_helpline__push(const char *msg)
  76. {
  77. newtPushHelpLine(msg);
  78. }
  79. static void ui_helpline__vpush(const char *fmt, va_list ap)
  80. {
  81. char *s;
  82. if (vasprintf(&s, fmt, ap) < 0)
  83. vfprintf(stderr, fmt, ap);
  84. else {
  85. ui_helpline__push(s);
  86. free(s);
  87. }
  88. }
  89. static void ui_helpline__fpush(const char *fmt, ...)
  90. {
  91. va_list ap;
  92. va_start(ap, fmt);
  93. ui_helpline__vpush(fmt, ap);
  94. va_end(ap);
  95. }
  96. static void ui_helpline__puts(const char *msg)
  97. {
  98. ui_helpline__pop();
  99. ui_helpline__push(msg);
  100. }
  101. static char browser__last_msg[1024];
  102. int browser__show_help(const char *format, va_list ap)
  103. {
  104. int ret;
  105. static int backlog;
  106. ret = vsnprintf(browser__last_msg + backlog,
  107. sizeof(browser__last_msg) - backlog, format, ap);
  108. backlog += ret;
  109. if (browser__last_msg[backlog - 1] == '\n') {
  110. ui_helpline__puts(browser__last_msg);
  111. newtRefresh();
  112. backlog = 0;
  113. }
  114. return ret;
  115. }
  116. static void newt_form__set_exit_keys(newtComponent self)
  117. {
  118. newtFormAddHotKey(self, NEWT_KEY_LEFT);
  119. newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
  120. newtFormAddHotKey(self, 'Q');
  121. newtFormAddHotKey(self, 'q');
  122. newtFormAddHotKey(self, CTRL('c'));
  123. }
  124. static newtComponent newt_form__new(void)
  125. {
  126. newtComponent self = newtForm(NULL, NULL, 0);
  127. if (self)
  128. newt_form__set_exit_keys(self);
  129. return self;
  130. }
  131. static int popup_menu(int argc, char * const argv[])
  132. {
  133. struct newtExitStruct es;
  134. int i, rc = -1, max_len = 5;
  135. newtComponent listbox, form = newt_form__new();
  136. if (form == NULL)
  137. return -1;
  138. listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
  139. if (listbox == NULL)
  140. goto out_destroy_form;
  141. newtFormAddComponent(form, listbox);
  142. for (i = 0; i < argc; ++i) {
  143. int len = strlen(argv[i]);
  144. if (len > max_len)
  145. max_len = len;
  146. if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
  147. goto out_destroy_form;
  148. }
  149. newtCenteredWindow(max_len, argc, NULL);
  150. newtFormRun(form, &es);
  151. rc = newtListboxGetCurrent(listbox) - NULL;
  152. if (es.reason == NEWT_EXIT_HOTKEY)
  153. rc = -1;
  154. newtPopWindow();
  155. out_destroy_form:
  156. newtFormDestroy(form);
  157. return rc;
  158. }
  159. static int ui__help_window(const char *text)
  160. {
  161. struct newtExitStruct es;
  162. newtComponent tb, form = newt_form__new();
  163. int rc = -1;
  164. int max_len = 0, nr_lines = 0;
  165. const char *t;
  166. if (form == NULL)
  167. return -1;
  168. t = text;
  169. while (1) {
  170. const char *sep = strchr(t, '\n');
  171. int len;
  172. if (sep == NULL)
  173. sep = strchr(t, '\0');
  174. len = sep - t;
  175. if (max_len < len)
  176. max_len = len;
  177. ++nr_lines;
  178. if (*sep == '\0')
  179. break;
  180. t = sep + 1;
  181. }
  182. tb = newtTextbox(0, 0, max_len, nr_lines, 0);
  183. if (tb == NULL)
  184. goto out_destroy_form;
  185. newtTextboxSetText(tb, text);
  186. newtFormAddComponent(form, tb);
  187. newtCenteredWindow(max_len, nr_lines, NULL);
  188. newtFormRun(form, &es);
  189. newtPopWindow();
  190. rc = 0;
  191. out_destroy_form:
  192. newtFormDestroy(form);
  193. return rc;
  194. }
  195. static bool dialog_yesno(const char *msg)
  196. {
  197. /* newtWinChoice should really be accepting const char pointers... */
  198. char yes[] = "Yes", no[] = "No";
  199. return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
  200. }
  201. static void ui__error_window(const char *fmt, ...)
  202. {
  203. va_list ap;
  204. va_start(ap, fmt);
  205. newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
  206. va_end(ap);
  207. }
  208. #define HE_COLORSET_TOP 50
  209. #define HE_COLORSET_MEDIUM 51
  210. #define HE_COLORSET_NORMAL 52
  211. #define HE_COLORSET_SELECTED 53
  212. #define HE_COLORSET_CODE 54
  213. static int ui_browser__percent_color(double percent, bool current)
  214. {
  215. if (current)
  216. return HE_COLORSET_SELECTED;
  217. if (percent >= MIN_RED)
  218. return HE_COLORSET_TOP;
  219. if (percent >= MIN_GREEN)
  220. return HE_COLORSET_MEDIUM;
  221. return HE_COLORSET_NORMAL;
  222. }
  223. struct ui_browser {
  224. newtComponent form, sb;
  225. u64 index, first_visible_entry_idx;
  226. void *first_visible_entry, *entries;
  227. u16 top, left, width, height;
  228. void *priv;
  229. unsigned int (*refresh_entries)(struct ui_browser *self);
  230. void (*seek)(struct ui_browser *self,
  231. off_t offset, int whence);
  232. u32 nr_entries;
  233. };
  234. static void ui_browser__list_head_seek(struct ui_browser *self,
  235. off_t offset, int whence)
  236. {
  237. struct list_head *head = self->entries;
  238. struct list_head *pos;
  239. switch (whence) {
  240. case SEEK_SET:
  241. pos = head->next;
  242. break;
  243. case SEEK_CUR:
  244. pos = self->first_visible_entry;
  245. break;
  246. case SEEK_END:
  247. pos = head->prev;
  248. break;
  249. default:
  250. return;
  251. }
  252. if (offset > 0) {
  253. while (offset-- != 0)
  254. pos = pos->next;
  255. } else {
  256. while (offset++ != 0)
  257. pos = pos->prev;
  258. }
  259. self->first_visible_entry = pos;
  260. }
  261. static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
  262. {
  263. return (self->first_visible_entry_idx + row) == self->index;
  264. }
  265. static void ui_browser__refresh_dimensions(struct ui_browser *self)
  266. {
  267. int cols, rows;
  268. newtGetScreenSize(&cols, &rows);
  269. if (self->width > cols - 4)
  270. self->width = cols - 4;
  271. self->height = rows - 5;
  272. if (self->height > self->nr_entries)
  273. self->height = self->nr_entries;
  274. self->top = (rows - self->height) / 2;
  275. self->left = (cols - self->width) / 2;
  276. }
  277. static void ui_browser__reset_index(struct ui_browser *self)
  278. {
  279. self->index = self->first_visible_entry_idx = 0;
  280. self->seek(self, 0, SEEK_SET);
  281. }
  282. static int ui_browser__show(struct ui_browser *self, const char *title)
  283. {
  284. if (self->form != NULL)
  285. return 0;
  286. ui_browser__refresh_dimensions(self);
  287. newtCenteredWindow(self->width + 2, self->height, title);
  288. self->form = newt_form__new();
  289. if (self->form == NULL)
  290. return -1;
  291. self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
  292. HE_COLORSET_NORMAL,
  293. HE_COLORSET_SELECTED);
  294. if (self->sb == NULL)
  295. return -1;
  296. newtFormAddHotKey(self->form, NEWT_KEY_UP);
  297. newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
  298. newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
  299. newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
  300. newtFormAddHotKey(self->form, NEWT_KEY_HOME);
  301. newtFormAddHotKey(self->form, NEWT_KEY_END);
  302. newtFormAddComponent(self->form, self->sb);
  303. return 0;
  304. }
  305. static int objdump_line__show(struct objdump_line *self, struct list_head *head,
  306. int width, struct hist_entry *he, int len,
  307. bool current_entry)
  308. {
  309. if (self->offset != -1) {
  310. struct symbol *sym = he->ms.sym;
  311. unsigned int hits = 0;
  312. double percent = 0.0;
  313. int color;
  314. struct sym_priv *priv = symbol__priv(sym);
  315. struct sym_ext *sym_ext = priv->ext;
  316. struct sym_hist *h = priv->hist;
  317. s64 offset = self->offset;
  318. struct objdump_line *next = objdump__get_next_ip_line(head, self);
  319. while (offset < (s64)len &&
  320. (next == NULL || offset < next->offset)) {
  321. if (sym_ext) {
  322. percent += sym_ext[offset].percent;
  323. } else
  324. hits += h->ip[offset];
  325. ++offset;
  326. }
  327. if (sym_ext == NULL && h->sum)
  328. percent = 100.0 * hits / h->sum;
  329. color = ui_browser__percent_color(percent, current_entry);
  330. SLsmg_set_color(color);
  331. slsmg_printf(" %7.2f ", percent);
  332. if (!current_entry)
  333. SLsmg_set_color(HE_COLORSET_CODE);
  334. } else {
  335. int color = ui_browser__percent_color(0, current_entry);
  336. SLsmg_set_color(color);
  337. slsmg_write_nstring(" ", 9);
  338. }
  339. SLsmg_write_char(':');
  340. slsmg_write_nstring(" ", 8);
  341. if (!*self->line)
  342. slsmg_write_nstring(" ", width - 18);
  343. else
  344. slsmg_write_nstring(self->line, width - 18);
  345. return 0;
  346. }
  347. static int ui_browser__refresh_entries(struct ui_browser *self)
  348. {
  349. int row;
  350. newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
  351. row = self->refresh_entries(self);
  352. SLsmg_set_color(HE_COLORSET_NORMAL);
  353. SLsmg_fill_region(self->top + row, self->left,
  354. self->height - row, self->width, ' ');
  355. return 0;
  356. }
  357. static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
  358. {
  359. if (ui_browser__refresh_entries(self) < 0)
  360. return -1;
  361. while (1) {
  362. off_t offset;
  363. newtFormRun(self->form, es);
  364. if (es->reason != NEWT_EXIT_HOTKEY)
  365. break;
  366. if (is_exit_key(es->u.key))
  367. return es->u.key;
  368. switch (es->u.key) {
  369. case NEWT_KEY_DOWN:
  370. if (self->index == self->nr_entries - 1)
  371. break;
  372. ++self->index;
  373. if (self->index == self->first_visible_entry_idx + self->height) {
  374. ++self->first_visible_entry_idx;
  375. self->seek(self, +1, SEEK_CUR);
  376. }
  377. break;
  378. case NEWT_KEY_UP:
  379. if (self->index == 0)
  380. break;
  381. --self->index;
  382. if (self->index < self->first_visible_entry_idx) {
  383. --self->first_visible_entry_idx;
  384. self->seek(self, -1, SEEK_CUR);
  385. }
  386. break;
  387. case NEWT_KEY_PGDN:
  388. case ' ':
  389. if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
  390. break;
  391. offset = self->height;
  392. if (self->index + offset > self->nr_entries - 1)
  393. offset = self->nr_entries - 1 - self->index;
  394. self->index += offset;
  395. self->first_visible_entry_idx += offset;
  396. self->seek(self, +offset, SEEK_CUR);
  397. break;
  398. case NEWT_KEY_PGUP:
  399. if (self->first_visible_entry_idx == 0)
  400. break;
  401. if (self->first_visible_entry_idx < self->height)
  402. offset = self->first_visible_entry_idx;
  403. else
  404. offset = self->height;
  405. self->index -= offset;
  406. self->first_visible_entry_idx -= offset;
  407. self->seek(self, -offset, SEEK_CUR);
  408. break;
  409. case NEWT_KEY_HOME:
  410. ui_browser__reset_index(self);
  411. break;
  412. case NEWT_KEY_END:
  413. offset = self->height - 1;
  414. if (offset > self->nr_entries)
  415. offset = self->nr_entries;
  416. self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
  417. self->seek(self, -offset, SEEK_END);
  418. break;
  419. case NEWT_KEY_RIGHT:
  420. case NEWT_KEY_LEFT:
  421. case NEWT_KEY_TAB:
  422. return es->u.key;
  423. default:
  424. continue;
  425. }
  426. if (ui_browser__refresh_entries(self) < 0)
  427. return -1;
  428. }
  429. return 0;
  430. }
  431. /*
  432. * When debugging newt problems it was useful to be able to "unroll"
  433. * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
  434. * a source file with the sequence of calls to these methods, to then
  435. * tweak the arrays to get the intended results, so I'm keeping this code
  436. * here, may be useful again in the future.
  437. */
  438. #undef NEWT_DEBUG
  439. static void newt_checkbox_tree__add(newtComponent tree, const char *str,
  440. void *priv, int *indexes)
  441. {
  442. #ifdef NEWT_DEBUG
  443. /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
  444. int i = 0, len = 40 - strlen(str);
  445. fprintf(stderr,
  446. "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
  447. len, len, " ", str, priv);
  448. while (indexes[i] != NEWT_ARG_LAST) {
  449. if (indexes[i] != NEWT_ARG_APPEND)
  450. fprintf(stderr, " %d,", indexes[i]);
  451. else
  452. fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
  453. ++i;
  454. }
  455. fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
  456. fflush(stderr);
  457. #endif
  458. newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
  459. }
  460. static char *callchain_list__sym_name(struct callchain_list *self,
  461. char *bf, size_t bfsize)
  462. {
  463. if (self->ms.sym)
  464. return self->ms.sym->name;
  465. snprintf(bf, bfsize, "%#Lx", self->ip);
  466. return bf;
  467. }
  468. static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self)
  469. {
  470. struct objdump_line *pos;
  471. struct list_head *head = self->entries;
  472. struct hist_entry *he = self->priv;
  473. int row = 0;
  474. int len = he->ms.sym->end - he->ms.sym->start;
  475. if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
  476. self->first_visible_entry = head->next;
  477. pos = list_entry(self->first_visible_entry, struct objdump_line, node);
  478. list_for_each_entry_from(pos, head, node) {
  479. bool current_entry = ui_browser__is_current_entry(self, row);
  480. SLsmg_gotorc(self->top + row, self->left);
  481. objdump_line__show(pos, head, self->width,
  482. he, len, current_entry);
  483. if (++row == self->height)
  484. break;
  485. }
  486. return row;
  487. }
  488. static void __callchain__append_graph_browser(struct callchain_node *self,
  489. newtComponent tree, u64 total,
  490. int *indexes, int depth)
  491. {
  492. struct rb_node *node;
  493. u64 new_total, remaining;
  494. int idx = 0;
  495. if (callchain_param.mode == CHAIN_GRAPH_REL)
  496. new_total = self->children_hit;
  497. else
  498. new_total = total;
  499. remaining = new_total;
  500. node = rb_first(&self->rb_root);
  501. while (node) {
  502. struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
  503. struct rb_node *next = rb_next(node);
  504. u64 cumul = cumul_hits(child);
  505. struct callchain_list *chain;
  506. int first = true, printed = 0;
  507. int chain_idx = -1;
  508. remaining -= cumul;
  509. indexes[depth] = NEWT_ARG_APPEND;
  510. indexes[depth + 1] = NEWT_ARG_LAST;
  511. list_for_each_entry(chain, &child->val, list) {
  512. char ipstr[BITS_PER_LONG / 4 + 1],
  513. *alloc_str = NULL;
  514. const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
  515. if (first) {
  516. double percent = cumul * 100.0 / new_total;
  517. first = false;
  518. if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
  519. str = "Not enough memory!";
  520. else
  521. str = alloc_str;
  522. } else {
  523. indexes[depth] = idx;
  524. indexes[depth + 1] = NEWT_ARG_APPEND;
  525. indexes[depth + 2] = NEWT_ARG_LAST;
  526. ++chain_idx;
  527. }
  528. newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
  529. free(alloc_str);
  530. ++printed;
  531. }
  532. indexes[depth] = idx;
  533. if (chain_idx != -1)
  534. indexes[depth + 1] = chain_idx;
  535. if (printed != 0)
  536. ++idx;
  537. __callchain__append_graph_browser(child, tree, new_total, indexes,
  538. depth + (chain_idx != -1 ? 2 : 1));
  539. node = next;
  540. }
  541. }
  542. static void callchain__append_graph_browser(struct callchain_node *self,
  543. newtComponent tree, u64 total,
  544. int *indexes, int parent_idx)
  545. {
  546. struct callchain_list *chain;
  547. int i = 0;
  548. indexes[1] = NEWT_ARG_APPEND;
  549. indexes[2] = NEWT_ARG_LAST;
  550. list_for_each_entry(chain, &self->val, list) {
  551. char ipstr[BITS_PER_LONG / 4 + 1], *str;
  552. if (chain->ip >= PERF_CONTEXT_MAX)
  553. continue;
  554. if (!i++ && sort__first_dimension == SORT_SYM)
  555. continue;
  556. str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
  557. newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
  558. }
  559. indexes[1] = parent_idx;
  560. indexes[2] = NEWT_ARG_APPEND;
  561. indexes[3] = NEWT_ARG_LAST;
  562. __callchain__append_graph_browser(self, tree, total, indexes, 2);
  563. }
  564. static void hist_entry__append_callchain_browser(struct hist_entry *self,
  565. newtComponent tree, u64 total, int parent_idx)
  566. {
  567. struct rb_node *rb_node;
  568. int indexes[1024] = { [0] = parent_idx, };
  569. int idx = 0;
  570. struct callchain_node *chain;
  571. rb_node = rb_first(&self->sorted_chain);
  572. while (rb_node) {
  573. chain = rb_entry(rb_node, struct callchain_node, rb_node);
  574. switch (callchain_param.mode) {
  575. case CHAIN_FLAT:
  576. break;
  577. case CHAIN_GRAPH_ABS: /* falldown */
  578. case CHAIN_GRAPH_REL:
  579. callchain__append_graph_browser(chain, tree, total, indexes, idx++);
  580. break;
  581. case CHAIN_NONE:
  582. default:
  583. break;
  584. }
  585. rb_node = rb_next(rb_node);
  586. }
  587. }
  588. static size_t hist_entry__append_browser(struct hist_entry *self,
  589. newtComponent tree, u64 total)
  590. {
  591. char s[256];
  592. size_t ret;
  593. if (symbol_conf.exclude_other && !self->parent)
  594. return 0;
  595. ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
  596. false, 0, false, total);
  597. if (symbol_conf.use_callchain) {
  598. int indexes[2];
  599. indexes[0] = NEWT_ARG_APPEND;
  600. indexes[1] = NEWT_ARG_LAST;
  601. newt_checkbox_tree__add(tree, s, &self->ms, indexes);
  602. } else
  603. newtListboxAppendEntry(tree, s, &self->ms);
  604. return ret;
  605. }
  606. int hist_entry__tui_annotate(struct hist_entry *self)
  607. {
  608. struct ui_browser browser;
  609. struct newtExitStruct es;
  610. struct objdump_line *pos, *n;
  611. LIST_HEAD(head);
  612. int ret;
  613. if (self->ms.sym == NULL)
  614. return -1;
  615. if (self->ms.map->dso->annotate_warned)
  616. return -1;
  617. if (hist_entry__annotate(self, &head) < 0) {
  618. ui__error_window(browser__last_msg);
  619. return -1;
  620. }
  621. ui_helpline__push("Press <- or ESC to exit");
  622. memset(&browser, 0, sizeof(browser));
  623. browser.entries = &head;
  624. browser.refresh_entries = hist_entry__annotate_browser_refresh;
  625. browser.seek = ui_browser__list_head_seek;
  626. browser.priv = self;
  627. list_for_each_entry(pos, &head, node) {
  628. size_t line_len = strlen(pos->line);
  629. if (browser.width < line_len)
  630. browser.width = line_len;
  631. ++browser.nr_entries;
  632. }
  633. browser.width += 18; /* Percentage */
  634. ui_browser__show(&browser, self->ms.sym->name);
  635. ui_browser__run(&browser, &es);
  636. newtFormDestroy(browser.form);
  637. newtPopWindow();
  638. list_for_each_entry_safe(pos, n, &head, node) {
  639. list_del(&pos->node);
  640. objdump_line__free(pos);
  641. }
  642. ui_helpline__pop();
  643. return ret;
  644. }
  645. static const void *newt__symbol_tree_get_current(newtComponent self)
  646. {
  647. if (symbol_conf.use_callchain)
  648. return newtCheckboxTreeGetCurrent(self);
  649. return newtListboxGetCurrent(self);
  650. }
  651. static void hist_browser__selection(newtComponent self, void *data)
  652. {
  653. const struct map_symbol **symbol_ptr = data;
  654. *symbol_ptr = newt__symbol_tree_get_current(self);
  655. }
  656. struct hist_browser {
  657. newtComponent form, tree;
  658. const struct map_symbol *selection;
  659. };
  660. static struct hist_browser *hist_browser__new(void)
  661. {
  662. struct hist_browser *self = malloc(sizeof(*self));
  663. if (self != NULL)
  664. self->form = NULL;
  665. return self;
  666. }
  667. static void hist_browser__delete(struct hist_browser *self)
  668. {
  669. newtFormDestroy(self->form);
  670. newtPopWindow();
  671. free(self);
  672. }
  673. static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
  674. const char *title)
  675. {
  676. int max_len = 0, idx, cols, rows;
  677. struct ui_progress *progress;
  678. struct rb_node *nd;
  679. u64 curr_hist = 0;
  680. char seq[] = ".", unit;
  681. char str[256];
  682. unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
  683. if (self->form) {
  684. newtFormDestroy(self->form);
  685. newtPopWindow();
  686. }
  687. nr_events = convert_unit(nr_events, &unit);
  688. snprintf(str, sizeof(str), "Events: %lu%c ",
  689. nr_events, unit);
  690. newtDrawRootText(0, 0, str);
  691. newtGetScreenSize(NULL, &rows);
  692. if (symbol_conf.use_callchain)
  693. self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
  694. NEWT_FLAG_SCROLL);
  695. else
  696. self->tree = newtListbox(0, 0, rows - 5,
  697. (NEWT_FLAG_SCROLL |
  698. NEWT_FLAG_RETURNEXIT));
  699. newtComponentAddCallback(self->tree, hist_browser__selection,
  700. &self->selection);
  701. progress = ui_progress__new("Adding entries to the browser...",
  702. hists->nr_entries);
  703. if (progress == NULL)
  704. return -1;
  705. idx = 0;
  706. for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
  707. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  708. int len;
  709. if (h->filtered)
  710. continue;
  711. len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
  712. if (len > max_len)
  713. max_len = len;
  714. if (symbol_conf.use_callchain)
  715. hist_entry__append_callchain_browser(h, self->tree,
  716. hists->stats.total_period, idx++);
  717. ++curr_hist;
  718. if (curr_hist % 5)
  719. ui_progress__update(progress, curr_hist);
  720. }
  721. ui_progress__delete(progress);
  722. newtGetScreenSize(&cols, &rows);
  723. if (max_len > cols)
  724. max_len = cols - 3;
  725. if (!symbol_conf.use_callchain)
  726. newtListboxSetWidth(self->tree, max_len);
  727. newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
  728. rows - 5, title);
  729. self->form = newt_form__new();
  730. if (self->form == NULL)
  731. return -1;
  732. newtFormAddHotKey(self->form, 'A');
  733. newtFormAddHotKey(self->form, 'a');
  734. newtFormAddHotKey(self->form, 'D');
  735. newtFormAddHotKey(self->form, 'd');
  736. newtFormAddHotKey(self->form, 'T');
  737. newtFormAddHotKey(self->form, 't');
  738. newtFormAddHotKey(self->form, '?');
  739. newtFormAddHotKey(self->form, 'H');
  740. newtFormAddHotKey(self->form, 'h');
  741. newtFormAddHotKey(self->form, NEWT_KEY_F1);
  742. newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
  743. newtFormAddHotKey(self->form, NEWT_KEY_TAB);
  744. newtFormAddHotKey(self->form, NEWT_KEY_UNTAB);
  745. newtFormAddComponents(self->form, self->tree, NULL);
  746. self->selection = newt__symbol_tree_get_current(self->tree);
  747. return 0;
  748. }
  749. static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
  750. {
  751. int *indexes;
  752. if (!symbol_conf.use_callchain)
  753. goto out;
  754. indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
  755. if (indexes) {
  756. bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
  757. free(indexes);
  758. if (is_hist_entry)
  759. goto out;
  760. }
  761. return NULL;
  762. out:
  763. return container_of(self->selection, struct hist_entry, ms);
  764. }
  765. static struct thread *hist_browser__selected_thread(struct hist_browser *self)
  766. {
  767. struct hist_entry *he = hist_browser__selected_entry(self);
  768. return he ? he->thread : NULL;
  769. }
  770. static int hist_browser__title(char *bf, size_t size, const char *ev_name,
  771. const struct dso *dso, const struct thread *thread)
  772. {
  773. int printed = 0;
  774. if (thread)
  775. printed += snprintf(bf + printed, size - printed,
  776. "Thread: %s(%d)",
  777. (thread->comm_set ? thread->comm : ""),
  778. thread->pid);
  779. if (dso)
  780. printed += snprintf(bf + printed, size - printed,
  781. "%sDSO: %s", thread ? " " : "",
  782. dso->short_name);
  783. return printed ?: snprintf(bf, size, "Event: %s", ev_name);
  784. }
  785. int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
  786. {
  787. struct hist_browser *browser = hist_browser__new();
  788. struct pstack *fstack;
  789. const struct thread *thread_filter = NULL;
  790. const struct dso *dso_filter = NULL;
  791. struct newtExitStruct es;
  792. char msg[160];
  793. int key = -1;
  794. if (browser == NULL)
  795. return -1;
  796. fstack = pstack__new(2);
  797. if (fstack == NULL)
  798. goto out;
  799. ui_helpline__push(helpline);
  800. hist_browser__title(msg, sizeof(msg), ev_name,
  801. dso_filter, thread_filter);
  802. if (hist_browser__populate(browser, self, msg) < 0)
  803. goto out_free_stack;
  804. while (1) {
  805. const struct thread *thread;
  806. const struct dso *dso;
  807. char *options[16];
  808. int nr_options = 0, choice = 0, i,
  809. annotate = -2, zoom_dso = -2, zoom_thread = -2;
  810. newtFormRun(browser->form, &es);
  811. thread = hist_browser__selected_thread(browser);
  812. dso = browser->selection->map ? browser->selection->map->dso : NULL;
  813. if (es.reason == NEWT_EXIT_HOTKEY) {
  814. key = es.u.key;
  815. switch (key) {
  816. case NEWT_KEY_F1:
  817. goto do_help;
  818. case NEWT_KEY_TAB:
  819. case NEWT_KEY_UNTAB:
  820. /*
  821. * Exit the browser, let hists__browser_tree
  822. * go to the next or previous
  823. */
  824. goto out_free_stack;
  825. default:;
  826. }
  827. key = toupper(key);
  828. switch (key) {
  829. case 'A':
  830. if (browser->selection->map == NULL &&
  831. browser->selection->map->dso->annotate_warned)
  832. continue;
  833. goto do_annotate;
  834. case 'D':
  835. goto zoom_dso;
  836. case 'T':
  837. goto zoom_thread;
  838. case 'H':
  839. case '?':
  840. do_help:
  841. ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
  842. "<- Zoom out\n"
  843. "a Annotate current symbol\n"
  844. "h/?/F1 Show this window\n"
  845. "d Zoom into current DSO\n"
  846. "t Zoom into current Thread\n"
  847. "q/CTRL+C Exit browser");
  848. continue;
  849. default:;
  850. }
  851. if (is_exit_key(key)) {
  852. if (key == NEWT_KEY_ESCAPE) {
  853. if (dialog_yesno("Do you really want to exit?"))
  854. break;
  855. else
  856. continue;
  857. } else
  858. break;
  859. }
  860. if (es.u.key == NEWT_KEY_LEFT) {
  861. const void *top;
  862. if (pstack__empty(fstack))
  863. continue;
  864. top = pstack__pop(fstack);
  865. if (top == &dso_filter)
  866. goto zoom_out_dso;
  867. if (top == &thread_filter)
  868. goto zoom_out_thread;
  869. continue;
  870. }
  871. }
  872. if (browser->selection->sym != NULL &&
  873. !browser->selection->map->dso->annotate_warned &&
  874. asprintf(&options[nr_options], "Annotate %s",
  875. browser->selection->sym->name) > 0)
  876. annotate = nr_options++;
  877. if (thread != NULL &&
  878. asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
  879. (thread_filter ? "out of" : "into"),
  880. (thread->comm_set ? thread->comm : ""),
  881. thread->pid) > 0)
  882. zoom_thread = nr_options++;
  883. if (dso != NULL &&
  884. asprintf(&options[nr_options], "Zoom %s %s DSO",
  885. (dso_filter ? "out of" : "into"),
  886. (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
  887. zoom_dso = nr_options++;
  888. options[nr_options++] = (char *)"Exit";
  889. choice = popup_menu(nr_options, options);
  890. for (i = 0; i < nr_options - 1; ++i)
  891. free(options[i]);
  892. if (choice == nr_options - 1)
  893. break;
  894. if (choice == -1)
  895. continue;
  896. if (choice == annotate) {
  897. struct hist_entry *he;
  898. do_annotate:
  899. if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
  900. browser->selection->map->dso->annotate_warned = 1;
  901. ui_helpline__puts("No vmlinux file found, can't "
  902. "annotate with just a "
  903. "kallsyms file");
  904. continue;
  905. }
  906. he = hist_browser__selected_entry(browser);
  907. if (he == NULL)
  908. continue;
  909. hist_entry__tui_annotate(he);
  910. } else if (choice == zoom_dso) {
  911. zoom_dso:
  912. if (dso_filter) {
  913. pstack__remove(fstack, &dso_filter);
  914. zoom_out_dso:
  915. ui_helpline__pop();
  916. dso_filter = NULL;
  917. } else {
  918. if (dso == NULL)
  919. continue;
  920. ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
  921. dso->kernel ? "the Kernel" : dso->short_name);
  922. dso_filter = dso;
  923. pstack__push(fstack, &dso_filter);
  924. }
  925. hists__filter_by_dso(self, dso_filter);
  926. hist_browser__title(msg, sizeof(msg), ev_name,
  927. dso_filter, thread_filter);
  928. if (hist_browser__populate(browser, self, msg) < 0)
  929. goto out;
  930. } else if (choice == zoom_thread) {
  931. zoom_thread:
  932. if (thread_filter) {
  933. pstack__remove(fstack, &thread_filter);
  934. zoom_out_thread:
  935. ui_helpline__pop();
  936. thread_filter = NULL;
  937. } else {
  938. ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
  939. thread->comm_set ? thread->comm : "",
  940. thread->pid);
  941. thread_filter = thread;
  942. pstack__push(fstack, &thread_filter);
  943. }
  944. hists__filter_by_thread(self, thread_filter);
  945. hist_browser__title(msg, sizeof(msg), ev_name,
  946. dso_filter, thread_filter);
  947. if (hist_browser__populate(browser, self, msg) < 0)
  948. goto out;
  949. }
  950. }
  951. out_free_stack:
  952. pstack__delete(fstack);
  953. out:
  954. hist_browser__delete(browser);
  955. return key;
  956. }
  957. int hists__tui_browse_tree(struct rb_root *self, const char *help)
  958. {
  959. struct rb_node *first = rb_first(self), *nd = first, *next;
  960. int key = 0;
  961. while (nd) {
  962. struct hists *hists = rb_entry(nd, struct hists, rb_node);
  963. const char *ev_name = __event_name(hists->type, hists->config);
  964. key = hists__browse(hists, help, ev_name);
  965. if (is_exit_key(key))
  966. break;
  967. switch (key) {
  968. case NEWT_KEY_TAB:
  969. next = rb_next(nd);
  970. if (next)
  971. nd = next;
  972. break;
  973. case NEWT_KEY_UNTAB:
  974. if (nd == first)
  975. continue;
  976. nd = rb_prev(nd);
  977. default:
  978. break;
  979. }
  980. }
  981. return key;
  982. }
  983. static struct newtPercentTreeColors {
  984. const char *topColorFg, *topColorBg;
  985. const char *mediumColorFg, *mediumColorBg;
  986. const char *normalColorFg, *normalColorBg;
  987. const char *selColorFg, *selColorBg;
  988. const char *codeColorFg, *codeColorBg;
  989. } defaultPercentTreeColors = {
  990. "red", "lightgray",
  991. "green", "lightgray",
  992. "black", "lightgray",
  993. "lightgray", "magenta",
  994. "blue", "lightgray",
  995. };
  996. void setup_browser(void)
  997. {
  998. struct newtPercentTreeColors *c = &defaultPercentTreeColors;
  999. if (!isatty(1) || !use_browser || dump_trace) {
  1000. use_browser = 0;
  1001. setup_pager();
  1002. return;
  1003. }
  1004. use_browser = 1;
  1005. newtInit();
  1006. newtCls();
  1007. ui_helpline__puts(" ");
  1008. sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
  1009. sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
  1010. sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
  1011. sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
  1012. sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
  1013. }
  1014. void exit_browser(bool wait_for_ok)
  1015. {
  1016. if (use_browser > 0) {
  1017. if (wait_for_ok) {
  1018. char title[] = "Fatal Error", ok[] = "Ok";
  1019. newtWinMessage(title, ok, browser__last_msg);
  1020. }
  1021. newtFinished();
  1022. }
  1023. }