Improve PerfEventArray clean up
authorTeng Qin <qinteng@fb.com>
Thu, 25 May 2017 20:30:13 +0000 (13:30 -0700)
committerBrenden Blanco <bblanco@gmail.com>
Thu, 1 Jun 2017 16:25:11 +0000 (09:25 -0700)
src/python/bcc/__init__.py
src/python/bcc/table.py

index 269afa2d634307416692510b3e0807e82c1e530f..7a86b482134735180d12c3da3e0b5556798d3c8a 100644 (file)
@@ -25,7 +25,7 @@ import sys
 basestring = (unicode if sys.version_info[0] < 3 else str)
 
 from .libbcc import lib, _CB_TYPE, bcc_symbol, bcc_symbol_option, _SYM_CB_TYPE
-from .table import Table
+from .table import Table, PerfEventArray
 from .perf import Perf
 from .utils import get_online_cpus
 
@@ -1089,11 +1089,16 @@ class BPF(object):
 
     def cleanup(self):
         for k, v in list(self.open_kprobes.items()):
-            lib.perf_reader_free(v)
             # non-string keys here include the perf_events reader
             if isinstance(k, str):
+                lib.perf_reader_free(v)
                 lib.bpf_detach_kprobe(str(k).encode("ascii"))
-            self._del_kprobe(k)
+                self._del_kprobe(k)
+        # clean up opened perf ring buffer and perf events
+        table_keys = list(self.tables.keys())
+        for key in table_keys:
+            if isinstance(self.tables[key], PerfEventArray):
+                del self.tables[key]
         for k, v in list(self.open_uprobes.items()):
             lib.perf_reader_free(v)
             lib.bpf_detach_uprobe(str(k).encode("ascii"))
index b541ef510253fd057db016bd187c4dfdc788acd4..0fb6f2f40beae2c60e09868845ba74c0b31fac8d 100644 (file)
@@ -471,10 +471,30 @@ class PerfEventArray(ArrayBase):
 
     def __init__(self, *args, **kwargs):
         super(PerfEventArray, self).__init__(*args, **kwargs)
+        self._open_key_fds = {}
+
+    def __del__(self):
+        keys = list(self._open_key_fds.keys())
+        for key in keys:
+            del self[key]
 
     def __delitem__(self, key):
-        super(PerfEventArray, self).__delitem__(key)
-        self.close_perf_buffer(key)
+        if key not in self._open_key_fds:
+            return
+        # Delete entry from the array
+        c_key = self._normalize_key(key)
+        key_p = ct.pointer(c_key)
+        lib.bpf_delete_elem(self.map_fd, ct.cast(key_p, ct.c_void_p))
+        key_id = (id(self), key)
+        if key_id in self.bpf.open_kprobes:
+            # The key is opened for perf ring buffer
+            lib.perf_reader_free(self.bpf.open_kprobes[key_id])
+            self.bpf._del_kprobe(key_id)
+            del self._cbs[key]
+        else:
+            # The key is opened for perf event read
+            lib.bpf_close_perf_event_fd(self._open_key_fds[key])
+        del self._open_key_fds[key]
 
     def open_perf_buffer(self, callback, page_cnt=8, lost_cb=None):
         """open_perf_buffers(callback)
@@ -503,29 +523,21 @@ class PerfEventArray(ArrayBase):
         self.bpf._add_kprobe((id(self), cpu), reader)
         # keep a refcnt
         self._cbs[cpu] = (fn, lost_fn)
-
-    def close_perf_buffer(self, key):
-        reader = self.bpf.open_kprobes.get((id(self), key))
-        if reader:
-            lib.perf_reader_free(reader)
-            self.bpf._del_kprobe((id(self), key))
-        del self._cbs[key]
+        # The actual fd is held by the perf reader, add to track opened keys
+        self._open_key_fds[cpu] = -1
 
     def _open_perf_event(self, cpu, typ, config):
         fd = lib.bpf_open_perf_event(typ, config, -1, cpu)
         if fd < 0:
             raise Exception("bpf_open_perf_event failed")
-        try:
-            self[self.Key(cpu)] = self.Leaf(fd)
-        finally:
-            # the fd is kept open in the map itself by the kernel
-            os.close(fd)
+        self[self.Key(cpu)] = self.Leaf(fd)
+        self._open_key_fds[cpu] = fd
 
     def open_perf_event(self, typ, config):
         """open_perf_event(typ, config)
 
         Configures the table such that calls from the bpf program to
-        table.perf_read(bpf_get_smp_processor_id()) will return the hardware
+        table.perf_read(CUR_CPU_IDENTIFIER) will return the hardware
         counter denoted by event ev on the local cpu.
         """
         for i in get_online_cpus():