From 1a9607b2cfc58a935f7245eafee5de0ba8e3062b Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 21 Nov 2024 08:45:04 +0100 Subject: [PATCH] MINOR: activity/memprofile: also monitor strdup() activity 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. (cherry picked from commit 33c0ce299da0a56a01ec1f59ebc78e8d59081acc) Signed-off-by: Christopher Faulet --- include/haproxy/activity-t.h | 1 + src/activity.c | 46 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/include/haproxy/activity-t.h b/include/haproxy/activity-t.h index 82666d3..88e96c2 100644 --- a/include/haproxy/activity-t.h +++ b/include/haproxy/activity-t.h @@ -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() diff --git a/src/activity.c b/src/activity.c index b6939d9..a1701e0 100644 --- a/src/activity.c +++ b/src/activity.c @@ -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 -- 1.7.10.4