]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: activity/memory: count allocations performed under a lock
authorWilly Tarreau <w@1wt.eu>
Thu, 11 Sep 2025 13:36:13 +0000 (15:36 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 11 Sep 2025 14:32:34 +0000 (16:32 +0200)
By checking the current thread's locking status, it becomes possible
to know during a memory allocation whether it's performed under a lock
or not. Both pools and memprofile functions were instrumented to check
for this and to increment the memprofile bin's locked_calls counter.

This one, when not zero, is reported on "show profiling memory" with a
percentage of all allocations that such locked allocations represent.
This way it becomes possible to try to target certain code paths that
are particularly expensive. Example:

  $ socat - /tmp/sock1 <<< "show profiling memory"|grep lock
     20297301           0     2598054528              0|   0x62a820fa3991 sockaddr_alloc+0x61/0xa3 p_alloc(128) [pool=sockaddr] [locked=54962 (0.2 %)]
            0    20297301              0     2598054528|   0x62a820fa3a24 sockaddr_free+0x44/0x59 p_free(-128) [pool=sockaddr] [locked=34300 (0.1 %)]
      9908432           0     1268279296              0|   0x62a820eb8524 main+0x81974 p_alloc(128) [pool=task] [locked=9908432 (100.0 %)]
      9908432           0      554872192              0|   0x62a820eb85a6 main+0x819f6 p_alloc(56) [pool=tasklet] [locked=9908432 (100.0 %)]
       263001           0       63120240              0|   0x62a820fa3c97 conn_new+0x37/0x1b2 p_alloc(240) [pool=connection] [locked=20662 (7.8 %)]
        71643           0       47307584              0|   0x62a82105204d pool_get_from_os_noinc+0x12d/0x161 posix_memalign(660) [locked=5393 (7.5 %)]

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

index ba9241d03a2fd35e45376312fa64163a0826562c..f7ea1f130bfc8a18581fe501b79f00e5c6b32798 100644 (file)
@@ -76,12 +76,12 @@ struct memprof_stats {
        const void *caller;
        enum memprof_method method;
        /* 4-7 bytes hole here */
+       unsigned long long locked_calls;
        unsigned long long alloc_calls;
        unsigned long long free_calls;
        unsigned long long alloc_tot;
        unsigned long long free_tot;
        void *info; // for pools, ptr to the pool
-       void *pad;  // pad to 64
 };
 #endif
 
index ad4d60d8b5a32a7d440f1537d57e5c6882be78e2..9f6bd6162f0020a964dea9c2b0b5bb1a4f2fd9e1 100644 (file)
@@ -342,6 +342,8 @@ void *malloc(size_t size)
        size = malloc_usable_size(ret) + sizeof(void *);
 
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MALLOC);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_tot, size);
        return ret;
@@ -367,6 +369,8 @@ void *calloc(size_t nmemb, size_t size)
        size = malloc_usable_size(ret) + sizeof(void *);
 
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_CALLOC);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_tot, size);
        return ret;
@@ -400,6 +404,8 @@ void *realloc(void *ptr, size_t size)
                size += sizeof(void *);
 
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_REALLOC);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        if (size > size_before) {
                _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
                _HA_ATOMIC_ADD(&bin->alloc_tot, size - size_before);
@@ -431,6 +437,8 @@ char *strdup(const char *s)
        size = malloc_usable_size(ret) + sizeof(void *);
 
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRDUP);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_tot, size);
        return ret;
@@ -461,6 +469,8 @@ void free(void *ptr)
        memprof_free_handler(ptr);
 
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_FREE);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->free_calls, 1);
        _HA_ATOMIC_ADD(&bin->free_tot, size_before);
 }
@@ -481,6 +491,8 @@ char *strndup(const char *s, size_t size)
 
        size = malloc_usable_size(ret) + sizeof(void *);
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_STRNDUP);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_tot, size);
        return ret;
@@ -500,6 +512,8 @@ void *valloc(size_t size)
 
        size = malloc_usable_size(ret) + sizeof(void *);
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_VALLOC);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_tot, size);
        return ret;
@@ -519,6 +533,8 @@ void *pvalloc(size_t size)
 
        size = malloc_usable_size(ret) + sizeof(void *);
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_PVALLOC);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_tot, size);
        return ret;
@@ -538,6 +554,8 @@ void *memalign(size_t align, size_t size)
 
        size = malloc_usable_size(ret) + sizeof(void *);
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_MEMALIGN);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_tot, size);
        return ret;
@@ -557,6 +575,8 @@ void *aligned_alloc(size_t align, size_t size)
 
        size = malloc_usable_size(ret) + sizeof(void *);
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_ALIGNED_ALLOC);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_tot, size);
        return ret;
@@ -579,6 +599,8 @@ int posix_memalign(void **ptr, size_t align, size_t size)
 
        size = malloc_usable_size(*ptr) + sizeof(void *);
        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_POSIX_MEMALIGN);
+       if (unlikely(th_ctx->lock_level & 0x7F))
+               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
        _HA_ATOMIC_ADD(&bin->alloc_tot, size);
        return ret;
@@ -715,6 +737,7 @@ static int cli_parse_set_profiling(char **args, char *payload, struct appctx *ap
 
                        /* also flush current profiling stats */
                        for (i = 0; i < sizeof(memprof_stats) / sizeof(memprof_stats[0]); i++) {
+                               HA_ATOMIC_STORE(&memprof_stats[i].locked_calls, 0);
                                HA_ATOMIC_STORE(&memprof_stats[i].alloc_calls, 0);
                                HA_ATOMIC_STORE(&memprof_stats[i].free_calls, 0);
                                HA_ATOMIC_STORE(&memprof_stats[i].alloc_tot, 0);
@@ -1125,6 +1148,15 @@ static int cli_io_handler_show_profiling(struct appctx *appctx)
                        chunk_appendf(&trash," [pool=%s]", pool->name);
                }
 
+               if (entry->locked_calls) {
+                       unsigned long long tot_calls = entry->alloc_calls + entry->free_calls;
+
+                       chunk_appendf(&trash," [locked=%llu (%d.%1d %%)]",
+                                     entry->locked_calls,
+                                     (int)(100ULL * entry->locked_calls / tot_calls),
+                                     (int)((1000ULL * entry->locked_calls / tot_calls) % 10));
+               }
+
                chunk_appendf(&trash, "\n");
 
                if (applet_putchk(appctx, &trash) == -1)
index d824b2b93128b004ced07509d8c56d46aca7fc38..bcfb4fdc77845dc73405cdd1470d77b0085703cc 100644 (file)
@@ -953,7 +953,7 @@ void pool_gc(struct pool_head *pool_ctx)
        uint64_t mem_wait_start = 0;
        int isolated = thread_isolated();
 
-       if (th_ctx->flags & TH_FL_TASK_PROFILING)
+       if (unlikely(th_ctx->flags & TH_FL_TASK_PROFILING))
                mem_wait_start = now_mono_time();
 
        if (!isolated)
@@ -1031,6 +1031,8 @@ void *__pool_alloc(struct pool_head *pool, unsigned int flags)
                        struct memprof_stats *bin;
 
                        bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_P_ALLOC);
+                       if (unlikely(th_ctx->lock_level & 0x7F))
+                               _HA_ATOMIC_ADD(&bin->locked_calls, 1);
                        _HA_ATOMIC_ADD(&bin->alloc_calls, 1);
                        _HA_ATOMIC_ADD(&bin->alloc_tot, pool->size);
                        _HA_ATOMIC_STORE(&bin->info, pool);
@@ -1069,6 +1071,8 @@ void __pool_free(struct pool_head *pool, void *ptr)
                struct memprof_stats *bin;
 
                bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_P_FREE);
+               if (unlikely(th_ctx->lock_level & 0x7F))
+                       _HA_ATOMIC_ADD(&bin->locked_calls, 1);
                _HA_ATOMIC_ADD(&bin->free_calls, 1);
                _HA_ATOMIC_ADD(&bin->free_tot, pool->size);
                _HA_ATOMIC_STORE(&bin->info, pool);