Commit c08c431a authored by Sasha Goldshtein's avatar Sasha Goldshtein

Moved common tracepoint support into tracepoint.py

parent fd60d55c
......@@ -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 = {}
......
# 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")
......@@ -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)
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment