blob: 331a8868632966e61fa7965e3c7132dda33de661 [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 Goldshtein88533472016-10-25 00:08:33 -07007# USAGE: ugc {java,python,ruby,node} PID [-v] [-m]
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
23"""
24parser = argparse.ArgumentParser(
25 description="Summarize garbage collection events in high-level languages.",
26 formatter_class=argparse.RawDescriptionHelpFormatter,
27 epilog=examples)
Sasha Goldshtein88533472016-10-25 00:08:33 -070028parser.add_argument("language", choices=["java", "python", "ruby", "node"],
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070029 help="language to trace")
30parser.add_argument("pid", type=int, help="process id to attach to")
31parser.add_argument("-v", "--verbose", action="store_true",
32 help="verbose mode: print the BPF program (for debugging purposes)")
33parser.add_argument("-m", "--milliseconds", action="store_true",
34 help="report times in milliseconds (default is microseconds)")
35args = parser.parse_args()
36
37usdt = USDT(pid=args.pid)
38
39program = """
40struct gc_event_t {
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070041 u64 probe_index;
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070042 u64 elapsed_ns;
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070043 u64 field1;
44 u64 field2;
45 u64 field3;
46 u64 field4;
47 char string1[32];
48 char string2[32];
49};
50struct entry_t {
51 u64 start_ns;
52 u64 field1;
53 u64 field2;
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070054};
55
56BPF_PERF_OUTPUT(gcs);
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070057BPF_HASH(entry, u64, struct entry_t);
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070058"""
59
60class Probe(object):
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070061 def __init__(self, begin, end, begin_save, end_save, formatter):
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070062 self.begin = begin
63 self.end = end
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070064 self.begin_save = begin_save
65 self.end_save = end_save
66 self.formatter = formatter
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070067
68 def generate(self):
69 text = """
70int trace_%s(struct pt_regs *ctx) {
71 u64 pid = bpf_get_current_pid_tgid();
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070072 struct entry_t e = {};
73 e.start_ns = bpf_ktime_get_ns();
74 %s
75 entry.update(&pid, &e);
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070076 return 0;
77}
78int trace_%s(struct pt_regs *ctx) {
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070079 u64 elapsed;
80 struct entry_t *e;
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070081 struct gc_event_t event = {};
82 u64 pid = bpf_get_current_pid_tgid();
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070083 e = entry.lookup(&pid);
84 if (!e) {
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070085 return 0; // missed the entry event on this thread
86 }
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070087 elapsed = bpf_ktime_get_ns() - e->start_ns;
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070088 event.elapsed_ns = elapsed;
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070089 %s
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070090 gcs.perf_submit(ctx, &event, sizeof(event));
91 return 0;
92}
Sasha Goldshteinddef09a2016-10-20 16:07:23 -070093 """ % (self.begin, self.begin_save, self.end, self.end_save)
Sasha Goldshteine5d9d992016-10-17 09:04:37 -070094 return text
95
96 def attach(self):
97 usdt.enable_probe(self.begin, "trace_%s" % self.begin)
98 usdt.enable_probe(self.end, "trace_%s" % self.end)
99
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700100 def format(self, data):
101 return self.formatter(data)
102
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700103probes = []
104
105if args.language == "java":
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700106 # Oddly, the gc__begin/gc__end probes don't really have any useful
107 # information, while the mem__pool* ones do. There's also a bunch of
108 # probes described in the hotspot_gc*.stp file which aren't there
109 # when looking at a live Java process.
110 begin_save = """
111 bpf_usdt_readarg(6, ctx, &e.field1); // used bytes
112 bpf_usdt_readarg(8, ctx, &e.field2); // max bytes
113 """
114 end_save = """
115 event.field1 = e->field1; // used bytes at start
116 event.field2 = e->field2; // max bytes at start
117 bpf_usdt_readarg(6, ctx, &event.field3); // used bytes at end
118 bpf_usdt_readarg(8, ctx, &event.field4); // max bytes at end
119 u64 manager = 0, pool = 0;
120 bpf_usdt_readarg(1, ctx, &manager); // ptr to manager name
121 bpf_usdt_readarg(3, ctx, &pool); // ptr to pool name
122 bpf_probe_read(&event.string1, sizeof(event.string1), (void *)manager);
123 bpf_probe_read(&event.string2, sizeof(event.string2), (void *)pool);
124 """
125 formatter = lambda e: "%s %s used=%d->%d max=%d->%d" % \
126 (e.string1, e.string2, e.field1, e.field3, e.field2, e.field4)
127 probes.append(Probe("mem__pool__gc__begin", "mem__pool__gc__end",
128 begin_save, end_save, formatter))
129 probes.append(Probe("gc__begin", "gc__end",
130 "", "", lambda _: "no additional info available"))
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700131elif args.language == "python":
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700132 begin_save = """
133 int gen = 0;
134 bpf_usdt_readarg(1, ctx, &gen);
135 e.field1 = gen;
136 """
137 end_save = """
138 long objs = 0;
139 bpf_usdt_readarg(1, ctx, &objs);
140 event.field1 = e->field1;
141 event.field2 = objs;
142 """
143 formatter = lambda event: "gen %d GC collected %d objects" % \
144 (event.field1, event.field2)
145 probes.append(Probe("gc__start", "gc__done",
146 begin_save, end_save, formatter))
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700147elif args.language == "ruby":
148 # Ruby GC probes do not have any additional information available.
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700149 probes.append(Probe("gc__mark__begin", "gc__mark__end",
150 "", "", lambda _: "GC mark stage"))
151 probes.append(Probe("gc__sweep__begin", "gc__sweep__end",
152 "", "", lambda _: "GC sweep stage"))
Sasha Goldshtein88533472016-10-25 00:08:33 -0700153elif args.language == "node":
154 end_save = """
155 u32 gc_type = 0;
156 bpf_usdt_readarg(1, ctx, &gc_type);
157 event.field1 = gc_type;
158 """
159 descs = {"GC scavenge": 1, "GC mark-sweep-compact": 2,
160 "GC incremental mark": 4, "GC weak callbacks": 8}
161 probes.append(Probe("gc__start", "gc__done", "", end_save,
162 lambda e: str.join(", ",
163 [desc for desc, val in descs.items()
164 if e.field1 & val != 0])))
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700165
166for probe in probes:
167 program += probe.generate()
168 probe.attach()
169
170if args.verbose:
171 print(usdt.get_text())
172 print(program)
173
174bpf = BPF(text=program, usdt_contexts=[usdt])
175print("Tracing garbage collections in %s process %d... Ctrl-C to quit." %
176 (args.language, args.pid))
177time_col = "TIME (ms)" if args.milliseconds else "TIME (us)"
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700178print("%-8s %-40s %-8s" % ("START", "DESCRIPTION", time_col))
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700179
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700180class GCEvent(ct.Structure):
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700181 _fields_ = [
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700182 ("probe_index", ct.c_ulonglong),
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700183 ("elapsed_ns", ct.c_ulonglong),
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700184 ("field1", ct.c_ulonglong),
185 ("field2", ct.c_ulonglong),
186 ("field3", ct.c_ulonglong),
187 ("field4", ct.c_ulonglong),
188 ("string1", ct.c_char * 32),
189 ("string2", ct.c_char * 32)
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700190 ]
191
192start_ts = time.time()
193
194def print_event(cpu, data, size):
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700195 event = ct.cast(data, ct.POINTER(GCEvent)).contents
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700196 elapsed = event.elapsed_ns/1000000 if args.milliseconds else \
197 event.elapsed_ns/1000
Sasha Goldshteinddef09a2016-10-20 16:07:23 -0700198 print("%-8.3f %-40s %-8.2f" % (time.time() - start_ts,
199 probes[event.probe_index].format(event),
200 elapsed))
Sasha Goldshteine5d9d992016-10-17 09:04:37 -0700201
202bpf["gcs"].open_perf_buffer(print_event)
203while 1:
204 bpf.kprobe_poll()