-
Notifications
You must be signed in to change notification settings - Fork 0
/
funclat.py
94 lines (83 loc) · 3.2 KB
/
funclat.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
#!/usr/bin/python
#
# funclat Display a cummulative histogram of functions' latencies.
#
# USAGE: funclat [-v] [-p PID [PID ...]] [-m THRESHOLD] [-u]
# [-i INTERVAL] PATH_TO_LIBRARY FUNC1,FUNC2,...
#
# Written as a basic example of getting cummulative latency of functions in a program
from bcc import BPF
import argparse
from time import sleep, strftime
from ctypes import c_int
examples = """
dbstat /usr/local/pgsql/bin/postgres LockRelationForExtension # display a histogram of PostgreSQL function latencies
dbstat /usr/local/pgsql/bin/postgres LockRelationForExtension -v # display latencies and print the BPF program
dbstat /usr/local/pgsql/bin/postgres LockRelationForExtension -u # display latencies in microseconds (default: ms)
dbstat /usr/local/pgsql/bin/postgres LockRelationForExtension -m 5 # trace only latencies slower than 5ms
dbstat /usr/local/pgsql/bin/postgres LockRelationForExtension -i 1 # trace latencies and print every 1 second
"""
parser = argparse.ArgumentParser(
description="",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=examples)
parser.add_argument("-v", "--verbose", action="store_true",
help="print the BPF program")
parser.add_argument("-m", "--threshold", type=int, default=0,
help="trace queries slower than this threshold (ms)")
parser.add_argument("-u", "--microseconds", action="store_true",
help="display functions' latencies in microseconds (default: milliseconds)")
parser.add_argument("path",
help="path to binary")
parser.add_argument("func",
help="path to binary")
parser.add_argument("-i", "--interval", type=int, default=99999999999,
help="print summary at this interval (seconds)")
args = parser.parse_args()
program = """
#include <uapi/linux/ptrace.h>
BPF_HASH(temp, u64, u64);
BPF_HISTOGRAM(latency);
int probe_start(struct pt_regs *ctx) {
u64 timestamp = bpf_ktime_get_ns();
u64 pid = bpf_get_current_pid_tgid();
temp.update(&pid, ×tamp);
return 0;
}
int probe_end(struct pt_regs *ctx) {
u64 *timestampp;
u64 pid = bpf_get_current_pid_tgid();
timestampp = temp.lookup(&pid);
if (!timestampp)
return 0;
u64 delta = bpf_ktime_get_ns() - *timestampp;
FILTER
delta /= SCALE;
latency.increment(bpf_log2l(delta));
temp.delete(&pid);
return 0;
}
"""
program = program.replace("SCALE", str(1000 if args.microseconds else 1000000))
program = program.replace("FILTER", "" if args.threshold == 0 else
"if (delta / 1000000 < %d) { return 0; }" % args.threshold)
bpf = BPF(text=program)
flist = args.func
for function in flist.split(","):
bpf.attach_uprobe(name=args.path, sym=function, fn_name="probe_start")
bpf.attach_uretprobe(name=args.path, sym=function, fn_name="probe_end")
print("Tracing latencies slower than %d ms..." % args.threshold)
latencies = bpf["latency"]
def print_hist():
print("[%s]" % strftime("%H:%M:%S"))
latencies.print_log2_hist("functions' latency (%s)" %
("us" if args.microseconds else "ms"))
print("")
latencies.clear()
while True:
try:
sleep(args.interval)
print_hist()
except KeyboardInterrupt:
print_hist()
break