From: Willy Tarreau Date: Thu, 21 Nov 2024 07:45:04 +0000 (+0100) Subject: MINOR: activity/memprofile: also monitor strdup() activity X-Git-Tag: v3.1-dev14~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=33c0ce299da0a56a01ec1f59ebc78e8d59081acc;p=thirdparty%2Fhaproxy.git 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. --- diff --git a/include/haproxy/activity-t.h b/include/haproxy/activity-t.h index b396dc0232..74bf772fe3 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 5a058f32f1..df2541b68d 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