blob: f41708728d69c59f7efdacd82da60c4421f66953 [file] [log] [blame]
Brendan Gregg3e55ae22015-09-10 12:11:35 -07001#!/usr/bin/python
Alexei Starovoitovbdf07732016-01-14 10:09:20 -08002# @lint-avoid-python-3-compatibility-imports
Brendan Gregg3e55ae22015-09-10 12:11:35 -07003#
Alexei Starovoitovbdf07732016-01-14 10:09:20 -08004# funccount Count kernel function calls.
5# For Linux, uses BCC, eBPF. See .c file.
Brendan Gregg3e55ae22015-09-10 12:11:35 -07006#
7# USAGE: funccount [-h] [-p PID] [-i INTERVAL] [-T] [-r] pattern
8#
9# The pattern is a string with optional '*' wildcards, similar to file globbing.
10# If you'd prefer to use regular expressions, use the -r option.
11#
12# Copyright (c) 2015 Brendan Gregg.
13# Licensed under the Apache License, Version 2.0 (the "License")
14#
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080015# 09-Sep-2015 Brendan Gregg Created this.
Brendan Gregg3e55ae22015-09-10 12:11:35 -070016
17from __future__ import print_function
18from bcc import BPF
19from time import sleep, strftime
20import argparse
Brendan Gregg3e55ae22015-09-10 12:11:35 -070021import signal
22
23# arguments
24examples = """examples:
25 ./funccount 'vfs_*' # count kernel functions starting with "vfs"
26 ./funccount 'tcp_send*' # count kernel funcs starting with "tcp_send"
27 ./funccount -r '^vfs.*' # same as above, using regular expressions
28 ./funccount -Ti 5 'vfs_*' # output every 5 seconds, with timestamps
29 ./funccount -p 185 'vfs_*' # count vfs calls for PID 181 only
30"""
31parser = argparse.ArgumentParser(
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080032 description="Count kernel function calls",
33 formatter_class=argparse.RawDescriptionHelpFormatter,
34 epilog=examples)
Brendan Gregg3e55ae22015-09-10 12:11:35 -070035parser.add_argument("-p", "--pid",
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080036 help="trace this PID only")
Brendan Gregg3e55ae22015-09-10 12:11:35 -070037parser.add_argument("-i", "--interval", default=99999999,
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080038 help="summary interval, seconds")
Brendan Gregg3e55ae22015-09-10 12:11:35 -070039parser.add_argument("-T", "--timestamp", action="store_true",
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080040 help="include timestamp on output")
Brendan Gregg3e55ae22015-09-10 12:11:35 -070041parser.add_argument("-r", "--regexp", action="store_true",
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080042 help="use regular expressions. Default is \"*\" wildcards only.")
Brendan Gregg3e55ae22015-09-10 12:11:35 -070043parser.add_argument("pattern",
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080044 help="search expression for kernel functions")
Brendan Gregg3e55ae22015-09-10 12:11:35 -070045args = parser.parse_args()
46pattern = args.pattern
47if not args.regexp:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080048 pattern = pattern.replace('*', '.*')
49 pattern = '^' + pattern + '$'
Brendan Gregg3e55ae22015-09-10 12:11:35 -070050debug = 0
51
52# signal handler
53def signal_ignore(signal, frame):
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080054 print()
Brendan Gregg3e55ae22015-09-10 12:11:35 -070055
56# load BPF program
57bpf_text = """
58#include <uapi/linux/ptrace.h>
59
60struct key_t {
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080061 u64 ip;
Brendan Gregg3e55ae22015-09-10 12:11:35 -070062};
63BPF_HASH(counts, struct key_t);
64
65int trace_count(struct pt_regs *ctx) {
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080066 FILTER
67 struct key_t key = {};
68 u64 zero = 0, *val;
69 key.ip = ctx->ip;
70 val = counts.lookup_or_init(&key, &zero);
71 (*val)++;
72 return 0;
Brendan Gregg3e55ae22015-09-10 12:11:35 -070073}
74"""
75if args.pid:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080076 bpf_text = bpf_text.replace('FILTER',
77 ('u32 pid; pid = bpf_get_current_pid_tgid(); ' +
78 'if (pid != %s) { return 0; }') % (args.pid))
Brendan Gregg3e55ae22015-09-10 12:11:35 -070079else:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080080 bpf_text = bpf_text.replace('FILTER', '')
Brendan Gregg3e55ae22015-09-10 12:11:35 -070081if debug:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080082 print(bpf_text)
Brendan Gregg3e55ae22015-09-10 12:11:35 -070083b = BPF(text=bpf_text)
84b.attach_kprobe(event_re=pattern, fn_name="trace_count")
Brendan Gregg6b8add02015-09-25 11:16:33 -070085matched = b.num_open_kprobes()
86if matched == 0:
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080087 print("0 functions matched by \"%s\". Exiting." % args.pattern)
88 exit()
Brendan Gregg3e55ae22015-09-10 12:11:35 -070089
90# header
Brendan Gregg6b8add02015-09-25 11:16:33 -070091print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." %
92 (matched, args.pattern))
Brendan Gregg3e55ae22015-09-10 12:11:35 -070093
94# output
95exiting = 0 if args.interval else 1
96while (1):
Alexei Starovoitovbdf07732016-01-14 10:09:20 -080097 try:
98 sleep(int(args.interval))
99 except KeyboardInterrupt:
100 exiting = 1
101 # as cleanup can take many seconds, trap Ctrl-C:
102 signal.signal(signal.SIGINT, signal_ignore)
Brendan Gregg3e55ae22015-09-10 12:11:35 -0700103
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800104 print()
105 if args.timestamp:
106 print("%-8s\n" % strftime("%H:%M:%S"), end="")
Brendan Gregg3e55ae22015-09-10 12:11:35 -0700107
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800108 print("%-16s %-26s %8s" % ("ADDR", "FUNC", "COUNT"))
109 counts = b.get_table("counts")
110 for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
111 print("%-16x %-26s %8d" % (k.ip, b.ksym(k.ip), v.value))
112 counts.clear()
Brendan Gregg3e55ae22015-09-10 12:11:35 -0700113
Alexei Starovoitovbdf07732016-01-14 10:09:20 -0800114 if exiting:
115 print("Detaching...")
116 exit()