xdp_monitor_user.c 19 KB

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