You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

564 lines
16 KiB
C

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/******************************************************************************
* file lib/management/memory.c
* author YuLiang
* version 1.0.0
* date 14-Sep-2021
* brief This file provides all the memory operation functions.
******************************************************************************
* Attention
*
* <h2><center>&copy; COPYRIGHT(c) 2021 LandPower</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of LandPower nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
/* Includes ------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* 标准C库头文件. */
#include <malloc.h>
#include <sys/resource.h>
/* 用户代码头文件. */
#include "cmd.h"
/* Private define ------------------------------------------------------------*/
#define MEM_LOCK pthread_mutex_lock(&mem_mutex)
#define MEM_UNLOCK pthread_mutex_unlock(&mem_mutex)
/* Private macro -------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static mem_node_t mem_list_management[] =
{
{MTYPE_CLI, "Cli"},
{MTYPE_BUF, "Buffer"},
{MTYPE_BUF_DATA, "Buffer data"},
{MTYPE_BUF_TMP, "Buffer temp"},
{MTYPE_VTY, "Vty"},
{MTYPE_VTY_TMP, "Vty temp"},
{MTYPE_VTY_OUT_BUF, "Vty output buffer"},
{MTYPE_VTY_HIST, "Vty history"},
{MTYPE_HASH, "Hash"},
{MTYPE_HASH_BACKET, "Hash Bucket"},
{MTYPE_HASH_INDEX, "Hash Index"},
{MTYPE_LOG, "Logging"},
{MTYPE_MTIMER, "Timer"},
{MTYPE_THREAD_MONITOR, "Thread monitor"},
{MTYPE_PREFIX, "Prefix"},
{MTYPE_THREAD, "Thread"},
{MTYPE_THREAD_STATS, "Thread stats"},
{MTYPE_THREAD_MASTER, "Thread master"},
{MTYPE_THREAD_FUNCNAME, "Thread function name"},
{MTYPE_FIFO, "fifo"},
{MTYPE_GPIO, "gpio"},
{ -1, NULL },
};
static mem_node_t mem_list_transceiver[] =
{
{MTYPE_RECV, "Receive"},
{MTYPE_SENT, "Sent"},
{MTYPE_USART, "Usart"},
{MTYPE_ETH, "Ethernet"},
{ -1, NULL },
};
static mem_node_t mem_list_process[] =
{
{MTYPE_GIS, "GIS"},
{MTYPE_DAU, "DAU"},
{MTYPE_CSG, "CSG"},
{MTYPE_STORAGE, "STORAGE"},
{MTYPE_DEBUG, "DEBUG"},
{ -1, NULL },
};
static mem_list_t mlists[] __attribute__ ((unused)) =
{
{mem_list_management, "MANAGEMENT"},
{mem_list_transceiver, "TRANSCEIVER"},
{mem_list_process, "PROCESS"},
{NULL, NULL},
};
/* 同于统计每个模块申请内存的次数. */
static struct
{
const char *name;
int32_t alloc;
uint32_t t_malloc;
uint32_t c_malloc;
uint32_t t_calloc;
uint32_t c_calloc;
uint32_t t_realloc;
uint32_t t_free;
uint32_t c_strdup;
} mstat[MTYPE_MAX];
static pthread_mutex_t mem_mutex;
/* Private function prototypes -----------------------------------------------*/
/* Internal functions --------------------------------------------------------*/
/* 查找模块相应的说明字符串: key -- 模块 */
static const char *_x_desc_lookup(uint32_t key)
{
const mem_list_t *list = NULL;
const mem_node_t *pnt = NULL;
for(list = mlists; list->nodes != NULL; list++)
{
for(pnt = list->nodes; pnt->index >= 0; pnt++)
{
if (pnt->index == key)
{
return pnt->format;
}
}
}
return "";
}
/* 打印每个模块的内存使用情况: pri -- log优先级 */
static void _x_memstats_print(int32_t pri)
{
mem_list_t *ml = NULL;
for (ml = mlists; ml->nodes; ml++)
{
mem_node_t *m = NULL;
log_out(LOG_MEMORY, pri, "Memory utilization in module %s:", ml->name);
for(m = ml->nodes; m->index >= 0; m++)
{
if (m->index && mstat[m->index].alloc)
{
log_out(LOG_MEMORY, pri, " %-30s: %10ld", m->format, (long)mstat[m->index].alloc);
}
}
}
}
/* 打印内存申请错误信息: fname -- 调用的函数type -- 模块, size -- 大小 */
static void __attribute__ ((noreturn)) _x_mem_error(const char *fname, int32_t type, size_t size)
{
log_err(LOG_MEMORY, "%s : can't allocate memory for '%s' size %d: %s!",
fname, _x_desc_lookup(type), (int)size, safe_strerror(errno));
/* 打印每个模块的内存使用请款 */
_x_memstats_print(LOG_LVL_WARN);
/* 打印堆栈信息 */
log_backtrace(LOG_LVL_WARN);
abort();
}
/* 增加内存alloc计数 */
static void _x_alloc_inc(int32_t type)
{
MEM_LOCK;
mstat[type].alloc++;
MEM_UNLOCK;
}
/* 减少内存alloc计数 */
static void _x_alloc_dec(int32_t type)
{
MEM_LOCK;
mstat[type].alloc--;
MEM_UNLOCK;
}
/* 打印内存申请释放的debug信息. */
static void _x_mtype_log(char *func, void *memory, const char *file, int32_t line, uint32_t type)
{
//log_debug(LOG_MEMORY, "%s: %s %p %s %d", func, _x_desc_lookup(type), memory, file, line);
log_out(LOG_MEMORY, LOG_LVL_DBG,"%s: %s %p %s %d", func, _x_desc_lookup(type), memory, file, line);
}
/* Stats querying from users */
/* Return a pointer to a human friendly string describing
* the byte count passed in. E.g:
* "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
* Up to 4 significant figures will be given.
* The pointer returned may be NULL (indicating an error)
* or point to the given buffer, or point to static storage. */
static const char *_x_mtype_memstr(char *buf, size_t len, uint32_t bytes)
{
int32_t t = 0;
int32_t g = 0;
int32_t m = 0;
int32_t k = 0;
/* easy cases */
if (!bytes)
return "0 bytes";
if (1 == bytes)
return "1 byte";
#if 0
if (sizeof(unsigned long) >= 8)
/* Hacked to make it not warn on ILP32 machines
* Shift will always be 40 at runtime. See below too */
t = bytes >> (sizeof (unsigned long) >= 8 ? 40 : 0);
else
t = 0;
#endif
g = bytes >> 30;
m = bytes >> 20;
k = bytes >> 10;
if (t > 10)
{
/* The shift will always be 39 at runtime.
* Just hacked to make it not warn on 'smaller' machines.
* Static compiler analysis should mean no extra code */
if (bytes & (1UL << (sizeof(unsigned long) >= 8 ? 39 : 0)))
t++;
snprintf (buf, len, "%4d TiB", t);
}
else if (g > 10)
{
if (bytes & (1 << 29))
g++;
snprintf (buf, len, "%d GiB", g);
}
else if (m > 10)
{
if (bytes & (1 << 19))
m++;
snprintf (buf, len, "%d MiB", m);
}
else if (k > 10)
{
if (bytes & (1 << 9))
k++;
snprintf (buf, len, "%d KiB", k);
}
else
snprintf (buf, len, "%d bytes", bytes);
return buf;
}
/* 打印内存使用情况根据系统调用. */
static int32_t _x_show_memory_mallinfo(vty_t *vty)
{
struct mallinfo minfo = mallinfo();
char buf[MTYPE_MEMSTR_LEN] = {0};
vty_out(vty, "System allocator statistics:%s", VTY_NEWLINE);
vty_out(vty, " Total heap allocated: %s%s",
_x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.arena), VTY_NEWLINE);
vty_out(vty, " Holding block headers: %s%s",
_x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.hblkhd), VTY_NEWLINE);
vty_out(vty, " Used small blocks: %s%s",
_x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.usmblks), VTY_NEWLINE);
vty_out(vty, " Used ordinary blocks: %s%s",
_x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.uordblks), VTY_NEWLINE);
vty_out(vty, " Free small blocks: %s%s",
_x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fsmblks), VTY_NEWLINE);
vty_out(vty, " Free ordinary blocks: %s%s",
_x_mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fordblks), VTY_NEWLINE);
vty_out(vty, " Ordinary blocks: %ld%s", (unsigned long)minfo.ordblks, VTY_NEWLINE);
vty_out(vty, " Small blocks: %ld%s", (unsigned long)minfo.smblks, VTY_NEWLINE);
vty_out(vty, " Holding blocks: %ld%s", (unsigned long)minfo.hblks, VTY_NEWLINE);
vty_out(vty, "(see system documentation for 'mallinfo' for meaning)%s", VTY_NEWLINE);
return 1;
}
static void _x_show_separator(vty_t *vty)
{
vty_out(vty, "-----------------------------%s", VTY_NEWLINE);
}
int32_t _x_show_memory_vty(vty_t *vty, mem_node_t *list)
{
mem_node_t *m = NULL;
int32_t needsep = 0;
for (m = list; m->index >= 0; m++)
if (0 == m->index)
{
if (needsep)
{
_x_show_separator(vty);
needsep = 0;
}
}
else
{
vty_out(vty, "%-30s: %10d%s", m->format, mstat[m->index].alloc, VTY_NEWLINE);
needsep = 1;
}
return needsep;
}
/* 申请内存,并增加相应的模块的申请数: type -- 模块size -- 大小 */
static void *_x_malloc (int32_t type, size_t size)
{
void *memory = NULL;
memory = malloc(size);
if (NULL == memory)
_x_mem_error("malloc", type, size);
memset(memory, 0, size);
_x_alloc_inc(type);
return memory;
}
/* 申请内存,并增加相应的模块的申请数: type -- 模块size -- 大小 */
static void *_x_malloc_q (int32_t type, size_t size)
{
void *memory = NULL;
memory = malloc(size);
if (NULL == memory)
_x_mem_error("malloc", type, size);
_x_alloc_inc(type);
return memory;
}
/* Allocate memory as in zmalloc, and also clear the memory. */
static void *_x_calloc(int32_t type, size_t size)
{
void *memory = NULL;
memory = calloc(1, size);
if (NULL == memory)
_x_mem_error("calloc", type, size);
memset(memory, 0, size);
_x_alloc_inc(type);
return memory;
}
/* Given a pointer returned by zmalloc or zcalloc, free it and
* return a pointer to a new size, basically acting like realloc().
* Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the
* same type.
* Effects: Returns a pointer to the new memory, or aborts. */
static void *_x_realloc(int32_t type, void *ptr, size_t size)
{
void *memory = NULL;
memory = realloc(ptr, size);
if (NULL == memory)
_x_mem_error("realloc", type, size);
if (NULL == ptr)
_x_alloc_inc(type);
return memory;
}
/* Free memory allocated by z*alloc or zstrdup.
* Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the
* same type.
* Effects: The memory is freed and may no longer be referenced. */
static void _x_free(int32_t type, void *ptr)
{
if (ptr != NULL)
{
_x_alloc_dec(type);
free(ptr);
}
}
/* Duplicate a string, counting memory usage by type.
* Effects: The string is duplicated, and the return value must
* eventually be passed to zfree with the same type. The function will
* succeed or abort. */
static char *_x_strdup(int32_t type, const char *str)
{
void *dup = NULL;
dup = strdup(str);
if (dup == NULL)
_x_mem_error("strdup", type, strlen(str));
_x_alloc_inc(type);
return dup;
}
/* Interface functions -------------------------------------------------------*/
/* description: 申请内存.
param: file -- 调用的文件
line -- 调用的行数
type -- 哪个模块调用
size -- 分配的大小
return: 分配的内存地址 */
void *mtype_x_malloc(const char *file, int32_t line, int32_t type, size_t size)
{
void *memory = NULL;
mstat[type].c_malloc++;
mstat[type].t_malloc++;
memory = _x_malloc(type, size);
_x_mtype_log("x_malloc", memory, file, line, type);
return memory;
}
void *mtype_x_malloc_q(const char *file, int32_t line, int32_t type, size_t size)
{
void *memory = NULL;
mstat[type].c_malloc++;
mstat[type].t_malloc++;
memory = _x_malloc_q(type, size);
_x_mtype_log("x_malloc_q", memory, file, line, type);
return memory;
}
/* 见mtype_x_malloc. */
void *mtype_x_calloc(const char *file, int32_t line, int32_t type, size_t size)
{
void *memory = NULL;
mstat[type].c_calloc++;
mstat[type].t_calloc++;
memory = _x_calloc(type, size);
_x_mtype_log("x_calloc", memory, file, line, type);
return memory;
}
/* 见mtype_x_malloc. */
void *mtype_x_realloc(const char *file, int32_t line, int32_t type, void *ptr, size_t size)
{
void *memory = NULL;
/* Realloc need before allocated pointer. */
mstat[type].t_realloc++;
memory = _x_realloc(type, ptr, size);
_x_mtype_log("x_realloc", memory, file, line, type);
return memory;
}
/* 见mtype_x_malloc. */
void mtype_x_free(const char *file, int32_t line, int32_t type, void *ptr)
{
mstat[type].t_free++;
_x_free(type, ptr);
_x_mtype_log("x_free", ptr, file, line, type);
}
/* 见mtype_x_malloc. */
char *mtype_x_strdup(const char *file, int32_t line, int32_t type, const char *str)
{
char *memory = NULL;
mstat[type].c_strdup++;
memory = _x_strdup(type, str);
_x_mtype_log("x_strdup", memory, file, line, type);
return memory;
}
CMD(show_memory_all,
show_memory_all_cmd,
"show memory",
SHOW_STR
"Memory statistics\n")
{
mem_list_t *ml = NULL;
int needsep = 0;
needsep = _x_show_memory_mallinfo(vty);
for (ml = mlists; ml->nodes; ml++)
{
if (needsep)
_x_show_separator(vty);
needsep = _x_show_memory_vty(vty, ml->nodes);
}
return CMD_SUCCESS;
}
void mem_show(vty_t *vty)
{
mem_list_t *ml = NULL;
int needsep = 0;
for (ml = mlists; ml->nodes; ml++)
{
if (needsep)
_x_show_separator(vty);
needsep = _x_show_memory_vty(vty, ml->nodes);
}
}
/* description: memory模块初始化.
param:
return: */
void mtype_init(void)
{
cmd_install_element(COMMON_NODE, &show_memory_all_cmd);
}
/* description: memory模块初始化.
param:
return: */
void mtype_init_befor(void)
{
/* 初始化线程锁. */
pthread_mutex_init(&mem_mutex, NULL);
}
/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/