blob: 7ed3e1b9d778a89d05854fc05ea3125712d814b5 [file] [log] [blame]
Brendan Gregg74016c32015-09-21 15:49:21 -07001#!/usr/bin/python
2#
3# funclatency Time kernel funcitons and print latency as a histogram.
4# For Linux, uses BCC, eBPF.
5#
6# USAGE: funclatency [-h] [-p PID] [-i INTERVAL] [-T] [-u] [-m] [-r] pattern
7#
8# Run "funclatency -h" for full usage.
9#
10# The pattern is a string with optional '*' wildcards, similar to file globbing.
11# If you'd prefer to use regular expressions, use the -r option. Matching
12# multiple functions is of limited use, since the output has one histogram for
13# everything. Future versions should split the output histogram by the function.
14#
15# Copyright (c) 2015 Brendan Gregg.
16# Licensed under the Apache License, Version 2.0 (the "License")
17#
18# 20-Sep-2015 Brendan Gregg Created this.
19
20from __future__ import print_function
21from bcc import BPF
22from time import sleep, strftime
23import argparse
24import signal
25
26# arguments
27examples = """examples:
28 ./funclatency do_sys_open # time the do_sys_open() kenel function
29 ./funclatency -u vfs_read # time vfs_read(), in microseconds
30 ./funclatency -m do_nanosleep # time do_nanosleep(), in milliseconds
31 ./funclatency -mTi 5 vfs_read # output every 5 seconds, with timestamps
32 ./funclatency -p 181 vfs_read # time process 181 only
33 ./funclatency 'vfs_fstat*' # time both vfs_fstat() and vfs_fstatat()
34"""
35parser = argparse.ArgumentParser(
36 description="Time kernel funcitons and print latency as a histogram",
37 formatter_class=argparse.RawDescriptionHelpFormatter,
38 epilog=examples)
39parser.add_argument("-p", "--pid",
40 help="trace this PID only")
41parser.add_argument("-i", "--interval", default=99999999,
42 help="summary interval, seconds")
43parser.add_argument("-T", "--timestamp", action="store_true",
44 help="include timestamp on output")
45parser.add_argument("-u", "--microseconds", action="store_true",
46 help="microsecond histogram")
47parser.add_argument("-m", "--milliseconds", action="store_true",
48 help="millisecond histogram")
49parser.add_argument("-r", "--regexp", action="store_true",
50 help="use regular expressions. Default is \"*\" wildcards only.")
51parser.add_argument("pattern",
52 help="search expression for kernel functions")
53args = parser.parse_args()
54pattern = args.pattern
55if not args.regexp:
56 pattern = pattern.replace('*', '.*')
57 pattern = '^' + pattern + '$'
58debug = 0
59
60# define BPF program
61bpf_text = """
62#include <uapi/linux/ptrace.h>
63#include <linux/blkdev.h>
64
Brendan Gregg74016c32015-09-21 15:49:21 -070065BPF_HASH(start, u32);
Brendan Gregg8d70a882015-09-25 11:07:23 -070066BPF_HISTOGRAM(dist);
Brendan Gregg74016c32015-09-21 15:49:21 -070067
68int trace_func_entry(struct pt_regs *ctx)
69{
70 u32 pid = bpf_get_current_pid_tgid();
71 u64 ts = bpf_ktime_get_ns();
72
73 FILTER
74 start.update(&pid, &ts);
75
76 return 0;
77}
78
79int trace_func_return(struct pt_regs *ctx)
80{
81 u64 *tsp, delta;
82 u32 pid = bpf_get_current_pid_tgid();
83
84 // calculate delta time
85 tsp = start.lookup(&pid);
86 if (tsp == 0) {
87 return 0; // missed start
88 }
89 start.delete(&pid);
90 delta = bpf_ktime_get_ns() - *tsp;
91 FACTOR
92
93 // store as histogram
Brendan Gregg8d70a882015-09-25 11:07:23 -070094 dist.increment(bpf_log2l(delta));
Brendan Gregg74016c32015-09-21 15:49:21 -070095
96 return 0;
97}
98"""
99if args.pid:
100 bpf_text = bpf_text.replace('FILTER',
101 'if (pid != %s) { return 0; }' % args.pid)
102else:
103 bpf_text = bpf_text.replace('FILTER', '')
104if args.milliseconds:
105 bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;')
106 label = "msecs"
107elif args.microseconds:
108 bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;')
109 label = "usecs"
110else:
111 bpf_text = bpf_text.replace('FACTOR', '')
112 label = "nsecs"
113if debug:
114 print(bpf_text)
115
116# signal handler
117def signal_ignore(signal, frame):
118 print()
119
120# load BPF program
121b = BPF(text=bpf_text)
122b.attach_kprobe(event_re=pattern, fn_name="trace_func_entry")
123b.attach_kretprobe(event_re=pattern, fn_name="trace_func_return")
Brendan Gregg6b8add02015-09-25 11:16:33 -0700124matched = b.num_open_kprobes()
125if matched == 0:
126 print("0 functions matched by \"%s\". Exiting." % args.pattern)
127 exit()
Brendan Gregg74016c32015-09-21 15:49:21 -0700128
129# header
Brendan Gregg6b8add02015-09-25 11:16:33 -0700130print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." %
131 (matched / 2, args.pattern))
Brendan Gregg74016c32015-09-21 15:49:21 -0700132
133# output
134exiting = 0 if args.interval else 1
135dist = b.get_table("dist")
136while (1):
137 try:
138 sleep(int(args.interval))
139 except KeyboardInterrupt:
140 exiting=1
141 # as cleanup can take many seconds, trap Ctrl-C:
142 signal.signal(signal.SIGINT, signal_ignore)
143
144 print()
145 if args.timestamp:
146 print("%-8s\n" % strftime("%H:%M:%S"), end="")
147
148 dist.print_log2_hist(label)
149 dist.clear()
150
151 if exiting:
152 print("Detaching...")
153 exit()