Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9aaf5a5f authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Arnaldo Carvalho de Melo
Browse files

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: default avatarArnaldo 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: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 55d43bca
Loading
Loading
Loading
Loading
+108 −1
Original line number Diff line number Diff line
@@ -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;