mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-05 22:56:57 +02:00
MINOR: cli/pools: record the list of pool registrations even when merging them
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.
This commit is contained in:
parent
baf8b742b4
commit
9091c5317f
@ -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.
|
||||
|
@ -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);
|
||||
|
41
src/pool.c
41
src/pool.c
@ -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, ®->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(®->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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user