aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenoît Ganne <bganne@cisco.com>2020-09-18 10:05:37 +0200
committerDamjan Marion <dmarion@me.com>2020-11-10 17:13:53 +0000
commitec4749a20f36af70d71dd37d86737a0d210fdc22 (patch)
treeb1645d6b7c764dbae4a6b70670bdc943d194b96a
parentab0a87f32780a94f4d576bb9c5bef2715188d102 (diff)
vpp: use vpp heap for libc
This makes libc use vpp main heap instead of the default libc heap. This gives better visibility (accounting, tracing) on allocations happening in external libraries called from vpp (eg. OpenSSL). Type: feature Change-Id: I5d8a673472145a4e090bedb443b8c58a967d1cca Signed-off-by: Benoît Ganne <bganne@cisco.com>
-rw-r--r--docs/troubleshooting/mem.rst84
-rw-r--r--src/vpp/CMakeLists.txt8
-rw-r--r--src/vpp/mem/mem.c121
-rw-r--r--src/vpp/mem/mem.md21
-rw-r--r--src/vppinfra/mem_dlmalloc.c12
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);
}