Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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
 17#define FOLDED_SIGN  '+'
 18#define UNFOLD_SIGN  '-'
 19#define NOCHLD_SIGN  ' '
 20
 21struct browser_entry {
 22	struct list_head node;
 23	struct annotated_member *data;
 24	struct type_hist_entry *hists;
 25	struct browser_entry *parent;
 26	struct list_head children;
 27	int indent;  /*indentation level, starts from 0 */
 28	int nr_entries; /* # of visible entries: self + descendents */
 29	bool folded;  /* only can be false when it has children */
 30};
 31
 32struct annotated_data_browser {
 33	struct ui_browser b;
 34	struct list_head entries;
 35	struct browser_entry *curr;
 36	int nr_events;
 37};
 38
 39static struct annotated_data_browser *get_browser(struct ui_browser *uib)
 40{
 41	return container_of(uib, struct annotated_data_browser, b);
 42}
 43
 44static void update_hist_entry(struct type_hist_entry *dst,
 45			      struct type_hist_entry *src)
 46{
 47	dst->nr_samples += src->nr_samples;
 48	dst->period += src->period;
 49}
 50
 51static int get_member_overhead(struct annotated_data_type *adt,
 52			       struct browser_entry *entry,
 53			       struct evsel *leader)
 54{
 55	struct annotated_member *member = entry->data;
 56	int i, k;
 57
 58	for (i = 0; i < member->size; i++) {
 59		struct type_hist *h;
 60		struct evsel *evsel;
 61		int offset = member->offset + i;
 62
 63		k = 0;
 64		for_each_group_evsel(evsel, leader) {
 65			if (symbol_conf.skip_empty &&
 66			    evsel__hists(evsel)->stats.nr_samples == 0)
 67				continue;
 68
 69			h = adt->histograms[evsel->core.idx];
 70			update_hist_entry(&entry->hists[k++], &h->addr[offset]);
 71		}
 72	}
 73	return 0;
 74}
 75
 76static int add_child_entries(struct annotated_data_browser *browser,
 77			     struct browser_entry *parent,
 78			     struct annotated_data_type *adt,
 79			     struct annotated_member *member,
 80			     struct evsel *evsel, int indent)
 81{
 82	struct annotated_member *pos;
 83	struct browser_entry *entry;
 84	struct list_head *parent_list;
 85
 86	entry = zalloc(sizeof(*entry));
 87	if (entry == NULL)
 88		return -1;
 89
 90	entry->hists = calloc(browser->nr_events, sizeof(*entry->hists));
 91	if (entry->hists == NULL) {
 92		free(entry);
 93		return -1;
 94	}
 95
 96	entry->data = member;
 97	entry->parent = parent;
 98	entry->indent = indent;
 99	if (get_member_overhead(adt, entry, evsel) < 0) {
100		free(entry);
101		return -1;
102	}
103
104	INIT_LIST_HEAD(&entry->children);
105	if (parent)
106		parent_list = &parent->children;
107	else
108		parent_list = &browser->entries;
109
110	list_add_tail(&entry->node, parent_list);
111
112	list_for_each_entry(pos, &member->children, node) {
113		int nr = add_child_entries(browser, entry, adt, pos, evsel,
114					   indent + 1);
115		if (nr < 0)
116			return nr;
117	}
118
119	/* add an entry for the closing bracket ("}") */
120	if (!list_empty(&member->children)) {
121		struct browser_entry *bracket;
122
123		bracket = zalloc(sizeof(*bracket));
124		if (bracket == NULL)
125			return -1;
126
127		bracket->indent = indent;
128		bracket->parent = entry;
129		bracket->folded = true;
130		bracket->nr_entries = 1;
131
132		INIT_LIST_HEAD(&bracket->children);
133		list_add_tail(&bracket->node, &entry->children);
134	}
135
136	/* fold child entries by default */
137	entry->folded = true;
138	entry->nr_entries = 1;
139	return 0;
140}
141
142static u32 count_visible_entries(struct annotated_data_browser *browser)
143{
144	int nr = 0;
145	struct browser_entry *entry;
146
147	list_for_each_entry(entry, &browser->entries, node)
148		nr += entry->nr_entries;
149
150	return nr;
151}
152
153static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser)
154{
155	struct hist_entry *he = browser->b.priv;
156	struct annotated_data_type *adt = he->mem_type;
157	struct evsel *evsel = hists_to_evsel(he->hists);
158
159	INIT_LIST_HEAD(&browser->entries);
160
161	add_child_entries(browser, /*parent=*/NULL, adt, &adt->self, evsel,
162			  /*indent=*/0);
163
164	browser->b.entries = &browser->entries;
165	browser->b.nr_entries = count_visible_entries(browser);
166	return 0;
167}
168
169static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser)
170{
171	struct browser_entry *pos, *tmp;
172
173	list_for_each_entry_safe(pos, tmp, &browser->entries, node) {
174		list_del_init(&pos->node);
175		zfree(&pos->hists);
176		free(pos);
177	}
178}
179
180static struct browser_entry *get_first_child(struct browser_entry *entry)
181{
182	if (list_empty(&entry->children))
183		return NULL;
184
185	return list_first_entry(&entry->children, struct browser_entry, node);
186}
187
188static struct browser_entry *get_last_child(struct browser_entry *entry)
189{
190	if (list_empty(&entry->children))
191		return NULL;
192
193	return list_last_entry(&entry->children, struct browser_entry, node);
194}
195
196static bool is_first_child(struct browser_entry *entry)
197{
198	/* This will be checked in a different way */
199	if (entry->parent == NULL)
200		return false;
201
202	return get_first_child(entry->parent) == entry;
203}
204
205static bool is_last_child(struct browser_entry *entry)
206{
207	/* This will be checked in a different way */
208	if (entry->parent == NULL)
209		return false;
210
211	return get_last_child(entry->parent) == entry;
212}
213
214static struct browser_entry *browser__prev_entry(struct ui_browser *uib,
215						 struct browser_entry *entry)
216{
217	struct annotated_data_browser *browser = get_browser(uib);
218	struct browser_entry *first;
219
220	first = list_first_entry(&browser->entries, struct browser_entry, node);
221
222	while (entry != first) {
223		if (is_first_child(entry))
224			entry = entry->parent;
225		else {
226			entry = list_prev_entry(entry, node);
227			while (!entry->folded)
228				entry = get_last_child(entry);
229		}
230
231		if (!uib->filter || !uib->filter(uib, &entry->node))
232			return entry;
233	}
234	return first;
235}
236
237static struct browser_entry *browser__next_entry(struct ui_browser *uib,
238						 struct browser_entry *entry)
239{
240	struct annotated_data_browser *browser = get_browser(uib);
241	struct browser_entry *last;
242
243	last = list_last_entry(&browser->entries, struct browser_entry, node);
244	while (!last->folded)
245		last = get_last_child(last);
246
247	while (entry != last) {
248		if (!entry->folded)
249			entry = get_first_child(entry);
250		else {
251			while (is_last_child(entry))
252				entry = entry->parent;
253
254			entry = list_next_entry(entry, node);
255		}
256
257		if (!uib->filter || !uib->filter(uib, &entry->node))
258			return entry;
259	}
260	return last;
261}
262
263static void browser__seek(struct ui_browser *uib, off_t offset, int whence)
264{
265	struct annotated_data_browser *browser = get_browser(uib);
266	struct browser_entry *entry;
267
268	if (uib->nr_entries == 0)
269		return;
270
271	switch (whence) {
272	case SEEK_SET:
273		entry = list_first_entry(&browser->entries, typeof(*entry), node);
274		if (uib->filter && uib->filter(uib, &entry->node))
275			entry = browser__next_entry(uib, entry);
276		break;
277	case SEEK_CUR:
278		entry = list_entry(uib->top, typeof(*entry), node);
279		break;
280	case SEEK_END:
281		entry = list_last_entry(&browser->entries, typeof(*entry), node);
282		while (!entry->folded)
283			entry = get_last_child(entry);
284		if (uib->filter && uib->filter(uib, &entry->node))
285			entry = browser__prev_entry(uib, entry);
286		break;
287	default:
288		return;
289	}
290
291	assert(entry != NULL);
292
293	if (offset > 0) {
294		while (offset-- != 0)
295			entry = browser__next_entry(uib, entry);
296	} else {
297		while (offset++ != 0)
298			entry = browser__prev_entry(uib, entry);
299	}
300
301	uib->top = &entry->node;
302}
303
304static unsigned int browser__refresh(struct ui_browser *uib)
305{
306	struct annotated_data_browser *browser = get_browser(uib);
307	struct browser_entry *entry, *next;
308	int row = 0;
309
310	if (uib->top == NULL || uib->top == uib->entries)
311		browser__seek(uib, SEEK_SET, 0);
312
313	entry = list_entry(uib->top, typeof(*entry), node);
314
315	while (true) {
316		if (!uib->filter || !uib->filter(uib, &entry->node)) {
317			ui_browser__gotorc(uib, row, 0);
318			uib->write(uib, entry, row);
319			if (uib->top_idx + row == uib->index)
320				browser->curr = entry;
321			if (++row == uib->rows)
322				break;
323		}
324		next = browser__next_entry(uib, entry);
325		if (next == entry)
326			break;
327
328		entry = next;
329	}
330
331	return row;
332}
333
334static int browser__show(struct ui_browser *uib)
335{
336	struct hist_entry *he = uib->priv;
337	struct annotated_data_type *adt = he->mem_type;
338	struct annotated_data_browser *browser = get_browser(uib);
339	const char *help = "Press 'h' for help on key bindings";
340	char title[256];
341
342	snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)",
343		 adt->self.type_name, he->stat.nr_events);
344
345	if (ui_browser__show(uib, title, help) < 0)
346		return -1;
347
348	/* second line header */
349	ui_browser__gotorc_title(uib, 0, 0);
350	ui_browser__set_color(uib, HE_COLORSET_ROOT);
351
352	if (symbol_conf.show_total_period)
353		strcpy(title, "Period");
354	else if (symbol_conf.show_nr_samples)
355		strcpy(title, "Samples");
356	else
357		strcpy(title, "Percent");
358
359	ui_browser__printf(uib, "%*s %10s %10s %10s  %s",
360			   2 + 11 * (browser->nr_events - 1), "",
361			   title, "Offset", "Size", "Field");
362	ui_browser__write_nstring(uib, "", uib->width);
363	return 0;
364}
365
366static void browser__write_overhead(struct ui_browser *uib,
367				    struct type_hist *total,
368				    struct type_hist_entry *hist, int row)
369{
370	u64 period = hist->period;
371	double percent = total->period ? (100.0 * period / total->period) : 0;
372	bool current = ui_browser__is_current_entry(uib, row);
373	int nr_samples = 0;
374
375	ui_browser__set_percent_color(uib, percent, current);
376
377	if (symbol_conf.show_total_period)
378		ui_browser__printf(uib, " %10" PRIu64, period);
379	else if (symbol_conf.show_nr_samples)
380		ui_browser__printf(uib, " %10d", nr_samples);
381	else
382		ui_browser__printf(uib, " %10.2f", percent);
383
384	ui_browser__set_percent_color(uib, 0, current);
385}
386
387static void browser__write(struct ui_browser *uib, void *entry, int row)
388{
389	struct annotated_data_browser *browser = get_browser(uib);
390	struct browser_entry *be = entry;
391	struct annotated_member *member = be->data;
392	struct hist_entry *he = uib->priv;
393	struct annotated_data_type *adt = he->mem_type;
394	struct evsel *leader = hists_to_evsel(he->hists);
395	struct evsel *evsel;
396	int idx = 0;
397	bool current = ui_browser__is_current_entry(uib, row);
398
399	if (member == NULL) {
400		/* print the closing bracket */
401		ui_browser__set_percent_color(uib, 0, current);
402		ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
403		ui_browser__write_nstring(uib, "", 11 * browser->nr_events);
404		ui_browser__printf(uib, " %10s %10s  %*s};",
405				   "", "", be->indent * 4, "");
406		ui_browser__write_nstring(uib, "", uib->width);
407		return;
408	}
409
410	ui_browser__set_percent_color(uib, 0, current);
411
412	if (!list_empty(&be->children))
413		ui_browser__printf(uib, "%c ", be->folded ? FOLDED_SIGN : UNFOLD_SIGN);
414	else
415		ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
416
417	/* print the number */
418	for_each_group_evsel(evsel, leader) {
419		struct type_hist *h = adt->histograms[evsel->core.idx];
420
421		if (symbol_conf.skip_empty &&
422		    evsel__hists(evsel)->stats.nr_samples == 0)
423			continue;
424
425		browser__write_overhead(uib, h, &be->hists[idx++], row);
426	}
427
428	/* print type info */
429	if (be->indent == 0 && !member->var_name) {
430		ui_browser__printf(uib, " %#10x %#10x  %s%s",
431				   member->offset, member->size,
432				   member->type_name,
433				   list_empty(&member->children) || be->folded? ";" : " {");
434	} else {
435		ui_browser__printf(uib, " %#10x %#10x  %*s%s\t%s%s",
436				   member->offset, member->size,
437				   be->indent * 4, "", member->type_name,
438				   member->var_name ?: "",
439				   list_empty(&member->children) || be->folded ? ";" : " {");
440	}
441	/* fill the rest */
442	ui_browser__write_nstring(uib, "", uib->width);
443}
444
445static void annotated_data_browser__fold(struct annotated_data_browser *browser,
446					 struct browser_entry *entry,
447					 bool recursive)
448{
449	struct browser_entry *child;
450
451	if (list_empty(&entry->children))
452		return;
453	if (entry->folded && !recursive)
454		return;
455
456	if (recursive) {
457		list_for_each_entry(child, &entry->children, node)
458			annotated_data_browser__fold(browser, child, true);
459	}
460
461	entry->nr_entries = 1;
462	entry->folded = true;
463}
464
465static void annotated_data_browser__unfold(struct annotated_data_browser *browser,
466					   struct browser_entry *entry,
467					   bool recursive)
468{
469	struct browser_entry *child;
470	int nr_entries;
471
472	if (list_empty(&entry->children))
473		return;
474	if (!entry->folded && !recursive)
475		return;
476
477	nr_entries = 1; /* for self */
478	list_for_each_entry(child, &entry->children, node) {
479		if (recursive)
480			annotated_data_browser__unfold(browser, child, true);
481
482		nr_entries += child->nr_entries;
483	}
484
485	entry->nr_entries = nr_entries;
486	entry->folded = false;
487}
488
489static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser,
490						bool recursive)
491{
492	struct browser_entry *curr = browser->curr;
493	struct browser_entry *parent;
494
495	parent = curr->parent;
496	while (parent) {
497		parent->nr_entries -= curr->nr_entries;
498		parent = parent->parent;
499	}
500	browser->b.nr_entries -= curr->nr_entries;
501
502	if (curr->folded)
503		annotated_data_browser__unfold(browser, curr, recursive);
504	else
505		annotated_data_browser__fold(browser, curr, recursive);
506
507	parent = curr->parent;
508	while (parent) {
509		parent->nr_entries += curr->nr_entries;
510		parent = parent->parent;
511	}
512	browser->b.nr_entries += curr->nr_entries;
513
514	assert(browser->b.nr_entries == count_visible_entries(browser));
515}
516
517static int annotated_data_browser__run(struct annotated_data_browser *browser,
518				       struct evsel *evsel __maybe_unused,
519				       struct hist_browser_timer *hbt)
520{
521	int delay_secs = hbt ? hbt->refresh : 0;
522	int key;
523
524	if (browser__show(&browser->b) < 0)
525		return -1;
526
527	while (1) {
528		key = ui_browser__run(&browser->b, delay_secs);
529
530		switch (key) {
531		case K_TIMER:
532			if (hbt)
533				hbt->timer(hbt->arg);
534			continue;
535		case K_F1:
536		case 'h':
537			ui_browser__help_window(&browser->b,
538		"UP/DOWN/PGUP\n"
539		"PGDN/SPACE    Navigate\n"
540		"</>           Move to prev/next symbol\n"
541		"e             Expand/Collapse current entry\n"
542		"E             Expand/Collapse all children of the current\n"
543		"q/ESC/CTRL+C  Exit\n\n");
544			continue;
545		case 'e':
546			annotated_data_browser__toggle_fold(browser,
547							    /*recursive=*/false);
548			break;
549		case 'E':
550			annotated_data_browser__toggle_fold(browser,
551							    /*recursive=*/true);
552			break;
553		case K_LEFT:
554		case '<':
555		case '>':
556		case K_ESC:
557		case 'q':
558		case CTRL('c'):
559			goto out;
560		default:
561			continue;
562		}
563	}
564out:
565	ui_browser__hide(&browser->b);
566	return key;
567}
568
569int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel,
570				  struct hist_browser_timer *hbt)
571{
572	struct annotated_data_browser browser = {
573		.b = {
574			.refresh = browser__refresh,
575			.seek	 = browser__seek,
576			.write	 = browser__write,
577			.priv	 = he,
578			.extra_title_lines = 1,
579		},
580		.nr_events = 1,
581	};
582	int ret;
583
584	ui_helpline__push("Press ESC to exit");
585
586	if (evsel__is_group_event(evsel)) {
587		struct evsel *pos;
588		int nr = 0;
589
590		for_each_group_evsel(pos, evsel) {
591			if (!symbol_conf.skip_empty ||
592			    evsel__hists(pos)->stats.nr_samples)
593				nr++;
594		}
595		browser.nr_events = nr;
596	}
597
598	ret = annotated_data_browser__collect_entries(&browser);
599	if (ret < 0)
600		goto out;
601
602	/* To get the top and current entry */
603	browser__refresh(&browser.b);
604	/* Show the first-level child entries by default */
605	annotated_data_browser__toggle_fold(&browser, /*recursive=*/false);
606
607	ret = annotated_data_browser__run(&browser, evsel, hbt);
608
609out:
610	annotated_data_browser__delete_entries(&browser);
611
612	return ret;
613}