app/procinfo: add collectd format and host id
[dpdk.git] / app / proc_info / main.c
1 /*
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdint.h>
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <inttypes.h>
40 #include <sys/queue.h>
41 #include <stdlib.h>
42 #include <getopt.h>
43 #include <unistd.h>
44
45 #include <rte_eal.h>
46 #include <rte_common.h>
47 #include <rte_debug.h>
48 #include <rte_ethdev.h>
49 #include <rte_malloc.h>
50 #include <rte_memory.h>
51 #include <rte_memzone.h>
52 #include <rte_launch.h>
53 #include <rte_tailq.h>
54 #include <rte_per_lcore.h>
55 #include <rte_lcore.h>
56 #include <rte_debug.h>
57 #include <rte_log.h>
58 #include <rte_atomic.h>
59 #include <rte_branch_prediction.h>
60 #include <rte_string_fns.h>
61
62 /* Maximum long option length for option parsing. */
63 #define MAX_LONG_OPT_SZ 64
64 #define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1
65
66 #define MAX_STRING_LEN 256
67
68 /**< mask of enabled ports */
69 static uint32_t enabled_port_mask;
70 /**< Enable stats. */
71 static uint32_t enable_stats;
72 /**< Enable xstats. */
73 static uint32_t enable_xstats;
74 /**< Enable collectd format*/
75 static uint32_t enable_collectd_format;
76 /**< FD to send collectd format messages to STDOUT*/
77 static int stdout_fd;
78 /**< Host id process is running on */
79 static char host_id[MAX_LONG_OPT_SZ];
80 /**< Enable stats reset. */
81 static uint32_t reset_stats;
82 /**< Enable xstats reset. */
83 static uint32_t reset_xstats;
84 /**< Enable memory info. */
85 static uint32_t mem_info;
86
87 /**< display usage */
88 static void
89 proc_info_usage(const char *prgname)
90 {
91         printf("%s [EAL options] -- -p PORTMASK\n"
92                 "  -m to display DPDK memory zones, segments and TAILQ information\n"
93                 "  -p PORTMASK: hexadecimal bitmask of ports to retrieve stats for\n"
94                 "  --stats: to display port statistics, enabled by default\n"
95                 "  --xstats: to display extended port statistics, disabled by "
96                         "default\n"
97                 "  --stats-reset: to reset port statistics\n"
98                 "  --xstats-reset: to reset port extended statistics\n"
99                 "  --collectd-format: to print statistics to STDOUT in expected by collectd format\n"
100                 "  --host-id STRING: host id used to identify the system process is running on\n",
101                 prgname);
102 }
103
104 /*
105  * Parse the portmask provided at run time.
106  */
107 static int
108 parse_portmask(const char *portmask)
109 {
110         char *end = NULL;
111         unsigned long pm;
112
113         errno = 0;
114
115         /* parse hexadecimal string */
116         pm = strtoul(portmask, &end, 16);
117         if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0') ||
118                 (errno != 0)) {
119                 printf("%s ERROR parsing the port mask\n", __func__);
120                 return -1;
121         }
122
123         if (pm == 0)
124                 return -1;
125
126         return pm;
127
128 }
129
130 static int
131 proc_info_preparse_args(int argc, char **argv)
132 {
133         char *prgname = argv[0];
134         int i;
135
136         for (i = 0; i < argc; i++) {
137                 /* Print stats or xstats to STDOUT in collectd format */
138                 if (!strncmp(argv[i], "--collectd-format", MAX_LONG_OPT_SZ)) {
139                         enable_collectd_format = 1;
140                         stdout_fd = dup(STDOUT_FILENO);
141                         close(STDOUT_FILENO);
142                 }
143                 if (!strncmp(argv[i], "--host-id", MAX_LONG_OPT_SZ)) {
144                         if ((i + 1) == argc) {
145                                 printf("Invalid host id or not specified\n");
146                                 proc_info_usage(prgname);
147                                 return -1;
148                         }
149                         strncpy(host_id, argv[i+1], sizeof(host_id));
150                 }
151         }
152
153         if (!strlen(host_id)) {
154                 int err = gethostname(host_id, MAX_LONG_OPT_SZ-1);
155
156                 if (err)
157                         strcpy(host_id, "unknown");
158         }
159
160         return 0;
161 }
162
163 /* Parse the argument given in the command line of the application */
164 static int
165 proc_info_parse_args(int argc, char **argv)
166 {
167         int opt;
168         int option_index;
169         char *prgname = argv[0];
170         static struct option long_option[] = {
171                 {"stats", 0, NULL, 0},
172                 {"stats-reset", 0, NULL, 0},
173                 {"xstats", 0, NULL, 0},
174                 {"xstats-reset", 0, NULL, 0},
175                 {"collectd-format", 0, NULL, 0},
176                 {"host-id", 0, NULL, 0},
177                 {NULL, 0, 0, 0}
178         };
179
180         if (argc == 1)
181                 proc_info_usage(prgname);
182
183         /* Parse command line */
184         while ((opt = getopt_long(argc, argv, "p:m",
185                         long_option, &option_index)) != EOF) {
186                 switch (opt) {
187                 /* portmask */
188                 case 'p':
189                         enabled_port_mask = parse_portmask(optarg);
190                         if (enabled_port_mask == 0) {
191                                 printf("invalid portmask\n");
192                                 proc_info_usage(prgname);
193                                 return -1;
194                         }
195                         break;
196                 case 'm':
197                         mem_info = 1;
198                         break;
199                 case 0:
200                         /* Print stats */
201                         if (!strncmp(long_option[option_index].name, "stats",
202                                         MAX_LONG_OPT_SZ))
203                                 enable_stats = 1;
204                         /* Print xstats */
205                         else if (!strncmp(long_option[option_index].name, "xstats",
206                                         MAX_LONG_OPT_SZ))
207                                 enable_xstats = 1;
208                         /* Reset stats */
209                         if (!strncmp(long_option[option_index].name, "stats-reset",
210                                         MAX_LONG_OPT_SZ))
211                                 reset_stats = 1;
212                         /* Reset xstats */
213                         else if (!strncmp(long_option[option_index].name, "xstats-reset",
214                                         MAX_LONG_OPT_SZ))
215                                 reset_xstats = 1;
216                         break;
217
218                 default:
219                         proc_info_usage(prgname);
220                         return -1;
221                 }
222         }
223         return 0;
224 }
225
226 static void
227 meminfo_display(void)
228 {
229         printf("----------- MEMORY_SEGMENTS -----------\n");
230         rte_dump_physmem_layout(stdout);
231         printf("--------- END_MEMORY_SEGMENTS ---------\n");
232
233         printf("------------ MEMORY_ZONES -------------\n");
234         rte_memzone_dump(stdout);
235         printf("---------- END_MEMORY_ZONES -----------\n");
236
237         printf("------------- TAIL_QUEUES -------------\n");
238         rte_dump_tailq(stdout);
239         printf("---------- END_TAIL_QUEUES ------------\n");
240 }
241
242 static void
243 nic_stats_display(uint8_t port_id)
244 {
245         struct rte_eth_stats stats;
246         uint8_t i;
247
248         static const char *nic_stats_border = "########################";
249
250         rte_eth_stats_get(port_id, &stats);
251         printf("\n  %s NIC statistics for port %-2d %s\n",
252                    nic_stats_border, port_id, nic_stats_border);
253
254         printf("  RX-packets: %-10"PRIu64"  RX-errors:  %-10"PRIu64
255                "  RX-bytes:  %-10"PRIu64"\n", stats.ipackets, stats.ierrors,
256                stats.ibytes);
257         printf("  RX-nombuf:  %-10"PRIu64"\n", stats.rx_nombuf);
258         printf("  TX-packets: %-10"PRIu64"  TX-errors:  %-10"PRIu64
259                "  TX-bytes:  %-10"PRIu64"\n", stats.opackets, stats.oerrors,
260                stats.obytes);
261
262         printf("\n");
263         for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) {
264                 printf("  Stats reg %2d RX-packets: %-10"PRIu64
265                        "  RX-errors: %-10"PRIu64
266                        "  RX-bytes: %-10"PRIu64"\n",
267                        i, stats.q_ipackets[i], stats.q_errors[i], stats.q_ibytes[i]);
268         }
269
270         printf("\n");
271         for (i = 0; i < RTE_ETHDEV_QUEUE_STAT_CNTRS; i++) {
272                 printf("  Stats reg %2d TX-packets: %-10"PRIu64
273                        "  TX-bytes: %-10"PRIu64"\n",
274                        i, stats.q_opackets[i], stats.q_obytes[i]);
275         }
276
277         printf("  %s############################%s\n",
278                    nic_stats_border, nic_stats_border);
279 }
280
281 static void
282 nic_stats_clear(uint8_t port_id)
283 {
284         printf("\n Clearing NIC stats for port %d\n", port_id);
285         rte_eth_stats_reset(port_id);
286         printf("\n  NIC statistics for port %d cleared\n", port_id);
287 }
288
289 static void collectd_resolve_cnt_type(char *cnt_type, size_t cnt_type_len,
290                                       const char *cnt_name) {
291         char *type_end = strrchr(cnt_name, '_');
292
293         if ((type_end != NULL) &&
294             (strncmp(cnt_name, "rx_", strlen("rx_")) == 0)) {
295                 if (strncmp(type_end, "_errors", strlen("_errors")) == 0)
296                         strncpy(cnt_type, "if_rx_errors", cnt_type_len);
297                 else if (strncmp(type_end, "_dropped", strlen("_dropped")) == 0)
298                         strncpy(cnt_type, "if_rx_dropped", cnt_type_len);
299                 else if (strncmp(type_end, "_bytes", strlen("_bytes")) == 0)
300                         strncpy(cnt_type, "if_rx_octets", cnt_type_len);
301                 else if (strncmp(type_end, "_packets", strlen("_packets")) == 0)
302                         strncpy(cnt_type, "if_rx_packets", cnt_type_len);
303                 else if (strncmp(type_end, "_placement",
304                                  strlen("_placement")) == 0)
305                         strncpy(cnt_type, "if_rx_errors", cnt_type_len);
306                 else if (strncmp(type_end, "_buff", strlen("_buff")) == 0)
307                         strncpy(cnt_type, "if_rx_errors", cnt_type_len);
308                 else
309                         /* Does not fit obvious type: use a more generic one */
310                         strncpy(cnt_type, "derive", cnt_type_len);
311         } else if ((type_end != NULL) &&
312                 (strncmp(cnt_name, "tx_", strlen("tx_"))) == 0) {
313                 if (strncmp(type_end, "_errors", strlen("_errors")) == 0)
314                         strncpy(cnt_type, "if_tx_errors", cnt_type_len);
315                 else if (strncmp(type_end, "_dropped", strlen("_dropped")) == 0)
316                         strncpy(cnt_type, "if_tx_dropped", cnt_type_len);
317                 else if (strncmp(type_end, "_bytes", strlen("_bytes")) == 0)
318                         strncpy(cnt_type, "if_tx_octets", cnt_type_len);
319                 else if (strncmp(type_end, "_packets", strlen("_packets")) == 0)
320                         strncpy(cnt_type, "if_tx_packets", cnt_type_len);
321                 else
322                         /* Does not fit obvious type: use a more generic one */
323                         strncpy(cnt_type, "derive", cnt_type_len);
324         } else if ((type_end != NULL) &&
325                    (strncmp(cnt_name, "flow_", strlen("flow_"))) == 0) {
326                 if (strncmp(type_end, "_filters", strlen("_filters")) == 0)
327                         strncpy(cnt_type, "operations", cnt_type_len);
328                 else if (strncmp(type_end, "_errors", strlen("_errors")) == 0)
329                         strncpy(cnt_type, "errors", cnt_type_len);
330                 else if (strncmp(type_end, "_filters", strlen("_filters")) == 0)
331                         strncpy(cnt_type, "filter_result", cnt_type_len);
332         } else if ((type_end != NULL) &&
333                    (strncmp(cnt_name, "mac_", strlen("mac_"))) == 0) {
334                 if (strncmp(type_end, "_errors", strlen("_errors")) == 0)
335                         strncpy(cnt_type, "errors", cnt_type_len);
336         } else {
337                 /* Does not fit obvious type, or strrchr error: */
338                 /* use a more generic type */
339                 strncpy(cnt_type, "derive", cnt_type_len);
340         }
341 }
342
343 static void
344 nic_xstats_display(uint8_t port_id)
345 {
346         struct rte_eth_xstat_name *xstats_names;
347         struct rte_eth_xstat *xstats;
348         int len, ret, i;
349         static const char *nic_stats_border = "########################";
350
351         len = rte_eth_xstats_get_names(port_id, NULL, 0);
352         if (len < 0) {
353                 printf("Cannot get xstats count\n");
354                 return;
355         }
356         xstats = malloc(sizeof(xstats[0]) * len);
357         if (xstats == NULL) {
358                 printf("Cannot allocate memory for xstats\n");
359                 return;
360         }
361
362         xstats_names = malloc(sizeof(struct rte_eth_xstat_name) * len);
363         if (xstats_names == NULL) {
364                 printf("Cannot allocate memory for xstat names\n");
365                 free(xstats);
366                 return;
367         }
368         if (len != rte_eth_xstats_get_names(
369                         port_id, xstats_names, len)) {
370                 printf("Cannot get xstat names\n");
371                 goto err;
372         }
373
374         printf("###### NIC extended statistics for port %-2d #########\n",
375                            port_id);
376         printf("%s############################\n",
377                            nic_stats_border);
378         ret = rte_eth_xstats_get(port_id, xstats, len);
379         if (ret < 0 || ret > len) {
380                 printf("Cannot get xstats\n");
381                 goto err;
382         }
383
384         for (i = 0; i < len; i++) {
385                 if (enable_collectd_format) {
386                         char counter_type[MAX_STRING_LEN];
387                         char buf[MAX_STRING_LEN];
388
389                         collectd_resolve_cnt_type(counter_type,
390                                                   sizeof(counter_type),
391                                                   xstats_names[i].name);
392                         sprintf(buf, "PUTVAL %s/dpdkstat-port.%u/%s-%s N:%"
393                                 PRIu64"\n", host_id, port_id, counter_type,
394                                 xstats_names[i].name, xstats[i].value);
395                         write(stdout_fd, buf, strlen(buf));
396                 } else {
397                         printf("%s: %"PRIu64"\n", xstats_names[i].name,
398                                xstats[i].value);
399                 }
400         }
401
402         printf("%s############################\n",
403                            nic_stats_border);
404 err:
405         free(xstats);
406         free(xstats_names);
407 }
408
409 static void
410 nic_xstats_clear(uint8_t port_id)
411 {
412         printf("\n Clearing NIC xstats for port %d\n", port_id);
413         rte_eth_xstats_reset(port_id);
414         printf("\n  NIC extended statistics for port %d cleared\n", port_id);
415 }
416
417 int
418 main(int argc, char **argv)
419 {
420         int ret;
421         int i;
422         char c_flag[] = "-c1";
423         char n_flag[] = "-n4";
424         char mp_flag[] = "--proc-type=secondary";
425         char *argp[argc + 3];
426         uint8_t nb_ports;
427
428         /* preparse app arguments */
429         ret = proc_info_preparse_args(argc, argv);
430         if (ret < 0) {
431                 printf("Failed to parse arguments\n");
432                 return -1;
433         }
434
435         argp[0] = argv[0];
436         argp[1] = c_flag;
437         argp[2] = n_flag;
438         argp[3] = mp_flag;
439
440         for (i = 1; i < argc; i++)
441                 argp[i + 3] = argv[i];
442
443         argc += 3;
444
445         ret = rte_eal_init(argc, argp);
446         if (ret < 0)
447                 rte_panic("Cannot init EAL\n");
448
449         argc -= ret;
450         argv += (ret - 3);
451
452         if (!rte_eal_primary_proc_alive(NULL))
453                 rte_exit(EXIT_FAILURE, "No primary DPDK process is running.\n");
454
455         /* parse app arguments */
456         ret = proc_info_parse_args(argc, argv);
457         if (ret < 0)
458                 rte_exit(EXIT_FAILURE, "Invalid argument\n");
459
460         if (mem_info) {
461                 meminfo_display();
462                 return 0;
463         }
464
465         nb_ports = rte_eth_dev_count();
466         if (nb_ports == 0)
467                 rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
468
469         /* If no port mask was specified*/
470         if (enabled_port_mask == 0)
471                 enabled_port_mask = 0xffff;
472
473         for (i = 0; i < nb_ports; i++) {
474                 if (enabled_port_mask & (1 << i)) {
475                         if (enable_stats)
476                                 nic_stats_display(i);
477                         else if (enable_xstats)
478                                 nic_xstats_display(i);
479                         else if (reset_stats)
480                                 nic_stats_clear(i);
481                         else if (reset_xstats)
482                                 nic_xstats_clear(i);
483                 }
484         }
485
486         return 0;
487 }