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 13from __future__ import print_function 14from bcc import BPF 15from bcc.utils import printb 16from time import sleep 17import sys 18 19if len(sys.argv) < 2: 20 print("USAGE: mallocstacks PID [NUM_STACKS=1024]") 21 exit() 22pid = int(sys.argv[1]) 23if len(sys.argv) == 3: 24 try: 25 assert int(sys.argv[2]) > 0, "" 26 except (ValueError, AssertionError) as e: 27 print("USAGE: mallocstacks PID [NUM_STACKS=1024]") 28 print("NUM_STACKS must be a non-zero, positive integer") 29 exit() 30 stacks = sys.argv[2] 31else: 32 stacks = "1024" 33 34# load BPF program 35b = BPF(text=""" 36#include <uapi/linux/ptrace.h> 37 38BPF_HASH(calls, int); 39BPF_STACK_TRACE(stack_traces, """ + stacks + """); 40 41int alloc_enter(struct pt_regs *ctx, size_t size) { 42 int key = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); 43 if (key < 0) 44 return 0; 45 46 // could also use `calls.increment(key, size);` 47 u64 zero = 0, *val; 48 val = calls.lookup_or_try_init(&key, &zero); 49 if (val) { 50 (*val) += size; 51 } 52 return 0; 53}; 54""") 55 56b.attach_uprobe(name="c", sym="malloc", fn_name="alloc_enter", pid=pid) 57print("Attaching to malloc in pid %d, Ctrl+C to quit." % pid) 58 59# sleep until Ctrl-C 60try: 61 sleep(99999999) 62except KeyboardInterrupt: 63 pass 64 65calls = b.get_table("calls") 66stack_traces = b.get_table("stack_traces") 67 68for k, v in reversed(sorted(calls.items(), key=lambda c: c[1].value)): 69 print("%d bytes allocated at:" % v.value) 70 if k.value > 0 : 71 for addr in stack_traces.walk(k.value): 72 printb(b"\t%s" % b.sym(addr, pid, show_offset=True)) 73