blob: 6cbea11620c8deec9c705e00637c3a309a637862 [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
135 val = data.lookup_or_init(&key, &zero);
136 val->count++;
muahao8cdcb0d2018-08-16 13:32:22 +0800137 val->total_ns += bpf_ktime_get_ns() - *start_ns;
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500138#else
139 u64 *val, zero = 0;
140 val = data.lookup_or_init(&key, &zero);
141 ++(*val);
142#endif
143 return 0;
144}
145"""
146
147if args.pid:
148 text = ("#define FILTER_PID %d\n" % args.pid) + text
149if args.failures:
150 text = "#define FILTER_FAILED\n" + text
Lucian Adrian Grijincu5426ef22018-01-11 12:07:42 -0800151if args.errno:
152 text = "#define FILTER_ERRNO %d\n" % abs(args.errno) + text
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500153if args.latency:
154 text = "#define LATENCY\n" + text
155if args.process:
156 text = "#define BY_PROCESS\n" + text
Nathan Scottcf0792f2018-02-02 16:56:50 +1100157if args.ebpf:
158 print(text)
159 exit()
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500160
161bpf = BPF(text=text)
162
163def print_stats():
164 if args.latency:
165 print_latency_stats()
166 else:
167 print_count_stats()
168
169agg_colname = "PID COMM" if args.process else "SYSCALL"
170time_colname = "TIME (ms)" if args.milliseconds else "TIME (us)"
171
172def comm_for_pid(pid):
173 try:
Gary Lin3a459352018-03-27 15:54:09 +0800174 return open("/proc/%d/comm" % pid, "rb").read().strip()
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500175 except Exception:
Gary Lin3a459352018-03-27 15:54:09 +0800176 return b"[unknown]"
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500177
178def agg_colval(key):
179 if args.process:
Gary Lin3a459352018-03-27 15:54:09 +0800180 return b"%-6d %-15s" % (key.value, comm_for_pid(key.value))
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500181 else:
William Cohen218f7482018-12-06 13:41:01 -0500182 return syscall_name(key.value)
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500183
184def print_count_stats():
185 data = bpf["data"]
186 print("[%s]" % strftime("%H:%M:%S"))
187 print("%-22s %8s" % (agg_colname, "COUNT"))
188 for k, v in sorted(data.items(), key=lambda kv: -kv[1].value)[:args.top]:
189 if k.value == 0xFFFFFFFF:
190 continue # happens occasionally, we don't need it
Gary Lin3a459352018-03-27 15:54:09 +0800191 printb(b"%-22s %8d" % (agg_colval(k), v.value))
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500192 print("")
193 data.clear()
194
195def print_latency_stats():
196 data = bpf["data"]
197 print("[%s]" % strftime("%H:%M:%S"))
198 print("%-22s %8s %16s" % (agg_colname, "COUNT", time_colname))
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100199 for k, v in sorted(data.items(),
200 key=lambda kv: -kv[1].total_ns)[:args.top]:
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500201 if k.value == 0xFFFFFFFF:
202 continue # happens occasionally, we don't need it
Gary Lin3a459352018-03-27 15:54:09 +0800203 printb((b"%-22s %8d " + (b"%16.6f" if args.milliseconds else b"%16.3f")) %
204 (agg_colval(k), v.count,
205 v.total_ns / (1e6 if args.milliseconds else 1e3)))
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500206 print("")
207 data.clear()
208
209print("Tracing %ssyscalls, printing top %d... Ctrl+C to quit." %
210 ("failed " if args.failures else "", args.top))
Akilesh Kailash89967192018-05-18 13:36:54 -0700211exiting = 0 if args.interval else 1
212seconds = 0
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500213while True:
214 try:
Akilesh Kailash89967192018-05-18 13:36:54 -0700215 sleep(args.interval)
216 seconds += args.interval
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500217 except KeyboardInterrupt:
Akilesh Kailash89967192018-05-18 13:36:54 -0700218 exiting = 1
219 signal.signal(signal.SIGINT, signal_ignore)
220 if args.duration and seconds >= args.duration:
221 exiting = 1
222
223 print_stats()
224
225 if exiting:
226 print("Detaching...")
227 exit()