/****************************************************************************** * * Copyright (C) 2014 Google, Inc. * * 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 #include "bt_common.h" #include "osi/allocator.h" #if HEAP_MEMORY_STATS #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "freertos/semphr.h" #endif extern void *pvPortZalloc(size_t size); extern void vPortFree(void *pv); #if HEAP_MEMORY_DEBUG #define OSI_MEM_DBG_INFO_MAX 1024*3 typedef struct { void *p; int size; const char *func; int line; } osi_mem_dbg_info_t; static uint32_t mem_dbg_count = 0; static osi_mem_dbg_info_t mem_dbg_info[OSI_MEM_DBG_INFO_MAX]; static uint32_t mem_dbg_current_size = 0; static uint32_t mem_dbg_max_size = 0; #define OSI_MEM_DBG_MAX_SECTION_NUM 5 typedef struct { bool used; uint32_t max_size; } osi_mem_dbg_max_size_section_t; static osi_mem_dbg_max_size_section_t mem_dbg_max_size_section[OSI_MEM_DBG_MAX_SECTION_NUM]; void osi_mem_dbg_init(void) { int i; for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) { mem_dbg_info[i].p = NULL; mem_dbg_info[i].size = 0; mem_dbg_info[i].func = NULL; mem_dbg_info[i].line = 0; } mem_dbg_count = 0; mem_dbg_current_size = 0; mem_dbg_max_size = 0; for (i = 0; i < OSI_MEM_DBG_MAX_SECTION_NUM; i++){ mem_dbg_max_size_section[i].used = false; mem_dbg_max_size_section[i].max_size = 0; } } void osi_mem_dbg_record(void *p, int size, const char *func, int line) { int i; if (!p || size == 0) { OSI_TRACE_ERROR("%s invalid !!\n", __func__); return; } for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) { if (mem_dbg_info[i].p == NULL) { mem_dbg_info[i].p = p; mem_dbg_info[i].size = size; mem_dbg_info[i].func = func; mem_dbg_info[i].line = line; mem_dbg_count++; break; } } if (i >= OSI_MEM_DBG_INFO_MAX) { OSI_TRACE_ERROR("%s full %s %d !!\n", __func__, func, line); } mem_dbg_current_size += size; if(mem_dbg_max_size < mem_dbg_current_size) { mem_dbg_max_size = mem_dbg_current_size; } for (i = 0; i < OSI_MEM_DBG_MAX_SECTION_NUM; i++){ if (mem_dbg_max_size_section[i].used) { if(mem_dbg_max_size_section[i].max_size < mem_dbg_current_size) { mem_dbg_max_size_section[i].max_size = mem_dbg_current_size; } } } } void osi_mem_dbg_clean(void *p, const char *func, int line) { int i; if (!p) { OSI_TRACE_ERROR("%s invalid\n", __func__); return; } for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) { if (mem_dbg_info[i].p == p) { mem_dbg_current_size -= mem_dbg_info[i].size; mem_dbg_info[i].p = NULL; mem_dbg_info[i].size = 0; mem_dbg_info[i].func = NULL; mem_dbg_info[i].line = 0; mem_dbg_count--; break; } } if (i >= OSI_MEM_DBG_INFO_MAX) { OSI_TRACE_ERROR("%s full %s %d !!\n", __func__, func, line); } } void osi_mem_dbg_show(void) { int i; for (i = 0; i < OSI_MEM_DBG_INFO_MAX; i++) { if (mem_dbg_info[i].p || mem_dbg_info[i].size != 0 ) { OSI_TRACE_ERROR("--> p %p, s %d, f %s, l %d\n", mem_dbg_info[i].p, mem_dbg_info[i].size, mem_dbg_info[i].func, mem_dbg_info[i].line); } } OSI_TRACE_ERROR("--> count %d\n", mem_dbg_count); OSI_TRACE_ERROR("--> size %dB\n--> max size %dB\n", mem_dbg_current_size, mem_dbg_max_size); } uint32_t osi_mem_dbg_get_max_size(void) { return mem_dbg_max_size; } uint32_t osi_mem_dbg_get_current_size(void) { return mem_dbg_current_size; } void osi_men_dbg_set_section_start(uint8_t index) { if (index >= OSI_MEM_DBG_MAX_SECTION_NUM) { OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n", OSI_MEM_DBG_MAX_SECTION_NUM - 1, index); return; } if (mem_dbg_max_size_section[index].used) { OSI_TRACE_WARNING("This index(%d) has been started, restart it.\n", index); } mem_dbg_max_size_section[index].used = true; mem_dbg_max_size_section[index].max_size = mem_dbg_current_size; } void osi_men_dbg_set_section_end(uint8_t index) { if (index >= OSI_MEM_DBG_MAX_SECTION_NUM) { OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n", OSI_MEM_DBG_MAX_SECTION_NUM - 1, index); return; } if (!mem_dbg_max_size_section[index].used) { OSI_TRACE_ERROR("This index(%d) has not been started.\n", index); return; } mem_dbg_max_size_section[index].used = false; } uint32_t osi_mem_dbg_get_max_size_section(uint8_t index) { if (index >= OSI_MEM_DBG_MAX_SECTION_NUM){ OSI_TRACE_ERROR("Then range of index should be between 0 and %d, current index is %d.\n", OSI_MEM_DBG_MAX_SECTION_NUM - 1, index); return 0; } return mem_dbg_max_size_section[index].max_size; } #endif #if HEAP_MEMORY_STATS static size_t s_mem_used_size = 0; static SemaphoreHandle_t s_mem_mutex = NULL; int osi_mem_init(void) { s_mem_mutex = xSemaphoreCreateMutex(); if (s_mem_mutex == NULL) { return -1; } return 0; } void osi_mem_deinit(void) { if (s_mem_mutex) { vSemaphoreDelete(s_mem_mutex); s_mem_mutex = NULL; } } #endif char *osi_strdup(const char *str) { size_t size = strlen(str) + 1; // + 1 for the null terminator char *new_string = (char *)osi_calloc(size); if (!new_string) { return NULL; } memcpy(new_string, str, size); return new_string; } void *osi_malloc_func(size_t size) { void *p = osi_malloc_base(size); if (size != 0 && p == NULL) { OSI_TRACE_ERROR("malloc failed (caller=%p size=%u)", __builtin_return_address(0), size); OSI_TRACE_ERROR("heap info: free=%d, largest_block=%d", heap_caps_get_free_size(MALLOC_CAP_DEFAULT), heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)); #if HEAP_ALLOCATION_FAILS_ABORT assert(0); #endif } #if HEAP_MEMORY_STATS if (s_mem_mutex != NULL && p != NULL) { size_t alloc_size = heap_caps_get_allocated_size(p); xSemaphoreTake(s_mem_mutex, portMAX_DELAY); s_mem_used_size += alloc_size; xSemaphoreGive(s_mem_mutex); } #endif return p; } void *osi_calloc_func(size_t size) { void *p = osi_calloc_base(size); if (size != 0 && p == NULL) { OSI_TRACE_ERROR("calloc failed (caller=%p size=%u)", __builtin_return_address(0), size); OSI_TRACE_ERROR("heap info: free=%d, largest_block=%d", heap_caps_get_free_size(MALLOC_CAP_DEFAULT), heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT)); #if HEAP_ALLOCATION_FAILS_ABORT assert(0); #endif } #if HEAP_MEMORY_STATS if (s_mem_mutex != NULL && p != NULL) { size_t alloc_size = heap_caps_get_allocated_size(p); xSemaphoreTake(s_mem_mutex, portMAX_DELAY); s_mem_used_size += alloc_size; xSemaphoreGive(s_mem_mutex); } #endif return p; } void osi_free_func(void *ptr) { #if HEAP_MEMORY_DEBUG osi_mem_dbg_clean(ptr, __func__, __LINE__); #endif #if HEAP_MEMORY_STATS if (s_mem_mutex != NULL && ptr != NULL) { size_t free_size = heap_caps_get_allocated_size(ptr); xSemaphoreTake(s_mem_mutex, portMAX_DELAY); if (s_mem_used_size >= free_size) { s_mem_used_size -= free_size; } else { OSI_TRACE_ERROR("The size of malloc and free not match: alloc_size=%u free_size=%u", s_mem_used_size, free_size); } xSemaphoreGive(s_mem_mutex); } #endif free(ptr); } #if HEAP_MEMORY_STATS // Get the size of memory allocated by Bluedroid but not yet freed uint32_t esp_host_used_heap_size_get(void) { return s_mem_used_size; } #endif