xref: /aosp_15_r20/external/bcc/tools/lib/uobjnew.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# uobjnew  Summarize object allocations 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: uobjnew [-h] [-T TOP] [-v] {c,java,ruby,tcl} pid [interval]
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# 25-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 Workerfrom time import sleep
18*387f9dfdSAndroid Build Coastguard Workerimport os
19*387f9dfdSAndroid Build Coastguard Worker
20*387f9dfdSAndroid Build Coastguard Worker# C needs to be the last language.
21*387f9dfdSAndroid Build Coastguard Workerlanguages = ["c", "java", "ruby", "tcl"]
22*387f9dfdSAndroid Build Coastguard Worker
23*387f9dfdSAndroid Build Coastguard Workerexamples = """examples:
24*387f9dfdSAndroid Build Coastguard Worker    ./uobjnew -l java 145         # summarize Java allocations in process 145
25*387f9dfdSAndroid Build Coastguard Worker    ./uobjnew -l c 2020 1         # grab malloc() sizes and print every second
26*387f9dfdSAndroid Build Coastguard Worker    ./uobjnew -l ruby 6712 -C 10  # top 10 Ruby types by number of allocations
27*387f9dfdSAndroid Build Coastguard Worker    ./uobjnew -l ruby 6712 -S 10  # top 10 Ruby types by total size
28*387f9dfdSAndroid Build Coastguard Worker"""
29*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
30*387f9dfdSAndroid Build Coastguard Worker    description="Summarize object allocations in high-level languages.",
31*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
32*387f9dfdSAndroid Build Coastguard Worker    epilog=examples)
33*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-l", "--language", choices=languages,
34*387f9dfdSAndroid Build Coastguard Worker    help="language to trace")
35*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("pid", type=int, help="process id to attach to")
36*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("interval", type=int, nargs='?',
37*387f9dfdSAndroid Build Coastguard Worker    help="print every specified number of seconds")
38*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-C", "--top-count", type=int,
39*387f9dfdSAndroid Build Coastguard Worker    help="number of most frequently allocated types to print")
40*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-S", "--top-size", type=int,
41*387f9dfdSAndroid Build Coastguard Worker    help="number of largest types by allocated bytes to print")
42*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-v", "--verbose", action="store_true",
43*387f9dfdSAndroid Build Coastguard Worker    help="verbose mode: print the BPF program (for debugging purposes)")
44*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true",
45*387f9dfdSAndroid Build Coastguard Worker    help=argparse.SUPPRESS)
46*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args()
47*387f9dfdSAndroid Build Coastguard Worker
48*387f9dfdSAndroid Build Coastguard Workerlanguage = args.language
49*387f9dfdSAndroid Build Coastguard Workerif not language:
50*387f9dfdSAndroid Build Coastguard Worker    language = utils.detect_language(languages, args.pid)
51*387f9dfdSAndroid Build Coastguard Worker
52*387f9dfdSAndroid Build Coastguard Workerprogram = """
53*387f9dfdSAndroid Build Coastguard Worker#include <linux/ptrace.h>
54*387f9dfdSAndroid Build Coastguard Worker
55*387f9dfdSAndroid Build Coastguard Workerstruct key_t {
56*387f9dfdSAndroid Build Coastguard Worker#if MALLOC_TRACING
57*387f9dfdSAndroid Build Coastguard Worker    u64 size;
58*387f9dfdSAndroid Build Coastguard Worker#else
59*387f9dfdSAndroid Build Coastguard Worker    char name[50];
60*387f9dfdSAndroid Build Coastguard Worker#endif
61*387f9dfdSAndroid Build Coastguard Worker};
62*387f9dfdSAndroid Build Coastguard Worker
63*387f9dfdSAndroid Build Coastguard Workerstruct val_t {
64*387f9dfdSAndroid Build Coastguard Worker    u64 total_size;
65*387f9dfdSAndroid Build Coastguard Worker    u64 num_allocs;
66*387f9dfdSAndroid Build Coastguard Worker};
67*387f9dfdSAndroid Build Coastguard Worker
68*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(allocs, struct key_t, struct val_t);
69*387f9dfdSAndroid Build Coastguard Worker""".replace("MALLOC_TRACING", "1" if language == "c" else "0")
70*387f9dfdSAndroid Build Coastguard Worker
71*387f9dfdSAndroid Build Coastguard Workerusdt = USDT(pid=args.pid)
72*387f9dfdSAndroid Build Coastguard Worker
73*387f9dfdSAndroid Build Coastguard Worker#
74*387f9dfdSAndroid Build Coastguard Worker# C
75*387f9dfdSAndroid Build Coastguard Worker#
76*387f9dfdSAndroid Build Coastguard Workerif language == "c":
77*387f9dfdSAndroid Build Coastguard Worker    program += """
78*387f9dfdSAndroid Build Coastguard Workerint alloc_entry(struct pt_regs *ctx, size_t size) {
79*387f9dfdSAndroid Build Coastguard Worker    struct key_t key = {};
80*387f9dfdSAndroid Build Coastguard Worker    struct val_t *valp, zero = {};
81*387f9dfdSAndroid Build Coastguard Worker    key.size = size;
82*387f9dfdSAndroid Build Coastguard Worker    valp = allocs.lookup_or_try_init(&key, &zero);
83*387f9dfdSAndroid Build Coastguard Worker    if (valp) {
84*387f9dfdSAndroid Build Coastguard Worker        valp->total_size += size;
85*387f9dfdSAndroid Build Coastguard Worker        valp->num_allocs += 1;
86*387f9dfdSAndroid Build Coastguard Worker    }
87*387f9dfdSAndroid Build Coastguard Worker    return 0;
88*387f9dfdSAndroid Build Coastguard Worker}
89*387f9dfdSAndroid Build Coastguard Worker    """
90*387f9dfdSAndroid Build Coastguard Worker#
91*387f9dfdSAndroid Build Coastguard Worker# Java
92*387f9dfdSAndroid Build Coastguard Worker#
93*387f9dfdSAndroid Build Coastguard Workerelif language == "java":
94*387f9dfdSAndroid Build Coastguard Worker    program += """
95*387f9dfdSAndroid Build Coastguard Workerint alloc_entry(struct pt_regs *ctx) {
96*387f9dfdSAndroid Build Coastguard Worker    struct key_t key = {};
97*387f9dfdSAndroid Build Coastguard Worker    struct val_t *valp, zero = {};
98*387f9dfdSAndroid Build Coastguard Worker    u64 classptr = 0, size = 0;
99*387f9dfdSAndroid Build Coastguard Worker    u32 length = 0;
100*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(2, ctx, &classptr);
101*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(3, ctx, &length);
102*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(4, ctx, &size);
103*387f9dfdSAndroid Build Coastguard Worker    bpf_probe_read_user(&key.name, min(sizeof(key.name), (size_t)length), (void *)classptr);
104*387f9dfdSAndroid Build Coastguard Worker    valp = allocs.lookup_or_try_init(&key, &zero);
105*387f9dfdSAndroid Build Coastguard Worker    if (valp) {
106*387f9dfdSAndroid Build Coastguard Worker        valp->total_size += size;
107*387f9dfdSAndroid Build Coastguard Worker        valp->num_allocs += 1;
108*387f9dfdSAndroid Build Coastguard Worker    }
109*387f9dfdSAndroid Build Coastguard Worker    return 0;
110*387f9dfdSAndroid Build Coastguard Worker}
111*387f9dfdSAndroid Build Coastguard Worker    """
112*387f9dfdSAndroid Build Coastguard Worker    usdt.enable_probe_or_bail("object__alloc", "alloc_entry")
113*387f9dfdSAndroid Build Coastguard Worker#
114*387f9dfdSAndroid Build Coastguard Worker# Ruby
115*387f9dfdSAndroid Build Coastguard Worker#
116*387f9dfdSAndroid Build Coastguard Workerelif language == "ruby":
117*387f9dfdSAndroid Build Coastguard Worker    create_template = """
118*387f9dfdSAndroid Build Coastguard Workerint THETHING_alloc_entry(struct pt_regs *ctx) {
119*387f9dfdSAndroid Build Coastguard Worker    struct key_t key = { .name = "THETHING" };
120*387f9dfdSAndroid Build Coastguard Worker    struct val_t *valp, zero = {};
121*387f9dfdSAndroid Build Coastguard Worker    u64 size = 0;
122*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(1, ctx, &size);
123*387f9dfdSAndroid Build Coastguard Worker    valp = allocs.lookup_or_try_init(&key, &zero);
124*387f9dfdSAndroid Build Coastguard Worker    if (valp) {
125*387f9dfdSAndroid Build Coastguard Worker        valp->total_size += size;
126*387f9dfdSAndroid Build Coastguard Worker        valp->num_allocs += 1;
127*387f9dfdSAndroid Build Coastguard Worker    }
128*387f9dfdSAndroid Build Coastguard Worker    return 0;
129*387f9dfdSAndroid Build Coastguard Worker}
130*387f9dfdSAndroid Build Coastguard Worker    """
131*387f9dfdSAndroid Build Coastguard Worker    program += """
132*387f9dfdSAndroid Build Coastguard Workerint object_alloc_entry(struct pt_regs *ctx) {
133*387f9dfdSAndroid Build Coastguard Worker    struct key_t key = {};
134*387f9dfdSAndroid Build Coastguard Worker    struct val_t *valp, zero = {};
135*387f9dfdSAndroid Build Coastguard Worker    u64 classptr = 0;
136*387f9dfdSAndroid Build Coastguard Worker    bpf_usdt_readarg(1, ctx, &classptr);
137*387f9dfdSAndroid Build Coastguard Worker    bpf_probe_read_user(&key.name, sizeof(key.name), (void *)classptr);
138*387f9dfdSAndroid Build Coastguard Worker    valp = allocs.lookup_or_try_init(&key, &zero);
139*387f9dfdSAndroid Build Coastguard Worker    if (valp) {
140*387f9dfdSAndroid Build Coastguard Worker        valp->num_allocs += 1;  // We don't know the size, unfortunately
141*387f9dfdSAndroid Build Coastguard Worker    }
142*387f9dfdSAndroid Build Coastguard Worker    return 0;
143*387f9dfdSAndroid Build Coastguard Worker}
144*387f9dfdSAndroid Build Coastguard Worker    """
145*387f9dfdSAndroid Build Coastguard Worker    usdt.enable_probe_or_bail("object__create", "object_alloc_entry")
146*387f9dfdSAndroid Build Coastguard Worker    for thing in ["string", "hash", "array"]:
147*387f9dfdSAndroid Build Coastguard Worker        program += create_template.replace("THETHING", thing)
148*387f9dfdSAndroid Build Coastguard Worker        usdt.enable_probe_or_bail("%s__create" % thing,
149*387f9dfdSAndroid Build Coastguard Worker                                  "%s_alloc_entry" % thing)
150*387f9dfdSAndroid Build Coastguard Worker#
151*387f9dfdSAndroid Build Coastguard Worker# Tcl
152*387f9dfdSAndroid Build Coastguard Worker#
153*387f9dfdSAndroid Build Coastguard Workerelif language == "tcl":
154*387f9dfdSAndroid Build Coastguard Worker    program += """
155*387f9dfdSAndroid Build Coastguard Workerint alloc_entry(struct pt_regs *ctx) {
156*387f9dfdSAndroid Build Coastguard Worker    struct key_t key = { .name = "<ALL>" };
157*387f9dfdSAndroid Build Coastguard Worker    struct val_t *valp, zero = {};
158*387f9dfdSAndroid Build Coastguard Worker    valp = allocs.lookup_or_try_init(&key, &zero);
159*387f9dfdSAndroid Build Coastguard Worker    if (valp) {
160*387f9dfdSAndroid Build Coastguard Worker        valp->num_allocs += 1;
161*387f9dfdSAndroid Build Coastguard Worker    }
162*387f9dfdSAndroid Build Coastguard Worker    return 0;
163*387f9dfdSAndroid Build Coastguard Worker}
164*387f9dfdSAndroid Build Coastguard Worker    """
165*387f9dfdSAndroid Build Coastguard Worker    usdt.enable_probe_or_bail("obj__create", "alloc_entry")
166*387f9dfdSAndroid Build Coastguard Workerelse:
167*387f9dfdSAndroid Build Coastguard Worker    print("No language detected; use -l to trace a language.")
168*387f9dfdSAndroid Build Coastguard Worker    exit(1)
169*387f9dfdSAndroid Build Coastguard Worker
170*387f9dfdSAndroid Build Coastguard Worker
171*387f9dfdSAndroid Build Coastguard Workerif args.ebpf or args.verbose:
172*387f9dfdSAndroid Build Coastguard Worker    if args.verbose:
173*387f9dfdSAndroid Build Coastguard Worker        print(usdt.get_text())
174*387f9dfdSAndroid Build Coastguard Worker    print(program)
175*387f9dfdSAndroid Build Coastguard Worker    if args.ebpf:
176*387f9dfdSAndroid Build Coastguard Worker        exit()
177*387f9dfdSAndroid Build Coastguard Worker
178*387f9dfdSAndroid Build Coastguard Workerbpf = BPF(text=program, usdt_contexts=[usdt])
179*387f9dfdSAndroid Build Coastguard Workerif language == "c":
180*387f9dfdSAndroid Build Coastguard Worker    bpf.attach_uprobe(name="c", sym="malloc", fn_name="alloc_entry",
181*387f9dfdSAndroid Build Coastguard Worker                      pid=args.pid)
182*387f9dfdSAndroid Build Coastguard Worker
183*387f9dfdSAndroid Build Coastguard Workerexit_signaled = False
184*387f9dfdSAndroid Build Coastguard Workerprint("Tracing allocations in process %d (language: %s)... Ctrl-C to quit." %
185*387f9dfdSAndroid Build Coastguard Worker      (args.pid, language or "none"))
186*387f9dfdSAndroid Build Coastguard Workerwhile True:
187*387f9dfdSAndroid Build Coastguard Worker    try:
188*387f9dfdSAndroid Build Coastguard Worker        sleep(args.interval or 99999999)
189*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
190*387f9dfdSAndroid Build Coastguard Worker        exit_signaled = True
191*387f9dfdSAndroid Build Coastguard Worker    print()
192*387f9dfdSAndroid Build Coastguard Worker    data = bpf["allocs"]
193*387f9dfdSAndroid Build Coastguard Worker    if args.top_count:
194*387f9dfdSAndroid Build Coastguard Worker        data = sorted(data.items(), key=lambda kv: kv[1].num_allocs)
195*387f9dfdSAndroid Build Coastguard Worker        data = data[-args.top_count:]
196*387f9dfdSAndroid Build Coastguard Worker    elif args.top_size:
197*387f9dfdSAndroid Build Coastguard Worker        data = sorted(data.items(), key=lambda kv: kv[1].total_size)
198*387f9dfdSAndroid Build Coastguard Worker        data = data[-args.top_size:]
199*387f9dfdSAndroid Build Coastguard Worker    else:
200*387f9dfdSAndroid Build Coastguard Worker        data = sorted(data.items(), key=lambda kv: kv[1].total_size)
201*387f9dfdSAndroid Build Coastguard Worker    print("%-30s %8s %12s" % ("NAME/TYPE", "# ALLOCS", "# BYTES"))
202*387f9dfdSAndroid Build Coastguard Worker    for key, value in data:
203*387f9dfdSAndroid Build Coastguard Worker        if language == "c":
204*387f9dfdSAndroid Build Coastguard Worker            obj_type = "block size %d" % key.size
205*387f9dfdSAndroid Build Coastguard Worker        else:
206*387f9dfdSAndroid Build Coastguard Worker            obj_type = key.name
207*387f9dfdSAndroid Build Coastguard Worker        print("%-30s %8d %12d" %
208*387f9dfdSAndroid Build Coastguard Worker              (obj_type, value.num_allocs, value.total_size))
209*387f9dfdSAndroid Build Coastguard Worker    if args.interval and not exit_signaled:
210*387f9dfdSAndroid Build Coastguard Worker        bpf["allocs"].clear()
211*387f9dfdSAndroid Build Coastguard Worker    else:
212*387f9dfdSAndroid Build Coastguard Worker        exit()
213