1 /* Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc.
3 static const char *__doc__
=
4 "XDP monitor tool, based on tracepoints\n"
7 static const char *__doc_err_only__
=
8 " NOTICE: Only tracking XDP redirect errors\n"
9 " Enable TX success stats via '--stats'\n"
10 " (which comes with a per packet processing overhead)\n"
23 #include <sys/resource.h>
32 static int verbose
= 1;
33 static bool debug
= false;
35 static const struct option long_options
[] = {
36 {"help", no_argument
, NULL
, 'h' },
37 {"debug", no_argument
, NULL
, 'D' },
38 {"stats", no_argument
, NULL
, 'S' },
39 {"sec", required_argument
, NULL
, 's' },
43 static void usage(char *argv
[])
46 printf("\nDOCUMENTATION:\n%s\n", __doc__
);
48 printf(" Usage: %s (options-see-below)\n",
50 printf(" Listing options:\n");
51 for (i
= 0; long_options
[i
].name
!= 0; i
++) {
52 printf(" --%-15s", long_options
[i
].name
);
53 if (long_options
[i
].flag
!= NULL
)
54 printf(" flag (internal value:%d)",
55 *long_options
[i
].flag
);
57 printf("(internal short-option: -%c)",
64 #define NANOSEC_PER_SEC 1000000000 /* 10^9 */
65 static __u64
gettime(void)
70 res
= clock_gettime(CLOCK_MONOTONIC
, &t
);
72 fprintf(stderr
, "Error with gettimeofday! (%i)\n", res
);
75 return (__u64
) t
.tv_sec
* NANOSEC_PER_SEC
+ t
.tv_nsec
;
82 #define REDIR_RES_MAX 2
83 static const char *redir_names
[REDIR_RES_MAX
] = {
84 [REDIR_SUCCESS
] = "Success",
85 [REDIR_ERROR
] = "Error",
87 static const char *err2str(int err
)
89 if (err
< REDIR_RES_MAX
)
90 return redir_names
[err
];
94 #define XDP_UNKNOWN XDP_REDIRECT + 1
95 #define XDP_ACTION_MAX (XDP_UNKNOWN + 1)
96 static const char *xdp_action_names
[XDP_ACTION_MAX
] = {
97 [XDP_ABORTED
] = "XDP_ABORTED",
98 [XDP_DROP
] = "XDP_DROP",
99 [XDP_PASS
] = "XDP_PASS",
101 [XDP_REDIRECT
] = "XDP_REDIRECT",
102 [XDP_UNKNOWN
] = "XDP_UNKNOWN",
104 static const char *action2str(int action
)
106 if (action
< XDP_ACTION_MAX
)
107 return xdp_action_names
[action
];
116 struct stats_record
{
117 struct record xdp_redir
[REDIR_RES_MAX
];
118 struct record xdp_exception
[XDP_ACTION_MAX
];
121 static void stats_print_headers(bool err_only
)
124 printf("\n%s\n", __doc_err_only__
);
126 printf("%-14s %-11s %-10s %-18s %-9s\n",
127 "ACTION", "result", "pps ", "pps-human-readable", "measure-period");
130 static double calc_period(struct record
*r
, struct record
*p
)
135 period
= r
->timestamp
- p
->timestamp
;
137 period_
= ((double) period
/ NANOSEC_PER_SEC
);
142 static double calc_pps(struct record
*r
, struct record
*p
, double period
)
148 packets
= r
->counter
- p
->counter
;
149 pps
= packets
/ period
;
154 static void stats_print(struct stats_record
*rec
,
155 struct stats_record
*prev
,
158 double period
= 0, pps
= 0;
159 struct record
*r
, *p
;
162 char *fmt
= "%-14s %-11s %-10.0f %'-18.0f %f\n";
164 /* tracepoint: xdp:xdp_redirect_* */
168 for (; i
< REDIR_RES_MAX
; i
++) {
169 r
= &rec
->xdp_redir
[i
];
170 p
= &prev
->xdp_redir
[i
];
173 period
= calc_period(r
, p
);
174 pps
= calc_pps(r
, p
, period
);
176 printf(fmt
, "XDP_REDIRECT", err2str(i
), pps
, pps
, period
);
179 /* tracepoint: xdp:xdp_exception */
180 for (i
= 0; i
< XDP_ACTION_MAX
; i
++) {
181 r
= &rec
->xdp_exception
[i
];
182 p
= &prev
->xdp_exception
[i
];
184 period
= calc_period(r
, p
);
185 pps
= calc_pps(r
, p
, period
);
188 printf(fmt
, action2str(i
), "Exception",
194 static __u64
get_key32_value64_percpu(int fd
, __u32 key
)
196 /* For percpu maps, userspace gets a value per possible CPU */
197 unsigned int nr_cpus
= bpf_num_possible_cpus();
198 __u64 values
[nr_cpus
];
202 if ((bpf_map_lookup_elem(fd
, &key
, values
)) != 0) {
204 "ERR: bpf_map_lookup_elem failed key:0x%X\n", key
);
208 /* Sum values from each CPU */
209 for (i
= 0; i
< nr_cpus
; i
++) {
215 static bool stats_collect(struct stats_record
*rec
)
220 /* TODO: Detect if someone unloaded the perf event_fd's, as
221 * this can happen by someone running perf-record -e
224 fd
= map_data
[0].fd
; /* map0: redirect_err_cnt */
225 for (i
= 0; i
< REDIR_RES_MAX
; i
++) {
226 rec
->xdp_redir
[i
].timestamp
= gettime();
227 rec
->xdp_redir
[i
].counter
= get_key32_value64_percpu(fd
, i
);
230 fd
= map_data
[1].fd
; /* map1: exception_cnt */
231 for (i
= 0; i
< XDP_ACTION_MAX
; i
++) {
232 rec
->xdp_exception
[i
].timestamp
= gettime();
233 rec
->xdp_exception
[i
].counter
= get_key32_value64_percpu(fd
, i
);
239 static void stats_poll(int interval
, bool err_only
)
241 struct stats_record rec
, prev
;
243 memset(&rec
, 0, sizeof(rec
));
245 /* Trick to pretty printf with thousands separators use %' */
246 setlocale(LC_NUMERIC
, "en_US");
250 printf("\n%s", __doc__
);
252 /* TODO Need more advanced stats on error types */
254 printf(" - Stats map0: %s\n", map_data
[0].name
);
255 printf(" - Stats map1: %s\n", map_data
[1].name
);
261 memcpy(&prev
, &rec
, sizeof(rec
));
263 stats_print_headers(err_only
);
264 stats_print(&rec
, &prev
, err_only
);
270 static void print_bpf_prog_info(void)
275 printf("Loaded BPF prog have %d bpf program(s)\n", prog_cnt
);
276 for (i
= 0; i
< prog_cnt
; i
++) {
277 printf(" - prog_fd[%d] = fd(%d)\n", i
, prog_fd
[i
]);
281 printf("Loaded BPF prog have %d map(s)\n", map_data_count
);
282 for (i
= 0; i
< map_data_count
; i
++) {
283 char *name
= map_data
[i
].name
;
284 int fd
= map_data
[i
].fd
;
286 printf(" - map_data[%d] = fd(%d) name:%s\n", i
, fd
, name
);
290 printf("Searching for (max:%d) event file descriptor(s)\n", prog_cnt
);
291 for (i
= 0; i
< prog_cnt
; i
++) {
292 if (event_fd
[i
] != -1)
293 printf(" - event_fd[%d] = fd(%d)\n", i
, event_fd
[i
]);
297 int main(int argc
, char **argv
)
299 struct rlimit r
= {RLIM_INFINITY
, RLIM_INFINITY
};
300 int longindex
= 0, opt
;
301 int ret
= EXIT_SUCCESS
;
302 char bpf_obj_file
[256];
304 /* Default settings: */
305 bool errors_only
= true;
308 snprintf(bpf_obj_file
, sizeof(bpf_obj_file
), "%s_kern.o", argv
[0]);
310 /* Parse commands line args */
311 while ((opt
= getopt_long(argc
, argv
, "h",
312 long_options
, &longindex
)) != -1) {
321 interval
= atoi(optarg
);
330 if (setrlimit(RLIMIT_MEMLOCK
, &r
)) {
331 perror("setrlimit(RLIMIT_MEMLOCK)");
335 if (load_bpf_file(bpf_obj_file
)) {
336 printf("ERROR - bpf_log_buf: %s", bpf_log_buf
);
340 printf("ERROR - load_bpf_file: %s\n", strerror(errno
));
345 print_bpf_prog_info();
348 /* Unload/stop tracepoint event by closing fd's */
350 /* The prog_fd[i] and event_fd[i] depend on the
351 * order the functions was defined in _kern.c
353 close(event_fd
[2]); /* tracepoint/xdp/xdp_redirect */
354 close(prog_fd
[2]); /* func: trace_xdp_redirect */
355 close(event_fd
[3]); /* tracepoint/xdp/xdp_redirect_map */
356 close(prog_fd
[3]); /* func: trace_xdp_redirect_map */
359 stats_poll(interval
, errors_only
);