From: Xueming Li <xuemingl@mellanox.com>
To: Anatoly Burakov <anatoly.burakov@intel.com>,
Ferruh Yigit <ferruh.yigit@intel.com>,
Stephen Hemminger <stephen@networkplumber.org>
Cc: dev@dpdk.org, Asaf Penso <asafp@mellanox.com>
Subject: [dpdk-dev] [RFC v2 1/2] malloc: support malloc and free tracking log
Date: Fri, 17 Apr 2020 08:03:42 +0000 [thread overview]
Message-ID: <1587110623-405-2-git-send-email-xuemingl@mellanox.com> (raw)
In-Reply-To: <1587110623-405-1-git-send-email-xuemingl@mellanox.com>
In-Reply-To: <1586318694-31358-1-git-send-email-xuemingl@mellanox.com>
This patch introduces new feature to track rte_malloc leakage by logging
malloc and free.
Signed-off-by: Xueming Li <xuemingl@mellanox.com>
---
lib/librte_eal/common/eal_memcfg.h | 26 ++++
lib/librte_eal/common/malloc_elem.h | 6 +-
lib/librte_eal/common/rte_malloc.c | 261 +++++++++++++++++++++++++++++++++++-
lib/librte_eal/include/rte_malloc.h | 39 +++++-
lib/librte_eal/rte_eal_version.map | 3 +
5 files changed, 327 insertions(+), 8 deletions(-)
diff --git a/lib/librte_eal/common/eal_memcfg.h b/lib/librte_eal/common/eal_memcfg.h
index 583fcb5..49d59d8 100644
--- a/lib/librte_eal/common/eal_memcfg.h
+++ b/lib/librte_eal/common/eal_memcfg.h
@@ -14,6 +14,28 @@
#include "malloc_heap.h"
+
+enum rte_malloc_log_type {
+ rte_malloc_log_malloc,
+ rte_malloc_log_realloc,
+ rte_malloc_log_free,
+};
+
+/**
+ * Memory allocation and free tracking log
+ */
+struct rte_malloc_log {
+ int socket;
+ void *addr;
+ size_t req_size; /* requested size */
+ size_t size; /* outer element and pad */
+ size_t align;
+ uint32_t pad;
+ uint32_t type:2; /* log entry type. */
+ uint32_t paired:1; /* allocation and free both tracked. */
+ char name[64]; /* name may come from stack, copy. */
+};
+
/**
* Memory configuration shared across multiple processes.
*/
@@ -73,6 +95,10 @@ struct rte_mem_config {
/**< TSC rate */
uint8_t dma_maskbits; /**< Keeps the more restricted dma mask. */
+
+ struct rte_malloc_log *malloc_logs; /**< Log entries. */
+ int malloc_log_max; /**< Max number of logs to write. */
+ int malloc_log_count; /**< count of logs written. */
};
/* update internal config from shared mem config */
diff --git a/lib/librte_eal/common/malloc_elem.h b/lib/librte_eal/common/malloc_elem.h
index a1e5f7f..006480c 100644
--- a/lib/librte_eal/common/malloc_elem.h
+++ b/lib/librte_eal/common/malloc_elem.h
@@ -9,6 +9,8 @@
#define MIN_DATA_SIZE (RTE_CACHE_LINE_SIZE)
+#define MALLOC_TRACKING_MAX (1 << 29)
+
/* dummy definition of struct so we can use pointers to it in malloc_elem struct */
struct malloc_heap;
@@ -27,7 +29,9 @@ struct malloc_elem {
LIST_ENTRY(malloc_elem) free_list;
/**< list of free elements in heap */
struct rte_memseg_list *msl;
- volatile enum elem_state state;
+ volatile uint32_t state:2;
+ volatile uint32_t log:1; /* Element logged. */
+ volatile uint32_t log_index:29; /* Element log index. */
uint32_t pad;
size_t size;
struct malloc_elem *orig_elem;
diff --git a/lib/librte_eal/common/rte_malloc.c b/lib/librte_eal/common/rte_malloc.c
index d6026a2..2e30693 100644
--- a/lib/librte_eal/common/rte_malloc.c
+++ b/lib/librte_eal/common/rte_malloc.c
@@ -6,6 +6,7 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
+#include <assert.h>
#include <sys/queue.h>
#include <rte_errno.h>
@@ -29,12 +30,245 @@
#include "eal_private.h"
+const char *rte_malloc_log_type_name[] = { "malloc", "realloc", "free" };
+
+/*
+ * Track and log memory allocation information.
+ */
+static void
+rte_malloc_log(const char *type, size_t size, unsigned int align,
+ void *addr)
+{
+ struct rte_malloc_log *log;
+ struct malloc_elem *elem = malloc_elem_from_data(addr);
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (!addr || !elem || !mcfg->malloc_log_max ||
+ mcfg->malloc_log_max == mcfg->malloc_log_count)
+ return;
+
+ log = &mcfg->malloc_logs[mcfg->malloc_log_count];
+ if (type)
+ strncpy(log->name, type, sizeof(log->name) - 1);
+ log->req_size = size;
+ log->pad = elem->pad;
+ log->size = elem->size + elem->pad;
+ align = align ? align : 1;
+ log->align = RTE_CACHE_LINE_ROUNDUP(align);
+ log->addr = addr;
+ log->socket = elem->heap->socket_id;
+ elem->log = 1;
+ elem->log_index = mcfg->malloc_log_count++;
+}
+
+/*
+ * track and log memory realloc
+ */
+static void
+rte_realloc_log(uint32_t pref_malloc, uint32_t prev_index,
+ void *new_addr, size_t size, unsigned int align)
+{
+ struct rte_malloc_log *prev_log;
+ struct rte_malloc_log *log;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ struct malloc_elem *elem = malloc_elem_from_data(new_addr);
+
+ if (!mcfg->malloc_log_max || !elem)
+ return;
+ if (pref_malloc) {
+ prev_log = &mcfg->malloc_logs[prev_index];
+ prev_log->paired = 1;
+ }
+ if (mcfg->malloc_log_max == mcfg->malloc_log_count)
+ return;
+ log = &mcfg->malloc_logs[mcfg->malloc_log_count++];
+ log->addr = new_addr;
+ log->type = rte_malloc_log_realloc;
+ log->req_size = size;
+ if (pref_malloc)
+ strncpy(log->name, prev_log->name, sizeof(log->name));
+ log->pad = elem->pad;
+ log->size = elem->size + elem->pad;
+ align = align ? align : 1;
+ log->align = RTE_CACHE_LINE_ROUNDUP(align);
+ log->socket = elem->heap->socket_id;
+ elem->log = 1;
+ elem->log_index = mcfg->malloc_log_count++;
+}
+
+/*
+ * track and log memory free
+ */
+static void
+rte_free_log(void *addr, uint32_t log_malloc, uint32_t log_index, size_t size)
+{
+ struct rte_malloc_log *alloc_log;
+ struct rte_malloc_log *log;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (!addr || !mcfg->malloc_log_max)
+ return;
+ if (log_malloc) {
+ alloc_log = &mcfg->malloc_logs[log_index];
+ alloc_log->paired = 1;
+ }
+ if (mcfg->malloc_log_max == mcfg->malloc_log_count)
+ return;
+ log = &mcfg->malloc_logs[mcfg->malloc_log_count++];
+ log->addr = addr;
+ log->type = rte_malloc_log_free;
+ log->size = size;
+ if (log_malloc) {
+ strncpy(log->name, alloc_log->name, sizeof(log->name));
+ log->req_size = alloc_log->req_size;
+ log->align = alloc_log->align;
+ log->socket = alloc_log->socket;
+ log->pad = alloc_log->pad;
+ log->paired = 1;
+ }
+}
+
+/*
+ * Initialize malloc tracking with max count of log entries
+ */
+int
+rte_malloc_log_init(uint32_t count)
+{
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (mcfg->malloc_log_max) {
+ RTE_LOG(ERR, EAL, "malloc tracking started\n");
+ return -EINVAL;
+ }
+ if (!count || count > MALLOC_TRACKING_MAX) {
+ RTE_LOG(ERR, EAL, "invalid malloc tracking log number\n");
+ return -EINVAL;
+ }
+
+ /* allocate logs. */
+ mcfg->malloc_logs = rte_calloc("malloc_logs", count,
+ sizeof(*mcfg->malloc_logs), 0);
+ if (!mcfg->malloc_logs)
+ return -ENOMEM;
+
+ mcfg->malloc_log_count = 0;
+ mcfg->malloc_log_max = count;
+ return 0;
+}
+
+/*
+ * Initialize malloc tracking with max count of log entries
+ */
+int
+rte_malloc_log_stop(void)
+{
+ struct malloc_elem *elem;
+ int i;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+ struct rte_malloc_log *log;
+
+ if (!mcfg->malloc_log_max) {
+ RTE_LOG(ERR, EAL, "malloc tracking not started\n");
+ return -EINVAL;
+ }
+
+ /* release all logs. */
+ for (i = 0; i < mcfg->malloc_log_count; i++) {
+ log = &mcfg->malloc_logs[i];
+ /* clear log info from malloc elem. */
+ if (log->type)
+ continue;
+ elem = malloc_elem_from_data(log->addr);
+ if (elem) {
+ elem->log = 0;
+ elem->log_index = 0;
+ }
+ }
+
+ rte_free(mcfg->malloc_logs);
+ mcfg->malloc_logs = NULL;
+ mcfg->malloc_log_max = 0;
+ mcfg->malloc_log_count = 0;
+
+ return 0;
+}
+
+/*
+ * Dump malloc tracking
+ */
+void
+rte_malloc_log_dump(FILE *out, uint32_t detail)
+{
+ struct rte_malloc_log *log;
+ int i;
+ uint32_t n_alloc_free = 0;
+ uint32_t n_alloc = 0;
+ uint32_t n_free_alloc = 0;
+ uint32_t n_free = 0;
+ size_t alloc_leak = 0;
+ size_t free_leak = 0;
+ struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
+
+ if (!mcfg->malloc_log_max || !mcfg->malloc_logs) {
+ RTE_LOG(ERR, EAL, "malloc tracking not started\n");
+ return;
+ }
+ if (detail)
+ fprintf(out, "Socket Action Paired Address Size Align Mgmt Pad Total Type\n");
+ for (i = 0; i < mcfg->malloc_log_count; i++) {
+ log = &mcfg->malloc_logs[i];
+ if (detail > 1 ||
+ (detail == 1 && !log->paired &&
+ log->type != rte_malloc_log_free))
+ fprintf(out, "%6d %7s %6s %18p %7lu %5lu %4d %3u %7lu %s\n",
+ log->socket,
+ rte_malloc_log_type_name[log->type],
+ log->paired ? "y" : "n",
+ log->addr, log->req_size, log->align,
+ MALLOC_ELEM_OVERHEAD,
+ log->pad,
+ log->size,
+ log->name);
+ if (log->type == rte_malloc_log_free) {
+ n_free++;
+ if (log->paired)
+ n_free_alloc++;
+ else
+ free_leak += log->size;
+ } else {
+ n_alloc++;
+ if (log->paired)
+ n_alloc_free++;
+ else
+ alloc_leak += log->size;
+ }
+ }
+ fprintf(out, "Total rte_malloc and rte_realloc: %u/%u leak %zu(B), rte_free: %u/%u leak: %zu(B)\n",
+ n_alloc_free, n_alloc, alloc_leak,
+ n_free_alloc, n_free, free_leak);
+ if (mcfg->malloc_log_count == mcfg->malloc_log_max)
+ fprintf(out, "Warning: log full, please consider larger log buffer\n");
+}
+
/* Free the memory space back to heap */
void rte_free(void *addr)
{
+ struct malloc_elem *elem = malloc_elem_from_data(addr);
+ uint32_t log_malloc, log_index;
+ size_t size;
+
if (addr == NULL) return;
- if (malloc_heap_free(malloc_elem_from_data(addr)) < 0)
+ if (elem) {
+ log_malloc = elem->log;
+ log_index = elem->log_index;
+ size = elem->size + elem->pad;
+ if (malloc_heap_free(elem) < 0)
+ RTE_LOG(ERR, EAL, "Error: Invalid memory\n");
+ else
+ rte_free_log(addr, log_malloc, log_index, size);
+ } else {
RTE_LOG(ERR, EAL, "Error: Invalid memory\n");
+ }
}
/*
@@ -44,6 +278,8 @@ void rte_free(void *addr)
rte_malloc_socket(const char *type, size_t size, unsigned int align,
int socket_arg)
{
+ void *ret;
+
/* return NULL if size is 0 or alignment is not power-of-2 */
if (size == 0 || (align && !rte_is_power_of_2(align)))
return NULL;
@@ -57,8 +293,13 @@ void rte_free(void *addr)
!rte_eal_has_hugepages())
socket_arg = SOCKET_ID_ANY;
- return malloc_heap_alloc(type, size, socket_arg, 0,
+ ret = malloc_heap_alloc(type, size, socket_arg, 0,
align == 0 ? 1 : align, 0, false);
+
+ if (ret)
+ rte_malloc_log(type, size, align == 0 ? 1 : align, ret);
+
+ return ret;
}
/*
@@ -123,10 +364,12 @@ void rte_free(void *addr)
void *
rte_realloc_socket(void *ptr, size_t size, unsigned int align, int socket)
{
+ struct malloc_elem *elem = malloc_elem_from_data(ptr);
+ uint32_t log_malloc, log_index;
+
if (ptr == NULL)
return rte_malloc_socket(NULL, size, align, socket);
- struct malloc_elem *elem = malloc_elem_from_data(ptr);
if (elem == NULL) {
RTE_LOG(ERR, EAL, "Error: memory corruption detected\n");
return NULL;
@@ -139,9 +382,15 @@ void rte_free(void *addr)
*/
if ((socket == SOCKET_ID_ANY ||
(unsigned int)socket == elem->heap->socket_id) &&
- RTE_PTR_ALIGN(ptr, align) == ptr &&
- malloc_heap_resize(elem, size) == 0)
- return ptr;
+ RTE_PTR_ALIGN(ptr, align) == ptr) {
+ log_malloc = elem->log;
+ log_index = elem->log_index;
+ if (malloc_heap_resize(elem, size) == 0) {
+ rte_realloc_log(log_malloc, log_index, ptr, size,
+ align);
+ return ptr;
+ }
+ }
/* either requested socket id doesn't match, alignment is off
* or we have no room to expand,
diff --git a/lib/librte_eal/include/rte_malloc.h b/lib/librte_eal/include/rte_malloc.h
index 42ca051..4947ba9 100644
--- a/lib/librte_eal/include/rte_malloc.h
+++ b/lib/librte_eal/include/rte_malloc.h
@@ -508,7 +508,44 @@ struct rte_malloc_socket_stats {
* to dump all objects.
*/
void
-rte_malloc_dump_stats(FILE *f, const char *type);
+rte_malloc_dump_stats(FILE *out, const char *type);
+
+/**
+ * Initialize malloc tracking log buffer.
+ *
+ * @param count
+ * Max count of tracking log entries, range (1 : 1 << 29)
+ * @return
+ * 0 on success, negative errno otherwise
+ */
+__rte_experimental
+int
+rte_malloc_log_init(uint32_t count);
+
+/**
+ * Stop malloc tracking log.
+ *
+ * @return
+ * 0 on success, negative errno otherwise
+ */
+__rte_experimental
+int
+rte_malloc_log_stop(void);
+
+/**
+ * Dump malloc tracking log to output.
+ *
+ * @param out
+ * output file handle
+ * @param detail
+ * detail level of output
+ * 0: summary
+ * 1: potential leaks - allocated without free
+ * 2: all entries
+ */
+__rte_experimental
+void
+rte_malloc_log_dump(FILE *out, uint32_t detail);
/**
* Dump contents of all malloc heaps to a file.
diff --git a/lib/librte_eal/rte_eal_version.map b/lib/librte_eal/rte_eal_version.map
index f9ede5b..5151057 100644
--- a/lib/librte_eal/rte_eal_version.map
+++ b/lib/librte_eal/rte_eal_version.map
@@ -338,4 +338,7 @@ EXPERIMENTAL {
# added in 20.05
rte_log_can_log;
+ rte_malloc_log_init;
+ rte_malloc_log_dump;
+ rte_malloc_log_stop;
};
--
1.8.3.1
next prev parent reply other threads:[~2020-04-17 8:04 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-04-03 7:54 [dpdk-dev] [RFC] malloc: add malloc and free log function Xueming Li
2020-04-03 7:54 ` Xueming Li
2020-04-03 9:50 ` Burakov, Anatoly
2020-04-08 4:04 ` [dpdk-dev] [PATCH v1 0/2] malloc: support malloc and free tracking log Xueming Li
2020-04-17 8:03 ` [dpdk-dev] [RFC v2 " Xueming Li
2020-04-17 8:03 ` Xueming Li [this message]
2020-04-17 8:03 ` [dpdk-dev] [RFC v2 2/2] app/testpmd: " Xueming Li
2020-04-21 13:41 ` Iremonger, Bernard
2020-07-30 15:10 ` Somnath Kotur
2020-07-30 15:13 ` Xueming(Steven) Li
2020-07-30 15:30 ` Somnath Kotur
2020-04-08 4:04 ` [dpdk-dev] [PATCH v1 1/2] malloc: " Xueming Li
2020-04-08 5:10 ` Stephen Hemminger
2020-04-08 5:11 ` Stephen Hemminger
2020-04-08 6:45 ` Xueming(Steven) Li
2020-04-08 4:04 ` [dpdk-dev] [PATCH v1 2/2] app/testpmd: " Xueming Li
2020-04-16 10:10 ` Iremonger, Bernard
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1587110623-405-2-git-send-email-xuemingl@mellanox.com \
--to=xuemingl@mellanox.com \
--cc=anatoly.burakov@intel.com \
--cc=asafp@mellanox.com \
--cc=dev@dpdk.org \
--cc=ferruh.yigit@intel.com \
--cc=stephen@networkplumber.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).