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