]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: stream: split stats_dump_full_strm_to_buffer() in two
authorWilly Tarreau <w@1wt.eu>
Wed, 27 Sep 2023 06:17:17 +0000 (08:17 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 29 Sep 2023 07:20:27 +0000 (09:20 +0200)
The function only works with the CLI's appctx and does most of the
convenient work of dumping a stream into a buffer (well, the trash
buffer for now). Let's split it in two so that most of the work is
done in a generic function and that the CLI-specific function relies
on that one.

The diff looks huge due to the changed indent caused by the extraction
of the switch/case statement, but when looked at using diff -b it's
small.

src/stream.c

index 6e6439e870db571f3e0ed212b613b11f77e82ec1..6696240bb0e06b8250fe17dd433a333279bb42ee 100644 (file)
@@ -3238,327 +3238,337 @@ struct show_sess_ctx {
        int pos;                /* last position of the current session's buffer */
 };
 
-/* This function dumps a complete stream state onto the stream connector's
- * read buffer. The stream has to be set in strm. It returns 0 if the output
- * buffer is full and it needs to be called again, otherwise non-zero. It is
- * designed to be called from stats_dump_strm_to_buffer() below.
+/* This function appends a complete dump of a stream state onto the trash
+ * buffer, possibly anonymizing using the specified anon_key. The caller is
+ * responsible for ensuring that enough room remains in the buffer to dump a
+ * complete stream at once.
  */
-static int stats_dump_full_strm_to_buffer(struct stconn *sc, struct stream *strm)
+static void strm_dump_to_buffer(struct stream *strm, uint32_t anon_key)
 {
-       struct appctx *appctx = __sc_appctx(sc);
-       struct show_sess_ctx *ctx = appctx->svcctx;
        struct stconn *scf, *scb;
        struct tm tm;
        extern const char *monthname[12];
        char pn[INET6_ADDRSTRLEN];
        struct connection *conn;
        struct appctx *tmpctx;
-       uint32_t anon_key = appctx->cli_anon_key;
-
-       chunk_reset(&trash);
 
-       if (ctx->section > 0 && ctx->uid != strm->uniq_id) {
-               /* stream changed, no need to go any further */
-               chunk_appendf(&trash, "  *** session terminated while we were watching it ***\n");
-               if (applet_putchk(appctx, &trash) == -1)
-                       goto full;
-               goto done;
+       get_localtime(strm->logs.accept_date.tv_sec, &tm);
+       chunk_appendf(&trash,
+                    "%p: [%02d/%s/%04d:%02d:%02d:%02d.%06d] id=%u proto=%s",
+                    strm,
+                    tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
+                    tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(strm->logs.accept_date.tv_usec),
+                    strm->uniq_id,
+                    strm_li(strm) ? strm_li(strm)->rx.proto->name : "?");
+
+       conn = objt_conn(strm_orig(strm));
+       switch (conn && conn_get_src(conn) ? addr_to_str(conn->src, pn, sizeof(pn)) : AF_UNSPEC) {
+       case AF_INET:
+       case AF_INET6:
+               chunk_appendf(&trash, " source=%s:%d\n",
+                             HA_ANON_STR(anon_key, pn), get_host_port(conn->src));
+               break;
+       case AF_UNIX:
+               chunk_appendf(&trash, " source=unix:%d\n", strm_li(strm)->luid);
+               break;
+       default:
+               /* no more information to print right now */
+               chunk_appendf(&trash, "\n");
+               break;
+       }
+
+       chunk_appendf(&trash,
+                    "  flags=0x%x, conn_retries=%d, conn_exp=%s conn_et=0x%03x srv_conn=%p, pend_pos=%p waiting=%d epoch=%#x\n",
+                    strm->flags, strm->conn_retries,
+                    strm->conn_exp ?
+                            tick_is_expired(strm->conn_exp, now_ms) ? "<PAST>" :
+                                    human_time(TICKS_TO_MS(strm->conn_exp - now_ms),
+                                    TICKS_TO_MS(1000)) : "<NEVER>",
+                    strm->conn_err_type, strm->srv_conn, strm->pend_pos,
+                    LIST_INLIST(&strm->buffer_wait.list), strm->stream_epoch);
+
+       chunk_appendf(&trash,
+                    "  frontend=%s (id=%u mode=%s), listener=%s (id=%u)",
+                    HA_ANON_STR(anon_key, strm_fe(strm)->id), strm_fe(strm)->uuid, proxy_mode_str(strm_fe(strm)->mode),
+                    strm_li(strm) ? strm_li(strm)->name ? strm_li(strm)->name : "?" : "?",
+                    strm_li(strm) ? strm_li(strm)->luid : 0);
+
+       switch (conn && conn_get_dst(conn) ? addr_to_str(conn->dst, pn, sizeof(pn)) : AF_UNSPEC) {
+       case AF_INET:
+       case AF_INET6:
+               chunk_appendf(&trash, " addr=%s:%d\n",
+                            HA_ANON_STR(anon_key, pn), get_host_port(conn->dst));
+               break;
+       case AF_UNIX:
+               chunk_appendf(&trash, " addr=unix:%d\n", strm_li(strm)->luid);
+               break;
+       default:
+               /* no more information to print right now */
+               chunk_appendf(&trash, "\n");
+               break;
        }
 
-       switch (ctx->section) {
-       case 0: /* main status of the stream */
-               ctx->uid = strm->uniq_id;
-               ctx->section = 1;
-               __fallthrough;
-
-       case 1:
-               get_localtime(strm->logs.accept_date.tv_sec, &tm);
+       if (strm->be->cap & PR_CAP_BE)
                chunk_appendf(&trash,
-                            "%p: [%02d/%s/%04d:%02d:%02d:%02d.%06d] id=%u proto=%s",
-                            strm,
-                            tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
-                            tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(strm->logs.accept_date.tv_usec),
-                            strm->uniq_id,
-                            strm_li(strm) ? strm_li(strm)->rx.proto->name : "?");
-
-               conn = objt_conn(strm_orig(strm));
-               switch (conn && conn_get_src(conn) ? addr_to_str(conn->src, pn, sizeof(pn)) : AF_UNSPEC) {
-               case AF_INET:
-               case AF_INET6:
-                       chunk_appendf(&trash, " source=%s:%d\n",
-                                     HA_ANON_STR(anon_key, pn), get_host_port(conn->src));
-                       break;
-               case AF_UNIX:
-                       chunk_appendf(&trash, " source=unix:%d\n", strm_li(strm)->luid);
-                       break;
-               default:
-                       /* no more information to print right now */
-                       chunk_appendf(&trash, "\n");
-                       break;
-               }
+                            "  backend=%s (id=%u mode=%s)",
+                            HA_ANON_STR(anon_key, strm->be->id),
+                            strm->be->uuid, proxy_mode_str(strm->be->mode));
+       else
+               chunk_appendf(&trash, "  backend=<NONE> (id=-1 mode=-)");
+
+       conn = sc_conn(strm->scb);
+       switch (conn && conn_get_src(conn) ? addr_to_str(conn->src, pn, sizeof(pn)) : AF_UNSPEC) {
+       case AF_INET:
+       case AF_INET6:
+               chunk_appendf(&trash, " addr=%s:%d\n",
+                            HA_ANON_STR(anon_key, pn), get_host_port(conn->src));
+               break;
+       case AF_UNIX:
+               chunk_appendf(&trash, " addr=unix\n");
+               break;
+       default:
+               /* no more information to print right now */
+               chunk_appendf(&trash, "\n");
+               break;
+       }
 
+       if (strm->be->cap & PR_CAP_BE)
                chunk_appendf(&trash,
-                            "  flags=0x%x, conn_retries=%d, conn_exp=%s conn_et=0x%03x srv_conn=%p, pend_pos=%p waiting=%d epoch=%#x\n",
-                            strm->flags, strm->conn_retries,
-                            strm->conn_exp ?
-                                    tick_is_expired(strm->conn_exp, now_ms) ? "<PAST>" :
-                                            human_time(TICKS_TO_MS(strm->conn_exp - now_ms),
-                                            TICKS_TO_MS(1000)) : "<NEVER>",
-                            strm->conn_err_type, strm->srv_conn, strm->pend_pos,
-                            LIST_INLIST(&strm->buffer_wait.list), strm->stream_epoch);
-
+                            "  server=%s (id=%u)",
+                            objt_server(strm->target) ? HA_ANON_STR(anon_key, __objt_server(strm->target)->id) : "<none>",
+                            objt_server(strm->target) ? __objt_server(strm->target)->puid : 0);
+       else
+               chunk_appendf(&trash, "  server=<NONE> (id=-1)");
+
+       switch (conn && conn_get_dst(conn) ? addr_to_str(conn->dst, pn, sizeof(pn)) : AF_UNSPEC) {
+       case AF_INET:
+       case AF_INET6:
+               chunk_appendf(&trash, " addr=%s:%d\n",
+                            HA_ANON_STR(anon_key, pn), get_host_port(conn->dst));
+               break;
+       case AF_UNIX:
+               chunk_appendf(&trash, " addr=unix\n");
+               break;
+       default:
+               /* no more information to print right now */
+               chunk_appendf(&trash, "\n");
+               break;
+       }
+
+       chunk_appendf(&trash,
+                    "  task=%p (state=0x%02x nice=%d calls=%u rate=%u exp=%s tid=%d(%d/%d)%s",
+                    strm->task,
+                    strm->task->state,
+                    strm->task->nice, strm->task->calls, read_freq_ctr(&strm->call_rate),
+                    strm->task->expire ?
+                            tick_is_expired(strm->task->expire, now_ms) ? "<PAST>" :
+                                    human_time(TICKS_TO_MS(strm->task->expire - now_ms),
+                                    TICKS_TO_MS(1000)) : "<NEVER>",
+                    strm->task->tid,
+                    ha_thread_info[strm->task->tid].tgid,
+                    ha_thread_info[strm->task->tid].ltid,
+                    task_in_rq(strm->task) ? ", running" : "");
+
+       chunk_appendf(&trash,
+                    " age=%s)\n",
+                    human_time(ns_to_sec(now_ns) - ns_to_sec(strm->logs.accept_ts), 1));
+
+       if (strm->txn)
                chunk_appendf(&trash,
-                            "  frontend=%s (id=%u mode=%s), listener=%s (id=%u)",
-                            HA_ANON_STR(anon_key, strm_fe(strm)->id), strm_fe(strm)->uuid, proxy_mode_str(strm_fe(strm)->mode),
-                            strm_li(strm) ? strm_li(strm)->name ? strm_li(strm)->name : "?" : "?",
-                            strm_li(strm) ? strm_li(strm)->luid : 0);
-
-               switch (conn && conn_get_dst(conn) ? addr_to_str(conn->dst, pn, sizeof(pn)) : AF_UNSPEC) {
-               case AF_INET:
-               case AF_INET6:
-                       chunk_appendf(&trash, " addr=%s:%d\n",
-                                    HA_ANON_STR(anon_key, pn), get_host_port(conn->dst));
-                       break;
-               case AF_UNIX:
-                       chunk_appendf(&trash, " addr=unix:%d\n", strm_li(strm)->luid);
-                       break;
-               default:
-                       /* no more information to print right now */
+                     "  txn=%p flags=0x%x meth=%d status=%d req.st=%s rsp.st=%s req.f=0x%02x rsp.f=0x%02x\n",
+                     strm->txn, strm->txn->flags, strm->txn->meth, strm->txn->status,
+                     h1_msg_state_str(strm->txn->req.msg_state), h1_msg_state_str(strm->txn->rsp.msg_state),
+                     strm->txn->req.flags, strm->txn->rsp.flags);
+
+       scf = strm->scf;
+       chunk_appendf(&trash, "  scf=%p flags=0x%08x state=%s endp=%s,%p,0x%08x sub=%d",
+                     scf, scf->flags, sc_state_str(scf->state),
+                     (sc_ep_test(scf, SE_FL_T_MUX) ? "CONN" : (sc_ep_test(scf, SE_FL_T_APPLET) ? "APPCTX" : "NONE")),
+                     scf->sedesc->se, sc_ep_get(scf), scf->wait_event.events);
+       chunk_appendf(&trash, " rex=%s",
+                     sc_ep_rcv_ex(scf) ? human_time(TICKS_TO_MS(sc_ep_rcv_ex(scf) - now_ms), TICKS_TO_MS(1000)) : "<NEVER>");
+       chunk_appendf(&trash, " wex=%s\n",
+                     sc_ep_snd_ex(scf) ? human_time(TICKS_TO_MS(sc_ep_snd_ex(scf) - now_ms), TICKS_TO_MS(1000)) : "<NEVER>");
+
+       if ((conn = sc_conn(scf)) != NULL) {
+               if (conn->mux && conn->mux->show_sd) {
+                       chunk_appendf(&trash, "     ");
+                       conn->mux->show_sd(&trash, scf->sedesc, "     ");
                        chunk_appendf(&trash, "\n");
-                       break;
-               }
-
-               if (strm->be->cap & PR_CAP_BE)
-                       chunk_appendf(&trash,
-                                    "  backend=%s (id=%u mode=%s)",
-                                    HA_ANON_STR(anon_key, strm->be->id),
-                                    strm->be->uuid, proxy_mode_str(strm->be->mode));
-               else
-                       chunk_appendf(&trash, "  backend=<NONE> (id=-1 mode=-)");
-
-               conn = sc_conn(strm->scb);
-               switch (conn && conn_get_src(conn) ? addr_to_str(conn->src, pn, sizeof(pn)) : AF_UNSPEC) {
-               case AF_INET:
-               case AF_INET6:
-                       chunk_appendf(&trash, " addr=%s:%d\n",
-                                    HA_ANON_STR(anon_key, pn), get_host_port(conn->src));
-                       break;
-               case AF_UNIX:
-                       chunk_appendf(&trash, " addr=unix\n");
-                       break;
-               default:
-                       /* no more information to print right now */
-                       chunk_appendf(&trash, "\n");
-                       break;
                }
 
-               if (strm->be->cap & PR_CAP_BE)
-                       chunk_appendf(&trash,
-                                    "  server=%s (id=%u)",
-                                    objt_server(strm->target) ? HA_ANON_STR(anon_key, __objt_server(strm->target)->id) : "<none>",
-                                    objt_server(strm->target) ? __objt_server(strm->target)->puid : 0);
-               else
-                       chunk_appendf(&trash, "  server=<NONE> (id=-1)");
+               chunk_appendf(&trash,
+                             "      co0=%p ctrl=%s xprt=%s mux=%s data=%s target=%s:%p\n",
+                             conn,
+                             conn_get_ctrl_name(conn),
+                             conn_get_xprt_name(conn),
+                             conn_get_mux_name(conn),
+                             sc_get_data_name(scf),
+                             obj_type_name(conn->target),
+                             obj_base_ptr(conn->target));
 
-               switch (conn && conn_get_dst(conn) ? addr_to_str(conn->dst, pn, sizeof(pn)) : AF_UNSPEC) {
-               case AF_INET:
-               case AF_INET6:
-                       chunk_appendf(&trash, " addr=%s:%d\n",
-                                    HA_ANON_STR(anon_key, pn), get_host_port(conn->dst));
-                       break;
-               case AF_UNIX:
-                       chunk_appendf(&trash, " addr=unix\n");
-                       break;
-               default:
-                       /* no more information to print right now */
+               chunk_appendf(&trash,
+                             "      flags=0x%08x fd=%d fd.state=%02x updt=%d fd.tmask=0x%lx\n",
+                             conn->flags,
+                             conn_fd(conn),
+                             conn_fd(conn) >= 0 ? fdtab[conn->handle.fd].state : 0,
+                             conn_fd(conn) >= 0 ? !!(fdtab[conn->handle.fd].update_mask & ti->ltid_bit) : 0,
+                             conn_fd(conn) >= 0 ? fdtab[conn->handle.fd].thread_mask: 0);
+       }
+       else if ((tmpctx = sc_appctx(scf)) != NULL) {
+               chunk_appendf(&trash,
+                             "      app0=%p st0=%d st1=%d applet=%s tid=%d nice=%d calls=%u rate=%u\n",
+                             tmpctx,
+                             tmpctx->st0,
+                             tmpctx->st1,
+                             tmpctx->applet->name,
+                             tmpctx->t->tid,
+                             tmpctx->t->nice, tmpctx->t->calls, read_freq_ctr(&tmpctx->call_rate));
+       }
+
+       scb = strm->scb;
+       chunk_appendf(&trash, "  scb=%p flags=0x%08x state=%s endp=%s,%p,0x%08x sub=%d",
+                     scb, scb->flags, sc_state_str(scb->state),
+                     (sc_ep_test(scb, SE_FL_T_MUX) ? "CONN" : (sc_ep_test(scb, SE_FL_T_APPLET) ? "APPCTX" : "NONE")),
+                     scb->sedesc->se, sc_ep_get(scb), scb->wait_event.events);
+       chunk_appendf(&trash, " rex=%s",
+                     sc_ep_rcv_ex(scb) ? human_time(TICKS_TO_MS(sc_ep_rcv_ex(scb) - now_ms), TICKS_TO_MS(1000)) : "<NEVER>");
+       chunk_appendf(&trash, " wex=%s\n",
+                     sc_ep_snd_ex(scb) ? human_time(TICKS_TO_MS(sc_ep_snd_ex(scb) - now_ms), TICKS_TO_MS(1000)) : "<NEVER>");
+
+       if ((conn = sc_conn(scb)) != NULL) {
+               if (conn->mux && conn->mux->show_sd) {
+                       chunk_appendf(&trash, "     ");
+                       conn->mux->show_sd(&trash, scb->sedesc, "     ");
                        chunk_appendf(&trash, "\n");
-                       break;
                }
 
                chunk_appendf(&trash,
-                            "  task=%p (state=0x%02x nice=%d calls=%u rate=%u exp=%s tid=%d(%d/%d)%s",
-                            strm->task,
-                            strm->task->state,
-                            strm->task->nice, strm->task->calls, read_freq_ctr(&strm->call_rate),
-                            strm->task->expire ?
-                                    tick_is_expired(strm->task->expire, now_ms) ? "<PAST>" :
-                                            human_time(TICKS_TO_MS(strm->task->expire - now_ms),
-                                            TICKS_TO_MS(1000)) : "<NEVER>",
-                            strm->task->tid,
-                            ha_thread_info[strm->task->tid].tgid,
-                            ha_thread_info[strm->task->tid].ltid,
-                            task_in_rq(strm->task) ? ", running" : "");
+                             "      co1=%p ctrl=%s xprt=%s mux=%s data=%s target=%s:%p\n",
+                             conn,
+                             conn_get_ctrl_name(conn),
+                             conn_get_xprt_name(conn),
+                             conn_get_mux_name(conn),
+                             sc_get_data_name(scb),
+                             obj_type_name(conn->target),
+                             obj_base_ptr(conn->target));
 
                chunk_appendf(&trash,
-                            " age=%s)\n",
-                            human_time(ns_to_sec(now_ns) - ns_to_sec(strm->logs.accept_ts), 1));
-
-               if (strm->txn)
-                       chunk_appendf(&trash,
-                             "  txn=%p flags=0x%x meth=%d status=%d req.st=%s rsp.st=%s req.f=0x%02x rsp.f=0x%02x\n",
-                             strm->txn, strm->txn->flags, strm->txn->meth, strm->txn->status,
-                             h1_msg_state_str(strm->txn->req.msg_state), h1_msg_state_str(strm->txn->rsp.msg_state),
-                             strm->txn->req.flags, strm->txn->rsp.flags);
-
-               scf = strm->scf;
-               chunk_appendf(&trash, "  scf=%p flags=0x%08x state=%s endp=%s,%p,0x%08x sub=%d",
-                             scf, scf->flags, sc_state_str(scf->state),
-                             (sc_ep_test(scf, SE_FL_T_MUX) ? "CONN" : (sc_ep_test(scf, SE_FL_T_APPLET) ? "APPCTX" : "NONE")),
-                             scf->sedesc->se, sc_ep_get(scf), scf->wait_event.events);
-               chunk_appendf(&trash, " rex=%s",
-                             sc_ep_rcv_ex(scf) ? human_time(TICKS_TO_MS(sc_ep_rcv_ex(scf) - now_ms), TICKS_TO_MS(1000)) : "<NEVER>");
-               chunk_appendf(&trash, " wex=%s\n",
-                             sc_ep_snd_ex(scf) ? human_time(TICKS_TO_MS(sc_ep_snd_ex(scf) - now_ms), TICKS_TO_MS(1000)) : "<NEVER>");
-
-               if ((conn = sc_conn(scf)) != NULL) {
-                       if (conn->mux && conn->mux->show_sd) {
-                               chunk_appendf(&trash, "     ");
-                               conn->mux->show_sd(&trash, scf->sedesc, "     ");
-                               chunk_appendf(&trash, "\n");
-                       }
-
-                       chunk_appendf(&trash,
-                                     "      co0=%p ctrl=%s xprt=%s mux=%s data=%s target=%s:%p\n",
-                                     conn,
-                                     conn_get_ctrl_name(conn),
-                                     conn_get_xprt_name(conn),
-                                     conn_get_mux_name(conn),
-                                     sc_get_data_name(scf),
-                                     obj_type_name(conn->target),
-                                     obj_base_ptr(conn->target));
-
-                       chunk_appendf(&trash,
-                                     "      flags=0x%08x fd=%d fd.state=%02x updt=%d fd.tmask=0x%lx\n",
-                                     conn->flags,
-                                     conn_fd(conn),
-                                     conn_fd(conn) >= 0 ? fdtab[conn->handle.fd].state : 0,
-                                     conn_fd(conn) >= 0 ? !!(fdtab[conn->handle.fd].update_mask & ti->ltid_bit) : 0,
-                                     conn_fd(conn) >= 0 ? fdtab[conn->handle.fd].thread_mask: 0);
-               }
-               else if ((tmpctx = sc_appctx(scf)) != NULL) {
-                       chunk_appendf(&trash,
-                                     "      app0=%p st0=%d st1=%d applet=%s tid=%d nice=%d calls=%u rate=%u\n",
-                                     tmpctx,
-                                     tmpctx->st0,
-                                     tmpctx->st1,
-                                     tmpctx->applet->name,
-                                     tmpctx->t->tid,
-                                     tmpctx->t->nice, tmpctx->t->calls, read_freq_ctr(&tmpctx->call_rate));
-               }
-
-               scb = strm->scb;
-               chunk_appendf(&trash, "  scb=%p flags=0x%08x state=%s endp=%s,%p,0x%08x sub=%d",
-                             scb, scb->flags, sc_state_str(scb->state),
-                             (sc_ep_test(scb, SE_FL_T_MUX) ? "CONN" : (sc_ep_test(scb, SE_FL_T_APPLET) ? "APPCTX" : "NONE")),
-                             scb->sedesc->se, sc_ep_get(scb), scb->wait_event.events);
-               chunk_appendf(&trash, " rex=%s",
-                             sc_ep_rcv_ex(scb) ? human_time(TICKS_TO_MS(sc_ep_rcv_ex(scb) - now_ms), TICKS_TO_MS(1000)) : "<NEVER>");
-               chunk_appendf(&trash, " wex=%s\n",
-                             sc_ep_snd_ex(scb) ? human_time(TICKS_TO_MS(sc_ep_snd_ex(scb) - now_ms), TICKS_TO_MS(1000)) : "<NEVER>");
-
-               if ((conn = sc_conn(scb)) != NULL) {
-                       if (conn->mux && conn->mux->show_sd) {
-                               chunk_appendf(&trash, "     ");
-                               conn->mux->show_sd(&trash, scb->sedesc, "     ");
-                               chunk_appendf(&trash, "\n");
-                       }
-
-                       chunk_appendf(&trash,
-                                     "      co1=%p ctrl=%s xprt=%s mux=%s data=%s target=%s:%p\n",
-                                     conn,
-                                     conn_get_ctrl_name(conn),
-                                     conn_get_xprt_name(conn),
-                                     conn_get_mux_name(conn),
-                                     sc_get_data_name(scb),
-                                     obj_type_name(conn->target),
-                                     obj_base_ptr(conn->target));
+                             "      flags=0x%08x fd=%d fd.state=%02x updt=%d fd.tmask=0x%lx\n",
+                             conn->flags,
+                             conn_fd(conn),
+                             conn_fd(conn) >= 0 ? fdtab[conn->handle.fd].state : 0,
+                             conn_fd(conn) >= 0 ? !!(fdtab[conn->handle.fd].update_mask & ti->ltid_bit) : 0,
+                             conn_fd(conn) >= 0 ? fdtab[conn->handle.fd].thread_mask: 0);
+       }
+       else if ((tmpctx = sc_appctx(scb)) != NULL) {
+               chunk_appendf(&trash,
+                             "      app1=%p st0=%d st1=%d applet=%s tid=%d nice=%d calls=%u rate=%u\n",
+                             tmpctx,
+                             tmpctx->st0,
+                             tmpctx->st1,
+                             tmpctx->applet->name,
+                             tmpctx->t->tid,
+                             tmpctx->t->nice, tmpctx->t->calls, read_freq_ctr(&tmpctx->call_rate));
+       }
+
+       chunk_appendf(&trash,
+                    "  req=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
+                    "      an_exp=%s buf=%p data=%p o=%u p=%u i=%u size=%u\n",
+                    &strm->req,
+                    strm->req.flags, strm->req.analysers,
+                    strm->req.pipe ? strm->req.pipe->data : 0,
+                    strm->req.to_forward, strm->req.total,
+                    strm->req.analyse_exp ?
+                    human_time(TICKS_TO_MS(strm->req.analyse_exp - now_ms),
+                               TICKS_TO_MS(1000)) : "<NEVER>",
+                    &strm->req.buf,
+                    b_orig(&strm->req.buf), (unsigned int)co_data(&strm->req),
+                    (unsigned int)ci_head_ofs(&strm->req), (unsigned int)ci_data(&strm->req),
+                    (unsigned int)strm->req.buf.size);
+
+       if (IS_HTX_STRM(strm)) {
+               struct htx *htx = htxbuf(&strm->req.buf);
 
-                       chunk_appendf(&trash,
-                                     "      flags=0x%08x fd=%d fd.state=%02x updt=%d fd.tmask=0x%lx\n",
-                                     conn->flags,
-                                     conn_fd(conn),
-                                     conn_fd(conn) >= 0 ? fdtab[conn->handle.fd].state : 0,
-                                     conn_fd(conn) >= 0 ? !!(fdtab[conn->handle.fd].update_mask & ti->ltid_bit) : 0,
-                                     conn_fd(conn) >= 0 ? fdtab[conn->handle.fd].thread_mask: 0);
-               }
-               else if ((tmpctx = sc_appctx(scb)) != NULL) {
-                       chunk_appendf(&trash,
-                                     "      app1=%p st0=%d st1=%d applet=%s tid=%d nice=%d calls=%u rate=%u\n",
-                                     tmpctx,
-                                     tmpctx->st0,
-                                     tmpctx->st1,
-                                     tmpctx->applet->name,
-                                     tmpctx->t->tid,
-                                     tmpctx->t->nice, tmpctx->t->calls, read_freq_ctr(&tmpctx->call_rate));
-               }
+               chunk_appendf(&trash,
+                             "      htx=%p flags=0x%x size=%u data=%u used=%u wrap=%s extra=%llu\n",
+                             htx, htx->flags, htx->size, htx->data, htx_nbblks(htx),
+                             (htx->tail >= htx->head) ? "NO" : "YES",
+                             (unsigned long long)htx->extra);
+       }
+       if (HAS_FILTERS(strm) && strm_flt(strm)->current[0]) {
+               struct filter *flt = strm_flt(strm)->current[0];
+
+               chunk_appendf(&trash, "      current_filter=%p (id=\"%s\" flags=0x%x pre=0x%x post=0x%x) \n",
+                             flt, flt->config->id, flt->flags, flt->pre_analyzers, flt->post_analyzers);
+       }
+
+       chunk_appendf(&trash,
+                    "  res=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
+                    "      an_exp=%s buf=%p data=%p o=%u p=%u i=%u size=%u\n",
+                    &strm->res,
+                    strm->res.flags, strm->res.analysers,
+                    strm->res.pipe ? strm->res.pipe->data : 0,
+                    strm->res.to_forward, strm->res.total,
+                    strm->res.analyse_exp ?
+                    human_time(TICKS_TO_MS(strm->res.analyse_exp - now_ms),
+                               TICKS_TO_MS(1000)) : "<NEVER>",
+                    &strm->res.buf,
+                    b_orig(&strm->res.buf), (unsigned int)co_data(&strm->res),
+                    (unsigned int)ci_head_ofs(&strm->res), (unsigned int)ci_data(&strm->res),
+                    (unsigned int)strm->res.buf.size);
+
+       if (IS_HTX_STRM(strm)) {
+               struct htx *htx = htxbuf(&strm->res.buf);
 
                chunk_appendf(&trash,
-                            "  req=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
-                            "      an_exp=%s buf=%p data=%p o=%u p=%u i=%u size=%u\n",
-                            &strm->req,
-                            strm->req.flags, strm->req.analysers,
-                            strm->req.pipe ? strm->req.pipe->data : 0,
-                            strm->req.to_forward, strm->req.total,
-                            strm->req.analyse_exp ?
-                            human_time(TICKS_TO_MS(strm->req.analyse_exp - now_ms),
-                                       TICKS_TO_MS(1000)) : "<NEVER>",
-                            &strm->req.buf,
-                            b_orig(&strm->req.buf), (unsigned int)co_data(&strm->req),
-                            (unsigned int)ci_head_ofs(&strm->req), (unsigned int)ci_data(&strm->req),
-                            (unsigned int)strm->req.buf.size);
-
-               if (IS_HTX_STRM(strm)) {
-                       struct htx *htx = htxbuf(&strm->req.buf);
+                             "      htx=%p flags=0x%x size=%u data=%u used=%u wrap=%s extra=%llu\n",
+                             htx, htx->flags, htx->size, htx->data, htx_nbblks(htx),
+                             (htx->tail >= htx->head) ? "NO" : "YES",
+                             (unsigned long long)htx->extra);
+       }
+       if (HAS_FILTERS(strm) && strm_flt(strm)->current[1]) {
+               struct filter *flt = strm_flt(strm)->current[1];
 
-                       chunk_appendf(&trash,
-                                     "      htx=%p flags=0x%x size=%u data=%u used=%u wrap=%s extra=%llu\n",
-                                     htx, htx->flags, htx->size, htx->data, htx_nbblks(htx),
-                                     (htx->tail >= htx->head) ? "NO" : "YES",
-                                     (unsigned long long)htx->extra);
-               }
-               if (HAS_FILTERS(strm) && strm_flt(strm)->current[0]) {
-                       struct filter *flt = strm_flt(strm)->current[0];
+               chunk_appendf(&trash, "      current_filter=%p (id=\"%s\" flags=0x%x pre=0x%x post=0x%x) \n",
+                             flt, flt->config->id, flt->flags, flt->pre_analyzers, flt->post_analyzers);
+       }
 
-                       chunk_appendf(&trash, "      current_filter=%p (id=\"%s\" flags=0x%x pre=0x%x post=0x%x) \n",
-                                     flt, flt->config->id, flt->flags, flt->pre_analyzers, flt->post_analyzers);
-               }
+       if (strm->current_rule_list && strm->current_rule) {
+               const struct act_rule *rule = strm->current_rule;
+               chunk_appendf(&trash, "  current_rule=\"%s\" [%s:%d]\n", rule->kw->kw, rule->conf.file, rule->conf.line);
+       }
+}
 
-               chunk_appendf(&trash,
-                            "  res=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
-                            "      an_exp=%s buf=%p data=%p o=%u p=%u i=%u size=%u\n",
-                            &strm->res,
-                            strm->res.flags, strm->res.analysers,
-                            strm->res.pipe ? strm->res.pipe->data : 0,
-                            strm->res.to_forward, strm->res.total,
-                            strm->res.analyse_exp ?
-                            human_time(TICKS_TO_MS(strm->res.analyse_exp - now_ms),
-                                       TICKS_TO_MS(1000)) : "<NEVER>",
-                            &strm->res.buf,
-                            b_orig(&strm->res.buf), (unsigned int)co_data(&strm->res),
-                            (unsigned int)ci_head_ofs(&strm->res), (unsigned int)ci_data(&strm->res),
-                            (unsigned int)strm->res.buf.size);
-
-               if (IS_HTX_STRM(strm)) {
-                       struct htx *htx = htxbuf(&strm->res.buf);
+/* This function dumps a complete stream state onto the stream connector's
+ * read buffer. The stream has to be set in strm. It returns 0 if the output
+ * buffer is full and it needs to be called again, otherwise non-zero. It is
+ * designed to be called from stats_dump_strm_to_buffer() below.
+ */
+static int stats_dump_full_strm_to_buffer(struct stconn *sc, struct stream *strm)
+{
+       struct appctx *appctx = __sc_appctx(sc);
+       struct show_sess_ctx *ctx = appctx->svcctx;
 
-                       chunk_appendf(&trash,
-                                     "      htx=%p flags=0x%x size=%u data=%u used=%u wrap=%s extra=%llu\n",
-                                     htx, htx->flags, htx->size, htx->data, htx_nbblks(htx),
-                                     (htx->tail >= htx->head) ? "NO" : "YES",
-                                     (unsigned long long)htx->extra);
-               }
-               if (HAS_FILTERS(strm) && strm_flt(strm)->current[1]) {
-                       struct filter *flt = strm_flt(strm)->current[1];
+       chunk_reset(&trash);
 
-                       chunk_appendf(&trash, "      current_filter=%p (id=\"%s\" flags=0x%x pre=0x%x post=0x%x) \n",
-                                     flt, flt->config->id, flt->flags, flt->pre_analyzers, flt->post_analyzers);
-               }
+       if (ctx->section > 0 && ctx->uid != strm->uniq_id) {
+               /* stream changed, no need to go any further */
+               chunk_appendf(&trash, "  *** session terminated while we were watching it ***\n");
+               if (applet_putchk(appctx, &trash) == -1)
+                       goto full;
+               goto done;
+       }
 
-               if (strm->current_rule_list && strm->current_rule) {
-                       const struct act_rule *rule = strm->current_rule;
-                       chunk_appendf(&trash, "  current_rule=\"%s\" [%s:%d]\n", rule->kw->kw, rule->conf.file, rule->conf.line);
-               }
+       switch (ctx->section) {
+       case 0: /* main status of the stream */
+               ctx->uid = strm->uniq_id;
+               ctx->section = 1;
+               __fallthrough;
 
+       case 1:
+               /* the function appends to the trash */
+               strm_dump_to_buffer(strm, appctx->cli_anon_key);
                if (applet_putchk(appctx, &trash) == -1)
                        goto full;