Line data Source code
1 : /** @file memdbg.c
2 : * @brief Memory leak tracker implementation
3 : * @date 25/09/2006
4 : * @author François Cerbelle (Fanfan), francois@cerbelle.net
5 : * @copyright Copyright (c) 1997-2024, François Cerbelle
6 : *
7 : * Originally inspired by "L'art du code", Steve Maguire, Microsoft Press
8 : *
9 : * @internal
10 : * Compiler gcc
11 : * Last modified 2024-12-19 19:49
12 : * Organization Cerbelle.net
13 : * Company Home
14 : *
15 : * This source code is released for free distribution under the terms of the
16 : * GNU General Public License as published by the Free Software Foundation.
17 : */
18 :
19 : #define _XOPEN_SOURCE 500 /* strdup */
20 : #define _GNU_SOURCE /* vasprintf */
21 : #include "libdebug/memdbg.h"
22 : #include <string.h> /* memset, memcpy, memmove */
23 : #include <stdio.h> /* asprintf */
24 : #include <stdarg.h> /* va_list, va_start, va_arg, va_end */
25 :
26 : /* Documented in header file */
27 648 : void* dbg_malloc(
28 : const size_t Size,
29 : const char* File,
30 : const int Line,
31 : const char* CompilDate,
32 : const char* CompilTime,
33 : const char* Function
34 : ) {
35 : /* Memory allocation */
36 648 : void* l_tmp = malloc(Size);
37 648 : if (NULL == l_tmp) return (l_tmp);
38 :
39 : /* If successful, track the memory block */
40 : #pragma GCC diagnostic push /* save the actual diag context */
41 : #ifdef __clang__
42 : #elif __GNUC__
43 : #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" /* locally disable maybe warnings */
44 : #elif _MSC_VER
45 : /*usually has the version number in _MSC_VER*/
46 : #elif __BORLANDC__
47 : #elif __MINGW32__
48 : #endif
49 648 : if (0!=memtrack_addblock( l_tmp, Size, File,Line,CompilDate,CompilTime,Function)) {
50 : #pragma GCC diagnostic pop /* restore previous diag context */
51 : /* If tracking fails, the whole allocation fails */
52 138 : free(l_tmp);
53 138 : l_tmp = NULL;
54 : };
55 :
56 648 : return l_tmp;
57 : }
58 :
59 : /* Documented in header file */
60 578 : void dbg_free(
61 : void* Ptr,
62 : const char* File,
63 : const int Line,
64 : const char* CompilDate,
65 : const char* CompilTime,
66 : const char* Function
67 : ) {
68 : /* If the pointer was not NULL, it was tracked, remove it from the tracked
69 : * list. If it was not tracked, removing returns an error. Abort the
70 : * process as it should never have an untracked pointer. Either it was
71 : * allocated from a non instrumented binary, or it was allocated from
72 : * a non monitored function (see memory.h) or there is a bug in the
73 : * memory leak tracker. */
74 578 : if (NULL!=Ptr)
75 512 : if (0!=memtrack_delblock(Ptr,File,Line,CompilDate,CompilTime,Function))
76 8 : abort();
77 :
78 : /* If the pointer was NULL or tracked, forward it to the real free */
79 570 : free(Ptr);
80 570 : }
81 :
82 : /* Documented in header file */
83 84 : void* dbg_calloc(
84 : const size_t NMemb,
85 : const size_t Size,
86 : const char* File,
87 : const int Line,
88 : const char* CompilDate,
89 : const char* CompilTime,
90 : const char* Function
91 : ) {
92 : void* l_tmp;
93 :
94 : /* Use the dbg_malloc function to allocate the memory */
95 84 : l_tmp = dbg_malloc(
96 : NMemb*Size,
97 : File,Line,CompilDate,CompilTime,Function);
98 :
99 : /* Implement the calloc specific behavior compared to simple malloc:
100 : * it fills the allocated memory block with 0 */
101 84 : if (NULL != l_tmp)
102 18 : memset((char*)l_tmp, 0, NMemb*Size);
103 :
104 84 : return l_tmp;
105 : }
106 :
107 : /* Documented in header file */
108 118 : void* dbg_realloc(
109 : void* Ptr,
110 : const size_t Size,
111 : const char* File,
112 : const int Line,
113 : const char* CompilDate,
114 : const char* CompilTime,
115 : const char* Function
116 : ) {
117 : size_t l_oldsize; /**< Existing bloc size */
118 : char *newblk; /**< New block */
119 :
120 : /* NULL is not tracked but valid */
121 118 : if (NULL==Ptr) {
122 30 : l_oldsize=0;
123 : } else {
124 : /* Fetch existing block size */
125 88 : l_oldsize = memtrack_getblocksize(Ptr);
126 :
127 : /* This is probably a bug in the memory tracker.
128 : * It should not track zero sized blocks */
129 88 : if (0==l_oldsize)
130 16 : abort();
131 : }
132 :
133 : /* If new size is 0, then act as free, like realloc */
134 102 : if (0==Size) {
135 18 : dbg_free(Ptr,
136 : File,Line,CompilDate,CompilTime,Function
137 : );
138 18 : return Ptr;
139 : }
140 :
141 : /* New sized block allocation to simulate the worst case scenario and
142 : * test a pointer change, a data loss (in case of shrink) */
143 84 : newblk=(char*)dbg_malloc(
144 : Size,
145 : File,Line,CompilDate,CompilTime,Function
146 : );
147 : /* The new block can fail */
148 : /* The real realloc function could succeed here, in case of inplace
149 : * shrink in an OOM situation. */
150 84 : if (NULL == newblk) return (newblk);
151 :
152 : /* Copy only the relevant data from old block to new block, loosing extra
153 : * data in case of shrink, and not initializing new data in case of
154 : * increase */
155 54 : memcpy(newblk,(char*)Ptr,(l_oldsize<Size?l_oldsize:Size));
156 :
157 : /* Free old block */
158 54 : dbg_free(
159 : Ptr,
160 : File,Line,CompilDate,CompilTime,Function
161 : );
162 :
163 54 : return (void*) newblk;
164 : }
165 :
166 :
167 : /* Documented in header file */
168 90 : char* dbg_strdup(
169 : const char* Ptr,
170 : const char* File,
171 : const int Line,
172 : const char* CompilDate,
173 : const char* CompilTime,
174 : const char* Function
175 : ) {
176 : char* l_newblk; /** Copy address */
177 :
178 : /* Use strdup to actually copy the string with its own return values and
179 : * abort (SIGSEGV in case of NULL) */
180 90 : l_newblk=NULL;
181 90 : l_newblk=strdup(Ptr); /* cppcheck-suppress ctunullpointer */
182 :
183 : /* If the copy succeeded, try to track the memory allocation */
184 78 : if (NULL != l_newblk)
185 78 : if (0!=memtrack_addblock(
186 : l_newblk,
187 78 : strlen(l_newblk)+1,
188 : File,Line,CompilDate,CompilTime,Function
189 : )) {
190 : /* If tracking fails, the whole operation is reverted and fails */
191 54 : free(l_newblk);
192 54 : l_newblk=NULL;
193 : };
194 :
195 78 : return l_newblk;
196 : }
197 :
198 :
199 : /* Documented in header file */
200 100 : int dbg_asprintf(char **p_Ptr,
201 : const char* p_Format,
202 : const char *File,
203 : const int Line,
204 : const char *CompilDate,
205 : const char *CompilTime, const char *Function,
206 : ...) {
207 : int l_returncode;
208 :
209 : /* NULL is not allowed, where would we store the result, then ? */
210 100 : if(NULL==p_Ptr)
211 4 : abort();
212 :
213 : /* Limit the scope of the variadic manipulation variables */
214 : {
215 : va_list l_ap;
216 96 : va_start (l_ap, Function);
217 : /* Use the original vasprintf to build the formatted string */
218 : /** @todo Implement a vasprintf wrapping function to catch allocation
219 : * and use it here */
220 96 : l_returncode = vasprintf(p_Ptr, p_Format, l_ap);
221 96 : va_end(l_ap);
222 : }
223 :
224 : /* If formatting succeeded, try to track the memory allocation */
225 96 : if (-1 != l_returncode)
226 90 : if (1==memtrack_addblock(
227 : *p_Ptr,
228 90 : strlen (*p_Ptr)+1,
229 : File,Line,CompilDate,CompilTime,Function
230 : )) {
231 : /* If tracking fails, the whole operation is reverted and fails */
232 60 : free(*p_Ptr);
233 60 : l_returncode=-2;
234 : };
235 :
236 96 : return l_returncode;
237 : }
238 : /* vim: set tw=80: */
|