]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: cli/pools: record the list of pool registrations even when merging them
authorWilly Tarreau <w@1wt.eu>
Wed, 19 Mar 2025 10:33:36 +0000 (11:33 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 21 Mar 2025 16:09:30 +0000 (17:09 +0100)
By default, create_pool() tries to merge similar pools into one. But when
dealing with certain bugs, it's hard to say which ones were merged together.
We do have the information at registration time, so let's just create a
list of registrations ("pool_registration") attached to each pool, that
will store that information. It can then be consulted on the CLI using
"show pools detailed", where the names, sizes, alignment and flags are
reported.

doc/management.txt
include/haproxy/pool-t.h
src/pool.c

index 798a627ee774a797e312b7f2db1ac7469091ca42..5d4ca5681557a36c4ca28bf4a8a378c628df7f23 100644 (file)
@@ -3125,7 +3125,7 @@ show peers [dict|-] [<peers section>]
             table:0x55871b5b46a0 id=stkt update=1 localupdate=0 \
               commitupdate=0 syncing=0
 
-show pools [byname|bysize|byusage] [match <pfx>] [<nb>]
+show pools [byname|bysize|byusage] [detailed] [match <pfx>] [<nb>]
   Dump the status of internal memory pools. This is useful to track memory
   usage when suspecting a memory leak for example. It does exactly the same
   as the SIGQUIT when running in foreground except that it does not flush the
@@ -3133,10 +3133,12 @@ show pools [byname|bysize|byusage] [match <pfx>] [<nb>]
   sorted by pool name; if "bysize" is specified, it is sorted by item size in
   reverse order; if "byusage" is specified, it is sorted by total usage in
   reverse order, and only used entries are shown. It is also possible to limit
-  the output to the <nb> first entries (e.g. when sorting by usage). Finally,
-  if "match" followed by a prefix is specified, then only pools whose name
-  starts with this prefix will be shown. The reported total only concerns pools
-  matching the filtering criteria. Example:
+  the output to the <nb> first entries (e.g. when sorting by usage). It is
+  possible to also dump more internal details, including the list of all pools
+  that were merged together, by specifying "detailed". Finally, if "match"
+  followed by a prefix is specified, then only pools whose name starts with
+  this prefix will be shown. The reported total only concerns pools matching
+  the filtering criteria. Example:
 
     $ socat - /tmp/haproxy.sock <<< "show pools match quic byusage"
     Dumping pools usage. Use SIGQUIT to flush them.
index 157e2cad882ec2d3ed0b2ac86f93f9d8a38758c0..c727614cd29fa8a36e175820062906dec168591b 100644 (file)
@@ -62,6 +62,17 @@ struct pool_cache_head {
        ulong fill_pattern;  /* pattern used to fill the area on free */
 } THREAD_ALIGNED(64);
 
+/* This describes a pool registration, which is what was passed to
+ * create_pool() and that might have been merged with an existing pool.
+ */
+struct pool_registration {
+       struct list list;    /* link element */
+       char name[12];       /* name of the pool */
+       unsigned int size;   /* expected object size */
+       unsigned int flags;  /* MEM_F_* */
+       unsigned int align;  /* expected alignment; 0=unspecified */
+};
+
 /* This represents one item stored in the thread-local cache. <by_pool> links
  * the object to the list of objects in the pool, and <by_lru> links the object
  * to the local thread's list of hottest objects. This way it's possible to
@@ -117,6 +128,7 @@ struct pool_head {
        struct list list;       /* list of all known pools */
        void *base_addr;        /* allocation address, for free() */
        char name[12];          /* name of the pool */
+       struct list regs;       /* registrations: alt names for this pool */
 
        /* heavily read-write part */
        THREAD_ALIGN(64);
index 98c357a3311e19aec28f9bf354f06a31e0d1f8dc..da6e389da15de61ed4a283c8935a2e5b4b2ed07a 100644 (file)
@@ -293,12 +293,23 @@ static int mem_should_fail(const struct pool_head *pool)
 struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
 {
        unsigned int extra_mark, extra_caller, extra;
+       struct pool_registration *reg;
        struct pool_head *pool;
        struct pool_head *entry;
        struct list *start;
        unsigned int align;
        int thr __maybe_unused;
 
+       pool = NULL;
+       reg = calloc(1, sizeof(*reg));
+       if (!reg)
+               goto fail;
+
+       strlcpy2(reg->name, name, sizeof(reg->name));
+       reg->size = size;
+       reg->flags = flags;
+       reg->align = 0;
+
        extra_mark = (pool_debugging & POOL_DBG_TAG) ? POOL_EXTRA_MARK : 0;
        extra_caller = (pool_debugging & POOL_DBG_CALLER) ? POOL_EXTRA_CALLER : 0;
        extra = extra_mark + extra_caller;
@@ -327,7 +338,6 @@ struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
        /* TODO: thread: we do not lock pool list for now because all pools are
         * created during HAProxy startup (so before threads creation) */
        start = &pools;
-       pool = NULL;
 
        list_for_each_entry(entry, &pools, list) {
                if (entry->size == size) {
@@ -356,7 +366,7 @@ struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
 
                pool_addr = calloc(1, sizeof(*pool) + __alignof__(*pool));
                if (!pool_addr)
-                       return NULL;
+                       goto fail;
 
                /* always provide an aligned pool */
                pool = (struct pool_head*)((((size_t)pool_addr) + __alignof__(*pool)) & -(size_t)__alignof__(*pool));
@@ -368,6 +378,7 @@ struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
                pool->size = size;
                pool->flags = flags;
                LIST_APPEND(start, &pool->list);
+               LIST_INIT(&pool->regs);
 
                if (!(pool_debugging & POOL_DBG_NO_CACHE)) {
                        /* update per-thread pool cache if necessary */
@@ -378,8 +389,13 @@ struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
                        }
                }
        }
+
+       LIST_APPEND(&pool->regs, &reg->list);
        pool->users++;
        return pool;
+ fail:
+       free(reg);
+       return NULL;
 }
 
 /* Tries to allocate an object for the pool <pool> using the system's allocator
@@ -942,7 +958,16 @@ void *pool_destroy(struct pool_head *pool)
                        return pool;
                pool->users--;
                if (!pool->users) {
+                       /* remove all registrations at once */
+                       struct pool_registration *reg, *back;
+
+                       list_for_each_entry_safe(reg, back, &pool->regs, list) {
+                               LIST_DELETE(&reg->list);
+                               free(reg);
+                       }
+
                        LIST_DELETE(&pool->list);
+
                        /* note that if used == 0, the cache is empty */
                        free(pool->base_addr);
                }
@@ -1146,6 +1171,7 @@ void dump_pools_to_trash(int how, int max, const char *pfx)
        uint cached = 0;
        uint alloc_items;
        int by_what = how & 0xF; // bits 0..3 = sorting criterion
+       int detailed = !!(how & 0x10); // print details
 
        allocated = used = nbpools = 0;
 
@@ -1204,6 +1230,12 @@ void dump_pools_to_trash(int how, int max, const char *pfx)
                cached_bytes += pool_info[i].cached_items * (ulong)pool_info[i].entry->size;
                allocated    += pool_info[i].alloc_items  * (ulong)pool_info[i].entry->size;
                used         += pool_info[i].used_items   * (ulong)pool_info[i].entry->size;
+
+               if (detailed) {
+                       struct pool_registration *reg;
+                       list_for_each_entry(reg, &pool_info[i].entry->regs, list)
+                               chunk_appendf(&trash, "      >  %-12s: size=%u flags=%#x align=%u\n", reg->name, reg->size, reg->flags, reg->align);
+               }
        }
 
        chunk_appendf(&trash, "Total: %d pools, %llu bytes allocated, %llu used"
@@ -1372,6 +1404,9 @@ static int cli_parse_show_pools(char **args, char *payload, struct appctx *appct
                else if (strcmp(args[arg], "byusage") == 0) {
                        ctx->how = (ctx->how & ~0xF) | 3; // sort output by total allocated size
                }
+               else if (strcmp(args[arg], "detailed") == 0) {
+                       ctx->how |= 0x10;                 // print detailed registrations
+               }
                else if (strcmp(args[arg], "match") == 0 && *args[arg+1]) {
                        ctx->prefix = strdup(args[arg+1]); // only pools starting with this
                        if (!ctx->prefix)
@@ -1382,7 +1417,7 @@ static int cli_parse_show_pools(char **args, char *payload, struct appctx *appct
                        ctx->maxcnt = atoi(args[arg]); // number of entries to dump
                }
                else
-                       return cli_err(appctx, "Expects either 'byname', 'bysize', 'byusage', 'match <pfx>', or a max number of output lines.\n");
+                       return cli_err(appctx, "Expects either 'byname', 'bysize', 'byusage', 'match <pfx>', 'detailed', or a max number of output lines.\n");
        }
        return 0;
 }