Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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}