/src/lvm2/libdm/mm/dbg_malloc.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /*  | 
2  |  |  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.    | 
3  |  |  * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.  | 
4  |  |  *  | 
5  |  |  * This file is part of the device-mapper userspace tools.  | 
6  |  |  *  | 
7  |  |  * This copyrighted material is made available to anyone wishing to use,  | 
8  |  |  * modify, copy, or redistribute it subject to the terms and conditions  | 
9  |  |  * of the GNU Lesser General Public License v.2.1.  | 
10  |  |  *  | 
11  |  |  * You should have received a copy of the GNU Lesser General Public License  | 
12  |  |  * along with this program; if not, write to the Free Software Foundation,  | 
13  |  |  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA  | 
14  |  |  */  | 
15  |  |  | 
16  |  | #include "libdm/misc/dmlib.h"  | 
17  |  |  | 
18  |  | #ifdef VALGRIND_POOL  | 
19  |  | #include <memcheck.h>  | 
20  |  | #endif  | 
21  |  | #include <assert.h>  | 
22  |  | #include <stdarg.h>  | 
23  |  |  | 
24  |  | void *dm_malloc_aux(size_t s, const char *file, int line)  | 
25  |  |         __attribute__((__malloc__)) __attribute__((__warn_unused_result__));  | 
26  |  | void *dm_malloc_aux_debug(size_t s, const char *file, int line)  | 
27  |  |         __attribute__((__malloc__)) __attribute__((__warn_unused_result__));  | 
28  |  | static void *_dm_malloc_aligned_aux(size_t s, size_t a, const char *file, int line)  | 
29  |  |         __attribute__((__malloc__)) __attribute__((__warn_unused_result__));  | 
30  |  | void *dm_zalloc_aux(size_t s, const char *file, int line)  | 
31  |  |         __attribute__((__malloc__)) __attribute__((__warn_unused_result__));  | 
32  |  | void *dm_zalloc_aux_debug(size_t s, const char *file, int line)  | 
33  |  |         __attribute__((__malloc__)) __attribute__((__warn_unused_result__));  | 
34  |  | void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)  | 
35  |  |         __attribute__((__warn_unused_result__));  | 
36  |  | void dm_free_aux(void *p);  | 
37  |  | char *dm_strdup_aux(const char *str, const char *file, int line)  | 
38  |  |         __attribute__((__warn_unused_result__));  | 
39  |  | int dm_dump_memory_debug(void);  | 
40  |  | void dm_bounds_check_debug(void);  | 
41  |  |  | 
42  |  | char *dm_strdup_aux(const char *str, const char *file, int line)  | 
43  | 0  | { | 
44  | 0  |   char *ret;  | 
45  | 0  |   size_t len;  | 
46  |  | 
  | 
47  | 0  |   if (!str) { | 
48  | 0  |     log_error(INTERNAL_ERROR "dm_strdup called with NULL pointer");  | 
49  | 0  |     return NULL;  | 
50  | 0  |   }  | 
51  |  |  | 
52  | 0  |   len = strlen(str) + 1;  | 
53  | 0  |   if ((ret = dm_malloc_aux_debug(len, file, line)))  | 
54  | 0  |     memcpy(ret, str, len);  | 
55  |  | 
  | 
56  | 0  |   return ret;  | 
57  | 0  | }  | 
58  |  |  | 
59  |  | struct memblock { | 
60  |  |   struct memblock *prev, *next; /* All allocated blocks are linked */  | 
61  |  |   size_t length;    /* Size of the requested block */  | 
62  |  |   int id;     /* Index of the block */  | 
63  |  |   const char *file; /* File that allocated */  | 
64  |  |   int line;   /* Line that allocated */  | 
65  |  |   void *magic;    /* Address of this block */  | 
66  |  | } __attribute__((aligned(8)));  | 
67  |  |  | 
68  |  | static struct { | 
69  |  |   unsigned block_serialno;/* Non-decreasing serialno of block */  | 
70  |  |   unsigned blocks_allocated; /* Current number of blocks allocated */  | 
71  |  |   unsigned blocks_max;  /* Max no of concurrently-allocated blocks */  | 
72  |  |   unsigned int bytes, mbytes;  | 
73  |  |  | 
74  |  | } _mem_stats = { | 
75  |  | 0, 0, 0, 0, 0};  | 
76  |  |  | 
77  |  | static struct memblock *_head = 0;  | 
78  |  | static struct memblock *_tail = 0;  | 
79  |  |  | 
80  |  | void *dm_malloc_aux_debug(size_t s, const char *file, int line)  | 
81  | 0  | { | 
82  | 0  |   struct memblock *nb;  | 
83  | 0  |   size_t tsize = s + sizeof(*nb) + sizeof(unsigned long);  | 
84  |  | 
  | 
85  | 0  |   if (s > 50000000) { | 
86  | 0  |     log_error("Huge memory allocation (size %" PRIsize_t | 
87  | 0  |         ") rejected - metadata corruption?", s);  | 
88  | 0  |     return 0;  | 
89  | 0  |   }  | 
90  |  |  | 
91  | 0  |   if (!(nb = malloc(tsize))) { | 
92  | 0  |     log_error("couldn't allocate any memory, size = %" PRIsize_t, | 
93  | 0  |         s);  | 
94  | 0  |     return 0;  | 
95  | 0  |   }  | 
96  |  |  | 
97  |  |   /* set up the file and line info */  | 
98  | 0  |   nb->file = file;  | 
99  | 0  |   nb->line = line;  | 
100  |  | 
  | 
101  | 0  |   dm_bounds_check();  | 
102  |  |  | 
103  |  |   /* setup fields */  | 
104  | 0  |   nb->magic = nb + 1;  | 
105  | 0  |   nb->length = s;  | 
106  | 0  |   nb->id = ++_mem_stats.block_serialno;  | 
107  | 0  |   nb->next = 0;  | 
108  |  |  | 
109  |  |   /* stomp a pretty pattern across the new memory  | 
110  |  |      and fill in the boundary bytes */  | 
111  | 0  |   { | 
112  | 0  |     char *ptr = (char *) (nb + 1);  | 
113  | 0  |     size_t i;  | 
114  | 0  |     for (i = 0; i < s; i++)  | 
115  | 0  |       *ptr++ = i & 0x1 ? (char) 0xba : (char) 0xbe;  | 
116  |  | 
  | 
117  | 0  |     for (i = 0; i < sizeof(unsigned long); i++)  | 
118  | 0  |       *ptr++ = (char) nb->id;  | 
119  | 0  |   }  | 
120  |  | 
  | 
121  | 0  |   nb->prev = _tail;  | 
122  |  |  | 
123  |  |   /* link to tail of the list */  | 
124  | 0  |   if (!_head)  | 
125  | 0  |     _head = _tail = nb;  | 
126  | 0  |   else { | 
127  | 0  |     _tail->next = nb;  | 
128  | 0  |     _tail = nb;  | 
129  | 0  |   }  | 
130  |  | 
  | 
131  | 0  |   _mem_stats.blocks_allocated++;  | 
132  | 0  |   if (_mem_stats.blocks_allocated > _mem_stats.blocks_max)  | 
133  | 0  |     _mem_stats.blocks_max = _mem_stats.blocks_allocated;  | 
134  |  | 
  | 
135  | 0  |   _mem_stats.bytes += s;  | 
136  | 0  |   if (_mem_stats.bytes > _mem_stats.mbytes)  | 
137  | 0  |     _mem_stats.mbytes = _mem_stats.bytes;  | 
138  |  |  | 
139  |  |   /* log_debug_mem("Allocated: %u %u %u", nb->id, _mem_stats.blocks_allocated, | 
140  |  |       _mem_stats.bytes); */  | 
141  |  | #ifdef VALGRIND_POOL  | 
142  |  |   VALGRIND_MAKE_MEM_UNDEFINED(nb + 1, s);  | 
143  |  | #endif  | 
144  | 0  |   return nb + 1;  | 
145  | 0  | }  | 
146  |  |  | 
147  |  | void *dm_zalloc_aux_debug(size_t s, const char *file, int line)  | 
148  | 0  | { | 
149  | 0  |   void *ptr = dm_malloc_aux_debug(s, file, line);  | 
150  |  | 
  | 
151  | 0  |   if (ptr)  | 
152  | 0  |     memset(ptr, 0, s);  | 
153  |  | 
  | 
154  | 0  |   return ptr;  | 
155  | 0  | }  | 
156  |  |  | 
157  |  | void dm_free_aux(void *p)  | 
158  | 0  | { | 
159  | 0  |   char *ptr;  | 
160  | 0  |   size_t i;  | 
161  | 0  |   struct memblock *mb = ((struct memblock *) p) - 1;  | 
162  | 0  |   if (!p)  | 
163  | 0  |     return;  | 
164  |  |  | 
165  | 0  |   dm_bounds_check();  | 
166  |  |  | 
167  |  |   /* sanity check */  | 
168  | 0  |   assert(mb->magic == p);  | 
169  |  | #ifdef VALGRIND_POOL  | 
170  |  |   VALGRIND_MAKE_MEM_DEFINED(p, mb->length);  | 
171  |  | #endif  | 
172  |  |   /* check data at the far boundary */  | 
173  | 0  |   ptr = (char *) p + mb->length;  | 
174  | 0  |   for (i = 0; i < sizeof(unsigned long); i++)  | 
175  | 0  |     if (ptr[i] != (char) mb->id)  | 
176  | 0  |       assert(!"Damage at far end of block");  | 
177  |  |  | 
178  |  |   /* have we freed this before ? */  | 
179  | 0  |   assert(mb->id != 0);  | 
180  |  |  | 
181  |  |   /* unlink */  | 
182  | 0  |   if (mb->prev)  | 
183  | 0  |     mb->prev->next = mb->next;  | 
184  | 0  |   else  | 
185  | 0  |     _head = mb->next;  | 
186  |  | 
  | 
187  | 0  |   if (mb->next)  | 
188  | 0  |     mb->next->prev = mb->prev;  | 
189  | 0  |   else  | 
190  | 0  |     _tail = mb->prev;  | 
191  |  | 
  | 
192  | 0  |   mb->id = 0;  | 
193  |  |  | 
194  |  |   /* stomp a different pattern across the memory */  | 
195  | 0  |   ptr = p;  | 
196  | 0  |   for (i = 0; i < mb->length; i++)  | 
197  | 0  |     ptr[i] = i & 1 ? (char) 0xde : (char) 0xad;  | 
198  |  | 
  | 
199  | 0  |   assert(_mem_stats.blocks_allocated);  | 
200  | 0  |   _mem_stats.blocks_allocated--;  | 
201  | 0  |   _mem_stats.bytes -= mb->length;  | 
202  |  |  | 
203  |  |   /* free the memory */  | 
204  | 0  |   free(mb);  | 
205  | 0  | }  | 
206  |  |  | 
207  |  | void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)  | 
208  | 0  | { | 
209  | 0  |   void *r;  | 
210  | 0  |   struct memblock *mb = ((struct memblock *) p) - 1;  | 
211  |  | 
  | 
212  | 0  |   r = dm_malloc_aux_debug(s, file, line);  | 
213  |  | 
  | 
214  | 0  |   if (r && p) { | 
215  | 0  |     memcpy(r, p, mb->length);  | 
216  | 0  |     dm_free_aux(p);  | 
217  | 0  |   }  | 
218  |  | 
  | 
219  | 0  |   return r;  | 
220  | 0  | }  | 
221  |  |  | 
222  |  | int dm_dump_memory_debug(void)  | 
223  | 0  | { | 
224  | 0  |   unsigned long tot = 0;  | 
225  | 0  |   struct memblock *mb;  | 
226  | 0  |   char str[32];  | 
227  |  | 
  | 
228  | 0  |   if (_head)  | 
229  | 0  |     log_very_verbose("You have a memory leak:"); | 
230  |  | 
  | 
231  | 0  |   for (mb = _head; mb; mb = mb->next) { | 
232  |  | #ifdef VALGRIND_POOL  | 
233  |  |     /*  | 
234  |  |      * We can't look at the memory in case it has had  | 
235  |  |      * VALGRIND_MAKE_MEM_NOACCESS called on it.  | 
236  |  |      */  | 
237  |  |     str[0] = '\0';  | 
238  |  | #else  | 
239  | 0  |     size_t c;  | 
240  |  | 
  | 
241  | 0  |     for (c = 0; c < sizeof(str) - 1; c++) { | 
242  | 0  |       if (c >= mb->length)  | 
243  | 0  |         str[c] = ' ';  | 
244  | 0  |       else if (((char *)mb->magic)[c] == '\0')  | 
245  | 0  |         str[c] = '\0';  | 
246  | 0  |       else if (((char *)mb->magic)[c] < ' ')  | 
247  | 0  |         str[c] = '?';  | 
248  | 0  |       else  | 
249  | 0  |         str[c] = ((char *)mb->magic)[c];  | 
250  | 0  |     }  | 
251  | 0  |     str[sizeof(str) - 1] = '\0';  | 
252  | 0  | #endif  | 
253  |  | 
  | 
254  | 0  |     LOG_MESG(_LOG_INFO, mb->file, mb->line, 0,  | 
255  | 0  |        "block %d at %p, size %" PRIsize_t "\t [%s]",  | 
256  | 0  |        mb->id, mb->magic, mb->length, str);  | 
257  | 0  |     tot += mb->length;  | 
258  | 0  |   }  | 
259  |  | 
  | 
260  | 0  |   if (_head)  | 
261  | 0  |     log_very_verbose("%ld bytes leaked in total", tot); | 
262  |  | 
  | 
263  | 0  |   return 1;  | 
264  | 0  | }  | 
265  |  |  | 
266  |  | void dm_bounds_check_debug(void)  | 
267  | 0  | { | 
268  | 0  |   struct memblock *mb = _head;  | 
269  | 0  |   while (mb) { | 
270  | 0  |     size_t i;  | 
271  | 0  |     char *ptr = ((char *) (mb + 1)) + mb->length;  | 
272  | 0  |     for (i = 0; i < sizeof(unsigned long); i++)  | 
273  | 0  |       if (*ptr++ != (char) mb->id)  | 
274  | 0  |         assert(!"Memory smash");  | 
275  |  |  | 
276  | 0  |     mb = mb->next;  | 
277  | 0  |   }  | 
278  | 0  | }  | 
279  |  |  | 
280  |  | void *dm_malloc_aux(size_t s, const char *file __attribute__((unused)),  | 
281  |  |         int line __attribute__((unused)))  | 
282  | 0  | { | 
283  | 0  |   if (s > 50000000) { | 
284  | 0  |     log_error("Huge memory allocation (size %" PRIsize_t | 
285  | 0  |         ") rejected - metadata corruption?", s);  | 
286  | 0  |     return 0;  | 
287  | 0  |   }  | 
288  |  |  | 
289  | 0  |   return malloc(s);  | 
290  | 0  | }  | 
291  |  |  | 
292  |  | /* Allocate size s with alignment a (or page size if 0) */  | 
293  |  | static void *_dm_malloc_aligned_aux(size_t s, size_t a, const char *file __attribute__((unused)),  | 
294  |  |             int line __attribute__((unused)))  | 
295  | 0  | { | 
296  | 0  |   void *memptr;  | 
297  | 0  |   int r;  | 
298  |  | 
  | 
299  | 0  |   if (!a)  | 
300  | 0  |     a = getpagesize();  | 
301  |  | 
  | 
302  | 0  |   if (s > 50000000) { | 
303  | 0  |     log_error("Huge memory allocation (size %" PRIsize_t | 
304  | 0  |         ") rejected - metadata corruption?", s);  | 
305  | 0  |     return 0;  | 
306  | 0  |   }  | 
307  |  |  | 
308  | 0  |   if ((r = posix_memalign(&memptr, a, s))) { | 
309  | 0  |     log_error("Failed to allocate %" PRIsize_t " bytes aligned to %" PRIsize_t ": %s", s, a, strerror(r)); | 
310  | 0  |     return 0;  | 
311  | 0  |   }  | 
312  |  |  | 
313  | 0  |   return memptr;  | 
314  | 0  | }  | 
315  |  |  | 
316  |  | void *dm_zalloc_aux(size_t s, const char *file, int line)  | 
317  | 0  | { | 
318  | 0  |   void *ptr = dm_malloc_aux(s, file, line);  | 
319  |  | 
  | 
320  | 0  |   if (ptr)  | 
321  | 0  |     memset(ptr, 0, s);  | 
322  |  | 
  | 
323  | 0  |   return ptr;  | 
324  | 0  | }  | 
325  |  |  | 
326  |  | #ifdef DEBUG_MEM  | 
327  |  |  | 
328  |  | void *dm_malloc_wrapper(size_t s, const char *file, int line)  | 
329  |  | { | 
330  |  |   return dm_malloc_aux_debug(s, file, line);  | 
331  |  | }  | 
332  |  |  | 
333  |  | void *dm_malloc_aligned_wrapper(size_t s, size_t a, const char *file, int line)  | 
334  |  | { | 
335  |  |   /* FIXME Implement alignment when debugging - currently just ignored */  | 
336  |  |   return _dm_malloc_aux_debug(s, file, line);  | 
337  |  | }  | 
338  |  |  | 
339  |  | void *dm_zalloc_wrapper(size_t s, const char *file, int line)  | 
340  |  | { | 
341  |  |   return dm_zalloc_aux_debug(s, file, line);  | 
342  |  | }  | 
343  |  |  | 
344  |  | char *dm_strdup_wrapper(const char *str, const char *file, int line)  | 
345  |  | { | 
346  |  |   return dm_strdup_aux(str, file, line);  | 
347  |  | }  | 
348  |  |  | 
349  |  | void dm_free_wrapper(void *ptr)  | 
350  |  | { | 
351  |  |   dm_free_aux(ptr);  | 
352  |  | }  | 
353  |  |  | 
354  |  | void *dm_realloc_wrapper(void *p, unsigned int s, const char *file, int line)  | 
355  |  | { | 
356  |  |   return dm_realloc_aux(p, s, file, line);  | 
357  |  | }  | 
358  |  |  | 
359  |  | int dm_dump_memory_wrapper(void)  | 
360  |  | { | 
361  |  |   return dm_dump_memory_debug();  | 
362  |  | }  | 
363  |  |  | 
364  |  | void dm_bounds_check_wrapper(void)  | 
365  |  | { | 
366  |  |   dm_bounds_check_debug();  | 
367  |  | }  | 
368  |  |  | 
369  |  | #else /* !DEBUG_MEM */  | 
370  |  |  | 
371  |  | void *dm_malloc_wrapper(size_t s, const char *file, int line)  | 
372  | 0  | { | 
373  | 0  |   return dm_malloc_aux(s, file, line);  | 
374  | 0  | }  | 
375  |  |  | 
376  |  | void *dm_malloc_aligned_wrapper(size_t s, size_t a, const char *file, int line)  | 
377  | 0  | { | 
378  | 0  |   return _dm_malloc_aligned_aux(s, a, file, line);  | 
379  | 0  | }  | 
380  |  |  | 
381  |  | void *dm_zalloc_wrapper(size_t s, const char *file, int line)  | 
382  | 0  | { | 
383  | 0  |   return dm_zalloc_aux(s, file, line);  | 
384  | 0  | }  | 
385  |  |  | 
386  |  | char *dm_strdup_wrapper(const char *str,  | 
387  |  |       const char *file __attribute__((unused)),  | 
388  |  |       int line __attribute__((unused)))  | 
389  | 0  | { | 
390  | 0  |   return strdup(str);  | 
391  | 0  | }  | 
392  |  |  | 
393  |  | void dm_free_wrapper(void *ptr)  | 
394  | 11.2k  | { | 
395  | 11.2k  |   free(ptr);  | 
396  | 11.2k  | }  | 
397  |  |  | 
398  |  | void *dm_realloc_wrapper(void *p, unsigned int s,   | 
399  |  |        const char *file __attribute__((unused)),  | 
400  |  |        int line __attribute__((unused)))  | 
401  | 0  | { | 
402  | 0  |   return realloc(p, s);  | 
403  | 0  | }  | 
404  |  |  | 
405  |  | int dm_dump_memory_wrapper(void)  | 
406  | 0  | { | 
407  | 0  |   return 1;  | 
408  | 0  | }  | 
409  |  |  | 
410  |  | void dm_bounds_check_wrapper(void)  | 
411  | 0  | { | 
412  | 0  | }  | 
413  |  |  | 
414  |  | #endif /* DEBUG_MEM */  |