From 840dc98676773c027e699bd6efc3793118a5f1ef Mon Sep 17 00:00:00 2001 From: charan makkina Date: Tue, 22 Jan 2019 14:18:33 +0530 Subject: Test: Testcases for bps, cps, ip6, lp and te. Change-Id: I17ad8a915c4a9332c11797e7f02c82abbfadfbbc Signed-off-by: charan makkina --- thirdparty/apps/CMakeLists.txt | 1 + thirdparty/apps/testapp/CMakeLists.txt | 56 ++ thirdparty/apps/testapp/README.md | 212 ++++++ thirdparty/apps/testapp/bps/bps.c | 775 +++++++++++++++++++ thirdparty/apps/testapp/bps/bps.h | 112 +++ thirdparty/apps/testapp/cps/cps.c | 833 +++++++++++++++++++++ thirdparty/apps/testapp/cps/cps.h | 283 +++++++ thirdparty/apps/testapp/cps/cps_c.c | 394 ++++++++++ thirdparty/apps/testapp/cps/cps_s.c | 320 ++++++++ thirdparty/apps/testapp/ft.c | 74 ++ thirdparty/apps/testapp/ip6/ip6.c | 739 ++++++++++++++++++ thirdparty/apps/testapp/lb/api.h | 169 +++++ thirdparty/apps/testapp/lb/lb.c | 1290 ++++++++++++++++++++++++++++++++ thirdparty/apps/testapp/lb/lb.h | 731 ++++++++++++++++++ thirdparty/apps/testapp/lp/lp.c | 1138 ++++++++++++++++++++++++++++ thirdparty/apps/testapp/lp/lp.h | 615 +++++++++++++++ thirdparty/apps/testapp/lp/lpc.c | 669 +++++++++++++++++ thirdparty/apps/testapp/lp/lps.c | 372 +++++++++ thirdparty/apps/testapp/te/te.c | 782 +++++++++++++++++++ 19 files changed, 9565 insertions(+) create mode 100644 thirdparty/apps/testapp/CMakeLists.txt create mode 100644 thirdparty/apps/testapp/README.md create mode 100644 thirdparty/apps/testapp/bps/bps.c create mode 100644 thirdparty/apps/testapp/bps/bps.h create mode 100644 thirdparty/apps/testapp/cps/cps.c create mode 100644 thirdparty/apps/testapp/cps/cps.h create mode 100644 thirdparty/apps/testapp/cps/cps_c.c create mode 100644 thirdparty/apps/testapp/cps/cps_s.c create mode 100644 thirdparty/apps/testapp/ft.c create mode 100644 thirdparty/apps/testapp/ip6/ip6.c create mode 100644 thirdparty/apps/testapp/lb/api.h create mode 100644 thirdparty/apps/testapp/lb/lb.c create mode 100644 thirdparty/apps/testapp/lb/lb.h create mode 100644 thirdparty/apps/testapp/lp/lp.c create mode 100644 thirdparty/apps/testapp/lp/lp.h create mode 100644 thirdparty/apps/testapp/lp/lpc.c create mode 100644 thirdparty/apps/testapp/lp/lps.c create mode 100644 thirdparty/apps/testapp/te/te.c diff --git a/thirdparty/apps/CMakeLists.txt b/thirdparty/apps/CMakeLists.txt index 0fffccd..f4f1cbe 100644 --- a/thirdparty/apps/CMakeLists.txt +++ b/thirdparty/apps/CMakeLists.txt @@ -77,3 +77,4 @@ ExternalProject_Add( ) set_target_properties(NETPERF PROPERTIES EXCLUDE_FROM_ALL TRUE) +ADD_SUBDIRECTORY(testapp) diff --git a/thirdparty/apps/testapp/CMakeLists.txt b/thirdparty/apps/testapp/CMakeLists.txt new file mode 100644 index 0000000..b4c56df --- /dev/null +++ b/thirdparty/apps/testapp/CMakeLists.txt @@ -0,0 +1,56 @@ +######################################################################### +# +# Copyright (c) 2018 Huawei Technologies Co.,Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +######################################################################### + + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11) +#PROJECT(leibniz) + +#SET(CMAKE_C_COMPILER "gcc") +INCLUDE_DIRECTORIES(lb) +ADD_DEFINITIONS(-O2 -g -fPIC -m64 -pthread -Werror) +ADD_DEFINITIONS(-D_GNU_SOURCE -U_FORTIFY_SOURCE -DDEBUG) +LINK_LIBRARIES(-pthread -lrt -g -m64) +LINK_LIBRARIES(-L../../../../build -llb) +LINK_LIBRARIES(-L../../../../release/lib64 -lnStackAPI) + +FILE(GLOB lb_src lb/*.c) +ADD_LIBRARY(lb STATIC ${lb_src}) + +#SET(BINS lp bps cps ip6 te) +LIST(APPEND BINS "lp") +LIST(APPEND BINS "bps") +LIST(APPEND BINS "cps") +LIST(APPEND BINS "ip6") +LIST(APPEND BINS "te") +MESSAGE(STATUS "" ${BINS}) +FOREACH(one ${BINS}) + FILE(GLOB ${one}_src ${one}/*.c) + ADD_EXECUTABLE(${one} ${${one}_src}) + ADD_DEPENDENCIES(${one} lb) + ADD_DEPENDENCIES(${one} nStackAPI) +ENDFOREACH(one) + +ADD_EXECUTABLE(ft ft.c) +ADD_DEPENDENCIES(ft lb) +ADD_DEPENDENCIES(ft nStackAPI) +IF(EXISTS ${nslib}) + MESSAGE(STATUS "! set nstack lib directory ${NSLIb}") + LINK_LIBRARIES(-Wl, --whole-archive -L${NSLIB} -lnStackAPI -lrtp_mem -lsecurec -Wl,--no-whole-archive) + FOREACH(one ${BINS}) + ADD_EXECUTABLE(ns${one} ${${one}_src}) + ADD_DEPENDENCIES(ns${one} lb) + ENDFOREACH(one) +ENDIF() diff --git a/thirdparty/apps/testapp/README.md b/thirdparty/apps/testapp/README.md new file mode 100644 index 0000000..83bc121 --- /dev/null +++ b/thirdparty/apps/testapp/README.md @@ -0,0 +1,212 @@ +# 1. BPS +**NAME** +bps + +**SYNOPSIS** +bps [OPTIONS] [SERVER-ADDRESS] + +**DESCRIPTION** +bps is used to test throughput. +``` + -h, --help help + -v, --verbose show more statistics + -e, --exact show exact value + -i, --interval=SECONDS report time(default:1s) + -l, --length=LENGTH message length(default:458 max:1024) + -B, --buffer=BUFFER recv buffer size(default:LENGTH max:1024) + -C, --core=COREMASK bound core mask HEX(default:0(no bind core)) + -S, --send-only only send + -R, --recv-only only receive + -c, --client client mode + Client mode options: + -t, --time=SECOND test time(default:30s) + -b, --bind=ADDRESS bind address + -p, --parallel=# parallel number(default:1 max:128) + -D, --debug show debug information + ADDRESS: X.X.X.X:PORT default port:58177 +``` +**EXAMPLE** +Server: +``` +$./bps 192.168.1.1:6666 -l 1000 +``` + +Client: +``` +$./bps -c 192.168.1.1:6666 -l 1000 +``` + +#2. CPS +**NAME** +cps + +**SYNOPSIS** +cps [OPTIONS] [SERVER-ADDRESS] + +**DESCRIPTION** +cps is used to test the connection rate. +``` + -i, --interval=# report time(default: 10s max:3600s) + -c, --client server address list for one thread + -e, --evnum epoll event number(default:256 max:1024) + -T, --time=# C test time(default: 300s max:604800s) + -d, --data=#[:#] C request and response data length(default:1:1 max:4096) + -r, --rate=#[k|m|w] C global connect rate per each thread(CPS, default: 10000 max:100000000) + -t, --thread=CONFIG set one net and thread(max: 128) + server=X.X.X.X:P server address set(max: 32) + core=# bind to core + client=X.X.X.X C client ip address set(max: 32 max ip: 256) + rate=# C set connect rate for this thread(default: use global set) + cf C client loop first(default: both) + sf C server loop first(default: both) + -D, --debug show debug information + -m, --more show more statistics + -v, --verbose show thread statistics + -h, --help help + IMPORTANT: + socket() EMFILE(24) error: ulimit -n 1048576 + bind() EADDRINUSE(98) error: echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle + connect() EADDRNOTAVAIL(99) error: echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse + echo "3000 65534" > /proc/sys/net/ipv4/ip_local_port_range +``` + +**EXAMPLE** +Server: +``` +$./cps -t "server=192.168.1.1:8888" +``` +Client: +``` +$./cps -c -t "server=192.168.1.1:8888,rate=10" +``` + +# 3. LP +**NAME** +lp + +**SYNOPSIS** +lp [OPTIONS] TEST-SET... + +**DESCRIPTION** +lp test multiple connections, connect rate and close rate. +``` + -s, --server LIST set one server address list + X.Y.Z.M-N:P1-P2,... + -c, --client LIST set one client address list + CLIENT*SERVER: R.S.T.K-J:Pa-Pb,...*X.Y.Z.M-N:P1-P2,... + A,B,C,D*1,2 random link + A,B,C,D=1,2 A1B2C1D2 + A,B,C,D}1,2 A1B1C1D1 A2B2C2D2 + A,B,C,D{1,2 A1A2 B1B2 C1C2 D1D2 + -b, --block set block mode for connecting(client only) + -n, --nodelay set nodelay + -i, --interval # report time(default:1s max:60s) + -m, --core #HEX set bind cpu core mask(hex mode) + -D, --debug show debug information + -w, --watch show watch time statistic + -e, --no-error #-# skip error + -E, --error-msg show error message + -C, --no-color no color + -v, --verbose show worker statistics + -h, --help help + TEST-SET for client + TARGET@TIME+UP-DOWN=QUERY:REPLY*TIMES-/PERIOD%WAIT (client only) + TARGET max connection(default: INFINITE) + @TIME max time(0 or default:INFINITE) + +UP connect rate(default: 0 no connnect; *: INFINITE) + -DOWN close rate(default: 0 no close; *: INFINITE) + =... IO set(default: no IO) + QUERY send query data len(8-65536) + :REPLY receive response data len(0-10485760; default: same with QUERY) + *TIMES- IO times(0 or default: INFINITE; suffix-: IO then close) + /PERIOD IO period time(0-3600s; default: one by one) + %WAIT first IO wait time(0-3600s; default: 0 no wait) + UNITS: + k=1000 m=1000k g=1000m w=10000 K=1024 M=1024K G=1024M + s=Seconds m=Minutes h=Hours + + =QUERY:REPLY*TIMES-/PERIOD%WAIT +This section set the send/recv action + +``` +**EXAMPLE** +Server: +``` +$./lp -s 192.168.1.1:1000-1999 -s 182.168.1.1:2000-2999 + #each -s start 1 thread, open 1000 fd to listen +``` + +Client: +``` +$./lp -c 192.168.1.100:10000-19999*192.168.1.1:1000-1999 TEST-SET + #each -c start 1 thread + #192.168.1.100:10000-19999 is client addresses + #192.168.1.1:1000-1999 are server addresses + #TEST-SET is client only, you can set some test case, see options +``` + +**TEST-SET EXAMPLES** + +``` +1. $./lp -c 192.168.1.100:10000-19999*192.168.1.1:1000-1999 1m+10k @10s 0-1k +``` + +Step1: target 1000000 connection, connect rate is 10k/s +Step2: no target, sleep 10 seconds +Step3: target 0 connection, close rate is 1k/s + +``` +2. $./lp -c 192.168.1.100:10000-19999*192.168.1.1:1000-1999 100000+10k-5k @10s 0-1k +``` +Step1: target 100000 connection, both connect and close are execute, connect rate 10k/s, close rate 5k/s + +# 4. TE +**NAME** +te + +**SYNOPSIS** +te [OPTIONS] + +**DESCRIPTION** +te is used to test the test the basic functions of IPv6 +``` +l : v6_tcp_server_listen ( X::X PORT ) +L : v6_tcp_server_listen ( X::X PORT ) + +c : v6_tcp_client_s ( X::X PORT ) +s : v6_tcp_server_shutdown_rd ( X::X PORT ) +d : v6_tcp_server_shutdown_wr ( X::X PORT ) +f : v6_tcp_server_shutdown_rdwr ( X::X PORT ) + +1 : v4_tcp_client_s ( X.X.X.X PORT ) +2 : v4_tcp_server_shutdown_rd ( X.X.X.X PORT ) +3 : v4_tcp_server_shutdown_wr ( X.X.X.X PORT ) + +U : test_v6_udp ( X::X ) +u : test_v4_udp ( X.X.X.X ) +t : test_v4_tcp ( X.X.X.X ) + +b : v6_udp_close_select ( X::X PORT ) +``` + +# 5. IP6 +**NAME** +ip6 + +**SYNOPSIS** +ip6 [OPTIONS] SERVER-ADDRESS + +**DESCRIPTION** +ip6 is used to test the test the basic functions of IPv6 +``` + -b, --bind IP.PORT bind address + -c, --client client mode + -u, --udp udp mode + -n, --number # C packet number(default:LOOP) + -d, --delay # C seconds wait send next packet(default:1, 0: no delay) + -l, --length # C data length(default:100) + -o, --output # show received data(default:16) + -D, --debug show debug information + -v, --verbose show thread statistics + -h, --help help +``` diff --git a/thirdparty/apps/testapp/bps/bps.c b/thirdparty/apps/testapp/bps/bps.c new file mode 100644 index 0000000..a3e9bdf --- /dev/null +++ b/thirdparty/apps/testapp/bps/bps.c @@ -0,0 +1,775 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" +#include "bps.h" + +struct bps_var bps = { 0 }; + +/********************/ + +inline static int +bps_cpu () +{ + int i; + + if (bps.bind_core == 0) + return -1; + + for (i = 0; i < 64; ++i) + { + if (bps.bind_core & (1ul << i)) + { + bps.bind_core &= ~(1ul << i); + out ("mask:0x%lx cps:%d\n", bps.bind_core, i); + return i; + } + } + + return -1; +} + +void * +bps_send (void *arg) +{ + static char bps_send_buf[BPS_MAX_LEN]; + + struct bps_sess *sess = (struct bps_sess *) arg; + const int sid = BPS_SESS_ID (sess); + int fd, sent = 0; + char *buf = malloc (bps.msg_len); + + out ("client send\n"); + + if (!buf) + buf = bps_send_buf; + + futex_wait (&sess->fd, -1); + __sync_synchronize (); + + fd = sess->fd; + + while (sess->state == BPS_RUNNING) + { + int ret = _send (fd, buf + sent, bps.msg_len - sent, 0); + if (ret > 0) + { + sent += ret; + bps.rec_now[sid].snd += ret; + if (sent >= bps.msg_len) + sent -= bps.msg_len; + } + else + { + if (ret < 0) + { + const int e = errno; + if (e == EWOULDBLOCK || e == EINTR || e == EAGAIN) + continue; + out ("send()=%d:%d\n", ret, e); + } + //sess->state = BPS_SEND_ERROR; + } + } + + if (buf != bps_send_buf) + free (buf); + return NULL; +} + +void * +bps_recv (void *arg) +{ + static char bps_recv_buf[BPS_MAX_LEN]; + + struct bps_sess *sess = (struct bps_sess *) arg; + const int sid = BPS_SESS_ID (sess); + int fd, recved = 0; + char *buf = malloc (bps.buf_size); + out ("client recv\n"); + if (!buf) + buf = bps_recv_buf; + + futex_wait (&sess->fd, -1); + __sync_synchronize (); + + fd = sess->fd; + + while (sess->state == BPS_RUNNING) + { + int ret = _recv (fd, buf + recved, bps.buf_size - recved, 0); + if (ret > 0) + { + recved += ret; + bps.rec_now[sid].rcv += ret; + if (recved >= bps.msg_len) + recved -= bps.msg_len; + } + else + { + if (ret < 0) + { + const int e = errno; + if (e == EWOULDBLOCK || e == EINTR || e == EAGAIN) + continue; + out ("recv()=%d:%d\n", ret, e); + } + //sess->state = BPS_RECV_ERROR; + } + } + + if (buf != bps_recv_buf) + free (buf); + return NULL; +} + +void +bps_stop (struct bps_sess *sess) +{ + if (sess->state == BPS_RUNNING) + sess->state = BPS_STOP; + + if (sess->send_tid) + (void) pthread_join (sess->send_tid, NULL); + if (sess->recv_tid) + (void) pthread_join (sess->recv_tid, NULL); + + if (sess->recv_core >= 0) + bps.bind_core |= (1 << sess->recv_core); + if (sess->send_core >= 0) + bps.bind_core |= (1 << sess->send_core); + + if (sess->fd >= 0) + { + _close (sess->fd); + sess->fd = -1; + } + + bps.sess_num--; + + sess->head.prev->head.next = sess->head.next; + sess->head.next->head.prev = sess->head.prev; + + sess->head.next = bps.free_sess; + bps.free_sess = sess; +} + +struct bps_sess * +bps_start (int fd) +{ + int ret; + struct bps_sess *sess; + + if (!bps.free_sess) + { + _close (fd); + return NULL; + } + + sess = bps.free_sess; + bps.free_sess = sess->head.next; + + sess->fd = fd; + sess->state = BPS_RUNNING; + sess->index = (uint16_t) (++bps.global_index); + sess->recv_core = sess->send_core = -1; + sess->recv_tid = sess->send_tid = 0; + + ++bps.sess_num; + + sess->head.next = (struct bps_sess *) &bps.sess_head; + sess->head.prev = bps.sess_head.prev; + bps.sess_head.prev->head.next = sess; + bps.sess_head.prev = sess; + + ret = set_nonblock (fd); + ERR_GOTO (ret, ERR_EXIT, "fcntl(%d, F_SETFL, O_NONBLOCK)=%d:%d\n", fd, ret, + errno); + + if (bps.io_mode & BPS_IO_SEND) + { + sess->send_core = bps_cpu (); + sess->send_tid = lb_thread (bps_send, sess, "bps-send-%d", fd); + ERR_GOTO (sess->send_tid == 0, ERR_EXIT, "lb_thread(send:%d)=0:%d\n", + fd, errno); + if (sess->send_core >= 0) + { + lb_setcpu (sess->send_tid, sess->send_core); + } + } + + if (bps.io_mode & BPS_IO_RECV) + { + sess->recv_core = bps_cpu (); + sess->recv_tid = lb_thread (bps_recv, sess, "bps-recv-%d", fd); + ERR_GOTO (sess->recv_tid == 0, ERR_EXIT, "lb_thread(recv:%d)=0:%d\n", + fd, errno); + if (sess->recv_core >= 0) + { + lb_setcpu (sess->recv_tid, sess->recv_core); + } + } + + if (BPS_SESS_ID (sess) > bps.max_sess_id) + bps.max_sess_id = BPS_SESS_ID (sess); + + return sess; + +ERR_EXIT: + bps_stop (sess); + return NULL; +} + +void +bps_accept () +{ + while (bps.state == BPS_RUNNING) + { + int ret, fd; + struct bps_sess *sess; + struct sockaddr_in addr = { 0 }, s_addr = + { + 0}; + socklen_t len = sizeof (addr); + + fd = _accept (bps.listen_fd, (struct sockaddr *) &addr, &len); + if (fd < 0) + { + if (! + (errno == ETIMEDOUT || errno == EWOULDBLOCK || errno == EAGAIN)) + { + wrn ("accept(%d)=%d:%d\n", bps.listen_fd, fd, errno); + } + return; + } + + len = sizeof (s_addr); + ret = _getsockname (fd, (struct sockaddr *) &s_addr, &len); + if (ret) + { + _close (fd); + wrn ("getsockname(%d)=%d:%d\n", fd, ret, errno); + continue; + } + + sess = bps_start (fd); + if (sess) + { + out ("[%d:%d] accepted %s --> %s\n", sess->index, fd, + f_inaddr (&addr), f_inaddr (&s_addr)); + } + else + { + out ("[ERR:%d] accept %s --> %s FAILED\n", fd, f_inaddr (&addr), + f_inaddr (&s_addr)); + } + } +} + +void +bps_output (uint16_t index, int fd, uint64_t nsec, struct bps_rec *rec) +{ + const int UBPS = bps.exact ? 1 : MB; + const int UPPS = bps.exact ? 1 : KB; + const int SBPS = bps.exact ? 14 : 6; + const int SPPS = bps.exact ? 10 : 5; + + char buf[256]; + char *pos = buf; + + *pos++ = ' '; + pos += r_uint (pos, lb_gdiv ((rec->rcv + rec->snd) * 8, nsec) / UBPS, SBPS); + *pos++ = ' '; + pos += + r_uint (pos, lb_gdiv ((rec->rcv + rec->snd), nsec * bps.msg_len) / UPPS, + SPPS); + *pos++ = ' '; + *pos++ = '|'; + *pos++ = ' '; + pos += r_uint (pos, lb_gdiv (rec->snd * 8, nsec) / UBPS, SBPS); + *pos++ = ' '; + pos += r_uint (pos, lb_gdiv (rec->snd, nsec * bps.msg_len) / UPPS, SPPS); + *pos++ = ' '; + *pos++ = '|'; + *pos++ = ' '; + pos += r_uint (pos, lb_gdiv (rec->rcv * 8, nsec) / UBPS, SBPS); + *pos++ = ' '; + pos += r_uint (pos, lb_gdiv (rec->rcv, nsec * bps.msg_len) / UPPS, SPPS); + + if (index == 0) + { + if (fd > 60 * 60) + pos += + sprintf (pos, " | %d:%02d:%02d", fd / 3600, fd % 3600 / 60, + fd % 60); + else + pos += sprintf (pos, " | %02d:%02d", fd / 60, fd % 60); + } + else + { + pos += sprintf (pos, " | %u-%d", index, fd); + } + + *pos = 0; + + out ("%s\n", buf); +} + +void +bps_report (const struct timespec *now) +{ + static const char *HEAD[] = { + " T:mbps kpps | S:mbps kpps | R:mbps kpps | info", + " total:bps pps | send:bps pps | recv:bps pps | info" + }; + static int report_set = 0; + + int i; + struct bps_sess *sess; + struct bps_rec rec, *last = bps.rec_now; + uint64_t nsec = LB_SUB_NS (*now, bps.last_time); + + bps.rec_now = bps.rec_list[last == bps.rec_list[0] ? 1 : 0]; + + lb_sleep (0, BPS_EXCH_DELAY * NSOFMS); /* wait memory */ + + rec.rcv = rec.snd = 0; + for (i = 0; i <= bps.max_sess_id; ++i) + { + rec.rcv += last[i].rcv; + rec.snd += last[i].snd; + } + + if (rec.rcv == 0 && rec.snd == 0) + { + if (report_set != 0) + { + out ("\n"); + report_set = 0; + } + return; + } + + if (report_set++ == 0) + out ("%s\n", HEAD[bps.exact]); + + nsec = LB_SUB_NS (*now, bps.last_time); + + sess = bps.sess_head.next; + while (sess != (struct bps_sess *) &bps.sess_head) + { + struct bps_sess *next = sess->head.next; + + if (bps.verbose) + bps_output (sess->index, sess->fd, nsec, last + BPS_SESS_ID (sess)); + + if (sess->state != BPS_RUNNING) + bps_stop (sess); + + sess = next; + } + + bps_output (0, (int) (LB_SUB_NS (*now, bps.begin_time) / NSOFS), nsec, + &rec); + + (void) memset (last, 0, sizeof (struct bps_rec) * (bps.max_sess_id + 1)); +} + +void +bps_loop () +{ + while (bps.state == BPS_RUNNING) + { + struct timespec now; + + LB_TIME (now); + + if (LB_CMP (now, bps.next_time) >= 0) + { + bps_report (&now); + bps.last_time = now; + bps.next_time.tv_sec += bps.report_time; + } + + if (bps.client_mode) + { + if (LB_CMP_S (now, bps.begin_time, bps.test_time)) + { + bps.state = BPS_STOP; + break; + } + } + else + { + bps_accept (); + } + + lb_sleep (0, BPS_STAT_TIMER * NSOFMS); + } +} + +int +bps_server () +{ + int ret; + + /* server socket listen */ + + bps.listen_fd = _socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (bps.listen_fd < 0, 1, "socket()=%d:%d\n", bps.listen_fd, errno); + + ret = + _bind (bps.listen_fd, (struct sockaddr *) &bps.server_addr, + sizeof (bps.server_addr)); + ERR_RETURN (ret, 1, "bind(%d)=%d:%d\n", bps.listen_fd, ret, errno); + + ret = _listen (bps.listen_fd, 10); + ERR_RETURN (ret, 1, "listen(%d)=%d:%d\n", bps.listen_fd, ret, errno); + + ret = set_nonblock (bps.listen_fd); + ERR_RETURN (ret, 1, "set_nonblock(%d) failed\n", bps.listen_fd); + + out ("[%d] listen on %s\n", bps.listen_fd, f_inaddr (&bps.server_addr)); + + return 0; +} + +int +bps_client () +{ + int i, fd; + + for (i = 0; i < bps.parallel; ++i) + { + int ret; + struct bps_sess *sess; + struct sockaddr_in addr = { 0 }; + socklen_t len = sizeof (addr); + + fd = _socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (fd < 0, 1, "socket()=%d:%d\n", fd, errno); + + if (bps.client_bind) + { + ret = + _bind (fd, (struct sockaddr *) &bps.bind_addr, + sizeof (bps.bind_addr)); + ERR_GOTO (ret, ERR_EXIT, "bind(%d, %s)=%d:%d\n", fd, + f_inaddr (&bps.bind_addr), ret, errno); + } + + ret = + _connect (fd, (struct sockaddr *) &bps.server_addr, + sizeof (bps.server_addr)); + ERR_GOTO (ret, ERR_EXIT, "connect(%d, %s)=%d:%d\n", fd, + f_inaddr (&bps.server_addr), ret, errno); + + ret = _getsockname (fd, (struct sockaddr *) &addr, &len); + ERR_GOTO (ret, ERR_EXIT, "getsockname(%d)=%d:%d\n", fd, ret, errno); + + sess = bps_start (fd); + if (ret) + { + return 1; + } + + out ("[%d:%d] connected %s --> %s\n", sess->index, fd, f_inaddr (&addr), + f_inaddr (&bps.server_addr)); + } + + return 0; + +ERR_EXIT: + _close (fd); + return 1; +} + +void +bps_exit () +{ + while (bps.sess_head.next != (struct bps_sess *) &bps.sess_head) + { + struct bps_sess *sess = bps.sess_head.next; + + bps_stop (sess); + } + + if (bps.listen_fd >= 0) + { + _close (bps.listen_fd); + bps.listen_fd = -1; + } +} + +int +bps_init () +{ + int i; + struct bps_sess *sess; + + bps.listen_fd = -1; + bps.rec_now = bps.rec_list[0]; + + if (bps.parallel == 0) + bps.parallel = BPS_DEF_PARALLEL; + if (bps.test_time == 0) + bps.test_time = BPS_DEF_TIME; + if (bps.report_time == 0) + bps.report_time = BPS_DEF_REPORT_TIME; + if (bps.io_mode == 0) + bps.io_mode = BPS_IO_SEND | BPS_IO_RECV; + if (bps.msg_len == 0) + bps.msg_len = BPS_DEF_LEN; + if (bps.buf_size < bps.msg_len) + bps.buf_size = bps.msg_len; + + bps.free_sess = sess = bps.sess_list; + for (i = 0; i < BPS_MAX_SESS - 1; ++i) + { + sess->head.next = sess + 1; + sess++; + } + sess->head.next = NULL; + + bps.sess_head.next = bps.sess_head.prev = + (struct bps_sess *) &bps.sess_head; + + LB_TIME (bps.begin_time); + bps.last_time = bps.next_time = bps.begin_time; + bps.next_time.tv_sec += bps.report_time; + + return 0; +} + +#ifndef EXEC_BPS_ +#define EXEC_BPS_ + +#define OPTIONS "i:l:B:cC:SRt:b:p:m:veh" DBGOPT + +static const struct option options[] = { + {"interval", 1, 0, 'i'}, + {"length", 1, 0, 'l'}, + {"buffer", 1, 0, 'B'}, + {"client", 0, 0, 'c'}, + {"core", 1, 0, 'C'}, + {"time", 1, 0, 't'}, + {"bind", 1, 0, 'b'}, + {"parallel", 1, 0, 'p'}, + {"send-only", 0, 0, 'S'}, + {"recv-only", 0, 0, 'R'}, + {"verbose", 0, 0, 'v'}, + {"exact", 0, 0, 'e'}, + {"help", 0, 0, 'h'}, + DBGOPT_LONG {0, 0, 0, 0} +}; + +void +bps_usage (const char *name) +{ + out ("USAGE: %s [OPTIONS] [SERVER-ADDRESS] # %s version\n", name, + VERSION_NAME); + out (" Options:\n"); + out (" -h, --help help\n"); + out (" -v, --verbose show more statistics\n"); + out (" -e, --exact show exact value\n"); + out (" -i, --interval=SECONDS report time(default:%ds)\n", + BPS_DEF_REPORT_TIME); + out (" -l, --length=LENGTH message length(default:%d max:%d)\n", + BPS_DEF_LEN, BPS_MAX_LEN); + out + (" -B, --buffer=BUFFER recv buffer size(default:LENGTH max:%d)\n", + BPS_MAX_LEN); + out + (" -C, --core=COREMASK bound core mask HEX(default:0(no bind core))\n"); + out (" -S, --send-only only send\n"); + out (" -R, --recv-only only receive\n"); + out (" -c, --client client mode\n"); + out (" Client mode options:\n"); + out (" -t, --time=SECOND test time(default:%ds)\n", + BPS_DEF_TIME); + out (" -b, --bind=ADDRESS bind address\n"); + out + (" -p, --parallel=# parallel number(default:%d max:%d)\n", + BPS_DEF_PARALLEL, BPS_MAX_PARALLEL); +#ifdef DEBUG + out (" -D, --debug show debug information\n"); +#endif + out (" ADDRESS: X.X.X.X:PORT default port:%u\n", BPS_DEF_PORT); +} + +int +bps_args (int argc, char *argv[]) +{ + const char *end; + int opt, index; + + bps.bind_addr.sin_family = AF_INET; + bps.bind_addr.sin_addr.s_addr = INADDR_ANY; + bps.bind_addr.sin_port = 0; + + bps.server_addr.sin_family = AF_INET; + bps.server_addr.sin_addr.s_addr = INADDR_ANY; + bps.server_addr.sin_port = htons (BPS_DEF_PORT); + + while (EOF != (opt = getopt_long (argc, argv, OPTIONS, options, &index))) + { + switch (opt) + { + case 'i': + bps.report_time = atoi (optarg); + break; + case 'l': + bps.msg_len = atoi (optarg); + ERR_RETURN (bps.msg_len > BPS_MAX_LEN, 1, + "Message len must between 1 and %d\n", BPS_MAX_LEN); + break; + case 'B': + bps.buf_size = atoi (optarg); + break; + case 'c': + bps.client_mode = 1; + break; + case 'C': + bps.bind_core = p_hex (optarg, &end); + ERR_RETURN (!end + || *end, 1, "Invalid bind core mask '%s'\n", optarg); + break; + case 't': + bps.test_time = atoi (optarg); + break; + case 'b': + ERR_RETURN (p_addr (optarg, &bps.bind_addr), 1, + "Invalid bind address '%s'\n", optarg); + bps.client_bind = 1; + break; + case 'p': + bps.parallel = atoi (optarg); + ERR_RETURN (bps.parallel > BPS_MAX_PARALLEL || bps.parallel <= 0, 1, + "Parallel must between 1 and %d\n", BPS_MAX_PARALLEL); + break; + case 'S': + bps.io_mode = BPS_IO_SEND; + break; + case 'R': + bps.io_mode = BPS_IO_RECV; + break; + case 'v': + bps.verbose = 1; + break; + case 'e': + bps.exact = 1; + break; +#ifdef DEBUG + case 'D': + enable_debug = 1; + break; +#endif + case 'h': + bps_usage (argv[0]); + exit (0); + case '?': + err ("Invalid arguments\n"); + return 1; + default: + err ("Unknown option '%c'.\n", opt); + return 1; + } + } + + if (optind == argc - 1) + { + ERR_RETURN (p_addr (argv[optind], &bps.server_addr), 1, + "Invalid server address '%s'\n", argv[optind]); + } + else if (optind < argc) + { + while (optind < argc) + err ("Unknown argument '%s'\n", argv[optind++]); + return 1; + } + else if (bps.client_mode) + { + bps.server_addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); + } + + printf + ("bps param: verbose %d\n exact %d\n client_mode %d\n client_bind %d\n io_mode %d\n parallel %d\n \ + buf_size %d\n msg_len %d\n bind_core %lu\n report_time %d\n test_time %d\n bind_addr %x\n server_addr %x\n", + bps.verbose, bps.exact, bps.client_mode, bps.client_bind, bps.io_mode, bps.parallel, bps.buf_size, bps.msg_len, bps.bind_core, bps.report_time, bps.test_time, + bps.bind_addr.sin_addr.s_addr, bps.server_addr.sin_addr.s_addr); + return 0; +} + +void +bps_break (int s) +{ + if (bps.state < 0) + exit (1); + + out ("\n"); + + bps.state = BPS_BREAK; +} + +void +bps_sigpipe (int s) +{ + DBG ("SIGPIPE\n"); +} + +void +bps_set_sig () +{ + struct sigaction s = { 0 }; + + (void) sigemptyset (&s.sa_mask); + + s.sa_flags = SA_NODEFER; + s.sa_handler = (void *) bps_break; + (void) sigaction (SIGINT, &s, NULL); + (void) sigaction (SIGQUIT, &s, NULL); + + s.sa_handler = bps_sigpipe; + (void) sigaction (SIGPIPE, &s, NULL); +} + +#endif /* #ifndef EXEC_BPS_ */ + +int +main (int argc, char *argv[]) +{ + int ret; + enable_debug = 1; + if (bps_args (argc, argv)) + return 1; + + if (bps_init ()) + return 1; + + bps_set_sig (); + + if (bps.client_mode) + ret = bps_client (); + else + ret = bps_server (); + + if (!ret) + bps_loop (); + + bps_exit (); + + return ret; +} diff --git a/thirdparty/apps/testapp/bps/bps.h b/thirdparty/apps/testapp/bps/bps.h new file mode 100644 index 0000000..b91bd8d --- /dev/null +++ b/thirdparty/apps/testapp/bps/bps.h @@ -0,0 +1,112 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef _BPS_H_ +#define _BPS_H_ + +#define BPS_EXCH_DELAY 10 /* ms */ +#define BPS_STAT_TIMER 49 /* ms */ + +#define BPS_MAX_PARALLEL 128 +#define BPS_MAX_SESS 1024 + +#define BPS_MAX_LEN (1 * 1024) + +#define BPS_DEF_PORT 58177 +#define BPS_DEF_LEN 458 +#define BPS_DEF_PARALLEL 1 +#define BPS_DEF_TIME 30 +#define BPS_DEF_REPORT_TIME 1 + +#define BPS_IO_SEND 0x1 +#define BPS_IO_RECV 0x2 + +#define BPS_BREAK (-1) +#define BPS_RUNNING 0 +#define BPS_STOP 1 + +#define BPS_ERROR 64 +#define BPS_SEND_ERROR (BPS_ERROR | 1) +#define BPS_RECV_ERROR (BPS_ERROR | 2) + +struct bps_rec +{ + uint64_t rcv; + uint64_t snd; +}; + +struct bps_sess_head +{ + struct bps_sess *next; + struct bps_sess *prev; +}; + +struct bps_sess +{ + struct bps_sess_head head; //must be first + + int fd; + uint16_t index; + volatile short state; + + int recv_core; + int send_core; + pthread_t recv_tid; + pthread_t send_tid; +}; + +struct bps_var +{ + /* begin config */ + int verbose; + int exact; + int client_mode; + int client_bind; + + int io_mode; + int parallel; + int buf_size; + int msg_len; + + uint64_t bind_core; + int report_time; + int test_time; + struct sockaddr_in bind_addr; + struct sockaddr_in server_addr; + + /* end config */ + + uint16_t global_index; + short state; + int listen_fd; + + struct bps_rec rec_list[2][BPS_MAX_SESS]; + struct bps_rec *rec_now; + + struct timespec begin_time; + struct timespec last_time; + struct timespec next_time; + + struct bps_sess sess_list[BPS_MAX_SESS]; + struct bps_sess *free_sess; + struct bps_sess_head sess_head; + int sess_num; + int max_sess_id; +}; + +#define BPS_SESS_ID(sess) ((sess) - &bps.sess_list[0]) + +#endif /* #ifndef _BPS_H_ */ diff --git a/thirdparty/apps/testapp/cps/cps.c b/thirdparty/apps/testapp/cps/cps.c new file mode 100644 index 0000000..377d0de --- /dev/null +++ b/thirdparty/apps/testapp/cps/cps.c @@ -0,0 +1,833 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" +#include "cps.h" + +void *cps_s_thread (void *arg); +void *cps_c_thread (void *arg); + +static const char *const cps_stat_name[CPS_CNT_NUM] = { + [CPS_CNT_CONN_MAX] = "max", + [CPS_CNT_CONN_NUM] = "conn", + + [CPS_CNT_GTFD] = "GREATER-THAN-MAX-FD", + [CPS_CNT_SOCKET_ERR] = "socket-err", + [CPS_CNT_BIND_ERR] = "bind-err", + [CPS_CNT_CONNECT_ERR] = "connect-err", + [CPS_CNT_REUSEADDR_ERR] = "reuseaddr-err", + [CPS_CNT_NODELAY_ERR] = "nodelay-err", + [CPS_CNT_NONBLOCK_ERR] = "nonblock-err", + [CPS_CNT_ACCEPT_ERR] = "accept-err", + [CPS_CNT_SEND_ERR] = "send-err", + [CPS_CNT_RECV_ERR] = "recv-err", + [CPS_CNT_EPOLL_ERR] = "epoll-err", + [CPS_CNT_ERR_EVENT] = "err-event", + [CPS_CNT_FD_ERR] = "cid-err", +}; + +struct cps_var cps = { 0 }; + +void +cps_title () +{ + out ("------------------------------------------------------------\n"); + out (" cps test %s\n", cps.client ? "client" : "server"); + out + (" total server: %d total thread: %d report interval: %ds request:%d response: %d CPU NUM: %d\n", + cps.server_num, cps.thread_num, cps.interval, cps.req_len, cps.res_len, + cps.CPU_NUM); + if (cps.client) + { + out (" total client: %d test time: %ds defalut rate:%lu\n", + cps.client_num, cps.test_time, cps.rate); + } + else + { + } + out ("------------------------------------------------------------\n"); +} + +inline static char * +cps_tip (char *pos, char tip) +{ + *pos++ = '|'; + *pos++ = tip; + *pos++ = ':'; + *pos++ = ' '; + return pos; +} + +inline static char * +cps_fmt (char *pos, uint64_t val, int *size) +{ + int s = r_uint (pos, val, *size); + pos += s; + *pos++ = ' '; + if (s > *size) + *size = s; + return pos; +} + +inline static char * +cps_format (char *pos, char tip, uint64_t val, uint64_t nsec, int *size) +{ + pos = cps_tip (pos, tip); + + if (cps.more) + pos = cps_fmt (pos, val, size); + + pos = cps_fmt (pos, lb_gdiv (val, nsec), size + 1); + + return pos; +} + +void +cps_output (int index, struct cps_stat *stat, uint64_t nsec) +{ + struct fmtsize + { + int s_init[2]; + int s_conn[3]; + int s_recv[3]; + int s_send[3]; + int s_fail[2]; + }; + static struct fmtsize size = { 0 }; + static int space_line = 0; + static char buf[512]; + + char *pos = buf; + int i, cnt_num = 0; + + if (!stat->rec[CPS_REC_INIT] && !stat->rec[CPS_REC_CONN] && + !stat->rec[CPS_REC_RECV] && !stat->rec[CPS_REC_SEND] + && !stat->rec[CPS_REC_FAIL]) + { + for (i = 0; i < CPS_CNT_NUM; ++i) + { + if (stat->cnt[i]) + break; + } + if (i >= CPS_CNT_NUM) + { + if (index < 0 && space_line++ == 0) + { + out ("\n"); + memset (&size, 0, sizeof (size)); + } + return; + } + } + + space_line = 0; + + if (index < 0) + pos += sprintf (pos, " sum "); + else + pos += sprintf (pos, " %3d ", index); + + pos = + cps_format (pos, (cps.client ? 'C' : 'A'), stat->rec[CPS_REC_INIT], nsec, + size.s_init); + pos = cps_format (pos, 'E', stat->rec[CPS_REC_CONN], nsec, size.s_conn); + if (cps.client) + { + pos = cps_format (pos, 'S', stat->rec[CPS_REC_SEND], nsec, size.s_send); + pos = cps_format (pos, 'R', stat->rec[CPS_REC_RECV], nsec, size.s_recv); + } + else + { + pos = cps_format (pos, 'R', stat->rec[CPS_REC_RECV], nsec, size.s_recv); + pos = cps_format (pos, 'S', stat->rec[CPS_REC_SEND], nsec, size.s_send); + } + pos = cps_format (pos, 'F', stat->rec[CPS_REC_FAIL], nsec, size.s_fail); + + pos = cps_tip (pos, 'T'); + pos = + cps_fmt (pos, + lb_sdiv (stat->rec[CPS_REC_CONN_TIME], stat->rec[CPS_REC_CONN]), + &size.s_conn[2]); + if (cps.client) + { + pos = + cps_fmt (pos, + lb_sdiv (stat->rec[CPS_REC_SEND_TIME], + stat->rec[CPS_REC_SEND]), &size.s_send[2]); + pos = + cps_fmt (pos, + lb_sdiv (stat->rec[CPS_REC_RECV_TIME], + stat->rec[CPS_REC_RECV]), &size.s_recv[2]); + } + else + { + pos = + cps_fmt (pos, + lb_sdiv (stat->rec[CPS_REC_RECV_TIME], + stat->rec[CPS_REC_RECV]), &size.s_recv[2]); + pos = + cps_fmt (pos, + lb_sdiv (stat->rec[CPS_REC_SEND_TIME], + stat->rec[CPS_REC_SEND]), &size.s_send[2]); + } + + *pos++ = '|'; + *pos = 0; + + out ("%s", buf); + + for (i = 0; i < CPS_CNT_NUM; ++i) + { + if (stat->cnt[i]) + { + if (cnt_num++ == 0) + out (" { %s:%s", cps_stat_name[i], f_uint (stat->cnt[i])); + else + out (" %s:%s", cps_stat_name[i], f_uint (stat->cnt[i])); + } + } + + if (cnt_num) + out (" }\n"); + else + out ("\n"); + + for (i = 1; i < CPS_ERR_NUM; ++i) + { + if (stat->err[i]) + out (" %s\n", i, f_uint (stat->err[i]), strerror (i)); + } + if (stat->err[0]) + out (" Other error\n", f_uint (stat->err[0])); +} + +void +cps_close () +{ + cps.run_state = CPS_CLOSING; +} + +void +cps_timer (uint64_t nsec) +{ + const static struct timespec delay = {.tv_sec = 0,.tv_nsec = + CPS_DELAY_MS * 1000 * 1000 + }; + + int i, j; + struct cps_stat sum = { 0 }; + struct cps_stat *curr = cps.curr; + + cps.curr = cps.next; + cps.next = curr; + + /*wait for cps.curr use */ + (void) nanosleep (&delay, NULL); + + for (i = 0; i < cps.thread_num; ++i, ++curr) + { + struct cps_thread *thread = cps.thread[i]; + + curr->cnt[CPS_CNT_CONN_NUM] = thread->conn_num; + if (cps.verbose) + cps_output (thread->index, curr, nsec); + + for (j = 0; j < CPS_REC_NUM; ++j) + { + sum.rec[j] += curr->rec[j]; + curr->rec[j] = 0; + } + + for (j = 0; j < CPS_CNT_NUM; ++j) + { + sum.cnt[j] += curr->cnt[j]; + curr->cnt[j] = 0; + } + + for (j = 0; j < CPS_ERR_NUM; ++j) + { + sum.err[j] += curr->err[j]; + curr->err[j] = 0; + } + } + + cps_output (-1, &sum, nsec); +} + +int +cps_loop () +{ + const static struct timespec timeout = {.tv_sec = 0,.tv_nsec = + CPS_TIMER_MS * 1000 * 1000 + }; + + struct timespec begin, from; + time_t next_time = cps.interval; + + LB_TIME (begin); + from = begin; + + while (cps.run_state == CPS_RUNNING) + { + struct timespec now; + + (void) nanosleep (&timeout, NULL); + + LB_TIME (now); + + if (cps.client) + { + if (LB_CMP_S (now, begin, cps.test_time)) + cps_close (); + } + + if (!LB_CMP_S (now, begin, next_time)) + continue; + + cps_timer (LB_SUB_NS (now, from)); + + from = now; + next_time += cps.interval; + } + + while (cps.run_state == CPS_CLOSING && cps.active_thread) + { + (void) nanosleep (&timeout, NULL); + } + + return 0; +} + +int +cps_start () +{ + int i; + void *(*proc) (void *); + const char *name; + + cps.conn = + (struct cps_conn *) malloc (sizeof (struct cps_conn) * CPS_MAX_FD); + ERR_RETURN (!cps.conn, -1, "Out of memory\n"); + + if (cps.thread_num <= 0) + { + struct cps_thread *thread = cps_new_thread (); + ERR_RETURN (!thread, -1, "Out of memory\n"); + + cps.server_num = 1; + thread->server_num = 1; + thread->s_addr[0].sin_family = AF_INET; + thread->s_addr[0].sin_port = htons (CPS_PORT_DEF); + if (cps.client) + { + cps.client_num = 1; + thread->client_num = 1; + thread->c_addr_num = 1; + thread->s_addr[0].sin_addr.s_addr = htonl (0x7F000001); + } + else + { + thread->s_addr[0].sin_addr.s_addr = INADDR_ANY; + } + } + else if (cps.client) + { + for (i = 0; i < cps.thread_num; ++i) + { + if (cps.thread[i]->client_num) + continue; + cps.thread[i]->client_num = 1; + cps.thread[i]->c_addr[0].ip = INADDR_ANY; + cps.thread[i]->c_addr[0].ip_num = 1; + cps.client_num++; + } + } + + if (cps.req_len <= 0) + cps.req_len = CPS_REQ_DEF; + if (cps.res_len <= 0) + cps.res_len = CPS_RES_DEF; + if (cps.evnum <= 0) + cps.evnum = CPS_EVNUM_DEF; + if (cps.interval <= 0) + cps.interval = CPS_INTERVAL_DEF; + if (cps.test_time <= 0) + cps.test_time = CPS_TIME_DEF; + if (cps.rate == 0) + cps.rate = CPS_RATE_DEF; + + cps.curr = cps.records[0]; + cps.next = cps.records[1]; + + if (cps.client) + { + proc = cps_c_thread; + name = "client"; + } + else + { + proc = cps_s_thread; + name = "server"; + } + + cps_title (); + + for (i = 0; i < cps.thread_num; ++i) + { + if (cps.thread[i]->rate == 0) + cps.thread[i]->rate = cps.rate; + + cps.thread[i]->epfd = _epoll_create (CPS_EPSIZE); + ERR_RETURN (cps.thread[i]->epfd < 0, -1, "epoll_create(%d)=%d:%d\n", + CPS_EPSIZE, cps.thread[i]->epfd, errno); + + cps.thread[i]->tid = + lb_thread (proc, cps.thread[i], "cps-%s-%d", name, i); + ERR_RETURN (cps.thread[i]->tid == 0, -1, "Create thread %s-%d failed", + name, i); + + if (cps.thread[i]->core >= 0) + { + int ret = lb_setcpu (cps.thread[i]->tid, cps.thread[i]->core); + WRN (ret != 0, "Bind core error thread:%d\n", i); + } + + __sync_fetch_and_add (&cps.active_thread, 1); + } + + cps.run_state = CPS_RUNNING; + futex_wake (&cps.run_state, cps.thread_num); + + return 0; +} + +void +cps_exit () +{ + int i; + + cps.run_state = CPS_EXIT; + + for (i = 0; i < cps.thread_num; ++i) + { + int fd; + struct cps_thread *thread = cps.thread[i]; + + if (!thread) + continue; + + if (thread->tid) + pthread_join (thread->tid, NULL); + + if (thread->epfd >= 0) + _close (thread->epfd); + + for (fd = thread->server; fd >= 0; fd = CPS_CONN (fd)->next) + _close (fd); + + for (fd = thread->conn; fd >= 0; fd = CPS_CONN (fd)->next) + _close (fd); + + cps.thread[i] = NULL; + free (thread); + } + + if (cps.conn) + free (cps.conn); +} + +void +cps_break (int s) +{ + DBG (" SIGNALED %d running:%d\n", s, cps.run_state); + out ("\n"); + + if (cps.run_state == CPS_INIT || cps.run_state == CPS_RUNNING) + cps_close (); + else if (cps.run_state != CPS_EXIT) + cps_exit (); + else + exit (1); +} + +void +cps_sigpipe (int s) +{ + DBG ("SIGPIPE\n"); +} + +int +cps_init () +{ + struct sigaction s = { 0 }; + + (void) sigemptyset (&s.sa_mask); + + s.sa_flags = SA_NODEFER; + s.sa_handler = (void *) cps_break; + (void) sigaction (SIGINT, &s, NULL); + (void) sigaction (SIGQUIT, &s, NULL); + + s.sa_handler = cps_sigpipe; + (void) sigaction (SIGPIPE, &s, NULL); + +// lb_sigsegv_setup(); + + cps.CPU_NUM = get_nprocs (); + + if (cps.CPU_NUM <= 0) + cps.CPU_NUM = 1; + + return 0; +} + +#ifndef EXEC_CPS_C_ +#define EXEC_CPS_C_ + +#define CPS_OPTIONS "d:e:T:t:r:ci:" DBGOPT "mvh" + +static const struct option cps_options[] = { + {"data", 1, 0, 'd'}, + {"interval", 1, 0, 'i'}, + {"evnum", 1, 0, 'e'}, + {"client", 0, 0, 'c'}, + {"thread", 1, 0, 't'}, + {"rate", 1, 0, 'r'}, + {"time", 1, 0, 'T'}, + DBGOPT_LONG {"more", 0, 0, 'm'}, + {"verbose", 0, 0, 'v'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} +}; + +enum +{ + CPSOPT_SERVER = 0, + CPSOPT_S, + CPSOPT_CLIENT, + CPSOPT_C, + CPSOPT_RATE, + CPSOPT_CORE, + CPSOPT_CF, + CPSOPT_SF, +}; + +char *const cps_tokens[] = { + [CPSOPT_SERVER] = "server", + [CPSOPT_S] = "s", + [CPSOPT_CLIENT] = "client", + [CPSOPT_C] = "c", + [CPSOPT_RATE] = "rate", + [CPSOPT_CORE] = "core", + [CPSOPT_CF] = "cf", + [CPSOPT_SF] = "sf", + NULL +}; + +void +cps_usage (const char *name) +{ + out ("USAGE: %s [OPTIONS] [SERVER-ADDRESS] # %s version\n", name, + VERSION_NAME); + out (" Options:\n"); + out + (" -i, --interval=# report time(default: %ds max:%ds)\n", + CPS_INTERVAL_DEF, CPS_INTERVAL_MAX); + out + (" -c, --client server address list for one thread\n"); + out + (" -e, --evnum epoll event number(default:%d max:%d)\n", + CPS_EVNUM_DEF, CPS_EVNUM_MAX); + out + (" -T, --time=# C test time(default: %ds max:%ds)\n", + CPS_TIME_DEF, CPS_TIME_MAX); + out + (" -d, --data=#[:#] C request and response data length(default:%d:%d max:%d)\n", + CPS_REQ_DEF, CPS_RES_DEF, CPS_DATA_MAX); + out + (" -r, --rate=#[k|m|w] C global connect rate per each thread(CPS, default: %d max:%d)\n", + CPS_RATE_DEF, CPS_RATE_MAX); + out + (" -t, --thread=CONFIG set one net and thread(max: %d)\n", + CPS_THREAD_MAX); + out + (" server=X.X.X.X:P server address set(max: %d)\n", + CPS_SERVER_MAX); + out + (" core=# bind to core\n"); + out + (" client=X.X.X.X C client ip address set(max: %d max ip: %d)\n", + CPS_CLIENT_IAS_MAX, CPS_CLIENT_MAX); + out + (" rate=# C set connect rate for this thread(default: use global set)\n"); + out + (" cf C client loop first(default: both)\n"); + out + (" sf C server loop first(default: both)\n"); +#ifdef DEBUG + out + (" -D, --debug show debug information\n"); +#endif + out + (" -m, --more show more statistics\n"); + out + (" -v, --verbose show thread statistics\n"); + out (" -h, --help help\n"); + out (" IMPORTANT:\n"); + out + (" socket() EMFILE(%d) error: ulimit -n 1048576\n", + EMFILE); + out + (" bind() EADDRINUSE(%d) error: echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle\n", + EADDRINUSE); + out + (" connect() EADDRNOTAVAIL(%d) error: echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse\n", + EADDRNOTAVAIL); + out + (" echo \"3000 65534\" > /proc/sys/net/ipv4/ip_local_port_range\n"); +} + +inline static uint64_t +cps_p_rate (const char *arg) +{ + uint64_t rate = p_uint (arg, CPS_RATE_MAX, &arg); + + if (!arg) + return (uint64_t) - 1ul; + + switch (*arg) + { + case 'm': /* fall through */ + case 'M': + rate *= 100; /* fall through */ + case 'w': /* fall through */ + case 'W': + rate *= 10; /* fall through */ + case 'k': /* fall through */ + case 'K': + rate *= 1000; + arg++; + break; + } + + if (*arg) + return (uint64_t) - 1ul; + + return rate; +} + +int +cps_opts (char *opts) +{ + struct cps_thread *thread; + struct inaddrs *client; + struct sockaddr_in *server; + + ERR_RETURN (cps.thread_num >= CPS_THREAD_MAX, -1, + "Too many thread, max %d\n", CPS_THREAD_MAX); + + thread = cps_new_thread (); + ERR_RETURN (!thread, -1, "Out of memory\n"); + server = thread->s_addr; + client = thread->c_addr; + + while (*opts) + { + char *value; + const char *end; + int ret = getsubopt (&opts, cps_tokens, &value); + switch (ret) + { + case CPSOPT_SERVER: + case CPSOPT_S: + { + int i; + uint64_t num; + struct inaddrs addr; + + end = p_addr_set (value, &addr, PA_DEF_PORT | CPS_PORT_DEF); + ERR_RETURN (!end + || *end, -1, "Invalid server address '%s'.\n", value); + + num = (uint64_t) addr.ip_num * addr.port_num; + ERR_RETURN (num > CPS_SERVER_MAX - thread->server_num, -1, + "Too many server, max %d\n", CPS_SERVER_MAX); + + for (i = 0; i < addr.ip_num; ++i) + { + uint32_t ip = addr.ip + i; + uint16_t j; + for (j = 0; j < addr.port_num; ++j) + { + server->sin_family = AF_INET; + server->sin_addr.s_addr = htonl (ip); + server->sin_port = htons (addr.port + j); + server++; + } + } + thread->server_num += num; + break; + } + case CPSOPT_CLIENT: + case CPSOPT_C: + { + ERR_RETURN (thread->c_addr_num >= CPS_CLIENT_IAS_MAX, -1, + "Too many client set, max %d\n", CPS_CLIENT_IAS_MAX); + + end = p_addr_set (value, client, PA_NO_PORT); + ERR_RETURN (!end + || *end, -1, "Invalid client address '%s'.\n", value); + ERR_RETURN (client->ip_num > CPS_CLIENT_MAX - thread->client_num, + -1, "Too many client, max %d\n", CPS_CLIENT_MAX); + + client->port = 0; + client->port_num = 1; + thread->c_addr_num++; + thread->client_num += client->ip_num; + client++; + break; + } + case CPSOPT_RATE: + thread->rate = cps_p_rate (value); + ERR_RETURN (thread->rate > CPS_RATE_MAX, -1, + "Invalid thread rate '%s'\n", value); + break; + + case CPSOPT_CF: + thread->loop = CPS_LOOP_CF; + break; + case CPSOPT_SF: + thread->loop = CPS_LOOP_SF; + break; + + case CPSOPT_CORE: + thread->core = (int) p_int (value, cps.CPU_NUM - 1, &end); + ERR_RETURN (!end || *end + || thread->core <= 0, -1, "Invalid bind core '%s'.\n", + value); + break; + + default: + ERR_RETURN (1, -1, "Unknown thread option '%s'\n", value); + } + } + + ERR_RETURN (!thread->server_num, -1, "No server set for net %d\n", + thread->index); + + cps.server_num += thread->server_num; + cps.client_num += thread->client_num; + return 0; +} + +int +cps_args (int argc, char *argv[]) +{ + int opt, index; + + while (EOF != + (opt = getopt_long (argc, argv, CPS_OPTIONS, cps_options, &index))) + { + const char *end; + + switch (opt) + { + case 't': + if (cps_opts (optarg)) + return -1; + break; + + case 'c': + cps.client = 1; + break; + + case 'd': + cps.req_len = (int) p_int (optarg, CPS_DATA_MAX, &end); + ERR_RETURN (!end, -1, "Invalid data length '%s'\n", optarg); + if (*end == ':') + { + end++; + cps.res_len = (int) p_int (end, CPS_DATA_MAX, &end); + ERR_RETURN (!end, -1, "Invalid response data length '%s'\n", + optarg); + } + else + { + cps.res_len = cps.req_len; + } + ERR_RETURN (*end != 0, -1, "Invalid data length '%s'\n", optarg); + break; + + case 'i': + cps.interval = (int) p_int (optarg, CPS_INTERVAL_MAX, &end); + ERR_RETURN (!end || *end, -1, "Invalid interval '%s'\n", optarg); + break; + case 'e': + cps.evnum = (int) p_int (optarg, CPS_EVNUM_MAX, &end); + ERR_RETURN (!end + || *end, -1, "Invalid event number '%s'\n", optarg); + break; + case 'T': + cps.test_time = (int) p_int (optarg, CPS_TIME_MAX, &end); + ERR_RETURN (!end || *end, -1, "Invalid test time '%s'\n", optarg); + break; + case 'r': + cps.rate = cps_p_rate (optarg); + ERR_RETURN (cps.rate > CPS_RATE_MAX, -1, "Invalid rate '%s'\n", + optarg); + break; + case 'v': + cps.verbose = 1; + break; + case 'm': + cps.more = 1; + break; + +#ifdef DEBUG + case 'D': + enable_debug = 1; + break; +#endif + case 'h': + cps_usage (argv[0]); + exit (0); + case '?': + err ("Invalid arguments\n"); + return -1; + default: + err ("Unknown option '%c'.\n", opt); + return -1; + } + } + + return 0; +} + +#endif /* #ifndef EXEC_CPS_C_ */ + +int +main (int argc, char *argv[]) +{ + int ret; + + if (cps_init ()) + return 1; + + cps_args (argc, argv) || cps_start () || cps_loop (); + + cps_exit (); + return 0; +} diff --git a/thirdparty/apps/testapp/cps/cps.h b/thirdparty/apps/testapp/cps/cps.h new file mode 100644 index 0000000..9b949f3 --- /dev/null +++ b/thirdparty/apps/testapp/cps/cps.h @@ -0,0 +1,283 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef _CPS_H_ +#define _CPS_H_ + +#define CPS_FRAG_NUM 64 +#define CPS_FRAG_NS (20 * 1000 * 1000) /* 20 ms */ +#define CPS_FRAG_LOOP (CPS_FRAG_NS * CPS_FRAG_NUM) /* 1.28 s */ + +#define CPS_TIMER_MS 100 +#define CPS_DELAY_MS 5 + +#define CPS_MAX_FD (16 * 1024 * 1024) /* 10M */ +#define CPS_CONN_MAX (256 * 1024) + +#define CPS_EPSIZE (1 * 1000) /* =1k */ +#define CPS_EPWAIT_MS 200 /* ms */ + +#define CPS_THREAD_MAX 128 +#define CPS_SERVER_MAX 32 +#define CPS_CLIENT_MAX 256 +#define CPS_CLIENT_IAS_MAX 32 + +#define CPS_PORT_DEF 58166 +#define CPS_EVNUM_DEF 256 +#define CPS_EVNUM_MAX 1024 +#define CPS_TIME_DEF 300 +#define CPS_TIME_MAX (60 * 60 * 24 * 7) /* 604800s = 1 week */ +#define CPS_RATE_DEF 10000 /* 1w */ +#define CPS_RATE_MAX (100 * 1000 * 1000) /* 100m */ +#define CPS_REQ_DEF 1 +#define CPS_RES_DEF 1 +#define CPS_DATA_MAX 4096 +#define CPS_INTERVAL_DEF 10 /* s */ +#define CPS_INTERVAL_MAX 3600 /* s */ + +#define CPS_ERR_NUM 256 + +#define CPS_CONN_SID (-1) +#define CPS_EV_DATA(sid, fd) (((uint64_t)(uint32_t)(sid) << 32) | (uint64_t)(uint32_t)(fd)) +#define CPS_EV_FD(u64) ((int)(uint32_t)(u64)) +#define CPS_EV_SID(u64) ((int)(uint32_t)((u64) >> 32)) + +enum +{ + CPS_LOOP_BOTH = 0, + CPS_LOOP_CF, + CPS_LOOP_SF, +}; + +enum +{ + CPS_ERROR = -2, + CPS_EXIT = -1, + + CPS_INIT = 0, + + CPS_RUNNING = 1, + CPS_CLOSING = 2, +}; + +enum +{ + CPS_CNT_CONN_MAX, + CPS_CNT_CONN_NUM, + + CPS_CNT_GTFD, + CPS_CNT_SOCKET_ERR, + CPS_CNT_BIND_ERR, + CPS_CNT_ACCEPT_ERR, + CPS_CNT_CONNECT_ERR, + CPS_CNT_REUSEADDR_ERR, + CPS_CNT_NODELAY_ERR, + CPS_CNT_NONBLOCK_ERR, + CPS_CNT_SEND_ERR, + CPS_CNT_RECV_ERR, + CPS_CNT_EPOLL_ERR, + CPS_CNT_ERR_EVENT, + CPS_CNT_FD_ERR, + + CPS_CNT_NUM +}; + +#define CPS_CNT_ITEM(thread, id) (cps.curr[(thread)->index].cnt[(id)]) + +#define CPS_CNT_INC(thread, id) (++CPS_CNT_ITEM((thread), (id))) +#define CPS_CNT_INC_E(thread, id, e) do { \ + struct cps_stat *_stat = cps.curr + (thread)->index; \ + ++_stat->cnt[(id)]; \ + if ((e) >= CPS_ERR_NUM) ++_stat->err[0];\ + else ++_stat->err[(e)]; \ +} while(0) + +enum +{ + CPS_REC_INIT, + + CPS_REC_CONN, + CPS_REC_CONN_TIME, + + CPS_REC_RECV, + CPS_REC_RECV_TIME, + + CPS_REC_SEND, + CPS_REC_SEND_TIME, + + CPS_REC_FAIL, + + CPS_REC_NUM +}; + +#define CPS_REC_INC(thread, id) (++cps.curr[(thread)->index].rec[(id)]) + +#define CPS_REC_TIMED_INC(thread, id, last) do { \ + struct timespec _time; \ + struct cps_stat *_stat = cps.curr + (thread)->index; \ + LB_TIME(_time); \ + _stat->rec[(id)]++; \ + _stat->rec[(id) + 1] += LB_SUB_NS(_time, (last)); \ + (last) = _time; \ +} while (0) + +struct cps_conn +{ + union + { + int size; + int sid; + }; + int next; + int *prev; + struct timespec last; + + struct timespec create_time; +}; + +struct cps_stat +{ + uint64_t cnt[CPS_CNT_NUM]; + uint64_t rec[CPS_REC_NUM]; + uint64_t err[CPS_ERR_NUM]; +}; + +struct cps_thread +{ + int epfd; + int index; + int core; + int loop; + + int server; + int conn; + int conn_num; + + int server_num; + int client_num; + int c_addr_num; + + uint64_t rate; + pthread_t tid; + + struct sockaddr_in s_addr[CPS_SERVER_MAX]; + struct inaddrs c_addr[CPS_CLIENT_MAX]; + struct epoll_event event[CPS_EVNUM_MAX]; +}; + +struct cps_var +{ + int run_state; + int CPU_NUM; + + int verbose; + int more; + int client; + int evnum; + + int req_len; + int res_len; + + int test_time; + int interval; + + int active_thread; + int thread_num; + int server_num; + int client_num; + + uint64_t rate; + + struct cps_stat *curr; + struct cps_stat *next; + + struct cps_conn *conn; + struct cps_thread *thread[CPS_THREAD_MAX]; + + struct cps_stat records[2][CPS_THREAD_MAX]; +}; + +extern struct cps_var cps; + +inline static struct cps_conn * +CPS_CONN (int fd) +{ + return cps.conn + fd; +} + +inline static void +cps_add_server (struct cps_thread *thread, int fd, int sid) +{ + struct cps_conn *conn = CPS_CONN (fd); + + conn->sid = sid; + conn->next = thread->server; + conn->prev = &thread->server; + LB_TIME (conn->last); + if (thread->server >= 0) + CPS_CONN (thread->server)->prev = &conn->next; + thread->server = fd; +} + +inline static void +cps_add_conn (struct cps_thread *thread, int fd, int size, + struct timespec *begin) +{ + struct cps_conn *conn = CPS_CONN (fd); + + conn->size = size; + conn->last = *begin; + conn->next = thread->conn; + conn->prev = &thread->conn; + if (thread->conn >= 0) + CPS_CONN (thread->conn)->prev = &conn->next; + thread->conn = fd; + + if (++thread->conn_num > CPS_CNT_ITEM (thread, CPS_CNT_CONN_MAX)) + CPS_CNT_ITEM (thread, CPS_CNT_CONN_MAX) = thread->conn_num; + CPS_REC_TIMED_INC (thread, CPS_REC_CONN, conn->last); +} + +inline static void +cps_rem_conn (struct cps_thread *thread, int fd, struct cps_conn *conn) +{ + --thread->conn_num; + + *conn->prev = conn->next; + if (conn->next >= 0) + CPS_CONN (conn->next)->prev = conn->prev; +} + +inline static struct cps_thread * +cps_new_thread () +{ + struct cps_thread *thread = calloc (1, sizeof (struct cps_thread)); + + if (thread) + { + thread->index = cps.thread_num; + thread->epfd = -1; + thread->core = -1; + thread->conn = -1; + thread->server = -1; + + cps.thread[cps.thread_num++] = thread; + } + + return thread; +} + +#endif /* #ifndef _CPS_H_ */ diff --git a/thirdparty/apps/testapp/cps/cps_c.c b/thirdparty/apps/testapp/cps/cps_c.c new file mode 100644 index 0000000..dfcc341 --- /dev/null +++ b/thirdparty/apps/testapp/cps/cps_c.c @@ -0,0 +1,394 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" +#include "cps.h" + +struct cps_frag +{ + struct timespec begin; + uint64_t count; +}; + +struct cps_run +{ + struct cps_frag frag[CPS_FRAG_NUM]; + uint64_t total; + uint32_t fid; + int sid, ci, ii; + struct sockaddr_in addr; +}; + +inline static void +cps_c_next (struct cps_thread *thread, struct cps_run *run) +{ + if (thread->loop == CPS_LOOP_CF) + { + if (++run->ii >= thread->c_addr[run->ci].ip_num) + { + run->ii = 0; + if (++run->ci == thread->client_num) + { + run->ci = 0; + if (++run->sid >= thread->server_num) + run->sid = 0; + } + } + run->addr.sin_addr.s_addr = + htonl (thread->c_addr[run->ci].ip + run->ii); + } + else if (thread->loop == CPS_LOOP_SF) + { + if (++run->sid >= thread->server_num) + { + run->sid = 0; + if (++run->ii >= thread->c_addr[run->ci].ip_num) + { + run->ii = 0; + if (++run->ci >= thread->client_num) + run->ci = 0; + } + run->addr.sin_addr.s_addr = + htonl (thread->c_addr[run->ci].ip + run->ii); + } + } + else + { + if (++run->sid == thread->server_num) + run->sid = 0; + if (++run->ii >= thread->c_addr[run->ci].ip_num) + { + run->ii = 0; + if (++run->ci == thread->client_num) + run->ci = 0; + } + run->addr.sin_addr.s_addr = + htonl (thread->c_addr[run->ci].ip + run->ii); + } +} + +inline static int +cps_c_trigger (struct cps_thread *thread, struct cps_run *run) +{ + uint64_t nsec, num; + struct timespec now; + struct cps_frag *frag = &run->frag[run->fid % CPS_FRAG_NUM]; + struct cps_frag *from = &run->frag[(run->fid + 1) % CPS_FRAG_NUM]; + + LB_TIME (now); + + if (LB_CMP_NS (now, frag->begin, CPS_FRAG_NS)) + { + /* move to next fragment */ + frag = from; + from = &run->frag[++run->fid % CPS_FRAG_NUM]; + run->total -= frag->count; + frag->count = 0; + frag->begin = now; + } + + nsec = LB_SUB_NS (now, from->begin); + num = thread->rate * nsec / NSOFS; + if (num >= run->total) + { + run->total++; + frag->count++; + return 1; + } + + return 0; +} + +int +cps_c_create (struct cps_thread *thread, struct cps_run *run) +{ + int fd, ret; + struct timespec begin; + struct epoll_event event; + + CPS_REC_INC (thread, CPS_REC_INIT); + LB_TIME (begin); + + fd = _socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd < 0) + { + CPS_CNT_INC_E (thread, CPS_CNT_SOCKET_ERR, errno); + CPS_REC_INC (thread, CPS_REC_FAIL); + DBG ("->socket(...)=%d:%d\n", fd, errno); + return -1; + } + + if (fd >= CPS_MAX_FD) + { + CPS_CNT_INC (thread, CPS_CNT_GTFD); + goto ERR; + } + + ret = set_reuseaddr (fd, 1); + if (ret) + CPS_CNT_INC_E (thread, CPS_CNT_REUSEADDR_ERR, errno); + + ret = _bind (fd, (struct sockaddr *) &run->addr, sizeof (run->addr)); + if (ret) + { + CPS_CNT_INC_E (thread, CPS_CNT_BIND_ERR, errno); + DBG ("->bind(%d, %s)=%d:%d\n", fd, f_inaddr (&run->addr), ret, errno); + goto ERR; + } + ret = set_nodelay (fd, 1); + if (ret) + CPS_CNT_INC_E (thread, CPS_CNT_NODELAY_ERR, errno); + + ret = set_nonblock (fd); + if (ret) + { + CPS_CNT_INC_E (thread, CPS_CNT_NONBLOCK_ERR, errno); + goto ERR; + } + + ret = + _connect (fd, (struct sockaddr *) &thread->s_addr[run->sid], + sizeof (thread->s_addr[run->sid])); + if (ret) + { + const int e = errno; + if (e != EINPROGRESS) + { + CPS_CNT_INC_E (thread, CPS_CNT_CONNECT_ERR, e); + DBG ("->connect(%d, %s)=%d:%d\n", fd, + f_inaddr (&thread->s_addr[run->sid]), ret, errno); + goto ERR; + } + } + + event.events = EPOLLIN | EPOLLOUT | EPOLLET; + event.data.u64 = CPS_EV_DATA (CPS_CONN_SID, fd); + ret = _epoll_ctl (thread->epfd, EPOLL_CTL_ADD, fd, &event); + if (ret) + { + CPS_CNT_INC_E (thread, CPS_CNT_EPOLL_ERR, errno); + DBG ("->epoll_ctl(%d, ADD, %d)=%d:%d\n", thread->epfd, fd, ret, errno); + goto ERR; + } + + cps_add_conn (thread, fd, 0, &begin); + + return 0; + +ERR: + _close (fd); + CPS_REC_INC (thread, CPS_REC_FAIL); + return -1; +} + +int +cps_c_io (struct cps_thread *thread, int fd, uint32_t events) +{ + int ret; + static char buf[CPS_DATA_MAX]; + +// struct cps_server *server = &thread->server[sid]; + struct cps_conn *conn = CPS_CONN (fd); + + if (events & EPOLLERR) + { + CPS_CNT_INC (thread, CPS_CNT_ERR_EVENT); + DBG ("(%d, %d, %x) EPOLLERR\n", thread->index, fd, events); + goto ERR; + } + + if (conn->size >= 0) + { + if (0 == (events & EPOLLOUT)) + return 0; + + while (1) + { + if (cps.run_state <= CPS_INIT) + return -1; + + ret = _send (fd, buf, cps.req_len - conn->size, 0); + if (ret > 0) + { + conn->size += ret; + if (conn->size >= cps.req_len) + { + struct epoll_event event; + event.events = EPOLLIN; + event.data.u64 = CPS_EV_DATA (CPS_CONN_SID, fd); + conn->size = -cps.res_len; + ret = _epoll_ctl (thread->epfd, EPOLL_CTL_MOD, fd, &event); + if (ret) + { + CPS_CNT_INC_E (thread, CPS_CNT_EPOLL_ERR, errno); + DBG ("->epoll_ctl(%d, MOD, %d)=%d:%d\n", thread->epfd, + fd, ret, errno); + goto ERR; + } + CPS_REC_TIMED_INC (thread, CPS_REC_SEND, conn->last); + break; + } + } + else + { + if (ret < 0) + { + const int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN) + return 0; + if (e == EINTR) + continue; + CPS_CNT_INC_E (thread, CPS_CNT_SEND_ERR, e); + } + else + { + CPS_CNT_INC (thread, CPS_CNT_SEND_ERR); + } + DBG ("->send(%d,, %d)=%d:%d\n", fd, cps.req_len - conn->size, + ret, errno); + goto ERR; + } + } + } + + if (0 == (events & EPOLLIN)) + return 0; + + while (cps.run_state > CPS_INIT) + { + ret = _recv (fd, buf, -conn->size, 0); + if (ret > 0) + { + conn->size += ret; + if (conn->size >= 0) + { + /* receive success */ + _close (fd); + CPS_REC_TIMED_INC (thread, CPS_REC_RECV, conn->last); + cps_rem_conn (thread, fd, conn); + return 0; + } + } + else + { + if (ret < 0) + { + const int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN) + return 0; /*wait event */ + if (e == EINTR) /* The receive was interrupted by delivery of a signal... */ + continue; /*recv again */ + CPS_CNT_INC_E (thread, CPS_CNT_RECV_ERR, e); + } + else + { + CPS_CNT_INC (thread, CPS_CNT_RECV_ERR); + } + DBG ("->recv(%d,, %d)=%d:%d\n", fd, -conn->size, ret, errno); + goto ERR; /* ret == 0 and not block meaning error */ + } + } + + DBG ("(%d, %d) cannot run there\n", thread->index, fd); + +ERR: + _close (fd); + CPS_REC_INC (thread, CPS_REC_FAIL); + cps_rem_conn (thread, fd, conn); + return -1; +} + +void * +cps_c_thread (void *arg) +{ + int i, num = 0; + struct cps_run run = { 0 }; + struct cps_thread *thread = (struct cps_thread *) arg; + struct epoll_event *event = thread->event; + + run.addr.sin_family = AF_INET; + run.addr.sin_port = htons (0); + + out + ("[%d] initialize thread %ld client:%d server:%d rate:%lu core:%d epfd:%d\n", + thread->index, pthread_self (), thread->client_num, thread->server_num, + thread->rate, thread->core, thread->epfd); + + futex_wait (&cps.run_state, CPS_INIT); + + LB_TIME (run.frag[0].begin); + for (i = 1; i < CPS_FRAG_NUM; ++i) + run.frag[i].begin = run.frag[0].begin; + + while (1) + { + /* open 1 connect */ + if (cps.run_state == CPS_RUNNING) + { + if (cps_c_trigger (thread, &run)) + { + cps_c_next (thread, &run); + cps_c_create (thread, &run); + } + } + else if (cps.run_state == CPS_CLOSING) + { + if (thread->conn_num <= 0) + break; + } + else + { + break; + } + + /* process 1 event */ + if (num > 0) + { + int fd = CPS_EV_FD (event->data.u64); + DBG ("epoll event:{sid:%d fd:%d e:%x}\n", + CPS_EV_SID (event->data.u64), fd, event->events); + + if ((uint32_t) fd >= CPS_MAX_FD) + { + CPS_CNT_INC (thread, CPS_CNT_FD_ERR); + } + else + { + (void) cps_c_io (thread, fd, event->events); + } + + num--; + event++; + } + + /* wait events */ + if (num <= 0) + { + event = thread->event; + num = _epoll_wait (thread->epfd, event, cps.evnum, 0); /* no wait */ + if (num < 0) + { + int e = errno; + if (e != EINTR) + CPS_CNT_INC_E (thread, CPS_CNT_EPOLL_ERR, e); + } + } + + } + + __sync_fetch_and_sub (&cps.active_thread, 1); + return NULL; +} diff --git a/thirdparty/apps/testapp/cps/cps_s.c b/thirdparty/apps/testapp/cps/cps_s.c new file mode 100644 index 0000000..57d41c7 --- /dev/null +++ b/thirdparty/apps/testapp/cps/cps_s.c @@ -0,0 +1,320 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" +#include "cps.h" + +inline static void +cps_s_close (struct cps_thread *thread) +{ + int fd; + struct epoll_event ev = { 0 }; + + for (fd = thread->server; fd >= 0; fd = CPS_CONN (fd)->next) + { + (void) _epoll_ctl (thread->epfd, EPOLL_CTL_DEL, fd, &ev); + _close (fd); + } + + thread->server = -1; +} + +int +cps_s_listen (struct cps_thread *thread) +{ + int i; + + for (i = 0; i < thread->server_num; ++i) + { + int fd, ret; + struct timespec dummy; + struct epoll_event event; + struct sockaddr_in *server = &thread->s_addr[i]; + + fd = _socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (fd < 0, -1, "socket()=%d:%d\n", fd, errno); + + ret = _bind (fd, (struct sockaddr *) server, sizeof (*server)); + ERR_RETURN (ret, -1, "bind(%d)=%d:%d\n", fd, ret, errno); + + ret = set_nonblock (fd); + ERR_RETURN (ret, -1, "set_nonblock(%d)=%d:%d\n", fd, ret, errno); + + ret = _listen (fd, SOMAXCONN); + ERR_RETURN (ret, -1, "listen(%d)=%d:%d\n", fd, ret, errno); + + event.events = EPOLLIN | EPOLLET; + event.data.u64 = CPS_EV_DATA (i, fd); + ret = _epoll_ctl (thread->epfd, EPOLL_CTL_ADD, fd, &event); + ERR_RETURN (ret, -1, "epoll_ctl(%d, %d)=%d:%d\n", thread->epfd, fd, ret, + errno); + + out ("[%d.%d:%d] listen on %s\n", thread->index, i, fd, + f_inaddr (server)); + + cps_add_server (thread, fd, i); + } + + return 0; +} + +int +cps_s_accept (struct cps_thread *thread, int server_fd) +{ + while (cps.run_state == CPS_RUNNING) + { + int fd, ret; + struct timespec begin; + struct epoll_event event; +#if defined(DEBUG) + struct sockaddr_in addr; + socklen_t len = sizeof (addr); +#endif + + LB_TIME (begin); + +#if defined(DEBUG) && 0 + fd = + _accept4 (server_fd, (struct sockaddr *) &addr, &len, SOCK_NONBLOCK); +#else + fd = _accept4 (server_fd, NULL, NULL, SOCK_NONBLOCK); +#endif + if (fd < 0) + { + int e = errno; + if (e == EAGAIN || e == EWOULDBLOCK) + return 0; + DBG ("->accept4(%d)=%d:%d\n", server_fd, fd, e); + CPS_CNT_INC_E (thread, CPS_CNT_ACCEPT_ERR, errno); + return -1; + } + + CPS_REC_INC (thread, CPS_REC_INIT); +// DBG("(%d, %d) -> accepted(%d) %d: %s", thread->index, sid, server->fd, fd, f_inaddr(&addr)); + + if (fd >= CPS_MAX_FD) + { + _close (fd); + CPS_REC_INC (thread, CPS_REC_FAIL); + continue; + } + + ret = set_nodelay (fd, 1); + if (ret) + CPS_CNT_INC_E (thread, CPS_CNT_NODELAY_ERR, errno); + + event.events = EPOLLIN | EPOLLET; + event.data.u64 = CPS_EV_DATA (CPS_CONN_SID, fd); + ret = _epoll_ctl (thread->epfd, EPOLL_CTL_ADD, fd, &event); + if (ret) + { + _close (fd); + CPS_CNT_INC_E (thread, CPS_CNT_EPOLL_ERR, errno); + CPS_REC_INC (thread, CPS_REC_FAIL); + DBG ("epoll_ctl(%d, %d)=%d:%d\n", thread->epfd, fd, ret, errno); + continue; + } + + cps_add_conn (thread, fd, -cps.req_len, &begin); + } + + return 0; +} + +int +cps_s_io (struct cps_thread *thread, int fd, uint32_t events) +{ + static char buf[CPS_DATA_MAX]; + + int ret; + struct cps_conn *conn = CPS_CONN (fd); + + DBG ("(%d, %d, %x) conn:{size:%d next:%d prev:%p:%d}\n", + thread->index, fd, events, conn->size, conn->next, conn->prev, + *conn->prev); + + if (events & EPOLLERR) + { + CPS_CNT_INC (thread, CPS_CNT_ERR_EVENT); + DBG ("(%d, %d, 0x%x)\n", thread->index, fd, events); + goto ERR; + } + + if (0 == (events & EPOLLIN)) + return 0; + + while (1) + { + if (cps.run_state <= CPS_INIT) + goto ERR; + + ret = _recv (fd, buf, sizeof (buf), 0); + if (ret > 0) + { + conn->size += ret; + if (conn->size >= 0) + { + CPS_REC_TIMED_INC (thread, CPS_REC_RECV, conn->last); + break; /* receive success */ + } + } + else + { + if (ret < 0) + { + const int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN) + return 0; + if (e == EINTR) + continue; + CPS_CNT_INC_E (thread, CPS_CNT_RECV_ERR, e); + } + else + { + CPS_CNT_INC (thread, CPS_CNT_RECV_ERR); + } + DBG ("->recv(%d,, %ld)=%d:%d\n", fd, sizeof (buf), ret, errno); + goto ERR; + } + } + + conn->size = 0; + + while (cps.run_state > CPS_INIT) + { + ret = _send (fd, buf, cps.res_len - conn->size, 0); + if (ret > 0) + { + conn->size += ret; + if (conn->size >= cps.res_len) + { + _close (fd); + CPS_REC_TIMED_INC (thread, CPS_REC_SEND, conn->last); + cps_rem_conn (thread, fd, conn); + return 0; + } + } + else + { + if (ret < 0) + { + const int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN || e == EINTR) + continue; + CPS_CNT_INC_E (thread, CPS_CNT_SEND_ERR, e); + } + else + { + CPS_CNT_INC (thread, CPS_CNT_SEND_ERR); + } + DBG ("->send(%d,, %d)=%d:%d\n", fd, cps.res_len - conn->size, ret, + errno); + goto ERR; + } + } + + return 0; + +ERR: + _close (fd); + cps_rem_conn (thread, fd, conn); + CPS_REC_INC (thread, CPS_REC_FAIL); + return -1; +} + +void * +cps_s_thread (void *arg) +{ + int num = 0; + struct cps_thread *thread = (struct cps_thread *) arg; + struct epoll_event *event = thread->event; + + out ("[%d] initialize thread %ld server:%d core:%d epfd:%d\n", + thread->index, pthread_self (), thread->server_num, thread->core, + thread->epfd); + + if (cps_s_listen (thread)) + { + cps.run_state = CPS_ERROR; + return NULL; + } + + futex_wait (&cps.run_state, CPS_INIT); + + while (1) + { + + if (num > 0) + { + int sid = CPS_EV_SID (event->data.u64); + int fd = CPS_EV_FD (event->data.u64); + DBG ("epoll evnet{sid:%d fd:%d event:%x}\n", sid, fd, + event->events); + if (sid >= 0) + { + if (event->events & EPOLLIN) + { + (void) cps_s_accept (thread, fd); + } + if (event->events & EPOLLERR) + { + wrn ("Error event for server %d\n", fd); + } + } + else + { + if (fd >= CPS_MAX_FD) + { + err ("Error connection index %d\n", fd); + } + else + { + (void) cps_s_io (thread, fd, event->events); + } + } + + num--; + event++; + } + + if (num <= 0) + { + if (cps.run_state == CPS_CLOSING) + { + if (thread->server >= 0) + cps_s_close (thread); + if (thread->conn_num <= 0) + break; + } + else if (cps.run_state <= CPS_INIT) + { + break; + } + + event = thread->event; + num = _epoll_wait (thread->epfd, event, cps.evnum, CPS_EPWAIT_MS); + if (num < 0) + { + int e = errno; + if (e != EINTR && e != ETIMEDOUT) + CPS_CNT_INC_E (thread, CPS_CNT_EPOLL_ERR, e); + } + } + } + + __sync_fetch_and_sub (&cps.active_thread, 1); + return NULL; +} diff --git a/thirdparty/apps/testapp/ft.c b/thirdparty/apps/testapp/ft.c new file mode 100644 index 0000000..c3c6e1a --- /dev/null +++ b/thirdparty/apps/testapp/ft.c @@ -0,0 +1,74 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" + +int fd = -1; + +void * +thread (void *arg) +{ + struct epoll_event ev[32]; + int id = *(int *) arg; + int ep = _epoll_create (1); + int i, num; + + ev[0].events = EPOLLIN | EPOLLET; + ev[0].data.fd = id; + _epoll_ctl (ep, EPOLL_CTL_ADD, fd, &ev[0]); + + while (1) + { + num = _epoll_wait (ep, ev, 32, -1); + + (void) printf ("thread %d recv events %d\n", id, num); + + for (i = 0; i < num; ++i) + { + (void) + printf + ("thread %d recv events %d : index: %d id: %d event: 0x%x\n", id, + num, i, ev[i].data.fd, ev[i].events); + } + } + + return NULL; +} + +int +main (int argc, const char *argv[]) +{ + struct sockaddr_in addr = { 0 }; + int id1 = 1, id2 = 2, id3 = 3; + + fd = _socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd < 0) + return 1; + + addr.sin_family = AF_INET; + addr.sin_port = htons (10000); + + _bind (fd, (struct sockaddr *) &addr, sizeof (addr)); + _listen (fd, 10); + + pthread_t t1 = lb_thread (thread, (void *) &id1, "thread-1"); + pthread_t t2 = lb_thread (thread, (void *) &id2, "thread-2"); + pthread_t t3 = lb_thread (thread, (void *) &id3, "thread-3"); + + sleep (1000000); + + return 0; +} diff --git a/thirdparty/apps/testapp/ip6/ip6.c b/thirdparty/apps/testapp/ip6/ip6.c new file mode 100644 index 0000000..3b61801 --- /dev/null +++ b/thirdparty/apps/testapp/ip6/ip6.c @@ -0,0 +1,739 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" + +#define IP6_BUF_MAX (32 * 1024) + +#define IP6_DEF_SHOW 16 +#define IP6_MAX_SHOW 1024 + +#define IP6_DEF_LEN 100 +#define IP6_MAX_LEN (1024 * 1024) + +#define IP6_DEF_NUM -1 +#define IP6_MAX_NUM INT32_MAX + +#define IP6_DEF_DELAY 1 +#define IP6_MAX_DELAY 100 + +struct ip6_var +{ + struct sockaddr_in6 s_addr; + struct sockaddr_in6 c_addr; + int client_bind; + int is_client; + int is_udp; + int num; + int delay; + int len; + int verbose; + int show_len; + + int fd; + int sfd; + int epfd; + + char *buf; +}; + +struct ip6_var ip6 = { 0 }; + +#if 1 + +int +tcp_listen () +{ + int ret; + + ip6.sfd = _socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (!ip6.sfd, -1, "socket()=%s%d:%d%s\n", BR__, ip6.sfd, errno, CC); + + ret = _bind (ip6.sfd, (struct sockaddr *) &ip6.s_addr, sizeof (ip6.s_addr)); + ERR_RETURN (ret, ret, "bind(%d, %s)=%s%d:%d%s\n", ip6.sfd, + f_in6addr (&ip6.s_addr), BR__, ret, errno, CC); + + ret = _listen (ip6.sfd, 100); + ERR_RETURN (ret, ret, "listen(%d)=%s%d:%d%s\n", ip6.sfd, + BR__, ret, errno, CC); + + out ("TCP server listen on %s\n", f_in6addr (&ip6.s_addr)); + + return 0; +} + +void +tcp_server () +{ + if (tcp_listen ()) + return; + + while (1) + { + int recv_all = 0, sent_all = 0; + struct sockaddr_in6 addr; + socklen_t addr_len = sizeof (addr); + + ip6.fd = _accept (ip6.sfd, (struct sockaddr *) &addr, &addr_len); + if (ip6.fd < 0) + { + out ("accept(%d)=%s%d:%d%s\n", ip6.sfd, BR__, ip6.fd, errno, CC); + break; + } + + out ("incoming %d from %s\n", ip6.fd, f_in6addr (&addr)); + + while (1) + { + int recv_len, sent_len = 0, len, times = 0; + + recv_len = _recv (ip6.fd, ip6.buf, IP6_MAX_LEN, 0); + if (recv_len == 0) + break; + if (recv_len < 0) + { + if (errno == EINTR) + continue; + out ("recv(%d)=%s%d:%d%s\n", ip6.fd, BR__, recv_len, errno, CC); + break; + } + recv_all += recv_len; + if (ip6.verbose) + out ("recv: %d = %d\n", recv_all, recv_len); + + SENDING: + len = _send (ip6.fd, ip6.buf + sent_len, recv_len - sent_len, 0); + if (len == 0) + break; + if (len < 0) + { + if (errno == EINTR) + continue; + out ("send(%d, %d)=%s%d:%d%s\n", ip6.fd, recv_len - sent_len, + BR__, len, errno, CC); + break; + } + + times++; + sent_len += len; + sent_all += len; + if (ip6.verbose) + out ("sent: %d = %d + %d\n", sent_all, sent_len, len); + + if (sent_len < recv_len) + goto SENDING; + + if (times > 1) + out ("%d replied in %d times\n", sent_len, times); + else + out ("%d replied\n", sent_len); + } + + out ("closing %d --- input:%d output:%d\n", ip6.fd, recv_all, sent_all); + _close (ip6.fd); + ip6.fd = -1; + } + + err ("TCP server break\n"); +} + +int +tcp_connect () +{ + int ret; + + ip6.fd = _socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (!ip6.fd, -1, + "socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)=%s%d:%d%s\n", BR__, + ip6.fd, errno, CC); + + if (ip6.client_bind) + { + ret = + _bind (ip6.fd, (struct sockaddr *) &ip6.c_addr, sizeof (ip6.c_addr)); + ERR_RETURN (ret, ret, "bind(%d, %s)=%s%d:%d%s\n", ip6.fd, + f_in6addr (&ip6.c_addr), BR__, ret, errno, CC); + } + + ret = + _connect (ip6.fd, (struct sockaddr *) &ip6.s_addr, sizeof (ip6.s_addr)); + ERR_RETURN (ret, ret, "connect(%d, %s)=%s%d:%d%s\n", ip6.fd, + f_in6addr (&ip6.s_addr), BR__, ret, errno, CC); + + if (!ip6.client_bind) + { + socklen_t len = sizeof (ip6.c_addr); + ret = _getsockname (ip6.fd, (struct sockaddr *) &ip6.c_addr, &len); + ERR_RETURN (ret, ret, "getsockname()=%d:%d\n", ret, errno); + } + + out ("connected %d from %s to %s\n", ip6.fd, + f_in6addr (&ip6.c_addr), f_in6addr (&ip6.s_addr)); + + return 0; +} + +void +tcp_client () +{ + int i, count = 0, sent_all = 0, recv_all = 0, sent_num = 0, recv_num = 0; +const struct timespec delay = { tv_sec: ip6.delay /* / 1000 */ , tv_nsec: /*(ip6.delay % 1000) * 1000 * 100 */ 0 + }; + + if (tcp_connect ()) + return; + + for (i = 0; i < ip6.len; ++i) + ip6.buf[i] = (char) i; + + while (ip6.num != 0) + { + int len, recv_len = 0, sent_len = 0, recv_times = 0, send_times = 0; + + SENDING: + len = _send (ip6.fd, ip6.buf + sent_len, ip6.len - sent_len, 0); + if (len == 0) + break; + if (len < 0) + { + if (errno == EINTR) + continue; + out ("send(%d, %d)=%s%d:%d%s\n", ip6.fd, ip6.len - sent_len, BR__, + len, errno, CC); + break; + } + + sent_len += len; + sent_all += len; + if (ip6.verbose) + out ("sent: %d = %d + %d\n", sent_all, sent_len, len); + if (sent_len < ip6.len) + goto SENDING; + sent_num++; + + RECVING: + len = _recv (ip6.fd, ip6.buf + recv_len, IP6_MAX_LEN - recv_len, 0); + if (len == 0) + break; + if (len < 0) + { + if (errno == EINTR) + continue; + out ("recv(%d)=%s%d:%d%s\n", ip6.fd, BR__, len, errno, CC); + break; + } + recv_len += len; + recv_all += len; + if (ip6.verbose) + out ("recv: %d = %d + %d\n", recv_all, recv_len, len); + if (recv_len < sent_len) + goto RECVING; + recv_num++; + + out ("%d replied", sent_len); + if (send_times > 1) + out (" %d sending", send_times); + if (recv_times > 1) + out (" %d receiving", recv_times); + + for (i = 0; i < recv_len; ++i) + { + if (ip6.buf[i] != (char) i) + { + out (" data error [%d]:0x%02x != %02x", i, ip6.buf[i], + i & 0xFF); + for (; i < recv_len; ++i) + ip6.buf[i] = (char) i; + break; + } + } + + out ("\n"); + + if (ip6.num > 0 && sent_num >= ip6.num) + break; + + if (ip6.delay) + { + (void) nanosleep (&delay, NULL); + } + } + + out ("closing %d\n", ip6.fd); + + if (sent_num) + { + out ("--- %s -> %s TCP ping statistics ---\n", + f_in6addr (&ip6.c_addr), f_in6addr (&ip6.s_addr)); + out + ("%d output, %d input, %d packets transmitted, %d received, %d%% packet loss\n", + sent_all, recv_all, sent_num, recv_num, + (sent_num - recv_num) * 100 / sent_num); + } +} + +#endif +#if 1 + +void +udp_recv_show (char *buf, int len) +{ + int i, dot = 0; + char co[256]; + + co_init (co, sizeof (co)); + + if (len > ip6.show_len) + { + len = ip6.show_len; + dot = 1; + } + + if (len > 16) + co_app_ch (co, '\n'); + + for (i = 0; i < len; ++i) + { + char ch; + + co_ch_if (i && (i % 32) == 0, co, '\n'); + co_ch_if ((i % 16) == 0, co, ' '); + co_ch_if ((i % 4) == 0, co, ' '); + + ch = buf[i] >> 4; + ch += (ch > 9 ? 'A' : '0'); + co_app_ch (co, ch); + ch = buf[i] & 0xF; + ch += (ch > 9 ? 'A' : '0'); + co_app_ch (co, ch); + } + + if (dot) + co_append (co, 6, " ...\n"); + else + co_app_ch (co, '\n'); + + co_flush (co); +} + +void +udp_server () +{ + int ret; + uint32_t count = 0; + + ip6.fd = _socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (ip6.fd < 0) + { + err ("socket()=%d:%d\n", ip6.fd, errno); + return; + } + + ret = _bind (ip6.fd, (struct sockaddr *) &ip6.s_addr, sizeof (ip6.s_addr)); + if (ret) + { + err ("bind(%d, %s)=%d:%d\n", ip6.fd, f_in6addr (&ip6.s_addr), ret, + errno); + return; + } + + out ("UDP server bound %s\n", f_in6addr (&ip6.s_addr)); + + while (1) + { + struct sockaddr_in6 addr; + socklen_t addrlen = sizeof (addr); + int recv_len, sent_len; + + recv_len = + _recvfrom (ip6.fd, ip6.buf, IP6_BUF_MAX, 0, (struct sockaddr *) &addr, + &addrlen); + + if (recv_len == 0) + { + out ("recvfrom()=0 --> exit\n"); + break; + } + if (recv_len < 0) + { + if (errno != EINTR) + out ("recvfrom()=%s%d:%d%s\n", FR__, recv_len, errno, CC); + continue; + } + + SENDING: + sent_len = + _sendto (ip6.fd, ip6.buf, recv_len, 0, (struct sockaddr *) &addr, + sizeof (addr)); + if (sent_len == 0) + break; + if (sent_len < 0) + { + if (errno == EINTR) + goto SENDING; + out ("sendto(%d, %s)=%s%d:%d%s\n", recv_len, f_in6addr (&addr), + FR__, sent_len, errno, CC); + continue; + } + + out ("%d received from %s", recv_len, f_in6addr (&addr)); + if (recv_len == sent_len) + out ("\n"); + else + out (", sent %s%d%s\n", FR__, sent_len, CC); + } + + err ("UDP server break\n"); +} + +void +udp_client () +{ + int ret, i, sent_num = 0, recv_num = 0; +const struct timespec delay = { tv_sec: ip6.delay /* / 1000 */ , tv_nsec: /*(ip6.delay % 1000) * 1000 * 100 */ 0 + }; + + ip6.fd = _socket (PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (ip6.fd < 0) + { + err ("socket()=%d:%d\n", ip6.fd, errno); + return; + } + + if (ip6.client_bind) + { + ret = + _bind (ip6.fd, (struct sockaddr *) &ip6.c_addr, + sizeof (struct sockaddr_in6)); + if (ret) + { + err ("bind(%d, %s)=%d:%d\n", ip6.fd, f_in6addr (&ip6.c_addr), ret, + errno); + return; + } + out ("UDP client bind %s\n", f_in6addr (&ip6.c_addr)); + } + + out ("UDP client ping %s size:%d\n", f_in6addr (&ip6.s_addr), ip6.len); + + for (i = 0; i < ip6.len; ++i) + ip6.buf[i] = (char) i; + + while (1) + { + struct sockaddr_in6 addr; + socklen_t addrlen = sizeof (addr); + int sent_len, recv_len; + + sent_len = + _sendto (ip6.fd, ip6.buf, ip6.len, 0, + (const struct sockaddr *) &ip6.s_addr, addrlen); + if (sent_len == 0) + break; + if (sent_len < 0) + { + if (errno != EINTR) + out ("sendto(%d, %s)=%s%d:%d%s\n", ip6.len, + f_in6addr (&ip6.s_addr), FR__, sent_len, errno, CC); + continue; + } + sent_num++; + if (sent_len != ip6.len) + { + out ("sendto(%d, %s)=%s%d:%d%s\n", ip6.len, f_in6addr (&ip6.s_addr), + FR__, sent_len, errno, CC); + } + RECVING: + recv_len = + recvfrom (ip6.fd, ip6.buf, IP6_MAX_LEN, 0, (struct sockaddr *) &addr, + &addrlen); + if (recv_len == 0) + break; + if (recv_len < 0) + { + if (errno == EINTR) + goto RECVING; + out ("recvfrom()=%s%d:%d%s\n", FR__, recv_len, errno, CC); + continue; + } + + recv_num++; + out ("%d bytes from %s", recv_len, f_in6addr (&addr)); + + if (sent_len != recv_len) + out (" recv_len != sent_len %d", sent_len); + + if (addr.sin6_port != ip6.s_addr.sin6_port || + addr.sin6_addr.s6_addr32[0] != ip6.s_addr.sin6_addr.s6_addr32[0] || + addr.sin6_addr.s6_addr32[1] != ip6.s_addr.sin6_addr.s6_addr32[1] || + addr.sin6_addr.s6_addr32[2] != ip6.s_addr.sin6_addr.s6_addr32[2] || + addr.sin6_addr.s6_addr32[3] != ip6.s_addr.sin6_addr.s6_addr32[3]) + { + out (" address error"); + } + + for (i = 0; i < recv_len; ++i) + { + if (ip6.buf[i] != (char) i) + { + out ("data error [%d]:0x%02x != %02x", i, ip6.buf[i], i & 0xFF); + for (; i < ip6.len; ++i) + ip6.buf[i] = (char) i; + break; + } + } + + out ("\n"); + + if (ip6.num > 0 && sent_num >= ip6.num) + break; + + if (ip6.delay) + { + (void) nanosleep (&delay, NULL); + } + } + + if (sent_num) + { + out ("--- UDP ping %s statistics ---\n", f_in6addr (&ip6.s_addr)); + out ("%d packets transmitted, %d received, %d%% packet loss\n", + sent_num, recv_num, (sent_num - recv_num) * 100 / sent_num); + } +} + +#endif + +int +ip6_start () +{ + ip6.fd = -1; + ip6.sfd = -1; + ip6.epfd = -1; + + if (ip6.num == 0) + ip6.num = IP6_DEF_NUM; + + ip6.s_addr.sin6_family = AF_INET6; + ip6.c_addr.sin6_family = AF_INET6; + + ip6.buf = malloc (IP6_MAX_LEN); + ERR_RETURN (!ip6.buf, -1, "Out of memory"); + + return 0; +} + +void +ip6_exit () +{ + FD_CLOSE (ip6.fd); + FD_CLOSE (ip6.sfd); + FD_CLOSE (ip6.epfd); + + BUF_FREE (ip6.buf); +} + +#define IP6_OPTIONS "b:cun:d:l:o:" DBGOPT "vh" + +static const struct option ip6_options[] = { + {"bind", 1, 0, 'b'}, + {"client", 0, 0, 'c'}, + {"udp", 0, 0, 'u'}, + {"number", 1, 0, 'n'}, + {"delay", 1, 0, 'd'}, + {"len", 1, 0, 'l'}, + {"output", 1, 0, 'o'}, + DBGOPT_LONG {"verbose", 0, 0, 'v'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} +}; + +void +ip6_usage (const char *name) +{ + out ("USAGE: %s [OPTIONS] SERVER-ADDRESS # %s version\n", name, + VERSION_NAME); + out (" Options:\n"); + out (" -b, --bind IP.PORT bind address\n"); + out (" -c, --client client mode\n"); + out (" -u, --udp udp mode\n"); + out (" -n, --number # C packet number(default:LOOP)\n"); + out + (" -d, --delay # C seconds wait send next packet(default:1, 0: no delay)\n"); + out (" -l, --length # C data length(default:%u)\n", IP6_DEF_LEN); + out + (" -o, --output # show received data(default:%u)\n", + IP6_DEF_SHOW); +#ifdef DEBUG + out (" -D, --debug show debug information\n"); +#endif + out (" -v, --verbose show thread statistics\n"); + out (" -h, --help help\n"); +} + +int +ip6_args (int argc, char *argv[]) +{ + int ret, opt, index; + + ip6.delay = IP6_DEF_DELAY; + ip6.len = IP6_DEF_LEN; + + while (EOF != + (opt = getopt_long (argc, argv, IP6_OPTIONS, ip6_options, &index))) + { + const char *end; + + switch (opt) + { + case 'b': + ret = p_addr6 (optarg, &ip6.c_addr); + ERR_RETURN (ret, -1, "Invalid client set '%s'\n", optarg); + ip6.client_bind = 1; + break; + case 'c': + ip6.is_client = 1; + break; + + case 'u': + ip6.is_udp = 1; + break; + + case 'n': + ip6.num = p_uint (optarg, IP6_MAX_NUM, &end); + ERR_RETURN (!end || *end, -1, "Invalid number '%s'\n", optarg); + break; + case 'd': + ip6.delay = (int) p_int (optarg, IP6_MAX_DELAY, &end); + ERR_RETURN (!end || *end, -1, "Invalid delay '%s'\n", optarg); + break; + + case 'l': + ip6.len = (int) p_int (optarg, IP6_MAX_LEN, &end); + ERR_RETURN (!end || *end, -1, "Invalid query '%s'\n", optarg); + break; + + case 'o': + ip6.show_len = (int) p_int (optarg, IP6_MAX_SHOW, &end); + ERR_RETURN (!end || *end, -1, "Invalid reply '%s'\n", optarg); + break; + + case 'v': + ip6.verbose = 1; + break; + +#ifdef DEBUG + case 'D': + enable_debug = 1; + break; +#endif + case 'h': + ip6_usage (argv[0]); + exit (0); + case '?': + err ("Invalid arguments\n"); + return -1; + default: + err ("Unknown option '%c'.\n", opt); + return -1; + } + } + + if (optind == argc - 1) + { + ERR_RETURN (p_addr6 (argv[optind], &ip6.s_addr), -1, + "Invalid server address '%s'\n", argv[optind]); + } + else if (optind < argc) + { + while (optind < argc) + err ("Unknown argument '%s'\n", argv[optind++]); + return -1; + } + else + { + err ("NO server address\n"); + return -1; + } + + return 0; +} + +void +ip6_break (int s) +{ + DBG (" SIGNALED %d\n", s); + out ("\n"); + + ip6_exit (); + exit (0); +} + +void +ip6_sigpipe (int s) +{ + DBG ("SIGPIPE\n"); +} + +int +ip6_init () +{ + struct sigaction s = { 0 }; + + (void) sigemptyset (&s.sa_mask); + + s.sa_flags = SA_NODEFER; + s.sa_handler = (void *) ip6_break; + (void) sigaction (SIGINT, &s, NULL); + (void) sigaction (SIGQUIT, &s, NULL); + + s.sa_handler = ip6_sigpipe; + (void) sigaction (SIGPIPE, &s, NULL); + +// lb_sigsegv_setup(); + + return 0; +} + +int +main (int argc, char *argv[]) +{ + if (ip6_init ()) + return 1; + + if (ip6_args (argc, argv)) + return 1; + + ip6_start (); + + if (ip6.is_client) + { + if (ip6.is_udp) + udp_client (); + else + tcp_client (); + } + else + { + if (ip6.is_udp) + udp_server (); + else + tcp_server (); + } + + ip6_exit (); + return 0; +} diff --git a/thirdparty/apps/testapp/lb/api.h b/thirdparty/apps/testapp/lb/api.h new file mode 100644 index 0000000..f930227 --- /dev/null +++ b/thirdparty/apps/testapp/lb/api.h @@ -0,0 +1,169 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef _API_H_ +#define _API_H_ + +#if defined(NSOCKET) +#define VERSION_NAME "NSTACK" +#define _socket nstack_socket +#define _fcntl nstack_fcntl +#define _bind nstack_bind +#define _listen nstack_listen +#define _accept nstack_accept +#define _accept4 nstack_accept4 +#define _connect nstack_connect +#define _close nstack_close +#define _shutdown nstack_shutdown +#define _recv nstack_recv +#define _send nstack_send +#define _getsockname nstack_getsockname +#define _getpeername nstack_getpeername +#define _getsockopt nstack_getsockopt +#define _setsockopt nstack_setsockopt +#define _recvfrom nstack_recvfrom +#define _sendto nstack_sendto +#define _read nstack_read +#define _write nstack_write +#define _epoll_create nstack_epoll_create +#define _epoll_ctl nstack_epoll_ctl +#define _epoll_wait nstack_epoll_wait +#elif defined(LWIP) +#define VERSION_NAME "LWIP" +#define _socket lwip_socket +#define _fcntl lwip_fcntl +#define _bind lwip_bind +#define _listen lwip_listen +#define _accept lwip_accept +#define _accept4 lwip_accept4 +#define _connect lwip_connect +#define _close lwip_close +#define _shutdown lwip_shutdown +#define _recv lwip_recv +#define _send lwip_send +#define _getsockname lwip_getsockname +#define _getpeername lwip_getpeername +#define _getsockopt lwip_getsockopt +#define _setsockopt lwip_setsockopt +#define _recvfrom lwip_recvfrom +#define _sendto lwip_sendto +#define _read lwip_read +#define _write lwip_write +#define _epoll_create lwip_epoll_create +#define _epoll_ctl lwip_epoll_ctl +#define _epoll_wait lwip_epoll_wait +#else +#define VERSION_NAME "POSIX" +#define _socket socket +#define _fcntl fcntl +#define _bind bind +#define _listen listen +#define _accept accept +#define _accept4 accept4 +#define _connect connect +#define _close close +#define _shutdown shutdown +#define _recv recv +#define _send send +#define _getsockname getsockname +#define _getpeername getpeername +#define _getsockopt getsockopt +#define _setsockopt setsockopt +#define _recvfrom recvfrom +#define _sendto sendto +#define _read read +#define _write write +#define _epoll_create epoll_create +#define _epoll_ctl epoll_ctl +#define _epoll_wait epoll_wait +#endif + +#endif /* #ifndef _API_H_ */ + +#ifndef SOCKET_WARP_LB_H_ +#define SOCKET_WARP_LB_H_ + +inline static int +set_nonblock_v (int fd, int nonblock) +{ + int fl = _fcntl (fd, F_GETFL, 0); + + if (fl < 0) + return fl; + + if (nonblock) + { + if (fl & O_NONBLOCK) + return 0; + fl |= O_NONBLOCK; + } + else + { + if (0 == (fl & O_NONBLOCK)) + return 0; + fl &= ~(O_NONBLOCK); + } + + return _fcntl (fd, F_SETFL, fl | O_NONBLOCK); +} + +inline static int +set_nonblock (int fd) +{ + int fl = _fcntl (fd, F_GETFL, 0); + if (fl < 0) + return fl; + return _fcntl (fd, F_SETFL, fl | O_NONBLOCK); +} + +inline static int +set_nodelay (int fd, int nodelay) +{ + return _setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (void *) &nodelay, + sizeof (nodelay)); +} + +inline static int +set_rcvtimeo (int fd, int us) +{ + struct timeval timeout = {.tv_sec = us / USOFS,.tv_usec = us % USOFS }; + return _setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof (timeout)); +} + +inline static int +set_reuseaddr (int fd, int reuseaddr) +{ + return _setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (char *) &reuseaddr, + sizeof (reuseaddr)); +} + +inline static int +set_reuseport (int fd, int reuseport) +{ + return _setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, (char *) &reuseport, + sizeof (reuseport)); +} + +inline static int +set_sndtimeo (int fd, int us) +{ + struct timeval timeout = {.tv_sec = us / USOFS,.tv_usec = us % USOFS }; + return _setsockopt (fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, + sizeof (timeout)); +} + +#endif /* #ifndef SOCKET_WARP_LB_H_ */ diff --git a/thirdparty/apps/testapp/lb/lb.c b/thirdparty/apps/testapp/lb/lb.c new file mode 100644 index 0000000..058ddce --- /dev/null +++ b/thirdparty/apps/testapp/lb/lb.c @@ -0,0 +1,1290 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" + +int enable_debug = 1; + +#ifdef COLOR_LB_H_ + +const static struct lb_color lb_color_list[2] = { + [LB_DEF_COLOR] = { + .clear = "\033[0m", + .high = "\033[1m", + .uline = "\033[4m", + .flash = "\033[5m", + .rev = "\033[7m", + + .fblack = "\033[30m", + .fr__ = "\033[31m", + .f_g_ = "\033[32m", + .f__b = "\033[34m", + .frg_ = "\033[33m", + .fr_b = "\033[35m", + .f_gb = "\033[36m", + .fwhite = "\033[37m", + + .bblack = "\033[40m", + .br__ = "\033[41m", + .b_g_ = "\033[42m", + .b__b = "\033[44m", + .brg_ = "\033[43m", + .br_b = "\033[45m", + .b_gb = "\033[46m", + .bwhite = "\033[47m", + }, + [LB_NO_COLOR] = { + .clear = "", + .high = "", + .uline = "", + .flash = "", + .rev = "", + + .fblack = "", + .fr__ = "", + .f_g_ = "", + .f__b = "", + .frg_ = "", + .fr_b = "", + .f_gb = "", + .fwhite = "", + + .bblack = "", + .br__ = "", + .b_g_ = "", + .b__b = "", + .brg_ = "", + .br_b = "", + .b_gb = "", + .bwhite = "", + }, +}; + +const struct lb_color *lb_color = &lb_color_list[0]; +int lb_color_index = 0; + +void +lb_set_color (int index) +{ + lb_color_index = index; + lb_color = &lb_color_list[index]; +} + +#endif + +#ifdef FORMAT_LB_H_ + +static __thread char s_fmt_buf[64][64]; +static __thread uint32_t s_fmt_id = 0; + +#if 0 +const char * +f_in6 (const struct in6_addr *ip6) +{ + char *buf = s_fmt_buf[(s_fmt_id++) & 63]; + char *p = buf; + int i; + + for (i = 0; i < 8; ++i) + { + uint16_t c, v = htons (ip6->s6_addr16[i]); + + c = v >> 12; + if (c > 9) + *p++ = 'A' + c - 10; + else if (c > 0) + *p++ = '0' + c; + + c = (v >> 8) & 0xF; + if (c > 9) + *p++ = 'A' + c - 10; + else if (v > 0x00FF) + *p++ = '0' + c; + + c = (v >> 4) & 0xF; + if (c > 9) + *p++ = 'A' + c - 10; + else if (v > 0x000F) + *p++ = '0' + c; + + c = v & 0xF; + if (c > 9) + *p++ = 'A' + c - 10; + else + *p++ = '0' + c; + + *p++ = ':'; + } + + p--; + *p = 0; + return buf; +} +#endif + +static int +f_in6_f (char *buf, int len, uint16_t val) +{ + uint16_t v; + int num = 0; + + if (val > 0xFFF) + { + if (num >= len) + return -1; + v = val >> 12; + if (v < 10) + buf[num++] = v + '0'; + else + buf[num++] = v + 'a' - 10; + } + if (val > 0xff) + { + if (num >= len) + return -1; + v = (val >> 8) & 0xF; + if (v < 10) + buf[num++] = v + '0'; + else + buf[num++] = v + 'a' - 10; + } + if (val > 0xf) + { + if (num >= len) + return -1; + v = (val >> 4) & 0xF; + if (v < 10) + buf[num++] = v + '0'; + else + buf[num++] = v + 'a' - 10; + } + + if (num >= len) + return -1; + v = val & 0xF; + if (v < 10) + buf[num++] = v + '0'; + else + buf[num++] = v + 'a' - 10; + + return num; +} + +static void +f_in6_z (const struct in6_addr *addr, int *pos, int *num) +{ + int zero_pos = 8, zero_num = 0; + int try_pos = -1, try_num = 0; + int i; + + for (i = 0; i < 8; ++i) + { + if (addr->s6_addr16[i] == 0) + { + if (try_num) + { + try_num++; + } + else + { + try_pos = i; + try_num = 1; + } + } + else if (try_num) + { + if (try_num > zero_num) + { + zero_pos = try_pos; + zero_num = try_num; + } + try_num = 0; + } + } + + if (try_num && try_num > zero_num) + { + *pos = try_pos; + *num = try_num; + } + else + { + *pos = zero_pos; + *num = zero_num; + } +} + +int +f_in6_s (char *buf, int size, const struct in6_addr *addr) +{ + int ret, i, num = 0; + int zero_pos, zero_num; + + f_in6_z (addr, &zero_pos, &zero_num); + + if (zero_pos == 0) + { + if (num >= size) + return -1; + buf[num++] = ':'; + } + else + { + for (i = 0; i < zero_pos; ++i) + { + ret = f_in6_f (&buf[num], size - num, htons (addr->s6_addr16[i])); + if (ret < 0) + return -1; + num += ret; + if (num >= size) + return -1; + buf[num++] = ':'; + } + if (zero_pos == 8) + { + buf[--num] = 0; + return num; + } + } + + if (zero_pos + zero_num == 8) + { + if (num >= size) + return -1; + buf[num++] = ':'; + } + else + { + for (i = zero_pos + zero_num; i < 8; ++i) + { + if (num >= size) + return -1; + buf[num++] = ':'; + ret = f_in6_f (&buf[num], size - num, htons (addr->s6_addr16[i])); + if (ret < 0) + return -1; + num += ret; + } + } + + if (num >= size) + return -1; + buf[num] = 0; + + return num; +} + +const char * +f_in6_r (const struct in6_addr *addr, char *buf, int buflen) +{ + if (f_in6_s (buf, buflen, addr) <= 0) + return NULL; + return buf; +} + +const char * +f_in6 (const struct in6_addr *addr) +{ + char *buf = s_fmt_buf[(s_fmt_id++) & 63]; + return f_in6_r (addr, buf, sizeof (s_fmt_buf[0])); +} + +const char * +f_inaddr (const struct sockaddr_in *addr) +{ + char *buf = s_fmt_buf[(s_fmt_id++) & 63]; + char *p = buf; + const uint8_t *ip = (uint8_t *) & addr->sin_addr.s_addr; + const uint16_t pt = ntohs (addr->sin_port); + +#define IPP(v) do { \ + if (v > 99) { *p++ = '0' + v / 100 % 10; } \ + if (v > 9) { *p++ = '0' + v / 10 % 10; } \ + *p++ = '0' + v % 10; \ +} while (0) + +#define PTP(v) do { \ + if (v > 9999) { *p++ = '0' + v / 10000 % 10; } \ + if (v > 999) { *p++ = '0' + v / 1000 % 10; } \ + IPP(v); \ +} while (0) + + IPP (ip[0]); + *p++ = '.'; + IPP (ip[1]); + *p++ = '.'; + IPP (ip[2]); + *p++ = '.'; + IPP (ip[3]); + *p++ = ':'; + PTP (pt); + *p = 0; + + return buf; +} + +const char * +f_in6addr (const struct sockaddr_in6 *addr) +{ + char *p, *buf = s_fmt_buf[(s_fmt_id++) & 63]; + const uint16_t pt = htons (addr->sin6_port); + int len = f_in6_s (buf, sizeof (s_fmt_buf[0]), &addr->sin6_addr); + + if (len < 0) + return ""; + + p = buf + len; + *p++ = '.'; + PTP (pt); + *p = 0; + + return buf; +} + +inline static int +r_uint_use (uint64_t val) +{ + const static uint64_t LB_VAL[21] = { + 0, + 9, + 99, + 999, + 9999, + 99999, + 999999, + 9999999, + 99999999, + 999999999, + 9999999999, + 99999999999, + 999999999999, + 9999999999999, + 99999999999999, + 999999999999999, + 9999999999999999, + 99999999999999999, + 999999999999999999, + 9999999999999999999u, + 18446744073709551615u, + /* --%%%***+++###^^^@@@ */ + }; + + int a = 1, b = 20; + + do + { + int i = (a + b) / 2; + if (val > LB_VAL[i]) + a = i + 1; + else + b = i; + } + while (a != b); + + return a; +} + +inline static int +r_uint_wide (uint64_t val) +{ + int wide = r_uint_use (val); + + wide = wide + (wide - 1) / 3; + + return wide; +} + +inline static void +r_uint_fmt (char *buf, uint64_t val, int wide) +{ + char *p = buf + wide; + + *p-- = 0; + + while (1) + { + *p-- = ('0' + val % 10); + if ((val /= 10) == 0) + break; + + *p-- = ('0' + val % 10); + if ((val /= 10) == 0) + break; + + *p-- = ('0' + val % 10); + if ((val /= 10) == 0) + break; + + *p-- = ','; + } + + while (p >= buf) + *p-- = ' '; +} + +int +r_uint (char *buf, uint64_t val, int wide) +{ + const int size = r_uint_wide (val); + + if (size > wide) + wide = size; + + r_uint_fmt (buf, val, wide); + + return wide; +} + +inline int +s_uint (char *buf, uint64_t val) +{ + char *p = buf; + +#define F_NUM(n) if (val >= n) { *p++ = '0' + val / n % 10; } +#define C_NUM(n) if (val >= n) { *p++ = '0' + val / n % 10; *p++ = ','; } + + if (val >= 10000000ul) + { + if (val >= 1000000000000ul) + { + F_NUM (10000000000000000000ul); + C_NUM (1000000000000000000ul); + F_NUM (100000000000000000ul); + F_NUM (10000000000000000ul); + C_NUM (1000000000000000ul); + F_NUM (100000000000000ul); + F_NUM (10000000000000ul); + C_NUM (1000000000000ul); + } + F_NUM (100000000000ul); + F_NUM (10000000000ul); + C_NUM (1000000000ul); + F_NUM (100000000ul); + F_NUM (10000000ul); + } + C_NUM (1000000ul); + F_NUM (100000ul); + F_NUM (10000ul); + C_NUM (1000ul); + F_NUM (100ul); + F_NUM (10ul); + *p++ = '0' + val % 10; + *p = 0; + +#undef F_NUM +#undef C_NUM + return p - buf; +} + +const char * +f_uint (uint64_t val) +{ + char *buf = s_fmt_buf[(s_fmt_id++) & 63]; + (void) s_uint (buf, val); + return buf; +} + +#endif +#ifdef PARSE_LB_H_ + +#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9') +#define IS_hex(c) ((c) >= 'a' && (c) <= 'f') +#define IS_HEX(c) ((c) >= 'A' && (c) <= 'F') + +uint64_t +p_hex (const char *arg, const char **end) +{ + int i; + uint64_t val = 0; + + while (*arg) + { + if (IS_DIGIT (*arg)) + val = (val << 4) | (uint64_t) (*arg++ - '0'); + else if (IS_hex (*arg)) + val = (val << 4) | (uint64_t) (*arg++ - 'a' + 0xa); + else if (IS_HEX (*arg)) + val = (val << 4) | (uint64_t) (*arg++ - 'A' + 0xA); + else + break; + if (val > 0x0FFFffffFFFFffff) + break; + } + + if (end) + *end = arg; + return val; +} + +#define P_UINT_RET(ret, out) ((*end = (out)), (ret)) + +uint64_t +p_uint (const char *arg, uint64_t max, const char **end) +{ + int i; + uint64_t v; + const char *out; + + if (!end) + end = &out; + + if (!arg) + goto P_UINT_ERR; + + if (*arg == '0') + { + if (IS_DIGIT (arg[1])) + goto P_UINT_ERR; + return P_UINT_RET (0, arg + 1); + } + + if (!IS_DIGIT (*arg)) + return P_UINT_RET (0, NULL); + + v = *arg++ - '0'; + if (v > max) + goto P_UINT_ERR; + + for (i = 2; i <= 19; ++i) + { + if (!IS_DIGIT (*arg)) + return P_UINT_RET (v, arg); + v = v * 10 + (*arg++ - '0'); + if (v > max) + goto P_UINT_ERR; + } + + if (IS_DIGIT (*arg)) + { + uint64_t n; + if (v > (UINT64_MAX / 10)) + goto P_UINT_ERR; + n = v * 10; + if (UINT64_MAX - n > (*arg - '0')) + goto P_UINT_ERR; + v = n + (*arg - '0'); + if (v > max) + goto P_UINT_ERR; + arg++; + if (IS_DIGIT (*arg)) + goto P_UINT_ERR; + } + + return P_UINT_RET (v, arg); + +P_UINT_ERR: + return P_UINT_RET (UINT64_MAX, NULL); +} + +inline uint32_t +p_ip (const char **arg) +{ + uint32_t b1, b2, b3, b4; + const char *p = *arg; + + b1 = (uint32_t) p_uint (p, 255, &p); + if (!p || *p++ != '.') + goto P_IP_ERR; + b2 = (uint32_t) p_uint (p, 255, &p); + if (!p || *p++ != '.') + goto P_IP_ERR; + b3 = (uint32_t) p_uint (p, 255, &p); + if (!p || *p++ != '.') + goto P_IP_ERR; + b4 = (uint32_t) p_uint (p, 255, &p); + if (!p) + goto P_IP_ERR; + + *arg = p; + return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + +P_IP_ERR: + *arg = NULL; + return 0; +} + +int +p_addr (const char *arg, struct sockaddr_in *addr) +{ + uint16_t port; + + if (*arg != ':') + { + uint32_t ip = p_ip (&arg); + if (!arg) + return -1; + addr->sin_addr.s_addr = htonl (ip); + if (*arg == 0) + return 0; + if (*arg != ':') + return -1; + arg++; + } + + port = (uint16_t) p_uint (arg, 0xffff, &arg); + if (!arg || *arg != 0) + return -1; + addr->sin_port = htons (port); + + return 0; +} + +const char * +p_addr_set (const char *arg, struct inaddrs *addr, uint32_t flag) +{ + addr->ip = p_ip (&arg); + if (!arg) + return NULL; + + if (!(flag & PA_NO_TO_IP) && *arg == '-') + { + uint8_t to; + to = (uint8_t) p_uint (arg + 1, 255, &arg); + if (!arg) + return NULL; + addr->ip_num = to - (uint8_t) addr->ip; + if (addr->ip_num >= 0) + ++addr->ip_num; + else if (flag & PA_MAY_INV_IP) + --addr->ip_num; + else + return NULL; + } + else if (!(flag & PA_NO_NUM_IP) && *arg == '+') + { + addr->ip_num = (int) p_uint (arg + 1, 0x7FFFffff, &arg); + if (!arg || addr->ip_num == 0 || 0xFFFFFFFF - addr->ip < addr->ip_num) + return NULL; + } + else + { + addr->ip_num = 1; + } + + if ((flag & PA_DEF_PORT) == PA_NO_PORT) + return arg; + + if (*arg != ':') + { + if ((flag & PA_DEF_PORT) == PA_MUST_PORT) + return NULL; + if ((flag & PA_DEF_PORT) == PA_DEF_PORT) + { + addr->port = (uint16_t) (flag); + addr->port_num = 1; + } + else + { + addr->port = 0; + addr->port_num = 0; + } + return arg; + } + + addr->port = (uint16_t) p_uint (arg + 1, 0xffff, &arg); + if (!arg) + return NULL; + + if (!(flag & PA_NO_TO_PORT) && *arg == '-') + { + uint16_t to = (uint16_t) p_uint (arg + 1, 0xffff, &arg); + if (!arg || to < addr->port) + return NULL; + addr->port_num = to - addr->port + 1; + } + else if (!(flag & PA_NO_NUM_PORT) && *arg == '+') + { + addr->port_num = (uint16_t) p_uint (arg + 1, 0xffff, &arg); + if (!arg || addr->port_num == 0 || 0xFFFF - addr->port < addr->port_num) + return NULL; + } + else + { + addr->port_num = 1; + } + + if (flag & PA_MULTI_ONE) + { + if (addr->ip_num > 1 && addr->port_num > 1) + return NULL; + } + + return arg; +} + +inline static const char * +pal_trim (const char *arg, uint32_t flag) +{ + if (flag & PAL_NO_SPACE) + return arg; + + if (flag & PAL_WITH_NL) + { + while (*arg == ' ' || *arg == '\t' || *arg == '\r' || *arg == '\n') + arg++; + } + else + { + while (*arg == ' ' || *arg == '\t') + arg++; + } + + return arg; +} + +int +p_addr_list (const char *arg, struct inaddrs *list, int num, uint32_t flag, + const char **end) +{ + int count = 0; + + while (count < num) + { + arg = pal_trim (arg, flag); + arg = p_addr_set (arg, list, flag); + count++; + if (!arg) + return -count; + arg = pal_trim (arg, flag); + if (*arg != ',') + break; + arg++; + list++; + } + + if (end) + *end = arg; + else if (*arg) + return -count; + + return count; +} + +int +addr_total (const struct inaddrs *list, int num, uint32_t mode) +{ + int total = 0; + + mode &= PAL_CROSS_MASK; + + for (--num; num >= 0; --num) + { + if (mode == PAL_IP_X_PORT || mode == PAL_PORT_X_IP) + { + total += list[num].ip_num * list[num].port_num; + } + else if (mode == PAL_INC_BOTH) + { + int iip = 0; + uint16_t iport = 0; + do + { + total++; + if (++iip == list[num].ip_num) + iip = 0; + if (++iport == list[num].port_num) + iport = 0; + } + while (iip != 0 && iport != 0); + } + else + return -1; + } + + return total; +} + +int +addr_layout (const struct inaddrs *list, int list_num, + struct sockaddr_in *addr, int addr_num, uint32_t mode) +{ + uint16_t ipt; + int i, iip, count = 0; + + mode &= PAL_CROSS_MASK; + + if (mode == PAL_IP_X_PORT) + { + for (i = 0; i < list_num; ++i) + { + if (count + list[i].ip_num * list[i].port_num > addr_num) + return -1; + for (iip = 0; iip < list[i].ip_num; ++iip) + { + for (ipt = 0; ipt < list[i].port_num; ++ipt) + { + addr[count].sin_family = AF_INET; + addr[count].sin_addr.s_addr = htonl (list[i].ip + iip); + addr[count].sin_port = htons (list[i].port + ipt); + count++; + } + } + } + } + else if (mode == PAL_PORT_X_IP) + { + for (i = 0; i < list_num; ++i) + { + if (count + list[i].ip_num * list[i].port_num > addr_num) + return -1; + for (ipt = 0; ipt < list[i].port_num; ++ipt) + { + for (iip = 0; iip < list[i].ip_num; ++iip) + { + addr[count].sin_family = AF_INET; + addr[count].sin_addr.s_addr = htonl (list[i].ip + iip); + addr[count].sin_port = htons (list[i].port + ipt); + count++; + } + } + } + } + else if (mode == PAL_INC_BOTH) + { + for (i = 0; i < list_num; ++i) + { + do + { + if (count >= addr_num) + return -1; + addr[count].sin_family = AF_INET; + addr[count].sin_addr.s_addr = htonl (list[i].ip + iip); + addr[count].sin_port = htons (list[i].port + ipt); + count++; + if (++iip >= list[i].ip_num) + iip = 0; + if (++ipt >= list[i].port_num) + ipt = 0; + } + while (iip != 0 && ipt != 0); + } + } + else + { + return -1; + } + + return count; +} + +inline static const char * +p_ip6_se (const char *p, uint16_t * val) +{ + int i; + uint32_t v = 0; + + for (i = 0; i < 4; ++i) + { + if (p[i] >= '0' && p[i] <= '9') + v = (v << 4) + p[i] - '0'; + else if (p[i] >= 'a' && p[i] <= 'f') + v = (v << 4) + p[i] - 'a' + 10; + else if (p[i] >= 'A' && p[i] <= 'F') + v = (v << 4) + p[i] - 'A' + 10; + else + break; + } + + if (i == 0) + return NULL; + + *val = htons (v); + return p + i; +} + +const char * +p_ip6 (const char *pos, struct in6_addr *ip) +{ + int zero = -1, num = 0; + const char *last; + + if (*pos == ':') + { + pos++; + if (*pos != ':') + return NULL; + pos++; + zero = 0; + } + + last = pos; + + while (*pos) + { + pos = p_ip6_se (pos, &ip->s6_addr16[num]); + if (!pos) + { + if (zero == num) + break; + return NULL; + } + + num++; + if (num == 8) + break; + + if (*pos == ':') + { + pos++; + if (*pos == ':') + { + if (zero >= 0) + return NULL; + zero = num; + pos++; + } + last = pos; + } + else if (*pos == '.') + { + if (num > 6) + return NULL; + *(uint32_t *) (&ip->s6_addr16[num - 1]) = htonl (p_ip (&last)); + if (!last) + return NULL; + pos = last; + num++; + break; + } + else + { + break; + } + } + + if (num == 0) + { + if (zero != 0) + return NULL; + ip->s6_addr32[0] = htonl (0); + ip->s6_addr32[1] = htonl (0); + ip->s6_addr32[2] = htonl (0); + ip->s6_addr32[3] = htonl (0); + return pos; + } + + if (num < 8) + { + int i, cp; + if (zero < 0) + return NULL; + /* move */ + for (i = num - 1, cp = 7; i >= zero; --i) + ip->s6_addr16[cp--] = ip->s6_addr16[i]; + /* fill 0 */ + for (i = num, cp = zero; i < 8; ++i) + ip->s6_addr16[cp++] = htons (0); + } + else if (zero >= 0) + { + return NULL; + } + + return pos; +} + +int +p_addr6 (const char *arg, struct sockaddr_in6 *addr) +{ + uint16_t port; + + if (*arg != '.') + { + arg = p_ip6 (arg, &addr->sin6_addr); + if (!arg) + return -1; + if (*arg == 0) + return 0; + if (*arg != '.') + return -1; + arg++; + } + + port = (uint16_t) p_uint (arg, 0xffff, &arg); + if (!arg || *arg != 0) + return -1; + addr->sin6_port = htons (port); + + return 0; +} + +#endif +#ifdef UNIT_LB_H_ + +const static uint64_t UNITS[UNIT_NUM] = { + [UNIT_1] = 1, + [UNIT_k] = 1000, + [UNIT_m] = 1000 * 1000, + [UNIT_g] = 1000 * 1000 * 1000, + [UNIT_w] = 10000, + [UNIT_K] = 1024, + [UNIT_M] = 1024 * 1024, + [UNIT_G] = 1024 * 1024 * 1024, + + [UNIT_hour] = 60 * 60, + [UNIT_min] = 60, + [UNIT_sec] = 1, + + [UNIT_hn] = 1000ull * 1000 * 1000 * 60 * 60, + [UNIT_mn] = 1000ull * 1000 * 1000 * 60, + [UNIT_sn] = 1000 * 1000 * 1000, + [UNIT_1n] = 1000 * 1000 * 1000, + [UNIT_ms] = 1000 * 1000, + [UNIT_us] = 1000, + [UNIT_ns] = 1, + + [UNIT_PC] = 1, +}; + +#define P_UNIT_END(ret, end) ({ \ + *arg = (end); \ + if (unit) *unit = UNITS[ret]; \ + ret; \ +}) + +int +p_unit (const char **arg, int mask, uint64_t * unit) +{ + const char *opt = *arg; + int ret; + + switch (*opt) + { + case 'w': + ret = UNIT_w; + break; + case 'm': + if ((mask & UB_ms) && opt[1] == 's') + return P_UNIT_END (UNIT_ms, opt + 2); + if (mask & UB_min) + return P_UNIT_END (UNIT_min, opt + 1); + ret = UNIT_m; + break; + case 'k': + ret = UNIT_k; + break; + case 'g': + ret = UNIT_g; + break; + + case 'h': + ret = UNIT_hour; + break; + case 's': + ret = UNIT_sec; + break; + case 'u': + if ((mask & UB_us) && opt[1] == 's') + return P_UNIT_END (UNIT_us, opt + 2); + goto NO_UNIT; + case 'n': + if ((mask & UB_ns) && opt[1] == 's') + return P_UNIT_END (UNIT_ns, opt + 2); + goto NO_UNIT; + + case '%': + ret = UNIT_PC; + break; + + case 'K': + ret = UNIT_K; + break; + case 'M': + ret = UNIT_M; + break; + case 'G': + ret = UNIT_G; + break; + + default: + goto NO_UNIT; + } + + if ((1 << ret) & mask) + return P_UNIT_END (ret, opt + 1); + +NO_UNIT: + if (0 == (mask & UB_1)) + return -1; + + return P_UNIT_END (UNIT_1, opt); +} + +#endif +#ifdef CACHED_OUTPUT_LB_H_ + +int +co_app_ch (char buf[], char ch) +{ + struct cohead *head = (struct cohead *) buf; + + buf += sizeof (struct cohead); + + if (head->free > 3) + { + buf += (head->size - head->free); + buf[0] = ch; + buf[1] = 0; + head->free -= 1; + } + else + { + (void) fprintf (CO_OUT (head), "%s%c", buf, ch); + buf[0] = 0; + head->free = head->size; + } + + return 1; +} + +int +co_append (char buf[], cosize_t max, const char *fmt, ...) +{ + int ret; + va_list ap; + struct cohead *head = (struct cohead *) buf; + + /* no space for max+1 size -> output if cached */ + if (max >= head->free && head->free < head->size) + _co_flush (head); + + /* enough space -> try cache format */ + if (max < head->free) + { + char *p = buf + (sizeof (struct cohead) + head->size - head->free); + va_start (ap, fmt); + ret = vsnprintf (p, head->free, fmt, ap); + va_end (ap); + if (ret >= 0 && ret < head->free) + { + head->free -= ret; + return ret; + } + } + + /* no space or format failed -> output if cached */ + if (head->free != head->size) + _co_flush (head); + + /* direct output */ + va_start (ap, fmt); + ret = fprintf (CO_OUT (head), fmt, ap); + va_end (ap); + + return ret; +} + +int +co_wr_uint (char buf[], uint64_t val, int wide) +{ + struct cohead *head = (struct cohead *) buf; + int size = r_uint_wide (val); + char *p; + + if (size > wide) + wide = size; + + if (head->free <= wide) + _co_flush (head); + + if (head->free <= wide) + { + assert (0); + } + + p = buf + (sizeof (struct cohead) + head->size - head->free); + + r_uint_fmt (p, val, wide); + + head->free -= wide; + + return wide; +} + +#endif + +#ifdef SYSTEM_LB_H_ + +pthread_t +lb_thread (void *(*proc) (void *), void *arg, const char *fmt, ...) +{ + pthread_t tid; + int ret; + + ret = pthread_create (&tid, NULL, proc, arg); + if (ret) + return 0; + + if (fmt) + { + char name[20]; + va_list args; + + va_start (args, fmt); + ret = vsnprintf (name, sizeof (name), fmt, args); + va_end (args); + + if (ret > 0) + { + name[sizeof (name) - 1] = 0; + (void) pthread_setname_np (tid, name); + } + } + + return tid; +} + +static void +lb_sigsegv_proc (int s) +{ + int num; + void *buf[128]; + + out ("Signal SIGSEGV, Segmentation fault!\n"); + + num = backtrace (buf, 128); + if (num > 0) + backtrace_symbols_fd (buf, num, STDOUT_FILENO); + exit (1); +} + +void +lb_sigsegv_setup () +{ + struct sigaction s = { 0 }; + + (void) sigemptyset (&s.sa_mask); + + s.sa_flags = SA_NODEFER; + s.sa_handler = (void *) lb_sigsegv_proc; + (void) sigaction (SIGSEGV, &s, NULL); +} + +#endif + +#ifdef KERNEL_SYSCALL_API + +#define KAPI(name) (typeof name) *k_##name = ##name; +#include "kapi.h" +#undef API + +void +kapi_init () +{ +#define KAPI(name) k_##name = ld_load; +} + +#endif diff --git a/thirdparty/apps/testapp/lb/lb.h b/thirdparty/apps/testapp/lb/lb.h new file mode 100644 index 0000000..b7e4ddb --- /dev/null +++ b/thirdparty/apps/testapp/lb/lb.h @@ -0,0 +1,731 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef _LB_H_ +#define _LB_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KB 1000 +#define MB (1000 * KB) +#define GB (1000 * MB) +#define TB (1000 * GB) + +#define MSOFS (1000) +#define USOFS (1000 * 1000) +#define NSOFS (1000 * 1000 * 1000) +#define NSOFMS (1000 * 1000) + +#define NOINLINE __attribute__((noinline)) +#define no_inline __attribute__((noinline)) + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT 15 +#endif + +#include "api.h" + +#define out(fmt, arg...) (void)printf(fmt, ##arg) + +#define info(fmt, arg...) (void)printf("%s[I:%d]%s " fmt, CR, __LINE__, CC, ##arg) +#define wrn(fmt, arg...) (void)printf("%s[W:%d]%s " fmt, FR__, __LINE__, CC, ##arg) +#define err(fmt, arg...) (void)fprintf(stderr, "%s[E:%d]%s " fmt, BR__, __LINE__, CC, ##arg) + +#define WRN(cond, fmt, arg...) do { if (cond) wrn(fmt, ##arg); } while (0) + +#define ERR_RETURN(cond, ret, fmt, arg...) do { \ + if (cond) { \ + if (fmt) err(fmt, ##arg); \ + return ret; \ + } \ +} while(0) + +#define ERR_GOTO(cond, TO, fmt, arg...) do { \ + if (cond) { \ + if (fmt) err(fmt, ##arg); \ + goto TO; \ + } \ +} while (0) + +#define DBGOPT "D" +#define DBGOPT_LONG {"debug", 0, 0, 'D'}, + +extern int enable_debug; + +#define DBG(fmt, arg...) do { \ + if (enable_debug) \ + out("[D:%d]%s " fmt, __LINE__, __func__, ##arg); \ +} while (0) + +#define T DBG("\n"); + +#define CNT_OF(a) (sizeof(a) / sizeof(a[0])) + +#define OFF_OF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define CON_OF(ptr, type, member) ({ \ + const typeof( ((type*)0)->member ) *__mptr = (ptr); \ + (type*)( (char*)__mptr - OFF_OF(type, member) ); \ +}) +#define NUM_OF(a) (sizeof((a)) / sizeof((a)[0])) + +#if 1 +#define TIMEPOINT + +#define TP(name) struct timespec _tp_##name; \ + (void)clock_gettime(CLOCK_MONOTONIC, &_tp_##name) +#define TO(from, to) do { \ + struct timespec _t; \ + LB_SUB_TS(_tp_##to, _tp_##from, _t); \ + out("@TP>%s:%d:%s %lu.%09lu %s-%s\n", __FILE__, __LINE__, __func__, \ + _t.tv_sec, _t.tv_nsec, #from, #to); \ +} while (0) +#define TN(from, to, NUM) do { \ + static uint64_t _n = 0; \ + static struct timespec _t = {0}; \ + _t.tv_nsec += (_tp_##to.tv_nsec - _tp_##from.tv_nsec); \ + _t.tv_sec += (_tp_##to.tv_sec - _tp_##from.tv_sec); \ + if (++_n >= NUM) { \ + uint64_t _nsec = _t.tv_sec * 1000000000ul + _t.tv_nsec; \ + _nsec /= _n; \ + out("@TP<%lu>%s:%d:%s %lu.%09lu %s-%s\n", _n, __FILE__, __LINE__, __func__, \ + _nsec/1000000000ul, _nsec%1000000000ul, #from, #to); \ + _t.tv_sec = 0; \ + _t.tv_nsec = 0; \ + _n = 0; \ + } \ +} while (0) +#else + +#define TP(name) ((void)0) +#define TO(from, to) ((void)0) +#define TN(from, to, NUM) ((void)0) + +#endif + +#define TQ(name) do { TP(__); TO(name, __); } while (0) +#define TM(name, NUM) do { TP(__); TN(name, __, NUM); } while (0) + +#define TO1(n1,n2) TO(n1,n2) +#define TO2(n1,n2,n3) do { TO(n1,n2); TO1(n2,n3); } while(0) +#define TO3(n1,n2,n3...) do { TO(n1,n2); TO2(n2,n3); } while(0) +#define TO4(n1,n2,n3...) do { TO(n1,n2); TO3(n2,n3); } while(0) +#define TO5(n1,n2,n3...) do { TO(n1,n2); TO4(n2,n3); } while(0) +#define TO6(n1,n2,n3...) do { TO(n1,n2); TO5(n2,n3); } while(0) + +#define TE(N,n...) do { TP(__); TO##N(n, __);} while (0) + +#define tp1 TP(1) +#define tq1 TQ(1) +#define tp2 TP(2) +#define tq2 TQ(2) +#define tp3 TP(3) +#define tq3 TQ(3) +#define tp4 TP(4) +#define tq4 TQ(4) +#define tp5 TP(5) +#define tq5 TQ(5) + +#ifndef COLOR_LB_H_ +#define COLOR_LB_H_ + +struct lb_color +{ + const char *clear; + const char *high; + const char *uline; + const char *flash; + const char *rev; + + const char *fblack; + const char *fr__; + const char *f_g_; + const char *f__b; + const char *frg_; + const char *fr_b; + const char *f_gb; + const char *fwhite; + + const char *bblack; + const char *br__; + const char *b_g_; + const char *b__b; + const char *brg_; + const char *br_b; + const char *b_gb; + const char *bwhite; +}; + +enum +{ + LB_DEF_COLOR = 0, + LB_NO_COLOR = 1, +}; + +extern const struct lb_color *lb_color; +extern int lb_color_index; +void lb_set_color (int index); + +#define CC lb_color->clear +#define CR lb_color->rev +#define CH lb_color->high +#define CU lb_color->uline +#define CF lb_color->flash + +#define FBLACK lb_color->fblack +#define FR__ lb_color->fr__ +#define F_G_ lb_color->f_g_ +#define F__B lb_color->f__b +#define FRG_ lb_color->frg_ +#define FR_B lb_color->fr_b +#define F_GB lb_color->f_gb +#define FWHITE lb_color->fwhite + +#define BBLACK lb_color->bblack +#define BR__ lb_color->br__ +#define B_G_ lb_color->b_g_ +#define B__B lb_color->b__b +#define BRG_ lb_color->brg_ +#define BR_B lb_color->br_b +#define B_GB lb_color->b_gb +#define BWHITE lb_color->bwhite + +#endif + +#ifndef TIME_LB_H_ +#define TIME_LB_H_ + +#define LB_RAND(num) ((int) ((random() / (RAND_MAX + 1.0)) * num)) + +#define LB_TIME(now) (void)clock_gettime(CLOCK_MONOTONIC, &(now)) +#define LB_REALTIME(now) (void)clock_gettime(CLOCK_REALTIME, &(now)) + +#define LB_SUB_OS(end, begin) ((end).tv_sec - (begin).tv_sec) +#define LB_SUB_NS(end, begin) (((end).tv_sec - (begin).tv_sec) * NSOFS + (end).tv_nsec - (begin).tv_nsec) + +#define LB_SUB_TS(end, begin, out) ({ \ + if ((end).tv_nsec >= (begin).tv_nsec) { \ + (out).tv_nsec = (end).tv_nsec - (begin).tv_nsec; \ + (out).tv_sec = (end).tv_sec - (begin).tv_sec; \ + } else { \ + (out).tv_nsec = (end).tv_nsec + NSOFS - (begin).tv_nsec; \ + (out).tv_sec = (end).tv_sec - (begin).tv_sec - 1; \ + } \ +}) + +#define LB_CMP(end, begin) ((end).tv_sec > (begin).tv_sec ? 1 : ( \ + (end).tv_sec < (begin).tv_sec ? -1 : (end).tv_nsec - (begin).tv_nsec)) + +#define LB_CMP_SN(end, begin, sec, nsec) ({ \ + time_t _s = (end).tv_sec - (begin).tv_sec; \ + (_s > (sec)) || (_s == (sec) && (end).tv_nsec - (begin).tv_nsec >= (nsec)); \ +}) + +#define LB_CMP_TS(end, begin, ts) LB_CMP_SN((end), (begin), (ts).tv_sec, (ts).tv_nsec) +#define LB_CMP_S(end, begin, sec) LB_CMP_SN((end), (begin), (sec), 0) +#define LB_CMP_NS(end, begin, nsec) LB_CMP_SN((end), (begin), 0, (nsec)) + +#endif /* #ifndef TIME_LB_H_ */ + +#ifndef MATH_LB_H_ +#define MATH_LB_H_ + +/* return a * 10^9 / b */ +inline static uint64_t +lb_gdiv (uint64_t a, uint64_t b) +{ + const uint64_t M = 0xFFFFffffFFFFfffful; + const uint64_t N = 1000 * 1000 * 1000; + const uint64_t P = 1000; + + uint64_t r; + + if (b == 0) + b = 1; + + if (a <= M / N) + return a * N / b; + + r = a / b; + + a = a % b * P; + r = r * P + a / b; + + a = a % b * P; + r = r * P + a / b; + + a = a % b * P; + r = r * P + a / b; + + return r; +} + +inline static uint64_t +lb_sdiv (uint64_t a, uint64_t b) +{ + if (b) + return a / b; + return 0; +} + +#endif /* #ifndef MATH_LB_H_ */ +#ifndef RUN_LB_H_ +#define RUN_LB_H_ + +struct lb_slot +{ + struct timespec begin; + uint64_t count; +}; + +struct lb_run +{ + uint32_t index; + uint32_t mask; + uint32_t rate; + uint32_t time; + uint64_t total; + struct lb_slot slot[0]; +}; + +/* num:1 << N; time: ns */ +inline static void +run_init (struct lb_run *run, uint32_t rate, uint32_t num, uint32_t time) +{ + int i; + struct lb_slot *slot = run->slot; + struct timespec begin; + + LB_TIME (begin); + + run->index = 0; + run->mask = num - 1; + run->rate = rate; + run->time = time; + run->total = 0; + + for (i = 0; i < num; ++i, ++slot) + { + slot->begin = begin; + slot->count = 0; + } +} + +/* return: the number should add for run to now */ +inline static int64_t +run_test (struct lb_run *run, struct timespec *now) +{ + uint64_t time, num; + struct lb_slot *slot = run->slot; + struct lb_slot *cur = slot + (run->index & run->mask); + + if (LB_CMP_NS (*now, cur->begin, run->time)) + { + cur = slot + ((++run->index) & run->mask); + run->total -= cur->count; + cur->count = 0; + cur->begin = *now; + } + + slot += ((run->index + 1) & run->mask); + time = LB_SUB_NS (*now, slot->begin); + num = time * run->rate; + + if ((num % NSOFS) >= (NSOFS / 2)) + return num / NSOFS - run->total + 1; + return num / NSOFS - run->total; +} + +inline static int +run_add (struct lb_run *run, int64_t num) +{ + run->total += num; + run->slot[run->index & run->mask].count += num; +} + +#endif + +#ifndef FORMAT_LB_H_ +#define FORMAT_LB_H_ + +const char *f_in6 (const struct in6_addr *ip6); +const char *f_in6addr (const struct sockaddr_in6 *addr); +const char *f_inaddr (const struct sockaddr_in *addr); +const char *f_uint (uint64_t val); +int s_uint (char *buf, uint64_t val); +int r_uint (char *buf, uint64_t val, int size); + +#endif + +#ifndef PARSE_LB_H_ +#define PARSE_LB_H_ + +uint64_t p_hex (const char *arg, const char **end); +uint64_t p_uint (const char *arg, uint64_t max, const char **end); +inline static long +p_int (const char *arg, long max, const char **end) +{ + return (long) (unsigned long) p_uint (arg, (uint64_t) (unsigned long) max, + end); +} + +struct inaddrs +{ + uint32_t ip; +#if 0 + uint32_t ip_num; + uint32_t ip_step; +#else + int ip_num; +#endif + uint16_t port; + uint16_t port_num; +}; + +uint32_t p_ip (const char **arg); +int p_addr (const char *str, struct sockaddr_in *addr); +const char *p_addr_set (const char *arg, struct inaddrs *addr, uint32_t flag); +int p_addr_list (const char *arg, struct inaddrs *list, int num, + uint32_t flag, const char **end); +int addr_total (const struct inaddrs *list, int num, uint32_t mode); +int addr_layout (const struct inaddrs *list, int list_num, + struct sockaddr_in *addr, int addr_num, uint32_t mode); +inline static int +p_addrin_list (const char *arg, struct sockaddr_in **addr, int max, + uint32_t flag, const char **end) +{ + int num, total; + struct inaddrs list[max]; + struct sockaddr_in *out; + + num = p_addr_list (arg, list, max, flag, end); + if (num <= 0) + return -1; + + total = addr_total (list, num, flag); + if (total > max) + return -1; + + out = (struct sockaddr_in *) malloc (sizeof (struct sockaddr_in) * total); + if (!out) + return -1; + + num = addr_layout (list, num, out, total, flag); + + if (num != total) + { + free (out); + return -1; + } + + *addr = out; + return num; +} + +#define PA_DEFPORT_MASK 0x0000FFFF + +#define PA_NO_TO_IP 0x00010000 +#define PA_NO_NUM_IP 0x00020000 +#define PA_MAY_INV_IP 0x00040000 +#define PA_MULTI_ONE 0x00080000 + +#define PA_NO_TO_PORT 0x00100000 +#define PA_NO_NUM_PORT 0x00200000 +#define PA_NO_PORT 0x00400000 +#define PA_MUST_PORT 0x00800000 +#define PA_DEF_PORT 0x00C00000 + +#define PA_NO_TO_ALL (PA_NO_TO_IP | PA_NO_TO_PORT) +#define PA_NO_NUM_ALL (PA_NO_NUM_IP | PA_NO_NUM_PORT) +#define PA_SINGLE_IP (PA_NO_TO_IP | PA_NO_NUM_IP) +#define PA_SINGLE_PORT (PA_NO_TO_PORT | PA_NO_NUM_PORT) + +#define PAL_NO_SPACE 0x10000000 +#define PAL_WITH_NL 0x20000000 +//#define PAL_SC_SPLIT 0x40000000 + +#define PAL_CROSS_MASK 0x03000000 +#define PAL_IP_X_PORT 0x00000000 +#define PAL_INC_BOTH 0x01000000 +#define PAL_PORT_X_IP 0x02000000 + +const char *p_ip6 (const char *pos, struct in6_addr *ip); +inline static int +inet6_aton (const char *cp, struct in6_addr *addr) +{ + cp = p_ip6 (cp, addr); + if (!cp || cp[0] != 0) + return 0; + return 1; +} + +int p_addr6 (const char *arg, struct sockaddr_in6 *addr); + +#endif + +#ifndef UNIT_LB_H_ +#define UNIT_LB_H_ + +enum unit_type +{ + UNIT_1, + + UNIT_k, + UNIT_m, + UNIT_g, + UNIT_w, + + UNIT_K, + UNIT_M, + UNIT_G, + + UNIT_hour, + UNIT_min, + UNIT_sec, + + UNIT_1n, + UNIT_hn, + UNIT_mn, + UNIT_sn, + UNIT_ms, + UNIT_us, + UNIT_ns, + + UNIT_PC, + + UNIT_NUM, + + UB_1 = 1 << UNIT_1, + + UB_k = 1 << UNIT_k, + UB_m = 1 << UNIT_m, + UB_g = 1 << UNIT_g, + UB_w = 1 << UNIT_w, + + UB_K = 1 << UNIT_K, + UB_M = 1 << UNIT_M, + UB_G = 1 << UNIT_G, + + UB_hour = 1 << UNIT_hour, + UB_min = 1 << UNIT_min, + UB_sec = 1 << UNIT_sec, + + UB_hn = 1 << UNIT_hn, + UB_mn = 1 << UNIT_mn, + UB_sn = 1 << UNIT_sn, + UB_1n = 1 << UNIT_1n, + UB_ms = 1 << UNIT_ms, + UB_us = 1 << UNIT_us, + UB_ns = 1 << UNIT_ns, + + UB_PC = 1 << UNIT_PC, + + UB_T1U_MASK = 3 << UNIT_NUM, + UB_T1U_ns = 0 << UNIT_NUM, + UB_T1U_us = 1 << UNIT_NUM, + UB_T1U_ms = 2 << UNIT_NUM, + UB_T1U_s = 3 << UNIT_NUM, + + UB_kmgw = UB_k | UB_m | UB_g | UB_w, + UB_KMG = UB_K | UB_M | UB_G, + UB_1kmgwKMG = UB_1 | UB_kmgw | UB_KMG, + + UB_smun = UB_sn | UB_ms | UB_us | UB_ns, + UB_hms1 = UB_hour | UB_min | UB_sec | UB_1, + +}; + +int p_unit (const char **arg, int mask, uint64_t * unit); + +inline static uint64_t +p_value (const char *arg, uint64_t max, int mask, const char **end) +{ + uint64_t val = p_uint (arg, max, &arg); + + if (arg) + { + uint64_t unit; + if (p_unit (&arg, mask, &unit) >= 0) + { + val *= unit; + if (val <= max) + { + if (end) + *end = arg; + return val; + } + } + } + + if (end) + *end = NULL; + return 0; +} + +#endif + +#ifndef CACHED_OUTPUT_LB_H_ +#define CACHED_OUTPUT_LB_H_ + +typedef uint16_t cosize_t; + +struct cohead +{ + cosize_t size; + cosize_t free; +}; + +#define CO_OUT(head) stdout +#define CO_INIT(buf) co_init(buf, sizeof(buf)) + +inline static void +co_init (char buf[], cosize_t size) +{ + struct cohead *head = (struct cohead *) buf; + head->size = size - sizeof (struct cohead); + head->free = head->size; + buf[sizeof (struct cohead)] = 0; +} + +inline static void +_co_flush (struct cohead *head) +{ + char *buf = (char *) (head + 1); + (void) fputs (buf, CO_OUT (head)); + head->free = head->size; + *buf = 0; +} + +inline static void +co_flush (char buf[]) +{ + struct cohead *head = (struct cohead *) buf; + if (head->free != head->size) + _co_flush (head); +} + +int co_wr_uint (char buf[], uint64_t val, int wide); + +int co_app_ch (char buf[], char ch); +#define co_ch_if(cond, buf, ch) ( \ + (!!(cond)) ? co_app_ch((buf), (ch)) : 0) + +int co_append (char buf[], cosize_t max, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 3, 4))); +#define co_app_if(cond, buf, max, fmt, arg...) ( \ + (!!(cond)) ? co_append((buf), (max), (fmt), ##arg) : 0) +#define co_app_fls(buf, max, fmt, arg...) do { \ + co_append(buf, max, fmt, ##arg); \ + co_flush(); \ +} while (0) + +#endif + +#ifndef SYSTEM_LB_H_ +#define SYSTEM_LB_H_ + +inline static void +futex_wait (volatile int *addr, int val) +{ + while (*addr == val) + { + (void) syscall (SYS_futex, addr, FUTEX_WAIT, val, NULL, NULL, 0); + } +} + +inline static void +futex_wake (volatile int *addr, int num) +{ + (void) syscall (SYS_futex, addr, FUTEX_WAKE, num, NULL, NULL, 0); +} + +pthread_t lb_thread (void *(*proc) (void *), void *arg, const char *fmt, ...); + +inline static int +lb_setcpu (pthread_t thread, int cpu) +{ + cpu_set_t set; + CPU_ZERO (&set); + CPU_SET (cpu, &set); + return pthread_setaffinity_np (thread, sizeof (set), &set); +} + +inline static int +lb_sleep (time_t sec, long nsec) +{ +const struct timespec timeout = { tv_sec: sec, tv_nsec:nsec }; + return nanosleep (&timeout, NULL); +} + +void lb_sigsegv_setup (); + +#endif /* #ifndef SYSTEM_LB_H_ */ + +#define FD_CLOSE(fd) do { \ + if(fd >= 0) { \ + _close(fd); \ + fd = -1; \ + } \ +} while(0) + +#define BUF_FREE(p) do { \ + if (p) { \ + free(p); \ + p = NULL; \ + } \ +} while (0) + +#endif + +#ifdef KERNEL_SYSCALL_API +#define KERNEL_SYSCALL_API + +#define KAPI(name) extern (typeof name) *k_##name; +#include "kapi.h" +#undef API + +#endif diff --git a/thirdparty/apps/testapp/lp/lp.c b/thirdparty/apps/testapp/lp/lp.c new file mode 100644 index 0000000..a9b71d1 --- /dev/null +++ b/thirdparty/apps/testapp/lp/lp.c @@ -0,0 +1,1138 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" +#include "lp.h" + +void lp_exec (struct lp_worker *worker); + +#define LP_OPTIONS "s:c:bi:e:m:nwEC" DBGOPT "vh" + +static const struct option lp_options[] = { + {"block", 0, 0, 'b'}, + {"server", 1, 0, 's'}, + {"client", 1, 0, 'c'}, + {"nodelay", 0, 0, 'n'}, + {"interval", 1, 0, 'i'}, + {"no-error", 1, 0, 'e'}, + {"error-msg", 0, 0, 'E'}, + {"core", 1, 0, 'm'}, + DBGOPT_LONG {"watch", 0, 0, 'w'}, + {"no-color", 0, 0, 'C'}, + {"verbose", 0, 0, 'v'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} +}; + +static const char *MODES[] = { + [LP_RAND] = "*random link", + [LP_SYNC] = "=sync increment", + [LP_CPS] = "}client per server", + [LP_SPC] = "{server per client", +}; + +struct lp_var lp = { 0 }; + +void +lp_dump_sess (int fd) +{ + char buf[256]; + struct lp_sess *sess = LP_SESS (fd); + + co_init (buf, sizeof (buf)); + + co_append (buf, 80, + "fd:%d sess:%p state:0x%x test:%u epout:%u io_num:%u query:%d reply:%d\n", + fd, sess, sess->state, sess->test, sess->epout, sess->io_num, + sess->query, sess->reply); + co_append (buf, 50, " nest-sess:%d prev-sess:%p", sess->next_sess, + sess->prev_sess); + co_app_if (sess->prev_sess, buf, 10, "(%ld)", + CON_OF (sess->prev_sess, struct lp_sess, next_sess) - lp.sess); + co_append (buf, 50, " nest-rest:%d prev-rest:%p", sess->next_rest, + sess->prev_rest); + co_app_if (sess->prev_rest, buf, 10, "(%ld)", + CON_OF (sess->prev_rest, struct lp_sess, next_rest) - lp.sess); + co_append (buf, 30, " time:%ld.%09ld\n", sess->time.tv_sec, + sess->time.tv_nsec); + + co_flush (buf); +} + +void +lp_clean (struct lp_worker *worker) +{ + int i, fd; + + while (lp.run_state == LP_CLEAN) + { + fd = worker->conn.first; + if (fd < 0) + break; + lp_del_conn (worker, fd, LP_SESS (fd)); + } + LP_ASSERT (worker->conn.num == 0); + LP_ASSERT (worker->conn.first == -1); + LP_ASSERT (worker->conn.last = &worker->conn.first); + + while (lp.run_state == LP_CLEAN) + { + fd = worker->sess.first; + if (fd < 0) + break; + lp_del_sess (worker, fd); + } + LP_ASSERT (worker->sess.num == 0); + LP_ASSERT (worker->sess.first == -1); + LP_ASSERT (worker->sess.last = &worker->sess.first); + + for (i = 0; i < LP_MAX_TEST; ++i) + { + LP_ASSERT (worker->rest[i].num == 0); + LP_ASSERT (worker->rest[i].first == -1); + LP_ASSERT (worker->rest[i].last = &worker->rest[i].first); + LP_ASSERT (worker->wait[i].num == 0); + LP_ASSERT (worker->wait[i].first == -1); + LP_ASSERT (worker->wait[i].last = &worker->rest[i].first); + } +} + +void * +lp_thread (void *arg) +{ + const uint64_t one = 1; + struct lp_worker *worker = (struct lp_worker *) arg; + + (void) __sync_or_and_fetch (&lp.active_worker, one << worker->index); + + futex_wait (&lp.run_state, LP_INIT); + + if (lp.client_mode) + lp_client (worker); + else + lp_server (worker); + + lp_clean (worker); + + out ("%s! worker %d return%s\n", CR, worker->index, CC); + (void) __sync_and_and_fetch (&lp.active_worker, ~(one << worker->index)); + + return NULL; +} + +void +lp_round (int id) +{ + struct lp_test *test = &lp.test[id]; + char buf[128]; + + co_init (buf, sizeof (buf)); + + co_append (buf, 30, "%s! round %d:", CR, id); + co_append (buf, 30, " target:%s", + test->target == LP_TARGET_INF ? "*" : f_uint (test->target)); + co_append (buf, 30, " time:%s", + test->time == LP_TIME_INF ? "*" : f_uint (test->time)); + co_append (buf, 30, " up:%s", + test->up == LP_UP_INF ? "*" : f_uint (test->up)); + co_append (buf, 30, " down:%s", + test->down == LP_DOWN_INF ? "*" : f_uint (test->down)); + co_append (buf, 30, " query:%u", test->query); + co_append (buf, 30, " reply:%u", test->reply); + co_append (buf, 30, " times:%s", + test->times == LP_TIMES_INF ? "*" : f_uint (test->times)); + co_app_if (test->close_after_io, buf, 1, "-"); + co_append (buf, 30, " period:%u", test->period); + co_append (buf, 30, " wait:%u", test->wait); + co_append (buf, 30, " %s\n", CC); + + co_flush (buf); +} + +int +lp_start () +{ + int i; + cpu_set_t set; + + lp.sess = (struct lp_sess *) calloc (LP_MAX_FD, sizeof (struct lp_sess)); + ERR_RETURN (!lp.sess, -1, "Out of memory\n"); + + lp.run_state = LP_INIT; + + lp.curr = lp.stat[0]; + lp.next = lp.stat[1]; + + if (lp.interval == 0) + lp.interval = LP_INTERVAL_DEF; + + if (lp.core) + { + CPU_ZERO (&set); + for (i = 0; i < 64; ++i) + { + if (lp.core & (1 << i)) + CPU_SET (i, &set); + } + } + + for (i = 0; i < lp.worker_num; ++i) + { + int ret; + struct epoll_event event; + struct lp_worker *worker = LP_WORKER (i); + + out ("[%d] creating worker\n", i); + + worker->io_buf = malloc (LP_IOBUF_SIZE); + ERR_RETURN (!worker->io_buf, -1, "Out of memory for IO buffer\n"); + + worker->ev_buf = malloc (sizeof (struct epoll_event) * LP_EVENT_NUM); + ERR_RETURN (!worker->ev_buf, -1, "Out of memory for events buffer\n"); + + TP (epoll_create); + worker->epfd = _epoll_create (10); + ERR_RETURN (worker->epfd < 0, -1, "epoll_create()=%d:%d\n", + worker->epfd, errno); + worker->ctlfd = eventfd (0, 0); + ERR_RETURN (worker->ctlfd < 0, -1, "eventfd()=%d:%d\n", worker->epfd, + errno); + TQ (epoll_create); + + event.events = EPOLLIN; + event.data.u64 = LP_EV_MK (LP_CONTROL_TYPE, worker->ctlfd); + ret = _epoll_ctl (worker->epfd, EPOLL_CTL_ADD, worker->ctlfd, &event); + ERR_RETURN (ret, -1, "epoll_ctl(epfd:%d, add, evfd:%d,)=%d:%d\n", + worker->epfd, worker->ctlfd, ret, errno); + + worker->tid = + lb_thread (lp_thread, worker, "lp-%s-%d", + lp.client_mode ? "client" : "server", i); + ERR_RETURN (worker->tid <= 0, -1, "Create worker thread failed\n"); + + if (lp.core) + { + ret = pthread_setaffinity_np (worker->tid, sizeof (set), &set); + if (ret) + err ("Bind core failed!\n"); + } + + if (lp.client_mode) + { + size_t size; + + size = + sizeof (struct lb_run) + sizeof (struct lb_slot) * LP_UP_SLOT; + worker->up_run = (struct lb_run *) calloc (1, size); + ERR_RETURN (!worker->up_run, -1, "Out of memory for up run\n"); + + size = + sizeof (struct lb_run) + sizeof (struct lb_slot) * LP_DOWN_SLOT; + worker->down_run = (struct lb_run *) calloc (1, size); + ERR_RETURN (!worker->down_run, -1, "Out of memory for down run\n"); + + out + ("[%d] worker %ld created, client:%d server:%d epfd:%d ctlfd:%d\n", + worker->index, pthread_self (), worker->client_num, + worker->server_num, worker->epfd, worker->ctlfd); + } + else + { + int j; + TP (lp_listen); + for (j = 0; j < worker->server_num; ++j) + { + if (lp_listen (worker, j)) + return -1; + } + TQ (lp_listen); + out ("[%d] worker %ld created, server:%d epfd:%d ctlfd:%d\n", + worker->index, pthread_self (), worker->server_num, + worker->epfd, worker->ctlfd); + } + } + + out ("%s! running %s\n", CR, CC); + if (lp.client_mode) + { + lp_round (0); + } + + lp.run_state = LP_EXEC; + + futex_wake (&lp.run_state, lp.worker_num); + return 0; +} + +void +lp_stop (int state) +{ + int i; + + lp.run_state = state; + + for (i = 0; i < lp.worker_num; ++i) + { + struct lp_worker *worker = LP_WORKER (i); + if (worker->tid && worker->ctlfd >= 0) + { + (void) lp_post_cmd (worker->ctlfd, LP_CMD_STOP); + } + } +} + +#define _FREE(p) do { if (p) { free(p); p = NULL; } } while (0) + +void +lp_exit () +{ + int i; + + lp_stop (LP_EXIT); + + for (i = 0; i < lp.worker_num; ++i) + { + int fd; + struct lp_worker *worker = LP_WORKER (i); + + if (worker->tid) + { + const struct timespec wait = { tv_sec: 3, tv_nsec:0 }; + (void) pthread_timedjoin_np (worker->tid, NULL, &wait); + worker->tid = 0; + } + + for (fd = worker->server.first; fd >= 0; fd = LP_SESS (fd)->next_sess) + _close (fd); + + lp_clean (worker); + + if (worker->ctlfd >= 0) + { + _close (worker->ctlfd); + worker->ctlfd = -1; + } + if (worker->epfd >= 0) + { + _close (worker->epfd); + worker->epfd = -1; + } + + _FREE (worker->server_addr); + _FREE (worker->client_addr); + _FREE (worker->up_run); + _FREE (worker->down_run); + _FREE (worker->io_buf); + _FREE (worker->ev_buf); + } + + _FREE (lp.sess); +} + +inline static const char * +lp_errmsg (int e) +{ + static const char *errmsg[LP_ERRNO_NUM] = { 0 }; + if (NULL == errmsg[e]) + errmsg[e] = strerror (e); + return errmsg[e]; +} + +const static char *lp_cntmsg[LP_CNT_NUM] = { + [LP_E_SOCKET] = "socket", + [LP_E_BIND] = "bind", + [LP_E_ACCEPT] = "accept", + [LP_E_CONNECT] = "connect", + [LP_E_NODELAY] = "nodelay", + [LP_E_NONBLOCK] = "nonblock", + [LP_E_REUSEADDR] = "reuseaddr", + [LP_E_REUSEPORT] = "reuseport", + [LP_E_RECV] = "recv", + [LP_E_SEND] = "send", + + [LP_E_EPADD] = "ep-add", + [LP_E_EPMOD] = "ep-mod", + [LP_E_EPDEL] = "ep-del", + [LP_E_EPWAIT] = "ep-wait", + [LP_E_EPUNUSED] = "ep-unused", + [LP_E_EPHUP] = "ep-hup", + [LP_E_EPERR] = "ep-err", + [LP_E_EPINOUT] = "ep-inout", + [LP_E_EVIDLE] = "ep-idle", + [LP_E_EPEVENT] = "ep-event", + + [LP_E_IOSHUT] = "io-shut", + [LP_E_IOSIZE] = "io-size", + [LP_E_IOMORE] = "io-more", + [LP_E_IOEXCEED] = "io-exceed", + [LP_E_IOSEND0] = "io-send0", + + [LP_W_CREATE] = "cre", + [LP_W_SOCKET] = "soc", + [LP_W_BIND] = "bin", + [LP_W_CONNECT] = "con", + [LP_W_CONNECTED] = "est", + [LP_W_ACCEPT] = "acc", + [LP_W_CLOSE] = "clo", +}; + +void +lp_output (char buf[], const struct lp_stat *stat, uint64_t nsec, int mask) +{ + int i; + const uint64_t *cnt = stat->cnt; + static int w_num = 0, w_up = 0, w_down = 0, w_conn = 0; + static int w_q_mb = 0, w_q_cp = 0, w_r_mb = 0, w_r_cp = 0; + + co_append (buf, 5, " %s ", CH); + w_num = co_wr_uint (buf, cnt[LP_REC_NUM], w_num); + co_append (buf, 5, "%s", CC); + + co_append (buf, 3, " < "); + w_up = co_wr_uint (buf, lb_gdiv (cnt[LP_CONNECTED], nsec), w_up); + if (lp.client_mode && !lp.block_connecting) + { + co_append (buf, 1, " / "); + w_conn = co_wr_uint (buf, lb_gdiv (cnt[LP_CONNECT], nsec), w_conn); + } + co_append (buf, 3, " - "); + w_down = co_wr_uint (buf, lb_gdiv (cnt[LP_CLOSE], nsec), w_down); + co_append (buf, 3, " > "); + + co_append (buf, 3, " [ "); + w_q_mb = + co_wr_uint (buf, + lb_gdiv (cnt[LP_QUERY_BYTE] * 8, nsec /* * (1000000 / 8) */ ), + w_q_mb); + co_append (buf, 1, " "); + w_q_cp = co_wr_uint (buf, lb_gdiv (cnt[LP_QUERY_COMP], nsec /* * 1000 */ ), + w_q_cp); + co_append (buf, 3, " : "); + w_r_mb = + co_wr_uint (buf, + lb_gdiv (cnt[LP_REPLY_BYTE] * 8, nsec /* * (1000000 / 8) */ ), + w_r_mb); + co_append (buf, 1, " "); + w_r_cp = co_wr_uint (buf, lb_gdiv (cnt[LP_REPLY_COMP], nsec /* * 1000 */ ), + w_r_cp); + co_append (buf, 3, " ] "); + + co_app_if (cnt[LP_FAILED], buf, 40, " F:%s%s%s", + FR__, f_uint (lb_gdiv (cnt[LP_FAILED], nsec)), CC); + + if (mask & LP_W_SIGN) + { + co_append (buf, 8, " time{"); + for (i = LP_W_BEGIN; i < LP_W_END; i += 2) + co_app_if (cnt[i], buf, 60, " %s:%s", lp_cntmsg[i], + f_uint (cnt[i + 1] / cnt[i])); + co_append (buf, 4, " }"); + } + + if (mask & LP_E_SIGN) + { + co_append (buf, 8, " err{"); + for (i = LP_E_BEGIN; i < LP_E_END; ++i) + co_app_if (cnt[i], buf, 60, " %s:%s", lp_cntmsg[i], f_uint (cnt[i])); + co_append (buf, 4, " }"); + } + + if (!lp.err_msg && (mask & LP_ERR_SIGN)) + { + co_append (buf, 5, " E:{"); + for (i = 1; i < LP_ERRNO_NUM; ++i) + co_app_if (stat->err[i], buf, 40, " %d:%lu", i, stat->err[i]); + co_app_if (stat->err[0], buf, 30, " -:%lu", stat->err[0]); + co_append (buf, 5, " }"); + } + + co_append (buf, 4, "\n"); + + if (lp.err_msg && (mask & LP_ERR_SIGN)) + { + for (i = 1; i < LP_ERRNO_NUM; ++i) + co_app_if (stat->err[i], buf, 100, " %s\n", i, stat->err[i], + lp_errmsg (i)); + co_app_if (stat->err[0], buf, 100, " Other error\n", + f_uint (stat->err[0])); + } +} + +void +lp_timer (uint64_t nsec) +{ + const static struct timespec delay = {.tv_sec = 0,.tv_nsec = + LP_DELAY_MS * 1000 * 1000 + }; + static int line = -2; + static int base; + + int i, second, total = 0; + char buf[256]; + struct tm *lc; + struct lp_stat *curr, sum = { 0 }; + + curr = lp.curr; + lp.curr = lp.next; + lp.next = curr; + + { + time_t tv = time (NULL); + lc = localtime (&tv); + } + + co_init (buf, sizeof (buf)); + + (void) nanosleep (&delay, NULL) /* wait for cps.curr use */ ; + + for (i = 0; i < lp.worker_num; ++i) + { + int j, mask = 0; + uint64_t count = 0; + + curr->cnt[LP_REC_NUM] = LP_WORKER (i)->sess.num; + + for (count = 0, j = 0; j < LP_R_END; ++j) + { + sum.cnt[j] += curr->cnt[j]; + count += curr->cnt[j]; + } + if (count) + mask |= LP_R_SIGN; + + for (count = 0, j = LP_E_BEGIN; j < LP_E_END; ++j) + { + sum.cnt[j] += curr->cnt[j]; + count += curr->cnt[j]; + } + if (count) + mask |= LP_E_SIGN; + + if (lp.watch) + { + for (count = 0, j = LP_W_BEGIN; j < LP_W_END; j += 2) + { + sum.cnt[j] += curr->cnt[j]; + sum.cnt[j + 1] += curr->cnt[j + 1]; + count += curr->cnt[j]; + } + if (count) + mask |= LP_W_SIGN; + } + + for (count = 0, j = 0; j < LP_ERRNO_NUM; ++j) + { + sum.err[j] += curr->err[j]; + count += curr->err[j]; + } + if (count) + mask |= LP_ERR_SIGN; + + if (mask && lp.verbose) + { + co_append (buf, 10, " %4dw ", i); + lp_output (buf, curr + i, nsec, mask); + } + + total |= mask; + } + + if (total) + { + line = line < 0 ? 0 : line + 1; + if (line == 0) + { + base = lc->tm_hour * 3600 + lc->tm_min * 60 + lc->tm_sec; + } + } + else + { + line = line > 0 ? 0 : line - 1; + if (line == -1) + co_append (buf, 5, "\n"); + } + + if (line >= 0) + { + if (line) + { + second = (lc->tm_hour * 3600 + lc->tm_min * 60 + lc->tm_sec) - base; + while (second < 0) + second += (24 * 3600); + co_append (buf, 10, " %5d", second); + } + else + { + co_append (buf, 10, " %2d:%02d ", lc->tm_hour, lc->tm_min); + } + lp_output (buf, &sum, nsec, total); + } + + co_flush (buf); + + (void) memset (curr, 0, sizeof (struct lp_stat) * lp.worker_num); +} + +int +lp_loop () +{ + const static struct timespec timeout = {.tv_sec = 0,.tv_nsec = LP_LOOP_TIMER + }; + + struct timespec begin, from, last_begin; + time_t next_time = lp.interval; + + LB_TIME (begin); + from = begin; + last_begin = begin; + + while (lp.run_state > 0) + { + struct timespec now; + + (void) nanosleep (&timeout, NULL); + + LB_TIME (now); + + if (lp.run_state == LP_CLEAN) + { + if (lp.active_worker == 0) + break; + } + + if (lp.client_mode && lp.run_state == LP_EXEC) + { + struct lp_test *test = &lp.test[lp.test_id]; + uint64_t total = lp_total_sess (); + + if (LB_CMP_S (now, last_begin, test->time) + || (test->up >= test->down ? total >= test->target : total <= + test->target)) + { + if (lp.test_id >= lp.test_num - 1) + { + lp.run_state = LP_CLEAN; + out ("%s! cleanup%s\n", CR, CC); + } + else + { + lp.test_id++; /* run changed */ + last_begin = now; + lp_round (lp.test_id); + } + } + } + + if (!LB_CMP_S (now, begin, next_time)) + continue; + + lp_timer (LB_SUB_NS (now, from)); + + from = now; + next_time += lp.interval; + + if (!lp.client_mode) + { + const struct timespec rest = {.tv_sec = lp.interval - 1,.tv_nsec = + LP_LOOP_REST + }; + (void) nanosleep (&rest, NULL); + } + } + + return 0; +} + +#ifndef SIGNAL_LP_C_ +#define SIGNAL_LP_C_ + +void +lp_break (int s) +{ + DBG (" SIGNALED %d running:%d\n", s, lp.run_state); + out ("\n"); + + if (lp.run_state == LP_CLEAN) + { + out ("%s! safe exit%s\n", CR, CC); + lp_exit (); + } + else if (lp.run_state >= 0) + { + out ("%s! clean exit%s\n", CR, CC); + lp_stop (LP_CLEAN); + } + else + { + out ("%s! direct exit%s\n", CR, CC); + exit (1); + } +} + +void +lp_sigpipe (int s) +{ + DBG ("SIGPIPE\n"); +} + +int +lp_init () +{ + struct sigaction s = { 0 }; + + (void) sigemptyset (&s.sa_mask); + + s.sa_flags = SA_NODEFER; + s.sa_handler = (void *) lp_break; + (void) sigaction (SIGINT, &s, NULL); + (void) sigaction (SIGQUIT, &s, NULL); + + s.sa_handler = lp_sigpipe; + (void) sigaction (SIGPIPE, &s, NULL); + +// lb_sigsegv_setup(); + + lp.CPU_NUM = get_nprocs (); + if (lp.CPU_NUM <= 0) + lp.CPU_NUM = 1; + + { + struct timespec t; + LB_TIME (t); + srandom (getpid () + t.tv_sec + (t.tv_sec >> 32) + t.tv_nsec + + (t.tv_nsec >> 32)); + } + + return 0; +} + +#endif + +void +lp_usage (const char *name) +{ + out ("USAGE: %s [OPTIONS] TEST-SET... # %s version\n", name, VERSION_NAME); +} + +void +lp_help (const char *name) +{ + lp_usage (name); + out (" Options:\n"); + out (" -s, --server LIST set one server address list\n"); + out (" X.Y.Z.M-N:P1-P2,...\n"); + out (" -c, --client LIST set one client address list\n"); + out + (" CLIENT*SERVER: R.S.T.K-J:Pa-Pb,...*X.Y.Z.M-N:P1-P2,...\n"); + out (" A,B,C,D*1,2 random link\n"); + out (" A,B,C,D=1,2 A1B2C1D2\n"); + out (" A,B,C,D}1,2 A1B1C1D1 A2B2C2D2\n"); + out + (" A,B,C,D{1,2 A1A2 B1B2 C1C2 D1D2\n"); + out + (" -b, --block set block mode for connecting(client only)\n"); + out (" -n, --nodelay set nodelay\n"); + out (" -i, --interval # report time(default:%ds max:%ds)\n", + LP_INTERVAL_DEF, LP_INTERVAL_MAX); + out + (" -m, --core #HEX set bind cpu core mask(hex mode)\n"); +#ifdef DEBUG + out (" -D, --debug show debug information\n"); +#endif + out + (" -w, --watch show watch time statistic\n"); + out (" -e, --no-error #-# skip error\n"); + out (" -E, --error-msg show error message\n"); + out (" -C, --no-color no color\n"); + out (" -v, --verbose show worker statistics\n"); + out (" -h, --help help\n"); + out (" TEST-SET for client\n"); + out + (" TARGET@TIME+UP-DOWN=QUERY:REPLY*TIMES-/PERIOD%%WAIT (client only)\n"); + out (" TARGET max connection(default: INFINITE)\n"); + out (" @TIME max time(0 or default:INFINITE)\n"); + out + (" +UP connect rate(default: 0 no connnect; *: INFINITE)\n"); + out + (" -DOWN close rate(default: 0 no close; *: INFINITE)\n"); + out (" =... IO set(default: no IO)\n"); + out (" QUERY send query data len(%u-%u)\n", + LP_QUERY_MIN, LP_QUERY_MAX); + out + (" :REPLY receive response data len(0-%d; default: same with QUERY)\n", + LP_REPLY_MAX); + out + (" *TIMES- IO times(0 or default: INFINITE; suffix-: IO then close)\n"); + out + (" /PERIOD IO period time(0-%us; default: one by one)\n", + LP_PERIOD_MAX); + out + (" %%WAIT first IO wait time(0-%us; default: 0 no wait)\n", + LP_WAIT_MAX); + out (" UNITS:\n"); + out (" k=1000 m=1000k g=1000m w=10000 K=1024 M=1024K G=1024M\n"); + out (" s=Seconds m=Minutes h=Hours\n"); +} + +int +lp_args_test (const char *arg) +{ + const char *p; + struct lp_test *test = &lp.test[lp.test_num]; + + ERR_RETURN (lp.test_num >= LP_MAX_TEST, -1, "Too many test set, max:%d\n", + LP_MAX_TEST); + (void) memset (test, 0, sizeof (struct lp_test)); + + if (*arg >= '0' && *arg <= '9') + { + test->target = p_value (arg, LP_TARGET_MAX, UB_1kmgwKMG, &p); + ERR_RETURN (!p, -1, "Invalid test TARGET set: '%s'\n", arg); + } + else + { + test->target = LP_TARGET_DEF; + p = arg; + } + + if (*p == '@') + { + test->time = p_value (p + 1, LP_TIME_MAX, UB_hms1, &p); + ERR_RETURN (!p, -1, "Invalid test TIME set: '%s'\n", arg); + } + else + { + test->time = LP_TIME_DEF; + } + + if (*p == '+') + { + if (p[1] == '*') + { + test->up = LP_UP_INF; + p += 2; + } + else + { + test->up = p_value (p + 1, LP_UP_MAX, UB_1kmgwKMG, &p); + ERR_RETURN (!p, -1, "Invalid test UP-RATE set: '%s'\n", arg); + } + } + else + { + test->up = LP_UP_DEF; + } + + if (*p == '-') + { + if (p[1] == '*') + { + test->down = LP_DOWN_INF; + p += 2; + } + else + { + test->down = p_value (p + 1, LP_DOWN_MAX, UB_1kmgwKMG, &p); + ERR_RETURN (!p, -1, "Invalid test DOWN-RATE set: '%s'\n", arg); + } + } + else + { + test->down = LP_DOWN_DEF; + } + + if (*p == '=') + { + test->query = p_value (p + 1, LP_QUERY_MAX, UB_1kmgwKMG, &p); + ERR_RETURN (!p + || test->query < LP_QUERY_MIN, -1, + "Invalid test QUERY '%s'\n", arg); + + if (*p == ':') + { + test->reply = p_value (p + 1, LP_REPLY_MAX, UB_1kmgwKMG, &p); + ERR_RETURN (!p, -1, "Invalid test REPLY set: '%s'\n", arg); + } + else + { + test->reply = test->query; + } + + if (*p == '*') + { + test->times = p_uint (p + 1, LP_TIMES_MAX, &p); + ERR_RETURN (!p, -1, "Invalid test TIMES set: '%s'\n", arg); + if (test->times == 0) + test->times = LP_TIMES_INF; + if (*p == '-') + { + test->close_after_io = 1; + p++; + } + } + else + { + test->times = LP_TIMES_DEF; + } + + if (*p == '/') + { + test->period = p_value (p + 1, LP_PERIOD_MAX, UB_hms1, &p); + ERR_RETURN (!p, -1, "Invalid test PRIOD set: '%s'\n", arg); + if (*p == '%') + { + test->wait = p_value (p + 1, LP_WAIT_MAX, UB_hms1, &p); + ERR_RETURN (!p, -1, "Invalid test WAIT set: '%s'\n", arg); + } + else + { + test->wait = LP_WAIT_DEF; + } + } + else + { + test->period = LP_PERIOD_DEF; + test->wait = 0; + } + } + else + { + test->query = 0; + test->reply = 0; + test->times = 0; + test->period = 0; + test->wait = 0; + } + + ERR_RETURN (*p, -1, "Invalid test set: '%s'\n", arg); + + if (test->up < test->down) + test->down_mode = 1; + lp.test_num++; + return 0; +} + +inline static void +lp_noerr (int b, int e) +{ + for (; b <= e; ++b) + lp.no_err[b / 64] |= (1 << (b % 64)); +} + +/* -e M-N */ +int +lp_args_noerr (const char *arg) +{ + int b, e; + + b = (int) p_uint (arg, LP_ERRNO_NUM, &arg); + if (!arg) + return -1; + if (*arg == '-') + { + e = (int) p_uint (arg + 1, LP_ERRNO_NUM, &arg); + if (!arg || e < b) + return -1; + } + else + { + e = b; + } + + if (*arg != 0) + return -1; + + lp_noerr (b, e); + + return 0; +} + +struct lp_worker * +lp_init_worker () +{ + int i; + struct lp_worker *worker = LP_WORKER (lp.worker_num); + + ERR_RETURN (lp.worker_num >= LP_MAX_WORKER, NULL, + "Too many workers, limit:%d\n", LP_MAX_WORKER); + + (void) memset (worker, 0, sizeof (*worker)); + + worker->index = lp.worker_num; + worker->epfd = -1; + worker->ctlfd = -1; + + lp_init_head (&worker->server); + lp_init_head (&worker->sess); + lp_init_head (&worker->conn); + for (i = 0; i < LP_MAX_TEST; ++i) + { + lp_init_head (&worker->rest[i]); + lp_init_head (&worker->wait[i]); + } + + lp.worker_num++; + + return worker; +} + +int +lp_args (int argc, char *argv[]) +{ + int i, opt, index, ret; + struct lp_worker *worker; + const char *arg; + int server_mode = 0; + + while (EOF != + (opt = getopt_long (argc, argv, LP_OPTIONS, lp_options, &index))) + { + const char *end; + + switch (opt) + { + case 'c': + ERR_RETURN (server_mode, -1, "Only server or client\n"); + lp.client_mode = 1; + worker = lp_init_worker (); + if (!worker) + return -1; + worker->client_num = p_addrin_list (optarg, &worker->client_addr, + LP_CLIENT_MAX, + PA_DEF_PORT | PAL_NO_SPACE, + &arg); + ERR_RETURN (worker->client_num <= 0, -1, + "Bad client for address list '%s'\n", optarg); + for (i = 0; i < CNT_OF (MODES); ++i) + { + if (*arg == MODES[i][0]) + break; + } + ERR_RETURN (i >= CNT_OF (MODES), -1, + "Bad mode for address list '%s'\n", optarg); + worker->link_mode = i; + arg++; + worker->server_num = p_addrin_list (arg, &worker->server_addr, + LP_SERVER_MAX, + PA_MUST_PORT | PAL_NO_SPACE, + NULL); + ERR_RETURN (worker->server_num <= 0, -1, + "Bad server for address list '%s'\n", arg); + break; + case 's': + ERR_RETURN (lp.client_mode, -1, "Only server or client\n"); + server_mode = 1; + worker = lp_init_worker (); + if (!worker) + return -1; + worker->server_num = + p_addrin_list (optarg, &worker->server_addr, LP_SERVER_MAX, + PA_MUST_PORT | PAL_NO_SPACE, NULL); + ERR_RETURN (worker->server_num <= 0, -1, + "Bad server for address list '%s'\n", optarg); + break; + + case 'i': + lp.interval = (int) p_int (optarg, LP_INTERVAL_MAX, &end); + ERR_RETURN (!end || *end, -1, "Invalid interval '%s'\n", optarg); + break; + case 'e': + ret = lp_args_noerr (optarg); + ERR_RETURN (ret, ret, "Invalid no-error set '%s'\n", optarg); + break; + case 'm': + lp.core = p_hex (optarg, &end); + ERR_RETURN (!end + || lp.core >= (1 << lp.CPU_NUM), -1, + "Invalid bind core set\n"); + break; + case 'C': + lb_set_color (LB_NO_COLOR); + break; + + case 'b': + lp.block_connecting = 1; + break; + case 'n': + lp.nodelay = 1; + break; + case 'w': + lp.watch = 1; + break; + case 'E': + lp.err_msg = 1; + break; + case 'v': + lp.verbose = 1; + break; + +#ifdef DEBUG + case 'D': + enable_debug = 1; + break; +#endif + case 'h': + lp_help (argv[0]); + exit (0); + case '?': + err ("Invalid arguments\n"); + return -1; + default: + err ("Unknown option '%c'.\n", opt); + return -1; + } + } + + ERR_RETURN (lp.worker_num <= 0, -1, + "Please set server or client address\n"); + + if (lp.client_mode) + { + for (index = optind; index < argc; ++index) + { + ret = lp_args_test (argv[index]); + if (ret) + return ret; + } + ERR_RETURN (lp.test_num <= 0, -1, "Please set test\n"); + } + else + { + ERR_RETURN (optind < argc, -1, "Unknown option '%s'\n", argv[optind]); + } + + return 0; +} + +int +main (int argc, char *argv[]) +{ + if (argc <= 1) + { + lp_usage (argv[0]); + return 0; + } + + if (lp_init ()) + return 1; + + if (lp_args (argc, argv) == 0 && lp_start () == 0) + lp_loop (); + + lp_exit (); + + return 0; +} diff --git a/thirdparty/apps/testapp/lp/lp.h b/thirdparty/apps/testapp/lp/lp.h new file mode 100644 index 0000000..4a2a09a --- /dev/null +++ b/thirdparty/apps/testapp/lp/lp.h @@ -0,0 +1,615 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef _LP_H_ +#define _LP_H_ 1 + +#define LP_DBG 1 + +#if LP_DBG +#define LP_IF_DBG(line) line +#else +#define LP_IF_DBG(line) ((void)0) +#endif + +#define LP_ASSERT(cond) LP_IF_DBG(assert(cond)) + +#define LP_DELAY_MS 5 +#define LP_LOOP_TIMER (49 * 1000 * 1000) +#define LP_LOOP_REST (900 * 1000 * 1000) + +#define LP_UP_SLOT 1024 +#define LP_UP_NSEC (1000 * 1000) + +#define LP_DOWN_SLOT 1024 +#define LP_DOWN_NSEC (1000 * 1000) + +#define LP_MAX_FD (16 * 1024 * 1024) +#define LP_MAX_WORKER 64 +#define LP_MAX_TEST 10 + +#define LP_IOBUF_SIZE (128 * 1024) + +#define LP_EVENT_NUM 256 + +#define LP_SERVER_MAX 10000 +#define LP_CLIENT_MAX 100000 + +#define LP_INFINITE (0xFFFFffff) +#define LP_UNLIMITED LP_INFINITE +#define LP_VALUE_MAX (LP_INFINITE - 1) + +#define LP_INTERVAL_MIN 1 +#define LP_INTERVAL_MAX 60 +#define LP_INTERVAL_DEF 1 + +#define LP_TARGET_INF LP_INFINITE +#define LP_TARGET_MAX (16 * 1000 * 1000) /* 16m max */ +#define LP_TARGET_DEF LP_INFINITE + +#define LP_TIME_INF LP_INFINITE +#define LP_TIME_MAX (24 * 60 * 60) +#define LP_TIME_DEF LP_TIME_INF + +#define LP_UP_MAX ( 1 * 1000 * 1000) +#define LP_UP_INF LP_INFINITE +#define LP_UP_DEF 0 + +#define LP_DOWN_MAX (1 * 1000 * 1000) +#define LP_DOWN_INF LP_INFINITE +#define LP_DOWN_DEF 0 + +#define LP_QUERY_MIN 8 /* 2 unsigned int */ +#define LP_QUERY_MAX 65536 + +#define LP_REPLY_MAX (10 * 1024 * 1024) /* 10M */ + +#define LP_TIMES_INF LP_INFINITE +#define LP_TIMES_MAX LP_VALUE_MAX +#define LP_TIMES_DEF LP_TIMES_INF + +#define LP_PERIOD_MIN 1 +#define LP_PERIOD_MAX (60 * 60) +#define LP_PERIOD_DEF 1 + +#define LP_WAIT_MAX (60 * 60) +#define LP_WAIT_DEF 0 + +#define LP_FLAG_MASK (0xffffFFFFull << 32) +#define LP_TYPE_MASK (0x3ull << 32) +#define LP_CONTROL_TYPE (0x1ull << 32) +#define LP_LISTEN_TYPE (0x2ull << 32) +#define LP_SESSION_TYPE (0x3ull << 32) + +inline static uint64_t +LP_EV_MK (uint64_t flag, int fd) +{ + return flag | (uint64_t) (uint32_t) fd; +} + +inline static int +LP_EV_FD (uint64_t data_u64) +{ + return (int) (uint32_t) data_u64; +} + +inline static uint64_t +LP_EV_TYPE (uint64_t data_u64) +{ + return data_u64 & LP_TYPE_MASK; +} + +inline static uint64_t +LP_EV_FLAG (uint64_t data_u64) +{ + return data_u64 & LP_FLAG_MASK; +} + +#define LP_R_SIGN 1 +#define LP_E_SIGN 2 +#define LP_W_SIGN 4 +#define LP_ERR_SIGN 8 + +enum +{ + LP_CONNECTED, + LP_CONNECT, + LP_CLOSE, + LP_FAILED, + + LP_QUERY_COMP, + LP_QUERY_BYTE, + LP_REPLY_COMP, + LP_REPLY_BYTE, + + LP_REC_NUM, + + LP_R_END, + LP_E_BEGIN = LP_R_END, + + LP_E_SOCKET = LP_E_BEGIN, + LP_E_BIND, + LP_E_ACCEPT, + LP_E_CONNECT, + LP_E_NODELAY, + LP_E_NONBLOCK, + LP_E_REUSEADDR, + LP_E_REUSEPORT, + LP_E_RECV, + LP_E_SEND, + + LP_E_EPADD, + LP_E_EPMOD, + LP_E_EPDEL, + LP_E_EPWAIT, + LP_E_EPUNUSED, + LP_E_EPHUP, + LP_E_EPERR, + LP_E_EPINOUT, + LP_E_EVIDLE, + LP_E_EPEVENT, + + LP_E_IOSHUT, + LP_E_IOSIZE, + LP_E_IOMORE, + LP_E_IOEXCEED, + LP_E_IOSEND0, + + LP_E_END, + LP_W_BEGIN = LP_E_END, + + LP_W_CREATE = LP_W_BEGIN, LP_T_CREATE, + LP_W_SOCKET, LP_T_SOCKET, + LP_W_BIND, LP_T_BIND, + LP_W_CONNECT, LP_T_CONNECT, + LP_W_CONNECTED, LP_T_CONNECTED, + LP_W_ACCEPT, LP_T_ACCEPT, + LP_W_CLOSE, LP_T_CLOSE, + + LP_W_END, + + LP_CNT_NUM = LP_W_END +}; + +#define LP_ERRNO_NUM 256 +#define LP_NOERR_NUM ((LP_ERRNO_NUM + 63) / 64) + +struct lp_stat +{ + uint64_t cnt[LP_CNT_NUM]; + uint64_t err[LP_ERRNO_NUM]; +}; + +#define LP_STAT(worker) (lp.curr + (worker)->index) +#define LP_ADD(worker, id, num) (LP_STAT(worker)->cnt[(id)] += (num)) +#define LP_CNT(worker, id) (++LP_STAT(worker)->cnt[(id)]) +#define LP_ERR(worker, id, e) do { \ + unsigned int _e = (unsigned int)(e); \ + if (_e >= LP_ERRNO_NUM) \ + _e = 0; \ + if (0 == (lp.no_err[_e / 64] & (1 << (_e % 64)))) { \ + struct lp_stat *_stat = LP_STAT(worker); \ + _stat->cnt[(id)]++; \ + _stat->err[_e]++; \ + } \ +} while (0) +#define LP_CNT2(worker, id1, id2) do { \ + struct lp_stat *_stat = LP_STAT(worker); \ + _stat->cnt[(id1)]++; \ + _stat->cnt[(id2)]++; \ +} while (0) +#define LP_ERR2(worker, id1, id2, e) do { \ + unsigned int _e = (unsigned int)(e); \ + if (_e >= LP_ERRNO_NUM) \ + _e = 0; \ + if (0 == (lp.no_err[_e / 64] & (1 << (_e % 64)))) { \ + struct lp_stat *_stat = LP_STAT(worker); \ + _stat->cnt[(id1)]++; \ + _stat->cnt[(id2)]++; \ + _stat->err[_e]++; \ + } \ +} while (0) + +#define LP_TIME_SET(begin) struct timespec begin; \ + do { \ + if (lp.watch) \ + LB_TIME(begin); \ + } while (0) +#define LP_TIME_REG(worker, id, nsec) do { \ + if (lp.watch) { \ + uint64_t *_w = &(LP_STAT(worker)->cnt[(id)]); \ + _w[0] ++; \ + _w[1] += nsec; \ + } \ +} while (0) +#define LP_TIME_FOR(worker, id, begin, end) LP_TIME_REG((worker), (id), LB_SUB_NS((end), (begin))) +#define LP_TIME_END(worker, id, begin) do { \ + LP_TIME_SET(_end); \ + LP_TIME_FOR((worker), (id), (begin), _end); \ +} while (0) + +struct lp_io +{ + int query; + int reply; +} __attribute__ ((__packed__)); + +struct lp_head +{ + uint32_t num; + int first; + int *last; +}; + +enum +{ + LP_S_UNUSED = 0, + LP_S_PREPARE = 0x20, + LP_S_CONNECTING = 0x80, + + LP_S_CONNECTED = 0x10, + + LP_S_IDLE = LP_S_CONNECTED | 0, + LP_S_QUERY = LP_S_CONNECTED | 1, + LP_S_REPLY = LP_S_CONNECTED | 2, + LP_S_REST = LP_S_CONNECTED | 4, + LP_S_WAIT = LP_S_CONNECTED | 8, +}; + +struct lp_sess +{ + uint8_t state; + uint8_t test; + uint8_t epout; + uint32_t io_num; + int query; + int reply; + + int *prev_sess; + int next_sess; + int next_rest; + int *prev_rest; + struct timespec time; + +#if LP_DBG + uint16_t work; + uint16_t anum; + uint16_t fnum; +#endif +}; + +struct lp_test +{ + uint32_t target; + uint32_t time; + + uint32_t up; + uint32_t down; + + int query; + int reply; + + uint32_t times; + uint32_t period; + uint32_t wait; + + uint8_t down_mode; + uint8_t close_after_io; + uint8_t _pad[2]; +}; + +enum +{ + LP_SYNC, + LP_RAND, + LP_CPS, + LP_SPC, +}; + +struct lp_worker +{ + int index; + int epfd; + int ctlfd; + int server_num; + int client_num; + int link_mode; + + pthread_t tid; + + struct lp_head server; + struct lp_head sess; /* ESTABLISHED */ + struct lp_head conn; /* connecting(for nonblock of client) */ + struct lp_head rest[LP_MAX_TEST]; /* io wait queue */ + struct lp_head wait[LP_MAX_TEST]; /* io wait queue */ + + struct sockaddr_in *server_addr; + struct sockaddr_in *client_addr; + + struct lb_run *up_run; + struct lb_run *down_run; + + struct epoll_event *ev_buf; + + void *io_buf; +}; + +enum lp_state +{ + LP_EXIT = -1, + LP_INIT = 0, + LP_EXEC = 1, + LP_CLEAN = 2, +}; + +struct lp_var +{ + int CPU_NUM; + volatile int run_state; + int worker_num; + + uint8_t verbose; + uint8_t watch; + uint8_t err_msg; + uint8_t _pad; + + int client_mode; + int block_connecting; + + int interval; + int nodelay; + + int test_num; + volatile int test_id; + + uint64_t core; + uint64_t active_worker; + + struct lp_sess *sess; + struct lp_stat *volatile curr; + struct lp_stat *next; + struct lp_stat stat[2][LP_MAX_WORKER]; + struct lp_test test[LP_MAX_TEST]; + struct lp_worker worker[LP_MAX_WORKER]; + uint64_t no_err[LP_NOERR_NUM]; +}; + +extern struct lp_var lp; + +inline static struct lp_worker * +LP_WORKER (int index) +{ + return &lp.worker[index]; +} + +inline static struct lp_sess * +LP_SESS (int fd) +{ + return lp.sess + fd; +} + +inline static uint64_t +lp_total_sess () +{ + int i; + uint64_t total = 0; + for (i = 0; i < lp.worker_num; ++i) + total += lp.worker[i].sess.num; + return total; +} + +#define LP_APPEND(head, fd, sess, name) do { \ + struct lp_sess *_s = (sess); \ + LP_ASSERT(_s->next_##name == fd); \ + LP_ASSERT(_s->prev_##name == NULL); \ + _s->next_##name = -1; \ + _s->prev_##name = (head).last; \ + *(head).last = fd; \ + (head).last = &_s->next_##name; \ + (head).num++; \ +} while (0) + +#define LP_REMOVE(head, fd, sess, name) do { \ + struct lp_sess *_s = (sess); \ + LP_ASSERT((head).num); \ + if ((*_s->prev_##name = _s->next_##name) >= 0) \ + LP_SESS(_s->next_##name)->prev_##name = _s->prev_##name; \ + else \ + (head).last = _s->prev_##name; \ + (head).num--; \ + LP_IF_DBG(_s->next_##name = (fd)); \ + LP_IF_DBG(_s->prev_##name = NULL); \ +} while (0) + +inline static void +lp_init_head (struct lp_head *head) +{ + head->first = -1; + head->num = 0; + head->last = &head->first; +} + +inline static void +lp_dest_sess (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + LP_ASSERT (sess->next_rest == fd); + LP_ASSERT (sess->next_sess == fd); + LP_ASSERT (sess->prev_rest == NULL); + LP_ASSERT (sess->prev_sess == NULL); + LP_ASSERT (sess->work == worker->index); +#if LP_DBG + sess->fnum++; +#endif + LP_ASSERT (sess->fnum == sess->anum); + + sess->state = LP_S_UNUSED; +} + +inline static struct lp_sess * +lp_init_sess (struct lp_worker *worker, int fd) +{ + struct lp_sess *sess = LP_SESS (fd); + + if (sess->state != LP_S_UNUSED) + { + void lp_dump_sess (int fd); + wrn ("Invalid session fd:%d\n", fd); +#if LP_DBG + lp_dump_sess (fd); +#endif + LP_ASSERT (0); + } + LP_ASSERT (sess->fnum == sess->anum); + + sess->state = LP_S_PREPARE; + sess->epout = 0; + sess->io_num = 0; + sess->query = 0; + sess->reply = 0; + +#if LP_DBG + sess->work = worker->index; + sess->anum++; + sess->next_rest = sess->next_sess = fd; + sess->prev_rest = sess->prev_sess = NULL; +#endif + + return sess; +} + +inline static void +lp_add_conn (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + LP_ASSERT (sess->state == LP_S_PREPARE); + + LP_APPEND (worker->conn, fd, sess, sess); + sess->state = LP_S_CONNECTING; +} + +inline static void +lp_out_conn (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + LP_ASSERT (worker->conn.num); + LP_ASSERT (sess->state == LP_S_CONNECTING); + + LP_REMOVE (worker->conn, fd, sess, sess); +} + +inline static void +lp_del_conn (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + lp_out_conn (worker, fd, sess); + lp_dest_sess (worker, fd, sess); + _close (fd); +} + +inline static void +lp_add_rest (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + LP_APPEND (worker->rest[sess->test], fd, sess, rest); + sess->state = LP_S_REST; +} + +inline static void +lp_out_rest (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + LP_ASSERT (worker->rest[sess->test].num); + + LP_REMOVE (worker->rest[sess->test], fd, sess, rest); + sess->state = LP_S_IDLE; +} + +inline static void +lp_add_wait (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + LP_APPEND (worker->wait[sess->test], fd, sess, rest); + sess->state = LP_S_WAIT; +} + +inline static void +lp_out_wait (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + LP_ASSERT (worker->wait[sess->test].num); + + LP_REMOVE (worker->wait[sess->test], fd, sess, rest); + sess->state = LP_S_IDLE; +} + +inline static void +lp_del_sess (struct lp_worker *worker, int fd) +{ + struct lp_sess *sess = LP_SESS (fd); + + LP_ASSERT (sess->state & LP_S_CONNECTED); + + if (sess->state == LP_S_REST) + lp_out_rest (worker, fd, sess); + else if (sess->state == LP_S_WAIT) + lp_out_wait (worker, fd, sess); + LP_REMOVE (worker->sess, fd, sess, sess); + + lp_dest_sess (worker, fd, sess); + LP_TIME_SET (begin); + _close (fd); + LP_TIME_END (worker, LP_W_CLOSE, begin); + + LP_CNT (worker, LP_CLOSE); +} + +inline static int +lp_epctl (const struct lp_worker *worker, int op, int fd, uint32_t io) +{ + struct epoll_event event; + event.events = io | EPOLLET | EPOLLRDHUP | EPOLLHUP; + event.data.u64 = LP_EV_MK (LP_SESSION_TYPE, fd); + return _epoll_ctl (worker->epfd, op, fd, &event); +} + +#define lp_epadd(worker, fd, io) lp_epctl((worker), EPOLL_CTL_ADD, (fd), (io)) +#define lp_epmod(worker, fd, io) lp_epctl((worker), EPOLL_CTL_MOD, (fd), (io)) +#define lp_epdel(worker, fd) _epoll_ctl((worker->epfd), EPOLL_CTL_DEL, (fd), NULL) + +inline static int +lp_set_epout (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + if (sess->epout) + return 1; + + if (lp_epmod (worker, fd, EPOLLOUT)) + { + LP_ERR (worker, LP_E_EPMOD, errno); + return -1; + } + + sess->epout = 1; + return 1; +} + +#define LP_CMD_STOP 1 + +inline static int +lp_post_cmd (int fd, long long int cmd) +{ + ssize_t ret = _write (fd, (void *) &cmd, sizeof (cmd)); + + return ret - sizeof (cmd); +} + +void lp_client (struct lp_worker *worker); +void lp_server (struct lp_worker *worker); +int lp_listen (struct lp_worker *worker, int index); + +#endif /* #ifndef _LP_H_ */ diff --git a/thirdparty/apps/testapp/lp/lpc.c b/thirdparty/apps/testapp/lp/lpc.c new file mode 100644 index 0000000..7da0414 --- /dev/null +++ b/thirdparty/apps/testapp/lp/lpc.c @@ -0,0 +1,669 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" +#include "lp.h" + +inline static int +lp_io_finish (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + int ret; + + sess->state = LP_S_IDLE; + --sess->io_num; + + if (sess->io_num) + { + lp_add_rest (worker, fd, sess); + sess->time.tv_sec += lp.test[sess->test].period; + return 0; + } + + if (lp.test[sess->test].close_after_io) + return -1; + + if (lp_epdel (worker, fd)) + LP_ERR (worker, LP_E_EPDEL, errno); + + return 0; +} + +int +lp_recv_reply (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + int ret; + char *buf = worker->io_buf; + + LP_ASSERT (sess->query == 0); + + while (1) + { + ret = _recv (fd, buf, LP_IOBUF_SIZE, 0); + if (ret > 0) + { + LP_ADD (worker, LP_REPLY_BYTE, ret); + sess->reply -= ret; + } + else if (ret < 0) + { + int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN) + { + if (sess->reply > 0) + return 1; + break; + } + if (e == EINTR) + continue; + LP_ERR (worker, LP_E_RECV, e); + return -1; + } + else + { + if (sess->reply) + LP_CNT (worker, LP_E_IOSHUT); + else + LP_CNT (worker, LP_REPLY_COMP); + return -1; + } + } + + LP_CNT (worker, LP_REPLY_COMP); + + if (sess->reply < 0) + { + LP_CNT (worker, LP_E_IOEXCEED); + sess->reply = 0; + } + + return lp_io_finish (worker, fd, sess); +} + +int +lp_more_query (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + int ret; + char *buf = worker->io_buf; + + LP_ASSERT (sess->io_num); + + while (sess->query > 0) + { + const int LEN = + sess->query < LP_IOBUF_SIZE ? sess->query : LP_IOBUF_SIZE; + + ret = _send (fd, buf, LEN, 0); + if (ret > 0) + { + LP_ADD (worker, LP_QUERY_BYTE, ret); + sess->query -= ret; + } + else if (ret < 0) + { + int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN) + return lp_set_epout (worker, fd, sess); + if (e == EINTR) + continue; + LP_ERR (worker, LP_E_SEND, e); + return -1; + } + else + { + LP_CNT (worker, LP_E_IOSEND0); + } + } + + LP_CNT (worker, LP_QUERY_COMP); + + if (sess->epout) + { + if (lp_epmod (worker, fd, EPOLLIN)) + { + LP_ERR (worker, LP_E_EPMOD, errno); + return -1; + } + sess->epout = 0; + } + + if (!sess->reply) + { + return lp_io_finish (worker, fd, sess); + } + + sess->state = LP_S_REPLY; + return 0; +} + +int +lp_new_query (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + int ret, len = 0; + char *buf = worker->io_buf; + struct lp_io *io = (struct lp_io *) buf; + + LP_ASSERT (sess->state == LP_S_IDLE); + LP_ASSERT (sess->query == 0); + LP_ASSERT (sess->reply == 0); + LP_ASSERT (sess->io_num); + + sess->state = LP_S_QUERY; + sess->query = lp.test[sess->test].query; + sess->reply = lp.test[sess->test].reply; + + io->query = htonl (sess->query); + io->reply = htonl (sess->reply); + + while (1) + { + ret = _send (fd, buf + len, sess->query - len, 0); + if (ret > 0) + { + LP_ADD (worker, LP_QUERY_BYTE, ret); + len += ret; + if (len >= sizeof (struct lp_io)) + break; + LP_CNT (worker, LP_E_IOMORE); + } + else if (ret < 0) + { + int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN) + { + LP_CNT (worker, LP_E_IOMORE); + continue; + } + if (e == EINTR) + continue; + LP_ERR (worker, LP_E_SEND, e); + return -1; + } + else + { + LP_CNT (worker, LP_E_IOSEND0); + } + } + + sess->query -= len; + + return lp_more_query (worker, fd, sess); +} + +int +lp_pre_connect (struct lp_worker *worker, int fd, struct sockaddr_in *c_addr) +{ + int ret; + + if (fd >= LP_MAX_FD) + { + LP_CNT (worker, LP_FAILED); + err ("fd(%d) >= LP_MAX_FD(%d)\n", fd, LP_MAX_FD); + return -1; + } + + ret = set_reuseaddr (fd, 1); + if (ret) + { + const int e = errno; + LP_ERR (worker, LP_E_REUSEADDR, e); + DBG ("set_reuseaddr(%d, 1)=%d:%d\n", fd, ret, e); + } + + ret = set_reuseport (fd, 1); + if (ret) + { + const int e = errno; + LP_ERR (worker, LP_E_REUSEPORT, errno); + DBG ("set_reuseport(%d, 1)=%d:%d\n", fd, ret, e); + } + + if (c_addr->sin_addr.s_addr != INADDR_ANY || c_addr->sin_port != 0) + { + LP_TIME_SET (bind_begin); + ret = + _bind (fd, (struct sockaddr *) c_addr, sizeof (struct sockaddr_in)); + LP_TIME_END (worker, LP_W_BIND, bind_begin); + if (ret) + { + int e = errno; + if (e == EADDRINUSE) + return -1; + LP_ERR2 (worker, LP_FAILED, LP_E_BIND, errno); + DBG ("->bind(%d, %s)=%d:%d\n", fd, f_inaddr (c_addr), ret, errno); + return -1; + } + } + + if (lp.nodelay) + { + ret = set_nodelay (fd, 1); + if (ret) + LP_ERR2 (worker, LP_FAILED, LP_E_NODELAY, errno); + } + + if (!lp.block_connecting) + { + ret = set_nonblock (fd); + if (ret) + { + LP_ERR2 (worker, LP_FAILED, LP_E_NONBLOCK, errno); + return -1; + } +#if 1 + ret = lp_epadd (worker, fd, EPOLLOUT); + if (ret) + { + LP_ERR2 (worker, LP_FAILED, LP_E_EPADD, errno); + DBG ("->epoll_ctl(%d, ADD, %d)=%d:%d", worker->epfd, fd, ret, + errno); + return -1; + } + return 1; +#endif + } + + return 0; +} + +int +lp_connected (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + struct timespec now; + struct lp_test *test; + + LB_TIME (now); + LP_TIME_FOR (worker, LP_W_CONNECTED, sess->time, now); + sess->time = now; + + LP_APPEND (worker->sess, fd, sess, sess); + sess->state = LP_S_CONNECTED; + LP_CNT (worker, LP_CONNECTED); + + test = &lp.test[sess->test]; + if (test->query) + { + if (lp.block_connecting && set_nonblock (fd)) + { + LP_ERR (worker, LP_E_NONBLOCK, errno); + return -1; + } + if (sess->epout) + { + if (lp_epmod (worker, fd, EPOLLIN)) + { + LP_ERR (worker, LP_E_EPMOD, errno); + return -1; + } + sess->epout = 0; + } + else + { + if (lp_epadd (worker, fd, EPOLLIN)) + { + LP_ERR (worker, LP_E_EPADD, errno); + return -1; + } + } + sess->io_num = test->times; + + if (test->wait) + { + sess->time.tv_sec += test->wait; + lp_add_wait (worker, fd, sess); + return 0; + } + + return lp_new_query (worker, fd, sess); + } + + if (sess->epout) + { + if (lp_epdel (worker, fd)) + LP_ERR (worker, LP_E_EPDEL, errno); + sess->epout = 0; + } + + return 0; +} + +int +lp_connect (struct lp_worker *worker, int cid, int sid, int test_id) +{ + int ret, fd; + struct lp_sess *sess; + struct timespec connect_begin; + + LP_TIME_SET (begin); + fd = _socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd < 0) + { + LP_ERR2 (worker, LP_FAILED, LP_E_SOCKET, errno); + DBG ("->socket(...)=%d:%d\n", fd, errno); + return -1; + } + LP_TIME_END (worker, LP_W_SOCKET, begin); + + ret = lp_pre_connect (worker, fd, worker->client_addr + cid); + if (ret < 0) + { + _close (fd); + return -1; + } + + LB_TIME (connect_begin); + ret = + _connect (fd, (struct sockaddr *) (worker->server_addr + sid), + sizeof (struct sockaddr_in)); + LP_TIME_END (worker, LP_W_CONNECT, connect_begin); + + if (ret) + { + const int e = errno; + if (lp.block_connecting || e != EINPROGRESS) + { + LP_ERR2 (worker, LP_FAILED, LP_E_CONNECT, e); + DBG ("->connect(%d, %s)=%d:%d\n", fd, + f_inaddr (worker->server_addr + sid), ret, e); + _close (fd); + return -1; + } + } + + LP_TIME_END (worker, LP_W_CREATE, begin); + LP_CNT (worker, LP_CONNECT); + + sess = lp_init_sess (worker, fd); + sess->test = test_id; + sess->time = connect_begin; + if (!lp.block_connecting) + sess->epout = 1; + + if (ret) + { + /* nonblock connect inprogress */ + lp_add_conn (worker, fd, sess); + return 1; + } + + ret = lp_connected (worker, fd, sess); + if (ret < 0) + lp_del_sess (worker, fd); + + return ret; +} + +inline static int +lp_handle_client (struct lp_worker *worker, int fd, uint32_t events) +{ + struct lp_sess *sess = LP_SESS (fd); + + if (sess->state == LP_S_UNUSED) + { + LP_CNT (worker, LP_E_EPUNUSED); + return 0; + } + + if (events & EPOLLRDHUP) + { + LP_CNT (worker, LP_E_EPHUP); + return -1; + } + + if (events & EPOLLERR) + { + LP_CNT (worker, LP_E_EPERR); + DBG ("epoll event error, fd:%d event:0x%x\n", fd, events); + return -1; + } + + if (events & EPOLLIN) + { + if (sess->state == LP_S_REPLY) + return lp_recv_reply (worker, fd, sess); + LP_CNT (worker, LP_E_EPERR); + return 0; + } + + if (events & EPOLLOUT) + { + if (sess->state == LP_S_CONNECTING) + { + lp_out_conn (worker, fd, sess); + return lp_connected (worker, fd, sess); + } + if (sess->state == LP_S_QUERY) + return lp_more_query (worker, fd, sess); + LP_CNT (worker, LP_E_EPERR); + return 0; + } + + LP_CNT (worker, LP_E_EPEVENT); + return -1; +} + +inline static void +lp_init_mode (const struct lp_worker *worker, int *cid, int *sid) +{ + if (worker->link_mode == LP_RAND) + { + *cid = LB_RAND (worker->client_num); + *sid = LB_RAND (worker->server_num); + } + else + { + *cid = 0; + *sid = 0; + } +} + +inline static void +lp_next_mode (const struct lp_worker *worker, int *cid, int *sid) +{ + if (worker->link_mode == LP_RAND) + { + *cid = LB_RAND (worker->client_num); + *sid = LB_RAND (worker->server_num); + } + else if (worker->link_mode == LP_SYNC) + { + if (++*sid >= worker->server_num) + *sid = 0; + if (++*cid >= worker->client_num) + *cid = 0; + } + else if (worker->link_mode == LP_CPS) + { + if (++*cid >= worker->client_num) + { + *cid = 0; + if (++*sid >= worker->server_num) + *sid = 0; + } + } + else if (worker->link_mode == LP_SPC) + { + if (++*sid >= worker->server_num) + { + *sid = 0; + if (++*cid >= worker->client_num) + *cid = 0; + } + } + else + { + err ("Error mode value:%d\n", worker->link_mode); + } +} + +void +lp_client (struct lp_worker *worker) +{ + int ret, num = 0, cid, sid, test_id = -1; + struct epoll_event *event; + struct lp_test *test = &lp.test[0]; + struct lb_run *up = worker->up_run; + struct lb_run *down = worker->down_run; + const int epfd = worker->epfd; + + lp_init_mode (worker, &cid, &sid); + + while (lp.run_state == LP_EXEC) + { + int i; + struct timespec now; + + if (test_id != lp.test_id) + { + DBG ("worker %d change test_id %d\n", worker->index, lp.test_id); + test = &lp.test[test_id = lp.test_id]; + + if (test->up) + run_init (up, (test->up + lp.worker_num - 1) / lp.worker_num, + LP_UP_SLOT, LP_UP_NSEC); + if (test->down) + run_init (down, (test->down + lp.worker_num - 1) / lp.worker_num, + LP_DOWN_SLOT, LP_DOWN_NSEC); + } + + LB_TIME (now); + + /* up process */ + if (test->up) + { + if (run_test (up, &now) > 0) + { + ret = lp_connect (worker, cid, sid, test_id); + if (ret >= 0) + run_add (up, 1); + lp_next_mode (worker, &cid, &sid); + LB_TIME (now); + } + } + + /* down process */ + if (test->down && worker->sess.num) + { + if (run_test (down, &now) > 0) + { + lp_del_sess (worker, worker->sess.first); + run_add (down, 1); + LB_TIME (now); + } + } + + /* query */ + for (i = 0; i <= test_id; ++i) + { + int fd; + struct lp_sess *sess; + + if (worker->rest[i].num) + { + fd = worker->rest[i].first; + sess = LP_SESS (fd); + if (LB_CMP (now, sess->time) >= 0) + { + LP_ASSERT (sess->state == LP_S_REST); + LP_ASSERT (sess->test == i); + lp_out_rest (worker, fd, sess); + if (lp_new_query (worker, fd, sess) < 0) + { + lp_del_sess (worker, fd); + if (test->down) + run_add (down, 1); + } + LB_TIME (now); + } + } + if (worker->wait[i].num) + { + fd = worker->wait[i].first; + sess = LP_SESS (fd); + if (LB_CMP (now, sess->time) >= 0) + { + LP_ASSERT (sess->state == LP_S_WAIT); + LP_ASSERT (sess->test == i); + lp_out_wait (worker, fd, sess); + if (lp_new_query (worker, fd, sess) < 0) + { + lp_del_sess (worker, fd); + if (test->down) + run_add (down, 1); + } + LB_TIME (now); + } + } + } + + /* check connect timeout */ + if (worker->conn.first >= 0) + { + } + + /* epoll event process */ + if (num > 0) + { + const uint64_t type = LP_EV_TYPE (event->data.u64); + const int fd = LP_EV_FD (event->data.u64); + + if (type == LP_SESSION_TYPE) + { + if (lp_handle_client (worker, fd, event->events) < 0) + { + struct lp_sess *sess = LP_SESS (fd); + if (sess->state & LP_S_CONNECTED) + { + lp_del_sess (worker, fd); + if (test->down) + run_add (down, 1); + } + else if (sess->state == LP_S_CONNECTING) + { + lp_del_conn (worker, fd, sess); + } + else + { + } + } + } + else if (type == LP_CONTROL_TYPE) + { + break; + } + else + { + err ("epoll event error flag:%lx, fd:%d event:%x}\n", + LP_EV_FLAG (event->data.u64), fd, event->events); + } + num--; + event++; + } + else + { + num = _epoll_wait (epfd, worker->ev_buf, LP_EVENT_NUM, 0); + if (num > 0) + { + event = worker->ev_buf; + } + else if (num < 0) + { + int e = errno; + if (e != EINTR && e != ETIMEDOUT) + LP_ERR (worker, LP_E_EPWAIT, e); + } + } + } +} diff --git a/thirdparty/apps/testapp/lp/lps.c b/thirdparty/apps/testapp/lp/lps.c new file mode 100644 index 0000000..8462c21 --- /dev/null +++ b/thirdparty/apps/testapp/lp/lps.c @@ -0,0 +1,372 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" +#include "lp.h" + +int +lp_send_reply (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + int ret; + char *buf = worker->io_buf; + + while (sess->reply > 0) + { + const int LEN = + sess->reply < LP_IOBUF_SIZE ? sess->reply : LP_IOBUF_SIZE; + + ret = _send (fd, buf, LEN, 0); + if (ret > 0) + { + LP_ADD (worker, LP_REPLY_BYTE, ret); + sess->reply -= ret; + } + else if (ret < 0) + { + int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN) + return lp_set_epout (worker, fd, sess); + if (e == EINTR) + continue; + LP_ERR (worker, LP_E_SEND, e); + return -1; + } + else + { + LP_CNT (worker, LP_E_IOSEND0); + } + } + + LP_CNT (worker, LP_REPLY_COMP); + + if (sess->epout) + { + if (lp_epmod (worker, fd, EPOLLIN)) + { + LP_ERR (worker, LP_E_EPMOD, errno); + return -1; + } + sess->epout = 0; + } + + return 0; +} + +int +lp_just_query (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + char *buf = worker->io_buf; + struct lp_io *io = (struct lp_io *) buf; + int len = 0; + + while (1) + { + int ret; + + ret = _recv (fd, buf + len, LP_IOBUF_SIZE - len, 0); + + if (ret > 0) + { + LP_ADD (worker, LP_QUERY_BYTE, ret); + len += ret; + if (len >= sizeof (struct lp_io)) + break; + LP_CNT (worker, LP_E_IOMORE); + } + else if (ret < 0) + { + int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN) + { + LP_CNT (worker, LP_E_IOMORE); + continue; + } + if (e == EINTR) + continue; + LP_ERR (worker, LP_E_RECV, e); + return -1; + } + else + { + //LP_CNT(worker, LP_E_IOSHUT); + return -1; + } + } + + sess->query = htonl (io->query); + sess->reply = htonl (io->reply); + + if (sess->query < LP_QUERY_MIN || sess->reply < 0) + { + LP_CNT (worker, LP_E_IOSIZE); + return -1; + } + + sess->query -= len; + return 0; +} + +int +lp_recv_query (struct lp_worker *worker, int fd, struct lp_sess *sess) +{ + int ret; + char *buf = worker->io_buf; + + if (sess->query == 0) + { + ret = lp_just_query (worker, fd, sess); + if (ret) + return ret; + } + + while (1) + { + ret = _recv (fd, buf, LP_IOBUF_SIZE, 0); + if (ret > 0) + { + LP_ADD (worker, LP_QUERY_BYTE, ret); + sess->query -= ret; + } + else if (ret < 0) + { + int e = errno; + if (e == EWOULDBLOCK || e == EAGAIN) + { + if (sess->query > 0) + return 1; + break; + } + if (e == EINTR) + continue; + LP_ERR (worker, LP_E_RECV, e); + return -1; + } + else + { + if (sess->query || sess->reply) + { + //LP_CNT(worker, LP_E_IOSHUT); + } + else + LP_CNT (worker, LP_QUERY_COMP); + return -1; + } + } + + LP_CNT (worker, LP_QUERY_COMP); + + if (sess->query < 0) + { + LP_CNT (worker, LP_E_IOEXCEED); + sess->query = 0; + } + + if (sess->reply) + return lp_send_reply (worker, fd, sess); + + return 0; +} + +inline static int +lp_service (struct lp_worker *worker, int fd, uint32_t events) +{ + struct lp_sess *sess = LP_SESS (fd); + + if (sess->state == LP_S_UNUSED) + { + LP_CNT (worker, LP_E_EPUNUSED); + return 0; + } + + if (events & EPOLLRDHUP) + return -1; + + if (events & EPOLLERR) + { + LP_CNT (worker, LP_E_EPERR); + DBG ("epoll event error, fd:%d event:0x%x\n", fd, events); + return -1; + } + + if ((events & (EPOLLIN | EPOLLOUT)) == (EPOLLIN | EPOLLOUT)) + { + LP_CNT (worker, LP_E_EPINOUT); + return -1; + } + + if (events & EPOLLIN) + return lp_recv_query (worker, fd, sess); + + if (events & EPOLLOUT) + return lp_send_reply (worker, fd, sess); + + LP_CNT (worker, LP_E_EPEVENT); + return -1; +} + +void +lp_accept (struct lp_worker *worker, int listen_fd) +{ + while (lp.run_state == LP_EXEC) + { + int fd, ret; + struct lp_sess *sess; + + LP_TIME_SET (begin); + + fd = _accept4 (listen_fd, NULL, NULL, SOCK_NONBLOCK); + if (fd < 0) + { + int e = errno; + if (e == EAGAIN || e == EWOULDBLOCK) + return; + if (e == EINTR) + continue; + LP_ERR (worker, LP_E_ACCEPT, e); + DBG ("->accept4(%d)=%d:%d\n", listen_fd, fd, e); + return; + } + else + { + LP_TIME_END (worker, LP_W_ACCEPT, begin); + } + + if (fd >= LP_MAX_FD) + { + LP_CNT (worker, LP_FAILED); + _close (fd); + err ("accept fd(%d) >= LP_MAX_FD(%d)\n", fd, LP_MAX_FD); + continue; + } + + if (lp.nodelay) + { + ret = set_nodelay (fd, 1); + if (ret) + LP_ERR (worker, LP_E_NODELAY, errno); + } + + ret = lp_epadd (worker, fd, EPOLLIN); + if (ret) + { + int e = errno; + LP_ERR2 (worker, LP_FAILED, LP_E_EPADD, e); + _close (fd); + DBG ("epoll_ctl(%d, %d)=%d:%d\n\n", worker->epfd, fd, ret, e); + continue; + } + + sess = lp_init_sess (worker, fd); + LP_APPEND (worker->sess, fd, sess, sess); + sess->state = LP_S_CONNECTED; + LP_CNT (worker, LP_CONNECTED); + + LP_TIME_END (worker, LP_W_CREATE, begin); + } +} + +void +lp_server (struct lp_worker *worker) +{ + int num = 0; + struct epoll_event *event; + const int epfd = worker->epfd; + + while (lp.run_state == LP_EXEC) + { + if (num > 0) + { + const uint64_t type = LP_EV_TYPE (event->data.u64); + const int fd = LP_EV_FD (event->data.u64); + + if (type == LP_LISTEN_TYPE) + { + lp_accept (worker, fd); + } + else if (type == LP_SESSION_TYPE) + { + if (lp_service (worker, fd, event->events) < 0) + lp_del_sess (worker, fd); + } + else if (type == LP_CONTROL_TYPE) + { + break; + } + else + { + err ("epoll event error flag:%lx, fd:%d event:%x}\n", + LP_EV_FLAG (event->data.u64), fd, event->events); + } + num--; + event++; + } + else + { + num = _epoll_wait (epfd, worker->ev_buf, LP_EVENT_NUM, -1); + if (num > 0) + { + event = worker->ev_buf; + } + else if (num < 0) + { + int e = errno; + if (e != EINTR && e != ETIMEDOUT) + LP_ERR (worker, LP_E_EPWAIT, e); + } + } + } +} + +int +lp_listen (struct lp_worker *worker, int index) +{ + int fd, ret; + struct epoll_event event; + + fd = _socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (fd < 0, -1, "socket()=%d:%d\n", fd, errno); + + if (fd >= LP_MAX_FD) + { + err ("socket()=%d >= LP_MAX_FD(%d)\n", fd, LP_MAX_FD); + (void) _close (fd); + return -1; + } + + LP_APPEND (worker->server, fd, lp_init_sess (worker, fd), sess); + + ret = + _bind (fd, (struct sockaddr *) &worker->server_addr[index], + sizeof (worker->server_addr[index])); + ERR_RETURN (ret, -1, "bind(%d, %s)=%d:%d\n", fd, + f_inaddr (&worker->server_addr[index]), ret, errno); + + ret = set_nonblock (fd); + ERR_RETURN (ret, -1, "set_nonblock(%d)=%d:%d\n", fd, ret, errno); + + ret = _listen (fd, SOMAXCONN); + ERR_RETURN (ret, -1, "listen(%d)=%d:%d\n", fd, ret, errno); + + event.events = EPOLLIN | EPOLLET; + event.data.u64 = LP_EV_MK (LP_LISTEN_TYPE, fd); + ret = _epoll_ctl (worker->epfd, EPOLL_CTL_ADD, fd, &event); + ERR_RETURN (ret, -1, "epoll_ctl(%d, %d)=%d:%d\n", worker->epfd, fd, ret, + errno); + + DBG ("worker %d server %d fd %d listen on %s\n", worker->index, index, fd, + f_inaddr (&worker->server_addr[index])); + return 0; +} diff --git a/thirdparty/apps/testapp/te/te.c b/thirdparty/apps/testapp/te/te.c new file mode 100644 index 0000000..4f17d29 --- /dev/null +++ b/thirdparty/apps/testapp/te/te.c @@ -0,0 +1,782 @@ +/* +* +* Copyright (c) 2018 Huawei Technologies Co.,Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "lb.h" + +#define TEST_ASSERT(cond) do { if(!(cond)) err("%s\n", #cond); } while(0) + +int +test_v6_udp (int argc, const char *argv[]) +{ + int fd, ret; + struct sockaddr_in6 addr = { 0 }; + struct sockaddr_in6 out = { 0 }; + socklen_t len = sizeof (out); + const char *ip = argv[1]; + + fd = _socket (AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + ERR_RETURN (fd < 0, -1, "socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)=%d:%d\n", + fd, errno); + + ret = inet_pton (AF_INET6, ip, &addr.sin6_addr); + ERR_GOTO (ret != 1, CLEAN, "inet_pton(AF_INET6, %s)=%d:%d\n", ip, ret, + errno); + + addr.sin6_family = AF_INET6; + addr.sin6_port = htons (54321); + ret = _bind (fd, (struct sockaddr *) &addr, sizeof (addr)); + ERR_GOTO (ret < 0, CLEAN, "bind(%d, %s, %ld)=%d:%d\n", fd, + f_in6addr (&addr), sizeof (addr), ret, errno); + + ret = _getsockname (fd, (struct sockaddr *) &out, &len); + ERR_GOTO (ret < 0, CLEAN, "getsockname(%d, %s, %d)=%d:%d\n", fd, + f_in6addr (&out), len, ret, errno); + TEST_ASSERT (out.sin6_family == AF_INET6); + TEST_ASSERT (out.sin6_addr.s6_addr32[0] == addr.sin6_addr.s6_addr32[0]); + TEST_ASSERT (out.sin6_addr.s6_addr32[1] == addr.sin6_addr.s6_addr32[1]); + TEST_ASSERT (out.sin6_addr.s6_addr32[2] == addr.sin6_addr.s6_addr32[2]); + TEST_ASSERT (out.sin6_addr.s6_addr32[3] == addr.sin6_addr.s6_addr32[3]); + TEST_ASSERT (out.sin6_port == addr.sin6_port); + + ret = _close (fd); + if (ret) + err ("close(%d)=%d:%d\n", fd, ret, errno); + + return ret; + +CLEAN: + (void) _close (fd); + return -1; +} + +int +test_v4_udp (int argc, const char *argv[]) +{ + int fd, ret; + struct sockaddr_in addr = { 0 }; + struct sockaddr_in out = { 0 }; + socklen_t len = sizeof (out); + const char *ip = argv[1]; + + fd = _socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + ERR_RETURN (fd < 0, fd, "socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)=%d:%d\n", + fd, errno); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr (ip); + addr.sin_port = htons (12345); + ret = _bind (fd, (struct sockaddr *) &addr, sizeof (addr)); + ERR_GOTO (ret < 0, CLEAN, "bind(%d, %s, %ld)=%d:%d\n", fd, f_inaddr (&addr), + sizeof (addr), ret, errno); + + ret = _getsockname (fd, (struct sockaddr *) &out, &len); + ERR_GOTO (ret < 0, CLEAN, "getsockname(%d, %s, %d)=%d:%d\n", fd, + f_inaddr (&out), len, ret, errno); + TEST_ASSERT (out.sin_family == AF_INET); + TEST_ASSERT (out.sin_addr.s_addr == addr.sin_addr.s_addr); + TEST_ASSERT (out.sin_port == addr.sin_port); + + ret = _close (fd); + if (ret) + err ("close(%d)=%d:%d\n", fd, ret, errno); + + return ret; + +CLEAN: + (void) _close (fd); + return -1; +} + +int +test_v4_tcp (int argc, const char *argv[]) +{ + int sfd, cfd, afd, ret; + struct sockaddr_in saddr = { 0 }, caddr = + { + 0}, aaddr = + { + 0}, out = + { + 0}; + socklen_t len; + const char *ip = argv[1]; + + sfd = _socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (sfd < 0, sfd, + "sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", sfd, + errno); + + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = inet_addr (ip); + saddr.sin_port = htons (23456); + ret = _bind (sfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN_S, "bind(%d, %s, %ld)=%d:%d\n", sfd, + f_inaddr (&saddr), sizeof (saddr), ret, errno); + + ret = _listen (sfd, 100); + ERR_GOTO (ret < 0, CLEAN_S, "listen(%d, 100)=%d:%d\n", sfd, ret, errno); + + cfd = _socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + ERR_GOTO (cfd < 0, CLEAN_S, + "cfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", cfd, + errno); + + ret = _connect (cfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN_C, "connect(%d, %s, %ld)=%d:%d\n", cfd, + f_inaddr (&saddr), sizeof (saddr), ret, errno); + + len = sizeof (caddr); + ret = _getsockname (cfd, (struct sockaddr *) &caddr, &len); + TEST_ASSERT (ret == 0); + TEST_ASSERT (len == sizeof (caddr)); + + len = sizeof (aaddr); + afd = _accept (sfd, (struct sockaddr *) &aaddr, &len); + ERR_GOTO (ret < 0, CLEAN_C, "accept(%d, %s, %d)=%d:%d\n", sfd, + f_inaddr (&aaddr), len, ret, errno); + TEST_ASSERT (len == sizeof (caddr)); + + len = sizeof (out); + ret = _getsockname (afd, (struct sockaddr *) &out, &len); + TEST_ASSERT (ret == 0); + TEST_ASSERT (len == sizeof (out)); + TEST_ASSERT (out.sin_family == AF_INET); + TEST_ASSERT (out.sin_addr.s_addr == saddr.sin_addr.s_addr); + TEST_ASSERT (out.sin_port == saddr.sin_port); + + len = sizeof (out); + ret = _getpeername (afd, (struct sockaddr *) &out, &len); + TEST_ASSERT (ret == 0); + TEST_ASSERT (len == sizeof (out)); + TEST_ASSERT (out.sin_family == AF_INET); + TEST_ASSERT (out.sin_addr.s_addr == caddr.sin_addr.s_addr); + TEST_ASSERT (out.sin_port == caddr.sin_port); + + ret = _close (afd); + TEST_ASSERT (ret == 0); + + ret = _close (cfd); + TEST_ASSERT (ret == 0); + + ret = _close (sfd); + TEST_ASSERT (ret == 0); + + return ret; + +CLEAN: + (void) _close (afd); +CLEAN_C: + (void) _close (cfd); +CLEAN_S: + (void) _close (sfd); + return -1; +} + +int +v6_udp_close_select (int argc, const char *argv[]) +{ + int fd, ret; + struct sockaddr_in6 addr = { 0 }; + struct sockaddr_in6 out = { 0 }; + socklen_t len = sizeof (out); + const char *ip = argv[1]; + const char *port = argv[2]; + + fd = _socket (AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + ERR_RETURN (fd < 0, -1, "socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)=%d:%d\n", + fd, errno); + + ret = inet_pton (AF_INET6, ip, &addr.sin6_addr); + ERR_GOTO (ret != 1, CLEAN, "inet_pton(AF_INET6, %s)=%d:%d\n", ip, ret, + errno); + + addr.sin6_family = AF_INET6; + addr.sin6_port = htons (atoi (port)); + ret = _bind (fd, (struct sockaddr *) &addr, sizeof (addr)); + ERR_GOTO (ret < 0, CLEAN, "bind(%d, %s, %ld)=%d:%d\n", fd, + f_in6addr (&addr), sizeof (addr), ret, errno); + + ret = _close (fd); + if (ret) + err ("close(%d)=%d:%d\n", fd, ret, errno); + + { + fd_set rfds, wfds, efds; + int nfds = fd + 1; + FD_ZERO (&rfds); + FD_SET (fd, &rfds); + FD_ZERO (&wfds); + FD_SET (fd, &wfds); + FD_ZERO (&efds); + FD_SET (fd, &efds); + ret = select (nfds, &rfds, &wfds, &efds, NULL); + int err = errno; + TEST_ASSERT (ret == -1); + TEST_ASSERT (err == EBADF); + } + + return 0; + +CLEAN: + (void) _close (fd); + return -1; +} + +int +v6_tcp_server_listen (int argc, const char *argv[]) +{ + return -1; +} + +int +v6_tcp_server_shutdown_rd (int argc, const char *argv[]) +{ + int sfd = -1, afd = -1, ret; + struct sockaddr_in6 saddr = { 0 }, aaddr = + { + 0}; + socklen_t len; + const char *ip = argv[1]; + const char *port = argv[2]; + + sfd = _socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (sfd < 0, sfd, + "socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", sfd, + errno); + + saddr.sin6_family = AF_INET6; + ret = inet_pton (AF_INET6, ip, &saddr.sin6_addr); + ERR_GOTO (ret != 1, CLEAN_S, + "inet_pton(AF_INET6, ip=\"%s\", &saddr.sin6_addr)=%d:%d\n", ip, + ret, errno); + saddr.sin6_port = htons (atoi (port)); + + ret = _bind (sfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN_S, "bind(%d, %s, %ld)=%d:%d\n", sfd, + f_in6addr (&saddr), sizeof (saddr), ret, errno); + + ret = _listen (sfd, 100); + ERR_GOTO (ret < 0, CLEAN_S, "listen(%d, 100)=%d:%d\n", sfd, ret, errno); + + len = sizeof (aaddr); + afd = _accept (sfd, (struct sockaddr *) &aaddr, &len); + ERR_GOTO (ret < 0, CLEAN_S, "accept(%d, %s, %d)=%d:%d\n", sfd, + f_in6addr (&aaddr), len, ret, errno); + + out ("accept(sfd=%d, addr=%s, len=%d)=%d\n", sfd, f_in6addr (&aaddr), len, + afd); + + ret = _shutdown (afd, SHUT_RD); + ERR_GOTO (ret != 0, CLEAN, "shutdown(afd=%d, SHUT_RD)=%d:%d\n", afd, ret, + errno); + + out ("shutdown(afd=%d, SHUT_RD) ok --> sleep(10)\n", afd); + sleep (10); + + out ("closing\n"); + + ret = _close (afd); + TEST_ASSERT (ret == 0); + + ret = _close (sfd); + TEST_ASSERT (ret == 0); + + return ret; + +CLEAN: + (void) _close (afd); +CLEAN_S: + (void) _close (sfd); + return -1; +} + +int +v6_tcp_server_shutdown_wr (int argc, const char *argv[]) +{ + int sfd = -1, afd = -1, ret; + struct sockaddr_in6 saddr = { 0 }, aaddr = + { + 0}; + socklen_t len; + const char *ip = argv[1]; + const char *port = argv[2]; + + sfd = _socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (sfd < 0, sfd, + "socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", sfd, + errno); + + saddr.sin6_family = AF_INET6; + ret = inet_pton (AF_INET6, ip, &saddr.sin6_addr); + ERR_GOTO (ret != 1, CLEAN_S, + "inet_pton(AF_INET6, ip=\"%s\", &saddr.sin6_addr)=%d:%d\n", ip, + ret, errno); + saddr.sin6_port = htons (atoi (port)); + + ret = _bind (sfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN_S, "bind(%d, %s, %ld)=%d:%d\n", sfd, + f_in6addr (&saddr), sizeof (saddr), ret, errno); + + ret = _listen (sfd, 100); + ERR_GOTO (ret < 0, CLEAN_S, "listen(%d, 100)=%d:%d\n", sfd, ret, errno); + + len = sizeof (aaddr); + afd = _accept (sfd, (struct sockaddr *) &aaddr, &len); + ERR_GOTO (ret < 0, CLEAN_S, "accept(%d, %s, %d)=%d:%d\n", sfd, + f_in6addr (&aaddr), len, ret, errno); + + out ("accept(sfd=%d, addr=%s, len=%d)=%d\n", sfd, f_in6addr (&aaddr), len, + afd); + + ret = _shutdown (afd, SHUT_WR); + ERR_GOTO (ret != 0, CLEAN, "shutdown(afd=%d, SHUT_WR)=%d:%d\n", afd, ret, + errno); + + out ("shutdown(afd=%d, SHUT_WR) ok --> sleep(10)\n", afd); + sleep (10); + + out ("closing\n"); + + ret = _close (afd); + TEST_ASSERT (ret == 0); + + ret = _close (sfd); + TEST_ASSERT (ret == 0); + + return ret; + +CLEAN: + (void) _close (afd); +CLEAN_S: + (void) _close (sfd); + return -1; +} + +int +v6_tcp_server_shutdown_rdwr (int argc, const char *argv[]) +{ + int sfd = -1, afd = -1, ret; + struct sockaddr_in6 saddr = { 0 }, aaddr = + { + 0}; + socklen_t len; + const char *ip = argv[1]; + const char *port = argv[2]; + + sfd = _socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (sfd < 0, sfd, + "socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", sfd, + errno); + + saddr.sin6_family = AF_INET6; + ret = inet_pton (AF_INET6, ip, &saddr.sin6_addr); + ERR_GOTO (ret != 1, CLEAN_S, + "inet_pton(AF_INET6, ip=\"%s\", &saddr.sin6_addr)=%d:%d\n", ip, + ret, errno); + saddr.sin6_port = htons (atoi (port)); + + ret = _bind (sfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN_S, "bind(%d, %s, %ld)=%d:%d\n", sfd, + f_in6addr (&saddr), sizeof (saddr), ret, errno); + + ret = _listen (sfd, 100); + ERR_GOTO (ret < 0, CLEAN_S, "listen(%d, 100)=%d:%d\n", sfd, ret, errno); + + len = sizeof (aaddr); + afd = _accept (sfd, (struct sockaddr *) &aaddr, &len); + ERR_GOTO (ret < 0, CLEAN_S, "accept(%d, %s, %d)=%d:%d\n", sfd, + f_in6addr (&aaddr), len, ret, errno); + + out ("accept(sfd=%d, addr=%s, len=%d)=%d\n", sfd, f_in6addr (&aaddr), len, + afd); + + ret = _shutdown (afd, SHUT_RDWR); + ERR_GOTO (ret != 0, CLEAN, "shutdown(afd=%d, SHUT_RDWR)=%d:%d\n", afd, ret, + errno); + + out ("shutdown(afd=%d, SHUT_WR) ok --> sleep(10)\n", afd); + sleep (10); + + out ("closing\n"); + + ret = _close (afd); + TEST_ASSERT (ret == 0); + + ret = _close (sfd); + TEST_ASSERT (ret == 0); + + return ret; + +CLEAN: + (void) _close (afd); +CLEAN_S: + (void) _close (sfd); + return -1; +} + +int +v6_tcp_client_s (int argc, const char *argv[]) +{ + int cfd = -1, ret; + struct sockaddr_in6 saddr = { 0 }; + socklen_t len; + char buf[10] = { 'X', 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + const char *ip = argv[1]; + const char *port = argv[2]; + + cfd = _socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (cfd < 0, cfd, + "socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", cfd, + errno); + + saddr.sin6_family = AF_INET6; + ret = inet_pton (AF_INET6, ip, &saddr.sin6_addr); + ERR_GOTO (ret != 1, CLEAN, + "inet_pton(AF_INET6, ip=\"%s\", &saddr.sin6_addr)=%d:%d\n", ip, + ret, errno); + saddr.sin6_port = htons (atoi (port)); + + ret = _connect (cfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN, "connect(%d, %s, %d)=%d:%d\n", cfd, + f_in6addr (&saddr), len, ret, errno); + out ("connect ok --> sleep(5)\n"); + sleep (5); + + ret = _send (cfd, buf, 10, 0); + out ("send()=%d:%d --> sleep(5)\n", ret, errno); + sleep (5); + + ret = _close (cfd); + TEST_ASSERT (ret == 0); + + return ret; + +CLEAN: + (void) _close (cfd); + return -1; +} + +int +v4_tcp_server_shutdown_rd (int argc, const char *argv[]) +{ + int sfd = -1, afd = -1, ret; + struct sockaddr_in saddr = { 0 }, aaddr = + { + 0}; + socklen_t len; + const char *ip = argv[1]; + const char *port = argv[2]; + + sfd = _socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (sfd < 0, sfd, + "socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", sfd, + errno); + + saddr.sin_family = AF_INET; + ret = inet_pton (AF_INET, ip, &saddr.sin_addr); + ERR_GOTO (ret != 1, CLEAN_S, + "inet_pton(AF_INET, ip=\"%s\", &saddr.sin_addr)=%d:%d\n", ip, ret, + errno); + saddr.sin_port = htons (atoi (port)); + + ret = _bind (sfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN_S, "bind(%d, %s, %ld)=%d:%d\n", sfd, + f_inaddr (&saddr), sizeof (saddr), ret, errno); + + ret = _listen (sfd, 100); + ERR_GOTO (ret < 0, CLEAN_S, "listen(%d, 100)=%d:%d\n", sfd, ret, errno); + + len = sizeof (aaddr); + afd = _accept (sfd, (struct sockaddr *) &aaddr, &len); + ERR_GOTO (ret < 0, CLEAN_S, "accept(%d, %s, %d)=%d:%d\n", sfd, + f_inaddr (&aaddr), len, ret, errno); + + out ("accept(sfd=%d, addr=%s, len=%d)=%d\n", sfd, f_inaddr (&aaddr), len, + afd); + sleep (2); + + ret = _shutdown (afd, SHUT_RD); + ERR_GOTO (ret != 0, CLEAN, "shutdown(afd=%d, SHUT_RD)=%d:%d\n", afd, ret, + errno); + + out ("shutdown(afd=%d, SHUT_RD) ok --> sleep(10)\n", afd); + sleep (10); + + out ("closing\n"); + + ret = _close (afd); + TEST_ASSERT (ret == 0); + + ret = _close (sfd); + TEST_ASSERT (ret == 0); + + return ret; + +CLEAN: + (void) _close (afd); +CLEAN_S: + (void) _close (sfd); + return -1; +} + +int +v4_tcp_server_shutdown_wr (int argc, const char *argv[]) +{ + int sfd = -1, afd = -1, ret; + struct sockaddr_in saddr = { 0 }, aaddr = + { + 0}; + socklen_t len; + const char *ip = argv[1]; + const char *port = argv[2]; + + sfd = _socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (sfd < 0, sfd, + "socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", sfd, + errno); + + saddr.sin_family = AF_INET; + ret = inet_pton (AF_INET, ip, &saddr.sin_addr); + ERR_GOTO (ret != 1, CLEAN_S, + "inet_pton(AF_INET, ip=\"%s\", &saddr.sin_addr)=%d:%d\n", ip, ret, + errno); + saddr.sin_port = htons (atoi (port)); + + ret = _bind (sfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN_S, "bind(%d, %s, %ld)=%d:%d\n", sfd, + f_inaddr (&saddr), sizeof (saddr), ret, errno); + + ret = _listen (sfd, 100); + ERR_GOTO (ret < 0, CLEAN_S, "listen(%d, 100)=%d:%d\n", sfd, ret, errno); + + len = sizeof (aaddr); + afd = _accept (sfd, (struct sockaddr *) &aaddr, &len); + ERR_GOTO (ret < 0, CLEAN_S, "accept(%d, %s, %d)=%d:%d\n", sfd, + f_inaddr (&aaddr), len, ret, errno); + out ("accept(sfd=%d, addr=%s, len=%d)=%d\n", sfd, f_inaddr (&aaddr), len, + afd); + + ret = _shutdown (afd, SHUT_WR); + ERR_GOTO (ret != 0, CLEAN, "shutdown(afd=%d, SHUT_RD)=%d:%d\n", afd, ret, + errno); + out ("shutdown(afd=%d, SHUT_WR) ok --> sleep(10)\n", afd); + sleep (10); + + out ("closing\n"); + + ret = _close (afd); + TEST_ASSERT (ret == 0); + + ret = _close (sfd); + TEST_ASSERT (ret == 0); + + return ret; + +CLEAN: + (void) _close (afd); +CLEAN_S: + (void) _close (sfd); + return -1; +} + +int +v4_tcp_client_s (int argc, const char *argv[]) +{ + int cfd = -1, ret; + struct sockaddr_in saddr = { 0 }; + socklen_t len; + char buf[10] = { 'X', 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + const char *ip = argv[1]; + const char *port = argv[2]; + + cfd = _socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (cfd < 0, cfd, + "socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", cfd, + errno); + + saddr.sin_family = AF_INET; + ret = inet_pton (AF_INET, ip, &saddr.sin_addr); + ERR_GOTO (ret != 1, CLEAN, + "inet_pton(AF_INET, ip=\"%s\", &saddr.sin_addr)=%d:%d\n", ip, ret, + errno); + saddr.sin_port = htons (atoi (port)); + + ret = _connect (cfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN, "connect(%d, %s, %d)=%d:%d\n", cfd, + f_inaddr (&saddr), len, ret, errno); + out ("connect ok --> sleep(5)\n"); + sleep (5); + + ret = _send (cfd, buf, 10, 0); + out ("send()=%d:%d --> sleep(5)\n", ret, errno); + sleep (5); + + ret = _close (cfd); + TEST_ASSERT (ret == 0); + + return ret; + +CLEAN: + (void) _close (cfd); + return -1; +} + +int +v6_tcp_server_close_select (int argc, const char *argv[]) +{ + int sfd = -1, afd = -1, ret; + struct sockaddr_in6 saddr = { 0 }, aaddr = + { + 0}; + socklen_t len; + const char *ip = argv[1]; + const char *port = argv[2]; + + sfd = _socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP); + ERR_RETURN (sfd < 0, sfd, + "socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)=%d:%d\n", sfd, + errno); + + saddr.sin6_family = AF_INET6; + ret = inet_pton (AF_INET6, ip, &saddr.sin6_addr); + ERR_GOTO (ret != 1, CLEAN_S, + "inet_pton(AF_INET6, ip=\"%s\", &saddr.sin6_addr)=%d:%d\n", ip, + ret, errno); + saddr.sin6_port = htons (atoi (port)); + + ret = _bind (sfd, (struct sockaddr *) &saddr, sizeof (saddr)); + ERR_GOTO (ret < 0, CLEAN_S, "bind(%d, %s, %ld)=%d:%d\n", sfd, + f_in6addr (&saddr), sizeof (saddr), ret, errno); + + ret = _listen (sfd, 100); + ERR_GOTO (ret < 0, CLEAN_S, "listen(%d, 100)=%d:%d\n", sfd, ret, errno); + + len = sizeof (aaddr); + afd = _accept (sfd, (struct sockaddr *) &aaddr, &len); + ERR_GOTO (ret < 0, CLEAN_S, "accept(%d, %s, %d)=%d:%d\n", sfd, + f_in6addr (&aaddr), len, ret, errno); + + out ("accept(sfd=%d, addr=%s, len=%d)=%d\n", sfd, f_in6addr (&aaddr), len, + afd); + + ret = _close (afd); + ERR_GOTO (ret != 0, CLEAN_S, "close(afd=%d)=%d:%d\n", afd, ret, errno); + out ("close(afd=%d) ok --> sleep(2)\n", afd); + sleep (2); + + { + fd_set rfds, wfds, efds; + int nfds = afd + 1; + FD_ZERO (&rfds); + FD_SET (afd, &rfds); + FD_ZERO (&wfds); + FD_SET (afd, &wfds); + FD_ZERO (&efds); + FD_SET (afd, &efds); + ret = select (nfds, &rfds, &wfds, &efds, NULL); + int err = errno; + TEST_ASSERT (ret == -1); + TEST_ASSERT (err == EBADF); + } + + ret = _close (sfd); + TEST_ASSERT (ret == 0); + + return ret; + +CLEAN: + (void) _close (afd); +CLEAN_S: + (void) _close (sfd); + return -1; +} + +struct config +{ + char opt; + const char *name; + const char *help; + int (*proc) (int argc, const char *argv[]); +}; + +struct config list[] = { + {'l', "v6_tcp_server_listen", "X::X PORT", v6_tcp_server_listen}, + {'L', "v6_tcp_server_listen", "X::X PORT", v6_tcp_server_listen}, + {'-', NULL, NULL, NULL}, + {'c', "v6_tcp_client_s", "X::X PORT", v6_tcp_client_s}, + {'s', "v6_tcp_server_shutdown_rd", "X::X PORT", v6_tcp_server_shutdown_rd}, + {'d', "v6_tcp_server_shutdown_wr", "X::X PORT", v6_tcp_server_shutdown_wr}, + {'f', "v6_tcp_server_shutdown_rdwr", "X::X PORT", + v6_tcp_server_shutdown_rdwr}, + {'-', NULL, NULL, NULL}, + {'1', "v4_tcp_client_s", "X.X.X.X PORT", v4_tcp_client_s}, + {'2', "v4_tcp_server_shutdown_rd", "X.X.X.X PORT", + v4_tcp_server_shutdown_rd}, + {'3', "v4_tcp_server_shutdown_wr", "X.X.X.X PORT", + v4_tcp_server_shutdown_wr}, + {'-', NULL, NULL, NULL}, + {'U', "test_v6_udp", "X::X", test_v6_udp}, + {'u', "test_v4_udp", "X.X.X.X", test_v4_udp}, + {'t', "test_v4_tcp", "X.X.X.X", test_v4_tcp}, + {'-', NULL, NULL, NULL}, + {'b', "v6_udp_close_select", "X::X PORT", v6_udp_close_select}, +}; + +int +usage () +{ + int i; + for (i = 0; i < sizeof (list) / sizeof (list[0]); ++i) + { + if (list[i].opt == '-') + out ("\n"); + else + out ("%c : %s ( %s )\n", list[i].opt, list[i].name, list[i].help); + } + return 0; +} + +int +main (int argc, const char *argv[]) +{ + int i, ret; + + argc--; + argv++; + + if (argc <= 0) + return usage (); + + for (i = 0; i < sizeof (list) / sizeof (list[0]); ++i) + { + if (list[i].opt == '-') + continue; + if (argv[0][0] != list[i].opt) + continue; + + out ("Test %s%s%s begin\n", CH, list[i].name, CC); + ret = list[i].proc (argc, argv); + if (ret) + out ("%sFAILED%s: %s\n", FR__, CC, list[i].name); + else + out ("OK: %s\n", list[i].name); + + out ("\n <<<< over <<<<\n"); + return 0; + } + + return usage (); +} -- cgit 1.2.3-korg