1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python 2*387f9dfdSAndroid Build Coastguard Worker# @lint-avoid-python-3-compatibility-imports 3*387f9dfdSAndroid Build Coastguard Worker# 4*387f9dfdSAndroid Build Coastguard Worker# cpuunclaimed Sample CPU run queues and calculate unclaimed idle CPU. 5*387f9dfdSAndroid Build Coastguard Worker# For Linux, uses BCC, eBPF. 6*387f9dfdSAndroid Build Coastguard Worker# 7*387f9dfdSAndroid Build Coastguard Worker# This samples the length of the run queues and determine when there are idle 8*387f9dfdSAndroid Build Coastguard Worker# CPUs, yet queued threads waiting their turn. Report the amount of idle 9*387f9dfdSAndroid Build Coastguard Worker# (yet unclaimed by waiting threads) CPU as a system-wide percentage. 10*387f9dfdSAndroid Build Coastguard Worker# 11*387f9dfdSAndroid Build Coastguard Worker# This situation can happen for a number of reasons: 12*387f9dfdSAndroid Build Coastguard Worker# 13*387f9dfdSAndroid Build Coastguard Worker# - An application has been bound to some, but not all, CPUs, and has runnable 14*387f9dfdSAndroid Build Coastguard Worker# threads that cannot migrate to other CPUs due to this configuration. 15*387f9dfdSAndroid Build Coastguard Worker# - CPU affinity: an optimization that leaves threads on CPUs where the CPU 16*387f9dfdSAndroid Build Coastguard Worker# caches are warm, even if this means short periods of waiting while other 17*387f9dfdSAndroid Build Coastguard Worker# CPUs are idle. The wait period is tunale (see sysctl, kernel.sched*). 18*387f9dfdSAndroid Build Coastguard Worker# - Scheduler bugs. 19*387f9dfdSAndroid Build Coastguard Worker# 20*387f9dfdSAndroid Build Coastguard Worker# An unclaimed idle of < 1% is likely to be CPU affinity, and not usually a 21*387f9dfdSAndroid Build Coastguard Worker# cause for concern. By leaving the CPU idle, overall throughput of the system 22*387f9dfdSAndroid Build Coastguard Worker# may be improved. This tool is best for identifying larger issues, > 2%, due 23*387f9dfdSAndroid Build Coastguard Worker# to the coarseness of its 99 Hertz samples. 24*387f9dfdSAndroid Build Coastguard Worker# 25*387f9dfdSAndroid Build Coastguard Worker# This is an experimental tool that currently works by use of sampling to 26*387f9dfdSAndroid Build Coastguard Worker# keep overheads low. Tool assumptions: 27*387f9dfdSAndroid Build Coastguard Worker# 28*387f9dfdSAndroid Build Coastguard Worker# - CPU samples consistently fire around the same offset. There will sometimes 29*387f9dfdSAndroid Build Coastguard Worker# be a lag as a sample is delayed by higher-priority interrupts, but it is 30*387f9dfdSAndroid Build Coastguard Worker# assumed the subsequent samples will catch up to the expected offsets (as 31*387f9dfdSAndroid Build Coastguard Worker# is seen in practice). You can use -J to inspect sample offsets. Some 32*387f9dfdSAndroid Build Coastguard Worker# systems can power down CPUs when idle, and when they wake up again they 33*387f9dfdSAndroid Build Coastguard Worker# may begin firing at a skewed offset: this tool will detect the skew, print 34*387f9dfdSAndroid Build Coastguard Worker# an error, and exit. 35*387f9dfdSAndroid Build Coastguard Worker# - All CPUs are online (see ncpu). 36*387f9dfdSAndroid Build Coastguard Worker# 37*387f9dfdSAndroid Build Coastguard Worker# If this identifies unclaimed CPU, you can double check it by dumping raw 38*387f9dfdSAndroid Build Coastguard Worker# samples (-j), as well as using other tracing tools to instrument scheduler 39*387f9dfdSAndroid Build Coastguard Worker# events (although this latter approach has much higher overhead). 40*387f9dfdSAndroid Build Coastguard Worker# 41*387f9dfdSAndroid Build Coastguard Worker# This tool passes all sampled events to user space for post processing. 42*387f9dfdSAndroid Build Coastguard Worker# I originally wrote this to do the calculations entirerly in kernel context, 43*387f9dfdSAndroid Build Coastguard Worker# and only pass a summary. That involves a number of challenges, and the 44*387f9dfdSAndroid Build Coastguard Worker# overhead savings may not outweigh the caveats. You can see my WIP here: 45*387f9dfdSAndroid Build Coastguard Worker# https://gist.github.com/brendangregg/731cf2ce54bf1f9a19d4ccd397625ad9 46*387f9dfdSAndroid Build Coastguard Worker# 47*387f9dfdSAndroid Build Coastguard Worker# USAGE: cpuunclaimed [-h] [-j] [-J] [-T] [interval] [count] 48*387f9dfdSAndroid Build Coastguard Worker# 49*387f9dfdSAndroid Build Coastguard Worker# If you see "Lost 1881 samples" warnings, try increasing wakeup_hz. 50*387f9dfdSAndroid Build Coastguard Worker# 51*387f9dfdSAndroid Build Coastguard Worker# REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support). Under tools/old is 52*387f9dfdSAndroid Build Coastguard Worker# a version of this tool that may work on Linux 4.6 - 4.8. 53*387f9dfdSAndroid Build Coastguard Worker# 54*387f9dfdSAndroid Build Coastguard Worker# Copyright 2016 Netflix, Inc. 55*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License") 56*387f9dfdSAndroid Build Coastguard Worker# 57*387f9dfdSAndroid Build Coastguard Worker# 20-Dec-2016 Brendan Gregg Created this. 58*387f9dfdSAndroid Build Coastguard Worker 59*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function 60*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF, PerfType, PerfSWConfig 61*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime 62*387f9dfdSAndroid Build Coastguard Workerimport argparse 63*387f9dfdSAndroid Build Coastguard Workerimport multiprocessing 64*387f9dfdSAndroid Build Coastguard Workerfrom os import getpid, system, open, close, dup, unlink, O_WRONLY 65*387f9dfdSAndroid Build Coastguard Workerfrom tempfile import NamedTemporaryFile 66*387f9dfdSAndroid Build Coastguard Worker 67*387f9dfdSAndroid Build Coastguard Worker# arguments 68*387f9dfdSAndroid Build Coastguard Workerexamples = """examples: 69*387f9dfdSAndroid Build Coastguard Worker ./cpuunclaimed # sample and calculate unclaimed idle CPUs, 70*387f9dfdSAndroid Build Coastguard Worker # output every 1 second (default) 71*387f9dfdSAndroid Build Coastguard Worker ./cpuunclaimed 5 10 # print 5 second summaries, 10 times 72*387f9dfdSAndroid Build Coastguard Worker ./cpuunclaimed -T 1 # 1s summaries and timestamps 73*387f9dfdSAndroid Build Coastguard Worker ./cpuunclaimed -j # raw dump of all samples (verbose), CSV 74*387f9dfdSAndroid Build Coastguard Worker""" 75*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser( 76*387f9dfdSAndroid Build Coastguard Worker description="Sample CPU run queues and calculate unclaimed idle CPU", 77*387f9dfdSAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter, 78*387f9dfdSAndroid Build Coastguard Worker epilog=examples) 79*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-j", "--csv", action="store_true", 80*387f9dfdSAndroid Build Coastguard Worker help="print sample summaries (verbose) as comma-separated values") 81*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-J", "--fullcsv", action="store_true", 82*387f9dfdSAndroid Build Coastguard Worker help="print sample summaries with extra fields: CPU sample offsets") 83*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-T", "--timestamp", action="store_true", 84*387f9dfdSAndroid Build Coastguard Worker help="include timestamp on output") 85*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("interval", nargs="?", default=-1, 86*387f9dfdSAndroid Build Coastguard Worker help="output interval, in seconds") 87*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("count", nargs="?", default=99999999, 88*387f9dfdSAndroid Build Coastguard Worker help="number of outputs") 89*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true", 90*387f9dfdSAndroid Build Coastguard Worker help=argparse.SUPPRESS) 91*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args() 92*387f9dfdSAndroid Build Coastguard Workercountdown = int(args.count) 93*387f9dfdSAndroid Build Coastguard Workerfrequency = 99 94*387f9dfdSAndroid Build Coastguard Workerdobind = 1 95*387f9dfdSAndroid Build Coastguard Workerwakeup_hz = 10 # frequency to read buffers 96*387f9dfdSAndroid Build Coastguard Workerwakeup_s = float(1) / wakeup_hz 97*387f9dfdSAndroid Build Coastguard Workerncpu = multiprocessing.cpu_count() # assume all are online 98*387f9dfdSAndroid Build Coastguard Workerdebug = 0 99*387f9dfdSAndroid Build Coastguard Worker 100*387f9dfdSAndroid Build Coastguard Worker# Linux 4.15 introduced a new field runnable_weight 101*387f9dfdSAndroid Build Coastguard Worker# in linux_src:kernel/sched/sched.h as 102*387f9dfdSAndroid Build Coastguard Worker# struct cfs_rq { 103*387f9dfdSAndroid Build Coastguard Worker# struct load_weight load; 104*387f9dfdSAndroid Build Coastguard Worker# unsigned long runnable_weight; 105*387f9dfdSAndroid Build Coastguard Worker# unsigned int nr_running, h_nr_running; 106*387f9dfdSAndroid Build Coastguard Worker# ...... 107*387f9dfdSAndroid Build Coastguard Worker# } 108*387f9dfdSAndroid Build Coastguard Worker# and this tool requires to access nr_running to get 109*387f9dfdSAndroid Build Coastguard Worker# runqueue len information. 110*387f9dfdSAndroid Build Coastguard Worker# 111*387f9dfdSAndroid Build Coastguard Worker# The commit which introduces cfs_rq->runnable_weight 112*387f9dfdSAndroid Build Coastguard Worker# field also introduces the field sched_entity->runnable_weight 113*387f9dfdSAndroid Build Coastguard Worker# where sched_entity is defined in linux_src:include/linux/sched.h. 114*387f9dfdSAndroid Build Coastguard Worker# 115*387f9dfdSAndroid Build Coastguard Worker# To cope with pre-4.15 and 4.15/post-4.15 releases, 116*387f9dfdSAndroid Build Coastguard Worker# we run a simple BPF program to detect whether 117*387f9dfdSAndroid Build Coastguard Worker# field sched_entity->runnable_weight exists. The existence of 118*387f9dfdSAndroid Build Coastguard Worker# this field should infer the existence of cfs_rq->runnable_weight. 119*387f9dfdSAndroid Build Coastguard Worker# 120*387f9dfdSAndroid Build Coastguard Worker# This will need maintenance as the relationship between these 121*387f9dfdSAndroid Build Coastguard Worker# two fields may change in the future. 122*387f9dfdSAndroid Build Coastguard Worker# 123*387f9dfdSAndroid Build Coastguard Workerdef check_runnable_weight_field(): 124*387f9dfdSAndroid Build Coastguard Worker # Define the bpf program for checking purpose 125*387f9dfdSAndroid Build Coastguard Worker bpf_check_text = """ 126*387f9dfdSAndroid Build Coastguard Worker#include <linux/sched.h> 127*387f9dfdSAndroid Build Coastguard Workerunsigned long dummy(struct sched_entity *entity) 128*387f9dfdSAndroid Build Coastguard Worker{ 129*387f9dfdSAndroid Build Coastguard Worker return entity->runnable_weight; 130*387f9dfdSAndroid Build Coastguard Worker} 131*387f9dfdSAndroid Build Coastguard Worker""" 132*387f9dfdSAndroid Build Coastguard Worker 133*387f9dfdSAndroid Build Coastguard Worker # Get a temporary file name 134*387f9dfdSAndroid Build Coastguard Worker tmp_file = NamedTemporaryFile(delete=False) 135*387f9dfdSAndroid Build Coastguard Worker tmp_file.close(); 136*387f9dfdSAndroid Build Coastguard Worker 137*387f9dfdSAndroid Build Coastguard Worker # Duplicate and close stderr (fd = 2) 138*387f9dfdSAndroid Build Coastguard Worker old_stderr = dup(2) 139*387f9dfdSAndroid Build Coastguard Worker close(2) 140*387f9dfdSAndroid Build Coastguard Worker 141*387f9dfdSAndroid Build Coastguard Worker # Open a new file, should get fd number 2 142*387f9dfdSAndroid Build Coastguard Worker # This will avoid printing llvm errors on the screen 143*387f9dfdSAndroid Build Coastguard Worker fd = open(tmp_file.name, O_WRONLY) 144*387f9dfdSAndroid Build Coastguard Worker try: 145*387f9dfdSAndroid Build Coastguard Worker t = BPF(text=bpf_check_text) 146*387f9dfdSAndroid Build Coastguard Worker success_compile = True 147*387f9dfdSAndroid Build Coastguard Worker except: 148*387f9dfdSAndroid Build Coastguard Worker success_compile = False 149*387f9dfdSAndroid Build Coastguard Worker 150*387f9dfdSAndroid Build Coastguard Worker # Release the fd 2, and next dup should restore old stderr 151*387f9dfdSAndroid Build Coastguard Worker close(fd) 152*387f9dfdSAndroid Build Coastguard Worker dup(old_stderr) 153*387f9dfdSAndroid Build Coastguard Worker close(old_stderr) 154*387f9dfdSAndroid Build Coastguard Worker 155*387f9dfdSAndroid Build Coastguard Worker # remove the temporary file and return 156*387f9dfdSAndroid Build Coastguard Worker unlink(tmp_file.name) 157*387f9dfdSAndroid Build Coastguard Worker return success_compile 158*387f9dfdSAndroid Build Coastguard Worker 159*387f9dfdSAndroid Build Coastguard Worker 160*387f9dfdSAndroid Build Coastguard Worker# process arguments 161*387f9dfdSAndroid Build Coastguard Workerif args.fullcsv: 162*387f9dfdSAndroid Build Coastguard Worker args.csv = True 163*387f9dfdSAndroid Build Coastguard Workerif args.csv: 164*387f9dfdSAndroid Build Coastguard Worker interval = 0.2 165*387f9dfdSAndroid Build Coastguard Workerif args.interval != -1 and (args.fullcsv or args.csv): 166*387f9dfdSAndroid Build Coastguard Worker print("ERROR: cannot use interval with either -j or -J. Exiting.") 167*387f9dfdSAndroid Build Coastguard Worker exit() 168*387f9dfdSAndroid Build Coastguard Workerif args.interval == -1: 169*387f9dfdSAndroid Build Coastguard Worker args.interval = "1" 170*387f9dfdSAndroid Build Coastguard Workerinterval = float(args.interval) 171*387f9dfdSAndroid Build Coastguard Worker 172*387f9dfdSAndroid Build Coastguard Worker# define BPF program 173*387f9dfdSAndroid Build Coastguard Workerbpf_text = """ 174*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 175*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/bpf_perf_event.h> 176*387f9dfdSAndroid Build Coastguard Worker#include <linux/sched.h> 177*387f9dfdSAndroid Build Coastguard Worker 178*387f9dfdSAndroid Build Coastguard Workerstruct data_t { 179*387f9dfdSAndroid Build Coastguard Worker u64 ts; 180*387f9dfdSAndroid Build Coastguard Worker u64 cpu; 181*387f9dfdSAndroid Build Coastguard Worker u64 len; 182*387f9dfdSAndroid Build Coastguard Worker}; 183*387f9dfdSAndroid Build Coastguard Worker 184*387f9dfdSAndroid Build Coastguard WorkerBPF_PERF_OUTPUT(events); 185*387f9dfdSAndroid Build Coastguard Worker 186*387f9dfdSAndroid Build Coastguard Worker// Declare enough of cfs_rq to find nr_running, since we can't #import the 187*387f9dfdSAndroid Build Coastguard Worker// header. This will need maintenance. It is from kernel/sched/sched.h: 188*387f9dfdSAndroid Build Coastguard Worker// The runnable_weight field is removed from Linux 5.7.0 189*387f9dfdSAndroid Build Coastguard Workerstruct cfs_rq_partial { 190*387f9dfdSAndroid Build Coastguard Worker struct load_weight load; 191*387f9dfdSAndroid Build Coastguard Worker#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0) 192*387f9dfdSAndroid Build Coastguard Worker RUNNABLE_WEIGHT_FIELD 193*387f9dfdSAndroid Build Coastguard Worker#endif 194*387f9dfdSAndroid Build Coastguard Worker unsigned int nr_running, h_nr_running; 195*387f9dfdSAndroid Build Coastguard Worker}; 196*387f9dfdSAndroid Build Coastguard Worker 197*387f9dfdSAndroid Build Coastguard Workerint do_perf_event(struct bpf_perf_event_data *ctx) 198*387f9dfdSAndroid Build Coastguard Worker{ 199*387f9dfdSAndroid Build Coastguard Worker int cpu = bpf_get_smp_processor_id(); 200*387f9dfdSAndroid Build Coastguard Worker u64 now = bpf_ktime_get_ns(); 201*387f9dfdSAndroid Build Coastguard Worker 202*387f9dfdSAndroid Build Coastguard Worker /* 203*387f9dfdSAndroid Build Coastguard Worker * Fetch the run queue length from task->se.cfs_rq->nr_running. This is an 204*387f9dfdSAndroid Build Coastguard Worker * unstable interface and may need maintenance. Perhaps a future version 205*387f9dfdSAndroid Build Coastguard Worker * of BPF will support task_rq(p) or something similar as a more reliable 206*387f9dfdSAndroid Build Coastguard Worker * interface. 207*387f9dfdSAndroid Build Coastguard Worker */ 208*387f9dfdSAndroid Build Coastguard Worker unsigned int len = 0; 209*387f9dfdSAndroid Build Coastguard Worker struct task_struct *task = NULL; 210*387f9dfdSAndroid Build Coastguard Worker struct cfs_rq_partial *my_q = NULL; 211*387f9dfdSAndroid Build Coastguard Worker task = (struct task_struct *)bpf_get_current_task(); 212*387f9dfdSAndroid Build Coastguard Worker my_q = (struct cfs_rq_partial *)task->se.cfs_rq; 213*387f9dfdSAndroid Build Coastguard Worker len = my_q->nr_running; 214*387f9dfdSAndroid Build Coastguard Worker 215*387f9dfdSAndroid Build Coastguard Worker struct data_t data = {.ts = now, .cpu = cpu, .len = len}; 216*387f9dfdSAndroid Build Coastguard Worker events.perf_submit(ctx, &data, sizeof(data)); 217*387f9dfdSAndroid Build Coastguard Worker 218*387f9dfdSAndroid Build Coastguard Worker return 0; 219*387f9dfdSAndroid Build Coastguard Worker} 220*387f9dfdSAndroid Build Coastguard Worker""" 221*387f9dfdSAndroid Build Coastguard Worker 222*387f9dfdSAndroid Build Coastguard Worker# If target has BTF enabled, use BTF to check runnable_weight field exists in 223*387f9dfdSAndroid Build Coastguard Worker# cfs_rq first, otherwise fallback to use check_runnable_weight_field(). 224*387f9dfdSAndroid Build Coastguard Workerif BPF.kernel_struct_has_field(b'cfs_rq', b'runnable_weight') == 1 \ 225*387f9dfdSAndroid Build Coastguard Worker or check_runnable_weight_field(): 226*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', 'unsigned long runnable_weight;') 227*387f9dfdSAndroid Build Coastguard Workerelse: 228*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', '') 229*387f9dfdSAndroid Build Coastguard Worker 230*387f9dfdSAndroid Build Coastguard Worker# code substitutions 231*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf: 232*387f9dfdSAndroid Build Coastguard Worker print(bpf_text) 233*387f9dfdSAndroid Build Coastguard Worker if args.ebpf: 234*387f9dfdSAndroid Build Coastguard Worker exit() 235*387f9dfdSAndroid Build Coastguard Worker 236*387f9dfdSAndroid Build Coastguard Worker# initialize BPF & perf_events 237*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text) 238*387f9dfdSAndroid Build Coastguard Worker# TODO: check for HW counters first and use if more accurate 239*387f9dfdSAndroid Build Coastguard Workerb.attach_perf_event(ev_type=PerfType.SOFTWARE, 240*387f9dfdSAndroid Build Coastguard Worker ev_config=PerfSWConfig.TASK_CLOCK, fn_name="do_perf_event", 241*387f9dfdSAndroid Build Coastguard Worker sample_period=0, sample_freq=frequency) 242*387f9dfdSAndroid Build Coastguard Worker 243*387f9dfdSAndroid Build Coastguard Workerif args.csv: 244*387f9dfdSAndroid Build Coastguard Worker if args.timestamp: 245*387f9dfdSAndroid Build Coastguard Worker print("TIME", end=",") 246*387f9dfdSAndroid Build Coastguard Worker print("TIMESTAMP_ns", end=",") 247*387f9dfdSAndroid Build Coastguard Worker print(",".join("CPU" + str(c) for c in range(ncpu)), end="") 248*387f9dfdSAndroid Build Coastguard Worker if args.fullcsv: 249*387f9dfdSAndroid Build Coastguard Worker print(",", end="") 250*387f9dfdSAndroid Build Coastguard Worker print(",".join("OFFSET_ns_CPU" + str(c) for c in range(ncpu)), end="") 251*387f9dfdSAndroid Build Coastguard Worker print() 252*387f9dfdSAndroid Build Coastguard Workerelse: 253*387f9dfdSAndroid Build Coastguard Worker print(("Sampling run queues... Output every %s seconds. " + 254*387f9dfdSAndroid Build Coastguard Worker "Hit Ctrl-C to end.") % args.interval) 255*387f9dfdSAndroid Build Coastguard Worker 256*387f9dfdSAndroid Build Coastguard Workersamples = {} 257*387f9dfdSAndroid Build Coastguard Workergroup = {} 258*387f9dfdSAndroid Build Coastguard Workerlast = 0 259*387f9dfdSAndroid Build Coastguard Worker 260*387f9dfdSAndroid Build Coastguard Worker# process event 261*387f9dfdSAndroid Build Coastguard Workerdef print_event(cpu, data, size): 262*387f9dfdSAndroid Build Coastguard Worker event = b["events"].event(data) 263*387f9dfdSAndroid Build Coastguard Worker samples[event.ts] = {} 264*387f9dfdSAndroid Build Coastguard Worker samples[event.ts]['cpu'] = event.cpu 265*387f9dfdSAndroid Build Coastguard Worker samples[event.ts]['len'] = event.len 266*387f9dfdSAndroid Build Coastguard Worker 267*387f9dfdSAndroid Build Coastguard Workerexiting = 0 if args.interval else 1 268*387f9dfdSAndroid Build Coastguard Workerslept = float(0) 269*387f9dfdSAndroid Build Coastguard Worker 270*387f9dfdSAndroid Build Coastguard Worker# Choose the elapsed time from one sample group to the next that identifies a 271*387f9dfdSAndroid Build Coastguard Worker# new sample group (a group being a set of samples from all CPUs). The 272*387f9dfdSAndroid Build Coastguard Worker# earliest timestamp is compared in each group. This trigger is also used 273*387f9dfdSAndroid Build Coastguard Worker# for sanity testing, if a group's samples exceed half this value. 274*387f9dfdSAndroid Build Coastguard Workertrigger = int(0.8 * (1000000000 / frequency)) 275*387f9dfdSAndroid Build Coastguard Worker 276*387f9dfdSAndroid Build Coastguard Worker# read events 277*387f9dfdSAndroid Build Coastguard Workerb["events"].open_perf_buffer(print_event, page_cnt=64) 278*387f9dfdSAndroid Build Coastguard Workerwhile 1: 279*387f9dfdSAndroid Build Coastguard Worker # allow some buffering by calling sleep(), to reduce the context switch 280*387f9dfdSAndroid Build Coastguard Worker # rate and lower overhead. 281*387f9dfdSAndroid Build Coastguard Worker try: 282*387f9dfdSAndroid Build Coastguard Worker if not exiting: 283*387f9dfdSAndroid Build Coastguard Worker sleep(wakeup_s) 284*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 285*387f9dfdSAndroid Build Coastguard Worker exiting = 1 286*387f9dfdSAndroid Build Coastguard Worker b.perf_buffer_poll() 287*387f9dfdSAndroid Build Coastguard Worker slept += wakeup_s 288*387f9dfdSAndroid Build Coastguard Worker 289*387f9dfdSAndroid Build Coastguard Worker if slept < 0.999 * interval: # floating point workaround 290*387f9dfdSAndroid Build Coastguard Worker continue 291*387f9dfdSAndroid Build Coastguard Worker slept = 0 292*387f9dfdSAndroid Build Coastguard Worker 293*387f9dfdSAndroid Build Coastguard Worker positive = 0 # number of samples where an idle CPU could have run work 294*387f9dfdSAndroid Build Coastguard Worker running = 0 295*387f9dfdSAndroid Build Coastguard Worker idle = 0 296*387f9dfdSAndroid Build Coastguard Worker if debug >= 2: 297*387f9dfdSAndroid Build Coastguard Worker print("DEBUG: begin samples loop, count %d" % len(samples)) 298*387f9dfdSAndroid Build Coastguard Worker for e in sorted(samples): 299*387f9dfdSAndroid Build Coastguard Worker if debug >= 2: 300*387f9dfdSAndroid Build Coastguard Worker print("DEBUG: ts %d cpu %d len %d delta %d trig %d" % (e, 301*387f9dfdSAndroid Build Coastguard Worker samples[e]['cpu'], samples[e]['len'], e - last, 302*387f9dfdSAndroid Build Coastguard Worker e - last > trigger)) 303*387f9dfdSAndroid Build Coastguard Worker 304*387f9dfdSAndroid Build Coastguard Worker # look for time jumps to identify a new sample group 305*387f9dfdSAndroid Build Coastguard Worker if e - last > trigger: 306*387f9dfdSAndroid Build Coastguard Worker 307*387f9dfdSAndroid Build Coastguard Worker # first first group timestamp, and sanity test 308*387f9dfdSAndroid Build Coastguard Worker g_time = 0 309*387f9dfdSAndroid Build Coastguard Worker g_max = 0 310*387f9dfdSAndroid Build Coastguard Worker for ge in sorted(group): 311*387f9dfdSAndroid Build Coastguard Worker if g_time == 0: 312*387f9dfdSAndroid Build Coastguard Worker g_time = ge 313*387f9dfdSAndroid Build Coastguard Worker g_max = ge 314*387f9dfdSAndroid Build Coastguard Worker 315*387f9dfdSAndroid Build Coastguard Worker # process previous sample group 316*387f9dfdSAndroid Build Coastguard Worker if args.csv: 317*387f9dfdSAndroid Build Coastguard Worker lens = [0] * ncpu 318*387f9dfdSAndroid Build Coastguard Worker offs = [0] * ncpu 319*387f9dfdSAndroid Build Coastguard Worker for ge in sorted(group): 320*387f9dfdSAndroid Build Coastguard Worker lens[samples[ge]['cpu']] = samples[ge]['len'] 321*387f9dfdSAndroid Build Coastguard Worker if args.fullcsv: 322*387f9dfdSAndroid Build Coastguard Worker offs[samples[ge]['cpu']] = ge - g_time 323*387f9dfdSAndroid Build Coastguard Worker if g_time > 0: # else first sample 324*387f9dfdSAndroid Build Coastguard Worker if args.timestamp: 325*387f9dfdSAndroid Build Coastguard Worker print("%-8s" % strftime("%H:%M:%S"), end=",") 326*387f9dfdSAndroid Build Coastguard Worker print("%d" % g_time, end=",") 327*387f9dfdSAndroid Build Coastguard Worker print(",".join(str(lens[c]) for c in range(ncpu)), end="") 328*387f9dfdSAndroid Build Coastguard Worker if args.fullcsv: 329*387f9dfdSAndroid Build Coastguard Worker print(",", end="") 330*387f9dfdSAndroid Build Coastguard Worker print(",".join(str(offs[c]) for c in range(ncpu))) 331*387f9dfdSAndroid Build Coastguard Worker else: 332*387f9dfdSAndroid Build Coastguard Worker print() 333*387f9dfdSAndroid Build Coastguard Worker else: 334*387f9dfdSAndroid Build Coastguard Worker # calculate stats 335*387f9dfdSAndroid Build Coastguard Worker g_running = 0 336*387f9dfdSAndroid Build Coastguard Worker g_queued = 0 337*387f9dfdSAndroid Build Coastguard Worker for ge in group: 338*387f9dfdSAndroid Build Coastguard Worker if samples[ge]['len'] > 0: 339*387f9dfdSAndroid Build Coastguard Worker g_running += 1 340*387f9dfdSAndroid Build Coastguard Worker if samples[ge]['len'] > 1: 341*387f9dfdSAndroid Build Coastguard Worker g_queued += samples[ge]['len'] - 1 342*387f9dfdSAndroid Build Coastguard Worker g_idle = ncpu - g_running 343*387f9dfdSAndroid Build Coastguard Worker 344*387f9dfdSAndroid Build Coastguard Worker # calculate the number of threads that could have run as the 345*387f9dfdSAndroid Build Coastguard Worker # minimum of idle and queued 346*387f9dfdSAndroid Build Coastguard Worker if g_idle > 0 and g_queued > 0: 347*387f9dfdSAndroid Build Coastguard Worker if g_queued > g_idle: 348*387f9dfdSAndroid Build Coastguard Worker i = g_idle 349*387f9dfdSAndroid Build Coastguard Worker else: 350*387f9dfdSAndroid Build Coastguard Worker i = g_queued 351*387f9dfdSAndroid Build Coastguard Worker positive += i 352*387f9dfdSAndroid Build Coastguard Worker running += g_running 353*387f9dfdSAndroid Build Coastguard Worker idle += g_idle 354*387f9dfdSAndroid Build Coastguard Worker 355*387f9dfdSAndroid Build Coastguard Worker # now sanity test, after -J output 356*387f9dfdSAndroid Build Coastguard Worker g_range = g_max - g_time 357*387f9dfdSAndroid Build Coastguard Worker if g_range > trigger / 2: 358*387f9dfdSAndroid Build Coastguard Worker # if a sample group exceeds half the interval, we can no 359*387f9dfdSAndroid Build Coastguard Worker # longer draw conclusions about some CPUs idle while others 360*387f9dfdSAndroid Build Coastguard Worker # have queued work. Error and exit. This can happen when 361*387f9dfdSAndroid Build Coastguard Worker # CPUs power down, then start again on different offsets. 362*387f9dfdSAndroid Build Coastguard Worker # TODO: Since this is a sampling tool, an error margin should 363*387f9dfdSAndroid Build Coastguard Worker # be anticipated, so an improvement may be to bump a counter 364*387f9dfdSAndroid Build Coastguard Worker # instead of exiting, and only exit if this counter shows 365*387f9dfdSAndroid Build Coastguard Worker # a skewed sample rate of over, say, 1%. Such an approach 366*387f9dfdSAndroid Build Coastguard Worker # would allow a small rate of outliers (sampling error), 367*387f9dfdSAndroid Build Coastguard Worker # and, we could tighten the trigger to be, say, trigger / 5. 368*387f9dfdSAndroid Build Coastguard Worker # In the case of a power down, if it's detectable, perhaps 369*387f9dfdSAndroid Build Coastguard Worker # the tool could reinitialize the timers (although exiting 370*387f9dfdSAndroid Build Coastguard Worker # is simple and works). 371*387f9dfdSAndroid Build Coastguard Worker print(("ERROR: CPU samples arrived at skewed offsets " + 372*387f9dfdSAndroid Build Coastguard Worker "(CPUs may have powered down when idle), " + 373*387f9dfdSAndroid Build Coastguard Worker "spanning %d ns (expected < %d ns). Debug with -J, " + 374*387f9dfdSAndroid Build Coastguard Worker "and see the man page. As output may begin to be " + 375*387f9dfdSAndroid Build Coastguard Worker "unreliable, exiting.") % (g_range, trigger / 2)) 376*387f9dfdSAndroid Build Coastguard Worker exit() 377*387f9dfdSAndroid Build Coastguard Worker 378*387f9dfdSAndroid Build Coastguard Worker # these are done, remove 379*387f9dfdSAndroid Build Coastguard Worker for ge in sorted(group): 380*387f9dfdSAndroid Build Coastguard Worker del samples[ge] 381*387f9dfdSAndroid Build Coastguard Worker 382*387f9dfdSAndroid Build Coastguard Worker # begin next group 383*387f9dfdSAndroid Build Coastguard Worker group = {} 384*387f9dfdSAndroid Build Coastguard Worker last = e 385*387f9dfdSAndroid Build Coastguard Worker 386*387f9dfdSAndroid Build Coastguard Worker # stash this timestamp in a sample group dict 387*387f9dfdSAndroid Build Coastguard Worker group[e] = 1 388*387f9dfdSAndroid Build Coastguard Worker 389*387f9dfdSAndroid Build Coastguard Worker if not args.csv: 390*387f9dfdSAndroid Build Coastguard Worker total = running + idle 391*387f9dfdSAndroid Build Coastguard Worker unclaimed = util = 0 392*387f9dfdSAndroid Build Coastguard Worker 393*387f9dfdSAndroid Build Coastguard Worker if debug: 394*387f9dfdSAndroid Build Coastguard Worker print("DEBUG: hit %d running %d idle %d total %d buffered %d" % ( 395*387f9dfdSAndroid Build Coastguard Worker positive, running, idle, total, len(samples))) 396*387f9dfdSAndroid Build Coastguard Worker 397*387f9dfdSAndroid Build Coastguard Worker if args.timestamp: 398*387f9dfdSAndroid Build Coastguard Worker print("%-8s " % strftime("%H:%M:%S"), end="") 399*387f9dfdSAndroid Build Coastguard Worker 400*387f9dfdSAndroid Build Coastguard Worker # output 401*387f9dfdSAndroid Build Coastguard Worker if total: 402*387f9dfdSAndroid Build Coastguard Worker unclaimed = float(positive) / total 403*387f9dfdSAndroid Build Coastguard Worker util = float(running) / total 404*387f9dfdSAndroid Build Coastguard Worker print("%%CPU %6.2f%%, unclaimed idle %0.2f%%" % (100 * util, 405*387f9dfdSAndroid Build Coastguard Worker 100 * unclaimed)) 406*387f9dfdSAndroid Build Coastguard Worker 407*387f9dfdSAndroid Build Coastguard Worker countdown -= 1 408*387f9dfdSAndroid Build Coastguard Worker if exiting or countdown == 0: 409*387f9dfdSAndroid Build Coastguard Worker exit() 410