frameworks_bluetooth/debug/bt_memory.c

167 lines
4.5 KiB
C

/****************************************************************************
* Copyright (C) 2025 Xiaomi Corporation
*
* 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 "bt_memory.h"
#include <assert.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#define GUARD_SIZE 16
#define GUARD_HEAD 0xAA
#define GUARD_TAIL 0xBB
typedef struct bt_mem_node {
void* addr;
size_t size;
const char* file;
int line;
unsigned char guard[GUARD_SIZE];
struct bt_mem_node* next;
} bt_mem_node_t;
typedef struct bt_mem_manager {
bt_mem_node_t* mem_list;
pthread_mutex_t list_lock;
size_t current_mem;
size_t peak_mem;
} bt_mem_manager_t;
static bt_mem_manager_t g_mem_manager = {
.mem_list = NULL,
.list_lock = PTHREAD_MUTEX_INITIALIZER,
.current_mem = 0,
.peak_mem = 0,
};
void* bt_malloc_hook(size_t size, const char* file, int line)
{
bt_mem_manager_t* mem_manager = &g_mem_manager;
void* raw;
bt_mem_node_t* head;
raw = malloc(size + sizeof(bt_mem_node_t) + GUARD_SIZE);
if (!raw) {
syslog(LOG_ALERT, "[bt_memory] malloc failed, size: %zu, file: %s, line: %d", size, file, line);
return NULL;
}
head = (bt_mem_node_t*)raw;
*head = (bt_mem_node_t) {
.addr = (char*)raw + sizeof(bt_mem_node_t),
.size = size,
.file = file,
.line = line,
.next = NULL
};
memset(head->guard, GUARD_HEAD, sizeof(head->guard));
memset((char*)head->addr + size, GUARD_TAIL, GUARD_SIZE);
pthread_mutex_lock(&mem_manager->list_lock);
mem_manager->current_mem += size;
if (mem_manager->current_mem > mem_manager->peak_mem) {
mem_manager->peak_mem = mem_manager->current_mem;
}
head->next = mem_manager->mem_list;
mem_manager->mem_list = head;
pthread_mutex_unlock(&mem_manager->list_lock);
return head->addr;
}
void* bt_calloc_hook(size_t num, size_t size, const char* file, int line)
{
size_t total;
void* ptr;
total = num * size;
ptr = bt_malloc_hook(total, file, line);
if (!ptr) {
syslog(LOG_ALERT, "[bt_memory] calloc failed, num: %zu, size: %zu, file: %s, line: %d", num, size, file, line);
return NULL;
}
memset(ptr, 0, total);
return ptr;
}
void bt_free_hook(void* ptr)
{
bt_mem_manager_t* mem_manager = &g_mem_manager;
bt_mem_node_t** pp;
bool found = false;
if (!ptr) {
return;
}
pthread_mutex_lock(&mem_manager->list_lock);
pp = &mem_manager->mem_list;
while (*pp) {
if ((*pp)->addr == ptr) {
bt_mem_node_t* node = *pp;
for (int i = 0; i < GUARD_SIZE; i++) {
if (((uint8_t)node->guard[i] != GUARD_HEAD) || ((uint8_t)((char*)ptr + node->size)[i] != GUARD_TAIL)) {
syslog(LOG_ALERT, "[bt_memory] Buffer overflow at %s:%d", node->file, node->line);
assert(0);
}
}
mem_manager->current_mem -= node->size;
*pp = node->next;
found = true;
free(node);
break;
}
pp = &(*pp)->next;
}
pthread_mutex_unlock(&mem_manager->list_lock);
if (!found) {
syslog(LOG_ALERT, "[bt_memory] Freeing unallocated memory %p", ptr);
assert(0);
}
}
void bt_report_leak(void)
{
bt_mem_manager_t* mem_manager = &g_mem_manager;
bt_mem_node_t* node;
pthread_mutex_lock(&mem_manager->list_lock);
syslog(LOG_ALERT, "[bt_memory] ===== Memory Leak Report =====");
syslog(LOG_ALERT, "[bt_memory] Peak memory: %zu bytes", mem_manager->peak_mem);
node = mem_manager->mem_list;
while (node) {
syslog(LOG_ALERT, "[bt_memory] Leak %p (%zu bytes) at %s:%d",
node->addr, node->size, node->file, node->line);
node = node->next;
}
syslog(LOG_ALERT, "[bt_memory] ===== End of Report =====");
pthread_mutex_unlock(&mem_manager->list_lock);
}