2009-10-09 01:17:38 +04:00
|
|
|
/*
|
|
|
|
* builtin-probe.c
|
|
|
|
*
|
|
|
|
* Builtin probe command: Set up probe events by C expression
|
|
|
|
*
|
|
|
|
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "perf.h"
|
|
|
|
#include "builtin.h"
|
|
|
|
#include "util/util.h"
|
2009-12-09 01:03:23 +03:00
|
|
|
#include "util/strlist.h"
|
2011-01-20 17:15:39 +03:00
|
|
|
#include "util/strfilter.h"
|
2010-03-17 01:05:37 +03:00
|
|
|
#include "util/symbol.h"
|
2009-10-17 04:08:10 +04:00
|
|
|
#include "util/debug.h"
|
2015-12-15 18:39:39 +03:00
|
|
|
#include <subcmd/parse-options.h>
|
2009-10-09 01:17:38 +04:00
|
|
|
#include "util/probe-finder.h"
|
2009-12-01 03:19:58 +03:00
|
|
|
#include "util/probe-event.h"
|
2015-09-04 15:16:03 +03:00
|
|
|
#include "util/probe-file.h"
|
2009-10-09 01:17:38 +04:00
|
|
|
|
2011-01-20 17:15:39 +03:00
|
|
|
#define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
|
2011-01-20 17:15:45 +03:00
|
|
|
#define DEFAULT_FUNC_FILTER "!_*"
|
2016-07-01 11:03:26 +03:00
|
|
|
#define DEFAULT_LIST_FILTER "*"
|
2009-10-09 01:17:38 +04:00
|
|
|
|
|
|
|
/* Session management structure */
|
|
|
|
static struct {
|
2015-05-05 18:22:57 +03:00
|
|
|
int command; /* Command short_name */
|
2009-12-15 18:31:14 +03:00
|
|
|
bool list_events;
|
2012-04-16 16:09:09 +04:00
|
|
|
bool uprobes;
|
2014-10-27 23:31:31 +03:00
|
|
|
bool quiet;
|
2015-04-01 13:25:42 +03:00
|
|
|
bool target_used;
|
2010-03-17 01:06:12 +03:00
|
|
|
int nevents;
|
|
|
|
struct perf_probe_event events[MAX_PROBES];
|
2010-01-06 17:45:34 +03:00
|
|
|
struct line_range line_range;
|
2014-01-16 13:39:47 +04:00
|
|
|
char *target;
|
2011-01-20 17:15:39 +03:00
|
|
|
struct strfilter *filter;
|
2010-03-17 01:05:44 +03:00
|
|
|
} params;
|
2009-10-09 01:17:38 +04:00
|
|
|
|
2009-10-27 23:43:10 +03:00
|
|
|
/* Parse an event definition. Note that any error must die. */
|
2010-04-12 21:17:42 +04:00
|
|
|
static int parse_probe_event(const char *str)
|
2009-10-09 01:17:38 +04:00
|
|
|
{
|
2010-03-17 01:06:12 +03:00
|
|
|
struct perf_probe_event *pev = ¶ms.events[params.nevents];
|
2010-04-12 21:17:42 +04:00
|
|
|
int ret;
|
2009-10-09 01:17:38 +04:00
|
|
|
|
2010-03-17 01:06:12 +03:00
|
|
|
pr_debug("probe-definition(%d): %s\n", params.nevents, str);
|
2010-05-19 05:57:27 +04:00
|
|
|
if (++params.nevents == MAX_PROBES) {
|
|
|
|
pr_err("Too many probes (> %d) were specified.", MAX_PROBES);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-10-09 01:17:38 +04:00
|
|
|
|
2012-04-16 16:09:09 +04:00
|
|
|
pev->uprobes = params.uprobes;
|
2015-04-01 13:25:39 +03:00
|
|
|
if (params.target) {
|
|
|
|
pev->target = strdup(params.target);
|
|
|
|
if (!pev->target)
|
|
|
|
return -ENOMEM;
|
2015-04-01 13:25:42 +03:00
|
|
|
params.target_used = true;
|
2015-04-01 13:25:39 +03:00
|
|
|
}
|
2012-04-16 16:09:09 +04:00
|
|
|
|
2010-03-17 01:06:12 +03:00
|
|
|
/* Parse a perf-probe command into event */
|
2010-04-12 21:17:42 +04:00
|
|
|
ret = parse_perf_probe_command(str, pev);
|
2010-03-17 01:06:12 +03:00
|
|
|
pr_debug("%d arguments\n", pev->nargs);
|
2010-04-12 21:17:42 +04:00
|
|
|
|
|
|
|
return ret;
|
2009-10-27 23:43:02 +03:00
|
|
|
}
|
|
|
|
|
2015-04-24 12:47:50 +03:00
|
|
|
static int params_add_filter(const char *str)
|
|
|
|
{
|
|
|
|
const char *err = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
pr_debug2("Add filter: %s\n", str);
|
|
|
|
if (!params.filter) {
|
|
|
|
params.filter = strfilter__new(str, &err);
|
|
|
|
if (!params.filter)
|
|
|
|
ret = err ? -EINVAL : -ENOMEM;
|
|
|
|
} else
|
|
|
|
ret = strfilter__or(params.filter, str, &err);
|
|
|
|
|
|
|
|
if (ret == -EINVAL) {
|
|
|
|
pr_err("Filter parse error at %td.\n", err - str + 1);
|
|
|
|
pr_err("Source: \"%s\"\n", str);
|
|
|
|
pr_err(" %*c\n", (int)(err - str + 1), '^');
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-04-16 16:09:25 +04:00
|
|
|
static int set_target(const char *ptr)
|
|
|
|
{
|
|
|
|
int found = 0;
|
|
|
|
const char *buf;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The first argument after options can be an absolute path
|
|
|
|
* to an executable / library or kernel module.
|
|
|
|
*
|
|
|
|
* TODO: Support relative path, and $PATH, $LD_LIBRARY_PATH,
|
|
|
|
* short module name.
|
|
|
|
*/
|
|
|
|
if (!params.target && ptr && *ptr == '/') {
|
2014-01-16 13:39:47 +04:00
|
|
|
params.target = strdup(ptr);
|
|
|
|
if (!params.target)
|
|
|
|
return -ENOMEM;
|
2015-04-01 13:25:42 +03:00
|
|
|
params.target_used = false;
|
2014-01-16 13:39:47 +04:00
|
|
|
|
2012-04-16 16:09:25 +04:00
|
|
|
found = 1;
|
|
|
|
buf = ptr + (strlen(ptr) - 3);
|
|
|
|
|
|
|
|
if (strcmp(buf, ".ko"))
|
|
|
|
params.uprobes = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2010-04-12 21:17:42 +04:00
|
|
|
static int parse_probe_event_argv(int argc, const char **argv)
|
2009-12-09 01:02:54 +03:00
|
|
|
{
|
2012-04-16 16:09:25 +04:00
|
|
|
int i, len, ret, found_target;
|
2009-12-09 01:02:54 +03:00
|
|
|
char *buf;
|
|
|
|
|
2012-04-16 16:09:25 +04:00
|
|
|
found_target = set_target(argv[0]);
|
2014-01-16 13:39:47 +04:00
|
|
|
if (found_target < 0)
|
|
|
|
return found_target;
|
|
|
|
|
2012-04-16 16:09:25 +04:00
|
|
|
if (found_target && argc == 1)
|
|
|
|
return 0;
|
|
|
|
|
2009-12-09 01:02:54 +03:00
|
|
|
/* Bind up rest arguments */
|
|
|
|
len = 0;
|
2012-04-16 16:09:25 +04:00
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
if (i == 0 && found_target)
|
|
|
|
continue;
|
|
|
|
|
2009-12-09 01:02:54 +03:00
|
|
|
len += strlen(argv[i]) + 1;
|
2012-04-16 16:09:25 +04:00
|
|
|
}
|
2010-05-19 05:57:27 +04:00
|
|
|
buf = zalloc(len + 1);
|
|
|
|
if (buf == NULL)
|
|
|
|
return -ENOMEM;
|
2009-12-09 01:02:54 +03:00
|
|
|
len = 0;
|
2012-04-16 16:09:25 +04:00
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
if (i == 0 && found_target)
|
|
|
|
continue;
|
|
|
|
|
2009-12-09 01:02:54 +03:00
|
|
|
len += sprintf(&buf[len], "%s ", argv[i]);
|
2012-04-16 16:09:25 +04:00
|
|
|
}
|
2010-04-12 21:17:42 +04:00
|
|
|
ret = parse_probe_event(buf);
|
2009-12-09 01:02:54 +03:00
|
|
|
free(buf);
|
2010-04-12 21:17:42 +04:00
|
|
|
return ret;
|
2009-12-09 01:02:54 +03:00
|
|
|
}
|
|
|
|
|
2012-04-16 16:09:09 +04:00
|
|
|
static int opt_set_target(const struct option *opt, const char *str,
|
2012-09-11 02:15:03 +04:00
|
|
|
int unset __maybe_unused)
|
2012-04-16 16:09:09 +04:00
|
|
|
{
|
|
|
|
int ret = -ENOENT;
|
2013-12-26 09:41:50 +04:00
|
|
|
char *tmp;
|
2012-04-16 16:09:09 +04:00
|
|
|
|
2015-04-01 13:25:39 +03:00
|
|
|
if (str) {
|
2012-04-16 16:09:09 +04:00
|
|
|
if (!strcmp(opt->long_name, "exec"))
|
|
|
|
params.uprobes = true;
|
|
|
|
else if (!strcmp(opt->long_name, "module"))
|
|
|
|
params.uprobes = false;
|
|
|
|
else
|
|
|
|
return ret;
|
|
|
|
|
2013-12-26 09:41:50 +04:00
|
|
|
/* Expand given path to absolute path, except for modulename */
|
|
|
|
if (params.uprobes || strchr(str, '/')) {
|
|
|
|
tmp = realpath(str, NULL);
|
|
|
|
if (!tmp) {
|
|
|
|
pr_warning("Failed to get the absolute path of %s: %m\n", str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tmp = strdup(str);
|
|
|
|
if (!tmp)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2015-04-01 13:25:39 +03:00
|
|
|
free(params.target);
|
2013-12-26 09:41:50 +04:00
|
|
|
params.target = tmp;
|
2015-04-01 13:25:42 +03:00
|
|
|
params.target_used = false;
|
2012-04-16 16:09:09 +04:00
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-05-05 18:22:57 +03:00
|
|
|
/* Command option callbacks */
|
|
|
|
|
2013-09-30 14:07:11 +04:00
|
|
|
#ifdef HAVE_DWARF_SUPPORT
|
2015-05-05 18:22:57 +03:00
|
|
|
static int opt_show_lines(const struct option *opt,
|
2012-09-11 02:15:03 +04:00
|
|
|
const char *str, int unset __maybe_unused)
|
2010-01-16 15:31:16 +03:00
|
|
|
{
|
2010-04-12 21:17:42 +04:00
|
|
|
int ret = 0;
|
|
|
|
|
2011-08-11 15:02:53 +04:00
|
|
|
if (!str)
|
|
|
|
return 0;
|
|
|
|
|
2015-05-05 18:22:57 +03:00
|
|
|
if (params.command == 'L') {
|
2011-08-11 15:02:53 +04:00
|
|
|
pr_warning("Warning: more than one --line options are"
|
|
|
|
" detected. Only the first one is valid.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-05-05 18:22:57 +03:00
|
|
|
params.command = opt->short_name;
|
2011-08-11 15:02:53 +04:00
|
|
|
ret = parse_line_range_desc(str, ¶ms.line_range);
|
2010-04-12 21:17:42 +04:00
|
|
|
|
|
|
|
return ret;
|
2010-01-16 15:31:16 +03:00
|
|
|
}
|
2010-10-21 14:13:23 +04:00
|
|
|
|
2015-05-05 18:22:57 +03:00
|
|
|
static int opt_show_vars(const struct option *opt,
|
2012-09-11 02:15:03 +04:00
|
|
|
const char *str, int unset __maybe_unused)
|
2010-10-21 14:13:23 +04:00
|
|
|
{
|
|
|
|
struct perf_probe_event *pev = ¶ms.events[params.nevents];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret = parse_probe_event(str);
|
|
|
|
if (!ret && pev->nargs != 0) {
|
|
|
|
pr_err(" Error: '--vars' doesn't accept arguments.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2015-05-05 18:22:57 +03:00
|
|
|
params.command = opt->short_name;
|
2010-10-21 14:13:23 +04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
perf tools: Make options always available, even if required libs not linked
This patch keeps options of perf builtins same in all conditions. If
one option is disabled because of compiling options, users should be
notified.
Masami suggested another implementation in [1] that, by adding a
OPTION_NEXT_DEPENDS option before those options in the 'struct option'
array, options parser knows an option is disabled. However, in some
cases this array is reordered (options__order()). In addition, in
parse-option.c that array is const, so we can't simply merge
information in decorator option into the affacted option.
This patch chooses a simpler implementation that, introducing a
set_option_nobuild() function and two option parsing flags. Builtins
with such options should call set_option_nobuild() before option
parsing. The complexity of this patch is because we want some of options
can be skipped safely. In this case their arguments should also be
consumed.
Options in 'perf record' and 'perf probe' are fixed in this patch.
[1] http://lkml.kernel.org/g/50399556C9727B4D88A595C8584AAB3752627CD4@GSjpTKYDCembx32.service.hitachi.net
Test result:
Normal case:
# ./perf probe --vmlinux /tmp/vmlinux sys_write
Added new event:
probe:sys_write (on sys_write)
You can now use it in all perf tools, such as:
perf record -e probe:sys_write -aR sleep 1
Build with NO_DWARF=1:
# ./perf probe -L sys_write
Error: switch `L' is not available because NO_DWARF=1
Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
or: perf probe [<options>] --del '[GROUP:]EVENT' ...
or: perf probe --list [GROUP:]EVENT ...
or: perf probe [<options>] --funcs
-L, --line <FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]>
Show source code lines.
(not built-in because NO_DWARF=1)
# ./perf probe -k /tmp/vmlinux sys_write
Warning: switch `k' is being ignored because NO_DWARF=1
Added new event:
probe:sys_write (on sys_write)
You can now use it in all perf tools, such as:
perf record -e probe:sys_write -aR sleep 1
# ./perf probe --vmlinux /tmp/vmlinux sys_write
Warning: option `vmlinux' is being ignored because NO_DWARF=1
Added new event:
[SNIP]
# ./perf probe -l
Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
...
-k, --vmlinux <file> vmlinux pathname
(not built-in because NO_DWARF=1)
-L, --line <FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]>
Show source code lines.
(not built-in because NO_DWARF=1)
...
-V, --vars <FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT>
Show accessible variables on PROBEDEF
(not built-in because NO_DWARF=1)
--externs Show external variables too (with --vars only)
(not built-in because NO_DWARF=1)
--no-inlines Don't search inlined functions
(not built-in because NO_DWARF=1)
--range Show variables location range in scope (with --vars only)
(not built-in because NO_DWARF=1)
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1450089563-122430-14-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-12-14 13:39:22 +03:00
|
|
|
#else
|
|
|
|
# define opt_show_lines NULL
|
|
|
|
# define opt_show_vars NULL
|
2011-01-20 17:15:45 +03:00
|
|
|
#endif
|
2015-05-05 18:22:57 +03:00
|
|
|
static int opt_add_probe_event(const struct option *opt,
|
|
|
|
const char *str, int unset __maybe_unused)
|
|
|
|
{
|
|
|
|
if (str) {
|
|
|
|
params.command = opt->short_name;
|
|
|
|
return parse_probe_event(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int opt_set_filter_with_command(const struct option *opt,
|
|
|
|
const char *str, int unset)
|
2015-05-05 05:29:50 +03:00
|
|
|
{
|
|
|
|
if (!unset)
|
2015-05-05 18:22:57 +03:00
|
|
|
params.command = opt->short_name;
|
2015-05-05 05:29:50 +03:00
|
|
|
|
|
|
|
if (str)
|
|
|
|
return params_add_filter(str);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-20 17:15:39 +03:00
|
|
|
|
2012-09-11 02:15:03 +04:00
|
|
|
static int opt_set_filter(const struct option *opt __maybe_unused,
|
|
|
|
const char *str, int unset __maybe_unused)
|
2011-01-20 17:15:39 +03:00
|
|
|
{
|
2015-04-24 12:47:50 +03:00
|
|
|
if (str)
|
|
|
|
return params_add_filter(str);
|
2011-01-20 17:15:39 +03:00
|
|
|
|
2015-04-24 12:47:50 +03:00
|
|
|
return 0;
|
2011-01-20 17:15:39 +03:00
|
|
|
}
|
2009-10-09 01:17:38 +04:00
|
|
|
|
2014-02-06 09:32:09 +04:00
|
|
|
static int init_params(void)
|
2014-01-16 13:39:47 +04:00
|
|
|
{
|
2014-02-06 09:32:09 +04:00
|
|
|
return line_range__init(¶ms.line_range);
|
2014-01-16 13:39:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void cleanup_params(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < params.nevents; i++)
|
|
|
|
clear_perf_probe_event(params.events + i);
|
|
|
|
line_range__clear(¶ms.line_range);
|
|
|
|
free(params.target);
|
2015-07-04 08:44:22 +03:00
|
|
|
strfilter__delete(params.filter);
|
2014-01-16 13:39:47 +04:00
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
}
|
|
|
|
|
2014-06-06 11:13:52 +04:00
|
|
|
static void pr_err_with_code(const char *msg, int err)
|
|
|
|
{
|
2014-08-14 06:22:34 +04:00
|
|
|
char sbuf[STRERR_BUFSIZE];
|
|
|
|
|
2014-06-06 11:13:52 +04:00
|
|
|
pr_err("%s", msg);
|
2014-08-14 06:22:34 +04:00
|
|
|
pr_debug(" Reason: %s (Code: %d)",
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 17:56:20 +03:00
|
|
|
str_error_r(-err, sbuf, sizeof(sbuf)), err);
|
2014-06-06 11:13:52 +04:00
|
|
|
pr_err("\n");
|
|
|
|
}
|
|
|
|
|
2015-09-04 15:16:01 +03:00
|
|
|
static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int i, k;
|
|
|
|
const char *event = NULL, *group = NULL;
|
|
|
|
|
2015-09-10 05:27:05 +03:00
|
|
|
ret = init_probe_symbol_maps(pevs->uprobes);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2015-09-04 15:16:01 +03:00
|
|
|
ret = convert_perf_probe_events(pevs, npevs);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_cleanup;
|
|
|
|
|
2016-08-25 19:24:27 +03:00
|
|
|
if (params.command == 'D') { /* it shows definition */
|
|
|
|
ret = show_probe_trace_events(pevs, npevs);
|
|
|
|
goto out_cleanup;
|
|
|
|
}
|
|
|
|
|
2015-09-04 15:16:01 +03:00
|
|
|
ret = apply_perf_probe_events(pevs, npevs);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_cleanup;
|
|
|
|
|
|
|
|
for (i = k = 0; i < npevs; i++)
|
|
|
|
k += pevs[i].ntevs;
|
|
|
|
|
|
|
|
pr_info("Added new event%s\n", (k > 1) ? "s:" : ":");
|
|
|
|
for (i = 0; i < npevs; i++) {
|
|
|
|
struct perf_probe_event *pev = &pevs[i];
|
|
|
|
|
|
|
|
for (k = 0; k < pev->ntevs; k++) {
|
|
|
|
struct probe_trace_event *tev = &pev->tevs[k];
|
|
|
|
|
|
|
|
/* We use tev's name for showing new events */
|
|
|
|
show_perf_probe_event(tev->group, tev->event, pev,
|
|
|
|
tev->point.module, false);
|
|
|
|
|
|
|
|
/* Save the last valid name */
|
|
|
|
event = tev->event;
|
|
|
|
group = tev->group;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Note that it is possible to skip all events because of blacklist */
|
|
|
|
if (event) {
|
|
|
|
/* Show how to use the event. */
|
|
|
|
pr_info("\nYou can now use it in all perf tools, such as:\n\n");
|
|
|
|
pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event);
|
|
|
|
}
|
|
|
|
|
|
|
|
out_cleanup:
|
|
|
|
cleanup_perf_probe_events(pevs, npevs);
|
2015-09-10 05:27:05 +03:00
|
|
|
exit_probe_symbol_maps();
|
2015-09-04 15:16:01 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-07-01 11:03:36 +03:00
|
|
|
static int del_perf_probe_caches(struct strfilter *filter)
|
|
|
|
{
|
|
|
|
struct probe_cache *cache;
|
|
|
|
struct strlist *bidlist;
|
|
|
|
struct str_node *nd;
|
|
|
|
int ret;
|
|
|
|
|
2016-07-12 13:04:54 +03:00
|
|
|
bidlist = build_id_cache__list_all(false);
|
2016-07-01 11:03:36 +03:00
|
|
|
if (!bidlist) {
|
|
|
|
ret = -errno;
|
|
|
|
pr_debug("Failed to get buildids: %d\n", ret);
|
|
|
|
return ret ?: -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
strlist__for_each_entry(nd, bidlist) {
|
|
|
|
cache = probe_cache__new(nd->s);
|
|
|
|
if (!cache)
|
|
|
|
continue;
|
|
|
|
if (probe_cache__filter_purge(cache, filter) < 0 ||
|
|
|
|
probe_cache__commit(cache) < 0)
|
|
|
|
pr_warning("Failed to remove entries for %s\n", nd->s);
|
|
|
|
probe_cache__delete(cache);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-04 15:16:03 +03:00
|
|
|
static int perf_del_probe_events(struct strfilter *filter)
|
|
|
|
{
|
|
|
|
int ret, ret2, ufd = -1, kfd = -1;
|
|
|
|
char *str = strfilter__string(filter);
|
|
|
|
struct strlist *klist = NULL, *ulist = NULL;
|
|
|
|
struct str_node *ent;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
pr_debug("Delete filter: \'%s\'\n", str);
|
|
|
|
|
2016-07-01 11:03:36 +03:00
|
|
|
if (probe_conf.cache)
|
|
|
|
return del_perf_probe_caches(filter);
|
|
|
|
|
2015-09-04 15:16:03 +03:00
|
|
|
/* Get current event names */
|
|
|
|
ret = probe_file__open_both(&kfd, &ufd, PF_FL_RW);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
klist = strlist__new(NULL, NULL);
|
2015-09-16 15:52:42 +03:00
|
|
|
ulist = strlist__new(NULL, NULL);
|
|
|
|
if (!klist || !ulist) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-09-04 15:16:03 +03:00
|
|
|
|
|
|
|
ret = probe_file__get_events(kfd, filter, klist);
|
|
|
|
if (ret == 0) {
|
2016-06-23 17:31:20 +03:00
|
|
|
strlist__for_each_entry(ent, klist)
|
2015-09-04 15:16:03 +03:00
|
|
|
pr_info("Removed event: %s\n", ent->s);
|
|
|
|
|
|
|
|
ret = probe_file__del_strlist(kfd, klist);
|
|
|
|
if (ret < 0)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret2 = probe_file__get_events(ufd, filter, ulist);
|
|
|
|
if (ret2 == 0) {
|
2016-06-23 17:31:20 +03:00
|
|
|
strlist__for_each_entry(ent, ulist)
|
2015-09-04 15:16:03 +03:00
|
|
|
pr_info("Removed event: %s\n", ent->s);
|
|
|
|
|
|
|
|
ret2 = probe_file__del_strlist(ufd, ulist);
|
|
|
|
if (ret2 < 0)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == -ENOENT && ret2 == -ENOENT)
|
|
|
|
pr_debug("\"%s\" does not hit any event.\n", str);
|
|
|
|
/* Note that this is silently ignored */
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (kfd >= 0)
|
|
|
|
close(kfd);
|
|
|
|
if (ufd >= 0)
|
|
|
|
close(ufd);
|
|
|
|
out:
|
|
|
|
strlist__delete(klist);
|
|
|
|
strlist__delete(ulist);
|
|
|
|
free(str);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-08-25 19:24:27 +03:00
|
|
|
#ifdef HAVE_DWARF_SUPPORT
|
|
|
|
#define PROBEDEF_STR \
|
|
|
|
"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT [[NAME=]ARG ...]"
|
|
|
|
#else
|
|
|
|
#define PROBEDEF_STR "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2014-01-16 13:39:47 +04:00
|
|
|
static int
|
|
|
|
__cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
|
2012-10-01 22:20:58 +04:00
|
|
|
{
|
|
|
|
const char * const probe_usage[] = {
|
|
|
|
"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
|
|
|
|
"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
|
|
|
|
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
|
2015-04-24 12:47:50 +03:00
|
|
|
"perf probe --list [GROUP:]EVENT ...",
|
2013-09-30 14:07:11 +04:00
|
|
|
#ifdef HAVE_DWARF_SUPPORT
|
2012-10-01 22:20:58 +04:00
|
|
|
"perf probe [<options>] --line 'LINEDESC'",
|
|
|
|
"perf probe [<options>] --vars 'PROBEPOINT'",
|
2010-02-25 16:35:12 +03:00
|
|
|
#endif
|
2015-04-23 16:46:12 +03:00
|
|
|
"perf probe [<options>] --funcs",
|
2012-10-01 22:20:58 +04:00
|
|
|
NULL
|
2015-04-24 12:47:50 +03:00
|
|
|
};
|
2014-10-22 19:15:49 +04:00
|
|
|
struct option options[] = {
|
2010-04-13 12:37:33 +04:00
|
|
|
OPT_INCR('v', "verbose", &verbose,
|
2009-10-17 04:08:10 +04:00
|
|
|
"be more verbose (show parsed arguments, etc)"),
|
2014-10-27 23:31:31 +03:00
|
|
|
OPT_BOOLEAN('q', "quiet", ¶ms.quiet,
|
|
|
|
"be quiet (do not show any mesages)"),
|
2015-04-24 12:47:50 +03:00
|
|
|
OPT_CALLBACK_DEFAULT('l', "list", NULL, "[GROUP:]EVENT",
|
2015-05-05 18:22:57 +03:00
|
|
|
"list up probe events",
|
|
|
|
opt_set_filter_with_command, DEFAULT_LIST_FILTER),
|
2009-12-09 01:03:23 +03:00
|
|
|
OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
|
2015-05-05 18:22:57 +03:00
|
|
|
opt_set_filter_with_command),
|
2016-08-25 19:24:27 +03:00
|
|
|
OPT_CALLBACK('a', "add", NULL, PROBEDEF_STR,
|
2009-10-09 01:17:38 +04:00
|
|
|
"probe point definition, where\n"
|
2009-12-15 18:32:18 +03:00
|
|
|
"\t\tGROUP:\tGroup name (optional)\n"
|
|
|
|
"\t\tEVENT:\tEvent name\n"
|
2009-10-09 01:17:38 +04:00
|
|
|
"\t\tFUNC:\tFunction name\n"
|
2010-02-25 16:36:12 +03:00
|
|
|
"\t\tOFF:\tOffset from function entry (in byte)\n"
|
2009-10-27 23:43:10 +03:00
|
|
|
"\t\t%return:\tPut the probe at function return\n"
|
2013-09-30 14:07:11 +04:00
|
|
|
#ifdef HAVE_DWARF_SUPPORT
|
2009-10-09 01:17:38 +04:00
|
|
|
"\t\tSRC:\tSource code path\n"
|
2010-02-25 16:36:12 +03:00
|
|
|
"\t\tRL:\tRelative line number from function entry.\n"
|
|
|
|
"\t\tAL:\tAbsolute line number in file.\n"
|
|
|
|
"\t\tPT:\tLazy expression of line code.\n"
|
2009-10-09 01:17:38 +04:00
|
|
|
"\t\tARG:\tProbe argument (local variable name or\n"
|
2009-12-01 03:19:58 +03:00
|
|
|
"\t\t\tkprobe-tracer argument format.)\n",
|
2010-03-22 19:10:26 +03:00
|
|
|
#else
|
|
|
|
"\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n",
|
|
|
|
#endif
|
2009-10-27 23:43:10 +03:00
|
|
|
opt_add_probe_event),
|
2016-08-25 19:24:27 +03:00
|
|
|
OPT_CALLBACK('D', "definition", NULL, PROBEDEF_STR,
|
|
|
|
"Show trace event definition of given traceevent for k/uprobe_events.",
|
|
|
|
opt_add_probe_event),
|
2015-05-08 04:03:31 +03:00
|
|
|
OPT_BOOLEAN('f', "force", &probe_conf.force_add, "forcibly add events"
|
2009-12-15 18:32:25 +03:00
|
|
|
" with existing name"),
|
2010-01-06 17:45:34 +03:00
|
|
|
OPT_CALLBACK('L', "line", NULL,
|
2010-04-02 20:50:39 +04:00
|
|
|
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
|
2010-01-06 17:45:34 +03:00
|
|
|
"Show source code lines.", opt_show_lines),
|
2010-10-21 14:13:23 +04:00
|
|
|
OPT_CALLBACK('V', "vars", NULL,
|
|
|
|
"FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
|
|
|
|
"Show accessible variables on PROBEDEF", opt_show_vars),
|
2015-05-08 04:03:31 +03:00
|
|
|
OPT_BOOLEAN('\0', "externs", &probe_conf.show_ext_vars,
|
2010-10-21 14:13:35 +04:00
|
|
|
"Show external variables too (with --vars only)"),
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 12:25:03 +03:00
|
|
|
OPT_BOOLEAN('\0', "range", &probe_conf.show_location_range,
|
|
|
|
"Show variables location range in scope (with --vars only)"),
|
2010-03-22 19:10:26 +03:00
|
|
|
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
|
|
|
"file", "vmlinux pathname"),
|
2010-06-14 23:26:30 +04:00
|
|
|
OPT_STRING('s', "source", &symbol_conf.source_prefix,
|
|
|
|
"directory", "path to kernel source"),
|
2015-05-08 04:03:33 +03:00
|
|
|
OPT_BOOLEAN('\0', "no-inlines", &probe_conf.no_inlines,
|
|
|
|
"Don't search inlined functions"),
|
2010-03-17 01:06:05 +03:00
|
|
|
OPT__DRY_RUN(&probe_event_dry_run),
|
2015-05-08 04:03:31 +03:00
|
|
|
OPT_INTEGER('\0', "max-probes", &probe_conf.max_probes,
|
2010-04-21 23:56:40 +04:00
|
|
|
"Set how many probe points can be found for a probe."),
|
2015-05-05 05:29:50 +03:00
|
|
|
OPT_CALLBACK_DEFAULT('F', "funcs", NULL, "[FILTER]",
|
|
|
|
"Show potential probe-able functions.",
|
2015-05-05 18:22:57 +03:00
|
|
|
opt_set_filter_with_command, DEFAULT_FUNC_FILTER),
|
2011-01-20 17:15:45 +03:00
|
|
|
OPT_CALLBACK('\0', "filter", NULL,
|
|
|
|
"[!]FILTER", "Set a filter (with --vars/funcs only)\n"
|
|
|
|
"\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n"
|
|
|
|
"\t\t\t \"" DEFAULT_FUNC_FILTER "\" for --funcs)",
|
|
|
|
opt_set_filter),
|
2012-04-16 16:09:09 +04:00
|
|
|
OPT_CALLBACK('x', "exec", NULL, "executable|path",
|
|
|
|
"target executable name or path", opt_set_target),
|
2015-10-02 15:58:32 +03:00
|
|
|
OPT_CALLBACK('m', "module", NULL, "modname|path",
|
|
|
|
"target module name (for online) or path (for offline)",
|
|
|
|
opt_set_target),
|
2013-10-28 12:04:24 +04:00
|
|
|
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
|
2014-10-27 23:31:24 +03:00
|
|
|
"Enable symbol demangling"),
|
2014-09-13 08:15:05 +04:00
|
|
|
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
|
|
|
|
"Enable kernel symbol demangling"),
|
2016-06-15 06:28:40 +03:00
|
|
|
OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
|
2009-10-09 01:17:38 +04:00
|
|
|
OPT_END()
|
2012-10-01 22:20:58 +04:00
|
|
|
};
|
2010-04-12 21:17:42 +04:00
|
|
|
int ret;
|
|
|
|
|
2014-10-22 19:15:49 +04:00
|
|
|
set_option_flag(options, 'a', "add", PARSE_OPT_EXCLUSIVE);
|
|
|
|
set_option_flag(options, 'd', "del", PARSE_OPT_EXCLUSIVE);
|
2016-08-25 19:24:27 +03:00
|
|
|
set_option_flag(options, 'D', "definition", PARSE_OPT_EXCLUSIVE);
|
2014-10-22 19:15:49 +04:00
|
|
|
set_option_flag(options, 'l', "list", PARSE_OPT_EXCLUSIVE);
|
|
|
|
#ifdef HAVE_DWARF_SUPPORT
|
|
|
|
set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE);
|
|
|
|
set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE);
|
perf tools: Make options always available, even if required libs not linked
This patch keeps options of perf builtins same in all conditions. If
one option is disabled because of compiling options, users should be
notified.
Masami suggested another implementation in [1] that, by adding a
OPTION_NEXT_DEPENDS option before those options in the 'struct option'
array, options parser knows an option is disabled. However, in some
cases this array is reordered (options__order()). In addition, in
parse-option.c that array is const, so we can't simply merge
information in decorator option into the affacted option.
This patch chooses a simpler implementation that, introducing a
set_option_nobuild() function and two option parsing flags. Builtins
with such options should call set_option_nobuild() before option
parsing. The complexity of this patch is because we want some of options
can be skipped safely. In this case their arguments should also be
consumed.
Options in 'perf record' and 'perf probe' are fixed in this patch.
[1] http://lkml.kernel.org/g/50399556C9727B4D88A595C8584AAB3752627CD4@GSjpTKYDCembx32.service.hitachi.net
Test result:
Normal case:
# ./perf probe --vmlinux /tmp/vmlinux sys_write
Added new event:
probe:sys_write (on sys_write)
You can now use it in all perf tools, such as:
perf record -e probe:sys_write -aR sleep 1
Build with NO_DWARF=1:
# ./perf probe -L sys_write
Error: switch `L' is not available because NO_DWARF=1
Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
or: perf probe [<options>] --del '[GROUP:]EVENT' ...
or: perf probe --list [GROUP:]EVENT ...
or: perf probe [<options>] --funcs
-L, --line <FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]>
Show source code lines.
(not built-in because NO_DWARF=1)
# ./perf probe -k /tmp/vmlinux sys_write
Warning: switch `k' is being ignored because NO_DWARF=1
Added new event:
probe:sys_write (on sys_write)
You can now use it in all perf tools, such as:
perf record -e probe:sys_write -aR sleep 1
# ./perf probe --vmlinux /tmp/vmlinux sys_write
Warning: option `vmlinux' is being ignored because NO_DWARF=1
Added new event:
[SNIP]
# ./perf probe -l
Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
...
-k, --vmlinux <file> vmlinux pathname
(not built-in because NO_DWARF=1)
-L, --line <FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]>
Show source code lines.
(not built-in because NO_DWARF=1)
...
-V, --vars <FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT>
Show accessible variables on PROBEDEF
(not built-in because NO_DWARF=1)
--externs Show external variables too (with --vars only)
(not built-in because NO_DWARF=1)
--no-inlines Don't search inlined functions
(not built-in because NO_DWARF=1)
--range Show variables location range in scope (with --vars only)
(not built-in because NO_DWARF=1)
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1450089563-122430-14-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-12-14 13:39:22 +03:00
|
|
|
#else
|
|
|
|
# define set_nobuild(s, l, c) set_option_nobuild(options, s, l, "NO_DWARF=1", c)
|
|
|
|
set_nobuild('L', "line", false);
|
|
|
|
set_nobuild('V', "vars", false);
|
|
|
|
set_nobuild('\0', "externs", false);
|
|
|
|
set_nobuild('\0', "range", false);
|
|
|
|
set_nobuild('k', "vmlinux", true);
|
|
|
|
set_nobuild('s', "source", true);
|
|
|
|
set_nobuild('\0', "no-inlines", true);
|
|
|
|
# undef set_nobuild
|
2014-10-22 19:15:49 +04:00
|
|
|
#endif
|
2015-04-23 16:46:12 +03:00
|
|
|
set_option_flag(options, 'F', "funcs", PARSE_OPT_EXCLUSIVE);
|
2014-10-22 19:15:49 +04:00
|
|
|
|
2009-10-09 01:17:38 +04:00
|
|
|
argc = parse_options(argc, argv, options, probe_usage,
|
2009-10-27 23:43:02 +03:00
|
|
|
PARSE_OPT_STOP_AT_NON_OPTION);
|
2009-12-15 18:31:28 +03:00
|
|
|
if (argc > 0) {
|
|
|
|
if (strcmp(argv[0], "-") == 0) {
|
2015-10-24 18:49:27 +03:00
|
|
|
usage_with_options_msg(probe_usage, options,
|
|
|
|
"'-' is not supported.\n");
|
2009-12-15 18:31:28 +03:00
|
|
|
}
|
2015-05-05 18:22:57 +03:00
|
|
|
if (params.command && params.command != 'a') {
|
2015-10-24 18:49:27 +03:00
|
|
|
usage_with_options_msg(probe_usage, options,
|
|
|
|
"another command except --add is set.\n");
|
2015-05-05 18:22:57 +03:00
|
|
|
}
|
2010-04-12 21:17:42 +04:00
|
|
|
ret = parse_probe_event_argv(argc, argv);
|
|
|
|
if (ret < 0) {
|
2014-06-06 11:13:52 +04:00
|
|
|
pr_err_with_code(" Error: Command Parse Error.", ret);
|
2010-04-12 21:17:42 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2015-05-05 18:22:57 +03:00
|
|
|
params.command = 'a';
|
2009-12-15 18:31:28 +03:00
|
|
|
}
|
2009-10-27 23:43:02 +03:00
|
|
|
|
2014-10-27 23:31:31 +03:00
|
|
|
if (params.quiet) {
|
|
|
|
if (verbose != 0) {
|
|
|
|
pr_err(" Error: -v and -q are exclusive.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
verbose = -1;
|
|
|
|
}
|
|
|
|
|
2015-05-08 04:03:31 +03:00
|
|
|
if (probe_conf.max_probes == 0)
|
|
|
|
probe_conf.max_probes = MAX_PROBES;
|
2010-04-21 23:56:40 +04:00
|
|
|
|
2010-12-10 16:06:03 +03:00
|
|
|
/*
|
|
|
|
* Only consider the user's kernel image path if given.
|
|
|
|
*/
|
|
|
|
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
|
|
|
|
|
2016-08-26 17:57:58 +03:00
|
|
|
/*
|
|
|
|
* Except for --list, --del and --add, other command doesn't depend
|
|
|
|
* nor change running kernel. So if user gives offline vmlinux,
|
|
|
|
* ignore its buildid.
|
|
|
|
*/
|
|
|
|
if (!strchr("lda", params.command) && symbol_conf.vmlinux_name)
|
|
|
|
symbol_conf.ignore_vmlinux_buildid = true;
|
|
|
|
|
2015-05-05 18:22:57 +03:00
|
|
|
switch (params.command) {
|
|
|
|
case 'l':
|
2012-04-16 16:09:09 +04:00
|
|
|
if (params.uprobes) {
|
2015-10-24 18:49:27 +03:00
|
|
|
pr_err(" Error: Don't use --list with --exec.\n");
|
|
|
|
parse_options_usage(probe_usage, options, "l", true);
|
|
|
|
parse_options_usage(NULL, options, "x", true);
|
|
|
|
return -EINVAL;
|
2012-04-16 16:09:09 +04:00
|
|
|
}
|
2015-04-24 12:47:50 +03:00
|
|
|
ret = show_perf_probe_events(params.filter);
|
2010-04-12 21:17:42 +04:00
|
|
|
if (ret < 0)
|
2014-06-06 11:13:52 +04:00
|
|
|
pr_err_with_code(" Error: Failed to show event list.", ret);
|
2010-04-12 21:17:42 +04:00
|
|
|
return ret;
|
2015-05-05 18:22:57 +03:00
|
|
|
case 'F':
|
2012-04-16 16:09:09 +04:00
|
|
|
ret = show_available_funcs(params.target, params.filter,
|
|
|
|
params.uprobes);
|
2011-01-13 15:46:11 +03:00
|
|
|
if (ret < 0)
|
2014-06-06 11:13:52 +04:00
|
|
|
pr_err_with_code(" Error: Failed to show functions.", ret);
|
2011-01-13 15:46:11 +03:00
|
|
|
return ret;
|
2013-09-30 14:07:11 +04:00
|
|
|
#ifdef HAVE_DWARF_SUPPORT
|
2015-05-05 18:22:57 +03:00
|
|
|
case 'L':
|
2014-09-17 12:40:54 +04:00
|
|
|
ret = show_line_range(¶ms.line_range, params.target,
|
|
|
|
params.uprobes);
|
2010-04-12 21:17:42 +04:00
|
|
|
if (ret < 0)
|
2014-06-06 11:13:52 +04:00
|
|
|
pr_err_with_code(" Error: Failed to show lines.", ret);
|
2010-04-12 21:17:42 +04:00
|
|
|
return ret;
|
2015-05-05 18:22:57 +03:00
|
|
|
case 'V':
|
2011-01-20 17:15:39 +03:00
|
|
|
if (!params.filter)
|
|
|
|
params.filter = strfilter__new(DEFAULT_VAR_FILTER,
|
|
|
|
NULL);
|
|
|
|
|
2010-10-21 14:13:23 +04:00
|
|
|
ret = show_available_vars(params.events, params.nevents,
|
2015-05-08 04:03:31 +03:00
|
|
|
params.filter);
|
2010-10-21 14:13:23 +04:00
|
|
|
if (ret < 0)
|
2014-06-06 11:13:52 +04:00
|
|
|
pr_err_with_code(" Error: Failed to show vars.", ret);
|
2010-10-21 14:13:23 +04:00
|
|
|
return ret;
|
2010-01-06 17:45:34 +03:00
|
|
|
#endif
|
2015-05-05 18:22:57 +03:00
|
|
|
case 'd':
|
2015-09-04 15:16:03 +03:00
|
|
|
ret = perf_del_probe_events(params.filter);
|
2010-04-12 21:17:42 +04:00
|
|
|
if (ret < 0) {
|
2014-06-06 11:13:52 +04:00
|
|
|
pr_err_with_code(" Error: Failed to delete events.", ret);
|
2010-04-12 21:17:42 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2015-05-05 18:22:57 +03:00
|
|
|
break;
|
2016-08-25 19:24:27 +03:00
|
|
|
case 'D':
|
2016-08-25 19:24:42 +03:00
|
|
|
case 'a':
|
|
|
|
|
2015-04-01 13:25:42 +03:00
|
|
|
/* Ensure the last given target is used */
|
|
|
|
if (params.target && !params.target_used) {
|
2015-10-24 18:49:27 +03:00
|
|
|
pr_err(" Error: -x/-m must follow the probe definitions.\n");
|
|
|
|
parse_options_usage(probe_usage, options, "m", true);
|
|
|
|
parse_options_usage(NULL, options, "x", true);
|
|
|
|
return -EINVAL;
|
2015-04-01 13:25:42 +03:00
|
|
|
}
|
|
|
|
|
2015-09-04 15:16:01 +03:00
|
|
|
ret = perf_add_probe_events(params.events, params.nevents);
|
2010-04-12 21:17:42 +04:00
|
|
|
if (ret < 0) {
|
2014-06-06 11:13:52 +04:00
|
|
|
pr_err_with_code(" Error: Failed to add events.", ret);
|
2010-04-12 21:17:42 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2015-05-05 18:22:57 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage_with_options(probe_usage, options);
|
2010-04-12 21:17:42 +04:00
|
|
|
}
|
2009-10-09 01:17:38 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2014-01-16 13:39:47 +04:00
|
|
|
|
|
|
|
int cmd_probe(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2014-02-06 09:32:09 +04:00
|
|
|
ret = init_params();
|
|
|
|
if (!ret) {
|
|
|
|
ret = __cmd_probe(argc, argv, prefix);
|
|
|
|
cleanup_params();
|
|
|
|
}
|
2014-01-16 13:39:47 +04:00
|
|
|
|
2015-05-06 15:46:45 +03:00
|
|
|
return ret < 0 ? ret : 0;
|
2014-01-16 13:39:47 +04:00
|
|
|
}
|