1#!/usr/bin/env python 2# @lint-avoid-python-3-compatibility-imports 3# 4# killsnoop Trace signals issued by the kill() syscall. 5# For Linux, uses BCC, eBPF. Embedded C. 6# 7# USAGE: killsnoop [-h] [-x] [-p PID] [-T PID] [-s SIGNAL] 8# 9# Copyright (c) 2015 Brendan Gregg. 10# Licensed under the Apache License, Version 2.0 (the "License") 11# 12# 20-Sep-2015 Brendan Gregg Created this. 13# 19-Feb-2016 Allan McAleavy migrated to BPF_PERF_OUTPUT 14 15from __future__ import print_function 16from bcc import BPF 17from bcc.utils import ArgString, printb 18import argparse 19from time import strftime 20 21# arguments 22examples = """examples: 23 ./killsnoop # trace all kill() signals 24 ./killsnoop -x # only show failed kills 25 ./killsnoop -p 181 # only trace PID 181 26 ./killsnoop -T 189 # only trace target PID 189 27 ./killsnoop -s 9 # only trace signal 9 28""" 29parser = argparse.ArgumentParser( 30 description="Trace signals issued by the kill() syscall", 31 formatter_class=argparse.RawDescriptionHelpFormatter, 32 epilog=examples) 33parser.add_argument("-x", "--failed", action="store_true", 34 help="only show failed kill syscalls") 35parser.add_argument("-p", "--pid", 36 help="trace this PID only which is the sender of signal") 37parser.add_argument("-T", "--tpid", 38 help="trace this target PID only which is the receiver of signal") 39parser.add_argument("-s", "--signal", 40 help="trace this signal only") 41parser.add_argument("--ebpf", action="store_true", 42 help=argparse.SUPPRESS) 43args = parser.parse_args() 44debug = 0 45 46# define BPF program 47bpf_text = """ 48#include <uapi/linux/ptrace.h> 49#include <linux/sched.h> 50 51struct val_t { 52 u32 pid; 53 int sig; 54 int tpid; 55 char comm[TASK_COMM_LEN]; 56}; 57 58struct data_t { 59 u32 pid; 60 int tpid; 61 int sig; 62 int ret; 63 char comm[TASK_COMM_LEN]; 64}; 65 66BPF_HASH(infotmp, u32, struct val_t); 67BPF_PERF_OUTPUT(events); 68 69int syscall__kill(struct pt_regs *ctx, int tpid, int sig) 70{ 71 u64 pid_tgid = bpf_get_current_pid_tgid(); 72 u32 pid = pid_tgid >> 32; 73 u32 tid = (u32)pid_tgid; 74 75 TPID_FILTER 76 PID_FILTER 77 SIGNAL_FILTER 78 79 struct val_t val = {.pid = pid}; 80 if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) == 0) { 81 val.tpid = tpid; 82 val.sig = sig; 83 infotmp.update(&tid, &val); 84 } 85 86 return 0; 87}; 88 89int do_ret_sys_kill(struct pt_regs *ctx) 90{ 91 struct data_t data = {}; 92 struct val_t *valp; 93 u64 pid_tgid = bpf_get_current_pid_tgid(); 94 u32 pid = pid_tgid >> 32; 95 u32 tid = (u32)pid_tgid; 96 97 valp = infotmp.lookup(&tid); 98 if (valp == 0) { 99 // missed entry 100 return 0; 101 } 102 103 bpf_probe_read_kernel(&data.comm, sizeof(data.comm), valp->comm); 104 data.pid = pid; 105 data.tpid = valp->tpid; 106 data.ret = PT_REGS_RC(ctx); 107 data.sig = valp->sig; 108 109 events.perf_submit(ctx, &data, sizeof(data)); 110 infotmp.delete(&tid); 111 112 return 0; 113} 114""" 115 116if args.tpid: 117 bpf_text = bpf_text.replace('TPID_FILTER', 118 'if (tpid != %s) { return 0; }' % args.tpid) 119else: 120 bpf_text = bpf_text.replace('TPID_FILTER', '') 121 122if args.pid: 123 bpf_text = bpf_text.replace('PID_FILTER', 124 'if (pid != %s) { return 0; }' % args.pid) 125else: 126 bpf_text = bpf_text.replace('PID_FILTER', '') 127 128if args.signal: 129 bpf_text = bpf_text.replace('SIGNAL_FILTER', 130 'if (sig != %s) { return 0; }' % args.signal) 131else: 132 bpf_text = bpf_text.replace('SIGNAL_FILTER', '') 133 134if debug or args.ebpf: 135 print(bpf_text) 136 if args.ebpf: 137 exit() 138 139# initialize BPF 140b = BPF(text=bpf_text) 141kill_fnname = b.get_syscall_fnname("kill") 142b.attach_kprobe(event=kill_fnname, fn_name="syscall__kill") 143b.attach_kretprobe(event=kill_fnname, fn_name="do_ret_sys_kill") 144 145# detect the length of PID column 146pid_bytes = 6 147try: 148 with open("/proc/sys/kernel/pid_max", 'r') as f: 149 pid_bytes = len(f.read().strip()) + 1 150 f.close() 151except: 152 pass # not fatal error, just use the default value 153 154# header 155print("%-9s %-*s %-16s %-4s %-*s %s" % ( 156 "TIME", pid_bytes, "PID", "COMM", "SIG", pid_bytes, "TPID", "RESULT")) 157 158# process event 159def print_event(cpu, data, size): 160 event = b["events"].event(data) 161 162 if (args.failed and (event.ret >= 0)): 163 return 164 165 printb(b"%-9s %-*d %-16s %-4d %-*d %d" % (strftime("%H:%M:%S").encode('ascii'), 166 pid_bytes, event.pid, event.comm, event.sig, pid_bytes, event.tpid, event.ret)) 167 168# loop with callback to print_event 169b["events"].open_perf_buffer(print_event) 170while 1: 171 try: 172 b.perf_buffer_poll() 173 except KeyboardInterrupt: 174 exit() 175