xref: /aosp_15_r20/external/bcc/tools/runqlen.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# runqlen    Summarize scheduler run queue length as a histogram.
5*387f9dfdSAndroid Build Coastguard Worker#            For Linux, uses BCC, eBPF.
6*387f9dfdSAndroid Build Coastguard Worker#
7*387f9dfdSAndroid Build Coastguard Worker# This counts the length of the run queue, excluding the currently running
8*387f9dfdSAndroid Build Coastguard Worker# thread, and shows it as a histogram.
9*387f9dfdSAndroid Build Coastguard Worker#
10*387f9dfdSAndroid Build Coastguard Worker# Also answers run queue occupancy.
11*387f9dfdSAndroid Build Coastguard Worker#
12*387f9dfdSAndroid Build Coastguard Worker# USAGE: runqlen [-h] [-T] [-Q] [-m] [-D] [interval] [count]
13*387f9dfdSAndroid Build Coastguard Worker#
14*387f9dfdSAndroid Build Coastguard Worker# REQUIRES: Linux 4.9+ (BPF_PROG_TYPE_PERF_EVENT support). Under tools/old is
15*387f9dfdSAndroid Build Coastguard Worker# a version of this tool that may work on Linux 4.6 - 4.8.
16*387f9dfdSAndroid Build Coastguard Worker#
17*387f9dfdSAndroid Build Coastguard Worker# Copyright 2016 Netflix, Inc.
18*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
19*387f9dfdSAndroid Build Coastguard Worker#
20*387f9dfdSAndroid Build Coastguard Worker# 12-Dec-2016   Brendan Gregg   Created this.
21*387f9dfdSAndroid Build Coastguard Worker
22*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function
23*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF, PerfType, PerfSWConfig, utils
24*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime
25*387f9dfdSAndroid Build Coastguard Workerfrom tempfile import NamedTemporaryFile
26*387f9dfdSAndroid Build Coastguard Workerfrom os import open, close, dup, unlink, O_WRONLY
27*387f9dfdSAndroid Build Coastguard Workerimport argparse
28*387f9dfdSAndroid Build Coastguard Worker
29*387f9dfdSAndroid Build Coastguard Worker# arguments
30*387f9dfdSAndroid Build Coastguard Workerexamples = """examples:
31*387f9dfdSAndroid Build Coastguard Worker    ./runqlen            # summarize run queue length as a histogram
32*387f9dfdSAndroid Build Coastguard Worker    ./runqlen 1 10       # print 1 second summaries, 10 times
33*387f9dfdSAndroid Build Coastguard Worker    ./runqlen -T 1       # 1s summaries and timestamps
34*387f9dfdSAndroid Build Coastguard Worker    ./runqlen -O         # report run queue occupancy
35*387f9dfdSAndroid Build Coastguard Worker    ./runqlen -C         # show each CPU separately
36*387f9dfdSAndroid Build Coastguard Worker"""
37*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
38*387f9dfdSAndroid Build Coastguard Worker    description="Summarize scheduler run queue length as a histogram",
39*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
40*387f9dfdSAndroid Build Coastguard Worker    epilog=examples)
41*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-T", "--timestamp", action="store_true",
42*387f9dfdSAndroid Build Coastguard Worker    help="include timestamp on output")
43*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-O", "--runqocc", action="store_true",
44*387f9dfdSAndroid Build Coastguard Worker    help="report run queue occupancy")
45*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-C", "--cpus", action="store_true",
46*387f9dfdSAndroid Build Coastguard Worker    help="print output for each CPU separately")
47*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("interval", nargs="?", default=99999999,
48*387f9dfdSAndroid Build Coastguard Worker    help="output interval, in seconds")
49*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("count", nargs="?", default=99999999,
50*387f9dfdSAndroid Build Coastguard Worker    help="number of outputs")
51*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true",
52*387f9dfdSAndroid Build Coastguard Worker    help=argparse.SUPPRESS)
53*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args()
54*387f9dfdSAndroid Build Coastguard Workercountdown = int(args.count)
55*387f9dfdSAndroid Build Coastguard Workerdebug = 0
56*387f9dfdSAndroid Build Coastguard Workerfrequency = 99
57*387f9dfdSAndroid Build Coastguard Worker
58*387f9dfdSAndroid Build Coastguard Worker# Linux 4.15 introduced a new field runnable_weight
59*387f9dfdSAndroid Build Coastguard Worker# in linux_src:kernel/sched/sched.h as
60*387f9dfdSAndroid Build Coastguard Worker#     struct cfs_rq {
61*387f9dfdSAndroid Build Coastguard Worker#         struct load_weight load;
62*387f9dfdSAndroid Build Coastguard Worker#         unsigned long runnable_weight;
63*387f9dfdSAndroid Build Coastguard Worker#         unsigned int nr_running, h_nr_running;
64*387f9dfdSAndroid Build Coastguard Worker#         ......
65*387f9dfdSAndroid Build Coastguard Worker#     }
66*387f9dfdSAndroid Build Coastguard Worker# and this tool requires to access nr_running to get
67*387f9dfdSAndroid Build Coastguard Worker# runqueue len information.
68*387f9dfdSAndroid Build Coastguard Worker#
69*387f9dfdSAndroid Build Coastguard Worker# The commit which introduces cfs_rq->runnable_weight
70*387f9dfdSAndroid Build Coastguard Worker# field also introduces the field sched_entity->runnable_weight
71*387f9dfdSAndroid Build Coastguard Worker# where sched_entity is defined in linux_src:include/linux/sched.h.
72*387f9dfdSAndroid Build Coastguard Worker#
73*387f9dfdSAndroid Build Coastguard Worker# To cope with pre-4.15 and 4.15/post-4.15 releases,
74*387f9dfdSAndroid Build Coastguard Worker# we run a simple BPF program to detect whether
75*387f9dfdSAndroid Build Coastguard Worker# field sched_entity->runnable_weight exists. The existence of
76*387f9dfdSAndroid Build Coastguard Worker# this field should infer the existence of cfs_rq->runnable_weight.
77*387f9dfdSAndroid Build Coastguard Worker#
78*387f9dfdSAndroid Build Coastguard Worker# This will need maintenance as the relationship between these
79*387f9dfdSAndroid Build Coastguard Worker# two fields may change in the future.
80*387f9dfdSAndroid Build Coastguard Worker#
81*387f9dfdSAndroid Build Coastguard Workerdef check_runnable_weight_field():
82*387f9dfdSAndroid Build Coastguard Worker    # Define the bpf program for checking purpose
83*387f9dfdSAndroid Build Coastguard Worker    bpf_check_text = """
84*387f9dfdSAndroid Build Coastguard Worker#include <linux/sched.h>
85*387f9dfdSAndroid Build Coastguard Workerunsigned long dummy(struct sched_entity *entity)
86*387f9dfdSAndroid Build Coastguard Worker{
87*387f9dfdSAndroid Build Coastguard Worker    return entity->runnable_weight;
88*387f9dfdSAndroid Build Coastguard Worker}
89*387f9dfdSAndroid Build Coastguard Worker"""
90*387f9dfdSAndroid Build Coastguard Worker
91*387f9dfdSAndroid Build Coastguard Worker    # Get a temporary file name
92*387f9dfdSAndroid Build Coastguard Worker    tmp_file = NamedTemporaryFile(delete=False)
93*387f9dfdSAndroid Build Coastguard Worker    tmp_file.close()
94*387f9dfdSAndroid Build Coastguard Worker
95*387f9dfdSAndroid Build Coastguard Worker    # Duplicate and close stderr (fd = 2)
96*387f9dfdSAndroid Build Coastguard Worker    old_stderr = dup(2)
97*387f9dfdSAndroid Build Coastguard Worker    close(2)
98*387f9dfdSAndroid Build Coastguard Worker
99*387f9dfdSAndroid Build Coastguard Worker    # Open a new file, should get fd number 2
100*387f9dfdSAndroid Build Coastguard Worker    # This will avoid printing llvm errors on the screen
101*387f9dfdSAndroid Build Coastguard Worker    fd = open(tmp_file.name, O_WRONLY)
102*387f9dfdSAndroid Build Coastguard Worker    try:
103*387f9dfdSAndroid Build Coastguard Worker        t = BPF(text=bpf_check_text)
104*387f9dfdSAndroid Build Coastguard Worker        success_compile = True
105*387f9dfdSAndroid Build Coastguard Worker    except:
106*387f9dfdSAndroid Build Coastguard Worker        success_compile = False
107*387f9dfdSAndroid Build Coastguard Worker
108*387f9dfdSAndroid Build Coastguard Worker    # Release the fd 2, and next dup should restore old stderr
109*387f9dfdSAndroid Build Coastguard Worker    close(fd)
110*387f9dfdSAndroid Build Coastguard Worker    dup(old_stderr)
111*387f9dfdSAndroid Build Coastguard Worker    close(old_stderr)
112*387f9dfdSAndroid Build Coastguard Worker
113*387f9dfdSAndroid Build Coastguard Worker    # remove the temporary file and return
114*387f9dfdSAndroid Build Coastguard Worker    unlink(tmp_file.name)
115*387f9dfdSAndroid Build Coastguard Worker    return success_compile
116*387f9dfdSAndroid Build Coastguard Worker
117*387f9dfdSAndroid Build Coastguard Worker
118*387f9dfdSAndroid Build Coastguard Worker# define BPF program
119*387f9dfdSAndroid Build Coastguard Workerbpf_text = """
120*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
121*387f9dfdSAndroid Build Coastguard Worker#include <linux/sched.h>
122*387f9dfdSAndroid Build Coastguard Worker
123*387f9dfdSAndroid Build Coastguard Worker// Declare enough of cfs_rq to find nr_running, since we can't #import the
124*387f9dfdSAndroid Build Coastguard Worker// header. This will need maintenance. It is from kernel/sched/sched.h:
125*387f9dfdSAndroid Build Coastguard Worker// The runnable_weight field is removed from Linux 5.7.0
126*387f9dfdSAndroid Build Coastguard Workerstruct cfs_rq_partial {
127*387f9dfdSAndroid Build Coastguard Worker    struct load_weight load;
128*387f9dfdSAndroid Build Coastguard Worker#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0)
129*387f9dfdSAndroid Build Coastguard Worker    RUNNABLE_WEIGHT_FIELD
130*387f9dfdSAndroid Build Coastguard Worker#endif
131*387f9dfdSAndroid Build Coastguard Worker    unsigned int nr_running, h_nr_running;
132*387f9dfdSAndroid Build Coastguard Worker};
133*387f9dfdSAndroid Build Coastguard Worker
134*387f9dfdSAndroid Build Coastguard Workertypedef struct cpu_key {
135*387f9dfdSAndroid Build Coastguard Worker    int cpu;
136*387f9dfdSAndroid Build Coastguard Worker    unsigned int slot;
137*387f9dfdSAndroid Build Coastguard Worker} cpu_key_t;
138*387f9dfdSAndroid Build Coastguard WorkerSTORAGE
139*387f9dfdSAndroid Build Coastguard Worker
140*387f9dfdSAndroid Build Coastguard Workerint do_perf_event()
141*387f9dfdSAndroid Build Coastguard Worker{
142*387f9dfdSAndroid Build Coastguard Worker    unsigned int len = 0;
143*387f9dfdSAndroid Build Coastguard Worker    pid_t pid = 0;
144*387f9dfdSAndroid Build Coastguard Worker    struct task_struct *task = NULL;
145*387f9dfdSAndroid Build Coastguard Worker    struct cfs_rq_partial *my_q = NULL;
146*387f9dfdSAndroid Build Coastguard Worker
147*387f9dfdSAndroid Build Coastguard Worker    // Fetch the run queue length from task->se.cfs_rq->nr_running. This is an
148*387f9dfdSAndroid Build Coastguard Worker    // unstable interface and may need maintenance. Perhaps a future version
149*387f9dfdSAndroid Build Coastguard Worker    // of BPF will support task_rq(p) or something similar as a more reliable
150*387f9dfdSAndroid Build Coastguard Worker    // interface.
151*387f9dfdSAndroid Build Coastguard Worker    task = (struct task_struct *)bpf_get_current_task();
152*387f9dfdSAndroid Build Coastguard Worker    my_q = (struct cfs_rq_partial *)task->se.cfs_rq;
153*387f9dfdSAndroid Build Coastguard Worker    len = my_q->nr_running;
154*387f9dfdSAndroid Build Coastguard Worker
155*387f9dfdSAndroid Build Coastguard Worker    // Calculate run queue length by subtracting the currently running task,
156*387f9dfdSAndroid Build Coastguard Worker    // if present. len 0 == idle, len 1 == one running task.
157*387f9dfdSAndroid Build Coastguard Worker    if (len > 0)
158*387f9dfdSAndroid Build Coastguard Worker        len--;
159*387f9dfdSAndroid Build Coastguard Worker
160*387f9dfdSAndroid Build Coastguard Worker    STORE
161*387f9dfdSAndroid Build Coastguard Worker
162*387f9dfdSAndroid Build Coastguard Worker    return 0;
163*387f9dfdSAndroid Build Coastguard Worker}
164*387f9dfdSAndroid Build Coastguard Worker"""
165*387f9dfdSAndroid Build Coastguard Worker
166*387f9dfdSAndroid Build Coastguard Worker# code substitutions
167*387f9dfdSAndroid Build Coastguard Workerif args.cpus:
168*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('STORAGE',
169*387f9dfdSAndroid Build Coastguard Worker        'BPF_HISTOGRAM(dist, cpu_key_t, MAX_CPUS);')
170*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('STORE', 'cpu_key_t key = {.slot = len}; ' +
171*387f9dfdSAndroid Build Coastguard Worker        'key.cpu = bpf_get_smp_processor_id(); ' +
172*387f9dfdSAndroid Build Coastguard Worker        'dist.increment(key);')
173*387f9dfdSAndroid Build Coastguard Workerelse:
174*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('STORAGE',
175*387f9dfdSAndroid Build Coastguard Worker        'BPF_HISTOGRAM(dist, unsigned int);')
176*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('STORE', 'dist.atomic_increment(len);')
177*387f9dfdSAndroid Build Coastguard Worker
178*387f9dfdSAndroid Build Coastguard Worker# If target has BTF enabled, use BTF to check runnable_weight field exists in
179*387f9dfdSAndroid Build Coastguard Worker# cfs_rq first, otherwise fallback to use check_runnable_weight_field().
180*387f9dfdSAndroid Build Coastguard Workerif BPF.kernel_struct_has_field(b'cfs_rq', b'runnable_weight') == 1 \
181*387f9dfdSAndroid Build Coastguard Worker        or check_runnable_weight_field():
182*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', 'unsigned long runnable_weight;')
183*387f9dfdSAndroid Build Coastguard Workerelse:
184*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('RUNNABLE_WEIGHT_FIELD', '')
185*387f9dfdSAndroid Build Coastguard Worker
186*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf:
187*387f9dfdSAndroid Build Coastguard Worker    print(bpf_text)
188*387f9dfdSAndroid Build Coastguard Worker    if args.ebpf:
189*387f9dfdSAndroid Build Coastguard Worker        exit()
190*387f9dfdSAndroid Build Coastguard Worker
191*387f9dfdSAndroid Build Coastguard Workernum_cpus = len(utils.get_online_cpus())
192*387f9dfdSAndroid Build Coastguard Worker
193*387f9dfdSAndroid Build Coastguard Worker# initialize BPF & perf_events
194*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text, cflags=['-DMAX_CPUS=%s' % str(num_cpus)])
195*387f9dfdSAndroid Build Coastguard Workerb.attach_perf_event(ev_type=PerfType.SOFTWARE,
196*387f9dfdSAndroid Build Coastguard Worker    ev_config=PerfSWConfig.CPU_CLOCK, fn_name="do_perf_event",
197*387f9dfdSAndroid Build Coastguard Worker    sample_period=0, sample_freq=frequency)
198*387f9dfdSAndroid Build Coastguard Worker
199*387f9dfdSAndroid Build Coastguard Workerprint("Sampling run queue length... Hit Ctrl-C to end.")
200*387f9dfdSAndroid Build Coastguard Worker
201*387f9dfdSAndroid Build Coastguard Worker# output
202*387f9dfdSAndroid Build Coastguard Workerexiting = 0 if args.interval else 1
203*387f9dfdSAndroid Build Coastguard Workerdist = b.get_table("dist")
204*387f9dfdSAndroid Build Coastguard Workerwhile (1):
205*387f9dfdSAndroid Build Coastguard Worker    try:
206*387f9dfdSAndroid Build Coastguard Worker        sleep(int(args.interval))
207*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
208*387f9dfdSAndroid Build Coastguard Worker        exiting = 1
209*387f9dfdSAndroid Build Coastguard Worker
210*387f9dfdSAndroid Build Coastguard Worker    print()
211*387f9dfdSAndroid Build Coastguard Worker    if args.timestamp:
212*387f9dfdSAndroid Build Coastguard Worker        print("%-8s\n" % strftime("%H:%M:%S"), end="")
213*387f9dfdSAndroid Build Coastguard Worker
214*387f9dfdSAndroid Build Coastguard Worker    if args.runqocc:
215*387f9dfdSAndroid Build Coastguard Worker        if args.cpus:
216*387f9dfdSAndroid Build Coastguard Worker            # run queue occupancy, per-CPU summary
217*387f9dfdSAndroid Build Coastguard Worker            idle = {}
218*387f9dfdSAndroid Build Coastguard Worker            queued = {}
219*387f9dfdSAndroid Build Coastguard Worker            cpumax = 0
220*387f9dfdSAndroid Build Coastguard Worker            for k, v in dist.items():
221*387f9dfdSAndroid Build Coastguard Worker                if k.cpu > cpumax:
222*387f9dfdSAndroid Build Coastguard Worker                    cpumax = k.cpu
223*387f9dfdSAndroid Build Coastguard Worker            for c in range(0, cpumax + 1):
224*387f9dfdSAndroid Build Coastguard Worker                idle[c] = 0
225*387f9dfdSAndroid Build Coastguard Worker                queued[c] = 0
226*387f9dfdSAndroid Build Coastguard Worker            for k, v in dist.items():
227*387f9dfdSAndroid Build Coastguard Worker                if k.slot == 0:
228*387f9dfdSAndroid Build Coastguard Worker                    idle[k.cpu] += v.value
229*387f9dfdSAndroid Build Coastguard Worker                else:
230*387f9dfdSAndroid Build Coastguard Worker                    queued[k.cpu] += v.value
231*387f9dfdSAndroid Build Coastguard Worker            for c in range(0, cpumax + 1):
232*387f9dfdSAndroid Build Coastguard Worker                samples = idle[c] + queued[c]
233*387f9dfdSAndroid Build Coastguard Worker                if samples:
234*387f9dfdSAndroid Build Coastguard Worker                    runqocc = float(queued[c]) / samples
235*387f9dfdSAndroid Build Coastguard Worker                else:
236*387f9dfdSAndroid Build Coastguard Worker                    runqocc = 0
237*387f9dfdSAndroid Build Coastguard Worker                print("runqocc, CPU %-3d %6.2f%%" % (c, 100 * runqocc))
238*387f9dfdSAndroid Build Coastguard Worker
239*387f9dfdSAndroid Build Coastguard Worker        else:
240*387f9dfdSAndroid Build Coastguard Worker            # run queue occupancy, system-wide summary
241*387f9dfdSAndroid Build Coastguard Worker            idle = 0
242*387f9dfdSAndroid Build Coastguard Worker            queued = 0
243*387f9dfdSAndroid Build Coastguard Worker            for k, v in dist.items():
244*387f9dfdSAndroid Build Coastguard Worker                if k.value == 0:
245*387f9dfdSAndroid Build Coastguard Worker                    idle += v.value
246*387f9dfdSAndroid Build Coastguard Worker                else:
247*387f9dfdSAndroid Build Coastguard Worker                    queued += v.value
248*387f9dfdSAndroid Build Coastguard Worker            samples = idle + queued
249*387f9dfdSAndroid Build Coastguard Worker            if samples:
250*387f9dfdSAndroid Build Coastguard Worker                runqocc = float(queued) / samples
251*387f9dfdSAndroid Build Coastguard Worker            else:
252*387f9dfdSAndroid Build Coastguard Worker                runqocc = 0
253*387f9dfdSAndroid Build Coastguard Worker            print("runqocc: %0.2f%%" % (100 * runqocc))
254*387f9dfdSAndroid Build Coastguard Worker
255*387f9dfdSAndroid Build Coastguard Worker    else:
256*387f9dfdSAndroid Build Coastguard Worker        # run queue length histograms
257*387f9dfdSAndroid Build Coastguard Worker        dist.print_linear_hist("runqlen", "cpu")
258*387f9dfdSAndroid Build Coastguard Worker
259*387f9dfdSAndroid Build Coastguard Worker    dist.clear()
260*387f9dfdSAndroid Build Coastguard Worker
261*387f9dfdSAndroid Build Coastguard Worker    countdown -= 1
262*387f9dfdSAndroid Build Coastguard Worker    if exiting or countdown == 0:
263*387f9dfdSAndroid Build Coastguard Worker        exit()
264