xref: /aosp_15_r20/external/bcc/tools/cpuunclaimed.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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