From: Aurelien DARRAGON Date: Thu, 16 May 2024 10:08:56 +0000 (+0200) Subject: DEBUG: tools: add vma_set_name() helper X-Git-Tag: v3.0-dev13~62 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=51a8f134efcd26d29c3deabbb10084716d05cee9;p=thirdparty%2Fhaproxy.git DEBUG: tools: add vma_set_name() helper Following David Carlier's work in 98d22f21 ("MEDIUM: shctx: Naming shared memory context"), let's provide an helper function to set a name hint on a virtual memory area (ie: anonymous map created using mmap(), or memory area returned by malloc()). Naming will only occur if available, and naming errors will be ignored. The function takes mandatory and parameterss to build the map name as follow: "type:name". When looking at /proc//maps, vma named using this helper function will show up this way (provided that the kernel has prtcl support for PR_SET_VMA_ANON_NAME): example, using mmap + MAP_SHARED|MAP_ANONYMOUS: 7364c4fff000-736508000000 rw-s 00000000 00:01 3540 [anon_shmem:type:name] Another example, using mmap + MAP_PRIVATE|MAP_ANONYMOUS or using glibc/malloc() above MMAP_THRESHOLD: 7364c4fff000-736508000000 rw-s 00000000 00:01 3540 [anon:type:name] --- diff --git a/include/haproxy/tools.h b/include/haproxy/tools.h index 8538a8ebc2..fbd8a59c4e 100644 --- a/include/haproxy/tools.h +++ b/include/haproxy/tools.h @@ -1199,4 +1199,7 @@ int openssl_compare_current_version(const char *version); /* compare the current OpenSSL name to a string */ int openssl_compare_current_name(const char *name); +/* vma helpers */ +void vma_set_name(void *addr, size_t size, const char *type, const char *name); + #endif /* _HAPROXY_TOOLS_H */ diff --git a/src/tools.c b/src/tools.c index 1f67665237..717de41355 100644 --- a/src/tools.c +++ b/src/tools.c @@ -41,6 +41,7 @@ extern void *__elf_aux_vector; #include #include #include +#include #include #include #include @@ -52,6 +53,10 @@ extern void *__elf_aux_vector; #include #endif +#if defined(USE_PRCTL) +#include +#endif + #include #include #include @@ -6459,6 +6464,83 @@ int openssl_compare_current_name(const char *name) return 1; } +/* prctl/PR_SET_VMA wrapper to easily give a name to virtual memory areas, + * knowing their address and size. + * + * It is only intended for use with memory allocated using mmap (private or + * shared anonymous maps) or malloc (provided that is at least one page + * large), which is memory that may be released using munmap(). For memory + * allocated using malloc(), no naming will be attempted if the vma is less + * than one page large, because naming is only relevant for large memory + * blocks. For instance, glibc/malloc() will directly use mmap() once + * MMAP_THRESHOLD is reached (defaults to 128K), and will try to use the + * heap as much as possible below that. + * + * and are mandatory + * + * The function does nothing if naming API is not available, and naming errors + * are ignored. + */ +void vma_set_name(void *addr, size_t size, const char *type, const char *name) +{ + long pagesize = sysconf(_SC_PAGESIZE); + void *aligned_addr; + __maybe_unused size_t aligned_size; + + BUG_ON(!type || !name); + + /* prctl/PR_SET/VMA expects the start of an aligned memory address, but + * user may have provided address returned by malloc() which may not be + * aligned nor point to the beginning of the map + */ + aligned_addr = (void *)((uintptr_t)addr & -4096); + aligned_size = (((addr + size) - aligned_addr) + 4095) & -4096; + + if (aligned_addr != addr) { + /* provided pointer likely comes from malloc(), at least it + * doesn't come from mmap() which only returns aligned addresses + */ + if (size < pagesize) + return; + } +#if defined(USE_PRCTL) && defined(PR_SET_VMA) + { + /* + * From Linux 5.17 (and if the `CONFIG_ANON_VMA_NAME` kernel config is set)`, + * anonymous regions can be named. + * We intentionally ignore errors as it should not jeopardize the memory context + * mapping whatsoever (e.g. older kernels). + * + * The naming can take up to 79 characters, accepting valid ASCII values + * except [, ], \, $ and '. + * As a result, when looking for /proc//maps, we can see the anonymous range + * as follow : + * `7364c4fff000-736508000000 rw-s 00000000 00:01 3540 [anon_shmem:scope.name]` + * (MAP_SHARED) + * `7364c4fff000-736508000000 rw-s 00000000 00:01 3540 [anon:scope.name]` + * (MAP_PRIVATE) + */ + char fullname[80]; + int rn; + + rn = snprintf(fullname, sizeof(fullname), "%s:%s", type, name); + + if (rn >= 0) { + /* Give a name to the map by setting PR_SET_VMA_ANON_NAME attribute + * using prctl/PR_SET_VMA combination. + * + * note from 'man prctl': + * assigning an attribute to a virtual memory area might prevent it + * from being merged with adjacent virtual memory areas due to the + * difference in that attribute's value. + */ + (void)prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, + aligned_addr, aligned_size, fullname); + } + } +#endif +} + #if defined(RTLD_DEFAULT) || defined(RTLD_NEXT) /* redefine dlopen() so that we can detect unexpected replacement of some * critical symbols, typically init/alloc/free functions coming from alternate