From 38821fe299c7a39d1b410a9901038392aa26021f Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Mon, 26 Jul 2021 12:56:06 -0400 Subject: [PATCH] bcc/python: Add support for API 'bpf_attach_perf_event_raw' in BPF python interface 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 --- src/python/bcc/__init__.py | 20 ++++++++ src/python/bcc/libbcc.py | 6 +++ tests/python/test_attach_perf_event.py | 64 ++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100755 tests/python/test_attach_perf_event.py diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py index ab80f81e..76678e6e 100644 --- a/src/python/bcc/__init__.py +++ b/src/python/bcc/__init__.py @@ -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)] diff --git a/src/python/bcc/libbcc.py b/src/python/bcc/libbcc.py index 959296e3..049968bb 100644 --- a/src/python/bcc/libbcc.py +++ b/src/python/bcc/libbcc.py @@ -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 index 00000000..c53f450e --- /dev/null +++ b/tests/python/test_attach_perf_event.py @@ -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 +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() -- 2.34.1