xref: /openbmc/linux/tools/perf/util/record.c (revision 94c7b6fc)
1 #include "evlist.h"
2 #include "evsel.h"
3 #include "cpumap.h"
4 #include "parse-events.h"
5 #include <api/fs/fs.h>
6 #include "util.h"
7 
8 typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel);
9 
10 static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
11 {
12 	struct perf_evlist *evlist;
13 	struct perf_evsel *evsel;
14 	int err = -EAGAIN, fd;
15 
16 	evlist = perf_evlist__new();
17 	if (!evlist)
18 		return -ENOMEM;
19 
20 	if (parse_events(evlist, str))
21 		goto out_delete;
22 
23 	evsel = perf_evlist__first(evlist);
24 
25 	fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0);
26 	if (fd < 0)
27 		goto out_delete;
28 	close(fd);
29 
30 	fn(evsel);
31 
32 	fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0);
33 	if (fd < 0) {
34 		if (errno == EINVAL)
35 			err = -EINVAL;
36 		goto out_delete;
37 	}
38 	close(fd);
39 	err = 0;
40 
41 out_delete:
42 	perf_evlist__delete(evlist);
43 	return err;
44 }
45 
46 static bool perf_probe_api(setup_probe_fn_t fn)
47 {
48 	const char *try[] = {"cycles:u", "instructions:u", "cpu-clock", NULL};
49 	struct cpu_map *cpus;
50 	int cpu, ret, i = 0;
51 
52 	cpus = cpu_map__new(NULL);
53 	if (!cpus)
54 		return false;
55 	cpu = cpus->map[0];
56 	cpu_map__delete(cpus);
57 
58 	do {
59 		ret = perf_do_probe_api(fn, cpu, try[i++]);
60 		if (!ret)
61 			return true;
62 	} while (ret == -EAGAIN && try[i]);
63 
64 	return false;
65 }
66 
67 static void perf_probe_sample_identifier(struct perf_evsel *evsel)
68 {
69 	evsel->attr.sample_type |= PERF_SAMPLE_IDENTIFIER;
70 }
71 
72 bool perf_can_sample_identifier(void)
73 {
74 	return perf_probe_api(perf_probe_sample_identifier);
75 }
76 
77 void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts)
78 {
79 	struct perf_evsel *evsel;
80 	bool use_sample_identifier = false;
81 
82 	/*
83 	 * Set the evsel leader links before we configure attributes,
84 	 * since some might depend on this info.
85 	 */
86 	if (opts->group)
87 		perf_evlist__set_leader(evlist);
88 
89 	if (evlist->cpus->map[0] < 0)
90 		opts->no_inherit = true;
91 
92 	evlist__for_each(evlist, evsel)
93 		perf_evsel__config(evsel, opts);
94 
95 	if (evlist->nr_entries > 1) {
96 		struct perf_evsel *first = perf_evlist__first(evlist);
97 
98 		evlist__for_each(evlist, evsel) {
99 			if (evsel->attr.sample_type == first->attr.sample_type)
100 				continue;
101 			use_sample_identifier = perf_can_sample_identifier();
102 			break;
103 		}
104 		evlist__for_each(evlist, evsel)
105 			perf_evsel__set_sample_id(evsel, use_sample_identifier);
106 	}
107 
108 	perf_evlist__set_id_pos(evlist);
109 }
110 
111 static int get_max_rate(unsigned int *rate)
112 {
113 	char path[PATH_MAX];
114 	const char *procfs = procfs__mountpoint();
115 
116 	if (!procfs)
117 		return -1;
118 
119 	snprintf(path, PATH_MAX,
120 		 "%s/sys/kernel/perf_event_max_sample_rate", procfs);
121 
122 	return filename__read_int(path, (int *) rate);
123 }
124 
125 static int record_opts__config_freq(struct record_opts *opts)
126 {
127 	bool user_freq = opts->user_freq != UINT_MAX;
128 	unsigned int max_rate;
129 
130 	if (opts->user_interval != ULLONG_MAX)
131 		opts->default_interval = opts->user_interval;
132 	if (user_freq)
133 		opts->freq = opts->user_freq;
134 
135 	/*
136 	 * User specified count overrides default frequency.
137 	 */
138 	if (opts->default_interval)
139 		opts->freq = 0;
140 	else if (opts->freq) {
141 		opts->default_interval = opts->freq;
142 	} else {
143 		pr_err("frequency and count are zero, aborting\n");
144 		return -1;
145 	}
146 
147 	if (get_max_rate(&max_rate))
148 		return 0;
149 
150 	/*
151 	 * User specified frequency is over current maximum.
152 	 */
153 	if (user_freq && (max_rate < opts->freq)) {
154 		pr_err("Maximum frequency rate (%u) reached.\n"
155 		   "Please use -F freq option with lower value or consider\n"
156 		   "tweaking /proc/sys/kernel/perf_event_max_sample_rate.\n",
157 		   max_rate);
158 		return -1;
159 	}
160 
161 	/*
162 	 * Default frequency is over current maximum.
163 	 */
164 	if (max_rate < opts->freq) {
165 		pr_warning("Lowering default frequency rate to %u.\n"
166 			   "Please consider tweaking "
167 			   "/proc/sys/kernel/perf_event_max_sample_rate.\n",
168 			   max_rate);
169 		opts->freq = max_rate;
170 	}
171 
172 	return 0;
173 }
174 
175 int record_opts__config(struct record_opts *opts)
176 {
177 	return record_opts__config_freq(opts);
178 }
179 
180 bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
181 {
182 	struct perf_evlist *temp_evlist;
183 	struct perf_evsel *evsel;
184 	int err, fd, cpu;
185 	bool ret = false;
186 
187 	temp_evlist = perf_evlist__new();
188 	if (!temp_evlist)
189 		return false;
190 
191 	err = parse_events(temp_evlist, str);
192 	if (err)
193 		goto out_delete;
194 
195 	evsel = perf_evlist__last(temp_evlist);
196 
197 	if (!evlist || cpu_map__empty(evlist->cpus)) {
198 		struct cpu_map *cpus = cpu_map__new(NULL);
199 
200 		cpu =  cpus ? cpus->map[0] : 0;
201 		cpu_map__delete(cpus);
202 	} else {
203 		cpu = evlist->cpus->map[0];
204 	}
205 
206 	fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0);
207 	if (fd >= 0) {
208 		close(fd);
209 		ret = true;
210 	}
211 
212 out_delete:
213 	perf_evlist__delete(temp_evlist);
214 	return ret;
215 }
216