forked from iovisor/bcc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
funccount.py
executable file
·131 lines (118 loc) · 3.89 KB
/
funccount.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/python
# @lint-avoid-python-3-compatibility-imports
#
# funccount Count kernel function calls.
# For Linux, uses BCC, eBPF. See .c file.
#
# USAGE: funccount [-h] [-p PID] [-i INTERVAL] [-T] [-r] pattern
#
# The pattern is a string with optional '*' wildcards, similar to file globbing.
# If you'd prefer to use regular expressions, use the -r option.
#
# Copyright (c) 2015 Brendan Gregg.
# Licensed under the Apache License, Version 2.0 (the "License")
#
# 09-Sep-2015 Brendan Gregg Created this.
from __future__ import print_function
from bcc import BPF
from time import sleep, strftime
import argparse
import signal
import os
# arguments
examples = """examples:
./funccount 'vfs_*' # count kernel functions starting with "vfs"
./funccount 'tcp_send*' # count kernel funcs starting with "tcp_send"
./funccount -r '^vfs.*' # same as above, using regular expressions
./funccount -Ti 5 'vfs_*' # output every 5 seconds, with timestamps
./funccount -p 185 'vfs_*' # count vfs calls for PID 181 only
"""
parser = argparse.ArgumentParser(
description="Count kernel function calls",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=examples)
parser.add_argument("-p", "--pid",
help="trace this PID only")
parser.add_argument("-i", "--interval", default=99999999,
help="summary interval, seconds")
parser.add_argument("-T", "--timestamp", action="store_true",
help="include timestamp on output")
parser.add_argument("-r", "--regexp", action="store_true",
help="use regular expressions. Default is \"*\" wildcards only.")
parser.add_argument("pattern",
help="search expression for kernel functions")
args = parser.parse_args()
pattern = args.pattern
if not args.regexp:
pattern = pattern.replace('*', '.*')
pattern = '^' + pattern + '$'
debug = 0
# signal handler
def signal_ignore(signal, frame):
print()
# load BPF program
bpf_text = """
#include <uapi/linux/ptrace.h>
struct key_t {
u64 ip;
};
BPF_HASH(counts, struct key_t);
int trace_count(struct pt_regs *ctx) {
FILTER
struct key_t key = {};
u64 *val;
// the kprobe pc is slightly after the function starting address, align
// back to the start (4 byte alignment) in order to match /proc/kallsyms
key.ip = PT_REGS_IP(ctx) & ~3ull;
val = counts.lookup(&key);
if (!val)
return 0;
(*val)++;
return 0;
}
"""
filter_text = "u32 pid; pid = bpf_get_current_pid_tgid(); "
if args.pid:
filter_text += "if (pid != %s) { return 0; }" % args.pid
else:
filter_text += "if (pid == %s) { return 0; }" % os.getpid()
bpf_text = bpf_text.replace('FILTER', filter_text)
if debug:
print(bpf_text)
b = BPF(text=bpf_text)
counts = b.get_table("counts")
# pre-insert the function addresses into the counts table
fns = b._get_kprobe_functions(pattern)
for fn in fns:
addr = b.ksymname(fn)
if addr == -1:
raise Exception("Unknown symbol name %s" % fn)
counts[counts.Key(addr)] = counts.Leaf()
b.attach_kprobe(event_re=pattern, fn_name="trace_count")
matched = b.num_open_kprobes()
if matched == 0:
print("0 functions matched by \"%s\". Exiting." % args.pattern)
exit()
# header
print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." %
(matched, args.pattern))
# output
exiting = 0 if args.interval else 1
while (1):
try:
sleep(int(args.interval))
except KeyboardInterrupt:
exiting = 1
# as cleanup can take many seconds, trap Ctrl-C:
signal.signal(signal.SIGINT, signal_ignore)
print()
if args.timestamp:
print("%-8s\n" % strftime("%H:%M:%S"), end="")
print("%-16s %-26s %8s" % ("ADDR", "FUNC", "COUNT"))
for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
if v.value == 0: continue
print("%-16x %-26s %8d" % (k.ip, b.ksym(k.ip), v.value))
counts.zero()
if exiting:
print("Detaching...")
exit()