xref: /aosp_15_r20/external/bcc/tools/lib/uflow.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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