bcc/python: Add support for API 'bpf_attach_perf_event_raw' in BPF python interface
authorAthira Rajeev <atrajeev@linux.vnet.ibm.com>
Mon, 26 Jul 2021 16:56:06 +0000 (12:56 -0400)
committerAthira Rajeev <atrajeev@linux.vnet.ibm.com>
Fri, 13 Aug 2021 11:20:40 +0000 (16:50 +0530)
Add python interface for attach_perf_event_raw to bcc.
The bpf_attach_perf_event_raw API provide flexibility to use
advanced features of perf events with BPF. Presently, this
API is available to use in BPF programs via C and C++ interface.
Patch enables support to use in python interface.

Patch also adds testcase under 'tests/python' which uses
the newly added python interface 'attach_perf_event_raw'.

Signed-off-by: Athira Rajeev <atrajeev@linux.vnet.ibm.com>
src/python/bcc/__init__.py
src/python/bcc/libbcc.py
tests/python/test_attach_perf_event.py [new file with mode: 0755]

index ab80f81e2f98d7bebf42c85f24cfe008a7ca945e..76678e6efdeab09f3d88d3efda132328ea1a2ad4 100644 (file)
@@ -1178,6 +1178,26 @@ class BPF(object):
                         sample_period, sample_freq, pid, i, group_fd)
         self.open_perf_events[(ev_type, ev_config)] = res
 
+    def _attach_perf_event_raw(self, progfd, attr, pid, cpu, group_fd):
+        res = lib.bpf_attach_perf_event_raw(progfd, ct.byref(attr), pid,
+                cpu, group_fd, 0)
+        if res < 0:
+            raise Exception("Failed to attach BPF to perf raw event")
+        return res
+
+    def attach_perf_event_raw(self, attr=-1, fn_name=b"", pid=-1, cpu=-1, group_fd=-1):
+        fn_name = _assert_is_bytes(fn_name)
+        fn = self.load_func(fn_name, BPF.PERF_EVENT)
+        res = {}
+        if cpu >= 0:
+            res[cpu] = self._attach_perf_event_raw(fn.fd, attr,
+                    pid, cpu, group_fd)
+        else:
+            for i in get_online_cpus():
+                res[i] = self._attach_perf_event_raw(fn.fd, attr,
+                        pid, i, group_fd)
+        self.open_perf_events[(attr.type, attr.config)] = res
+
     def detach_perf_event(self, ev_type=-1, ev_config=-1):
         try:
             fds = self.open_perf_events[(ev_type, ev_config)]
index 959296e393b562fd2cbd8b38c397696317fd2052..049968bbe688338645746b77c707616a8ea90d38 100644 (file)
@@ -16,6 +16,9 @@ import ctypes as ct
 
 lib = ct.CDLL("libbcc.so.0", use_errno=True)
 
+# needed for perf_event_attr() ctype
+from .perf import Perf
+
 # keep in sync with bcc_common.h
 lib.bpf_module_create_b.restype = ct.c_void_p
 lib.bpf_module_create_b.argtypes = [ct.c_char_p, ct.c_char_p, ct.c_uint,
@@ -147,6 +150,9 @@ lib.bpf_attach_perf_event.restype = ct.c_int
 lib.bpf_attach_perf_event.argtype = [ct.c_int, ct.c_uint, ct.c_uint, ct.c_ulonglong, ct.c_ulonglong,
         ct.c_int, ct.c_int, ct.c_int]
 
+lib.bpf_attach_perf_event_raw.restype = ct.c_int
+lib.bpf_attach_perf_event_raw.argtype = [Perf.perf_event_attr(), ct.c_uint, ct.c_uint, ct.c_uint, ct.c_uint]
+
 lib.bpf_close_perf_event_fd.restype = ct.c_int
 lib.bpf_close_perf_event_fd.argtype = [ct.c_int]
 
diff --git a/tests/python/test_attach_perf_event.py b/tests/python/test_attach_perf_event.py
new file mode 100755 (executable)
index 0000000..c53f450
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# Copyright 2021, Athira Rajeev, IBM Corp.
+# Licensed under the Apache License, Version 2.0 (the "License")
+
+import bcc
+import os
+import time
+import unittest
+from bcc import BPF, PerfType, PerfHWConfig
+from bcc import Perf
+from time import sleep
+
+class TestPerfAttachRaw(unittest.TestCase):
+    def test_attach_raw_event(self):
+        bpf_text="""
+#include <linux/perf_event.h>
+struct key_t {
+    int cpu;
+    int pid;
+    char name[100];
+};
+
+static inline __attribute__((always_inline)) void get_key(struct key_t* key) {
+    key->cpu = bpf_get_smp_processor_id();
+    key->pid = bpf_get_current_pid_tgid();
+    bpf_get_current_comm(&(key->name), sizeof(key->name));
+}
+
+int on_sample_hit(struct bpf_perf_event_data *ctx) {
+    struct key_t key = {};
+    get_key(&key);
+    u64 addr = 0;
+    struct bpf_perf_event_data_kern *kctx;
+    struct perf_sample_data *data;
+
+    kctx = (struct bpf_perf_event_data_kern *)ctx;
+    bpf_probe_read(&data, sizeof(struct perf_sample_data*), &(kctx->data));
+    if (data)
+        bpf_probe_read(&addr, sizeof(u64), &(data->addr));
+
+    bpf_trace_printk("Hit a sample with pid: %ld, comm: %s, addr: 0x%llx\\n", key.pid, key.name, addr);
+    return 0;
+}
+
+"""
+
+        b = BPF(text=bpf_text)
+        try:
+            event_attr = Perf.perf_event_attr()
+            event_attr.type = Perf.PERF_TYPE_HARDWARE
+            event_attr.config = PerfHWConfig.CACHE_MISSES
+            event_attr.sample_period = 1000000
+            event_attr.sample_type = 0x8 # PERF_SAMPLE_ADDR
+            event_attr.exclude_kernel = 1
+            b.attach_perf_event_raw(attr=event_attr, fn_name="on_sample_hit", pid=-1, cpu=-1)
+        except Exception:
+            print("Failed to attach to a raw event. Please check the event attr used")
+            exit()
+
+        print("Running for 4 seconds or hit Ctrl-C to end. Check trace file for samples information written by bpf_trace_printk.")
+        sleep(4)
+
+if __name__ == "__main__":
+    unittest.main()