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