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# funclatency Time functions and print latency as a histogram. 5*387f9dfdSAndroid Build Coastguard Worker# For Linux, uses BCC, eBPF. 6*387f9dfdSAndroid Build Coastguard Worker# 7*387f9dfdSAndroid Build Coastguard Worker# USAGE: funclatency [-h] [-p PID] [-i INTERVAL] [-T] [-u] [-m] [-F] [-r] [-v] 8*387f9dfdSAndroid Build Coastguard Worker# pattern 9*387f9dfdSAndroid Build Coastguard Worker# 10*387f9dfdSAndroid Build Coastguard Worker# Run "funclatency -h" for full usage. 11*387f9dfdSAndroid Build Coastguard Worker# 12*387f9dfdSAndroid Build Coastguard Worker# The pattern is a string with optional '*' wildcards, similar to file 13*387f9dfdSAndroid Build Coastguard Worker# globbing. If you'd prefer to use regular expressions, use the -r option. 14*387f9dfdSAndroid Build Coastguard Worker# 15*387f9dfdSAndroid Build Coastguard Worker# Without the '-l' option, only the innermost calls will be recorded. 16*387f9dfdSAndroid Build Coastguard Worker# Use '-l LEVEL' to record the outermost n levels of nested/recursive functions. 17*387f9dfdSAndroid Build Coastguard Worker# 18*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) 2015 Brendan Gregg. 19*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) 2021 Chenyue Zhou. 20*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License") 21*387f9dfdSAndroid Build Coastguard Worker# 22*387f9dfdSAndroid Build Coastguard Worker# 20-Sep-2015 Brendan Gregg Created this. 23*387f9dfdSAndroid Build Coastguard Worker# 06-Oct-2016 Sasha Goldshtein Added user function support. 24*387f9dfdSAndroid Build Coastguard Worker# 14-Apr-2021 Chenyue Zhou Added nested or recursive function support. 25*387f9dfdSAndroid Build Coastguard Worker 26*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function 27*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF 28*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime 29*387f9dfdSAndroid Build Coastguard Workerimport argparse 30*387f9dfdSAndroid Build Coastguard Workerimport signal 31*387f9dfdSAndroid Build Coastguard Worker 32*387f9dfdSAndroid Build Coastguard Worker# arguments 33*387f9dfdSAndroid Build Coastguard Workerexamples = """examples: 34*387f9dfdSAndroid Build Coastguard Worker ./funclatency do_sys_open # time the do_sys_open() kernel function 35*387f9dfdSAndroid Build Coastguard Worker ./funclatency c:read # time the read() C library function 36*387f9dfdSAndroid Build Coastguard Worker ./funclatency -u vfs_read # time vfs_read(), in microseconds 37*387f9dfdSAndroid Build Coastguard Worker ./funclatency -m do_nanosleep # time do_nanosleep(), in milliseconds 38*387f9dfdSAndroid Build Coastguard Worker ./funclatency -i 2 -d 10 c:open # output every 2 seconds, for duration 10s 39*387f9dfdSAndroid Build Coastguard Worker ./funclatency -mTi 5 vfs_read # output every 5 seconds, with timestamps 40*387f9dfdSAndroid Build Coastguard Worker ./funclatency -p 181 vfs_read # time process 181 only 41*387f9dfdSAndroid Build Coastguard Worker ./funclatency 'vfs_fstat*' # time both vfs_fstat() and vfs_fstatat() 42*387f9dfdSAndroid Build Coastguard Worker ./funclatency 'c:*printf' # time the *printf family of functions 43*387f9dfdSAndroid Build Coastguard Worker ./funclatency -F 'vfs_r*' # show one histogram per matched function 44*387f9dfdSAndroid Build Coastguard Worker""" 45*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser( 46*387f9dfdSAndroid Build Coastguard Worker description="Time functions and print latency as a histogram", 47*387f9dfdSAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter, 48*387f9dfdSAndroid Build Coastguard Worker epilog=examples) 49*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-p", "--pid", type=int, 50*387f9dfdSAndroid Build Coastguard Worker help="trace this PID only") 51*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-i", "--interval", type=int, 52*387f9dfdSAndroid Build Coastguard Worker help="summary interval, in seconds") 53*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-d", "--duration", type=int, 54*387f9dfdSAndroid Build Coastguard Worker help="total duration of trace, in seconds") 55*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-T", "--timestamp", action="store_true", 56*387f9dfdSAndroid Build Coastguard Worker help="include timestamp on output") 57*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-u", "--microseconds", action="store_true", 58*387f9dfdSAndroid Build Coastguard Worker help="microsecond histogram") 59*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-m", "--milliseconds", action="store_true", 60*387f9dfdSAndroid Build Coastguard Worker help="millisecond histogram") 61*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-F", "--function", action="store_true", 62*387f9dfdSAndroid Build Coastguard Worker help="show a separate histogram per function") 63*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-r", "--regexp", action="store_true", 64*387f9dfdSAndroid Build Coastguard Worker help="use regular expressions. Default is \"*\" wildcards only.") 65*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-l", "--level", type=int, 66*387f9dfdSAndroid Build Coastguard Worker help="set the level of nested or recursive functions") 67*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-v", "--verbose", action="store_true", 68*387f9dfdSAndroid Build Coastguard Worker help="print the BPF program (for debugging purposes)") 69*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("pattern", 70*387f9dfdSAndroid Build Coastguard Worker help="search expression for functions") 71*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true", 72*387f9dfdSAndroid Build Coastguard Worker help=argparse.SUPPRESS) 73*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args() 74*387f9dfdSAndroid Build Coastguard Workerif args.duration and not args.interval: 75*387f9dfdSAndroid Build Coastguard Worker args.interval = args.duration 76*387f9dfdSAndroid Build Coastguard Workerif not args.interval: 77*387f9dfdSAndroid Build Coastguard Worker args.interval = 99999999 78*387f9dfdSAndroid Build Coastguard Worker 79*387f9dfdSAndroid Build Coastguard Workerdef bail(error): 80*387f9dfdSAndroid Build Coastguard Worker print("Error: " + error) 81*387f9dfdSAndroid Build Coastguard Worker exit(1) 82*387f9dfdSAndroid Build Coastguard Worker 83*387f9dfdSAndroid Build Coastguard Workerparts = args.pattern.split(':') 84*387f9dfdSAndroid Build Coastguard Workerif len(parts) == 1: 85*387f9dfdSAndroid Build Coastguard Worker library = None 86*387f9dfdSAndroid Build Coastguard Worker pattern = args.pattern 87*387f9dfdSAndroid Build Coastguard Workerelif len(parts) == 2: 88*387f9dfdSAndroid Build Coastguard Worker library = parts[0] 89*387f9dfdSAndroid Build Coastguard Worker libpath = BPF.find_library(library) or BPF.find_exe(library) 90*387f9dfdSAndroid Build Coastguard Worker if not libpath: 91*387f9dfdSAndroid Build Coastguard Worker bail("can't resolve library %s" % library) 92*387f9dfdSAndroid Build Coastguard Worker library = libpath 93*387f9dfdSAndroid Build Coastguard Worker pattern = parts[1] 94*387f9dfdSAndroid Build Coastguard Workerelse: 95*387f9dfdSAndroid Build Coastguard Worker bail("unrecognized pattern format '%s'" % args.pattern) 96*387f9dfdSAndroid Build Coastguard Worker 97*387f9dfdSAndroid Build Coastguard Workerif not args.regexp: 98*387f9dfdSAndroid Build Coastguard Worker pattern = pattern.replace('*', '.*') 99*387f9dfdSAndroid Build Coastguard Worker pattern = '^' + pattern + '$' 100*387f9dfdSAndroid Build Coastguard Worker 101*387f9dfdSAndroid Build Coastguard Worker# define BPF program 102*387f9dfdSAndroid Build Coastguard Workerbpf_text = """ 103*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 104*387f9dfdSAndroid Build Coastguard Worker 105*387f9dfdSAndroid Build Coastguard Workertypedef struct ip_pid { 106*387f9dfdSAndroid Build Coastguard Worker u64 ip; 107*387f9dfdSAndroid Build Coastguard Worker u64 pid; 108*387f9dfdSAndroid Build Coastguard Worker} ip_pid_t; 109*387f9dfdSAndroid Build Coastguard Worker 110*387f9dfdSAndroid Build Coastguard Workertypedef struct hist_key { 111*387f9dfdSAndroid Build Coastguard Worker ip_pid_t key; 112*387f9dfdSAndroid Build Coastguard Worker u64 slot; 113*387f9dfdSAndroid Build Coastguard Worker} hist_key_t; 114*387f9dfdSAndroid Build Coastguard Worker 115*387f9dfdSAndroid Build Coastguard WorkerTYPEDEF 116*387f9dfdSAndroid Build Coastguard Worker 117*387f9dfdSAndroid Build Coastguard WorkerBPF_ARRAY(avg, u64, 2); 118*387f9dfdSAndroid Build Coastguard WorkerSTORAGE 119*387f9dfdSAndroid Build Coastguard WorkerFUNCTION 120*387f9dfdSAndroid Build Coastguard Worker 121*387f9dfdSAndroid Build Coastguard Workerint trace_func_entry(struct pt_regs *ctx) 122*387f9dfdSAndroid Build Coastguard Worker{ 123*387f9dfdSAndroid Build Coastguard Worker u64 pid_tgid = bpf_get_current_pid_tgid(); 124*387f9dfdSAndroid Build Coastguard Worker u32 pid = pid_tgid; 125*387f9dfdSAndroid Build Coastguard Worker u32 tgid = pid_tgid >> 32; 126*387f9dfdSAndroid Build Coastguard Worker u64 ts = bpf_ktime_get_ns(); 127*387f9dfdSAndroid Build Coastguard Worker 128*387f9dfdSAndroid Build Coastguard Worker FILTER 129*387f9dfdSAndroid Build Coastguard Worker ENTRYSTORE 130*387f9dfdSAndroid Build Coastguard Worker 131*387f9dfdSAndroid Build Coastguard Worker return 0; 132*387f9dfdSAndroid Build Coastguard Worker} 133*387f9dfdSAndroid Build Coastguard Worker 134*387f9dfdSAndroid Build Coastguard Workerint trace_func_return(struct pt_regs *ctx) 135*387f9dfdSAndroid Build Coastguard Worker{ 136*387f9dfdSAndroid Build Coastguard Worker u64 *tsp, delta; 137*387f9dfdSAndroid Build Coastguard Worker u64 pid_tgid = bpf_get_current_pid_tgid(); 138*387f9dfdSAndroid Build Coastguard Worker u32 pid = pid_tgid; 139*387f9dfdSAndroid Build Coastguard Worker u32 tgid = pid_tgid >> 32; 140*387f9dfdSAndroid Build Coastguard Worker 141*387f9dfdSAndroid Build Coastguard Worker // calculate delta time 142*387f9dfdSAndroid Build Coastguard Worker CALCULATE 143*387f9dfdSAndroid Build Coastguard Worker 144*387f9dfdSAndroid Build Coastguard Worker u32 lat = 0; 145*387f9dfdSAndroid Build Coastguard Worker u32 cnt = 1; 146*387f9dfdSAndroid Build Coastguard Worker avg.atomic_increment(lat, delta); 147*387f9dfdSAndroid Build Coastguard Worker avg.atomic_increment(cnt); 148*387f9dfdSAndroid Build Coastguard Worker 149*387f9dfdSAndroid Build Coastguard Worker FACTOR 150*387f9dfdSAndroid Build Coastguard Worker 151*387f9dfdSAndroid Build Coastguard Worker // store as histogram 152*387f9dfdSAndroid Build Coastguard Worker STORE 153*387f9dfdSAndroid Build Coastguard Worker 154*387f9dfdSAndroid Build Coastguard Worker return 0; 155*387f9dfdSAndroid Build Coastguard Worker} 156*387f9dfdSAndroid Build Coastguard Worker""" 157*387f9dfdSAndroid Build Coastguard Worker 158*387f9dfdSAndroid Build Coastguard Worker# do we need to store the IP and pid for each invocation? 159*387f9dfdSAndroid Build Coastguard Workerneed_key = args.function or (library and not args.pid) 160*387f9dfdSAndroid Build Coastguard Worker 161*387f9dfdSAndroid Build Coastguard Worker# code substitutions 162*387f9dfdSAndroid Build Coastguard Workerif args.pid: 163*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('FILTER', 164*387f9dfdSAndroid Build Coastguard Worker 'if (tgid != %d) { return 0; }' % args.pid) 165*387f9dfdSAndroid Build Coastguard Workerelse: 166*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('FILTER', '') 167*387f9dfdSAndroid Build Coastguard Workerif args.milliseconds: 168*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000000;') 169*387f9dfdSAndroid Build Coastguard Worker label = "msecs" 170*387f9dfdSAndroid Build Coastguard Workerelif args.microseconds: 171*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('FACTOR', 'delta /= 1000;') 172*387f9dfdSAndroid Build Coastguard Worker label = "usecs" 173*387f9dfdSAndroid Build Coastguard Workerelse: 174*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('FACTOR', '') 175*387f9dfdSAndroid Build Coastguard Worker label = "nsecs" 176*387f9dfdSAndroid Build Coastguard Workerif need_key: 177*387f9dfdSAndroid Build Coastguard Worker pid = '-1' if not library else 'tgid' 178*387f9dfdSAndroid Build Coastguard Worker 179*387f9dfdSAndroid Build Coastguard Worker if args.level and args.level > 1: 180*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('TYPEDEF', 181*387f9dfdSAndroid Build Coastguard Worker """ 182*387f9dfdSAndroid Build Coastguard Worker#define STACK_DEPTH %s 183*387f9dfdSAndroid Build Coastguard Worker 184*387f9dfdSAndroid Build Coastguard Workertypedef struct { 185*387f9dfdSAndroid Build Coastguard Worker u64 ip; 186*387f9dfdSAndroid Build Coastguard Worker u64 start_ts; 187*387f9dfdSAndroid Build Coastguard Worker} func_cache_t; 188*387f9dfdSAndroid Build Coastguard Worker 189*387f9dfdSAndroid Build Coastguard Worker/* LIFO */ 190*387f9dfdSAndroid Build Coastguard Workertypedef struct { 191*387f9dfdSAndroid Build Coastguard Worker u32 head; 192*387f9dfdSAndroid Build Coastguard Worker func_cache_t cache[STACK_DEPTH]; 193*387f9dfdSAndroid Build Coastguard Worker} func_stack_t; 194*387f9dfdSAndroid Build Coastguard Worker """ % args.level) 195*387f9dfdSAndroid Build Coastguard Worker 196*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('STORAGE', 197*387f9dfdSAndroid Build Coastguard Worker """ 198*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(func_stack, u32, func_stack_t); 199*387f9dfdSAndroid Build Coastguard WorkerBPF_HISTOGRAM(dist, hist_key_t); 200*387f9dfdSAndroid Build Coastguard Worker """) 201*387f9dfdSAndroid Build Coastguard Worker 202*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('FUNCTION', 203*387f9dfdSAndroid Build Coastguard Worker """ 204*387f9dfdSAndroid Build Coastguard Workerstatic inline int stack_pop(func_stack_t *stack, func_cache_t *cache) { 205*387f9dfdSAndroid Build Coastguard Worker if (stack->head <= 0) { 206*387f9dfdSAndroid Build Coastguard Worker return -1; 207*387f9dfdSAndroid Build Coastguard Worker } 208*387f9dfdSAndroid Build Coastguard Worker 209*387f9dfdSAndroid Build Coastguard Worker u32 index = --stack->head; 210*387f9dfdSAndroid Build Coastguard Worker if (index < STACK_DEPTH) { 211*387f9dfdSAndroid Build Coastguard Worker /* bound check */ 212*387f9dfdSAndroid Build Coastguard Worker cache->ip = stack->cache[index].ip; 213*387f9dfdSAndroid Build Coastguard Worker cache->start_ts = stack->cache[index].start_ts; 214*387f9dfdSAndroid Build Coastguard Worker } 215*387f9dfdSAndroid Build Coastguard Worker 216*387f9dfdSAndroid Build Coastguard Worker return 0; 217*387f9dfdSAndroid Build Coastguard Worker} 218*387f9dfdSAndroid Build Coastguard Worker 219*387f9dfdSAndroid Build Coastguard Workerstatic inline int stack_push(func_stack_t *stack, func_cache_t *cache) { 220*387f9dfdSAndroid Build Coastguard Worker u32 index = stack->head; 221*387f9dfdSAndroid Build Coastguard Worker 222*387f9dfdSAndroid Build Coastguard Worker if (index > STACK_DEPTH - 1) { 223*387f9dfdSAndroid Build Coastguard Worker /* bound check */ 224*387f9dfdSAndroid Build Coastguard Worker return -1; 225*387f9dfdSAndroid Build Coastguard Worker } 226*387f9dfdSAndroid Build Coastguard Worker 227*387f9dfdSAndroid Build Coastguard Worker stack->head++; 228*387f9dfdSAndroid Build Coastguard Worker stack->cache[index].ip = cache->ip; 229*387f9dfdSAndroid Build Coastguard Worker stack->cache[index].start_ts = cache->start_ts; 230*387f9dfdSAndroid Build Coastguard Worker 231*387f9dfdSAndroid Build Coastguard Worker return 0; 232*387f9dfdSAndroid Build Coastguard Worker} 233*387f9dfdSAndroid Build Coastguard Worker """) 234*387f9dfdSAndroid Build Coastguard Worker 235*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('ENTRYSTORE', 236*387f9dfdSAndroid Build Coastguard Worker """ 237*387f9dfdSAndroid Build Coastguard Worker u64 ip = PT_REGS_IP(ctx); 238*387f9dfdSAndroid Build Coastguard Worker func_cache_t cache = { 239*387f9dfdSAndroid Build Coastguard Worker .ip = ip, 240*387f9dfdSAndroid Build Coastguard Worker .start_ts = ts, 241*387f9dfdSAndroid Build Coastguard Worker }; 242*387f9dfdSAndroid Build Coastguard Worker 243*387f9dfdSAndroid Build Coastguard Worker func_stack_t *stack = func_stack.lookup(&pid); 244*387f9dfdSAndroid Build Coastguard Worker if (!stack) { 245*387f9dfdSAndroid Build Coastguard Worker func_stack_t new_stack = { 246*387f9dfdSAndroid Build Coastguard Worker .head = 0, 247*387f9dfdSAndroid Build Coastguard Worker }; 248*387f9dfdSAndroid Build Coastguard Worker 249*387f9dfdSAndroid Build Coastguard Worker if (!stack_push(&new_stack, &cache)) { 250*387f9dfdSAndroid Build Coastguard Worker func_stack.update(&pid, &new_stack); 251*387f9dfdSAndroid Build Coastguard Worker } 252*387f9dfdSAndroid Build Coastguard Worker 253*387f9dfdSAndroid Build Coastguard Worker return 0; 254*387f9dfdSAndroid Build Coastguard Worker } 255*387f9dfdSAndroid Build Coastguard Worker 256*387f9dfdSAndroid Build Coastguard Worker if (!stack_push(stack, &cache)) { 257*387f9dfdSAndroid Build Coastguard Worker func_stack.update(&pid, stack); 258*387f9dfdSAndroid Build Coastguard Worker } 259*387f9dfdSAndroid Build Coastguard Worker """) 260*387f9dfdSAndroid Build Coastguard Worker 261*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('CALCULATE', 262*387f9dfdSAndroid Build Coastguard Worker """ 263*387f9dfdSAndroid Build Coastguard Worker u64 ip, start_ts; 264*387f9dfdSAndroid Build Coastguard Worker func_stack_t *stack = func_stack.lookup(&pid); 265*387f9dfdSAndroid Build Coastguard Worker if (!stack) { 266*387f9dfdSAndroid Build Coastguard Worker /* miss start */ 267*387f9dfdSAndroid Build Coastguard Worker return 0; 268*387f9dfdSAndroid Build Coastguard Worker } 269*387f9dfdSAndroid Build Coastguard Worker 270*387f9dfdSAndroid Build Coastguard Worker func_cache_t cache = {}; 271*387f9dfdSAndroid Build Coastguard Worker if (stack_pop(stack, &cache)) { 272*387f9dfdSAndroid Build Coastguard Worker func_stack.delete(&pid); 273*387f9dfdSAndroid Build Coastguard Worker 274*387f9dfdSAndroid Build Coastguard Worker return 0; 275*387f9dfdSAndroid Build Coastguard Worker } 276*387f9dfdSAndroid Build Coastguard Worker ip = cache.ip; 277*387f9dfdSAndroid Build Coastguard Worker start_ts = cache.start_ts; 278*387f9dfdSAndroid Build Coastguard Worker delta = bpf_ktime_get_ns() - start_ts; 279*387f9dfdSAndroid Build Coastguard Worker """) 280*387f9dfdSAndroid Build Coastguard Worker 281*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('STORE', 282*387f9dfdSAndroid Build Coastguard Worker """ 283*387f9dfdSAndroid Build Coastguard Worker hist_key_t key; 284*387f9dfdSAndroid Build Coastguard Worker key.key.ip = ip; 285*387f9dfdSAndroid Build Coastguard Worker key.key.pid = %s; 286*387f9dfdSAndroid Build Coastguard Worker key.slot = bpf_log2l(delta); 287*387f9dfdSAndroid Build Coastguard Worker dist.atomic_increment(key); 288*387f9dfdSAndroid Build Coastguard Worker 289*387f9dfdSAndroid Build Coastguard Worker if (stack->head == 0) { 290*387f9dfdSAndroid Build Coastguard Worker /* empty */ 291*387f9dfdSAndroid Build Coastguard Worker func_stack.delete(&pid); 292*387f9dfdSAndroid Build Coastguard Worker } 293*387f9dfdSAndroid Build Coastguard Worker """ % pid) 294*387f9dfdSAndroid Build Coastguard Worker 295*387f9dfdSAndroid Build Coastguard Worker else: 296*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('STORAGE', 'BPF_HASH(ipaddr, u32);\n'\ 297*387f9dfdSAndroid Build Coastguard Worker 'BPF_HISTOGRAM(dist, hist_key_t);\n'\ 298*387f9dfdSAndroid Build Coastguard Worker 'BPF_HASH(start, u32);') 299*387f9dfdSAndroid Build Coastguard Worker # stash the IP on entry, as on return it's kretprobe_trampoline: 300*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('ENTRYSTORE', 301*387f9dfdSAndroid Build Coastguard Worker 'u64 ip = PT_REGS_IP(ctx); ipaddr.update(&pid, &ip);'\ 302*387f9dfdSAndroid Build Coastguard Worker ' start.update(&pid, &ts);') 303*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('STORE', 304*387f9dfdSAndroid Build Coastguard Worker """ 305*387f9dfdSAndroid Build Coastguard Worker u64 ip, *ipp = ipaddr.lookup(&pid); 306*387f9dfdSAndroid Build Coastguard Worker if (ipp) { 307*387f9dfdSAndroid Build Coastguard Worker ip = *ipp; 308*387f9dfdSAndroid Build Coastguard Worker hist_key_t key; 309*387f9dfdSAndroid Build Coastguard Worker key.key.ip = ip; 310*387f9dfdSAndroid Build Coastguard Worker key.key.pid = %s; 311*387f9dfdSAndroid Build Coastguard Worker key.slot = bpf_log2l(delta); 312*387f9dfdSAndroid Build Coastguard Worker dist.atomic_increment(key); 313*387f9dfdSAndroid Build Coastguard Worker ipaddr.delete(&pid); 314*387f9dfdSAndroid Build Coastguard Worker } 315*387f9dfdSAndroid Build Coastguard Worker """ % pid) 316*387f9dfdSAndroid Build Coastguard Workerelse: 317*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(dist);\n'\ 318*387f9dfdSAndroid Build Coastguard Worker 'BPF_HASH(start, u32);') 319*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('ENTRYSTORE', 'start.update(&pid, &ts);') 320*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('STORE', 321*387f9dfdSAndroid Build Coastguard Worker 'dist.atomic_increment(bpf_log2l(delta));') 322*387f9dfdSAndroid Build Coastguard Worker 323*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('TYPEDEF', '') 324*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('FUNCTION', '') 325*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('CALCULATE', 326*387f9dfdSAndroid Build Coastguard Worker """ 327*387f9dfdSAndroid Build Coastguard Worker tsp = start.lookup(&pid); 328*387f9dfdSAndroid Build Coastguard Worker if (tsp == 0) { 329*387f9dfdSAndroid Build Coastguard Worker return 0; // missed start 330*387f9dfdSAndroid Build Coastguard Worker } 331*387f9dfdSAndroid Build Coastguard Worker delta = bpf_ktime_get_ns() - *tsp; 332*387f9dfdSAndroid Build Coastguard Worker start.delete(&pid); 333*387f9dfdSAndroid Build Coastguard Worker """) 334*387f9dfdSAndroid Build Coastguard Worker 335*387f9dfdSAndroid Build Coastguard Workerif args.verbose or args.ebpf: 336*387f9dfdSAndroid Build Coastguard Worker print(bpf_text) 337*387f9dfdSAndroid Build Coastguard Worker if args.ebpf: 338*387f9dfdSAndroid Build Coastguard Worker exit() 339*387f9dfdSAndroid Build Coastguard Worker 340*387f9dfdSAndroid Build Coastguard Worker# signal handler 341*387f9dfdSAndroid Build Coastguard Workerdef signal_ignore(signal, frame): 342*387f9dfdSAndroid Build Coastguard Worker print() 343*387f9dfdSAndroid Build Coastguard Worker 344*387f9dfdSAndroid Build Coastguard Worker# load BPF program 345*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text) 346*387f9dfdSAndroid Build Coastguard Worker 347*387f9dfdSAndroid Build Coastguard Worker# attach probes 348*387f9dfdSAndroid Build Coastguard Workerif not library: 349*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event_re=pattern, fn_name="trace_func_entry") 350*387f9dfdSAndroid Build Coastguard Worker b.attach_kretprobe(event_re=pattern, fn_name="trace_func_return") 351*387f9dfdSAndroid Build Coastguard Worker matched = b.num_open_kprobes() 352*387f9dfdSAndroid Build Coastguard Workerelse: 353*387f9dfdSAndroid Build Coastguard Worker b.attach_uprobe(name=library, sym_re=pattern, fn_name="trace_func_entry", 354*387f9dfdSAndroid Build Coastguard Worker pid=args.pid or -1) 355*387f9dfdSAndroid Build Coastguard Worker b.attach_uretprobe(name=library, sym_re=pattern, 356*387f9dfdSAndroid Build Coastguard Worker fn_name="trace_func_return", pid=args.pid or -1) 357*387f9dfdSAndroid Build Coastguard Worker matched = b.num_open_uprobes() 358*387f9dfdSAndroid Build Coastguard Worker 359*387f9dfdSAndroid Build Coastguard Workerif matched == 0: 360*387f9dfdSAndroid Build Coastguard Worker print("0 functions matched by \"%s\". Exiting." % args.pattern) 361*387f9dfdSAndroid Build Coastguard Worker exit() 362*387f9dfdSAndroid Build Coastguard Worker 363*387f9dfdSAndroid Build Coastguard Worker# header 364*387f9dfdSAndroid Build Coastguard Workerprint("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." % 365*387f9dfdSAndroid Build Coastguard Worker (matched / 2, args.pattern)) 366*387f9dfdSAndroid Build Coastguard Worker 367*387f9dfdSAndroid Build Coastguard Worker# output 368*387f9dfdSAndroid Build Coastguard Workerdef print_section(key): 369*387f9dfdSAndroid Build Coastguard Worker if not library: 370*387f9dfdSAndroid Build Coastguard Worker return BPF.sym(key[0], -1).decode('utf-8', 'replace') 371*387f9dfdSAndroid Build Coastguard Worker else: 372*387f9dfdSAndroid Build Coastguard Worker return "%s [%d]" % (BPF.sym(key[0], key[1]).decode('utf-8', 'replace'), key[1]) 373*387f9dfdSAndroid Build Coastguard Worker 374*387f9dfdSAndroid Build Coastguard Workerexiting = 0 if args.interval else 1 375*387f9dfdSAndroid Build Coastguard Workerseconds = 0 376*387f9dfdSAndroid Build Coastguard Workerdist = b.get_table("dist") 377*387f9dfdSAndroid Build Coastguard Workerwhile (1): 378*387f9dfdSAndroid Build Coastguard Worker try: 379*387f9dfdSAndroid Build Coastguard Worker sleep(args.interval) 380*387f9dfdSAndroid Build Coastguard Worker seconds += args.interval 381*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 382*387f9dfdSAndroid Build Coastguard Worker exiting = 1 383*387f9dfdSAndroid Build Coastguard Worker # as cleanup can take many seconds, trap Ctrl-C: 384*387f9dfdSAndroid Build Coastguard Worker signal.signal(signal.SIGINT, signal_ignore) 385*387f9dfdSAndroid Build Coastguard Worker if args.duration and seconds >= args.duration: 386*387f9dfdSAndroid Build Coastguard Worker exiting = 1 387*387f9dfdSAndroid Build Coastguard Worker 388*387f9dfdSAndroid Build Coastguard Worker print() 389*387f9dfdSAndroid Build Coastguard Worker if args.timestamp: 390*387f9dfdSAndroid Build Coastguard Worker print("%-8s\n" % strftime("%H:%M:%S"), end="") 391*387f9dfdSAndroid Build Coastguard Worker 392*387f9dfdSAndroid Build Coastguard Worker if need_key: 393*387f9dfdSAndroid Build Coastguard Worker dist.print_log2_hist(label, "Function", section_print_fn=print_section, 394*387f9dfdSAndroid Build Coastguard Worker bucket_fn=lambda k: (k.ip, k.pid)) 395*387f9dfdSAndroid Build Coastguard Worker else: 396*387f9dfdSAndroid Build Coastguard Worker dist.print_log2_hist(label) 397*387f9dfdSAndroid Build Coastguard Worker 398*387f9dfdSAndroid Build Coastguard Worker total = b['avg'][0].value 399*387f9dfdSAndroid Build Coastguard Worker counts = b['avg'][1].value 400*387f9dfdSAndroid Build Coastguard Worker if counts > 0: 401*387f9dfdSAndroid Build Coastguard Worker if label == 'msecs': 402*387f9dfdSAndroid Build Coastguard Worker total /= 1000000 403*387f9dfdSAndroid Build Coastguard Worker elif label == 'usecs': 404*387f9dfdSAndroid Build Coastguard Worker total /= 1000 405*387f9dfdSAndroid Build Coastguard Worker avg = total/counts 406*387f9dfdSAndroid Build Coastguard Worker print("\navg = %ld %s, total: %ld %s, count: %ld\n" %(total/counts, label, total, label, counts)) 407*387f9dfdSAndroid Build Coastguard Worker 408*387f9dfdSAndroid Build Coastguard Worker dist.clear() 409*387f9dfdSAndroid Build Coastguard Worker b['avg'].clear() 410*387f9dfdSAndroid Build Coastguard Worker 411*387f9dfdSAndroid Build Coastguard Worker if exiting: 412*387f9dfdSAndroid Build Coastguard Worker print("Detaching...") 413*387f9dfdSAndroid Build Coastguard Worker exit() 414