xref: /linux/tools/perf/util/tool_pmu.c (revision 0709a82c10bb98d0426dd289a6b8c143f61a8786)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include "cgroup.h"
3 #include "counts.h"
4 #include "evsel.h"
5 #include "pmu.h"
6 #include "print-events.h"
7 #include "time-utils.h"
8 #include "tool_pmu.h"
9 #include <api/io.h>
10 #include <internal/threadmap.h>
11 #include <perf/threadmap.h>
12 #include <fcntl.h>
13 #include <strings.h>
14 
15 static const char *const tool_pmu__event_names[TOOL_PMU__EVENT_MAX] = {
16 	NULL,
17 	"duration_time",
18 	"user_time",
19 	"system_time",
20 };
21 
22 
23 const char *perf_tool_event__to_str(enum tool_pmu_event ev)
24 {
25 	if (ev > TOOL_PMU__EVENT_NONE && ev < TOOL_PMU__EVENT_MAX)
26 		return tool_pmu__event_names[ev];
27 
28 	return NULL;
29 }
30 
31 enum tool_pmu_event perf_tool_event__from_str(const char *str)
32 {
33 	int i;
34 
35 	perf_tool_event__for_each_event(i) {
36 		if (!strcasecmp(str, tool_pmu__event_names[i]))
37 			return i;
38 	}
39 	return TOOL_PMU__EVENT_NONE;
40 }
41 
42 static int tool_pmu__config_term(struct perf_event_attr *attr,
43 				 struct parse_events_term *term,
44 				 struct parse_events_error *err)
45 {
46 	if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
47 		enum tool_pmu_event ev = perf_tool_event__from_str(term->config);
48 
49 		if (ev == TOOL_PMU__EVENT_NONE)
50 			goto err_out;
51 
52 		attr->config = ev;
53 		return 0;
54 	}
55 err_out:
56 	if (err) {
57 		char *err_str;
58 
59 		parse_events_error__handle(err, term->err_val,
60 					asprintf(&err_str,
61 						"unexpected tool event term (%s) %s",
62 						parse_events__term_type_str(term->type_term),
63 						term->config) < 0
64 					? strdup("unexpected tool event term")
65 					: err_str,
66 					NULL);
67 	}
68 	return -EINVAL;
69 }
70 
71 int tool_pmu__config_terms(struct perf_event_attr *attr,
72 			   struct parse_events_terms *terms,
73 			   struct parse_events_error *err)
74 {
75 	struct parse_events_term *term;
76 
77 	list_for_each_entry(term, &terms->terms, list) {
78 		if (tool_pmu__config_term(attr, term, err))
79 			return -EINVAL;
80 	}
81 
82 	return 0;
83 
84 }
85 
86 int tool_pmu__for_each_event_cb(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
87 {
88 	struct pmu_event_info info = {
89 		.pmu = pmu,
90 		.event_type_desc = "Tool event",
91 	};
92 	int i;
93 
94 	perf_tool_event__for_each_event(i) {
95 		int ret;
96 
97 		info.name = perf_tool_event__to_str(i);
98 		info.alias = NULL;
99 		info.scale_unit = NULL;
100 		info.desc = NULL;
101 		info.long_desc = NULL;
102 		info.encoding_desc = NULL;
103 		info.topic = NULL;
104 		info.pmu_name = pmu->name;
105 		info.deprecated = false;
106 		ret = cb(state, &info);
107 		if (ret)
108 			return ret;
109 	}
110 	return 0;
111 }
112 
113 bool perf_pmu__is_tool(const struct perf_pmu *pmu)
114 {
115 	return pmu && pmu->type == PERF_PMU_TYPE_TOOL;
116 }
117 
118 bool evsel__is_tool(const struct evsel *evsel)
119 {
120 	return perf_pmu__is_tool(evsel->pmu);
121 }
122 
123 enum tool_pmu_event evsel__tool_event(const struct evsel *evsel)
124 {
125 	if (!evsel__is_tool(evsel))
126 		return TOOL_PMU__EVENT_NONE;
127 
128 	return (enum tool_pmu_event)evsel->core.attr.config;
129 }
130 
131 const char *evsel__tool_pmu_event_name(const struct evsel *evsel)
132 {
133 	return perf_tool_event__to_str(evsel->core.attr.config);
134 }
135 
136 static bool read_until_char(struct io *io, char e)
137 {
138 	int c;
139 
140 	do {
141 		c = io__get_char(io);
142 		if (c == -1)
143 			return false;
144 	} while (c != e);
145 	return true;
146 }
147 
148 static int read_stat_field(int fd, struct perf_cpu cpu, int field, __u64 *val)
149 {
150 	char buf[256];
151 	struct io io;
152 	int i;
153 
154 	io__init(&io, fd, buf, sizeof(buf));
155 
156 	/* Skip lines to relevant CPU. */
157 	for (i = -1; i < cpu.cpu; i++) {
158 		if (!read_until_char(&io, '\n'))
159 			return -EINVAL;
160 	}
161 	/* Skip to "cpu". */
162 	if (io__get_char(&io) != 'c') return -EINVAL;
163 	if (io__get_char(&io) != 'p') return -EINVAL;
164 	if (io__get_char(&io) != 'u') return -EINVAL;
165 
166 	/* Skip N of cpuN. */
167 	if (!read_until_char(&io, ' '))
168 		return -EINVAL;
169 
170 	i = 1;
171 	while (true) {
172 		if (io__get_dec(&io, val) != ' ')
173 			break;
174 		if (field == i)
175 			return 0;
176 		i++;
177 	}
178 	return -EINVAL;
179 }
180 
181 static int read_pid_stat_field(int fd, int field, __u64 *val)
182 {
183 	char buf[256];
184 	struct io io;
185 	int c, i;
186 
187 	io__init(&io, fd, buf, sizeof(buf));
188 	if (io__get_dec(&io, val) != ' ')
189 		return -EINVAL;
190 	if (field == 1)
191 		return 0;
192 
193 	/* Skip comm. */
194 	if (io__get_char(&io) != '(' || !read_until_char(&io, ')'))
195 		return -EINVAL;
196 	if (field == 2)
197 		return -EINVAL; /* String can't be returned. */
198 
199 	/* Skip state */
200 	if (io__get_char(&io) != ' ' || io__get_char(&io) == -1)
201 		return -EINVAL;
202 	if (field == 3)
203 		return -EINVAL; /* String can't be returned. */
204 
205 	/* Loop over numeric fields*/
206 	if (io__get_char(&io) != ' ')
207 		return -EINVAL;
208 
209 	i = 4;
210 	while (true) {
211 		c = io__get_dec(&io, val);
212 		if (c == -1)
213 			return -EINVAL;
214 		if (c == -2) {
215 			/* Assume a -ve was read */
216 			c = io__get_dec(&io, val);
217 			*val *= -1;
218 		}
219 		if (c != ' ')
220 			return -EINVAL;
221 		if (field == i)
222 			return 0;
223 		i++;
224 	}
225 	return -EINVAL;
226 }
227 
228 int evsel__tool_pmu_prepare_open(struct evsel *evsel,
229 				 struct perf_cpu_map *cpus,
230 				 int nthreads)
231 {
232 	if ((evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME ||
233 	     evsel__tool_event(evsel) == TOOL_PMU__EVENT_USER_TIME) &&
234 	    !evsel->start_times) {
235 		evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus),
236 						  nthreads,
237 						  sizeof(__u64));
238 		if (!evsel->start_times)
239 			return -ENOMEM;
240 	}
241 	return 0;
242 }
243 
244 #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
245 
246 int evsel__tool_pmu_open(struct evsel *evsel,
247 			 struct perf_thread_map *threads,
248 			 int start_cpu_map_idx, int end_cpu_map_idx)
249 {
250 	enum tool_pmu_event ev = evsel__tool_event(evsel);
251 	int pid = -1, idx = 0, thread = 0, nthreads, err = 0, old_errno;
252 
253 	if (ev == TOOL_PMU__EVENT_DURATION_TIME) {
254 		if (evsel->core.attr.sample_period) /* no sampling */
255 			return -EINVAL;
256 		evsel->start_time = rdclock();
257 		return 0;
258 	}
259 
260 	if (evsel->cgrp)
261 		pid = evsel->cgrp->fd;
262 
263 	nthreads = perf_thread_map__nr(threads);
264 	for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
265 		for (thread = 0; thread < nthreads; thread++) {
266 			if (thread >= nthreads)
267 				break;
268 
269 			if (!evsel->cgrp && !evsel->core.system_wide)
270 				pid = perf_thread_map__pid(threads, thread);
271 
272 			if (ev == TOOL_PMU__EVENT_USER_TIME || ev == TOOL_PMU__EVENT_SYSTEM_TIME) {
273 				bool system = ev == TOOL_PMU__EVENT_SYSTEM_TIME;
274 				__u64 *start_time = NULL;
275 				int fd;
276 
277 				if (evsel->core.attr.sample_period) {
278 					/* no sampling */
279 					err = -EINVAL;
280 					goto out_close;
281 				}
282 				if (pid > -1) {
283 					char buf[64];
284 
285 					snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
286 					fd = open(buf, O_RDONLY);
287 					evsel->pid_stat = true;
288 				} else {
289 					fd = open("/proc/stat", O_RDONLY);
290 				}
291 				FD(evsel, idx, thread) = fd;
292 				if (fd < 0) {
293 					err = -errno;
294 					goto out_close;
295 				}
296 				start_time = xyarray__entry(evsel->start_times, idx, thread);
297 				if (pid > -1) {
298 					err = read_pid_stat_field(fd, system ? 15 : 14,
299 								  start_time);
300 				} else {
301 					struct perf_cpu cpu;
302 
303 					cpu = perf_cpu_map__cpu(evsel->core.cpus, idx);
304 					err = read_stat_field(fd, cpu, system ? 3 : 1,
305 							      start_time);
306 				}
307 				if (err)
308 					goto out_close;
309 			}
310 
311 		}
312 	}
313 	return 0;
314 out_close:
315 	if (err)
316 		threads->err_thread = thread;
317 
318 	old_errno = errno;
319 	do {
320 		while (--thread >= 0) {
321 			if (FD(evsel, idx, thread) >= 0)
322 				close(FD(evsel, idx, thread));
323 			FD(evsel, idx, thread) = -1;
324 		}
325 		thread = nthreads;
326 	} while (--idx >= 0);
327 	errno = old_errno;
328 	return err;
329 }
330 
331 int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread)
332 {
333 	__u64 *start_time, cur_time, delta_start;
334 	int fd, err = 0;
335 	struct perf_counts_values *count;
336 	bool adjust = false;
337 
338 	count = perf_counts(evsel->counts, cpu_map_idx, thread);
339 
340 	switch (evsel__tool_event(evsel)) {
341 	case TOOL_PMU__EVENT_DURATION_TIME:
342 		/*
343 		 * Pretend duration_time is only on the first CPU and thread, or
344 		 * else aggregation will scale duration_time by the number of
345 		 * CPUs/threads.
346 		 */
347 		start_time = &evsel->start_time;
348 		if (cpu_map_idx == 0 && thread == 0)
349 			cur_time = rdclock();
350 		else
351 			cur_time = *start_time;
352 		break;
353 	case TOOL_PMU__EVENT_USER_TIME:
354 	case TOOL_PMU__EVENT_SYSTEM_TIME: {
355 		bool system = evsel__tool_event(evsel) == TOOL_PMU__EVENT_SYSTEM_TIME;
356 
357 		start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread);
358 		fd = FD(evsel, cpu_map_idx, thread);
359 		lseek(fd, SEEK_SET, 0);
360 		if (evsel->pid_stat) {
361 			/* The event exists solely on 1 CPU. */
362 			if (cpu_map_idx == 0)
363 				err = read_pid_stat_field(fd, system ? 15 : 14, &cur_time);
364 			else
365 				cur_time = 0;
366 		} else {
367 			/* The event is for all threads. */
368 			if (thread == 0) {
369 				struct perf_cpu cpu = perf_cpu_map__cpu(evsel->core.cpus,
370 									cpu_map_idx);
371 
372 				err = read_stat_field(fd, cpu, system ? 3 : 1, &cur_time);
373 			} else {
374 				cur_time = 0;
375 			}
376 		}
377 		adjust = true;
378 		break;
379 	}
380 	case TOOL_PMU__EVENT_NONE:
381 	case TOOL_PMU__EVENT_MAX:
382 	default:
383 		err = -EINVAL;
384 	}
385 	if (err)
386 		return err;
387 
388 	delta_start = cur_time - *start_time;
389 	if (adjust) {
390 		__u64 ticks_per_sec = sysconf(_SC_CLK_TCK);
391 
392 		delta_start *= 1000000000 / ticks_per_sec;
393 	}
394 	count->val    = delta_start;
395 	count->ena    = count->run = delta_start;
396 	count->lost   = 0;
397 	return 0;
398 }
399 
400 struct perf_pmu *perf_pmus__tool_pmu(void)
401 {
402 	static struct perf_pmu tool = {
403 		.name = "tool",
404 		.type = PERF_PMU_TYPE_TOOL,
405 		.aliases = LIST_HEAD_INIT(tool.aliases),
406 		.caps = LIST_HEAD_INIT(tool.caps),
407 		.format = LIST_HEAD_INIT(tool.format),
408 	};
409 
410 	return &tool;
411 }
412