From c08c431ae1e373316ef2f9d534503e19719f7b28 Mon Sep 17 00:00:00 2001 From: Sasha Goldshtein Date: Mon, 21 Mar 2016 03:52:09 -0700 Subject: [PATCH] Moved common tracepoint support into tracepoint.py --- src/python/bcc/__init__.py | 1 + src/python/bcc/tracepoint.py | 183 +++++++++++++++++++++++++++++++++++++++++++ tools/argdist.py | 167 +-------------------------------------- tools/trace.py | 168 ++------------------------------------- 4 files changed, 193 insertions(+), 326 deletions(-) create mode 100644 src/python/bcc/tracepoint.py diff --git a/src/python/bcc/__init__.py b/src/python/bcc/__init__.py index baeec0e..452997d 100644 --- a/src/python/bcc/__init__.py +++ b/src/python/bcc/__init__.py @@ -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 index 0000000..24ef402 --- /dev/null +++ b/src/python/bcc/tracepoint.py @@ -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") + diff --git a/tools/argdist.py b/tools/argdist.py index a92c975..738a717 100755 --- a/tools/argdist.py +++ b/tools/argdist.py @@ -12,132 +12,14 @@ # 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) diff --git a/tools/trace.py b/tools/trace.py index 8f1ce89..b159510 100755 --- a/tools/trace.py +++ b/tools/trace.py @@ -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) -- 2.7.4