diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2015-07-15 05:14:07 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-07-20 16:49:49 -0400 |
commit | 92f6c72e7ac40cbf8d12682d1aeeb82c905f2a64 (patch) | |
tree | 28195ce00c81a76eff553e802ff09a1d3f01460d /tools/perf/util/probe-file.c | |
parent | a3c9de6280b8d196ab89ca7fad143bfa2a949790 (diff) |
perf probe: Move ftrace probe-event operations to probe-file.c
Move ftrace probe-event operations to probe-file.c from probe-event.c.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20150715091407.8915.14316.stgit@localhost.localdomain
[ Fixed up strlist__new() calls wrt 4a77e2183fc0 ("perf strlist: Make dupstr be the...") ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util/probe-file.c')
-rw-r--r-- | tools/perf/util/probe-file.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c new file mode 100644 index 000000000000..bbb243717ec8 --- /dev/null +++ b/tools/perf/util/probe-file.c | |||
@@ -0,0 +1,301 @@ | |||
1 | /* | ||
2 | * probe-file.c : operate ftrace k/uprobe events files | ||
3 | * | ||
4 | * Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | #include "util.h" | ||
18 | #include "event.h" | ||
19 | #include "strlist.h" | ||
20 | #include "debug.h" | ||
21 | #include "cache.h" | ||
22 | #include "color.h" | ||
23 | #include "symbol.h" | ||
24 | #include "thread.h" | ||
25 | #include <api/fs/debugfs.h> | ||
26 | #include <api/fs/tracefs.h> | ||
27 | #include "probe-event.h" | ||
28 | #include "probe-file.h" | ||
29 | #include "session.h" | ||
30 | |||
31 | #define MAX_CMDLEN 256 | ||
32 | |||
33 | static void print_open_warning(int err, bool uprobe) | ||
34 | { | ||
35 | char sbuf[STRERR_BUFSIZE]; | ||
36 | |||
37 | if (err == -ENOENT) { | ||
38 | const char *config; | ||
39 | |||
40 | if (uprobe) | ||
41 | config = "CONFIG_UPROBE_EVENTS"; | ||
42 | else | ||
43 | config = "CONFIG_KPROBE_EVENTS"; | ||
44 | |||
45 | pr_warning("%cprobe_events file does not exist" | ||
46 | " - please rebuild kernel with %s.\n", | ||
47 | uprobe ? 'u' : 'k', config); | ||
48 | } else if (err == -ENOTSUP) | ||
49 | pr_warning("Tracefs or debugfs is not mounted.\n"); | ||
50 | else | ||
51 | pr_warning("Failed to open %cprobe_events: %s\n", | ||
52 | uprobe ? 'u' : 'k', | ||
53 | strerror_r(-err, sbuf, sizeof(sbuf))); | ||
54 | } | ||
55 | |||
56 | static void print_both_open_warning(int kerr, int uerr) | ||
57 | { | ||
58 | /* Both kprobes and uprobes are disabled, warn it. */ | ||
59 | if (kerr == -ENOTSUP && uerr == -ENOTSUP) | ||
60 | pr_warning("Tracefs or debugfs is not mounted.\n"); | ||
61 | else if (kerr == -ENOENT && uerr == -ENOENT) | ||
62 | pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS " | ||
63 | "or/and CONFIG_UPROBE_EVENTS.\n"); | ||
64 | else { | ||
65 | char sbuf[STRERR_BUFSIZE]; | ||
66 | pr_warning("Failed to open kprobe events: %s.\n", | ||
67 | strerror_r(-kerr, sbuf, sizeof(sbuf))); | ||
68 | pr_warning("Failed to open uprobe events: %s.\n", | ||
69 | strerror_r(-uerr, sbuf, sizeof(sbuf))); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | static int open_probe_events(const char *trace_file, bool readwrite) | ||
74 | { | ||
75 | char buf[PATH_MAX]; | ||
76 | const char *__debugfs; | ||
77 | const char *tracing_dir = ""; | ||
78 | int ret; | ||
79 | |||
80 | __debugfs = tracefs_find_mountpoint(); | ||
81 | if (__debugfs == NULL) { | ||
82 | tracing_dir = "tracing/"; | ||
83 | |||
84 | __debugfs = debugfs_find_mountpoint(); | ||
85 | if (__debugfs == NULL) | ||
86 | return -ENOTSUP; | ||
87 | } | ||
88 | |||
89 | ret = e_snprintf(buf, PATH_MAX, "%s/%s%s", | ||
90 | __debugfs, tracing_dir, trace_file); | ||
91 | if (ret >= 0) { | ||
92 | pr_debug("Opening %s write=%d\n", buf, readwrite); | ||
93 | if (readwrite && !probe_event_dry_run) | ||
94 | ret = open(buf, O_RDWR | O_APPEND, 0); | ||
95 | else | ||
96 | ret = open(buf, O_RDONLY, 0); | ||
97 | |||
98 | if (ret < 0) | ||
99 | ret = -errno; | ||
100 | } | ||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | static int open_kprobe_events(bool readwrite) | ||
105 | { | ||
106 | return open_probe_events("kprobe_events", readwrite); | ||
107 | } | ||
108 | |||
109 | static int open_uprobe_events(bool readwrite) | ||
110 | { | ||
111 | return open_probe_events("uprobe_events", readwrite); | ||
112 | } | ||
113 | |||
114 | int probe_file__open(int flag) | ||
115 | { | ||
116 | int fd; | ||
117 | |||
118 | if (flag & PF_FL_UPROBE) | ||
119 | fd = open_uprobe_events(flag & PF_FL_RW); | ||
120 | else | ||
121 | fd = open_kprobe_events(flag & PF_FL_RW); | ||
122 | if (fd < 0) | ||
123 | print_open_warning(fd, flag & PF_FL_UPROBE); | ||
124 | |||
125 | return fd; | ||
126 | } | ||
127 | |||
128 | int probe_file__open_both(int *kfd, int *ufd, int flag) | ||
129 | { | ||
130 | if (!kfd || !ufd) | ||
131 | return -EINVAL; | ||
132 | |||
133 | *kfd = open_kprobe_events(flag & PF_FL_RW); | ||
134 | *ufd = open_uprobe_events(flag & PF_FL_RW); | ||
135 | if (*kfd < 0 && *ufd < 0) { | ||
136 | print_both_open_warning(*kfd, *ufd); | ||
137 | return *kfd; | ||
138 | } | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | /* Get raw string list of current kprobe_events or uprobe_events */ | ||
144 | struct strlist *probe_file__get_rawlist(int fd) | ||
145 | { | ||
146 | int ret, idx; | ||
147 | FILE *fp; | ||
148 | char buf[MAX_CMDLEN]; | ||
149 | char *p; | ||
150 | struct strlist *sl; | ||
151 | |||
152 | sl = strlist__new(NULL, NULL); | ||
153 | |||
154 | fp = fdopen(dup(fd), "r"); | ||
155 | while (!feof(fp)) { | ||
156 | p = fgets(buf, MAX_CMDLEN, fp); | ||
157 | if (!p) | ||
158 | break; | ||
159 | |||
160 | idx = strlen(p) - 1; | ||
161 | if (p[idx] == '\n') | ||
162 | p[idx] = '\0'; | ||
163 | ret = strlist__add(sl, buf); | ||
164 | if (ret < 0) { | ||
165 | pr_debug("strlist__add failed (%d)\n", ret); | ||
166 | strlist__delete(sl); | ||
167 | return NULL; | ||
168 | } | ||
169 | } | ||
170 | fclose(fp); | ||
171 | |||
172 | return sl; | ||
173 | } | ||
174 | |||
175 | static struct strlist *__probe_file__get_namelist(int fd, bool include_group) | ||
176 | { | ||
177 | char buf[128]; | ||
178 | struct strlist *sl, *rawlist; | ||
179 | struct str_node *ent; | ||
180 | struct probe_trace_event tev; | ||
181 | int ret = 0; | ||
182 | |||
183 | memset(&tev, 0, sizeof(tev)); | ||
184 | rawlist = probe_file__get_rawlist(fd); | ||
185 | if (!rawlist) | ||
186 | return NULL; | ||
187 | sl = strlist__new(NULL, NULL); | ||
188 | strlist__for_each(ent, rawlist) { | ||
189 | ret = parse_probe_trace_command(ent->s, &tev); | ||
190 | if (ret < 0) | ||
191 | break; | ||
192 | if (include_group) { | ||
193 | ret = e_snprintf(buf, 128, "%s:%s", tev.group, | ||
194 | tev.event); | ||
195 | if (ret >= 0) | ||
196 | ret = strlist__add(sl, buf); | ||
197 | } else | ||
198 | ret = strlist__add(sl, tev.event); | ||
199 | clear_probe_trace_event(&tev); | ||
200 | if (ret < 0) | ||
201 | break; | ||
202 | } | ||
203 | strlist__delete(rawlist); | ||
204 | |||
205 | if (ret < 0) { | ||
206 | strlist__delete(sl); | ||
207 | return NULL; | ||
208 | } | ||
209 | return sl; | ||
210 | } | ||
211 | |||
212 | /* Get current perf-probe event names */ | ||
213 | struct strlist *probe_file__get_namelist(int fd) | ||
214 | { | ||
215 | return __probe_file__get_namelist(fd, false); | ||
216 | } | ||
217 | |||
218 | int probe_file__add_event(int fd, struct probe_trace_event *tev) | ||
219 | { | ||
220 | int ret = 0; | ||
221 | char *buf = synthesize_probe_trace_command(tev); | ||
222 | char sbuf[STRERR_BUFSIZE]; | ||
223 | |||
224 | if (!buf) { | ||
225 | pr_debug("Failed to synthesize probe trace event.\n"); | ||
226 | return -EINVAL; | ||
227 | } | ||
228 | |||
229 | pr_debug("Writing event: %s\n", buf); | ||
230 | if (!probe_event_dry_run) { | ||
231 | ret = write(fd, buf, strlen(buf)); | ||
232 | if (ret <= 0) { | ||
233 | ret = -errno; | ||
234 | pr_warning("Failed to write event: %s\n", | ||
235 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
236 | } | ||
237 | } | ||
238 | free(buf); | ||
239 | |||
240 | return ret; | ||
241 | } | ||
242 | |||
243 | static int __del_trace_probe_event(int fd, struct str_node *ent) | ||
244 | { | ||
245 | char *p; | ||
246 | char buf[128]; | ||
247 | int ret; | ||
248 | |||
249 | /* Convert from perf-probe event to trace-probe event */ | ||
250 | ret = e_snprintf(buf, 128, "-:%s", ent->s); | ||
251 | if (ret < 0) | ||
252 | goto error; | ||
253 | |||
254 | p = strchr(buf + 2, ':'); | ||
255 | if (!p) { | ||
256 | pr_debug("Internal error: %s should have ':' but not.\n", | ||
257 | ent->s); | ||
258 | ret = -ENOTSUP; | ||
259 | goto error; | ||
260 | } | ||
261 | *p = '/'; | ||
262 | |||
263 | pr_debug("Writing event: %s\n", buf); | ||
264 | ret = write(fd, buf, strlen(buf)); | ||
265 | if (ret < 0) { | ||
266 | ret = -errno; | ||
267 | goto error; | ||
268 | } | ||
269 | |||
270 | pr_info("Removed event: %s\n", ent->s); | ||
271 | return 0; | ||
272 | error: | ||
273 | pr_warning("Failed to delete event: %s\n", | ||
274 | strerror_r(-ret, buf, sizeof(buf))); | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | int probe_file__del_events(int fd, struct strfilter *filter) | ||
279 | { | ||
280 | struct strlist *namelist; | ||
281 | struct str_node *ent; | ||
282 | const char *p; | ||
283 | int ret = -ENOENT; | ||
284 | |||
285 | namelist = __probe_file__get_namelist(fd, true); | ||
286 | if (!namelist) | ||
287 | return -ENOENT; | ||
288 | |||
289 | strlist__for_each(ent, namelist) { | ||
290 | p = strchr(ent->s, ':'); | ||
291 | if ((p && strfilter__compare(filter, p + 1)) || | ||
292 | strfilter__compare(filter, ent->s)) { | ||
293 | ret = __del_trace_probe_event(fd, ent); | ||
294 | if (ret < 0) | ||
295 | break; | ||
296 | } | ||
297 | } | ||
298 | strlist__delete(namelist); | ||
299 | |||
300 | return ret; | ||
301 | } | ||