blob: 66898728b5dc7216d41710a1576021c6d37e2617 [file] [log] [blame]
Brendan Gregg3e55ae22015-09-10 12:11:35 -07001#!/usr/bin/python
2#
3# funccount Count kernel function calls.
4# For Linux, uses BCC, eBPF. See .c file.
5#
6# USAGE: funccount [-h] [-p PID] [-i INTERVAL] [-T] [-r] pattern
7#
8# The pattern is a string with optional '*' wildcards, similar to file globbing.
9# If you'd prefer to use regular expressions, use the -r option.
10#
11# Copyright (c) 2015 Brendan Gregg.
12# Licensed under the Apache License, Version 2.0 (the "License")
13#
14# 09-Sep-2015 Brendan Gregg Created this.
15
16from __future__ import print_function
17from bcc import BPF
18from time import sleep, strftime
19import argparse
Brendan Gregg3e55ae22015-09-10 12:11:35 -070020import signal
21
22# arguments
23examples = """examples:
24 ./funccount 'vfs_*' # count kernel functions starting with "vfs"
25 ./funccount 'tcp_send*' # count kernel funcs starting with "tcp_send"
26 ./funccount -r '^vfs.*' # same as above, using regular expressions
27 ./funccount -Ti 5 'vfs_*' # output every 5 seconds, with timestamps
28 ./funccount -p 185 'vfs_*' # count vfs calls for PID 181 only
29"""
30parser = argparse.ArgumentParser(
31 description="Count kernel function calls",
32 formatter_class=argparse.RawDescriptionHelpFormatter,
33 epilog=examples)
34parser.add_argument("-p", "--pid",
35 help="trace this PID only")
36parser.add_argument("-i", "--interval", default=99999999,
37 help="summary interval, seconds")
38parser.add_argument("-T", "--timestamp", action="store_true",
39 help="include timestamp on output")
40parser.add_argument("-r", "--regexp", action="store_true",
41 help="use regular expressions. Default is \"*\" wildcards only.")
42parser.add_argument("pattern",
43 help="search expression for kernel functions")
44args = parser.parse_args()
45pattern = args.pattern
46if not args.regexp:
47 pattern = pattern.replace('*', '.*')
48 pattern = '^' + pattern + '$'
49debug = 0
50
51# signal handler
52def signal_ignore(signal, frame):
53 print()
54
55# load BPF program
56bpf_text = """
57#include <uapi/linux/ptrace.h>
58
59struct key_t {
60 u64 ip;
61};
62BPF_HASH(counts, struct key_t);
63
64int trace_count(struct pt_regs *ctx) {
Brendan Gregg91734d32015-09-21 11:58:16 -070065 FILTER
Brendan Gregg3e55ae22015-09-10 12:11:35 -070066 struct key_t key = {};
67 u64 zero = 0, *val;
68 key.ip = ctx->ip;
69 val = counts.lookup_or_init(&key, &zero);
70 (*val)++;
Brendan Gregg3e55ae22015-09-10 12:11:35 -070071 return 0;
72}
73"""
74if args.pid:
Brendan Gregg91734d32015-09-21 11:58:16 -070075 bpf_text = bpf_text.replace('FILTER',
Brendan Gregg3e55ae22015-09-10 12:11:35 -070076 ('u32 pid; pid = bpf_get_current_pid_tgid(); ' +
Brendan Gregg91734d32015-09-21 11:58:16 -070077 'if (pid != %s) { return 0; }') % (args.pid))
Brendan Gregg3e55ae22015-09-10 12:11:35 -070078else:
Brendan Gregg91734d32015-09-21 11:58:16 -070079 bpf_text = bpf_text.replace('FILTER', '')
Brendan Gregg3e55ae22015-09-10 12:11:35 -070080if debug:
81 print(bpf_text)
82b = BPF(text=bpf_text)
83b.attach_kprobe(event_re=pattern, fn_name="trace_count")
Brendan Gregg6b8add02015-09-25 11:16:33 -070084matched = b.num_open_kprobes()
85if matched == 0:
86 print("0 functions matched by \"%s\". Exiting." % args.pattern)
87 exit()
Brendan Gregg3e55ae22015-09-10 12:11:35 -070088
89# header
Brendan Gregg6b8add02015-09-25 11:16:33 -070090print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." %
91 (matched, args.pattern))
Brendan Gregg3e55ae22015-09-10 12:11:35 -070092
93# output
94exiting = 0 if args.interval else 1
95while (1):
96 try:
97 sleep(int(args.interval))
98 except KeyboardInterrupt:
99 exiting=1
100 # as cleanup can take many seconds, trap Ctrl-C:
101 signal.signal(signal.SIGINT, signal_ignore)
102
103 print()
104 if args.timestamp:
105 print("%-8s\n" % strftime("%H:%M:%S"), end="")
106
107 print("%-16s %-26s %8s" % ("ADDR", "FUNC", "COUNT"))
108 counts = b.get_table("counts")
109 for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
110 print("%-16x %-26s %8d" % (k.ip, b.ksym(k.ip), v.value))
111 counts.clear()
112
113 if exiting:
114 print("Detaching...")
115 exit()