18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci * Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc.
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_cistatic const char *__doc__=
58c2ecf20Sopenharmony_ci "XDP monitor tool, based on tracepoints\n"
68c2ecf20Sopenharmony_ci;
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_cistatic const char *__doc_err_only__=
98c2ecf20Sopenharmony_ci " NOTICE: Only tracking XDP redirect errors\n"
108c2ecf20Sopenharmony_ci "         Enable TX success stats via '--stats'\n"
118c2ecf20Sopenharmony_ci "         (which comes with a per packet processing overhead)\n"
128c2ecf20Sopenharmony_ci;
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <errno.h>
158c2ecf20Sopenharmony_ci#include <stdio.h>
168c2ecf20Sopenharmony_ci#include <stdlib.h>
178c2ecf20Sopenharmony_ci#include <stdbool.h>
188c2ecf20Sopenharmony_ci#include <stdint.h>
198c2ecf20Sopenharmony_ci#include <string.h>
208c2ecf20Sopenharmony_ci#include <ctype.h>
218c2ecf20Sopenharmony_ci#include <unistd.h>
228c2ecf20Sopenharmony_ci#include <locale.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <sys/resource.h>
258c2ecf20Sopenharmony_ci#include <getopt.h>
268c2ecf20Sopenharmony_ci#include <net/if.h>
278c2ecf20Sopenharmony_ci#include <time.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <signal.h>
308c2ecf20Sopenharmony_ci#include <bpf/bpf.h>
318c2ecf20Sopenharmony_ci#include <bpf/libbpf.h>
328c2ecf20Sopenharmony_ci#include "bpf_util.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cienum map_type {
358c2ecf20Sopenharmony_ci	REDIRECT_ERR_CNT,
368c2ecf20Sopenharmony_ci	EXCEPTION_CNT,
378c2ecf20Sopenharmony_ci	CPUMAP_ENQUEUE_CNT,
388c2ecf20Sopenharmony_ci	CPUMAP_KTHREAD_CNT,
398c2ecf20Sopenharmony_ci	DEVMAP_XMIT_CNT,
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic const char *const map_type_strings[] = {
438c2ecf20Sopenharmony_ci	[REDIRECT_ERR_CNT] = "redirect_err_cnt",
448c2ecf20Sopenharmony_ci	[EXCEPTION_CNT] = "exception_cnt",
458c2ecf20Sopenharmony_ci	[CPUMAP_ENQUEUE_CNT] = "cpumap_enqueue_cnt",
468c2ecf20Sopenharmony_ci	[CPUMAP_KTHREAD_CNT] = "cpumap_kthread_cnt",
478c2ecf20Sopenharmony_ci	[DEVMAP_XMIT_CNT] = "devmap_xmit_cnt",
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define NUM_MAP 5
518c2ecf20Sopenharmony_ci#define NUM_TP 8
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int tp_cnt;
548c2ecf20Sopenharmony_cistatic int map_cnt;
558c2ecf20Sopenharmony_cistatic int verbose = 1;
568c2ecf20Sopenharmony_cistatic bool debug = false;
578c2ecf20Sopenharmony_cistruct bpf_map *map_data[NUM_MAP] = {};
588c2ecf20Sopenharmony_cistruct bpf_link *tp_links[NUM_TP] = {};
598c2ecf20Sopenharmony_cistruct bpf_object *obj;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct option long_options[] = {
628c2ecf20Sopenharmony_ci	{"help",	no_argument,		NULL, 'h' },
638c2ecf20Sopenharmony_ci	{"debug",	no_argument,		NULL, 'D' },
648c2ecf20Sopenharmony_ci	{"stats",	no_argument,		NULL, 'S' },
658c2ecf20Sopenharmony_ci	{"sec", 	required_argument,	NULL, 's' },
668c2ecf20Sopenharmony_ci	{0, 0, NULL,  0 }
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void int_exit(int sig)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	/* Detach tracepoints */
728c2ecf20Sopenharmony_ci	while (tp_cnt)
738c2ecf20Sopenharmony_ci		bpf_link__destroy(tp_links[--tp_cnt]);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	bpf_object__close(obj);
768c2ecf20Sopenharmony_ci	exit(0);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* C standard specifies two constants, EXIT_SUCCESS(0) and EXIT_FAILURE(1) */
808c2ecf20Sopenharmony_ci#define EXIT_FAIL_MEM	5
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic void usage(char *argv[])
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	int i;
858c2ecf20Sopenharmony_ci	printf("\nDOCUMENTATION:\n%s\n", __doc__);
868c2ecf20Sopenharmony_ci	printf("\n");
878c2ecf20Sopenharmony_ci	printf(" Usage: %s (options-see-below)\n",
888c2ecf20Sopenharmony_ci	       argv[0]);
898c2ecf20Sopenharmony_ci	printf(" Listing options:\n");
908c2ecf20Sopenharmony_ci	for (i = 0; long_options[i].name != 0; i++) {
918c2ecf20Sopenharmony_ci		printf(" --%-15s", long_options[i].name);
928c2ecf20Sopenharmony_ci		if (long_options[i].flag != NULL)
938c2ecf20Sopenharmony_ci			printf(" flag (internal value:%d)",
948c2ecf20Sopenharmony_ci			       *long_options[i].flag);
958c2ecf20Sopenharmony_ci		else
968c2ecf20Sopenharmony_ci			printf("short-option: -%c",
978c2ecf20Sopenharmony_ci			       long_options[i].val);
988c2ecf20Sopenharmony_ci		printf("\n");
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci	printf("\n");
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define NANOSEC_PER_SEC 1000000000 /* 10^9 */
1048c2ecf20Sopenharmony_cistatic __u64 gettime(void)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct timespec t;
1078c2ecf20Sopenharmony_ci	int res;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	res = clock_gettime(CLOCK_MONOTONIC, &t);
1108c2ecf20Sopenharmony_ci	if (res < 0) {
1118c2ecf20Sopenharmony_ci		fprintf(stderr, "Error with gettimeofday! (%i)\n", res);
1128c2ecf20Sopenharmony_ci		exit(EXIT_FAILURE);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci	return (__u64) t.tv_sec * NANOSEC_PER_SEC + t.tv_nsec;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cienum {
1188c2ecf20Sopenharmony_ci	REDIR_SUCCESS = 0,
1198c2ecf20Sopenharmony_ci	REDIR_ERROR = 1,
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci#define REDIR_RES_MAX 2
1228c2ecf20Sopenharmony_cistatic const char *redir_names[REDIR_RES_MAX] = {
1238c2ecf20Sopenharmony_ci	[REDIR_SUCCESS]	= "Success",
1248c2ecf20Sopenharmony_ci	[REDIR_ERROR]	= "Error",
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_cistatic const char *err2str(int err)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	if (err < REDIR_RES_MAX)
1298c2ecf20Sopenharmony_ci		return redir_names[err];
1308c2ecf20Sopenharmony_ci	return NULL;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci/* enum xdp_action */
1338c2ecf20Sopenharmony_ci#define XDP_UNKNOWN	XDP_REDIRECT + 1
1348c2ecf20Sopenharmony_ci#define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
1358c2ecf20Sopenharmony_cistatic const char *xdp_action_names[XDP_ACTION_MAX] = {
1368c2ecf20Sopenharmony_ci	[XDP_ABORTED]	= "XDP_ABORTED",
1378c2ecf20Sopenharmony_ci	[XDP_DROP]	= "XDP_DROP",
1388c2ecf20Sopenharmony_ci	[XDP_PASS]	= "XDP_PASS",
1398c2ecf20Sopenharmony_ci	[XDP_TX]	= "XDP_TX",
1408c2ecf20Sopenharmony_ci	[XDP_REDIRECT]	= "XDP_REDIRECT",
1418c2ecf20Sopenharmony_ci	[XDP_UNKNOWN]	= "XDP_UNKNOWN",
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_cistatic const char *action2str(int action)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	if (action < XDP_ACTION_MAX)
1468c2ecf20Sopenharmony_ci		return xdp_action_names[action];
1478c2ecf20Sopenharmony_ci	return NULL;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/* Common stats data record shared with _kern.c */
1518c2ecf20Sopenharmony_cistruct datarec {
1528c2ecf20Sopenharmony_ci	__u64 processed;
1538c2ecf20Sopenharmony_ci	__u64 dropped;
1548c2ecf20Sopenharmony_ci	__u64 info;
1558c2ecf20Sopenharmony_ci	__u64 err;
1568c2ecf20Sopenharmony_ci};
1578c2ecf20Sopenharmony_ci#define MAX_CPUS 64
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/* Userspace structs for collection of stats from maps */
1608c2ecf20Sopenharmony_cistruct record {
1618c2ecf20Sopenharmony_ci	__u64 timestamp;
1628c2ecf20Sopenharmony_ci	struct datarec total;
1638c2ecf20Sopenharmony_ci	struct datarec *cpu;
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_cistruct u64rec {
1668c2ecf20Sopenharmony_ci	__u64 processed;
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_cistruct record_u64 {
1698c2ecf20Sopenharmony_ci	/* record for _kern side __u64 values */
1708c2ecf20Sopenharmony_ci	__u64 timestamp;
1718c2ecf20Sopenharmony_ci	struct u64rec total;
1728c2ecf20Sopenharmony_ci	struct u64rec *cpu;
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistruct stats_record {
1768c2ecf20Sopenharmony_ci	struct record_u64 xdp_redirect[REDIR_RES_MAX];
1778c2ecf20Sopenharmony_ci	struct record_u64 xdp_exception[XDP_ACTION_MAX];
1788c2ecf20Sopenharmony_ci	struct record xdp_cpumap_kthread;
1798c2ecf20Sopenharmony_ci	struct record xdp_cpumap_enqueue[MAX_CPUS];
1808c2ecf20Sopenharmony_ci	struct record xdp_devmap_xmit;
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic bool map_collect_record(int fd, __u32 key, struct record *rec)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	/* For percpu maps, userspace gets a value per possible CPU */
1868c2ecf20Sopenharmony_ci	unsigned int nr_cpus = bpf_num_possible_cpus();
1878c2ecf20Sopenharmony_ci	struct datarec values[nr_cpus];
1888c2ecf20Sopenharmony_ci	__u64 sum_processed = 0;
1898c2ecf20Sopenharmony_ci	__u64 sum_dropped = 0;
1908c2ecf20Sopenharmony_ci	__u64 sum_info = 0;
1918c2ecf20Sopenharmony_ci	__u64 sum_err = 0;
1928c2ecf20Sopenharmony_ci	int i;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
1958c2ecf20Sopenharmony_ci		fprintf(stderr,
1968c2ecf20Sopenharmony_ci			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
1978c2ecf20Sopenharmony_ci		return false;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	/* Get time as close as possible to reading map contents */
2008c2ecf20Sopenharmony_ci	rec->timestamp = gettime();
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* Record and sum values from each CPU */
2038c2ecf20Sopenharmony_ci	for (i = 0; i < nr_cpus; i++) {
2048c2ecf20Sopenharmony_ci		rec->cpu[i].processed = values[i].processed;
2058c2ecf20Sopenharmony_ci		sum_processed        += values[i].processed;
2068c2ecf20Sopenharmony_ci		rec->cpu[i].dropped = values[i].dropped;
2078c2ecf20Sopenharmony_ci		sum_dropped        += values[i].dropped;
2088c2ecf20Sopenharmony_ci		rec->cpu[i].info = values[i].info;
2098c2ecf20Sopenharmony_ci		sum_info        += values[i].info;
2108c2ecf20Sopenharmony_ci		rec->cpu[i].err = values[i].err;
2118c2ecf20Sopenharmony_ci		sum_err        += values[i].err;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	rec->total.processed = sum_processed;
2148c2ecf20Sopenharmony_ci	rec->total.dropped   = sum_dropped;
2158c2ecf20Sopenharmony_ci	rec->total.info      = sum_info;
2168c2ecf20Sopenharmony_ci	rec->total.err       = sum_err;
2178c2ecf20Sopenharmony_ci	return true;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic bool map_collect_record_u64(int fd, __u32 key, struct record_u64 *rec)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	/* For percpu maps, userspace gets a value per possible CPU */
2238c2ecf20Sopenharmony_ci	unsigned int nr_cpus = bpf_num_possible_cpus();
2248c2ecf20Sopenharmony_ci	struct u64rec values[nr_cpus];
2258c2ecf20Sopenharmony_ci	__u64 sum_total = 0;
2268c2ecf20Sopenharmony_ci	int i;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if ((bpf_map_lookup_elem(fd, &key, values)) != 0) {
2298c2ecf20Sopenharmony_ci		fprintf(stderr,
2308c2ecf20Sopenharmony_ci			"ERR: bpf_map_lookup_elem failed key:0x%X\n", key);
2318c2ecf20Sopenharmony_ci		return false;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci	/* Get time as close as possible to reading map contents */
2348c2ecf20Sopenharmony_ci	rec->timestamp = gettime();
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Record and sum values from each CPU */
2378c2ecf20Sopenharmony_ci	for (i = 0; i < nr_cpus; i++) {
2388c2ecf20Sopenharmony_ci		rec->cpu[i].processed = values[i].processed;
2398c2ecf20Sopenharmony_ci		sum_total            += values[i].processed;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci	rec->total.processed = sum_total;
2428c2ecf20Sopenharmony_ci	return true;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic double calc_period(struct record *r, struct record *p)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	double period_ = 0;
2488c2ecf20Sopenharmony_ci	__u64 period = 0;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	period = r->timestamp - p->timestamp;
2518c2ecf20Sopenharmony_ci	if (period > 0)
2528c2ecf20Sopenharmony_ci		period_ = ((double) period / NANOSEC_PER_SEC);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return period_;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic double calc_period_u64(struct record_u64 *r, struct record_u64 *p)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	double period_ = 0;
2608c2ecf20Sopenharmony_ci	__u64 period = 0;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	period = r->timestamp - p->timestamp;
2638c2ecf20Sopenharmony_ci	if (period > 0)
2648c2ecf20Sopenharmony_ci		period_ = ((double) period / NANOSEC_PER_SEC);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return period_;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic double calc_pps(struct datarec *r, struct datarec *p, double period)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	__u64 packets = 0;
2728c2ecf20Sopenharmony_ci	double pps = 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (period > 0) {
2758c2ecf20Sopenharmony_ci		packets = r->processed - p->processed;
2768c2ecf20Sopenharmony_ci		pps = packets / period;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci	return pps;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic double calc_pps_u64(struct u64rec *r, struct u64rec *p, double period)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	__u64 packets = 0;
2848c2ecf20Sopenharmony_ci	double pps = 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (period > 0) {
2878c2ecf20Sopenharmony_ci		packets = r->processed - p->processed;
2888c2ecf20Sopenharmony_ci		pps = packets / period;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	return pps;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic double calc_drop(struct datarec *r, struct datarec *p, double period)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	__u64 packets = 0;
2968c2ecf20Sopenharmony_ci	double pps = 0;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (period > 0) {
2998c2ecf20Sopenharmony_ci		packets = r->dropped - p->dropped;
3008c2ecf20Sopenharmony_ci		pps = packets / period;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci	return pps;
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic double calc_info(struct datarec *r, struct datarec *p, double period)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	__u64 packets = 0;
3088c2ecf20Sopenharmony_ci	double pps = 0;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (period > 0) {
3118c2ecf20Sopenharmony_ci		packets = r->info - p->info;
3128c2ecf20Sopenharmony_ci		pps = packets / period;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci	return pps;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic double calc_err(struct datarec *r, struct datarec *p, double period)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	__u64 packets = 0;
3208c2ecf20Sopenharmony_ci	double pps = 0;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (period > 0) {
3238c2ecf20Sopenharmony_ci		packets = r->err - p->err;
3248c2ecf20Sopenharmony_ci		pps = packets / period;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci	return pps;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic void stats_print(struct stats_record *stats_rec,
3308c2ecf20Sopenharmony_ci			struct stats_record *stats_prev,
3318c2ecf20Sopenharmony_ci			bool err_only)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	unsigned int nr_cpus = bpf_num_possible_cpus();
3348c2ecf20Sopenharmony_ci	int rec_i = 0, i, to_cpu;
3358c2ecf20Sopenharmony_ci	double t = 0, pps = 0;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* Header */
3388c2ecf20Sopenharmony_ci	printf("%-15s %-7s %-12s %-12s %-9s\n",
3398c2ecf20Sopenharmony_ci	       "XDP-event", "CPU:to", "pps", "drop-pps", "extra-info");
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* tracepoint: xdp:xdp_redirect_* */
3428c2ecf20Sopenharmony_ci	if (err_only)
3438c2ecf20Sopenharmony_ci		rec_i = REDIR_ERROR;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	for (; rec_i < REDIR_RES_MAX; rec_i++) {
3468c2ecf20Sopenharmony_ci		struct record_u64 *rec, *prev;
3478c2ecf20Sopenharmony_ci		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
3488c2ecf20Sopenharmony_ci		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		rec  =  &stats_rec->xdp_redirect[rec_i];
3518c2ecf20Sopenharmony_ci		prev = &stats_prev->xdp_redirect[rec_i];
3528c2ecf20Sopenharmony_ci		t = calc_period_u64(rec, prev);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		for (i = 0; i < nr_cpus; i++) {
3558c2ecf20Sopenharmony_ci			struct u64rec *r = &rec->cpu[i];
3568c2ecf20Sopenharmony_ci			struct u64rec *p = &prev->cpu[i];
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci			pps = calc_pps_u64(r, p, t);
3598c2ecf20Sopenharmony_ci			if (pps > 0)
3608c2ecf20Sopenharmony_ci				printf(fmt1, "XDP_REDIRECT", i,
3618c2ecf20Sopenharmony_ci				       rec_i ? 0.0: pps, rec_i ? pps : 0.0,
3628c2ecf20Sopenharmony_ci				       err2str(rec_i));
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci		pps = calc_pps_u64(&rec->total, &prev->total, t);
3658c2ecf20Sopenharmony_ci		printf(fmt2, "XDP_REDIRECT", "total",
3668c2ecf20Sopenharmony_ci		       rec_i ? 0.0: pps, rec_i ? pps : 0.0, err2str(rec_i));
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* tracepoint: xdp:xdp_exception */
3708c2ecf20Sopenharmony_ci	for (rec_i = 0; rec_i < XDP_ACTION_MAX; rec_i++) {
3718c2ecf20Sopenharmony_ci		struct record_u64 *rec, *prev;
3728c2ecf20Sopenharmony_ci		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %s\n";
3738c2ecf20Sopenharmony_ci		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %s\n";
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		rec  =  &stats_rec->xdp_exception[rec_i];
3768c2ecf20Sopenharmony_ci		prev = &stats_prev->xdp_exception[rec_i];
3778c2ecf20Sopenharmony_ci		t = calc_period_u64(rec, prev);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		for (i = 0; i < nr_cpus; i++) {
3808c2ecf20Sopenharmony_ci			struct u64rec *r = &rec->cpu[i];
3818c2ecf20Sopenharmony_ci			struct u64rec *p = &prev->cpu[i];
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci			pps = calc_pps_u64(r, p, t);
3848c2ecf20Sopenharmony_ci			if (pps > 0)
3858c2ecf20Sopenharmony_ci				printf(fmt1, "Exception", i,
3868c2ecf20Sopenharmony_ci				       0.0, pps, action2str(rec_i));
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci		pps = calc_pps_u64(&rec->total, &prev->total, t);
3898c2ecf20Sopenharmony_ci		if (pps > 0)
3908c2ecf20Sopenharmony_ci			printf(fmt2, "Exception", "total",
3918c2ecf20Sopenharmony_ci			       0.0, pps, action2str(rec_i));
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* cpumap enqueue stats */
3958c2ecf20Sopenharmony_ci	for (to_cpu = 0; to_cpu < MAX_CPUS; to_cpu++) {
3968c2ecf20Sopenharmony_ci		char *fmt1 = "%-15s %3d:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
3978c2ecf20Sopenharmony_ci		char *fmt2 = "%-15s %3s:%-3d %'-12.0f %'-12.0f %'-10.2f %s\n";
3988c2ecf20Sopenharmony_ci		struct record *rec, *prev;
3998c2ecf20Sopenharmony_ci		char *info_str = "";
4008c2ecf20Sopenharmony_ci		double drop, info;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		rec  =  &stats_rec->xdp_cpumap_enqueue[to_cpu];
4038c2ecf20Sopenharmony_ci		prev = &stats_prev->xdp_cpumap_enqueue[to_cpu];
4048c2ecf20Sopenharmony_ci		t = calc_period(rec, prev);
4058c2ecf20Sopenharmony_ci		for (i = 0; i < nr_cpus; i++) {
4068c2ecf20Sopenharmony_ci			struct datarec *r = &rec->cpu[i];
4078c2ecf20Sopenharmony_ci			struct datarec *p = &prev->cpu[i];
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci			pps  = calc_pps(r, p, t);
4108c2ecf20Sopenharmony_ci			drop = calc_drop(r, p, t);
4118c2ecf20Sopenharmony_ci			info = calc_info(r, p, t);
4128c2ecf20Sopenharmony_ci			if (info > 0) {
4138c2ecf20Sopenharmony_ci				info_str = "bulk-average";
4148c2ecf20Sopenharmony_ci				info = pps / info; /* calc average bulk size */
4158c2ecf20Sopenharmony_ci			}
4168c2ecf20Sopenharmony_ci			if (pps > 0)
4178c2ecf20Sopenharmony_ci				printf(fmt1, "cpumap-enqueue",
4188c2ecf20Sopenharmony_ci				       i, to_cpu, pps, drop, info, info_str);
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci		pps = calc_pps(&rec->total, &prev->total, t);
4218c2ecf20Sopenharmony_ci		if (pps > 0) {
4228c2ecf20Sopenharmony_ci			drop = calc_drop(&rec->total, &prev->total, t);
4238c2ecf20Sopenharmony_ci			info = calc_info(&rec->total, &prev->total, t);
4248c2ecf20Sopenharmony_ci			if (info > 0) {
4258c2ecf20Sopenharmony_ci				info_str = "bulk-average";
4268c2ecf20Sopenharmony_ci				info = pps / info; /* calc average bulk size */
4278c2ecf20Sopenharmony_ci			}
4288c2ecf20Sopenharmony_ci			printf(fmt2, "cpumap-enqueue",
4298c2ecf20Sopenharmony_ci			       "sum", to_cpu, pps, drop, info, info_str);
4308c2ecf20Sopenharmony_ci		}
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* cpumap kthread stats */
4348c2ecf20Sopenharmony_ci	{
4358c2ecf20Sopenharmony_ci		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.0f %s\n";
4368c2ecf20Sopenharmony_ci		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.0f %s\n";
4378c2ecf20Sopenharmony_ci		struct record *rec, *prev;
4388c2ecf20Sopenharmony_ci		double drop, info;
4398c2ecf20Sopenharmony_ci		char *i_str = "";
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		rec  =  &stats_rec->xdp_cpumap_kthread;
4428c2ecf20Sopenharmony_ci		prev = &stats_prev->xdp_cpumap_kthread;
4438c2ecf20Sopenharmony_ci		t = calc_period(rec, prev);
4448c2ecf20Sopenharmony_ci		for (i = 0; i < nr_cpus; i++) {
4458c2ecf20Sopenharmony_ci			struct datarec *r = &rec->cpu[i];
4468c2ecf20Sopenharmony_ci			struct datarec *p = &prev->cpu[i];
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci			pps  = calc_pps(r, p, t);
4498c2ecf20Sopenharmony_ci			drop = calc_drop(r, p, t);
4508c2ecf20Sopenharmony_ci			info = calc_info(r, p, t);
4518c2ecf20Sopenharmony_ci			if (info > 0)
4528c2ecf20Sopenharmony_ci				i_str = "sched";
4538c2ecf20Sopenharmony_ci			if (pps > 0 || drop > 0)
4548c2ecf20Sopenharmony_ci				printf(fmt1, "cpumap-kthread",
4558c2ecf20Sopenharmony_ci				       i, pps, drop, info, i_str);
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci		pps = calc_pps(&rec->total, &prev->total, t);
4588c2ecf20Sopenharmony_ci		drop = calc_drop(&rec->total, &prev->total, t);
4598c2ecf20Sopenharmony_ci		info = calc_info(&rec->total, &prev->total, t);
4608c2ecf20Sopenharmony_ci		if (info > 0)
4618c2ecf20Sopenharmony_ci			i_str = "sched-sum";
4628c2ecf20Sopenharmony_ci		printf(fmt2, "cpumap-kthread", "total", pps, drop, info, i_str);
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	/* devmap ndo_xdp_xmit stats */
4668c2ecf20Sopenharmony_ci	{
4678c2ecf20Sopenharmony_ci		char *fmt1 = "%-15s %-7d %'-12.0f %'-12.0f %'-10.2f %s %s\n";
4688c2ecf20Sopenharmony_ci		char *fmt2 = "%-15s %-7s %'-12.0f %'-12.0f %'-10.2f %s %s\n";
4698c2ecf20Sopenharmony_ci		struct record *rec, *prev;
4708c2ecf20Sopenharmony_ci		double drop, info, err;
4718c2ecf20Sopenharmony_ci		char *i_str = "";
4728c2ecf20Sopenharmony_ci		char *err_str = "";
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		rec  =  &stats_rec->xdp_devmap_xmit;
4758c2ecf20Sopenharmony_ci		prev = &stats_prev->xdp_devmap_xmit;
4768c2ecf20Sopenharmony_ci		t = calc_period(rec, prev);
4778c2ecf20Sopenharmony_ci		for (i = 0; i < nr_cpus; i++) {
4788c2ecf20Sopenharmony_ci			struct datarec *r = &rec->cpu[i];
4798c2ecf20Sopenharmony_ci			struct datarec *p = &prev->cpu[i];
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci			pps  = calc_pps(r, p, t);
4828c2ecf20Sopenharmony_ci			drop = calc_drop(r, p, t);
4838c2ecf20Sopenharmony_ci			info = calc_info(r, p, t);
4848c2ecf20Sopenharmony_ci			err  = calc_err(r, p, t);
4858c2ecf20Sopenharmony_ci			if (info > 0) {
4868c2ecf20Sopenharmony_ci				i_str = "bulk-average";
4878c2ecf20Sopenharmony_ci				info = (pps+drop) / info; /* calc avg bulk */
4888c2ecf20Sopenharmony_ci			}
4898c2ecf20Sopenharmony_ci			if (err > 0)
4908c2ecf20Sopenharmony_ci				err_str = "drv-err";
4918c2ecf20Sopenharmony_ci			if (pps > 0 || drop > 0)
4928c2ecf20Sopenharmony_ci				printf(fmt1, "devmap-xmit",
4938c2ecf20Sopenharmony_ci				       i, pps, drop, info, i_str, err_str);
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci		pps = calc_pps(&rec->total, &prev->total, t);
4968c2ecf20Sopenharmony_ci		drop = calc_drop(&rec->total, &prev->total, t);
4978c2ecf20Sopenharmony_ci		info = calc_info(&rec->total, &prev->total, t);
4988c2ecf20Sopenharmony_ci		err  = calc_err(&rec->total, &prev->total, t);
4998c2ecf20Sopenharmony_ci		if (info > 0) {
5008c2ecf20Sopenharmony_ci			i_str = "bulk-average";
5018c2ecf20Sopenharmony_ci			info = (pps+drop) / info; /* calc avg bulk */
5028c2ecf20Sopenharmony_ci		}
5038c2ecf20Sopenharmony_ci		if (err > 0)
5048c2ecf20Sopenharmony_ci			err_str = "drv-err";
5058c2ecf20Sopenharmony_ci		printf(fmt2, "devmap-xmit", "total", pps, drop,
5068c2ecf20Sopenharmony_ci		       info, i_str, err_str);
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	printf("\n");
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic bool stats_collect(struct stats_record *rec)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	int fd;
5158c2ecf20Sopenharmony_ci	int i;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* TODO: Detect if someone unloaded the perf event_fd's, as
5188c2ecf20Sopenharmony_ci	 * this can happen by someone running perf-record -e
5198c2ecf20Sopenharmony_ci	 */
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	fd = bpf_map__fd(map_data[REDIRECT_ERR_CNT]);
5228c2ecf20Sopenharmony_ci	for (i = 0; i < REDIR_RES_MAX; i++)
5238c2ecf20Sopenharmony_ci		map_collect_record_u64(fd, i, &rec->xdp_redirect[i]);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	fd = bpf_map__fd(map_data[EXCEPTION_CNT]);
5268c2ecf20Sopenharmony_ci	for (i = 0; i < XDP_ACTION_MAX; i++) {
5278c2ecf20Sopenharmony_ci		map_collect_record_u64(fd, i, &rec->xdp_exception[i]);
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	fd = bpf_map__fd(map_data[CPUMAP_ENQUEUE_CNT]);
5318c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CPUS; i++)
5328c2ecf20Sopenharmony_ci		map_collect_record(fd, i, &rec->xdp_cpumap_enqueue[i]);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	fd = bpf_map__fd(map_data[CPUMAP_KTHREAD_CNT]);
5358c2ecf20Sopenharmony_ci	map_collect_record(fd, 0, &rec->xdp_cpumap_kthread);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	fd = bpf_map__fd(map_data[DEVMAP_XMIT_CNT]);
5388c2ecf20Sopenharmony_ci	map_collect_record(fd, 0, &rec->xdp_devmap_xmit);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	return true;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic void *alloc_rec_per_cpu(int record_size)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	unsigned int nr_cpus = bpf_num_possible_cpus();
5468c2ecf20Sopenharmony_ci	void *array;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	array = calloc(nr_cpus, record_size);
5498c2ecf20Sopenharmony_ci	if (!array) {
5508c2ecf20Sopenharmony_ci		fprintf(stderr, "Mem alloc error (nr_cpus:%u)\n", nr_cpus);
5518c2ecf20Sopenharmony_ci		exit(EXIT_FAIL_MEM);
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci	return array;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_cistatic struct stats_record *alloc_stats_record(void)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	struct stats_record *rec;
5598c2ecf20Sopenharmony_ci	int rec_sz;
5608c2ecf20Sopenharmony_ci	int i;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/* Alloc main stats_record structure */
5638c2ecf20Sopenharmony_ci	rec = calloc(1, sizeof(*rec));
5648c2ecf20Sopenharmony_ci	if (!rec) {
5658c2ecf20Sopenharmony_ci		fprintf(stderr, "Mem alloc error\n");
5668c2ecf20Sopenharmony_ci		exit(EXIT_FAIL_MEM);
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	/* Alloc stats stored per CPU for each record */
5708c2ecf20Sopenharmony_ci	rec_sz = sizeof(struct u64rec);
5718c2ecf20Sopenharmony_ci	for (i = 0; i < REDIR_RES_MAX; i++)
5728c2ecf20Sopenharmony_ci		rec->xdp_redirect[i].cpu = alloc_rec_per_cpu(rec_sz);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	for (i = 0; i < XDP_ACTION_MAX; i++)
5758c2ecf20Sopenharmony_ci		rec->xdp_exception[i].cpu = alloc_rec_per_cpu(rec_sz);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	rec_sz = sizeof(struct datarec);
5788c2ecf20Sopenharmony_ci	rec->xdp_cpumap_kthread.cpu = alloc_rec_per_cpu(rec_sz);
5798c2ecf20Sopenharmony_ci	rec->xdp_devmap_xmit.cpu    = alloc_rec_per_cpu(rec_sz);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CPUS; i++)
5828c2ecf20Sopenharmony_ci		rec->xdp_cpumap_enqueue[i].cpu = alloc_rec_per_cpu(rec_sz);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	return rec;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic void free_stats_record(struct stats_record *r)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	int i;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	for (i = 0; i < REDIR_RES_MAX; i++)
5928c2ecf20Sopenharmony_ci		free(r->xdp_redirect[i].cpu);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	for (i = 0; i < XDP_ACTION_MAX; i++)
5958c2ecf20Sopenharmony_ci		free(r->xdp_exception[i].cpu);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	free(r->xdp_cpumap_kthread.cpu);
5988c2ecf20Sopenharmony_ci	free(r->xdp_devmap_xmit.cpu);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CPUS; i++)
6018c2ecf20Sopenharmony_ci		free(r->xdp_cpumap_enqueue[i].cpu);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	free(r);
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci/* Pointer swap trick */
6078c2ecf20Sopenharmony_cistatic inline void swap(struct stats_record **a, struct stats_record **b)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct stats_record *tmp;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	tmp = *a;
6128c2ecf20Sopenharmony_ci	*a = *b;
6138c2ecf20Sopenharmony_ci	*b = tmp;
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic void stats_poll(int interval, bool err_only)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	struct stats_record *rec, *prev;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	rec  = alloc_stats_record();
6218c2ecf20Sopenharmony_ci	prev = alloc_stats_record();
6228c2ecf20Sopenharmony_ci	stats_collect(rec);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (err_only)
6258c2ecf20Sopenharmony_ci		printf("\n%s\n", __doc_err_only__);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* Trick to pretty printf with thousands separators use %' */
6288c2ecf20Sopenharmony_ci	setlocale(LC_NUMERIC, "en_US");
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	/* Header */
6318c2ecf20Sopenharmony_ci	if (verbose)
6328c2ecf20Sopenharmony_ci		printf("\n%s", __doc__);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	/* TODO Need more advanced stats on error types */
6358c2ecf20Sopenharmony_ci	if (verbose) {
6368c2ecf20Sopenharmony_ci		printf(" - Stats map0: %s\n", bpf_map__name(map_data[0]));
6378c2ecf20Sopenharmony_ci		printf(" - Stats map1: %s\n", bpf_map__name(map_data[1]));
6388c2ecf20Sopenharmony_ci		printf("\n");
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci	fflush(stdout);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	while (1) {
6438c2ecf20Sopenharmony_ci		swap(&prev, &rec);
6448c2ecf20Sopenharmony_ci		stats_collect(rec);
6458c2ecf20Sopenharmony_ci		stats_print(rec, prev, err_only);
6468c2ecf20Sopenharmony_ci		fflush(stdout);
6478c2ecf20Sopenharmony_ci		sleep(interval);
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	free_stats_record(rec);
6518c2ecf20Sopenharmony_ci	free_stats_record(prev);
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_cistatic void print_bpf_prog_info(void)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	struct bpf_program *prog;
6578c2ecf20Sopenharmony_ci	struct bpf_map *map;
6588c2ecf20Sopenharmony_ci	int i = 0;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	/* Prog info */
6618c2ecf20Sopenharmony_ci	printf("Loaded BPF prog have %d bpf program(s)\n", tp_cnt);
6628c2ecf20Sopenharmony_ci	bpf_object__for_each_program(prog, obj) {
6638c2ecf20Sopenharmony_ci		printf(" - prog_fd[%d] = fd(%d)\n", i, bpf_program__fd(prog));
6648c2ecf20Sopenharmony_ci		i++;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	i = 0;
6688c2ecf20Sopenharmony_ci	/* Maps info */
6698c2ecf20Sopenharmony_ci	printf("Loaded BPF prog have %d map(s)\n", map_cnt);
6708c2ecf20Sopenharmony_ci	bpf_object__for_each_map(map, obj) {
6718c2ecf20Sopenharmony_ci		const char *name = bpf_map__name(map);
6728c2ecf20Sopenharmony_ci		int fd		 = bpf_map__fd(map);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci		printf(" - map_data[%d] = fd(%d) name:%s\n", i, fd, name);
6758c2ecf20Sopenharmony_ci		i++;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/* Event info */
6798c2ecf20Sopenharmony_ci	printf("Searching for (max:%d) event file descriptor(s)\n", tp_cnt);
6808c2ecf20Sopenharmony_ci	for (i = 0; i < tp_cnt; i++) {
6818c2ecf20Sopenharmony_ci		int fd = bpf_link__fd(tp_links[i]);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		if (fd != -1)
6848c2ecf20Sopenharmony_ci			printf(" - event_fd[%d] = fd(%d)\n", i, fd);
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ciint main(int argc, char **argv)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
6918c2ecf20Sopenharmony_ci	struct bpf_program *prog;
6928c2ecf20Sopenharmony_ci	int longindex = 0, opt;
6938c2ecf20Sopenharmony_ci	int ret = EXIT_FAILURE;
6948c2ecf20Sopenharmony_ci	enum map_type type;
6958c2ecf20Sopenharmony_ci	char filename[256];
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* Default settings: */
6988c2ecf20Sopenharmony_ci	bool errors_only = true;
6998c2ecf20Sopenharmony_ci	int interval = 2;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/* Parse commands line args */
7028c2ecf20Sopenharmony_ci	while ((opt = getopt_long(argc, argv, "hDSs:",
7038c2ecf20Sopenharmony_ci				  long_options, &longindex)) != -1) {
7048c2ecf20Sopenharmony_ci		switch (opt) {
7058c2ecf20Sopenharmony_ci		case 'D':
7068c2ecf20Sopenharmony_ci			debug = true;
7078c2ecf20Sopenharmony_ci			break;
7088c2ecf20Sopenharmony_ci		case 'S':
7098c2ecf20Sopenharmony_ci			errors_only = false;
7108c2ecf20Sopenharmony_ci			break;
7118c2ecf20Sopenharmony_ci		case 's':
7128c2ecf20Sopenharmony_ci			interval = atoi(optarg);
7138c2ecf20Sopenharmony_ci			break;
7148c2ecf20Sopenharmony_ci		case 'h':
7158c2ecf20Sopenharmony_ci		default:
7168c2ecf20Sopenharmony_ci			usage(argv);
7178c2ecf20Sopenharmony_ci			return ret;
7188c2ecf20Sopenharmony_ci		}
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
7228c2ecf20Sopenharmony_ci	if (setrlimit(RLIMIT_MEMLOCK, &r)) {
7238c2ecf20Sopenharmony_ci		perror("setrlimit(RLIMIT_MEMLOCK)");
7248c2ecf20Sopenharmony_ci		return ret;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* Remove tracepoint program when program is interrupted or killed */
7288c2ecf20Sopenharmony_ci	signal(SIGINT, int_exit);
7298c2ecf20Sopenharmony_ci	signal(SIGTERM, int_exit);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	obj = bpf_object__open_file(filename, NULL);
7328c2ecf20Sopenharmony_ci	if (libbpf_get_error(obj)) {
7338c2ecf20Sopenharmony_ci		printf("ERROR: opening BPF object file failed\n");
7348c2ecf20Sopenharmony_ci		obj = NULL;
7358c2ecf20Sopenharmony_ci		goto cleanup;
7368c2ecf20Sopenharmony_ci	}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* load BPF program */
7398c2ecf20Sopenharmony_ci	if (bpf_object__load(obj)) {
7408c2ecf20Sopenharmony_ci		printf("ERROR: loading BPF object file failed\n");
7418c2ecf20Sopenharmony_ci		goto cleanup;
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	for (type = 0; type < NUM_MAP; type++) {
7458c2ecf20Sopenharmony_ci		map_data[type] =
7468c2ecf20Sopenharmony_ci			bpf_object__find_map_by_name(obj, map_type_strings[type]);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		if (libbpf_get_error(map_data[type])) {
7498c2ecf20Sopenharmony_ci			printf("ERROR: finding a map in obj file failed\n");
7508c2ecf20Sopenharmony_ci			goto cleanup;
7518c2ecf20Sopenharmony_ci		}
7528c2ecf20Sopenharmony_ci		map_cnt++;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	bpf_object__for_each_program(prog, obj) {
7568c2ecf20Sopenharmony_ci		tp_links[tp_cnt] = bpf_program__attach(prog);
7578c2ecf20Sopenharmony_ci		if (libbpf_get_error(tp_links[tp_cnt])) {
7588c2ecf20Sopenharmony_ci			printf("ERROR: bpf_program__attach failed\n");
7598c2ecf20Sopenharmony_ci			tp_links[tp_cnt] = NULL;
7608c2ecf20Sopenharmony_ci			goto cleanup;
7618c2ecf20Sopenharmony_ci		}
7628c2ecf20Sopenharmony_ci		tp_cnt++;
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (debug) {
7668c2ecf20Sopenharmony_ci		print_bpf_prog_info();
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	/* Unload/stop tracepoint event by closing bpf_link's */
7708c2ecf20Sopenharmony_ci	if (errors_only) {
7718c2ecf20Sopenharmony_ci		/* The bpf_link[i] depend on the order of
7728c2ecf20Sopenharmony_ci		 * the functions was defined in _kern.c
7738c2ecf20Sopenharmony_ci		 */
7748c2ecf20Sopenharmony_ci		bpf_link__destroy(tp_links[2]);	/* tracepoint/xdp/xdp_redirect */
7758c2ecf20Sopenharmony_ci		tp_links[2] = NULL;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci		bpf_link__destroy(tp_links[3]);	/* tracepoint/xdp/xdp_redirect_map */
7788c2ecf20Sopenharmony_ci		tp_links[3] = NULL;
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	stats_poll(interval, errors_only);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	ret = EXIT_SUCCESS;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cicleanup:
7868c2ecf20Sopenharmony_ci	/* Detach tracepoints */
7878c2ecf20Sopenharmony_ci	while (tp_cnt)
7888c2ecf20Sopenharmony_ci		bpf_link__destroy(tp_links[--tp_cnt]);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	bpf_object__close(obj);
7918c2ecf20Sopenharmony_ci	return ret;
7928c2ecf20Sopenharmony_ci}
793