blob: 7ba08dd324fde1c9910dbcb10490fd9cbbd35477 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -05002#
3# syscount Summarize syscall counts and latencies.
4#
5# USAGE: syscount [-p PID] [-i INTERVAL] [-T TOP] [-x] [-L] [-m] [-P] [-l]
6#
7# Copyright 2017, Sasha Goldshtein.
8# Licensed under the Apache License, Version 2.0 (the "License")
9#
10# 15-Feb-2017 Sasha Goldshtein Created this.
11
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050012from time import sleep, strftime
13import argparse
Lucian Adrian Grijincu5426ef22018-01-11 12:07:42 -080014import errno
Sasha Goldshtein65453902017-03-26 13:06:51 +000015import itertools
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050016import sys
Akilesh Kailash89967192018-05-18 13:36:54 -070017import signal
William Cohen218f7482018-12-06 13:41:01 -050018from bcc import BPF
19from bcc.utils import printb
Andreas Stührk5f7c8292019-02-03 08:50:49 +010020from bcc.syscall import syscall_name, syscalls
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050021
Sasha Goldshtein65453902017-03-26 13:06:51 +000022if sys.version_info.major < 3:
23 izip_longest = itertools.izip_longest
24else:
25 izip_longest = itertools.zip_longest
26
Akilesh Kailash89967192018-05-18 13:36:54 -070027# signal handler
28def signal_ignore(signal, frame):
29 print()
Lucian Adrian Grijincu5426ef22018-01-11 12:07:42 -080030
31def handle_errno(errstr):
32 try:
33 return abs(int(errstr))
34 except ValueError:
35 pass
36
37 try:
38 return getattr(errno, errstr)
39 except AttributeError:
40 raise argparse.ArgumentTypeError("couldn't map %s to an errno" % errstr)
41
42
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050043parser = argparse.ArgumentParser(
44 description="Summarize syscall counts and latencies.")
45parser.add_argument("-p", "--pid", type=int, help="trace only this pid")
46parser.add_argument("-i", "--interval", type=int,
47 help="print summary at this interval (seconds)")
Akilesh Kailash89967192018-05-18 13:36:54 -070048parser.add_argument("-d", "--duration", type=int,
49 help="total duration of trace, in seconds")
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050050parser.add_argument("-T", "--top", type=int, default=10,
51 help="print only the top syscalls by count or latency")
52parser.add_argument("-x", "--failures", action="store_true",
53 help="trace only failed syscalls (return < 0)")
Lucian Adrian Grijincu5426ef22018-01-11 12:07:42 -080054parser.add_argument("-e", "--errno", type=handle_errno,
55 help="trace only syscalls that return this error (numeric or EPERM, etc.)")
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050056parser.add_argument("-L", "--latency", action="store_true",
57 help="collect syscall latency")
58parser.add_argument("-m", "--milliseconds", action="store_true",
59 help="display latency in milliseconds (default: microseconds)")
60parser.add_argument("-P", "--process", action="store_true",
61 help="count by process and not by syscall")
62parser.add_argument("-l", "--list", action="store_true",
63 help="print list of recognized syscalls and exit")
Nathan Scottcf0792f2018-02-02 16:56:50 +110064parser.add_argument("--ebpf", action="store_true",
65 help=argparse.SUPPRESS)
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050066args = parser.parse_args()
Akilesh Kailash89967192018-05-18 13:36:54 -070067if args.duration and not args.interval:
68 args.interval = args.duration
69if not args.interval:
70 args.interval = 99999999
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050071
72if args.list:
73 for grp in izip_longest(*(iter(sorted(syscalls.values())),) * 4):
74 print(" ".join(["%-20s" % s for s in grp if s is not None]))
75 sys.exit(0)
76
77text = """
78#ifdef LATENCY
79struct data_t {
80 u64 count;
81 u64 total_ns;
82};
83
84BPF_HASH(start, u64, u64);
85BPF_HASH(data, u32, struct data_t);
86#else
87BPF_HASH(data, u32, u64);
88#endif
89
90#ifdef LATENCY
91TRACEPOINT_PROBE(raw_syscalls, sys_enter) {
92 u64 pid_tgid = bpf_get_current_pid_tgid();
93
94#ifdef FILTER_PID
95 if (pid_tgid >> 32 != FILTER_PID)
96 return 0;
97#endif
98
99 u64 t = bpf_ktime_get_ns();
100 start.update(&pid_tgid, &t);
101 return 0;
102}
103#endif
104
105TRACEPOINT_PROBE(raw_syscalls, sys_exit) {
106 u64 pid_tgid = bpf_get_current_pid_tgid();
107
108#ifdef FILTER_PID
109 if (pid_tgid >> 32 != FILTER_PID)
110 return 0;
111#endif
112
113#ifdef FILTER_FAILED
114 if (args->ret >= 0)
115 return 0;
116#endif
117
Lucian Adrian Grijincu5426ef22018-01-11 12:07:42 -0800118#ifdef FILTER_ERRNO
119 if (args->ret != -FILTER_ERRNO)
120 return 0;
121#endif
122
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500123#ifdef BY_PROCESS
124 u32 key = pid_tgid >> 32;
125#else
126 u32 key = args->id;
127#endif
128
129#ifdef LATENCY
130 struct data_t *val, zero = {};
131 u64 *start_ns = start.lookup(&pid_tgid);
132 if (!start_ns)
133 return 0;
134
yonghong-song82f43022019-10-31 08:16:12 -0700135 val = data.lookup_or_try_init(&key, &zero);
Philip Gladstoneba64f032019-09-20 01:12:01 -0400136 if (val) {
137 val->count++;
138 val->total_ns += bpf_ktime_get_ns() - *start_ns;
139 }
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500140#else
141 u64 *val, zero = 0;
yonghong-song82f43022019-10-31 08:16:12 -0700142 val = data.lookup_or_try_init(&key, &zero);
Philip Gladstoneba64f032019-09-20 01:12:01 -0400143 if (val) {
144 ++(*val);
145 }
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500146#endif
147 return 0;
148}
149"""
150
151if args.pid:
152 text = ("#define FILTER_PID %d\n" % args.pid) + text
153if args.failures:
154 text = "#define FILTER_FAILED\n" + text
Lucian Adrian Grijincu5426ef22018-01-11 12:07:42 -0800155if args.errno:
156 text = "#define FILTER_ERRNO %d\n" % abs(args.errno) + text
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500157if args.latency:
158 text = "#define LATENCY\n" + text
159if args.process:
160 text = "#define BY_PROCESS\n" + text
Nathan Scottcf0792f2018-02-02 16:56:50 +1100161if args.ebpf:
162 print(text)
163 exit()
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500164
165bpf = BPF(text=text)
166
167def print_stats():
168 if args.latency:
169 print_latency_stats()
170 else:
171 print_count_stats()
172
173agg_colname = "PID COMM" if args.process else "SYSCALL"
174time_colname = "TIME (ms)" if args.milliseconds else "TIME (us)"
175
176def comm_for_pid(pid):
177 try:
Gary Lin3a459352018-03-27 15:54:09 +0800178 return open("/proc/%d/comm" % pid, "rb").read().strip()
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500179 except Exception:
Gary Lin3a459352018-03-27 15:54:09 +0800180 return b"[unknown]"
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500181
182def agg_colval(key):
183 if args.process:
Gary Lin3a459352018-03-27 15:54:09 +0800184 return b"%-6d %-15s" % (key.value, comm_for_pid(key.value))
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500185 else:
William Cohen218f7482018-12-06 13:41:01 -0500186 return syscall_name(key.value)
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500187
188def print_count_stats():
189 data = bpf["data"]
190 print("[%s]" % strftime("%H:%M:%S"))
191 print("%-22s %8s" % (agg_colname, "COUNT"))
192 for k, v in sorted(data.items(), key=lambda kv: -kv[1].value)[:args.top]:
193 if k.value == 0xFFFFFFFF:
194 continue # happens occasionally, we don't need it
Gary Lin3a459352018-03-27 15:54:09 +0800195 printb(b"%-22s %8d" % (agg_colval(k), v.value))
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500196 print("")
197 data.clear()
198
199def print_latency_stats():
200 data = bpf["data"]
201 print("[%s]" % strftime("%H:%M:%S"))
202 print("%-22s %8s %16s" % (agg_colname, "COUNT", time_colname))
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100203 for k, v in sorted(data.items(),
204 key=lambda kv: -kv[1].total_ns)[:args.top]:
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500205 if k.value == 0xFFFFFFFF:
206 continue # happens occasionally, we don't need it
Gary Lin3a459352018-03-27 15:54:09 +0800207 printb((b"%-22s %8d " + (b"%16.6f" if args.milliseconds else b"%16.3f")) %
208 (agg_colval(k), v.count,
209 v.total_ns / (1e6 if args.milliseconds else 1e3)))
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500210 print("")
211 data.clear()
212
213print("Tracing %ssyscalls, printing top %d... Ctrl+C to quit." %
214 ("failed " if args.failures else "", args.top))
Akilesh Kailash89967192018-05-18 13:36:54 -0700215exiting = 0 if args.interval else 1
216seconds = 0
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500217while True:
218 try:
Akilesh Kailash89967192018-05-18 13:36:54 -0700219 sleep(args.interval)
220 seconds += args.interval
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500221 except KeyboardInterrupt:
Akilesh Kailash89967192018-05-18 13:36:54 -0700222 exiting = 1
223 signal.signal(signal.SIGINT, signal_ignore)
224 if args.duration and seconds >= args.duration:
225 exiting = 1
226
227 print_stats()
228
229 if exiting:
230 print("Detaching...")
231 exit()