Support kfunc in opensnoop.py

Adding kfunc return trampoline probe if available instead of
kprobe/kretprobe probes.

The return trampoline has access to function entry arguments,
so we are good with just single eBPF program.

The kfunc trampolines are also faster - less intrusive.

Below are stats for compiling linux kernel while running
opensnoop.py on the background for kprobes and kfuncs.

Without opensnoop.py:

 Performance counter stats for 'system wide':

   849,741,782,765      cycles:k

     162.235646336 seconds time elapsed

With opensnoop.py using kprobes:

 Performance counter stats for 'system wide':

   941,615,199,769      cycles:k

     164.355032879 seconds time elapsed

With opensnoop.py using trampolines:

 Performance counter stats for 'system wide':

   913,437,005,488      cycles:k

     163.742914795 seconds time elapsed

Signed-off-by: Jiri Olsa <[email protected]>
diff --git a/tools/opensnoop.py b/tools/opensnoop.py
index 6d1388b..4f94d01 100755
--- a/tools/opensnoop.py
+++ b/tools/opensnoop.py
@@ -105,8 +105,11 @@
 #if CGROUPSET
 BPF_TABLE_PINNED("hash", u64, u64, cgroupset, 1024, "CGROUPPATH");
 #endif
-BPF_HASH(infotmp, u64, struct val_t);
 BPF_PERF_OUTPUT(events);
+"""
+
+bpf_text_kprobe = """
+BPF_HASH(infotmp, u64, struct val_t);
 
 int trace_entry(struct pt_regs *ctx, int dfd, const char __user *filename, int flags)
 {
@@ -162,6 +165,41 @@
     return 0;
 }
 """
+
+bpf_text_kfunc= """
+KRETFUNC_PROBE(do_sys_open, int dfd, const char *filename, int flags, int mode, int ret)
+{
+    u64 id = bpf_get_current_pid_tgid();
+    u32 pid = id >> 32; // PID is higher part
+    u32 tid = id;       // Cast and get the lower part
+    u32 uid = bpf_get_current_uid_gid();
+
+    PID_TID_FILTER
+    UID_FILTER
+    FLAGS_FILTER
+
+    struct data_t data = {};
+    bpf_get_current_comm(&data.comm, sizeof(data.comm));
+
+    u64 tsp = bpf_ktime_get_ns();
+
+    bpf_probe_read(&data.fname, sizeof(data.fname), (void *)filename);
+    data.id    = id;
+    data.ts    = tsp / 1000;
+    data.uid   = bpf_get_current_uid_gid();
+    data.flags = flags; // EXTENDED_STRUCT_MEMBER
+    data.ret   = ret;
+
+    events.perf_submit(ctx, &data, sizeof(data));
+}
+"""
+
+is_support_kfunc = BPF.support_kfunc()
+if is_support_kfunc:
+    bpf_text += bpf_text_kfunc
+else:
+    bpf_text += bpf_text_kprobe
+
 if args.tid:  # TID trumps PID
     bpf_text = bpf_text.replace('PID_TID_FILTER',
         'if (tid != %s) { return 0; }' % args.tid)
@@ -195,8 +233,9 @@
 
 # initialize BPF
 b = BPF(text=bpf_text)
-b.attach_kprobe(event="do_sys_open", fn_name="trace_entry")
-b.attach_kretprobe(event="do_sys_open", fn_name="trace_return")
+if not is_support_kfunc:
+    b.attach_kprobe(event="do_sys_open", fn_name="trace_entry")
+    b.attach_kretprobe(event="do_sys_open", fn_name="trace_return")
 
 initial_ts = 0