mirror of
https://github.com/haproxy/haproxy.git
synced 2026-04-26 00:27:54 -04:00
87 lines
4.8 KiB
Text
87 lines
4.8 KiB
Text
|
|
2025-08-13 - Memory allocation in HAProxy 3.3
|
||
|
|
|
||
|
|
The vast majority of dynamic memory allocations are performed from pools. Pools
|
||
|
|
are optimized to store pre-calibrated objects of the right size for a given
|
||
|
|
usage, try to favor locality and hot objects as much as possible, and are
|
||
|
|
heavily instrumented to detect and help debug a wide class of bugs including
|
||
|
|
buffer overflows, use-after-free, etc.
|
||
|
|
|
||
|
|
For objects of random sizes, or those used only at configuration time, pools
|
||
|
|
are not suited, and the regular malloc/free family is available, in addition of
|
||
|
|
a few others.
|
||
|
|
|
||
|
|
The standard allocation calls are intercepted at the code level (#define) when
|
||
|
|
the code is compiled with -DDEBUG_MEM_STATS. For this reason, these calls are
|
||
|
|
redefined as macros in "bug.h", and one must not try to use the pointers to
|
||
|
|
such functions, as this may break DEBUG_MEM_STATS. This provides fine-grained
|
||
|
|
stats about allocation/free per line of source code using locally implemented
|
||
|
|
counters that can be consulted by "debug dev memstats". The calls are
|
||
|
|
categorized into one of "calloc", "free", "malloc", "realloc", "strdup",
|
||
|
|
"p_alloc", "p_free", the latter two designating pools. Extra calls such as
|
||
|
|
memalign() and similar are also intercepted and counted as malloc.
|
||
|
|
|
||
|
|
Due to the nature of this replacement, DEBUG_MEM_STATS cannot see operations
|
||
|
|
performed in libraries or dependencies.
|
||
|
|
|
||
|
|
In addition to DEBUG_MEM_STATS, when haproxy is built with USE_MEMORY_PROFILING
|
||
|
|
the standard functions are wrapped by new ones defined in "activity.c", which
|
||
|
|
also hold counters by call place. These ones are able to trace activity in
|
||
|
|
libraries because the functions check the return pointer to figure where the
|
||
|
|
call was made. The approach is different and relies on a large hash table. The
|
||
|
|
files, function names and line numbers are not know, but by passing the pointer
|
||
|
|
to dladdr(), we can often resolve most of these symbols. These operations are
|
||
|
|
consulted via "show profiling memory". It must first be enabled either in the
|
||
|
|
global config "profiling.memory on" or the CLI using "set profiling memory on".
|
||
|
|
Memory profiling can also track pool allocations and frees thanks to knowing
|
||
|
|
the size of the element and knowing a place where to store it. Some future
|
||
|
|
evolutions might consider making this possible as well for pure malloc/free
|
||
|
|
too by leveraging malloc_usable_size() a bit more.
|
||
|
|
|
||
|
|
Finally, 3.3 brought aligned allocations. These are made available via a new
|
||
|
|
family of functions around ha_aligned_alloc() that simply map to either
|
||
|
|
posix_memalign(), memalign() or _aligned_malloc() for CYGWIN, depending on
|
||
|
|
which one is available. This latter one requires to pass the pointer to
|
||
|
|
_aligned_free() instead of free(), so for this reason, all aligned allocations
|
||
|
|
have to be released using ha_aligned_free(). Since this mostly happens on
|
||
|
|
configuration elements, in practice it's not as inconvenient as it can sound.
|
||
|
|
These functions are in reality macros handled in "bug.h" like the previous
|
||
|
|
ones in order to deal with DEBUG_MEM_STATS. All "alloc" variants are reported
|
||
|
|
in memstats as "malloc". All "zalloc" variants are reported in memstats as
|
||
|
|
"calloc".
|
||
|
|
|
||
|
|
The currently available allocators are the following:
|
||
|
|
|
||
|
|
- void *ha_aligned_alloc(size_t align, size_t size)
|
||
|
|
- void *ha_aligned_zalloc(size_t align, size_t size)
|
||
|
|
|
||
|
|
Equivalent of malloc() but aligned to <align> bytes. The alignment MUST be
|
||
|
|
at least as large as one word and MUST be a power of two. The "zalloc"
|
||
|
|
variant also zeroes the area on success. Both return NULL on failure.
|
||
|
|
|
||
|
|
- void *ha_aligned_alloc_safe(size_t align, size_t size)
|
||
|
|
- void *ha_aligned_zalloc_safe(size_t align, size_t size)
|
||
|
|
|
||
|
|
Equivalent of malloc() but aligned to <align> bytes. The alignment is
|
||
|
|
automatically adjusted to the nearest larger power of two that is at least
|
||
|
|
as large as a word. The "zalloc" variant also zeroes the area on
|
||
|
|
success. Both return NULL on failure.
|
||
|
|
|
||
|
|
- (type *)ha_aligned_alloc_typed(size_t count, type)
|
||
|
|
(type *)ha_aligned_zalloc_typed(size_t count, type)
|
||
|
|
|
||
|
|
This macro returns an area aligned to the required alignment for type
|
||
|
|
<type>, large enough for <count> objects of this type, and the result is a
|
||
|
|
pointer of this type. The goal is to ease allocation of known structures
|
||
|
|
whose alignment is not necessarily known to the developer (and to avoid
|
||
|
|
encouraging to hard-code alignment). The cast in return also provides a
|
||
|
|
last-minute control in case a wrong type is mistakenly used due to a poor
|
||
|
|
copy-paste or an extra "*" after the type. When DEBUG_MEM_STATS is in use,
|
||
|
|
the type is stored as a string in the ".extra" field so that it can be
|
||
|
|
displayed in "debug dev memstats". The "zalloc" variant also zeroes the
|
||
|
|
area on success. Both return NULL on failure.
|
||
|
|
|
||
|
|
- void ha_aligned_free(void *ptr)
|
||
|
|
|
||
|
|
Frees the area pointed to by ptr. It is the equivalent of free() but for
|
||
|
|
objects allocated using one of the functions above.
|