• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 PLUMgrid
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from __future__ import print_function
16import atexit
17import ctypes as ct
18import fcntl
19import json
20import os
21import re
22import struct
23import errno
24import sys
25basestring = (unicode if sys.version_info[0] < 3 else str)
26
27from .libbcc import lib, bcc_symbol, bcc_symbol_option, _SYM_CB_TYPE
28from .table import Table, PerfEventArray
29from .perf import Perf
30from .syscall import syscall_name
31from .utils import get_online_cpus, printb, _assert_is_bytes, ArgString
32from .version import __version__
33
34_probe_limit = 1000
35_num_open_probes = 0
36
37# for tests
38def _get_num_open_probes():
39    global _num_open_probes
40    return _num_open_probes
41
42TRACEFS = "/sys/kernel/debug/tracing"
43
44# Debug flags
45
46# Debug output compiled LLVM IR.
47DEBUG_LLVM_IR = 0x1
48# Debug output loaded BPF bytecode and register state on branches.
49DEBUG_BPF = 0x2
50# Debug output pre-processor result.
51DEBUG_PREPROCESSOR = 0x4
52# Debug output ASM instructions embedded with source.
53DEBUG_SOURCE = 0x8
54#Debug output register state on all instructions in addition to DEBUG_BPF.
55DEBUG_BPF_REGISTER_STATE = 0x10
56
57class SymbolCache(object):
58    def __init__(self, pid):
59        self.cache = lib.bcc_symcache_new(
60                pid, ct.cast(None, ct.POINTER(bcc_symbol_option)))
61
62    def resolve(self, addr, demangle):
63        """
64        Return a tuple of the symbol (function), its offset from the beginning
65        of the function, and the module in which it lies. For example:
66            ("start_thread", 0x202, "/usr/lib/.../libpthread-2.24.so")
67        If the symbol cannot be found but we know which module it is in,
68        return the module name and the offset from the beginning of the
69        module. If we don't even know the module, return the absolute
70        address as the offset.
71        """
72        sym = bcc_symbol()
73        if demangle:
74            res = lib.bcc_symcache_resolve(self.cache, addr, ct.byref(sym))
75        else:
76            res = lib.bcc_symcache_resolve_no_demangle(self.cache, addr,
77                                                       ct.byref(sym))
78        if res < 0:
79            if sym.module and sym.offset:
80                return (None, sym.offset,
81                        ct.cast(sym.module, ct.c_char_p).value)
82            return (None, addr, None)
83        if demangle:
84            name_res = sym.demangle_name
85            lib.bcc_symbol_free_demangle_name(ct.byref(sym))
86        else:
87            name_res = sym.name
88        return (name_res, sym.offset, ct.cast(sym.module, ct.c_char_p).value)
89
90    def resolve_name(self, module, name):
91        module = _assert_is_bytes(module)
92        name = _assert_is_bytes(name)
93        addr = ct.c_ulonglong()
94        if lib.bcc_symcache_resolve_name(self.cache, module, name,
95                ct.byref(addr)) < 0:
96            return -1
97        return addr.value
98
99class PerfType:
100    # From perf_type_id in uapi/linux/perf_event.h
101    HARDWARE = 0
102    SOFTWARE = 1
103
104class PerfHWConfig:
105    # From perf_hw_id in uapi/linux/perf_event.h
106    CPU_CYCLES = 0
107    INSTRUCTIONS = 1
108    CACHE_REFERENCES = 2
109    CACHE_MISSES = 3
110    BRANCH_INSTRUCTIONS = 4
111    BRANCH_MISSES = 5
112    BUS_CYCLES = 6
113    STALLED_CYCLES_FRONTEND = 7
114    STALLED_CYCLES_BACKEND = 8
115    REF_CPU_CYCLES = 9
116
117class PerfSWConfig:
118    # From perf_sw_id in uapi/linux/perf_event.h
119    CPU_CLOCK = 0
120    TASK_CLOCK = 1
121    PAGE_FAULTS = 2
122    CONTEXT_SWITCHES = 3
123    CPU_MIGRATIONS = 4
124    PAGE_FAULTS_MIN = 5
125    PAGE_FAULTS_MAJ = 6
126    ALIGNMENT_FAULTS = 7
127    EMULATION_FAULTS = 8
128    DUMMY = 9
129    BPF_OUTPUT = 10
130
131class BPF(object):
132    # From bpf_prog_type in uapi/linux/bpf.h
133    SOCKET_FILTER = 1
134    KPROBE = 2
135    SCHED_CLS = 3
136    SCHED_ACT = 4
137    TRACEPOINT = 5
138    XDP = 6
139    PERF_EVENT = 7
140    CGROUP_SKB = 8
141    CGROUP_SOCK = 9
142    LWT_IN = 10
143    LWT_OUT = 11
144    LWT_XMIT = 12
145    SOCK_OPS = 13
146    SK_SKB = 14
147    CGROUP_DEVICE = 15
148    SK_MSG = 16
149    RAW_TRACEPOINT = 17
150    CGROUP_SOCK_ADDR = 18
151
152    # from xdp_action uapi/linux/bpf.h
153    XDP_ABORTED = 0
154    XDP_DROP = 1
155    XDP_PASS = 2
156    XDP_TX = 3
157    XDP_REDIRECT = 4
158
159    _probe_repl = re.compile(b"[^a-zA-Z0-9_]")
160    _sym_caches = {}
161
162    _auto_includes = {
163        "linux/time.h": ["time"],
164        "linux/fs.h": ["fs", "file"],
165        "linux/blkdev.h": ["bio", "request"],
166        "linux/slab.h": ["alloc"],
167        "linux/netdevice.h": ["sk_buff", "net_device"]
168    }
169
170    _syscall_prefixes = [
171        b"sys_",
172        b"__x64_sys_",
173        b"__x32_compat_sys_",
174        b"__ia32_compat_sys_",
175        b"__arm64_sys_",
176    ]
177
178    # BPF timestamps come from the monotonic clock. To be able to filter
179    # and compare them from Python, we need to invoke clock_gettime.
180    # Adapted from http://stackoverflow.com/a/1205762
181    CLOCK_MONOTONIC = 1         # see <linux/time.h>
182
183    class timespec(ct.Structure):
184        _fields_ = [('tv_sec', ct.c_long), ('tv_nsec', ct.c_long)]
185
186    _librt = ct.CDLL('librt.so.1', use_errno=True)
187    _clock_gettime = _librt.clock_gettime
188    _clock_gettime.argtypes = [ct.c_int, ct.POINTER(timespec)]
189
190    @classmethod
191    def monotonic_time(cls):
192        """monotonic_time()
193        Returns the system monotonic time from clock_gettime, using the
194        CLOCK_MONOTONIC constant. The time returned is in nanoseconds.
195        """
196        t = cls.timespec()
197        if cls._clock_gettime(cls.CLOCK_MONOTONIC, ct.byref(t)) != 0:
198            errno = ct.get_errno()
199            raise OSError(errno, os.strerror(errno))
200        return t.tv_sec * 1e9 + t.tv_nsec
201
202    @classmethod
203    def generate_auto_includes(cls, program_words):
204        """
205        Generates #include statements automatically based on a set of
206        recognized types such as sk_buff and bio. The input is all the words
207        that appear in the BPF program, and the output is a (possibly empty)
208        string of #include statements, such as "#include <linux/fs.h>".
209        """
210        headers = ""
211        for header, keywords in cls._auto_includes.items():
212            for keyword in keywords:
213                for word in program_words:
214                    if keyword in word and header not in headers:
215                        headers += "#include <%s>\n" % header
216        return headers
217
218    # defined for compatibility reasons, to be removed
219    Table = Table
220
221    class Function(object):
222        def __init__(self, bpf, name, fd):
223            self.bpf = bpf
224            self.name = name
225            self.fd = fd
226
227    @staticmethod
228    def _find_file(filename):
229        """ If filename is invalid, search in ./ of argv[0] """
230        if filename:
231            if not os.path.isfile(filename):
232                argv0 = ArgString(sys.argv[0])
233                t = b"/".join([os.path.abspath(os.path.dirname(argv0.__str__())), filename])
234                if os.path.isfile(t):
235                    filename = t
236                else:
237                    raise Exception("Could not find file %s" % filename)
238        return filename
239
240    @staticmethod
241    def find_exe(bin_path):
242        """
243        find_exe(bin_path)
244
245        Traverses the PATH environment variable, looking for the first
246        directory that contains an executable file named bin_path, and
247        returns the full path to that file, or None if no such file
248        can be found. This is meant to replace invocations of the
249        "which" shell utility, which doesn't have portable semantics
250        for skipping aliases.
251        """
252        # Source: http://stackoverflow.com/a/377028
253        def is_exe(fpath):
254            return os.path.isfile(fpath) and \
255                os.access(fpath, os.X_OK)
256
257        fpath, fname = os.path.split(bin_path)
258        if fpath:
259            if is_exe(bin_path):
260                return bin_path
261        else:
262            for path in os.environ["PATH"].split(os.pathsep):
263                path = path.strip('"')
264                exe_file = os.path.join(path, bin_path)
265                if is_exe(exe_file):
266                    return exe_file
267        return None
268
269    def __init__(self, src_file=b"", hdr_file=b"", text=None, debug=0,
270            cflags=[], usdt_contexts=[]):
271        """Create a new BPF module with the given source code.
272
273        Note:
274            All fields are marked as optional, but either `src_file` or `text`
275            must be supplied, and not both.
276
277        Args:
278            src_file (Optional[str]): Path to a source file for the module
279            hdr_file (Optional[str]): Path to a helper header file for the `src_file`
280            text (Optional[str]): Contents of a source file for the module
281            debug (Optional[int]): Flags used for debug prints, can be |'d together
282                                   See "Debug flags" for explanation
283        """
284
285        src_file = _assert_is_bytes(src_file)
286        hdr_file = _assert_is_bytes(hdr_file)
287        text = _assert_is_bytes(text)
288
289        self.kprobe_fds = {}
290        self.uprobe_fds = {}
291        self.tracepoint_fds = {}
292        self.raw_tracepoint_fds = {}
293        self.perf_buffers = {}
294        self.open_perf_events = {}
295        self.tracefile = None
296        atexit.register(self.cleanup)
297
298        self.debug = debug
299        self.funcs = {}
300        self.tables = {}
301        self.module = None
302        cflags_array = (ct.c_char_p * len(cflags))()
303        for i, s in enumerate(cflags): cflags_array[i] = bytes(ArgString(s))
304        if text:
305            ctx_array = (ct.c_void_p * len(usdt_contexts))()
306            for i, usdt in enumerate(usdt_contexts):
307                ctx_array[i] = ct.c_void_p(usdt.get_context())
308            usdt_text = lib.bcc_usdt_genargs(ctx_array, len(usdt_contexts))
309            if usdt_text is None:
310                raise Exception("can't generate USDT probe arguments; " +
311                                "possible cause is missing pid when a " +
312                                "probe in a shared object has multiple " +
313                                "locations")
314            text = usdt_text + text
315
316        if text:
317            self.module = lib.bpf_module_create_c_from_string(text,
318                    self.debug, cflags_array, len(cflags_array))
319            if not self.module:
320                raise Exception("Failed to compile BPF text")
321        else:
322            src_file = BPF._find_file(src_file)
323            hdr_file = BPF._find_file(hdr_file)
324            if src_file.endswith(b".b"):
325                self.module = lib.bpf_module_create_b(src_file, hdr_file,
326                        self.debug)
327            else:
328                self.module = lib.bpf_module_create_c(src_file, self.debug,
329                        cflags_array, len(cflags_array))
330            if not self.module:
331                raise Exception("Failed to compile BPF module %s" % src_file)
332
333        for usdt_context in usdt_contexts:
334            usdt_context.attach_uprobes(self)
335
336        # If any "kprobe__" or "tracepoint__" or "raw_tracepoint__"
337        # prefixed functions were defined,
338        # they will be loaded and attached here.
339        self._trace_autoload()
340
341    def load_funcs(self, prog_type=KPROBE):
342        """load_funcs(prog_type=KPROBE)
343
344        Load all functions in this BPF module with the given type.
345        Returns a list of the function handles."""
346
347        fns = []
348        for i in range(0, lib.bpf_num_functions(self.module)):
349            func_name = lib.bpf_function_name(self.module, i)
350            fns.append(self.load_func(func_name, prog_type))
351
352        return fns
353
354    def load_func(self, func_name, prog_type):
355        func_name = _assert_is_bytes(func_name)
356        if func_name in self.funcs:
357            return self.funcs[func_name]
358        if not lib.bpf_function_start(self.module, func_name):
359            raise Exception("Unknown program %s" % func_name)
360        log_level = 0
361        if (self.debug & DEBUG_BPF_REGISTER_STATE):
362            log_level = 2
363        elif (self.debug & DEBUG_BPF):
364            log_level = 1
365        fd = lib.bpf_prog_load(prog_type, func_name,
366                lib.bpf_function_start(self.module, func_name),
367                lib.bpf_function_size(self.module, func_name),
368                lib.bpf_module_license(self.module),
369                lib.bpf_module_kern_version(self.module),
370                log_level, None, 0);
371
372        if fd < 0:
373            atexit.register(self.donothing)
374            if ct.get_errno() == errno.EPERM:
375                raise Exception("Need super-user privileges to run")
376
377            errstr = os.strerror(ct.get_errno())
378            raise Exception("Failed to load BPF program %s: %s" %
379                            (func_name, errstr))
380
381        fn = BPF.Function(self, func_name, fd)
382        self.funcs[func_name] = fn
383
384        return fn
385
386    def dump_func(self, func_name):
387        """
388        Return the eBPF bytecodes for the specified function as a string
389        """
390        func_name = _assert_is_bytes(func_name)
391        if not lib.bpf_function_start(self.module, func_name):
392            raise Exception("Unknown program %s" % func_name)
393
394        start, = lib.bpf_function_start(self.module, func_name),
395        size, = lib.bpf_function_size(self.module, func_name),
396        return ct.string_at(start, size)
397
398    str2ctype = {
399        u"_Bool": ct.c_bool,
400        u"char": ct.c_char,
401        u"wchar_t": ct.c_wchar,
402        u"unsigned char": ct.c_ubyte,
403        u"short": ct.c_short,
404        u"unsigned short": ct.c_ushort,
405        u"int": ct.c_int,
406        u"unsigned int": ct.c_uint,
407        u"long": ct.c_long,
408        u"unsigned long": ct.c_ulong,
409        u"long long": ct.c_longlong,
410        u"unsigned long long": ct.c_ulonglong,
411        u"float": ct.c_float,
412        u"double": ct.c_double,
413        u"long double": ct.c_longdouble,
414        u"__int128": ct.c_int64 * 2,
415        u"unsigned __int128": ct.c_uint64 * 2,
416    }
417    @staticmethod
418    def _decode_table_type(desc):
419        if isinstance(desc, basestring):
420            return BPF.str2ctype[desc]
421        anon = []
422        fields = []
423        for t in desc[1]:
424            if len(t) == 2:
425                fields.append((t[0], BPF._decode_table_type(t[1])))
426            elif len(t) == 3:
427                if isinstance(t[2], list):
428                    fields.append((t[0], BPF._decode_table_type(t[1]) * t[2][0]))
429                elif isinstance(t[2], int):
430                    fields.append((t[0], BPF._decode_table_type(t[1]), t[2]))
431                elif isinstance(t[2], basestring) and (
432                        t[2] == u"union" or t[2] == u"struct" or
433                        t[2] == u"struct_packed"):
434                    name = t[0]
435                    if name == "":
436                        name = "__anon%d" % len(anon)
437                        anon.append(name)
438                    fields.append((name, BPF._decode_table_type(t)))
439                else:
440                    raise Exception("Failed to decode type %s" % str(t))
441            else:
442                raise Exception("Failed to decode type %s" % str(t))
443        base = ct.Structure
444        is_packed = False
445        if len(desc) > 2:
446            if desc[2] == u"union":
447                base = ct.Union
448            elif desc[2] == u"struct":
449                base = ct.Structure
450            elif desc[2] == u"struct_packed":
451                base = ct.Structure
452                is_packed = True
453        if is_packed:
454            cls = type(str(desc[0]), (base,), dict(_anonymous_=anon, _pack_=1,
455                _fields_=fields))
456        else:
457            cls = type(str(desc[0]), (base,), dict(_anonymous_=anon,
458                _fields_=fields))
459        return cls
460
461    def get_table(self, name, keytype=None, leaftype=None, reducer=None):
462        name = _assert_is_bytes(name)
463        map_id = lib.bpf_table_id(self.module, name)
464        map_fd = lib.bpf_table_fd(self.module, name)
465        if map_fd < 0:
466            raise KeyError
467        if not keytype:
468            key_desc = lib.bpf_table_key_desc(self.module, name).decode("utf-8")
469            if not key_desc:
470                raise Exception("Failed to load BPF Table %s key desc" % name)
471            keytype = BPF._decode_table_type(json.loads(key_desc))
472        if not leaftype:
473            leaf_desc = lib.bpf_table_leaf_desc(self.module, name).decode("utf-8")
474            if not leaf_desc:
475                raise Exception("Failed to load BPF Table %s leaf desc" % name)
476            leaftype = BPF._decode_table_type(json.loads(leaf_desc))
477        return Table(self, map_id, map_fd, keytype, leaftype, reducer=reducer)
478
479    def __getitem__(self, key):
480        if key not in self.tables:
481            self.tables[key] = self.get_table(key)
482        return self.tables[key]
483
484    def __setitem__(self, key, leaf):
485        self.tables[key] = leaf
486
487    def __len__(self):
488        return len(self.tables)
489
490    def __delitem__(self, key):
491        del self.tables[key]
492
493    def __iter__(self):
494        return self.tables.__iter__()
495
496    @staticmethod
497    def attach_raw_socket(fn, dev):
498        dev = _assert_is_bytes(dev)
499        if not isinstance(fn, BPF.Function):
500            raise Exception("arg 1 must be of type BPF.Function")
501        sock = lib.bpf_open_raw_sock(dev)
502        if sock < 0:
503            errstr = os.strerror(ct.get_errno())
504            raise Exception("Failed to open raw device %s: %s" % (dev, errstr))
505        res = lib.bpf_attach_socket(sock, fn.fd)
506        if res < 0:
507            errstr = os.strerror(ct.get_errno())
508            raise Exception("Failed to attach BPF to device %s: %s"
509                    % (dev, errstr))
510        fn.sock = sock
511
512    @staticmethod
513    def get_kprobe_functions(event_re):
514        with open("%s/../kprobes/blacklist" % TRACEFS, "rb") as blacklist_f:
515            blacklist = set([line.rstrip().split()[1] for line in blacklist_f])
516        fns = []
517
518        in_init_section = 0
519        in_irq_section = 0
520        with open("/proc/kallsyms", "rb") as avail_file:
521            for line in avail_file:
522                (t, fn) = line.rstrip().split()[1:3]
523                # Skip all functions defined between __init_begin and
524                # __init_end
525                if in_init_section == 0:
526                    if fn == b'__init_begin':
527                        in_init_section = 1
528                        continue
529                elif in_init_section == 1:
530                    if fn == b'__init_end':
531                        in_init_section = 2
532                    continue
533                # Skip all functions defined between __irqentry_text_start and
534                # __irqentry_text_end
535                if in_irq_section == 0:
536                    if fn == b'__irqentry_text_start':
537                        in_irq_section = 1
538                        continue
539                elif in_irq_section == 1:
540                    if fn == b'__irqentry_text_end':
541                        in_irq_section = 2
542                    continue
543                # All functions defined as NOKPROBE_SYMBOL() start with the
544                # prefix _kbl_addr_*, blacklisting them by looking at the name
545                # allows to catch also those symbols that are defined in kernel
546                # modules.
547                if fn.startswith(b'_kbl_addr_'):
548                    continue
549                # Explicitly blacklist perf-related functions, they are all
550                # non-attachable.
551                elif fn.startswith(b'__perf') or fn.startswith(b'perf_'):
552                    continue
553                if (t.lower() in [b't', b'w']) and re.match(event_re, fn) \
554                    and fn not in blacklist:
555                    fns.append(fn)
556        return set(fns)     # Some functions may appear more than once
557
558    def _check_probe_quota(self, num_new_probes):
559        global _num_open_probes
560        if _num_open_probes + num_new_probes > _probe_limit:
561            raise Exception("Number of open probes would exceed global quota")
562
563    def _add_kprobe_fd(self, name, fd):
564        global _num_open_probes
565        self.kprobe_fds[name] = fd
566        _num_open_probes += 1
567
568    def _del_kprobe_fd(self, name):
569        global _num_open_probes
570        del self.kprobe_fds[name]
571        _num_open_probes -= 1
572
573    def _add_uprobe_fd(self, name, fd):
574        global _num_open_probes
575        self.uprobe_fds[name] = fd
576        _num_open_probes += 1
577
578    def _del_uprobe_fd(self, name):
579        global _num_open_probes
580        del self.uprobe_fds[name]
581        _num_open_probes -= 1
582
583    # Find current system's syscall prefix by testing on the BPF syscall.
584    # If no valid value found, will return the first possible value which
585    # would probably lead to error in later API calls.
586    def get_syscall_prefix(self):
587        for prefix in self._syscall_prefixes:
588            if self.ksymname(b"%sbpf" % prefix) != -1:
589                return prefix
590        return self._syscall_prefixes[0]
591
592    # Given a syscall's name, return the full Kernel function name with current
593    # system's syscall prefix. For example, given "clone" the helper would
594    # return "sys_clone" or "__x64_sys_clone".
595    def get_syscall_fnname(self, name):
596        name = _assert_is_bytes(name)
597        return self.get_syscall_prefix() + name
598
599    # Given a Kernel function name that represents a syscall but already has a
600    # prefix included, transform it to current system's prefix. For example,
601    # if "sys_clone" provided, the helper may translate it to "__x64_sys_clone".
602    def fix_syscall_fnname(self, name):
603        name = _assert_is_bytes(name)
604        for prefix in self._syscall_prefixes:
605            if name.startswith(prefix):
606                return self.get_syscall_fnname(name[len(prefix):])
607        return name
608
609    def attach_kprobe(self, event=b"", event_off=0, fn_name=b"", event_re=b""):
610        event = _assert_is_bytes(event)
611        fn_name = _assert_is_bytes(fn_name)
612        event_re = _assert_is_bytes(event_re)
613
614        # allow the caller to glob multiple functions together
615        if event_re:
616            matches = BPF.get_kprobe_functions(event_re)
617            self._check_probe_quota(len(matches))
618            for line in matches:
619                try:
620                    self.attach_kprobe(event=line, fn_name=fn_name)
621                except:
622                    pass
623            return
624
625        self._check_probe_quota(1)
626        fn = self.load_func(fn_name, BPF.KPROBE)
627        ev_name = b"p_" + event.replace(b"+", b"_").replace(b".", b"_")
628        fd = lib.bpf_attach_kprobe(fn.fd, 0, ev_name, event, event_off)
629        if fd < 0:
630            raise Exception("Failed to attach BPF program %s to kprobe %s" %
631                            (fn_name, event))
632        self._add_kprobe_fd(ev_name, fd)
633        return self
634
635    def attach_kretprobe(self, event=b"", fn_name=b"", event_re=b""):
636        event = _assert_is_bytes(event)
637        fn_name = _assert_is_bytes(fn_name)
638        event_re = _assert_is_bytes(event_re)
639
640        # allow the caller to glob multiple functions together
641        if event_re:
642            for line in BPF.get_kprobe_functions(event_re):
643                try:
644                    self.attach_kretprobe(event=line, fn_name=fn_name)
645                except:
646                    pass
647            return
648
649        self._check_probe_quota(1)
650        fn = self.load_func(fn_name, BPF.KPROBE)
651        ev_name = b"r_" + event.replace(b"+", b"_").replace(b".", b"_")
652        fd = lib.bpf_attach_kprobe(fn.fd, 1, ev_name, event, 0)
653        if fd < 0:
654            raise Exception("Failed to attach BPF program %s to kretprobe %s" %
655                            (fn_name, event))
656        self._add_kprobe_fd(ev_name, fd)
657        return self
658
659    def detach_kprobe_event(self, ev_name):
660        if ev_name not in self.kprobe_fds:
661            raise Exception("Kprobe %s is not attached" % ev_name)
662        res = lib.bpf_close_perf_event_fd(self.kprobe_fds[ev_name])
663        if res < 0:
664            raise Exception("Failed to close kprobe FD")
665        res = lib.bpf_detach_kprobe(ev_name)
666        if res < 0:
667            raise Exception("Failed to detach BPF from kprobe")
668        self._del_kprobe_fd(ev_name)
669
670    def detach_kprobe(self, event):
671        event = _assert_is_bytes(event)
672        ev_name = b"p_" + event.replace(b"+", b"_").replace(b".", b"_")
673        self.detach_kprobe_event(ev_name)
674
675    def detach_kretprobe(self, event):
676        event = _assert_is_bytes(event)
677        ev_name = b"r_" + event.replace(b"+", b"_").replace(b".", b"_")
678        self.detach_kprobe_event(ev_name)
679
680    @staticmethod
681    def attach_xdp(dev, fn, flags=0):
682        '''
683            This function attaches a BPF function to a device on the device
684            driver level (XDP)
685        '''
686        dev = _assert_is_bytes(dev)
687        if not isinstance(fn, BPF.Function):
688            raise Exception("arg 1 must be of type BPF.Function")
689        res = lib.bpf_attach_xdp(dev, fn.fd, flags)
690        if res < 0:
691            err_no = ct.get_errno()
692            if err_no == errno.EBADMSG:
693                raise Exception("Internal error while attaching BPF to device,"+
694                    " try increasing the debug level!")
695            else:
696                errstr = os.strerror(err_no)
697                raise Exception("Failed to attach BPF to device %s: %s"
698                            % (dev, errstr))
699
700    @staticmethod
701    def remove_xdp(dev, flags=0):
702        '''
703            This function removes any BPF function from a device on the
704            device driver level (XDP)
705        '''
706        dev = _assert_is_bytes(dev)
707        res = lib.bpf_attach_xdp(dev, -1, flags)
708        if res < 0:
709            errstr = os.strerror(ct.get_errno())
710            raise Exception("Failed to detach BPF from device %s: %s"
711                            % (dev, errstr))
712
713
714
715    @classmethod
716    def _check_path_symbol(cls, module, symname, addr, pid):
717        module = _assert_is_bytes(module)
718        symname = _assert_is_bytes(symname)
719        sym = bcc_symbol()
720        c_pid = 0 if pid == -1 else pid
721        if lib.bcc_resolve_symname(
722            module, symname,
723            addr or 0x0, c_pid,
724            ct.cast(None, ct.POINTER(bcc_symbol_option)),
725            ct.byref(sym),
726        ) < 0:
727            raise Exception("could not determine address of symbol %s" % symname)
728        module_path = ct.cast(sym.module, ct.c_char_p).value
729        lib.bcc_procutils_free(sym.module)
730        return module_path, sym.offset
731
732    @staticmethod
733    def find_library(libname):
734        libname = _assert_is_bytes(libname)
735        res = lib.bcc_procutils_which_so(libname, 0)
736        if not res:
737            return None
738        libpath = ct.cast(res, ct.c_char_p).value
739        lib.bcc_procutils_free(res)
740        return libpath
741
742    @staticmethod
743    def get_tracepoints(tp_re):
744        results = []
745        events_dir = os.path.join(TRACEFS, "events")
746        for category in os.listdir(events_dir):
747            cat_dir = os.path.join(events_dir, category)
748            if not os.path.isdir(cat_dir):
749                continue
750            for event in os.listdir(cat_dir):
751                evt_dir = os.path.join(cat_dir, event)
752                if os.path.isdir(evt_dir):
753                    tp = ("%s:%s" % (category, event))
754                    if re.match(tp_re, tp):
755                        results.append(tp)
756        return results
757
758    @staticmethod
759    def tracepoint_exists(category, event):
760        evt_dir = os.path.join(TRACEFS, "events", category, event)
761        return os.path.isdir(evt_dir)
762
763    def attach_tracepoint(self, tp=b"", tp_re=b"", fn_name=b""):
764        """attach_tracepoint(tp="", tp_re="", fn_name="")
765
766        Run the bpf function denoted by fn_name every time the kernel tracepoint
767        specified by 'tp' is hit. The optional parameters pid, cpu, and group_fd
768        can be used to filter the probe. The tracepoint specification is simply
769        the tracepoint category and the tracepoint name, separated by a colon.
770        For example: sched:sched_switch, syscalls:sys_enter_bind, etc.
771
772        Instead of a tracepoint name, a regular expression can be provided in
773        tp_re. The program will then attach to tracepoints that match the
774        provided regular expression.
775
776        To obtain a list of kernel tracepoints, use the tplist tool or cat the
777        file /sys/kernel/debug/tracing/available_events.
778
779        Examples:
780            BPF(text).attach_tracepoint(tp="sched:sched_switch", fn_name="on_switch")
781            BPF(text).attach_tracepoint(tp_re="sched:.*", fn_name="on_switch")
782        """
783
784        tp = _assert_is_bytes(tp)
785        tp_re = _assert_is_bytes(tp_re)
786        fn_name = _assert_is_bytes(fn_name)
787        if tp_re:
788            for tp in BPF.get_tracepoints(tp_re):
789                self.attach_tracepoint(tp=tp, fn_name=fn_name)
790            return
791
792        fn = self.load_func(fn_name, BPF.TRACEPOINT)
793        (tp_category, tp_name) = tp.split(b':')
794        fd = lib.bpf_attach_tracepoint(fn.fd, tp_category, tp_name)
795        if fd < 0:
796            raise Exception("Failed to attach BPF program %s to tracepoint %s" %
797                            (fn_name, tp))
798        self.tracepoint_fds[tp] = fd
799        return self
800
801    def attach_raw_tracepoint(self, tp=b"", fn_name=b""):
802        """attach_raw_tracepoint(self, tp=b"", fn_name=b"")
803
804        Run the bpf function denoted by fn_name every time the kernel tracepoint
805        specified by 'tp' is hit. The bpf function should be loaded as a
806        RAW_TRACEPOINT type. The fn_name is the kernel tracepoint name,
807        e.g., sched_switch, sys_enter_bind, etc.
808
809        Examples:
810            BPF(text).attach_raw_tracepoint(tp="sched_switch", fn_name="on_switch")
811        """
812
813        tp = _assert_is_bytes(tp)
814        if tp in self.raw_tracepoint_fds:
815            raise Exception("Raw tracepoint %s has been attached" % tp)
816
817        fn_name = _assert_is_bytes(fn_name)
818        fn = self.load_func(fn_name, BPF.RAW_TRACEPOINT)
819        fd = lib.bpf_attach_raw_tracepoint(fn.fd, tp)
820        if fd < 0:
821            raise Exception("Failed to attach BPF to raw tracepoint")
822        self.raw_tracepoint_fds[tp] = fd;
823        return self
824
825    def detach_raw_tracepoint(self, tp=b""):
826        """detach_raw_tracepoint(tp="")
827
828        Stop running the bpf function that is attached to the kernel tracepoint
829        specified by 'tp'.
830
831        Example: bpf.detach_raw_tracepoint("sched_switch")
832        """
833
834        tp = _assert_is_bytes(tp)
835        if tp not in self.raw_tracepoint_fds:
836            raise Exception("Raw tracepoint %s is not attached" % tp)
837        os.close(self.raw_tracepoint_fds[tp])
838        del self.raw_tracepoint_fds[tp]
839
840    @staticmethod
841    def support_raw_tracepoint():
842        # kernel symbol "bpf_find_raw_tracepoint" indicates raw_tracepint support
843        if BPF.ksymname("bpf_find_raw_tracepoint") != -1:
844            return True
845        return False
846
847    def detach_tracepoint(self, tp=b""):
848        """detach_tracepoint(tp="")
849
850        Stop running a bpf function that is attached to the kernel tracepoint
851        specified by 'tp'.
852
853        Example: bpf.detach_tracepoint("sched:sched_switch")
854        """
855
856        tp = _assert_is_bytes(tp)
857        if tp not in self.tracepoint_fds:
858            raise Exception("Tracepoint %s is not attached" % tp)
859        res = lib.bpf_close_perf_event_fd(self.tracepoint_fds[tp])
860        if res < 0:
861            raise Exception("Failed to detach BPF from tracepoint")
862        (tp_category, tp_name) = tp.split(b':')
863        res = lib.bpf_detach_tracepoint(tp_category, tp_name)
864        if res < 0:
865            raise Exception("Failed to detach BPF from tracepoint")
866        del self.tracepoint_fds[tp]
867
868    def _attach_perf_event(self, progfd, ev_type, ev_config,
869            sample_period, sample_freq, pid, cpu, group_fd):
870        res = lib.bpf_attach_perf_event(progfd, ev_type, ev_config,
871                sample_period, sample_freq, pid, cpu, group_fd)
872        if res < 0:
873            raise Exception("Failed to attach BPF to perf event")
874        return res
875
876    def attach_perf_event(self, ev_type=-1, ev_config=-1, fn_name=b"",
877            sample_period=0, sample_freq=0, pid=-1, cpu=-1, group_fd=-1):
878        fn_name = _assert_is_bytes(fn_name)
879        fn = self.load_func(fn_name, BPF.PERF_EVENT)
880        res = {}
881        if cpu >= 0:
882            res[cpu] = self._attach_perf_event(fn.fd, ev_type, ev_config,
883                    sample_period, sample_freq, pid, cpu, group_fd)
884        else:
885            for i in get_online_cpus():
886                res[i] = self._attach_perf_event(fn.fd, ev_type, ev_config,
887                        sample_period, sample_freq, pid, i, group_fd)
888        self.open_perf_events[(ev_type, ev_config)] = res
889
890    def detach_perf_event(self, ev_type=-1, ev_config=-1):
891        try:
892            fds = self.open_perf_events[(ev_type, ev_config)]
893        except KeyError:
894            raise Exception("Perf event type {} config {} not attached".format(
895                ev_type, ev_config))
896
897        res = 0
898        for fd in fds.values():
899            res = lib.bpf_close_perf_event_fd(fd) or res
900        if res != 0:
901            raise Exception("Failed to detach BPF from perf event")
902        del self.open_perf_events[(ev_type, ev_config)]
903
904    @staticmethod
905    def get_user_functions(name, sym_re):
906        return set([name for (name, _) in
907                    BPF.get_user_functions_and_addresses(name, sym_re)])
908
909    @staticmethod
910    def get_user_addresses(name, sym_re):
911        """
912        We are returning addresses here instead of symbol names because it
913        turns out that the same name may appear multiple times with different
914        addresses, and the same address may appear multiple times with the same
915        name. We can't attach a uprobe to the same address more than once, so
916        it makes sense to return the unique set of addresses that are mapped to
917        a symbol that matches the provided regular expression.
918        """
919        return set([address for (_, address) in
920                    BPF.get_user_functions_and_addresses(name, sym_re)])
921
922    @staticmethod
923    def get_user_functions_and_addresses(name, sym_re):
924        name = _assert_is_bytes(name)
925        sym_re = _assert_is_bytes(sym_re)
926        addresses = []
927        def sym_cb(sym_name, addr):
928            dname = sym_name
929            if re.match(sym_re, dname):
930                addresses.append((dname, addr))
931            return 0
932
933        res = lib.bcc_foreach_function_symbol(name, _SYM_CB_TYPE(sym_cb))
934        if res < 0:
935            raise Exception("Error %d enumerating symbols in %s" % (res, name))
936        return addresses
937
938    def _get_uprobe_evname(self, prefix, path, addr, pid):
939        if pid == -1:
940            return b"%s_%s_0x%x" % (prefix, self._probe_repl.sub(b"_", path), addr)
941        else:
942            # if pid is valid, put pid in the name, so different pid
943            # can have different event names
944            return b"%s_%s_0x%x_%d" % (prefix, self._probe_repl.sub(b"_", path), addr, pid)
945
946    def attach_uprobe(self, name=b"", sym=b"", sym_re=b"", addr=None,
947            fn_name=b"", pid=-1):
948        """attach_uprobe(name="", sym="", sym_re="", addr=None, fn_name=""
949                         pid=-1)
950
951        Run the bpf function denoted by fn_name every time the symbol sym in
952        the library or binary 'name' is encountered. The real address addr may
953        be supplied in place of sym. Optional parameters pid, cpu, and group_fd
954        can be used to filter the probe.
955
956        Instead of a symbol name, a regular expression can be provided in
957        sym_re. The uprobe will then attach to symbols that match the provided
958        regular expression.
959
960        Libraries can be given in the name argument without the lib prefix, or
961        with the full path (/usr/lib/...). Binaries can be given only with the
962        full path (/bin/sh). If a PID is given, the uprobe will attach to the
963        version of the library used by the process.
964
965        Example: BPF(text).attach_uprobe("c", "malloc")
966                 BPF(text).attach_uprobe("/usr/bin/python", "main")
967        """
968
969        name = _assert_is_bytes(name)
970        sym = _assert_is_bytes(sym)
971        sym_re = _assert_is_bytes(sym_re)
972        fn_name = _assert_is_bytes(fn_name)
973
974        if sym_re:
975            addresses = BPF.get_user_addresses(name, sym_re)
976            self._check_probe_quota(len(addresses))
977            for sym_addr in addresses:
978                self.attach_uprobe(name=name, addr=sym_addr,
979                                   fn_name=fn_name, pid=pid)
980            return
981
982        (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
983
984        self._check_probe_quota(1)
985        fn = self.load_func(fn_name, BPF.KPROBE)
986        ev_name = self._get_uprobe_evname(b"p", path, addr, pid)
987        fd = lib.bpf_attach_uprobe(fn.fd, 0, ev_name, path, addr, pid)
988        if fd < 0:
989            raise Exception("Failed to attach BPF to uprobe")
990        self._add_uprobe_fd(ev_name, fd)
991        return self
992
993    def attach_uretprobe(self, name=b"", sym=b"", sym_re=b"", addr=None,
994            fn_name=b"", pid=-1):
995        """attach_uretprobe(name="", sym="", sym_re="", addr=None, fn_name=""
996                            pid=-1)
997
998        Run the bpf function denoted by fn_name every time the symbol sym in
999        the library or binary 'name' finishes execution. See attach_uprobe for
1000        meaning of additional parameters.
1001        """
1002
1003        name = _assert_is_bytes(name)
1004        sym = _assert_is_bytes(sym)
1005        sym_re = _assert_is_bytes(sym_re)
1006        fn_name = _assert_is_bytes(fn_name)
1007
1008        if sym_re:
1009            for sym_addr in BPF.get_user_addresses(name, sym_re):
1010                self.attach_uretprobe(name=name, addr=sym_addr,
1011                                      fn_name=fn_name, pid=pid)
1012            return
1013
1014        (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
1015
1016        self._check_probe_quota(1)
1017        fn = self.load_func(fn_name, BPF.KPROBE)
1018        ev_name = self._get_uprobe_evname(b"r", path, addr, pid)
1019        fd = lib.bpf_attach_uprobe(fn.fd, 1, ev_name, path, addr, pid)
1020        if fd < 0:
1021            raise Exception("Failed to attach BPF to uretprobe")
1022        self._add_uprobe_fd(ev_name, fd)
1023        return self
1024
1025    def detach_uprobe_event(self, ev_name):
1026        if ev_name not in self.uprobe_fds:
1027            raise Exception("Uprobe %s is not attached" % ev_name)
1028        res = lib.bpf_close_perf_event_fd(self.uprobe_fds[ev_name])
1029        if res < 0:
1030            raise Exception("Failed to detach BPF from uprobe")
1031        res = lib.bpf_detach_uprobe(ev_name)
1032        if res < 0:
1033            raise Exception("Failed to detach BPF from uprobe")
1034        self._del_uprobe_fd(ev_name)
1035
1036    def detach_uprobe(self, name=b"", sym=b"", addr=None, pid=-1):
1037        """detach_uprobe(name="", sym="", addr=None, pid=-1)
1038
1039        Stop running a bpf function that is attached to symbol 'sym' in library
1040        or binary 'name'.
1041        """
1042
1043        name = _assert_is_bytes(name)
1044        sym = _assert_is_bytes(sym)
1045        (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
1046        ev_name = self._get_uprobe_evname(b"p", path, addr, pid)
1047        self.detach_uprobe_event(ev_name)
1048
1049    def detach_uretprobe(self, name=b"", sym=b"", addr=None, pid=-1):
1050        """detach_uretprobe(name="", sym="", addr=None, pid=-1)
1051
1052        Stop running a bpf function that is attached to symbol 'sym' in library
1053        or binary 'name'.
1054        """
1055
1056        name = _assert_is_bytes(name)
1057        sym = _assert_is_bytes(sym)
1058
1059        (path, addr) = BPF._check_path_symbol(name, sym, addr, pid)
1060        ev_name = self._get_uprobe_evname(b"r", path, addr, pid)
1061        self.detach_uprobe_event(ev_name)
1062
1063    def _trace_autoload(self):
1064        for i in range(0, lib.bpf_num_functions(self.module)):
1065            func_name = lib.bpf_function_name(self.module, i)
1066            if func_name.startswith(b"kprobe__"):
1067                fn = self.load_func(func_name, BPF.KPROBE)
1068                self.attach_kprobe(
1069                    event=self.fix_syscall_fnname(func_name[8:]),
1070                    fn_name=fn.name)
1071            elif func_name.startswith(b"kretprobe__"):
1072                fn = self.load_func(func_name, BPF.KPROBE)
1073                self.attach_kretprobe(
1074                    event=self.fix_syscall_fnname(func_name[11:]),
1075                    fn_name=fn.name)
1076            elif func_name.startswith(b"tracepoint__"):
1077                fn = self.load_func(func_name, BPF.TRACEPOINT)
1078                tp = fn.name[len(b"tracepoint__"):].replace(b"__", b":")
1079                self.attach_tracepoint(tp=tp, fn_name=fn.name)
1080            elif func_name.startswith(b"raw_tracepoint__"):
1081                fn = self.load_func(func_name, BPF.RAW_TRACEPOINT)
1082                tp = fn.name[len(b"raw_tracepoint__"):]
1083                self.attach_raw_tracepoint(tp=tp, fn_name=fn.name)
1084
1085    def trace_open(self, nonblocking=False):
1086        """trace_open(nonblocking=False)
1087
1088        Open the trace_pipe if not already open
1089        """
1090        if not self.tracefile:
1091            self.tracefile = open("%s/trace_pipe" % TRACEFS, "rb")
1092            if nonblocking:
1093                fd = self.tracefile.fileno()
1094                fl = fcntl.fcntl(fd, fcntl.F_GETFL)
1095                fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
1096        return self.tracefile
1097
1098    def trace_fields(self, nonblocking=False):
1099        """trace_fields(nonblocking=False)
1100
1101        Read from the kernel debug trace pipe and return a tuple of the
1102        fields (task, pid, cpu, flags, timestamp, msg) or None if no
1103        line was read (nonblocking=True)
1104        """
1105        while True:
1106            line = self.trace_readline(nonblocking)
1107            if not line and nonblocking: return (None,) * 6
1108            # don't print messages related to lost events
1109            if line.startswith(b"CPU:"): continue
1110            task = line[:16].lstrip()
1111            line = line[17:]
1112            ts_end = line.find(b":")
1113            pid, cpu, flags, ts = line[:ts_end].split()
1114            cpu = cpu[1:-1]
1115            # line[ts_end:] will have ": [sym_or_addr]: msgs"
1116            # For trace_pipe debug output, the addr typically
1117            # is invalid (e.g., 0x1). For kernel 4.12 or earlier,
1118            # if address is not able to match a kernel symbol,
1119            # nothing will be printed out. For kernel 4.13 and later,
1120            # however, the illegal address will be printed out.
1121            # Hence, both cases are handled here.
1122            line = line[ts_end + 1:]
1123            sym_end = line.find(b":")
1124            msg = line[sym_end + 2:]
1125            return (task, int(pid), int(cpu), flags, float(ts), msg)
1126
1127    def trace_readline(self, nonblocking=False):
1128        """trace_readline(nonblocking=False)
1129
1130        Read from the kernel debug trace pipe and return one line
1131        If nonblocking is False, this will block until ctrl-C is pressed.
1132        """
1133
1134        trace = self.trace_open(nonblocking)
1135
1136        line = None
1137        try:
1138            line = trace.readline(1024).rstrip()
1139        except IOError:
1140            pass
1141        return line
1142
1143    def trace_print(self, fmt=None):
1144        """trace_print(self, fmt=None)
1145
1146        Read from the kernel debug trace pipe and print on stdout.
1147        If fmt is specified, apply as a format string to the output. See
1148        trace_fields for the members of the tuple
1149        example: trace_print(fmt="pid {1}, msg = {5}")
1150        """
1151
1152        while True:
1153            if fmt:
1154                fields = self.trace_fields(nonblocking=False)
1155                if not fields: continue
1156                line = fmt.format(*fields)
1157            else:
1158                line = self.trace_readline(nonblocking=False)
1159            print(line)
1160            sys.stdout.flush()
1161
1162    @staticmethod
1163    def _sym_cache(pid):
1164        """_sym_cache(pid)
1165
1166        Returns a symbol cache for the specified PID.
1167        The kernel symbol cache is accessed by providing any PID less than zero.
1168        """
1169        if pid < 0 and pid != -1:
1170            pid = -1
1171        if not pid in BPF._sym_caches:
1172            BPF._sym_caches[pid] = SymbolCache(pid)
1173        return BPF._sym_caches[pid]
1174
1175    @staticmethod
1176    def sym(addr, pid, show_module=False, show_offset=False, demangle=True):
1177        """sym(addr, pid, show_module=False, show_offset=False)
1178
1179        Translate a memory address into a function name for a pid, which is
1180        returned. When show_module is True, the module name is also included.
1181        When show_offset is True, the instruction offset as a hexadecimal
1182        number is also included in the string.
1183
1184        A pid of less than zero will access the kernel symbol cache.
1185
1186        Example output when both show_module and show_offset are True:
1187            "start_thread+0x202 [libpthread-2.24.so]"
1188
1189        Example output when both show_module and show_offset are False:
1190            "start_thread"
1191        """
1192        name, offset, module = BPF._sym_cache(pid).resolve(addr, demangle)
1193        offset = b"+0x%x" % offset if show_offset and name is not None else b""
1194        name = name or b"[unknown]"
1195        name = name + offset
1196        module = b" [%s]" % os.path.basename(module) \
1197            if show_module and module is not None else b""
1198        return name + module
1199
1200    @staticmethod
1201    def ksym(addr, show_module=False, show_offset=False):
1202        """ksym(addr)
1203
1204        Translate a kernel memory address into a kernel function name, which is
1205        returned. When show_module is True, the module name ("kernel") is also
1206        included. When show_offset is true, the instruction offset as a
1207        hexadecimal number is also included in the string.
1208
1209        Example output when both show_module and show_offset are True:
1210            "default_idle+0x0 [kernel]"
1211        """
1212        return BPF.sym(addr, -1, show_module, show_offset, False)
1213
1214    @staticmethod
1215    def ksymname(name):
1216        """ksymname(name)
1217
1218        Translate a kernel name into an address. This is the reverse of
1219        ksym. Returns -1 when the function name is unknown."""
1220        return BPF._sym_cache(-1).resolve_name(None, name)
1221
1222    def num_open_kprobes(self):
1223        """num_open_kprobes()
1224
1225        Get the number of open K[ret]probes. Can be useful for scenarios where
1226        event_re is used while attaching and detaching probes.
1227        """
1228        return len(self.kprobe_fds)
1229
1230    def num_open_uprobes(self):
1231        """num_open_uprobes()
1232
1233        Get the number of open U[ret]probes.
1234        """
1235        return len(self.uprobe_fds)
1236
1237    def num_open_tracepoints(self):
1238        """num_open_tracepoints()
1239
1240        Get the number of open tracepoints.
1241        """
1242        return len(self.tracepoint_fds)
1243
1244    def perf_buffer_poll(self, timeout = -1):
1245        """perf_buffer_poll(self)
1246
1247        Poll from all open perf ring buffers, calling the callback that was
1248        provided when calling open_perf_buffer for each entry.
1249        """
1250        readers = (ct.c_void_p * len(self.perf_buffers))()
1251        for i, v in enumerate(self.perf_buffers.values()):
1252            readers[i] = v
1253        lib.perf_reader_poll(len(readers), readers, timeout)
1254
1255    def kprobe_poll(self, timeout = -1):
1256        """kprobe_poll(self)
1257
1258        Deprecated. Use perf_buffer_poll instead.
1259        """
1260        self.perf_buffer_poll(timeout)
1261
1262    def free_bcc_memory(self):
1263        return lib.bcc_free_memory()
1264
1265    def donothing(self):
1266        """the do nothing exit handler"""
1267
1268    def cleanup(self):
1269        # Clean up opened probes
1270        for k, v in list(self.kprobe_fds.items()):
1271            self.detach_kprobe_event(k)
1272        for k, v in list(self.uprobe_fds.items()):
1273            self.detach_uprobe_event(k)
1274        for k, v in list(self.tracepoint_fds.items()):
1275            self.detach_tracepoint(k)
1276        for k, v in list(self.raw_tracepoint_fds.items()):
1277            self.detach_raw_tracepoint(k)
1278
1279        # Clean up opened perf ring buffer and perf events
1280        table_keys = list(self.tables.keys())
1281        for key in table_keys:
1282            if isinstance(self.tables[key], PerfEventArray):
1283                del self.tables[key]
1284        for (ev_type, ev_config) in list(self.open_perf_events.keys()):
1285            self.detach_perf_event(ev_type, ev_config)
1286        if self.tracefile:
1287            self.tracefile.close()
1288            self.tracefile = None
1289        if self.module:
1290            lib.bpf_module_destroy(self.module)
1291            self.module = None
1292
1293    def __enter__(self):
1294        return self
1295
1296    def __exit__(self, exc_type, exc_val, exc_tb):
1297        self.cleanup()
1298
1299
1300from .usdt import USDT, USDTException
1301