xref: /illumos-gate/usr/src/lib/libdwarf/common/malloc_check.c (revision 78801af7286cd73dbc996d470f789e75993cf15d)
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