1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/python 2*387f9dfdSAndroid Build Coastguard Worker# @lint-avoid-python-3-compatibility-imports 3*387f9dfdSAndroid Build Coastguard Worker# 4*387f9dfdSAndroid Build Coastguard Worker# uflow Trace method execution flow in high-level languages. 5*387f9dfdSAndroid Build Coastguard Worker# For Linux, uses BCC, eBPF. 6*387f9dfdSAndroid Build Coastguard Worker# 7*387f9dfdSAndroid Build Coastguard Worker# USAGE: uflow [-C CLASS] [-M METHOD] [-v] {java,perl,php,python,ruby,tcl} pid 8*387f9dfdSAndroid Build Coastguard Worker# 9*387f9dfdSAndroid Build Coastguard Worker# Copyright 2016 Sasha Goldshtein 10*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License") 11*387f9dfdSAndroid Build Coastguard Worker# 12*387f9dfdSAndroid Build Coastguard Worker# 27-Oct-2016 Sasha Goldshtein Created this. 13*387f9dfdSAndroid Build Coastguard Worker 14*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function 15*387f9dfdSAndroid Build Coastguard Workerimport argparse 16*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF, USDT, utils 17*387f9dfdSAndroid Build Coastguard Workerimport ctypes as ct 18*387f9dfdSAndroid Build Coastguard Workerimport time 19*387f9dfdSAndroid Build Coastguard Workerimport os 20*387f9dfdSAndroid Build Coastguard Worker 21*387f9dfdSAndroid Build Coastguard Workerlanguages = ["java", "perl", "php", "python", "ruby", "tcl"] 22*387f9dfdSAndroid Build Coastguard Worker 23*387f9dfdSAndroid Build Coastguard Workerexamples = """examples: 24*387f9dfdSAndroid Build Coastguard Worker ./uflow -l java 185 # trace Java method calls in process 185 25*387f9dfdSAndroid Build Coastguard Worker ./uflow -l ruby 134 # trace Ruby method calls in process 134 26*387f9dfdSAndroid Build Coastguard Worker ./uflow -M indexOf -l java 185 # trace only 'indexOf'-prefixed methods 27*387f9dfdSAndroid Build Coastguard Worker ./uflow -C '<stdin>' -l python 180 # trace only REPL-defined methods 28*387f9dfdSAndroid Build Coastguard Worker""" 29*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser( 30*387f9dfdSAndroid Build Coastguard Worker description="Trace method execution flow in high-level languages.", 31*387f9dfdSAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter, 32*387f9dfdSAndroid Build Coastguard Worker epilog=examples) 33*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-l", "--language", choices=languages, 34*387f9dfdSAndroid Build Coastguard Worker help="language to trace") 35*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("pid", type=int, help="process id to attach to") 36*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-M", "--method", 37*387f9dfdSAndroid Build Coastguard Worker help="trace only calls to methods starting with this prefix") 38*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-C", "--class", dest="clazz", 39*387f9dfdSAndroid Build Coastguard Worker help="trace only calls to classes starting with this prefix") 40*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-v", "--verbose", action="store_true", 41*387f9dfdSAndroid Build Coastguard Worker help="verbose mode: print the BPF program (for debugging purposes)") 42*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true", 43*387f9dfdSAndroid Build Coastguard Worker help=argparse.SUPPRESS) 44*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args() 45*387f9dfdSAndroid Build Coastguard Worker 46*387f9dfdSAndroid Build Coastguard Workerusdt = USDT(pid=args.pid) 47*387f9dfdSAndroid Build Coastguard Worker 48*387f9dfdSAndroid Build Coastguard Workerprogram = """ 49*387f9dfdSAndroid Build Coastguard Workerstruct call_t { 50*387f9dfdSAndroid Build Coastguard Worker u64 depth; // first bit is direction (0 entry, 1 return) 51*387f9dfdSAndroid Build Coastguard Worker u64 pid; // (tgid << 32) + pid from bpf_get_current... 52*387f9dfdSAndroid Build Coastguard Worker char clazz[80]; 53*387f9dfdSAndroid Build Coastguard Worker char method[80]; 54*387f9dfdSAndroid Build Coastguard Worker}; 55*387f9dfdSAndroid Build Coastguard Worker 56*387f9dfdSAndroid Build Coastguard WorkerBPF_PERF_OUTPUT(calls); 57*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(entry, u64, u64); 58*387f9dfdSAndroid Build Coastguard Worker""" 59*387f9dfdSAndroid Build Coastguard Worker 60*387f9dfdSAndroid Build Coastguard Workerprefix_template = """ 61*387f9dfdSAndroid Build Coastguard Workerstatic inline bool prefix_%s(char *actual) { 62*387f9dfdSAndroid Build Coastguard Worker char expected[] = "%s"; 63*387f9dfdSAndroid Build Coastguard Worker for (int i = 0; i < sizeof(expected) - 1; ++i) { 64*387f9dfdSAndroid Build Coastguard Worker if (expected[i] != actual[i]) { 65*387f9dfdSAndroid Build Coastguard Worker return false; 66*387f9dfdSAndroid Build Coastguard Worker } 67*387f9dfdSAndroid Build Coastguard Worker } 68*387f9dfdSAndroid Build Coastguard Worker return true; 69*387f9dfdSAndroid Build Coastguard Worker} 70*387f9dfdSAndroid Build Coastguard Worker""" 71*387f9dfdSAndroid Build Coastguard Worker 72*387f9dfdSAndroid Build Coastguard Workerif args.clazz: 73*387f9dfdSAndroid Build Coastguard Worker program += prefix_template % ("class", args.clazz) 74*387f9dfdSAndroid Build Coastguard Workerif args.method: 75*387f9dfdSAndroid Build Coastguard Worker program += prefix_template % ("method", args.method) 76*387f9dfdSAndroid Build Coastguard Worker 77*387f9dfdSAndroid Build Coastguard Workertrace_template = """ 78*387f9dfdSAndroid Build Coastguard Workerint NAME(struct pt_regs *ctx) { 79*387f9dfdSAndroid Build Coastguard Worker u64 *depth, zero = 0, clazz = 0, method = 0 ; 80*387f9dfdSAndroid Build Coastguard Worker struct call_t data = {}; 81*387f9dfdSAndroid Build Coastguard Worker 82*387f9dfdSAndroid Build Coastguard Worker READ_CLASS 83*387f9dfdSAndroid Build Coastguard Worker READ_METHOD 84*387f9dfdSAndroid Build Coastguard Worker bpf_probe_read_user(&data.clazz, sizeof(data.clazz), (void *)clazz); 85*387f9dfdSAndroid Build Coastguard Worker bpf_probe_read_user(&data.method, sizeof(data.method), (void *)method); 86*387f9dfdSAndroid Build Coastguard Worker 87*387f9dfdSAndroid Build Coastguard Worker FILTER_CLASS 88*387f9dfdSAndroid Build Coastguard Worker FILTER_METHOD 89*387f9dfdSAndroid Build Coastguard Worker 90*387f9dfdSAndroid Build Coastguard Worker data.pid = bpf_get_current_pid_tgid(); 91*387f9dfdSAndroid Build Coastguard Worker depth = entry.lookup_or_try_init(&data.pid, &zero); 92*387f9dfdSAndroid Build Coastguard Worker if (!depth) { 93*387f9dfdSAndroid Build Coastguard Worker depth = &zero; 94*387f9dfdSAndroid Build Coastguard Worker } 95*387f9dfdSAndroid Build Coastguard Worker data.depth = DEPTH; 96*387f9dfdSAndroid Build Coastguard Worker UPDATE 97*387f9dfdSAndroid Build Coastguard Worker 98*387f9dfdSAndroid Build Coastguard Worker calls.perf_submit(ctx, &data, sizeof(data)); 99*387f9dfdSAndroid Build Coastguard Worker return 0; 100*387f9dfdSAndroid Build Coastguard Worker} 101*387f9dfdSAndroid Build Coastguard Worker""" 102*387f9dfdSAndroid Build Coastguard Worker 103*387f9dfdSAndroid Build Coastguard Workerdef enable_probe(probe_name, func_name, read_class, read_method, is_return): 104*387f9dfdSAndroid Build Coastguard Worker global program, trace_template, usdt 105*387f9dfdSAndroid Build Coastguard Worker depth = "*depth + 1" if not is_return else "*depth | (1ULL << 63)" 106*387f9dfdSAndroid Build Coastguard Worker update = "++(*depth);" if not is_return else "if (*depth) --(*depth);" 107*387f9dfdSAndroid Build Coastguard Worker filter_class = "if (!prefix_class(data.clazz)) { return 0; }" \ 108*387f9dfdSAndroid Build Coastguard Worker if args.clazz else "" 109*387f9dfdSAndroid Build Coastguard Worker filter_method = "if (!prefix_method(data.method)) { return 0; }" \ 110*387f9dfdSAndroid Build Coastguard Worker if args.method else "" 111*387f9dfdSAndroid Build Coastguard Worker program += trace_template.replace("NAME", func_name) \ 112*387f9dfdSAndroid Build Coastguard Worker .replace("READ_CLASS", read_class) \ 113*387f9dfdSAndroid Build Coastguard Worker .replace("READ_METHOD", read_method) \ 114*387f9dfdSAndroid Build Coastguard Worker .replace("FILTER_CLASS", filter_class) \ 115*387f9dfdSAndroid Build Coastguard Worker .replace("FILTER_METHOD", filter_method) \ 116*387f9dfdSAndroid Build Coastguard Worker .replace("DEPTH", depth) \ 117*387f9dfdSAndroid Build Coastguard Worker .replace("UPDATE", update) 118*387f9dfdSAndroid Build Coastguard Worker usdt.enable_probe_or_bail(probe_name, func_name) 119*387f9dfdSAndroid Build Coastguard Worker 120*387f9dfdSAndroid Build Coastguard Workerusdt = USDT(pid=args.pid) 121*387f9dfdSAndroid Build Coastguard Worker 122*387f9dfdSAndroid Build Coastguard Workerlanguage = args.language 123*387f9dfdSAndroid Build Coastguard Workerif not language: 124*387f9dfdSAndroid Build Coastguard Worker language = utils.detect_language(languages, args.pid) 125*387f9dfdSAndroid Build Coastguard Worker 126*387f9dfdSAndroid Build Coastguard Workerif language == "java": 127*387f9dfdSAndroid Build Coastguard Worker enable_probe("method__entry", "java_entry", 128*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &clazz);", 129*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(4, ctx, &method);", is_return=False) 130*387f9dfdSAndroid Build Coastguard Worker enable_probe("method__return", "java_return", 131*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &clazz);", 132*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(4, ctx, &method);", is_return=True) 133*387f9dfdSAndroid Build Coastguard Workerelif language == "perl": 134*387f9dfdSAndroid Build Coastguard Worker enable_probe("sub__entry", "perl_entry", 135*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &clazz);", 136*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &method);", is_return=False) 137*387f9dfdSAndroid Build Coastguard Worker enable_probe("sub__return", "perl_return", 138*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &clazz);", 139*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &method);", is_return=True) 140*387f9dfdSAndroid Build Coastguard Workerelif language == "php": 141*387f9dfdSAndroid Build Coastguard Worker enable_probe("function__entry", "php_entry", 142*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(4, ctx, &clazz);", 143*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &method);", is_return=False) 144*387f9dfdSAndroid Build Coastguard Worker enable_probe("function__return", "php_return", 145*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(4, ctx, &clazz);", 146*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &method);", is_return=True) 147*387f9dfdSAndroid Build Coastguard Workerelif language == "python": 148*387f9dfdSAndroid Build Coastguard Worker enable_probe("function__entry", "python_entry", 149*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &clazz);", # filename really 150*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &method);", is_return=False) 151*387f9dfdSAndroid Build Coastguard Worker enable_probe("function__return", "python_return", 152*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &clazz);", # filename really 153*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &method);", is_return=True) 154*387f9dfdSAndroid Build Coastguard Workerelif language == "ruby": 155*387f9dfdSAndroid Build Coastguard Worker enable_probe("method__entry", "ruby_entry", 156*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &clazz);", 157*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &method);", is_return=False) 158*387f9dfdSAndroid Build Coastguard Worker enable_probe("method__return", "ruby_return", 159*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &clazz);", 160*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &method);", is_return=True) 161*387f9dfdSAndroid Build Coastguard Worker enable_probe("cmethod__entry", "ruby_centry", 162*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &clazz);", 163*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &method);", is_return=False) 164*387f9dfdSAndroid Build Coastguard Worker enable_probe("cmethod__return", "ruby_creturn", 165*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &clazz);", 166*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(2, ctx, &method);", is_return=True) 167*387f9dfdSAndroid Build Coastguard Workerelif language == "tcl": 168*387f9dfdSAndroid Build Coastguard Worker enable_probe("proc__args", "tcl_entry", 169*387f9dfdSAndroid Build Coastguard Worker "", # no class/file info available 170*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &method);", is_return=False) 171*387f9dfdSAndroid Build Coastguard Worker enable_probe("proc__return", "tcl_return", 172*387f9dfdSAndroid Build Coastguard Worker "", # no class/file info available 173*387f9dfdSAndroid Build Coastguard Worker "bpf_usdt_readarg(1, ctx, &method);", is_return=True) 174*387f9dfdSAndroid Build Coastguard Workerelse: 175*387f9dfdSAndroid Build Coastguard Worker print("No language detected; use -l to trace a language.") 176*387f9dfdSAndroid Build Coastguard Worker exit(1) 177*387f9dfdSAndroid Build Coastguard Worker 178*387f9dfdSAndroid Build Coastguard Workerif args.ebpf or args.verbose: 179*387f9dfdSAndroid Build Coastguard Worker if args.verbose: 180*387f9dfdSAndroid Build Coastguard Worker print(usdt.get_text()) 181*387f9dfdSAndroid Build Coastguard Worker print(program) 182*387f9dfdSAndroid Build Coastguard Worker if args.ebpf: 183*387f9dfdSAndroid Build Coastguard Worker exit() 184*387f9dfdSAndroid Build Coastguard Worker 185*387f9dfdSAndroid Build Coastguard Workerbpf = BPF(text=program, usdt_contexts=[usdt]) 186*387f9dfdSAndroid Build Coastguard Workerprint("Tracing method calls in %s process %d... Ctrl-C to quit." % 187*387f9dfdSAndroid Build Coastguard Worker (language, args.pid)) 188*387f9dfdSAndroid Build Coastguard Workerprint("%-3s %-6s %-6s %-8s %s" % ("CPU", "PID", "TID", "TIME(us)", "METHOD")) 189*387f9dfdSAndroid Build Coastguard Worker 190*387f9dfdSAndroid Build Coastguard Workerclass CallEvent(ct.Structure): 191*387f9dfdSAndroid Build Coastguard Worker _fields_ = [ 192*387f9dfdSAndroid Build Coastguard Worker ("depth", ct.c_ulonglong), 193*387f9dfdSAndroid Build Coastguard Worker ("pid", ct.c_ulonglong), 194*387f9dfdSAndroid Build Coastguard Worker ("clazz", ct.c_char * 80), 195*387f9dfdSAndroid Build Coastguard Worker ("method", ct.c_char * 80) 196*387f9dfdSAndroid Build Coastguard Worker ] 197*387f9dfdSAndroid Build Coastguard Worker 198*387f9dfdSAndroid Build Coastguard Workerstart_ts = time.time() 199*387f9dfdSAndroid Build Coastguard Worker 200*387f9dfdSAndroid Build Coastguard Workerdef print_event(cpu, data, size): 201*387f9dfdSAndroid Build Coastguard Worker event = ct.cast(data, ct.POINTER(CallEvent)).contents 202*387f9dfdSAndroid Build Coastguard Worker depth = event.depth & (~(1 << 63)) 203*387f9dfdSAndroid Build Coastguard Worker direction = "<- " if event.depth & (1 << 63) else "-> " 204*387f9dfdSAndroid Build Coastguard Worker print("%-3d %-6d %-6d %-8.3f %-40s" % (cpu, event.pid >> 32, 205*387f9dfdSAndroid Build Coastguard Worker event.pid & 0xFFFFFFFF, time.time() - start_ts, 206*387f9dfdSAndroid Build Coastguard Worker (" " * (depth - 1)) + direction + \ 207*387f9dfdSAndroid Build Coastguard Worker event.clazz.decode('utf-8', 'replace') + "." + \ 208*387f9dfdSAndroid Build Coastguard Worker event.method.decode('utf-8', 'replace'))) 209*387f9dfdSAndroid Build Coastguard Worker 210*387f9dfdSAndroid Build Coastguard Workerbpf["calls"].open_perf_buffer(print_event) 211*387f9dfdSAndroid Build Coastguard Workerwhile 1: 212*387f9dfdSAndroid Build Coastguard Worker try: 213*387f9dfdSAndroid Build Coastguard Worker bpf.perf_buffer_poll() 214*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 215*387f9dfdSAndroid Build Coastguard Worker exit() 216