blob: b5a6a9782de2a22fec96c7d47b1450efe8a0fec7 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Sasha Goldshtein40975ab2016-06-29 03:57:01 +03002# @lint-avoid-python-3-compatibility-imports
3#
Sasha Goldshtein9972f272016-06-29 01:48:08 -07004# cpudist Summarize on- and off-CPU time per task as a histogram.
Sasha Goldshtein40975ab2016-06-29 03:57:01 +03005#
Sasha Goldshtein9972f272016-06-29 01:48:08 -07006# USAGE: cpudist [-h] [-O] [-T] [-m] [-P] [-L] [-p PID] [interval] [count]
Sasha Goldshtein40975ab2016-06-29 03:57:01 +03007#
Sasha Goldshtein9972f272016-06-29 01:48:08 -07008# This measures the time a task spends on or off the CPU, and shows this time
9# as a histogram, optionally per-process.
Sasha Goldshtein40975ab2016-06-29 03:57:01 +030010#
11# Copyright 2016 Sasha Goldshtein
12# Licensed under the Apache License, Version 2.0 (the "License")
13
14from __future__ import print_function
Rafael Fonsecaaf236e72017-02-15 17:28:26 +010015from bcc import BPF
Sasha Goldshtein40975ab2016-06-29 03:57:01 +030016from time import sleep, strftime
17import argparse
18
19examples = """examples:
20 cpudist # summarize on-CPU time as a histogram
Sasha Goldshtein9972f272016-06-29 01:48:08 -070021 cpudist -O # summarize off-CPU time as a histogram
Sasha Goldshtein40975ab2016-06-29 03:57:01 +030022 cpudist 1 10 # print 1 second summaries, 10 times
23 cpudist -mT 1 # 1s summaries, milliseconds, and timestamps
24 cpudist -P # show each PID separately
25 cpudist -p 185 # trace PID 185 only
26"""
27parser = argparse.ArgumentParser(
28 description="Summarize on-CPU time per task as a histogram.",
29 formatter_class=argparse.RawDescriptionHelpFormatter,
30 epilog=examples)
Sasha Goldshtein9972f272016-06-29 01:48:08 -070031parser.add_argument("-O", "--offcpu", action="store_true",
32 help="measure off-CPU time")
Sasha Goldshtein40975ab2016-06-29 03:57:01 +030033parser.add_argument("-T", "--timestamp", action="store_true",
34 help="include timestamp on output")
35parser.add_argument("-m", "--milliseconds", action="store_true",
36 help="millisecond histogram")
37parser.add_argument("-P", "--pids", action="store_true",
38 help="print a histogram per process ID")
39parser.add_argument("-L", "--tids", action="store_true",
40 help="print a histogram per thread ID")
41parser.add_argument("-p", "--pid",
42 help="trace this PID only")
43parser.add_argument("interval", nargs="?", default=99999999,
44 help="output interval, in seconds")
45parser.add_argument("count", nargs="?", default=99999999,
46 help="number of outputs")
Nathan Scottcf0792f2018-02-02 16:56:50 +110047parser.add_argument("--ebpf", action="store_true",
48 help=argparse.SUPPRESS)
Sasha Goldshtein40975ab2016-06-29 03:57:01 +030049args = parser.parse_args()
50countdown = int(args.count)
51debug = 0
52
Sasha Goldshtein06d90d32016-06-30 07:39:27 -070053bpf_text = """#include <uapi/linux/ptrace.h>
54#include <linux/sched.h>
55"""
Sasha Goldshtein40975ab2016-06-29 03:57:01 +030056
Sasha Goldshtein9972f272016-06-29 01:48:08 -070057if not args.offcpu:
58 bpf_text += "#define ONCPU\n"
59
Sasha Goldshtein40975ab2016-06-29 03:57:01 +030060bpf_text += """
61typedef struct pid_key {
62 u64 id;
63 u64 slot;
64} pid_key_t;
65
Sasha Goldshtein9972f272016-06-29 01:48:08 -070066
Tristan Cacqueray9b433f22019-10-23 15:02:03 +000067BPF_HASH(start, u32, u64, MAX_PID);
Sasha Goldshtein40975ab2016-06-29 03:57:01 +030068STORAGE
69
Sasha Goldshtein9972f272016-06-29 01:48:08 -070070static inline void store_start(u32 tgid, u32 pid, u64 ts)
71{
Sasha Goldshtein9972f272016-06-29 01:48:08 -070072 if (FILTER)
73 return;
74
75 start.update(&pid, &ts);
76}
77
78static inline void update_hist(u32 tgid, u32 pid, u64 ts)
79{
Sasha Goldshtein9972f272016-06-29 01:48:08 -070080 if (FILTER)
81 return;
82
83 u64 *tsp = start.lookup(&pid);
84 if (tsp == 0)
85 return;
86
Sasha Goldshteinbee8d362016-06-30 10:46:27 -070087 if (ts < *tsp) {
88 // Probably a clock issue where the recorded on-CPU event had a
89 // timestamp later than the recorded off-CPU event, or vice versa.
90 return;
91 }
Sasha Goldshtein9972f272016-06-29 01:48:08 -070092 u64 delta = ts - *tsp;
93 FACTOR
94 STORE
95}
96
Sasha Goldshtein06d90d32016-06-30 07:39:27 -070097int sched_switch(struct pt_regs *ctx, struct task_struct *prev)
Sasha Goldshtein40975ab2016-06-29 03:57:01 +030098{
Sasha Goldshtein9972f272016-06-29 01:48:08 -070099 u64 ts = bpf_ktime_get_ns();
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300100 u64 pid_tgid = bpf_get_current_pid_tgid();
Sasha Goldshtein9972f272016-06-29 01:48:08 -0700101 u32 tgid = pid_tgid >> 32, pid = pid_tgid;
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300102
Nick-nizhenab14faf2021-05-27 13:21:59 +0800103 u32 prev_pid = prev->pid;
104 u32 prev_tgid = prev->tgid;
Sasha Goldshtein9972f272016-06-29 01:48:08 -0700105#ifdef ONCPU
Nick-nizhenab14faf2021-05-27 13:21:59 +0800106 update_hist(prev_tgid, prev_pid, ts);
Sasha Goldshtein9972f272016-06-29 01:48:08 -0700107#else
Nick-nizhenab14faf2021-05-27 13:21:59 +0800108 store_start(prev_tgid, prev_pid, ts);
Sasha Goldshtein3c976bb2016-06-29 23:35:43 -0700109#endif
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300110
Sasha Goldshtein3c976bb2016-06-29 23:35:43 -0700111BAIL:
Sasha Goldshtein9972f272016-06-29 01:48:08 -0700112#ifdef ONCPU
113 store_start(tgid, pid, ts);
114#else
115 update_hist(tgid, pid, ts);
116#endif
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300117
118 return 0;
119}
120"""
121
122if args.pid:
123 bpf_text = bpf_text.replace('FILTER', 'tgid != %s' % args.pid)
124else:
125 bpf_text = bpf_text.replace('FILTER', '0')
126if args.milliseconds:
127 bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;')
128 label = "msecs"
129else:
130 bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;')
131 label = "usecs"
132if args.pids or args.tids:
133 section = "pid"
134 pid = "tgid"
135 if args.tids:
136 pid = "pid"
137 section = "tid"
138 bpf_text = bpf_text.replace('STORAGE',
Tristan Cacqueray9b433f22019-10-23 15:02:03 +0000139 'BPF_HISTOGRAM(dist, pid_key_t, MAX_PID);')
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300140 bpf_text = bpf_text.replace('STORE',
141 'pid_key_t key = {.id = ' + pid + ', .slot = bpf_log2l(delta)}; ' +
142 'dist.increment(key);')
143else:
144 section = ""
145 bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist);')
146 bpf_text = bpf_text.replace('STORE',
147 'dist.increment(bpf_log2l(delta));')
Nathan Scottcf0792f2018-02-02 16:56:50 +1100148if debug or args.ebpf:
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300149 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100150 if args.ebpf:
151 exit()
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300152
Tristan Cacqueray9b433f22019-10-23 15:02:03 +0000153max_pid = int(open("/proc/sys/kernel/pid_max").read())
154
155b = BPF(text=bpf_text, cflags=["-DMAX_PID=%d" % max_pid])
Guodong Xu00b72fd2021-03-13 02:23:47 +0000156b.attach_kprobe(event_re="^finish_task_switch$|^finish_task_switch\.isra\.\d$",
157 fn_name="sched_switch")
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300158
Sasha Goldshtein9972f272016-06-29 01:48:08 -0700159print("Tracing %s-CPU time... Hit Ctrl-C to end." %
160 ("off" if args.offcpu else "on"))
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300161
162exiting = 0 if args.interval else 1
163dist = b.get_table("dist")
164while (1):
165 try:
166 sleep(int(args.interval))
167 except KeyboardInterrupt:
168 exiting = 1
169
170 print()
171 if args.timestamp:
172 print("%-8s\n" % strftime("%H:%M:%S"), end="")
173
Sasha Goldshtein4b72f052016-06-29 02:18:06 -0700174 def pid_to_comm(pid):
175 try:
176 comm = open("/proc/%d/comm" % pid, "r").read()
177 return "%d %s" % (pid, comm)
178 except IOError:
179 return str(pid)
180
181 dist.print_log2_hist(label, section, section_print_fn=pid_to_comm)
Sasha Goldshtein40975ab2016-06-29 03:57:01 +0300182 dist.clear()
183
184 countdown -= 1
185 if exiting or countdown == 0:
186 exit()