diff options
-rw-r--r-- | docs/troubleshooting/mem.rst | 84 | ||||
-rw-r--r-- | src/vpp/CMakeLists.txt | 8 | ||||
-rw-r--r-- | src/vpp/mem/mem.c | 121 | ||||
-rw-r--r-- | src/vpp/mem/mem.md | 21 | ||||
-rw-r--r-- | src/vppinfra/mem_dlmalloc.c | 12 |
5 files changed, 241 insertions, 5 deletions
diff --git a/docs/troubleshooting/mem.rst b/docs/troubleshooting/mem.rst new file mode 100644 index 00000000000..5475051c085 --- /dev/null +++ b/docs/troubleshooting/mem.rst @@ -0,0 +1,84 @@ +.. _memleak: + +***************** +Memory leaks +***************** + +Memory traces +============= + +VPP supports memory traces to help debug (suspected) memory leaks. Each +allocation/deallocation is instrumented so that the number of allocations and +current global allocated size is maintained for each unique allocation stack +trace. + +Looking at a memory trace can help diagnose where memory is (over-)used, and +comparing memory traces at different point in time can help diagnose if and +where memory leaks happen. + +To enable memory traces on main-heap: + +.. code-block:: console + $ vppctl memory-trace on main-heap + +To dump memory traces for analysis: + +.. code-block:: console + $ vppctl show memory-trace on main-heap + Thread 0 vpp_main + base 0x7fffb6422000, size 1g, locked, unmap-on-destroy, name 'main heap' + page stats: page-size 4K, total 262144, mapped 30343, not-mapped 231801 + numa 0: 30343 pages, 118.53m bytes + total: 1023.99M, used: 115.49M, free: 908.50M, trimmable: 908.48M + free chunks 451 free fastbin blks 0 + max total allocated 1023.99M + + Bytes Count Sample Traceback + 31457440 1 0x7fffbb31ad00 clib_mem_alloc_aligned_at_offset + 0x80 + clib_mem_alloc_aligned + 0x26 + alloc_aligned_8_8 + 0xe1 + clib_bihash_instantiate_8_8 + 0x76 + clib_bihash_init2_8_8 + 0x2ec + clib_bihash_init_8_8 + 0x6a + l2fib_table_init + 0x54 + set_int_l2_mode + 0x89 + int_l3 + 0xb4 + vlib_cli_dispatch_sub_commands + 0xeee + vlib_cli_dispatch_sub_commands + 0xc62 + vlib_cli_dispatch_sub_commands + 0xc62 + 266768 5222 0x7fffbd79f978 clib_mem_alloc_aligned_at_offset + 0x80 + vec_resize_allocate_memory + 0xa8 + _vec_resize_inline + 0x240 + unix_cli_file_add + 0x83d + unix_cli_listen_read_ready + 0x10b + linux_epoll_input_inline + 0x943 + linux_epoll_input + 0x39 + dispatch_node + 0x336 + vlib_main_or_worker_loop + 0xbf1 + vlib_main_loop + 0x1a + vlib_main + 0xae7 + thread0 + 0x3e + .... + +libc memory traces +================== + +Internal VPP memory allocations rely on VPP main-heap, however when using +external libraries, esp. in plugins (eg. OpenSSL library used by the IKEv2 +plugin), those external libraries usually manages memory using the standard +libc malloc()/free()/... calls. This, in turn, makes use of the default +libc heap. + +VPP has no knowledge of this heap and tools such as memory traces cannot be +used. + +In order to enable the use of standard VPP debugging tools, this library +replaces standard libc memory management calls with version using VPP +main-heap. + +To use it, you need to use the `LD_PRELOAD` mechanism, eg. + +.. code-block:: console + ~# LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libvppmem_preload.so /usr/bin/vpp -c /etc/vpp/startup.conf + +You can then use tools such as memory traces as usual. diff --git a/src/vpp/CMakeLists.txt b/src/vpp/CMakeLists.txt index ae1e4d76c02..0a8ec31601b 100644 --- a/src/vpp/CMakeLists.txt +++ b/src/vpp/CMakeLists.txt @@ -124,5 +124,13 @@ add_vpp_executable(vpp_prometheus_export DEPENDS api_headers ) +############################################################################## +# vppmem_preload library +############################################################################## +add_vpp_library(vppmem_preload + SOURCES mem/mem.c + LINK_LIBRARIES vppinfra +) + install(FILES conf/startup.conf DESTINATION etc/vpp COMPONENT vpp) install(FILES conf/80-vpp.conf DESTINATION etc/sysctl.d COMPONENT vpp) diff --git a/src/vpp/mem/mem.c b/src/vpp/mem/mem.c new file mode 100644 index 00000000000..d438c970447 --- /dev/null +++ b/src/vpp/mem/mem.c @@ -0,0 +1,121 @@ +#include <stdio.h> +#include <vppinfra/mem.h> + +extern void * __libc_malloc (size_t); +extern void __libc_free (void *); +extern void * __libc_calloc (size_t, size_t); +extern void * __libc_realloc (void *, size_t); +extern void * __libc_valloc (size_t); +extern void * __libc_memalign (size_t, size_t); +extern void * __libc_pvalloc (size_t); + +__thread u64 vpp_mem_no_vpp_heap; + +static void no_heap (void) +{ + vpp_mem_no_vpp_heap++; + + if (1 == vpp_mem_no_vpp_heap) + fprintf (stderr, "vpp mem: libc allocation requested but no vpp heap ready, defaulting to libc.\n"); +} + +static_always_inline int +check_vpp_heap (void) +{ + if (PREDICT_TRUE (clib_mem_get_per_cpu_heap () != 0)) + return 1; + + no_heap (); + return 0; +} + +void * +malloc(size_t size) +{ + if (!check_vpp_heap ()) + return __libc_malloc (size); + + return clib_mem_alloc (size); +} + +void +free(void *p) +{ + if (!p) + return; + + if (!check_vpp_heap ()) + return __libc_free (p); + + clib_mem_free (p); +} + +void * +calloc(size_t nmemb, size_t size) +{ + void * p; + + if (!check_vpp_heap ()) + return __libc_calloc (nmemb, size); + + p = clib_mem_alloc (nmemb * size); + clib_memset (p, 0, nmemb * size); + return p; +} + +void * +realloc(void *p, size_t size) +{ + if (!check_vpp_heap ()) + return __libc_realloc (p, size); + + return clib_mem_realloc (p, size, clib_mem_size (p)); +} + +int +posix_memalign(void **memptr, size_t alignment, size_t size) +{ + if (!check_vpp_heap ()) + *memptr = __libc_memalign (alignment, size); + else + *memptr = clib_mem_alloc_aligned (size, alignment); + return 0; +} + +void * +aligned_alloc(size_t alignment, size_t size) +{ + if (!check_vpp_heap ()) + return __libc_memalign (alignment, size); + + return clib_mem_alloc_aligned (size, alignment); +} + +void * +valloc(size_t size) +{ + if (!check_vpp_heap ()) + return __libc_valloc (size); + + return clib_mem_alloc_aligned (size, clib_mem_get_page_size ()); +} + +void *memalign(size_t alignment, size_t size) +{ + if (!check_vpp_heap ()) + return __libc_memalign (alignment, size); + + return clib_mem_alloc_aligned (size, alignment); +} + +void * +pvalloc(size_t size) +{ + uword pagesz; + + if (!check_vpp_heap ()) + return __libc_pvalloc (size); + + pagesz = clib_mem_get_page_size (); + return clib_mem_alloc_aligned (round_pow2 (size, pagesz), pagesz); +} diff --git a/src/vpp/mem/mem.md b/src/vpp/mem/mem.md new file mode 100644 index 00000000000..84ab820e5e5 --- /dev/null +++ b/src/vpp/mem/mem.md @@ -0,0 +1,21 @@ +# VPP mem preload {#mempreload_doc} + +Internal VPP memory allocations rely on VPP main-heap, however when using +external libraries, esp. in plugins (eg. OpenSSL library used by the IKEv2 +plugin), those external libraries usually manages memory using the standard +libc `malloc()`/`free()`/... calls. This, in turn, makes use of the default +libc heap. + +VPP has no knowledge of this heap and tools such as memory traces cannot be +used. + +In order to enable the use of standard VPP debugging tools, this library +replaces standard libc memory management calls with version using VPP +main-heap. + +To use it, you need to use the `LD_PRELOAD` mechanism, eg. +``` +~# LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libvppmem_preload.so /usr/bin/vpp -c /etc/vpp/startup.conf +``` + +You can then use tools such as memory traces as usual. diff --git a/src/vppinfra/mem_dlmalloc.c b/src/vppinfra/mem_dlmalloc.c index df1489fd8d8..bc6561a738e 100644 --- a/src/vppinfra/mem_dlmalloc.c +++ b/src/vppinfra/mem_dlmalloc.c @@ -74,17 +74,17 @@ mheap_get_trace (uword offset, uword size) /* Spurious Coverity warnings be gone. */ clib_memset (&trace, 0, sizeof (trace)); - /* Skip our frame and mspace_get_aligned's frame */ - n_callers = clib_backtrace (trace.callers, ARRAY_LEN (trace.callers), 2); - if (n_callers == 0) - return; - clib_spinlock_lock (&tm->lock); /* Turn off tracing to avoid embarrassment... */ save_enabled = tm->enabled; tm->enabled = 0; + /* Skip our frame and mspace_get_aligned's frame */ + n_callers = clib_backtrace (trace.callers, ARRAY_LEN (trace.callers), 2); + if (n_callers == 0) + goto out; + if (!tm->trace_by_callers) tm->trace_by_callers = hash_create_shmem (0, sizeof (trace.callers), sizeof (uword)); @@ -137,6 +137,8 @@ mheap_get_trace (uword offset, uword size) t->n_bytes += size; t->offset = offset; /* keep a sample to autopsy */ hash_set (tm->trace_index_by_offset, offset, t - tm->traces); + +out: tm->enabled = save_enabled; clib_spinlock_unlock (&tm->lock); } |