From: Willy Tarreau Date: Tue, 30 Jul 2024 15:57:37 +0000 (+0200) Subject: MINOR: stconn: add a new pair of sf functions {bs,fs}.debug_str X-Git-Tag: v3.1-dev5~36 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=921e04bf87a94b410287a85d2397a0502520348c;p=thirdparty%2Fhaproxy.git MINOR: stconn: add a new pair of sf functions {bs,fs}.debug_str These are passed to the underlying mux to retrieve debug information at the mux level (stream/connection) as a string that's meant to be added to logs. The API is quite complex just because we can't pass any info to the bottom function. So we construct a union and pass the argument as an int, and expect the callee to fill that with its buffer in return. Most likely the mux->ctl and ->sctl API should be reworked before the release to simplify this. The functions take an optional argument that is a bit mask of the layers to dump: muxs=1 muxc=2 xprt=4 conn=8 sock=16 The default (0) logs everything available. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index d5c6a0e86f..6d975df77d 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -23966,11 +23966,13 @@ Summary of sample fetch methods in this section and their respective types: keyword output type ----------------------------------------------------+------------- bs.aborted boolean +bs.debug_str([]) string bs.id integer bs.rst_code integer distcc_body([,]) binary distcc_param([,]) integer fs.aborted boolean +fs.debug_str([]) string fs.id integer fs.rst_code integer payload(,) binary @@ -24008,6 +24010,28 @@ bs.aborted: boolean Returns true is an abort was received from the server for the current stream. Otherwise false is returned. +bs.debug_str([]) : string + This function is meant to be used by developers during certain complex + troubleshooting sessions. It extracts some internal states from the lower + layers of the backend stream and connection, and arranges them as a string, + generally in the form of a series of "name=value" delimited with spaces. The + optional argument indicates what layer(s) to extract information + from, and is an arithmetic OR (or a sum) of the following values: + - socket layer: 16 + - connection layer: 8 + - transport layer (e.g. SSL): 4 + - mux connection: 2 + - mux stream: 1 + + These values might change across versions. The default value of zero is + special and enables all layers. Please do not rely on the output of this + function for long-term production monitoring. It is meant to evolve even + within a stable branch, as the needs for increased details arise. One use + typical use case is to concatenate these information at the very end of a + log-format, along with fs.debug_str(). Example: + + log-format "$HAPROXY_HTTP_LOG_FMT fs=<%[fs.debug_str]> bs=<%[bs.debug_str]>" + bs.id : integer Returns the multiplexer's stream ID on the server side. It is the multiplexer's responsibility to return the appropriate information. @@ -24048,6 +24072,28 @@ fs.aborted: boolean Returns true is an abort was received from the client for the current stream. Otherwise false is returned. +fs.debug_str([]) : string + This function is meant to be used by developers during certain complex + troubleshooting sessions. It extracts some internal states from the lower + layers of the frontend stream and connection, and arranges them as a string, + generally in the form of a series of "name=value" delimited with spaces. The + optional argument indicates what layer(s) to extract information + from, and is an arithmetic OR (or a sum) of the following values: + - socket layer: 16 + - connection layer: 8 + - transport layer (e.g. SSL): 4 + - mux connection: 2 + - mux stream: 1 + + These values might change across versions. The default value of zero is + special and enables all layers. Please do not rely on the output of this + function for long-term production monitoring. It is meant to evolve even + within a stable branch, as the needs for increased details arise. One use + typical use case is to concatenate these information at the very end of a + log-format, along with bs.debug_str(). Example: + + log-format "$HAPROXY_HTTP_LOG_FMT fs=<%[fs.debug_str]> bs=<%[bs.debug_str]>" + fs.id : integer Returns the multiplexer's stream ID on the client side. It is the multiplexer's responsibility to return the appropriate information. For diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h index 41c937c8c8..7119c0c20d 100644 --- a/include/haproxy/connection-t.h +++ b/include/haproxy/connection-t.h @@ -331,8 +331,16 @@ enum mux_ctl_type { /* sctl command used by mux->sctl() */ enum mux_sctl_type { MUX_SCTL_SID, /* Return the mux stream ID as output, as a signed 64bits integer */ + MUX_SCTL_DBG_STR, /* takes a mux_sctl_dbg_str_ctx argument, reads flags and returns debug info */ }; +#define MUX_SCTL_DBG_STR_L_MUXS 0x00000001 // info from mux stream +#define MUX_SCTL_DBG_STR_L_MUXC 0x00000002 // info from mux connection +#define MUX_SCTL_DBG_STR_L_XPRT 0x00000004 // info from xprt layer +#define MUX_SCTL_DBG_STR_L_CONN 0x00000008 // info from struct connection layer +#define MUX_SCTL_DBG_STR_L_SOCK 0x00000010 // info from socket layer (quic_conn as well) + + /* response for ctl MUX_STATUS */ #define MUX_STATUS_READY (1 << 0) @@ -688,6 +696,15 @@ struct tlv_ssl { uint8_t sub_tlv[VAR_ARRAY]; }__attribute__((packed)); +/* context for a MUX_SCTL_DBG_STR call */ +union mux_sctl_dbg_str_ctx { + struct { + uint debug_flags; // union of MUX_SCTL_DBG_STR_L_* + } arg; // sctl argument for the call + struct { + struct buffer buf; + } ret; // sctl return contents +}; /* This structure is used to manage idle connections, their locking, and the * list of such idle connections to be removed. It is per-thread and must be diff --git a/src/stconn.c b/src/stconn.c index cabc23a7ad..eacbb5c6be 100644 --- a/src/stconn.c +++ b/src/stconn.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -2364,6 +2365,45 @@ void sc_conn_commit_endp_upgrade(struct stconn *sc) BUG_ON(!sc->sedesc); } +/* Return a debug string exposing the internals of the front or back + * stream/connection when supported. It will be protocol-dependent and will + * change over time like the output of "show fd" or "show sess all". + */ +static int smp_fetch_debug_str(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + struct connection *conn; + struct stconn *sc; + union mux_sctl_dbg_str_ctx sctl_ctx = { }; + + if (!smp->strm) + return 0; + + sc = (kw[0] == 'f' ? smp->strm->scf : smp->strm->scb); + conn = sc_conn(sc); + + if (!conn) + return 0; + + /* a missing mux is necessarily on the backend, and may arrive later */ + if (!conn->mux) { + smp->flags |= SMP_F_MAY_CHANGE; + return 0; + } + + /* Not implemented, return nothing */ + if (!conn->mux->sctl) + return 0; + + sctl_ctx.arg.debug_flags = args->data.sint ? args->data.sint : ~0U; + if (conn->mux->sctl(sc, MUX_SCTL_DBG_STR, &sctl_ctx) == -1) + return 0; + + smp->data.type = SMP_T_STR; + smp->flags = SMP_F_VOL_TEST | SMP_F_MAY_CHANGE; + smp->data.u.str = sctl_ctx.ret.buf; + return 1; +} + /* return the frontend or backend mux stream ID. */ static int @@ -2459,9 +2499,11 @@ smp_fetch_strm_rst_code(const struct arg *args, struct sample *smp, const char * * common denominator, the type that can be casted into all other ones. */ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { + { "bs.debug_str", smp_fetch_debug_str, ARG1(0,SINT), NULL, SMP_T_STR, SMP_USE_L5SRV }, { "bs.id", smp_fetch_sid, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV }, { "bs.aborted", smp_fetch_strm_aborted, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV }, { "bs.rst_code", smp_fetch_strm_rst_code, 0, NULL, SMP_T_SINT, SMP_USE_L5SRV }, + { "fs.debug_str", smp_fetch_debug_str, ARG1(0,SINT), NULL, SMP_T_STR, SMP_USE_L5CLI }, { "fs.id", smp_fetch_sid, 0, NULL, SMP_T_STR, SMP_USE_L5CLI }, { "fs.aborted", smp_fetch_strm_aborted, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI }, { "fs.rst_code", smp_fetch_strm_rst_code, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },