1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python 2*387f9dfdSAndroid Build Coastguard Worker# 3*387f9dfdSAndroid Build Coastguard Worker# wakeuptime Summarize sleep to wakeup time by waker kernel stack 4*387f9dfdSAndroid Build Coastguard Worker# For Linux, uses BCC, eBPF. 5*387f9dfdSAndroid Build Coastguard Worker# 6*387f9dfdSAndroid Build Coastguard Worker# USAGE: wakeuptime [-h] [-u] [-p PID] [-v] [-f] [duration] 7*387f9dfdSAndroid Build Coastguard Worker# 8*387f9dfdSAndroid Build Coastguard Worker# Copyright 2016 Netflix, Inc. 9*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License") 10*387f9dfdSAndroid Build Coastguard Worker# 11*387f9dfdSAndroid Build Coastguard Worker# 14-Jan-2016 Brendan Gregg Created this. 12*387f9dfdSAndroid Build Coastguard Worker# 03-Apr-2023 Rocky Xing Modified the order of stack output. 13*387f9dfdSAndroid Build Coastguard Worker# 04-Apr-2023 Rocky Xing Updated default stack storage size. 14*387f9dfdSAndroid Build Coastguard Worker 15*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function 16*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF 17*387f9dfdSAndroid Build Coastguard Workerfrom bcc.utils import printb 18*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep 19*387f9dfdSAndroid Build Coastguard Workerimport argparse 20*387f9dfdSAndroid Build Coastguard Workerimport signal 21*387f9dfdSAndroid Build Coastguard Workerimport errno 22*387f9dfdSAndroid Build Coastguard Workerfrom sys import stderr 23*387f9dfdSAndroid Build Coastguard Worker 24*387f9dfdSAndroid Build Coastguard Worker# arg validation 25*387f9dfdSAndroid Build Coastguard Workerdef positive_int(val): 26*387f9dfdSAndroid Build Coastguard Worker try: 27*387f9dfdSAndroid Build Coastguard Worker ival = int(val) 28*387f9dfdSAndroid Build Coastguard Worker except ValueError: 29*387f9dfdSAndroid Build Coastguard Worker raise argparse.ArgumentTypeError("must be an integer") 30*387f9dfdSAndroid Build Coastguard Worker 31*387f9dfdSAndroid Build Coastguard Worker if ival < 0: 32*387f9dfdSAndroid Build Coastguard Worker raise argparse.ArgumentTypeError("must be positive") 33*387f9dfdSAndroid Build Coastguard Worker return ival 34*387f9dfdSAndroid Build Coastguard Worker 35*387f9dfdSAndroid Build Coastguard Workerdef positive_nonzero_int(val): 36*387f9dfdSAndroid Build Coastguard Worker ival = positive_int(val) 37*387f9dfdSAndroid Build Coastguard Worker if ival == 0: 38*387f9dfdSAndroid Build Coastguard Worker raise argparse.ArgumentTypeError("must be nonzero") 39*387f9dfdSAndroid Build Coastguard Worker return ival 40*387f9dfdSAndroid Build Coastguard Worker 41*387f9dfdSAndroid Build Coastguard Worker# arguments 42*387f9dfdSAndroid Build Coastguard Workerexamples = """examples: 43*387f9dfdSAndroid Build Coastguard Worker ./wakeuptime # trace blocked time with waker stacks 44*387f9dfdSAndroid Build Coastguard Worker ./wakeuptime 5 # trace for 5 seconds only 45*387f9dfdSAndroid Build Coastguard Worker ./wakeuptime -f 5 # 5 seconds, and output in folded format 46*387f9dfdSAndroid Build Coastguard Worker ./wakeuptime -u # don't include kernel threads (user only) 47*387f9dfdSAndroid Build Coastguard Worker ./wakeuptime -p 185 # trace for PID 185 only 48*387f9dfdSAndroid Build Coastguard Worker""" 49*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser( 50*387f9dfdSAndroid Build Coastguard Worker description="Summarize sleep to wakeup time by waker kernel stack", 51*387f9dfdSAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter, 52*387f9dfdSAndroid Build Coastguard Worker epilog=examples) 53*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-u", "--useronly", action="store_true", 54*387f9dfdSAndroid Build Coastguard Worker help="user threads only (no kernel threads)") 55*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-p", "--pid", 56*387f9dfdSAndroid Build Coastguard Worker type=positive_int, 57*387f9dfdSAndroid Build Coastguard Worker help="trace this PID only") 58*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-v", "--verbose", action="store_true", 59*387f9dfdSAndroid Build Coastguard Worker help="show raw addresses") 60*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-f", "--folded", action="store_true", 61*387f9dfdSAndroid Build Coastguard Worker help="output folded format") 62*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--stack-storage-size", default=16384, 63*387f9dfdSAndroid Build Coastguard Worker type=positive_nonzero_int, 64*387f9dfdSAndroid Build Coastguard Worker help="the number of unique stack traces that can be stored and " 65*387f9dfdSAndroid Build Coastguard Worker "displayed (default 16384)") 66*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("duration", nargs="?", default=99999999, 67*387f9dfdSAndroid Build Coastguard Worker type=positive_nonzero_int, 68*387f9dfdSAndroid Build Coastguard Worker help="duration of trace, in seconds") 69*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-m", "--min-block-time", default=1, 70*387f9dfdSAndroid Build Coastguard Worker type=positive_nonzero_int, 71*387f9dfdSAndroid Build Coastguard Worker help="the amount of time in microseconds over which we " + 72*387f9dfdSAndroid Build Coastguard Worker "store traces (default 1)") 73*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-M", "--max-block-time", default=(1 << 64) - 1, 74*387f9dfdSAndroid Build Coastguard Worker type=positive_nonzero_int, 75*387f9dfdSAndroid Build Coastguard Worker help="the amount of time in microseconds under which we " + 76*387f9dfdSAndroid Build Coastguard Worker "store traces (default U64_MAX)") 77*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true", 78*387f9dfdSAndroid Build Coastguard Worker help=argparse.SUPPRESS) 79*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args() 80*387f9dfdSAndroid Build Coastguard Workerfolded = args.folded 81*387f9dfdSAndroid Build Coastguard Workerduration = int(args.duration) 82*387f9dfdSAndroid Build Coastguard Workerdebug = 0 83*387f9dfdSAndroid Build Coastguard Workerif args.pid and args.useronly: 84*387f9dfdSAndroid Build Coastguard Worker parser.error("use either -p or -u.") 85*387f9dfdSAndroid Build Coastguard Worker 86*387f9dfdSAndroid Build Coastguard Worker# signal handler 87*387f9dfdSAndroid Build Coastguard Workerdef signal_ignore(signal, frame): 88*387f9dfdSAndroid Build Coastguard Worker print() 89*387f9dfdSAndroid Build Coastguard Worker 90*387f9dfdSAndroid Build Coastguard Worker# define BPF program 91*387f9dfdSAndroid Build Coastguard Workerbpf_text = """ 92*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 93*387f9dfdSAndroid Build Coastguard Worker#include <linux/sched.h> 94*387f9dfdSAndroid Build Coastguard Worker 95*387f9dfdSAndroid Build Coastguard Worker#define MINBLOCK_US MINBLOCK_US_VALUEULL 96*387f9dfdSAndroid Build Coastguard Worker#define MAXBLOCK_US MAXBLOCK_US_VALUEULL 97*387f9dfdSAndroid Build Coastguard Worker 98*387f9dfdSAndroid Build Coastguard Workerstruct key_t { 99*387f9dfdSAndroid Build Coastguard Worker int w_k_stack_id; 100*387f9dfdSAndroid Build Coastguard Worker char waker[TASK_COMM_LEN]; 101*387f9dfdSAndroid Build Coastguard Worker char target[TASK_COMM_LEN]; 102*387f9dfdSAndroid Build Coastguard Worker}; 103*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(counts, struct key_t); 104*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(start, u32); 105*387f9dfdSAndroid Build Coastguard WorkerBPF_STACK_TRACE(stack_traces, STACK_STORAGE_SIZE); 106*387f9dfdSAndroid Build Coastguard Worker 107*387f9dfdSAndroid Build Coastguard Workerstatic int offcpu_sched_switch() { 108*387f9dfdSAndroid Build Coastguard Worker u64 pid_tgid = bpf_get_current_pid_tgid(); 109*387f9dfdSAndroid Build Coastguard Worker u32 pid = pid_tgid >> 32; 110*387f9dfdSAndroid Build Coastguard Worker u32 tid = (u32)pid_tgid; 111*387f9dfdSAndroid Build Coastguard Worker struct task_struct *p = (struct task_struct *) bpf_get_current_task(); 112*387f9dfdSAndroid Build Coastguard Worker u64 ts; 113*387f9dfdSAndroid Build Coastguard Worker 114*387f9dfdSAndroid Build Coastguard Worker if (FILTER) 115*387f9dfdSAndroid Build Coastguard Worker return 0; 116*387f9dfdSAndroid Build Coastguard Worker 117*387f9dfdSAndroid Build Coastguard Worker ts = bpf_ktime_get_ns(); 118*387f9dfdSAndroid Build Coastguard Worker start.update(&tid, &ts); 119*387f9dfdSAndroid Build Coastguard Worker return 0; 120*387f9dfdSAndroid Build Coastguard Worker} 121*387f9dfdSAndroid Build Coastguard Worker 122*387f9dfdSAndroid Build Coastguard Workerstatic int wakeup(ARG0, struct task_struct *p) { 123*387f9dfdSAndroid Build Coastguard Worker u32 pid = p->tgid; 124*387f9dfdSAndroid Build Coastguard Worker u32 tid = p->pid; 125*387f9dfdSAndroid Build Coastguard Worker u64 delta, *tsp, ts; 126*387f9dfdSAndroid Build Coastguard Worker 127*387f9dfdSAndroid Build Coastguard Worker tsp = start.lookup(&tid); 128*387f9dfdSAndroid Build Coastguard Worker if (tsp == 0) 129*387f9dfdSAndroid Build Coastguard Worker return 0; // missed start 130*387f9dfdSAndroid Build Coastguard Worker start.delete(&tid); 131*387f9dfdSAndroid Build Coastguard Worker 132*387f9dfdSAndroid Build Coastguard Worker if (FILTER) 133*387f9dfdSAndroid Build Coastguard Worker return 0; 134*387f9dfdSAndroid Build Coastguard Worker 135*387f9dfdSAndroid Build Coastguard Worker // calculate delta time 136*387f9dfdSAndroid Build Coastguard Worker delta = bpf_ktime_get_ns() - *tsp; 137*387f9dfdSAndroid Build Coastguard Worker delta = delta / 1000; 138*387f9dfdSAndroid Build Coastguard Worker if ((delta < MINBLOCK_US) || (delta > MAXBLOCK_US)) 139*387f9dfdSAndroid Build Coastguard Worker return 0; 140*387f9dfdSAndroid Build Coastguard Worker 141*387f9dfdSAndroid Build Coastguard Worker struct key_t key = {}; 142*387f9dfdSAndroid Build Coastguard Worker 143*387f9dfdSAndroid Build Coastguard Worker key.w_k_stack_id = stack_traces.get_stackid(ctx, 0); 144*387f9dfdSAndroid Build Coastguard Worker bpf_probe_read_kernel(&key.target, sizeof(key.target), p->comm); 145*387f9dfdSAndroid Build Coastguard Worker bpf_get_current_comm(&key.waker, sizeof(key.waker)); 146*387f9dfdSAndroid Build Coastguard Worker 147*387f9dfdSAndroid Build Coastguard Worker counts.atomic_increment(key, delta); 148*387f9dfdSAndroid Build Coastguard Worker return 0; 149*387f9dfdSAndroid Build Coastguard Worker} 150*387f9dfdSAndroid Build Coastguard Worker""" 151*387f9dfdSAndroid Build Coastguard Worker 152*387f9dfdSAndroid Build Coastguard Workerbpf_text_kprobe = """ 153*387f9dfdSAndroid Build Coastguard Workerint offcpu(struct pt_regs *ctx) { 154*387f9dfdSAndroid Build Coastguard Worker return offcpu_sched_switch(); 155*387f9dfdSAndroid Build Coastguard Worker} 156*387f9dfdSAndroid Build Coastguard Worker 157*387f9dfdSAndroid Build Coastguard Workerint waker(struct pt_regs *ctx, struct task_struct *p) { 158*387f9dfdSAndroid Build Coastguard Worker return wakeup(ctx, p); 159*387f9dfdSAndroid Build Coastguard Worker} 160*387f9dfdSAndroid Build Coastguard Worker""" 161*387f9dfdSAndroid Build Coastguard Worker 162*387f9dfdSAndroid Build Coastguard Workerbpf_text_raw_tp = """ 163*387f9dfdSAndroid Build Coastguard WorkerRAW_TRACEPOINT_PROBE(sched_switch) 164*387f9dfdSAndroid Build Coastguard Worker{ 165*387f9dfdSAndroid Build Coastguard Worker // TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next) 166*387f9dfdSAndroid Build Coastguard Worker return offcpu_sched_switch(); 167*387f9dfdSAndroid Build Coastguard Worker} 168*387f9dfdSAndroid Build Coastguard Worker 169*387f9dfdSAndroid Build Coastguard WorkerRAW_TRACEPOINT_PROBE(sched_wakeup) 170*387f9dfdSAndroid Build Coastguard Worker{ 171*387f9dfdSAndroid Build Coastguard Worker // TP_PROTO(struct task_struct *p) 172*387f9dfdSAndroid Build Coastguard Worker struct task_struct *p = (struct task_struct *)ctx->args[0]; 173*387f9dfdSAndroid Build Coastguard Worker return wakeup(ctx, p); 174*387f9dfdSAndroid Build Coastguard Worker} 175*387f9dfdSAndroid Build Coastguard Worker""" 176*387f9dfdSAndroid Build Coastguard Worker 177*387f9dfdSAndroid Build Coastguard Workeris_supported_raw_tp = BPF.support_raw_tracepoint() 178*387f9dfdSAndroid Build Coastguard Workerif is_supported_raw_tp: 179*387f9dfdSAndroid Build Coastguard Worker bpf_text += bpf_text_raw_tp 180*387f9dfdSAndroid Build Coastguard Workerelse: 181*387f9dfdSAndroid Build Coastguard Worker bpf_text += bpf_text_kprobe 182*387f9dfdSAndroid Build Coastguard Worker 183*387f9dfdSAndroid Build Coastguard Workerif args.pid: 184*387f9dfdSAndroid Build Coastguard Worker filter = 'pid != %s' % args.pid 185*387f9dfdSAndroid Build Coastguard Workerelif args.useronly: 186*387f9dfdSAndroid Build Coastguard Worker filter = 'p->flags & PF_KTHREAD' 187*387f9dfdSAndroid Build Coastguard Workerelse: 188*387f9dfdSAndroid Build Coastguard Worker filter = '0' 189*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('FILTER', filter) 190*387f9dfdSAndroid Build Coastguard Worker 191*387f9dfdSAndroid Build Coastguard Workerif is_supported_raw_tp: 192*387f9dfdSAndroid Build Coastguard Worker arg0 = 'struct bpf_raw_tracepoint_args *ctx' 193*387f9dfdSAndroid Build Coastguard Workerelse: 194*387f9dfdSAndroid Build Coastguard Worker arg0 = 'struct pt_regs *ctx' 195*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('ARG0', arg0) 196*387f9dfdSAndroid Build Coastguard Worker 197*387f9dfdSAndroid Build Coastguard Worker# set stack storage size 198*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('STACK_STORAGE_SIZE', str(args.stack_storage_size)) 199*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('MINBLOCK_US_VALUE', str(args.min_block_time)) 200*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('MAXBLOCK_US_VALUE', str(args.max_block_time)) 201*387f9dfdSAndroid Build Coastguard Worker 202*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf: 203*387f9dfdSAndroid Build Coastguard Worker print(bpf_text) 204*387f9dfdSAndroid Build Coastguard Worker if args.ebpf: 205*387f9dfdSAndroid Build Coastguard Worker exit() 206*387f9dfdSAndroid Build Coastguard Worker 207*387f9dfdSAndroid Build Coastguard Worker# initialize BPF 208*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text) 209*387f9dfdSAndroid Build Coastguard Workerif not is_supported_raw_tp: 210*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event="schedule", fn_name="offcpu") 211*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event="try_to_wake_up", fn_name="waker") 212*387f9dfdSAndroid Build Coastguard Worker matched = b.num_open_kprobes() 213*387f9dfdSAndroid Build Coastguard Worker if matched == 0: 214*387f9dfdSAndroid Build Coastguard Worker print("0 functions traced. Exiting.") 215*387f9dfdSAndroid Build Coastguard Worker exit() 216*387f9dfdSAndroid Build Coastguard Worker 217*387f9dfdSAndroid Build Coastguard Worker# header 218*387f9dfdSAndroid Build Coastguard Workerif not folded: 219*387f9dfdSAndroid Build Coastguard Worker print("Tracing blocked time (us) by kernel stack", end="") 220*387f9dfdSAndroid Build Coastguard Worker if duration < 99999999: 221*387f9dfdSAndroid Build Coastguard Worker print(" for %d secs." % duration) 222*387f9dfdSAndroid Build Coastguard Worker else: 223*387f9dfdSAndroid Build Coastguard Worker print("... Hit Ctrl-C to end.") 224*387f9dfdSAndroid Build Coastguard Worker 225*387f9dfdSAndroid Build Coastguard Worker# output 226*387f9dfdSAndroid Build Coastguard Workerwhile (1): 227*387f9dfdSAndroid Build Coastguard Worker try: 228*387f9dfdSAndroid Build Coastguard Worker sleep(duration) 229*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 230*387f9dfdSAndroid Build Coastguard Worker # as cleanup can take many seconds, trap Ctrl-C: 231*387f9dfdSAndroid Build Coastguard Worker signal.signal(signal.SIGINT, signal_ignore) 232*387f9dfdSAndroid Build Coastguard Worker 233*387f9dfdSAndroid Build Coastguard Worker if not folded: 234*387f9dfdSAndroid Build Coastguard Worker print() 235*387f9dfdSAndroid Build Coastguard Worker missing_stacks = 0 236*387f9dfdSAndroid Build Coastguard Worker has_enomem = False 237*387f9dfdSAndroid Build Coastguard Worker counts = b.get_table("counts") 238*387f9dfdSAndroid Build Coastguard Worker stack_traces = b.get_table("stack_traces") 239*387f9dfdSAndroid Build Coastguard Worker for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): 240*387f9dfdSAndroid Build Coastguard Worker # handle get_stackid errors 241*387f9dfdSAndroid Build Coastguard Worker # check for an ENOMEM error 242*387f9dfdSAndroid Build Coastguard Worker if k.w_k_stack_id == -errno.ENOMEM: 243*387f9dfdSAndroid Build Coastguard Worker missing_stacks += 1 244*387f9dfdSAndroid Build Coastguard Worker continue 245*387f9dfdSAndroid Build Coastguard Worker 246*387f9dfdSAndroid Build Coastguard Worker waker_kernel_stack = [] if k.w_k_stack_id < 1 else \ 247*387f9dfdSAndroid Build Coastguard Worker list(stack_traces.walk(k.w_k_stack_id))[1:] 248*387f9dfdSAndroid Build Coastguard Worker 249*387f9dfdSAndroid Build Coastguard Worker if folded: 250*387f9dfdSAndroid Build Coastguard Worker # print folded stack output 251*387f9dfdSAndroid Build Coastguard Worker line = \ 252*387f9dfdSAndroid Build Coastguard Worker [k.waker] + \ 253*387f9dfdSAndroid Build Coastguard Worker [b.ksym(addr) 254*387f9dfdSAndroid Build Coastguard Worker for addr in reversed(waker_kernel_stack)] + \ 255*387f9dfdSAndroid Build Coastguard Worker [k.target] 256*387f9dfdSAndroid Build Coastguard Worker printb(b"%s %d" % (b";".join(line), v.value)) 257*387f9dfdSAndroid Build Coastguard Worker else: 258*387f9dfdSAndroid Build Coastguard Worker # print default multi-line stack output 259*387f9dfdSAndroid Build Coastguard Worker printb(b" %-16s %s" % (b"target:", k.target)) 260*387f9dfdSAndroid Build Coastguard Worker for addr in waker_kernel_stack: 261*387f9dfdSAndroid Build Coastguard Worker printb(b" %-16x %s" % (addr, b.ksym(addr))) 262*387f9dfdSAndroid Build Coastguard Worker printb(b" %-16s %s" % (b"waker:", k.waker)) 263*387f9dfdSAndroid Build Coastguard Worker print(" %d\n" % v.value) 264*387f9dfdSAndroid Build Coastguard Worker counts.clear() 265*387f9dfdSAndroid Build Coastguard Worker 266*387f9dfdSAndroid Build Coastguard Worker if missing_stacks > 0: 267*387f9dfdSAndroid Build Coastguard Worker enomem_str = " Consider increasing --stack-storage-size." 268*387f9dfdSAndroid Build Coastguard Worker print("WARNING: %d stack traces could not be displayed.%s" % 269*387f9dfdSAndroid Build Coastguard Worker (missing_stacks, enomem_str), 270*387f9dfdSAndroid Build Coastguard Worker file=stderr) 271*387f9dfdSAndroid Build Coastguard Worker 272*387f9dfdSAndroid Build Coastguard Worker if not folded: 273*387f9dfdSAndroid Build Coastguard Worker print("Detaching...") 274*387f9dfdSAndroid Build Coastguard Worker exit() 275