Commit 788b94ba authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 0afb1704 fefd2d96
...@@ -12,9 +12,9 @@ SYNOPSIS ...@@ -12,9 +12,9 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
This command manages the build-id cache. It can add and remove files to/from This command manages the build-id cache. It can add, remove, update and purge
the cache. In the future it should as well purge older entries, set upper files to/from the cache. In the future it should as well set upper limits for
limits for the space used by the cache, etc. the space used by the cache, etc.
OPTIONS OPTIONS
------- -------
...@@ -36,14 +36,24 @@ OPTIONS ...@@ -36,14 +36,24 @@ OPTIONS
actually made. actually made.
-r:: -r::
--remove=:: --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:: -M::
--missing=:: --missing=::
List missing build ids in the cache for the specified file. List missing build ids in the cache for the specified file.
-u:: -u::
--update:: --update=::
Update specified file of the cache. It can be used to update kallsyms Update specified file of the cache. Note that this doesn't remove
kernel dso to vmlinux in order to support annotation. 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:: -v::
--verbose:: --verbose::
Be more verbose. Be more verbose.
......
...@@ -20,6 +20,11 @@ If no parameters are passed it will assume perf.data.old and perf.data. ...@@ -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 The differential profile is displayed only for events matching both
specified perf.data files. 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 OPTIONS
------- -------
-D:: -D::
......
...@@ -127,6 +127,12 @@ To limit the list use: ...@@ -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 One or more types can be used at the same time, listing the events for the
types specified. 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 SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-top[1], linkperf:perf-stat[1], linkperf:perf-top[1],
......
...@@ -196,9 +196,8 @@ static int build_id_cache__add_file(const char *filename) ...@@ -196,9 +196,8 @@ static int build_id_cache__add_file(const char *filename)
build_id__sprintf(build_id, sizeof(build_id), sbuild_id); build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
err = build_id_cache__add_s(sbuild_id, filename, err = build_id_cache__add_s(sbuild_id, filename,
false, false); false, false);
if (verbose) pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
pr_info("Adding %s %s: %s\n", sbuild_id, filename, err ? "FAIL" : "Ok");
err ? "FAIL" : "Ok");
return err; return err;
} }
...@@ -216,9 +215,33 @@ static int build_id_cache__remove_file(const char *filename) ...@@ -216,9 +215,33 @@ static int build_id_cache__remove_file(const char *filename)
build_id__sprintf(build_id, sizeof(build_id), sbuild_id); build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
err = build_id_cache__remove_s(sbuild_id); err = build_id_cache__remove_s(sbuild_id);
if (verbose) pr_debug("Removing %s %s: %s\n", sbuild_id, filename,
pr_info("Removing %s %s: %s\n", sbuild_id, filename, err ? "FAIL" : "Ok");
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; return err;
} }
...@@ -255,7 +278,7 @@ static int build_id_cache__update_file(const char *filename) ...@@ -255,7 +278,7 @@ static int build_id_cache__update_file(const char *filename)
u8 build_id[BUILD_ID_SIZE]; u8 build_id[BUILD_ID_SIZE];
char sbuild_id[BUILD_ID_SIZE * 2 + 1]; 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) { if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
pr_debug("Couldn't read a build-id in %s\n", filename); 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) ...@@ -263,13 +286,14 @@ static int build_id_cache__update_file(const char *filename)
} }
build_id__sprintf(build_id, sizeof(build_id), sbuild_id); 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) if (!err)
err = build_id_cache__add_s(sbuild_id, filename, false, false); err = build_id_cache__add_s(sbuild_id, filename, false, false);
if (verbose) pr_debug("Updating %s %s: %s\n", sbuild_id, filename,
pr_info("Updating %s %s: %s\n", sbuild_id, filename, err ? "FAIL" : "Ok");
err ? "FAIL" : "Ok");
return err; return err;
} }
...@@ -283,6 +307,7 @@ int cmd_buildid_cache(int argc, const char **argv, ...@@ -283,6 +307,7 @@ int cmd_buildid_cache(int argc, const char **argv,
bool force = false; bool force = false;
char const *add_name_list_str = NULL, char const *add_name_list_str = NULL,
*remove_name_list_str = NULL, *remove_name_list_str = NULL,
*purge_name_list_str = NULL,
*missing_filename = NULL, *missing_filename = NULL,
*update_name_list_str = NULL, *update_name_list_str = NULL,
*kcore_filename = NULL; *kcore_filename = NULL;
...@@ -300,6 +325,8 @@ int cmd_buildid_cache(int argc, const char **argv, ...@@ -300,6 +325,8 @@ int cmd_buildid_cache(int argc, const char **argv,
"file", "kcore file to add"), "file", "kcore file to add"),
OPT_STRING('r', "remove", &remove_name_list_str, "file list", OPT_STRING('r', "remove", &remove_name_list_str, "file list",
"file(s) to remove"), "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", OPT_STRING('M', "missing", &missing_filename, "file",
"to find missing build ids in the cache"), "to find missing build ids in the cache"),
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
...@@ -316,6 +343,11 @@ int cmd_buildid_cache(int argc, const char **argv, ...@@ -316,6 +343,11 @@ int cmd_buildid_cache(int argc, const char **argv,
argc = parse_options(argc, argv, buildid_cache_options, argc = parse_options(argc, argv, buildid_cache_options,
buildid_cache_usage, 0); 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) { if (missing_filename) {
file.path = missing_filename; file.path = missing_filename;
file.force = force; file.force = force;
...@@ -366,6 +398,24 @@ int cmd_buildid_cache(int argc, const char **argv, ...@@ -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) if (missing_filename)
ret = build_id_cache__fprintf_missing(session, stdout); ret = build_id_cache__fprintf_missing(session, stdout);
......
...@@ -86,7 +86,7 @@ static int cmd_data_convert(int argc, const char **argv, ...@@ -86,7 +86,7 @@ static int cmd_data_convert(int argc, const char **argv,
static struct data_cmd data_cmds[] = { static struct data_cmd data_cmds[] = {
{ "convert", "converts data file between formats", cmd_data_convert }, { "convert", "converts data file between formats", cmd_data_convert },
{ NULL }, { .name = NULL, },
}; };
int cmd_data(int argc, const char **argv, const char *prefix) 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) ...@@ -36,41 +36,36 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
setup_pager(); setup_pager();
if (raw_dump) {
print_events(NULL, true);
return 0;
}
if (!raw_dump) if (!raw_dump)
printf("\nList of pre-defined events (to be used in -e):\n\n"); printf("\nList of pre-defined events (to be used in -e):\n\n");
if (argc == 0) { if (argc == 0) {
print_events(NULL, false); print_events(NULL, raw_dump);
return 0; return 0;
} }
for (i = 0; i < argc; ++i) { for (i = 0; i < argc; ++i) {
if (i) if (strcmp(argv[i], "tracepoint") == 0)
putchar('\n'); print_tracepoint_events(NULL, NULL, raw_dump);
if (strncmp(argv[i], "tracepoint", 10) == 0)
print_tracepoint_events(NULL, NULL, false);
else if (strcmp(argv[i], "hw") == 0 || else if (strcmp(argv[i], "hw") == 0 ||
strcmp(argv[i], "hardware") == 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 || else if (strcmp(argv[i], "sw") == 0 ||
strcmp(argv[i], "software") == 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 || else if (strcmp(argv[i], "cache") == 0 ||
strcmp(argv[i], "hwcache") == 0) strcmp(argv[i], "hwcache") == 0)
print_hwcache_events(NULL, false); print_hwcache_events(NULL, raw_dump);
else if (strcmp(argv[i], "pmu") == 0) else if (strcmp(argv[i], "pmu") == 0)
print_pmu_events(NULL, false); print_pmu_events(NULL, raw_dump);
else { else {
char *sep = strchr(argv[i], ':'), *s; char *sep = strchr(argv[i], ':'), *s;
int sep_idx; int sep_idx;
if (sep == NULL) { if (sep == NULL) {
print_events(argv[i], false); print_events(argv[i], raw_dump);
continue; continue;
} }
sep_idx = sep - argv[i]; sep_idx = sep - argv[i];
...@@ -79,7 +74,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -79,7 +74,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
return -1; return -1;
s[sep_idx] = '\0'; 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); free(s);
} }
} }
......
...@@ -768,7 +768,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -768,7 +768,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
* 0/1 means the user chose a mode. * 0/1 means the user chose a mode.
*/ */
if (((branch_mode == -1 && has_br_stack) || branch_mode == 1) && if (((branch_mode == -1 && has_br_stack) || branch_mode == 1) &&
branch_call_mode == -1) { !branch_call_mode) {
sort__mode = SORT_MODE__BRANCH; sort__mode = SORT_MODE__BRANCH;
symbol_conf.cumulate_callchain = false; symbol_conf.cumulate_callchain = false;
} }
......
...@@ -52,7 +52,9 @@ struct tp_field { ...@@ -52,7 +52,9 @@ struct tp_field {
#define TP_UINT_FIELD(bits) \ #define TP_UINT_FIELD(bits) \
static u64 tp_field__u##bits(struct tp_field *field, struct perf_sample *sample) \ 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); TP_UINT_FIELD(8);
...@@ -63,7 +65,8 @@ TP_UINT_FIELD(64); ...@@ -63,7 +65,8 @@ TP_UINT_FIELD(64);
#define TP_UINT_FIELD__SWAPPED(bits) \ #define TP_UINT_FIELD__SWAPPED(bits) \
static u64 tp_field__swapped_u##bits(struct tp_field *field, struct perf_sample *sample) \ 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);\ return bswap_##bits(value);\
} }
...@@ -1517,11 +1520,22 @@ static int trace__read_syscall_info(struct trace *trace, int id) ...@@ -1517,11 +1520,22 @@ static int trace__read_syscall_info(struct trace *trace, int id)
return syscall__set_arg_fmts(sc); 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, 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) struct thread *thread)
{ {
size_t printed = 0; size_t printed = 0;
unsigned char *p;
unsigned long val;
if (sc->tp_format != NULL) { if (sc->tp_format != NULL) {
struct format_field *field; struct format_field *field;
...@@ -1537,12 +1551,17 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, ...@@ -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) { field = field->next, ++arg.idx, bit <<= 1) {
if (arg.mask & bit) if (arg.mask & bit)
continue; 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 * Suppress this argument if its value is zero and
* and we don't have a string associated in an * and we don't have a string associated in an
* strarray for it. * strarray for it.
*/ */
if (args[arg.idx] == 0 && if (val == 0 &&
!(sc->arg_scnprintf && !(sc->arg_scnprintf &&
sc->arg_scnprintf[arg.idx] == SCA_STRARRAY && sc->arg_scnprintf[arg.idx] == SCA_STRARRAY &&
sc->arg_parm[arg.idx])) sc->arg_parm[arg.idx]))
...@@ -1551,23 +1570,26 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, ...@@ -1551,23 +1570,26 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
printed += scnprintf(bf + printed, size - printed, printed += scnprintf(bf + printed, size - printed,
"%s%s: ", printed ? ", " : "", field->name); "%s%s: ", printed ? ", " : "", field->name);
if (sc->arg_scnprintf && sc->arg_scnprintf[arg.idx]) { if (sc->arg_scnprintf && sc->arg_scnprintf[arg.idx]) {
arg.val = args[arg.idx]; arg.val = val;
if (sc->arg_parm) if (sc->arg_parm)
arg.parm = sc->arg_parm[arg.idx]; arg.parm = sc->arg_parm[arg.idx];
printed += sc->arg_scnprintf[arg.idx](bf + printed, printed += sc->arg_scnprintf[arg.idx](bf + printed,
size - printed, &arg); size - printed, &arg);
} else { } else {
printed += scnprintf(bf + printed, size - printed, printed += scnprintf(bf + printed, size - printed,
"%ld", args[arg.idx]); "%ld", val);
} }
} }
} else { } else {
int i = 0; int i = 0;
while (i < 6) { 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, printed += scnprintf(bf + printed, size - printed,
"%sarg%d: %ld", "%sarg%d: %ld",
printed ? ", " : "", i, args[i]); printed ? ", " : "", i, val);
++i; ++i;
} }
} }
......
...@@ -110,13 +110,11 @@ __perf_main () ...@@ -110,13 +110,11 @@ __perf_main ()
# List perf subcommands or long options # List perf subcommands or long options
if [ $cword -eq 1 ]; then if [ $cword -eq 1 ]; then
if [[ $cur == --* ]]; then if [[ $cur == --* ]]; then
__perfcomp '--help --version \ cmds=$($cmd --list-opts)
--exec-path --html-path --paginate --no-pager \
--perf-dir --work-tree --debugfs-dir' -- "$cur"
else else
cmds=$($cmd --list-cmds) cmds=$($cmd --list-cmds)
__perfcomp "$cmds" "$cur"
fi fi
__perfcomp "$cmds" "$cur"
# List possible events for -e option # List possible events for -e option
elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then
evts=$($cmd list --raw-dump) evts=$($cmd list --raw-dump)
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "util/quote.h" #include "util/quote.h"
#include "util/run-command.h" #include "util/run-command.h"
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/parse-options.h"
#include "util/debug.h" #include "util/debug.h"
#include <api/fs/debugfs.h> #include <api/fs/debugfs.h>
#include <pthread.h> #include <pthread.h>
...@@ -125,6 +126,23 @@ static void commit_pager_choice(void) ...@@ -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) static int handle_options(const char ***argv, int *argc, int *envchanged)
{ {
int handled = 0; int handled = 0;
...@@ -223,6 +241,16 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) ...@@ -223,6 +241,16 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
struct cmd_struct *p = commands+i; struct cmd_struct *p = commands+i;
printf("%s ", p->cmd); 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); exit(0);
} else if (!strcmp(cmd, "--debug")) { } else if (!strcmp(cmd, "--debug")) {
if (*argc < 2) { if (*argc < 2) {
......
...@@ -281,35 +281,93 @@ void disable_buildid_cache(void) ...@@ -281,35 +281,93 @@ void disable_buildid_cache(void)
no_buildid_cache = true; 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, int build_id_cache__add_s(const char *sbuild_id, const char *name,
bool is_kallsyms, bool is_vdso) bool is_kallsyms, bool is_vdso)
{ {
const size_t size = PATH_MAX; const size_t size = PATH_MAX;
char *realname, *filename = zalloc(size), char *realname = NULL, *filename = NULL, *dir_name = NULL,
*linkname = zalloc(size), *targetname, *tmp; *linkname = zalloc(size), *targetname, *tmp;
int len, err = -1; int err = -1;
bool slash = is_kallsyms || is_vdso;
if (is_kallsyms) { 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
realname = realpath(name, NULL); 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; goto out_free;
len = scnprintf(filename, size, "%s%s%s", if (mkdir_p(dir_name, 0755))
buildid_dir, slash ? "/" : "",
is_vdso ? DSO__NAME_VDSO : realname);
if (mkdir_p(filename, 0755))
goto out_free; 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 (access(filename, F_OK)) {
if (is_kallsyms) { if (is_kallsyms) {
...@@ -337,6 +395,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, ...@@ -337,6 +395,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
if (!is_kallsyms) if (!is_kallsyms)
free(realname); free(realname);
free(filename); free(filename);
free(dir_name);
free(linkname); free(linkname);
return err; return err;
} }
...@@ -352,6 +411,18 @@ static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, ...@@ -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); 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) int build_id_cache__remove_s(const char *sbuild_id)
{ {
const size_t size = PATH_MAX; const size_t size = PATH_MAX;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#define BUILD_ID_SIZE 20 #define BUILD_ID_SIZE 20
#include "tool.h" #include "tool.h"
#include "strlist.h"
#include <linux/types.h> #include <linux/types.h>
extern struct perf_tool build_id__mark_dso_hit_ops; 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); ...@@ -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__write_buildid_table(struct perf_session *session, int fd);
int perf_session__cache_build_ids(struct perf_session *session); 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, int build_id_cache__add_s(const char *sbuild_id,
const char *name, bool is_kallsyms, bool is_vdso); const char *name, bool is_kallsyms, bool is_vdso);
int build_id_cache__remove_s(const char *sbuild_id); int build_id_cache__remove_s(const char *sbuild_id);
......
...@@ -20,11 +20,6 @@ ...@@ -20,11 +20,6 @@
#define MAX_NAME_LEN 100 #define MAX_NAME_LEN 100
struct event_symbol {
const char *symbol;
const char *alias;
};
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
extern int parse_events_debug; extern int parse_events_debug;
#endif #endif
...@@ -39,7 +34,7 @@ static struct perf_pmu_event_symbol *perf_pmu_events_list; ...@@ -39,7 +34,7 @@ static struct perf_pmu_event_symbol *perf_pmu_events_list;
*/ */
static int perf_pmu_events_list_num; 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] = { [PERF_COUNT_HW_CPU_CYCLES] = {
.symbol = "cpu-cycles", .symbol = "cpu-cycles",
.alias = "cycles", .alias = "cycles",
...@@ -82,7 +77,7 @@ static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { ...@@ -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] = { [PERF_COUNT_SW_CPU_CLOCK] = {
.symbol = "cpu-clock", .symbol = "cpu-clock",
.alias = "", .alias = "",
...@@ -1089,6 +1084,14 @@ static const char * const event_type_descriptors[] = { ...@@ -1089,6 +1084,14 @@ static const char * const event_type_descriptors[] = {
"Hardware breakpoint", "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 * 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, ...@@ -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; struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
char dir_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); sys_dir = opendir(tracing_events_path);
if (!sys_dir) if (!sys_dir)
return; 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) { for_each_subsystem(sys_dir, sys_dirent, sys_next) {
if (subsys_glob != NULL && if (subsys_glob != NULL &&
!strglobmatch(sys_dirent.d_name, subsys_glob)) !strglobmatch(sys_dirent.d_name, subsys_glob))
...@@ -1121,19 +1134,56 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, ...@@ -1121,19 +1134,56 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
!strglobmatch(evt_dirent.d_name, event_glob)) !strglobmatch(evt_dirent.d_name, event_glob))
continue; continue;
if (name_only) { if (!evt_num_known) {
printf("%s:%s ", sys_dirent.d_name, evt_dirent.d_name); evt_num++;
continue; continue;
} }
snprintf(evt_path, MAXPATHLEN, "%s:%s", snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent.d_name, evt_dirent.d_name); 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(evt_dir);
} }
closedir(sys_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) ...@@ -1214,38 +1264,19 @@ static bool is_event_supported(u8 type, unsigned config)
return ret; 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) 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 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 (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { 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) ...@@ -1263,27 +1294,66 @@ int print_hwcache_events(const char *event_glob, bool name_only)
type | (op << 8) | (i << 16))) type | (op << 8) | (i << 16)))
continue; continue;
if (name_only) if (!evt_num_known) {
printf("%s ", name); evt_num++;
else continue;
printf(" %-50s [%s]\n", name, }
event_type_descriptors[PERF_TYPE_HW_CACHE]);
++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[PERF_TYPE_HW_CACHE]);
}
if (evt_num)
printf("\n"); 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, struct event_symbol *syms, unsigned max,
bool name_only) bool name_only)
{ {
unsigned i, printed = 0; unsigned int i, evt_i = 0, evt_num = 0;
char name[MAX_NAME_LEN]; 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++) { for (i = 0; i < max; i++, syms++) {
...@@ -1295,23 +1365,49 @@ static void print_symbol_events(const char *event_glob, unsigned type, ...@@ -1295,23 +1365,49 @@ static void print_symbol_events(const char *event_glob, unsigned type,
if (!is_event_supported(type, i)) if (!is_event_supported(type, i))
continue; continue;
if (name_only) { if (!evt_num_known) {
printf("%s ", syms->symbol); evt_num++;
continue; continue;
} }
if (strlen(syms->alias)) if (!name_only && strlen(syms->alias))
snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias); snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);
else else
strncpy(name, syms->symbol, MAX_NAME_LEN); strncpy(name, syms->symbol, MAX_NAME_LEN);
printf(" %-50s [%s]\n", name, event_type_descriptors[type]); evt_list[evt_i] = strdup(name);
if (evt_list[evt_i] == NULL)
printed++; 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"); 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, ...@@ -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 parse_events_error(void *data, void *scanner, char const *msg);
void print_events(const char *event_glob, bool name_only); 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, void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
bool name_only); bool name_only);
int print_hwcache_events(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 ...@@ -505,15 +505,18 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
break; break;
case PARSE_OPT_LIST_OPTS: case PARSE_OPT_LIST_OPTS:
while (options->type != OPTION_END) { while (options->type != OPTION_END) {
printf("--%s ", options->long_name); if (options->long_name)
printf("--%s ", options->long_name);
options++; options++;
} }
putchar('\n');
exit(130); exit(130);
case PARSE_OPT_LIST_SUBCMDS: case PARSE_OPT_LIST_SUBCMDS:
if (subcommands) { if (subcommands) {
for (int i = 0; subcommands[i]; i++) for (int i = 0; subcommands[i]; i++)
printf("%s ", subcommands[i]); printf("%s ", subcommands[i]);
} }
putchar('\n');
exit(130); exit(130);
default: /* PARSE_OPT_UNKNOWN */ default: /* PARSE_OPT_UNKNOWN */
if (ctx.argv[0][1] == '-') { if (ctx.argv[0][1] == '-') {
......
...@@ -151,7 +151,7 @@ static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) ...@@ -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); sym = __find_kernel_function_by_name(name, &map);
if (sym) if (sym)
return map->unmap_ip(map, sym->start) - return map->unmap_ip(map, sym->start) -
(reloc) ? 0 : map->reloc; ((reloc) ? 0 : map->reloc);
} }
return 0; return 0;
} }
...@@ -533,7 +533,7 @@ static int get_real_path(const char *raw_path, const char *comp_dir, ...@@ -533,7 +533,7 @@ static int get_real_path(const char *raw_path, const char *comp_dir,
else { else {
if (access(raw_path, R_OK) == 0) { if (access(raw_path, R_OK) == 0) {
*new_path = strdup(raw_path); *new_path = strdup(raw_path);
return 0; return *new_path ? 0 : -ENOMEM;
} else } else
return -errno; return -errno;
} }
...@@ -549,9 +549,11 @@ static int get_real_path(const char *raw_path, const char *comp_dir, ...@@ -549,9 +549,11 @@ static int get_real_path(const char *raw_path, const char *comp_dir,
if (access(*new_path, R_OK) == 0) if (access(*new_path, R_OK) == 0)
return 0; return 0;
if (!symbol_conf.source_prefix) if (!symbol_conf.source_prefix) {
/* In case of searching comp_dir, don't retry */ /* In case of searching comp_dir, don't retry */
zfree(new_path);
return -errno; return -errno;
}
switch (errno) { switch (errno) {
case ENAMETOOLONG: case ENAMETOOLONG:
...@@ -1903,6 +1905,95 @@ static struct strlist *get_probe_trace_command_rawlist(int fd) ...@@ -1903,6 +1905,95 @@ static struct strlist *get_probe_trace_command_rawlist(int fd)
return sl; 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 */ /* Show an event */
static int show_perf_probe_event(struct perf_probe_event *pev, static int show_perf_probe_event(struct perf_probe_event *pev,
const char *module) const char *module)
...@@ -2117,6 +2208,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, ...@@ -2117,6 +2208,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
char buf[64]; char buf[64];
const char *event, *group; const char *event, *group;
struct strlist *namelist; struct strlist *namelist;
LIST_HEAD(blacklist);
struct kprobe_blacklist_node *node;
if (pev->uprobes) if (pev->uprobes)
fd = open_uprobe_events(true); fd = open_uprobe_events(true);
...@@ -2134,11 +2227,25 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, ...@@ -2134,11 +2227,25 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
pr_debug("Failed to get current event list.\n"); pr_debug("Failed to get current event list.\n");
return -EIO; 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; ret = 0;
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) { for (i = 0; i < ntevs; i++) {
tev = &tevs[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) if (pev->event)
event = pev->event; event = pev->event;
else else
...@@ -2189,13 +2296,15 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, ...@@ -2189,13 +2296,15 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
allow_suffix = true; 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. */ /* Show how to use the event. */
pr_info("\nYou can now use it in all perf tools, such as:\n\n"); 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, pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
tev->event); tev->event);
} }
kprobe_blacklist__delete(&blacklist);
strlist__delete(namelist); strlist__delete(namelist);
close(fd); close(fd);
return ret; return ret;
......
...@@ -1463,6 +1463,15 @@ int sort_dimension__add(const char *tok) ...@@ -1463,6 +1463,15 @@ int sort_dimension__add(const char *tok)
sort__has_parent = 1; sort__has_parent = 1;
} else if (sd->entry == &sort_sym) { } else if (sd->entry == &sort_sym) {
sort__has_sym = 1; 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) { } else if (sd->entry == &sort_dso) {
sort__has_dso = 1; sort__has_dso = 1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment