blob: 5bc3f34c295b238f50e49cd8d21e174a93d4ceb4 [file] [log] [blame]
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -05001#!/usr/bin/env python
2#
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
12from bcc import BPF
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050013from time import sleep, strftime
14import argparse
Sasha Goldshtein65453902017-03-26 13:06:51 +000015import itertools
Sasha Goldshtein51803bf2017-03-09 13:43:27 +000016import subprocess
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050017import sys
18
Sasha Goldshtein65453902017-03-26 13:06:51 +000019if sys.version_info.major < 3:
20 izip_longest = itertools.izip_longest
21else:
22 izip_longest = itertools.zip_longest
23
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050024#
25# Syscall table for Linux x86_64, not very recent.
26# Automatically generated from strace/linux/x86_64/syscallent.h using the
27# following command:
28#
29# cat syscallent.h | awk -F, '{ gsub(/[ \t"}]/, "", $4);
30# gsub(/[ \t/*]/, "", $5);
31# print " "$5": \""$4"\","; }
Paul Chaignon956ca1c2017-03-04 20:07:56 +010032# BEGIN { print "syscalls = {" }
33# END { print "}" }'
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -050034#
35syscalls = {
36 0: "read",
37 1: "write",
38 2: "open",
39 3: "close",
40 4: "stat",
41 5: "fstat",
42 6: "lstat",
43 7: "poll",
44 8: "lseek",
45 9: "mmap",
46 10: "mprotect",
47 11: "munmap",
48 12: "brk",
49 13: "rt_sigaction",
50 14: "rt_sigprocmask",
51 15: "rt_sigreturn",
52 16: "ioctl",
53 17: "pread",
54 18: "pwrite",
55 19: "readv",
56 20: "writev",
57 21: "access",
58 22: "pipe",
59 23: "select",
60 24: "sched_yield",
61 25: "mremap",
62 26: "msync",
63 27: "mincore",
64 28: "madvise",
65 29: "shmget",
66 30: "shmat",
67 31: "shmctl",
68 32: "dup",
69 33: "dup2",
70 34: "pause",
71 35: "nanosleep",
72 36: "getitimer",
73 37: "alarm",
74 38: "setitimer",
75 39: "getpid",
76 40: "sendfile",
77 41: "socket",
78 42: "connect",
79 43: "accept",
80 44: "sendto",
81 45: "recvfrom",
82 46: "sendmsg",
83 47: "recvmsg",
84 48: "shutdown",
85 49: "bind",
86 50: "listen",
87 51: "getsockname",
88 52: "getpeername",
89 53: "socketpair",
90 54: "setsockopt",
91 55: "getsockopt",
92 56: "clone",
93 57: "fork",
94 58: "vfork",
95 59: "execve",
96 60: "_exit",
97 61: "wait4",
98 62: "kill",
99 63: "uname",
100 64: "semget",
101 65: "semop",
102 66: "semctl",
103 67: "shmdt",
104 68: "msgget",
105 69: "msgsnd",
106 70: "msgrcv",
107 71: "msgctl",
108 72: "fcntl",
109 73: "flock",
110 74: "fsync",
111 75: "fdatasync",
112 76: "truncate",
113 77: "ftruncate",
114 78: "getdents",
115 79: "getcwd",
116 80: "chdir",
117 81: "fchdir",
118 82: "rename",
119 83: "mkdir",
120 84: "rmdir",
121 85: "creat",
122 86: "link",
123 87: "unlink",
124 88: "symlink",
125 89: "readlink",
126 90: "chmod",
127 91: "fchmod",
128 92: "chown",
129 93: "fchown",
130 94: "lchown",
131 95: "umask",
132 96: "gettimeofday",
133 97: "getrlimit",
134 98: "getrusage",
135 99: "sysinfo",
136 100: "times",
137 101: "ptrace",
138 102: "getuid",
139 103: "syslog",
140 104: "getgid",
141 105: "setuid",
142 106: "setgid",
143 107: "geteuid",
144 108: "getegid",
145 109: "setpgid",
146 110: "getppid",
147 111: "getpgrp",
148 112: "setsid",
149 113: "setreuid",
150 114: "setregid",
151 115: "getgroups",
152 116: "setgroups",
153 117: "setresuid",
154 118: "getresuid",
155 119: "setresgid",
156 120: "getresgid",
157 121: "getpgid",
158 122: "setfsuid",
159 123: "setfsgid",
160 124: "getsid",
161 125: "capget",
162 126: "capset",
163 127: "rt_sigpending",
164 128: "rt_sigtimedwait",
165 129: "rt_sigqueueinfo",
166 130: "rt_sigsuspend",
167 131: "sigaltstack",
168 132: "utime",
169 133: "mknod",
170 134: "uselib",
171 135: "personality",
172 136: "ustat",
173 137: "statfs",
174 138: "fstatfs",
175 139: "sysfs",
176 140: "getpriority",
177 141: "setpriority",
178 142: "sched_setparam",
179 143: "sched_getparam",
180 144: "sched_setscheduler",
181 145: "sched_getscheduler",
182 146: "sched_get_priority_max",
183 147: "sched_get_priority_min",
184 148: "sched_rr_get_interval",
185 149: "mlock",
186 150: "munlock",
187 151: "mlockall",
188 152: "munlockall",
189 153: "vhangup",
190 154: "modify_ldt",
191 155: "pivot_root",
192 156: "_sysctl",
193 157: "prctl",
194 158: "arch_prctl",
195 159: "adjtimex",
196 160: "setrlimit",
197 161: "chroot",
198 162: "sync",
199 163: "acct",
200 164: "settimeofday",
201 165: "mount",
202 166: "umount",
203 167: "swapon",
204 168: "swapoff",
205 169: "reboot",
206 170: "sethostname",
207 171: "setdomainname",
208 172: "iopl",
209 173: "ioperm",
210 174: "create_module",
211 175: "init_module",
212 176: "delete_module",
213 177: "get_kernel_syms",
214 178: "query_module",
215 179: "quotactl",
216 180: "nfsservctl",
217 181: "getpmsg",
218 182: "putpmsg",
219 183: "afs_syscall",
220 184: "tuxcall",
221 185: "security",
222 186: "gettid",
223 187: "readahead",
224 188: "setxattr",
225 189: "lsetxattr",
226 190: "fsetxattr",
227 191: "getxattr",
228 192: "lgetxattr",
229 193: "fgetxattr",
230 194: "listxattr",
231 195: "llistxattr",
232 196: "flistxattr",
233 197: "removexattr",
234 198: "lremovexattr",
235 199: "fremovexattr",
236 200: "tkill",
237 201: "time",
238 202: "futex",
239 203: "sched_setaffinity",
240 204: "sched_getaffinity",
241 205: "set_thread_area",
242 206: "io_setup",
243 207: "io_destroy",
244 208: "io_getevents",
245 209: "io_submit",
246 210: "io_cancel",
247 211: "get_thread_area",
248 212: "lookup_dcookie",
249 213: "epoll_create",
250 214: "epoll_ctl_old",
251 215: "epoll_wait_old",
252 216: "remap_file_pages",
253 217: "getdents64",
254 218: "set_tid_address",
255 219: "restart_syscall",
256 220: "semtimedop",
257 221: "fadvise64",
258 222: "timer_create",
259 223: "timer_settime",
260 224: "timer_gettime",
261 225: "timer_getoverrun",
262 226: "timer_delete",
263 227: "clock_settime",
264 228: "clock_gettime",
265 229: "clock_getres",
266 230: "clock_nanosleep",
267 231: "exit_group",
268 232: "epoll_wait",
269 233: "epoll_ctl",
270 234: "tgkill",
271 235: "utimes",
272 236: "vserver",
273 237: "mbind",
274 238: "set_mempolicy",
275 239: "get_mempolicy",
276 240: "mq_open",
277 241: "mq_unlink",
278 242: "mq_timedsend",
279 243: "mq_timedreceive",
280 244: "mq_notify",
281 245: "mq_getsetattr",
282 246: "kexec_load",
283 247: "waitid",
284 248: "add_key",
285 249: "request_key",
286 250: "keyctl",
287 251: "ioprio_set",
288 252: "ioprio_get",
289 253: "inotify_init",
290 254: "inotify_add_watch",
291 255: "inotify_rm_watch",
292 256: "migrate_pages",
293 257: "openat",
294 258: "mkdirat",
295 259: "mknodat",
296 260: "fchownat",
297 261: "futimesat",
298 262: "newfstatat",
299 263: "unlinkat",
300 264: "renameat",
301 265: "linkat",
302 266: "symlinkat",
303 267: "readlinkat",
304 268: "fchmodat",
305 269: "faccessat",
306 270: "pselect6",
307 271: "ppoll",
308 272: "unshare",
309 273: "set_robust_list",
310 274: "get_robust_list",
311 275: "splice",
312 276: "tee",
313 277: "sync_file_range",
314 278: "vmsplice",
315 279: "move_pages",
316 280: "utimensat",
317 281: "epoll_pwait",
318 282: "signalfd",
319 283: "timerfd_create",
320 284: "eventfd",
321 285: "fallocate",
322 286: "timerfd_settime",
323 287: "timerfd_gettime",
324 288: "accept4",
325 289: "signalfd4",
326 290: "eventfd2",
327 291: "epoll_create1",
328 292: "dup3",
329 293: "pipe2",
330 294: "inotify_init1",
331 295: "preadv",
332 296: "pwritev",
333 297: "rt_tgsigqueueinfo",
334 298: "perf_event_open",
335 299: "recvmmsg",
336 300: "fanotify_init",
337 301: "fanotify_mark",
338 302: "prlimit64",
339 303: "name_to_handle_at",
340 304: "open_by_handle_at",
341 305: "clock_adjtime",
342 306: "syncfs",
343 307: "sendmmsg",
344 308: "setns",
345 309: "getcpu",
346 310: "process_vm_readv",
347 311: "process_vm_writev",
348 312: "kcmp",
349 313: "finit_module",
350}
351
Sasha Goldshtein51803bf2017-03-09 13:43:27 +0000352# Try to use ausyscall if it is available, because it can give us an up-to-date
353# list of syscalls for various architectures, rather than the x86-64 hardcoded
354# list above.
355def parse_syscall(line):
356 parts = line.split()
357 return (int(parts[0]), parts[1].strip())
358
359try:
360 # Skip the first line, which is a header. The rest of the lines are simply
361 # SYSCALL_NUM\tSYSCALL_NAME pairs.
362 out = subprocess.check_output('ausyscall --dump | tail -n +2', shell=True)
363 syscalls = dict(map(parse_syscall, out.strip().split('\n')))
364except Exception as e:
365 pass
366
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500367parser = argparse.ArgumentParser(
368 description="Summarize syscall counts and latencies.")
369parser.add_argument("-p", "--pid", type=int, help="trace only this pid")
370parser.add_argument("-i", "--interval", type=int,
371 help="print summary at this interval (seconds)")
372parser.add_argument("-T", "--top", type=int, default=10,
373 help="print only the top syscalls by count or latency")
374parser.add_argument("-x", "--failures", action="store_true",
375 help="trace only failed syscalls (return < 0)")
376parser.add_argument("-L", "--latency", action="store_true",
377 help="collect syscall latency")
378parser.add_argument("-m", "--milliseconds", action="store_true",
379 help="display latency in milliseconds (default: microseconds)")
380parser.add_argument("-P", "--process", action="store_true",
381 help="count by process and not by syscall")
382parser.add_argument("-l", "--list", action="store_true",
383 help="print list of recognized syscalls and exit")
384args = parser.parse_args()
385
386if args.list:
387 for grp in izip_longest(*(iter(sorted(syscalls.values())),) * 4):
388 print(" ".join(["%-20s" % s for s in grp if s is not None]))
389 sys.exit(0)
390
391text = """
392#ifdef LATENCY
393struct data_t {
394 u64 count;
395 u64 total_ns;
396};
397
398BPF_HASH(start, u64, u64);
399BPF_HASH(data, u32, struct data_t);
400#else
401BPF_HASH(data, u32, u64);
402#endif
403
404#ifdef LATENCY
405TRACEPOINT_PROBE(raw_syscalls, sys_enter) {
406 u64 pid_tgid = bpf_get_current_pid_tgid();
407
408#ifdef FILTER_PID
409 if (pid_tgid >> 32 != FILTER_PID)
410 return 0;
411#endif
412
413 u64 t = bpf_ktime_get_ns();
414 start.update(&pid_tgid, &t);
415 return 0;
416}
417#endif
418
419TRACEPOINT_PROBE(raw_syscalls, sys_exit) {
420 u64 pid_tgid = bpf_get_current_pid_tgid();
421
422#ifdef FILTER_PID
423 if (pid_tgid >> 32 != FILTER_PID)
424 return 0;
425#endif
426
427#ifdef FILTER_FAILED
428 if (args->ret >= 0)
429 return 0;
430#endif
431
432#ifdef BY_PROCESS
433 u32 key = pid_tgid >> 32;
434#else
435 u32 key = args->id;
436#endif
437
438#ifdef LATENCY
439 struct data_t *val, zero = {};
440 u64 *start_ns = start.lookup(&pid_tgid);
441 if (!start_ns)
442 return 0;
443
444 val = data.lookup_or_init(&key, &zero);
445 val->count++;
446 val->total_ns = bpf_ktime_get_ns() - *start_ns;
447#else
448 u64 *val, zero = 0;
449 val = data.lookup_or_init(&key, &zero);
450 ++(*val);
451#endif
452 return 0;
453}
454"""
455
456if args.pid:
457 text = ("#define FILTER_PID %d\n" % args.pid) + text
458if args.failures:
459 text = "#define FILTER_FAILED\n" + text
460if args.latency:
461 text = "#define LATENCY\n" + text
462if args.process:
463 text = "#define BY_PROCESS\n" + text
464
465bpf = BPF(text=text)
466
467def print_stats():
468 if args.latency:
469 print_latency_stats()
470 else:
471 print_count_stats()
472
473agg_colname = "PID COMM" if args.process else "SYSCALL"
474time_colname = "TIME (ms)" if args.milliseconds else "TIME (us)"
475
476def comm_for_pid(pid):
477 try:
478 return open("/proc/%d/comm" % pid).read().strip()
479 except Exception:
480 return "[unknown]"
481
482def agg_colval(key):
483 if args.process:
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100484 return "%-6d %-15s" % (key.value, comm_for_pid(key.value))
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500485 else:
486 return syscalls.get(key.value, "[unknown: %d]" % key.value)
487
488def print_count_stats():
489 data = bpf["data"]
490 print("[%s]" % strftime("%H:%M:%S"))
491 print("%-22s %8s" % (agg_colname, "COUNT"))
492 for k, v in sorted(data.items(), key=lambda kv: -kv[1].value)[:args.top]:
493 if k.value == 0xFFFFFFFF:
494 continue # happens occasionally, we don't need it
495 print("%-22s %8d" % (agg_colval(k), v.value))
496 print("")
497 data.clear()
498
499def print_latency_stats():
500 data = bpf["data"]
501 print("[%s]" % strftime("%H:%M:%S"))
502 print("%-22s %8s %16s" % (agg_colname, "COUNT", time_colname))
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100503 for k, v in sorted(data.items(),
504 key=lambda kv: -kv[1].total_ns)[:args.top]:
Sasha Goldshtein8e583cc2017-02-09 10:11:50 -0500505 if k.value == 0xFFFFFFFF:
506 continue # happens occasionally, we don't need it
507 print(("%-22s %8d " + ("%16.6f" if args.milliseconds else "%16.3f")) %
508 (agg_colval(k), v.count,
509 v.total_ns / (1e6 if args.milliseconds else 1e3)))
510 print("")
511 data.clear()
512
513print("Tracing %ssyscalls, printing top %d... Ctrl+C to quit." %
514 ("failed " if args.failures else "", args.top))
515while True:
516 try:
517 sleep(args.interval or 999999999)
518 print_stats()
519 except KeyboardInterrupt:
520 if not args.interval:
521 print_stats()
522 break