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