perf/core improvements and fixes:
User visible: - Fix SIGBUS failures due to misaligned accesses in Sparc64 (David Ahern) - Fix branch stack mode in 'perf report' (He Kuang) - Fix a 'perf probe' operator precedence bug (He Kuang) - Fix Support for different binaries with same name in 'perf diff' (Kan Liang) - Check kprobes blacklist when adding new events via 'perf probe' (Masami Hiramatsu) - Add --purge FILE to remove all caches of FILE in 'perf buildid-cache' (Masami Hiramatsu) - Show usage with some incorrect params (Masami Hiramatsu) - Add new buildid cache if update target is not cached in 'buildid-cache' (Masami Hiramatsu) - Allow listing events with 'tracepoint' prefix in 'perf list' (Yunlong Song) - Sort the output of 'perf list' (Yunlong Song) - Fix bash completion of 'perf --' (Yunlong Song) Developer Zone: - Handle strdup() failure path in 'perf probe' (Arnaldo Carvalho de Melo) - Fix get_real_path to free allocated memory in error path in 'perf probe' (Masami Hiramatsu) - Use pr_debug instead of verbose && pr_info perf buildid-cache (Masami Hiramatsu) - Fix building of 'perf data' with some gcc versions due to incorrect array struct entry (Yunlong Song) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJU8MCoAAoJEBpxZoYYoA71ZOIIANNy2I+ZvipY4WxXf5L6V1TS fKlL8Kz2XXr7ZcSgTxU4dMFyiXJ/hFsDEpf1hKq2wjaf20UDdhLRY0jszdKrJ0Je 4n2Gt7gJy7odDBsgBI7GPXAceXrD2MKY6njKHjzsDFLJmqVPY3NqVqvt9Dufgfwm H5lWG35z9Jt/aJbaHeEe49YKBqoacJcqNiCxltE3VW8mIsKA82FUbOHxz03WU7mu fgjK4EN2oV7sOPAao8ZnkAG9Ib+dyJe5oTI9WkT1ofxM2doaqhX5/vi2t+KOprzC OB4miIBHody2yuiQZq+y6+BGSVvGqYsZM1sVerA3YQBiK6f4Pf94QxRo8yEJGzI= =dRR8 -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: User visible changes: - Fix SIGBUS failures due to misaligned accesses in Sparc64 (David Ahern) - Fix branch stack mode in 'perf report' (He Kuang) - Fix a 'perf probe' operator precedence bug (He Kuang) - Fix Support for different binaries with same name in 'perf diff' (Kan Liang) - Check kprobes blacklist when adding new events via 'perf probe' (Masami Hiramatsu) - Add --purge FILE to remove all caches of FILE in 'perf buildid-cache' (Masami Hiramatsu) - Show usage with some incorrect params (Masami Hiramatsu) - Add new buildid cache if update target is not cached in 'buildid-cache' (Masami Hiramatsu) - Allow listing events with 'tracepoint' prefix in 'perf list' (Yunlong Song) - Sort the output of 'perf list' (Yunlong Song) - Fix bash completion of 'perf --' (Yunlong Song) Developer Zone: - Handle strdup() failure path in 'perf probe' (Arnaldo Carvalho de Melo) - Fix get_real_path to free allocated memory in error path in 'perf probe' (Masami Hiramatsu) - Use pr_debug instead of verbose && pr_info perf buildid-cache (Masami Hiramatsu) - Fix building of 'perf data' with some gcc versions due to incorrect array struct entry (Yunlong Song) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
788b94ba6c
@ -12,9 +12,9 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command manages the build-id cache. It can add and remove files to/from
|
||||
the cache. In the future it should as well purge older entries, set upper
|
||||
limits for the space used by the cache, etc.
|
||||
This command manages the build-id cache. It can add, remove, update and purge
|
||||
files to/from the cache. In the future it should as well set upper limits for
|
||||
the space used by the cache, etc.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
@ -36,14 +36,24 @@ OPTIONS
|
||||
actually made.
|
||||
-r::
|
||||
--remove=::
|
||||
Remove specified file from the cache.
|
||||
Remove a cached binary which has same build-id of specified file
|
||||
from the cache.
|
||||
-p::
|
||||
--purge=::
|
||||
Purge all cached binaries including older caches which have specified
|
||||
path from the cache.
|
||||
-M::
|
||||
--missing=::
|
||||
List missing build ids in the cache for the specified file.
|
||||
-u::
|
||||
--update::
|
||||
Update specified file of the cache. It can be used to update kallsyms
|
||||
kernel dso to vmlinux in order to support annotation.
|
||||
--update=::
|
||||
Update specified file of the cache. Note that this doesn't remove
|
||||
older entires since those may be still needed for annotating old
|
||||
(or remote) perf.data. Only if there is already a cache which has
|
||||
exactly same build-id, that is replaced by new one. It can be used
|
||||
to update kallsyms and kernel dso to vmlinux in order to support
|
||||
annotation.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose.
|
||||
|
@ -20,6 +20,11 @@ If no parameters are passed it will assume perf.data.old and perf.data.
|
||||
The differential profile is displayed only for events matching both
|
||||
specified perf.data files.
|
||||
|
||||
If no parameters are passed the samples will be sorted by dso and symbol.
|
||||
As the perf.data files could come from different binaries, the symbols addresses
|
||||
could vary. So perf diff is based on the comparison of the files and
|
||||
symbols name.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-D::
|
||||
|
@ -127,6 +127,12 @@ To limit the list use:
|
||||
One or more types can be used at the same time, listing the events for the
|
||||
types specified.
|
||||
|
||||
Support raw format:
|
||||
|
||||
. '--raw-dump', shows the raw-dump of all the events.
|
||||
. '--raw-dump [hw|sw|cache|tracepoint|pmu|event_glob]', shows the raw-dump of
|
||||
a certain kind of events.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-top[1],
|
||||
|
@ -196,9 +196,8 @@ static int build_id_cache__add_file(const char *filename)
|
||||
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
||||
err = build_id_cache__add_s(sbuild_id, filename,
|
||||
false, false);
|
||||
if (verbose)
|
||||
pr_info("Adding %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -216,9 +215,33 @@ static int build_id_cache__remove_file(const char *filename)
|
||||
|
||||
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
||||
err = build_id_cache__remove_s(sbuild_id);
|
||||
if (verbose)
|
||||
pr_info("Removing %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
pr_debug("Removing %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int build_id_cache__purge_path(const char *pathname)
|
||||
{
|
||||
struct strlist *list;
|
||||
struct str_node *pos;
|
||||
int err;
|
||||
|
||||
err = build_id_cache__list_build_ids(pathname, &list);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
strlist__for_each(pos, list) {
|
||||
err = build_id_cache__remove_s(pos->s);
|
||||
pr_debug("Removing %s %s: %s\n", pos->s, pathname,
|
||||
err ? "FAIL" : "Ok");
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
strlist__delete(list);
|
||||
|
||||
out:
|
||||
pr_debug("Purging %s: %s\n", pathname, err ? "FAIL" : "Ok");
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -255,7 +278,7 @@ static int build_id_cache__update_file(const char *filename)
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
|
||||
pr_debug("Couldn't read a build-id in %s\n", filename);
|
||||
@ -263,13 +286,14 @@ static int build_id_cache__update_file(const char *filename)
|
||||
}
|
||||
|
||||
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
|
||||
err = build_id_cache__remove_s(sbuild_id);
|
||||
if (build_id_cache__cached(sbuild_id))
|
||||
err = build_id_cache__remove_s(sbuild_id);
|
||||
|
||||
if (!err)
|
||||
err = build_id_cache__add_s(sbuild_id, filename, false, false);
|
||||
|
||||
if (verbose)
|
||||
pr_info("Updating %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
pr_debug("Updating %s %s: %s\n", sbuild_id, filename,
|
||||
err ? "FAIL" : "Ok");
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -283,6 +307,7 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
bool force = false;
|
||||
char const *add_name_list_str = NULL,
|
||||
*remove_name_list_str = NULL,
|
||||
*purge_name_list_str = NULL,
|
||||
*missing_filename = NULL,
|
||||
*update_name_list_str = NULL,
|
||||
*kcore_filename = NULL;
|
||||
@ -300,6 +325,8 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
"file", "kcore file to add"),
|
||||
OPT_STRING('r', "remove", &remove_name_list_str, "file list",
|
||||
"file(s) to remove"),
|
||||
OPT_STRING('p', "purge", &purge_name_list_str, "path list",
|
||||
"path(s) to remove (remove old caches too)"),
|
||||
OPT_STRING('M', "missing", &missing_filename, "file",
|
||||
"to find missing build ids in the cache"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
@ -316,6 +343,11 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
argc = parse_options(argc, argv, buildid_cache_options,
|
||||
buildid_cache_usage, 0);
|
||||
|
||||
if (argc || (!add_name_list_str && !kcore_filename &&
|
||||
!remove_name_list_str && !purge_name_list_str &&
|
||||
!missing_filename && !update_name_list_str))
|
||||
usage_with_options(buildid_cache_usage, buildid_cache_options);
|
||||
|
||||
if (missing_filename) {
|
||||
file.path = missing_filename;
|
||||
file.force = force;
|
||||
@ -366,6 +398,24 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
}
|
||||
}
|
||||
|
||||
if (purge_name_list_str) {
|
||||
list = strlist__new(true, purge_name_list_str);
|
||||
if (list) {
|
||||
strlist__for_each(pos, list)
|
||||
if (build_id_cache__purge_path(pos->s)) {
|
||||
if (errno == ENOENT) {
|
||||
pr_debug("%s wasn't in the cache\n",
|
||||
pos->s);
|
||||
continue;
|
||||
}
|
||||
pr_warning("Couldn't remove %s: %s\n",
|
||||
pos->s, strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
strlist__delete(list);
|
||||
}
|
||||
}
|
||||
|
||||
if (missing_filename)
|
||||
ret = build_id_cache__fprintf_missing(session, stdout);
|
||||
|
||||
|
@ -86,7 +86,7 @@ static int cmd_data_convert(int argc, const char **argv,
|
||||
|
||||
static struct data_cmd data_cmds[] = {
|
||||
{ "convert", "converts data file between formats", cmd_data_convert },
|
||||
{ NULL },
|
||||
{ .name = NULL, },
|
||||
};
|
||||
|
||||
int cmd_data(int argc, const char **argv, const char *prefix)
|
||||
|
@ -36,41 +36,36 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
setup_pager();
|
||||
|
||||
if (raw_dump) {
|
||||
print_events(NULL, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!raw_dump)
|
||||
printf("\nList of pre-defined events (to be used in -e):\n\n");
|
||||
|
||||
if (argc == 0) {
|
||||
print_events(NULL, false);
|
||||
print_events(NULL, raw_dump);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < argc; ++i) {
|
||||
if (i)
|
||||
putchar('\n');
|
||||
if (strncmp(argv[i], "tracepoint", 10) == 0)
|
||||
print_tracepoint_events(NULL, NULL, false);
|
||||
if (strcmp(argv[i], "tracepoint") == 0)
|
||||
print_tracepoint_events(NULL, NULL, raw_dump);
|
||||
else if (strcmp(argv[i], "hw") == 0 ||
|
||||
strcmp(argv[i], "hardware") == 0)
|
||||
print_events_type(PERF_TYPE_HARDWARE);
|
||||
print_symbol_events(NULL, PERF_TYPE_HARDWARE,
|
||||
event_symbols_hw, PERF_COUNT_HW_MAX, raw_dump);
|
||||
else if (strcmp(argv[i], "sw") == 0 ||
|
||||
strcmp(argv[i], "software") == 0)
|
||||
print_events_type(PERF_TYPE_SOFTWARE);
|
||||
print_symbol_events(NULL, PERF_TYPE_SOFTWARE,
|
||||
event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump);
|
||||
else if (strcmp(argv[i], "cache") == 0 ||
|
||||
strcmp(argv[i], "hwcache") == 0)
|
||||
print_hwcache_events(NULL, false);
|
||||
print_hwcache_events(NULL, raw_dump);
|
||||
else if (strcmp(argv[i], "pmu") == 0)
|
||||
print_pmu_events(NULL, false);
|
||||
print_pmu_events(NULL, raw_dump);
|
||||
else {
|
||||
char *sep = strchr(argv[i], ':'), *s;
|
||||
int sep_idx;
|
||||
|
||||
if (sep == NULL) {
|
||||
print_events(argv[i], false);
|
||||
print_events(argv[i], raw_dump);
|
||||
continue;
|
||||
}
|
||||
sep_idx = sep - argv[i];
|
||||
@ -79,7 +74,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
return -1;
|
||||
|
||||
s[sep_idx] = '\0';
|
||||
print_tracepoint_events(s, s + sep_idx + 1, false);
|
||||
print_tracepoint_events(s, s + sep_idx + 1, raw_dump);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
|
@ -768,7 +768,7 @@ repeat:
|
||||
* 0/1 means the user chose a mode.
|
||||
*/
|
||||
if (((branch_mode == -1 && has_br_stack) || branch_mode == 1) &&
|
||||
branch_call_mode == -1) {
|
||||
!branch_call_mode) {
|
||||
sort__mode = SORT_MODE__BRANCH;
|
||||
symbol_conf.cumulate_callchain = false;
|
||||
}
|
||||
|
@ -52,7 +52,9 @@ struct tp_field {
|
||||
#define TP_UINT_FIELD(bits) \
|
||||
static u64 tp_field__u##bits(struct tp_field *field, struct perf_sample *sample) \
|
||||
{ \
|
||||
return *(u##bits *)(sample->raw_data + field->offset); \
|
||||
u##bits value; \
|
||||
memcpy(&value, sample->raw_data + field->offset, sizeof(value)); \
|
||||
return value; \
|
||||
}
|
||||
|
||||
TP_UINT_FIELD(8);
|
||||
@ -63,7 +65,8 @@ TP_UINT_FIELD(64);
|
||||
#define TP_UINT_FIELD__SWAPPED(bits) \
|
||||
static u64 tp_field__swapped_u##bits(struct tp_field *field, struct perf_sample *sample) \
|
||||
{ \
|
||||
u##bits value = *(u##bits *)(sample->raw_data + field->offset); \
|
||||
u##bits value; \
|
||||
memcpy(&value, sample->raw_data + field->offset, sizeof(value)); \
|
||||
return bswap_##bits(value);\
|
||||
}
|
||||
|
||||
@ -1517,11 +1520,22 @@ static int trace__read_syscall_info(struct trace *trace, int id)
|
||||
return syscall__set_arg_fmts(sc);
|
||||
}
|
||||
|
||||
/*
|
||||
* args is to be interpreted as a series of longs but we need to handle
|
||||
* 8-byte unaligned accesses. args points to raw_data within the event
|
||||
* and raw_data is guaranteed to be 8-byte unaligned because it is
|
||||
* preceded by raw_size which is a u32. So we need to copy args to a temp
|
||||
* variable to read it. Most notably this avoids extended load instructions
|
||||
* on unaligned addresses
|
||||
*/
|
||||
|
||||
static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
|
||||
unsigned long *args, struct trace *trace,
|
||||
unsigned char *args, struct trace *trace,
|
||||
struct thread *thread)
|
||||
{
|
||||
size_t printed = 0;
|
||||
unsigned char *p;
|
||||
unsigned long val;
|
||||
|
||||
if (sc->tp_format != NULL) {
|
||||
struct format_field *field;
|
||||
@ -1537,12 +1551,17 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
|
||||
field = field->next, ++arg.idx, bit <<= 1) {
|
||||
if (arg.mask & bit)
|
||||
continue;
|
||||
|
||||
/* special care for unaligned accesses */
|
||||
p = args + sizeof(unsigned long) * arg.idx;
|
||||
memcpy(&val, p, sizeof(val));
|
||||
|
||||
/*
|
||||
* Suppress this argument if its value is zero and
|
||||
* and we don't have a string associated in an
|
||||
* strarray for it.
|
||||
*/
|
||||
if (args[arg.idx] == 0 &&
|
||||
if (val == 0 &&
|
||||
!(sc->arg_scnprintf &&
|
||||
sc->arg_scnprintf[arg.idx] == SCA_STRARRAY &&
|
||||
sc->arg_parm[arg.idx]))
|
||||
@ -1551,23 +1570,26 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
"%s%s: ", printed ? ", " : "", field->name);
|
||||
if (sc->arg_scnprintf && sc->arg_scnprintf[arg.idx]) {
|
||||
arg.val = args[arg.idx];
|
||||
arg.val = val;
|
||||
if (sc->arg_parm)
|
||||
arg.parm = sc->arg_parm[arg.idx];
|
||||
printed += sc->arg_scnprintf[arg.idx](bf + printed,
|
||||
size - printed, &arg);
|
||||
} else {
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
"%ld", args[arg.idx]);
|
||||
"%ld", val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int i = 0;
|
||||
|
||||
while (i < 6) {
|
||||
/* special care for unaligned accesses */
|
||||
p = args + sizeof(unsigned long) * i;
|
||||
memcpy(&val, p, sizeof(val));
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
"%sarg%d: %ld",
|
||||
printed ? ", " : "", i, args[i]);
|
||||
printed ? ", " : "", i, val);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
@ -110,13 +110,11 @@ __perf_main ()
|
||||
# List perf subcommands or long options
|
||||
if [ $cword -eq 1 ]; then
|
||||
if [[ $cur == --* ]]; then
|
||||
__perfcomp '--help --version \
|
||||
--exec-path --html-path --paginate --no-pager \
|
||||
--perf-dir --work-tree --debugfs-dir' -- "$cur"
|
||||
cmds=$($cmd --list-opts)
|
||||
else
|
||||
cmds=$($cmd --list-cmds)
|
||||
__perfcomp "$cmds" "$cur"
|
||||
fi
|
||||
__perfcomp "$cmds" "$cur"
|
||||
# List possible events for -e option
|
||||
elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
|
||||
evts=$($cmd list --raw-dump)
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "util/quote.h"
|
||||
#include "util/run-command.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/debug.h"
|
||||
#include <api/fs/debugfs.h>
|
||||
#include <pthread.h>
|
||||
@ -125,6 +126,23 @@ static void commit_pager_choice(void)
|
||||
}
|
||||
}
|
||||
|
||||
struct option options[] = {
|
||||
OPT_ARGUMENT("help", "help"),
|
||||
OPT_ARGUMENT("version", "version"),
|
||||
OPT_ARGUMENT("exec-path", "exec-path"),
|
||||
OPT_ARGUMENT("html-path", "html-path"),
|
||||
OPT_ARGUMENT("paginate", "paginate"),
|
||||
OPT_ARGUMENT("no-pager", "no-pager"),
|
||||
OPT_ARGUMENT("perf-dir", "perf-dir"),
|
||||
OPT_ARGUMENT("work-tree", "work-tree"),
|
||||
OPT_ARGUMENT("debugfs-dir", "debugfs-dir"),
|
||||
OPT_ARGUMENT("buildid-dir", "buildid-dir"),
|
||||
OPT_ARGUMENT("list-cmds", "list-cmds"),
|
||||
OPT_ARGUMENT("list-opts", "list-opts"),
|
||||
OPT_ARGUMENT("debug", "debug"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static int handle_options(const char ***argv, int *argc, int *envchanged)
|
||||
{
|
||||
int handled = 0;
|
||||
@ -223,6 +241,16 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
||||
struct cmd_struct *p = commands+i;
|
||||
printf("%s ", p->cmd);
|
||||
}
|
||||
putchar('\n');
|
||||
exit(0);
|
||||
} else if (!strcmp(cmd, "--list-opts")) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(options)-1; i++) {
|
||||
struct option *p = options+i;
|
||||
printf("--%s ", p->long_name);
|
||||
}
|
||||
putchar('\n');
|
||||
exit(0);
|
||||
} else if (!strcmp(cmd, "--debug")) {
|
||||
if (*argc < 2) {
|
||||
|
@ -281,35 +281,93 @@ void disable_buildid_cache(void)
|
||||
no_buildid_cache = true;
|
||||
}
|
||||
|
||||
static char *build_id_cache__dirname_from_path(const char *name,
|
||||
bool is_kallsyms, bool is_vdso)
|
||||
{
|
||||
char *realname = (char *)name, *filename;
|
||||
bool slash = is_kallsyms || is_vdso;
|
||||
|
||||
if (!slash) {
|
||||
realname = realpath(name, NULL);
|
||||
if (!realname)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (asprintf(&filename, "%s%s%s", buildid_dir, slash ? "/" : "",
|
||||
is_vdso ? DSO__NAME_VDSO : realname) < 0)
|
||||
filename = NULL;
|
||||
|
||||
if (!slash)
|
||||
free(realname);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
int build_id_cache__list_build_ids(const char *pathname,
|
||||
struct strlist **result)
|
||||
{
|
||||
struct strlist *list;
|
||||
char *dir_name;
|
||||
DIR *dir;
|
||||
struct dirent *d;
|
||||
int ret = 0;
|
||||
|
||||
list = strlist__new(true, NULL);
|
||||
dir_name = build_id_cache__dirname_from_path(pathname, false, false);
|
||||
if (!list || !dir_name) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* List up all dirents */
|
||||
dir = opendir(dir_name);
|
||||
if (!dir) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((d = readdir(dir)) != NULL) {
|
||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||
continue;
|
||||
strlist__add(list, d->d_name);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
out:
|
||||
free(dir_name);
|
||||
if (ret)
|
||||
strlist__delete(list);
|
||||
else
|
||||
*result = list;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int build_id_cache__add_s(const char *sbuild_id, const char *name,
|
||||
bool is_kallsyms, bool is_vdso)
|
||||
{
|
||||
const size_t size = PATH_MAX;
|
||||
char *realname, *filename = zalloc(size),
|
||||
char *realname = NULL, *filename = NULL, *dir_name = NULL,
|
||||
*linkname = zalloc(size), *targetname, *tmp;
|
||||
int len, err = -1;
|
||||
bool slash = is_kallsyms || is_vdso;
|
||||
int err = -1;
|
||||
|
||||
if (is_kallsyms) {
|
||||
if (symbol_conf.kptr_restrict) {
|
||||
pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
|
||||
err = 0;
|
||||
goto out_free;
|
||||
}
|
||||
realname = (char *) name;
|
||||
} else
|
||||
if (!is_kallsyms) {
|
||||
realname = realpath(name, NULL);
|
||||
if (!realname)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (realname == NULL || filename == NULL || linkname == NULL)
|
||||
dir_name = build_id_cache__dirname_from_path(name, is_kallsyms, is_vdso);
|
||||
if (!dir_name)
|
||||
goto out_free;
|
||||
|
||||
len = scnprintf(filename, size, "%s%s%s",
|
||||
buildid_dir, slash ? "/" : "",
|
||||
is_vdso ? DSO__NAME_VDSO : realname);
|
||||
if (mkdir_p(filename, 0755))
|
||||
if (mkdir_p(dir_name, 0755))
|
||||
goto out_free;
|
||||
|
||||
snprintf(filename + len, size - len, "/%s", sbuild_id);
|
||||
if (asprintf(&filename, "%s/%s", dir_name, sbuild_id) < 0) {
|
||||
filename = NULL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (access(filename, F_OK)) {
|
||||
if (is_kallsyms) {
|
||||
@ -337,6 +395,7 @@ out_free:
|
||||
if (!is_kallsyms)
|
||||
free(realname);
|
||||
free(filename);
|
||||
free(dir_name);
|
||||
free(linkname);
|
||||
return err;
|
||||
}
|
||||
@ -352,6 +411,18 @@ static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
|
||||
return build_id_cache__add_s(sbuild_id, name, is_kallsyms, is_vdso);
|
||||
}
|
||||
|
||||
bool build_id_cache__cached(const char *sbuild_id)
|
||||
{
|
||||
bool ret = false;
|
||||
char *filename = build_id__filename(sbuild_id, NULL, 0);
|
||||
|
||||
if (filename && !access(filename, F_OK))
|
||||
ret = true;
|
||||
free(filename);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int build_id_cache__remove_s(const char *sbuild_id)
|
||||
{
|
||||
const size_t size = PATH_MAX;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#define BUILD_ID_SIZE 20
|
||||
|
||||
#include "tool.h"
|
||||
#include "strlist.h"
|
||||
#include <linux/types.h>
|
||||
|
||||
extern struct perf_tool build_id__mark_dso_hit_ops;
|
||||
@ -22,6 +23,9 @@ bool perf_session__read_build_ids(struct perf_session *session, bool with_hits);
|
||||
int perf_session__write_buildid_table(struct perf_session *session, int fd);
|
||||
int perf_session__cache_build_ids(struct perf_session *session);
|
||||
|
||||
int build_id_cache__list_build_ids(const char *pathname,
|
||||
struct strlist **result);
|
||||
bool build_id_cache__cached(const char *sbuild_id);
|
||||
int build_id_cache__add_s(const char *sbuild_id,
|
||||
const char *name, bool is_kallsyms, bool is_vdso);
|
||||
int build_id_cache__remove_s(const char *sbuild_id);
|
||||
|
@ -20,11 +20,6 @@
|
||||
|
||||
#define MAX_NAME_LEN 100
|
||||
|
||||
struct event_symbol {
|
||||
const char *symbol;
|
||||
const char *alias;
|
||||
};
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
extern int parse_events_debug;
|
||||
#endif
|
||||
@ -39,7 +34,7 @@ static struct perf_pmu_event_symbol *perf_pmu_events_list;
|
||||
*/
|
||||
static int perf_pmu_events_list_num;
|
||||
|
||||
static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
|
||||
struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
|
||||
[PERF_COUNT_HW_CPU_CYCLES] = {
|
||||
.symbol = "cpu-cycles",
|
||||
.alias = "cycles",
|
||||
@ -82,7 +77,7 @@ static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
|
||||
struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
|
||||
[PERF_COUNT_SW_CPU_CLOCK] = {
|
||||
.symbol = "cpu-clock",
|
||||
.alias = "",
|
||||
@ -1089,6 +1084,14 @@ static const char * const event_type_descriptors[] = {
|
||||
"Hardware breakpoint",
|
||||
};
|
||||
|
||||
static int cmp_string(const void *a, const void *b)
|
||||
{
|
||||
const char * const *as = a;
|
||||
const char * const *bs = b;
|
||||
|
||||
return strcmp(*as, *bs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the events from <debugfs_mount_point>/tracing/events
|
||||
*/
|
||||
@ -1100,11 +1103,21 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
|
||||
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
|
||||
char evt_path[MAXPATHLEN];
|
||||
char dir_path[MAXPATHLEN];
|
||||
char **evt_list = NULL;
|
||||
unsigned int evt_i = 0, evt_num = 0;
|
||||
bool evt_num_known = false;
|
||||
|
||||
restart:
|
||||
sys_dir = opendir(tracing_events_path);
|
||||
if (!sys_dir)
|
||||
return;
|
||||
|
||||
if (evt_num_known) {
|
||||
evt_list = zalloc(sizeof(char *) * evt_num);
|
||||
if (!evt_list)
|
||||
goto out_close_sys_dir;
|
||||
}
|
||||
|
||||
for_each_subsystem(sys_dir, sys_dirent, sys_next) {
|
||||
if (subsys_glob != NULL &&
|
||||
!strglobmatch(sys_dirent.d_name, subsys_glob))
|
||||
@ -1121,19 +1134,56 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
|
||||
!strglobmatch(evt_dirent.d_name, event_glob))
|
||||
continue;
|
||||
|
||||
if (name_only) {
|
||||
printf("%s:%s ", sys_dirent.d_name, evt_dirent.d_name);
|
||||
if (!evt_num_known) {
|
||||
evt_num++;
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(evt_path, MAXPATHLEN, "%s:%s",
|
||||
sys_dirent.d_name, evt_dirent.d_name);
|
||||
printf(" %-50s [%s]\n", evt_path,
|
||||
event_type_descriptors[PERF_TYPE_TRACEPOINT]);
|
||||
|
||||
evt_list[evt_i] = strdup(evt_path);
|
||||
if (evt_list[evt_i] == NULL)
|
||||
goto out_close_evt_dir;
|
||||
evt_i++;
|
||||
}
|
||||
closedir(evt_dir);
|
||||
}
|
||||
closedir(sys_dir);
|
||||
|
||||
if (!evt_num_known) {
|
||||
evt_num_known = true;
|
||||
goto restart;
|
||||
}
|
||||
qsort(evt_list, evt_num, sizeof(char *), cmp_string);
|
||||
evt_i = 0;
|
||||
while (evt_i < evt_num) {
|
||||
if (name_only) {
|
||||
printf("%s ", evt_list[evt_i++]);
|
||||
continue;
|
||||
}
|
||||
printf(" %-50s [%s]\n", evt_list[evt_i++],
|
||||
event_type_descriptors[PERF_TYPE_TRACEPOINT]);
|
||||
}
|
||||
if (evt_num)
|
||||
printf("\n");
|
||||
|
||||
out_free:
|
||||
evt_num = evt_i;
|
||||
for (evt_i = 0; evt_i < evt_num; evt_i++)
|
||||
zfree(&evt_list[evt_i]);
|
||||
zfree(&evt_list);
|
||||
return;
|
||||
|
||||
out_close_evt_dir:
|
||||
closedir(evt_dir);
|
||||
out_close_sys_dir:
|
||||
closedir(sys_dir);
|
||||
|
||||
printf("FATAL: not enough memory to print %s\n",
|
||||
event_type_descriptors[PERF_TYPE_TRACEPOINT]);
|
||||
if (evt_list)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1214,38 +1264,19 @@ static bool is_event_supported(u8 type, unsigned config)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __print_events_type(u8 type, struct event_symbol *syms,
|
||||
unsigned max)
|
||||
{
|
||||
char name[64];
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < max ; i++, syms++) {
|
||||
if (!is_event_supported(type, i))
|
||||
continue;
|
||||
|
||||
if (strlen(syms->alias))
|
||||
snprintf(name, sizeof(name), "%s OR %s",
|
||||
syms->symbol, syms->alias);
|
||||
else
|
||||
snprintf(name, sizeof(name), "%s", syms->symbol);
|
||||
|
||||
printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
|
||||
}
|
||||
}
|
||||
|
||||
void print_events_type(u8 type)
|
||||
{
|
||||
if (type == PERF_TYPE_SOFTWARE)
|
||||
__print_events_type(type, event_symbols_sw, PERF_COUNT_SW_MAX);
|
||||
else
|
||||
__print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX);
|
||||
}
|
||||
|
||||
int print_hwcache_events(const char *event_glob, bool name_only)
|
||||
{
|
||||
unsigned int type, op, i, printed = 0;
|
||||
unsigned int type, op, i, evt_i = 0, evt_num = 0;
|
||||
char name[64];
|
||||
char **evt_list = NULL;
|
||||
bool evt_num_known = false;
|
||||
|
||||
restart:
|
||||
if (evt_num_known) {
|
||||
evt_list = zalloc(sizeof(char *) * evt_num);
|
||||
if (!evt_list)
|
||||
goto out_enomem;
|
||||
}
|
||||
|
||||
for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
|
||||
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
|
||||
@ -1263,27 +1294,66 @@ int print_hwcache_events(const char *event_glob, bool name_only)
|
||||
type | (op << 8) | (i << 16)))
|
||||
continue;
|
||||
|
||||
if (name_only)
|
||||
printf("%s ", name);
|
||||
else
|
||||
printf(" %-50s [%s]\n", name,
|
||||
event_type_descriptors[PERF_TYPE_HW_CACHE]);
|
||||
++printed;
|
||||
if (!evt_num_known) {
|
||||
evt_num++;
|
||||
continue;
|
||||
}
|
||||
|
||||
evt_list[evt_i] = strdup(name);
|
||||
if (evt_list[evt_i] == NULL)
|
||||
goto out_enomem;
|
||||
evt_i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (printed)
|
||||
if (!evt_num_known) {
|
||||
evt_num_known = true;
|
||||
goto restart;
|
||||
}
|
||||
qsort(evt_list, evt_num, sizeof(char *), cmp_string);
|
||||
evt_i = 0;
|
||||
while (evt_i < evt_num) {
|
||||
if (name_only) {
|
||||
printf("%s ", evt_list[evt_i++]);
|
||||
continue;
|
||||
}
|
||||
printf(" %-50s [%s]\n", evt_list[evt_i++],
|
||||
event_type_descriptors[PERF_TYPE_HW_CACHE]);
|
||||
}
|
||||
if (evt_num)
|
||||
printf("\n");
|
||||
return printed;
|
||||
|
||||
out_free:
|
||||
evt_num = evt_i;
|
||||
for (evt_i = 0; evt_i < evt_num; evt_i++)
|
||||
zfree(&evt_list[evt_i]);
|
||||
zfree(&evt_list);
|
||||
return evt_num;
|
||||
|
||||
out_enomem:
|
||||
printf("FATAL: not enough memory to print %s\n", event_type_descriptors[PERF_TYPE_HW_CACHE]);
|
||||
if (evt_list)
|
||||
goto out_free;
|
||||
return evt_num;
|
||||
}
|
||||
|
||||
static void print_symbol_events(const char *event_glob, unsigned type,
|
||||
void print_symbol_events(const char *event_glob, unsigned type,
|
||||
struct event_symbol *syms, unsigned max,
|
||||
bool name_only)
|
||||
{
|
||||
unsigned i, printed = 0;
|
||||
unsigned int i, evt_i = 0, evt_num = 0;
|
||||
char name[MAX_NAME_LEN];
|
||||
char **evt_list = NULL;
|
||||
bool evt_num_known = false;
|
||||
|
||||
restart:
|
||||
if (evt_num_known) {
|
||||
evt_list = zalloc(sizeof(char *) * evt_num);
|
||||
if (!evt_list)
|
||||
goto out_enomem;
|
||||
syms -= max;
|
||||
}
|
||||
|
||||
for (i = 0; i < max; i++, syms++) {
|
||||
|
||||
@ -1295,23 +1365,49 @@ static void print_symbol_events(const char *event_glob, unsigned type,
|
||||
if (!is_event_supported(type, i))
|
||||
continue;
|
||||
|
||||
if (name_only) {
|
||||
printf("%s ", syms->symbol);
|
||||
if (!evt_num_known) {
|
||||
evt_num++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strlen(syms->alias))
|
||||
if (!name_only && strlen(syms->alias))
|
||||
snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);
|
||||
else
|
||||
strncpy(name, syms->symbol, MAX_NAME_LEN);
|
||||
|
||||
printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
|
||||
|
||||
printed++;
|
||||
evt_list[evt_i] = strdup(name);
|
||||
if (evt_list[evt_i] == NULL)
|
||||
goto out_enomem;
|
||||
evt_i++;
|
||||
}
|
||||
|
||||
if (printed)
|
||||
if (!evt_num_known) {
|
||||
evt_num_known = true;
|
||||
goto restart;
|
||||
}
|
||||
qsort(evt_list, evt_num, sizeof(char *), cmp_string);
|
||||
evt_i = 0;
|
||||
while (evt_i < evt_num) {
|
||||
if (name_only) {
|
||||
printf("%s ", evt_list[evt_i++]);
|
||||
continue;
|
||||
}
|
||||
printf(" %-50s [%s]\n", evt_list[evt_i++], event_type_descriptors[type]);
|
||||
}
|
||||
if (evt_num)
|
||||
printf("\n");
|
||||
|
||||
out_free:
|
||||
evt_num = evt_i;
|
||||
for (evt_i = 0; evt_i < evt_num; evt_i++)
|
||||
zfree(&evt_list[evt_i]);
|
||||
zfree(&evt_list);
|
||||
return;
|
||||
|
||||
out_enomem:
|
||||
printf("FATAL: not enough memory to print %s\n", event_type_descriptors[type]);
|
||||
if (evt_list)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -116,7 +116,16 @@ void parse_events_update_lists(struct list_head *list_event,
|
||||
void parse_events_error(void *data, void *scanner, char const *msg);
|
||||
|
||||
void print_events(const char *event_glob, bool name_only);
|
||||
void print_events_type(u8 type);
|
||||
|
||||
struct event_symbol {
|
||||
const char *symbol;
|
||||
const char *alias;
|
||||
};
|
||||
extern struct event_symbol event_symbols_hw[];
|
||||
extern struct event_symbol event_symbols_sw[];
|
||||
void print_symbol_events(const char *event_glob, unsigned type,
|
||||
struct event_symbol *syms, unsigned max,
|
||||
bool name_only);
|
||||
void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
|
||||
bool name_only);
|
||||
int print_hwcache_events(const char *event_glob, bool name_only);
|
||||
|
@ -505,15 +505,18 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
|
||||
break;
|
||||
case PARSE_OPT_LIST_OPTS:
|
||||
while (options->type != OPTION_END) {
|
||||
printf("--%s ", options->long_name);
|
||||
if (options->long_name)
|
||||
printf("--%s ", options->long_name);
|
||||
options++;
|
||||
}
|
||||
putchar('\n');
|
||||
exit(130);
|
||||
case PARSE_OPT_LIST_SUBCMDS:
|
||||
if (subcommands) {
|
||||
for (int i = 0; subcommands[i]; i++)
|
||||
printf("%s ", subcommands[i]);
|
||||
}
|
||||
putchar('\n');
|
||||
exit(130);
|
||||
default: /* PARSE_OPT_UNKNOWN */
|
||||
if (ctx.argv[0][1] == '-') {
|
||||
|
@ -151,7 +151,7 @@ static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc)
|
||||
sym = __find_kernel_function_by_name(name, &map);
|
||||
if (sym)
|
||||
return map->unmap_ip(map, sym->start) -
|
||||
(reloc) ? 0 : map->reloc;
|
||||
((reloc) ? 0 : map->reloc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -533,7 +533,7 @@ static int get_real_path(const char *raw_path, const char *comp_dir,
|
||||
else {
|
||||
if (access(raw_path, R_OK) == 0) {
|
||||
*new_path = strdup(raw_path);
|
||||
return 0;
|
||||
return *new_path ? 0 : -ENOMEM;
|
||||
} else
|
||||
return -errno;
|
||||
}
|
||||
@ -549,9 +549,11 @@ static int get_real_path(const char *raw_path, const char *comp_dir,
|
||||
if (access(*new_path, R_OK) == 0)
|
||||
return 0;
|
||||
|
||||
if (!symbol_conf.source_prefix)
|
||||
if (!symbol_conf.source_prefix) {
|
||||
/* In case of searching comp_dir, don't retry */
|
||||
zfree(new_path);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
switch (errno) {
|
||||
case ENAMETOOLONG:
|
||||
@ -1903,6 +1905,95 @@ static struct strlist *get_probe_trace_command_rawlist(int fd)
|
||||
return sl;
|
||||
}
|
||||
|
||||
struct kprobe_blacklist_node {
|
||||
struct list_head list;
|
||||
unsigned long start;
|
||||
unsigned long end;
|
||||
char *symbol;
|
||||
};
|
||||
|
||||
static void kprobe_blacklist__delete(struct list_head *blacklist)
|
||||
{
|
||||
struct kprobe_blacklist_node *node;
|
||||
|
||||
while (!list_empty(blacklist)) {
|
||||
node = list_first_entry(blacklist,
|
||||
struct kprobe_blacklist_node, list);
|
||||
list_del(&node->list);
|
||||
free(node->symbol);
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
static int kprobe_blacklist__load(struct list_head *blacklist)
|
||||
{
|
||||
struct kprobe_blacklist_node *node;
|
||||
const char *__debugfs = debugfs_find_mountpoint();
|
||||
char buf[PATH_MAX], *p;
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
if (__debugfs == NULL)
|
||||
return -ENOTSUP;
|
||||
|
||||
ret = e_snprintf(buf, PATH_MAX, "%s/kprobes/blacklist", __debugfs);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fp = fopen(buf, "r");
|
||||
if (!fp)
|
||||
return -errno;
|
||||
|
||||
ret = 0;
|
||||
while (fgets(buf, PATH_MAX, fp)) {
|
||||
node = zalloc(sizeof(*node));
|
||||
if (!node) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
INIT_LIST_HEAD(&node->list);
|
||||
list_add_tail(&node->list, blacklist);
|
||||
if (sscanf(buf, "0x%lx-0x%lx", &node->start, &node->end) != 2) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
p = strchr(buf, '\t');
|
||||
if (p) {
|
||||
p++;
|
||||
if (p[strlen(p) - 1] == '\n')
|
||||
p[strlen(p) - 1] = '\0';
|
||||
} else
|
||||
p = (char *)"unknown";
|
||||
node->symbol = strdup(p);
|
||||
if (!node->symbol) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
pr_debug2("Blacklist: 0x%lx-0x%lx, %s\n",
|
||||
node->start, node->end, node->symbol);
|
||||
ret++;
|
||||
}
|
||||
if (ret < 0)
|
||||
kprobe_blacklist__delete(blacklist);
|
||||
fclose(fp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct kprobe_blacklist_node *
|
||||
kprobe_blacklist__find_by_address(struct list_head *blacklist,
|
||||
unsigned long address)
|
||||
{
|
||||
struct kprobe_blacklist_node *node;
|
||||
|
||||
list_for_each_entry(node, blacklist, list) {
|
||||
if (node->start <= address && address <= node->end)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Show an event */
|
||||
static int show_perf_probe_event(struct perf_probe_event *pev,
|
||||
const char *module)
|
||||
@ -2117,6 +2208,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
||||
char buf[64];
|
||||
const char *event, *group;
|
||||
struct strlist *namelist;
|
||||
LIST_HEAD(blacklist);
|
||||
struct kprobe_blacklist_node *node;
|
||||
|
||||
if (pev->uprobes)
|
||||
fd = open_uprobe_events(true);
|
||||
@ -2134,11 +2227,25 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
||||
pr_debug("Failed to get current event list.\n");
|
||||
return -EIO;
|
||||
}
|
||||
/* Get kprobe blacklist if exists */
|
||||
if (!pev->uprobes) {
|
||||
ret = kprobe_blacklist__load(&blacklist);
|
||||
if (ret < 0)
|
||||
pr_debug("No kprobe blacklist support, ignored\n");
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
|
||||
for (i = 0; i < ntevs; i++) {
|
||||
tev = &tevs[i];
|
||||
/* Ensure that the address is NOT blacklisted */
|
||||
node = kprobe_blacklist__find_by_address(&blacklist,
|
||||
tev->point.address);
|
||||
if (node) {
|
||||
pr_warning("Warning: Skipped probing on blacklisted function: %s\n", node->symbol);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pev->event)
|
||||
event = pev->event;
|
||||
else
|
||||
@ -2189,13 +2296,15 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
||||
allow_suffix = true;
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
/* Note that it is possible to skip all events because of blacklist */
|
||||
if (ret >= 0 && tev->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", tev->group,
|
||||
tev->event);
|
||||
}
|
||||
|
||||
kprobe_blacklist__delete(&blacklist);
|
||||
strlist__delete(namelist);
|
||||
close(fd);
|
||||
return ret;
|
||||
|
@ -1463,6 +1463,15 @@ int sort_dimension__add(const char *tok)
|
||||
sort__has_parent = 1;
|
||||
} else if (sd->entry == &sort_sym) {
|
||||
sort__has_sym = 1;
|
||||
/*
|
||||
* perf diff displays the performance difference amongst
|
||||
* two or more perf.data files. Those files could come
|
||||
* from different binaries. So we should not compare
|
||||
* their ips, but the name of symbol.
|
||||
*/
|
||||
if (sort__mode == SORT_MODE__DIFF)
|
||||
sd->entry->se_collapse = sort__sym_sort;
|
||||
|
||||
} else if (sd->entry == &sort_dso) {
|
||||
sort__has_dso = 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user