xref: /linux/tools/perf/util/tp_pmu.c (revision f4f346c3465949ebba80c6cc52cd8d2eeaa545fd)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 #include "tp_pmu.h"
3 #include "pmus.h"
4 #include <api/fs/fs.h>
5 #include <api/fs/tracing_path.h>
6 #include <api/io_dir.h>
7 #include <linux/kernel.h>
8 #include <errno.h>
9 #include <string.h>
10 
tp_pmu__id(const char * sys,const char * name)11 int tp_pmu__id(const char *sys, const char *name)
12 {
13 	char *tp_dir = get_events_file(sys);
14 	char path[PATH_MAX];
15 	int id, err;
16 
17 	if (!tp_dir)
18 		return -1;
19 
20 	scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name);
21 	put_events_file(tp_dir);
22 	err = filename__read_int(path, &id);
23 	if (err)
24 		return err;
25 
26 	return id;
27 }
28 
29 
tp_pmu__for_each_tp_event(const char * sys,void * state,tp_event_callback cb)30 int tp_pmu__for_each_tp_event(const char *sys, void *state, tp_event_callback cb)
31 {
32 	char *evt_path;
33 	struct io_dirent64 *evt_ent;
34 	struct io_dir evt_dir;
35 	int ret = 0;
36 
37 	evt_path = get_events_file(sys);
38 	if (!evt_path)
39 		return -errno;
40 
41 	io_dir__init(&evt_dir, open(evt_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
42 	if (evt_dir.dirfd < 0) {
43 		ret = -errno;
44 		put_events_file(evt_path);
45 		return ret;
46 	}
47 	put_events_file(evt_path);
48 
49 	while (!ret && (evt_ent = io_dir__readdir(&evt_dir))) {
50 		if (!strcmp(evt_ent->d_name, ".")
51 		    || !strcmp(evt_ent->d_name, "..")
52 		    || !strcmp(evt_ent->d_name, "enable")
53 		    || !strcmp(evt_ent->d_name, "filter"))
54 			continue;
55 
56 		ret = cb(state, sys, evt_ent->d_name);
57 		if (ret)
58 			break;
59 	}
60 	close(evt_dir.dirfd);
61 	return ret;
62 }
63 
tp_pmu__for_each_tp_sys(void * state,tp_sys_callback cb)64 int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb)
65 {
66 	struct io_dirent64 *events_ent;
67 	struct io_dir events_dir;
68 	int ret = 0;
69 	char *events_dir_path = get_tracing_file("events");
70 
71 	if (!events_dir_path)
72 		return -errno;
73 
74 	io_dir__init(&events_dir, open(events_dir_path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
75 	if (events_dir.dirfd < 0) {
76 		ret = -errno;
77 		put_events_file(events_dir_path);
78 		return ret;
79 	}
80 	put_events_file(events_dir_path);
81 
82 	while (!ret && (events_ent = io_dir__readdir(&events_dir))) {
83 		if (!strcmp(events_ent->d_name, ".") ||
84 		    !strcmp(events_ent->d_name, "..") ||
85 		    !strcmp(events_ent->d_name, "enable") ||
86 		    !strcmp(events_ent->d_name, "header_event") ||
87 		    !strcmp(events_ent->d_name, "header_page"))
88 			continue;
89 
90 		ret = cb(state, events_ent->d_name);
91 		if (ret)
92 			break;
93 	}
94 	close(events_dir.dirfd);
95 	return ret;
96 }
97 
perf_pmu__is_tracepoint(const struct perf_pmu * pmu)98 bool perf_pmu__is_tracepoint(const struct perf_pmu *pmu)
99 {
100 	return pmu->type == PERF_TYPE_TRACEPOINT;
101 }
102 
103 struct for_each_event_args {
104 	void *state;
105 	pmu_event_callback cb;
106 	const struct perf_pmu *pmu;
107 };
108 
for_each_event_cb(void * state,const char * sys_name,const char * evt_name)109 static int for_each_event_cb(void *state, const char *sys_name, const char *evt_name)
110 {
111 	struct for_each_event_args *args = state;
112 	char name[2 * FILENAME_MAX + 2];
113 	/* 16 possible hex digits and 22 other characters and \0. */
114 	char encoding[16 + 22];
115 	char *format = NULL;
116 	size_t format_size;
117 	struct pmu_event_info info = {
118 		.pmu = args->pmu,
119 		.pmu_name = args->pmu->name,
120 		.event_type_desc = "Tracepoint event",
121 	};
122 	char *tp_dir = get_events_file(sys_name);
123 	char path[PATH_MAX];
124 	int id, err;
125 
126 	if (!tp_dir)
127 		return -1;
128 
129 	scnprintf(path, sizeof(path), "%s/%s/id", tp_dir, evt_name);
130 	err = filename__read_int(path, &id);
131 	if (err == 0) {
132 		snprintf(encoding, sizeof(encoding), "tracepoint/config=0x%x/", id);
133 		info.encoding_desc = encoding;
134 	}
135 
136 	scnprintf(path, sizeof(path), "%s/%s/format", tp_dir, evt_name);
137 	put_events_file(tp_dir);
138 	err = filename__read_str(path, &format, &format_size);
139 	if (err == 0) {
140 		info.long_desc = format;
141 		for (size_t i = 0 ; i < format_size; i++) {
142 			/* Swap tabs to spaces due to some rendering issues. */
143 			if (format[i] == '\t')
144 				format[i] = ' ';
145 		}
146 	}
147 	snprintf(name, sizeof(name), "%s:%s", sys_name, evt_name);
148 	info.name = name;
149 	err = args->cb(args->state, &info);
150 	free(format);
151 	return err;
152 }
153 
for_each_event_sys_cb(void * state,const char * sys_name)154 static int for_each_event_sys_cb(void *state, const char *sys_name)
155 {
156 	return tp_pmu__for_each_tp_event(sys_name, state, for_each_event_cb);
157 }
158 
tp_pmu__for_each_event(struct perf_pmu * pmu,void * state,pmu_event_callback cb)159 int tp_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
160 {
161 	struct for_each_event_args args = {
162 		.state = state,
163 		.cb = cb,
164 		.pmu = pmu,
165 	};
166 
167 	return tp_pmu__for_each_tp_sys(&args, for_each_event_sys_cb);
168 }
169 
num_events_cb(void * state,const char * sys_name __maybe_unused,const char * evt_name __maybe_unused)170 static int num_events_cb(void *state, const char *sys_name __maybe_unused,
171 			 const char *evt_name __maybe_unused)
172 {
173 	size_t *count = state;
174 
175 	(*count)++;
176 	return 0;
177 }
178 
num_events_sys_cb(void * state,const char * sys_name)179 static int num_events_sys_cb(void *state, const char *sys_name)
180 {
181 	return tp_pmu__for_each_tp_event(sys_name, state, num_events_cb);
182 }
183 
tp_pmu__num_events(struct perf_pmu * pmu __maybe_unused)184 size_t tp_pmu__num_events(struct perf_pmu *pmu __maybe_unused)
185 {
186 	size_t count = 0;
187 
188 	tp_pmu__for_each_tp_sys(&count, num_events_sys_cb);
189 	return count;
190 }
191 
tp_pmu__have_event(struct perf_pmu * pmu __maybe_unused,const char * name)192 bool tp_pmu__have_event(struct perf_pmu *pmu __maybe_unused, const char *name)
193 {
194 	char *dup_name, *colon;
195 	int id;
196 
197 	colon = strchr(name, ':');
198 	if (colon == NULL)
199 		return false;
200 
201 	dup_name = strdup(name);
202 	if (!dup_name)
203 		return false;
204 
205 	colon = dup_name + (colon - name);
206 	*colon = '\0';
207 	id = tp_pmu__id(dup_name, colon + 1);
208 	free(dup_name);
209 	return id >= 0;
210 }
211