/src/server/mysys/my_malloc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Copyright (c) 2000, 2013, Oracle and/or its affiliates |
3 | | Copyright (c) 2009, 2014, SkySQL Ab |
4 | | |
5 | | This program is free software; you can redistribute it and/or modify |
6 | | it under the terms of the GNU General Public License as published by |
7 | | the Free Software Foundation; version 2 of the License. |
8 | | |
9 | | This program is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with this program; if not, write to the Free Software |
16 | | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ |
17 | | |
18 | | #include "mysys_priv.h" |
19 | | #include "mysys_err.h" |
20 | | #include <m_string.h> |
21 | | |
22 | | struct my_memory_header |
23 | | { |
24 | | PSI_thread *m_owner; |
25 | | size_t m_size; |
26 | | PSI_memory_key m_key; |
27 | | }; |
28 | | typedef struct my_memory_header my_memory_header; |
29 | 0 | #define HEADER_SIZE 24 |
30 | | |
31 | 0 | #define USER_TO_HEADER(P) ((my_memory_header*)((char *)(P) - HEADER_SIZE)) |
32 | 0 | #define HEADER_TO_USER(P) ((char*)(P) + HEADER_SIZE) |
33 | | |
34 | | /** |
35 | | Inform application that memory usage has changed |
36 | | |
37 | | @param size Size of memory segment allocated or freed |
38 | | @param flag 1 if thread specific (allocated by MY_THREAD_SPECIFIC), |
39 | | 0 if system specific. |
40 | | |
41 | | The type os size is long long, to be able to handle negative numbers to |
42 | | decrement the memory usage |
43 | | |
44 | | @return 0 - ok |
45 | | 1 - failure, abort the allocation |
46 | | */ |
47 | | |
48 | | static void dummy(long long size __attribute__((unused)), |
49 | | my_bool is_thread_specific __attribute__((unused))) |
50 | 0 | {} |
51 | | |
52 | | MALLOC_SIZE_CB update_malloc_size= dummy; |
53 | | |
54 | | void set_malloc_size_cb(MALLOC_SIZE_CB func) |
55 | 0 | { |
56 | 0 | update_malloc_size= func; |
57 | 0 | } |
58 | | |
59 | | |
60 | | /** |
61 | | Allocate a sized block of memory. |
62 | | |
63 | | @param key Key to register instrumented memory |
64 | | @param size The size of the memory block in bytes. |
65 | | @param flags Failure action modifiers (bitmasks). |
66 | | |
67 | | @return A pointer to the allocated memory block, or NULL on failure. |
68 | | */ |
69 | | ATTRIBUTE_MALLOC |
70 | | void *my_malloc(PSI_memory_key key, size_t size, myf my_flags) |
71 | 0 | { |
72 | 0 | my_memory_header *mh; |
73 | 0 | void *point; |
74 | 0 | DBUG_ENTER("my_malloc"); |
75 | 0 | DBUG_PRINT("my",("size: %zu flags: %lu", size, my_flags)); |
76 | 0 | compile_time_assert(sizeof(my_memory_header) <= HEADER_SIZE); |
77 | |
|
78 | 0 | if (!(my_flags & (MY_WME | MY_FAE))) |
79 | 0 | my_flags|= my_global_flags; |
80 | | |
81 | | /* Safety */ |
82 | 0 | if (!size) |
83 | 0 | size=1; |
84 | 0 | if (size > SIZE_T_MAX - 1024L*1024L*16L) /* Wrong call */ |
85 | 0 | DBUG_RETURN(0); |
86 | | |
87 | | /* We have to align size as we store MY_THREAD_SPECIFIC flag in the LSB */ |
88 | 0 | size= ALIGN_SIZE(size); |
89 | |
|
90 | 0 | if (DBUG_IF("simulate_out_of_memory")) |
91 | 0 | mh= NULL; |
92 | 0 | else |
93 | 0 | mh= (my_memory_header*) sf_malloc(size + HEADER_SIZE, my_flags); |
94 | |
|
95 | 0 | if (mh == NULL) |
96 | 0 | { |
97 | 0 | my_errno=errno; |
98 | 0 | if (my_flags & MY_FAE) |
99 | 0 | error_handler_hook=fatal_error_handler_hook; |
100 | 0 | if (my_flags & (MY_FAE+MY_WME)) |
101 | 0 | my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_ERROR_LOG+ME_FATAL),size); |
102 | 0 | if (my_flags & MY_FAE) |
103 | 0 | abort(); |
104 | 0 | point= NULL; |
105 | 0 | } |
106 | 0 | else |
107 | 0 | { |
108 | 0 | int flag= MY_TEST(my_flags & MY_THREAD_SPECIFIC); |
109 | 0 | mh->m_size= size | flag; |
110 | 0 | mh->m_key= PSI_CALL_memory_alloc(key, size, & mh->m_owner); |
111 | 0 | if (update_malloc_size) |
112 | 0 | { |
113 | 0 | mh->m_size|=2; |
114 | 0 | update_malloc_size(size + HEADER_SIZE, flag); |
115 | 0 | } |
116 | 0 | point= HEADER_TO_USER(mh); |
117 | 0 | if (my_flags & MY_ZEROFILL) |
118 | 0 | bzero(point, size); |
119 | 0 | else |
120 | 0 | TRASH_ALLOC(point, size); |
121 | 0 | } |
122 | 0 | DBUG_PRINT("exit",("ptr: %p", point)); |
123 | 0 | DBUG_RETURN(point); |
124 | 0 | } |
125 | | |
126 | | |
127 | | /** |
128 | | @brief wrapper around realloc() |
129 | | |
130 | | @param key key to register instrumented memory |
131 | | @param old_point pointer to currently allocated area |
132 | | @param size new size requested, must be >0 |
133 | | @param my_flags flags |
134 | | |
135 | | @note if size==0 realloc() may return NULL; my_realloc() treats this as an |
136 | | error which is not the intention of realloc() |
137 | | */ |
138 | | void *my_realloc(PSI_memory_key key, void *old_point, size_t size, myf my_flags) |
139 | 0 | { |
140 | 0 | my_memory_header *old_mh, *mh; |
141 | 0 | void *point; |
142 | 0 | size_t old_size; |
143 | 0 | my_bool old_flags; |
144 | 0 | DBUG_ENTER("my_realloc"); |
145 | 0 | DBUG_PRINT("my",("ptr: %p size: %zu flags: %lu", old_point, size, my_flags)); |
146 | |
|
147 | 0 | DBUG_ASSERT(size > 0); |
148 | 0 | if (!old_point && (my_flags & MY_ALLOW_ZERO_PTR)) |
149 | 0 | DBUG_RETURN(my_malloc(key, size, my_flags)); |
150 | | |
151 | 0 | old_mh= USER_TO_HEADER(old_point); |
152 | 0 | old_size= old_mh->m_size & ~3; |
153 | 0 | old_flags= old_mh->m_size & 3; |
154 | |
|
155 | 0 | DBUG_ASSERT(old_mh->m_key == key || old_mh->m_key == PSI_NOT_INSTRUMENTED); |
156 | 0 | DBUG_ASSERT((old_flags & 1) == MY_TEST(my_flags & MY_THREAD_SPECIFIC)); |
157 | |
|
158 | 0 | size= ALIGN_SIZE(size); |
159 | 0 | mh= sf_realloc(old_mh, size + HEADER_SIZE, my_flags); |
160 | |
|
161 | 0 | if (mh == NULL) |
162 | 0 | { |
163 | 0 | if (size < old_size) |
164 | 0 | DBUG_RETURN(old_point); |
165 | 0 | my_errno=errno; |
166 | 0 | if (my_flags & MY_FREE_ON_ERROR) |
167 | 0 | { |
168 | | /* my_free will take care of size accounting */ |
169 | 0 | my_free(old_point); |
170 | 0 | old_point= 0; |
171 | 0 | } |
172 | 0 | if (my_flags & (MY_FAE+MY_WME)) |
173 | 0 | my_error(EE_OUTOFMEMORY, MYF(ME_BELL + ME_FATAL), size); |
174 | 0 | point= NULL; |
175 | 0 | } |
176 | 0 | else |
177 | 0 | { |
178 | 0 | mh->m_size= size | old_flags; |
179 | 0 | mh->m_key= PSI_CALL_memory_realloc(key, old_size, size, & mh->m_owner); |
180 | 0 | if (update_malloc_size && (old_flags & 2)) |
181 | 0 | update_malloc_size((longlong)size - (longlong)old_size, old_flags & 1); |
182 | 0 | point= HEADER_TO_USER(mh); |
183 | 0 | } |
184 | | |
185 | 0 | DBUG_PRINT("exit",("ptr: %p", point)); |
186 | 0 | DBUG_RETURN(point); |
187 | 0 | } |
188 | | |
189 | | |
190 | | /** |
191 | | Free memory allocated with my_malloc. |
192 | | |
193 | | @param ptr Pointer to the memory allocated by my_malloc. |
194 | | */ |
195 | | void my_free(void *ptr) |
196 | 0 | { |
197 | 0 | my_memory_header *mh; |
198 | 0 | size_t old_size; |
199 | 0 | my_bool old_flags; |
200 | 0 | DBUG_ENTER("my_free"); |
201 | 0 | DBUG_PRINT("my",("ptr: %p", ptr)); |
202 | |
|
203 | 0 | if (ptr == NULL) |
204 | 0 | DBUG_VOID_RETURN; |
205 | | |
206 | 0 | mh= USER_TO_HEADER(ptr); |
207 | 0 | old_size= mh->m_size & ~3; |
208 | 0 | old_flags= mh->m_size & 3; |
209 | 0 | PSI_CALL_memory_free(mh->m_key, old_size, mh->m_owner); |
210 | |
|
211 | 0 | if (update_malloc_size && (old_flags & 2)) |
212 | 0 | update_malloc_size(- (longlong) old_size - HEADER_SIZE, old_flags & 1); |
213 | |
|
214 | 0 | #ifndef SAFEMALLOC |
215 | | /* |
216 | | Trash memory if not safemalloc. We don't have to do this if safemalloc |
217 | | is used as safemalloc will also do trashing |
218 | | */ |
219 | 0 | TRASH_FREE(ptr, old_size); |
220 | 0 | #endif |
221 | 0 | sf_free(mh); |
222 | 0 | DBUG_VOID_RETURN; |
223 | 0 | } |
224 | | |
225 | | |
226 | | void *my_memdup(PSI_memory_key key, const void *from, size_t length, myf my_flags) |
227 | 0 | { |
228 | 0 | void *ptr; |
229 | 0 | DBUG_ENTER("my_memdup"); |
230 | |
|
231 | 0 | if ((ptr= my_malloc(key, length,my_flags)) != 0) |
232 | 0 | memcpy(ptr, from, length); |
233 | 0 | DBUG_RETURN(ptr); |
234 | 0 | } |
235 | | |
236 | | |
237 | | char *my_strdup(PSI_memory_key key, const char *from, myf my_flags) |
238 | 0 | { |
239 | 0 | char *ptr; |
240 | 0 | size_t length= strlen(from)+1; |
241 | 0 | DBUG_ENTER("my_strdup"); |
242 | |
|
243 | 0 | if ((ptr= (char*) my_malloc(key, length, my_flags))) |
244 | 0 | memcpy(ptr, from, length); |
245 | 0 | DBUG_RETURN(ptr); |
246 | 0 | } |
247 | | |
248 | | |
249 | | char *my_strndup(PSI_memory_key key, const char *from, size_t length, myf my_flags) |
250 | 0 | { |
251 | 0 | char *ptr; |
252 | 0 | DBUG_ENTER("my_strndup"); |
253 | |
|
254 | 0 | if ((ptr= (char*) my_malloc(key, length+1, my_flags))) |
255 | 0 | { |
256 | 0 | memcpy(ptr, from, length); |
257 | 0 | ptr[length]= 0; |
258 | 0 | } |
259 | 0 | DBUG_RETURN(ptr); |
260 | 0 | } |
261 | | |