blob: 60359c427ea9469434630cd17e5e7a5c05d12577 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -07002# @lint-avoid-python-3-compatibility-imports
3#
4# uobjnew Summarize object allocations in high-level languages.
5# For Linux, uses BCC, eBPF.
6#
Marko Myllynen9f3662e2018-10-10 21:48:53 +03007# USAGE: uobjnew [-h] [-T TOP] [-v] {c,java,ruby,tcl} pid [interval]
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -07008#
9# Copyright 2016 Sasha Goldshtein
10# Licensed under the Apache License, Version 2.0 (the "License")
11#
12# 25-Oct-2016 Sasha Goldshtein Created this.
13
14from __future__ import print_function
15import argparse
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +020016from bcc import BPF, USDT, utils
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -070017from time import sleep
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +020018import os
19
20# C needs to be the last language.
Marko Myllynen9f3662e2018-10-10 21:48:53 +030021languages = ["c", "java", "ruby", "tcl"]
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -070022
23examples = """examples:
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +020024 ./uobjnew -l java 145 # summarize Java allocations in process 145
25 ./uobjnew -l c 2020 1 # grab malloc() sizes and print every second
26 ./uobjnew -l ruby 6712 -C 10 # top 10 Ruby types by number of allocations
27 ./uobjnew -l ruby 6712 -S 10 # top 10 Ruby types by total size
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -070028"""
29parser = argparse.ArgumentParser(
30 description="Summarize object allocations in high-level languages.",
31 formatter_class=argparse.RawDescriptionHelpFormatter,
32 epilog=examples)
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +020033parser.add_argument("-l", "--language", choices=languages,
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -070034 help="language to trace")
35parser.add_argument("pid", type=int, help="process id to attach to")
36parser.add_argument("interval", type=int, nargs='?',
37 help="print every specified number of seconds")
38parser.add_argument("-C", "--top-count", type=int,
39 help="number of most frequently allocated types to print")
40parser.add_argument("-S", "--top-size", type=int,
41 help="number of largest types by allocated bytes to print")
42parser.add_argument("-v", "--verbose", action="store_true",
43 help="verbose mode: print the BPF program (for debugging purposes)")
Marko Myllynen27e7aea2018-09-26 20:09:07 +030044parser.add_argument("--ebpf", action="store_true",
45 help=argparse.SUPPRESS)
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -070046args = parser.parse_args()
47
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +020048language = args.language
49if not language:
50 language = utils.detect_language(languages, args.pid)
51
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -070052program = """
53#include <linux/ptrace.h>
54
55struct key_t {
56#if MALLOC_TRACING
57 u64 size;
58#else
59 char name[50];
60#endif
61};
62
63struct val_t {
64 u64 total_size;
65 u64 num_allocs;
66};
67
68BPF_HASH(allocs, struct key_t, struct val_t);
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +020069""".replace("MALLOC_TRACING", "1" if language == "c" else "0")
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -070070
71usdt = USDT(pid=args.pid)
72
Sasha Goldshteinbee71b22016-10-26 06:34:06 -070073#
Marko Myllynen27e7aea2018-09-26 20:09:07 +030074# C
75#
76if language == "c":
77 program += """
78int alloc_entry(struct pt_regs *ctx, size_t size) {
79 struct key_t key = {};
80 struct val_t *valp, zero = {};
81 key.size = size;
yonghong-song82f43022019-10-31 08:16:12 -070082 valp = allocs.lookup_or_try_init(&key, &zero);
Philip Gladstoneba64f032019-09-20 01:12:01 -040083 if (valp) {
84 valp->total_size += size;
85 valp->num_allocs += 1;
86 }
Marko Myllynen27e7aea2018-09-26 20:09:07 +030087 return 0;
88}
89 """
90#
Sasha Goldshteinbee71b22016-10-26 06:34:06 -070091# Java
92#
Marko Myllynen27e7aea2018-09-26 20:09:07 +030093elif language == "java":
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -070094 program += """
95int alloc_entry(struct pt_regs *ctx) {
96 struct key_t key = {};
97 struct val_t *valp, zero = {};
98 u64 classptr = 0, size = 0;
denghui.ddh039cef62021-11-09 21:11:40 +080099 u32 length = 0;
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700100 bpf_usdt_readarg(2, ctx, &classptr);
denghui.ddh039cef62021-11-09 21:11:40 +0800101 bpf_usdt_readarg(3, ctx, &length);
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700102 bpf_usdt_readarg(4, ctx, &size);
denghui.ddh039cef62021-11-09 21:11:40 +0800103 bpf_probe_read_user(&key.name, min(sizeof(key.name), (size_t)length), (void *)classptr);
yonghong-song82f43022019-10-31 08:16:12 -0700104 valp = allocs.lookup_or_try_init(&key, &zero);
Philip Gladstoneba64f032019-09-20 01:12:01 -0400105 if (valp) {
106 valp->total_size += size;
107 valp->num_allocs += 1;
108 }
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700109 return 0;
110}
111 """
Sasha Goldshteindc3a57c2017-02-08 16:02:11 -0500112 usdt.enable_probe_or_bail("object__alloc", "alloc_entry")
Sasha Goldshteinbee71b22016-10-26 06:34:06 -0700113#
114# Ruby
115#
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +0200116elif language == "ruby":
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700117 create_template = """
118int THETHING_alloc_entry(struct pt_regs *ctx) {
119 struct key_t key = { .name = "THETHING" };
120 struct val_t *valp, zero = {};
121 u64 size = 0;
122 bpf_usdt_readarg(1, ctx, &size);
yonghong-song82f43022019-10-31 08:16:12 -0700123 valp = allocs.lookup_or_try_init(&key, &zero);
Philip Gladstoneba64f032019-09-20 01:12:01 -0400124 if (valp) {
125 valp->total_size += size;
126 valp->num_allocs += 1;
127 }
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700128 return 0;
129}
130 """
131 program += """
132int object_alloc_entry(struct pt_regs *ctx) {
133 struct key_t key = {};
134 struct val_t *valp, zero = {};
135 u64 classptr = 0;
136 bpf_usdt_readarg(1, ctx, &classptr);
Sumanth Korikkar023154c2020-04-20 05:54:57 -0500137 bpf_probe_read_user(&key.name, sizeof(key.name), (void *)classptr);
yonghong-song82f43022019-10-31 08:16:12 -0700138 valp = allocs.lookup_or_try_init(&key, &zero);
Philip Gladstoneba64f032019-09-20 01:12:01 -0400139 if (valp) {
140 valp->num_allocs += 1; // We don't know the size, unfortunately
141 }
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700142 return 0;
143}
144 """
Sasha Goldshteindc3a57c2017-02-08 16:02:11 -0500145 usdt.enable_probe_or_bail("object__create", "object_alloc_entry")
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700146 for thing in ["string", "hash", "array"]:
147 program += create_template.replace("THETHING", thing)
Paul Chaignon956ca1c2017-03-04 20:07:56 +0100148 usdt.enable_probe_or_bail("%s__create" % thing,
149 "%s_alloc_entry" % thing)
Marko Myllynen9f3662e2018-10-10 21:48:53 +0300150#
151# Tcl
152#
153elif language == "tcl":
154 program += """
155int alloc_entry(struct pt_regs *ctx) {
156 struct key_t key = { .name = "<ALL>" };
157 struct val_t *valp, zero = {};
yonghong-song82f43022019-10-31 08:16:12 -0700158 valp = allocs.lookup_or_try_init(&key, &zero);
Philip Gladstoneba64f032019-09-20 01:12:01 -0400159 if (valp) {
160 valp->num_allocs += 1;
161 }
Marko Myllynen9f3662e2018-10-10 21:48:53 +0300162 return 0;
163}
164 """
165 usdt.enable_probe_or_bail("obj__create", "alloc_entry")
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +0200166else:
167 print("No language detected; use -l to trace a language.")
168 exit(1)
169
170
Marko Myllynen27e7aea2018-09-26 20:09:07 +0300171if args.ebpf or args.verbose:
172 if args.verbose:
173 print(usdt.get_text())
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700174 print(program)
Marko Myllynen27e7aea2018-09-26 20:09:07 +0300175 if args.ebpf:
176 exit()
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700177
178bpf = BPF(text=program, usdt_contexts=[usdt])
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +0200179if language == "c":
Sasha Goldshtein39ace6f2016-12-19 09:52:34 +0000180 bpf.attach_uprobe(name="c", sym="malloc", fn_name="alloc_entry",
181 pid=args.pid)
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700182
183exit_signaled = False
184print("Tracing allocations in process %d (language: %s)... Ctrl-C to quit." %
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +0200185 (args.pid, language or "none"))
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700186while True:
187 try:
188 sleep(args.interval or 99999999)
189 except KeyboardInterrupt:
190 exit_signaled = True
191 print()
192 data = bpf["allocs"]
193 if args.top_count:
Rafael Fonsecac465a242017-02-13 16:04:33 +0100194 data = sorted(data.items(), key=lambda kv: kv[1].num_allocs)
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700195 data = data[-args.top_count:]
196 elif args.top_size:
Rafael Fonsecac465a242017-02-13 16:04:33 +0100197 data = sorted(data.items(), key=lambda kv: kv[1].total_size)
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700198 data = data[-args.top_size:]
199 else:
Rafael Fonsecac465a242017-02-13 16:04:33 +0100200 data = sorted(data.items(), key=lambda kv: kv[1].total_size)
Marko Myllynen9f3662e2018-10-10 21:48:53 +0300201 print("%-30s %8s %12s" % ("NAME/TYPE", "# ALLOCS", "# BYTES"))
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700202 for key, value in data:
Paul Chaignon4bb6d7f2017-03-30 19:05:40 +0200203 if language == "c":
Sasha Goldshtein2e7e2402016-10-25 05:28:29 -0700204 obj_type = "block size %d" % key.size
205 else:
206 obj_type = key.name
207 print("%-30s %8d %12d" %
208 (obj_type, value.num_allocs, value.total_size))
209 if args.interval and not exit_signaled:
210 bpf["allocs"].clear()
211 else:
212 exit()