1// SPDX-License-Identifier: GPL-2.0
2#include <inttypes.h>
3#include <string.h>
4#include <linux/zalloc.h>
5#include <sys/ttydefaults.h>
6
7#include "ui/browser.h"
8#include "ui/helpline.h"
9#include "ui/keysyms.h"
10#include "ui/ui.h"
11#include "util/annotate.h"
12#include "util/annotate-data.h"
13#include "util/evsel.h"
14#include "util/evlist.h"
15#include "util/sort.h"
16
17struct annotated_data_browser {
18	struct ui_browser b;
19	struct list_head entries;
20	int nr_events;
21};
22
23struct browser_entry {
24	struct list_head node;
25	struct annotated_member *data;
26	struct type_hist_entry *hists;
27	int indent;
28};
29
30static struct annotated_data_browser *get_browser(struct ui_browser *uib)
31{
32	return container_of(uib, struct annotated_data_browser, b);
33}
34
35static void update_hist_entry(struct type_hist_entry *dst,
36			      struct type_hist_entry *src)
37{
38	dst->nr_samples += src->nr_samples;
39	dst->period += src->period;
40}
41
42static int get_member_overhead(struct annotated_data_type *adt,
43			       struct browser_entry *entry,
44			       struct evsel *leader)
45{
46	struct annotated_member *member = entry->data;
47	int i, k;
48
49	for (i = 0; i < member->size; i++) {
50		struct type_hist *h;
51		struct evsel *evsel;
52		int offset = member->offset + i;
53
54		for_each_group_evsel(evsel, leader) {
55			h = adt->histograms[evsel->core.idx];
56			k = evsel__group_idx(evsel);
57			update_hist_entry(&entry->hists[k], &h->addr[offset]);
58		}
59	}
60	return 0;
61}
62
63static int add_child_entries(struct annotated_data_browser *browser,
64			     struct annotated_data_type *adt,
65			     struct annotated_member *member,
66			     struct evsel *evsel, int indent)
67{
68	struct annotated_member *pos;
69	struct browser_entry *entry;
70	int nr_entries = 0;
71
72	entry = zalloc(sizeof(*entry));
73	if (entry == NULL)
74		return -1;
75
76	entry->hists = calloc(browser->nr_events, sizeof(*entry->hists));
77	if (entry->hists == NULL) {
78		free(entry);
79		return -1;
80	}
81
82	entry->data = member;
83	entry->indent = indent;
84	if (get_member_overhead(adt, entry, evsel) < 0) {
85		free(entry);
86		return -1;
87	}
88
89	list_add_tail(&entry->node, &browser->entries);
90	nr_entries++;
91
92	list_for_each_entry(pos, &member->children, node) {
93		int nr = add_child_entries(browser, adt, pos, evsel, indent + 1);
94
95		if (nr < 0)
96			return nr;
97
98		nr_entries += nr;
99	}
100
101	/* add an entry for the closing bracket ("}") */
102	if (!list_empty(&member->children)) {
103		entry = zalloc(sizeof(*entry));
104		if (entry == NULL)
105			return -1;
106
107		entry->indent = indent;
108		list_add_tail(&entry->node, &browser->entries);
109		nr_entries++;
110	}
111
112	return nr_entries;
113}
114
115static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser)
116{
117	struct hist_entry *he = browser->b.priv;
118	struct annotated_data_type *adt = he->mem_type;
119	struct evsel *evsel = hists_to_evsel(he->hists);
120
121	INIT_LIST_HEAD(&browser->entries);
122	browser->b.entries = &browser->entries;
123	browser->b.nr_entries = add_child_entries(browser, adt, &adt->self,
124						  evsel, /*indent=*/0);
125	return 0;
126}
127
128static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser)
129{
130	struct browser_entry *pos, *tmp;
131
132	list_for_each_entry_safe(pos, tmp, &browser->entries, node) {
133		list_del_init(&pos->node);
134		zfree(&pos->hists);
135		free(pos);
136	}
137}
138
139static unsigned int browser__refresh(struct ui_browser *uib)
140{
141	return ui_browser__list_head_refresh(uib);
142}
143
144static int browser__show(struct ui_browser *uib)
145{
146	struct hist_entry *he = uib->priv;
147	struct annotated_data_type *adt = he->mem_type;
148	struct annotated_data_browser *browser = get_browser(uib);
149	const char *help = "Press 'h' for help on key bindings";
150	char title[256];
151
152	snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)",
153		 adt->self.type_name, he->stat.nr_events);
154
155	if (ui_browser__show(uib, title, help) < 0)
156		return -1;
157
158	/* second line header */
159	ui_browser__gotorc_title(uib, 0, 0);
160	ui_browser__set_color(uib, HE_COLORSET_ROOT);
161
162	if (symbol_conf.show_total_period)
163		strcpy(title, "Period");
164	else if (symbol_conf.show_nr_samples)
165		strcpy(title, "Samples");
166	else
167		strcpy(title, "Percent");
168
169	ui_browser__printf(uib, "%*s %10s %10s %10s  %s",
170			   11 * (browser->nr_events - 1), "",
171			   title, "Offset", "Size", "Field");
172	ui_browser__write_nstring(uib, "", uib->width);
173	return 0;
174}
175
176static void browser__write_overhead(struct ui_browser *uib,
177				    struct type_hist *total,
178				    struct type_hist_entry *hist, int row)
179{
180	u64 period = hist->period;
181	double percent = total->period ? (100.0 * period / total->period) : 0;
182	bool current = ui_browser__is_current_entry(uib, row);
183	int nr_samples = 0;
184
185	ui_browser__set_percent_color(uib, percent, current);
186
187	if (symbol_conf.show_total_period)
188		ui_browser__printf(uib, " %10" PRIu64, period);
189	else if (symbol_conf.show_nr_samples)
190		ui_browser__printf(uib, " %10d", nr_samples);
191	else
192		ui_browser__printf(uib, " %10.2f", percent);
193
194	ui_browser__set_percent_color(uib, 0, current);
195}
196
197static void browser__write(struct ui_browser *uib, void *entry, int row)
198{
199	struct annotated_data_browser *browser = get_browser(uib);
200	struct browser_entry *be = entry;
201	struct annotated_member *member = be->data;
202	struct hist_entry *he = uib->priv;
203	struct annotated_data_type *adt = he->mem_type;
204	struct evsel *leader = hists_to_evsel(he->hists);
205	struct evsel *evsel;
206
207	if (member == NULL) {
208		bool current = ui_browser__is_current_entry(uib, row);
209
210		/* print the closing bracket */
211		ui_browser__set_percent_color(uib, 0, current);
212		ui_browser__write_nstring(uib, "", 11 * browser->nr_events);
213		ui_browser__printf(uib, " %10s %10s  %*s};",
214				   "", "", be->indent * 4, "");
215		ui_browser__write_nstring(uib, "", uib->width);
216		return;
217	}
218
219	/* print the number */
220	for_each_group_evsel(evsel, leader) {
221		struct type_hist *h = adt->histograms[evsel->core.idx];
222		int idx = evsel__group_idx(evsel);
223
224		browser__write_overhead(uib, h, &be->hists[idx], row);
225	}
226
227	/* print type info */
228	if (be->indent == 0 && !member->var_name) {
229		ui_browser__printf(uib, " %10d %10d  %s%s",
230				   member->offset, member->size,
231				   member->type_name,
232				   list_empty(&member->children) ? ";" : " {");
233	} else {
234		ui_browser__printf(uib, " %10d %10d  %*s%s\t%s%s",
235				   member->offset, member->size,
236				   be->indent * 4, "", member->type_name,
237				   member->var_name ?: "",
238				   list_empty(&member->children) ? ";" : " {");
239	}
240	/* fill the rest */
241	ui_browser__write_nstring(uib, "", uib->width);
242}
243
244static int annotated_data_browser__run(struct annotated_data_browser *browser,
245				       struct evsel *evsel __maybe_unused,
246				       struct hist_browser_timer *hbt)
247{
248	int delay_secs = hbt ? hbt->refresh : 0;
249	int key;
250
251	if (browser__show(&browser->b) < 0)
252		return -1;
253
254	while (1) {
255		key = ui_browser__run(&browser->b, delay_secs);
256
257		switch (key) {
258		case K_TIMER:
259			if (hbt)
260				hbt->timer(hbt->arg);
261			continue;
262		case K_F1:
263		case 'h':
264			ui_browser__help_window(&browser->b,
265		"UP/DOWN/PGUP\n"
266		"PGDN/SPACE    Navigate\n"
267		"</>           Move to prev/next symbol\n"
268		"q/ESC/CTRL+C  Exit\n\n");
269			continue;
270		case K_LEFT:
271		case '<':
272		case '>':
273		case K_ESC:
274		case 'q':
275		case CTRL('c'):
276			goto out;
277		default:
278			continue;
279		}
280	}
281out:
282	ui_browser__hide(&browser->b);
283	return key;
284}
285
286int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel,
287				  struct hist_browser_timer *hbt)
288{
289	struct annotated_data_browser browser = {
290		.b = {
291			.refresh = browser__refresh,
292			.seek	 = ui_browser__list_head_seek,
293			.write	 = browser__write,
294			.priv	 = he,
295			.extra_title_lines = 1,
296		},
297		.nr_events = 1,
298	};
299	int ret;
300
301	ui_helpline__push("Press ESC to exit");
302
303	if (evsel__is_group_event(evsel))
304		browser.nr_events = evsel->core.nr_members;
305
306	ret = annotated_data_browser__collect_entries(&browser);
307	if (ret == 0)
308		ret = annotated_data_browser__run(&browser, evsel, hbt);
309
310	annotated_data_browser__delete_entries(&browser);
311
312	return ret;
313}
314