REORG: mux-quic: extract traces in a dedicated source file

QUIC MUX implements several APIs to interface with stream, quic-conn and
app-ops layers. It is planified to better separate this roles, possibly
by using several files.

The first step is to extract QUIC MUX traces in a dedicated source
files. This will allow to reuse traces in multiple files.

The main objective is to be
able to support both TCP and HTTP proxy mode with a common base and add
specialized modules on top of it.

This should be backported up to 2.6.
diff --git a/Makefile b/Makefile
index e798b53..08d0804 100644
--- a/Makefile
+++ b/Makefile
@@ -646,7 +646,7 @@
                 src/cbuf.o src/qpack-dec.o src/qpack-tbl.o src/h3.o src/qpack-enc.o \
                 src/hq_interop.o src/cfgparse-quic.o src/quic_loss.o \
                 src/quic_tp.o src/quic_stream.o src/quic_stats.o src/h3_stats.o \
-                src/quic_cc_cubic.o
+                src/quic_cc_cubic.o src/qmux_trace.o
 endif
 
 ifneq ($(USE_LUA),)
diff --git a/include/haproxy/mux_quic.h b/include/haproxy/mux_quic.h
index 892ae91..7d3e47e 100644
--- a/include/haproxy/mux_quic.h
+++ b/include/haproxy/mux_quic.h
@@ -71,6 +71,18 @@
 	return !quic_stream_is_uni(id);
 }
 
+static inline char *qcs_st_to_str(enum qcs_state st)
+{
+	switch (st) {
+	case QC_SS_IDLE: return "IDL";
+	case QC_SS_OPEN: return "OPN";
+	case QC_SS_HLOC: return "HCL";
+	case QC_SS_HREM: return "HCR";
+	case QC_SS_CLO:  return "CLO";
+	default:         return "???";
+	}
+}
+
 int qcc_install_app_ops(struct qcc *qcc, const struct qcc_app_ops *app_ops);
 
 static inline struct stconn *qc_attach_sc(struct qcs *qcs, struct buffer *buf)
diff --git a/include/haproxy/qmux_trace.h b/include/haproxy/qmux_trace.h
new file mode 100644
index 0000000..c612c96
--- /dev/null
+++ b/include/haproxy/qmux_trace.h
@@ -0,0 +1,67 @@
+#ifndef _HAPROXY_QMUX_TRACE_H
+#define _HAPROXY_QMUX_TRACE_H
+
+#ifdef USE_QUIC
+
+#include <haproxy/api-t.h>
+#include <haproxy/trace.h>
+
+extern struct trace_source trace_qmux;
+#define TRACE_SOURCE    &trace_qmux
+
+static const struct trace_event qmux_trace_events[] = {
+#define           QMUX_EV_QCC_NEW       (1ULL << 0)
+	{ .mask = QMUX_EV_QCC_NEW ,     .name = "qcc_new",     .desc = "new QUIC connection" },
+#define           QMUX_EV_QCC_RECV      (1ULL << 1)
+	{ .mask = QMUX_EV_QCC_RECV,     .name = "qcc_recv",     .desc = "Rx on QUIC connection" },
+#define           QMUX_EV_QCC_SEND      (1ULL << 2)
+	{ .mask = QMUX_EV_QCC_SEND,     .name = "qcc_send",     .desc = "Tx on QUIC connection" },
+#define           QMUX_EV_QCC_WAKE      (1ULL << 3)
+	{ .mask = QMUX_EV_QCC_WAKE,     .name = "qcc_wake",     .desc = "QUIC connection woken up" },
+#define           QMUX_EV_QCC_END       (1ULL << 4)
+	{ .mask = QMUX_EV_QCC_END,      .name = "qcc_end",      .desc = "QUIC connection terminated" },
+#define           QMUX_EV_QCC_NQCS      (1ULL << 5)
+	{ .mask = QMUX_EV_QCC_NQCS,     .name = "qcc_no_qcs",   .desc = "QUIC stream not found" },
+#define           QMUX_EV_QCS_NEW       (1ULL << 6)
+	{ .mask = QMUX_EV_QCS_NEW,      .name = "qcs_new",      .desc = "new QUIC stream" },
+#define           QMUX_EV_QCS_RECV      (1ULL << 7)
+	{ .mask = QMUX_EV_QCS_RECV,     .name = "qcs_recv",     .desc = "Rx on QUIC stream" },
+#define           QMUX_EV_QCS_SEND      (1ULL << 8)
+	{ .mask = QMUX_EV_QCS_SEND,     .name = "qcs_send",     .desc = "Tx on QUIC stream" },
+#define           QMUX_EV_QCS_END       (1ULL << 9)
+	{ .mask = QMUX_EV_QCS_END,      .name = "qcs_end",      .desc = "QUIC stream terminated" },
+#define           QMUX_EV_STRM_RECV     (1ULL << 10)
+	{ .mask = QMUX_EV_STRM_RECV,    .name = "strm_recv",    .desc = "receiving data for stream" },
+#define           QMUX_EV_STRM_SEND     (1ULL << 11)
+	{ .mask = QMUX_EV_STRM_SEND,    .name = "strm_send",    .desc = "sending data for stream" },
+#define           QMUX_EV_STRM_END      (1ULL << 12)
+	{ .mask = QMUX_EV_STRM_END,     .name = "strm_end",     .desc = "detaching app-layer stream" },
+#define           QMUX_EV_SEND_FRM      (1ULL << 13)
+	{ .mask = QMUX_EV_SEND_FRM,     .name = "send_frm",     .desc = "sending QUIC frame" },
+/* special event dedicated to qcs_xfer_data */
+#define           QMUX_EV_QCS_XFER_DATA  (1ULL << 14)
+	{ .mask = QMUX_EV_QCS_XFER_DATA,  .name = "qcs_xfer_data", .desc = "qcs_xfer_data" },
+/* special event dedicated to qcs_build_stream_frm */
+#define           QMUX_EV_QCS_BUILD_STRM (1ULL << 15)
+	{ .mask = QMUX_EV_QCS_BUILD_STRM, .name = "qcs_build_stream_frm", .desc = "qcs_build_stream_frm" },
+#define           QMUX_EV_PROTO_ERR     (1ULL << 16)
+	{ .mask = QMUX_EV_PROTO_ERR,    .name = "proto_err",    .desc = "protocol error" },
+	{ }
+};
+
+/* custom arg for QMUX_EV_QCS_XFER_DATA */
+struct qcs_xfer_data_trace_arg {
+	size_t prep;
+	int xfer;
+};
+
+/* custom arg for QMUX_EV_QCS_BUILD_STRM */
+struct qcs_build_stream_trace_arg {
+	size_t len;
+	char fin;
+	uint64_t offset;
+};
+
+#endif /* USE_QUIC */
+
+#endif /* _HAPROXY_QMUX_TRACE_H */
diff --git a/src/mux_quic.c b/src/mux_quic.c
index 83672cb..44f449a 100644
--- a/src/mux_quic.c
+++ b/src/mux_quic.c
@@ -9,6 +9,7 @@
 #include <haproxy/list.h>
 #include <haproxy/ncbuf.h>
 #include <haproxy/pool.h>
+#include <haproxy/qmux_trace.h>
 #include <haproxy/quic_stream.h>
 #include <haproxy/quic_tp-t.h>
 #include <haproxy/ssl_sock-t.h>
@@ -19,93 +20,6 @@
 DECLARE_POOL(pool_head_qcc, "qcc", sizeof(struct qcc));
 DECLARE_POOL(pool_head_qcs, "qcs", sizeof(struct qcs));
 
-/* trace source and events */
-static void qmux_trace(enum trace_level level, uint64_t mask,
-                       const struct trace_source *src,
-                       const struct ist where, const struct ist func,
-                       const void *a1, const void *a2, const void *a3, const void *a4);
-
-static const struct trace_event qmux_trace_events[] = {
-#define           QMUX_EV_QCC_NEW       (1ULL << 0)
-	{ .mask = QMUX_EV_QCC_NEW ,     .name = "qcc_new",     .desc = "new QUIC connection" },
-#define           QMUX_EV_QCC_RECV      (1ULL << 1)
-	{ .mask = QMUX_EV_QCC_RECV,     .name = "qcc_recv",     .desc = "Rx on QUIC connection" },
-#define           QMUX_EV_QCC_SEND      (1ULL << 2)
-	{ .mask = QMUX_EV_QCC_SEND,     .name = "qcc_send",     .desc = "Tx on QUIC connection" },
-#define           QMUX_EV_QCC_WAKE      (1ULL << 3)
-	{ .mask = QMUX_EV_QCC_WAKE,     .name = "qcc_wake",     .desc = "QUIC connection woken up" },
-#define           QMUX_EV_QCC_END       (1ULL << 4)
-	{ .mask = QMUX_EV_QCC_END,      .name = "qcc_end",      .desc = "QUIC connection terminated" },
-#define           QMUX_EV_QCC_NQCS      (1ULL << 5)
-	{ .mask = QMUX_EV_QCC_NQCS,     .name = "qcc_no_qcs",   .desc = "QUIC stream not found" },
-#define           QMUX_EV_QCS_NEW       (1ULL << 6)
-	{ .mask = QMUX_EV_QCS_NEW,      .name = "qcs_new",      .desc = "new QUIC stream" },
-#define           QMUX_EV_QCS_RECV      (1ULL << 7)
-	{ .mask = QMUX_EV_QCS_RECV,     .name = "qcs_recv",     .desc = "Rx on QUIC stream" },
-#define           QMUX_EV_QCS_SEND      (1ULL << 8)
-	{ .mask = QMUX_EV_QCS_SEND,     .name = "qcs_send",     .desc = "Tx on QUIC stream" },
-#define           QMUX_EV_QCS_END       (1ULL << 9)
-	{ .mask = QMUX_EV_QCS_END,      .name = "qcs_end",      .desc = "QUIC stream terminated" },
-#define           QMUX_EV_STRM_RECV     (1ULL << 10)
-	{ .mask = QMUX_EV_STRM_RECV,    .name = "strm_recv",    .desc = "receiving data for stream" },
-#define           QMUX_EV_STRM_SEND     (1ULL << 11)
-	{ .mask = QMUX_EV_STRM_SEND,    .name = "strm_send",    .desc = "sending data for stream" },
-#define           QMUX_EV_STRM_END      (1ULL << 12)
-	{ .mask = QMUX_EV_STRM_END,     .name = "strm_end",     .desc = "detaching app-layer stream" },
-#define           QMUX_EV_SEND_FRM      (1ULL << 13)
-	{ .mask = QMUX_EV_SEND_FRM,     .name = "send_frm",     .desc = "sending QUIC frame" },
-/* special event dedicated to qcs_xfer_data */
-#define           QMUX_EV_QCS_XFER_DATA  (1ULL << 14)
-	{ .mask = QMUX_EV_QCS_XFER_DATA,  .name = "qcs_xfer_data", .desc = "qcs_xfer_data" },
-/* special event dedicated to qcs_build_stream_frm */
-#define           QMUX_EV_QCS_BUILD_STRM (1ULL << 15)
-	{ .mask = QMUX_EV_QCS_BUILD_STRM, .name = "qcs_build_stream_frm", .desc = "qcs_build_stream_frm" },
-#define           QMUX_EV_PROTO_ERR     (1ULL << 16)
-	{ .mask = QMUX_EV_PROTO_ERR,    .name = "proto_err",    .desc = "protocol error" },
-	{ }
-};
-
-/* custom arg for QMUX_EV_QCS_XFER_DATA */
-struct qcs_xfer_data_trace_arg {
-	size_t prep;
-	int xfer;
-};
-/* custom arg for QMUX_EV_QCS_BUILD_STRM */
-struct qcs_build_stream_trace_arg {
-	size_t len;
-	char fin;
-	uint64_t offset;
-};
-
-static const struct name_desc qmux_trace_lockon_args[4] = {
-	/* arg1 */ { /* already used by the connection */ },
-	/* arg2 */ { .name="qcs", .desc="QUIC stream" },
-	/* arg3 */ { },
-	/* arg4 */ { }
-};
-
-static const struct name_desc qmux_trace_decoding[] = {
-#define QMUX_VERB_CLEAN    1
-	{ .name="clean",    .desc="only user-friendly stuff, generally suitable for level \"user\"" },
-#define QMUX_VERB_MINIMAL  2
-	{ .name="minimal",  .desc="report only qcc/qcs state and flags, no real decoding" },
-	{ /* end */ }
-};
-
-struct trace_source trace_qmux = {
-	.name = IST("qmux"),
-	.desc = "QUIC multiplexer",
-	.arg_def = TRC_ARG1_CONN,  /* TRACE()'s first argument is always a connection */
-	.default_cb = qmux_trace,
-	.known_events = qmux_trace_events,
-	.lockon_args = qmux_trace_lockon_args,
-	.decoding = qmux_trace_decoding,
-	.report_events = ~0,  /* report everything by default */
-};
-
-#define TRACE_SOURCE    &trace_qmux
-INITCALL1(STG_REGISTER, trace_register_source, TRACE_SOURCE);
-
 /* Emit a CONNECTION_CLOSE with error <err>. This will interrupt all future
  * send/receive operations.
  */
@@ -2341,18 +2255,6 @@
 }
 
 
-static char *qcs_st_to_str(enum qcs_state st)
-{
-	switch (st) {
-	case QC_SS_IDLE: return "IDL";
-	case QC_SS_OPEN: return "OPN";
-	case QC_SS_HLOC: return "HCL";
-	case QC_SS_HREM: return "HCR";
-	case QC_SS_CLO:  return "CLO";
-	default:         return "???";
-	}
-}
-
 /* for debugging with CLI's "show sess" command. May emit multiple lines, each
  * new one being prefixed with <pfx>, if <pfx> is not NULL, otherwise a single
  * line is used. Each field starts with a space so it's safe to print it after
@@ -2380,70 +2282,6 @@
 }
 
 
-static void qmux_trace_frm(const struct quic_frame *frm)
-{
-	switch (frm->type) {
-	case QUIC_FT_MAX_STREAMS_BIDI:
-		chunk_appendf(&trace_buf, " max_streams=%llu",
-		              (ull)frm->max_streams_bidi.max_streams);
-		break;
-
-	case QUIC_FT_MAX_STREAMS_UNI:
-		chunk_appendf(&trace_buf, " max_streams=%llu",
-		              (ull)frm->max_streams_uni.max_streams);
-		break;
-
-	default:
-		break;
-	}
-}
-
-/* quic-mux trace handler */
-static void qmux_trace(enum trace_level level, uint64_t mask,
-                       const struct trace_source *src,
-                       const struct ist where, const struct ist func,
-                       const void *a1, const void *a2, const void *a3, const void *a4)
-{
-	const struct connection *conn = a1;
-	const struct qcc *qcc   = conn ? conn->ctx : NULL;
-	const struct qcs *qcs   = a2;
-
-	if (!qcc)
-		return;
-
-	if (src->verbosity > QMUX_VERB_CLEAN) {
-		chunk_appendf(&trace_buf, " : qcc=%p(F)", qcc);
-		if (qcc->conn->handle.qc)
-			chunk_appendf(&trace_buf, " qc=%p", qcc->conn->handle.qc);
-
-		if (qcs)
-			chunk_appendf(&trace_buf, " qcs=%p .id=%llu .st=%s",
-			              qcs, (ull)qcs->id,
-			              qcs_st_to_str(qcs->st));
-
-		if (mask & QMUX_EV_QCC_NQCS) {
-			const uint64_t *id = a3;
-			chunk_appendf(&trace_buf, " id=%llu", (ull)*id);
-		}
-
-		if (mask & QMUX_EV_SEND_FRM)
-			qmux_trace_frm(a3);
-
-		if (mask & QMUX_EV_QCS_XFER_DATA) {
-			const struct qcs_xfer_data_trace_arg *arg = a3;
-			chunk_appendf(&trace_buf, " prep=%llu xfer=%d",
-			              (ull)arg->prep, arg->xfer);
-		}
-
-		if (mask & QMUX_EV_QCS_BUILD_STRM) {
-			const struct qcs_build_stream_trace_arg *arg = a3;
-			chunk_appendf(&trace_buf, " len=%llu fin=%d offset=%llu",
-			              (ull)arg->len, arg->fin, (ull)arg->offset);
-		}
-	}
-}
-
-
 static const struct mux_ops qc_ops = {
 	.init = qc_init,
 	.destroy = qc_destroy,
diff --git a/src/qmux_trace.c b/src/qmux_trace.c
new file mode 100644
index 0000000..6d84b7a
--- /dev/null
+++ b/src/qmux_trace.c
@@ -0,0 +1,107 @@
+#include <haproxy/qmux_trace.h>
+
+#include <import/ist.h>
+#include <haproxy/api.h>
+#include <haproxy/connection.h>
+#include <haproxy/chunk.h>
+#include <haproxy/mux_quic.h>
+
+/* trace source and events */
+static void qmux_trace(enum trace_level level, uint64_t mask,
+                       const struct trace_source *src,
+                       const struct ist where, const struct ist func,
+                       const void *a1, const void *a2, const void *a3, const void *a4);
+
+static const struct name_desc qmux_trace_lockon_args[4] = {
+	/* arg1 */ { /* already used by the connection */ },
+	/* arg2 */ { .name="qcs", .desc="QUIC stream" },
+	/* arg3 */ { },
+	/* arg4 */ { }
+};
+
+static const struct name_desc qmux_trace_decoding[] = {
+#define QMUX_VERB_CLEAN    1
+	{ .name="clean",    .desc="only user-friendly stuff, generally suitable for level \"user\"" },
+#define QMUX_VERB_MINIMAL  2
+	{ .name="minimal",  .desc="report only qcc/qcs state and flags, no real decoding" },
+	{ /* end */ }
+};
+
+struct trace_source trace_qmux = {
+	.name = IST("qmux"),
+	.desc = "QUIC multiplexer",
+	.arg_def = TRC_ARG1_CONN,  /* TRACE()'s first argument is always a connection */
+	.default_cb = qmux_trace,
+	.known_events = qmux_trace_events,
+	.lockon_args = qmux_trace_lockon_args,
+	.decoding = qmux_trace_decoding,
+	.report_events = ~0,  /* report everything by default */
+};
+
+
+static void qmux_trace_frm(const struct quic_frame *frm)
+{
+	switch (frm->type) {
+	case QUIC_FT_MAX_STREAMS_BIDI:
+		chunk_appendf(&trace_buf, " max_streams=%llu",
+		              (ull)frm->max_streams_bidi.max_streams);
+		break;
+
+	case QUIC_FT_MAX_STREAMS_UNI:
+		chunk_appendf(&trace_buf, " max_streams=%llu",
+		              (ull)frm->max_streams_uni.max_streams);
+		break;
+
+	default:
+		break;
+	}
+}
+
+/* quic-mux trace handler */
+static void qmux_trace(enum trace_level level, uint64_t mask,
+                       const struct trace_source *src,
+                       const struct ist where, const struct ist func,
+                       const void *a1, const void *a2, const void *a3, const void *a4)
+{
+	const struct connection *conn = a1;
+	const struct qcc *qcc   = conn ? conn->ctx : NULL;
+	const struct qcs *qcs   = a2;
+
+	if (!qcc)
+		return;
+
+	if (src->verbosity > QMUX_VERB_CLEAN) {
+		chunk_appendf(&trace_buf, " : qcc=%p(F)", qcc);
+		if (qcc->conn->handle.qc)
+			chunk_appendf(&trace_buf, " qc=%p", qcc->conn->handle.qc);
+
+		if (qcs)
+			chunk_appendf(&trace_buf, " qcs=%p .id=%llu .st=%s",
+			              qcs, (ull)qcs->id,
+			              qcs_st_to_str(qcs->st));
+
+		if (mask & QMUX_EV_QCC_NQCS) {
+			const uint64_t *id = a3;
+			chunk_appendf(&trace_buf, " id=%llu", (ull)*id);
+		}
+
+		if (mask & QMUX_EV_SEND_FRM)
+			qmux_trace_frm(a3);
+
+		if (mask & QMUX_EV_QCS_XFER_DATA) {
+			const struct qcs_xfer_data_trace_arg *arg = a3;
+			chunk_appendf(&trace_buf, " prep=%llu xfer=%d",
+			              (ull)arg->prep, arg->xfer);
+		}
+
+		if (mask & QMUX_EV_QCS_BUILD_STRM) {
+			const struct qcs_build_stream_trace_arg *arg = a3;
+			chunk_appendf(&trace_buf, " len=%llu fin=%d offset=%llu",
+			              (ull)arg->len, arg->fin, (ull)arg->offset);
+		}
+	}
+}
+
+
+/* register qmux traces */
+INITCALL1(STG_REGISTER, trace_register_source, TRACE_SOURCE);