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 /*======
5 This file is part of PerconaFT.
6 
7 
8 Copyright (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 
38    Licensed under the Apache License, Version 2.0 (the "License");
39    you may not use this file except in compliance with the License.
40    You may obtain a copy of the License at
41 
42        http://www.apache.org/licenses/LICENSE-2.0
43 
44    Unless required by applicable law or agreed to in writing, software
45    distributed under the License is distributed on an "AS IS" BASIS,
46    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47    See the License for the specific language governing permissions and
48    limitations under the License.
49 ======= */
50 
51 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
52 
53 #include <portability/toku_config.h>
54 
55 #include <toku_portability.h>
56 #include <string.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <dlfcn.h>
60 #include <toku_race_tools.h>
61 #include "memory.h"
62 #include "toku_assert.h"
63 #include <portability/toku_atomic.h>
64 
65 static malloc_fun_t  t_malloc  = 0;
66 static malloc_aligned_fun_t t_malloc_aligned = 0;
67 static malloc_fun_t  t_xmalloc = 0;
68 static malloc_aligned_fun_t t_xmalloc_aligned = 0;
69 static free_fun_t    t_free    = 0;
70 static realloc_fun_t t_realloc = 0;
71 static realloc_aligned_fun_t t_realloc_aligned = 0;
72 static realloc_fun_t t_xrealloc = 0;
73 
74 static LOCAL_MEMORY_STATUS_S status;
75 int toku_memory_do_stats = 0;
76 
77 static bool memory_startup_complete = false;
78 
79 int
toku_memory_startup(void)80 toku_memory_startup(void) {
81     if (memory_startup_complete) {
82         return 0;
83     }
84     memory_startup_complete = true;
85 
86     int result = 0;
87 
88 #if defined(HAVE_M_MMAP_THRESHOLD)
89     // initialize libc malloc
90     size_t mmap_threshold = 64 * 1024; // 64K and larger should be malloced with mmap().
91     int success = mallopt(M_MMAP_THRESHOLD, mmap_threshold);
92     if (success) {
93         status.mallocator_version = "libc";
94         status.mmap_threshold = mmap_threshold;
95     } else {
96         result = EINVAL;
97     }
98     assert(result == 0);
99 #else
100     // just a guess
101     status.mallocator_version = "darwin";
102     status.mmap_threshold = 16 * 1024;
103 #endif
104 
105     // jemalloc has a mallctl function, while libc malloc does not.  we can check if jemalloc
106     // is loaded by checking if the mallctl function can be found.  if it can, we call it
107     // to get version and mmap threshold configuration.
108     typedef int (*mallctl_fun_t)(const char *, void *, size_t *, void *, size_t);
109     mallctl_fun_t mallctl_f;
110     mallctl_f = (mallctl_fun_t) dlsym(RTLD_DEFAULT, "mallctl");
111     if (mallctl_f) { // jemalloc is loaded
112         size_t version_length = sizeof status.mallocator_version;
113         result = mallctl_f("version", &status.mallocator_version, &version_length, NULL, 0);
114         assert(result == 0);
115         if (result == 0) {
116             size_t lg_chunk; // log2 of the mmap threshold
117             size_t lg_chunk_length = sizeof lg_chunk;
118             result  = mallctl_f("opt.lg_chunk", &lg_chunk, &lg_chunk_length, NULL, 0);
119             if (result == 0) {
120                 status.mmap_threshold = 1 << lg_chunk;
121             } else {
122                 status.mmap_threshold = 1 << 22;
123                 result = 0;
124             }
125         }
126     }
127 
128     return result;
129 }
130 
131 static bool memory_shutdown_complete;
132 
133 void
toku_memory_shutdown(void)134 toku_memory_shutdown(void) {
135     if (memory_shutdown_complete) {
136         return;
137     }
138     memory_shutdown_complete = true;
139 }
140 
141 void
toku_memory_get_status(LOCAL_MEMORY_STATUS s)142 toku_memory_get_status(LOCAL_MEMORY_STATUS s) {
143     *s = status;
144 }
145 
146 // jemalloc's malloc_usable_size does not work with a NULL pointer, so we implement a version that works
147 static size_t
my_malloc_usable_size(void * p)148 my_malloc_usable_size(void *p) {
149     return p == NULL ? 0 : os_malloc_usable_size(p);
150 }
151 
152 // Note that max_in_use may be slightly off because use of max_in_use is not thread-safe.
153 // It is not worth the overhead to make it completely accurate, but
154 // this logic is intended to guarantee that it increases monotonically.
155 // Note that status.sum_used and status.sum_freed increase monotonically
156 // and that status.max_in_use is declared volatile.
157 static inline void
set_max(uint64_t sum_used,uint64_t sum_freed)158 set_max(uint64_t sum_used, uint64_t sum_freed) {
159     if (sum_used >= sum_freed) {
160         uint64_t in_use = sum_used - sum_freed;
161         uint64_t old_max;
162         do {
163             old_max = status.max_in_use;
164         } while (old_max < in_use &&
165                  !toku_sync_bool_compare_and_swap(&status.max_in_use, old_max, in_use));
166     }
167 }
168 
169 // Effect: Like toku_memory_footprint, except instead of passing p,
170 //   we pass toku_malloc_usable_size(p).
171 size_t
toku_memory_footprint_given_usable_size(size_t touched,size_t usable)172 toku_memory_footprint_given_usable_size(size_t touched, size_t usable)
173 {
174     size_t pagesize = toku_os_get_pagesize();
175     if (usable >= status.mmap_threshold) {
176         int num_pages = (touched + pagesize) / pagesize;
177         return num_pages * pagesize;
178     }
179     return usable;
180 }
181 
182 // Effect: Return an estimate how how much space an object is using, possibly by
183 //   using toku_malloc_usable_size(p).
184 //   If p is NULL then returns 0.
185 size_t
toku_memory_footprint(void * p,size_t touched)186 toku_memory_footprint(void * p, size_t touched)
187 {
188     if (!p) return 0;
189     return toku_memory_footprint_given_usable_size(touched,
190                                                    my_malloc_usable_size(p));
191 }
192 
193 void *
toku_malloc(size_t size)194 toku_malloc(size_t size) {
195 #if defined(__APPLE__)
196     if (size == 0) {
197         return nullptr;
198     }
199 #endif
200 
201     if (size > status.max_requested_size) {
202         status.max_requested_size = size;
203     }
204     void *p = t_malloc ? t_malloc(size) : os_malloc(size);
205     if (p) {
206         TOKU_ANNOTATE_NEW_MEMORY(p, size); // see #4671 and https://bugs.kde.org/show_bug.cgi?id=297147
207         if (toku_memory_do_stats) {
208             size_t used = my_malloc_usable_size(p);
209             toku_sync_add_and_fetch(&status.malloc_count, 1);
210             toku_sync_add_and_fetch(&status.requested,size);
211             toku_sync_add_and_fetch(&status.used, used);
212             set_max(status.used, status.freed);
213         }
214     } else {
215         toku_sync_add_and_fetch(&status.malloc_fail, 1);
216         status.last_failed_size = size;
217     }
218   return p;
219 }
220 
toku_malloc_aligned(size_t alignment,size_t size)221 void *toku_malloc_aligned(size_t alignment, size_t size) {
222 #if defined(__APPLE__)
223     if (size == 0) {
224         return nullptr;
225     }
226 #endif
227 
228     if (size > status.max_requested_size) {
229         status.max_requested_size = size;
230     }
231     void *p = t_malloc_aligned ? t_malloc_aligned(alignment, size) : os_malloc_aligned(alignment, size);
232     if (p) {
233         TOKU_ANNOTATE_NEW_MEMORY(p, size); // see #4671 and https://bugs.kde.org/show_bug.cgi?id=297147
234         if (toku_memory_do_stats) {
235             size_t used = my_malloc_usable_size(p);
236             toku_sync_add_and_fetch(&status.malloc_count, 1);
237             toku_sync_add_and_fetch(&status.requested,size);
238             toku_sync_add_and_fetch(&status.used, used);
239             set_max(status.used, status.freed);
240         }
241     } else {
242         toku_sync_add_and_fetch(&status.malloc_fail, 1);
243         status.last_failed_size = size;
244     }
245   return p;
246 }
247 
248 void *
toku_calloc(size_t nmemb,size_t size)249 toku_calloc(size_t nmemb, size_t size) {
250     size_t newsize = nmemb * size;
251     void *p = toku_malloc(newsize);
252     if (p) memset(p, 0, newsize);
253     return p;
254 }
255 
256 void *
toku_realloc(void * p,size_t size)257 toku_realloc(void *p, size_t size) {
258 #if defined(__APPLE__)
259     if (size == 0) {
260         if (p != nullptr) {
261             toku_free(p);
262         }
263         return nullptr;
264     }
265 #endif
266 
267     if (size > status.max_requested_size) {
268         status.max_requested_size = size;
269     }
270     size_t used_orig = p ? my_malloc_usable_size(p) : 0;
271     void *q = t_realloc ? t_realloc(p, size) : os_realloc(p, size);
272     if (q) {
273         if (toku_memory_do_stats) {
274             size_t used = my_malloc_usable_size(q);
275             toku_sync_add_and_fetch(&status.realloc_count, 1);
276             toku_sync_add_and_fetch(&status.requested, size);
277             toku_sync_add_and_fetch(&status.used, used);
278             toku_sync_add_and_fetch(&status.freed, used_orig);
279             set_max(status.used, status.freed);
280         }
281     } else {
282         toku_sync_add_and_fetch(&status.realloc_fail, 1);
283         status.last_failed_size = size;
284     }
285     return q;
286 }
287 
toku_realloc_aligned(size_t alignment,void * p,size_t size)288 void *toku_realloc_aligned(size_t alignment, void *p, size_t size) {
289 #if defined(__APPLE__)
290     if (size == 0) {
291         if (p != nullptr) {
292             toku_free(p);
293         }
294         return nullptr;
295     }
296 #endif
297 
298     if (size > status.max_requested_size) {
299         status.max_requested_size = size;
300     }
301     size_t used_orig = p ? my_malloc_usable_size(p) : 0;
302     void *q = t_realloc_aligned ? t_realloc_aligned(alignment, p, size) : os_realloc_aligned(alignment, p, size);
303     if (q) {
304         if (toku_memory_do_stats) {
305             size_t used = my_malloc_usable_size(q);
306             toku_sync_add_and_fetch(&status.realloc_count, 1);
307             toku_sync_add_and_fetch(&status.requested, size);
308             toku_sync_add_and_fetch(&status.used, used);
309             toku_sync_add_and_fetch(&status.freed, used_orig);
310             set_max(status.used, status.freed);
311         }
312     } else {
313         toku_sync_add_and_fetch(&status.realloc_fail, 1);
314         status.last_failed_size = size;
315     }
316     return q;
317 }
318 
319 
320 void *
toku_memdup(const void * v,size_t len)321 toku_memdup(const void *v, size_t len) {
322     void *p = toku_malloc(len);
323     if (p) memcpy(p, v,len);
324     return p;
325 }
326 
327 char *
toku_strdup(const char * s)328 toku_strdup(const char *s) {
329     return (char *) toku_memdup(s, strlen(s)+1);
330 }
331 
toku_strndup(const char * s,size_t n)332 char *toku_strndup(const char *s, size_t n) {
333     size_t s_size = strlen(s);
334     size_t bytes_to_copy = n > s_size ? s_size : n;
335     ++bytes_to_copy;
336     char *result = (char *)toku_memdup(s, bytes_to_copy);
337     result[bytes_to_copy - 1] = 0;
338     return result;
339 }
340 
341 void
toku_free(void * p)342 toku_free(void *p) {
343     if (p) {
344         if (toku_memory_do_stats) {
345             size_t used = my_malloc_usable_size(p);
346             toku_sync_add_and_fetch(&status.free_count, 1);
347             toku_sync_add_and_fetch(&status.freed, used);
348         }
349         if (t_free)
350             t_free(p);
351         else
352             os_free(p);
353     }
354 }
355 
356 void *
toku_xmalloc(size_t size)357 toku_xmalloc(size_t size) {
358 #if defined(__APPLE__)
359     if (size == 0) {
360         return nullptr;
361     }
362 #endif
363 
364     if (size > status.max_requested_size) {
365         status.max_requested_size = size;
366     }
367     void *p = t_xmalloc ? t_xmalloc(size) : os_malloc(size);
368     if (p == NULL) {  // avoid function call in common case
369         status.last_failed_size = size;
370         resource_assert(p);
371     }
372     TOKU_ANNOTATE_NEW_MEMORY(p, size); // see #4671 and https://bugs.kde.org/show_bug.cgi?id=297147
373     if (toku_memory_do_stats) {
374         size_t used = my_malloc_usable_size(p);
375         toku_sync_add_and_fetch(&status.malloc_count, 1);
376         toku_sync_add_and_fetch(&status.requested, size);
377         toku_sync_add_and_fetch(&status.used, used);
378         set_max(status.used, status.freed);
379     }
380     return p;
381 }
382 
toku_xmalloc_aligned(size_t alignment,size_t size)383 void* toku_xmalloc_aligned(size_t alignment, size_t size)
384 // Effect: Perform a malloc(size) with the additional property that the returned pointer is a multiple of ALIGNMENT.
385 //  Fail with a resource_assert if the allocation fails (don't return an error code).
386 // Requires: alignment is a power of two.
387 {
388 #if defined(__APPLE__)
389     if (size == 0) {
390         return nullptr;
391     }
392 #endif
393 
394     if (size > status.max_requested_size) {
395         status.max_requested_size = size;
396     }
397     void *p = t_xmalloc_aligned ? t_xmalloc_aligned(alignment, size) : os_malloc_aligned(alignment,size);
398     if (p == NULL && size != 0) {
399         status.last_failed_size = size;
400         resource_assert(p);
401     }
402     if (toku_memory_do_stats) {
403         size_t used = my_malloc_usable_size(p);
404         toku_sync_add_and_fetch(&status.malloc_count, 1);
405         toku_sync_add_and_fetch(&status.requested, size);
406         toku_sync_add_and_fetch(&status.used, used);
407         set_max(status.used, status.freed);
408     }
409     return p;
410 }
411 
412 void *
toku_xcalloc(size_t nmemb,size_t size)413 toku_xcalloc(size_t nmemb, size_t size) {
414     size_t newsize = nmemb * size;
415     void *vp = toku_xmalloc(newsize);
416     if (vp) memset(vp, 0, newsize);
417     return vp;
418 }
419 
420 void *
toku_xrealloc(void * v,size_t size)421 toku_xrealloc(void *v, size_t size) {
422 #if defined(__APPLE__)
423     if (size == 0) {
424         if (v != nullptr) {
425             toku_free(v);
426         }
427         return nullptr;
428     }
429 #endif
430 
431     if (size > status.max_requested_size) {
432         status.max_requested_size = size;
433     }
434     size_t used_orig = v ? my_malloc_usable_size(v) : 0;
435     void *p = t_xrealloc ? t_xrealloc(v, size) : os_realloc(v, size);
436     if (p == 0) {  // avoid function call in common case
437         status.last_failed_size = size;
438         resource_assert(p);
439     }
440     if (toku_memory_do_stats) {
441         size_t used = my_malloc_usable_size(p);
442         toku_sync_add_and_fetch(&status.realloc_count, 1);
443         toku_sync_add_and_fetch(&status.requested, size);
444         toku_sync_add_and_fetch(&status.used, used);
445         toku_sync_add_and_fetch(&status.freed, used_orig);
446         set_max(status.used, status.freed);
447     }
448     return p;
449 }
450 
451 size_t
toku_malloc_usable_size(void * p)452 toku_malloc_usable_size(void *p) {
453     return my_malloc_usable_size(p);
454 }
455 
456 void *
toku_xmemdup(const void * v,size_t len)457 toku_xmemdup (const void *v, size_t len) {
458     void *p = toku_xmalloc(len);
459     memcpy(p, v, len);
460     return p;
461 }
462 
463 char *
toku_xstrdup(const char * s)464 toku_xstrdup (const char *s) {
465     return (char *) toku_xmemdup(s, strlen(s)+1);
466 }
467 
468 void
toku_set_func_malloc(malloc_fun_t f)469 toku_set_func_malloc(malloc_fun_t f) {
470     t_malloc = f;
471     t_xmalloc = f;
472 }
473 
474 void
toku_set_func_xmalloc_only(malloc_fun_t f)475 toku_set_func_xmalloc_only(malloc_fun_t f) {
476     t_xmalloc = f;
477 }
478 
479 void
toku_set_func_malloc_only(malloc_fun_t f)480 toku_set_func_malloc_only(malloc_fun_t f) {
481     t_malloc = f;
482 }
483 
484 void
toku_set_func_realloc(realloc_fun_t f)485 toku_set_func_realloc(realloc_fun_t f) {
486     t_realloc = f;
487     t_xrealloc = f;
488 }
489 
490 void
toku_set_func_xrealloc_only(realloc_fun_t f)491 toku_set_func_xrealloc_only(realloc_fun_t f) {
492     t_xrealloc = f;
493 }
494 
495 void
toku_set_func_realloc_only(realloc_fun_t f)496 toku_set_func_realloc_only(realloc_fun_t f) {
497     t_realloc = f;
498 
499 }
500 
501 void
toku_set_func_free(free_fun_t f)502 toku_set_func_free(free_fun_t f) {
503     t_free = f;
504 }
505 
506 #include <toku_race_tools.h>
507 void __attribute__((constructor)) toku_memory_helgrind_ignore(void);
508 void
toku_memory_helgrind_ignore(void)509 toku_memory_helgrind_ignore(void) {
510     TOKU_VALGRIND_HG_DISABLE_CHECKING(&status, sizeof status);
511 }
512