1 #!/usr/bin/python 2 # 3 # mallocstacks Trace malloc() calls in a process and print the full 4 # stack trace for all callsites. 5 # For Linux, uses BCC, eBPF. Embedded C. 6 # 7 # This script is a basic example of the new Linux 4.6+ BPF_STACK_TRACE 8 # table API. 9 # 10 # Copyright 2016 GitHub, Inc. 11 # Licensed under the Apache License, Version 2.0 (the "License") 12 13 from __future__ import print_function 14 from bcc import BPF 15 from time import sleep 16 import sys 17 18 if len(sys.argv) < 2: 19 print("USAGE: mallocstacks PID") 20 exit() 21 pid = int(sys.argv[1]) 22 23 # load BPF program 24 b = BPF(text=""" 25 #include <uapi/linux/ptrace.h> 26 27 BPF_HASH(calls, int); 28 BPF_STACK_TRACE(stack_traces, 1024); 29 30 int alloc_enter(struct pt_regs *ctx, size_t size) { 31 int key = stack_traces.get_stackid(ctx, 32 BPF_F_USER_STACK|BPF_F_REUSE_STACKID); 33 if (key < 0) 34 return 0; 35 36 // could also use `calls.increment(key, size);` 37 u64 zero = 0, *val; 38 val = calls.lookup_or_init(&key, &zero); 39 (*val) += size; 40 return 0; 41 }; 42 """) 43 44 b.attach_uprobe(name="c", sym="malloc", fn_name="alloc_enter", pid=pid) 45 print("Attaching to malloc in pid %d, Ctrl+C to quit." % pid) 46 47 # sleep until Ctrl-C 48 try: 49 sleep(99999999) 50 except KeyboardInterrupt: 51 pass 52 53 calls = b.get_table("calls") 54 stack_traces = b.get_table("stack_traces") 55 56 for k, v in reversed(sorted(calls.items(), key=lambda c: c[1].value)): 57 print("%d bytes allocated at:" % v.value) 58 for addr in stack_traces.walk(k.value): 59 print("\t%s" % b.sym(addr, pid, show_offset=True)) 60