summaryrefslogtreecommitdiff
path: root/tools/perf/util/ui/browsers/top.c
blob: 88403cf8396ad6294d4be6585fdfd92eaf5b4a6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*
 * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
 *
 * Parts came from builtin-{top,stat,record}.c, see those files for further
 * copyright notes.
 *
 * Released under the GPL v2. (and only v2, not any later version)
 */
#include "../browser.h"
#include "../../annotate.h"
#include "../helpline.h"
#include "../libslang.h"
#include "../util.h"
#include "../../evlist.h"
#include "../../hist.h"
#include "../../sort.h"
#include "../../symbol.h"
#include "../../top.h"

struct perf_top_browser {
	struct ui_browser b;
	struct rb_root	  root;
	struct sym_entry  *selection;
	float		  sum_ksamples;
	int		  dso_width;
	int		  dso_short_width;
	int		  sym_width;
};

static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row)
{
	struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b);
	struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node);
	bool current_entry = ui_browser__is_current_entry(browser, row);
	struct symbol *symbol = sym_entry__symbol(syme);
	struct perf_top *top = browser->priv;
	int width = browser->width;
	double pcnt;

	pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) /
				 top_browser->sum_ksamples));
	ui_browser__set_percent_color(browser, pcnt, current_entry);

	if (top->evlist->nr_entries == 1 || !top->display_weighted) {
		slsmg_printf("%20.2f ", syme->weight);
		width -= 24;
	} else {
		slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count);
		width -= 23;
	}

	slsmg_printf("%4.1f%%", pcnt);
	width -= 7;

	if (verbose) {
		slsmg_printf(" %016" PRIx64, symbol->start);
		width -= 17;
	}

	slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width,
		     symbol->name);
	width -= top_browser->sym_width;
	slsmg_write_nstring(width >= syme->map->dso->long_name_len ?
				syme->map->dso->long_name :
				syme->map->dso->short_name, width);

	if (current_entry)
		top_browser->selection = syme;
}

static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser)
{
	struct perf_top *top = browser->b.priv;
	u64 top_idx = browser->b.top_idx;

	browser->root = RB_ROOT;
	browser->b.top = NULL;
	browser->sum_ksamples = perf_top__decay_samples(top, &browser->root);
	/*
 	 * No active symbols
 	 */
	if (top->rb_entries == 0)
		return;

	perf_top__find_widths(top, &browser->root, &browser->dso_width,
			      &browser->dso_short_width,
                              &browser->sym_width);
	if (browser->sym_width + browser->dso_width > browser->b.width - 29) {
		browser->dso_width = browser->dso_short_width;
		if (browser->sym_width + browser->dso_width > browser->b.width - 29)
			browser->sym_width = browser->b.width - browser->dso_width - 29;
	}

	/*
	 * Adjust the ui_browser indexes since the entries in the browser->root
	 * rb_tree may have changed, then seek it from start, so that we get a
	 * possible new top of the screen.
 	 */
	browser->b.nr_entries = top->rb_entries;

	if (top_idx >= browser->b.nr_entries) {
		if (browser->b.height >= browser->b.nr_entries)
			top_idx = browser->b.nr_entries - browser->b.height;
		else
			top_idx = 0;
	}

	if (browser->b.index >= top_idx + browser->b.height)
		browser->b.index = top_idx + browser->b.index - browser->b.top_idx;

	if (browser->b.index >= browser->b.nr_entries)
		browser->b.index = browser->b.nr_entries - 1;

	browser->b.top_idx = top_idx;
	browser->b.seek(&browser->b, top_idx, SEEK_SET);
}

static void perf_top_browser__annotate(struct perf_top_browser *browser)
{
	struct sym_entry *syme = browser->selection;
	struct symbol *sym = sym_entry__symbol(syme);
	struct annotation *notes = symbol__annotation(sym);
	struct perf_top *top = browser->b.priv;

	if (notes->src != NULL)
		goto do_annotation;

	pthread_mutex_lock(&notes->lock);

	top->sym_filter_entry = NULL;

	if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) {
		pr_err("Not enough memory for annotating '%s' symbol!\n",
		       sym->name);
		pthread_mutex_unlock(&notes->lock);
		return;
	}

	top->sym_filter_entry = syme;

	pthread_mutex_unlock(&notes->lock);
do_annotation:
	symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000);
}

static int perf_top_browser__run(struct perf_top_browser *browser)
{
	int key;
	char title[160];
	struct perf_top *top = browser->b.priv;
	int delay_msecs = top->delay_secs * 1000;
	int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, };

	perf_top_browser__update_rb_tree(browser);
        perf_top__header_snprintf(top, title, sizeof(title));
        perf_top__reset_sample_counters(top);

	if (ui_browser__show(&browser->b, title,
			     "ESC: exit, ENTER|->|a: Live Annotate") < 0)
		return -1;

	newtFormSetTimer(browser->b.form, delay_msecs);
	ui_browser__add_exit_keys(&browser->b, exit_keys);

	while (1) {
		key = ui_browser__run(&browser->b);

		switch (key) {
		case -1:
			/* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
			perf_top_browser__update_rb_tree(browser);
			perf_top__header_snprintf(top, title, sizeof(title));
			perf_top__reset_sample_counters(top);
			ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT);
			SLsmg_gotorc(0, 0);
			slsmg_write_nstring(title, browser->b.width);
			break;
		case 'a':
		case NEWT_KEY_RIGHT:
		case NEWT_KEY_ENTER:
			if (browser->selection)
				perf_top_browser__annotate(browser);
			break;
		case NEWT_KEY_LEFT:
			continue;
		case NEWT_KEY_ESCAPE:
			if (!ui__dialog_yesno("Do you really want to exit?"))
				continue;
			/* Fall thru */
		default:
			goto out;
		}
	}
out:
	ui_browser__hide(&browser->b);
	return key;
}

int perf_top__tui_browser(struct perf_top *top)
{
	struct perf_top_browser browser = {
		.b = {
			.entries = &browser.root,
			.refresh = ui_browser__rb_tree_refresh,
			.seek	 = ui_browser__rb_tree_seek,
			.write	 = perf_top_browser__write,
			.priv	 = top,
		},
	};

	return perf_top_browser__run(&browser);
}