|
@@ -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;
|
|
|
}
|