<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/26454">View Change</a></p><div style="white-space:pre-wrap">Approvals:
laforge: Looks good to me, approved
Jenkins Builder: Verified
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">select: gather statistics for TCP connections<br><br>osmocom applications are deployed in a variety of different situations.<br>Dependung on the medium that interconnects the network components<br>unexpected behaviour may occur. To debug problems with the<br>interconnection between network components it might help to monitor the<br>health of the related TCP connections.<br><br>Change-Id: I1416f95aff2adcf13689646b7574845de169fa3d<br>Related: SYS#5701<br>---<br>M configure.ac<br>M include/Makefile.am<br>A include/osmocom/core/stats_tcp.h<br>M src/Makefile.am<br>M src/select.c<br>M src/stats.c<br>A src/stats_tcp.c<br>M src/vty/stats_vty.c<br>M tests/stats/stats_vty_test.vty<br>9 files changed, 406 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configure.ac b/configure.ac</span><br><span>index a69f793..8fb299b 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -516,6 +516,28 @@</span><br><span> CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports],</span><br><span> [Runtime SIMD detection will be disabled])</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+dnl There are some members in struct tcp_info that might not exist on all linux versions</span><br><span style="color: hsl(120, 100%, 40%);">+AC_CHECK_MEMBER([struct tcp_info.tcpi_rwnd_limited],</span><br><span style="color: hsl(120, 100%, 40%);">+ AC_DEFINE([HAVE_TCP_INFO_TCPI_RWND_LIMITED],</span><br><span style="color: hsl(120, 100%, 40%);">+ [1],</span><br><span style="color: hsl(120, 100%, 40%);">+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_rwnd_limited member in struct tcp_info]),</span><br><span style="color: hsl(120, 100%, 40%);">+ [],</span><br><span style="color: hsl(120, 100%, 40%);">+ [#include <linux/tcp.h>])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AC_CHECK_MEMBER([struct tcp_info.tcpi_sndbuf_limited],</span><br><span style="color: hsl(120, 100%, 40%);">+ AC_DEFINE([HAVE_TCP_INFO_TCPI_SNDBUF_LIMITED],</span><br><span style="color: hsl(120, 100%, 40%);">+ [1],</span><br><span style="color: hsl(120, 100%, 40%);">+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_sndbuf_limited member in struct tcp_info]),</span><br><span style="color: hsl(120, 100%, 40%);">+ [],</span><br><span style="color: hsl(120, 100%, 40%);">+ [#include <linux/tcp.h>])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AC_CHECK_MEMBER([struct tcp_info.tcpi_reord_seen],</span><br><span style="color: hsl(120, 100%, 40%);">+ AC_DEFINE([HAVE_TCP_INFO_TCPI_REORD_SEEN],</span><br><span style="color: hsl(120, 100%, 40%);">+ [1],</span><br><span style="color: hsl(120, 100%, 40%);">+ [Define to 1 if your <linux/tcp.h> header file have the tcpi_reord_seen member in struct tcp_info]),</span><br><span style="color: hsl(120, 100%, 40%);">+ [],</span><br><span style="color: hsl(120, 100%, 40%);">+ [#include <linux/tcp.h>])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> AC_MSG_RESULT([CFLAGS="$CFLAGS"])</span><br><span> AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])</span><br><span> </span><br><span>diff --git a/include/Makefile.am b/include/Makefile.am</span><br><span>index eab0489..a234014 100644</span><br><span>--- a/include/Makefile.am</span><br><span>+++ b/include/Makefile.am</span><br><span>@@ -46,6 +46,7 @@</span><br><span> osmocom/core/process.h \</span><br><span> osmocom/core/rate_ctr.h \</span><br><span> osmocom/core/stat_item.h \</span><br><span style="color: hsl(120, 100%, 40%);">+ osmocom/core/stats_tcp.h \</span><br><span> osmocom/core/select.h \</span><br><span> osmocom/core/sercomm.h \</span><br><span> osmocom/core/signal.h \</span><br><span>diff --git a/include/osmocom/core/stats_tcp.h b/include/osmocom/core/stats_tcp.h</span><br><span>new file mode 100644</span><br><span>index 0000000..6b9657a</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/core/stats_tcp.h</span><br><span>@@ -0,0 +1,16 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define TCP_STATS_DEFAULT_INTERVAL 0 /* secs */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TCP_STATS_DEFAULT_BATCH_SIZE 1 /* sockets per interval */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tcp_stats_config {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* poll interval in seconds, use osmo_stats_tcp_set_interval() to manipulate this value */</span><br><span style="color: hsl(120, 100%, 40%);">+ int interval;</span><br><span style="color: hsl(120, 100%, 40%);">+ /* specify how many sockets are processed when the interval timer expires */</span><br><span style="color: hsl(120, 100%, 40%);">+ int batch_size;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+extern struct osmo_tcp_stats_config *osmo_tcp_stats_config;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_stats_tcp_osmo_fd_register(const struct osmo_fd *fd, const char *name);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_stats_tcp_osmo_fd_unregister(const struct osmo_fd *fd);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_stats_tcp_set_interval(int interval);</span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index 4bda456..5777c31 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -22,6 +22,7 @@</span><br><span> conv.c application.c rbtree.c strrb.c \</span><br><span> loggingrb.c crc8gen.c crc16gen.c crc32gen.c crc64gen.c \</span><br><span> macaddr.c stat_item.c stats.c stats_statsd.c prim.c \</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp.c \</span><br><span> conv_acc.c conv_acc_generic.c sercomm.c prbs.c \</span><br><span> isdnhdlc.c \</span><br><span> tdef.c \</span><br><span>diff --git a/src/select.c b/src/select.c</span><br><span>index 590c3db..b48d13e 100644</span><br><span>--- a/src/select.c</span><br><span>+++ b/src/select.c</span><br><span>@@ -33,6 +33,8 @@</span><br><span> #include <osmocom/core/logging.h></span><br><span> #include <osmocom/core/talloc.h></span><br><span> #include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/stat_item.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/stats_tcp.h></span><br><span> </span><br><span> #include "../config.h"</span><br><span> </span><br><span>@@ -181,6 +183,9 @@</span><br><span> #ifndef FORCE_IO_SELECT</span><br><span> g_poll.num_registered--;</span><br><span> #endif /* FORCE_IO_SELECT */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If existent, free any statistical data */</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stats_tcp_osmo_fd_unregister(fd);</span><br><span> }</span><br><span> </span><br><span> /*! Close a file descriptor, mark it as closed + unregister from select loop abstraction</span><br><span>diff --git a/src/stats.c b/src/stats.c</span><br><span>index f8c91fd..4267284 100644</span><br><span>--- a/src/stats.c</span><br><span>+++ b/src/stats.c</span><br><span>@@ -85,6 +85,7 @@</span><br><span> #include <osmocom/core/select.h></span><br><span> #include <osmocom/core/counter.h></span><br><span> #include <osmocom/core/msgb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/stats_tcp.h></span><br><span> </span><br><span> #ifdef HAVE_SYSTEMTAP</span><br><span> /* include the generated probes header and put markers in code */</span><br><span>@@ -240,6 +241,10 @@</span><br><span> osmo_stats_ctx = ctx;</span><br><span> is_initialised = 1;</span><br><span> start_timer();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Make sure that the tcp-stats interval timer also runs at its</span><br><span style="color: hsl(120, 100%, 40%);">+ * preconfigured rate. The vty might change this setting later. */</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stats_tcp_set_interval(osmo_tcp_stats_config->interval);</span><br><span> }</span><br><span> </span><br><span> /*! Find a stats_reporter of given \a type and \a name.</span><br><span>diff --git a/src/stats_tcp.c b/src/stats_tcp.c</span><br><span>new file mode 100644</span><br><span>index 0000000..fffb10a</span><br><span>--- /dev/null</span><br><span>+++ b/src/stats_tcp.c</span><br><span>@@ -0,0 +1,322 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2021 by sysmocom - s.f.m.c. GmbH</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Philipp Maier <pmaier@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \addtogroup stats</span><br><span style="color: hsl(120, 100%, 40%);">+ * @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file stats_tcp.c */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "config.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#if !defined(EMBEDDED)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/types.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/stat.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <sys/socket.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <netinet/in.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <netinet/ip.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <linux/tcp.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <pthread.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/select.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/talloc.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/timer.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/stat_item.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/stats.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/socket.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/stats_tcp.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_tcp_stats_config s_tcp_stats_config = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .interval = TCP_STATS_DEFAULT_INTERVAL,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tcp_stats_config *osmo_tcp_stats_config = &s_tcp_stats_config;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_timer_list stats_tcp_poll_timer;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static LLIST_HEAD(stats_tcp);</span><br><span style="color: hsl(120, 100%, 40%);">+static struct stats_tcp_entry *stats_tcp_entry_cur;</span><br><span style="color: hsl(120, 100%, 40%);">+pthread_mutex_t stats_tcp_lock;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct stats_tcp_entry {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct osmo_fd *fd;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_stat_item_group *stats_tcp;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS_TCP_UNACKED,</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS_TCP_LOST,</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS_TCP_RETRANS,</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS_TCP_RTT,</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS_TCP_RCV_RTT,</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS_TCP_NOTSENT_BYTES,</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS_TCP_RWND_LIMITED,</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS_TCP_SNDBUF_LIMITED,</span><br><span style="color: hsl(120, 100%, 40%);">+ STATS_TCP_REORD_SEEN,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_stat_item_desc stats_tcp_item_desc[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [STATS_TCP_UNACKED] = { "tcp:unacked", "unacknowledged packets", "", 60, 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+ [STATS_TCP_LOST] = { "tcp:lost", "lost packets", "", 60, 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+ [STATS_TCP_RETRANS] = { "tcp:retrans", "retransmitted packets", "", 60, 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+ [STATS_TCP_RTT] = { "tcp:rtt", "roundtrip-time", "", 60, 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+ [STATS_TCP_RCV_RTT] = { "tcp:rcv_rtt", "roundtrip-time (receive)", "", 60, 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+ [STATS_TCP_NOTSENT_BYTES] = { "tcp:notsent_bytes", "bytes not yet sent", "", 60, 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+ [STATS_TCP_RWND_LIMITED] = { "tcp:rwnd_limited", "time (usec) limited by receive window", "", 60, 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+ [STATS_TCP_SNDBUF_LIMITED] = { "tcp:sndbuf_limited", "Time (usec) limited by send buffer", "", 60, 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+ [STATS_TCP_REORD_SEEN] = { "tcp:sndbuf_limited", "reordering events seen", "", 60, 0 },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_stat_item_group_desc stats_tcp_desc = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .group_name_prefix = "tcp",</span><br><span style="color: hsl(120, 100%, 40%);">+ .group_description = "stats tcp",</span><br><span style="color: hsl(120, 100%, 40%);">+ .class_id = OSMO_STATS_CLASS_GLOBAL,</span><br><span style="color: hsl(120, 100%, 40%);">+ .num_items = ARRAY_SIZE(stats_tcp_item_desc),</span><br><span style="color: hsl(120, 100%, 40%);">+ .item_desc = stats_tcp_item_desc,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void fill_stats(struct stats_tcp_entry *stats_tcp_entry)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct tcp_info tcp_info;</span><br><span style="color: hsl(120, 100%, 40%);">+ socklen_t tcp_info_len = sizeof(tcp_info);</span><br><span style="color: hsl(120, 100%, 40%);">+ char stat_name[256];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Do not fill in anything before the socket is connected to a remote end */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (osmo_sock_get_ip_and_port(stats_tcp_entry->fd->fd, NULL, 0, NULL, 0, false) != 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Gather TCP statistics and update the stats items */</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = getsockopt(stats_tcp_entry->fd->fd, IPPROTO_TCP, TCP_INFO, &tcp_info, &tcp_info_len);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Create stats items if they do not exist yet */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!stats_tcp_entry->stats_tcp) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp_entry->stats_tcp =</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_group_alloc(stats_tcp_entry, &stats_tcp_desc, stats_tcp_entry->fd->fd);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(stats_tcp_entry->stats_tcp);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Update statistics */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (stats_tcp_entry->name)</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(stat_name, sizeof(stat_name), "%s,%s", stats_tcp_entry->name,</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_sock_get_name2(stats_tcp_entry->fd->fd));</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(stat_name, sizeof(stat_name), "%s", osmo_sock_get_name2(stats_tcp_entry->fd->fd));</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_group_set_name(stats_tcp_entry->stats_tcp, stat_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_UNACKED),</span><br><span style="color: hsl(120, 100%, 40%);">+ tcp_info.tcpi_unacked);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_LOST),</span><br><span style="color: hsl(120, 100%, 40%);">+ tcp_info.tcpi_lost);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RETRANS),</span><br><span style="color: hsl(120, 100%, 40%);">+ tcp_info.tcpi_retrans);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RTT), tcp_info.tcpi_rtt);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RCV_RTT),</span><br><span style="color: hsl(120, 100%, 40%);">+ tcp_info.tcpi_rcv_rtt);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_NOTSENT_BYTES),</span><br><span style="color: hsl(120, 100%, 40%);">+ tcp_info.tcpi_notsent_bytes);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#if HAVE_TCP_INFO_TCPI_RWND_LIMITED == 1</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RWND_LIMITED),</span><br><span style="color: hsl(120, 100%, 40%);">+ tcp_info.tcpi_rwnd_limited);</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_RWND_LIMITED), -1);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#if STATS_TCP_SNDBUF_LIMITED == 1</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN),</span><br><span style="color: hsl(120, 100%, 40%);">+ tcp_info.tcpi_sndbuf_limited);</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN), -1);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#if HAVE_TCP_INFO_TCPI_REORD_SEEN == 1</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN),</span><br><span style="color: hsl(120, 100%, 40%);">+ tcp_info.tcpi_reord_seen);</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_set(osmo_stat_item_group_get_item(stats_tcp_entry->stats_tcp, STATS_TCP_REORD_SEEN), -1);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static bool is_tcp(const struct osmo_fd *fd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stat fd_stat;</span><br><span style="color: hsl(120, 100%, 40%);">+ int so_protocol = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ socklen_t so_protocol_len = sizeof(so_protocol);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Is this a socket? */</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = fstat(fd->fd, &fd_stat);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!S_ISSOCK(fd_stat.st_mode))</span><br><span style="color: hsl(120, 100%, 40%);">+ return false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Is it a TCP socket? */</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = getsockopt(fd->fd, SOL_SOCKET, SO_PROTOCOL, &so_protocol, &so_protocol_len);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (so_protocol == IPPROTO_TCP)</span><br><span style="color: hsl(120, 100%, 40%);">+ return true;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return false;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Register an osmo_fd for TCP stats monitoring.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] fd osmocom file descriptor to be registered.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] human readbla name that is used as prefix for the related stats item.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success; negative in case of error. */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_stats_tcp_osmo_fd_register(const struct osmo_fd *fd, const char *name)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stats_tcp_entry *stats_tcp_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Only TCP sockets can be registered for monitoring, anything else will fall through. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!is_tcp(fd))</span><br><span style="color: hsl(120, 100%, 40%);">+ return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* When the osmo_fd is registered and unregistered properly there shouldn't be any leftovers from already closed</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_fds in the stats_tcp list. But lets proactively make sure that any leftovers are cleaned up. */</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stats_tcp_osmo_fd_unregister(fd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Make a new list object, attach the osmo_fd... */</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp_entry = talloc_zero(OTC_GLOBAL, struct stats_tcp_entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(stats_tcp_entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp_entry->fd = fd;</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp_entry->name = talloc_strdup(stats_tcp_entry, name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ pthread_mutex_lock(&stats_tcp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_add_tail(&stats_tcp_entry->entry, &stats_tcp);</span><br><span style="color: hsl(120, 100%, 40%);">+ pthread_mutex_unlock(&stats_tcp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void next_stats_tcp_entry(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stats_tcp_entry *last;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (llist_empty(&stats_tcp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp_entry_cur = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ last = (struct stats_tcp_entry *)llist_last_entry(&stats_tcp, struct stats_tcp_entry, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!stats_tcp_entry_cur || stats_tcp_entry_cur == last)</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp_entry_cur =</span><br><span style="color: hsl(120, 100%, 40%);">+ (struct stats_tcp_entry *)llist_first_entry(&stats_tcp, struct stats_tcp_entry, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp_entry_cur =</span><br><span style="color: hsl(120, 100%, 40%);">+ (struct stats_tcp_entry *)llist_entry(stats_tcp_entry_cur->entry.next, struct stats_tcp_entry,</span><br><span style="color: hsl(120, 100%, 40%);">+ entry);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Register an osmo_fd for TCP stats monitoring.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] fd osmocom file descriptor to be unregistered.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success; negative in case of error. */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_stats_tcp_osmo_fd_unregister(const struct osmo_fd *fd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct stats_tcp_entry *stats_tcp_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc = -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ pthread_mutex_lock(&stats_tcp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each_entry(stats_tcp_entry, &stats_tcp, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (fd->fd == stats_tcp_entry->fd->fd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* In case we want to remove exactly that item which is also selected as the current itemy, we</span><br><span style="color: hsl(120, 100%, 40%);">+ * must designate either a different item or invalidate the current item. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (stats_tcp_entry == stats_tcp_entry_cur) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (llist_count(&stats_tcp) > 2)</span><br><span style="color: hsl(120, 100%, 40%);">+ next_stats_tcp_entry();</span><br><span style="color: hsl(120, 100%, 40%);">+ else</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp_entry_cur = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Date item from list */</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_del(&stats_tcp_entry->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_group_free(stats_tcp_entry->stats_tcp);</span><br><span style="color: hsl(120, 100%, 40%);">+ talloc_free(stats_tcp_entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ pthread_mutex_unlock(&stats_tcp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void stats_tcp_poll_timer_cb(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ int batch_size;</span><br><span style="color: hsl(120, 100%, 40%);">+ int llist_size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ pthread_mutex_lock(&stats_tcp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Make sure we do not run over the same sockets multiple times if the</span><br><span style="color: hsl(120, 100%, 40%);">+ * configured llist_size is larger then the actual list */</span><br><span style="color: hsl(120, 100%, 40%);">+ batch_size = osmo_tcp_stats_config->batch_size;</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_size = llist_count(&stats_tcp);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (llist_size < batch_size)</span><br><span style="color: hsl(120, 100%, 40%);">+ batch_size = llist_size;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Process a batch of sockets */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < batch_size; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ next_stats_tcp_entry();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (stats_tcp_entry_cur)</span><br><span style="color: hsl(120, 100%, 40%);">+ fill_stats(stats_tcp_entry_cur);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ pthread_mutex_unlock(&stats_tcp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (osmo_tcp_stats_config->interval > 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_schedule(&stats_tcp_poll_timer, osmo_tcp_stats_config->interval, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Set the polling interval (common for all sockets)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] interval Poll interval in seconds</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns 0 on success; negative on error */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_stats_tcp_set_interval(int interval)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_tcp_stats_config->interval = interval;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (osmo_tcp_stats_config->interval > 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_schedule(&stats_tcp_poll_timer, osmo_tcp_stats_config->interval, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static __attribute__((constructor))</span><br><span style="color: hsl(120, 100%, 40%);">+void on_dso_load_stats_tcp(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ stats_tcp_entry_cur = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ pthread_mutex_init(&stats_tcp_lock, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_tcp_stats_config->interval = TCP_STATS_DEFAULT_INTERVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_tcp_stats_config->batch_size = TCP_STATS_DEFAULT_BATCH_SIZE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_timer_setup(&stats_tcp_poll_timer, stats_tcp_poll_timer_cb, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* !EMBEDDED */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* @} */</span><br><span>diff --git a/src/vty/stats_vty.c b/src/vty/stats_vty.c</span><br><span>index e2247a3..a73fafb 100644</span><br><span>--- a/src/vty/stats_vty.c</span><br><span>+++ b/src/vty/stats_vty.c</span><br><span>@@ -33,6 +33,7 @@</span><br><span> #include <osmocom/core/stats.h></span><br><span> #include <osmocom/core/counter.h></span><br><span> #include <osmocom/core/rate_ctr.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/stats_tcp.h></span><br><span> </span><br><span> #define CFG_STATS_STR "Configure stats sub-system\n"</span><br><span> #define CFG_REPORTER_STR "Configure a stats reporter\n"</span><br><span>@@ -389,6 +390,32 @@</span><br><span> return CMD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_tcp_stats_interval, cfg_tcp_stats_interval_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "stats-tcp interval <0-65535>",</span><br><span style="color: hsl(120, 100%, 40%);">+ CFG_STATS_STR "Set the tcp socket stats polling interval\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Interval in seconds (0 disables the polling interval)\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+ int interval = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = osmo_stats_tcp_set_interval(interval);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "%% Unable to set interval: %s%s",</span><br><span style="color: hsl(120, 100%, 40%);">+ strerror(-rc), VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFUN(cfg_tcp_stats_batch_size, cfg_tcp_stats_batch_size_cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ "stats-tcp batch-size <1-65535>",</span><br><span style="color: hsl(120, 100%, 40%);">+ CFG_STATS_STR "Set the number of tcp sockets that are processed per stats polling interval\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Number of sockets per interval\n")</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_tcp_stats_config->batch_size = atoi(argv[0]);</span><br><span style="color: hsl(120, 100%, 40%);">+ return CMD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> DEFUN(show_stats,</span><br><span> show_stats_cmd,</span><br><span> "show stats",</span><br><span>@@ -677,6 +704,10 @@</span><br><span> struct osmo_stats_reporter *srep;</span><br><span> </span><br><span> vty_out(vty, "stats interval %d%s", osmo_stats_config->interval, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (osmo_tcp_stats_config->interval != TCP_STATS_DEFAULT_INTERVAL)</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "stats-tcp interval %d%s", osmo_tcp_stats_config->interval, VTY_NEWLINE);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (osmo_tcp_stats_config->batch_size != TCP_STATS_DEFAULT_BATCH_SIZE)</span><br><span style="color: hsl(120, 100%, 40%);">+ vty_out(vty, "stats-tcp batch-size %d%s", osmo_tcp_stats_config->batch_size, VTY_NEWLINE);</span><br><span> </span><br><span> /* Loop through all reporters */</span><br><span> llist_for_each_entry(srep, &osmo_stats_reporter_list, list)</span><br><span>@@ -699,6 +730,8 @@</span><br><span> install_lib_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd);</span><br><span> install_lib_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd);</span><br><span> install_lib_element(CONFIG_NODE, &cfg_stats_interval_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ install_lib_element(CONFIG_NODE, &cfg_tcp_stats_interval_cmd);</span><br><span style="color: hsl(120, 100%, 40%);">+ install_lib_element(CONFIG_NODE, &cfg_tcp_stats_batch_size_cmd);</span><br><span> </span><br><span> install_node(&cfg_stats_node, config_write_stats);</span><br><span> </span><br><span>diff --git a/tests/stats/stats_vty_test.vty b/tests/stats/stats_vty_test.vty</span><br><span>index 94cc7e8..8732d50 100644</span><br><span>--- a/tests/stats/stats_vty_test.vty</span><br><span>+++ b/tests/stats/stats_vty_test.vty</span><br><span>@@ -7,6 +7,7 @@</span><br><span> stats reporter log [NAME]</span><br><span> no stats reporter log [NAME]</span><br><span> stats interval <0-65535></span><br><span style="color: hsl(120, 100%, 40%);">+ stats-tcp interval <0-65535></span><br><span> ...</span><br><span> </span><br><span> stats_vty_test(config)# ### No reporters shall be configured by default</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/26454">change 26454</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmocore/+/26454"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I1416f95aff2adcf13689646b7574845de169fa3d </div>
<div style="display:none"> Gerrit-Change-Number: 26454 </div>
<div style="display:none"> Gerrit-PatchSet: 19 </div>
<div style="display:none"> Gerrit-Owner: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-CC: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>