]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] stats: add "show table [<name>]" to dump a stick-table
authorWilly Tarreau <w@1wt.eu>
Mon, 12 Jul 2010 15:55:33 +0000 (17:55 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 10 Aug 2010 16:04:14 +0000 (18:04 +0200)
It is now possible to dump a table's contents with keys, expire,
use count, and various data using the command above on the stats
socket.

"show table" only shows main table stats, while "show table <name>"
dumps table contents, only if the socket level is admin.

include/proto/dumpstats.h
include/types/session.h
src/dumpstats.c

index c3b8c4c025c96d1e845d00b7306fd4fe537034ab..56d8abbe70fe9a5bb5cbdc7b86af323130f042f0 100644 (file)
@@ -51,6 +51,7 @@
 #define STAT_CLI_O_INFO 5   /* dump info/stats */
 #define STAT_CLI_O_SESS 6   /* dump sessions */
 #define STAT_CLI_O_ERR  7   /* dump errors */
+#define STAT_CLI_O_TAB  8   /* dump tables */
 
 
 int stats_accept(struct session *s);
@@ -60,6 +61,7 @@ int stats_dump_raw_to_buffer(struct session *s, struct buffer *rep);
 int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri);
 int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri);
 int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep);
+int stats_dump_table_to_buffer(struct session *s, struct buffer *rep);
 int stats_dump_errors_to_buffer(struct session *s, struct buffer *rep);
 void http_stats_io_handler(struct stream_interface *si);
 
index 222c30f7adc52d577d31dc1f5afdc68f1a628627..f2f15c45634bc64e681e8cb2d9502594903250c1 100644 (file)
@@ -230,6 +230,11 @@ struct session {
                        int ptr;                /* <0: headers, >=0 : text pointer to restart from */
                        int bol;                /* pointer to beginning of current line */
                } errors;
+               struct {
+                       void *target;           /* table we want to dump, or NULL for all */
+                       struct proxy *proxy;    /* table being currently dumped (first if NULL) */
+                       struct stksess *entry;  /* last entry we were trying to dump (or first if NULL) */
+               } table;
                struct {
                        const char *msg;        /* pointer to a persistent message to be returned in PRINT state */
                } cli;
index 1d1e0ff62245f05707044661eda07a556b3e3f86..13e2dd0c43855952a8cbaeb55dd28911aeeb771f 100644 (file)
@@ -64,6 +64,7 @@ const char stats_sock_usage_msg[] =
        "  show stat      : report counters for each proxy and server\n"
        "  show errors    : report last request and response errors for each proxy\n"
        "  show sess [id] : report the list of current sessions or dump this session\n"
+       "  show table [id]: report table usage stats or dump this table's contents\n"
        "  get weight     : report a server's current weight\n"
        "  set weight     : change a server's weight\n"
        "  set timeout    : change a timeout setting\n"
@@ -377,6 +378,16 @@ int stats_sock_parse_request(struct stream_interface *si, char *line)
                        s->data_state = DATA_ST_INIT;
                        si->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
                }
+               else if (strcmp(args[1], "table") == 0) {
+                       s->data_state = DATA_ST_INIT;
+                       if (*args[2])
+                               s->data_ctx.table.target = findproxy(args[2], 0);
+                       else
+                               s->data_ctx.table.target = NULL;
+                       s->data_ctx.table.proxy = NULL;
+                       s->data_ctx.table.entry = NULL;
+                       si->st0 = STAT_CLI_O_TAB; // stats_dump_table_to_buffer
+               }
                else { /* neither "stat" nor "info" nor "sess" nor "errors"*/
                        return 0;
                }
@@ -807,6 +818,10 @@ void stats_io_handler(struct stream_interface *si)
                                if (stats_dump_errors_to_buffer(s, res))
                                        si->st0 = STAT_CLI_PROMPT;
                                break;
+                       case STAT_CLI_O_TAB:
+                               if (stats_dump_table_to_buffer(s, res))
+                                       si->st0 = STAT_CLI_PROMPT;
+                               break;
                        default: /* abnormal state */
                                si->st0 = STAT_CLI_PROMPT;
                                break;
@@ -2769,6 +2784,174 @@ int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep)
        }
 }
 
+/* This function is called to send output to the response buffer.
+ * It dumps the tables states onto the output buffer <rep>.
+ * Expects to be called with client socket shut down on input.
+ * s->data_ctx must have been zeroed first, and the flags properly set.
+ * It returns 0 as long as it does not complete, non-zero upon completion.
+ */
+int stats_dump_table_to_buffer(struct session *s, struct buffer *rep)
+{
+       struct chunk msg;
+       struct ebmb_node *eb;
+       int dt;
+
+       /*
+        * We have 3 possible states in s->data_state :
+        *   - DATA_ST_INIT : the first call
+        *   - DATA_ST_INFO : the proxy pointer points to the next table to
+        *     dump, the entry pointer is NULL ;
+        *   - DATA_ST_LIST : the proxy pointer points to the current table
+        *     and the entry pointer points to the next entry to be dumped,
+        *     and the refcount on the next entry is held ;
+        *   - DATA_ST_END : nothing left to dump, the buffer may contain some
+        *     data though.
+        */
+
+       if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW))) {
+               /* in case of abort, remove any refcount we might have set on an entry */
+               if (s->data_state == DATA_ST_LIST)
+                       s->data_ctx.table.entry->ref_cnt--;
+               return 1;
+       }
+
+       chunk_init(&msg, trash, sizeof(trash));
+
+       while (s->data_state != DATA_ST_FIN) {
+               switch (s->data_state) {
+               case DATA_ST_INIT:
+                       s->data_ctx.table.proxy = s->data_ctx.table.target;
+                       if (!s->data_ctx.table.proxy)
+                               s->data_ctx.table.proxy = proxy;
+
+                       s->data_ctx.table.entry = NULL;
+                       s->data_state = DATA_ST_INFO;
+                       break;
+
+               case DATA_ST_INFO:
+                       if (!s->data_ctx.table.proxy ||
+                           (s->data_ctx.table.target &&
+                            s->data_ctx.table.proxy != s->data_ctx.table.target)) {
+                               s->data_state = DATA_ST_END;
+                               break;
+                       }
+
+                       if (s->data_ctx.table.proxy->table.size) {
+                               chunk_printf(&msg, "# table: %s, type: %ld, size:%d, used:%d\n",
+                                            s->data_ctx.table.proxy->id,
+                                            s->data_ctx.table.proxy->table.type,
+                                            s->data_ctx.table.proxy->table.size,
+                                            s->data_ctx.table.proxy->table.current);
+
+                               /* any other information should be dumped here */
+
+                               if (s->data_ctx.table.target &&
+                                   s->listener->perm.ux.level < ACCESS_LVL_OPER)
+                                       chunk_printf(&msg, "# contents not dumped due to insufficient privileges\n");
+
+                               if (buffer_feed_chunk(rep, &msg) >= 0)
+                                       return 0;
+
+                               if (s->data_ctx.table.target &&
+                                   s->listener->perm.ux.level >= ACCESS_LVL_OPER) {
+                                       /* dump entries only if table explicitly requested */
+                                       eb = ebmb_first(&s->data_ctx.table.proxy->table.keys);
+                                       if (eb) {
+                                               s->data_ctx.table.entry = ebmb_entry(eb, struct stksess, key);
+                                               s->data_ctx.table.entry->ref_cnt++;
+                                               s->data_state = DATA_ST_LIST;
+                                               break;
+                                       }
+                               }
+                       }
+                       s->data_ctx.table.proxy = s->data_ctx.table.proxy->next;
+                       break;
+
+               case DATA_ST_LIST:
+                       chunk_printf(&msg, "%p:", s->data_ctx.table.entry);
+
+                       if (s->data_ctx.table.proxy->table.type == STKTABLE_TYPE_IP) {
+                               char addr[16];
+                               inet_ntop(AF_INET,
+                                         (const void *)&s->data_ctx.table.entry->key.key,
+                                         addr, sizeof(addr));
+                               chunk_printf(&msg, " key=%s", addr);
+                       }
+                       else
+                               chunk_printf(&msg, " key=?");
+
+                       chunk_printf(&msg, " use=%d exp=%d",
+                                    s->data_ctx.table.entry->ref_cnt - 1,
+                                    tick_remain(now_ms, s->data_ctx.table.entry->expire));
+
+                       for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
+                               void *ptr;
+
+                               if (s->data_ctx.table.proxy->table.data_ofs[dt] == 0)
+                                       continue;
+                               if (stktable_data_types[dt].arg_type == ARG_T_DELAY)
+                                       chunk_printf(&msg, " %s(%d)=",
+                                                    stktable_data_types[dt].name,
+                                                    s->data_ctx.table.proxy->table.data_arg[dt].u);
+                               else
+                                       chunk_printf(&msg, " %s=", stktable_data_types[dt].name);
+
+                               ptr = stktable_data_ptr(&s->data_ctx.table.proxy->table,
+                                                       s->data_ctx.table.entry,
+                                                       dt);
+                               switch (dt) {
+                                       /* all entries using the same type can be folded */
+                               case STKTABLE_DT_SERVER_ID:
+                               case STKTABLE_DT_GPC0:
+                               case STKTABLE_DT_CONN_CNT:
+                               case STKTABLE_DT_CONN_CUR:
+                               case STKTABLE_DT_SESS_CNT:
+                               case STKTABLE_DT_HTTP_REQ_CNT:
+                               case STKTABLE_DT_HTTP_ERR_CNT:
+                                       chunk_printf(&msg, "%u", stktable_data_cast(ptr, server_id));
+                                       break;
+                               case STKTABLE_DT_CONN_RATE:
+                               case STKTABLE_DT_SESS_RATE:
+                               case STKTABLE_DT_HTTP_REQ_RATE:
+                               case STKTABLE_DT_HTTP_ERR_RATE:
+                               case STKTABLE_DT_BYTES_IN_RATE:
+                               case STKTABLE_DT_BYTES_OUT_RATE:
+                                       chunk_printf(&msg, "%d",
+                                                    read_freq_ctr_period(&stktable_data_cast(ptr, conn_rate),
+                                                                         s->data_ctx.table.proxy->table.data_arg[dt].u));
+                                       break;
+                               case STKTABLE_DT_BYTES_IN_CNT:
+                               case STKTABLE_DT_BYTES_OUT_CNT:
+                                       chunk_printf(&msg, "%lld", stktable_data_cast(ptr, bytes_in_cnt));
+                                       break;
+                               }
+                       }
+                       chunk_printf(&msg, "\n");
+
+                       if (buffer_feed_chunk(rep, &msg) >= 0)
+                               return 0;
+
+                       s->data_ctx.table.entry->ref_cnt--;
+
+                       eb = ebmb_next(&s->data_ctx.table.entry->key);
+                       if (eb) {
+                               s->data_ctx.table.entry = ebmb_entry(eb, struct stksess, key);
+                               s->data_ctx.table.entry->ref_cnt++;
+                               break;
+                       }
+
+                       s->data_ctx.table.proxy = s->data_ctx.table.proxy->next;
+                       s->data_state = DATA_ST_INFO;
+                       break;
+
+               case DATA_ST_END:
+                       s->data_state = DATA_ST_FIN;
+                       break;
+               }
+       }
+       return 1;
+}
+
 /* print a line of text buffer (limited to 70 bytes) to <out>. The format is :
  * <2 spaces> <offset=5 digits> <space or plus> <space> <70 chars max> <\n>
  * which is 60 chars per line. Non-printable chars \t, \n, \r and \e are