summaryrefslogtreecommitdiffstats
path: root/src/dbg_malloc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dbg_malloc.cpp')
-rw-r--r--src/dbg_malloc.cpp629
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
+ */