LCOV - code coverage report
Current view: top level - lib/pcapng - rte_pcapng.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 215 286 75.2 %
Date: 2026-04-01 20:02:27 Functions: 10 11 90.9 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 115 231 49.8 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: BSD-3-Clause
       2                 :            :  * Copyright(c) 2019 Microsoft Corporation
       3                 :            :  */
       4                 :            : 
       5                 :            : #include <errno.h>
       6                 :            : #include <stdbool.h>
       7                 :            : #include <stdio.h>
       8                 :            : #include <stdlib.h>
       9                 :            : #include <string.h>
      10                 :            : #include <time.h>
      11                 :            : #include <unistd.h>
      12                 :            : 
      13                 :            : #ifndef RTE_EXEC_ENV_WINDOWS
      14                 :            : #include <net/if.h>
      15                 :            : #include <sys/uio.h>
      16                 :            : #endif
      17                 :            : 
      18                 :            : #include <bus_driver.h>
      19                 :            : #include <eal_export.h>
      20                 :            : #include <rte_common.h>
      21                 :            : #include <rte_cycles.h>
      22                 :            : #include <dev_driver.h>
      23                 :            : #include <rte_errno.h>
      24                 :            : #include <rte_ethdev.h>
      25                 :            : #include <rte_ether.h>
      26                 :            : #include <rte_mbuf.h>
      27                 :            : #include <rte_os_shim.h>
      28                 :            : #include <rte_pcapng.h>
      29                 :            : #include <rte_reciprocal.h>
      30                 :            : #include <rte_time.h>
      31                 :            : 
      32                 :            : #include "pcapng_proto.h"
      33                 :            : 
      34                 :            : /* conversion from DPDK speed to PCAPNG */
      35                 :            : #define PCAPNG_MBPS_SPEED 1000000ull
      36                 :            : 
      37                 :            : /* upper bound for strings in pcapng option data */
      38                 :            : #define PCAPNG_STR_MAX  UINT16_MAX
      39                 :            : 
      40                 :            : /*
      41                 :            :  * Converter from TSC values to nanoseconds since Unix epoch.
      42                 :            :  * Uses reciprocal multiply to avoid runtime division.
      43                 :            :  */
      44                 :            : struct tsc_clock {
      45                 :            :         uint64_t tsc_base;          /* TSC value at initialization. */
      46                 :            :         uint64_t ns_base;           /* Nanoseconds since epoch at init. */
      47                 :            :         struct rte_reciprocal_u64 tsc_hz_inv; /* Reciprocal of TSC frequency. */
      48                 :            :         uint32_t shift;             /* Pre-shift to avoid overflow. */
      49                 :            : };
      50                 :            : 
      51                 :            : /* Format of the capture file handle */
      52                 :            : struct rte_pcapng {
      53                 :            :         int  outfd;             /* output file */
      54                 :            :         unsigned int ports;     /* number of interfaces added */
      55                 :            : 
      56                 :            :         struct tsc_clock clock;
      57                 :            : 
      58                 :            :         /* DPDK port id to interface index in file */
      59                 :            :         uint32_t port_index[RTE_MAX_ETHPORTS];
      60                 :            : };
      61                 :            : 
      62                 :            : #ifdef RTE_EXEC_ENV_WINDOWS
      63                 :            : /*
      64                 :            :  * Windows does not have writev() call.
      65                 :            :  * Emulate this by copying to a new buffer.
      66                 :            :  * The copy is necessary since pcapng needs to be thread-safe
      67                 :            :  * and do atomic write operations.
      68                 :            :  */
      69                 :            : 
      70                 :            : #define IOV_MAX 128
      71                 :            : struct iovec {
      72                 :            :         void   *iov_base;
      73                 :            :         size_t  iov_len;
      74                 :            : };
      75                 :            : 
      76                 :            : static ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
      77                 :            : {
      78                 :            :         size_t bytes = 0;
      79                 :            :         uint8_t *ptr;
      80                 :            :         void *tmp_buf;
      81                 :            :         ssize_t ret;
      82                 :            :         int i;
      83                 :            : 
      84                 :            :         for (i = 0; i < iovcnt; i++)
      85                 :            :                 bytes += iov[i].iov_len;
      86                 :            : 
      87                 :            :         if (unlikely(bytes == 0))
      88                 :            :                 return 0;
      89                 :            : 
      90                 :            :         tmp_buf = malloc(bytes);
      91                 :            :         if (unlikely(tmp_buf == NULL)) {
      92                 :            :                 errno = ENOMEM;
      93                 :            :                 return -1;
      94                 :            :         }
      95                 :            : 
      96                 :            :         ptr = tmp_buf;
      97                 :            :         for (i = 0; i < iovcnt; i++) {
      98                 :            :                 rte_memcpy(ptr, iov[i].iov_base, iov[i].iov_len);
      99                 :            :                 ptr += iov[i].iov_len;
     100                 :            :         }
     101                 :            : 
     102                 :            :         ret = write(fd, tmp_buf, bytes);
     103                 :            :         free(tmp_buf);
     104                 :            :         return ret;
     105                 :            : }
     106                 :            : 
     107                 :            : #define IF_NAMESIZE     16
     108                 :            : /* compatibility wrapper because name is optional */
     109                 :            : #define if_indextoname(ifindex, ifname) NULL
     110                 :            : #endif
     111                 :            : 
     112                 :            : /*
     113                 :            :  * Initialize TSC-to-epoch-ns converter.
     114                 :            :  *
     115                 :            :  * Captures current TSC and system clock as a reference point.
     116                 :            :  */
     117                 :            : static int
     118                 :          3 : tsc_clock_init(struct tsc_clock *clk)
     119                 :            : {
     120                 :            :         struct timespec ts;
     121                 :            :         uint64_t cycles, tsc_hz, divisor;
     122                 :            :         uint32_t shift;
     123                 :            : 
     124                 :            :         memset(clk, 0, sizeof(*clk));
     125                 :            : 
     126                 :            :         /* If Hz is zero, something is seriously broken. */
     127                 :          3 :         tsc_hz = rte_get_tsc_hz();
     128         [ +  - ]:          3 :         if (tsc_hz == 0)
     129                 :            :                 return -1;
     130                 :            : 
     131                 :            :         /*
     132                 :            :          * Choose shift so (delta >> shift) * NSEC_PER_SEC fits in uint64_t.
     133                 :            :          * For typical GHz-range TSC and ~1s deltas this is 0.
     134                 :            :          */
     135                 :            :         shift = 0;
     136                 :            :         divisor = tsc_hz;
     137         [ -  + ]:          3 :         while (divisor > UINT64_MAX / NSEC_PER_SEC) {
     138                 :          0 :                 divisor >>= 1;
     139                 :          0 :                 shift++;
     140                 :            :         }
     141                 :            : 
     142                 :          3 :         clk->shift = shift;
     143                 :          3 :         clk->tsc_hz_inv = rte_reciprocal_value_u64(divisor);
     144                 :            : 
     145                 :            :         /* Sample TSC and system clock as close together as possible. */
     146                 :            :         cycles = rte_get_tsc_cycles();
     147                 :          3 :         clock_gettime(CLOCK_REALTIME, &ts);
     148                 :          3 :         clk->tsc_base = (cycles + rte_get_tsc_cycles()) / 2;
     149                 :          3 :         clk->ns_base = (uint64_t)ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
     150                 :            : 
     151                 :          3 :         return 0;
     152                 :            : }
     153                 :            : 
     154                 :            : /* Convert a TSC value to nanoseconds since Unix epoch. */
     155                 :            : static inline uint64_t
     156                 :       1794 : tsc_to_ns_epoch(const struct tsc_clock *clk, uint64_t tsc)
     157                 :            : {
     158                 :            :         uint64_t delta, ns;
     159                 :            : 
     160         [ +  + ]:       1794 :         if (unlikely(tsc < clk->tsc_base)) {
     161                 :          8 :                 delta = clk->tsc_base - tsc;
     162                 :          8 :                 ns = (delta >> clk->shift) * NSEC_PER_SEC;
     163                 :          8 :                 return clk->ns_base - rte_reciprocal_divide_u64(ns, &clk->tsc_hz_inv);
     164                 :            :         }
     165                 :            : 
     166                 :       1786 :         delta = tsc - clk->tsc_base;
     167                 :       1786 :         ns = (delta >> clk->shift) * NSEC_PER_SEC;
     168                 :       1786 :         return clk->ns_base + rte_reciprocal_divide_u64(ns, &clk->tsc_hz_inv);
     169                 :            : }
     170                 :            : 
     171                 :            : /* length of option including padding */
     172                 :            : static uint16_t pcapng_optlen(uint16_t len)
     173                 :            : {
     174                 :         88 :         return RTE_ALIGN(sizeof(struct pcapng_option) + len,
     175                 :            :                          sizeof(uint32_t));
     176                 :            : }
     177                 :            : 
     178                 :            : /* build TLV option and return location of next */
     179                 :            : static struct pcapng_option *
     180                 :            : pcapng_add_option(struct pcapng_option *popt, uint16_t code,
     181                 :            :                   const void *data, uint16_t len)
     182                 :            : {
     183                 :       1856 :         popt->code = code;
     184                 :       1856 :         popt->length = len;
     185                 :         43 :         if (len > 0)
     186         [ -  + ]:       1846 :                 memcpy(popt->data, data, len);
     187                 :            : 
     188                 :         51 :         return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len));
     189                 :            : }
     190                 :            : 
     191                 :            : /*
     192                 :            :  * Write required initial section header describing the capture
     193                 :            :  */
     194                 :            : static int
     195                 :          3 : pcapng_section_block(rte_pcapng_t *self,
     196                 :            :                     const char *os, const char *hw,
     197                 :            :                     const char *app, const char *comment)
     198                 :            : {
     199                 :            :         struct pcapng_section_header *hdr;
     200                 :            :         struct pcapng_option *opt;
     201                 :            :         uint32_t *buf;
     202                 :            :         uint32_t len;
     203                 :            :         ssize_t ret;
     204                 :            : 
     205                 :            :         len = sizeof(*hdr);
     206         [ -  + ]:          3 :         if (hw)
     207                 :          0 :                 len += pcapng_optlen(strlen(hw));
     208         [ -  + ]:          3 :         if (os)
     209                 :          0 :                 len += pcapng_optlen(strlen(os));
     210         [ +  - ]:          3 :         if (app)
     211                 :          3 :                 len += pcapng_optlen(strlen(app));
     212         [ -  + ]:          3 :         if (comment)
     213                 :          0 :                 len += pcapng_optlen(strlen(comment));
     214                 :            : 
     215                 :            :         /* reserve space for OPT_END */
     216                 :            :         len += pcapng_optlen(0);
     217                 :          3 :         len += sizeof(uint32_t);
     218                 :            : 
     219                 :          3 :         buf = malloc(len);
     220         [ +  - ]:          3 :         if (buf == NULL)
     221                 :            :                 return -ENOMEM;
     222                 :            : 
     223                 :            :         hdr = (struct pcapng_section_header *)buf;
     224                 :          3 :         *hdr = (struct pcapng_section_header) {
     225                 :            :                 .block_type = PCAPNG_SECTION_BLOCK,
     226                 :            :                 .block_length = len,
     227                 :            :                 .byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
     228                 :            :                 .major_version = PCAPNG_MAJOR_VERS,
     229                 :            :                 .minor_version = PCAPNG_MINOR_VERS,
     230                 :            :                 .section_length = UINT64_MAX,
     231                 :            :         };
     232                 :            : 
     233                 :            :         /* After the section header insert variable length options. */
     234                 :          3 :         opt = (struct pcapng_option *)(hdr + 1);
     235         [ -  + ]:          3 :         if (comment)
     236                 :          0 :                 opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
     237         [ #  # ]:          0 :                                         comment, strlen(comment));
     238         [ -  + ]:          3 :         if (hw)
     239                 :          0 :                 opt = pcapng_add_option(opt, PCAPNG_SHB_HARDWARE,
     240         [ #  # ]:          0 :                                         hw, strlen(hw));
     241         [ -  + ]:          3 :         if (os)
     242                 :          0 :                 opt = pcapng_add_option(opt, PCAPNG_SHB_OS,
     243         [ #  # ]:          0 :                                         os, strlen(os));
     244         [ +  - ]:          3 :         if (app)
     245                 :          3 :                 opt = pcapng_add_option(opt, PCAPNG_SHB_USERAPPL,
     246         [ +  - ]:          3 :                                         app, strlen(app));
     247                 :            : 
     248                 :            :         /* The standard requires last option to be OPT_END */
     249                 :            :         opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
     250                 :            : 
     251                 :            :         /* clone block_length after option */
     252                 :          3 :         memcpy(opt, &hdr->block_length, sizeof(uint32_t));
     253                 :            : 
     254                 :          3 :         ret = write(self->outfd, buf, len);
     255                 :          3 :         free(buf);
     256         [ -  + ]:          3 :         return ret < 0 ? -errno : 0;
     257                 :            : }
     258                 :            : 
     259                 :            : /* Write an interface block for a DPDK port */
     260                 :            : RTE_EXPORT_SYMBOL(rte_pcapng_add_interface)
     261                 :            : int
     262                 :          5 : rte_pcapng_add_interface(rte_pcapng_t *self, uint16_t port, uint16_t link_type,
     263                 :            :                          const char *ifname, const char *ifdescr,
     264                 :            :                          const char *filter)
     265                 :            : {
     266                 :            :         struct pcapng_interface_block *hdr;
     267                 :            :         struct rte_eth_dev_info dev_info;
     268                 :            :         struct rte_ether_addr *ea, macaddr;
     269                 :            :         const struct rte_device *dev;
     270                 :            :         struct rte_eth_link link;
     271                 :            :         struct pcapng_option *opt;
     272                 :          5 :         const uint8_t tsresol = 9;      /* nanosecond resolution */
     273                 :            :         uint32_t len;
     274                 :            :         uint32_t *buf;
     275                 :            :         char ifname_buf[IF_NAMESIZE];
     276                 :            :         char ifhw[256];
     277                 :          5 :         uint64_t speed = 0;
     278                 :            :         int ret;
     279                 :            : 
     280                 :          5 :         ret = rte_eth_dev_info_get(port, &dev_info);
     281         [ +  - ]:          5 :         if (ret < 0)
     282                 :            :                 return -1;  /* should be ret */
     283                 :            : 
     284                 :            :         /* make something like an interface name */
     285         [ +  + ]:          5 :         if (ifname == NULL) {
     286                 :            :                 /* Use kernel name if available */
     287                 :          4 :                 ifname = if_indextoname(dev_info.if_index, ifname_buf);
     288         [ +  - ]:          4 :                 if (ifname == NULL) {
     289                 :            :                         snprintf(ifname_buf, IF_NAMESIZE, "dpdk:%u", port);
     290                 :            :                         ifname = ifname_buf;
     291                 :            :                 }
     292         [ +  - ]:          1 :         } else if (strlen(ifname) > PCAPNG_STR_MAX) {
     293                 :            :                 return -1; /* ENAMETOOLONG */
     294                 :            :         }
     295                 :            : 
     296   [ +  +  +  -  :          5 :         if ((ifdescr && strlen(ifdescr) > PCAPNG_STR_MAX) ||
                   +  + ]
     297         [ +  - ]:          1 :             (filter && strlen(filter) > PCAPNG_STR_MAX))
     298                 :            :                 return -1; /* EINVAL */
     299                 :            : 
     300                 :            :         /* make a useful device hardware string */
     301                 :          5 :         dev = dev_info.device;
     302         [ +  - ]:          5 :         if (dev)
     303                 :            :                 snprintf(ifhw, sizeof(ifhw),
     304                 :          5 :                          "%s-%s", dev->bus->name, dev->name);
     305                 :            : 
     306                 :            :         /* DPDK reports in units of Mbps */
     307         [ +  - ]:          5 :         if (rte_eth_link_get(port, &link) == 0 &&
     308         [ -  + ]:          5 :             link.link_status == RTE_ETH_LINK_UP)
     309                 :          0 :                 speed = link.link_speed * PCAPNG_MBPS_SPEED;
     310                 :            : 
     311         [ +  - ]:          5 :         if (rte_eth_macaddr_get(port, &macaddr) < 0)
     312                 :            :                 ea = NULL;
     313                 :            :         else
     314                 :            :                 ea = &macaddr;
     315                 :            : 
     316                 :            :         /* Compute length of interface block options */
     317                 :            :         len = sizeof(*hdr);
     318                 :            : 
     319                 :            :         len += pcapng_optlen(sizeof(tsresol));  /* timestamp */
     320                 :          5 :         len += pcapng_optlen(strlen(ifname));   /* ifname */
     321                 :            : 
     322         [ +  + ]:          5 :         if (ifdescr)
     323                 :          1 :                 len += pcapng_optlen(strlen(ifdescr));
     324         [ +  - ]:          5 :         if (ea)
     325                 :          5 :                 len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */
     326         [ -  + ]:          5 :         if (speed != 0)
     327                 :          0 :                 len += pcapng_optlen(sizeof(uint64_t));
     328         [ +  + ]:          5 :         if (filter)
     329                 :          1 :                 len += pcapng_optlen(strlen(filter) + 1);
     330         [ +  - ]:          5 :         if (dev)
     331                 :          5 :                 len += pcapng_optlen(strlen(ifhw));
     332                 :            : 
     333                 :            :         len += pcapng_optlen(0);
     334                 :          5 :         len += sizeof(uint32_t);
     335                 :            : 
     336                 :          5 :         buf = malloc(len);
     337         [ +  - ]:          5 :         if (buf == NULL)
     338                 :            :                 return -1; /* ENOMEM */
     339                 :            : 
     340                 :            :         hdr = (struct pcapng_interface_block *)buf;
     341         [ +  - ]:          5 :         *hdr = (struct pcapng_interface_block) {
     342                 :            :                 .block_type = PCAPNG_INTERFACE_BLOCK,
     343                 :            :                 .link_type = link_type,
     344                 :            :                 .block_length = len,
     345                 :            :         };
     346                 :            : 
     347                 :            :         opt = (struct pcapng_option *)(hdr + 1);
     348                 :            :         opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL,
     349                 :            :                                 &tsresol, sizeof(tsresol));
     350                 :          5 :         opt = pcapng_add_option(opt, PCAPNG_IFB_NAME,
     351         [ +  - ]:          5 :                                 ifname, strlen(ifname));
     352         [ +  + ]:          5 :         if (ifdescr)
     353                 :          1 :                 opt = pcapng_add_option(opt, PCAPNG_IFB_DESCRIPTION,
     354         [ +  - ]:          1 :                                         ifdescr, strlen(ifdescr));
     355         [ +  - ]:          5 :         if (ea)
     356                 :            :                 opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR,
     357                 :            :                                         ea, RTE_ETHER_ADDR_LEN);
     358         [ -  + ]:          5 :         if (speed != 0)
     359                 :            :                 opt = pcapng_add_option(opt, PCAPNG_IFB_SPEED,
     360                 :            :                                          &speed, sizeof(uint64_t));
     361         [ +  - ]:          5 :         if (dev)
     362                 :          5 :                 opt = pcapng_add_option(opt, PCAPNG_IFB_HARDWARE,
     363         [ +  - ]:          5 :                                          ifhw, strlen(ifhw));
     364         [ +  + ]:          5 :         if (filter) {
     365                 :          1 :                 const size_t filter_len = strlen(filter) + 1;
     366                 :            : 
     367                 :          1 :                 opt->code = PCAPNG_IFB_FILTER;
     368                 :          1 :                 opt->length = filter_len;
     369                 :            :                 /* Encoding is that the first octet indicates string vs BPF */
     370                 :          1 :                 opt->data[0] = 0;
     371                 :          1 :                 memcpy(opt->data + 1, filter, strlen(filter));
     372                 :            : 
     373                 :          1 :                 opt = (struct pcapng_option *)((uint8_t *)opt + pcapng_optlen(filter_len));
     374                 :            :         }
     375                 :            : 
     376                 :            :         opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
     377                 :            : 
     378                 :            :         /* clone block_length after options */
     379                 :          5 :         memcpy(opt, &hdr->block_length, sizeof(uint32_t));
     380                 :            : 
     381                 :          5 :         ret = write(self->outfd, buf, len);
     382                 :          5 :         free(buf);
     383                 :            : 
     384                 :            :         /* remember the file index only after successful write */
     385         [ +  - ]:          5 :         if (ret > 0)
     386                 :          5 :                 self->port_index[port] = self->ports++;
     387                 :            : 
     388                 :            :         return ret;
     389                 :            : }
     390                 :            : 
     391                 :            : /*
     392                 :            :  * Write an Interface statistics block at the end of capture.
     393                 :            :  */
     394                 :            : RTE_EXPORT_SYMBOL(rte_pcapng_write_stats)
     395                 :            : ssize_t
     396                 :          2 : rte_pcapng_write_stats(rte_pcapng_t *self, uint16_t port_id,
     397                 :            :                        uint64_t ifrecv, uint64_t ifdrop,
     398                 :            :                        const char *comment)
     399                 :            : {
     400                 :            :         struct pcapng_statistics *hdr;
     401                 :            :         struct pcapng_option *opt;
     402                 :          2 :         uint64_t start_time = self->clock.ns_base;
     403                 :            :         uint64_t sample_time;
     404                 :            :         uint32_t optlen, len;
     405                 :            :         uint32_t *buf;
     406                 :            :         ssize_t ret;
     407                 :            : 
     408         [ -  + ]:          2 :         RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
     409                 :            : 
     410   [ +  +  +  - ]:          2 :         if (comment && strlen(comment) > PCAPNG_STR_MAX)
     411                 :            :                 return -EINVAL;
     412                 :            : 
     413                 :            :         optlen = 0;
     414                 :            : 
     415         [ +  - ]:          2 :         if (ifrecv != UINT64_MAX)
     416                 :            :                 optlen += pcapng_optlen(sizeof(ifrecv));
     417         [ +  - ]:          2 :         if (ifdrop != UINT64_MAX)
     418                 :          2 :                 optlen += pcapng_optlen(sizeof(ifdrop));
     419                 :            : 
     420         [ +  - ]:          2 :         if (start_time != 0)
     421                 :          2 :                 optlen += pcapng_optlen(sizeof(start_time));
     422                 :            : 
     423         [ +  + ]:          2 :         if (comment)
     424                 :          1 :                 optlen += pcapng_optlen(strlen(comment));
     425         [ +  - ]:          2 :         if (optlen != 0)
     426                 :          2 :                 optlen += pcapng_optlen(0);
     427                 :            : 
     428                 :          2 :         len = sizeof(*hdr) + optlen + sizeof(uint32_t);
     429                 :          2 :         buf = malloc(len);
     430         [ +  - ]:          2 :         if (buf == NULL)
     431                 :            :                 return -ENOMEM;
     432                 :            : 
     433                 :            :         hdr = (struct pcapng_statistics *)buf;
     434                 :          2 :         opt = (struct pcapng_option *)(hdr + 1);
     435                 :            : 
     436         [ +  + ]:          2 :         if (comment)
     437                 :          1 :                 opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT,
     438         [ +  - ]:          1 :                                         comment, strnlen(comment, PCAPNG_STR_MAX));
     439         [ +  - ]:          2 :         if (start_time != 0)
     440                 :            :                 opt = pcapng_add_option(opt, PCAPNG_ISB_STARTTIME,
     441                 :            :                                          &start_time, sizeof(start_time));
     442         [ +  - ]:          2 :         if (ifrecv != UINT64_MAX)
     443                 :            :                 opt = pcapng_add_option(opt, PCAPNG_ISB_IFRECV,
     444                 :            :                                 &ifrecv, sizeof(ifrecv));
     445         [ +  - ]:          2 :         if (ifdrop != UINT64_MAX)
     446                 :            :                 opt = pcapng_add_option(opt, PCAPNG_ISB_IFDROP,
     447                 :            :                                 &ifdrop, sizeof(ifdrop));
     448         [ +  - ]:          2 :         if (optlen != 0)
     449                 :            :                 opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0);
     450                 :            : 
     451                 :          2 :         hdr->block_type = PCAPNG_INTERFACE_STATS_BLOCK;
     452                 :          2 :         hdr->block_length = len;
     453                 :          2 :         hdr->interface_id = self->port_index[port_id];
     454                 :            : 
     455                 :          2 :         sample_time = tsc_to_ns_epoch(&self->clock, rte_get_tsc_cycles());
     456                 :          2 :         hdr->timestamp_hi = sample_time >> 32;
     457                 :          2 :         hdr->timestamp_lo = (uint32_t)sample_time;
     458                 :            : 
     459                 :            :         /* clone block_length after option */
     460                 :            :         memcpy(opt, &len, sizeof(uint32_t));
     461                 :            : 
     462                 :          2 :         ret = write(self->outfd, buf, len);
     463                 :          2 :         free(buf);
     464                 :          2 :         return ret;
     465                 :            : }
     466                 :            : 
     467                 :            : RTE_EXPORT_SYMBOL(rte_pcapng_mbuf_size)
     468                 :            : uint32_t
     469                 :          1 : rte_pcapng_mbuf_size(uint32_t length)
     470                 :            : {
     471                 :            :         /* The VLAN and EPB header must fit in the mbuf headroom. */
     472                 :            :         RTE_ASSERT(sizeof(struct pcapng_enhance_packet_block) +
     473                 :            :                    sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
     474                 :            : 
     475                 :            :         /* The flags and queue information are added at the end. */
     476                 :            :         return sizeof(struct rte_mbuf)
     477                 :          1 :                 + RTE_ALIGN(length, sizeof(uint32_t))
     478                 :            :                 + pcapng_optlen(sizeof(uint32_t)) /* flag option */
     479                 :            :                 + pcapng_optlen(sizeof(uint32_t)) /* queue option */
     480                 :          1 :                 + sizeof(uint32_t);               /*  length */
     481                 :            : }
     482                 :            : 
     483                 :            : /* More generalized version rte_vlan_insert() */
     484                 :            : static int
     485                 :          0 : pcapng_vlan_insert(struct rte_mbuf *m, uint16_t ether_type, uint16_t tci)
     486                 :            : {
     487                 :            :         struct rte_ether_hdr *nh, *oh;
     488                 :            :         struct rte_vlan_hdr *vh;
     489                 :            : 
     490   [ #  #  #  # ]:          0 :         if (!RTE_MBUF_DIRECT(m) || rte_mbuf_refcnt_read(m) > 1)
     491                 :          0 :                 return -EINVAL;
     492                 :            : 
     493         [ #  # ]:          0 :         if (rte_pktmbuf_data_len(m) < sizeof(*oh))
     494                 :            :                 return -EINVAL;
     495                 :            : 
     496         [ #  # ]:          0 :         oh = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
     497                 :            :         nh = (struct rte_ether_hdr *)
     498                 :            :                 rte_pktmbuf_prepend(m, sizeof(struct rte_vlan_hdr));
     499         [ #  # ]:          0 :         if (nh == NULL)
     500                 :          0 :                 return -ENOSPC;
     501                 :            : 
     502                 :            :         memmove(nh, oh, 2 * RTE_ETHER_ADDR_LEN);
     503         [ #  # ]:          0 :         nh->ether_type = rte_cpu_to_be_16(ether_type);
     504                 :            : 
     505                 :            :         vh = (struct rte_vlan_hdr *) (nh + 1);
     506         [ #  # ]:          0 :         vh->vlan_tci = rte_cpu_to_be_16(tci);
     507                 :            : 
     508                 :          0 :         return 0;
     509                 :            : }
     510                 :            : 
     511                 :            : /*
     512                 :            :  *   The mbufs created use the Pcapng standard enhanced packet  block.
     513                 :            :  *
     514                 :            :  *                         1                   2                   3
     515                 :            :  *     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     516                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     517                 :            :  *  0 |                    Block Type = 0x00000006                    |
     518                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     519                 :            :  *  4 |                      Block Total Length                       |
     520                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     521                 :            :  *  8 |                         Interface ID                          |
     522                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     523                 :            :  * 12 |                        Timestamp (High)                       |
     524                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     525                 :            :  * 16 |                        Timestamp (Low)                        |
     526                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     527                 :            :  * 20 |                    Captured Packet Length                     |
     528                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     529                 :            :  * 24 |                    Original Packet Length                     |
     530                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     531                 :            :  * 28 /                                                               /
     532                 :            :  *    /                          Packet Data                          /
     533                 :            :  *    /              variable length, padded to 32 bits               /
     534                 :            :  *    /                                                               /
     535                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     536                 :            :  *    |      Option Code = 0x0002     |     Option Length = 0x004     |
     537                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     538                 :            :  *    |              Flags (direction)                                |
     539                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     540                 :            :  *    |      Option Code = 0x0006     |     Option Length = 0x002     |
     541                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     542                 :            :  *    |              Queue id                                         |
     543                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     544                 :            :  *    |                      Block Total Length                       |
     545                 :            :  *    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     546                 :            :  */
     547                 :            : 
     548                 :            : /* Make a copy of original mbuf with pcapng header and options */
     549                 :            : RTE_EXPORT_SYMBOL(rte_pcapng_copy)
     550                 :            : struct rte_mbuf *
     551                 :       1792 : rte_pcapng_copy(uint16_t port_id, uint32_t queue,
     552                 :            :                 const struct rte_mbuf *md,
     553                 :            :                 struct rte_mempool *mp,
     554                 :            :                 uint32_t length,
     555                 :            :                 enum rte_pcapng_direction direction,
     556                 :            :                 const char *comment)
     557                 :            : {
     558                 :            :         struct pcapng_enhance_packet_block *epb;
     559                 :            :         uint32_t orig_len, pkt_len, padding, flags;
     560                 :            :         struct pcapng_option *opt;
     561                 :            :         uint64_t timestamp;
     562                 :            :         uint16_t optlen;
     563                 :            :         struct rte_mbuf *mc;
     564                 :            :         bool rss_hash;
     565                 :            : 
     566                 :            : #ifdef RTE_LIBRTE_ETHDEV_DEBUG
     567                 :            :         /*
     568                 :            :          * Since this function is used in the fast path for packet capture
     569                 :            :          * skip argument validation checks unless debug is enabled.
     570                 :            :          */
     571                 :            :         RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, NULL);
     572                 :            : #endif
     573                 :       1792 :         orig_len = rte_pktmbuf_pkt_len(md);
     574                 :            : 
     575                 :            :         /* Take snapshot of the data */
     576                 :       1792 :         mc = rte_pktmbuf_copy(md, mp, 0, length);
     577         [ +  - ]:       1792 :         if (unlikely(mc == NULL))
     578                 :            :                 return NULL;
     579                 :            : 
     580                 :            :         /* Expand any offloaded VLAN information */
     581         [ +  - ]:       1792 :         if ((direction == RTE_PCAPNG_DIRECTION_IN &&
     582   [ +  -  -  + ]:       1792 :              (md->ol_flags & RTE_MBUF_F_RX_VLAN_STRIPPED)) ||
     583                 :          0 :             (direction == RTE_PCAPNG_DIRECTION_OUT &&
     584         [ #  # ]:          0 :              (md->ol_flags & RTE_MBUF_F_TX_VLAN))) {
     585         [ #  # ]:          0 :                 if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_VLAN,
     586                 :          0 :                                        md->vlan_tci) != 0)
     587                 :          0 :                         goto fail;
     588                 :            :         }
     589                 :            : 
     590         [ +  - ]:       1792 :         if ((direction == RTE_PCAPNG_DIRECTION_IN &&
     591   [ +  -  -  + ]:       1792 :              (md->ol_flags & RTE_MBUF_F_RX_QINQ_STRIPPED)) ||
     592                 :          0 :             (direction == RTE_PCAPNG_DIRECTION_OUT &&
     593         [ #  # ]:          0 :              (md->ol_flags & RTE_MBUF_F_TX_QINQ))) {
     594         [ #  # ]:          0 :                 if (pcapng_vlan_insert(mc, RTE_ETHER_TYPE_QINQ,
     595                 :          0 :                                        md->vlan_tci_outer) != 0)
     596                 :          0 :                         goto fail;
     597                 :            :         }
     598                 :            : 
     599                 :            :         /* record HASH on incoming packets */
     600         [ +  - ]:       1792 :         rss_hash = (direction == RTE_PCAPNG_DIRECTION_IN &&
     601         [ +  - ]:       1792 :                     (md->ol_flags & RTE_MBUF_F_RX_RSS_HASH));
     602                 :            : 
     603                 :            :         /* pad the packet to 32 bit boundary */
     604                 :       1792 :         pkt_len = rte_pktmbuf_pkt_len(mc);
     605                 :       1792 :         padding = RTE_ALIGN(pkt_len, sizeof(uint32_t)) - pkt_len;
     606         [ +  + ]:       1792 :         if (padding > 0) {
     607                 :       1334 :                 void *tail = rte_pktmbuf_append(mc, padding);
     608                 :            : 
     609         [ -  + ]:       1334 :                 if (tail == NULL)
     610                 :          0 :                         goto fail;
     611                 :       1334 :                 memset(tail, 0, padding);
     612                 :            :         }
     613                 :            : 
     614                 :            :         optlen = pcapng_optlen(sizeof(flags));
     615                 :            :         optlen += pcapng_optlen(sizeof(queue));
     616         [ -  + ]:       1792 :         if (rss_hash)
     617                 :            :                 optlen += pcapng_optlen(sizeof(uint8_t) + sizeof(uint32_t));
     618                 :            : 
     619                 :            :         /* Comment is silently truncated if necessary */
     620         [ +  + ]:       1792 :         if (comment)
     621                 :         28 :                 optlen += pcapng_optlen(strnlen(comment, PCAPNG_STR_MAX));
     622                 :            : 
     623                 :            :         /*
     624                 :            :          * Try to put options at the end of this mbuf.
     625                 :            :          * If not extend the mbuf by adding another segment.
     626                 :            :          */
     627                 :       1792 :         opt = (struct pcapng_option *)rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
     628         [ +  + ]:       1792 :         if (unlikely(opt == NULL)) {
     629                 :         21 :                 struct rte_mbuf *ml = rte_pktmbuf_alloc(mp);
     630                 :            : 
     631         [ -  + ]:         21 :                 if (unlikely(ml == NULL))
     632                 :          0 :                         goto fail;      /* mbuf pool is empty */
     633                 :            : 
     634         [ -  + ]:         21 :                 if (unlikely(rte_pktmbuf_chain(mc, ml) != 0)) {
     635                 :          0 :                         rte_pktmbuf_free(ml);
     636                 :          0 :                         goto fail;      /* too many segments in the mbuf */
     637                 :            :                 }
     638                 :            : 
     639                 :            :                 opt = (struct pcapng_option *)rte_pktmbuf_append(mc, optlen + sizeof(uint32_t));
     640         [ -  + ]:         21 :                 if (unlikely(opt == NULL))
     641                 :          0 :                         goto fail;      /* additional segment and still no space */
     642                 :            :         }
     643                 :            : 
     644      [ +  -  - ]:       1792 :         switch (direction) {
     645                 :       1792 :         case RTE_PCAPNG_DIRECTION_IN:
     646                 :       1792 :                 flags = PCAPNG_IFB_INBOUND;
     647                 :       1792 :                 break;
     648                 :          0 :         case RTE_PCAPNG_DIRECTION_OUT:
     649                 :          0 :                 flags = PCAPNG_IFB_OUTBOUND;
     650                 :          0 :                 break;
     651                 :          0 :         default:
     652                 :          0 :                 flags = 0;
     653                 :            :         }
     654                 :            : 
     655                 :            :         opt = pcapng_add_option(opt, PCAPNG_EPB_FLAGS,
     656                 :            :                                 &flags, sizeof(flags));
     657                 :            : 
     658                 :            :         opt = pcapng_add_option(opt, PCAPNG_EPB_QUEUE,
     659                 :            :                                 &queue, sizeof(queue));
     660                 :            : 
     661         [ -  + ]:       1792 :         if (rss_hash) {
     662                 :            :                 uint8_t hash_opt[5];
     663                 :            : 
     664                 :            :                 /* The algorithm could be something else if
     665                 :            :                  * using rte_flow_action_rss; but the current API does not
     666                 :            :                  * have a way for ethdev to report  this on a per-packet basis.
     667                 :            :                  */
     668                 :          0 :                 hash_opt[0] = PCAPNG_HASH_TOEPLITZ;
     669                 :            : 
     670                 :            :                 memcpy(&hash_opt[1], &md->hash.rss, sizeof(uint32_t));
     671                 :            :                 opt = pcapng_add_option(opt, PCAPNG_EPB_HASH,
     672                 :            :                                         &hash_opt, sizeof(hash_opt));
     673                 :            :         }
     674                 :            : 
     675         [ +  + ]:       1792 :         if (comment)
     676                 :         28 :                 opt = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, comment,
     677         [ +  - ]:         28 :                                         strnlen(comment, PCAPNG_STR_MAX));
     678                 :            : 
     679                 :            :         /* Note: END_OPT necessary here. Wireshark doesn't do it. */
     680                 :            : 
     681                 :            :         /* Add PCAPNG packet header */
     682                 :            :         epb = (struct pcapng_enhance_packet_block *)
     683                 :            :                 rte_pktmbuf_prepend(mc, sizeof(*epb));
     684         [ -  + ]:       1792 :         if (unlikely(epb == NULL))
     685                 :          0 :                 goto fail;
     686                 :            : 
     687                 :       1792 :         epb->block_type = PCAPNG_ENHANCED_PACKET_BLOCK;
     688                 :       1792 :         epb->block_length = rte_pktmbuf_pkt_len(mc);
     689                 :            : 
     690                 :            :         /* Interface index is filled in later during write */
     691                 :       1792 :         mc->port = port_id;
     692                 :            : 
     693                 :            :         /* Put timestamp in cycles here - adjust in packet write */
     694                 :            :         timestamp = rte_get_tsc_cycles();
     695                 :       1792 :         epb->timestamp_hi = timestamp >> 32;
     696                 :       1792 :         epb->timestamp_lo = (uint32_t)timestamp;
     697                 :       1792 :         epb->capture_length = pkt_len;
     698                 :       1792 :         epb->original_length = orig_len;
     699                 :            : 
     700                 :            :         /* set trailer of block length */
     701                 :       1792 :         *(uint32_t *)opt = epb->block_length;
     702                 :            : 
     703                 :       1792 :         return mc;
     704                 :            : 
     705                 :          0 : fail:
     706                 :          0 :         rte_pktmbuf_free(mc);
     707                 :          0 :         return NULL;
     708                 :            : }
     709                 :            : 
     710                 :            : /* Write pre-formatted packets to file. */
     711                 :            : RTE_EXPORT_SYMBOL(rte_pcapng_write_packets)
     712                 :            : ssize_t
     713                 :         97 : rte_pcapng_write_packets(rte_pcapng_t *self,
     714                 :            :                          struct rte_mbuf *pkts[], uint16_t nb_pkts)
     715                 :            : {
     716                 :            :         struct iovec iov[IOV_MAX];
     717                 :            :         unsigned int i, cnt = 0;
     718                 :            :         ssize_t ret, total = 0;
     719                 :            : 
     720         [ +  + ]:       1889 :         for (i = 0; i < nb_pkts; i++) {
     721                 :       1792 :                 struct rte_mbuf *m = pkts[i];
     722                 :            :                 struct pcapng_enhance_packet_block *epb;
     723                 :            :                 uint64_t cycles, timestamp;
     724                 :            : 
     725                 :            :                 /* sanity check that is really a pcapng mbuf */
     726                 :       1792 :                 epb = rte_pktmbuf_mtod(m, struct pcapng_enhance_packet_block *);
     727   [ +  -  -  + ]:       1792 :                 if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK ||
     728                 :            :                              epb->block_length != rte_pktmbuf_pkt_len(m))) {
     729                 :          0 :                         rte_errno = EINVAL;
     730                 :          0 :                         return -1;
     731                 :            :                 }
     732                 :            : 
     733                 :            :                 /* check that this interface was added. */
     734                 :       1792 :                 epb->interface_id = self->port_index[m->port];
     735         [ -  + ]:       1792 :                 if (unlikely(epb->interface_id > RTE_MAX_ETHPORTS)) {
     736                 :          0 :                         rte_errno = EINVAL;
     737                 :          0 :                         return -1;
     738                 :            :                 }
     739                 :            : 
     740                 :            :                 /*
     741                 :            :                  * When data is captured by pcapng_copy the current TSC is stored.
     742                 :            :                  * Adjust the value recorded in file to PCAP epoch units.
     743                 :            :                  */
     744                 :       1792 :                 cycles = (uint64_t)epb->timestamp_hi << 32;
     745                 :       1792 :                 cycles += epb->timestamp_lo;
     746                 :       1792 :                 timestamp = tsc_to_ns_epoch(&self->clock, cycles);
     747                 :       1792 :                 epb->timestamp_hi = timestamp >> 32;
     748                 :       1792 :                 epb->timestamp_lo = (uint32_t)timestamp;
     749                 :            : 
     750                 :            :                 /*
     751                 :            :                  * Handle case of highly fragmented and large burst size
     752                 :            :                  * Note: this assumes that max segments per mbuf < IOV_MAX
     753                 :            :                  */
     754         [ -  + ]:       1792 :                 if (unlikely(cnt + m->nb_segs >= IOV_MAX)) {
     755                 :          0 :                         ret = writev(self->outfd, iov, cnt);
     756         [ #  # ]:          0 :                         if (unlikely(ret < 0)) {
     757                 :          0 :                                 rte_errno = errno;
     758                 :          0 :                                 return -1;
     759                 :            :                         }
     760                 :          0 :                         total += ret;
     761                 :            :                         cnt = 0;
     762                 :            :                 }
     763                 :            : 
     764                 :            :                 /*
     765                 :            :                  * The DPDK port is recorded during pcapng_copy.
     766                 :            :                  * Map that to PCAPNG interface in file.
     767                 :            :                  */
     768                 :            :                 do {
     769                 :       1833 :                         iov[cnt].iov_base = rte_pktmbuf_mtod(m, void *);
     770                 :       1833 :                         iov[cnt].iov_len = rte_pktmbuf_data_len(m);
     771                 :       1833 :                         ++cnt;
     772         [ +  + ]:       1833 :                 } while ((m = m->next));
     773                 :            :         }
     774                 :            : 
     775                 :         97 :         ret = writev(self->outfd, iov, cnt);
     776         [ -  + ]:         97 :         if (unlikely(ret < 0)) {
     777                 :          0 :                 rte_errno = errno;
     778                 :          0 :                 return -1;
     779                 :            :         }
     780                 :         97 :         return total + ret;
     781                 :            : }
     782                 :            : 
     783                 :            : /* Create new pcapng writer handle */
     784                 :            : RTE_EXPORT_SYMBOL(rte_pcapng_fdopen)
     785                 :            : rte_pcapng_t *
     786                 :          3 : rte_pcapng_fdopen(int fd,
     787                 :            :                   const char *osname, const char *hardware,
     788                 :            :                   const char *appname, const char *comment)
     789                 :            : {
     790                 :            :         unsigned int i;
     791                 :            :         rte_pcapng_t *self;
     792                 :            :         int ret;
     793                 :            : 
     794   [ -  +  -  -  :          3 :         if ((osname && strlen(osname) > PCAPNG_STR_MAX) ||
                   -  + ]
     795   [ -  -  +  - ]:          3 :             (hardware && strlen(hardware) > PCAPNG_STR_MAX) ||
     796   [ +  -  -  + ]:          3 :             (appname && strlen(appname) > PCAPNG_STR_MAX) ||
     797         [ #  # ]:          0 :             (comment && strlen(comment) > PCAPNG_STR_MAX)) {
     798                 :          0 :                 rte_errno = EINVAL;
     799                 :          0 :                 return NULL;
     800                 :            :         }
     801                 :            : 
     802                 :          3 :         self = malloc(sizeof(*self));
     803         [ -  + ]:          3 :         if (!self) {
     804                 :          0 :                 rte_errno = ENOMEM;
     805                 :          0 :                 return NULL;
     806                 :            :         }
     807                 :            : 
     808                 :          3 :         self->outfd = fd;
     809                 :          3 :         self->ports = 0;
     810                 :            : 
     811         [ -  + ]:          3 :         if (tsc_clock_init(&self->clock) < 0) {
     812                 :          0 :                 rte_errno = ENODEV;
     813                 :          0 :                 goto fail;
     814                 :            :         }
     815                 :            : 
     816         [ +  + ]:         99 :         for (i = 0; i < RTE_MAX_ETHPORTS; i++)
     817                 :         96 :                 self->port_index[i] = UINT32_MAX;
     818                 :            : 
     819                 :          3 :         ret = pcapng_section_block(self, osname, hardware, appname, comment);
     820         [ -  + ]:          3 :         if (ret < 0) {
     821                 :          0 :                 rte_errno = -ret;
     822                 :          0 :                 goto fail;
     823                 :            :         }
     824                 :            : 
     825                 :            :         return self;
     826                 :          0 : fail:
     827                 :          0 :         free(self);
     828                 :          0 :         return NULL;
     829                 :            : }
     830                 :            : 
     831                 :            : RTE_EXPORT_SYMBOL(rte_pcapng_close)
     832                 :            : void
     833                 :          3 : rte_pcapng_close(rte_pcapng_t *self)
     834                 :            : {
     835         [ +  - ]:          3 :         if (self) {
     836                 :          3 :                 close(self->outfd);
     837                 :          3 :                 free(self);
     838                 :            :         }
     839                 :          3 : }

Generated by: LCOV version 1.14