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
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,8 +196,7 @@ 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,
pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
err ? "FAIL" : "Ok");
return err;
}
......@@ -216,13 +215,37 @@ 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,
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;
}
static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
{
char filename[PATH_MAX];
......@@ -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,12 +286,13 @@ static int build_id_cache__update_file(const char *filename)
}
build_id__sprintf(build_id, sizeof(build_id), 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,
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 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
* 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;
if (!is_kallsyms) {
realname = realpath(name, NULL);
if (!realname)
goto out_free;
}
realname = (char *) name;
} else
realname = realpath(name, NULL);
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 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
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) {
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;
}
......
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