Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  1/* SPDX-License-Identifier: GPL-2.0
  2 * Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc.
  3 */
  4static const char *__doc__=
  5 "XDP monitor tool, based on tracepoints\n"
  6;
  7
  8static const char *__doc_err_only__=
  9 " NOTICE: Only tracking XDP redirect errors\n"
 10 "         Enable TX success stats via '--stats'\n"
 11 "         (which comes with a per packet processing overhead)\n"
 12;
 13
 14#include <errno.h>
 15#include <stdio.h>
 16#include <stdlib.h>
 17#include <stdbool.h>
 18#include <stdint.h>
 19#include <string.h>
 20#include <ctype.h>
 21#include <unistd.h>
 22#include <locale.h>
 23
 24#include <sys/resource.h>
 25#include <getopt.h>
 26#include <net/if.h>
 27#include <time.h>
 28
 29#include <signal.h>
 30#include <bpf/bpf.h>
 31#include <bpf/libbpf.h>
 32#include "bpf_util.h"
 33
 34enum map_type {
 35	REDIRECT_ERR_CNT,
 36	EXCEPTION_CNT,
 37	CPUMAP_ENQUEUE_CNT,
 38	CPUMAP_KTHREAD_CNT,
 39	DEVMAP_XMIT_CNT,
 40};
 41
 42static const char *const map_type_strings[] = {
 43	[REDIRECT_ERR_CNT] = "redirect_err_cnt",
 44	[EXCEPTION_CNT] = "exception_cnt",
 45	[CPUMAP_ENQUEUE_CNT] = "cpumap_enqueue_cnt",
 46	[CPUMAP_KTHREAD_CNT] = "cpumap_kthread_cnt",
 47	[DEVMAP_XMIT_CNT] = "devmap_xmit_cnt",
 48};
 49
 50#define NUM_MAP 5
 51#define NUM_TP 8
 52
 53static int tp_cnt;
 54static int map_cnt;
 55static int verbose = 1;
 56static bool debug = false;
 57struct bpf_map *map_data[NUM_MAP] = {};
 58struct bpf_link *tp_links[NUM_TP] = {};
 59struct bpf_object *obj;
 60
 61static const struct option long_options[] = {
 62	{"help",	no_argument,		NULL, 'h' },
 63	{"debug",	no_argument,		NULL, 'D' },
 64	{"stats",	no_argument,		NULL, 'S' },
 65	{"sec", 	required_argument,	NULL, 's' },
 66	{0, 0, NULL,  0 }
 67};
 68
 69static void int_exit(int sig)
 70{
 71	/* Detach tracepoints */
 72	while (tp_cnt)
 73		bpf_link__destroy(tp_links[--tp_cnt]);
 74
 75	bpf_object__close(obj);
 76	exit(0);
 77}
 78
 79/* C standard specifies two constants, EXIT_SUCCESS(0) and EXIT_FAILURE(1) */
 80#define EXIT_FAIL_MEM	5
 81
 82static void usage(char *argv[])
 83{
 84	int i;
 85	printf("\nDOCUMENTATION:\n%s\n", __doc__);
 86	printf("\n");
 87	printf(" Usage: %s (options-see-below)\n",
 88	       argv[0]);
 89	printf(" Listing options:\n");
 90	for (i = 0; long_options[i].name != 0; i++) {
 91		printf(" --%-15s", long_options[i].name);
 92		if (long_options[i].flag != NULL)
 93			printf(" flag (internal value:%d)",
 94			       *long_options[i].flag);
 95		else
 96			printf("short-option: -%c",
 97			       long_options[i].val);
 98		printf("\n");
 99	}
100	printf("\n");
101}
102
103#define NANOSEC_PER_SEC 1000000000 /* 10^9 */
104static __u64 gettime(void)
105{
106	struct timespec t;
107	int res;
108
109	res = clock_gettime(CLOCK_MONOTONIC, &t);
110	if (res < 0) {
111		fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
112		exit(EXIT_FAILURE);
113	}
114	return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
115}
116
117enum {
118	REDIR_SUCCESS = 0,
119	REDIR_ERROR = 1,
120};
121#define REDIR_RES_MAX 2
122static const char *redir_names[REDIR_RES_MAX] = {
123	[REDIR_SUCCESS]	= "Success",
124	[REDIR_ERROR]	= "Error",
125};
126static const char *err2str(int err)
127{
128	if (err < REDIR_RES_MAX)
129		return redir_names[err];
130	return NULL;
131}
132/* enum xdp_action */
133#define XDP_UNKNOWN	XDP_REDIRECT + 1
134#define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
135static const char *xdp_action_names[XDP_ACTION_MAX] = {
136	[XDP_ABORTED]	= "XDP_ABORTED",
137	[XDP_DROP]	= "XDP_DROP",
138	[XDP_PASS]	= "XDP_PASS",
139	[XDP_TX]	= "XDP_TX",
140	[XDP_REDIRECT]	= "XDP_REDIRECT",
141	[XDP_UNKNOWN]	= "XDP_UNKNOWN",
142};
143static const char *action2str(int action)
144{
145	if (action < XDP_ACTION_MAX)
146		return xdp_action_names[action];
147	return NULL;
148}
149
150/* Common stats data record shared with _kern.c */
151struct datarec {
152	__u64 processed;
153	__u64 dropped;
154	__u64 info;
155	__u64 err;
156};
157#define MAX_CPUS 64
158
159/* Userspace structs for collection of stats from maps */
160struct record {
161	__u64 timestamp;
162	struct datarec total;
163	struct datarec *cpu;
164};
165struct u64rec {
166	__u64 processed;
167};
168struct record_u64 {
169	/* record for _kern side __u64 values */
170	__u64 timestamp;
171	struct u64rec total;
172	struct u64rec *cpu;
173};
174
175struct stats_record {
176	struct record_u64 xdp_redirect[REDIR_RES_MAX];
177	struct record_u64 xdp_exception[XDP_ACTION_MAX];
178	struct record xdp_cpumap_kthread;
179	struct record xdp_cpumap_enqueue[MAX_CPUS];
180	struct record xdp_devmap_xmit;
181};
182
183static bool map_collect_record(int fd, __u32 key, struct record *rec)
184{
185	/* For percpu maps, userspace gets a value per possible CPU */
186	unsigned int nr_cpus = bpf_num_possible_cpus();
187	struct datarec values[nr_cpus];
188	__u64 sum_processed = 0;
189	__u64 sum_dropped = 0;
190	__u64 sum_info = 0;
191	__u64 sum_err = 0;
192	int i;
193
194	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
195		fprintf(stderr,
196			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
197		return false;
198	}
199	/* Get time as close as possible to reading map contents */
200	rec->timestamp = gettime();
201
202	/* Record and sum values from each CPU */
203	for (i = 0; i < nr_cpus; i++) {
204		rec->cpu[i].processed = values[i].processed;
205		sum_processed        += values[i].processed;
206		rec->cpu[i].dropped = values[i].dropped;
207		sum_dropped        += values[i].dropped;
208		rec->cpu[i].info = values[i].info;
209		sum_info        += values[i].info;
210		rec->cpu[i].err = values[i].err;
211		sum_err        += values[i].err;
212	}
213	rec->total.processed = sum_processed;
214	rec->total.dropped   = sum_dropped;
215	rec->total.info      = sum_info;
216	rec->total.err       = sum_err;
217	return true;
218}
219
220static bool map_collect_record_u64(int fd, __u32 key, struct record_u64 *rec)
221{
222	/* For percpu maps, userspace gets a value per possible CPU */
223	unsigned int nr_cpus = bpf_num_possible_cpus();
224	struct u64rec values[nr_cpus];
225	__u64 sum_total = 0;
226	int i;
227
228	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
229		fprintf(stderr,
230			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
231		return false;
232	}
233	/* Get time as close as possible to reading map contents */
234	rec->timestamp = gettime();
235
236	/* Record and sum values from each CPU */
237	for (i = 0; i < nr_cpus; i++) {
238		rec->cpu[i].processed = values[i].processed;
239		sum_total            += values[i].processed;
240	}
241	rec->total.processed = sum_total;
242	return true;
243}
244
245static double calc_period(struct record *r, struct record *p)
246{
247	double period_ = 0;
248	__u64 period = 0;
249
250	period = r->timestamp - p->timestamp;
251	if (period > 0)
252		period_ = ((double) period / NANOSEC_PER_SEC);
253
254	return period_;
255}
256
257static double calc_period_u64(struct record_u64 *r, struct record_u64 *p)
258{
259	double period_ = 0;
260	__u64 period = 0;
261
262	period = r->timestamp - p->timestamp;
263	if (period > 0)
264		period_ = ((double) period / NANOSEC_PER_SEC);
265
266	return period_;
267}
268
269static double calc_pps(struct datarec *r, struct datarec *p, double period)
270{
271	__u64 packets = 0;
272	double pps = 0;
273
274	if (period > 0) {
275		packets = r->processed - p->processed;
276		pps = packets / period;
277	}
278	return pps;
279}
280
281static double calc_pps_u64(struct u64rec *r, struct u64rec *p, double period)
282{
283	__u64 packets = 0;
284	double pps = 0;
285
286	if (period > 0) {
287		packets = r->processed - p->processed;
288		pps = packets / period;
289	}
290	return pps;
291}
292
293static double calc_drop(struct datarec *r, struct datarec *p, double period)
294{
295	__u64 packets = 0;
296	double pps = 0;
297
298	if (period > 0) {
299		packets = r->dropped - p->dropped;
300		pps = packets / period;
301	}
302	return pps;
303}
304
305static double calc_info(struct datarec *r, struct datarec *p, double period)
306{
307	__u64 packets = 0;
308	double pps = 0;
309
310	if (period > 0) {
311		packets = r->info - p->info;
312		pps = packets / period;
313	}
314	return pps;
315}
316
317static double calc_err(struct datarec *r, struct datarec *p, double period)
318{
319	__u64 packets = 0;
320	double pps = 0;
321
322	if (period > 0) {
323		packets = r->err - p->err;
324		pps = packets / period;
325	}
326	return pps;
327}
328
329static void stats_print(struct stats_record *stats_rec,
330			struct stats_record *stats_prev,
331			bool err_only)
332{
333	unsigned int nr_cpus = bpf_num_possible_cpus();
334	int rec_i = 0, i, to_cpu;
335	double t = 0, pps = 0;
336
337	/* Header */
338	printf("%-15s %-7s %-12s %-12s %-9s\n",
339	       "XDP-event", "CPU:to", "pps", "drop-pps", "extra-info");
340
341	/* tracepoint: xdp:xdp_redirect_* */
342	if (err_only)
343		rec_i = REDIR_ERROR;
344
345	for (; rec_i < REDIR_RES_MAX; rec_i++) {
346		struct record_u64 *rec, *prev;
347		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
348		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
349
350		rec  =  &stats_rec->xdp_redirect[rec_i];
351		prev = &stats_prev->xdp_redirect[rec_i];
352		t = calc_period_u64(rec, prev);
353
354		for (i = 0; i < nr_cpus; i++) {
355			struct u64rec *r = &rec->cpu[i];
356			struct u64rec *p = &prev->cpu[i];
357
358			pps = calc_pps_u64(r, p, t);
359			if (pps > 0)
360				printf(fmt1, "XDP_REDIRECT", i,
361				       rec_i ? 0.0: pps, rec_i ? pps : 0.0,
362				       err2str(rec_i));
363		}
364		pps = calc_pps_u64(&rec->total, &prev->total, t);
365		printf(fmt2, "XDP_REDIRECT", "total",
366		       rec_i ? 0.0: pps, rec_i ? pps : 0.0, err2str(rec_i));
367	}
368
369	/* tracepoint: xdp:xdp_exception */
370	for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) {
371		struct record_u64 *rec, *prev;
372		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
373		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
374
375		rec  =  &stats_rec->xdp_exception[rec_i];
376		prev = &stats_prev->xdp_exception[rec_i];
377		t = calc_period_u64(rec, prev);
378
379		for (i = 0; i < nr_cpus; i++) {
380			struct u64rec *r = &rec->cpu[i];
381			struct u64rec *p = &prev->cpu[i];
382
383			pps = calc_pps_u64(r, p, t);
384			if (pps > 0)
385				printf(fmt1, "Exception", i,
386				       0.0, pps, action2str(rec_i));
387		}
388		pps = calc_pps_u64(&rec->total, &prev->total, t);
389		if (pps > 0)
390			printf(fmt2, "Exception", "total",
391			       0.0, pps, action2str(rec_i));
392	}
393
394	/* cpumap enqueue stats */
395	for (to_cpu = 0; to_cpu < MAX_CPUS; to_cpu++) {
396		char *fmt1 = "%-15s %3d:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
397		char *fmt2 = "%-15s %3s:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
398		struct record *rec, *prev;
399		char *info_str = "";
400		double drop, info;
401
402		rec  =  &stats_rec->xdp_cpumap_enqueue[to_cpu];
403		prev = &stats_prev->xdp_cpumap_enqueue[to_cpu];
404		t = calc_period(rec, prev);
405		for (i = 0; i < nr_cpus; i++) {
406			struct datarec *r = &rec->cpu[i];
407			struct datarec *p = &prev->cpu[i];
408
409			pps  = calc_pps(r, p, t);
410			drop = calc_drop(r, p, t);
411			info = calc_info(r, p, t);
412			if (info > 0) {
413				info_str = "bulk-average";
414				info = pps / info; /* calc average bulk size */
415			}
416			if (pps > 0)
417				printf(fmt1, "cpumap-enqueue",
418				       i, to_cpu, pps, drop, info, info_str);
419		}
420		pps = calc_pps(&rec->total, &prev->total, t);
421		if (pps > 0) {
422			drop = calc_drop(&rec->total, &prev->total, t);
423			info = calc_info(&rec->total, &prev->total, t);
424			if (info > 0) {
425				info_str = "bulk-average";
426				info = pps / info; /* calc average bulk size */
427			}
428			printf(fmt2, "cpumap-enqueue",
429			       "sum", to_cpu, pps, drop, info, info_str);
430		}
431	}
432
433	/* cpumap kthread stats */
434	{
435		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.0f %s\n";
436		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.0f %s\n";
437		struct record *rec, *prev;
438		double drop, info;
439		char *i_str = "";
440
441		rec  =  &stats_rec->xdp_cpumap_kthread;
442		prev = &stats_prev->xdp_cpumap_kthread;
443		t = calc_period(rec, prev);
444		for (i = 0; i < nr_cpus; i++) {
445			struct datarec *r = &rec->cpu[i];
446			struct datarec *p = &prev->cpu[i];
447
448			pps  = calc_pps(r, p, t);
449			drop = calc_drop(r, p, t);
450			info = calc_info(r, p, t);
451			if (info > 0)
452				i_str = "sched";
453			if (pps > 0 || drop > 0)
454				printf(fmt1, "cpumap-kthread",
455				       i, pps, drop, info, i_str);
456		}
457		pps = calc_pps(&rec->total, &prev->total, t);
458		drop = calc_drop(&rec->total, &prev->total, t);
459		info = calc_info(&rec->total, &prev->total, t);
460		if (info > 0)
461			i_str = "sched-sum";
462		printf(fmt2, "cpumap-kthread", "total", pps, drop, info, i_str);
463	}
464
465	/* devmap ndo_xdp_xmit stats */
466	{
467		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.2f %s %s\n";
468		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.2f %s %s\n";
469		struct record *rec, *prev;
470		double drop, info, err;
471		char *i_str = "";
472		char *err_str = "";
473
474		rec  =  &stats_rec->xdp_devmap_xmit;
475		prev = &stats_prev->xdp_devmap_xmit;
476		t = calc_period(rec, prev);
477		for (i = 0; i < nr_cpus; i++) {
478			struct datarec *r = &rec->cpu[i];
479			struct datarec *p = &prev->cpu[i];
480
481			pps  = calc_pps(r, p, t);
482			drop = calc_drop(r, p, t);
483			info = calc_info(r, p, t);
484			err  = calc_err(r, p, t);
485			if (info > 0) {
486				i_str = "bulk-average";
487				info = (pps+drop) / info; /* calc avg bulk */
488			}
489			if (err > 0)
490				err_str = "drv-err";
491			if (pps > 0 || drop > 0)
492				printf(fmt1, "devmap-xmit",
493				       i, pps, drop, info, i_str, err_str);
494		}
495		pps = calc_pps(&rec->total, &prev->total, t);
496		drop = calc_drop(&rec->total, &prev->total, t);
497		info = calc_info(&rec->total, &prev->total, t);
498		err  = calc_err(&rec->total, &prev->total, t);
499		if (info > 0) {
500			i_str = "bulk-average";
501			info = (pps+drop) / info; /* calc avg bulk */
502		}
503		if (err > 0)
504			err_str = "drv-err";
505		printf(fmt2, "devmap-xmit", "total", pps, drop,
506		       info, i_str, err_str);
507	}
508
509	printf("\n");
510}
511
512static bool stats_collect(struct stats_record *rec)
513{
514	int fd;
515	int i;
516
517	/* TODO: Detect if someone unloaded the perf event_fd's, as
518	 * this can happen by someone running perf-record -e
519	 */
520
521	fd = bpf_map__fd(map_data[REDIRECT_ERR_CNT]);
522	for (i = 0; i < REDIR_RES_MAX; i++)
523		map_collect_record_u64(fd, i, &rec->xdp_redirect[i]);
524
525	fd = bpf_map__fd(map_data[EXCEPTION_CNT]);
526	for (i = 0; i < XDP_ACTION_MAX; i++) {
527		map_collect_record_u64(fd, i, &rec->xdp_exception[i]);
528	}
529
530	fd = bpf_map__fd(map_data[CPUMAP_ENQUEUE_CNT]);
531	for (i = 0; i < MAX_CPUS; i++)
532		map_collect_record(fd, i, &rec->xdp_cpumap_enqueue[i]);
533
534	fd = bpf_map__fd(map_data[CPUMAP_KTHREAD_CNT]);
535	map_collect_record(fd, 0, &rec->xdp_cpumap_kthread);
536
537	fd = bpf_map__fd(map_data[DEVMAP_XMIT_CNT]);
538	map_collect_record(fd, 0, &rec->xdp_devmap_xmit);
539
540	return true;
541}
542
543static void *alloc_rec_per_cpu(int record_size)
544{
545	unsigned int nr_cpus = bpf_num_possible_cpus();
546	void *array;
547
548	array = calloc(nr_cpus, record_size);
549	if (!array) {
550		fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus);
551		exit(EXIT_FAIL_MEM);
552	}
553	return array;
554}
555
556static struct stats_record *alloc_stats_record(void)
557{
558	struct stats_record *rec;
559	int rec_sz;
560	int i;
561
562	/* Alloc main stats_record structure */
563	rec = calloc(1, sizeof(*rec));
564	if (!rec) {
565		fprintf(stderr, "Mem alloc error\n");
566		exit(EXIT_FAIL_MEM);
567	}
568
569	/* Alloc stats stored per CPU for each record */
570	rec_sz = sizeof(struct u64rec);
571	for (i = 0; i < REDIR_RES_MAX; i++)
572		rec->xdp_redirect[i].cpu = alloc_rec_per_cpu(rec_sz);
573
574	for (i = 0; i < XDP_ACTION_MAX; i++)
575		rec->xdp_exception[i].cpu = alloc_rec_per_cpu(rec_sz);
576
577	rec_sz = sizeof(struct datarec);
578	rec->xdp_cpumap_kthread.cpu = alloc_rec_per_cpu(rec_sz);
579	rec->xdp_devmap_xmit.cpu    = alloc_rec_per_cpu(rec_sz);
580
581	for (i = 0; i < MAX_CPUS; i++)
582		rec->xdp_cpumap_enqueue[i].cpu = alloc_rec_per_cpu(rec_sz);
583
584	return rec;
585}
586
587static void free_stats_record(struct stats_record *r)
588{
589	int i;
590
591	for (i = 0; i < REDIR_RES_MAX; i++)
592		free(r->xdp_redirect[i].cpu);
593
594	for (i = 0; i < XDP_ACTION_MAX; i++)
595		free(r->xdp_exception[i].cpu);
596
597	free(r->xdp_cpumap_kthread.cpu);
598	free(r->xdp_devmap_xmit.cpu);
599
600	for (i = 0; i < MAX_CPUS; i++)
601		free(r->xdp_cpumap_enqueue[i].cpu);
602
603	free(r);
604}
605
606/* Pointer swap trick */
607static inline void swap(struct stats_record **a, struct stats_record **b)
608{
609	struct stats_record *tmp;
610
611	tmp = *a;
612	*a = *b;
613	*b = tmp;
614}
615
616static void stats_poll(int interval, bool err_only)
617{
618	struct stats_record *rec, *prev;
619
620	rec  = alloc_stats_record();
621	prev = alloc_stats_record();
622	stats_collect(rec);
623
624	if (err_only)
625		printf("\n%s\n", __doc_err_only__);
626
627	/* Trick to pretty printf with thousands separators use %' */
628	setlocale(LC_NUMERIC, "en_US");
629
630	/* Header */
631	if (verbose)
632		printf("\n%s", __doc__);
633
634	/* TODO Need more advanced stats on error types */
635	if (verbose) {
636		printf(" - Stats map0: %s\n", bpf_map__name(map_data[0]));
637		printf(" - Stats map1: %s\n", bpf_map__name(map_data[1]));
638		printf("\n");
639	}
640	fflush(stdout);
641
642	while (1) {
643		swap(&prev, &rec);
644		stats_collect(rec);
645		stats_print(rec, prev, err_only);
646		fflush(stdout);
647		sleep(interval);
648	}
649
650	free_stats_record(rec);
651	free_stats_record(prev);
652}
653
654static void print_bpf_prog_info(void)
655{
656	struct bpf_program *prog;
657	struct bpf_map *map;
658	int i = 0;
659
660	/* Prog info */
661	printf("Loaded BPF prog have %d bpf program(s)\n", tp_cnt);
662	bpf_object__for_each_program(prog, obj) {
663		printf(" - prog_fd[%d] = fd(%d)\n", i, bpf_program__fd(prog));
664		i++;
665	}
666
667	i = 0;
668	/* Maps info */
669	printf("Loaded BPF prog have %d map(s)\n", map_cnt);
670	bpf_object__for_each_map(map, obj) {
671		const char *name = bpf_map__name(map);
672		int fd		 = bpf_map__fd(map);
673
674		printf(" - map_data[%d] = fd(%d) name:%s\n", i, fd, name);
675		i++;
676	}
677
678	/* Event info */
679	printf("Searching for (max:%d) event file descriptor(s)\n", tp_cnt);
680	for (i = 0; i < tp_cnt; i++) {
681		int fd = bpf_link__fd(tp_links[i]);
682
683		if (fd != -1)
684			printf(" - event_fd[%d] = fd(%d)\n", i, fd);
685	}
686}
687
688int main(int argc, char **argv)
689{
690	struct bpf_program *prog;
691	int longindex = 0, opt;
692	int ret = EXIT_FAILURE;
693	enum map_type type;
694	char filename[256];
695
696	/* Default settings: */
697	bool errors_only = true;
698	int interval = 2;
699
700	/* Parse commands line args */
701	while ((opt = getopt_long(argc, argv, "hDSs:",
702				  long_options, &longindex)) != -1) {
703		switch (opt) {
704		case 'D':
705			debug = true;
706			break;
707		case 'S':
708			errors_only = false;
709			break;
710		case 's':
711			interval = atoi(optarg);
712			break;
713		case 'h':
714		default:
715			usage(argv);
716			return ret;
717		}
718	}
719
720	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
721
722	/* Remove tracepoint program when program is interrupted or killed */
723	signal(SIGINT, int_exit);
724	signal(SIGTERM, int_exit);
725
726	obj = bpf_object__open_file(filename, NULL);
727	if (libbpf_get_error(obj)) {
728		printf("ERROR: opening BPF object file failed\n");
729		obj = NULL;
730		goto cleanup;
731	}
732
733	/* load BPF program */
734	if (bpf_object__load(obj)) {
735		printf("ERROR: loading BPF object file failed\n");
736		goto cleanup;
737	}
738
739	for (type = 0; type < NUM_MAP; type++) {
740		map_data[type] =
741			bpf_object__find_map_by_name(obj, map_type_strings[type]);
742
743		if (libbpf_get_error(map_data[type])) {
744			printf("ERROR: finding a map in obj file failed\n");
745			goto cleanup;
746		}
747		map_cnt++;
748	}
749
750	bpf_object__for_each_program(prog, obj) {
751		tp_links[tp_cnt] = bpf_program__attach(prog);
752		if (libbpf_get_error(tp_links[tp_cnt])) {
753			printf("ERROR: bpf_program__attach failed\n");
754			tp_links[tp_cnt] = NULL;
755			goto cleanup;
756		}
757		tp_cnt++;
758	}
759
760	if (debug) {
761		print_bpf_prog_info();
762	}
763
764	/* Unload/stop tracepoint event by closing bpf_link's */
765	if (errors_only) {
766		/* The bpf_link[i] depend on the order of
767		 * the functions was defined in _kern.c
768		 */
769		bpf_link__destroy(tp_links[2]);	/* tracepoint/xdp/xdp_redirect */
770		tp_links[2] = NULL;
771
772		bpf_link__destroy(tp_links[3]);	/* tracepoint/xdp/xdp_redirect_map */
773		tp_links[3] = NULL;
774	}
775
776	stats_poll(interval, errors_only);
777
778	ret = EXIT_SUCCESS;
779
780cleanup:
781	/* Detach tracepoints */
782	while (tp_cnt)
783		bpf_link__destroy(tp_links[--tp_cnt]);
784
785	bpf_object__close(obj);
786	return ret;
787}