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
hash_address(unsigned long addr)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
print_alloc_dealloc_detail(unsigned long addr,int code,char * whichisit)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 *
newone(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
dwarf_malloc_check_alloc_data(void * addr_in,unsigned char code)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
print_entry(char * msg,struct mc_data_s * data)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
balanced_by_alloc_p(struct mc_data_s * newd,long * addr_match_num,struct mc_data_s ** addr_match,struct mc_data_s * base)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
dwarf_malloc_check_dealloc_data(void * addr_in,unsigned char code)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
dwarf_malloc_check_complete(char * msg)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