Commit b755c709 authored by Brenden Blanco's avatar Brenden Blanco Committed by GitHub

Merge pull request #614 from markdrayton/fix-auto-kprobe

Fix probe detaching and auto-kprobes
parents a1333bcd cb679d7b
...@@ -20,7 +20,6 @@ import json ...@@ -20,7 +20,6 @@ import json
import multiprocessing import multiprocessing
import os import os
import re import re
from subprocess import Popen, PIPE, STDOUT
import struct import struct
import sys import sys
basestring = (unicode if sys.version_info[0] < 3 else str) basestring = (unicode if sys.version_info[0] < 3 else str)
...@@ -31,45 +30,20 @@ from .table import Table ...@@ -31,45 +30,20 @@ from .table import Table
from .tracepoint import Perf, Tracepoint from .tracepoint import Perf, Tracepoint
from .usyms import ProcessSymbols from .usyms import ProcessSymbols
open_kprobes = {}
open_uprobes = {}
open_tracepoints = {}
tracefile = None
TRACEFS = "/sys/kernel/debug/tracing"
_kprobe_limit = 1000 _kprobe_limit = 1000
_num_open_probes = 0
# for tests
def _get_num_open_probes():
global _num_open_probes
return _num_open_probes
TRACEFS = "/sys/kernel/debug/tracing"
DEBUG_LLVM_IR = 0x1 DEBUG_LLVM_IR = 0x1
DEBUG_BPF = 0x2 DEBUG_BPF = 0x2
DEBUG_PREPROCESSOR = 0x4 DEBUG_PREPROCESSOR = 0x4
@atexit.register
def cleanup_kprobes():
for k, v in open_kprobes.items():
lib.perf_reader_free(v)
if isinstance(k, str):
desc = "-:kprobes/%s" % k
lib.bpf_detach_kprobe(desc.encode("ascii"))
for k, v in open_uprobes.items():
lib.perf_reader_free(v)
if isinstance(k, str):
desc = "-:uprobes/%s" % k
lib.bpf_detach_uprobe(desc.encode("ascii"))
for k, v in open_tracepoints.items():
lib.perf_reader_free(v)
if isinstance(k, str):
(tp_category, tp_name) = k.split(':')
lib.bpf_detach_tracepoint(tp_category, tp_name)
open_kprobes.clear()
open_uprobes.clear()
open_tracepoints.clear()
if tracefile:
tracefile.close()
def _check_probe_quota(num_new_probes):
if len(open_kprobes) + len(open_uprobes) + num_new_probes > _kprobe_limit:
raise Exception("Number of open probes would exceed quota")
class SymbolCache(object): class SymbolCache(object):
def __init__(self, pid): def __init__(self, pid):
self.cache = lib.bcc_symcache_new(pid) self.cache = lib.bcc_symcache_new(pid)
...@@ -159,6 +133,12 @@ class BPF(object): ...@@ -159,6 +133,12 @@ class BPF(object):
DEBUG_PREPROCESSOR: print Preprocessed C file to stderr DEBUG_PREPROCESSOR: print Preprocessed C file to stderr
""" """
self.open_kprobes = {}
self.open_uprobes = {}
self.open_tracepoints = {}
self.tracefile = None
atexit.register(self.cleanup)
self._reader_cb_impl = _CB_TYPE(BPF._reader_cb) self._reader_cb_impl = _CB_TYPE(BPF._reader_cb)
self._user_cb = cb self._user_cb = cb
self.debug = debug self.debug = debug
...@@ -348,24 +328,40 @@ class BPF(object): ...@@ -348,24 +328,40 @@ class BPF(object):
% (dev, errstr)) % (dev, errstr))
fn.sock = sock fn.sock = sock
@staticmethod def _get_kprobe_functions(self, event_re):
def _get_kprobe_functions(event_re): blacklist = set([line.rstrip().split()[1] for line in
p = Popen(["awk", "$1 ~ /%s/ { print $1 }" % event_re, open("%s/../kprobes/blacklist" % TRACEFS)])
"%s/available_filter_functions" % TRACEFS], stdout=PIPE) fns = []
lines = p.communicate()[0].decode().split() with open("%s/available_filter_functions" % TRACEFS) as f:
with open("%s/../kprobes/blacklist" % TRACEFS) as f: for line in f:
blacklist = [line.split()[1] for line in f.readlines()] fn = line.rstrip().split()[0]
fns = [line.rstrip() for line in lines if if re.match(event_re, fn) and fn not in blacklist:
(line != "\n" and line not in blacklist)] fns.append(fn)
_check_probe_quota(len(fns)) self._check_probe_quota(len(fns))
return fns return fns
def _check_probe_quota(self, num_new_probes):
global _num_open_probes
if _num_open_probes + num_new_probes > _kprobe_limit:
raise Exception("Number of open probes would exceed global quota")
def _add_kprobe(self, name, probe):
global _num_open_probes
self.open_kprobes[name] = probe
_num_open_probes += 1
def _del_kprobe(self, name):
global _num_open_probes
del self.open_kprobes[name]
_num_open_probes -= 1
def attach_kprobe(self, event="", fn_name="", event_re="", def attach_kprobe(self, event="", fn_name="", event_re="",
pid=-1, cpu=0, group_fd=-1): pid=-1, cpu=0, group_fd=-1):
assert isinstance(event, str), "event must be a string"
# allow the caller to glob multiple functions together # allow the caller to glob multiple functions together
if event_re: if event_re:
for line in BPF._get_kprobe_functions(event_re): for line in self._get_kprobe_functions(event_re):
try: try:
self.attach_kprobe(event=line, fn_name=fn_name, pid=pid, self.attach_kprobe(event=line, fn_name=fn_name, pid=pid,
cpu=cpu, group_fd=group_fd) cpu=cpu, group_fd=group_fd)
...@@ -373,7 +369,7 @@ class BPF(object): ...@@ -373,7 +369,7 @@ class BPF(object):
pass pass
return return
_check_probe_quota(1) self._check_probe_quota(1)
fn = self.load_func(fn_name, BPF.KPROBE) fn = self.load_func(fn_name, BPF.KPROBE)
ev_name = "p_" + event.replace("+", "_").replace(".", "_") ev_name = "p_" + event.replace("+", "_").replace(".", "_")
desc = "p:kprobes/%s %s" % (ev_name, event) desc = "p:kprobes/%s %s" % (ev_name, event)
...@@ -383,42 +379,28 @@ class BPF(object): ...@@ -383,42 +379,28 @@ class BPF(object):
res = ct.cast(res, ct.c_void_p) res = ct.cast(res, ct.c_void_p)
if not res: if not res:
raise Exception("Failed to attach BPF to kprobe") raise Exception("Failed to attach BPF to kprobe")
open_kprobes[ev_name] = res self._add_kprobe(ev_name, res)
return self return self
@staticmethod def detach_kprobe(self, event):
def open_kprobes(): assert isinstance(event, str), "event must be a string"
global open_kprobes
return open_kprobes
@staticmethod
def open_uprobes():
global open_uprobes
return open_uprobes
@staticmethod
def open_tracepoints():
global open_tracepoints
return open_tracepoints
@staticmethod
def detach_kprobe(event):
ev_name = "p_" + event.replace("+", "_").replace(".", "_") ev_name = "p_" + event.replace("+", "_").replace(".", "_")
if ev_name not in open_kprobes: if ev_name not in self.open_kprobes:
raise Exception("Kprobe %s is not attached" % event) raise Exception("Kprobe %s is not attached" % event)
lib.perf_reader_free(open_kprobes[ev_name]) lib.perf_reader_free(self.open_kprobes[ev_name])
desc = "-:kprobes/%s" % ev_name desc = "-:kprobes/%s" % ev_name
res = lib.bpf_detach_kprobe(desc.encode("ascii")) res = lib.bpf_detach_kprobe(desc.encode("ascii"))
if res < 0: if res < 0:
raise Exception("Failed to detach BPF from kprobe") raise Exception("Failed to detach BPF from kprobe")
del open_kprobes[ev_name] self._del_kprobe(ev_name)
def attach_kretprobe(self, event="", fn_name="", event_re="", def attach_kretprobe(self, event="", fn_name="", event_re="",
pid=-1, cpu=0, group_fd=-1): pid=-1, cpu=0, group_fd=-1):
assert isinstance(event, str), "event must be a string"
# allow the caller to glob multiple functions together # allow the caller to glob multiple functions together
if event_re: if event_re:
for line in BPF._get_kprobe_functions(event_re): for line in self._get_kprobe_functions(event_re):
try: try:
self.attach_kretprobe(event=line, fn_name=fn_name, pid=pid, self.attach_kretprobe(event=line, fn_name=fn_name, pid=pid,
cpu=cpu, group_fd=group_fd) cpu=cpu, group_fd=group_fd)
...@@ -426,7 +408,7 @@ class BPF(object): ...@@ -426,7 +408,7 @@ class BPF(object):
pass pass
return return
_check_probe_quota(1) self._check_probe_quota(1)
fn = self.load_func(fn_name, BPF.KPROBE) fn = self.load_func(fn_name, BPF.KPROBE)
ev_name = "r_" + event.replace("+", "_").replace(".", "_") ev_name = "r_" + event.replace("+", "_").replace(".", "_")
desc = "r:kprobes/%s %s" % (ev_name, event) desc = "r:kprobes/%s %s" % (ev_name, event)
...@@ -436,20 +418,20 @@ class BPF(object): ...@@ -436,20 +418,20 @@ class BPF(object):
res = ct.cast(res, ct.c_void_p) res = ct.cast(res, ct.c_void_p)
if not res: if not res:
raise Exception("Failed to attach BPF to kprobe") raise Exception("Failed to attach BPF to kprobe")
open_kprobes[ev_name] = res self._add_kprobe(ev_name, res)
return self return self
@staticmethod def detach_kretprobe(self, event):
def detach_kretprobe(event): assert isinstance(event, str), "event must be a string"
ev_name = "r_" + event.replace("+", "_").replace(".", "_") ev_name = "r_" + event.replace("+", "_").replace(".", "_")
if ev_name not in open_kprobes: if ev_name not in self.open_kprobes:
raise Exception("Kretprobe %s is not attached" % event) raise Exception("Kretprobe %s is not attached" % event)
lib.perf_reader_free(open_kprobes[ev_name]) lib.perf_reader_free(self.open_kprobes[ev_name])
desc = "-:kprobes/%s" % ev_name desc = "-:kprobes/%s" % ev_name
res = lib.bpf_detach_kprobe(desc.encode("ascii")) res = lib.bpf_detach_kprobe(desc.encode("ascii"))
if res < 0: if res < 0:
raise Exception("Failed to detach BPF from kprobe") raise Exception("Failed to detach BPF from kprobe")
del open_kprobes[ev_name] self._del_kprobe(ev_name)
@classmethod @classmethod
def _check_path_symbol(cls, module, symname, addr): def _check_path_symbol(cls, module, symname, addr):
...@@ -489,7 +471,7 @@ class BPF(object): ...@@ -489,7 +471,7 @@ class BPF(object):
res = ct.cast(res, ct.c_void_p) res = ct.cast(res, ct.c_void_p)
if not res: if not res:
raise Exception("Failed to attach BPF to tracepoint") raise Exception("Failed to attach BPF to tracepoint")
open_tracepoints[tp] = res self.open_tracepoints[tp] = res
return self return self
def detach_tracepoint(self, tp=""): def detach_tracepoint(self, tp=""):
...@@ -501,15 +483,25 @@ class BPF(object): ...@@ -501,15 +483,25 @@ class BPF(object):
Example: bpf.detach_tracepoint("sched:sched_switch") Example: bpf.detach_tracepoint("sched:sched_switch")
""" """
if tp not in open_tracepoints: if tp not in self.open_tracepoints:
raise Exception("Tracepoint %s is not attached" % tp) raise Exception("Tracepoint %s is not attached" % tp)
lib.perf_reader_free(open_tracepoints[tp]) lib.perf_reader_free(self.open_tracepoints[tp])
(tp_category, tp_name) = tp.split(':') (tp_category, tp_name) = tp.split(':')
res = lib.bpf_detach_tracepoint(tp_category.encode("ascii"), res = lib.bpf_detach_tracepoint(tp_category.encode("ascii"),
tp_name.encode("ascii")) tp_name.encode("ascii"))
if res < 0: if res < 0:
raise Exception("Failed to detach BPF from tracepoint") raise Exception("Failed to detach BPF from tracepoint")
del open_tracepoints[tp] del self.open_tracepoints[tp]
def _add_uprobe(self, name, probe):
global _num_open_probes
self.open_uprobes[name] = probe
_num_open_probes += 1
def _del_uprobe(self, name):
global _num_open_probes
del self.open_uprobes[name]
_num_open_probes -= 1
def attach_uprobe(self, name="", sym="", addr=None, def attach_uprobe(self, name="", sym="", addr=None,
fn_name="", pid=-1, cpu=0, group_fd=-1): fn_name="", pid=-1, cpu=0, group_fd=-1):
...@@ -531,7 +523,7 @@ class BPF(object): ...@@ -531,7 +523,7 @@ class BPF(object):
(path, addr) = BPF._check_path_symbol(name, sym, addr) (path, addr) = BPF._check_path_symbol(name, sym, addr)
_check_probe_quota(1) self._check_probe_quota(1)
fn = self.load_func(fn_name, BPF.KPROBE) fn = self.load_func(fn_name, BPF.KPROBE)
ev_name = "p_%s_0x%x" % (self._probe_repl.sub("_", path), addr) ev_name = "p_%s_0x%x" % (self._probe_repl.sub("_", path), addr)
desc = "p:uprobes/%s %s:0x%x" % (ev_name, path, addr) desc = "p:uprobes/%s %s:0x%x" % (ev_name, path, addr)
...@@ -541,11 +533,10 @@ class BPF(object): ...@@ -541,11 +533,10 @@ class BPF(object):
res = ct.cast(res, ct.c_void_p) res = ct.cast(res, ct.c_void_p)
if not res: if not res:
raise Exception("Failed to attach BPF to uprobe") raise Exception("Failed to attach BPF to uprobe")
open_uprobes[ev_name] = res self._add_uprobe(ev_name, res)
return self return self
@classmethod def detach_uprobe(self, name="", sym="", addr=None):
def detach_uprobe(cls, name="", sym="", addr=None):
"""detach_uprobe(name="", sym="", addr=None) """detach_uprobe(name="", sym="", addr=None)
Stop running a bpf function that is attached to symbol 'sym' in library Stop running a bpf function that is attached to symbol 'sym' in library
...@@ -553,15 +544,15 @@ class BPF(object): ...@@ -553,15 +544,15 @@ class BPF(object):
""" """
(path, addr) = BPF._check_path_symbol(name, sym, addr) (path, addr) = BPF._check_path_symbol(name, sym, addr)
ev_name = "p_%s_0x%x" % (cls._probe_repl.sub("_", path), addr) ev_name = "p_%s_0x%x" % (self._probe_repl.sub("_", path), addr)
if ev_name not in open_uprobes: if ev_name not in self.open_uprobes:
raise Exception("Uprobe %s is not attached" % event) raise Exception("Uprobe %s is not attached" % event)
lib.perf_reader_free(open_uprobes[ev_name]) lib.perf_reader_free(self.open_uprobes[ev_name])
desc = "-:uprobes/%s" % ev_name desc = "-:uprobes/%s" % ev_name
res = lib.bpf_detach_uprobe(desc.encode("ascii")) res = lib.bpf_detach_uprobe(desc.encode("ascii"))
if res < 0: if res < 0:
raise Exception("Failed to detach BPF from uprobe") raise Exception("Failed to detach BPF from uprobe")
del open_uprobes[ev_name] self._del_uprobe(ev_name)
def attach_uretprobe(self, name="", sym="", addr=None, def attach_uretprobe(self, name="", sym="", addr=None,
fn_name="", pid=-1, cpu=0, group_fd=-1): fn_name="", pid=-1, cpu=0, group_fd=-1):
...@@ -575,7 +566,7 @@ class BPF(object): ...@@ -575,7 +566,7 @@ class BPF(object):
(path, addr) = BPF._check_path_symbol(name, sym, addr) (path, addr) = BPF._check_path_symbol(name, sym, addr)
_check_probe_quota(1) self._check_probe_quota(1)
fn = self.load_func(fn_name, BPF.KPROBE) fn = self.load_func(fn_name, BPF.KPROBE)
ev_name = "r_%s_0x%x" % (self._probe_repl.sub("_", path), addr) ev_name = "r_%s_0x%x" % (self._probe_repl.sub("_", path), addr)
desc = "r:uprobes/%s %s:0x%x" % (ev_name, path, addr) desc = "r:uprobes/%s %s:0x%x" % (ev_name, path, addr)
...@@ -585,11 +576,10 @@ class BPF(object): ...@@ -585,11 +576,10 @@ class BPF(object):
res = ct.cast(res, ct.c_void_p) res = ct.cast(res, ct.c_void_p)
if not res: if not res:
raise Exception("Failed to attach BPF to uprobe") raise Exception("Failed to attach BPF to uprobe")
open_uprobes[ev_name] = res self._add_uprobe(ev_name, res)
return self return self
@classmethod def detach_uretprobe(self, name="", sym="", addr=None):
def detach_uretprobe(cls, name="", sym="", addr=None):
"""detach_uretprobe(name="", sym="", addr=None) """detach_uretprobe(name="", sym="", addr=None)
Stop running a bpf function that is attached to symbol 'sym' in library Stop running a bpf function that is attached to symbol 'sym' in library
...@@ -597,23 +587,23 @@ class BPF(object): ...@@ -597,23 +587,23 @@ class BPF(object):
""" """
(path, addr) = BPF._check_path_symbol(name, sym, addr) (path, addr) = BPF._check_path_symbol(name, sym, addr)
ev_name = "r_%s_0x%x" % (cls._probe_repl.sub("_", path), addr) ev_name = "r_%s_0x%x" % (self._probe_repl.sub("_", path), addr)
if ev_name not in open_uprobes: if ev_name not in self.open_uprobes:
raise Exception("Kretprobe %s is not attached" % event) raise Exception("Kretprobe %s is not attached" % event)
lib.perf_reader_free(open_uprobes[ev_name]) lib.perf_reader_free(self.open_uprobes[ev_name])
desc = "-:uprobes/%s" % ev_name desc = "-:uprobes/%s" % ev_name
res = lib.bpf_detach_uprobe(desc.encode("ascii")) res = lib.bpf_detach_uprobe(desc.encode("ascii"))
if res < 0: if res < 0:
raise Exception("Failed to detach BPF from uprobe") raise Exception("Failed to detach BPF from uprobe")
del open_uprobes[ev_name] self._del_uprobe(ev_name)
def _trace_autoload(self): def _trace_autoload(self):
for i in range(0, lib.bpf_num_functions(self.module)): for i in range(0, lib.bpf_num_functions(self.module)):
func_name = lib.bpf_function_name(self.module, i).decode() func_name = lib.bpf_function_name(self.module, i)
if len(open_kprobes) == 0 and func_name.startswith("kprobe__"): if func_name.startswith("kprobe__"):
fn = self.load_func(func_name, BPF.KPROBE) fn = self.load_func(func_name, BPF.KPROBE)
self.attach_kprobe(event=fn.name[8:], fn_name=fn.name) self.attach_kprobe(event=fn.name[8:], fn_name=fn.name)
elif len(open_kprobes) == 0 and func_name.startswith("kretprobe__"): elif func_name.startswith("kretprobe__"):
fn = self.load_func(func_name, BPF.KPROBE) fn = self.load_func(func_name, BPF.KPROBE)
self.attach_kretprobe(event=fn.name[11:], fn_name=fn.name) self.attach_kretprobe(event=fn.name[11:], fn_name=fn.name)
elif func_name.startswith("tracepoint__"): elif func_name.startswith("tracepoint__"):
...@@ -626,14 +616,13 @@ class BPF(object): ...@@ -626,14 +616,13 @@ class BPF(object):
Open the trace_pipe if not already open Open the trace_pipe if not already open
""" """
global tracefile if not self.tracefile:
if not tracefile: self.tracefile = open("%s/trace_pipe" % TRACEFS)
tracefile = open("%s/trace_pipe" % TRACEFS)
if nonblocking: if nonblocking:
fd = tracefile.fileno() fd = self.tracefile.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL) fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
return tracefile return self.tracefile
def trace_fields(self, nonblocking=False): def trace_fields(self, nonblocking=False):
"""trace_fields(nonblocking=False) """trace_fields(nonblocking=False)
...@@ -750,14 +739,14 @@ class BPF(object): ...@@ -750,14 +739,14 @@ class BPF(object):
ksymaddr. Returns -1 when the function name is unknown.""" ksymaddr. Returns -1 when the function name is unknown."""
return BPF._sym_cache(-1).resolve_name(name) return BPF._sym_cache(-1).resolve_name(name)
@staticmethod def num_open_kprobes(self):
def num_open_kprobes():
"""num_open_kprobes() """num_open_kprobes()
Get the number of open K[ret]probes. Can be useful for scenarios where Get the number of open K[ret]probes. Can be useful for scenarios where
event_re is used while attaching and detaching probes event_re is used while attaching and detaching probes. Excludes
perf_events readers.
""" """
return len(open_kprobes) return len([k for k in self.open_kprobes.keys() if isinstance(k, str)])
def kprobe_poll(self, timeout = -1): def kprobe_poll(self, timeout = -1):
"""kprobe_poll(self) """kprobe_poll(self)
...@@ -766,12 +755,33 @@ class BPF(object): ...@@ -766,12 +755,33 @@ class BPF(object):
cb() that was given in the BPF constructor for each entry. cb() that was given in the BPF constructor for each entry.
""" """
try: try:
readers = (ct.c_void_p * len(open_kprobes))() readers = (ct.c_void_p * len(self.open_kprobes))()
for i, v in enumerate(open_kprobes.values()): for i, v in enumerate(self.open_kprobes.values()):
readers[i] = v readers[i] = v
lib.perf_reader_poll(len(open_kprobes), readers, timeout) lib.perf_reader_poll(len(self.open_kprobes), readers, timeout)
except KeyboardInterrupt: except KeyboardInterrupt:
exit() exit()
from .usdt import USDTReader def cleanup(self):
for k, v in self.open_kprobes.items():
lib.perf_reader_free(v)
# non-string keys here include the perf_events reader
if isinstance(k, str):
desc = "-:kprobes/%s" % k
lib.bpf_detach_kprobe(desc.encode("ascii"))
self._del_kprobe(k)
for k, v in self.open_uprobes.items():
lib.perf_reader_free(v)
desc = "-:uprobes/%s" % k
lib.bpf_detach_uprobe(desc.encode("ascii"))
self._del_uprobe(k)
for k, v in self.open_tracepoints.items():
lib.perf_reader_free(v)
(tp_category, tp_name) = k.split(':')
lib.bpf_detach_tracepoint(tp_category, tp_name)
self.open_tracepoints.clear()
if self.tracefile:
self.tracefile.close()
from .usdt import USDTReader
...@@ -393,15 +393,15 @@ class PerfEventArray(ArrayBase): ...@@ -393,15 +393,15 @@ class PerfEventArray(ArrayBase):
raise Exception("Could not open perf buffer") raise Exception("Could not open perf buffer")
fd = lib.perf_reader_fd(reader) fd = lib.perf_reader_fd(reader)
self[self.Key(cpu)] = self.Leaf(fd) self[self.Key(cpu)] = self.Leaf(fd)
self.bpf.open_kprobes()[(id(self), cpu)] = reader self.bpf._add_kprobe((id(self), cpu), reader)
# keep a refcnt # keep a refcnt
self._cbs[cpu] = fn self._cbs[cpu] = fn
def close_perf_buffer(self, key): def close_perf_buffer(self, key):
reader = self.bpf.open_kprobes().get((id(self), key)) reader = self.bpf.open_kprobes.get((id(self), key))
if reader: if reader:
lib.perf_reader_free(reader) lib.perf_reader_free(reader)
del(self.bpf.open_kprobes()[(id(self), key)]) self.bpf._del_kprobe((id(self), key))
del self._cbs[key] del self._cbs[key]
class PerCpuHash(HashTable): class PerCpuHash(HashTable):
......
...@@ -31,6 +31,7 @@ int kprobe__htab_map_delete_elem(struct pt_regs *ctx, struct bpf_map *map, u64 * ...@@ -31,6 +31,7 @@ int kprobe__htab_map_delete_elem(struct pt_regs *ctx, struct bpf_map *map, u64 *
try: del b["stub"][c_ulonglong(1 << i)] try: del b["stub"][c_ulonglong(1 << i)]
except: pass except: pass
b["hist1"].print_log2_hist() b["hist1"].print_log2_hist()
b.cleanup()
def test_struct(self): def test_struct(self):
b = BPF(text=""" b = BPF(text="""
...@@ -52,6 +53,7 @@ int kprobe__htab_map_delete_elem(struct pt_regs *ctx, struct bpf_map *map, u64 * ...@@ -52,6 +53,7 @@ int kprobe__htab_map_delete_elem(struct pt_regs *ctx, struct bpf_map *map, u64 *
try: del b["stub2"][c_ulonglong(1 << i)] try: del b["stub2"][c_ulonglong(1 << i)]
except: pass except: pass
b["hist1"].print_log2_hist() b["hist1"].print_log2_hist()
b.cleanup()
def test_chars(self): def test_chars(self):
b = BPF(text=""" b = BPF(text="""
...@@ -68,6 +70,7 @@ int kprobe__finish_task_switch(struct pt_regs *ctx, struct task_struct *prev) { ...@@ -68,6 +70,7 @@ int kprobe__finish_task_switch(struct pt_regs *ctx, struct task_struct *prev) {
""") """)
for i in range(0, 100): time.sleep(0.01) for i in range(0, 100): time.sleep(0.01)
b["hist1"].print_log2_hist() b["hist1"].print_log2_hist()
b.cleanup()
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Copyright (c) Suchakra Sharma <suchakrapani.sharma@polymtl.ca> # Copyright (c) Suchakra Sharma <suchakrapani.sharma@polymtl.ca>
# Licensed under the Apache License, Version 2.0 (the "License") # Licensed under the Apache License, Version 2.0 (the "License")
from bcc import BPF from bcc import BPF, _get_num_open_probes
import os import os
import sys import sys
from unittest import main, TestCase from unittest import main, TestCase
...@@ -25,6 +25,39 @@ class TestKprobeCnt(TestCase): ...@@ -25,6 +25,39 @@ class TestKprobeCnt(TestCase):
open_cnt = self.b.num_open_kprobes() open_cnt = self.b.num_open_kprobes()
self.assertEqual(actual_cnt, open_cnt) self.assertEqual(actual_cnt, open_cnt)
def tearDown(self):
self.b.cleanup()
class TestProbeGlobalCnt(TestCase):
def setUp(self):
self.b1 = BPF(text="""int count(void *ctx) { return 0; }""")
self.b2 = BPF(text="""int count(void *ctx) { return 0; }""")
def test_probe_quota(self):
self.b1.attach_kprobe(event="schedule", fn_name="count")
self.b2.attach_kprobe(event="submit_bio", fn_name="count")
self.assertEqual(1, self.b1.num_open_kprobes())
self.assertEqual(1, self.b2.num_open_kprobes())
self.assertEqual(2, _get_num_open_probes())
self.b1.cleanup()
self.b2.cleanup()
self.assertEqual(0, _get_num_open_probes())
class TestAutoKprobe(TestCase):
def setUp(self):
self.b = BPF(text="""
int kprobe__schedule(void *ctx) { return 0; }
int kretprobe__schedule(void *ctx) { return 0; }
""")
def test_count(self):
self.assertEqual(2, self.b.num_open_kprobes())
def tearDown(self):
self.b.cleanup()
class TestProbeQuota(TestCase): class TestProbeQuota(TestCase):
def setUp(self): def setUp(self):
...@@ -34,6 +67,10 @@ class TestProbeQuota(TestCase): ...@@ -34,6 +67,10 @@ class TestProbeQuota(TestCase):
with self.assertRaises(Exception): with self.assertRaises(Exception):
self.b.attach_kprobe(event_re=".*", fn_name="count") self.b.attach_kprobe(event_re=".*", fn_name="count")
def tearDown(self):
self.b.cleanup()
class TestProbeNotExist(TestCase): class TestProbeNotExist(TestCase):
def setUp(self): def setUp(self):
self.b = BPF(text="""int count(void *ctx) { return 0; }""") self.b = BPF(text="""int count(void *ctx) { return 0; }""")
...@@ -42,6 +79,9 @@ class TestProbeNotExist(TestCase): ...@@ -42,6 +79,9 @@ class TestProbeNotExist(TestCase):
with self.assertRaises(Exception): with self.assertRaises(Exception):
b.attach_kprobe(event="___doesnotexist", fn_name="count") b.attach_kprobe(event="___doesnotexist", fn_name="count")
def tearDown(self):
self.b.cleanup()
if __name__ == "__main__": if __name__ == "__main__":
main() main()
...@@ -640,8 +640,8 @@ struct __string_t { char s[%d]; }; ...@@ -640,8 +640,8 @@ struct __string_t { char s[%d]; };
for probe in self.probes: for probe in self.probes:
probe.attach(self.bpf) probe.attach(self.bpf)
if self.args.verbose: if self.args.verbose:
print("open uprobes: %s" % BPF.open_uprobes()) print("open uprobes: %s" % self.bpf.open_uprobes)
print("open kprobes: %s" % BPF.open_kprobes()) print("open kprobes: %s" % self.bpf.open_kprobes)
def _main_loop(self): def _main_loop(self):
count_so_far = 0 count_so_far = 0
......
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