From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 7FC19A058A; Fri, 17 Apr 2020 10:04:03 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 5B3401DE7B; Fri, 17 Apr 2020 10:03:57 +0200 (CEST) Received: from git-send-mailer.rdmz.labs.mlnx (unknown [37.142.13.130]) by dpdk.org (Postfix) with ESMTP id 11B391DE40 for ; Fri, 17 Apr 2020 10:03:55 +0200 (CEST) From: Xueming Li To: Anatoly Burakov , Ferruh Yigit , Stephen Hemminger Cc: dev@dpdk.org, Asaf Penso Date: Fri, 17 Apr 2020 08:03:42 +0000 Message-Id: <1587110623-405-2-git-send-email-xuemingl@mellanox.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1587110623-405-1-git-send-email-xuemingl@mellanox.com> References: <1587110623-405-1-git-send-email-xuemingl@mellanox.com> In-Reply-To: <1586318694-31358-1-git-send-email-xuemingl@mellanox.com> References: <1586318694-31358-1-git-send-email-xuemingl@mellanox.com> Subject: [dpdk-dev] [RFC v2 1/2] malloc: support malloc and free tracking log X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" This patch introduces new feature to track rte_malloc leakage by logging malloc and free. Signed-off-by: Xueming Li --- 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 #include #include +#include #include #include @@ -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