1 /* 2 * testcode/memstats.c - debug tool to show memory allocation statistics. 3 * 4 * Copyright (c) 2007, NLnet Labs. All rights reserved. 5 * 6 * This software is open source. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * Redistributions in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 19 * Neither the name of the NLNET LABS nor the names of its contributors may 20 * be used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 /** 37 * \file 38 * 39 * This program reads a log file and prints the memory allocation summed 40 * up. 41 */ 42 #include "config.h" 43 #include "util/log.h" 44 #include "util/rbtree.h" 45 #include "util/locks.h" 46 #include "util/fptr_wlist.h" 47 #include <sys/stat.h> 48 49 /** 50 * The allocation statistics block 51 */ 52 struct codeline { 53 /** rbtree node */ 54 rbnode_type node; 55 /** the name of the file:linenumber */ 56 char* codeline; 57 /** the name of the function */ 58 char* func; 59 /** number of bytes allocated */ 60 uint64_t alloc; 61 /** number of bytes freed */ 62 uint64_t free; 63 /** number allocations and frees */ 64 uint64_t calls; 65 }; 66 67 /** print usage and exit */ 68 static void 69 usage(void) 70 { 71 printf("usage: memstats <logfile>\n"); 72 printf("statistics are printed on stdout.\n"); 73 exit(1); 74 } 75 76 /** match logfile line to see if it needs accounting processing */ 77 static int 78 match(char* line) 79 { 80 /* f.e.: 81 * [1187340064] unbound[24604:0] info: ul/rb.c:81 r_create malloc(12) 82 * 0123456789 123456789 123456789 123456789 83 * But now also: 84 * Sep 16 15:18:20 unbound[1:0] info: ul/nh.c:143 memdup malloc(11) 85 */ 86 if(strlen(line) < 32) /* up to 'info: ' */ 87 return 0; 88 if(!strstr(line, " info: ")) 89 return 0; 90 if(strstr(line, "info: stat ")) 91 return 0; /* skip the hex dumps */ 92 if(strstr(line+30, "malloc(")) 93 return 1; 94 else if(strstr(line+30, "calloc(")) 95 return 1; 96 /* skip reallocs */ 97 return 0; 98 } 99 100 /** find or alloc codeline in tree */ 101 static struct codeline* 102 get_codeline(rbtree_type* tree, char* key, char* func) 103 { 104 struct codeline* cl = (struct codeline*)rbtree_search(tree, key); 105 if(!cl) { 106 cl = calloc(1, sizeof(*cl)); 107 if(!cl) return 0; 108 cl->codeline = strdup(key); 109 if(!cl->codeline) return 0; 110 cl->func = strdup(func); 111 if(!cl->func) return 0; 112 cl->alloc = 0; 113 cl->node.key = cl->codeline; 114 (void)rbtree_insert(tree, &cl->node); 115 } 116 return cl; 117 } 118 119 /** read up the malloc stats */ 120 static void 121 read_malloc_stat(char* line, rbtree_type* tree) 122 { 123 char codeline[10240]; 124 char name[10240]; 125 int skip = 0; 126 long num = 0; 127 struct codeline* cl = 0; 128 line = strstr(line, "info: ")+6; 129 if(sscanf(line, "%s %s %n", codeline, name, &skip) != 2) { 130 printf("%s\n", line); 131 fatal_exit("unhandled malloc"); 132 } 133 if(sscanf(line+skip+7, "%ld", &num) != 1) { 134 printf("%s\n%s\n", line, line+skip+7); 135 fatal_exit("unhandled malloc"); 136 } 137 cl = get_codeline(tree, codeline, name); 138 if(!cl) 139 fatal_exit("alloc failure"); 140 cl->alloc += num; 141 cl->calls ++; 142 } 143 144 /** read up the calloc stats */ 145 static void 146 read_calloc_stat(char* line, rbtree_type* tree) 147 { 148 char codeline[10240]; 149 char name[10240]; 150 int skip = 0; 151 long num = 0, sz = 0; 152 struct codeline* cl = 0; 153 line = strstr(line, "info: ")+6; 154 if(sscanf(line, "%s %s %n", codeline, name, &skip) != 2) { 155 printf("%s\n", line); 156 fatal_exit("unhandled calloc"); 157 } 158 if(sscanf(line+skip+7, "%ld, %ld", &num, &sz) != 2) { 159 printf("%s\n%s\n", line, line+skip+7); 160 fatal_exit("unhandled calloc"); 161 } 162 163 cl = get_codeline(tree, codeline, name); 164 if(!cl) 165 fatal_exit("alloc failure"); 166 cl->alloc += num*sz; 167 cl->calls ++; 168 } 169 170 /** get size of file */ 171 static off_t 172 get_file_size(const char* fname) 173 { 174 struct stat s; 175 if(stat(fname, &s) < 0) { 176 fatal_exit("could not stat %s: %s", fname, strerror(errno)); 177 } 178 return s.st_size; 179 } 180 181 /** read the logfile */ 182 static void 183 readfile(rbtree_type* tree, const char* fname) 184 { 185 off_t total = get_file_size(fname); 186 off_t done = (off_t)0; 187 int report = 0; 188 FILE* in = fopen(fname, "r"); 189 char buf[102400]; 190 if(!in) 191 fatal_exit("could not open %s: %s", fname, strerror(errno)); 192 printf("Reading %s of size " ARG_LL "d\n", fname, (long long)total); 193 while(fgets(buf, 102400, in)) { 194 buf[102400-1] = 0; 195 done += (off_t)strlen(buf); 196 /* progress count */ 197 if((int)(((double)done / (double)total)*100.) > report) { 198 report = (int)(((double)done / (double)total)*100.); 199 fprintf(stderr, " %d%%", report); 200 } 201 202 if(!match(buf)) 203 continue; 204 else if(strstr(buf+30, "malloc(")) 205 read_malloc_stat(buf, tree); 206 else if(strstr(buf+30, "calloc(")) 207 read_calloc_stat(buf, tree); 208 else { 209 printf("%s\n", buf); 210 fatal_exit("unhandled input"); 211 } 212 } 213 fprintf(stderr, " done\n"); 214 fclose(in); 215 } 216 217 /** print memory stats */ 218 static void 219 printstats(rbtree_type* tree) 220 { 221 struct codeline* cl; 222 uint64_t total = 0, tcalls = 0; 223 RBTREE_FOR(cl, struct codeline*, tree) { 224 printf("%12lld / %8lld in %s %s\n", (long long)cl->alloc, 225 (long long)cl->calls, cl->codeline, cl->func); 226 total += cl->alloc; 227 tcalls += cl->calls; 228 } 229 printf("------------\n"); 230 printf("%12lld / %8lld total in %ld code lines\n", (long long)total, 231 (long long)tcalls, (long)tree->count); 232 printf("\n"); 233 } 234 235 /** main program */ 236 int main(int argc, const char* argv[]) 237 { 238 rbtree_type* tree = 0; 239 log_init(NULL, 0, 0); 240 if(argc != 2) { 241 usage(); 242 } 243 tree = rbtree_create(codeline_cmp); 244 if(!tree) 245 fatal_exit("alloc failure"); 246 readfile(tree, argv[1]); 247 printstats(tree); 248 return 0; 249 } 250