xref: /aosp_15_r20/external/bcc/tools/funcinterval.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# funcinterval   Time interval between the same function, tracepoint
5*387f9dfdSAndroid Build Coastguard Worker#                as a histogram.
6*387f9dfdSAndroid Build Coastguard Worker#
7*387f9dfdSAndroid Build Coastguard Worker# USAGE: funcinterval [-h] [-p PID] [-i INTERVAL] [-T] [-u] [-m] [-v] pattern
8*387f9dfdSAndroid Build Coastguard Worker#
9*387f9dfdSAndroid Build Coastguard Worker# Run "funcinterval -h" for full usage.
10*387f9dfdSAndroid Build Coastguard Worker#
11*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) 2020 Realtek, Inc.
12*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
13*387f9dfdSAndroid Build Coastguard Worker#
14*387f9dfdSAndroid Build Coastguard Worker# 03-Jun-2020   Edward Wu   Referenced funclatency and created this.
15*387f9dfdSAndroid Build Coastguard Worker
16*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function
17*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF
18*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime
19*387f9dfdSAndroid Build Coastguard Workerimport argparse
20*387f9dfdSAndroid Build Coastguard Workerimport signal
21*387f9dfdSAndroid Build Coastguard Worker
22*387f9dfdSAndroid Build Coastguard Worker# arguments
23*387f9dfdSAndroid Build Coastguard Workerexamples = """examples:
24*387f9dfdSAndroid Build Coastguard Worker    # time the interval of do_sys_open()
25*387f9dfdSAndroid Build Coastguard Worker    ./funcinterval do_sys_open
26*387f9dfdSAndroid Build Coastguard Worker    # time the interval of xhci_ring_ep_doorbell(), in microseconds
27*387f9dfdSAndroid Build Coastguard Worker    ./funcinterval -u xhci_ring_ep_doorbell
28*387f9dfdSAndroid Build Coastguard Worker    # time the interval of do_nanosleep(), in milliseconds
29*387f9dfdSAndroid Build Coastguard Worker    ./funcinterval -m do_nanosleep
30*387f9dfdSAndroid Build Coastguard Worker    # output every 5 seconds, with timestamps
31*387f9dfdSAndroid Build Coastguard Worker    ./funcinterval -mTi 5 vfs_read
32*387f9dfdSAndroid Build Coastguard Worker    # time process 181 only
33*387f9dfdSAndroid Build Coastguard Worker    ./funcinterval -p 181 vfs_read
34*387f9dfdSAndroid Build Coastguard Worker    # time the interval of mm_vmscan_direct_reclaim_begin tracepoint
35*387f9dfdSAndroid Build Coastguard Worker    ./funcinterval t:vmscan:mm_vmscan_direct_reclaim_begin
36*387f9dfdSAndroid Build Coastguard Worker    # time the interval of c:malloc used by top every 3 seconds
37*387f9dfdSAndroid Build Coastguard Worker    ./funcinterval -p `pidof -s top` -i 3 c:malloc
38*387f9dfdSAndroid Build Coastguard Worker    # time /usr/local/bin/python main function
39*387f9dfdSAndroid Build Coastguard Worker    ./funcinterval /usr/local/bin/python:main
40*387f9dfdSAndroid Build Coastguard Worker"""
41*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
42*387f9dfdSAndroid Build Coastguard Worker    description="Time interval and print latency as a histogram",
43*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
44*387f9dfdSAndroid Build Coastguard Worker    epilog=examples)
45*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-p", "--pid", type=int,
46*387f9dfdSAndroid Build Coastguard Worker    help="trace this PID only")
47*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-i", "--interval", type=int,
48*387f9dfdSAndroid Build Coastguard Worker    help="summary interval, in seconds")
49*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-d", "--duration", type=int,
50*387f9dfdSAndroid Build Coastguard Worker    help="total duration of trace, in seconds")
51*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-T", "--timestamp", action="store_true",
52*387f9dfdSAndroid Build Coastguard Worker    help="include timestamp on output")
53*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-u", "--microseconds", action="store_true",
54*387f9dfdSAndroid Build Coastguard Worker    help="microsecond histogram")
55*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-m", "--milliseconds", action="store_true",
56*387f9dfdSAndroid Build Coastguard Worker    help="millisecond histogram")
57*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-v", "--verbose", action="store_true",
58*387f9dfdSAndroid Build Coastguard Worker    help="print the BPF program (for debugging purposes)")
59*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("pattern",
60*387f9dfdSAndroid Build Coastguard Worker    help="Function/Tracepoint name for tracing")
61*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true",
62*387f9dfdSAndroid Build Coastguard Worker    help=argparse.SUPPRESS)
63*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args()
64*387f9dfdSAndroid Build Coastguard Workerif args.duration and not args.interval:
65*387f9dfdSAndroid Build Coastguard Worker    args.interval = args.duration
66*387f9dfdSAndroid Build Coastguard Workerif not args.interval:
67*387f9dfdSAndroid Build Coastguard Worker    args.interval = 99999999
68*387f9dfdSAndroid Build Coastguard Worker
69*387f9dfdSAndroid Build Coastguard Workerdef bail(error):
70*387f9dfdSAndroid Build Coastguard Worker    print("Error: " + error)
71*387f9dfdSAndroid Build Coastguard Worker    exit(1)
72*387f9dfdSAndroid Build Coastguard Worker
73*387f9dfdSAndroid Build Coastguard Worker
74*387f9dfdSAndroid Build Coastguard Workerparts = args.pattern.split(':')
75*387f9dfdSAndroid Build Coastguard Workerif len(parts) == 1:
76*387f9dfdSAndroid Build Coastguard Worker    attach_type = "kprobe function"
77*387f9dfdSAndroid Build Coastguard Worker    pattern = args.pattern
78*387f9dfdSAndroid Build Coastguard Workerelif len(parts) == 2:
79*387f9dfdSAndroid Build Coastguard Worker    attach_type = "uprobe function"
80*387f9dfdSAndroid Build Coastguard Worker    elf = BPF.find_library(parts[0]) or BPF.find_exe(parts[0])
81*387f9dfdSAndroid Build Coastguard Worker    if not elf:
82*387f9dfdSAndroid Build Coastguard Worker        bail("Can't find elf binary %s" % elf)
83*387f9dfdSAndroid Build Coastguard Worker    pattern = parts[1]
84*387f9dfdSAndroid Build Coastguard Workerelif len(parts) == 3:
85*387f9dfdSAndroid Build Coastguard Worker    attach_type = "tracepoint"
86*387f9dfdSAndroid Build Coastguard Worker    pattern = ':'.join(parts[1:])
87*387f9dfdSAndroid Build Coastguard Workerelse:
88*387f9dfdSAndroid Build Coastguard Worker    bail("unrecognized pattern format '%s'" % pattern)
89*387f9dfdSAndroid Build Coastguard Worker
90*387f9dfdSAndroid Build Coastguard Worker# define BPF program
91*387f9dfdSAndroid Build Coastguard Workerbpf_text = """
92*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
93*387f9dfdSAndroid Build Coastguard Worker
94*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(start, u32, u64, 1);
95*387f9dfdSAndroid Build Coastguard WorkerBPF_HISTOGRAM(dist);
96*387f9dfdSAndroid Build Coastguard Worker
97*387f9dfdSAndroid Build Coastguard Workerint trace_func_entry(struct pt_regs *ctx)
98*387f9dfdSAndroid Build Coastguard Worker{
99*387f9dfdSAndroid Build Coastguard Worker    u64 pid_tgid = bpf_get_current_pid_tgid();
100*387f9dfdSAndroid Build Coastguard Worker    u32 index = 0, tgid = pid_tgid >> 32;
101*387f9dfdSAndroid Build Coastguard Worker    u64 *tsp, ts = bpf_ktime_get_ns(), delta;
102*387f9dfdSAndroid Build Coastguard Worker
103*387f9dfdSAndroid Build Coastguard Worker    FILTER
104*387f9dfdSAndroid Build Coastguard Worker    tsp = start.lookup(&index);
105*387f9dfdSAndroid Build Coastguard Worker    if (tsp == 0)
106*387f9dfdSAndroid Build Coastguard Worker        goto out;
107*387f9dfdSAndroid Build Coastguard Worker
108*387f9dfdSAndroid Build Coastguard Worker    delta = ts - *tsp;
109*387f9dfdSAndroid Build Coastguard Worker    FACTOR
110*387f9dfdSAndroid Build Coastguard Worker
111*387f9dfdSAndroid Build Coastguard Worker    // store as histogram
112*387f9dfdSAndroid Build Coastguard Worker    dist.atomic_increment(bpf_log2l(delta));
113*387f9dfdSAndroid Build Coastguard Worker
114*387f9dfdSAndroid Build Coastguard Workerout:
115*387f9dfdSAndroid Build Coastguard Worker    start.update(&index, &ts);
116*387f9dfdSAndroid Build Coastguard Worker
117*387f9dfdSAndroid Build Coastguard Worker    return 0;
118*387f9dfdSAndroid Build Coastguard Worker}
119*387f9dfdSAndroid Build Coastguard Worker"""
120*387f9dfdSAndroid Build Coastguard Worker
121*387f9dfdSAndroid Build Coastguard Worker# code substitutions
122*387f9dfdSAndroid Build Coastguard Workerif args.pid:
123*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('FILTER',
124*387f9dfdSAndroid Build Coastguard Worker        'if (tgid != %d) { return 0; }' % args.pid)
125*387f9dfdSAndroid Build Coastguard Workerelse:
126*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('FILTER', '')
127*387f9dfdSAndroid Build Coastguard Workerif args.milliseconds:
128*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;')
129*387f9dfdSAndroid Build Coastguard Worker    label = "msecs"
130*387f9dfdSAndroid Build Coastguard Workerelif args.microseconds:
131*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;')
132*387f9dfdSAndroid Build Coastguard Worker    label = "usecs"
133*387f9dfdSAndroid Build Coastguard Workerelse:
134*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('FACTOR', '')
135*387f9dfdSAndroid Build Coastguard Worker    label = "nsecs"
136*387f9dfdSAndroid Build Coastguard Worker
137*387f9dfdSAndroid Build Coastguard Workerif args.verbose or args.ebpf:
138*387f9dfdSAndroid Build Coastguard Worker    print(bpf_text)
139*387f9dfdSAndroid Build Coastguard Worker    if args.ebpf:
140*387f9dfdSAndroid Build Coastguard Worker        exit()
141*387f9dfdSAndroid Build Coastguard Worker
142*387f9dfdSAndroid Build Coastguard Worker# signal handler
143*387f9dfdSAndroid Build Coastguard Workerdef signal_ignore(signal, frame):
144*387f9dfdSAndroid Build Coastguard Worker    print()
145*387f9dfdSAndroid Build Coastguard Worker
146*387f9dfdSAndroid Build Coastguard Worker
147*387f9dfdSAndroid Build Coastguard Worker# load BPF program
148*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text)
149*387f9dfdSAndroid Build Coastguard Worker
150*387f9dfdSAndroid Build Coastguard Workerif len(parts) == 1:
151*387f9dfdSAndroid Build Coastguard Worker    b.attach_kprobe(event=pattern, fn_name="trace_func_entry")
152*387f9dfdSAndroid Build Coastguard Worker    matched = b.num_open_kprobes()
153*387f9dfdSAndroid Build Coastguard Workerelif len(parts) == 2:
154*387f9dfdSAndroid Build Coastguard Worker    # sym_re is regular expression for symbols
155*387f9dfdSAndroid Build Coastguard Worker    b.attach_uprobe(name = elf, sym_re = pattern, fn_name = "trace_func_entry",
156*387f9dfdSAndroid Build Coastguard Worker                    pid = args.pid or -1)
157*387f9dfdSAndroid Build Coastguard Worker    matched = b.num_open_uprobes()
158*387f9dfdSAndroid Build Coastguard Workerelif len(parts) == 3:
159*387f9dfdSAndroid Build Coastguard Worker    b.attach_tracepoint(tp=pattern, fn_name="trace_func_entry")
160*387f9dfdSAndroid Build Coastguard Worker    matched = b.num_open_tracepoints()
161*387f9dfdSAndroid Build Coastguard Worker
162*387f9dfdSAndroid Build Coastguard Workerif matched == 0:
163*387f9dfdSAndroid Build Coastguard Worker    print("0 %s matched by \"%s\". Exiting." % (attach_type, pattern))
164*387f9dfdSAndroid Build Coastguard Worker    exit()
165*387f9dfdSAndroid Build Coastguard Worker
166*387f9dfdSAndroid Build Coastguard Worker# header
167*387f9dfdSAndroid Build Coastguard Workerprint("Tracing %s for \"%s\"... Hit Ctrl-C to end." %
168*387f9dfdSAndroid Build Coastguard Worker    (attach_type, pattern))
169*387f9dfdSAndroid Build Coastguard Worker
170*387f9dfdSAndroid Build Coastguard Workerexiting = 0 if args.interval else 1
171*387f9dfdSAndroid Build Coastguard Workerseconds = 0
172*387f9dfdSAndroid Build Coastguard Workerdist = b.get_table("dist")
173*387f9dfdSAndroid Build Coastguard Workerstart = b.get_table("start")
174*387f9dfdSAndroid Build Coastguard Workerwhile (1):
175*387f9dfdSAndroid Build Coastguard Worker    try:
176*387f9dfdSAndroid Build Coastguard Worker        sleep(args.interval)
177*387f9dfdSAndroid Build Coastguard Worker        seconds += args.interval
178*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
179*387f9dfdSAndroid Build Coastguard Worker        exiting = 1
180*387f9dfdSAndroid Build Coastguard Worker        # as cleanup can take many seconds, trap Ctrl-C:
181*387f9dfdSAndroid Build Coastguard Worker        signal.signal(signal.SIGINT, signal_ignore)
182*387f9dfdSAndroid Build Coastguard Worker    if args.duration and seconds >= args.duration:
183*387f9dfdSAndroid Build Coastguard Worker        exiting = 1
184*387f9dfdSAndroid Build Coastguard Worker
185*387f9dfdSAndroid Build Coastguard Worker    print()
186*387f9dfdSAndroid Build Coastguard Worker    if args.timestamp:
187*387f9dfdSAndroid Build Coastguard Worker        print("%-8s\n" % strftime("%H:%M:%S"), end="")
188*387f9dfdSAndroid Build Coastguard Worker
189*387f9dfdSAndroid Build Coastguard Worker    dist.print_log2_hist(label)
190*387f9dfdSAndroid Build Coastguard Worker    dist.clear()
191*387f9dfdSAndroid Build Coastguard Worker    start.clear()
192*387f9dfdSAndroid Build Coastguard Worker
193*387f9dfdSAndroid Build Coastguard Worker    if exiting:
194*387f9dfdSAndroid Build Coastguard Worker        print("Detaching...")
195*387f9dfdSAndroid Build Coastguard Worker        exit()
196