Commit bee4ee93 authored by Ondřej Surý's avatar Ondřej Surý
Browse files

Merge branch '2416-improve-netmgr-unit-tests-reliability' into 'main'

Improve reliability of the netmgr unit tests

Closes #2455 and #2416

See merge request isc-projects/bind9!4628
parents e8cd3d3c d96c94d7
......@@ -143,10 +143,6 @@ stages:
# Ubuntu
.ubuntu-xenial-amd64: &ubuntu_xenial_amd64_image
image: "$CI_REGISTRY_IMAGE:ubuntu-xenial-amd64"
<<: *linux_amd64
.ubuntu-focal-amd64: &ubuntu_focal_amd64_image
image: "$CI_REGISTRY_IMAGE:ubuntu-focal-amd64"
<<: *linux_amd64
......@@ -644,6 +640,7 @@ gcc:stretch:amd64:
variables:
CC: gcc
CFLAGS: "${CFLAGS_COMMON} -O2"
EXTRA_CONFIGURE: "--without-cmocka"
<<: *debian_stretch_amd64_image
<<: *build_job
......@@ -842,30 +839,6 @@ unit:gcc:tumbleweed:amd64:
- job: gcc:tumbleweed:amd64
artifacts: true
# Jobs for regular GCC builds on Ubuntu 16.04 Xenial Xerus (amd64)
gcc:xenial:amd64:
variables:
CC: gcc
CFLAGS: "${CFLAGS_COMMON} -O2"
EXTRA_CONFIGURE: "--disable-geoip"
<<: *ubuntu_xenial_amd64_image
<<: *build_job
system:gcc:xenial:amd64:
<<: *ubuntu_xenial_amd64_image
<<: *system_test_job
needs:
- job: gcc:xenial:amd64
artifacts: true
unit:gcc:xenial:amd64:
<<: *ubuntu_xenial_amd64_image
<<: *unit_test_job
needs:
- job: gcc:xenial:amd64
artifacts: true
# Jobs for regular GCC builds on Ubuntu 20.04 Focal Fossa (amd64)
gcc:focal:amd64:
......
......@@ -1350,10 +1350,10 @@ AC_ARG_WITH([cmocka],
AS_CASE([$with_cmocka],
[no],[],
[detect],[PKG_CHECK_MODULES([CMOCKA], [cmocka >= 1.0.0],
[detect],[PKG_CHECK_MODULES([CMOCKA], [cmocka >= 1.1.3],
[AC_DEFINE([HAVE_CMOCKA], [1], [Use CMocka])
with_cmocka=yes],[with_cmocka=no])],
[yes],[PKG_CHECK_MODULES([CMOCKA], [cmocka >= 1.0.0],
[yes],[PKG_CHECK_MODULES([CMOCKA], [cmocka >= 1.1.3],
[AC_DEFINE([HAVE_CMOCKA], [1], [Use CMocka])])],
[AC_MSG_ERROR([Use PKG_CONFIG_PATH to specify path to CMocka library])]
)
......
......@@ -38,10 +38,15 @@
#include "openssl_shim.h"
#include "uv-compat.h"
#ifdef NETMGR_TRACE
#if NETMGR_TRACE
#if HAVE_BACKTRACE
#include <execinfo.h>
#endif
#else /* HAVE_BACKTRACE */
#define backtrace(buffer, size) 0
#define backtrace_symbols_fd(buffer, size, fd) \
fprintf(stderr, "<not available>");
#endif /* HAVE_BACKTRACE */
#endif /* NETMGR_TRACE */
/*%
* How many isc_nmhandles and isc_nm_uvreqs will we be
......@@ -908,8 +913,9 @@ isc___nmsocket_attach(isc_nmsocket_t *sock, isc_nmsocket_t **target FLARG) {
rsock = sock;
}
NETMGR_TRACE_LOG("isc__nmsocket_attach():%p->references = %lu\n", rsock,
isc_refcount_current(&rsock->references) + 1);
NETMGR_TRACE_LOG("isc__nmsocket_attach():%p->references = %" PRIuFAST32
"\n",
rsock, isc_refcount_current(&rsock->references) + 1);
isc_refcount_increment0(&rsock->references);
......@@ -927,8 +933,9 @@ nmsocket_cleanup(isc_nmsocket_t *sock, bool dofree FLARG) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(!isc__nmsocket_active(sock));
NETMGR_TRACE_LOG("nmsocket_cleanup():%p->references = %lu\n", sock,
isc_refcount_current(&sock->references));
NETMGR_TRACE_LOG("nmsocket_cleanup():%p->references = %" PRIuFAST32
"\n",
sock, isc_refcount_current(&sock->references));
atomic_store(&sock->destroying, true);
......@@ -1076,7 +1083,8 @@ void
isc___nmsocket_prep_destroy(isc_nmsocket_t *sock FLARG) {
REQUIRE(sock->parent == NULL);
NETMGR_TRACE_LOG("isc___nmsocket_prep_destroy():%p->references = %lu\n",
NETMGR_TRACE_LOG("isc___nmsocket_prep_destroy():%p->references = "
"%" PRIuFAST32 "\n",
sock, isc_refcount_current(&sock->references));
/*
......@@ -1149,8 +1157,9 @@ isc___nmsocket_detach(isc_nmsocket_t **sockp FLARG) {
rsock = sock;
}
NETMGR_TRACE_LOG("isc__nmsocket_detach():%p->references = %lu\n", rsock,
isc_refcount_current(&rsock->references) - 1);
NETMGR_TRACE_LOG("isc__nmsocket_detach():%p->references = %" PRIuFAST32
"\n",
rsock, isc_refcount_current(&rsock->references) - 1);
if (isc_refcount_decrement(&rsock->references) == 1) {
isc___nmsocket_prep_destroy(rsock FLARG_PASS);
......@@ -1191,7 +1200,7 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
.inactivereqs = isc_astack_new(
mgr->mctx, ISC_NM_REQS_STACK_SIZE) };
#ifdef NETMGR_TRACE
#if NETMGR_TRACE
sock->backtrace_size = backtrace(sock->backtrace, TRACE_SIZE);
ISC_LINK_INIT(sock, active_link);
ISC_LIST_INIT(sock->active_handles);
......@@ -1249,8 +1258,9 @@ isc___nmsocket_init(isc_nmsocket_t *sock, isc_nm_t *mgr, isc_nmsocket_type type,
memset(&sock->tlsstream, 0, sizeof(sock->tlsstream));
NETMGR_TRACE_LOG("isc__nmsocket_init():%p->references = %lu\n", sock,
isc_refcount_current(&sock->references));
NETMGR_TRACE_LOG("isc__nmsocket_init():%p->references = %" PRIuFAST32
"\n",
sock, isc_refcount_current(&sock->references));
atomic_init(&sock->active, true);
atomic_init(&sock->sequential, false);
......@@ -1332,12 +1342,13 @@ isc___nmhandle_get(isc_nmsocket_t *sock, isc_sockaddr_t *peer,
INSIST(VALID_NMHANDLE(handle));
}
NETMGR_TRACE_LOG("isc__nmhandle_get():handle %p->references = %lu\n",
handle, isc_refcount_current(&handle->references));
NETMGR_TRACE_LOG(
"isc__nmhandle_get():handle %p->references = %" PRIuFAST32 "\n",
handle, isc_refcount_current(&handle->references));
isc___nmsocket_attach(sock, &handle->sock FLARG_PASS);
#ifdef NETMGR_TRACE
#if NETMGR_TRACE
handle->backtrace_size = backtrace(handle->backtrace, TRACE_SIZE);
#endif
......@@ -1415,7 +1426,8 @@ isc__nmhandle_attach(isc_nmhandle_t *handle, isc_nmhandle_t **handlep FLARG) {
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(handlep != NULL && *handlep == NULL);
NETMGR_TRACE_LOG("isc__nmhandle_attach():handle %p->references = %lu\n",
NETMGR_TRACE_LOG("isc__nmhandle_attach():handle %p->references = "
"%" PRIuFAST32 "\n",
handle, isc_refcount_current(&handle->references) + 1);
isc_refcount_increment(&handle->references);
......@@ -1519,7 +1531,8 @@ nmhandle_detach_cb(isc_nmhandle_t **handlep FLARG) {
handle = *handlep;
*handlep = NULL;
NETMGR_TRACE_LOG("isc__nmhandle_detach():%p->references = %lu\n",
NETMGR_TRACE_LOG("isc__nmhandle_detach():%p->references = %" PRIuFAST32
"\n",
handle, isc_refcount_current(&handle->references) - 1);
if (isc_refcount_decrement(&handle->references) > 1) {
......@@ -2831,7 +2844,7 @@ nmsocket_type_totext(isc_nmsocket_type type) {
static void
nmhandle_dump(isc_nmhandle_t *handle) {
fprintf(stderr, "Active handle %p, refs %lu\n", handle,
fprintf(stderr, "Active handle %p, refs %" PRIuFAST32 "\n", handle,
isc_refcount_current(&handle->references));
fprintf(stderr, "Created by:\n");
backtrace_symbols_fd(handle->backtrace, handle->backtrace_size,
......@@ -2845,17 +2858,18 @@ nmsocket_dump(isc_nmsocket_t *sock) {
LOCK(&sock->lock);
fprintf(stderr, "\n=================\n");
fprintf(stderr, "Active %s socket %p, type %s, refs %lu\n",
sock->client ? "client" : "server", sock,
fprintf(stderr, "Active %s socket %p, type %s, refs %" PRIuFAST32 "\n",
atomic_load(&sock->client) ? "client" : "server", sock,
nmsocket_type_totext(sock->type),
isc_refcount_current(&sock->references));
fprintf(stderr,
"Parent %p, listener %p, server %p, statichandle = %p\n",
sock->parent, sock->listener, sock->server, sock->statichandle);
fprintf(stderr, "Flags:%s%s%s%s%s\n", sock->active ? " active" : "",
sock->closing ? " closing" : "",
sock->destroying ? " destroying" : "",
sock->connecting ? " connecting" : "",
fprintf(stderr, "Flags:%s%s%s%s%s\n",
atomic_load(&sock->active) ? " active" : "",
atomic_load(&sock->closing) ? " closing" : "",
atomic_load(&sock->destroying) ? " destroying" : "",
atomic_load(&sock->connecting) ? " connecting" : "",
sock->accepting ? " accepting" : "");
fprintf(stderr, "Created by:\n");
backtrace_symbols_fd(sock->backtrace, sock->backtrace_size,
......
......@@ -9,7 +9,7 @@ LDADD += \
$(LIBISC_LIBS)
check_LTLIBRARIES = libisctest.la
libisctest_la_SOURCES = \
libisctest_la_SOURCES = \
../unix/socket_p.h \
isctest.c \
isctest.h \
......@@ -31,6 +31,7 @@ TESTS = \
md_test \
mem_test \
netaddr_test \
netmgr_test \
parse_test \
pool_test \
quota_test \
......@@ -45,13 +46,8 @@ TESTS = \
symtab_test \
task_test \
taskpool_test \
tcp_quota_test \
tcp_test \
tcpdns_test \
time_test \
timer_test \
tlsdns_test \
udp_test
timer_test
check_PROGRAMS = \
$(TESTS)
......@@ -86,48 +82,12 @@ random_test_LDADD = \
$(LDADD) \
-lm
tcp_test_CPPFLAGS = \
netmgr_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
tcp_test_LDADD = \
$(LDADD) \
$(LIBUV_LIBS)
tcp_quota_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
tcp_quota_test_LDADD = \
$(LDADD) \
$(LIBUV_LIBS)
tcpdns_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
tcpdns_test_LDADD = \
$(LDADD) \
$(LIBUV_LIBS)
tlsdns_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
tlsdns_test_LDADD = \
$(LDADD) \
$(LIBUV_LIBS)
udp_test_CPPFLAGS = \
$(AM_CPPFLAGS) \
$(LIBUV_CFLAGS) \
$(OPENSSL_CFLAGS)
udp_test_LDADD = \
netmgr_test_LDADD = \
$(LDADD) \
$(LIBUV_LIBS)
......
......@@ -85,8 +85,9 @@ create_managers(unsigned int workers) {
if (p != NULL) {
workers = atoi(p);
}
INSIST(workers != 0);
isc_hp_init(ISC_MAX(ISC_MIN(workers, 256), 128));
isc_hp_init(4 * workers);
netmgr = isc_nm_start(test_mctx, workers);
CHECK(isc_taskmgr_create(test_mctx, workers, 0, netmgr, &taskmgr));
......
This diff is collapsed.
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#if HAVE_CMOCKA
#include <sched.h> /* IWYU pragma: keep */
#include <setjmp.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <uv.h>
#define UNIT_TESTING
#include <cmocka.h>
#include <isc/atomic.h>
#include <isc/buffer.h>
#include <isc/condition.h>
#include <isc/mutex.h>
#include <isc/netmgr.h>
#include <isc/nonce.h>
#include <isc/os.h>
#include <isc/refcount.h>
#include <isc/sockaddr.h>
#include <isc/thread.h>
#include "../netmgr/netmgr-int.h"
#include "isctest.h"
#define MAX_NM 2
static isc_sockaddr_t tcp_listen_addr;
static uint64_t send_magic = 0;
static uint64_t stop_magic = 0;
static uv_buf_t send_msg = { .base = (char *)&send_magic,
.len = sizeof(send_magic) };
static uv_buf_t stop_msg = { .base = (char *)&stop_magic,
.len = sizeof(stop_magic) };
static atomic_uint_fast64_t nsends;
static atomic_uint_fast64_t ssends;
static atomic_uint_fast64_t sreads;
static atomic_uint_fast64_t saccepts;
static atomic_uint_fast64_t cconnects;
static atomic_uint_fast64_t csends;
static atomic_uint_fast64_t creads;
static atomic_uint_fast64_t ctimeouts;
static unsigned int workers = 2;
static isc_quota_t listener_quota;
static atomic_bool check_listener_quota;
#define NSENDS 100
#define NWRITES 10
/* Enable this to print values while running tests */
#undef PRINT_DEBUG
#ifdef PRINT_DEBUG
#define X(v) fprintf(stderr, #v " = %" PRIu64 "\n", atomic_load(&v))
#define P(v) fprintf(stderr, #v " = %" PRIu64 "\n", v)
#else
#define X(v)
#define P(v)
#endif
static int
setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) {
isc_result_t result;
socklen_t addrlen = sizeof(*addr);
int fd;
int r;
isc_sockaddr_fromin6(addr, &in6addr_loopback, 0);
fd = socket(AF_INET6, family, 0);
if (fd < 0) {
perror("setup_ephemeral_port: socket()");
return (-1);
}
r = bind(fd, (const struct sockaddr *)&addr->type.sa,
sizeof(addr->type.sin6));
if (r != 0) {
perror("setup_ephemeral_port: bind()");
close(fd);
return (r);
}
r = getsockname(fd, (struct sockaddr *)&addr->type.sa, &addrlen);
if (r != 0) {
perror("setup_ephemeral_port: getsockname()");
close(fd);
return (r);
}
result = isc__nm_socket_reuse(fd);
if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
fprintf(stderr,
"setup_ephemeral_port: isc__nm_socket_reuse(): %s",
isc_result_totext(result));
close(fd);
return (-1);
}
result = isc__nm_socket_reuse_lb(fd);
if (result != ISC_R_SUCCESS && result != ISC_R_NOTIMPLEMENTED) {
fprintf(stderr,
"setup_ephemeral_port: isc__nm_socket_reuse_lb(): %s",
isc_result_totext(result));
close(fd);
return (-1);
}
#if IPV6_RECVERR
#define setsockopt_on(socket, level, name) \
setsockopt(socket, level, name, &(int){ 1 }, sizeof(int))
r = setsockopt_on(fd, IPPROTO_IPV6, IPV6_RECVERR);
if (r != 0) {
perror("setup_ephemeral_port");
close(fd);
return (r);
}
#endif
return (fd);
}
static int
_setup(void **state) {
UNUSED(state);
/* workers = isc_os_ncpus(); */
if (isc_test_begin(NULL, true, workers) != ISC_R_SUCCESS) {
return (-1);
}
signal(SIGPIPE, SIG_IGN);
return (0);
}
static int
_teardown(void **state) {
UNUSED(state);
isc_test_end();
return (0);
}
/* Generic */
thread_local uint8_t tcp_buffer_storage[4096];
thread_local size_t tcp_buffer_length = 0;
static int
nm_setup(void **state) {
size_t nworkers = ISC_MAX(ISC_MIN(workers, 32), 1);
int tcp_listen_sock = -1;
isc_nm_t **nm = NULL;
tcp_listen_addr = (isc_sockaddr_t){ .length = 0 };
tcp_listen_sock = setup_ephemeral_port(&tcp_listen_addr, SOCK_STREAM);
if (tcp_listen_sock < 0) {
return (-1);
}
close(tcp_listen_sock);
tcp_listen_sock = -1;
atomic_store(&nsends, NSENDS * NWRITES);
atomic_store(&csends, 0);
atomic_store(&creads, 0);
atomic_store(&sreads, 0);
atomic_store(&ssends, 0);
atomic_store(&saccepts, 0);
atomic_store(&ctimeouts, 0);
atomic_store(&cconnects, 0);
isc_nonce_buf(&send_magic, sizeof(send_magic));
isc_nonce_buf(&stop_magic, sizeof(stop_magic));
if (send_magic == stop_magic) {
return (-1);
}
nm = isc_mem_get(test_mctx, MAX_NM * sizeof(nm[0]));
for (size_t i = 0; i < MAX_NM; i++) {
nm[i] = isc_nm_start(test_mctx, nworkers);
assert_non_null(nm[i]);
}
*state = nm;
isc_quota_init(&listener_quota, 0);
atomic_store(&check_listener_quota, false);
return (0);
}
static int
nm_teardown(void **state) {
isc_nm_t **nm = (isc_nm_t **)*state;
for (size_t i = 0; i < MAX_NM; i++) {
isc_nm_destroy(&nm[i]);
assert_null(nm[i]);
}
isc_mem_put(test_mctx, nm, MAX_NM * sizeof(nm[0]));
isc_quota_destroy(&listener_quota);
return (0);
}
thread_local size_t nwrites = NWRITES;
/* TCP Connect */
static void
tcp_connect_send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg);
static void
tcp_connect_send(isc_nmhandle_t *handle);
static void
tcp_connect_read_cb(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *cbarg) {
uint64_t magic = 0;
UNUSED(cbarg);
assert_non_null(handle);
if (eresult != ISC_R_SUCCESS) {
goto unref;
}
memmove(tcp_buffer_storage + tcp_buffer_length, region->base,
region->length);
tcp_buffer_length += region->length;
if (tcp_buffer_length >= sizeof(magic)) {
isc_nm_pauseread(handle);
atomic_fetch_add(&creads, 1);
memmove(&magic, tcp_buffer_storage, sizeof(magic));
assert_true(magic == stop_magic || magic == send_magic);
tcp_buffer_length -= sizeof(magic);
memmove(tcp_buffer_storage, tcp_buffer_storage + sizeof(magic),
tcp_buffer_length);
if (magic == send_magic) {
tcp_connect_send(handle);
return;
} else if (magic == stop_magic) {
/* We are done, so we don't send anything back */
/* There should be no more packets in the buffer */
assert_int_equal(tcp_buffer_length, 0);
}
}
unref:
isc_nmhandle_detach(&handle);
}
static void
tcp_connect_send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
assert_non_null(handle);
UNUSED(cbarg);
if (eresult == ISC_R_SUCCESS) {
atomic_fetch_add(&csends, 1);
isc_nm_resumeread(handle);
} else {
/* Send failed, we need to stop reading too */
isc_nm_cancelread(handle);
}
}
static void
tcp_connect_shutdown(isc_nmhandle_t *handle, isc_result_t eresult,
void *cbarg) {
UNUSED(cbarg);
assert_non_null(handle);
if (eresult == ISC_R_SUCCESS) {
atomic_fetch_add(&csends, 1);
} else {
isc_nm_cancelread(handle);
}
}
static void
tcp_connect_send(isc_nmhandle_t *handle) {
uint_fast64_t sends = atomic_load(&nsends);
while (sends > 0) {