• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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