xref: /aosp_15_r20/external/bcc/tools/cpudist.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# cpudist   Summarize on- and off-CPU time per task as a histogram.
5*387f9dfdSAndroid Build Coastguard Worker#
6*387f9dfdSAndroid Build Coastguard Worker# USAGE: cpudist [-h] [-O] [-T] [-m] [-P] [-L] [-p PID] [-I] [-e] [interval] [count]
7*387f9dfdSAndroid Build Coastguard Worker#
8*387f9dfdSAndroid Build Coastguard Worker# This measures the time a task spends on or off the CPU, and shows this time
9*387f9dfdSAndroid Build Coastguard Worker# as a histogram, optionally per-process.
10*387f9dfdSAndroid Build Coastguard Worker#
11*387f9dfdSAndroid Build Coastguard Worker# By default CPU idle time are excluded by simply excluding PID 0.
12*387f9dfdSAndroid Build Coastguard Worker#
13*387f9dfdSAndroid Build Coastguard Worker# Copyright 2016 Sasha Goldshtein
14*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
15*387f9dfdSAndroid Build Coastguard Worker#
16*387f9dfdSAndroid Build Coastguard Worker# 27-Mar-2022   Rocky Xing      Changed to exclude CPU idle time by default.
17*387f9dfdSAndroid Build Coastguard Worker# 25-Jul-2022   Rocky Xing      Added extension summary support.
18*387f9dfdSAndroid Build Coastguard Worker
19*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function
20*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF
21*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime
22*387f9dfdSAndroid Build Coastguard Workerimport argparse
23*387f9dfdSAndroid Build Coastguard Worker
24*387f9dfdSAndroid Build Coastguard Workerexamples = """examples:
25*387f9dfdSAndroid Build Coastguard Worker    cpudist              # summarize on-CPU time as a histogram
26*387f9dfdSAndroid Build Coastguard Worker    cpudist -O           # summarize off-CPU time as a histogram
27*387f9dfdSAndroid Build Coastguard Worker    cpudist 1 10         # print 1 second summaries, 10 times
28*387f9dfdSAndroid Build Coastguard Worker    cpudist -mT 1        # 1s summaries, milliseconds, and timestamps
29*387f9dfdSAndroid Build Coastguard Worker    cpudist -P           # show each PID separately
30*387f9dfdSAndroid Build Coastguard Worker    cpudist -p 185       # trace PID 185 only
31*387f9dfdSAndroid Build Coastguard Worker    cpudist -I           # include CPU idle time
32*387f9dfdSAndroid Build Coastguard Worker    cpudist -e           # show extension summary (average/total/count)
33*387f9dfdSAndroid Build Coastguard Worker"""
34*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
35*387f9dfdSAndroid Build Coastguard Worker    description="Summarize on- and off-CPU time per task as a histogram.",
36*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
37*387f9dfdSAndroid Build Coastguard Worker    epilog=examples)
38*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-O", "--offcpu", action="store_true",
39*387f9dfdSAndroid Build Coastguard Worker    help="measure off-CPU time")
40*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-T", "--timestamp", action="store_true",
41*387f9dfdSAndroid Build Coastguard Worker    help="include timestamp on output")
42*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-m", "--milliseconds", action="store_true",
43*387f9dfdSAndroid Build Coastguard Worker    help="millisecond histogram")
44*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-P", "--pids", action="store_true",
45*387f9dfdSAndroid Build Coastguard Worker    help="print a histogram per process ID")
46*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-L", "--tids", action="store_true",
47*387f9dfdSAndroid Build Coastguard Worker    help="print a histogram per thread ID")
48*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-p", "--pid",
49*387f9dfdSAndroid Build Coastguard Worker    help="trace this PID only")
50*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-I", "--include-idle", action="store_true",
51*387f9dfdSAndroid Build Coastguard Worker    help="include CPU idle time")
52*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-e", "--extension", action="store_true",
53*387f9dfdSAndroid Build Coastguard Worker    help="show extension summary (average/total/count)")
54*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("interval", nargs="?", default=99999999,
55*387f9dfdSAndroid Build Coastguard Worker    help="output interval, in seconds")
56*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("count", nargs="?", default=99999999,
57*387f9dfdSAndroid Build Coastguard Worker    help="number of outputs")
58*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true",
59*387f9dfdSAndroid Build Coastguard Worker    help=argparse.SUPPRESS)
60*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args()
61*387f9dfdSAndroid Build Coastguard Workercountdown = int(args.count)
62*387f9dfdSAndroid Build Coastguard Workerdebug = 0
63*387f9dfdSAndroid Build Coastguard Worker
64*387f9dfdSAndroid Build Coastguard Workerbpf_text = """
65*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
66*387f9dfdSAndroid Build Coastguard Worker#include <linux/sched.h>
67*387f9dfdSAndroid Build Coastguard Worker"""
68*387f9dfdSAndroid Build Coastguard Worker
69*387f9dfdSAndroid Build Coastguard Workerif not args.offcpu:
70*387f9dfdSAndroid Build Coastguard Worker    bpf_text += "#define ONCPU\n"
71*387f9dfdSAndroid Build Coastguard Worker
72*387f9dfdSAndroid Build Coastguard Workerbpf_text += """
73*387f9dfdSAndroid Build Coastguard Workertypedef struct entry_key {
74*387f9dfdSAndroid Build Coastguard Worker    u32 pid;
75*387f9dfdSAndroid Build Coastguard Worker    u32 cpu;
76*387f9dfdSAndroid Build Coastguard Worker} entry_key_t;
77*387f9dfdSAndroid Build Coastguard Worker
78*387f9dfdSAndroid Build Coastguard Workertypedef struct pid_key {
79*387f9dfdSAndroid Build Coastguard Worker    u64 id;
80*387f9dfdSAndroid Build Coastguard Worker    u64 slot;
81*387f9dfdSAndroid Build Coastguard Worker} pid_key_t;
82*387f9dfdSAndroid Build Coastguard Worker
83*387f9dfdSAndroid Build Coastguard Workertypedef struct ext_val {
84*387f9dfdSAndroid Build Coastguard Worker    u64 total;
85*387f9dfdSAndroid Build Coastguard Worker    u64 count;
86*387f9dfdSAndroid Build Coastguard Worker} ext_val_t;
87*387f9dfdSAndroid Build Coastguard Worker
88*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(start, entry_key_t, u64, MAX_PID);
89*387f9dfdSAndroid Build Coastguard WorkerSTORAGE
90*387f9dfdSAndroid Build Coastguard Worker
91*387f9dfdSAndroid Build Coastguard Workerstatic inline void store_start(u32 tgid, u32 pid, u32 cpu, u64 ts)
92*387f9dfdSAndroid Build Coastguard Worker{
93*387f9dfdSAndroid Build Coastguard Worker    if (PID_FILTER)
94*387f9dfdSAndroid Build Coastguard Worker        return;
95*387f9dfdSAndroid Build Coastguard Worker
96*387f9dfdSAndroid Build Coastguard Worker    if (IDLE_FILTER)
97*387f9dfdSAndroid Build Coastguard Worker        return;
98*387f9dfdSAndroid Build Coastguard Worker
99*387f9dfdSAndroid Build Coastguard Worker    entry_key_t entry_key = { .pid = pid, .cpu = (pid == 0 ? cpu : 0xFFFFFFFF) };
100*387f9dfdSAndroid Build Coastguard Worker    start.update(&entry_key, &ts);
101*387f9dfdSAndroid Build Coastguard Worker}
102*387f9dfdSAndroid Build Coastguard Worker
103*387f9dfdSAndroid Build Coastguard Workerstatic inline void update_hist(u32 tgid, u32 pid, u32 cpu, u64 ts)
104*387f9dfdSAndroid Build Coastguard Worker{
105*387f9dfdSAndroid Build Coastguard Worker    if (PID_FILTER)
106*387f9dfdSAndroid Build Coastguard Worker        return;
107*387f9dfdSAndroid Build Coastguard Worker
108*387f9dfdSAndroid Build Coastguard Worker    if (IDLE_FILTER)
109*387f9dfdSAndroid Build Coastguard Worker        return;
110*387f9dfdSAndroid Build Coastguard Worker
111*387f9dfdSAndroid Build Coastguard Worker    entry_key_t entry_key = { .pid = pid, .cpu = (pid == 0 ? cpu : 0xFFFFFFFF) };
112*387f9dfdSAndroid Build Coastguard Worker    u64 *tsp = start.lookup(&entry_key);
113*387f9dfdSAndroid Build Coastguard Worker    if (tsp == 0)
114*387f9dfdSAndroid Build Coastguard Worker        return;
115*387f9dfdSAndroid Build Coastguard Worker
116*387f9dfdSAndroid Build Coastguard Worker    if (ts < *tsp) {
117*387f9dfdSAndroid Build Coastguard Worker        // Probably a clock issue where the recorded on-CPU event had a
118*387f9dfdSAndroid Build Coastguard Worker        // timestamp later than the recorded off-CPU event, or vice versa.
119*387f9dfdSAndroid Build Coastguard Worker        return;
120*387f9dfdSAndroid Build Coastguard Worker    }
121*387f9dfdSAndroid Build Coastguard Worker    u64 delta = ts - *tsp;
122*387f9dfdSAndroid Build Coastguard Worker    FACTOR
123*387f9dfdSAndroid Build Coastguard Worker    STORE
124*387f9dfdSAndroid Build Coastguard Worker}
125*387f9dfdSAndroid Build Coastguard Worker
126*387f9dfdSAndroid Build Coastguard Workerint sched_switch(struct pt_regs *ctx, struct task_struct *prev)
127*387f9dfdSAndroid Build Coastguard Worker{
128*387f9dfdSAndroid Build Coastguard Worker    u64 ts = bpf_ktime_get_ns();
129*387f9dfdSAndroid Build Coastguard Worker    u64 pid_tgid = bpf_get_current_pid_tgid();
130*387f9dfdSAndroid Build Coastguard Worker    u32 tgid = pid_tgid >> 32, pid = pid_tgid;
131*387f9dfdSAndroid Build Coastguard Worker    u32 cpu = bpf_get_smp_processor_id();
132*387f9dfdSAndroid Build Coastguard Worker
133*387f9dfdSAndroid Build Coastguard Worker    u32 prev_pid = prev->pid;
134*387f9dfdSAndroid Build Coastguard Worker    u32 prev_tgid = prev->tgid;
135*387f9dfdSAndroid Build Coastguard Worker#ifdef ONCPU
136*387f9dfdSAndroid Build Coastguard Worker    update_hist(prev_tgid, prev_pid, cpu, ts);
137*387f9dfdSAndroid Build Coastguard Worker#else
138*387f9dfdSAndroid Build Coastguard Worker    store_start(prev_tgid, prev_pid, cpu, ts);
139*387f9dfdSAndroid Build Coastguard Worker#endif
140*387f9dfdSAndroid Build Coastguard Worker
141*387f9dfdSAndroid Build Coastguard WorkerBAIL:
142*387f9dfdSAndroid Build Coastguard Worker#ifdef ONCPU
143*387f9dfdSAndroid Build Coastguard Worker    store_start(tgid, pid, cpu, ts);
144*387f9dfdSAndroid Build Coastguard Worker#else
145*387f9dfdSAndroid Build Coastguard Worker    update_hist(tgid, pid, cpu, ts);
146*387f9dfdSAndroid Build Coastguard Worker#endif
147*387f9dfdSAndroid Build Coastguard Worker
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 Workerif args.pid:
153*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('PID_FILTER', 'tgid != %s' % args.pid)
154*387f9dfdSAndroid Build Coastguard Workerelse:
155*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('PID_FILTER', '0')
156*387f9dfdSAndroid Build Coastguard Worker
157*387f9dfdSAndroid Build Coastguard Worker# set idle filter
158*387f9dfdSAndroid Build Coastguard Workeridle_filter = 'pid == 0'
159*387f9dfdSAndroid Build Coastguard Workerif args.include_idle:
160*387f9dfdSAndroid Build Coastguard Worker    idle_filter = '0'
161*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('IDLE_FILTER', idle_filter)
162*387f9dfdSAndroid Build Coastguard Worker
163*387f9dfdSAndroid Build Coastguard Workerif args.milliseconds:
164*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;')
165*387f9dfdSAndroid Build Coastguard Worker    label = "msecs"
166*387f9dfdSAndroid Build Coastguard Workerelse:
167*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;')
168*387f9dfdSAndroid Build Coastguard Worker    label = "usecs"
169*387f9dfdSAndroid Build Coastguard Worker
170*387f9dfdSAndroid Build Coastguard Workerstorage_str = ""
171*387f9dfdSAndroid Build Coastguard Workerstore_str = ""
172*387f9dfdSAndroid Build Coastguard Worker
173*387f9dfdSAndroid Build Coastguard Workerif args.pids or args.tids:
174*387f9dfdSAndroid Build Coastguard Worker    section = "pid"
175*387f9dfdSAndroid Build Coastguard Worker    pid = "tgid"
176*387f9dfdSAndroid Build Coastguard Worker    if args.tids:
177*387f9dfdSAndroid Build Coastguard Worker        pid = "pid"
178*387f9dfdSAndroid Build Coastguard Worker        section = "tid"
179*387f9dfdSAndroid Build Coastguard Worker    storage_str += "BPF_HISTOGRAM(dist, pid_key_t, MAX_PID);"
180*387f9dfdSAndroid Build Coastguard Worker    store_str += """
181*387f9dfdSAndroid Build Coastguard Worker    pid_key_t key = {.id = """ + pid + """, .slot = bpf_log2l(delta)};
182*387f9dfdSAndroid Build Coastguard Worker    dist.increment(key);
183*387f9dfdSAndroid Build Coastguard Worker    """
184*387f9dfdSAndroid Build Coastguard Workerelse:
185*387f9dfdSAndroid Build Coastguard Worker    section = ""
186*387f9dfdSAndroid Build Coastguard Worker    storage_str += "BPF_HISTOGRAM(dist);"
187*387f9dfdSAndroid Build Coastguard Worker    store_str += "dist.atomic_increment(bpf_log2l(delta));"
188*387f9dfdSAndroid Build Coastguard Worker
189*387f9dfdSAndroid Build Coastguard Workerif args.extension:
190*387f9dfdSAndroid Build Coastguard Worker    storage_str += "BPF_ARRAY(extension, ext_val_t, 1);"
191*387f9dfdSAndroid Build Coastguard Worker    store_str += """
192*387f9dfdSAndroid Build Coastguard Worker    u32 index = 0;
193*387f9dfdSAndroid Build Coastguard Worker    ext_val_t *ext_val = extension.lookup(&index);
194*387f9dfdSAndroid Build Coastguard Worker    if (ext_val) {
195*387f9dfdSAndroid Build Coastguard Worker        lock_xadd(&ext_val->total, delta);
196*387f9dfdSAndroid Build Coastguard Worker        lock_xadd(&ext_val->count, 1);
197*387f9dfdSAndroid Build Coastguard Worker    }
198*387f9dfdSAndroid Build Coastguard Worker    """
199*387f9dfdSAndroid Build Coastguard Worker
200*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace("STORAGE", storage_str)
201*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace("STORE", store_str)
202*387f9dfdSAndroid Build Coastguard Worker
203*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf:
204*387f9dfdSAndroid Build Coastguard Worker    print(bpf_text)
205*387f9dfdSAndroid Build Coastguard Worker    if args.ebpf:
206*387f9dfdSAndroid Build Coastguard Worker        exit()
207*387f9dfdSAndroid Build Coastguard Worker
208*387f9dfdSAndroid Build Coastguard Workermax_pid = int(open("/proc/sys/kernel/pid_max").read())
209*387f9dfdSAndroid Build Coastguard Worker
210*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text, cflags=["-DMAX_PID=%d" % max_pid])
211*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event_re="^finish_task_switch$|^finish_task_switch\.isra\.\d$",
212*387f9dfdSAndroid Build Coastguard Worker                fn_name="sched_switch")
213*387f9dfdSAndroid Build Coastguard Worker
214*387f9dfdSAndroid Build Coastguard Workerprint("Tracing %s-CPU time... Hit Ctrl-C to end." %
215*387f9dfdSAndroid Build Coastguard Worker      ("off" if args.offcpu else "on"))
216*387f9dfdSAndroid Build Coastguard Worker
217*387f9dfdSAndroid Build Coastguard Workerexiting = 0 if args.interval else 1
218*387f9dfdSAndroid Build Coastguard Workerdist = b.get_table("dist")
219*387f9dfdSAndroid Build Coastguard Workerif args.extension:
220*387f9dfdSAndroid Build Coastguard Worker    extension = b.get_table("extension")
221*387f9dfdSAndroid Build Coastguard Workerwhile (1):
222*387f9dfdSAndroid Build Coastguard Worker    try:
223*387f9dfdSAndroid Build Coastguard Worker        sleep(int(args.interval))
224*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
225*387f9dfdSAndroid Build Coastguard Worker        exiting = 1
226*387f9dfdSAndroid Build Coastguard Worker
227*387f9dfdSAndroid Build Coastguard Worker    print()
228*387f9dfdSAndroid Build Coastguard Worker    if args.timestamp:
229*387f9dfdSAndroid Build Coastguard Worker        print("%-8s\n" % strftime("%H:%M:%S"), end="")
230*387f9dfdSAndroid Build Coastguard Worker
231*387f9dfdSAndroid Build Coastguard Worker    def pid_to_comm(pid):
232*387f9dfdSAndroid Build Coastguard Worker        try:
233*387f9dfdSAndroid Build Coastguard Worker            comm = open("/proc/%d/comm" % pid, "r").read()
234*387f9dfdSAndroid Build Coastguard Worker            return "%d %s" % (pid, comm)
235*387f9dfdSAndroid Build Coastguard Worker        except IOError:
236*387f9dfdSAndroid Build Coastguard Worker            return str(pid)
237*387f9dfdSAndroid Build Coastguard Worker
238*387f9dfdSAndroid Build Coastguard Worker    dist.print_log2_hist(label, section, section_print_fn=pid_to_comm)
239*387f9dfdSAndroid Build Coastguard Worker
240*387f9dfdSAndroid Build Coastguard Worker    if args.extension:
241*387f9dfdSAndroid Build Coastguard Worker        total = extension[0].total
242*387f9dfdSAndroid Build Coastguard Worker        count = extension[0].count
243*387f9dfdSAndroid Build Coastguard Worker        if count > 0:
244*387f9dfdSAndroid Build Coastguard Worker            print("\navg = %ld %s, total: %ld %s, count: %ld\n" %
245*387f9dfdSAndroid Build Coastguard Worker                (total / count, label, total, label, count))
246*387f9dfdSAndroid Build Coastguard Worker        extension.clear()
247*387f9dfdSAndroid Build Coastguard Worker
248*387f9dfdSAndroid Build Coastguard Worker    dist.clear()
249*387f9dfdSAndroid Build Coastguard Worker
250*387f9dfdSAndroid Build Coastguard Worker    countdown -= 1
251*387f9dfdSAndroid Build Coastguard Worker    if exiting or countdown == 0:
252*387f9dfdSAndroid Build Coastguard Worker        exit()
253