1#!/usr/bin/env bcc-lua 2--[[ 3Copyright 2016 GitHub, Inc 4 5Licensed under the Apache License, Version 2.0 (the "License"); 6you may not use this file except in compliance with the License. 7You may obtain a copy of the License at 8 9http://www.apache.org/licenses/LICENSE-2.0 10 11Unless required by applicable law or agreed to in writing, software 12distributed under the License is distributed on an "AS IS" BASIS, 13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14See the License for the specific language governing permissions and 15limitations under the License. 16--]] 17 18local program = [[ 19#include <uapi/linux/ptrace.h> 20#include <linux/sched.h> 21 22struct data_t { 23 u64 stack_id; 24 u32 pid; 25 char comm[TASK_COMM_LEN]; 26}; 27 28BPF_STACK_TRACE(stack_traces, 128); 29BPF_PERF_OUTPUT(events); 30 31void trace_stack(struct pt_regs *ctx) { 32 u32 pid = bpf_get_current_pid_tgid(); 33 FILTER 34 struct data_t data = {}; 35 data.stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID), 36 data.pid = pid; 37 bpf_get_current_comm(&data.comm, sizeof(data.comm)); 38 events.perf_submit(ctx, &data, sizeof(data)); 39} 40]] 41 42local ffi = require("ffi") 43 44return function(BPF, utils) 45 local parser = utils.argparse("stacksnoop", 46 "Trace and print kernel stack traces for a kernel function") 47 parser:flag("-s --offset") 48 parser:flag("-v --verbose") 49 parser:option("-p --pid"):convert(tonumber) 50 parser:argument("function", "kernel function name"):target("fn") 51 52 local args = parser:parse() 53 local ksym = BPF.SymbolCache() 54 local filter = "" 55 56 if args.pid then 57 filter = "if (pid != %d) { return; }" % args.pid 58 end 59 60 local text = program:gsub("FILTER", filter) 61 local bpf = BPF:new{text=text} 62 bpf:attach_kprobe{event=args.fn, fn_name="trace_stack"} 63 64 if BPF.num_open_kprobes() == 0 then 65 print("Function \"%s\" not found. Exiting." % {args.fn}) 66 return 67 end 68 69 if args.verbose then 70 print("%-18s %-12s %-6s %-3s %s" % 71 {"TIME(s)", "COMM", "PID", "CPU", "FUNCTION"}) 72 else 73 print("%-18s %s" % {"TIME(s)", "FUNCTION"}) 74 end 75 76 local stack_traces = bpf:get_table("stack_traces") 77 local start_ts = utils.posix.time_ns() 78 79 local function print_event(cpu, event) 80 local ts = (utils.posix.time_ns() - start_ts) / 1e9 81 82 if args.verbose then 83 print("%-18.9f %-12.12s %-6d %-3d %s" % 84 {ts, ffi.string(event.comm), event.pid, cpu, args.fn}) 85 else 86 print("%-18.9f %s" % {ts, args.fn}) 87 end 88 89 for addr in stack_traces:walk(tonumber(event.stack_id)) do 90 local sym, offset = ksym:resolve(addr) 91 if args.offset then 92 print("\t%-16p %s+0x%x" % {addr, sym, tonumber(offset)}) 93 else 94 print("\t%-16p %s" % {addr, sym}) 95 end 96 end 97 98 print() 99 end 100 101 local TASK_COMM_LEN = 16 -- linux/sched.h 102 103 bpf:get_table("events"):open_perf_buffer(print_event, 104 "struct { uint64_t stack_id; uint32_t pid; char comm[$]; }", 105 {TASK_COMM_LEN}) 106 bpf:perf_buffer_poll_loop() 107end 108