xref: /aosp_15_r20/external/bcc/tools/lib/ugc.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# ugc  Summarize garbage collection events 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: ugc [-v] [-m] [-M MSEC] [-F FILTER] {java,node,python,ruby} 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# 19-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", "node", "python", "ruby"]
22*387f9dfdSAndroid Build Coastguard Worker
23*387f9dfdSAndroid Build Coastguard Workerexamples = """examples:
24*387f9dfdSAndroid Build Coastguard Worker    ./ugc -l java 185        # trace Java GCs in process 185
25*387f9dfdSAndroid Build Coastguard Worker    ./ugc -l ruby 1344 -m    # trace Ruby GCs reporting in ms
26*387f9dfdSAndroid Build Coastguard Worker    ./ugc -M 10 -l java 185  # trace only Java GCs longer than 10ms
27*387f9dfdSAndroid Build Coastguard Worker"""
28*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
29*387f9dfdSAndroid Build Coastguard Worker    description="Summarize garbage collection events in high-level languages.",
30*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
31*387f9dfdSAndroid Build Coastguard Worker    epilog=examples)
32*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-l", "--language", choices=languages,
33*387f9dfdSAndroid Build Coastguard Worker    help="language to trace")
34*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("pid", type=int, help="process id to attach to")
35*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-v", "--verbose", action="store_true",
36*387f9dfdSAndroid Build Coastguard Worker    help="verbose mode: print the BPF program (for debugging purposes)")
37*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-m", "--milliseconds", action="store_true",
38*387f9dfdSAndroid Build Coastguard Worker    help="report times in milliseconds (default is microseconds)")
39*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-M", "--minimum", type=int, default=0,
40*387f9dfdSAndroid Build Coastguard Worker    help="display only GCs longer than this many milliseconds")
41*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-F", "--filter", type=str,
42*387f9dfdSAndroid Build Coastguard Worker    help="display only GCs whose description contains this text")
43*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true",
44*387f9dfdSAndroid Build Coastguard Worker    help=argparse.SUPPRESS)
45*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args()
46*387f9dfdSAndroid Build Coastguard Worker
47*387f9dfdSAndroid Build Coastguard Workerusdt = USDT(pid=args.pid)
48*387f9dfdSAndroid Build Coastguard Worker
49*387f9dfdSAndroid Build Coastguard Workerprogram = """
50*387f9dfdSAndroid Build Coastguard Workerstruct gc_event_t {
51*387f9dfdSAndroid Build Coastguard Worker    u64 probe_index;
52*387f9dfdSAndroid Build Coastguard Worker    u64 elapsed_ns;
53*387f9dfdSAndroid Build Coastguard Worker    u64 field1;
54*387f9dfdSAndroid Build Coastguard Worker    u64 field2;
55*387f9dfdSAndroid Build Coastguard Worker    u64 field3;
56*387f9dfdSAndroid Build Coastguard Worker    u64 field4;
57*387f9dfdSAndroid Build Coastguard Worker    char string1[32];
58*387f9dfdSAndroid Build Coastguard Worker    char string2[32];
59*387f9dfdSAndroid Build Coastguard Worker};
60*387f9dfdSAndroid Build Coastguard Workerstruct entry_t {
61*387f9dfdSAndroid Build Coastguard Worker    u64 start_ns;
62*387f9dfdSAndroid Build Coastguard Worker    u64 field1;
63*387f9dfdSAndroid Build Coastguard Worker    u64 field2;
64*387f9dfdSAndroid Build Coastguard Worker};
65*387f9dfdSAndroid Build Coastguard Worker
66*387f9dfdSAndroid Build Coastguard WorkerBPF_PERF_OUTPUT(gcs);
67*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(entry, u64, struct entry_t);
68*387f9dfdSAndroid Build Coastguard Worker"""
69*387f9dfdSAndroid Build Coastguard Worker
70*387f9dfdSAndroid Build Coastguard Workerclass Probe(object):
71*387f9dfdSAndroid Build Coastguard Worker    def __init__(self, begin, end, begin_save, end_save, formatter):
72*387f9dfdSAndroid Build Coastguard Worker        self.begin = begin
73*387f9dfdSAndroid Build Coastguard Worker        self.end = end
74*387f9dfdSAndroid Build Coastguard Worker        self.begin_save = begin_save
75*387f9dfdSAndroid Build Coastguard Worker        self.end_save = end_save
76*387f9dfdSAndroid Build Coastguard Worker        self.formatter = formatter
77*387f9dfdSAndroid Build Coastguard Worker
78*387f9dfdSAndroid Build Coastguard Worker    def generate(self):
79*387f9dfdSAndroid Build Coastguard Worker        text = """
80*387f9dfdSAndroid Build Coastguard Workerint trace_%s(struct pt_regs *ctx) {
81*387f9dfdSAndroid Build Coastguard Worker    u64 pid = bpf_get_current_pid_tgid();
82*387f9dfdSAndroid Build Coastguard Worker    struct entry_t e = {};
83*387f9dfdSAndroid Build Coastguard Worker    e.start_ns = bpf_ktime_get_ns();
84*387f9dfdSAndroid Build Coastguard Worker    %s
85*387f9dfdSAndroid Build Coastguard Worker    entry.update(&pid, &e);
86*387f9dfdSAndroid Build Coastguard Worker    return 0;
87*387f9dfdSAndroid Build Coastguard Worker}
88*387f9dfdSAndroid Build Coastguard Workerint trace_%s(struct pt_regs *ctx) {
89*387f9dfdSAndroid Build Coastguard Worker    u64 elapsed;
90*387f9dfdSAndroid Build Coastguard Worker    struct entry_t *e;
91*387f9dfdSAndroid Build Coastguard Worker    struct gc_event_t event = {};
92*387f9dfdSAndroid Build Coastguard Worker    u64 pid = bpf_get_current_pid_tgid();
93*387f9dfdSAndroid Build Coastguard Worker    e = entry.lookup(&pid);
94*387f9dfdSAndroid Build Coastguard Worker    if (!e) {
95*387f9dfdSAndroid Build Coastguard Worker        return 0;   // missed the entry event on this thread
96*387f9dfdSAndroid Build Coastguard Worker    }
97*387f9dfdSAndroid Build Coastguard Worker    elapsed = bpf_ktime_get_ns() - e->start_ns;
98*387f9dfdSAndroid Build Coastguard Worker    if (elapsed < %d) {
99*387f9dfdSAndroid Build Coastguard Worker        return 0;
100*387f9dfdSAndroid Build Coastguard Worker    }
101*387f9dfdSAndroid Build Coastguard Worker    event.elapsed_ns = elapsed;
102*387f9dfdSAndroid Build Coastguard Worker    %s
103*387f9dfdSAndroid Build Coastguard Worker    gcs.perf_submit(ctx, &event, sizeof(event));
104*387f9dfdSAndroid Build Coastguard Worker    return 0;
105*387f9dfdSAndroid Build Coastguard Worker}
106*387f9dfdSAndroid Build Coastguard Worker        """ % (self.begin, self.begin_save, self.end,
107*387f9dfdSAndroid Build Coastguard Worker               args.minimum * 1000000, self.end_save)
108*387f9dfdSAndroid Build Coastguard Worker        return text
109*387f9dfdSAndroid Build Coastguard Worker
110*387f9dfdSAndroid Build Coastguard Worker    def attach(self):
111*387f9dfdSAndroid Build Coastguard Worker        usdt.enable_probe_or_bail(self.begin, "trace_%s" % self.begin)
112*387f9dfdSAndroid Build Coastguard Worker        usdt.enable_probe_or_bail(self.end, "trace_%s" % self.end)
113*387f9dfdSAndroid Build Coastguard Worker
114*387f9dfdSAndroid Build Coastguard Worker    def format(self, data):
115*387f9dfdSAndroid Build Coastguard Worker        return self.formatter(data)
116*387f9dfdSAndroid Build Coastguard Worker
117*387f9dfdSAndroid Build Coastguard Workerprobes = []
118*387f9dfdSAndroid Build Coastguard Worker
119*387f9dfdSAndroid Build Coastguard Workerlanguage = args.language
120*387f9dfdSAndroid Build Coastguard Workerif not language:
121*387f9dfdSAndroid Build Coastguard Worker    language = utils.detect_language(languages, args.pid)
122*387f9dfdSAndroid Build Coastguard Worker
123*387f9dfdSAndroid Build Coastguard Worker#
124*387f9dfdSAndroid Build Coastguard Worker# Java
125*387f9dfdSAndroid Build Coastguard Worker#
126*387f9dfdSAndroid Build Coastguard Workerif language == "java":
127*387f9dfdSAndroid Build Coastguard Worker    # Oddly, the gc__begin/gc__end probes don't really have any useful
128*387f9dfdSAndroid Build Coastguard Worker    # information, while the mem__pool* ones do. There's also a bunch of
129*387f9dfdSAndroid Build Coastguard Worker    # probes described in the hotspot_gc*.stp file which aren't there
130*387f9dfdSAndroid Build Coastguard Worker    # when looking at a live Java process.
131*387f9dfdSAndroid Build Coastguard Worker    begin_save = """
132*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(6, ctx, &e.field1);    // used bytes
133*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(8, ctx, &e.field2);    // max bytes
134*387f9dfdSAndroid Build Coastguard Worker    """
135*387f9dfdSAndroid Build Coastguard Worker    end_save = """
136*387f9dfdSAndroid Build Coastguard Worker    event.field1 = e->field1;                  // used bytes at start
137*387f9dfdSAndroid Build Coastguard Worker    event.field2 = e->field2;                  // max bytes at start
138*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(6, ctx, &event.field3);   // used bytes at end
139*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(8, ctx, &event.field4);   // max bytes at end
140*387f9dfdSAndroid Build Coastguard Worker    u64 manager = 0, pool = 0;
141*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(1, ctx, &manager);        // ptr to manager name
142*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(3, ctx, &pool);           // ptr to pool name
143*387f9dfdSAndroid Build Coastguard Worker    bpf_probe_read_user(&event.string1, sizeof(event.string1), (void *)manager);
144*387f9dfdSAndroid Build Coastguard Worker    bpf_probe_read_user(&event.string2, sizeof(event.string2), (void *)pool);
145*387f9dfdSAndroid Build Coastguard Worker    """
146*387f9dfdSAndroid Build Coastguard Worker
147*387f9dfdSAndroid Build Coastguard Worker    def formatter(e):
148*387f9dfdSAndroid Build Coastguard Worker        "%s %s used=%d->%d max=%d->%d" % \
149*387f9dfdSAndroid Build Coastguard Worker            (e.string1, e.string2, e.field1, e.field3, e.field2, e.field4)
150*387f9dfdSAndroid Build Coastguard Worker    probes.append(Probe("mem__pool__gc__begin", "mem__pool__gc__end",
151*387f9dfdSAndroid Build Coastguard Worker                        begin_save, end_save, formatter))
152*387f9dfdSAndroid Build Coastguard Worker    probes.append(Probe("gc__begin", "gc__end",
153*387f9dfdSAndroid Build Coastguard Worker                        "", "", lambda _: "no additional info available"))
154*387f9dfdSAndroid Build Coastguard Worker#
155*387f9dfdSAndroid Build Coastguard Worker# Node
156*387f9dfdSAndroid Build Coastguard Worker#
157*387f9dfdSAndroid Build Coastguard Workerelif language == "node":
158*387f9dfdSAndroid Build Coastguard Worker    end_save = """
159*387f9dfdSAndroid Build Coastguard Worker    u32 gc_type = 0;
160*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(1, ctx, &gc_type);
161*387f9dfdSAndroid Build Coastguard Worker    event.field1 = gc_type;
162*387f9dfdSAndroid Build Coastguard Worker    """
163*387f9dfdSAndroid Build Coastguard Worker    descs = {"GC scavenge": 1, "GC mark-sweep-compact": 2,
164*387f9dfdSAndroid Build Coastguard Worker             "GC incremental mark": 4, "GC weak callbacks": 8}
165*387f9dfdSAndroid Build Coastguard Worker    probes.append(Probe("gc__start", "gc__done", "", end_save,
166*387f9dfdSAndroid Build Coastguard Worker                  lambda e: str.join(", ",
167*387f9dfdSAndroid Build Coastguard Worker                                     [desc for desc, val in descs.items()
168*387f9dfdSAndroid Build Coastguard Worker                                      if e.field1 & val != 0])))
169*387f9dfdSAndroid Build Coastguard Worker#
170*387f9dfdSAndroid Build Coastguard Worker# Python
171*387f9dfdSAndroid Build Coastguard Worker#
172*387f9dfdSAndroid Build Coastguard Workerelif language == "python":
173*387f9dfdSAndroid Build Coastguard Worker    begin_save = """
174*387f9dfdSAndroid Build Coastguard Worker    int gen = 0;
175*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(1, ctx, &gen);
176*387f9dfdSAndroid Build Coastguard Worker    e.field1 = gen;
177*387f9dfdSAndroid Build Coastguard Worker    """
178*387f9dfdSAndroid Build Coastguard Worker    end_save = """
179*387f9dfdSAndroid Build Coastguard Worker    long objs = 0;
180*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(1, ctx, &objs);
181*387f9dfdSAndroid Build Coastguard Worker    event.field1 = e->field1;
182*387f9dfdSAndroid Build Coastguard Worker    event.field2 = objs;
183*387f9dfdSAndroid Build Coastguard Worker    """
184*387f9dfdSAndroid Build Coastguard Worker
185*387f9dfdSAndroid Build Coastguard Worker    def formatter(event):
186*387f9dfdSAndroid Build Coastguard Worker        "gen %d GC collected %d objects" % \
187*387f9dfdSAndroid Build Coastguard Worker            (event.field1, event.field2)
188*387f9dfdSAndroid Build Coastguard Worker    probes.append(Probe("gc__start", "gc__done",
189*387f9dfdSAndroid Build Coastguard Worker                        begin_save, end_save, formatter))
190*387f9dfdSAndroid Build Coastguard Worker#
191*387f9dfdSAndroid Build Coastguard Worker# Ruby
192*387f9dfdSAndroid Build Coastguard Worker#
193*387f9dfdSAndroid Build Coastguard Workerelif language == "ruby":
194*387f9dfdSAndroid Build Coastguard Worker    # Ruby GC probes do not have any additional information available.
195*387f9dfdSAndroid Build Coastguard Worker    probes.append(Probe("gc__mark__begin", "gc__mark__end",
196*387f9dfdSAndroid Build Coastguard Worker                        "", "", lambda _: "GC mark stage"))
197*387f9dfdSAndroid Build Coastguard Worker    probes.append(Probe("gc__sweep__begin", "gc__sweep__end",
198*387f9dfdSAndroid Build Coastguard Worker                        "", "", lambda _: "GC sweep stage"))
199*387f9dfdSAndroid Build Coastguard Worker
200*387f9dfdSAndroid Build Coastguard Workerelse:
201*387f9dfdSAndroid Build Coastguard Worker    print("No language detected; use -l to trace a language.")
202*387f9dfdSAndroid Build Coastguard Worker    exit(1)
203*387f9dfdSAndroid Build Coastguard Worker
204*387f9dfdSAndroid Build Coastguard Worker
205*387f9dfdSAndroid Build Coastguard Workerfor probe in probes:
206*387f9dfdSAndroid Build Coastguard Worker    program += probe.generate()
207*387f9dfdSAndroid Build Coastguard Worker    probe.attach()
208*387f9dfdSAndroid Build Coastguard Worker
209*387f9dfdSAndroid Build Coastguard Workerif args.ebpf or args.verbose:
210*387f9dfdSAndroid Build Coastguard Worker    if args.verbose:
211*387f9dfdSAndroid Build Coastguard Worker        print(usdt.get_text())
212*387f9dfdSAndroid Build Coastguard Worker    print(program)
213*387f9dfdSAndroid Build Coastguard Worker    if args.ebpf:
214*387f9dfdSAndroid Build Coastguard Worker        exit()
215*387f9dfdSAndroid Build Coastguard Worker
216*387f9dfdSAndroid Build Coastguard Workerbpf = BPF(text=program, usdt_contexts=[usdt])
217*387f9dfdSAndroid Build Coastguard Workerprint("Tracing garbage collections in %s process %d... Ctrl-C to quit." %
218*387f9dfdSAndroid Build Coastguard Worker      (language, args.pid))
219*387f9dfdSAndroid Build Coastguard Workertime_col = "TIME (ms)" if args.milliseconds else "TIME (us)"
220*387f9dfdSAndroid Build Coastguard Workerprint("%-8s %-8s %-40s" % ("START", time_col, "DESCRIPTION"))
221*387f9dfdSAndroid Build Coastguard Worker
222*387f9dfdSAndroid Build Coastguard Workerclass GCEvent(ct.Structure):
223*387f9dfdSAndroid Build Coastguard Worker    _fields_ = [
224*387f9dfdSAndroid Build Coastguard Worker        ("probe_index", ct.c_ulonglong),
225*387f9dfdSAndroid Build Coastguard Worker        ("elapsed_ns", ct.c_ulonglong),
226*387f9dfdSAndroid Build Coastguard Worker        ("field1", ct.c_ulonglong),
227*387f9dfdSAndroid Build Coastguard Worker        ("field2", ct.c_ulonglong),
228*387f9dfdSAndroid Build Coastguard Worker        ("field3", ct.c_ulonglong),
229*387f9dfdSAndroid Build Coastguard Worker        ("field4", ct.c_ulonglong),
230*387f9dfdSAndroid Build Coastguard Worker        ("string1", ct.c_char * 32),
231*387f9dfdSAndroid Build Coastguard Worker        ("string2", ct.c_char * 32)
232*387f9dfdSAndroid Build Coastguard Worker        ]
233*387f9dfdSAndroid Build Coastguard Worker
234*387f9dfdSAndroid Build Coastguard Workerstart_ts = time.time()
235*387f9dfdSAndroid Build Coastguard Worker
236*387f9dfdSAndroid Build Coastguard Workerdef print_event(cpu, data, size):
237*387f9dfdSAndroid Build Coastguard Worker    event = ct.cast(data, ct.POINTER(GCEvent)).contents
238*387f9dfdSAndroid Build Coastguard Worker    elapsed = event.elapsed_ns / 1000000 if args.milliseconds else \
239*387f9dfdSAndroid Build Coastguard Worker              event.elapsed_ns / 1000
240*387f9dfdSAndroid Build Coastguard Worker    description = probes[event.probe_index].format(event)
241*387f9dfdSAndroid Build Coastguard Worker    if args.filter and args.filter not in description:
242*387f9dfdSAndroid Build Coastguard Worker        return
243*387f9dfdSAndroid Build Coastguard Worker    print("%-8.3f %-8.2f %s" % (time.time() - start_ts, elapsed, description))
244*387f9dfdSAndroid Build Coastguard Worker
245*387f9dfdSAndroid Build Coastguard Workerbpf["gcs"].open_perf_buffer(print_event)
246*387f9dfdSAndroid Build Coastguard Workerwhile 1:
247*387f9dfdSAndroid Build Coastguard Worker    try:
248*387f9dfdSAndroid Build Coastguard Worker        bpf.perf_buffer_poll()
249*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
250*387f9dfdSAndroid Build Coastguard Worker        exit()
251