1#!/usr/bin/env python
2#
3# bpflist   Display processes currently using BPF programs and maps,
4#           pinned BPF programs and maps, and enabled probes.
5#
6# USAGE: bpflist [-v]
7#
8# Idea by Brendan Gregg.
9#
10# Copyright 2017, Sasha Goldshtein
11# Licensed under the Apache License, Version 2.0
12#
13# 09-Mar-2017   Sasha Goldshtein   Created this.
14
15from bcc import BPF, USDT
16import argparse
17import re
18import os
19import subprocess
20
21examples = """examples:
22    bpflist     # display all processes currently using BPF
23    bpflist -v  # also count kprobes/uprobes
24    bpflist -vv # display kprobes/uprobes and count them
25"""
26parser = argparse.ArgumentParser(
27    description="Display processes currently using BPF programs and maps",
28    formatter_class=argparse.RawDescriptionHelpFormatter,
29    epilog=examples)
30parser.add_argument("-v", "--verbosity", action="count", default=0,
31    help="count and display kprobes/uprobes as well")
32args = parser.parse_args()
33
34def comm_for_pid(pid):
35    try:
36        return open("/proc/%d/comm" % pid).read().strip()
37    except:
38        return "[unknown]"
39
40counts = {}
41
42def parse_probes(typ):
43    if args.verbosity > 1:
44        print("open %ss:" % typ)
45    for probe in open("/sys/kernel/debug/tracing/%s_events" % typ):
46        # Probes opened by bcc have a specific pattern that includes the pid
47        # of the requesting process.
48        match = re.search('_bcc_(\\d+)\\s', probe)
49        if match:
50            pid = int(match.group(1))
51            counts[(pid, typ)] = counts.get((pid, typ), 0) + 1
52        if args.verbosity > 1:
53            print(probe.strip())
54    if args.verbosity > 1:
55        print("")
56
57if args.verbosity > 0:
58    parse_probes("kprobe")
59    parse_probes("uprobe")
60
61def find_bpf_fds(pid):
62    root = '/proc/%d/fd' % pid
63    for fd in os.listdir(root):
64        try:
65            link = os.readlink(os.path.join(root, fd))
66        except OSError:
67            continue
68        match = re.match('.*bpf-(\\w+)', link)
69        if match:
70            tup = (pid, match.group(1))
71            counts[tup] = counts.get(tup, 0) + 1
72
73for pdir in os.listdir('/proc'):
74    if re.match('\\d+', pdir):
75        try:
76            find_bpf_fds(int(pdir))
77        except OSError:
78            continue
79print("%-6s %-16s %-8s %s" % ("PID", "COMM", "TYPE", "COUNT"))
80for (pid, typ), count in sorted(counts.items(), key=lambda t: t[0][0]):
81    comm = comm_for_pid(pid)
82    print("%-6d %-16s %-8s %-4d" % (pid, comm, typ, count))
83