summaryrefslogtreecommitdiffstats
path: root/lib/mem_tracker.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mem_tracker.c')
-rw-r--r--lib/mem_tracker.c583
1 files changed, 583 insertions, 0 deletions
diff --git a/lib/mem_tracker.c b/lib/mem_tracker.c
new file mode 100644
index 00000000..a7ee430f
--- /dev/null
+++ b/lib/mem_tracker.c
@@ -0,0 +1,583 @@
+/* Memory Allocation Tracker
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "vty.h"
+
+/*==============================================================================
+ * Memory Tracker
+ */
+typedef struct mem_descriptor* mem_descriptor ;
+struct mem_descriptor
+{
+ void* addr ;
+ const char* name ;
+
+ uint32_t next ; /* MS Type is encoded as MS 4 bits */
+ uint32_t size ; /* LS Type is encoded as MS 4 bits */
+} ;
+
+typedef uint32_t md_index ;
+
+enum
+{
+ md_next_bits = 28, /* up to 256M allocated objects */
+ md_next_mask = (1 << md_next_bits) - 1,
+
+ md_index_max = md_next_mask + 1,
+
+ md_size_bits = 28, /* up to 256M individual item */
+ md_size_mask = (1 << md_size_bits) - 1,
+
+ md_size_max = md_size_mask,
+
+ md_next_type_bits = 32 - md_next_bits,
+ md_next_type_mask = (1 << md_next_type_bits) - 1,
+ md_size_type_bits = 32 - md_size_bits,
+ md_size_type_mask = (1 << md_size_type_bits) - 1,
+
+ md_i_index_bits = 16,
+ md_i_index_count = 1 << md_i_index_bits,
+ md_i_index_mask = md_i_index_count - 1,
+
+ md_page_bits = md_next_bits - md_i_index_bits,
+ md_page_count = 1 << md_page_bits,
+ md_page_mask = md_page_count - 1,
+} ;
+
+CONFIRM(MTYPE_MAX < (1 << (md_next_type_bits + md_size_type_bits))) ;
+
+static struct mem_type_tracker
+{
+ struct mem_tracker mt[MTYPE_MAX] ;
+} mem_type_tracker ;
+
+static mem_descriptor mem_page_table[md_page_count] ;
+
+static mem_descriptor mem_free_descriptors ;
+static md_index mem_next_index ;
+
+static struct mem_tracker mem ;
+
+uint32_t mem_base_count ;
+
+md_index* mem_bases ;
+
+inline static void
+mem_md_set_type(mem_descriptor md, enum MTYPE mtype)
+{
+ uint32_t t_ms ;
+ uint32_t t_ls ;
+
+ t_ms = mtype >> md_size_type_bits ;
+ t_ls = mtype ;
+
+ t_ms = (t_ms & md_next_type_mask) << md_next_bits ;
+ t_ls = (t_ls & md_size_type_mask) << md_size_bits ;
+
+ md->next = (md->next & md_next_mask) | t_ms ;
+ md->size = (md->size & md_size_mask) | t_ls ;
+} ;
+
+inline static void
+mem_md_set_next(mem_descriptor md, md_index next)
+{
+ md->next = (md->next & ~md_next_mask) | (next & md_next_mask) ;
+} ;
+
+inline static void
+mem_md_set_size(mem_descriptor md, size_t size)
+{
+ md->size = (md->size & ~md_size_mask) | (size & md_size_mask) ;
+} ;
+
+inline static uint8_t
+mem_md_type(mem_descriptor md)
+{
+ return ( (md->next >> (md_next_bits - md_size_type_bits))
+ & (md_next_type_mask << md_size_type_bits) )
+ | ( (md->size >> md_size_bits) & md_size_type_mask ) ;
+} ;
+
+inline static md_index
+mem_md_next(mem_descriptor md)
+{
+ return md->next & md_next_mask ;
+} ;
+
+inline static size_t
+mem_md_size(mem_descriptor md)
+{
+ return md->size & md_size_mask ;
+} ;
+
+inline static mem_descriptor
+mem_md_ptr(md_index mdi)
+{
+ mem_descriptor page ;
+
+ if (mdi == 0)
+ return NULL ;
+
+ page = mem_page_table[(mdi >> md_i_index_bits) & md_page_mask] ;
+ passert(page != NULL) ;
+ return page + (mdi & md_i_index_mask) ;
+} ;
+
+static void mem_md_make_bases(void) ;
+
+inline static md_index*
+mem_md_base(void* address)
+{
+ if (mem_bases == NULL)
+ mem_md_make_bases() ;
+
+ return mem_bases + ((uintptr_t)address % mem_base_count) ;
+} ;
+
+static void
+mem_md_make_bases(void)
+{
+ md_index* bases_was = mem_bases ;
+ uint32_t count_was = mem_base_count ;
+
+ mem_base_count += 256 * 1024 ;
+ mem_base_count |= 1 ;
+ mem_bases = calloc(mem_base_count, sizeof(md_index)) ;
+
+ if (bases_was == NULL)
+ passert(count_was == 0) ;
+ else
+ {
+ md_index* base = bases_was ;
+ md_index* new_base ;
+ md_index this ;
+ md_index next ;
+ mem_descriptor md ;
+
+ while (count_was)
+ {
+ next = *base++ ;
+ while (next != 0)
+ {
+ this = next ;
+ md = mem_md_ptr(this) ;
+ next = mem_md_next(md) ;
+
+ new_base = mem_md_base(md->addr) ;
+ mem_md_set_next(md, *new_base) ;
+ *new_base = this ;
+ } ;
+ --count_was ;
+ } ;
+
+ free(bases_was) ;
+ } ;
+} ;
+
+static void
+mem_md_make_descriptors(void)
+{
+ mem_descriptor md ;
+ md_index mdi ;
+
+ mdi = mem_next_index ;
+ passert(mdi < md_index_max) ;
+
+ mem_free_descriptors
+ = mem_page_table[(mdi >> md_i_index_bits) & md_page_mask]
+ = calloc(md_i_index_count, sizeof(struct mem_descriptor)) ;
+
+ mem_next_index += md_i_index_count ;
+
+ if (mdi == 0)
+ {
+ ++mem_free_descriptors ; /* don't use index == 0 */
+ ++mdi ;
+ } ;
+
+ md = mem_free_descriptors ;
+ while (mdi < mem_next_index)
+ {
+ md->addr = md + 1 ; /* point at next entry */
+ md->next = mdi ; /* set to point at self */
+ ++md ;
+ ++mdi ;
+ } ;
+ (md-1)->addr = NULL ; /* set end of list */
+} ;
+
+inline static void
+mem_md_malloc(enum MTYPE mtype, void* address, size_t size, const char* name)
+{
+ mem_tracker mtt ;
+ md_index* base ;
+ mem_descriptor md ;
+ md_index mdi ;
+
+ passert(size <= md_size_max) ;
+
+ if (mem_free_descriptors == NULL)
+ mem_md_make_descriptors() ;
+
+ md = mem_free_descriptors ;
+ mem_free_descriptors = md->addr ;
+ mdi = md->next ;
+
+ if (mem.tracked_count >= (mem_base_count * 4))
+ mem_md_make_bases() ;
+
+ base = mem_md_base(address) ;
+
+ md->addr = address ;
+ md->name = name ;
+ md->size = size ;
+ md->next = *base ;
+ mem_md_set_type(md, mtype) ;
+
+ *base = mdi ;
+
+ ++mem.malloc_count ;
+ ++mem.tracked_count ;
+
+ mem.tracked_size += size ;
+
+ if (mem.tracked_max_count < mem.tracked_count)
+ mem.tracked_max_count = mem.tracked_count ;
+
+ if (mem.tracked_max_size < mem.tracked_size)
+ mem.tracked_max_size = mem.tracked_size ;
+
+ mtt = &(mem_type_tracker.mt[mtype]) ;
+
+ ++(mtt->malloc_count) ;
+ ++(mtt->tracked_count) ;
+ mtt->tracked_size += size ;
+
+ if (mtt->tracked_max_count < mtt->tracked_count)
+ mtt->tracked_max_count = mtt->tracked_count ;
+
+ if (mtt->tracked_max_size < mtt->tracked_size)
+ mtt->tracked_max_size = mtt->tracked_size ;
+} ;
+
+inline static void
+mem_md_free(enum MTYPE mtype, void* address)
+{
+ mem_tracker mtt ;
+ md_index* base ;
+ mem_descriptor md, prev_md ;
+ md_index this, next ;
+
+ base = mem_md_base(address) ;
+
+ prev_md = NULL ;
+ this = *base ;
+ while (this != 0)
+ {
+ md = mem_md_ptr(this) ;
+ next = mem_md_next(md) ;
+
+ if (md->addr == address)
+ {
+ if (mem_md_type(md) != mtype)
+ zabort("memory type mismatch in free") ;
+
+ ++mem.free_count ;
+ --mem.tracked_count ;
+
+ mem.tracked_size -= mem_md_size(md) ;
+
+ mtt = &(mem_type_tracker.mt[mtype]) ;
+
+ ++(mtt->free_count) ;
+ --(mtt->tracked_count) ;
+ mtt->tracked_size -= mem_md_size(md) ;
+
+ if (prev_md == NULL)
+ *base = next ;
+ else
+ mem_md_set_next(prev_md, next) ;
+
+ md->addr = mem_free_descriptors ;
+ mem_free_descriptors = md ;
+ md->next = this ;
+
+ return ;
+ }
+ else
+ {
+ prev_md = md ;
+ this = next ;
+ } ;
+ } ;
+
+ zabort("Failed to find memory being freed") ;
+} ;
+
+inline static void
+mem_md_realloc(enum MTYPE mtype, void* old_address, void* new_address,
+ size_t size, const char* name)
+{
+ mem_tracker mtt ;
+ md_index* base ;
+ mem_descriptor md, prev_md ;
+ md_index this, next ;
+
+ if (old_address == NULL)
+ {
+ mem_md_malloc(mtype, new_address, size, name) ;
+ return ;
+ } ;
+
+ passert(size <= md_size_max) ;
+
+ base = mem_md_base(old_address) ;
+
+ prev_md = NULL ;
+ this = *base ;
+ while (this != 0)
+ {
+ md = mem_md_ptr(this) ;
+ next = mem_md_next(md) ;
+
+ if (md->addr == old_address)
+ {
+ if (mem_md_type(md) != mtype)
+ zabort("memory type mismatch in realloc") ;
+
+ ++mem.realloc_count ;
+
+ mem.tracked_size += size - mem_md_size(md) ;
+
+ if (mem.tracked_max_size < mem.tracked_size)
+ mem.tracked_max_size = mem.tracked_size ;
+
+ mtt = &(mem_type_tracker.mt[mtype]) ;
+
+ ++(mtt->realloc_count) ;
+ mtt->tracked_size += size - mem_md_size(md) ;
+
+ if (mtt->tracked_max_size < mtt->tracked_size)
+ mtt->tracked_max_size = mtt->tracked_size ;
+
+ md->name = name ;
+ mem_md_set_size(md, size) ;
+
+ if (old_address == new_address)
+ return ;
+
+ if (prev_md == NULL)
+ *base = next ;
+ else
+ mem_md_set_next(prev_md, next) ;
+
+ base = mem_md_base(new_address) ;
+ mem_md_set_next(md, *base) ;
+ *base = this ;
+
+ md->addr = new_address ;
+
+ return ;
+ }
+ else
+ {
+ prev_md = md ;
+ this = next ;
+ } ;
+ } ;
+
+ zabort("Failed to find memory being realloced") ;
+} ;
+
+/*==============================================================================
+ * Memory Tracker Display
+ */
+
+static const char* scale_d_tags [] =
+{
+ [0] = " " ,
+ [1] = "k",
+ [2] = "m",
+ [3] = "g",
+} ;
+
+static const char* scale_b_tags [] =
+{
+ [0] = " " ,
+ [1] = "KiB",
+ [2] = "MiB",
+ [3] = "GiB",
+} ;
+
+static char*
+mem_show_commas(char* buff, size_t size, uint64_t val, const char* tag)
+{
+ char* p ;
+ const char* q ;
+ int n ;
+
+ passert(size > 10) ;
+
+ p = buff + size ;
+ *(--p) = '\0' ;
+
+ q = tag + strlen(tag) ;
+ while ((p > buff) && (q > tag))
+ *(--p) = *(--q) ;
+
+ n = 3 ;
+ while (p > buff)
+ {
+ *(--p) = '0' + (val % 10) ;
+ val /= 10 ;
+ if (val == 0)
+ break ;
+
+ if ((--n == 0) && (p > buff))
+ {
+ *(--p) = ',' ;
+ n = 3 ;
+ } ;
+ } ;
+
+ return p ;
+} ;
+
+static char*
+mem_show_count(char* buff, size_t size, uint64_t val, int scale)
+{
+ int i, r ;
+
+ i = 0 ;
+ if (scale)
+ {
+ r = 0 ;
+ while ((i < 3) && (val >= 10000))
+ {
+ r = (val % 1000) ;
+ val /= 1000 ;
+ ++i ;
+ } ;
+ if (r >= 500) {
+ val += 1 ;
+ if ((val == 10000) && (i < 3))
+ {
+ val /= 1000 ;
+ ++i ;
+ } ;
+ } ;
+ } ;
+
+ return mem_show_commas(buff, size, val, scale_d_tags[i]) ;
+} ;
+
+static char*
+mem_show_byte_count(char* buff, size_t size, uint64_t val, int scale)
+{
+ int i, r ;
+
+ i = 0 ;
+ if (scale)
+ {
+ r = 0 ;
+ while ((i < 3) && (val >= 10000))
+ {
+ r = (val % 1024) ;
+ val /= 1024 ;
+ ++i ;
+ } ;
+ if (r >= 512) {
+ val += 1 ;
+ if ((val == 10000) && (i < 3))
+ {
+ val /= 1024 ;
+ ++i ;
+ } ;
+ } ;
+ } ;
+
+ return mem_show_commas(buff, size, val, scale_b_tags[i]) ;
+} ;
+
+static int
+show_memory_tracker_summary(struct vty *vty)
+{
+ struct mem_tracker mt ;
+ enum { sbs = 100 } ;
+ char buf[sbs];
+ size_t overhead ;
+
+ LOCK ;
+ overhead = (sizeof(struct mem_descriptor) * mem_next_index)
+ + (sizeof(md_index) * mem_base_count)
+ + (sizeof(mem_descriptor) * md_page_count) ;
+
+ mt = mem ; /* copy the overall memory information */
+ UNLOCK ;
+
+ vty_out (vty, "Memory Tracker Statistics:%s", VTY_NEWLINE);
+ vty_out (vty, " Current memory allocated: %10s%s",
+ mem_show_byte_count(buf, sbs, mt.tracked_size, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Current allocated objects: %8s%s",
+ mem_show_count (buf, sbs, mt.tracked_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Maximum memory allocated: %10s%s",
+ mem_show_byte_count(buf, sbs, mt.tracked_max_size, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Maximum allocated objects: %8s%s",
+ mem_show_count (buf, sbs, mt.tracked_max_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " malloc/calloc call count: %8s%s",
+ mem_show_count (buf, sbs, mt.malloc_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " realloc_call_count: %8s%s",
+ mem_show_count (buf, sbs, mt.realloc_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " free call count: %8s%s",
+ mem_show_count (buf, sbs, mt.free_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Memory Tracker overhead: %10s%s",
+ mem_show_byte_count(buf, sbs, overhead, 1),
+ VTY_NEWLINE);
+ return 1;
+} ;
+
+static int
+show_memory_tracker_detail(struct vty *vty, struct mem_tracker* mt,
+ unsigned long alloc)
+{
+ enum { sbs = 100 } ;
+ char buf[sbs];
+
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->tracked_count, 1)) ;
+ vty_out(vty, "%10s", mem_show_byte_count(buf, sbs, mt->tracked_size, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->tracked_max_count, 1)) ;
+ vty_out(vty, "%10s", mem_show_byte_count(buf, sbs, mt->tracked_max_size, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->malloc_count, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->realloc_count, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->free_count, 1)) ;
+
+ if (alloc != mt->tracked_count)
+ vty_out(vty, " %8s!!", mem_show_count(buf, sbs, alloc, 1)) ;
+
+ return 1;
+} ;