]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: activity/memprofile: also monitor strdup() activity
authorWilly Tarreau <w@1wt.eu>
Thu, 21 Nov 2024 07:45:04 +0000 (08:45 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 21 Nov 2024 18:58:06 +0000 (19:58 +0100)
Some memory profiling outputs have showed negative counters, very likely
due to some libs calling strdup(). Let's add it to the list of monitored
activities.

Actually even haproxy itself uses some. Having "profiling.memory on" in
the config reveals 35 call places.

include/haproxy/activity-t.h
src/activity.c

index b396dc0232d1cfe2ae7350af93f04a675960a6e1..74bf772fe3c8de394d2a0f0f835ef0a61e0b22f3 100644 (file)
@@ -42,6 +42,7 @@ enum memprof_method {
        MEMPROF_METH_MALLOC,
        MEMPROF_METH_CALLOC,
        MEMPROF_METH_REALLOC,
+       MEMPROF_METH_STRDUP,
        MEMPROF_METH_FREE,
        MEMPROF_METH_P_ALLOC, // pool_alloc()
        MEMPROF_METH_P_FREE,  // pool_free()
index 5a058f32f1c8c32c67919d6878e15c802e70aa74..df2541b68de9d570372620c04c403c37da86951b 100644 (file)
@@ -46,6 +46,7 @@ struct show_activity_ctx {
 #undef calloc
 #undef malloc
 #undef realloc
+#undef strdup
 #endif
 
 /* bit field of profiling options. Beware, may be modified at runtime! */
@@ -67,7 +68,7 @@ struct sched_activity sched_activity[SCHED_ACT_HASH_BUCKETS] __attribute__((alig
 #ifdef USE_MEMORY_PROFILING
 
 static const char *const memprof_methods[MEMPROF_METH_METHODS] = {
-       "unknown", "malloc", "calloc", "realloc", "free", "p_alloc", "p_free",
+       "unknown", "malloc", "calloc", "realloc", "strdup", "free", "p_alloc", "p_free",
 };
 
 /* last one is for hash collisions ("others") and has no caller address */
@@ -82,6 +83,7 @@ static THREAD_LOCAL int in_memprof = 0;
 static void *memprof_malloc_initial_handler(size_t size);
 static void *memprof_calloc_initial_handler(size_t nmemb, size_t size);
 static void *memprof_realloc_initial_handler(void *ptr, size_t size);
+static char *memprof_strdup_initial_handler(const char *s);
 static void  memprof_free_initial_handler(void *ptr);
 
 /* Fallback handlers for the main alloc/free functions. They are preset to
@@ -90,6 +92,7 @@ static void  memprof_free_initial_handler(void *ptr);
 static void *(*memprof_malloc_handler)(size_t size)               = memprof_malloc_initial_handler;
 static void *(*memprof_calloc_handler)(size_t nmemb, size_t size) = memprof_calloc_initial_handler;
 static void *(*memprof_realloc_handler)(void *ptr, size_t size)   = memprof_realloc_initial_handler;
+static char *(*memprof_strdup_handler)(const char *s)             = memprof_strdup_initial_handler;
 static void  (*memprof_free_handler)(void *ptr)                   = memprof_free_initial_handler;
 
 /* Used to force to die if it's not possible to retrieve the allocation
@@ -126,6 +129,10 @@ static void memprof_init()
        if (!memprof_realloc_handler)
                memprof_die("FATAL: realloc() function not found.\n");
 
+       memprof_strdup_handler  = get_sym_next_addr("strdup");
+       if (!memprof_strdup_handler)
+               memprof_die("FATAL: strdup() function not found.\n");
+
        memprof_free_handler    = get_sym_next_addr("free");
        if (!memprof_free_handler)
                memprof_die("FATAL: free() function not found.\n");
@@ -168,6 +175,17 @@ static void *memprof_realloc_initial_handler(void *ptr, size_t size)
        return memprof_realloc_handler(ptr, size);
 }
 
+static char *memprof_strdup_initial_handler(const char *s)
+{
+       if (in_memprof) {
+               /* probably that dlsym() needs strdup(), let's fail */
+               return NULL;
+       }
+
+       memprof_init();
+       return memprof_strdup_handler(s);
+}
+
 static void  memprof_free_initial_handler(void *ptr)
 {
        memprof_init();
@@ -295,6 +313,32 @@ void *realloc(void *ptr, size_t size)
        return ret;
 }
 
+/* This is the new global strdup() function. It must optimize for the normal
+ * case (i.e. profiling disabled) hence the first test to permit a direct jump.
+ * It must remain simple to guarantee the lack of reentrance. stdio is not
+ * possible there even for debugging. The reported size is the really allocated
+ * one as returned by malloc_usable_size(), because this will allow it to be
+ * compared to the one before realloc() or free(). This is a GNU and jemalloc
+ * extension but other systems may also store this size in ptr[-1].
+ */
+char *strdup(const char *s)
+{
+       struct memprof_stats *bin;
+       size_t size;
+       char *ret;
+
+       if (likely(!(profiling & HA_PROF_MEMORY)))
+               return memprof_strdup_handler(s);
+
+       ret = memprof_strdup_handler(s);
+       size = malloc_usable_size(ret) + sizeof(void *);
+
+       bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRDUP);
+       _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
+       _HA_ATOMIC_ADD(&bin->alloc_tot, size);
+       return ret;
+}
+
 /* This is the new global free() function. It must optimize for the normal
  * case (i.e. profiling disabled) hence the first test to permit a direct jump.
  * It must remain simple to guarantee the lack of reentrance. stdio is not