/****************************************************************************** * 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_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 ****************/