/******************************************************************************
* 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
*
*
© COPYRIGHT(c) 2021 LandPower
*
* 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
#include
/* 用户代码头文件. */
#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_process[] =
{
{ -1, NULL },
};
static mem_list_t mlists[] __attribute__ ((unused)) =
{
{mem_list_management, "MANAGEMENT"},
{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 ****************/