DEBUG: pool: store the memprof bin on alloc() and update it on free()

When looking at "show pools", it's often difficult to know which alloc()
corresponds to which free() since it's not often 1:1. But sometimes we
have all elements available to maintain a link between alloc and free.
Indeed, when the caller is recorded in the allocated area, we can store
the pointer to the just created bin instead of the caller address itself,
since the caller address is already in the memprof bin. By doing so, we
permit the pool_free() call to locate the allocator bin and update its
free count when caller tracing is enabled. This for example allows to
produce outputs like this on "show profiling" and a process started with
-dMcaller:

  1391967  1391968  22805987328  22806003712|  0x59f72f process_stream+0x19f/0x3a7a p_alloc(0) [delta=-16384] [pool=buffer]
  1391936  1391937  22805479424  22805495808|  0x6e1476 task_run_applet+0x426/0xea2 p_alloc(0) [delta=-16384] [pool=buffer]
  1391925  1391925  22805299200  22805299200|  0x58435a main+0xdf07a p_alloc(0) [delta=0] [pool=buffer]
        0  2087930            0  34208645120|  0x59b519 stream_release_buffers+0xf9/0x110 p_free(-16384) [pool=buffer]
   695993   695992  11403149312  11403132928|  0x66018f main+0x1baeaf p_alloc(0) [delta=16384] [pool=buffer]
        0  1391957            0  22805823488|  0x59b47c stream_release_buffers+0x5c/0x110 p_free(-16384) [pool=buffer]
   695968   695970  11402739712  11402772480|  0x587b85 h1_io_cb+0x9a5/0xe7c p_alloc(0) [delta=-32768] [pool=buffer]
        0  1391923            0  22805266432|  0x57f388 main+0xda0a8 p_free(-16384) [pool=buffer]
   695959   695960  11402592256  11402608640|  0x586add main+0xe17fd p_alloc(0) [delta=-16384] [pool=buffer]
          0      695978              0    11402903552|         0x59cc58 stream_free+0x178/0x9ea p_free(-16384) [pool=buffer]
(...)

Here it's quickly visible that all of them got properly released.
This commit is contained in:
Willy Tarreau 2023-10-17 11:13:00 +02:00
parent 68d02e5fa9
commit 5714aff4a6

View File

@ -844,12 +844,20 @@ void *__pool_alloc(struct pool_head *pool, unsigned int flags)
if (likely(p)) {
#ifdef USE_MEMORY_PROFILING
if (unlikely(profiling & HA_PROF_MEMORY)) {
extern struct memprof_stats memprof_stats[MEMPROF_HASH_BUCKETS + 1];
struct memprof_stats *bin;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_P_ALLOC);
_HA_ATOMIC_ADD(&bin->alloc_calls, 1);
_HA_ATOMIC_ADD(&bin->alloc_tot, pool->size);
_HA_ATOMIC_STORE(&bin->info, pool);
/* replace the caller with the allocated bin: this way
* we'll the pool_free() call will be able to update our
* entry. We only do it for non-colliding entries though,
* since thse ones store the true caller location.
*/
if (bin >= &memprof_stats[0] && bin < &memprof_stats[MEMPROF_HASH_BUCKETS])
POOL_DEBUG_TRACE_CALLER(pool, (struct pool_cache_item *)p, bin);
}
#endif
if (unlikely(flags & POOL_F_MUST_ZERO))
@ -874,12 +882,22 @@ void __pool_free(struct pool_head *pool, void *ptr)
#ifdef USE_MEMORY_PROFILING
if (unlikely(profiling & HA_PROF_MEMORY) && ptr) {
extern struct memprof_stats memprof_stats[MEMPROF_HASH_BUCKETS + 1];
struct memprof_stats *bin;
bin = memprof_get_bin(__builtin_return_address(0), MEMPROF_METH_P_FREE);
_HA_ATOMIC_ADD(&bin->free_calls, 1);
_HA_ATOMIC_ADD(&bin->free_tot, pool->size);
_HA_ATOMIC_STORE(&bin->info, pool);
/* check if the caller is an allocator, and if so, let's update
* its free() count.
*/
bin = *(struct memprof_stats**)(((char *)ptr) + pool->alloc_sz - sizeof(void*));
if (bin >= &memprof_stats[0] && bin < &memprof_stats[MEMPROF_HASH_BUCKETS]) {
_HA_ATOMIC_ADD(&bin->free_calls, 1);
_HA_ATOMIC_ADD(&bin->free_tot, pool->size);
}
}
#endif