blob: bd5c0c9ca14614de007ff81db37afada5528995b [file] [log] [blame]
Brendan Gregg38cef482016-01-15 17:26:30 -08001#!/usr/bin/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# The current implementation uses an unrolled loop for x86_64, and was written
9# as a proof of concept. This implementation should be replaced in the future
10# with an appropriate bpf_ call, when available.
11#
12# The stack depth is limited to 10 (+1 for the current instruction pointer).
13# This could be tunable in a future version.
14#
15# Copyright 2016 Netflix, Inc.
16# Licensed under the Apache License, Version 2.0 (the "License")
17#
18# 12-Jan-2016 Brendan Gregg Created this.
19
20from __future__ import print_function
21from bcc import BPF
22import argparse
23
24# arguments
25examples = """examples:
26 ./stacksnoop ext4_sync_fs # print kernel stack traces for ext4_sync_fs
27 ./stacksnoop -s ext4_sync_fs # ... also show symbol offsets
28 ./stacksnoop -v ext4_sync_fs # ... show extra columns
29 ./stacksnoop -p 185 ext4_sync_fs # ... only when PID 185 is on-CPU
30"""
31parser = argparse.ArgumentParser(
32 description="Trace and print kernel stack traces for a kernel function",
33 formatter_class=argparse.RawDescriptionHelpFormatter,
34 epilog=examples)
35parser.add_argument("-p", "--pid",
36 help="trace this PID only")
37parser.add_argument("-s", "--offset", action="store_true",
38 help="show address offsets")
39parser.add_argument("-v", "--verbose", action="store_true",
40 help="print more fields")
41parser.add_argument("function",
42 help="kernel function name")
43args = parser.parse_args()
44function = args.function
45offset = args.offset
46verbose = args.verbose
47debug = 0
48
49# define BPF program
50bpf_text = """
51#include <uapi/linux/ptrace.h>
52
53static int print_frame(u64 *bp, int *depth) {
54 if (*bp) {
55 // The following stack walker is x86_64 specific
56 u64 ret = 0;
57 if (bpf_probe_read(&ret, sizeof(ret), (void *)(*bp+8)))
58 return 0;
59 if (!ret || ret < __START_KERNEL_map)
60 return 0;
61 bpf_trace_printk("r%d: %llx\\n", *depth, ret);
62 if (bpf_probe_read(bp, sizeof(*bp), (void *)*bp))
63 return 0;
64 *depth += 1;
65 return 1;
66 }
67 return 0;
68}
69
70void trace_stack(struct pt_regs *ctx) {
71 FILTER
72 u64 bp = 0;
73 int depth = 0;
74
75 bpf_trace_printk("\\n");
76 if (ctx->ip)
77 bpf_trace_printk("ip: %llx\\n", ctx->ip);
78 bp = ctx->bp;
79
80 // unrolled loop, 10 frames deep:
81 if (!print_frame(&bp, &depth)) return;
82 if (!print_frame(&bp, &depth)) return;
83 if (!print_frame(&bp, &depth)) return;
84 if (!print_frame(&bp, &depth)) return;
85 if (!print_frame(&bp, &depth)) return;
86 if (!print_frame(&bp, &depth)) return;
87 if (!print_frame(&bp, &depth)) return;
88 if (!print_frame(&bp, &depth)) return;
89 if (!print_frame(&bp, &depth)) return;
90 if (!print_frame(&bp, &depth)) return;
91};
92"""
93if args.pid:
94 bpf_text = bpf_text.replace('FILTER',
95 ('u32 pid; pid = bpf_get_current_pid_tgid(); ' +
96 'if (pid != %s) { return 0; }') % (args.pid))
97else:
98 bpf_text = bpf_text.replace('FILTER', '')
99if debug:
100 print(bpf_text)
101
102# initialize BPF
103b = BPF(text=bpf_text)
104b.attach_kprobe(event=function, fn_name="trace_stack")
105matched = b.num_open_kprobes()
106if matched == 0:
107 print("Function \"%s\" not found. Exiting." % function)
108 exit()
109
110# header
111if verbose:
112 print("%-18s %-12s %-6s %-3s %s" % ("TIME(s)", "COMM", "PID", "CPU",
113 "STACK"))
114else:
115 print("%-18s %s" % ("TIME(s)", "STACK"))
116
117# format output
118while 1:
119 (task, pid, cpu, flags, ts, msg) = b.trace_fields()
120 if msg != "":
121 (reg, addr) = msg.split(" ")
122 if offset:
123 ip = b.ksymaddr(int(addr, 16))
124 else:
125 ip = b.ksym(int(addr, 16))
126 msg = msg + " " + ip
127 if verbose:
128 print("%-18.9f %-12.12s %-6d %-3d %s" % (ts, task, pid, cpu, msg))
129 else:
130 print("%-18.9f %s" % (ts, msg))