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) { 110 free(cl); 111 return 0; 112 } 113 cl->func = strdup(func); 114 if(!cl->func) { 115 free(cl->codeline); 116 free(cl); 117 return 0; 118 } 119 cl->alloc = 0; 120 cl->node.key = cl->codeline; 121 (void)rbtree_insert(tree, &cl->node); 122 } 123 return cl; 124 } 125 126 /** read up the malloc stats */ 127 static void 128 read_malloc_stat(char* line, rbtree_type* tree) 129 { 130 char codeline[10240]; 131 char name[10240]; 132 int skip = 0; 133 long num = 0; 134 struct codeline* cl = 0; 135 line = strstr(line, "info: ")+6; 136 if(sscanf(line, "%s %s %n", codeline, name, &skip) != 2) { 137 printf("%s\n", line); 138 fatal_exit("unhandled malloc"); 139 } 140 if(sscanf(line+skip+7, "%ld", &num) != 1) { 141 printf("%s\n%s\n", line, line+skip+7); 142 fatal_exit("unhandled malloc"); 143 } 144 cl = get_codeline(tree, codeline, name); 145 if(!cl) 146 fatal_exit("alloc failure"); 147 cl->alloc += num; 148 cl->calls ++; 149 } 150 151 /** read up the calloc stats */ 152 static void 153 read_calloc_stat(char* line, rbtree_type* tree) 154 { 155 char codeline[10240]; 156 char name[10240]; 157 int skip = 0; 158 long num = 0, sz = 0; 159 struct codeline* cl = 0; 160 line = strstr(line, "info: ")+6; 161 if(sscanf(line, "%s %s %n", codeline, name, &skip) != 2) { 162 printf("%s\n", line); 163 fatal_exit("unhandled calloc"); 164 } 165 if(sscanf(line+skip+7, "%ld, %ld", &num, &sz) != 2) { 166 printf("%s\n%s\n", line, line+skip+7); 167 fatal_exit("unhandled calloc"); 168 } 169 170 cl = get_codeline(tree, codeline, name); 171 if(!cl) 172 fatal_exit("alloc failure"); 173 cl->alloc += num*sz; 174 cl->calls ++; 175 } 176 177 /** get size of file */ 178 static off_t 179 get_file_size(const char* fname) 180 { 181 struct stat s; 182 if(stat(fname, &s) < 0) { 183 fatal_exit("could not stat %s: %s", fname, strerror(errno)); 184 } 185 return s.st_size; 186 } 187 188 /** read the logfile */ 189 static void 190 readfile(rbtree_type* tree, const char* fname) 191 { 192 off_t total = get_file_size(fname); 193 off_t done = (off_t)0; 194 int report = 0; 195 FILE* in = fopen(fname, "r"); 196 char buf[102400]; 197 if(!in) 198 fatal_exit("could not open %s: %s", fname, strerror(errno)); 199 printf("Reading %s of size " ARG_LL "d\n", fname, (long long)total); 200 while(fgets(buf, 102400, in)) { 201 buf[102400-1] = 0; 202 done += (off_t)strlen(buf); 203 /* progress count */ 204 if((int)(((double)done / (double)total)*100.) > report) { 205 report = (int)(((double)done / (double)total)*100.); 206 fprintf(stderr, " %d%%", report); 207 } 208 209 if(!match(buf)) 210 continue; 211 else if(strstr(buf+30, "malloc(")) 212 read_malloc_stat(buf, tree); 213 else if(strstr(buf+30, "calloc(")) 214 read_calloc_stat(buf, tree); 215 else { 216 printf("%s\n", buf); 217 fatal_exit("unhandled input"); 218 } 219 } 220 fprintf(stderr, " done\n"); 221 fclose(in); 222 } 223 224 /** print memory stats */ 225 static void 226 printstats(rbtree_type* tree) 227 { 228 struct codeline* cl; 229 uint64_t total = 0, tcalls = 0; 230 RBTREE_FOR(cl, struct codeline*, tree) { 231 printf("%12lld / %8lld in %s %s\n", (long long)cl->alloc, 232 (long long)cl->calls, cl->codeline, cl->func); 233 total += cl->alloc; 234 tcalls += cl->calls; 235 } 236 printf("------------\n"); 237 printf("%12lld / %8lld total in %ld code lines\n", (long long)total, 238 (long long)tcalls, (long)tree->count); 239 printf("\n"); 240 } 241 242 /** main program */ 243 int main(int argc, const char* argv[]) 244 { 245 rbtree_type* tree = 0; 246 log_init(NULL, 0, 0); 247 if(argc != 2) { 248 usage(); 249 } 250 tree = rbtree_create(codeline_cmp); 251 if(!tree) 252 fatal_exit("alloc failure"); 253 readfile(tree, argv[1]); 254 printstats(tree); 255 return 0; 256 } 257