diff options
Diffstat (limited to 'tools/perf/util/bpf-loader.c')
| -rw-r--r-- | tools/perf/util/bpf-loader.c | 222 | 
1 files changed, 221 insertions, 1 deletions
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index ab56073c5d6e..56f6fe8cf318 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -10,6 +10,8 @@  #include "perf.h"  #include "debug.h"  #include "bpf-loader.h" +#include "probe-event.h" +#include "probe-finder.h" // for MAX_PROBES  #define DEFINE_PRINT_FN(name, level) \  static int libbpf_##name(const char *fmt, ...)	\ @@ -27,6 +29,10 @@ DEFINE_PRINT_FN(warning, 0)  DEFINE_PRINT_FN(info, 0)  DEFINE_PRINT_FN(debug, 1) +struct bpf_prog_priv { +	struct perf_probe_event pev; +}; +  struct bpf_object *bpf__prepare_load(const char *filename)  {  	struct bpf_object *obj; @@ -52,6 +58,220 @@ void bpf__clear(void)  {  	struct bpf_object *obj, *tmp; -	bpf_object__for_each_safe(obj, tmp) +	bpf_object__for_each_safe(obj, tmp) { +		bpf__unprobe(obj);  		bpf_object__close(obj); +	} +} + +static void +bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused, +		     void *_priv) +{ +	struct bpf_prog_priv *priv = _priv; + +	cleanup_perf_probe_events(&priv->pev, 1); +	free(priv); +} + +static int +config_bpf_program(struct bpf_program *prog) +{ +	struct perf_probe_event *pev = NULL; +	struct bpf_prog_priv *priv = NULL; +	const char *config_str; +	int err; + +	config_str = bpf_program__title(prog, false); +	if (!config_str) { +		pr_debug("bpf: unable to get title for program\n"); +		return -EINVAL; +	} + +	priv = calloc(sizeof(*priv), 1); +	if (!priv) { +		pr_debug("bpf: failed to alloc priv\n"); +		return -ENOMEM; +	} +	pev = &priv->pev; + +	pr_debug("bpf: config program '%s'\n", config_str); +	err = parse_perf_probe_command(config_str, pev); +	if (err < 0) { +		pr_debug("bpf: '%s' is not a valid config string\n", +			 config_str); +		err = -EINVAL; +		goto errout; +	} + +	if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { +		pr_debug("bpf: '%s': group for event is set and not '%s'.\n", +			 config_str, PERF_BPF_PROBE_GROUP); +		err = -EINVAL; +		goto errout; +	} else if (!pev->group) +		pev->group = strdup(PERF_BPF_PROBE_GROUP); + +	if (!pev->group) { +		pr_debug("bpf: strdup failed\n"); +		err = -ENOMEM; +		goto errout; +	} + +	if (!pev->event) { +		pr_debug("bpf: '%s': event name is missing\n", +			 config_str); +		err = -EINVAL; +		goto errout; +	} +	pr_debug("bpf: config '%s' is ok\n", config_str); + +	err = bpf_program__set_private(prog, priv, bpf_prog_priv__clear); +	if (err) { +		pr_debug("Failed to set priv for program '%s'\n", config_str); +		goto errout; +	} + +	return 0; + +errout: +	if (pev) +		clear_perf_probe_event(pev); +	free(priv); +	return err; +} + +static int bpf__prepare_probe(void) +{ +	static int err = 0; +	static bool initialized = false; + +	/* +	 * Make err static, so if init failed the first, bpf__prepare_probe() +	 * fails each time without calling init_probe_symbol_maps multiple +	 * times. +	 */ +	if (initialized) +		return err; + +	initialized = true; +	err = init_probe_symbol_maps(false); +	if (err < 0) +		pr_debug("Failed to init_probe_symbol_maps\n"); +	probe_conf.max_probes = MAX_PROBES; +	return err; +} + +int bpf__probe(struct bpf_object *obj) +{ +	int err = 0; +	struct bpf_program *prog; +	struct bpf_prog_priv *priv; +	struct perf_probe_event *pev; + +	err = bpf__prepare_probe(); +	if (err) { +		pr_debug("bpf__prepare_probe failed\n"); +		return err; +	} + +	bpf_object__for_each_program(prog, obj) { +		err = config_bpf_program(prog); +		if (err) +			goto out; + +		err = bpf_program__get_private(prog, (void **)&priv); +		if (err || !priv) +			goto out; +		pev = &priv->pev; + +		err = convert_perf_probe_events(pev, 1); +		if (err < 0) { +			pr_debug("bpf_probe: failed to convert perf probe events"); +			goto out; +		} + +		err = apply_perf_probe_events(pev, 1); +		if (err < 0) { +			pr_debug("bpf_probe: failed to apply perf probe events"); +			goto out; +		} +	} +out: +	return err < 0 ? err : 0; +} + +#define EVENTS_WRITE_BUFSIZE  4096 +int bpf__unprobe(struct bpf_object *obj) +{ +	int err, ret = 0; +	struct bpf_program *prog; +	struct bpf_prog_priv *priv; + +	bpf_object__for_each_program(prog, obj) { +		int i; + +		err = bpf_program__get_private(prog, (void **)&priv); +		if (err || !priv) +			continue; + +		for (i = 0; i < priv->pev.ntevs; i++) { +			struct probe_trace_event *tev = &priv->pev.tevs[i]; +			char name_buf[EVENTS_WRITE_BUFSIZE]; +			struct strfilter *delfilter; + +			snprintf(name_buf, EVENTS_WRITE_BUFSIZE, +				 "%s:%s", tev->group, tev->event); +			name_buf[EVENTS_WRITE_BUFSIZE - 1] = '\0'; + +			delfilter = strfilter__new(name_buf, NULL); +			if (!delfilter) { +				pr_debug("Failed to create filter for unprobing\n"); +				ret = -ENOMEM; +				continue; +			} + +			err = del_perf_probe_events(delfilter); +			strfilter__delete(delfilter); +			if (err) { +				pr_debug("Failed to delete %s\n", name_buf); +				ret = err; +				continue; +			} +		} +	} +	return ret; +} + +#define bpf__strerror_head(err, buf, size) \ +	char sbuf[STRERR_BUFSIZE], *emsg;\ +	if (!size)\ +		return 0;\ +	if (err < 0)\ +		err = -err;\ +	emsg = strerror_r(err, sbuf, sizeof(sbuf));\ +	switch (err) {\ +	default:\ +		scnprintf(buf, size, "%s", emsg);\ +		break; + +#define bpf__strerror_entry(val, fmt...)\ +	case val: {\ +		scnprintf(buf, size, fmt);\ +		break;\ +	} + +#define bpf__strerror_end(buf, size)\ +	}\ +	buf[size - 1] = '\0'; + +int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, +			int err, char *buf, size_t size) +{ +	bpf__strerror_head(err, buf, size); +	bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'"); +	bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0\n"); +	bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file\n"); +	bpf__strerror_end(buf, size); +	return 0;  }  | 
