1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2
3 /*
4 * sigsnoop Trace standard and real-time signals.
5 *
6 * Copyright (c) 2021~2022 Hengqi Chen
7 *
8 * 08-Aug-2021 Hengqi Chen Created this.
9 */
10 #include <argp.h>
11 #include <libgen.h>
12 #include <signal.h>
13 #include <time.h>
14
15 #include <bpf/bpf.h>
16 #include "sigsnoop.h"
17 #include "sigsnoop.skel.h"
18
19 #define PERF_BUFFER_PAGES 16
20 #define PERF_POLL_TIMEOUT_MS 100
21 #define warn(...) fprintf(stderr, __VA_ARGS__)
22 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
23
24 static volatile sig_atomic_t exiting = 0;
25
26 static pid_t target_pid = 0;
27 static int target_signal = 0;
28 static bool failed_only = false;
29 static bool kill_only = false;
30 static bool signal_name = false;
31 static bool verbose = false;
32
33 static const char *sig_name[] = {
34 [0] = "N/A",
35 [1] = "SIGHUP",
36 [2] = "SIGINT",
37 [3] = "SIGQUIT",
38 [4] = "SIGILL",
39 [5] = "SIGTRAP",
40 [6] = "SIGABRT",
41 [7] = "SIGBUS",
42 [8] = "SIGFPE",
43 [9] = "SIGKILL",
44 [10] = "SIGUSR1",
45 [11] = "SIGSEGV",
46 [12] = "SIGUSR2",
47 [13] = "SIGPIPE",
48 [14] = "SIGALRM",
49 [15] = "SIGTERM",
50 [16] = "SIGSTKFLT",
51 [17] = "SIGCHLD",
52 [18] = "SIGCONT",
53 [19] = "SIGSTOP",
54 [20] = "SIGTSTP",
55 [21] = "SIGTTIN",
56 [22] = "SIGTTOU",
57 [23] = "SIGURG",
58 [24] = "SIGXCPU",
59 [25] = "SIGXFSZ",
60 [26] = "SIGVTALRM",
61 [27] = "SIGPROF",
62 [28] = "SIGWINCH",
63 [29] = "SIGIO",
64 [30] = "SIGPWR",
65 [31] = "SIGSYS",
66 };
67
68 const char *argp_program_version = "sigsnoop 0.1";
69 const char *argp_program_bug_address =
70 "https://github.com/iovisor/bcc/tree/master/libbpf-tools";
71 const char argp_program_doc[] =
72 "Trace standard and real-time signals.\n"
73 "\n"
74 "USAGE: sigsnoop [-h] [-x] [-k] [-n] [-p PID] [-s SIGNAL]\n"
75 "\n"
76 "EXAMPLES:\n"
77 " sigsnoop # trace signals system-wide\n"
78 " sigsnoop -k # trace signals issued by kill syscall only\n"
79 " sigsnoop -x # trace failed signals only\n"
80 " sigsnoop -p 1216 # only trace PID 1216\n"
81 " sigsnoop -s 9 # only trace signal 9\n";
82
83 static const struct argp_option opts[] = {
84 { "failed", 'x', NULL, 0, "Trace failed signals only." },
85 { "kill", 'k', NULL, 0, "Trace signals issued by kill syscall only." },
86 { "pid", 'p', "PID", 0, "Process ID to trace" },
87 { "signal", 's', "SIGNAL", 0, "Signal to trace." },
88 { "name", 'n', NULL, 0, "Output signal name instead of signal number." },
89 { "verbose", 'v', NULL, 0, "Verbose debug output" },
90 { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
91 {},
92 };
93
parse_arg(int key,char * arg,struct argp_state * state)94 static error_t parse_arg(int key, char *arg, struct argp_state *state)
95 {
96 long pid, sig;
97
98 switch (key) {
99 case 'p':
100 errno = 0;
101 pid = strtol(arg, NULL, 10);
102 if (errno || pid <= 0) {
103 warn("Invalid PID: %s\n", arg);
104 argp_usage(state);
105 }
106 target_pid = pid;
107 break;
108 case 's':
109 errno = 0;
110 sig = strtol(arg, NULL, 10);
111 if (errno || sig <= 0) {
112 warn("Invalid SIGNAL: %s\n", arg);
113 argp_usage(state);
114 }
115 target_signal = sig;
116 break;
117 case 'n':
118 signal_name = true;
119 break;
120 case 'x':
121 failed_only = true;
122 break;
123 case 'k':
124 kill_only = true;
125 break;
126 case 'v':
127 verbose = true;
128 break;
129 case 'h':
130 argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
131 break;
132 default:
133 return ARGP_ERR_UNKNOWN;
134 }
135 return 0;
136 }
137
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)138 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
139 {
140 if (level == LIBBPF_DEBUG && !verbose)
141 return 0;
142 return vfprintf(stderr, format, args);
143 }
144
alias_parse(char * prog)145 static void alias_parse(char *prog)
146 {
147 char *name = basename(prog);
148
149 if (!strcmp(name, "killsnoop")) {
150 kill_only = true;
151 }
152 }
153
sig_int(int signo)154 static void sig_int(int signo)
155 {
156 exiting = 1;
157 }
158
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)159 static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
160 {
161 struct event *e = data;
162 struct tm *tm;
163 char ts[32];
164 time_t t;
165
166 time(&t);
167 tm = localtime(&t);
168 strftime(ts, sizeof(ts), "%H:%M:%S", tm);
169 if (signal_name && e->sig < ARRAY_SIZE(sig_name))
170 printf("%-8s %-7d %-16s %-9s %-7d %-6d\n",
171 ts, e->pid, e->comm, sig_name[e->sig], e->tpid, e->ret);
172 else
173 printf("%-8s %-7d %-16s %-9d %-7d %-6d\n",
174 ts, e->pid, e->comm, e->sig, e->tpid, e->ret);
175 }
176
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)177 static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
178 {
179 warn("lost %llu events on CPU #%d\n", lost_cnt, cpu);
180 }
181
main(int argc,char ** argv)182 int main(int argc, char **argv)
183 {
184 static const struct argp argp = {
185 .options = opts,
186 .parser = parse_arg,
187 .doc = argp_program_doc,
188 };
189 struct perf_buffer *pb = NULL;
190 struct sigsnoop_bpf *obj;
191 int err;
192
193 alias_parse(argv[0]);
194 err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
195 if (err)
196 return err;
197
198 libbpf_set_print(libbpf_print_fn);
199
200 obj = sigsnoop_bpf__open();
201 if (!obj) {
202 warn("failed to open BPF object\n");
203 return 1;
204 }
205
206 obj->rodata->filtered_pid = target_pid;
207 obj->rodata->target_signal = target_signal;
208 obj->rodata->failed_only = failed_only;
209
210 if (kill_only) {
211 bpf_program__set_autoload(obj->progs.sig_trace, false);
212 } else {
213 bpf_program__set_autoload(obj->progs.kill_entry, false);
214 bpf_program__set_autoload(obj->progs.kill_exit, false);
215 bpf_program__set_autoload(obj->progs.tkill_entry, false);
216 bpf_program__set_autoload(obj->progs.tkill_exit, false);
217 bpf_program__set_autoload(obj->progs.tgkill_entry, false);
218 bpf_program__set_autoload(obj->progs.tgkill_exit, false);
219 }
220
221 err = sigsnoop_bpf__load(obj);
222 if (err) {
223 warn("failed to load BPF object: %d\n", err);
224 goto cleanup;
225 }
226
227 err = sigsnoop_bpf__attach(obj);
228 if (err) {
229 warn("failed to attach BPF programs: %d\n", err);
230 goto cleanup;
231 }
232
233 pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
234 handle_event, handle_lost_events, NULL, NULL);
235 if (!pb) {
236 err = -errno;
237 warn("failed to open perf buffer: %d\n", err);
238 goto cleanup;
239 }
240
241 if (signal(SIGINT, sig_int) == SIG_ERR) {
242 warn("can't set signal handler: %s\n", strerror(errno));
243 goto cleanup;
244 }
245
246 printf("%-8s %-7s %-16s %-9s %-7s %-6s\n",
247 "TIME", "PID", "COMM", "SIG", "TPID", "RESULT");
248
249 while (!exiting) {
250 err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
251 if (err < 0 && err != -EINTR) {
252 warn("error polling perf buffer: %s\n", strerror(-err));
253 goto cleanup;
254 }
255 /* reset err to return 0 if exiting */
256 err = 0;
257 }
258
259 cleanup:
260 perf_buffer__free(pb);
261 sigsnoop_bpf__destroy(obj);
262
263 return err != 0;
264 }
265