summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorin Coras <fcoras@cisco.com>2019-08-31 09:45:13 -0700
committerAndrew Yourtchenko <ayourtch@gmail.com>2019-09-30 00:08:22 +0000
commit2ff21af396b3f22147f439c62a80a5eaeda6cdf1 (patch)
tree78fe2a97b6ceccea5e59fa3be9cae484444073dc
parent6702641f536ba1a14075b2b24ffd2fb9c6167791 (diff)
session: improve cli
Type: feature Allow session cli filtering based on thread index, transport protocol, session state and range of session pool indices. For instance show session thread 1 proto tcp state ready range 0 20 verbose Shows the session ids for the first 20 tcp sessions in thread 1 that are in ready state. To avoid excessive output that could reasult in the worker barrier being held by the main thread for long periods of time, the session cli will only output: - session ids (verbose == 1) for a maximum of 50 sessions / worker - verbose > 1 details for a maximum of 10 sessions Change-Id: I2cfb351b548e2e0a1d5b4345810be613e2917d17 Signed-off-by: Florin Coras <fcoras@cisco.com> (cherry picked from commit 5bb23ecd098eac639641e2b3d62eb8744e0efef0)
-rw-r--r--src/plugins/hs_apps/sapi/vpp_echo_proto_quic.c6
-rw-r--r--src/vnet/session/session.h12
-rwxr-xr-xsrc/vnet/session/session_cli.c341
-rw-r--r--src/vnet/session/session_types.h31
-rw-r--r--src/vnet/session/transport.c85
-rw-r--r--src/vnet/session/transport_types.h19
-rw-r--r--src/vnet/tcp/tcp.c2
7 files changed, 348 insertions, 148 deletions
diff --git a/src/plugins/hs_apps/sapi/vpp_echo_proto_quic.c b/src/plugins/hs_apps/sapi/vpp_echo_proto_quic.c
index 28c89a65fbf..41f9cd9e09c 100644
--- a/src/plugins/hs_apps/sapi/vpp_echo_proto_quic.c
+++ b/src/plugins/hs_apps/sapi/vpp_echo_proto_quic.c
@@ -54,7 +54,7 @@ quic_echo_on_connected_connect (session_connected_msg_t * mp,
{
echo_main_t *em = &echo_main;
quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
- u8 *uri = format (0, "QUIC://session/%lu", mp->handle);
+ u8 *uri = format (0, "quic://session/%lu", mp->handle);
u64 i;
echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
@@ -107,7 +107,7 @@ quic_echo_on_accept_connect (session_accepted_msg_t * mp, u32 session_index)
echo_main_t *em = &echo_main;
quic_echo_proto_main_t *eqm = &quic_echo_proto_main;
ECHO_LOG (1, "Accept on QSession 0x%lx %u", mp->handle);
- u8 *uri = format (0, "QUIC://session/%lu", mp->handle);
+ u8 *uri = format (0, "quic://session/%lu", mp->handle);
u32 i;
echo_notify_event (em, ECHO_EVT_FIRST_SCONNECT);
@@ -303,7 +303,7 @@ quic_echo_retry_connect (u32 session_index)
else
{
session = pool_elt_at_index (em->sessions, session_index);
- uri = format (0, "QUIC://session/%lu", session->vpp_session_handle);
+ uri = format (0, "quic://session/%lu", session->vpp_session_handle);
ECHO_LOG (1, "Retrying connect %s", uri);
echo_send_rpc (em, echo_send_connect, (void *) uri, session_index);
}
diff --git a/src/vnet/session/session.h b/src/vnet/session/session.h
index de44bed27c9..b3805dba0b0 100644
--- a/src/vnet/session/session.h
+++ b/src/vnet/session/session.h
@@ -552,6 +552,14 @@ session_main_get_worker (u32 thread_index)
return &session_main.wrk[thread_index];
}
+static inline session_worker_t *
+session_main_get_worker_if_valid (u32 thread_index)
+{
+ if (pool_is_free_index (session_main.wrk, thread_index))
+ return 0;
+ return &session_main.wrk[thread_index];
+}
+
always_inline svm_msg_q_t *
session_main_get_vpp_event_queue (u32 thread_index)
{
@@ -566,8 +574,8 @@ session_main_is_enabled ()
#define session_cli_return_if_not_enabled() \
do { \
- if (!session_main.is_enabled) \
- return clib_error_return(0, "session layer is not enabled"); \
+ if (!session_main.is_enabled) \
+ return clib_error_return (0, "session layer is not enabled"); \
} while (0)
int session_main_flush_enqueue_events (u8 proto, u32 thread_index);
diff --git a/src/vnet/session/session_cli.c b/src/vnet/session/session_cli.c
index fd598f45d3e..d8aedd64e23 100755
--- a/src/vnet/session/session_cli.c
+++ b/src/vnet/session/session_cli.c
@@ -162,6 +162,27 @@ unformat_stream_session_id (unformat_input_t * input, va_list * args)
}
uword
+unformat_session_state (unformat_input_t * input, va_list * args)
+{
+ session_state_t *state = va_arg (*args, session_state_t *);
+ u8 *state_vec = 0;
+ int rv = 0;
+
+#define _(sym, str) \
+ if (unformat (input, str)) \
+ { \
+ *state = SESSION_STATE_ ## sym; \
+ rv = 1; \
+ goto done; \
+ }
+ foreach_session_state
+#undef _
+done:
+ vec_free (state_vec);
+ return rv;
+}
+
+uword
unformat_session (unformat_input_t * input, va_list * args)
{
session_t **result = va_arg (*args, session_t **);
@@ -227,42 +248,270 @@ unformat_transport_connection (unformat_input_t * input, va_list * args)
return 0;
}
+static void
+session_cli_show_all_sessions (vlib_main_t * vm, int verbose)
+{
+ session_main_t *smm = &session_main;
+ u32 n_closed = 0, thread_index;
+ session_t *pool, *s;
+
+ for (thread_index = 0; thread_index < vec_len (smm->wrk); thread_index++)
+ {
+ pool = smm->wrk[thread_index].sessions;
+
+ if (!pool_elts (pool))
+ {
+ vlib_cli_output (vm, "Thread %d: no sessions", thread_index);
+ continue;
+ }
+
+ if (!verbose)
+ {
+ vlib_cli_output (vm, "Thread %d: %d sessions", thread_index,
+ pool_elts (pool));
+ continue;
+ }
+
+ if (pool_elts (pool) > 50)
+ {
+ vlib_cli_output (vm, "Thread %u: %d sessions. Verbose output "
+ "suppressed. For more details use filters.",
+ thread_index, pool_elts (pool));
+ continue;
+ }
+
+ if (verbose == 1)
+ vlib_cli_output (vm, "%s%-50s%-15s%-10s%-10s",
+ thread_index ? "\n" : "",
+ "Connection", "State", "Rx-f", "Tx-f");
+
+ /* *INDENT-OFF* */
+ pool_foreach(s, pool, ({
+ if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED)
+ {
+ n_closed += 1;
+ continue;
+ }
+ vlib_cli_output (vm, "%U", format_session, s, verbose);
+ }));
+ /* *INDENT-ON* */
+
+ if (!n_closed)
+ vlib_cli_output (vm, "Thread %d: active sessions %u", thread_index,
+ pool_elts (pool) - n_closed);
+ else
+ vlib_cli_output (vm, "Thread %d: active sessions %u closed %u",
+ thread_index, pool_elts (pool) - n_closed, n_closed);
+ }
+}
+
+static int
+session_cli_filter_check (session_t * s, session_state_t * states,
+ transport_proto_t tp)
+{
+ if (states)
+ {
+ session_state_t *state;
+ vec_foreach (state, states) if (s->session_state == *state)
+ goto check_transport;
+ return 0;
+ }
+
+check_transport:
+
+ if (tp != TRANSPORT_N_PROTO && session_get_transport_proto (s) != tp)
+ return 0;
+
+ return 1;
+}
+
+static void
+session_cli_show_session_filter (vlib_main_t * vm, u32 thread_index,
+ u32 start, u32 end, session_state_t * states,
+ transport_proto_t tp, int verbose)
+{
+ u8 output_suppressed = 0;
+ session_worker_t *wrk;
+ session_t *pool, *s;
+ u32 count = 0, max_index;
+ int i;
+
+ wrk = session_main_get_worker_if_valid (thread_index);
+ if (!wrk)
+ {
+ vlib_cli_output (vm, "invalid thread index %u", thread_index);
+ return;
+ }
+
+ pool = wrk->sessions;
+
+ if (tp == TRANSPORT_N_PROTO && states == 0 && !verbose
+ && (start == 0 && end == ~0))
+ {
+ vlib_cli_output (vm, "Thread %d: %u sessions", thread_index,
+ pool_elts (pool));
+ return;
+ }
+
+ max_index = pool_elts (pool) ? pool_elts (pool) - 1 : 0;
+ for (i = start; i <= clib_min (end, max_index); i++)
+ {
+ if (pool_is_free_index (pool, i))
+ continue;
+
+ s = pool_elt_at_index (pool, i);
+
+ if (session_cli_filter_check (s, states, tp))
+ {
+ count += 1;
+ if (verbose)
+ {
+ if (count > 50 || (verbose > 1 && count > 10))
+ {
+ output_suppressed = 1;
+ continue;
+ }
+ if (s->session_state < SESSION_STATE_TRANSPORT_DELETED)
+ vlib_cli_output (vm, "%U", format_session, s, verbose);
+ }
+ }
+ }
+
+ if (!output_suppressed)
+ vlib_cli_output (vm, "Thread %d: %u sessions matched filter",
+ thread_index, count);
+ else
+ vlib_cli_output (vm, "Thread %d: %u sessions matched filter. Not all"
+ " shown. Use finer grained filter.", thread_index,
+ count);
+}
+
+static void
+session_cli_print_transport_protos (vlib_main_t * vm)
+{
+#define _(sym, str, sstr) vlib_cli_output (vm, str);
+ foreach_transport_proto
+#undef _
+}
+
+static void
+session_cli_print_session_states (vlib_main_t * vm)
+{
+#define _(sym, str) vlib_cli_output (vm, str);
+ foreach_session_state
+#undef _
+}
+
static clib_error_t *
show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
vlib_cli_command_t * cmd)
{
- u8 one_session = 0, do_listeners = 0, sst, do_elog = 0;
+ u8 one_session = 0, do_listeners = 0, sst, do_elog = 0, do_filter = 0;
+ u32 track_index, thread_index = 0, start = 0, end = ~0, session_index;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ transport_proto_t transport_proto = TRANSPORT_N_PROTO;
+ session_state_t state = SESSION_N_STATES, *states = 0;
session_main_t *smm = &session_main;
- u32 transport_proto = ~0, track_index;
- session_t *pool, *s;
- transport_connection_t *tc;
+ clib_error_t *error = 0;
app_worker_t *app_wrk;
- int verbose = 0, i;
+ u32 transport_index;
const u8 *app_name;
+ int verbose = 0;
+ session_t *s;
- if (!smm->is_enabled)
+ session_cli_return_if_not_enabled ();
+
+ if (!unformat_user (input, unformat_line_input, line_input))
{
- return clib_error_return (0, "session layer is not enabled");
+ session_cli_show_all_sessions (vm, 0);
+ return 0;
}
- while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
{
- if (unformat (input, "verbose %d", &verbose))
+ if (unformat (line_input, "verbose %d", &verbose))
;
- else if (unformat (input, "verbose"))
+ else if (unformat (line_input, "verbose"))
verbose = 1;
- else if (unformat (input, "listeners %U", unformat_transport_proto,
+ else if (unformat (line_input, "listeners %U", unformat_transport_proto,
&transport_proto))
do_listeners = 1;
- else if (unformat (input, "%U", unformat_session, &s))
+ else if (unformat (line_input, "%U", unformat_session, &s))
{
one_session = 1;
}
- else if (unformat (input, "elog"))
+ else if (unformat (line_input, "thread %u index %u", &thread_index,
+ &session_index))
+ {
+ s = session_get_if_valid (session_index, thread_index);
+ if (!s)
+ {
+ vlib_cli_output (vm, "session is not allocated");
+ goto done;
+ }
+ one_session = 1;
+ }
+ else if (unformat (line_input, "thread %u", &thread_index))
+ {
+ do_filter = 1;
+ }
+ else
+ if (unformat (line_input, "state %U", unformat_session_state, &state))
+ {
+ vec_add1 (states, state);
+ do_filter = 1;
+ }
+ else if (unformat (line_input, "proto %U index %u",
+ unformat_transport_proto, &transport_proto,
+ &transport_index))
+ {
+ transport_connection_t *tc;
+ tc = transport_get_connection (transport_proto, transport_index,
+ thread_index);
+ if (!tc)
+ {
+ vlib_cli_output (vm, "transport connection %u thread %u is not"
+ " allocated", transport_index, thread_index);
+ goto done;
+ }
+ s = session_get_if_valid (tc->s_index, thread_index);
+ if (!s)
+ {
+ vlib_cli_output (vm, "session for transport connection %u "
+ "thread %u does not exist", transport_index,
+ thread_index);
+ goto done;
+ }
+ one_session = 1;
+ }
+ else if (unformat (line_input, "proto %U", unformat_transport_proto,
+ &transport_proto))
+ do_filter = 1;
+ else if (unformat (line_input, "range %u %u", &start, &end))
+ do_filter = 1;
+ else if (unformat (line_input, "range %u", &start))
+ {
+ end = start + 50;
+ do_filter = 1;
+ }
+ else if (unformat (line_input, "elog"))
do_elog = 1;
+ else if (unformat (line_input, "protos"))
+ {
+ session_cli_print_transport_protos (vm);
+ goto done;
+ }
+ else if (unformat (line_input, "states"))
+ {
+ session_cli_print_session_states (vm);
+ goto done;
+ }
else
- return clib_error_return (0, "unknown input `%U'",
- format_unformat_error, input);
+ {
+ error = clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
}
if (one_session)
@@ -271,6 +520,7 @@ show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
if (do_elog && s->session_state != SESSION_STATE_LISTENING)
{
elog_main_t *em = &vm->elog_main;
+ transport_connection_t *tc;
f64 dt;
tc = session_get_transport (s);
@@ -283,7 +533,7 @@ show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
}
vlib_cli_output (vm, "%v", str);
vec_free (str);
- return 0;
+ goto done;
}
if (do_listeners)
@@ -301,53 +551,28 @@ show_session_command_fn (vlib_main_t * vm, unformat_input_t * input,
app_name);
}));
/* *INDENT-ON* */
- return 0;
+ goto done;
}
- for (i = 0; i < vec_len (smm->wrk); i++)
+ if (do_filter)
{
- u32 once_per_pool = 1, n_closed = 0;
-
- pool = smm->wrk[i].sessions;
- if (!pool_elts (pool))
- {
- vlib_cli_output (vm, "Thread %d: no sessions", i);
- continue;
- }
-
- if (!verbose)
- {
- vlib_cli_output (vm, "Thread %d: %d sessions", i, pool_elts (pool));
- continue;
- }
-
- if (once_per_pool && verbose == 1)
+ if (end < start)
{
- vlib_cli_output (vm, "%s%-50s%-15s%-10s%-10s", i ? "\n" : "",
- "Connection", "State", "Rx-f", "Tx-f");
- once_per_pool = 0;
+ error = clib_error_return (0, "invalid range start: %u end: %u",
+ start, end);
+ goto done;
}
-
- /* *INDENT-OFF* */
- pool_foreach (s, pool, ({
- if (s->session_state >= SESSION_STATE_TRANSPORT_DELETED)
- {
- n_closed += 1;
- continue;
- }
- vlib_cli_output (vm, "%U", format_session, s, verbose);
- }));
- /* *INDENT-ON* */
-
- if (!n_closed)
- vlib_cli_output (vm, "Thread %d: active sessions %u", i,
- pool_elts (pool) - n_closed);
- else
- vlib_cli_output (vm, "Thread %d: active sessions %u closed %u", i,
- pool_elts (pool) - n_closed, n_closed);
+ session_cli_show_session_filter (vm, thread_index, start, end, states,
+ transport_proto, verbose);
+ goto done;
}
- return 0;
+ session_cli_show_all_sessions (vm, verbose);
+
+done:
+ unformat_free (line_input);
+ vec_free (states);
+ return error;
}
/* *INDENT-OFF* */
@@ -355,7 +580,9 @@ VLIB_CLI_COMMAND (vlib_cli_show_session_command) =
{
.path = "show session",
.short_help = "show session [verbose [n]] [listeners <proto>] "
- "[<session-id> [elog]]",
+ "[<session-id> [elog]] [thread <n> [index <n>] "
+ "[proto <proto>] [state <state>] [range <min> [<max>]] "
+ "[protos] [states] ",
.function = show_session_command_fn,
};
/* *INDENT-ON* */
diff --git a/src/vnet/session/session_types.h b/src/vnet/session/session_types.h
index f9472ba3828..05cf2e39912 100644
--- a/src/vnet/session/session_types.h
+++ b/src/vnet/session/session_types.h
@@ -121,21 +121,26 @@ typedef enum
/*
* Session states
*/
+#define foreach_session_state \
+ _(CREATED, "created") \
+ _(LISTENING, "listening") \
+ _(CONNECTING, "connecting") \
+ _(ACCEPTING, "accepting") \
+ _(READY, "ready") \
+ _(OPENED, "opened") \
+ _(TRANSPORT_CLOSING, "transport-closing") \
+ _(CLOSING, "closing") \
+ _(APP_CLOSED, "app-closed") \
+ _(TRANSPORT_CLOSED, "transport-closed") \
+ _(TRANSPORT_DELETED, "transport-deleted") \
+ _(CLOSED, "closed") \
+
typedef enum
{
- SESSION_STATE_CREATED,
- SESSION_STATE_LISTENING,
- SESSION_STATE_CONNECTING,
- SESSION_STATE_ACCEPTING,
- SESSION_STATE_READY,
- SESSION_STATE_OPENED,
- SESSION_STATE_TRANSPORT_CLOSING,
- SESSION_STATE_CLOSING,
- SESSION_STATE_APP_CLOSED,
- SESSION_STATE_TRANSPORT_CLOSED,
- SESSION_STATE_TRANSPORT_DELETED,
- SESSION_STATE_CLOSED,
- SESSION_STATE_N_STATES,
+#define _(sym, str) SESSION_STATE_ ## sym,
+ foreach_session_state
+#undef _
+ SESSION_N_STATES,
} session_state_t;
typedef enum session_flags_
diff --git a/src/vnet/session/transport.c b/src/vnet/session/transport.c
index 1b7928162b6..6caa4d38cd7 100644
--- a/src/vnet/session/transport.c
+++ b/src/vnet/session/transport.c
@@ -57,27 +57,12 @@ format_transport_proto (u8 * s, va_list * args)
u32 transport_proto = va_arg (*args, u32);
switch (transport_proto)
{
- case TRANSPORT_PROTO_TCP:
- s = format (s, "TCP");
- break;
- case TRANSPORT_PROTO_UDP:
- s = format (s, "UDP");
- break;
- case TRANSPORT_PROTO_SCTP:
- s = format (s, "SCTP");
- break;
- case TRANSPORT_PROTO_NONE:
- s = format (s, "NONE");
- break;
- case TRANSPORT_PROTO_TLS:
- s = format (s, "TLS");
- break;
- case TRANSPORT_PROTO_UDPC:
- s = format (s, "UDPC");
- break;
- case TRANSPORT_PROTO_QUIC:
- s = format (s, "QUIC");
+#define _(sym, str, sstr) \
+ case TRANSPORT_PROTO_ ## sym: \
+ s = format (s, str); \
break;
+ foreach_transport_proto
+#undef _
default:
s = format (s, "UNKNOWN");
break;
@@ -91,27 +76,12 @@ format_transport_proto_short (u8 * s, va_list * args)
u32 transport_proto = va_arg (*args, u32);
switch (transport_proto)
{
- case TRANSPORT_PROTO_TCP:
- s = format (s, "T");
- break;
- case TRANSPORT_PROTO_UDP:
- s = format (s, "U");
- break;
- case TRANSPORT_PROTO_SCTP:
- s = format (s, "S");
- break;
- case TRANSPORT_PROTO_NONE:
- s = format (s, "N");
- break;
- case TRANSPORT_PROTO_TLS:
- s = format (s, "J");
- break;
- case TRANSPORT_PROTO_UDPC:
- s = format (s, "U");
- break;
- case TRANSPORT_PROTO_QUIC:
- s = format (s, "Q");
+#define _(sym, str, sstr) \
+ case TRANSPORT_PROTO_ ## sym: \
+ s = format (s, sstr); \
break;
+ foreach_transport_proto
+#undef _
default:
s = format (s, "?");
break;
@@ -179,33 +149,16 @@ uword
unformat_transport_proto (unformat_input_t * input, va_list * args)
{
u32 *proto = va_arg (*args, u32 *);
- if (unformat (input, "tcp"))
- *proto = TRANSPORT_PROTO_TCP;
- else if (unformat (input, "TCP"))
- *proto = TRANSPORT_PROTO_TCP;
- else if (unformat (input, "udpc"))
- *proto = TRANSPORT_PROTO_UDPC;
- else if (unformat (input, "UDPC"))
- *proto = TRANSPORT_PROTO_UDPC;
- else if (unformat (input, "udp"))
- *proto = TRANSPORT_PROTO_UDP;
- else if (unformat (input, "UDP"))
- *proto = TRANSPORT_PROTO_UDP;
- else if (unformat (input, "sctp"))
- *proto = TRANSPORT_PROTO_SCTP;
- else if (unformat (input, "SCTP"))
- *proto = TRANSPORT_PROTO_SCTP;
- else if (unformat (input, "tls"))
- *proto = TRANSPORT_PROTO_TLS;
- else if (unformat (input, "TLS"))
- *proto = TRANSPORT_PROTO_TLS;
- else if (unformat (input, "quic"))
- *proto = TRANSPORT_PROTO_QUIC;
- else if (unformat (input, "QUIC"))
- *proto = TRANSPORT_PROTO_QUIC;
- else
+
+#define _(sym, str, sstr) \
+ if (unformat (input, str)) \
+ { \
+ *proto = TRANSPORT_PROTO_ ## sym; \
+ return 1; \
+ }
+ foreach_transport_proto
+#undef _
return 0;
- return 1;
}
u32
diff --git a/src/vnet/session/transport_types.h b/src/vnet/session/transport_types.h
index 41897c6d170..ac2177c77b0 100644
--- a/src/vnet/session/transport_types.h
+++ b/src/vnet/session/transport_types.h
@@ -139,15 +139,20 @@ STATIC_ASSERT (STRUCT_OFFSET_OF (transport_connection_t, s_index)
STATIC_ASSERT (sizeof (transport_connection_t) <= 128,
"moved into 3rd cache line");
+#define foreach_transport_proto \
+ _(TCP, "tcp", "T") \
+ _(UDP, "udp", "U") \
+ _(SCTP, "sctp", "S") \
+ _(NONE, "ct", "C") \
+ _(TLS, "tls", "J") \
+ _(UDPC, "udpc", "U") \
+ _(QUIC, "quic", "Q") \
+
typedef enum _transport_proto
{
- TRANSPORT_PROTO_TCP,
- TRANSPORT_PROTO_UDP,
- TRANSPORT_PROTO_SCTP,
- TRANSPORT_PROTO_NONE,
- TRANSPORT_PROTO_TLS,
- TRANSPORT_PROTO_UDPC,
- TRANSPORT_PROTO_QUIC,
+#define _(sym, str, sstr) TRANSPORT_PROTO_ ## sym,
+ foreach_transport_proto
+#undef _
TRANSPORT_N_PROTO
} transport_proto_t;
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index b898dd5902d..d88fc9730bb 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -1167,6 +1167,8 @@ static transport_connection_t *
tcp_session_get_transport (u32 conn_index, u32 thread_index)
{
tcp_connection_t *tc = tcp_connection_get (conn_index, thread_index);
+ if (PREDICT_FALSE (!tc))
+ return 0;
return &tc->connection;
}