blob: 3cd70621114ecef5c389242a80ad3056499b3831 [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
Mark Drayton266d6f62016-05-24 07:01:01 -070023import ctypes as ct
24import time
Brendan Gregg38cef482016-01-15 17:26:30 -080025
26# arguments
27examples = """examples:
28 ./stacksnoop ext4_sync_fs # print kernel stack traces for ext4_sync_fs
29 ./stacksnoop -s ext4_sync_fs # ... also show symbol offsets
30 ./stacksnoop -v ext4_sync_fs # ... show extra columns
31 ./stacksnoop -p 185 ext4_sync_fs # ... only when PID 185 is on-CPU
32"""
33parser = argparse.ArgumentParser(
34 description="Trace and print kernel stack traces for a kernel function",
35 formatter_class=argparse.RawDescriptionHelpFormatter,
36 epilog=examples)
37parser.add_argument("-p", "--pid",
38 help="trace this PID only")
39parser.add_argument("-s", "--offset", action="store_true",
40 help="show address offsets")
41parser.add_argument("-v", "--verbose", action="store_true",
42 help="print more fields")
43parser.add_argument("function",
44 help="kernel function name")
45args = parser.parse_args()
46function = args.function
47offset = args.offset
48verbose = args.verbose
49debug = 0
50
51# define BPF program
52bpf_text = """
53#include <uapi/linux/ptrace.h>
Mark Drayton266d6f62016-05-24 07:01:01 -070054#include <linux/sched.h>
55
56struct data_t {
57 u64 stack_id;
58 u32 pid;
59 char comm[TASK_COMM_LEN];
60};
Brendan Gregg38cef482016-01-15 17:26:30 -080061
Vicent Marti592414e2016-03-27 18:22:03 +020062BPF_STACK_TRACE(stack_traces, 128)
Mark Drayton266d6f62016-05-24 07:01:01 -070063BPF_PERF_OUTPUT(events);
Brendan Gregg38cef482016-01-15 17:26:30 -080064
65void trace_stack(struct pt_regs *ctx) {
Mark Drayton266d6f62016-05-24 07:01:01 -070066 u32 pid = bpf_get_current_pid_tgid();
Brendan Gregg38cef482016-01-15 17:26:30 -080067 FILTER
Mark Drayton266d6f62016-05-24 07:01:01 -070068 struct data_t data = {};
69 data.stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID),
70 data.pid = pid;
71 bpf_get_current_comm(&data.comm, sizeof(data.comm));
72 events.perf_submit(ctx, &data, sizeof(data));
Vicent Marti592414e2016-03-27 18:22:03 +020073}
Brendan Gregg38cef482016-01-15 17:26:30 -080074"""
75if args.pid:
76 bpf_text = bpf_text.replace('FILTER',
Mark Drayton266d6f62016-05-24 07:01:01 -070077 'if (pid != %s) { return; }' % args.pid)
Brendan Gregg38cef482016-01-15 17:26:30 -080078else:
79 bpf_text = bpf_text.replace('FILTER', '')
80if debug:
81 print(bpf_text)
82
83# initialize BPF
84b = BPF(text=bpf_text)
85b.attach_kprobe(event=function, fn_name="trace_stack")
Mark Drayton266d6f62016-05-24 07:01:01 -070086
87TASK_COMM_LEN = 16 # linux/sched.h
88
89class Data(ct.Structure):
90 _fields_ = [
91 ("stack_id", ct.c_ulonglong),
92 ("pid", ct.c_uint),
93 ("comm", ct.c_char * TASK_COMM_LEN),
94 ]
95
Brendan Gregg38cef482016-01-15 17:26:30 -080096matched = b.num_open_kprobes()
97if matched == 0:
98 print("Function \"%s\" not found. Exiting." % function)
99 exit()
100
Vicent Marti592414e2016-03-27 18:22:03 +0200101stack_traces = b.get_table("stack_traces")
Mark Drayton266d6f62016-05-24 07:01:01 -0700102start_ts = time.time()
Vicent Marti592414e2016-03-27 18:22:03 +0200103
Brendan Gregg38cef482016-01-15 17:26:30 -0800104# header
105if verbose:
Mark Drayton266d6f62016-05-24 07:01:01 -0700106 print("%-18s %-12s %-6s %-3s %s" %
107 ("TIME(s)", "COMM", "PID", "CPU", "FUNCTION"))
Brendan Gregg38cef482016-01-15 17:26:30 -0800108else:
Mark Drayton266d6f62016-05-24 07:01:01 -0700109 print("%-18s %s" % ("TIME(s)", "FUNCTION"))
Brendan Gregg38cef482016-01-15 17:26:30 -0800110
Mark Drayton266d6f62016-05-24 07:01:01 -0700111def print_event(cpu, data, size):
112 event = ct.cast(data, ct.POINTER(Data)).contents
113
114 ts = time.time() - start_ts
115
116 if verbose:
Sasha Goldshteinf41ae862016-10-19 01:14:30 +0300117 print("%-18.9f %-12.12s %-6d %-3d %s" %
118 (ts, event.comm, event.pid, cpu, function))
Mark Drayton266d6f62016-05-24 07:01:01 -0700119 else:
120 print("%-18.9f %s" % (ts, function))
121
122 for addr in stack_traces.walk(event.stack_id):
Sasha Goldshtein01553852017-02-09 03:58:09 -0500123 sym = b.ksym(addr, show_offset=offset)
Sasha Goldshtein2bf3ff32017-02-08 23:25:00 -0500124 print("\t%s" % sym)
Mark Drayton266d6f62016-05-24 07:01:01 -0700125
126 print()
127
128b["events"].open_perf_buffer(print_event)
Brendan Gregg38cef482016-01-15 17:26:30 -0800129while 1:
Mark Drayton266d6f62016-05-24 07:01:01 -0700130 b.kprobe_poll()