]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
DEBUG: tools: add vma_set_name() helper
authorAurelien DARRAGON <adarragon@haproxy.com>
Thu, 16 May 2024 10:08:56 +0000 (12:08 +0200)
committerAurelien DARRAGON <adarragon@haproxy.com>
Tue, 21 May 2024 15:54:58 +0000 (17:54 +0200)
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 <type> and <name> parameterss to build the
map name as follow: "type:name". When looking at /proc/<pid>/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]

include/haproxy/tools.h
src/tools.c

index 8538a8ebc263cf54c0609d9c57cb3c4be8609849..fbd8a59c4ebf9e4028933177da922b167cf8e3ae 100644 (file)
@@ -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 */
index 1f6766523783f8e15e0c247d0dff69fa93f17e19..717de41355e80942556e36b372005ede0c38744f 100644 (file)
@@ -41,6 +41,7 @@ extern void *__elf_aux_vector;
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -52,6 +53,10 @@ extern void *__elf_aux_vector;
 #include <sys/auxv.h>
 #endif
 
+#if defined(USE_PRCTL)
+#include <sys/prctl.h>
+#endif
+
 #include <import/eb32sctree.h>
 #include <import/eb32tree.h>
 #include <import/ebmbtree.h>
@@ -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 <size> 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.
+ *
+ * <type> and <name> 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/<pid>/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