1/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3#ident "$Id$"
4/*======
5This file is part of PerconaFT.
6
7
8Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9
10 PerconaFT is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License, version 2,
12 as published by the Free Software Foundation.
13
14 PerconaFT is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
21
22----------------------------------------
23
24 PerconaFT is free software: you can redistribute it and/or modify
25 it under the terms of the GNU Affero General Public License, version 3,
26 as published by the Free Software Foundation.
27
28 PerconaFT is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU Affero General Public License for more details.
32
33 You should have received a copy of the GNU Affero General Public License
34 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
35======= */
36
37#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38
39#include <portability/toku_config.h>
40
41#include <toku_portability.h>
42#include <stdlib.h>
43#if defined(HAVE_MALLOC_H)
44# include <malloc.h>
45#elif defined(HAVE_SYS_MALLOC_H)
46# include <sys/malloc.h>
47#endif
48#include <dlfcn.h>
49
50#include <string.h>
51
52// #define this to use a version of os_malloc that helps to debug certain features.
53// This version uses the real malloc (so that valgrind should still work) but it forces things to be slightly
54// misaligned (in particular, avoiding 512-byte alignment if possible, to find situations where O_DIRECT will fail.
55// #define USE_DEBUGGING_MALLOCS
56
57#ifdef USE_DEBUGGING_MALLOCS
58#include <pthread.h>
59
60// Make things misaligned on 512-byte boundaries
61static size_t malloced_now_count=0, malloced_now_size=0;
62struct malloc_pair {
63 void *returned_pointer;
64 void *true_pointer;
65 size_t requested_size = 0;
66};
67static struct malloc_pair *malloced_now;
68static pthread_mutex_t malloc_mutex = PTHREAD_MUTEX_INITIALIZER;
69
70static void malloc_lock(void) {
71 int r = pthread_mutex_lock(&malloc_mutex);
72 assert(r==0);
73}
74static void malloc_unlock(void) {
75 int r = pthread_mutex_unlock(&malloc_mutex);
76 assert(r==0);
77}
78
79static void push_to_malloced_memory(void *returned_pointer, void *true_pointer, size_t requested_size) {
80 malloc_lock();
81 if (malloced_now_count == malloced_now_size) {
82 malloced_now_size = 2*malloced_now_size + 1;
83 malloced_now = (struct malloc_pair *)realloc(malloced_now, malloced_now_size * sizeof(*malloced_now));
84 }
85 malloced_now[malloced_now_count].returned_pointer = returned_pointer;
86 malloced_now[malloced_now_count].true_pointer = true_pointer;
87 malloced_now[malloced_now_count].requested_size = requested_size;
88 malloced_now_count++;
89 malloc_unlock();
90}
91
92static struct malloc_pair *find_malloced_pair(const void *p)
93// Requires: Lock must be held before calling.
94{
95 for (size_t i=0; i<malloced_now_count; i++) {
96 if (malloced_now[i].returned_pointer==p) return &malloced_now[i];
97 }
98 return 0;
99}
100
101void *os_malloc(size_t size) {
102 void *raw_ptr = malloc(size+16); // allocate 16 extra bytes
103 size_t raw_ptr_i = (size_t) raw_ptr;
104 if (raw_ptr_i%512==0) {
105 push_to_malloced_memory(16+(char*)raw_ptr, raw_ptr, size);
106 return 16+(char*)raw_ptr;
107 } else {
108 push_to_malloced_memory(raw_ptr, raw_ptr, size);
109 return raw_ptr;
110 }
111}
112
113void *os_malloc_aligned(size_t alignment, size_t size)
114// Effect: Perform a malloc(size) with the additional property that the returned pointer is a multiple of ALIGNMENT.
115// Requires: alignment is a power of two.
116{
117 void *p;
118 int r = posix_memalign(&p, alignment, size);
119 if (r != 0) {
120 errno = r;
121 p = nullptr;
122 }
123 return p;
124 if (alignment%512==0) {
125 void *raw_ptr;
126 int r = posix_memalign(&raw_ptr, alignment, size);
127 if (r != 0) {
128 errno = r;
129 return nullptr;
130 }
131 push_to_malloced_memory(raw_ptr, raw_ptr, size);
132 return raw_ptr;
133 } else {
134 // Make sure it isn't 512-byte aligned
135 void *raw_ptr;
136 int r = posix_memalign(&raw_ptr, alignment, size+alignment);
137 if (r != 0) {
138 errno = r;
139 return nullptr;
140 }
141 size_t raw_ptr_i = (size_t) raw_ptr;
142 if (raw_ptr_i%512==0) {
143 push_to_malloced_memory(alignment+(char*)raw_ptr, raw_ptr, size);
144 return alignment+(char*)raw_ptr;
145 } else {
146 push_to_malloced_memory(raw_ptr, raw_ptr, size);
147 return raw_ptr;
148 }
149 }
150}
151
152static size_t min(size_t a, size_t b) {
153 if (a<b) return a;
154 else return b;
155}
156
157void *os_realloc(void *p, size_t size) {
158 size_t alignment;
159 if (size<4) {
160 alignment = 1;
161 } else if (size<8) {
162 alignment = 4;
163 } else if (size<16) {
164 alignment = 8;
165 } else {
166 alignment = 16;
167 }
168 return os_realloc_aligned(alignment, p, size);
169}
170
171void * os_realloc_aligned(size_t alignment, void *p, size_t size)
172// Effect: Perform a realloc(p, size) with the additional property that the returned pointer is a multiple of ALIGNMENT.
173// Requires: alignment is a power of two.
174{
175 if (p==NULL) {
176 return os_malloc_aligned(alignment, size);
177 } else {
178 void *result = os_malloc_aligned(alignment, size);
179 malloc_lock();
180 struct malloc_pair *mp = find_malloced_pair(p);
181 assert(mp);
182 // now copy all the good stuff from p to result
183 memcpy(result, p, min(size, mp->requested_size));
184 malloc_unlock();
185 os_free(p);
186 return result;
187 }
188}
189
190
191void os_free(void* p) {
192 malloc_lock();
193 struct malloc_pair *mp = find_malloced_pair(p);
194 assert(mp);
195 free(mp->true_pointer);
196 *mp = malloced_now[--malloced_now_count];
197 malloc_unlock();
198}
199
200size_t os_malloc_usable_size(const void *p) {
201 malloc_lock();
202 struct malloc_pair *mp = find_malloced_pair(p);
203 assert(mp);
204 size_t size = mp->requested_size;
205 malloc_unlock();
206 return size;
207}
208
209#else
210
211void *
212os_malloc(size_t size)
213{
214 return malloc(size);
215}
216
217void *os_malloc_aligned(size_t alignment, size_t size)
218// Effect: Perform a malloc(size) with the additional property that the returned pointer is a multiple of ALIGNMENT.
219// Requires: alignment is a power of two.
220{
221 void *p;
222 int r = posix_memalign(&p, alignment, size);
223 if (r != 0) {
224 errno = r;
225 p = nullptr;
226 }
227 return p;
228}
229
230void *
231os_realloc(void *p, size_t size)
232{
233 return realloc(p, size);
234}
235
236void * os_realloc_aligned(size_t alignment, void *p, size_t size)
237// Effect: Perform a realloc(p, size) with the additional property that the returned pointer is a multiple of ALIGNMENT.
238// Requires: alignment is a power of two.
239{
240#if 1
241 if (p==NULL) {
242 return os_malloc_aligned(alignment, size);
243 } else {
244 void *newp = realloc(p, size);
245 if (0!=((long long)newp%alignment)) {
246 // it's not aligned, so align it ourselves.
247 void *newp2 = os_malloc_aligned(alignment, size);
248 memcpy(newp2, newp, size);
249 free(newp);
250 newp = newp2;
251 }
252 return newp;
253 }
254#else
255 // THIS STUFF SEEMS TO FAIL VALGRIND
256 if (p==NULL) {
257 return os_malloc_aligned(alignment, size);
258 } else {
259 size_t ignore;
260 int r = rallocm(&p, // returned pointer
261 &ignore, // actual size of returned object.
262 size, // the size we want
263 0, // extra bytes to "try" to allocate at the end
264 ALLOCM_ALIGN(alignment));
265 if (r!=0) return NULL;
266 else return p;
267 }
268#endif
269}
270
271
272void
273os_free(void* p)
274{
275 free(p);
276}
277
278typedef size_t (*malloc_usable_size_fun_t)(const void *);
279static malloc_usable_size_fun_t malloc_usable_size_f = NULL;
280
281size_t os_malloc_usable_size(const void *p) {
282 if (p==NULL) return 0;
283 if (!malloc_usable_size_f) {
284 malloc_usable_size_f = (malloc_usable_size_fun_t) dlsym(RTLD_DEFAULT, "malloc_usable_size");
285 if (!malloc_usable_size_f) {
286 malloc_usable_size_f = (malloc_usable_size_fun_t) dlsym(RTLD_DEFAULT, "malloc_size"); // darwin
287 if (!malloc_usable_size_f) {
288 abort(); // couldn't find a malloc size function
289 }
290 }
291 }
292 return malloc_usable_size_f(p);
293}
294#endif
295