diff options
Diffstat (limited to 'lib/mem_tracker.c')
-rw-r--r-- | lib/mem_tracker.c | 583 |
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; +} ; |