1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2022 Jingxiang Zeng
3 //
4 // Based on oomkill(8) from BCC by Brendan Gregg.
5 // 13-Jan-2022 Jingxiang Zeng Created this.
6 #include <argp.h>
7 #include <errno.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
13 #include <unistd.h>
14
15 #include <bpf/bpf.h>
16 #include <bpf/libbpf.h>
17 #include "oomkill.skel.h"
18 #include "oomkill.h"
19 #include "trace_helpers.h"
20
21 #define PERF_POLL_TIMEOUT_MS 100
22
23 static volatile sig_atomic_t exiting = 0;
24
25 static bool verbose = false;
26
27 const char *argp_program_version = "oomkill 0.1";
28 const char *argp_program_bug_address =
29 "https://github.com/iovisor/bcc/tree/master/libbpf-tools";
30 const char argp_program_doc[] =
31 "Trace OOM kills.\n"
32 "\n"
33 "USAGE: oomkill [-h]\n"
34 "\n"
35 "EXAMPLES:\n"
36 " oomkill # trace OOM kills\n";
37
38 static const struct argp_option opts[] = {
39 { "verbose", 'v', NULL, 0, "Verbose debug output" },
40 { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
41 {},
42 };
43
parse_arg(int key,char * arg,struct argp_state * state)44 static error_t parse_arg(int key, char *arg, struct argp_state *state)
45 {
46 switch (key) {
47 case 'v':
48 verbose = true;
49 break;
50 case 'h':
51 argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
52 break;
53 default:
54 return ARGP_ERR_UNKNOWN;
55 }
56 return 0;
57 }
58
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)59 static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
60 {
61 FILE *f;
62 char buf[256];
63 int n = 0;
64 struct tm *tm;
65 char ts[32];
66 time_t t;
67 struct data_t *e = data;
68
69 f = fopen("/proc/loadavg", "r");
70 if (f) {
71 memset(buf, 0, sizeof(buf));
72 n = fread(buf, 1, sizeof(buf), f);
73 fclose(f);
74 }
75 time(&t);
76 tm = localtime(&t);
77 strftime(ts, sizeof(ts), "%H:%M:%S", tm);
78
79 if (n)
80 printf("%s Triggered by PID %d (\"%s\"), OOM kill of PID %d (\"%s\"), %lld pages, loadavg: %s\n",
81 ts, e->fpid, e->fcomm, e->tpid, e->tcomm, e->pages, buf);
82 else
83 printf("%s Triggered by PID %d (\"%s\"), OOM kill of PID %d (\"%s\"), %lld pages\n",
84 ts, e->fpid, e->fcomm, e->tpid, e->tcomm, e->pages);
85 }
86
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)87 static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
88 {
89 printf("Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
90 }
91
sig_int(int signo)92 static void sig_int(int signo)
93 {
94 exiting = 1;
95 }
96
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)97 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
98 {
99 if (level == LIBBPF_DEBUG && !verbose)
100 return 0;
101 return vfprintf(stderr, format, args);
102 }
103
main(int argc,char ** argv)104 int main(int argc, char **argv)
105 {
106 static const struct argp argp = {
107 .options = opts,
108 .parser = parse_arg,
109 .doc = argp_program_doc,
110 };
111 struct perf_buffer *pb = NULL;
112 struct oomkill_bpf *obj;
113 int err;
114
115 err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
116 if (err)
117 return err;
118
119 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
120 libbpf_set_print(libbpf_print_fn);
121
122 obj = oomkill_bpf__open_and_load();
123 if (!obj) {
124 fprintf(stderr, "failed to load and open BPF object\n");
125 return 1;
126 }
127
128 err = oomkill_bpf__attach(obj);
129 if (err) {
130 fprintf(stderr, "failed to attach BPF programs\n");
131 goto cleanup;
132 }
133
134 pb = perf_buffer__new(bpf_map__fd(obj->maps.events), 64,
135 handle_event, handle_lost_events, NULL, NULL);
136 if (!pb) {
137 err = -errno;
138 fprintf(stderr, "failed to open perf buffer: %d\n", err);
139 goto cleanup;
140 }
141
142 if (signal(SIGINT, sig_int) == SIG_ERR) {
143 fprintf(stderr, "can't set signal handler: %d\n", err);
144 err = 1;
145 goto cleanup;
146 }
147
148 printf("Tracing OOM kills... Ctrl-C to stop.\n");
149
150 while (!exiting) {
151 err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
152 if (err < 0 && err != -EINTR) {
153 fprintf(stderr, "error polling perf buffer: %d\n", err);
154 goto cleanup;
155 }
156 /* reset err to return 0 if exiting */
157 err = 0;
158 }
159
160 cleanup:
161 perf_buffer__free(pb);
162 oomkill_bpf__destroy(obj);
163
164 return err != 0;
165 }
166