diff options
Diffstat (limited to 'tools/perf/builtin-probe.c')
-rw-r--r-- | tools/perf/builtin-probe.c | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c new file mode 100644 index 000000000000..a58e11b7ea80 --- /dev/null +++ b/tools/perf/builtin-probe.c | |||
@@ -0,0 +1,242 @@ | |||
1 | /* | ||
2 | * builtin-probe.c | ||
3 | * | ||
4 | * Builtin probe command: Set up probe events by C expression | ||
5 | * | ||
6 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
21 | * | ||
22 | */ | ||
23 | #define _GNU_SOURCE | ||
24 | #include <sys/utsname.h> | ||
25 | #include <sys/types.h> | ||
26 | #include <sys/stat.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <errno.h> | ||
29 | #include <stdio.h> | ||
30 | #include <unistd.h> | ||
31 | #include <stdlib.h> | ||
32 | #include <string.h> | ||
33 | |||
34 | #undef _GNU_SOURCE | ||
35 | #include "perf.h" | ||
36 | #include "builtin.h" | ||
37 | #include "util/util.h" | ||
38 | #include "util/event.h" | ||
39 | #include "util/debug.h" | ||
40 | #include "util/parse-options.h" | ||
41 | #include "util/parse-events.h" /* For debugfs_path */ | ||
42 | #include "util/probe-finder.h" | ||
43 | #include "util/probe-event.h" | ||
44 | |||
45 | /* Default vmlinux search paths */ | ||
46 | #define NR_SEARCH_PATH 3 | ||
47 | const char *default_search_path[NR_SEARCH_PATH] = { | ||
48 | "/lib/modules/%s/build/vmlinux", /* Custom build kernel */ | ||
49 | "/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */ | ||
50 | "/boot/vmlinux-debug-%s", /* Ubuntu */ | ||
51 | }; | ||
52 | |||
53 | #define MAX_PATH_LEN 256 | ||
54 | #define MAX_PROBES 128 | ||
55 | |||
56 | /* Session management structure */ | ||
57 | static struct { | ||
58 | char *vmlinux; | ||
59 | char *release; | ||
60 | int need_dwarf; | ||
61 | int nr_probe; | ||
62 | struct probe_point probes[MAX_PROBES]; | ||
63 | } session; | ||
64 | |||
65 | static bool listing; | ||
66 | |||
67 | /* Parse an event definition. Note that any error must die. */ | ||
68 | static void parse_probe_event(const char *str) | ||
69 | { | ||
70 | struct probe_point *pp = &session.probes[session.nr_probe]; | ||
71 | |||
72 | pr_debug("probe-definition(%d): %s\n", session.nr_probe, str); | ||
73 | if (++session.nr_probe == MAX_PROBES) | ||
74 | die("Too many probes (> %d) are specified.", MAX_PROBES); | ||
75 | |||
76 | /* Parse perf-probe event into probe_point */ | ||
77 | session.need_dwarf = parse_perf_probe_event(str, pp); | ||
78 | |||
79 | pr_debug("%d arguments\n", pp->nr_args); | ||
80 | } | ||
81 | |||
82 | static int opt_add_probe_event(const struct option *opt __used, | ||
83 | const char *str, int unset __used) | ||
84 | { | ||
85 | if (str) | ||
86 | parse_probe_event(str); | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | #ifndef NO_LIBDWARF | ||
91 | static int open_default_vmlinux(void) | ||
92 | { | ||
93 | struct utsname uts; | ||
94 | char fname[MAX_PATH_LEN]; | ||
95 | int fd, ret, i; | ||
96 | |||
97 | ret = uname(&uts); | ||
98 | if (ret) { | ||
99 | pr_debug("uname() failed.\n"); | ||
100 | return -errno; | ||
101 | } | ||
102 | session.release = uts.release; | ||
103 | for (i = 0; i < NR_SEARCH_PATH; i++) { | ||
104 | ret = snprintf(fname, MAX_PATH_LEN, | ||
105 | default_search_path[i], session.release); | ||
106 | if (ret >= MAX_PATH_LEN || ret < 0) { | ||
107 | pr_debug("Filename(%d,%s) is too long.\n", i, | ||
108 | uts.release); | ||
109 | errno = E2BIG; | ||
110 | return -E2BIG; | ||
111 | } | ||
112 | pr_debug("try to open %s\n", fname); | ||
113 | fd = open(fname, O_RDONLY); | ||
114 | if (fd >= 0) | ||
115 | break; | ||
116 | } | ||
117 | return fd; | ||
118 | } | ||
119 | #endif | ||
120 | |||
121 | static const char * const probe_usage[] = { | ||
122 | "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", | ||
123 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", | ||
124 | "perf probe --list", | ||
125 | NULL | ||
126 | }; | ||
127 | |||
128 | static const struct option options[] = { | ||
129 | OPT_BOOLEAN('v', "verbose", &verbose, | ||
130 | "be more verbose (show parsed arguments, etc)"), | ||
131 | #ifndef NO_LIBDWARF | ||
132 | OPT_STRING('k', "vmlinux", &session.vmlinux, "file", | ||
133 | "vmlinux/module pathname"), | ||
134 | #endif | ||
135 | OPT_BOOLEAN('l', "list", &listing, "list up current probes"), | ||
136 | OPT_CALLBACK('a', "add", NULL, | ||
137 | #ifdef NO_LIBDWARF | ||
138 | "FUNC[+OFFS|%return] [ARG ...]", | ||
139 | #else | ||
140 | "FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]", | ||
141 | #endif | ||
142 | "probe point definition, where\n" | ||
143 | "\t\tGRP:\tGroup name (optional)\n" | ||
144 | "\t\tNAME:\tEvent name\n" | ||
145 | "\t\tFUNC:\tFunction name\n" | ||
146 | "\t\tOFFS:\tOffset from function entry (in byte)\n" | ||
147 | "\t\t%return:\tPut the probe at function return\n" | ||
148 | #ifdef NO_LIBDWARF | ||
149 | "\t\tARG:\tProbe argument (only \n" | ||
150 | #else | ||
151 | "\t\tSRC:\tSource code path\n" | ||
152 | "\t\tRLN:\tRelative line number from function entry.\n" | ||
153 | "\t\tALN:\tAbsolute line number in file.\n" | ||
154 | "\t\tARG:\tProbe argument (local variable name or\n" | ||
155 | #endif | ||
156 | "\t\t\tkprobe-tracer argument format.)\n", | ||
157 | opt_add_probe_event), | ||
158 | OPT_END() | ||
159 | }; | ||
160 | |||
161 | int cmd_probe(int argc, const char **argv, const char *prefix __used) | ||
162 | { | ||
163 | int i, j, ret; | ||
164 | #ifndef NO_LIBDWARF | ||
165 | int fd; | ||
166 | #endif | ||
167 | struct probe_point *pp; | ||
168 | |||
169 | argc = parse_options(argc, argv, options, probe_usage, | ||
170 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
171 | for (i = 0; i < argc; i++) | ||
172 | parse_probe_event(argv[i]); | ||
173 | |||
174 | if ((session.nr_probe == 0 && !listing) || | ||
175 | (session.nr_probe != 0 && listing)) | ||
176 | usage_with_options(probe_usage, options); | ||
177 | |||
178 | if (listing) { | ||
179 | show_perf_probe_events(); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | if (session.need_dwarf) | ||
184 | #ifdef NO_LIBDWARF | ||
185 | die("Debuginfo-analysis is not supported"); | ||
186 | #else /* !NO_LIBDWARF */ | ||
187 | pr_debug("Some probes require debuginfo.\n"); | ||
188 | |||
189 | if (session.vmlinux) | ||
190 | fd = open(session.vmlinux, O_RDONLY); | ||
191 | else | ||
192 | fd = open_default_vmlinux(); | ||
193 | if (fd < 0) { | ||
194 | if (session.need_dwarf) | ||
195 | die("Could not open vmlinux/module file."); | ||
196 | |||
197 | pr_warning("Could not open vmlinux/module file." | ||
198 | " Try to use symbols.\n"); | ||
199 | goto end_dwarf; | ||
200 | } | ||
201 | |||
202 | /* Searching probe points */ | ||
203 | for (j = 0; j < session.nr_probe; j++) { | ||
204 | pp = &session.probes[j]; | ||
205 | if (pp->found) | ||
206 | continue; | ||
207 | |||
208 | lseek(fd, SEEK_SET, 0); | ||
209 | ret = find_probepoint(fd, pp); | ||
210 | if (ret < 0) { | ||
211 | if (session.need_dwarf) | ||
212 | die("Could not analyze debuginfo."); | ||
213 | |||
214 | pr_warning("An error occurred in debuginfo analysis. Try to use symbols.\n"); | ||
215 | break; | ||
216 | } | ||
217 | if (ret == 0) /* No error but failed to find probe point. */ | ||
218 | die("No probe point found."); | ||
219 | } | ||
220 | close(fd); | ||
221 | |||
222 | end_dwarf: | ||
223 | #endif /* !NO_LIBDWARF */ | ||
224 | |||
225 | /* Synthesize probes without dwarf */ | ||
226 | for (j = 0; j < session.nr_probe; j++) { | ||
227 | pp = &session.probes[j]; | ||
228 | if (pp->found) /* This probe is already found. */ | ||
229 | continue; | ||
230 | |||
231 | ret = synthesize_trace_kprobe_event(pp); | ||
232 | if (ret == -E2BIG) | ||
233 | die("probe point definition becomes too long."); | ||
234 | else if (ret < 0) | ||
235 | die("Failed to synthesize a probe point."); | ||
236 | } | ||
237 | |||
238 | /* Settng up probe points */ | ||
239 | add_trace_kprobe_events(session.probes, session.nr_probe); | ||
240 | return 0; | ||
241 | } | ||
242 | |||