1#!/usr/bin/env python 2# 3# stacksnoop Trace a kernel function and print all kernel stack traces. 4# For Linux, uses BCC, eBPF, and currently x86_64 only. Inline C. 5# 6# USAGE: stacksnoop [-h] [-p PID] [-s] [-v] function 7# 8# Copyright 2016 Netflix, Inc. 9# Licensed under the Apache License, Version 2.0 (the "License") 10# 11# 12-Jan-2016 Brendan Gregg Created this. 12 13from __future__ import print_function 14from bcc import BPF 15import argparse 16import ctypes as ct 17import time 18 19# arguments 20examples = """examples: 21 ./stacksnoop ext4_sync_fs # print kernel stack traces for ext4_sync_fs 22 ./stacksnoop -s ext4_sync_fs # ... also show symbol offsets 23 ./stacksnoop -v ext4_sync_fs # ... show extra columns 24 ./stacksnoop -p 185 ext4_sync_fs # ... only when PID 185 is on-CPU 25""" 26parser = argparse.ArgumentParser( 27 description="Trace and print kernel stack traces for a kernel function", 28 formatter_class=argparse.RawDescriptionHelpFormatter, 29 epilog=examples) 30parser.add_argument("-p", "--pid", 31 help="trace this PID only") 32parser.add_argument("-s", "--offset", action="store_true", 33 help="show address offsets") 34parser.add_argument("-v", "--verbose", action="store_true", 35 help="print more fields") 36parser.add_argument("function", 37 help="kernel function name") 38args = parser.parse_args() 39function = args.function 40offset = args.offset 41verbose = args.verbose 42debug = 0 43 44# define BPF program 45bpf_text = """ 46#include <uapi/linux/ptrace.h> 47#include <linux/sched.h> 48 49struct data_t { 50 u64 stack_id; 51 u32 pid; 52 char comm[TASK_COMM_LEN]; 53}; 54 55BPF_STACK_TRACE(stack_traces, 128); 56BPF_PERF_OUTPUT(events); 57 58void trace_stack(struct pt_regs *ctx) { 59 u32 pid = bpf_get_current_pid_tgid(); 60 FILTER 61 struct data_t data = {}; 62 data.stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID), 63 data.pid = pid; 64 bpf_get_current_comm(&data.comm, sizeof(data.comm)); 65 events.perf_submit(ctx, &data, sizeof(data)); 66} 67""" 68if args.pid: 69 bpf_text = bpf_text.replace('FILTER', 70 'if (pid != %s) { return; }' % args.pid) 71else: 72 bpf_text = bpf_text.replace('FILTER', '') 73if debug: 74 print(bpf_text) 75 76# initialize BPF 77b = BPF(text=bpf_text) 78b.attach_kprobe(event=function, fn_name="trace_stack") 79 80TASK_COMM_LEN = 16 # linux/sched.h 81 82class Data(ct.Structure): 83 _fields_ = [ 84 ("stack_id", ct.c_ulonglong), 85 ("pid", ct.c_uint), 86 ("comm", ct.c_char * TASK_COMM_LEN), 87 ] 88 89matched = b.num_open_kprobes() 90if matched == 0: 91 print("Function \"%s\" not found. Exiting." % function) 92 exit() 93 94stack_traces = b.get_table("stack_traces") 95start_ts = time.time() 96 97# header 98if verbose: 99 print("%-18s %-12s %-6s %-3s %s" % 100 ("TIME(s)", "COMM", "PID", "CPU", "FUNCTION")) 101else: 102 print("%-18s %s" % ("TIME(s)", "FUNCTION")) 103 104def print_event(cpu, data, size): 105 event = ct.cast(data, ct.POINTER(Data)).contents 106 107 ts = time.time() - start_ts 108 109 if verbose: 110 print("%-18.9f %-12.12s %-6d %-3d %s" % 111 (ts, event.comm.decode(), event.pid, cpu, function)) 112 else: 113 print("%-18.9f %s" % (ts, function)) 114 115 for addr in stack_traces.walk(event.stack_id): 116 sym = b.ksym(addr, show_offset=offset) 117 print("\t%s" % sym) 118 119 print() 120 121b["events"].open_perf_buffer(print_event) 122while 1: 123 b.perf_buffer_poll() 124