hists.c 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <linux/rbtree.h>
  5. #include "../../util/evsel.h"
  6. #include "../../util/evlist.h"
  7. #include "../../util/hist.h"
  8. #include "../../util/pstack.h"
  9. #include "../../util/sort.h"
  10. #include "../../util/util.h"
  11. #include "../../util/top.h"
  12. #include "../../arch/common.h"
  13. #include "../browser.h"
  14. #include "../helpline.h"
  15. #include "../util.h"
  16. #include "../ui.h"
  17. #include "map.h"
  18. #include "annotate.h"
  19. struct hist_browser {
  20. struct ui_browser b;
  21. struct hists *hists;
  22. struct hist_entry *he_selection;
  23. struct map_symbol *selection;
  24. struct hist_browser_timer *hbt;
  25. struct pstack *pstack;
  26. struct perf_env *env;
  27. int print_seq;
  28. bool show_dso;
  29. bool show_headers;
  30. float min_pcnt;
  31. u64 nr_non_filtered_entries;
  32. u64 nr_callchain_rows;
  33. };
  34. extern void hist_browser__init_hpp(void);
  35. static int hists__browser_title(struct hists *hists,
  36. struct hist_browser_timer *hbt,
  37. char *bf, size_t size);
  38. static void hist_browser__update_nr_entries(struct hist_browser *hb);
  39. static struct rb_node *hists__filter_entries(struct rb_node *nd,
  40. float min_pcnt);
  41. static bool hist_browser__has_filter(struct hist_browser *hb)
  42. {
  43. return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
  44. }
  45. static int hist_browser__get_folding(struct hist_browser *browser)
  46. {
  47. struct rb_node *nd;
  48. struct hists *hists = browser->hists;
  49. int unfolded_rows = 0;
  50. for (nd = rb_first(&hists->entries);
  51. (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
  52. nd = rb_next(nd)) {
  53. struct hist_entry *he =
  54. rb_entry(nd, struct hist_entry, rb_node);
  55. if (he->unfolded)
  56. unfolded_rows += he->nr_rows;
  57. }
  58. return unfolded_rows;
  59. }
  60. static u32 hist_browser__nr_entries(struct hist_browser *hb)
  61. {
  62. u32 nr_entries;
  63. if (hist_browser__has_filter(hb))
  64. nr_entries = hb->nr_non_filtered_entries;
  65. else
  66. nr_entries = hb->hists->nr_entries;
  67. hb->nr_callchain_rows = hist_browser__get_folding(hb);
  68. return nr_entries + hb->nr_callchain_rows;
  69. }
  70. static void hist_browser__update_rows(struct hist_browser *hb)
  71. {
  72. struct ui_browser *browser = &hb->b;
  73. u16 header_offset = hb->show_headers ? 1 : 0, index_row;
  74. browser->rows = browser->height - header_offset;
  75. /*
  76. * Verify if we were at the last line and that line isn't
  77. * visibe because we now show the header line(s).
  78. */
  79. index_row = browser->index - browser->top_idx;
  80. if (index_row >= browser->rows)
  81. browser->index -= index_row - browser->rows + 1;
  82. }
  83. static void hist_browser__refresh_dimensions(struct ui_browser *browser)
  84. {
  85. struct hist_browser *hb = container_of(browser, struct hist_browser, b);
  86. /* 3 == +/- toggle symbol before actual hist_entry rendering */
  87. browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
  88. /*
  89. * FIXME: Just keeping existing behaviour, but this really should be
  90. * before updating browser->width, as it will invalidate the
  91. * calculation above. Fix this and the fallout in another
  92. * changeset.
  93. */
  94. ui_browser__refresh_dimensions(browser);
  95. hist_browser__update_rows(hb);
  96. }
  97. static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
  98. {
  99. u16 header_offset = browser->show_headers ? 1 : 0;
  100. ui_browser__gotorc(&browser->b, row + header_offset, column);
  101. }
  102. static void hist_browser__reset(struct hist_browser *browser)
  103. {
  104. /*
  105. * The hists__remove_entry_filter() already folds non-filtered
  106. * entries so we can assume it has 0 callchain rows.
  107. */
  108. browser->nr_callchain_rows = 0;
  109. hist_browser__update_nr_entries(browser);
  110. browser->b.nr_entries = hist_browser__nr_entries(browser);
  111. hist_browser__refresh_dimensions(&browser->b);
  112. ui_browser__reset_index(&browser->b);
  113. }
  114. static char tree__folded_sign(bool unfolded)
  115. {
  116. return unfolded ? '-' : '+';
  117. }
  118. static char hist_entry__folded(const struct hist_entry *he)
  119. {
  120. return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
  121. }
  122. static char callchain_list__folded(const struct callchain_list *cl)
  123. {
  124. return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
  125. }
  126. static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
  127. {
  128. cl->unfolded = unfold ? cl->has_children : false;
  129. }
  130. static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
  131. {
  132. int n = 0;
  133. struct rb_node *nd;
  134. for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
  135. struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
  136. struct callchain_list *chain;
  137. char folded_sign = ' '; /* No children */
  138. list_for_each_entry(chain, &child->val, list) {
  139. ++n;
  140. /* We need this because we may not have children */
  141. folded_sign = callchain_list__folded(chain);
  142. if (folded_sign == '+')
  143. break;
  144. }
  145. if (folded_sign == '-') /* Have children and they're unfolded */
  146. n += callchain_node__count_rows_rb_tree(child);
  147. }
  148. return n;
  149. }
  150. static int callchain_node__count_rows(struct callchain_node *node)
  151. {
  152. struct callchain_list *chain;
  153. bool unfolded = false;
  154. int n = 0;
  155. list_for_each_entry(chain, &node->val, list) {
  156. ++n;
  157. unfolded = chain->unfolded;
  158. }
  159. if (unfolded)
  160. n += callchain_node__count_rows_rb_tree(node);
  161. return n;
  162. }
  163. static int callchain__count_rows(struct rb_root *chain)
  164. {
  165. struct rb_node *nd;
  166. int n = 0;
  167. for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
  168. struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
  169. n += callchain_node__count_rows(node);
  170. }
  171. return n;
  172. }
  173. static bool hist_entry__toggle_fold(struct hist_entry *he)
  174. {
  175. if (!he)
  176. return false;
  177. if (!he->has_children)
  178. return false;
  179. he->unfolded = !he->unfolded;
  180. return true;
  181. }
  182. static bool callchain_list__toggle_fold(struct callchain_list *cl)
  183. {
  184. if (!cl)
  185. return false;
  186. if (!cl->has_children)
  187. return false;
  188. cl->unfolded = !cl->unfolded;
  189. return true;
  190. }
  191. static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
  192. {
  193. struct rb_node *nd = rb_first(&node->rb_root);
  194. for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
  195. struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
  196. struct callchain_list *chain;
  197. bool first = true;
  198. list_for_each_entry(chain, &child->val, list) {
  199. if (first) {
  200. first = false;
  201. chain->has_children = chain->list.next != &child->val ||
  202. !RB_EMPTY_ROOT(&child->rb_root);
  203. } else
  204. chain->has_children = chain->list.next == &child->val &&
  205. !RB_EMPTY_ROOT(&child->rb_root);
  206. }
  207. callchain_node__init_have_children_rb_tree(child);
  208. }
  209. }
  210. static void callchain_node__init_have_children(struct callchain_node *node,
  211. bool has_sibling)
  212. {
  213. struct callchain_list *chain;
  214. chain = list_entry(node->val.next, struct callchain_list, list);
  215. chain->has_children = has_sibling;
  216. if (!list_empty(&node->val)) {
  217. chain = list_entry(node->val.prev, struct callchain_list, list);
  218. chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
  219. }
  220. callchain_node__init_have_children_rb_tree(node);
  221. }
  222. static void callchain__init_have_children(struct rb_root *root)
  223. {
  224. struct rb_node *nd = rb_first(root);
  225. bool has_sibling = nd && rb_next(nd);
  226. for (nd = rb_first(root); nd; nd = rb_next(nd)) {
  227. struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
  228. callchain_node__init_have_children(node, has_sibling);
  229. }
  230. }
  231. static void hist_entry__init_have_children(struct hist_entry *he)
  232. {
  233. if (!he->init_have_children) {
  234. he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
  235. callchain__init_have_children(&he->sorted_chain);
  236. he->init_have_children = true;
  237. }
  238. }
  239. static bool hist_browser__toggle_fold(struct hist_browser *browser)
  240. {
  241. struct hist_entry *he = browser->he_selection;
  242. struct map_symbol *ms = browser->selection;
  243. struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
  244. bool has_children;
  245. if (ms == &he->ms)
  246. has_children = hist_entry__toggle_fold(he);
  247. else
  248. has_children = callchain_list__toggle_fold(cl);
  249. if (has_children) {
  250. hist_entry__init_have_children(he);
  251. browser->b.nr_entries -= he->nr_rows;
  252. browser->nr_callchain_rows -= he->nr_rows;
  253. if (he->unfolded)
  254. he->nr_rows = callchain__count_rows(&he->sorted_chain);
  255. else
  256. he->nr_rows = 0;
  257. browser->b.nr_entries += he->nr_rows;
  258. browser->nr_callchain_rows += he->nr_rows;
  259. return true;
  260. }
  261. /* If it doesn't have children, no toggling performed */
  262. return false;
  263. }
  264. static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
  265. {
  266. int n = 0;
  267. struct rb_node *nd;
  268. for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
  269. struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
  270. struct callchain_list *chain;
  271. bool has_children = false;
  272. list_for_each_entry(chain, &child->val, list) {
  273. ++n;
  274. callchain_list__set_folding(chain, unfold);
  275. has_children = chain->has_children;
  276. }
  277. if (has_children)
  278. n += callchain_node__set_folding_rb_tree(child, unfold);
  279. }
  280. return n;
  281. }
  282. static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
  283. {
  284. struct callchain_list *chain;
  285. bool has_children = false;
  286. int n = 0;
  287. list_for_each_entry(chain, &node->val, list) {
  288. ++n;
  289. callchain_list__set_folding(chain, unfold);
  290. has_children = chain->has_children;
  291. }
  292. if (has_children)
  293. n += callchain_node__set_folding_rb_tree(node, unfold);
  294. return n;
  295. }
  296. static int callchain__set_folding(struct rb_root *chain, bool unfold)
  297. {
  298. struct rb_node *nd;
  299. int n = 0;
  300. for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
  301. struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
  302. n += callchain_node__set_folding(node, unfold);
  303. }
  304. return n;
  305. }
  306. static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
  307. {
  308. hist_entry__init_have_children(he);
  309. he->unfolded = unfold ? he->has_children : false;
  310. if (he->has_children) {
  311. int n = callchain__set_folding(&he->sorted_chain, unfold);
  312. he->nr_rows = unfold ? n : 0;
  313. } else
  314. he->nr_rows = 0;
  315. }
  316. static void
  317. __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
  318. {
  319. struct rb_node *nd;
  320. struct hists *hists = browser->hists;
  321. for (nd = rb_first(&hists->entries);
  322. (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
  323. nd = rb_next(nd)) {
  324. struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
  325. hist_entry__set_folding(he, unfold);
  326. browser->nr_callchain_rows += he->nr_rows;
  327. }
  328. }
  329. static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
  330. {
  331. browser->nr_callchain_rows = 0;
  332. __hist_browser__set_folding(browser, unfold);
  333. browser->b.nr_entries = hist_browser__nr_entries(browser);
  334. /* Go to the start, we may be way after valid entries after a collapse */
  335. ui_browser__reset_index(&browser->b);
  336. }
  337. static void ui_browser__warn_lost_events(struct ui_browser *browser)
  338. {
  339. ui_browser__warning(browser, 4,
  340. "Events are being lost, check IO/CPU overload!\n\n"
  341. "You may want to run 'perf' using a RT scheduler policy:\n\n"
  342. " perf top -r 80\n\n"
  343. "Or reduce the sampling frequency.");
  344. }
  345. static int hist_browser__run(struct hist_browser *browser, const char *help)
  346. {
  347. int key;
  348. char title[160];
  349. struct hist_browser_timer *hbt = browser->hbt;
  350. int delay_secs = hbt ? hbt->refresh : 0;
  351. browser->b.entries = &browser->hists->entries;
  352. browser->b.nr_entries = hist_browser__nr_entries(browser);
  353. hists__browser_title(browser->hists, hbt, title, sizeof(title));
  354. if (ui_browser__show(&browser->b, title, help) < 0)
  355. return -1;
  356. while (1) {
  357. key = ui_browser__run(&browser->b, delay_secs);
  358. switch (key) {
  359. case K_TIMER: {
  360. u64 nr_entries;
  361. hbt->timer(hbt->arg);
  362. if (hist_browser__has_filter(browser))
  363. hist_browser__update_nr_entries(browser);
  364. nr_entries = hist_browser__nr_entries(browser);
  365. ui_browser__update_nr_entries(&browser->b, nr_entries);
  366. if (browser->hists->stats.nr_lost_warned !=
  367. browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
  368. browser->hists->stats.nr_lost_warned =
  369. browser->hists->stats.nr_events[PERF_RECORD_LOST];
  370. ui_browser__warn_lost_events(&browser->b);
  371. }
  372. hists__browser_title(browser->hists,
  373. hbt, title, sizeof(title));
  374. ui_browser__show_title(&browser->b, title);
  375. continue;
  376. }
  377. case 'D': { /* Debug */
  378. static int seq;
  379. struct hist_entry *h = rb_entry(browser->b.top,
  380. struct hist_entry, rb_node);
  381. ui_helpline__pop();
  382. ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
  383. seq++, browser->b.nr_entries,
  384. browser->hists->nr_entries,
  385. browser->b.rows,
  386. browser->b.index,
  387. browser->b.top_idx,
  388. h->row_offset, h->nr_rows);
  389. }
  390. break;
  391. case 'C':
  392. /* Collapse the whole world. */
  393. hist_browser__set_folding(browser, false);
  394. break;
  395. case 'E':
  396. /* Expand the whole world. */
  397. hist_browser__set_folding(browser, true);
  398. break;
  399. case 'H':
  400. browser->show_headers = !browser->show_headers;
  401. hist_browser__update_rows(browser);
  402. break;
  403. case K_ENTER:
  404. if (hist_browser__toggle_fold(browser))
  405. break;
  406. /* fall thru */
  407. default:
  408. goto out;
  409. }
  410. }
  411. out:
  412. ui_browser__hide(&browser->b);
  413. return key;
  414. }
  415. struct callchain_print_arg {
  416. /* for hists browser */
  417. off_t row_offset;
  418. bool is_current_entry;
  419. /* for file dump */
  420. FILE *fp;
  421. int printed;
  422. };
  423. typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
  424. struct callchain_list *chain,
  425. const char *str, int offset,
  426. unsigned short row,
  427. struct callchain_print_arg *arg);
  428. static void hist_browser__show_callchain_entry(struct hist_browser *browser,
  429. struct callchain_list *chain,
  430. const char *str, int offset,
  431. unsigned short row,
  432. struct callchain_print_arg *arg)
  433. {
  434. int color, width;
  435. char folded_sign = callchain_list__folded(chain);
  436. bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
  437. color = HE_COLORSET_NORMAL;
  438. width = browser->b.width - (offset + 2);
  439. if (ui_browser__is_current_entry(&browser->b, row)) {
  440. browser->selection = &chain->ms;
  441. color = HE_COLORSET_SELECTED;
  442. arg->is_current_entry = true;
  443. }
  444. ui_browser__set_color(&browser->b, color);
  445. hist_browser__gotorc(browser, row, 0);
  446. ui_browser__write_nstring(&browser->b, " ", offset);
  447. ui_browser__printf(&browser->b, "%c", folded_sign);
  448. ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
  449. ui_browser__write_nstring(&browser->b, str, width);
  450. }
  451. static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
  452. struct callchain_list *chain,
  453. const char *str, int offset,
  454. unsigned short row __maybe_unused,
  455. struct callchain_print_arg *arg)
  456. {
  457. char folded_sign = callchain_list__folded(chain);
  458. arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
  459. folded_sign, str);
  460. }
  461. typedef bool (*check_output_full_fn)(struct hist_browser *browser,
  462. unsigned short row);
  463. static bool hist_browser__check_output_full(struct hist_browser *browser,
  464. unsigned short row)
  465. {
  466. return browser->b.rows == row;
  467. }
  468. static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
  469. unsigned short row __maybe_unused)
  470. {
  471. return false;
  472. }
  473. #define LEVEL_OFFSET_STEP 3
  474. static int hist_browser__show_callchain(struct hist_browser *browser,
  475. struct rb_root *root, int level,
  476. unsigned short row, u64 total,
  477. print_callchain_entry_fn print,
  478. struct callchain_print_arg *arg,
  479. check_output_full_fn is_output_full)
  480. {
  481. struct rb_node *node;
  482. int first_row = row, offset = level * LEVEL_OFFSET_STEP;
  483. u64 new_total;
  484. bool need_percent;
  485. node = rb_first(root);
  486. need_percent = node && rb_next(node);
  487. while (node) {
  488. struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
  489. struct rb_node *next = rb_next(node);
  490. u64 cumul = callchain_cumul_hits(child);
  491. struct callchain_list *chain;
  492. char folded_sign = ' ';
  493. int first = true;
  494. int extra_offset = 0;
  495. list_for_each_entry(chain, &child->val, list) {
  496. char bf[1024], *alloc_str;
  497. const char *str;
  498. bool was_first = first;
  499. if (first)
  500. first = false;
  501. else if (need_percent)
  502. extra_offset = LEVEL_OFFSET_STEP;
  503. folded_sign = callchain_list__folded(chain);
  504. if (arg->row_offset != 0) {
  505. arg->row_offset--;
  506. goto do_next;
  507. }
  508. alloc_str = NULL;
  509. str = callchain_list__sym_name(chain, bf, sizeof(bf),
  510. browser->show_dso);
  511. if (was_first && need_percent) {
  512. double percent = cumul * 100.0 / total;
  513. if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
  514. str = "Not enough memory!";
  515. else
  516. str = alloc_str;
  517. }
  518. print(browser, chain, str, offset + extra_offset, row, arg);
  519. free(alloc_str);
  520. if (is_output_full(browser, ++row))
  521. goto out;
  522. do_next:
  523. if (folded_sign == '+')
  524. break;
  525. }
  526. if (folded_sign == '-') {
  527. const int new_level = level + (extra_offset ? 2 : 1);
  528. if (callchain_param.mode == CHAIN_GRAPH_REL)
  529. new_total = child->children_hit;
  530. else
  531. new_total = total;
  532. row += hist_browser__show_callchain(browser, &child->rb_root,
  533. new_level, row, new_total,
  534. print, arg, is_output_full);
  535. }
  536. if (is_output_full(browser, row))
  537. break;
  538. node = next;
  539. }
  540. out:
  541. return row - first_row;
  542. }
  543. struct hpp_arg {
  544. struct ui_browser *b;
  545. char folded_sign;
  546. bool current_entry;
  547. };
  548. static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
  549. {
  550. struct hpp_arg *arg = hpp->ptr;
  551. int ret, len;
  552. va_list args;
  553. double percent;
  554. va_start(args, fmt);
  555. len = va_arg(args, int);
  556. percent = va_arg(args, double);
  557. va_end(args);
  558. ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
  559. ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
  560. ui_browser__printf(arg->b, "%s", hpp->buf);
  561. advance_hpp(hpp, ret);
  562. return ret;
  563. }
  564. #define __HPP_COLOR_PERCENT_FN(_type, _field) \
  565. static u64 __hpp_get_##_field(struct hist_entry *he) \
  566. { \
  567. return he->stat._field; \
  568. } \
  569. \
  570. static int \
  571. hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
  572. struct perf_hpp *hpp, \
  573. struct hist_entry *he) \
  574. { \
  575. return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
  576. __hpp__slsmg_color_printf, true); \
  577. }
  578. #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
  579. static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
  580. { \
  581. return he->stat_acc->_field; \
  582. } \
  583. \
  584. static int \
  585. hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
  586. struct perf_hpp *hpp, \
  587. struct hist_entry *he) \
  588. { \
  589. if (!symbol_conf.cumulate_callchain) { \
  590. struct hpp_arg *arg = hpp->ptr; \
  591. int len = fmt->user_len ?: fmt->len; \
  592. int ret = scnprintf(hpp->buf, hpp->size, \
  593. "%*s", len, "N/A"); \
  594. ui_browser__printf(arg->b, "%s", hpp->buf); \
  595. \
  596. return ret; \
  597. } \
  598. return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
  599. " %*.2f%%", __hpp__slsmg_color_printf, true); \
  600. }
  601. __HPP_COLOR_PERCENT_FN(overhead, period)
  602. __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
  603. __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
  604. __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
  605. __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
  606. __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
  607. #undef __HPP_COLOR_PERCENT_FN
  608. #undef __HPP_COLOR_ACC_PERCENT_FN
  609. void hist_browser__init_hpp(void)
  610. {
  611. perf_hpp__format[PERF_HPP__OVERHEAD].color =
  612. hist_browser__hpp_color_overhead;
  613. perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
  614. hist_browser__hpp_color_overhead_sys;
  615. perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
  616. hist_browser__hpp_color_overhead_us;
  617. perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
  618. hist_browser__hpp_color_overhead_guest_sys;
  619. perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
  620. hist_browser__hpp_color_overhead_guest_us;
  621. perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
  622. hist_browser__hpp_color_overhead_acc;
  623. }
  624. static int hist_browser__show_entry(struct hist_browser *browser,
  625. struct hist_entry *entry,
  626. unsigned short row)
  627. {
  628. char s[256];
  629. int printed = 0;
  630. int width = browser->b.width;
  631. char folded_sign = ' ';
  632. bool current_entry = ui_browser__is_current_entry(&browser->b, row);
  633. off_t row_offset = entry->row_offset;
  634. bool first = true;
  635. struct perf_hpp_fmt *fmt;
  636. if (current_entry) {
  637. browser->he_selection = entry;
  638. browser->selection = &entry->ms;
  639. }
  640. if (symbol_conf.use_callchain) {
  641. hist_entry__init_have_children(entry);
  642. folded_sign = hist_entry__folded(entry);
  643. }
  644. if (row_offset == 0) {
  645. struct hpp_arg arg = {
  646. .b = &browser->b,
  647. .folded_sign = folded_sign,
  648. .current_entry = current_entry,
  649. };
  650. struct perf_hpp hpp = {
  651. .buf = s,
  652. .size = sizeof(s),
  653. .ptr = &arg,
  654. };
  655. hist_browser__gotorc(browser, row, 0);
  656. perf_hpp__for_each_format(fmt) {
  657. if (perf_hpp__should_skip(fmt))
  658. continue;
  659. if (current_entry && browser->b.navkeypressed) {
  660. ui_browser__set_color(&browser->b,
  661. HE_COLORSET_SELECTED);
  662. } else {
  663. ui_browser__set_color(&browser->b,
  664. HE_COLORSET_NORMAL);
  665. }
  666. if (first) {
  667. if (symbol_conf.use_callchain) {
  668. ui_browser__printf(&browser->b, "%c ", folded_sign);
  669. width -= 2;
  670. }
  671. first = false;
  672. } else {
  673. ui_browser__printf(&browser->b, " ");
  674. width -= 2;
  675. }
  676. if (fmt->color) {
  677. width -= fmt->color(fmt, &hpp, entry);
  678. } else {
  679. width -= fmt->entry(fmt, &hpp, entry);
  680. ui_browser__printf(&browser->b, "%s", s);
  681. }
  682. }
  683. /* The scroll bar isn't being used */
  684. if (!browser->b.navkeypressed)
  685. width += 1;
  686. ui_browser__write_nstring(&browser->b, "", width);
  687. ++row;
  688. ++printed;
  689. } else
  690. --row_offset;
  691. if (folded_sign == '-' && row != browser->b.rows) {
  692. u64 total = hists__total_period(entry->hists);
  693. struct callchain_print_arg arg = {
  694. .row_offset = row_offset,
  695. .is_current_entry = current_entry,
  696. };
  697. if (callchain_param.mode == CHAIN_GRAPH_REL) {
  698. if (symbol_conf.cumulate_callchain)
  699. total = entry->stat_acc->period;
  700. else
  701. total = entry->stat.period;
  702. }
  703. printed += hist_browser__show_callchain(browser,
  704. &entry->sorted_chain, 1, row, total,
  705. hist_browser__show_callchain_entry, &arg,
  706. hist_browser__check_output_full);
  707. if (arg.is_current_entry)
  708. browser->he_selection = entry;
  709. }
  710. return printed;
  711. }
  712. static int advance_hpp_check(struct perf_hpp *hpp, int inc)
  713. {
  714. advance_hpp(hpp, inc);
  715. return hpp->size <= 0;
  716. }
  717. static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
  718. {
  719. struct perf_hpp dummy_hpp = {
  720. .buf = buf,
  721. .size = size,
  722. };
  723. struct perf_hpp_fmt *fmt;
  724. size_t ret = 0;
  725. if (symbol_conf.use_callchain) {
  726. ret = scnprintf(buf, size, " ");
  727. if (advance_hpp_check(&dummy_hpp, ret))
  728. return ret;
  729. }
  730. perf_hpp__for_each_format(fmt) {
  731. if (perf_hpp__should_skip(fmt))
  732. continue;
  733. ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
  734. if (advance_hpp_check(&dummy_hpp, ret))
  735. break;
  736. ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
  737. if (advance_hpp_check(&dummy_hpp, ret))
  738. break;
  739. }
  740. return ret;
  741. }
  742. static void hist_browser__show_headers(struct hist_browser *browser)
  743. {
  744. char headers[1024];
  745. hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
  746. ui_browser__gotorc(&browser->b, 0, 0);
  747. ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
  748. ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
  749. }
  750. static void ui_browser__hists_init_top(struct ui_browser *browser)
  751. {
  752. if (browser->top == NULL) {
  753. struct hist_browser *hb;
  754. hb = container_of(browser, struct hist_browser, b);
  755. browser->top = rb_first(&hb->hists->entries);
  756. }
  757. }
  758. static unsigned int hist_browser__refresh(struct ui_browser *browser)
  759. {
  760. unsigned row = 0;
  761. u16 header_offset = 0;
  762. struct rb_node *nd;
  763. struct hist_browser *hb = container_of(browser, struct hist_browser, b);
  764. if (hb->show_headers) {
  765. hist_browser__show_headers(hb);
  766. header_offset = 1;
  767. }
  768. ui_browser__hists_init_top(browser);
  769. for (nd = browser->top; nd; nd = rb_next(nd)) {
  770. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  771. float percent;
  772. if (h->filtered)
  773. continue;
  774. percent = hist_entry__get_percent_limit(h);
  775. if (percent < hb->min_pcnt)
  776. continue;
  777. row += hist_browser__show_entry(hb, h, row);
  778. if (row == browser->rows)
  779. break;
  780. }
  781. return row + header_offset;
  782. }
  783. static struct rb_node *hists__filter_entries(struct rb_node *nd,
  784. float min_pcnt)
  785. {
  786. while (nd != NULL) {
  787. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  788. float percent = hist_entry__get_percent_limit(h);
  789. if (!h->filtered && percent >= min_pcnt)
  790. return nd;
  791. nd = rb_next(nd);
  792. }
  793. return NULL;
  794. }
  795. static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
  796. float min_pcnt)
  797. {
  798. while (nd != NULL) {
  799. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  800. float percent = hist_entry__get_percent_limit(h);
  801. if (!h->filtered && percent >= min_pcnt)
  802. return nd;
  803. nd = rb_prev(nd);
  804. }
  805. return NULL;
  806. }
  807. static void ui_browser__hists_seek(struct ui_browser *browser,
  808. off_t offset, int whence)
  809. {
  810. struct hist_entry *h;
  811. struct rb_node *nd;
  812. bool first = true;
  813. struct hist_browser *hb;
  814. hb = container_of(browser, struct hist_browser, b);
  815. if (browser->nr_entries == 0)
  816. return;
  817. ui_browser__hists_init_top(browser);
  818. switch (whence) {
  819. case SEEK_SET:
  820. nd = hists__filter_entries(rb_first(browser->entries),
  821. hb->min_pcnt);
  822. break;
  823. case SEEK_CUR:
  824. nd = browser->top;
  825. goto do_offset;
  826. case SEEK_END:
  827. nd = hists__filter_prev_entries(rb_last(browser->entries),
  828. hb->min_pcnt);
  829. first = false;
  830. break;
  831. default:
  832. return;
  833. }
  834. /*
  835. * Moves not relative to the first visible entry invalidates its
  836. * row_offset:
  837. */
  838. h = rb_entry(browser->top, struct hist_entry, rb_node);
  839. h->row_offset = 0;
  840. /*
  841. * Here we have to check if nd is expanded (+), if it is we can't go
  842. * the next top level hist_entry, instead we must compute an offset of
  843. * what _not_ to show and not change the first visible entry.
  844. *
  845. * This offset increments when we are going from top to bottom and
  846. * decreases when we're going from bottom to top.
  847. *
  848. * As we don't have backpointers to the top level in the callchains
  849. * structure, we need to always print the whole hist_entry callchain,
  850. * skipping the first ones that are before the first visible entry
  851. * and stop when we printed enough lines to fill the screen.
  852. */
  853. do_offset:
  854. if (offset > 0) {
  855. do {
  856. h = rb_entry(nd, struct hist_entry, rb_node);
  857. if (h->unfolded) {
  858. u16 remaining = h->nr_rows - h->row_offset;
  859. if (offset > remaining) {
  860. offset -= remaining;
  861. h->row_offset = 0;
  862. } else {
  863. h->row_offset += offset;
  864. offset = 0;
  865. browser->top = nd;
  866. break;
  867. }
  868. }
  869. nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
  870. if (nd == NULL)
  871. break;
  872. --offset;
  873. browser->top = nd;
  874. } while (offset != 0);
  875. } else if (offset < 0) {
  876. while (1) {
  877. h = rb_entry(nd, struct hist_entry, rb_node);
  878. if (h->unfolded) {
  879. if (first) {
  880. if (-offset > h->row_offset) {
  881. offset += h->row_offset;
  882. h->row_offset = 0;
  883. } else {
  884. h->row_offset += offset;
  885. offset = 0;
  886. browser->top = nd;
  887. break;
  888. }
  889. } else {
  890. if (-offset > h->nr_rows) {
  891. offset += h->nr_rows;
  892. h->row_offset = 0;
  893. } else {
  894. h->row_offset = h->nr_rows + offset;
  895. offset = 0;
  896. browser->top = nd;
  897. break;
  898. }
  899. }
  900. }
  901. nd = hists__filter_prev_entries(rb_prev(nd),
  902. hb->min_pcnt);
  903. if (nd == NULL)
  904. break;
  905. ++offset;
  906. browser->top = nd;
  907. if (offset == 0) {
  908. /*
  909. * Last unfiltered hist_entry, check if it is
  910. * unfolded, if it is then we should have
  911. * row_offset at its last entry.
  912. */
  913. h = rb_entry(nd, struct hist_entry, rb_node);
  914. if (h->unfolded)
  915. h->row_offset = h->nr_rows;
  916. break;
  917. }
  918. first = false;
  919. }
  920. } else {
  921. browser->top = nd;
  922. h = rb_entry(nd, struct hist_entry, rb_node);
  923. h->row_offset = 0;
  924. }
  925. }
  926. static int hist_browser__fprintf_callchain(struct hist_browser *browser,
  927. struct hist_entry *he, FILE *fp)
  928. {
  929. u64 total = hists__total_period(he->hists);
  930. struct callchain_print_arg arg = {
  931. .fp = fp,
  932. };
  933. if (symbol_conf.cumulate_callchain)
  934. total = he->stat_acc->period;
  935. hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
  936. hist_browser__fprintf_callchain_entry, &arg,
  937. hist_browser__check_dump_full);
  938. return arg.printed;
  939. }
  940. static int hist_browser__fprintf_entry(struct hist_browser *browser,
  941. struct hist_entry *he, FILE *fp)
  942. {
  943. char s[8192];
  944. int printed = 0;
  945. char folded_sign = ' ';
  946. struct perf_hpp hpp = {
  947. .buf = s,
  948. .size = sizeof(s),
  949. };
  950. struct perf_hpp_fmt *fmt;
  951. bool first = true;
  952. int ret;
  953. if (symbol_conf.use_callchain)
  954. folded_sign = hist_entry__folded(he);
  955. if (symbol_conf.use_callchain)
  956. printed += fprintf(fp, "%c ", folded_sign);
  957. perf_hpp__for_each_format(fmt) {
  958. if (perf_hpp__should_skip(fmt))
  959. continue;
  960. if (!first) {
  961. ret = scnprintf(hpp.buf, hpp.size, " ");
  962. advance_hpp(&hpp, ret);
  963. } else
  964. first = false;
  965. ret = fmt->entry(fmt, &hpp, he);
  966. advance_hpp(&hpp, ret);
  967. }
  968. printed += fprintf(fp, "%s\n", rtrim(s));
  969. if (folded_sign == '-')
  970. printed += hist_browser__fprintf_callchain(browser, he, fp);
  971. return printed;
  972. }
  973. static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
  974. {
  975. struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
  976. browser->min_pcnt);
  977. int printed = 0;
  978. while (nd) {
  979. struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
  980. printed += hist_browser__fprintf_entry(browser, h, fp);
  981. nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
  982. }
  983. return printed;
  984. }
  985. static int hist_browser__dump(struct hist_browser *browser)
  986. {
  987. char filename[64];
  988. FILE *fp;
  989. while (1) {
  990. scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
  991. if (access(filename, F_OK))
  992. break;
  993. /*
  994. * XXX: Just an arbitrary lazy upper limit
  995. */
  996. if (++browser->print_seq == 8192) {
  997. ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
  998. return -1;
  999. }
  1000. }
  1001. fp = fopen(filename, "w");
  1002. if (fp == NULL) {
  1003. char bf[64];
  1004. const char *err = strerror_r(errno, bf, sizeof(bf));
  1005. ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
  1006. return -1;
  1007. }
  1008. ++browser->print_seq;
  1009. hist_browser__fprintf(browser, fp);
  1010. fclose(fp);
  1011. ui_helpline__fpush("%s written!", filename);
  1012. return 0;
  1013. }
  1014. static struct hist_browser *hist_browser__new(struct hists *hists,
  1015. struct hist_browser_timer *hbt,
  1016. struct perf_env *env)
  1017. {
  1018. struct hist_browser *browser = zalloc(sizeof(*browser));
  1019. if (browser) {
  1020. browser->hists = hists;
  1021. browser->b.refresh = hist_browser__refresh;
  1022. browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
  1023. browser->b.seek = ui_browser__hists_seek;
  1024. browser->b.use_navkeypressed = true;
  1025. browser->show_headers = symbol_conf.show_hist_headers;
  1026. browser->hbt = hbt;
  1027. browser->env = env;
  1028. }
  1029. return browser;
  1030. }
  1031. static void hist_browser__delete(struct hist_browser *browser)
  1032. {
  1033. free(browser);
  1034. }
  1035. static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
  1036. {
  1037. return browser->he_selection;
  1038. }
  1039. static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
  1040. {
  1041. return browser->he_selection->thread;
  1042. }
  1043. /* Check whether the browser is for 'top' or 'report' */
  1044. static inline bool is_report_browser(void *timer)
  1045. {
  1046. return timer == NULL;
  1047. }
  1048. static int hists__browser_title(struct hists *hists,
  1049. struct hist_browser_timer *hbt,
  1050. char *bf, size_t size)
  1051. {
  1052. char unit;
  1053. int printed;
  1054. const struct dso *dso = hists->dso_filter;
  1055. const struct thread *thread = hists->thread_filter;
  1056. unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
  1057. u64 nr_events = hists->stats.total_period;
  1058. struct perf_evsel *evsel = hists_to_evsel(hists);
  1059. const char *ev_name = perf_evsel__name(evsel);
  1060. char buf[512];
  1061. size_t buflen = sizeof(buf);
  1062. char ref[30] = " show reference callgraph, ";
  1063. bool enable_ref = false;
  1064. if (symbol_conf.filter_relative) {
  1065. nr_samples = hists->stats.nr_non_filtered_samples;
  1066. nr_events = hists->stats.total_non_filtered_period;
  1067. }
  1068. if (perf_evsel__is_group_event(evsel)) {
  1069. struct perf_evsel *pos;
  1070. perf_evsel__group_desc(evsel, buf, buflen);
  1071. ev_name = buf;
  1072. for_each_group_member(pos, evsel) {
  1073. struct hists *pos_hists = evsel__hists(pos);
  1074. if (symbol_conf.filter_relative) {
  1075. nr_samples += pos_hists->stats.nr_non_filtered_samples;
  1076. nr_events += pos_hists->stats.total_non_filtered_period;
  1077. } else {
  1078. nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
  1079. nr_events += pos_hists->stats.total_period;
  1080. }
  1081. }
  1082. }
  1083. if (symbol_conf.show_ref_callgraph &&
  1084. strstr(ev_name, "call-graph=no"))
  1085. enable_ref = true;
  1086. nr_samples = convert_unit(nr_samples, &unit);
  1087. printed = scnprintf(bf, size,
  1088. "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
  1089. nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
  1090. if (hists->uid_filter_str)
  1091. printed += snprintf(bf + printed, size - printed,
  1092. ", UID: %s", hists->uid_filter_str);
  1093. if (thread)
  1094. printed += scnprintf(bf + printed, size - printed,
  1095. ", Thread: %s(%d)",
  1096. (thread->comm_set ? thread__comm_str(thread) : ""),
  1097. thread->tid);
  1098. if (dso)
  1099. printed += scnprintf(bf + printed, size - printed,
  1100. ", DSO: %s", dso->short_name);
  1101. if (!is_report_browser(hbt)) {
  1102. struct perf_top *top = hbt->arg;
  1103. if (top->zero)
  1104. printed += scnprintf(bf + printed, size - printed, " [z]");
  1105. }
  1106. return printed;
  1107. }
  1108. static inline void free_popup_options(char **options, int n)
  1109. {
  1110. int i;
  1111. for (i = 0; i < n; ++i)
  1112. zfree(&options[i]);
  1113. }
  1114. /*
  1115. * Only runtime switching of perf data file will make "input_name" point
  1116. * to a malloced buffer. So add "is_input_name_malloced" flag to decide
  1117. * whether we need to call free() for current "input_name" during the switch.
  1118. */
  1119. static bool is_input_name_malloced = false;
  1120. static int switch_data_file(void)
  1121. {
  1122. char *pwd, *options[32], *abs_path[32], *tmp;
  1123. DIR *pwd_dir;
  1124. int nr_options = 0, choice = -1, ret = -1;
  1125. struct dirent *dent;
  1126. pwd = getenv("PWD");
  1127. if (!pwd)
  1128. return ret;
  1129. pwd_dir = opendir(pwd);
  1130. if (!pwd_dir)
  1131. return ret;
  1132. memset(options, 0, sizeof(options));
  1133. memset(options, 0, sizeof(abs_path));
  1134. while ((dent = readdir(pwd_dir))) {
  1135. char path[PATH_MAX];
  1136. u64 magic;
  1137. char *name = dent->d_name;
  1138. FILE *file;
  1139. if (!(dent->d_type == DT_REG))
  1140. continue;
  1141. snprintf(path, sizeof(path), "%s/%s", pwd, name);
  1142. file = fopen(path, "r");
  1143. if (!file)
  1144. continue;
  1145. if (fread(&magic, 1, 8, file) < 8)
  1146. goto close_file_and_continue;
  1147. if (is_perf_magic(magic)) {
  1148. options[nr_options] = strdup(name);
  1149. if (!options[nr_options])
  1150. goto close_file_and_continue;
  1151. abs_path[nr_options] = strdup(path);
  1152. if (!abs_path[nr_options]) {
  1153. zfree(&options[nr_options]);
  1154. ui__warning("Can't search all data files due to memory shortage.\n");
  1155. fclose(file);
  1156. break;
  1157. }
  1158. nr_options++;
  1159. }
  1160. close_file_and_continue:
  1161. fclose(file);
  1162. if (nr_options >= 32) {
  1163. ui__warning("Too many perf data files in PWD!\n"
  1164. "Only the first 32 files will be listed.\n");
  1165. break;
  1166. }
  1167. }
  1168. closedir(pwd_dir);
  1169. if (nr_options) {
  1170. choice = ui__popup_menu(nr_options, options);
  1171. if (choice < nr_options && choice >= 0) {
  1172. tmp = strdup(abs_path[choice]);
  1173. if (tmp) {
  1174. if (is_input_name_malloced)
  1175. free((void *)input_name);
  1176. input_name = tmp;
  1177. is_input_name_malloced = true;
  1178. ret = 0;
  1179. } else
  1180. ui__warning("Data switch failed due to memory shortage!\n");
  1181. }
  1182. }
  1183. free_popup_options(options, nr_options);
  1184. free_popup_options(abs_path, nr_options);
  1185. return ret;
  1186. }
  1187. struct popup_action {
  1188. struct thread *thread;
  1189. struct dso *dso;
  1190. struct map_symbol ms;
  1191. int (*fn)(struct hist_browser *browser, struct popup_action *act);
  1192. };
  1193. static int
  1194. do_annotate(struct hist_browser *browser, struct popup_action *act)
  1195. {
  1196. struct perf_evsel *evsel;
  1197. struct annotation *notes;
  1198. struct hist_entry *he;
  1199. int err;
  1200. if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
  1201. return 0;
  1202. notes = symbol__annotation(act->ms.sym);
  1203. if (!notes->src)
  1204. return 0;
  1205. evsel = hists_to_evsel(browser->hists);
  1206. err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
  1207. he = hist_browser__selected_entry(browser);
  1208. /*
  1209. * offer option to annotate the other branch source or target
  1210. * (if they exists) when returning from annotate
  1211. */
  1212. if ((err == 'q' || err == CTRL('c')) && he->branch_info)
  1213. return 1;
  1214. ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
  1215. if (err)
  1216. ui_browser__handle_resize(&browser->b);
  1217. return 0;
  1218. }
  1219. static int
  1220. add_annotate_opt(struct hist_browser *browser __maybe_unused,
  1221. struct popup_action *act, char **optstr,
  1222. struct map *map, struct symbol *sym)
  1223. {
  1224. if (sym == NULL || map->dso->annotate_warned)
  1225. return 0;
  1226. if (asprintf(optstr, "Annotate %s", sym->name) < 0)
  1227. return 0;
  1228. act->ms.map = map;
  1229. act->ms.sym = sym;
  1230. act->fn = do_annotate;
  1231. return 1;
  1232. }
  1233. static int
  1234. do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
  1235. {
  1236. struct thread *thread = act->thread;
  1237. if (browser->hists->thread_filter) {
  1238. pstack__remove(browser->pstack, &browser->hists->thread_filter);
  1239. perf_hpp__set_elide(HISTC_THREAD, false);
  1240. thread__zput(browser->hists->thread_filter);
  1241. ui_helpline__pop();
  1242. } else {
  1243. ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
  1244. thread->comm_set ? thread__comm_str(thread) : "",
  1245. thread->tid);
  1246. browser->hists->thread_filter = thread__get(thread);
  1247. perf_hpp__set_elide(HISTC_THREAD, false);
  1248. pstack__push(browser->pstack, &browser->hists->thread_filter);
  1249. }
  1250. hists__filter_by_thread(browser->hists);
  1251. hist_browser__reset(browser);
  1252. return 0;
  1253. }
  1254. static int
  1255. add_thread_opt(struct hist_browser *browser, struct popup_action *act,
  1256. char **optstr, struct thread *thread)
  1257. {
  1258. if (thread == NULL)
  1259. return 0;
  1260. if (asprintf(optstr, "Zoom %s %s(%d) thread",
  1261. browser->hists->thread_filter ? "out of" : "into",
  1262. thread->comm_set ? thread__comm_str(thread) : "",
  1263. thread->tid) < 0)
  1264. return 0;
  1265. act->thread = thread;
  1266. act->fn = do_zoom_thread;
  1267. return 1;
  1268. }
  1269. static int
  1270. do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
  1271. {
  1272. struct dso *dso = act->dso;
  1273. if (browser->hists->dso_filter) {
  1274. pstack__remove(browser->pstack, &browser->hists->dso_filter);
  1275. perf_hpp__set_elide(HISTC_DSO, false);
  1276. browser->hists->dso_filter = NULL;
  1277. ui_helpline__pop();
  1278. } else {
  1279. if (dso == NULL)
  1280. return 0;
  1281. ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
  1282. dso->kernel ? "the Kernel" : dso->short_name);
  1283. browser->hists->dso_filter = dso;
  1284. perf_hpp__set_elide(HISTC_DSO, true);
  1285. pstack__push(browser->pstack, &browser->hists->dso_filter);
  1286. }
  1287. hists__filter_by_dso(browser->hists);
  1288. hist_browser__reset(browser);
  1289. return 0;
  1290. }
  1291. static int
  1292. add_dso_opt(struct hist_browser *browser, struct popup_action *act,
  1293. char **optstr, struct dso *dso)
  1294. {
  1295. if (dso == NULL)
  1296. return 0;
  1297. if (asprintf(optstr, "Zoom %s %s DSO",
  1298. browser->hists->dso_filter ? "out of" : "into",
  1299. dso->kernel ? "the Kernel" : dso->short_name) < 0)
  1300. return 0;
  1301. act->dso = dso;
  1302. act->fn = do_zoom_dso;
  1303. return 1;
  1304. }
  1305. static int
  1306. do_browse_map(struct hist_browser *browser __maybe_unused,
  1307. struct popup_action *act)
  1308. {
  1309. map__browse(act->ms.map);
  1310. return 0;
  1311. }
  1312. static int
  1313. add_map_opt(struct hist_browser *browser __maybe_unused,
  1314. struct popup_action *act, char **optstr, struct map *map)
  1315. {
  1316. if (map == NULL)
  1317. return 0;
  1318. if (asprintf(optstr, "Browse map details") < 0)
  1319. return 0;
  1320. act->ms.map = map;
  1321. act->fn = do_browse_map;
  1322. return 1;
  1323. }
  1324. static int
  1325. do_run_script(struct hist_browser *browser __maybe_unused,
  1326. struct popup_action *act)
  1327. {
  1328. char script_opt[64];
  1329. memset(script_opt, 0, sizeof(script_opt));
  1330. if (act->thread) {
  1331. scnprintf(script_opt, sizeof(script_opt), " -c %s ",
  1332. thread__comm_str(act->thread));
  1333. } else if (act->ms.sym) {
  1334. scnprintf(script_opt, sizeof(script_opt), " -S %s ",
  1335. act->ms.sym->name);
  1336. }
  1337. script_browse(script_opt);
  1338. return 0;
  1339. }
  1340. static int
  1341. add_script_opt(struct hist_browser *browser __maybe_unused,
  1342. struct popup_action *act, char **optstr,
  1343. struct thread *thread, struct symbol *sym)
  1344. {
  1345. if (thread) {
  1346. if (asprintf(optstr, "Run scripts for samples of thread [%s]",
  1347. thread__comm_str(thread)) < 0)
  1348. return 0;
  1349. } else if (sym) {
  1350. if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
  1351. sym->name) < 0)
  1352. return 0;
  1353. } else {
  1354. if (asprintf(optstr, "Run scripts for all samples") < 0)
  1355. return 0;
  1356. }
  1357. act->thread = thread;
  1358. act->ms.sym = sym;
  1359. act->fn = do_run_script;
  1360. return 1;
  1361. }
  1362. static int
  1363. do_switch_data(struct hist_browser *browser __maybe_unused,
  1364. struct popup_action *act __maybe_unused)
  1365. {
  1366. if (switch_data_file()) {
  1367. ui__warning("Won't switch the data files due to\n"
  1368. "no valid data file get selected!\n");
  1369. return 0;
  1370. }
  1371. return K_SWITCH_INPUT_DATA;
  1372. }
  1373. static int
  1374. add_switch_opt(struct hist_browser *browser,
  1375. struct popup_action *act, char **optstr)
  1376. {
  1377. if (!is_report_browser(browser->hbt))
  1378. return 0;
  1379. if (asprintf(optstr, "Switch to another data file in PWD") < 0)
  1380. return 0;
  1381. act->fn = do_switch_data;
  1382. return 1;
  1383. }
  1384. static int
  1385. do_exit_browser(struct hist_browser *browser __maybe_unused,
  1386. struct popup_action *act __maybe_unused)
  1387. {
  1388. return 0;
  1389. }
  1390. static int
  1391. add_exit_opt(struct hist_browser *browser __maybe_unused,
  1392. struct popup_action *act, char **optstr)
  1393. {
  1394. if (asprintf(optstr, "Exit") < 0)
  1395. return 0;
  1396. act->fn = do_exit_browser;
  1397. return 1;
  1398. }
  1399. static void hist_browser__update_nr_entries(struct hist_browser *hb)
  1400. {
  1401. u64 nr_entries = 0;
  1402. struct rb_node *nd = rb_first(&hb->hists->entries);
  1403. if (hb->min_pcnt == 0) {
  1404. hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
  1405. return;
  1406. }
  1407. while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
  1408. nr_entries++;
  1409. nd = rb_next(nd);
  1410. }
  1411. hb->nr_non_filtered_entries = nr_entries;
  1412. }
  1413. static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
  1414. const char *helpline,
  1415. bool left_exits,
  1416. struct hist_browser_timer *hbt,
  1417. float min_pcnt,
  1418. struct perf_env *env)
  1419. {
  1420. struct hists *hists = evsel__hists(evsel);
  1421. struct hist_browser *browser = hist_browser__new(hists, hbt, env);
  1422. struct branch_info *bi;
  1423. #define MAX_OPTIONS 16
  1424. char *options[MAX_OPTIONS];
  1425. struct popup_action actions[MAX_OPTIONS];
  1426. int nr_options = 0;
  1427. int key = -1;
  1428. char buf[64];
  1429. int delay_secs = hbt ? hbt->refresh : 0;
  1430. struct perf_hpp_fmt *fmt;
  1431. #define HIST_BROWSER_HELP_COMMON \
  1432. "h/?/F1 Show this window\n" \
  1433. "UP/DOWN/PGUP\n" \
  1434. "PGDN/SPACE Navigate\n" \
  1435. "q/ESC/CTRL+C Exit browser\n\n" \
  1436. "For multiple event sessions:\n\n" \
  1437. "TAB/UNTAB Switch events\n\n" \
  1438. "For symbolic views (--sort has sym):\n\n" \
  1439. "-> Zoom into DSO/Threads & Annotate current symbol\n" \
  1440. "<- Zoom out\n" \
  1441. "a Annotate current symbol\n" \
  1442. "C Collapse all callchains\n" \
  1443. "d Zoom into current DSO\n" \
  1444. "E Expand all callchains\n" \
  1445. "F Toggle percentage of filtered entries\n" \
  1446. "H Display column headers\n" \
  1447. /* help messages are sorted by lexical order of the hotkey */
  1448. const char report_help[] = HIST_BROWSER_HELP_COMMON
  1449. "i Show header information\n"
  1450. "P Print histograms to perf.hist.N\n"
  1451. "r Run available scripts\n"
  1452. "s Switch to another data file in PWD\n"
  1453. "t Zoom into current Thread\n"
  1454. "V Verbose (DSO names in callchains, etc)\n"
  1455. "/ Filter symbol by name";
  1456. const char top_help[] = HIST_BROWSER_HELP_COMMON
  1457. "P Print histograms to perf.hist.N\n"
  1458. "t Zoom into current Thread\n"
  1459. "V Verbose (DSO names in callchains, etc)\n"
  1460. "z Toggle zeroing of samples\n"
  1461. "f Enable/Disable events\n"
  1462. "/ Filter symbol by name";
  1463. if (browser == NULL)
  1464. return -1;
  1465. /* reset abort key so that it can get Ctrl-C as a key */
  1466. SLang_reset_tty();
  1467. SLang_init_tty(0, 0, 0);
  1468. if (min_pcnt) {
  1469. browser->min_pcnt = min_pcnt;
  1470. hist_browser__update_nr_entries(browser);
  1471. }
  1472. browser->pstack = pstack__new(2);
  1473. if (browser->pstack == NULL)
  1474. goto out;
  1475. ui_helpline__push(helpline);
  1476. memset(options, 0, sizeof(options));
  1477. memset(actions, 0, sizeof(actions));
  1478. perf_hpp__for_each_format(fmt)
  1479. perf_hpp__reset_width(fmt, hists);
  1480. if (symbol_conf.col_width_list_str)
  1481. perf_hpp__set_user_width(symbol_conf.col_width_list_str);
  1482. while (1) {
  1483. struct thread *thread = NULL;
  1484. struct dso *dso = NULL;
  1485. int choice = 0;
  1486. nr_options = 0;
  1487. key = hist_browser__run(browser, helpline);
  1488. if (browser->he_selection != NULL) {
  1489. thread = hist_browser__selected_thread(browser);
  1490. dso = browser->selection->map ? browser->selection->map->dso : NULL;
  1491. }
  1492. switch (key) {
  1493. case K_TAB:
  1494. case K_UNTAB:
  1495. if (nr_events == 1)
  1496. continue;
  1497. /*
  1498. * Exit the browser, let hists__browser_tree
  1499. * go to the next or previous
  1500. */
  1501. goto out_free_stack;
  1502. case 'a':
  1503. if (!sort__has_sym) {
  1504. ui_browser__warning(&browser->b, delay_secs * 2,
  1505. "Annotation is only available for symbolic views, "
  1506. "include \"sym*\" in --sort to use it.");
  1507. continue;
  1508. }
  1509. if (browser->selection == NULL ||
  1510. browser->selection->sym == NULL ||
  1511. browser->selection->map->dso->annotate_warned)
  1512. continue;
  1513. actions->ms.map = browser->selection->map;
  1514. actions->ms.sym = browser->selection->sym;
  1515. do_annotate(browser, actions);
  1516. continue;
  1517. case 'P':
  1518. hist_browser__dump(browser);
  1519. continue;
  1520. case 'd':
  1521. actions->dso = dso;
  1522. do_zoom_dso(browser, actions);
  1523. continue;
  1524. case 'V':
  1525. browser->show_dso = !browser->show_dso;
  1526. continue;
  1527. case 't':
  1528. actions->thread = thread;
  1529. do_zoom_thread(browser, actions);
  1530. continue;
  1531. case '/':
  1532. if (ui_browser__input_window("Symbol to show",
  1533. "Please enter the name of symbol you want to see",
  1534. buf, "ENTER: OK, ESC: Cancel",
  1535. delay_secs * 2) == K_ENTER) {
  1536. hists->symbol_filter_str = *buf ? buf : NULL;
  1537. hists__filter_by_symbol(hists);
  1538. hist_browser__reset(browser);
  1539. }
  1540. continue;
  1541. case 'r':
  1542. if (is_report_browser(hbt)) {
  1543. actions->thread = NULL;
  1544. actions->ms.sym = NULL;
  1545. do_run_script(browser, actions);
  1546. }
  1547. continue;
  1548. case 's':
  1549. if (is_report_browser(hbt)) {
  1550. key = do_switch_data(browser, actions);
  1551. if (key == K_SWITCH_INPUT_DATA)
  1552. goto out_free_stack;
  1553. }
  1554. continue;
  1555. case 'i':
  1556. /* env->arch is NULL for live-mode (i.e. perf top) */
  1557. if (env->arch)
  1558. tui__header_window(env);
  1559. continue;
  1560. case 'F':
  1561. symbol_conf.filter_relative ^= 1;
  1562. continue;
  1563. case 'z':
  1564. if (!is_report_browser(hbt)) {
  1565. struct perf_top *top = hbt->arg;
  1566. top->zero = !top->zero;
  1567. }
  1568. continue;
  1569. case K_F1:
  1570. case 'h':
  1571. case '?':
  1572. ui_browser__help_window(&browser->b,
  1573. is_report_browser(hbt) ? report_help : top_help);
  1574. continue;
  1575. case K_ENTER:
  1576. case K_RIGHT:
  1577. /* menu */
  1578. break;
  1579. case K_ESC:
  1580. case K_LEFT: {
  1581. const void *top;
  1582. if (pstack__empty(browser->pstack)) {
  1583. /*
  1584. * Go back to the perf_evsel_menu__run or other user
  1585. */
  1586. if (left_exits)
  1587. goto out_free_stack;
  1588. if (key == K_ESC &&
  1589. ui_browser__dialog_yesno(&browser->b,
  1590. "Do you really want to exit?"))
  1591. goto out_free_stack;
  1592. continue;
  1593. }
  1594. top = pstack__peek(browser->pstack);
  1595. if (top == &browser->hists->dso_filter) {
  1596. /*
  1597. * No need to set actions->dso here since
  1598. * it's just to remove the current filter.
  1599. * Ditto for thread below.
  1600. */
  1601. do_zoom_dso(browser, actions);
  1602. }
  1603. if (top == &browser->hists->thread_filter)
  1604. do_zoom_thread(browser, actions);
  1605. continue;
  1606. }
  1607. case 'q':
  1608. case CTRL('c'):
  1609. goto out_free_stack;
  1610. case 'f':
  1611. if (!is_report_browser(hbt)) {
  1612. struct perf_top *top = hbt->arg;
  1613. perf_evlist__toggle_enable(top->evlist);
  1614. /*
  1615. * No need to refresh, resort/decay histogram
  1616. * entries if we are not collecting samples:
  1617. */
  1618. if (top->evlist->enabled) {
  1619. helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
  1620. hbt->refresh = delay_secs;
  1621. } else {
  1622. helpline = "Press 'f' again to re-enable the events";
  1623. hbt->refresh = 0;
  1624. }
  1625. continue;
  1626. }
  1627. /* Fall thru */
  1628. default:
  1629. helpline = "Press '?' for help on key bindings";
  1630. continue;
  1631. }
  1632. if (!sort__has_sym)
  1633. goto add_exit_option;
  1634. if (browser->selection == NULL)
  1635. goto skip_annotation;
  1636. if (sort__mode == SORT_MODE__BRANCH) {
  1637. bi = browser->he_selection->branch_info;
  1638. if (bi == NULL)
  1639. goto skip_annotation;
  1640. nr_options += add_annotate_opt(browser,
  1641. &actions[nr_options],
  1642. &options[nr_options],
  1643. bi->from.map,
  1644. bi->from.sym);
  1645. if (bi->to.sym != bi->from.sym)
  1646. nr_options += add_annotate_opt(browser,
  1647. &actions[nr_options],
  1648. &options[nr_options],
  1649. bi->to.map,
  1650. bi->to.sym);
  1651. } else {
  1652. nr_options += add_annotate_opt(browser,
  1653. &actions[nr_options],
  1654. &options[nr_options],
  1655. browser->selection->map,
  1656. browser->selection->sym);
  1657. }
  1658. skip_annotation:
  1659. nr_options += add_thread_opt(browser, &actions[nr_options],
  1660. &options[nr_options], thread);
  1661. nr_options += add_dso_opt(browser, &actions[nr_options],
  1662. &options[nr_options], dso);
  1663. nr_options += add_map_opt(browser, &actions[nr_options],
  1664. &options[nr_options],
  1665. browser->selection ?
  1666. browser->selection->map : NULL);
  1667. /* perf script support */
  1668. if (browser->he_selection) {
  1669. nr_options += add_script_opt(browser,
  1670. &actions[nr_options],
  1671. &options[nr_options],
  1672. thread, NULL);
  1673. /*
  1674. * Note that browser->selection != NULL
  1675. * when browser->he_selection is not NULL,
  1676. * so we don't need to check browser->selection
  1677. * before fetching browser->selection->sym like what
  1678. * we do before fetching browser->selection->map.
  1679. *
  1680. * See hist_browser__show_entry.
  1681. */
  1682. nr_options += add_script_opt(browser,
  1683. &actions[nr_options],
  1684. &options[nr_options],
  1685. NULL, browser->selection->sym);
  1686. }
  1687. nr_options += add_script_opt(browser, &actions[nr_options],
  1688. &options[nr_options], NULL, NULL);
  1689. nr_options += add_switch_opt(browser, &actions[nr_options],
  1690. &options[nr_options]);
  1691. add_exit_option:
  1692. nr_options += add_exit_opt(browser, &actions[nr_options],
  1693. &options[nr_options]);
  1694. do {
  1695. struct popup_action *act;
  1696. choice = ui__popup_menu(nr_options, options);
  1697. if (choice == -1 || choice >= nr_options)
  1698. break;
  1699. act = &actions[choice];
  1700. key = act->fn(browser, act);
  1701. } while (key == 1);
  1702. if (key == K_SWITCH_INPUT_DATA)
  1703. break;
  1704. }
  1705. out_free_stack:
  1706. pstack__delete(browser->pstack);
  1707. out:
  1708. hist_browser__delete(browser);
  1709. free_popup_options(options, MAX_OPTIONS);
  1710. return key;
  1711. }
  1712. struct perf_evsel_menu {
  1713. struct ui_browser b;
  1714. struct perf_evsel *selection;
  1715. bool lost_events, lost_events_warned;
  1716. float min_pcnt;
  1717. struct perf_env *env;
  1718. };
  1719. static void perf_evsel_menu__write(struct ui_browser *browser,
  1720. void *entry, int row)
  1721. {
  1722. struct perf_evsel_menu *menu = container_of(browser,
  1723. struct perf_evsel_menu, b);
  1724. struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
  1725. struct hists *hists = evsel__hists(evsel);
  1726. bool current_entry = ui_browser__is_current_entry(browser, row);
  1727. unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
  1728. const char *ev_name = perf_evsel__name(evsel);
  1729. char bf[256], unit;
  1730. const char *warn = " ";
  1731. size_t printed;
  1732. ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
  1733. HE_COLORSET_NORMAL);
  1734. if (perf_evsel__is_group_event(evsel)) {
  1735. struct perf_evsel *pos;
  1736. ev_name = perf_evsel__group_name(evsel);
  1737. for_each_group_member(pos, evsel) {
  1738. struct hists *pos_hists = evsel__hists(pos);
  1739. nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
  1740. }
  1741. }
  1742. nr_events = convert_unit(nr_events, &unit);
  1743. printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
  1744. unit, unit == ' ' ? "" : " ", ev_name);
  1745. ui_browser__printf(browser, "%s", bf);
  1746. nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
  1747. if (nr_events != 0) {
  1748. menu->lost_events = true;
  1749. if (!current_entry)
  1750. ui_browser__set_color(browser, HE_COLORSET_TOP);
  1751. nr_events = convert_unit(nr_events, &unit);
  1752. printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
  1753. nr_events, unit, unit == ' ' ? "" : " ");
  1754. warn = bf;
  1755. }
  1756. ui_browser__write_nstring(browser, warn, browser->width - printed);
  1757. if (current_entry)
  1758. menu->selection = evsel;
  1759. }
  1760. static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
  1761. int nr_events, const char *help,
  1762. struct hist_browser_timer *hbt)
  1763. {
  1764. struct perf_evlist *evlist = menu->b.priv;
  1765. struct perf_evsel *pos;
  1766. const char *title = "Available samples";
  1767. int delay_secs = hbt ? hbt->refresh : 0;
  1768. int key;
  1769. if (ui_browser__show(&menu->b, title,
  1770. "ESC: exit, ENTER|->: Browse histograms") < 0)
  1771. return -1;
  1772. while (1) {
  1773. key = ui_browser__run(&menu->b, delay_secs);
  1774. switch (key) {
  1775. case K_TIMER:
  1776. hbt->timer(hbt->arg);
  1777. if (!menu->lost_events_warned && menu->lost_events) {
  1778. ui_browser__warn_lost_events(&menu->b);
  1779. menu->lost_events_warned = true;
  1780. }
  1781. continue;
  1782. case K_RIGHT:
  1783. case K_ENTER:
  1784. if (!menu->selection)
  1785. continue;
  1786. pos = menu->selection;
  1787. browse_hists:
  1788. perf_evlist__set_selected(evlist, pos);
  1789. /*
  1790. * Give the calling tool a chance to populate the non
  1791. * default evsel resorted hists tree.
  1792. */
  1793. if (hbt)
  1794. hbt->timer(hbt->arg);
  1795. key = perf_evsel__hists_browse(pos, nr_events, help,
  1796. true, hbt,
  1797. menu->min_pcnt,
  1798. menu->env);
  1799. ui_browser__show_title(&menu->b, title);
  1800. switch (key) {
  1801. case K_TAB:
  1802. if (pos->node.next == &evlist->entries)
  1803. pos = perf_evlist__first(evlist);
  1804. else
  1805. pos = perf_evsel__next(pos);
  1806. goto browse_hists;
  1807. case K_UNTAB:
  1808. if (pos->node.prev == &evlist->entries)
  1809. pos = perf_evlist__last(evlist);
  1810. else
  1811. pos = perf_evsel__prev(pos);
  1812. goto browse_hists;
  1813. case K_SWITCH_INPUT_DATA:
  1814. case 'q':
  1815. case CTRL('c'):
  1816. goto out;
  1817. case K_ESC:
  1818. default:
  1819. continue;
  1820. }
  1821. case K_LEFT:
  1822. continue;
  1823. case K_ESC:
  1824. if (!ui_browser__dialog_yesno(&menu->b,
  1825. "Do you really want to exit?"))
  1826. continue;
  1827. /* Fall thru */
  1828. case 'q':
  1829. case CTRL('c'):
  1830. goto out;
  1831. default:
  1832. continue;
  1833. }
  1834. }
  1835. out:
  1836. ui_browser__hide(&menu->b);
  1837. return key;
  1838. }
  1839. static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
  1840. void *entry)
  1841. {
  1842. struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
  1843. if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
  1844. return true;
  1845. return false;
  1846. }
  1847. static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
  1848. int nr_entries, const char *help,
  1849. struct hist_browser_timer *hbt,
  1850. float min_pcnt,
  1851. struct perf_env *env)
  1852. {
  1853. struct perf_evsel *pos;
  1854. struct perf_evsel_menu menu = {
  1855. .b = {
  1856. .entries = &evlist->entries,
  1857. .refresh = ui_browser__list_head_refresh,
  1858. .seek = ui_browser__list_head_seek,
  1859. .write = perf_evsel_menu__write,
  1860. .filter = filter_group_entries,
  1861. .nr_entries = nr_entries,
  1862. .priv = evlist,
  1863. },
  1864. .min_pcnt = min_pcnt,
  1865. .env = env,
  1866. };
  1867. ui_helpline__push("Press ESC to exit");
  1868. evlist__for_each(evlist, pos) {
  1869. const char *ev_name = perf_evsel__name(pos);
  1870. size_t line_len = strlen(ev_name) + 7;
  1871. if (menu.b.width < line_len)
  1872. menu.b.width = line_len;
  1873. }
  1874. return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
  1875. }
  1876. int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
  1877. struct hist_browser_timer *hbt,
  1878. float min_pcnt,
  1879. struct perf_env *env)
  1880. {
  1881. int nr_entries = evlist->nr_entries;
  1882. single_entry:
  1883. if (nr_entries == 1) {
  1884. struct perf_evsel *first = perf_evlist__first(evlist);
  1885. return perf_evsel__hists_browse(first, nr_entries, help,
  1886. false, hbt, min_pcnt,
  1887. env);
  1888. }
  1889. if (symbol_conf.event_group) {
  1890. struct perf_evsel *pos;
  1891. nr_entries = 0;
  1892. evlist__for_each(evlist, pos) {
  1893. if (perf_evsel__is_group_leader(pos))
  1894. nr_entries++;
  1895. }
  1896. if (nr_entries == 1)
  1897. goto single_entry;
  1898. }
  1899. return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
  1900. hbt, min_pcnt, env);
  1901. }