# 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 os from .utils import get_online_cpus class _sample_period_union(ct.Union): _fields_ = [ ('sample_period', ct.c_ulong), ('sample_freq', ct.c_ulong), ] class _wakeup_events_union(ct.Union): _fields_ = [ ('wakeup_events', ct.c_uint), ('wakeup_watermark', ct.c_uint), ] class _bp_addr_union(ct.Union): _fields_ = [ ('bp_addr', ct.c_ulong), ('kprobe_func', ct.c_ulong), ('uprobe_path', ct.c_ulong), ('config1', ct.c_ulong), ] class _bp_len_union(ct.Union): _fields_ = [ ('bp_len', ct.c_ulong), ('kprobe_addr', ct.c_ulong), ('probe_offset', ct.c_ulong), ('config2', ct.c_ulong), ] class Perf(object): class perf_event_attr(ct.Structure): _anonymous_ = [ "_sample_period_union", "_wakeup_events_union", "_bp_addr_union", "_bp_len_union" ] _fields_ = [ ('type', ct.c_uint), ('size', ct.c_uint), ('config', ct.c_ulong), ('_sample_period_union', _sample_period_union), # ct.c_ulong ('sample_type', ct.c_ulong), ('read_format', ct.c_ulong), ('disabled', ct.c_uint, 1), ('inherit', ct.c_uint, 1), ('pinned', ct.c_uint, 1), ('exclusive', ct.c_uint, 1), ('exclude_user', ct.c_uint, 1), ('exclude_kernel', ct.c_uint, 1), ('exclude_hv', ct.c_uint, 1), ('exclude_idle', ct.c_uint, 1), ('mmap', ct.c_uint, 1), ('comm', ct.c_uint, 1), ('freq', ct.c_uint, 1), ('inherit_stat', ct.c_uint, 1), ('enable_on_exec', ct.c_uint, 1), ('task', ct.c_uint, 1), ('watermark', ct.c_uint, 1), ('precise_ip', ct.c_uint, 2), ('mmap_data', ct.c_uint, 1), ('sample_id_all', ct.c_uint, 1), ('exclude_host', ct.c_uint, 1), ('exclude_guest', ct.c_uint, 1), ('exclude_callchain_kernel', ct.c_uint, 1), ('exclude_callchain_user', ct.c_uint, 1), ('mmap2', ct.c_uint, 1), ('comm_exec', ct.c_uint, 1), ('use_clockid', ct.c_uint, 1), ('context_switch', ct.c_uint, 1), ('write_backward', ct.c_uint, 1), ('namespaces', ct.c_uint, 1), ('ksymbol', ct.c_uint, 1), ('bpf_event', ct.c_uint, 1), ('aux_output', ct.c_uint, 1), ('cgroup', ct.c_uint, 1), ('text_poke', ct.c_uint, 1), ('__reserved_1', ct.c_uint, 30), ('_wakeup_events_union', _wakeup_events_union), # ct.c_uint ('bp_type', ct.c_uint), ('_bp_addr_union', _bp_addr_union), # ct.c_ulong ('_bp_len_union', _bp_len_union), # ct.c_ulong ('branch_sample_type', ct.c_ulong), ('sample_regs_user', ct.c_ulong), ('sample_stack_user', ct.c_uint), ('clockid', ct.c_int), ('sample_regs_intr', ct.c_ulong), ('aux_watermark', ct.c_uint), ('sample_max_stack', ct.c_uint16), ('__reserved_2', ct.c_uint16), ('aux_sample_size', ct.c_uint), ('__reserved_3', ct.c_uint), ] def __init__(self): self.size = 120 # PERF_ATTR_SIZE_VER6 self.ctype_fields = [item[0] for item in self._fields_] self.ctype_fields.extend([item[0] for item in _sample_period_union._fields_]) self.ctype_fields.extend([item[0] for item in _wakeup_events_union._fields_]) self.ctype_fields.extend([item[0] for item in _bp_addr_union._fields_]) self.ctype_fields.extend([item[0] for item in _bp_len_union._fields_]) def __setattr__(self, key, value): if hasattr(self, 'ctype_fields') and key not in self.ctype_fields: print("Warning: Setting field {} on perf_event_attr that isn't part of the ctype - {} won't make it to perf_event_open".format(key, key)) super(Perf.perf_event_attr, self).__setattr__(key, value) # x86 specific, from arch/x86/include/generated/uapi/asm/unistd_64.h NR_PERF_EVENT_OPEN = 298 # # Selected constants from include/uapi/linux/perf_event.h. # Values copied during Linux 4.7 series. # # perf_type_id PERF_TYPE_HARDWARE = 0 PERF_TYPE_SOFTWARE = 1 PERF_TYPE_TRACEPOINT = 2 PERF_TYPE_HW_CACHE = 3 # perf_event_sample_format PERF_SAMPLE_RAW = 1024 # it's a u32; could also try zero args # perf_event.h PERF_FLAG_FD_CLOEXEC = 8 PERF_EVENT_IOC_SET_FILTER = 1074275334 PERF_EVENT_IOC_ENABLE = 9216 # fetch syscall routines 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), attr.pid, cpu, -1, Perf.PERF_FLAG_FD_CLOEXEC) if pfd < 0: errno_ = ct.get_errno() raise OSError(errno_, os.strerror(errno_)) if attr.type == Perf.PERF_TYPE_TRACEPOINT: if Perf.ioctl(pfd, Perf.PERF_EVENT_IOC_SET_FILTER, "common_pid == -17") < 0: errno_ = ct.get_errno() raise OSError(errno_, os.strerror(errno_)) # we don't setup the perf ring buffers, as we won't read them 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, pid=-1, ptype=PERF_TYPE_TRACEPOINT, freq=0): attr = Perf.perf_event_attr() attr.config = tpoint_id attr.pid = pid attr.type = ptype attr.sample_type = Perf.PERF_SAMPLE_RAW if freq > 0: # setup sampling attr.freq = 1 # no mmap or comm attr.sample_period = freq else: attr.sample_period = 1 attr.wakeup_events = 9999999 # don't wake up for cpu in get_online_cpus(): Perf._open_for_cpu(cpu, attr)