1#include "../browser.h"
2#include "../helpline.h"
3#include "../libslang.h"
4#include "../../annotate.h"
5#include "../../hist.h"
6#include "../../sort.h"
7#include "../../symbol.h"
8#include <pthread.h>
9
10static void ui__error_window(const char *fmt, ...)
11{
12	va_list ap;
13
14	va_start(ap, fmt);
15	newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
16	va_end(ap);
17}
18
19struct annotate_browser {
20	struct ui_browser b;
21	struct rb_root	  entries;
22	struct rb_node	  *curr_hot;
23};
24
25struct objdump_line_rb_node {
26	struct rb_node	rb_node;
27	double		percent;
28	u32		idx;
29};
30
31static inline
32struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
33{
34	return (struct objdump_line_rb_node *)(self + 1);
35}
36
37static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
38{
39	struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
40	bool current_entry = ui_browser__is_current_entry(self, row);
41	int width = self->width;
42
43	if (ol->offset != -1) {
44		struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
45		ui_browser__set_percent_color(self, olrb->percent, current_entry);
46		slsmg_printf(" %7.2f ", olrb->percent);
47	} else {
48		ui_browser__set_percent_color(self, 0, current_entry);
49		slsmg_write_nstring(" ", 9);
50	}
51
52	SLsmg_write_char(':');
53	slsmg_write_nstring(" ", 8);
54	if (!*ol->line)
55		slsmg_write_nstring(" ", width - 18);
56	else
57		slsmg_write_nstring(ol->line, width - 18);
58
59	if (!current_entry)
60		ui_browser__set_color(self, HE_COLORSET_CODE);
61}
62
63static double objdump_line__calc_percent(struct objdump_line *self,
64					 struct symbol *sym, int evidx)
65{
66	double percent = 0.0;
67
68	if (self->offset != -1) {
69		int len = sym->end - sym->start;
70		unsigned int hits = 0;
71		struct annotation *notes = symbol__annotation(sym);
72		struct source_line *src_line = notes->src->lines;
73		struct sym_hist *h = annotation__histogram(notes, evidx);
74		s64 offset = self->offset;
75		struct objdump_line *next;
76
77		next = objdump__get_next_ip_line(&notes->src->source, self);
78		while (offset < (s64)len &&
79		       (next == NULL || offset < next->offset)) {
80			if (src_line) {
81				percent += src_line[offset].percent;
82			} else
83				hits += h->addr[offset];
84
85			++offset;
86		}
87		/*
88 		 * If the percentage wasn't already calculated in
89 		 * symbol__get_source_line, do it now:
90 		 */
91		if (src_line == NULL && h->sum)
92			percent = 100.0 * hits / h->sum;
93	}
94
95	return percent;
96}
97
98static void objdump__insert_line(struct rb_root *self,
99				 struct objdump_line_rb_node *line)
100{
101	struct rb_node **p = &self->rb_node;
102	struct rb_node *parent = NULL;
103	struct objdump_line_rb_node *l;
104
105	while (*p != NULL) {
106		parent = *p;
107		l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
108		if (line->percent < l->percent)
109			p = &(*p)->rb_left;
110		else
111			p = &(*p)->rb_right;
112	}
113	rb_link_node(&line->rb_node, parent, p);
114	rb_insert_color(&line->rb_node, self);
115}
116
117static void annotate_browser__set_top(struct annotate_browser *self,
118				      struct rb_node *nd)
119{
120	struct objdump_line_rb_node *rbpos;
121	struct objdump_line *pos;
122	unsigned back;
123
124	ui_browser__refresh_dimensions(&self->b);
125	back = self->b.height / 2;
126	rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
127	pos = ((struct objdump_line *)rbpos) - 1;
128	self->b.top_idx = self->b.index = rbpos->idx;
129
130	while (self->b.top_idx != 0 && back != 0) {
131		pos = list_entry(pos->node.prev, struct objdump_line, node);
132
133		--self->b.top_idx;
134		--back;
135	}
136
137	self->b.top = pos;
138	self->curr_hot = nd;
139}
140
141static void annotate_browser__calc_percent(struct annotate_browser *browser,
142					   int evidx)
143{
144	struct symbol *sym = browser->b.priv;
145	struct annotation *notes = symbol__annotation(sym);
146	struct objdump_line *pos;
147
148	browser->entries = RB_ROOT;
149
150	pthread_mutex_lock(&notes->lock);
151
152	list_for_each_entry(pos, &notes->src->source, node) {
153		struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
154		rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
155		if (rbpos->percent < 0.01) {
156			RB_CLEAR_NODE(&rbpos->rb_node);
157			continue;
158		}
159		objdump__insert_line(&browser->entries, rbpos);
160	}
161	pthread_mutex_unlock(&notes->lock);
162
163	browser->curr_hot = rb_last(&browser->entries);
164}
165
166static int annotate_browser__run(struct annotate_browser *self, int evidx,
167				 int refresh)
168{
169	struct rb_node *nd = NULL;
170	struct symbol *sym = self->b.priv;
171	/*
172	 * RIGHT To allow builtin-annotate to cycle thru multiple symbols by
173	 * examining the exit key for this function.
174	 */
175	int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB,
176			    NEWT_KEY_RIGHT, 0 };
177	int key;
178
179	if (ui_browser__show(&self->b, sym->name,
180			     "<-, -> or ESC: exit, TAB/shift+TAB: "
181			     "cycle hottest lines, H: Hottest") < 0)
182		return -1;
183
184	ui_browser__add_exit_keys(&self->b, exit_keys);
185	annotate_browser__calc_percent(self, evidx);
186
187	if (self->curr_hot)
188		annotate_browser__set_top(self, self->curr_hot);
189
190	nd = self->curr_hot;
191
192	if (refresh != 0)
193		newtFormSetTimer(self->b.form, refresh);
194
195	while (1) {
196		key = ui_browser__run(&self->b);
197
198		if (refresh != 0) {
199			annotate_browser__calc_percent(self, evidx);
200			/*
201			 * Current line focus got out of the list of most active
202			 * lines, NULL it so that if TAB|UNTAB is pressed, we
203			 * move to curr_hot (current hottest line).
204			 */
205			if (nd != NULL && RB_EMPTY_NODE(nd))
206				nd = NULL;
207		}
208
209		switch (key) {
210		case -1:
211			/*
212 			 * FIXME we need to check if it was
213 			 * es.reason == NEWT_EXIT_TIMER
214 			 */
215			if (refresh != 0)
216				symbol__annotate_decay_histogram(sym, evidx);
217			continue;
218		case NEWT_KEY_TAB:
219			if (nd != NULL) {
220				nd = rb_prev(nd);
221				if (nd == NULL)
222					nd = rb_last(&self->entries);
223			} else
224				nd = self->curr_hot;
225			break;
226		case NEWT_KEY_UNTAB:
227			if (nd != NULL)
228				nd = rb_next(nd);
229				if (nd == NULL)
230					nd = rb_first(&self->entries);
231			else
232				nd = self->curr_hot;
233			break;
234		case 'H':
235			nd = self->curr_hot;
236			break;
237		default:
238			goto out;
239		}
240
241		if (nd != NULL)
242			annotate_browser__set_top(self, nd);
243	}
244out:
245	ui_browser__hide(&self->b);
246	return key;
247}
248
249int hist_entry__tui_annotate(struct hist_entry *he, int evidx)
250{
251	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0);
252}
253
254int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
255			 int refresh)
256{
257	struct objdump_line *pos, *n;
258	struct annotation *notes;
259	struct annotate_browser browser = {
260		.b = {
261			.refresh = ui_browser__list_head_refresh,
262			.seek	 = ui_browser__list_head_seek,
263			.write	 = annotate_browser__write,
264			.priv	 = sym,
265		},
266	};
267	int ret;
268
269	if (sym == NULL)
270		return -1;
271
272	if (map->dso->annotate_warned)
273		return -1;
274
275	if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
276		ui__error_window(ui_helpline__last_msg);
277		return -1;
278	}
279
280	ui_helpline__push("Press <- or ESC to exit");
281
282	notes = symbol__annotation(sym);
283
284	list_for_each_entry(pos, &notes->src->source, node) {
285		struct objdump_line_rb_node *rbpos;
286		size_t line_len = strlen(pos->line);
287
288		if (browser.b.width < line_len)
289			browser.b.width = line_len;
290		rbpos = objdump_line__rb(pos);
291		rbpos->idx = browser.b.nr_entries++;
292	}
293
294	browser.b.entries = &notes->src->source,
295	browser.b.width += 18; /* Percentage */
296	ret = annotate_browser__run(&browser, evidx, refresh);
297	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
298		list_del(&pos->node);
299		objdump_line__free(pos);
300	}
301	return ret;
302}
303