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