blob: cf0ca7dcd226fddb19d4e35cddde1b5223af6571 [file] [log] [blame]
Brendan Gregg7bf0e492016-01-27 23:17:40 -08001#!/usr/bin/python
2#
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
15from time import sleep, strftime
16import argparse
17import signal
Sandipan Das2c2d46f2017-11-16 17:14:38 +053018import errno
19from sys import stderr
20
21# arg validation
22def positive_int(val):
23 try:
24 ival = int(val)
25 except ValueError:
26 raise argparse.ArgumentTypeError("must be an integer")
27
28 if ival < 0:
29 raise argparse.ArgumentTypeError("must be positive")
30 return ival
31
32def positive_nonzero_int(val):
33 ival = positive_int(val)
34 if ival == 0:
35 raise argparse.ArgumentTypeError("must be nonzero")
36 return ival
Brendan Gregg7bf0e492016-01-27 23:17:40 -080037
38# arguments
39examples = """examples:
40 ./wakeuptime # trace blocked time with waker stacks
41 ./wakeuptime 5 # trace for 5 seconds only
42 ./wakeuptime -f 5 # 5 seconds, and output in folded format
43 ./wakeuptime -u # don't include kernel threads (user only)
44 ./wakeuptime -p 185 # trace fo PID 185 only
45"""
46parser = argparse.ArgumentParser(
47 description="Summarize sleep to wakeup time by waker kernel stack",
48 formatter_class=argparse.RawDescriptionHelpFormatter,
49 epilog=examples)
50parser.add_argument("-u", "--useronly", action="store_true",
51 help="user threads only (no kernel threads)")
52parser.add_argument("-p", "--pid",
Sandipan Das2c2d46f2017-11-16 17:14:38 +053053 type=positive_int,
Brendan Gregg7bf0e492016-01-27 23:17:40 -080054 help="trace this PID only")
55parser.add_argument("-v", "--verbose", action="store_true",
56 help="show raw addresses")
57parser.add_argument("-f", "--folded", action="store_true",
58 help="output folded format")
Sandipan Das2c2d46f2017-11-16 17:14:38 +053059parser.add_argument("--stack-storage-size", default=1024,
60 type=positive_nonzero_int,
61 help="the number of unique stack traces that can be stored and "
62 "displayed (default 1024)")
Brendan Gregg7bf0e492016-01-27 23:17:40 -080063parser.add_argument("duration", nargs="?", default=99999999,
Sandipan Das2c2d46f2017-11-16 17:14:38 +053064 type=positive_nonzero_int,
Brendan Gregg7bf0e492016-01-27 23:17:40 -080065 help="duration of trace, in seconds")
Sandipan Das2c2d46f2017-11-16 17:14:38 +053066parser.add_argument("-m", "--min-block-time", default=1,
67 type=positive_nonzero_int,
68 help="the amount of time in microseconds over which we " +
69 "store traces (default 1)")
70parser.add_argument("-M", "--max-block-time", default=(1 << 64) - 1,
71 type=positive_nonzero_int,
72 help="the amount of time in microseconds under which we " +
73 "store traces (default U64_MAX)")
Nathan Scottcf0792f2018-02-02 16:56:50 +110074parser.add_argument("--ebpf", action="store_true",
75 help=argparse.SUPPRESS)
Brendan Gregg7bf0e492016-01-27 23:17:40 -080076args = parser.parse_args()
77folded = args.folded
78duration = int(args.duration)
79debug = 0
Brendan Gregg7bf0e492016-01-27 23:17:40 -080080if args.pid and args.useronly:
Sandipan Das2c2d46f2017-11-16 17:14:38 +053081 parser.error("use either -p or -u.")
Brendan Gregg7bf0e492016-01-27 23:17:40 -080082
83# signal handler
84def signal_ignore(signal, frame):
85 print()
86
87# define BPF program
88bpf_text = """
89#include <uapi/linux/ptrace.h>
90#include <linux/sched.h>
91
Sandipan Das2c2d46f2017-11-16 17:14:38 +053092#define MINBLOCK_US MINBLOCK_US_VALUEULL
93#define MAXBLOCK_US MAXBLOCK_US_VALUEULL
Brendan Gregg7bf0e492016-01-27 23:17:40 -080094
95struct key_t {
Sandipan Das2c2d46f2017-11-16 17:14:38 +053096 int w_k_stack_id;
Brendan Gregg7bf0e492016-01-27 23:17:40 -080097 char waker[TASK_COMM_LEN];
98 char target[TASK_COMM_LEN];
Brendan Gregg7bf0e492016-01-27 23:17:40 -080099};
100BPF_HASH(counts, struct key_t);
101BPF_HASH(start, u32);
Song Liu67ae6052018-02-01 14:59:24 -0800102BPF_STACK_TRACE(stack_traces, STACK_STORAGE_SIZE);
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800103
104int offcpu(struct pt_regs *ctx) {
105 u32 pid = bpf_get_current_pid_tgid();
Sandipan Das3edb4532017-11-17 12:41:06 +0530106 struct task_struct *p = (struct task_struct *) bpf_get_current_task();
107 u64 ts;
108
109 if (FILTER)
110 return 0;
111
112 ts = bpf_ktime_get_ns();
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800113 start.update(&pid, &ts);
114 return 0;
115}
116
117int waker(struct pt_regs *ctx, struct task_struct *p) {
118 u32 pid = p->pid;
119 u64 delta, *tsp, ts;
120
121 tsp = start.lookup(&pid);
122 if (tsp == 0)
123 return 0; // missed start
124 start.delete(&pid);
125
126 if (FILTER)
127 return 0;
128
129 // calculate delta time
130 delta = bpf_ktime_get_ns() - *tsp;
131 delta = delta / 1000;
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530132 if ((delta < MINBLOCK_US) || (delta > MAXBLOCK_US))
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800133 return 0;
134
135 struct key_t key = {};
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530136 u64 zero = 0, *val;
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800137
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530138 key.w_k_stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID);
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800139 bpf_probe_read(&key.target, sizeof(key.target), p->comm);
140 bpf_get_current_comm(&key.waker, sizeof(key.waker));
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800141
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800142 val = counts.lookup_or_init(&key, &zero);
143 (*val) += delta;
144 return 0;
145}
146"""
147if args.pid:
148 filter = 'pid != %s' % args.pid
149elif args.useronly:
150 filter = 'p->flags & PF_KTHREAD'
151else:
152 filter = '0'
153bpf_text = bpf_text.replace('FILTER', filter)
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530154
155# set stack storage size
156bpf_text = bpf_text.replace('STACK_STORAGE_SIZE', str(args.stack_storage_size))
157bpf_text = bpf_text.replace('MINBLOCK_US_VALUE', str(args.min_block_time))
158bpf_text = bpf_text.replace('MAXBLOCK_US_VALUE', str(args.max_block_time))
159
Nathan Scottcf0792f2018-02-02 16:56:50 +1100160if debug or args.ebpf:
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800161 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100162 if args.ebpf:
163 exit()
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800164
165# initialize BPF
166b = BPF(text=bpf_text)
167b.attach_kprobe(event="schedule", fn_name="offcpu")
168b.attach_kprobe(event="try_to_wake_up", fn_name="waker")
169matched = b.num_open_kprobes()
170if matched == 0:
171 print("0 functions traced. Exiting.")
172 exit()
173
174# header
175if not folded:
176 print("Tracing blocked time (us) by kernel stack", end="")
177 if duration < 99999999:
178 print(" for %d secs." % duration)
179 else:
180 print("... Hit Ctrl-C to end.")
181
182# output
183while (1):
184 try:
185 sleep(duration)
186 except KeyboardInterrupt:
187 # as cleanup can take many seconds, trap Ctrl-C:
188 signal.signal(signal.SIGINT, signal_ignore)
189
190 if not folded:
191 print()
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530192 missing_stacks = 0
193 has_enomem = False
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800194 counts = b.get_table("counts")
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530195 stack_traces = b.get_table("stack_traces")
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800196 for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530197 # handle get_stackid errors
198 # check for an ENOMEM error
199 if k.w_k_stack_id == -errno.ENOMEM:
200 missing_stacks += 1
201 continue
202
203 waker_kernel_stack = [] if k.w_k_stack_id < 1 else \
204 reversed(list(stack_traces.walk(k.w_k_stack_id))[1:])
205
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800206 if folded:
207 # print folded stack output
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530208 line = \
209 [k.waker.decode()] + \
210 [b.ksym(addr)
211 for addr in reversed(list(waker_kernel_stack))] + \
212 [k.target.decode()]
213 print("%s %d" % (";".join(line), v.value))
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800214 else:
215 # print default multi-line stack output
Rafael F78948e42017-03-26 14:54:25 +0200216 print(" %-16s %s" % ("target:", k.target.decode()))
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530217 for addr in waker_kernel_stack:
218 print(" %-16x %s" % (addr, b.ksym(addr)))
Rafael F78948e42017-03-26 14:54:25 +0200219 print(" %-16s %s" % ("waker:", k.waker.decode()))
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800220 print(" %d\n" % v.value)
221 counts.clear()
222
Sandipan Das2c2d46f2017-11-16 17:14:38 +0530223 if missing_stacks > 0:
224 enomem_str = " Consider increasing --stack-storage-size."
225 print("WARNING: %d stack traces could not be displayed.%s" %
226 (missing_stacks, enomem_str),
227 file=stderr)
228
Brendan Gregg7bf0e492016-01-27 23:17:40 -0800229 if not folded:
230 print("Detaching...")
231 exit()