blob: 90b114cf93f95c52532dea985ca6563c5b969d74 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Brendan Gregg7bf0e492016-01-27 23:17:40 -08002#
3# wakeuptime Summarize sleep to wakeup time by waker kernel stack
4# For Linux, uses BCC, eBPF.
5#
Brendan Gregg62f4c282016-01-30 11:05:40 -08006# USAGE: wakeuptime [-h] [-u] [-p PID] [-v] [-f] [duration]
Brendan Gregg7bf0e492016-01-27 23:17:40 -08007#
Brendan Gregg7bf0e492016-01-27 23:17:40 -08008# Copyright 2016 Netflix, Inc.
9# Licensed under the Apache License, Version 2.0 (the "License")
10#
11# 14-Jan-2016 Brendan Gregg Created this.
12
13from __future__ import print_function
14from bcc import BPF
Brenden Blancoe8001c32018-07-23 08:15:56 -070015from bcc.utils import printb
Brendan Gregg7bf0e492016-01-27 23:17:40 -080016from time import sleep, strftime
17import argparse
18import signal
Sandipan Das2c2d46f2017-11-16 17:14:38 +053019import errno
20from sys import stderr
21
22# arg validation
23def positive_int(val):
24 try:
25 ival = int(val)
26 except ValueError:
27 raise argparse.ArgumentTypeError("must be an integer")
28
29 if ival < 0:
30 raise argparse.ArgumentTypeError("must be positive")
31 return ival
32
33def positive_nonzero_int(val):
34 ival = positive_int(val)
35 if ival == 0:
36 raise argparse.ArgumentTypeError("must be nonzero")
37 return ival
Brendan Gregg7bf0e492016-01-27 23:17:40 -080038
39# arguments
40examples = """examples:
41 ./wakeuptime # trace blocked time with waker stacks
42 ./wakeuptime 5 # trace for 5 seconds only
43 ./wakeuptime -f 5 # 5 seconds, and output in folded format
44 ./wakeuptime -u # don't include kernel threads (user only)
Pascal Lothb1508e82018-08-31 21:50:44 +020045 ./wakeuptime -p 185 # trace for PID 185 only
Brendan Gregg7bf0e492016-01-27 23:17:40 -080046"""
47parser = argparse.ArgumentParser(
48 description="Summarize sleep to wakeup time by waker kernel stack",
49 formatter_class=argparse.RawDescriptionHelpFormatter,
50 epilog=examples)
51parser.add_argument("-u", "--useronly", action="store_true",
52 help="user threads only (no kernel threads)")
53parser.add_argument("-p", "--pid",
Sandipan Das2c2d46f2017-11-16 17:14:38 +053054 type=positive_int,
Brendan Gregg7bf0e492016-01-27 23:17:40 -080055 help="trace this PID only")
56parser.add_argument("-v", "--verbose", action="store_true",
57 help="show raw addresses")
58parser.add_argument("-f", "--folded", action="store_true",
59 help="output folded format")
Sandipan Das2c2d46f2017-11-16 17:14:38 +053060parser.add_argument("--stack-storage-size", default=1024,
61 type=positive_nonzero_int,
62 help="the number of unique stack traces that can be stored and "
63 "displayed (default 1024)")
Brendan Gregg7bf0e492016-01-27 23:17:40 -080064parser.add_argument("duration", nargs="?", default=99999999,
Sandipan Das2c2d46f2017-11-16 17:14:38 +053065 type=positive_nonzero_int,
Brendan Gregg7bf0e492016-01-27 23:17:40 -080066 help="duration of trace, in seconds")
Sandipan Das2c2d46f2017-11-16 17:14:38 +053067parser.add_argument("-m", "--min-block-time", default=1,
68 type=positive_nonzero_int,
69 help="the amount of time in microseconds over which we " +
70 "store traces (default 1)")
71parser.add_argument("-M", "--max-block-time", default=(1 << 64) - 1,
72 type=positive_nonzero_int,
73 help="the amount of time in microseconds under which we " +
74 "store traces (default U64_MAX)")
Nathan Scottcf0792f2018-02-02 16:56:50 +110075parser.add_argument("--ebpf", action="store_true",
76 help=argparse.SUPPRESS)
Brendan Gregg7bf0e492016-01-27 23:17:40 -080077args = parser.parse_args()
78folded = args.folded
79duration = int(args.duration)
80debug = 0
Brendan Gregg7bf0e492016-01-27 23:17:40 -080081if args.pid and args.useronly:
Sandipan Das2c2d46f2017-11-16 17:14:38 +053082 parser.error("use either -p or -u.")
Brendan Gregg7bf0e492016-01-27 23:17:40 -080083
84# signal handler
85def signal_ignore(signal, frame):
86 print()
87
88# define BPF program
89bpf_text = """
90#include <uapi/linux/ptrace.h>
91#include <linux/sched.h>
92
Sandipan Das2c2d46f2017-11-16 17:14:38 +053093#define MINBLOCK_US MINBLOCK_US_VALUEULL
94#define MAXBLOCK_US MAXBLOCK_US_VALUEULL
Brendan Gregg7bf0e492016-01-27 23:17:40 -080095
96struct key_t {
Sandipan Das2c2d46f2017-11-16 17:14:38 +053097 int w_k_stack_id;
Brendan Gregg7bf0e492016-01-27 23:17:40 -080098 char waker[TASK_COMM_LEN];
99 char target[TASK_COMM_LEN];
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800100};
101BPF_HASH(counts, struct key_t);
102BPF_HASH(start, u32);
Song Liu67ae6052018-02-01 14:59:24 -0800103BPF_STACK_TRACE(stack_traces, STACK_STORAGE_SIZE);
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800104
Ananth N Mavinakayanahallibdd73372021-02-03 16:18:11 +0530105static int offcpu_sched_switch() {
Hengqi Chenf0a0dc72021-05-20 22:49:25 +0800106 u64 pid_tgid = bpf_get_current_pid_tgid();
107 u32 pid = pid_tgid >> 32;
108 u32 tid = (u32)pid_tgid;
Sandipan Das3edb4532017-11-17 12:41:06 +0530109 struct task_struct *p = (struct task_struct *) bpf_get_current_task();
110 u64 ts;
111
112 if (FILTER)
113 return 0;
114
115 ts = bpf_ktime_get_ns();
Hengqi Chenf0a0dc72021-05-20 22:49:25 +0800116 start.update(&tid, &ts);
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800117 return 0;
118}
119
Ananth N Mavinakayanahallibdd73372021-02-03 16:18:11 +0530120static int wakeup(ARG0, struct task_struct *p) {
Hengqi Chenf0a0dc72021-05-20 22:49:25 +0800121 u32 pid = p->tgid;
122 u32 tid = p->pid;
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800123 u64 delta, *tsp, ts;
124
Hengqi Chenf0a0dc72021-05-20 22:49:25 +0800125 tsp = start.lookup(&tid);
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800126 if (tsp == 0)
127 return 0; // missed start
Hengqi Chenf0a0dc72021-05-20 22:49:25 +0800128 start.delete(&tid);
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800129
130 if (FILTER)
131 return 0;
132
133 // calculate delta time
134 delta = bpf_ktime_get_ns() - *tsp;
135 delta = delta / 1000;
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530136 if ((delta < MINBLOCK_US) || (delta > MAXBLOCK_US))
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800137 return 0;
138
139 struct key_t key = {};
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800140
Yonghong Song90f20862019-11-27 09:16:23 -0800141 key.w_k_stack_id = stack_traces.get_stackid(ctx, 0);
Sumanth Korikkar7f6066d2020-05-20 10:49:56 -0500142 bpf_probe_read_kernel(&key.target, sizeof(key.target), p->comm);
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800143 bpf_get_current_comm(&key.waker, sizeof(key.waker));
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800144
zcy80242fb2021-07-02 00:12:32 +0800145 counts.atomic_increment(key, delta);
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800146 return 0;
147}
148"""
Ananth N Mavinakayanahallibdd73372021-02-03 16:18:11 +0530149
150bpf_text_kprobe = """
151int offcpu(struct pt_regs *ctx) {
152 return offcpu_sched_switch();
153}
154
155int waker(struct pt_regs *ctx, struct task_struct *p) {
156 return wakeup(ctx, p);
157}
158"""
159
160bpf_text_raw_tp = """
161RAW_TRACEPOINT_PROBE(sched_switch)
162{
163 // TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next)
164 return offcpu_sched_switch();
165}
166
167RAW_TRACEPOINT_PROBE(sched_wakeup)
168{
169 // TP_PROTO(struct task_struct *p)
Wenbo Zhangeeb09292021-03-01 22:31:27 +0800170 struct task_struct *p = (struct task_struct *)ctx->args[0];
Ananth N Mavinakayanahallibdd73372021-02-03 16:18:11 +0530171 return wakeup(ctx, p);
172}
173"""
174
175is_supported_raw_tp = BPF.support_raw_tracepoint()
176if is_supported_raw_tp:
177 bpf_text += bpf_text_raw_tp
178else:
179 bpf_text += bpf_text_kprobe
180
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800181if args.pid:
182 filter = 'pid != %s' % args.pid
183elif args.useronly:
184 filter = 'p->flags & PF_KTHREAD'
185else:
186 filter = '0'
187bpf_text = bpf_text.replace('FILTER', filter)
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530188
Ananth N Mavinakayanahallibdd73372021-02-03 16:18:11 +0530189if is_supported_raw_tp:
190 arg0 = 'struct bpf_raw_tracepoint_args *ctx'
191else:
192 arg0 = 'struct pt_regs *ctx'
193bpf_text = bpf_text.replace('ARG0', arg0)
194
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530195# set stack storage size
196bpf_text = bpf_text.replace('STACK_STORAGE_SIZE', str(args.stack_storage_size))
197bpf_text = bpf_text.replace('MINBLOCK_US_VALUE', str(args.min_block_time))
198bpf_text = bpf_text.replace('MAXBLOCK_US_VALUE', str(args.max_block_time))
199
Nathan Scottcf0792f2018-02-02 16:56:50 +1100200if debug or args.ebpf:
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800201 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100202 if args.ebpf:
203 exit()
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800204
205# initialize BPF
206b = BPF(text=bpf_text)
Ananth N Mavinakayanahallibdd73372021-02-03 16:18:11 +0530207if not is_supported_raw_tp:
208 b.attach_kprobe(event="schedule", fn_name="offcpu")
209 b.attach_kprobe(event="try_to_wake_up", fn_name="waker")
210 matched = b.num_open_kprobes()
211 if matched == 0:
212 print("0 functions traced. Exiting.")
213 exit()
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800214
215# header
216if not folded:
217 print("Tracing blocked time (us) by kernel stack", end="")
218 if duration < 99999999:
219 print(" for %d secs." % duration)
220 else:
221 print("... Hit Ctrl-C to end.")
222
223# output
224while (1):
225 try:
226 sleep(duration)
227 except KeyboardInterrupt:
228 # as cleanup can take many seconds, trap Ctrl-C:
229 signal.signal(signal.SIGINT, signal_ignore)
230
231 if not folded:
232 print()
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530233 missing_stacks = 0
234 has_enomem = False
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800235 counts = b.get_table("counts")
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530236 stack_traces = b.get_table("stack_traces")
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800237 for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530238 # handle get_stackid errors
239 # check for an ENOMEM error
240 if k.w_k_stack_id == -errno.ENOMEM:
241 missing_stacks += 1
242 continue
243
244 waker_kernel_stack = [] if k.w_k_stack_id < 1 else \
245 reversed(list(stack_traces.walk(k.w_k_stack_id))[1:])
246
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800247 if folded:
248 # print folded stack output
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530249 line = \
Brenden Blancoe8001c32018-07-23 08:15:56 -0700250 [k.waker] + \
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530251 [b.ksym(addr)
252 for addr in reversed(list(waker_kernel_stack))] + \
Brenden Blancoe8001c32018-07-23 08:15:56 -0700253 [k.target]
254 printb(b"%s %d" % (b";".join(line), v.value))
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800255 else:
256 # print default multi-line stack output
Brenden Blancoe8001c32018-07-23 08:15:56 -0700257 printb(b" %-16s %s" % (b"target:", k.target))
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530258 for addr in waker_kernel_stack:
Brenden Blancoe8001c32018-07-23 08:15:56 -0700259 printb(b" %-16x %s" % (addr, b.ksym(addr)))
260 printb(b" %-16s %s" % (b"waker:", k.waker))
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800261 print(" %d\n" % v.value)
262 counts.clear()
263
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530264 if missing_stacks > 0:
265 enomem_str = " Consider increasing --stack-storage-size."
266 print("WARNING: %d stack traces could not be displayed.%s" %
267 (missing_stacks, enomem_str),
268 file=stderr)
269
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800270 if not folded:
271 print("Detaching...")
272 exit()