blob: 1bd923862ff3b54e5199915f283ec85dcf556344 [file] [log] [blame]
Sasha Goldshteine5d9d992016-10-17 09:04:37 -07001#!/usr/bin/python
2# @lint-avoid-python-3-compatibility-imports
3#
4# ugc Summarize garbage collection events in high-level languages.
5# For Linux, uses BCC, eBPF.
6#
Sasha Goldshteinc3252002017-02-08 13:22:40 -05007# USAGE: ugc [-v] [-m] [-M MSEC] [-F FILTER] {java,python,ruby,node} pid
Sasha Goldshteine5d9d992016-10-17 09:04:37 -07008#
9# Copyright 2016 Sasha Goldshtein
10# Licensed under the Apache License, Version 2.0 (the "License")
11#
12# 19-Oct-2016 Sasha Goldshtein Created this.
13
14from __future__ import print_function
15import argparse
16from bcc import BPF, USDT
17import ctypes as ct
18import time
19
20examples = """examples:
21 ./ugc java 185 # trace Java GCs in process 185
22 ./ugc ruby 1344 -m # trace Ruby GCs reporting in ms
Sasha Goldshteinc3252002017-02-08 13:22:40 -050023 ./ugc -M 10 java 185 # trace only Java GCs longer than 10ms
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070024"""
25parser = argparse.ArgumentParser(
26 description="Summarize garbage collection events in high-level languages.",
27 formatter_class=argparse.RawDescriptionHelpFormatter,
28 epilog=examples)
Sasha Goldshtein88533472016-10-25 00:08:33 -070029parser.add_argument("language", choices=["java", "python", "ruby", "node"],
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070030 help="language to trace")
31parser.add_argument("pid", type=int, help="process id to attach to")
32parser.add_argument("-v", "--verbose", action="store_true",
33 help="verbose mode: print the BPF program (for debugging purposes)")
34parser.add_argument("-m", "--milliseconds", action="store_true",
35 help="report times in milliseconds (default is microseconds)")
Sasha Goldshteinc3252002017-02-08 13:22:40 -050036parser.add_argument("-M", "--minimum", type=int, default=0,
37 help="display only GCs longer than this many milliseconds")
38parser.add_argument("-F", "--filter", type=str,
39 help="display only GCs whose description contains this text")
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070040args = parser.parse_args()
41
42usdt = USDT(pid=args.pid)
43
44program = """
45struct gc_event_t {
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070046 u64 probe_index;
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070047 u64 elapsed_ns;
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070048 u64 field1;
49 u64 field2;
50 u64 field3;
51 u64 field4;
52 char string1[32];
53 char string2[32];
54};
55struct entry_t {
56 u64 start_ns;
57 u64 field1;
58 u64 field2;
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070059};
60
61BPF_PERF_OUTPUT(gcs);
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070062BPF_HASH(entry, u64, struct entry_t);
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070063"""
64
65class Probe(object):
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070066 def __init__(self, begin, end, begin_save, end_save, formatter):
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070067 self.begin = begin
68 self.end = end
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070069 self.begin_save = begin_save
70 self.end_save = end_save
71 self.formatter = formatter
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070072
73 def generate(self):
74 text = """
75int trace_%s(struct pt_regs *ctx) {
76 u64 pid = bpf_get_current_pid_tgid();
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070077 struct entry_t e = {};
78 e.start_ns = bpf_ktime_get_ns();
79 %s
80 entry.update(&pid, &e);
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070081 return 0;
82}
83int trace_%s(struct pt_regs *ctx) {
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070084 u64 elapsed;
85 struct entry_t *e;
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070086 struct gc_event_t event = {};
87 u64 pid = bpf_get_current_pid_tgid();
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070088 e = entry.lookup(&pid);
89 if (!e) {
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070090 return 0; // missed the entry event on this thread
91 }
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070092 elapsed = bpf_ktime_get_ns() - e->start_ns;
Sasha Goldshteinc3252002017-02-08 13:22:40 -050093 if (elapsed < %d) {
94 return 0;
95 }
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070096 event.elapsed_ns = elapsed;
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070097 %s
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070098 gcs.perf_submit(ctx, &event, sizeof(event));
99 return 0;
100}
Sasha Goldshteinc3252002017-02-08 13:22:40 -0500101 """ % (self.begin, self.begin_save, self.end,
102 args.minimum * 1000000, self.end_save)
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700103 return text
104
105 def attach(self):
Sasha Goldshteindc3a57c2017-02-08 16:02:11 -0500106 usdt.enable_probe_or_bail(self.begin, "trace_%s" % self.begin)
107 usdt.enable_probe_or_bail(self.end, "trace_%s" % self.end)
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700108
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700109 def format(self, data):
110 return self.formatter(data)
111
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700112probes = []
113
Sasha Goldshteinbee71b22016-10-26 06:34:06 -0700114#
115# Java
116#
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700117if args.language == "java":
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700118 # Oddly, the gc__begin/gc__end probes don't really have any useful
119 # information, while the mem__pool* ones do. There's also a bunch of
120 # probes described in the hotspot_gc*.stp file which aren't there
121 # when looking at a live Java process.
122 begin_save = """
123 bpf_usdt_readarg(6, ctx, &e.field1); // used bytes
124 bpf_usdt_readarg(8, ctx, &e.field2); // max bytes
125 """
126 end_save = """
127 event.field1 = e->field1; // used bytes at start
128 event.field2 = e->field2; // max bytes at start
129 bpf_usdt_readarg(6, ctx, &event.field3); // used bytes at end
130 bpf_usdt_readarg(8, ctx, &event.field4); // max bytes at end
131 u64 manager = 0, pool = 0;
132 bpf_usdt_readarg(1, ctx, &manager); // ptr to manager name
133 bpf_usdt_readarg(3, ctx, &pool); // ptr to pool name
134 bpf_probe_read(&event.string1, sizeof(event.string1), (void *)manager);
135 bpf_probe_read(&event.string2, sizeof(event.string2), (void *)pool);
136 """
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100137
138 def formatter(e):
139 "%s %s used=%d->%d max=%d->%d" % \
140 (e.string1, e.string2, e.field1, e.field3, e.field2, e.field4)
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700141 probes.append(Probe("mem__pool__gc__begin", "mem__pool__gc__end",
142 begin_save, end_save, formatter))
143 probes.append(Probe("gc__begin", "gc__end",
144 "", "", lambda _: "no additional info available"))
Sasha Goldshteinbee71b22016-10-26 06:34:06 -0700145#
146# Python
147#
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700148elif args.language == "python":
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700149 begin_save = """
150 int gen = 0;
151 bpf_usdt_readarg(1, ctx, &gen);
152 e.field1 = gen;
153 """
154 end_save = """
155 long objs = 0;
156 bpf_usdt_readarg(1, ctx, &objs);
157 event.field1 = e->field1;
158 event.field2 = objs;
159 """
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100160
161 def formatter(event):
162 "gen %d GC collected %d objects" % \
163 (event.field1, event.field2)
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700164 probes.append(Probe("gc__start", "gc__done",
165 begin_save, end_save, formatter))
Sasha Goldshteinbee71b22016-10-26 06:34:06 -0700166#
167# Ruby
168#
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700169elif args.language == "ruby":
170 # Ruby GC probes do not have any additional information available.
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700171 probes.append(Probe("gc__mark__begin", "gc__mark__end",
172 "", "", lambda _: "GC mark stage"))
173 probes.append(Probe("gc__sweep__begin", "gc__sweep__end",
174 "", "", lambda _: "GC sweep stage"))
Sasha Goldshteinbee71b22016-10-26 06:34:06 -0700175#
176# Node
177#
Sasha Goldshtein88533472016-10-25 00:08:33 -0700178elif args.language == "node":
179 end_save = """
180 u32 gc_type = 0;
181 bpf_usdt_readarg(1, ctx, &gc_type);
182 event.field1 = gc_type;
183 """
184 descs = {"GC scavenge": 1, "GC mark-sweep-compact": 2,
185 "GC incremental mark": 4, "GC weak callbacks": 8}
186 probes.append(Probe("gc__start", "gc__done", "", end_save,
187 lambda e: str.join(", ",
188 [desc for desc, val in descs.items()
189 if e.field1 & val != 0])))
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700190
191for probe in probes:
192 program += probe.generate()
193 probe.attach()
194
195if args.verbose:
196 print(usdt.get_text())
197 print(program)
198
199bpf = BPF(text=program, usdt_contexts=[usdt])
200print("Tracing garbage collections in %s process %d... Ctrl-C to quit." %
201 (args.language, args.pid))
202time_col = "TIME (ms)" if args.milliseconds else "TIME (us)"
Sasha Goldshteinc3252002017-02-08 13:22:40 -0500203print("%-8s %-8s %-40s" % ("START", time_col, "DESCRIPTION"))
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700204
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700205class GCEvent(ct.Structure):
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700206 _fields_ = [
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700207 ("probe_index", ct.c_ulonglong),
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700208 ("elapsed_ns", ct.c_ulonglong),
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700209 ("field1", ct.c_ulonglong),
210 ("field2", ct.c_ulonglong),
211 ("field3", ct.c_ulonglong),
212 ("field4", ct.c_ulonglong),
213 ("string1", ct.c_char * 32),
214 ("string2", ct.c_char * 32)
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700215 ]
216
217start_ts = time.time()
218
219def print_event(cpu, data, size):
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700220 event = ct.cast(data, ct.POINTER(GCEvent)).contents
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100221 elapsed = event.elapsed_ns / 1000000 if args.milliseconds else \
222 event.elapsed_ns / 1000
Sasha Goldshteinc3252002017-02-08 13:22:40 -0500223 description = probes[event.probe_index].format(event)
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100224 if args.filter and args.filter not in description:
Sasha Goldshteinc3252002017-02-08 13:22:40 -0500225 return
226 print("%-8.3f %-8.2f %s" % (time.time() - start_ts, elapsed, description))
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700227
228bpf["gcs"].open_perf_buffer(print_event)
229while 1:
230 bpf.kprobe_poll()