diff options
Diffstat (limited to 'src/dbg_malloc.cpp')
-rw-r--r-- | src/dbg_malloc.cpp | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/src/dbg_malloc.cpp b/src/dbg_malloc.cpp new file mode 100644 index 0000000..18214b1 --- /dev/null +++ b/src/dbg_malloc.cpp @@ -0,0 +1,629 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "include.h" + + +static struct otc_dbg_mem *dbg_mem = nullptr; + + +/*** + * NAME + * otc_dbg_set_metadata - + * + * ARGUMENTS + * ptr - the real address of the allocated data + * data - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_set_metadata(void *ptr, struct otc_dbg_mem_data *data) +{ + struct otc_dbg_mem_metadata *metadata; + + if (ptr == nullptr) + return; + + metadata = OT_CAST_TYPEOF(metadata, ptr); + metadata->data = (data == nullptr) ? OT_CAST_TYPEOF(data, metadata) : data; + metadata->magic = DBG_MEM_MAGIC; +} + + +/*** + * NAME + * otc_dbg_mem_add - + * + * ARGUMENTS + * func - + * line - + * ptr - the real address of the allocated data + * size - + * data - + * op_idx - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_mem_add(const char *func, int line, void *ptr, size_t size, struct otc_dbg_mem_data *data, int op_idx) +{ + (void)snprintf(data->func, sizeof(data->func), "%s:%d", func, line); + + data->ptr = ptr; + data->size = size; + data->used = 1; + + dbg_mem->size += size; + dbg_mem->op_cnt[op_idx]++; + + otc_dbg_set_metadata(ptr, data); +} + + +/*** + * NAME + * otc_dbg_mem_alloc - + * + * ARGUMENTS + * func - + * line - + * old_ptr - the address of the data returned to the program + * ptr - the real address of the allocated data + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_mem_alloc(const char *func, int line, void *old_ptr, void *ptr, size_t size) +{ + size_t i = 0; + int rc; + + if (dbg_mem == nullptr) { + return; + } + else if (ptr == nullptr) { + DBG_MEM_ERR("invalid memory address: %p", ptr); + + return; + } + + if ((rc = pthread_mutex_lock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot lock mutex: %s", otc_strerror(rc)); + + return; + } + + if (old_ptr != nullptr) { + /* Reallocating memory. */ + struct otc_dbg_mem_metadata *metadata = OT_CAST_TYPEOF(metadata, ptr); + + if (metadata == nullptr) { + DBG_MEM_ERR("no metadata: MEM_REALLOC %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); + } + else if (metadata->data == nullptr) { + DBG_MEM_ERR("invalid metadata: MEM_REALLOC %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); + } + else if (metadata->data == OT_CAST_TYPEOF(metadata->data, metadata)) { + DBG_MEM_ERR("unset metadata: MEM_REALLOC %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); + } + else if (metadata->magic != DBG_MEM_MAGIC) { + DBG_MEM_ERR("invalid magic: MEM_REALLOC %s:%d(%p -> %p %zu) 0x%016" PRIu64, func, line, old_ptr, DBG_MEM_PTR(ptr), size, metadata->magic); + } + else if (metadata->data->used && (metadata->data->ptr == DBG_MEM_DATA(old_ptr))) { + DBG_MEM_INFO(1, "MEM_REALLOC: %s:%d(%p %zu -> %p %zu)", func, line, old_ptr, metadata->data->size, DBG_MEM_PTR(ptr), size); + + dbg_mem->size -= metadata->data->size; + otc_dbg_mem_add(func, line, ptr, size, metadata->data, 1); + } + } else { + otc_dbg_set_metadata(ptr, nullptr); + + /* + * The first attempt is to find a location that has not been + * used at all so far. If such is not found, an attempt is + * made to find the first available location. + */ + if (dbg_mem->unused < dbg_mem->count) { + i = dbg_mem->unused++; + } else { + do { + if (dbg_mem->reused >= dbg_mem->count) + dbg_mem->reused = 0; + + if (!dbg_mem->data[dbg_mem->reused].used) { + i = dbg_mem->reused++; + + break; + } + + dbg_mem->reused++; + } while (++i <= dbg_mem->count); + } + + if (i < dbg_mem->count) { + DBG_MEM_INFO(1, "MEM_ALLOC: %s:%d(%p %zu %zu)", func, line, DBG_MEM_PTR(ptr), size, i); + + otc_dbg_mem_add(func, line, ptr, size, dbg_mem->data + i, 0); + } + } + + if ((rc = pthread_mutex_unlock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot unlock mutex: %s", otc_strerror(rc)); + + return; + } + + if (i >= dbg_mem->count) + DBG_MEM_ERR("alloc overflow: %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); +} + + +/*** + * NAME + * otc_dbg_mem_release - + * + * ARGUMENTS + * func - + * line - + * ptr - the address of the data returned to the program + * op_idx - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_mem_release(const char *func, int line, void *ptr, int op_idx) +{ + struct otc_dbg_mem_metadata *metadata; + bool flag_invalid = 0; + size_t i; + int rc; + + if (dbg_mem == nullptr) { + return; + } + else if (ptr == nullptr) { + DBG_MEM_ERR("invalid memory address: %p", ptr); + + return; + } + + if ((rc = pthread_mutex_lock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot lock mutex: %s", otc_strerror(rc)); + + return; + } + + metadata = DBG_MEM_DATA(ptr); + if (metadata == nullptr) { + DBG_MEM_ERR("no metadata: MEM_%s %s:%d(%p)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr); + } + else if (metadata->data == nullptr) { + DBG_MEM_ERR("invalid metadata: MEM_%s %s:%d(%p)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr); + } + else if (metadata->data == OT_CAST_TYPEOF(metadata->data, metadata)) { + DBG_MEM_ERR("unset metadata: MEM_%s %s:%d(%p)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr); + } + else if (metadata->magic != DBG_MEM_MAGIC) { + DBG_MEM_ERR("invalid magic: MEM_%s %s:%d(%p) 0x%016" PRIu64, (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr, metadata->magic); + } + else if (metadata->data->used && (metadata->data->ptr == metadata)) { + DBG_MEM_INFO(1, "MEM_%s: %s:%d(%p %zu)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr, metadata->data->size); + + metadata->data->used = 0; + + dbg_mem->size -= metadata->data->size; + dbg_mem->op_cnt[op_idx]++; + } + else { + flag_invalid = 1; + } + + if ((rc = pthread_mutex_unlock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot unlock mutex: %s", otc_strerror(rc)); + + return; + } + + if (flag_invalid) { + DBG_MEM_ERR("invalid ptr: %s:%d(%p)", func, line, ptr); + + if (metadata != nullptr) + for (i = 0; i < dbg_mem->count; i++) + if (dbg_mem->data[i].ptr == metadata) + DBG_MEM_ERR("possible previous use: %s %hhu", dbg_mem->data[i].func, dbg_mem->data[i].used); + } +} + + +/*** + * NAME + * otc_dbg_malloc - + * + * ARGUMENTS + * func - + * line - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_malloc(const char *func, int line, size_t size) +{ + void *retptr; + + retptr = malloc(DBG_MEM_SIZE(size)); + + otc_dbg_mem_alloc(func, line, nullptr, retptr, size); + + return DBG_MEM_RETURN(retptr); +} + + +/*** + * NAME + * otc_dbg_calloc - + * + * ARGUMENTS + * func - + * line - + * nelem - + * elsize - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_calloc(const char *func, int line, size_t nelem, size_t elsize) +{ + void *retptr; + + retptr = malloc(DBG_MEM_SIZE(nelem * elsize)); + if (retptr != nullptr) + (void)memset(retptr, 0, DBG_MEM_SIZE(nelem * elsize)); + + otc_dbg_mem_alloc(func, line, nullptr, retptr, nelem * elsize); + + return DBG_MEM_RETURN(retptr); +} + + +/*** + * NAME + * otc_dbg_realloc - + * + * ARGUMENTS + * func - + * line - + * ptr - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_realloc(const char *func, int line, void *ptr, size_t size) +{ + void *retptr; + + if (ptr == nullptr) { + retptr = malloc(DBG_MEM_SIZE(size)); + + otc_dbg_mem_alloc(func, line, nullptr, retptr, size); + } else { + struct otc_dbg_mem_metadata *metadata = DBG_MEM_DATA(ptr); + + /* + * If memory is not allocated via these debug functions, it + * must not be reallocated via them either. + */ + if ((metadata == nullptr) || (metadata->data == nullptr) || (metadata->magic != DBG_MEM_MAGIC)) { + retptr = realloc(ptr, size); + + return retptr; + } else { + retptr = realloc(DBG_MEM_DATA(ptr), DBG_MEM_SIZE(size)); + + otc_dbg_mem_alloc(func, line, ptr, retptr, size); + } + } + + return DBG_MEM_RETURN(retptr); +} + + +/*** + * NAME + * otc_dbg_free - + * + * ARGUMENTS + * func - + * line - + * ptr - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_dbg_free(const char *func, int line, void *ptr) +{ + struct otc_dbg_mem_metadata *metadata; + + otc_dbg_mem_release(func, line, ptr, 2); + + metadata = DBG_MEM_DATA(ptr); + if ((metadata == nullptr) || (metadata->data == nullptr) || (metadata->magic != DBG_MEM_MAGIC)) + free(ptr); + else + free(DBG_MEM_DATA(ptr)); +} + + +/*** + * NAME + * otc_dbg_strdup - + * + * ARGUMENTS + * func - + * line - + * s - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +char *otc_dbg_strdup(const char *func, int line, const char *s) +{ + size_t len = 0; + char *retptr = nullptr; + + if (s != nullptr) { + len = strlen(s) + 1; + retptr = OT_CAST_TYPEOF(retptr, malloc(DBG_MEM_SIZE(len))); + if (retptr != nullptr) + (void)memcpy(DBG_MEM_PTR(retptr), s, len); + } + + otc_dbg_mem_alloc(func, line, nullptr, retptr, len); + + return OT_CAST_TYPEOF(retptr, DBG_MEM_RETURN(retptr)); +} + + +/*** + * NAME + * otc_dbg_strndup - + * + * ARGUMENTS + * func - + * line - + * s - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +char *otc_dbg_strndup(const char *func, int line, const char *s, size_t size) +{ + size_t len = 0; + char *retptr = nullptr; + + if (s != nullptr) { + len = strlen(s); + len = (len < size) ? len : size; + retptr = OT_CAST_TYPEOF(retptr, malloc(DBG_MEM_SIZE(len + 1))); + if (retptr != nullptr) { + (void)memcpy(DBG_MEM_PTR(retptr), s, len); + DBG_MEM_PTR(retptr)[len] = '\0'; + } + } + + otc_dbg_mem_alloc(func, line, nullptr, retptr, len + 1); + + return OT_CAST_TYPEOF(retptr, DBG_MEM_RETURN(retptr)); +} + + +/*** + * NAME + * otc_dbg_mem_init - + * + * ARGUMENTS + * mem - + * data - + * count - + * level - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int otc_dbg_mem_init(struct otc_dbg_mem *mem, struct otc_dbg_mem_data *data, size_t count, uint8_t level) +{ + pthread_mutexattr_t attr; + int rc, retval = -1; + + if ((mem == nullptr) || (data == nullptr) || (count == 0)) + return retval; + + (void)memset(mem, 0, sizeof(*mem)); + (void)memset(data, 0, sizeof(*data) * count); + + dbg_mem = mem; + dbg_mem->data = data; + dbg_mem->count = count; + dbg_mem->level = level; + + if ((rc = pthread_mutexattr_init(&attr)) == 0) + if ((rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) == 0) + if ((rc = pthread_mutex_init(&(dbg_mem->mutex), &attr)) == 0) + retval = 0; + + return retval; +} + + +/*** + * NAME + * otc_dbg_mem_disable - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_dbg_mem_disable(void) +{ + if (dbg_mem == nullptr) + return; + + (void)pthread_mutex_destroy(&(dbg_mem->mutex)); + + dbg_mem = nullptr; +} + + +/*** + * NAME + * otc_dbg_mem_info - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_dbg_mem_info(void) +{ +#ifdef HAVE_MALLINFO + struct mallinfo mi; +#endif + size_t i, n = 0; + uint64_t size = 0; + + if (dbg_mem == nullptr) + return; + + DBG_MEM_INFO(0, "--- Memory info -------------------------------------"); + DBG_MEM_INFO(0, " alloc/realloc: %" PRIu64 "/%" PRIu64 ", free/release: %" PRIu64 "/%" PRIu64, dbg_mem->op_cnt[0], dbg_mem->op_cnt[1], dbg_mem->op_cnt[2], dbg_mem->op_cnt[3]); + DBG_MEM_INFO(0, " unused: %zu, reused: %zu, count: %zu", dbg_mem->unused, dbg_mem->reused, dbg_mem->count); + for (i = 0; i < dbg_mem->count; i++) + if (dbg_mem->data[i].used) { + DBG_MEM_INFO(0, " %zu %s(%p %zu)", n, dbg_mem->data[i].func, dbg_mem->data[i].ptr, dbg_mem->data[i].size); + + size += dbg_mem->data[i].size; + n++; + } + + if (n > 0) + DBG_MEM_INFO(0, " allocated %zu byte(s) in %zu chunk(s)", size, n); + + if (dbg_mem->size != size) + DBG_MEM_INFO(0, " size does not match: %zu != %zu", dbg_mem->size, size); + +#ifdef HAVE_MALLINFO + mi = mallinfo(); + DBG_MEM_INFO(0, "--- Memory space usage ------------------------------"); + DBG_MEM_INFO(0, " Total non-mmapped bytes: %" PRI_MI, mi.arena); + DBG_MEM_INFO(0, " # of free chunks: %" PRI_MI, mi.ordblks); + DBG_MEM_INFO(0, " # of free fastbin blocks: %" PRI_MI, mi.smblks); + DBG_MEM_INFO(0, " Bytes in mapped regions: %" PRI_MI, mi.hblkhd); + DBG_MEM_INFO(0, " # of mapped regions: %" PRI_MI, mi.hblks); + DBG_MEM_INFO(0, " Max. total allocated space: %" PRI_MI, mi.usmblks); + DBG_MEM_INFO(0, " Free bytes held in fastbins: %" PRI_MI, mi.fsmblks); + DBG_MEM_INFO(0, " Total allocated space: %" PRI_MI, mi.uordblks); + DBG_MEM_INFO(0, " Total free space: %" PRI_MI, mi.fordblks); + DBG_MEM_INFO(0, " Topmost releasable block: %" PRI_MI, mi.keepcost); +#endif +} + + +/*** + * NAME + * otc_dbg_memdup - + * + * ARGUMENTS + * func - + * line - + * s - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_memdup(const char *func, int line, const void *s, size_t size) +{ + void *retptr = nullptr; + + if (s != nullptr) { + retptr = malloc(DBG_MEM_SIZE(size + 1)); + if (retptr != nullptr) { + (void)memcpy(DBG_MEM_PTR(retptr), s, size); + DBG_MEM_PTR(retptr)[size] = '\0'; + } + } + + otc_dbg_mem_alloc(func, line, nullptr, retptr, size); + + return DBG_MEM_RETURN(retptr); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ |