Moved common tracepoint support into tracepoint.py
authorSasha Goldshtein <goldshtn@gmail.com>
Mon, 21 Mar 2016 10:52:09 +0000 (03:52 -0700)
committerSasha Goldshtein <goldshtn@gmail.com>
Mon, 21 Mar 2016 10:52:09 +0000 (03:52 -0700)
src/python/bcc/__init__.py
src/python/bcc/tracepoint.py [new file with mode: 0644]
tools/argdist.py
tools/trace.py

index baeec0e..452997d 100644 (file)
@@ -27,6 +27,7 @@ basestring = (unicode if sys.version_info[0] < 3 else str)
 
 from .libbcc import lib, _CB_TYPE
 from .table import Table
+from .tracepoint import Perf, Tracepoint
 
 open_kprobes = {}
 open_uprobes = {}
diff --git a/src/python/bcc/tracepoint.py b/src/python/bcc/tracepoint.py
new file mode 100644 (file)
index 0000000..24ef402
--- /dev/null
@@ -0,0 +1,183 @@
+# Copyright 2016 Sasha Goldshtein
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import ctypes as ct
+import multiprocessing
+import os
+import re
+
+class Perf(object):
+        class perf_event_attr(ct.Structure):
+                _fields_ = [
+                        ('type', ct.c_uint),
+                        ('size', ct.c_uint),
+                        ('config', ct.c_ulong),
+                        ('sample_period', ct.c_ulong),
+                        ('sample_type', ct.c_ulong),
+                        ('IGNORE1', ct.c_ulong),
+                        ('IGNORE2', ct.c_ulong),
+                        ('wakeup_events', ct.c_uint),
+                        ('IGNORE3', ct.c_uint),
+                        ('IGNORE4', ct.c_ulong),
+                        ('IGNORE5', ct.c_ulong),
+                        ('IGNORE6', ct.c_ulong),
+                        ('IGNORE7', ct.c_uint),
+                        ('IGNORE8', ct.c_int),
+                        ('IGNORE9', ct.c_ulong),
+                        ('IGNORE10', ct.c_uint),
+                        ('IGNORE11', ct.c_uint)
+                ]
+
+        NR_PERF_EVENT_OPEN = 298
+        PERF_TYPE_TRACEPOINT = 2
+        PERF_SAMPLE_RAW = 1024
+        PERF_FLAG_FD_CLOEXEC = 8
+        PERF_EVENT_IOC_SET_FILTER = 1074275334
+        PERF_EVENT_IOC_ENABLE = 9216
+
+        libc = ct.CDLL('libc.so.6', use_errno=True)
+        syscall = libc.syscall          # not declaring vararg types
+        ioctl = libc.ioctl              # not declaring vararg types
+
+        @staticmethod
+        def _open_for_cpu(cpu, attr):
+                pfd = Perf.syscall(Perf.NR_PERF_EVENT_OPEN, ct.byref(attr),
+                                   -1, cpu, -1, Perf.PERF_FLAG_FD_CLOEXEC)
+                if pfd < 0:
+                        errno_ = ct.get_errno()
+                        raise OSError(errno_, os.strerror(errno_))
+                if Perf.ioctl(pfd, Perf.PERF_EVENT_IOC_SET_FILTER,
+                              "common_pid == -17") < 0:
+                        errno_ = ct.get_errno()
+                        raise OSError(errno_, os.strerror(errno_))
+                if Perf.ioctl(pfd, Perf.PERF_EVENT_IOC_ENABLE, 0) < 0:
+                        errno_ = ct.get_errno()
+                        raise OSError(errno_, os.strerror(errno_))
+
+        @staticmethod
+        def perf_event_open(tpoint_id):
+                attr = Perf.perf_event_attr()
+                attr.config = tpoint_id
+                attr.type = Perf.PERF_TYPE_TRACEPOINT
+                attr.sample_type = Perf.PERF_SAMPLE_RAW
+                attr.sample_period = 1
+                attr.wakeup_events = 1
+                for cpu in range(0, multiprocessing.cpu_count()):
+                        Perf._open_for_cpu(cpu, attr)
+
+class Tracepoint(object):
+        enabled_tracepoints = []
+        trace_root = "/sys/kernel/debug/tracing"
+        event_root = os.path.join(trace_root, "events")
+
+        @classmethod
+        def _any_tracepoints_enabled(cls):
+                return len(cls.enabled_tracepoints) > 0
+
+        @classmethod
+        def generate_decl(cls):
+                if not cls._any_tracepoints_enabled():
+                        return ""
+                return "\nBPF_HASH(__trace_di, u64, u64);\n"
+
+        @classmethod
+        def generate_entry_probe(cls):
+                if not cls._any_tracepoints_enabled():
+                        return ""
+                return """
+int __trace_entry_update(struct pt_regs *ctx)
+{
+        u64 tid = bpf_get_current_pid_tgid();
+        u64 val = ctx->di;
+        __trace_di.update(&tid, &val);
+        return 0;
+}
+"""
+
+        def __init__(self, category, event, tp_id):
+                self.category = category
+                self.event = event
+                self.tp_id = tp_id
+
+        def _generate_struct_fields(self):
+                format_lines = Tracepoint.get_tpoint_format(self.category,
+                                                            self.event)
+                text = ""
+                for line in format_lines:
+                        match = re.search(r'field:([^;]*);.*size:(\d+);', line)
+                        if match is None:
+                                continue
+                        parts = match.group(1).split()
+                        field_name = parts[-1:][0]
+                        field_type = " ".join(parts[:-1])
+                        field_size = int(match.group(2))
+                        if "__data_loc" in field_type:
+                                continue
+                        if field_name.startswith("common_"):
+                                continue
+                        text += "        %s %s;\n" % (field_type, field_name)
+                return text
+
+        def generate_struct(self):
+                self.struct_name = self.event + "_trace_entry"
+                return """
+struct %s {
+        u64 __do_not_use__;
+        %s
+};
+                """ % (self.struct_name, self._generate_struct_fields())
+
+        def generate_get_struct(self):
+                return """
+        u64 tid = bpf_get_current_pid_tgid();
+        u64 *di = __trace_di.lookup(&tid);
+        if (di == 0) { return 0; }
+        struct %s tp = {};
+        bpf_probe_read(&tp, sizeof(tp), (void *)*di);
+                """ % self.struct_name
+
+        @classmethod
+        def enable_tracepoint(cls, category, event):
+                tp_id = cls.get_tpoint_id(category, event)
+                if tp_id == -1:
+                        raise ValueError("no such tracepoint found: %s:%s" %
+                                         (category, event))
+                Perf.perf_event_open(tp_id)
+                new_tp = Tracepoint(category, event, tp_id)
+                cls.enabled_tracepoints.append(new_tp)
+                return new_tp
+
+        @staticmethod
+        def get_tpoint_id(category, event):
+                evt_dir = os.path.join(Tracepoint.event_root, category, event)
+                try:
+                        return int(
+                          open(os.path.join(evt_dir, "id")).read().strip())
+                except:
+                        return -1
+
+        @staticmethod
+        def get_tpoint_format(category, event):
+                evt_dir = os.path.join(Tracepoint.event_root, category, event)
+                try:
+                        return open(os.path.join(evt_dir, "format")).readlines()
+                except:
+                        return ""
+
+        @classmethod
+        def attach(cls, bpf):
+                if cls._any_tracepoints_enabled():
+                        bpf.attach_kprobe(event="tracing_generic_entry_update",
+                                          fn_name="__trace_entry_update")
+
index a92c975..738a717 100755 (executable)
 # Licensed under the Apache License, Version 2.0 (the "License")
 # Copyright (C) 2016 Sasha Goldshtein.
 
-from bcc import BPF
+from bcc import BPF, Tracepoint, Perf
 from time import sleep, strftime
 import argparse
-import ctypes as ct
 import re
 import traceback
 import os
-import multiprocessing
 import sys
 
-class Perf(object):
-        class perf_event_attr(ct.Structure):
-                _fields_ = [
-                        ('type', ct.c_uint),
-                        ('size', ct.c_uint),
-                        ('config', ct.c_ulong),
-                        ('sample_period', ct.c_ulong),
-                        ('sample_type', ct.c_ulong),
-                        ('IGNORE1', ct.c_ulong),
-                        ('IGNORE2', ct.c_ulong),
-                        ('wakeup_events', ct.c_uint),
-                        ('IGNORE3', ct.c_uint),
-                        ('IGNORE4', ct.c_ulong),
-                        ('IGNORE5', ct.c_ulong),
-                        ('IGNORE6', ct.c_ulong),
-                        ('IGNORE7', ct.c_uint),
-                        ('IGNORE8', ct.c_int),
-                        ('IGNORE9', ct.c_ulong),
-                        ('IGNORE10', ct.c_uint),
-                        ('IGNORE11', ct.c_uint)
-                ]
-
-        NR_PERF_EVENT_OPEN = 298
-        PERF_TYPE_TRACEPOINT = 2
-        PERF_SAMPLE_RAW = 1024
-        PERF_FLAG_FD_CLOEXEC = 8
-        PERF_EVENT_IOC_SET_FILTER = 1074275334
-        PERF_EVENT_IOC_ENABLE = 9216
-
-        libc = ct.CDLL('libc.so.6', use_errno=True)
-        syscall = libc.syscall          # not declaring vararg types
-        ioctl = libc.ioctl              # not declaring vararg types
-
-        @staticmethod
-        def _open_for_cpu(cpu, attr):
-                pfd = Perf.syscall(Perf.NR_PERF_EVENT_OPEN, ct.byref(attr),
-                                   -1, cpu, -1, Perf.PERF_FLAG_FD_CLOEXEC)
-                if pfd < 0:
-                        errno_ = ct.get_errno()
-                        raise OSError(errno_, os.strerror(errno_))
-                if Perf.ioctl(pfd, Perf.PERF_EVENT_IOC_SET_FILTER,
-                              "common_pid == -17") < 0:
-                        errno_ = ct.get_errno()
-                        raise OSError(errno_, os.strerror(errno_))
-                if Perf.ioctl(pfd, Perf.PERF_EVENT_IOC_ENABLE, 0) < 0:
-                        errno_ = ct.get_errno()
-                        raise OSError(errno_, os.strerror(errno_))
-
-        @staticmethod
-        def perf_event_open(tpoint_id):
-                attr = Perf.perf_event_attr()
-                attr.config = tpoint_id
-                attr.type = Perf.PERF_TYPE_TRACEPOINT
-                attr.sample_type = Perf.PERF_SAMPLE_RAW
-                attr.sample_period = 1
-                attr.wakeup_events = 1
-                for cpu in range(0, multiprocessing.cpu_count()):
-                        Perf._open_for_cpu(cpu, attr)
-
-class Tracepoint(object):
-        tracepoints_enabled = 0
-        trace_root = "/sys/kernel/debug/tracing"
-        event_root = os.path.join(trace_root, "events")
-
-        @staticmethod
-        def generate_decl():
-                if Tracepoint.tracepoints_enabled == 0:
-                        return ""
-                return "\nBPF_HASH(__trace_di, u64, u64);\n"
-
-        @staticmethod
-        def generate_entry_probe():
-                if Tracepoint.tracepoints_enabled == 0:
-                        return ""
-                return """
-int __trace_entry_update(struct pt_regs *ctx)
-{
-        u64 tid = bpf_get_current_pid_tgid();
-        u64 val = ctx->di;
-        __trace_di.update(&tid, &val);
-        return 0;
-}
-"""
-
-        @staticmethod
-        def enable_tracepoint(category, event):
-                tp_id = Tracepoint.get_tpoint_id(category, event)
-                if tp_id == -1:
-                        raise ValueError("no such tracepoint found: %s:%s" %
-                                         (category, event))
-                Perf.perf_event_open(tp_id)
-                Tracepoint.tracepoints_enabled += 1
-
-        @staticmethod
-        def get_tpoint_id(category, event):
-                evt_dir = os.path.join(Tracepoint.event_root, category, event)
-                try:
-                        return int(
-                          open(os.path.join(evt_dir, "id")).read().strip())
-                except:
-                        return -1
-
-        @staticmethod
-        def get_tpoint_format(category, event):
-                evt_dir = os.path.join(Tracepoint.event_root, category, event)
-                try:
-                        return open(os.path.join(evt_dir, "format")).readlines()
-                except:
-                        return ""
-
-        @staticmethod
-        def attach(bpf):
-                if Tracepoint.tracepoints_enabled > 0:
-                        bpf.attach_kprobe(event="tracing_generic_entry_update",
-                                          fn_name="__trace_entry_update")
-
 class Specifier(object):
         probe_text = """
 DATA_DECL
@@ -343,7 +225,7 @@ u64 __time = bpf_ktime_get_ns();
                         self.library = ""       # kernel
                         self.tp_category = parts[1]
                         self.tp_event = self.function
-                        Tracepoint.enable_tracepoint(
+                        self.tp = Tracepoint.enable_tracepoint(
                                         self.tp_category, self.tp_event)
                         self.function = "perf_trace_" + self.function
                 else:
@@ -452,47 +334,6 @@ u64 __time = bpf_ktime_get_ns();
                 else:
                         return ""
 
-        def _generate_tpoint_entry_struct_fields(self):
-                format_lines = Tracepoint.get_tpoint_format(self.tp_category,
-                                                            self.tp_event)
-                text = ""
-                for line in format_lines:
-                        match = re.search(r'field:([^;]*);.*size:(\d+);', line)
-                        if match is None:
-                                continue
-                        parts = match.group(1).split()
-                        field_name = parts[-1:][0]
-                        field_type = " ".join(parts[:-1])
-                        field_size = int(match.group(2))
-                        if "__data_loc" in field_type:
-                                continue
-                        if field_name.startswith("common_"):
-                                continue
-                        text += "        %s %s;\n" % (field_type, field_name)
-                return text
-
-        def _generate_tpoint_entry_struct(self):
-                text = """
-struct %s {
-        u64 __do_not_use__;
-%s
-};
-                """
-                self.tp_entry_struct_name = self.probe_func_name + \
-                                            "_trace_entry"
-                fields = self._generate_tpoint_entry_struct_fields()
-                return text % (self.tp_entry_struct_name, fields)
-
-        def _generate_tpoint_entry_prefix(self):
-                text = """
-        u64 tid = bpf_get_current_pid_tgid();
-        u64 *di = __trace_di.lookup(&tid);
-        if (di == 0) { return 0; }
-        struct %s tp = {};
-        bpf_probe_read(&tp, sizeof(tp), (void *)*di);
-                """ % self.tp_entry_struct_name
-                return text
-
         def generate_text(self):
                 program = ""
 
@@ -510,8 +351,8 @@ struct %s {
                 # that enables access to the tracepoint structure and also
                 # the structure definition itself
                 if self.probe_type == "t":
-                        program += self._generate_tpoint_entry_struct()
-                        prefix += self._generate_tpoint_entry_prefix()
+                        program += self.tp.generate_struct()
+                        prefix += self.tp.generate_get_struct()
 
                 program += self.probe_text.replace("PROBENAME",
                                                    self.probe_func_name)
index 8f1ce89..b159510 100755 (executable)
@@ -9,12 +9,11 @@
 # Licensed under the Apache License, Version 2.0 (the "License")
 # Copyright (C) 2016 Sasha Goldshtein.
 
-from bcc import BPF
+from bcc import BPF, Tracepoint, Perf
 from time import sleep, strftime
 import argparse
 import re
 import ctypes as ct
-import multiprocessing
 import os
 import traceback
 import sys
@@ -44,122 +43,6 @@ class Time(object):
                         raise OSError(errno_, os.strerror(errno_))
                 return t.tv_sec * 1e9 + t.tv_nsec
 
-class Perf(object):
-        class perf_event_attr(ct.Structure):
-                _fields_ = [
-                        ('type', ct.c_uint),
-                        ('size', ct.c_uint),
-                        ('config', ct.c_ulong),
-                        ('sample_period', ct.c_ulong),
-                        ('sample_type', ct.c_ulong),
-                        ('IGNORE1', ct.c_ulong),
-                        ('IGNORE2', ct.c_ulong),
-                        ('wakeup_events', ct.c_uint),
-                        ('IGNORE3', ct.c_uint),
-                        ('IGNORE4', ct.c_ulong),
-                        ('IGNORE5', ct.c_ulong),
-                        ('IGNORE6', ct.c_ulong),
-                        ('IGNORE7', ct.c_uint),
-                        ('IGNORE8', ct.c_int),
-                        ('IGNORE9', ct.c_ulong),
-                        ('IGNORE10', ct.c_uint),
-                        ('IGNORE11', ct.c_uint)
-                ]
-
-        NR_PERF_EVENT_OPEN = 298
-        PERF_TYPE_TRACEPOINT = 2
-        PERF_SAMPLE_RAW = 1024
-        PERF_FLAG_FD_CLOEXEC = 8
-        PERF_EVENT_IOC_SET_FILTER = 1074275334
-        PERF_EVENT_IOC_ENABLE = 9216
-
-        libc = ct.CDLL('libc.so.6', use_errno=True)
-        syscall = libc.syscall          # not declaring vararg types
-        ioctl = libc.ioctl              # not declaring vararg types
-
-        @staticmethod
-        def _open_for_cpu(cpu, attr):
-                pfd = Perf.syscall(Perf.NR_PERF_EVENT_OPEN, ct.byref(attr),
-                                   -1, cpu, -1, Perf.PERF_FLAG_FD_CLOEXEC)
-                if pfd < 0:
-                        errno_ = ct.get_errno()
-                        raise OSError(errno_, os.strerror(errno_))
-                if Perf.ioctl(pfd, Perf.PERF_EVENT_IOC_SET_FILTER,
-                              "common_pid == -17") < 0:
-                        errno_ = ct.get_errno()
-                        raise OSError(errno_, os.strerror(errno_))
-                if Perf.ioctl(pfd, Perf.PERF_EVENT_IOC_ENABLE, 0) < 0:
-                        errno_ = ct.get_errno()
-                        raise OSError(errno_, os.strerror(errno_))
-
-        @staticmethod
-        def perf_event_open(tpoint_id):
-                attr = Perf.perf_event_attr()
-                attr.config = tpoint_id
-                attr.type = Perf.PERF_TYPE_TRACEPOINT
-                attr.sample_type = Perf.PERF_SAMPLE_RAW
-                attr.sample_period = 1
-                attr.wakeup_events = 1
-                for cpu in range(0, multiprocessing.cpu_count()):
-                        Perf._open_for_cpu(cpu, attr)
-
-class Tracepoint(object):
-        tracepoints_enabled = 0
-        trace_root = "/sys/kernel/debug/tracing"
-        event_root = os.path.join(trace_root, "events")
-
-        @staticmethod
-        def generate_decl():
-                if Tracepoint.tracepoints_enabled == 0:
-                        return ""
-                return "\nBPF_HASH(__trace_di, u64, u64);\n"
-
-        @staticmethod
-        def generate_entry_probe():
-                if Tracepoint.tracepoints_enabled == 0:
-                        return ""
-                return """
-int __trace_entry_update(struct pt_regs *ctx)
-{
-        u64 tid = bpf_get_current_pid_tgid();
-        u64 val = ctx->di;
-        __trace_di.update(&tid, &val);
-        return 0;
-}
-"""
-
-        @staticmethod
-        def enable_tracepoint(category, event):
-                tp_id = Tracepoint.get_tpoint_id(category, event)
-                if tp_id == -1:
-                        raise ValueError("no such tracepoint found: %s:%s" %
-                                         (category, event))
-                Perf.perf_event_open(tp_id)
-                Tracepoint.tracepoints_enabled += 1
-
-        @staticmethod
-        def get_tpoint_id(category, event):
-                evt_dir = os.path.join(Tracepoint.event_root, category, event)
-                try:
-                        return int(
-                          open(os.path.join(evt_dir, "id")).read().strip())
-                except:
-                        return -1
-
-        @staticmethod
-        def get_tpoint_format(category, event):
-                evt_dir = os.path.join(Tracepoint.event_root, category, event)
-                try:
-                        return open(os.path.join(evt_dir, "format")).readlines()
-                except:
-                        return ""
-
-        @staticmethod
-        def attach(bpf):
-                if Tracepoint.tracepoints_enabled > 0:
-                        bpf.attach_kprobe(event="tracing_generic_entry_update",
-                                          fn_name="__trace_entry_update")
-
 class Probe(object):
         probe_count = 0
         max_events = None
@@ -190,47 +73,6 @@ class Probe(object):
         def is_default_action(self):
                 return self.python_format == ""
 
-        def _generate_tpoint_entry_struct_fields(self):
-                format_lines = Tracepoint.get_tpoint_format(self.tp_category,
-                                                            self.tp_event)
-                text = ""
-                for line in format_lines:
-                        match = re.search(r'field:([^;]*);.*size:(\d+);', line)
-                        if match is None:
-                                continue
-                        parts = match.group(1).split()
-                        field_name = parts[-1:][0]
-                        field_type = " ".join(parts[:-1])
-                        field_size = int(match.group(2))
-                        if "__data_loc" in field_type:
-                                continue
-                        if field_name.startswith("common_"):
-                                continue
-                        text += "        %s %s;\n" % (field_type, field_name)
-                return text
-
-        def _generate_tpoint_entry_struct(self):
-                text = """
-struct %s {
-        u64 __do_not_use__;
-%s
-};
-                """
-                self.tp_entry_struct_name = self.probe_name + \
-                                            "_trace_entry"
-                fields = self._generate_tpoint_entry_struct_fields()
-                return text % (self.tp_entry_struct_name, fields)
-
-        def _generate_tpoint_entry_prefix(self):
-                text = """
-        u64 tid = bpf_get_current_pid_tgid();
-        u64 *di = __trace_di.lookup(&tid);
-        if (di == 0) { return 0; }
-        struct %s tp = {};
-        bpf_probe_read(&tp, sizeof(tp), (void *)*di);
-                """ % self.tp_entry_struct_name
-                return text
-
         def _bail(self, error):
                 raise ValueError("error parsing probe '%s': %s" %
                                  (self.raw_probe, error))
@@ -290,8 +132,8 @@ struct %s {
                 if self.probe_type == "t":
                         self.tp_category = parts[1]
                         self.tp_event = parts[2]
-                        Tracepoint.enable_tracepoint(self.tp_category,
-                                                     self.tp_event)
+                        self.tp = Tracepoint.enable_tracepoint(
+                                        self.tp_category, self.tp_event)
                         self.library = ""       # kernel
                         self.function = "perf_trace_%s" % self.tp_event
                 else:
@@ -457,8 +299,8 @@ BPF_PERF_OUTPUT(%s);
 
                 prefix = ""
                 if self.probe_type == "t":
-                        data_decl += self._generate_tpoint_entry_struct()
-                        prefix = self._generate_tpoint_entry_prefix()
+                        data_decl += self.tp.generate_struct()
+                        prefix = self.tp.generate_get_struct()
 
                 text = """
 int %s(struct pt_regs *ctx)