perf probe: Check kprobes blacklist when adding new events

Recent linux kernel provides a blacklist of the functions which can not
be probed. perf probe can now check this blacklist before setting new
events and indicate better error message for users.

Without this patch,
  ----
  # perf probe --add vmalloc_fault
  Added new event:
  Failed to write event: Invalid argument
    Error: Failed to add events.
  ----
With this patch
  ----
  # perf probe --add vmalloc_fault
  Added new event:
  Warning: Skipped probing on blacklisted function: vmalloc_fault
  ----

Reported-by: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20150219143113.14434.5387.stgit@localhost.localdomain
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Masami Hiramatsu 2015-02-19 23:31:13 +09:00 committed by Arnaldo Carvalho de Melo
parent 55d43bcafe
commit 9aaf5a5f47

View File

@ -1903,6 +1903,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 +2206,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 +2225,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 +2294,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;