1 /* 2 3 Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved. 4 5 This program is free software; you can redistribute it and/or modify it 6 under the terms of version 2.1 of the GNU Lesser General Public License 7 as published by the Free Software Foundation. 8 9 This program is distributed in the hope that it would be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13 Further, this software is distributed without any warranty that it is 14 free of the rightful claim of any third person regarding infringement 15 or the like. Any license provided herein, whether implied or 16 otherwise, applies only to this software file. Patent licenses, if 17 any, provided herein do not apply to combinations of this program with 18 other software, or any other product whatsoever. 19 20 You should have received a copy of the GNU Lesser General Public 21 License along with this program; if not, write the Free Software 22 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301, 23 USA. 24 25 */ 26 27 /* malloc_check.c For checking dealloc completeness. 28 29 This code is as simple as possible and works ok for 30 reasonable size allocation counts. 31 32 It treats allocation as global, and so will not 33 work very well if an application opens more than one 34 Dwarf_Debug. 35 36 */ 37 38 #include <stdio.h> 39 #ifdef HAVE_STDLIB_H 40 #include <stdlib.h> 41 #endif /* HAVE_STDLIB_H */ 42 #ifdef HAVE_MALLOC_H 43 /* Useful include for some Windows compilers. */ 44 #include <malloc.h> 45 #endif /* HAVE_MALLOC_H */ 46 #include "config.h" 47 #include "dwarf_incl.h" 48 #include "malloc_check.h" 49 #ifdef WANT_LIBBDWARF_MALLOC_CHECK 50 51 /* To turn off printing every entry, just change the define 52 to set PRINT_MALLOC_DETAILS 0. 53 */ 54 #define PRINT_MALLOC_DETAILS 0 55 56 #define MC_TYPE_UNKNOWN 0 57 #define MC_TYPE_ALLOC 1 58 #define MC_TYPE_DEALLOC 2 59 60 struct mc_data_s { 61 struct mc_data_s *mc_prev; 62 unsigned long mc_address; /* Assumes this is large enough to hold 63 a pointer! */ 64 65 long mc_alloc_number; /* Assigned in order by when record 66 created. */ 67 unsigned char mc_alloc_code; /* Allocation code, libdwarf. */ 68 unsigned char mc_type; 69 unsigned char mc_dealloc_noted; /* Used on an ALLOC node. */ 70 unsigned char mc_dealloc_noted_count; /* Used on an ALLOC 71 node. */ 72 }; 73 74 /* 75 76 */ 77 #define HASH_TABLE_SIZE 10501 78 static struct mc_data_s *mc_data_hash[HASH_TABLE_SIZE]; 79 static long mc_data_list_size = 0; 80 81 static char *alloc_type_name[MAX_DW_DLA + 1] = { 82 "", 83 "DW_DLA_STRING", 84 "DW_DLA_LOC", 85 "DW_DLA_LOCDESC", 86 "DW_DLA_ELLIST", 87 "DW_DLA_BOUNDS", 88 "DW_DLA_BLOCK", 89 "DW_DLA_DEBUG", 90 "DW_DLA_DIE", 91 "DW_DLA_LINE", 92 "DW_DLA_ATTR", 93 "DW_DLA_TYPE", 94 "DW_DLA_SUBSCR", 95 "DW_DLA_GLOBAL", 96 "DW_DLA_ERROR", 97 "DW_DLA_LIST", 98 "DW_DLA_LINEBUF", 99 "DW_DLA_ARANGE", 100 "DW_DLA_ABBREV", 101 "DW_DLA_FRAME_OP", 102 "DW_DLA_CIE", 103 "DW_DLA_FDE", 104 "DW_DLA_LOC_BLOCK", 105 "DW_DLA_FRAME_BLOCK", 106 "DW_DLA_FUNC", 107 "DW_DLA_TYPENAME", 108 "DW_DLA_VAR", 109 "DW_DLA_WEAK", 110 "DW_DLA_ADDR", 111 "DW_DLA_ABBREV_LIST", 112 "DW_DLA_CHAIN", 113 "DW_DLA_CU_CONTEXT", 114 "DW_DLA_FRAME", 115 "DW_DLA_GLOBAL_CONTEXT", 116 "DW_DLA_FILE_ENTRY", 117 "DW_DLA_LINE_CONTEXT", 118 "DW_DLA_LOC_CHAIN", 119 "DW_DLA_HASH_TABLE", 120 "DW_DLA_FUNC_CONTEXT", 121 "DW_DLA_TYPENAME_CONTEXT", 122 "DW_DLA_VAR_CONTEXT", 123 "DW_DLA_WEAK_CONTEXT", 124 "DW_DLA_PUBTYPES_CONTEXT" 125 /* Don't forget to expand this list if the list of codes 126 expands. */ 127 }; 128 129 static unsigned 130 hash_address(unsigned long addr) 131 { 132 unsigned long a = addr >> 2; 133 134 return a % HASH_TABLE_SIZE; 135 } 136 137 #if PRINT_MALLOC_DETAILS 138 static void 139 print_alloc_dealloc_detail(unsigned long addr, 140 int code, char *whichisit) 141 { 142 fprintf(stderr, 143 "%s addr 0x%lx code %d (%s) entry %ld\n", 144 whichisit, addr, code, alloc_type_name[code], 145 mc_data_list_size); 146 } 147 #else 148 #define print_alloc_dealloc_detail(a,b,c) /* nothing */ 149 #endif 150 151 /* Create a zeroed struct or die. */ 152 static void * 153 newone(void) 154 { 155 struct mc_data_s *newd = malloc(sizeof(struct mc_data_s)); 156 157 if (newd == 0) { 158 fprintf(stderr, "out of memory , # %ld\n", mc_data_list_size); 159 exit(1); 160 } 161 memset(newd, 0, sizeof(struct mc_data_s)); 162 return newd; 163 } 164 165 /* Notify checker that get_alloc has allocated user data. */ 166 void 167 dwarf_malloc_check_alloc_data(void *addr_in, unsigned char code) 168 { 169 struct mc_data_s *newd = newone(); 170 unsigned long addr = (unsigned long) addr_in; 171 struct mc_data_s **base = &mc_data_hash[hash_address(addr)]; 172 173 print_alloc_dealloc_detail(addr, code, "alloc "); 174 newd->mc_address = addr; 175 newd->mc_alloc_code = code; 176 newd->mc_type = MC_TYPE_ALLOC; 177 newd->mc_alloc_number = mc_data_list_size; 178 newd->mc_prev = *base; 179 *base = newd; 180 newd->mc_alloc_number = mc_data_list_size; 181 mc_data_list_size += 1; 182 } 183 184 static void 185 print_entry(char *msg, struct mc_data_s *data) 186 { 187 fprintf(stderr, 188 "%s: 0x%08lx code %2d (%s) type %s dealloc noted %u ct %u\n", 189 msg, 190 (long) data->mc_address, 191 data->mc_alloc_code, 192 alloc_type_name[data->mc_alloc_code], 193 (data->mc_type == MC_TYPE_ALLOC) ? "alloc " : 194 (data->mc_type == MC_TYPE_DEALLOC) ? "dealloc" : "unknown", 195 (unsigned) data->mc_dealloc_noted, 196 (unsigned) data->mc_dealloc_noted_count); 197 } 198 199 /* newd is a 'dealloc'. 200 */ 201 static long 202 balanced_by_alloc_p(struct mc_data_s *newd, 203 long *addr_match_num, 204 struct mc_data_s **addr_match, 205 struct mc_data_s *base) 206 { 207 struct mc_data_s *cur = base; 208 209 for (; cur; cur = cur->mc_prev) { 210 if (cur->mc_address == newd->mc_address) { 211 if (cur->mc_type == MC_TYPE_ALLOC) { 212 if (cur->mc_alloc_code == newd->mc_alloc_code) { 213 *addr_match = cur; 214 *addr_match_num = cur->mc_alloc_number; 215 return cur->mc_alloc_number; 216 } else { 217 /* code mismatch */ 218 *addr_match = cur; 219 *addr_match_num = cur->mc_alloc_number; 220 return -1; 221 } 222 } else { 223 /* Unbalanced new/del */ 224 *addr_match = cur; 225 *addr_match_num = cur->mc_alloc_number; 226 return -1; 227 } 228 } 229 } 230 return -1; 231 } 232 233 /* A dealloc is to take place. Ensure it balances an alloc. 234 */ 235 void 236 dwarf_malloc_check_dealloc_data(void *addr_in, unsigned char code) 237 { 238 struct mc_data_s *newd = newone(); 239 long prev; 240 long addr_match_num = -1; 241 struct mc_data_s *addr_match = 0; 242 unsigned long addr = (unsigned long) addr_in; 243 struct mc_data_s **base = &mc_data_hash[hash_address(addr)]; 244 245 246 print_alloc_dealloc_detail(addr, code, "dealloc "); 247 newd->mc_address = (unsigned long) addr; 248 newd->mc_alloc_code = code; 249 newd->mc_type = MC_TYPE_DEALLOC; 250 newd->mc_prev = *base; 251 prev = 252 balanced_by_alloc_p(newd, &addr_match_num, &addr_match, *base); 253 if (prev < 0) { 254 fprintf(stderr, 255 "Unbalanced dealloc at index %ld\n", mc_data_list_size); 256 print_entry("new", newd); 257 fprintf(stderr, "addr-match_num? %ld\n", addr_match_num); 258 if (addr_match) { 259 print_entry("prev entry", addr_match); 260 if (addr_match->mc_dealloc_noted > 1) { 261 fprintf(stderr, "Above is Duplicate dealloc!\n"); 262 } 263 } 264 abort(); 265 exit(3); 266 } 267 addr_match->mc_dealloc_noted = 1; 268 addr_match->mc_dealloc_noted_count += 1; 269 if (addr_match->mc_dealloc_noted_count > 1) { 270 fprintf(stderr, "Double dealloc entry %ld\n", addr_match_num); 271 print_entry("new dealloc entry", newd); 272 print_entry("bad alloc entry", addr_match); 273 } 274 *base = newd; 275 mc_data_list_size += 1; 276 } 277 278 /* Final check for leaks. 279 */ 280 void 281 dwarf_malloc_check_complete(char *msg) 282 { 283 long i = 0; 284 long total = mc_data_list_size; 285 long hash_slots_used = 0; 286 long max_chain_length = 0; 287 288 fprintf(stderr, "Run complete, %s. %ld entries\n", msg, total); 289 for (; i < HASH_TABLE_SIZE; ++i) { 290 struct mc_data_s *cur = mc_data_hash[i]; 291 long cur_chain_length = 0; 292 293 if (cur == 0) 294 continue; 295 ++hash_slots_used; 296 for (; cur; cur = cur->mc_prev) { 297 ++cur_chain_length; 298 if (cur->mc_type == MC_TYPE_ALLOC) { 299 if (cur->mc_dealloc_noted) { 300 if (cur->mc_dealloc_noted > 1) { 301 fprintf(stderr, 302 " Duplicate dealloc! entry %ld\n", 303 cur->mc_alloc_number); 304 print_entry("duplicate dealloc", cur); 305 306 } 307 continue; 308 } else { 309 fprintf(stderr, "malloc no dealloc, entry %ld\n", 310 cur->mc_alloc_number); 311 print_entry("dangle", cur); 312 } 313 } else { 314 /* mc_type is MC_TYPE_DEALLOC, already checked */ 315 316 } 317 } 318 if (cur_chain_length > max_chain_length) { 319 max_chain_length = cur_chain_length; 320 } 321 } 322 fprintf(stderr, "mc hash table slots=%ld, " 323 "used=%ld, maxchain=%ld\n", 324 (long) HASH_TABLE_SIZE, hash_slots_used, max_chain_length); 325 return; 326 } 327 328 #else 329 330 extern void *libdwarf_an_unused_function_so_not_empty_c_file(void); 331 332 #endif /* WANT_LIBBDWARF_MALLOC_CHECK */ 333