| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 1 | #!/usr/bin/env bcc-lua |
| 2 | --[[ |
| 3 | Copyright 2016 GitHub, Inc |
| 4 | |
| 5 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | you may not use this file except in compliance with the License. |
| 7 | You may obtain a copy of the License at |
| 8 | |
| 9 | http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | |
| 11 | Unless required by applicable law or agreed to in writing, software |
| 12 | distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | See the License for the specific language governing permissions and |
| 15 | limitations under the License. |
| 16 | --]] |
| 17 | |
| 18 | local program = [[ |
| 19 | #include <uapi/linux/ptrace.h> |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 20 | #include <linux/sched.h> |
| 21 | |
| 22 | struct data_t { |
| 23 | u64 stack_id; |
| 24 | u32 pid; |
| 25 | char comm[TASK_COMM_LEN]; |
| 26 | }; |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 27 | |
| 28 | BPF_STACK_TRACE(stack_traces, 128) |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 29 | BPF_PERF_OUTPUT(events); |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 30 | |
| 31 | void trace_stack(struct pt_regs *ctx) { |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 32 | u32 pid = bpf_get_current_pid_tgid(); |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 33 | FILTER |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 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)); |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 39 | } |
| 40 | ]] |
| 41 | |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 42 | local ffi = require("ffi") |
| 43 | |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 44 | return 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 |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 57 | filter = "if (pid != %d) { return; }" % args.pid |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 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 |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 65 | print("Function \"%s\" not found. Exiting." % {args.fn}) |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 66 | return |
| 67 | end |
| 68 | |
| 69 | if args.verbose then |
| 70 | print("%-18s %-12s %-6s %-3s %s" % |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 71 | {"TIME(s)", "COMM", "PID", "CPU", "FUNCTION"}) |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 72 | else |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 73 | print("%-18s %s" % {"TIME(s)", "FUNCTION"}) |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 74 | end |
| 75 | |
| 76 | local stack_traces = bpf:get_table("stack_traces") |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 77 | local start_ts = utils.posix.time_ns() |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 78 | |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 79 | local function print_event(cpu, event) |
| 80 | local ts = (utils.posix.time_ns() - start_ts) / 1e9 |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 81 | |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 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)}) |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 93 | else |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 94 | print("\t%-16p %s" % {addr, sym}) |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 95 | end |
| 96 | end |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 97 | |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 98 | print() |
| 99 | end |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 100 | |
| 101 | local TASK_COMM_LEN = 16 -- linux/sched.h |
| 102 | |
| Vicent Marti | 973a528 | 2016-05-24 17:57:34 +0200 | [diff] [blame] | 103 | bpf:get_table("events"):open_perf_buffer(print_event, |
| 104 | "struct { uint64_t stack_id; uint32_t pid; char comm[$]; }", |
| Mark Drayton | 5f5687e | 2017-02-20 18:13:03 +0000 | [diff] [blame] | 105 | {TASK_COMM_LEN}) |
| Mark Drayton | 266d6f6 | 2016-05-24 07:01:01 -0700 | [diff] [blame] | 106 | bpf:kprobe_poll_loop() |
| Mark Drayton | 75291d0 | 2016-04-20 14:11:00 -0700 | [diff] [blame] | 107 | end |