From 6fe044de03f53284832f5430f843cb3c4aa861be Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Mon, 3 Sep 2007 13:40:17 +0000 Subject: [PATCH] debug tool for mem stats. git-svn-id: file:///svn/unbound/trunk@580 be551aaa-1e26-0410-a405-d3ace91eadb9 --- Makefile.in | 11 +- doc/Changelog | 1 + testcode/memstats.c | 268 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 testcode/memstats.c diff --git a/Makefile.in b/Makefile.in index 9e7fbb866..3cf9dc837 100644 --- a/Makefile.in +++ b/Makefile.in @@ -73,8 +73,11 @@ PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c $(COMMON_SRC) PKTVIEW_OBJ=$(addprefix $(BUILD),$(PKTVIEW_SRC:.c=.o)) $(COMPAT_OBJ) SIGNIT_SRC=testcode/signit.c $(COMMON_SRC) SIGNIT_OBJ=$(addprefix $(BUILD),$(SIGNIT_SRC:.c=.o)) $(COMPAT_OBJ) +MEMSTATS_SRC=testcode/memstats.c $(COMMON_SRC) +MEMSTATS_OBJ=$(addprefix $(BUILD),$(MEMSTATS_SRC:.c=.o)) $(COMPAT_OBJ) ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \ - $(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) $(SIGNIT_SRC) + $(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) $(SIGNIT_SRC) \ + $(MEMSTATS_SRC) ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.o) \ $(addprefix compat/,$(LIBOBJS))) $(COMPAT_OBJ) @@ -89,7 +92,7 @@ $(BUILD)%.o: $(srcdir)/%.c .PHONY: clean realclean doc lint all install uninstall -all: $(COMMON_OBJ) unbound unittest testbound lock-verify pktview signit +all: $(COMMON_OBJ) unbound unittest testbound lock-verify pktview signit memstats unbound: $(DAEMON_OBJ) $(INFO) Link $@ @@ -115,6 +118,10 @@ signit: $(SIGNIT_OBJ) $(INFO) Link $@ $Q$(LINK) -o $@ $^ $(LIBS) +memstats: $(MEMSTATS_OBJ) + $(INFO) Link $@ + $Q$(LINK) -o $@ $^ $(LIBS) + #testcode/ldns-testpkts.c: $(ldnsdir)/examples/ldns-testpkts.c \ # $(ldnsdir)/examples/ldns-testpkts.h # cp $(ldnsdir)/examples/ldns-testpkts.c testcode/ldns-testpkts.c diff --git a/doc/Changelog b/doc/Changelog index d608f848c..e6707f5b9 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -8,6 +8,7 @@ not show lame cache growth as a leakage growth. - config setting for lameness cache expressed in bytes, instead of number of entries. + - tool too summarize allocations per code line. 31 August 2007: Wouter - can read bind trusted-keys { ... }; files, in a compatibility mode. diff --git a/testcode/memstats.c b/testcode/memstats.c new file mode 100644 index 000000000..cf00a9a47 --- /dev/null +++ b/testcode/memstats.c @@ -0,0 +1,268 @@ +/* + * testcode/memstats.c - debug tool to show memory allocation statistics. + * + * Copyright (c) 2007, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 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. + * + * Neither the name of the NLNET LABS 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 REGENTS 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. + */ + +/** + * \file + * + * This program reads a log file and prints the memory allocation summed + * up. + */ +#include "config.h" +#include "util/log.h" +#include "util/rbtree.h" +#include + +/** + * The allocation statistics block + */ +struct codeline { + /** rbtree node */ + rbnode_t node; + /** the name of the file:linenumber */ + char* codeline; + /** the name of the function */ + char* func; + /** number of bytes allocated */ + uint64_t alloc; + /** number of bytes freed */ + uint64_t free; +}; + +/** + * Other allocation stats + */ +struct alloc_misc { + /** number of region allocs */ + uint64_t region_alloc; +}; + +/** print usage and exit */ +static void +usage() +{ + printf("usage: memstats \n"); + printf("statistics are printed on stdout.\n"); + exit(1); +} + +/** compare two codeline structs for rbtree */ +static int +codeline_cmp(const void* a, const void* b) +{ + return strcmp((const char*)a, (const char*)b); +} + +/** match logfile line to see if it needs accounting processing */ +static int +match(char* line) +{ + /* f.e.: + * [1187340064] unbound[24604:0] info: ul/rb.c:81 r_create malloc(12) + * 0123456789 123456789 123456789 123456789 + */ + if(strlen(line) < 36) /* up to 'info: ' */ + return 0; + if(strncmp(line+30, "info: ", 6) != 0) + return 0; + if(strncmp(line+36, "stat ", 5) == 0) + return 0; /* skip the hex dumps */ + if(strstr(line+36, "malloc(")) + return 1; + else if(strstr(line+36, "calloc(")) + return 1; + /* skip reallocs */ + return 0; +} + +/** read up the region stats */ +static void +read_region_stat(char* line, struct alloc_misc* misc) +{ + long num = 0; + if(sscanf(line+50, "%ld", &num) != 1) { + printf("%s\n%s\n", line, line+50); + fatal_exit("unhandled region"); + } + misc->region_alloc += num; +} + +/** find or alloc codeline in tree */ +static struct codeline* +get_codeline(rbtree_t* tree, char* key, char* func) +{ + struct codeline* cl = (struct codeline*)rbtree_search(tree, key); + if(!cl) { + cl = calloc(1, sizeof(*cl)); + if(!cl) return 0; + cl->codeline = strdup(key); + if(!cl->codeline) return 0; + cl->func = strdup(func); + if(!cl->func) return 0; + cl->alloc = 0; + cl->node.key = cl->codeline; + rbtree_insert(tree, &cl->node); + } + return cl; +} + +/** read up the malloc stats */ +static void +read_malloc_stat(char* line, rbtree_t* tree) +{ + char codeline[10240]; + char name[10240]; + int skip = 0; + long num = 0; + struct codeline* cl = 0; + if(sscanf(line+36, "%s %s %n", codeline, name, &skip) != 2) { + printf("%s\n%s\n", line, line+36); + fatal_exit("unhandled malloc"); + } + if(sscanf(line+36+skip+7, "%ld", &num) != 1) { + printf("%s\n%s\n", line, line+36+skip+7); + fatal_exit("unhandled malloc"); + } + cl = get_codeline(tree, codeline, name); + if(!cl) + fatal_exit("alloc failure"); + cl->alloc += num; +} + +/** read up the calloc stats */ +static void +read_calloc_stat(char* line, rbtree_t* tree) +{ + char codeline[10240]; + char name[10240]; + int skip = 0; + long num = 0, sz = 0; + struct codeline* cl = 0; + if(sscanf(line+36, "%s %s %n", codeline, name, &skip) != 2) { + printf("%s\n%s\n", line, line+36); + fatal_exit("unhandled calloc"); + } + if(sscanf(line+36+skip+7, "%ld, %ld", &num, &sz) != 2) { + printf("%s\n%s\n", line, line+36+skip+7); + fatal_exit("unhandled calloc"); + } + + cl = get_codeline(tree, codeline, name); + if(!cl) + fatal_exit("alloc failure"); + cl->alloc += num*sz; +} + +/** get size of file */ +static off_t +get_file_size(const char* fname) +{ + struct stat s; + if(stat(fname, &s) < 0) { + fatal_exit("could not stat %s: %s", fname, strerror(errno)); + } + return s.st_size; +} + +/** read the logfile */ +static void +readfile(rbtree_t* tree, const char* fname, struct alloc_misc* misc) +{ + off_t total = get_file_size(fname); + off_t done = 0; + int report = 0; + FILE* in = fopen(fname, "r"); + char buf[102400]; + if(!in) + fatal_exit("could not open %s: %s", fname, strerror(errno)); + printf("Reading %s of size %lld\n", fname, (uint64_t)total); + while(fgets(buf, 102400, in)) { + buf[102400-1] = 0; + done += strlen(buf); + /* progress count */ + if((int)(((double)done / (double)total)*100.) > report) { + report = (int)(((double)done / (double)total)*100.); + fprintf(stderr, " %d%%", report); + } + + if(!match(buf)) + continue; + if(strncmp(buf+36, "region ", 7) == 0) + read_region_stat(buf, misc); + else if(strstr(buf+36, "malloc(")) + read_malloc_stat(buf, tree); + else if(strstr(buf+36, "calloc(")) + read_calloc_stat(buf, tree); + else { + printf("%s\n", buf); + fatal_exit("unhandled input"); + } + } + fprintf(stderr, " done\n"); + fclose(in); +} + +/** print memory stats */ +static void +printstats(rbtree_t* tree, struct alloc_misc* misc) +{ + struct codeline* cl; + uint64_t total = 0; + printf("%12lld in region alloc\n", misc->region_alloc); + total += misc->region_alloc; + RBTREE_FOR(cl, struct codeline*, tree) { + printf("%12lld in %s %s\n", cl->alloc, cl->codeline, cl->func); + total += cl->alloc; + } + printf("------------\n"); + printf("%12lld total in %ld code lines\n", total, (long)tree->count); + printf("\n"); +} + +/** main program */ +int main(int argc, const char* argv[]) +{ + rbtree_t* tree = 0; + struct alloc_misc misc; + if(argc != 2) { + usage(); + } + tree = rbtree_create(codeline_cmp); + if(!tree) + fatal_exit("alloc failure"); + memset(&misc, 0, sizeof(misc)); + readfile(tree, argv[1], &misc); + printstats(tree, &misc); + return 0; +} -- 2.47.2