1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * builtin-probe.c |
4 | * |
5 | * Builtin probe command: Set up probe events by C expression |
6 | * |
7 | * Written by Masami Hiramatsu <mhiramat@redhat.com> |
8 | */ |
9 | #include <sys/utsname.h> |
10 | #include <sys/types.h> |
11 | #include <sys/stat.h> |
12 | #include <fcntl.h> |
13 | #include <errno.h> |
14 | #include <stdio.h> |
15 | #include <unistd.h> |
16 | #include <stdlib.h> |
17 | #include <string.h> |
18 | |
19 | #include "builtin.h" |
20 | #include "namespaces.h" |
21 | #include "util/build-id.h" |
22 | #include "util/strlist.h" |
23 | #include "util/strfilter.h" |
24 | #include "util/symbol.h" |
25 | #include "util/symbol_conf.h" |
26 | #include "util/debug.h" |
27 | #include <subcmd/parse-options.h> |
28 | #include "util/probe-finder.h" |
29 | #include "util/probe-event.h" |
30 | #include "util/probe-file.h" |
31 | #include <linux/string.h> |
32 | #include <linux/zalloc.h> |
33 | |
34 | #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*" |
35 | #define DEFAULT_FUNC_FILTER "!_* & !*@plt" |
36 | #define DEFAULT_LIST_FILTER "*" |
37 | |
38 | /* Session management structure */ |
39 | static struct { |
40 | int command; /* Command short_name */ |
41 | bool list_events; |
42 | bool uprobes; |
43 | bool target_used; |
44 | int nevents; |
45 | struct perf_probe_event events[MAX_PROBES]; |
46 | struct line_range line_range; |
47 | char *target; |
48 | struct strfilter *filter; |
49 | struct nsinfo *nsi; |
50 | } *params; |
51 | |
52 | /* Parse an event definition. Note that any error must die. */ |
53 | static int parse_probe_event(const char *str) |
54 | { |
55 | struct perf_probe_event *pev = ¶ms->events[params->nevents]; |
56 | int ret; |
57 | |
58 | pr_debug("probe-definition(%d): %s\n" , params->nevents, str); |
59 | if (++params->nevents == MAX_PROBES) { |
60 | pr_err("Too many probes (> %d) were specified." , MAX_PROBES); |
61 | return -1; |
62 | } |
63 | |
64 | pev->uprobes = params->uprobes; |
65 | if (params->target) { |
66 | pev->target = strdup(params->target); |
67 | if (!pev->target) |
68 | return -ENOMEM; |
69 | params->target_used = true; |
70 | } |
71 | |
72 | pev->nsi = nsinfo__get(params->nsi); |
73 | |
74 | /* Parse a perf-probe command into event */ |
75 | ret = parse_perf_probe_command(cmd: str, pev); |
76 | pr_debug("%d arguments\n" , pev->nargs); |
77 | |
78 | return ret; |
79 | } |
80 | |
81 | static int params_add_filter(const char *str) |
82 | { |
83 | const char *err = NULL; |
84 | int ret = 0; |
85 | |
86 | pr_debug2("Add filter: %s\n" , str); |
87 | if (!params->filter) { |
88 | params->filter = strfilter__new(rules: str, err: &err); |
89 | if (!params->filter) |
90 | ret = err ? -EINVAL : -ENOMEM; |
91 | } else |
92 | ret = strfilter__or(filter: params->filter, rules: str, err: &err); |
93 | |
94 | if (ret == -EINVAL) { |
95 | pr_err("Filter parse error at %td.\n" , err - str + 1); |
96 | pr_err("Source: \"%s\"\n" , str); |
97 | pr_err(" %*c\n" , (int)(err - str + 1), '^'); |
98 | } |
99 | |
100 | return ret; |
101 | } |
102 | |
103 | static int set_target(const char *ptr) |
104 | { |
105 | int found = 0; |
106 | const char *buf; |
107 | |
108 | /* |
109 | * The first argument after options can be an absolute path |
110 | * to an executable / library or kernel module. |
111 | * |
112 | * TODO: Support relative path, and $PATH, $LD_LIBRARY_PATH, |
113 | * short module name. |
114 | */ |
115 | if (!params->target && ptr && *ptr == '/') { |
116 | params->target = strdup(ptr); |
117 | if (!params->target) |
118 | return -ENOMEM; |
119 | params->target_used = false; |
120 | |
121 | found = 1; |
122 | buf = ptr + (strlen(ptr) - 3); |
123 | |
124 | if (strcmp(buf, ".ko" )) |
125 | params->uprobes = true; |
126 | |
127 | } |
128 | |
129 | return found; |
130 | } |
131 | |
132 | static int parse_probe_event_argv(int argc, const char **argv) |
133 | { |
134 | int i, len, ret, found_target; |
135 | char *buf; |
136 | |
137 | found_target = set_target(argv[0]); |
138 | if (found_target < 0) |
139 | return found_target; |
140 | |
141 | if (found_target && argc == 1) |
142 | return 0; |
143 | |
144 | /* Bind up rest arguments */ |
145 | len = 0; |
146 | for (i = 0; i < argc; i++) { |
147 | if (i == 0 && found_target) |
148 | continue; |
149 | |
150 | len += strlen(argv[i]) + 1; |
151 | } |
152 | buf = zalloc(len + 1); |
153 | if (buf == NULL) |
154 | return -ENOMEM; |
155 | len = 0; |
156 | for (i = 0; i < argc; i++) { |
157 | if (i == 0 && found_target) |
158 | continue; |
159 | |
160 | len += sprintf(buf: &buf[len], fmt: "%s " , argv[i]); |
161 | } |
162 | ret = parse_probe_event(str: buf); |
163 | free(buf); |
164 | return ret; |
165 | } |
166 | |
167 | static int opt_set_target(const struct option *opt, const char *str, |
168 | int unset __maybe_unused) |
169 | { |
170 | int ret = -ENOENT; |
171 | char *tmp; |
172 | |
173 | if (str) { |
174 | if (!strcmp(opt->long_name, "exec" )) |
175 | params->uprobes = true; |
176 | else if (!strcmp(opt->long_name, "module" )) |
177 | params->uprobes = false; |
178 | else |
179 | return ret; |
180 | |
181 | /* Expand given path to absolute path, except for modulename */ |
182 | if (params->uprobes || strchr(str, '/')) { |
183 | tmp = nsinfo__realpath(str, params->nsi); |
184 | if (!tmp) { |
185 | pr_warning("Failed to get the absolute path of %s: %m\n" , str); |
186 | return ret; |
187 | } |
188 | } else { |
189 | tmp = strdup(str); |
190 | if (!tmp) |
191 | return -ENOMEM; |
192 | } |
193 | free(params->target); |
194 | params->target = tmp; |
195 | params->target_used = false; |
196 | ret = 0; |
197 | } |
198 | |
199 | return ret; |
200 | } |
201 | |
202 | static int opt_set_target_ns(const struct option *opt __maybe_unused, |
203 | const char *str, int unset __maybe_unused) |
204 | { |
205 | int ret = -ENOENT; |
206 | pid_t ns_pid; |
207 | struct nsinfo *nsip; |
208 | |
209 | if (str) { |
210 | errno = 0; |
211 | ns_pid = (pid_t)strtol(str, NULL, 10); |
212 | if (errno != 0) { |
213 | ret = -errno; |
214 | pr_warning("Failed to parse %s as a pid: %s\n" , str, |
215 | strerror(errno)); |
216 | return ret; |
217 | } |
218 | nsip = nsinfo__new(ns_pid); |
219 | if (nsip && nsinfo__need_setns(nsip)) |
220 | params->nsi = nsinfo__get(nsip); |
221 | nsinfo__put(nsip); |
222 | |
223 | ret = 0; |
224 | } |
225 | |
226 | return ret; |
227 | } |
228 | |
229 | |
230 | /* Command option callbacks */ |
231 | |
232 | #ifdef HAVE_DWARF_SUPPORT |
233 | static int opt_show_lines(const struct option *opt, |
234 | const char *str, int unset __maybe_unused) |
235 | { |
236 | int ret = 0; |
237 | |
238 | if (!str) |
239 | return 0; |
240 | |
241 | if (params->command == 'L') { |
242 | pr_warning("Warning: more than one --line options are" |
243 | " detected. Only the first one is valid.\n" ); |
244 | return 0; |
245 | } |
246 | |
247 | params->command = opt->short_name; |
248 | ret = parse_line_range_desc(str, ¶ms->line_range); |
249 | |
250 | return ret; |
251 | } |
252 | |
253 | static int opt_show_vars(const struct option *opt, |
254 | const char *str, int unset __maybe_unused) |
255 | { |
256 | struct perf_probe_event *pev = ¶ms->events[params->nevents]; |
257 | int ret; |
258 | |
259 | if (!str) |
260 | return 0; |
261 | |
262 | ret = parse_probe_event(str); |
263 | if (!ret && pev->nargs != 0) { |
264 | pr_err(" Error: '--vars' doesn't accept arguments.\n" ); |
265 | return -EINVAL; |
266 | } |
267 | params->command = opt->short_name; |
268 | |
269 | return ret; |
270 | } |
271 | #else |
272 | # define opt_show_lines NULL |
273 | # define opt_show_vars NULL |
274 | #endif |
275 | static int opt_add_probe_event(const struct option *opt, |
276 | const char *str, int unset __maybe_unused) |
277 | { |
278 | if (str) { |
279 | params->command = opt->short_name; |
280 | return parse_probe_event(str); |
281 | } |
282 | |
283 | return 0; |
284 | } |
285 | |
286 | static int opt_set_filter_with_command(const struct option *opt, |
287 | const char *str, int unset) |
288 | { |
289 | if (!unset) |
290 | params->command = opt->short_name; |
291 | |
292 | if (str) |
293 | return params_add_filter(str); |
294 | |
295 | return 0; |
296 | } |
297 | |
298 | static int opt_set_filter(const struct option *opt __maybe_unused, |
299 | const char *str, int unset __maybe_unused) |
300 | { |
301 | if (str) |
302 | return params_add_filter(str); |
303 | |
304 | return 0; |
305 | } |
306 | |
307 | static int init_params(void) |
308 | { |
309 | int ret; |
310 | |
311 | params = calloc(1, sizeof(*params)); |
312 | if (!params) |
313 | return -ENOMEM; |
314 | |
315 | ret = line_range__init(lr: ¶ms->line_range); |
316 | if (ret) |
317 | zfree(¶ms); |
318 | return ret; |
319 | } |
320 | |
321 | static void cleanup_params(void) |
322 | { |
323 | int i; |
324 | |
325 | for (i = 0; i < params->nevents; i++) |
326 | clear_perf_probe_event(pev: params->events + i); |
327 | line_range__clear(lr: ¶ms->line_range); |
328 | free(params->target); |
329 | strfilter__delete(filter: params->filter); |
330 | nsinfo__put(params->nsi); |
331 | zfree(¶ms); |
332 | } |
333 | |
334 | static void pr_err_with_code(const char *msg, int err) |
335 | { |
336 | char sbuf[STRERR_BUFSIZE]; |
337 | |
338 | pr_err("%s" , msg); |
339 | pr_debug(" Reason: %s (Code: %d)" , |
340 | str_error_r(-err, sbuf, sizeof(sbuf)), err); |
341 | pr_err("\n" ); |
342 | } |
343 | |
344 | static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs) |
345 | { |
346 | int ret; |
347 | int i, k; |
348 | const char *event = NULL, *group = NULL; |
349 | |
350 | ret = init_probe_symbol_maps(user_only: pevs->uprobes); |
351 | if (ret < 0) |
352 | return ret; |
353 | |
354 | ret = convert_perf_probe_events(pevs, npevs); |
355 | if (ret < 0) |
356 | goto out_cleanup; |
357 | |
358 | if (params->command == 'D') { /* it shows definition */ |
359 | if (probe_conf.bootconfig) |
360 | ret = show_bootconfig_events(pevs, npevs); |
361 | else |
362 | ret = show_probe_trace_events(pevs, npevs); |
363 | goto out_cleanup; |
364 | } |
365 | |
366 | ret = apply_perf_probe_events(pevs, npevs); |
367 | if (ret < 0) |
368 | goto out_cleanup; |
369 | |
370 | for (i = k = 0; i < npevs; i++) |
371 | k += pevs[i].ntevs; |
372 | |
373 | pr_info("Added new event%s\n" , (k > 1) ? "s:" : ":" ); |
374 | for (i = 0; i < npevs; i++) { |
375 | struct perf_probe_event *pev = &pevs[i]; |
376 | |
377 | for (k = 0; k < pev->ntevs; k++) { |
378 | struct probe_trace_event *tev = &pev->tevs[k]; |
379 | /* Skipped events have no event name */ |
380 | if (!tev->event) |
381 | continue; |
382 | |
383 | /* We use tev's name for showing new events */ |
384 | show_perf_probe_event(group: tev->group, event: tev->event, pev, |
385 | module: tev->point.module, use_stdout: false); |
386 | |
387 | /* Save the last valid name */ |
388 | event = tev->event; |
389 | group = tev->group; |
390 | } |
391 | } |
392 | |
393 | /* Note that it is possible to skip all events because of blacklist */ |
394 | if (event) { |
395 | #ifndef HAVE_LIBTRACEEVENT |
396 | pr_info("\nperf is not linked with libtraceevent, to use the new probe you can use tracefs:\n\n" ); |
397 | pr_info("\tcd /sys/kernel/tracing/\n" ); |
398 | pr_info("\techo 1 > events/%s/%s/enable\n" , group, event); |
399 | pr_info("\techo 1 > tracing_on\n" ); |
400 | pr_info("\tcat trace_pipe\n" ); |
401 | pr_info("\tBefore removing the probe, echo 0 > events/%s/%s/enable\n" , group, event); |
402 | #else |
403 | /* Show how to use the event. */ |
404 | pr_info("\nYou can now use it in all perf tools, such as:\n\n" ); |
405 | pr_info("\tperf record -e %s:%s -aR sleep 1\n\n" , group, event); |
406 | #endif |
407 | } |
408 | |
409 | out_cleanup: |
410 | cleanup_perf_probe_events(pevs, npevs); |
411 | exit_probe_symbol_maps(); |
412 | return ret; |
413 | } |
414 | |
415 | static int del_perf_probe_caches(struct strfilter *filter) |
416 | { |
417 | struct probe_cache *cache; |
418 | struct strlist *bidlist; |
419 | struct str_node *nd; |
420 | int ret; |
421 | |
422 | bidlist = build_id_cache__list_all(validonly: false); |
423 | if (!bidlist) { |
424 | ret = -errno; |
425 | pr_debug("Failed to get buildids: %d\n" , ret); |
426 | return ret ?: -ENOMEM; |
427 | } |
428 | |
429 | strlist__for_each_entry(nd, bidlist) { |
430 | cache = probe_cache__new(tgt: nd->s, NULL); |
431 | if (!cache) |
432 | continue; |
433 | if (probe_cache__filter_purge(cache, filter) < 0 || |
434 | probe_cache__commit(cache) < 0) |
435 | pr_warning("Failed to remove entries for %s\n" , nd->s); |
436 | probe_cache__delete(cache); |
437 | } |
438 | return 0; |
439 | } |
440 | |
441 | static int perf_del_probe_events(struct strfilter *filter) |
442 | { |
443 | int ret, ret2, ufd = -1, kfd = -1; |
444 | char *str = strfilter__string(filter); |
445 | struct strlist *klist = NULL, *ulist = NULL; |
446 | struct str_node *ent; |
447 | |
448 | if (!str) |
449 | return -EINVAL; |
450 | |
451 | pr_debug("Delete filter: \'%s\'\n" , str); |
452 | |
453 | if (probe_conf.cache) |
454 | return del_perf_probe_caches(filter); |
455 | |
456 | /* Get current event names */ |
457 | ret = probe_file__open_both(&kfd, &ufd, PF_FL_RW); |
458 | if (ret < 0) |
459 | goto out; |
460 | |
461 | klist = strlist__new(NULL, NULL); |
462 | ulist = strlist__new(NULL, NULL); |
463 | if (!klist || !ulist) { |
464 | ret = -ENOMEM; |
465 | goto out; |
466 | } |
467 | |
468 | ret = probe_file__get_events(kfd, filter, klist); |
469 | if (ret == 0) { |
470 | strlist__for_each_entry(ent, klist) |
471 | pr_info("Removed event: %s\n" , ent->s); |
472 | |
473 | ret = probe_file__del_strlist(kfd, klist); |
474 | if (ret < 0) |
475 | goto error; |
476 | } else if (ret == -ENOMEM) |
477 | goto error; |
478 | |
479 | ret2 = probe_file__get_events(ufd, filter, ulist); |
480 | if (ret2 == 0) { |
481 | strlist__for_each_entry(ent, ulist) |
482 | pr_info("Removed event: %s\n" , ent->s); |
483 | |
484 | ret2 = probe_file__del_strlist(ufd, ulist); |
485 | if (ret2 < 0) |
486 | goto error; |
487 | } else if (ret2 == -ENOMEM) |
488 | goto error; |
489 | |
490 | if (ret == -ENOENT && ret2 == -ENOENT) |
491 | pr_warning("\"%s\" does not hit any event.\n" , str); |
492 | else |
493 | ret = 0; |
494 | |
495 | error: |
496 | if (kfd >= 0) |
497 | close(kfd); |
498 | if (ufd >= 0) |
499 | close(ufd); |
500 | out: |
501 | strlist__delete(slist: klist); |
502 | strlist__delete(slist: ulist); |
503 | free(str); |
504 | |
505 | return ret; |
506 | } |
507 | |
508 | #ifdef HAVE_DWARF_SUPPORT |
509 | #define PROBEDEF_STR \ |
510 | "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT [[NAME=]ARG ...]" |
511 | #else |
512 | #define PROBEDEF_STR "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]" |
513 | #endif |
514 | |
515 | |
516 | static int |
517 | __cmd_probe(int argc, const char **argv) |
518 | { |
519 | const char * const probe_usage[] = { |
520 | "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]" , |
521 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]" , |
522 | "perf probe [<options>] --del '[GROUP:]EVENT' ..." , |
523 | "perf probe --list [GROUP:]EVENT ..." , |
524 | #ifdef HAVE_DWARF_SUPPORT |
525 | "perf probe [<options>] --line 'LINEDESC'" , |
526 | "perf probe [<options>] --vars 'PROBEPOINT'" , |
527 | #endif |
528 | "perf probe [<options>] --funcs" , |
529 | NULL |
530 | }; |
531 | struct option options[] = { |
532 | OPT_INCR('v', "verbose" , &verbose, |
533 | "be more verbose (show parsed arguments, etc)" ), |
534 | OPT_BOOLEAN('q', "quiet" , &quiet, |
535 | "be quiet (do not show any warnings or messages)" ), |
536 | OPT_CALLBACK_DEFAULT('l', "list" , NULL, "[GROUP:]EVENT" , |
537 | "list up probe events" , |
538 | opt_set_filter_with_command, DEFAULT_LIST_FILTER), |
539 | OPT_CALLBACK('d', "del" , NULL, "[GROUP:]EVENT" , "delete a probe event." , |
540 | opt_set_filter_with_command), |
541 | OPT_CALLBACK('a', "add" , NULL, PROBEDEF_STR, |
542 | "probe point definition, where\n" |
543 | "\t\tGROUP:\tGroup name (optional)\n" |
544 | "\t\tEVENT:\tEvent name\n" |
545 | "\t\tFUNC:\tFunction name\n" |
546 | "\t\tOFF:\tOffset from function entry (in byte)\n" |
547 | "\t\t%return:\tPut the probe at function return\n" |
548 | #ifdef HAVE_DWARF_SUPPORT |
549 | "\t\tSRC:\tSource code path\n" |
550 | "\t\tRL:\tRelative line number from function entry.\n" |
551 | "\t\tAL:\tAbsolute line number in file.\n" |
552 | "\t\tPT:\tLazy expression of line code.\n" |
553 | "\t\tARG:\tProbe argument (local variable name or\n" |
554 | "\t\t\tkprobe-tracer argument format.)\n" , |
555 | #else |
556 | "\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n" , |
557 | #endif |
558 | opt_add_probe_event), |
559 | OPT_CALLBACK('D', "definition" , NULL, PROBEDEF_STR, |
560 | "Show trace event definition of given traceevent for k/uprobe_events." , |
561 | opt_add_probe_event), |
562 | OPT_BOOLEAN('f', "force" , &probe_conf.force_add, "forcibly add events" |
563 | " with existing name" ), |
564 | OPT_CALLBACK('L', "line" , NULL, |
565 | "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]" , |
566 | "Show source code lines." , opt_show_lines), |
567 | OPT_CALLBACK('V', "vars" , NULL, |
568 | "FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" , |
569 | "Show accessible variables on PROBEDEF" , opt_show_vars), |
570 | OPT_BOOLEAN('\0', "externs" , &probe_conf.show_ext_vars, |
571 | "Show external variables too (with --vars only)" ), |
572 | OPT_BOOLEAN('\0', "range" , &probe_conf.show_location_range, |
573 | "Show variables location range in scope (with --vars only)" ), |
574 | OPT_STRING('k', "vmlinux" , &symbol_conf.vmlinux_name, |
575 | "file" , "vmlinux pathname" ), |
576 | OPT_STRING('s', "source" , &symbol_conf.source_prefix, |
577 | "directory" , "path to kernel source" ), |
578 | OPT_BOOLEAN('\0', "no-inlines" , &probe_conf.no_inlines, |
579 | "Don't search inlined functions" ), |
580 | OPT__DRY_RUN(&probe_event_dry_run), |
581 | OPT_INTEGER('\0', "max-probes" , &probe_conf.max_probes, |
582 | "Set how many probe points can be found for a probe." ), |
583 | OPT_CALLBACK_DEFAULT('F', "funcs" , NULL, "[FILTER]" , |
584 | "Show potential probe-able functions." , |
585 | opt_set_filter_with_command, DEFAULT_FUNC_FILTER), |
586 | OPT_CALLBACK('\0', "filter" , NULL, |
587 | "[!]FILTER" , "Set a filter (with --vars/funcs only)\n" |
588 | "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n" |
589 | "\t\t\t \"" DEFAULT_FUNC_FILTER "\" for --funcs)" , |
590 | opt_set_filter), |
591 | OPT_CALLBACK('x', "exec" , NULL, "executable|path" , |
592 | "target executable name or path" , opt_set_target), |
593 | OPT_CALLBACK('m', "module" , NULL, "modname|path" , |
594 | "target module name (for online) or path (for offline)" , |
595 | opt_set_target), |
596 | OPT_BOOLEAN(0, "demangle" , &symbol_conf.demangle, |
597 | "Enable symbol demangling" ), |
598 | OPT_BOOLEAN(0, "demangle-kernel" , &symbol_conf.demangle_kernel, |
599 | "Enable kernel symbol demangling" ), |
600 | OPT_BOOLEAN(0, "cache" , &probe_conf.cache, "Manipulate probe cache" ), |
601 | OPT_STRING(0, "symfs" , &symbol_conf.symfs, "directory" , |
602 | "Look for files with symbols relative to this directory" ), |
603 | OPT_CALLBACK(0, "target-ns" , NULL, "pid" , |
604 | "target pid for namespace contexts" , opt_set_target_ns), |
605 | OPT_BOOLEAN(0, "bootconfig" , &probe_conf.bootconfig, |
606 | "Output probe definition with bootconfig format" ), |
607 | OPT_END() |
608 | }; |
609 | int ret; |
610 | |
611 | set_option_flag(options, 'a', "add" , PARSE_OPT_EXCLUSIVE); |
612 | set_option_flag(options, 'd', "del" , PARSE_OPT_EXCLUSIVE); |
613 | set_option_flag(options, 'D', "definition" , PARSE_OPT_EXCLUSIVE); |
614 | set_option_flag(options, 'l', "list" , PARSE_OPT_EXCLUSIVE); |
615 | #ifdef HAVE_DWARF_SUPPORT |
616 | set_option_flag(options, 'L', "line" , PARSE_OPT_EXCLUSIVE); |
617 | set_option_flag(options, 'V', "vars" , PARSE_OPT_EXCLUSIVE); |
618 | #else |
619 | # define set_nobuild(s, l, c) set_option_nobuild(options, s, l, "NO_DWARF=1", c) |
620 | set_nobuild('L', "line" , false); |
621 | set_nobuild('V', "vars" , false); |
622 | set_nobuild('\0', "externs" , false); |
623 | set_nobuild('\0', "range" , false); |
624 | set_nobuild('k', "vmlinux" , true); |
625 | set_nobuild('s', "source" , true); |
626 | set_nobuild('\0', "no-inlines" , true); |
627 | # undef set_nobuild |
628 | #endif |
629 | set_option_flag(options, 'F', "funcs" , PARSE_OPT_EXCLUSIVE); |
630 | |
631 | argc = parse_options(argc, argv, options, probe_usage, |
632 | PARSE_OPT_STOP_AT_NON_OPTION); |
633 | |
634 | if (quiet) { |
635 | if (verbose != 0) { |
636 | pr_err(" Error: -v and -q are exclusive.\n" ); |
637 | return -EINVAL; |
638 | } |
639 | verbose = -1; |
640 | } |
641 | |
642 | if (argc > 0) { |
643 | if (strcmp(argv[0], "-" ) == 0) { |
644 | usage_with_options_msg(probe_usage, options, |
645 | "'-' is not supported.\n" ); |
646 | } |
647 | if (params->command && params->command != 'a') { |
648 | usage_with_options_msg(probe_usage, options, |
649 | "another command except --add is set.\n" ); |
650 | } |
651 | ret = parse_probe_event_argv(argc, argv); |
652 | if (ret < 0) { |
653 | pr_err_with_code(msg: " Error: Command Parse Error." , err: ret); |
654 | return ret; |
655 | } |
656 | params->command = 'a'; |
657 | } |
658 | |
659 | ret = symbol__validate_sym_arguments(); |
660 | if (ret) |
661 | return ret; |
662 | |
663 | if (probe_conf.max_probes == 0) |
664 | probe_conf.max_probes = MAX_PROBES; |
665 | |
666 | /* |
667 | * Only consider the user's kernel image path if given. |
668 | */ |
669 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
670 | |
671 | /* |
672 | * Except for --list, --del and --add, other command doesn't depend |
673 | * nor change running kernel. So if user gives offline vmlinux, |
674 | * ignore its buildid. |
675 | */ |
676 | if (!strchr("lda" , params->command) && symbol_conf.vmlinux_name) |
677 | symbol_conf.ignore_vmlinux_buildid = true; |
678 | |
679 | switch (params->command) { |
680 | case 'l': |
681 | if (params->uprobes) { |
682 | pr_err(" Error: Don't use --list with --exec.\n" ); |
683 | parse_options_usage(probe_usage, options, "l" , true); |
684 | parse_options_usage(NULL, options, "x" , true); |
685 | return -EINVAL; |
686 | } |
687 | ret = show_perf_probe_events(filter: params->filter); |
688 | if (ret < 0) |
689 | pr_err_with_code(msg: " Error: Failed to show event list." , err: ret); |
690 | return ret; |
691 | case 'F': |
692 | ret = show_available_funcs(module: params->target, nsi: params->nsi, |
693 | filter: params->filter, user: params->uprobes); |
694 | if (ret < 0) |
695 | pr_err_with_code(msg: " Error: Failed to show functions." , err: ret); |
696 | return ret; |
697 | #ifdef HAVE_DWARF_SUPPORT |
698 | case 'L': |
699 | ret = show_line_range(¶ms->line_range, params->target, |
700 | params->nsi, params->uprobes); |
701 | if (ret < 0) |
702 | pr_err_with_code(" Error: Failed to show lines." , ret); |
703 | return ret; |
704 | case 'V': |
705 | if (!params->filter) |
706 | params->filter = strfilter__new(DEFAULT_VAR_FILTER, |
707 | NULL); |
708 | |
709 | ret = show_available_vars(params->events, params->nevents, |
710 | params->filter); |
711 | if (ret < 0) |
712 | pr_err_with_code(" Error: Failed to show vars." , ret); |
713 | return ret; |
714 | #endif |
715 | case 'd': |
716 | ret = perf_del_probe_events(filter: params->filter); |
717 | if (ret < 0) { |
718 | pr_err_with_code(msg: " Error: Failed to delete events." , err: ret); |
719 | return ret; |
720 | } |
721 | break; |
722 | case 'D': |
723 | if (probe_conf.bootconfig && params->uprobes) { |
724 | pr_err(" Error: --bootconfig doesn't support uprobes.\n" ); |
725 | return -EINVAL; |
726 | } |
727 | fallthrough; |
728 | case 'a': |
729 | |
730 | /* Ensure the last given target is used */ |
731 | if (params->target && !params->target_used) { |
732 | pr_err(" Error: -x/-m must follow the probe definitions.\n" ); |
733 | parse_options_usage(probe_usage, options, "m" , true); |
734 | parse_options_usage(NULL, options, "x" , true); |
735 | return -EINVAL; |
736 | } |
737 | |
738 | ret = perf_add_probe_events(pevs: params->events, npevs: params->nevents); |
739 | if (ret < 0) { |
740 | |
741 | /* |
742 | * When perf_add_probe_events() fails it calls |
743 | * cleanup_perf_probe_events(pevs, npevs), i.e. |
744 | * cleanup_perf_probe_events(params->events, params->nevents), which |
745 | * will call clear_perf_probe_event(), so set nevents to zero |
746 | * to avoid cleanup_params() to call clear_perf_probe_event() again |
747 | * on the same pevs. |
748 | */ |
749 | params->nevents = 0; |
750 | pr_err_with_code(msg: " Error: Failed to add events." , err: ret); |
751 | return ret; |
752 | } |
753 | break; |
754 | default: |
755 | usage_with_options(probe_usage, options); |
756 | } |
757 | return 0; |
758 | } |
759 | |
760 | int cmd_probe(int argc, const char **argv) |
761 | { |
762 | int ret; |
763 | |
764 | ret = init_params(); |
765 | if (!ret) { |
766 | ret = __cmd_probe(argc, argv); |
767 | cleanup_params(); |
768 | } |
769 | |
770 | return ret < 0 ? ret : 0; |
771 | } |
772 | |